diff options
120 files changed, 1747 insertions, 593 deletions
diff --git a/.editorconfig b/.editorconfig index 7303759ed7..a04cb9054c 100644 --- a/.editorconfig +++ b/.editorconfig @@ -47,3 +47,16 @@ emacs_mode = glsl [*.json] indent_style = space emacs_mode = python + +# by default follow QEMU's style +[*.pl] +indent_style = space +indent_size = 4 +emacs_mode = perl + +# but user kernel "style" for imported scripts +[scripts/{kernel-doc,get_maintainer.pl,checkpatch.pl}] +indent_style = tab +indent_size = 8 +emacs_mode = perl + diff --git a/MAINTAINERS b/MAINTAINERS index a928ce3e41..846b81e3ec 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -72,11 +72,14 @@ R: Markus Armbruster <armbru@redhat.com> R: Philippe Mathieu-Daudé <philmd@linaro.org> W: https://www.qemu.org/docs/master/devel/index.html S: Odd Fixes -F: docs/devel/style.rst +F: docs/devel/build-environment.rst F: docs/devel/code-of-conduct.rst +F: docs/devel/codebase.rst F: docs/devel/conflict-resolution.rst +F: docs/devel/style.rst F: docs/devel/submitting-a-patch.rst F: docs/devel/submitting-a-pull-request.rst +F: docs/glossary.rst Responsible Disclosure, Reporting Security Issues ------------------------------------------------- diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 453eb20ec9..d56ca13cdd 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -633,9 +633,10 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr) * Exit the loop and potentially generate a new TB executing the * just the I/O insns. We also limit instrumentation to memory * operations only (which execute after completion) so we don't - * double instrument the instruction. + * double instrument the instruction. Also don't let an IRQ sneak + * in before we execute it. */ - cpu->cflags_next_tb = curr_cflags(cpu) | CF_MEMI_ONLY | n; + cpu->cflags_next_tb = curr_cflags(cpu) | CF_MEMI_ONLY | CF_NOIRQ | n; if (qemu_loglevel_mask(CPU_LOG_EXEC)) { vaddr pc = cpu->cc->get_pc(cpu); diff --git a/bsd-user/main.c b/bsd-user/main.c index 0a5bc57836..b2f6a9be2f 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -628,7 +628,7 @@ int main(int argc, char **argv) target_cpu_init(env, regs); if (gdbstub) { - gdbserver_start(gdbstub); + gdbserver_start(gdbstub, &error_fatal); gdb_handlesig(cpu, 0, NULL, NULL, 0); } cpu_loop(env); diff --git a/configure b/configure index 18336376bf..02f1dd2311 100755 --- a/configure +++ b/configure @@ -528,25 +528,6 @@ case "$cpu" in ;; esac -# Now we have our CPU_CFLAGS we can check if we are targeting a 32 or -# 64 bit host. - -check_64bit_host() { -cat > $TMPC <<EOF -#if __SIZEOF_POINTER__ != 8 -#error not 64 bit system -#endif -int main(void) { return 0; } -EOF - compile_object "$1" -} - -if check_64bit_host "$CPU_CFLAGS"; then - host_bits=64 -else - host_bits=32 -fi - if test -n "$host_arch" && { ! test -d "$source_path/linux-user/include/host/$host_arch" || ! test -d "$source_path/common-user/host/$host_arch"; }; then @@ -1072,7 +1053,7 @@ if test "$static" = "yes" ; then fi plugins="no" fi -if test "$plugins" != "no" && test $host_bits -eq 64; then +if test "$plugins" != "no"; then if has_meson_option "-Dtcg_interpreter=true"; then plugins="no" else diff --git a/contrib/plugins/cache.c b/contrib/plugins/cache.c index 512ef6776b..7baff86860 100644 --- a/contrib/plugins/cache.c +++ b/contrib/plugins/cache.c @@ -208,7 +208,7 @@ static int fifo_get_first_block(Cache *cache, int set) static void fifo_update_on_miss(Cache *cache, int set, int blk_idx) { GQueue *q = cache->sets[set].fifo_queue; - g_queue_push_head(q, GINT_TO_POINTER(blk_idx)); + g_queue_push_head(q, (gpointer)(intptr_t) blk_idx); } static void fifo_destroy(Cache *cache) @@ -471,13 +471,8 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) n_insns = qemu_plugin_tb_n_insns(tb); for (i = 0; i < n_insns; i++) { struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i); - uint64_t effective_addr; - - if (sys) { - effective_addr = (uint64_t) qemu_plugin_insn_haddr(insn); - } else { - effective_addr = (uint64_t) qemu_plugin_insn_vaddr(insn); - } + uint64_t effective_addr = sys ? (uintptr_t) qemu_plugin_insn_haddr(insn) : + qemu_plugin_insn_vaddr(insn); /* * Instructions might get translated multiple times, we do not create @@ -485,14 +480,13 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) * entry from the hash table and register it for the callback again. */ g_mutex_lock(&hashtable_lock); - data = g_hash_table_lookup(miss_ht, GUINT_TO_POINTER(effective_addr)); + data = g_hash_table_lookup(miss_ht, &effective_addr); if (data == NULL) { data = g_new0(InsnData, 1); data->disas_str = qemu_plugin_insn_disas(insn); data->symbol = qemu_plugin_insn_symbol(insn); data->addr = effective_addr; - g_hash_table_insert(miss_ht, GUINT_TO_POINTER(effective_addr), - (gpointer) data); + g_hash_table_insert(miss_ht, &data->addr, data); } g_mutex_unlock(&hashtable_lock); @@ -853,7 +847,7 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); - miss_ht = g_hash_table_new_full(NULL, g_direct_equal, NULL, insn_free); + miss_ht = g_hash_table_new_full(g_int64_hash, g_int64_equal, NULL, insn_free); return 0; } diff --git a/contrib/plugins/cflow.c b/contrib/plugins/cflow.c index b39974d1cf..930ecb46fc 100644 --- a/contrib/plugins/cflow.c +++ b/contrib/plugins/cflow.c @@ -76,6 +76,8 @@ typedef struct { /* We use this to track the current execution state */ typedef struct { + /* address of current translated block */ + uint64_t tb_pc; /* address of end of block */ uint64_t end_block; /* next pc after end of block */ @@ -85,6 +87,7 @@ typedef struct { } VCPUScoreBoard; /* descriptors for accessing the above scoreboard */ +static qemu_plugin_u64 tb_pc; static qemu_plugin_u64 end_block; static qemu_plugin_u64 pc_after_block; static qemu_plugin_u64 last_pc; @@ -189,10 +192,11 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) static void plugin_init(void) { g_mutex_init(&node_lock); - nodes = g_hash_table_new(NULL, g_direct_equal); + nodes = g_hash_table_new(g_int64_hash, g_int64_equal); state = qemu_plugin_scoreboard_new(sizeof(VCPUScoreBoard)); /* score board declarations */ + tb_pc = qemu_plugin_scoreboard_u64_in_struct(state, VCPUScoreBoard, tb_pc); end_block = qemu_plugin_scoreboard_u64_in_struct(state, VCPUScoreBoard, end_block); pc_after_block = qemu_plugin_scoreboard_u64_in_struct(state, VCPUScoreBoard, @@ -215,10 +219,10 @@ static NodeData *fetch_node(uint64_t addr, bool create_if_not_found) NodeData *node = NULL; g_mutex_lock(&node_lock); - node = (NodeData *) g_hash_table_lookup(nodes, (gconstpointer) addr); + node = (NodeData *) g_hash_table_lookup(nodes, &addr); if (!node && create_if_not_found) { node = create_node(addr); - g_hash_table_insert(nodes, (gpointer) addr, (gpointer) node); + g_hash_table_insert(nodes, &node->addr, node); } g_mutex_unlock(&node_lock); return node; @@ -234,7 +238,7 @@ static void vcpu_tb_branched_exec(unsigned int cpu_index, void *udata) uint64_t lpc = qemu_plugin_u64_get(last_pc, cpu_index); uint64_t ebpc = qemu_plugin_u64_get(end_block, cpu_index); uint64_t npc = qemu_plugin_u64_get(pc_after_block, cpu_index); - uint64_t pc = GPOINTER_TO_UINT(udata); + uint64_t pc = qemu_plugin_u64_get(tb_pc, cpu_index); /* return early for address 0 */ if (!lpc) { @@ -305,10 +309,11 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) * handle both early block exits and normal branches in the * callback if we hit it. */ - gpointer udata = GUINT_TO_POINTER(pc); + qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( + tb, QEMU_PLUGIN_INLINE_STORE_U64, tb_pc, pc); qemu_plugin_register_vcpu_tb_exec_cond_cb( tb, vcpu_tb_branched_exec, QEMU_PLUGIN_CB_NO_REGS, - QEMU_PLUGIN_COND_NE, pc_after_block, pc, udata); + QEMU_PLUGIN_COND_NE, pc_after_block, pc, NULL); /* * Now we can set start/end for this block so the next block can diff --git a/contrib/plugins/hotblocks.c b/contrib/plugins/hotblocks.c index 02bc5078bd..f12bfb7a26 100644 --- a/contrib/plugins/hotblocks.c +++ b/contrib/plugins/hotblocks.c @@ -29,7 +29,7 @@ static guint64 limit = 20; * * The internals of the TCG are not exposed to plugins so we can only * get the starting PC for each block. We cheat this slightly by - * xor'ing the number of instructions to the hash to help + * checking the number of instructions as well to help * differentiate. */ typedef struct { @@ -50,6 +50,20 @@ static gint cmp_exec_count(gconstpointer a, gconstpointer b) return count_a > count_b ? -1 : 1; } +static guint exec_count_hash(gconstpointer v) +{ + const ExecCount *e = v; + return e->start_addr ^ e->insns; +} + +static gboolean exec_count_equal(gconstpointer v1, gconstpointer v2) +{ + const ExecCount *ea = v1; + const ExecCount *eb = v2; + return (ea->start_addr == eb->start_addr) && + (ea->insns == eb->insns); +} + static void exec_count_free(gpointer key, gpointer value, gpointer user_data) { ExecCount *cnt = value; @@ -91,7 +105,7 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) static void plugin_init(void) { - hotblocks = g_hash_table_new(NULL, g_direct_equal); + hotblocks = g_hash_table_new(exec_count_hash, exec_count_equal); } static void vcpu_tb_exec(unsigned int cpu_index, void *udata) @@ -111,10 +125,15 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) ExecCount *cnt; uint64_t pc = qemu_plugin_tb_vaddr(tb); size_t insns = qemu_plugin_tb_n_insns(tb); - uint64_t hash = pc ^ insns; g_mutex_lock(&lock); - cnt = (ExecCount *) g_hash_table_lookup(hotblocks, (gconstpointer) hash); + { + ExecCount e; + e.start_addr = pc; + e.insns = insns; + cnt = (ExecCount *) g_hash_table_lookup(hotblocks, &e); + } + if (cnt) { cnt->trans_count++; } else { @@ -123,7 +142,7 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) cnt->trans_count = 1; cnt->insns = insns; cnt->exec_count = qemu_plugin_scoreboard_new(sizeof(uint64_t)); - g_hash_table_insert(hotblocks, (gpointer) hash, (gpointer) cnt); + g_hash_table_insert(hotblocks, cnt, cnt); } g_mutex_unlock(&lock); diff --git a/contrib/plugins/hotpages.c b/contrib/plugins/hotpages.c index 8316ae50c7..c6e6493719 100644 --- a/contrib/plugins/hotpages.c +++ b/contrib/plugins/hotpages.c @@ -103,7 +103,7 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) static void plugin_init(void) { page_mask = (page_size - 1); - pages = g_hash_table_new(NULL, g_direct_equal); + pages = g_hash_table_new(g_int64_hash, g_int64_equal); } static void vcpu_haddr(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo, @@ -130,12 +130,12 @@ static void vcpu_haddr(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo, page &= ~page_mask; g_mutex_lock(&lock); - count = (PageCounters *) g_hash_table_lookup(pages, GUINT_TO_POINTER(page)); + count = (PageCounters *) g_hash_table_lookup(pages, &page); if (!count) { count = g_new0(PageCounters, 1); count->page_address = page; - g_hash_table_insert(pages, GUINT_TO_POINTER(page), (gpointer) count); + g_hash_table_insert(pages, &count->page_address, count); } if (qemu_plugin_mem_is_store(meminfo)) { count->writes++; diff --git a/contrib/plugins/howvec.c b/contrib/plugins/howvec.c index 9be67f7453..2aa9029c3f 100644 --- a/contrib/plugins/howvec.c +++ b/contrib/plugins/howvec.c @@ -253,6 +253,8 @@ static struct qemu_plugin_scoreboard *find_counter( int i; uint64_t *cnt = NULL; uint32_t opcode = 0; + /* if opcode is greater than 32 bits, we should refactor insn hash table. */ + G_STATIC_ASSERT(sizeof(opcode) == sizeof(uint32_t)); InsnClassExecCount *class = NULL; /* @@ -284,7 +286,7 @@ static struct qemu_plugin_scoreboard *find_counter( g_mutex_lock(&lock); icount = (InsnExecCount *) g_hash_table_lookup(insns, - GUINT_TO_POINTER(opcode)); + (gpointer)(intptr_t) opcode); if (!icount) { icount = g_new0(InsnExecCount, 1); @@ -295,8 +297,7 @@ static struct qemu_plugin_scoreboard *find_counter( qemu_plugin_scoreboard_new(sizeof(uint64_t)); icount->count = qemu_plugin_scoreboard_u64(score); - g_hash_table_insert(insns, GUINT_TO_POINTER(opcode), - (gpointer) icount); + g_hash_table_insert(insns, (gpointer)(intptr_t) opcode, icount); } g_mutex_unlock(&lock); diff --git a/contrib/plugins/hwprofile.c b/contrib/plugins/hwprofile.c index 739ac0c66b..2a4cbc47d4 100644 --- a/contrib/plugins/hwprofile.c +++ b/contrib/plugins/hwprofile.c @@ -43,6 +43,8 @@ typedef struct { static GMutex lock; static GHashTable *devices; +static struct qemu_plugin_scoreboard *source_pc_scoreboard; +static qemu_plugin_u64 source_pc; /* track the access pattern to a piece of HW */ static bool pattern; @@ -159,7 +161,7 @@ static DeviceCounts *new_count(const char *name, uint64_t base) count->name = name; count->base = base; if (pattern || source) { - count->detail = g_hash_table_new(NULL, NULL); + count->detail = g_hash_table_new(g_int64_hash, g_int64_equal); } g_hash_table_insert(devices, (gpointer) name, count); return count; @@ -169,7 +171,7 @@ static IOLocationCounts *new_location(GHashTable *table, uint64_t off_or_pc) { IOLocationCounts *loc = g_new0(IOLocationCounts, 1); loc->off_or_pc = off_or_pc; - g_hash_table_insert(table, (gpointer) off_or_pc, loc); + g_hash_table_insert(table, &loc->off_or_pc, loc); return loc; } @@ -224,12 +226,12 @@ static void vcpu_haddr(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo, /* either track offsets or source of access */ if (source) { - off = (uint64_t) udata; + off = qemu_plugin_u64_get(source_pc, cpu_index); } if (pattern || source) { IOLocationCounts *io_count = g_hash_table_lookup(counts->detail, - (gpointer) off); + &off); if (!io_count) { io_count = new_location(counts->detail, off); } @@ -247,10 +249,14 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) for (i = 0; i < n; i++) { struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i); - gpointer udata = (gpointer) (source ? qemu_plugin_insn_vaddr(insn) : 0); + if (source) { + uint64_t pc = qemu_plugin_insn_vaddr(insn); + qemu_plugin_register_vcpu_mem_inline_per_vcpu( + insn, rw, QEMU_PLUGIN_INLINE_STORE_U64, + source_pc, pc); + } qemu_plugin_register_vcpu_mem_cb(insn, vcpu_haddr, - QEMU_PLUGIN_CB_NO_REGS, - rw, udata); + QEMU_PLUGIN_CB_NO_REGS, rw, NULL); } } @@ -306,10 +312,9 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, return -1; } - /* Just warn about overflow */ - if (info->system.smp_vcpus > 64 || - info->system.max_vcpus > 64) { - fprintf(stderr, "hwprofile: can only track up to 64 CPUs\n"); + if (source) { + source_pc_scoreboard = qemu_plugin_scoreboard_new(sizeof(uint64_t)); + source_pc = qemu_plugin_scoreboard_u64(source_pc_scoreboard); } plugin_init(); diff --git a/contrib/plugins/meson.build b/contrib/plugins/meson.build index 63a32c2b4f..484b9a808c 100644 --- a/contrib/plugins/meson.build +++ b/contrib/plugins/meson.build @@ -12,7 +12,7 @@ if get_option('plugins') t += shared_module(i, files(i + '.c') + 'win32_linker.c', include_directories: '../../include/qemu', link_depends: [win32_qemu_plugin_api_lib], - link_args: ['-Lplugins', '-lqemu_plugin_api'], + link_args: win32_qemu_plugin_api_link_flags, dependencies: glib) else t += shared_module(i, files(i + '.c'), diff --git a/contrib/plugins/stoptrigger.c b/contrib/plugins/stoptrigger.c index 03ee22f4c6..b3a6ed66a7 100644 --- a/contrib/plugins/stoptrigger.c +++ b/contrib/plugins/stoptrigger.c @@ -21,9 +21,11 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; /* Scoreboard to track executed instructions count */ typedef struct { uint64_t insn_count; + uint64_t current_pc; } InstructionsCount; static struct qemu_plugin_scoreboard *insn_count_sb; static qemu_plugin_u64 insn_count; +static qemu_plugin_u64 current_pc; static uint64_t icount; static int icount_exit_code; @@ -34,6 +36,11 @@ static bool exit_on_address; /* Map trigger addresses to exit code */ static GHashTable *addrs_ht; +typedef struct { + uint64_t exit_addr; + int exit_code; +} ExitInfo; + static void exit_emulation(int return_code, char *message) { qemu_plugin_outs(message); @@ -43,23 +50,18 @@ static void exit_emulation(int return_code, char *message) static void exit_icount_reached(unsigned int cpu_index, void *udata) { - uint64_t insn_vaddr = GPOINTER_TO_UINT(udata); + uint64_t insn_vaddr = qemu_plugin_u64_get(current_pc, cpu_index); char *msg = g_strdup_printf("icount reached at 0x%" PRIx64 ", exiting\n", insn_vaddr); - exit_emulation(icount_exit_code, msg); } static void exit_address_reached(unsigned int cpu_index, void *udata) { - uint64_t insn_vaddr = GPOINTER_TO_UINT(udata); - char *msg = g_strdup_printf("0x%" PRIx64 " reached, exiting\n", insn_vaddr); - int exit_code; - - exit_code = GPOINTER_TO_INT( - g_hash_table_lookup(addrs_ht, GUINT_TO_POINTER(insn_vaddr))); - - exit_emulation(exit_code, msg); + ExitInfo *ei = udata; + g_assert(ei); + char *msg = g_strdup_printf("0x%" PRIx64 " reached, exiting\n", ei->exit_addr); + exit_emulation(ei->exit_code, msg); } static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) @@ -67,23 +69,25 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) size_t tb_n = qemu_plugin_tb_n_insns(tb); for (size_t i = 0; i < tb_n; i++) { struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i); - gpointer insn_vaddr = GUINT_TO_POINTER(qemu_plugin_insn_vaddr(insn)); + uint64_t insn_vaddr = qemu_plugin_insn_vaddr(insn); if (exit_on_icount) { /* Increment and check scoreboard for each instruction */ qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( insn, QEMU_PLUGIN_INLINE_ADD_U64, insn_count, 1); + qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( + insn, QEMU_PLUGIN_INLINE_STORE_U64, current_pc, insn_vaddr); qemu_plugin_register_vcpu_insn_exec_cond_cb( insn, exit_icount_reached, QEMU_PLUGIN_CB_NO_REGS, - QEMU_PLUGIN_COND_EQ, insn_count, icount + 1, insn_vaddr); + QEMU_PLUGIN_COND_EQ, insn_count, icount + 1, NULL); } if (exit_on_address) { - if (g_hash_table_contains(addrs_ht, insn_vaddr)) { + ExitInfo *ei = g_hash_table_lookup(addrs_ht, &insn_vaddr); + if (ei) { /* Exit triggered by address */ qemu_plugin_register_vcpu_insn_exec_cb( - insn, exit_address_reached, QEMU_PLUGIN_CB_NO_REGS, - insn_vaddr); + insn, exit_address_reached, QEMU_PLUGIN_CB_NO_REGS, ei); } } } @@ -99,11 +103,13 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, int argc, char **argv) { - addrs_ht = g_hash_table_new(NULL, g_direct_equal); + addrs_ht = g_hash_table_new_full(g_int64_hash, g_int64_equal, NULL, g_free); insn_count_sb = qemu_plugin_scoreboard_new(sizeof(InstructionsCount)); insn_count = qemu_plugin_scoreboard_u64_in_struct( insn_count_sb, InstructionsCount, insn_count); + current_pc = qemu_plugin_scoreboard_u64_in_struct( + insn_count_sb, InstructionsCount, current_pc); for (int i = 0; i < argc; i++) { char *opt = argv[i]; @@ -124,13 +130,13 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, exit_on_icount = true; } else if (g_strcmp0(tokens[0], "addr") == 0) { g_auto(GStrv) addr_tokens = g_strsplit(tokens[1], ":", 2); - uint64_t exit_addr = g_ascii_strtoull(addr_tokens[0], NULL, 0); - int exit_code = 0; + ExitInfo *ei = g_malloc(sizeof(ExitInfo)); + ei->exit_addr = g_ascii_strtoull(addr_tokens[0], NULL, 0); + ei->exit_code = 0; if (addr_tokens[1]) { - exit_code = g_ascii_strtoull(addr_tokens[1], NULL, 0); + ei->exit_code = g_ascii_strtoull(addr_tokens[1], NULL, 0); } - g_hash_table_insert(addrs_ht, GUINT_TO_POINTER(exit_addr), - GINT_TO_POINTER(exit_code)); + g_hash_table_insert(addrs_ht, &ei->exit_addr, ei); exit_on_address = true; } else { fprintf(stderr, "option parsing failed: %s\n", opt); diff --git a/docs/about/build-platforms.rst b/docs/about/build-platforms.rst index d8b0445157..482b09819c 100644 --- a/docs/about/build-platforms.rst +++ b/docs/about/build-platforms.rst @@ -29,6 +29,9 @@ The `Repology`_ site is a useful resource to identify currently shipped versions of software in various operating systems, though it does not cover all distros listed below. +You can find how to install build dependencies for different systems on the +:ref:`setup-build-env` page. + Supported host architectures ---------------------------- @@ -130,7 +133,6 @@ Optional build dependencies cross compilation using ``docker`` or ``podman``, or to use pre-built binaries distributed with QEMU. - Windows ------- diff --git a/docs/about/emulation.rst b/docs/about/emulation.rst index 3028d5fff7..3bc3579434 100644 --- a/docs/about/emulation.rst +++ b/docs/about/emulation.rst @@ -176,6 +176,8 @@ for that architecture. - System - Tensilica ISS SIMCALL +.. _tcg-plugins: + TCG Plugins ----------- diff --git a/docs/devel/build-environment.rst b/docs/devel/build-environment.rst new file mode 100644 index 0000000000..f133ef2e01 --- /dev/null +++ b/docs/devel/build-environment.rst @@ -0,0 +1,118 @@ + +.. _setup-build-env: + +Setup build environment +======================= + +QEMU uses a lot of dependencies on the host system. glib2 is used everywhere in +the code base, and most of the other dependencies are optional. + +We present here simple instructions to enable native builds on most popular +systems. + +You can find additional instructions on `QEMU wiki <https://wiki.qemu.org/>`_: + +- `Linux <https://wiki.qemu.org/Hosts/Linux>`_ +- `MacOS <https://wiki.qemu.org/Hosts/Mac>`_ +- `Windows <https://wiki.qemu.org/Hosts/W32>`_ +- `BSD <https://wiki.qemu.org/Hosts/BSD>`_ + +Note: Installing dependencies using your package manager build dependencies may +miss out on deps that have been newly introduced in qemu.git. In more, it misses +deps the distribution has decided to exclude. + +Linux +----- + +Fedora +++++++ + +:: + + sudo dnf update && sudo dnf builddep qemu + +Debian/Ubuntu ++++++++++++++ + +You first need to enable `Sources List <https://wiki.debian.org/SourcesList>`_. +Then, use apt to install dependencies: + +:: + + sudo apt update && sudo apt build-dep qemu + +MacOS +----- + +You first need to install `Homebrew <https://brew.sh/>`_. Then, use it to +install dependencies: + +:: + + brew update && brew install $(brew deps --include-build qemu) + +Windows +------- + +You first need to install `MSYS2 <https://www.msys2.org/>`_. +MSYS2 offers `different environments <https://www.msys2.org/docs/environments/>`_. +x86_64 environments are based on GCC, while aarch64 is based on Clang. + +We recommend to use MINGW64 for windows-x86_64 and CLANGARM64 for windows-aarch64 +(only available on windows-aarch64 hosts). + +Then, you can open a windows shell, and enter msys2 env using: + +:: + + c:/msys64/msys2_shell.cmd -defterm -here -no-start -mingw64 + # Replace -ucrt64 by -clangarm64 or -ucrt64 for other environments. + +MSYS2 package manager does not offer a built-in way to install build +dependencies. You can start with this list of packages using pacman: + +Note: Dependencies need to be installed again if you use a different MSYS2 +environment. + +:: + + # update MSYS2 itself, you need to reopen your shell at the end. + pacman -Syu + pacman -S \ + base-devel binutils bison diffutils flex git grep make sed \ + ${MINGW_PACKAGE_PREFIX}-toolchain \ + ${MINGW_PACKAGE_PREFIX}-glib2 \ + ${MINGW_PACKAGE_PREFIX}-gtk3 \ + ${MINGW_PACKAGE_PREFIX}-libnfs \ + ${MINGW_PACKAGE_PREFIX}-libssh \ + ${MINGW_PACKAGE_PREFIX}-ninja \ + ${MINGW_PACKAGE_PREFIX}-pixman \ + ${MINGW_PACKAGE_PREFIX}-pkgconf \ + ${MINGW_PACKAGE_PREFIX}-python \ + ${MINGW_PACKAGE_PREFIX}-SDL2 \ + ${MINGW_PACKAGE_PREFIX}-zstd + +If you want to install all dependencies, it's possible to use recipe used to +build QEMU in MSYS2 itself. + +:: + + pacman -S wget + wget https://raw.githubusercontent.com/msys2/MINGW-packages/refs/heads/master/mingw-w64-qemu/PKGBUILD + # Some packages may be missing for your environment, installation will still + # be done though. + makepkg -s PKGBUILD || true + +Build on windows-aarch64 +++++++++++++++++++++++++ + +When trying to cross compile meson for x86_64 using UCRT64 or MINGW64 env, +configure will run into an error because the cpu detected is not correct. + +Meson detects x86_64 processes emulated, so you need to manually set the cpu, +and force a cross compilation (with empty prefix). + +:: + + ./configure --cpu=x86_64 --cross-prefix= + diff --git a/docs/devel/codebase.rst b/docs/devel/codebase.rst new file mode 100644 index 0000000000..4039875ee0 --- /dev/null +++ b/docs/devel/codebase.rst @@ -0,0 +1,220 @@ +======== +Codebase +======== + +This section presents the various parts of QEMU and how the codebase is +organized. + +Beyond giving succint descriptions, the goal is to offer links to various +parts of the documentation/codebase. + +Subsystems +---------- + +An exhaustive list of subsystems and associated files can be found in the +`MAINTAINERS <https://gitlab.com/qemu-project/qemu/-/blob/master/MAINTAINERS>`_ +file. + +Some of the main QEMU subsystems are: + +- `Accelerators<Accelerators>` +- Block devices and `disk images<disk images>` support +- `CI<ci>` and `Tests<testing>` +- `Devices<device-emulation>` & Board models +- `Documentation <documentation-root>` +- `GDB support<GDB usage>` +- `Migration<migration>` +- `Monitor<QEMU monitor>` +- :ref:`QOM (QEMU Object Model)<qom>` +- `System mode<System emulation>` +- :ref:`TCG (Tiny Code Generator)<tcg>` +- `User mode<user-mode>` (`Linux<linux-user-mode>` & `BSD<bsd-user-mode>`) +- User Interfaces + +More documentation on QEMU subsystems can be found on :ref:`internal-subsystem` +page. + +The Grand tour +-------------- + +We present briefly here what every folder in the top directory of the codebase +contains. Hop on! + +The folder name links here will take you to that folder in our gitlab +repository. Other links will take you to more detailed documentation for that +subsystem, where we have it. Unfortunately not every subsystem has documentation +yet, so sometimes the source code is all you have. + +* `accel <https://gitlab.com/qemu-project/qemu/-/tree/master/accel>`_: + Infrastructure and architecture agnostic code related to the various + `accelerators <Accelerators>` supported by QEMU + (TCG, KVM, hvf, whpx, xen, nvmm). + Contains interfaces for operations that will be implemented per + `target <https://gitlab.com/qemu-project/qemu/-/tree/master/target>`_. +* `audio <https://gitlab.com/qemu-project/qemu/-/tree/master/audio>`_: + Audio (host) support. +* `authz <https://gitlab.com/qemu-project/qemu/-/tree/master/authz>`_: + `QEMU Authorization framework<client authorization>`. +* `backends <https://gitlab.com/qemu-project/qemu/-/tree/master/backends>`_: + Various backends that are used to access resources on the host (e.g. for + random number generation, memory backing or cryptographic functions). +* `block <https://gitlab.com/qemu-project/qemu/-/tree/master/block>`_: + Block devices and `image formats<disk images>` implementation. +* `bsd-user <https://gitlab.com/qemu-project/qemu/-/tree/master/bsd-user>`_: + `BSD User mode<bsd-user-mode>`. +* build: Where the code built goes by default. You can tell the QEMU build + system to put the built code anywhere else you like. +* `chardev <https://gitlab.com/qemu-project/qemu/-/tree/master/chardev>`_: + Various backends used by char devices. +* `common-user <https://gitlab.com/qemu-project/qemu/-/tree/master/common-user>`_: + User-mode assembly code for dealing with signals occuring during syscalls. +* `configs <https://gitlab.com/qemu-project/qemu/-/tree/master/configs>`_: + Makefiles defining configurations to build QEMU. +* `contrib <https://gitlab.com/qemu-project/qemu/-/tree/master/contrib>`_: + Community contributed devices/plugins/tools. +* `crypto <https://gitlab.com/qemu-project/qemu/-/tree/master/crypto>`_: + Cryptographic algorithms used in QEMU. +* `disas <https://gitlab.com/qemu-project/qemu/-/tree/master/disas>`_: + Disassembly functions used by QEMU target code. +* `docs <https://gitlab.com/qemu-project/qemu/-/tree/master/docs>`_: + QEMU Documentation. +* `dump <https://gitlab.com/qemu-project/qemu/-/tree/master/dump>`_: + Code to dump memory of a running VM. +* `ebpf <https://gitlab.com/qemu-project/qemu/-/tree/master/ebpf>`_: + eBPF program support in QEMU. `virtio-net RSS<ebpf-rss>` uses it. +* `fpu <https://gitlab.com/qemu-project/qemu/-/tree/master/fpu>`_: + Floating-point software emulation. +* `fsdev <https://gitlab.com/qemu-project/qemu/-/tree/master/fsdev>`_: + `VirtFS <https://www.linux-kvm.org/page/VirtFS>`_ support. +* `gdbstub <https://gitlab.com/qemu-project/qemu/-/tree/master/gdbstub>`_: + `GDB <GDB usage>` support. +* `gdb-xml <https://gitlab.com/qemu-project/qemu/-/tree/master/gdb-xml>`_: + Set of XML files describing architectures and used by `gdbstub <GDB usage>`. +* `host <https://gitlab.com/qemu-project/qemu/-/tree/master/host>`_: + Various architecture specific header files (crypto, atomic, memory + operations). +* `linux-headers <https://gitlab.com/qemu-project/qemu/-/tree/master/linux-headers>`_: + A subset of headers imported from Linux kernel and used for implementing + KVM support and user-mode. +* `linux-user <https://gitlab.com/qemu-project/qemu/-/tree/master/linux-user>`_: + `User mode <user-mode>` implementation. Contains one folder per target + architecture. +* `.gitlab-ci.d <https://gitlab.com/qemu-project/qemu/-/tree/master/.gitlab-ci.d>`_: + `CI <ci>` yaml and scripts. +* `include <https://gitlab.com/qemu-project/qemu/-/tree/master/include>`_: + All headers associated to different subsystems in QEMU. The hierachy used + mirrors source code organization and naming. +* `hw <https://gitlab.com/qemu-project/qemu/-/tree/master/hw>`_: + `Devices <device-emulation>` and boards emulation. Devices are categorized by + type/protocol/architecture and located in associated subfolder. +* `io <https://gitlab.com/qemu-project/qemu/-/tree/master/io>`_: + QEMU `I/O channels <https://lists.gnu.org/archive/html/qemu-devel/2015-11/msg04208.html>`_. +* `libdecnumber <https://gitlab.com/qemu-project/qemu/-/tree/master/libdecnumber>`_: + Import of gcc library, used to implement decimal number arithmetic. +* `migration <https://gitlab.com/qemu-project/qemu/-/tree/master/migration>`__: + `Migration framework <migration>`. +* `monitor <https://gitlab.com/qemu-project/qemu/-/tree/master/monitor>`_: + `Monitor <QEMU monitor>` implementation (HMP & QMP). +* `nbd <https://gitlab.com/qemu-project/qemu/-/tree/master/nbd>`_: + QEMU `NBD (Network Block Device) <nbd>` server. +* `net <https://gitlab.com/qemu-project/qemu/-/tree/master/net>`_: + Network (host) support. +* `pc-bios <https://gitlab.com/qemu-project/qemu/-/tree/master/pc-bios>`_: + Contains pre-built firmware binaries and boot images, ready to use in + QEMU without compilation. +* `plugins <https://gitlab.com/qemu-project/qemu/-/tree/master/plugins>`_: + :ref:`TCG plugins <tcg-plugins>` core implementation. Plugins can be found in + `tests <https://gitlab.com/qemu-project/qemu/-/tree/master/tests/tcg/plugins>`__ + and `contrib <https://gitlab.com/qemu-project/qemu/-/tree/master/contrib/plugins>`__ + folders. +* `po <https://gitlab.com/qemu-project/qemu/-/tree/master/po>`_: + Translation files. +* `python <https://gitlab.com/qemu-project/qemu/-/tree/master/python>`_: + Python part of our build/test system. +* `qapi <https://gitlab.com/qemu-project/qemu/-/tree/master/qapi>`_: + `QAPI <qapi>` implementation. +* `qobject <https://gitlab.com/qemu-project/qemu/-/tree/master/qobject>`_: + QEMU Object implementation. +* `qga <https://gitlab.com/qemu-project/qemu/-/tree/master/qga>`_: + QEMU `Guest agent <qemu-ga>` implementation. +* `qom <https://gitlab.com/qemu-project/qemu/-/tree/master/qom>`_: + QEMU :ref:`Object model <qom>` implementation, with monitor associated commands. +* `replay <https://gitlab.com/qemu-project/qemu/-/tree/master/replay>`_: + QEMU :ref:`Record/replay <replay>` implementation. +* `roms <https://gitlab.com/qemu-project/qemu/-/tree/master/roms>`_: + Contains source code for various firmware and ROMs, which can be compiled if + custom or updated versions are needed. +* `rust <https://gitlab.com/qemu-project/qemu/-/tree/master/rust>`_: + Rust integration in QEMU. It contains the new interfaces defined and + associated devices using it. +* `scripts <https://gitlab.com/qemu-project/qemu/-/tree/master/scripts>`_: + Collection of scripts used in build and test systems, and various + tools for QEMU codebase and execution traces. +* `scsi <https://gitlab.com/qemu-project/qemu/-/tree/master/scsi>`_: + Code related to SCSI support, used by SCSI devices. +* `semihosting <https://gitlab.com/qemu-project/qemu/-/tree/master/semihosting>`_: + QEMU `Semihosting <Semihosting>` implementation. +* `stats <https://gitlab.com/qemu-project/qemu/-/tree/master/stats>`_: + `Monitor <QEMU monitor>` stats commands implementation. +* `storage-daemon <https://gitlab.com/qemu-project/qemu/-/tree/master/storage-daemon>`_: + QEMU `Storage daemon <storage-daemon>` implementation. +* `stubs <https://gitlab.com/qemu-project/qemu/-/tree/master/stubs>`_: + Various stubs (empty functions) used to compile QEMU with specific + configurations. +* `subprojects <https://gitlab.com/qemu-project/qemu/-/tree/master/subprojects>`_: + QEMU submodules used by QEMU build system. +* `system <https://gitlab.com/qemu-project/qemu/-/tree/master/system>`_: + QEMU `system mode <System emulation>` implementation (cpu, mmu, boot support). +* `target <https://gitlab.com/qemu-project/qemu/-/tree/master/target>`_: + Contains code for all target architectures supported (one subfolder + per arch). For every architecture, you can find accelerator specific + implementations. +* `tcg <https://gitlab.com/qemu-project/qemu/-/tree/master/tcg>`_: + :ref:`TCG <tcg>` related code. + Contains one subfolder per host supported architecture. +* `tests <https://gitlab.com/qemu-project/qemu/-/tree/master/tests>`_: + QEMU `test <testing>` suite + + - `avocado <https://gitlab.com/qemu-project/qemu/-/tree/master/tests/avocado>`_: + Functional tests booting full VM using `Avocado framework <checkavocado-ref>`. + Those tests will be transformed and moved into + `tests/functional <https://gitlab.com/qemu-project/qemu/-/tree/master/tests/functional>`_ + in the future. + - `data <https://gitlab.com/qemu-project/qemu/-/tree/master/tests/data>`_: + Data for various tests. + - `decode <https://gitlab.com/qemu-project/qemu/-/tree/master/tests/decode>`_: + Testsuite for :ref:`decodetree <decodetree>` implementation. + - `docker <https://gitlab.com/qemu-project/qemu/-/tree/master/tests/docker>`_: + Code and scripts to create `containers <container-ref>` used in `CI <ci>`. + - `fp <https://gitlab.com/qemu-project/qemu/-/tree/master/tests/fp>`_: + QEMU testsuite for soft float implementation. + - `functional <https://gitlab.com/qemu-project/qemu/-/tree/master/tests/functional>`_: + `Functional tests <checkfunctional-ref>` (full VM boot). + - `lcitool <https://gitlab.com/qemu-project/qemu/-/tree/master/tests/lcitool>`_: + Generate dockerfiles for CI containers. + - `migration <https://gitlab.com/qemu-project/qemu/-/tree/master/tests/migration>`_: + Test scripts and data for `Migration framework <migration>`. + - `multiboot <https://gitlab.com/qemu-project/qemu/-/tree/master/tests/multiboot>`_: + Test multiboot functionality for x86_64/i386. + - `qapi-schema <https://gitlab.com/qemu-project/qemu/-/tree/master/tests/qapi-schema>`_: + Test scripts and data for `QAPI <qapi-tests>`. + - `qemu-iotests <https://gitlab.com/qemu-project/qemu/-/tree/master/tests/qemu-iotests>`_: + `Disk image and block tests <qemu-iotests>`. + - `qtest <https://gitlab.com/qemu-project/qemu/-/tree/master/tests/qtest>`_: + `Device emulation testing <qtest>`. + - `tcg <https://gitlab.com/qemu-project/qemu/-/tree/master/tests/tcg>`__: + `TCG related tests <checktcg-ref>`. Contains code per architecture + (subfolder) and multiarch tests as well. + - `tsan <https://gitlab.com/qemu-project/qemu/-/tree/master/tests/tsan>`_: + `Suppressions <tsan-suppressions>` for thread sanitizer. + - `uefi-test-tools <https://gitlab.com/qemu-project/qemu/-/tree/master/tests/uefi-test-tools>`_: + Test tool for UEFI support. + - `unit <https://gitlab.com/qemu-project/qemu/-/tree/master/tests/unit>`_: + QEMU `Unit tests <unit-tests>`. +* `trace <https://gitlab.com/qemu-project/qemu/-/tree/master/trace>`_: + :ref:`Tracing framework <tracing>`. Used to print information associated to various + events during execution. +* `ui <https://gitlab.com/qemu-project/qemu/-/tree/master/ui>`_: + QEMU User interfaces. +* `util <https://gitlab.com/qemu-project/qemu/-/tree/master/util>`_: + Utility code used by other parts of QEMU. diff --git a/docs/devel/control-flow-integrity.rst b/docs/devel/control-flow-integrity.rst index e6b73a4fe1..3d5702fa4c 100644 --- a/docs/devel/control-flow-integrity.rst +++ b/docs/devel/control-flow-integrity.rst @@ -1,3 +1,5 @@ +.. _cfi: + ============================ Control-Flow Integrity (CFI) ============================ diff --git a/docs/devel/decodetree.rst b/docs/devel/decodetree.rst index e3392aa705..98ad33a487 100644 --- a/docs/devel/decodetree.rst +++ b/docs/devel/decodetree.rst @@ -1,3 +1,5 @@ +.. _decodetree: + ======================== Decodetree Specification ======================== diff --git a/docs/devel/ebpf_rss.rst b/docs/devel/ebpf_rss.rst index 4a68682b31..ed5d33767b 100644 --- a/docs/devel/ebpf_rss.rst +++ b/docs/devel/ebpf_rss.rst @@ -1,3 +1,5 @@ +.. _ebpf-rss: + =========================== eBPF RSS virtio-net support =========================== diff --git a/docs/devel/index-build.rst b/docs/devel/index-build.rst index 0023953be3..0745c81a26 100644 --- a/docs/devel/index-build.rst +++ b/docs/devel/index-build.rst @@ -8,6 +8,7 @@ some of the basics if you are adding new files and targets to the build. :maxdepth: 3 build-system + build-environment kconfig docs qapi-code-gen diff --git a/docs/devel/index-internals.rst b/docs/devel/index-internals.rst index ab9fbc4482..bca597c658 100644 --- a/docs/devel/index-internals.rst +++ b/docs/devel/index-internals.rst @@ -1,3 +1,5 @@ +.. _internal-subsystem: + Internal Subsystem Information ------------------------------ diff --git a/docs/devel/index.rst b/docs/devel/index.rst index a53f1bfda5..29f032d6a8 100644 --- a/docs/devel/index.rst +++ b/docs/devel/index.rst @@ -35,3 +35,4 @@ the :ref:`tcg_internals`. index-api index-internals index-tcg + codebase diff --git a/docs/devel/migration/main.rst b/docs/devel/migration/main.rst index c2857fc244..cdd4f4a6d7 100644 --- a/docs/devel/migration/main.rst +++ b/docs/devel/migration/main.rst @@ -1,3 +1,5 @@ +.. _migration: + =================== Migration framework =================== diff --git a/docs/devel/multi-thread-tcg.rst b/docs/devel/multi-thread-tcg.rst index d706c27ea7..7fd0a07633 100644 --- a/docs/devel/multi-thread-tcg.rst +++ b/docs/devel/multi-thread-tcg.rst @@ -4,6 +4,8 @@ This work is licensed under the terms of the GNU GPL, version 2 or later. See the COPYING file in the top-level directory. +.. _mttcg: + ================== Multi-threaded TCG ================== diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst index 583207a8ec..3e26d2d104 100644 --- a/docs/devel/qapi-code-gen.rst +++ b/docs/devel/qapi-code-gen.rst @@ -9,6 +9,7 @@ How to use the QAPI code generator This work is licensed under the terms of the GNU GPL, version 2 or later. See the COPYING file in the top-level directory. +.. _qapi: Introduction ============ diff --git a/docs/devel/style.rst b/docs/devel/style.rst index 2f68b50079..d025933808 100644 --- a/docs/devel/style.rst +++ b/docs/devel/style.rst @@ -416,6 +416,26 @@ definitions instead of typedefs in headers and function prototypes; this avoids problems with duplicated typedefs and reduces the need to include headers from other headers. +Bitfields +--------- + +C bitfields can be a cause of non-portability issues, especially under windows +where `MSVC has a different way to lay them out than GCC +<https://gcc.gnu.org/onlinedocs/gcc/x86-Type-Attributes.html>`_, or where +endianness matters. + +For this reason, we disallow usage of bitfields in packed structures and in any +structures which are supposed to exactly match a specific layout in guest +memory. Some existing code may use it, and we carefully ensured the layout was +the one expected. + +We also suggest avoiding bitfields even in structures where the exact +layout does not matter, unless you can show that they provide a significant +usability benefit. + +We encourage the usage of ``include/hw/registerfields.h`` as a safe replacement +for bitfields. + Reserved namespaces in C and POSIX ---------------------------------- diff --git a/docs/devel/submitting-a-patch.rst b/docs/devel/submitting-a-patch.rst index 03b2ac298a..65c64078cb 100644 --- a/docs/devel/submitting-a-patch.rst +++ b/docs/devel/submitting-a-patch.rst @@ -235,6 +235,31 @@ to another list.) ``git send-email`` (`step-by-step setup guide works best for delivering the patch without mangling it, but attachments can be used as a last resort on a first-time submission. +.. _use_git_publish: + +Use git-publish +~~~~~~~~~~~~~~~ + +If you already configured git send-email, you can simply use `git-publish +<https://github.com/stefanha/git-publish>`__ to send series. + +:: + + $ git checkout master -b my-feature + $ # work on new commits, add your 'Signed-off-by' lines to each + $ git publish + $ ... more work, rebase on master, ... + $ git publish # will send a v2 + +Each time you post a series, git-publish will create a local tag with the format +``<branchname>-v<version>`` to record the patch series. + +When sending patch emails, 'git publish' will consult the output of +'scripts/get_maintainers.pl' and automatically CC anyone listed as maintainers +of the affected code. Generally you should accept the suggested CC list, but +there may sometimes be scenarios where it is appropriate to cut it down (eg on +certain large tree-wide cleanups), or augment it with other interested people. + .. _if_you_cannot_send_patch_emails: If you cannot send patch emails @@ -408,6 +433,20 @@ For more details on how QEMU's stable process works, refer to the .. _participating_in_code_review: +Retrieve an existing series +--------------------------- + +If you want to apply an existing series on top of your tree, you can simply use +`b4 <https://github.com/mricon/b4>`__. + +:: + + b4 shazam $msg-id + +The message id is related to the patch series that has been sent to the mailing +list. You need to retrieve the "Message-Id:" header from one of the patches. Any +of them can be used and b4 will apply the whole series. + Participating in Code Review ---------------------------- diff --git a/docs/devel/testing/main.rst b/docs/devel/testing/main.rst index 91f4dc61fb..9869bcf034 100644 --- a/docs/devel/testing/main.rst +++ b/docs/devel/testing/main.rst @@ -39,6 +39,8 @@ Before running tests, it is best to build QEMU programs first. Some tests expect the executables to exist and will fail with obscure messages if they cannot find them. +.. _unit-tests: + Unit tests ~~~~~~~~~~ @@ -126,6 +128,8 @@ successfully on various hosts. The following list shows some best practices: #ifdef in the codes. If the whole test suite cannot run on Windows, disable the build in the meson.build file. +.. _qapi-tests: + QAPI schema tests ~~~~~~~~~~~~~~~~~ @@ -160,6 +164,8 @@ check-block are in the "auto" group). See the "QEMU iotests" section below for more information. +.. _qemu-iotests: + QEMU iotests ------------ @@ -679,6 +685,8 @@ The above exitcode=0 has TSan continue without error if any warnings are found. This allows for running the test and then checking the warnings afterwards. If you want TSan to stop and exit with error on warnings, use exitcode=66. +.. _tsan-suppressions: + TSan Suppressions ~~~~~~~~~~~~~~~~~ Keep in mind that for any data race warning, although there might be a data race @@ -901,7 +909,6 @@ You can run the avocado tests simply by executing: See :ref:`checkavocado-ref` for more details. - .. _checktcg-ref: Testing with "make check-tcg" diff --git a/docs/devel/testing/qtest.rst b/docs/devel/testing/qtest.rst index c5b8546b3e..73ef7702b7 100644 --- a/docs/devel/testing/qtest.rst +++ b/docs/devel/testing/qtest.rst @@ -1,3 +1,5 @@ +.. _qtest: + ======================================== QTest Device Emulation Testing Framework ======================================== diff --git a/docs/glossary.rst b/docs/glossary.rst new file mode 100644 index 0000000000..693d9855dd --- /dev/null +++ b/docs/glossary.rst @@ -0,0 +1,280 @@ +.. _Glossary: + +-------- +Glossary +-------- + +This section of the manual presents brief definitions of acronyms and terms used +by QEMU developers. + +Accelerator +----------- + +A specific API used to accelerate execution of guest instructions. It can be +hardware-based, through a virtualization API provided by the host OS (kvm, hvf, +whpx, ...), or software-based (tcg). See this description of `supported +accelerators<Accelerators>`. + +Board +----- + +Another name for :ref:`machine`. + +Block +----- + +Block drivers are the available `disk formats and front-ends +<block-drivers>` available, and block devices `(see Block device section on +options page)<sec_005finvocation>` are using them to implement disks for a +virtual machine. + +CFI +--- + +Control Flow Integrity is a hardening technique used to prevent exploits +targeting QEMU by detecting unexpected branches during execution. QEMU `actively +supports<cfi>` being compiled with CFI enabled. + +Device +------ + +In QEMU, a device is a piece of hardware visible to the guest. Examples include +UARTs, PCI controllers, PCI cards, VGA controllers, and many more. + +QEMU is able to emulate a CPU, and all the hardware interacting with it, +including `many devices<device-emulation>`. When QEMU runs a virtual machine +using a hardware-based accelerator, it is responsible for emulating, using +software, all devices. + +EDK2 +---- + +EDK2, as known as `TianoCore <https://www.tianocore.org/>`_, is an open source +implementation of UEFI standard. QEMU virtual machines that boot a UEFI firmware +usually use EDK2. + +gdbstub +------- + +QEMU implements a `gdb server <GDB usage>`, allowing gdb to attach to it and +debug a running virtual machine, or a program in user-mode. This allows +debugging the guest code that is running inside QEMU. + +glib2 +----- + +`GLib2 <https://docs.gtk.org/glib/>`_ is one of the most important libraries we +are using through the codebase. It provides many data structures, macros, string +and thread utilities and portable functions across different OS. It's required +to build QEMU. + +Guest agent +----------- + +The `QEMU Guest Agent <qemu-ga>` is a daemon intended to be run within virtual +machines. It provides various services to help QEMU to interact with it. + +.. _guest: + +Guest +----- + +Guest is the architecture of the virtual machine, which is emulated. +See also :ref:`host`. + +Sometimes this is called the :ref:`target` architecture, but that term +can be ambiguous. + +.. _host: + +Host +---- + +Host is the architecture on which QEMU is running on, which is native. +See also :ref:`guest`. + +Hypervisor +---------- + +The formal definition of an hypervisor is a program or API than can be used to +manage a virtual machine. QEMU is a virtualizer, that interacts with various +hypervisors. + +In the context of QEMU, an hypervisor is an API, provided by the Host OS, +allowing to execute virtual machines. Linux implementation is KVM (and supports +Xen as well). For MacOS, it's HVF. Windows defines WHPX. And NetBSD provides +NVMM. + +.. _machine: + +Machine +------- + +QEMU's system emulation models many different types of hardware. A machine model +(sometimes called a board model) is the model of a complete virtual system with +RAM, one or more CPUs, and various devices. It can be selected with the option +``-machine`` of qemu-system. Our machine models can be found on this `page +<system-targets-ref>`. + +Migration +--------- + +QEMU can save and restore the execution of a virtual machine between different +host systems. This is provided by the `Migration framework<migration>`. + +NBD +--- + +The `QEMU Network Block Device server <qemu-nbd>` is a tool that can be used to +mount and access QEMU images, providing functionality similar to a loop device. + +Mailing List +------------ + +This is `where <https://wiki.qemu.org/Contribute/MailingLists>`_ all the +development happens! Changes are posted as series, that all developers can +review and share feedback for. + +For reporting issues, our `GitLab +<https://gitlab.com/qemu-project/qemu/-/issues>`_ tracker is the best place. + +.. _softmmu: + +MMU / softmmu +------------- + +The Memory Management Unit is responsible for translating virtual addresses to +physical addresses and managing memory protection. QEMU system mode is named +"softmmu" precisely because it implements this in software, including a TLB +(Translation lookaside buffer), for the guest virtual machine. + +QEMU user-mode does not implement a full software MMU, but "simply" translates +virtual addresses by adding a specific offset, and relying on host MMU/OS +instead. + +Monitor / QMP / HMP +------------------- + +The `QEMU Monitor <QEMU monitor>` is a text interface which can be used to interact +with a running virtual machine. + +QMP stands for QEMU Monitor Protocol and is a json based interface. +HMP stands for Human Monitor Protocol and is a set of text commands available +for users who prefer natural language to json. + +MTTCG +----- + +Multiple CPU support was first implemented using a round-robin algorithm +running on a single thread. Later on, `Multi-threaded TCG <mttcg>` was developed +to benefit from multiple cores to speed up execution. + +Plugins +------- + +`TCG Plugins <TCG Plugins>` is an API used to instrument guest code, in system +and user mode. The end goal is to have a similar set of functionality compared +to `DynamoRIO <https://dynamorio.org/>`_ or `valgrind <https://valgrind.org/>`_. + +One key advantage of QEMU plugins is that they can be used to perform +architecture agnostic instrumentation. + +Patchew +------- + +`Patchew <https://patchew.org/QEMU/>`_ is a website that tracks patches on the +Mailing List. + +PR +-- + +Once a series is reviewed and accepted by a subsystem maintainer, it will be +included in a PR (Pull Request) that the project maintainer will merge into QEMU +main branch, after running tests. + +The QEMU project doesn't currently expect most developers to directly submit +pull requests. + +QCOW2 +----- + +QEMU Copy On Write is a disk format developed by QEMU. It provides transparent +compression, automatic extension, and many other advantages over a raw image. + +qcow2 is the recommended format to use. + +QEMU +---- + +`QEMU (Quick Emulator) <https://www.qemu.org/>`_ is a generic and open source +machine emulator and virtualizer. + +QOM +--- + +`QEMU Object Model <qom>` is an object oriented API used to define various +devices and hardware in the QEMU codebase. + +Record/replay +------------- + +`Record/replay <replay>` is a feature of QEMU allowing to have a deterministic +and reproducible execution of a virtual machine. + +Rust +---- + +`A new programming language <https://www.rust-lang.org/>`_, memory safe by +default. There is a work in progress to integrate it in QEMU codebase for +various subsystems. + +System mode +----------- + +QEMU System mode provides a virtual model of an entire machine (CPU, memory and +emulated devices) to run a guest OS. In this mode the CPU may be fully emulated, +or it may work with a hypervisor such as KVM, Xen or Hypervisor.Framework to +allow the guest to run directly on the host CPU. + +QEMU System mode is called :ref:`softmmu <softmmu>` as well. + +.. _target: + +Target +------ + +The term "target" can be ambiguous. In most places in QEMU it is used as a +synonym for :ref:`guest`. For example the code for emulating Arm CPUs is in +``target/arm/``. However in the :ref:`TCG subsystem <tcg>` "target" refers to the +architecture which QEMU is running on, i.e. the :ref:`host`. + +TCG +--- + +TCG is the QEMU `Tiny Code Generator <tcg>`. It is the JIT (just-in-time) +compiler we use to emulate a guest CPU in software. + +It is one of the accelerators supported by QEMU, and supports a lot of +guest/host architectures. + +User mode +--------- + +QEMU User mode can launch processes compiled for one CPU on another CPU. In this +mode the CPU is always emulated. In this mode, QEMU translate system calls from +guest to host kernel. It is available for Linux and BSD. + +VirtIO +------ + +VirtIO is an open standard used to define and implement virtual devices with a +minimal overhead, defining a set of data structures and hypercalls (similar to +system calls, but targeting an hypervisor, which happens to be QEMU in our +case). It's designed to be more efficient than emulating a real device, by +minimizing the amount of interactions between a guest VM and its hypervisor. + +vhost-user +---------- + +`Vhost-user <vhost_user>` is an interface used to implement VirtIO devices +outside of QEMU itself. diff --git a/docs/index.rst b/docs/index.rst index 0b9ee9901d..5665de85ca 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -3,6 +3,8 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. +.. _documentation-root: + ================================ Welcome to QEMU's documentation! ================================ @@ -18,3 +20,4 @@ Welcome to QEMU's documentation! interop/index specs/index devel/index + glossary diff --git a/docs/interop/qemu-ga.rst b/docs/interop/qemu-ga.rst index 11f7bae460..d16cc1b9f0 100644 --- a/docs/interop/qemu-ga.rst +++ b/docs/interop/qemu-ga.rst @@ -1,3 +1,5 @@ +.. _qemu-ga: + QEMU Guest Agent ================ diff --git a/docs/sphinx/depfile.py b/docs/sphinx/depfile.py index e74be6af98..d3c774d28b 100644 --- a/docs/sphinx/depfile.py +++ b/docs/sphinx/depfile.py @@ -31,6 +31,9 @@ def get_infiles(env): for path in Path(static_path).rglob('*'): yield str(path) + # also include kdoc script + yield str(env.config.kerneldoc_bin[1]) + def write_depfile(app, exception): if exception: diff --git a/docs/system/arm/virt.rst b/docs/system/arm/virt.rst index 766a7455f0..0c9c2ce035 100644 --- a/docs/system/arm/virt.rst +++ b/docs/system/arm/virt.rst @@ -1,3 +1,5 @@ +.. _arm-virt: + 'virt' generic virtual platform (``virt``) ========================================== diff --git a/docs/system/images.rst b/docs/system/images.rst index d000bd6b6f..a5551173c9 100644 --- a/docs/system/images.rst +++ b/docs/system/images.rst @@ -82,4 +82,6 @@ VM snapshots currently have the following known limitations: - A few device drivers still have incomplete snapshot support so their state is not saved or restored properly (in particular USB). +.. _block-drivers: + .. include:: qemu-block-drivers.rst.inc diff --git a/docs/system/qemu-block-drivers.rst.inc b/docs/system/qemu-block-drivers.rst.inc index 384e95ba76..cfe1acb78a 100644 --- a/docs/system/qemu-block-drivers.rst.inc +++ b/docs/system/qemu-block-drivers.rst.inc @@ -500,6 +500,8 @@ What you should *never* do: - expect it to work when loadvm'ing - write to the FAT directory on the host system while accessing it with the guest system +.. _nbd: + NBD access ~~~~~~~~~~ diff --git a/docs/tools/qemu-nbd.rst b/docs/tools/qemu-nbd.rst index 329f44d989..4f21b7904a 100644 --- a/docs/tools/qemu-nbd.rst +++ b/docs/tools/qemu-nbd.rst @@ -1,3 +1,5 @@ +.. _qemu-nbd: + ===================================== QEMU Disk Network Block Device Server ===================================== diff --git a/docs/tools/qemu-storage-daemon.rst b/docs/tools/qemu-storage-daemon.rst index ea00149a63..35ab2d7807 100644 --- a/docs/tools/qemu-storage-daemon.rst +++ b/docs/tools/qemu-storage-daemon.rst @@ -1,3 +1,5 @@ +.. _storage-daemon: + =================== QEMU Storage Daemon =================== diff --git a/docs/user/main.rst b/docs/user/main.rst index 7a126ee809..80a77f0a0c 100644 --- a/docs/user/main.rst +++ b/docs/user/main.rst @@ -1,3 +1,5 @@ +.. _user-mode: + QEMU User space emulator ======================== @@ -42,6 +44,8 @@ QEMU was conceived so that ultimately it can emulate itself. Although it is not very useful, it is an important test to show the power of the emulator. +.. _linux-user-mode: + Linux User space emulator ------------------------- @@ -175,6 +179,8 @@ Other binaries * ``qemu-sparc64`` can execute some Sparc64 (Sparc64 CPU, 64 bit ABI) and SPARC32PLUS binaries (Sparc64 CPU, 32 bit ABI). +.. _bsd-user-mode: + BSD User space emulator ----------------------- diff --git a/gdbstub/system.c b/gdbstub/system.c index 2d9fdff2fe..8ce79fa88c 100644 --- a/gdbstub/system.c +++ b/gdbstub/system.c @@ -330,26 +330,27 @@ static void create_processes(GDBState *s) gdb_create_default_process(s); } -int gdbserver_start(const char *device) +bool gdbserver_start(const char *device, Error **errp) { Chardev *chr = NULL; Chardev *mon_chr; g_autoptr(GString) cs = g_string_new(device); if (!first_cpu) { - error_report("gdbstub: meaningless to attach gdb to a " - "machine without any CPU."); - return -1; + error_setg(errp, "gdbstub: meaningless to attach gdb to a " + "machine without any CPU."); + return false; } if (!gdb_supports_guest_debug()) { - error_report("gdbstub: current accelerator doesn't " - "support guest debugging"); - return -1; + error_setg(errp, "gdbstub: current accelerator doesn't " + "support guest debugging"); + return false; } if (cs->len == 0) { - return -1; + error_setg(errp, "gdbstub: missing connection string"); + return false; } trace_gdbstub_op_start(cs->str); @@ -374,7 +375,8 @@ int gdbserver_start(const char *device) */ chr = qemu_chr_new_noreplay("gdb", cs->str, true, NULL); if (!chr) { - return -1; + error_setg(errp, "gdbstub: couldn't create chardev"); + return false; } } @@ -406,7 +408,7 @@ int gdbserver_start(const char *device) gdbserver_system_state.mon_chr = mon_chr; gdb_syscall_reset(); - return 0; + return true; } static void register_types(void) diff --git a/gdbstub/user.c b/gdbstub/user.c index 0b4bfa9c48..c2bdfc3d49 100644 --- a/gdbstub/user.c +++ b/gdbstub/user.c @@ -13,6 +13,7 @@ #include "qemu/bitops.h" #include "qemu/cutils.h" #include "qemu/sockets.h" +#include "qapi/error.h" #include "exec/hwaddr.h" #include "exec/tb-flush.h" #include "exec/gdbstub.h" @@ -372,14 +373,14 @@ static bool gdb_accept_tcp(int gdb_fd) return true; } -static int gdbserver_open_port(int port) +static int gdbserver_open_port(int port, Error **errp) { struct sockaddr_in sockaddr; int fd, ret; fd = socket(PF_INET, SOCK_STREAM, 0); if (fd < 0) { - perror("socket"); + error_setg_errno(errp, errno, "Failed to create socket"); return -1; } qemu_set_cloexec(fd); @@ -391,13 +392,13 @@ static int gdbserver_open_port(int port) sockaddr.sin_addr.s_addr = 0; ret = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)); if (ret < 0) { - perror("bind"); + error_setg_errno(errp, errno, "Failed to bind socket"); close(fd); return -1; } ret = listen(fd, 1); if (ret < 0) { - perror("listen"); + error_setg_errno(errp, errno, "Failed to listen to socket"); close(fd); return -1; } @@ -405,31 +406,32 @@ static int gdbserver_open_port(int port) return fd; } -int gdbserver_start(const char *port_or_path) +bool gdbserver_start(const char *port_or_path, Error **errp) { int port = g_ascii_strtoull(port_or_path, NULL, 10); int gdb_fd; if (port > 0) { - gdb_fd = gdbserver_open_port(port); + gdb_fd = gdbserver_open_port(port, errp); } else { gdb_fd = gdbserver_open_socket(port_or_path); } if (gdb_fd < 0) { - return -1; + return false; } if (port > 0 && gdb_accept_tcp(gdb_fd)) { - return 0; + return true; } else if (gdb_accept_socket(gdb_fd)) { gdbserver_user_state.socket_path = g_strdup(port_or_path); - return 0; + return true; } /* gone wrong */ close(gdb_fd); - return -1; + error_setg(errp, "gdbstub: failed to accept connection"); + return false; } void gdbserver_fork_start(void) diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 1edc16f65c..cb79566cc5 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -243,7 +243,6 @@ static void cpu_common_initfn(Object *obj) cpu->cluster_index = UNASSIGNED_CLUSTER_INDEX; /* user-mode doesn't have configurable SMP topology */ /* the default value is changed by qemu_init_vcpu() for system-mode */ - cpu->nr_cores = 1; cpu->nr_threads = 1; cpu->cflags_next_tb = -1; diff --git a/hw/i386/kvm/clock.c b/hw/i386/kvm/clock.c index 63be508842..17443552e9 100644 --- a/hw/i386/kvm/clock.c +++ b/hw/i386/kvm/clock.c @@ -27,7 +27,6 @@ #include "qapi/error.h" #include <linux/kvm.h> -#include "standard-headers/asm-x86/kvm_para.h" #include "qom/object.h" #define TYPE_KVM_CLOCK "kvmclock" @@ -333,8 +332,8 @@ void kvmclock_create(bool create_always) assert(kvm_enabled()); if (create_always || - cpu->env.features[FEAT_KVM] & ((1ULL << KVM_FEATURE_CLOCKSOURCE) | - (1ULL << KVM_FEATURE_CLOCKSOURCE2))) { + cpu->env.features[FEAT_KVM] & (CPUID_KVM_CLOCK | + CPUID_KVM_CLOCK2)) { sysbus_create_simple(TYPE_KVM_CLOCK, -1, NULL); } } diff --git a/hw/i386/x86-common.c b/hw/i386/x86-common.c index 97b4f7d4a0..008496b5b8 100644 --- a/hw/i386/x86-common.c +++ b/hw/i386/x86-common.c @@ -248,9 +248,7 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, CPUX86State *env = &cpu->env; MachineState *ms = MACHINE(hotplug_dev); X86MachineState *x86ms = X86_MACHINE(hotplug_dev); - unsigned int smp_cores = ms->smp.cores; - unsigned int smp_threads = ms->smp.threads; - X86CPUTopoInfo topo_info; + X86CPUTopoInfo *topo_info = &env->topo_info; if (!object_dynamic_cast(OBJECT(cpu), ms->cpu_type)) { error_setg(errp, "Invalid CPU type, expected cpu type: '%s'", @@ -269,15 +267,13 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, } } - init_topo_info(&topo_info, x86ms); + init_topo_info(topo_info, x86ms); if (ms->smp.modules > 1) { - env->nr_modules = ms->smp.modules; set_bit(CPU_TOPOLOGY_LEVEL_MODULE, env->avail_cpu_topo); } if (ms->smp.dies > 1) { - env->nr_dies = ms->smp.dies; set_bit(CPU_TOPOLOGY_LEVEL_DIE, env->avail_cpu_topo); } @@ -329,17 +325,17 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, if (cpu->core_id < 0) { error_setg(errp, "CPU core-id is not set"); return; - } else if (cpu->core_id > (smp_cores - 1)) { + } else if (cpu->core_id > (ms->smp.cores - 1)) { error_setg(errp, "Invalid CPU core-id: %u must be in range 0:%u", - cpu->core_id, smp_cores - 1); + cpu->core_id, ms->smp.cores - 1); return; } if (cpu->thread_id < 0) { error_setg(errp, "CPU thread-id is not set"); return; - } else if (cpu->thread_id > (smp_threads - 1)) { + } else if (cpu->thread_id > (ms->smp.threads - 1)) { error_setg(errp, "Invalid CPU thread-id: %u must be in range 0:%u", - cpu->thread_id, smp_threads - 1); + cpu->thread_id, ms->smp.threads - 1); return; } @@ -348,12 +344,12 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, topo_ids.module_id = cpu->module_id; topo_ids.core_id = cpu->core_id; topo_ids.smt_id = cpu->thread_id; - cpu->apic_id = x86_apicid_from_topo_ids(&topo_info, &topo_ids); + cpu->apic_id = x86_apicid_from_topo_ids(topo_info, &topo_ids); } cpu_slot = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, &idx); if (!cpu_slot) { - x86_topo_ids_from_apicid(cpu->apic_id, &topo_info, &topo_ids); + x86_topo_ids_from_apicid(cpu->apic_id, topo_info, &topo_ids); error_setg(errp, "Invalid CPU [socket: %u, die: %u, module: %u, core: %u, thread: %u]" @@ -376,7 +372,7 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, /* TODO: move socket_id/core_id/thread_id checks into x86_cpu_realizefn() * once -smp refactoring is complete and there will be CPU private * CPUState::nr_cores and CPUState::nr_threads fields instead of globals */ - x86_topo_ids_from_apicid(cpu->apic_id, &topo_info, &topo_ids); + x86_topo_ids_from_apicid(cpu->apic_id, topo_info, &topo_ids); if (cpu->socket_id != -1 && cpu->socket_id != topo_ids.pkg_id) { error_setg(errp, "property socket-id: %u doesn't match set apic-id:" " 0x%x (socket-id: %u)", cpu->socket_id, cpu->apic_id, diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h index d73f424f56..0675b0b646 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -49,12 +49,18 @@ void gdb_unregister_coprocessor_all(CPUState *cpu); /** * gdbserver_start: start the gdb server * @port_or_device: connection spec for gdb + * @errp: error handle * * For CONFIG_USER this is either a tcp port or a path to a fifo. For * system emulation you can use a full chardev spec for your gdbserver * port. + * + * The error handle should be either &error_fatal (for start-up) or + * &error_warn (for QMP/HMP initiated sessions). + * + * Returns true when server successfully started. */ -int gdbserver_start(const char *port_or_device); +bool gdbserver_start(const char *port_or_device, Error **errp); /** * gdb_feature_builder_init() - Initialize GDBFeatureBuilder. diff --git a/include/exec/memory.h b/include/exec/memory.h index 9458e2801d..3ee1901b52 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -784,7 +784,6 @@ struct MemoryRegion { bool terminates; bool ram_device; bool enabled; - bool warning_printed; /* For reservations */ uint8_t vga_logging_count; MemoryRegion *alias; hwaddr alias_offset; @@ -1194,7 +1193,7 @@ static inline bool MemoryRegionSection_eq(MemoryRegionSection *a, MemoryRegionSection *memory_region_section_new_copy(MemoryRegionSection *s); /** - * memory_region_section_new_copy: Free a copied memory region section + * memory_region_section_free_copy: Free a copied memory region section * * Free a copy of a memory section created via memory_region_section_new_copy(). * properly dropping references on all relevant members. @@ -2510,7 +2509,7 @@ MemoryRegionSection memory_region_find(MemoryRegion *mr, void memory_global_dirty_log_sync(bool last_stage); /** - * memory_global_dirty_log_sync: synchronize the dirty log for all memory + * memory_global_after_dirty_log_sync: synchronize the dirty log for all memory * * Synchronizes the vCPUs with a thread that is reading the dirty bitmap. * This function must be called after the dirty log bitmap is cleared, and diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index c3ca0babcb..fb397cdfc5 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -407,7 +407,6 @@ struct qemu_work_item; * Under TCG this value is propagated to @tcg_cflags. * See TranslationBlock::TCG CF_CLUSTER_MASK. * @tcg_cflags: Pre-computed cflags for this cpu. - * @nr_cores: Number of cores within this CPU package. * @nr_threads: Number of threads within this CPU core. * @thread: Host thread details, only live once @created is #true * @sem: WIN32 only semaphore used only for qtest @@ -466,7 +465,6 @@ struct CPUState { CPUClass *cc; /*< public >*/ - int nr_cores; int nr_threads; struct QemuThread *thread; diff --git a/include/hw/i386/topology.h b/include/hw/i386/topology.h index b2c8bf2de1..f6380f1ed7 100644 --- a/include/hw/i386/topology.h +++ b/include/hw/i386/topology.h @@ -121,9 +121,10 @@ static inline unsigned apicid_pkg_offset(X86CPUTopoInfo *topo_info) } /* - * Make APIC ID for the CPU based on Pkg_ID, Core_ID, SMT_ID + * Make APIC ID for the CPU based on topology and IDs of each topology level. * - * The caller must make sure core_id < nr_cores and smt_id < nr_threads. + * The caller must make sure the ID of each level doesn't exceed the width of + * the level. */ static inline apic_id_t x86_apicid_from_topo_ids(X86CPUTopoInfo *topo_info, const X86CPUTopoIDs *topo_ids) @@ -202,4 +203,29 @@ static inline bool x86_has_extended_topo(unsigned long *topo_bitmap) test_bit(CPU_TOPOLOGY_LEVEL_DIE, topo_bitmap); } +static inline unsigned x86_module_per_pkg(X86CPUTopoInfo *topo_info) +{ + return topo_info->modules_per_die * topo_info->dies_per_pkg; +} + +static inline unsigned x86_cores_per_pkg(X86CPUTopoInfo *topo_info) +{ + return topo_info->cores_per_module * x86_module_per_pkg(topo_info); +} + +static inline unsigned x86_threads_per_pkg(X86CPUTopoInfo *topo_info) +{ + return topo_info->threads_per_core * x86_cores_per_pkg(topo_info); +} + +static inline unsigned x86_threads_per_module(X86CPUTopoInfo *topo_info) +{ + return topo_info->threads_per_core * topo_info->cores_per_module; +} + +static inline unsigned x86_threads_per_die(X86CPUTopoInfo *topo_info) +{ + return x86_threads_per_module(topo_info) * topo_info->modules_per_die; +} + #endif /* HW_I386_TOPOLOGY_H */ diff --git a/include/qemu/compiler.h b/include/qemu/compiler.h index c06954ccb4..d904408e5e 100644 --- a/include/qemu/compiler.h +++ b/include/qemu/compiler.h @@ -22,12 +22,7 @@ #define QEMU_EXTERN_C extern #endif -#if defined(_WIN32) && (defined(__x86_64__) || defined(__i386__)) -# define QEMU_PACKED __attribute__((gcc_struct, packed)) -#else -# define QEMU_PACKED __attribute__((packed)) -#endif - +#define QEMU_PACKED __attribute__((packed)) #define QEMU_ALIGNED(X) __attribute__((aligned(X))) #ifndef glue diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h index 0fba36ae02..3a850aa216 100644 --- a/include/qemu/qemu-plugin.h +++ b/include/qemu/qemu-plugin.h @@ -583,7 +583,7 @@ QEMU_PLUGIN_API bool qemu_plugin_mem_is_store(qemu_plugin_meminfo_t info); /** - * qemu_plugin_mem_get_mem_value() - return last value loaded/stored + * qemu_plugin_mem_get_value() - return last value loaded/stored * @info: opaque memory transaction handle * * Returns: memory value diff --git a/include/semihosting/console.h b/include/semihosting/console.h index bd78e5f03f..1c12e178ee 100644 --- a/include/semihosting/console.h +++ b/include/semihosting/console.h @@ -9,8 +9,6 @@ #ifndef SEMIHOST_CONSOLE_H #define SEMIHOST_CONSOLE_H -#include "cpu.h" - /** * qemu_semihosting_console_read: * @cs: CPUState diff --git a/include/semihosting/syscalls.h b/include/semihosting/syscalls.h index b5937c619a..6627c45fb2 100644 --- a/include/semihosting/syscalls.h +++ b/include/semihosting/syscalls.h @@ -9,6 +9,7 @@ #ifndef SEMIHOSTING_SYSCALLS_H #define SEMIHOSTING_SYSCALLS_H +#include "exec/cpu-defs.h" #include "gdbstub/syscalls.h" /* diff --git a/include/semihosting/uaccess.h b/include/semihosting/uaccess.h index c2fa5a655d..6bc90b12d6 100644 --- a/include/semihosting/uaccess.h +++ b/include/semihosting/uaccess.h @@ -19,41 +19,96 @@ #include "exec/tswap.h" #include "exec/page-protection.h" +/** + * get_user_u64: + * + * Returns: 0 on success, -1 on error. + */ #define get_user_u64(val, addr) \ ({ uint64_t val_ = 0; \ int ret_ = cpu_memory_rw_debug(env_cpu(env), (addr), \ &val_, sizeof(val_), 0); \ (val) = tswap64(val_); ret_; }) +/** + * get_user_u32: + * + * Returns: 0 on success, -1 on error. + */ #define get_user_u32(val, addr) \ ({ uint32_t val_ = 0; \ int ret_ = cpu_memory_rw_debug(env_cpu(env), (addr), \ &val_, sizeof(val_), 0); \ (val) = tswap32(val_); ret_; }) +/** + * get_user_u8: + * + * Returns: 0 on success, -1 on error. + */ #define get_user_u8(val, addr) \ ({ uint8_t val_ = 0; \ int ret_ = cpu_memory_rw_debug(env_cpu(env), (addr), \ &val_, sizeof(val_), 0); \ (val) = val_; ret_; }) +/** + * get_user_ual: + * + * Returns: 0 on success, -1 on error. + */ #define get_user_ual(arg, p) get_user_u32(arg, p) +/** + * put_user_u64: + * + * Returns: 0 on success, -1 on error. + */ #define put_user_u64(val, addr) \ ({ uint64_t val_ = tswap64(val); \ cpu_memory_rw_debug(env_cpu(env), (addr), &val_, sizeof(val_), 1); }) +/** + * put_user_u32: + * + * Returns: 0 on success, -1 on error. + */ #define put_user_u32(val, addr) \ ({ uint32_t val_ = tswap32(val); \ cpu_memory_rw_debug(env_cpu(env), (addr), &val_, sizeof(val_), 1); }) +/** + * put_user_ual: + * + * Returns: 0 on success, -1 on error. + */ #define put_user_ual(arg, p) put_user_u32(arg, p) +/** + * uaccess_lock_user: + * + * The returned pointer should be freed using uaccess_unlock_user(). + */ void *uaccess_lock_user(CPUArchState *env, target_ulong addr, target_ulong len, bool copy); +/** + * lock_user: + * + * The returned pointer should be freed using unlock_user(). + */ #define lock_user(type, p, len, copy) uaccess_lock_user(env, p, len, copy) +/** + * uaccess_lock_user_string: + * + * The returned string should be freed using uaccess_unlock_user(). + */ char *uaccess_lock_user_string(CPUArchState *env, target_ulong addr); +/** + * uaccess_lock_user_string: + * + * The returned string should be freed using unlock_user(). + */ #define lock_user_string(p) uaccess_lock_user_string(env, p) void uaccess_unlock_user(CPUArchState *env, void *p, diff --git a/linux-user/main.c b/linux-user/main.c index b97634a32d..7198fa0986 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -1023,11 +1023,7 @@ int main(int argc, char **argv, char **envp) target_cpu_copy_regs(env, regs); if (gdbstub) { - if (gdbserver_start(gdbstub) < 0) { - fprintf(stderr, "qemu: could not open gdbserver on %s\n", - gdbstub); - exit(EXIT_FAILURE); - } + gdbserver_start(gdbstub, &error_fatal); gdb_handlesig(cpu, 0, NULL, NULL, 0); } diff --git a/meson.build b/meson.build index d06f59095c..15a066043b 100644 --- a/meson.build +++ b/meson.build @@ -378,9 +378,9 @@ elif host_os == 'sunos' elif host_os == 'haiku' qemu_common_flags += ['-DB_USE_POSITIVE_POSIX_ERRORS', '-D_BSD_SOURCE', '-fPIC'] elif host_os == 'windows' - if not compiler.compiles('struct x { int y; } __attribute__((gcc_struct));', - args: '-Werror') - error('Your compiler does not support __attribute__((gcc_struct)) - please use GCC instead of Clang') + # plugins use delaylib, and clang needs to be used with lld to make it work. + if compiler.get_id() == 'clang' and compiler.get_linker_id() != 'ld.lld' + error('On windows, you need to use lld with clang - use msys2 clang64/clangarm64 env') endif endif diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index 80b2e5ff9f..0aa22e1ae2 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -285,7 +285,7 @@ void hmp_gdbserver(Monitor *mon, const QDict *qdict) device = "tcp::" DEFAULT_GDBSTUB_PORT; } - if (gdbserver_start(device) < 0) { + if (!gdbserver_start(device, &error_warn)) { monitor_printf(mon, "Could not open gdbserver on device '%s'\n", device); } else if (strcmp(device, "none") == 0) { diff --git a/plugins/meson.build b/plugins/meson.build index 98542e926f..d60be2a4d6 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -17,14 +17,15 @@ if not enable_modules capture: true, command: ['sed', '-ne', 's/^[[:space:]]*\\(qemu_.*\\);/_\\1/p', '@INPUT@']) emulator_link_args += ['-Wl,-exported_symbols_list,plugins/qemu-plugins-ld64.symbols'] + elif host_os == 'windows' and meson.get_compiler('c').get_id() == 'clang' + # LLVM/lld does not support exporting specific symbols. However, it works + # out of the box with dllexport/dllimport attribute we set in the code. else emulator_link_args += ['-Xlinker', '--dynamic-list=' + qemu_plugin_symbols.full_path()] endif endif if host_os == 'windows' - dlltool = find_program('dlltool', required: true) - # Generate a .lib file for plugins to link against. # First, create a .def file listing all the symbols a plugin should expect to have # available in qemu @@ -33,12 +34,27 @@ if host_os == 'windows' output: 'qemu_plugin_api.def', capture: true, command: ['sed', '-e', '0,/^/s//EXPORTS/; s/[{};]//g', '@INPUT@']) + # then use dlltool to assemble a delaylib. + # The delaylib will have an "imaginary" name (qemu.exe), that is used by the + # linker file we add with plugins (win32_linker.c) to identify that we want + # to find missing symbols in current program. + win32_qemu_plugin_api_link_flags = ['-Lplugins', '-lqemu_plugin_api'] + if meson.get_compiler('c').get_id() == 'clang' + # With LLVM/lld, delaylib is specified at link time (-delayload) + dlltool = find_program('llvm-dlltool', required: true) + dlltool_cmd = [dlltool, '-d', '@INPUT@', '-l', '@OUTPUT@', '-D', 'qemu.exe'] + win32_qemu_plugin_api_link_flags += ['-Wl,-delayload=qemu.exe'] + else + # With gcc/ld, delay lib is built with a specific delay parameter. + dlltool = find_program('dlltool', required: true) + dlltool_cmd = [dlltool, '--input-def', '@INPUT@', + '--output-delaylib', '@OUTPUT@', '--dllname', 'qemu.exe'] + endif win32_qemu_plugin_api_lib = configure_file( input: win32_plugin_def, output: 'libqemu_plugin_api.a', - command: [dlltool, '--input-def', '@INPUT@', - '--output-delaylib', '@OUTPUT@', '--dllname', 'qemu.exe'] + command: dlltool_cmd ) endif specific_ss.add(files( diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 18cc122951..994c2fc059 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -5,7 +5,7 @@ use core::ptr::{addr_of_mut, NonNull}; use std::{ ffi::CStr, - os::raw::{c_int, c_uchar, c_uint, c_void}, + os::raw::{c_int, c_uint, c_void}, }; use qemu_api::{ @@ -14,7 +14,7 @@ use qemu_api::{ irq::InterruptSource, prelude::*, qdev::DeviceImpl, - qom::ObjectImpl, + qom::{ClassInitImpl, ObjectImpl, ParentField}, }; use crate::{ @@ -33,27 +33,20 @@ const FBRD_MASK: u32 = 0x3f; /// QEMU sourced constant. pub const PL011_FIFO_DEPTH: u32 = 16; -#[derive(Clone, Copy, Debug)] -enum DeviceId { - #[allow(dead_code)] - Arm = 0, - Luminary, -} +#[derive(Clone, Copy)] +struct DeviceId(&'static [u8; 8]); impl std::ops::Index<hwaddr> for DeviceId { - type Output = c_uchar; + type Output = u8; fn index(&self, idx: hwaddr) -> &Self::Output { - match self { - Self::Arm => &Self::PL011_ID_ARM[idx as usize], - Self::Luminary => &Self::PL011_ID_LUMINARY[idx as usize], - } + &self.0[idx as usize] } } impl DeviceId { - const PL011_ID_ARM: [c_uchar; 8] = [0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1]; - const PL011_ID_LUMINARY: [c_uchar; 8] = [0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1]; + const ARM: Self = Self(&[0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1]); + const LUMINARY: Self = Self(&[0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1]); } // FIFOs use 32-bit indices instead of usize, for compatibility with @@ -86,7 +79,7 @@ impl std::ops::Index<u32> for Fifo { #[derive(Debug, qemu_api_macros::Object, qemu_api_macros::offsets)] /// PL011 Device Model in QEMU pub struct PL011State { - pub parent_obj: SysBusDevice, + pub parent_obj: ParentField<SysBusDevice>, pub iomem: MemoryRegion, #[doc(alias = "fr")] pub flags: registers::Flags, @@ -126,21 +119,33 @@ pub struct PL011State { pub clock: NonNull<Clock>, #[doc(alias = "migrate_clk")] pub migrate_clock: bool, - /// The byte string that identifies the device. - device_id: DeviceId, } qom_isa!(PL011State : SysBusDevice, DeviceState, Object); +pub struct PL011Class { + parent_class: <SysBusDevice as ObjectType>::Class, + /// The byte string that identifies the device. + device_id: DeviceId, +} + unsafe impl ObjectType for PL011State { - type Class = <SysBusDevice as ObjectType>::Class; + type Class = PL011Class; const TYPE_NAME: &'static CStr = crate::TYPE_PL011; } +impl ClassInitImpl<PL011Class> for PL011State { + fn class_init(klass: &mut PL011Class) { + klass.device_id = DeviceId::ARM; + <Self as ClassInitImpl<SysBusDeviceClass>>::class_init(&mut klass.parent_class); + } +} + impl ObjectImpl for PL011State { type ParentType = SysBusDevice; const INSTANCE_INIT: Option<unsafe fn(&mut Self)> = Some(Self::init); + const INSTANCE_POST_INIT: Option<fn(&Self)> = Some(Self::post_init); } impl DeviceImpl for PL011State { @@ -179,14 +184,6 @@ impl PL011State { Self::TYPE_NAME.as_ptr(), 0x1000, ); - - let sbd: &mut SysBusDevice = self.upcast_mut(); - sysbus_init_mmio(sbd, addr_of_mut!(self.iomem)); - } - - for irq in self.interrupts.iter() { - let sbd: &SysBusDevice = self.upcast(); - sbd.init_irq(irq); } // SAFETY: @@ -209,12 +206,20 @@ impl PL011State { } } + fn post_init(&self) { + self.init_mmio(&self.iomem); + for irq in self.interrupts.iter() { + self.init_irq(irq); + } + } + pub fn read(&mut self, offset: hwaddr, _size: c_uint) -> std::ops::ControlFlow<u64, u64> { use RegisterOffset::*; let value = match RegisterOffset::try_from(offset) { Err(v) if (0x3f8..0x400).contains(&(v >> 2)) => { - u32::from(self.device_id[(offset - 0xfe0) >> 2]) + let device_id = self.get_class().device_id; + u32::from(device_id[(offset - 0xfe0) >> 2]) } Err(_) => { // qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset); @@ -645,19 +650,13 @@ pub unsafe extern "C" fn pl011_create( #[derive(Debug, qemu_api_macros::Object)] /// PL011 Luminary device model. pub struct PL011Luminary { - parent_obj: PL011State, + parent_obj: ParentField<PL011State>, } -impl PL011Luminary { - /// Initializes a pre-allocated, unitialized instance of `PL011Luminary`. - /// - /// # Safety - /// - /// We expect the FFI user of this function to pass a valid pointer, that - /// has the same size as [`PL011Luminary`]. We also expect the device is - /// readable/writeable from one thread at any time. - unsafe fn init(&mut self) { - self.parent_obj.device_id = DeviceId::Luminary; +impl ClassInitImpl<PL011Class> for PL011Luminary { + fn class_init(klass: &mut PL011Class) { + klass.device_id = DeviceId::LUMINARY; + <Self as ClassInitImpl<SysBusDeviceClass>>::class_init(&mut klass.parent_class); } } @@ -670,8 +669,6 @@ unsafe impl ObjectType for PL011Luminary { impl ObjectImpl for PL011Luminary { type ParentType = PL011State; - - const INSTANCE_INIT: Option<unsafe fn(&mut Self)> = Some(Self::init); } impl DeviceImpl for PL011Luminary {} diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs index 69064d6929..0a89d393e0 100644 --- a/rust/hw/char/pl011/src/lib.rs +++ b/rust/hw/char/pl011/src/lib.rs @@ -45,7 +45,7 @@ pub const TYPE_PL011_LUMINARY: &::std::ffi::CStr = c_str!("pl011_luminary"); #[doc(alias = "offset")] #[allow(non_camel_case_types)] #[repr(u64)] -#[derive(Debug)] +#[derive(Debug, qemu_api_macros::TryInto)] pub enum RegisterOffset { /// Data Register /// @@ -102,32 +102,6 @@ pub enum RegisterOffset { //Reserved = 0x04C, } -impl core::convert::TryFrom<u64> for RegisterOffset { - type Error = u64; - - fn try_from(value: u64) -> Result<Self, Self::Error> { - macro_rules! case { - ($($discriminant:ident),*$(,)*) => { - /* check that matching on all macro arguments compiles, which means we are not - * missing any enum value; if the type definition ever changes this will stop - * compiling. - */ - const fn _assert_exhaustive(val: RegisterOffset) { - match val { - $(RegisterOffset::$discriminant => (),)* - } - } - - match value { - $(x if x == Self::$discriminant as u64 => Ok(Self::$discriminant),)* - _ => Err(value), - } - } - } - case! { DR, RSR, FR, FBRD, ILPR, IBRD, LCR_H, CR, FLS, IMSC, RIS, MIS, ICR, DMACR } - } -} - pub mod registers { //! Device registers exposed as typed structs which are backed by arbitrary //! integer bitmaps. [`Data`], [`Control`], [`LineControl`], etc. diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs index 74a8bc7503..7ec218202f 100644 --- a/rust/qemu-api-macros/src/lib.rs +++ b/rust/qemu-api-macros/src/lib.rs @@ -3,75 +3,81 @@ // SPDX-License-Identifier: GPL-2.0-or-later use proc_macro::TokenStream; -use proc_macro2::Span; -use quote::{quote, quote_spanned}; +use quote::quote; use syn::{ - parse_macro_input, parse_quote, punctuated::Punctuated, token::Comma, Data, DeriveInput, Field, - Fields, Ident, Type, Visibility, + parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned, token::Comma, Data, + DeriveInput, Field, Fields, Ident, Meta, Path, Token, Type, Variant, Visibility, }; -struct CompileError(String, Span); +mod utils; +use utils::MacroError; -impl From<CompileError> for proc_macro2::TokenStream { - fn from(err: CompileError) -> Self { - let CompileError(msg, span) = err; - quote_spanned! { span => compile_error!(#msg); } +fn get_fields<'a>( + input: &'a DeriveInput, + msg: &str, +) -> Result<&'a Punctuated<Field, Comma>, MacroError> { + if let Data::Struct(s) = &input.data { + if let Fields::Named(fs) = &s.fields { + Ok(&fs.named) + } else { + Err(MacroError::Message( + format!("Named fields required for {}", msg), + input.ident.span(), + )) + } + } else { + Err(MacroError::Message( + format!("Struct required for {}", msg), + input.ident.span(), + )) } } -fn is_c_repr(input: &DeriveInput, msg: &str) -> Result<(), CompileError> { +fn is_c_repr(input: &DeriveInput, msg: &str) -> Result<(), MacroError> { let expected = parse_quote! { #[repr(C)] }; if input.attrs.iter().any(|attr| attr == &expected) { Ok(()) } else { - Err(CompileError( + Err(MacroError::Message( format!("#[repr(C)] required for {}", msg), input.ident.span(), )) } } -#[proc_macro_derive(Object)] -pub fn derive_object(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as DeriveInput); - let name = input.ident; +fn derive_object_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream, MacroError> { + is_c_repr(&input, "#[derive(Object)]")?; + + let name = &input.ident; + let parent = &get_fields(&input, "#[derive(Object)]")?[0].ident; + + Ok(quote! { + ::qemu_api::assert_field_type!(#name, #parent, + ::qemu_api::qom::ParentField<<#name as ::qemu_api::qom::ObjectImpl>::ParentType>); - let expanded = quote! { ::qemu_api::module_init! { MODULE_INIT_QOM => unsafe { ::qemu_api::bindings::type_register_static(&<#name as ::qemu_api::qom::ObjectImpl>::TYPE_INFO); } } - }; - - TokenStream::from(expanded) + }) } -fn get_fields(input: &DeriveInput) -> Result<&Punctuated<Field, Comma>, CompileError> { - if let Data::Struct(s) = &input.data { - if let Fields::Named(fs) = &s.fields { - Ok(&fs.named) - } else { - Err(CompileError( - "Cannot generate offsets for unnamed fields.".to_string(), - input.ident.span(), - )) - } - } else { - Err(CompileError( - "Cannot generate offsets for union or enum.".to_string(), - input.ident.span(), - )) - } +#[proc_macro_derive(Object)] +pub fn derive_object(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let expanded = derive_object_or_error(input).unwrap_or_else(Into::into); + + TokenStream::from(expanded) } #[rustfmt::skip::macros(quote)] -fn derive_offsets_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream, CompileError> { +fn derive_offsets_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream, MacroError> { is_c_repr(&input, "#[derive(offsets)]")?; let name = &input.ident; - let fields = get_fields(&input)?; + let fields = get_fields(&input, "#[derive(offsets)]")?; let field_names: Vec<&Ident> = fields.iter().map(|f| f.ident.as_ref().unwrap()).collect(); let field_types: Vec<&Type> = fields.iter().map(|f| &f.ty).collect(); let field_vis: Vec<&Visibility> = fields.iter().map(|f| &f.vis).collect(); @@ -92,3 +98,73 @@ pub fn derive_offsets(input: TokenStream) -> TokenStream { TokenStream::from(expanded) } + +#[allow(non_snake_case)] +fn get_repr_uN(input: &DeriveInput, msg: &str) -> Result<Path, MacroError> { + let repr = input.attrs.iter().find(|attr| attr.path().is_ident("repr")); + if let Some(repr) = repr { + let nested = repr.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?; + for meta in nested { + match meta { + Meta::Path(path) if path.is_ident("u8") => return Ok(path), + Meta::Path(path) if path.is_ident("u16") => return Ok(path), + Meta::Path(path) if path.is_ident("u32") => return Ok(path), + Meta::Path(path) if path.is_ident("u64") => return Ok(path), + _ => {} + } + } + } + + Err(MacroError::Message( + format!("#[repr(u8/u16/u32/u64) required for {}", msg), + input.ident.span(), + )) +} + +fn get_variants(input: &DeriveInput) -> Result<&Punctuated<Variant, Comma>, MacroError> { + if let Data::Enum(e) = &input.data { + if let Some(v) = e.variants.iter().find(|v| v.fields != Fields::Unit) { + return Err(MacroError::Message( + "Cannot derive TryInto for enum with non-unit variants.".to_string(), + v.fields.span(), + )); + } + Ok(&e.variants) + } else { + Err(MacroError::Message( + "Cannot derive TryInto for union or struct.".to_string(), + input.ident.span(), + )) + } +} + +#[rustfmt::skip::macros(quote)] +fn derive_tryinto_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream, MacroError> { + let repr = get_repr_uN(&input, "#[derive(TryInto)]")?; + + let name = &input.ident; + let variants = get_variants(&input)?; + let discriminants: Vec<&Ident> = variants.iter().map(|f| &f.ident).collect(); + + Ok(quote! { + impl core::convert::TryFrom<#repr> for #name { + type Error = #repr; + + fn try_from(value: #repr) -> Result<Self, Self::Error> { + #(const #discriminants: #repr = #name::#discriminants as #repr;)*; + match value { + #(#discriminants => Ok(Self::#discriminants),)* + _ => Err(value), + } + } + } + }) +} + +#[proc_macro_derive(TryInto)] +pub fn derive_tryinto(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let expanded = derive_tryinto_or_error(input).unwrap_or_else(Into::into); + + TokenStream::from(expanded) +} diff --git a/rust/qemu-api-macros/src/utils.rs b/rust/qemu-api-macros/src/utils.rs new file mode 100644 index 0000000000..02c91aed7f --- /dev/null +++ b/rust/qemu-api-macros/src/utils.rs @@ -0,0 +1,26 @@ +// Procedural macro utilities. +// Author(s): Paolo Bonzini <pbonzini@redhat.com> +// SPDX-License-Identifier: GPL-2.0-or-later + +use proc_macro2::Span; +use quote::quote_spanned; + +pub enum MacroError { + Message(String, Span), + ParseError(syn::Error), +} + +impl From<syn::Error> for MacroError { + fn from(err: syn::Error) -> Self { + MacroError::ParseError(err) + } +} + +impl From<MacroError> for proc_macro2::TokenStream { + fn from(err: MacroError) -> Self { + match err { + MacroError::Message(msg, span) => quote_spanned! { span => compile_error!(#msg); }, + MacroError::ParseError(err) => err.into_compile_error(), + } + } +} diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index ccb20f38c1..60944a657d 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -7,7 +7,7 @@ if rustc.version().version_compare('>=1.77.0') _qemu_api_cfg += ['--cfg', 'has_offset_of'] endif if get_option('debug_mutex') - _qemu_api_cfg += ['--feature', 'debug_cell'] + _qemu_api_cfg += ['--cfg', 'feature="debug_cell"'] endif _qemu_api_rs = static_library( @@ -15,6 +15,7 @@ _qemu_api_rs = static_library( structured_sources( [ 'src/lib.rs', + 'src/assertions.rs', 'src/bindings.rs', 'src/bitops.rs', 'src/callbacks.rs', diff --git a/rust/qemu-api/src/assertions.rs b/rust/qemu-api/src/assertions.rs new file mode 100644 index 0000000000..6e42046980 --- /dev/null +++ b/rust/qemu-api/src/assertions.rs @@ -0,0 +1,90 @@ +// Copyright 2024, Red Hat Inc. +// Author(s): Paolo Bonzini <pbonzini@redhat.com> +// SPDX-License-Identifier: GPL-2.0-or-later + +//! This module provides macros to check the equality of types and +//! the type of `struct` fields. This can be useful to ensure that +//! types match the expectations of C code. + +// Based on https://stackoverflow.com/questions/64251852/x/70978292#70978292 +// (stackoverflow answers are released under MIT license). + +#[doc(hidden)] +pub trait EqType { + type Itself; +} + +impl<T> EqType for T { + type Itself = T; +} + +/// Assert that two types are the same. +/// +/// # Examples +/// +/// ``` +/// # use qemu_api::assert_same_type; +/// # use std::ops::Deref; +/// assert_same_type!(u32, u32); +/// assert_same_type!(<Box<u32> as Deref>::Target, u32); +/// ``` +/// +/// Different types will cause a compile failure +/// +/// ```compile_fail +/// # use qemu_api::assert_same_type; +/// assert_same_type!(&Box<u32>, &u32); +/// ``` +#[macro_export] +macro_rules! assert_same_type { + ($t1:ty, $t2:ty) => { + const _: () = { + #[allow(unused)] + fn assert_same_type(v: $t1) { + fn types_must_be_equal<T, U>(_: T) + where + T: $crate::assertions::EqType<Itself = U>, + { + } + types_must_be_equal::<_, $t2>(v); + } + }; + }; +} + +/// Assert that a field of a struct has the given type. +/// +/// # Examples +/// +/// ``` +/// # use qemu_api::assert_field_type; +/// pub struct A { +/// field1: u32, +/// } +/// +/// assert_field_type!(A, field1, u32); +/// ``` +/// +/// Different types will cause a compile failure +/// +/// ```compile_fail +/// # use qemu_api::assert_field_type; +/// # pub struct A { field1: u32 } +/// assert_field_type!(A, field1, i32); +/// ``` +#[macro_export] +macro_rules! assert_field_type { + ($t:ty, $i:tt, $ti:ty) => { + const _: () = { + #[allow(unused)] + fn assert_field_type(v: $t) { + fn types_must_be_equal<T, U>(_: T) + where + T: $crate::assertions::EqType<Itself = U>, + { + } + types_must_be_equal::<_, $ti>(v.$i); + } + }; + }; +} diff --git a/rust/qemu-api/src/irq.rs b/rust/qemu-api/src/irq.rs index 6258141bdf..378e520295 100644 --- a/rust/qemu-api/src/irq.rs +++ b/rust/qemu-api/src/irq.rs @@ -24,8 +24,7 @@ use crate::{ /// /// Interrupts are implemented as a pointer to the interrupt "sink", which has /// type [`IRQState`]. A device exposes its source as a QOM link property using -/// a function such as -/// [`SysBusDevice::init_irq`](crate::sysbus::SysBusDevice::init_irq), and +/// a function such as [`SysBusDeviceMethods::init_irq`], and /// initially leaves the pointer to a NULL value, representing an unconnected /// interrupt. To connect it, whoever creates the device fills the pointer with /// the sink's `IRQState *`, for example using `sysbus_connect_irq`. Because diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index 4b43e02c0f..83c6a987c0 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -12,6 +12,7 @@ pub mod bindings; #[rustfmt::skip] pub mod prelude; +pub mod assertions; pub mod bitops; pub mod c_str; pub mod callbacks; diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index 6f32deeb2e..4ea70b9c82 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -16,3 +16,5 @@ pub use crate::qom::ObjectMethods; pub use crate::qom::ObjectType; pub use crate::qom_isa; + +pub use crate::sysbus::SysBusDeviceMethods; diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index 7d5fbef1e1..97901fb908 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -55,6 +55,7 @@ use std::{ ffi::CStr, + fmt, ops::{Deref, DerefMut}, os::raw::c_void, }; @@ -105,6 +106,52 @@ macro_rules! qom_isa { }; } +/// This is the same as [`ManuallyDrop<T>`](std::mem::ManuallyDrop), though +/// it hides the standard methods of `ManuallyDrop`. +/// +/// The first field of an `ObjectType` must be of type `ParentField<T>`. +/// (Technically, this is only necessary if there is at least one Rust +/// superclass in the hierarchy). This is to ensure that the parent field is +/// dropped after the subclass; this drop order is enforced by the C +/// `object_deinit` function. +/// +/// # Examples +/// +/// ```ignore +/// #[repr(C)] +/// #[derive(qemu_api_macros::Object)] +/// pub struct MyDevice { +/// parent: ParentField<DeviceState>, +/// ... +/// } +/// ``` +#[derive(Debug)] +#[repr(transparent)] +pub struct ParentField<T: ObjectType>(std::mem::ManuallyDrop<T>); + +impl<T: ObjectType> Deref for ParentField<T> { + type Target = T; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<T: ObjectType> DerefMut for ParentField<T> { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl<T: fmt::Display + ObjectType> fmt::Display for ParentField<T> { + #[inline(always)] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + self.0.fmt(f) + } +} + unsafe extern "C" fn rust_instance_init<T: ObjectImpl>(obj: *mut Object) { // SAFETY: obj is an instance of T, since rust_instance_init<T> // is called from QOM core as the instance_init function @@ -116,11 +163,7 @@ unsafe extern "C" fn rust_instance_post_init<T: ObjectImpl>(obj: *mut Object) { // SAFETY: obj is an instance of T, since rust_instance_post_init<T> // is called from QOM core as the instance_post_init function // for class T - // - // FIXME: it's not really guaranteed that there are no backpointers to - // obj; it's quite possible that they have been created by instance_init(). - // The receiver should be &self, not &mut self. - T::INSTANCE_POST_INIT.unwrap()(unsafe { &mut *obj.cast::<T>() }) + T::INSTANCE_POST_INIT.unwrap()(unsafe { &*obj.cast::<T>() }) } unsafe extern "C" fn rust_class_init<T: ObjectType + ClassInitImpl<T::Class>>( @@ -133,6 +176,16 @@ unsafe extern "C" fn rust_class_init<T: ObjectType + ClassInitImpl<T::Class>>( T::class_init(unsafe { &mut *klass.cast::<T::Class>() }) } +unsafe extern "C" fn drop_object<T: ObjectImpl>(obj: *mut Object) { + // SAFETY: obj is an instance of T, since drop_object<T> is called + // from the QOM core function object_deinit() as the instance_finalize + // function for class T. Note that while object_deinit() will drop the + // superclass field separately after this function returns, `T` must + // implement the unsafe trait ObjectType; the safety rules for the + // trait mandate that the parent field is manually dropped. + unsafe { std::ptr::drop_in_place(obj.cast::<T>()) } +} + /// Trait exposed by all structs corresponding to QOM objects. /// /// # Safety @@ -151,11 +204,16 @@ unsafe extern "C" fn rust_class_init<T: ObjectType + ClassInitImpl<T::Class>>( /// /// - the struct must be `#[repr(C)]`; /// -/// - the first field of the struct must be of the instance struct corresponding -/// to the superclass, which is `ObjectImpl::ParentType` +/// - the first field of the struct must be of type +/// [`ParentField<T>`](ParentField), where `T` is the parent type +/// [`ObjectImpl::ParentType`] +/// +/// - the first field of the `Class` must be of the class struct corresponding +/// to the superclass, which is `ObjectImpl::ParentType::Class`. `ParentField` +/// is not needed here. /// -/// - likewise, the first field of the `Class` must be of the class struct -/// corresponding to the superclass, which is `ObjectImpl::ParentType::Class`. +/// In both cases, having a separate class type is not necessary if the subclass +/// does not add any field. pub unsafe trait ObjectType: Sized { /// The QOM class object corresponding to this struct. This is used /// to automatically generate a `class_init` method. @@ -384,13 +442,12 @@ impl<T: ObjectType> ObjectCastMut for &mut T {} /// Trait a type must implement to be registered with QEMU. pub trait ObjectImpl: ObjectType + ClassInitImpl<Self::Class> { - /// The parent of the type. This should match the first field of - /// the struct that implements `ObjectImpl`: + /// The parent of the type. This should match the first field of the + /// struct that implements `ObjectImpl`, minus the `ParentField<_>` wrapper. type ParentType: ObjectType; /// Whether the object can be instantiated const ABSTRACT: bool = false; - const INSTANCE_FINALIZE: Option<unsafe extern "C" fn(obj: *mut Object)> = None; /// Function that is called to initialize an object. The parent class will /// have already been initialized so the type is only responsible for @@ -402,7 +459,7 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl<Self::Class> { /// Function that is called to finish initialization of an object, once /// `INSTANCE_INIT` functions have been called. - const INSTANCE_POST_INIT: Option<fn(&mut Self)> = None; + const INSTANCE_POST_INIT: Option<fn(&Self)> = None; /// Called on descendent classes after all parent class initialization /// has occurred, but before the class itself is initialized. This @@ -426,7 +483,7 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl<Self::Class> { None => None, Some(_) => Some(rust_instance_post_init::<Self>), }, - instance_finalize: Self::INSTANCE_FINALIZE, + instance_finalize: Some(drop_object::<Self>), abstract_: Self::ABSTRACT, class_size: core::mem::size_of::<Self::Class>(), class_init: Some(rust_class_init::<Self>), diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs index 8193734bde..e6762b5c14 100644 --- a/rust/qemu-api/src/sysbus.rs +++ b/rust/qemu-api/src/sysbus.rs @@ -32,20 +32,33 @@ where } } -impl SysBusDevice { - /// Return `self` cast to a mutable pointer, for use in calls to C code. - const fn as_mut_ptr(&self) -> *mut SysBusDevice { - addr_of!(*self) as *mut _ +/// Trait for methods of [`SysBusDevice`] and its subclasses. +pub trait SysBusDeviceMethods: ObjectDeref +where + Self::Target: IsA<SysBusDevice>, +{ + /// Expose a memory region to the board so that it can give it an address + /// in guest memory. Note that the ordering of calls to `init_mmio` is + /// important, since whoever creates the sysbus device will refer to the + /// region with a number that corresponds to the order of calls to + /// `init_mmio`. + fn init_mmio(&self, iomem: &bindings::MemoryRegion) { + assert!(bql_locked()); + unsafe { + bindings::sysbus_init_mmio(self.as_mut_ptr(), addr_of!(*iomem) as *mut _); + } } /// Expose an interrupt source outside the device as a qdev GPIO output. /// Note that the ordering of calls to `init_irq` is important, since /// whoever creates the sysbus device will refer to the interrupts with /// a number that corresponds to the order of calls to `init_irq`. - pub fn init_irq(&self, irq: &InterruptSource) { + fn init_irq(&self, irq: &InterruptSource) { assert!(bql_locked()); unsafe { bindings::sysbus_init_irq(self.as_mut_ptr(), irq.as_ptr()); } } } + +impl<R: ObjectDeref> SysBusDeviceMethods for R where R::Target: IsA<SysBusDevice> {} diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index 1d2825b098..526c3f4f8e 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -15,7 +15,7 @@ use qemu_api::{ declare_properties, define_property, prelude::*, qdev::{DeviceImpl, DeviceState, Property}, - qom::ObjectImpl, + qom::{ObjectImpl, ParentField}, vmstate::VMStateDescription, zeroable::Zeroable, }; @@ -31,7 +31,7 @@ pub static VMSTATE: VMStateDescription = VMStateDescription { #[repr(C)] #[derive(qemu_api_macros::Object)] pub struct DummyState { - parent: DeviceState, + parent: ParentField<DeviceState>, migrate_clock: bool, } diff --git a/scripts/cocci-macro-file.h b/scripts/cocci-macro-file.h index d247a5086e..c64831d540 100644 --- a/scripts/cocci-macro-file.h +++ b/scripts/cocci-macro-file.h @@ -23,11 +23,7 @@ #define G_GNUC_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) #define G_GNUC_NULL_TERMINATED __attribute__((sentinel)) -#if defined(_WIN32) && (defined(__x86_64__) || defined(__i386__)) -# define QEMU_PACKED __attribute__((gcc_struct, packed)) -#else -# define QEMU_PACKED __attribute__((packed)) -#endif +#define QEMU_PACKED __attribute__((packed)) #define cat(x,y) x ## y #define cat2(x,y) cat(x,y) diff --git a/scripts/make-release b/scripts/make-release index 8dc939124c..2885e87210 100755 --- a/scripts/make-release +++ b/scripts/make-release @@ -10,6 +10,27 @@ # This work is licensed under the terms of the GNU GPLv2 or later. # See the COPYING file in the top-level directory. +function subproject_dir() { + if test ! -f "subprojects/$1.wrap"; then + error "scripts/archive-source.sh should only process wrap subprojects" + fi + + # Print the directory key of the wrap file, defaulting to the + # subproject name. The wrap file is in ini format and should + # have a single section only. There should be only one section + # named "[wrap-*]", which helps keeping the script simple. + local dir + dir=$(sed -n \ + -e '/^\[wrap-[a-z][a-z]*\]$/,/^\[/{' \ + -e '/^directory *= */!b' \ + -e 's///p' \ + -e 'q' \ + -e '}' \ + "subprojects/$1.wrap") + + echo "${dir:-$1}" +} + if [ $# -ne 2 ]; then echo "Usage:" echo " $0 gitrepo version" @@ -51,5 +72,13 @@ meson subprojects download $SUBPROJECTS CryptoPkg/Library/OpensslLib/openssl \ MdeModulePkg/Library/BrotliCustomDecompressLib/brotli) popd -tar --exclude=.git -cJf ${destination}.tar.xz ${destination} + +exclude=(--exclude=.git) +# include the tarballs in subprojects/packagecache but not their expansion +for sp in $SUBPROJECTS; do + if grep -xqF "[wrap-file]" subprojects/$sp.wrap; then + exclude+=(--exclude=subprojects/"$(subproject_dir $sp)") + fi +done +tar "${exclude[@]}" -cJf ${destination}.tar.xz ${destination} rm -rf ${destination} diff --git a/scripts/nsis.py b/scripts/nsis.py index 03ed7608a2..af4e064819 100644 --- a/scripts/nsis.py +++ b/scripts/nsis.py @@ -37,10 +37,10 @@ def find_deps(exe_or_dll, search_path, analyzed_deps): analyzed_deps.add(dep) # locate the dll dependencies recursively - rdeps = find_deps(dll, search_path, analyzed_deps) + analyzed_deps, rdeps = find_deps(dll, search_path, analyzed_deps) deps.extend(rdeps) - return deps + return analyzed_deps, deps def main(): parser = argparse.ArgumentParser(description="QEMU NSIS build helper.") @@ -92,18 +92,18 @@ def main(): dlldir = os.path.join(destdir + prefix, "dll") os.mkdir(dlldir) + analyzed_deps = set() for exe in glob.glob(os.path.join(destdir + prefix, "*.exe")): signcode(exe) # find all dll dependencies - deps = set(find_deps(exe, search_path, set())) + analyzed_deps, deps = find_deps(exe, search_path, analyzed_deps) + deps = set(deps) deps.remove(exe) # copy all dlls to the DLLDIR for dep in deps: dllfile = os.path.join(dlldir, os.path.basename(dep)) - if (os.path.exists(dllfile)): - continue print("Copying '%s' to '%s'" % (dep, dllfile)) shutil.copy(dep, dllfile) diff --git a/scripts/rust/rustc_args.py b/scripts/rust/rustc_args.py index 5525b3886f..2633157df2 100644 --- a/scripts/rust/rustc_args.py +++ b/scripts/rust/rustc_args.py @@ -215,6 +215,8 @@ def main() -> None: if rustc_version >= (1, 80): if args.lints: + print("--check-cfg") + print("cfg(test)") for cfg in sorted(cargo_toml.check_cfg): print("--check-cfg") print(cfg) diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index d78c6428b9..86e5260e50 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -166,6 +166,7 @@ static LayoutInfo common_semi_find_bases(CPUState *cs) #endif +#include "cpu.h" #include "common-semi-target.h" /* diff --git a/semihosting/console.c b/semihosting/console.c index 60102bbab6..c3683a1566 100644 --- a/semihosting/console.c +++ b/semihosting/console.c @@ -18,14 +18,15 @@ #include "qemu/osdep.h" #include "semihosting/semihost.h" #include "semihosting/console.h" +#include "exec/cpu-common.h" #include "exec/gdbstub.h" -#include "exec/exec-all.h" #include "qemu/log.h" #include "chardev/char.h" #include "chardev/char-fe.h" #include "qemu/main-loop.h" #include "qapi/error.h" #include "qemu/fifo8.h" +#include "hw/core/cpu.h" /* Access to this structure is protected by the BQL */ typedef struct SemihostingConsole { diff --git a/semihosting/meson.build b/semihosting/meson.build index 34933e5a19..86f5004bed 100644 --- a/semihosting/meson.build +++ b/semihosting/meson.build @@ -4,13 +4,16 @@ specific_ss.add(when: 'CONFIG_SEMIHOSTING', if_true: files( )) specific_ss.add(when: ['CONFIG_SEMIHOSTING', 'CONFIG_SYSTEM_ONLY'], if_true: files( - 'config.c', - 'console.c', 'uaccess.c', )) common_ss.add(when: ['CONFIG_SEMIHOSTING', 'CONFIG_SYSTEM_ONLY'], if_false: files('stubs-all.c')) -system_ss.add(when: ['CONFIG_SEMIHOSTING'], if_false: files('stubs-system.c')) +system_ss.add(when: ['CONFIG_SEMIHOSTING'], if_true: files( + 'config.c', + 'console.c', +), if_false: files( + 'stubs-system.c', +)) specific_ss.add(when: ['CONFIG_ARM_COMPATIBLE_SEMIHOSTING'], if_true: files('arm-compat-semi.c')) diff --git a/semihosting/syscalls.c b/semihosting/syscalls.c index c40348f996..f6451d9bb0 100644 --- a/semihosting/syscalls.c +++ b/semihosting/syscalls.c @@ -7,6 +7,7 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "cpu.h" #include "gdbstub/syscalls.h" #include "semihosting/guestfd.h" @@ -287,6 +288,7 @@ static void host_open(CPUState *cs, gdb_syscall_complete_cb complete, ret = open(p, host_flags, mode); if (ret < 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: failed to open %s\n", __func__, p); complete(cs, -1, errno); } else { int guestfd = alloc_guestfd(); diff --git a/semihosting/uaccess.c b/semihosting/uaccess.c index dc587d73bc..382a366ce3 100644 --- a/semihosting/uaccess.c +++ b/semihosting/uaccess.c @@ -8,6 +8,7 @@ */ #include "qemu/osdep.h" +#include "exec/cpu-all.h" #include "exec/exec-all.h" #include "semihosting/uaccess.h" diff --git a/subprojects/arbitrary-int-1-rs.wrap b/subprojects/arbitrary-int-1-rs.wrap index e580538a87..a1838b20b0 100644 --- a/subprojects/arbitrary-int-1-rs.wrap +++ b/subprojects/arbitrary-int-1-rs.wrap @@ -5,3 +5,6 @@ source_filename = arbitrary-int-1.2.7.tar.gz source_hash = c84fc003e338a6f69fbd4f7fe9f92b535ff13e9af8997f3b14b6ddff8b1df46d #method = cargo patch_directory = arbitrary-int-1-rs + +# bump this version number on every change to meson.build or the patches: +# v2 diff --git a/subprojects/bilge-0.2-rs.wrap b/subprojects/bilge-0.2-rs.wrap index 7a4339d298..900bb1497b 100644 --- a/subprojects/bilge-0.2-rs.wrap +++ b/subprojects/bilge-0.2-rs.wrap @@ -5,3 +5,6 @@ source_filename = bilge-0.2.0.tar.gz source_hash = dc707ed8ebf81de5cd6c7f48f54b4c8621760926cdf35a57000747c512e67b57 #method = cargo patch_directory = bilge-0.2-rs + +# bump this version number on every change to meson.build or the patches: +# v2 diff --git a/subprojects/bilge-impl-0.2-rs.wrap b/subprojects/bilge-impl-0.2-rs.wrap index b24c34a904..d14c3dc769 100644 --- a/subprojects/bilge-impl-0.2-rs.wrap +++ b/subprojects/bilge-impl-0.2-rs.wrap @@ -6,3 +6,6 @@ source_hash = feb11e002038ad243af39c2068c8a72bcf147acf05025dcdb916fcc000adb2d8 #method = cargo patch_directory = bilge-impl-0.2-rs diff_files = bilge-impl-1.63.0.patch + +# bump this version number on every change to meson.build or the patches: +# v2 diff --git a/subprojects/either-1-rs.wrap b/subprojects/either-1-rs.wrap index 6046712036..352e11cfee 100644 --- a/subprojects/either-1-rs.wrap +++ b/subprojects/either-1-rs.wrap @@ -5,3 +5,6 @@ source_filename = either-1.12.0.tar.gz source_hash = 3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b #method = cargo patch_directory = either-1-rs + +# bump this version number on every change to meson.build or the patches: +# v2 diff --git a/subprojects/itertools-0.11-rs.wrap b/subprojects/itertools-0.11-rs.wrap index 66b05252cd..ee12d0053b 100644 --- a/subprojects/itertools-0.11-rs.wrap +++ b/subprojects/itertools-0.11-rs.wrap @@ -5,3 +5,6 @@ source_filename = itertools-0.11.0.tar.gz source_hash = b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57 #method = cargo patch_directory = itertools-0.11-rs + +# bump this version number on every change to meson.build or the patches: +# v2 diff --git a/subprojects/libvhost-user/libvhost-user.h b/subprojects/libvhost-user/libvhost-user.h index deb40e77b3..2ffc58c11b 100644 --- a/subprojects/libvhost-user/libvhost-user.h +++ b/subprojects/libvhost-user/libvhost-user.h @@ -186,11 +186,7 @@ typedef struct VhostUserShared { unsigned char uuid[UUID_LEN]; } VhostUserShared; -#if defined(_WIN32) && (defined(__x86_64__) || defined(__i386__)) -# define VU_PACKED __attribute__((gcc_struct, packed)) -#else -# define VU_PACKED __attribute__((packed)) -#endif +#define VU_PACKED __attribute__((packed)) typedef struct VhostUserMsg { int request; diff --git a/subprojects/packagefiles/arbitrary-int-1-rs/meson.build b/subprojects/packagefiles/arbitrary-int-1-rs/meson.build index cff3f62ce7..00733d1faa 100644 --- a/subprojects/packagefiles/arbitrary-int-1-rs/meson.build +++ b/subprojects/packagefiles/arbitrary-int-1-rs/meson.build @@ -9,6 +9,7 @@ _arbitrary_int_rs = static_library( files('src/lib.rs'), gnu_symbol_visibility: 'hidden', override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_args: ['--cap-lints', 'allow'], rust_abi: 'rust', dependencies: [], ) diff --git a/subprojects/packagefiles/bilge-0.2-rs/meson.build b/subprojects/packagefiles/bilge-0.2-rs/meson.build index e69bac91b4..ce13d0fe80 100644 --- a/subprojects/packagefiles/bilge-0.2-rs/meson.build +++ b/subprojects/packagefiles/bilge-0.2-rs/meson.build @@ -17,6 +17,7 @@ lib = static_library( 'src/lib.rs', override_options : ['rust_std=2021', 'build.rust_std=2021'], rust_abi : 'rust', + rust_args: ['--cap-lints', 'allow'], dependencies: [ arbitrary_int_dep, bilge_impl_dep, diff --git a/subprojects/packagefiles/bilge-impl-0.2-rs/meson.build b/subprojects/packagefiles/bilge-impl-0.2-rs/meson.build index f8f3486fc0..42b03dcd53 100644 --- a/subprojects/packagefiles/bilge-impl-0.2-rs/meson.build +++ b/subprojects/packagefiles/bilge-impl-0.2-rs/meson.build @@ -25,6 +25,7 @@ _bilge_impl_rs = rust.proc_macro( files('src/lib.rs'), override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_args: [ + '--cap-lints', 'allow', '--cfg', 'use_fallback', '--cfg', 'feature="syn-error"', '--cfg', 'feature="proc-macro"', diff --git a/subprojects/packagefiles/either-1-rs/meson.build b/subprojects/packagefiles/either-1-rs/meson.build index 608e64e31f..04c96cc5fb 100644 --- a/subprojects/packagefiles/either-1-rs/meson.build +++ b/subprojects/packagefiles/either-1-rs/meson.build @@ -11,6 +11,7 @@ _either_rs = static_library( override_options: ['rust_std=2018', 'build.rust_std=2018'], rust_abi: 'rust', rust_args: [ + '--cap-lints', 'allow', '--cfg', 'feature="use_std"', '--cfg', 'feature="use_alloc"', ], diff --git a/subprojects/packagefiles/itertools-0.11-rs/meson.build b/subprojects/packagefiles/itertools-0.11-rs/meson.build index 30982a4ee7..2a3fbe9ee5 100644 --- a/subprojects/packagefiles/itertools-0.11-rs/meson.build +++ b/subprojects/packagefiles/itertools-0.11-rs/meson.build @@ -15,6 +15,7 @@ _itertools_rs = static_library( override_options: ['rust_std=2018', 'build.rust_std=2018'], rust_abi: 'rust', rust_args: [ + '--cap-lints', 'allow', '--cfg', 'feature="use_std"', '--cfg', 'feature="use_alloc"', ], diff --git a/subprojects/packagefiles/proc-macro-error-1-rs/meson.build b/subprojects/packagefiles/proc-macro-error-1-rs/meson.build index ae27a69686..10c2741085 100644 --- a/subprojects/packagefiles/proc-macro-error-1-rs/meson.build +++ b/subprojects/packagefiles/proc-macro-error-1-rs/meson.build @@ -20,6 +20,7 @@ _proc_macro_error_rs = static_library( override_options: ['rust_std=2018', 'build.rust_std=2018'], rust_abi: 'rust', rust_args: [ + '--cap-lints', 'allow', '--cfg', 'use_fallback', '--cfg', 'feature="syn-error"', '--cfg', 'feature="proc-macro"', diff --git a/subprojects/packagefiles/proc-macro-error-attr-1-rs/meson.build b/subprojects/packagefiles/proc-macro-error-attr-1-rs/meson.build index 3281b26433..c4c4c5e397 100644 --- a/subprojects/packagefiles/proc-macro-error-attr-1-rs/meson.build +++ b/subprojects/packagefiles/proc-macro-error-attr-1-rs/meson.build @@ -16,6 +16,7 @@ _proc_macro_error_attr_rs = rust.proc_macro( files('src/lib.rs'), override_options: ['rust_std=2018', 'build.rust_std=2018'], rust_args: [ + '--cap-lints', 'allow', '--cfg', 'use_fallback', '--cfg', 'feature="syn-error"', '--cfg', 'feature="proc-macro"' diff --git a/subprojects/packagefiles/proc-macro2-1-rs/meson.build b/subprojects/packagefiles/proc-macro2-1-rs/meson.build index f9c8675eba..5759df3ecc 100644 --- a/subprojects/packagefiles/proc-macro2-1-rs/meson.build +++ b/subprojects/packagefiles/proc-macro2-1-rs/meson.build @@ -15,6 +15,7 @@ _proc_macro2_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', rust_args: [ + '--cap-lints', 'allow', '--cfg', 'feature="proc-macro"', '--cfg', 'no_literal_byte_character', '--cfg', 'no_literal_c_string', diff --git a/subprojects/packagefiles/quote-1-rs/meson.build b/subprojects/packagefiles/quote-1-rs/meson.build index 7f7792569b..bf41fad99b 100644 --- a/subprojects/packagefiles/quote-1-rs/meson.build +++ b/subprojects/packagefiles/quote-1-rs/meson.build @@ -15,6 +15,7 @@ _quote_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', rust_args: [ + '--cap-lints', 'allow', '--cfg', 'feature="proc-macro"', ], dependencies: [ diff --git a/subprojects/packagefiles/syn-2-rs/meson.build b/subprojects/packagefiles/syn-2-rs/meson.build index 2c62cf7e1b..a009417408 100644 --- a/subprojects/packagefiles/syn-2-rs/meson.build +++ b/subprojects/packagefiles/syn-2-rs/meson.build @@ -19,6 +19,7 @@ _syn_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', rust_args: [ + '--cap-lints', 'allow', '--cfg', 'feature="full"', '--cfg', 'feature="derive"', '--cfg', 'feature="parsing"', diff --git a/subprojects/packagefiles/unicode-ident-1-rs/meson.build b/subprojects/packagefiles/unicode-ident-1-rs/meson.build index 9d76ebbd1a..11a5dab97d 100644 --- a/subprojects/packagefiles/unicode-ident-1-rs/meson.build +++ b/subprojects/packagefiles/unicode-ident-1-rs/meson.build @@ -10,6 +10,7 @@ _unicode_ident_rs = static_library( gnu_symbol_visibility: 'hidden', override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', + rust_args: ['--cap-lints', 'allow'], dependencies: [], native: true, ) diff --git a/subprojects/proc-macro-error-1-rs.wrap b/subprojects/proc-macro-error-1-rs.wrap index b7db03b06a..59f892f782 100644 --- a/subprojects/proc-macro-error-1-rs.wrap +++ b/subprojects/proc-macro-error-1-rs.wrap @@ -5,3 +5,6 @@ source_filename = proc-macro-error-1.0.4.tar.gz source_hash = da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c #method = cargo patch_directory = proc-macro-error-1-rs + +# bump this version number on every change to meson.build or the patches: +# v2 diff --git a/subprojects/proc-macro-error-attr-1-rs.wrap b/subprojects/proc-macro-error-attr-1-rs.wrap index d13d8a239a..5aeb224a10 100644 --- a/subprojects/proc-macro-error-attr-1-rs.wrap +++ b/subprojects/proc-macro-error-attr-1-rs.wrap @@ -5,3 +5,6 @@ source_filename = proc-macro-error-attr-1.0.4.tar.gz source_hash = a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869 #method = cargo patch_directory = proc-macro-error-attr-1-rs + +# bump this version number on every change to meson.build or the patches: +# v2 diff --git a/subprojects/proc-macro2-1-rs.wrap b/subprojects/proc-macro2-1-rs.wrap index 7053e2c013..6c9369f0df 100644 --- a/subprojects/proc-macro2-1-rs.wrap +++ b/subprojects/proc-macro2-1-rs.wrap @@ -5,3 +5,6 @@ source_filename = proc-macro2-1.0.84.0.tar.gz source_hash = ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6 #method = cargo patch_directory = proc-macro2-1-rs + +# bump this version number on every change to meson.build or the patches: +# v2 diff --git a/subprojects/quote-1-rs.wrap b/subprojects/quote-1-rs.wrap index 6e7ea69049..8b721dfa00 100644 --- a/subprojects/quote-1-rs.wrap +++ b/subprojects/quote-1-rs.wrap @@ -5,3 +5,6 @@ source_filename = quote-1.0.36.0.tar.gz source_hash = 0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7 #method = cargo patch_directory = quote-1-rs + +# bump this version number on every change to meson.build or the patches: +# v2 diff --git a/subprojects/syn-2-rs.wrap b/subprojects/syn-2-rs.wrap index 13ffdac3c3..d79cf750fb 100644 --- a/subprojects/syn-2-rs.wrap +++ b/subprojects/syn-2-rs.wrap @@ -5,3 +5,6 @@ source_filename = syn-2.0.66.0.tar.gz source_hash = c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5 #method = cargo patch_directory = syn-2-rs + +# bump this version number on every change to meson.build or the patches: +# v2 diff --git a/subprojects/unicode-ident-1-rs.wrap b/subprojects/unicode-ident-1-rs.wrap index 4609f96ed9..50988f612e 100644 --- a/subprojects/unicode-ident-1-rs.wrap +++ b/subprojects/unicode-ident-1-rs.wrap @@ -5,3 +5,6 @@ source_filename = unicode-ident-1.0.12.tar.gz source_hash = 3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b #method = cargo patch_directory = unicode-ident-1-rs + +# bump this version number on every change to meson.build or the patches: +# v2 diff --git a/subprojects/unicode-ident-1-rs/meson.build b/subprojects/unicode-ident-1-rs/meson.build deleted file mode 100644 index 54f2376854..0000000000 --- a/subprojects/unicode-ident-1-rs/meson.build +++ /dev/null @@ -1,20 +0,0 @@ -project('unicode-ident-1-rs', 'rust', - version: '1.0.12', - license: '(MIT OR Apache-2.0) AND Unicode-DFS-2016', - default_options: []) - -_unicode_ident_rs = static_library( - 'unicode_ident', - files('src/lib.rs'), - gnu_symbol_visibility: 'hidden', - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - dependencies: [], - native: true, -) - -unicode_ident_dep = declare_dependency( - link_with: _unicode_ident_rs, -) - -meson.override_dependency('unicode-ident-1-rs', unicode_ident_dep, native: true) diff --git a/system/cpus.c b/system/cpus.c index 99f83806c1..37e5892c24 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -687,7 +687,6 @@ void qemu_init_vcpu(CPUState *cpu) { MachineState *ms = MACHINE(qdev_get_machine()); - cpu->nr_cores = machine_topo_get_cores_per_socket(ms); cpu->nr_threads = ms->smp.threads; cpu->stopped = true; cpu->random_seed = qemu_guest_random_seed_thread_part1(); diff --git a/system/vl.c b/system/vl.c index be029c52ef..c567826718 100644 --- a/system/vl.c +++ b/system/vl.c @@ -811,29 +811,15 @@ static void configure_msg(QemuOpts *opts) /***********************************************************/ /* USB devices */ -static int usb_device_add(const char *devname) +static bool usb_parse(const char *cmdline, Error **errp) { - USBDevice *dev = NULL; + g_assert(machine_usb(current_machine)); - if (!machine_usb(current_machine)) { - return -1; - } - - dev = usbdevice_create(devname); - if (!dev) - return -1; - - return 0; -} - -static int usb_parse(const char *cmdline) -{ - int r; - r = usb_device_add(cmdline); - if (r < 0) { - error_report("could not add USB device '%s'", cmdline); + if (!usbdevice_create(cmdline)) { + error_setg(errp, "could not add USB device '%s'", cmdline); + return false; } - return r; + return true; } /***********************************************************/ @@ -1307,22 +1293,27 @@ static void add_device_config(int type, const char *cmdline) QTAILQ_INSERT_TAIL(&device_configs, conf, next); } -static int foreach_device_config(int type, int (*func)(const char *cmdline)) +/** + * foreach_device_config_or_exit(): process per-device configs + * @type: device_config type + * @func: device specific config function, returning pass/fail + * + * @func is called with the &error_fatal handler so device specific + * error messages can be reported on failure. + */ +static void foreach_device_config_or_exit(int type, + bool (*func)(const char *cmdline, + Error **errp)) { struct device_config *conf; - int rc; QTAILQ_FOREACH(conf, &device_configs, next) { if (conf->type != type) continue; loc_push_restore(&conf->loc); - rc = func(conf->cmdline); + func(conf->cmdline, &error_fatal); loc_pop(&conf->loc); - if (rc) { - return rc; - } } - return 0; } static void qemu_disable_default_devices(void) @@ -1452,7 +1443,7 @@ static void qemu_create_default_devices(void) } } -static int serial_parse(const char *devname) +static bool serial_parse(const char *devname, Error **errp) { int index = num_serial_hds; @@ -1467,13 +1458,13 @@ static int serial_parse(const char *devname) serial_hds[index] = qemu_chr_new_mux_mon(label, devname, NULL); if (!serial_hds[index]) { - error_report("could not connect serial device" - " to character backend '%s'", devname); - return -1; + error_setg(errp, "could not connect serial device" + " to character backend '%s'", devname); + return false; } } num_serial_hds++; - return 0; + return true; } Chardev *serial_hd(int i) @@ -1485,44 +1476,44 @@ Chardev *serial_hd(int i) return NULL; } -static int parallel_parse(const char *devname) +static bool parallel_parse(const char *devname, Error **errp) { static int index = 0; char label[32]; if (strcmp(devname, "none") == 0) - return 0; + return true; if (index == MAX_PARALLEL_PORTS) { - error_report("too many parallel ports"); - exit(1); + error_setg(errp, "too many parallel ports"); + return false; } snprintf(label, sizeof(label), "parallel%d", index); parallel_hds[index] = qemu_chr_new_mux_mon(label, devname, NULL); if (!parallel_hds[index]) { - error_report("could not connect parallel device" - " to character backend '%s'", devname); - return -1; + error_setg(errp, "could not connect parallel device" + " to character backend '%s'", devname); + return false; } index++; - return 0; + return true; } -static int debugcon_parse(const char *devname) +static bool debugcon_parse(const char *devname, Error **errp) { QemuOpts *opts; if (!qemu_chr_new_mux_mon("debugcon", devname, NULL)) { - error_report("invalid character backend '%s'", devname); - exit(1); + error_setg(errp, "invalid character backend '%s'", devname); + return false; } opts = qemu_opts_create(qemu_find_opts("device"), "debugcon", 1, NULL); if (!opts) { - error_report("already have a debugcon device"); - exit(1); + error_setg(errp, "already have a debugcon device"); + return false; } qemu_opt_set(opts, "driver", "isa-debugcon", &error_abort); qemu_opt_set(opts, "chardev", "debugcon", &error_abort); - return 0; + return true; } static gint machine_class_cmp(gconstpointer a, gconstpointer b) @@ -2044,12 +2035,9 @@ static void qemu_create_late_backends(void) qemu_opts_foreach(qemu_find_opts("mon"), mon_init_func, NULL, &error_fatal); - if (foreach_device_config(DEV_SERIAL, serial_parse) < 0) - exit(1); - if (foreach_device_config(DEV_PARALLEL, parallel_parse) < 0) - exit(1); - if (foreach_device_config(DEV_DEBUGCON, debugcon_parse) < 0) - exit(1); + foreach_device_config_or_exit(DEV_SERIAL, serial_parse); + foreach_device_config_or_exit(DEV_PARALLEL, parallel_parse); + foreach_device_config_or_exit(DEV_DEBUGCON, debugcon_parse); /* now chardevs have been created we may have semihosting to connect */ qemu_semihosting_chardev_init(); @@ -2667,8 +2655,7 @@ static void qemu_create_cli_devices(void) /* init USB devices */ if (machine_usb(current_machine)) { - if (foreach_device_config(DEV_USB, usb_parse) < 0) - exit(1); + foreach_device_config_or_exit(DEV_USB, usb_parse); } /* init generic devices */ @@ -2715,10 +2702,8 @@ static bool qemu_machine_creation_done(Error **errp) exit(1); } - if (foreach_device_config(DEV_GDB, gdbserver_start) < 0) { - error_setg(errp, "could not start gdbserver"); - return false; - } + foreach_device_config_or_exit(DEV_GDB, gdbserver_start); + if (!vga_interface_created && !default_vga && vga_interface_type != VGA_NONE) { warn_report("A -vga option was passed but this machine " diff --git a/target/i386/confidential-guest.h b/target/i386/confidential-guest.h index 0afb8317b5..164be7633a 100644 --- a/target/i386/confidential-guest.h +++ b/target/i386/confidential-guest.h @@ -46,7 +46,7 @@ struct X86ConfidentialGuestClass { /** * x86_confidential_guest_kvm_type: * - * Calls #X86ConfidentialGuestClass.unplug callback of @plug_handler. + * Calls #X86ConfidentialGuestClass.kvm_type() callback. */ static inline int x86_confidential_guest_kvm_type(X86ConfidentialGuest *cg) { diff --git a/target/i386/cpu-system.c b/target/i386/cpu-system.c index 9d007afdab..b56a2821af 100644 --- a/target/i386/cpu-system.c +++ b/target/i386/cpu-system.c @@ -309,3 +309,14 @@ void x86_cpu_get_crash_info_qom(Object *obj, Visitor *v, errp); qapi_free_GuestPanicInformation(panic_info); } + +uint64_t cpu_x86_get_msr_core_thread_count(X86CPU *cpu) +{ + CPUX86State *env = &cpu->env; + uint64_t val; + + val = x86_threads_per_pkg(&env->topo_info); /* thread count, bits 15..0 */ + val |= x86_cores_per_pkg(&env->topo_info) << 16; /* core count, bits 31..16 */ + + return val; +} diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 0b639848cd..1b9c11022c 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -312,13 +312,11 @@ static uint32_t num_threads_by_topo_level(X86CPUTopoInfo *topo_info, case CPU_TOPOLOGY_LEVEL_CORE: return topo_info->threads_per_core; case CPU_TOPOLOGY_LEVEL_MODULE: - return topo_info->threads_per_core * topo_info->cores_per_module; + return x86_threads_per_module(topo_info); case CPU_TOPOLOGY_LEVEL_DIE: - return topo_info->threads_per_core * topo_info->cores_per_module * - topo_info->modules_per_die; + return x86_threads_per_die(topo_info); case CPU_TOPOLOGY_LEVEL_SOCKET: - return topo_info->threads_per_core * topo_info->cores_per_module * - topo_info->modules_per_die * topo_info->dies_per_pkg; + return x86_threads_per_pkg(topo_info); default: g_assert_not_reached(); } @@ -6498,18 +6496,10 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, CPUState *cs = env_cpu(env); uint32_t limit; uint32_t signature[3]; - X86CPUTopoInfo topo_info; - uint32_t cores_per_pkg; + X86CPUTopoInfo *topo_info = &env->topo_info; uint32_t threads_per_pkg; - topo_info.dies_per_pkg = env->nr_dies; - topo_info.modules_per_die = env->nr_modules; - topo_info.cores_per_module = cs->nr_cores / env->nr_dies / env->nr_modules; - topo_info.threads_per_core = cs->nr_threads; - - cores_per_pkg = topo_info.cores_per_module * topo_info.modules_per_die * - topo_info.dies_per_pkg; - threads_per_pkg = cores_per_pkg * topo_info.threads_per_core; + threads_per_pkg = x86_threads_per_pkg(topo_info); /* Calculate & apply limits for different index ranges */ if (index >= 0xC0000000) { @@ -6548,7 +6538,6 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *edx = env->features[FEAT_1_EDX]; if (threads_per_pkg > 1) { *ebx |= threads_per_pkg << 16; - *edx |= CPUID_HT; } if (!cpu->enable_pmu) { *ecx &= ~CPUID_EXT_PDCM; @@ -6586,12 +6575,12 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, int host_vcpus_per_cache = 1 + ((*eax & 0x3FFC000) >> 14); *eax &= ~0xFC000000; - *eax |= max_core_ids_in_package(&topo_info) << 26; + *eax |= max_core_ids_in_package(topo_info) << 26; if (host_vcpus_per_cache > threads_per_pkg) { *eax &= ~0x3FFC000; /* Share the cache at package level. */ - *eax |= max_thread_ids_for_cache(&topo_info, + *eax |= max_thread_ids_for_cache(topo_info, CPU_TOPOLOGY_LEVEL_SOCKET) << 14; } } @@ -6603,7 +6592,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, switch (count) { case 0: /* L1 dcache info */ encode_cache_cpuid4(env->cache_info_cpuid4.l1d_cache, - &topo_info, + topo_info, eax, ebx, ecx, edx); if (!cpu->l1_cache_per_core) { *eax &= ~MAKE_64BIT_MASK(14, 12); @@ -6611,7 +6600,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; case 1: /* L1 icache info */ encode_cache_cpuid4(env->cache_info_cpuid4.l1i_cache, - &topo_info, + topo_info, eax, ebx, ecx, edx); if (!cpu->l1_cache_per_core) { *eax &= ~MAKE_64BIT_MASK(14, 12); @@ -6619,13 +6608,13 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; case 2: /* L2 cache info */ encode_cache_cpuid4(env->cache_info_cpuid4.l2_cache, - &topo_info, + topo_info, eax, ebx, ecx, edx); break; case 3: /* L3 cache info */ if (cpu->enable_l3_cache) { encode_cache_cpuid4(env->cache_info_cpuid4.l3_cache, - &topo_info, + topo_info, eax, ebx, ecx, edx); break; } @@ -6708,12 +6697,12 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, switch (count) { case 0: - *eax = apicid_core_offset(&topo_info); - *ebx = topo_info.threads_per_core; + *eax = apicid_core_offset(topo_info); + *ebx = topo_info->threads_per_core; *ecx |= CPUID_B_ECX_TOPO_LEVEL_SMT << 8; break; case 1: - *eax = apicid_pkg_offset(&topo_info); + *eax = apicid_pkg_offset(topo_info); *ebx = threads_per_pkg; *ecx |= CPUID_B_ECX_TOPO_LEVEL_CORE << 8; break; @@ -6739,7 +6728,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; } - encode_topo_cpuid1f(env, count, &topo_info, eax, ebx, ecx, edx); + encode_topo_cpuid1f(env, count, topo_info, eax, ebx, ecx, edx); break; case 0xD: { /* Processor Extended State */ @@ -6964,17 +6953,6 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *ecx = env->features[FEAT_8000_0001_ECX]; *edx = env->features[FEAT_8000_0001_EDX]; - /* The Linux kernel checks for the CMPLegacy bit and - * discards multiple thread information if it is set. - * So don't set it here for Intel to make Linux guests happy. - */ - if (threads_per_pkg > 1) { - if (env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1 || - env->cpuid_vendor2 != CPUID_VENDOR_INTEL_2 || - env->cpuid_vendor3 != CPUID_VENDOR_INTEL_3) { - *ecx |= 1 << 1; /* CmpLegacy bit */ - } - } if (tcg_enabled() && env->cpuid_vendor1 == CPUID_VENDOR_INTEL_1 && !(env->hflags & HF_LMA_MASK)) { *edx &= ~CPUID_EXT2_SYSCALL; @@ -7042,7 +7020,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, * thread ID within a package". * Bits 7:0 is "The number of threads in the package is NC+1" */ - *ecx = (apicid_pkg_offset(&topo_info) << 12) | + *ecx = (apicid_pkg_offset(topo_info) << 12) | (threads_per_pkg - 1); } else { *ecx = 0; @@ -7071,19 +7049,19 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, switch (count) { case 0: /* L1 dcache info */ encode_cache_cpuid8000001d(env->cache_info_amd.l1d_cache, - &topo_info, eax, ebx, ecx, edx); + topo_info, eax, ebx, ecx, edx); break; case 1: /* L1 icache info */ encode_cache_cpuid8000001d(env->cache_info_amd.l1i_cache, - &topo_info, eax, ebx, ecx, edx); + topo_info, eax, ebx, ecx, edx); break; case 2: /* L2 cache info */ encode_cache_cpuid8000001d(env->cache_info_amd.l2_cache, - &topo_info, eax, ebx, ecx, edx); + topo_info, eax, ebx, ecx, edx); break; case 3: /* L3 cache info */ encode_cache_cpuid8000001d(env->cache_info_amd.l3_cache, - &topo_info, eax, ebx, ecx, edx); + topo_info, eax, ebx, ecx, edx); break; default: /* end of info */ *eax = *ebx = *ecx = *edx = 0; @@ -7095,7 +7073,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; case 0x8000001E: if (cpu->core_id <= 255) { - encode_topo_cpuid8000001e(cpu, &topo_info, eax, ebx, ecx, edx); + encode_topo_cpuid8000001e(cpu, topo_info, eax, ebx, ecx, edx); } else { *eax = 0; *ebx = 0; @@ -7539,6 +7517,19 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp) } } + if (x86_threads_per_pkg(&env->topo_info) > 1) { + env->features[FEAT_1_EDX] |= CPUID_HT; + + /* + * The Linux kernel checks for the CMPLegacy bit and + * discards multiple thread information if it is set. + * So don't set it here for Intel to make Linux guests happy. + */ + if (!IS_INTEL_CPU(env)) { + env->features[FEAT_8000_0001_ECX] |= CPUID_EXT3_CMP_LEG; + } + } + for (i = 0; i < ARRAY_SIZE(feature_dependencies); i++) { FeatureDep *d = &feature_dependencies[i]; if (!(env->features[d->from.index] & d->from.mask)) { @@ -7719,8 +7710,10 @@ static bool x86_cpu_filter_features(X86CPU *cpu, bool verbose) env->avx10_version = version; have_filtered_features = true; } - } else if (env->avx10_version && prefix) { - warn_report("%s: avx10.%d.", prefix, env->avx10_version); + } else if (env->avx10_version) { + if (prefix) { + warn_report("%s: avx10.%d.", prefix, env->avx10_version); + } have_filtered_features = true; } @@ -7891,6 +7884,21 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) */ cpu->mwait.ecx |= CPUID_MWAIT_EMX | CPUID_MWAIT_IBE; + /* + * 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. + */ + if (IS_AMD_CPU(env) && + !(env->features[FEAT_8000_0001_ECX] & CPUID_EXT3_TOPOEXT) && + env->topo_info.threads_per_core > 1) { + warn_report_once("This family of AMD CPU doesn't support " + "hyperthreading(%d). Please configure -smp " + "options properly or try enabling topoext " + "feature.", env->topo_info.threads_per_core); + } + /* For 64bit systems think about the number of physical bits to present. * ideally this should be the same as the host; anything other than matching * the host can cause incorrect guest behaviour. @@ -7995,24 +8003,6 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) x86_cpu_gdb_init(cs); qemu_init_vcpu(cs); - /* - * 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_AMD_CPU(env) && - !(env->features[FEAT_8000_0001_ECX] & CPUID_EXT3_TOPOEXT) && - cs->nr_threads > 1) { - warn_report_once("This family of AMD CPU doesn't support " - "hyperthreading(%d). Please configure -smp " - "options properly or try enabling topoext " - "feature.", cs->nr_threads); - } - #ifndef CONFIG_USER_ONLY x86_cpu_apic_realize(cpu, &local_err); if (local_err != NULL) { @@ -8171,8 +8161,7 @@ static void x86_cpu_init_default_topo(X86CPU *cpu) { CPUX86State *env = &cpu->env; - env->nr_modules = 1; - env->nr_dies = 1; + env->topo_info = (X86CPUTopoInfo) {1, 1, 1, 1}; /* thread, core and socket levels are set by default. */ set_bit(CPU_TOPOLOGY_LEVEL_THREAD, env->avail_cpu_topo); diff --git a/target/i386/cpu.h b/target/i386/cpu.h index dbd8f1ffc7..b26e25ba15 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -29,6 +29,7 @@ #include "qapi/qapi-types-common.h" #include "qemu/cpu-float.h" #include "qemu/timer.h" +#include "standard-headers/asm-x86/kvm_para.h" #define XEN_NR_VIRQS 24 @@ -1010,6 +1011,28 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w); #define CPUID_8000_0007_EBX_OVERFLOW_RECOV (1U << 0) #define CPUID_8000_0007_EBX_SUCCOR (1U << 1) +/* (Old) KVM paravirtualized clocksource */ +#define CPUID_KVM_CLOCK (1U << KVM_FEATURE_CLOCKSOURCE) +/* (New) KVM specific paravirtualized clocksource */ +#define CPUID_KVM_CLOCK2 (1U << KVM_FEATURE_CLOCKSOURCE2) +/* KVM asynchronous page fault */ +#define CPUID_KVM_ASYNCPF (1U << KVM_FEATURE_ASYNC_PF) +/* KVM stolen (when guest vCPU is not running) time accounting */ +#define CPUID_KVM_STEAL_TIME (1U << KVM_FEATURE_STEAL_TIME) +/* KVM paravirtualized end-of-interrupt signaling */ +#define CPUID_KVM_PV_EOI (1U << KVM_FEATURE_PV_EOI) +/* KVM paravirtualized spinlocks support */ +#define CPUID_KVM_PV_UNHALT (1U << KVM_FEATURE_PV_UNHALT) +/* KVM host-side polling on HLT control from the guest */ +#define CPUID_KVM_POLL_CONTROL (1U << KVM_FEATURE_POLL_CONTROL) +/* KVM interrupt based asynchronous page fault*/ +#define CPUID_KVM_ASYNCPF_INT (1U << KVM_FEATURE_ASYNC_PF_INT) +/* KVM 'Extended Destination ID' support for external interrupts */ +#define CPUID_KVM_MSI_EXT_DEST_ID (1U << KVM_FEATURE_MSI_EXT_DEST_ID) + +/* Hint to KVM that vCPUs expect never preempted for an unlimited time */ +#define CPUID_KVM_HINTS_REALTIME (1U << KVM_HINTS_REALTIME) + /* CLZERO instruction */ #define CPUID_8000_0008_EBX_CLZERO (1U << 0) /* Always save/restore FP error pointers */ @@ -2045,11 +2068,7 @@ typedef struct CPUArchState { TPRAccess tpr_access_type; - /* Number of dies within this CPU package. */ - unsigned nr_dies; - - /* Number of modules within one die. */ - unsigned nr_modules; + X86CPUTopoInfo topo_info; /* Bitmap of available CPU topology levels for this CPU. */ DECLARE_BITMAP(avail_cpu_topo, CPU_TOPOLOGY_LEVEL__MAX); @@ -2390,6 +2409,8 @@ static inline void cpu_x86_load_seg_cache_sipi(X86CPU *cpu, cs->halted = 0; } +uint64_t cpu_x86_get_msr_core_thread_count(X86CPU *cpu); + int cpu_x86_get_descr_debug(CPUX86State *env, unsigned int selector, target_ulong *base, unsigned int *limit, unsigned int *flags); diff --git a/target/i386/hvf/x86_emu.c b/target/i386/hvf/x86_emu.c index 015f760acb..69c61c9c07 100644 --- a/target/i386/hvf/x86_emu.c +++ b/target/i386/hvf/x86_emu.c @@ -765,8 +765,7 @@ void simulate_rdmsr(CPUX86State *env) val = env->mtrr_deftype; break; case MSR_CORE_THREAD_COUNT: - val = cs->nr_threads * cs->nr_cores; /* thread count, bits 15..0 */ - val |= ((uint32_t)cs->nr_cores << 16); /* core count, bits 31..16 */ + val = cpu_x86_get_msr_core_thread_count(cpu); break; default: /* fprintf(stderr, "%s: unknown msr 0x%x\n", __func__, msr); */ diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 2f66e63b88..6c749d4ee8 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -95,9 +95,6 @@ #define KVM_APIC_BUS_CYCLE_NS 1 #define KVM_APIC_BUS_FREQUENCY (1000000000ULL / KVM_APIC_BUS_CYCLE_NS) -#define MSR_KVM_WALL_CLOCK 0x11 -#define MSR_KVM_SYSTEM_TIME 0x12 - /* A 4096-byte buffer can hold the 8-byte kvm_msrs header, plus * 255 kvm_msr_entry structs */ #define MSR_BUF_SIZE 4096 @@ -111,8 +108,8 @@ typedef struct { } KVMMSRHandlers; static void kvm_init_msrs(X86CPU *cpu); -static bool kvm_filter_msr(KVMState *s, uint32_t msr, QEMURDMSRHandler *rdmsr, - QEMUWRMSRHandler *wrmsr); +static int kvm_filter_msr(KVMState *s, uint32_t msr, QEMURDMSRHandler *rdmsr, + QEMUWRMSRHandler *wrmsr); const KVMCapabilityInfo kvm_arch_required_capabilities[] = { KVM_CAP_INFO(SET_TSS_ADDR), @@ -564,13 +561,13 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function, * be enabled without the in-kernel irqchip */ if (!kvm_irqchip_in_kernel()) { - ret &= ~(1U << KVM_FEATURE_PV_UNHALT); + ret &= ~CPUID_KVM_PV_UNHALT; } if (kvm_irqchip_is_split()) { - ret |= 1U << KVM_FEATURE_MSI_EXT_DEST_ID; + ret |= CPUID_KVM_MSI_EXT_DEST_ID; } } else if (function == KVM_CPUID_FEATURES && reg == R_EDX) { - ret |= 1U << KVM_HINTS_REALTIME; + ret |= CPUID_KVM_HINTS_REALTIME; } if (current_machine->cgs) { @@ -2617,10 +2614,7 @@ static bool kvm_rdmsr_core_thread_count(X86CPU *cpu, uint32_t msr, uint64_t *val) { - CPUState *cs = CPU(cpu); - - *val = cs->nr_threads * cs->nr_cores; /* thread count, bits 15..0 */ - *val |= ((uint32_t)cs->nr_cores << 16); /* core count, bits 31..16 */ + *val = cpu_x86_get_msr_core_thread_count(cpu); return true; } @@ -2939,7 +2933,6 @@ static int kvm_msr_energy_thread_init(KVMState *s, MachineState *ms) { MachineClass *mc = MACHINE_GET_CLASS(ms); struct KVMMsrEnergy *r = &s->msr_energy; - int ret = 0; /* * Sanity check @@ -2949,13 +2942,11 @@ static int kvm_msr_energy_thread_init(KVMState *s, MachineState *ms) if (!is_host_cpu_intel()) { error_report("The RAPL feature can only be enabled on hosts " "with Intel CPU models"); - ret = 1; - goto out; + return -1; } if (!is_rapl_enabled()) { - ret = 1; - goto out; + return -1; } /* Retrieve the virtual topology */ @@ -2977,16 +2968,14 @@ static int kvm_msr_energy_thread_init(KVMState *s, MachineState *ms) r->host_topo.maxcpus = vmsr_get_maxcpus(); if (r->host_topo.maxcpus == 0) { error_report("host max cpus = 0"); - ret = 1; - goto out; + return -1; } /* Max number of packages on the host */ r->host_topo.maxpkgs = vmsr_get_max_physical_package(r->host_topo.maxcpus); if (r->host_topo.maxpkgs == 0) { error_report("host max pkgs = 0"); - ret = 1; - goto out; + return -1; } /* Allocate memory for each package on the host */ @@ -2998,8 +2987,7 @@ static int kvm_msr_energy_thread_init(KVMState *s, MachineState *ms) for (int i = 0; i < r->host_topo.maxpkgs; i++) { if (r->host_topo.pkg_cpu_count[i] == 0) { error_report("cpu per packages = 0 on package_%d", i); - ret = 1; - goto out; + return -1; } } @@ -3016,8 +3004,7 @@ static int kvm_msr_energy_thread_init(KVMState *s, MachineState *ms) if (s->msr_energy.sioc == NULL) { error_report("vmsr socket opening failed"); - ret = 1; - goto out; + return -1; } /* Those MSR values should not change */ @@ -3029,15 +3016,13 @@ static int kvm_msr_energy_thread_init(KVMState *s, MachineState *ms) s->msr_energy.sioc); if (r->msr_unit == 0 || r->msr_limit == 0 || r->msr_info == 0) { error_report("can't read any virtual msr"); - ret = 1; - goto out; + return -1; } qemu_thread_create(&r->msr_thr, "kvm-msr", kvm_msr_energy_thread, s, QEMU_THREAD_JOINABLE); -out: - return ret; + return 0; } int kvm_arch_get_default_type(MachineState *ms) @@ -3103,10 +3088,7 @@ static int kvm_vm_set_tss_addr(KVMState *s, uint64_t tss_base) static int kvm_vm_enable_disable_exits(KVMState *s) { int disable_exits = kvm_check_extension(s, KVM_CAP_X86_DISABLE_EXITS); -/* 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 | @@ -3156,59 +3138,64 @@ static int kvm_vm_enable_notify_vmexit(KVMState *s) static int kvm_vm_enable_userspace_msr(KVMState *s) { - int ret = kvm_vm_enable_cap(s, KVM_CAP_X86_USER_SPACE_MSR, 0, - KVM_MSR_EXIT_REASON_FILTER); + int ret; + + ret = kvm_vm_enable_cap(s, KVM_CAP_X86_USER_SPACE_MSR, 0, + KVM_MSR_EXIT_REASON_FILTER); if (ret < 0) { error_report("Could not enable user space MSRs: %s", strerror(-ret)); exit(1); } - if (!kvm_filter_msr(s, MSR_CORE_THREAD_COUNT, - kvm_rdmsr_core_thread_count, NULL)) { - error_report("Could not install MSR_CORE_THREAD_COUNT handler!"); + ret = kvm_filter_msr(s, MSR_CORE_THREAD_COUNT, + kvm_rdmsr_core_thread_count, NULL); + if (ret < 0) { + error_report("Could not install MSR_CORE_THREAD_COUNT handler: %s", + strerror(-ret)); exit(1); } return 0; } -static void kvm_vm_enable_energy_msrs(KVMState *s) +static int kvm_vm_enable_energy_msrs(KVMState *s) { - bool r; + int ret; + if (s->msr_energy.enable == true) { - r = kvm_filter_msr(s, MSR_RAPL_POWER_UNIT, - kvm_rdmsr_rapl_power_unit, NULL); - if (!r) { - error_report("Could not install MSR_RAPL_POWER_UNIT \ - handler"); - exit(1); + ret = kvm_filter_msr(s, MSR_RAPL_POWER_UNIT, + kvm_rdmsr_rapl_power_unit, NULL); + if (ret < 0) { + error_report("Could not install MSR_RAPL_POWER_UNIT handler: %s", + strerror(-ret)); + return ret; } - r = kvm_filter_msr(s, MSR_PKG_POWER_LIMIT, - kvm_rdmsr_pkg_power_limit, NULL); - if (!r) { - error_report("Could not install MSR_PKG_POWER_LIMIT \ - handler"); - exit(1); + ret = kvm_filter_msr(s, MSR_PKG_POWER_LIMIT, + kvm_rdmsr_pkg_power_limit, NULL); + if (ret < 0) { + error_report("Could not install MSR_PKG_POWER_LIMIT handler: %s", + strerror(-ret)); + return ret; } - r = kvm_filter_msr(s, MSR_PKG_POWER_INFO, - kvm_rdmsr_pkg_power_info, NULL); - if (!r) { - error_report("Could not install MSR_PKG_POWER_INFO \ - handler"); - exit(1); + ret = kvm_filter_msr(s, MSR_PKG_POWER_INFO, + kvm_rdmsr_pkg_power_info, NULL); + if (ret < 0) { + error_report("Could not install MSR_PKG_POWER_INFO handler: %s", + strerror(-ret)); + return ret; } - r = kvm_filter_msr(s, MSR_PKG_ENERGY_STATUS, - kvm_rdmsr_pkg_energy_status, NULL); - if (!r) { - error_report("Could not install MSR_PKG_ENERGY_STATUS \ - handler"); - exit(1); + ret = kvm_filter_msr(s, MSR_PKG_ENERGY_STATUS, + kvm_rdmsr_pkg_energy_status, NULL); + if (ret < 0) { + error_report("Could not install MSR_PKG_ENERGY_STATUS handler: %s", + strerror(-ret)); + return ret; } } - return; + return 0; } int kvm_arch_init(MachineState *ms, KVMState *s) @@ -3275,7 +3262,10 @@ int kvm_arch_init(MachineState *ms, KVMState *s) return ret; } - kvm_get_supported_feature_msrs(s); + ret = kvm_get_supported_feature_msrs(s); + if (ret < 0) { + return ret; + } uname(&utsname); lm_capable_kernel = strcmp(utsname.machine, "x86_64") == 0; @@ -3311,6 +3301,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s) if (ret < 0) { error_report("kvm: guest stopping CPU not supported: %s", strerror(-ret)); + return ret; } } @@ -3342,10 +3333,15 @@ int kvm_arch_init(MachineState *ms, KVMState *s) } if (s->msr_energy.enable == true) { - kvm_vm_enable_energy_msrs(s); - if (kvm_msr_energy_thread_init(s, ms)) { + ret = kvm_vm_enable_energy_msrs(s); + if (ret < 0) { + return ret; + } + + ret = kvm_msr_energy_thread_init(s, ms); + if (ret < 0) { error_report("kvm : error RAPL feature requirement not met"); - exit(1); + return ret; } } } @@ -3976,22 +3972,24 @@ static int kvm_put_msrs(X86CPU *cpu, int level) */ if (level >= KVM_PUT_RESET_STATE) { kvm_msr_entry_add(cpu, MSR_IA32_TSC, env->tsc); - kvm_msr_entry_add(cpu, MSR_KVM_SYSTEM_TIME, env->system_time_msr); - kvm_msr_entry_add(cpu, MSR_KVM_WALL_CLOCK, env->wall_clock_msr); - if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_ASYNC_PF_INT)) { + if (env->features[FEAT_KVM] & (CPUID_KVM_CLOCK | CPUID_KVM_CLOCK2)) { + kvm_msr_entry_add(cpu, MSR_KVM_SYSTEM_TIME, env->system_time_msr); + kvm_msr_entry_add(cpu, MSR_KVM_WALL_CLOCK, env->wall_clock_msr); + } + if (env->features[FEAT_KVM] & CPUID_KVM_ASYNCPF_INT) { kvm_msr_entry_add(cpu, MSR_KVM_ASYNC_PF_INT, env->async_pf_int_msr); } - if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_ASYNC_PF)) { + if (env->features[FEAT_KVM] & CPUID_KVM_ASYNCPF) { kvm_msr_entry_add(cpu, MSR_KVM_ASYNC_PF_EN, env->async_pf_en_msr); } - if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_PV_EOI)) { + if (env->features[FEAT_KVM] & CPUID_KVM_PV_EOI) { kvm_msr_entry_add(cpu, MSR_KVM_PV_EOI_EN, env->pv_eoi_en_msr); } - if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_STEAL_TIME)) { + if (env->features[FEAT_KVM] & CPUID_KVM_STEAL_TIME) { kvm_msr_entry_add(cpu, MSR_KVM_STEAL_TIME, env->steal_time_msr); } - if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_POLL_CONTROL)) { + if (env->features[FEAT_KVM] & CPUID_KVM_POLL_CONTROL) { kvm_msr_entry_add(cpu, MSR_KVM_POLL_CONTROL, env->poll_control_msr); } @@ -4454,21 +4452,23 @@ static int kvm_get_msrs(X86CPU *cpu) } } #endif - kvm_msr_entry_add(cpu, MSR_KVM_SYSTEM_TIME, 0); - kvm_msr_entry_add(cpu, MSR_KVM_WALL_CLOCK, 0); - if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_ASYNC_PF_INT)) { + if (env->features[FEAT_KVM] & (CPUID_KVM_CLOCK | CPUID_KVM_CLOCK2)) { + kvm_msr_entry_add(cpu, MSR_KVM_SYSTEM_TIME, 0); + kvm_msr_entry_add(cpu, MSR_KVM_WALL_CLOCK, 0); + } + if (env->features[FEAT_KVM] & CPUID_KVM_ASYNCPF_INT) { kvm_msr_entry_add(cpu, MSR_KVM_ASYNC_PF_INT, 0); } - if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_ASYNC_PF)) { + if (env->features[FEAT_KVM] & CPUID_KVM_ASYNCPF) { kvm_msr_entry_add(cpu, MSR_KVM_ASYNC_PF_EN, 0); } - if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_PV_EOI)) { + if (env->features[FEAT_KVM] & CPUID_KVM_PV_EOI) { kvm_msr_entry_add(cpu, MSR_KVM_PV_EOI_EN, 0); } - if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_STEAL_TIME)) { + if (env->features[FEAT_KVM] & CPUID_KVM_STEAL_TIME) { kvm_msr_entry_add(cpu, MSR_KVM_STEAL_TIME, 0); } - if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_POLL_CONTROL)) { + if (env->features[FEAT_KVM] & CPUID_KVM_POLL_CONTROL) { kvm_msr_entry_add(cpu, MSR_KVM_POLL_CONTROL, 1); } if (has_architectural_pmu_version > 0) { @@ -5843,15 +5843,16 @@ void kvm_arch_update_guest_debug(CPUState *cpu, struct kvm_guest_debug *dbg) } } -static bool kvm_install_msr_filters(KVMState *s) +static int kvm_install_msr_filters(KVMState *s) { uint64_t zero = 0; struct kvm_msr_filter filter = { .flags = KVM_MSR_FILTER_DEFAULT_ALLOW, }; - int r, i, j = 0; + int i, j = 0; - for (i = 0; i < KVM_MSR_FILTER_MAX_RANGES; i++) { + QEMU_BUILD_BUG_ON(ARRAY_SIZE(msr_handlers) != ARRAY_SIZE(filter.ranges)); + for (i = 0; i < ARRAY_SIZE(msr_handlers); i++) { KVMMSRHandlers *handler = &msr_handlers[i]; if (handler->msr) { struct kvm_msr_filter_range *range = &filter.ranges[j++]; @@ -5873,18 +5874,13 @@ static bool kvm_install_msr_filters(KVMState *s) } } - r = kvm_vm_ioctl(s, KVM_X86_SET_MSR_FILTER, &filter); - if (r) { - return false; - } - - return true; + return kvm_vm_ioctl(s, KVM_X86_SET_MSR_FILTER, &filter); } -static bool kvm_filter_msr(KVMState *s, uint32_t msr, QEMURDMSRHandler *rdmsr, - QEMUWRMSRHandler *wrmsr) +static int kvm_filter_msr(KVMState *s, uint32_t msr, QEMURDMSRHandler *rdmsr, + QEMUWRMSRHandler *wrmsr) { - int i; + int i, ret; for (i = 0; i < ARRAY_SIZE(msr_handlers); i++) { if (!msr_handlers[i].msr) { @@ -5894,16 +5890,17 @@ static bool kvm_filter_msr(KVMState *s, uint32_t msr, QEMURDMSRHandler *rdmsr, .wrmsr = wrmsr, }; - if (!kvm_install_msr_filters(s)) { + ret = kvm_install_msr_filters(s); + if (ret) { msr_handlers[i] = (KVMMSRHandlers) { }; - return false; + return ret; } - return true; + return 0; } } - return false; + return -EINVAL; } static int kvm_handle_rdmsr(X86CPU *cpu, struct kvm_run *run) @@ -6195,7 +6192,7 @@ uint64_t kvm_swizzle_msi_ext_dest_id(uint64_t address) return address; } env = &X86_CPU(first_cpu)->env; - if (!(env->features[FEAT_KVM] & (1 << KVM_FEATURE_MSI_EXT_DEST_ID))) { + if (!(env->features[FEAT_KVM] & CPUID_KVM_MSI_EXT_DEST_ID)) { return address; } diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 785ff63f2a..c4cc5f48d8 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -286,24 +286,25 @@ static void gen_load(DisasContext *s, X86DecodedInsn *decode, int opn, TCGv v) gen_op_ld_v(s, op->ot, v, s->A0); } - } else if (op->ot == MO_8 && byte_reg_is_xH(s, op->n)) { - if (v == s->T0 && decode->e.special == X86_SPECIAL_SExtT0) { - tcg_gen_sextract_tl(v, cpu_regs[op->n - 4], 8, 8); - } else { - tcg_gen_extract_tl(v, cpu_regs[op->n - 4], 8, 8); - } - } else if (op->ot < MO_TL && v == s->T0 && (decode->e.special == X86_SPECIAL_SExtT0 || decode->e.special == X86_SPECIAL_ZExtT0)) { - if (decode->e.special == X86_SPECIAL_SExtT0) { - tcg_gen_ext_tl(v, cpu_regs[op->n], op->ot | MO_SIGN); + if (op->ot == MO_8 && byte_reg_is_xH(s, op->n)) { + if (decode->e.special == X86_SPECIAL_SExtT0) { + tcg_gen_sextract_tl(v, cpu_regs[op->n - 4], 8, 8); + } else { + tcg_gen_extract_tl(v, cpu_regs[op->n - 4], 8, 8); + } } else { - tcg_gen_ext_tl(v, cpu_regs[op->n], op->ot); + if (decode->e.special == X86_SPECIAL_SExtT0) { + tcg_gen_ext_tl(v, cpu_regs[op->n], op->ot | MO_SIGN); + } else { + tcg_gen_ext_tl(v, cpu_regs[op->n], op->ot); + } } } else { - tcg_gen_mov_tl(v, cpu_regs[op->n]); + gen_op_mov_v_reg(s, op->ot, v, op->n); } break; case X86_OP_IMM: @@ -1443,8 +1444,9 @@ static TCGv gen_bt_mask(DisasContext *s, X86DecodedInsn *decode) return mask; } -/* Expects truncated bit index in s->T1, 1 << s->T1 in MASK. */ -static void gen_bt_flags(DisasContext *s, X86DecodedInsn *decode, TCGv src, TCGv mask) +/* Expects truncated bit index in COUNT, 1 << COUNT in MASK. */ +static void gen_bt_flags(DisasContext *s, X86DecodedInsn *decode, TCGv src, + TCGv count, TCGv mask) { TCGv cf; @@ -1467,15 +1469,34 @@ static void gen_bt_flags(DisasContext *s, X86DecodedInsn *decode, TCGv src, TCGv decode->cc_src = tcg_temp_new(); decode->cc_dst = cpu_cc_dst; decode->cc_op = CC_OP_SARB + cc_op_size(s->cc_op); - tcg_gen_shr_tl(decode->cc_src, src, s->T1); + tcg_gen_shr_tl(decode->cc_src, src, count); } } static void gen_BT(DisasContext *s, X86DecodedInsn *decode) { - TCGv mask = gen_bt_mask(s, decode); + TCGv count = s->T1; + TCGv mask; + + /* + * Try to ensure that the rhs of the TSTNE condition is a constant (and a + * power of two), as that is more readily available on most TCG backends. + * + * For immediate bit number gen_bt_mask()'s output is already a constant; + * for register bit number, shift the source right and check bit 0. + */ + if (decode->e.op2 == X86_TYPE_I) { + mask = gen_bt_mask(s, decode); + } else { + MemOp ot = decode->op[1].ot; + + tcg_gen_andi_tl(s->T1, s->T1, (8 << ot) - 1); + tcg_gen_shr_tl(s->T0, s->T0, s->T1); - gen_bt_flags(s, decode, s->T0, mask); + count = tcg_constant_tl(0); + mask = tcg_constant_tl(1); + } + gen_bt_flags(s, decode, s->T0, count, mask); } static void gen_BTC(DisasContext *s, X86DecodedInsn *decode) @@ -1491,7 +1512,7 @@ static void gen_BTC(DisasContext *s, X86DecodedInsn *decode) tcg_gen_xor_tl(s->T0, s->T0, mask); } - gen_bt_flags(s, decode, old, mask); + gen_bt_flags(s, decode, old, s->T1, mask); } static void gen_BTR(DisasContext *s, X86DecodedInsn *decode) @@ -1509,7 +1530,7 @@ static void gen_BTR(DisasContext *s, X86DecodedInsn *decode) tcg_gen_andc_tl(s->T0, s->T0, mask); } - gen_bt_flags(s, decode, old, mask); + gen_bt_flags(s, decode, old, s->T1, mask); } static void gen_BTS(DisasContext *s, X86DecodedInsn *decode) @@ -1525,7 +1546,7 @@ static void gen_BTS(DisasContext *s, X86DecodedInsn *decode) tcg_gen_or_tl(s->T0, s->T0, mask); } - gen_bt_flags(s, decode, old, mask); + gen_bt_flags(s, decode, old, s->T1, mask); } static void gen_BZHI(DisasContext *s, X86DecodedInsn *decode) diff --git a/target/i386/tcg/system/misc_helper.c b/target/i386/tcg/system/misc_helper.c index ffed8a3215..c9c4d42f84 100644 --- a/target/i386/tcg/system/misc_helper.c +++ b/target/i386/tcg/system/misc_helper.c @@ -468,8 +468,7 @@ void helper_rdmsr(CPUX86State *env) val = x86_cpu->ucode_rev; break; case MSR_CORE_THREAD_COUNT: { - CPUState *cs = CPU(x86_cpu); - val = (cs->nr_threads * cs->nr_cores) | (cs->nr_cores << 16); + val = cpu_x86_get_msr_core_thread_count(x86_cpu); break; } case MSR_APIC_START ... MSR_APIC_END: { diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 834aea1e59..dbc9d637c4 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -486,7 +486,7 @@ static inline void gen_op_mov_v_reg(DisasContext *s, MemOp ot, TCGv t0, int reg) { if (ot == MO_8 && byte_reg_is_xH(s, reg)) { - tcg_gen_extract_tl(t0, cpu_regs[reg - 4], 8, 8); + tcg_gen_shri_tl(t0, cpu_regs[reg - 4], 8); } else { tcg_gen_mov_tl(t0, cpu_regs[reg]); } diff --git a/tests/qtest/libqos/qgraph.h b/tests/qtest/libqos/qgraph.h index 1b5de02e7b..81fbfdd0e2 100644 --- a/tests/qtest/libqos/qgraph.h +++ b/tests/qtest/libqos/qgraph.h @@ -355,7 +355,7 @@ void qos_object_start_hw(QOSGraphObject *obj); QOSGraphObject *qos_machine_new(QOSGraphNode *node, QTestState *qts); /** - * qos_machine_new(): instantiate a new driver node + * qos_driver_new(): instantiate a new driver node * @node: A driver node to be instantiated * @parent: A #QOSGraphObject to be consumed by the new driver node * @alloc: An allocator to be used by the new driver node. diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h index 8f3bde5d16..ce88d23eae 100644 --- a/tests/qtest/libqtest.h +++ b/tests/qtest/libqtest.h @@ -382,7 +382,7 @@ QDict *qtest_qmp_event_ref(QTestState *s, const char *event); char *qtest_hmp(QTestState *s, const char *fmt, ...) G_GNUC_PRINTF(2, 3); /** - * qtest_hmpv: + * qtest_vhmp: * @s: #QTestState instance to operate on. * @fmt: HMP command to send to QEMU, formats arguments like vsprintf(). * @ap: HMP command arguments @@ -935,7 +935,7 @@ void qtest_qmp_assert_success(QTestState *qts, const char *fmt, ...) #ifndef _WIN32 /** - * qtest_qmp_fd_assert_success_ref: + * qtest_qmp_fds_assert_success_ref: * @qts: QTestState instance to operate on * @fds: the file descriptors to send * @nfds: number of @fds to send @@ -952,7 +952,7 @@ QDict *qtest_qmp_fds_assert_success_ref(QTestState *qts, int *fds, size_t nfds, G_GNUC_PRINTF(4, 5); /** - * qtest_qmp_fd_assert_success: + * qtest_qmp_fds_assert_success: * @qts: QTestState instance to operate on * @fds: the file descriptors to send * @nfds: number of @fds to send diff --git a/tests/tcg/plugins/insn.c b/tests/tcg/plugins/insn.c index baf2d07205..0c723cb9ed 100644 --- a/tests/tcg/plugins/insn.c +++ b/tests/tcg/plugins/insn.c @@ -150,10 +150,8 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( insn, QEMU_PLUGIN_INLINE_ADD_U64, insn_count, 1); } else { - uint64_t vaddr = qemu_plugin_insn_vaddr(insn); qemu_plugin_register_vcpu_insn_exec_cb( - insn, vcpu_insn_exec_before, QEMU_PLUGIN_CB_NO_REGS, - GUINT_TO_POINTER(vaddr)); + insn, vcpu_insn_exec_before, QEMU_PLUGIN_CB_NO_REGS, NULL); } if (do_size) { diff --git a/tests/tcg/plugins/mem.c b/tests/tcg/plugins/mem.c index b0fa8a9f27..d87d6628e0 100644 --- a/tests/tcg/plugins/mem.c +++ b/tests/tcg/plugins/mem.c @@ -135,14 +135,14 @@ static void update_region_info(uint64_t region, uint64_t offset, g_assert(offset + size <= region_size); g_mutex_lock(&lock); - ri = (RegionInfo *) g_hash_table_lookup(regions, GUINT_TO_POINTER(region)); + ri = (RegionInfo *) g_hash_table_lookup(regions, ®ion); if (!ri) { ri = g_new0(RegionInfo, 1); ri->region_address = region; ri->data = g_malloc0(region_size); ri->seen_all = true; - g_hash_table_insert(regions, GUINT_TO_POINTER(region), (gpointer) ri); + g_hash_table_insert(regions, &ri->region_address, ri); } if (is_store) { @@ -392,7 +392,7 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, if (do_region_summary) { region_mask = (region_size - 1); - regions = g_hash_table_new(NULL, g_direct_equal); + regions = g_hash_table_new(g_int64_hash, g_int64_equal); } counts = qemu_plugin_scoreboard_new(sizeof(CPUCount)); diff --git a/tests/tcg/plugins/meson.build b/tests/tcg/plugins/meson.build index f847849b1b..87a17d67bd 100644 --- a/tests/tcg/plugins/meson.build +++ b/tests/tcg/plugins/meson.build @@ -5,9 +5,8 @@ if get_option('plugins') t += shared_module(i, files(i + '.c') + '../../../contrib/plugins/win32_linker.c', include_directories: '../../../include/qemu', link_depends: [win32_qemu_plugin_api_lib], - link_args: ['-Lplugins', '-lqemu_plugin_api'], + link_args: win32_qemu_plugin_api_link_flags, dependencies: glib) - else t += shared_module(i, files(i + '.c'), include_directories: '../../../include/qemu', diff --git a/tests/tcg/plugins/syscall.c b/tests/tcg/plugins/syscall.c index ff452178b1..47aad55fc1 100644 --- a/tests/tcg/plugins/syscall.c +++ b/tests/tcg/plugins/syscall.c @@ -76,12 +76,12 @@ static int64_t write_sysno = -1; static SyscallStats *get_or_create_entry(int64_t num) { SyscallStats *entry = - (SyscallStats *) g_hash_table_lookup(statistics, GINT_TO_POINTER(num)); + (SyscallStats *) g_hash_table_lookup(statistics, &num); if (!entry) { entry = g_new0(SyscallStats, 1); entry->num = num; - g_hash_table_insert(statistics, GINT_TO_POINTER(num), (gpointer) entry); + g_hash_table_insert(statistics, &entry->num, entry); } return entry; @@ -232,7 +232,7 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, } if (!do_print) { - statistics = g_hash_table_new_full(NULL, g_direct_equal, NULL, g_free); + statistics = g_hash_table_new_full(g_int64_hash, g_int64_equal, NULL, g_free); } if (do_log_writes) { |