From 3bd8359379c899f813049f1dd9e8f8a8817d8c98 Mon Sep 17 00:00:00 2001 From: Matheus Tavares Bernardino Date: Sun, 8 Oct 2023 15:09:43 -0700 Subject: [PATCH 001/974] target/hexagon: move GETPC() calls to top level helpers As docs/devel/loads-stores.rst states: ``GETPC()`` should be used with great care: calling it in other functions that are *not* the top level ``HELPER(foo)`` will cause unexpected behavior. Instead, the value of ``GETPC()`` should be read from the helper and passed if needed to the functions that the helper calls. Let's fix the GETPC() usage in Hexagon, making sure it's always called from top level helpers and passed down to the places where it's needed. There are a few snippets where that is not currently the case: - probe_store(), which is only called from two helpers, so it's easy to move GETPC() up. - mem_load*() functions, which are also called directly from helpers, but through the MEM_LOAD*() set of macros. Note that this are only used when compiling with --disable-hexagon-idef-parser. In this case, we also take this opportunity to simplify the code, unifying the mem_load*() functions. - HELPER(probe_hvx_stores), when called from another helper, ends up using its own GETPC() expansion instead of the top level caller. Signed-off-by: Matheus Tavares Bernardino Reviewed-by: Taylor Simpson Message-Id: <2c74c3696946edba7cc5b2942cf296a5af532052.1689070412.git.quic_mathbern@quicinc.com>-ne Reviewed-by: Brian Cain Signed-off-by: Brian Cain Message-Id: <20231008220945.983643-2-bcain@quicinc.com> --- target/hexagon/macros.h | 19 +++++----- target/hexagon/op_helper.c | 75 +++++++++++++++----------------------- target/hexagon/op_helper.h | 9 ----- 3 files changed, 38 insertions(+), 65 deletions(-) diff --git a/target/hexagon/macros.h b/target/hexagon/macros.h index b356d85792..9a51b5709b 100644 --- a/target/hexagon/macros.h +++ b/target/hexagon/macros.h @@ -173,15 +173,6 @@ #define MEM_STORE8(VA, DATA, SLOT) \ MEM_STORE8_FUNC(DATA)(tcg_env, VA, DATA, SLOT) #else -#define MEM_LOAD1s(VA) ((int8_t)mem_load1(env, pkt_has_store_s1, slot, VA)) -#define MEM_LOAD1u(VA) ((uint8_t)mem_load1(env, pkt_has_store_s1, slot, VA)) -#define MEM_LOAD2s(VA) ((int16_t)mem_load2(env, pkt_has_store_s1, slot, VA)) -#define MEM_LOAD2u(VA) ((uint16_t)mem_load2(env, pkt_has_store_s1, slot, VA)) -#define MEM_LOAD4s(VA) ((int32_t)mem_load4(env, pkt_has_store_s1, slot, VA)) -#define MEM_LOAD4u(VA) ((uint32_t)mem_load4(env, pkt_has_store_s1, slot, VA)) -#define MEM_LOAD8s(VA) ((int64_t)mem_load8(env, pkt_has_store_s1, slot, VA)) -#define MEM_LOAD8u(VA) ((uint64_t)mem_load8(env, pkt_has_store_s1, slot, VA)) - #define MEM_STORE1(VA, DATA, SLOT) log_store32(env, VA, DATA, 1, SLOT) #define MEM_STORE2(VA, DATA, SLOT) log_store32(env, VA, DATA, 2, SLOT) #define MEM_STORE4(VA, DATA, SLOT) log_store32(env, VA, DATA, 4, SLOT) @@ -530,8 +521,16 @@ static inline TCGv gen_read_ireg(TCGv result, TCGv val, int shift) #ifdef QEMU_GENERATE #define fLOAD(NUM, SIZE, SIGN, EA, DST) MEM_LOAD##SIZE##SIGN(DST, EA) #else +#define MEM_LOAD1 cpu_ldub_data_ra +#define MEM_LOAD2 cpu_lduw_data_ra +#define MEM_LOAD4 cpu_ldl_data_ra +#define MEM_LOAD8 cpu_ldq_data_ra + #define fLOAD(NUM, SIZE, SIGN, EA, DST) \ - DST = (size##SIZE##SIGN##_t)MEM_LOAD##SIZE##SIGN(EA) + do { \ + check_noshuf(env, pkt_has_store_s1, slot, EA, SIZE, GETPC()); \ + DST = (size##SIZE##SIGN##_t)MEM_LOAD##SIZE(env, EA, GETPC()); \ + } while (0) #endif #define fMEMOP(NUM, SIZE, SIGN, EA, FNTYPE, VALUE) diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c index 12967ac21e..8ca3976a65 100644 --- a/target/hexagon/op_helper.c +++ b/target/hexagon/op_helper.c @@ -95,9 +95,8 @@ void HELPER(debug_check_store_width)(CPUHexagonState *env, int slot, int check) } } -void HELPER(commit_store)(CPUHexagonState *env, int slot_num) +static void commit_store(CPUHexagonState *env, int slot_num, uintptr_t ra) { - uintptr_t ra = GETPC(); uint8_t width = env->mem_log_stores[slot_num].width; target_ulong va = env->mem_log_stores[slot_num].va; @@ -119,6 +118,12 @@ void HELPER(commit_store)(CPUHexagonState *env, int slot_num) } } +void HELPER(commit_store)(CPUHexagonState *env, int slot_num) +{ + uintptr_t ra = GETPC(); + commit_store(env, slot_num, ra); +} + void HELPER(gather_store)(CPUHexagonState *env, uint32_t addr, int slot) { mem_gather_store(env, addr, slot); @@ -467,13 +472,12 @@ int32_t HELPER(cabacdecbin_pred)(int64_t RssV, int64_t RttV) } static void probe_store(CPUHexagonState *env, int slot, int mmu_idx, - bool is_predicated) + bool is_predicated, uintptr_t retaddr) { if (!is_predicated || !(env->slot_cancelled & (1 << slot))) { size1u_t width = env->mem_log_stores[slot].width; target_ulong va = env->mem_log_stores[slot].va; - uintptr_t ra = GETPC(); - probe_write(env, va, width, mmu_idx, ra); + probe_write(env, va, width, mmu_idx, retaddr); } } @@ -494,12 +498,13 @@ void HELPER(probe_pkt_scalar_store_s0)(CPUHexagonState *env, int args) int mmu_idx = FIELD_EX32(args, PROBE_PKT_SCALAR_STORE_S0, MMU_IDX); bool is_predicated = FIELD_EX32(args, PROBE_PKT_SCALAR_STORE_S0, IS_PREDICATED); - probe_store(env, 0, mmu_idx, is_predicated); + uintptr_t ra = GETPC(); + probe_store(env, 0, mmu_idx, is_predicated, ra); } -void HELPER(probe_hvx_stores)(CPUHexagonState *env, int mmu_idx) +static void probe_hvx_stores(CPUHexagonState *env, int mmu_idx, + uintptr_t retaddr) { - uintptr_t retaddr = GETPC(); int i; /* Normal (possibly masked) vector store */ @@ -538,6 +543,12 @@ void HELPER(probe_hvx_stores)(CPUHexagonState *env, int mmu_idx) } } +void HELPER(probe_hvx_stores)(CPUHexagonState *env, int mmu_idx) +{ + uintptr_t retaddr = GETPC(); + probe_hvx_stores(env, mmu_idx, retaddr); +} + void HELPER(probe_pkt_scalar_hvx_stores)(CPUHexagonState *env, int mask) { bool has_st0 = FIELD_EX32(mask, PROBE_PKT_SCALAR_HVX_STORES, HAS_ST0); @@ -547,18 +558,20 @@ void HELPER(probe_pkt_scalar_hvx_stores)(CPUHexagonState *env, int mask) bool s0_is_pred = FIELD_EX32(mask, PROBE_PKT_SCALAR_HVX_STORES, S0_IS_PRED); bool s1_is_pred = FIELD_EX32(mask, PROBE_PKT_SCALAR_HVX_STORES, S1_IS_PRED); int mmu_idx = FIELD_EX32(mask, PROBE_PKT_SCALAR_HVX_STORES, MMU_IDX); + uintptr_t ra = GETPC(); if (has_st0) { - probe_store(env, 0, mmu_idx, s0_is_pred); + probe_store(env, 0, mmu_idx, s0_is_pred, ra); } if (has_st1) { - probe_store(env, 1, mmu_idx, s1_is_pred); + probe_store(env, 1, mmu_idx, s1_is_pred, ra); } if (has_hvx_stores) { - HELPER(probe_hvx_stores)(env, mmu_idx); + probe_hvx_stores(env, mmu_idx, ra); } } +#ifndef CONFIG_HEXAGON_IDEF_PARSER /* * mem_noshuf * Section 5.5 of the Hexagon V67 Programmer's Reference Manual @@ -567,46 +580,16 @@ void HELPER(probe_pkt_scalar_hvx_stores)(CPUHexagonState *env, int mask) * wasn't cancelled), we have to do the store first. */ static void check_noshuf(CPUHexagonState *env, bool pkt_has_store_s1, - uint32_t slot, target_ulong vaddr, int size) + uint32_t slot, target_ulong vaddr, int size, + uintptr_t ra) { if (slot == 0 && pkt_has_store_s1 && ((env->slot_cancelled & (1 << 1)) == 0)) { - HELPER(probe_noshuf_load)(env, vaddr, size, MMU_USER_IDX); - HELPER(commit_store)(env, 1); + probe_read(env, vaddr, size, MMU_USER_IDX, ra); + commit_store(env, 1, ra); } } - -uint8_t mem_load1(CPUHexagonState *env, bool pkt_has_store_s1, - uint32_t slot, target_ulong vaddr) -{ - uintptr_t ra = GETPC(); - check_noshuf(env, pkt_has_store_s1, slot, vaddr, 1); - return cpu_ldub_data_ra(env, vaddr, ra); -} - -uint16_t mem_load2(CPUHexagonState *env, bool pkt_has_store_s1, - uint32_t slot, target_ulong vaddr) -{ - uintptr_t ra = GETPC(); - check_noshuf(env, pkt_has_store_s1, slot, vaddr, 2); - return cpu_lduw_data_ra(env, vaddr, ra); -} - -uint32_t mem_load4(CPUHexagonState *env, bool pkt_has_store_s1, - uint32_t slot, target_ulong vaddr) -{ - uintptr_t ra = GETPC(); - check_noshuf(env, pkt_has_store_s1, slot, vaddr, 4); - return cpu_ldl_data_ra(env, vaddr, ra); -} - -uint64_t mem_load8(CPUHexagonState *env, bool pkt_has_store_s1, - uint32_t slot, target_ulong vaddr) -{ - uintptr_t ra = GETPC(); - check_noshuf(env, pkt_has_store_s1, slot, vaddr, 8); - return cpu_ldq_data_ra(env, vaddr, ra); -} +#endif /* Floating point */ float64 HELPER(conv_sf2df)(CPUHexagonState *env, float32 RsV) diff --git a/target/hexagon/op_helper.h b/target/hexagon/op_helper.h index 8f3764d15e..66119cf3d4 100644 --- a/target/hexagon/op_helper.h +++ b/target/hexagon/op_helper.h @@ -19,15 +19,6 @@ #define HEXAGON_OP_HELPER_H /* Misc functions */ -uint8_t mem_load1(CPUHexagonState *env, bool pkt_has_store_s1, - uint32_t slot, target_ulong vaddr); -uint16_t mem_load2(CPUHexagonState *env, bool pkt_has_store_s1, - uint32_t slot, target_ulong vaddr); -uint32_t mem_load4(CPUHexagonState *env, bool pkt_has_store_s1, - uint32_t slot, target_ulong vaddr); -uint64_t mem_load8(CPUHexagonState *env, bool pkt_has_store_s1, - uint32_t slot, target_ulong vaddr); - void log_store64(CPUHexagonState *env, target_ulong addr, int64_t val, int width, int slot); void log_store32(CPUHexagonState *env, target_ulong addr, From 20c34a9216f41977803659f5bd458618c291d56c Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Sun, 8 Oct 2023 15:09:44 -0700 Subject: [PATCH 002/974] target/hexagon: fix some occurrences of -Wshadow=local MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Of the changes in this commit, the changes in `HELPER(commit_hvx_stores)()` are less obvious. They are required because of some macro invocations like SCATTER_OP_WRITE_TO_MEM(). e.g.: In file included from ../target/hexagon/op_helper.c:31: ../target/hexagon/mmvec/macros.h:205:18: error: declaration of ‘i’ shadows a previous local [-Werror=shadow=compatible-local] 205 | for (int i = 0; i < sizeof(MMVector); i += sizeof(TYPE)) { \ | ^ ../target/hexagon/op_helper.c:157:17: note: in expansion of macro ‘SCATTER_OP_WRITE_TO_MEM’ 157 | SCATTER_OP_WRITE_TO_MEM(uint16_t); | ^~~~~~~~~~~~~~~~~~~~~~~ ../target/hexagon/op_helper.c:135:9: note: shadowed declaration is here 135 | int i; | ^ In file included from ../target/hexagon/op_helper.c:31: ../target/hexagon/mmvec/macros.h:204:19: error: declaration of ‘ra’ shadows a previous local [-Werror=shadow=compatible-local] 204 | uintptr_t ra = GETPC(); \ | ^~ ../target/hexagon/op_helper.c:160:17: note: in expansion of macro ‘SCATTER_OP_WRITE_TO_MEM’ 160 | SCATTER_OP_WRITE_TO_MEM(uint32_t); | ^~~~~~~~~~~~~~~~~~~~~~~ ../target/hexagon/op_helper.c:134:15: note: shadowed declaration is here 134 | uintptr_t ra = GETPC(); | ^~ Reviewed-by: Matheus Tavares Bernardino Signed-off-by: Brian Cain Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20231008220945.983643-3-bcain@quicinc.com> --- target/hexagon/imported/alu.idef | 6 +++--- target/hexagon/mmvec/macros.h | 2 +- target/hexagon/op_helper.c | 9 +++------ target/hexagon/translate.c | 10 +++++----- 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/target/hexagon/imported/alu.idef b/target/hexagon/imported/alu.idef index 12d2aac5d4..b855676989 100644 --- a/target/hexagon/imported/alu.idef +++ b/target/hexagon/imported/alu.idef @@ -1142,9 +1142,9 @@ Q6INSN(A4_cround_rr,"Rd32=cround(Rs32,Rt32)",ATTRIBS(),"Convergent Round", {RdV tmp128 = fSHIFTR128(tmp128, SHIFT);\ DST = fCAST16S_8S(tmp128);\ } else {\ - size16s_t rndbit_128 = fCAST8S_16S((1LL << (SHIFT - 1))); \ - size16s_t src_128 = fCAST8S_16S(SRC); \ - size16s_t tmp128 = fADD128(src_128, rndbit_128);\ + rndbit_128 = fCAST8S_16S((1LL << (SHIFT - 1))); \ + src_128 = fCAST8S_16S(SRC); \ + tmp128 = fADD128(src_128, rndbit_128);\ tmp128 = fSHIFTR128(tmp128, SHIFT);\ DST = fCAST16S_8S(tmp128);\ } diff --git a/target/hexagon/mmvec/macros.h b/target/hexagon/mmvec/macros.h index a655634fd1..1ceb9453ee 100644 --- a/target/hexagon/mmvec/macros.h +++ b/target/hexagon/mmvec/macros.h @@ -201,7 +201,7 @@ } while (0) #define SCATTER_OP_WRITE_TO_MEM(TYPE) \ do { \ - uintptr_t ra = GETPC(); \ + ra = GETPC(); \ for (int i = 0; i < sizeof(MMVector); i += sizeof(TYPE)) { \ if (test_bit(i, env->vtcm_log.mask)) { \ TYPE dst = 0; \ diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c index 8ca3976a65..da10ac5847 100644 --- a/target/hexagon/op_helper.c +++ b/target/hexagon/op_helper.c @@ -132,10 +132,9 @@ void HELPER(gather_store)(CPUHexagonState *env, uint32_t addr, int slot) void HELPER(commit_hvx_stores)(CPUHexagonState *env) { uintptr_t ra = GETPC(); - int i; /* Normal (possibly masked) vector store */ - for (i = 0; i < VSTORES_MAX; i++) { + for (int i = 0; i < VSTORES_MAX; i++) { if (env->vstore_pending[i]) { env->vstore_pending[i] = 0; target_ulong va = env->vstore[i].va; @@ -162,7 +161,7 @@ void HELPER(commit_hvx_stores)(CPUHexagonState *env) g_assert_not_reached(); } } else { - for (i = 0; i < sizeof(MMVector); i++) { + for (int i = 0; i < sizeof(MMVector); i++) { if (test_bit(i, env->vtcm_log.mask)) { cpu_stb_data_ra(env, env->vtcm_log.va[i], env->vtcm_log.data.ub[i], ra); @@ -505,10 +504,8 @@ void HELPER(probe_pkt_scalar_store_s0)(CPUHexagonState *env, int args) static void probe_hvx_stores(CPUHexagonState *env, int mmu_idx, uintptr_t retaddr) { - int i; - /* Normal (possibly masked) vector store */ - for (i = 0; i < VSTORES_MAX; i++) { + for (int i = 0; i < VSTORES_MAX; i++) { if (env->vstore_pending[i]) { target_ulong va = env->vstore[i].va; int size = env->vstore[i].size; diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index 663b7bbc3a..666c061180 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -553,7 +553,7 @@ static void gen_start_packet(DisasContext *ctx) /* Preload the predicated registers into get_result_gpr(ctx, i) */ if (ctx->need_commit && !bitmap_empty(ctx->predicated_regs, TOTAL_PER_THREAD_REGS)) { - int i = find_first_bit(ctx->predicated_regs, TOTAL_PER_THREAD_REGS); + i = find_first_bit(ctx->predicated_regs, TOTAL_PER_THREAD_REGS); while (i < TOTAL_PER_THREAD_REGS) { tcg_gen_mov_tl(get_result_gpr(ctx, i), hex_gpr[i]); i = find_next_bit(ctx->predicated_regs, TOTAL_PER_THREAD_REGS, @@ -566,7 +566,7 @@ static void gen_start_packet(DisasContext *ctx) * Only endloop instructions conditionally write to pred registers */ if (ctx->need_commit && pkt->pkt_has_endloop) { - for (int i = 0; i < ctx->preg_log_idx; i++) { + for (i = 0; i < ctx->preg_log_idx; i++) { int pred_num = ctx->preg_log[i]; ctx->new_pred_value[pred_num] = tcg_temp_new(); tcg_gen_mov_tl(ctx->new_pred_value[pred_num], hex_pred[pred_num]); @@ -575,7 +575,7 @@ static void gen_start_packet(DisasContext *ctx) /* Preload the predicated HVX registers into future_VRegs and tmp_VRegs */ if (!bitmap_empty(ctx->predicated_future_vregs, NUM_VREGS)) { - int i = find_first_bit(ctx->predicated_future_vregs, NUM_VREGS); + i = find_first_bit(ctx->predicated_future_vregs, NUM_VREGS); while (i < NUM_VREGS) { const intptr_t VdV_off = ctx_future_vreg_off(ctx, i, 1, true); @@ -588,7 +588,7 @@ static void gen_start_packet(DisasContext *ctx) } } if (!bitmap_empty(ctx->predicated_tmp_vregs, NUM_VREGS)) { - int i = find_first_bit(ctx->predicated_tmp_vregs, NUM_VREGS); + i = find_first_bit(ctx->predicated_tmp_vregs, NUM_VREGS); while (i < NUM_VREGS) { const intptr_t VdV_off = ctx_tmp_vreg_off(ctx, i, 1, true); @@ -1228,7 +1228,7 @@ void hexagon_translate_init(void) offsetof(CPUHexagonState, mem_log_stores[i].data64), store_val64_names[i]); } - for (int i = 0; i < VSTORES_MAX; i++) { + for (i = 0; i < VSTORES_MAX; i++) { snprintf(vstore_addr_names[i], NAME_LEN, "vstore_addr_%d", i); hex_vstore_addr[i] = tcg_global_mem_new(tcg_env, offsetof(CPUHexagonState, vstore[i].va), From cbe27c3ec3783bd8a838042d23ab6180d67b098d Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Mon, 25 Sep 2023 15:52:12 +0930 Subject: [PATCH 003/974] MAINTAINERS: aspeed: Update Andrew's email address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I've changed employers, have company email that deals with patch-based workflows without too much of a headache, and am trying to steer some content out of my personal mail. Signed-off-by: Andrew Jeffery Signed-off-by: Cédric Le Goater --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index d36aa44661..cd8d6b140f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1131,7 +1131,7 @@ F: docs/system/arm/emcraft-sf2.rst ASPEED BMCs M: Cédric Le Goater M: Peter Maydell -R: Andrew Jeffery +R: Andrew Jeffery R: Joel Stanley L: qemu-arm@nongnu.org S: Maintained From 2f4ec776798bb178f42940961668f39c764386d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 24 Oct 2023 18:24:12 +0200 Subject: [PATCH 004/974] hw/arm/aspeed: Extract code common to all boards to a common file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit aspeed_soc.c contains definitions specific to the AST2400 and AST2500 SoCs, but also some definitions for other AST SoCs: move them to a common file. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_soc.c | 96 ------------------------------- hw/arm/aspeed_soc_common.c | 114 +++++++++++++++++++++++++++++++++++++ hw/arm/meson.build | 1 + 3 files changed, 115 insertions(+), 96 deletions(-) create mode 100644 hw/arm/aspeed_soc_common.c diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c index bf22258de9..f6c2ead4ac 100644 --- a/hw/arm/aspeed_soc.c +++ b/hw/arm/aspeed_soc.c @@ -585,99 +585,3 @@ static void aspeed_soc_register_types(void) }; type_init(aspeed_soc_register_types); - -qemu_irq aspeed_soc_get_irq(AspeedSoCState *s, int dev) -{ - return ASPEED_SOC_GET_CLASS(s)->get_irq(s, dev); -} - -bool aspeed_soc_uart_realize(AspeedSoCState *s, Error **errp) -{ - AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); - SerialMM *smm; - - for (int i = 0, uart = ASPEED_DEV_UART1; i < sc->uarts_num; i++, uart++) { - smm = &s->uart[i]; - - /* Chardev property is set by the machine. */ - qdev_prop_set_uint8(DEVICE(smm), "regshift", 2); - qdev_prop_set_uint32(DEVICE(smm), "baudbase", 38400); - qdev_set_legacy_instance_id(DEVICE(smm), sc->memmap[uart], 2); - qdev_prop_set_uint8(DEVICE(smm), "endianness", DEVICE_LITTLE_ENDIAN); - if (!sysbus_realize(SYS_BUS_DEVICE(smm), errp)) { - return false; - } - - sysbus_connect_irq(SYS_BUS_DEVICE(smm), 0, aspeed_soc_get_irq(s, uart)); - aspeed_mmio_map(s, SYS_BUS_DEVICE(smm), 0, sc->memmap[uart]); - } - - return true; -} - -void aspeed_soc_uart_set_chr(AspeedSoCState *s, int dev, Chardev *chr) -{ - AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); - int i = dev - ASPEED_DEV_UART1; - - g_assert(0 <= i && i < ARRAY_SIZE(s->uart) && i < sc->uarts_num); - qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", chr); -} - -/* - * SDMC should be realized first to get correct RAM size and max size - * values - */ -bool aspeed_soc_dram_init(AspeedSoCState *s, Error **errp) -{ - AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); - ram_addr_t ram_size, max_ram_size; - - ram_size = object_property_get_uint(OBJECT(&s->sdmc), "ram-size", - &error_abort); - max_ram_size = object_property_get_uint(OBJECT(&s->sdmc), "max-ram-size", - &error_abort); - - memory_region_init(&s->dram_container, OBJECT(s), "ram-container", - max_ram_size); - memory_region_add_subregion(&s->dram_container, 0, s->dram_mr); - - /* - * Add a memory region beyond the RAM region to let firmwares scan - * the address space with load/store and guess how much RAM the - * SoC has. - */ - if (ram_size < max_ram_size) { - DeviceState *dev = qdev_new(TYPE_UNIMPLEMENTED_DEVICE); - - qdev_prop_set_string(dev, "name", "ram-empty"); - qdev_prop_set_uint64(dev, "size", max_ram_size - ram_size); - if (!sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), errp)) { - return false; - } - - memory_region_add_subregion_overlap(&s->dram_container, ram_size, - sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0), -1000); - } - - memory_region_add_subregion(s->memory, - sc->memmap[ASPEED_DEV_SDRAM], &s->dram_container); - return true; -} - -void aspeed_mmio_map(AspeedSoCState *s, SysBusDevice *dev, int n, hwaddr addr) -{ - memory_region_add_subregion(s->memory, addr, - sysbus_mmio_get_region(dev, n)); -} - -void aspeed_mmio_map_unimplemented(AspeedSoCState *s, SysBusDevice *dev, - const char *name, hwaddr addr, uint64_t size) -{ - qdev_prop_set_string(DEVICE(dev), "name", name); - qdev_prop_set_uint64(DEVICE(dev), "size", size); - sysbus_realize(dev, &error_abort); - - memory_region_add_subregion_overlap(s->memory, addr, - sysbus_mmio_get_region(dev, 0), -1000); -} diff --git a/hw/arm/aspeed_soc_common.c b/hw/arm/aspeed_soc_common.c new file mode 100644 index 0000000000..a43f5d2a6f --- /dev/null +++ b/hw/arm/aspeed_soc_common.c @@ -0,0 +1,114 @@ +/* + * ASPEED SoC family + * + * Andrew Jeffery + * Jeremy Kerr + * + * Copyright 2016 IBM Corp. + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/misc/unimp.h" +#include "hw/arm/aspeed_soc.h" +#include "hw/char/serial.h" + + +qemu_irq aspeed_soc_get_irq(AspeedSoCState *s, int dev) +{ + return ASPEED_SOC_GET_CLASS(s)->get_irq(s, dev); +} + +bool aspeed_soc_uart_realize(AspeedSoCState *s, Error **errp) +{ + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + SerialMM *smm; + + for (int i = 0, uart = ASPEED_DEV_UART1; i < sc->uarts_num; i++, uart++) { + smm = &s->uart[i]; + + /* Chardev property is set by the machine. */ + qdev_prop_set_uint8(DEVICE(smm), "regshift", 2); + qdev_prop_set_uint32(DEVICE(smm), "baudbase", 38400); + qdev_set_legacy_instance_id(DEVICE(smm), sc->memmap[uart], 2); + qdev_prop_set_uint8(DEVICE(smm), "endianness", DEVICE_LITTLE_ENDIAN); + if (!sysbus_realize(SYS_BUS_DEVICE(smm), errp)) { + return false; + } + + sysbus_connect_irq(SYS_BUS_DEVICE(smm), 0, aspeed_soc_get_irq(s, uart)); + aspeed_mmio_map(s, SYS_BUS_DEVICE(smm), 0, sc->memmap[uart]); + } + + return true; +} + +void aspeed_soc_uart_set_chr(AspeedSoCState *s, int dev, Chardev *chr) +{ + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + int i = dev - ASPEED_DEV_UART1; + + g_assert(0 <= i && i < ARRAY_SIZE(s->uart) && i < sc->uarts_num); + qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", chr); +} + +/* + * SDMC should be realized first to get correct RAM size and max size + * values + */ +bool aspeed_soc_dram_init(AspeedSoCState *s, Error **errp) +{ + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + ram_addr_t ram_size, max_ram_size; + + ram_size = object_property_get_uint(OBJECT(&s->sdmc), "ram-size", + &error_abort); + max_ram_size = object_property_get_uint(OBJECT(&s->sdmc), "max-ram-size", + &error_abort); + + memory_region_init(&s->dram_container, OBJECT(s), "ram-container", + max_ram_size); + memory_region_add_subregion(&s->dram_container, 0, s->dram_mr); + + /* + * Add a memory region beyond the RAM region to let firmwares scan + * the address space with load/store and guess how much RAM the + * SoC has. + */ + if (ram_size < max_ram_size) { + DeviceState *dev = qdev_new(TYPE_UNIMPLEMENTED_DEVICE); + + qdev_prop_set_string(dev, "name", "ram-empty"); + qdev_prop_set_uint64(dev, "size", max_ram_size - ram_size); + if (!sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), errp)) { + return false; + } + + memory_region_add_subregion_overlap(&s->dram_container, ram_size, + sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0), -1000); + } + + memory_region_add_subregion(s->memory, + sc->memmap[ASPEED_DEV_SDRAM], &s->dram_container); + return true; +} + +void aspeed_mmio_map(AspeedSoCState *s, SysBusDevice *dev, int n, hwaddr addr) +{ + memory_region_add_subregion(s->memory, addr, + sysbus_mmio_get_region(dev, n)); +} + +void aspeed_mmio_map_unimplemented(AspeedSoCState *s, SysBusDevice *dev, + const char *name, hwaddr addr, uint64_t size) +{ + qdev_prop_set_string(DEVICE(dev), "name", name); + qdev_prop_set_uint64(DEVICE(dev), "size", size); + sysbus_realize(dev, &error_abort); + + memory_region_add_subregion_overlap(s->memory, addr, + sysbus_mmio_get_region(dev, 0), -1000); +} diff --git a/hw/arm/meson.build b/hw/arm/meson.build index a6feaf1af9..42e7aa36f3 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -50,6 +50,7 @@ arm_ss.add(when: 'CONFIG_FSL_IMX6', if_true: files('fsl-imx6.c')) arm_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files( 'aspeed_soc.c', 'aspeed.c', + 'aspeed_soc_common.c', 'aspeed_ast2600.c', 'aspeed_ast10x0.c', 'aspeed_eeprom.c', From a1508362398654f27193f42945e49ec8812175b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 24 Oct 2023 18:24:13 +0200 Subject: [PATCH 005/974] hw/arm/aspeed: Rename aspeed_soc_init() as AST2400/2500 specific MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_soc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c index f6c2ead4ac..bb377e9e6e 100644 --- a/hw/arm/aspeed_soc.c +++ b/hw/arm/aspeed_soc.c @@ -140,7 +140,7 @@ static qemu_irq aspeed_soc_ast2400_get_irq(AspeedSoCState *s, int dev) return qdev_get_gpio_in(DEVICE(&s->vic), sc->irqmap[dev]); } -static void aspeed_soc_init(Object *obj) +static void aspeed_ast2400_soc_init(Object *obj) { AspeedSoCState *s = ASPEED_SOC(obj); AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); @@ -546,7 +546,7 @@ static void aspeed_soc_ast2400_class_init(ObjectClass *oc, void *data) static const TypeInfo aspeed_soc_ast2400_type_info = { .name = "ast2400-a1", .parent = TYPE_ASPEED_SOC, - .instance_init = aspeed_soc_init, + .instance_init = aspeed_ast2400_soc_init, .instance_size = sizeof(AspeedSoCState), .class_init = aspeed_soc_ast2400_class_init, }; @@ -573,7 +573,7 @@ static void aspeed_soc_ast2500_class_init(ObjectClass *oc, void *data) static const TypeInfo aspeed_soc_ast2500_type_info = { .name = "ast2500-a1", .parent = TYPE_ASPEED_SOC, - .instance_init = aspeed_soc_init, + .instance_init = aspeed_ast2400_soc_init, .instance_size = sizeof(AspeedSoCState), .class_init = aspeed_soc_ast2500_class_init, }; From aa6c6697bbab5130f962d227924cfe7f94cbee19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 24 Oct 2023 18:24:14 +0200 Subject: [PATCH 006/974] hw/arm/aspeed: Rename aspeed_soc_realize() as AST2400/2500 specific MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep aspeed_soc_class_init() generic, set the realize handler to aspeed_ast2400_soc_realize() in each 2400/2500 class_init. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_soc.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c index bb377e9e6e..191276a320 100644 --- a/hw/arm/aspeed_soc.c +++ b/hw/arm/aspeed_soc.c @@ -239,7 +239,7 @@ static void aspeed_ast2400_soc_init(Object *obj) object_initialize_child(obj, "video", &s->video, TYPE_UNIMPLEMENTED_DEVICE); } -static void aspeed_soc_realize(DeviceState *dev, Error **errp) +static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) { int i; AspeedSoCState *s = ASPEED_SOC(dev); @@ -509,9 +509,6 @@ static void aspeed_soc_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); - dc->realize = aspeed_soc_realize; - /* Reason: Uses serial_hds and nd_table in realize() directly */ - dc->user_creatable = false; device_class_set_props(dc, aspeed_soc_properties); } @@ -527,6 +524,11 @@ static const TypeInfo aspeed_soc_type_info = { static void aspeed_soc_ast2400_class_init(ObjectClass *oc, void *data) { AspeedSoCClass *sc = ASPEED_SOC_CLASS(oc); + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = aspeed_ast2400_soc_realize; + /* Reason: Uses serial_hds and nd_table in realize() directly */ + dc->user_creatable = false; sc->name = "ast2400-a1"; sc->cpu_type = ARM_CPU_TYPE_NAME("arm926"); @@ -554,6 +556,11 @@ static const TypeInfo aspeed_soc_ast2400_type_info = { static void aspeed_soc_ast2500_class_init(ObjectClass *oc, void *data) { AspeedSoCClass *sc = ASPEED_SOC_CLASS(oc); + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = aspeed_ast2400_soc_realize; + /* Reason: Uses serial_hds and nd_table in realize() directly */ + dc->user_creatable = false; sc->name = "ast2500-a1"; sc->cpu_type = ARM_CPU_TYPE_NAME("arm1176"); From 3c392e87df051d457429274af55fe967808d6cb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 24 Oct 2023 18:24:15 +0200 Subject: [PATCH 007/974] hw/arm/aspeed: Dynamically allocate AspeedMachineState::soc field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We want to derivate the big AspeedSoCState object in some more SoC-specific ones. Since the object size will vary, allocate it dynamically. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/arm/aspeed.c | 101 +++++++++++++++++++++++++----------------------- 1 file changed, 52 insertions(+), 49 deletions(-) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index f8ba67531a..cc59176563 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -40,7 +40,7 @@ struct AspeedMachineState { MachineState parent_obj; /* Public */ - AspeedSoCState soc; + AspeedSoCState *soc; MemoryRegion boot_rom; bool mmio_exec; uint32_t uart_chosen; @@ -288,7 +288,7 @@ static void write_boot_rom(BlockBackend *blk, hwaddr addr, size_t rom_size, static void aspeed_install_boot_rom(AspeedMachineState *bmc, BlockBackend *blk, uint64_t rom_size) { - AspeedSoCState *soc = &bmc->soc; + AspeedSoCState *soc = bmc->soc; memory_region_init_rom(&bmc->boot_rom, NULL, "aspeed.boot_rom", rom_size, &error_abort); @@ -337,7 +337,7 @@ static void sdhci_attach_drive(SDHCIState *sdhci, DriveInfo *dinfo) static void connect_serial_hds_to_uarts(AspeedMachineState *bmc) { AspeedMachineClass *amc = ASPEED_MACHINE_GET_CLASS(bmc); - AspeedSoCState *s = &bmc->soc; + AspeedSoCState *s = bmc->soc; AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); int uart_chosen = bmc->uart_chosen ? bmc->uart_chosen : amc->uart_default; @@ -358,32 +358,33 @@ static void aspeed_machine_init(MachineState *machine) int i; NICInfo *nd = &nd_table[0]; - object_initialize_child(OBJECT(machine), "soc", &bmc->soc, amc->soc_name); - - sc = ASPEED_SOC_GET_CLASS(&bmc->soc); + bmc->soc = ASPEED_SOC(object_new(amc->soc_name)); + object_property_add_child(OBJECT(machine), "soc", OBJECT(bmc->soc)); + object_unref(OBJECT(bmc->soc)); + sc = ASPEED_SOC_GET_CLASS(bmc->soc); /* * This will error out if the RAM size is not supported by the * memory controller of the SoC. */ - object_property_set_uint(OBJECT(&bmc->soc), "ram-size", machine->ram_size, + object_property_set_uint(OBJECT(bmc->soc), "ram-size", machine->ram_size, &error_fatal); for (i = 0; i < sc->macs_num; i++) { if ((amc->macs_mask & (1 << i)) && nd->used) { qemu_check_nic_model(nd, TYPE_FTGMAC100); - qdev_set_nic_properties(DEVICE(&bmc->soc.ftgmac100[i]), nd); + qdev_set_nic_properties(DEVICE(&bmc->soc->ftgmac100[i]), nd); nd++; } } - object_property_set_int(OBJECT(&bmc->soc), "hw-strap1", amc->hw_strap1, + object_property_set_int(OBJECT(bmc->soc), "hw-strap1", amc->hw_strap1, &error_abort); - object_property_set_int(OBJECT(&bmc->soc), "hw-strap2", amc->hw_strap2, + object_property_set_int(OBJECT(bmc->soc), "hw-strap2", amc->hw_strap2, &error_abort); - object_property_set_link(OBJECT(&bmc->soc), "memory", + object_property_set_link(OBJECT(bmc->soc), "memory", OBJECT(get_system_memory()), &error_abort); - object_property_set_link(OBJECT(&bmc->soc), "dram", + object_property_set_link(OBJECT(bmc->soc), "dram", OBJECT(machine->ram), &error_abort); if (machine->kernel_filename) { /* @@ -391,17 +392,17 @@ static void aspeed_machine_init(MachineState *machine) * that runs to unlock the SCU. In this case set the default to * be unlocked as the kernel expects */ - object_property_set_int(OBJECT(&bmc->soc), "hw-prot-key", + object_property_set_int(OBJECT(bmc->soc), "hw-prot-key", ASPEED_SCU_PROT_KEY, &error_abort); } connect_serial_hds_to_uarts(bmc); - qdev_realize(DEVICE(&bmc->soc), NULL, &error_abort); + qdev_realize(DEVICE(bmc->soc), NULL, &error_abort); if (defaults_enabled()) { - aspeed_board_init_flashes(&bmc->soc.fmc, + aspeed_board_init_flashes(&bmc->soc->fmc, bmc->fmc_model ? bmc->fmc_model : amc->fmc_model, amc->num_cs, 0); - aspeed_board_init_flashes(&bmc->soc.spi[0], + aspeed_board_init_flashes(&bmc->soc->spi[0], bmc->spi_model ? bmc->spi_model : amc->spi_model, 1, amc->num_cs); } @@ -426,22 +427,22 @@ static void aspeed_machine_init(MachineState *machine) amc->i2c_init(bmc); } - for (i = 0; i < bmc->soc.sdhci.num_slots; i++) { - sdhci_attach_drive(&bmc->soc.sdhci.slots[i], + for (i = 0; i < bmc->soc->sdhci.num_slots; i++) { + sdhci_attach_drive(&bmc->soc->sdhci.slots[i], drive_get(IF_SD, 0, i)); } - if (bmc->soc.emmc.num_slots) { - sdhci_attach_drive(&bmc->soc.emmc.slots[0], - drive_get(IF_SD, 0, bmc->soc.sdhci.num_slots)); + if (bmc->soc->emmc.num_slots) { + sdhci_attach_drive(&bmc->soc->emmc.slots[0], + drive_get(IF_SD, 0, bmc->soc->sdhci.num_slots)); } if (!bmc->mmio_exec) { - DeviceState *dev = ssi_get_cs(bmc->soc.fmc.spi, 0); + DeviceState *dev = ssi_get_cs(bmc->soc->fmc.spi, 0); BlockBackend *fmc0 = dev ? m25p80_get_blk(dev) : NULL; if (fmc0) { - uint64_t rom_size = memory_region_size(&bmc->soc.spi_boot); + uint64_t rom_size = memory_region_size(&bmc->soc->spi_boot); aspeed_install_boot_rom(bmc, fmc0, rom_size); } } @@ -451,7 +452,7 @@ static void aspeed_machine_init(MachineState *machine) static void palmetto_bmc_i2c_init(AspeedMachineState *bmc) { - AspeedSoCState *soc = &bmc->soc; + AspeedSoCState *soc = bmc->soc; DeviceState *dev; uint8_t *eeprom_buf = g_malloc0(32 * 1024); @@ -473,7 +474,7 @@ static void palmetto_bmc_i2c_init(AspeedMachineState *bmc) static void quanta_q71l_bmc_i2c_init(AspeedMachineState *bmc) { - AspeedSoCState *soc = &bmc->soc; + AspeedSoCState *soc = bmc->soc; /* * The quanta-q71l platform expects tmp75s which are compatible with @@ -505,7 +506,7 @@ static void quanta_q71l_bmc_i2c_init(AspeedMachineState *bmc) static void ast2500_evb_i2c_init(AspeedMachineState *bmc) { - AspeedSoCState *soc = &bmc->soc; + AspeedSoCState *soc = bmc->soc; uint8_t *eeprom_buf = g_malloc0(8 * 1024); smbus_eeprom_init_one(aspeed_i2c_get_bus(&soc->i2c, 3), 0x50, @@ -518,7 +519,7 @@ static void ast2500_evb_i2c_init(AspeedMachineState *bmc) static void ast2600_evb_i2c_init(AspeedMachineState *bmc) { - AspeedSoCState *soc = &bmc->soc; + AspeedSoCState *soc = bmc->soc; uint8_t *eeprom_buf = g_malloc0(8 * 1024); smbus_eeprom_init_one(aspeed_i2c_get_bus(&soc->i2c, 7), 0x50, @@ -531,7 +532,7 @@ static void ast2600_evb_i2c_init(AspeedMachineState *bmc) static void yosemitev2_bmc_i2c_init(AspeedMachineState *bmc) { - AspeedSoCState *soc = &bmc->soc; + AspeedSoCState *soc = bmc->soc; at24c_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 4), 0x51, 128 * KiB); at24c_eeprom_init_rom(aspeed_i2c_get_bus(&soc->i2c, 8), 0x51, 128 * KiB, @@ -545,7 +546,7 @@ static void yosemitev2_bmc_i2c_init(AspeedMachineState *bmc) static void romulus_bmc_i2c_init(AspeedMachineState *bmc) { - AspeedSoCState *soc = &bmc->soc; + AspeedSoCState *soc = bmc->soc; /* The romulus board expects Epson RX8900 I2C RTC but a ds1338 is * good enough */ @@ -554,7 +555,7 @@ static void romulus_bmc_i2c_init(AspeedMachineState *bmc) static void tiogapass_bmc_i2c_init(AspeedMachineState *bmc) { - AspeedSoCState *soc = &bmc->soc; + AspeedSoCState *soc = bmc->soc; at24c_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 4), 0x54, 128 * KiB); at24c_eeprom_init_rom(aspeed_i2c_get_bus(&soc->i2c, 6), 0x54, 128 * KiB, @@ -573,7 +574,7 @@ static void create_pca9552(AspeedSoCState *soc, int bus_id, int addr) static void sonorapass_bmc_i2c_init(AspeedMachineState *bmc) { - AspeedSoCState *soc = &bmc->soc; + AspeedSoCState *soc = bmc->soc; /* bus 2 : */ i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 2), "tmp105", 0x48); @@ -627,7 +628,7 @@ static void witherspoon_bmc_i2c_init(AspeedMachineState *bmc) {14, LED_COLOR_GREEN, "front-power-3", GPIO_POLARITY_ACTIVE_LOW}, {15, LED_COLOR_GREEN, "front-id-5", GPIO_POLARITY_ACTIVE_LOW}, }; - AspeedSoCState *soc = &bmc->soc; + AspeedSoCState *soc = bmc->soc; uint8_t *eeprom_buf = g_malloc0(8 * 1024); DeviceState *dev; LEDState *led; @@ -672,7 +673,7 @@ static void witherspoon_bmc_i2c_init(AspeedMachineState *bmc) static void g220a_bmc_i2c_init(AspeedMachineState *bmc) { - AspeedSoCState *soc = &bmc->soc; + AspeedSoCState *soc = bmc->soc; DeviceState *dev; dev = DEVICE(i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 3), @@ -708,7 +709,7 @@ static void g220a_bmc_i2c_init(AspeedMachineState *bmc) static void fp5280g2_bmc_i2c_init(AspeedMachineState *bmc) { - AspeedSoCState *soc = &bmc->soc; + AspeedSoCState *soc = bmc->soc; I2CSlave *i2c_mux; /* The at24c256 */ @@ -735,7 +736,7 @@ static void fp5280g2_bmc_i2c_init(AspeedMachineState *bmc) static void rainier_bmc_i2c_init(AspeedMachineState *bmc) { - AspeedSoCState *soc = &bmc->soc; + AspeedSoCState *soc = bmc->soc; I2CSlave *i2c_mux; at24c_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 0), 0x51, 32 * KiB); @@ -852,7 +853,7 @@ static void get_pca9548_channels(I2CBus *bus, uint8_t mux_addr, static void fuji_bmc_i2c_init(AspeedMachineState *bmc) { - AspeedSoCState *soc = &bmc->soc; + AspeedSoCState *soc = bmc->soc; I2CBus *i2c[144] = {}; for (int i = 0; i < 16; i++) { @@ -930,7 +931,7 @@ static void fuji_bmc_i2c_init(AspeedMachineState *bmc) static void bletchley_bmc_i2c_init(AspeedMachineState *bmc) { - AspeedSoCState *soc = &bmc->soc; + AspeedSoCState *soc = bmc->soc; I2CBus *i2c[13] = {}; for (int i = 0; i < 13; i++) { if ((i == 8) || (i == 11)) { @@ -976,7 +977,7 @@ static void bletchley_bmc_i2c_init(AspeedMachineState *bmc) static void fby35_i2c_init(AspeedMachineState *bmc) { - AspeedSoCState *soc = &bmc->soc; + AspeedSoCState *soc = bmc->soc; I2CBus *i2c[16]; for (int i = 0; i < 16; i++) { @@ -1008,14 +1009,14 @@ static void fby35_i2c_init(AspeedMachineState *bmc) static void qcom_dc_scm_bmc_i2c_init(AspeedMachineState *bmc) { - AspeedSoCState *soc = &bmc->soc; + AspeedSoCState *soc = bmc->soc; i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 15), "tmp105", 0x4d); } static void qcom_dc_scm_firework_i2c_init(AspeedMachineState *bmc) { - AspeedSoCState *soc = &bmc->soc; + AspeedSoCState *soc = bmc->soc; I2CSlave *therm_mux, *cpuvr_mux; /* Create the generic DC-SCM hardware */ @@ -1477,7 +1478,7 @@ static void aspeed_machine_bletchley_class_init(ObjectClass *oc, void *data) static void fby35_reset(MachineState *state, ShutdownCause reason) { AspeedMachineState *bmc = ASPEED_MACHINE(state); - AspeedGPIOState *gpio = &bmc->soc.gpio; + AspeedGPIOState *gpio = &bmc->soc->gpio; qemu_devices_reset(reason); @@ -1528,24 +1529,26 @@ static void aspeed_minibmc_machine_init(MachineState *machine) sysclk = clock_new(OBJECT(machine), "SYSCLK"); clock_set_hz(sysclk, SYSCLK_FRQ); - object_initialize_child(OBJECT(machine), "soc", &bmc->soc, amc->soc_name); - qdev_connect_clock_in(DEVICE(&bmc->soc), "sysclk", sysclk); + bmc->soc = ASPEED_SOC(object_new(amc->soc_name)); + object_property_add_child(OBJECT(machine), "soc", OBJECT(bmc->soc)); + object_unref(OBJECT(bmc->soc)); + qdev_connect_clock_in(DEVICE(bmc->soc), "sysclk", sysclk); - object_property_set_link(OBJECT(&bmc->soc), "memory", + object_property_set_link(OBJECT(bmc->soc), "memory", OBJECT(get_system_memory()), &error_abort); connect_serial_hds_to_uarts(bmc); - qdev_realize(DEVICE(&bmc->soc), NULL, &error_abort); + qdev_realize(DEVICE(bmc->soc), NULL, &error_abort); - aspeed_board_init_flashes(&bmc->soc.fmc, + aspeed_board_init_flashes(&bmc->soc->fmc, bmc->fmc_model ? bmc->fmc_model : amc->fmc_model, amc->num_cs, 0); - aspeed_board_init_flashes(&bmc->soc.spi[0], + aspeed_board_init_flashes(&bmc->soc->spi[0], bmc->spi_model ? bmc->spi_model : amc->spi_model, amc->num_cs, amc->num_cs); - aspeed_board_init_flashes(&bmc->soc.spi[1], + aspeed_board_init_flashes(&bmc->soc->spi[1], bmc->spi_model ? bmc->spi_model : amc->spi_model, amc->num_cs, (amc->num_cs * 2)); @@ -1561,7 +1564,7 @@ static void aspeed_minibmc_machine_init(MachineState *machine) static void ast1030_evb_i2c_init(AspeedMachineState *bmc) { - AspeedSoCState *soc = &bmc->soc; + AspeedSoCState *soc = bmc->soc; /* U10 24C08 connects to SDA/SCL Group 1 by default */ uint8_t *eeprom_buf = g_malloc0(32 * 1024); From df4ab0764db4490d70e990b786f0f4928f83d596 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 24 Oct 2023 18:24:16 +0200 Subject: [PATCH 008/974] hw/arm/aspeed: Introduce TYPE_ASPEED10X0_SOC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TYPE_ASPEED10X0_SOC inherits from TYPE_ASPEED_SOC. In few commits we'll add more fields, but to keep review process simple, don't add any yet. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast10x0.c | 26 +++++++++++++------------- include/hw/arm/aspeed_soc.h | 7 +++++++ 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/hw/arm/aspeed_ast10x0.c b/hw/arm/aspeed_ast10x0.c index 649b3b13c1..1c15bf422f 100644 --- a/hw/arm/aspeed_ast10x0.c +++ b/hw/arm/aspeed_ast10x0.c @@ -435,18 +435,18 @@ static void aspeed_soc_ast1030_class_init(ObjectClass *klass, void *data) sc->get_irq = aspeed_soc_ast1030_get_irq; } -static const TypeInfo aspeed_soc_ast1030_type_info = { - .name = "ast1030-a1", - .parent = TYPE_ASPEED_SOC, - .instance_size = sizeof(AspeedSoCState), - .instance_init = aspeed_soc_ast1030_init, - .class_init = aspeed_soc_ast1030_class_init, - .class_size = sizeof(AspeedSoCClass), +static const TypeInfo aspeed_soc_ast10x0_types[] = { + { + .name = TYPE_ASPEED10X0_SOC, + .parent = TYPE_ASPEED_SOC, + .instance_size = sizeof(Aspeed10x0SoCState), + .abstract = true, + }, { + .name = "ast1030-a1", + .parent = TYPE_ASPEED10X0_SOC, + .instance_init = aspeed_soc_ast1030_init, + .class_init = aspeed_soc_ast1030_class_init, + }, }; -static void aspeed_soc_register_types(void) -{ - type_register_static(&aspeed_soc_ast1030_type_info); -} - -type_init(aspeed_soc_register_types) +DEFINE_TYPES(aspeed_soc_ast10x0_types) diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index 8adff70072..dcb43a4ecd 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -101,6 +101,13 @@ struct AspeedSoCState { #define TYPE_ASPEED_SOC "aspeed-soc" OBJECT_DECLARE_TYPE(AspeedSoCState, AspeedSoCClass, ASPEED_SOC) +struct Aspeed10x0SoCState { + AspeedSoCState parent; +}; + +#define TYPE_ASPEED10X0_SOC "aspeed10x0-soc" +OBJECT_DECLARE_SIMPLE_TYPE(Aspeed10x0SoCState, ASPEED10X0_SOC) + struct AspeedSoCClass { DeviceClass parent_class; From 4fc5e8065b3632cec2b9cfbb62b3b4d053660576 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 24 Oct 2023 18:24:17 +0200 Subject: [PATCH 009/974] hw/arm/aspeed: Introduce TYPE_ASPEED2600_SOC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TYPE_ASPEED2600_SOC inherits from TYPE_ASPEED_SOC. In few commits we'll add more fields, but to keep review process simple, don't add any yet. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast2600.c | 26 +++++++++++++------------- include/hw/arm/aspeed_soc.h | 7 +++++++ 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c index e122e1c32d..1ee460e56c 100644 --- a/hw/arm/aspeed_ast2600.c +++ b/hw/arm/aspeed_ast2600.c @@ -646,18 +646,18 @@ static void aspeed_soc_ast2600_class_init(ObjectClass *oc, void *data) sc->get_irq = aspeed_soc_ast2600_get_irq; } -static const TypeInfo aspeed_soc_ast2600_type_info = { - .name = "ast2600-a3", - .parent = TYPE_ASPEED_SOC, - .instance_size = sizeof(AspeedSoCState), - .instance_init = aspeed_soc_ast2600_init, - .class_init = aspeed_soc_ast2600_class_init, - .class_size = sizeof(AspeedSoCClass), +static const TypeInfo aspeed_soc_ast2600_types[] = { + { + .name = TYPE_ASPEED2600_SOC, + .parent = TYPE_ASPEED_SOC, + .instance_size = sizeof(Aspeed2600SoCState), + .abstract = true, + }, { + .name = "ast2600-a3", + .parent = TYPE_ASPEED2600_SOC, + .instance_init = aspeed_soc_ast2600_init, + .class_init = aspeed_soc_ast2600_class_init, + }, }; -static void aspeed_soc_register_types(void) -{ - type_register_static(&aspeed_soc_ast2600_type_info); -}; - -type_init(aspeed_soc_register_types) +DEFINE_TYPES(aspeed_soc_ast2600_types) diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index dcb43a4ecd..103b1598f6 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -101,6 +101,13 @@ struct AspeedSoCState { #define TYPE_ASPEED_SOC "aspeed-soc" OBJECT_DECLARE_TYPE(AspeedSoCState, AspeedSoCClass, ASPEED_SOC) +struct Aspeed2600SoCState { + AspeedSoCState parent; +}; + +#define TYPE_ASPEED2600_SOC "aspeed2600-soc" +OBJECT_DECLARE_SIMPLE_TYPE(Aspeed2600SoCState, ASPEED2600_SOC) + struct Aspeed10x0SoCState { AspeedSoCState parent; }; From 1a94fae4c18aacc563a6c6bf61aa7f357f1e2eb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 24 Oct 2023 18:24:18 +0200 Subject: [PATCH 010/974] hw/arm/aspeed: Introduce TYPE_ASPEED2400_SOC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TYPE_ASPEED2400_SOC inherits from TYPE_ASPEED_SOC. In few commits we'll add more fields, but to keep review process simple, don't add any yet. TYPE_ASPEED_SOC is common to various Aspeed SoCs, define it in aspeed_soc_common.c. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_soc.c | 61 +++++++++++-------------------------- hw/arm/aspeed_soc_common.c | 29 ++++++++++++++++++ include/hw/arm/aspeed_soc.h | 7 +++++ 3 files changed, 53 insertions(+), 44 deletions(-) diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c index 191276a320..dfb97f6dbd 100644 --- a/hw/arm/aspeed_soc.c +++ b/hw/arm/aspeed_soc.c @@ -497,29 +497,6 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->hace), 0, aspeed_soc_get_irq(s, ASPEED_DEV_HACE)); } -static Property aspeed_soc_properties[] = { - DEFINE_PROP_LINK("memory", AspeedSoCState, memory, TYPE_MEMORY_REGION, - MemoryRegion *), - DEFINE_PROP_LINK("dram", AspeedSoCState, dram_mr, TYPE_MEMORY_REGION, - MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), -}; - -static void aspeed_soc_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - device_class_set_props(dc, aspeed_soc_properties); -} - -static const TypeInfo aspeed_soc_type_info = { - .name = TYPE_ASPEED_SOC, - .parent = TYPE_DEVICE, - .instance_size = sizeof(AspeedSoCState), - .class_size = sizeof(AspeedSoCClass), - .class_init = aspeed_soc_class_init, - .abstract = true, -}; static void aspeed_soc_ast2400_class_init(ObjectClass *oc, void *data) { @@ -545,14 +522,6 @@ static void aspeed_soc_ast2400_class_init(ObjectClass *oc, void *data) sc->get_irq = aspeed_soc_ast2400_get_irq; } -static const TypeInfo aspeed_soc_ast2400_type_info = { - .name = "ast2400-a1", - .parent = TYPE_ASPEED_SOC, - .instance_init = aspeed_ast2400_soc_init, - .instance_size = sizeof(AspeedSoCState), - .class_init = aspeed_soc_ast2400_class_init, -}; - static void aspeed_soc_ast2500_class_init(ObjectClass *oc, void *data) { AspeedSoCClass *sc = ASPEED_SOC_CLASS(oc); @@ -577,18 +546,22 @@ static void aspeed_soc_ast2500_class_init(ObjectClass *oc, void *data) sc->get_irq = aspeed_soc_ast2400_get_irq; } -static const TypeInfo aspeed_soc_ast2500_type_info = { - .name = "ast2500-a1", - .parent = TYPE_ASPEED_SOC, - .instance_init = aspeed_ast2400_soc_init, - .instance_size = sizeof(AspeedSoCState), - .class_init = aspeed_soc_ast2500_class_init, -}; -static void aspeed_soc_register_types(void) -{ - type_register_static(&aspeed_soc_type_info); - type_register_static(&aspeed_soc_ast2400_type_info); - type_register_static(&aspeed_soc_ast2500_type_info); +static const TypeInfo aspeed_soc_ast2400_types[] = { + { + .name = TYPE_ASPEED2400_SOC, + .parent = TYPE_ASPEED_SOC, + .instance_init = aspeed_ast2400_soc_init, + .instance_size = sizeof(Aspeed2400SoCState), + .abstract = true, + }, { + .name = "ast2400-a1", + .parent = TYPE_ASPEED2400_SOC, + .class_init = aspeed_soc_ast2400_class_init, + }, { + .name = "ast2500-a1", + .parent = TYPE_ASPEED2400_SOC, + .class_init = aspeed_soc_ast2500_class_init, + }, }; -type_init(aspeed_soc_register_types); +DEFINE_TYPES(aspeed_soc_ast2400_types) diff --git a/hw/arm/aspeed_soc_common.c b/hw/arm/aspeed_soc_common.c index a43f5d2a6f..b66f769d18 100644 --- a/hw/arm/aspeed_soc_common.c +++ b/hw/arm/aspeed_soc_common.c @@ -12,6 +12,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "hw/qdev-properties.h" #include "hw/misc/unimp.h" #include "hw/arm/aspeed_soc.h" #include "hw/char/serial.h" @@ -112,3 +113,31 @@ void aspeed_mmio_map_unimplemented(AspeedSoCState *s, SysBusDevice *dev, memory_region_add_subregion_overlap(s->memory, addr, sysbus_mmio_get_region(dev, 0), -1000); } + +static Property aspeed_soc_properties[] = { + DEFINE_PROP_LINK("dram", AspeedSoCState, dram_mr, TYPE_MEMORY_REGION, + MemoryRegion *), + DEFINE_PROP_LINK("memory", AspeedSoCState, memory, TYPE_MEMORY_REGION, + MemoryRegion *), + DEFINE_PROP_END_OF_LIST(), +}; + +static void aspeed_soc_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + device_class_set_props(dc, aspeed_soc_properties); +} + +static const TypeInfo aspeed_soc_types[] = { + { + .name = TYPE_ASPEED_SOC, + .parent = TYPE_DEVICE, + .instance_size = sizeof(AspeedSoCState), + .class_size = sizeof(AspeedSoCClass), + .class_init = aspeed_soc_class_init, + .abstract = true, + }, +}; + +DEFINE_TYPES(aspeed_soc_types) diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index 103b1598f6..ee7926b81c 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -101,6 +101,13 @@ struct AspeedSoCState { #define TYPE_ASPEED_SOC "aspeed-soc" OBJECT_DECLARE_TYPE(AspeedSoCState, AspeedSoCClass, ASPEED_SOC) +struct Aspeed2400SoCState { + AspeedSoCState parent; +}; + +#define TYPE_ASPEED2400_SOC "aspeed2400-soc" +OBJECT_DECLARE_SIMPLE_TYPE(Aspeed2400SoCState, ASPEED2400_SOC) + struct Aspeed2600SoCState { AspeedSoCState parent; }; From 24a88476ffbecdd6ffb96a5298d90de10176a301 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 24 Oct 2023 18:24:19 +0200 Subject: [PATCH 011/974] hw/arm/aspeed: Check 'memory' link is set in common aspeed_soc_realize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_soc_common.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/hw/arm/aspeed_soc_common.c b/hw/arm/aspeed_soc_common.c index b66f769d18..828f61093b 100644 --- a/hw/arm/aspeed_soc_common.c +++ b/hw/arm/aspeed_soc_common.c @@ -114,6 +114,16 @@ void aspeed_mmio_map_unimplemented(AspeedSoCState *s, SysBusDevice *dev, sysbus_mmio_get_region(dev, 0), -1000); } +static void aspeed_soc_realize(DeviceState *dev, Error **errp) +{ + AspeedSoCState *s = ASPEED_SOC(dev); + + if (!s->memory) { + error_setg(errp, "'memory' link is not set"); + return; + } +} + static Property aspeed_soc_properties[] = { DEFINE_PROP_LINK("dram", AspeedSoCState, dram_mr, TYPE_MEMORY_REGION, MemoryRegion *), @@ -126,6 +136,7 @@ static void aspeed_soc_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); + dc->realize = aspeed_soc_realize; device_class_set_props(dc, aspeed_soc_properties); } From a0c21030705246fc53703253d9b9fccd88aa88d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 24 Oct 2023 18:24:20 +0200 Subject: [PATCH 012/974] hw/arm/aspeed: Move AspeedSoCState::armv7m to Aspeed10x0SoCState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The v7-M core is specific to the Aspeed 10x0 series, remove it from the common AspeedSoCState. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast10x0.c | 27 +++++++++++++++------------ hw/arm/fby35.c | 13 ++++++++----- include/hw/arm/aspeed_soc.h | 5 ++--- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/hw/arm/aspeed_ast10x0.c b/hw/arm/aspeed_ast10x0.c index 1c15bf422f..8becb146a8 100644 --- a/hw/arm/aspeed_ast10x0.c +++ b/hw/arm/aspeed_ast10x0.c @@ -101,13 +101,15 @@ static const int aspeed_soc_ast1030_irqmap[] = { static qemu_irq aspeed_soc_ast1030_get_irq(AspeedSoCState *s, int dev) { + Aspeed10x0SoCState *a = ASPEED10X0_SOC(s); AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); - return qdev_get_gpio_in(DEVICE(&s->armv7m), sc->irqmap[dev]); + return qdev_get_gpio_in(DEVICE(&a->armv7m), sc->irqmap[dev]); } static void aspeed_soc_ast1030_init(Object *obj) { + Aspeed10x0SoCState *a = ASPEED10X0_SOC(obj); AspeedSoCState *s = ASPEED_SOC(obj); AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); char socname[8]; @@ -118,7 +120,7 @@ static void aspeed_soc_ast1030_init(Object *obj) g_assert_not_reached(); } - object_initialize_child(obj, "armv7m", &s->armv7m, TYPE_ARMV7M); + object_initialize_child(obj, "armv7m", &a->armv7m, TYPE_ARMV7M); s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0); @@ -185,6 +187,7 @@ static void aspeed_soc_ast1030_init(Object *obj) static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) { + Aspeed10x0SoCState *a = ASPEED10X0_SOC(dev_soc); AspeedSoCState *s = ASPEED_SOC(dev_soc); AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); DeviceState *armv7m; @@ -206,17 +209,17 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) 0x40000); /* AST1030 CPU Core */ - armv7m = DEVICE(&s->armv7m); + armv7m = DEVICE(&a->armv7m); qdev_prop_set_uint32(armv7m, "num-irq", 256); qdev_prop_set_string(armv7m, "cpu-type", sc->cpu_type); qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk); - object_property_set_link(OBJECT(&s->armv7m), "memory", + object_property_set_link(OBJECT(&a->armv7m), "memory", OBJECT(s->memory), &error_abort); - sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), &error_abort); + sysbus_realize(SYS_BUS_DEVICE(&a->armv7m), &error_abort); /* Internal SRAM */ sram_name = g_strdup_printf("aspeed.sram.%d", - CPU(s->armv7m.cpu)->cpu_index); + CPU(a->armv7m.cpu)->cpu_index); memory_region_init_ram(&s->sram, OBJECT(s), sram_name, sc->sram_size, &err); if (err != NULL) { error_propagate(errp, err); @@ -249,7 +252,7 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) } aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->i2c), 0, sc->memmap[ASPEED_DEV_I2C]); for (i = 0; i < ASPEED_I2C_GET_CLASS(&s->i2c)->num_busses; i++) { - qemu_irq irq = qdev_get_gpio_in(DEVICE(&s->armv7m), + qemu_irq irq = qdev_get_gpio_in(DEVICE(&a->armv7m), sc->irqmap[ASPEED_DEV_I2C] + i); /* The AST1030 I2C controller has one IRQ per bus. */ sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c.busses[i]), 0, irq); @@ -261,7 +264,7 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) } aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->i3c), 0, sc->memmap[ASPEED_DEV_I3C]); for (i = 0; i < ASPEED_I3C_NR_DEVICES; i++) { - qemu_irq irq = qdev_get_gpio_in(DEVICE(&s->armv7m), + qemu_irq irq = qdev_get_gpio_in(DEVICE(&a->armv7m), sc->irqmap[ASPEED_DEV_I3C] + i); /* The AST1030 I3C controller has one IRQ per bus. */ sysbus_connect_irq(SYS_BUS_DEVICE(&s->i3c.devices[i]), 0, irq); @@ -290,19 +293,19 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) * On the AST1030 LPC subdevice IRQs are connected straight to the GIC. */ sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_1, - qdev_get_gpio_in(DEVICE(&s->armv7m), + qdev_get_gpio_in(DEVICE(&a->armv7m), sc->irqmap[ASPEED_DEV_KCS] + aspeed_lpc_kcs_1)); sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_2, - qdev_get_gpio_in(DEVICE(&s->armv7m), + qdev_get_gpio_in(DEVICE(&a->armv7m), sc->irqmap[ASPEED_DEV_KCS] + aspeed_lpc_kcs_2)); sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_3, - qdev_get_gpio_in(DEVICE(&s->armv7m), + qdev_get_gpio_in(DEVICE(&a->armv7m), sc->irqmap[ASPEED_DEV_KCS] + aspeed_lpc_kcs_3)); sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_4, - qdev_get_gpio_in(DEVICE(&s->armv7m), + qdev_get_gpio_in(DEVICE(&a->armv7m), sc->irqmap[ASPEED_DEV_KCS] + aspeed_lpc_kcs_4)); /* UART */ diff --git a/hw/arm/fby35.c b/hw/arm/fby35.c index f2ff6c1abf..c8bc75d870 100644 --- a/hw/arm/fby35.c +++ b/hw/arm/fby35.c @@ -28,7 +28,7 @@ struct Fby35State { Clock *bic_sysclk; AspeedSoCState bmc; - AspeedSoCState bic; + Aspeed10x0SoCState bic; bool mmio_exec; }; @@ -114,10 +114,13 @@ static void fby35_bmc_init(Fby35State *s) static void fby35_bic_init(Fby35State *s) { + AspeedSoCState *soc; + s->bic_sysclk = clock_new(OBJECT(s), "SYSCLK"); clock_set_hz(s->bic_sysclk, 200000000ULL); object_initialize_child(OBJECT(s), "bic", &s->bic, "ast1030-a1"); + soc = ASPEED_SOC(&s->bic); memory_region_init(&s->bic_memory, OBJECT(&s->bic), "bic-memory", UINT64_MAX); @@ -125,12 +128,12 @@ static void fby35_bic_init(Fby35State *s) qdev_connect_clock_in(DEVICE(&s->bic), "sysclk", s->bic_sysclk); object_property_set_link(OBJECT(&s->bic), "memory", OBJECT(&s->bic_memory), &error_abort); - aspeed_soc_uart_set_chr(&s->bic, ASPEED_DEV_UART5, serial_hd(1)); + aspeed_soc_uart_set_chr(soc, ASPEED_DEV_UART5, serial_hd(1)); qdev_realize(DEVICE(&s->bic), NULL, &error_abort); - aspeed_board_init_flashes(&s->bic.fmc, "sst25vf032b", 2, 2); - aspeed_board_init_flashes(&s->bic.spi[0], "sst25vf032b", 2, 4); - aspeed_board_init_flashes(&s->bic.spi[1], "sst25vf032b", 2, 6); + aspeed_board_init_flashes(&soc->fmc, "sst25vf032b", 2, 2); + aspeed_board_init_flashes(&soc->spi[0], "sst25vf032b", 2, 4); + aspeed_board_init_flashes(&soc->spi[1], "sst25vf032b", 2, 6); } static void fby35_init(MachineState *machine) diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index ee7926b81c..2118a441f7 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -47,13 +47,10 @@ #define ASPEED_JTAG_NUM 2 struct AspeedSoCState { - /*< private >*/ DeviceState parent; - /*< public >*/ ARMCPU cpu[ASPEED_CPUS_NUM]; A15MPPrivState a7mpcore; - ARMv7MState armv7m; MemoryRegion *memory; MemoryRegion *dram_mr; MemoryRegion dram_container; @@ -117,6 +114,8 @@ OBJECT_DECLARE_SIMPLE_TYPE(Aspeed2600SoCState, ASPEED2600_SOC) struct Aspeed10x0SoCState { AspeedSoCState parent; + + ARMv7MState armv7m; }; #define TYPE_ASPEED10X0_SOC "aspeed10x0-soc" From c17fc025714faa031cd6570ca2f74a0d5b008431 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 24 Oct 2023 18:24:21 +0200 Subject: [PATCH 013/974] hw/arm/aspeed: Move AspeedSoCState::a7mpcore to Aspeed2600SoCState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The v7-A cluster is specific to the Aspeed 2600 series, remove it from the common AspeedSoCState. The ARM cores belong to the MP cluster, but the array is currently used by TYPE_ASPEED2600_SOC. We'll clean that soon, but for now keep it in Aspeed2600SoCState. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast2600.c | 49 ++++++++++++++++++++----------------- hw/arm/fby35.c | 14 ++++++----- include/hw/arm/aspeed_soc.h | 4 ++- 3 files changed, 37 insertions(+), 30 deletions(-) diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c index 1ee460e56c..b965fbab5e 100644 --- a/hw/arm/aspeed_ast2600.c +++ b/hw/arm/aspeed_ast2600.c @@ -137,13 +137,15 @@ static const int aspeed_soc_ast2600_irqmap[] = { static qemu_irq aspeed_soc_ast2600_get_irq(AspeedSoCState *s, int dev) { + Aspeed2600SoCState *a = ASPEED2600_SOC(s); AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); - return qdev_get_gpio_in(DEVICE(&s->a7mpcore), sc->irqmap[dev]); + return qdev_get_gpio_in(DEVICE(&a->a7mpcore), sc->irqmap[dev]); } static void aspeed_soc_ast2600_init(Object *obj) { + Aspeed2600SoCState *a = ASPEED2600_SOC(obj); AspeedSoCState *s = ASPEED_SOC(obj); AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); int i; @@ -155,7 +157,7 @@ static void aspeed_soc_ast2600_init(Object *obj) } for (i = 0; i < sc->num_cpus; i++) { - object_initialize_child(obj, "cpu[*]", &s->cpu[i], sc->cpu_type); + object_initialize_child(obj, "cpu[*]", &a->cpu[i], sc->cpu_type); } snprintf(typename, sizeof(typename), "aspeed.scu-%s", socname); @@ -169,7 +171,7 @@ static void aspeed_soc_ast2600_init(Object *obj) object_property_add_alias(obj, "hw-prot-key", OBJECT(&s->scu), "hw-prot-key"); - object_initialize_child(obj, "a7mpcore", &s->a7mpcore, + object_initialize_child(obj, "a7mpcore", &a->a7mpcore, TYPE_A15MPCORE_PRIV); object_initialize_child(obj, "rtc", &s->rtc, TYPE_ASPEED_RTC); @@ -277,6 +279,7 @@ static uint64_t aspeed_calc_affinity(int cpu) static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) { int i; + Aspeed2600SoCState *a = ASPEED2600_SOC(dev); AspeedSoCState *s = ASPEED_SOC(dev); AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); Error *err = NULL; @@ -306,39 +309,39 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) /* CPU */ for (i = 0; i < sc->num_cpus; i++) { if (sc->num_cpus > 1) { - object_property_set_int(OBJECT(&s->cpu[i]), "reset-cbar", + object_property_set_int(OBJECT(&a->cpu[i]), "reset-cbar", ASPEED_A7MPCORE_ADDR, &error_abort); } - object_property_set_int(OBJECT(&s->cpu[i]), "mp-affinity", + object_property_set_int(OBJECT(&a->cpu[i]), "mp-affinity", aspeed_calc_affinity(i), &error_abort); - object_property_set_int(OBJECT(&s->cpu[i]), "cntfrq", 1125000000, + object_property_set_int(OBJECT(&a->cpu[i]), "cntfrq", 1125000000, &error_abort); - object_property_set_bool(OBJECT(&s->cpu[i]), "neon", false, + object_property_set_bool(OBJECT(&a->cpu[i]), "neon", false, &error_abort); - object_property_set_bool(OBJECT(&s->cpu[i]), "vfp-d32", false, + object_property_set_bool(OBJECT(&a->cpu[i]), "vfp-d32", false, &error_abort); - object_property_set_link(OBJECT(&s->cpu[i]), "memory", + object_property_set_link(OBJECT(&a->cpu[i]), "memory", OBJECT(s->memory), &error_abort); - if (!qdev_realize(DEVICE(&s->cpu[i]), NULL, errp)) { + if (!qdev_realize(DEVICE(&a->cpu[i]), NULL, errp)) { return; } } /* A7MPCORE */ - object_property_set_int(OBJECT(&s->a7mpcore), "num-cpu", sc->num_cpus, + object_property_set_int(OBJECT(&a->a7mpcore), "num-cpu", sc->num_cpus, &error_abort); - object_property_set_int(OBJECT(&s->a7mpcore), "num-irq", + object_property_set_int(OBJECT(&a->a7mpcore), "num-irq", ROUND_UP(AST2600_MAX_IRQ + GIC_INTERNAL, 32), &error_abort); - sysbus_realize(SYS_BUS_DEVICE(&s->a7mpcore), &error_abort); - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->a7mpcore), 0, ASPEED_A7MPCORE_ADDR); + sysbus_realize(SYS_BUS_DEVICE(&a->a7mpcore), &error_abort); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->a7mpcore), 0, ASPEED_A7MPCORE_ADDR); for (i = 0; i < sc->num_cpus; i++) { - SysBusDevice *sbd = SYS_BUS_DEVICE(&s->a7mpcore); - DeviceState *d = DEVICE(&s->cpu[i]); + SysBusDevice *sbd = SYS_BUS_DEVICE(&a->a7mpcore); + DeviceState *d = DEVICE(&a->cpu[i]); irq = qdev_get_gpio_in(d, ARM_CPU_IRQ); sysbus_connect_irq(sbd, i, irq); @@ -351,7 +354,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) } /* SRAM */ - sram_name = g_strdup_printf("aspeed.sram.%d", CPU(&s->cpu[0])->cpu_index); + sram_name = g_strdup_printf("aspeed.sram.%d", CPU(&a->cpu[0])->cpu_index); memory_region_init_ram(&s->sram, OBJECT(s), sram_name, sc->sram_size, &err); if (err) { error_propagate(errp, err); @@ -413,7 +416,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) } aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->i2c), 0, sc->memmap[ASPEED_DEV_I2C]); for (i = 0; i < ASPEED_I2C_GET_CLASS(&s->i2c)->num_busses; i++) { - irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), + irq = qdev_get_gpio_in(DEVICE(&a->a7mpcore), sc->irqmap[ASPEED_DEV_I2C] + i); /* The AST2600 I2C controller has one IRQ per bus. */ sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c.busses[i]), 0, irq); @@ -579,19 +582,19 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) * offset 0. */ sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_1, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), + qdev_get_gpio_in(DEVICE(&a->a7mpcore), sc->irqmap[ASPEED_DEV_KCS] + aspeed_lpc_kcs_1)); sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_2, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), + qdev_get_gpio_in(DEVICE(&a->a7mpcore), sc->irqmap[ASPEED_DEV_KCS] + aspeed_lpc_kcs_2)); sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_3, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), + qdev_get_gpio_in(DEVICE(&a->a7mpcore), sc->irqmap[ASPEED_DEV_KCS] + aspeed_lpc_kcs_3)); sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_4, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), + qdev_get_gpio_in(DEVICE(&a->a7mpcore), sc->irqmap[ASPEED_DEV_KCS] + aspeed_lpc_kcs_4)); /* HACE */ @@ -611,7 +614,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) } aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->i3c), 0, sc->memmap[ASPEED_DEV_I3C]); for (i = 0; i < ASPEED_I3C_NR_DEVICES; i++) { - irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), + irq = qdev_get_gpio_in(DEVICE(&a->a7mpcore), sc->irqmap[ASPEED_DEV_I3C] + i); /* The AST2600 I3C controller has one IRQ per bus. */ sysbus_connect_irq(SYS_BUS_DEVICE(&s->i3c.devices[i]), 0, irq); diff --git a/hw/arm/fby35.c b/hw/arm/fby35.c index c8bc75d870..c9964bd283 100644 --- a/hw/arm/fby35.c +++ b/hw/arm/fby35.c @@ -27,7 +27,7 @@ struct Fby35State { MemoryRegion bic_memory; Clock *bic_sysclk; - AspeedSoCState bmc; + Aspeed2600SoCState bmc; Aspeed10x0SoCState bic; bool mmio_exec; @@ -70,7 +70,10 @@ static void fby35_bmc_write_boot_rom(DriveInfo *dinfo, MemoryRegion *mr, static void fby35_bmc_init(Fby35State *s) { + AspeedSoCState *soc; + object_initialize_child(OBJECT(s), "bmc", &s->bmc, "ast2600-a3"); + soc = ASPEED_SOC(&s->bmc); memory_region_init(&s->bmc_memory, OBJECT(&s->bmc), "bmc-memory", UINT64_MAX); @@ -87,22 +90,21 @@ static void fby35_bmc_init(Fby35State *s) &error_abort); object_property_set_int(OBJECT(&s->bmc), "hw-strap2", 0x00000003, &error_abort); - aspeed_soc_uart_set_chr(&s->bmc, ASPEED_DEV_UART5, serial_hd(0)); + aspeed_soc_uart_set_chr(soc, ASPEED_DEV_UART5, serial_hd(0)); qdev_realize(DEVICE(&s->bmc), NULL, &error_abort); - aspeed_board_init_flashes(&s->bmc.fmc, "n25q00", 2, 0); + aspeed_board_init_flashes(&soc->fmc, "n25q00", 2, 0); /* Install first FMC flash content as a boot rom. */ if (!s->mmio_exec) { DriveInfo *mtd0 = drive_get(IF_MTD, 0, 0); if (mtd0) { - AspeedSoCState *bmc = &s->bmc; - uint64_t rom_size = memory_region_size(&bmc->spi_boot); + uint64_t rom_size = memory_region_size(&soc->spi_boot); memory_region_init_rom(&s->bmc_boot_rom, NULL, "aspeed.boot_rom", rom_size, &error_abort); - memory_region_add_subregion_overlap(&bmc->spi_boot_container, 0, + memory_region_add_subregion_overlap(&soc->spi_boot_container, 0, &s->bmc_boot_rom, 1); fby35_bmc_write_boot_rom(mtd0, &s->bmc_boot_rom, diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index 2118a441f7..6f783138e1 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -50,7 +50,6 @@ struct AspeedSoCState { DeviceState parent; ARMCPU cpu[ASPEED_CPUS_NUM]; - A15MPPrivState a7mpcore; MemoryRegion *memory; MemoryRegion *dram_mr; MemoryRegion dram_container; @@ -107,6 +106,9 @@ OBJECT_DECLARE_SIMPLE_TYPE(Aspeed2400SoCState, ASPEED2400_SOC) struct Aspeed2600SoCState { AspeedSoCState parent; + + A15MPPrivState a7mpcore; + ARMCPU cpu[ASPEED_CPUS_NUM]; /* XXX belong to a7mpcore */ }; #define TYPE_ASPEED2600_SOC "aspeed2600-soc" From dd41ce7a6f13ad4f45ebaf52b9fa91fe5fc961df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 24 Oct 2023 18:24:22 +0200 Subject: [PATCH 014/974] hw/arm/aspeed: Move AspeedSoCState::cpu/vic to Aspeed2400SoCState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ARM array and VIC peripheral are only used by the 2400 series, remove them from the common AspeedSoCState. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/arm/{aspeed_soc.c => aspeed_ast2400.c} | 27 +++++++++++++---------- hw/arm/meson.build | 2 +- include/hw/arm/aspeed_soc.h | 5 +++-- 3 files changed, 19 insertions(+), 15 deletions(-) rename hw/arm/{aspeed_soc.c => aspeed_ast2400.c} (95%) diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_ast2400.c similarity index 95% rename from hw/arm/aspeed_soc.c rename to hw/arm/aspeed_ast2400.c index dfb97f6dbd..a4334c81b8 100644 --- a/hw/arm/aspeed_soc.c +++ b/hw/arm/aspeed_ast2400.c @@ -135,13 +135,15 @@ static const int aspeed_soc_ast2400_irqmap[] = { static qemu_irq aspeed_soc_ast2400_get_irq(AspeedSoCState *s, int dev) { + Aspeed2400SoCState *a = ASPEED2400_SOC(s); AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); - return qdev_get_gpio_in(DEVICE(&s->vic), sc->irqmap[dev]); + return qdev_get_gpio_in(DEVICE(&a->vic), sc->irqmap[dev]); } static void aspeed_ast2400_soc_init(Object *obj) { + Aspeed2400SoCState *a = ASPEED2400_SOC(obj); AspeedSoCState *s = ASPEED_SOC(obj); AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); int i; @@ -153,7 +155,7 @@ static void aspeed_ast2400_soc_init(Object *obj) } for (i = 0; i < sc->num_cpus; i++) { - object_initialize_child(obj, "cpu[*]", &s->cpu[i], sc->cpu_type); + object_initialize_child(obj, "cpu[*]", &a->cpu[i], sc->cpu_type); } snprintf(typename, sizeof(typename), "aspeed.scu-%s", socname); @@ -167,7 +169,7 @@ static void aspeed_ast2400_soc_init(Object *obj) object_property_add_alias(obj, "hw-prot-key", OBJECT(&s->scu), "hw-prot-key"); - object_initialize_child(obj, "vic", &s->vic, TYPE_ASPEED_VIC); + object_initialize_child(obj, "vic", &a->vic, TYPE_ASPEED_VIC); object_initialize_child(obj, "rtc", &s->rtc, TYPE_ASPEED_RTC); @@ -242,6 +244,7 @@ static void aspeed_ast2400_soc_init(Object *obj) static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) { int i; + Aspeed2400SoCState *a = ASPEED2400_SOC(dev); AspeedSoCState *s = ASPEED_SOC(dev); AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); Error *err = NULL; @@ -264,15 +267,15 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) /* CPU */ for (i = 0; i < sc->num_cpus; i++) { - object_property_set_link(OBJECT(&s->cpu[i]), "memory", + object_property_set_link(OBJECT(&a->cpu[i]), "memory", OBJECT(s->memory), &error_abort); - if (!qdev_realize(DEVICE(&s->cpu[i]), NULL, errp)) { + if (!qdev_realize(DEVICE(&a->cpu[i]), NULL, errp)) { return; } } /* SRAM */ - sram_name = g_strdup_printf("aspeed.sram.%d", CPU(&s->cpu[0])->cpu_index); + sram_name = g_strdup_printf("aspeed.sram.%d", CPU(&a->cpu[0])->cpu_index); memory_region_init_ram(&s->sram, OBJECT(s), sram_name, sc->sram_size, &err); if (err) { error_propagate(errp, err); @@ -288,14 +291,14 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->scu), 0, sc->memmap[ASPEED_DEV_SCU]); /* VIC */ - if (!sysbus_realize(SYS_BUS_DEVICE(&s->vic), errp)) { + if (!sysbus_realize(SYS_BUS_DEVICE(&a->vic), errp)) { return; } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->vic), 0, sc->memmap[ASPEED_DEV_VIC]); - sysbus_connect_irq(SYS_BUS_DEVICE(&s->vic), 0, - qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_IRQ)); - sysbus_connect_irq(SYS_BUS_DEVICE(&s->vic), 1, - qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_FIQ)); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->vic), 0, sc->memmap[ASPEED_DEV_VIC]); + sysbus_connect_irq(SYS_BUS_DEVICE(&a->vic), 0, + qdev_get_gpio_in(DEVICE(&a->cpu), ARM_CPU_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&a->vic), 1, + qdev_get_gpio_in(DEVICE(&a->cpu), ARM_CPU_FIQ)); /* RTC */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->rtc), errp)) { diff --git a/hw/arm/meson.build b/hw/arm/meson.build index 42e7aa36f3..68245d3ad1 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -48,9 +48,9 @@ arm_ss.add(when: 'CONFIG_FSL_IMX25', if_true: files('fsl-imx25.c', 'imx25_pdk.c' arm_ss.add(when: 'CONFIG_FSL_IMX31', if_true: files('fsl-imx31.c', 'kzm.c')) arm_ss.add(when: 'CONFIG_FSL_IMX6', if_true: files('fsl-imx6.c')) arm_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files( - 'aspeed_soc.c', 'aspeed.c', 'aspeed_soc_common.c', + 'aspeed_ast2400.c', 'aspeed_ast2600.c', 'aspeed_ast10x0.c', 'aspeed_eeprom.c', diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index 6f783138e1..cb832bc1ee 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -49,14 +49,12 @@ struct AspeedSoCState { DeviceState parent; - ARMCPU cpu[ASPEED_CPUS_NUM]; MemoryRegion *memory; MemoryRegion *dram_mr; MemoryRegion dram_container; MemoryRegion sram; MemoryRegion spi_boot_container; MemoryRegion spi_boot; - AspeedVICState vic; AspeedRtcState rtc; AspeedTimerCtrlState timerctrl; AspeedI2CState i2c; @@ -99,6 +97,9 @@ OBJECT_DECLARE_TYPE(AspeedSoCState, AspeedSoCClass, ASPEED_SOC) struct Aspeed2400SoCState { AspeedSoCState parent; + + ARMCPU cpu[ASPEED_CPUS_NUM]; + AspeedVICState vic; }; #define TYPE_ASPEED2400_SOC "aspeed2400-soc" From 930f1865cc654b637ffe1207fa5b44bf0a156279 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 15 Oct 2023 16:20:51 -0700 Subject: [PATCH 015/974] target/sparc: Clear may_lookup for npc == DYNAMIC_PC With pairs of jmp+rett, pc == DYNAMIC_PC_LOOKUP and npc == DYNAMIC_PC. Make sure that we exit for interrupts. Cc: qemu-stable@nongnu.org Fixes: 633c42834c7 ("target/sparc: Introduce DYNAMIC_PC_LOOKUP") Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/translate.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index f92ff80ac8..8fabed28fd 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -5654,10 +5654,10 @@ static void sparc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) break; } + may_lookup = true; if (dc->pc & 3) { switch (dc->pc) { case DYNAMIC_PC_LOOKUP: - may_lookup = true; break; case DYNAMIC_PC: may_lookup = false; @@ -5667,10 +5667,24 @@ static void sparc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } } else { tcg_gen_movi_tl(cpu_pc, dc->pc); - may_lookup = true; } - save_npc(dc); + if (dc->npc & 3) { + switch (dc->npc) { + case JUMP_PC: + gen_generic_branch(dc); + break; + case DYNAMIC_PC: + may_lookup = false; + break; + case DYNAMIC_PC_LOOKUP: + break; + default: + g_assert_not_reached(); + } + } else { + tcg_gen_movi_tl(cpu_npc, dc->npc); + } if (may_lookup) { tcg_gen_lookup_and_goto_ptr(); } else { From 186e78905a4fe69b62598af73903c6cf4df1f24f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 16 Oct 2023 13:23:15 -0700 Subject: [PATCH 016/974] target/sparc: Implement check_align inline Emit the exception at the end of the translation block, so that the non-exception case can fall through. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/helper.h | 1 - target/sparc/ldst_helper.c | 7 ++-- target/sparc/translate.c | 68 +++++++++++++++++++++++++++++++++----- 3 files changed, 61 insertions(+), 15 deletions(-) diff --git a/target/sparc/helper.h b/target/sparc/helper.h index b8f1e78c75..b116ddcb29 100644 --- a/target/sparc/helper.h +++ b/target/sparc/helper.h @@ -24,7 +24,6 @@ DEF_HELPER_FLAGS_2(tick_set_count, TCG_CALL_NO_RWG, void, ptr, i64) DEF_HELPER_FLAGS_3(tick_get_count, TCG_CALL_NO_WG, i64, env, ptr, int) DEF_HELPER_FLAGS_2(tick_set_limit, TCG_CALL_NO_RWG, void, ptr, i64) #endif -DEF_HELPER_FLAGS_3(check_align, TCG_CALL_NO_WG, void, env, tl, i32) DEF_HELPER_1(debug, void, env) DEF_HELPER_1(save, void, env) DEF_HELPER_1(restore, void, env) diff --git a/target/sparc/ldst_helper.c b/target/sparc/ldst_helper.c index 78b03308ae..246de86c98 100644 --- a/target/sparc/ldst_helper.c +++ b/target/sparc/ldst_helper.c @@ -360,6 +360,7 @@ static inline void do_check_asi(CPUSPARCState *env, int asi, uintptr_t ra) #endif /* !CONFIG_USER_ONLY */ #endif +#if defined(TARGET_SPARC64) || !defined(CONFIG_USER_ONLY) static void do_check_align(CPUSPARCState *env, target_ulong addr, uint32_t align, uintptr_t ra) { @@ -367,11 +368,7 @@ static void do_check_align(CPUSPARCState *env, target_ulong addr, cpu_raise_exception_ra(env, TT_UNALIGNED, ra); } } - -void helper_check_align(CPUSPARCState *env, target_ulong addr, uint32_t align) -{ - do_check_align(env, addr, align, GETPC()); -} +#endif #if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY) && \ defined(DEBUG_MXCC) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 8fabed28fd..8f6fd453e7 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -68,6 +68,15 @@ static TCGv cpu_wim; /* Floating point registers */ static TCGv_i64 cpu_fpr[TARGET_DPREGS]; +typedef struct DisasDelayException { + struct DisasDelayException *next; + TCGLabel *lab; + TCGv_i32 excp; + /* Saved state at parent insn. */ + target_ulong pc; + target_ulong npc; +} DisasDelayException; + typedef struct DisasContext { DisasContextBase base; target_ulong pc; /* current Program Counter: integer or DYNAMIC_PC */ @@ -89,6 +98,7 @@ typedef struct DisasContext { int fprs_dirty; int asi; #endif + DisasDelayException *delay_excp_list; } DisasContext; typedef struct { @@ -984,9 +994,38 @@ static void gen_exception(DisasContext *dc, int which) dc->base.is_jmp = DISAS_NORETURN; } -static void gen_check_align(TCGv addr, int mask) +static TCGLabel *delay_exceptionv(DisasContext *dc, TCGv_i32 excp) { - gen_helper_check_align(tcg_env, addr, tcg_constant_i32(mask)); + DisasDelayException *e = g_new0(DisasDelayException, 1); + + e->next = dc->delay_excp_list; + dc->delay_excp_list = e; + + e->lab = gen_new_label(); + e->excp = excp; + e->pc = dc->pc; + /* Caller must have used flush_cond before branch. */ + assert(e->npc != JUMP_PC); + e->npc = dc->npc; + + return e->lab; +} + +static TCGLabel *delay_exception(DisasContext *dc, int excp) +{ + return delay_exceptionv(dc, tcg_constant_i32(excp)); +} + +static void gen_check_align(DisasContext *dc, TCGv addr, int mask) +{ + TCGv t = tcg_temp_new(); + TCGLabel *lab; + + tcg_gen_andi_tl(t, addr, mask); + + flush_cond(dc); + lab = delay_exception(dc, TT_UNALIGNED); + tcg_gen_brcondi_tl(TCG_COND_NE, t, 0, lab); } static void gen_mov_pc_npc(DisasContext *dc) @@ -5019,9 +5058,9 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) tcg_gen_mov_tl(cpu_tmp0, cpu_src1); } } + gen_check_align(dc, cpu_tmp0, 3); gen_helper_restore(tcg_env); gen_mov_pc_npc(dc); - gen_check_align(cpu_tmp0, 3); tcg_gen_mov_tl(cpu_npc, cpu_tmp0); dc->npc = DYNAMIC_PC_LOOKUP; goto jmp_insn; @@ -5044,12 +5083,9 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) switch (xop) { case 0x38: /* jmpl */ { - TCGv t = gen_dest_gpr(dc, rd); - tcg_gen_movi_tl(t, dc->pc); - gen_store_gpr(dc, rd, t); - + gen_check_align(dc, cpu_tmp0, 3); + gen_store_gpr(dc, rd, tcg_constant_tl(dc->pc)); gen_mov_pc_npc(dc); - gen_check_align(cpu_tmp0, 3); gen_address_mask(dc, cpu_tmp0); tcg_gen_mov_tl(cpu_npc, cpu_tmp0); dc->npc = DYNAMIC_PC_LOOKUP; @@ -5060,8 +5096,8 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) { if (!supervisor(dc)) goto priv_insn; + gen_check_align(dc, cpu_tmp0, 3); gen_mov_pc_npc(dc); - gen_check_align(cpu_tmp0, 3); tcg_gen_mov_tl(cpu_npc, cpu_tmp0); dc->npc = DYNAMIC_PC; gen_helper_rett(tcg_env); @@ -5643,6 +5679,7 @@ static void sparc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) static void sparc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) { DisasContext *dc = container_of(dcbase, DisasContext, base); + DisasDelayException *e, *e_next; bool may_lookup; switch (dc->base.is_jmp) { @@ -5704,6 +5741,19 @@ static void sparc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) default: g_assert_not_reached(); } + + for (e = dc->delay_excp_list; e ; e = e_next) { + gen_set_label(e->lab); + + tcg_gen_movi_tl(cpu_pc, e->pc); + if (e->npc % 4 == 0) { + tcg_gen_movi_tl(cpu_npc, e->npc); + } + gen_helper_raise_exception(tcg_env, e->excp); + + e_next = e->next; + g_free(e); + } } static void sparc_tr_disas_log(const DisasContextBase *dcbase, From d9125cf27cbb2dcd457a95f336f8aaae87412525 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 16 Oct 2023 19:56:51 -0700 Subject: [PATCH 017/974] target/sparc: Avoid helper_raise_exception in helper_st_asi Always use cpu_raise_exception_ra with GETPC for unwind. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/ldst_helper.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/target/sparc/ldst_helper.c b/target/sparc/ldst_helper.c index 246de86c98..09066d5487 100644 --- a/target/sparc/ldst_helper.c +++ b/target/sparc/ldst_helper.c @@ -1650,7 +1650,7 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val, int idx = ((asi & 2) >> 1) | ((asi & 8) >> 2); env->dmmu.sun4v_tsb_pointers[idx] = val; } else { - helper_raise_exception(env, TT_ILL_INSN); + goto illegal_insn; } break; case 0x33: @@ -1662,7 +1662,7 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val, */ env->dmmu.sun4v_ctx_config[(asi & 8) >> 3] = val; } else { - helper_raise_exception(env, TT_ILL_INSN); + goto illegal_insn; } break; case 0x35: @@ -1679,7 +1679,7 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val, int idx = ((asi & 2) >> 1) | ((asi & 8) >> 2); env->immu.sun4v_tsb_pointers[idx] = val; } else { - helper_raise_exception(env, TT_ILL_INSN); + goto illegal_insn; } break; case 0x37: @@ -1691,7 +1691,7 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val, */ env->immu.sun4v_ctx_config[(asi & 8) >> 3] = val; } else { - helper_raise_exception(env, TT_ILL_INSN); + goto illegal_insn; } break; case ASI_UPA_CONFIG: /* UPA config */ @@ -1920,6 +1920,8 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val, default: sparc_raise_mmu_fault(cs, addr, true, false, 1, size, GETPC()); return; + illegal_insn: + cpu_raise_exception_ra(env, TT_ILL_INSN, GETPC()); } } #endif /* CONFIG_USER_ONLY */ From e0f46055a18e3d51ffe4c0a38e09c5263bd4f059 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 20 Jun 2023 18:36:33 +0200 Subject: [PATCH 018/974] target/sparc: Set TCG_GUEST_DEFAULT_MO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Always use TSO, per the Oracle 2015 manual. This is slightly less restrictive than the TCG_MO_ALL default, and happens to match the i386 model, which will eliminate a few extra barriers on that host. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/cpu.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index b3a98f1d74..9fc5c401d2 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -6,6 +6,29 @@ #include "exec/cpu-defs.h" #include "qemu/cpu-float.h" +/* + * From Oracle SPARC Architecture 2015: + * + * Compatibility notes: The PSO memory model described in SPARC V8 and + * SPARC V9 compatibility architecture specifications was never implemented + * in a SPARC V9 implementation and is not included in the Oracle SPARC + * Architecture specification. + * + * The RMO memory model described in the SPARC V9 specification was + * implemented in some non-Sun SPARC V9 implementations, but is not + * directly supported in Oracle SPARC Architecture 2015 implementations. + * + * Therefore always use TSO in QEMU. + * + * D.5 Specification of Partial Store Order (PSO) + * ... [loads] are followed by an implied MEMBAR #LoadLoad | #LoadStore. + * + * D.6 Specification of Total Store Order (TSO) + * ... PSO with the additional requirement that all [stores] are followed + * by an implied MEMBAR #StoreStore. + */ +#define TCG_GUEST_DEFAULT_MO (TCG_MO_LD_LD | TCG_MO_LD_ST | TCG_MO_ST_ST) + #if !defined(TARGET_SPARC64) #define TARGET_DPREGS 16 #else From ec860e5d039d4dcd40077fc6152e3cf8dcedd2ea Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 20 Jun 2023 18:38:10 +0200 Subject: [PATCH 019/974] configs: Enable MTTCG for sparc, sparc64 This will be of small comfort to sparc64, because both sun4u and sun4v board models force max_cpus = 1. But it does enable actual smp for sparc32 sun4m. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- configs/targets/sparc-softmmu.mak | 1 + configs/targets/sparc64-softmmu.mak | 1 + 2 files changed, 2 insertions(+) diff --git a/configs/targets/sparc-softmmu.mak b/configs/targets/sparc-softmmu.mak index 454eb35499..a5d9200382 100644 --- a/configs/targets/sparc-softmmu.mak +++ b/configs/targets/sparc-softmmu.mak @@ -1,2 +1,3 @@ TARGET_ARCH=sparc TARGET_BIG_ENDIAN=y +TARGET_SUPPORTS_MTTCG=y diff --git a/configs/targets/sparc64-softmmu.mak b/configs/targets/sparc64-softmmu.mak index d3f8a3b710..36ca64ec41 100644 --- a/configs/targets/sparc64-softmmu.mak +++ b/configs/targets/sparc64-softmmu.mak @@ -1,3 +1,4 @@ TARGET_ARCH=sparc64 TARGET_BASE_ARCH=sparc TARGET_BIG_ENDIAN=y +TARGET_SUPPORTS_MTTCG=y From bd7ff659a76ff541c2674e4a48e06e3b047776d0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 15 Oct 2023 17:02:33 -0700 Subject: [PATCH 020/974] target/sparc: Define features via cpu-feature.h.inc Manage feature bits automatically. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/cpu-feature.h.inc | 19 +++++++++++++++++++ target/sparc/cpu.h | 32 +++++++++++++------------------- 2 files changed, 32 insertions(+), 19 deletions(-) create mode 100644 target/sparc/cpu-feature.h.inc diff --git a/target/sparc/cpu-feature.h.inc b/target/sparc/cpu-feature.h.inc new file mode 100644 index 0000000000..d35fe90c92 --- /dev/null +++ b/target/sparc/cpu-feature.h.inc @@ -0,0 +1,19 @@ +FEATURE(FLOAT) +FEATURE(FLOAT128) +FEATURE(SWAP) +FEATURE(MUL) +FEATURE(DIV) +FEATURE(FLUSH) +FEATURE(FSQRT) +FEATURE(FMUL) +FEATURE(VIS1) +FEATURE(VIS2) +FEATURE(FSMULD) +FEATURE(HYPV) +FEATURE(CMT) +FEATURE(GL) +FEATURE(TA0_SHUTDOWN) /* Shutdown on "ta 0x0" */ +FEATURE(ASR17) +FEATURE(CACHE_CTRL) +FEATURE(POWERDOWN) +FEATURE(CASA) diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index 9fc5c401d2..aaecbf0876 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -291,25 +291,19 @@ struct sparc_def_t { uint32_t maxtl; }; -#define CPU_FEATURE_FLOAT (1 << 0) -#define CPU_FEATURE_FLOAT128 (1 << 1) -#define CPU_FEATURE_SWAP (1 << 2) -#define CPU_FEATURE_MUL (1 << 3) -#define CPU_FEATURE_DIV (1 << 4) -#define CPU_FEATURE_FLUSH (1 << 5) -#define CPU_FEATURE_FSQRT (1 << 6) -#define CPU_FEATURE_FMUL (1 << 7) -#define CPU_FEATURE_VIS1 (1 << 8) -#define CPU_FEATURE_VIS2 (1 << 9) -#define CPU_FEATURE_FSMULD (1 << 10) -#define CPU_FEATURE_HYPV (1 << 11) -#define CPU_FEATURE_CMT (1 << 12) -#define CPU_FEATURE_GL (1 << 13) -#define CPU_FEATURE_TA0_SHUTDOWN (1 << 14) /* Shutdown on "ta 0x0" */ -#define CPU_FEATURE_ASR17 (1 << 15) -#define CPU_FEATURE_CACHE_CTRL (1 << 16) -#define CPU_FEATURE_POWERDOWN (1 << 17) -#define CPU_FEATURE_CASA (1 << 18) +#define FEATURE(X) CPU_FEATURE_BIT_##X, +enum { +#include "cpu-feature.h.inc" +}; + +#undef FEATURE +#define FEATURE(X) CPU_FEATURE_##X = 1u << CPU_FEATURE_BIT_##X, + +enum { +#include "cpu-feature.h.inc" +}; + +#undef FEATURE #ifndef TARGET_SPARC64 #define CPU_DEFAULT_FEATURES (CPU_FEATURE_FLOAT | CPU_FEATURE_SWAP | \ From de1f52032fac58b23b85dc24b95df5fed5b10b1c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 15 Oct 2023 17:08:53 -0700 Subject: [PATCH 021/974] target/sparc: Use CPU_FEATURE_BIT_* for cpu properties Use symbols not integer constants for the bit positions. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/cpu.c | 72 ++++++++++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 28 deletions(-) diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index 8ba96ae225..330b7bead3 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -545,21 +545,22 @@ static const sparc_def_t sparc_defs[] = { #endif }; +/* This must match sparc_cpu_properties[]. */ static const char * const feature_name[] = { - "float", - "float128", - "swap", - "mul", - "div", - "flush", - "fsqrt", - "fmul", - "vis1", - "vis2", - "fsmuld", - "hypv", - "cmt", - "gl", + [CPU_FEATURE_BIT_FLOAT] = "float", + [CPU_FEATURE_BIT_FLOAT128] = "float128", + [CPU_FEATURE_BIT_SWAP] = "swap", + [CPU_FEATURE_BIT_MUL] = "mul", + [CPU_FEATURE_BIT_DIV] = "div", + [CPU_FEATURE_BIT_FLUSH] = "flush", + [CPU_FEATURE_BIT_FSQRT] = "fsqrt", + [CPU_FEATURE_BIT_FMUL] = "fmul", + [CPU_FEATURE_BIT_VIS1] = "vis1", + [CPU_FEATURE_BIT_VIS2] = "vis2", + [CPU_FEATURE_BIT_FSMULD] = "fsmuld", + [CPU_FEATURE_BIT_HYPV] = "hypv", + [CPU_FEATURE_BIT_CMT] = "cmt", + [CPU_FEATURE_BIT_GL] = "gl", }; static void print_features(uint32_t features, const char *prefix) @@ -835,21 +836,36 @@ static PropertyInfo qdev_prop_nwindows = { .set = sparc_set_nwindows, }; +/* This must match feature_name[]. */ static Property sparc_cpu_properties[] = { - DEFINE_PROP_BIT("float", SPARCCPU, env.def.features, 0, false), - DEFINE_PROP_BIT("float128", SPARCCPU, env.def.features, 1, false), - DEFINE_PROP_BIT("swap", SPARCCPU, env.def.features, 2, false), - DEFINE_PROP_BIT("mul", SPARCCPU, env.def.features, 3, false), - DEFINE_PROP_BIT("div", SPARCCPU, env.def.features, 4, false), - DEFINE_PROP_BIT("flush", SPARCCPU, env.def.features, 5, false), - DEFINE_PROP_BIT("fsqrt", SPARCCPU, env.def.features, 6, false), - DEFINE_PROP_BIT("fmul", SPARCCPU, env.def.features, 7, false), - DEFINE_PROP_BIT("vis1", SPARCCPU, env.def.features, 8, false), - DEFINE_PROP_BIT("vis2", SPARCCPU, env.def.features, 9, false), - DEFINE_PROP_BIT("fsmuld", SPARCCPU, env.def.features, 10, false), - DEFINE_PROP_BIT("hypv", SPARCCPU, env.def.features, 11, false), - DEFINE_PROP_BIT("cmt", SPARCCPU, env.def.features, 12, false), - DEFINE_PROP_BIT("gl", SPARCCPU, env.def.features, 13, false), + DEFINE_PROP_BIT("float", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_FLOAT, false), + DEFINE_PROP_BIT("float128", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_FLOAT128, false), + DEFINE_PROP_BIT("swap", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_SWAP, false), + DEFINE_PROP_BIT("mul", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_MUL, false), + DEFINE_PROP_BIT("div", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_DIV, false), + DEFINE_PROP_BIT("flush", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_FLUSH, false), + DEFINE_PROP_BIT("fsqrt", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_FSQRT, false), + DEFINE_PROP_BIT("fmul", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_FMUL, false), + DEFINE_PROP_BIT("vis1", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_VIS1, false), + DEFINE_PROP_BIT("vis2", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_VIS2, false), + DEFINE_PROP_BIT("fsmuld", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_FSMULD, false), + DEFINE_PROP_BIT("hypv", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_HYPV, false), + DEFINE_PROP_BIT("cmt", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_CMT, false), + DEFINE_PROP_BIT("gl", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_GL, false), DEFINE_PROP_UNSIGNED("iu-version", SPARCCPU, env.def.iu_version, 0, qdev_prop_uint64, target_ulong), DEFINE_PROP_UINT32("fpu-version", SPARCCPU, env.def.fpu_version, 0), From 5f25b383a8b76055607cabeb287a0b0e903da50c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Oct 2023 20:34:14 -0700 Subject: [PATCH 022/974] target/sparc: Remove sparcv7 cpu features The oldest supported cpu is the microsparc 1; all other cpus use CPU_DEFAULT_FEATURES. Remove the features that must always be present for sparcv7: FLOAT, SWAP, FLUSH, FSQRT, FMUL. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- linux-user/sparc/target_syscall.h | 6 +----- target/sparc/cpu-feature.h.inc | 5 ----- target/sparc/cpu.c | 24 +++--------------------- target/sparc/cpu.h | 21 +++++++-------------- target/sparc/translate.c | 12 ------------ 5 files changed, 11 insertions(+), 57 deletions(-) diff --git a/linux-user/sparc/target_syscall.h b/linux-user/sparc/target_syscall.h index be77e44eb8..e421165357 100644 --- a/linux-user/sparc/target_syscall.h +++ b/linux-user/sparc/target_syscall.h @@ -50,11 +50,7 @@ static inline abi_ulong target_shmlba(CPUSPARCState *env) #ifdef TARGET_SPARC64 return MAX(TARGET_PAGE_SIZE, 16 * 1024); #else - if (!(env->def.features & CPU_FEATURE_FLUSH)) { - return 64 * 1024; - } else { - return 256 * 1024; - } + return 256 * 1024; #endif } diff --git a/target/sparc/cpu-feature.h.inc b/target/sparc/cpu-feature.h.inc index d35fe90c92..d800f18c4e 100644 --- a/target/sparc/cpu-feature.h.inc +++ b/target/sparc/cpu-feature.h.inc @@ -1,11 +1,6 @@ -FEATURE(FLOAT) FEATURE(FLOAT128) -FEATURE(SWAP) FEATURE(MUL) FEATURE(DIV) -FEATURE(FLUSH) -FEATURE(FSQRT) -FEATURE(FMUL) FEATURE(VIS1) FEATURE(VIS2) FEATURE(FSMULD) diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index 330b7bead3..f527244aa4 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -403,9 +403,7 @@ static const sparc_def_t sparc_defs[] = { .mmu_sfsr_mask = 0x00016fff, .mmu_trcr_mask = 0x0000003f, .nwindows = 7, - .features = CPU_FEATURE_FLOAT | CPU_FEATURE_SWAP | CPU_FEATURE_MUL | - CPU_FEATURE_DIV | CPU_FEATURE_FLUSH | CPU_FEATURE_FSQRT | - CPU_FEATURE_FMUL, + .features = CPU_FEATURE_MUL | CPU_FEATURE_DIV, }, { .name = "TI MicroSparc II", @@ -547,14 +545,9 @@ static const sparc_def_t sparc_defs[] = { /* This must match sparc_cpu_properties[]. */ static const char * const feature_name[] = { - [CPU_FEATURE_BIT_FLOAT] = "float", [CPU_FEATURE_BIT_FLOAT128] = "float128", - [CPU_FEATURE_BIT_SWAP] = "swap", [CPU_FEATURE_BIT_MUL] = "mul", [CPU_FEATURE_BIT_DIV] = "div", - [CPU_FEATURE_BIT_FLUSH] = "flush", - [CPU_FEATURE_BIT_FSQRT] = "fsqrt", - [CPU_FEATURE_BIT_FMUL] = "fmul", [CPU_FEATURE_BIT_VIS1] = "vis1", [CPU_FEATURE_BIT_VIS2] = "vis2", [CPU_FEATURE_BIT_FSMULD] = "fsmuld", @@ -758,9 +751,8 @@ static void sparc_cpu_realizefn(DeviceState *dev, Error **errp) CPUSPARCState *env = &cpu->env; #if defined(CONFIG_USER_ONLY) - if ((env->def.features & CPU_FEATURE_FLOAT)) { - env->def.features |= CPU_FEATURE_FLOAT128; - } + /* We are emulating the kernel, which will trap and emulate float128. */ + env->def.features |= CPU_FEATURE_FLOAT128; #endif env->version = env->def.iu_version; @@ -838,22 +830,12 @@ static PropertyInfo qdev_prop_nwindows = { /* This must match feature_name[]. */ static Property sparc_cpu_properties[] = { - DEFINE_PROP_BIT("float", SPARCCPU, env.def.features, - CPU_FEATURE_BIT_FLOAT, false), DEFINE_PROP_BIT("float128", SPARCCPU, env.def.features, CPU_FEATURE_BIT_FLOAT128, false), - DEFINE_PROP_BIT("swap", SPARCCPU, env.def.features, - CPU_FEATURE_BIT_SWAP, false), DEFINE_PROP_BIT("mul", SPARCCPU, env.def.features, CPU_FEATURE_BIT_MUL, false), DEFINE_PROP_BIT("div", SPARCCPU, env.def.features, CPU_FEATURE_BIT_DIV, false), - DEFINE_PROP_BIT("flush", SPARCCPU, env.def.features, - CPU_FEATURE_BIT_FLUSH, false), - DEFINE_PROP_BIT("fsqrt", SPARCCPU, env.def.features, - CPU_FEATURE_BIT_FSQRT, false), - DEFINE_PROP_BIT("fmul", SPARCCPU, env.def.features, - CPU_FEATURE_BIT_FMUL, false), DEFINE_PROP_BIT("vis1", SPARCCPU, env.def.features, CPU_FEATURE_BIT_VIS1, false), DEFINE_PROP_BIT("vis2", SPARCCPU, env.def.features, diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index aaecbf0876..758a4e8aaa 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -306,17 +306,12 @@ enum { #undef FEATURE #ifndef TARGET_SPARC64 -#define CPU_DEFAULT_FEATURES (CPU_FEATURE_FLOAT | CPU_FEATURE_SWAP | \ - CPU_FEATURE_MUL | CPU_FEATURE_DIV | \ - CPU_FEATURE_FLUSH | CPU_FEATURE_FSQRT | \ - CPU_FEATURE_FMUL | CPU_FEATURE_FSMULD) +#define CPU_DEFAULT_FEATURES (CPU_FEATURE_MUL | CPU_FEATURE_DIV | \ + CPU_FEATURE_FSMULD) #else -#define CPU_DEFAULT_FEATURES (CPU_FEATURE_FLOAT | CPU_FEATURE_SWAP | \ - CPU_FEATURE_MUL | CPU_FEATURE_DIV | \ - CPU_FEATURE_FLUSH | CPU_FEATURE_FSQRT | \ - CPU_FEATURE_FMUL | CPU_FEATURE_VIS1 | \ - CPU_FEATURE_VIS2 | CPU_FEATURE_FSMULD | \ - CPU_FEATURE_CASA) +#define CPU_DEFAULT_FEATURES (CPU_FEATURE_MUL | CPU_FEATURE_DIV | \ + CPU_FEATURE_FSMULD | CPU_FEATURE_CASA | \ + CPU_FEATURE_VIS1 | CPU_FEATURE_VIS2) enum { mmu_us_12, // Ultrasparc < III (64 entry TLB) mmu_us_3, // Ultrasparc III (512 entry TLB) @@ -799,14 +794,12 @@ static inline void cpu_get_tb_cpu_state(CPUSPARCState *env, vaddr *pc, if (env->pstate & PS_AM) { flags |= TB_FLAG_AM_ENABLED; } - if ((env->def.features & CPU_FEATURE_FLOAT) - && (env->pstate & PS_PEF) - && (env->fprs & FPRS_FEF)) { + if ((env->pstate & PS_PEF) && (env->fprs & FPRS_FEF)) { flags |= TB_FLAG_FPU_ENABLED; } flags |= env->asi << TB_FLAG_ASI_SHIFT; #else - if ((env->def.features & CPU_FEATURE_FLOAT) && env->psref) { + if (env->psref) { flags |= TB_FLAG_FPU_ENABLED; } #endif diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 8f6fd453e7..cab9f13421 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -3527,11 +3527,9 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) gen_ne_fop_FF(dc, rd, rs2, gen_helper_fabss); break; case 0x29: /* fsqrts */ - CHECK_FPU_FEATURE(dc, FSQRT); gen_fop_FF(dc, rd, rs2, gen_helper_fsqrts); break; case 0x2a: /* fsqrtd */ - CHECK_FPU_FEATURE(dc, FSQRT); gen_fop_DD(dc, rd, rs2, gen_helper_fsqrtd); break; case 0x2b: /* fsqrtq */ @@ -3559,16 +3557,13 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) gen_fop_QQQ(dc, rd, rs1, rs2, gen_helper_fsubq); break; case 0x49: /* fmuls */ - CHECK_FPU_FEATURE(dc, FMUL); gen_fop_FFF(dc, rd, rs1, rs2, gen_helper_fmuls); break; case 0x4a: /* fmuld */ - CHECK_FPU_FEATURE(dc, FMUL); gen_fop_DDD(dc, rd, rs1, rs2, gen_helper_fmuld); break; case 0x4b: /* fmulq */ CHECK_FPU_FEATURE(dc, FLOAT128); - CHECK_FPU_FEATURE(dc, FMUL); gen_fop_QQQ(dc, rd, rs1, rs2, gen_helper_fmulq); break; case 0x4d: /* fdivs */ @@ -5105,8 +5100,6 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) goto jmp_insn; #endif case 0x3b: /* flush */ - if (!((dc)->def->features & CPU_FEATURE_FLUSH)) - goto unimp_flush; /* nop */ break; case 0x3c: /* save */ @@ -5224,7 +5217,6 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) break; case 0x0f: /* swap, swap register with memory. Also atomically */ - CHECK_IU_FEATURE(dc, SWAP); cpu_src1 = gen_load_gpr(dc, rd); gen_swap(dc, cpu_val, cpu_src1, cpu_addr, dc->mem_idx, MO_TEUL); @@ -5256,7 +5248,6 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) break; case 0x1f: /* swapa, swap reg with alt. memory. Also atomically */ - CHECK_IU_FEATURE(dc, SWAP); cpu_src1 = gen_load_gpr(dc, rd); gen_swap_asi(dc, cpu_val, cpu_src1, cpu_addr, insn); break; @@ -5578,9 +5569,6 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) illegal_insn: gen_exception(dc, TT_ILL_INSN); return; - unimp_flush: - gen_exception(dc, TT_UNIMP_FLUSH); - return; #if !defined(CONFIG_USER_ONLY) priv_insn: gen_exception(dc, TT_PRIV_INSN); From 554abe47c7b4b68c716edb68b8843f64490d7e55 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 22 Oct 2023 16:05:15 -0700 Subject: [PATCH 023/974] target/sparc: Partition cpu features In the sparc32 binaries, do not advertise features only available to sparc64, so they cannot be enabled. In the sparc64 binaries, do not advertise features mandatory in v9, so they cannot be disabled. Signed-off-by: Richard Henderson --- target/sparc/cpu.c | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index f527244aa4..bb1a155510 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -546,14 +546,17 @@ static const sparc_def_t sparc_defs[] = { /* This must match sparc_cpu_properties[]. */ static const char * const feature_name[] = { [CPU_FEATURE_BIT_FLOAT128] = "float128", - [CPU_FEATURE_BIT_MUL] = "mul", - [CPU_FEATURE_BIT_DIV] = "div", - [CPU_FEATURE_BIT_VIS1] = "vis1", - [CPU_FEATURE_BIT_VIS2] = "vis2", - [CPU_FEATURE_BIT_FSMULD] = "fsmuld", - [CPU_FEATURE_BIT_HYPV] = "hypv", +#ifdef TARGET_SPARC64 [CPU_FEATURE_BIT_CMT] = "cmt", [CPU_FEATURE_BIT_GL] = "gl", + [CPU_FEATURE_BIT_HYPV] = "hypv", + [CPU_FEATURE_BIT_VIS1] = "vis1", + [CPU_FEATURE_BIT_VIS2] = "vis2", +#else + [CPU_FEATURE_BIT_MUL] = "mul", + [CPU_FEATURE_BIT_DIV] = "div", + [CPU_FEATURE_BIT_FSMULD] = "fsmuld", +#endif }; static void print_features(uint32_t features, const char *prefix) @@ -832,22 +835,25 @@ static PropertyInfo qdev_prop_nwindows = { static Property sparc_cpu_properties[] = { DEFINE_PROP_BIT("float128", SPARCCPU, env.def.features, CPU_FEATURE_BIT_FLOAT128, false), - DEFINE_PROP_BIT("mul", SPARCCPU, env.def.features, - CPU_FEATURE_BIT_MUL, false), - DEFINE_PROP_BIT("div", SPARCCPU, env.def.features, - CPU_FEATURE_BIT_DIV, false), - DEFINE_PROP_BIT("vis1", SPARCCPU, env.def.features, - CPU_FEATURE_BIT_VIS1, false), - DEFINE_PROP_BIT("vis2", SPARCCPU, env.def.features, - CPU_FEATURE_BIT_VIS2, false), - DEFINE_PROP_BIT("fsmuld", SPARCCPU, env.def.features, - CPU_FEATURE_BIT_FSMULD, false), - DEFINE_PROP_BIT("hypv", SPARCCPU, env.def.features, - CPU_FEATURE_BIT_HYPV, false), +#ifdef TARGET_SPARC64 DEFINE_PROP_BIT("cmt", SPARCCPU, env.def.features, CPU_FEATURE_BIT_CMT, false), DEFINE_PROP_BIT("gl", SPARCCPU, env.def.features, CPU_FEATURE_BIT_GL, false), + DEFINE_PROP_BIT("hypv", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_HYPV, false), + DEFINE_PROP_BIT("vis1", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_VIS1, false), + DEFINE_PROP_BIT("vis2", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_VIS2, false), +#else + DEFINE_PROP_BIT("mul", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_MUL, false), + DEFINE_PROP_BIT("div", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_DIV, false), + DEFINE_PROP_BIT("fsmuld", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_FSMULD, false), +#endif DEFINE_PROP_UNSIGNED("iu-version", SPARCCPU, env.def.iu_version, 0, qdev_prop_uint64, target_ulong), DEFINE_PROP_UINT32("fpu-version", SPARCCPU, env.def.fpu_version, 0), From 878cc6773a69f9018357ff673f258acef58422b3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 1 Oct 2023 14:56:04 -0700 Subject: [PATCH 024/974] target/sparc: Add decodetree infrastructure Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 5 +++ target/sparc/meson.build | 3 ++ target/sparc/translate.c | 69 ++++++++++++++++++++++++++------------- 3 files changed, 55 insertions(+), 22 deletions(-) create mode 100644 target/sparc/insns.decode diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode new file mode 100644 index 0000000000..5811a679db --- /dev/null +++ b/target/sparc/insns.decode @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: LGPL-2.0+ +# +# Sparc instruction decode definitions. +# Copyright (c) 2023 Richard Henderson + diff --git a/target/sparc/meson.build b/target/sparc/meson.build index 48025cce76..c316773db6 100644 --- a/target/sparc/meson.build +++ b/target/sparc/meson.build @@ -1,4 +1,7 @@ +gen = decodetree.process('insns.decode') + sparc_ss = ss.source_set() +sparc_ss.add(gen) sparc_ss.add(files( 'cc_helper.c', 'cpu.c', diff --git a/target/sparc/translate.c b/target/sparc/translate.c index cab9f13421..080bc5f8a2 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -3003,6 +3003,47 @@ static void gen_faligndata(TCGv dst, TCGv gsr, TCGv s1, TCGv s2) } #endif +/* Include the auto-generated decoder. */ +#include "decode-insns.c.inc" + +#define TRANS(NAME, AVAIL, FUNC, ...) \ + static bool trans_##NAME(DisasContext *dc, arg_##NAME *a) \ + { return avail_##AVAIL(dc) && FUNC(dc, __VA_ARGS__); } + +#define avail_ALL(C) true +#ifdef TARGET_SPARC64 +# define avail_32(C) false +# define avail_64(C) true +#else +# define avail_32(C) true +# define avail_64(C) false +#endif + +/* Default case for non jump instructions. */ +static bool advance_pc(DisasContext *dc) +{ + if (dc->npc & 3) { + switch (dc->npc) { + case DYNAMIC_PC: + case DYNAMIC_PC_LOOKUP: + dc->pc = dc->npc; + gen_op_next_insn(); + break; + case JUMP_PC: + /* we can do a static jump */ + gen_branch2(dc, dc->jump_pc[0], dc->jump_pc[1], cpu_cond); + dc->base.is_jmp = DISAS_NORETURN; + break; + default: + g_assert_not_reached(); + } + } else { + dc->pc = dc->npc; + dc->npc = dc->npc + 4; + } + return true; +} + #define CHECK_IU_FEATURE(dc, FEATURE) \ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ goto illegal_insn; @@ -3011,7 +3052,7 @@ static void gen_faligndata(TCGv dst, TCGv gsr, TCGv s1, TCGv s2) goto nfpu_insn; /* before an instruction, dc->pc must be static */ -static void disas_sparc_insn(DisasContext * dc, unsigned int insn) +static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) { unsigned int opc, rs1, rs2, rd; TCGv cpu_src1, cpu_src2; @@ -5544,26 +5585,7 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) } break; } - /* default case for non jump instructions */ - if (dc->npc & 3) { - switch (dc->npc) { - case DYNAMIC_PC: - case DYNAMIC_PC_LOOKUP: - dc->pc = dc->npc; - gen_op_next_insn(); - break; - case JUMP_PC: - /* we can do a static jump */ - gen_branch2(dc, dc->jump_pc[0], dc->jump_pc[1], cpu_cond); - dc->base.is_jmp = DISAS_NORETURN; - break; - default: - g_assert_not_reached(); - } - } else { - dc->pc = dc->npc; - dc->npc = dc->npc + 4; - } + advance_pc(dc); jmp_insn: return; illegal_insn: @@ -5654,7 +5676,10 @@ static void sparc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) insn = translator_ldl(env, &dc->base, dc->pc); dc->base.pc_next += 4; - disas_sparc_insn(dc, insn); + + if (!decode(dc, insn)) { + disas_sparc_legacy(dc, insn); + } if (dc->base.is_jmp == DISAS_NORETURN) { return; From b1bc09ea6bce116c679bbffa66edcb7520d57424 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 1 Oct 2023 15:10:31 -0700 Subject: [PATCH 025/974] target/sparc: Define AM_CHECK for sparc32 Define as false, which allows some ifdef removal. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/translate.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 080bc5f8a2..9eb2b7e52f 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -268,20 +268,21 @@ static void gen_move_Q(DisasContext *dc, unsigned int rd, unsigned int rs) #endif #endif -#ifdef TARGET_SPARC64 -#ifndef TARGET_ABI32 -#define AM_CHECK(dc) ((dc)->address_mask_32bit) +#if !defined(TARGET_SPARC64) +# define AM_CHECK(dc) false +#elif defined(TARGET_ABI32) +# define AM_CHECK(dc) true +#elif defined(CONFIG_USER_ONLY) +# define AM_CHECK(dc) false #else -#define AM_CHECK(dc) (1) -#endif +# define AM_CHECK(dc) ((dc)->address_mask_32bit) #endif static void gen_address_mask(DisasContext *dc, TCGv addr) { -#ifdef TARGET_SPARC64 - if (AM_CHECK(dc)) + if (AM_CHECK(dc)) { tcg_gen_andi_tl(addr, addr, 0xffffffffULL); -#endif + } } static TCGv gen_load_gpr(DisasContext *dc, int reg) @@ -1366,11 +1367,9 @@ static void do_branch(DisasContext *dc, int32_t offset, uint32_t insn, int cc) unsigned int cond = GET_FIELD(insn, 3, 6), a = (insn & (1 << 29)); target_ulong target = dc->pc + offset; -#ifdef TARGET_SPARC64 if (unlikely(AM_CHECK(dc))) { target &= 0xffffffffULL; } -#endif if (cond == 0x0) { /* unconditional not taken */ if (a) { @@ -1406,11 +1405,9 @@ static void do_fbranch(DisasContext *dc, int32_t offset, uint32_t insn, int cc) unsigned int cond = GET_FIELD(insn, 3, 6), a = (insn & (1 << 29)); target_ulong target = dc->pc + offset; -#ifdef TARGET_SPARC64 if (unlikely(AM_CHECK(dc))) { target &= 0xffffffffULL; } -#endif if (cond == 0x0) { /* unconditional not taken */ if (a) { From 23ada1b16fb22eb5121f7de1bfac342a2b4f72a5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 1 Oct 2023 15:11:00 -0700 Subject: [PATCH 026/974] target/sparc: Move CALL to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 1 + target/sparc/translate.c | 34 +++++++++++++++++----------------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 5811a679db..a5f5d2681e 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -3,3 +3,4 @@ # Sparc instruction decode definitions. # Copyright (c) 2023 Richard Henderson +CALL 01 i:s30 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 9eb2b7e52f..7ef4c6d4f7 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -285,6 +285,11 @@ static void gen_address_mask(DisasContext *dc, TCGv addr) } } +static target_ulong address_mask_i(DisasContext *dc, target_ulong addr) +{ + return AM_CHECK(dc) ? (uint32_t)addr : addr; +} + static TCGv gen_load_gpr(DisasContext *dc, int reg) { if (reg > 0) { @@ -3041,6 +3046,16 @@ static bool advance_pc(DisasContext *dc) return true; } +static bool trans_CALL(DisasContext *dc, arg_CALL *a) +{ + target_long target = address_mask_i(dc, dc->pc + a->i * 4); + + gen_store_gpr(dc, 15, tcg_constant_tl(dc->pc)); + gen_mov_pc_npc(dc); + dc->npc = target; + return true; +} + #define CHECK_IU_FEATURE(dc, FEATURE) \ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ goto illegal_insn; @@ -3146,23 +3161,8 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) break; } break; - case 1: /*CALL*/ - { - target_long target = GET_FIELDs(insn, 2, 31) << 2; - TCGv o7 = gen_dest_gpr(dc, 15); - - tcg_gen_movi_tl(o7, dc->pc); - gen_store_gpr(dc, 15, o7); - target += dc->pc; - gen_mov_pc_npc(dc); -#ifdef TARGET_SPARC64 - if (unlikely(AM_CHECK(dc))) { - target &= 0xffffffffULL; - } -#endif - dc->npc = target; - } - goto jmp_insn; + case 1: + g_assert_not_reached(); /* in decodetree */ case 2: /* FPU & Logical Operations */ { unsigned int xop = GET_FIELD(insn, 7, 12); From 276567aaf6fc59a8029652d81298c309289d7c20 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 1 Oct 2023 15:57:34 -0700 Subject: [PATCH 027/974] target/sparc: Move BPcc and Bicc to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 4 ++ target/sparc/translate.c | 117 +++++++++++++++++++------------------- 2 files changed, 61 insertions(+), 60 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index a5f5d2681e..15cd975f4e 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -3,4 +3,8 @@ # Sparc instruction decode definitions. # Copyright (c) 2023 Richard Henderson +&bcc i a cond cc +BPcc 00 a:1 cond:4 001 cc:1 0 - i:s19 &bcc +Bicc 00 a:1 cond:4 010 i:s22 &bcc cc=0 + CALL 01 i:s30 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 7ef4c6d4f7..92ea6bab6b 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -1367,44 +1367,6 @@ static void gen_cond_reg(TCGv r_dst, int cond, TCGv r_src) } #endif -static void do_branch(DisasContext *dc, int32_t offset, uint32_t insn, int cc) -{ - unsigned int cond = GET_FIELD(insn, 3, 6), a = (insn & (1 << 29)); - target_ulong target = dc->pc + offset; - - if (unlikely(AM_CHECK(dc))) { - target &= 0xffffffffULL; - } - if (cond == 0x0) { - /* unconditional not taken */ - if (a) { - dc->pc = dc->npc + 4; - dc->npc = dc->pc + 4; - } else { - dc->pc = dc->npc; - dc->npc = dc->pc + 4; - } - } else if (cond == 0x8) { - /* unconditional taken */ - if (a) { - dc->pc = target; - dc->npc = dc->pc + 4; - } else { - dc->pc = dc->npc; - dc->npc = target; - tcg_gen_mov_tl(cpu_pc, cpu_npc); - } - } else { - flush_cond(dc); - gen_cond(cpu_cond, cc, cond, dc); - if (a) { - gen_branch_a(dc, target); - } else { - gen_branch_n(dc, target); - } - } -} - static void do_fbranch(DisasContext *dc, int32_t offset, uint32_t insn, int cc) { unsigned int cond = GET_FIELD(insn, 3, 6), a = (insn & (1 << 29)); @@ -3046,6 +3008,61 @@ static bool advance_pc(DisasContext *dc) return true; } +static bool advance_jump_uncond_never(DisasContext *dc, bool annul) +{ + if (annul) { + dc->pc = dc->npc + 4; + dc->npc = dc->pc + 4; + } else { + dc->pc = dc->npc; + dc->npc = dc->pc + 4; + } + return true; +} + +static bool advance_jump_uncond_always(DisasContext *dc, bool annul, + target_ulong dest) +{ + if (annul) { + dc->pc = dest; + dc->npc = dest + 4; + } else { + dc->pc = dc->npc; + dc->npc = dest; + tcg_gen_mov_tl(cpu_pc, cpu_npc); + } + return true; +} + +static bool advance_jump_cond(DisasContext *dc, bool annul, target_ulong dest) +{ + if (annul) { + gen_branch_a(dc, dest); + } else { + gen_branch_n(dc, dest); + } + return true; +} + +static bool do_bpcc(DisasContext *dc, arg_bcc *a) +{ + target_long target = address_mask_i(dc, dc->pc + a->i * 4); + + switch (a->cond) { + case 0x0: + return advance_jump_uncond_never(dc, a->a); + case 0x8: + return advance_jump_uncond_always(dc, a->a, target); + default: + flush_cond(dc); + gen_cond(cpu_cond, a->cc, a->cond, dc); + return advance_jump_cond(dc, a->a, target); + } +} + +TRANS(Bicc, ALL, do_bpcc, a) +TRANS(BPcc, 64, do_bpcc, a) + static bool trans_CALL(DisasContext *dc, arg_CALL *a) { target_long target = address_mask_i(dc, dc->pc + a->i * 4); @@ -3083,21 +3100,7 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) switch (xop) { #ifdef TARGET_SPARC64 case 0x1: /* V9 BPcc */ - { - int cc; - - target = GET_FIELD_SP(insn, 0, 18); - target = sign_extend(target, 19); - target <<= 2; - cc = GET_FIELD_SP(insn, 20, 21); - if (cc == 0) - do_branch(dc, target, insn, 0); - else if (cc == 2) - do_branch(dc, target, insn, 1); - else - goto illegal_insn; - goto jmp_insn; - } + g_assert_not_reached(); /* in decodetree */ case 0x3: /* V9 BPr */ { target = GET_FIELD_SP(insn, 0, 13) | @@ -3127,13 +3130,7 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) } #endif case 0x2: /* BN+x */ - { - target = GET_FIELD(insn, 10, 31); - target = sign_extend(target, 22); - target <<= 2; - do_branch(dc, target, insn, 0); - goto jmp_insn; - } + g_assert_not_reached(); /* in decodetree */ case 0x6: /* FBN+x */ { if (gen_trap_ifnofpu(dc)) { From ab9ffe988a10dcc05e6e609dd721318b7a70bf23 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 1 Oct 2023 16:23:14 -0700 Subject: [PATCH 028/974] target/sparc: Move BPr to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 3 ++ target/sparc/translate.c | 63 ++++++++++++++------------------------- 2 files changed, 25 insertions(+), 41 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 15cd975f4e..838f4cdb1d 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -7,4 +7,7 @@ BPcc 00 a:1 cond:4 001 cc:1 0 - i:s19 &bcc Bicc 00 a:1 cond:4 010 i:s22 &bcc cc=0 +%d16 20:s2 0:14 +BPr 00 a:1 0 cond:3 011 .. - rs1:5 .............. i=%d16 + CALL 01 i:s30 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 92ea6bab6b..2d08c81821 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -1336,14 +1336,13 @@ static void gen_fcond(TCGv r_dst, unsigned int cc, unsigned int cond) } } -#ifdef TARGET_SPARC64 // Inverted logic -static const int gen_tcg_cond_reg[8] = { - -1, +static const TCGCond gen_tcg_cond_reg[8] = { + TCG_COND_NEVER, /* reserved */ TCG_COND_NE, TCG_COND_GT, TCG_COND_GE, - -1, + TCG_COND_NEVER, /* reserved */ TCG_COND_EQ, TCG_COND_LE, TCG_COND_LT, @@ -1357,16 +1356,6 @@ static void gen_compare_reg(DisasCompare *cmp, int cond, TCGv r_src) cmp->c2 = tcg_constant_tl(0); } -static void gen_cond_reg(TCGv r_dst, int cond, TCGv r_src) -{ - DisasCompare cmp; - gen_compare_reg(&cmp, cond, r_src); - - /* The interface is to return a boolean in r_dst. */ - tcg_gen_setcond_tl(cmp.cond, r_dst, cmp.c1, cmp.c2); -} -#endif - static void do_fbranch(DisasContext *dc, int32_t offset, uint32_t insn, int cc) { unsigned int cond = GET_FIELD(insn, 3, 6), a = (insn & (1 << 29)); @@ -1406,24 +1395,6 @@ static void do_fbranch(DisasContext *dc, int32_t offset, uint32_t insn, int cc) } #ifdef TARGET_SPARC64 -static void do_branch_reg(DisasContext *dc, int32_t offset, uint32_t insn, - TCGv r_reg) -{ - unsigned int cond = GET_FIELD_SP(insn, 25, 27), a = (insn & (1 << 29)); - target_ulong target = dc->pc + offset; - - if (unlikely(AM_CHECK(dc))) { - target &= 0xffffffffULL; - } - flush_cond(dc); - gen_cond_reg(cpu_cond, cond, r_reg); - if (a) { - gen_branch_a(dc, target); - } else { - gen_branch_n(dc, target); - } -} - static void gen_op_fcmps(int fccno, TCGv_i32 r_rs1, TCGv_i32 r_rs2) { switch (fccno) { @@ -3063,6 +3034,24 @@ static bool do_bpcc(DisasContext *dc, arg_bcc *a) TRANS(Bicc, ALL, do_bpcc, a) TRANS(BPcc, 64, do_bpcc, a) +static bool trans_BPr(DisasContext *dc, arg_BPr *a) +{ + target_long target = address_mask_i(dc, dc->pc + a->i * 4); + DisasCompare cmp; + + if (!avail_64(dc)) { + return false; + } + if (gen_tcg_cond_reg[a->cond] == TCG_COND_NEVER) { + return false; + } + + flush_cond(dc); + gen_compare_reg(&cmp, a->cond, gen_load_gpr(dc, a->rs1)); + tcg_gen_setcond_tl(cmp.cond, cpu_cond, cmp.c1, cmp.c2); + return advance_jump_cond(dc, a->a, target); +} + static bool trans_CALL(DisasContext *dc, arg_CALL *a) { target_long target = address_mask_i(dc, dc->pc + a->i * 4); @@ -3102,15 +3091,7 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x1: /* V9 BPcc */ g_assert_not_reached(); /* in decodetree */ case 0x3: /* V9 BPr */ - { - target = GET_FIELD_SP(insn, 0, 13) | - (GET_FIELD_SP(insn, 20, 21) << 14); - target = sign_extend(target, 16); - target <<= 2; - cpu_src1 = get_src1(dc, insn); - do_branch_reg(dc, target, insn, cpu_src1); - goto jmp_insn; - } + g_assert_not_reached(); /* in decodetree */ case 0x5: /* V9 FBPcc */ { int cc = GET_FIELD_SP(insn, 20, 21); From 45196ea4f47b84ed0a9890f309de80665a3e7a81 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 4 Oct 2023 13:00:53 -0700 Subject: [PATCH 029/974] target/sparc: Move FBPfcc and FBfcc to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 4 ++ target/sparc/translate.c | 102 +++++++++++++++----------------------- 2 files changed, 43 insertions(+), 63 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 838f4cdb1d..9ab3f2eb82 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -6,8 +6,12 @@ &bcc i a cond cc BPcc 00 a:1 cond:4 001 cc:1 0 - i:s19 &bcc Bicc 00 a:1 cond:4 010 i:s22 &bcc cc=0 +FBPfcc 00 a:1 cond:4 101 cc:2 - i:s19 &bcc +FBfcc 00 a:1 cond:4 110 i:s22 &bcc cc=0 %d16 20:s2 0:14 BPr 00 a:1 0 cond:3 011 .. - rs1:5 .............. i=%d16 +NCP 00 - ---- 111 ---------------------- # CBcc + CALL 01 i:s30 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 2d08c81821..69e85b1842 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -1356,44 +1356,6 @@ static void gen_compare_reg(DisasCompare *cmp, int cond, TCGv r_src) cmp->c2 = tcg_constant_tl(0); } -static void do_fbranch(DisasContext *dc, int32_t offset, uint32_t insn, int cc) -{ - unsigned int cond = GET_FIELD(insn, 3, 6), a = (insn & (1 << 29)); - target_ulong target = dc->pc + offset; - - if (unlikely(AM_CHECK(dc))) { - target &= 0xffffffffULL; - } - if (cond == 0x0) { - /* unconditional not taken */ - if (a) { - dc->pc = dc->npc + 4; - dc->npc = dc->pc + 4; - } else { - dc->pc = dc->npc; - dc->npc = dc->pc + 4; - } - } else if (cond == 0x8) { - /* unconditional taken */ - if (a) { - dc->pc = target; - dc->npc = dc->pc + 4; - } else { - dc->pc = dc->npc; - dc->npc = target; - tcg_gen_mov_tl(cpu_pc, cpu_npc); - } - } else { - flush_cond(dc); - gen_fcond(cpu_cond, cc, cond); - if (a) { - gen_branch_a(dc, target); - } else { - gen_branch_n(dc, target); - } - } -} - #ifdef TARGET_SPARC64 static void gen_op_fcmps(int fccno, TCGv_i32 r_rs1, TCGv_i32 r_rs2) { @@ -3034,6 +2996,28 @@ static bool do_bpcc(DisasContext *dc, arg_bcc *a) TRANS(Bicc, ALL, do_bpcc, a) TRANS(BPcc, 64, do_bpcc, a) +static bool do_fbpfcc(DisasContext *dc, arg_bcc *a) +{ + target_long target = address_mask_i(dc, dc->pc + a->i * 4); + + if (gen_trap_ifnofpu(dc)) { + return true; + } + switch (a->cond) { + case 0x0: + return advance_jump_uncond_never(dc, a->a); + case 0x8: + return advance_jump_uncond_always(dc, a->a, target); + default: + flush_cond(dc); + gen_fcond(cpu_cond, a->cc, a->cond); + return advance_jump_cond(dc, a->a, target); + } +} + +TRANS(FBPfcc, 64, do_fbpfcc, a) +TRANS(FBfcc, ALL, do_fbpfcc, a) + static bool trans_BPr(DisasContext *dc, arg_BPr *a) { target_long target = address_mask_i(dc, dc->pc + a->i * 4); @@ -3062,6 +3046,20 @@ static bool trans_CALL(DisasContext *dc, arg_CALL *a) return true; } +static bool trans_NCP(DisasContext *dc, arg_NCP *a) +{ + /* + * For sparc32, always generate the no-coprocessor exception. + * For sparc64, always generate illegal instruction. + */ +#ifdef TARGET_SPARC64 + return false; +#else + gen_exception(dc, TT_NCP_INSN); + return true; +#endif +} + #define CHECK_IU_FEATURE(dc, FEATURE) \ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ goto illegal_insn; @@ -3085,7 +3083,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0: /* branches/sethi */ { unsigned int xop = GET_FIELD(insn, 7, 9); - int32_t target; switch (xop) { #ifdef TARGET_SPARC64 case 0x1: /* V9 BPcc */ @@ -3093,36 +3090,15 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x3: /* V9 BPr */ g_assert_not_reached(); /* in decodetree */ case 0x5: /* V9 FBPcc */ - { - int cc = GET_FIELD_SP(insn, 20, 21); - if (gen_trap_ifnofpu(dc)) { - goto jmp_insn; - } - target = GET_FIELD_SP(insn, 0, 18); - target = sign_extend(target, 19); - target <<= 2; - do_fbranch(dc, target, insn, cc); - goto jmp_insn; - } + g_assert_not_reached(); /* in decodetree */ #else case 0x7: /* CBN+x */ - { - goto ncp_insn; - } + g_assert_not_reached(); /* in decodetree */ #endif case 0x2: /* BN+x */ g_assert_not_reached(); /* in decodetree */ case 0x6: /* FBN+x */ - { - if (gen_trap_ifnofpu(dc)) { - goto jmp_insn; - } - target = GET_FIELD(insn, 10, 31); - target = sign_extend(target, 22); - target <<= 2; - do_fbranch(dc, target, insn, 0); - goto jmp_insn; - } + g_assert_not_reached(); /* in decodetree */ case 0x4: /* SETHI */ /* Special-case %g0 because that's the canonical nop. */ if (rd) { From 1ea9c62a5eb32d910f3ff9e7bbbdfb9f875a600f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 4 Oct 2023 13:04:14 -0700 Subject: [PATCH 030/974] target/sparc: Merge gen_cond with only caller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/translate.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 69e85b1842..2664db302d 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -1309,20 +1309,6 @@ static void gen_fcompare(DisasCompare *cmp, unsigned int cc, unsigned int cond) } } -static void gen_cond(TCGv r_dst, unsigned int cc, unsigned int cond, - DisasContext *dc) -{ - DisasCompare cmp; - gen_compare(&cmp, cc, cond, dc); - - /* The interface is to return a boolean in r_dst. */ - if (cmp.is_bool) { - tcg_gen_mov_tl(r_dst, cmp.c1); - } else { - tcg_gen_setcond_tl(cmp.cond, r_dst, cmp.c1, cmp.c2); - } -} - static void gen_fcond(TCGv r_dst, unsigned int cc, unsigned int cond) { DisasCompare cmp; @@ -2980,6 +2966,7 @@ static bool advance_jump_cond(DisasContext *dc, bool annul, target_ulong dest) static bool do_bpcc(DisasContext *dc, arg_bcc *a) { target_long target = address_mask_i(dc, dc->pc + a->i * 4); + DisasCompare cmp; switch (a->cond) { case 0x0: @@ -2988,7 +2975,13 @@ static bool do_bpcc(DisasContext *dc, arg_bcc *a) return advance_jump_uncond_always(dc, a->a, target); default: flush_cond(dc); - gen_cond(cpu_cond, a->cc, a->cond, dc); + + gen_compare(&cmp, a->cc, a->cond, dc); + if (cmp.is_bool) { + tcg_gen_mov_tl(cpu_cond, cmp.c1); + } else { + tcg_gen_setcond_tl(cmp.cond, cpu_cond, cmp.c1, cmp.c2); + } return advance_jump_cond(dc, a->a, target); } } From d5471936164e4ee3039f15d18308029040013a31 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 4 Oct 2023 13:20:30 -0700 Subject: [PATCH 031/974] target/sparc: Merge gen_fcond with only caller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/translate.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 2664db302d..b8d51d6d64 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -1309,19 +1309,6 @@ static void gen_fcompare(DisasCompare *cmp, unsigned int cc, unsigned int cond) } } -static void gen_fcond(TCGv r_dst, unsigned int cc, unsigned int cond) -{ - DisasCompare cmp; - gen_fcompare(&cmp, cc, cond); - - /* The interface is to return a boolean in r_dst. */ - if (cmp.is_bool) { - tcg_gen_mov_tl(r_dst, cmp.c1); - } else { - tcg_gen_setcond_tl(cmp.cond, r_dst, cmp.c1, cmp.c2); - } -} - // Inverted logic static const TCGCond gen_tcg_cond_reg[8] = { TCG_COND_NEVER, /* reserved */ @@ -2992,6 +2979,7 @@ TRANS(BPcc, 64, do_bpcc, a) static bool do_fbpfcc(DisasContext *dc, arg_bcc *a) { target_long target = address_mask_i(dc, dc->pc + a->i * 4); + DisasCompare cmp; if (gen_trap_ifnofpu(dc)) { return true; @@ -3003,7 +2991,13 @@ static bool do_fbpfcc(DisasContext *dc, arg_bcc *a) return advance_jump_uncond_always(dc, a->a, target); default: flush_cond(dc); - gen_fcond(cpu_cond, a->cc, a->cond); + + gen_fcompare(&cmp, a->cc, a->cond); + if (cmp.is_bool) { + tcg_gen_mov_tl(cpu_cond, cmp.c1); + } else { + tcg_gen_setcond_tl(cmp.cond, cpu_cond, cmp.c1, cmp.c2); + } return advance_jump_cond(dc, a->a, target); } } From 6b3e4cc685a6582fe555d6207d563ee972c966b3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 4 Oct 2023 13:23:07 -0700 Subject: [PATCH 032/974] target/sparc: Merge gen_branch_[an] with only caller Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/translate.c | 73 +++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 43 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index b8d51d6d64..503598ed93 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -900,47 +900,6 @@ static void gen_branch2(DisasContext *dc, target_ulong pc1, gen_goto_tb(dc, 1, pc2, pc2 + 4); } -static void gen_branch_a(DisasContext *dc, target_ulong pc1) -{ - TCGLabel *l1 = gen_new_label(); - target_ulong npc = dc->npc; - - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_cond, 0, l1); - - gen_goto_tb(dc, 0, npc, pc1); - - gen_set_label(l1); - gen_goto_tb(dc, 1, npc + 4, npc + 8); - - dc->base.is_jmp = DISAS_NORETURN; -} - -static void gen_branch_n(DisasContext *dc, target_ulong pc1) -{ - target_ulong npc = dc->npc; - - if (npc & 3) { - switch (npc) { - case DYNAMIC_PC: - case DYNAMIC_PC_LOOKUP: - tcg_gen_mov_tl(cpu_pc, cpu_npc); - tcg_gen_addi_tl(cpu_npc, cpu_npc, 4); - tcg_gen_movcond_tl(TCG_COND_NE, cpu_npc, - cpu_cond, tcg_constant_tl(0), - tcg_constant_tl(pc1), cpu_npc); - dc->pc = npc; - break; - default: - g_assert_not_reached(); - } - } else { - dc->pc = npc; - dc->jump_pc[0] = pc1; - dc->jump_pc[1] = npc + 4; - dc->npc = JUMP_PC; - } -} - static void gen_generic_branch(DisasContext *dc) { TCGv npc0 = tcg_constant_tl(dc->jump_pc[0]); @@ -2942,10 +2901,38 @@ static bool advance_jump_uncond_always(DisasContext *dc, bool annul, static bool advance_jump_cond(DisasContext *dc, bool annul, target_ulong dest) { + target_ulong npc = dc->npc; + if (annul) { - gen_branch_a(dc, dest); + TCGLabel *l1 = gen_new_label(); + + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_cond, 0, l1); + gen_goto_tb(dc, 0, npc, dest); + gen_set_label(l1); + gen_goto_tb(dc, 1, npc + 4, npc + 8); + + dc->base.is_jmp = DISAS_NORETURN; } else { - gen_branch_n(dc, dest); + if (npc & 3) { + switch (npc) { + case DYNAMIC_PC: + case DYNAMIC_PC_LOOKUP: + tcg_gen_mov_tl(cpu_pc, cpu_npc); + tcg_gen_addi_tl(cpu_npc, cpu_npc, 4); + tcg_gen_movcond_tl(TCG_COND_NE, cpu_npc, + cpu_cond, tcg_constant_tl(0), + tcg_constant_tl(dest), cpu_npc); + dc->pc = npc; + break; + default: + g_assert_not_reached(); + } + } else { + dc->pc = npc; + dc->jump_pc[0] = dest; + dc->jump_pc[1] = npc + 4; + dc->npc = JUMP_PC; + } } return true; } From 9d4e2bc76137299aef88d7398c3af6b688d299fa Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 4 Oct 2023 13:38:18 -0700 Subject: [PATCH 033/974] target/sparc: Pass DisasCompare to advance_jump_cond Fold the condition into the branch or movcond when possible. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/translate.c | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 503598ed93..d12f2b4b87 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -2899,14 +2899,15 @@ static bool advance_jump_uncond_always(DisasContext *dc, bool annul, return true; } -static bool advance_jump_cond(DisasContext *dc, bool annul, target_ulong dest) +static bool advance_jump_cond(DisasContext *dc, DisasCompare *cmp, + bool annul, target_ulong dest) { target_ulong npc = dc->npc; if (annul) { TCGLabel *l1 = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_cond, 0, l1); + tcg_gen_brcond_tl(tcg_invert_cond(cmp->cond), cmp->c1, cmp->c2, l1); gen_goto_tb(dc, 0, npc, dest); gen_set_label(l1); gen_goto_tb(dc, 1, npc + 4, npc + 8); @@ -2919,8 +2920,8 @@ static bool advance_jump_cond(DisasContext *dc, bool annul, target_ulong dest) case DYNAMIC_PC_LOOKUP: tcg_gen_mov_tl(cpu_pc, cpu_npc); tcg_gen_addi_tl(cpu_npc, cpu_npc, 4); - tcg_gen_movcond_tl(TCG_COND_NE, cpu_npc, - cpu_cond, tcg_constant_tl(0), + tcg_gen_movcond_tl(cmp->cond, cpu_npc, + cmp->c1, cmp->c2, tcg_constant_tl(dest), cpu_npc); dc->pc = npc; break; @@ -2932,6 +2933,11 @@ static bool advance_jump_cond(DisasContext *dc, bool annul, target_ulong dest) dc->jump_pc[0] = dest; dc->jump_pc[1] = npc + 4; dc->npc = JUMP_PC; + if (cmp->is_bool) { + tcg_gen_mov_tl(cpu_cond, cmp->c1); + } else { + tcg_gen_setcond_tl(cmp->cond, cpu_cond, cmp->c1, cmp->c2); + } } } return true; @@ -2951,12 +2957,7 @@ static bool do_bpcc(DisasContext *dc, arg_bcc *a) flush_cond(dc); gen_compare(&cmp, a->cc, a->cond, dc); - if (cmp.is_bool) { - tcg_gen_mov_tl(cpu_cond, cmp.c1); - } else { - tcg_gen_setcond_tl(cmp.cond, cpu_cond, cmp.c1, cmp.c2); - } - return advance_jump_cond(dc, a->a, target); + return advance_jump_cond(dc, &cmp, a->a, target); } } @@ -2980,12 +2981,7 @@ static bool do_fbpfcc(DisasContext *dc, arg_bcc *a) flush_cond(dc); gen_fcompare(&cmp, a->cc, a->cond); - if (cmp.is_bool) { - tcg_gen_mov_tl(cpu_cond, cmp.c1); - } else { - tcg_gen_setcond_tl(cmp.cond, cpu_cond, cmp.c1, cmp.c2); - } - return advance_jump_cond(dc, a->a, target); + return advance_jump_cond(dc, &cmp, a->a, target); } } @@ -3006,8 +3002,7 @@ static bool trans_BPr(DisasContext *dc, arg_BPr *a) flush_cond(dc); gen_compare_reg(&cmp, a->cond, gen_load_gpr(dc, a->rs1)); - tcg_gen_setcond_tl(cmp.cond, cpu_cond, cmp.c1, cmp.c2); - return advance_jump_cond(dc, a->a, target); + return advance_jump_cond(dc, &cmp, a->a, target); } static bool trans_CALL(DisasContext *dc, arg_CALL *a) From 6d2a0768426554ae5bcaca2445d0c9c53335809a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 1 Oct 2023 16:48:18 -0700 Subject: [PATCH 034/974] target/sparc: Move SETHI to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 6 +++++ target/sparc/translate.c | 50 ++++++++++++--------------------------- 2 files changed, 21 insertions(+), 35 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 9ab3f2eb82..f6f5401b10 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -3,6 +3,10 @@ # Sparc instruction decode definitions. # Copyright (c) 2023 Richard Henderson +## +## Major Opcodes 00 and 01 -- branches, call, and sethi. +## + &bcc i a cond cc BPcc 00 a:1 cond:4 001 cc:1 0 - i:s19 &bcc Bicc 00 a:1 cond:4 010 i:s22 &bcc cc=0 @@ -14,4 +18,6 @@ BPr 00 a:1 0 cond:3 011 .. - rs1:5 .............. i=%d16 NCP 00 - ---- 111 ---------------------- # CBcc +SETHI 00 rd:5 100 i:22 + CALL 01 i:s30 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index d12f2b4b87..cdd929282a 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -2873,6 +2873,10 @@ static bool advance_pc(DisasContext *dc) return true; } +/* + * Major opcodes 00 and 01 -- branches, call, and sethi + */ + static bool advance_jump_uncond_never(DisasContext *dc, bool annul) { if (annul) { @@ -3029,6 +3033,15 @@ static bool trans_NCP(DisasContext *dc, arg_NCP *a) #endif } +static bool trans_SETHI(DisasContext *dc, arg_SETHI *a) +{ + /* Special-case %g0 because that's the canonical nop. */ + if (a->rd) { + gen_store_gpr(dc, a->rd, tcg_constant_tl((uint32_t)a->i << 10)); + } + return advance_pc(dc); +} + #define CHECK_IU_FEATURE(dc, FEATURE) \ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ goto illegal_insn; @@ -3049,41 +3062,8 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) rd = GET_FIELD(insn, 2, 6); switch (opc) { - case 0: /* branches/sethi */ - { - unsigned int xop = GET_FIELD(insn, 7, 9); - switch (xop) { -#ifdef TARGET_SPARC64 - case 0x1: /* V9 BPcc */ - g_assert_not_reached(); /* in decodetree */ - case 0x3: /* V9 BPr */ - g_assert_not_reached(); /* in decodetree */ - case 0x5: /* V9 FBPcc */ - g_assert_not_reached(); /* in decodetree */ -#else - case 0x7: /* CBN+x */ - g_assert_not_reached(); /* in decodetree */ -#endif - case 0x2: /* BN+x */ - g_assert_not_reached(); /* in decodetree */ - case 0x6: /* FBN+x */ - g_assert_not_reached(); /* in decodetree */ - case 0x4: /* SETHI */ - /* Special-case %g0 because that's the canonical nop. */ - if (rd) { - uint32_t value = GET_FIELD(insn, 10, 31); - TCGv t = gen_dest_gpr(dc, rd); - tcg_gen_movi_tl(t, value << 10); - gen_store_gpr(dc, rd, t); - } - break; - case 0x0: /* UNIMPL */ - default: - goto illegal_insn; - } - break; - } - break; + case 0: + goto illegal_insn; /* in decodetree */ case 1: g_assert_not_reached(); /* in decodetree */ case 2: /* FPU & Logical Operations */ From 3037663616db929a3f9c21daa64ff9605bb6ac6f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 1 Oct 2023 18:29:42 -0700 Subject: [PATCH 035/974] target/sparc: Move Tcc to decodetree Use the new delay_exceptionv function in the implementation. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 13 ++++ target/sparc/translate.c | 155 +++++++++++++++++++------------------- 2 files changed, 89 insertions(+), 79 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index f6f5401b10..0517f5591b 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -21,3 +21,16 @@ NCP 00 - ---- 111 ---------------------- # CBcc SETHI 00 rd:5 100 i:22 CALL 01 i:s30 + +Tcc_r 10 0 cond:4 111010 rs1:5 0 cc:1 0000000 rs2:5 +{ + # For v7, the entire simm13 field is present, but masked to 7 bits. + # For v8, [12:7] are reserved. However, a compatibility note for + # the Tcc insn in the v9 manual suggests that the v8 reserved field + # was ignored and did not produce traps. + Tcc_i_v7 10 0 cond:4 111010 rs1:5 1 ------ i:7 + + # For v9, bits [12:11] are cc1 and cc0 (and cc0 must be 0). + # Bits [10:8] are reserved and the OSA2011 manual says they must be 0. + Tcc_i_v9 10 0 cond:4 111010 rs1:5 1 cc:1 0 000 i:8 +} diff --git a/target/sparc/translate.c b/target/sparc/translate.c index cdd929282a..b927b212ca 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -3042,6 +3042,81 @@ static bool trans_SETHI(DisasContext *dc, arg_SETHI *a) return advance_pc(dc); } +static bool do_tcc(DisasContext *dc, int cond, int cc, + int rs1, bool imm, int rs2_or_imm) +{ + int mask = ((dc->def->features & CPU_FEATURE_HYPV) && supervisor(dc) + ? UA2005_HTRAP_MASK : V8_TRAP_MASK); + DisasCompare cmp; + TCGLabel *lab; + TCGv_i32 trap; + + /* Trap never. */ + if (cond == 0) { + return advance_pc(dc); + } + + /* + * Immediate traps are the most common case. Since this value is + * live across the branch, it really pays to evaluate the constant. + */ + if (rs1 == 0 && (imm || rs2_or_imm == 0)) { + trap = tcg_constant_i32((rs2_or_imm & mask) + TT_TRAP); + } else { + trap = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(trap, gen_load_gpr(dc, rs1)); + if (imm) { + tcg_gen_addi_i32(trap, trap, rs2_or_imm); + } else { + TCGv_i32 t2 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(t2, gen_load_gpr(dc, rs2_or_imm)); + tcg_gen_add_i32(trap, trap, t2); + } + tcg_gen_andi_i32(trap, trap, mask); + tcg_gen_addi_i32(trap, trap, TT_TRAP); + } + + /* Trap always. */ + if (cond == 8) { + save_state(dc); + gen_helper_raise_exception(tcg_env, trap); + dc->base.is_jmp = DISAS_NORETURN; + return true; + } + + /* Conditional trap. */ + flush_cond(dc); + lab = delay_exceptionv(dc, trap); + gen_compare(&cmp, cc, cond, dc); + tcg_gen_brcond_tl(cmp.cond, cmp.c1, cmp.c2, lab); + + return advance_pc(dc); +} + +static bool trans_Tcc_r(DisasContext *dc, arg_Tcc_r *a) +{ + if (avail_32(dc) && a->cc) { + return false; + } + return do_tcc(dc, a->cond, a->cc, a->rs1, false, a->rs2); +} + +static bool trans_Tcc_i_v7(DisasContext *dc, arg_Tcc_i_v7 *a) +{ + if (avail_64(dc)) { + return false; + } + return do_tcc(dc, a->cond, 0, a->rs1, true, a->i); +} + +static bool trans_Tcc_i_v9(DisasContext *dc, arg_Tcc_i_v9 *a) +{ + if (avail_32(dc)) { + return false; + } + return do_tcc(dc, a->cond, a->cc, a->rs1, true, a->i); +} + #define CHECK_IU_FEATURE(dc, FEATURE) \ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ goto illegal_insn; @@ -3072,85 +3147,7 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) TCGv cpu_dst = tcg_temp_new(); TCGv cpu_tmp0; - if (xop == 0x3a) { /* generate trap */ - int cond = GET_FIELD(insn, 3, 6); - TCGv_i32 trap; - TCGLabel *l1 = NULL; - int mask; - - if (cond == 0) { - /* Trap never. */ - break; - } - - save_state(dc); - - if (cond != 8) { - /* Conditional trap. */ - DisasCompare cmp; -#ifdef TARGET_SPARC64 - /* V9 icc/xcc */ - int cc = GET_FIELD_SP(insn, 11, 12); - if (cc == 0) { - gen_compare(&cmp, 0, cond, dc); - } else if (cc == 2) { - gen_compare(&cmp, 1, cond, dc); - } else { - goto illegal_insn; - } -#else - gen_compare(&cmp, 0, cond, dc); -#endif - l1 = gen_new_label(); - tcg_gen_brcond_tl(tcg_invert_cond(cmp.cond), - cmp.c1, cmp.c2, l1); - } - - mask = ((dc->def->features & CPU_FEATURE_HYPV) && supervisor(dc) - ? UA2005_HTRAP_MASK : V8_TRAP_MASK); - - /* Don't use the normal temporaries, as they may well have - gone out of scope with the branch above. While we're - doing that we might as well pre-truncate to 32-bit. */ - trap = tcg_temp_new_i32(); - - rs1 = GET_FIELD_SP(insn, 14, 18); - if (IS_IMM) { - rs2 = GET_FIELD_SP(insn, 0, 7); - if (rs1 == 0) { - tcg_gen_movi_i32(trap, (rs2 & mask) + TT_TRAP); - /* Signal that the trap value is fully constant. */ - mask = 0; - } else { - TCGv t1 = gen_load_gpr(dc, rs1); - tcg_gen_trunc_tl_i32(trap, t1); - tcg_gen_addi_i32(trap, trap, rs2); - } - } else { - TCGv t1, t2; - rs2 = GET_FIELD_SP(insn, 0, 4); - t1 = gen_load_gpr(dc, rs1); - t2 = gen_load_gpr(dc, rs2); - tcg_gen_add_tl(t1, t1, t2); - tcg_gen_trunc_tl_i32(trap, t1); - } - if (mask != 0) { - tcg_gen_andi_i32(trap, trap, mask); - tcg_gen_addi_i32(trap, trap, TT_TRAP); - } - - gen_helper_raise_exception(tcg_env, trap); - - if (cond == 8) { - /* An unconditional trap ends the TB. */ - dc->base.is_jmp = DISAS_NORETURN; - goto jmp_insn; - } else { - /* A conditional trap falls through to the next insn. */ - gen_set_label(l1); - break; - } - } else if (xop == 0x28) { + if (xop == 0x28) { rs1 = GET_FIELD(insn, 13, 17); switch(rs1) { case 0: /* rdy */ From af25071c1d9ecccd3f05fd7411209a858e5b45b4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 1 Oct 2023 19:21:38 -0700 Subject: [PATCH 036/974] target/sparc: Move RDASR, STBAR, MEMBAR to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 22 +++ target/sparc/translate.c | 342 ++++++++++++++++++++++++-------------- 2 files changed, 235 insertions(+), 129 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 0517f5591b..1b084c5b97 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -22,6 +22,28 @@ SETHI 00 rd:5 100 i:22 CALL 01 i:s30 +{ + [ + STBAR 10 00000 101000 01111 0 0000000000000 + MEMBAR 10 00000 101000 01111 1 000000 cmask:3 mmask:4 + + RDCCR 10 rd:5 101000 00010 0 0000000000000 + RDASI 10 rd:5 101000 00011 0 0000000000000 + RDTICK 10 rd:5 101000 00100 0 0000000000000 + RDPC 10 rd:5 101000 00101 0 0000000000000 + RDFPRS 10 rd:5 101000 00110 0 0000000000000 + RDASR17 10 rd:5 101000 10001 0 0000000000000 + RDGSR 10 rd:5 101000 10011 0 0000000000000 + RDSOFTINT 10 rd:5 101000 10110 0 0000000000000 + RDTICK_CMPR 10 rd:5 101000 10111 0 0000000000000 + RDSTICK 10 rd:5 101000 11000 0 0000000000000 + RDSTICK_CMPR 10 rd:5 101000 11001 0 0000000000000 + RDSTRAND_STATUS 10 rd:5 101000 11010 0 0000000000000 + ] + # Before v8, all rs1 accepted; otherwise rs1==0. + RDY 10 rd:5 101000 rs1:5 0 0000000000000 +} + Tcc_r 10 0 cond:4 111010 rs1:5 0 cc:1 0000000 rs2:5 { # For v7, the entire simm13 field is present, but masked to 7 bits. diff --git a/target/sparc/translate.c b/target/sparc/translate.c index b927b212ca..9af4578525 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -36,6 +36,11 @@ #include "exec/helper-info.c.inc" #undef HELPER_H +#ifndef TARGET_SPARC64 +# define gen_helper_rdccr(D, E) qemu_build_not_reached() +# define gen_helper_tick_get_count(D, E, T, C) qemu_build_not_reached() +#endif + /* Dynamic PC, must exit to main loop. */ #define DYNAMIC_PC 1 /* Dynamic PC, one of two values according to jump_pc[T2]. */ @@ -64,10 +69,21 @@ static TCGv cpu_tick_cmpr, cpu_stick_cmpr, cpu_hstick_cmpr; static TCGv cpu_hintp, cpu_htba, cpu_hver, cpu_ssr, cpu_ver; #else static TCGv cpu_wim; +# define cpu_fprs ({ qemu_build_not_reached(); (TCGv)NULL; }) +# define cpu_gsr ({ qemu_build_not_reached(); (TCGv)NULL; }) +# define cpu_tick_cmpr ({ qemu_build_not_reached(); (TCGv)NULL; }) +# define cpu_stick_cmpr ({ qemu_build_not_reached(); (TCGv)NULL; }) #endif /* Floating point registers */ static TCGv_i64 cpu_fpr[TARGET_DPREGS]; +#define env_field_offsetof(X) offsetof(CPUSPARCState, X) +#ifdef TARGET_SPARC64 +# define env64_field_offsetof(X) env_field_offsetof(X) +#else +# define env64_field_offsetof(X) ({ qemu_build_not_reached(); 0; }) +#endif + typedef struct DisasDelayException { struct DisasDelayException *next; TCGLabel *lab; @@ -2842,10 +2858,14 @@ static void gen_faligndata(TCGv dst, TCGv gsr, TCGv s1, TCGv s2) #define avail_ALL(C) true #ifdef TARGET_SPARC64 # define avail_32(C) false +# define avail_ASR17(C) false # define avail_64(C) true +# define avail_HYPV(C) ((C)->def->features & CPU_FEATURE_HYPV) #else # define avail_32(C) true +# define avail_ASR17(C) ((C)->def->features & CPU_FEATURE_ASR17) # define avail_64(C) false +# define avail_HYPV(C) false #endif /* Default case for non jump instructions. */ @@ -2947,6 +2967,12 @@ static bool advance_jump_cond(DisasContext *dc, DisasCompare *cmp, return true; } +static bool raise_priv(DisasContext *dc) +{ + gen_exception(dc, TT_PRIV_INSN); + return true; +} + static bool do_bpcc(DisasContext *dc, arg_bcc *a) { target_long target = address_mask_i(dc, dc->pc + a->i * 4); @@ -3117,6 +3143,183 @@ static bool trans_Tcc_i_v9(DisasContext *dc, arg_Tcc_i_v9 *a) return do_tcc(dc, a->cond, a->cc, a->rs1, true, a->i); } +static bool trans_STBAR(DisasContext *dc, arg_STBAR *a) +{ + tcg_gen_mb(TCG_MO_ST_ST | TCG_BAR_SC); + return advance_pc(dc); +} + +static bool trans_MEMBAR(DisasContext *dc, arg_MEMBAR *a) +{ + if (avail_32(dc)) { + return false; + } + if (a->mmask) { + /* Note TCG_MO_* was modeled on sparc64, so mmask matches. */ + tcg_gen_mb(a->mmask | TCG_BAR_SC); + } + if (a->cmask) { + /* For #Sync, etc, end the TB to recognize interrupts. */ + dc->base.is_jmp = DISAS_EXIT; + } + return advance_pc(dc); +} + +static bool do_rd_special(DisasContext *dc, bool priv, int rd, + TCGv (*func)(DisasContext *, TCGv)) +{ + if (!priv) { + return raise_priv(dc); + } + gen_store_gpr(dc, rd, func(dc, gen_dest_gpr(dc, rd))); + return advance_pc(dc); +} + +static TCGv do_rdy(DisasContext *dc, TCGv dst) +{ + return cpu_y; +} + +static bool trans_RDY(DisasContext *dc, arg_RDY *a) +{ + /* + * TODO: Need a feature bit for sparcv8. In the meantime, treat all + * 32-bit cpus like sparcv7, which ignores the rs1 field. + * This matches after all other ASR, so Leon3 Asr17 is handled first. + */ + if (avail_64(dc) && a->rs1 != 0) { + return false; + } + return do_rd_special(dc, true, a->rd, do_rdy); +} + +static TCGv do_rd_leon3_config(DisasContext *dc, TCGv dst) +{ + uint32_t val; + + /* + * TODO: There are many more fields to be filled, + * some of which are writable. + */ + val = dc->def->nwindows - 1; /* [4:0] NWIN */ + val |= 1 << 8; /* [8] V8 */ + + return tcg_constant_tl(val); +} + +TRANS(RDASR17, ASR17, do_rd_special, true, a->rd, do_rd_leon3_config) + +static TCGv do_rdccr(DisasContext *dc, TCGv dst) +{ + update_psr(dc); + gen_helper_rdccr(dst, tcg_env); + return dst; +} + +TRANS(RDCCR, 64, do_rd_special, true, a->rd, do_rdccr) + +static TCGv do_rdasi(DisasContext *dc, TCGv dst) +{ +#ifdef TARGET_SPARC64 + return tcg_constant_tl(dc->asi); +#else + qemu_build_not_reached(); +#endif +} + +TRANS(RDASI, 64, do_rd_special, true, a->rd, do_rdasi) + +static TCGv do_rdtick(DisasContext *dc, TCGv dst) +{ + TCGv_ptr r_tickptr = tcg_temp_new_ptr(); + + tcg_gen_ld_ptr(r_tickptr, tcg_env, env64_field_offsetof(tick)); + if (translator_io_start(&dc->base)) { + dc->base.is_jmp = DISAS_EXIT; + } + gen_helper_tick_get_count(dst, tcg_env, r_tickptr, + tcg_constant_i32(dc->mem_idx)); + return dst; +} + +/* TODO: non-priv access only allowed when enabled. */ +TRANS(RDTICK, 64, do_rd_special, true, a->rd, do_rdtick) + +static TCGv do_rdpc(DisasContext *dc, TCGv dst) +{ + return tcg_constant_tl(address_mask_i(dc, dc->pc)); +} + +TRANS(RDPC, 64, do_rd_special, true, a->rd, do_rdpc) + +static TCGv do_rdfprs(DisasContext *dc, TCGv dst) +{ + tcg_gen_ext_i32_tl(dst, cpu_fprs); + return dst; +} + +TRANS(RDFPRS, 64, do_rd_special, true, a->rd, do_rdfprs) + +static TCGv do_rdgsr(DisasContext *dc, TCGv dst) +{ + gen_trap_ifnofpu(dc); + return cpu_gsr; +} + +TRANS(RDGSR, 64, do_rd_special, true, a->rd, do_rdgsr) + +static TCGv do_rdsoftint(DisasContext *dc, TCGv dst) +{ + tcg_gen_ld32s_tl(dst, tcg_env, env64_field_offsetof(softint)); + return dst; +} + +TRANS(RDSOFTINT, 64, do_rd_special, supervisor(dc), a->rd, do_rdsoftint) + +static TCGv do_rdtick_cmpr(DisasContext *dc, TCGv dst) +{ + return cpu_tick_cmpr; +} + +/* TODO: non-priv access only allowed when enabled. */ +TRANS(RDTICK_CMPR, 64, do_rd_special, true, a->rd, do_rdtick_cmpr) + +static TCGv do_rdstick(DisasContext *dc, TCGv dst) +{ + TCGv_ptr r_tickptr = tcg_temp_new_ptr(); + + tcg_gen_ld_ptr(r_tickptr, tcg_env, env64_field_offsetof(stick)); + if (translator_io_start(&dc->base)) { + dc->base.is_jmp = DISAS_EXIT; + } + gen_helper_tick_get_count(dst, tcg_env, r_tickptr, + tcg_constant_i32(dc->mem_idx)); + return dst; +} + +/* TODO: non-priv access only allowed when enabled. */ +TRANS(RDSTICK, 64, do_rd_special, true, a->rd, do_rdstick) + +static TCGv do_rdstick_cmpr(DisasContext *dc, TCGv dst) +{ + return cpu_stick_cmpr; +} + +/* TODO: supervisor access only allowed when enabled by hypervisor. */ +TRANS(RDSTICK_CMPR, 64, do_rd_special, supervisor(dc), a->rd, do_rdstick_cmpr) + +/* + * UltraSPARC-T1 Strand status. + * HYPV check maybe not enough, UA2005 & UA2007 describe + * this ASR as impl. dep + */ +static TCGv do_rdstrand_status(DisasContext *dc, TCGv dst) +{ + return tcg_constant_tl(1); +} + +TRANS(RDSTRAND_STATUS, HYPV, do_rd_special, true, a->rd, do_rdstrand_status) + #define CHECK_IU_FEATURE(dc, FEATURE) \ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ goto illegal_insn; @@ -3143,134 +3346,12 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) g_assert_not_reached(); /* in decodetree */ case 2: /* FPU & Logical Operations */ { - unsigned int xop = GET_FIELD(insn, 7, 12); - TCGv cpu_dst = tcg_temp_new(); - TCGv cpu_tmp0; + unsigned int xop __attribute__((unused)) = GET_FIELD(insn, 7, 12); + TCGv cpu_dst __attribute__((unused)) = tcg_temp_new(); + TCGv cpu_tmp0 __attribute__((unused)); - if (xop == 0x28) { - rs1 = GET_FIELD(insn, 13, 17); - switch(rs1) { - case 0: /* rdy */ -#ifndef TARGET_SPARC64 - case 0x01 ... 0x0e: /* undefined in the SPARCv8 - manual, rdy on the microSPARC - II */ - case 0x0f: /* stbar in the SPARCv8 manual, - rdy on the microSPARC II */ - case 0x10 ... 0x1f: /* implementation-dependent in the - SPARCv8 manual, rdy on the - microSPARC II */ - /* Read Asr17 */ - if (rs1 == 0x11 && dc->def->features & CPU_FEATURE_ASR17) { - TCGv t = gen_dest_gpr(dc, rd); - /* Read Asr17 for a Leon3 monoprocessor */ - tcg_gen_movi_tl(t, (1 << 8) | (dc->def->nwindows - 1)); - gen_store_gpr(dc, rd, t); - break; - } -#endif - gen_store_gpr(dc, rd, cpu_y); - break; -#ifdef TARGET_SPARC64 - case 0x2: /* V9 rdccr */ - update_psr(dc); - gen_helper_rdccr(cpu_dst, tcg_env); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x3: /* V9 rdasi */ - tcg_gen_movi_tl(cpu_dst, dc->asi); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x4: /* V9 rdtick */ - { - TCGv_ptr r_tickptr; - TCGv_i32 r_const; - - r_tickptr = tcg_temp_new_ptr(); - r_const = tcg_constant_i32(dc->mem_idx); - tcg_gen_ld_ptr(r_tickptr, tcg_env, - offsetof(CPUSPARCState, tick)); - if (translator_io_start(&dc->base)) { - dc->base.is_jmp = DISAS_EXIT; - } - gen_helper_tick_get_count(cpu_dst, tcg_env, r_tickptr, - r_const); - gen_store_gpr(dc, rd, cpu_dst); - } - break; - case 0x5: /* V9 rdpc */ - { - TCGv t = gen_dest_gpr(dc, rd); - if (unlikely(AM_CHECK(dc))) { - tcg_gen_movi_tl(t, dc->pc & 0xffffffffULL); - } else { - tcg_gen_movi_tl(t, dc->pc); - } - gen_store_gpr(dc, rd, t); - } - break; - case 0x6: /* V9 rdfprs */ - tcg_gen_ext_i32_tl(cpu_dst, cpu_fprs); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0xf: /* V9 membar */ - break; /* no effect */ - case 0x13: /* Graphics Status */ - if (gen_trap_ifnofpu(dc)) { - goto jmp_insn; - } - gen_store_gpr(dc, rd, cpu_gsr); - break; - case 0x16: /* Softint */ - tcg_gen_ld32s_tl(cpu_dst, tcg_env, - offsetof(CPUSPARCState, softint)); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x17: /* Tick compare */ - gen_store_gpr(dc, rd, cpu_tick_cmpr); - break; - case 0x18: /* System tick */ - { - TCGv_ptr r_tickptr; - TCGv_i32 r_const; - - r_tickptr = tcg_temp_new_ptr(); - r_const = tcg_constant_i32(dc->mem_idx); - tcg_gen_ld_ptr(r_tickptr, tcg_env, - offsetof(CPUSPARCState, stick)); - if (translator_io_start(&dc->base)) { - dc->base.is_jmp = DISAS_EXIT; - } - gen_helper_tick_get_count(cpu_dst, tcg_env, r_tickptr, - r_const); - gen_store_gpr(dc, rd, cpu_dst); - } - break; - case 0x19: /* System tick compare */ - gen_store_gpr(dc, rd, cpu_stick_cmpr); - break; - case 0x1a: /* UltraSPARC-T1 Strand status */ - /* XXX HYPV check maybe not enough, UA2005 & UA2007 describe - * this ASR as impl. dep - */ - CHECK_IU_FEATURE(dc, HYPV); - { - TCGv t = gen_dest_gpr(dc, rd); - tcg_gen_movi_tl(t, 1UL); - gen_store_gpr(dc, rd, t); - } - break; - case 0x10: /* Performance Control */ - case 0x11: /* Performance Instrumentation Counter */ - case 0x12: /* Dispatch Control */ - case 0x14: /* Softint set, WO */ - case 0x15: /* Softint clear, WO */ -#endif - default: - goto illegal_insn; - } #if !defined(CONFIG_USER_ONLY) - } else if (xop == 0x29) { /* rdpsr / UA2005 rdhpr */ + if (xop == 0x29) { /* rdpsr / UA2005 rdhpr */ #ifndef TARGET_SPARC64 if (!supervisor(dc)) { goto priv_insn; @@ -3308,7 +3389,8 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) #endif gen_store_gpr(dc, rd, cpu_dst); break; - } else if (xop == 0x2a) { /* rdwim / V9 rdpr */ + } + if (xop == 0x2a) { /* rdwim / V9 rdpr */ if (!supervisor(dc)) { goto priv_insn; } @@ -3432,9 +3514,10 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) #endif gen_store_gpr(dc, rd, cpu_tmp0); break; + } #endif #if defined(TARGET_SPARC64) || !defined(CONFIG_USER_ONLY) - } else if (xop == 0x2b) { /* rdtbr / V9 flushw */ + if (xop == 0x2b) { /* rdtbr / V9 flushw */ #ifdef TARGET_SPARC64 gen_helper_flushw(tcg_env); #else @@ -3443,8 +3526,9 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) gen_store_gpr(dc, rd, cpu_tbr); #endif break; + } #endif - } else if (xop == 0x34) { /* FPU Operations */ + if (xop == 0x34) { /* FPU Operations */ if (gen_trap_ifnofpu(dc)) { goto jmp_insn; } From 668bb9b755e3d4e4e88625aefab14f8e642ca2f3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 1 Oct 2023 20:34:14 -0700 Subject: [PATCH 037/974] target/sparc: Move RDPSR, RDHPR to decodetree Implement htstate in the obvious way. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/847 Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 10 ++++ target/sparc/translate.c | 116 ++++++++++++++++++++++++-------------- 2 files changed, 83 insertions(+), 43 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 1b084c5b97..a7d78eb6c6 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -44,6 +44,16 @@ CALL 01 i:s30 RDY 10 rd:5 101000 rs1:5 0 0000000000000 } +{ + RDPSR 10 rd:5 101001 00000 0 0000000000000 + RDHPR_hpstate 10 rd:5 101001 00000 0 0000000000000 +} +RDHPR_htstate 10 rd:5 101001 00001 0 0000000000000 +RDHPR_hintp 10 rd:5 101001 00011 0 0000000000000 +RDHPR_htba 10 rd:5 101001 00101 0 0000000000000 +RDHPR_hver 10 rd:5 101001 00110 0 0000000000000 +RDHPR_hstick_cmpr 10 rd:5 101001 11111 0 0000000000000 + Tcc_r 10 0 cond:4 111010 rs1:5 0 cc:1 0000000 rs2:5 { # For v7, the entire simm13 field is present, but masked to 7 bits. diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 9af4578525..b58c2594ca 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -36,9 +36,12 @@ #include "exec/helper-info.c.inc" #undef HELPER_H -#ifndef TARGET_SPARC64 +#ifdef TARGET_SPARC64 +# define gen_helper_rdpsr(D, E) qemu_build_not_reached() +#else # define gen_helper_rdccr(D, E) qemu_build_not_reached() # define gen_helper_tick_get_count(D, E, T, C) qemu_build_not_reached() +# define MAXTL_MASK 0 #endif /* Dynamic PC, must exit to main loop. */ @@ -71,8 +74,12 @@ static TCGv cpu_hintp, cpu_htba, cpu_hver, cpu_ssr, cpu_ver; static TCGv cpu_wim; # define cpu_fprs ({ qemu_build_not_reached(); (TCGv)NULL; }) # define cpu_gsr ({ qemu_build_not_reached(); (TCGv)NULL; }) -# define cpu_tick_cmpr ({ qemu_build_not_reached(); (TCGv)NULL; }) +# define cpu_hintp ({ qemu_build_not_reached(); (TCGv)NULL; }) +# define cpu_hstick_cmpr ({ qemu_build_not_reached(); (TCGv)NULL; }) +# define cpu_htba ({ qemu_build_not_reached(); (TCGv)NULL; }) +# define cpu_hver ({ qemu_build_not_reached(); (TCGv)NULL; }) # define cpu_stick_cmpr ({ qemu_build_not_reached(); (TCGv)NULL; }) +# define cpu_tick_cmpr ({ qemu_build_not_reached(); (TCGv)NULL; }) #endif /* Floating point registers */ static TCGv_i64 cpu_fpr[TARGET_DPREGS]; @@ -272,15 +279,14 @@ static void gen_move_Q(DisasContext *dc, unsigned int rd, unsigned int rs) /* moves */ #ifdef CONFIG_USER_ONLY #define supervisor(dc) 0 -#ifdef TARGET_SPARC64 #define hypervisor(dc) 0 -#endif #else #ifdef TARGET_SPARC64 #define hypervisor(dc) (dc->hypervisor) #define supervisor(dc) (dc->supervisor | dc->hypervisor) #else #define supervisor(dc) (dc->supervisor) +#define hypervisor(dc) 0 #endif #endif @@ -3320,6 +3326,69 @@ static TCGv do_rdstrand_status(DisasContext *dc, TCGv dst) TRANS(RDSTRAND_STATUS, HYPV, do_rd_special, true, a->rd, do_rdstrand_status) +static TCGv do_rdpsr(DisasContext *dc, TCGv dst) +{ + update_psr(dc); + gen_helper_rdpsr(dst, tcg_env); + return dst; +} + +TRANS(RDPSR, 32, do_rd_special, supervisor(dc), a->rd, do_rdpsr) + +static TCGv do_rdhpstate(DisasContext *dc, TCGv dst) +{ + tcg_gen_ld_tl(dst, tcg_env, env64_field_offsetof(hpstate)); + return dst; +} + +TRANS(RDHPR_hpstate, HYPV, do_rd_special, hypervisor(dc), a->rd, do_rdhpstate) + +static TCGv do_rdhtstate(DisasContext *dc, TCGv dst) +{ + TCGv_i32 tl = tcg_temp_new_i32(); + TCGv_ptr tp = tcg_temp_new_ptr(); + + tcg_gen_ld_i32(tl, tcg_env, env64_field_offsetof(tl)); + tcg_gen_andi_i32(tl, tl, MAXTL_MASK); + tcg_gen_shli_i32(tl, tl, 3); + tcg_gen_ext_i32_ptr(tp, tl); + tcg_gen_add_ptr(tp, tp, tcg_env); + + tcg_gen_ld_tl(dst, tp, env64_field_offsetof(htstate)); + return dst; +} + +TRANS(RDHPR_htstate, HYPV, do_rd_special, hypervisor(dc), a->rd, do_rdhtstate) + +static TCGv do_rdhintp(DisasContext *dc, TCGv dst) +{ + return cpu_hintp; +} + +TRANS(RDHPR_hintp, HYPV, do_rd_special, hypervisor(dc), a->rd, do_rdhintp) + +static TCGv do_rdhtba(DisasContext *dc, TCGv dst) +{ + return cpu_htba; +} + +TRANS(RDHPR_htba, HYPV, do_rd_special, hypervisor(dc), a->rd, do_rdhtba) + +static TCGv do_rdhver(DisasContext *dc, TCGv dst) +{ + return cpu_hver; +} + +TRANS(RDHPR_hver, HYPV, do_rd_special, hypervisor(dc), a->rd, do_rdhver) + +static TCGv do_rdhstick_cmpr(DisasContext *dc, TCGv dst) +{ + return cpu_hstick_cmpr; +} + +TRANS(RDHPR_hstick_cmpr, HYPV, do_rd_special, hypervisor(dc), a->rd, + do_rdhstick_cmpr) + #define CHECK_IU_FEATURE(dc, FEATURE) \ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ goto illegal_insn; @@ -3351,45 +3420,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) TCGv cpu_tmp0 __attribute__((unused)); #if !defined(CONFIG_USER_ONLY) - if (xop == 0x29) { /* rdpsr / UA2005 rdhpr */ -#ifndef TARGET_SPARC64 - if (!supervisor(dc)) { - goto priv_insn; - } - update_psr(dc); - gen_helper_rdpsr(cpu_dst, tcg_env); -#else - CHECK_IU_FEATURE(dc, HYPV); - if (!hypervisor(dc)) - goto priv_insn; - rs1 = GET_FIELD(insn, 13, 17); - switch (rs1) { - case 0: // hpstate - tcg_gen_ld_i64(cpu_dst, tcg_env, - offsetof(CPUSPARCState, hpstate)); - break; - case 1: // htstate - // gen_op_rdhtstate(); - break; - case 3: // hintp - tcg_gen_mov_tl(cpu_dst, cpu_hintp); - break; - case 5: // htba - tcg_gen_mov_tl(cpu_dst, cpu_htba); - break; - case 6: // hver - tcg_gen_mov_tl(cpu_dst, cpu_hver); - break; - case 31: // hstick_cmpr - tcg_gen_mov_tl(cpu_dst, cpu_hstick_cmpr); - break; - default: - goto illegal_insn; - } -#endif - gen_store_gpr(dc, rd, cpu_dst); - break; - } if (xop == 0x2a) { /* rdwim / V9 rdpr */ if (!supervisor(dc)) { goto priv_insn; From 5d617bfba070b6c771ed7a0cdfb238bfe7bd1a33 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 1 Oct 2023 21:21:43 -0700 Subject: [PATCH 038/974] target/sparc: Move RDWIM, RDPR to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 22 +++ target/sparc/translate.c | 320 ++++++++++++++++++++++---------------- 2 files changed, 204 insertions(+), 138 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index a7d78eb6c6..7d91a7bc83 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -54,6 +54,28 @@ RDHPR_htba 10 rd:5 101001 00101 0 0000000000000 RDHPR_hver 10 rd:5 101001 00110 0 0000000000000 RDHPR_hstick_cmpr 10 rd:5 101001 11111 0 0000000000000 +{ + RDWIM 10 rd:5 101010 00000 0 0000000000000 + RDPR_tpc 10 rd:5 101010 00000 0 0000000000000 +} +RDPR_tnpc 10 rd:5 101010 00001 0 0000000000000 +RDPR_tstate 10 rd:5 101010 00010 0 0000000000000 +RDPR_tt 10 rd:5 101010 00011 0 0000000000000 +RDPR_tick 10 rd:5 101010 00100 0 0000000000000 +RDPR_tba 10 rd:5 101010 00101 0 0000000000000 +RDPR_pstate 10 rd:5 101010 00110 0 0000000000000 +RDPR_tl 10 rd:5 101010 00111 0 0000000000000 +RDPR_pil 10 rd:5 101010 01000 0 0000000000000 +RDPR_cwp 10 rd:5 101010 01001 0 0000000000000 +RDPR_cansave 10 rd:5 101010 01010 0 0000000000000 +RDPR_canrestore 10 rd:5 101010 01011 0 0000000000000 +RDPR_cleanwin 10 rd:5 101010 01100 0 0000000000000 +RDPR_otherwin 10 rd:5 101010 01101 0 0000000000000 +RDPR_wstate 10 rd:5 101010 01110 0 0000000000000 +RDPR_gl 10 rd:5 101010 10000 0 0000000000000 +RDPR_strand_status 10 rd:5 101010 11010 0 0000000000000 +RDPR_ver 10 rd:5 101010 11111 0 0000000000000 + Tcc_r 10 0 cond:4 111010 rs1:5 0 cc:1 0000000 rs2:5 { # For v7, the entire simm13 field is present, but masked to 7 bits. diff --git a/target/sparc/translate.c b/target/sparc/translate.c index b58c2594ca..6e415d7070 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -40,6 +40,7 @@ # define gen_helper_rdpsr(D, E) qemu_build_not_reached() #else # define gen_helper_rdccr(D, E) qemu_build_not_reached() +# define gen_helper_rdcwp(D, E) qemu_build_not_reached() # define gen_helper_tick_get_count(D, E, T, C) qemu_build_not_reached() # define MAXTL_MASK 0 #endif @@ -61,15 +62,14 @@ static TCGv_i32 cpu_psr; static TCGv cpu_fsr, cpu_pc, cpu_npc; static TCGv cpu_regs[32]; static TCGv cpu_y; -#ifndef CONFIG_USER_ONLY static TCGv cpu_tbr; -#endif static TCGv cpu_cond; #ifdef TARGET_SPARC64 static TCGv_i32 cpu_xcc, cpu_fprs; static TCGv cpu_gsr; static TCGv cpu_tick_cmpr, cpu_stick_cmpr, cpu_hstick_cmpr; static TCGv cpu_hintp, cpu_htba, cpu_hver, cpu_ssr, cpu_ver; +# define cpu_wim ({ qemu_build_not_reached(); (TCGv)NULL; }) #else static TCGv cpu_wim; # define cpu_fprs ({ qemu_build_not_reached(); (TCGv)NULL; }) @@ -78,8 +78,10 @@ static TCGv cpu_wim; # define cpu_hstick_cmpr ({ qemu_build_not_reached(); (TCGv)NULL; }) # define cpu_htba ({ qemu_build_not_reached(); (TCGv)NULL; }) # define cpu_hver ({ qemu_build_not_reached(); (TCGv)NULL; }) +# define cpu_ssr ({ qemu_build_not_reached(); (TCGv)NULL; }) # define cpu_stick_cmpr ({ qemu_build_not_reached(); (TCGv)NULL; }) # define cpu_tick_cmpr ({ qemu_build_not_reached(); (TCGv)NULL; }) +# define cpu_ver ({ qemu_build_not_reached(); (TCGv)NULL; }) #endif /* Floating point registers */ static TCGv_i64 cpu_fpr[TARGET_DPREGS]; @@ -2709,8 +2711,7 @@ static void gen_fmovq(DisasContext *dc, DisasCompare *cmp, int rd, int rs) gen_update_fprs_dirty(dc, qd); } -#ifndef CONFIG_USER_ONLY -static void gen_load_trap_state_at_tl(TCGv_ptr r_tsptr, TCGv_env tcg_env) +static void gen_load_trap_state_at_tl(TCGv_ptr r_tsptr) { TCGv_i32 r_tl = tcg_temp_new_i32(); @@ -2731,7 +2732,6 @@ static void gen_load_trap_state_at_tl(TCGv_ptr r_tsptr, TCGv_env tcg_env) tcg_gen_add_ptr(r_tsptr, r_tsptr, r_tl_tmp); } } -#endif static void gen_edge(DisasContext *dc, TCGv dst, TCGv s1, TCGv s2, int width, bool cc, bool left) @@ -2866,11 +2866,13 @@ static void gen_faligndata(TCGv dst, TCGv gsr, TCGv s1, TCGv s2) # define avail_32(C) false # define avail_ASR17(C) false # define avail_64(C) true +# define avail_GL(C) ((C)->def->features & CPU_FEATURE_GL) # define avail_HYPV(C) ((C)->def->features & CPU_FEATURE_HYPV) #else # define avail_32(C) true # define avail_ASR17(C) ((C)->def->features & CPU_FEATURE_ASR17) # define avail_64(C) false +# define avail_GL(C) false # define avail_HYPV(C) false #endif @@ -3389,6 +3391,177 @@ static TCGv do_rdhstick_cmpr(DisasContext *dc, TCGv dst) TRANS(RDHPR_hstick_cmpr, HYPV, do_rd_special, hypervisor(dc), a->rd, do_rdhstick_cmpr) +static TCGv do_rdwim(DisasContext *dc, TCGv dst) +{ + return cpu_wim; +} + +TRANS(RDWIM, 32, do_rd_special, supervisor(dc), a->rd, do_rdwim) + +static TCGv do_rdtpc(DisasContext *dc, TCGv dst) +{ +#ifdef TARGET_SPARC64 + TCGv_ptr r_tsptr = tcg_temp_new_ptr(); + + gen_load_trap_state_at_tl(r_tsptr); + tcg_gen_ld_tl(dst, r_tsptr, offsetof(trap_state, tpc)); + return dst; +#else + qemu_build_not_reached(); +#endif +} + +TRANS(RDPR_tpc, 64, do_rd_special, supervisor(dc), a->rd, do_rdtpc) + +static TCGv do_rdtnpc(DisasContext *dc, TCGv dst) +{ +#ifdef TARGET_SPARC64 + TCGv_ptr r_tsptr = tcg_temp_new_ptr(); + + gen_load_trap_state_at_tl(r_tsptr); + tcg_gen_ld_tl(dst, r_tsptr, offsetof(trap_state, tnpc)); + return dst; +#else + qemu_build_not_reached(); +#endif +} + +TRANS(RDPR_tnpc, 64, do_rd_special, supervisor(dc), a->rd, do_rdtnpc) + +static TCGv do_rdtstate(DisasContext *dc, TCGv dst) +{ +#ifdef TARGET_SPARC64 + TCGv_ptr r_tsptr = tcg_temp_new_ptr(); + + gen_load_trap_state_at_tl(r_tsptr); + tcg_gen_ld_tl(dst, r_tsptr, offsetof(trap_state, tstate)); + return dst; +#else + qemu_build_not_reached(); +#endif +} + +TRANS(RDPR_tstate, 64, do_rd_special, supervisor(dc), a->rd, do_rdtstate) + +static TCGv do_rdtt(DisasContext *dc, TCGv dst) +{ +#ifdef TARGET_SPARC64 + TCGv_ptr r_tsptr = tcg_temp_new_ptr(); + + gen_load_trap_state_at_tl(r_tsptr); + tcg_gen_ld32s_tl(dst, r_tsptr, offsetof(trap_state, tt)); + return dst; +#else + qemu_build_not_reached(); +#endif +} + +TRANS(RDPR_tt, 64, do_rd_special, supervisor(dc), a->rd, do_rdtt) +TRANS(RDPR_tick, 64, do_rd_special, supervisor(dc), a->rd, do_rdtick) + +static TCGv do_rdtba(DisasContext *dc, TCGv dst) +{ + return cpu_tbr; +} + +TRANS(RDPR_tba, 64, do_rd_special, supervisor(dc), a->rd, do_rdtba) + +static TCGv do_rdpstate(DisasContext *dc, TCGv dst) +{ + tcg_gen_ld32s_tl(dst, tcg_env, env64_field_offsetof(pstate)); + return dst; +} + +TRANS(RDPR_pstate, 64, do_rd_special, supervisor(dc), a->rd, do_rdpstate) + +static TCGv do_rdtl(DisasContext *dc, TCGv dst) +{ + tcg_gen_ld32s_tl(dst, tcg_env, env64_field_offsetof(tl)); + return dst; +} + +TRANS(RDPR_tl, 64, do_rd_special, supervisor(dc), a->rd, do_rdtl) + +static TCGv do_rdpil(DisasContext *dc, TCGv dst) +{ + tcg_gen_ld32s_tl(dst, tcg_env, env_field_offsetof(psrpil)); + return dst; +} + +TRANS(RDPR_pil, 64, do_rd_special, supervisor(dc), a->rd, do_rdpil) + +static TCGv do_rdcwp(DisasContext *dc, TCGv dst) +{ + gen_helper_rdcwp(dst, tcg_env); + return dst; +} + +TRANS(RDPR_cwp, 64, do_rd_special, supervisor(dc), a->rd, do_rdcwp) + +static TCGv do_rdcansave(DisasContext *dc, TCGv dst) +{ + tcg_gen_ld32s_tl(dst, tcg_env, env64_field_offsetof(cansave)); + return dst; +} + +TRANS(RDPR_cansave, 64, do_rd_special, supervisor(dc), a->rd, do_rdcansave) + +static TCGv do_rdcanrestore(DisasContext *dc, TCGv dst) +{ + tcg_gen_ld32s_tl(dst, tcg_env, env64_field_offsetof(canrestore)); + return dst; +} + +TRANS(RDPR_canrestore, 64, do_rd_special, supervisor(dc), a->rd, + do_rdcanrestore) + +static TCGv do_rdcleanwin(DisasContext *dc, TCGv dst) +{ + tcg_gen_ld32s_tl(dst, tcg_env, env64_field_offsetof(cleanwin)); + return dst; +} + +TRANS(RDPR_cleanwin, 64, do_rd_special, supervisor(dc), a->rd, do_rdcleanwin) + +static TCGv do_rdotherwin(DisasContext *dc, TCGv dst) +{ + tcg_gen_ld32s_tl(dst, tcg_env, env64_field_offsetof(otherwin)); + return dst; +} + +TRANS(RDPR_otherwin, 64, do_rd_special, supervisor(dc), a->rd, do_rdotherwin) + +static TCGv do_rdwstate(DisasContext *dc, TCGv dst) +{ + tcg_gen_ld32s_tl(dst, tcg_env, env64_field_offsetof(wstate)); + return dst; +} + +TRANS(RDPR_wstate, 64, do_rd_special, supervisor(dc), a->rd, do_rdwstate) + +static TCGv do_rdgl(DisasContext *dc, TCGv dst) +{ + tcg_gen_ld32s_tl(dst, tcg_env, env64_field_offsetof(gl)); + return dst; +} + +TRANS(RDPR_gl, GL, do_rd_special, supervisor(dc), a->rd, do_rdgl) + +/* UA2005 strand status */ +static TCGv do_rdssr(DisasContext *dc, TCGv dst) +{ + return cpu_ssr; +} + +TRANS(RDPR_strand_status, HYPV, do_rd_special, hypervisor(dc), a->rd, do_rdssr) + +static TCGv do_rdver(DisasContext *dc, TCGv dst) +{ + return cpu_ver; +} + +TRANS(RDPR_ver, 64, do_rd_special, supervisor(dc), a->rd, do_rdver) + #define CHECK_IU_FEATURE(dc, FEATURE) \ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ goto illegal_insn; @@ -3419,133 +3592,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) TCGv cpu_dst __attribute__((unused)) = tcg_temp_new(); TCGv cpu_tmp0 __attribute__((unused)); -#if !defined(CONFIG_USER_ONLY) - if (xop == 0x2a) { /* rdwim / V9 rdpr */ - if (!supervisor(dc)) { - goto priv_insn; - } - cpu_tmp0 = tcg_temp_new(); -#ifdef TARGET_SPARC64 - rs1 = GET_FIELD(insn, 13, 17); - switch (rs1) { - case 0: // tpc - { - TCGv_ptr r_tsptr; - - r_tsptr = tcg_temp_new_ptr(); - gen_load_trap_state_at_tl(r_tsptr, tcg_env); - tcg_gen_ld_tl(cpu_tmp0, r_tsptr, - offsetof(trap_state, tpc)); - } - break; - case 1: // tnpc - { - TCGv_ptr r_tsptr; - - r_tsptr = tcg_temp_new_ptr(); - gen_load_trap_state_at_tl(r_tsptr, tcg_env); - tcg_gen_ld_tl(cpu_tmp0, r_tsptr, - offsetof(trap_state, tnpc)); - } - break; - case 2: // tstate - { - TCGv_ptr r_tsptr; - - r_tsptr = tcg_temp_new_ptr(); - gen_load_trap_state_at_tl(r_tsptr, tcg_env); - tcg_gen_ld_tl(cpu_tmp0, r_tsptr, - offsetof(trap_state, tstate)); - } - break; - case 3: // tt - { - TCGv_ptr r_tsptr = tcg_temp_new_ptr(); - - gen_load_trap_state_at_tl(r_tsptr, tcg_env); - tcg_gen_ld32s_tl(cpu_tmp0, r_tsptr, - offsetof(trap_state, tt)); - } - break; - case 4: // tick - { - TCGv_ptr r_tickptr; - TCGv_i32 r_const; - - r_tickptr = tcg_temp_new_ptr(); - r_const = tcg_constant_i32(dc->mem_idx); - tcg_gen_ld_ptr(r_tickptr, tcg_env, - offsetof(CPUSPARCState, tick)); - if (translator_io_start(&dc->base)) { - dc->base.is_jmp = DISAS_EXIT; - } - gen_helper_tick_get_count(cpu_tmp0, tcg_env, - r_tickptr, r_const); - } - break; - case 5: // tba - tcg_gen_mov_tl(cpu_tmp0, cpu_tbr); - break; - case 6: // pstate - tcg_gen_ld32s_tl(cpu_tmp0, tcg_env, - offsetof(CPUSPARCState, pstate)); - break; - case 7: // tl - tcg_gen_ld32s_tl(cpu_tmp0, tcg_env, - offsetof(CPUSPARCState, tl)); - break; - case 8: // pil - tcg_gen_ld32s_tl(cpu_tmp0, tcg_env, - offsetof(CPUSPARCState, psrpil)); - break; - case 9: // cwp - gen_helper_rdcwp(cpu_tmp0, tcg_env); - break; - case 10: // cansave - tcg_gen_ld32s_tl(cpu_tmp0, tcg_env, - offsetof(CPUSPARCState, cansave)); - break; - case 11: // canrestore - tcg_gen_ld32s_tl(cpu_tmp0, tcg_env, - offsetof(CPUSPARCState, canrestore)); - break; - case 12: // cleanwin - tcg_gen_ld32s_tl(cpu_tmp0, tcg_env, - offsetof(CPUSPARCState, cleanwin)); - break; - case 13: // otherwin - tcg_gen_ld32s_tl(cpu_tmp0, tcg_env, - offsetof(CPUSPARCState, otherwin)); - break; - case 14: // wstate - tcg_gen_ld32s_tl(cpu_tmp0, tcg_env, - offsetof(CPUSPARCState, wstate)); - break; - case 16: // UA2005 gl - CHECK_IU_FEATURE(dc, GL); - tcg_gen_ld32s_tl(cpu_tmp0, tcg_env, - offsetof(CPUSPARCState, gl)); - break; - case 26: // UA2005 strand status - CHECK_IU_FEATURE(dc, HYPV); - if (!hypervisor(dc)) - goto priv_insn; - tcg_gen_mov_tl(cpu_tmp0, cpu_ssr); - break; - case 31: // ver - tcg_gen_mov_tl(cpu_tmp0, cpu_ver); - break; - case 15: // fq - default: - goto illegal_insn; - } -#else - tcg_gen_ext_i32_tl(cpu_tmp0, cpu_wim); -#endif - gen_store_gpr(dc, rd, cpu_tmp0); - break; - } -#endif #if defined(TARGET_SPARC64) || !defined(CONFIG_USER_ONLY) if (xop == 0x2b) { /* rdtbr / V9 flushw */ #ifdef TARGET_SPARC64 @@ -4391,7 +4437,7 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) TCGv_ptr r_tsptr; r_tsptr = tcg_temp_new_ptr(); - gen_load_trap_state_at_tl(r_tsptr, tcg_env); + gen_load_trap_state_at_tl(r_tsptr); tcg_gen_st_tl(cpu_tmp0, r_tsptr, offsetof(trap_state, tpc)); } @@ -4401,7 +4447,7 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) TCGv_ptr r_tsptr; r_tsptr = tcg_temp_new_ptr(); - gen_load_trap_state_at_tl(r_tsptr, tcg_env); + gen_load_trap_state_at_tl(r_tsptr); tcg_gen_st_tl(cpu_tmp0, r_tsptr, offsetof(trap_state, tnpc)); } @@ -4411,7 +4457,7 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) TCGv_ptr r_tsptr; r_tsptr = tcg_temp_new_ptr(); - gen_load_trap_state_at_tl(r_tsptr, tcg_env); + gen_load_trap_state_at_tl(r_tsptr); tcg_gen_st_tl(cpu_tmp0, r_tsptr, offsetof(trap_state, tstate)); @@ -4422,7 +4468,7 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) TCGv_ptr r_tsptr; r_tsptr = tcg_temp_new_ptr(); - gen_load_trap_state_at_tl(r_tsptr, tcg_env); + gen_load_trap_state_at_tl(r_tsptr); tcg_gen_st32_tl(cpu_tmp0, r_tsptr, offsetof(trap_state, tt)); } @@ -5851,9 +5897,7 @@ void sparc_tcg_init(void) { &cpu_pc, offsetof(CPUSPARCState, pc), "pc" }, { &cpu_npc, offsetof(CPUSPARCState, npc), "npc" }, { &cpu_y, offsetof(CPUSPARCState, y), "y" }, -#ifndef CONFIG_USER_ONLY { &cpu_tbr, offsetof(CPUSPARCState, tbr), "tbr" }, -#endif }; unsigned int i; From e8325dc02d059685331d8607d5e620328bac594e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 1 Oct 2023 21:29:10 -0700 Subject: [PATCH 039/974] target/sparc: Move RDTBR, FLUSHW to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 5 +++++ target/sparc/translate.c | 23 +++++++++++------------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 7d91a7bc83..0b6f4c9c38 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -76,6 +76,11 @@ RDPR_gl 10 rd:5 101010 10000 0 0000000000000 RDPR_strand_status 10 rd:5 101010 11010 0 0000000000000 RDPR_ver 10 rd:5 101010 11111 0 0000000000000 +{ + FLUSHW 10 00000 101011 00000 0 0000000000000 + RDTBR 10 rd:5 101011 00000 0 0000000000000 +} + Tcc_r 10 0 cond:4 111010 rs1:5 0 cc:1 0000000 rs2:5 { # For v7, the entire simm13 field is present, but masked to 7 bits. diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 6e415d7070..67d3292e68 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -39,6 +39,7 @@ #ifdef TARGET_SPARC64 # define gen_helper_rdpsr(D, E) qemu_build_not_reached() #else +# define gen_helper_flushw(E) qemu_build_not_reached() # define gen_helper_rdccr(D, E) qemu_build_not_reached() # define gen_helper_rdcwp(D, E) qemu_build_not_reached() # define gen_helper_tick_get_count(D, E, T, C) qemu_build_not_reached() @@ -3464,6 +3465,7 @@ static TCGv do_rdtba(DisasContext *dc, TCGv dst) return cpu_tbr; } +TRANS(RDTBR, 32, do_rd_special, supervisor(dc), a->rd, do_rdtba) TRANS(RDPR_tba, 64, do_rd_special, supervisor(dc), a->rd, do_rdtba) static TCGv do_rdpstate(DisasContext *dc, TCGv dst) @@ -3562,6 +3564,15 @@ static TCGv do_rdver(DisasContext *dc, TCGv dst) TRANS(RDPR_ver, 64, do_rd_special, supervisor(dc), a->rd, do_rdver) +static bool trans_FLUSHW(DisasContext *dc, arg_FLUSHW *a) +{ + if (avail_64(dc)) { + gen_helper_flushw(tcg_env); + return advance_pc(dc); + } + return false; +} + #define CHECK_IU_FEATURE(dc, FEATURE) \ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ goto illegal_insn; @@ -3592,18 +3603,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) TCGv cpu_dst __attribute__((unused)) = tcg_temp_new(); TCGv cpu_tmp0 __attribute__((unused)); -#if defined(TARGET_SPARC64) || !defined(CONFIG_USER_ONLY) - if (xop == 0x2b) { /* rdtbr / V9 flushw */ -#ifdef TARGET_SPARC64 - gen_helper_flushw(tcg_env); -#else - if (!supervisor(dc)) - goto priv_insn; - gen_store_gpr(dc, rd, cpu_tbr); -#endif - break; - } -#endif if (xop == 0x34) { /* FPU Operations */ if (gen_trap_ifnofpu(dc)) { goto jmp_insn; From 0faef01b3989bb1fb6e0ed73ffa909e77b79f780 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 2 Oct 2023 00:25:46 -0700 Subject: [PATCH 040/974] target/sparc: Move WRASR to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 31 ++++ target/sparc/translate.c | 341 +++++++++++++++++++++----------------- 2 files changed, 216 insertions(+), 156 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 0b6f4c9c38..118f0f1f17 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -22,6 +22,13 @@ SETHI 00 rd:5 100 i:22 CALL 01 i:s30 +## +## Major Opcode 10 -- integer, floating-point, vis, and system insns. +## + +&r_r_ri rd rs1 rs2_or_imm imm:bool +@n_r_ri .. ..... ...... rs1:5 imm:1 rs2_or_imm:s13 &r_r_ri rd=0 + { [ STBAR 10 00000 101000 01111 0 0000000000000 @@ -44,6 +51,30 @@ CALL 01 i:s30 RDY 10 rd:5 101000 rs1:5 0 0000000000000 } +{ + [ + WRY 10 00000 110000 ..... . ............. @n_r_ri + WRCCR 10 00010 110000 ..... . ............. @n_r_ri + WRASI 10 00011 110000 ..... . ............. @n_r_ri + WRFPRS 10 00110 110000 ..... . ............. @n_r_ri + { + WRGSR 10 10011 110000 ..... . ............. @n_r_ri + WRPOWERDOWN 10 10011 110000 ..... . ............. @n_r_ri + } + WRSOFTINT_SET 10 10100 110000 ..... . ............. @n_r_ri + WRSOFTINT_CLR 10 10101 110000 ..... . ............. @n_r_ri + WRSOFTINT 10 10110 110000 ..... . ............. @n_r_ri + WRTICK_CMPR 10 10111 110000 ..... . ............. @n_r_ri + WRSTICK 10 11000 110000 ..... . ............. @n_r_ri + WRSTICK_CMPR 10 11001 110000 ..... . ............. @n_r_ri + ] + # Before v8, rs1==0 was WRY, and the rest executed as nop. + [ + NOP_v7 10 ----- 110000 ----- 0 00000000 ----- + NOP_v7 10 ----- 110000 ----- 1 -------- ----- + ] +} + { RDPSR 10 rd:5 101001 00000 0 0000000000000 RDHPR_hpstate 10 rd:5 101001 00000 0 0000000000000 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 67d3292e68..83716307f6 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -38,11 +38,16 @@ #ifdef TARGET_SPARC64 # define gen_helper_rdpsr(D, E) qemu_build_not_reached() +# define gen_helper_power_down(E) qemu_build_not_reached() #else +# define gen_helper_clear_softint(E, S) qemu_build_not_reached() # define gen_helper_flushw(E) qemu_build_not_reached() # define gen_helper_rdccr(D, E) qemu_build_not_reached() # define gen_helper_rdcwp(D, E) qemu_build_not_reached() +# define gen_helper_set_softint(E, S) qemu_build_not_reached() # define gen_helper_tick_get_count(D, E, T, C) qemu_build_not_reached() +# define gen_helper_wrccr(E, S) qemu_build_not_reached() +# define gen_helper_write_softint(E, S) qemu_build_not_reached() # define MAXTL_MASK 0 #endif @@ -2866,12 +2871,14 @@ static void gen_faligndata(TCGv dst, TCGv gsr, TCGv s1, TCGv s2) #ifdef TARGET_SPARC64 # define avail_32(C) false # define avail_ASR17(C) false +# define avail_POWERDOWN(C) false # define avail_64(C) true # define avail_GL(C) ((C)->def->features & CPU_FEATURE_GL) # define avail_HYPV(C) ((C)->def->features & CPU_FEATURE_HYPV) #else # define avail_32(C) true # define avail_ASR17(C) ((C)->def->features & CPU_FEATURE_ASR17) +# define avail_POWERDOWN(C) ((C)->def->features & CPU_FEATURE_POWERDOWN) # define avail_64(C) false # define avail_GL(C) false # define avail_HYPV(C) false @@ -3077,6 +3084,10 @@ static bool trans_SETHI(DisasContext *dc, arg_SETHI *a) return advance_pc(dc); } +/* + * Major Opcode 10 -- integer, floating-point, vis, and system insns. + */ + static bool do_tcc(DisasContext *dc, int cond, int cc, int rs1, bool imm, int rs2_or_imm) { @@ -3573,6 +3584,179 @@ static bool trans_FLUSHW(DisasContext *dc, arg_FLUSHW *a) return false; } +static bool do_wr_special(DisasContext *dc, arg_r_r_ri *a, bool priv, + void (*func)(DisasContext *, TCGv)) +{ + TCGv src; + + /* For simplicity, we under-decoded the rs2 form. */ + if (!a->imm && (a->rs2_or_imm & ~0x1f)) { + return false; + } + if (!priv) { + return raise_priv(dc); + } + + if (a->rs1 == 0 && (a->imm || a->rs2_or_imm == 0)) { + src = tcg_constant_tl(a->rs2_or_imm); + } else { + TCGv src1 = gen_load_gpr(dc, a->rs1); + if (a->rs2_or_imm == 0) { + src = src1; + } else { + src = tcg_temp_new(); + if (a->imm) { + tcg_gen_xori_tl(src, src1, a->rs2_or_imm); + } else { + tcg_gen_xor_tl(src, src1, gen_load_gpr(dc, a->rs2_or_imm)); + } + } + } + func(dc, src); + return advance_pc(dc); +} + +static void do_wry(DisasContext *dc, TCGv src) +{ + tcg_gen_ext32u_tl(cpu_y, src); +} + +TRANS(WRY, ALL, do_wr_special, a, true, do_wry) + +static void do_wrccr(DisasContext *dc, TCGv src) +{ + gen_helper_wrccr(tcg_env, src); +} + +TRANS(WRCCR, 64, do_wr_special, a, true, do_wrccr) + +static void do_wrasi(DisasContext *dc, TCGv src) +{ + TCGv tmp = tcg_temp_new(); + + tcg_gen_ext8u_tl(tmp, src); + tcg_gen_st32_tl(tmp, tcg_env, env64_field_offsetof(asi)); + /* End TB to notice changed ASI. */ + dc->base.is_jmp = DISAS_EXIT; +} + +TRANS(WRASI, 64, do_wr_special, a, true, do_wrasi) + +static void do_wrfprs(DisasContext *dc, TCGv src) +{ +#ifdef TARGET_SPARC64 + tcg_gen_trunc_tl_i32(cpu_fprs, src); + dc->fprs_dirty = 0; + dc->base.is_jmp = DISAS_EXIT; +#else + qemu_build_not_reached(); +#endif +} + +TRANS(WRFPRS, 64, do_wr_special, a, true, do_wrfprs) + +static void do_wrgsr(DisasContext *dc, TCGv src) +{ + gen_trap_ifnofpu(dc); + tcg_gen_mov_tl(cpu_gsr, src); +} + +TRANS(WRGSR, 64, do_wr_special, a, true, do_wrgsr) + +static void do_wrsoftint_set(DisasContext *dc, TCGv src) +{ + gen_helper_set_softint(tcg_env, src); +} + +TRANS(WRSOFTINT_SET, 64, do_wr_special, a, supervisor(dc), do_wrsoftint_set) + +static void do_wrsoftint_clr(DisasContext *dc, TCGv src) +{ + gen_helper_clear_softint(tcg_env, src); +} + +TRANS(WRSOFTINT_CLR, 64, do_wr_special, a, supervisor(dc), do_wrsoftint_clr) + +static void do_wrsoftint(DisasContext *dc, TCGv src) +{ + gen_helper_write_softint(tcg_env, src); +} + +TRANS(WRSOFTINT, 64, do_wr_special, a, supervisor(dc), do_wrsoftint) + +static void do_wrtick_cmpr(DisasContext *dc, TCGv src) +{ +#ifdef TARGET_SPARC64 + TCGv_ptr r_tickptr = tcg_temp_new_ptr(); + + tcg_gen_mov_tl(cpu_tick_cmpr, src); + tcg_gen_ld_ptr(r_tickptr, tcg_env, offsetof(CPUSPARCState, tick)); + translator_io_start(&dc->base); + gen_helper_tick_set_limit(r_tickptr, cpu_tick_cmpr); + /* End TB to handle timer interrupt */ + dc->base.is_jmp = DISAS_EXIT; +#else + qemu_build_not_reached(); +#endif +} + +TRANS(WRTICK_CMPR, 64, do_wr_special, a, supervisor(dc), do_wrtick_cmpr) + +static void do_wrstick(DisasContext *dc, TCGv src) +{ +#ifdef TARGET_SPARC64 + TCGv_ptr r_tickptr = tcg_temp_new_ptr(); + + tcg_gen_ld_ptr(r_tickptr, tcg_env, offsetof(CPUSPARCState, stick)); + translator_io_start(&dc->base); + gen_helper_tick_set_count(r_tickptr, src); + /* End TB to handle timer interrupt */ + dc->base.is_jmp = DISAS_EXIT; +#else + qemu_build_not_reached(); +#endif +} + +TRANS(WRSTICK, 64, do_wr_special, a, supervisor(dc), do_wrstick) + +static void do_wrstick_cmpr(DisasContext *dc, TCGv src) +{ +#ifdef TARGET_SPARC64 + TCGv_ptr r_tickptr = tcg_temp_new_ptr(); + + tcg_gen_mov_tl(cpu_stick_cmpr, src); + tcg_gen_ld_ptr(r_tickptr, tcg_env, offsetof(CPUSPARCState, stick)); + translator_io_start(&dc->base); + gen_helper_tick_set_limit(r_tickptr, cpu_stick_cmpr); + /* End TB to handle timer interrupt */ + dc->base.is_jmp = DISAS_EXIT; +#else + qemu_build_not_reached(); +#endif +} + +TRANS(WRSTICK_CMPR, 64, do_wr_special, a, supervisor(dc), do_wrstick_cmpr) + +static void do_wrpowerdown(DisasContext *dc, TCGv src) +{ + save_state(dc); + gen_helper_power_down(tcg_env); +} + +TRANS(WRPOWERDOWN, POWERDOWN, do_wr_special, a, supervisor(dc), do_wrpowerdown) + +static bool trans_NOP_v7(DisasContext *dc, arg_NOP_v7 *a) +{ + /* + * TODO: Need a feature bit for sparcv8. + * In the meantime, treat all 32-bit cpus like sparcv7. + */ + if (avail_32(dc)) { + return advance_pc(dc); + } + return false; +} + #define CHECK_IU_FEATURE(dc, FEATURE) \ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ goto illegal_insn; @@ -4233,162 +4417,7 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) break; #endif case 0x30: - { - cpu_tmp0 = tcg_temp_new(); - switch(rd) { - case 0: /* wry */ - tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2); - tcg_gen_andi_tl(cpu_y, cpu_tmp0, 0xffffffff); - break; -#ifndef TARGET_SPARC64 - case 0x01 ... 0x0f: /* undefined in the - SPARCv8 manual, nop - on the microSPARC - II */ - case 0x10 ... 0x1f: /* implementation-dependent - in the SPARCv8 - manual, nop on the - microSPARC II */ - if ((rd == 0x13) && (dc->def->features & - CPU_FEATURE_POWERDOWN)) { - /* LEON3 power-down */ - save_state(dc); - gen_helper_power_down(tcg_env); - } - break; -#else - case 0x2: /* V9 wrccr */ - tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2); - gen_helper_wrccr(tcg_env, cpu_tmp0); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_FLAGS); - dc->cc_op = CC_OP_FLAGS; - break; - case 0x3: /* V9 wrasi */ - tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2); - tcg_gen_andi_tl(cpu_tmp0, cpu_tmp0, 0xff); - tcg_gen_st32_tl(cpu_tmp0, tcg_env, - offsetof(CPUSPARCState, asi)); - /* - * End TB to notice changed ASI. - * TODO: Could notice src1 = %g0 and IS_IMM, - * update DisasContext and not exit the TB. - */ - save_state(dc); - gen_op_next_insn(); - tcg_gen_lookup_and_goto_ptr(); - dc->base.is_jmp = DISAS_NORETURN; - break; - case 0x6: /* V9 wrfprs */ - tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2); - tcg_gen_trunc_tl_i32(cpu_fprs, cpu_tmp0); - dc->fprs_dirty = 0; - save_state(dc); - gen_op_next_insn(); - tcg_gen_exit_tb(NULL, 0); - dc->base.is_jmp = DISAS_NORETURN; - break; - case 0xf: /* V9 sir, nop if user */ -#if !defined(CONFIG_USER_ONLY) - if (supervisor(dc)) { - ; // XXX - } -#endif - break; - case 0x13: /* Graphics Status */ - if (gen_trap_ifnofpu(dc)) { - goto jmp_insn; - } - tcg_gen_xor_tl(cpu_gsr, cpu_src1, cpu_src2); - break; - case 0x14: /* Softint set */ - if (!supervisor(dc)) - goto illegal_insn; - tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2); - gen_helper_set_softint(tcg_env, cpu_tmp0); - break; - case 0x15: /* Softint clear */ - if (!supervisor(dc)) - goto illegal_insn; - tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2); - gen_helper_clear_softint(tcg_env, cpu_tmp0); - break; - case 0x16: /* Softint write */ - if (!supervisor(dc)) - goto illegal_insn; - tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2); - gen_helper_write_softint(tcg_env, cpu_tmp0); - break; - case 0x17: /* Tick compare */ -#if !defined(CONFIG_USER_ONLY) - if (!supervisor(dc)) - goto illegal_insn; -#endif - { - TCGv_ptr r_tickptr; - - tcg_gen_xor_tl(cpu_tick_cmpr, cpu_src1, - cpu_src2); - r_tickptr = tcg_temp_new_ptr(); - tcg_gen_ld_ptr(r_tickptr, tcg_env, - offsetof(CPUSPARCState, tick)); - translator_io_start(&dc->base); - gen_helper_tick_set_limit(r_tickptr, - cpu_tick_cmpr); - /* End TB to handle timer interrupt */ - dc->base.is_jmp = DISAS_EXIT; - } - break; - case 0x18: /* System tick */ -#if !defined(CONFIG_USER_ONLY) - if (!supervisor(dc)) - goto illegal_insn; -#endif - { - TCGv_ptr r_tickptr; - - tcg_gen_xor_tl(cpu_tmp0, cpu_src1, - cpu_src2); - r_tickptr = tcg_temp_new_ptr(); - tcg_gen_ld_ptr(r_tickptr, tcg_env, - offsetof(CPUSPARCState, stick)); - translator_io_start(&dc->base); - gen_helper_tick_set_count(r_tickptr, - cpu_tmp0); - /* End TB to handle timer interrupt */ - dc->base.is_jmp = DISAS_EXIT; - } - break; - case 0x19: /* System tick compare */ -#if !defined(CONFIG_USER_ONLY) - if (!supervisor(dc)) - goto illegal_insn; -#endif - { - TCGv_ptr r_tickptr; - - tcg_gen_xor_tl(cpu_stick_cmpr, cpu_src1, - cpu_src2); - r_tickptr = tcg_temp_new_ptr(); - tcg_gen_ld_ptr(r_tickptr, tcg_env, - offsetof(CPUSPARCState, stick)); - translator_io_start(&dc->base); - gen_helper_tick_set_limit(r_tickptr, - cpu_stick_cmpr); - /* End TB to handle timer interrupt */ - dc->base.is_jmp = DISAS_EXIT; - } - break; - - case 0x10: /* Performance Control */ - case 0x11: /* Performance Instrumentation - Counter */ - case 0x12: /* Dispatch Control */ -#endif - default: - goto illegal_insn; - } - } - break; + goto illegal_insn; /* WRASR in decodetree */ #if !defined(CONFIG_USER_ONLY) case 0x31: /* wrpsr, V9 saved, restored */ { From 25524734c62b276d040f8790e64048717955845b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 2 Oct 2023 07:38:42 -0700 Subject: [PATCH 041/974] target/sparc: Move WRPSR, SAVED, RESTORED to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 10 +++++++ target/sparc/translate.c | 62 ++++++++++++++++++--------------------- 2 files changed, 39 insertions(+), 33 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 118f0f1f17..45cf47a32f 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -85,6 +85,16 @@ RDHPR_htba 10 rd:5 101001 00101 0 0000000000000 RDHPR_hver 10 rd:5 101001 00110 0 0000000000000 RDHPR_hstick_cmpr 10 rd:5 101001 11111 0 0000000000000 +{ + WRPSR 10 00000 110001 ..... . ............. @n_r_ri + SAVED 10 00000 110001 00000 0 0000000000000 +} +RESTORED 10 00001 110001 00000 0 0000000000000 +# UA2005 ALLCLEAN +# UA2005 OTHERW +# UA2005 NORMALW +# UA2005 INVALW + { RDWIM 10 rd:5 101010 00000 0 0000000000000 RDPR_tpc 10 rd:5 101010 00000 0 0000000000000 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 83716307f6..58ea3b2ba7 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -39,11 +39,14 @@ #ifdef TARGET_SPARC64 # define gen_helper_rdpsr(D, E) qemu_build_not_reached() # define gen_helper_power_down(E) qemu_build_not_reached() +# define gen_helper_wrpsr(E, S) qemu_build_not_reached() #else # define gen_helper_clear_softint(E, S) qemu_build_not_reached() # define gen_helper_flushw(E) qemu_build_not_reached() # define gen_helper_rdccr(D, E) qemu_build_not_reached() # define gen_helper_rdcwp(D, E) qemu_build_not_reached() +# define gen_helper_restored(E) qemu_build_not_reached() +# define gen_helper_saved(E) qemu_build_not_reached() # define gen_helper_set_softint(E, S) qemu_build_not_reached() # define gen_helper_tick_get_count(D, E, T, C) qemu_build_not_reached() # define gen_helper_wrccr(E, S) qemu_build_not_reached() @@ -3745,6 +3748,32 @@ static void do_wrpowerdown(DisasContext *dc, TCGv src) TRANS(WRPOWERDOWN, POWERDOWN, do_wr_special, a, supervisor(dc), do_wrpowerdown) +static void do_wrpsr(DisasContext *dc, TCGv src) +{ + gen_helper_wrpsr(tcg_env, src); + tcg_gen_movi_i32(cpu_cc_op, CC_OP_FLAGS); + dc->cc_op = CC_OP_FLAGS; + dc->base.is_jmp = DISAS_EXIT; +} + +TRANS(WRPSR, 32, do_wr_special, a, supervisor(dc), do_wrpsr) + +static bool do_saved_restored(DisasContext *dc, bool saved) +{ + if (!supervisor(dc)) { + return raise_priv(dc); + } + if (saved) { + gen_helper_saved(tcg_env); + } else { + gen_helper_restored(tcg_env); + } + return advance_pc(dc); +} + +TRANS(SAVED, 64, do_saved_restored, true) +TRANS(RESTORED, 64, do_saved_restored, false) + static bool trans_NOP_v7(DisasContext *dc, arg_NOP_v7 *a) { /* @@ -4419,39 +4448,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x30: goto illegal_insn; /* WRASR in decodetree */ #if !defined(CONFIG_USER_ONLY) - case 0x31: /* wrpsr, V9 saved, restored */ - { - if (!supervisor(dc)) - goto priv_insn; -#ifdef TARGET_SPARC64 - switch (rd) { - case 0: - gen_helper_saved(tcg_env); - break; - case 1: - gen_helper_restored(tcg_env); - break; - case 2: /* UA2005 allclean */ - case 3: /* UA2005 otherw */ - case 4: /* UA2005 normalw */ - case 5: /* UA2005 invalw */ - // XXX - default: - goto illegal_insn; - } -#else - cpu_tmp0 = tcg_temp_new(); - tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2); - gen_helper_wrpsr(tcg_env, cpu_tmp0); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_FLAGS); - dc->cc_op = CC_OP_FLAGS; - save_state(dc); - gen_op_next_insn(); - tcg_gen_exit_tb(NULL, 0); - dc->base.is_jmp = DISAS_NORETURN; -#endif - } - break; case 0x32: /* wrwim, V9 wrpr */ { if (!supervisor(dc)) From 9422278ef8651e17c1a02909ac00564059177c90 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 2 Oct 2023 13:31:20 -0700 Subject: [PATCH 042/974] target/sparc: Move WRWIM, WRPR to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 21 +++ target/sparc/translate.c | 315 ++++++++++++++++++++++---------------- 2 files changed, 200 insertions(+), 136 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 45cf47a32f..f478999ee1 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -117,6 +117,27 @@ RDPR_gl 10 rd:5 101010 10000 0 0000000000000 RDPR_strand_status 10 rd:5 101010 11010 0 0000000000000 RDPR_ver 10 rd:5 101010 11111 0 0000000000000 +{ + WRWIM 10 00000 110010 ..... . ............. @n_r_ri + WRPR_tpc 10 00000 110010 ..... . ............. @n_r_ri +} +WRPR_tnpc 10 00001 110010 ..... . ............. @n_r_ri +WRPR_tstate 10 00010 110010 ..... . ............. @n_r_ri +WRPR_tt 10 00011 110010 ..... . ............. @n_r_ri +WRPR_tick 10 00100 110010 ..... . ............. @n_r_ri +WRPR_tba 10 00101 110010 ..... . ............. @n_r_ri +WRPR_pstate 10 00110 110010 ..... . ............. @n_r_ri +WRPR_tl 10 00111 110010 ..... . ............. @n_r_ri +WRPR_pil 10 01000 110010 ..... . ............. @n_r_ri +WRPR_cwp 10 01001 110010 ..... . ............. @n_r_ri +WRPR_cansave 10 01010 110010 ..... . ............. @n_r_ri +WRPR_canrestore 10 01011 110010 ..... . ............. @n_r_ri +WRPR_cleanwin 10 01100 110010 ..... . ............. @n_r_ri +WRPR_otherwin 10 01101 110010 ..... . ............. @n_r_ri +WRPR_wstate 10 01110 110010 ..... . ............. @n_r_ri +WRPR_gl 10 10000 110010 ..... . ............. @n_r_ri +WRPR_strand_status 10 11010 110010 ..... . ............. @n_r_ri + { FLUSHW 10 00000 101011 00000 0 0000000000000 RDTBR 10 rd:5 101011 00000 0 0000000000000 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 58ea3b2ba7..9b82e2bf6f 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -49,8 +49,13 @@ # define gen_helper_saved(E) qemu_build_not_reached() # define gen_helper_set_softint(E, S) qemu_build_not_reached() # define gen_helper_tick_get_count(D, E, T, C) qemu_build_not_reached() +# define gen_helper_tick_set_count(P, S) qemu_build_not_reached() # define gen_helper_wrccr(E, S) qemu_build_not_reached() +# define gen_helper_wrcwp(E, S) qemu_build_not_reached() +# define gen_helper_wrgl(E, S) qemu_build_not_reached() # define gen_helper_write_softint(E, S) qemu_build_not_reached() +# define gen_helper_wrpil(E, S) qemu_build_not_reached() +# define gen_helper_wrpstate(E, S) qemu_build_not_reached() # define MAXTL_MASK 0 #endif @@ -3758,6 +3763,178 @@ static void do_wrpsr(DisasContext *dc, TCGv src) TRANS(WRPSR, 32, do_wr_special, a, supervisor(dc), do_wrpsr) +static void do_wrwim(DisasContext *dc, TCGv src) +{ + target_ulong mask = MAKE_64BIT_MASK(0, dc->def->nwindows); + tcg_gen_andi_tl(cpu_wim, src, mask); +} + +TRANS(WRWIM, 32, do_wr_special, a, supervisor(dc), do_wrwim) + +static void do_wrtpc(DisasContext *dc, TCGv src) +{ +#ifdef TARGET_SPARC64 + TCGv_ptr r_tsptr = tcg_temp_new_ptr(); + + gen_load_trap_state_at_tl(r_tsptr); + tcg_gen_st_tl(src, r_tsptr, offsetof(trap_state, tpc)); +#else + qemu_build_not_reached(); +#endif +} + +TRANS(WRPR_tpc, 64, do_wr_special, a, supervisor(dc), do_wrtpc) + +static void do_wrtnpc(DisasContext *dc, TCGv src) +{ +#ifdef TARGET_SPARC64 + TCGv_ptr r_tsptr = tcg_temp_new_ptr(); + + gen_load_trap_state_at_tl(r_tsptr); + tcg_gen_st_tl(src, r_tsptr, offsetof(trap_state, tnpc)); +#else + qemu_build_not_reached(); +#endif +} + +TRANS(WRPR_tnpc, 64, do_wr_special, a, supervisor(dc), do_wrtnpc) + +static void do_wrtstate(DisasContext *dc, TCGv src) +{ +#ifdef TARGET_SPARC64 + TCGv_ptr r_tsptr = tcg_temp_new_ptr(); + + gen_load_trap_state_at_tl(r_tsptr); + tcg_gen_st_tl(src, r_tsptr, offsetof(trap_state, tstate)); +#else + qemu_build_not_reached(); +#endif +} + +TRANS(WRPR_tstate, 64, do_wr_special, a, supervisor(dc), do_wrtstate) + +static void do_wrtt(DisasContext *dc, TCGv src) +{ +#ifdef TARGET_SPARC64 + TCGv_ptr r_tsptr = tcg_temp_new_ptr(); + + gen_load_trap_state_at_tl(r_tsptr); + tcg_gen_st32_tl(src, r_tsptr, offsetof(trap_state, tt)); +#else + qemu_build_not_reached(); +#endif +} + +TRANS(WRPR_tt, 64, do_wr_special, a, supervisor(dc), do_wrtt) + +static void do_wrtick(DisasContext *dc, TCGv src) +{ + TCGv_ptr r_tickptr = tcg_temp_new_ptr(); + + tcg_gen_ld_ptr(r_tickptr, tcg_env, env64_field_offsetof(tick)); + translator_io_start(&dc->base); + gen_helper_tick_set_count(r_tickptr, src); + /* End TB to handle timer interrupt */ + dc->base.is_jmp = DISAS_EXIT; +} + +TRANS(WRPR_tick, 64, do_wr_special, a, supervisor(dc), do_wrtick) + +static void do_wrtba(DisasContext *dc, TCGv src) +{ + tcg_gen_mov_tl(cpu_tbr, src); +} + +TRANS(WRPR_tba, 64, do_wr_special, a, supervisor(dc), do_wrtba) + +static void do_wrpstate(DisasContext *dc, TCGv src) +{ + save_state(dc); + if (translator_io_start(&dc->base)) { + dc->base.is_jmp = DISAS_EXIT; + } + gen_helper_wrpstate(tcg_env, src); + dc->npc = DYNAMIC_PC; +} + +TRANS(WRPR_pstate, 64, do_wr_special, a, supervisor(dc), do_wrpstate) + +static void do_wrtl(DisasContext *dc, TCGv src) +{ + save_state(dc); + tcg_gen_st32_tl(src, tcg_env, env64_field_offsetof(tl)); + dc->npc = DYNAMIC_PC; +} + +TRANS(WRPR_tl, 64, do_wr_special, a, supervisor(dc), do_wrtl) + +static void do_wrpil(DisasContext *dc, TCGv src) +{ + if (translator_io_start(&dc->base)) { + dc->base.is_jmp = DISAS_EXIT; + } + gen_helper_wrpil(tcg_env, src); +} + +TRANS(WRPR_pil, 64, do_wr_special, a, supervisor(dc), do_wrpil) + +static void do_wrcwp(DisasContext *dc, TCGv src) +{ + gen_helper_wrcwp(tcg_env, src); +} + +TRANS(WRPR_cwp, 64, do_wr_special, a, supervisor(dc), do_wrcwp) + +static void do_wrcansave(DisasContext *dc, TCGv src) +{ + tcg_gen_st32_tl(src, tcg_env, env64_field_offsetof(cansave)); +} + +TRANS(WRPR_cansave, 64, do_wr_special, a, supervisor(dc), do_wrcansave) + +static void do_wrcanrestore(DisasContext *dc, TCGv src) +{ + tcg_gen_st32_tl(src, tcg_env, env64_field_offsetof(canrestore)); +} + +TRANS(WRPR_canrestore, 64, do_wr_special, a, supervisor(dc), do_wrcanrestore) + +static void do_wrcleanwin(DisasContext *dc, TCGv src) +{ + tcg_gen_st32_tl(src, tcg_env, env64_field_offsetof(cleanwin)); +} + +TRANS(WRPR_cleanwin, 64, do_wr_special, a, supervisor(dc), do_wrcleanwin) + +static void do_wrotherwin(DisasContext *dc, TCGv src) +{ + tcg_gen_st32_tl(src, tcg_env, env64_field_offsetof(otherwin)); +} + +TRANS(WRPR_otherwin, 64, do_wr_special, a, supervisor(dc), do_wrotherwin) + +static void do_wrwstate(DisasContext *dc, TCGv src) +{ + tcg_gen_st32_tl(src, tcg_env, env64_field_offsetof(wstate)); +} + +TRANS(WRPR_wstate, 64, do_wr_special, a, supervisor(dc), do_wrwstate) + +static void do_wrgl(DisasContext *dc, TCGv src) +{ + gen_helper_wrgl(tcg_env, src); +} + +TRANS(WRPR_gl, GL, do_wr_special, a, supervisor(dc), do_wrgl) + +/* UA2005 strand status */ +static void do_wrssr(DisasContext *dc, TCGv src) +{ + tcg_gen_mov_tl(cpu_ssr, src); +} + +TRANS(WRPR_strand_status, HYPV, do_wr_special, a, hypervisor(dc), do_wrssr) + static bool do_saved_restored(DisasContext *dc, bool saved) { if (!supervisor(dc)) { @@ -4448,142 +4625,8 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x30: goto illegal_insn; /* WRASR in decodetree */ #if !defined(CONFIG_USER_ONLY) - case 0x32: /* wrwim, V9 wrpr */ - { - if (!supervisor(dc)) - goto priv_insn; - cpu_tmp0 = tcg_temp_new(); - tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2); -#ifdef TARGET_SPARC64 - switch (rd) { - case 0: // tpc - { - TCGv_ptr r_tsptr; - - r_tsptr = tcg_temp_new_ptr(); - gen_load_trap_state_at_tl(r_tsptr); - tcg_gen_st_tl(cpu_tmp0, r_tsptr, - offsetof(trap_state, tpc)); - } - break; - case 1: // tnpc - { - TCGv_ptr r_tsptr; - - r_tsptr = tcg_temp_new_ptr(); - gen_load_trap_state_at_tl(r_tsptr); - tcg_gen_st_tl(cpu_tmp0, r_tsptr, - offsetof(trap_state, tnpc)); - } - break; - case 2: // tstate - { - TCGv_ptr r_tsptr; - - r_tsptr = tcg_temp_new_ptr(); - gen_load_trap_state_at_tl(r_tsptr); - tcg_gen_st_tl(cpu_tmp0, r_tsptr, - offsetof(trap_state, - tstate)); - } - break; - case 3: // tt - { - TCGv_ptr r_tsptr; - - r_tsptr = tcg_temp_new_ptr(); - gen_load_trap_state_at_tl(r_tsptr); - tcg_gen_st32_tl(cpu_tmp0, r_tsptr, - offsetof(trap_state, tt)); - } - break; - case 4: // tick - { - TCGv_ptr r_tickptr; - - r_tickptr = tcg_temp_new_ptr(); - tcg_gen_ld_ptr(r_tickptr, tcg_env, - offsetof(CPUSPARCState, tick)); - translator_io_start(&dc->base); - gen_helper_tick_set_count(r_tickptr, - cpu_tmp0); - /* End TB to handle timer interrupt */ - dc->base.is_jmp = DISAS_EXIT; - } - break; - case 5: // tba - tcg_gen_mov_tl(cpu_tbr, cpu_tmp0); - break; - case 6: // pstate - save_state(dc); - if (translator_io_start(&dc->base)) { - dc->base.is_jmp = DISAS_EXIT; - } - gen_helper_wrpstate(tcg_env, cpu_tmp0); - dc->npc = DYNAMIC_PC; - break; - case 7: // tl - save_state(dc); - tcg_gen_st32_tl(cpu_tmp0, tcg_env, - offsetof(CPUSPARCState, tl)); - dc->npc = DYNAMIC_PC; - break; - case 8: // pil - if (translator_io_start(&dc->base)) { - dc->base.is_jmp = DISAS_EXIT; - } - gen_helper_wrpil(tcg_env, cpu_tmp0); - break; - case 9: // cwp - gen_helper_wrcwp(tcg_env, cpu_tmp0); - break; - case 10: // cansave - tcg_gen_st32_tl(cpu_tmp0, tcg_env, - offsetof(CPUSPARCState, - cansave)); - break; - case 11: // canrestore - tcg_gen_st32_tl(cpu_tmp0, tcg_env, - offsetof(CPUSPARCState, - canrestore)); - break; - case 12: // cleanwin - tcg_gen_st32_tl(cpu_tmp0, tcg_env, - offsetof(CPUSPARCState, - cleanwin)); - break; - case 13: // otherwin - tcg_gen_st32_tl(cpu_tmp0, tcg_env, - offsetof(CPUSPARCState, - otherwin)); - break; - case 14: // wstate - tcg_gen_st32_tl(cpu_tmp0, tcg_env, - offsetof(CPUSPARCState, - wstate)); - break; - case 16: // UA2005 gl - CHECK_IU_FEATURE(dc, GL); - gen_helper_wrgl(tcg_env, cpu_tmp0); - break; - case 26: // UA2005 strand status - CHECK_IU_FEATURE(dc, HYPV); - if (!hypervisor(dc)) - goto priv_insn; - tcg_gen_mov_tl(cpu_ssr, cpu_tmp0); - break; - default: - goto illegal_insn; - } -#else - tcg_gen_trunc_tl_i32(cpu_wim, cpu_tmp0); - if (dc->def->nwindows != 32) { - tcg_gen_andi_tl(cpu_wim, cpu_wim, - (1 << dc->def->nwindows) - 1); - } -#endif - } - break; + case 0x32: + goto illegal_insn; /* WRPR in decodetree */ case 0x33: /* wrtbr, UA2005 wrhpr */ { #ifndef TARGET_SPARC64 From bb97f2f5d77227cc6b5edeed637218e7dd214816 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 2 Oct 2023 13:46:42 -0700 Subject: [PATCH 043/974] target/sparc: Move WRTBR, WRHPR to decodetree Implement htstate in the obvious way. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 9 ++++ target/sparc/translate.c | 111 +++++++++++++++++++------------------- 2 files changed, 66 insertions(+), 54 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index f478999ee1..eab737fdcc 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -143,6 +143,15 @@ WRPR_strand_status 10 11010 110010 ..... . ............. @n_r_ri RDTBR 10 rd:5 101011 00000 0 0000000000000 } +{ + WRTBR 10 00000 110011 ..... . ............. @n_r_ri + WRHPR_hpstate 10 00000 110011 ..... . ............. @n_r_ri +} +WRHPR_htstate 10 00001 110011 ..... . ............. @n_r_ri +WRHPR_hintp 10 00011 110011 ..... . ............. @n_r_ri +WRHPR_htba 10 00101 110011 ..... . ............. @n_r_ri +WRHPR_hstick_cmpr 10 11111 110011 ..... . ............. @n_r_ri + Tcc_r 10 0 cond:4 111010 rs1:5 0 cc:1 0000000 rs2:5 { # For v7, the entire simm13 field is present, but masked to 7 bits. diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 9b82e2bf6f..d87c08c18c 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -50,6 +50,7 @@ # define gen_helper_set_softint(E, S) qemu_build_not_reached() # define gen_helper_tick_get_count(D, E, T, C) qemu_build_not_reached() # define gen_helper_tick_set_count(P, S) qemu_build_not_reached() +# define gen_helper_tick_set_limit(P, S) qemu_build_not_reached() # define gen_helper_wrccr(E, S) qemu_build_not_reached() # define gen_helper_wrcwp(E, S) qemu_build_not_reached() # define gen_helper_wrgl(E, S) qemu_build_not_reached() @@ -3935,6 +3936,61 @@ static void do_wrssr(DisasContext *dc, TCGv src) TRANS(WRPR_strand_status, HYPV, do_wr_special, a, hypervisor(dc), do_wrssr) +TRANS(WRTBR, 32, do_wr_special, a, supervisor(dc), do_wrtba) + +static void do_wrhpstate(DisasContext *dc, TCGv src) +{ + tcg_gen_st_tl(src, tcg_env, env64_field_offsetof(hpstate)); + dc->base.is_jmp = DISAS_EXIT; +} + +TRANS(WRHPR_hpstate, HYPV, do_wr_special, a, hypervisor(dc), do_wrhpstate) + +static void do_wrhtstate(DisasContext *dc, TCGv src) +{ + TCGv_i32 tl = tcg_temp_new_i32(); + TCGv_ptr tp = tcg_temp_new_ptr(); + + tcg_gen_ld_i32(tl, tcg_env, env64_field_offsetof(tl)); + tcg_gen_andi_i32(tl, tl, MAXTL_MASK); + tcg_gen_shli_i32(tl, tl, 3); + tcg_gen_ext_i32_ptr(tp, tl); + tcg_gen_add_ptr(tp, tp, tcg_env); + + tcg_gen_st_tl(src, tp, env64_field_offsetof(htstate)); +} + +TRANS(WRHPR_htstate, HYPV, do_wr_special, a, hypervisor(dc), do_wrhtstate) + +static void do_wrhintp(DisasContext *dc, TCGv src) +{ + tcg_gen_mov_tl(cpu_hintp, src); +} + +TRANS(WRHPR_hintp, HYPV, do_wr_special, a, hypervisor(dc), do_wrhintp) + +static void do_wrhtba(DisasContext *dc, TCGv src) +{ + tcg_gen_mov_tl(cpu_htba, src); +} + +TRANS(WRHPR_htba, HYPV, do_wr_special, a, hypervisor(dc), do_wrhtba) + +static void do_wrhstick_cmpr(DisasContext *dc, TCGv src) +{ + TCGv_ptr r_tickptr = tcg_temp_new_ptr(); + + tcg_gen_mov_tl(cpu_hstick_cmpr, src); + tcg_gen_ld_ptr(r_tickptr, tcg_env, env64_field_offsetof(hstick)); + translator_io_start(&dc->base); + gen_helper_tick_set_limit(r_tickptr, cpu_hstick_cmpr); + /* End TB to handle timer interrupt */ + dc->base.is_jmp = DISAS_EXIT; +} + +TRANS(WRHPR_hstick_cmpr, HYPV, do_wr_special, a, hypervisor(dc), + do_wrhstick_cmpr) + static bool do_saved_restored(DisasContext *dc, bool saved) { if (!supervisor(dc)) { @@ -4624,63 +4680,10 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) #endif case 0x30: goto illegal_insn; /* WRASR in decodetree */ -#if !defined(CONFIG_USER_ONLY) case 0x32: goto illegal_insn; /* WRPR in decodetree */ case 0x33: /* wrtbr, UA2005 wrhpr */ - { -#ifndef TARGET_SPARC64 - if (!supervisor(dc)) - goto priv_insn; - tcg_gen_xor_tl(cpu_tbr, cpu_src1, cpu_src2); -#else - CHECK_IU_FEATURE(dc, HYPV); - if (!hypervisor(dc)) - goto priv_insn; - cpu_tmp0 = tcg_temp_new(); - tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2); - switch (rd) { - case 0: // hpstate - tcg_gen_st_i64(cpu_tmp0, tcg_env, - offsetof(CPUSPARCState, - hpstate)); - save_state(dc); - gen_op_next_insn(); - tcg_gen_exit_tb(NULL, 0); - dc->base.is_jmp = DISAS_NORETURN; - break; - case 1: // htstate - // XXX gen_op_wrhtstate(); - break; - case 3: // hintp - tcg_gen_mov_tl(cpu_hintp, cpu_tmp0); - break; - case 5: // htba - tcg_gen_mov_tl(cpu_htba, cpu_tmp0); - break; - case 31: // hstick_cmpr - { - TCGv_ptr r_tickptr; - - tcg_gen_mov_tl(cpu_hstick_cmpr, cpu_tmp0); - r_tickptr = tcg_temp_new_ptr(); - tcg_gen_ld_ptr(r_tickptr, tcg_env, - offsetof(CPUSPARCState, hstick)); - translator_io_start(&dc->base); - gen_helper_tick_set_limit(r_tickptr, - cpu_hstick_cmpr); - /* End TB to handle timer interrupt */ - dc->base.is_jmp = DISAS_EXIT; - } - break; - case 6: // hver readonly - default: - goto illegal_insn; - } -#endif - } - break; -#endif + goto illegal_insn; /* WRTBR, WRHPR in decodetree */ #ifdef TARGET_SPARC64 case 0x2c: /* V9 movcc */ { From cd6269f7c966f8c8119abc0318dd6b60a8376989 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 22 Oct 2023 14:10:30 -0700 Subject: [PATCH 044/974] target/sparc: Remove cpu_wim Use direct loads and stores to env instead. Signed-off-by: Richard Henderson --- target/sparc/translate.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index d87c08c18c..096cbb869f 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -84,9 +84,7 @@ static TCGv_i32 cpu_xcc, cpu_fprs; static TCGv cpu_gsr; static TCGv cpu_tick_cmpr, cpu_stick_cmpr, cpu_hstick_cmpr; static TCGv cpu_hintp, cpu_htba, cpu_hver, cpu_ssr, cpu_ver; -# define cpu_wim ({ qemu_build_not_reached(); (TCGv)NULL; }) #else -static TCGv cpu_wim; # define cpu_fprs ({ qemu_build_not_reached(); (TCGv)NULL; }) # define cpu_gsr ({ qemu_build_not_reached(); (TCGv)NULL; }) # define cpu_hintp ({ qemu_build_not_reached(); (TCGv)NULL; }) @@ -103,8 +101,10 @@ static TCGv_i64 cpu_fpr[TARGET_DPREGS]; #define env_field_offsetof(X) offsetof(CPUSPARCState, X) #ifdef TARGET_SPARC64 +# define env32_field_offsetof(X) ({ qemu_build_not_reached(); 0; }) # define env64_field_offsetof(X) env_field_offsetof(X) #else +# define env32_field_offsetof(X) env_field_offsetof(X) # define env64_field_offsetof(X) ({ qemu_build_not_reached(); 0; }) #endif @@ -3414,7 +3414,8 @@ TRANS(RDHPR_hstick_cmpr, HYPV, do_rd_special, hypervisor(dc), a->rd, static TCGv do_rdwim(DisasContext *dc, TCGv dst) { - return cpu_wim; + tcg_gen_ld_tl(dst, tcg_env, env32_field_offsetof(wim)); + return dst; } TRANS(RDWIM, 32, do_rd_special, supervisor(dc), a->rd, do_rdwim) @@ -3767,7 +3768,10 @@ TRANS(WRPSR, 32, do_wr_special, a, supervisor(dc), do_wrpsr) static void do_wrwim(DisasContext *dc, TCGv src) { target_ulong mask = MAKE_64BIT_MASK(0, dc->def->nwindows); - tcg_gen_andi_tl(cpu_wim, src, mask); + TCGv tmp = tcg_temp_new(); + + tcg_gen_andi_tl(tmp, src, mask); + tcg_gen_st_tl(tmp, tcg_env, env32_field_offsetof(wim)); } TRANS(WRWIM, 32, do_wr_special, a, supervisor(dc), do_wrwim) @@ -5939,8 +5943,6 @@ void sparc_tcg_init(void) #ifdef TARGET_SPARC64 { &cpu_xcc, offsetof(CPUSPARCState, xcc), "xcc" }, { &cpu_fprs, offsetof(CPUSPARCState, fprs), "fprs" }, -#else - { &cpu_wim, offsetof(CPUSPARCState, wim), "wim" }, #endif { &cpu_cc_op, offsetof(CPUSPARCState, cc_op), "cc_op" }, { &cpu_psr, offsetof(CPUSPARCState, psr), "psr" }, From 577efa45571b2b62b882f7fd23da197ea99f6014 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 22 Oct 2023 14:19:29 -0700 Subject: [PATCH 045/974] target/sparc: Remove cpu_tick_cmpr, cpu_stick_cmpr, cpu_hstick_cmpr Use direct loads and stores to env instead. Signed-off-by: Richard Henderson --- target/sparc/translate.c | 41 ++++++++++++++-------------------------- 1 file changed, 14 insertions(+), 27 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 096cbb869f..65b71dd931 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -82,18 +82,14 @@ static TCGv cpu_cond; #ifdef TARGET_SPARC64 static TCGv_i32 cpu_xcc, cpu_fprs; static TCGv cpu_gsr; -static TCGv cpu_tick_cmpr, cpu_stick_cmpr, cpu_hstick_cmpr; static TCGv cpu_hintp, cpu_htba, cpu_hver, cpu_ssr, cpu_ver; #else # define cpu_fprs ({ qemu_build_not_reached(); (TCGv)NULL; }) # define cpu_gsr ({ qemu_build_not_reached(); (TCGv)NULL; }) # define cpu_hintp ({ qemu_build_not_reached(); (TCGv)NULL; }) -# define cpu_hstick_cmpr ({ qemu_build_not_reached(); (TCGv)NULL; }) # define cpu_htba ({ qemu_build_not_reached(); (TCGv)NULL; }) # define cpu_hver ({ qemu_build_not_reached(); (TCGv)NULL; }) # define cpu_ssr ({ qemu_build_not_reached(); (TCGv)NULL; }) -# define cpu_stick_cmpr ({ qemu_build_not_reached(); (TCGv)NULL; }) -# define cpu_tick_cmpr ({ qemu_build_not_reached(); (TCGv)NULL; }) # define cpu_ver ({ qemu_build_not_reached(); (TCGv)NULL; }) #endif /* Floating point registers */ @@ -3307,7 +3303,8 @@ TRANS(RDSOFTINT, 64, do_rd_special, supervisor(dc), a->rd, do_rdsoftint) static TCGv do_rdtick_cmpr(DisasContext *dc, TCGv dst) { - return cpu_tick_cmpr; + tcg_gen_ld_tl(dst, tcg_env, env64_field_offsetof(tick_cmpr)); + return dst; } /* TODO: non-priv access only allowed when enabled. */ @@ -3331,7 +3328,8 @@ TRANS(RDSTICK, 64, do_rd_special, true, a->rd, do_rdstick) static TCGv do_rdstick_cmpr(DisasContext *dc, TCGv dst) { - return cpu_stick_cmpr; + tcg_gen_ld_tl(dst, tcg_env, env64_field_offsetof(stick_cmpr)); + return dst; } /* TODO: supervisor access only allowed when enabled by hypervisor. */ @@ -3406,7 +3404,8 @@ TRANS(RDHPR_hver, HYPV, do_rd_special, hypervisor(dc), a->rd, do_rdhver) static TCGv do_rdhstick_cmpr(DisasContext *dc, TCGv dst) { - return cpu_hstick_cmpr; + tcg_gen_ld_tl(dst, tcg_env, env64_field_offsetof(hstick_cmpr)); + return dst; } TRANS(RDHPR_hstick_cmpr, HYPV, do_rd_special, hypervisor(dc), a->rd, @@ -3696,18 +3695,14 @@ TRANS(WRSOFTINT, 64, do_wr_special, a, supervisor(dc), do_wrsoftint) static void do_wrtick_cmpr(DisasContext *dc, TCGv src) { -#ifdef TARGET_SPARC64 TCGv_ptr r_tickptr = tcg_temp_new_ptr(); - tcg_gen_mov_tl(cpu_tick_cmpr, src); - tcg_gen_ld_ptr(r_tickptr, tcg_env, offsetof(CPUSPARCState, tick)); + tcg_gen_st_tl(src, tcg_env, env64_field_offsetof(tick_cmpr)); + tcg_gen_ld_ptr(r_tickptr, tcg_env, env64_field_offsetof(tick)); translator_io_start(&dc->base); - gen_helper_tick_set_limit(r_tickptr, cpu_tick_cmpr); + gen_helper_tick_set_limit(r_tickptr, src); /* End TB to handle timer interrupt */ dc->base.is_jmp = DISAS_EXIT; -#else - qemu_build_not_reached(); -#endif } TRANS(WRTICK_CMPR, 64, do_wr_special, a, supervisor(dc), do_wrtick_cmpr) @@ -3731,18 +3726,14 @@ TRANS(WRSTICK, 64, do_wr_special, a, supervisor(dc), do_wrstick) static void do_wrstick_cmpr(DisasContext *dc, TCGv src) { -#ifdef TARGET_SPARC64 TCGv_ptr r_tickptr = tcg_temp_new_ptr(); - tcg_gen_mov_tl(cpu_stick_cmpr, src); - tcg_gen_ld_ptr(r_tickptr, tcg_env, offsetof(CPUSPARCState, stick)); + tcg_gen_st_tl(src, tcg_env, env64_field_offsetof(stick_cmpr)); + tcg_gen_ld_ptr(r_tickptr, tcg_env, env64_field_offsetof(stick)); translator_io_start(&dc->base); - gen_helper_tick_set_limit(r_tickptr, cpu_stick_cmpr); + gen_helper_tick_set_limit(r_tickptr, src); /* End TB to handle timer interrupt */ dc->base.is_jmp = DISAS_EXIT; -#else - qemu_build_not_reached(); -#endif } TRANS(WRSTICK_CMPR, 64, do_wr_special, a, supervisor(dc), do_wrstick_cmpr) @@ -3984,10 +3975,10 @@ static void do_wrhstick_cmpr(DisasContext *dc, TCGv src) { TCGv_ptr r_tickptr = tcg_temp_new_ptr(); - tcg_gen_mov_tl(cpu_hstick_cmpr, src); + tcg_gen_st_tl(src, tcg_env, env64_field_offsetof(hstick_cmpr)); tcg_gen_ld_ptr(r_tickptr, tcg_env, env64_field_offsetof(hstick)); translator_io_start(&dc->base); - gen_helper_tick_set_limit(r_tickptr, cpu_hstick_cmpr); + gen_helper_tick_set_limit(r_tickptr, src); /* End TB to handle timer interrupt */ dc->base.is_jmp = DISAS_EXIT; } @@ -5951,10 +5942,6 @@ void sparc_tcg_init(void) static const struct { TCGv *ptr; int off; const char *name; } rtl[] = { #ifdef TARGET_SPARC64 { &cpu_gsr, offsetof(CPUSPARCState, gsr), "gsr" }, - { &cpu_tick_cmpr, offsetof(CPUSPARCState, tick_cmpr), "tick_cmpr" }, - { &cpu_stick_cmpr, offsetof(CPUSPARCState, stick_cmpr), "stick_cmpr" }, - { &cpu_hstick_cmpr, offsetof(CPUSPARCState, hstick_cmpr), - "hstick_cmpr" }, { &cpu_hintp, offsetof(CPUSPARCState, hintp), "hintp" }, { &cpu_htba, offsetof(CPUSPARCState, htba), "htba" }, { &cpu_hver, offsetof(CPUSPARCState, hver), "hver" }, From 2da789ded5a89dff4a3485bc486b21278f8b61d3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 22 Oct 2023 14:25:33 -0700 Subject: [PATCH 046/974] target/sparc: Remove cpu_hintp, cpu_htba, cpu_hver, cpu_ssr, cpu_ver Use direct loads and stores to env instead. Signed-off-by: Richard Henderson --- target/sparc/translate.c | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 65b71dd931..7b83803b6b 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -82,15 +82,9 @@ static TCGv cpu_cond; #ifdef TARGET_SPARC64 static TCGv_i32 cpu_xcc, cpu_fprs; static TCGv cpu_gsr; -static TCGv cpu_hintp, cpu_htba, cpu_hver, cpu_ssr, cpu_ver; #else # define cpu_fprs ({ qemu_build_not_reached(); (TCGv)NULL; }) # define cpu_gsr ({ qemu_build_not_reached(); (TCGv)NULL; }) -# define cpu_hintp ({ qemu_build_not_reached(); (TCGv)NULL; }) -# define cpu_htba ({ qemu_build_not_reached(); (TCGv)NULL; }) -# define cpu_hver ({ qemu_build_not_reached(); (TCGv)NULL; }) -# define cpu_ssr ({ qemu_build_not_reached(); (TCGv)NULL; }) -# define cpu_ver ({ qemu_build_not_reached(); (TCGv)NULL; }) #endif /* Floating point registers */ static TCGv_i64 cpu_fpr[TARGET_DPREGS]; @@ -3383,21 +3377,24 @@ TRANS(RDHPR_htstate, HYPV, do_rd_special, hypervisor(dc), a->rd, do_rdhtstate) static TCGv do_rdhintp(DisasContext *dc, TCGv dst) { - return cpu_hintp; + tcg_gen_ld_tl(dst, tcg_env, env64_field_offsetof(hintp)); + return dst; } TRANS(RDHPR_hintp, HYPV, do_rd_special, hypervisor(dc), a->rd, do_rdhintp) static TCGv do_rdhtba(DisasContext *dc, TCGv dst) { - return cpu_htba; + tcg_gen_ld_tl(dst, tcg_env, env64_field_offsetof(htba)); + return dst; } TRANS(RDHPR_htba, HYPV, do_rd_special, hypervisor(dc), a->rd, do_rdhtba) static TCGv do_rdhver(DisasContext *dc, TCGv dst) { - return cpu_hver; + tcg_gen_ld_tl(dst, tcg_env, env64_field_offsetof(hver)); + return dst; } TRANS(RDHPR_hver, HYPV, do_rd_special, hypervisor(dc), a->rd, do_rdhver) @@ -3572,14 +3569,16 @@ TRANS(RDPR_gl, GL, do_rd_special, supervisor(dc), a->rd, do_rdgl) /* UA2005 strand status */ static TCGv do_rdssr(DisasContext *dc, TCGv dst) { - return cpu_ssr; + tcg_gen_ld_tl(dst, tcg_env, env64_field_offsetof(ssr)); + return dst; } TRANS(RDPR_strand_status, HYPV, do_rd_special, hypervisor(dc), a->rd, do_rdssr) static TCGv do_rdver(DisasContext *dc, TCGv dst) { - return cpu_ver; + tcg_gen_ld_tl(dst, tcg_env, env64_field_offsetof(version)); + return dst; } TRANS(RDPR_ver, 64, do_rd_special, supervisor(dc), a->rd, do_rdver) @@ -3926,7 +3925,7 @@ TRANS(WRPR_gl, GL, do_wr_special, a, supervisor(dc), do_wrgl) /* UA2005 strand status */ static void do_wrssr(DisasContext *dc, TCGv src) { - tcg_gen_mov_tl(cpu_ssr, src); + tcg_gen_st_tl(src, tcg_env, env64_field_offsetof(ssr)); } TRANS(WRPR_strand_status, HYPV, do_wr_special, a, hypervisor(dc), do_wrssr) @@ -3959,14 +3958,14 @@ TRANS(WRHPR_htstate, HYPV, do_wr_special, a, hypervisor(dc), do_wrhtstate) static void do_wrhintp(DisasContext *dc, TCGv src) { - tcg_gen_mov_tl(cpu_hintp, src); + tcg_gen_st_tl(src, tcg_env, env64_field_offsetof(hintp)); } TRANS(WRHPR_hintp, HYPV, do_wr_special, a, hypervisor(dc), do_wrhintp) static void do_wrhtba(DisasContext *dc, TCGv src) { - tcg_gen_mov_tl(cpu_htba, src); + tcg_gen_st_tl(src, tcg_env, env64_field_offsetof(htba)); } TRANS(WRHPR_htba, HYPV, do_wr_special, a, hypervisor(dc), do_wrhtba) @@ -5942,11 +5941,6 @@ void sparc_tcg_init(void) static const struct { TCGv *ptr; int off; const char *name; } rtl[] = { #ifdef TARGET_SPARC64 { &cpu_gsr, offsetof(CPUSPARCState, gsr), "gsr" }, - { &cpu_hintp, offsetof(CPUSPARCState, hintp), "hintp" }, - { &cpu_htba, offsetof(CPUSPARCState, htba), "htba" }, - { &cpu_hver, offsetof(CPUSPARCState, hver), "hver" }, - { &cpu_ssr, offsetof(CPUSPARCState, ssr), "ssr" }, - { &cpu_ver, offsetof(CPUSPARCState, version), "ver" }, #endif { &cpu_cond, offsetof(CPUSPARCState, cond), "cond" }, { &cpu_cc_src, offsetof(CPUSPARCState, cc_src), "cc_src" }, From 428881deba62aa8fd5ef9248deba79594f70615a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 1 Oct 2023 21:49:43 -0700 Subject: [PATCH 047/974] target/sparc: Move basic arithmetic to decodetree Move ADD, AND, OR, XOR, SUB, ANDN, ORN, XORN. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 12 +++ target/sparc/translate.c | 185 +++++++++++++++++--------------------- 2 files changed, 94 insertions(+), 103 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index eab737fdcc..3271c2997d 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -29,6 +29,9 @@ CALL 01 i:s30 &r_r_ri rd rs1 rs2_or_imm imm:bool @n_r_ri .. ..... ...... rs1:5 imm:1 rs2_or_imm:s13 &r_r_ri rd=0 +&r_r_ri_cc rd rs1 rs2_or_imm imm:bool cc:bool +@r_r_ri_cc .. rd:5 . cc:1 .... rs1:5 imm:1 rs2_or_imm:s13 &r_r_ri_cc + { [ STBAR 10 00000 101000 01111 0 0000000000000 @@ -152,6 +155,15 @@ WRHPR_hintp 10 00011 110011 ..... . ............. @n_r_ri WRHPR_htba 10 00101 110011 ..... . ............. @n_r_ri WRHPR_hstick_cmpr 10 11111 110011 ..... . ............. @n_r_ri +ADD 10 ..... 0.0000 ..... . ............. @r_r_ri_cc +AND 10 ..... 0.0001 ..... . ............. @r_r_ri_cc +OR 10 ..... 0.0010 ..... . ............. @r_r_ri_cc +XOR 10 ..... 0.0011 ..... . ............. @r_r_ri_cc +SUB 10 ..... 0.0100 ..... . ............. @r_r_ri_cc +ANDN 10 ..... 0.0101 ..... . ............. @r_r_ri_cc +ORN 10 ..... 0.0110 ..... . ............. @r_r_ri_cc +XORN 10 ..... 0.0111 ..... . ............. @r_r_ri_cc + Tcc_r 10 0 cond:4 111010 rs1:5 0 cc:1 0000000 rs2:5 { # For v7, the entire simm13 field is present, but masked to 7 bits. diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 7b83803b6b..2a8846f2ac 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -4013,6 +4013,88 @@ static bool trans_NOP_v7(DisasContext *dc, arg_NOP_v7 *a) return false; } +static bool do_arith_int(DisasContext *dc, arg_r_r_ri_cc *a, int cc_op, + void (*func)(TCGv, TCGv, TCGv), + void (*funci)(TCGv, TCGv, target_long)) +{ + TCGv dst, src1; + + /* For simplicity, we under-decoded the rs2 form. */ + if (!a->imm && a->rs2_or_imm & ~0x1f) { + return false; + } + + if (a->cc) { + dst = cpu_cc_dst; + } else { + dst = gen_dest_gpr(dc, a->rd); + } + src1 = gen_load_gpr(dc, a->rs1); + + if (a->imm || a->rs2_or_imm == 0) { + if (funci) { + funci(dst, src1, a->rs2_or_imm); + } else { + func(dst, src1, tcg_constant_tl(a->rs2_or_imm)); + } + } else { + func(dst, src1, cpu_regs[a->rs2_or_imm]); + } + gen_store_gpr(dc, a->rd, dst); + + if (a->cc) { + tcg_gen_movi_i32(cpu_cc_op, cc_op); + dc->cc_op = cc_op; + } + return advance_pc(dc); +} + +static bool do_arith(DisasContext *dc, arg_r_r_ri_cc *a, int cc_op, + void (*func)(TCGv, TCGv, TCGv), + void (*funci)(TCGv, TCGv, target_long), + void (*func_cc)(TCGv, TCGv, TCGv)) +{ + if (a->cc) { + return do_arith_int(dc, a, cc_op, func_cc, NULL); + } + return do_arith_int(dc, a, cc_op, func, funci); +} + +static bool do_logic(DisasContext *dc, arg_r_r_ri_cc *a, + void (*func)(TCGv, TCGv, TCGv), + void (*funci)(TCGv, TCGv, target_long)) +{ + return do_arith_int(dc, a, CC_OP_LOGIC, func, funci); +} + +TRANS(ADD, ALL, do_arith, a, CC_OP_ADD, + tcg_gen_add_tl, tcg_gen_addi_tl, gen_op_add_cc) +TRANS(SUB, ALL, do_arith, a, CC_OP_SUB, + tcg_gen_sub_tl, tcg_gen_subi_tl, gen_op_sub_cc) + +TRANS(AND, ALL, do_logic, a, tcg_gen_and_tl, tcg_gen_andi_tl) +TRANS(XOR, ALL, do_logic, a, tcg_gen_xor_tl, tcg_gen_xori_tl) +TRANS(ANDN, ALL, do_logic, a, tcg_gen_andc_tl, NULL) +TRANS(ORN, ALL, do_logic, a, tcg_gen_orc_tl, NULL) +TRANS(XORN, ALL, do_logic, a, tcg_gen_eqv_tl, NULL) + +static bool trans_OR(DisasContext *dc, arg_r_r_ri_cc *a) +{ + /* OR with %g0 is the canonical alias for MOV. */ + if (!a->cc && a->rs1 == 0) { + if (a->imm || a->rs2_or_imm == 0) { + gen_store_gpr(dc, a->rd, tcg_constant_tl(a->rs2_or_imm)); + } else if (a->rs2_or_imm & ~0x1f) { + /* For simplicity, we under-decoded the rs2 form. */ + return false; + } else { + gen_store_gpr(dc, a->rd, cpu_regs[a->rs2_or_imm]); + } + return advance_pc(dc); + } + return do_logic(dc, a, tcg_gen_or_tl, tcg_gen_ori_tl); +} + #define CHECK_IU_FEATURE(dc, FEATURE) \ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ goto illegal_insn; @@ -4361,43 +4443,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) default: goto illegal_insn; } - } else if (xop == 0x2) { - TCGv dst = gen_dest_gpr(dc, rd); - rs1 = GET_FIELD(insn, 13, 17); - if (rs1 == 0) { - /* clr/mov shortcut : or %g0, x, y -> mov x, y */ - if (IS_IMM) { /* immediate */ - simm = GET_FIELDs(insn, 19, 31); - tcg_gen_movi_tl(dst, simm); - gen_store_gpr(dc, rd, dst); - } else { /* register */ - rs2 = GET_FIELD(insn, 27, 31); - if (rs2 == 0) { - tcg_gen_movi_tl(dst, 0); - gen_store_gpr(dc, rd, dst); - } else { - cpu_src2 = gen_load_gpr(dc, rs2); - gen_store_gpr(dc, rd, cpu_src2); - } - } - } else { - cpu_src1 = get_src1(dc, insn); - if (IS_IMM) { /* immediate */ - simm = GET_FIELDs(insn, 19, 31); - tcg_gen_ori_tl(dst, cpu_src1, simm); - gen_store_gpr(dc, rd, dst); - } else { /* register */ - rs2 = GET_FIELD(insn, 27, 31); - if (rs2 == 0) { - /* mov shortcut: or x, %g0, y -> mov x, y */ - gen_store_gpr(dc, rd, cpu_src1); - } else { - cpu_src2 = gen_load_gpr(dc, rs2); - tcg_gen_or_tl(dst, cpu_src1, cpu_src2); - gen_store_gpr(dc, rd, dst); - } - } - } #ifdef TARGET_SPARC64 } else if (xop == 0x25) { /* sll, V9 sllx */ cpu_src1 = get_src1(dc, insn); @@ -4474,72 +4519,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) cpu_src1 = get_src1(dc, insn); cpu_src2 = get_src2(dc, insn); switch (xop & ~0x10) { - case 0x0: /* add */ - if (xop & 0x10) { - gen_op_add_cc(cpu_dst, cpu_src1, cpu_src2); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_ADD); - dc->cc_op = CC_OP_ADD; - } else { - tcg_gen_add_tl(cpu_dst, cpu_src1, cpu_src2); - } - break; - case 0x1: /* and */ - tcg_gen_and_tl(cpu_dst, cpu_src1, cpu_src2); - if (xop & 0x10) { - tcg_gen_mov_tl(cpu_cc_dst, cpu_dst); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_LOGIC); - dc->cc_op = CC_OP_LOGIC; - } - break; - case 0x2: /* or */ - tcg_gen_or_tl(cpu_dst, cpu_src1, cpu_src2); - if (xop & 0x10) { - tcg_gen_mov_tl(cpu_cc_dst, cpu_dst); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_LOGIC); - dc->cc_op = CC_OP_LOGIC; - } - break; - case 0x3: /* xor */ - tcg_gen_xor_tl(cpu_dst, cpu_src1, cpu_src2); - if (xop & 0x10) { - tcg_gen_mov_tl(cpu_cc_dst, cpu_dst); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_LOGIC); - dc->cc_op = CC_OP_LOGIC; - } - break; - case 0x4: /* sub */ - if (xop & 0x10) { - gen_op_sub_cc(cpu_dst, cpu_src1, cpu_src2); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_SUB); - dc->cc_op = CC_OP_SUB; - } else { - tcg_gen_sub_tl(cpu_dst, cpu_src1, cpu_src2); - } - break; - case 0x5: /* andn */ - tcg_gen_andc_tl(cpu_dst, cpu_src1, cpu_src2); - if (xop & 0x10) { - tcg_gen_mov_tl(cpu_cc_dst, cpu_dst); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_LOGIC); - dc->cc_op = CC_OP_LOGIC; - } - break; - case 0x6: /* orn */ - tcg_gen_orc_tl(cpu_dst, cpu_src1, cpu_src2); - if (xop & 0x10) { - tcg_gen_mov_tl(cpu_cc_dst, cpu_dst); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_LOGIC); - dc->cc_op = CC_OP_LOGIC; - } - break; - case 0x7: /* xorn */ - tcg_gen_eqv_tl(cpu_dst, cpu_src1, cpu_src2); - if (xop & 0x10) { - tcg_gen_mov_tl(cpu_cc_dst, cpu_dst); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_LOGIC); - dc->cc_op = CC_OP_LOGIC; - } - break; case 0x8: /* addx, V9 addc */ gen_op_addx_int(dc, cpu_dst, cpu_src1, cpu_src2, (xop & 0x10)); From 420a187d8065288cc77bac4f1979b23da97afb94 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 20 Oct 2023 19:20:44 -0700 Subject: [PATCH 048/974] target/sparc: Move ADDC to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 1 + target/sparc/translate.c | 154 ++++++++++++++++++++++++-------------- 2 files changed, 97 insertions(+), 58 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 3271c2997d..1cff18fa1f 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -163,6 +163,7 @@ SUB 10 ..... 0.0100 ..... . ............. @r_r_ri_cc ANDN 10 ..... 0.0101 ..... . ............. @r_r_ri_cc ORN 10 ..... 0.0110 ..... . ............. @r_r_ri_cc XORN 10 ..... 0.0111 ..... . ............. @r_r_ri_cc +ADDC 10 ..... 0.1000 ..... . ............. @r_r_ri_cc Tcc_r 10 0 cond:4 111010 rs1:5 0 cc:1 0000000 rs2:5 { diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 2a8846f2ac..eb829acc68 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -447,71 +447,89 @@ static TCGv_i32 gen_sub32_carry32(void) return carry_32; } -static void gen_op_addx_int(DisasContext *dc, TCGv dst, TCGv src1, - TCGv src2, int update_cc) +static void gen_op_addc_int(TCGv dst, TCGv src1, TCGv src2, + TCGv_i32 carry_32, bool update_cc) { - TCGv_i32 carry_32; - TCGv carry; + tcg_gen_add_tl(dst, src1, src2); - switch (dc->cc_op) { - case CC_OP_DIV: - case CC_OP_LOGIC: - /* Carry is known to be zero. Fall back to plain ADD. */ - if (update_cc) { - gen_op_add_cc(dst, src1, src2); - } else { - tcg_gen_add_tl(dst, src1, src2); - } - return; - - case CC_OP_ADD: - case CC_OP_TADD: - case CC_OP_TADDTV: - if (TARGET_LONG_BITS == 32) { - /* We can re-use the host's hardware carry generation by using - an ADD2 opcode. We discard the low part of the output. - Ideally we'd combine this operation with the add that - generated the carry in the first place. */ - carry = tcg_temp_new(); - tcg_gen_add2_tl(carry, dst, cpu_cc_src, src1, cpu_cc_src2, src2); - goto add_done; - } - carry_32 = gen_add32_carry32(); - break; - - case CC_OP_SUB: - case CC_OP_TSUB: - case CC_OP_TSUBTV: - carry_32 = gen_sub32_carry32(); - break; - - default: - /* We need external help to produce the carry. */ - carry_32 = tcg_temp_new_i32(); - gen_helper_compute_C_icc(carry_32, tcg_env); - break; - } - -#if TARGET_LONG_BITS == 64 - carry = tcg_temp_new(); - tcg_gen_extu_i32_i64(carry, carry_32); +#ifdef TARGET_SPARC64 + TCGv carry = tcg_temp_new(); + tcg_gen_extu_i32_tl(carry, carry_32); + tcg_gen_add_tl(dst, dst, carry); #else - carry = carry_32; + tcg_gen_add_i32(dst, dst, carry_32); #endif - tcg_gen_add_tl(dst, src1, src2); - tcg_gen_add_tl(dst, dst, carry); - - add_done: if (update_cc) { + tcg_debug_assert(dst == cpu_cc_dst); tcg_gen_mov_tl(cpu_cc_src, src1); tcg_gen_mov_tl(cpu_cc_src2, src2); - tcg_gen_mov_tl(cpu_cc_dst, dst); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_ADDX); - dc->cc_op = CC_OP_ADDX; } } +static void gen_op_addc_int_add(TCGv dst, TCGv src1, TCGv src2, bool update_cc) +{ + TCGv discard; + + if (TARGET_LONG_BITS == 64) { + gen_op_addc_int(dst, src1, src2, gen_add32_carry32(), update_cc); + return; + } + + /* + * We can re-use the host's hardware carry generation by using + * an ADD2 opcode. We discard the low part of the output. + * Ideally we'd combine this operation with the add that + * generated the carry in the first place. + */ + discard = tcg_temp_new(); + tcg_gen_add2_tl(discard, dst, cpu_cc_src, src1, cpu_cc_src2, src2); + + if (update_cc) { + tcg_debug_assert(dst == cpu_cc_dst); + tcg_gen_mov_tl(cpu_cc_src, src1); + tcg_gen_mov_tl(cpu_cc_src2, src2); + } +} + +static void gen_op_addc_add(TCGv dst, TCGv src1, TCGv src2) +{ + gen_op_addc_int_add(dst, src1, src2, false); +} + +static void gen_op_addccc_add(TCGv dst, TCGv src1, TCGv src2) +{ + gen_op_addc_int_add(dst, src1, src2, true); +} + +static void gen_op_addc_sub(TCGv dst, TCGv src1, TCGv src2) +{ + gen_op_addc_int(dst, src1, src2, gen_sub32_carry32(), false); +} + +static void gen_op_addccc_sub(TCGv dst, TCGv src1, TCGv src2) +{ + gen_op_addc_int(dst, src1, src2, gen_sub32_carry32(), true); +} + +static void gen_op_addc_int_generic(TCGv dst, TCGv src1, TCGv src2, + bool update_cc) +{ + TCGv_i32 carry_32 = tcg_temp_new_i32(); + gen_helper_compute_C_icc(carry_32, tcg_env); + gen_op_addc_int(dst, src1, src2, carry_32, update_cc); +} + +static void gen_op_addc_generic(TCGv dst, TCGv src1, TCGv src2) +{ + gen_op_addc_int_generic(dst, src1, src2, false); +} + +static void gen_op_addccc_generic(TCGv dst, TCGv src1, TCGv src2) +{ + gen_op_addc_int_generic(dst, src1, src2, true); +} + static void gen_op_sub_cc(TCGv dst, TCGv src1, TCGv src2) { tcg_gen_mov_tl(cpu_cc_src, src1); @@ -4095,6 +4113,30 @@ static bool trans_OR(DisasContext *dc, arg_r_r_ri_cc *a) return do_logic(dc, a, tcg_gen_or_tl, tcg_gen_ori_tl); } +static bool trans_ADDC(DisasContext *dc, arg_r_r_ri_cc *a) +{ + switch (dc->cc_op) { + case CC_OP_DIV: + case CC_OP_LOGIC: + /* Carry is known to be zero. Fall back to plain ADD. */ + return do_arith(dc, a, CC_OP_ADD, + tcg_gen_add_tl, tcg_gen_addi_tl, gen_op_add_cc); + case CC_OP_ADD: + case CC_OP_TADD: + case CC_OP_TADDTV: + return do_arith(dc, a, CC_OP_ADDX, + gen_op_addc_add, NULL, gen_op_addccc_add); + case CC_OP_SUB: + case CC_OP_TSUB: + case CC_OP_TSUBTV: + return do_arith(dc, a, CC_OP_ADDX, + gen_op_addc_sub, NULL, gen_op_addccc_sub); + default: + return do_arith(dc, a, CC_OP_ADDX, + gen_op_addc_generic, NULL, gen_op_addccc_generic); + } +} + #define CHECK_IU_FEATURE(dc, FEATURE) \ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ goto illegal_insn; @@ -4519,10 +4561,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) cpu_src1 = get_src1(dc, insn); cpu_src2 = get_src2(dc, insn); switch (xop & ~0x10) { - case 0x8: /* addx, V9 addc */ - gen_op_addx_int(dc, cpu_dst, cpu_src1, cpu_src2, - (xop & 0x10)); - break; #ifdef TARGET_SPARC64 case 0x9: /* V9 mulx */ tcg_gen_mul_i64(cpu_dst, cpu_src1, cpu_src2); From 22188d7da819bd07e64f4179417e47dd6555ec29 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 1 Oct 2023 22:46:24 -0700 Subject: [PATCH 049/974] target/sparc: Move MULX to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 3 +++ target/sparc/translate.c | 8 +++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 1cff18fa1f..1a04a8e229 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -31,6 +31,7 @@ CALL 01 i:s30 &r_r_ri_cc rd rs1 rs2_or_imm imm:bool cc:bool @r_r_ri_cc .. rd:5 . cc:1 .... rs1:5 imm:1 rs2_or_imm:s13 &r_r_ri_cc +@r_r_ri_cc0 .. rd:5 ...... rs1:5 imm:1 rs2_or_imm:s13 &r_r_ri_cc cc=0 { [ @@ -165,6 +166,8 @@ ORN 10 ..... 0.0110 ..... . ............. @r_r_ri_cc XORN 10 ..... 0.0111 ..... . ............. @r_r_ri_cc ADDC 10 ..... 0.1000 ..... . ............. @r_r_ri_cc +MULX 10 ..... 001001 ..... . ............. @r_r_ri_cc0 + Tcc_r 10 0 cond:4 111010 rs1:5 0 cc:1 0000000 rs2:5 { # For v7, the entire simm13 field is present, but masked to 7 bits. diff --git a/target/sparc/translate.c b/target/sparc/translate.c index eb829acc68..2ecd9edb33 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -4073,6 +4073,7 @@ static bool do_arith(DisasContext *dc, arg_r_r_ri_cc *a, int cc_op, void (*func_cc)(TCGv, TCGv, TCGv)) { if (a->cc) { + assert(cc_op >= 0); return do_arith_int(dc, a, cc_op, func_cc, NULL); } return do_arith_int(dc, a, cc_op, func, funci); @@ -4096,6 +4097,8 @@ TRANS(ANDN, ALL, do_logic, a, tcg_gen_andc_tl, NULL) TRANS(ORN, ALL, do_logic, a, tcg_gen_orc_tl, NULL) TRANS(XORN, ALL, do_logic, a, tcg_gen_eqv_tl, NULL) +TRANS(MULX, 64, do_arith, a, -1, tcg_gen_mul_tl, tcg_gen_muli_tl, NULL) + static bool trans_OR(DisasContext *dc, arg_r_r_ri_cc *a) { /* OR with %g0 is the canonical alias for MOV. */ @@ -4561,11 +4564,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) cpu_src1 = get_src1(dc, insn); cpu_src2 = get_src2(dc, insn); switch (xop & ~0x10) { -#ifdef TARGET_SPARC64 - case 0x9: /* V9 mulx */ - tcg_gen_mul_i64(cpu_dst, cpu_src1, cpu_src2); - break; -#endif case 0xa: /* umul */ CHECK_IU_FEATURE(dc, MUL); gen_op_umul(cpu_dst, cpu_src1, cpu_src2); From b5372650e2a0d10102124813f7616a78a2370327 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 1 Oct 2023 22:55:04 -0700 Subject: [PATCH 050/974] target/sparc: Move UMUL, SMUL to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 2 ++ target/sparc/translate.c | 22 ++++------------------ 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 1a04a8e229..d6a7256e71 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -167,6 +167,8 @@ XORN 10 ..... 0.0111 ..... . ............. @r_r_ri_cc ADDC 10 ..... 0.1000 ..... . ............. @r_r_ri_cc MULX 10 ..... 001001 ..... . ............. @r_r_ri_cc0 +UMUL 10 ..... 0.1010 ..... . ............. @r_r_ri_cc +SMUL 10 ..... 0.1011 ..... . ............. @r_r_ri_cc Tcc_r 10 0 cond:4 111010 rs1:5 0 cc:1 0000000 rs2:5 { diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 2ecd9edb33..e7c3c68402 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -2888,6 +2888,7 @@ static void gen_faligndata(TCGv dst, TCGv gsr, TCGv s1, TCGv s2) #ifdef TARGET_SPARC64 # define avail_32(C) false # define avail_ASR17(C) false +# define avail_MUL(C) true # define avail_POWERDOWN(C) false # define avail_64(C) true # define avail_GL(C) ((C)->def->features & CPU_FEATURE_GL) @@ -2895,6 +2896,7 @@ static void gen_faligndata(TCGv dst, TCGv gsr, TCGv s1, TCGv s2) #else # define avail_32(C) true # define avail_ASR17(C) ((C)->def->features & CPU_FEATURE_ASR17) +# define avail_MUL(C) ((C)->def->features & CPU_FEATURE_MUL) # define avail_POWERDOWN(C) ((C)->def->features & CPU_FEATURE_POWERDOWN) # define avail_64(C) false # define avail_GL(C) false @@ -4098,6 +4100,8 @@ TRANS(ORN, ALL, do_logic, a, tcg_gen_orc_tl, NULL) TRANS(XORN, ALL, do_logic, a, tcg_gen_eqv_tl, NULL) TRANS(MULX, 64, do_arith, a, -1, tcg_gen_mul_tl, tcg_gen_muli_tl, NULL) +TRANS(UMUL, MUL, do_logic, a, gen_op_umul, NULL) +TRANS(SMUL, MUL, do_logic, a, gen_op_smul, NULL) static bool trans_OR(DisasContext *dc, arg_r_r_ri_cc *a) { @@ -4564,24 +4568,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) cpu_src1 = get_src1(dc, insn); cpu_src2 = get_src2(dc, insn); switch (xop & ~0x10) { - case 0xa: /* umul */ - CHECK_IU_FEATURE(dc, MUL); - gen_op_umul(cpu_dst, cpu_src1, cpu_src2); - if (xop & 0x10) { - tcg_gen_mov_tl(cpu_cc_dst, cpu_dst); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_LOGIC); - dc->cc_op = CC_OP_LOGIC; - } - break; - case 0xb: /* smul */ - CHECK_IU_FEATURE(dc, MUL); - gen_op_smul(cpu_dst, cpu_src1, cpu_src2); - if (xop & 0x10) { - tcg_gen_mov_tl(cpu_cc_dst, cpu_dst); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_LOGIC); - dc->cc_op = CC_OP_LOGIC; - } - break; case 0xc: /* subx, V9 subc */ gen_op_subx_int(dc, cpu_dst, cpu_src1, cpu_src2, (xop & 0x10)); From dfebb950da13e61e1277e6773ae43a7d8f2d2193 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 1 Oct 2023 23:11:50 -0700 Subject: [PATCH 051/974] target/sparc: Move SUBC to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 1 + target/sparc/translate.c | 139 ++++++++++++++++++++++++-------------- 2 files changed, 90 insertions(+), 50 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index d6a7256e71..a188452d2e 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -165,6 +165,7 @@ ANDN 10 ..... 0.0101 ..... . ............. @r_r_ri_cc ORN 10 ..... 0.0110 ..... . ............. @r_r_ri_cc XORN 10 ..... 0.0111 ..... . ............. @r_r_ri_cc ADDC 10 ..... 0.1000 ..... . ............. @r_r_ri_cc +SUBC 10 ..... 0.1100 ..... . ............. @r_r_ri_cc MULX 10 ..... 001001 ..... . ............. @r_r_ri_cc0 UMUL 10 ..... 0.1010 ..... . ............. @r_r_ri_cc diff --git a/target/sparc/translate.c b/target/sparc/translate.c index e7c3c68402..b8fbd18a4c 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -538,51 +538,11 @@ static void gen_op_sub_cc(TCGv dst, TCGv src1, TCGv src2) tcg_gen_mov_tl(dst, cpu_cc_dst); } -static void gen_op_subx_int(DisasContext *dc, TCGv dst, TCGv src1, - TCGv src2, int update_cc) +static void gen_op_subc_int(TCGv dst, TCGv src1, TCGv src2, + TCGv_i32 carry_32, bool update_cc) { - TCGv_i32 carry_32; TCGv carry; - switch (dc->cc_op) { - case CC_OP_DIV: - case CC_OP_LOGIC: - /* Carry is known to be zero. Fall back to plain SUB. */ - if (update_cc) { - gen_op_sub_cc(dst, src1, src2); - } else { - tcg_gen_sub_tl(dst, src1, src2); - } - return; - - case CC_OP_ADD: - case CC_OP_TADD: - case CC_OP_TADDTV: - carry_32 = gen_add32_carry32(); - break; - - case CC_OP_SUB: - case CC_OP_TSUB: - case CC_OP_TSUBTV: - if (TARGET_LONG_BITS == 32) { - /* We can re-use the host's hardware carry generation by using - a SUB2 opcode. We discard the low part of the output. - Ideally we'd combine this operation with the add that - generated the carry in the first place. */ - carry = tcg_temp_new(); - tcg_gen_sub2_tl(carry, dst, cpu_cc_src, src1, cpu_cc_src2, src2); - goto sub_done; - } - carry_32 = gen_sub32_carry32(); - break; - - default: - /* We need external help to produce the carry. */ - carry_32 = tcg_temp_new_i32(); - gen_helper_compute_C_icc(carry_32, tcg_env); - break; - } - #if TARGET_LONG_BITS == 64 carry = tcg_temp_new(); tcg_gen_extu_i32_i64(carry, carry_32); @@ -593,16 +553,75 @@ static void gen_op_subx_int(DisasContext *dc, TCGv dst, TCGv src1, tcg_gen_sub_tl(dst, src1, src2); tcg_gen_sub_tl(dst, dst, carry); - sub_done: if (update_cc) { + tcg_debug_assert(dst == cpu_cc_dst); tcg_gen_mov_tl(cpu_cc_src, src1); tcg_gen_mov_tl(cpu_cc_src2, src2); - tcg_gen_mov_tl(cpu_cc_dst, dst); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_SUBX); - dc->cc_op = CC_OP_SUBX; } } +static void gen_op_subc_add(TCGv dst, TCGv src1, TCGv src2) +{ + gen_op_subc_int(dst, src1, src2, gen_add32_carry32(), false); +} + +static void gen_op_subccc_add(TCGv dst, TCGv src1, TCGv src2) +{ + gen_op_subc_int(dst, src1, src2, gen_add32_carry32(), true); +} + +static void gen_op_subc_int_sub(TCGv dst, TCGv src1, TCGv src2, bool update_cc) +{ + TCGv discard; + + if (TARGET_LONG_BITS == 64) { + gen_op_subc_int(dst, src1, src2, gen_sub32_carry32(), update_cc); + return; + } + + /* + * We can re-use the host's hardware carry generation by using + * a SUB2 opcode. We discard the low part of the output. + */ + discard = tcg_temp_new(); + tcg_gen_sub2_tl(discard, dst, cpu_cc_src, src1, cpu_cc_src2, src2); + + if (update_cc) { + tcg_debug_assert(dst == cpu_cc_dst); + tcg_gen_mov_tl(cpu_cc_src, src1); + tcg_gen_mov_tl(cpu_cc_src2, src2); + } +} + +static void gen_op_subc_sub(TCGv dst, TCGv src1, TCGv src2) +{ + gen_op_subc_int_sub(dst, src1, src2, false); +} + +static void gen_op_subccc_sub(TCGv dst, TCGv src1, TCGv src2) +{ + gen_op_subc_int_sub(dst, src1, src2, true); +} + +static void gen_op_subc_int_generic(TCGv dst, TCGv src1, TCGv src2, + bool update_cc) +{ + TCGv_i32 carry_32 = tcg_temp_new_i32(); + + gen_helper_compute_C_icc(carry_32, tcg_env); + gen_op_subc_int(dst, src1, src2, carry_32, update_cc); +} + +static void gen_op_subc_generic(TCGv dst, TCGv src1, TCGv src2) +{ + gen_op_subc_int_generic(dst, src1, src2, false); +} + +static void gen_op_subccc_generic(TCGv dst, TCGv src1, TCGv src2) +{ + gen_op_subc_int_generic(dst, src1, src2, true); +} + static void gen_op_mulscc(TCGv dst, TCGv src1, TCGv src2) { TCGv r_temp, zero, t0; @@ -4144,6 +4163,30 @@ static bool trans_ADDC(DisasContext *dc, arg_r_r_ri_cc *a) } } +static bool trans_SUBC(DisasContext *dc, arg_r_r_ri_cc *a) +{ + switch (dc->cc_op) { + case CC_OP_DIV: + case CC_OP_LOGIC: + /* Carry is known to be zero. Fall back to plain SUB. */ + return do_arith(dc, a, CC_OP_SUB, + tcg_gen_sub_tl, tcg_gen_subi_tl, gen_op_sub_cc); + case CC_OP_ADD: + case CC_OP_TADD: + case CC_OP_TADDTV: + return do_arith(dc, a, CC_OP_SUBX, + gen_op_subc_add, NULL, gen_op_subccc_add); + case CC_OP_SUB: + case CC_OP_TSUB: + case CC_OP_TSUBTV: + return do_arith(dc, a, CC_OP_SUBX, + gen_op_subc_sub, NULL, gen_op_subccc_sub); + default: + return do_arith(dc, a, CC_OP_SUBX, + gen_op_subc_generic, NULL, gen_op_subccc_generic); + } +} + #define CHECK_IU_FEATURE(dc, FEATURE) \ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ goto illegal_insn; @@ -4568,10 +4611,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) cpu_src1 = get_src1(dc, insn); cpu_src2 = get_src2(dc, insn); switch (xop & ~0x10) { - case 0xc: /* subx, V9 subc */ - gen_op_subx_int(dc, cpu_dst, cpu_src1, cpu_src2, - (xop & 0x10)); - break; #ifdef TARGET_SPARC64 case 0xd: /* V9 udivx */ gen_helper_udivx(cpu_dst, tcg_env, cpu_src1, cpu_src2); From 4ee85ea94bc3c6f34231590467ac7988386913e9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 1 Oct 2023 23:34:14 -0700 Subject: [PATCH 052/974] target/sparc: Move UDIVX, SDIVX to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 3 +++ target/sparc/translate.c | 24 +++++++++++++++--------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index a188452d2e..dd0ed3a993 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -171,6 +171,9 @@ MULX 10 ..... 001001 ..... . ............. @r_r_ri_cc0 UMUL 10 ..... 0.1010 ..... . ............. @r_r_ri_cc SMUL 10 ..... 0.1011 ..... . ............. @r_r_ri_cc +UDIVX 10 ..... 001101 ..... . ............. @r_r_ri_cc0 +SDIVX 10 ..... 101101 ..... . ............. @r_r_ri_cc0 + Tcc_r 10 0 cond:4 111010 rs1:5 0 cc:1 0000000 rs2:5 { # For v7, the entire simm13 field is present, but masked to 7 bits. diff --git a/target/sparc/translate.c b/target/sparc/translate.c index b8fbd18a4c..826ba0441b 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -47,10 +47,12 @@ # define gen_helper_rdcwp(D, E) qemu_build_not_reached() # define gen_helper_restored(E) qemu_build_not_reached() # define gen_helper_saved(E) qemu_build_not_reached() +# define gen_helper_sdivx(D, E, A, B) qemu_build_not_reached() # define gen_helper_set_softint(E, S) qemu_build_not_reached() # define gen_helper_tick_get_count(D, E, T, C) qemu_build_not_reached() # define gen_helper_tick_set_count(P, S) qemu_build_not_reached() # define gen_helper_tick_set_limit(P, S) qemu_build_not_reached() +# define gen_helper_udivx(D, E, A, B) qemu_build_not_reached() # define gen_helper_wrccr(E, S) qemu_build_not_reached() # define gen_helper_wrcwp(E, S) qemu_build_not_reached() # define gen_helper_wrgl(E, S) qemu_build_not_reached() @@ -698,6 +700,16 @@ static void gen_op_smul(TCGv dst, TCGv src1, TCGv src2) gen_op_multiply(dst, src1, src2, 1); } +static void gen_op_udivx(TCGv dst, TCGv src1, TCGv src2) +{ + gen_helper_udivx(dst, tcg_env, src1, src2); +} + +static void gen_op_sdivx(TCGv dst, TCGv src1, TCGv src2) +{ + gen_helper_sdivx(dst, tcg_env, src1, src2); +} + // 1 static void gen_op_eval_ba(TCGv dst) { @@ -4122,6 +4134,9 @@ TRANS(MULX, 64, do_arith, a, -1, tcg_gen_mul_tl, tcg_gen_muli_tl, NULL) TRANS(UMUL, MUL, do_logic, a, gen_op_umul, NULL) TRANS(SMUL, MUL, do_logic, a, gen_op_smul, NULL) +TRANS(UDIVX, 64, do_arith, a, -1, gen_op_udivx, NULL, NULL) +TRANS(SDIVX, 64, do_arith, a, -1, gen_op_sdivx, NULL, NULL) + static bool trans_OR(DisasContext *dc, arg_r_r_ri_cc *a) { /* OR with %g0 is the canonical alias for MOV. */ @@ -4611,11 +4626,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) cpu_src1 = get_src1(dc, insn); cpu_src2 = get_src2(dc, insn); switch (xop & ~0x10) { -#ifdef TARGET_SPARC64 - case 0xd: /* V9 udivx */ - gen_helper_udivx(cpu_dst, tcg_env, cpu_src1, cpu_src2); - break; -#endif case 0xe: /* udiv */ CHECK_IU_FEATURE(dc, DIV); if (xop & 0x10) { @@ -4753,10 +4763,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) gen_store_gpr(dc, rd, dst); break; } - case 0x2d: /* V9 sdivx */ - gen_helper_sdivx(cpu_dst, tcg_env, cpu_src1, cpu_src2); - gen_store_gpr(dc, rd, cpu_dst); - break; case 0x2e: /* V9 popc */ tcg_gen_ctpop_tl(cpu_dst, cpu_src2); gen_store_gpr(dc, rd, cpu_dst); From c26368532dca4670aa55fcecc9c7fb100cc30319 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 1 Oct 2023 23:46:47 -0700 Subject: [PATCH 053/974] target/sparc: Move UDIV, SDIV to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/helper.c | 4 --- target/sparc/insns.decode | 2 ++ target/sparc/translate.c | 54 ++++++++++++++++++--------------------- 3 files changed, 27 insertions(+), 33 deletions(-) diff --git a/target/sparc/helper.c b/target/sparc/helper.c index c4358bba84..e25fdaeedd 100644 --- a/target/sparc/helper.c +++ b/target/sparc/helper.c @@ -102,9 +102,7 @@ static target_ulong do_udiv(CPUSPARCState *env, target_ulong a, } if (cc) { - env->cc_dst = x0; env->cc_src2 = overflow; - env->cc_op = CC_OP_DIV; } return x0; } @@ -143,9 +141,7 @@ static target_ulong do_sdiv(CPUSPARCState *env, target_ulong a, } if (cc) { - env->cc_dst = x0; env->cc_src2 = overflow; - env->cc_op = CC_OP_DIV; } return x0; } diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index dd0ed3a993..4415d03858 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -173,6 +173,8 @@ SMUL 10 ..... 0.1011 ..... . ............. @r_r_ri_cc UDIVX 10 ..... 001101 ..... . ............. @r_r_ri_cc0 SDIVX 10 ..... 101101 ..... . ............. @r_r_ri_cc0 +UDIV 10 ..... 0.1110 ..... . ............. @r_r_ri_cc +SDIV 10 ..... 0.1111 ..... . ............. @r_r_ri_cc Tcc_r 10 0 cond:4 111010 rs1:5 0 cc:1 0000000 rs2:5 { diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 826ba0441b..1ebaaa1114 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -710,6 +710,26 @@ static void gen_op_sdivx(TCGv dst, TCGv src1, TCGv src2) gen_helper_sdivx(dst, tcg_env, src1, src2); } +static void gen_op_udiv(TCGv dst, TCGv src1, TCGv src2) +{ + gen_helper_udiv(dst, tcg_env, src1, src2); +} + +static void gen_op_sdiv(TCGv dst, TCGv src1, TCGv src2) +{ + gen_helper_sdiv(dst, tcg_env, src1, src2); +} + +static void gen_op_udivcc(TCGv dst, TCGv src1, TCGv src2) +{ + gen_helper_udiv_cc(dst, tcg_env, src1, src2); +} + +static void gen_op_sdivcc(TCGv dst, TCGv src1, TCGv src2) +{ + gen_helper_sdiv_cc(dst, tcg_env, src1, src2); +} + // 1 static void gen_op_eval_ba(TCGv dst) { @@ -2919,6 +2939,7 @@ static void gen_faligndata(TCGv dst, TCGv gsr, TCGv s1, TCGv s2) #ifdef TARGET_SPARC64 # define avail_32(C) false # define avail_ASR17(C) false +# define avail_DIV(C) true # define avail_MUL(C) true # define avail_POWERDOWN(C) false # define avail_64(C) true @@ -2927,6 +2948,7 @@ static void gen_faligndata(TCGv dst, TCGv gsr, TCGv s1, TCGv s2) #else # define avail_32(C) true # define avail_ASR17(C) ((C)->def->features & CPU_FEATURE_ASR17) +# define avail_DIV(C) ((C)->def->features & CPU_FEATURE_DIV) # define avail_MUL(C) ((C)->def->features & CPU_FEATURE_MUL) # define avail_POWERDOWN(C) ((C)->def->features & CPU_FEATURE_POWERDOWN) # define avail_64(C) false @@ -4136,6 +4158,8 @@ TRANS(SMUL, MUL, do_logic, a, gen_op_smul, NULL) TRANS(UDIVX, 64, do_arith, a, -1, gen_op_udivx, NULL, NULL) TRANS(SDIVX, 64, do_arith, a, -1, gen_op_sdivx, NULL, NULL) +TRANS(UDIV, DIV, do_arith, a, CC_OP_DIV, gen_op_udiv, NULL, gen_op_udivcc) +TRANS(SDIV, DIV, do_arith, a, CC_OP_DIV, gen_op_sdiv, NULL, gen_op_sdivcc) static bool trans_OR(DisasContext *dc, arg_r_r_ri_cc *a) { @@ -4623,35 +4647,7 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) #endif } else if (xop < 0x36) { if (xop < 0x20) { - cpu_src1 = get_src1(dc, insn); - cpu_src2 = get_src2(dc, insn); - switch (xop & ~0x10) { - case 0xe: /* udiv */ - CHECK_IU_FEATURE(dc, DIV); - if (xop & 0x10) { - gen_helper_udiv_cc(cpu_dst, tcg_env, cpu_src1, - cpu_src2); - dc->cc_op = CC_OP_DIV; - } else { - gen_helper_udiv(cpu_dst, tcg_env, cpu_src1, - cpu_src2); - } - break; - case 0xf: /* sdiv */ - CHECK_IU_FEATURE(dc, DIV); - if (xop & 0x10) { - gen_helper_sdiv_cc(cpu_dst, tcg_env, cpu_src1, - cpu_src2); - dc->cc_op = CC_OP_DIV; - } else { - gen_helper_sdiv(cpu_dst, tcg_env, cpu_src1, - cpu_src2); - } - break; - default: - goto illegal_insn; - } - gen_store_gpr(dc, rd, cpu_dst); + goto illegal_insn; } else { cpu_src1 = get_src1(dc, insn); cpu_src2 = get_src2(dc, insn); From a9aba13dae346cc84391b06c620c6d41b09eb53c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 2 Oct 2023 14:40:04 -0700 Subject: [PATCH 054/974] target/sparc: Move TADD, TSUB, MULS to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/helper.c | 4 ---- target/sparc/insns.decode | 7 ++++++ target/sparc/translate.c | 48 ++++++++++++++++++--------------------- 3 files changed, 29 insertions(+), 30 deletions(-) diff --git a/target/sparc/helper.c b/target/sparc/helper.c index e25fdaeedd..2bcdc81d54 100644 --- a/target/sparc/helper.c +++ b/target/sparc/helper.c @@ -198,10 +198,8 @@ target_ulong helper_taddcctv(CPUSPARCState *env, target_ulong src1, } /* Only modify the CC after any exceptions have been generated. */ - env->cc_op = CC_OP_TADDTV; env->cc_src = src1; env->cc_src2 = src2; - env->cc_dst = dst; return dst; tag_overflow: @@ -226,10 +224,8 @@ target_ulong helper_tsubcctv(CPUSPARCState *env, target_ulong src1, } /* Only modify the CC after any exceptions have been generated. */ - env->cc_op = CC_OP_TSUBTV; env->cc_src = src1; env->cc_src2 = src2; - env->cc_dst = dst; return dst; tag_overflow: diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 4415d03858..b35921aabb 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -32,6 +32,7 @@ CALL 01 i:s30 &r_r_ri_cc rd rs1 rs2_or_imm imm:bool cc:bool @r_r_ri_cc .. rd:5 . cc:1 .... rs1:5 imm:1 rs2_or_imm:s13 &r_r_ri_cc @r_r_ri_cc0 .. rd:5 ...... rs1:5 imm:1 rs2_or_imm:s13 &r_r_ri_cc cc=0 +@r_r_ri_cc1 .. rd:5 ...... rs1:5 imm:1 rs2_or_imm:s13 &r_r_ri_cc cc=1 { [ @@ -170,12 +171,18 @@ SUBC 10 ..... 0.1100 ..... . ............. @r_r_ri_cc MULX 10 ..... 001001 ..... . ............. @r_r_ri_cc0 UMUL 10 ..... 0.1010 ..... . ............. @r_r_ri_cc SMUL 10 ..... 0.1011 ..... . ............. @r_r_ri_cc +MULScc 10 ..... 100100 ..... . ............. @r_r_ri_cc1 UDIVX 10 ..... 001101 ..... . ............. @r_r_ri_cc0 SDIVX 10 ..... 101101 ..... . ............. @r_r_ri_cc0 UDIV 10 ..... 0.1110 ..... . ............. @r_r_ri_cc SDIV 10 ..... 0.1111 ..... . ............. @r_r_ri_cc +TADDcc 10 ..... 100000 ..... . ............. @r_r_ri_cc1 +TSUBcc 10 ..... 100001 ..... . ............. @r_r_ri_cc1 +TADDccTV 10 ..... 100010 ..... . ............. @r_r_ri_cc1 +TSUBccTV 10 ..... 100011 ..... . ............. @r_r_ri_cc1 + Tcc_r 10 0 cond:4 111010 rs1:5 0 cc:1 0000000 rs2:5 { # For v7, the entire simm13 field is present, but masked to 7 bits. diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 1ebaaa1114..861c6e8f1e 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -730,6 +730,16 @@ static void gen_op_sdivcc(TCGv dst, TCGv src1, TCGv src2) gen_helper_sdiv_cc(dst, tcg_env, src1, src2); } +static void gen_op_taddcctv(TCGv dst, TCGv src1, TCGv src2) +{ + gen_helper_taddcctv(dst, tcg_env, src1, src2); +} + +static void gen_op_tsubcctv(TCGv dst, TCGv src1, TCGv src2) +{ + gen_helper_tsubcctv(dst, tcg_env, src1, src2); +} + // 1 static void gen_op_eval_ba(TCGv dst) { @@ -4146,6 +4156,11 @@ TRANS(ADD, ALL, do_arith, a, CC_OP_ADD, TRANS(SUB, ALL, do_arith, a, CC_OP_SUB, tcg_gen_sub_tl, tcg_gen_subi_tl, gen_op_sub_cc) +TRANS(TADDcc, ALL, do_arith, a, CC_OP_TADD, NULL, NULL, gen_op_add_cc) +TRANS(TSUBcc, ALL, do_arith, a, CC_OP_TSUB, NULL, NULL, gen_op_sub_cc) +TRANS(TADDccTV, ALL, do_arith, a, CC_OP_TADDTV, NULL, NULL, gen_op_taddcctv) +TRANS(TSUBccTV, ALL, do_arith, a, CC_OP_TSUBTV, NULL, NULL, gen_op_tsubcctv) + TRANS(AND, ALL, do_logic, a, tcg_gen_and_tl, tcg_gen_andi_tl) TRANS(XOR, ALL, do_logic, a, tcg_gen_xor_tl, tcg_gen_xori_tl) TRANS(ANDN, ALL, do_logic, a, tcg_gen_andc_tl, NULL) @@ -4226,6 +4241,12 @@ static bool trans_SUBC(DisasContext *dc, arg_r_r_ri_cc *a) } } +static bool trans_MULScc(DisasContext *dc, arg_r_r_ri_cc *a) +{ + update_psr(dc); + return do_arith(dc, a, CC_OP_ADD, NULL, NULL, gen_op_mulscc); +} + #define CHECK_IU_FEATURE(dc, FEATURE) \ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ goto illegal_insn; @@ -4653,36 +4674,11 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) cpu_src2 = get_src2(dc, insn); switch (xop) { case 0x20: /* taddcc */ - gen_op_add_cc(cpu_dst, cpu_src1, cpu_src2); - gen_store_gpr(dc, rd, cpu_dst); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_TADD); - dc->cc_op = CC_OP_TADD; - break; case 0x21: /* tsubcc */ - gen_op_sub_cc(cpu_dst, cpu_src1, cpu_src2); - gen_store_gpr(dc, rd, cpu_dst); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_TSUB); - dc->cc_op = CC_OP_TSUB; - break; case 0x22: /* taddcctv */ - gen_helper_taddcctv(cpu_dst, tcg_env, - cpu_src1, cpu_src2); - gen_store_gpr(dc, rd, cpu_dst); - dc->cc_op = CC_OP_TADDTV; - break; case 0x23: /* tsubcctv */ - gen_helper_tsubcctv(cpu_dst, tcg_env, - cpu_src1, cpu_src2); - gen_store_gpr(dc, rd, cpu_dst); - dc->cc_op = CC_OP_TSUBTV; - break; case 0x24: /* mulscc */ - update_psr(dc); - gen_op_mulscc(cpu_dst, cpu_src1, cpu_src2); - gen_store_gpr(dc, rd, cpu_dst); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_ADD); - dc->cc_op = CC_OP_ADD; - break; + goto illegal_insn; /* in decodetree */ #ifndef TARGET_SPARC64 case 0x25: /* sll */ if (IS_IMM) { /* immediate */ From 5fc546ee3559ae4655e440c3f92f933c6854ca5f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 2 Oct 2023 14:48:10 -0700 Subject: [PATCH 055/974] target/sparc: Move SLL, SRL, SRA to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 14 +++ target/sparc/translate.c | 182 ++++++++++++++++---------------------- 2 files changed, 92 insertions(+), 104 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index b35921aabb..51cddc826d 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -183,6 +183,20 @@ TSUBcc 10 ..... 100001 ..... . ............. @r_r_ri_cc1 TADDccTV 10 ..... 100010 ..... . ............. @r_r_ri_cc1 TSUBccTV 10 ..... 100011 ..... . ............. @r_r_ri_cc1 +&shiftr rd rs1 rs2 x:bool +@shiftr .. rd:5 ...... rs1:5 . x:1 ....... rs2:5 &shiftr + +SLL_r 10 ..... 100101 ..... 0 . 0000000 ..... @shiftr +SRL_r 10 ..... 100110 ..... 0 . 0000000 ..... @shiftr +SRA_r 10 ..... 100111 ..... 0 . 0000000 ..... @shiftr + +&shifti rd rs1 i x:bool +@shifti .. rd:5 ...... rs1:5 . x:1 ...... i:6 &shifti + +SLL_i 10 ..... 100101 ..... 1 . 000000 ...... @shifti +SRL_i 10 ..... 100110 ..... 1 . 000000 ...... @shifti +SRA_i 10 ..... 100111 ..... 1 . 000000 ...... @shifti + Tcc_r 10 0 cond:4 111010 rs1:5 0 cc:1 0000000 rs2:5 { # For v7, the entire simm13 field is present, but masked to 7 bits. diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 861c6e8f1e..ac8cd40aa1 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -4247,6 +4247,83 @@ static bool trans_MULScc(DisasContext *dc, arg_r_r_ri_cc *a) return do_arith(dc, a, CC_OP_ADD, NULL, NULL, gen_op_mulscc); } +static bool do_shift_r(DisasContext *dc, arg_shiftr *a, bool l, bool u) +{ + TCGv dst, src1, src2; + + /* Reject 64-bit shifts for sparc32. */ + if (avail_32(dc) && a->x) { + return false; + } + + src2 = tcg_temp_new(); + tcg_gen_andi_tl(src2, gen_load_gpr(dc, a->rs2), a->x ? 63 : 31); + src1 = gen_load_gpr(dc, a->rs1); + dst = gen_dest_gpr(dc, a->rd); + + if (l) { + tcg_gen_shl_tl(dst, src1, src2); + if (!a->x) { + tcg_gen_ext32u_tl(dst, dst); + } + } else if (u) { + if (!a->x) { + tcg_gen_ext32u_tl(dst, src1); + src1 = dst; + } + tcg_gen_shr_tl(dst, src1, src2); + } else { + if (!a->x) { + tcg_gen_ext32s_tl(dst, src1); + src1 = dst; + } + tcg_gen_sar_tl(dst, src1, src2); + } + gen_store_gpr(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(SLL_r, ALL, do_shift_r, a, true, true) +TRANS(SRL_r, ALL, do_shift_r, a, false, true) +TRANS(SRA_r, ALL, do_shift_r, a, false, false) + +static bool do_shift_i(DisasContext *dc, arg_shifti *a, bool l, bool u) +{ + TCGv dst, src1; + + /* Reject 64-bit shifts for sparc32. */ + if (avail_32(dc) && (a->x || a->i >= 32)) { + return false; + } + + src1 = gen_load_gpr(dc, a->rs1); + dst = gen_dest_gpr(dc, a->rd); + + if (avail_32(dc) || a->x) { + if (l) { + tcg_gen_shli_tl(dst, src1, a->i); + } else if (u) { + tcg_gen_shri_tl(dst, src1, a->i); + } else { + tcg_gen_sari_tl(dst, src1, a->i); + } + } else { + if (l) { + tcg_gen_deposit_z_tl(dst, src1, a->i, 32 - a->i); + } else if (u) { + tcg_gen_extract_tl(dst, src1, a->i, 32 - a->i); + } else { + tcg_gen_sextract_tl(dst, src1, a->i, 32 - a->i); + } + } + gen_store_gpr(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(SLL_i, ALL, do_shift_i, a, true, true) +TRANS(SRL_i, ALL, do_shift_i, a, false, true) +TRANS(SRA_i, ALL, do_shift_i, a, false, false) + #define CHECK_IU_FEATURE(dc, FEATURE) \ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ goto illegal_insn; @@ -4595,77 +4672,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) default: goto illegal_insn; } -#ifdef TARGET_SPARC64 - } else if (xop == 0x25) { /* sll, V9 sllx */ - cpu_src1 = get_src1(dc, insn); - if (IS_IMM) { /* immediate */ - simm = GET_FIELDs(insn, 20, 31); - if (insn & (1 << 12)) { - tcg_gen_shli_i64(cpu_dst, cpu_src1, simm & 0x3f); - } else { - tcg_gen_shli_i64(cpu_dst, cpu_src1, simm & 0x1f); - } - } else { /* register */ - rs2 = GET_FIELD(insn, 27, 31); - cpu_src2 = gen_load_gpr(dc, rs2); - cpu_tmp0 = tcg_temp_new(); - if (insn & (1 << 12)) { - tcg_gen_andi_i64(cpu_tmp0, cpu_src2, 0x3f); - } else { - tcg_gen_andi_i64(cpu_tmp0, cpu_src2, 0x1f); - } - tcg_gen_shl_i64(cpu_dst, cpu_src1, cpu_tmp0); - } - gen_store_gpr(dc, rd, cpu_dst); - } else if (xop == 0x26) { /* srl, V9 srlx */ - cpu_src1 = get_src1(dc, insn); - if (IS_IMM) { /* immediate */ - simm = GET_FIELDs(insn, 20, 31); - if (insn & (1 << 12)) { - tcg_gen_shri_i64(cpu_dst, cpu_src1, simm & 0x3f); - } else { - tcg_gen_andi_i64(cpu_dst, cpu_src1, 0xffffffffULL); - tcg_gen_shri_i64(cpu_dst, cpu_dst, simm & 0x1f); - } - } else { /* register */ - rs2 = GET_FIELD(insn, 27, 31); - cpu_src2 = gen_load_gpr(dc, rs2); - cpu_tmp0 = tcg_temp_new(); - if (insn & (1 << 12)) { - tcg_gen_andi_i64(cpu_tmp0, cpu_src2, 0x3f); - tcg_gen_shr_i64(cpu_dst, cpu_src1, cpu_tmp0); - } else { - tcg_gen_andi_i64(cpu_tmp0, cpu_src2, 0x1f); - tcg_gen_andi_i64(cpu_dst, cpu_src1, 0xffffffffULL); - tcg_gen_shr_i64(cpu_dst, cpu_dst, cpu_tmp0); - } - } - gen_store_gpr(dc, rd, cpu_dst); - } else if (xop == 0x27) { /* sra, V9 srax */ - cpu_src1 = get_src1(dc, insn); - if (IS_IMM) { /* immediate */ - simm = GET_FIELDs(insn, 20, 31); - if (insn & (1 << 12)) { - tcg_gen_sari_i64(cpu_dst, cpu_src1, simm & 0x3f); - } else { - tcg_gen_ext32s_i64(cpu_dst, cpu_src1); - tcg_gen_sari_i64(cpu_dst, cpu_dst, simm & 0x1f); - } - } else { /* register */ - rs2 = GET_FIELD(insn, 27, 31); - cpu_src2 = gen_load_gpr(dc, rs2); - cpu_tmp0 = tcg_temp_new(); - if (insn & (1 << 12)) { - tcg_gen_andi_i64(cpu_tmp0, cpu_src2, 0x3f); - tcg_gen_sar_i64(cpu_dst, cpu_src1, cpu_tmp0); - } else { - tcg_gen_andi_i64(cpu_tmp0, cpu_src2, 0x1f); - tcg_gen_ext32s_i64(cpu_dst, cpu_src1); - tcg_gen_sar_i64(cpu_dst, cpu_dst, cpu_tmp0); - } - } - gen_store_gpr(dc, rd, cpu_dst); -#endif } else if (xop < 0x36) { if (xop < 0x20) { goto illegal_insn; @@ -4678,42 +4684,10 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x22: /* taddcctv */ case 0x23: /* tsubcctv */ case 0x24: /* mulscc */ - goto illegal_insn; /* in decodetree */ -#ifndef TARGET_SPARC64 case 0x25: /* sll */ - if (IS_IMM) { /* immediate */ - simm = GET_FIELDs(insn, 20, 31); - tcg_gen_shli_tl(cpu_dst, cpu_src1, simm & 0x1f); - } else { /* register */ - cpu_tmp0 = tcg_temp_new(); - tcg_gen_andi_tl(cpu_tmp0, cpu_src2, 0x1f); - tcg_gen_shl_tl(cpu_dst, cpu_src1, cpu_tmp0); - } - gen_store_gpr(dc, rd, cpu_dst); - break; case 0x26: /* srl */ - if (IS_IMM) { /* immediate */ - simm = GET_FIELDs(insn, 20, 31); - tcg_gen_shri_tl(cpu_dst, cpu_src1, simm & 0x1f); - } else { /* register */ - cpu_tmp0 = tcg_temp_new(); - tcg_gen_andi_tl(cpu_tmp0, cpu_src2, 0x1f); - tcg_gen_shr_tl(cpu_dst, cpu_src1, cpu_tmp0); - } - gen_store_gpr(dc, rd, cpu_dst); - break; case 0x27: /* sra */ - if (IS_IMM) { /* immediate */ - simm = GET_FIELDs(insn, 20, 31); - tcg_gen_sari_tl(cpu_dst, cpu_src1, simm & 0x1f); - } else { /* register */ - cpu_tmp0 = tcg_temp_new(); - tcg_gen_andi_tl(cpu_tmp0, cpu_src2, 0x1f); - tcg_gen_sar_tl(cpu_dst, cpu_src1, cpu_tmp0); - } - gen_store_gpr(dc, rd, cpu_dst); - break; -#endif + goto illegal_insn; /* in decodetree */ case 0x30: goto illegal_insn; /* WRASR in decodetree */ case 0x32: From fb4ed7aad4a5e351e3c4478d576e04605cacb264 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 4 Oct 2023 15:37:54 -0700 Subject: [PATCH 056/974] target/sparc: Move MOVcc, MOVR to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 4 ++ target/sparc/translate.c | 116 ++++++++++++++++++++------------------ 2 files changed, 64 insertions(+), 56 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 51cddc826d..cb21405e1a 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -209,3 +209,7 @@ Tcc_r 10 0 cond:4 111010 rs1:5 0 cc:1 0000000 rs2:5 # Bits [10:8] are reserved and the OSA2011 manual says they must be 0. Tcc_i_v9 10 0 cond:4 111010 rs1:5 1 cc:1 0 000 i:8 } + +MOVcc 10 rd:5 101100 1 cond:4 imm:1 cc:1 0 rs2_or_imm:s11 +MOVfcc 10 rd:5 101100 0 cond:4 imm:1 cc:2 rs2_or_imm:s11 +MOVR 10 rd:5 101111 rs1:5 imm:1 cond:3 rs2_or_imm:s10 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index ac8cd40aa1..10b2d1b578 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -4324,6 +4324,64 @@ TRANS(SLL_i, ALL, do_shift_i, a, true, true) TRANS(SRL_i, ALL, do_shift_i, a, false, true) TRANS(SRA_i, ALL, do_shift_i, a, false, false) +static TCGv gen_rs2_or_imm(DisasContext *dc, bool imm, int rs2_or_imm) +{ + /* For simplicity, we under-decoded the rs2 form. */ + if (!imm && rs2_or_imm & ~0x1f) { + return NULL; + } + if (imm || rs2_or_imm == 0) { + return tcg_constant_tl(rs2_or_imm); + } else { + return cpu_regs[rs2_or_imm]; + } +} + +static bool do_mov_cond(DisasContext *dc, DisasCompare *cmp, int rd, TCGv src2) +{ + TCGv dst = gen_load_gpr(dc, rd); + + tcg_gen_movcond_tl(cmp->cond, dst, cmp->c1, cmp->c2, src2, dst); + gen_store_gpr(dc, rd, dst); + return advance_pc(dc); +} + +static bool trans_MOVcc(DisasContext *dc, arg_MOVcc *a) +{ + TCGv src2 = gen_rs2_or_imm(dc, a->imm, a->rs2_or_imm); + DisasCompare cmp; + + if (src2 == NULL) { + return false; + } + gen_compare(&cmp, a->cc, a->cond, dc); + return do_mov_cond(dc, &cmp, a->rd, src2); +} + +static bool trans_MOVfcc(DisasContext *dc, arg_MOVfcc *a) +{ + TCGv src2 = gen_rs2_or_imm(dc, a->imm, a->rs2_or_imm); + DisasCompare cmp; + + if (src2 == NULL) { + return false; + } + gen_fcompare(&cmp, a->cc, a->cond); + return do_mov_cond(dc, &cmp, a->rd, src2); +} + +static bool trans_MOVR(DisasContext *dc, arg_MOVR *a) +{ + TCGv src2 = gen_rs2_or_imm(dc, a->imm, a->rs2_or_imm); + DisasCompare cmp; + + if (src2 == NULL) { + return false; + } + gen_compare_reg(&cmp, a->cond, gen_load_gpr(dc, a->rs1)); + return do_mov_cond(dc, &cmp, a->rd, src2); +} + #define CHECK_IU_FEATURE(dc, FEATURE) \ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ goto illegal_insn; @@ -4696,66 +4754,12 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) goto illegal_insn; /* WRTBR, WRHPR in decodetree */ #ifdef TARGET_SPARC64 case 0x2c: /* V9 movcc */ - { - int cc = GET_FIELD_SP(insn, 11, 12); - int cond = GET_FIELD_SP(insn, 14, 17); - DisasCompare cmp; - TCGv dst; - - if (insn & (1 << 18)) { - if (cc == 0) { - gen_compare(&cmp, 0, cond, dc); - } else if (cc == 2) { - gen_compare(&cmp, 1, cond, dc); - } else { - goto illegal_insn; - } - } else { - gen_fcompare(&cmp, cc, cond); - } - - /* The get_src2 above loaded the normal 13-bit - immediate field, not the 11-bit field we have - in movcc. But it did handle the reg case. */ - if (IS_IMM) { - simm = GET_FIELD_SPs(insn, 0, 10); - tcg_gen_movi_tl(cpu_src2, simm); - } - - dst = gen_load_gpr(dc, rd); - tcg_gen_movcond_tl(cmp.cond, dst, - cmp.c1, cmp.c2, - cpu_src2, dst); - gen_store_gpr(dc, rd, dst); - break; - } + case 0x2f: /* V9 movr */ + goto illegal_insn; /* in decodetree */ case 0x2e: /* V9 popc */ tcg_gen_ctpop_tl(cpu_dst, cpu_src2); gen_store_gpr(dc, rd, cpu_dst); break; - case 0x2f: /* V9 movr */ - { - int cond = GET_FIELD_SP(insn, 10, 12); - DisasCompare cmp; - TCGv dst; - - gen_compare_reg(&cmp, cond, cpu_src1); - - /* The get_src2 above loaded the normal 13-bit - immediate field, not the 10-bit field we have - in movr. But it did handle the reg case. */ - if (IS_IMM) { - simm = GET_FIELD_SPs(insn, 0, 9); - tcg_gen_movi_tl(cpu_src2, simm); - } - - dst = gen_load_gpr(dc, rd); - tcg_gen_movcond_tl(cmp.cond, dst, - cmp.c1, cmp.c2, - cpu_src2, dst); - gen_store_gpr(dc, rd, dst); - break; - } #endif default: goto illegal_insn; From 9c6ec5bcadc24927eef182bf63a333c153bf9a5d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 20 Oct 2023 21:28:43 -0700 Subject: [PATCH 057/974] target/sparc: Move POPC to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 3 +++ target/sparc/translate.c | 56 +++++++-------------------------------- 2 files changed, 12 insertions(+), 47 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index cb21405e1a..a596b0fc85 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -183,6 +183,9 @@ TSUBcc 10 ..... 100001 ..... . ............. @r_r_ri_cc1 TADDccTV 10 ..... 100010 ..... . ............. @r_r_ri_cc1 TSUBccTV 10 ..... 100011 ..... . ............. @r_r_ri_cc1 +POPC 10 rd:5 101110 00000 imm:1 rs2_or_imm:s13 \ + &r_r_ri_cc rs1=0 cc=0 + &shiftr rd rs1 rs2 x:bool @shiftr .. rd:5 ...... rs1:5 . x:1 ....... rs2:5 &shiftr diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 10b2d1b578..9122f82844 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -740,6 +740,11 @@ static void gen_op_tsubcctv(TCGv dst, TCGv src1, TCGv src2) gen_helper_tsubcctv(dst, tcg_env, src1, src2); } +static void gen_op_popc(TCGv dst, TCGv src1, TCGv src2) +{ + tcg_gen_ctpop_tl(dst, src2); +} + // 1 static void gen_op_eval_ba(TCGv dst) { @@ -2733,19 +2738,6 @@ static TCGv get_src1(DisasContext *dc, unsigned int insn) return gen_load_gpr(dc, rs1); } -static TCGv get_src2(DisasContext *dc, unsigned int insn) -{ - if (IS_IMM) { /* immediate */ - target_long simm = GET_FIELDs(insn, 19, 31); - TCGv t = tcg_temp_new(); - tcg_gen_movi_tl(t, simm); - return t; - } else { /* register */ - unsigned int rs2 = GET_FIELD(insn, 27, 31); - return gen_load_gpr(dc, rs2); - } -} - #ifdef TARGET_SPARC64 static void gen_fmovs(DisasContext *dc, DisasCompare *cmp, int rd, int rs) { @@ -4176,6 +4168,9 @@ TRANS(SDIVX, 64, do_arith, a, -1, gen_op_sdivx, NULL, NULL) TRANS(UDIV, DIV, do_arith, a, CC_OP_DIV, gen_op_udiv, NULL, gen_op_udivcc) TRANS(SDIV, DIV, do_arith, a, CC_OP_DIV, gen_op_sdiv, NULL, gen_op_sdivcc) +/* TODO: Should have feature bit -- comes in with UltraSparc T2. */ +TRANS(POPC, 64, do_arith, a, -1, gen_op_popc, NULL, NULL) + static bool trans_OR(DisasContext *dc, arg_r_r_ri_cc *a) { /* OR with %g0 is the canonical alias for MOV. */ @@ -4731,40 +4726,7 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) goto illegal_insn; } } else if (xop < 0x36) { - if (xop < 0x20) { - goto illegal_insn; - } else { - cpu_src1 = get_src1(dc, insn); - cpu_src2 = get_src2(dc, insn); - switch (xop) { - case 0x20: /* taddcc */ - case 0x21: /* tsubcc */ - case 0x22: /* taddcctv */ - case 0x23: /* tsubcctv */ - case 0x24: /* mulscc */ - case 0x25: /* sll */ - case 0x26: /* srl */ - case 0x27: /* sra */ - goto illegal_insn; /* in decodetree */ - case 0x30: - goto illegal_insn; /* WRASR in decodetree */ - case 0x32: - goto illegal_insn; /* WRPR in decodetree */ - case 0x33: /* wrtbr, UA2005 wrhpr */ - goto illegal_insn; /* WRTBR, WRHPR in decodetree */ -#ifdef TARGET_SPARC64 - case 0x2c: /* V9 movcc */ - case 0x2f: /* V9 movr */ - goto illegal_insn; /* in decodetree */ - case 0x2e: /* V9 popc */ - tcg_gen_ctpop_tl(cpu_dst, cpu_src2); - gen_store_gpr(dc, rd, cpu_dst); - break; -#endif - default: - goto illegal_insn; - } - } + goto illegal_insn; /* in decodetree */ } else if (xop == 0x36) { /* UltraSparc shutdown, VIS, V8 CPop1 */ #ifdef TARGET_SPARC64 int opf = GET_FIELD_SP(insn, 5, 13); From d3c7e8ad74be9b728fb6eae8c6aa18c0a94a3ebb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 4 Oct 2023 12:43:44 -0700 Subject: [PATCH 058/974] target/sparc: Convert remaining v8 coproc insns to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 11 +++++++++++ target/sparc/translate.c | 32 ++++++-------------------------- 2 files changed, 17 insertions(+), 26 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index a596b0fc85..8de986f0bb 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -216,3 +216,14 @@ Tcc_r 10 0 cond:4 111010 rs1:5 0 cc:1 0000000 rs2:5 MOVcc 10 rd:5 101100 1 cond:4 imm:1 cc:1 0 rs2_or_imm:s11 MOVfcc 10 rd:5 101100 0 cond:4 imm:1 cc:2 rs2_or_imm:s11 MOVR 10 rd:5 101111 rs1:5 imm:1 cond:3 rs2_or_imm:s10 + +NCP 10 ----- 110110 ----- --------- ----- # v8 CPop1 +NCP 10 ----- 110111 ----- --------- ----- # v8 CPop2 + +NCP 11 ----- 110000 ----- --------- ----- # v8 LDC +NCP 11 ----- 110001 ----- --------- ----- # v8 LDCSR +NCP 11 ----- 110011 ----- --------- ----- # v8 LDDC +NCP 11 ----- 110100 ----- --------- ----- # v8 STC +NCP 11 ----- 110101 ----- --------- ----- # v8 STCSR +NCP 11 ----- 110110 ----- --------- ----- # v8 STDCQ +NCP 11 ----- 110111 ----- --------- ----- # v8 STDC diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 9122f82844..7cd1af1014 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -4727,8 +4727,9 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) } } else if (xop < 0x36) { goto illegal_insn; /* in decodetree */ - } else if (xop == 0x36) { /* UltraSparc shutdown, VIS, V8 CPop1 */ + } else if (xop == 0x36) { #ifdef TARGET_SPARC64 + /* VIS */ int opf = GET_FIELD_SP(insn, 5, 13); rs1 = GET_FIELD(insn, 13, 17); rs2 = GET_FIELD(insn, 27, 31); @@ -5168,14 +5169,11 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) goto illegal_insn; } #else - goto ncp_insn; -#endif - } else if (xop == 0x37) { /* V8 CPop2, V9 impdep2 */ -#ifdef TARGET_SPARC64 - goto illegal_insn; -#else - goto ncp_insn; + g_assert_not_reached(); /* in decodetree */ #endif + } else if (xop == 0x37) { + /* V8 CPop2, V9 impdep2 */ + goto illegal_insn; /* in decodetree */ #ifdef TARGET_SPARC64 } else if (xop == 0x39) { /* V9 return */ save_state(dc); @@ -5391,13 +5389,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) cpu_src1 = gen_load_gpr(dc, rd); gen_swap_asi(dc, cpu_val, cpu_src1, cpu_addr, insn); break; - -#ifndef TARGET_SPARC64 - case 0x30: /* ldc */ - case 0x31: /* ldcsr */ - case 0x33: /* lddc */ - goto ncp_insn; -#endif #endif #ifdef TARGET_SPARC64 case 0x08: /* V9 ldsw */ @@ -5658,12 +5649,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) cpu_src2 = gen_load_gpr(dc, rs2); gen_casx_asi(dc, cpu_addr, cpu_src2, insn, rd); break; -#else - case 0x34: /* stc */ - case 0x35: /* stcsr */ - case 0x36: /* stdcq */ - case 0x37: /* stdc */ - goto ncp_insn; #endif #if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) case 0x3c: /* V9 or LEON3 casa */ @@ -5703,11 +5688,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) gen_op_fpexception_im(dc, FSR_FTT_SEQ_ERROR); return; #endif -#ifndef TARGET_SPARC64 - ncp_insn: - gen_exception(dc, TT_NCP_INSN); - return; -#endif } static void sparc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) From 86b82fe021f46ed4501b16132f7e3fccd0a1ad5d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 4 Oct 2023 17:51:37 -0700 Subject: [PATCH 059/974] target/sparc: Move JMPL, RETT, RETURN to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 7 +++ target/sparc/translate.c | 126 ++++++++++++++++++++++++-------------- 2 files changed, 88 insertions(+), 45 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 8de986f0bb..271789ac13 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -28,6 +28,7 @@ CALL 01 i:s30 &r_r_ri rd rs1 rs2_or_imm imm:bool @n_r_ri .. ..... ...... rs1:5 imm:1 rs2_or_imm:s13 &r_r_ri rd=0 +@r_r_ri .. rd:5 ...... rs1:5 imm:1 rs2_or_imm:s13 &r_r_ri &r_r_ri_cc rd rs1 rs2_or_imm imm:bool cc:bool @r_r_ri_cc .. rd:5 . cc:1 .... rs1:5 imm:1 rs2_or_imm:s13 &r_r_ri_cc @@ -217,6 +218,12 @@ MOVcc 10 rd:5 101100 1 cond:4 imm:1 cc:1 0 rs2_or_imm:s11 MOVfcc 10 rd:5 101100 0 cond:4 imm:1 cc:2 rs2_or_imm:s11 MOVR 10 rd:5 101111 rs1:5 imm:1 cond:3 rs2_or_imm:s10 +JMPL 10 ..... 111000 ..... . ............. @r_r_ri +{ + RETT 10 00000 111001 ..... . ............. @n_r_ri + RETURN 10 00000 111001 ..... . ............. @n_r_ri +} + NCP 10 ----- 110110 ----- --------- ----- # v8 CPop1 NCP 10 ----- 110111 ----- --------- ----- # v8 CPop2 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 7cd1af1014..91de0b1206 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -38,6 +38,7 @@ #ifdef TARGET_SPARC64 # define gen_helper_rdpsr(D, E) qemu_build_not_reached() +# define gen_helper_rett(E) qemu_build_not_reached() # define gen_helper_power_down(E) qemu_build_not_reached() # define gen_helper_wrpsr(E, S) qemu_build_not_reached() #else @@ -4377,6 +4378,85 @@ static bool trans_MOVR(DisasContext *dc, arg_MOVR *a) return do_mov_cond(dc, &cmp, a->rd, src2); } +static bool do_add_special(DisasContext *dc, arg_r_r_ri *a, + bool (*func)(DisasContext *dc, int rd, TCGv src)) +{ + TCGv src1, sum; + + /* For simplicity, we under-decoded the rs2 form. */ + if (!a->imm && a->rs2_or_imm & ~0x1f) { + return false; + } + + /* + * Always load the sum into a new temporary. + * This is required to capture the value across a window change, + * e.g. SAVE and RESTORE, and may be optimized away otherwise. + */ + sum = tcg_temp_new(); + src1 = gen_load_gpr(dc, a->rs1); + if (a->imm || a->rs2_or_imm == 0) { + tcg_gen_addi_tl(sum, src1, a->rs2_or_imm); + } else { + tcg_gen_add_tl(sum, src1, cpu_regs[a->rs2_or_imm]); + } + return func(dc, a->rd, sum); +} + +static bool do_jmpl(DisasContext *dc, int rd, TCGv src) +{ + /* + * Preserve pc across advance, so that we can delay + * the writeback to rd until after src is consumed. + */ + target_ulong cur_pc = dc->pc; + + gen_check_align(dc, src, 3); + + gen_mov_pc_npc(dc); + tcg_gen_mov_tl(cpu_npc, src); + gen_address_mask(dc, cpu_npc); + gen_store_gpr(dc, rd, tcg_constant_tl(cur_pc)); + + dc->npc = DYNAMIC_PC_LOOKUP; + return true; +} + +TRANS(JMPL, ALL, do_add_special, a, do_jmpl) + +static bool do_rett(DisasContext *dc, int rd, TCGv src) +{ + if (!supervisor(dc)) { + return raise_priv(dc); + } + + gen_check_align(dc, src, 3); + + gen_mov_pc_npc(dc); + tcg_gen_mov_tl(cpu_npc, src); + gen_helper_rett(tcg_env); + + dc->npc = DYNAMIC_PC; + return true; +} + +TRANS(RETT, 32, do_add_special, a, do_rett) + +static bool do_return(DisasContext *dc, int rd, TCGv src) +{ + gen_check_align(dc, src, 3); + + gen_mov_pc_npc(dc); + tcg_gen_mov_tl(cpu_npc, src); + gen_address_mask(dc, cpu_npc); + + gen_helper_restore(tcg_env); + dc->npc = DYNAMIC_PC_LOOKUP; + return true; +} + +TRANS(RETURN, 64, do_add_special, a, do_return) + #define CHECK_IU_FEATURE(dc, FEATURE) \ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ goto illegal_insn; @@ -5174,30 +5254,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) } else if (xop == 0x37) { /* V8 CPop2, V9 impdep2 */ goto illegal_insn; /* in decodetree */ -#ifdef TARGET_SPARC64 - } else if (xop == 0x39) { /* V9 return */ - save_state(dc); - cpu_src1 = get_src1(dc, insn); - cpu_tmp0 = tcg_temp_new(); - if (IS_IMM) { /* immediate */ - simm = GET_FIELDs(insn, 19, 31); - tcg_gen_addi_tl(cpu_tmp0, cpu_src1, simm); - } else { /* register */ - rs2 = GET_FIELD(insn, 27, 31); - if (rs2) { - cpu_src2 = gen_load_gpr(dc, rs2); - tcg_gen_add_tl(cpu_tmp0, cpu_src1, cpu_src2); - } else { - tcg_gen_mov_tl(cpu_tmp0, cpu_src1); - } - } - gen_check_align(dc, cpu_tmp0, 3); - gen_helper_restore(tcg_env); - gen_mov_pc_npc(dc); - tcg_gen_mov_tl(cpu_npc, cpu_tmp0); - dc->npc = DYNAMIC_PC_LOOKUP; - goto jmp_insn; -#endif } else { cpu_src1 = get_src1(dc, insn); cpu_tmp0 = tcg_temp_new(); @@ -5215,28 +5271,8 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) } switch (xop) { case 0x38: /* jmpl */ - { - gen_check_align(dc, cpu_tmp0, 3); - gen_store_gpr(dc, rd, tcg_constant_tl(dc->pc)); - gen_mov_pc_npc(dc); - gen_address_mask(dc, cpu_tmp0); - tcg_gen_mov_tl(cpu_npc, cpu_tmp0); - dc->npc = DYNAMIC_PC_LOOKUP; - } - goto jmp_insn; -#if !defined(CONFIG_USER_ONLY) && !defined(TARGET_SPARC64) case 0x39: /* rett, V9 return */ - { - if (!supervisor(dc)) - goto priv_insn; - gen_check_align(dc, cpu_tmp0, 3); - gen_mov_pc_npc(dc); - tcg_gen_mov_tl(cpu_npc, cpu_tmp0); - dc->npc = DYNAMIC_PC; - gen_helper_rett(tcg_env); - } - goto jmp_insn; -#endif + g_assert_not_reached(); /* in decode tree */ case 0x3b: /* flush */ /* nop */ break; From d38258003138a42b0aaaf0e1053c7925e54a1635 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 4 Oct 2023 18:52:38 -0700 Subject: [PATCH 060/974] target/sparc: Move FLUSH, SAVE, RESTORE to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 4 ++++ target/sparc/translate.c | 35 +++++++++++++++++++++++++---------- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 271789ac13..aa90b5c5bb 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -223,6 +223,10 @@ JMPL 10 ..... 111000 ..... . ............. @r_r_ri RETT 10 00000 111001 ..... . ............. @n_r_ri RETURN 10 00000 111001 ..... . ............. @n_r_ri } +NOP 10 00000 111011 ----- 0 00000000----- # FLUSH reg+reg +NOP 10 00000 111011 ----- 1 ------------- # FLUSH reg+imm +SAVE 10 ..... 111100 ..... . ............. @r_r_ri +RESTORE 10 ..... 111101 ..... . ............. @r_r_ri NCP 10 ----- 110110 ----- --------- ----- # v8 CPop1 NCP 10 ----- 110111 ----- --------- ----- # v8 CPop2 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 91de0b1206..e0a13e5d29 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -4077,6 +4077,11 @@ static bool do_saved_restored(DisasContext *dc, bool saved) TRANS(SAVED, 64, do_saved_restored, true) TRANS(RESTORED, 64, do_saved_restored, false) +static bool trans_NOP(DisasContext *dc, arg_NOP *a) +{ + return advance_pc(dc); +} + static bool trans_NOP_v7(DisasContext *dc, arg_NOP_v7 *a) { /* @@ -4457,6 +4462,24 @@ static bool do_return(DisasContext *dc, int rd, TCGv src) TRANS(RETURN, 64, do_add_special, a, do_return) +static bool do_save(DisasContext *dc, int rd, TCGv src) +{ + gen_helper_save(tcg_env); + gen_store_gpr(dc, rd, src); + return advance_pc(dc); +} + +TRANS(SAVE, ALL, do_add_special, a, do_save) + +static bool do_restore(DisasContext *dc, int rd, TCGv src) +{ + gen_helper_restore(tcg_env); + gen_store_gpr(dc, rd, src); + return advance_pc(dc); +} + +TRANS(RESTORE, ALL, do_add_special, a, do_restore) + #define CHECK_IU_FEATURE(dc, FEATURE) \ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ goto illegal_insn; @@ -5272,18 +5295,10 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) switch (xop) { case 0x38: /* jmpl */ case 0x39: /* rett, V9 return */ - g_assert_not_reached(); /* in decode tree */ - case 0x3b: /* flush */ - /* nop */ - break; + case 0x3b: /* flush */ case 0x3c: /* save */ - gen_helper_save(tcg_env); - gen_store_gpr(dc, rd, cpu_tmp0); - break; case 0x3d: /* restore */ - gen_helper_restore(tcg_env); - gen_store_gpr(dc, rd, cpu_tmp0); - break; + g_assert_not_reached(); /* in decode tree */ #if !defined(CONFIG_USER_ONLY) && defined(TARGET_SPARC64) case 0x3e: /* V9 done/retry */ { From 8f75b8a4eb7902b786a8c683e05bdd3adc9da5e5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 4 Oct 2023 19:05:45 -0700 Subject: [PATCH 061/974] target/sparc: Move DONE, RETRY to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 3 ++ target/sparc/translate.c | 88 ++++++++++++--------------------------- 2 files changed, 29 insertions(+), 62 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index aa90b5c5bb..137b7eb3c6 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -228,6 +228,9 @@ NOP 10 00000 111011 ----- 1 ------------- # FLUSH reg+imm SAVE 10 ..... 111100 ..... . ............. @r_r_ri RESTORE 10 ..... 111101 ..... . ............. @r_r_ri +DONE 10 00000 111110 00000 0 0000000000000 +RETRY 10 00001 111110 00000 0 0000000000000 + NCP 10 ----- 110110 ----- --------- ----- # v8 CPop1 NCP 10 ----- 110111 ----- --------- ----- # v8 CPop2 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index e0a13e5d29..8faf2c59b2 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -43,10 +43,12 @@ # define gen_helper_wrpsr(E, S) qemu_build_not_reached() #else # define gen_helper_clear_softint(E, S) qemu_build_not_reached() +# define gen_helper_done(E) qemu_build_not_reached() # define gen_helper_flushw(E) qemu_build_not_reached() # define gen_helper_rdccr(D, E) qemu_build_not_reached() # define gen_helper_rdcwp(D, E) qemu_build_not_reached() # define gen_helper_restored(E) qemu_build_not_reached() +# define gen_helper_retry(E) qemu_build_not_reached() # define gen_helper_saved(E) qemu_build_not_reached() # define gen_helper_sdivx(D, E, A, B) qemu_build_not_reached() # define gen_helper_set_softint(E, S) qemu_build_not_reached() @@ -4480,6 +4482,25 @@ static bool do_restore(DisasContext *dc, int rd, TCGv src) TRANS(RESTORE, ALL, do_add_special, a, do_restore) +static bool do_done_retry(DisasContext *dc, bool done) +{ + if (!supervisor(dc)) { + return raise_priv(dc); + } + dc->npc = DYNAMIC_PC; + dc->pc = DYNAMIC_PC; + translator_io_start(&dc->base); + if (done) { + gen_helper_done(tcg_env); + } else { + gen_helper_retry(tcg_env); + } + return true; +} + +TRANS(DONE, 64, do_done_retry, true) +TRANS(RETRY, 64, do_done_retry, false) + #define CHECK_IU_FEATURE(dc, FEATURE) \ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ goto illegal_insn; @@ -4491,7 +4512,8 @@ TRANS(RESTORE, ALL, do_add_special, a, do_restore) static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) { unsigned int opc, rs1, rs2, rd; - TCGv cpu_src1, cpu_src2; + TCGv cpu_src1; + TCGv cpu_src2 __attribute__((unused)); TCGv_i32 cpu_src1_32, cpu_src2_32, cpu_dst_32; TCGv_i64 cpu_src1_64, cpu_src2_64, cpu_dst_64; target_long simm; @@ -4506,9 +4528,8 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) g_assert_not_reached(); /* in decodetree */ case 2: /* FPU & Logical Operations */ { - unsigned int xop __attribute__((unused)) = GET_FIELD(insn, 7, 12); + unsigned int xop = GET_FIELD(insn, 7, 12); TCGv cpu_dst __attribute__((unused)) = tcg_temp_new(); - TCGv cpu_tmp0 __attribute__((unused)); if (xop == 0x34) { /* FPU Operations */ if (gen_trap_ifnofpu(dc)) { @@ -4828,8 +4849,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) default: goto illegal_insn; } - } else if (xop < 0x36) { - goto illegal_insn; /* in decodetree */ } else if (xop == 0x36) { #ifdef TARGET_SPARC64 /* VIS */ @@ -5271,65 +5290,10 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) default: goto illegal_insn; } -#else - g_assert_not_reached(); /* in decodetree */ #endif - } else if (xop == 0x37) { - /* V8 CPop2, V9 impdep2 */ - goto illegal_insn; /* in decodetree */ } else { - cpu_src1 = get_src1(dc, insn); - cpu_tmp0 = tcg_temp_new(); - if (IS_IMM) { /* immediate */ - simm = GET_FIELDs(insn, 19, 31); - tcg_gen_addi_tl(cpu_tmp0, cpu_src1, simm); - } else { /* register */ - rs2 = GET_FIELD(insn, 27, 31); - if (rs2) { - cpu_src2 = gen_load_gpr(dc, rs2); - tcg_gen_add_tl(cpu_tmp0, cpu_src1, cpu_src2); - } else { - tcg_gen_mov_tl(cpu_tmp0, cpu_src1); - } - } - switch (xop) { - case 0x38: /* jmpl */ - case 0x39: /* rett, V9 return */ - case 0x3b: /* flush */ - case 0x3c: /* save */ - case 0x3d: /* restore */ - g_assert_not_reached(); /* in decode tree */ -#if !defined(CONFIG_USER_ONLY) && defined(TARGET_SPARC64) - case 0x3e: /* V9 done/retry */ - { - switch (rd) { - case 0: - if (!supervisor(dc)) - goto priv_insn; - dc->npc = DYNAMIC_PC; - dc->pc = DYNAMIC_PC; - translator_io_start(&dc->base); - gen_helper_done(tcg_env); - goto jmp_insn; - case 1: - if (!supervisor(dc)) - goto priv_insn; - dc->npc = DYNAMIC_PC; - dc->pc = DYNAMIC_PC; - translator_io_start(&dc->base); - gen_helper_retry(tcg_env); - goto jmp_insn; - default: - goto illegal_insn; - } - } - break; -#endif - default: - goto illegal_insn; - } + goto illegal_insn; /* in decodetree */ } - break; } break; case 3: /* load/store instructions */ @@ -5726,7 +5690,7 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) illegal_insn: gen_exception(dc, TT_ILL_INSN); return; -#if !defined(CONFIG_USER_ONLY) +#if !defined(CONFIG_USER_ONLY) && !defined(TARGET_SPARC64) priv_insn: gen_exception(dc, TT_PRIV_INSN); return; From 811cc0b0ceb453d50305e9e5a4e646aa868ef971 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 4 Oct 2023 21:10:47 -0700 Subject: [PATCH 062/974] target/sparc: Split out resolve_asi Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/translate.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 8faf2c59b2..6c1610486e 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -1919,15 +1919,25 @@ typedef struct { MemOp memop; } DisasASI; -static DisasASI get_asi(DisasContext *dc, int insn, MemOp memop) +/* + * Build DisasASI. + * For asi == -1, treat as non-asi. + * For ask == -2, treat as immediate offset (v8 error, v9 %asi). + */ +static DisasASI resolve_asi(DisasContext *dc, int asi, MemOp memop) { - int asi = GET_FIELD(insn, 19, 26); ASIType type = GET_ASI_HELPER; int mem_idx = dc->mem_idx; + if (asi == -1) { + /* Artificial "non-asi" case. */ + type = GET_ASI_DIRECT; + goto done; + } + #ifndef TARGET_SPARC64 /* Before v9, all asis are immediate and privileged. */ - if (IS_IMM) { + if (asi < 0) { gen_exception(dc, TT_ILL_INSN); type = GET_ASI_EXCP; } else if (supervisor(dc) @@ -1970,7 +1980,7 @@ static DisasASI get_asi(DisasContext *dc, int insn, MemOp memop) type = GET_ASI_EXCP; } #else - if (IS_IMM) { + if (asi < 0) { asi = dc->asi; } /* With v9, all asis below 0x80 are privileged. */ @@ -2129,9 +2139,16 @@ static DisasASI get_asi(DisasContext *dc, int insn, MemOp memop) } #endif + done: return (DisasASI){ type, asi, mem_idx, memop }; } +static DisasASI get_asi(DisasContext *dc, int insn, MemOp memop) +{ + int asi = IS_IMM ? -2 : GET_FIELD(insn, 19, 26); + return resolve_asi(dc, asi, memop); +} + static void gen_ld_asi(DisasContext *dc, TCGv dst, TCGv addr, int insn, MemOp memop) { From a76779ee3b1291b2e29a04229299545a0348160f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 4 Oct 2023 21:59:26 -0700 Subject: [PATCH 063/974] target/sparc: Drop ifdef around get_asi and friends Mark some of the functions as unused, temporarily. Fix up some tl vs i64 issues revealed in the process. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/translate.c | 189 +++++++++++++++------------------------ 1 file changed, 72 insertions(+), 117 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 6c1610486e..09b01ccf77 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -1900,7 +1900,6 @@ static void gen_ldstub(DisasContext *dc, TCGv dst, TCGv addr, int mmu_idx) } /* asi moves */ -#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) typedef enum { GET_ASI_HELPER, GET_ASI_EXCP, @@ -2149,8 +2148,22 @@ static DisasASI get_asi(DisasContext *dc, int insn, MemOp memop) return resolve_asi(dc, asi, memop); } -static void gen_ld_asi(DisasContext *dc, TCGv dst, TCGv addr, - int insn, MemOp memop) +#if defined(CONFIG_USER_ONLY) && !defined(TARGET_SPARC64) +static void gen_helper_ld_asi(TCGv_i64 r, TCGv_env e, TCGv a, + TCGv_i32 asi, TCGv_i32 mop) +{ + g_assert_not_reached(); +} + +static void gen_helper_st_asi(TCGv_env e, TCGv a, TCGv_i64 r, + TCGv_i32 asi, TCGv_i32 mop) +{ + g_assert_not_reached(); +} +#endif + +static void __attribute__((unused)) +gen_ld_asi(DisasContext *dc, TCGv dst, TCGv addr, int insn, MemOp memop) { DisasASI da = get_asi(dc, insn, memop); @@ -2184,8 +2197,8 @@ static void gen_ld_asi(DisasContext *dc, TCGv dst, TCGv addr, } } -static void gen_st_asi(DisasContext *dc, TCGv src, TCGv addr, - int insn, MemOp memop) +static void __attribute__((unused)) +gen_st_asi(DisasContext *dc, TCGv src, TCGv addr, int insn, MemOp memop) { DisasASI da = get_asi(dc, insn, memop); @@ -2260,8 +2273,8 @@ static void gen_st_asi(DisasContext *dc, TCGv src, TCGv addr, } } -static void gen_swap_asi(DisasContext *dc, TCGv dst, TCGv src, - TCGv addr, int insn) +static void __attribute__((unused)) +gen_swap_asi(DisasContext *dc, TCGv dst, TCGv src, TCGv addr, int insn) { DisasASI da = get_asi(dc, insn, MO_TEUL); @@ -2278,8 +2291,8 @@ static void gen_swap_asi(DisasContext *dc, TCGv dst, TCGv src, } } -static void gen_cas_asi(DisasContext *dc, TCGv addr, TCGv cmpv, - int insn, int rd) +static void __attribute__((unused)) +gen_cas_asi(DisasContext *dc, TCGv addr, TCGv cmpv, int insn, int rd) { DisasASI da = get_asi(dc, insn, MO_TEUL); TCGv oldv; @@ -2300,7 +2313,8 @@ static void gen_cas_asi(DisasContext *dc, TCGv addr, TCGv cmpv, } } -static void gen_ldstub_asi(DisasContext *dc, TCGv dst, TCGv addr, int insn) +static void __attribute__((unused)) +gen_ldstub_asi(DisasContext *dc, TCGv dst, TCGv addr, int insn) { DisasASI da = get_asi(dc, insn, MO_UB); @@ -2335,11 +2349,9 @@ static void gen_ldstub_asi(DisasContext *dc, TCGv dst, TCGv addr, int insn) break; } } -#endif -#ifdef TARGET_SPARC64 -static void gen_ldf_asi(DisasContext *dc, TCGv addr, - int insn, int size, int rd) +static void __attribute__((unused)) +gen_ldf_asi(DisasContext *dc, TCGv addr, int insn, int size, int rd) { DisasASI da = get_asi(dc, insn, (size == 4 ? MO_TEUL : MO_TEUQ)); TCGv_i32 d32; @@ -2447,8 +2459,8 @@ static void gen_ldf_asi(DisasContext *dc, TCGv addr, } } -static void gen_stf_asi(DisasContext *dc, TCGv addr, - int insn, int size, int rd) +static void __attribute__((unused)) +gen_stf_asi(DisasContext *dc, TCGv addr, int insn, int size, int rd) { DisasASI da = get_asi(dc, insn, (size == 4 ? MO_TEUL : MO_TEUQ)); TCGv_i32 d32; @@ -2530,21 +2542,23 @@ static void gen_stf_asi(DisasContext *dc, TCGv addr, } } -static void gen_ldda_asi(DisasContext *dc, TCGv addr, int insn, int rd) +static void __attribute__((unused)) +gen_ldda_asi(DisasContext *dc, TCGv addr, int insn, int rd) { DisasASI da = get_asi(dc, insn, MO_TEUQ); - TCGv_i64 hi = gen_dest_gpr(dc, rd); - TCGv_i64 lo = gen_dest_gpr(dc, rd + 1); + TCGv hi = gen_dest_gpr(dc, rd); + TCGv lo = gen_dest_gpr(dc, rd + 1); switch (da.type) { case GET_ASI_EXCP: return; case GET_ASI_DTWINX: + assert(TARGET_LONG_BITS == 64); gen_address_mask(dc, addr); - tcg_gen_qemu_ld_i64(hi, addr, da.mem_idx, da.memop | MO_ALIGN_16); + tcg_gen_qemu_ld_tl(hi, addr, da.mem_idx, da.memop | MO_ALIGN_16); tcg_gen_addi_tl(addr, addr, 8); - tcg_gen_qemu_ld_i64(lo, addr, da.mem_idx, da.memop); + tcg_gen_qemu_ld_tl(lo, addr, da.mem_idx, da.memop); break; case GET_ASI_DIRECT: @@ -2558,9 +2572,9 @@ static void gen_ldda_asi(DisasContext *dc, TCGv addr, int insn, int rd) result is byte swapped. Having just performed one 64-bit bswap, we need now to swap the writebacks. */ if ((da.memop & MO_BSWAP) == MO_TE) { - tcg_gen_extr32_i64(lo, hi, tmp); + tcg_gen_extr_i64_tl(lo, hi, tmp); } else { - tcg_gen_extr32_i64(hi, lo, tmp); + tcg_gen_extr_i64_tl(hi, lo, tmp); } } break; @@ -2580,9 +2594,9 @@ static void gen_ldda_asi(DisasContext *dc, TCGv addr, int insn, int rd) /* See above. */ if ((da.memop & MO_BSWAP) == MO_TE) { - tcg_gen_extr32_i64(lo, hi, tmp); + tcg_gen_extr_i64_tl(lo, hi, tmp); } else { - tcg_gen_extr32_i64(hi, lo, tmp); + tcg_gen_extr_i64_tl(hi, lo, tmp); } } break; @@ -2592,8 +2606,8 @@ static void gen_ldda_asi(DisasContext *dc, TCGv addr, int insn, int rd) gen_store_gpr(dc, rd + 1, lo); } -static void gen_stda_asi(DisasContext *dc, TCGv hi, TCGv addr, - int insn, int rd) +static void __attribute__((unused)) +gen_stda_asi(DisasContext *dc, TCGv hi, TCGv addr, int insn, int rd) { DisasASI da = get_asi(dc, insn, MO_TEUQ); TCGv lo = gen_load_gpr(dc, rd + 1); @@ -2603,10 +2617,11 @@ static void gen_stda_asi(DisasContext *dc, TCGv hi, TCGv addr, break; case GET_ASI_DTWINX: + assert(TARGET_LONG_BITS == 64); gen_address_mask(dc, addr); - tcg_gen_qemu_st_i64(hi, addr, da.mem_idx, da.memop | MO_ALIGN_16); + tcg_gen_qemu_st_tl(hi, addr, da.mem_idx, da.memop | MO_ALIGN_16); tcg_gen_addi_tl(addr, addr, 8); - tcg_gen_qemu_st_i64(lo, addr, da.mem_idx, da.memop); + tcg_gen_qemu_st_tl(lo, addr, da.mem_idx, da.memop); break; case GET_ASI_DIRECT: @@ -2617,15 +2632,37 @@ static void gen_stda_asi(DisasContext *dc, TCGv hi, TCGv addr, byte swapped. We will perform one 64-bit LE store, so now we must swap the order of the construction. */ if ((da.memop & MO_BSWAP) == MO_TE) { - tcg_gen_concat32_i64(t64, lo, hi); + tcg_gen_concat_tl_i64(t64, lo, hi); } else { - tcg_gen_concat32_i64(t64, hi, lo); + tcg_gen_concat_tl_i64(t64, hi, lo); } gen_address_mask(dc, addr); tcg_gen_qemu_st_i64(t64, addr, da.mem_idx, da.memop | MO_ALIGN); } break; + case GET_ASI_BFILL: + assert(TARGET_LONG_BITS == 32); + /* Store 32 bytes of T64 to ADDR. */ + /* ??? The original qemu code suggests 8-byte alignment, dropping + the low bits, but the only place I can see this used is in the + Linux kernel with 32 byte alignment, which would make more sense + as a cacheline-style operation. */ + { + TCGv_i64 t64 = tcg_temp_new_i64(); + TCGv d_addr = tcg_temp_new(); + TCGv eight = tcg_constant_tl(8); + int i; + + tcg_gen_concat_tl_i64(t64, lo, hi); + tcg_gen_andi_tl(d_addr, addr, -8); + for (i = 0; i < 32; i += 8) { + tcg_gen_qemu_st_i64(t64, d_addr, da.mem_idx, da.memop); + tcg_gen_add_tl(d_addr, d_addr, eight); + } + } + break; + default: /* ??? In theory we've handled all of the ASIs that are valid for stda, and this should raise DAE_invalid_asi. */ @@ -2636,9 +2673,9 @@ static void gen_stda_asi(DisasContext *dc, TCGv hi, TCGv addr, /* See above. */ if ((da.memop & MO_BSWAP) == MO_TE) { - tcg_gen_concat32_i64(t64, lo, hi); + tcg_gen_concat_tl_i64(t64, lo, hi); } else { - tcg_gen_concat32_i64(t64, hi, lo); + tcg_gen_concat_tl_i64(t64, hi, lo); } save_state(dc); @@ -2648,8 +2685,8 @@ static void gen_stda_asi(DisasContext *dc, TCGv hi, TCGv addr, } } -static void gen_casx_asi(DisasContext *dc, TCGv addr, TCGv cmpv, - int insn, int rd) +static void __attribute__((unused)) +gen_casx_asi(DisasContext *dc, TCGv addr, TCGv cmpv, int insn, int rd) { DisasASI da = get_asi(dc, insn, MO_TEUQ); TCGv oldv; @@ -2670,88 +2707,6 @@ static void gen_casx_asi(DisasContext *dc, TCGv addr, TCGv cmpv, } } -#elif !defined(CONFIG_USER_ONLY) -static void gen_ldda_asi(DisasContext *dc, TCGv addr, int insn, int rd) -{ - /* ??? Work around an apparent bug in Ubuntu gcc 4.8.2-10ubuntu2+12, - whereby "rd + 1" elicits "error: array subscript is above array". - Since we have already asserted that rd is even, the semantics - are unchanged. */ - TCGv lo = gen_dest_gpr(dc, rd | 1); - TCGv hi = gen_dest_gpr(dc, rd); - TCGv_i64 t64 = tcg_temp_new_i64(); - DisasASI da = get_asi(dc, insn, MO_TEUQ); - - switch (da.type) { - case GET_ASI_EXCP: - return; - case GET_ASI_DIRECT: - gen_address_mask(dc, addr); - tcg_gen_qemu_ld_i64(t64, addr, da.mem_idx, da.memop | MO_ALIGN); - break; - default: - { - TCGv_i32 r_asi = tcg_constant_i32(da.asi); - TCGv_i32 r_mop = tcg_constant_i32(MO_UQ); - - save_state(dc); - gen_helper_ld_asi(t64, tcg_env, addr, r_asi, r_mop); - } - break; - } - - tcg_gen_extr_i64_i32(lo, hi, t64); - gen_store_gpr(dc, rd | 1, lo); - gen_store_gpr(dc, rd, hi); -} - -static void gen_stda_asi(DisasContext *dc, TCGv hi, TCGv addr, - int insn, int rd) -{ - DisasASI da = get_asi(dc, insn, MO_TEUQ); - TCGv lo = gen_load_gpr(dc, rd + 1); - TCGv_i64 t64 = tcg_temp_new_i64(); - - tcg_gen_concat_tl_i64(t64, lo, hi); - - switch (da.type) { - case GET_ASI_EXCP: - break; - case GET_ASI_DIRECT: - gen_address_mask(dc, addr); - tcg_gen_qemu_st_i64(t64, addr, da.mem_idx, da.memop | MO_ALIGN); - break; - case GET_ASI_BFILL: - /* Store 32 bytes of T64 to ADDR. */ - /* ??? The original qemu code suggests 8-byte alignment, dropping - the low bits, but the only place I can see this used is in the - Linux kernel with 32 byte alignment, which would make more sense - as a cacheline-style operation. */ - { - TCGv d_addr = tcg_temp_new(); - TCGv eight = tcg_constant_tl(8); - int i; - - tcg_gen_andi_tl(d_addr, addr, -8); - for (i = 0; i < 32; i += 8) { - tcg_gen_qemu_st_i64(t64, d_addr, da.mem_idx, da.memop); - tcg_gen_add_tl(d_addr, d_addr, eight); - } - } - break; - default: - { - TCGv_i32 r_asi = tcg_constant_i32(da.asi); - TCGv_i32 r_mop = tcg_constant_i32(MO_UQ); - - save_state(dc); - gen_helper_st_asi(tcg_env, addr, t64, r_asi, r_mop); - } - break; - } -} -#endif - static TCGv get_src1(DisasContext *dc, unsigned int insn) { unsigned int rs1 = GET_FIELD(insn, 13, 17); From c03a0fd15cb0cd694240a68964be630dd3232aca Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 4 Oct 2023 22:44:11 -0700 Subject: [PATCH 064/974] target/sparc: Split out ldst functions with asi pre-computed As an intermediate step in decodetree conversion, create new functions passing in DisasASI and not insn. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/translate.c | 215 ++++++++++++++++++++++----------------- 1 file changed, 123 insertions(+), 92 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 09b01ccf77..e24945e50e 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -2162,25 +2162,21 @@ static void gen_helper_st_asi(TCGv_env e, TCGv a, TCGv_i64 r, } #endif -static void __attribute__((unused)) -gen_ld_asi(DisasContext *dc, TCGv dst, TCGv addr, int insn, MemOp memop) +static void gen_ld_asi0(DisasContext *dc, DisasASI *da, TCGv dst, TCGv addr) { - DisasASI da = get_asi(dc, insn, memop); - - switch (da.type) { + switch (da->type) { case GET_ASI_EXCP: break; case GET_ASI_DTWINX: /* Reserved for ldda. */ gen_exception(dc, TT_ILL_INSN); break; case GET_ASI_DIRECT: - gen_address_mask(dc, addr); - tcg_gen_qemu_ld_tl(dst, addr, da.mem_idx, da.memop | MO_ALIGN); + tcg_gen_qemu_ld_tl(dst, addr, da->mem_idx, da->memop | MO_ALIGN); break; default: { - TCGv_i32 r_asi = tcg_constant_i32(da.asi); - TCGv_i32 r_mop = tcg_constant_i32(memop | MO_ALIGN); + TCGv_i32 r_asi = tcg_constant_i32(da->asi); + TCGv_i32 r_mop = tcg_constant_i32(da->memop | MO_ALIGN); save_state(dc); #ifdef TARGET_SPARC64 @@ -2198,33 +2194,38 @@ gen_ld_asi(DisasContext *dc, TCGv dst, TCGv addr, int insn, MemOp memop) } static void __attribute__((unused)) -gen_st_asi(DisasContext *dc, TCGv src, TCGv addr, int insn, MemOp memop) +gen_ld_asi(DisasContext *dc, TCGv dst, TCGv addr, int insn, MemOp memop) { DisasASI da = get_asi(dc, insn, memop); - switch (da.type) { + gen_address_mask(dc, addr); + gen_ld_asi0(dc, &da, dst, addr); +} + +static void gen_st_asi0(DisasContext *dc, DisasASI *da, TCGv src, TCGv addr) +{ + switch (da->type) { case GET_ASI_EXCP: break; + case GET_ASI_DTWINX: /* Reserved for stda. */ -#ifndef TARGET_SPARC64 - gen_exception(dc, TT_ILL_INSN); - break; -#else - if (!(dc->def->features & CPU_FEATURE_HYPV)) { + if (TARGET_LONG_BITS == 32) { + gen_exception(dc, TT_ILL_INSN); + break; + } else if (!(dc->def->features & CPU_FEATURE_HYPV)) { /* Pre OpenSPARC CPUs don't have these */ gen_exception(dc, TT_ILL_INSN); - return; + break; } - /* in OpenSPARC T1+ CPUs TWINX ASIs in store instructions - * are ST_BLKINIT_ ASIs */ -#endif + /* In OpenSPARC T1+ CPUs TWINX ASIs in store are ST_BLKINIT_ ASIs */ /* fall through */ + case GET_ASI_DIRECT: - gen_address_mask(dc, addr); - tcg_gen_qemu_st_tl(src, addr, da.mem_idx, da.memop | MO_ALIGN); + tcg_gen_qemu_st_tl(src, addr, da->mem_idx, da->memop | MO_ALIGN); break; -#if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY) + case GET_ASI_BCOPY: + assert(TARGET_LONG_BITS == 32); /* Copy 32 bytes from the address in SRC to ADDR. */ /* ??? The original qemu code suggests 4-byte alignment, dropping the low bits, but the only place I can see this used is in the @@ -2242,18 +2243,18 @@ gen_st_asi(DisasContext *dc, TCGv src, TCGv addr, int insn, MemOp memop) for (i = 0; i < 32; i += 4) { /* Since the loads and stores are paired, allow the copy to happen in the host endianness. */ - tcg_gen_qemu_ld_i32(tmp, saddr, da.mem_idx, MO_UL); - tcg_gen_qemu_st_i32(tmp, daddr, da.mem_idx, MO_UL); + tcg_gen_qemu_ld_i32(tmp, saddr, da->mem_idx, MO_UL); + tcg_gen_qemu_st_i32(tmp, daddr, da->mem_idx, MO_UL); tcg_gen_add_tl(saddr, saddr, four); tcg_gen_add_tl(daddr, daddr, four); } } break; -#endif + default: { - TCGv_i32 r_asi = tcg_constant_i32(da.asi); - TCGv_i32 r_mop = tcg_constant_i32(memop | MO_ALIGN); + TCGv_i32 r_asi = tcg_constant_i32(da->asi); + TCGv_i32 r_mop = tcg_constant_i32(da->memop | MO_ALIGN); save_state(dc); #ifdef TARGET_SPARC64 @@ -2273,16 +2274,49 @@ gen_st_asi(DisasContext *dc, TCGv src, TCGv addr, int insn, MemOp memop) } } +static void __attribute__((unused)) +gen_st_asi(DisasContext *dc, TCGv src, TCGv addr, int insn, MemOp memop) +{ + DisasASI da = get_asi(dc, insn, memop); + + gen_address_mask(dc, addr); + gen_st_asi0(dc, &da, src, addr); +} + +static void gen_swap_asi0(DisasContext *dc, DisasASI *da, + TCGv dst, TCGv src, TCGv addr) +{ + switch (da->type) { + case GET_ASI_EXCP: + break; + case GET_ASI_DIRECT: + gen_swap(dc, dst, src, addr, da->mem_idx, da->memop); + break; + default: + /* ??? Should be DAE_invalid_asi. */ + gen_exception(dc, TT_DATA_ACCESS); + break; + } +} + static void __attribute__((unused)) gen_swap_asi(DisasContext *dc, TCGv dst, TCGv src, TCGv addr, int insn) { DisasASI da = get_asi(dc, insn, MO_TEUL); - switch (da.type) { + gen_address_mask(dc, addr); + gen_swap_asi0(dc, &da, dst, src, addr); +} + +static void gen_cas_asi0(DisasContext *dc, DisasASI *da, + TCGv oldv, TCGv newv, TCGv cmpv, TCGv addr) +{ + switch (da->type) { case GET_ASI_EXCP: - break; + return; case GET_ASI_DIRECT: - gen_swap(dc, dst, src, addr, da.mem_idx, da.memop); + tcg_gen_atomic_cmpxchg_tl(oldv, addr, cmpv, newv, + da->mem_idx, da->memop | MO_ALIGN); break; default: /* ??? Should be DAE_invalid_asi. */ @@ -2295,34 +2329,33 @@ static void __attribute__((unused)) gen_cas_asi(DisasContext *dc, TCGv addr, TCGv cmpv, int insn, int rd) { DisasASI da = get_asi(dc, insn, MO_TEUL); - TCGv oldv; + TCGv oldv = gen_dest_gpr(dc, rd); + TCGv newv = gen_load_gpr(dc, rd); - switch (da.type) { - case GET_ASI_EXCP: - return; - case GET_ASI_DIRECT: - oldv = tcg_temp_new(); - tcg_gen_atomic_cmpxchg_tl(oldv, addr, cmpv, gen_load_gpr(dc, rd), - da.mem_idx, da.memop | MO_ALIGN); - gen_store_gpr(dc, rd, oldv); - break; - default: - /* ??? Should be DAE_invalid_asi. */ - gen_exception(dc, TT_DATA_ACCESS); - break; - } + gen_address_mask(dc, addr); + gen_cas_asi0(dc, &da, oldv, newv, cmpv, addr); + gen_store_gpr(dc, rd, oldv); } static void __attribute__((unused)) -gen_ldstub_asi(DisasContext *dc, TCGv dst, TCGv addr, int insn) +gen_casx_asi(DisasContext *dc, TCGv addr, TCGv cmpv, int insn, int rd) { - DisasASI da = get_asi(dc, insn, MO_UB); + DisasASI da = get_asi(dc, insn, MO_TEUQ); + TCGv oldv = gen_dest_gpr(dc, rd); + TCGv newv = gen_load_gpr(dc, rd); - switch (da.type) { + gen_address_mask(dc, addr); + gen_cas_asi0(dc, &da, oldv, newv, cmpv, addr); + gen_store_gpr(dc, rd, oldv); +} + +static void gen_ldstub_asi0(DisasContext *dc, DisasASI *da, TCGv dst, TCGv addr) +{ + switch (da->type) { case GET_ASI_EXCP: break; case GET_ASI_DIRECT: - gen_ldstub(dc, dst, addr, da.mem_idx); + gen_ldstub(dc, dst, addr, da->mem_idx); break; default: /* ??? In theory, this should be raise DAE_invalid_asi. @@ -2330,7 +2363,7 @@ gen_ldstub_asi(DisasContext *dc, TCGv dst, TCGv addr, int insn) if (tb_cflags(dc->base.tb) & CF_PARALLEL) { gen_helper_exit_atomic(tcg_env); } else { - TCGv_i32 r_asi = tcg_constant_i32(da.asi); + TCGv_i32 r_asi = tcg_constant_i32(da->asi); TCGv_i32 r_mop = tcg_constant_i32(MO_UB); TCGv_i64 s64, t64; @@ -2350,6 +2383,15 @@ gen_ldstub_asi(DisasContext *dc, TCGv dst, TCGv addr, int insn) } } +static void __attribute__((unused)) +gen_ldstub_asi(DisasContext *dc, TCGv dst, TCGv addr, int insn) +{ + DisasASI da = get_asi(dc, insn, MO_UB); + + gen_address_mask(dc, addr); + gen_ldstub_asi0(dc, &da, dst, addr); +} + static void __attribute__((unused)) gen_ldf_asi(DisasContext *dc, TCGv addr, int insn, int size, int rd) { @@ -2542,36 +2584,32 @@ gen_stf_asi(DisasContext *dc, TCGv addr, int insn, int size, int rd) } } -static void __attribute__((unused)) -gen_ldda_asi(DisasContext *dc, TCGv addr, int insn, int rd) +static void gen_ldda_asi0(DisasContext *dc, DisasASI *da, TCGv addr, int rd) { - DisasASI da = get_asi(dc, insn, MO_TEUQ); TCGv hi = gen_dest_gpr(dc, rd); TCGv lo = gen_dest_gpr(dc, rd + 1); - switch (da.type) { + switch (da->type) { case GET_ASI_EXCP: return; case GET_ASI_DTWINX: assert(TARGET_LONG_BITS == 64); - gen_address_mask(dc, addr); - tcg_gen_qemu_ld_tl(hi, addr, da.mem_idx, da.memop | MO_ALIGN_16); + tcg_gen_qemu_ld_tl(hi, addr, da->mem_idx, da->memop | MO_ALIGN_16); tcg_gen_addi_tl(addr, addr, 8); - tcg_gen_qemu_ld_tl(lo, addr, da.mem_idx, da.memop); + tcg_gen_qemu_ld_tl(lo, addr, da->mem_idx, da->memop); break; case GET_ASI_DIRECT: { TCGv_i64 tmp = tcg_temp_new_i64(); - gen_address_mask(dc, addr); - tcg_gen_qemu_ld_i64(tmp, addr, da.mem_idx, da.memop | MO_ALIGN); + tcg_gen_qemu_ld_i64(tmp, addr, da->mem_idx, da->memop | MO_ALIGN); /* Note that LE ldda acts as if each 32-bit register result is byte swapped. Having just performed one 64-bit bswap, we need now to swap the writebacks. */ - if ((da.memop & MO_BSWAP) == MO_TE) { + if ((da->memop & MO_BSWAP) == MO_TE) { tcg_gen_extr_i64_tl(lo, hi, tmp); } else { tcg_gen_extr_i64_tl(hi, lo, tmp); @@ -2585,15 +2623,15 @@ gen_ldda_asi(DisasContext *dc, TCGv addr, int insn, int rd) real hardware allows others. This can be seen with e.g. FreeBSD 10.3 wrt ASI_IC_TAG. */ { - TCGv_i32 r_asi = tcg_constant_i32(da.asi); - TCGv_i32 r_mop = tcg_constant_i32(da.memop); + TCGv_i32 r_asi = tcg_constant_i32(da->asi); + TCGv_i32 r_mop = tcg_constant_i32(da->memop); TCGv_i64 tmp = tcg_temp_new_i64(); save_state(dc); gen_helper_ld_asi(tmp, tcg_env, addr, r_asi, r_mop); /* See above. */ - if ((da.memop & MO_BSWAP) == MO_TE) { + if ((da->memop & MO_BSWAP) == MO_TE) { tcg_gen_extr_i64_tl(lo, hi, tmp); } else { tcg_gen_extr_i64_tl(hi, lo, tmp); @@ -2607,21 +2645,28 @@ gen_ldda_asi(DisasContext *dc, TCGv addr, int insn, int rd) } static void __attribute__((unused)) -gen_stda_asi(DisasContext *dc, TCGv hi, TCGv addr, int insn, int rd) +gen_ldda_asi(DisasContext *dc, TCGv addr, int insn, int rd) { DisasASI da = get_asi(dc, insn, MO_TEUQ); + + gen_address_mask(dc, addr); + gen_ldda_asi0(dc, &da, addr, rd); +} + +static void gen_stda_asi0(DisasContext *dc, DisasASI *da, TCGv addr, int rd) +{ + TCGv hi = gen_load_gpr(dc, rd); TCGv lo = gen_load_gpr(dc, rd + 1); - switch (da.type) { + switch (da->type) { case GET_ASI_EXCP: break; case GET_ASI_DTWINX: assert(TARGET_LONG_BITS == 64); - gen_address_mask(dc, addr); - tcg_gen_qemu_st_tl(hi, addr, da.mem_idx, da.memop | MO_ALIGN_16); + tcg_gen_qemu_st_tl(hi, addr, da->mem_idx, da->memop | MO_ALIGN_16); tcg_gen_addi_tl(addr, addr, 8); - tcg_gen_qemu_st_tl(lo, addr, da.mem_idx, da.memop); + tcg_gen_qemu_st_tl(lo, addr, da->mem_idx, da->memop); break; case GET_ASI_DIRECT: @@ -2631,13 +2676,12 @@ gen_stda_asi(DisasContext *dc, TCGv hi, TCGv addr, int insn, int rd) /* Note that LE stda acts as if each 32-bit register result is byte swapped. We will perform one 64-bit LE store, so now we must swap the order of the construction. */ - if ((da.memop & MO_BSWAP) == MO_TE) { + if ((da->memop & MO_BSWAP) == MO_TE) { tcg_gen_concat_tl_i64(t64, lo, hi); } else { tcg_gen_concat_tl_i64(t64, hi, lo); } - gen_address_mask(dc, addr); - tcg_gen_qemu_st_i64(t64, addr, da.mem_idx, da.memop | MO_ALIGN); + tcg_gen_qemu_st_i64(t64, addr, da->mem_idx, da->memop | MO_ALIGN); } break; @@ -2657,7 +2701,7 @@ gen_stda_asi(DisasContext *dc, TCGv hi, TCGv addr, int insn, int rd) tcg_gen_concat_tl_i64(t64, lo, hi); tcg_gen_andi_tl(d_addr, addr, -8); for (i = 0; i < 32; i += 8) { - tcg_gen_qemu_st_i64(t64, d_addr, da.mem_idx, da.memop); + tcg_gen_qemu_st_i64(t64, d_addr, da->mem_idx, da->memop); tcg_gen_add_tl(d_addr, d_addr, eight); } } @@ -2667,12 +2711,12 @@ gen_stda_asi(DisasContext *dc, TCGv hi, TCGv addr, int insn, int rd) /* ??? In theory we've handled all of the ASIs that are valid for stda, and this should raise DAE_invalid_asi. */ { - TCGv_i32 r_asi = tcg_constant_i32(da.asi); - TCGv_i32 r_mop = tcg_constant_i32(da.memop); + TCGv_i32 r_asi = tcg_constant_i32(da->asi); + TCGv_i32 r_mop = tcg_constant_i32(da->memop); TCGv_i64 t64 = tcg_temp_new_i64(); /* See above. */ - if ((da.memop & MO_BSWAP) == MO_TE) { + if ((da->memop & MO_BSWAP) == MO_TE) { tcg_gen_concat_tl_i64(t64, lo, hi); } else { tcg_gen_concat_tl_i64(t64, hi, lo); @@ -2686,25 +2730,12 @@ gen_stda_asi(DisasContext *dc, TCGv hi, TCGv addr, int insn, int rd) } static void __attribute__((unused)) -gen_casx_asi(DisasContext *dc, TCGv addr, TCGv cmpv, int insn, int rd) +gen_stda_asi(DisasContext *dc, TCGv hi, TCGv addr, int insn, int rd) { DisasASI da = get_asi(dc, insn, MO_TEUQ); - TCGv oldv; - switch (da.type) { - case GET_ASI_EXCP: - return; - case GET_ASI_DIRECT: - oldv = tcg_temp_new(); - tcg_gen_atomic_cmpxchg_tl(oldv, addr, cmpv, gen_load_gpr(dc, rd), - da.mem_idx, da.memop | MO_ALIGN); - gen_store_gpr(dc, rd, oldv); - break; - default: - /* ??? Should be DAE_invalid_asi. */ - gen_exception(dc, TT_DATA_ACCESS); - break; - } + gen_address_mask(dc, addr); + gen_stda_asi0(dc, &da, addr, rd); } static TCGv get_src1(DisasContext *dc, unsigned int insn) From ebbbec921645d91dcb99bb59d817ede0bcbb4e45 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 13 Oct 2023 14:17:17 -0700 Subject: [PATCH 065/974] target/sparc: Use tcg_gen_qemu_{ld,st}_i128 for GET_ASI_DTWINX Perform one atomic 16-byte operation. The atomicity is required for the LDTXA instructions. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/translate.c | 48 +++++++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index e24945e50e..7ce9ab66c6 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -2594,11 +2594,27 @@ static void gen_ldda_asi0(DisasContext *dc, DisasASI *da, TCGv addr, int rd) return; case GET_ASI_DTWINX: - assert(TARGET_LONG_BITS == 64); - tcg_gen_qemu_ld_tl(hi, addr, da->mem_idx, da->memop | MO_ALIGN_16); - tcg_gen_addi_tl(addr, addr, 8); - tcg_gen_qemu_ld_tl(lo, addr, da->mem_idx, da->memop); +#ifdef TARGET_SPARC64 + { + MemOp mop = (da->memop & MO_BSWAP) | MO_128 | MO_ALIGN_16; + TCGv_i128 t = tcg_temp_new_i128(); + + tcg_gen_qemu_ld_i128(t, addr, da->mem_idx, mop); + /* + * Note that LE twinx acts as if each 64-bit register result is + * byte swapped. We perform one 128-bit LE load, so must swap + * the order of the writebacks. + */ + if ((mop & MO_BSWAP) == MO_TE) { + tcg_gen_extr_i128_i64(lo, hi, t); + } else { + tcg_gen_extr_i128_i64(hi, lo, t); + } + } break; +#else + g_assert_not_reached(); +#endif case GET_ASI_DIRECT: { @@ -2663,11 +2679,27 @@ static void gen_stda_asi0(DisasContext *dc, DisasASI *da, TCGv addr, int rd) break; case GET_ASI_DTWINX: - assert(TARGET_LONG_BITS == 64); - tcg_gen_qemu_st_tl(hi, addr, da->mem_idx, da->memop | MO_ALIGN_16); - tcg_gen_addi_tl(addr, addr, 8); - tcg_gen_qemu_st_tl(lo, addr, da->mem_idx, da->memop); +#ifdef TARGET_SPARC64 + { + MemOp mop = (da->memop & MO_BSWAP) | MO_128 | MO_ALIGN_16; + TCGv_i128 t = tcg_temp_new_i128(); + + /* + * Note that LE twinx acts as if each 64-bit register result is + * byte swapped. We perform one 128-bit LE store, so must swap + * the order of the construction. + */ + if ((mop & MO_BSWAP) == MO_TE) { + tcg_gen_concat_i64_i128(t, lo, hi); + } else { + tcg_gen_concat_i64_i128(t, hi, lo); + } + tcg_gen_qemu_st_i128(t, addr, da->mem_idx, mop); + } break; +#else + g_assert_not_reached(); +#endif case GET_ASI_DIRECT: { From 0880d20b2e3dffad6dfd95a8597ca0da86295bea Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 4 Oct 2023 19:50:47 -0700 Subject: [PATCH 066/974] target/sparc: Move simple integer load/store to decodetree Move LDUW, LDUB, LDUH, LDD, LDSW, LDSB, LDSH, LDX, STW, STB, STH, STD, STX. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 22 +++++ target/sparc/translate.c | 196 +++++++++++++++++++++++--------------- 2 files changed, 142 insertions(+), 76 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 137b7eb3c6..6197fbdb03 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -231,6 +231,28 @@ RESTORE 10 ..... 111101 ..... . ............. @r_r_ri DONE 10 00000 111110 00000 0 0000000000000 RETRY 10 00001 111110 00000 0 0000000000000 +## +## Major Opcode 11 -- load and store instructions +## + +&r_r_ri_asi rd rs1 rs2_or_imm asi imm:bool +@r_r_ri_na .. rd:5 ...... rs1:5 imm:1 rs2_or_imm:s13 &r_r_ri_asi asi=-1 + +LDUW 11 ..... 000000 ..... . ............. @r_r_ri_na +LDUB 11 ..... 000001 ..... . ............. @r_r_ri_na +LDUH 11 ..... 000010 ..... . ............. @r_r_ri_na +LDD 11 ..... 000011 ..... . ............. @r_r_ri_na +LDSW 11 ..... 001000 ..... . ............. @r_r_ri_na +LDSB 11 ..... 001001 ..... . ............. @r_r_ri_na +LDSH 11 ..... 001010 ..... . ............. @r_r_ri_na +LDX 11 ..... 001011 ..... . ............. @r_r_ri_na + +STW 11 ..... 000100 ..... . ............. @r_r_ri_na +STB 11 ..... 000101 ..... . ............. @r_r_ri_na +STH 11 ..... 000110 ..... . ............. @r_r_ri_na +STD 11 ..... 000111 ..... . ............. @r_r_ri_na +STX 11 ..... 001110 ..... . ............. @r_r_ri_na + NCP 10 ----- 110110 ----- --------- ----- # v8 CPop1 NCP 10 ----- 110111 ----- --------- ----- # v8 CPop2 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 7ce9ab66c6..db09255854 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -4536,6 +4536,117 @@ static bool do_done_retry(DisasContext *dc, bool done) TRANS(DONE, 64, do_done_retry, true) TRANS(RETRY, 64, do_done_retry, false) +/* + * Major opcode 11 -- load and store instructions + */ + +static TCGv gen_ldst_addr(DisasContext *dc, int rs1, bool imm, int rs2_or_imm) +{ + TCGv addr, tmp = NULL; + + /* For simplicity, we under-decoded the rs2 form. */ + if (!imm && rs2_or_imm & ~0x1f) { + return NULL; + } + + addr = gen_load_gpr(dc, rs1); + if (rs2_or_imm) { + tmp = tcg_temp_new(); + if (imm) { + tcg_gen_addi_tl(tmp, addr, rs2_or_imm); + } else { + tcg_gen_add_tl(tmp, addr, cpu_regs[rs2_or_imm]); + } + addr = tmp; + } + if (AM_CHECK(dc)) { + if (!tmp) { + tmp = tcg_temp_new(); + } + tcg_gen_ext32u_tl(tmp, addr); + addr = tmp; + } + return addr; +} + +static bool do_ld_gpr(DisasContext *dc, arg_r_r_ri_asi *a, MemOp mop) +{ + TCGv reg, addr = gen_ldst_addr(dc, a->rs1, a->imm, a->rs2_or_imm); + DisasASI da; + + if (addr == NULL) { + return false; + } + da = resolve_asi(dc, a->asi, mop); + + reg = gen_dest_gpr(dc, a->rd); + gen_ld_asi0(dc, &da, reg, addr); + gen_store_gpr(dc, a->rd, reg); + return advance_pc(dc); +} + +TRANS(LDUW, ALL, do_ld_gpr, a, MO_TEUL) +TRANS(LDUB, ALL, do_ld_gpr, a, MO_UB) +TRANS(LDUH, ALL, do_ld_gpr, a, MO_TEUW) +TRANS(LDSB, ALL, do_ld_gpr, a, MO_SB) +TRANS(LDSH, ALL, do_ld_gpr, a, MO_TESW) +TRANS(LDSW, 64, do_ld_gpr, a, MO_TESL) +TRANS(LDX, 64, do_ld_gpr, a, MO_TEUQ) + +static bool do_st_gpr(DisasContext *dc, arg_r_r_ri_asi *a, MemOp mop) +{ + TCGv reg, addr = gen_ldst_addr(dc, a->rs1, a->imm, a->rs2_or_imm); + DisasASI da; + + if (addr == NULL) { + return false; + } + da = resolve_asi(dc, a->asi, mop); + + reg = gen_load_gpr(dc, a->rd); + gen_st_asi0(dc, &da, reg, addr); + return advance_pc(dc); +} + +TRANS(STW, ALL, do_st_gpr, a, MO_TEUL) +TRANS(STB, ALL, do_st_gpr, a, MO_UB) +TRANS(STH, ALL, do_st_gpr, a, MO_TEUW) +TRANS(STX, 64, do_st_gpr, a, MO_TEUQ) + +static bool trans_LDD(DisasContext *dc, arg_r_r_ri_asi *a) +{ + TCGv addr; + DisasASI da; + + if (a->rd & 1) { + return false; + } + addr = gen_ldst_addr(dc, a->rs1, a->imm, a->rs2_or_imm); + if (addr == NULL) { + return false; + } + da = resolve_asi(dc, a->asi, MO_TEUQ); + gen_ldda_asi0(dc, &da, addr, a->rd); + return advance_pc(dc); +} + +static bool trans_STD(DisasContext *dc, arg_r_r_ri_asi *a) +{ + TCGv addr; + DisasASI da; + + if (a->rd & 1) { + return false; + } + addr = gen_ldst_addr(dc, a->rs1, a->imm, a->rs2_or_imm); + if (addr == NULL) { + return false; + } + da = resolve_asi(dc, a->asi, MO_TEUQ); + gen_stda_asi0(dc, &da, addr, a->rd); + return advance_pc(dc); +} + #define CHECK_IU_FEATURE(dc, FEATURE) \ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ goto illegal_insn; @@ -5359,47 +5470,15 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) switch (xop) { case 0x0: /* ld, V9 lduw, load unsigned word */ - gen_address_mask(dc, cpu_addr); - tcg_gen_qemu_ld_tl(cpu_val, cpu_addr, - dc->mem_idx, MO_TEUL | MO_ALIGN); - break; case 0x1: /* ldub, load unsigned byte */ - gen_address_mask(dc, cpu_addr); - tcg_gen_qemu_ld_tl(cpu_val, cpu_addr, - dc->mem_idx, MO_UB); - break; case 0x2: /* lduh, load unsigned halfword */ - gen_address_mask(dc, cpu_addr); - tcg_gen_qemu_ld_tl(cpu_val, cpu_addr, - dc->mem_idx, MO_TEUW | MO_ALIGN); - break; case 0x3: /* ldd, load double word */ - if (rd & 1) - goto illegal_insn; - else { - TCGv_i64 t64; - - gen_address_mask(dc, cpu_addr); - t64 = tcg_temp_new_i64(); - tcg_gen_qemu_ld_i64(t64, cpu_addr, - dc->mem_idx, MO_TEUQ | MO_ALIGN); - tcg_gen_trunc_i64_tl(cpu_val, t64); - tcg_gen_ext32u_tl(cpu_val, cpu_val); - gen_store_gpr(dc, rd + 1, cpu_val); - tcg_gen_shri_i64(t64, t64, 32); - tcg_gen_trunc_i64_tl(cpu_val, t64); - tcg_gen_ext32u_tl(cpu_val, cpu_val); - } - break; case 0x9: /* ldsb, load signed byte */ - gen_address_mask(dc, cpu_addr); - tcg_gen_qemu_ld_tl(cpu_val, cpu_addr, dc->mem_idx, MO_SB); - break; case 0xa: /* ldsh, load signed halfword */ - gen_address_mask(dc, cpu_addr); - tcg_gen_qemu_ld_tl(cpu_val, cpu_addr, - dc->mem_idx, MO_TESW | MO_ALIGN); - break; + g_assert_not_reached(); /* in decodetree */ + case 0x08: /* V9 ldsw */ + case 0x0b: /* V9 ldx */ + goto illegal_insn; /* in decodetree */ case 0xd: /* ldstub */ gen_ldstub(dc, cpu_val, cpu_addr, dc->mem_idx); break; @@ -5441,16 +5520,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) break; #endif #ifdef TARGET_SPARC64 - case 0x08: /* V9 ldsw */ - gen_address_mask(dc, cpu_addr); - tcg_gen_qemu_ld_tl(cpu_val, cpu_addr, - dc->mem_idx, MO_TESL | MO_ALIGN); - break; - case 0x0b: /* V9 ldx */ - gen_address_mask(dc, cpu_addr); - tcg_gen_qemu_ld_tl(cpu_val, cpu_addr, - dc->mem_idx, MO_TEUQ | MO_ALIGN); - break; case 0x18: /* V9 ldswa */ gen_ld_asi(dc, cpu_val, cpu_addr, insn, MO_TESL); break; @@ -5543,38 +5612,18 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) } } else if (xop < 8 || (xop >= 0x14 && xop < 0x18) || xop == 0xe || xop == 0x1e) { +#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) TCGv cpu_val = gen_load_gpr(dc, rd); +#endif switch (xop) { case 0x4: /* st, store word */ - gen_address_mask(dc, cpu_addr); - tcg_gen_qemu_st_tl(cpu_val, cpu_addr, - dc->mem_idx, MO_TEUL | MO_ALIGN); - break; case 0x5: /* stb, store byte */ - gen_address_mask(dc, cpu_addr); - tcg_gen_qemu_st_tl(cpu_val, cpu_addr, dc->mem_idx, MO_UB); - break; case 0x6: /* sth, store halfword */ - gen_address_mask(dc, cpu_addr); - tcg_gen_qemu_st_tl(cpu_val, cpu_addr, - dc->mem_idx, MO_TEUW | MO_ALIGN); - break; case 0x7: /* std, store double word */ - if (rd & 1) - goto illegal_insn; - else { - TCGv_i64 t64; - TCGv lo; - - gen_address_mask(dc, cpu_addr); - lo = gen_load_gpr(dc, rd + 1); - t64 = tcg_temp_new_i64(); - tcg_gen_concat_tl_i64(t64, lo, cpu_val); - tcg_gen_qemu_st_i64(t64, cpu_addr, - dc->mem_idx, MO_TEUQ | MO_ALIGN); - } - break; + g_assert_not_reached(); /* in decodetree */ + case 0x0e: /* V9 stx */ + goto illegal_insn; /* in decodetree */ #if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) case 0x14: /* sta, V9 stwa, store word alternate */ gen_st_asi(dc, cpu_val, cpu_addr, insn, MO_TEUL); @@ -5593,11 +5642,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) break; #endif #ifdef TARGET_SPARC64 - case 0x0e: /* V9 stx */ - gen_address_mask(dc, cpu_addr); - tcg_gen_qemu_st_tl(cpu_val, cpu_addr, - dc->mem_idx, MO_TEUQ | MO_ALIGN); - break; case 0x1e: /* V9 stxa */ gen_st_asi(dc, cpu_val, cpu_addr, insn, MO_TEUQ); break; From 42071fc16d0a26f710897e2be1e6962c7c883a8b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 4 Oct 2023 23:19:55 -0700 Subject: [PATCH 067/974] target/sparc: Move asi integer load/store to decodetree Move LDDA, LDSBA, LDSHA, LDSWA, LDUBA, LDUHA, LDUWA, LDXA, STBA, STDA, STHA, STWA, STXA. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 31 +++++++++ target/sparc/translate.c | 128 +++++--------------------------------- 2 files changed, 48 insertions(+), 111 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 6197fbdb03..280b19f033 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -237,6 +237,9 @@ RETRY 10 00001 111110 00000 0 0000000000000 &r_r_ri_asi rd rs1 rs2_or_imm asi imm:bool @r_r_ri_na .. rd:5 ...... rs1:5 imm:1 rs2_or_imm:s13 &r_r_ri_asi asi=-1 +@r_r_r_asi .. rd:5 ...... rs1:5 0 asi:8 rs2_or_imm:5 &r_r_ri_asi imm=0 +@r_r_i_asi .. rd:5 ...... rs1:5 1 rs2_or_imm:s13 \ + &r_r_ri_asi imm=1 asi=-2 LDUW 11 ..... 000000 ..... . ............. @r_r_ri_na LDUB 11 ..... 000001 ..... . ............. @r_r_ri_na @@ -253,6 +256,34 @@ STH 11 ..... 000110 ..... . ............. @r_r_ri_na STD 11 ..... 000111 ..... . ............. @r_r_ri_na STX 11 ..... 001110 ..... . ............. @r_r_ri_na +LDUW 11 ..... 010000 ..... . ............. @r_r_r_asi # LDUWA +LDUW 11 ..... 010000 ..... . ............. @r_r_i_asi # LDUWA +LDUB 11 ..... 010001 ..... . ............. @r_r_r_asi # LDUBA +LDUB 11 ..... 010001 ..... . ............. @r_r_i_asi # LDUBA +LDUH 11 ..... 010010 ..... . ............. @r_r_r_asi # LDUHA +LDUH 11 ..... 010010 ..... . ............. @r_r_i_asi # LDUHA +LDD 11 ..... 010011 ..... . ............. @r_r_r_asi # LDDA +LDD 11 ..... 010011 ..... . ............. @r_r_i_asi # LDDA +LDX 11 ..... 011011 ..... . ............. @r_r_r_asi # LDXA +LDX 11 ..... 011011 ..... . ............. @r_r_i_asi # LDXA +LDSB 11 ..... 011001 ..... . ............. @r_r_r_asi # LDSBA +LDSB 11 ..... 011001 ..... . ............. @r_r_i_asi # LDSBA +LDSH 11 ..... 011010 ..... . ............. @r_r_r_asi # LDSHA +LDSH 11 ..... 011010 ..... . ............. @r_r_i_asi # LDSHA +LDSW 11 ..... 011000 ..... . ............. @r_r_r_asi # LDSWA +LDSW 11 ..... 011000 ..... . ............. @r_r_i_asi # LDSWA + +STW 11 ..... 010100 ..... . ............. @r_r_r_asi # STWA +STW 11 ..... 010100 ..... . ............. @r_r_i_asi # STWA +STB 11 ..... 010101 ..... . ............. @r_r_r_asi # STBA +STB 11 ..... 010101 ..... . ............. @r_r_i_asi # STBA +STH 11 ..... 010110 ..... . ............. @r_r_r_asi # STHA +STH 11 ..... 010110 ..... . ............. @r_r_i_asi # STHA +STD 11 ..... 010111 ..... . ............. @r_r_r_asi # STDA +STD 11 ..... 010111 ..... . ............. @r_r_i_asi # STDA +STX 11 ..... 011110 ..... . ............. @r_r_r_asi # STXA +STX 11 ..... 011110 ..... . ............. @r_r_i_asi # STXA + NCP 10 ----- 110110 ----- --------- ----- # v8 CPop1 NCP 10 ----- 110111 ----- --------- ----- # v8 CPop2 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index db09255854..74cf3105f0 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -2162,7 +2162,7 @@ static void gen_helper_st_asi(TCGv_env e, TCGv a, TCGv_i64 r, } #endif -static void gen_ld_asi0(DisasContext *dc, DisasASI *da, TCGv dst, TCGv addr) +static void gen_ld_asi(DisasContext *dc, DisasASI *da, TCGv dst, TCGv addr) { switch (da->type) { case GET_ASI_EXCP: @@ -2193,16 +2193,7 @@ static void gen_ld_asi0(DisasContext *dc, DisasASI *da, TCGv dst, TCGv addr) } } -static void __attribute__((unused)) -gen_ld_asi(DisasContext *dc, TCGv dst, TCGv addr, int insn, MemOp memop) -{ - DisasASI da = get_asi(dc, insn, memop); - - gen_address_mask(dc, addr); - gen_ld_asi0(dc, &da, dst, addr); -} - -static void gen_st_asi0(DisasContext *dc, DisasASI *da, TCGv src, TCGv addr) +static void gen_st_asi(DisasContext *dc, DisasASI *da, TCGv src, TCGv addr) { switch (da->type) { case GET_ASI_EXCP: @@ -2274,15 +2265,6 @@ static void gen_st_asi0(DisasContext *dc, DisasASI *da, TCGv src, TCGv addr) } } -static void __attribute__((unused)) -gen_st_asi(DisasContext *dc, TCGv src, TCGv addr, int insn, MemOp memop) -{ - DisasASI da = get_asi(dc, insn, memop); - - gen_address_mask(dc, addr); - gen_st_asi0(dc, &da, src, addr); -} - static void gen_swap_asi0(DisasContext *dc, DisasASI *da, TCGv dst, TCGv src, TCGv addr) { @@ -2584,7 +2566,7 @@ gen_stf_asi(DisasContext *dc, TCGv addr, int insn, int size, int rd) } } -static void gen_ldda_asi0(DisasContext *dc, DisasASI *da, TCGv addr, int rd) +static void gen_ldda_asi(DisasContext *dc, DisasASI *da, TCGv addr, int rd) { TCGv hi = gen_dest_gpr(dc, rd); TCGv lo = gen_dest_gpr(dc, rd + 1); @@ -2660,16 +2642,7 @@ static void gen_ldda_asi0(DisasContext *dc, DisasASI *da, TCGv addr, int rd) gen_store_gpr(dc, rd + 1, lo); } -static void __attribute__((unused)) -gen_ldda_asi(DisasContext *dc, TCGv addr, int insn, int rd) -{ - DisasASI da = get_asi(dc, insn, MO_TEUQ); - - gen_address_mask(dc, addr); - gen_ldda_asi0(dc, &da, addr, rd); -} - -static void gen_stda_asi0(DisasContext *dc, DisasASI *da, TCGv addr, int rd) +static void gen_stda_asi(DisasContext *dc, DisasASI *da, TCGv addr, int rd) { TCGv hi = gen_load_gpr(dc, rd); TCGv lo = gen_load_gpr(dc, rd + 1); @@ -2761,15 +2734,6 @@ static void gen_stda_asi0(DisasContext *dc, DisasASI *da, TCGv addr, int rd) } } -static void __attribute__((unused)) -gen_stda_asi(DisasContext *dc, TCGv hi, TCGv addr, int insn, int rd) -{ - DisasASI da = get_asi(dc, insn, MO_TEUQ); - - gen_address_mask(dc, addr); - gen_stda_asi0(dc, &da, addr, rd); -} - static TCGv get_src1(DisasContext *dc, unsigned int insn) { unsigned int rs1 = GET_FIELD(insn, 13, 17); @@ -4580,7 +4544,7 @@ static bool do_ld_gpr(DisasContext *dc, arg_r_r_ri_asi *a, MemOp mop) da = resolve_asi(dc, a->asi, mop); reg = gen_dest_gpr(dc, a->rd); - gen_ld_asi0(dc, &da, reg, addr); + gen_ld_asi(dc, &da, reg, addr); gen_store_gpr(dc, a->rd, reg); return advance_pc(dc); } @@ -4604,7 +4568,7 @@ static bool do_st_gpr(DisasContext *dc, arg_r_r_ri_asi *a, MemOp mop) da = resolve_asi(dc, a->asi, mop); reg = gen_load_gpr(dc, a->rd); - gen_st_asi0(dc, &da, reg, addr); + gen_st_asi(dc, &da, reg, addr); return advance_pc(dc); } @@ -4626,7 +4590,7 @@ static bool trans_LDD(DisasContext *dc, arg_r_r_ri_asi *a) return false; } da = resolve_asi(dc, a->asi, MO_TEUQ); - gen_ldda_asi0(dc, &da, addr, a->rd); + gen_ldda_asi(dc, &da, addr, a->rd); return advance_pc(dc); } @@ -4643,7 +4607,7 @@ static bool trans_STD(DisasContext *dc, arg_r_r_ri_asi *a) return false; } da = resolve_asi(dc, a->asi, MO_TEUQ); - gen_stda_asi0(dc, &da, addr, a->rd); + gen_stda_asi(dc, &da, addr, a->rd); return advance_pc(dc); } @@ -5475,9 +5439,17 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x3: /* ldd, load double word */ case 0x9: /* ldsb, load signed byte */ case 0xa: /* ldsh, load signed halfword */ + case 0x10: /* lda, V9 lduwa, load word alternate */ + case 0x11: /* lduba, load unsigned byte alternate */ + case 0x12: /* lduha, load unsigned halfword alternate */ + case 0x13: /* ldda, load double word alternate */ + case 0x19: /* ldsba, load signed byte alternate */ + case 0x1a: /* ldsha, load signed halfword alternate */ g_assert_not_reached(); /* in decodetree */ case 0x08: /* V9 ldsw */ case 0x0b: /* V9 ldx */ + case 0x18: /* V9 ldswa */ + case 0x1b: /* V9 ldxa */ goto illegal_insn; /* in decodetree */ case 0xd: /* ldstub */ gen_ldstub(dc, cpu_val, cpu_addr, dc->mem_idx); @@ -5489,27 +5461,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) dc->mem_idx, MO_TEUL); break; #if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) - case 0x10: /* lda, V9 lduwa, load word alternate */ - gen_ld_asi(dc, cpu_val, cpu_addr, insn, MO_TEUL); - break; - case 0x11: /* lduba, load unsigned byte alternate */ - gen_ld_asi(dc, cpu_val, cpu_addr, insn, MO_UB); - break; - case 0x12: /* lduha, load unsigned halfword alternate */ - gen_ld_asi(dc, cpu_val, cpu_addr, insn, MO_TEUW); - break; - case 0x13: /* ldda, load double word alternate */ - if (rd & 1) { - goto illegal_insn; - } - gen_ldda_asi(dc, cpu_addr, insn, rd); - goto skip_move; - case 0x19: /* ldsba, load signed byte alternate */ - gen_ld_asi(dc, cpu_val, cpu_addr, insn, MO_SB); - break; - case 0x1a: /* ldsha, load signed halfword alternate */ - gen_ld_asi(dc, cpu_val, cpu_addr, insn, MO_TESW); - break; case 0x1d: /* ldstuba -- XXX: should be atomically */ gen_ldstub_asi(dc, cpu_val, cpu_addr, insn); break; @@ -5520,12 +5471,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) break; #endif #ifdef TARGET_SPARC64 - case 0x18: /* V9 ldswa */ - gen_ld_asi(dc, cpu_val, cpu_addr, insn, MO_TESL); - break; - case 0x1b: /* V9 ldxa */ - gen_ld_asi(dc, cpu_val, cpu_addr, insn, MO_TEUQ); - break; case 0x2d: /* V9 prefetch, no effect */ goto skip_move; case 0x30: /* V9 ldfa */ @@ -5557,7 +5502,7 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) goto illegal_insn; } gen_store_gpr(dc, rd, cpu_val); -#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) +#if defined(TARGET_SPARC64) skip_move: ; #endif } else if (xop >= 0x20 && xop < 0x24) { @@ -5610,45 +5555,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) default: goto illegal_insn; } - } else if (xop < 8 || (xop >= 0x14 && xop < 0x18) || - xop == 0xe || xop == 0x1e) { -#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) - TCGv cpu_val = gen_load_gpr(dc, rd); -#endif - - switch (xop) { - case 0x4: /* st, store word */ - case 0x5: /* stb, store byte */ - case 0x6: /* sth, store halfword */ - case 0x7: /* std, store double word */ - g_assert_not_reached(); /* in decodetree */ - case 0x0e: /* V9 stx */ - goto illegal_insn; /* in decodetree */ -#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) - case 0x14: /* sta, V9 stwa, store word alternate */ - gen_st_asi(dc, cpu_val, cpu_addr, insn, MO_TEUL); - break; - case 0x15: /* stba, store byte alternate */ - gen_st_asi(dc, cpu_val, cpu_addr, insn, MO_UB); - break; - case 0x16: /* stha, store halfword alternate */ - gen_st_asi(dc, cpu_val, cpu_addr, insn, MO_TEUW); - break; - case 0x17: /* stda, store double word alternate */ - if (rd & 1) { - goto illegal_insn; - } - gen_stda_asi(dc, cpu_val, cpu_addr, insn, rd); - break; -#endif -#ifdef TARGET_SPARC64 - case 0x1e: /* V9 stxa */ - gen_st_asi(dc, cpu_val, cpu_addr, insn, MO_TEUQ); - break; -#endif - default: - goto illegal_insn; - } } else if (xop > 0x23 && xop < 0x28) { if (gen_trap_ifnofpu(dc)) { goto jmp_insn; From cf07cd1e68b85b39d084714e50ad78b4053e5eef Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 4 Oct 2023 23:29:37 -0700 Subject: [PATCH 068/974] target/sparc: Move LDSTUB, LDSTUBA to decodetree Remove gen_ldstub_asi. Rename gen_ldstub_asi0 to gen_ldstub_asi. Merge gen_ldstub into gen_ldstub_asi. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 4 ++++ target/sparc/translate.c | 46 +++++++++++++++++++-------------------- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 280b19f033..2f950000b5 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -284,6 +284,10 @@ STD 11 ..... 010111 ..... . ............. @r_r_i_asi # STDA STX 11 ..... 011110 ..... . ............. @r_r_r_asi # STXA STX 11 ..... 011110 ..... . ............. @r_r_i_asi # STXA +LDSTUB 11 ..... 001101 ..... . ............. @r_r_ri_na +LDSTUB 11 ..... 011101 ..... . ............. @r_r_r_asi # LDSTUBA +LDSTUB 11 ..... 011101 ..... . ............. @r_r_i_asi # LDSTUBA + NCP 10 ----- 110110 ----- --------- ----- # v8 CPop1 NCP 10 ----- 110111 ----- --------- ----- # v8 CPop2 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 74cf3105f0..ddfb76af68 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -1892,13 +1892,6 @@ static void gen_swap(DisasContext *dc, TCGv dst, TCGv src, tcg_gen_atomic_xchg_tl(dst, addr, src, mmu_idx, memop | MO_ALIGN); } -static void gen_ldstub(DisasContext *dc, TCGv dst, TCGv addr, int mmu_idx) -{ - TCGv m1 = tcg_constant_tl(0xff); - gen_address_mask(dc, addr); - tcg_gen_atomic_xchg_tl(dst, addr, m1, mmu_idx, MO_UB); -} - /* asi moves */ typedef enum { GET_ASI_HELPER, @@ -2331,13 +2324,14 @@ gen_casx_asi(DisasContext *dc, TCGv addr, TCGv cmpv, int insn, int rd) gen_store_gpr(dc, rd, oldv); } -static void gen_ldstub_asi0(DisasContext *dc, DisasASI *da, TCGv dst, TCGv addr) +static void gen_ldstub_asi(DisasContext *dc, DisasASI *da, TCGv dst, TCGv addr) { switch (da->type) { case GET_ASI_EXCP: break; case GET_ASI_DIRECT: - gen_ldstub(dc, dst, addr, da->mem_idx); + tcg_gen_atomic_xchg_tl(dst, addr, tcg_constant_tl(0xff), + da->mem_idx, MO_UB); break; default: /* ??? In theory, this should be raise DAE_invalid_asi. @@ -2365,15 +2359,6 @@ static void gen_ldstub_asi0(DisasContext *dc, DisasASI *da, TCGv dst, TCGv addr) } } -static void __attribute__((unused)) -gen_ldstub_asi(DisasContext *dc, TCGv dst, TCGv addr, int insn) -{ - DisasASI da = get_asi(dc, insn, MO_UB); - - gen_address_mask(dc, addr); - gen_ldstub_asi0(dc, &da, dst, addr); -} - static void __attribute__((unused)) gen_ldf_asi(DisasContext *dc, TCGv addr, int insn, int size, int rd) { @@ -4611,6 +4596,23 @@ static bool trans_STD(DisasContext *dc, arg_r_r_ri_asi *a) return advance_pc(dc); } +static bool trans_LDSTUB(DisasContext *dc, arg_r_r_ri_asi *a) +{ + TCGv addr, reg; + DisasASI da; + + addr = gen_ldst_addr(dc, a->rs1, a->imm, a->rs2_or_imm); + if (addr == NULL) { + return false; + } + da = resolve_asi(dc, a->asi, MO_UB); + + reg = gen_dest_gpr(dc, a->rd); + gen_ldstub_asi(dc, &da, reg, addr); + gen_store_gpr(dc, a->rd, reg); + return advance_pc(dc); +} + #define CHECK_IU_FEATURE(dc, FEATURE) \ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ goto illegal_insn; @@ -5439,21 +5441,20 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x3: /* ldd, load double word */ case 0x9: /* ldsb, load signed byte */ case 0xa: /* ldsh, load signed halfword */ + case 0xd: /* ldstub */ case 0x10: /* lda, V9 lduwa, load word alternate */ case 0x11: /* lduba, load unsigned byte alternate */ case 0x12: /* lduha, load unsigned halfword alternate */ case 0x13: /* ldda, load double word alternate */ case 0x19: /* ldsba, load signed byte alternate */ case 0x1a: /* ldsha, load signed halfword alternate */ + case 0x1d: /* ldstuba */ g_assert_not_reached(); /* in decodetree */ case 0x08: /* V9 ldsw */ case 0x0b: /* V9 ldx */ case 0x18: /* V9 ldswa */ case 0x1b: /* V9 ldxa */ goto illegal_insn; /* in decodetree */ - case 0xd: /* ldstub */ - gen_ldstub(dc, cpu_val, cpu_addr, dc->mem_idx); - break; case 0x0f: /* swap, swap register with memory. Also atomically */ cpu_src1 = gen_load_gpr(dc, rd); @@ -5461,9 +5462,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) dc->mem_idx, MO_TEUL); break; #if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) - case 0x1d: /* ldstuba -- XXX: should be atomically */ - gen_ldstub_asi(dc, cpu_val, cpu_addr, insn); - break; case 0x1f: /* swapa, swap reg with alt. memory. Also atomically */ cpu_src1 = gen_load_gpr(dc, rd); From dca544b991f21319f522efa7c2881e2944481154 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 4 Oct 2023 23:45:21 -0700 Subject: [PATCH 069/974] target/sparc: Move SWAP, SWAPA to decodetree Remove gen_swap_asi. Rename gen_swap_asi0 to gen_swap_asi. Merge gen_swap into gen_swap_asi. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 4 +++ target/sparc/translate.c | 58 +++++++++++++++++---------------------- 2 files changed, 29 insertions(+), 33 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 2f950000b5..9c4597317c 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -288,6 +288,10 @@ LDSTUB 11 ..... 001101 ..... . ............. @r_r_ri_na LDSTUB 11 ..... 011101 ..... . ............. @r_r_r_asi # LDSTUBA LDSTUB 11 ..... 011101 ..... . ............. @r_r_i_asi # LDSTUBA +SWAP 11 ..... 001111 ..... . ............. @r_r_ri_na +SWAP 11 ..... 011111 ..... . ............. @r_r_r_asi # SWAPA +SWAP 11 ..... 011111 ..... . ............. @r_r_i_asi # SWAPA + NCP 10 ----- 110110 ----- --------- ----- # v8 CPop1 NCP 10 ----- 110111 ----- --------- ----- # v8 CPop2 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index ddfb76af68..29bfc98522 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -1885,13 +1885,6 @@ static void gen_ne_fop_QD(DisasContext *dc, int rd, int rs, gen_update_fprs_dirty(dc, QFPREG(rd)); } -static void gen_swap(DisasContext *dc, TCGv dst, TCGv src, - TCGv addr, int mmu_idx, MemOp memop) -{ - gen_address_mask(dc, addr); - tcg_gen_atomic_xchg_tl(dst, addr, src, mmu_idx, memop | MO_ALIGN); -} - /* asi moves */ typedef enum { GET_ASI_HELPER, @@ -2258,14 +2251,15 @@ static void gen_st_asi(DisasContext *dc, DisasASI *da, TCGv src, TCGv addr) } } -static void gen_swap_asi0(DisasContext *dc, DisasASI *da, - TCGv dst, TCGv src, TCGv addr) +static void gen_swap_asi(DisasContext *dc, DisasASI *da, + TCGv dst, TCGv src, TCGv addr) { switch (da->type) { case GET_ASI_EXCP: break; case GET_ASI_DIRECT: - gen_swap(dc, dst, src, addr, da->mem_idx, da->memop); + tcg_gen_atomic_xchg_tl(dst, addr, src, + da->mem_idx, da->memop | MO_ALIGN); break; default: /* ??? Should be DAE_invalid_asi. */ @@ -2274,15 +2268,6 @@ static void gen_swap_asi0(DisasContext *dc, DisasASI *da, } } -static void __attribute__((unused)) -gen_swap_asi(DisasContext *dc, TCGv dst, TCGv src, TCGv addr, int insn) -{ - DisasASI da = get_asi(dc, insn, MO_TEUL); - - gen_address_mask(dc, addr); - gen_swap_asi0(dc, &da, dst, src, addr); -} - static void gen_cas_asi0(DisasContext *dc, DisasASI *da, TCGv oldv, TCGv newv, TCGv cmpv, TCGv addr) { @@ -4613,6 +4598,24 @@ static bool trans_LDSTUB(DisasContext *dc, arg_r_r_ri_asi *a) return advance_pc(dc); } +static bool trans_SWAP(DisasContext *dc, arg_r_r_ri_asi *a) +{ + TCGv addr, dst, src; + DisasASI da; + + addr = gen_ldst_addr(dc, a->rs1, a->imm, a->rs2_or_imm); + if (addr == NULL) { + return false; + } + da = resolve_asi(dc, a->asi, MO_TEUL); + + dst = gen_dest_gpr(dc, a->rd); + src = gen_load_gpr(dc, a->rd); + gen_swap_asi(dc, &da, dst, src, addr); + gen_store_gpr(dc, a->rd, dst); + return advance_pc(dc); +} + #define CHECK_IU_FEATURE(dc, FEATURE) \ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ goto illegal_insn; @@ -4624,7 +4627,7 @@ static bool trans_LDSTUB(DisasContext *dc, arg_r_r_ri_asi *a) static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) { unsigned int opc, rs1, rs2, rd; - TCGv cpu_src1; + TCGv cpu_src1 __attribute__((unused)); TCGv cpu_src2 __attribute__((unused)); TCGv_i32 cpu_src1_32, cpu_src2_32, cpu_dst_32; TCGv_i64 cpu_src1_64, cpu_src2_64, cpu_dst_64; @@ -5442,6 +5445,7 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x9: /* ldsb, load signed byte */ case 0xa: /* ldsh, load signed halfword */ case 0xd: /* ldstub */ + case 0x0f: /* swap */ case 0x10: /* lda, V9 lduwa, load word alternate */ case 0x11: /* lduba, load unsigned byte alternate */ case 0x12: /* lduha, load unsigned halfword alternate */ @@ -5449,25 +5453,13 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x19: /* ldsba, load signed byte alternate */ case 0x1a: /* ldsha, load signed halfword alternate */ case 0x1d: /* ldstuba */ + case 0x1f: /* swapa */ g_assert_not_reached(); /* in decodetree */ case 0x08: /* V9 ldsw */ case 0x0b: /* V9 ldx */ case 0x18: /* V9 ldswa */ case 0x1b: /* V9 ldxa */ goto illegal_insn; /* in decodetree */ - case 0x0f: - /* swap, swap register with memory. Also atomically */ - cpu_src1 = gen_load_gpr(dc, rd); - gen_swap(dc, cpu_val, cpu_src1, cpu_addr, - dc->mem_idx, MO_TEUL); - break; -#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) - case 0x1f: /* swapa, swap reg with alt. memory. Also - atomically */ - cpu_src1 = gen_load_gpr(dc, rd); - gen_swap_asi(dc, cpu_val, cpu_src1, cpu_addr, insn); - break; -#endif #ifdef TARGET_SPARC64 case 0x2d: /* V9 prefetch, no effect */ goto skip_move; From d0a11d25f0332dbaeb3a4f733a5cfb23ed40413d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 5 Oct 2023 00:09:36 -0700 Subject: [PATCH 070/974] target/sparc: Move CASA, CASXA to decodetree Remove gen_cas_asi, gen_casx_asi. Rename gen_cas_asi0 to gen_cas_asi. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 7 ++++ target/sparc/translate.c | 72 ++++++++++++++++----------------------- 2 files changed, 36 insertions(+), 43 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 9c4597317c..82c484fbc7 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -240,6 +240,8 @@ RETRY 10 00001 111110 00000 0 0000000000000 @r_r_r_asi .. rd:5 ...... rs1:5 0 asi:8 rs2_or_imm:5 &r_r_ri_asi imm=0 @r_r_i_asi .. rd:5 ...... rs1:5 1 rs2_or_imm:s13 \ &r_r_ri_asi imm=1 asi=-2 +@casa_imm .. rd:5 ...... rs1:5 1 00000000 rs2_or_imm:5 \ + &r_r_ri_asi imm=1 asi=-2 LDUW 11 ..... 000000 ..... . ............. @r_r_ri_na LDUB 11 ..... 000001 ..... . ............. @r_r_ri_na @@ -292,6 +294,11 @@ SWAP 11 ..... 001111 ..... . ............. @r_r_ri_na SWAP 11 ..... 011111 ..... . ............. @r_r_r_asi # SWAPA SWAP 11 ..... 011111 ..... . ............. @r_r_i_asi # SWAPA +CASA 11 ..... 111100 ..... . ............. @r_r_r_asi +CASA 11 ..... 111100 ..... . ............. @casa_imm +CASXA 11 ..... 111110 ..... . ............. @r_r_r_asi +CASXA 11 ..... 111110 ..... . ............. @casa_imm + NCP 10 ----- 110110 ----- --------- ----- # v8 CPop1 NCP 10 ----- 110111 ----- --------- ----- # v8 CPop2 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 29bfc98522..2175c00ded 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -2268,8 +2268,8 @@ static void gen_swap_asi(DisasContext *dc, DisasASI *da, } } -static void gen_cas_asi0(DisasContext *dc, DisasASI *da, - TCGv oldv, TCGv newv, TCGv cmpv, TCGv addr) +static void gen_cas_asi(DisasContext *dc, DisasASI *da, + TCGv oldv, TCGv newv, TCGv cmpv, TCGv addr) { switch (da->type) { case GET_ASI_EXCP: @@ -2285,30 +2285,6 @@ static void gen_cas_asi0(DisasContext *dc, DisasASI *da, } } -static void __attribute__((unused)) -gen_cas_asi(DisasContext *dc, TCGv addr, TCGv cmpv, int insn, int rd) -{ - DisasASI da = get_asi(dc, insn, MO_TEUL); - TCGv oldv = gen_dest_gpr(dc, rd); - TCGv newv = gen_load_gpr(dc, rd); - - gen_address_mask(dc, addr); - gen_cas_asi0(dc, &da, oldv, newv, cmpv, addr); - gen_store_gpr(dc, rd, oldv); -} - -static void __attribute__((unused)) -gen_casx_asi(DisasContext *dc, TCGv addr, TCGv cmpv, int insn, int rd) -{ - DisasASI da = get_asi(dc, insn, MO_TEUQ); - TCGv oldv = gen_dest_gpr(dc, rd); - TCGv newv = gen_load_gpr(dc, rd); - - gen_address_mask(dc, addr); - gen_cas_asi0(dc, &da, oldv, newv, cmpv, addr); - gen_store_gpr(dc, rd, oldv); -} - static void gen_ldstub_asi(DisasContext *dc, DisasASI *da, TCGv dst, TCGv addr) { switch (da->type) { @@ -2913,6 +2889,7 @@ static void gen_faligndata(TCGv dst, TCGv gsr, TCGv s1, TCGv s2) #ifdef TARGET_SPARC64 # define avail_32(C) false # define avail_ASR17(C) false +# define avail_CASA(C) true # define avail_DIV(C) true # define avail_MUL(C) true # define avail_POWERDOWN(C) false @@ -2922,6 +2899,7 @@ static void gen_faligndata(TCGv dst, TCGv gsr, TCGv s1, TCGv s2) #else # define avail_32(C) true # define avail_ASR17(C) ((C)->def->features & CPU_FEATURE_ASR17) +# define avail_CASA(C) ((C)->def->features & CPU_FEATURE_CASA) # define avail_DIV(C) ((C)->def->features & CPU_FEATURE_DIV) # define avail_MUL(C) ((C)->def->features & CPU_FEATURE_MUL) # define avail_POWERDOWN(C) ((C)->def->features & CPU_FEATURE_POWERDOWN) @@ -4616,6 +4594,28 @@ static bool trans_SWAP(DisasContext *dc, arg_r_r_ri_asi *a) return advance_pc(dc); } +static bool do_casa(DisasContext *dc, arg_r_r_ri_asi *a, MemOp mop) +{ + TCGv addr, o, n, c; + DisasASI da; + + addr = gen_ldst_addr(dc, a->rs1, true, 0); + if (addr == NULL) { + return false; + } + da = resolve_asi(dc, a->asi, mop); + + o = gen_dest_gpr(dc, a->rd); + n = gen_load_gpr(dc, a->rd); + c = gen_load_gpr(dc, a->rs2_or_imm); + gen_cas_asi(dc, &da, o, n, c, addr); + gen_store_gpr(dc, a->rd, o); + return advance_pc(dc); +} + +TRANS(CASA, CASA, do_casa, a, MO_TEUL) +TRANS(CASXA, 64, do_casa, a, MO_TEUQ) + #define CHECK_IU_FEATURE(dc, FEATURE) \ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ goto illegal_insn; @@ -5419,9 +5419,7 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) TCGv cpu_addr = tcg_temp_new(); tcg_gen_mov_tl(cpu_addr, get_src1(dc, insn)); - if (xop == 0x3c || xop == 0x3e) { - /* V9 casa/casxa : no offset */ - } else if (IS_IMM) { /* immediate */ + if (IS_IMM) { /* immediate */ simm = GET_FIELDs(insn, 19, 31); if (simm != 0) { tcg_gen_addi_tl(cpu_addr, cpu_addr, simm); @@ -5634,22 +5632,10 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) } gen_stf_asi(dc, cpu_addr, insn, 8, DFPREG(rd)); break; +#endif case 0x3e: /* V9 casxa */ - rs2 = GET_FIELD(insn, 27, 31); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_casx_asi(dc, cpu_addr, cpu_src2, insn, rd); - break; -#endif -#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) case 0x3c: /* V9 or LEON3 casa */ -#ifndef TARGET_SPARC64 - CHECK_IU_FEATURE(dc, CASA); -#endif - rs2 = GET_FIELD(insn, 27, 31); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_cas_asi(dc, cpu_addr, cpu_src2, insn, rd); - break; -#endif + goto illegal_insn; /* in decodetree */ default: goto illegal_insn; } From 5458fd3153b24609c8862c995f2aff86e70a67e4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 5 Oct 2023 00:20:37 -0700 Subject: [PATCH 071/974] target/sparc: Move PREFETCH, PREFETCHA to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 8 ++++++-- target/sparc/translate.c | 23 ++++++++--------------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 82c484fbc7..aa452f1d00 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -231,6 +231,9 @@ RESTORE 10 ..... 111101 ..... . ............. @r_r_ri DONE 10 00000 111110 00000 0 0000000000000 RETRY 10 00001 111110 00000 0 0000000000000 +NCP 10 ----- 110110 ----- --------- ----- # v8 CPop1 +NCP 10 ----- 110111 ----- --------- ----- # v8 CPop2 + ## ## Major Opcode 11 -- load and store instructions ## @@ -299,8 +302,9 @@ CASA 11 ..... 111100 ..... . ............. @casa_imm CASXA 11 ..... 111110 ..... . ............. @r_r_r_asi CASXA 11 ..... 111110 ..... . ............. @casa_imm -NCP 10 ----- 110110 ----- --------- ----- # v8 CPop1 -NCP 10 ----- 110111 ----- --------- ----- # v8 CPop2 +NOP_v9 11 ----- 101101 ----- 0 00000000 ----- # PREFETCH +NOP_v9 11 ----- 101101 ----- 1 ------------- # PREFETCH +NOP_v9 11 ----- 111101 ----- - ------------- # PREFETCHA NCP 11 ----- 110000 ----- --------- ----- # v8 LDC NCP 11 ----- 110001 ----- --------- ----- # v8 LDCSR diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 2175c00ded..363d8b7fc8 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -4031,17 +4031,12 @@ static bool trans_NOP(DisasContext *dc, arg_NOP *a) return advance_pc(dc); } -static bool trans_NOP_v7(DisasContext *dc, arg_NOP_v7 *a) -{ - /* - * TODO: Need a feature bit for sparcv8. - * In the meantime, treat all 32-bit cpus like sparcv7. - */ - if (avail_32(dc)) { - return advance_pc(dc); - } - return false; -} +/* + * TODO: Need a feature bit for sparcv8. + * In the meantime, treat all 32-bit cpus like sparcv7. + */ +TRANS(NOP_v7, 32, trans_NOP, a) +TRANS(NOP_v9, 64, trans_NOP, a) static bool do_arith_int(DisasContext *dc, arg_r_r_ri_cc *a, int cc_op, void (*func)(TCGv, TCGv, TCGv), @@ -5457,10 +5452,10 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x0b: /* V9 ldx */ case 0x18: /* V9 ldswa */ case 0x1b: /* V9 ldxa */ + case 0x2d: /* V9 prefetch */ + case 0x3d: /* V9 prefetcha */ goto illegal_insn; /* in decodetree */ #ifdef TARGET_SPARC64 - case 0x2d: /* V9 prefetch, no effect */ - goto skip_move; case 0x30: /* V9 ldfa */ if (gen_trap_ifnofpu(dc)) { goto jmp_insn; @@ -5475,8 +5470,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) gen_ldf_asi(dc, cpu_addr, insn, 8, DFPREG(rd)); gen_update_fprs_dirty(dc, DFPREG(rd)); goto skip_move; - case 0x3d: /* V9 prefetcha, no effect */ - goto skip_move; case 0x32: /* V9 ldqfa */ CHECK_FPU_FEATURE(dc, FLOAT128); if (gen_trap_ifnofpu(dc)) { From 3259b9e2fd4b3d92b61159aa65f5b515bf573c4f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 5 Oct 2023 01:37:06 -0700 Subject: [PATCH 072/974] target/sparc: Split out fp ldst functions with asi precomputed Take the operation size from the MemOp instead of a separate parameter. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/translate.c | 138 +++++++++++++++++++++++---------------- 1 file changed, 80 insertions(+), 58 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 363d8b7fc8..55d6f83736 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -2320,35 +2320,41 @@ static void gen_ldstub_asi(DisasContext *dc, DisasASI *da, TCGv dst, TCGv addr) } } -static void __attribute__((unused)) -gen_ldf_asi(DisasContext *dc, TCGv addr, int insn, int size, int rd) +static void gen_ldf_asi0(DisasContext *dc, DisasASI *da, MemOp orig_size, + TCGv addr, int rd) { - DisasASI da = get_asi(dc, insn, (size == 4 ? MO_TEUL : MO_TEUQ)); + MemOp memop = da->memop; + MemOp size = memop & MO_SIZE; TCGv_i32 d32; TCGv_i64 d64; - switch (da.type) { + /* TODO: Use 128-bit load/store below. */ + if (size == MO_128) { + memop = (memop & ~MO_SIZE) | MO_64; + } + + switch (da->type) { case GET_ASI_EXCP: break; case GET_ASI_DIRECT: - gen_address_mask(dc, addr); + memop |= MO_ALIGN_4; switch (size) { - case 4: + case MO_32: d32 = gen_dest_fpr_F(dc); - tcg_gen_qemu_ld_i32(d32, addr, da.mem_idx, da.memop | MO_ALIGN); + tcg_gen_qemu_ld_i32(d32, addr, da->mem_idx, memop); gen_store_fpr_F(dc, rd, d32); break; - case 8: - tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2], addr, da.mem_idx, - da.memop | MO_ALIGN_4); + + case MO_64: + tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2], addr, da->mem_idx, memop); break; - case 16: + + case MO_128: d64 = tcg_temp_new_i64(); - tcg_gen_qemu_ld_i64(d64, addr, da.mem_idx, da.memop | MO_ALIGN_4); + tcg_gen_qemu_ld_i64(d64, addr, da->mem_idx, memop); tcg_gen_addi_tl(addr, addr, 8); - tcg_gen_qemu_ld_i64(cpu_fpr[rd/2+1], addr, da.mem_idx, - da.memop | MO_ALIGN_4); + tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2 + 1], addr, da->mem_idx, memop); tcg_gen_mov_i64(cpu_fpr[rd / 2], d64); break; default: @@ -2358,24 +2364,19 @@ gen_ldf_asi(DisasContext *dc, TCGv addr, int insn, int size, int rd) case GET_ASI_BLOCK: /* Valid for lddfa on aligned registers only. */ - if (size == 8 && (rd & 7) == 0) { - MemOp memop; + if (orig_size == MO_64 && (rd & 7) == 0) { TCGv eight; int i; - gen_address_mask(dc, addr); - /* The first operation checks required alignment. */ - memop = da.memop | MO_ALIGN_64; eight = tcg_constant_tl(8); for (i = 0; ; ++i) { - tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2 + i], addr, - da.mem_idx, memop); + tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2 + i], addr, da->mem_idx, + memop | (i == 0 ? MO_ALIGN_64 : 0)); if (i == 7) { break; } tcg_gen_add_tl(addr, addr, eight); - memop = da.memop; } } else { gen_exception(dc, TT_ILL_INSN); @@ -2384,10 +2385,9 @@ gen_ldf_asi(DisasContext *dc, TCGv addr, int insn, int size, int rd) case GET_ASI_SHORT: /* Valid for lddfa only. */ - if (size == 8) { - gen_address_mask(dc, addr); - tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2], addr, da.mem_idx, - da.memop | MO_ALIGN); + if (orig_size == MO_64) { + tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2], addr, da->mem_idx, + memop | MO_ALIGN); } else { gen_exception(dc, TT_ILL_INSN); } @@ -2395,8 +2395,8 @@ gen_ldf_asi(DisasContext *dc, TCGv addr, int insn, int size, int rd) default: { - TCGv_i32 r_asi = tcg_constant_i32(da.asi); - TCGv_i32 r_mop = tcg_constant_i32(da.memop | MO_ALIGN); + TCGv_i32 r_asi = tcg_constant_i32(da->asi); + TCGv_i32 r_mop = tcg_constant_i32(memop | MO_ALIGN); save_state(dc); /* According to the table in the UA2011 manual, the only @@ -2404,21 +2404,23 @@ gen_ldf_asi(DisasContext *dc, TCGv addr, int insn, int size, int rd) the NO_FAULT asis. We still need a helper for these, but we can just use the integer asi helper for them. */ switch (size) { - case 4: + case MO_32: d64 = tcg_temp_new_i64(); gen_helper_ld_asi(d64, tcg_env, addr, r_asi, r_mop); d32 = gen_dest_fpr_F(dc); tcg_gen_extrl_i64_i32(d32, d64); gen_store_fpr_F(dc, rd, d32); break; - case 8: - gen_helper_ld_asi(cpu_fpr[rd / 2], tcg_env, addr, r_asi, r_mop); + case MO_64: + gen_helper_ld_asi(cpu_fpr[rd / 2], tcg_env, addr, + r_asi, r_mop); break; - case 16: + case MO_128: d64 = tcg_temp_new_i64(); gen_helper_ld_asi(d64, tcg_env, addr, r_asi, r_mop); tcg_gen_addi_tl(addr, addr, 8); - gen_helper_ld_asi(cpu_fpr[rd/2+1], tcg_env, addr, r_asi, r_mop); + gen_helper_ld_asi(cpu_fpr[rd / 2 + 1], tcg_env, addr, + r_asi, r_mop); tcg_gen_mov_i64(cpu_fpr[rd / 2], d64); break; default: @@ -2430,36 +2432,52 @@ gen_ldf_asi(DisasContext *dc, TCGv addr, int insn, int size, int rd) } static void __attribute__((unused)) -gen_stf_asi(DisasContext *dc, TCGv addr, int insn, int size, int rd) +gen_ldf_asi(DisasContext *dc, TCGv addr, int insn, int size, int rd) { - DisasASI da = get_asi(dc, insn, (size == 4 ? MO_TEUL : MO_TEUQ)); + MemOp sz = ctz32(size); + DisasASI da = get_asi(dc, insn, MO_TE | sz); + + gen_address_mask(dc, addr); + gen_ldf_asi0(dc, &da, sz, addr, rd); +} + +static void gen_stf_asi0(DisasContext *dc, DisasASI *da, MemOp orig_size, + TCGv addr, int rd) +{ + MemOp memop = da->memop; + MemOp size = memop & MO_SIZE; TCGv_i32 d32; - switch (da.type) { + /* TODO: Use 128-bit load/store below. */ + if (size == MO_128) { + memop = (memop & ~MO_SIZE) | MO_64; + } + + switch (da->type) { case GET_ASI_EXCP: break; case GET_ASI_DIRECT: - gen_address_mask(dc, addr); + memop |= MO_ALIGN_4; switch (size) { - case 4: + case MO_32: d32 = gen_load_fpr_F(dc, rd); - tcg_gen_qemu_st_i32(d32, addr, da.mem_idx, da.memop | MO_ALIGN); + tcg_gen_qemu_st_i32(d32, addr, da->mem_idx, memop | MO_ALIGN); break; - case 8: - tcg_gen_qemu_st_i64(cpu_fpr[rd / 2], addr, da.mem_idx, - da.memop | MO_ALIGN_4); + case MO_64: + tcg_gen_qemu_st_i64(cpu_fpr[rd / 2], addr, da->mem_idx, + memop | MO_ALIGN_4); break; - case 16: + case MO_128: /* Only 4-byte alignment required. However, it is legal for the cpu to signal the alignment fault, and the OS trap handler is required to fix it up. Requiring 16-byte alignment here avoids having to probe the second page before performing the first write. */ - tcg_gen_qemu_st_i64(cpu_fpr[rd / 2], addr, da.mem_idx, - da.memop | MO_ALIGN_16); + tcg_gen_qemu_st_i64(cpu_fpr[rd / 2], addr, da->mem_idx, + memop | MO_ALIGN_16); tcg_gen_addi_tl(addr, addr, 8); - tcg_gen_qemu_st_i64(cpu_fpr[rd/2+1], addr, da.mem_idx, da.memop); + tcg_gen_qemu_st_i64(cpu_fpr[rd / 2 + 1], addr, da->mem_idx, memop); break; default: g_assert_not_reached(); @@ -2468,24 +2486,19 @@ gen_stf_asi(DisasContext *dc, TCGv addr, int insn, int size, int rd) case GET_ASI_BLOCK: /* Valid for stdfa on aligned registers only. */ - if (size == 8 && (rd & 7) == 0) { - MemOp memop; + if (orig_size == MO_64 && (rd & 7) == 0) { TCGv eight; int i; - gen_address_mask(dc, addr); - /* The first operation checks required alignment. */ - memop = da.memop | MO_ALIGN_64; eight = tcg_constant_tl(8); for (i = 0; ; ++i) { - tcg_gen_qemu_st_i64(cpu_fpr[rd / 2 + i], addr, - da.mem_idx, memop); + tcg_gen_qemu_st_i64(cpu_fpr[rd / 2 + i], addr, da->mem_idx, + memop | (i == 0 ? MO_ALIGN_64 : 0)); if (i == 7) { break; } tcg_gen_add_tl(addr, addr, eight); - memop = da.memop; } } else { gen_exception(dc, TT_ILL_INSN); @@ -2494,10 +2507,9 @@ gen_stf_asi(DisasContext *dc, TCGv addr, int insn, int size, int rd) case GET_ASI_SHORT: /* Valid for stdfa only. */ - if (size == 8) { - gen_address_mask(dc, addr); - tcg_gen_qemu_st_i64(cpu_fpr[rd / 2], addr, da.mem_idx, - da.memop | MO_ALIGN); + if (orig_size == MO_64) { + tcg_gen_qemu_st_i64(cpu_fpr[rd / 2], addr, da->mem_idx, + memop | MO_ALIGN); } else { gen_exception(dc, TT_ILL_INSN); } @@ -2512,6 +2524,16 @@ gen_stf_asi(DisasContext *dc, TCGv addr, int insn, int size, int rd) } } +static void __attribute__((unused)) +gen_stf_asi(DisasContext *dc, TCGv addr, int insn, int size, int rd) +{ + MemOp sz = ctz32(size); + DisasASI da = get_asi(dc, insn, MO_TE | sz); + + gen_address_mask(dc, addr); + gen_stf_asi0(dc, &da, sz, addr, rd); +} + static void gen_ldda_asi(DisasContext *dc, DisasASI *da, TCGv addr, int rd) { TCGv hi = gen_dest_gpr(dc, rd); From 06c060d9e5bad5c5317cb158fcce2e2ae3f942a8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 5 Oct 2023 01:38:50 -0700 Subject: [PATCH 073/974] target/sparc: Move simple fp load/store to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 19 ++++ target/sparc/translate.c | 194 ++++++++++++++++++-------------------- 2 files changed, 113 insertions(+), 100 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index aa452f1d00..1150890e44 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -238,8 +238,16 @@ NCP 10 ----- 110111 ----- --------- ----- # v8 CPop2 ## Major Opcode 11 -- load and store instructions ## +%dfp_rd 25:5 !function=extract_dfpreg +%qfp_rd 25:5 !function=extract_qfpreg + &r_r_ri_asi rd rs1 rs2_or_imm asi imm:bool @r_r_ri_na .. rd:5 ...... rs1:5 imm:1 rs2_or_imm:s13 &r_r_ri_asi asi=-1 +@d_r_ri_na .. ..... ...... rs1:5 imm:1 rs2_or_imm:s13 \ + &r_r_ri_asi rd=%dfp_rd asi=-1 +@q_r_ri_na .. ..... ...... rs1:5 imm:1 rs2_or_imm:s13 \ + &r_r_ri_asi rd=%qfp_rd asi=-1 + @r_r_r_asi .. rd:5 ...... rs1:5 0 asi:8 rs2_or_imm:5 &r_r_ri_asi imm=0 @r_r_i_asi .. rd:5 ...... rs1:5 1 rs2_or_imm:s13 \ &r_r_ri_asi imm=1 asi=-2 @@ -289,6 +297,17 @@ STD 11 ..... 010111 ..... . ............. @r_r_i_asi # STDA STX 11 ..... 011110 ..... . ............. @r_r_r_asi # STXA STX 11 ..... 011110 ..... . ............. @r_r_i_asi # STXA +LDF 11 ..... 100000 ..... . ............. @r_r_ri_na +LDQF 11 ..... 100010 ..... . ............. @q_r_ri_na +LDDF 11 ..... 100011 ..... . ............. @d_r_ri_na + +STF 11 ..... 100100 ..... . ............. @r_r_ri_na +{ + STQF 11 ..... 100110 ..... . ............. @q_r_ri_na + STDFQ 11 ----- 100110 ----- - ------------- +} +STDF 11 ..... 100111 ..... . ............. @d_r_ri_na + LDSTUB 11 ..... 001101 ..... . ............. @r_r_ri_na LDSTUB 11 ..... 011101 ..... . ............. @r_r_r_asi # LDSTUBA LDSTUB 11 ..... 011101 ..... . ............. @r_r_i_asi # LDSTUBA diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 55d6f83736..dab3dfd09c 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -254,29 +254,7 @@ static void gen_op_store_QT0_fpr(unsigned int dst) offsetof(CPU_QuadU, ll.lower)); } -static void gen_store_fpr_Q(DisasContext *dc, unsigned int dst, - TCGv_i64 v1, TCGv_i64 v2) -{ - dst = QFPREG(dst); - - tcg_gen_mov_i64(cpu_fpr[dst / 2], v1); - tcg_gen_mov_i64(cpu_fpr[dst / 2 + 1], v2); - gen_update_fprs_dirty(dc, dst); -} - #ifdef TARGET_SPARC64 -static TCGv_i64 gen_load_fpr_Q0(DisasContext *dc, unsigned int src) -{ - src = QFPREG(src); - return cpu_fpr[src / 2]; -} - -static TCGv_i64 gen_load_fpr_Q1(DisasContext *dc, unsigned int src) -{ - src = QFPREG(src); - return cpu_fpr[src / 2 + 1]; -} - static void gen_move_Q(DisasContext *dc, unsigned int rd, unsigned int rs) { rd = QFPREG(rd); @@ -2900,6 +2878,16 @@ static void gen_faligndata(TCGv dst, TCGv gsr, TCGv s1, TCGv s2) } #endif +static int extract_dfpreg(DisasContext *dc, int x) +{ + return DFPREG(x); +} + +static int extract_qfpreg(DisasContext *dc, int x) +{ + return QFPREG(x); +} + /* Include the auto-generated decoder. */ #include "decode-insns.c.inc" @@ -3035,6 +3023,20 @@ static bool raise_priv(DisasContext *dc) return true; } +static bool raise_unimpfpop(DisasContext *dc) +{ + gen_op_fpexception_im(dc, FSR_FTT_UNIMPFPOP); + return true; +} + +static bool gen_trap_float128(DisasContext *dc) +{ + if (dc->def->features & CPU_FEATURE_FLOAT128) { + return false; + } + return raise_unimpfpop(dc); +} + static bool do_bpcc(DisasContext *dc, arg_bcc *a) { target_long target = address_mask_i(dc, dc->pc + a->i * 4); @@ -4633,6 +4635,68 @@ static bool do_casa(DisasContext *dc, arg_r_r_ri_asi *a, MemOp mop) TRANS(CASA, CASA, do_casa, a, MO_TEUL) TRANS(CASXA, 64, do_casa, a, MO_TEUQ) +static bool do_ld_fpr(DisasContext *dc, arg_r_r_ri_asi *a, MemOp sz) +{ + TCGv addr = gen_ldst_addr(dc, a->rs1, a->imm, a->rs2_or_imm); + DisasASI da; + + if (addr == NULL) { + return false; + } + if (gen_trap_ifnofpu(dc)) { + return true; + } + if (sz == MO_128 && gen_trap_float128(dc)) { + return true; + } + da = resolve_asi(dc, a->asi, MO_TE | sz); + gen_ldf_asi0(dc, &da, sz, addr, a->rd); + gen_update_fprs_dirty(dc, a->rd); + return advance_pc(dc); +} + +TRANS(LDF, ALL, do_ld_fpr, a, MO_32) +TRANS(LDDF, ALL, do_ld_fpr, a, MO_64) +TRANS(LDQF, ALL, do_ld_fpr, a, MO_128) + +static bool do_st_fpr(DisasContext *dc, arg_r_r_ri_asi *a, MemOp sz) +{ + TCGv addr = gen_ldst_addr(dc, a->rs1, a->imm, a->rs2_or_imm); + DisasASI da; + + if (addr == NULL) { + return false; + } + if (gen_trap_ifnofpu(dc)) { + return true; + } + if (sz == MO_128 && gen_trap_float128(dc)) { + return true; + } + da = resolve_asi(dc, a->asi, MO_TE | sz); + gen_stf_asi0(dc, &da, sz, addr, a->rd); + return advance_pc(dc); +} + +TRANS(STF, ALL, do_st_fpr, a, MO_32) +TRANS(STDF, ALL, do_st_fpr, a, MO_64) +TRANS(STQF, ALL, do_st_fpr, a, MO_128) + +static bool trans_STDFQ(DisasContext *dc, arg_STDFQ *a) +{ + if (!avail_32(dc)) { + return false; + } + if (!supervisor(dc)) { + return raise_priv(dc); + } + if (gen_trap_ifnofpu(dc)) { + return true; + } + gen_op_fpexception_im(dc, FSR_FTT_SEQ_ERROR); + return true; +} + #define CHECK_IU_FEATURE(dc, FEATURE) \ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ goto illegal_insn; @@ -4647,7 +4711,8 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) TCGv cpu_src1 __attribute__((unused)); TCGv cpu_src2 __attribute__((unused)); TCGv_i32 cpu_src1_32, cpu_src2_32, cpu_dst_32; - TCGv_i64 cpu_src1_64, cpu_src2_64, cpu_dst_64; + TCGv_i64 cpu_src1_64, cpu_src2_64; + TCGv_i64 cpu_dst_64 __attribute__((unused)); target_long simm; opc = GET_FIELD(insn, 0, 1); @@ -5514,12 +5579,9 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) } switch (xop) { case 0x20: /* ldf, load fpreg */ - gen_address_mask(dc, cpu_addr); - cpu_dst_32 = gen_dest_fpr_F(dc); - tcg_gen_qemu_ld_i32(cpu_dst_32, cpu_addr, - dc->mem_idx, MO_TEUL | MO_ALIGN); - gen_store_fpr_F(dc, rd, cpu_dst_32); - break; + case 0x22: /* ldqf, load quad fpreg */ + case 0x23: /* lddf, load double fpreg */ + g_assert_not_reached(); /* in decodetree */ case 0x21: /* ldfsr, V9 ldxfsr */ #ifdef TARGET_SPARC64 gen_address_mask(dc, cpu_addr); @@ -5536,25 +5598,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) dc->mem_idx, MO_TEUL | MO_ALIGN); gen_helper_ldfsr(cpu_fsr, tcg_env, cpu_fsr, cpu_dst_32); break; - case 0x22: /* ldqf, load quad fpreg */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_address_mask(dc, cpu_addr); - cpu_src1_64 = tcg_temp_new_i64(); - tcg_gen_qemu_ld_i64(cpu_src1_64, cpu_addr, dc->mem_idx, - MO_TEUQ | MO_ALIGN_4); - tcg_gen_addi_tl(cpu_addr, cpu_addr, 8); - cpu_src2_64 = tcg_temp_new_i64(); - tcg_gen_qemu_ld_i64(cpu_src2_64, cpu_addr, dc->mem_idx, - MO_TEUQ | MO_ALIGN_4); - gen_store_fpr_Q(dc, rd, cpu_src1_64, cpu_src2_64); - break; - case 0x23: /* lddf, load double fpreg */ - gen_address_mask(dc, cpu_addr); - cpu_dst_64 = gen_dest_fpr_D(dc, rd); - tcg_gen_qemu_ld_i64(cpu_dst_64, cpu_addr, dc->mem_idx, - MO_TEUQ | MO_ALIGN_4); - gen_store_fpr_D(dc, rd, cpu_dst_64); - break; default: goto illegal_insn; } @@ -5564,11 +5607,9 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) } switch (xop) { case 0x24: /* stf, store fpreg */ - gen_address_mask(dc, cpu_addr); - cpu_src1_32 = gen_load_fpr_F(dc, rd); - tcg_gen_qemu_st_i32(cpu_src1_32, cpu_addr, - dc->mem_idx, MO_TEUL | MO_ALIGN); - break; + case 0x26: /* v9 stqf, v8 stdfq */ + case 0x27: /* stdf, store double fpreg */ + g_assert_not_reached(); case 0x25: /* stfsr, V9 stxfsr */ { #ifdef TARGET_SPARC64 @@ -5583,43 +5624,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) dc->mem_idx, MO_TEUL | MO_ALIGN); } break; - case 0x26: -#ifdef TARGET_SPARC64 - /* V9 stqf, store quad fpreg */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_address_mask(dc, cpu_addr); - /* ??? While stqf only requires 4-byte alignment, it is - legal for the cpu to signal the unaligned exception. - The OS trap handler is then required to fix it up. - For qemu, this avoids having to probe the second page - before performing the first write. */ - cpu_src1_64 = gen_load_fpr_Q0(dc, rd); - tcg_gen_qemu_st_i64(cpu_src1_64, cpu_addr, - dc->mem_idx, MO_TEUQ | MO_ALIGN_16); - tcg_gen_addi_tl(cpu_addr, cpu_addr, 8); - cpu_src2_64 = gen_load_fpr_Q1(dc, rd); - tcg_gen_qemu_st_i64(cpu_src1_64, cpu_addr, - dc->mem_idx, MO_TEUQ); - break; -#else /* !TARGET_SPARC64 */ - /* stdfq, store floating point queue */ -#if defined(CONFIG_USER_ONLY) - goto illegal_insn; -#else - if (!supervisor(dc)) - goto priv_insn; - if (gen_trap_ifnofpu(dc)) { - goto jmp_insn; - } - goto nfq_insn; -#endif -#endif - case 0x27: /* stdf, store double fpreg */ - gen_address_mask(dc, cpu_addr); - cpu_src1_64 = gen_load_fpr_D(dc, rd); - tcg_gen_qemu_st_i64(cpu_src1_64, cpu_addr, dc->mem_idx, - MO_TEUQ | MO_ALIGN_4); - break; default: goto illegal_insn; } @@ -5666,19 +5670,9 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) illegal_insn: gen_exception(dc, TT_ILL_INSN); return; -#if !defined(CONFIG_USER_ONLY) && !defined(TARGET_SPARC64) - priv_insn: - gen_exception(dc, TT_PRIV_INSN); - return; -#endif nfpu_insn: gen_op_fpexception_im(dc, FSR_FTT_UNIMPFPOP); return; -#if !defined(CONFIG_USER_ONLY) && !defined(TARGET_SPARC64) - nfq_insn: - gen_op_fpexception_im(dc, FSR_FTT_SEQ_ERROR); - return; -#endif } static void sparc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) From 287b11520bd4dafd58e42ccff7010f8c4bbafcf9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 5 Oct 2023 01:56:02 -0700 Subject: [PATCH 074/974] target/sparc: Move asi fp load/store to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 51 +++++++++-- target/sparc/translate.c | 173 ++++++++------------------------------ 2 files changed, 81 insertions(+), 143 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 1150890e44..45eb6a967f 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -251,6 +251,14 @@ NCP 10 ----- 110111 ----- --------- ----- # v8 CPop2 @r_r_r_asi .. rd:5 ...... rs1:5 0 asi:8 rs2_or_imm:5 &r_r_ri_asi imm=0 @r_r_i_asi .. rd:5 ...... rs1:5 1 rs2_or_imm:s13 \ &r_r_ri_asi imm=1 asi=-2 +@d_r_r_asi .. ..... ...... rs1:5 0 asi:8 rs2_or_imm:5 \ + &r_r_ri_asi rd=%dfp_rd imm=0 +@d_r_i_asi .. ..... ...... rs1:5 1 rs2_or_imm:s13 \ + &r_r_ri_asi rd=%dfp_rd imm=1 asi=-2 +@q_r_r_asi .. ..... ...... rs1:5 0 asi:8 rs2_or_imm:5 \ + &r_r_ri_asi rd=%qfp_rd imm=0 +@q_r_i_asi .. ..... ...... rs1:5 1 rs2_or_imm:s13 \ + &r_r_ri_asi rd=%qfp_rd imm=1 asi=-2 @casa_imm .. rd:5 ...... rs1:5 1 00000000 rs2_or_imm:5 \ &r_r_ri_asi imm=1 asi=-2 @@ -325,10 +333,43 @@ NOP_v9 11 ----- 101101 ----- 0 00000000 ----- # PREFETCH NOP_v9 11 ----- 101101 ----- 1 ------------- # PREFETCH NOP_v9 11 ----- 111101 ----- - ------------- # PREFETCHA -NCP 11 ----- 110000 ----- --------- ----- # v8 LDC +{ + [ + LDFA 11 ..... 110000 ..... . ............. @r_r_r_asi + LDFA 11 ..... 110000 ..... . ............. @r_r_i_asi + ] + NCP 11 ----- 110000 ----- --------- ----- # v8 LDC +} NCP 11 ----- 110001 ----- --------- ----- # v8 LDCSR -NCP 11 ----- 110011 ----- --------- ----- # v8 LDDC -NCP 11 ----- 110100 ----- --------- ----- # v8 STC +LDQFA 11 ..... 110010 ..... . ............. @q_r_r_asi +LDQFA 11 ..... 110010 ..... . ............. @q_r_i_asi +{ + [ + LDDFA 11 ..... 110011 ..... . ............. @d_r_r_asi + LDDFA 11 ..... 110011 ..... . ............. @d_r_i_asi + ] + NCP 11 ----- 110011 ----- --------- ----- # v8 LDDC +} + +{ + [ + STFA 11 ..... 110100 ..... . ............. @r_r_r_asi + STFA 11 ..... 110100 ..... . ............. @r_r_i_asi + ] + NCP 11 ----- 110100 ----- --------- ----- # v8 STC +} NCP 11 ----- 110101 ----- --------- ----- # v8 STCSR -NCP 11 ----- 110110 ----- --------- ----- # v8 STDCQ -NCP 11 ----- 110111 ----- --------- ----- # v8 STDC +{ + [ + STQFA 11 ..... 110110 ..... . ............. @q_r_r_asi + STQFA 11 ..... 110110 ..... . ............. @q_r_i_asi + ] + NCP 11 ----- 110110 ----- --------- ----- # v8 STDCQ +} +{ + [ + STDFA 11 ..... 110111 ..... . ............. @d_r_r_asi + STDFA 11 ..... 110111 ..... . ............. @d_r_i_asi + ] + NCP 11 ----- 110111 ----- --------- ----- # v8 STDC +} diff --git a/target/sparc/translate.c b/target/sparc/translate.c index dab3dfd09c..ebffcd7393 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -2106,12 +2106,6 @@ static DisasASI resolve_asi(DisasContext *dc, int asi, MemOp memop) return (DisasASI){ type, asi, mem_idx, memop }; } -static DisasASI get_asi(DisasContext *dc, int insn, MemOp memop) -{ - int asi = IS_IMM ? -2 : GET_FIELD(insn, 19, 26); - return resolve_asi(dc, asi, memop); -} - #if defined(CONFIG_USER_ONLY) && !defined(TARGET_SPARC64) static void gen_helper_ld_asi(TCGv_i64 r, TCGv_env e, TCGv a, TCGv_i32 asi, TCGv_i32 mop) @@ -2298,13 +2292,14 @@ static void gen_ldstub_asi(DisasContext *dc, DisasASI *da, TCGv dst, TCGv addr) } } -static void gen_ldf_asi0(DisasContext *dc, DisasASI *da, MemOp orig_size, - TCGv addr, int rd) +static void gen_ldf_asi(DisasContext *dc, DisasASI *da, MemOp orig_size, + TCGv addr, int rd) { MemOp memop = da->memop; MemOp size = memop & MO_SIZE; TCGv_i32 d32; TCGv_i64 d64; + TCGv addr_tmp; /* TODO: Use 128-bit load/store below. */ if (size == MO_128) { @@ -2331,8 +2326,9 @@ static void gen_ldf_asi0(DisasContext *dc, DisasASI *da, MemOp orig_size, case MO_128: d64 = tcg_temp_new_i64(); tcg_gen_qemu_ld_i64(d64, addr, da->mem_idx, memop); - tcg_gen_addi_tl(addr, addr, 8); - tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2 + 1], addr, da->mem_idx, memop); + addr_tmp = tcg_temp_new(); + tcg_gen_addi_tl(addr_tmp, addr, 8); + tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2 + 1], addr_tmp, da->mem_idx, memop); tcg_gen_mov_i64(cpu_fpr[rd / 2], d64); break; default: @@ -2343,18 +2339,16 @@ static void gen_ldf_asi0(DisasContext *dc, DisasASI *da, MemOp orig_size, case GET_ASI_BLOCK: /* Valid for lddfa on aligned registers only. */ if (orig_size == MO_64 && (rd & 7) == 0) { - TCGv eight; - int i; - /* The first operation checks required alignment. */ - eight = tcg_constant_tl(8); - for (i = 0; ; ++i) { + addr_tmp = tcg_temp_new(); + for (int i = 0; ; ++i) { tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2 + i], addr, da->mem_idx, memop | (i == 0 ? MO_ALIGN_64 : 0)); if (i == 7) { break; } - tcg_gen_add_tl(addr, addr, eight); + tcg_gen_addi_tl(addr_tmp, addr, 8); + addr = addr_tmp; } } else { gen_exception(dc, TT_ILL_INSN); @@ -2396,8 +2390,9 @@ static void gen_ldf_asi0(DisasContext *dc, DisasASI *da, MemOp orig_size, case MO_128: d64 = tcg_temp_new_i64(); gen_helper_ld_asi(d64, tcg_env, addr, r_asi, r_mop); - tcg_gen_addi_tl(addr, addr, 8); - gen_helper_ld_asi(cpu_fpr[rd / 2 + 1], tcg_env, addr, + addr_tmp = tcg_temp_new(); + tcg_gen_addi_tl(addr_tmp, addr, 8); + gen_helper_ld_asi(cpu_fpr[rd / 2 + 1], tcg_env, addr_tmp, r_asi, r_mop); tcg_gen_mov_i64(cpu_fpr[rd / 2], d64); break; @@ -2409,22 +2404,13 @@ static void gen_ldf_asi0(DisasContext *dc, DisasASI *da, MemOp orig_size, } } -static void __attribute__((unused)) -gen_ldf_asi(DisasContext *dc, TCGv addr, int insn, int size, int rd) -{ - MemOp sz = ctz32(size); - DisasASI da = get_asi(dc, insn, MO_TE | sz); - - gen_address_mask(dc, addr); - gen_ldf_asi0(dc, &da, sz, addr, rd); -} - -static void gen_stf_asi0(DisasContext *dc, DisasASI *da, MemOp orig_size, - TCGv addr, int rd) +static void gen_stf_asi(DisasContext *dc, DisasASI *da, MemOp orig_size, + TCGv addr, int rd) { MemOp memop = da->memop; MemOp size = memop & MO_SIZE; TCGv_i32 d32; + TCGv addr_tmp; /* TODO: Use 128-bit load/store below. */ if (size == MO_128) { @@ -2454,8 +2440,9 @@ static void gen_stf_asi0(DisasContext *dc, DisasASI *da, MemOp orig_size, write. */ tcg_gen_qemu_st_i64(cpu_fpr[rd / 2], addr, da->mem_idx, memop | MO_ALIGN_16); - tcg_gen_addi_tl(addr, addr, 8); - tcg_gen_qemu_st_i64(cpu_fpr[rd / 2 + 1], addr, da->mem_idx, memop); + addr_tmp = tcg_temp_new(); + tcg_gen_addi_tl(addr_tmp, addr, 8); + tcg_gen_qemu_st_i64(cpu_fpr[rd / 2 + 1], addr_tmp, da->mem_idx, memop); break; default: g_assert_not_reached(); @@ -2465,18 +2452,16 @@ static void gen_stf_asi0(DisasContext *dc, DisasASI *da, MemOp orig_size, case GET_ASI_BLOCK: /* Valid for stdfa on aligned registers only. */ if (orig_size == MO_64 && (rd & 7) == 0) { - TCGv eight; - int i; - /* The first operation checks required alignment. */ - eight = tcg_constant_tl(8); - for (i = 0; ; ++i) { + addr_tmp = tcg_temp_new(); + for (int i = 0; ; ++i) { tcg_gen_qemu_st_i64(cpu_fpr[rd / 2 + i], addr, da->mem_idx, memop | (i == 0 ? MO_ALIGN_64 : 0)); if (i == 7) { break; } - tcg_gen_add_tl(addr, addr, eight); + tcg_gen_addi_tl(addr_tmp, addr, 8); + addr = addr_tmp; } } else { gen_exception(dc, TT_ILL_INSN); @@ -2502,16 +2487,6 @@ static void gen_stf_asi0(DisasContext *dc, DisasASI *da, MemOp orig_size, } } -static void __attribute__((unused)) -gen_stf_asi(DisasContext *dc, TCGv addr, int insn, int size, int rd) -{ - MemOp sz = ctz32(size); - DisasASI da = get_asi(dc, insn, MO_TE | sz); - - gen_address_mask(dc, addr); - gen_stf_asi0(dc, &da, sz, addr, rd); -} - static void gen_ldda_asi(DisasContext *dc, DisasASI *da, TCGv addr, int rd) { TCGv hi = gen_dest_gpr(dc, rd); @@ -4650,7 +4625,7 @@ static bool do_ld_fpr(DisasContext *dc, arg_r_r_ri_asi *a, MemOp sz) return true; } da = resolve_asi(dc, a->asi, MO_TE | sz); - gen_ldf_asi0(dc, &da, sz, addr, a->rd); + gen_ldf_asi(dc, &da, sz, addr, a->rd); gen_update_fprs_dirty(dc, a->rd); return advance_pc(dc); } @@ -4659,6 +4634,10 @@ TRANS(LDF, ALL, do_ld_fpr, a, MO_32) TRANS(LDDF, ALL, do_ld_fpr, a, MO_64) TRANS(LDQF, ALL, do_ld_fpr, a, MO_128) +TRANS(LDFA, 64, do_ld_fpr, a, MO_32) +TRANS(LDDFA, 64, do_ld_fpr, a, MO_64) +TRANS(LDQFA, 64, do_ld_fpr, a, MO_128) + static bool do_st_fpr(DisasContext *dc, arg_r_r_ri_asi *a, MemOp sz) { TCGv addr = gen_ldst_addr(dc, a->rs1, a->imm, a->rs2_or_imm); @@ -4674,7 +4653,7 @@ static bool do_st_fpr(DisasContext *dc, arg_r_r_ri_asi *a, MemOp sz) return true; } da = resolve_asi(dc, a->asi, MO_TE | sz); - gen_stf_asi0(dc, &da, sz, addr, a->rd); + gen_stf_asi(dc, &da, sz, addr, a->rd); return advance_pc(dc); } @@ -4682,6 +4661,10 @@ TRANS(STF, ALL, do_st_fpr, a, MO_32) TRANS(STDF, ALL, do_st_fpr, a, MO_64) TRANS(STQF, ALL, do_st_fpr, a, MO_128) +TRANS(STFA, 64, do_st_fpr, a, MO_32) +TRANS(STDFA, 64, do_st_fpr, a, MO_64) +TRANS(STQFA, 64, do_st_fpr, a, MO_128) + static bool trans_STDFQ(DisasContext *dc, arg_STDFQ *a) { if (!avail_32(dc)) { @@ -5515,64 +5498,7 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) if (xop < 4 || (xop > 7 && xop < 0x14 && xop != 0x0e) || (xop > 0x17 && xop <= 0x1d ) || (xop > 0x2c && xop <= 0x33) || xop == 0x1f || xop == 0x3d) { - TCGv cpu_val = gen_dest_gpr(dc, rd); - - switch (xop) { - case 0x0: /* ld, V9 lduw, load unsigned word */ - case 0x1: /* ldub, load unsigned byte */ - case 0x2: /* lduh, load unsigned halfword */ - case 0x3: /* ldd, load double word */ - case 0x9: /* ldsb, load signed byte */ - case 0xa: /* ldsh, load signed halfword */ - case 0xd: /* ldstub */ - case 0x0f: /* swap */ - case 0x10: /* lda, V9 lduwa, load word alternate */ - case 0x11: /* lduba, load unsigned byte alternate */ - case 0x12: /* lduha, load unsigned halfword alternate */ - case 0x13: /* ldda, load double word alternate */ - case 0x19: /* ldsba, load signed byte alternate */ - case 0x1a: /* ldsha, load signed halfword alternate */ - case 0x1d: /* ldstuba */ - case 0x1f: /* swapa */ - g_assert_not_reached(); /* in decodetree */ - case 0x08: /* V9 ldsw */ - case 0x0b: /* V9 ldx */ - case 0x18: /* V9 ldswa */ - case 0x1b: /* V9 ldxa */ - case 0x2d: /* V9 prefetch */ - case 0x3d: /* V9 prefetcha */ - goto illegal_insn; /* in decodetree */ -#ifdef TARGET_SPARC64 - case 0x30: /* V9 ldfa */ - if (gen_trap_ifnofpu(dc)) { - goto jmp_insn; - } - gen_ldf_asi(dc, cpu_addr, insn, 4, rd); - gen_update_fprs_dirty(dc, rd); - goto skip_move; - case 0x33: /* V9 lddfa */ - if (gen_trap_ifnofpu(dc)) { - goto jmp_insn; - } - gen_ldf_asi(dc, cpu_addr, insn, 8, DFPREG(rd)); - gen_update_fprs_dirty(dc, DFPREG(rd)); - goto skip_move; - case 0x32: /* V9 ldqfa */ - CHECK_FPU_FEATURE(dc, FLOAT128); - if (gen_trap_ifnofpu(dc)) { - goto jmp_insn; - } - gen_ldf_asi(dc, cpu_addr, insn, 16, QFPREG(rd)); - gen_update_fprs_dirty(dc, QFPREG(rd)); - goto skip_move; -#endif - default: - goto illegal_insn; - } - gen_store_gpr(dc, rd, cpu_val); -#if defined(TARGET_SPARC64) - skip_move: ; -#endif + goto illegal_insn; /* in decodetree */ } else if (xop >= 0x20 && xop < 0x24) { if (gen_trap_ifnofpu(dc)) { goto jmp_insn; @@ -5628,36 +5554,7 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) goto illegal_insn; } } else if (xop > 0x33 && xop < 0x3f) { - switch (xop) { -#ifdef TARGET_SPARC64 - case 0x34: /* V9 stfa */ - if (gen_trap_ifnofpu(dc)) { - goto jmp_insn; - } - gen_stf_asi(dc, cpu_addr, insn, 4, rd); - break; - case 0x36: /* V9 stqfa */ - { - CHECK_FPU_FEATURE(dc, FLOAT128); - if (gen_trap_ifnofpu(dc)) { - goto jmp_insn; - } - gen_stf_asi(dc, cpu_addr, insn, 16, QFPREG(rd)); - } - break; - case 0x37: /* V9 stdfa */ - if (gen_trap_ifnofpu(dc)) { - goto jmp_insn; - } - gen_stf_asi(dc, cpu_addr, insn, 8, DFPREG(rd)); - break; -#endif - case 0x3e: /* V9 casxa */ - case 0x3c: /* V9 or LEON3 casa */ - goto illegal_insn; /* in decodetree */ - default: - goto illegal_insn; - } + goto illegal_insn; /* in decodetree */ } else { goto illegal_insn; } From 3d3c06737b27c07181740e9f696dcbe25b9e8e19 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 5 Oct 2023 02:12:59 -0700 Subject: [PATCH 075/974] target/sparc: Move LDFSR, STFSR to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 4 + target/sparc/translate.c | 152 +++++++++++++++----------------------- 2 files changed, 64 insertions(+), 92 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 45eb6a967f..5df3b1add4 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -306,10 +306,14 @@ STX 11 ..... 011110 ..... . ............. @r_r_r_asi # STXA STX 11 ..... 011110 ..... . ............. @r_r_i_asi # STXA LDF 11 ..... 100000 ..... . ............. @r_r_ri_na +LDFSR 11 00000 100001 ..... . ............. @n_r_ri +LDXFSR 11 00001 100001 ..... . ............. @n_r_ri LDQF 11 ..... 100010 ..... . ............. @q_r_ri_na LDDF 11 ..... 100011 ..... . ............. @d_r_ri_na STF 11 ..... 100100 ..... . ............. @r_r_ri_na +STFSR 11 00000 100101 ..... . ............. @n_r_ri +STXFSR 11 00001 100101 ..... . ............. @n_r_ri { STQF 11 ..... 100110 ..... . ............. @q_r_ri_na STDFQ 11 ----- 100110 ----- - ------------- diff --git a/target/sparc/translate.c b/target/sparc/translate.c index ebffcd7393..4c0e7cde4a 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -45,6 +45,7 @@ # define gen_helper_clear_softint(E, S) qemu_build_not_reached() # define gen_helper_done(E) qemu_build_not_reached() # define gen_helper_flushw(E) qemu_build_not_reached() +# define gen_helper_ldxfsr(D, E, A, B) qemu_build_not_reached() # define gen_helper_rdccr(D, E) qemu_build_not_reached() # define gen_helper_rdcwp(D, E) qemu_build_not_reached() # define gen_helper_restored(E) qemu_build_not_reached() @@ -164,12 +165,6 @@ typedef struct { #define UA2005_HTRAP_MASK 0xff #define V8_TRAP_MASK 0x7f -static int sign_extend(int x, int len) -{ - len = 32 - len; - return (x << len) >> len; -} - #define IS_IMM (insn & (1<<13)) static void gen_update_fprs_dirty(DisasContext *dc, int rd) @@ -2655,13 +2650,13 @@ static void gen_stda_asi(DisasContext *dc, DisasASI *da, TCGv addr, int rd) } } +#ifdef TARGET_SPARC64 static TCGv get_src1(DisasContext *dc, unsigned int insn) { unsigned int rs1 = GET_FIELD(insn, 13, 17); return gen_load_gpr(dc, rs1); } -#ifdef TARGET_SPARC64 static void gen_fmovs(DisasContext *dc, DisasCompare *cmp, int rd, int rs) { TCGv_i32 c32, zero, dst, s1, s2; @@ -4680,6 +4675,61 @@ static bool trans_STDFQ(DisasContext *dc, arg_STDFQ *a) return true; } +static bool trans_LDFSR(DisasContext *dc, arg_r_r_ri *a) +{ + TCGv addr; + TCGv_i32 tmp; + + addr = gen_ldst_addr(dc, a->rs1, a->imm, a->rs2_or_imm); + if (addr == NULL) { + return false; + } + if (gen_trap_ifnofpu(dc)) { + return true; + } + tmp = tcg_temp_new_i32(); + tcg_gen_qemu_ld_i32(tmp, addr, dc->mem_idx, MO_TEUL | MO_ALIGN); + gen_helper_ldfsr(cpu_fsr, tcg_env, cpu_fsr, tmp); + return advance_pc(dc); +} + +static bool trans_LDXFSR(DisasContext *dc, arg_r_r_ri *a) +{ + TCGv addr; + TCGv_i64 tmp; + + if (!avail_64(dc)) { + return false; + } + addr = gen_ldst_addr(dc, a->rs1, a->imm, a->rs2_or_imm); + if (addr == NULL) { + return false; + } + if (gen_trap_ifnofpu(dc)) { + return true; + } + tmp = tcg_temp_new_i64(); + tcg_gen_qemu_ld_i64(tmp, addr, dc->mem_idx, MO_TEUQ | MO_ALIGN); + gen_helper_ldxfsr(cpu_fsr, tcg_env, cpu_fsr, tmp); + return advance_pc(dc); +} + +static bool do_stfsr(DisasContext *dc, arg_r_r_ri *a, MemOp mop) +{ + TCGv addr = gen_ldst_addr(dc, a->rs1, a->imm, a->rs2_or_imm); + if (addr == NULL) { + return false; + } + if (gen_trap_ifnofpu(dc)) { + return true; + } + tcg_gen_qemu_st_tl(cpu_fsr, addr, dc->mem_idx, mop | MO_ALIGN); + return advance_pc(dc); +} + +TRANS(STFSR, ALL, do_stfsr, a, MO_TEUL) +TRANS(STXFSR, 64, do_stfsr, a, MO_TEUQ) + #define CHECK_IU_FEATURE(dc, FEATURE) \ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ goto illegal_insn; @@ -4693,10 +4743,10 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) unsigned int opc, rs1, rs2, rd; TCGv cpu_src1 __attribute__((unused)); TCGv cpu_src2 __attribute__((unused)); - TCGv_i32 cpu_src1_32, cpu_src2_32, cpu_dst_32; + TCGv_i32 cpu_src1_32, cpu_src2_32; TCGv_i64 cpu_src1_64, cpu_src2_64; + TCGv_i32 cpu_dst_32 __attribute__((unused)); TCGv_i64 cpu_dst_64 __attribute__((unused)); - target_long simm; opc = GET_FIELD(insn, 0, 1); rd = GET_FIELD(insn, 2, 6); @@ -5477,89 +5527,7 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) } break; case 3: /* load/store instructions */ - { - unsigned int xop = GET_FIELD(insn, 7, 12); - /* ??? gen_address_mask prevents us from using a source - register directly. Always generate a temporary. */ - TCGv cpu_addr = tcg_temp_new(); - - tcg_gen_mov_tl(cpu_addr, get_src1(dc, insn)); - if (IS_IMM) { /* immediate */ - simm = GET_FIELDs(insn, 19, 31); - if (simm != 0) { - tcg_gen_addi_tl(cpu_addr, cpu_addr, simm); - } - } else { /* register */ - rs2 = GET_FIELD(insn, 27, 31); - if (rs2 != 0) { - tcg_gen_add_tl(cpu_addr, cpu_addr, gen_load_gpr(dc, rs2)); - } - } - if (xop < 4 || (xop > 7 && xop < 0x14 && xop != 0x0e) || - (xop > 0x17 && xop <= 0x1d ) || - (xop > 0x2c && xop <= 0x33) || xop == 0x1f || xop == 0x3d) { - goto illegal_insn; /* in decodetree */ - } else if (xop >= 0x20 && xop < 0x24) { - if (gen_trap_ifnofpu(dc)) { - goto jmp_insn; - } - switch (xop) { - case 0x20: /* ldf, load fpreg */ - case 0x22: /* ldqf, load quad fpreg */ - case 0x23: /* lddf, load double fpreg */ - g_assert_not_reached(); /* in decodetree */ - case 0x21: /* ldfsr, V9 ldxfsr */ -#ifdef TARGET_SPARC64 - gen_address_mask(dc, cpu_addr); - if (rd == 1) { - TCGv_i64 t64 = tcg_temp_new_i64(); - tcg_gen_qemu_ld_i64(t64, cpu_addr, - dc->mem_idx, MO_TEUQ | MO_ALIGN); - gen_helper_ldxfsr(cpu_fsr, tcg_env, cpu_fsr, t64); - break; - } -#endif - cpu_dst_32 = tcg_temp_new_i32(); - tcg_gen_qemu_ld_i32(cpu_dst_32, cpu_addr, - dc->mem_idx, MO_TEUL | MO_ALIGN); - gen_helper_ldfsr(cpu_fsr, tcg_env, cpu_fsr, cpu_dst_32); - break; - default: - goto illegal_insn; - } - } else if (xop > 0x23 && xop < 0x28) { - if (gen_trap_ifnofpu(dc)) { - goto jmp_insn; - } - switch (xop) { - case 0x24: /* stf, store fpreg */ - case 0x26: /* v9 stqf, v8 stdfq */ - case 0x27: /* stdf, store double fpreg */ - g_assert_not_reached(); - case 0x25: /* stfsr, V9 stxfsr */ - { -#ifdef TARGET_SPARC64 - gen_address_mask(dc, cpu_addr); - if (rd == 1) { - tcg_gen_qemu_st_tl(cpu_fsr, cpu_addr, - dc->mem_idx, MO_TEUQ | MO_ALIGN); - break; - } -#endif - tcg_gen_qemu_st_tl(cpu_fsr, cpu_addr, - dc->mem_idx, MO_TEUL | MO_ALIGN); - } - break; - default: - goto illegal_insn; - } - } else if (xop > 0x33 && xop < 0x3f) { - goto illegal_insn; /* in decodetree */ - } else { - goto illegal_insn; - } - } - break; + goto illegal_insn; /* in decodetree */ } advance_pc(dc); jmp_insn: From da6814060456c27ae6966d711a8a85782a21a092 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 5 Oct 2023 02:31:25 -0700 Subject: [PATCH 076/974] target/sparc: Merge LDFSR, LDXFSR implementations Combine the helper to a single set_fsr(). Perform the mask and merge inline. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/fop_helper.c | 17 ++-------------- target/sparc/helper.h | 3 +-- target/sparc/translate.c | 42 ++++++++++++--------------------------- 3 files changed, 16 insertions(+), 46 deletions(-) diff --git a/target/sparc/fop_helper.c b/target/sparc/fop_helper.c index f54fa9b959..0f8aa3abcd 100644 --- a/target/sparc/fop_helper.c +++ b/target/sparc/fop_helper.c @@ -382,20 +382,7 @@ static void set_fsr(CPUSPARCState *env, target_ulong fsr) set_float_rounding_mode(rnd_mode, &env->fp_status); } -target_ulong helper_ldfsr(CPUSPARCState *env, target_ulong old_fsr, - uint32_t new_fsr) +void helper_set_fsr(CPUSPARCState *env, target_ulong fsr) { - old_fsr = (new_fsr & FSR_LDFSR_MASK) | (old_fsr & FSR_LDFSR_OLDMASK); - set_fsr(env, old_fsr); - return old_fsr; + set_fsr(env, fsr); } - -#ifdef TARGET_SPARC64 -target_ulong helper_ldxfsr(CPUSPARCState *env, target_ulong old_fsr, - uint64_t new_fsr) -{ - old_fsr = (new_fsr & FSR_LDXFSR_MASK) | (old_fsr & FSR_LDXFSR_OLDMASK); - set_fsr(env, old_fsr); - return old_fsr; -} -#endif diff --git a/target/sparc/helper.h b/target/sparc/helper.h index b116ddcb29..790752467f 100644 --- a/target/sparc/helper.h +++ b/target/sparc/helper.h @@ -42,7 +42,7 @@ DEF_HELPER_FLAGS_4(ld_asi, TCG_CALL_NO_WG, i64, env, tl, int, i32) DEF_HELPER_FLAGS_5(st_asi, TCG_CALL_NO_WG, void, env, tl, i64, int, i32) #endif DEF_HELPER_FLAGS_1(check_ieee_exceptions, TCG_CALL_NO_WG, tl, env) -DEF_HELPER_FLAGS_3(ldfsr, TCG_CALL_NO_RWG, tl, env, tl, i32) +DEF_HELPER_FLAGS_2(set_fsr, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_1(fabss, TCG_CALL_NO_RWG_SE, f32, f32) DEF_HELPER_FLAGS_2(fsqrts, TCG_CALL_NO_RWG, f32, env, f32) DEF_HELPER_FLAGS_2(fsqrtd, TCG_CALL_NO_RWG, f64, env, f64) @@ -54,7 +54,6 @@ DEF_HELPER_FLAGS_1(fsqrtq, TCG_CALL_NO_RWG, void, env) DEF_HELPER_FLAGS_1(fcmpq, TCG_CALL_NO_WG, tl, env) DEF_HELPER_FLAGS_1(fcmpeq, TCG_CALL_NO_WG, tl, env) #ifdef TARGET_SPARC64 -DEF_HELPER_FLAGS_3(ldxfsr, TCG_CALL_NO_RWG, tl, env, tl, i64) DEF_HELPER_FLAGS_1(fabsd, TCG_CALL_NO_RWG_SE, f64, f64) DEF_HELPER_FLAGS_3(fcmps_fcc1, TCG_CALL_NO_WG, tl, env, f32, f32) DEF_HELPER_FLAGS_3(fcmps_fcc2, TCG_CALL_NO_WG, tl, env, f32, f32) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 4c0e7cde4a..bf8c5a16b6 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -45,7 +45,6 @@ # define gen_helper_clear_softint(E, S) qemu_build_not_reached() # define gen_helper_done(E) qemu_build_not_reached() # define gen_helper_flushw(E) qemu_build_not_reached() -# define gen_helper_ldxfsr(D, E, A, B) qemu_build_not_reached() # define gen_helper_rdccr(D, E) qemu_build_not_reached() # define gen_helper_rdcwp(D, E) qemu_build_not_reached() # define gen_helper_restored(E) qemu_build_not_reached() @@ -63,6 +62,8 @@ # define gen_helper_write_softint(E, S) qemu_build_not_reached() # define gen_helper_wrpil(E, S) qemu_build_not_reached() # define gen_helper_wrpstate(E, S) qemu_build_not_reached() +# define FSR_LDXFSR_MASK 0 +# define FSR_LDXFSR_OLDMASK 0 # define MAXTL_MASK 0 #endif @@ -4675,44 +4676,27 @@ static bool trans_STDFQ(DisasContext *dc, arg_STDFQ *a) return true; } -static bool trans_LDFSR(DisasContext *dc, arg_r_r_ri *a) +static bool do_ldfsr(DisasContext *dc, arg_r_r_ri *a, MemOp mop, + target_ulong new_mask, target_ulong old_mask) { - TCGv addr; - TCGv_i32 tmp; - - addr = gen_ldst_addr(dc, a->rs1, a->imm, a->rs2_or_imm); + TCGv tmp, addr = gen_ldst_addr(dc, a->rs1, a->imm, a->rs2_or_imm); if (addr == NULL) { return false; } if (gen_trap_ifnofpu(dc)) { return true; } - tmp = tcg_temp_new_i32(); - tcg_gen_qemu_ld_i32(tmp, addr, dc->mem_idx, MO_TEUL | MO_ALIGN); - gen_helper_ldfsr(cpu_fsr, tcg_env, cpu_fsr, tmp); + tmp = tcg_temp_new(); + tcg_gen_qemu_ld_tl(tmp, addr, dc->mem_idx, mop | MO_ALIGN); + tcg_gen_andi_tl(tmp, tmp, new_mask); + tcg_gen_andi_tl(cpu_fsr, cpu_fsr, old_mask); + tcg_gen_or_tl(cpu_fsr, cpu_fsr, tmp); + gen_helper_set_fsr(tcg_env, cpu_fsr); return advance_pc(dc); } -static bool trans_LDXFSR(DisasContext *dc, arg_r_r_ri *a) -{ - TCGv addr; - TCGv_i64 tmp; - - if (!avail_64(dc)) { - return false; - } - addr = gen_ldst_addr(dc, a->rs1, a->imm, a->rs2_or_imm); - if (addr == NULL) { - return false; - } - if (gen_trap_ifnofpu(dc)) { - return true; - } - tmp = tcg_temp_new_i64(); - tcg_gen_qemu_ld_i64(tmp, addr, dc->mem_idx, MO_TEUQ | MO_ALIGN); - gen_helper_ldxfsr(cpu_fsr, tcg_env, cpu_fsr, tmp); - return advance_pc(dc); -} +TRANS(LDFSR, ALL, do_ldfsr, a, MO_TEUL, FSR_LDFSR_MASK, FSR_LDFSR_OLDMASK) +TRANS(LDXFSR, 64, do_ldfsr, a, MO_TEUQ, FSR_LDXFSR_MASK, FSR_LDXFSR_OLDMASK) static bool do_stfsr(DisasContext *dc, arg_r_r_ri *a, MemOp mop) { From b88ce6f246f40fd293803562ac60f29ff1ce9f6f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 5 Oct 2023 03:12:12 -0700 Subject: [PATCH 077/974] target/sparc: Move EDGE* to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 22 +++- target/sparc/translate.c | 271 ++++++++++++++++---------------------- 2 files changed, 133 insertions(+), 160 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 5df3b1add4..a9630509bd 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -35,6 +35,9 @@ CALL 01 i:s30 @r_r_ri_cc0 .. rd:5 ...... rs1:5 imm:1 rs2_or_imm:s13 &r_r_ri_cc cc=0 @r_r_ri_cc1 .. rd:5 ...... rs1:5 imm:1 rs2_or_imm:s13 &r_r_ri_cc cc=1 +&r_r_r rd rs1 rs2 +@r_r_r .. rd:5 ...... rs1:5 . ........ rs2:5 &r_r_r + { [ STBAR 10 00000 101000 01111 0 0000000000000 @@ -231,7 +234,24 @@ RESTORE 10 ..... 111101 ..... . ............. @r_r_ri DONE 10 00000 111110 00000 0 0000000000000 RETRY 10 00001 111110 00000 0 0000000000000 -NCP 10 ----- 110110 ----- --------- ----- # v8 CPop1 +{ + [ + EDGE8cc 10 ..... 110110 ..... 0 0000 0000 ..... @r_r_r + EDGE8N 10 ..... 110110 ..... 0 0000 0001 ..... @r_r_r + EDGE8Lcc 10 ..... 110110 ..... 0 0000 0010 ..... @r_r_r + EDGE8LN 10 ..... 110110 ..... 0 0000 0011 ..... @r_r_r + EDGE16cc 10 ..... 110110 ..... 0 0000 0100 ..... @r_r_r + EDGE16N 10 ..... 110110 ..... 0 0000 0101 ..... @r_r_r + EDGE16Lcc 10 ..... 110110 ..... 0 0000 0110 ..... @r_r_r + EDGE16LN 10 ..... 110110 ..... 0 0000 0111 ..... @r_r_r + EDGE32cc 10 ..... 110110 ..... 0 0000 1000 ..... @r_r_r + EDGE32N 10 ..... 110110 ..... 0 0000 1001 ..... @r_r_r + EDGE32Lcc 10 ..... 110110 ..... 0 0000 1010 ..... @r_r_r + EDGE32LN 10 ..... 110110 ..... 0 0000 1011 ..... @r_r_r + ] + NCP 10 ----- 110110 ----- --------- ----- # v8 CPop1 +} + NCP 10 ----- 110111 ----- --------- ----- # v8 CPop2 ## diff --git a/target/sparc/translate.c b/target/sparc/translate.c index bf8c5a16b6..2f38453dc6 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -2728,93 +2728,6 @@ static void gen_load_trap_state_at_tl(TCGv_ptr r_tsptr) } } -static void gen_edge(DisasContext *dc, TCGv dst, TCGv s1, TCGv s2, - int width, bool cc, bool left) -{ - TCGv lo1, lo2; - uint64_t amask, tabl, tabr; - int shift, imask, omask; - - if (cc) { - tcg_gen_mov_tl(cpu_cc_src, s1); - tcg_gen_mov_tl(cpu_cc_src2, s2); - tcg_gen_sub_tl(cpu_cc_dst, s1, s2); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_SUB); - dc->cc_op = CC_OP_SUB; - } - - /* Theory of operation: there are two tables, left and right (not to - be confused with the left and right versions of the opcode). These - are indexed by the low 3 bits of the inputs. To make things "easy", - these tables are loaded into two constants, TABL and TABR below. - The operation index = (input & imask) << shift calculates the index - into the constant, while val = (table >> index) & omask calculates - the value we're looking for. */ - switch (width) { - case 8: - imask = 0x7; - shift = 3; - omask = 0xff; - if (left) { - tabl = 0x80c0e0f0f8fcfeffULL; - tabr = 0xff7f3f1f0f070301ULL; - } else { - tabl = 0x0103070f1f3f7fffULL; - tabr = 0xfffefcf8f0e0c080ULL; - } - break; - case 16: - imask = 0x6; - shift = 1; - omask = 0xf; - if (left) { - tabl = 0x8cef; - tabr = 0xf731; - } else { - tabl = 0x137f; - tabr = 0xfec8; - } - break; - case 32: - imask = 0x4; - shift = 0; - omask = 0x3; - if (left) { - tabl = (2 << 2) | 3; - tabr = (3 << 2) | 1; - } else { - tabl = (1 << 2) | 3; - tabr = (3 << 2) | 2; - } - break; - default: - abort(); - } - - lo1 = tcg_temp_new(); - lo2 = tcg_temp_new(); - tcg_gen_andi_tl(lo1, s1, imask); - tcg_gen_andi_tl(lo2, s2, imask); - tcg_gen_shli_tl(lo1, lo1, shift); - tcg_gen_shli_tl(lo2, lo2, shift); - - tcg_gen_shr_tl(lo1, tcg_constant_tl(tabl), lo1); - tcg_gen_shr_tl(lo2, tcg_constant_tl(tabr), lo2); - tcg_gen_andi_tl(lo1, lo1, omask); - tcg_gen_andi_tl(lo2, lo2, omask); - - amask = -8; - if (AM_CHECK(dc)) { - amask &= 0xffffffffULL; - } - tcg_gen_andi_tl(s1, s1, amask); - tcg_gen_andi_tl(s2, s2, amask); - - /* Compute dst = (s1 == s2 ? lo1 : lo1 & lo2). */ - tcg_gen_and_tl(lo2, lo2, lo1); - tcg_gen_movcond_tl(TCG_COND_EQ, dst, s1, s2, lo1, lo2); -} - static void gen_alignaddr(TCGv dst, TCGv s1, TCGv s2, bool left) { TCGv tmp = tcg_temp_new(); @@ -2877,6 +2790,8 @@ static int extract_qfpreg(DisasContext *dc, int x) # define avail_64(C) true # define avail_GL(C) ((C)->def->features & CPU_FEATURE_GL) # define avail_HYPV(C) ((C)->def->features & CPU_FEATURE_HYPV) +# define avail_VIS1(C) ((C)->def->features & CPU_FEATURE_VIS1) +# define avail_VIS2(C) ((C)->def->features & CPU_FEATURE_VIS2) #else # define avail_32(C) true # define avail_ASR17(C) ((C)->def->features & CPU_FEATURE_ASR17) @@ -2887,6 +2802,8 @@ static int extract_qfpreg(DisasContext *dc, int x) # define avail_64(C) false # define avail_GL(C) false # define avail_HYPV(C) false +# define avail_VIS1(C) false +# define avail_VIS2(C) false #endif /* Default case for non jump instructions. */ @@ -4187,6 +4104,113 @@ static bool trans_MULScc(DisasContext *dc, arg_r_r_ri_cc *a) return do_arith(dc, a, CC_OP_ADD, NULL, NULL, gen_op_mulscc); } +static bool gen_edge(DisasContext *dc, arg_r_r_r *a, + int width, bool cc, bool left) +{ + TCGv dst, s1, s2, lo1, lo2; + uint64_t amask, tabl, tabr; + int shift, imask, omask; + + dst = gen_dest_gpr(dc, a->rd); + s1 = gen_load_gpr(dc, a->rs1); + s2 = gen_load_gpr(dc, a->rs2); + + if (cc) { + tcg_gen_mov_tl(cpu_cc_src, s1); + tcg_gen_mov_tl(cpu_cc_src2, s2); + tcg_gen_sub_tl(cpu_cc_dst, s1, s2); + tcg_gen_movi_i32(cpu_cc_op, CC_OP_SUB); + dc->cc_op = CC_OP_SUB; + } + + /* + * Theory of operation: there are two tables, left and right (not to + * be confused with the left and right versions of the opcode). These + * are indexed by the low 3 bits of the inputs. To make things "easy", + * these tables are loaded into two constants, TABL and TABR below. + * The operation index = (input & imask) << shift calculates the index + * into the constant, while val = (table >> index) & omask calculates + * the value we're looking for. + */ + switch (width) { + case 8: + imask = 0x7; + shift = 3; + omask = 0xff; + if (left) { + tabl = 0x80c0e0f0f8fcfeffULL; + tabr = 0xff7f3f1f0f070301ULL; + } else { + tabl = 0x0103070f1f3f7fffULL; + tabr = 0xfffefcf8f0e0c080ULL; + } + break; + case 16: + imask = 0x6; + shift = 1; + omask = 0xf; + if (left) { + tabl = 0x8cef; + tabr = 0xf731; + } else { + tabl = 0x137f; + tabr = 0xfec8; + } + break; + case 32: + imask = 0x4; + shift = 0; + omask = 0x3; + if (left) { + tabl = (2 << 2) | 3; + tabr = (3 << 2) | 1; + } else { + tabl = (1 << 2) | 3; + tabr = (3 << 2) | 2; + } + break; + default: + abort(); + } + + lo1 = tcg_temp_new(); + lo2 = tcg_temp_new(); + tcg_gen_andi_tl(lo1, s1, imask); + tcg_gen_andi_tl(lo2, s2, imask); + tcg_gen_shli_tl(lo1, lo1, shift); + tcg_gen_shli_tl(lo2, lo2, shift); + + tcg_gen_shr_tl(lo1, tcg_constant_tl(tabl), lo1); + tcg_gen_shr_tl(lo2, tcg_constant_tl(tabr), lo2); + tcg_gen_andi_tl(lo1, lo1, omask); + tcg_gen_andi_tl(lo2, lo2, omask); + + amask = address_mask_i(dc, -8); + tcg_gen_andi_tl(s1, s1, amask); + tcg_gen_andi_tl(s2, s2, amask); + + /* Compute dst = (s1 == s2 ? lo1 : lo1 & lo2). */ + tcg_gen_and_tl(lo2, lo2, lo1); + tcg_gen_movcond_tl(TCG_COND_EQ, dst, s1, s2, lo1, lo2); + + gen_store_gpr(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(EDGE8cc, VIS1, gen_edge, a, 8, 1, 0) +TRANS(EDGE8Lcc, VIS1, gen_edge, a, 8, 1, 1) +TRANS(EDGE16cc, VIS1, gen_edge, a, 16, 1, 0) +TRANS(EDGE16Lcc, VIS1, gen_edge, a, 16, 1, 1) +TRANS(EDGE32cc, VIS1, gen_edge, a, 32, 1, 0) +TRANS(EDGE32Lcc, VIS1, gen_edge, a, 32, 1, 1) + +TRANS(EDGE8N, VIS2, gen_edge, a, 8, 0, 0) +TRANS(EDGE8LN, VIS2, gen_edge, a, 8, 0, 1) +TRANS(EDGE16N, VIS2, gen_edge, a, 16, 0, 0) +TRANS(EDGE16LN, VIS2, gen_edge, a, 16, 0, 1) +TRANS(EDGE32N, VIS2, gen_edge, a, 32, 0, 0) +TRANS(EDGE32LN, VIS2, gen_edge, a, 32, 0, 1) + static bool do_shift_r(DisasContext *dc, arg_shiftr *a, bool l, bool u) { TCGv dst, src1, src2; @@ -5075,89 +5099,18 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) switch (opf) { case 0x000: /* VIS I edge8cc */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 8, 1, 0); - gen_store_gpr(dc, rd, cpu_dst); - break; case 0x001: /* VIS II edge8n */ - CHECK_FPU_FEATURE(dc, VIS2); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 8, 0, 0); - gen_store_gpr(dc, rd, cpu_dst); - break; case 0x002: /* VIS I edge8lcc */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 8, 1, 1); - gen_store_gpr(dc, rd, cpu_dst); - break; case 0x003: /* VIS II edge8ln */ - CHECK_FPU_FEATURE(dc, VIS2); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 8, 0, 1); - gen_store_gpr(dc, rd, cpu_dst); - break; case 0x004: /* VIS I edge16cc */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 16, 1, 0); - gen_store_gpr(dc, rd, cpu_dst); - break; case 0x005: /* VIS II edge16n */ - CHECK_FPU_FEATURE(dc, VIS2); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 16, 0, 0); - gen_store_gpr(dc, rd, cpu_dst); - break; case 0x006: /* VIS I edge16lcc */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 16, 1, 1); - gen_store_gpr(dc, rd, cpu_dst); - break; case 0x007: /* VIS II edge16ln */ - CHECK_FPU_FEATURE(dc, VIS2); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 16, 0, 1); - gen_store_gpr(dc, rd, cpu_dst); - break; case 0x008: /* VIS I edge32cc */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 32, 1, 0); - gen_store_gpr(dc, rd, cpu_dst); - break; case 0x009: /* VIS II edge32n */ - CHECK_FPU_FEATURE(dc, VIS2); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 32, 0, 0); - gen_store_gpr(dc, rd, cpu_dst); - break; case 0x00a: /* VIS I edge32lcc */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 32, 1, 1); - gen_store_gpr(dc, rd, cpu_dst); - break; case 0x00b: /* VIS II edge32ln */ - CHECK_FPU_FEATURE(dc, VIS2); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 32, 0, 1); - gen_store_gpr(dc, rd, cpu_dst); - break; + g_assert_not_reached(); /* in decodetree */ case 0x010: /* VIS I array8 */ CHECK_FPU_FEATURE(dc, VIS1); cpu_src1 = gen_load_gpr(dc, rs1); From 45bfed3b2c2eb723723865d58e40840fe9778e95 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 5 Oct 2023 03:26:54 -0700 Subject: [PATCH 078/974] target/sparc: Move ARRAY* to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 4 +++ target/sparc/translate.c | 57 ++++++++++++++++++++++++--------------- 2 files changed, 40 insertions(+), 21 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index a9630509bd..b15ede5fd4 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -248,6 +248,10 @@ RETRY 10 00001 111110 00000 0 0000000000000 EDGE32N 10 ..... 110110 ..... 0 0000 1001 ..... @r_r_r EDGE32Lcc 10 ..... 110110 ..... 0 0000 1010 ..... @r_r_r EDGE32LN 10 ..... 110110 ..... 0 0000 1011 ..... @r_r_r + + ARRAY8 10 ..... 110110 ..... 0 0001 0000 ..... @r_r_r + ARRAY16 10 ..... 110110 ..... 0 0001 0010 ..... @r_r_r + ARRAY32 10 ..... 110110 ..... 0 0001 0100 ..... @r_r_r ] NCP 10 ----- 110110 ----- --------- ----- # v8 CPop1 } diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 2f38453dc6..d40d664f16 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -722,6 +722,25 @@ static void gen_op_popc(TCGv dst, TCGv src1, TCGv src2) tcg_gen_ctpop_tl(dst, src2); } +#ifndef TARGET_SPARC64 +static void gen_helper_array8(TCGv dst, TCGv src1, TCGv src2) +{ + g_assert_not_reached(); +} +#endif + +static void gen_op_array16(TCGv dst, TCGv src1, TCGv src2) +{ + gen_helper_array8(dst, src1, src2); + tcg_gen_shli_tl(dst, dst, 1); +} + +static void gen_op_array32(TCGv dst, TCGv src1, TCGv src2) +{ + gen_helper_array8(dst, src1, src2); + tcg_gen_shli_tl(dst, dst, 2); +} + // 1 static void gen_op_eval_ba(TCGv dst) { @@ -4211,6 +4230,22 @@ TRANS(EDGE16LN, VIS2, gen_edge, a, 16, 0, 1) TRANS(EDGE32N, VIS2, gen_edge, a, 32, 0, 0) TRANS(EDGE32LN, VIS2, gen_edge, a, 32, 0, 1) +static bool do_rrr(DisasContext *dc, arg_r_r_r *a, + void (*func)(TCGv, TCGv, TCGv)) +{ + TCGv dst = gen_dest_gpr(dc, a->rd); + TCGv src1 = gen_load_gpr(dc, a->rs1); + TCGv src2 = gen_load_gpr(dc, a->rs2); + + func(dst, src1, src2); + gen_store_gpr(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(ARRAY8, VIS1, do_rrr, a, gen_helper_array8) +TRANS(ARRAY16, VIS1, do_rrr, a, gen_op_array16) +TRANS(ARRAY32, VIS1, do_rrr, a, gen_op_array32) + static bool do_shift_r(DisasContext *dc, arg_shiftr *a, bool l, bool u) { TCGv dst, src1, src2; @@ -5110,30 +5145,10 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x009: /* VIS II edge32n */ case 0x00a: /* VIS I edge32lcc */ case 0x00b: /* VIS II edge32ln */ - g_assert_not_reached(); /* in decodetree */ case 0x010: /* VIS I array8 */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_helper_array8(cpu_dst, cpu_src1, cpu_src2); - gen_store_gpr(dc, rd, cpu_dst); - break; case 0x012: /* VIS I array16 */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_helper_array8(cpu_dst, cpu_src1, cpu_src2); - tcg_gen_shli_i64(cpu_dst, cpu_dst, 1); - gen_store_gpr(dc, rd, cpu_dst); - break; case 0x014: /* VIS I array32 */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_helper_array8(cpu_dst, cpu_src1, cpu_src2); - tcg_gen_shli_i64(cpu_dst, cpu_dst, 2); - gen_store_gpr(dc, rd, cpu_dst); - break; + g_assert_not_reached(); /* in decodetree */ case 0x018: /* VIS I alignaddr */ CHECK_FPU_FEATURE(dc, VIS1); cpu_src1 = gen_load_gpr(dc, rs1); From 9e20ca9409a24763c98eff4c934292e35266496e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 5 Oct 2023 08:55:50 -0700 Subject: [PATCH 079/974] target/sparc: Move ADDRALIGN* to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 3 +++ target/sparc/translate.c | 56 ++++++++++++++++++++++----------------- 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index b15ede5fd4..f70423895e 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -252,6 +252,9 @@ RETRY 10 00001 111110 00000 0 0000000000000 ARRAY8 10 ..... 110110 ..... 0 0001 0000 ..... @r_r_r ARRAY16 10 ..... 110110 ..... 0 0001 0010 ..... @r_r_r ARRAY32 10 ..... 110110 ..... 0 0001 0100 ..... @r_r_r + + ALIGNADDR 10 ..... 110110 ..... 0 0001 1000 ..... @r_r_r + ALIGNADDRL 10 ..... 110110 ..... 0 0001 1010 ..... @r_r_r ] NCP 10 ----- 110110 ----- --------- ----- # v8 CPop1 } diff --git a/target/sparc/translate.c b/target/sparc/translate.c index d40d664f16..5d2be34135 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -2747,18 +2747,6 @@ static void gen_load_trap_state_at_tl(TCGv_ptr r_tsptr) } } -static void gen_alignaddr(TCGv dst, TCGv s1, TCGv s2, bool left) -{ - TCGv tmp = tcg_temp_new(); - - tcg_gen_add_tl(tmp, s1, s2); - tcg_gen_andi_tl(dst, tmp, -8); - if (left) { - tcg_gen_neg_tl(tmp, tmp); - } - tcg_gen_deposit_tl(cpu_gsr, cpu_gsr, tmp, 0, 3); -} - static void gen_faligndata(TCGv dst, TCGv gsr, TCGv s1, TCGv s2) { TCGv t1, t2, shift; @@ -4246,6 +4234,36 @@ TRANS(ARRAY8, VIS1, do_rrr, a, gen_helper_array8) TRANS(ARRAY16, VIS1, do_rrr, a, gen_op_array16) TRANS(ARRAY32, VIS1, do_rrr, a, gen_op_array32) +static void gen_op_alignaddr(TCGv dst, TCGv s1, TCGv s2) +{ +#ifdef TARGET_SPARC64 + TCGv tmp = tcg_temp_new(); + + tcg_gen_add_tl(tmp, s1, s2); + tcg_gen_andi_tl(dst, tmp, -8); + tcg_gen_deposit_tl(cpu_gsr, cpu_gsr, tmp, 0, 3); +#else + g_assert_not_reached(); +#endif +} + +static void gen_op_alignaddrl(TCGv dst, TCGv s1, TCGv s2) +{ +#ifdef TARGET_SPARC64 + TCGv tmp = tcg_temp_new(); + + tcg_gen_add_tl(tmp, s1, s2); + tcg_gen_andi_tl(dst, tmp, -8); + tcg_gen_neg_tl(tmp, tmp); + tcg_gen_deposit_tl(cpu_gsr, cpu_gsr, tmp, 0, 3); +#else + g_assert_not_reached(); +#endif +} + +TRANS(ALIGNADDR, VIS1, do_rrr, a, gen_op_alignaddr) +TRANS(ALIGNADDRL, VIS1, do_rrr, a, gen_op_alignaddrl) + static bool do_shift_r(DisasContext *dc, arg_shiftr *a, bool l, bool u) { TCGv dst, src1, src2; @@ -5148,21 +5166,9 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x010: /* VIS I array8 */ case 0x012: /* VIS I array16 */ case 0x014: /* VIS I array32 */ - g_assert_not_reached(); /* in decodetree */ case 0x018: /* VIS I alignaddr */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_alignaddr(cpu_dst, cpu_src1, cpu_src2, 0); - gen_store_gpr(dc, rd, cpu_dst); - break; case 0x01a: /* VIS I alignaddrl */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_alignaddr(cpu_dst, cpu_src1, cpu_src2, 1); - gen_store_gpr(dc, rd, cpu_dst); - break; + g_assert_not_reached(); /* in decodetree */ case 0x019: /* VIS II bmask */ CHECK_FPU_FEATURE(dc, VIS2); cpu_src1 = gen_load_gpr(dc, rs1); From 39ca3490f85fa88ec8f3a1954dbc0c4bb96830a0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 5 Oct 2023 09:01:34 -0700 Subject: [PATCH 080/974] target/sparc: Move BMASK to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 2 ++ target/sparc/translate.c | 22 +++++++++++++--------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index f70423895e..db372573a2 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -255,6 +255,8 @@ RETRY 10 00001 111110 00000 0 0000000000000 ALIGNADDR 10 ..... 110110 ..... 0 0001 1000 ..... @r_r_r ALIGNADDRL 10 ..... 110110 ..... 0 0001 1010 ..... @r_r_r + + BMASK 10 ..... 110110 ..... 0 0001 1001 ..... @r_r_r ] NCP 10 ----- 110110 ----- --------- ----- # v8 CPop1 } diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 5d2be34135..ee0c263a99 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -4264,6 +4264,18 @@ static void gen_op_alignaddrl(TCGv dst, TCGv s1, TCGv s2) TRANS(ALIGNADDR, VIS1, do_rrr, a, gen_op_alignaddr) TRANS(ALIGNADDRL, VIS1, do_rrr, a, gen_op_alignaddrl) +static void gen_op_bmask(TCGv dst, TCGv s1, TCGv s2) +{ +#ifdef TARGET_SPARC64 + tcg_gen_add_tl(dst, s1, s2); + tcg_gen_deposit_tl(cpu_gsr, cpu_gsr, dst, 32, 32); +#else + g_assert_not_reached(); +#endif +} + +TRANS(BMASK, VIS2, do_rrr, a, gen_op_bmask) + static bool do_shift_r(DisasContext *dc, arg_shiftr *a, bool l, bool u) { TCGv dst, src1, src2; @@ -4803,7 +4815,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) { unsigned int opc, rs1, rs2, rd; TCGv cpu_src1 __attribute__((unused)); - TCGv cpu_src2 __attribute__((unused)); TCGv_i32 cpu_src1_32, cpu_src2_32; TCGv_i64 cpu_src1_64, cpu_src2_64; TCGv_i32 cpu_dst_32 __attribute__((unused)); @@ -5168,15 +5179,8 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x014: /* VIS I array32 */ case 0x018: /* VIS I alignaddr */ case 0x01a: /* VIS I alignaddrl */ - g_assert_not_reached(); /* in decodetree */ case 0x019: /* VIS II bmask */ - CHECK_FPU_FEATURE(dc, VIS2); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - tcg_gen_add_tl(cpu_dst, cpu_src1, cpu_src2); - tcg_gen_deposit_tl(cpu_gsr, cpu_gsr, cpu_dst, 32, 32); - gen_store_gpr(dc, rd, cpu_dst); - break; + g_assert_not_reached(); /* in decodetree */ case 0x020: /* VIS I fcmple16 */ CHECK_FPU_FEATURE(dc, VIS1); cpu_src1_64 = gen_load_fpr_D(dc, rs1); From baf3dbf25866bce36572f43040660989a4e6ad42 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 5 Oct 2023 09:41:05 -0700 Subject: [PATCH 081/974] target/sparc: Move FMOVS, FNEGS, FABSS, FSRC*S, FNOT*S to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 13 ++++++ target/sparc/translate.c | 92 +++++++++++++++++++++------------------ 2 files changed, 62 insertions(+), 43 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index db372573a2..669a54e297 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -38,6 +38,10 @@ CALL 01 i:s30 &r_r_r rd rs1 rs2 @r_r_r .. rd:5 ...... rs1:5 . ........ rs2:5 &r_r_r +&r_r rd rs +@r_r1 .. rd:5 ...... rs:5 . ........ ..... &r_r +@r_r2 .. rd:5 ...... ..... . ........ rs:5 &r_r + { [ STBAR 10 00000 101000 01111 0 0000000000000 @@ -234,6 +238,10 @@ RESTORE 10 ..... 111101 ..... . ............. @r_r_ri DONE 10 00000 111110 00000 0 0000000000000 RETRY 10 00001 111110 00000 0 0000000000000 +FMOVs 10 ..... 110100 00000 0 0000 0001 ..... @r_r2 +FNEGs 10 ..... 110100 00000 0 0000 0101 ..... @r_r2 +FABSs 10 ..... 110100 00000 0 0000 1001 ..... @r_r2 + { [ EDGE8cc 10 ..... 110110 ..... 0 0000 0000 ..... @r_r_r @@ -257,6 +265,11 @@ RETRY 10 00001 111110 00000 0 0000000000000 ALIGNADDRL 10 ..... 110110 ..... 0 0001 1010 ..... @r_r_r BMASK 10 ..... 110110 ..... 0 0001 1001 ..... @r_r_r + + FSRCs 10 ..... 110110 ..... 0 0111 0101 00000 @r_r1 # FSRC1s + FSRCs 10 ..... 110110 00000 0 0111 1001 ..... @r_r2 # FSRC2s + FNOTs 10 ..... 110110 ..... 0 0110 1011 00000 @r_r1 # FNOT1s + FNOTs 10 ..... 110110 00000 0 0110 0111 ..... @r_r2 # FNOT2s ] NCP 10 ----- 110110 ----- --------- ----- # v8 CPop1 } diff --git a/target/sparc/translate.c b/target/sparc/translate.c index ee0c263a99..eb0aa2020f 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -1397,6 +1397,29 @@ static void gen_compare_reg(DisasCompare *cmp, int cond, TCGv r_src) cmp->c2 = tcg_constant_tl(0); } +static void gen_op_clear_ieee_excp_and_FTT(void) +{ + tcg_gen_andi_tl(cpu_fsr, cpu_fsr, FSR_FTT_CEXC_NMASK); +} + +static void gen_op_fmovs(TCGv_i32 dst, TCGv_i32 src) +{ + gen_op_clear_ieee_excp_and_FTT(); + tcg_gen_mov_i32(dst, src); +} + +static void gen_op_fnegs(TCGv_i32 dst, TCGv_i32 src) +{ + gen_op_clear_ieee_excp_and_FTT(); + gen_helper_fnegs(dst, src); +} + +static void gen_op_fabss(TCGv_i32 dst, TCGv_i32 src) +{ + gen_op_clear_ieee_excp_and_FTT(); + gen_helper_fabss(dst, src); +} + #ifdef TARGET_SPARC64 static void gen_op_fcmps(int fccno, TCGv_i32 r_rs1, TCGv_i32 r_rs2) { @@ -1557,11 +1580,6 @@ static int gen_trap_ifnofpu(DisasContext *dc) return 0; } -static void gen_op_clear_ieee_excp_and_FTT(void) -{ - tcg_gen_andi_tl(cpu_fsr, cpu_fsr, FSR_FTT_CEXC_NMASK); -} - static void gen_fop_FF(DisasContext *dc, int rd, int rs, void (*gen)(TCGv_i32, TCGv_ptr, TCGv_i32)) { @@ -1576,19 +1594,6 @@ static void gen_fop_FF(DisasContext *dc, int rd, int rs, gen_store_fpr_F(dc, rd, dst); } -static void gen_ne_fop_FF(DisasContext *dc, int rd, int rs, - void (*gen)(TCGv_i32, TCGv_i32)) -{ - TCGv_i32 dst, src; - - src = gen_load_fpr_F(dc, rs); - dst = gen_dest_fpr_F(dc); - - gen(dst, src); - - gen_store_fpr_F(dc, rd, dst); -} - static void gen_fop_FFF(DisasContext *dc, int rd, int rs1, int rs2, void (*gen)(TCGv_i32, TCGv_ptr, TCGv_i32, TCGv_i32)) { @@ -4803,6 +4808,27 @@ static bool do_stfsr(DisasContext *dc, arg_r_r_ri *a, MemOp mop) TRANS(STFSR, ALL, do_stfsr, a, MO_TEUL) TRANS(STXFSR, 64, do_stfsr, a, MO_TEUQ) +static bool do_ff(DisasContext *dc, arg_r_r *a, + void (*func)(TCGv_i32, TCGv_i32)) +{ + TCGv_i32 tmp; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + tmp = gen_load_fpr_F(dc, a->rs); + func(tmp, tmp); + gen_store_fpr_F(dc, a->rd, tmp); + return advance_pc(dc); +} + +TRANS(FMOVs, ALL, do_ff, a, gen_op_fmovs) +TRANS(FNEGs, ALL, do_ff, a, gen_op_fnegs) +TRANS(FABSs, ALL, do_ff, a, gen_op_fabss) +TRANS(FSRCs, VIS1, do_ff, a, tcg_gen_mov_i32) +TRANS(FNOTs, VIS1, do_ff, a, tcg_gen_not_i32) + #define CHECK_IU_FEATURE(dc, FEATURE) \ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ goto illegal_insn; @@ -4844,15 +4870,9 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) switch (xop) { case 0x1: /* fmovs */ - cpu_src1_32 = gen_load_fpr_F(dc, rs2); - gen_store_fpr_F(dc, rd, cpu_src1_32); - break; case 0x5: /* fnegs */ - gen_ne_fop_FF(dc, rd, rs2, gen_helper_fnegs); - break; case 0x9: /* fabss */ - gen_ne_fop_FF(dc, rd, rs2, gen_helper_fabss); - break; + g_assert_not_reached(); /* in decodetree */ case 0x29: /* fsqrts */ gen_fop_FF(dc, rd, rs2, gen_helper_fsqrts); break; @@ -5180,6 +5200,10 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x018: /* VIS I alignaddr */ case 0x01a: /* VIS I alignaddrl */ case 0x019: /* VIS II bmask */ + case 0x067: /* VIS I fnot2s */ + case 0x06b: /* VIS I fnot1s */ + case 0x075: /* VIS I fsrc1s */ + case 0x079: /* VIS I fsrc2s */ g_assert_not_reached(); /* in decodetree */ case 0x020: /* VIS I fcmple16 */ CHECK_FPU_FEATURE(dc, VIS1); @@ -5367,10 +5391,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) CHECK_FPU_FEATURE(dc, VIS1); gen_ne_fop_DD(dc, rd, rs2, tcg_gen_not_i64); break; - case 0x067: /* VIS I fnot2s */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_FF(dc, rd, rs2, tcg_gen_not_i32); - break; case 0x068: /* VIS I fandnot1 */ CHECK_FPU_FEATURE(dc, VIS1); gen_ne_fop_DDD(dc, rd, rs2, rs1, tcg_gen_andc_i64); @@ -5383,10 +5403,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) CHECK_FPU_FEATURE(dc, VIS1); gen_ne_fop_DD(dc, rd, rs1, tcg_gen_not_i64); break; - case 0x06b: /* VIS I fnot1s */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_FF(dc, rd, rs1, tcg_gen_not_i32); - break; case 0x06c: /* VIS I fxor */ CHECK_FPU_FEATURE(dc, VIS1); gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_xor_i64); @@ -5424,11 +5440,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) cpu_src1_64 = gen_load_fpr_D(dc, rs1); gen_store_fpr_D(dc, rd, cpu_src1_64); break; - case 0x075: /* VIS I fsrc1s */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1_32 = gen_load_fpr_F(dc, rs1); - gen_store_fpr_F(dc, rd, cpu_src1_32); - break; case 0x076: /* VIS I fornot2 */ CHECK_FPU_FEATURE(dc, VIS1); gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_orc_i64); @@ -5442,11 +5453,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) cpu_src1_64 = gen_load_fpr_D(dc, rs2); gen_store_fpr_D(dc, rd, cpu_src1_64); break; - case 0x079: /* VIS I fsrc2s */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1_32 = gen_load_fpr_F(dc, rs2); - gen_store_fpr_F(dc, rd, cpu_src1_32); - break; case 0x07a: /* VIS I fornot1 */ CHECK_FPU_FEATURE(dc, VIS1); gen_ne_fop_DDD(dc, rd, rs2, rs1, tcg_gen_orc_i64); From c6d83e4ff50bc816c5c7fb7df1f5c87bf2315fff Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 5 Oct 2023 10:53:30 -0700 Subject: [PATCH 082/974] target/sparc: Move FMOVD, FNEGD, FABSD, FSRC*D, FNOT*D to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 7 +++ target/sparc/translate.c | 91 +++++++++++++++++++++------------------ 2 files changed, 56 insertions(+), 42 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 669a54e297..23a21f2c20 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -239,8 +239,11 @@ DONE 10 00000 111110 00000 0 0000000000000 RETRY 10 00001 111110 00000 0 0000000000000 FMOVs 10 ..... 110100 00000 0 0000 0001 ..... @r_r2 +FMOVd 10 ..... 110100 00000 0 0000 0010 ..... @r_r2 FNEGs 10 ..... 110100 00000 0 0000 0101 ..... @r_r2 +FNEGd 10 ..... 110100 00000 0 0000 0110 ..... @r_r2 FABSs 10 ..... 110100 00000 0 0000 1001 ..... @r_r2 +FABSd 10 ..... 110100 00000 0 0000 1010 ..... @r_r2 { [ @@ -266,9 +269,13 @@ FABSs 10 ..... 110100 00000 0 0000 1001 ..... @r_r2 BMASK 10 ..... 110110 ..... 0 0001 1001 ..... @r_r_r + FSRCd 10 ..... 110110 ..... 0 0111 0100 00000 @r_r1 # FSRC1d FSRCs 10 ..... 110110 ..... 0 0111 0101 00000 @r_r1 # FSRC1s + FSRCd 10 ..... 110110 00000 0 0111 1000 ..... @r_r2 # FSRC2d FSRCs 10 ..... 110110 00000 0 0111 1001 ..... @r_r2 # FSRC2s + FNOTd 10 ..... 110110 ..... 0 0110 1010 00000 @r_r1 # FNOT1d FNOTs 10 ..... 110110 ..... 0 0110 1011 00000 @r_r1 # FNOT1s + FNOTd 10 ..... 110110 00000 0 0110 0110 ..... @r_r2 # FNOT2d FNOTs 10 ..... 110110 00000 0 0110 0111 ..... @r_r2 # FNOT2s ] NCP 10 ----- 110110 ----- --------- ----- # v8 CPop1 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index eb0aa2020f..572de4ef14 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -44,7 +44,9 @@ #else # define gen_helper_clear_softint(E, S) qemu_build_not_reached() # define gen_helper_done(E) qemu_build_not_reached() +# define gen_helper_fabsd(D, S) qemu_build_not_reached() # define gen_helper_flushw(E) qemu_build_not_reached() +# define gen_helper_fnegd(D, S) qemu_build_not_reached() # define gen_helper_rdccr(D, E) qemu_build_not_reached() # define gen_helper_rdcwp(D, E) qemu_build_not_reached() # define gen_helper_restored(E) qemu_build_not_reached() @@ -1420,6 +1422,24 @@ static void gen_op_fabss(TCGv_i32 dst, TCGv_i32 src) gen_helper_fabss(dst, src); } +static void gen_op_fmovd(TCGv_i64 dst, TCGv_i64 src) +{ + gen_op_clear_ieee_excp_and_FTT(); + tcg_gen_mov_i64(dst, src); +} + +static void gen_op_fnegd(TCGv_i64 dst, TCGv_i64 src) +{ + gen_op_clear_ieee_excp_and_FTT(); + gen_helper_fnegd(dst, src); +} + +static void gen_op_fabsd(TCGv_i64 dst, TCGv_i64 src) +{ + gen_op_clear_ieee_excp_and_FTT(); + gen_helper_fabsd(dst, src); +} + #ifdef TARGET_SPARC64 static void gen_op_fcmps(int fccno, TCGv_i32 r_rs1, TCGv_i32 r_rs2) { @@ -1639,21 +1659,6 @@ static void gen_fop_DD(DisasContext *dc, int rd, int rs, gen_store_fpr_D(dc, rd, dst); } -#ifdef TARGET_SPARC64 -static void gen_ne_fop_DD(DisasContext *dc, int rd, int rs, - void (*gen)(TCGv_i64, TCGv_i64)) -{ - TCGv_i64 dst, src; - - src = gen_load_fpr_D(dc, rs); - dst = gen_dest_fpr_D(dc, rd); - - gen(dst, src); - - gen_store_fpr_D(dc, rd, dst); -} -#endif - static void gen_fop_DDD(DisasContext *dc, int rd, int rs1, int rs2, void (*gen)(TCGv_i64, TCGv_ptr, TCGv_i64, TCGv_i64)) { @@ -4829,6 +4834,28 @@ TRANS(FABSs, ALL, do_ff, a, gen_op_fabss) TRANS(FSRCs, VIS1, do_ff, a, tcg_gen_mov_i32) TRANS(FNOTs, VIS1, do_ff, a, tcg_gen_not_i32) +static bool do_dd(DisasContext *dc, arg_r_r *a, + void (*func)(TCGv_i64, TCGv_i64)) +{ + TCGv_i64 dst, src; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + dst = gen_dest_fpr_D(dc, a->rd); + src = gen_load_fpr_D(dc, a->rs); + func(dst, src); + gen_store_fpr_D(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(FMOVd, 64, do_dd, a, gen_op_fmovd) +TRANS(FNEGd, 64, do_dd, a, gen_op_fnegd) +TRANS(FABSd, 64, do_dd, a, gen_op_fabsd) +TRANS(FSRCd, VIS1, do_dd, a, tcg_gen_mov_i64) +TRANS(FNOTd, VIS1, do_dd, a, tcg_gen_not_i64) + #define CHECK_IU_FEATURE(dc, FEATURE) \ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ goto illegal_insn; @@ -4872,6 +4899,9 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x1: /* fmovs */ case 0x5: /* fnegs */ case 0x9: /* fabss */ + case 0x2: /* V9 fmovd */ + case 0x6: /* V9 fnegd */ + case 0xa: /* V9 fabsd */ g_assert_not_reached(); /* in decodetree */ case 0x29: /* fsqrts */ gen_fop_FF(dc, rd, rs2, gen_helper_fsqrts); @@ -4974,24 +5004,14 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) gen_fop_FQ(dc, rd, rs2, gen_helper_fqtoi); break; #ifdef TARGET_SPARC64 - case 0x2: /* V9 fmovd */ - cpu_src1_64 = gen_load_fpr_D(dc, rs2); - gen_store_fpr_D(dc, rd, cpu_src1_64); - break; case 0x3: /* V9 fmovq */ CHECK_FPU_FEATURE(dc, FLOAT128); gen_move_Q(dc, rd, rs2); break; - case 0x6: /* V9 fnegd */ - gen_ne_fop_DD(dc, rd, rs2, gen_helper_fnegd); - break; case 0x7: /* V9 fnegq */ CHECK_FPU_FEATURE(dc, FLOAT128); gen_ne_fop_QQ(dc, rd, rs2, gen_helper_fnegq); break; - case 0xa: /* V9 fabsd */ - gen_ne_fop_DD(dc, rd, rs2, gen_helper_fabsd); - break; case 0xb: /* V9 fabsq */ CHECK_FPU_FEATURE(dc, FLOAT128); gen_ne_fop_QQ(dc, rd, rs2, gen_helper_fabsq); @@ -5204,6 +5224,10 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x06b: /* VIS I fnot1s */ case 0x075: /* VIS I fsrc1s */ case 0x079: /* VIS I fsrc2s */ + case 0x066: /* VIS I fnot2 */ + case 0x06a: /* VIS I fnot1 */ + case 0x074: /* VIS I fsrc1 */ + case 0x078: /* VIS I fsrc2 */ g_assert_not_reached(); /* in decodetree */ case 0x020: /* VIS I fcmple16 */ CHECK_FPU_FEATURE(dc, VIS1); @@ -5387,10 +5411,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) CHECK_FPU_FEATURE(dc, VIS1); gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_andc_i32); break; - case 0x066: /* VIS I fnot2 */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DD(dc, rd, rs2, tcg_gen_not_i64); - break; case 0x068: /* VIS I fandnot1 */ CHECK_FPU_FEATURE(dc, VIS1); gen_ne_fop_DDD(dc, rd, rs2, rs1, tcg_gen_andc_i64); @@ -5399,10 +5419,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) CHECK_FPU_FEATURE(dc, VIS1); gen_ne_fop_FFF(dc, rd, rs2, rs1, tcg_gen_andc_i32); break; - case 0x06a: /* VIS I fnot1 */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DD(dc, rd, rs1, tcg_gen_not_i64); - break; case 0x06c: /* VIS I fxor */ CHECK_FPU_FEATURE(dc, VIS1); gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_xor_i64); @@ -5435,10 +5451,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) CHECK_FPU_FEATURE(dc, VIS1); gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_eqv_i32); break; - case 0x074: /* VIS I fsrc1 */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1_64 = gen_load_fpr_D(dc, rs1); - gen_store_fpr_D(dc, rd, cpu_src1_64); break; case 0x076: /* VIS I fornot2 */ CHECK_FPU_FEATURE(dc, VIS1); @@ -5448,11 +5460,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) CHECK_FPU_FEATURE(dc, VIS1); gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_orc_i32); break; - case 0x078: /* VIS I fsrc2 */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1_64 = gen_load_fpr_D(dc, rs2); - gen_store_fpr_D(dc, rd, cpu_src1_64); - break; case 0x07a: /* VIS I fornot1 */ CHECK_FPU_FEATURE(dc, VIS1); gen_ne_fop_DDD(dc, rd, rs2, rs1, tcg_gen_orc_i64); From fafba1bb0b1c37e668c0ac89477dfb84a5fb8ae4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 10 Oct 2023 10:48:56 -0700 Subject: [PATCH 083/974] target/sparc: Use tcg_gen_vec_{add,sub}* Replace the local helpers for the same integer operations. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/helper.h | 12 -------- target/sparc/translate.c | 15 +++++----- target/sparc/vis_helper.c | 59 --------------------------------------- 3 files changed, 7 insertions(+), 79 deletions(-) diff --git a/target/sparc/helper.h b/target/sparc/helper.h index 790752467f..dd1721a340 100644 --- a/target/sparc/helper.h +++ b/target/sparc/helper.h @@ -137,18 +137,6 @@ DEF_HELPER_FLAGS_2(fpack16, TCG_CALL_NO_RWG_SE, i32, i64, i64) DEF_HELPER_FLAGS_3(fpack32, TCG_CALL_NO_RWG_SE, i64, i64, i64, i64) DEF_HELPER_FLAGS_2(fpackfix, TCG_CALL_NO_RWG_SE, i32, i64, i64) DEF_HELPER_FLAGS_3(bshuffle, TCG_CALL_NO_RWG_SE, i64, i64, i64, i64) -#define VIS_HELPER(name) \ - DEF_HELPER_FLAGS_2(f ## name ## 16, TCG_CALL_NO_RWG_SE, \ - i64, i64, i64) \ - DEF_HELPER_FLAGS_2(f ## name ## 16s, TCG_CALL_NO_RWG_SE, \ - i32, i32, i32) \ - DEF_HELPER_FLAGS_2(f ## name ## 32, TCG_CALL_NO_RWG_SE, \ - i64, i64, i64) \ - DEF_HELPER_FLAGS_2(f ## name ## 32s, TCG_CALL_NO_RWG_SE, \ - i32, i32, i32) - -VIS_HELPER(padd) -VIS_HELPER(psub) #define VIS_CMPHELPER(name) \ DEF_HELPER_FLAGS_2(f##name##16, TCG_CALL_NO_RWG_SE, \ i64, i64, i64) \ diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 572de4ef14..09ca9c51bd 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -25,9 +25,8 @@ #include "exec/helper-proto.h" #include "exec/exec-all.h" #include "tcg/tcg-op.h" - +#include "tcg/tcg-op-gvec.h" #include "exec/helper-gen.h" - #include "exec/translator.h" #include "exec/log.h" #include "asi.h" @@ -5353,15 +5352,15 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) break; case 0x050: /* VIS I fpadd16 */ CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fpadd16); + gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_vec_add16_i64); break; case 0x051: /* VIS I fpadd16s */ CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_FFF(dc, rd, rs1, rs2, gen_helper_fpadd16s); + gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_vec_add16_i32); break; case 0x052: /* VIS I fpadd32 */ CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fpadd32); + gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_vec_add32_i64); break; case 0x053: /* VIS I fpadd32s */ CHECK_FPU_FEATURE(dc, VIS1); @@ -5369,15 +5368,15 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) break; case 0x054: /* VIS I fpsub16 */ CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fpsub16); + gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_vec_sub16_i64); break; case 0x055: /* VIS I fpsub16s */ CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_FFF(dc, rd, rs1, rs2, gen_helper_fpsub16s); + gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_vec_sub16_i32); break; case 0x056: /* VIS I fpsub32 */ CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fpsub32); + gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_vec_add32_i64); break; case 0x057: /* VIS I fpsub32s */ CHECK_FPU_FEATURE(dc, VIS1); diff --git a/target/sparc/vis_helper.c b/target/sparc/vis_helper.c index 3afdc6975c..7763b16c24 100644 --- a/target/sparc/vis_helper.c +++ b/target/sparc/vis_helper.c @@ -275,65 +275,6 @@ uint64_t helper_fexpand(uint64_t src1, uint64_t src2) return d.ll; } -#define VIS_HELPER(name, F) \ - uint64_t name##16(uint64_t src1, uint64_t src2) \ - { \ - VIS64 s, d; \ - \ - s.ll = src1; \ - d.ll = src2; \ - \ - d.VIS_W64(0) = F(d.VIS_W64(0), s.VIS_W64(0)); \ - d.VIS_W64(1) = F(d.VIS_W64(1), s.VIS_W64(1)); \ - d.VIS_W64(2) = F(d.VIS_W64(2), s.VIS_W64(2)); \ - d.VIS_W64(3) = F(d.VIS_W64(3), s.VIS_W64(3)); \ - \ - return d.ll; \ - } \ - \ - uint32_t name##16s(uint32_t src1, uint32_t src2) \ - { \ - VIS32 s, d; \ - \ - s.l = src1; \ - d.l = src2; \ - \ - d.VIS_W32(0) = F(d.VIS_W32(0), s.VIS_W32(0)); \ - d.VIS_W32(1) = F(d.VIS_W32(1), s.VIS_W32(1)); \ - \ - return d.l; \ - } \ - \ - uint64_t name##32(uint64_t src1, uint64_t src2) \ - { \ - VIS64 s, d; \ - \ - s.ll = src1; \ - d.ll = src2; \ - \ - d.VIS_L64(0) = F(d.VIS_L64(0), s.VIS_L64(0)); \ - d.VIS_L64(1) = F(d.VIS_L64(1), s.VIS_L64(1)); \ - \ - return d.ll; \ - } \ - \ - uint32_t name##32s(uint32_t src1, uint32_t src2) \ - { \ - VIS32 s, d; \ - \ - s.l = src1; \ - d.l = src2; \ - \ - d.l = F(d.l, s.l); \ - \ - return d.l; \ - } - -#define FADD(a, b) ((a) + (b)) -#define FSUB(a, b) ((a) - (b)) -VIS_HELPER(helper_fpadd, FADD) -VIS_HELPER(helper_fpsub, FSUB) - #define VIS_CMPHELPER(name, F) \ uint64_t name##16(uint64_t src1, uint64_t src2) \ { \ From 7f10b52f7b238d4ef1a6e5d4740604e07631a6c4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 10 Oct 2023 12:58:14 -0700 Subject: [PATCH 084/974] target/sparc: Move gen_ne_fop_FFF insns to decodetree Move FANDNOT1s, FANDNOT2s, FANDs, FNANDs, FNORs, FORNOT1s, FORNOT2s, FORs, FPADD16s, FPADD32s, FPSUB16s, FPSUB32s, FXNORs, FXORs. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 16 ++++++ target/sparc/translate.c | 116 ++++++++++++++------------------------ 2 files changed, 59 insertions(+), 73 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 23a21f2c20..0290f2aefb 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -37,6 +37,7 @@ CALL 01 i:s30 &r_r_r rd rs1 rs2 @r_r_r .. rd:5 ...... rs1:5 . ........ rs2:5 &r_r_r +@r_r_r_swap .. rd:5 ...... rs2:5 . ........ rs1:5 &r_r_r &r_r rd rs @r_r1 .. rd:5 ...... rs:5 . ........ ..... &r_r @@ -277,6 +278,21 @@ FABSd 10 ..... 110100 00000 0 0000 1010 ..... @r_r2 FNOTs 10 ..... 110110 ..... 0 0110 1011 00000 @r_r1 # FNOT1s FNOTd 10 ..... 110110 00000 0 0110 0110 ..... @r_r2 # FNOT2d FNOTs 10 ..... 110110 00000 0 0110 0111 ..... @r_r2 # FNOT2s + + FPADD16s 10 ..... 110110 ..... 0 0101 0001 ..... @r_r_r + FPADD32s 10 ..... 110110 ..... 0 0101 0011 ..... @r_r_r + FPSUB16s 10 ..... 110110 ..... 0 0101 0101 ..... @r_r_r + FPSUB32s 10 ..... 110110 ..... 0 0101 0111 ..... @r_r_r + FNORs 10 ..... 110110 ..... 0 0110 0011 ..... @r_r_r + FANDNOTs 10 ..... 110110 ..... 0 0110 0101 ..... @r_r_r # FANDNOT2s + FANDNOTs 10 ..... 110110 ..... 0 0110 1001 ..... @r_r_r_swap # ... 1s + FXORs 10 ..... 110110 ..... 0 0110 1101 ..... @r_r_r + FNANDs 10 ..... 110110 ..... 0 0110 1111 ..... @r_r_r + FANDs 10 ..... 110110 ..... 0 0111 0001 ..... @r_r_r + FXNORs 10 ..... 110110 ..... 0 0111 0011 ..... @r_r_r + FORNOTs 10 ..... 110110 ..... 0 0111 0111 ..... @r_r_r # FORNOT2s + FORNOTs 10 ..... 110110 ..... 0 0111 1011 ..... @r_r_r_swap # ... 1s + FORs 10 ..... 110110 ..... 0 0111 1101 ..... @r_r_r ] NCP 10 ----- 110110 ----- --------- ----- # v8 CPop1 } diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 09ca9c51bd..949d1954a4 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -1628,22 +1628,6 @@ static void gen_fop_FFF(DisasContext *dc, int rd, int rs1, int rs2, gen_store_fpr_F(dc, rd, dst); } -#ifdef TARGET_SPARC64 -static void gen_ne_fop_FFF(DisasContext *dc, int rd, int rs1, int rs2, - void (*gen)(TCGv_i32, TCGv_i32, TCGv_i32)) -{ - TCGv_i32 dst, src1, src2; - - src1 = gen_load_fpr_F(dc, rs1); - src2 = gen_load_fpr_F(dc, rs2); - dst = gen_dest_fpr_F(dc); - - gen(dst, src1, src2); - - gen_store_fpr_F(dc, rd, dst); -} -#endif - static void gen_fop_DD(DisasContext *dc, int rd, int rs, void (*gen)(TCGv_i64, TCGv_ptr, TCGv_i64)) { @@ -4855,6 +4839,35 @@ TRANS(FABSd, 64, do_dd, a, gen_op_fabsd) TRANS(FSRCd, VIS1, do_dd, a, tcg_gen_mov_i64) TRANS(FNOTd, VIS1, do_dd, a, tcg_gen_not_i64) +static bool do_fff(DisasContext *dc, arg_r_r_r *a, + void (*func)(TCGv_i32, TCGv_i32, TCGv_i32)) +{ + TCGv_i32 src1, src2; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + src1 = gen_load_fpr_F(dc, a->rs1); + src2 = gen_load_fpr_F(dc, a->rs2); + func(src1, src1, src2); + gen_store_fpr_F(dc, a->rd, src1); + return advance_pc(dc); +} + +TRANS(FPADD16s, VIS1, do_fff, a, tcg_gen_vec_add16_i32) +TRANS(FPADD32s, VIS1, do_fff, a, tcg_gen_add_i32) +TRANS(FPSUB16s, VIS1, do_fff, a, tcg_gen_vec_sub16_i32) +TRANS(FPSUB32s, VIS1, do_fff, a, tcg_gen_sub_i32) +TRANS(FNORs, VIS1, do_fff, a, tcg_gen_nor_i32) +TRANS(FANDNOTs, VIS1, do_fff, a, tcg_gen_andc_i32) +TRANS(FXORs, VIS1, do_fff, a, tcg_gen_xor_i32) +TRANS(FNANDs, VIS1, do_fff, a, tcg_gen_nand_i32) +TRANS(FANDs, VIS1, do_fff, a, tcg_gen_and_i32) +TRANS(FXNORs, VIS1, do_fff, a, tcg_gen_eqv_i32) +TRANS(FORNOTs, VIS1, do_fff, a, tcg_gen_orc_i32) +TRANS(FORs, VIS1, do_fff, a, tcg_gen_or_i32) + #define CHECK_IU_FEATURE(dc, FEATURE) \ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ goto illegal_insn; @@ -5227,6 +5240,20 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x06a: /* VIS I fnot1 */ case 0x074: /* VIS I fsrc1 */ case 0x078: /* VIS I fsrc2 */ + case 0x051: /* VIS I fpadd16s */ + case 0x053: /* VIS I fpadd32s */ + case 0x055: /* VIS I fpsub16s */ + case 0x057: /* VIS I fpsub32s */ + case 0x063: /* VIS I fnors */ + case 0x065: /* VIS I fandnot2s */ + case 0x069: /* VIS I fandnot1s */ + case 0x06d: /* VIS I fxors */ + case 0x06f: /* VIS I fnands */ + case 0x071: /* VIS I fands */ + case 0x073: /* VIS I fxnors */ + case 0x077: /* VIS I fornot2s */ + case 0x07b: /* VIS I fornot1s */ + case 0x07d: /* VIS I fors */ g_assert_not_reached(); /* in decodetree */ case 0x020: /* VIS I fcmple16 */ CHECK_FPU_FEATURE(dc, VIS1); @@ -5354,34 +5381,18 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) CHECK_FPU_FEATURE(dc, VIS1); gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_vec_add16_i64); break; - case 0x051: /* VIS I fpadd16s */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_vec_add16_i32); - break; case 0x052: /* VIS I fpadd32 */ CHECK_FPU_FEATURE(dc, VIS1); gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_vec_add32_i64); break; - case 0x053: /* VIS I fpadd32s */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_add_i32); - break; case 0x054: /* VIS I fpsub16 */ CHECK_FPU_FEATURE(dc, VIS1); gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_vec_sub16_i64); break; - case 0x055: /* VIS I fpsub16s */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_vec_sub16_i32); - break; case 0x056: /* VIS I fpsub32 */ CHECK_FPU_FEATURE(dc, VIS1); gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_vec_add32_i64); break; - case 0x057: /* VIS I fpsub32s */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_sub_i32); - break; case 0x060: /* VIS I fzero */ CHECK_FPU_FEATURE(dc, VIS1); cpu_dst_64 = gen_dest_fpr_D(dc, rd); @@ -5398,83 +5409,42 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) CHECK_FPU_FEATURE(dc, VIS1); gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_nor_i64); break; - case 0x063: /* VIS I fnors */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_nor_i32); - break; case 0x064: /* VIS I fandnot2 */ CHECK_FPU_FEATURE(dc, VIS1); gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_andc_i64); break; - case 0x065: /* VIS I fandnot2s */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_andc_i32); - break; case 0x068: /* VIS I fandnot1 */ CHECK_FPU_FEATURE(dc, VIS1); gen_ne_fop_DDD(dc, rd, rs2, rs1, tcg_gen_andc_i64); break; - case 0x069: /* VIS I fandnot1s */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_FFF(dc, rd, rs2, rs1, tcg_gen_andc_i32); - break; case 0x06c: /* VIS I fxor */ CHECK_FPU_FEATURE(dc, VIS1); gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_xor_i64); break; - case 0x06d: /* VIS I fxors */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_xor_i32); - break; case 0x06e: /* VIS I fnand */ CHECK_FPU_FEATURE(dc, VIS1); gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_nand_i64); break; - case 0x06f: /* VIS I fnands */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_nand_i32); - break; case 0x070: /* VIS I fand */ CHECK_FPU_FEATURE(dc, VIS1); gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_and_i64); break; - case 0x071: /* VIS I fands */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_and_i32); - break; case 0x072: /* VIS I fxnor */ CHECK_FPU_FEATURE(dc, VIS1); gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_eqv_i64); break; - case 0x073: /* VIS I fxnors */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_eqv_i32); - break; - break; case 0x076: /* VIS I fornot2 */ CHECK_FPU_FEATURE(dc, VIS1); gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_orc_i64); break; - case 0x077: /* VIS I fornot2s */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_orc_i32); - break; case 0x07a: /* VIS I fornot1 */ CHECK_FPU_FEATURE(dc, VIS1); gen_ne_fop_DDD(dc, rd, rs2, rs1, tcg_gen_orc_i64); break; - case 0x07b: /* VIS I fornot1s */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_FFF(dc, rd, rs2, rs1, tcg_gen_orc_i32); - break; case 0x07c: /* VIS I for */ CHECK_FPU_FEATURE(dc, VIS1); gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_or_i64); break; - case 0x07d: /* VIS I fors */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_or_i32); - break; case 0x07e: /* VIS I fone */ CHECK_FPU_FEATURE(dc, VIS1); cpu_dst_64 = gen_dest_fpr_D(dc, rd); From e06c9f83c6e9d1fdad371fa5aa41eaa6fa7f8f67 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 10 Oct 2023 13:50:38 -0700 Subject: [PATCH 085/974] target/sparc: Move gen_ne_fop_DDD insns to decodetree Move FMUL8x16, FMUL8x16AU, FMUL8x16AL, FMUL8SUx16, FMUL8ULx16, FMULD8SUx16, FMULD8ULx16, FPMERGE, FEXPAND, FANDNOT1d, FANDNOT2d, FANDd, FNANDd, FNORd, FORNOT1d, FORNOT2d, FORd, FPADD16d, FPADD32d, FPSUB16d, FPSUB32d, FXNORd, FXORd. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 26 ++++++ target/sparc/translate.c | 178 +++++++++++++++----------------------- 2 files changed, 98 insertions(+), 106 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 0290f2aefb..0e29629b5c 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -270,6 +270,17 @@ FABSd 10 ..... 110100 00000 0 0000 1010 ..... @r_r2 BMASK 10 ..... 110110 ..... 0 0001 1001 ..... @r_r_r + FMUL8x16 10 ..... 110110 ..... 0 0011 0001 ..... @r_r_r + FMUL8x16AU 10 ..... 110110 ..... 0 0011 0011 ..... @r_r_r + FMUL8x16AL 10 ..... 110110 ..... 0 0011 0101 ..... @r_r_r + FMUL8SUx16 10 ..... 110110 ..... 0 0011 0110 ..... @r_r_r + FMUL8ULx16 10 ..... 110110 ..... 0 0011 0111 ..... @r_r_r + FMULD8SUx16 10 ..... 110110 ..... 0 0011 1000 ..... @r_r_r + FMULD8ULx16 10 ..... 110110 ..... 0 0011 1001 ..... @r_r_r + + FPMERGE 10 ..... 110110 ..... 0 0100 1011 ..... @r_r_r + FEXPAND 10 ..... 110110 ..... 0 0100 1101 ..... @r_r_r + FSRCd 10 ..... 110110 ..... 0 0111 0100 00000 @r_r1 # FSRC1d FSRCs 10 ..... 110110 ..... 0 0111 0101 00000 @r_r1 # FSRC1s FSRCd 10 ..... 110110 00000 0 0111 1000 ..... @r_r2 # FSRC2d @@ -279,19 +290,34 @@ FABSd 10 ..... 110100 00000 0 0000 1010 ..... @r_r2 FNOTd 10 ..... 110110 00000 0 0110 0110 ..... @r_r2 # FNOT2d FNOTs 10 ..... 110110 00000 0 0110 0111 ..... @r_r2 # FNOT2s + FPADD16 10 ..... 110110 ..... 0 0101 0000 ..... @r_r_r FPADD16s 10 ..... 110110 ..... 0 0101 0001 ..... @r_r_r + FPADD32 10 ..... 110110 ..... 0 0101 0010 ..... @r_r_r FPADD32s 10 ..... 110110 ..... 0 0101 0011 ..... @r_r_r + FPSUB16 10 ..... 110110 ..... 0 0101 0100 ..... @r_r_r FPSUB16s 10 ..... 110110 ..... 0 0101 0101 ..... @r_r_r + FPSUB32 10 ..... 110110 ..... 0 0101 0110 ..... @r_r_r FPSUB32s 10 ..... 110110 ..... 0 0101 0111 ..... @r_r_r + + FNORd 10 ..... 110110 ..... 0 0110 0010 ..... @r_r_r FNORs 10 ..... 110110 ..... 0 0110 0011 ..... @r_r_r + FANDNOTd 10 ..... 110110 ..... 0 0110 0100 ..... @r_r_r # FANDNOT2d FANDNOTs 10 ..... 110110 ..... 0 0110 0101 ..... @r_r_r # FANDNOT2s + FANDNOTd 10 ..... 110110 ..... 0 0110 1000 ..... @r_r_r_swap # ... 1d FANDNOTs 10 ..... 110110 ..... 0 0110 1001 ..... @r_r_r_swap # ... 1s + FXORd 10 ..... 110110 ..... 0 0110 1100 ..... @r_r_r FXORs 10 ..... 110110 ..... 0 0110 1101 ..... @r_r_r + FNANDd 10 ..... 110110 ..... 0 0110 1110 ..... @r_r_r FNANDs 10 ..... 110110 ..... 0 0110 1111 ..... @r_r_r + FANDd 10 ..... 110110 ..... 0 0111 0000 ..... @r_r_r FANDs 10 ..... 110110 ..... 0 0111 0001 ..... @r_r_r + FXNORd 10 ..... 110110 ..... 0 0111 0010 ..... @r_r_r FXNORs 10 ..... 110110 ..... 0 0111 0011 ..... @r_r_r + FORNOTd 10 ..... 110110 ..... 0 0111 0110 ..... @r_r_r # FORNOT2d FORNOTs 10 ..... 110110 ..... 0 0111 0111 ..... @r_r_r # FORNOT2s + FORNOTd 10 ..... 110110 ..... 0 0111 1010 ..... @r_r_r_swap # ... 1d FORNOTs 10 ..... 110110 ..... 0 0111 1011 ..... @r_r_r_swap # ... 1s + FORd 10 ..... 110110 ..... 0 0111 1100 ..... @r_r_r FORs 10 ..... 110110 ..... 0 0111 1101 ..... @r_r_r ] NCP 10 ----- 110110 ----- --------- ----- # v8 CPop1 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 949d1954a4..35fdcfe4c1 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -63,6 +63,15 @@ # define gen_helper_write_softint(E, S) qemu_build_not_reached() # define gen_helper_wrpil(E, S) qemu_build_not_reached() # define gen_helper_wrpstate(E, S) qemu_build_not_reached() +# define gen_helper_fexpand ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fmul8sux16 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fmul8ulx16 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fmul8x16al ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fmul8x16au ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fmul8x16 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fmuld8sux16 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fmuld8ulx16 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fpmerge ({ qemu_build_not_reached(); NULL; }) # define FSR_LDXFSR_MASK 0 # define FSR_LDXFSR_OLDMASK 0 # define MAXTL_MASK 0 @@ -1658,20 +1667,6 @@ static void gen_fop_DDD(DisasContext *dc, int rd, int rs1, int rs2, } #ifdef TARGET_SPARC64 -static void gen_ne_fop_DDD(DisasContext *dc, int rd, int rs1, int rs2, - void (*gen)(TCGv_i64, TCGv_i64, TCGv_i64)) -{ - TCGv_i64 dst, src1, src2; - - src1 = gen_load_fpr_D(dc, rs1); - src2 = gen_load_fpr_D(dc, rs2); - dst = gen_dest_fpr_D(dc, rd); - - gen(dst, src1, src2); - - gen_store_fpr_D(dc, rd, dst); -} - static void gen_gsr_fop_DDD(DisasContext *dc, int rd, int rs1, int rs2, void (*gen)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64)) { @@ -4868,6 +4863,46 @@ TRANS(FXNORs, VIS1, do_fff, a, tcg_gen_eqv_i32) TRANS(FORNOTs, VIS1, do_fff, a, tcg_gen_orc_i32) TRANS(FORs, VIS1, do_fff, a, tcg_gen_or_i32) +static bool do_ddd(DisasContext *dc, arg_r_r_r *a, + void (*func)(TCGv_i64, TCGv_i64, TCGv_i64)) +{ + TCGv_i64 dst, src1, src2; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + dst = gen_dest_fpr_D(dc, a->rd); + src1 = gen_load_fpr_D(dc, a->rs1); + src2 = gen_load_fpr_D(dc, a->rs2); + func(dst, src1, src2); + gen_store_fpr_D(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(FMUL8x16, VIS1, do_ddd, a, gen_helper_fmul8x16) +TRANS(FMUL8x16AU, VIS1, do_ddd, a, gen_helper_fmul8x16au) +TRANS(FMUL8x16AL, VIS1, do_ddd, a, gen_helper_fmul8x16al) +TRANS(FMUL8SUx16, VIS1, do_ddd, a, gen_helper_fmul8sux16) +TRANS(FMUL8ULx16, VIS1, do_ddd, a, gen_helper_fmul8ulx16) +TRANS(FMULD8SUx16, VIS1, do_ddd, a, gen_helper_fmuld8sux16) +TRANS(FMULD8ULx16, VIS1, do_ddd, a, gen_helper_fmuld8ulx16) +TRANS(FPMERGE, VIS1, do_ddd, a, gen_helper_fpmerge) +TRANS(FEXPAND, VIS1, do_ddd, a, gen_helper_fexpand) + +TRANS(FPADD16, VIS1, do_ddd, a, tcg_gen_vec_add16_i64) +TRANS(FPADD32, VIS1, do_ddd, a, tcg_gen_vec_add32_i64) +TRANS(FPSUB16, VIS1, do_ddd, a, tcg_gen_vec_sub16_i64) +TRANS(FPSUB32, VIS1, do_ddd, a, tcg_gen_vec_sub32_i64) +TRANS(FNORd, VIS1, do_ddd, a, tcg_gen_nor_i64) +TRANS(FANDNOTd, VIS1, do_ddd, a, tcg_gen_andc_i64) +TRANS(FXORd, VIS1, do_ddd, a, tcg_gen_xor_i64) +TRANS(FNANDd, VIS1, do_ddd, a, tcg_gen_nand_i64) +TRANS(FANDd, VIS1, do_ddd, a, tcg_gen_and_i64) +TRANS(FXNORd, VIS1, do_ddd, a, tcg_gen_eqv_i64) +TRANS(FORNOTd, VIS1, do_ddd, a, tcg_gen_orc_i64) +TRANS(FORd, VIS1, do_ddd, a, tcg_gen_or_i64) + #define CHECK_IU_FEATURE(dc, FEATURE) \ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ goto illegal_insn; @@ -5254,6 +5289,29 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x077: /* VIS I fornot2s */ case 0x07b: /* VIS I fornot1s */ case 0x07d: /* VIS I fors */ + case 0x050: /* VIS I fpadd16 */ + case 0x052: /* VIS I fpadd32 */ + case 0x054: /* VIS I fpsub16 */ + case 0x056: /* VIS I fpsub32 */ + case 0x062: /* VIS I fnor */ + case 0x064: /* VIS I fandnot2 */ + case 0x068: /* VIS I fandnot1 */ + case 0x06c: /* VIS I fxor */ + case 0x06e: /* VIS I fnand */ + case 0x070: /* VIS I fand */ + case 0x072: /* VIS I fxnor */ + case 0x076: /* VIS I fornot2 */ + case 0x07a: /* VIS I fornot1 */ + case 0x07c: /* VIS I for */ + case 0x031: /* VIS I fmul8x16 */ + case 0x033: /* VIS I fmul8x16au */ + case 0x035: /* VIS I fmul8x16al */ + case 0x036: /* VIS I fmul8sux16 */ + case 0x037: /* VIS I fmul8ulx16 */ + case 0x038: /* VIS I fmuld8sux16 */ + case 0x039: /* VIS I fmuld8ulx16 */ + case 0x04b: /* VIS I fpmerge */ + case 0x04d: /* VIS I fexpand */ g_assert_not_reached(); /* in decodetree */ case 0x020: /* VIS I fcmple16 */ CHECK_FPU_FEATURE(dc, VIS1); @@ -5311,34 +5369,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) gen_helper_fcmpeq32(cpu_dst, cpu_src1_64, cpu_src2_64); gen_store_gpr(dc, rd, cpu_dst); break; - case 0x031: /* VIS I fmul8x16 */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fmul8x16); - break; - case 0x033: /* VIS I fmul8x16au */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fmul8x16au); - break; - case 0x035: /* VIS I fmul8x16al */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fmul8x16al); - break; - case 0x036: /* VIS I fmul8sux16 */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fmul8sux16); - break; - case 0x037: /* VIS I fmul8ulx16 */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fmul8ulx16); - break; - case 0x038: /* VIS I fmuld8sux16 */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fmuld8sux16); - break; - case 0x039: /* VIS I fmuld8ulx16 */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fmuld8ulx16); - break; case 0x03a: /* VIS I fpack32 */ CHECK_FPU_FEATURE(dc, VIS1); gen_gsr_fop_DDD(dc, rd, rs1, rs2, gen_helper_fpack32); @@ -5365,34 +5395,10 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) CHECK_FPU_FEATURE(dc, VIS1); gen_gsr_fop_DDD(dc, rd, rs1, rs2, gen_faligndata); break; - case 0x04b: /* VIS I fpmerge */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fpmerge); - break; case 0x04c: /* VIS II bshuffle */ CHECK_FPU_FEATURE(dc, VIS2); gen_gsr_fop_DDD(dc, rd, rs1, rs2, gen_helper_bshuffle); break; - case 0x04d: /* VIS I fexpand */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fexpand); - break; - case 0x050: /* VIS I fpadd16 */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_vec_add16_i64); - break; - case 0x052: /* VIS I fpadd32 */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_vec_add32_i64); - break; - case 0x054: /* VIS I fpsub16 */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_vec_sub16_i64); - break; - case 0x056: /* VIS I fpsub32 */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_vec_add32_i64); - break; case 0x060: /* VIS I fzero */ CHECK_FPU_FEATURE(dc, VIS1); cpu_dst_64 = gen_dest_fpr_D(dc, rd); @@ -5405,46 +5411,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) tcg_gen_movi_i32(cpu_dst_32, 0); gen_store_fpr_F(dc, rd, cpu_dst_32); break; - case 0x062: /* VIS I fnor */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_nor_i64); - break; - case 0x064: /* VIS I fandnot2 */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_andc_i64); - break; - case 0x068: /* VIS I fandnot1 */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs2, rs1, tcg_gen_andc_i64); - break; - case 0x06c: /* VIS I fxor */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_xor_i64); - break; - case 0x06e: /* VIS I fnand */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_nand_i64); - break; - case 0x070: /* VIS I fand */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_and_i64); - break; - case 0x072: /* VIS I fxnor */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_eqv_i64); - break; - case 0x076: /* VIS I fornot2 */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_orc_i64); - break; - case 0x07a: /* VIS I fornot1 */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs2, rs1, tcg_gen_orc_i64); - break; - case 0x07c: /* VIS I for */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_or_i64); - break; case 0x07e: /* VIS I fone */ CHECK_FPU_FEATURE(dc, VIS1); cpu_dst_64 = gen_dest_fpr_D(dc, rd); From afb043448bf563b3720b55cf494fad943c0aad35 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 10 Oct 2023 14:05:16 -0700 Subject: [PATCH 086/974] target/sparc: Move PDIST to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 1 + target/sparc/translate.c | 41 +++++++++++++++++++++------------------ 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 0e29629b5c..42d740ad44 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -277,6 +277,7 @@ FABSd 10 ..... 110100 00000 0 0000 1010 ..... @r_r2 FMUL8ULx16 10 ..... 110110 ..... 0 0011 0111 ..... @r_r_r FMULD8SUx16 10 ..... 110110 ..... 0 0011 1000 ..... @r_r_r FMULD8ULx16 10 ..... 110110 ..... 0 0011 1001 ..... @r_r_r + PDIST 10 ..... 110110 ..... 0 0011 1110 ..... @r_r_r FPMERGE 10 ..... 110110 ..... 0 0100 1011 ..... @r_r_r FEXPAND 10 ..... 110110 ..... 0 0100 1101 ..... @r_r_r diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 35fdcfe4c1..40ef395d14 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -72,6 +72,7 @@ # define gen_helper_fmuld8sux16 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fmuld8ulx16 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fpmerge ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_pdist ({ qemu_build_not_reached(); NULL; }) # define FSR_LDXFSR_MASK 0 # define FSR_LDXFSR_OLDMASK 0 # define MAXTL_MASK 0 @@ -1680,21 +1681,6 @@ static void gen_gsr_fop_DDD(DisasContext *dc, int rd, int rs1, int rs2, gen_store_fpr_D(dc, rd, dst); } - -static void gen_ne_fop_DDDD(DisasContext *dc, int rd, int rs1, int rs2, - void (*gen)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64)) -{ - TCGv_i64 dst, src0, src1, src2; - - src1 = gen_load_fpr_D(dc, rs1); - src2 = gen_load_fpr_D(dc, rs2); - src0 = gen_load_fpr_D(dc, rd); - dst = gen_dest_fpr_D(dc, rd); - - gen(dst, src0, src1, src2); - - gen_store_fpr_D(dc, rd, dst); -} #endif static void gen_fop_QQ(DisasContext *dc, int rd, int rs, @@ -4903,6 +4889,26 @@ TRANS(FXNORd, VIS1, do_ddd, a, tcg_gen_eqv_i64) TRANS(FORNOTd, VIS1, do_ddd, a, tcg_gen_orc_i64) TRANS(FORd, VIS1, do_ddd, a, tcg_gen_or_i64) +static bool do_dddd(DisasContext *dc, arg_r_r_r *a, + void (*func)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64)) +{ + TCGv_i64 dst, src0, src1, src2; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + dst = gen_dest_fpr_D(dc, a->rd); + src0 = gen_load_fpr_D(dc, a->rd); + src1 = gen_load_fpr_D(dc, a->rs1); + src2 = gen_load_fpr_D(dc, a->rs2); + func(dst, src0, src1, src2); + gen_store_fpr_D(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(PDIST, VIS1, do_dddd, a, gen_helper_pdist) + #define CHECK_IU_FEATURE(dc, FEATURE) \ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ goto illegal_insn; @@ -5312,6 +5318,7 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x039: /* VIS I fmuld8ulx16 */ case 0x04b: /* VIS I fpmerge */ case 0x04d: /* VIS I fexpand */ + case 0x03e: /* VIS I pdist */ g_assert_not_reached(); /* in decodetree */ case 0x020: /* VIS I fcmple16 */ CHECK_FPU_FEATURE(dc, VIS1); @@ -5387,10 +5394,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) gen_helper_fpackfix(cpu_dst_32, cpu_gsr, cpu_src1_64); gen_store_fpr_F(dc, rd, cpu_dst_32); break; - case 0x03e: /* VIS I pdist */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDDD(dc, rd, rs1, rs2, gen_helper_pdist); - break; case 0x048: /* VIS I faligndata */ CHECK_FPU_FEATURE(dc, VIS1); gen_gsr_fop_DDD(dc, rd, rs1, rs2, gen_faligndata); From 4b6edc0a2799300e4958a94534592de1c0671093 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 10 Oct 2023 14:25:47 -0700 Subject: [PATCH 087/974] target/sparc: Move gen_gsr_fop_DDD insns to decodetree Move FPACK32, FALIGNDATA, BSHUFFLE. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 3 ++ target/sparc/translate.c | 101 ++++++++++++++++++++------------------ 2 files changed, 55 insertions(+), 49 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 42d740ad44..bc449023dd 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -277,9 +277,12 @@ FABSd 10 ..... 110100 00000 0 0000 1010 ..... @r_r2 FMUL8ULx16 10 ..... 110110 ..... 0 0011 0111 ..... @r_r_r FMULD8SUx16 10 ..... 110110 ..... 0 0011 1000 ..... @r_r_r FMULD8ULx16 10 ..... 110110 ..... 0 0011 1001 ..... @r_r_r + FPACK32 10 ..... 110110 ..... 0 0011 1010 ..... @r_r_r PDIST 10 ..... 110110 ..... 0 0011 1110 ..... @r_r_r + FALIGNDATAg 10 ..... 110110 ..... 0 0100 1000 ..... @r_r_r FPMERGE 10 ..... 110110 ..... 0 0100 1011 ..... @r_r_r + BSHUFFLE 10 ..... 110110 ..... 0 0100 1100 ..... @r_r_r FEXPAND 10 ..... 110110 ..... 0 0100 1101 ..... @r_r_r FSRCd 10 ..... 110110 ..... 0 0111 0100 00000 @r_r1 # FSRC1d diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 40ef395d14..197c0bea54 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -752,6 +752,51 @@ static void gen_op_array32(TCGv dst, TCGv src1, TCGv src2) tcg_gen_shli_tl(dst, dst, 2); } +static void gen_op_fpack32(TCGv_i64 dst, TCGv_i64 src1, TCGv_i64 src2) +{ +#ifdef TARGET_SPARC64 + gen_helper_fpack32(dst, cpu_gsr, src1, src2); +#else + g_assert_not_reached(); +#endif +} + +static void gen_op_faligndata(TCGv_i64 dst, TCGv_i64 s1, TCGv_i64 s2) +{ +#ifdef TARGET_SPARC64 + TCGv t1, t2, shift; + + t1 = tcg_temp_new(); + t2 = tcg_temp_new(); + shift = tcg_temp_new(); + + tcg_gen_andi_tl(shift, cpu_gsr, 7); + tcg_gen_shli_tl(shift, shift, 3); + tcg_gen_shl_tl(t1, s1, shift); + + /* + * A shift of 64 does not produce 0 in TCG. Divide this into a + * shift of (up to 63) followed by a constant shift of 1. + */ + tcg_gen_xori_tl(shift, shift, 63); + tcg_gen_shr_tl(t2, s2, shift); + tcg_gen_shri_tl(t2, t2, 1); + + tcg_gen_or_tl(dst, t1, t2); +#else + g_assert_not_reached(); +#endif +} + +static void gen_op_bshuffle(TCGv_i64 dst, TCGv_i64 src1, TCGv_i64 src2) +{ +#ifdef TARGET_SPARC64 + gen_helper_bshuffle(dst, cpu_gsr, src1, src2); +#else + g_assert_not_reached(); +#endif +} + // 1 static void gen_op_eval_ba(TCGv dst) { @@ -1667,22 +1712,6 @@ static void gen_fop_DDD(DisasContext *dc, int rd, int rs1, int rs2, gen_store_fpr_D(dc, rd, dst); } -#ifdef TARGET_SPARC64 -static void gen_gsr_fop_DDD(DisasContext *dc, int rd, int rs1, int rs2, - void (*gen)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64)) -{ - TCGv_i64 dst, src1, src2; - - src1 = gen_load_fpr_D(dc, rs1); - src2 = gen_load_fpr_D(dc, rs2); - dst = gen_dest_fpr_D(dc, rd); - - gen(dst, cpu_gsr, src1, src2); - - gen_store_fpr_D(dc, rd, dst); -} -#endif - static void gen_fop_QQ(DisasContext *dc, int rd, int rs, void (*gen)(TCGv_ptr)) { @@ -2720,27 +2749,6 @@ static void gen_load_trap_state_at_tl(TCGv_ptr r_tsptr) tcg_gen_add_ptr(r_tsptr, r_tsptr, r_tl_tmp); } } - -static void gen_faligndata(TCGv dst, TCGv gsr, TCGv s1, TCGv s2) -{ - TCGv t1, t2, shift; - - t1 = tcg_temp_new(); - t2 = tcg_temp_new(); - shift = tcg_temp_new(); - - tcg_gen_andi_tl(shift, gsr, 7); - tcg_gen_shli_tl(shift, shift, 3); - tcg_gen_shl_tl(t1, s1, shift); - - /* A shift of 64 does not produce 0 in TCG. Divide this into a - shift of (up to 63) followed by a constant shift of 1. */ - tcg_gen_xori_tl(shift, shift, 63); - tcg_gen_shr_tl(t2, s2, shift); - tcg_gen_shri_tl(t2, t2, 1); - - tcg_gen_or_tl(dst, t1, t2); -} #endif static int extract_dfpreg(DisasContext *dc, int x) @@ -4889,6 +4897,10 @@ TRANS(FXNORd, VIS1, do_ddd, a, tcg_gen_eqv_i64) TRANS(FORNOTd, VIS1, do_ddd, a, tcg_gen_orc_i64) TRANS(FORd, VIS1, do_ddd, a, tcg_gen_or_i64) +TRANS(FPACK32, VIS1, do_ddd, a, gen_op_fpack32) +TRANS(FALIGNDATAg, VIS1, do_ddd, a, gen_op_faligndata) +TRANS(BSHUFFLE, VIS2, do_ddd, a, gen_op_bshuffle) + static bool do_dddd(DisasContext *dc, arg_r_r_r *a, void (*func)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64)) { @@ -5319,6 +5331,9 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x04b: /* VIS I fpmerge */ case 0x04d: /* VIS I fexpand */ case 0x03e: /* VIS I pdist */ + case 0x03a: /* VIS I fpack32 */ + case 0x048: /* VIS I faligndata */ + case 0x04c: /* VIS II bshuffle */ g_assert_not_reached(); /* in decodetree */ case 0x020: /* VIS I fcmple16 */ CHECK_FPU_FEATURE(dc, VIS1); @@ -5376,10 +5391,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) gen_helper_fcmpeq32(cpu_dst, cpu_src1_64, cpu_src2_64); gen_store_gpr(dc, rd, cpu_dst); break; - case 0x03a: /* VIS I fpack32 */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_gsr_fop_DDD(dc, rd, rs1, rs2, gen_helper_fpack32); - break; case 0x03b: /* VIS I fpack16 */ CHECK_FPU_FEATURE(dc, VIS1); cpu_src1_64 = gen_load_fpr_D(dc, rs2); @@ -5394,14 +5405,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) gen_helper_fpackfix(cpu_dst_32, cpu_gsr, cpu_src1_64); gen_store_fpr_F(dc, rd, cpu_dst_32); break; - case 0x048: /* VIS I faligndata */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_gsr_fop_DDD(dc, rd, rs1, rs2, gen_faligndata); - break; - case 0x04c: /* VIS II bshuffle */ - CHECK_FPU_FEATURE(dc, VIS2); - gen_gsr_fop_DDD(dc, rd, rs1, rs2, gen_helper_bshuffle); - break; case 0x060: /* VIS I fzero */ CHECK_FPU_FEATURE(dc, VIS1); cpu_dst_64 = gen_dest_fpr_D(dc, rd); From 119cb94f69e2180f1a8685b53a0659f195bdd90c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 10 Oct 2023 14:44:32 -0700 Subject: [PATCH 088/974] target/sparc: Move gen_fop_FF insns to decodetree Move FSQRTs, FiTOs, FsTOi. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 3 +++ target/sparc/translate.c | 47 ++++++++++++++++++++------------------- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index bc449023dd..85464285b7 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -245,6 +245,9 @@ FNEGs 10 ..... 110100 00000 0 0000 0101 ..... @r_r2 FNEGd 10 ..... 110100 00000 0 0000 0110 ..... @r_r2 FABSs 10 ..... 110100 00000 0 0000 1001 ..... @r_r2 FABSd 10 ..... 110100 00000 0 0000 1010 ..... @r_r2 +FSQRTs 10 ..... 110100 00000 0 0010 1001 ..... @r_r2 +FiTOs 10 ..... 110100 00000 0 1100 0100 ..... @r_r2 +FsTOi 10 ..... 110100 00000 0 1101 0001 ..... @r_r2 { [ diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 197c0bea54..13932e087d 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -1654,20 +1654,6 @@ static int gen_trap_ifnofpu(DisasContext *dc) return 0; } -static void gen_fop_FF(DisasContext *dc, int rd, int rs, - void (*gen)(TCGv_i32, TCGv_ptr, TCGv_i32)) -{ - TCGv_i32 dst, src; - - src = gen_load_fpr_F(dc, rs); - dst = gen_dest_fpr_F(dc); - - gen(dst, tcg_env, src); - gen_helper_check_ieee_exceptions(cpu_fsr, tcg_env); - - gen_store_fpr_F(dc, rd, dst); -} - static void gen_fop_FFF(DisasContext *dc, int rd, int rs1, int rs2, void (*gen)(TCGv_i32, TCGv_ptr, TCGv_i32, TCGv_i32)) { @@ -4806,6 +4792,27 @@ TRANS(FABSs, ALL, do_ff, a, gen_op_fabss) TRANS(FSRCs, VIS1, do_ff, a, tcg_gen_mov_i32) TRANS(FNOTs, VIS1, do_ff, a, tcg_gen_not_i32) +static bool do_env_ff(DisasContext *dc, arg_r_r *a, + void (*func)(TCGv_i32, TCGv_env, TCGv_i32)) +{ + TCGv_i32 tmp; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + gen_op_clear_ieee_excp_and_FTT(); + tmp = gen_load_fpr_F(dc, a->rs); + func(tmp, tcg_env, tmp); + gen_helper_check_ieee_exceptions(cpu_fsr, tcg_env); + gen_store_fpr_F(dc, a->rd, tmp); + return advance_pc(dc); +} + +TRANS(FSQRTs, ALL, do_env_ff, a, gen_helper_fsqrts) +TRANS(FiTOs, ALL, do_env_ff, a, gen_helper_fitos) +TRANS(FsTOi, ALL, do_env_ff, a, gen_helper_fstoi) + static bool do_dd(DisasContext *dc, arg_r_r *a, void (*func)(TCGv_i64, TCGv_i64)) { @@ -4967,10 +4974,10 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x2: /* V9 fmovd */ case 0x6: /* V9 fnegd */ case 0xa: /* V9 fabsd */ - g_assert_not_reached(); /* in decodetree */ case 0x29: /* fsqrts */ - gen_fop_FF(dc, rd, rs2, gen_helper_fsqrts); - break; + case 0xc4: /* fitos */ + case 0xd1: /* fstoi */ + g_assert_not_reached(); /* in decodetree */ case 0x2a: /* fsqrtd */ gen_fop_DD(dc, rd, rs2, gen_helper_fsqrtd); break; @@ -5026,9 +5033,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) CHECK_FPU_FEATURE(dc, FLOAT128); gen_fop_QDD(dc, rd, rs1, rs2, gen_helper_fdmulq); break; - case 0xc4: /* fitos */ - gen_fop_FF(dc, rd, rs2, gen_helper_fitos); - break; case 0xc6: /* fdtos */ gen_fop_FD(dc, rd, rs2, gen_helper_fdtos); break; @@ -5058,9 +5062,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) CHECK_FPU_FEATURE(dc, FLOAT128); gen_ne_fop_QD(dc, rd, rs2, gen_helper_fdtoq); break; - case 0xd1: /* fstoi */ - gen_fop_FF(dc, rd, rs2, gen_helper_fstoi); - break; case 0xd2: /* fdtoi */ gen_fop_FD(dc, rd, rs2, gen_helper_fdtoi); break; From 8aa418b3ef1b261757408642385dd8fd62bffb36 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 10 Oct 2023 14:56:49 -0700 Subject: [PATCH 089/974] target/sparc: Move gen_fop_DD insns to decodetree Move FSQRTd, FxTOd, FdTOx. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 3 +++ target/sparc/translate.c | 50 +++++++++++++++++++++------------------ 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 85464285b7..1d766fab21 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -246,6 +246,9 @@ FNEGd 10 ..... 110100 00000 0 0000 0110 ..... @r_r2 FABSs 10 ..... 110100 00000 0 0000 1001 ..... @r_r2 FABSd 10 ..... 110100 00000 0 0000 1010 ..... @r_r2 FSQRTs 10 ..... 110100 00000 0 0010 1001 ..... @r_r2 +FSQRTd 10 ..... 110100 00000 0 0010 1010 ..... @r_r2 +FdTOx 10 ..... 110100 00000 0 1000 0010 ..... @r_r2 +FxTOd 10 ..... 110100 00000 0 1000 1000 ..... @r_r2 FiTOs 10 ..... 110100 00000 0 1100 0100 ..... @r_r2 FsTOi 10 ..... 110100 00000 0 1101 0001 ..... @r_r2 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 13932e087d..307fbc4628 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -63,6 +63,7 @@ # define gen_helper_write_softint(E, S) qemu_build_not_reached() # define gen_helper_wrpil(E, S) qemu_build_not_reached() # define gen_helper_wrpstate(E, S) qemu_build_not_reached() +# define gen_helper_fdtox ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fexpand ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fmul8sux16 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fmul8ulx16 ({ qemu_build_not_reached(); NULL; }) @@ -72,6 +73,7 @@ # define gen_helper_fmuld8sux16 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fmuld8ulx16 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fpmerge ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fxtod ({ qemu_build_not_reached(); NULL; }) # define gen_helper_pdist ({ qemu_build_not_reached(); NULL; }) # define FSR_LDXFSR_MASK 0 # define FSR_LDXFSR_OLDMASK 0 @@ -1669,20 +1671,6 @@ static void gen_fop_FFF(DisasContext *dc, int rd, int rs1, int rs2, gen_store_fpr_F(dc, rd, dst); } -static void gen_fop_DD(DisasContext *dc, int rd, int rs, - void (*gen)(TCGv_i64, TCGv_ptr, TCGv_i64)) -{ - TCGv_i64 dst, src; - - src = gen_load_fpr_D(dc, rs); - dst = gen_dest_fpr_D(dc, rd); - - gen(dst, tcg_env, src); - gen_helper_check_ieee_exceptions(cpu_fsr, tcg_env); - - gen_store_fpr_D(dc, rd, dst); -} - static void gen_fop_DDD(DisasContext *dc, int rd, int rs1, int rs2, void (*gen)(TCGv_i64, TCGv_ptr, TCGv_i64, TCGv_i64)) { @@ -4835,6 +4823,28 @@ TRANS(FABSd, 64, do_dd, a, gen_op_fabsd) TRANS(FSRCd, VIS1, do_dd, a, tcg_gen_mov_i64) TRANS(FNOTd, VIS1, do_dd, a, tcg_gen_not_i64) +static bool do_env_dd(DisasContext *dc, arg_r_r *a, + void (*func)(TCGv_i64, TCGv_env, TCGv_i64)) +{ + TCGv_i64 dst, src; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + gen_op_clear_ieee_excp_and_FTT(); + dst = gen_dest_fpr_D(dc, a->rd); + src = gen_load_fpr_D(dc, a->rs); + func(dst, tcg_env, src); + gen_helper_check_ieee_exceptions(cpu_fsr, tcg_env); + gen_store_fpr_D(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(FSQRTd, ALL, do_env_dd, a, gen_helper_fsqrtd) +TRANS(FxTOd, 64, do_env_dd, a, gen_helper_fxtod) +TRANS(FdTOx, 64, do_env_dd, a, gen_helper_fdtox) + static bool do_fff(DisasContext *dc, arg_r_r_r *a, void (*func)(TCGv_i32, TCGv_i32, TCGv_i32)) { @@ -4977,10 +4987,10 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x29: /* fsqrts */ case 0xc4: /* fitos */ case 0xd1: /* fstoi */ - g_assert_not_reached(); /* in decodetree */ case 0x2a: /* fsqrtd */ - gen_fop_DD(dc, rd, rs2, gen_helper_fsqrtd); - break; + case 0x82: /* V9 fdtox */ + case 0x88: /* V9 fxtod */ + g_assert_not_reached(); /* in decodetree */ case 0x2b: /* fsqrtq */ CHECK_FPU_FEATURE(dc, FLOAT128); gen_fop_QQ(dc, rd, rs2, gen_helper_fsqrtq); @@ -5085,9 +5095,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x81: /* V9 fstox */ gen_fop_DF(dc, rd, rs2, gen_helper_fstox); break; - case 0x82: /* V9 fdtox */ - gen_fop_DD(dc, rd, rs2, gen_helper_fdtox); - break; case 0x83: /* V9 fqtox */ CHECK_FPU_FEATURE(dc, FLOAT128); gen_fop_DQ(dc, rd, rs2, gen_helper_fqtox); @@ -5095,9 +5102,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x84: /* V9 fxtos */ gen_fop_FD(dc, rd, rs2, gen_helper_fxtos); break; - case 0x88: /* V9 fxtod */ - gen_fop_DD(dc, rd, rs2, gen_helper_fxtod); - break; case 0x8c: /* V9 fxtoq */ CHECK_FPU_FEATURE(dc, FLOAT128); gen_ne_fop_QD(dc, rd, rs2, gen_helper_fxtoq); From c995216bab4d5ef573d89659fc7704aacd9d404a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 10 Oct 2023 15:07:48 -0700 Subject: [PATCH 090/974] target/sparc: Move FSQRTq to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 1 + target/sparc/translate.c | 38 ++++++++++++++++++++++---------------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 1d766fab21..4cb250265d 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -247,6 +247,7 @@ FABSs 10 ..... 110100 00000 0 0000 1001 ..... @r_r2 FABSd 10 ..... 110100 00000 0 0000 1010 ..... @r_r2 FSQRTs 10 ..... 110100 00000 0 0010 1001 ..... @r_r2 FSQRTd 10 ..... 110100 00000 0 0010 1010 ..... @r_r2 +FSQRTq 10 ..... 110100 00000 0 0010 1011 ..... @r_r2 FdTOx 10 ..... 110100 00000 0 1000 0010 ..... @r_r2 FxTOd 10 ..... 110100 00000 0 1000 1000 ..... @r_r2 FiTOs 10 ..... 110100 00000 0 1100 0100 ..... @r_r2 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 307fbc4628..93efdf3383 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -1686,18 +1686,6 @@ static void gen_fop_DDD(DisasContext *dc, int rd, int rs1, int rs2, gen_store_fpr_D(dc, rd, dst); } -static void gen_fop_QQ(DisasContext *dc, int rd, int rs, - void (*gen)(TCGv_ptr)) -{ - gen_op_load_fpr_QT1(QFPREG(rs)); - - gen(tcg_env); - gen_helper_check_ieee_exceptions(cpu_fsr, tcg_env); - - gen_op_store_QT0_fpr(QFPREG(rd)); - gen_update_fprs_dirty(dc, QFPREG(rd)); -} - #ifdef TARGET_SPARC64 static void gen_ne_fop_QQ(DisasContext *dc, int rd, int rs, void (*gen)(TCGv_ptr)) @@ -4845,6 +4833,27 @@ TRANS(FSQRTd, ALL, do_env_dd, a, gen_helper_fsqrtd) TRANS(FxTOd, 64, do_env_dd, a, gen_helper_fxtod) TRANS(FdTOx, 64, do_env_dd, a, gen_helper_fdtox) +static bool do_env_qq(DisasContext *dc, arg_r_r *a, + void (*func)(TCGv_env)) +{ + if (gen_trap_ifnofpu(dc)) { + return true; + } + if (gen_trap_float128(dc)) { + return true; + } + + gen_op_clear_ieee_excp_and_FTT(); + gen_op_load_fpr_QT1(QFPREG(a->rs)); + func(tcg_env); + gen_helper_check_ieee_exceptions(cpu_fsr, tcg_env); + gen_op_store_QT0_fpr(QFPREG(a->rd)); + gen_update_fprs_dirty(dc, QFPREG(a->rd)); + return advance_pc(dc); +} + +TRANS(FSQRTq, ALL, do_env_qq, a, gen_helper_fsqrtq) + static bool do_fff(DisasContext *dc, arg_r_r_r *a, void (*func)(TCGv_i32, TCGv_i32, TCGv_i32)) { @@ -4990,11 +4999,8 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x2a: /* fsqrtd */ case 0x82: /* V9 fdtox */ case 0x88: /* V9 fxtod */ - g_assert_not_reached(); /* in decodetree */ case 0x2b: /* fsqrtq */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_fop_QQ(dc, rd, rs2, gen_helper_fsqrtq); - break; + g_assert_not_reached(); /* in decodetree */ case 0x41: /* fadds */ gen_fop_FFF(dc, rd, rs1, rs2, gen_helper_fadds); break; From c1514961e6bca738eab6a72559267a30e15aa816 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Oct 2023 15:43:18 -0700 Subject: [PATCH 091/974] target/sparc: Move gen_fop_FFF insns to decodetree Move FADDs, FSUBs, FMULs, FDIVs. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 4 +++ target/sparc/translate.c | 54 +++++++++++++++++++-------------------- 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 4cb250265d..09444e313f 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -248,6 +248,10 @@ FABSd 10 ..... 110100 00000 0 0000 1010 ..... @r_r2 FSQRTs 10 ..... 110100 00000 0 0010 1001 ..... @r_r2 FSQRTd 10 ..... 110100 00000 0 0010 1010 ..... @r_r2 FSQRTq 10 ..... 110100 00000 0 0010 1011 ..... @r_r2 +FADDs 10 ..... 110100 ..... 0 0100 0001 ..... @r_r_r +FSUBs 10 ..... 110100 ..... 0 0100 0101 ..... @r_r_r +FMULs 10 ..... 110100 ..... 0 0100 1001 ..... @r_r_r +FDIVs 10 ..... 110100 ..... 0 0100 1101 ..... @r_r_r FdTOx 10 ..... 110100 00000 0 1000 0010 ..... @r_r2 FxTOd 10 ..... 110100 00000 0 1000 1000 ..... @r_r2 FiTOs 10 ..... 110100 00000 0 1100 0100 ..... @r_r2 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 93efdf3383..d2205f7e7d 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -1656,21 +1656,6 @@ static int gen_trap_ifnofpu(DisasContext *dc) return 0; } -static void gen_fop_FFF(DisasContext *dc, int rd, int rs1, int rs2, - void (*gen)(TCGv_i32, TCGv_ptr, TCGv_i32, TCGv_i32)) -{ - TCGv_i32 dst, src1, src2; - - src1 = gen_load_fpr_F(dc, rs1); - src2 = gen_load_fpr_F(dc, rs2); - dst = gen_dest_fpr_F(dc); - - gen(dst, tcg_env, src1, src2); - gen_helper_check_ieee_exceptions(cpu_fsr, tcg_env); - - gen_store_fpr_F(dc, rd, dst); -} - static void gen_fop_DDD(DisasContext *dc, int rd, int rs1, int rs2, void (*gen)(TCGv_i64, TCGv_ptr, TCGv_i64, TCGv_i64)) { @@ -4883,6 +4868,29 @@ TRANS(FXNORs, VIS1, do_fff, a, tcg_gen_eqv_i32) TRANS(FORNOTs, VIS1, do_fff, a, tcg_gen_orc_i32) TRANS(FORs, VIS1, do_fff, a, tcg_gen_or_i32) +static bool do_env_fff(DisasContext *dc, arg_r_r_r *a, + void (*func)(TCGv_i32, TCGv_env, TCGv_i32, TCGv_i32)) +{ + TCGv_i32 src1, src2; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + gen_op_clear_ieee_excp_and_FTT(); + src1 = gen_load_fpr_F(dc, a->rs1); + src2 = gen_load_fpr_F(dc, a->rs2); + func(src1, tcg_env, src1, src2); + gen_helper_check_ieee_exceptions(cpu_fsr, tcg_env); + gen_store_fpr_F(dc, a->rd, src1); + return advance_pc(dc); +} + +TRANS(FADDs, ALL, do_env_fff, a, gen_helper_fadds) +TRANS(FSUBs, ALL, do_env_fff, a, gen_helper_fsubs) +TRANS(FMULs, ALL, do_env_fff, a, gen_helper_fmuls) +TRANS(FDIVs, ALL, do_env_fff, a, gen_helper_fdivs) + static bool do_ddd(DisasContext *dc, arg_r_r_r *a, void (*func)(TCGv_i64, TCGv_i64, TCGv_i64)) { @@ -5000,10 +5008,11 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x82: /* V9 fdtox */ case 0x88: /* V9 fxtod */ case 0x2b: /* fsqrtq */ - g_assert_not_reached(); /* in decodetree */ case 0x41: /* fadds */ - gen_fop_FFF(dc, rd, rs1, rs2, gen_helper_fadds); - break; + case 0x45: /* fsubs */ + case 0x49: /* fmuls */ + case 0x4d: /* fdivs */ + g_assert_not_reached(); /* in decodetree */ case 0x42: /* faddd */ gen_fop_DDD(dc, rd, rs1, rs2, gen_helper_faddd); break; @@ -5011,9 +5020,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) CHECK_FPU_FEATURE(dc, FLOAT128); gen_fop_QQQ(dc, rd, rs1, rs2, gen_helper_faddq); break; - case 0x45: /* fsubs */ - gen_fop_FFF(dc, rd, rs1, rs2, gen_helper_fsubs); - break; case 0x46: /* fsubd */ gen_fop_DDD(dc, rd, rs1, rs2, gen_helper_fsubd); break; @@ -5021,9 +5027,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) CHECK_FPU_FEATURE(dc, FLOAT128); gen_fop_QQQ(dc, rd, rs1, rs2, gen_helper_fsubq); break; - case 0x49: /* fmuls */ - gen_fop_FFF(dc, rd, rs1, rs2, gen_helper_fmuls); - break; case 0x4a: /* fmuld */ gen_fop_DDD(dc, rd, rs1, rs2, gen_helper_fmuld); break; @@ -5031,9 +5034,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) CHECK_FPU_FEATURE(dc, FLOAT128); gen_fop_QQQ(dc, rd, rs1, rs2, gen_helper_fmulq); break; - case 0x4d: /* fdivs */ - gen_fop_FFF(dc, rd, rs1, rs2, gen_helper_fdivs); - break; case 0x4e: /* fdivd */ gen_fop_DDD(dc, rd, rs1, rs2, gen_helper_fdivd); break; From f2a59b0ad7d2e3252e6edaf0999a5a1ae48da2a0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Oct 2023 15:51:13 -0700 Subject: [PATCH 092/974] target/sparc: Move gen_fop_DDD insns to decodetree Move FADDd, FSUBd, FMULd, FDIVd. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 4 +++ target/sparc/translate.c | 55 ++++++++++++++++++++------------------- 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 09444e313f..a429c04980 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -249,9 +249,13 @@ FSQRTs 10 ..... 110100 00000 0 0010 1001 ..... @r_r2 FSQRTd 10 ..... 110100 00000 0 0010 1010 ..... @r_r2 FSQRTq 10 ..... 110100 00000 0 0010 1011 ..... @r_r2 FADDs 10 ..... 110100 ..... 0 0100 0001 ..... @r_r_r +FADDd 10 ..... 110100 ..... 0 0100 0010 ..... @r_r_r FSUBs 10 ..... 110100 ..... 0 0100 0101 ..... @r_r_r +FSUBd 10 ..... 110100 ..... 0 0100 0110 ..... @r_r_r FMULs 10 ..... 110100 ..... 0 0100 1001 ..... @r_r_r +FMULd 10 ..... 110100 ..... 0 0100 1010 ..... @r_r_r FDIVs 10 ..... 110100 ..... 0 0100 1101 ..... @r_r_r +FDIVd 10 ..... 110100 ..... 0 0100 1110 ..... @r_r_r FdTOx 10 ..... 110100 00000 0 1000 0010 ..... @r_r2 FxTOd 10 ..... 110100 00000 0 1000 1000 ..... @r_r2 FiTOs 10 ..... 110100 00000 0 1100 0100 ..... @r_r2 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index d2205f7e7d..e2bcb3c7b1 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -1656,21 +1656,6 @@ static int gen_trap_ifnofpu(DisasContext *dc) return 0; } -static void gen_fop_DDD(DisasContext *dc, int rd, int rs1, int rs2, - void (*gen)(TCGv_i64, TCGv_ptr, TCGv_i64, TCGv_i64)) -{ - TCGv_i64 dst, src1, src2; - - src1 = gen_load_fpr_D(dc, rs1); - src2 = gen_load_fpr_D(dc, rs2); - dst = gen_dest_fpr_D(dc, rd); - - gen(dst, tcg_env, src1, src2); - gen_helper_check_ieee_exceptions(cpu_fsr, tcg_env); - - gen_store_fpr_D(dc, rd, dst); -} - #ifdef TARGET_SPARC64 static void gen_ne_fop_QQ(DisasContext *dc, int rd, int rs, void (*gen)(TCGv_ptr)) @@ -4935,6 +4920,30 @@ TRANS(FPACK32, VIS1, do_ddd, a, gen_op_fpack32) TRANS(FALIGNDATAg, VIS1, do_ddd, a, gen_op_faligndata) TRANS(BSHUFFLE, VIS2, do_ddd, a, gen_op_bshuffle) +static bool do_env_ddd(DisasContext *dc, arg_r_r_r *a, + void (*func)(TCGv_i64, TCGv_env, TCGv_i64, TCGv_i64)) +{ + TCGv_i64 dst, src1, src2; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + gen_op_clear_ieee_excp_and_FTT(); + dst = gen_dest_fpr_D(dc, a->rd); + src1 = gen_load_fpr_D(dc, a->rs1); + src2 = gen_load_fpr_D(dc, a->rs2); + func(dst, tcg_env, src1, src2); + gen_helper_check_ieee_exceptions(cpu_fsr, tcg_env); + gen_store_fpr_D(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(FADDd, ALL, do_env_ddd, a, gen_helper_faddd) +TRANS(FSUBd, ALL, do_env_ddd, a, gen_helper_fsubd) +TRANS(FMULd, ALL, do_env_ddd, a, gen_helper_fmuld) +TRANS(FDIVd, ALL, do_env_ddd, a, gen_helper_fdivd) + static bool do_dddd(DisasContext *dc, arg_r_r_r *a, void (*func)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64)) { @@ -5012,31 +5021,23 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x45: /* fsubs */ case 0x49: /* fmuls */ case 0x4d: /* fdivs */ - g_assert_not_reached(); /* in decodetree */ case 0x42: /* faddd */ - gen_fop_DDD(dc, rd, rs1, rs2, gen_helper_faddd); - break; + case 0x46: /* fsubd */ + case 0x4a: /* fmuld */ + case 0x4e: /* fdivd */ + g_assert_not_reached(); /* in decodetree */ case 0x43: /* faddq */ CHECK_FPU_FEATURE(dc, FLOAT128); gen_fop_QQQ(dc, rd, rs1, rs2, gen_helper_faddq); break; - case 0x46: /* fsubd */ - gen_fop_DDD(dc, rd, rs1, rs2, gen_helper_fsubd); - break; case 0x47: /* fsubq */ CHECK_FPU_FEATURE(dc, FLOAT128); gen_fop_QQQ(dc, rd, rs1, rs2, gen_helper_fsubq); break; - case 0x4a: /* fmuld */ - gen_fop_DDD(dc, rd, rs1, rs2, gen_helper_fmuld); - break; case 0x4b: /* fmulq */ CHECK_FPU_FEATURE(dc, FLOAT128); gen_fop_QQQ(dc, rd, rs1, rs2, gen_helper_fmulq); break; - case 0x4e: /* fdivd */ - gen_fop_DDD(dc, rd, rs1, rs2, gen_helper_fdivd); - break; case 0x4f: /* fdivq */ CHECK_FPU_FEATURE(dc, FLOAT128); gen_fop_QQQ(dc, rd, rs1, rs2, gen_helper_fdivq); From a405623991f4dc8ca9298da89453033f910d9efc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Oct 2023 15:59:28 -0700 Subject: [PATCH 093/974] target/sparc: Move gen_fop_QQQ insns to decodetree Move FADDq, FSUBq, FMULq, FDIVq. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 4 +++ target/sparc/translate.c | 52 +++++++++++++++++++-------------------- 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index a429c04980..f18fd99476 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -250,12 +250,16 @@ FSQRTd 10 ..... 110100 00000 0 0010 1010 ..... @r_r2 FSQRTq 10 ..... 110100 00000 0 0010 1011 ..... @r_r2 FADDs 10 ..... 110100 ..... 0 0100 0001 ..... @r_r_r FADDd 10 ..... 110100 ..... 0 0100 0010 ..... @r_r_r +FADDq 10 ..... 110100 ..... 0 0100 0011 ..... @r_r_r FSUBs 10 ..... 110100 ..... 0 0100 0101 ..... @r_r_r FSUBd 10 ..... 110100 ..... 0 0100 0110 ..... @r_r_r +FSUBq 10 ..... 110100 ..... 0 0100 0111 ..... @r_r_r FMULs 10 ..... 110100 ..... 0 0100 1001 ..... @r_r_r FMULd 10 ..... 110100 ..... 0 0100 1010 ..... @r_r_r +FMULq 10 ..... 110100 ..... 0 0100 1011 ..... @r_r_r FDIVs 10 ..... 110100 ..... 0 0100 1101 ..... @r_r_r FDIVd 10 ..... 110100 ..... 0 0100 1110 ..... @r_r_r +FDIVq 10 ..... 110100 ..... 0 0100 1111 ..... @r_r_r FdTOx 10 ..... 110100 00000 0 1000 0010 ..... @r_r2 FxTOd 10 ..... 110100 00000 0 1000 1000 ..... @r_r2 FiTOs 10 ..... 110100 00000 0 1100 0100 ..... @r_r2 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index e2bcb3c7b1..94ad75b897 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -1669,19 +1669,6 @@ static void gen_ne_fop_QQ(DisasContext *dc, int rd, int rs, } #endif -static void gen_fop_QQQ(DisasContext *dc, int rd, int rs1, int rs2, - void (*gen)(TCGv_ptr)) -{ - gen_op_load_fpr_QT0(QFPREG(rs1)); - gen_op_load_fpr_QT1(QFPREG(rs2)); - - gen(tcg_env); - gen_helper_check_ieee_exceptions(cpu_fsr, tcg_env); - - gen_op_store_QT0_fpr(QFPREG(rd)); - gen_update_fprs_dirty(dc, QFPREG(rd)); -} - static void gen_fop_DFF(DisasContext *dc, int rd, int rs1, int rs2, void (*gen)(TCGv_i64, TCGv_ptr, TCGv_i32, TCGv_i32)) { @@ -4964,6 +4951,31 @@ static bool do_dddd(DisasContext *dc, arg_r_r_r *a, TRANS(PDIST, VIS1, do_dddd, a, gen_helper_pdist) +static bool do_env_qqq(DisasContext *dc, arg_r_r_r *a, + void (*func)(TCGv_env)) +{ + if (gen_trap_ifnofpu(dc)) { + return true; + } + if (gen_trap_float128(dc)) { + return true; + } + + gen_op_clear_ieee_excp_and_FTT(); + gen_op_load_fpr_QT0(QFPREG(a->rs1)); + gen_op_load_fpr_QT1(QFPREG(a->rs2)); + func(tcg_env); + gen_helper_check_ieee_exceptions(cpu_fsr, tcg_env); + gen_op_store_QT0_fpr(QFPREG(a->rd)); + gen_update_fprs_dirty(dc, QFPREG(a->rd)); + return advance_pc(dc); +} + +TRANS(FADDq, ALL, do_env_qqq, a, gen_helper_faddq) +TRANS(FSUBq, ALL, do_env_qqq, a, gen_helper_fsubq) +TRANS(FMULq, ALL, do_env_qqq, a, gen_helper_fmulq) +TRANS(FDIVq, ALL, do_env_qqq, a, gen_helper_fdivq) + #define CHECK_IU_FEATURE(dc, FEATURE) \ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ goto illegal_insn; @@ -5025,23 +5037,11 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x46: /* fsubd */ case 0x4a: /* fmuld */ case 0x4e: /* fdivd */ - g_assert_not_reached(); /* in decodetree */ case 0x43: /* faddq */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_fop_QQQ(dc, rd, rs1, rs2, gen_helper_faddq); - break; case 0x47: /* fsubq */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_fop_QQQ(dc, rd, rs1, rs2, gen_helper_fsubq); - break; case 0x4b: /* fmulq */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_fop_QQQ(dc, rd, rs1, rs2, gen_helper_fmulq); - break; case 0x4f: /* fdivq */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_fop_QQQ(dc, rd, rs1, rs2, gen_helper_fdivq); - break; + g_assert_not_reached(); /* in decodetree */ case 0x69: /* fsmuld */ CHECK_FPU_FEATURE(dc, FSMULD); gen_fop_DFF(dc, rd, rs1, rs2, gen_helper_fsmuld); From ff4c711b8d01608c4589cead0e2f650588d4b804 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Oct 2023 16:05:13 -0700 Subject: [PATCH 094/974] target/sparc: Move FSMULD to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 1 + target/sparc/translate.c | 43 +++++++++++++++++++++------------------ 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index f18fd99476..6817d52ca2 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -260,6 +260,7 @@ FMULq 10 ..... 110100 ..... 0 0100 1011 ..... @r_r_r FDIVs 10 ..... 110100 ..... 0 0100 1101 ..... @r_r_r FDIVd 10 ..... 110100 ..... 0 0100 1110 ..... @r_r_r FDIVq 10 ..... 110100 ..... 0 0100 1111 ..... @r_r_r +FsMULd 10 ..... 110100 ..... 0 0110 1001 ..... @r_r_r FdTOx 10 ..... 110100 00000 0 1000 0010 ..... @r_r2 FxTOd 10 ..... 110100 00000 0 1000 1000 ..... @r_r2 FiTOs 10 ..... 110100 00000 0 1100 0100 ..... @r_r2 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 94ad75b897..6626042776 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -1669,22 +1669,6 @@ static void gen_ne_fop_QQ(DisasContext *dc, int rd, int rs, } #endif -static void gen_fop_DFF(DisasContext *dc, int rd, int rs1, int rs2, - void (*gen)(TCGv_i64, TCGv_ptr, TCGv_i32, TCGv_i32)) -{ - TCGv_i64 dst; - TCGv_i32 src1, src2; - - src1 = gen_load_fpr_F(dc, rs1); - src2 = gen_load_fpr_F(dc, rs2); - dst = gen_dest_fpr_D(dc, rd); - - gen(dst, tcg_env, src1, src2); - gen_helper_check_ieee_exceptions(cpu_fsr, tcg_env); - - gen_store_fpr_D(dc, rd, dst); -} - static void gen_fop_QDD(DisasContext *dc, int rd, int rs1, int rs2, void (*gen)(TCGv_ptr, TCGv_i64, TCGv_i64)) { @@ -4931,6 +4915,28 @@ TRANS(FSUBd, ALL, do_env_ddd, a, gen_helper_fsubd) TRANS(FMULd, ALL, do_env_ddd, a, gen_helper_fmuld) TRANS(FDIVd, ALL, do_env_ddd, a, gen_helper_fdivd) +static bool trans_FsMULd(DisasContext *dc, arg_r_r_r *a) +{ + TCGv_i64 dst; + TCGv_i32 src1, src2; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + if (!(dc->def->features & CPU_FEATURE_FSMULD)) { + return raise_unimpfpop(dc); + } + + gen_op_clear_ieee_excp_and_FTT(); + dst = gen_dest_fpr_D(dc, a->rd); + src1 = gen_load_fpr_F(dc, a->rs1); + src2 = gen_load_fpr_F(dc, a->rs2); + gen_helper_fsmuld(dst, tcg_env, src1, src2); + gen_helper_check_ieee_exceptions(cpu_fsr, tcg_env); + gen_store_fpr_D(dc, a->rd, dst); + return advance_pc(dc); +} + static bool do_dddd(DisasContext *dc, arg_r_r_r *a, void (*func)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64)) { @@ -5041,11 +5047,8 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x47: /* fsubq */ case 0x4b: /* fmulq */ case 0x4f: /* fdivq */ - g_assert_not_reached(); /* in decodetree */ case 0x69: /* fsmuld */ - CHECK_FPU_FEATURE(dc, FSMULD); - gen_fop_DFF(dc, rd, rs1, rs2, gen_helper_fsmuld); - break; + g_assert_not_reached(); /* in decodetree */ case 0x6e: /* fdmulq */ CHECK_FPU_FEATURE(dc, FLOAT128); gen_fop_QDD(dc, rd, rs1, rs2, gen_helper_fdmulq); From 5e3b17bbe9cc49c67d68f4a676113361944c8709 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Oct 2023 16:16:54 -0700 Subject: [PATCH 095/974] target/sparc: Move FDMULQ to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 1 + target/sparc/translate.c | 41 +++++++++++++++++++++------------------ 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 6817d52ca2..a19d191603 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -261,6 +261,7 @@ FDIVs 10 ..... 110100 ..... 0 0100 1101 ..... @r_r_r FDIVd 10 ..... 110100 ..... 0 0100 1110 ..... @r_r_r FDIVq 10 ..... 110100 ..... 0 0100 1111 ..... @r_r_r FsMULd 10 ..... 110100 ..... 0 0110 1001 ..... @r_r_r +FdMULq 10 ..... 110100 ..... 0 0110 1110 ..... @r_r_r FdTOx 10 ..... 110100 00000 0 1000 0010 ..... @r_r2 FxTOd 10 ..... 110100 00000 0 1000 1000 ..... @r_r2 FiTOs 10 ..... 110100 00000 0 1100 0100 ..... @r_r2 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 6626042776..29643768e1 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -1669,21 +1669,6 @@ static void gen_ne_fop_QQ(DisasContext *dc, int rd, int rs, } #endif -static void gen_fop_QDD(DisasContext *dc, int rd, int rs1, int rs2, - void (*gen)(TCGv_ptr, TCGv_i64, TCGv_i64)) -{ - TCGv_i64 src1, src2; - - src1 = gen_load_fpr_D(dc, rs1); - src2 = gen_load_fpr_D(dc, rs2); - - gen(tcg_env, src1, src2); - gen_helper_check_ieee_exceptions(cpu_fsr, tcg_env); - - gen_op_store_QT0_fpr(QFPREG(rd)); - gen_update_fprs_dirty(dc, QFPREG(rd)); -} - #ifdef TARGET_SPARC64 static void gen_fop_DF(DisasContext *dc, int rd, int rs, void (*gen)(TCGv_i64, TCGv_ptr, TCGv_i32)) @@ -4982,6 +4967,27 @@ TRANS(FSUBq, ALL, do_env_qqq, a, gen_helper_fsubq) TRANS(FMULq, ALL, do_env_qqq, a, gen_helper_fmulq) TRANS(FDIVq, ALL, do_env_qqq, a, gen_helper_fdivq) +static bool trans_FdMULq(DisasContext *dc, arg_r_r_r *a) +{ + TCGv_i64 src1, src2; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + if (gen_trap_float128(dc)) { + return true; + } + + gen_op_clear_ieee_excp_and_FTT(); + src1 = gen_load_fpr_D(dc, a->rs1); + src2 = gen_load_fpr_D(dc, a->rs2); + gen_helper_fdmulq(tcg_env, src1, src2); + gen_helper_check_ieee_exceptions(cpu_fsr, tcg_env); + gen_op_store_QT0_fpr(QFPREG(a->rd)); + gen_update_fprs_dirty(dc, QFPREG(a->rd)); + return advance_pc(dc); +} + #define CHECK_IU_FEATURE(dc, FEATURE) \ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ goto illegal_insn; @@ -5048,11 +5054,8 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x4b: /* fmulq */ case 0x4f: /* fdivq */ case 0x69: /* fsmuld */ - g_assert_not_reached(); /* in decodetree */ case 0x6e: /* fdmulq */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_fop_QDD(dc, rd, rs1, rs2, gen_helper_fdmulq); - break; + g_assert_not_reached(); /* in decodetree */ case 0xc6: /* fdtos */ gen_fop_FD(dc, rd, rs2, gen_helper_fdtos); break; From 8c94bcd8507afd3f492d4b0dd7aefe5aaa9aab9a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Oct 2023 16:29:46 -0700 Subject: [PATCH 096/974] target/sparc: Move gen_fop_FD insns to decodetree Move FdTOs, FdTOi, FxTOs. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 3 +++ target/sparc/translate.c | 51 +++++++++++++++++++++------------------ 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index a19d191603..faf2bcef83 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -263,9 +263,12 @@ FDIVq 10 ..... 110100 ..... 0 0100 1111 ..... @r_r_r FsMULd 10 ..... 110100 ..... 0 0110 1001 ..... @r_r_r FdMULq 10 ..... 110100 ..... 0 0110 1110 ..... @r_r_r FdTOx 10 ..... 110100 00000 0 1000 0010 ..... @r_r2 +FxTOs 10 ..... 110100 00000 0 1000 0100 ..... @r_r2 FxTOd 10 ..... 110100 00000 0 1000 1000 ..... @r_r2 FiTOs 10 ..... 110100 00000 0 1100 0100 ..... @r_r2 +FdTOs 10 ..... 110100 00000 0 1100 0110 ..... @r_r2 FsTOi 10 ..... 110100 00000 0 1101 0001 ..... @r_r2 +FdTOi 10 ..... 110100 00000 0 1101 0010 ..... @r_r2 { [ diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 29643768e1..6c1dcf6472 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -74,6 +74,7 @@ # define gen_helper_fmuld8ulx16 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fpmerge ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fxtod ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fxtos ({ qemu_build_not_reached(); NULL; }) # define gen_helper_pdist ({ qemu_build_not_reached(); NULL; }) # define FSR_LDXFSR_MASK 0 # define FSR_LDXFSR_OLDMASK 0 @@ -1700,21 +1701,6 @@ static void gen_ne_fop_DF(DisasContext *dc, int rd, int rs, gen_store_fpr_D(dc, rd, dst); } -static void gen_fop_FD(DisasContext *dc, int rd, int rs, - void (*gen)(TCGv_i32, TCGv_ptr, TCGv_i64)) -{ - TCGv_i32 dst; - TCGv_i64 src; - - src = gen_load_fpr_D(dc, rs); - dst = gen_dest_fpr_F(dc); - - gen(dst, tcg_env, src); - gen_helper_check_ieee_exceptions(cpu_fsr, tcg_env); - - gen_store_fpr_F(dc, rd, dst); -} - static void gen_fop_FQ(DisasContext *dc, int rd, int rs, void (*gen)(TCGv_i32, TCGv_ptr)) { @@ -4715,6 +4701,29 @@ TRANS(FSQRTs, ALL, do_env_ff, a, gen_helper_fsqrts) TRANS(FiTOs, ALL, do_env_ff, a, gen_helper_fitos) TRANS(FsTOi, ALL, do_env_ff, a, gen_helper_fstoi) +static bool do_env_fd(DisasContext *dc, arg_r_r *a, + void (*func)(TCGv_i32, TCGv_env, TCGv_i64)) +{ + TCGv_i32 dst; + TCGv_i64 src; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + gen_op_clear_ieee_excp_and_FTT(); + dst = gen_dest_fpr_F(dc); + src = gen_load_fpr_D(dc, a->rs); + func(dst, tcg_env, src); + gen_helper_check_ieee_exceptions(cpu_fsr, tcg_env); + gen_store_fpr_F(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(FdTOs, ALL, do_env_fd, a, gen_helper_fdtos) +TRANS(FdTOi, ALL, do_env_fd, a, gen_helper_fdtoi) +TRANS(FxTOs, 64, do_env_fd, a, gen_helper_fxtos) + static bool do_dd(DisasContext *dc, arg_r_r *a, void (*func)(TCGv_i64, TCGv_i64)) { @@ -5055,10 +5064,10 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x4f: /* fdivq */ case 0x69: /* fsmuld */ case 0x6e: /* fdmulq */ - g_assert_not_reached(); /* in decodetree */ case 0xc6: /* fdtos */ - gen_fop_FD(dc, rd, rs2, gen_helper_fdtos); - break; + case 0xd2: /* fdtoi */ + case 0x84: /* V9 fxtos */ + g_assert_not_reached(); /* in decodetree */ case 0xc7: /* fqtos */ CHECK_FPU_FEATURE(dc, FLOAT128); gen_fop_FQ(dc, rd, rs2, gen_helper_fqtos); @@ -5085,9 +5094,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) CHECK_FPU_FEATURE(dc, FLOAT128); gen_ne_fop_QD(dc, rd, rs2, gen_helper_fdtoq); break; - case 0xd2: /* fdtoi */ - gen_fop_FD(dc, rd, rs2, gen_helper_fdtoi); - break; case 0xd3: /* fqtoi */ CHECK_FPU_FEATURE(dc, FLOAT128); gen_fop_FQ(dc, rd, rs2, gen_helper_fqtoi); @@ -5112,9 +5118,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) CHECK_FPU_FEATURE(dc, FLOAT128); gen_fop_DQ(dc, rd, rs2, gen_helper_fqtox); break; - case 0x84: /* V9 fxtos */ - gen_fop_FD(dc, rd, rs2, gen_helper_fxtos); - break; case 0x8c: /* V9 fxtoq */ CHECK_FPU_FEATURE(dc, FLOAT128); gen_ne_fop_QD(dc, rd, rs2, gen_helper_fxtoq); From 199d43efb176793d5e052947707285bcb49e6f82 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Oct 2023 16:40:16 -0700 Subject: [PATCH 097/974] target/sparc: Move FiTOd, FsTOd, FsTOx to decodetree Note that gen_ne_fop_DF was incorrectly named and does pass env. The two sets of helpers should have been unified. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 3 ++ target/sparc/translate.c | 67 ++++++++++++++++----------------------- 2 files changed, 30 insertions(+), 40 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index faf2bcef83..ee9262061b 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -262,11 +262,14 @@ FDIVd 10 ..... 110100 ..... 0 0100 1110 ..... @r_r_r FDIVq 10 ..... 110100 ..... 0 0100 1111 ..... @r_r_r FsMULd 10 ..... 110100 ..... 0 0110 1001 ..... @r_r_r FdMULq 10 ..... 110100 ..... 0 0110 1110 ..... @r_r_r +FsTOx 10 ..... 110100 00000 0 1000 0001 ..... @r_r2 FdTOx 10 ..... 110100 00000 0 1000 0010 ..... @r_r2 FxTOs 10 ..... 110100 00000 0 1000 0100 ..... @r_r2 FxTOd 10 ..... 110100 00000 0 1000 1000 ..... @r_r2 FiTOs 10 ..... 110100 00000 0 1100 0100 ..... @r_r2 FdTOs 10 ..... 110100 00000 0 1100 0110 ..... @r_r2 +FiTOd 10 ..... 110100 00000 0 1100 1000 ..... @r_r2 +FsTOd 10 ..... 110100 00000 0 1100 1001 ..... @r_r2 FsTOi 10 ..... 110100 00000 0 1101 0001 ..... @r_r2 FdTOi 10 ..... 110100 00000 0 1101 0010 ..... @r_r2 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 6c1dcf6472..e71b41e196 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -73,6 +73,7 @@ # define gen_helper_fmuld8sux16 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fmuld8ulx16 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fpmerge ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fstox ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fxtod ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fxtos ({ qemu_build_not_reached(); NULL; }) # define gen_helper_pdist ({ qemu_build_not_reached(); NULL; }) @@ -1670,37 +1671,6 @@ static void gen_ne_fop_QQ(DisasContext *dc, int rd, int rs, } #endif -#ifdef TARGET_SPARC64 -static void gen_fop_DF(DisasContext *dc, int rd, int rs, - void (*gen)(TCGv_i64, TCGv_ptr, TCGv_i32)) -{ - TCGv_i64 dst; - TCGv_i32 src; - - src = gen_load_fpr_F(dc, rs); - dst = gen_dest_fpr_D(dc, rd); - - gen(dst, tcg_env, src); - gen_helper_check_ieee_exceptions(cpu_fsr, tcg_env); - - gen_store_fpr_D(dc, rd, dst); -} -#endif - -static void gen_ne_fop_DF(DisasContext *dc, int rd, int rs, - void (*gen)(TCGv_i64, TCGv_ptr, TCGv_i32)) -{ - TCGv_i64 dst; - TCGv_i32 src; - - src = gen_load_fpr_F(dc, rs); - dst = gen_dest_fpr_D(dc, rd); - - gen(dst, tcg_env, src); - - gen_store_fpr_D(dc, rd, dst); -} - static void gen_fop_FQ(DisasContext *dc, int rd, int rs, void (*gen)(TCGv_i32, TCGv_ptr)) { @@ -4768,6 +4738,29 @@ TRANS(FSQRTd, ALL, do_env_dd, a, gen_helper_fsqrtd) TRANS(FxTOd, 64, do_env_dd, a, gen_helper_fxtod) TRANS(FdTOx, 64, do_env_dd, a, gen_helper_fdtox) +static bool do_env_df(DisasContext *dc, arg_r_r *a, + void (*func)(TCGv_i64, TCGv_env, TCGv_i32)) +{ + TCGv_i64 dst; + TCGv_i32 src; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + gen_op_clear_ieee_excp_and_FTT(); + dst = gen_dest_fpr_D(dc, a->rd); + src = gen_load_fpr_F(dc, a->rs); + func(dst, tcg_env, src); + gen_helper_check_ieee_exceptions(cpu_fsr, tcg_env); + gen_store_fpr_D(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(FiTOd, ALL, do_env_df, a, gen_helper_fitod) +TRANS(FsTOd, ALL, do_env_df, a, gen_helper_fstod) +TRANS(FsTOx, 64, do_env_df, a, gen_helper_fstox) + static bool do_env_qq(DisasContext *dc, arg_r_r *a, void (*func)(TCGv_env)) { @@ -5067,17 +5060,14 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0xc6: /* fdtos */ case 0xd2: /* fdtoi */ case 0x84: /* V9 fxtos */ + case 0xc8: /* fitod */ + case 0xc9: /* fstod */ + case 0x81: /* V9 fstox */ g_assert_not_reached(); /* in decodetree */ case 0xc7: /* fqtos */ CHECK_FPU_FEATURE(dc, FLOAT128); gen_fop_FQ(dc, rd, rs2, gen_helper_fqtos); break; - case 0xc8: /* fitod */ - gen_ne_fop_DF(dc, rd, rs2, gen_helper_fitod); - break; - case 0xc9: /* fstod */ - gen_ne_fop_DF(dc, rd, rs2, gen_helper_fstod); - break; case 0xcb: /* fqtod */ CHECK_FPU_FEATURE(dc, FLOAT128); gen_fop_DQ(dc, rd, rs2, gen_helper_fqtod); @@ -5111,9 +5101,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) CHECK_FPU_FEATURE(dc, FLOAT128); gen_ne_fop_QQ(dc, rd, rs2, gen_helper_fabsq); break; - case 0x81: /* V9 fstox */ - gen_fop_DF(dc, rd, rs2, gen_helper_fstox); - break; case 0x83: /* V9 fqtox */ CHECK_FPU_FEATURE(dc, FLOAT128); gen_fop_DQ(dc, rd, rs2, gen_helper_fqtox); From bd9c5c428f89db6805c83e0f98ec9089b9279f1b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Oct 2023 16:47:14 -0700 Subject: [PATCH 098/974] target/sparc: Move FqTOs, FqTOi to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 2 ++ target/sparc/translate.c | 48 +++++++++++++++++++++------------------ 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index ee9262061b..33f0c738e6 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -268,10 +268,12 @@ FxTOs 10 ..... 110100 00000 0 1000 0100 ..... @r_r2 FxTOd 10 ..... 110100 00000 0 1000 1000 ..... @r_r2 FiTOs 10 ..... 110100 00000 0 1100 0100 ..... @r_r2 FdTOs 10 ..... 110100 00000 0 1100 0110 ..... @r_r2 +FqTOs 10 ..... 110100 00000 0 1100 0111 ..... @r_r2 FiTOd 10 ..... 110100 00000 0 1100 1000 ..... @r_r2 FsTOd 10 ..... 110100 00000 0 1100 1001 ..... @r_r2 FsTOi 10 ..... 110100 00000 0 1101 0001 ..... @r_r2 FdTOi 10 ..... 110100 00000 0 1101 0010 ..... @r_r2 +FqTOi 10 ..... 110100 00000 0 1101 0011 ..... @r_r2 { [ diff --git a/target/sparc/translate.c b/target/sparc/translate.c index e71b41e196..37405df926 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -1671,20 +1671,6 @@ static void gen_ne_fop_QQ(DisasContext *dc, int rd, int rs, } #endif -static void gen_fop_FQ(DisasContext *dc, int rd, int rs, - void (*gen)(TCGv_i32, TCGv_ptr)) -{ - TCGv_i32 dst; - - gen_op_load_fpr_QT1(QFPREG(rs)); - dst = gen_dest_fpr_F(dc); - - gen(dst, tcg_env); - gen_helper_check_ieee_exceptions(cpu_fsr, tcg_env); - - gen_store_fpr_F(dc, rd, dst); -} - static void gen_fop_DQ(DisasContext *dc, int rd, int rs, void (*gen)(TCGv_i64, TCGv_ptr)) { @@ -4782,6 +4768,30 @@ static bool do_env_qq(DisasContext *dc, arg_r_r *a, TRANS(FSQRTq, ALL, do_env_qq, a, gen_helper_fsqrtq) +static bool do_env_fq(DisasContext *dc, arg_r_r *a, + void (*func)(TCGv_i32, TCGv_env)) +{ + TCGv_i32 dst; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + if (gen_trap_float128(dc)) { + return true; + } + + gen_op_clear_ieee_excp_and_FTT(); + gen_op_load_fpr_QT1(QFPREG(a->rs)); + dst = gen_dest_fpr_F(dc); + func(dst, tcg_env); + gen_helper_check_ieee_exceptions(cpu_fsr, tcg_env); + gen_store_fpr_F(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(FqTOs, ALL, do_env_fq, a, gen_helper_fqtos) +TRANS(FqTOi, ALL, do_env_fq, a, gen_helper_fqtoi) + static bool do_fff(DisasContext *dc, arg_r_r_r *a, void (*func)(TCGv_i32, TCGv_i32, TCGv_i32)) { @@ -5063,11 +5073,9 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0xc8: /* fitod */ case 0xc9: /* fstod */ case 0x81: /* V9 fstox */ - g_assert_not_reached(); /* in decodetree */ case 0xc7: /* fqtos */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_fop_FQ(dc, rd, rs2, gen_helper_fqtos); - break; + case 0xd3: /* fqtoi */ + g_assert_not_reached(); /* in decodetree */ case 0xcb: /* fqtod */ CHECK_FPU_FEATURE(dc, FLOAT128); gen_fop_DQ(dc, rd, rs2, gen_helper_fqtod); @@ -5084,10 +5092,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) CHECK_FPU_FEATURE(dc, FLOAT128); gen_ne_fop_QD(dc, rd, rs2, gen_helper_fdtoq); break; - case 0xd3: /* fqtoi */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_fop_FQ(dc, rd, rs2, gen_helper_fqtoi); - break; #ifdef TARGET_SPARC64 case 0x3: /* V9 fmovq */ CHECK_FPU_FEATURE(dc, FLOAT128); From 1617586f5ab748518fc34ce592ea187d8bd208ee Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Oct 2023 17:00:34 -0700 Subject: [PATCH 099/974] target/sparc: Move FqTOd, FqTOx to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 2 ++ target/sparc/translate.c | 49 +++++++++++++++++++++------------------ 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 33f0c738e6..7b65b31f89 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -264,6 +264,7 @@ FsMULd 10 ..... 110100 ..... 0 0110 1001 ..... @r_r_r FdMULq 10 ..... 110100 ..... 0 0110 1110 ..... @r_r_r FsTOx 10 ..... 110100 00000 0 1000 0001 ..... @r_r2 FdTOx 10 ..... 110100 00000 0 1000 0010 ..... @r_r2 +FqTOx 10 ..... 110100 00000 0 1000 0011 ..... @r_r2 FxTOs 10 ..... 110100 00000 0 1000 0100 ..... @r_r2 FxTOd 10 ..... 110100 00000 0 1000 1000 ..... @r_r2 FiTOs 10 ..... 110100 00000 0 1100 0100 ..... @r_r2 @@ -271,6 +272,7 @@ FdTOs 10 ..... 110100 00000 0 1100 0110 ..... @r_r2 FqTOs 10 ..... 110100 00000 0 1100 0111 ..... @r_r2 FiTOd 10 ..... 110100 00000 0 1100 1000 ..... @r_r2 FsTOd 10 ..... 110100 00000 0 1100 1001 ..... @r_r2 +FqTOd 10 ..... 110100 00000 0 1100 1011 ..... @r_r2 FsTOi 10 ..... 110100 00000 0 1101 0001 ..... @r_r2 FdTOi 10 ..... 110100 00000 0 1101 0010 ..... @r_r2 FqTOi 10 ..... 110100 00000 0 1101 0011 ..... @r_r2 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 37405df926..39da3f1705 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -73,6 +73,7 @@ # define gen_helper_fmuld8sux16 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fmuld8ulx16 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fpmerge ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fqtox ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fstox ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fxtod ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fxtos ({ qemu_build_not_reached(); NULL; }) @@ -1671,20 +1672,6 @@ static void gen_ne_fop_QQ(DisasContext *dc, int rd, int rs, } #endif -static void gen_fop_DQ(DisasContext *dc, int rd, int rs, - void (*gen)(TCGv_i64, TCGv_ptr)) -{ - TCGv_i64 dst; - - gen_op_load_fpr_QT1(QFPREG(rs)); - dst = gen_dest_fpr_D(dc, rd); - - gen(dst, tcg_env); - gen_helper_check_ieee_exceptions(cpu_fsr, tcg_env); - - gen_store_fpr_D(dc, rd, dst); -} - static void gen_ne_fop_QF(DisasContext *dc, int rd, int rs, void (*gen)(TCGv_ptr, TCGv_i32)) { @@ -4792,6 +4779,30 @@ static bool do_env_fq(DisasContext *dc, arg_r_r *a, TRANS(FqTOs, ALL, do_env_fq, a, gen_helper_fqtos) TRANS(FqTOi, ALL, do_env_fq, a, gen_helper_fqtoi) +static bool do_env_dq(DisasContext *dc, arg_r_r *a, + void (*func)(TCGv_i64, TCGv_env)) +{ + TCGv_i64 dst; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + if (gen_trap_float128(dc)) { + return true; + } + + gen_op_clear_ieee_excp_and_FTT(); + gen_op_load_fpr_QT1(QFPREG(a->rs)); + dst = gen_dest_fpr_D(dc, a->rd); + func(dst, tcg_env); + gen_helper_check_ieee_exceptions(cpu_fsr, tcg_env); + gen_store_fpr_D(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(FqTOd, ALL, do_env_dq, a, gen_helper_fqtod) +TRANS(FqTOx, 64, do_env_dq, a, gen_helper_fqtox) + static bool do_fff(DisasContext *dc, arg_r_r_r *a, void (*func)(TCGv_i32, TCGv_i32, TCGv_i32)) { @@ -5075,11 +5086,9 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x81: /* V9 fstox */ case 0xc7: /* fqtos */ case 0xd3: /* fqtoi */ - g_assert_not_reached(); /* in decodetree */ case 0xcb: /* fqtod */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_fop_DQ(dc, rd, rs2, gen_helper_fqtod); - break; + case 0x83: /* V9 fqtox */ + g_assert_not_reached(); /* in decodetree */ case 0xcc: /* fitoq */ CHECK_FPU_FEATURE(dc, FLOAT128); gen_ne_fop_QF(dc, rd, rs2, gen_helper_fitoq); @@ -5105,10 +5114,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) CHECK_FPU_FEATURE(dc, FLOAT128); gen_ne_fop_QQ(dc, rd, rs2, gen_helper_fabsq); break; - case 0x83: /* V9 fqtox */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_fop_DQ(dc, rd, rs2, gen_helper_fqtox); - break; case 0x8c: /* V9 fxtoq */ CHECK_FPU_FEATURE(dc, FLOAT128); gen_ne_fop_QD(dc, rd, rs2, gen_helper_fxtoq); From 13ebcc77491f80a3f339a21ab4902d7452d64192 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Oct 2023 17:12:24 -0700 Subject: [PATCH 100/974] target/sparc: Move FiTOq, FsTOq to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 2 ++ target/sparc/translate.c | 44 +++++++++++++++++++++------------------ 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 7b65b31f89..c76e603f2d 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -273,6 +273,8 @@ FqTOs 10 ..... 110100 00000 0 1100 0111 ..... @r_r2 FiTOd 10 ..... 110100 00000 0 1100 1000 ..... @r_r2 FsTOd 10 ..... 110100 00000 0 1100 1001 ..... @r_r2 FqTOd 10 ..... 110100 00000 0 1100 1011 ..... @r_r2 +FiTOq 10 ..... 110100 00000 0 1100 1100 ..... @r_r2 +FsTOq 10 ..... 110100 00000 0 1100 1101 ..... @r_r2 FsTOi 10 ..... 110100 00000 0 1101 0001 ..... @r_r2 FdTOi 10 ..... 110100 00000 0 1101 0010 ..... @r_r2 FqTOi 10 ..... 110100 00000 0 1101 0011 ..... @r_r2 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 39da3f1705..ad2d794b3d 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -1672,19 +1672,6 @@ static void gen_ne_fop_QQ(DisasContext *dc, int rd, int rs, } #endif -static void gen_ne_fop_QF(DisasContext *dc, int rd, int rs, - void (*gen)(TCGv_ptr, TCGv_i32)) -{ - TCGv_i32 src; - - src = gen_load_fpr_F(dc, rs); - - gen(tcg_env, src); - - gen_op_store_QT0_fpr(QFPREG(rd)); - gen_update_fprs_dirty(dc, QFPREG(rd)); -} - static void gen_ne_fop_QD(DisasContext *dc, int rd, int rs, void (*gen)(TCGv_ptr, TCGv_i64)) { @@ -4803,6 +4790,29 @@ static bool do_env_dq(DisasContext *dc, arg_r_r *a, TRANS(FqTOd, ALL, do_env_dq, a, gen_helper_fqtod) TRANS(FqTOx, 64, do_env_dq, a, gen_helper_fqtox) +static bool do_env_qf(DisasContext *dc, arg_r_r *a, + void (*func)(TCGv_env, TCGv_i32)) +{ + TCGv_i32 src; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + if (gen_trap_float128(dc)) { + return true; + } + + gen_op_clear_ieee_excp_and_FTT(); + src = gen_load_fpr_F(dc, a->rs); + func(tcg_env, src); + gen_op_store_QT0_fpr(QFPREG(a->rd)); + gen_update_fprs_dirty(dc, QFPREG(a->rd)); + return advance_pc(dc); +} + +TRANS(FiTOq, ALL, do_env_qf, a, gen_helper_fitoq) +TRANS(FsTOq, ALL, do_env_qf, a, gen_helper_fstoq) + static bool do_fff(DisasContext *dc, arg_r_r_r *a, void (*func)(TCGv_i32, TCGv_i32, TCGv_i32)) { @@ -5088,15 +5098,9 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0xd3: /* fqtoi */ case 0xcb: /* fqtod */ case 0x83: /* V9 fqtox */ - g_assert_not_reached(); /* in decodetree */ case 0xcc: /* fitoq */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_ne_fop_QF(dc, rd, rs2, gen_helper_fitoq); - break; case 0xcd: /* fstoq */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_ne_fop_QF(dc, rd, rs2, gen_helper_fstoq); - break; + g_assert_not_reached(); /* in decodetree */ case 0xce: /* fdtoq */ CHECK_FPU_FEATURE(dc, FLOAT128); gen_ne_fop_QD(dc, rd, rs2, gen_helper_fdtoq); From 7b8e3e1a8730d69e75b269dec4ca5289ebf12533 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Oct 2023 17:20:04 -0700 Subject: [PATCH 101/974] target/sparc: Move FdTOq, FxTOq to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 2 ++ target/sparc/translate.c | 47 ++++++++++++++++++++++----------------- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index c76e603f2d..e1f5394d17 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -267,6 +267,7 @@ FdTOx 10 ..... 110100 00000 0 1000 0010 ..... @r_r2 FqTOx 10 ..... 110100 00000 0 1000 0011 ..... @r_r2 FxTOs 10 ..... 110100 00000 0 1000 0100 ..... @r_r2 FxTOd 10 ..... 110100 00000 0 1000 1000 ..... @r_r2 +FxTOq 10 ..... 110100 00000 0 1000 1100 ..... @r_r2 FiTOs 10 ..... 110100 00000 0 1100 0100 ..... @r_r2 FdTOs 10 ..... 110100 00000 0 1100 0110 ..... @r_r2 FqTOs 10 ..... 110100 00000 0 1100 0111 ..... @r_r2 @@ -275,6 +276,7 @@ FsTOd 10 ..... 110100 00000 0 1100 1001 ..... @r_r2 FqTOd 10 ..... 110100 00000 0 1100 1011 ..... @r_r2 FiTOq 10 ..... 110100 00000 0 1100 1100 ..... @r_r2 FsTOq 10 ..... 110100 00000 0 1100 1101 ..... @r_r2 +FdTOq 10 ..... 110100 00000 0 1100 1110 ..... @r_r2 FsTOi 10 ..... 110100 00000 0 1101 0001 ..... @r_r2 FdTOi 10 ..... 110100 00000 0 1101 0010 ..... @r_r2 FqTOi 10 ..... 110100 00000 0 1101 0011 ..... @r_r2 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index ad2d794b3d..40823f1325 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -77,6 +77,7 @@ # define gen_helper_fstox ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fxtod ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fxtos ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fxtoq ({ qemu_build_not_reached(); NULL; }) # define gen_helper_pdist ({ qemu_build_not_reached(); NULL; }) # define FSR_LDXFSR_MASK 0 # define FSR_LDXFSR_OLDMASK 0 @@ -1672,19 +1673,6 @@ static void gen_ne_fop_QQ(DisasContext *dc, int rd, int rs, } #endif -static void gen_ne_fop_QD(DisasContext *dc, int rd, int rs, - void (*gen)(TCGv_ptr, TCGv_i64)) -{ - TCGv_i64 src; - - src = gen_load_fpr_D(dc, rs); - - gen(tcg_env, src); - - gen_op_store_QT0_fpr(QFPREG(rd)); - gen_update_fprs_dirty(dc, QFPREG(rd)); -} - /* asi moves */ typedef enum { GET_ASI_HELPER, @@ -4813,6 +4801,29 @@ static bool do_env_qf(DisasContext *dc, arg_r_r *a, TRANS(FiTOq, ALL, do_env_qf, a, gen_helper_fitoq) TRANS(FsTOq, ALL, do_env_qf, a, gen_helper_fstoq) +static bool do_env_qd(DisasContext *dc, arg_r_r *a, + void (*func)(TCGv_env, TCGv_i64)) +{ + TCGv_i64 src; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + if (gen_trap_float128(dc)) { + return true; + } + + gen_op_clear_ieee_excp_and_FTT(); + src = gen_load_fpr_D(dc, a->rs); + func(tcg_env, src); + gen_op_store_QT0_fpr(QFPREG(a->rd)); + gen_update_fprs_dirty(dc, QFPREG(a->rd)); + return advance_pc(dc); +} + +TRANS(FdTOq, ALL, do_env_qd, a, gen_helper_fdtoq) +TRANS(FxTOq, 64, do_env_qd, a, gen_helper_fxtoq) + static bool do_fff(DisasContext *dc, arg_r_r_r *a, void (*func)(TCGv_i32, TCGv_i32, TCGv_i32)) { @@ -5100,11 +5111,9 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x83: /* V9 fqtox */ case 0xcc: /* fitoq */ case 0xcd: /* fstoq */ - g_assert_not_reached(); /* in decodetree */ case 0xce: /* fdtoq */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_ne_fop_QD(dc, rd, rs2, gen_helper_fdtoq); - break; + case 0x8c: /* V9 fxtoq */ + g_assert_not_reached(); /* in decodetree */ #ifdef TARGET_SPARC64 case 0x3: /* V9 fmovq */ CHECK_FPU_FEATURE(dc, FLOAT128); @@ -5118,10 +5127,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) CHECK_FPU_FEATURE(dc, FLOAT128); gen_ne_fop_QQ(dc, rd, rs2, gen_helper_fabsq); break; - case 0x8c: /* V9 fxtoq */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_ne_fop_QD(dc, rd, rs2, gen_helper_fxtoq); - break; #endif default: goto illegal_insn; From f4e18df5769c7921005357e278d81ed8f990e2c0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Oct 2023 17:32:24 -0700 Subject: [PATCH 102/974] target/sparc: Move FMOVq, FNEGq, FABSq to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 3 + target/sparc/translate.c | 142 +++++++++++++------------------------- 2 files changed, 51 insertions(+), 94 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index e1f5394d17..807ed3f66f 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -241,10 +241,13 @@ RETRY 10 00001 111110 00000 0 0000000000000 FMOVs 10 ..... 110100 00000 0 0000 0001 ..... @r_r2 FMOVd 10 ..... 110100 00000 0 0000 0010 ..... @r_r2 +FMOVq 10 ..... 110100 00000 0 0000 0011 ..... @r_r2 FNEGs 10 ..... 110100 00000 0 0000 0101 ..... @r_r2 FNEGd 10 ..... 110100 00000 0 0000 0110 ..... @r_r2 +FNEGq 10 ..... 110100 00000 0 0000 0111 ..... @r_r2 FABSs 10 ..... 110100 00000 0 0000 1001 ..... @r_r2 FABSd 10 ..... 110100 00000 0 0000 1010 ..... @r_r2 +FABSq 10 ..... 110100 00000 0 0000 1011 ..... @r_r2 FSQRTs 10 ..... 110100 00000 0 0010 1001 ..... @r_r2 FSQRTd 10 ..... 110100 00000 0 0010 1010 ..... @r_r2 FSQRTq 10 ..... 110100 00000 0 0010 1011 ..... @r_r2 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 40823f1325..e6451f3950 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -63,6 +63,7 @@ # define gen_helper_write_softint(E, S) qemu_build_not_reached() # define gen_helper_wrpil(E, S) qemu_build_not_reached() # define gen_helper_wrpstate(E, S) qemu_build_not_reached() +# define gen_helper_fabsq ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fdtox ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fexpand ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fmul8sux16 ({ qemu_build_not_reached(); NULL; }) @@ -72,12 +73,13 @@ # define gen_helper_fmul8x16 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fmuld8sux16 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fmuld8ulx16 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fnegq ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fpmerge ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fqtox ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fstox ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fxtod ({ qemu_build_not_reached(); NULL; }) -# define gen_helper_fxtos ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fxtoq ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fxtos ({ qemu_build_not_reached(); NULL; }) # define gen_helper_pdist ({ qemu_build_not_reached(); NULL; }) # define FSR_LDXFSR_MASK 0 # define FSR_LDXFSR_OLDMASK 0 @@ -267,18 +269,6 @@ static void gen_op_store_QT0_fpr(unsigned int dst) offsetof(CPU_QuadU, ll.lower)); } -#ifdef TARGET_SPARC64 -static void gen_move_Q(DisasContext *dc, unsigned int rd, unsigned int rs) -{ - rd = QFPREG(rd); - rs = QFPREG(rs); - - tcg_gen_mov_i64(cpu_fpr[rd / 2], cpu_fpr[rs / 2]); - tcg_gen_mov_i64(cpu_fpr[rd / 2 + 1], cpu_fpr[rs / 2 + 1]); - gen_update_fprs_dirty(dc, rd); -} -#endif - /* moves */ #ifdef CONFIG_USER_ONLY #define supervisor(dc) 0 @@ -1660,19 +1650,6 @@ static int gen_trap_ifnofpu(DisasContext *dc) return 0; } -#ifdef TARGET_SPARC64 -static void gen_ne_fop_QQ(DisasContext *dc, int rd, int rs, - void (*gen)(TCGv_ptr)) -{ - gen_op_load_fpr_QT1(QFPREG(rs)); - - gen(tcg_env); - - gen_op_store_QT0_fpr(QFPREG(rd)); - gen_update_fprs_dirty(dc, QFPREG(rd)); -} -#endif - /* asi moves */ typedef enum { GET_ASI_HELPER, @@ -4709,6 +4686,50 @@ TRANS(FiTOd, ALL, do_env_df, a, gen_helper_fitod) TRANS(FsTOd, ALL, do_env_df, a, gen_helper_fstod) TRANS(FsTOx, 64, do_env_df, a, gen_helper_fstox) +static bool trans_FMOVq(DisasContext *dc, arg_FMOVq *a) +{ + int rd, rs; + + if (!avail_64(dc)) { + return false; + } + if (gen_trap_ifnofpu(dc)) { + return true; + } + if (gen_trap_float128(dc)) { + return true; + } + + gen_op_clear_ieee_excp_and_FTT(); + rd = QFPREG(a->rd); + rs = QFPREG(a->rs); + tcg_gen_mov_i64(cpu_fpr[rd / 2], cpu_fpr[rs / 2]); + tcg_gen_mov_i64(cpu_fpr[rd / 2 + 1], cpu_fpr[rs / 2 + 1]); + gen_update_fprs_dirty(dc, rd); + return advance_pc(dc); +} + +static bool do_qq(DisasContext *dc, arg_r_r *a, + void (*func)(TCGv_env)) +{ + if (gen_trap_ifnofpu(dc)) { + return true; + } + if (gen_trap_float128(dc)) { + return true; + } + + gen_op_clear_ieee_excp_and_FTT(); + gen_op_load_fpr_QT1(QFPREG(a->rs)); + func(tcg_env); + gen_op_store_QT0_fpr(QFPREG(a->rd)); + gen_update_fprs_dirty(dc, QFPREG(a->rd)); + return advance_pc(dc); +} + +TRANS(FNEGq, 64, do_qq, a, gen_helper_fnegq) +TRANS(FABSq, 64, do_qq, a, gen_helper_fabsq) + static bool do_env_qq(DisasContext *dc, arg_r_r *a, void (*func)(TCGv_env)) { @@ -5063,74 +5084,7 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) TCGv cpu_dst __attribute__((unused)) = tcg_temp_new(); if (xop == 0x34) { /* FPU Operations */ - if (gen_trap_ifnofpu(dc)) { - goto jmp_insn; - } - gen_op_clear_ieee_excp_and_FTT(); - rs1 = GET_FIELD(insn, 13, 17); - rs2 = GET_FIELD(insn, 27, 31); - xop = GET_FIELD(insn, 18, 26); - - switch (xop) { - case 0x1: /* fmovs */ - case 0x5: /* fnegs */ - case 0x9: /* fabss */ - case 0x2: /* V9 fmovd */ - case 0x6: /* V9 fnegd */ - case 0xa: /* V9 fabsd */ - case 0x29: /* fsqrts */ - case 0xc4: /* fitos */ - case 0xd1: /* fstoi */ - case 0x2a: /* fsqrtd */ - case 0x82: /* V9 fdtox */ - case 0x88: /* V9 fxtod */ - case 0x2b: /* fsqrtq */ - case 0x41: /* fadds */ - case 0x45: /* fsubs */ - case 0x49: /* fmuls */ - case 0x4d: /* fdivs */ - case 0x42: /* faddd */ - case 0x46: /* fsubd */ - case 0x4a: /* fmuld */ - case 0x4e: /* fdivd */ - case 0x43: /* faddq */ - case 0x47: /* fsubq */ - case 0x4b: /* fmulq */ - case 0x4f: /* fdivq */ - case 0x69: /* fsmuld */ - case 0x6e: /* fdmulq */ - case 0xc6: /* fdtos */ - case 0xd2: /* fdtoi */ - case 0x84: /* V9 fxtos */ - case 0xc8: /* fitod */ - case 0xc9: /* fstod */ - case 0x81: /* V9 fstox */ - case 0xc7: /* fqtos */ - case 0xd3: /* fqtoi */ - case 0xcb: /* fqtod */ - case 0x83: /* V9 fqtox */ - case 0xcc: /* fitoq */ - case 0xcd: /* fstoq */ - case 0xce: /* fdtoq */ - case 0x8c: /* V9 fxtoq */ - g_assert_not_reached(); /* in decodetree */ -#ifdef TARGET_SPARC64 - case 0x3: /* V9 fmovq */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_move_Q(dc, rd, rs2); - break; - case 0x7: /* V9 fnegq */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_ne_fop_QQ(dc, rd, rs2, gen_helper_fnegq); - break; - case 0xb: /* V9 fabsq */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_ne_fop_QQ(dc, rd, rs2, gen_helper_fabsq); - break; -#endif - default: - goto illegal_insn; - } + goto illegal_insn; /* in decodetree */ } else if (xop == 0x35) { /* FPU Operations */ #ifdef TARGET_SPARC64 int cond; From f7ec8155f5714df29af2596b2468dd597c137256 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Oct 2023 18:04:56 -0700 Subject: [PATCH 103/974] target/sparc: Move FMOVR, FMOVcc, FMOVfcc to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 12 +++ target/sparc/translate.c | 192 ++++++++++++++++---------------------- 2 files changed, 91 insertions(+), 113 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 807ed3f66f..d4487e326a 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -284,6 +284,18 @@ FsTOi 10 ..... 110100 00000 0 1101 0001 ..... @r_r2 FdTOi 10 ..... 110100 00000 0 1101 0010 ..... @r_r2 FqTOi 10 ..... 110100 00000 0 1101 0011 ..... @r_r2 +FMOVscc 10 rd:5 110101 0 cond:4 1 cc:1 0 000001 rs2:5 +FMOVdcc 10 rd:5 110101 0 cond:4 1 cc:1 0 000010 rs2:5 +FMOVqcc 10 rd:5 110101 0 cond:4 1 cc:1 0 000011 rs2:5 + +FMOVsfcc 10 rd:5 110101 0 cond:4 0 cc:2 000001 rs2:5 +FMOVdfcc 10 rd:5 110101 0 cond:4 0 cc:2 000010 rs2:5 +FMOVqfcc 10 rd:5 110101 0 cond:4 0 cc:2 000011 rs2:5 + +FMOVRs 10 rd:5 110101 rs1:5 0 cond:3 00101 rs2:5 +FMOVRd 10 rd:5 110101 rs1:5 0 cond:3 00110 rs2:5 +FMOVRq 10 rd:5 110101 rs1:5 0 cond:3 00111 rs2:5 + { [ EDGE8cc 10 ..... 110110 ..... 0 0000 0000 ..... @r_r_r diff --git a/target/sparc/translate.c b/target/sparc/translate.c index e6451f3950..ff18dc0482 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -2442,15 +2442,9 @@ static void gen_stda_asi(DisasContext *dc, DisasASI *da, TCGv addr, int rd) } } -#ifdef TARGET_SPARC64 -static TCGv get_src1(DisasContext *dc, unsigned int insn) -{ - unsigned int rs1 = GET_FIELD(insn, 13, 17); - return gen_load_gpr(dc, rs1); -} - static void gen_fmovs(DisasContext *dc, DisasCompare *cmp, int rd, int rs) { +#ifdef TARGET_SPARC64 TCGv_i32 c32, zero, dst, s1, s2; /* We have two choices here: extend the 32 bit data and use movcond_i64, @@ -2473,19 +2467,27 @@ static void gen_fmovs(DisasContext *dc, DisasCompare *cmp, int rd, int rs) tcg_gen_movcond_i32(TCG_COND_NE, dst, c32, zero, s1, s2); gen_store_fpr_F(dc, rd, dst); +#else + qemu_build_not_reached(); +#endif } static void gen_fmovd(DisasContext *dc, DisasCompare *cmp, int rd, int rs) { +#ifdef TARGET_SPARC64 TCGv_i64 dst = gen_dest_fpr_D(dc, rd); tcg_gen_movcond_i64(cmp->cond, dst, cmp->c1, cmp->c2, gen_load_fpr_D(dc, rs), gen_load_fpr_D(dc, rd)); gen_store_fpr_D(dc, rd, dst); +#else + qemu_build_not_reached(); +#endif } static void gen_fmovq(DisasContext *dc, DisasCompare *cmp, int rd, int rs) { +#ifdef TARGET_SPARC64 int qd = QFPREG(rd); int qs = QFPREG(rs); @@ -2495,8 +2497,12 @@ static void gen_fmovq(DisasContext *dc, DisasCompare *cmp, int rd, int rs) cpu_fpr[qs / 2 + 1], cpu_fpr[qd / 2 + 1]); gen_update_fprs_dirty(dc, qd); +#else + qemu_build_not_reached(); +#endif } +#ifdef TARGET_SPARC64 static void gen_load_trap_state_at_tl(TCGv_ptr r_tsptr) { TCGv_i32 r_tl = tcg_temp_new_i32(); @@ -5053,6 +5059,72 @@ static bool trans_FdMULq(DisasContext *dc, arg_r_r_r *a) return advance_pc(dc); } +static bool do_fmovr(DisasContext *dc, arg_FMOVRs *a, bool is_128, + void (*func)(DisasContext *, DisasCompare *, int, int)) +{ + DisasCompare cmp; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + if (is_128 && gen_trap_float128(dc)) { + return true; + } + + gen_op_clear_ieee_excp_and_FTT(); + gen_compare_reg(&cmp, a->cond, gen_load_gpr(dc, a->rs1)); + func(dc, &cmp, a->rd, a->rs2); + return advance_pc(dc); +} + +TRANS(FMOVRs, 64, do_fmovr, a, false, gen_fmovs) +TRANS(FMOVRd, 64, do_fmovr, a, false, gen_fmovd) +TRANS(FMOVRq, 64, do_fmovr, a, true, gen_fmovq) + +static bool do_fmovcc(DisasContext *dc, arg_FMOVscc *a, bool is_128, + void (*func)(DisasContext *, DisasCompare *, int, int)) +{ + DisasCompare cmp; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + if (is_128 && gen_trap_float128(dc)) { + return true; + } + + gen_op_clear_ieee_excp_and_FTT(); + gen_compare(&cmp, a->cc, a->cond, dc); + func(dc, &cmp, a->rd, a->rs2); + return advance_pc(dc); +} + +TRANS(FMOVscc, 64, do_fmovcc, a, false, gen_fmovs) +TRANS(FMOVdcc, 64, do_fmovcc, a, false, gen_fmovd) +TRANS(FMOVqcc, 64, do_fmovcc, a, true, gen_fmovq) + +static bool do_fmovfcc(DisasContext *dc, arg_FMOVsfcc *a, bool is_128, + void (*func)(DisasContext *, DisasCompare *, int, int)) +{ + DisasCompare cmp; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + if (is_128 && gen_trap_float128(dc)) { + return true; + } + + gen_op_clear_ieee_excp_and_FTT(); + gen_fcompare(&cmp, a->cc, a->cond); + func(dc, &cmp, a->rd, a->rs2); + return advance_pc(dc); +} + +TRANS(FMOVsfcc, 64, do_fmovfcc, a, false, gen_fmovs) +TRANS(FMOVdfcc, 64, do_fmovfcc, a, false, gen_fmovd) +TRANS(FMOVqfcc, 64, do_fmovfcc, a, true, gen_fmovq) + #define CHECK_IU_FEATURE(dc, FEATURE) \ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ goto illegal_insn; @@ -5086,9 +5158,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) if (xop == 0x34) { /* FPU Operations */ goto illegal_insn; /* in decodetree */ } else if (xop == 0x35) { /* FPU Operations */ -#ifdef TARGET_SPARC64 - int cond; -#endif if (gen_trap_ifnofpu(dc)) { goto jmp_insn; } @@ -5097,110 +5166,7 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) rs2 = GET_FIELD(insn, 27, 31); xop = GET_FIELD(insn, 18, 26); -#ifdef TARGET_SPARC64 -#define FMOVR(sz) \ - do { \ - DisasCompare cmp; \ - cond = GET_FIELD_SP(insn, 10, 12); \ - cpu_src1 = get_src1(dc, insn); \ - gen_compare_reg(&cmp, cond, cpu_src1); \ - gen_fmov##sz(dc, &cmp, rd, rs2); \ - } while (0) - - if ((xop & 0x11f) == 0x005) { /* V9 fmovsr */ - FMOVR(s); - break; - } else if ((xop & 0x11f) == 0x006) { // V9 fmovdr - FMOVR(d); - break; - } else if ((xop & 0x11f) == 0x007) { // V9 fmovqr - CHECK_FPU_FEATURE(dc, FLOAT128); - FMOVR(q); - break; - } -#undef FMOVR -#endif switch (xop) { -#ifdef TARGET_SPARC64 -#define FMOVCC(fcc, sz) \ - do { \ - DisasCompare cmp; \ - cond = GET_FIELD_SP(insn, 14, 17); \ - gen_fcompare(&cmp, fcc, cond); \ - gen_fmov##sz(dc, &cmp, rd, rs2); \ - } while (0) - - case 0x001: /* V9 fmovscc %fcc0 */ - FMOVCC(0, s); - break; - case 0x002: /* V9 fmovdcc %fcc0 */ - FMOVCC(0, d); - break; - case 0x003: /* V9 fmovqcc %fcc0 */ - CHECK_FPU_FEATURE(dc, FLOAT128); - FMOVCC(0, q); - break; - case 0x041: /* V9 fmovscc %fcc1 */ - FMOVCC(1, s); - break; - case 0x042: /* V9 fmovdcc %fcc1 */ - FMOVCC(1, d); - break; - case 0x043: /* V9 fmovqcc %fcc1 */ - CHECK_FPU_FEATURE(dc, FLOAT128); - FMOVCC(1, q); - break; - case 0x081: /* V9 fmovscc %fcc2 */ - FMOVCC(2, s); - break; - case 0x082: /* V9 fmovdcc %fcc2 */ - FMOVCC(2, d); - break; - case 0x083: /* V9 fmovqcc %fcc2 */ - CHECK_FPU_FEATURE(dc, FLOAT128); - FMOVCC(2, q); - break; - case 0x0c1: /* V9 fmovscc %fcc3 */ - FMOVCC(3, s); - break; - case 0x0c2: /* V9 fmovdcc %fcc3 */ - FMOVCC(3, d); - break; - case 0x0c3: /* V9 fmovqcc %fcc3 */ - CHECK_FPU_FEATURE(dc, FLOAT128); - FMOVCC(3, q); - break; -#undef FMOVCC -#define FMOVCC(xcc, sz) \ - do { \ - DisasCompare cmp; \ - cond = GET_FIELD_SP(insn, 14, 17); \ - gen_compare(&cmp, xcc, cond, dc); \ - gen_fmov##sz(dc, &cmp, rd, rs2); \ - } while (0) - - case 0x101: /* V9 fmovscc %icc */ - FMOVCC(0, s); - break; - case 0x102: /* V9 fmovdcc %icc */ - FMOVCC(0, d); - break; - case 0x103: /* V9 fmovqcc %icc */ - CHECK_FPU_FEATURE(dc, FLOAT128); - FMOVCC(0, q); - break; - case 0x181: /* V9 fmovscc %xcc */ - FMOVCC(1, s); - break; - case 0x182: /* V9 fmovdcc %xcc */ - FMOVCC(1, d); - break; - case 0x183: /* V9 fmovqcc %xcc */ - CHECK_FPU_FEATURE(dc, FLOAT128); - FMOVCC(1, q); - break; -#undef FMOVCC -#endif case 0x51: /* fcmps, V9 %fcc */ cpu_src1_32 = gen_load_fpr_F(dc, rs1); cpu_src2_32 = gen_load_fpr_F(dc, rs2); From 40f9ad219bb991b50318836612b7331f35c7bb3b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Oct 2023 21:11:49 -0700 Subject: [PATCH 104/974] target/sparc: Convert FCMP, FCMPE to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 7 ++ target/sparc/translate.c | 145 +++++++++++++++++++++++--------------- 2 files changed, 96 insertions(+), 56 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index d4487e326a..f197bb0b36 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -296,6 +296,13 @@ FMOVRs 10 rd:5 110101 rs1:5 0 cond:3 00101 rs2:5 FMOVRd 10 rd:5 110101 rs1:5 0 cond:3 00110 rs2:5 FMOVRq 10 rd:5 110101 rs1:5 0 cond:3 00111 rs2:5 +FCMPs 10 000 cc:2 110101 rs1:5 0 0101 0001 rs2:5 +FCMPd 10 000 cc:2 110101 rs1:5 0 0101 0010 rs2:5 +FCMPq 10 000 cc:2 110101 rs1:5 0 0101 0011 rs2:5 +FCMPEs 10 000 cc:2 110101 rs1:5 0 0101 0101 rs2:5 +FCMPEd 10 000 cc:2 110101 rs1:5 0 0101 0110 rs2:5 +FCMPEq 10 000 cc:2 110101 rs1:5 0 0101 0111 rs2:5 + { [ EDGE8cc 10 ..... 110110 ..... 0 0000 0000 ..... @r_r_r diff --git a/target/sparc/translate.c b/target/sparc/translate.c index ff18dc0482..ddde64dfb4 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -5125,6 +5125,82 @@ TRANS(FMOVsfcc, 64, do_fmovfcc, a, false, gen_fmovs) TRANS(FMOVdfcc, 64, do_fmovfcc, a, false, gen_fmovd) TRANS(FMOVqfcc, 64, do_fmovfcc, a, true, gen_fmovq) +static bool do_fcmps(DisasContext *dc, arg_FCMPs *a, bool e) +{ + TCGv_i32 src1, src2; + + if (avail_32(dc) && a->cc != 0) { + return false; + } + if (gen_trap_ifnofpu(dc)) { + return true; + } + + gen_op_clear_ieee_excp_and_FTT(); + src1 = gen_load_fpr_F(dc, a->rs1); + src2 = gen_load_fpr_F(dc, a->rs2); + if (e) { + gen_op_fcmpes(a->cc, src1, src2); + } else { + gen_op_fcmps(a->cc, src1, src2); + } + return advance_pc(dc); +} + +TRANS(FCMPs, ALL, do_fcmps, a, false) +TRANS(FCMPEs, ALL, do_fcmps, a, true) + +static bool do_fcmpd(DisasContext *dc, arg_FCMPd *a, bool e) +{ + TCGv_i64 src1, src2; + + if (avail_32(dc) && a->cc != 0) { + return false; + } + if (gen_trap_ifnofpu(dc)) { + return true; + } + + gen_op_clear_ieee_excp_and_FTT(); + src1 = gen_load_fpr_D(dc, a->rs1); + src2 = gen_load_fpr_D(dc, a->rs2); + if (e) { + gen_op_fcmped(a->cc, src1, src2); + } else { + gen_op_fcmpd(a->cc, src1, src2); + } + return advance_pc(dc); +} + +TRANS(FCMPd, ALL, do_fcmpd, a, false) +TRANS(FCMPEd, ALL, do_fcmpd, a, true) + +static bool do_fcmpq(DisasContext *dc, arg_FCMPq *a, bool e) +{ + if (avail_32(dc) && a->cc != 0) { + return false; + } + if (gen_trap_ifnofpu(dc)) { + return true; + } + if (gen_trap_float128(dc)) { + return true; + } + + gen_op_clear_ieee_excp_and_FTT(); + gen_op_load_fpr_QT0(QFPREG(a->rs1)); + gen_op_load_fpr_QT1(QFPREG(a->rs2)); + if (e) { + gen_op_fcmpeq(a->cc); + } else { + gen_op_fcmpq(a->cc); + } + return advance_pc(dc); +} + +TRANS(FCMPq, ALL, do_fcmpq, a, false) +TRANS(FCMPEq, ALL, do_fcmpq, a, true) + #define CHECK_IU_FEATURE(dc, FEATURE) \ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ goto illegal_insn; @@ -5135,15 +5211,7 @@ TRANS(FMOVqfcc, 64, do_fmovfcc, a, true, gen_fmovq) /* before an instruction, dc->pc must be static */ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) { - unsigned int opc, rs1, rs2, rd; - TCGv cpu_src1 __attribute__((unused)); - TCGv_i32 cpu_src1_32, cpu_src2_32; - TCGv_i64 cpu_src1_64, cpu_src2_64; - TCGv_i32 cpu_dst_32 __attribute__((unused)); - TCGv_i64 cpu_dst_64 __attribute__((unused)); - - opc = GET_FIELD(insn, 0, 1); - rd = GET_FIELD(insn, 2, 6); + unsigned int opc = GET_FIELD(insn, 0, 1); switch (opc) { case 0: @@ -5153,61 +5221,22 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 2: /* FPU & Logical Operations */ { unsigned int xop = GET_FIELD(insn, 7, 12); - TCGv cpu_dst __attribute__((unused)) = tcg_temp_new(); if (xop == 0x34) { /* FPU Operations */ goto illegal_insn; /* in decodetree */ } else if (xop == 0x35) { /* FPU Operations */ - if (gen_trap_ifnofpu(dc)) { - goto jmp_insn; - } - gen_op_clear_ieee_excp_and_FTT(); - rs1 = GET_FIELD(insn, 13, 17); - rs2 = GET_FIELD(insn, 27, 31); - xop = GET_FIELD(insn, 18, 26); - - switch (xop) { - case 0x51: /* fcmps, V9 %fcc */ - cpu_src1_32 = gen_load_fpr_F(dc, rs1); - cpu_src2_32 = gen_load_fpr_F(dc, rs2); - gen_op_fcmps(rd & 3, cpu_src1_32, cpu_src2_32); - break; - case 0x52: /* fcmpd, V9 %fcc */ - cpu_src1_64 = gen_load_fpr_D(dc, rs1); - cpu_src2_64 = gen_load_fpr_D(dc, rs2); - gen_op_fcmpd(rd & 3, cpu_src1_64, cpu_src2_64); - break; - case 0x53: /* fcmpq, V9 %fcc */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_op_load_fpr_QT0(QFPREG(rs1)); - gen_op_load_fpr_QT1(QFPREG(rs2)); - gen_op_fcmpq(rd & 3); - break; - case 0x55: /* fcmpes, V9 %fcc */ - cpu_src1_32 = gen_load_fpr_F(dc, rs1); - cpu_src2_32 = gen_load_fpr_F(dc, rs2); - gen_op_fcmpes(rd & 3, cpu_src1_32, cpu_src2_32); - break; - case 0x56: /* fcmped, V9 %fcc */ - cpu_src1_64 = gen_load_fpr_D(dc, rs1); - cpu_src2_64 = gen_load_fpr_D(dc, rs2); - gen_op_fcmped(rd & 3, cpu_src1_64, cpu_src2_64); - break; - case 0x57: /* fcmpeq, V9 %fcc */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_op_load_fpr_QT0(QFPREG(rs1)); - gen_op_load_fpr_QT1(QFPREG(rs2)); - gen_op_fcmpeq(rd & 3); - break; - default: - goto illegal_insn; - } + goto illegal_insn; /* in decodetree */ } else if (xop == 0x36) { #ifdef TARGET_SPARC64 /* VIS */ + TCGv_i64 cpu_src1_64, cpu_src2_64, cpu_dst_64; + TCGv_i32 cpu_dst_32; + TCGv cpu_dst = tcg_temp_new(); int opf = GET_FIELD_SP(insn, 5, 13); - rs1 = GET_FIELD(insn, 13, 17); - rs2 = GET_FIELD(insn, 27, 31); + int rs1 = GET_FIELD(insn, 13, 17); + int rs2 = GET_FIELD(insn, 27, 31); + int rd = GET_FIELD(insn, 2, 6); + if (gen_trap_ifnofpu(dc)) { goto jmp_insn; } @@ -5392,14 +5421,18 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) goto illegal_insn; /* in decodetree */ } advance_pc(dc); +#ifdef TARGET_SPARC64 jmp_insn: +#endif return; illegal_insn: gen_exception(dc, TT_ILL_INSN); return; +#ifdef TARGET_SPARC64 nfpu_insn: gen_op_fpexception_im(dc, FSR_FTT_UNIMPFPOP); return; +#endif } static void sparc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) From e2fa6bd1ad7c2167e628ac49208b52843fca2c1f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Oct 2023 21:26:06 -0700 Subject: [PATCH 105/974] target/sparc: Move FPCMP* to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 9 ++++ target/sparc/translate.c | 90 +++++++++++++++++---------------------- 2 files changed, 47 insertions(+), 52 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index f197bb0b36..781c3cd7f7 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -327,6 +327,15 @@ FCMPEq 10 000 cc:2 110101 rs1:5 0 0101 0111 rs2:5 BMASK 10 ..... 110110 ..... 0 0001 1001 ..... @r_r_r + FPCMPLE16 10 ..... 110110 ..... 0 0010 0000 ..... @r_r_r + FPCMPNE16 10 ..... 110110 ..... 0 0010 0010 ..... @r_r_r + FPCMPGT16 10 ..... 110110 ..... 0 0010 1000 ..... @r_r_r + FPCMPEQ16 10 ..... 110110 ..... 0 0010 1010 ..... @r_r_r + FPCMPLE32 10 ..... 110110 ..... 0 0010 0100 ..... @r_r_r + FPCMPNE32 10 ..... 110110 ..... 0 0010 0110 ..... @r_r_r + FPCMPGT32 10 ..... 110110 ..... 0 0010 1100 ..... @r_r_r + FPCMPEQ32 10 ..... 110110 ..... 0 0010 1110 ..... @r_r_r + FMUL8x16 10 ..... 110110 ..... 0 0011 0001 ..... @r_r_r FMUL8x16AU 10 ..... 110110 ..... 0 0011 0011 ..... @r_r_r FMUL8x16AL 10 ..... 110110 ..... 0 0011 0101 ..... @r_r_r diff --git a/target/sparc/translate.c b/target/sparc/translate.c index ddde64dfb4..3ee1015cc4 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -64,6 +64,14 @@ # define gen_helper_wrpil(E, S) qemu_build_not_reached() # define gen_helper_wrpstate(E, S) qemu_build_not_reached() # define gen_helper_fabsq ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fcmpeq16 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fcmpeq32 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fcmpgt16 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fcmpgt32 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fcmple16 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fcmple32 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fcmpne16 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fcmpne32 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fdtox ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fexpand ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fmul8sux16 ({ qemu_build_not_reached(); NULL; }) @@ -4947,6 +4955,34 @@ TRANS(FPACK32, VIS1, do_ddd, a, gen_op_fpack32) TRANS(FALIGNDATAg, VIS1, do_ddd, a, gen_op_faligndata) TRANS(BSHUFFLE, VIS2, do_ddd, a, gen_op_bshuffle) +static bool do_rdd(DisasContext *dc, arg_r_r_r *a, + void (*func)(TCGv, TCGv_i64, TCGv_i64)) +{ + TCGv_i64 src1, src2; + TCGv dst; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + dst = gen_dest_gpr(dc, a->rd); + src1 = gen_load_fpr_D(dc, a->rs1); + src2 = gen_load_fpr_D(dc, a->rs2); + func(dst, src1, src2); + gen_store_gpr(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(FPCMPLE16, VIS1, do_rdd, a, gen_helper_fcmple16) +TRANS(FPCMPNE16, VIS1, do_rdd, a, gen_helper_fcmpne16) +TRANS(FPCMPGT16, VIS1, do_rdd, a, gen_helper_fcmpgt16) +TRANS(FPCMPEQ16, VIS1, do_rdd, a, gen_helper_fcmpeq16) + +TRANS(FPCMPLE32, VIS1, do_rdd, a, gen_helper_fcmple32) +TRANS(FPCMPNE32, VIS1, do_rdd, a, gen_helper_fcmpne32) +TRANS(FPCMPGT32, VIS1, do_rdd, a, gen_helper_fcmpgt32) +TRANS(FPCMPEQ32, VIS1, do_rdd, a, gen_helper_fcmpeq32) + static bool do_env_ddd(DisasContext *dc, arg_r_r_r *a, void (*func)(TCGv_i64, TCGv_env, TCGv_i64, TCGv_i64)) { @@ -5229,11 +5265,9 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) } else if (xop == 0x36) { #ifdef TARGET_SPARC64 /* VIS */ - TCGv_i64 cpu_src1_64, cpu_src2_64, cpu_dst_64; + TCGv_i64 cpu_src1_64, cpu_dst_64; TCGv_i32 cpu_dst_32; - TCGv cpu_dst = tcg_temp_new(); int opf = GET_FIELD_SP(insn, 5, 13); - int rs1 = GET_FIELD(insn, 13, 17); int rs2 = GET_FIELD(insn, 27, 31); int rd = GET_FIELD(insn, 2, 6); @@ -5309,63 +5343,15 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x03a: /* VIS I fpack32 */ case 0x048: /* VIS I faligndata */ case 0x04c: /* VIS II bshuffle */ - g_assert_not_reached(); /* in decodetree */ case 0x020: /* VIS I fcmple16 */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1_64 = gen_load_fpr_D(dc, rs1); - cpu_src2_64 = gen_load_fpr_D(dc, rs2); - gen_helper_fcmple16(cpu_dst, cpu_src1_64, cpu_src2_64); - gen_store_gpr(dc, rd, cpu_dst); - break; case 0x022: /* VIS I fcmpne16 */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1_64 = gen_load_fpr_D(dc, rs1); - cpu_src2_64 = gen_load_fpr_D(dc, rs2); - gen_helper_fcmpne16(cpu_dst, cpu_src1_64, cpu_src2_64); - gen_store_gpr(dc, rd, cpu_dst); - break; case 0x024: /* VIS I fcmple32 */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1_64 = gen_load_fpr_D(dc, rs1); - cpu_src2_64 = gen_load_fpr_D(dc, rs2); - gen_helper_fcmple32(cpu_dst, cpu_src1_64, cpu_src2_64); - gen_store_gpr(dc, rd, cpu_dst); - break; case 0x026: /* VIS I fcmpne32 */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1_64 = gen_load_fpr_D(dc, rs1); - cpu_src2_64 = gen_load_fpr_D(dc, rs2); - gen_helper_fcmpne32(cpu_dst, cpu_src1_64, cpu_src2_64); - gen_store_gpr(dc, rd, cpu_dst); - break; case 0x028: /* VIS I fcmpgt16 */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1_64 = gen_load_fpr_D(dc, rs1); - cpu_src2_64 = gen_load_fpr_D(dc, rs2); - gen_helper_fcmpgt16(cpu_dst, cpu_src1_64, cpu_src2_64); - gen_store_gpr(dc, rd, cpu_dst); - break; case 0x02a: /* VIS I fcmpeq16 */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1_64 = gen_load_fpr_D(dc, rs1); - cpu_src2_64 = gen_load_fpr_D(dc, rs2); - gen_helper_fcmpeq16(cpu_dst, cpu_src1_64, cpu_src2_64); - gen_store_gpr(dc, rd, cpu_dst); - break; case 0x02c: /* VIS I fcmpgt32 */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1_64 = gen_load_fpr_D(dc, rs1); - cpu_src2_64 = gen_load_fpr_D(dc, rs2); - gen_helper_fcmpgt32(cpu_dst, cpu_src1_64, cpu_src2_64); - gen_store_gpr(dc, rd, cpu_dst); - break; case 0x02e: /* VIS I fcmpeq32 */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1_64 = gen_load_fpr_D(dc, rs1); - cpu_src2_64 = gen_load_fpr_D(dc, rs2); - gen_helper_fcmpeq32(cpu_dst, cpu_src1_64, cpu_src2_64); - gen_store_gpr(dc, rd, cpu_dst); - break; + g_assert_not_reached(); /* in decodetree */ case 0x03b: /* VIS I fpack16 */ CHECK_FPU_FEATURE(dc, VIS1); cpu_src1_64 = gen_load_fpr_D(dc, rs2); From 2f72264169c2b9c94622a609fdc28c5d7ad77614 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Oct 2023 21:36:35 -0700 Subject: [PATCH 106/974] target/sparc: Move FPACK16, FPACKFIX to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 2 ++ target/sparc/translate.c | 55 ++++++++++++++++++++++++++++----------- 2 files changed, 42 insertions(+), 15 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 781c3cd7f7..18a840709f 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -344,6 +344,8 @@ FCMPEq 10 000 cc:2 110101 rs1:5 0 0101 0111 rs2:5 FMULD8SUx16 10 ..... 110110 ..... 0 0011 1000 ..... @r_r_r FMULD8ULx16 10 ..... 110110 ..... 0 0011 1001 ..... @r_r_r FPACK32 10 ..... 110110 ..... 0 0011 1010 ..... @r_r_r + FPACK16 10 ..... 110110 00000 0 0011 1011 ..... @r_r2 + FPACKFIX 10 ..... 110110 00000 0 0011 1101 ..... @r_r2 PDIST 10 ..... 110110 ..... 0 0011 1110 ..... @r_r_r FALIGNDATAg 10 ..... 110110 ..... 0 0100 1000 ..... @r_r_r diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 3ee1015cc4..15c91c6caa 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -756,6 +756,24 @@ static void gen_op_array32(TCGv dst, TCGv src1, TCGv src2) tcg_gen_shli_tl(dst, dst, 2); } +static void gen_op_fpack16(TCGv_i32 dst, TCGv_i64 src) +{ +#ifdef TARGET_SPARC64 + gen_helper_fpack16(dst, cpu_gsr, src); +#else + g_assert_not_reached(); +#endif +} + +static void gen_op_fpackfix(TCGv_i32 dst, TCGv_i64 src) +{ +#ifdef TARGET_SPARC64 + gen_helper_fpackfix(dst, cpu_gsr, src); +#else + g_assert_not_reached(); +#endif +} + static void gen_op_fpack32(TCGv_i64 dst, TCGv_i64 src1, TCGv_i64 src2) { #ifdef TARGET_SPARC64 @@ -4589,6 +4607,26 @@ TRANS(FABSs, ALL, do_ff, a, gen_op_fabss) TRANS(FSRCs, VIS1, do_ff, a, tcg_gen_mov_i32) TRANS(FNOTs, VIS1, do_ff, a, tcg_gen_not_i32) +static bool do_fd(DisasContext *dc, arg_r_r *a, + void (*func)(TCGv_i32, TCGv_i64)) +{ + TCGv_i32 dst; + TCGv_i64 src; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + dst = gen_dest_fpr_F(dc); + src = gen_load_fpr_D(dc, a->rs); + func(dst, src); + gen_store_fpr_F(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(FPACK16, VIS1, do_fd, a, gen_op_fpack16) +TRANS(FPACKFIX, VIS1, do_fd, a, gen_op_fpackfix) + static bool do_env_ff(DisasContext *dc, arg_r_r *a, void (*func)(TCGv_i32, TCGv_env, TCGv_i32)) { @@ -5265,10 +5303,9 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) } else if (xop == 0x36) { #ifdef TARGET_SPARC64 /* VIS */ - TCGv_i64 cpu_src1_64, cpu_dst_64; + TCGv_i64 cpu_dst_64; TCGv_i32 cpu_dst_32; int opf = GET_FIELD_SP(insn, 5, 13); - int rs2 = GET_FIELD(insn, 27, 31); int rd = GET_FIELD(insn, 2, 6); if (gen_trap_ifnofpu(dc)) { @@ -5351,21 +5388,9 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x02a: /* VIS I fcmpeq16 */ case 0x02c: /* VIS I fcmpgt32 */ case 0x02e: /* VIS I fcmpeq32 */ - g_assert_not_reached(); /* in decodetree */ case 0x03b: /* VIS I fpack16 */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1_64 = gen_load_fpr_D(dc, rs2); - cpu_dst_32 = gen_dest_fpr_F(dc); - gen_helper_fpack16(cpu_dst_32, cpu_gsr, cpu_src1_64); - gen_store_fpr_F(dc, rd, cpu_dst_32); - break; case 0x03d: /* VIS I fpackfix */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1_64 = gen_load_fpr_D(dc, rs2); - cpu_dst_32 = gen_dest_fpr_F(dc); - gen_helper_fpackfix(cpu_dst_32, cpu_gsr, cpu_src1_64); - gen_store_fpr_F(dc, rd, cpu_dst_32); - break; + g_assert_not_reached(); /* in decodetree */ case 0x060: /* VIS I fzero */ CHECK_FPU_FEATURE(dc, VIS1); cpu_dst_64 = gen_dest_fpr_D(dc, rd); From 3a38260e3caf9e14cb4065253ceb942c29fb77f4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Oct 2023 21:50:11 -0700 Subject: [PATCH 107/974] target/sparc: Convert FZERO, FONE to decodetree Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 5 +++ target/sparc/translate.c | 69 +++++++++++++++++++++++---------------- 2 files changed, 45 insertions(+), 29 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 18a840709f..0552f1447d 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -391,6 +391,11 @@ FCMPEq 10 000 cc:2 110101 rs1:5 0 0101 0111 rs2:5 FORNOTs 10 ..... 110110 ..... 0 0111 1011 ..... @r_r_r_swap # ... 1s FORd 10 ..... 110110 ..... 0 0111 1100 ..... @r_r_r FORs 10 ..... 110110 ..... 0 0111 1101 ..... @r_r_r + + FZEROd 10 rd:5 110110 00000 0 0110 0000 00000 + FZEROs 10 rd:5 110110 00000 0 0110 0001 00000 + FONEd 10 rd:5 110110 00000 0 0111 1110 00000 + FONEs 10 rd:5 110110 00000 0 0111 1111 00000 ] NCP 10 ----- 110110 ----- --------- ----- # v8 CPop1 } diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 15c91c6caa..62c77ae5e1 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -4586,6 +4586,45 @@ static bool do_stfsr(DisasContext *dc, arg_r_r_ri *a, MemOp mop) TRANS(STFSR, ALL, do_stfsr, a, MO_TEUL) TRANS(STXFSR, 64, do_stfsr, a, MO_TEUQ) +static bool do_fc(DisasContext *dc, int rd, bool c) +{ + uint64_t mask; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + if (rd & 1) { + mask = MAKE_64BIT_MASK(0, 32); + } else { + mask = MAKE_64BIT_MASK(32, 32); + } + if (c) { + tcg_gen_ori_i64(cpu_fpr[rd / 2], cpu_fpr[rd / 2], mask); + } else { + tcg_gen_andi_i64(cpu_fpr[rd / 2], cpu_fpr[rd / 2], ~mask); + } + gen_update_fprs_dirty(dc, rd); + return advance_pc(dc); +} + +TRANS(FZEROs, VIS1, do_fc, a->rd, 0) +TRANS(FONEs, VIS1, do_fc, a->rd, 1) + +static bool do_dc(DisasContext *dc, int rd, int64_t c) +{ + if (gen_trap_ifnofpu(dc)) { + return true; + } + + tcg_gen_movi_i64(cpu_fpr[rd / 2], c); + gen_update_fprs_dirty(dc, rd); + return advance_pc(dc); +} + +TRANS(FZEROd, VIS1, do_dc, a->rd, 0) +TRANS(FONEd, VIS1, do_dc, a->rd, -1) + static bool do_ff(DisasContext *dc, arg_r_r *a, void (*func)(TCGv_i32, TCGv_i32)) { @@ -5303,10 +5342,7 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) } else if (xop == 0x36) { #ifdef TARGET_SPARC64 /* VIS */ - TCGv_i64 cpu_dst_64; - TCGv_i32 cpu_dst_32; int opf = GET_FIELD_SP(insn, 5, 13); - int rd = GET_FIELD(insn, 2, 6); if (gen_trap_ifnofpu(dc)) { goto jmp_insn; @@ -5390,31 +5426,11 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) case 0x02e: /* VIS I fcmpeq32 */ case 0x03b: /* VIS I fpack16 */ case 0x03d: /* VIS I fpackfix */ - g_assert_not_reached(); /* in decodetree */ case 0x060: /* VIS I fzero */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_dst_64 = gen_dest_fpr_D(dc, rd); - tcg_gen_movi_i64(cpu_dst_64, 0); - gen_store_fpr_D(dc, rd, cpu_dst_64); - break; case 0x061: /* VIS I fzeros */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_dst_32 = gen_dest_fpr_F(dc); - tcg_gen_movi_i32(cpu_dst_32, 0); - gen_store_fpr_F(dc, rd, cpu_dst_32); - break; case 0x07e: /* VIS I fone */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_dst_64 = gen_dest_fpr_D(dc, rd); - tcg_gen_movi_i64(cpu_dst_64, -1); - gen_store_fpr_D(dc, rd, cpu_dst_64); - break; case 0x07f: /* VIS I fones */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_dst_32 = gen_dest_fpr_F(dc); - tcg_gen_movi_i32(cpu_dst_32, -1); - gen_store_fpr_F(dc, rd, cpu_dst_32); - break; + g_assert_not_reached(); /* in decodetree */ case 0x080: /* VIS I shutdown */ case 0x081: /* VIS II siam */ // XXX @@ -5439,11 +5455,6 @@ static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) illegal_insn: gen_exception(dc, TT_ILL_INSN); return; -#ifdef TARGET_SPARC64 - nfpu_insn: - gen_op_fpexception_im(dc, FSR_FTT_UNIMPFPOP); - return; -#endif } static void sparc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) From ba9c09b40b8e19ec50955216b61875d64042fa99 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 11 Oct 2023 21:52:20 -0700 Subject: [PATCH 108/974] target/sparc: Remove disas_sparc_legacy All instructions are now converted. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/translate.c | 145 +-------------------------------------- 1 file changed, 1 insertion(+), 144 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 62c77ae5e1..986a88c4e1 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -5314,149 +5314,6 @@ static bool do_fcmpq(DisasContext *dc, arg_FCMPq *a, bool e) TRANS(FCMPq, ALL, do_fcmpq, a, false) TRANS(FCMPEq, ALL, do_fcmpq, a, true) -#define CHECK_IU_FEATURE(dc, FEATURE) \ - if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ - goto illegal_insn; -#define CHECK_FPU_FEATURE(dc, FEATURE) \ - if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ - goto nfpu_insn; - -/* before an instruction, dc->pc must be static */ -static void disas_sparc_legacy(DisasContext *dc, unsigned int insn) -{ - unsigned int opc = GET_FIELD(insn, 0, 1); - - switch (opc) { - case 0: - goto illegal_insn; /* in decodetree */ - case 1: - g_assert_not_reached(); /* in decodetree */ - case 2: /* FPU & Logical Operations */ - { - unsigned int xop = GET_FIELD(insn, 7, 12); - - if (xop == 0x34) { /* FPU Operations */ - goto illegal_insn; /* in decodetree */ - } else if (xop == 0x35) { /* FPU Operations */ - goto illegal_insn; /* in decodetree */ - } else if (xop == 0x36) { -#ifdef TARGET_SPARC64 - /* VIS */ - int opf = GET_FIELD_SP(insn, 5, 13); - - if (gen_trap_ifnofpu(dc)) { - goto jmp_insn; - } - - switch (opf) { - case 0x000: /* VIS I edge8cc */ - case 0x001: /* VIS II edge8n */ - case 0x002: /* VIS I edge8lcc */ - case 0x003: /* VIS II edge8ln */ - case 0x004: /* VIS I edge16cc */ - case 0x005: /* VIS II edge16n */ - case 0x006: /* VIS I edge16lcc */ - case 0x007: /* VIS II edge16ln */ - case 0x008: /* VIS I edge32cc */ - case 0x009: /* VIS II edge32n */ - case 0x00a: /* VIS I edge32lcc */ - case 0x00b: /* VIS II edge32ln */ - case 0x010: /* VIS I array8 */ - case 0x012: /* VIS I array16 */ - case 0x014: /* VIS I array32 */ - case 0x018: /* VIS I alignaddr */ - case 0x01a: /* VIS I alignaddrl */ - case 0x019: /* VIS II bmask */ - case 0x067: /* VIS I fnot2s */ - case 0x06b: /* VIS I fnot1s */ - case 0x075: /* VIS I fsrc1s */ - case 0x079: /* VIS I fsrc2s */ - case 0x066: /* VIS I fnot2 */ - case 0x06a: /* VIS I fnot1 */ - case 0x074: /* VIS I fsrc1 */ - case 0x078: /* VIS I fsrc2 */ - case 0x051: /* VIS I fpadd16s */ - case 0x053: /* VIS I fpadd32s */ - case 0x055: /* VIS I fpsub16s */ - case 0x057: /* VIS I fpsub32s */ - case 0x063: /* VIS I fnors */ - case 0x065: /* VIS I fandnot2s */ - case 0x069: /* VIS I fandnot1s */ - case 0x06d: /* VIS I fxors */ - case 0x06f: /* VIS I fnands */ - case 0x071: /* VIS I fands */ - case 0x073: /* VIS I fxnors */ - case 0x077: /* VIS I fornot2s */ - case 0x07b: /* VIS I fornot1s */ - case 0x07d: /* VIS I fors */ - case 0x050: /* VIS I fpadd16 */ - case 0x052: /* VIS I fpadd32 */ - case 0x054: /* VIS I fpsub16 */ - case 0x056: /* VIS I fpsub32 */ - case 0x062: /* VIS I fnor */ - case 0x064: /* VIS I fandnot2 */ - case 0x068: /* VIS I fandnot1 */ - case 0x06c: /* VIS I fxor */ - case 0x06e: /* VIS I fnand */ - case 0x070: /* VIS I fand */ - case 0x072: /* VIS I fxnor */ - case 0x076: /* VIS I fornot2 */ - case 0x07a: /* VIS I fornot1 */ - case 0x07c: /* VIS I for */ - case 0x031: /* VIS I fmul8x16 */ - case 0x033: /* VIS I fmul8x16au */ - case 0x035: /* VIS I fmul8x16al */ - case 0x036: /* VIS I fmul8sux16 */ - case 0x037: /* VIS I fmul8ulx16 */ - case 0x038: /* VIS I fmuld8sux16 */ - case 0x039: /* VIS I fmuld8ulx16 */ - case 0x04b: /* VIS I fpmerge */ - case 0x04d: /* VIS I fexpand */ - case 0x03e: /* VIS I pdist */ - case 0x03a: /* VIS I fpack32 */ - case 0x048: /* VIS I faligndata */ - case 0x04c: /* VIS II bshuffle */ - case 0x020: /* VIS I fcmple16 */ - case 0x022: /* VIS I fcmpne16 */ - case 0x024: /* VIS I fcmple32 */ - case 0x026: /* VIS I fcmpne32 */ - case 0x028: /* VIS I fcmpgt16 */ - case 0x02a: /* VIS I fcmpeq16 */ - case 0x02c: /* VIS I fcmpgt32 */ - case 0x02e: /* VIS I fcmpeq32 */ - case 0x03b: /* VIS I fpack16 */ - case 0x03d: /* VIS I fpackfix */ - case 0x060: /* VIS I fzero */ - case 0x061: /* VIS I fzeros */ - case 0x07e: /* VIS I fone */ - case 0x07f: /* VIS I fones */ - g_assert_not_reached(); /* in decodetree */ - case 0x080: /* VIS I shutdown */ - case 0x081: /* VIS II siam */ - // XXX - goto illegal_insn; - default: - goto illegal_insn; - } -#endif - } else { - goto illegal_insn; /* in decodetree */ - } - } - break; - case 3: /* load/store instructions */ - goto illegal_insn; /* in decodetree */ - } - advance_pc(dc); -#ifdef TARGET_SPARC64 - jmp_insn: -#endif - return; - illegal_insn: - gen_exception(dc, TT_ILL_INSN); - return; -} - static void sparc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *dc = container_of(dcbase, DisasContext, base); @@ -5524,7 +5381,7 @@ static void sparc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) dc->base.pc_next += 4; if (!decode(dc, insn)) { - disas_sparc_legacy(dc, insn); + gen_exception(dc, TT_ILL_INSN); } if (dc->base.is_jmp == DISAS_NORETURN) { From 45b5933f7afb055080e915c83663f3a4709a02db Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 20 Oct 2023 00:51:34 +0200 Subject: [PATCH 109/974] tests/tcg: fix out-of-bounds access in test-avx This can cause differences between native and QEMU execution, due to ASLR. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- tests/tcg/i386/test-avx.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/tcg/i386/test-avx.c b/tests/tcg/i386/test-avx.c index c39c0e5bce..910b067353 100644 --- a/tests/tcg/i386/test-avx.c +++ b/tests/tcg/i386/test-avx.c @@ -236,12 +236,15 @@ v4di val_i64[] = { v4di deadbeef = {0xa5a5a5a5deadbeefull, 0xa5a5a5a5deadbeefull, 0xa5a5a5a5deadbeefull, 0xa5a5a5a5deadbeefull}; -v4di indexq = {0x000000000000001full, 0x000000000000008full, - 0xffffffffffffffffull, 0xffffffffffffff5full}; -v4di indexd = {0x00000002000000efull, 0xfffffff500000010ull, - 0x0000000afffffff0ull, 0x000000000000000eull}; +/* &gather_mem[0x10] is 512 bytes from the base; indices must be >=-64, <64 + * to account for scaling by 8 */ +v4di indexq = {0x000000000000001full, 0x000000000000003dull, + 0xffffffffffffffffull, 0xffffffffffffffdfull}; +v4di indexd = {0x00000002ffffffcdull, 0xfffffff500000010ull, + 0x0000003afffffff0ull, 0x000000000000000eull}; v4di gather_mem[0x20]; +_Static_assert(sizeof(gather_mem) == 1024); void init_f16reg(v4di *r) { From e582b629f0b50c10137ba47c4ca7fe30b3357e3d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 10 Oct 2023 10:31:17 +0200 Subject: [PATCH 110/974] target/i386: implement SHA instructions The implementation was validated with OpenSSL and with the test vectors in https://github.com/rust-lang/stdarch/blob/master/crates/core_arch/src/x86/sha.rs. The instructions provide a ~25% improvement on hashing a 64 MiB file: runtime goes down from 1.8 seconds to 1.4 seconds; instruction count on the host goes down from 5.8 billion to 4.8 billion with slightly better IPC too. Good job Intel. ;) Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 2 +- target/i386/ops_sse.h | 128 +++++++++++++++++++++++++++ target/i386/tcg/decode-new.c.inc | 11 +++ target/i386/tcg/decode-new.h | 1 + target/i386/tcg/emit.c.inc | 54 +++++++++++ target/i386/tcg/ops_sse_header.h.inc | 14 +++ 6 files changed, 209 insertions(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index bdca901dfa..070c02000f 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -714,7 +714,7 @@ void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, CPUID_7_0_EBX_PCOMMIT | CPUID_7_0_EBX_CLFLUSHOPT | \ CPUID_7_0_EBX_CLWB | CPUID_7_0_EBX_MPX | CPUID_7_0_EBX_FSGSBASE | \ CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_RDSEED | \ - CPUID_7_0_EBX_KERNEL_FEATURES) + CPUID_7_0_EBX_SHA_NI | CPUID_7_0_EBX_KERNEL_FEATURES) /* missing: CPUID_7_0_EBX_HLE CPUID_7_0_EBX_INVPCID, CPUID_7_0_EBX_RTM */ diff --git a/target/i386/ops_sse.h b/target/i386/ops_sse.h index 33908c0691..6a465a35fd 100644 --- a/target/i386/ops_sse.h +++ b/target/i386/ops_sse.h @@ -2527,6 +2527,134 @@ SSE_HELPER_FMAP(helper_fma4ps, ZMM_S, 2 << SHIFT, float32_muladd) SSE_HELPER_FMAP(helper_fma4pd, ZMM_D, 1 << SHIFT, float64_muladd) #endif +#if SHIFT == 1 +#define SSE_HELPER_SHA1RNDS4(name, F, K) \ + void name(Reg *d, Reg *a, Reg *b) \ + { \ + uint32_t A, B, C, D, E, t, i; \ + \ + A = a->L(3); \ + B = a->L(2); \ + C = a->L(1); \ + D = a->L(0); \ + E = 0; \ + \ + for (i = 0; i <= 3; i++) { \ + t = F(B, C, D) + rol32(A, 5) + b->L(3 - i) + E + K; \ + E = D; \ + D = C; \ + C = rol32(B, 30); \ + B = A; \ + A = t; \ + } \ + \ + d->L(3) = A; \ + d->L(2) = B; \ + d->L(1) = C; \ + d->L(0) = D; \ + } + +#define SHA1_F0(b, c, d) (((b) & (c)) ^ (~(b) & (d))) +#define SHA1_F1(b, c, d) ((b) ^ (c) ^ (d)) +#define SHA1_F2(b, c, d) (((b) & (c)) ^ ((b) & (d)) ^ ((c) & (d))) + +SSE_HELPER_SHA1RNDS4(helper_sha1rnds4_f0, SHA1_F0, 0x5A827999) +SSE_HELPER_SHA1RNDS4(helper_sha1rnds4_f1, SHA1_F1, 0x6ED9EBA1) +SSE_HELPER_SHA1RNDS4(helper_sha1rnds4_f2, SHA1_F2, 0x8F1BBCDC) +SSE_HELPER_SHA1RNDS4(helper_sha1rnds4_f3, SHA1_F1, 0xCA62C1D6) + +void helper_sha1nexte(Reg *d, Reg *a, Reg *b) +{ + d->L(3) = b->L(3) + rol32(a->L(3), 30); + d->L(2) = b->L(2); + d->L(1) = b->L(1); + d->L(0) = b->L(0); +} + +void helper_sha1msg1(Reg *d, Reg *a, Reg *b) +{ + /* These could be overwritten by the first two assignments, save them. */ + uint32_t b3 = b->L(3); + uint32_t b2 = b->L(2); + + d->L(3) = a->L(3) ^ a->L(1); + d->L(2) = a->L(2) ^ a->L(0); + d->L(1) = a->L(1) ^ b3; + d->L(0) = a->L(0) ^ b2; +} + +void helper_sha1msg2(Reg *d, Reg *a, Reg *b) +{ + d->L(3) = rol32(a->L(3) ^ b->L(2), 1); + d->L(2) = rol32(a->L(2) ^ b->L(1), 1); + d->L(1) = rol32(a->L(1) ^ b->L(0), 1); + d->L(0) = rol32(a->L(0) ^ d->L(3), 1); +} + +#define SHA256_CH(e, f, g) (((e) & (f)) ^ (~(e) & (g))) +#define SHA256_MAJ(a, b, c) (((a) & (b)) ^ ((a) & (c)) ^ ((b) & (c))) + +#define SHA256_RNDS0(w) (ror32((w), 2) ^ ror32((w), 13) ^ ror32((w), 22)) +#define SHA256_RNDS1(w) (ror32((w), 6) ^ ror32((w), 11) ^ ror32((w), 25)) +#define SHA256_MSGS0(w) (ror32((w), 7) ^ ror32((w), 18) ^ ((w) >> 3)) +#define SHA256_MSGS1(w) (ror32((w), 17) ^ ror32((w), 19) ^ ((w) >> 10)) + +void helper_sha256rnds2(Reg *d, Reg *a, Reg *b, uint32_t wk0, uint32_t wk1) +{ + uint32_t t, AA, EE; + + uint32_t A = b->L(3); + uint32_t B = b->L(2); + uint32_t C = a->L(3); + uint32_t D = a->L(2); + uint32_t E = b->L(1); + uint32_t F = b->L(0); + uint32_t G = a->L(1); + uint32_t H = a->L(0); + + /* Even round */ + t = SHA256_CH(E, F, G) + SHA256_RNDS1(E) + wk0 + H; + AA = t + SHA256_MAJ(A, B, C) + SHA256_RNDS0(A); + EE = t + D; + + /* These will be B and F at the end of the odd round */ + d->L(2) = AA; + d->L(0) = EE; + + D = C, C = B, B = A, A = AA; + H = G, G = F, F = E, E = EE; + + /* Odd round */ + t = SHA256_CH(E, F, G) + SHA256_RNDS1(E) + wk1 + H; + AA = t + SHA256_MAJ(A, B, C) + SHA256_RNDS0(A); + EE = t + D; + + d->L(3) = AA; + d->L(1) = EE; +} + +void helper_sha256msg1(Reg *d, Reg *a, Reg *b) +{ + /* b->L(0) could be overwritten by the first assignment, save it. */ + uint32_t b0 = b->L(0); + + d->L(0) = a->L(0) + SHA256_MSGS0(a->L(1)); + d->L(1) = a->L(1) + SHA256_MSGS0(a->L(2)); + d->L(2) = a->L(2) + SHA256_MSGS0(a->L(3)); + d->L(3) = a->L(3) + SHA256_MSGS0(b0); +} + +void helper_sha256msg2(Reg *d, Reg *a, Reg *b) +{ + /* Earlier assignments cannot overwrite any of the two operands. */ + d->L(0) = a->L(0) + SHA256_MSGS1(b->L(2)); + d->L(1) = a->L(1) + SHA256_MSGS1(b->L(3)); + /* Yes, this reuses the previously computed values. */ + d->L(2) = a->L(2) + SHA256_MSGS1(d->L(0)); + d->L(3) = a->L(3) + SHA256_MSGS1(d->L(1)); +} +#endif + #undef SSE_HELPER_S #undef LANE_WIDTH diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index 7d76f15275..ec5d260b7e 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -460,6 +460,13 @@ static const X86OpEntry opcodes_0F38_00toEF[240] = { [0xbe] = X86_OP_ENTRY3(VFNMSUB231Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66), [0xbf] = X86_OP_ENTRY3(VFNMSUB231Sx, V,x, H,x, W,x, vex6 cpuid(FMA) p_66), + [0xc8] = X86_OP_ENTRY2(SHA1NEXTE, V,dq, W,dq, cpuid(SHA_NI)), + [0xc9] = X86_OP_ENTRY2(SHA1MSG1, V,dq, W,dq, cpuid(SHA_NI)), + [0xca] = X86_OP_ENTRY2(SHA1MSG2, V,dq, W,dq, cpuid(SHA_NI)), + [0xcb] = X86_OP_ENTRY2(SHA256RNDS2, V,dq, W,dq, cpuid(SHA_NI)), + [0xcc] = X86_OP_ENTRY2(SHA256MSG1, V,dq, W,dq, cpuid(SHA_NI)), + [0xcd] = X86_OP_ENTRY2(SHA256MSG2, V,dq, W,dq, cpuid(SHA_NI)), + [0xdb] = X86_OP_ENTRY3(VAESIMC, V,dq, None,None, W,dq, vex4 cpuid(AES) p_66), [0xdc] = X86_OP_ENTRY3(VAESENC, V,x, H,x, W,x, vex4 cpuid(AES) p_66), [0xdd] = X86_OP_ENTRY3(VAESENCLAST, V,x, H,x, W,x, vex4 cpuid(AES) p_66), @@ -609,6 +616,8 @@ static const X86OpEntry opcodes_0F3A[256] = { [0x4b] = X86_OP_ENTRY4(VBLENDVPD, V,x, H,x, W,x, vex6 cpuid(AVX) p_66), [0x4c] = X86_OP_ENTRY4(VPBLENDVB, V,x, H,x, W,x, vex6 cpuid(AVX) p_66 avx2_256), + [0xcc] = X86_OP_ENTRY3(SHA1RNDS4, V,dq, W,dq, I,b, cpuid(SHA_NI)), + [0xdf] = X86_OP_ENTRY3(VAESKEYGEN, V,dq, W,dq, I,b, vex4 cpuid(AES) p_66), [0xF0] = X86_OP_ENTRY3(RORX, G,y, E,y, I,b, vex13 cpuid(BMI2) p_f2), @@ -1456,6 +1465,8 @@ static bool has_cpuid_feature(DisasContext *s, X86CPUIDFeature cpuid) return (s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI2); case X86_FEAT_AVX2: return (s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_AVX2); + case X86_FEAT_SHA_NI: + return (s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_SHA_NI); } g_assert_not_reached(); } diff --git a/target/i386/tcg/decode-new.h b/target/i386/tcg/decode-new.h index a542ec1681..9be8a6e65f 100644 --- a/target/i386/tcg/decode-new.h +++ b/target/i386/tcg/decode-new.h @@ -108,6 +108,7 @@ typedef enum X86CPUIDFeature { X86_FEAT_FMA, X86_FEAT_MOVBE, X86_FEAT_PCLMULQDQ, + X86_FEAT_SHA_NI, X86_FEAT_SSE, X86_FEAT_SSE2, X86_FEAT_SSE3, diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 88793ba988..16085a19d7 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -1800,6 +1800,60 @@ static void gen_SARX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) tcg_gen_sar_tl(s->T0, s->T0, s->T1); } +static void gen_SHA1NEXTE(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_helper_sha1nexte(OP_PTR0, OP_PTR1, OP_PTR2); +} + +static void gen_SHA1MSG1(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_helper_sha1msg1(OP_PTR0, OP_PTR1, OP_PTR2); +} + +static void gen_SHA1MSG2(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_helper_sha1msg2(OP_PTR0, OP_PTR1, OP_PTR2); +} + +static void gen_SHA1RNDS4(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + switch(decode->immediate & 3) { + case 0: + gen_helper_sha1rnds4_f0(OP_PTR0, OP_PTR0, OP_PTR1); + break; + case 1: + gen_helper_sha1rnds4_f1(OP_PTR0, OP_PTR0, OP_PTR1); + break; + case 2: + gen_helper_sha1rnds4_f2(OP_PTR0, OP_PTR0, OP_PTR1); + break; + case 3: + gen_helper_sha1rnds4_f3(OP_PTR0, OP_PTR0, OP_PTR1); + break; + } +} + +static void gen_SHA256MSG1(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_helper_sha256msg1(OP_PTR0, OP_PTR1, OP_PTR2); +} + +static void gen_SHA256MSG2(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_helper_sha256msg2(OP_PTR0, OP_PTR1, OP_PTR2); +} + +static void gen_SHA256RNDS2(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + TCGv_i32 wk0 = tcg_temp_new_i32(); + TCGv_i32 wk1 = tcg_temp_new_i32(); + + tcg_gen_ld_i32(wk0, tcg_env, ZMM_OFFSET(0) + offsetof(ZMMReg, ZMM_L(0))); + tcg_gen_ld_i32(wk1, tcg_env, ZMM_OFFSET(0) + offsetof(ZMMReg, ZMM_L(1))); + + gen_helper_sha256rnds2(OP_PTR0, OP_PTR1, OP_PTR2, wk0, wk1); +} + static void gen_SHLX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; diff --git a/target/i386/tcg/ops_sse_header.h.inc b/target/i386/tcg/ops_sse_header.h.inc index 8a7b2f4e2f..d92c6faf6d 100644 --- a/target/i386/tcg/ops_sse_header.h.inc +++ b/target/i386/tcg/ops_sse_header.h.inc @@ -399,6 +399,20 @@ DEF_HELPER_3(vpermq_ymm, void, Reg, Reg, i32) #endif #endif +/* SHA helpers */ +#if SHIFT == 1 +DEF_HELPER_3(sha1rnds4_f0, void, Reg, Reg, Reg) +DEF_HELPER_3(sha1rnds4_f1, void, Reg, Reg, Reg) +DEF_HELPER_3(sha1rnds4_f2, void, Reg, Reg, Reg) +DEF_HELPER_3(sha1rnds4_f3, void, Reg, Reg, Reg) +DEF_HELPER_3(sha1nexte, void, Reg, Reg, Reg) +DEF_HELPER_3(sha1msg1, void, Reg, Reg, Reg) +DEF_HELPER_3(sha1msg2, void, Reg, Reg, Reg) +DEF_HELPER_5(sha256rnds2, void, Reg, Reg, Reg, i32, i32) +DEF_HELPER_3(sha256msg1, void, Reg, Reg, Reg) +DEF_HELPER_3(sha256msg2, void, Reg, Reg, Reg) +#endif + #undef SHIFT #undef Reg #undef SUFFIX From 05a0a100a5e37716c11995532c0b249214846462 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 11 Oct 2023 10:07:27 +0200 Subject: [PATCH 111/974] tests/tcg/i386: initialize more registers in test-avx Some instructions use YMM0 implicitly, or use YMM9 as a read-modify-write register destination. Initialize those registers as well. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- tests/tcg/i386/test-avx.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/tcg/i386/test-avx.c b/tests/tcg/i386/test-avx.c index 910b067353..230e6d84b8 100644 --- a/tests/tcg/i386/test-avx.c +++ b/tests/tcg/i386/test-avx.c @@ -319,6 +319,8 @@ int main(int argc, char *argv[]) int i; init_all(&initI); + init_intreg(&initI.ymm[0]); + init_intreg(&initI.ymm[9]); init_intreg(&initI.ymm[10]); init_intreg(&initI.ymm[11]); init_intreg(&initI.ymm[12]); @@ -327,6 +329,8 @@ int main(int argc, char *argv[]) dump_regs(&initI); init_all(&initF16); + init_f16reg(&initF16.ymm[0]); + init_f16reg(&initF16.ymm[9]); init_f16reg(&initF16.ymm[10]); init_f16reg(&initF16.ymm[11]); init_f16reg(&initF16.ymm[12]); @@ -336,6 +340,8 @@ int main(int argc, char *argv[]) dump_regs(&initF16); init_all(&initF32); + init_f32reg(&initF32.ymm[0]); + init_f32reg(&initF32.ymm[9]); init_f32reg(&initF32.ymm[10]); init_f32reg(&initF32.ymm[11]); init_f32reg(&initF32.ymm[12]); @@ -345,6 +351,8 @@ int main(int argc, char *argv[]) dump_regs(&initF32); init_all(&initF64); + init_f64reg(&initF64.ymm[0]); + init_f64reg(&initF64.ymm[9]); init_f64reg(&initF64.ymm[10]); init_f64reg(&initF64.ymm[11]); init_f64reg(&initF64.ymm[12]); From 48adb240498de5f628631ba7c713a4ccd0cda358 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 10 Oct 2023 10:35:45 +0200 Subject: [PATCH 112/974] tests/tcg/i386: test-avx: add test cases for SHA new instructions Signed-off-by: Paolo Bonzini --- tests/tcg/i386/test-avx.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/tcg/i386/test-avx.py b/tests/tcg/i386/test-avx.py index 641a2ef69e..6063fb2d11 100755 --- a/tests/tcg/i386/test-avx.py +++ b/tests/tcg/i386/test-avx.py @@ -9,7 +9,7 @@ from fnmatch import fnmatch archs = [ "SSE", "SSE2", "SSE3", "SSSE3", "SSE4_1", "SSE4_2", "AES", "AVX", "AVX2", "AES+AVX", "VAES+AVX", - "F16C", "FMA", + "F16C", "FMA", "SHA", ] ignore = set(["FISTTP", @@ -43,6 +43,7 @@ imask = { 'vPS[LR][AL][WDQ]': 0x3f, 'vPS[RL]LDQ': 0x1f, 'vROUND[PS][SD]': 0x7, + 'SHA1RNDS4': 0x03, 'vSHUFPD': 0x0f, 'vSHUFPS': 0xff, 'vAESKEYGENASSIST': 0xff, From 183e6679e39fb5bcc17dbebaf668c1e83d8e57ee Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 9 Oct 2023 17:43:12 +0200 Subject: [PATCH 113/974] target/i386: group common checks in the decoding phase In preparation for adding more similar checks, move the VEX.L=0 check and several X86_SPECIAL_* checks to a new field, where each bit represent a common check on unused bits, or a restriction on the processor mode. Likewise, many SVM intercepts can be checked during the decoding phase, the main exception being the selective CR0 write, MSR and IOIO intercepts. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 85 ++++++++++++++++++++++++-------- target/i386/tcg/decode-new.h | 29 ++++++++--- target/i386/tcg/emit.c.inc | 8 --- 3 files changed, 85 insertions(+), 37 deletions(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index ec5d260b7e..25c1dae55a 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -90,8 +90,6 @@ X86_OP_ENTRY3(op, None, None, None, None, None, None, ## __VA_ARGS__) #define cpuid(feat) .cpuid = X86_FEAT_##feat, -#define i64 .special = X86_SPECIAL_i64, -#define o64 .special = X86_SPECIAL_o64, #define xchg .special = X86_SPECIAL_Locked, #define mmx .special = X86_SPECIAL_MMX, #define zext0 .special = X86_SPECIAL_ZExtOp0, @@ -114,6 +112,9 @@ #define vex12 .vex_class = 12, #define vex13 .vex_class = 13, +#define chk(a) .check = X86_CHECK_##a, +#define svm(a) .intercept = SVM_EXIT_##a, + #define avx2_256 .vex_special = X86_VEX_AVX2_256, #define P_00 1 @@ -161,8 +162,8 @@ static void decode_group15(DisasContext *s, CPUX86State *env, X86OpEntry *entry, }; static const X86OpEntry group15_mem[8] = { - [2] = X86_OP_ENTRYr(LDMXCSR, E,d, vex5), - [3] = X86_OP_ENTRYw(STMXCSR, E,d, vex5), + [2] = X86_OP_ENTRYr(LDMXCSR, E,d, vex5 chk(VEX128)), + [3] = X86_OP_ENTRYw(STMXCSR, E,d, vex5 chk(VEX128)), }; uint8_t modrm = get_modrm(s, env); @@ -1590,6 +1591,12 @@ static bool validate_vex(DisasContext *s, X86DecodedInsn *decode) if (s->flags & HF_EM_MASK) { goto illegal; } + + if (e->check & X86_CHECK_VEX128) { + if (s->vex_l) { + goto illegal; + } + } return true; nm_exception: @@ -1775,6 +1782,25 @@ static void disas_insn_new(DisasContext *s, CPUState *cpu, int b) goto illegal_op; } + /* Checks that result in #UD come first. */ + if (decode.e.check) { + if (decode.e.check & X86_CHECK_i64) { + if (CODE64(s)) { + goto illegal_op; + } + } + if (decode.e.check & X86_CHECK_o64) { + if (!CODE64(s)) { + goto illegal_op; + } + } + if (decode.e.check & X86_CHECK_prot) { + if (!PE(s) || VM86(s)) { + goto illegal_op; + } + } + } + switch (decode.e.special) { case X86_SPECIAL_None: break; @@ -1785,23 +1811,6 @@ static void disas_insn_new(DisasContext *s, CPUState *cpu, int b) } break; - case X86_SPECIAL_ProtMode: - if (!PE(s) || VM86(s)) { - goto illegal_op; - } - break; - - case X86_SPECIAL_i64: - if (CODE64(s)) { - goto illegal_op; - } - break; - case X86_SPECIAL_o64: - if (!CODE64(s)) { - goto illegal_op; - } - break; - case X86_SPECIAL_ZExtOp0: assert(decode.op[0].unit == X86_OP_INT); if (!decode.op[0].has_ea) { @@ -1831,6 +1840,37 @@ static void disas_insn_new(DisasContext *s, CPUState *cpu, int b) if (!validate_vex(s, &decode)) { return; } + + /* + * Checks that result in #GP or VMEXIT come second. Intercepts are + * generally checked after non-memory exceptions (i.e. before all + * exceptions if there is no memory operand). Exceptions are + * vm86 checks (INTn, IRET, PUSHF/POPF), RSM and XSETBV (!). + * + * RSM and XSETBV will be handled in the gen_* functions + * instead of using chk(). + */ + if (decode.e.check & X86_CHECK_cpl0) { + if (CPL(s) != 0) { + goto gp_fault; + } + } + if (decode.e.intercept && unlikely(GUEST(s))) { + gen_helper_svm_check_intercept(tcg_env, + tcg_constant_i32(decode.e.intercept)); + } + if (decode.e.check) { + if ((decode.e.check & X86_CHECK_vm86_iopl) && VM86(s)) { + if (IOPL(s) < 3) { + goto gp_fault; + } + } else if (decode.e.check & X86_CHECK_cpl_iopl) { + if (IOPL(s) < CPL(s)) { + goto gp_fault; + } + } + } + if (decode.e.special == X86_SPECIAL_MMX && !(s->prefix & (PREFIX_REPZ | PREFIX_REPNZ | PREFIX_DATA))) { gen_helper_enter_mmx(tcg_env); @@ -1857,6 +1897,9 @@ static void disas_insn_new(DisasContext *s, CPUState *cpu, int b) gen_writeback(s, &decode, 0, s->T0); } return; + gp_fault: + gen_exception_gpf(s); + return; illegal_op: gen_illegal_opcode(s); return; diff --git a/target/i386/tcg/decode-new.h b/target/i386/tcg/decode-new.h index 9be8a6e65f..bbc9aea940 100644 --- a/target/i386/tcg/decode-new.h +++ b/target/i386/tcg/decode-new.h @@ -131,15 +131,30 @@ typedef enum X86OpUnit { X86_OP_MMX, /* address in either s->ptrX or s->A0 depending on has_ea */ } X86OpUnit; +typedef enum X86InsnCheck { + /* Illegal or exclusive to 64-bit mode */ + X86_CHECK_i64 = 1, + X86_CHECK_o64 = 2, + + /* Fault outside protected mode */ + X86_CHECK_prot = 4, + + /* Privileged instruction checks */ + X86_CHECK_cpl0 = 8, + X86_CHECK_vm86_iopl = 16, + X86_CHECK_cpl_iopl = 32, + X86_CHECK_iopl = X86_CHECK_cpl_iopl | X86_CHECK_vm86_iopl, + + /* Fault if VEX.L=1 */ + X86_CHECK_VEX128 = 64, +} X86InsnCheck; + typedef enum X86InsnSpecial { X86_SPECIAL_None, /* Always locked if it has a memory operand (XCHG) */ X86_SPECIAL_Locked, - /* Fault outside protected mode */ - X86_SPECIAL_ProtMode, - /* * Register operand 0/2 is zero extended to 32 bits. Rd/Mb or Rd/Mw * in the manual. @@ -158,10 +173,6 @@ typedef enum X86InsnSpecial { * become P/P/Q/N, and size "x" becomes "q". */ X86_SPECIAL_MMX, - - /* Illegal or exclusive to 64-bit mode */ - X86_SPECIAL_i64, - X86_SPECIAL_o64, } X86InsnSpecial; /* @@ -224,7 +235,9 @@ struct X86OpEntry { X86CPUIDFeature cpuid:8; unsigned vex_class:8; X86VEXSpecial vex_special:8; - uint16_t valid_prefix:16; + unsigned valid_prefix:16; + unsigned check:16; + unsigned intercept:8; bool is_decode:1; }; diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 16085a19d7..82da5488d4 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -1236,10 +1236,6 @@ static void gen_INSERTQ_r(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec static void gen_LDMXCSR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { - if (s->vex_l) { - gen_illegal_opcode(s); - return; - } tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T1); gen_helper_ldmxcsr(tcg_env, s->tmp2_i32); } @@ -1886,10 +1882,6 @@ static void gen_VAESKEYGEN(DisasContext *s, CPUX86State *env, X86DecodedInsn *de static void gen_STMXCSR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { - if (s->vex_l) { - gen_illegal_opcode(s); - return; - } gen_helper_update_mxcsr(tcg_env); tcg_gen_ld32u_tl(s->T0, tcg_env, offsetof(CPUX86State, mxcsr)); } From e000687f1266d031528758271d0b16e288394ede Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 9 Oct 2023 18:16:27 +0200 Subject: [PATCH 114/974] target/i386: validate VEX.W for AVX instructions Instructions in VEX exception class 6 generally look at the value of VEX.W. Note that the manual places some instructions incorrectly in class 4, for example VPERMQ which has no non-VEX encoding and no legacy SSE analogue. AMD does a mess of its own, as documented in the comment that this patch adds. Most of them are checked for VEX.W=0, and are listed in the manual (though with an omission) in table 2-16; VPERMQ and VPERMPD check for VEX.W=1, which is only listed in the instruction description. Others, such as VPSRLV, VPSLLV and the FMA3 instructions, use VEX.W to switch between a 32-bit and 64-bit operation. Fix more of the class 4/class 6 mismatches, and implement the check for VEX.W in TCG. Acked-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 142 ++++++++++++++++++++++--------- target/i386/tcg/decode-new.h | 6 ++ 2 files changed, 107 insertions(+), 41 deletions(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index 25c1dae55a..2bdbb1bba0 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -23,7 +23,11 @@ * The decoder is mostly based on tables copied from the Intel SDM. As * a result, most operand load and writeback is done entirely in common * table-driven code using the same operand type (X86_TYPE_*) and - * size (X86_SIZE_*) codes used in the manual. + * size (X86_SIZE_*) codes used in the manual. There are a few differences + * though. + * + * Vector operands + * --------------- * * The main difference is that the V, U and W types are extended to * cover MMX as well; if an instruction is like @@ -43,6 +47,50 @@ * There are a couple cases in which instructions (e.g. MOVD) write the * whole XMM or MM register but are established incorrectly in the manual * as "d" or "q". These have to be fixed for the decoder to work correctly. + * + * VEX exception classes + * --------------------- + * + * Speaking about imprecisions in the manual, the decoder treats all + * exception-class 4 instructions as having an optional VEX prefix, and + * all exception-class 6 instructions as having a mandatory VEX prefix. + * This is true except for a dozen instructions; these are in exception + * class 4 but do not ignore the VEX.W bit (which does not even exist + * without a VEX prefix). These instructions are mostly listed in Intel's + * table 2-16, but with a few exceptions. + * + * The AMD manual has more precise subclasses for exceptions, and unlike Intel + * they list the VEX.W requirements in the exception classes as well (except + * when they don't). AMD describes class 6 as "AVX Mixed Memory Argument" + * without defining what a mixed memory argument is, but still use 4 as the + * primary exception class... except when they don't. + * + * The summary is: + * Intel AMD VEX.W note + * ------------------------------------------------------------------- + * vpblendd 4 4J 0 + * vpblendvb 4 4E-X 0 (*) + * vpbroadcastq 6 6D 0 (+) + * vpermd/vpermps 4 4H 0 (§) + * vpermq/vpermpd 4 4H-1 1 (§) + * vpermilpd/vpermilps 4 6E 0 (^) + * vpmaskmovd 6 4K significant (^) + * vpsllv 4 4K significant + * vpsrav 4 4J 0 + * vpsrlv 4 4K significant + * vtestps/vtestpd 4 4G 0 + * + * (*) AMD lists VPBLENDVB as related to SSE4.1 PBLENDVB, which may + * explain why it is considered exception class 4. However, + * Intel says that VEX-only instructions should be in class 6... + * + * (+) Not found in Intel's table 2-16 + * + * (§) 4H and 4H-1 do not mention VEX.W requirements, which are + * however present in the description of the instruction + * + * (^) these are the two cases in which Intel and AMD disagree on the + * primary exception class */ #define X86_OP_NONE { 0 }, @@ -338,11 +386,11 @@ static const X86OpEntry opcodes_0F38_00toEF[240] = { [0x07] = X86_OP_ENTRY3(PHSUBSW, V,x, H,x, W,x, vex4 cpuid(SSSE3) mmx avx2_256 p_00_66), [0x10] = X86_OP_ENTRY2(PBLENDVB, V,x, W,x, vex4 cpuid(SSE41) avx2_256 p_66), - [0x13] = X86_OP_ENTRY2(VCVTPH2PS, V,x, W,xh, vex11 cpuid(F16C) p_66), + [0x13] = X86_OP_ENTRY2(VCVTPH2PS, V,x, W,xh, vex11 chk(W0) cpuid(F16C) p_66), [0x14] = X86_OP_ENTRY2(BLENDVPS, V,x, W,x, vex4 cpuid(SSE41) p_66), [0x15] = X86_OP_ENTRY2(BLENDVPD, V,x, W,x, vex4 cpuid(SSE41) p_66), /* Listed incorrectly as type 4 */ - [0x16] = X86_OP_ENTRY3(VPERMD, V,qq, H,qq, W,qq, vex6 cpuid(AVX2) p_66), + [0x16] = X86_OP_ENTRY3(VPERMD, V,qq, H,qq, W,qq, vex6 chk(W0) cpuid(AVX2) p_66), /* vpermps */ [0x17] = X86_OP_ENTRY3(VPTEST, None,None, V,x, W,x, vex4 cpuid(SSE41) p_66), /* @@ -363,14 +411,14 @@ static const X86OpEntry opcodes_0F38_00toEF[240] = { [0x33] = X86_OP_ENTRY3(VPMOVZXWD, V,x, None,None, W,q, vex5 cpuid(SSE41) avx_movx avx2_256 p_66), [0x34] = X86_OP_ENTRY3(VPMOVZXWQ, V,x, None,None, W,d, vex5 cpuid(SSE41) avx_movx avx2_256 p_66), [0x35] = X86_OP_ENTRY3(VPMOVZXDQ, V,x, None,None, W,q, vex5 cpuid(SSE41) avx_movx avx2_256 p_66), - [0x36] = X86_OP_ENTRY3(VPERMD, V,qq, H,qq, W,qq, vex6 cpuid(AVX2) p_66), + [0x36] = X86_OP_ENTRY3(VPERMD, V,qq, H,qq, W,qq, vex6 chk(W0) cpuid(AVX2) p_66), [0x37] = X86_OP_ENTRY3(PCMPGTQ, V,x, H,x, W,x, vex4 cpuid(SSE42) avx2_256 p_66), [0x40] = X86_OP_ENTRY3(PMULLD, V,x, H,x, W,x, vex4 cpuid(SSE41) avx2_256 p_66), [0x41] = X86_OP_ENTRY3(VPHMINPOSUW, V,dq, None,None, W,dq, vex4 cpuid(SSE41) p_66), /* Listed incorrectly as type 4 */ [0x45] = X86_OP_ENTRY3(VPSRLV, V,x, H,x, W,x, vex6 cpuid(AVX2) p_66), - [0x46] = X86_OP_ENTRY3(VPSRAV, V,x, H,x, W,x, vex6 cpuid(AVX2) p_66), + [0x46] = X86_OP_ENTRY3(VPSRAV, V,x, H,x, W,x, vex6 chk(W0) cpuid(AVX2) p_66), [0x47] = X86_OP_ENTRY3(VPSLLV, V,x, H,x, W,x, vex6 cpuid(AVX2) p_66), [0x90] = X86_OP_ENTRY3(VPGATHERD, V,x, H,x, M,d, vex12 cpuid(AVX2) p_66), /* vpgatherdd/q */ @@ -392,14 +440,15 @@ static const X86OpEntry opcodes_0F38_00toEF[240] = { [0x09] = X86_OP_ENTRY3(PSIGNW, V,x, H,x, W,x, vex4 cpuid(SSSE3) mmx avx2_256 p_00_66), [0x0a] = X86_OP_ENTRY3(PSIGND, V,x, H,x, W,x, vex4 cpuid(SSSE3) mmx avx2_256 p_00_66), [0x0b] = X86_OP_ENTRY3(PMULHRSW, V,x, H,x, W,x, vex4 cpuid(SSSE3) mmx avx2_256 p_00_66), - [0x0c] = X86_OP_ENTRY3(VPERMILPS, V,x, H,x, W,x, vex4 cpuid(AVX) p_00_66), - [0x0d] = X86_OP_ENTRY3(VPERMILPD, V,x, H,x, W,x, vex4 cpuid(AVX) p_66), - [0x0e] = X86_OP_ENTRY3(VTESTPS, None,None, V,x, W,x, vex4 cpuid(AVX) p_66), - [0x0f] = X86_OP_ENTRY3(VTESTPD, None,None, V,x, W,x, vex4 cpuid(AVX) p_66), + /* Listed incorrectly as type 4 */ + [0x0c] = X86_OP_ENTRY3(VPERMILPS, V,x, H,x, W,x, vex6 chk(W0) cpuid(AVX) p_00_66), + [0x0d] = X86_OP_ENTRY3(VPERMILPD, V,x, H,x, W,x, vex6 chk(W0) cpuid(AVX) p_66), + [0x0e] = X86_OP_ENTRY3(VTESTPS, None,None, V,x, W,x, vex6 chk(W0) cpuid(AVX) p_66), + [0x0f] = X86_OP_ENTRY3(VTESTPD, None,None, V,x, W,x, vex6 chk(W0) cpuid(AVX) p_66), - [0x18] = X86_OP_ENTRY3(VPBROADCASTD, V,x, None,None, W,d, vex6 cpuid(AVX) p_66), /* vbroadcastss */ - [0x19] = X86_OP_ENTRY3(VPBROADCASTQ, V,qq, None,None, W,q, vex6 cpuid(AVX) p_66), /* vbroadcastsd */ - [0x1a] = X86_OP_ENTRY3(VBROADCASTx128, V,qq, None,None, WM,dq,vex6 cpuid(AVX) p_66), + [0x18] = X86_OP_ENTRY3(VPBROADCASTD, V,x, None,None, W,d, vex6 chk(W0) cpuid(AVX) p_66), /* vbroadcastss */ + [0x19] = X86_OP_ENTRY3(VPBROADCASTQ, V,qq, None,None, W,q, vex6 chk(W0) cpuid(AVX) p_66), /* vbroadcastsd */ + [0x1a] = X86_OP_ENTRY3(VBROADCASTx128, V,qq, None,None, WM,dq,vex6 chk(W0) cpuid(AVX) p_66), [0x1c] = X86_OP_ENTRY3(PABSB, V,x, None,None, W,x, vex4 cpuid(SSSE3) mmx avx2_256 p_00_66), [0x1d] = X86_OP_ENTRY3(PABSW, V,x, None,None, W,x, vex4 cpuid(SSSE3) mmx avx2_256 p_00_66), [0x1e] = X86_OP_ENTRY3(PABSD, V,x, None,None, W,x, vex4 cpuid(SSSE3) mmx avx2_256 p_00_66), @@ -408,11 +457,11 @@ static const X86OpEntry opcodes_0F38_00toEF[240] = { [0x29] = X86_OP_ENTRY3(PCMPEQQ, V,x, H,x, W,x, vex4 cpuid(SSE41) avx2_256 p_66), [0x2a] = X86_OP_ENTRY3(MOVDQ, V,x, None,None, WM,x, vex1 cpuid(SSE41) avx2_256 p_66), /* movntdqa */ [0x2b] = X86_OP_ENTRY3(VPACKUSDW, V,x, H,x, W,x, vex4 cpuid(SSE41) avx2_256 p_66), - [0x2c] = X86_OP_ENTRY3(VMASKMOVPS, V,x, H,x, WM,x, vex6 cpuid(AVX) p_66), - [0x2d] = X86_OP_ENTRY3(VMASKMOVPD, V,x, H,x, WM,x, vex6 cpuid(AVX) p_66), + [0x2c] = X86_OP_ENTRY3(VMASKMOVPS, V,x, H,x, WM,x, vex6 chk(W0) cpuid(AVX) p_66), + [0x2d] = X86_OP_ENTRY3(VMASKMOVPD, V,x, H,x, WM,x, vex6 chk(W0) cpuid(AVX) p_66), /* Incorrectly listed as Mx,Hx,Vx in the manual */ - [0x2e] = X86_OP_ENTRY3(VMASKMOVPS_st, M,x, V,x, H,x, vex6 cpuid(AVX) p_66), - [0x2f] = X86_OP_ENTRY3(VMASKMOVPD_st, M,x, V,x, H,x, vex6 cpuid(AVX) p_66), + [0x2e] = X86_OP_ENTRY3(VMASKMOVPS_st, M,x, V,x, H,x, vex6 chk(W0) cpuid(AVX) p_66), + [0x2f] = X86_OP_ENTRY3(VMASKMOVPD_st, M,x, V,x, H,x, vex6 chk(W0) cpuid(AVX) p_66), [0x38] = X86_OP_ENTRY3(PMINSB, V,x, H,x, W,x, vex4 cpuid(SSE41) avx2_256 p_66), [0x39] = X86_OP_ENTRY3(PMINSD, V,x, H,x, W,x, vex4 cpuid(SSE41) avx2_256 p_66), @@ -423,12 +472,13 @@ static const X86OpEntry opcodes_0F38_00toEF[240] = { [0x3e] = X86_OP_ENTRY3(PMAXUW, V,x, H,x, W,x, vex4 cpuid(SSE41) avx2_256 p_66), [0x3f] = X86_OP_ENTRY3(PMAXUD, V,x, H,x, W,x, vex4 cpuid(SSE41) avx2_256 p_66), - [0x58] = X86_OP_ENTRY3(VPBROADCASTD, V,x, None,None, W,d, vex6 cpuid(AVX2) p_66), - [0x59] = X86_OP_ENTRY3(VPBROADCASTQ, V,x, None,None, W,q, vex6 cpuid(AVX2) p_66), - [0x5a] = X86_OP_ENTRY3(VBROADCASTx128, V,qq, None,None, WM,dq,vex6 cpuid(AVX2) p_66), + /* VPBROADCASTQ not listed as W0 in table 2-16 */ + [0x58] = X86_OP_ENTRY3(VPBROADCASTD, V,x, None,None, W,d, vex6 chk(W0) cpuid(AVX2) p_66), + [0x59] = X86_OP_ENTRY3(VPBROADCASTQ, V,x, None,None, W,q, vex6 chk(W0) cpuid(AVX2) p_66), + [0x5a] = X86_OP_ENTRY3(VBROADCASTx128, V,qq, None,None, WM,dq,vex6 chk(W0) cpuid(AVX2) p_66), - [0x78] = X86_OP_ENTRY3(VPBROADCASTB, V,x, None,None, W,b, vex6 cpuid(AVX2) p_66), - [0x79] = X86_OP_ENTRY3(VPBROADCASTW, V,x, None,None, W,w, vex6 cpuid(AVX2) p_66), + [0x78] = X86_OP_ENTRY3(VPBROADCASTB, V,x, None,None, W,b, vex6 chk(W0) cpuid(AVX2) p_66), + [0x79] = X86_OP_ENTRY3(VPBROADCASTW, V,x, None,None, W,w, vex6 chk(W0) cpuid(AVX2) p_66), [0x8c] = X86_OP_ENTRY3(VPMASKMOV, V,x, H,x, WM,x, vex6 cpuid(AVX2) p_66), [0x8e] = X86_OP_ENTRY3(VPMASKMOV_st, M,x, V,x, H,x, vex6 cpuid(AVX2) p_66), @@ -562,18 +612,18 @@ static const X86OpEntry opcodes_0F3A[256] = { * Also the "qq" instructions are sometimes omitted by Table 2-17, but are VEX256 * only. */ - [0x00] = X86_OP_ENTRY3(VPERMQ, V,qq, W,qq, I,b, vex6 cpuid(AVX2) p_66), - [0x01] = X86_OP_ENTRY3(VPERMQ, V,qq, W,qq, I,b, vex6 cpuid(AVX2) p_66), /* VPERMPD */ - [0x02] = X86_OP_ENTRY4(VBLENDPS, V,x, H,x, W,x, vex6 cpuid(AVX2) p_66), /* VPBLENDD */ - [0x04] = X86_OP_ENTRY3(VPERMILPS_i, V,x, W,x, I,b, vex6 cpuid(AVX) p_66), - [0x05] = X86_OP_ENTRY3(VPERMILPD_i, V,x, W,x, I,b, vex6 cpuid(AVX) p_66), - [0x06] = X86_OP_ENTRY4(VPERM2x128, V,qq, H,qq, W,qq, vex6 cpuid(AVX) p_66), + [0x00] = X86_OP_ENTRY3(VPERMQ, V,qq, W,qq, I,b, vex6 chk(W1) cpuid(AVX2) p_66), + [0x01] = X86_OP_ENTRY3(VPERMQ, V,qq, W,qq, I,b, vex6 chk(W1) cpuid(AVX2) p_66), /* VPERMPD */ + [0x02] = X86_OP_ENTRY4(VBLENDPS, V,x, H,x, W,x, vex6 chk(W0) cpuid(AVX2) p_66), /* VPBLENDD */ + [0x04] = X86_OP_ENTRY3(VPERMILPS_i, V,x, W,x, I,b, vex6 chk(W0) cpuid(AVX) p_66), + [0x05] = X86_OP_ENTRY3(VPERMILPD_i, V,x, W,x, I,b, vex6 chk(W0) cpuid(AVX) p_66), + [0x06] = X86_OP_ENTRY4(VPERM2x128, V,qq, H,qq, W,qq, vex6 chk(W0) cpuid(AVX) p_66), [0x14] = X86_OP_ENTRY3(PEXTRB, E,b, V,dq, I,b, vex5 cpuid(SSE41) zext0 p_66), [0x15] = X86_OP_ENTRY3(PEXTRW, E,w, V,dq, I,b, vex5 cpuid(SSE41) zext0 p_66), [0x16] = X86_OP_ENTRY3(PEXTR, E,y, V,dq, I,b, vex5 cpuid(SSE41) p_66), [0x17] = X86_OP_ENTRY3(VEXTRACTPS, E,d, V,dq, I,b, vex5 cpuid(SSE41) p_66), - [0x1d] = X86_OP_ENTRY3(VCVTPS2PH, W,xh, V,x, I,b, vex11 cpuid(F16C) p_66), + [0x1d] = X86_OP_ENTRY3(VCVTPS2PH, W,xh, V,x, I,b, vex11 chk(W0) cpuid(F16C) p_66), [0x20] = X86_OP_ENTRY4(PINSRB, V,dq, H,dq, E,b, vex5 cpuid(SSE41) zext2 p_66), [0x21] = X86_OP_GROUP0(VINSERTPS), @@ -583,7 +633,7 @@ static const X86OpEntry opcodes_0F3A[256] = { [0x41] = X86_OP_ENTRY4(VDDPD, V,dq, H,dq, W,dq, vex2 cpuid(SSE41) p_66), [0x42] = X86_OP_ENTRY4(VMPSADBW, V,x, H,x, W,x, vex2 cpuid(SSE41) avx2_256 p_66), [0x44] = X86_OP_ENTRY4(PCLMULQDQ, V,dq, H,dq, W,dq, vex4 cpuid(PCLMULQDQ) p_66), - [0x46] = X86_OP_ENTRY4(VPERM2x128, V,qq, H,qq, W,qq, vex6 cpuid(AVX2) p_66), + [0x46] = X86_OP_ENTRY4(VPERM2x128, V,qq, H,qq, W,qq, vex6 chk(W0) cpuid(AVX2) p_66), [0x60] = X86_OP_ENTRY4(PCMPESTRM, None,None, V,dq, W,dq, vex4_unal cpuid(SSE42) p_66), [0x61] = X86_OP_ENTRY4(PCMPESTRI, None,None, V,dq, W,dq, vex4_unal cpuid(SSE42) p_66), @@ -606,16 +656,16 @@ static const X86OpEntry opcodes_0F3A[256] = { [0x0e] = X86_OP_ENTRY4(VPBLENDW, V,x, H,x, W,x, vex4 cpuid(SSE41) avx2_256 p_66), [0x0f] = X86_OP_ENTRY4(PALIGNR, V,x, H,x, W,x, vex4 cpuid(SSSE3) mmx avx2_256 p_00_66), - [0x18] = X86_OP_ENTRY4(VINSERTx128, V,qq, H,qq, W,qq, vex6 cpuid(AVX) p_66), - [0x19] = X86_OP_ENTRY3(VEXTRACTx128, W,dq, V,qq, I,b, vex6 cpuid(AVX) p_66), + [0x18] = X86_OP_ENTRY4(VINSERTx128, V,qq, H,qq, W,qq, vex6 chk(W0) cpuid(AVX) p_66), + [0x19] = X86_OP_ENTRY3(VEXTRACTx128, W,dq, V,qq, I,b, vex6 chk(W0) cpuid(AVX) p_66), - [0x38] = X86_OP_ENTRY4(VINSERTx128, V,qq, H,qq, W,qq, vex6 cpuid(AVX2) p_66), - [0x39] = X86_OP_ENTRY3(VEXTRACTx128, W,dq, V,qq, I,b, vex6 cpuid(AVX2) p_66), + [0x38] = X86_OP_ENTRY4(VINSERTx128, V,qq, H,qq, W,qq, vex6 chk(W0) cpuid(AVX2) p_66), + [0x39] = X86_OP_ENTRY3(VEXTRACTx128, W,dq, V,qq, I,b, vex6 chk(W0) cpuid(AVX2) p_66), /* Listed incorrectly as type 4 */ - [0x4a] = X86_OP_ENTRY4(VBLENDVPS, V,x, H,x, W,x, vex6 cpuid(AVX) p_66), - [0x4b] = X86_OP_ENTRY4(VBLENDVPD, V,x, H,x, W,x, vex6 cpuid(AVX) p_66), - [0x4c] = X86_OP_ENTRY4(VPBLENDVB, V,x, H,x, W,x, vex6 cpuid(AVX) p_66 avx2_256), + [0x4a] = X86_OP_ENTRY4(VBLENDVPS, V,x, H,x, W,x, vex6 chk(W0) cpuid(AVX) p_66), + [0x4b] = X86_OP_ENTRY4(VBLENDVPD, V,x, H,x, W,x, vex6 chk(W0) cpuid(AVX) p_66), + [0x4c] = X86_OP_ENTRY4(VPBLENDVB, V,x, H,x, W,x, vex6 chk(W0) cpuid(AVX) p_66 avx2_256), [0xcc] = X86_OP_ENTRY3(SHA1RNDS4, V,dq, W,dq, I,b, cpuid(SHA_NI)), @@ -1505,8 +1555,6 @@ static bool validate_vex(DisasContext *s, X86DecodedInsn *decode) } } - /* TODO: instructions that require VEX.W=0 (Table 2-16) */ - switch (e->vex_class) { case 0: if (s->prefix & PREFIX_VEX) { @@ -1592,9 +1640,21 @@ static bool validate_vex(DisasContext *s, X86DecodedInsn *decode) goto illegal; } - if (e->check & X86_CHECK_VEX128) { - if (s->vex_l) { - goto illegal; + if (e->check) { + if (e->check & X86_CHECK_VEX128) { + if (s->vex_l) { + goto illegal; + } + } + if (e->check & X86_CHECK_W0) { + if (s->vex_w) { + goto illegal; + } + } + if (e->check & X86_CHECK_W1) { + if (!s->vex_w) { + goto illegal; + } } } return true; diff --git a/target/i386/tcg/decode-new.h b/target/i386/tcg/decode-new.h index bbc9aea940..e6c904a319 100644 --- a/target/i386/tcg/decode-new.h +++ b/target/i386/tcg/decode-new.h @@ -147,6 +147,12 @@ typedef enum X86InsnCheck { /* Fault if VEX.L=1 */ X86_CHECK_VEX128 = 64, + + /* Fault if VEX.W=1 */ + X86_CHECK_W0 = 128, + + /* Fault if VEX.W=0 */ + X86_CHECK_W1 = 256, } X86InsnCheck; typedef enum X86InsnSpecial { From d83005424774fcfb85aec76effac169cadb375fd Mon Sep 17 00:00:00 2001 From: Ani Sinha Date: Tue, 12 Sep 2023 17:36:50 +0530 Subject: [PATCH 115/974] target/i386: check CPUID_PAE to determine 36 bit processor address space PAE mode in x86 supports 36 bit address space. Check the PAE CPUID on the guest processor and set phys_bits to 36 if PAE feature is set. This is in addition to checking the presence of PSE36 CPUID feature for setting 36 bit phys_bits. Signed-off-by: Ani Sinha Acked-by: Michael S. Tsirkin Reviewed-by: David Hildenbrand Message-ID: <20230912120650.371781-1-anisinha@redhat.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 070c02000f..fc8484cb5e 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -7377,7 +7377,7 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) return; } - if (env->features[FEAT_1_EDX] & CPUID_PSE36) { + if (env->features[FEAT_1_EDX] & (CPUID_PSE36 | CPUID_PAE)) { cpu->phys_bits = 36; } else { cpu->phys_bits = 32; From cd08948840c029ca537e414e27b575536dff5956 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 18 Oct 2023 11:15:09 +0200 Subject: [PATCH 116/974] kvm: remove unnecessary stub This function is only invoked from hw/intc/s390_flic_kvm.c, and therefore only if CONFIG_KVM is defined. Reviewed-by: Manos Pitsidianakis Signed-off-by: Paolo Bonzini --- accel/stubs/kvm-stub.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/accel/stubs/kvm-stub.c b/accel/stubs/kvm-stub.c index 51f522e52e..a323252f8e 100644 --- a/accel/stubs/kvm-stub.c +++ b/accel/stubs/kvm-stub.c @@ -92,11 +92,6 @@ void kvm_irqchip_change_notify(void) { } -int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter) -{ - return -ENOSYS; -} - int kvm_irqchip_add_irqfd_notifier_gsi(KVMState *s, EventNotifier *n, EventNotifier *rn, int virq) { From aacec9aee11660471ca56afaaafe3f1fdcf431ab Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 17 Oct 2023 13:51:30 +0200 Subject: [PATCH 117/974] kvm: require KVM_CAP_INTERNAL_ERROR_DATA This was introduced in KVM in Linux 2.6.33, we can require it unconditionally. Reviewed-by: Manos Pitsidianakis Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 3f7eafe08c..8eee504225 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -111,6 +111,7 @@ static const KVMCapabilityInfo kvm_required_capabilites[] = { KVM_CAP_INFO(USER_MEMORY), KVM_CAP_INFO(DESTROY_MEMORY_REGION_WORKS), KVM_CAP_INFO(JOIN_MEMORY_REGIONS_WORKS), + KVM_CAP_INFO(INTERNAL_ERROR_DATA), KVM_CAP_LAST_INFO }; @@ -2794,16 +2795,14 @@ static void kvm_handle_io(uint16_t port, MemTxAttrs attrs, void *data, int direc static int kvm_handle_internal_error(CPUState *cpu, struct kvm_run *run) { + int i; + fprintf(stderr, "KVM internal error. Suberror: %d\n", run->internal.suberror); - if (kvm_check_extension(kvm_state, KVM_CAP_INTERNAL_ERROR_DATA)) { - int i; - - for (i = 0; i < run->internal.ndata; ++i) { - fprintf(stderr, "extra data[%d]: 0x%016"PRIx64"\n", - i, (uint64_t)run->internal.data[i]); - } + for (i = 0; i < run->internal.ndata; ++i) { + fprintf(stderr, "extra data[%d]: 0x%016"PRIx64"\n", + i, (uint64_t)run->internal.data[i]); } if (run->internal.suberror == KVM_INTERNAL_ERROR_EMULATION) { fprintf(stderr, "emulation failure\n"); From cc5e719e2c8086c61bdd9114f42095f8d5b1b0db Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 17 Oct 2023 13:24:33 +0200 Subject: [PATCH 118/974] kvm: require KVM_CAP_SIGNAL_MSI This was introduced in KVM in Linux 3.5, we can require it unconditionally in kvm_irqchip_send_msi(). However, not all architectures have to implement it so check it only in x86, the only architecture that ever had MSI injection but not KVM_CAP_SIGNAL_MSI. ARM uses it to detect the presence of the ITS emulation in the kernel, introduced in Linux 4.8. Assume that it's there and possibly fail when realizing the arm-its-kvm device. Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 102 +++------------------------------ accel/stubs/kvm-stub.c | 1 - hw/intc/arm_gicv3_its_common.c | 3 +- include/sysemu/kvm.h | 9 --- include/sysemu/kvm_int.h | 1 - target/i386/kvm/kvm.c | 1 + 6 files changed, 9 insertions(+), 108 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 8eee504225..0c7b0569da 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -99,7 +99,6 @@ bool kvm_gsi_direct_mapping; bool kvm_allowed; bool kvm_readonly_mem_allowed; bool kvm_vm_attributes_allowed; -bool kvm_direct_msi_allowed; bool kvm_ioeventfd_any_length_allowed; bool kvm_msi_use_devid; bool kvm_has_guest_debug; @@ -1848,7 +1847,7 @@ static void clear_gsi(KVMState *s, unsigned int gsi) void kvm_init_irq_routing(KVMState *s) { - int gsi_count, i; + int gsi_count; gsi_count = kvm_check_extension(s, KVM_CAP_IRQ_ROUTING) - 1; if (gsi_count > 0) { @@ -1860,12 +1859,6 @@ void kvm_init_irq_routing(KVMState *s) s->irq_routes = g_malloc0(sizeof(*s->irq_routes)); s->nr_allocated_irq_routes = 0; - if (!kvm_direct_msi_allowed) { - for (i = 0; i < KVM_MSI_HASHTAB_SIZE; i++) { - QTAILQ_INIT(&s->msi_hashtab[i]); - } - } - kvm_arch_init_irq_routing(s); } @@ -1985,41 +1978,10 @@ void kvm_irqchip_change_notify(void) notifier_list_notify(&kvm_irqchip_change_notifiers, NULL); } -static unsigned int kvm_hash_msi(uint32_t data) -{ - /* This is optimized for IA32 MSI layout. However, no other arch shall - * repeat the mistake of not providing a direct MSI injection API. */ - return data & 0xff; -} - -static void kvm_flush_dynamic_msi_routes(KVMState *s) -{ - KVMMSIRoute *route, *next; - unsigned int hash; - - for (hash = 0; hash < KVM_MSI_HASHTAB_SIZE; hash++) { - QTAILQ_FOREACH_SAFE(route, &s->msi_hashtab[hash], entry, next) { - kvm_irqchip_release_virq(s, route->kroute.gsi); - QTAILQ_REMOVE(&s->msi_hashtab[hash], route, entry); - g_free(route); - } - } -} - static int kvm_irqchip_get_virq(KVMState *s) { int next_virq; - /* - * PIC and IOAPIC share the first 16 GSI numbers, thus the available - * GSI numbers are more than the number of IRQ route. Allocating a GSI - * number can succeed even though a new route entry cannot be added. - * When this happens, flush dynamic MSI entries to free IRQ route entries. - */ - if (!kvm_direct_msi_allowed && s->irq_routes->nr == s->gsi_count) { - kvm_flush_dynamic_msi_routes(s); - } - /* Return the lowest unused GSI in the bitmap */ next_virq = find_first_zero_bit(s->used_gsi_bitmap, s->gsi_count); if (next_virq >= s->gsi_count) { @@ -2029,63 +1991,17 @@ static int kvm_irqchip_get_virq(KVMState *s) } } -static KVMMSIRoute *kvm_lookup_msi_route(KVMState *s, MSIMessage msg) -{ - unsigned int hash = kvm_hash_msi(msg.data); - KVMMSIRoute *route; - - QTAILQ_FOREACH(route, &s->msi_hashtab[hash], entry) { - if (route->kroute.u.msi.address_lo == (uint32_t)msg.address && - route->kroute.u.msi.address_hi == (msg.address >> 32) && - route->kroute.u.msi.data == le32_to_cpu(msg.data)) { - return route; - } - } - return NULL; -} - int kvm_irqchip_send_msi(KVMState *s, MSIMessage msg) { struct kvm_msi msi; - KVMMSIRoute *route; - if (kvm_direct_msi_allowed) { - msi.address_lo = (uint32_t)msg.address; - msi.address_hi = msg.address >> 32; - msi.data = le32_to_cpu(msg.data); - msi.flags = 0; - memset(msi.pad, 0, sizeof(msi.pad)); + msi.address_lo = (uint32_t)msg.address; + msi.address_hi = msg.address >> 32; + msi.data = le32_to_cpu(msg.data); + msi.flags = 0; + memset(msi.pad, 0, sizeof(msi.pad)); - return kvm_vm_ioctl(s, KVM_SIGNAL_MSI, &msi); - } - - route = kvm_lookup_msi_route(s, msg); - if (!route) { - int virq; - - virq = kvm_irqchip_get_virq(s); - if (virq < 0) { - return virq; - } - - route = g_new0(KVMMSIRoute, 1); - route->kroute.gsi = virq; - route->kroute.type = KVM_IRQ_ROUTING_MSI; - route->kroute.flags = 0; - route->kroute.u.msi.address_lo = (uint32_t)msg.address; - route->kroute.u.msi.address_hi = msg.address >> 32; - route->kroute.u.msi.data = le32_to_cpu(msg.data); - - kvm_add_routing_entry(s, &route->kroute); - kvm_irqchip_commit_routes(s); - - QTAILQ_INSERT_TAIL(&s->msi_hashtab[kvm_hash_msi(msg.data)], route, - entry); - } - - assert(route->kroute.type == KVM_IRQ_ROUTING_MSI); - - return kvm_set_irq(s, route->kroute.gsi, 1); + return kvm_vm_ioctl(s, KVM_SIGNAL_MSI, &msi); } int kvm_irqchip_add_msi_route(KVMRouteChange *c, int vector, PCIDevice *dev) @@ -2660,10 +2576,6 @@ static int kvm_init(MachineState *ms) s->max_nested_state_len = kvm_check_extension(s, KVM_CAP_NESTED_STATE); -#ifdef KVM_CAP_IRQ_ROUTING - kvm_direct_msi_allowed = (kvm_check_extension(s, KVM_CAP_SIGNAL_MSI) > 0); -#endif - s->intx_set_mask = kvm_check_extension(s, KVM_CAP_PCI_2_3); s->irq_set_ioctl = KVM_IRQ_LINE; diff --git a/accel/stubs/kvm-stub.c b/accel/stubs/kvm-stub.c index a323252f8e..bce005adad 100644 --- a/accel/stubs/kvm-stub.c +++ b/accel/stubs/kvm-stub.c @@ -27,7 +27,6 @@ bool kvm_allowed; bool kvm_readonly_mem_allowed; bool kvm_ioeventfd_any_length_allowed; bool kvm_msi_use_devid; -bool kvm_direct_msi_allowed; void kvm_flush_coalesced_mmio_buffer(void) { diff --git a/hw/intc/arm_gicv3_its_common.c b/hw/intc/arm_gicv3_its_common.c index abaf77057e..fddd6d490c 100644 --- a/hw/intc/arm_gicv3_its_common.c +++ b/hw/intc/arm_gicv3_its_common.c @@ -163,8 +163,7 @@ type_init(gicv3_its_common_register_types) const char *its_class_name(void) { if (kvm_irqchip_in_kernel()) { - /* KVM implementation requires this capability */ - return kvm_direct_msi_enabled() ? "arm-its-kvm" : NULL; + return "arm-its-kvm"; } else { /* Software emulation based model */ return "arm-gicv3-its"; diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 97a8a4f201..93dccf5dd9 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -43,7 +43,6 @@ extern bool kvm_msi_via_irqfd_allowed; extern bool kvm_gsi_routing_allowed; extern bool kvm_gsi_direct_mapping; extern bool kvm_readonly_mem_allowed; -extern bool kvm_direct_msi_allowed; extern bool kvm_ioeventfd_any_length_allowed; extern bool kvm_msi_use_devid; @@ -147,13 +146,6 @@ extern bool kvm_msi_use_devid; */ #define kvm_readonly_mem_enabled() (kvm_readonly_mem_allowed) -/** - * kvm_direct_msi_enabled: - * - * Returns: true if KVM allows direct MSI injection. - */ -#define kvm_direct_msi_enabled() (kvm_direct_msi_allowed) - /** * kvm_ioeventfd_any_length_enabled: * Returns: true if KVM allows any length io eventfd. @@ -181,7 +173,6 @@ extern bool kvm_msi_use_devid; #define kvm_gsi_routing_allowed() (false) #define kvm_gsi_direct_mapping() (false) #define kvm_readonly_mem_enabled() (false) -#define kvm_direct_msi_enabled() (false) #define kvm_ioeventfd_any_length_enabled() (false) #define kvm_msi_devid_required() (false) diff --git a/include/sysemu/kvm_int.h b/include/sysemu/kvm_int.h index 075939a3c4..a7dacd12d6 100644 --- a/include/sysemu/kvm_int.h +++ b/include/sysemu/kvm_int.h @@ -103,7 +103,6 @@ struct KVMState int nr_allocated_irq_routes; unsigned long *used_gsi_bitmap; unsigned int gsi_count; - QTAILQ_HEAD(, KVMMSIRoute) msi_hashtab[KVM_MSI_HASHTAB_SIZE]; #endif KVMMemoryListener memory_listener; QLIST_HEAD(, KVMParkedVcpu) kvm_parked_vcpus; diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index e7c054cc16..fb6655254f 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -91,6 +91,7 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = { KVM_CAP_INFO(SET_TSS_ADDR), KVM_CAP_INFO(EXT_CPUID), KVM_CAP_INFO(MP_STATE), + KVM_CAP_INFO(SIGNAL_MSI), KVM_CAP_LAST_INFO }; From a788260b2000f1fe826885c06f2a34df1c5b335c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 17 Oct 2023 13:34:50 +0200 Subject: [PATCH 119/974] kvm: require KVM_IRQFD for kernel irqchip KVM_IRQFD was introduced in Linux 2.6.32, and since then it has always been available on architectures that support an in-kernel interrupt controller. We can require it unconditionally. Reviewed-by: Manos Pitsidianakis Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 13 +++++-------- accel/stubs/kvm-stub.c | 1 - hw/intc/arm_gicv3_its_kvm.c | 2 +- include/sysemu/kvm.h | 6 +++--- target/riscv/kvm/kvm-cpu.c | 2 +- 5 files changed, 10 insertions(+), 14 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 0c7b0569da..be50d47f7b 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -91,7 +91,6 @@ bool kvm_split_irqchip; bool kvm_async_interrupts_allowed; bool kvm_halt_in_kernel_allowed; bool kvm_eventfds_allowed; -bool kvm_irqfds_allowed; bool kvm_resamplefds_allowed; bool kvm_msi_via_irqfd_allowed; bool kvm_gsi_routing_allowed; @@ -2128,10 +2127,6 @@ static int kvm_irqchip_assign_irqfd(KVMState *s, EventNotifier *event, } } - if (!kvm_irqfds_enabled()) { - return -ENOSYS; - } - return kvm_vm_ioctl(s, KVM_IRQFD, &irqfd); } @@ -2292,6 +2287,11 @@ static void kvm_irqchip_create(KVMState *s) return; } + if (kvm_check_extension(s, KVM_CAP_IRQFD) <= 0) { + fprintf(stderr, "kvm: irqfd not implemented\n"); + exit(1); + } + /* First probe and see if there's a arch-specific hook to create the * in-kernel irqchip for us */ ret = kvm_arch_irqchip_create(s); @@ -2589,9 +2589,6 @@ static int kvm_init(MachineState *ms) kvm_eventfds_allowed = (kvm_check_extension(s, KVM_CAP_IOEVENTFD) > 0); - kvm_irqfds_allowed = - (kvm_check_extension(s, KVM_CAP_IRQFD) > 0); - kvm_resamplefds_allowed = (kvm_check_extension(s, KVM_CAP_IRQFD_RESAMPLE) > 0); diff --git a/accel/stubs/kvm-stub.c b/accel/stubs/kvm-stub.c index bce005adad..19d58f2778 100644 --- a/accel/stubs/kvm-stub.c +++ b/accel/stubs/kvm-stub.c @@ -18,7 +18,6 @@ KVMState *kvm_state; bool kvm_kernel_irqchip; bool kvm_async_interrupts_allowed; bool kvm_eventfds_allowed; -bool kvm_irqfds_allowed; bool kvm_resamplefds_allowed; bool kvm_msi_via_irqfd_allowed; bool kvm_gsi_routing_allowed; diff --git a/hw/intc/arm_gicv3_its_kvm.c b/hw/intc/arm_gicv3_its_kvm.c index 61c1cc7bdb..f7df602cff 100644 --- a/hw/intc/arm_gicv3_its_kvm.c +++ b/hw/intc/arm_gicv3_its_kvm.c @@ -123,7 +123,7 @@ static void kvm_arm_its_realize(DeviceState *dev, Error **errp) kvm_msi_use_devid = true; kvm_gsi_direct_mapping = false; - kvm_msi_via_irqfd_allowed = kvm_irqfds_enabled(); + kvm_msi_via_irqfd_allowed = true; } /** diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 93dccf5dd9..575dee53b3 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -37,7 +37,6 @@ extern bool kvm_split_irqchip; extern bool kvm_async_interrupts_allowed; extern bool kvm_halt_in_kernel_allowed; extern bool kvm_eventfds_allowed; -extern bool kvm_irqfds_allowed; extern bool kvm_resamplefds_allowed; extern bool kvm_msi_via_irqfd_allowed; extern bool kvm_gsi_routing_allowed; @@ -102,8 +101,10 @@ extern bool kvm_msi_use_devid; * Returns: true if we can use irqfds to inject interrupts into * a KVM CPU (ie the kernel supports irqfds and we are running * with a configuration where it is meaningful to use them). + * + * Always available if running with in-kernel irqchip. */ -#define kvm_irqfds_enabled() (kvm_irqfds_allowed) +#define kvm_irqfds_enabled() kvm_irqchip_in_kernel() /** * kvm_resamplefds_enabled: @@ -167,7 +168,6 @@ extern bool kvm_msi_use_devid; #define kvm_async_interrupts_enabled() (false) #define kvm_halt_in_kernel() (false) #define kvm_eventfds_enabled() (false) -#define kvm_irqfds_enabled() (false) #define kvm_resamplefds_enabled() (false) #define kvm_msi_via_irqfd_enabled() (false) #define kvm_gsi_routing_allowed() (false) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 090d617627..26e68c7ab4 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -1420,7 +1420,7 @@ void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift, exit(1); } - kvm_msi_via_irqfd_allowed = kvm_irqfds_enabled(); + kvm_msi_via_irqfd_allowed = true; } static void kvm_cpu_instance_init(CPUState *cs) From f8c0687fe364355ee35896e97fec89b61220340a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 21 Oct 2023 17:09:46 +0200 Subject: [PATCH 120/974] kvm: require KVM_IRQFD for kernel irqchip KVM_IRQFD was introduced in Linux 2.6.32, and since then it has always been available on architectures that support an in-kernel interrupt controller. We can require it unconditionally. Reviewed-by: Manos Pitsidianakis Signed-off-by: Paolo Bonzini --- include/sysemu/kvm.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 575dee53b3..16d58d2598 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -167,6 +167,7 @@ extern bool kvm_msi_use_devid; #define kvm_irqchip_is_split() (false) #define kvm_async_interrupts_enabled() (false) #define kvm_halt_in_kernel() (false) +#define kvm_irqfds_enabled() (false) #define kvm_eventfds_enabled() (false) #define kvm_resamplefds_enabled() (false) #define kvm_msi_via_irqfd_enabled() (false) From d19fe67ba86f60cf7b7de9306475fe90f5ac648f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 18 Oct 2023 12:11:34 +0200 Subject: [PATCH 121/974] kvm: drop reference to KVM_CAP_PCI_2_3 This is a remnant of pre-VFIO device assignment; it is not defined anymore by Linux and not used by QEMU. Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 7 ------- include/sysemu/kvm.h | 1 - include/sysemu/kvm_int.h | 1 - 3 files changed, 9 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index be50d47f7b..50717a0d63 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -2576,8 +2576,6 @@ static int kvm_init(MachineState *ms) s->max_nested_state_len = kvm_check_extension(s, KVM_CAP_NESTED_STATE); - s->intx_set_mask = kvm_check_extension(s, KVM_CAP_PCI_2_3); - s->irq_set_ioctl = KVM_IRQ_LINE; if (kvm_check_extension(s, KVM_CAP_IRQ_INJECT_STATUS)) { s->irq_set_ioctl = KVM_IRQ_LINE_STATUS; @@ -3237,11 +3235,6 @@ int kvm_has_gsi_routing(void) #endif } -int kvm_has_intx_set_mask(void) -{ - return kvm_state->intx_set_mask; -} - bool kvm_arm_supports_user_irq(void) { return kvm_check_extension(kvm_state, KVM_CAP_ARM_USER_IRQ); diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 16d58d2598..bcc9bd96a9 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -216,7 +216,6 @@ int kvm_has_debugregs(void); int kvm_max_nested_state_length(void); int kvm_has_many_ioeventfds(void); int kvm_has_gsi_routing(void); -int kvm_has_intx_set_mask(void); /** * kvm_arm_supports_user_irq diff --git a/include/sysemu/kvm_int.h b/include/sysemu/kvm_int.h index a7dacd12d6..817238b958 100644 --- a/include/sysemu/kvm_int.h +++ b/include/sysemu/kvm_int.h @@ -85,7 +85,6 @@ struct KVMState #endif int max_nested_state_len; int many_ioeventfds; - int intx_set_mask; int kvm_shadow_mem; bool kernel_irqchip_allowed; bool kernel_irqchip_required; From 5d9ec1f4c78ed25720b4fd01ddcddb00db50fa6c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 17 Oct 2023 14:08:22 +0200 Subject: [PATCH 122/974] kvm: assume that many ioeventfds can be created NR_IOBUS_DEVS was increased to 200 in Linux 2.6.34. By Linux 3.5 it had increased to 1000 and later ioeventfds were changed to not count against the limit. But the earlier limit of 200 would already be enough for kvm_check_many_ioeventfds() to be true, so remove the check. Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 47 ---------------------------------------- accel/stubs/kvm-stub.c | 5 ----- hw/virtio/virtio-pci.c | 4 ---- include/sysemu/kvm.h | 1 - include/sysemu/kvm_int.h | 1 - 5 files changed, 58 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 50717a0d63..05be687be1 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -1253,43 +1253,6 @@ static int kvm_set_ioeventfd_pio(int fd, uint16_t addr, uint16_t val, } -static int kvm_check_many_ioeventfds(void) -{ - /* Userspace can use ioeventfd for io notification. This requires a host - * that supports eventfd(2) and an I/O thread; since eventfd does not - * support SIGIO it cannot interrupt the vcpu. - * - * Older kernels have a 6 device limit on the KVM io bus. Find out so we - * can avoid creating too many ioeventfds. - */ -#if defined(CONFIG_EVENTFD) - int ioeventfds[7]; - int i, ret = 0; - for (i = 0; i < ARRAY_SIZE(ioeventfds); i++) { - ioeventfds[i] = eventfd(0, EFD_CLOEXEC); - if (ioeventfds[i] < 0) { - break; - } - ret = kvm_set_ioeventfd_pio(ioeventfds[i], 0, i, true, 2, true); - if (ret < 0) { - close(ioeventfds[i]); - break; - } - } - - /* Decide whether many devices are supported or not */ - ret = i == ARRAY_SIZE(ioeventfds); - - while (i-- > 0) { - kvm_set_ioeventfd_pio(ioeventfds[i], 0, i, false, 2, true); - close(ioeventfds[i]); - } - return ret; -#else - return 0; -#endif -} - static const KVMCapabilityInfo * kvm_check_extension_list(KVMState *s, const KVMCapabilityInfo *list) { @@ -2648,8 +2611,6 @@ static int kvm_init(MachineState *ms) memory_listener_register(&kvm_coalesced_pio_listener, &address_space_io); - s->many_ioeventfds = kvm_check_many_ioeventfds(); - s->sync_mmu = !!kvm_vm_check_extension(kvm_state, KVM_CAP_SYNC_MMU); if (!s->sync_mmu) { ret = ram_block_discard_disable(true); @@ -3218,14 +3179,6 @@ int kvm_max_nested_state_length(void) return kvm_state->max_nested_state_len; } -int kvm_has_many_ioeventfds(void) -{ - if (!kvm_enabled()) { - return 0; - } - return kvm_state->many_ioeventfds; -} - int kvm_has_gsi_routing(void) { #ifdef KVM_CAP_IRQ_ROUTING diff --git a/accel/stubs/kvm-stub.c b/accel/stubs/kvm-stub.c index 19d58f2778..b2d8885853 100644 --- a/accel/stubs/kvm-stub.c +++ b/accel/stubs/kvm-stub.c @@ -40,11 +40,6 @@ bool kvm_has_sync_mmu(void) return false; } -int kvm_has_many_ioeventfds(void) -{ - return 0; -} - int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr) { return 1; diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index af1f4bc187..5f614334ec 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -2114,10 +2114,6 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) bool pcie_port = pci_bus_is_express(pci_get_bus(pci_dev)) && !pci_bus_is_root(pci_get_bus(pci_dev)); - if (kvm_enabled() && !kvm_has_many_ioeventfds()) { - proxy->flags &= ~VIRTIO_PCI_FLAG_USE_IOEVENTFD; - } - /* fd-based ioevents can't be synchronized in record/replay */ if (replay_mode != REPLAY_MODE_NONE) { proxy->flags &= ~VIRTIO_PCI_FLAG_USE_IOEVENTFD; diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index bcc9bd96a9..8c5867ba8a 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -214,7 +214,6 @@ int kvm_has_vcpu_events(void); int kvm_has_robust_singlestep(void); int kvm_has_debugregs(void); int kvm_max_nested_state_length(void); -int kvm_has_many_ioeventfds(void); int kvm_has_gsi_routing(void); /** diff --git a/include/sysemu/kvm_int.h b/include/sysemu/kvm_int.h index 817238b958..840b905a2e 100644 --- a/include/sysemu/kvm_int.h +++ b/include/sysemu/kvm_int.h @@ -84,7 +84,6 @@ struct KVMState QTAILQ_HEAD(, kvm_sw_breakpoint) kvm_sw_breakpoints; #endif int max_nested_state_len; - int many_ioeventfds; int kvm_shadow_mem; bool kernel_irqchip_allowed; bool kernel_irqchip_required; From 126e7f780367b0263d9a112729736d6a0bd6d441 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 17 Oct 2023 14:13:04 +0200 Subject: [PATCH 123/974] kvm: require KVM_CAP_IOEVENTFD and KVM_CAP_IOEVENTFD_ANY_LENGTH KVM_CAP_IOEVENTFD_ANY_LENGTH was added in Linux 4.4, released in 2016. Assume that it is present. Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 22 ++++++---------------- accel/stubs/kvm-stub.c | 2 -- hw/misc/pci-testdev.c | 3 +-- hw/s390x/virtio-ccw.c | 4 ---- hw/virtio/vhost-user.c | 7 +------ hw/virtio/virtio-mmio.c | 4 ---- hw/virtio/virtio-pci.c | 19 ++++--------------- include/sysemu/kvm.h | 19 ------------------- system/memory.c | 16 ++++++---------- 9 files changed, 18 insertions(+), 78 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 05be687be1..120051da64 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -90,7 +90,6 @@ bool kvm_kernel_irqchip; bool kvm_split_irqchip; bool kvm_async_interrupts_allowed; bool kvm_halt_in_kernel_allowed; -bool kvm_eventfds_allowed; bool kvm_resamplefds_allowed; bool kvm_msi_via_irqfd_allowed; bool kvm_gsi_routing_allowed; @@ -98,7 +97,6 @@ bool kvm_gsi_direct_mapping; bool kvm_allowed; bool kvm_readonly_mem_allowed; bool kvm_vm_attributes_allowed; -bool kvm_ioeventfd_any_length_allowed; bool kvm_msi_use_devid; bool kvm_has_guest_debug; static int kvm_sstep_flags; @@ -110,6 +108,8 @@ static const KVMCapabilityInfo kvm_required_capabilites[] = { KVM_CAP_INFO(DESTROY_MEMORY_REGION_WORKS), KVM_CAP_INFO(JOIN_MEMORY_REGIONS_WORKS), KVM_CAP_INFO(INTERNAL_ERROR_DATA), + KVM_CAP_INFO(IOEVENTFD), + KVM_CAP_INFO(IOEVENTFD_ANY_LENGTH), KVM_CAP_LAST_INFO }; @@ -2547,18 +2547,12 @@ static int kvm_init(MachineState *ms) kvm_readonly_mem_allowed = (kvm_check_extension(s, KVM_CAP_READONLY_MEM) > 0); - kvm_eventfds_allowed = - (kvm_check_extension(s, KVM_CAP_IOEVENTFD) > 0); - kvm_resamplefds_allowed = (kvm_check_extension(s, KVM_CAP_IRQFD_RESAMPLE) > 0); kvm_vm_attributes_allowed = (kvm_check_extension(s, KVM_CAP_VM_ATTRIBUTES) > 0); - kvm_ioeventfd_any_length_allowed = - (kvm_check_extension(s, KVM_CAP_IOEVENTFD_ANY_LENGTH) > 0); - #ifdef KVM_CAP_SET_GUEST_DEBUG kvm_has_guest_debug = (kvm_check_extension(s, KVM_CAP_SET_GUEST_DEBUG) > 0); @@ -2595,19 +2589,15 @@ static int kvm_init(MachineState *ms) kvm_irqchip_create(s); } - if (kvm_eventfds_allowed) { - s->memory_listener.listener.eventfd_add = kvm_mem_ioeventfd_add; - s->memory_listener.listener.eventfd_del = kvm_mem_ioeventfd_del; - } + s->memory_listener.listener.eventfd_add = kvm_mem_ioeventfd_add; + s->memory_listener.listener.eventfd_del = kvm_mem_ioeventfd_del; s->memory_listener.listener.coalesced_io_add = kvm_coalesce_mmio_region; s->memory_listener.listener.coalesced_io_del = kvm_uncoalesce_mmio_region; kvm_memory_listener_register(s, &s->memory_listener, &address_space_memory, 0, "kvm-memory"); - if (kvm_eventfds_allowed) { - memory_listener_register(&kvm_io_listener, - &address_space_io); - } + memory_listener_register(&kvm_io_listener, + &address_space_io); memory_listener_register(&kvm_coalesced_pio_listener, &address_space_io); diff --git a/accel/stubs/kvm-stub.c b/accel/stubs/kvm-stub.c index b2d8885853..1b37d9a302 100644 --- a/accel/stubs/kvm-stub.c +++ b/accel/stubs/kvm-stub.c @@ -17,14 +17,12 @@ KVMState *kvm_state; bool kvm_kernel_irqchip; bool kvm_async_interrupts_allowed; -bool kvm_eventfds_allowed; bool kvm_resamplefds_allowed; bool kvm_msi_via_irqfd_allowed; bool kvm_gsi_routing_allowed; bool kvm_gsi_direct_mapping; bool kvm_allowed; bool kvm_readonly_mem_allowed; -bool kvm_ioeventfd_any_length_allowed; bool kvm_msi_use_devid; void kvm_flush_coalesced_mmio_buffer(void) diff --git a/hw/misc/pci-testdev.c b/hw/misc/pci-testdev.c index 49303134e4..acedd0f82b 100644 --- a/hw/misc/pci-testdev.c +++ b/hw/misc/pci-testdev.c @@ -245,7 +245,6 @@ static void pci_testdev_realize(PCIDevice *pci_dev, Error **errp) uint8_t *pci_conf; char *name; int r, i; - bool fastmmio = kvm_ioeventfd_any_length_enabled(); pci_conf = pci_dev->config; @@ -279,7 +278,7 @@ static void pci_testdev_realize(PCIDevice *pci_dev, Error **errp) g_free(name); test->hdr->offset = cpu_to_le32(IOTEST_SIZE(i) + i * IOTEST_ACCESS_WIDTH); test->match_data = strcmp(IOTEST_TEST(i), "wildcard-eventfd"); - if (fastmmio && IOTEST_IS_MEM(i) && !test->match_data) { + if (IOTEST_IS_MEM(i) && !test->match_data) { test->size = 0; } else { test->size = IOTEST_ACCESS_WIDTH; diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 17c548b84f..80453718a3 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -768,10 +768,6 @@ static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp) sch->cssid, sch->ssid, sch->schid, sch->devno, ccw_dev->devno.valid ? "user-configured" : "auto-configured"); - if (kvm_enabled() && !kvm_eventfds_enabled()) { - dev->flags &= ~VIRTIO_CCW_FLAG_USE_IOEVENTFD; - } - /* fd-based ioevents can't be synchronized in record/replay */ if (replay_mode != REPLAY_MODE_NONE) { dev->flags &= ~VIRTIO_CCW_FLAG_USE_IOEVENTFD; diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index b8a7b5542d..7b42ae8aae 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -264,11 +264,6 @@ struct scrub_regions { int fd_idx; }; -static bool ioeventfd_enabled(void) -{ - return !kvm_enabled() || kvm_eventfds_enabled(); -} - static int vhost_user_read_header(struct vhost_dev *dev, VhostUserMsg *msg) { struct vhost_user *u = dev->opaque; @@ -1318,7 +1313,7 @@ static int vhost_set_vring_file(struct vhost_dev *dev, .hdr.size = sizeof(msg.payload.u64), }; - if (ioeventfd_enabled() && file->fd > 0) { + if (file->fd > 0) { fds[fd_num++] = file->fd; } else { msg.payload.u64 |= VHOST_USER_VRING_NOFD_MASK; diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c index c2c6d85475..22f15e1e02 100644 --- a/hw/virtio/virtio-mmio.c +++ b/hw/virtio/virtio-mmio.c @@ -761,10 +761,6 @@ static void virtio_mmio_realizefn(DeviceState *d, Error **errp) qbus_init(&proxy->bus, sizeof(proxy->bus), TYPE_VIRTIO_MMIO_BUS, d, NULL); sysbus_init_irq(sbd, &proxy->irq); - if (!kvm_eventfds_enabled()) { - proxy->flags &= ~VIRTIO_IOMMIO_FLAG_USE_IOEVENTFD; - } - /* fd-based ioevents can't be synchronized in record/replay */ if (replay_mode != REPLAY_MODE_NONE) { proxy->flags &= ~VIRTIO_IOMMIO_FLAG_USE_IOEVENTFD; diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 5f614334ec..205dbf24fb 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -332,7 +332,6 @@ static int virtio_pci_ioeventfd_assign(DeviceState *d, EventNotifier *notifier, VirtQueue *vq = virtio_get_queue(vdev, n); bool legacy = virtio_pci_legacy(proxy); bool modern = virtio_pci_modern(proxy); - bool fast_mmio = kvm_ioeventfd_any_length_enabled(); bool modern_pio = proxy->flags & VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY; MemoryRegion *modern_mr = &proxy->notify.mr; MemoryRegion *modern_notify_mr = &proxy->notify_pio.mr; @@ -343,13 +342,8 @@ static int virtio_pci_ioeventfd_assign(DeviceState *d, EventNotifier *notifier, if (assign) { if (modern) { - if (fast_mmio) { - memory_region_add_eventfd(modern_mr, modern_addr, 0, - false, n, notifier); - } else { - memory_region_add_eventfd(modern_mr, modern_addr, 2, - false, n, notifier); - } + memory_region_add_eventfd(modern_mr, modern_addr, 0, + false, n, notifier); if (modern_pio) { memory_region_add_eventfd(modern_notify_mr, 0, 2, true, n, notifier); @@ -361,13 +355,8 @@ static int virtio_pci_ioeventfd_assign(DeviceState *d, EventNotifier *notifier, } } else { if (modern) { - if (fast_mmio) { - memory_region_del_eventfd(modern_mr, modern_addr, 0, - false, n, notifier); - } else { - memory_region_del_eventfd(modern_mr, modern_addr, 2, - false, n, notifier); - } + memory_region_del_eventfd(modern_mr, modern_addr, 0, + false, n, notifier); if (modern_pio) { memory_region_del_eventfd(modern_notify_mr, 0, 2, true, n, notifier); diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 8c5867ba8a..31c03cc193 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -36,13 +36,11 @@ extern bool kvm_kernel_irqchip; extern bool kvm_split_irqchip; extern bool kvm_async_interrupts_allowed; extern bool kvm_halt_in_kernel_allowed; -extern bool kvm_eventfds_allowed; extern bool kvm_resamplefds_allowed; extern bool kvm_msi_via_irqfd_allowed; extern bool kvm_gsi_routing_allowed; extern bool kvm_gsi_direct_mapping; extern bool kvm_readonly_mem_allowed; -extern bool kvm_ioeventfd_any_length_allowed; extern bool kvm_msi_use_devid; #define kvm_enabled() (kvm_allowed) @@ -86,15 +84,6 @@ extern bool kvm_msi_use_devid; */ #define kvm_halt_in_kernel() (kvm_halt_in_kernel_allowed) -/** - * kvm_eventfds_enabled: - * - * Returns: true if we can use eventfds to receive notifications - * from a KVM CPU (ie the kernel supports eventds and we are running - * with a configuration where it is meaningful to use them). - */ -#define kvm_eventfds_enabled() (kvm_eventfds_allowed) - /** * kvm_irqfds_enabled: * @@ -147,12 +136,6 @@ extern bool kvm_msi_use_devid; */ #define kvm_readonly_mem_enabled() (kvm_readonly_mem_allowed) -/** - * kvm_ioeventfd_any_length_enabled: - * Returns: true if KVM allows any length io eventfd. - */ -#define kvm_ioeventfd_any_length_enabled() (kvm_ioeventfd_any_length_allowed) - /** * kvm_msi_devid_required: * Returns: true if KVM requires a device id to be provided while @@ -168,13 +151,11 @@ extern bool kvm_msi_use_devid; #define kvm_async_interrupts_enabled() (false) #define kvm_halt_in_kernel() (false) #define kvm_irqfds_enabled() (false) -#define kvm_eventfds_enabled() (false) #define kvm_resamplefds_enabled() (false) #define kvm_msi_via_irqfd_enabled() (false) #define kvm_gsi_routing_allowed() (false) #define kvm_gsi_direct_mapping() (false) #define kvm_readonly_mem_enabled() (false) -#define kvm_ioeventfd_any_length_enabled() (false) #define kvm_msi_devid_required() (false) #endif /* CONFIG_KVM_IS_POSSIBLE */ diff --git a/system/memory.c b/system/memory.c index a800fbc9e5..4928f2525d 100644 --- a/system/memory.c +++ b/system/memory.c @@ -1535,7 +1535,12 @@ MemTxResult memory_region_dispatch_write(MemoryRegion *mr, adjust_endianness(mr, &data, op); - if ((!kvm_eventfds_enabled()) && + /* + * FIXME: it's not clear why under KVM the write would be processed + * directly, instead of going through eventfd. This probably should + * test "tcg_enabled() || qtest_enabled()", or should just go away. + */ + if (!kvm_enabled() && memory_region_dispatch_write_eventfds(mr, addr, data, size, attrs)) { return MEMTX_OK; } @@ -2550,8 +2555,6 @@ void memory_region_clear_flush_coalesced(MemoryRegion *mr) } } -static bool userspace_eventfd_warning; - void memory_region_add_eventfd(MemoryRegion *mr, hwaddr addr, unsigned size, @@ -2568,13 +2571,6 @@ void memory_region_add_eventfd(MemoryRegion *mr, }; unsigned i; - if (kvm_enabled() && (!(kvm_eventfds_enabled() || - userspace_eventfd_warning))) { - userspace_eventfd_warning = true; - error_report("Using eventfd without MMIO binding in KVM. " - "Suboptimal performance expected"); - } - if (size) { adjust_endianness(mr, &mrfd.data, size_memop(size) | MO_TE); } From 2cb81af0b1add4fa4f3582a913e0f8aaf1e77eb8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 17 Oct 2023 13:47:21 +0200 Subject: [PATCH 124/974] kvm: unify listeners for PIO address space Since we now assume that ioeventfds are present, kvm_io_listener is always registered. Merge it with kvm_coalesced_pio_listener in a single listener. Since PIO space does not have KVM memslots attached to it, the priority is irrelevant. Reviewed-by: Manos Pitsidianakis Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 120051da64..b59a48da92 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -1105,13 +1105,6 @@ static void kvm_coalesce_pio_del(MemoryListener *listener, } } -static MemoryListener kvm_coalesced_pio_listener = { - .name = "kvm-coalesced-pio", - .coalesced_io_add = kvm_coalesce_pio_add, - .coalesced_io_del = kvm_coalesce_pio_del, - .priority = MEMORY_LISTENER_PRIORITY_MIN, -}; - int kvm_check_extension(KVMState *s, unsigned int extension) { int ret; @@ -1768,6 +1761,8 @@ void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml, static MemoryListener kvm_io_listener = { .name = "kvm-io", + .coalesced_io_add = kvm_coalesce_pio_add, + .coalesced_io_del = kvm_coalesce_pio_del, .eventfd_add = kvm_io_ioeventfd_add, .eventfd_del = kvm_io_ioeventfd_del, .priority = MEMORY_LISTENER_PRIORITY_DEV_BACKEND, @@ -2598,8 +2593,6 @@ static int kvm_init(MachineState *ms) &address_space_memory, 0, "kvm-memory"); memory_listener_register(&kvm_io_listener, &address_space_io); - memory_listener_register(&kvm_coalesced_pio_listener, - &address_space_io); s->sync_mmu = !!kvm_vm_check_extension(kvm_state, KVM_CAP_SYNC_MMU); if (!s->sync_mmu) { From 4b2991666c52de1c708226cd0c022869e802aa26 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 17 Oct 2023 14:15:29 +0200 Subject: [PATCH 125/974] kvm: i386: move KVM_CAP_IRQ_ROUTING detection to kvm_arch_required_capabilities Simple code cleanup. Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index fb6655254f..94b2516c29 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -92,6 +92,7 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = { KVM_CAP_INFO(EXT_CPUID), KVM_CAP_INFO(MP_STATE), KVM_CAP_INFO(SIGNAL_MSI), + KVM_CAP_INFO(IRQ_ROUTING), KVM_CAP_LAST_INFO }; @@ -2590,11 +2591,6 @@ int kvm_arch_init(MachineState *ms, KVMState *s) return ret; } - if (!kvm_check_extension(s, KVM_CAP_IRQ_ROUTING)) { - error_report("kvm: KVM_CAP_IRQ_ROUTING not supported by KVM"); - return -ENOTSUP; - } - has_xsave = kvm_check_extension(s, KVM_CAP_XSAVE); has_xcrs = kvm_check_extension(s, KVM_CAP_XCRS); has_pit_state2 = kvm_check_extension(s, KVM_CAP_PIT_STATE2); From f57a4dd31154e9c054fe75d4ab27829033720a8d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 17 Oct 2023 13:15:11 +0200 Subject: [PATCH 126/974] kvm: i386: require KVM_CAP_DEBUGREGS This was introduced in KVM in Linux 2.6.35, we can require it unconditionally. Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 9 --------- include/sysemu/kvm.h | 1 - include/sysemu/kvm_int.h | 1 - target/i386/kvm/kvm.c | 9 +-------- 4 files changed, 1 insertion(+), 19 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index b59a48da92..aeda902b3e 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -2528,10 +2528,6 @@ static int kvm_init(MachineState *ms) s->robust_singlestep = kvm_check_extension(s, KVM_CAP_X86_ROBUST_SINGLESTEP); -#ifdef KVM_CAP_DEBUGREGS - s->debugregs = kvm_check_extension(s, KVM_CAP_DEBUGREGS); -#endif - s->max_nested_state_len = kvm_check_extension(s, KVM_CAP_NESTED_STATE); s->irq_set_ioctl = KVM_IRQ_LINE; @@ -3152,11 +3148,6 @@ int kvm_has_robust_singlestep(void) return kvm_state->robust_singlestep; } -int kvm_has_debugregs(void) -{ - return kvm_state->debugregs; -} - int kvm_max_nested_state_length(void) { return kvm_state->max_nested_state_len; diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 31c03cc193..02c031d1f2 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -193,7 +193,6 @@ unsigned int kvm_get_free_memslots(void); bool kvm_has_sync_mmu(void); int kvm_has_vcpu_events(void); int kvm_has_robust_singlestep(void); -int kvm_has_debugregs(void); int kvm_max_nested_state_length(void); int kvm_has_gsi_routing(void); diff --git a/include/sysemu/kvm_int.h b/include/sysemu/kvm_int.h index 840b905a2e..151ecc8423 100644 --- a/include/sysemu/kvm_int.h +++ b/include/sysemu/kvm_int.h @@ -79,7 +79,6 @@ struct KVMState bool coalesced_flush_in_progress; int vcpu_events; int robust_singlestep; - int debugregs; #ifdef KVM_CAP_SET_GUEST_DEBUG QTAILQ_HEAD(, kvm_sw_breakpoint) kvm_sw_breakpoints; #endif diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 94b2516c29..b8fb74d7f1 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -93,6 +93,7 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = { KVM_CAP_INFO(MP_STATE), KVM_CAP_INFO(SIGNAL_MSI), KVM_CAP_INFO(IRQ_ROUTING), + KVM_CAP_INFO(DEBUGREGS), KVM_CAP_LAST_INFO }; @@ -4601,10 +4602,6 @@ static int kvm_put_debugregs(X86CPU *cpu) struct kvm_debugregs dbgregs; int i; - if (!kvm_has_debugregs()) { - return 0; - } - memset(&dbgregs, 0, sizeof(dbgregs)); for (i = 0; i < 4; i++) { dbgregs.db[i] = env->dr[i]; @@ -4622,10 +4619,6 @@ static int kvm_get_debugregs(X86CPU *cpu) struct kvm_debugregs dbgregs; int i, ret; - if (!kvm_has_debugregs()) { - return 0; - } - ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_DEBUGREGS, &dbgregs); if (ret < 0) { return ret; From 8bba0a3b768de8d65e91afaff2fa6817e465be21 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 17 Oct 2023 13:18:15 +0200 Subject: [PATCH 127/974] kvm: i386: require KVM_CAP_XSAVE This was introduced in KVM in Linux 2.6.36, and could already be used at the time to save/restore FPU data even on older processor. We can require it unconditionally and stop using KVM_GET/SET_FPU. Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 70 ++----------------------------------------- 1 file changed, 2 insertions(+), 68 deletions(-) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index b8fb74d7f1..513a90f630 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -94,6 +94,7 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = { KVM_CAP_INFO(SIGNAL_MSI), KVM_CAP_INFO(IRQ_ROUTING), KVM_CAP_INFO(DEBUGREGS), + KVM_CAP_INFO(XSAVE), KVM_CAP_LAST_INFO }; @@ -137,7 +138,6 @@ static uint32_t has_architectural_pmu_version; static uint32_t num_architectural_pmu_gp_counters; static uint32_t num_architectural_pmu_fixed_counters; -static int has_xsave; static int has_xsave2; static int has_xcrs; static int has_pit_state2; @@ -1714,10 +1714,8 @@ static void kvm_init_xsave(CPUX86State *env) { if (has_xsave2) { env->xsave_buf_len = QEMU_ALIGN_UP(has_xsave2, 4096); - } else if (has_xsave) { - env->xsave_buf_len = sizeof(struct kvm_xsave); } else { - return; + env->xsave_buf_len = sizeof(struct kvm_xsave); } env->xsave_buf = qemu_memalign(4096, env->xsave_buf_len); @@ -2592,7 +2590,6 @@ int kvm_arch_init(MachineState *ms, KVMState *s) return ret; } - has_xsave = kvm_check_extension(s, KVM_CAP_XSAVE); has_xcrs = kvm_check_extension(s, KVM_CAP_XCRS); has_pit_state2 = kvm_check_extension(s, KVM_CAP_PIT_STATE2); has_sregs2 = kvm_check_extension(s, KVM_CAP_SREGS2) > 0; @@ -2877,40 +2874,11 @@ static int kvm_getput_regs(X86CPU *cpu, int set) return ret; } -static int kvm_put_fpu(X86CPU *cpu) -{ - CPUX86State *env = &cpu->env; - struct kvm_fpu fpu; - int i; - - memset(&fpu, 0, sizeof fpu); - fpu.fsw = env->fpus & ~(7 << 11); - fpu.fsw |= (env->fpstt & 7) << 11; - fpu.fcw = env->fpuc; - fpu.last_opcode = env->fpop; - fpu.last_ip = env->fpip; - fpu.last_dp = env->fpdp; - for (i = 0; i < 8; ++i) { - fpu.ftwx |= (!env->fptags[i]) << i; - } - memcpy(fpu.fpr, env->fpregs, sizeof env->fpregs); - for (i = 0; i < CPU_NB_REGS; i++) { - stq_p(&fpu.xmm[i][0], env->xmm_regs[i].ZMM_Q(0)); - stq_p(&fpu.xmm[i][8], env->xmm_regs[i].ZMM_Q(1)); - } - fpu.mxcsr = env->mxcsr; - - return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_FPU, &fpu); -} - static int kvm_put_xsave(X86CPU *cpu) { CPUX86State *env = &cpu->env; void *xsave = env->xsave_buf; - if (!has_xsave) { - return kvm_put_fpu(cpu); - } x86_cpu_xsave_all_areas(cpu, xsave, env->xsave_buf_len); return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_XSAVE, xsave); @@ -3655,46 +3623,12 @@ static int kvm_put_msrs(X86CPU *cpu, int level) } -static int kvm_get_fpu(X86CPU *cpu) -{ - CPUX86State *env = &cpu->env; - struct kvm_fpu fpu; - int i, ret; - - ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_FPU, &fpu); - if (ret < 0) { - return ret; - } - - env->fpstt = (fpu.fsw >> 11) & 7; - env->fpus = fpu.fsw; - env->fpuc = fpu.fcw; - env->fpop = fpu.last_opcode; - env->fpip = fpu.last_ip; - env->fpdp = fpu.last_dp; - for (i = 0; i < 8; ++i) { - env->fptags[i] = !((fpu.ftwx >> i) & 1); - } - memcpy(env->fpregs, fpu.fpr, sizeof env->fpregs); - for (i = 0; i < CPU_NB_REGS; i++) { - env->xmm_regs[i].ZMM_Q(0) = ldq_p(&fpu.xmm[i][0]); - env->xmm_regs[i].ZMM_Q(1) = ldq_p(&fpu.xmm[i][8]); - } - env->mxcsr = fpu.mxcsr; - - return 0; -} - static int kvm_get_xsave(X86CPU *cpu) { CPUX86State *env = &cpu->env; void *xsave = env->xsave_buf; int type, ret; - if (!has_xsave) { - return kvm_get_fpu(cpu); - } - type = has_xsave2 ? KVM_GET_XSAVE2 : KVM_GET_XSAVE; ret = kvm_vcpu_ioctl(CPU(cpu), type, xsave); if (ret < 0) { From 1a44a79ddf6474587719eff269fd7bacf98817c8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 17 Oct 2023 14:16:58 +0200 Subject: [PATCH 128/974] kvm: i386: require KVM_CAP_SET_VCPU_EVENTS and KVM_CAP_X86_ROBUST_SINGLESTEP Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 9 ---- include/sysemu/kvm.h | 1 - include/sysemu/kvm_int.h | 1 - target/i386/kvm/kvm.c | 92 +--------------------------------------- 4 files changed, 2 insertions(+), 101 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index aeda902b3e..e39a810a4e 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -2524,10 +2524,6 @@ static int kvm_init(MachineState *ms) #ifdef KVM_CAP_VCPU_EVENTS s->vcpu_events = kvm_check_extension(s, KVM_CAP_VCPU_EVENTS); #endif - - s->robust_singlestep = - kvm_check_extension(s, KVM_CAP_X86_ROBUST_SINGLESTEP); - s->max_nested_state_len = kvm_check_extension(s, KVM_CAP_NESTED_STATE); s->irq_set_ioctl = KVM_IRQ_LINE; @@ -3143,11 +3139,6 @@ int kvm_has_vcpu_events(void) return kvm_state->vcpu_events; } -int kvm_has_robust_singlestep(void) -{ - return kvm_state->robust_singlestep; -} - int kvm_max_nested_state_length(void) { return kvm_state->max_nested_state_len; diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 02c031d1f2..80b69d88f6 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -192,7 +192,6 @@ unsigned int kvm_get_max_memslots(void); unsigned int kvm_get_free_memslots(void); bool kvm_has_sync_mmu(void); int kvm_has_vcpu_events(void); -int kvm_has_robust_singlestep(void); int kvm_max_nested_state_length(void); int kvm_has_gsi_routing(void); diff --git a/include/sysemu/kvm_int.h b/include/sysemu/kvm_int.h index 151ecc8423..fd846394be 100644 --- a/include/sysemu/kvm_int.h +++ b/include/sysemu/kvm_int.h @@ -78,7 +78,6 @@ struct KVMState struct kvm_coalesced_mmio_ring *coalesced_mmio_ring; bool coalesced_flush_in_progress; int vcpu_events; - int robust_singlestep; #ifdef KVM_CAP_SET_GUEST_DEBUG QTAILQ_HEAD(, kvm_sw_breakpoint) kvm_sw_breakpoints; #endif diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 513a90f630..4a244174a0 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -95,6 +95,8 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = { KVM_CAP_INFO(IRQ_ROUTING), KVM_CAP_INFO(DEBUGREGS), KVM_CAP_INFO(XSAVE), + KVM_CAP_INFO(VCPU_EVENTS), + KVM_CAP_INFO(X86_ROBUST_SINGLESTEP), KVM_CAP_LAST_INFO }; @@ -690,15 +692,6 @@ void kvm_arch_on_sigbus_vcpu(CPUState *c, int code, void *addr) emit_hypervisor_memory_failure(MEMORY_FAILURE_ACTION_IGNORE, false); } -static void kvm_reset_exception(CPUX86State *env) -{ - env->exception_nr = -1; - env->exception_pending = 0; - env->exception_injected = 0; - env->exception_has_payload = false; - env->exception_payload = 0; -} - static void kvm_queue_exception(CPUX86State *env, int32_t exception_nr, uint8_t exception_has_payload, @@ -731,38 +724,6 @@ static void kvm_queue_exception(CPUX86State *env, } } -static int kvm_inject_mce_oldstyle(X86CPU *cpu) -{ - CPUX86State *env = &cpu->env; - - if (!kvm_has_vcpu_events() && env->exception_nr == EXCP12_MCHK) { - unsigned int bank, bank_num = env->mcg_cap & 0xff; - struct kvm_x86_mce mce; - - kvm_reset_exception(env); - - /* - * There must be at least one bank in use if an MCE is pending. - * Find it and use its values for the event injection. - */ - for (bank = 0; bank < bank_num; bank++) { - if (env->mce_banks[bank * 4 + 1] & MCI_STATUS_VAL) { - break; - } - } - assert(bank < bank_num); - - mce.bank = bank; - mce.status = env->mce_banks[bank * 4 + 1]; - mce.mcg_status = env->mcg_status; - mce.addr = env->mce_banks[bank * 4 + 2]; - mce.misc = env->mce_banks[bank * 4 + 3]; - - return kvm_vcpu_ioctl(CPU(cpu), KVM_X86_SET_MCE, &mce); - } - return 0; -} - static void cpu_update_state(void *opaque, bool running, RunState state) { CPUX86State *env = opaque; @@ -4359,10 +4320,6 @@ static int kvm_put_vcpu_events(X86CPU *cpu, int level) CPUX86State *env = &cpu->env; struct kvm_vcpu_events events = {}; - if (!kvm_has_vcpu_events()) { - return 0; - } - events.flags = 0; if (has_exception_payload) { @@ -4430,10 +4387,6 @@ static int kvm_get_vcpu_events(X86CPU *cpu) struct kvm_vcpu_events events; int ret; - if (!kvm_has_vcpu_events()) { - return 0; - } - memset(&events, 0, sizeof(events)); ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_VCPU_EVENTS, &events); if (ret < 0) { @@ -4499,37 +4452,6 @@ static int kvm_get_vcpu_events(X86CPU *cpu) return 0; } -static int kvm_guest_debug_workarounds(X86CPU *cpu) -{ - CPUState *cs = CPU(cpu); - CPUX86State *env = &cpu->env; - int ret = 0; - unsigned long reinject_trap = 0; - - if (!kvm_has_vcpu_events()) { - if (env->exception_nr == EXCP01_DB) { - reinject_trap = KVM_GUESTDBG_INJECT_DB; - } else if (env->exception_injected == EXCP03_INT3) { - reinject_trap = KVM_GUESTDBG_INJECT_BP; - } - kvm_reset_exception(env); - } - - /* - * Kernels before KVM_CAP_X86_ROBUST_SINGLESTEP overwrote flags.TF - * injected via SET_GUEST_DEBUG while updating GP regs. Work around this - * by updating the debug state once again if single-stepping is on. - * Another reason to call kvm_update_guest_debug here is a pending debug - * trap raise by the guest. On kernels without SET_VCPU_EVENTS we have to - * reinject them via SET_GUEST_DEBUG. - */ - if (reinject_trap || - (!kvm_has_robust_singlestep() && cs->singlestep_enabled)) { - ret = kvm_update_guest_debug(cs, reinject_trap); - } - return ret; -} - static int kvm_put_debugregs(X86CPU *cpu) { CPUX86State *env = &cpu->env; @@ -4702,11 +4624,6 @@ int kvm_arch_put_registers(CPUState *cpu, int level) if (ret < 0) { return ret; } - /* must be before kvm_put_msrs */ - ret = kvm_inject_mce_oldstyle(x86_cpu); - if (ret < 0) { - return ret; - } ret = kvm_put_msrs(x86_cpu, level); if (ret < 0) { return ret; @@ -4730,11 +4647,6 @@ int kvm_arch_put_registers(CPUState *cpu, int level) if (ret < 0) { return ret; } - /* must be last */ - ret = kvm_guest_debug_workarounds(x86_cpu); - if (ret < 0) { - return ret; - } return 0; } From 86f2438fc231666ad7fdf9560fc8f27eedd69252 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 17 Oct 2023 14:30:44 +0200 Subject: [PATCH 129/974] kvm: i386: require KVM_CAP_MCE This was introduced in KVM in Linux 2.6.34, we can require it unconditionally. Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 4a244174a0..42574c2df8 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -97,6 +97,7 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = { KVM_CAP_INFO(XSAVE), KVM_CAP_INFO(VCPU_EVENTS), KVM_CAP_INFO(X86_ROBUST_SINGLESTEP), + KVM_CAP_INFO(MCE), KVM_CAP_LAST_INFO }; @@ -582,14 +583,8 @@ uint64_t kvm_arch_get_supported_msr_feature(KVMState *s, uint32_t index) static int kvm_get_mce_cap_supported(KVMState *s, uint64_t *mce_cap, int *max_banks) { - int r; - - r = kvm_check_extension(s, KVM_CAP_MCE); - if (r > 0) { - *max_banks = r; - return kvm_ioctl(s, KVM_X86_GET_MCE_CAP_SUPPORTED, mce_cap); - } - return -ENOSYS; + *max_banks = kvm_check_extension(s, KVM_CAP_MCE); + return kvm_ioctl(s, KVM_X86_GET_MCE_CAP_SUPPORTED, mce_cap); } static void kvm_mce_inject(X86CPU *cpu, hwaddr paddr, int code) @@ -2116,8 +2111,7 @@ int kvm_arch_init_vcpu(CPUState *cs) if (((env->cpuid_version >> 8)&0xF) >= 6 && (env->features[FEAT_1_EDX] & (CPUID_MCE | CPUID_MCA)) == - (CPUID_MCE | CPUID_MCA) - && kvm_check_extension(cs->kvm_state, KVM_CAP_MCE) > 0) { + (CPUID_MCE | CPUID_MCA)) { uint64_t mcg_cap, unsupported_caps; int banks; int ret; From 700766ba602330a4fc907254a2f45773a6c694fa Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 17 Oct 2023 14:30:44 +0200 Subject: [PATCH 130/974] kvm: i386: require KVM_CAP_ADJUST_CLOCK This was introduced in KVM in Linux 2.6.33, we can require it unconditionally. KVM_CLOCK_TSC_STABLE was only added in Linux 4.9, for now do not require it (though it would allow the removal of some pretty yucky code). Signed-off-by: Paolo Bonzini --- hw/i386/kvm/clock.c | 4 ---- target/i386/kvm/kvm.c | 6 +----- target/i386/kvm/kvm_i386.h | 1 - 3 files changed, 1 insertion(+), 10 deletions(-) diff --git a/hw/i386/kvm/clock.c b/hw/i386/kvm/clock.c index f25977d3f6..e756b0aa43 100644 --- a/hw/i386/kvm/clock.c +++ b/hw/i386/kvm/clock.c @@ -333,10 +333,6 @@ void kvmclock_create(bool create_always) X86CPU *cpu = X86_CPU(first_cpu); assert(kvm_enabled()); - if (!kvm_has_adjust_clock()) { - return; - } - if (create_always || cpu->env.features[FEAT_KVM] & ((1ULL << KVM_FEATURE_CLOCKSOURCE) | (1ULL << KVM_FEATURE_CLOCKSOURCE2))) { diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 42574c2df8..d4bf327fa6 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -98,6 +98,7 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = { KVM_CAP_INFO(VCPU_EVENTS), KVM_CAP_INFO(X86_ROBUST_SINGLESTEP), KVM_CAP_INFO(MCE), + KVM_CAP_INFO(ADJUST_CLOCK), KVM_CAP_LAST_INFO }; @@ -177,11 +178,6 @@ bool kvm_has_adjust_clock_stable(void) return (ret & KVM_CLOCK_TSC_STABLE); } -bool kvm_has_adjust_clock(void) -{ - return kvm_check_extension(kvm_state, KVM_CAP_ADJUST_CLOCK); -} - bool kvm_has_exception_payload(void) { return has_exception_payload; diff --git a/target/i386/kvm/kvm_i386.h b/target/i386/kvm/kvm_i386.h index 55d4e68c34..7e60ea4f23 100644 --- a/target/i386/kvm/kvm_i386.h +++ b/target/i386/kvm/kvm_i386.h @@ -50,7 +50,6 @@ void kvm_request_xsave_components(X86CPU *cpu, uint64_t mask); #ifdef CONFIG_KVM -bool kvm_has_adjust_clock(void); bool kvm_has_adjust_clock_stable(void); bool kvm_has_exception_payload(void); void kvm_synchronize_all_tsc(void); From 52b04ea49d3c4da73828aaf66dab234301428912 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 17 Oct 2023 14:30:44 +0200 Subject: [PATCH 131/974] kvm: i386: require KVM_CAP_SET_IDENTITY_MAP_ADDR This was introduced in KVM in Linux 2.6.32, we can require it unconditionally. Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index d4bf327fa6..e364b842e6 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -99,6 +99,7 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = { KVM_CAP_INFO(X86_ROBUST_SINGLESTEP), KVM_CAP_INFO(MCE), KVM_CAP_INFO(ADJUST_CLOCK), + KVM_CAP_INFO(SET_IDENTITY_MAP_ADDR), KVM_CAP_LAST_INFO }; @@ -2600,20 +2601,13 @@ int kvm_arch_init(MachineState *ms, KVMState *s) * In order to use vm86 mode, an EPT identity map and a TSS are needed. * Since these must be part of guest physical memory, we need to allocate * them, both by setting their start addresses in the kernel and by - * creating a corresponding e820 entry. We need 4 pages before the BIOS. - * - * Older KVM versions may not support setting the identity map base. In - * that case we need to stick with the default, i.e. a 256K maximum BIOS - * size. + * creating a corresponding e820 entry. We need 4 pages before the BIOS, + * so this value allows up to 16M BIOSes. */ - if (kvm_check_extension(s, KVM_CAP_SET_IDENTITY_MAP_ADDR)) { - /* Allows up to 16M BIOSes. */ - identity_base = 0xfeffc000; - - ret = kvm_vm_ioctl(s, KVM_SET_IDENTITY_MAP_ADDR, &identity_base); - if (ret < 0) { - return ret; - } + identity_base = 0xfeffc000; + ret = kvm_vm_ioctl(s, KVM_SET_IDENTITY_MAP_ADDR, &identity_base); + if (ret < 0) { + return ret; } /* Set TSS base one page after EPT identity map. */ From 39dd3e1f55a70f568cc9d280f67467aa4e8a63bd Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 17 Oct 2023 14:29:03 +0200 Subject: [PATCH 132/974] kvm: i8254: require KVM_CAP_PIT2 and KVM_CAP_PIT_STATE2 Signed-off-by: Paolo Bonzini --- hw/i386/kvm/i8254.c | 38 ++++++++++++-------------------------- hw/i386/pc.c | 6 +----- target/i386/kvm/kvm.c | 7 ------- target/i386/kvm/kvm_i386.h | 1 - 4 files changed, 13 insertions(+), 39 deletions(-) diff --git a/hw/i386/kvm/i8254.c b/hw/i386/kvm/i8254.c index a649b2b7ca..e49b9c4b56 100644 --- a/hw/i386/kvm/i8254.c +++ b/hw/i386/kvm/i8254.c @@ -97,24 +97,12 @@ static void kvm_pit_get(PITCommonState *pit) return; } - if (kvm_has_pit_state2()) { - ret = kvm_vm_ioctl(kvm_state, KVM_GET_PIT2, &kpit); - if (ret < 0) { - fprintf(stderr, "KVM_GET_PIT2 failed: %s\n", strerror(-ret)); - abort(); - } - pit->channels[0].irq_disabled = kpit.flags & KVM_PIT_FLAGS_HPET_LEGACY; - } else { - /* - * kvm_pit_state2 is superset of kvm_pit_state struct, - * so we can use it for KVM_GET_PIT as well. - */ - ret = kvm_vm_ioctl(kvm_state, KVM_GET_PIT, &kpit); - if (ret < 0) { - fprintf(stderr, "KVM_GET_PIT failed: %s\n", strerror(-ret)); - abort(); - } + ret = kvm_vm_ioctl(kvm_state, KVM_GET_PIT2, &kpit); + if (ret < 0) { + fprintf(stderr, "KVM_GET_PIT2 failed: %s\n", strerror(-ret)); + abort(); } + pit->channels[0].irq_disabled = kpit.flags & KVM_PIT_FLAGS_HPET_LEGACY; for (i = 0; i < 3; i++) { kchan = &kpit.channels[i]; sc = &pit->channels[i]; @@ -170,12 +158,9 @@ static void kvm_pit_put(PITCommonState *pit) kchan->count_load_time = sc->count_load_time - s->kernel_clock_offset; } - ret = kvm_vm_ioctl(kvm_state, - kvm_has_pit_state2() ? KVM_SET_PIT2 : KVM_SET_PIT, - &kpit); + ret = kvm_vm_ioctl(kvm_state, KVM_SET_PIT2, &kpit); if (ret < 0) { - fprintf(stderr, "%s failed: %s\n", - kvm_has_pit_state2() ? "KVM_SET_PIT2" : "KVM_SET_PIT", + fprintf(stderr, "KVM_SET_PIT2 failed: %s\n", strerror(-ret)); abort(); } @@ -261,11 +246,12 @@ static void kvm_pit_realizefn(DeviceState *dev, Error **errp) }; int ret; - if (kvm_check_extension(kvm_state, KVM_CAP_PIT2)) { - ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_PIT2, &config); - } else { - ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_PIT); + if (!kvm_check_extension(kvm_state, KVM_CAP_PIT_STATE2) || + !kvm_check_extension(kvm_state, KVM_CAP_PIT2)) { + error_setg(errp, "In-kernel PIT not available"); } + + ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_PIT2, &config); if (ret < 0) { error_setg(errp, "Create kernel PIC irqchip failed: %s", strerror(-ret)); diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 11fed78d17..6031234a73 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1214,12 +1214,8 @@ void pc_basic_device_init(struct PCMachineState *pcms, /* * Check if an HPET shall be created. - * - * Without KVM_CAP_PIT_STATE2, we cannot switch off the in-kernel PIT - * when the HPET wants to take over. Thus we have to disable the latter. */ - if (pcms->hpet_enabled && (!kvm_irqchip_in_kernel() || - kvm_has_pit_state2())) { + if (pcms->hpet_enabled) { qemu_irq rtc_irq; hpet = qdev_try_new(TYPE_HPET); diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index e364b842e6..770e81d56e 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -145,7 +145,6 @@ static uint32_t num_architectural_pmu_fixed_counters; static int has_xsave2; static int has_xcrs; -static int has_pit_state2; static int has_sregs2; static int has_exception_payload; static int has_triple_fault_event; @@ -162,11 +161,6 @@ static KVMMSRHandlers msr_handlers[KVM_MSR_FILTER_MAX_RANGES]; static RateLimit bus_lock_ratelimit_ctrl; static int kvm_get_one_msr(X86CPU *cpu, int index, uint64_t *value); -bool kvm_has_pit_state2(void) -{ - return !!has_pit_state2; -} - bool kvm_has_smm(void) { return kvm_vm_check_extension(kvm_state, KVM_CAP_X86_SMM); @@ -2543,7 +2537,6 @@ int kvm_arch_init(MachineState *ms, KVMState *s) } has_xcrs = kvm_check_extension(s, KVM_CAP_XCRS); - has_pit_state2 = kvm_check_extension(s, KVM_CAP_PIT_STATE2); has_sregs2 = kvm_check_extension(s, KVM_CAP_SREGS2) > 0; hv_vpindex_settable = kvm_check_extension(s, KVM_CAP_HYPERV_VP_INDEX); diff --git a/target/i386/kvm/kvm_i386.h b/target/i386/kvm/kvm_i386.h index 7e60ea4f23..30fedcffea 100644 --- a/target/i386/kvm/kvm_i386.h +++ b/target/i386/kvm/kvm_i386.h @@ -33,7 +33,6 @@ bool kvm_has_smm(void); bool kvm_enable_x2apic(void); bool kvm_hv_vpindex_settable(void); -bool kvm_has_pit_state2(void); bool kvm_enable_sgx_provisioning(KVMState *s); bool kvm_hyperv_expand_features(X86CPU *cpu, Error **errp); From 63ba5e13b9943ea6ab228126ccf4ca3c97b7b66b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 9 Oct 2023 12:02:42 +0200 Subject: [PATCH 133/974] system/qtest: Clean up global variable shadowing in qtest_server_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the variable to fix: softmmu/qtest.c:869:13: error: declaration shadows a variable in the global scope [-Werror,-Wshadow] Object *qtest; ^ softmmu/qtest.c:53:15: note: previous declaration is here static QTest *qtest; ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-ID: <20231009100251.56019-2-philmd@linaro.org> Signed-off-by: Thomas Huth --- system/qtest.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/system/qtest.c b/system/qtest.c index 35b643a274..7964f0b248 100644 --- a/system/qtest.c +++ b/system/qtest.c @@ -866,7 +866,7 @@ void qtest_server_init(const char *qtest_chrdev, const char *qtest_log, Error ** { ERRP_GUARD(); Chardev *chr; - Object *qtest; + Object *qobj; chr = qemu_chr_new("qtest", qtest_chrdev, NULL); if (chr == NULL) { @@ -875,18 +875,18 @@ void qtest_server_init(const char *qtest_chrdev, const char *qtest_log, Error ** return; } - qtest = object_new(TYPE_QTEST); - object_property_set_str(qtest, "chardev", chr->label, &error_abort); + qobj = object_new(TYPE_QTEST); + object_property_set_str(qobj, "chardev", chr->label, &error_abort); if (qtest_log) { - object_property_set_str(qtest, "log", qtest_log, &error_abort); + object_property_set_str(qobj, "log", qtest_log, &error_abort); } - object_property_add_child(qdev_get_machine(), "qtest", qtest); - user_creatable_complete(USER_CREATABLE(qtest), errp); + object_property_add_child(qdev_get_machine(), "qtest", qobj); + user_creatable_complete(USER_CREATABLE(qobj), errp); if (*errp) { - object_unparent(qtest); + object_unparent(qobj); } object_unref(OBJECT(chr)); - object_unref(qtest); + object_unref(qobj); } static bool qtest_server_start(QTest *q, Error **errp) From e9894bc95afba5d5e8422b723fcf7d658cba1fea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 9 Oct 2023 12:02:43 +0200 Subject: [PATCH 134/974] tests/throttle: Clean up global variable shadowing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow all other tests pattern from this file, use the global 'cfg' variable to fix: tests/unit/test-throttle.c:621:20: error: declaration shadows a variable in the global scope [-Werror,-Wshadow] ThrottleConfig cfg; ^ tests/unit/test-throttle.c:28:23: note: previous declaration is here static ThrottleConfig cfg; ^ Signed-off-by: Philippe Mathieu-Daudé Acked-by: Alberto Garcia Message-ID: <20231009100251.56019-3-philmd@linaro.org> Signed-off-by: Thomas Huth --- tests/unit/test-throttle.c | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/unit/test-throttle.c b/tests/unit/test-throttle.c index ac35d65d19..2146cfacd3 100644 --- a/tests/unit/test-throttle.c +++ b/tests/unit/test-throttle.c @@ -618,7 +618,6 @@ static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */ { THROTTLE_OPS_TOTAL, THROTTLE_OPS_READ, THROTTLE_OPS_WRITE, } }; - ThrottleConfig cfg; BucketType index; int i; From 79a8d000732a967e554c0d726aaffdb08f70061b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 9 Oct 2023 12:02:44 +0200 Subject: [PATCH 135/974] tests/virtio-scsi: Clean up global variable shadowing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the (unused) 'allow' argument, following the pattern used by the other tests in this file. This fixes: tests/qtest/virtio-scsi-test.c:159:61: error: declaration shadows a variable in the global scope [-Werror,-Wshadow] static void hotplug(void *obj, void *data, QGuestAllocator *alloc) ^ tests/qtest/virtio-scsi-test.c:37:25: note: previous declaration is here static QGuestAllocator *alloc; ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-By: Emmanouil Pitsidianakis Reviewed-by: Thomas Huth Message-ID: <20231009100251.56019-4-philmd@linaro.org> Signed-off-by: Thomas Huth --- tests/qtest/virtio-scsi-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qtest/virtio-scsi-test.c b/tests/qtest/virtio-scsi-test.c index ceaa7f2415..db10d572d0 100644 --- a/tests/qtest/virtio-scsi-test.c +++ b/tests/qtest/virtio-scsi-test.c @@ -156,7 +156,7 @@ static QVirtioSCSIQueues *qvirtio_scsi_init(QVirtioDevice *dev) return vs; } -static void hotplug(void *obj, void *data, QGuestAllocator *alloc) +static void hotplug(void *obj, void *data, QGuestAllocator *t_alloc) { QTestState *qts = global_qtest; From e33ba60bdb4729b6a2531a7d964550e27f3c100e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 9 Oct 2023 12:02:45 +0200 Subject: [PATCH 136/974] tests/cdrom-test: Clean up global variable shadowing in prepare_image() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the variable to fix: tests/qtest/cdrom-test.c:40:50: error: declaration shadows a variable in the global scope [-Werror,-Wshadow] static int prepare_image(const char *arch, char *isoimage) ^ tests/qtest/cdrom-test.c:18:13: note: previous declaration is here static char isoimage[] = "cdrom-boot-iso-XXXXXX"; ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: John Snow Message-ID: <20231009100251.56019-5-philmd@linaro.org> Signed-off-by: Thomas Huth --- tests/qtest/cdrom-test.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/qtest/cdrom-test.c b/tests/qtest/cdrom-test.c index f2a8d91929..0945383789 100644 --- a/tests/qtest/cdrom-test.c +++ b/tests/qtest/cdrom-test.c @@ -37,17 +37,17 @@ static int exec_xorrisofs(const char **args) return exit_status; } -static int prepare_image(const char *arch, char *isoimage) +static int prepare_image(const char *arch, char *isoimagepath) { char srcdir[] = "cdrom-test-dir-XXXXXX"; char *codefile = NULL; int ifh, ret = -1; const char *args[] = { "xorrisofs", "-quiet", "-l", "-no-emul-boot", - "-b", NULL, "-o", isoimage, srcdir, NULL + "-b", NULL, "-o", isoimagepath, srcdir, NULL }; - ifh = mkstemp(isoimage); + ifh = mkstemp(isoimagepath); if (ifh < 0) { perror("Error creating temporary iso image file"); return -1; From a186fedbef01c79bf6c1ca0e9d170fb56f867882 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 9 Oct 2023 12:02:47 +0200 Subject: [PATCH 137/974] tests/rtl8139: Clean up global variable shadowing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the variable to fix: tests/qtest/rtl8139-test.c:28:33: error: declaration shadows a variable in the global scope [-Werror,-Wshadow] static void save_fn(QPCIDevice *dev, int devfn, void *data) ^ tests/qtest/rtl8139-test.c:37:17: error: declaration shadows a variable in the global scope [-Werror,-Wshadow] QPCIDevice *dev; ^ tests/qtest/rtl8139-test.c:25:20: note: previous declaration is here static QPCIDevice *dev; ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-ID: <20231009100251.56019-7-philmd@linaro.org> Signed-off-by: Thomas Huth --- tests/qtest/rtl8139-test.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/qtest/rtl8139-test.c b/tests/qtest/rtl8139-test.c index 4dc0a0d22e..eedf90f65a 100644 --- a/tests/qtest/rtl8139-test.c +++ b/tests/qtest/rtl8139-test.c @@ -22,7 +22,7 @@ static void nop(void) #define CLK 33333333 static QPCIBus *pcibus; -static QPCIDevice *dev; +static QPCIDevice *pcidev; static QPCIBar dev_bar; static void save_fn(QPCIDevice *dev, int devfn, void *data) @@ -46,7 +46,7 @@ static QPCIDevice *get_device(void) #define PORT(name, len, val) \ static unsigned __attribute__((unused)) in_##name(void) \ { \ - unsigned res = qpci_io_read##len(dev, dev_bar, (val)); \ + unsigned res = qpci_io_read##len(pcidev, dev_bar, (val)); \ if (verbosity_level >= 2) { \ g_test_message("*%s -> %x", #name, res); \ } \ @@ -57,7 +57,7 @@ static void out_##name(unsigned v) \ if (verbosity_level >= 2) { \ g_test_message("%x -> *%s", v, #name); \ } \ - qpci_io_write##len(dev, dev_bar, (val), v); \ + qpci_io_write##len(pcidev, dev_bar, (val), v); \ } PORT(Timer, l, 0x48) @@ -189,11 +189,11 @@ static void test_init(void) { uint64_t barsize; - dev = get_device(); + pcidev = get_device(); - dev_bar = qpci_iomap(dev, 0, &barsize); + dev_bar = qpci_iomap(pcidev, 0, &barsize); - qpci_device_enable(dev); + qpci_device_enable(pcidev); test_timer(); } From ec3ad0face736a48fc6d77e65f0e53ae5a5cd0d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 9 Oct 2023 12:02:48 +0200 Subject: [PATCH 138/974] tests/npcm7xx_adc: Clean up global variable shadowing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the global 'adc' variable in order to avoid: tests/qtest/npcm7xx_adc-test.c:98:58: error: declaration shadows a variable in the global scope [-Werror,-Wshadow] static uint32_t adc_read_con(QTestState *qts, const ADC *adc) ^ tests/qtest/npcm7xx_adc-test.c:103:55: error: declaration shadows a variable in the global scope [-Werror,-Wshadow] static void adc_write_con(QTestState *qts, const ADC *adc, uint32_t value) ^ tests/qtest/npcm7xx_adc-test.c:108:59: error: declaration shadows a variable in the global scope [-Werror,-Wshadow] static uint32_t adc_read_data(QTestState *qts, const ADC *adc) ^ tests/qtest/npcm7xx_adc-test.c:119:53: error: declaration shadows a variable in the global scope [-Werror,-Wshadow] static void adc_qom_set(QTestState *qts, const ADC *adc, ^ tests/qtest/npcm7xx_adc-test.c:135:57: error: declaration shadows a variable in the global scope [-Werror,-Wshadow] static void adc_write_input(QTestState *qts, const ADC *adc, ^ tests/qtest/npcm7xx_adc-test.c:144:56: error: declaration shadows a variable in the global scope [-Werror,-Wshadow] static void adc_write_vref(QTestState *qts, const ADC *adc, uint32_t value) ^ tests/qtest/npcm7xx_adc-test.c:162:59: error: declaration shadows a variable in the global scope [-Werror,-Wshadow] static uint32_t adc_prescaler(QTestState *qts, const ADC *adc) ^ tests/qtest/npcm7xx_adc-test.c:175:64: error: declaration shadows a variable in the global scope [-Werror,-Wshadow] static void adc_wait_conv_finished(QTestState *qts, const ADC *adc, ^ tests/qtest/npcm7xx_adc-test.c:196:16: error: declaration shadows a variable in the global scope [-Werror,-Wshadow] const ADC *adc = adc_p; ^ tests/qtest/npcm7xx_adc-test.c:207:16: error: declaration shadows a variable in the global scope [-Werror,-Wshadow] const ADC *adc = adc_p; ^ tests/qtest/npcm7xx_adc-test.c:235:16: error: declaration shadows a variable in the global scope [-Werror,-Wshadow] const ADC *adc = adc_p; ^ tests/qtest/npcm7xx_adc-test.c:267:16: error: declaration shadows a variable in the global scope [-Werror,-Wshadow] const ADC *adc = adc_p; ^ tests/qtest/npcm7xx_adc-test.c:293:16: error: declaration shadows a variable in the global scope [-Werror,-Wshadow] const ADC *adc = adc_p; ^ tests/qtest/npcm7xx_adc-test.c:311:16: error: declaration shadows a variable in the global scope [-Werror,-Wshadow] const ADC *adc = adc_p; ^ tests/qtest/npcm7xx_adc-test.c:93:5: note: previous declaration is here ADC adc = { ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-ID: <20231009100251.56019-8-philmd@linaro.org> Signed-off-by: Thomas Huth --- tests/qtest/npcm7xx_adc-test.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/qtest/npcm7xx_adc-test.c b/tests/qtest/npcm7xx_adc-test.c index 8048044d28..e751a72e36 100644 --- a/tests/qtest/npcm7xx_adc-test.c +++ b/tests/qtest/npcm7xx_adc-test.c @@ -90,7 +90,7 @@ typedef struct ADC { uint64_t base_addr; } ADC; -ADC adc = { +ADC adc_defs = { .irq = 0, .base_addr = 0xf000c000 }; @@ -367,12 +367,12 @@ int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); - add_test(init, &adc); - add_test(convert_internal, &adc); - add_test(convert_external, &adc); - add_test(interrupt, &adc); - add_test(reset, &adc); - add_test(calibrate, &adc); + add_test(init, &adc_defs); + add_test(convert_internal, &adc_defs); + add_test(convert_external, &adc_defs); + add_test(interrupt, &adc_defs); + add_test(reset, &adc_defs); + add_test(calibrate, &adc_defs); return g_test_run(); } From 0c2c2932a97e73fa400af6ab4e4f6625ead3e4a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 9 Oct 2023 12:02:49 +0200 Subject: [PATCH 139/974] tests/aio: Clean up global variable shadowing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the argument to fix: tests/unit/test-aio.c:130:44: error: declaration shadows a variable in the global scope [-Werror,-Wshadow] static void set_event_notifier(AioContext *ctx, EventNotifier *notifier, ^ tests/unit/test-aio.c:22:20: note: previous declaration is here static AioContext *ctx; ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-ID: <20231009100251.56019-9-philmd@linaro.org> Signed-off-by: Thomas Huth --- tests/unit/test-aio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/test-aio.c b/tests/unit/test-aio.c index 71ed31a4db..337b6e4ea7 100644 --- a/tests/unit/test-aio.c +++ b/tests/unit/test-aio.c @@ -127,10 +127,10 @@ static void *test_acquire_thread(void *opaque) return NULL; } -static void set_event_notifier(AioContext *ctx, EventNotifier *notifier, +static void set_event_notifier(AioContext *nctx, EventNotifier *notifier, EventNotifierHandler *handler) { - aio_set_event_notifier(ctx, notifier, handler, NULL, NULL); + aio_set_event_notifier(nctx, notifier, handler, NULL, NULL); } static void dummy_notifier_read(EventNotifier *n) From 89c90405648d34470b7f19120e6bf9cc054ad8b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 9 Oct 2023 12:02:51 +0200 Subject: [PATCH 140/974] tests/coroutine: Clean up global variable shadowing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the global variable to avoid: tests/unit/test-coroutine.c:430:11: error: declaration shadows a variable in the global scope [-Werror,-Wshadow] bool *done = opaque; ^ tests/unit/test-coroutine.c:438:10: error: declaration shadows a variable in the global scope [-Werror,-Wshadow] bool done = false; ^ tests/unit/test-coroutine.c:198:12: note: previous declaration is here static int done; ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-ID: <20231009100251.56019-11-philmd@linaro.org> Signed-off-by: Thomas Huth --- tests/unit/test-coroutine.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/unit/test-coroutine.c b/tests/unit/test-coroutine.c index a2563647e7..49d4d9b251 100644 --- a/tests/unit/test-coroutine.c +++ b/tests/unit/test-coroutine.c @@ -195,7 +195,7 @@ static void test_no_dangling_access(void) } static bool locked; -static int done; +static int done_count; static void coroutine_fn mutex_fn(void *opaque) { @@ -206,7 +206,7 @@ static void coroutine_fn mutex_fn(void *opaque) qemu_coroutine_yield(); locked = false; qemu_co_mutex_unlock(m); - done++; + done_count++; } static void coroutine_fn lockable_fn(void *opaque) @@ -218,7 +218,7 @@ static void coroutine_fn lockable_fn(void *opaque) qemu_coroutine_yield(); locked = false; qemu_lockable_unlock(x); - done++; + done_count++; } static void do_test_co_mutex(CoroutineEntry *entry, void *opaque) @@ -226,7 +226,7 @@ static void do_test_co_mutex(CoroutineEntry *entry, void *opaque) Coroutine *c1 = qemu_coroutine_create(entry, opaque); Coroutine *c2 = qemu_coroutine_create(entry, opaque); - done = 0; + done_count = 0; qemu_coroutine_enter(c1); g_assert(locked); qemu_coroutine_enter(c2); @@ -235,11 +235,11 @@ static void do_test_co_mutex(CoroutineEntry *entry, void *opaque) * terminates. */ qemu_coroutine_enter(c1); - g_assert_cmpint(done, ==, 1); + g_assert_cmpint(done_count, ==, 1); g_assert(locked); qemu_coroutine_enter(c2); - g_assert_cmpint(done, ==, 2); + g_assert_cmpint(done_count, ==, 2); g_assert(!locked); } From c7437f0ddb8ee45bf96d949ddfcbb7697ae3d415 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 6 Oct 2023 09:52:47 +0200 Subject: [PATCH 141/974] docs/about: Mark the old pc-i440fx-2.0 - 2.3 machine types as deprecated MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As we've seen in the past, it's useful for deprecating old machine types to finally be able to get of legacy code or do other clean-ups (see e.g. commit ea985d235b868047 that was used to drop the PCI code in the 128k bios binaries to free some precious space in those binaries). So let's continue deprecating the oldest pc machine types. QEMU 2.3 has been released 8 years ago, so that's plenty of time since such machine types have been used by default, thus deprecating pc-i440fx-2.0 up to pc-i440fx-2.3 should be fine nowadays. Message-ID: <20231006075247.403364-1-thuth@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth --- docs/about/deprecated.rst | 8 ++++++++ hw/i386/pc_piix.c | 1 + 2 files changed, 9 insertions(+) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 2febd2d12f..4e0eb2fe02 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -247,6 +247,14 @@ deprecated; use the new name ``dtb-randomness`` instead. The new name better reflects the way this property affects all random data within the device tree blob, not just the ``kaslr-seed`` node. +``pc-i440fx-2.0`` up to ``pc-i440fx-2.3`` (since 8.2) +''''''''''''''''''''''''''''''''''''''''''''''''''''' + +These old machine types are quite neglected nowadays and thus might have +various pitfalls with regards to live migration. Use a newer machine type +instead. + + Backend options --------------- diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 334d9a0299..26e161beb9 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -849,6 +849,7 @@ static void pc_i440fx_2_3_machine_options(MachineClass *m) { pc_i440fx_2_4_machine_options(m); m->hw_version = "2.3.0"; + m->deprecation_reason = "old and unattended - use a newer version instead"; compat_props_add(m->compat_props, hw_compat_2_3, hw_compat_2_3_len); compat_props_add(m->compat_props, pc_compat_2_3, pc_compat_2_3_len); } From c73272f52fc0e7614e4110b147b9d6efb589854b Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 16 Oct 2023 18:10:53 +0200 Subject: [PATCH 142/974] tests/vm/freebsd: Add additional library paths for libfdt libfdt is installed in /usr/local on FreeBSD, and since this library does not have a pkg-config file, we have to specify the paths manually. This way we can avoid that Meson has to recompile the dtc subproject each time. Message-ID: <20231016161053.39150-1-thuth@redhat.com> Reviewed-by: Warner Losh Signed-off-by: Thomas Huth --- tests/vm/freebsd | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/vm/freebsd b/tests/vm/freebsd index ac51376c82..b581bd17fb 100755 --- a/tests/vm/freebsd +++ b/tests/vm/freebsd @@ -38,8 +38,9 @@ class FreeBSDVM(basevm.BaseVM): cd $(mktemp -d /home/qemu/qemu-test.XXXXXX); mkdir src build; cd src; tar -xf /dev/vtbd1; - cd ../build - ../src/configure --python=python3.9 {configure_opts}; + cd ../build; + ../src/configure --python=python3.9 --extra-ldflags=-L/usr/local/lib \ + --extra-cflags=-I/usr/local/include {configure_opts}; gmake --output-sync -j{jobs} {target} {verbose}; """ From 1aa84a4b6e2cd3f0969101f1e608415e5381d9a2 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 18 Oct 2023 22:11:23 +0300 Subject: [PATCH 143/974] ipmi-bt-test: force ipv4 We open ipv4 listening socket. But "localhost" in qemu parameters may load to Qemu trying to connect with ipv6 and fail with "Connection refused". Force ipv4 by using ipv4 ip address. Signed-off-by: Vladimir Sementsov-Ogievskiy Message-ID: <20231018191123.1176602-1-vsementsov@yandex-team.ru> Acked-by: Corey Minyard Signed-off-by: Thomas Huth --- tests/qtest/ipmi-bt-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qtest/ipmi-bt-test.c b/tests/qtest/ipmi-bt-test.c index ed431e34e6..383239bcd4 100644 --- a/tests/qtest/ipmi-bt-test.c +++ b/tests/qtest/ipmi-bt-test.c @@ -411,7 +411,7 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); global_qtest = qtest_initf( - " -chardev socket,id=ipmi0,host=localhost,port=%d,reconnect=10" + " -chardev socket,id=ipmi0,host=127.0.0.1,port=%d,reconnect=10" " -device ipmi-bmc-extern,chardev=ipmi0,id=bmc0" " -device isa-ipmi-bt,bmc=bmc0", emu_port); qtest_irq_intercept_in(global_qtest, "ioapic"); From 3bcc53980b05dbcdc9bc70fc7ec3bc37320edcbd Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Sep 2023 19:54:52 +0100 Subject: [PATCH 144/974] target/arm: Correct minor errors in Cortex-A710 definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Correct a couple of minor errors in the Cortex-A710 definition: * ID_AA64DFR0_EL1.DebugVer is 9 (indicating Armv8.4 debug architecture) * ID_AA64ISAR1_EL1.APA is 5 (indicating more PAuth support) * there is an IMPDEF CPUCFR_EL1, like that on the Neoverse-N1 Fixes: e3d45c0a89576 ("target/arm: Implement cortex-a710") Signed-off-by: Peter Maydell Tested-by: Marcin Juszkiewicz Reviewed-by: Alex Bennée Message-id: 20230915185453.1871167-2-peter.maydell@linaro.org --- target/arm/tcg/cpu64.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index d978aa5f7a..e2bcac4854 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -840,6 +840,13 @@ static const ARMCPRegInfo cortex_a710_cp_reginfo[] = { { .name = "CPUPFR_EL3", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 6, .crn = 15, .crm = 8, .opc2 = 6, .access = PL3_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + /* + * Report CPUCFR_EL1.SCU as 1, as we do not implement the DSU + * (and in particular its system registers). + */ + { .name = "CPUCFR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 15, .crm = 0, .opc2 = 0, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 4 }, /* * Stub RAMINDEX, as we don't actually implement caches, BTB, @@ -909,12 +916,12 @@ static void aarch64_a710_initfn(Object *obj) cpu->isar.id_aa64pfr0 = 0x1201111120111112ull; /* GIC filled in later */ cpu->isar.id_aa64pfr1 = 0x0000000000000221ull; cpu->isar.id_aa64zfr0 = 0x0000110100110021ull; /* with Crypto */ - cpu->isar.id_aa64dfr0 = 0x000011f010305611ull; + cpu->isar.id_aa64dfr0 = 0x000011f010305619ull; cpu->isar.id_aa64dfr1 = 0; cpu->id_aa64afr0 = 0; cpu->id_aa64afr1 = 0; cpu->isar.id_aa64isar0 = 0x0221111110212120ull; /* with Crypto */ - cpu->isar.id_aa64isar1 = 0x0010111101211032ull; + cpu->isar.id_aa64isar1 = 0x0010111101211052ull; cpu->isar.id_aa64mmfr0 = 0x0000022200101122ull; cpu->isar.id_aa64mmfr1 = 0x0000000010212122ull; cpu->isar.id_aa64mmfr2 = 0x1221011110101011ull; From dfff1000fef24f6686e0be5e6472613985a363dc Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Sep 2023 19:54:53 +0100 Subject: [PATCH 145/974] target/arm: Implement Neoverse N2 CPU model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement a model of the Neoverse N2 CPU. This is an Armv9.0-A processor very similar to the Cortex-A710. The differences are: * no FEAT_EVT * FEAT_DGH (data gathering hint) * FEAT_NV (not yet implemented in QEMU) * Statistical Profiling Extension (not implemented in QEMU) * 48 bit physical address range, not 40 * CTR_EL0.DIC = 1 (no explicit icache cleaning needed) * PMCR_EL0.N = 6 (always 6 PMU counters, not 20) Because it has 48-bit physical address support, we can use this CPU in the sbsa-ref board as well as the virt board. Signed-off-by: Peter Maydell Tested-by: Marcin Juszkiewicz Reviewed-by: Alex Bennée Message-id: 20230915185453.1871167-3-peter.maydell@linaro.org --- docs/system/arm/virt.rst | 1 + hw/arm/sbsa-ref.c | 1 + hw/arm/virt.c | 1 + target/arm/tcg/cpu64.c | 103 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 106 insertions(+) diff --git a/docs/system/arm/virt.rst b/docs/system/arm/virt.rst index e1697ac8f4..7c4c80180c 100644 --- a/docs/system/arm/virt.rst +++ b/docs/system/arm/virt.rst @@ -63,6 +63,7 @@ Supported guest CPU types: - ``host`` (with KVM only) - ``neoverse-n1`` (64-bit) - ``neoverse-v1`` (64-bit) +- ``neoverse-n2`` (64-bit) - ``max`` (same as ``host`` for KVM; best possible emulation with TCG) Note that the default is ``cortex-a15``, so for an AArch64 guest you must diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c index e8a82618f0..bce44690e5 100644 --- a/hw/arm/sbsa-ref.c +++ b/hw/arm/sbsa-ref.c @@ -149,6 +149,7 @@ static const char * const valid_cpus[] = { ARM_CPU_TYPE_NAME("cortex-a72"), ARM_CPU_TYPE_NAME("neoverse-n1"), ARM_CPU_TYPE_NAME("neoverse-v1"), + ARM_CPU_TYPE_NAME("neoverse-n2"), ARM_CPU_TYPE_NAME("max"), }; diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 529f1c089c..92085d2d8f 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -215,6 +215,7 @@ static const char *valid_cpus[] = { ARM_CPU_TYPE_NAME("a64fx"), ARM_CPU_TYPE_NAME("neoverse-n1"), ARM_CPU_TYPE_NAME("neoverse-v1"), + ARM_CPU_TYPE_NAME("neoverse-n2"), #endif ARM_CPU_TYPE_NAME("cortex-a53"), ARM_CPU_TYPE_NAME("cortex-a57"), diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index e2bcac4854..a9a8c0a059 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -963,6 +963,108 @@ static void aarch64_a710_initfn(Object *obj) aarch64_add_sve_properties(obj); } +/* Extra IMPDEF regs in the N2 beyond those in the A710 */ +static const ARMCPRegInfo neoverse_n2_cp_reginfo[] = { + { .name = "CPURNDBR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 15, .crm = 3, .opc2 = 0, + .access = PL3_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPURNDPEID_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 15, .crm = 3, .opc2 = 1, + .access = PL3_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, +}; + +static void aarch64_neoverse_n2_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "arm,neoverse-n2"; + set_feature(&cpu->env, ARM_FEATURE_V8); + set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); + set_feature(&cpu->env, ARM_FEATURE_AARCH64); + set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); + set_feature(&cpu->env, ARM_FEATURE_EL2); + set_feature(&cpu->env, ARM_FEATURE_EL3); + set_feature(&cpu->env, ARM_FEATURE_PMU); + + /* Ordered by Section B.5: AArch64 ID registers */ + cpu->midr = 0x410FD493; /* r0p3 */ + cpu->revidr = 0; + cpu->isar.id_pfr0 = 0x21110131; + cpu->isar.id_pfr1 = 0x00010000; /* GIC filled in later */ + cpu->isar.id_dfr0 = 0x16011099; + cpu->id_afr0 = 0; + cpu->isar.id_mmfr0 = 0x10201105; + cpu->isar.id_mmfr1 = 0x40000000; + cpu->isar.id_mmfr2 = 0x01260000; + cpu->isar.id_mmfr3 = 0x02122211; + cpu->isar.id_isar0 = 0x02101110; + cpu->isar.id_isar1 = 0x13112111; + cpu->isar.id_isar2 = 0x21232042; + cpu->isar.id_isar3 = 0x01112131; + cpu->isar.id_isar4 = 0x00010142; + cpu->isar.id_isar5 = 0x11011121; /* with Crypto */ + cpu->isar.id_mmfr4 = 0x01021110; + cpu->isar.id_isar6 = 0x01111111; + cpu->isar.mvfr0 = 0x10110222; + cpu->isar.mvfr1 = 0x13211111; + cpu->isar.mvfr2 = 0x00000043; + cpu->isar.id_pfr2 = 0x00000011; + cpu->isar.id_aa64pfr0 = 0x1201111120111112ull; /* GIC filled in later */ + cpu->isar.id_aa64pfr1 = 0x0000000000000221ull; + cpu->isar.id_aa64zfr0 = 0x0000110100110021ull; /* with Crypto */ + cpu->isar.id_aa64dfr0 = 0x000011f210305619ull; + cpu->isar.id_aa64dfr1 = 0; + cpu->id_aa64afr0 = 0; + cpu->id_aa64afr1 = 0; + cpu->isar.id_aa64isar0 = 0x0221111110212120ull; /* with Crypto */ + cpu->isar.id_aa64isar1 = 0x0011111101211052ull; + cpu->isar.id_aa64mmfr0 = 0x0000022200101125ull; + cpu->isar.id_aa64mmfr1 = 0x0000000010212122ull; + cpu->isar.id_aa64mmfr2 = 0x1221011112101011ull; + cpu->clidr = 0x0000001482000023ull; + cpu->gm_blocksize = 4; + cpu->ctr = 0x00000004b444c004ull; + cpu->dcz_blocksize = 4; + /* TODO FEAT_MPAM: mpamidr_el1 = 0x0000_0001_001e_01ff */ + + /* Section B.7.2: PMCR_EL0 */ + cpu->isar.reset_pmcr_el0 = 0x3000; /* with 6 counters */ + + /* Section B.8.9: ICH_VTR_EL2 */ + cpu->gic_num_lrs = 4; + cpu->gic_vpribits = 5; + cpu->gic_vprebits = 5; + cpu->gic_pribits = 5; + + /* Section 14: Scalable Vector Extensions support */ + cpu->sve_vq.supported = 1 << 0; /* 128bit */ + + /* + * The Neoverse N2 TRM does not list CCSIDR values. The layout of + * the caches are in text in Table 7-1, Table 8-1, and Table 9-1. + * + * L1: 4-way set associative 64-byte line size, total 64K. + * L2: 8-way set associative 64 byte line size, total either 512K or 1024K. + */ + cpu->ccsidr[0] = make_ccsidr64(4, 64, 64 * KiB); /* L1 dcache */ + cpu->ccsidr[1] = cpu->ccsidr[0]; /* L1 icache */ + cpu->ccsidr[2] = make_ccsidr64(8, 64, 512 * KiB); /* L2 cache */ + + /* FIXME: Not documented -- copied from neoverse-v1 */ + cpu->reset_sctlr = 0x30c50838; + + /* + * The Neoverse N2 has all of the Cortex-A710 IMPDEF registers, + * and a few more RNG related ones. + */ + define_arm_cp_regs(cpu, cortex_a710_cp_reginfo); + define_arm_cp_regs(cpu, neoverse_n2_cp_reginfo); + + aarch64_add_pauth_properties(obj); + aarch64_add_sve_properties(obj); +} + /* * -cpu max: a CPU with as many features enabled as our emulation supports. * The version of '-cpu max' for qemu-system-arm is defined in cpu32.c; @@ -1165,6 +1267,7 @@ static const ARMCPUInfo aarch64_cpus[] = { { .name = "a64fx", .initfn = aarch64_a64fx_initfn }, { .name = "neoverse-n1", .initfn = aarch64_neoverse_n1_initfn }, { .name = "neoverse-v1", .initfn = aarch64_neoverse_v1_initfn }, + { .name = "neoverse-n2", .initfn = aarch64_neoverse_n2_initfn }, }; static void aarch64_cpu_register_types(void) From 5a534314a85b735f766aa64b35697ed1d59f7068 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 24 Oct 2023 17:35:05 +0100 Subject: [PATCH 146/974] target/arm: Move feature test functions to their own header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The feature test functions isar_feature_*() now take up nearly a thousand lines in target/arm/cpu.h. This header file is included by a lot of source files, most of which don't need these functions. Move the feature test functions to their own header file. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20231024163510.2972081-2-peter.maydell@linaro.org --- bsd-user/arm/target_arch.h | 1 + hw/arm/armv7m.c | 1 + hw/intc/armv7m_nvic.c | 1 + linux-user/aarch64/cpu_loop.c | 1 + linux-user/aarch64/signal.c | 1 + linux-user/aarch64/target_prctl.h | 2 + linux-user/arm/signal.c | 1 + linux-user/elfload.c | 4 + linux-user/mmap.c | 4 + target/arm/arch_dump.c | 1 + target/arm/cpu-features.h | 994 ++++++++++++++++++++++++++++++ target/arm/cpu.c | 1 + target/arm/cpu.h | 971 ----------------------------- target/arm/cpu64.c | 1 + target/arm/debug_helper.c | 1 + target/arm/gdbstub.c | 1 + target/arm/helper.c | 1 + target/arm/internals.h | 1 + target/arm/kvm64.c | 1 + target/arm/machine.c | 1 + target/arm/ptw.c | 1 + target/arm/tcg/cpu64.c | 1 + target/arm/tcg/hflags.c | 1 + target/arm/tcg/m_helper.c | 1 + target/arm/tcg/op_helper.c | 1 + target/arm/tcg/pauth_helper.c | 1 + target/arm/tcg/tlb_helper.c | 1 + target/arm/tcg/translate.h | 2 +- target/arm/vfp_helper.c | 1 + 29 files changed, 1028 insertions(+), 972 deletions(-) create mode 100644 target/arm/cpu-features.h diff --git a/bsd-user/arm/target_arch.h b/bsd-user/arm/target_arch.h index 561934bbd2..d80cb85c64 100644 --- a/bsd-user/arm/target_arch.h +++ b/bsd-user/arm/target_arch.h @@ -21,6 +21,7 @@ #define TARGET_ARCH_H #include "qemu.h" +#include "target/arm/cpu-features.h" void target_cpu_set_tls(CPUARMState *env, target_ulong newtls); target_ulong target_cpu_get_tls(CPUARMState *env); diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c index 1f78e18872..d10abb36a8 100644 --- a/hw/arm/armv7m.c +++ b/hw/arm/armv7m.c @@ -21,6 +21,7 @@ #include "qemu/module.h" #include "qemu/log.h" #include "target/arm/idau.h" +#include "target/arm/cpu-features.h" #include "migration/vmstate.h" /* Bitbanded IO. Each word corresponds to a single bit. */ diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 03b6b8c986..942be7bd11 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -21,6 +21,7 @@ #include "sysemu/tcg.h" #include "sysemu/runstate.h" #include "target/arm/cpu.h" +#include "target/arm/cpu-features.h" #include "exec/exec-all.h" #include "exec/memop.h" #include "qemu/log.h" diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index 2e2f7cf218..8c20dc8a39 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -25,6 +25,7 @@ #include "qemu/guest-random.h" #include "semihosting/common-semi.h" #include "target/arm/syndrome.h" +#include "target/arm/cpu-features.h" #define get_user_code_u32(x, gaddr, env) \ ({ abi_long __r = get_user_u32((x), (gaddr)); \ diff --git a/linux-user/aarch64/signal.c b/linux-user/aarch64/signal.c index b265cfd470..a1e22d526d 100644 --- a/linux-user/aarch64/signal.c +++ b/linux-user/aarch64/signal.c @@ -21,6 +21,7 @@ #include "user-internals.h" #include "signal-common.h" #include "linux-user/trace.h" +#include "target/arm/cpu-features.h" struct target_sigcontext { uint64_t fault_address; diff --git a/linux-user/aarch64/target_prctl.h b/linux-user/aarch64/target_prctl.h index 907c314146..5067e7d731 100644 --- a/linux-user/aarch64/target_prctl.h +++ b/linux-user/aarch64/target_prctl.h @@ -6,6 +6,8 @@ #ifndef AARCH64_TARGET_PRCTL_H #define AARCH64_TARGET_PRCTL_H +#include "target/arm/cpu-features.h" + static abi_long do_prctl_sve_get_vl(CPUArchState *env) { ARMCPU *cpu = env_archcpu(env); diff --git a/linux-user/arm/signal.c b/linux-user/arm/signal.c index cf99fd7b8a..4020601c54 100644 --- a/linux-user/arm/signal.c +++ b/linux-user/arm/signal.c @@ -21,6 +21,7 @@ #include "user-internals.h" #include "signal-common.h" #include "linux-user/trace.h" +#include "target/arm/cpu-features.h" struct target_sigcontext { abi_ulong trap_no; diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 2e3809f03c..baa69e5535 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -23,6 +23,10 @@ #include "target_signal.h" #include "accel/tcg/debuginfo.h" +#ifdef TARGET_ARM +#include "target/arm/cpu-features.h" +#endif + #ifdef _ARCH_PPC64 #undef ARCH_DLINFO #undef ELF_PLATFORM diff --git a/linux-user/mmap.c b/linux-user/mmap.c index 7b44b9ff49..96c9433e27 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -26,6 +26,10 @@ #include "target_mman.h" #include "qemu/interval-tree.h" +#ifdef TARGET_ARM +#include "target/arm/cpu-features.h" +#endif + static pthread_mutex_t mmap_mutex = PTHREAD_MUTEX_INITIALIZER; static __thread int mmap_lock_count; diff --git a/target/arm/arch_dump.c b/target/arm/arch_dump.c index 2d8e41ab8a..06cdf4ba28 100644 --- a/target/arm/arch_dump.c +++ b/target/arm/arch_dump.c @@ -22,6 +22,7 @@ #include "cpu.h" #include "elf.h" #include "sysemu/dump.h" +#include "cpu-features.h" /* struct user_pt_regs from arch/arm64/include/uapi/asm/ptrace.h */ struct aarch64_user_regs { diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h new file mode 100644 index 0000000000..bfc9bfafe7 --- /dev/null +++ b/target/arm/cpu-features.h @@ -0,0 +1,994 @@ +/* + * QEMU Arm CPU -- feature test functions + * + * Copyright (c) 2023 Linaro Ltd + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef TARGET_ARM_FEATURES_H +#define TARGET_ARM_FEATURES_H + +/* + * Naming convention for isar_feature functions: + * Functions which test 32-bit ID registers should have _aa32_ in + * their name. Functions which test 64-bit ID registers should have + * _aa64_ in their name. These must only be used in code where we + * know for certain that the CPU has AArch32 or AArch64 respectively + * or where the correct answer for a CPU which doesn't implement that + * CPU state is "false" (eg when generating A32 or A64 code, if adding + * system registers that are specific to that CPU state, for "should + * we let this system register bit be set" tests where the 32-bit + * flavour of the register doesn't have the bit, and so on). + * Functions which simply ask "does this feature exist at all" have + * _any_ in their name, and always return the logical OR of the _aa64_ + * and the _aa32_ function. + */ + +/* + * 32-bit feature tests via id registers. + */ +static inline bool isar_feature_aa32_thumb_div(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar0, ID_ISAR0, DIVIDE) != 0; +} + +static inline bool isar_feature_aa32_arm_div(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar0, ID_ISAR0, DIVIDE) > 1; +} + +static inline bool isar_feature_aa32_lob(const ARMISARegisters *id) +{ + /* (M-profile) low-overhead loops and branch future */ + return FIELD_EX32(id->id_isar0, ID_ISAR0, CMPBRANCH) >= 3; +} + +static inline bool isar_feature_aa32_jazelle(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar1, ID_ISAR1, JAZELLE) != 0; +} + +static inline bool isar_feature_aa32_aes(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar5, ID_ISAR5, AES) != 0; +} + +static inline bool isar_feature_aa32_pmull(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar5, ID_ISAR5, AES) > 1; +} + +static inline bool isar_feature_aa32_sha1(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar5, ID_ISAR5, SHA1) != 0; +} + +static inline bool isar_feature_aa32_sha2(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar5, ID_ISAR5, SHA2) != 0; +} + +static inline bool isar_feature_aa32_crc32(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar5, ID_ISAR5, CRC32) != 0; +} + +static inline bool isar_feature_aa32_rdm(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar5, ID_ISAR5, RDM) != 0; +} + +static inline bool isar_feature_aa32_vcma(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar5, ID_ISAR5, VCMA) != 0; +} + +static inline bool isar_feature_aa32_jscvt(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar6, ID_ISAR6, JSCVT) != 0; +} + +static inline bool isar_feature_aa32_dp(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar6, ID_ISAR6, DP) != 0; +} + +static inline bool isar_feature_aa32_fhm(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar6, ID_ISAR6, FHM) != 0; +} + +static inline bool isar_feature_aa32_sb(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar6, ID_ISAR6, SB) != 0; +} + +static inline bool isar_feature_aa32_predinv(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar6, ID_ISAR6, SPECRES) != 0; +} + +static inline bool isar_feature_aa32_bf16(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar6, ID_ISAR6, BF16) != 0; +} + +static inline bool isar_feature_aa32_i8mm(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar6, ID_ISAR6, I8MM) != 0; +} + +static inline bool isar_feature_aa32_ras(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_pfr0, ID_PFR0, RAS) != 0; +} + +static inline bool isar_feature_aa32_mprofile(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_pfr1, ID_PFR1, MPROGMOD) != 0; +} + +static inline bool isar_feature_aa32_m_sec_state(const ARMISARegisters *id) +{ + /* + * Return true if M-profile state handling insns + * (VSCCLRM, CLRM, FPCTX access insns) are implemented + */ + return FIELD_EX32(id->id_pfr1, ID_PFR1, SECURITY) >= 3; +} + +static inline bool isar_feature_aa32_fp16_arith(const ARMISARegisters *id) +{ + /* Sadly this is encoded differently for A-profile and M-profile */ + if (isar_feature_aa32_mprofile(id)) { + return FIELD_EX32(id->mvfr1, MVFR1, FP16) > 0; + } else { + return FIELD_EX32(id->mvfr1, MVFR1, FPHP) >= 3; + } +} + +static inline bool isar_feature_aa32_mve(const ARMISARegisters *id) +{ + /* + * Return true if MVE is supported (either integer or floating point). + * We must check for M-profile as the MVFR1 field means something + * else for A-profile. + */ + return isar_feature_aa32_mprofile(id) && + FIELD_EX32(id->mvfr1, MVFR1, MVE) > 0; +} + +static inline bool isar_feature_aa32_mve_fp(const ARMISARegisters *id) +{ + /* + * Return true if MVE is supported (either integer or floating point). + * We must check for M-profile as the MVFR1 field means something + * else for A-profile. + */ + return isar_feature_aa32_mprofile(id) && + FIELD_EX32(id->mvfr1, MVFR1, MVE) >= 2; +} + +static inline bool isar_feature_aa32_vfp_simd(const ARMISARegisters *id) +{ + /* + * Return true if either VFP or SIMD is implemented. + * In this case, a minimum of VFP w/ D0-D15. + */ + return FIELD_EX32(id->mvfr0, MVFR0, SIMDREG) > 0; +} + +static inline bool isar_feature_aa32_simd_r32(const ARMISARegisters *id) +{ + /* Return true if D16-D31 are implemented */ + return FIELD_EX32(id->mvfr0, MVFR0, SIMDREG) >= 2; +} + +static inline bool isar_feature_aa32_fpshvec(const ARMISARegisters *id) +{ + return FIELD_EX32(id->mvfr0, MVFR0, FPSHVEC) > 0; +} + +static inline bool isar_feature_aa32_fpsp_v2(const ARMISARegisters *id) +{ + /* Return true if CPU supports single precision floating point, VFPv2 */ + return FIELD_EX32(id->mvfr0, MVFR0, FPSP) > 0; +} + +static inline bool isar_feature_aa32_fpsp_v3(const ARMISARegisters *id) +{ + /* Return true if CPU supports single precision floating point, VFPv3 */ + return FIELD_EX32(id->mvfr0, MVFR0, FPSP) >= 2; +} + +static inline bool isar_feature_aa32_fpdp_v2(const ARMISARegisters *id) +{ + /* Return true if CPU supports double precision floating point, VFPv2 */ + return FIELD_EX32(id->mvfr0, MVFR0, FPDP) > 0; +} + +static inline bool isar_feature_aa32_fpdp_v3(const ARMISARegisters *id) +{ + /* Return true if CPU supports double precision floating point, VFPv3 */ + return FIELD_EX32(id->mvfr0, MVFR0, FPDP) >= 2; +} + +static inline bool isar_feature_aa32_vfp(const ARMISARegisters *id) +{ + return isar_feature_aa32_fpsp_v2(id) || isar_feature_aa32_fpdp_v2(id); +} + +/* + * We always set the FP and SIMD FP16 fields to indicate identical + * levels of support (assuming SIMD is implemented at all), so + * we only need one set of accessors. + */ +static inline bool isar_feature_aa32_fp16_spconv(const ARMISARegisters *id) +{ + return FIELD_EX32(id->mvfr1, MVFR1, FPHP) > 0; +} + +static inline bool isar_feature_aa32_fp16_dpconv(const ARMISARegisters *id) +{ + return FIELD_EX32(id->mvfr1, MVFR1, FPHP) > 1; +} + +/* + * Note that this ID register field covers both VFP and Neon FMAC, + * so should usually be tested in combination with some other + * check that confirms the presence of whichever of VFP or Neon is + * relevant, to avoid accidentally enabling a Neon feature on + * a VFP-no-Neon core or vice-versa. + */ +static inline bool isar_feature_aa32_simdfmac(const ARMISARegisters *id) +{ + return FIELD_EX32(id->mvfr1, MVFR1, SIMDFMAC) != 0; +} + +static inline bool isar_feature_aa32_vsel(const ARMISARegisters *id) +{ + return FIELD_EX32(id->mvfr2, MVFR2, FPMISC) >= 1; +} + +static inline bool isar_feature_aa32_vcvt_dr(const ARMISARegisters *id) +{ + return FIELD_EX32(id->mvfr2, MVFR2, FPMISC) >= 2; +} + +static inline bool isar_feature_aa32_vrint(const ARMISARegisters *id) +{ + return FIELD_EX32(id->mvfr2, MVFR2, FPMISC) >= 3; +} + +static inline bool isar_feature_aa32_vminmaxnm(const ARMISARegisters *id) +{ + return FIELD_EX32(id->mvfr2, MVFR2, FPMISC) >= 4; +} + +static inline bool isar_feature_aa32_pxn(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_mmfr0, ID_MMFR0, VMSA) >= 4; +} + +static inline bool isar_feature_aa32_pan(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_mmfr3, ID_MMFR3, PAN) != 0; +} + +static inline bool isar_feature_aa32_ats1e1(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_mmfr3, ID_MMFR3, PAN) >= 2; +} + +static inline bool isar_feature_aa32_pmuv3p1(const ARMISARegisters *id) +{ + /* 0xf means "non-standard IMPDEF PMU" */ + return FIELD_EX32(id->id_dfr0, ID_DFR0, PERFMON) >= 4 && + FIELD_EX32(id->id_dfr0, ID_DFR0, PERFMON) != 0xf; +} + +static inline bool isar_feature_aa32_pmuv3p4(const ARMISARegisters *id) +{ + /* 0xf means "non-standard IMPDEF PMU" */ + return FIELD_EX32(id->id_dfr0, ID_DFR0, PERFMON) >= 5 && + FIELD_EX32(id->id_dfr0, ID_DFR0, PERFMON) != 0xf; +} + +static inline bool isar_feature_aa32_pmuv3p5(const ARMISARegisters *id) +{ + /* 0xf means "non-standard IMPDEF PMU" */ + return FIELD_EX32(id->id_dfr0, ID_DFR0, PERFMON) >= 6 && + FIELD_EX32(id->id_dfr0, ID_DFR0, PERFMON) != 0xf; +} + +static inline bool isar_feature_aa32_hpd(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_mmfr4, ID_MMFR4, HPDS) != 0; +} + +static inline bool isar_feature_aa32_ac2(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_mmfr4, ID_MMFR4, AC2) != 0; +} + +static inline bool isar_feature_aa32_ccidx(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_mmfr4, ID_MMFR4, CCIDX) != 0; +} + +static inline bool isar_feature_aa32_tts2uxn(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_mmfr4, ID_MMFR4, XNX) != 0; +} + +static inline bool isar_feature_aa32_half_evt(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_mmfr4, ID_MMFR4, EVT) >= 1; +} + +static inline bool isar_feature_aa32_evt(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_mmfr4, ID_MMFR4, EVT) >= 2; +} + +static inline bool isar_feature_aa32_dit(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_pfr0, ID_PFR0, DIT) != 0; +} + +static inline bool isar_feature_aa32_ssbs(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_pfr2, ID_PFR2, SSBS) != 0; +} + +static inline bool isar_feature_aa32_debugv7p1(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_dfr0, ID_DFR0, COPDBG) >= 5; +} + +static inline bool isar_feature_aa32_debugv8p2(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_dfr0, ID_DFR0, COPDBG) >= 8; +} + +static inline bool isar_feature_aa32_doublelock(const ARMISARegisters *id) +{ + return FIELD_EX32(id->dbgdevid, DBGDEVID, DOUBLELOCK) > 0; +} + +/* + * 64-bit feature tests via id registers. + */ +static inline bool isar_feature_aa64_aes(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, AES) != 0; +} + +static inline bool isar_feature_aa64_pmull(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, AES) > 1; +} + +static inline bool isar_feature_aa64_sha1(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SHA1) != 0; +} + +static inline bool isar_feature_aa64_sha256(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SHA2) != 0; +} + +static inline bool isar_feature_aa64_sha512(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SHA2) > 1; +} + +static inline bool isar_feature_aa64_crc32(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, CRC32) != 0; +} + +static inline bool isar_feature_aa64_atomics(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, ATOMIC) != 0; +} + +static inline bool isar_feature_aa64_rdm(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, RDM) != 0; +} + +static inline bool isar_feature_aa64_sha3(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SHA3) != 0; +} + +static inline bool isar_feature_aa64_sm3(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SM3) != 0; +} + +static inline bool isar_feature_aa64_sm4(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SM4) != 0; +} + +static inline bool isar_feature_aa64_dp(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, DP) != 0; +} + +static inline bool isar_feature_aa64_fhm(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, FHM) != 0; +} + +static inline bool isar_feature_aa64_condm_4(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, TS) != 0; +} + +static inline bool isar_feature_aa64_condm_5(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, TS) >= 2; +} + +static inline bool isar_feature_aa64_rndr(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, RNDR) != 0; +} + +static inline bool isar_feature_aa64_jscvt(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, JSCVT) != 0; +} + +static inline bool isar_feature_aa64_fcma(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, FCMA) != 0; +} + +/* + * These are the values from APA/API/APA3. + * In general these must be compared '>=', per the normal Arm ARM + * treatment of fields in ID registers. + */ +typedef enum { + PauthFeat_None = 0, + PauthFeat_1 = 1, + PauthFeat_EPAC = 2, + PauthFeat_2 = 3, + PauthFeat_FPAC = 4, + PauthFeat_FPACCOMBINED = 5, +} ARMPauthFeature; + +static inline ARMPauthFeature +isar_feature_pauth_feature(const ARMISARegisters *id) +{ + /* + * Architecturally, only one of {APA,API,APA3} may be active (non-zero) + * and the other two must be zero. Thus we may avoid conditionals. + */ + return (FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, APA) | + FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, API) | + FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, APA3)); +} + +static inline bool isar_feature_aa64_pauth(const ARMISARegisters *id) +{ + /* + * Return true if any form of pauth is enabled, as this + * predicate controls migration of the 128-bit keys. + */ + return isar_feature_pauth_feature(id) != PauthFeat_None; +} + +static inline bool isar_feature_aa64_pauth_qarma5(const ARMISARegisters *id) +{ + /* + * Return true if pauth is enabled with the architected QARMA5 algorithm. + * QEMU will always enable or disable both APA and GPA. + */ + return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, APA) != 0; +} + +static inline bool isar_feature_aa64_pauth_qarma3(const ARMISARegisters *id) +{ + /* + * Return true if pauth is enabled with the architected QARMA3 algorithm. + * QEMU will always enable or disable both APA3 and GPA3. + */ + return FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, APA3) != 0; +} + +static inline bool isar_feature_aa64_tlbirange(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, TLB) == 2; +} + +static inline bool isar_feature_aa64_tlbios(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, TLB) != 0; +} + +static inline bool isar_feature_aa64_sb(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, SB) != 0; +} + +static inline bool isar_feature_aa64_predinv(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, SPECRES) != 0; +} + +static inline bool isar_feature_aa64_frint(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, FRINTTS) != 0; +} + +static inline bool isar_feature_aa64_dcpop(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, DPB) != 0; +} + +static inline bool isar_feature_aa64_dcpodp(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, DPB) >= 2; +} + +static inline bool isar_feature_aa64_bf16(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, BF16) != 0; +} + +static inline bool isar_feature_aa64_fp_simd(const ARMISARegisters *id) +{ + /* We always set the AdvSIMD and FP fields identically. */ + return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, FP) != 0xf; +} + +static inline bool isar_feature_aa64_fp16(const ARMISARegisters *id) +{ + /* We always set the AdvSIMD and FP fields identically wrt FP16. */ + return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, FP) == 1; +} + +static inline bool isar_feature_aa64_aa32(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, EL0) >= 2; +} + +static inline bool isar_feature_aa64_aa32_el1(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, EL1) >= 2; +} + +static inline bool isar_feature_aa64_aa32_el2(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, EL2) >= 2; +} + +static inline bool isar_feature_aa64_ras(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, RAS) != 0; +} + +static inline bool isar_feature_aa64_doublefault(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, RAS) >= 2; +} + +static inline bool isar_feature_aa64_sve(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, SVE) != 0; +} + +static inline bool isar_feature_aa64_sel2(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, SEL2) != 0; +} + +static inline bool isar_feature_aa64_rme(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, RME) != 0; +} + +static inline bool isar_feature_aa64_vh(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, VH) != 0; +} + +static inline bool isar_feature_aa64_lor(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, LO) != 0; +} + +static inline bool isar_feature_aa64_pan(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, PAN) != 0; +} + +static inline bool isar_feature_aa64_ats1e1(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, PAN) >= 2; +} + +static inline bool isar_feature_aa64_pan3(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, PAN) >= 3; +} + +static inline bool isar_feature_aa64_hcx(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, HCX) != 0; +} + +static inline bool isar_feature_aa64_tidcp1(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR1, TIDCP1) != 0; +} + +static inline bool isar_feature_aa64_uao(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, UAO) != 0; +} + +static inline bool isar_feature_aa64_st(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, ST) != 0; +} + +static inline bool isar_feature_aa64_lse2(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, AT) != 0; +} + +static inline bool isar_feature_aa64_fwb(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, FWB) != 0; +} + +static inline bool isar_feature_aa64_ids(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, IDS) != 0; +} + +static inline bool isar_feature_aa64_half_evt(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, EVT) >= 1; +} + +static inline bool isar_feature_aa64_evt(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, EVT) >= 2; +} + +static inline bool isar_feature_aa64_bti(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, BT) != 0; +} + +static inline bool isar_feature_aa64_mte_insn_reg(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, MTE) != 0; +} + +static inline bool isar_feature_aa64_mte(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, MTE) >= 2; +} + +static inline bool isar_feature_aa64_sme(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, SME) != 0; +} + +static inline bool isar_feature_aa64_pmuv3p1(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) >= 4 && + FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) != 0xf; +} + +static inline bool isar_feature_aa64_pmuv3p4(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) >= 5 && + FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) != 0xf; +} + +static inline bool isar_feature_aa64_pmuv3p5(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) >= 6 && + FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) != 0xf; +} + +static inline bool isar_feature_aa64_rcpc_8_3(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, LRCPC) != 0; +} + +static inline bool isar_feature_aa64_rcpc_8_4(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, LRCPC) >= 2; +} + +static inline bool isar_feature_aa64_i8mm(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, I8MM) != 0; +} + +static inline bool isar_feature_aa64_hbc(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, BC) != 0; +} + +static inline bool isar_feature_aa64_tgran4_lpa2(const ARMISARegisters *id) +{ + return FIELD_SEX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN4) >= 1; +} + +static inline bool isar_feature_aa64_tgran4_2_lpa2(const ARMISARegisters *id) +{ + unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN4_2); + return t >= 3 || (t == 0 && isar_feature_aa64_tgran4_lpa2(id)); +} + +static inline bool isar_feature_aa64_tgran16_lpa2(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN16) >= 2; +} + +static inline bool isar_feature_aa64_tgran16_2_lpa2(const ARMISARegisters *id) +{ + unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN16_2); + return t >= 3 || (t == 0 && isar_feature_aa64_tgran16_lpa2(id)); +} + +static inline bool isar_feature_aa64_tgran4(const ARMISARegisters *id) +{ + return FIELD_SEX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN4) >= 0; +} + +static inline bool isar_feature_aa64_tgran16(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN16) >= 1; +} + +static inline bool isar_feature_aa64_tgran64(const ARMISARegisters *id) +{ + return FIELD_SEX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN64) >= 0; +} + +static inline bool isar_feature_aa64_tgran4_2(const ARMISARegisters *id) +{ + unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN4_2); + return t >= 2 || (t == 0 && isar_feature_aa64_tgran4(id)); +} + +static inline bool isar_feature_aa64_tgran16_2(const ARMISARegisters *id) +{ + unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN16_2); + return t >= 2 || (t == 0 && isar_feature_aa64_tgran16(id)); +} + +static inline bool isar_feature_aa64_tgran64_2(const ARMISARegisters *id) +{ + unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN64_2); + return t >= 2 || (t == 0 && isar_feature_aa64_tgran64(id)); +} + +static inline bool isar_feature_aa64_fgt(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, FGT) != 0; +} + +static inline bool isar_feature_aa64_ccidx(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, CCIDX) != 0; +} + +static inline bool isar_feature_aa64_lva(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, VARANGE) != 0; +} + +static inline bool isar_feature_aa64_e0pd(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, E0PD) != 0; +} + +static inline bool isar_feature_aa64_hafs(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, HAFDBS) != 0; +} + +static inline bool isar_feature_aa64_hdbs(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, HAFDBS) >= 2; +} + +static inline bool isar_feature_aa64_tts2uxn(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, XNX) != 0; +} + +static inline bool isar_feature_aa64_dit(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, DIT) != 0; +} + +static inline bool isar_feature_aa64_scxtnum(const ARMISARegisters *id) +{ + int key = FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, CSV2); + if (key >= 2) { + return true; /* FEAT_CSV2_2 */ + } + if (key == 1) { + key = FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, CSV2_FRAC); + return key >= 2; /* FEAT_CSV2_1p2 */ + } + return false; +} + +static inline bool isar_feature_aa64_ssbs(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, SSBS) != 0; +} + +static inline bool isar_feature_aa64_debugv8p2(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, DEBUGVER) >= 8; +} + +static inline bool isar_feature_aa64_sve2(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, SVEVER) != 0; +} + +static inline bool isar_feature_aa64_sve2_aes(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, AES) != 0; +} + +static inline bool isar_feature_aa64_sve2_pmull128(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, AES) >= 2; +} + +static inline bool isar_feature_aa64_sve2_bitperm(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, BITPERM) != 0; +} + +static inline bool isar_feature_aa64_sve_bf16(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, BFLOAT16) != 0; +} + +static inline bool isar_feature_aa64_sve2_sha3(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, SHA3) != 0; +} + +static inline bool isar_feature_aa64_sve2_sm4(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, SM4) != 0; +} + +static inline bool isar_feature_aa64_sve_i8mm(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, I8MM) != 0; +} + +static inline bool isar_feature_aa64_sve_f32mm(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, F32MM) != 0; +} + +static inline bool isar_feature_aa64_sve_f64mm(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, F64MM) != 0; +} + +static inline bool isar_feature_aa64_sme_f64f64(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64smfr0, ID_AA64SMFR0, F64F64); +} + +static inline bool isar_feature_aa64_sme_i16i64(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64smfr0, ID_AA64SMFR0, I16I64) == 0xf; +} + +static inline bool isar_feature_aa64_sme_fa64(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64smfr0, ID_AA64SMFR0, FA64); +} + +static inline bool isar_feature_aa64_doublelock(const ARMISARegisters *id) +{ + return FIELD_SEX64(id->id_aa64dfr0, ID_AA64DFR0, DOUBLELOCK) >= 0; +} + +static inline bool isar_feature_aa64_mops(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, MOPS); +} + +/* + * Feature tests for "does this exist in either 32-bit or 64-bit?" + */ +static inline bool isar_feature_any_fp16(const ARMISARegisters *id) +{ + return isar_feature_aa64_fp16(id) || isar_feature_aa32_fp16_arith(id); +} + +static inline bool isar_feature_any_predinv(const ARMISARegisters *id) +{ + return isar_feature_aa64_predinv(id) || isar_feature_aa32_predinv(id); +} + +static inline bool isar_feature_any_pmuv3p1(const ARMISARegisters *id) +{ + return isar_feature_aa64_pmuv3p1(id) || isar_feature_aa32_pmuv3p1(id); +} + +static inline bool isar_feature_any_pmuv3p4(const ARMISARegisters *id) +{ + return isar_feature_aa64_pmuv3p4(id) || isar_feature_aa32_pmuv3p4(id); +} + +static inline bool isar_feature_any_pmuv3p5(const ARMISARegisters *id) +{ + return isar_feature_aa64_pmuv3p5(id) || isar_feature_aa32_pmuv3p5(id); +} + +static inline bool isar_feature_any_ccidx(const ARMISARegisters *id) +{ + return isar_feature_aa64_ccidx(id) || isar_feature_aa32_ccidx(id); +} + +static inline bool isar_feature_any_tts2uxn(const ARMISARegisters *id) +{ + return isar_feature_aa64_tts2uxn(id) || isar_feature_aa32_tts2uxn(id); +} + +static inline bool isar_feature_any_debugv8p2(const ARMISARegisters *id) +{ + return isar_feature_aa64_debugv8p2(id) || isar_feature_aa32_debugv8p2(id); +} + +static inline bool isar_feature_any_ras(const ARMISARegisters *id) +{ + return isar_feature_aa64_ras(id) || isar_feature_aa32_ras(id); +} + +static inline bool isar_feature_any_half_evt(const ARMISARegisters *id) +{ + return isar_feature_aa64_half_evt(id) || isar_feature_aa32_half_evt(id); +} + +static inline bool isar_feature_any_evt(const ARMISARegisters *id) +{ + return isar_feature_aa64_evt(id) || isar_feature_aa32_evt(id); +} + +/* + * Forward to the above feature tests given an ARMCPU pointer. + */ +#define cpu_isar_feature(name, cpu) \ + ({ ARMCPU *cpu_ = (cpu); isar_feature_##name(&cpu_->isar); }) + +#endif diff --git a/target/arm/cpu.c b/target/arm/cpu.c index aa4e006f21..954328d72a 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -31,6 +31,7 @@ #include "hw/core/tcg-cpu-ops.h" #endif /* CONFIG_TCG */ #include "internals.h" +#include "cpu-features.h" #include "exec/exec-all.h" #include "hw/qdev-properties.h" #if !defined(CONFIG_USER_ONLY) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 76d4cef9e3..d51dfe48db 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -3402,975 +3402,4 @@ static inline target_ulong cpu_untagged_addr(CPUState *cs, target_ulong x) } #endif -/* - * Naming convention for isar_feature functions: - * Functions which test 32-bit ID registers should have _aa32_ in - * their name. Functions which test 64-bit ID registers should have - * _aa64_ in their name. These must only be used in code where we - * know for certain that the CPU has AArch32 or AArch64 respectively - * or where the correct answer for a CPU which doesn't implement that - * CPU state is "false" (eg when generating A32 or A64 code, if adding - * system registers that are specific to that CPU state, for "should - * we let this system register bit be set" tests where the 32-bit - * flavour of the register doesn't have the bit, and so on). - * Functions which simply ask "does this feature exist at all" have - * _any_ in their name, and always return the logical OR of the _aa64_ - * and the _aa32_ function. - */ - -/* - * 32-bit feature tests via id registers. - */ -static inline bool isar_feature_aa32_thumb_div(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_isar0, ID_ISAR0, DIVIDE) != 0; -} - -static inline bool isar_feature_aa32_arm_div(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_isar0, ID_ISAR0, DIVIDE) > 1; -} - -static inline bool isar_feature_aa32_lob(const ARMISARegisters *id) -{ - /* (M-profile) low-overhead loops and branch future */ - return FIELD_EX32(id->id_isar0, ID_ISAR0, CMPBRANCH) >= 3; -} - -static inline bool isar_feature_aa32_jazelle(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_isar1, ID_ISAR1, JAZELLE) != 0; -} - -static inline bool isar_feature_aa32_aes(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_isar5, ID_ISAR5, AES) != 0; -} - -static inline bool isar_feature_aa32_pmull(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_isar5, ID_ISAR5, AES) > 1; -} - -static inline bool isar_feature_aa32_sha1(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_isar5, ID_ISAR5, SHA1) != 0; -} - -static inline bool isar_feature_aa32_sha2(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_isar5, ID_ISAR5, SHA2) != 0; -} - -static inline bool isar_feature_aa32_crc32(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_isar5, ID_ISAR5, CRC32) != 0; -} - -static inline bool isar_feature_aa32_rdm(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_isar5, ID_ISAR5, RDM) != 0; -} - -static inline bool isar_feature_aa32_vcma(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_isar5, ID_ISAR5, VCMA) != 0; -} - -static inline bool isar_feature_aa32_jscvt(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_isar6, ID_ISAR6, JSCVT) != 0; -} - -static inline bool isar_feature_aa32_dp(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_isar6, ID_ISAR6, DP) != 0; -} - -static inline bool isar_feature_aa32_fhm(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_isar6, ID_ISAR6, FHM) != 0; -} - -static inline bool isar_feature_aa32_sb(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_isar6, ID_ISAR6, SB) != 0; -} - -static inline bool isar_feature_aa32_predinv(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_isar6, ID_ISAR6, SPECRES) != 0; -} - -static inline bool isar_feature_aa32_bf16(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_isar6, ID_ISAR6, BF16) != 0; -} - -static inline bool isar_feature_aa32_i8mm(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_isar6, ID_ISAR6, I8MM) != 0; -} - -static inline bool isar_feature_aa32_ras(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_pfr0, ID_PFR0, RAS) != 0; -} - -static inline bool isar_feature_aa32_mprofile(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_pfr1, ID_PFR1, MPROGMOD) != 0; -} - -static inline bool isar_feature_aa32_m_sec_state(const ARMISARegisters *id) -{ - /* - * Return true if M-profile state handling insns - * (VSCCLRM, CLRM, FPCTX access insns) are implemented - */ - return FIELD_EX32(id->id_pfr1, ID_PFR1, SECURITY) >= 3; -} - -static inline bool isar_feature_aa32_fp16_arith(const ARMISARegisters *id) -{ - /* Sadly this is encoded differently for A-profile and M-profile */ - if (isar_feature_aa32_mprofile(id)) { - return FIELD_EX32(id->mvfr1, MVFR1, FP16) > 0; - } else { - return FIELD_EX32(id->mvfr1, MVFR1, FPHP) >= 3; - } -} - -static inline bool isar_feature_aa32_mve(const ARMISARegisters *id) -{ - /* - * Return true if MVE is supported (either integer or floating point). - * We must check for M-profile as the MVFR1 field means something - * else for A-profile. - */ - return isar_feature_aa32_mprofile(id) && - FIELD_EX32(id->mvfr1, MVFR1, MVE) > 0; -} - -static inline bool isar_feature_aa32_mve_fp(const ARMISARegisters *id) -{ - /* - * Return true if MVE is supported (either integer or floating point). - * We must check for M-profile as the MVFR1 field means something - * else for A-profile. - */ - return isar_feature_aa32_mprofile(id) && - FIELD_EX32(id->mvfr1, MVFR1, MVE) >= 2; -} - -static inline bool isar_feature_aa32_vfp_simd(const ARMISARegisters *id) -{ - /* - * Return true if either VFP or SIMD is implemented. - * In this case, a minimum of VFP w/ D0-D15. - */ - return FIELD_EX32(id->mvfr0, MVFR0, SIMDREG) > 0; -} - -static inline bool isar_feature_aa32_simd_r32(const ARMISARegisters *id) -{ - /* Return true if D16-D31 are implemented */ - return FIELD_EX32(id->mvfr0, MVFR0, SIMDREG) >= 2; -} - -static inline bool isar_feature_aa32_fpshvec(const ARMISARegisters *id) -{ - return FIELD_EX32(id->mvfr0, MVFR0, FPSHVEC) > 0; -} - -static inline bool isar_feature_aa32_fpsp_v2(const ARMISARegisters *id) -{ - /* Return true if CPU supports single precision floating point, VFPv2 */ - return FIELD_EX32(id->mvfr0, MVFR0, FPSP) > 0; -} - -static inline bool isar_feature_aa32_fpsp_v3(const ARMISARegisters *id) -{ - /* Return true if CPU supports single precision floating point, VFPv3 */ - return FIELD_EX32(id->mvfr0, MVFR0, FPSP) >= 2; -} - -static inline bool isar_feature_aa32_fpdp_v2(const ARMISARegisters *id) -{ - /* Return true if CPU supports double precision floating point, VFPv2 */ - return FIELD_EX32(id->mvfr0, MVFR0, FPDP) > 0; -} - -static inline bool isar_feature_aa32_fpdp_v3(const ARMISARegisters *id) -{ - /* Return true if CPU supports double precision floating point, VFPv3 */ - return FIELD_EX32(id->mvfr0, MVFR0, FPDP) >= 2; -} - -static inline bool isar_feature_aa32_vfp(const ARMISARegisters *id) -{ - return isar_feature_aa32_fpsp_v2(id) || isar_feature_aa32_fpdp_v2(id); -} - -/* - * We always set the FP and SIMD FP16 fields to indicate identical - * levels of support (assuming SIMD is implemented at all), so - * we only need one set of accessors. - */ -static inline bool isar_feature_aa32_fp16_spconv(const ARMISARegisters *id) -{ - return FIELD_EX32(id->mvfr1, MVFR1, FPHP) > 0; -} - -static inline bool isar_feature_aa32_fp16_dpconv(const ARMISARegisters *id) -{ - return FIELD_EX32(id->mvfr1, MVFR1, FPHP) > 1; -} - -/* - * Note that this ID register field covers both VFP and Neon FMAC, - * so should usually be tested in combination with some other - * check that confirms the presence of whichever of VFP or Neon is - * relevant, to avoid accidentally enabling a Neon feature on - * a VFP-no-Neon core or vice-versa. - */ -static inline bool isar_feature_aa32_simdfmac(const ARMISARegisters *id) -{ - return FIELD_EX32(id->mvfr1, MVFR1, SIMDFMAC) != 0; -} - -static inline bool isar_feature_aa32_vsel(const ARMISARegisters *id) -{ - return FIELD_EX32(id->mvfr2, MVFR2, FPMISC) >= 1; -} - -static inline bool isar_feature_aa32_vcvt_dr(const ARMISARegisters *id) -{ - return FIELD_EX32(id->mvfr2, MVFR2, FPMISC) >= 2; -} - -static inline bool isar_feature_aa32_vrint(const ARMISARegisters *id) -{ - return FIELD_EX32(id->mvfr2, MVFR2, FPMISC) >= 3; -} - -static inline bool isar_feature_aa32_vminmaxnm(const ARMISARegisters *id) -{ - return FIELD_EX32(id->mvfr2, MVFR2, FPMISC) >= 4; -} - -static inline bool isar_feature_aa32_pxn(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_mmfr0, ID_MMFR0, VMSA) >= 4; -} - -static inline bool isar_feature_aa32_pan(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_mmfr3, ID_MMFR3, PAN) != 0; -} - -static inline bool isar_feature_aa32_ats1e1(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_mmfr3, ID_MMFR3, PAN) >= 2; -} - -static inline bool isar_feature_aa32_pmuv3p1(const ARMISARegisters *id) -{ - /* 0xf means "non-standard IMPDEF PMU" */ - return FIELD_EX32(id->id_dfr0, ID_DFR0, PERFMON) >= 4 && - FIELD_EX32(id->id_dfr0, ID_DFR0, PERFMON) != 0xf; -} - -static inline bool isar_feature_aa32_pmuv3p4(const ARMISARegisters *id) -{ - /* 0xf means "non-standard IMPDEF PMU" */ - return FIELD_EX32(id->id_dfr0, ID_DFR0, PERFMON) >= 5 && - FIELD_EX32(id->id_dfr0, ID_DFR0, PERFMON) != 0xf; -} - -static inline bool isar_feature_aa32_pmuv3p5(const ARMISARegisters *id) -{ - /* 0xf means "non-standard IMPDEF PMU" */ - return FIELD_EX32(id->id_dfr0, ID_DFR0, PERFMON) >= 6 && - FIELD_EX32(id->id_dfr0, ID_DFR0, PERFMON) != 0xf; -} - -static inline bool isar_feature_aa32_hpd(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_mmfr4, ID_MMFR4, HPDS) != 0; -} - -static inline bool isar_feature_aa32_ac2(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_mmfr4, ID_MMFR4, AC2) != 0; -} - -static inline bool isar_feature_aa32_ccidx(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_mmfr4, ID_MMFR4, CCIDX) != 0; -} - -static inline bool isar_feature_aa32_tts2uxn(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_mmfr4, ID_MMFR4, XNX) != 0; -} - -static inline bool isar_feature_aa32_half_evt(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_mmfr4, ID_MMFR4, EVT) >= 1; -} - -static inline bool isar_feature_aa32_evt(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_mmfr4, ID_MMFR4, EVT) >= 2; -} - -static inline bool isar_feature_aa32_dit(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_pfr0, ID_PFR0, DIT) != 0; -} - -static inline bool isar_feature_aa32_ssbs(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_pfr2, ID_PFR2, SSBS) != 0; -} - -static inline bool isar_feature_aa32_debugv7p1(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_dfr0, ID_DFR0, COPDBG) >= 5; -} - -static inline bool isar_feature_aa32_debugv8p2(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_dfr0, ID_DFR0, COPDBG) >= 8; -} - -static inline bool isar_feature_aa32_doublelock(const ARMISARegisters *id) -{ - return FIELD_EX32(id->dbgdevid, DBGDEVID, DOUBLELOCK) > 0; -} - -/* - * 64-bit feature tests via id registers. - */ -static inline bool isar_feature_aa64_aes(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, AES) != 0; -} - -static inline bool isar_feature_aa64_pmull(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, AES) > 1; -} - -static inline bool isar_feature_aa64_sha1(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SHA1) != 0; -} - -static inline bool isar_feature_aa64_sha256(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SHA2) != 0; -} - -static inline bool isar_feature_aa64_sha512(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SHA2) > 1; -} - -static inline bool isar_feature_aa64_crc32(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, CRC32) != 0; -} - -static inline bool isar_feature_aa64_atomics(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, ATOMIC) != 0; -} - -static inline bool isar_feature_aa64_rdm(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, RDM) != 0; -} - -static inline bool isar_feature_aa64_sha3(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SHA3) != 0; -} - -static inline bool isar_feature_aa64_sm3(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SM3) != 0; -} - -static inline bool isar_feature_aa64_sm4(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SM4) != 0; -} - -static inline bool isar_feature_aa64_dp(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, DP) != 0; -} - -static inline bool isar_feature_aa64_fhm(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, FHM) != 0; -} - -static inline bool isar_feature_aa64_condm_4(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, TS) != 0; -} - -static inline bool isar_feature_aa64_condm_5(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, TS) >= 2; -} - -static inline bool isar_feature_aa64_rndr(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, RNDR) != 0; -} - -static inline bool isar_feature_aa64_jscvt(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, JSCVT) != 0; -} - -static inline bool isar_feature_aa64_fcma(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, FCMA) != 0; -} - -/* - * These are the values from APA/API/APA3. - * In general these must be compared '>=', per the normal Arm ARM - * treatment of fields in ID registers. - */ -typedef enum { - PauthFeat_None = 0, - PauthFeat_1 = 1, - PauthFeat_EPAC = 2, - PauthFeat_2 = 3, - PauthFeat_FPAC = 4, - PauthFeat_FPACCOMBINED = 5, -} ARMPauthFeature; - -static inline ARMPauthFeature -isar_feature_pauth_feature(const ARMISARegisters *id) -{ - /* - * Architecturally, only one of {APA,API,APA3} may be active (non-zero) - * and the other two must be zero. Thus we may avoid conditionals. - */ - return (FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, APA) | - FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, API) | - FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, APA3)); -} - -static inline bool isar_feature_aa64_pauth(const ARMISARegisters *id) -{ - /* - * Return true if any form of pauth is enabled, as this - * predicate controls migration of the 128-bit keys. - */ - return isar_feature_pauth_feature(id) != PauthFeat_None; -} - -static inline bool isar_feature_aa64_pauth_qarma5(const ARMISARegisters *id) -{ - /* - * Return true if pauth is enabled with the architected QARMA5 algorithm. - * QEMU will always enable or disable both APA and GPA. - */ - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, APA) != 0; -} - -static inline bool isar_feature_aa64_pauth_qarma3(const ARMISARegisters *id) -{ - /* - * Return true if pauth is enabled with the architected QARMA3 algorithm. - * QEMU will always enable or disable both APA3 and GPA3. - */ - return FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, APA3) != 0; -} - -static inline bool isar_feature_aa64_tlbirange(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, TLB) == 2; -} - -static inline bool isar_feature_aa64_tlbios(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, TLB) != 0; -} - -static inline bool isar_feature_aa64_sb(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, SB) != 0; -} - -static inline bool isar_feature_aa64_predinv(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, SPECRES) != 0; -} - -static inline bool isar_feature_aa64_frint(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, FRINTTS) != 0; -} - -static inline bool isar_feature_aa64_dcpop(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, DPB) != 0; -} - -static inline bool isar_feature_aa64_dcpodp(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, DPB) >= 2; -} - -static inline bool isar_feature_aa64_bf16(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, BF16) != 0; -} - -static inline bool isar_feature_aa64_fp_simd(const ARMISARegisters *id) -{ - /* We always set the AdvSIMD and FP fields identically. */ - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, FP) != 0xf; -} - -static inline bool isar_feature_aa64_fp16(const ARMISARegisters *id) -{ - /* We always set the AdvSIMD and FP fields identically wrt FP16. */ - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, FP) == 1; -} - -static inline bool isar_feature_aa64_aa32(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, EL0) >= 2; -} - -static inline bool isar_feature_aa64_aa32_el1(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, EL1) >= 2; -} - -static inline bool isar_feature_aa64_aa32_el2(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, EL2) >= 2; -} - -static inline bool isar_feature_aa64_ras(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, RAS) != 0; -} - -static inline bool isar_feature_aa64_doublefault(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, RAS) >= 2; -} - -static inline bool isar_feature_aa64_sve(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, SVE) != 0; -} - -static inline bool isar_feature_aa64_sel2(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, SEL2) != 0; -} - -static inline bool isar_feature_aa64_rme(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, RME) != 0; -} - -static inline bool isar_feature_aa64_vh(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, VH) != 0; -} - -static inline bool isar_feature_aa64_lor(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, LO) != 0; -} - -static inline bool isar_feature_aa64_pan(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, PAN) != 0; -} - -static inline bool isar_feature_aa64_ats1e1(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, PAN) >= 2; -} - -static inline bool isar_feature_aa64_pan3(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, PAN) >= 3; -} - -static inline bool isar_feature_aa64_hcx(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, HCX) != 0; -} - -static inline bool isar_feature_aa64_tidcp1(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR1, TIDCP1) != 0; -} - -static inline bool isar_feature_aa64_uao(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, UAO) != 0; -} - -static inline bool isar_feature_aa64_st(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, ST) != 0; -} - -static inline bool isar_feature_aa64_lse2(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, AT) != 0; -} - -static inline bool isar_feature_aa64_fwb(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, FWB) != 0; -} - -static inline bool isar_feature_aa64_ids(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, IDS) != 0; -} - -static inline bool isar_feature_aa64_half_evt(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, EVT) >= 1; -} - -static inline bool isar_feature_aa64_evt(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, EVT) >= 2; -} - -static inline bool isar_feature_aa64_bti(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, BT) != 0; -} - -static inline bool isar_feature_aa64_mte_insn_reg(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, MTE) != 0; -} - -static inline bool isar_feature_aa64_mte(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, MTE) >= 2; -} - -static inline bool isar_feature_aa64_sme(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, SME) != 0; -} - -static inline bool isar_feature_aa64_pmuv3p1(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) >= 4 && - FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) != 0xf; -} - -static inline bool isar_feature_aa64_pmuv3p4(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) >= 5 && - FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) != 0xf; -} - -static inline bool isar_feature_aa64_pmuv3p5(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) >= 6 && - FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) != 0xf; -} - -static inline bool isar_feature_aa64_rcpc_8_3(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, LRCPC) != 0; -} - -static inline bool isar_feature_aa64_rcpc_8_4(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, LRCPC) >= 2; -} - -static inline bool isar_feature_aa64_i8mm(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, I8MM) != 0; -} - -static inline bool isar_feature_aa64_hbc(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, BC) != 0; -} - -static inline bool isar_feature_aa64_tgran4_lpa2(const ARMISARegisters *id) -{ - return FIELD_SEX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN4) >= 1; -} - -static inline bool isar_feature_aa64_tgran4_2_lpa2(const ARMISARegisters *id) -{ - unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN4_2); - return t >= 3 || (t == 0 && isar_feature_aa64_tgran4_lpa2(id)); -} - -static inline bool isar_feature_aa64_tgran16_lpa2(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN16) >= 2; -} - -static inline bool isar_feature_aa64_tgran16_2_lpa2(const ARMISARegisters *id) -{ - unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN16_2); - return t >= 3 || (t == 0 && isar_feature_aa64_tgran16_lpa2(id)); -} - -static inline bool isar_feature_aa64_tgran4(const ARMISARegisters *id) -{ - return FIELD_SEX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN4) >= 0; -} - -static inline bool isar_feature_aa64_tgran16(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN16) >= 1; -} - -static inline bool isar_feature_aa64_tgran64(const ARMISARegisters *id) -{ - return FIELD_SEX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN64) >= 0; -} - -static inline bool isar_feature_aa64_tgran4_2(const ARMISARegisters *id) -{ - unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN4_2); - return t >= 2 || (t == 0 && isar_feature_aa64_tgran4(id)); -} - -static inline bool isar_feature_aa64_tgran16_2(const ARMISARegisters *id) -{ - unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN16_2); - return t >= 2 || (t == 0 && isar_feature_aa64_tgran16(id)); -} - -static inline bool isar_feature_aa64_tgran64_2(const ARMISARegisters *id) -{ - unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN64_2); - return t >= 2 || (t == 0 && isar_feature_aa64_tgran64(id)); -} - -static inline bool isar_feature_aa64_fgt(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, FGT) != 0; -} - -static inline bool isar_feature_aa64_ccidx(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, CCIDX) != 0; -} - -static inline bool isar_feature_aa64_lva(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, VARANGE) != 0; -} - -static inline bool isar_feature_aa64_e0pd(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, E0PD) != 0; -} - -static inline bool isar_feature_aa64_hafs(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, HAFDBS) != 0; -} - -static inline bool isar_feature_aa64_hdbs(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, HAFDBS) >= 2; -} - -static inline bool isar_feature_aa64_tts2uxn(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, XNX) != 0; -} - -static inline bool isar_feature_aa64_dit(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, DIT) != 0; -} - -static inline bool isar_feature_aa64_scxtnum(const ARMISARegisters *id) -{ - int key = FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, CSV2); - if (key >= 2) { - return true; /* FEAT_CSV2_2 */ - } - if (key == 1) { - key = FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, CSV2_FRAC); - return key >= 2; /* FEAT_CSV2_1p2 */ - } - return false; -} - -static inline bool isar_feature_aa64_ssbs(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, SSBS) != 0; -} - -static inline bool isar_feature_aa64_debugv8p2(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, DEBUGVER) >= 8; -} - -static inline bool isar_feature_aa64_sve2(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, SVEVER) != 0; -} - -static inline bool isar_feature_aa64_sve2_aes(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, AES) != 0; -} - -static inline bool isar_feature_aa64_sve2_pmull128(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, AES) >= 2; -} - -static inline bool isar_feature_aa64_sve2_bitperm(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, BITPERM) != 0; -} - -static inline bool isar_feature_aa64_sve_bf16(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, BFLOAT16) != 0; -} - -static inline bool isar_feature_aa64_sve2_sha3(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, SHA3) != 0; -} - -static inline bool isar_feature_aa64_sve2_sm4(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, SM4) != 0; -} - -static inline bool isar_feature_aa64_sve_i8mm(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, I8MM) != 0; -} - -static inline bool isar_feature_aa64_sve_f32mm(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, F32MM) != 0; -} - -static inline bool isar_feature_aa64_sve_f64mm(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, F64MM) != 0; -} - -static inline bool isar_feature_aa64_sme_f64f64(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64smfr0, ID_AA64SMFR0, F64F64); -} - -static inline bool isar_feature_aa64_sme_i16i64(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64smfr0, ID_AA64SMFR0, I16I64) == 0xf; -} - -static inline bool isar_feature_aa64_sme_fa64(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64smfr0, ID_AA64SMFR0, FA64); -} - -static inline bool isar_feature_aa64_doublelock(const ARMISARegisters *id) -{ - return FIELD_SEX64(id->id_aa64dfr0, ID_AA64DFR0, DOUBLELOCK) >= 0; -} - -static inline bool isar_feature_aa64_mops(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, MOPS); -} - -/* - * Feature tests for "does this exist in either 32-bit or 64-bit?" - */ -static inline bool isar_feature_any_fp16(const ARMISARegisters *id) -{ - return isar_feature_aa64_fp16(id) || isar_feature_aa32_fp16_arith(id); -} - -static inline bool isar_feature_any_predinv(const ARMISARegisters *id) -{ - return isar_feature_aa64_predinv(id) || isar_feature_aa32_predinv(id); -} - -static inline bool isar_feature_any_pmuv3p1(const ARMISARegisters *id) -{ - return isar_feature_aa64_pmuv3p1(id) || isar_feature_aa32_pmuv3p1(id); -} - -static inline bool isar_feature_any_pmuv3p4(const ARMISARegisters *id) -{ - return isar_feature_aa64_pmuv3p4(id) || isar_feature_aa32_pmuv3p4(id); -} - -static inline bool isar_feature_any_pmuv3p5(const ARMISARegisters *id) -{ - return isar_feature_aa64_pmuv3p5(id) || isar_feature_aa32_pmuv3p5(id); -} - -static inline bool isar_feature_any_ccidx(const ARMISARegisters *id) -{ - return isar_feature_aa64_ccidx(id) || isar_feature_aa32_ccidx(id); -} - -static inline bool isar_feature_any_tts2uxn(const ARMISARegisters *id) -{ - return isar_feature_aa64_tts2uxn(id) || isar_feature_aa32_tts2uxn(id); -} - -static inline bool isar_feature_any_debugv8p2(const ARMISARegisters *id) -{ - return isar_feature_aa64_debugv8p2(id) || isar_feature_aa32_debugv8p2(id); -} - -static inline bool isar_feature_any_ras(const ARMISARegisters *id) -{ - return isar_feature_aa64_ras(id) || isar_feature_aa32_ras(id); -} - -static inline bool isar_feature_any_half_evt(const ARMISARegisters *id) -{ - return isar_feature_aa64_half_evt(id) || isar_feature_aa32_half_evt(id); -} - -static inline bool isar_feature_any_evt(const ARMISARegisters *id) -{ - return isar_feature_aa64_evt(id) || isar_feature_aa32_evt(id); -} - -/* - * Forward to the above feature tests given an ARMCPU pointer. - */ -#define cpu_isar_feature(name, cpu) \ - ({ ARMCPU *cpu_ = (cpu); isar_feature_##name(&cpu_->isar); }) - #endif diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 1cb9d5b81a..1e9c6c85ae 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -32,6 +32,7 @@ #include "qapi/visitor.h" #include "hw/qdev-properties.h" #include "internals.h" +#include "cpu-features.h" #include "cpregs.h" void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) diff --git a/target/arm/debug_helper.c b/target/arm/debug_helper.c index abe72e35ae..79a3659c0c 100644 --- a/target/arm/debug_helper.c +++ b/target/arm/debug_helper.c @@ -9,6 +9,7 @@ #include "qemu/log.h" #include "cpu.h" #include "internals.h" +#include "cpu-features.h" #include "cpregs.h" #include "exec/exec-all.h" #include "exec/helper-proto.h" diff --git a/target/arm/gdbstub.c b/target/arm/gdbstub.c index b7ace24bfc..28f546a5ff 100644 --- a/target/arm/gdbstub.c +++ b/target/arm/gdbstub.c @@ -23,6 +23,7 @@ #include "gdbstub/helpers.h" #include "sysemu/tcg.h" #include "internals.h" +#include "cpu-features.h" #include "cpregs.h" typedef struct RegisterSysregXmlParam { diff --git a/target/arm/helper.c b/target/arm/helper.c index b29edb26af..5dc0d20a84 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -11,6 +11,7 @@ #include "trace.h" #include "cpu.h" #include "internals.h" +#include "cpu-features.h" #include "exec/helper-proto.h" #include "qemu/main-loop.h" #include "qemu/timer.h" diff --git a/target/arm/internals.h b/target/arm/internals.h index 1dd9182a54..f7224e6f4d 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -28,6 +28,7 @@ #include "hw/registerfields.h" #include "tcg/tcg-gvec-desc.h" #include "syndrome.h" +#include "cpu-features.h" /* register banks for CPU modes */ #define BANK_USRSYS 0 diff --git a/target/arm/kvm64.c b/target/arm/kvm64.c index 4bb68646e4..3c175c93a7 100644 --- a/target/arm/kvm64.c +++ b/target/arm/kvm64.c @@ -28,6 +28,7 @@ #include "sysemu/kvm_int.h" #include "kvm_arm.h" #include "internals.h" +#include "cpu-features.h" #include "hw/acpi/acpi.h" #include "hw/acpi/ghes.h" diff --git a/target/arm/machine.c b/target/arm/machine.c index fc4a4a4064..9e20b41189 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -5,6 +5,7 @@ #include "sysemu/tcg.h" #include "kvm_arm.h" #include "internals.h" +#include "cpu-features.h" #include "migration/cpu.h" static bool vfp_needed(void *opaque) diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 95db9ec4c3..53713e0300 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -13,6 +13,7 @@ #include "exec/exec-all.h" #include "cpu.h" #include "internals.h" +#include "cpu-features.h" #include "idau.h" #ifdef CONFIG_TCG # include "tcg/oversized-guest.h" diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index a9a8c0a059..08db1dbcc7 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -26,6 +26,7 @@ #include "hw/qdev-properties.h" #include "qemu/units.h" #include "internals.h" +#include "cpu-features.h" #include "cpregs.h" static uint64_t make_ccsidr64(unsigned assoc, unsigned linesize, diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c index cea1adb7b6..3d7fdce5c3 100644 --- a/target/arm/tcg/hflags.c +++ b/target/arm/tcg/hflags.c @@ -8,6 +8,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "internals.h" +#include "cpu-features.h" #include "exec/helper-proto.h" #include "cpregs.h" diff --git a/target/arm/tcg/m_helper.c b/target/arm/tcg/m_helper.c index 0045c18f80..a26adb75aa 100644 --- a/target/arm/tcg/m_helper.c +++ b/target/arm/tcg/m_helper.c @@ -9,6 +9,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "internals.h" +#include "cpu-features.h" #include "gdbstub/helpers.h" #include "exec/helper-proto.h" #include "qemu/main-loop.h" diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c index 403f8b09d3..ea08936a85 100644 --- a/target/arm/tcg/op_helper.c +++ b/target/arm/tcg/op_helper.c @@ -21,6 +21,7 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "internals.h" +#include "cpu-features.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" #include "cpregs.h" diff --git a/target/arm/tcg/pauth_helper.c b/target/arm/tcg/pauth_helper.c index 4da2962ad5..c4b143024f 100644 --- a/target/arm/tcg/pauth_helper.c +++ b/target/arm/tcg/pauth_helper.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "internals.h" +#include "cpu-features.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" #include "exec/helper-proto.h" diff --git a/target/arm/tcg/tlb_helper.c b/target/arm/tcg/tlb_helper.c index 59bff8b452..4fdd85359e 100644 --- a/target/arm/tcg/tlb_helper.c +++ b/target/arm/tcg/tlb_helper.c @@ -8,6 +8,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "internals.h" +#include "cpu-features.h" #include "exec/exec-all.h" #include "exec/helper-proto.h" diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index b4046611f5..9efe00cf6c 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -8,7 +8,7 @@ #include "exec/translator.h" #include "exec/helper-gen.h" #include "internals.h" - +#include "cpu-features.h" /* internal defines */ diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index 789bba36cc..3e5e37abbe 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -21,6 +21,7 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "internals.h" +#include "cpu-features.h" #ifdef CONFIG_TCG #include "qemu/log.h" #include "fpu/softfloat.h" From 326077724a0c71e06e0147c210494b4ea36033c9 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 24 Oct 2023 17:35:06 +0100 Subject: [PATCH 147/974] target/arm: Move ID_AA64MMFR1 and ID_AA64MMFR2 tests together MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Our list of isar_feature functions is not in any particular order, but tests on fields of the same ID register tend to be grouped together. A few functions that are tests of fields in ID_AA64MMFR1 and ID_AA64MMFR2 are not in the same place as the rest; move them into their groups. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20231024163510.2972081-3-peter.maydell@linaro.org --- target/arm/cpu-features.h | 60 +++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index bfc9bfafe7..fc85a8fe13 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -641,6 +641,21 @@ static inline bool isar_feature_aa64_tidcp1(const ARMISARegisters *id) return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR1, TIDCP1) != 0; } +static inline bool isar_feature_aa64_hafs(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, HAFDBS) != 0; +} + +static inline bool isar_feature_aa64_hdbs(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, HAFDBS) >= 2; +} + +static inline bool isar_feature_aa64_tts2uxn(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, XNX) != 0; +} + static inline bool isar_feature_aa64_uao(const ARMISARegisters *id) { return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, UAO) != 0; @@ -676,6 +691,21 @@ static inline bool isar_feature_aa64_evt(const ARMISARegisters *id) return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, EVT) >= 2; } +static inline bool isar_feature_aa64_ccidx(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, CCIDX) != 0; +} + +static inline bool isar_feature_aa64_lva(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, VARANGE) != 0; +} + +static inline bool isar_feature_aa64_e0pd(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, E0PD) != 0; +} + static inline bool isar_feature_aa64_bti(const ARMISARegisters *id) { return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, BT) != 0; @@ -794,36 +824,6 @@ static inline bool isar_feature_aa64_fgt(const ARMISARegisters *id) return FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, FGT) != 0; } -static inline bool isar_feature_aa64_ccidx(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, CCIDX) != 0; -} - -static inline bool isar_feature_aa64_lva(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, VARANGE) != 0; -} - -static inline bool isar_feature_aa64_e0pd(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, E0PD) != 0; -} - -static inline bool isar_feature_aa64_hafs(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, HAFDBS) != 0; -} - -static inline bool isar_feature_aa64_hdbs(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, HAFDBS) >= 2; -} - -static inline bool isar_feature_aa64_tts2uxn(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, XNX) != 0; -} - static inline bool isar_feature_aa64_dit(const ARMISARegisters *id) { return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, DIT) != 0; From 5181c751af28cbb49d924d474fcbeff6014c8319 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 24 Oct 2023 17:35:07 +0100 Subject: [PATCH 148/974] target/arm: Move ID_AA64MMFR0 tests up to before MMFR1 and MMFR2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the ID_AA64MMFR0 feature test functions up so they are before the ones for ID_AA64MMFR1 and ID_AA64MMFR2. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20231024163510.2972081-4-peter.maydell@linaro.org --- target/arm/cpu-features.h | 120 +++++++++++++++++++------------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index fc85a8fe13..90200a4b98 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -606,6 +606,66 @@ static inline bool isar_feature_aa64_rme(const ARMISARegisters *id) return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, RME) != 0; } +static inline bool isar_feature_aa64_tgran4_lpa2(const ARMISARegisters *id) +{ + return FIELD_SEX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN4) >= 1; +} + +static inline bool isar_feature_aa64_tgran4_2_lpa2(const ARMISARegisters *id) +{ + unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN4_2); + return t >= 3 || (t == 0 && isar_feature_aa64_tgran4_lpa2(id)); +} + +static inline bool isar_feature_aa64_tgran16_lpa2(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN16) >= 2; +} + +static inline bool isar_feature_aa64_tgran16_2_lpa2(const ARMISARegisters *id) +{ + unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN16_2); + return t >= 3 || (t == 0 && isar_feature_aa64_tgran16_lpa2(id)); +} + +static inline bool isar_feature_aa64_tgran4(const ARMISARegisters *id) +{ + return FIELD_SEX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN4) >= 0; +} + +static inline bool isar_feature_aa64_tgran16(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN16) >= 1; +} + +static inline bool isar_feature_aa64_tgran64(const ARMISARegisters *id) +{ + return FIELD_SEX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN64) >= 0; +} + +static inline bool isar_feature_aa64_tgran4_2(const ARMISARegisters *id) +{ + unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN4_2); + return t >= 2 || (t == 0 && isar_feature_aa64_tgran4(id)); +} + +static inline bool isar_feature_aa64_tgran16_2(const ARMISARegisters *id) +{ + unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN16_2); + return t >= 2 || (t == 0 && isar_feature_aa64_tgran16(id)); +} + +static inline bool isar_feature_aa64_tgran64_2(const ARMISARegisters *id) +{ + unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN64_2); + return t >= 2 || (t == 0 && isar_feature_aa64_tgran64(id)); +} + +static inline bool isar_feature_aa64_fgt(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, FGT) != 0; +} + static inline bool isar_feature_aa64_vh(const ARMISARegisters *id) { return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, VH) != 0; @@ -764,66 +824,6 @@ static inline bool isar_feature_aa64_hbc(const ARMISARegisters *id) return FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, BC) != 0; } -static inline bool isar_feature_aa64_tgran4_lpa2(const ARMISARegisters *id) -{ - return FIELD_SEX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN4) >= 1; -} - -static inline bool isar_feature_aa64_tgran4_2_lpa2(const ARMISARegisters *id) -{ - unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN4_2); - return t >= 3 || (t == 0 && isar_feature_aa64_tgran4_lpa2(id)); -} - -static inline bool isar_feature_aa64_tgran16_lpa2(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN16) >= 2; -} - -static inline bool isar_feature_aa64_tgran16_2_lpa2(const ARMISARegisters *id) -{ - unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN16_2); - return t >= 3 || (t == 0 && isar_feature_aa64_tgran16_lpa2(id)); -} - -static inline bool isar_feature_aa64_tgran4(const ARMISARegisters *id) -{ - return FIELD_SEX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN4) >= 0; -} - -static inline bool isar_feature_aa64_tgran16(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN16) >= 1; -} - -static inline bool isar_feature_aa64_tgran64(const ARMISARegisters *id) -{ - return FIELD_SEX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN64) >= 0; -} - -static inline bool isar_feature_aa64_tgran4_2(const ARMISARegisters *id) -{ - unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN4_2); - return t >= 2 || (t == 0 && isar_feature_aa64_tgran4(id)); -} - -static inline bool isar_feature_aa64_tgran16_2(const ARMISARegisters *id) -{ - unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN16_2); - return t >= 2 || (t == 0 && isar_feature_aa64_tgran16(id)); -} - -static inline bool isar_feature_aa64_tgran64_2(const ARMISARegisters *id) -{ - unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN64_2); - return t >= 2 || (t == 0 && isar_feature_aa64_tgran64(id)); -} - -static inline bool isar_feature_aa64_fgt(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, FGT) != 0; -} - static inline bool isar_feature_aa64_dit(const ARMISARegisters *id) { return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, DIT) != 0; From 338ddfb1f3db73ef960c069db6098b88dc88b0d0 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 24 Oct 2023 17:35:08 +0100 Subject: [PATCH 149/974] target/arm: Move ID_AA64ISAR* test functions together MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the feature test functions that test ID_AA64ISAR* fields together. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20231024163510.2972081-5-peter.maydell@linaro.org --- target/arm/cpu-features.h | 70 +++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index 90200a4b98..e73120ef97 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -451,6 +451,16 @@ static inline bool isar_feature_aa64_rndr(const ARMISARegisters *id) return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, RNDR) != 0; } +static inline bool isar_feature_aa64_tlbirange(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, TLB) == 2; +} + +static inline bool isar_feature_aa64_tlbios(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, TLB) != 0; +} + static inline bool isar_feature_aa64_jscvt(const ARMISARegisters *id) { return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, JSCVT) != 0; @@ -514,16 +524,6 @@ static inline bool isar_feature_aa64_pauth_qarma3(const ARMISARegisters *id) return FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, APA3) != 0; } -static inline bool isar_feature_aa64_tlbirange(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, TLB) == 2; -} - -static inline bool isar_feature_aa64_tlbios(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, TLB) != 0; -} - static inline bool isar_feature_aa64_sb(const ARMISARegisters *id) { return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, SB) != 0; @@ -554,6 +554,31 @@ static inline bool isar_feature_aa64_bf16(const ARMISARegisters *id) return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, BF16) != 0; } +static inline bool isar_feature_aa64_rcpc_8_3(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, LRCPC) != 0; +} + +static inline bool isar_feature_aa64_rcpc_8_4(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, LRCPC) >= 2; +} + +static inline bool isar_feature_aa64_i8mm(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, I8MM) != 0; +} + +static inline bool isar_feature_aa64_hbc(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, BC) != 0; +} + +static inline bool isar_feature_aa64_mops(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, MOPS); +} + static inline bool isar_feature_aa64_fp_simd(const ARMISARegisters *id) { /* We always set the AdvSIMD and FP fields identically. */ @@ -804,26 +829,6 @@ static inline bool isar_feature_aa64_pmuv3p5(const ARMISARegisters *id) FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) != 0xf; } -static inline bool isar_feature_aa64_rcpc_8_3(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, LRCPC) != 0; -} - -static inline bool isar_feature_aa64_rcpc_8_4(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, LRCPC) >= 2; -} - -static inline bool isar_feature_aa64_i8mm(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, I8MM) != 0; -} - -static inline bool isar_feature_aa64_hbc(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, BC) != 0; -} - static inline bool isar_feature_aa64_dit(const ARMISARegisters *id) { return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, DIT) != 0; @@ -922,11 +927,6 @@ static inline bool isar_feature_aa64_doublelock(const ARMISARegisters *id) return FIELD_SEX64(id->id_aa64dfr0, ID_AA64DFR0, DOUBLELOCK) >= 0; } -static inline bool isar_feature_aa64_mops(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, MOPS); -} - /* * Feature tests for "does this exist in either 32-bit or 64-bit?" */ From e677d7d47062b6714aeffd92cbc015d4ae2b9eaf Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 24 Oct 2023 17:35:09 +0100 Subject: [PATCH 150/974] target/arm: Move ID_AA64PFR* tests together MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move all the ID_AA64PFR* feature test functions together. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20231024163510.2972081-6-peter.maydell@linaro.org --- target/arm/cpu-features.h | 86 +++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index e73120ef97..0ed05b8b19 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -631,6 +631,49 @@ static inline bool isar_feature_aa64_rme(const ARMISARegisters *id) return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, RME) != 0; } +static inline bool isar_feature_aa64_dit(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, DIT) != 0; +} + +static inline bool isar_feature_aa64_scxtnum(const ARMISARegisters *id) +{ + int key = FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, CSV2); + if (key >= 2) { + return true; /* FEAT_CSV2_2 */ + } + if (key == 1) { + key = FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, CSV2_FRAC); + return key >= 2; /* FEAT_CSV2_1p2 */ + } + return false; +} + +static inline bool isar_feature_aa64_ssbs(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, SSBS) != 0; +} + +static inline bool isar_feature_aa64_bti(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, BT) != 0; +} + +static inline bool isar_feature_aa64_mte_insn_reg(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, MTE) != 0; +} + +static inline bool isar_feature_aa64_mte(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, MTE) >= 2; +} + +static inline bool isar_feature_aa64_sme(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, SME) != 0; +} + static inline bool isar_feature_aa64_tgran4_lpa2(const ARMISARegisters *id) { return FIELD_SEX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN4) >= 1; @@ -791,26 +834,6 @@ static inline bool isar_feature_aa64_e0pd(const ARMISARegisters *id) return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, E0PD) != 0; } -static inline bool isar_feature_aa64_bti(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, BT) != 0; -} - -static inline bool isar_feature_aa64_mte_insn_reg(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, MTE) != 0; -} - -static inline bool isar_feature_aa64_mte(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, MTE) >= 2; -} - -static inline bool isar_feature_aa64_sme(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, SME) != 0; -} - static inline bool isar_feature_aa64_pmuv3p1(const ARMISARegisters *id) { return FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) >= 4 && @@ -829,29 +852,6 @@ static inline bool isar_feature_aa64_pmuv3p5(const ARMISARegisters *id) FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) != 0xf; } -static inline bool isar_feature_aa64_dit(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, DIT) != 0; -} - -static inline bool isar_feature_aa64_scxtnum(const ARMISARegisters *id) -{ - int key = FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, CSV2); - if (key >= 2) { - return true; /* FEAT_CSV2_2 */ - } - if (key == 1) { - key = FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, CSV2_FRAC); - return key >= 2; /* FEAT_CSV2_1p2 */ - } - return false; -} - -static inline bool isar_feature_aa64_ssbs(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, SSBS) != 0; -} - static inline bool isar_feature_aa64_debugv8p2(const ARMISARegisters *id) { return FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, DEBUGVER) >= 8; From 1036ce4e6a2fa0f865e2e6514d198db931b5510f Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 24 Oct 2023 17:35:10 +0100 Subject: [PATCH 151/974] target/arm: Move ID_AA64DFR* feature tests together MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move all the ID_AA64DFR* feature test functions together. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20231024163510.2972081-7-peter.maydell@linaro.org --- target/arm/cpu-features.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index 0ed05b8b19..66212cd7ec 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -857,6 +857,11 @@ static inline bool isar_feature_aa64_debugv8p2(const ARMISARegisters *id) return FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, DEBUGVER) >= 8; } +static inline bool isar_feature_aa64_doublelock(const ARMISARegisters *id) +{ + return FIELD_SEX64(id->id_aa64dfr0, ID_AA64DFR0, DOUBLELOCK) >= 0; +} + static inline bool isar_feature_aa64_sve2(const ARMISARegisters *id) { return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, SVEVER) != 0; @@ -922,11 +927,6 @@ static inline bool isar_feature_aa64_sme_fa64(const ARMISARegisters *id) return FIELD_EX64(id->id_aa64smfr0, ID_AA64SMFR0, FA64); } -static inline bool isar_feature_aa64_doublelock(const ARMISARegisters *id) -{ - return FIELD_SEX64(id->id_aa64dfr0, ID_AA64DFR0, DOUBLELOCK) >= 0; -} - /* * Feature tests for "does this exist in either 32-bit or 64-bit?" */ From 307521d6e29e559c89afa9dbd337ae75fe3c436d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 24 Oct 2023 18:24:38 +0100 Subject: [PATCH 152/974] target/arm: Fix syndrome for FGT traps on ERET In commit 442c9d682c94fc2 when we converted the ERET, ERETAA, ERETAB instructions to decodetree, the conversion accidentally lost the correct setting of the syndrome register when taking a trap because of the FEAT_FGT HFGITR_EL1.ERET bit. Instead of reporting a correct full syndrome value with the EC and IL bits, we only reported the low two bits of the syndrome, because the call to syn_erettrap() got dropped. Fix the syndrome values for these traps by reinstating the syn_erettrap() calls. Fixes: 442c9d682c94fc2 ("target/arm: Convert ERET, ERETAA, ERETAB to decodetree") Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20231024172438.2990945-1-peter.maydell@linaro.org --- target/arm/tcg/translate-a64.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index ad78b8b120..41484d8ae5 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -1606,7 +1606,7 @@ static bool trans_ERET(DisasContext *s, arg_ERET *a) return false; } if (s->fgt_eret) { - gen_exception_insn_el(s, 0, EXCP_UDEF, 0, 2); + gen_exception_insn_el(s, 0, EXCP_UDEF, syn_erettrap(0), 2); return true; } dst = tcg_temp_new_i64(); @@ -1633,7 +1633,7 @@ static bool trans_ERETA(DisasContext *s, arg_reta *a) } /* The FGT trap takes precedence over an auth trap. */ if (s->fgt_eret) { - gen_exception_insn_el(s, 0, EXCP_UDEF, a->m ? 3 : 2, 2); + gen_exception_insn_el(s, 0, EXCP_UDEF, syn_erettrap(a->m ? 3 : 2), 2); return true; } dst = tcg_temp_new_i64(); From 0c436de6ba2718698e3f2cf4b662e38ff566711b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 25 Oct 2023 08:53:07 +0200 Subject: [PATCH 153/974] hw/arm/allwinner-a10: Remove 'hw/arm/boot.h' from header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "hw/arm/boot.h" is only required on the source file. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Luc Michel Message-id: 20231025065316.56817-2-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/cubieboard.c | 1 + include/hw/arm/allwinner-a10.h | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/cubieboard.c b/hw/arm/cubieboard.c index 8c7fa91529..29146f5018 100644 --- a/hw/arm/cubieboard.c +++ b/hw/arm/cubieboard.c @@ -21,6 +21,7 @@ #include "hw/boards.h" #include "hw/qdev-properties.h" #include "hw/arm/allwinner-a10.h" +#include "hw/arm/boot.h" #include "hw/i2c/i2c.h" static struct arm_boot_info cubieboard_binfo = { diff --git a/include/hw/arm/allwinner-a10.h b/include/hw/arm/allwinner-a10.h index cd1465c613..2eb83a17ea 100644 --- a/include/hw/arm/allwinner-a10.h +++ b/include/hw/arm/allwinner-a10.h @@ -1,7 +1,6 @@ #ifndef HW_ARM_ALLWINNER_A10_H #define HW_ARM_ALLWINNER_A10_H -#include "hw/arm/boot.h" #include "hw/timer/allwinner-a10-pit.h" #include "hw/intc/allwinner-a10-pic.h" #include "hw/net/allwinner_emac.h" From 0e246c6209b6058646ae48ec768afdd1bc76e962 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 25 Oct 2023 08:53:08 +0200 Subject: [PATCH 154/974] hw/arm/allwinner-h3: Remove 'hw/arm/boot.h' from header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "hw/arm/boot.h" is only required on the source file. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Luc Michel Message-id: 20231025065316.56817-3-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/orangepi.c | 1 + include/hw/arm/allwinner-h3.h | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c index 10653361ed..d0eca54cd9 100644 --- a/hw/arm/orangepi.c +++ b/hw/arm/orangepi.c @@ -25,6 +25,7 @@ #include "hw/boards.h" #include "hw/qdev-properties.h" #include "hw/arm/allwinner-h3.h" +#include "hw/arm/boot.h" static struct arm_boot_info orangepi_binfo; diff --git a/include/hw/arm/allwinner-h3.h b/include/hw/arm/allwinner-h3.h index f15d6d7cc7..24ba4e1bf4 100644 --- a/include/hw/arm/allwinner-h3.h +++ b/include/hw/arm/allwinner-h3.h @@ -36,7 +36,6 @@ #define HW_ARM_ALLWINNER_H3_H #include "qom/object.h" -#include "hw/arm/boot.h" #include "hw/timer/allwinner-a10-pit.h" #include "hw/intc/arm_gic.h" #include "hw/misc/allwinner-h3-ccu.h" From 6f4d538aa1b7923e1bf2917f38569888482c3a29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 25 Oct 2023 08:53:09 +0200 Subject: [PATCH 155/974] hw/arm/allwinner-r40: Remove 'hw/arm/boot.h' from header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "hw/arm/boot.h" is only required on the source file. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Luc Michel Message-id: 20231025065316.56817-4-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/bananapi_m2u.c | 1 + include/hw/arm/allwinner-r40.h | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/bananapi_m2u.c b/hw/arm/bananapi_m2u.c index 74121d8966..a7c7a9f96d 100644 --- a/hw/arm/bananapi_m2u.c +++ b/hw/arm/bananapi_m2u.c @@ -26,6 +26,7 @@ #include "hw/i2c/i2c.h" #include "hw/qdev-properties.h" #include "hw/arm/allwinner-r40.h" +#include "hw/arm/boot.h" static struct arm_boot_info bpim2u_binfo; diff --git a/include/hw/arm/allwinner-r40.h b/include/hw/arm/allwinner-r40.h index 72710d3edc..6e1ac9d4c1 100644 --- a/include/hw/arm/allwinner-r40.h +++ b/include/hw/arm/allwinner-r40.h @@ -21,7 +21,6 @@ #define HW_ARM_ALLWINNER_R40_H #include "qom/object.h" -#include "hw/arm/boot.h" #include "hw/timer/allwinner-a10-pit.h" #include "hw/intc/arm_gic.h" #include "hw/sd/allwinner-sdhost.h" From 6fda3b91bf28aa88791fc77954abba0b1648c296 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 25 Oct 2023 08:53:10 +0200 Subject: [PATCH 156/974] hw/arm/fsl-imx25: Remove 'hw/arm/boot.h' from header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "hw/arm/boot.h" is only required on the source file. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Luc Michel Message-id: 20231025065316.56817-5-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/imx25_pdk.c | 1 + include/hw/arm/fsl-imx25.h | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/imx25_pdk.c b/hw/arm/imx25_pdk.c index b4f7f4e8a7..7dfddd49e2 100644 --- a/hw/arm/imx25_pdk.c +++ b/hw/arm/imx25_pdk.c @@ -27,6 +27,7 @@ #include "qapi/error.h" #include "hw/qdev-properties.h" #include "hw/arm/fsl-imx25.h" +#include "hw/arm/boot.h" #include "hw/boards.h" #include "qemu/error-report.h" #include "sysemu/qtest.h" diff --git a/include/hw/arm/fsl-imx25.h b/include/hw/arm/fsl-imx25.h index 1b1086e945..df2f83980f 100644 --- a/include/hw/arm/fsl-imx25.h +++ b/include/hw/arm/fsl-imx25.h @@ -17,7 +17,6 @@ #ifndef FSL_IMX25_H #define FSL_IMX25_H -#include "hw/arm/boot.h" #include "hw/intc/imx_avic.h" #include "hw/misc/imx25_ccm.h" #include "hw/char/imx_serial.h" From 88e763dab4ea49bb177affeaa4596ee3270e3287 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 25 Oct 2023 08:53:11 +0200 Subject: [PATCH 157/974] hw/arm/fsl-imx31: Remove 'hw/arm/boot.h' from header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "hw/arm/boot.h" is only required on the source file. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Luc Michel Message-id: 20231025065316.56817-6-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/kzm.c | 1 + include/hw/arm/fsl-imx31.h | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/kzm.c b/hw/arm/kzm.c index b1b281c9ac..9be91ebeaa 100644 --- a/hw/arm/kzm.c +++ b/hw/arm/kzm.c @@ -16,6 +16,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/arm/fsl-imx31.h" +#include "hw/arm/boot.h" #include "hw/boards.h" #include "qemu/error-report.h" #include "exec/address-spaces.h" diff --git a/include/hw/arm/fsl-imx31.h b/include/hw/arm/fsl-imx31.h index c116a73e0b..40c593a5cf 100644 --- a/include/hw/arm/fsl-imx31.h +++ b/include/hw/arm/fsl-imx31.h @@ -17,7 +17,6 @@ #ifndef FSL_IMX31_H #define FSL_IMX31_H -#include "hw/arm/boot.h" #include "hw/intc/imx_avic.h" #include "hw/misc/imx31_ccm.h" #include "hw/char/imx_serial.h" From e77bf48586444ede4d37508d276358407c69ccc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 25 Oct 2023 08:53:12 +0200 Subject: [PATCH 158/974] hw/arm/fsl-imx6: Remove 'hw/arm/boot.h' from header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "hw/arm/boot.h" is only required on the source file. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Luc Michel Message-id: 20231025065316.56817-7-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/sabrelite.c | 1 + include/hw/arm/fsl-imx6.h | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/sabrelite.c b/hw/arm/sabrelite.c index 41191245b8..56f184b9ae 100644 --- a/hw/arm/sabrelite.c +++ b/hw/arm/sabrelite.c @@ -13,6 +13,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/arm/fsl-imx6.h" +#include "hw/arm/boot.h" #include "hw/boards.h" #include "hw/qdev-properties.h" #include "qemu/error-report.h" diff --git a/include/hw/arm/fsl-imx6.h b/include/hw/arm/fsl-imx6.h index 5b4d48da08..519b871014 100644 --- a/include/hw/arm/fsl-imx6.h +++ b/include/hw/arm/fsl-imx6.h @@ -17,7 +17,6 @@ #ifndef FSL_IMX6_H #define FSL_IMX6_H -#include "hw/arm/boot.h" #include "hw/cpu/a9mpcore.h" #include "hw/misc/imx6_ccm.h" #include "hw/misc/imx6_src.h" From 8727076bb97de473c48939e8900e583f91d93063 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 25 Oct 2023 08:53:13 +0200 Subject: [PATCH 159/974] hw/arm/fsl-imx6ul: Remove 'hw/arm/boot.h' from header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "hw/arm/boot.h" is only required on the source file. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Luc Michel Message-id: 20231025065316.56817-8-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/mcimx6ul-evk.c | 1 + include/hw/arm/fsl-imx6ul.h | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/mcimx6ul-evk.c b/hw/arm/mcimx6ul-evk.c index 3ac1e2ea9b..500427e94b 100644 --- a/hw/arm/mcimx6ul-evk.c +++ b/hw/arm/mcimx6ul-evk.c @@ -13,6 +13,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/arm/fsl-imx6ul.h" +#include "hw/arm/boot.h" #include "hw/boards.h" #include "hw/qdev-properties.h" #include "qemu/error-report.h" diff --git a/include/hw/arm/fsl-imx6ul.h b/include/hw/arm/fsl-imx6ul.h index 63012628ff..14390f6014 100644 --- a/include/hw/arm/fsl-imx6ul.h +++ b/include/hw/arm/fsl-imx6ul.h @@ -17,7 +17,6 @@ #ifndef FSL_IMX6UL_H #define FSL_IMX6UL_H -#include "hw/arm/boot.h" #include "hw/cpu/a15mpcore.h" #include "hw/misc/imx6ul_ccm.h" #include "hw/misc/imx6_src.h" From f27cbd94ee57897db09d816d8cfbc19d3c1ac85c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 25 Oct 2023 08:53:14 +0200 Subject: [PATCH 160/974] hw/arm/fsl-imx7: Remove 'hw/arm/boot.h' from header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "hw/arm/boot.h" is only required on the source file. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Luc Michel Message-id: 20231025065316.56817-9-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/mcimx7d-sabre.c | 1 + include/hw/arm/fsl-imx7.h | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/mcimx7d-sabre.c b/hw/arm/mcimx7d-sabre.c index d1778122b6..693a1023b6 100644 --- a/hw/arm/mcimx7d-sabre.c +++ b/hw/arm/mcimx7d-sabre.c @@ -15,6 +15,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/arm/fsl-imx7.h" +#include "hw/arm/boot.h" #include "hw/boards.h" #include "hw/qdev-properties.h" #include "qemu/error-report.h" diff --git a/include/hw/arm/fsl-imx7.h b/include/hw/arm/fsl-imx7.h index 2cbfc6b2b2..411fa1c2e3 100644 --- a/include/hw/arm/fsl-imx7.h +++ b/include/hw/arm/fsl-imx7.h @@ -19,7 +19,6 @@ #ifndef FSL_IMX7_H #define FSL_IMX7_H -#include "hw/arm/boot.h" #include "hw/cpu/a15mpcore.h" #include "hw/intc/imx_gpcv2.h" #include "hw/misc/imx7_ccm.h" From d95a3a75800342a9f5cdb5afe9fe7974e0b59209 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 25 Oct 2023 08:53:15 +0200 Subject: [PATCH 161/974] hw/arm/xlnx-versal: Remove 'hw/arm/boot.h' from header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "hw/arm/boot.h" is only required on the source file. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Luc Michel Message-id: 20231025065316.56817-10-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal-virt.c | 1 + include/hw/arm/xlnx-versal.h | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index 88c561ff63..537118224f 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -19,6 +19,7 @@ #include "cpu.h" #include "hw/qdev-properties.h" #include "hw/arm/xlnx-versal.h" +#include "hw/arm/boot.h" #include "qom/object.h" #define TYPE_XLNX_VERSAL_VIRT_MACHINE MACHINE_TYPE_NAME("xlnx-versal-virt") diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index 7b419f88c2..b710d71fb0 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -13,7 +13,6 @@ #define XLNX_VERSAL_H #include "hw/sysbus.h" -#include "hw/arm/boot.h" #include "hw/cpu/cluster.h" #include "hw/or-irq.h" #include "hw/sd/sdhci.h" From f3205af4c7f29ba4fc0f995d48e450787f93853e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 25 Oct 2023 08:53:16 +0200 Subject: [PATCH 162/974] hw/arm/xlnx-zynqmp: Remove 'hw/arm/boot.h' from header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "hw/arm/boot.h" is only required on the source file. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Luc Michel Message-id: 20231025065316.56817-11-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/xlnx-zcu102.c | 1 + include/hw/arm/xlnx-zynqmp.h | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/xlnx-zcu102.c b/hw/arm/xlnx-zcu102.c index c5a07cfe19..4667cb333c 100644 --- a/hw/arm/xlnx-zcu102.c +++ b/hw/arm/xlnx-zcu102.c @@ -18,6 +18,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/arm/xlnx-zynqmp.h" +#include "hw/arm/boot.h" #include "hw/boards.h" #include "qemu/error-report.h" #include "qemu/log.h" diff --git a/include/hw/arm/xlnx-zynqmp.h b/include/hw/arm/xlnx-zynqmp.h index 687c75e3b0..96358d51eb 100644 --- a/include/hw/arm/xlnx-zynqmp.h +++ b/include/hw/arm/xlnx-zynqmp.h @@ -18,7 +18,6 @@ #ifndef XLNX_ZYNQMP_H #define XLNX_ZYNQMP_H -#include "hw/arm/boot.h" #include "hw/intc/arm_gic.h" #include "hw/net/cadence_gem.h" #include "hw/char/cadence_uart.h" From 85500a1aef1cdfb2878d88a3d88db56ad972fb60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 20 Oct 2023 15:03:22 +0200 Subject: [PATCH 163/974] hw/sd/pxa2xx: Realize sysbus device before accessing it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sysbus_mmio_map() and sysbus_connect_irq() should not be called on unrealized device. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Message-id: 20231020130331.50048-2-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/sd/pxa2xx_mmci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/sd/pxa2xx_mmci.c b/hw/sd/pxa2xx_mmci.c index 124fbf8bbd..9f7a880bac 100644 --- a/hw/sd/pxa2xx_mmci.c +++ b/hw/sd/pxa2xx_mmci.c @@ -483,11 +483,11 @@ PXA2xxMMCIState *pxa2xx_mmci_init(MemoryRegion *sysmem, dev = qdev_new(TYPE_PXA2XX_MMCI); sbd = SYS_BUS_DEVICE(dev); + sysbus_realize_and_unref(sbd, &error_fatal); sysbus_mmio_map(sbd, 0, base); sysbus_connect_irq(sbd, 0, irq); qdev_connect_gpio_out_named(dev, "rx-dma", 0, rx_dma); qdev_connect_gpio_out_named(dev, "tx-dma", 0, tx_dma); - sysbus_realize_and_unref(sbd, &error_fatal); return PXA2XX_MMCI(dev); } From d8239c475b9aed7984b3c01942b08c03b469ee8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 20 Oct 2023 15:03:23 +0200 Subject: [PATCH 164/974] hw/sd/pxa2xx: Do not open-code sysbus_create_simple() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Message-id: 20231020130331.50048-3-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/sd/pxa2xx_mmci.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/hw/sd/pxa2xx_mmci.c b/hw/sd/pxa2xx_mmci.c index 9f7a880bac..4749e935d8 100644 --- a/hw/sd/pxa2xx_mmci.c +++ b/hw/sd/pxa2xx_mmci.c @@ -479,13 +479,8 @@ PXA2xxMMCIState *pxa2xx_mmci_init(MemoryRegion *sysmem, qemu_irq irq, qemu_irq rx_dma, qemu_irq tx_dma) { DeviceState *dev; - SysBusDevice *sbd; - dev = qdev_new(TYPE_PXA2XX_MMCI); - sbd = SYS_BUS_DEVICE(dev); - sysbus_realize_and_unref(sbd, &error_fatal); - sysbus_mmio_map(sbd, 0, base); - sysbus_connect_irq(sbd, 0, irq); + dev = sysbus_create_simple(TYPE_PXA2XX_MMCI, base, irq); qdev_connect_gpio_out_named(dev, "rx-dma", 0, rx_dma); qdev_connect_gpio_out_named(dev, "tx-dma", 0, tx_dma); From 217ceefee0dad668fe1557131c30be66c97ea699 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 20 Oct 2023 15:03:24 +0200 Subject: [PATCH 165/974] hw/pcmcia/pxa2xx: Realize sysbus device before accessing it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sysbus_mmio_map() should not be called on unrealized device. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Message-id: 20231020130331.50048-4-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/pcmcia/pxa2xx.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/hw/pcmcia/pxa2xx.c b/hw/pcmcia/pxa2xx.c index fcca7e571b..e7264feb45 100644 --- a/hw/pcmcia/pxa2xx.c +++ b/hw/pcmcia/pxa2xx.c @@ -142,15 +142,12 @@ PXA2xxPCMCIAState *pxa2xx_pcmcia_init(MemoryRegion *sysmem, hwaddr base) { DeviceState *dev; - PXA2xxPCMCIAState *s; dev = qdev_new(TYPE_PXA2XX_PCMCIA); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - s = PXA2XX_PCMCIA(dev); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - return s; + return PXA2XX_PCMCIA(dev); } static void pxa2xx_pcmcia_initfn(Object *obj) From 77d3fa5c3080c930af34f906263b0d883351eea9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 20 Oct 2023 15:03:25 +0200 Subject: [PATCH 166/974] hw/pcmcia/pxa2xx: Do not open-code sysbus_create_simple() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Thomas Huth Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20231020130331.50048-5-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/pcmcia/pxa2xx.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hw/pcmcia/pxa2xx.c b/hw/pcmcia/pxa2xx.c index e7264feb45..3d512a292c 100644 --- a/hw/pcmcia/pxa2xx.c +++ b/hw/pcmcia/pxa2xx.c @@ -143,9 +143,7 @@ PXA2xxPCMCIAState *pxa2xx_pcmcia_init(MemoryRegion *sysmem, { DeviceState *dev; - dev = qdev_new(TYPE_PXA2XX_PCMCIA); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + dev = sysbus_create_simple(TYPE_PXA2XX_PCMCIA, base, NULL); return PXA2XX_PCMCIA(dev); } From cbf08c18512600650b8b25d1b7d7aa3a83846dc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 20 Oct 2023 15:03:26 +0200 Subject: [PATCH 167/974] hw/pcmcia/pxa2xx: Inline pxa2xx_pcmcia_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Thomas Huth Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20231020130331.50048-6-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/pxa2xx.c | 12 ++++++++---- hw/pcmcia/pxa2xx.c | 10 ---------- include/hw/arm/pxa.h | 2 -- 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c index 07d5dd8691..601ddd8766 100644 --- a/hw/arm/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -2205,8 +2205,10 @@ PXA2xxState *pxa270_init(unsigned int sdram_size, const char *cpu_type) sysbus_create_simple("sysbus-ohci", 0x4c000000, qdev_get_gpio_in(s->pic, PXA2XX_PIC_USBH1)); - s->pcmcia[0] = pxa2xx_pcmcia_init(address_space, 0x20000000); - s->pcmcia[1] = pxa2xx_pcmcia_init(address_space, 0x30000000); + s->pcmcia[0] = PXA2XX_PCMCIA(sysbus_create_simple(TYPE_PXA2XX_PCMCIA, + 0x20000000, NULL)); + s->pcmcia[1] = PXA2XX_PCMCIA(sysbus_create_simple(TYPE_PXA2XX_PCMCIA, + 0x30000000, NULL)); sysbus_create_simple(TYPE_PXA2XX_RTC, 0x40900000, qdev_get_gpio_in(s->pic, PXA2XX_PIC_RTCALARM)); @@ -2338,8 +2340,10 @@ PXA2xxState *pxa255_init(unsigned int sdram_size) s->ssp[i] = (SSIBus *)qdev_get_child_bus(dev, "ssi"); } - s->pcmcia[0] = pxa2xx_pcmcia_init(address_space, 0x20000000); - s->pcmcia[1] = pxa2xx_pcmcia_init(address_space, 0x30000000); + s->pcmcia[0] = PXA2XX_PCMCIA(sysbus_create_simple(TYPE_PXA2XX_PCMCIA, + 0x20000000, NULL)); + s->pcmcia[1] = PXA2XX_PCMCIA(sysbus_create_simple(TYPE_PXA2XX_PCMCIA, + 0x30000000, NULL)); sysbus_create_simple(TYPE_PXA2XX_RTC, 0x40900000, qdev_get_gpio_in(s->pic, PXA2XX_PIC_RTCALARM)); diff --git a/hw/pcmcia/pxa2xx.c b/hw/pcmcia/pxa2xx.c index 3d512a292c..e3111fdf1a 100644 --- a/hw/pcmcia/pxa2xx.c +++ b/hw/pcmcia/pxa2xx.c @@ -138,16 +138,6 @@ static void pxa2xx_pcmcia_set_irq(void *opaque, int line, int level) qemu_set_irq(s->irq, level); } -PXA2xxPCMCIAState *pxa2xx_pcmcia_init(MemoryRegion *sysmem, - hwaddr base) -{ - DeviceState *dev; - - dev = sysbus_create_simple(TYPE_PXA2XX_PCMCIA, base, NULL); - - return PXA2XX_PCMCIA(dev); -} - static void pxa2xx_pcmcia_initfn(Object *obj) { SysBusDevice *sbd = SYS_BUS_DEVICE(obj); diff --git a/include/hw/arm/pxa.h b/include/hw/arm/pxa.h index 54eb895e42..4c6caee113 100644 --- a/include/hw/arm/pxa.h +++ b/include/hw/arm/pxa.h @@ -100,8 +100,6 @@ void pxa2xx_mmci_handlers(PXA2xxMMCIState *s, qemu_irq readonly, #define TYPE_PXA2XX_PCMCIA "pxa2xx-pcmcia" OBJECT_DECLARE_SIMPLE_TYPE(PXA2xxPCMCIAState, PXA2XX_PCMCIA) -PXA2xxPCMCIAState *pxa2xx_pcmcia_init(MemoryRegion *sysmem, - hwaddr base); int pxa2xx_pcmcia_attach(void *opaque, PCMCIACardState *card); int pxa2xx_pcmcia_detach(void *opaque); void pxa2xx_pcmcia_set_irq_cb(void *opaque, qemu_irq irq, qemu_irq cd_irq); From ead17ebf537c6bde0daa19d61a6913b595940e51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 20 Oct 2023 15:03:27 +0200 Subject: [PATCH 168/974] hw/intc/pxa2xx: Convert to Resettable interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Factor reset code out of the DeviceRealize() handler. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Message-id: 20231020130331.50048-7-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/pxa2xx_pic.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/hw/arm/pxa2xx_pic.c b/hw/arm/pxa2xx_pic.c index 47132ab982..2eb869a605 100644 --- a/hw/arm/pxa2xx_pic.c +++ b/hw/arm/pxa2xx_pic.c @@ -271,12 +271,9 @@ static int pxa2xx_pic_post_load(void *opaque, int version_id) return 0; } -DeviceState *pxa2xx_pic_init(hwaddr base, ARMCPU *cpu) +static void pxa2xx_pic_reset_hold(Object *obj) { - DeviceState *dev = qdev_new(TYPE_PXA2XX_PIC); - PXA2xxPICState *s = PXA2XX_PIC(dev); - - s->cpu = cpu; + PXA2xxPICState *s = PXA2XX_PIC(obj); s->int_pending[0] = 0; s->int_pending[1] = 0; @@ -284,6 +281,14 @@ DeviceState *pxa2xx_pic_init(hwaddr base, ARMCPU *cpu) s->int_enabled[1] = 0; s->is_fiq[0] = 0; s->is_fiq[1] = 0; +} + +DeviceState *pxa2xx_pic_init(hwaddr base, ARMCPU *cpu) +{ + DeviceState *dev = qdev_new(TYPE_PXA2XX_PIC); + PXA2xxPICState *s = PXA2XX_PIC(dev); + + s->cpu = cpu; sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); @@ -319,9 +324,11 @@ static const VMStateDescription vmstate_pxa2xx_pic_regs = { static void pxa2xx_pic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); dc->desc = "PXA2xx PIC"; dc->vmsd = &vmstate_pxa2xx_pic_regs; + rc->phases.hold = pxa2xx_pic_reset_hold; } static const TypeInfo pxa2xx_pic_info = { From ee5c9b5b19af0179d76b7afc8e7b00cedb0e615d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 20 Oct 2023 15:03:28 +0200 Subject: [PATCH 169/974] hw/intc/pxa2xx: Pass CPU reference using QOM link property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QOM objects shouldn't access each other internals fields except using the QOM API. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Message-id: 20231020130331.50048-8-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/pxa2xx_pic.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/hw/arm/pxa2xx_pic.c b/hw/arm/pxa2xx_pic.c index 2eb869a605..7e180635c2 100644 --- a/hw/arm/pxa2xx_pic.c +++ b/hw/arm/pxa2xx_pic.c @@ -15,6 +15,7 @@ #include "cpu.h" #include "hw/arm/pxa.h" #include "hw/sysbus.h" +#include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "qom/object.h" #include "target/arm/cpregs.h" @@ -288,7 +289,8 @@ DeviceState *pxa2xx_pic_init(hwaddr base, ARMCPU *cpu) DeviceState *dev = qdev_new(TYPE_PXA2XX_PIC); PXA2xxPICState *s = PXA2XX_PIC(dev); - s->cpu = cpu; + object_property_set_link(OBJECT(dev), "arm-cpu", + OBJECT(cpu), &error_abort); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); @@ -321,11 +323,18 @@ static const VMStateDescription vmstate_pxa2xx_pic_regs = { }, }; +static Property pxa2xx_pic_properties[] = { + DEFINE_PROP_LINK("arm-cpu", PXA2xxPICState, cpu, + TYPE_ARM_CPU, ARMCPU *), + DEFINE_PROP_END_OF_LIST(), +}; + static void pxa2xx_pic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); + device_class_set_props(dc, pxa2xx_pic_properties); dc->desc = "PXA2xx PIC"; dc->vmsd = &vmstate_pxa2xx_pic_regs; rc->phases.hold = pxa2xx_pic_reset_hold; From 25c4ff29d1688989ca6246e1a34e57505283ea2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 20 Oct 2023 15:03:29 +0200 Subject: [PATCH 170/974] hw/intc/pxa2xx: Factor pxa2xx_pic_realize() out of pxa2xx_pic_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Message-id: 20231020130331.50048-9-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/pxa2xx_pic.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/hw/arm/pxa2xx_pic.c b/hw/arm/pxa2xx_pic.c index 7e180635c2..1373a0d275 100644 --- a/hw/arm/pxa2xx_pic.c +++ b/hw/arm/pxa2xx_pic.c @@ -287,12 +287,18 @@ static void pxa2xx_pic_reset_hold(Object *obj) DeviceState *pxa2xx_pic_init(hwaddr base, ARMCPU *cpu) { DeviceState *dev = qdev_new(TYPE_PXA2XX_PIC); - PXA2xxPICState *s = PXA2XX_PIC(dev); object_property_set_link(OBJECT(dev), "arm-cpu", OBJECT(cpu), &error_abort); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + + return dev; +} + +static void pxa2xx_pic_realize(DeviceState *dev, Error **errp) +{ + PXA2xxPICState *s = PXA2XX_PIC(dev); qdev_init_gpio_in(dev, pxa2xx_pic_set_irq, PXA2XX_PIC_SRCS); @@ -300,12 +306,9 @@ DeviceState *pxa2xx_pic_init(hwaddr base, ARMCPU *cpu) memory_region_init_io(&s->iomem, OBJECT(s), &pxa2xx_pic_ops, s, "pxa2xx-pic", 0x00100000); sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); /* Enable IC coprocessor access. */ - define_arm_cp_regs_with_opaque(cpu, pxa_pic_cp_reginfo, s); - - return dev; + define_arm_cp_regs_with_opaque(s->cpu, pxa_pic_cp_reginfo, s); } static const VMStateDescription vmstate_pxa2xx_pic_regs = { @@ -335,6 +338,7 @@ static void pxa2xx_pic_class_init(ObjectClass *klass, void *data) ResettableClass *rc = RESETTABLE_CLASS(klass); device_class_set_props(dc, pxa2xx_pic_properties); + dc->realize = pxa2xx_pic_realize; dc->desc = "PXA2xx PIC"; dc->vmsd = &vmstate_pxa2xx_pic_regs; rc->phases.hold = pxa2xx_pic_reset_hold; From bf348bf9ab5459004927873c37e79f2040887d22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 20 Oct 2023 15:03:30 +0200 Subject: [PATCH 171/974] hw/arm/pxa2xx: Realize PXA2XX_I2C device before accessing it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qbus_new(), called in i2c_init_bus(), should not be called on unrealized device. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Message-id: 20231020130331.50048-10-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/pxa2xx.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c index 601ddd8766..f0bf407e66 100644 --- a/hw/arm/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -1513,14 +1513,15 @@ PXA2xxI2CState *pxa2xx_i2c_init(hwaddr base, qdev_prop_set_uint32(dev, "size", region_size + 1); qdev_prop_set_uint32(dev, "offset", base & region_size); + /* FIXME: Should the slave device really be on a separate bus? */ + i2cbus = i2c_init_bus(dev, "dummy"); + i2c_dev = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(i2c_dev, &error_fatal); sysbus_mmio_map(i2c_dev, 0, base & ~region_size); sysbus_connect_irq(i2c_dev, 0, irq); s = PXA2XX_I2C(i2c_dev); - /* FIXME: Should the slave device really be on a separate bus? */ - i2cbus = i2c_init_bus(dev, "dummy"); s->slave = PXA2XX_I2C_SLAVE(i2c_slave_create_simple(i2cbus, TYPE_PXA2XX_I2C_SLAVE, 0)); From f0109f721e8994deabd35b69d96d6d0bdfec0425 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 25 Oct 2023 08:59:09 +0200 Subject: [PATCH 172/974] hw/arm: Avoid using 'first_cpu' when first ARM CPU is reachable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prefer using a well known local first CPU rather than a global one. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20231025065909.57344-1-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/bananapi_m2u.c | 2 +- hw/arm/exynos4_boards.c | 7 ++++--- hw/arm/orangepi.c | 2 +- hw/arm/realview.c | 2 +- hw/arm/xilinx_zynq.c | 2 +- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/hw/arm/bananapi_m2u.c b/hw/arm/bananapi_m2u.c index a7c7a9f96d..8f24b18d8c 100644 --- a/hw/arm/bananapi_m2u.c +++ b/hw/arm/bananapi_m2u.c @@ -128,7 +128,7 @@ static void bpim2u_init(MachineState *machine) bpim2u_binfo.loader_start = r40->memmap[AW_R40_DEV_SDRAM]; bpim2u_binfo.ram_size = machine->ram_size; bpim2u_binfo.psci_conduit = QEMU_PSCI_CONDUIT_SMC; - arm_load_kernel(ARM_CPU(first_cpu), machine, &bpim2u_binfo); + arm_load_kernel(&r40->cpus[0], machine, &bpim2u_binfo); } static void bpim2u_machine_init(MachineClass *mc) diff --git a/hw/arm/exynos4_boards.c b/hw/arm/exynos4_boards.c index ef5bcbc212..b0e13eb4f0 100644 --- a/hw/arm/exynos4_boards.c +++ b/hw/arm/exynos4_boards.c @@ -134,9 +134,10 @@ exynos4_boards_init_common(MachineState *machine, static void nuri_init(MachineState *machine) { - exynos4_boards_init_common(machine, EXYNOS4_BOARD_NURI); + Exynos4BoardState *s = exynos4_boards_init_common(machine, + EXYNOS4_BOARD_NURI); - arm_load_kernel(ARM_CPU(first_cpu), machine, &exynos4_board_binfo); + arm_load_kernel(s->soc.cpu[0], machine, &exynos4_board_binfo); } static void smdkc210_init(MachineState *machine) @@ -146,7 +147,7 @@ static void smdkc210_init(MachineState *machine) lan9215_init(SMDK_LAN9118_BASE_ADDR, qemu_irq_invert(s->soc.irq_table[exynos4210_get_irq(37, 1)])); - arm_load_kernel(ARM_CPU(first_cpu), machine, &exynos4_board_binfo); + arm_load_kernel(s->soc.cpu[0], machine, &exynos4_board_binfo); } static void nuri_class_init(ObjectClass *oc, void *data) diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c index d0eca54cd9..f3784d45ca 100644 --- a/hw/arm/orangepi.c +++ b/hw/arm/orangepi.c @@ -106,7 +106,7 @@ static void orangepi_init(MachineState *machine) orangepi_binfo.loader_start = h3->memmap[AW_H3_DEV_SDRAM]; orangepi_binfo.ram_size = machine->ram_size; orangepi_binfo.psci_conduit = QEMU_PSCI_CONDUIT_SMC; - arm_load_kernel(ARM_CPU(first_cpu), machine, &orangepi_binfo); + arm_load_kernel(&h3->cpus[0], machine, &orangepi_binfo); } static void orangepi_machine_init(MachineClass *mc) diff --git a/hw/arm/realview.c b/hw/arm/realview.c index 8f89526596..132217b2ed 100644 --- a/hw/arm/realview.c +++ b/hw/arm/realview.c @@ -384,7 +384,7 @@ static void realview_init(MachineState *machine, realview_binfo.ram_size = ram_size; realview_binfo.board_id = realview_board_id[board_type]; realview_binfo.loader_start = (board_type == BOARD_PB_A8 ? 0x70000000 : 0); - arm_load_kernel(ARM_CPU(first_cpu), machine, &realview_binfo); + arm_load_kernel(cpu, machine, &realview_binfo); } static void realview_eb_init(MachineState *machine) diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index 8dc2ea83a9..dbb9793aa1 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -349,7 +349,7 @@ static void zynq_init(MachineState *machine) zynq_binfo.board_setup_addr = BOARD_SETUP_ADDR; zynq_binfo.write_board_setup = zynq_write_board_setup; - arm_load_kernel(ARM_CPU(first_cpu), machine, &zynq_binfo); + arm_load_kernel(cpu, machine, &zynq_binfo); } static void zynq_machine_class_init(ObjectClass *oc, void *data) From 6f83dc67168d17856744275e2a0d7a6addf6cfb9 Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Tue, 24 Oct 2023 14:19:45 -0500 Subject: [PATCH 173/974] misc/led: LED state is set opposite of what is expected MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Testing of the LED state showed that when the LED polarity was set to GPIO_POLARITY_ACTIVE_LOW and a low logic value was set on the input GPIO of the LED, the LED was being turn off when it was expected to be turned on. Fixes: ddb67f6402 ("hw/misc/led: Allow connecting from GPIO output") Signed-off-by: Glenn Miles Reviewed-by: Peter Maydell Reviewed-by: Andrew Jeffery Message-id: 20231024191945.4135036-1-milesg@linux.vnet.ibm.com Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- hw/misc/led.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/misc/led.c b/hw/misc/led.c index f6d6d68bce..42bb43a39a 100644 --- a/hw/misc/led.c +++ b/hw/misc/led.c @@ -63,7 +63,7 @@ static void led_set_state_gpio_handler(void *opaque, int line, int new_state) LEDState *s = LED(opaque); assert(line == 0); - led_set_state(s, !!new_state != s->gpio_active_high); + led_set_state(s, !!new_state == s->gpio_active_high); } static void led_reset(DeviceState *dev) From c755c943aa2e24df8eb135d3080c8097d1d5d40f Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Tue, 17 Oct 2023 21:44:12 +0200 Subject: [PATCH 174/974] hw/net/cadence_gem: use REG32 macro for register definitions Replace register defines with the REG32 macro from registerfields.h in the Cadence GEM device. Signed-off-by: Luc Michel Reviewed-by: sai.pavan.boddu@amd.com Message-id: 20231017194422.4124691-2-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/net/cadence_gem.c | 527 +++++++++++++++++++++---------------------- 1 file changed, 261 insertions(+), 266 deletions(-) diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index 37e209cda6..bea2224dd8 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -28,6 +28,7 @@ #include "hw/irq.h" #include "hw/net/cadence_gem.h" #include "hw/qdev-properties.h" +#include "hw/registerfields.h" #include "migration/vmstate.h" #include "qapi/error.h" #include "qemu/log.h" @@ -44,136 +45,131 @@ } \ } while (0) -#define GEM_NWCTRL (0x00000000 / 4) /* Network Control reg */ -#define GEM_NWCFG (0x00000004 / 4) /* Network Config reg */ -#define GEM_NWSTATUS (0x00000008 / 4) /* Network Status reg */ -#define GEM_USERIO (0x0000000C / 4) /* User IO reg */ -#define GEM_DMACFG (0x00000010 / 4) /* DMA Control reg */ -#define GEM_TXSTATUS (0x00000014 / 4) /* TX Status reg */ -#define GEM_RXQBASE (0x00000018 / 4) /* RX Q Base address reg */ -#define GEM_TXQBASE (0x0000001C / 4) /* TX Q Base address reg */ -#define GEM_RXSTATUS (0x00000020 / 4) /* RX Status reg */ -#define GEM_ISR (0x00000024 / 4) /* Interrupt Status reg */ -#define GEM_IER (0x00000028 / 4) /* Interrupt Enable reg */ -#define GEM_IDR (0x0000002C / 4) /* Interrupt Disable reg */ -#define GEM_IMR (0x00000030 / 4) /* Interrupt Mask reg */ -#define GEM_PHYMNTNC (0x00000034 / 4) /* Phy Maintenance reg */ -#define GEM_RXPAUSE (0x00000038 / 4) /* RX Pause Time reg */ -#define GEM_TXPAUSE (0x0000003C / 4) /* TX Pause Time reg */ -#define GEM_TXPARTIALSF (0x00000040 / 4) /* TX Partial Store and Forward */ -#define GEM_RXPARTIALSF (0x00000044 / 4) /* RX Partial Store and Forward */ -#define GEM_JUMBO_MAX_LEN (0x00000048 / 4) /* Max Jumbo Frame Size */ -#define GEM_HASHLO (0x00000080 / 4) /* Hash Low address reg */ -#define GEM_HASHHI (0x00000084 / 4) /* Hash High address reg */ -#define GEM_SPADDR1LO (0x00000088 / 4) /* Specific addr 1 low reg */ -#define GEM_SPADDR1HI (0x0000008C / 4) /* Specific addr 1 high reg */ -#define GEM_SPADDR2LO (0x00000090 / 4) /* Specific addr 2 low reg */ -#define GEM_SPADDR2HI (0x00000094 / 4) /* Specific addr 2 high reg */ -#define GEM_SPADDR3LO (0x00000098 / 4) /* Specific addr 3 low reg */ -#define GEM_SPADDR3HI (0x0000009C / 4) /* Specific addr 3 high reg */ -#define GEM_SPADDR4LO (0x000000A0 / 4) /* Specific addr 4 low reg */ -#define GEM_SPADDR4HI (0x000000A4 / 4) /* Specific addr 4 high reg */ -#define GEM_TIDMATCH1 (0x000000A8 / 4) /* Type ID1 Match reg */ -#define GEM_TIDMATCH2 (0x000000AC / 4) /* Type ID2 Match reg */ -#define GEM_TIDMATCH3 (0x000000B0 / 4) /* Type ID3 Match reg */ -#define GEM_TIDMATCH4 (0x000000B4 / 4) /* Type ID4 Match reg */ -#define GEM_WOLAN (0x000000B8 / 4) /* Wake on LAN reg */ -#define GEM_IPGSTRETCH (0x000000BC / 4) /* IPG Stretch reg */ -#define GEM_SVLAN (0x000000C0 / 4) /* Stacked VLAN reg */ -#define GEM_MODID (0x000000FC / 4) /* Module ID reg */ -#define GEM_OCTTXLO (0x00000100 / 4) /* Octets transmitted Low reg */ -#define GEM_OCTTXHI (0x00000104 / 4) /* Octets transmitted High reg */ -#define GEM_TXCNT (0x00000108 / 4) /* Error-free Frames transmitted */ -#define GEM_TXBCNT (0x0000010C / 4) /* Error-free Broadcast Frames */ -#define GEM_TXMCNT (0x00000110 / 4) /* Error-free Multicast Frame */ -#define GEM_TXPAUSECNT (0x00000114 / 4) /* Pause Frames Transmitted */ -#define GEM_TX64CNT (0x00000118 / 4) /* Error-free 64 TX */ -#define GEM_TX65CNT (0x0000011C / 4) /* Error-free 65-127 TX */ -#define GEM_TX128CNT (0x00000120 / 4) /* Error-free 128-255 TX */ -#define GEM_TX256CNT (0x00000124 / 4) /* Error-free 256-511 */ -#define GEM_TX512CNT (0x00000128 / 4) /* Error-free 512-1023 TX */ -#define GEM_TX1024CNT (0x0000012C / 4) /* Error-free 1024-1518 TX */ -#define GEM_TX1519CNT (0x00000130 / 4) /* Error-free larger than 1519 TX */ -#define GEM_TXURUNCNT (0x00000134 / 4) /* TX under run error counter */ -#define GEM_SINGLECOLLCNT (0x00000138 / 4) /* Single Collision Frames */ -#define GEM_MULTCOLLCNT (0x0000013C / 4) /* Multiple Collision Frames */ -#define GEM_EXCESSCOLLCNT (0x00000140 / 4) /* Excessive Collision Frames */ -#define GEM_LATECOLLCNT (0x00000144 / 4) /* Late Collision Frames */ -#define GEM_DEFERTXCNT (0x00000148 / 4) /* Deferred Transmission Frames */ -#define GEM_CSENSECNT (0x0000014C / 4) /* Carrier Sense Error Counter */ -#define GEM_OCTRXLO (0x00000150 / 4) /* Octets Received register Low */ -#define GEM_OCTRXHI (0x00000154 / 4) /* Octets Received register High */ -#define GEM_RXCNT (0x00000158 / 4) /* Error-free Frames Received */ -#define GEM_RXBROADCNT (0x0000015C / 4) /* Error-free Broadcast Frames RX */ -#define GEM_RXMULTICNT (0x00000160 / 4) /* Error-free Multicast Frames RX */ -#define GEM_RXPAUSECNT (0x00000164 / 4) /* Pause Frames Received Counter */ -#define GEM_RX64CNT (0x00000168 / 4) /* Error-free 64 byte Frames RX */ -#define GEM_RX65CNT (0x0000016C / 4) /* Error-free 65-127B Frames RX */ -#define GEM_RX128CNT (0x00000170 / 4) /* Error-free 128-255B Frames RX */ -#define GEM_RX256CNT (0x00000174 / 4) /* Error-free 256-512B Frames RX */ -#define GEM_RX512CNT (0x00000178 / 4) /* Error-free 512-1023B Frames RX */ -#define GEM_RX1024CNT (0x0000017C / 4) /* Error-free 1024-1518B Frames RX */ -#define GEM_RX1519CNT (0x00000180 / 4) /* Error-free 1519-max Frames RX */ -#define GEM_RXUNDERCNT (0x00000184 / 4) /* Undersize Frames Received */ -#define GEM_RXOVERCNT (0x00000188 / 4) /* Oversize Frames Received */ -#define GEM_RXJABCNT (0x0000018C / 4) /* Jabbers Received Counter */ -#define GEM_RXFCSCNT (0x00000190 / 4) /* Frame Check seq. Error Counter */ -#define GEM_RXLENERRCNT (0x00000194 / 4) /* Length Field Error Counter */ -#define GEM_RXSYMERRCNT (0x00000198 / 4) /* Symbol Error Counter */ -#define GEM_RXALIGNERRCNT (0x0000019C / 4) /* Alignment Error Counter */ -#define GEM_RXRSCERRCNT (0x000001A0 / 4) /* Receive Resource Error Counter */ -#define GEM_RXORUNCNT (0x000001A4 / 4) /* Receive Overrun Counter */ -#define GEM_RXIPCSERRCNT (0x000001A8 / 4) /* IP header Checksum Err Counter */ -#define GEM_RXTCPCCNT (0x000001AC / 4) /* TCP Checksum Error Counter */ -#define GEM_RXUDPCCNT (0x000001B0 / 4) /* UDP Checksum Error Counter */ +REG32(NWCTRL, 0x0) /* Network Control reg */ +REG32(NWCFG, 0x4) /* Network Config reg */ +REG32(NWSTATUS, 0x8) /* Network Status reg */ +REG32(USERIO, 0xc) /* User IO reg */ +REG32(DMACFG, 0x10) /* DMA Control reg */ +REG32(TXSTATUS, 0x14) /* TX Status reg */ +REG32(RXQBASE, 0x18) /* RX Q Base address reg */ +REG32(TXQBASE, 0x1c) /* TX Q Base address reg */ +REG32(RXSTATUS, 0x20) /* RX Status reg */ +REG32(ISR, 0x24) /* Interrupt Status reg */ +REG32(IER, 0x28) /* Interrupt Enable reg */ +REG32(IDR, 0x2c) /* Interrupt Disable reg */ +REG32(IMR, 0x30) /* Interrupt Mask reg */ +REG32(PHYMNTNC, 0x34) /* Phy Maintenance reg */ +REG32(RXPAUSE, 0x38) /* RX Pause Time reg */ +REG32(TXPAUSE, 0x3c) /* TX Pause Time reg */ +REG32(TXPARTIALSF, 0x40) /* TX Partial Store and Forward */ +REG32(RXPARTIALSF, 0x44) /* RX Partial Store and Forward */ +REG32(JUMBO_MAX_LEN, 0x48) /* Max Jumbo Frame Size */ +REG32(HASHLO, 0x80) /* Hash Low address reg */ +REG32(HASHHI, 0x84) /* Hash High address reg */ +REG32(SPADDR1LO, 0x88) /* Specific addr 1 low reg */ +REG32(SPADDR1HI, 0x8c) /* Specific addr 1 high reg */ +REG32(SPADDR2LO, 0x90) /* Specific addr 2 low reg */ +REG32(SPADDR2HI, 0x94) /* Specific addr 2 high reg */ +REG32(SPADDR3LO, 0x98) /* Specific addr 3 low reg */ +REG32(SPADDR3HI, 0x9c) /* Specific addr 3 high reg */ +REG32(SPADDR4LO, 0xa0) /* Specific addr 4 low reg */ +REG32(SPADDR4HI, 0xa4) /* Specific addr 4 high reg */ +REG32(TIDMATCH1, 0xa8) /* Type ID1 Match reg */ +REG32(TIDMATCH2, 0xac) /* Type ID2 Match reg */ +REG32(TIDMATCH3, 0xb0) /* Type ID3 Match reg */ +REG32(TIDMATCH4, 0xb4) /* Type ID4 Match reg */ +REG32(WOLAN, 0xb8) /* Wake on LAN reg */ +REG32(IPGSTRETCH, 0xbc) /* IPG Stretch reg */ +REG32(SVLAN, 0xc0) /* Stacked VLAN reg */ +REG32(MODID, 0xfc) /* Module ID reg */ +REG32(OCTTXLO, 0x100) /* Octects transmitted Low reg */ +REG32(OCTTXHI, 0x104) /* Octects transmitted High reg */ +REG32(TXCNT, 0x108) /* Error-free Frames transmitted */ +REG32(TXBCNT, 0x10c) /* Error-free Broadcast Frames */ +REG32(TXMCNT, 0x110) /* Error-free Multicast Frame */ +REG32(TXPAUSECNT, 0x114) /* Pause Frames Transmitted */ +REG32(TX64CNT, 0x118) /* Error-free 64 TX */ +REG32(TX65CNT, 0x11c) /* Error-free 65-127 TX */ +REG32(TX128CNT, 0x120) /* Error-free 128-255 TX */ +REG32(TX256CNT, 0x124) /* Error-free 256-511 */ +REG32(TX512CNT, 0x128) /* Error-free 512-1023 TX */ +REG32(TX1024CNT, 0x12c) /* Error-free 1024-1518 TX */ +REG32(TX1519CNT, 0x130) /* Error-free larger than 1519 TX */ +REG32(TXURUNCNT, 0x134) /* TX under run error counter */ +REG32(SINGLECOLLCNT, 0x138) /* Single Collision Frames */ +REG32(MULTCOLLCNT, 0x13c) /* Multiple Collision Frames */ +REG32(EXCESSCOLLCNT, 0x140) /* Excessive Collision Frames */ +REG32(LATECOLLCNT, 0x144) /* Late Collision Frames */ +REG32(DEFERTXCNT, 0x148) /* Deferred Transmission Frames */ +REG32(CSENSECNT, 0x14c) /* Carrier Sense Error Counter */ +REG32(OCTRXLO, 0x150) /* Octects Received register Low */ +REG32(OCTRXHI, 0x154) /* Octects Received register High */ +REG32(RXCNT, 0x158) /* Error-free Frames Received */ +REG32(RXBROADCNT, 0x15c) /* Error-free Broadcast Frames RX */ +REG32(RXMULTICNT, 0x160) /* Error-free Multicast Frames RX */ +REG32(RXPAUSECNT, 0x164) /* Pause Frames Received Counter */ +REG32(RX64CNT, 0x168) /* Error-free 64 byte Frames RX */ +REG32(RX65CNT, 0x16c) /* Error-free 65-127B Frames RX */ +REG32(RX128CNT, 0x170) /* Error-free 128-255B Frames RX */ +REG32(RX256CNT, 0x174) /* Error-free 256-512B Frames RX */ +REG32(RX512CNT, 0x178) /* Error-free 512-1023B Frames RX */ +REG32(RX1024CNT, 0x17c) /* Error-free 1024-1518B Frames RX */ +REG32(RX1519CNT, 0x180) /* Error-free 1519-max Frames RX */ +REG32(RXUNDERCNT, 0x184) /* Undersize Frames Received */ +REG32(RXOVERCNT, 0x188) /* Oversize Frames Received */ +REG32(RXJABCNT, 0x18c) /* Jabbers Received Counter */ +REG32(RXFCSCNT, 0x190) /* Frame Check seq. Error Counter */ +REG32(RXLENERRCNT, 0x194) /* Length Field Error Counter */ +REG32(RXSYMERRCNT, 0x198) /* Symbol Error Counter */ +REG32(RXALIGNERRCNT, 0x19c) /* Alignment Error Counter */ +REG32(RXRSCERRCNT, 0x1a0) /* Receive Resource Error Counter */ +REG32(RXORUNCNT, 0x1a4) /* Receive Overrun Counter */ +REG32(RXIPCSERRCNT, 0x1a8) /* IP header Checksum Err Counter */ +REG32(RXTCPCCNT, 0x1ac) /* TCP Checksum Error Counter */ +REG32(RXUDPCCNT, 0x1b0) /* UDP Checksum Error Counter */ -#define GEM_1588S (0x000001D0 / 4) /* 1588 Timer Seconds */ -#define GEM_1588NS (0x000001D4 / 4) /* 1588 Timer Nanoseconds */ -#define GEM_1588ADJ (0x000001D8 / 4) /* 1588 Timer Adjust */ -#define GEM_1588INC (0x000001DC / 4) /* 1588 Timer Increment */ -#define GEM_PTPETXS (0x000001E0 / 4) /* PTP Event Frame Transmitted (s) */ -#define GEM_PTPETXNS (0x000001E4 / 4) /* - * PTP Event Frame Transmitted (ns) - */ -#define GEM_PTPERXS (0x000001E8 / 4) /* PTP Event Frame Received (s) */ -#define GEM_PTPERXNS (0x000001EC / 4) /* PTP Event Frame Received (ns) */ -#define GEM_PTPPTXS (0x000001E0 / 4) /* PTP Peer Frame Transmitted (s) */ -#define GEM_PTPPTXNS (0x000001E4 / 4) /* PTP Peer Frame Transmitted (ns) */ -#define GEM_PTPPRXS (0x000001E8 / 4) /* PTP Peer Frame Received (s) */ -#define GEM_PTPPRXNS (0x000001EC / 4) /* PTP Peer Frame Received (ns) */ +REG32(1588S, 0x1d0) /* 1588 Timer Seconds */ +REG32(1588NS, 0x1d4) /* 1588 Timer Nanoseconds */ +REG32(1588ADJ, 0x1d8) /* 1588 Timer Adjust */ +REG32(1588INC, 0x1dc) /* 1588 Timer Increment */ +REG32(PTPETXS, 0x1e0) /* PTP Event Frame Transmitted (s) */ +REG32(PTPETXNS, 0x1e4) /* PTP Event Frame Transmitted (ns) */ +REG32(PTPERXS, 0x1e8) /* PTP Event Frame Received (s) */ +REG32(PTPERXNS, 0x1ec) /* PTP Event Frame Received (ns) */ +REG32(PTPPTXS, 0x1e0) /* PTP Peer Frame Transmitted (s) */ +REG32(PTPPTXNS, 0x1e4) /* PTP Peer Frame Transmitted (ns) */ +REG32(PTPPRXS, 0x1e8) /* PTP Peer Frame Received (s) */ +REG32(PTPPRXNS, 0x1ec) /* PTP Peer Frame Received (ns) */ /* Design Configuration Registers */ -#define GEM_DESCONF (0x00000280 / 4) -#define GEM_DESCONF2 (0x00000284 / 4) -#define GEM_DESCONF3 (0x00000288 / 4) -#define GEM_DESCONF4 (0x0000028C / 4) -#define GEM_DESCONF5 (0x00000290 / 4) -#define GEM_DESCONF6 (0x00000294 / 4) +REG32(DESCONF, 0x280) +REG32(DESCONF2, 0x284) +REG32(DESCONF3, 0x288) +REG32(DESCONF4, 0x28c) +REG32(DESCONF5, 0x290) +REG32(DESCONF6, 0x294) #define GEM_DESCONF6_64B_MASK (1U << 23) -#define GEM_DESCONF7 (0x00000298 / 4) +REG32(DESCONF7, 0x298) -#define GEM_INT_Q1_STATUS (0x00000400 / 4) -#define GEM_INT_Q1_MASK (0x00000640 / 4) +REG32(INT_Q1_STATUS, 0x400) +REG32(INT_Q1_MASK, 0x640) -#define GEM_TRANSMIT_Q1_PTR (0x00000440 / 4) -#define GEM_TRANSMIT_Q7_PTR (GEM_TRANSMIT_Q1_PTR + 6) +REG32(TRANSMIT_Q1_PTR, 0x440) +REG32(TRANSMIT_Q7_PTR, 0x458) -#define GEM_RECEIVE_Q1_PTR (0x00000480 / 4) -#define GEM_RECEIVE_Q7_PTR (GEM_RECEIVE_Q1_PTR + 6) +REG32(RECEIVE_Q1_PTR, 0x480) +REG32(RECEIVE_Q7_PTR, 0x498) -#define GEM_TBQPH (0x000004C8 / 4) -#define GEM_RBQPH (0x000004D4 / 4) +REG32(TBQPH, 0x4c8) +REG32(RBQPH, 0x4d4) -#define GEM_INT_Q1_ENABLE (0x00000600 / 4) -#define GEM_INT_Q7_ENABLE (GEM_INT_Q1_ENABLE + 6) +REG32(INT_Q1_ENABLE, 0x600) +REG32(INT_Q7_ENABLE, 0x618) -#define GEM_INT_Q1_DISABLE (0x00000620 / 4) -#define GEM_INT_Q7_DISABLE (GEM_INT_Q1_DISABLE + 6) +REG32(INT_Q1_DISABLE, 0x620) +REG32(INT_Q7_DISABLE, 0x638) -#define GEM_INT_Q1_MASK (0x00000640 / 4) -#define GEM_INT_Q7_MASK (GEM_INT_Q1_MASK + 6) - -#define GEM_SCREENING_TYPE1_REGISTER_0 (0x00000500 / 4) +REG32(SCREENING_TYPE1_REG0, 0x500) #define GEM_ST1R_UDP_PORT_MATCH_ENABLE (1 << 29) #define GEM_ST1R_DSTC_ENABLE (1 << 28) @@ -184,7 +180,7 @@ #define GEM_ST1R_QUEUE_SHIFT (0) #define GEM_ST1R_QUEUE_WIDTH (3 - GEM_ST1R_QUEUE_SHIFT + 1) -#define GEM_SCREENING_TYPE2_REGISTER_0 (0x00000540 / 4) +REG32(SCREENING_TYPE2_REG0, 0x540) #define GEM_ST2R_COMPARE_A_ENABLE (1 << 18) #define GEM_ST2R_COMPARE_A_SHIFT (13) @@ -196,8 +192,8 @@ #define GEM_ST2R_QUEUE_SHIFT (0) #define GEM_ST2R_QUEUE_WIDTH (3 - GEM_ST2R_QUEUE_SHIFT + 1) -#define GEM_SCREENING_TYPE2_ETHERTYPE_REG_0 (0x000006e0 / 4) -#define GEM_TYPE2_COMPARE_0_WORD_0 (0x00000700 / 4) +REG32(SCREENING_TYPE2_ETHERTYPE_REG0, 0x6e0) +REG32(TYPE2_COMPARE_0_WORD_0, 0x700) #define GEM_T2CW1_COMPARE_OFFSET_SHIFT (7) #define GEM_T2CW1_COMPARE_OFFSET_WIDTH (8 - GEM_T2CW1_COMPARE_OFFSET_SHIFT + 1) @@ -325,7 +321,7 @@ static inline uint64_t tx_desc_get_buffer(CadenceGEMState *s, uint32_t *desc) { uint64_t ret = desc[0]; - if (s->regs[GEM_DMACFG] & GEM_DMACFG_ADDR_64B) { + if (s->regs[R_DMACFG] & GEM_DMACFG_ADDR_64B) { ret |= (uint64_t)desc[2] << 32; } return ret; @@ -370,7 +366,7 @@ static inline uint64_t rx_desc_get_buffer(CadenceGEMState *s, uint32_t *desc) { uint64_t ret = desc[0] & ~0x3UL; - if (s->regs[GEM_DMACFG] & GEM_DMACFG_ADDR_64B) { + if (s->regs[R_DMACFG] & GEM_DMACFG_ADDR_64B) { ret |= (uint64_t)desc[2] << 32; } return ret; @@ -380,10 +376,10 @@ static inline int gem_get_desc_len(CadenceGEMState *s, bool rx_n_tx) { int ret = 2; - if (s->regs[GEM_DMACFG] & GEM_DMACFG_ADDR_64B) { + if (s->regs[R_DMACFG] & GEM_DMACFG_ADDR_64B) { ret += 2; } - if (s->regs[GEM_DMACFG] & (rx_n_tx ? GEM_DMACFG_RX_BD_EXT + if (s->regs[R_DMACFG] & (rx_n_tx ? GEM_DMACFG_RX_BD_EXT : GEM_DMACFG_TX_BD_EXT)) { ret += 2; } @@ -456,8 +452,8 @@ static const uint8_t broadcast_addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; static uint32_t gem_get_max_buf_len(CadenceGEMState *s, bool tx) { uint32_t size; - if (s->regs[GEM_NWCFG] & GEM_NWCFG_JUMBO_FRAME) { - size = s->regs[GEM_JUMBO_MAX_LEN]; + if (s->regs[R_NWCFG] & GEM_NWCFG_JUMBO_FRAME) { + size = s->regs[R_JUMBO_MAX_LEN]; if (size > s->jumbo_max_len) { size = s->jumbo_max_len; qemu_log_mask(LOG_GUEST_ERROR, "GEM_JUMBO_MAX_LEN reg cannot be" @@ -466,7 +462,7 @@ static uint32_t gem_get_max_buf_len(CadenceGEMState *s, bool tx) } else if (tx) { size = 1518; } else { - size = s->regs[GEM_NWCFG] & GEM_NWCFG_RCV_1538 ? 1538 : 1518; + size = s->regs[R_NWCFG] & GEM_NWCFG_RCV_1538 ? 1538 : 1518; } return size; } @@ -474,10 +470,10 @@ static uint32_t gem_get_max_buf_len(CadenceGEMState *s, bool tx) static void gem_set_isr(CadenceGEMState *s, int q, uint32_t flag) { if (q == 0) { - s->regs[GEM_ISR] |= flag & ~(s->regs[GEM_IMR]); + s->regs[R_ISR] |= flag & ~(s->regs[R_IMR]); } else { - s->regs[GEM_INT_Q1_STATUS + q - 1] |= flag & - ~(s->regs[GEM_INT_Q1_MASK + q - 1]); + s->regs[R_INT_Q1_STATUS + q - 1] |= flag & + ~(s->regs[R_INT_Q1_MASK + q - 1]); } } @@ -491,43 +487,43 @@ static void gem_init_register_masks(CadenceGEMState *s) unsigned int i; /* Mask of register bits which are read only */ memset(&s->regs_ro[0], 0, sizeof(s->regs_ro)); - s->regs_ro[GEM_NWCTRL] = 0xFFF80000; - s->regs_ro[GEM_NWSTATUS] = 0xFFFFFFFF; - s->regs_ro[GEM_DMACFG] = 0x8E00F000; - s->regs_ro[GEM_TXSTATUS] = 0xFFFFFE08; - s->regs_ro[GEM_RXQBASE] = 0x00000003; - s->regs_ro[GEM_TXQBASE] = 0x00000003; - s->regs_ro[GEM_RXSTATUS] = 0xFFFFFFF0; - s->regs_ro[GEM_ISR] = 0xFFFFFFFF; - s->regs_ro[GEM_IMR] = 0xFFFFFFFF; - s->regs_ro[GEM_MODID] = 0xFFFFFFFF; + s->regs_ro[R_NWCTRL] = 0xFFF80000; + s->regs_ro[R_NWSTATUS] = 0xFFFFFFFF; + s->regs_ro[R_DMACFG] = 0x8E00F000; + s->regs_ro[R_TXSTATUS] = 0xFFFFFE08; + s->regs_ro[R_RXQBASE] = 0x00000003; + s->regs_ro[R_TXQBASE] = 0x00000003; + s->regs_ro[R_RXSTATUS] = 0xFFFFFFF0; + s->regs_ro[R_ISR] = 0xFFFFFFFF; + s->regs_ro[R_IMR] = 0xFFFFFFFF; + s->regs_ro[R_MODID] = 0xFFFFFFFF; for (i = 0; i < s->num_priority_queues; i++) { - s->regs_ro[GEM_INT_Q1_STATUS + i] = 0xFFFFFFFF; - s->regs_ro[GEM_INT_Q1_ENABLE + i] = 0xFFFFF319; - s->regs_ro[GEM_INT_Q1_DISABLE + i] = 0xFFFFF319; - s->regs_ro[GEM_INT_Q1_MASK + i] = 0xFFFFFFFF; + s->regs_ro[R_INT_Q1_STATUS + i] = 0xFFFFFFFF; + s->regs_ro[R_INT_Q1_ENABLE + i] = 0xFFFFF319; + s->regs_ro[R_INT_Q1_DISABLE + i] = 0xFFFFF319; + s->regs_ro[R_INT_Q1_MASK + i] = 0xFFFFFFFF; } /* Mask of register bits which are clear on read */ memset(&s->regs_rtc[0], 0, sizeof(s->regs_rtc)); - s->regs_rtc[GEM_ISR] = 0xFFFFFFFF; + s->regs_rtc[R_ISR] = 0xFFFFFFFF; for (i = 0; i < s->num_priority_queues; i++) { - s->regs_rtc[GEM_INT_Q1_STATUS + i] = 0x00000CE6; + s->regs_rtc[R_INT_Q1_STATUS + i] = 0x00000CE6; } /* Mask of register bits which are write 1 to clear */ memset(&s->regs_w1c[0], 0, sizeof(s->regs_w1c)); - s->regs_w1c[GEM_TXSTATUS] = 0x000001F7; - s->regs_w1c[GEM_RXSTATUS] = 0x0000000F; + s->regs_w1c[R_TXSTATUS] = 0x000001F7; + s->regs_w1c[R_RXSTATUS] = 0x0000000F; /* Mask of register bits which are write only */ memset(&s->regs_wo[0], 0, sizeof(s->regs_wo)); - s->regs_wo[GEM_NWCTRL] = 0x00073E60; - s->regs_wo[GEM_IER] = 0x07FFFFFF; - s->regs_wo[GEM_IDR] = 0x07FFFFFF; + s->regs_wo[R_NWCTRL] = 0x00073E60; + s->regs_wo[R_IER] = 0x07FFFFFF; + s->regs_wo[R_IDR] = 0x07FFFFFF; for (i = 0; i < s->num_priority_queues; i++) { - s->regs_wo[GEM_INT_Q1_ENABLE + i] = 0x00000CE6; - s->regs_wo[GEM_INT_Q1_DISABLE + i] = 0x00000CE6; + s->regs_wo[R_INT_Q1_ENABLE + i] = 0x00000CE6; + s->regs_wo[R_INT_Q1_DISABLE + i] = 0x00000CE6; } } @@ -561,7 +557,7 @@ static bool gem_can_receive(NetClientState *nc) s = qemu_get_nic_opaque(nc); /* Do nothing if receive is not enabled. */ - if (!(s->regs[GEM_NWCTRL] & GEM_NWCTRL_RXENA)) { + if (!(s->regs[R_NWCTRL] & GEM_NWCTRL_RXENA)) { if (s->can_rx_state != 1) { s->can_rx_state = 1; DB_PRINT("can't receive - no enable\n"); @@ -598,10 +594,10 @@ static void gem_update_int_status(CadenceGEMState *s) { int i; - qemu_set_irq(s->irq[0], !!s->regs[GEM_ISR]); + qemu_set_irq(s->irq[0], !!s->regs[R_ISR]); for (i = 1; i < s->num_priority_queues; ++i) { - qemu_set_irq(s->irq[i], !!s->regs[GEM_INT_Q1_STATUS + i - 1]); + qemu_set_irq(s->irq[i], !!s->regs[R_INT_Q1_STATUS + i - 1]); } } @@ -615,39 +611,39 @@ static void gem_receive_updatestats(CadenceGEMState *s, const uint8_t *packet, uint64_t octets; /* Total octets (bytes) received */ - octets = ((uint64_t)(s->regs[GEM_OCTRXLO]) << 32) | - s->regs[GEM_OCTRXHI]; + octets = ((uint64_t)(s->regs[R_OCTRXLO]) << 32) | + s->regs[R_OCTRXHI]; octets += bytes; - s->regs[GEM_OCTRXLO] = octets >> 32; - s->regs[GEM_OCTRXHI] = octets; + s->regs[R_OCTRXLO] = octets >> 32; + s->regs[R_OCTRXHI] = octets; /* Error-free Frames received */ - s->regs[GEM_RXCNT]++; + s->regs[R_RXCNT]++; /* Error-free Broadcast Frames counter */ if (!memcmp(packet, broadcast_addr, 6)) { - s->regs[GEM_RXBROADCNT]++; + s->regs[R_RXBROADCNT]++; } /* Error-free Multicast Frames counter */ if (packet[0] == 0x01) { - s->regs[GEM_RXMULTICNT]++; + s->regs[R_RXMULTICNT]++; } if (bytes <= 64) { - s->regs[GEM_RX64CNT]++; + s->regs[R_RX64CNT]++; } else if (bytes <= 127) { - s->regs[GEM_RX65CNT]++; + s->regs[R_RX65CNT]++; } else if (bytes <= 255) { - s->regs[GEM_RX128CNT]++; + s->regs[R_RX128CNT]++; } else if (bytes <= 511) { - s->regs[GEM_RX256CNT]++; + s->regs[R_RX256CNT]++; } else if (bytes <= 1023) { - s->regs[GEM_RX512CNT]++; + s->regs[R_RX512CNT]++; } else if (bytes <= 1518) { - s->regs[GEM_RX1024CNT]++; + s->regs[R_RX1024CNT]++; } else { - s->regs[GEM_RX1519CNT]++; + s->regs[R_RX1519CNT]++; } } @@ -706,13 +702,13 @@ static int gem_mac_address_filter(CadenceGEMState *s, const uint8_t *packet) int i, is_mc; /* Promiscuous mode? */ - if (s->regs[GEM_NWCFG] & GEM_NWCFG_PROMISC) { + if (s->regs[R_NWCFG] & GEM_NWCFG_PROMISC) { return GEM_RX_PROMISCUOUS_ACCEPT; } if (!memcmp(packet, broadcast_addr, 6)) { /* Reject broadcast packets? */ - if (s->regs[GEM_NWCFG] & GEM_NWCFG_BCAST_REJ) { + if (s->regs[R_NWCFG] & GEM_NWCFG_BCAST_REJ) { return GEM_RX_REJECT; } return GEM_RX_BROADCAST_ACCEPT; @@ -720,13 +716,13 @@ static int gem_mac_address_filter(CadenceGEMState *s, const uint8_t *packet) /* Accept packets -w- hash match? */ is_mc = is_multicast_ether_addr(packet); - if ((is_mc && (s->regs[GEM_NWCFG] & GEM_NWCFG_MCAST_HASH)) || - (!is_mc && (s->regs[GEM_NWCFG] & GEM_NWCFG_UCAST_HASH))) { + if ((is_mc && (s->regs[R_NWCFG] & GEM_NWCFG_MCAST_HASH)) || + (!is_mc && (s->regs[R_NWCFG] & GEM_NWCFG_UCAST_HASH))) { uint64_t buckets; unsigned hash_index; hash_index = calc_mac_hash(packet); - buckets = ((uint64_t)s->regs[GEM_HASHHI] << 32) | s->regs[GEM_HASHLO]; + buckets = ((uint64_t)s->regs[R_HASHHI] << 32) | s->regs[R_HASHLO]; if ((buckets >> hash_index) & 1) { return is_mc ? GEM_RX_MULTICAST_HASH_ACCEPT : GEM_RX_UNICAST_HASH_ACCEPT; @@ -734,7 +730,7 @@ static int gem_mac_address_filter(CadenceGEMState *s, const uint8_t *packet) } /* Check all 4 specific addresses */ - gem_spaddr = (uint8_t *)&(s->regs[GEM_SPADDR1LO]); + gem_spaddr = (uint8_t *)&(s->regs[R_SPADDR1LO]); for (i = 3; i >= 0; i--) { if (s->sar_active[i] && !memcmp(packet, gem_spaddr + 8 * i, 6)) { return GEM_RX_SAR_ACCEPT + i; @@ -754,7 +750,7 @@ static int get_queue_from_screen(CadenceGEMState *s, uint8_t *rxbuf_ptr, int i, j; for (i = 0; i < s->num_type1_screeners; i++) { - reg = s->regs[GEM_SCREENING_TYPE1_REGISTER_0 + i]; + reg = s->regs[R_SCREENING_TYPE1_REG0 + i]; matched = false; mismatched = false; @@ -786,7 +782,7 @@ static int get_queue_from_screen(CadenceGEMState *s, uint8_t *rxbuf_ptr, } for (i = 0; i < s->num_type2_screeners; i++) { - reg = s->regs[GEM_SCREENING_TYPE2_REGISTER_0 + i]; + reg = s->regs[R_SCREENING_TYPE2_REG0 + i]; matched = false; mismatched = false; @@ -799,7 +795,7 @@ static int get_queue_from_screen(CadenceGEMState *s, uint8_t *rxbuf_ptr, qemu_log_mask(LOG_GUEST_ERROR, "Out of range ethertype " "register index: %d\n", et_idx); } - if (type == s->regs[GEM_SCREENING_TYPE2_ETHERTYPE_REG_0 + + if (type == s->regs[R_SCREENING_TYPE2_ETHERTYPE_REG0 + et_idx]) { matched = true; } else { @@ -823,8 +819,8 @@ static int get_queue_from_screen(CadenceGEMState *s, uint8_t *rxbuf_ptr, "register index: %d\n", cr_idx); } - cr0 = s->regs[GEM_TYPE2_COMPARE_0_WORD_0 + cr_idx * 2]; - cr1 = s->regs[GEM_TYPE2_COMPARE_0_WORD_0 + cr_idx * 2 + 1]; + cr0 = s->regs[R_TYPE2_COMPARE_0_WORD_0 + cr_idx * 2]; + cr1 = s->regs[R_TYPE2_COMPARE_0_WORD_0 + cr_idx * 2 + 1]; offset = extract32(cr1, GEM_T2CW1_OFFSET_VALUE_SHIFT, GEM_T2CW1_OFFSET_VALUE_WIDTH); @@ -871,11 +867,11 @@ static uint32_t gem_get_queue_base_addr(CadenceGEMState *s, bool tx, int q) switch (q) { case 0: - base_addr = s->regs[tx ? GEM_TXQBASE : GEM_RXQBASE]; + base_addr = s->regs[tx ? R_TXQBASE : R_RXQBASE]; break; case 1 ... (MAX_PRIORITY_QUEUES - 1): - base_addr = s->regs[(tx ? GEM_TRANSMIT_Q1_PTR : - GEM_RECEIVE_Q1_PTR) + q - 1]; + base_addr = s->regs[(tx ? R_TRANSMIT_Q1_PTR : + R_RECEIVE_Q1_PTR) + q - 1]; break; default: g_assert_not_reached(); @@ -898,8 +894,8 @@ static hwaddr gem_get_desc_addr(CadenceGEMState *s, bool tx, int q) { hwaddr desc_addr = 0; - if (s->regs[GEM_DMACFG] & GEM_DMACFG_ADDR_64B) { - desc_addr = s->regs[tx ? GEM_TBQPH : GEM_RBQPH]; + if (s->regs[R_DMACFG] & GEM_DMACFG_ADDR_64B) { + desc_addr = s->regs[tx ? R_TBQPH : R_RBQPH]; } desc_addr <<= 32; desc_addr |= tx ? s->tx_desc_addr[q] : s->rx_desc_addr[q]; @@ -930,7 +926,7 @@ static void gem_get_rx_desc(CadenceGEMState *s, int q) /* Descriptor owned by software ? */ if (rx_desc_get_ownership(s->rx_desc[q]) == 1) { DB_PRINT("descriptor 0x%" HWADDR_PRIx " owned by sw.\n", desc_addr); - s->regs[GEM_RXSTATUS] |= GEM_RXSTATUS_NOBUF; + s->regs[R_RXSTATUS] |= GEM_RXSTATUS_NOBUF; gem_set_isr(s, q, GEM_INT_RXUSED); /* Handle interrupt consequences */ gem_update_int_status(s); @@ -958,7 +954,7 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) } /* Discard packets with receive length error enabled ? */ - if (s->regs[GEM_NWCFG] & GEM_NWCFG_LERR_DISC) { + if (s->regs[R_NWCFG] & GEM_NWCFG_LERR_DISC) { unsigned type_len; /* Fish the ethertype / length field out of the RX packet */ @@ -975,13 +971,13 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) /* * Determine configured receive buffer offset (probably 0) */ - rxbuf_offset = (s->regs[GEM_NWCFG] & GEM_NWCFG_BUFF_OFST_M) >> + rxbuf_offset = (s->regs[R_NWCFG] & GEM_NWCFG_BUFF_OFST_M) >> GEM_NWCFG_BUFF_OFST_S; /* The configure size of each receive buffer. Determines how many * buffers needed to hold this packet. */ - rxbufsize = ((s->regs[GEM_DMACFG] & GEM_DMACFG_RBUFSZ_M) >> + rxbufsize = ((s->regs[R_DMACFG] & GEM_DMACFG_RBUFSZ_M) >> GEM_DMACFG_RBUFSZ_S) * GEM_DMACFG_RBUFSZ_MUL; bytes_to_copy = size; @@ -1001,7 +997,7 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) } /* Strip of FCS field ? (usually yes) */ - if (s->regs[GEM_NWCFG] & GEM_NWCFG_STRIP_FCS) { + if (s->regs[R_NWCFG] & GEM_NWCFG_STRIP_FCS) { rxbuf_ptr = (void *)buf; } else { unsigned crc_val; @@ -1107,7 +1103,7 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) /* Count it */ gem_receive_updatestats(s, buf, size); - s->regs[GEM_RXSTATUS] |= GEM_RXSTATUS_FRMRCVD; + s->regs[R_RXSTATUS] |= GEM_RXSTATUS_FRMRCVD; gem_set_isr(s, q, GEM_INT_RXCMPL); /* Handle interrupt consequences */ @@ -1126,39 +1122,39 @@ static void gem_transmit_updatestats(CadenceGEMState *s, const uint8_t *packet, uint64_t octets; /* Total octets (bytes) transmitted */ - octets = ((uint64_t)(s->regs[GEM_OCTTXLO]) << 32) | - s->regs[GEM_OCTTXHI]; + octets = ((uint64_t)(s->regs[R_OCTTXLO]) << 32) | + s->regs[R_OCTTXHI]; octets += bytes; - s->regs[GEM_OCTTXLO] = octets >> 32; - s->regs[GEM_OCTTXHI] = octets; + s->regs[R_OCTTXLO] = octets >> 32; + s->regs[R_OCTTXHI] = octets; /* Error-free Frames transmitted */ - s->regs[GEM_TXCNT]++; + s->regs[R_TXCNT]++; /* Error-free Broadcast Frames counter */ if (!memcmp(packet, broadcast_addr, 6)) { - s->regs[GEM_TXBCNT]++; + s->regs[R_TXBCNT]++; } /* Error-free Multicast Frames counter */ if (packet[0] == 0x01) { - s->regs[GEM_TXMCNT]++; + s->regs[R_TXMCNT]++; } if (bytes <= 64) { - s->regs[GEM_TX64CNT]++; + s->regs[R_TX64CNT]++; } else if (bytes <= 127) { - s->regs[GEM_TX65CNT]++; + s->regs[R_TX65CNT]++; } else if (bytes <= 255) { - s->regs[GEM_TX128CNT]++; + s->regs[R_TX128CNT]++; } else if (bytes <= 511) { - s->regs[GEM_TX256CNT]++; + s->regs[R_TX256CNT]++; } else if (bytes <= 1023) { - s->regs[GEM_TX512CNT]++; + s->regs[R_TX512CNT]++; } else if (bytes <= 1518) { - s->regs[GEM_TX1024CNT]++; + s->regs[R_TX1024CNT]++; } else { - s->regs[GEM_TX1519CNT]++; + s->regs[R_TX1519CNT]++; } } @@ -1175,7 +1171,7 @@ static void gem_transmit(CadenceGEMState *s) int q = 0; /* Do nothing if transmit is not enabled. */ - if (!(s->regs[GEM_NWCTRL] & GEM_NWCTRL_TXENA)) { + if (!(s->regs[R_NWCTRL] & GEM_NWCTRL_TXENA)) { return; } @@ -1200,7 +1196,7 @@ static void gem_transmit(CadenceGEMState *s) while (tx_desc_get_used(desc) == 0) { /* Do nothing if transmit is not enabled. */ - if (!(s->regs[GEM_NWCTRL] & GEM_NWCTRL_TXENA)) { + if (!(s->regs[R_NWCTRL] & GEM_NWCTRL_TXENA)) { return; } print_gem_tx_desc(desc, q); @@ -1258,14 +1254,14 @@ static void gem_transmit(CadenceGEMState *s) } DB_PRINT("TX descriptor next: 0x%08x\n", s->tx_desc_addr[q]); - s->regs[GEM_TXSTATUS] |= GEM_TXSTATUS_TXCMPL; + s->regs[R_TXSTATUS] |= GEM_TXSTATUS_TXCMPL; gem_set_isr(s, q, GEM_INT_TXCMPL); /* Handle interrupt consequences */ gem_update_int_status(s); /* Is checksum offload enabled? */ - if (s->regs[GEM_DMACFG] & GEM_DMACFG_TXCSUM_OFFL) { + if (s->regs[R_DMACFG] & GEM_DMACFG_TXCSUM_OFFL) { net_checksum_calculate(s->tx_packet, total_bytes, CSUM_ALL); } @@ -1273,7 +1269,7 @@ static void gem_transmit(CadenceGEMState *s) gem_transmit_updatestats(s, s->tx_packet, total_bytes); /* Send the packet somewhere */ - if (s->phy_loop || (s->regs[GEM_NWCTRL] & + if (s->phy_loop || (s->regs[R_NWCTRL] & GEM_NWCTRL_LOCALLOOP)) { qemu_receive_packet(qemu_get_queue(s->nic), s->tx_packet, total_bytes); @@ -1289,9 +1285,8 @@ static void gem_transmit(CadenceGEMState *s) /* read next descriptor */ if (tx_desc_get_wrap(desc)) { - - if (s->regs[GEM_DMACFG] & GEM_DMACFG_ADDR_64B) { - packet_desc_addr = s->regs[GEM_TBQPH]; + if (s->regs[R_DMACFG] & GEM_DMACFG_ADDR_64B) { + packet_desc_addr = s->regs[R_TBQPH]; packet_desc_addr <<= 32; } else { packet_desc_addr = 0; @@ -1307,7 +1302,7 @@ static void gem_transmit(CadenceGEMState *s) } if (tx_desc_get_used(desc)) { - s->regs[GEM_TXSTATUS] |= GEM_TXSTATUS_USED; + s->regs[R_TXSTATUS] |= GEM_TXSTATUS_USED; /* IRQ TXUSED is defined only for queue 0 */ if (q == 0) { gem_set_isr(s, 0, GEM_INT_TXUSED); @@ -1353,30 +1348,30 @@ static void gem_reset(DeviceState *d) /* Set post reset register values */ memset(&s->regs[0], 0, sizeof(s->regs)); - s->regs[GEM_NWCFG] = 0x00080000; - s->regs[GEM_NWSTATUS] = 0x00000006; - s->regs[GEM_DMACFG] = 0x00020784; - s->regs[GEM_IMR] = 0x07ffffff; - s->regs[GEM_TXPAUSE] = 0x0000ffff; - s->regs[GEM_TXPARTIALSF] = 0x000003ff; - s->regs[GEM_RXPARTIALSF] = 0x000003ff; - s->regs[GEM_MODID] = s->revision; - s->regs[GEM_DESCONF] = 0x02D00111; - s->regs[GEM_DESCONF2] = 0x2ab10000 | s->jumbo_max_len; - s->regs[GEM_DESCONF5] = 0x002f2045; - s->regs[GEM_DESCONF6] = GEM_DESCONF6_64B_MASK; - s->regs[GEM_INT_Q1_MASK] = 0x00000CE6; - s->regs[GEM_JUMBO_MAX_LEN] = s->jumbo_max_len; + s->regs[R_NWCFG] = 0x00080000; + s->regs[R_NWSTATUS] = 0x00000006; + s->regs[R_DMACFG] = 0x00020784; + s->regs[R_IMR] = 0x07ffffff; + s->regs[R_TXPAUSE] = 0x0000ffff; + s->regs[R_TXPARTIALSF] = 0x000003ff; + s->regs[R_RXPARTIALSF] = 0x000003ff; + s->regs[R_MODID] = s->revision; + s->regs[R_DESCONF] = 0x02D00111; + s->regs[R_DESCONF2] = 0x2ab10000 | s->jumbo_max_len; + s->regs[R_DESCONF5] = 0x002f2045; + s->regs[R_DESCONF6] = GEM_DESCONF6_64B_MASK; + s->regs[R_INT_Q1_MASK] = 0x00000CE6; + s->regs[R_JUMBO_MAX_LEN] = s->jumbo_max_len; if (s->num_priority_queues > 1) { queues_mask = MAKE_64BIT_MASK(1, s->num_priority_queues - 1); - s->regs[GEM_DESCONF6] |= queues_mask; + s->regs[R_DESCONF6] |= queues_mask; } /* Set MAC address */ a = &s->conf.macaddr.a[0]; - s->regs[GEM_SPADDR1LO] = a[0] | (a[1] << 8) | (a[2] << 16) | (a[3] << 24); - s->regs[GEM_SPADDR1HI] = a[4] | (a[5] << 8); + s->regs[R_SPADDR1LO] = a[0] | (a[1] << 8) | (a[2] << 16) | (a[3] << 24); + s->regs[R_SPADDR1HI] = a[4] | (a[5] << 8); for (i = 0; i < 4; i++) { s->sar_active[i] = false; @@ -1437,11 +1432,11 @@ static uint64_t gem_read(void *opaque, hwaddr offset, unsigned size) DB_PRINT("offset: 0x%04x read: 0x%08x\n", (unsigned)offset*4, retval); switch (offset) { - case GEM_ISR: + case R_ISR: DB_PRINT("lowering irqs on ISR read\n"); /* The interrupts get updated at the end of the function. */ break; - case GEM_PHYMNTNC: + case R_PHYMNTNC: if (retval & GEM_PHYMNTNC_OP_R) { uint32_t phy_addr, reg_num; @@ -1495,7 +1490,7 @@ static void gem_write(void *opaque, hwaddr offset, uint64_t val, /* Handle register write side effects */ switch (offset) { - case GEM_NWCTRL: + case R_NWCTRL: if (val & GEM_NWCTRL_RXENA) { for (i = 0; i < s->num_priority_queues; ++i) { gem_get_rx_desc(s, i); @@ -1515,56 +1510,56 @@ static void gem_write(void *opaque, hwaddr offset, uint64_t val, } break; - case GEM_TXSTATUS: + case R_TXSTATUS: gem_update_int_status(s); break; - case GEM_RXQBASE: + case R_RXQBASE: s->rx_desc_addr[0] = val; break; - case GEM_RECEIVE_Q1_PTR ... GEM_RECEIVE_Q7_PTR: - s->rx_desc_addr[offset - GEM_RECEIVE_Q1_PTR + 1] = val; + case R_RECEIVE_Q1_PTR ... R_RECEIVE_Q7_PTR: + s->rx_desc_addr[offset - R_RECEIVE_Q1_PTR + 1] = val; break; - case GEM_TXQBASE: + case R_TXQBASE: s->tx_desc_addr[0] = val; break; - case GEM_TRANSMIT_Q1_PTR ... GEM_TRANSMIT_Q7_PTR: - s->tx_desc_addr[offset - GEM_TRANSMIT_Q1_PTR + 1] = val; + case R_TRANSMIT_Q1_PTR ... R_TRANSMIT_Q7_PTR: + s->tx_desc_addr[offset - R_TRANSMIT_Q1_PTR + 1] = val; break; - case GEM_RXSTATUS: + case R_RXSTATUS: gem_update_int_status(s); break; - case GEM_IER: - s->regs[GEM_IMR] &= ~val; + case R_IER: + s->regs[R_IMR] &= ~val; gem_update_int_status(s); break; - case GEM_JUMBO_MAX_LEN: - s->regs[GEM_JUMBO_MAX_LEN] = val & MAX_JUMBO_FRAME_SIZE_MASK; + case R_JUMBO_MAX_LEN: + s->regs[R_JUMBO_MAX_LEN] = val & MAX_JUMBO_FRAME_SIZE_MASK; break; - case GEM_INT_Q1_ENABLE ... GEM_INT_Q7_ENABLE: - s->regs[GEM_INT_Q1_MASK + offset - GEM_INT_Q1_ENABLE] &= ~val; + case R_INT_Q1_ENABLE ... R_INT_Q7_ENABLE: + s->regs[R_INT_Q1_MASK + offset - R_INT_Q1_ENABLE] &= ~val; gem_update_int_status(s); break; - case GEM_IDR: - s->regs[GEM_IMR] |= val; + case R_IDR: + s->regs[R_IMR] |= val; gem_update_int_status(s); break; - case GEM_INT_Q1_DISABLE ... GEM_INT_Q7_DISABLE: - s->regs[GEM_INT_Q1_MASK + offset - GEM_INT_Q1_DISABLE] |= val; + case R_INT_Q1_DISABLE ... R_INT_Q7_DISABLE: + s->regs[R_INT_Q1_MASK + offset - R_INT_Q1_DISABLE] |= val; gem_update_int_status(s); break; - case GEM_SPADDR1LO: - case GEM_SPADDR2LO: - case GEM_SPADDR3LO: - case GEM_SPADDR4LO: - s->sar_active[(offset - GEM_SPADDR1LO) / 2] = false; + case R_SPADDR1LO: + case R_SPADDR2LO: + case R_SPADDR3LO: + case R_SPADDR4LO: + s->sar_active[(offset - R_SPADDR1LO) / 2] = false; break; - case GEM_SPADDR1HI: - case GEM_SPADDR2HI: - case GEM_SPADDR3HI: - case GEM_SPADDR4HI: - s->sar_active[(offset - GEM_SPADDR1HI) / 2] = true; + case R_SPADDR1HI: + case R_SPADDR2HI: + case R_SPADDR3HI: + case R_SPADDR4HI: + s->sar_active[(offset - R_SPADDR1HI) / 2] = true; break; - case GEM_PHYMNTNC: + case R_PHYMNTNC: if (val & GEM_PHYMNTNC_OP_W) { uint32_t phy_addr, reg_num; From b46b526c082840d35d0c9dc2690a1cb159bda176 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Tue, 17 Oct 2023 21:44:13 +0200 Subject: [PATCH 175/974] hw/net/cadence_gem: use FIELD for screening registers Describe screening registers fields using the FIELD macros. Signed-off-by: Luc Michel Reviewed-by: sai.pavan.boddu@amd.com Message-id: 20231017194422.4124691-3-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/net/cadence_gem.c | 94 ++++++++++++++++++++++---------------------- 1 file changed, 48 insertions(+), 46 deletions(-) diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index bea2224dd8..dd00556232 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -170,35 +170,38 @@ REG32(INT_Q1_DISABLE, 0x620) REG32(INT_Q7_DISABLE, 0x638) REG32(SCREENING_TYPE1_REG0, 0x500) - -#define GEM_ST1R_UDP_PORT_MATCH_ENABLE (1 << 29) -#define GEM_ST1R_DSTC_ENABLE (1 << 28) -#define GEM_ST1R_UDP_PORT_MATCH_SHIFT (12) -#define GEM_ST1R_UDP_PORT_MATCH_WIDTH (27 - GEM_ST1R_UDP_PORT_MATCH_SHIFT + 1) -#define GEM_ST1R_DSTC_MATCH_SHIFT (4) -#define GEM_ST1R_DSTC_MATCH_WIDTH (11 - GEM_ST1R_DSTC_MATCH_SHIFT + 1) -#define GEM_ST1R_QUEUE_SHIFT (0) -#define GEM_ST1R_QUEUE_WIDTH (3 - GEM_ST1R_QUEUE_SHIFT + 1) + FIELD(SCREENING_TYPE1_REG0, QUEUE_NUM, 0, 4) + FIELD(SCREENING_TYPE1_REG0, DSTC_MATCH, 4, 8) + FIELD(SCREENING_TYPE1_REG0, UDP_PORT_MATCH, 12, 16) + FIELD(SCREENING_TYPE1_REG0, DSTC_ENABLE, 28, 1) + FIELD(SCREENING_TYPE1_REG0, UDP_PORT_MATCH_EN, 29, 1) + FIELD(SCREENING_TYPE1_REG0, DROP_ON_MATCH, 30, 1) REG32(SCREENING_TYPE2_REG0, 0x540) - -#define GEM_ST2R_COMPARE_A_ENABLE (1 << 18) -#define GEM_ST2R_COMPARE_A_SHIFT (13) -#define GEM_ST2R_COMPARE_WIDTH (17 - GEM_ST2R_COMPARE_A_SHIFT + 1) -#define GEM_ST2R_ETHERTYPE_ENABLE (1 << 12) -#define GEM_ST2R_ETHERTYPE_INDEX_SHIFT (9) -#define GEM_ST2R_ETHERTYPE_INDEX_WIDTH (11 - GEM_ST2R_ETHERTYPE_INDEX_SHIFT \ - + 1) -#define GEM_ST2R_QUEUE_SHIFT (0) -#define GEM_ST2R_QUEUE_WIDTH (3 - GEM_ST2R_QUEUE_SHIFT + 1) + FIELD(SCREENING_TYPE2_REG0, QUEUE_NUM, 0, 4) + FIELD(SCREENING_TYPE2_REG0, VLAN_PRIORITY, 4, 3) + FIELD(SCREENING_TYPE2_REG0, VLAN_ENABLE, 8, 1) + FIELD(SCREENING_TYPE2_REG0, ETHERTYPE_REG_INDEX, 9, 3) + FIELD(SCREENING_TYPE2_REG0, ETHERTYPE_ENABLE, 12, 1) + FIELD(SCREENING_TYPE2_REG0, COMPARE_A, 13, 5) + FIELD(SCREENING_TYPE2_REG0, COMPARE_A_ENABLE, 18, 1) + FIELD(SCREENING_TYPE2_REG0, COMPARE_B, 19, 5) + FIELD(SCREENING_TYPE2_REG0, COMPARE_B_ENABLE, 24, 1) + FIELD(SCREENING_TYPE2_REG0, COMPARE_C, 25, 5) + FIELD(SCREENING_TYPE2_REG0, COMPARE_C_ENABLE, 30, 1) + FIELD(SCREENING_TYPE2_REG0, DROP_ON_MATCH, 31, 1) REG32(SCREENING_TYPE2_ETHERTYPE_REG0, 0x6e0) -REG32(TYPE2_COMPARE_0_WORD_0, 0x700) -#define GEM_T2CW1_COMPARE_OFFSET_SHIFT (7) -#define GEM_T2CW1_COMPARE_OFFSET_WIDTH (8 - GEM_T2CW1_COMPARE_OFFSET_SHIFT + 1) -#define GEM_T2CW1_OFFSET_VALUE_SHIFT (0) -#define GEM_T2CW1_OFFSET_VALUE_WIDTH (6 - GEM_T2CW1_OFFSET_VALUE_SHIFT + 1) +REG32(TYPE2_COMPARE_0_WORD_0, 0x700) + FIELD(TYPE2_COMPARE_0_WORD_0, MASK_VALUE, 0, 16) + FIELD(TYPE2_COMPARE_0_WORD_0, COMPARE_VALUE, 16, 16) + +REG32(TYPE2_COMPARE_0_WORD_1, 0x704) + FIELD(TYPE2_COMPARE_0_WORD_1, OFFSET_VALUE, 0, 7) + FIELD(TYPE2_COMPARE_0_WORD_1, COMPARE_OFFSET, 7, 2) + FIELD(TYPE2_COMPARE_0_WORD_1, DISABLE_MASK, 9, 1) + FIELD(TYPE2_COMPARE_0_WORD_1, COMPARE_VLAN_ID, 10, 1) /*****************************************/ #define GEM_NWCTRL_TXSTART 0x00000200 /* Transmit Enable */ @@ -755,10 +758,9 @@ static int get_queue_from_screen(CadenceGEMState *s, uint8_t *rxbuf_ptr, mismatched = false; /* Screening is based on UDP Port */ - if (reg & GEM_ST1R_UDP_PORT_MATCH_ENABLE) { + if (FIELD_EX32(reg, SCREENING_TYPE1_REG0, UDP_PORT_MATCH_EN)) { uint16_t udp_port = rxbuf_ptr[14 + 22] << 8 | rxbuf_ptr[14 + 23]; - if (udp_port == extract32(reg, GEM_ST1R_UDP_PORT_MATCH_SHIFT, - GEM_ST1R_UDP_PORT_MATCH_WIDTH)) { + if (udp_port == FIELD_EX32(reg, SCREENING_TYPE1_REG0, UDP_PORT_MATCH)) { matched = true; } else { mismatched = true; @@ -766,10 +768,9 @@ static int get_queue_from_screen(CadenceGEMState *s, uint8_t *rxbuf_ptr, } /* Screening is based on DS/TC */ - if (reg & GEM_ST1R_DSTC_ENABLE) { + if (FIELD_EX32(reg, SCREENING_TYPE1_REG0, DSTC_ENABLE)) { uint8_t dscp = rxbuf_ptr[14 + 1]; - if (dscp == extract32(reg, GEM_ST1R_DSTC_MATCH_SHIFT, - GEM_ST1R_DSTC_MATCH_WIDTH)) { + if (dscp == FIELD_EX32(reg, SCREENING_TYPE1_REG0, DSTC_MATCH)) { matched = true; } else { mismatched = true; @@ -777,7 +778,7 @@ static int get_queue_from_screen(CadenceGEMState *s, uint8_t *rxbuf_ptr, } if (matched && !mismatched) { - return extract32(reg, GEM_ST1R_QUEUE_SHIFT, GEM_ST1R_QUEUE_WIDTH); + return FIELD_EX32(reg, SCREENING_TYPE1_REG0, QUEUE_NUM); } } @@ -786,10 +787,10 @@ static int get_queue_from_screen(CadenceGEMState *s, uint8_t *rxbuf_ptr, matched = false; mismatched = false; - if (reg & GEM_ST2R_ETHERTYPE_ENABLE) { + if (FIELD_EX32(reg, SCREENING_TYPE2_REG0, ETHERTYPE_ENABLE)) { uint16_t type = rxbuf_ptr[12] << 8 | rxbuf_ptr[13]; - int et_idx = extract32(reg, GEM_ST2R_ETHERTYPE_INDEX_SHIFT, - GEM_ST2R_ETHERTYPE_INDEX_WIDTH); + int et_idx = FIELD_EX32(reg, SCREENING_TYPE2_REG0, + ETHERTYPE_REG_INDEX); if (et_idx > s->num_type2_screeners) { qemu_log_mask(LOG_GUEST_ERROR, "Out of range ethertype " @@ -805,27 +806,27 @@ static int get_queue_from_screen(CadenceGEMState *s, uint8_t *rxbuf_ptr, /* Compare A, B, C */ for (j = 0; j < 3; j++) { - uint32_t cr0, cr1, mask; + uint32_t cr0, cr1, mask, compare; uint16_t rx_cmp; int offset; - int cr_idx = extract32(reg, GEM_ST2R_COMPARE_A_SHIFT + j * 6, - GEM_ST2R_COMPARE_WIDTH); + int cr_idx = extract32(reg, R_SCREENING_TYPE2_REG0_COMPARE_A_SHIFT + j * 6, + R_SCREENING_TYPE2_REG0_COMPARE_A_LENGTH); - if (!(reg & (GEM_ST2R_COMPARE_A_ENABLE << (j * 6)))) { + if (!extract32(reg, R_SCREENING_TYPE2_REG0_COMPARE_A_ENABLE_SHIFT + j * 6, + R_SCREENING_TYPE2_REG0_COMPARE_A_ENABLE_LENGTH)) { continue; } + if (cr_idx > s->num_type2_screeners) { qemu_log_mask(LOG_GUEST_ERROR, "Out of range compare " "register index: %d\n", cr_idx); } cr0 = s->regs[R_TYPE2_COMPARE_0_WORD_0 + cr_idx * 2]; - cr1 = s->regs[R_TYPE2_COMPARE_0_WORD_0 + cr_idx * 2 + 1]; - offset = extract32(cr1, GEM_T2CW1_OFFSET_VALUE_SHIFT, - GEM_T2CW1_OFFSET_VALUE_WIDTH); + cr1 = s->regs[R_TYPE2_COMPARE_0_WORD_1 + cr_idx * 2]; + offset = FIELD_EX32(cr1, TYPE2_COMPARE_0_WORD_1, OFFSET_VALUE); - switch (extract32(cr1, GEM_T2CW1_COMPARE_OFFSET_SHIFT, - GEM_T2CW1_COMPARE_OFFSET_WIDTH)) { + switch (FIELD_EX32(cr1, TYPE2_COMPARE_0_WORD_1, COMPARE_OFFSET)) { case 3: /* Skip UDP header */ qemu_log_mask(LOG_UNIMP, "TCP compare offsets" "unimplemented - assuming UDP\n"); @@ -843,9 +844,10 @@ static int get_queue_from_screen(CadenceGEMState *s, uint8_t *rxbuf_ptr, } rx_cmp = rxbuf_ptr[offset] << 8 | rxbuf_ptr[offset]; - mask = extract32(cr0, 0, 16); + mask = FIELD_EX32(cr0, TYPE2_COMPARE_0_WORD_0, MASK_VALUE); + compare = FIELD_EX32(cr0, TYPE2_COMPARE_0_WORD_0, COMPARE_VALUE); - if ((rx_cmp & mask) == (extract32(cr0, 16, 16) & mask)) { + if ((rx_cmp & mask) == (compare & mask)) { matched = true; } else { mismatched = true; @@ -853,7 +855,7 @@ static int get_queue_from_screen(CadenceGEMState *s, uint8_t *rxbuf_ptr, } if (matched && !mismatched) { - return extract32(reg, GEM_ST2R_QUEUE_SHIFT, GEM_ST2R_QUEUE_WIDTH); + return FIELD_EX32(reg, SCREENING_TYPE2_REG0, QUEUE_NUM); } } From bd8a922d2fc99445b2814c3a53d6be0f99555ea3 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Tue, 17 Oct 2023 21:44:14 +0200 Subject: [PATCH 176/974] hw/net/cadence_gem: use FIELD to describe NWCTRL register fields Use the FIELD macro to describe the NWCTRL register fields. Signed-off-by: Luc Michel Reviewed-by: sai.pavan.boddu@amd.com Message-id: 20231017194422.4124691-4-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/net/cadence_gem.c | 53 +++++++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index dd00556232..1bcc9b6811 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -46,6 +46,38 @@ } while (0) REG32(NWCTRL, 0x0) /* Network Control reg */ + FIELD(NWCTRL, LOOPBACK , 0, 1) + FIELD(NWCTRL, LOOPBACK_LOCAL , 1, 1) + FIELD(NWCTRL, ENABLE_RECEIVE, 2, 1) + FIELD(NWCTRL, ENABLE_TRANSMIT, 3, 1) + FIELD(NWCTRL, MAN_PORT_EN , 4, 1) + FIELD(NWCTRL, CLEAR_ALL_STATS_REGS , 5, 1) + FIELD(NWCTRL, INC_ALL_STATS_REGS, 6, 1) + FIELD(NWCTRL, STATS_WRITE_EN, 7, 1) + FIELD(NWCTRL, BACK_PRESSURE, 8, 1) + FIELD(NWCTRL, TRANSMIT_START , 9, 1) + FIELD(NWCTRL, TRANSMIT_HALT, 10, 1) + FIELD(NWCTRL, TX_PAUSE_FRAME_RE, 11, 1) + FIELD(NWCTRL, TX_PAUSE_FRAME_ZE, 12, 1) + FIELD(NWCTRL, STATS_TAKE_SNAP, 13, 1) + FIELD(NWCTRL, STATS_READ_SNAP, 14, 1) + FIELD(NWCTRL, STORE_RX_TS, 15, 1) + FIELD(NWCTRL, PFC_ENABLE, 16, 1) + FIELD(NWCTRL, PFC_PRIO_BASED, 17, 1) + FIELD(NWCTRL, FLUSH_RX_PKT_PCLK , 18, 1) + FIELD(NWCTRL, TX_LPI_EN, 19, 1) + FIELD(NWCTRL, PTP_UNICAST_ENA, 20, 1) + FIELD(NWCTRL, ALT_SGMII_MODE, 21, 1) + FIELD(NWCTRL, STORE_UDP_OFFSET, 22, 1) + FIELD(NWCTRL, EXT_TSU_PORT_EN, 23, 1) + FIELD(NWCTRL, ONE_STEP_SYNC_MO, 24, 1) + FIELD(NWCTRL, PFC_CTRL , 25, 1) + FIELD(NWCTRL, EXT_RXQ_SEL_EN , 26, 1) + FIELD(NWCTRL, OSS_CORRECTION_FIELD, 27, 1) + FIELD(NWCTRL, SEL_MII_ON_RGMII, 28, 1) + FIELD(NWCTRL, TWO_PT_FIVE_GIG, 29, 1) + FIELD(NWCTRL, IFG_EATS_QAV_CREDIT, 30, 1) + REG32(NWCFG, 0x4) /* Network Config reg */ REG32(NWSTATUS, 0x8) /* Network Status reg */ REG32(USERIO, 0xc) /* User IO reg */ @@ -204,11 +236,6 @@ REG32(TYPE2_COMPARE_0_WORD_1, 0x704) FIELD(TYPE2_COMPARE_0_WORD_1, COMPARE_VLAN_ID, 10, 1) /*****************************************/ -#define GEM_NWCTRL_TXSTART 0x00000200 /* Transmit Enable */ -#define GEM_NWCTRL_TXENA 0x00000008 /* Transmit Enable */ -#define GEM_NWCTRL_RXENA 0x00000004 /* Receive Enable */ -#define GEM_NWCTRL_LOCALLOOP 0x00000002 /* Local Loopback */ - #define GEM_NWCFG_STRIP_FCS 0x00020000 /* Strip FCS field */ #define GEM_NWCFG_LERR_DISC 0x00010000 /* Discard RX frames with len err */ #define GEM_NWCFG_BUFF_OFST_M 0x0000C000 /* Receive buffer offset mask */ @@ -560,7 +587,7 @@ static bool gem_can_receive(NetClientState *nc) s = qemu_get_nic_opaque(nc); /* Do nothing if receive is not enabled. */ - if (!(s->regs[R_NWCTRL] & GEM_NWCTRL_RXENA)) { + if (!FIELD_EX32(s->regs[R_NWCTRL], NWCTRL, ENABLE_RECEIVE)) { if (s->can_rx_state != 1) { s->can_rx_state = 1; DB_PRINT("can't receive - no enable\n"); @@ -1173,7 +1200,7 @@ static void gem_transmit(CadenceGEMState *s) int q = 0; /* Do nothing if transmit is not enabled. */ - if (!(s->regs[R_NWCTRL] & GEM_NWCTRL_TXENA)) { + if (!FIELD_EX32(s->regs[R_NWCTRL], NWCTRL, ENABLE_TRANSMIT)) { return; } @@ -1198,7 +1225,7 @@ static void gem_transmit(CadenceGEMState *s) while (tx_desc_get_used(desc) == 0) { /* Do nothing if transmit is not enabled. */ - if (!(s->regs[R_NWCTRL] & GEM_NWCTRL_TXENA)) { + if (!FIELD_EX32(s->regs[R_NWCTRL], NWCTRL, ENABLE_TRANSMIT)) { return; } print_gem_tx_desc(desc, q); @@ -1271,8 +1298,8 @@ static void gem_transmit(CadenceGEMState *s) gem_transmit_updatestats(s, s->tx_packet, total_bytes); /* Send the packet somewhere */ - if (s->phy_loop || (s->regs[R_NWCTRL] & - GEM_NWCTRL_LOCALLOOP)) { + if (s->phy_loop || FIELD_EX32(s->regs[R_NWCTRL], NWCTRL, + LOOPBACK_LOCAL)) { qemu_receive_packet(qemu_get_queue(s->nic), s->tx_packet, total_bytes); } else { @@ -1493,15 +1520,15 @@ static void gem_write(void *opaque, hwaddr offset, uint64_t val, /* Handle register write side effects */ switch (offset) { case R_NWCTRL: - if (val & GEM_NWCTRL_RXENA) { + if (FIELD_EX32(val, NWCTRL, ENABLE_RECEIVE)) { for (i = 0; i < s->num_priority_queues; ++i) { gem_get_rx_desc(s, i); } } - if (val & GEM_NWCTRL_TXSTART) { + if (FIELD_EX32(val, NWCTRL, TRANSMIT_START)) { gem_transmit(s); } - if (!(val & GEM_NWCTRL_TXENA)) { + if (!(FIELD_EX32(val, NWCTRL, ENABLE_TRANSMIT))) { /* Reset to start of Q when transmit disabled. */ for (i = 0; i < s->num_priority_queues; i++) { s->tx_desc_addr[i] = gem_get_tx_queue_base_addr(s, i); From 87a49c3f80c5cb80754f59ddd7006c57d69020ac Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Tue, 17 Oct 2023 21:44:15 +0200 Subject: [PATCH 177/974] hw/net/cadence_gem: use FIELD to describe NWCFG register fields Use de FIELD macro to describe the NWCFG register fields. Signed-off-by: Luc Michel Reviewed-by: sai.pavan.boddu@amd.com Message-id: 20231017194422.4124691-5-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/net/cadence_gem.c | 60 ++++++++++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index 1bcc9b6811..cf8b1261ed 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -79,6 +79,35 @@ REG32(NWCTRL, 0x0) /* Network Control reg */ FIELD(NWCTRL, IFG_EATS_QAV_CREDIT, 30, 1) REG32(NWCFG, 0x4) /* Network Config reg */ + FIELD(NWCFG, SPEED, 0, 1) + FIELD(NWCFG, FULL_DUPLEX, 1, 1) + FIELD(NWCFG, DISCARD_NON_VLAN_FRAMES, 2, 1) + FIELD(NWCFG, JUMBO_FRAMES, 3, 1) + FIELD(NWCFG, PROMISC, 4, 1) + FIELD(NWCFG, NO_BROADCAST, 5, 1) + FIELD(NWCFG, MULTICAST_HASH_EN, 6, 1) + FIELD(NWCFG, UNICAST_HASH_EN, 7, 1) + FIELD(NWCFG, RECV_1536_BYTE_FRAMES, 8, 1) + FIELD(NWCFG, EXTERNAL_ADDR_MATCH_EN, 9, 1) + FIELD(NWCFG, GIGABIT_MODE_ENABLE, 10, 1) + FIELD(NWCFG, PCS_SELECT, 11, 1) + FIELD(NWCFG, RETRY_TEST, 12, 1) + FIELD(NWCFG, PAUSE_ENABLE, 13, 1) + FIELD(NWCFG, RECV_BUF_OFFSET, 14, 2) + FIELD(NWCFG, LEN_ERR_DISCARD, 16, 1) + FIELD(NWCFG, FCS_REMOVE, 17, 1) + FIELD(NWCFG, MDC_CLOCK_DIV, 18, 3) + FIELD(NWCFG, DATA_BUS_WIDTH, 21, 2) + FIELD(NWCFG, DISABLE_COPY_PAUSE_FRAMES, 23, 1) + FIELD(NWCFG, RECV_CSUM_OFFLOAD_EN, 24, 1) + FIELD(NWCFG, EN_HALF_DUPLEX_RX, 25, 1) + FIELD(NWCFG, IGNORE_RX_FCS, 26, 1) + FIELD(NWCFG, SGMII_MODE_ENABLE, 27, 1) + FIELD(NWCFG, IPG_STRETCH_ENABLE, 28, 1) + FIELD(NWCFG, NSP_ACCEPT, 29, 1) + FIELD(NWCFG, IGNORE_IPG_RX_ER, 30, 1) + FIELD(NWCFG, UNI_DIRECTION_ENABLE, 31, 1) + REG32(NWSTATUS, 0x8) /* Network Status reg */ REG32(USERIO, 0xc) /* User IO reg */ REG32(DMACFG, 0x10) /* DMA Control reg */ @@ -236,17 +265,6 @@ REG32(TYPE2_COMPARE_0_WORD_1, 0x704) FIELD(TYPE2_COMPARE_0_WORD_1, COMPARE_VLAN_ID, 10, 1) /*****************************************/ -#define GEM_NWCFG_STRIP_FCS 0x00020000 /* Strip FCS field */ -#define GEM_NWCFG_LERR_DISC 0x00010000 /* Discard RX frames with len err */ -#define GEM_NWCFG_BUFF_OFST_M 0x0000C000 /* Receive buffer offset mask */ -#define GEM_NWCFG_BUFF_OFST_S 14 /* Receive buffer offset shift */ -#define GEM_NWCFG_RCV_1538 0x00000100 /* Receive 1538 bytes frame */ -#define GEM_NWCFG_UCAST_HASH 0x00000080 /* accept unicast if hash match */ -#define GEM_NWCFG_MCAST_HASH 0x00000040 /* accept multicast if hash match */ -#define GEM_NWCFG_BCAST_REJ 0x00000020 /* Reject broadcast packets */ -#define GEM_NWCFG_PROMISC 0x00000010 /* Accept all packets */ -#define GEM_NWCFG_JUMBO_FRAME 0x00000008 /* Jumbo Frames enable */ - #define GEM_DMACFG_ADDR_64B (1U << 30) #define GEM_DMACFG_TX_BD_EXT (1U << 29) #define GEM_DMACFG_RX_BD_EXT (1U << 28) @@ -482,7 +500,7 @@ static const uint8_t broadcast_addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; static uint32_t gem_get_max_buf_len(CadenceGEMState *s, bool tx) { uint32_t size; - if (s->regs[R_NWCFG] & GEM_NWCFG_JUMBO_FRAME) { + if (FIELD_EX32(s->regs[R_NWCFG], NWCFG, JUMBO_FRAMES)) { size = s->regs[R_JUMBO_MAX_LEN]; if (size > s->jumbo_max_len) { size = s->jumbo_max_len; @@ -492,7 +510,8 @@ static uint32_t gem_get_max_buf_len(CadenceGEMState *s, bool tx) } else if (tx) { size = 1518; } else { - size = s->regs[R_NWCFG] & GEM_NWCFG_RCV_1538 ? 1538 : 1518; + size = FIELD_EX32(s->regs[R_NWCFG], + NWCFG, RECV_1536_BYTE_FRAMES) ? 1538 : 1518; } return size; } @@ -732,13 +751,13 @@ static int gem_mac_address_filter(CadenceGEMState *s, const uint8_t *packet) int i, is_mc; /* Promiscuous mode? */ - if (s->regs[R_NWCFG] & GEM_NWCFG_PROMISC) { + if (FIELD_EX32(s->regs[R_NWCFG], NWCFG, PROMISC)) { return GEM_RX_PROMISCUOUS_ACCEPT; } if (!memcmp(packet, broadcast_addr, 6)) { /* Reject broadcast packets? */ - if (s->regs[R_NWCFG] & GEM_NWCFG_BCAST_REJ) { + if (FIELD_EX32(s->regs[R_NWCFG], NWCFG, NO_BROADCAST)) { return GEM_RX_REJECT; } return GEM_RX_BROADCAST_ACCEPT; @@ -746,8 +765,8 @@ static int gem_mac_address_filter(CadenceGEMState *s, const uint8_t *packet) /* Accept packets -w- hash match? */ is_mc = is_multicast_ether_addr(packet); - if ((is_mc && (s->regs[R_NWCFG] & GEM_NWCFG_MCAST_HASH)) || - (!is_mc && (s->regs[R_NWCFG] & GEM_NWCFG_UCAST_HASH))) { + if ((is_mc && (FIELD_EX32(s->regs[R_NWCFG], NWCFG, MULTICAST_HASH_EN))) || + (!is_mc && FIELD_EX32(s->regs[R_NWCFG], NWCFG, UNICAST_HASH_EN))) { uint64_t buckets; unsigned hash_index; @@ -983,7 +1002,7 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) } /* Discard packets with receive length error enabled ? */ - if (s->regs[R_NWCFG] & GEM_NWCFG_LERR_DISC) { + if (FIELD_EX32(s->regs[R_NWCFG], NWCFG, LEN_ERR_DISCARD)) { unsigned type_len; /* Fish the ethertype / length field out of the RX packet */ @@ -1000,8 +1019,7 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) /* * Determine configured receive buffer offset (probably 0) */ - rxbuf_offset = (s->regs[R_NWCFG] & GEM_NWCFG_BUFF_OFST_M) >> - GEM_NWCFG_BUFF_OFST_S; + rxbuf_offset = FIELD_EX32(s->regs[R_NWCFG], NWCFG, RECV_BUF_OFFSET); /* The configure size of each receive buffer. Determines how many * buffers needed to hold this packet. @@ -1026,7 +1044,7 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) } /* Strip of FCS field ? (usually yes) */ - if (s->regs[R_NWCFG] & GEM_NWCFG_STRIP_FCS) { + if (FIELD_EX32(s->regs[R_NWCFG], NWCFG, FCS_REMOVE)) { rxbuf_ptr = (void *)buf; } else { unsigned crc_val; From 01f9175dbc0f85cd89ddeb5dd515580082a20af8 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Tue, 17 Oct 2023 21:44:16 +0200 Subject: [PATCH 178/974] hw/net/cadence_gem: use FIELD to describe DMACFG register fields Use de FIELD macro to describe the DMACFG register fields. Signed-off-by: Luc Michel Reviewed-by: sai.pavan.boddu@amd.com Message-id: 20231017194422.4124691-6-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/net/cadence_gem.c | 48 ++++++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index cf8b1261ed..e3724b8447 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -110,7 +110,27 @@ REG32(NWCFG, 0x4) /* Network Config reg */ REG32(NWSTATUS, 0x8) /* Network Status reg */ REG32(USERIO, 0xc) /* User IO reg */ + REG32(DMACFG, 0x10) /* DMA Control reg */ + FIELD(DMACFG, SEND_BCAST_TO_ALL_QS, 31, 1) + FIELD(DMACFG, DMA_ADDR_BUS_WIDTH, 30, 1) + FIELD(DMACFG, TX_BD_EXT_MODE_EN , 29, 1) + FIELD(DMACFG, RX_BD_EXT_MODE_EN , 28, 1) + FIELD(DMACFG, FORCE_MAX_AMBA_BURST_TX, 26, 1) + FIELD(DMACFG, FORCE_MAX_AMBA_BURST_RX, 25, 1) + FIELD(DMACFG, FORCE_DISCARD_ON_ERR, 24, 1) + FIELD(DMACFG, RX_BUF_SIZE, 16, 8) + FIELD(DMACFG, CRC_ERROR_REPORT, 13, 1) + FIELD(DMACFG, INF_LAST_DBUF_SIZE_EN, 12, 1) + FIELD(DMACFG, TX_PBUF_CSUM_OFFLOAD, 11, 1) + FIELD(DMACFG, TX_PBUF_SIZE, 10, 1) + FIELD(DMACFG, RX_PBUF_SIZE, 8, 2) + FIELD(DMACFG, ENDIAN_SWAP_PACKET, 7, 1) + FIELD(DMACFG, ENDIAN_SWAP_MGNT, 6, 1) + FIELD(DMACFG, HDR_DATA_SPLIT_EN, 5, 1) + FIELD(DMACFG, AMBA_BURST_LEN , 0, 5) +#define GEM_DMACFG_RBUFSZ_MUL 64 /* DMA RX Buffer Size multiplier */ + REG32(TXSTATUS, 0x14) /* TX Status reg */ REG32(RXQBASE, 0x18) /* RX Q Base address reg */ REG32(TXQBASE, 0x1c) /* TX Q Base address reg */ @@ -265,13 +285,6 @@ REG32(TYPE2_COMPARE_0_WORD_1, 0x704) FIELD(TYPE2_COMPARE_0_WORD_1, COMPARE_VLAN_ID, 10, 1) /*****************************************/ -#define GEM_DMACFG_ADDR_64B (1U << 30) -#define GEM_DMACFG_TX_BD_EXT (1U << 29) -#define GEM_DMACFG_RX_BD_EXT (1U << 28) -#define GEM_DMACFG_RBUFSZ_M 0x00FF0000 /* DMA RX Buffer Size mask */ -#define GEM_DMACFG_RBUFSZ_S 16 /* DMA RX Buffer Size shift */ -#define GEM_DMACFG_RBUFSZ_MUL 64 /* DMA RX Buffer Size multiplier */ -#define GEM_DMACFG_TXCSUM_OFFL 0x00000800 /* Transmit checksum offload */ #define GEM_TXSTATUS_TXCMPL 0x00000020 /* Transmit Complete */ #define GEM_TXSTATUS_USED 0x00000001 /* sw owned descriptor encountered */ @@ -369,7 +382,7 @@ static inline uint64_t tx_desc_get_buffer(CadenceGEMState *s, uint32_t *desc) { uint64_t ret = desc[0]; - if (s->regs[R_DMACFG] & GEM_DMACFG_ADDR_64B) { + if (FIELD_EX32(s->regs[R_DMACFG], DMACFG, DMA_ADDR_BUS_WIDTH)) { ret |= (uint64_t)desc[2] << 32; } return ret; @@ -414,7 +427,7 @@ static inline uint64_t rx_desc_get_buffer(CadenceGEMState *s, uint32_t *desc) { uint64_t ret = desc[0] & ~0x3UL; - if (s->regs[R_DMACFG] & GEM_DMACFG_ADDR_64B) { + if (FIELD_EX32(s->regs[R_DMACFG], DMACFG, DMA_ADDR_BUS_WIDTH)) { ret |= (uint64_t)desc[2] << 32; } return ret; @@ -424,11 +437,11 @@ static inline int gem_get_desc_len(CadenceGEMState *s, bool rx_n_tx) { int ret = 2; - if (s->regs[R_DMACFG] & GEM_DMACFG_ADDR_64B) { + if (FIELD_EX32(s->regs[R_DMACFG], DMACFG, DMA_ADDR_BUS_WIDTH)) { ret += 2; } - if (s->regs[R_DMACFG] & (rx_n_tx ? GEM_DMACFG_RX_BD_EXT - : GEM_DMACFG_TX_BD_EXT)) { + if (s->regs[R_DMACFG] & (rx_n_tx ? R_DMACFG_RX_BD_EXT_MODE_EN_MASK + : R_DMACFG_TX_BD_EXT_MODE_EN_MASK)) { ret += 2; } @@ -942,7 +955,7 @@ static hwaddr gem_get_desc_addr(CadenceGEMState *s, bool tx, int q) { hwaddr desc_addr = 0; - if (s->regs[R_DMACFG] & GEM_DMACFG_ADDR_64B) { + if (FIELD_EX32(s->regs[R_DMACFG], DMACFG, DMA_ADDR_BUS_WIDTH)) { desc_addr = s->regs[tx ? R_TBQPH : R_RBQPH]; } desc_addr <<= 32; @@ -1024,8 +1037,9 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) /* The configure size of each receive buffer. Determines how many * buffers needed to hold this packet. */ - rxbufsize = ((s->regs[R_DMACFG] & GEM_DMACFG_RBUFSZ_M) >> - GEM_DMACFG_RBUFSZ_S) * GEM_DMACFG_RBUFSZ_MUL; + rxbufsize = FIELD_EX32(s->regs[R_DMACFG], DMACFG, RX_BUF_SIZE); + rxbufsize *= GEM_DMACFG_RBUFSZ_MUL; + bytes_to_copy = size; /* Hardware allows a zero value here but warns against it. To avoid QEMU @@ -1308,7 +1322,7 @@ static void gem_transmit(CadenceGEMState *s) gem_update_int_status(s); /* Is checksum offload enabled? */ - if (s->regs[R_DMACFG] & GEM_DMACFG_TXCSUM_OFFL) { + if (FIELD_EX32(s->regs[R_DMACFG], DMACFG, TX_PBUF_CSUM_OFFLOAD)) { net_checksum_calculate(s->tx_packet, total_bytes, CSUM_ALL); } @@ -1332,7 +1346,7 @@ static void gem_transmit(CadenceGEMState *s) /* read next descriptor */ if (tx_desc_get_wrap(desc)) { - if (s->regs[R_DMACFG] & GEM_DMACFG_ADDR_64B) { + if (FIELD_EX32(s->regs[R_DMACFG], DMACFG, DMA_ADDR_BUS_WIDTH)) { packet_desc_addr = s->regs[R_TBQPH]; packet_desc_addr <<= 32; } else { From 466da857160aa060c4e4abb8d0d885eaae4e4bf9 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Tue, 17 Oct 2023 21:44:17 +0200 Subject: [PATCH 179/974] hw/net/cadence_gem: use FIELD to describe [TX|RX]STATUS register fields Use de FIELD macro to describe the TXSTATUS and RXSTATUS register fields. Signed-off-by: Luc Michel Reviewed-by: sai.pavan.boddu@amd.com Message-id: 20231017194422.4124691-7-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/net/cadence_gem.c | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index e3724b8447..d7fdc77514 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -132,9 +132,30 @@ REG32(DMACFG, 0x10) /* DMA Control reg */ #define GEM_DMACFG_RBUFSZ_MUL 64 /* DMA RX Buffer Size multiplier */ REG32(TXSTATUS, 0x14) /* TX Status reg */ + FIELD(TXSTATUS, TX_USED_BIT_READ_MIDFRAME, 12, 1) + FIELD(TXSTATUS, TX_FRAME_TOO_LARGE, 11, 1) + FIELD(TXSTATUS, TX_DMA_LOCKUP, 10, 1) + FIELD(TXSTATUS, TX_MAC_LOCKUP, 9, 1) + FIELD(TXSTATUS, RESP_NOT_OK, 8, 1) + FIELD(TXSTATUS, LATE_COLLISION, 7, 1) + FIELD(TXSTATUS, TRANSMIT_UNDER_RUN, 6, 1) + FIELD(TXSTATUS, TRANSMIT_COMPLETE, 5, 1) + FIELD(TXSTATUS, AMBA_ERROR, 4, 1) + FIELD(TXSTATUS, TRANSMIT_GO, 3, 1) + FIELD(TXSTATUS, RETRY_LIMIT, 2, 1) + FIELD(TXSTATUS, COLLISION, 1, 1) + FIELD(TXSTATUS, USED_BIT_READ, 0, 1) + REG32(RXQBASE, 0x18) /* RX Q Base address reg */ REG32(TXQBASE, 0x1c) /* TX Q Base address reg */ REG32(RXSTATUS, 0x20) /* RX Status reg */ + FIELD(RXSTATUS, RX_DMA_LOCKUP, 5, 1) + FIELD(RXSTATUS, RX_MAC_LOCKUP, 4, 1) + FIELD(RXSTATUS, RESP_NOT_OK, 3, 1) + FIELD(RXSTATUS, RECEIVE_OVERRUN, 2, 1) + FIELD(RXSTATUS, FRAME_RECEIVED, 1, 1) + FIELD(RXSTATUS, BUF_NOT_AVAILABLE, 0, 1) + REG32(ISR, 0x24) /* Interrupt Status reg */ REG32(IER, 0x28) /* Interrupt Enable reg */ REG32(IDR, 0x2c) /* Interrupt Disable reg */ @@ -286,11 +307,6 @@ REG32(TYPE2_COMPARE_0_WORD_1, 0x704) /*****************************************/ -#define GEM_TXSTATUS_TXCMPL 0x00000020 /* Transmit Complete */ -#define GEM_TXSTATUS_USED 0x00000001 /* sw owned descriptor encountered */ - -#define GEM_RXSTATUS_FRMRCVD 0x00000002 /* Frame received */ -#define GEM_RXSTATUS_NOBUF 0x00000001 /* Buffer unavailable */ /* GEM_ISR GEM_IER GEM_IDR GEM_IMR */ #define GEM_INT_TXCMPL 0x00000080 /* Transmit Complete */ @@ -987,7 +1003,7 @@ static void gem_get_rx_desc(CadenceGEMState *s, int q) /* Descriptor owned by software ? */ if (rx_desc_get_ownership(s->rx_desc[q]) == 1) { DB_PRINT("descriptor 0x%" HWADDR_PRIx " owned by sw.\n", desc_addr); - s->regs[R_RXSTATUS] |= GEM_RXSTATUS_NOBUF; + s->regs[R_RXSTATUS] |= R_RXSTATUS_BUF_NOT_AVAILABLE_MASK; gem_set_isr(s, q, GEM_INT_RXUSED); /* Handle interrupt consequences */ gem_update_int_status(s); @@ -1164,7 +1180,7 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) /* Count it */ gem_receive_updatestats(s, buf, size); - s->regs[R_RXSTATUS] |= GEM_RXSTATUS_FRMRCVD; + s->regs[R_RXSTATUS] |= R_RXSTATUS_FRAME_RECEIVED_MASK; gem_set_isr(s, q, GEM_INT_RXCMPL); /* Handle interrupt consequences */ @@ -1315,7 +1331,7 @@ static void gem_transmit(CadenceGEMState *s) } DB_PRINT("TX descriptor next: 0x%08x\n", s->tx_desc_addr[q]); - s->regs[R_TXSTATUS] |= GEM_TXSTATUS_TXCMPL; + s->regs[R_TXSTATUS] |= R_TXSTATUS_TRANSMIT_COMPLETE_MASK; gem_set_isr(s, q, GEM_INT_TXCMPL); /* Handle interrupt consequences */ @@ -1363,7 +1379,7 @@ static void gem_transmit(CadenceGEMState *s) } if (tx_desc_get_used(desc)) { - s->regs[R_TXSTATUS] |= GEM_TXSTATUS_USED; + s->regs[R_TXSTATUS] |= R_TXSTATUS_USED_BIT_READ_MASK; /* IRQ TXUSED is defined only for queue 0 */ if (q == 0) { gem_set_isr(s, 0, GEM_INT_TXUSED); From 987e80601724207ffb747d6a12d718d3829b9c7b Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Tue, 17 Oct 2023 21:44:18 +0200 Subject: [PATCH 180/974] hw/net/cadence_gem: use FIELD to describe IRQ register fields Use de FIELD macro to describe the IRQ related register fields. Signed-off-by: Luc Michel Reviewed-by: sai.pavan.boddu@amd.com Message-id: 20231017194422.4124691-8-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/net/cadence_gem.c | 51 +++++++++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index d7fdc77514..7e6cab7107 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -157,9 +157,42 @@ REG32(RXSTATUS, 0x20) /* RX Status reg */ FIELD(RXSTATUS, BUF_NOT_AVAILABLE, 0, 1) REG32(ISR, 0x24) /* Interrupt Status reg */ + FIELD(ISR, TX_LOCKUP, 31, 1) + FIELD(ISR, RX_LOCKUP, 30, 1) + FIELD(ISR, TSU_TIMER, 29, 1) + FIELD(ISR, WOL, 28, 1) + FIELD(ISR, RECV_LPI, 27, 1) + FIELD(ISR, TSU_SEC_INCR, 26, 1) + FIELD(ISR, PTP_PDELAY_RESP_XMIT, 25, 1) + FIELD(ISR, PTP_PDELAY_REQ_XMIT, 24, 1) + FIELD(ISR, PTP_PDELAY_RESP_RECV, 23, 1) + FIELD(ISR, PTP_PDELAY_REQ_RECV, 22, 1) + FIELD(ISR, PTP_SYNC_XMIT, 21, 1) + FIELD(ISR, PTP_DELAY_REQ_XMIT, 20, 1) + FIELD(ISR, PTP_SYNC_RECV, 19, 1) + FIELD(ISR, PTP_DELAY_REQ_RECV, 18, 1) + FIELD(ISR, PCS_LP_PAGE_RECV, 17, 1) + FIELD(ISR, PCS_AN_COMPLETE, 16, 1) + FIELD(ISR, EXT_IRQ, 15, 1) + FIELD(ISR, PAUSE_FRAME_XMIT, 14, 1) + FIELD(ISR, PAUSE_TIME_ELAPSED, 13, 1) + FIELD(ISR, PAUSE_FRAME_RECV, 12, 1) + FIELD(ISR, RESP_NOT_OK, 11, 1) + FIELD(ISR, RECV_OVERRUN, 10, 1) + FIELD(ISR, LINK_CHANGE, 9, 1) + FIELD(ISR, USXGMII_INT, 8, 1) + FIELD(ISR, XMIT_COMPLETE, 7, 1) + FIELD(ISR, AMBA_ERROR, 6, 1) + FIELD(ISR, RETRY_EXCEEDED, 5, 1) + FIELD(ISR, XMIT_UNDER_RUN, 4, 1) + FIELD(ISR, TX_USED, 3, 1) + FIELD(ISR, RX_USED, 2, 1) + FIELD(ISR, RECV_COMPLETE, 1, 1) + FIELD(ISR, MGNT_FRAME_SENT, 0, 1) REG32(IER, 0x28) /* Interrupt Enable reg */ REG32(IDR, 0x2c) /* Interrupt Disable reg */ REG32(IMR, 0x30) /* Interrupt Mask reg */ + REG32(PHYMNTNC, 0x34) /* Phy Maintenance reg */ REG32(RXPAUSE, 0x38) /* RX Pause Time reg */ REG32(TXPAUSE, 0x3c) /* TX Pause Time reg */ @@ -308,12 +341,6 @@ REG32(TYPE2_COMPARE_0_WORD_1, 0x704) /*****************************************/ -/* GEM_ISR GEM_IER GEM_IDR GEM_IMR */ -#define GEM_INT_TXCMPL 0x00000080 /* Transmit Complete */ -#define GEM_INT_AMBA_ERR 0x00000040 -#define GEM_INT_TXUSED 0x00000008 -#define GEM_INT_RXUSED 0x00000004 -#define GEM_INT_RXCMPL 0x00000002 #define GEM_PHYMNTNC_OP_R 0x20000000 /* read operation */ #define GEM_PHYMNTNC_OP_W 0x10000000 /* write operation */ @@ -1004,7 +1031,7 @@ static void gem_get_rx_desc(CadenceGEMState *s, int q) if (rx_desc_get_ownership(s->rx_desc[q]) == 1) { DB_PRINT("descriptor 0x%" HWADDR_PRIx " owned by sw.\n", desc_addr); s->regs[R_RXSTATUS] |= R_RXSTATUS_BUF_NOT_AVAILABLE_MASK; - gem_set_isr(s, q, GEM_INT_RXUSED); + gem_set_isr(s, q, R_ISR_RX_USED_MASK); /* Handle interrupt consequences */ gem_update_int_status(s); } @@ -1104,7 +1131,7 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) if (size > gem_get_max_buf_len(s, false)) { qemu_log_mask(LOG_GUEST_ERROR, "rx frame too long\n"); - gem_set_isr(s, q, GEM_INT_AMBA_ERR); + gem_set_isr(s, q, R_ISR_AMBA_ERROR_MASK); return -1; } @@ -1181,7 +1208,7 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) gem_receive_updatestats(s, buf, size); s->regs[R_RXSTATUS] |= R_RXSTATUS_FRAME_RECEIVED_MASK; - gem_set_isr(s, q, GEM_INT_RXCMPL); + gem_set_isr(s, q, R_ISR_RECV_COMPLETE_MASK); /* Handle interrupt consequences */ gem_update_int_status(s); @@ -1294,7 +1321,7 @@ static void gem_transmit(CadenceGEMState *s) HWADDR_PRIx " too large: size 0x%x space 0x%zx\n", packet_desc_addr, tx_desc_get_length(desc), gem_get_max_buf_len(s, true) - (p - s->tx_packet)); - gem_set_isr(s, q, GEM_INT_AMBA_ERR); + gem_set_isr(s, q, R_ISR_AMBA_ERROR_MASK); break; } @@ -1332,7 +1359,7 @@ static void gem_transmit(CadenceGEMState *s) DB_PRINT("TX descriptor next: 0x%08x\n", s->tx_desc_addr[q]); s->regs[R_TXSTATUS] |= R_TXSTATUS_TRANSMIT_COMPLETE_MASK; - gem_set_isr(s, q, GEM_INT_TXCMPL); + gem_set_isr(s, q, R_ISR_XMIT_COMPLETE_MASK); /* Handle interrupt consequences */ gem_update_int_status(s); @@ -1382,7 +1409,7 @@ static void gem_transmit(CadenceGEMState *s) s->regs[R_TXSTATUS] |= R_TXSTATUS_USED_BIT_READ_MASK; /* IRQ TXUSED is defined only for queue 0 */ if (q == 0) { - gem_set_isr(s, 0, GEM_INT_TXUSED); + gem_set_isr(s, 0, R_ISR_TX_USED_MASK); } gem_update_int_status(s); } From ce077875da2493787985d4a747bacc924b279081 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Tue, 17 Oct 2023 21:44:19 +0200 Subject: [PATCH 181/974] hw/net/cadence_gem: use FIELD to describe DESCONF6 register fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the FIELD macro to describe the DESCONF6 register fields. Signed-off-by: Luc Michel Reviewed-by: Philippe Mathieu-Daudé Message-id: 20231017194422.4124691-9-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/net/cadence_gem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index 7e6cab7107..dffcc64df2 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -283,7 +283,7 @@ REG32(DESCONF3, 0x288) REG32(DESCONF4, 0x28c) REG32(DESCONF5, 0x290) REG32(DESCONF6, 0x294) -#define GEM_DESCONF6_64B_MASK (1U << 23) + FIELD(DESCONF6, DMA_ADDR_64B, 23, 1) REG32(DESCONF7, 0x298) REG32(INT_Q1_STATUS, 0x400) @@ -1463,7 +1463,7 @@ static void gem_reset(DeviceState *d) s->regs[R_DESCONF] = 0x02D00111; s->regs[R_DESCONF2] = 0x2ab10000 | s->jumbo_max_len; s->regs[R_DESCONF5] = 0x002f2045; - s->regs[R_DESCONF6] = GEM_DESCONF6_64B_MASK; + s->regs[R_DESCONF6] = R_DESCONF6_DMA_ADDR_64B_MASK; s->regs[R_INT_Q1_MASK] = 0x00000CE6; s->regs[R_JUMBO_MAX_LEN] = s->jumbo_max_len; From 1b09eeb122aaf1666a9c5aa789eb219443772685 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Tue, 17 Oct 2023 21:44:20 +0200 Subject: [PATCH 182/974] hw/net/cadence_gem: use FIELD to describe PHYMNTNC register fields Use the FIELD macro to describe the PHYMNTNC register fields. Signed-off-by: Luc Michel Reviewed-by: sai.pavan.boddu@amd.com Message-id: 20231017194422.4124691-10-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/net/cadence_gem.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index dffcc64df2..373d3ee071 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -194,6 +194,14 @@ REG32(IDR, 0x2c) /* Interrupt Disable reg */ REG32(IMR, 0x30) /* Interrupt Mask reg */ REG32(PHYMNTNC, 0x34) /* Phy Maintenance reg */ + FIELD(PHYMNTNC, DATA, 0, 16) + FIELD(PHYMNTNC, REG_ADDR, 18, 5) + FIELD(PHYMNTNC, PHY_ADDR, 23, 5) + FIELD(PHYMNTNC, OP, 28, 2) + FIELD(PHYMNTNC, ST, 30, 2) +#define MDIO_OP_READ 0x3 +#define MDIO_OP_WRITE 0x2 + REG32(RXPAUSE, 0x38) /* RX Pause Time reg */ REG32(TXPAUSE, 0x3c) /* TX Pause Time reg */ REG32(TXPARTIALSF, 0x40) /* TX Partial Store and Forward */ @@ -342,13 +350,6 @@ REG32(TYPE2_COMPARE_0_WORD_1, 0x704) -#define GEM_PHYMNTNC_OP_R 0x20000000 /* read operation */ -#define GEM_PHYMNTNC_OP_W 0x10000000 /* write operation */ -#define GEM_PHYMNTNC_ADDR 0x0F800000 /* Address bits */ -#define GEM_PHYMNTNC_ADDR_SHFT 23 -#define GEM_PHYMNTNC_REG 0x007C0000 /* register bits */ -#define GEM_PHYMNTNC_REG_SHIFT 18 - /* Marvell PHY definitions */ #define BOARD_PHY_ADDRESS 0 /* PHY address we will emulate a device at */ @@ -1541,12 +1542,12 @@ static uint64_t gem_read(void *opaque, hwaddr offset, unsigned size) /* The interrupts get updated at the end of the function. */ break; case R_PHYMNTNC: - if (retval & GEM_PHYMNTNC_OP_R) { + if (FIELD_EX32(retval, PHYMNTNC, OP) == MDIO_OP_READ) { uint32_t phy_addr, reg_num; - phy_addr = (retval & GEM_PHYMNTNC_ADDR) >> GEM_PHYMNTNC_ADDR_SHFT; + phy_addr = FIELD_EX32(retval, PHYMNTNC, PHY_ADDR); if (phy_addr == s->phy_addr) { - reg_num = (retval & GEM_PHYMNTNC_REG) >> GEM_PHYMNTNC_REG_SHIFT; + reg_num = FIELD_EX32(retval, PHYMNTNC, REG_ADDR); retval &= 0xFFFF0000; retval |= gem_phy_read(s, reg_num); } else { @@ -1664,12 +1665,12 @@ static void gem_write(void *opaque, hwaddr offset, uint64_t val, s->sar_active[(offset - R_SPADDR1HI) / 2] = true; break; case R_PHYMNTNC: - if (val & GEM_PHYMNTNC_OP_W) { + if (FIELD_EX32(val, PHYMNTNC, OP) == MDIO_OP_WRITE) { uint32_t phy_addr, reg_num; - phy_addr = (val & GEM_PHYMNTNC_ADDR) >> GEM_PHYMNTNC_ADDR_SHFT; + phy_addr = FIELD_EX32(val, PHYMNTNC, PHY_ADDR); if (phy_addr == s->phy_addr) { - reg_num = (val & GEM_PHYMNTNC_REG) >> GEM_PHYMNTNC_REG_SHIFT; + reg_num = FIELD_EX32(val, PHYMNTNC, REG_ADDR); gem_phy_write(s, reg_num, val); } } From 71a082a3fa9845360224c66029eea8d972b8b8bd Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Tue, 17 Oct 2023 21:44:21 +0200 Subject: [PATCH 183/974] hw/net/cadence_gem: perform PHY access on write only The MDIO access is done only on a write to the PHYMNTNC register. A subsequent read is used to retrieve the result but does not trigger an MDIO access by itself. Refactor the PHY access logic to perform all accesses (MDIO reads and writes) at PHYMNTNC write time. Signed-off-by: Luc Michel Reviewed-by: sai.pavan.boddu@amd.com Message-id: 20231017194422.4124691-11-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/net/cadence_gem.c | 56 ++++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index 373d3ee071..06a101bfcd 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -1521,6 +1521,38 @@ static void gem_phy_write(CadenceGEMState *s, unsigned reg_num, uint16_t val) s->phy_regs[reg_num] = val; } +static void gem_handle_phy_access(CadenceGEMState *s) +{ + uint32_t val = s->regs[R_PHYMNTNC]; + uint32_t phy_addr, reg_num; + + phy_addr = FIELD_EX32(val, PHYMNTNC, PHY_ADDR); + + if (phy_addr != s->phy_addr) { + /* no phy at this address */ + if (FIELD_EX32(val, PHYMNTNC, OP) == MDIO_OP_READ) { + s->regs[R_PHYMNTNC] = FIELD_DP32(val, PHYMNTNC, DATA, 0xffff); + } + return; + } + + reg_num = FIELD_EX32(val, PHYMNTNC, REG_ADDR); + + switch (FIELD_EX32(val, PHYMNTNC, OP)) { + case MDIO_OP_READ: + s->regs[R_PHYMNTNC] = FIELD_DP32(val, PHYMNTNC, DATA, + gem_phy_read(s, reg_num)); + break; + + case MDIO_OP_WRITE: + gem_phy_write(s, reg_num, val); + break; + + default: + break; /* only clause 22 operations are supported */ + } +} + /* * gem_read32: * Read a GEM register. @@ -1541,20 +1573,6 @@ static uint64_t gem_read(void *opaque, hwaddr offset, unsigned size) DB_PRINT("lowering irqs on ISR read\n"); /* The interrupts get updated at the end of the function. */ break; - case R_PHYMNTNC: - if (FIELD_EX32(retval, PHYMNTNC, OP) == MDIO_OP_READ) { - uint32_t phy_addr, reg_num; - - phy_addr = FIELD_EX32(retval, PHYMNTNC, PHY_ADDR); - if (phy_addr == s->phy_addr) { - reg_num = FIELD_EX32(retval, PHYMNTNC, REG_ADDR); - retval &= 0xFFFF0000; - retval |= gem_phy_read(s, reg_num); - } else { - retval |= 0xFFFF; /* No device at this address */ - } - } - break; } /* Squash read to clear bits */ @@ -1665,15 +1683,7 @@ static void gem_write(void *opaque, hwaddr offset, uint64_t val, s->sar_active[(offset - R_SPADDR1HI) / 2] = true; break; case R_PHYMNTNC: - if (FIELD_EX32(val, PHYMNTNC, OP) == MDIO_OP_WRITE) { - uint32_t phy_addr, reg_num; - - phy_addr = FIELD_EX32(val, PHYMNTNC, PHY_ADDR); - if (phy_addr == s->phy_addr) { - reg_num = FIELD_EX32(val, PHYMNTNC, REG_ADDR); - gem_phy_write(s, reg_num, val); - } - } + gem_handle_phy_access(s); break; } From df93de987f423a0ed918c425f5dbd9a25d3c6229 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Tue, 17 Oct 2023 21:44:22 +0200 Subject: [PATCH 184/974] hw/net/cadence_gem: enforce 32 bits variable size for CRC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CRC was stored in an unsigned variable in gem_receive. Change it for a uint32_t to ensure we have the correct variable size here. Signed-off-by: Luc Michel Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: sai.pavan.boddu@amd.com Message-id: 20231017194422.4124691-12-luc.michel@amd.com Signed-off-by: Peter Maydell --- hw/net/cadence_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index 06a101bfcd..5b989f5b52 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -1105,7 +1105,7 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) if (FIELD_EX32(s->regs[R_NWCFG], NWCFG, FCS_REMOVE)) { rxbuf_ptr = (void *)buf; } else { - unsigned crc_val; + uint32_t crc_val; if (size > MAX_FRAME_SIZE - sizeof(crc_val)) { size = MAX_FRAME_SIZE - sizeof(crc_val); From 096434fea13acd19f4ead00cdf9babea8dc7e61e Mon Sep 17 00:00:00 2001 From: Jeuk Kim Date: Thu, 19 Oct 2023 23:28:06 +0900 Subject: [PATCH 185/974] hw/ufs: Modify lu.c to share codes with SCSI subsystem This patch removes the code that ufs-lu was duplicating from scsi-hd and allows them to share code. It makes ufs-lu have a virtual scsi-bus and scsi-hd internally. This allows scsi related commands to be passed thorugh to the scsi-hd. The query request and nop command work the same as the existing logic. Well-known lus do not have a virtual scsi-bus and scsi-hd, and handle the necessary scsi commands by emulating them directly. Signed-off-by: Jeuk Kim --- hw/ufs/lu.c | 1511 +++++++--------------------------------- hw/ufs/trace-events | 25 - hw/ufs/ufs.c | 202 +----- hw/ufs/ufs.h | 36 +- include/block/ufs.h | 2 +- tests/qtest/ufs-test.c | 37 +- 6 files changed, 334 insertions(+), 1479 deletions(-) diff --git a/hw/ufs/lu.c b/hw/ufs/lu.c index 13b5e37b53..81bfff9b4e 100644 --- a/hw/ufs/lu.c +++ b/hw/ufs/lu.c @@ -19,57 +19,117 @@ #include "trace.h" #include "ufs.h" -/* - * The code below handling SCSI commands is copied from hw/scsi/scsi-disk.c, - * with minor adjustments to make it work for UFS. - */ +#define SCSI_COMMAND_FAIL (-1) -#define SCSI_DMA_BUF_SIZE (128 * KiB) -#define SCSI_MAX_INQUIRY_LEN 256 -#define SCSI_INQUIRY_DATA_SIZE 36 -#define SCSI_MAX_MODE_LEN 256 - -typedef struct UfsSCSIReq { - SCSIRequest req; - /* Both sector and sector_count are in terms of BDRV_SECTOR_SIZE bytes. */ - uint64_t sector; - uint32_t sector_count; - uint32_t buflen; - bool started; - bool need_fua_emulation; - struct iovec iov; - QEMUIOVector qiov; - BlockAcctCookie acct; -} UfsSCSIReq; - -static void ufs_scsi_free_request(SCSIRequest *req) +static void ufs_build_upiu_sense_data(UfsRequest *req, uint8_t *sense, + uint32_t sense_len) { - UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req); - - qemu_vfree(r->iov.iov_base); + req->rsp_upiu.sr.sense_data_len = cpu_to_be16(sense_len); + assert(sense_len <= SCSI_SENSE_LEN); + memcpy(req->rsp_upiu.sr.sense_data, sense, sense_len); } -static void scsi_check_condition(UfsSCSIReq *r, SCSISense sense) +static void ufs_build_scsi_response_upiu(UfsRequest *req, uint8_t *sense, + uint32_t sense_len, + uint32_t transfered_len, + int16_t status) { - trace_ufs_scsi_check_condition(r->req.tag, sense.key, sense.asc, - sense.ascq); - scsi_req_build_sense(&r->req, sense); - scsi_req_complete(&r->req, CHECK_CONDITION); -} + uint32_t expected_len = be32_to_cpu(req->req_upiu.sc.exp_data_transfer_len); + uint8_t flags = 0, response = UFS_COMMAND_RESULT_SUCCESS; + uint16_t data_segment_length; -static int ufs_scsi_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf, - uint32_t outbuf_len) -{ - UfsHc *u = UFS(req->bus->qbus.parent); - UfsLu *lu = DO_UPCAST(UfsLu, qdev, req->dev); - uint8_t page_code = req->cmd.buf[2]; - int start, buflen = 0; - - if (outbuf_len < SCSI_INQUIRY_DATA_SIZE) { - return -1; + if (expected_len > transfered_len) { + req->rsp_upiu.sr.residual_transfer_count = + cpu_to_be32(expected_len - transfered_len); + flags |= UFS_UPIU_FLAG_UNDERFLOW; + } else if (expected_len < transfered_len) { + req->rsp_upiu.sr.residual_transfer_count = + cpu_to_be32(transfered_len - expected_len); + flags |= UFS_UPIU_FLAG_OVERFLOW; } - outbuf[buflen++] = lu->qdev.type & 0x1f; + if (status != 0) { + ufs_build_upiu_sense_data(req, sense, sense_len); + response = UFS_COMMAND_RESULT_FAIL; + } + + data_segment_length = + cpu_to_be16(sense_len + sizeof(req->rsp_upiu.sr.sense_data_len)); + ufs_build_upiu_header(req, UFS_UPIU_TRANSACTION_RESPONSE, flags, response, + status, data_segment_length); +} + +static void ufs_scsi_command_complete(SCSIRequest *scsi_req, size_t resid) +{ + UfsRequest *req = scsi_req->hba_private; + int16_t status = scsi_req->status; + + uint32_t transfered_len = scsi_req->cmd.xfer - resid; + + ufs_build_scsi_response_upiu(req, scsi_req->sense, scsi_req->sense_len, + transfered_len, status); + + ufs_complete_req(req, UFS_REQUEST_SUCCESS); + + scsi_req->hba_private = NULL; + scsi_req_unref(scsi_req); +} + +static QEMUSGList *ufs_get_sg_list(SCSIRequest *scsi_req) +{ + UfsRequest *req = scsi_req->hba_private; + return req->sg; +} + +static const struct SCSIBusInfo ufs_scsi_info = { + .tcq = true, + .max_target = 0, + .max_lun = UFS_MAX_LUS, + .max_channel = 0, + + .get_sg_list = ufs_get_sg_list, + .complete = ufs_scsi_command_complete, +}; + +static int ufs_emulate_report_luns(UfsRequest *req, uint8_t *outbuf, + uint32_t outbuf_len) +{ + UfsHc *u = req->hc; + int len = 0; + + /* TODO: Support for cases where SELECT REPORT is 1 and 2 */ + if (req->req_upiu.sc.cdb[2] != 0) { + return SCSI_COMMAND_FAIL; + } + + len += 8; + + for (uint8_t lun = 0; lun < UFS_MAX_LUS; ++lun) { + if (u->lus[lun]) { + if (len + 8 > outbuf_len) { + break; + } + + memset(outbuf + len, 0, 8); + outbuf[len] = 0; + outbuf[len + 1] = lun; + len += 8; + } + } + + /* store the LUN list length */ + stl_be_p(outbuf, len - 8); + + return len; +} + +static int ufs_scsi_emulate_vpd_page(UfsRequest *req, uint8_t *outbuf, + uint32_t outbuf_len) +{ + uint8_t page_code = req->req_upiu.sc.cdb[2]; + int start, buflen = 0; + + outbuf[buflen++] = TYPE_WLUN; outbuf[buflen++] = page_code; outbuf[buflen++] = 0x00; outbuf[buflen++] = 0x00; @@ -78,36 +138,12 @@ static int ufs_scsi_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf, switch (page_code) { case 0x00: /* Supported page codes, mandatory */ { - trace_ufs_scsi_emulate_vpd_page_00(req->cmd.xfer); outbuf[buflen++] = 0x00; /* list of supported pages (this page) */ - if (u->params.serial) { - outbuf[buflen++] = 0x80; /* unit serial number */ - } outbuf[buflen++] = 0x87; /* mode page policy */ break; } - case 0x80: /* Device serial number, optional */ - { - int l; - - if (!u->params.serial) { - trace_ufs_scsi_emulate_vpd_page_80_not_supported(); - return -1; - } - - l = strlen(u->params.serial); - if (l > SCSI_INQUIRY_DATA_SIZE) { - l = SCSI_INQUIRY_DATA_SIZE; - } - - trace_ufs_scsi_emulate_vpd_page_80(req->cmd.xfer); - memcpy(outbuf + buflen, u->params.serial, l); - buflen += l; - break; - } case 0x87: /* Mode Page Policy, mandatory */ { - trace_ufs_scsi_emulate_vpd_page_87(req->cmd.xfer); outbuf[buflen++] = 0x3f; /* apply to all mode pages and subpages */ outbuf[buflen++] = 0xff; outbuf[buflen++] = 0; /* shared */ @@ -115,7 +151,7 @@ static int ufs_scsi_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf, break; } default: - return -1; + return SCSI_COMMAND_FAIL; } /* done with EVPD */ assert(buflen - start <= 255); @@ -123,1150 +159,130 @@ static int ufs_scsi_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf, return buflen; } -static int ufs_scsi_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf, +static int ufs_emulate_wlun_inquiry(UfsRequest *req, uint8_t *outbuf, uint32_t outbuf_len) { - int buflen = 0; - - if (outbuf_len < SCSI_INQUIRY_DATA_SIZE) { - return -1; + if (outbuf_len < SCSI_INQUIRY_LEN) { + return 0; } - if (req->cmd.buf[1] & 0x1) { + if (req->req_upiu.sc.cdb[1] & 0x1) { /* Vital product data */ return ufs_scsi_emulate_vpd_page(req, outbuf, outbuf_len); } /* Standard INQUIRY data */ - if (req->cmd.buf[2] != 0) { - return -1; + if (req->req_upiu.sc.cdb[2] != 0) { + return SCSI_COMMAND_FAIL; } - /* PAGE CODE == 0 */ - buflen = req->cmd.xfer; - if (buflen > SCSI_MAX_INQUIRY_LEN) { - buflen = SCSI_MAX_INQUIRY_LEN; - } - - if (is_wlun(req->lun)) { - outbuf[0] = TYPE_WLUN; - } else { - outbuf[0] = 0; - } + outbuf[0] = TYPE_WLUN; outbuf[1] = 0; - - strpadcpy((char *)&outbuf[16], 16, "QEMU UFS", ' '); + outbuf[2] = 0x6; /* SPC-4 */ + outbuf[3] = 0x2; + outbuf[4] = 31; + outbuf[5] = 0; + outbuf[6] = 0; + outbuf[7] = 0x2; strpadcpy((char *)&outbuf[8], 8, "QEMU", ' '); - + strpadcpy((char *)&outbuf[16], 16, "QEMU UFS", ' '); memset(&outbuf[32], 0, 4); - outbuf[2] = 0x06; /* SPC-4 */ - outbuf[3] = 0x2; - - if (buflen > SCSI_INQUIRY_DATA_SIZE) { - outbuf[4] = buflen - 5; /* Additional Length = (Len - 1) - 4 */ - } else { - /* - * If the allocation length of CDB is too small, the additional - * length is not adjusted - */ - outbuf[4] = SCSI_INQUIRY_DATA_SIZE - 5; - } - - /* Support TCQ. */ - outbuf[7] = req->bus->info->tcq ? 0x02 : 0; - return buflen; + return SCSI_INQUIRY_LEN; } -static int mode_sense_page(UfsLu *lu, int page, uint8_t **p_outbuf, - int page_control) +static UfsReqResult ufs_emulate_scsi_cmd(UfsLu *lu, UfsRequest *req) { - static const int mode_sense_valid[0x3f] = { - [MODE_PAGE_CACHING] = 1, - [MODE_PAGE_R_W_ERROR] = 1, - [MODE_PAGE_CONTROL] = 1, - }; + uint8_t lun = lu->lun; + uint8_t outbuf[4096]; + uint8_t sense_buf[UFS_SENSE_SIZE]; + uint8_t scsi_status; + int len = 0; - uint8_t *p = *p_outbuf + 2; - int length; - - assert(page < ARRAY_SIZE(mode_sense_valid)); - if ((mode_sense_valid[page]) == 0) { - return -1; - } - - /* - * If Changeable Values are requested, a mask denoting those mode parameters - * that are changeable shall be returned. As we currently don't support - * parameter changes via MODE_SELECT all bits are returned set to zero. - * The buffer was already memset to zero by the caller of this function. - */ - switch (page) { - case MODE_PAGE_CACHING: - length = 0x12; - if (page_control == 1 || /* Changeable Values */ - blk_enable_write_cache(lu->qdev.conf.blk)) { - p[0] = 4; /* WCE */ - } - break; - - case MODE_PAGE_R_W_ERROR: - length = 10; - if (page_control == 1) { /* Changeable Values */ - break; - } - p[0] = 0x80; /* Automatic Write Reallocation Enabled */ - break; - - case MODE_PAGE_CONTROL: - length = 10; - if (page_control == 1) { /* Changeable Values */ - break; - } - p[1] = 0x10; /* Queue Algorithm modifier */ - p[8] = 0xff; /* Busy Timeout Period */ - p[9] = 0xff; - break; - - default: - return -1; - } - - assert(length < 256); - (*p_outbuf)[0] = page; - (*p_outbuf)[1] = length; - *p_outbuf += length + 2; - return length + 2; -} - -static int ufs_scsi_emulate_mode_sense(UfsSCSIReq *r, uint8_t *outbuf) -{ - UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev); - bool dbd; - int page, buflen, ret, page_control; - uint8_t *p; - uint8_t dev_specific_param = 0; - - dbd = (r->req.cmd.buf[1] & 0x8) != 0; - if (!dbd) { - return -1; - } - - page = r->req.cmd.buf[2] & 0x3f; - page_control = (r->req.cmd.buf[2] & 0xc0) >> 6; - - trace_ufs_scsi_emulate_mode_sense((r->req.cmd.buf[0] == MODE_SENSE) ? 6 : - 10, - page, r->req.cmd.xfer, page_control); - memset(outbuf, 0, r->req.cmd.xfer); - p = outbuf; - - if (!blk_is_writable(lu->qdev.conf.blk)) { - dev_specific_param |= 0x80; /* Readonly. */ - } - - p[2] = 0; /* Medium type. */ - p[3] = dev_specific_param; - p[6] = p[7] = 0; /* Block descriptor length. */ - p += 8; - - if (page_control == 3) { - /* Saved Values */ - scsi_check_condition(r, SENSE_CODE(SAVING_PARAMS_NOT_SUPPORTED)); - return -1; - } - - if (page == 0x3f) { - for (page = 0; page <= 0x3e; page++) { - mode_sense_page(lu, page, &p, page_control); - } - } else { - ret = mode_sense_page(lu, page, &p, page_control); - if (ret == -1) { - return -1; - } - } - - buflen = p - outbuf; - /* - * The mode data length field specifies the length in bytes of the - * following data that is available to be transferred. The mode data - * length does not include itself. - */ - outbuf[0] = ((buflen - 2) >> 8) & 0xff; - outbuf[1] = (buflen - 2) & 0xff; - return buflen; -} - -/* - * scsi_handle_rw_error has two return values. False means that the error - * must be ignored, true means that the error has been processed and the - * caller should not do anything else for this request. Note that - * scsi_handle_rw_error always manages its reference counts, independent - * of the return value. - */ -static bool scsi_handle_rw_error(UfsSCSIReq *r, int ret, bool acct_failed) -{ - bool is_read = (r->req.cmd.mode == SCSI_XFER_FROM_DEV); - UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev); - SCSISense sense = SENSE_CODE(NO_SENSE); - int error = 0; - bool req_has_sense = false; - BlockErrorAction action; - int status; - - if (ret < 0) { - status = scsi_sense_from_errno(-ret, &sense); - error = -ret; - } else { - /* A passthrough command has completed with nonzero status. */ - status = ret; - if (status == CHECK_CONDITION) { - req_has_sense = true; - error = scsi_sense_buf_to_errno(r->req.sense, sizeof(r->req.sense)); + switch (req->req_upiu.sc.cdb[0]) { + case REPORT_LUNS: + len = ufs_emulate_report_luns(req, outbuf, sizeof(outbuf)); + if (len == SCSI_COMMAND_FAIL) { + scsi_build_sense(sense_buf, SENSE_CODE(INVALID_FIELD)); + scsi_status = CHECK_CONDITION; } else { - error = EINVAL; + scsi_status = GOOD; } - } - - /* - * Check whether the error has to be handled by the guest or should - * rather follow the rerror=/werror= settings. Guest-handled errors - * are usually retried immediately, so do not post them to QMP and - * do not account them as failed I/O. - */ - if (req_has_sense && scsi_sense_buf_is_guest_recoverable( - r->req.sense, sizeof(r->req.sense))) { - action = BLOCK_ERROR_ACTION_REPORT; - acct_failed = false; - } else { - action = blk_get_error_action(lu->qdev.conf.blk, is_read, error); - blk_error_action(lu->qdev.conf.blk, action, is_read, error); - } - - switch (action) { - case BLOCK_ERROR_ACTION_REPORT: - if (acct_failed) { - block_acct_failed(blk_get_stats(lu->qdev.conf.blk), &r->acct); - } - if (!req_has_sense && status == CHECK_CONDITION) { - scsi_req_build_sense(&r->req, sense); - } - scsi_req_complete(&r->req, status); - return true; - - case BLOCK_ERROR_ACTION_IGNORE: - return false; - - case BLOCK_ERROR_ACTION_STOP: - scsi_req_retry(&r->req); - return true; - - default: - g_assert_not_reached(); - } -} - -static bool ufs_scsi_req_check_error(UfsSCSIReq *r, int ret, bool acct_failed) -{ - if (r->req.io_canceled) { - scsi_req_cancel_complete(&r->req); - return true; - } - - if (ret < 0) { - return scsi_handle_rw_error(r, ret, acct_failed); - } - - return false; -} - -static void scsi_aio_complete(void *opaque, int ret) -{ - UfsSCSIReq *r = (UfsSCSIReq *)opaque; - UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev); - - assert(r->req.aiocb != NULL); - r->req.aiocb = NULL; - aio_context_acquire(blk_get_aio_context(lu->qdev.conf.blk)); - if (ufs_scsi_req_check_error(r, ret, true)) { - goto done; - } - - block_acct_done(blk_get_stats(lu->qdev.conf.blk), &r->acct); - scsi_req_complete(&r->req, GOOD); - -done: - aio_context_release(blk_get_aio_context(lu->qdev.conf.blk)); - scsi_req_unref(&r->req); -} - -static int32_t ufs_scsi_emulate_command(SCSIRequest *req, uint8_t *buf) -{ - UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req); - UfsLu *lu = DO_UPCAST(UfsLu, qdev, req->dev); - uint32_t last_block = 0; - uint8_t *outbuf; - int buflen; - - switch (req->cmd.buf[0]) { - case INQUIRY: - case MODE_SENSE_10: - case START_STOP: - case REQUEST_SENSE: - break; - - default: - if (!blk_is_available(lu->qdev.conf.blk)) { - scsi_check_condition(r, SENSE_CODE(NO_MEDIUM)); - return 0; - } - break; - } - - /* - * FIXME: we shouldn't return anything bigger than 4k, but the code - * requires the buffer to be as big as req->cmd.xfer in several - * places. So, do not allow CDBs with a very large ALLOCATION - * LENGTH. The real fix would be to modify scsi_read_data and - * dma_buf_read, so that they return data beyond the buflen - * as all zeros. - */ - if (req->cmd.xfer > 65536) { - goto illegal_request; - } - r->buflen = MAX(4096, req->cmd.xfer); - - if (!r->iov.iov_base) { - r->iov.iov_base = blk_blockalign(lu->qdev.conf.blk, r->buflen); - } - - outbuf = r->iov.iov_base; - memset(outbuf, 0, r->buflen); - switch (req->cmd.buf[0]) { - case TEST_UNIT_READY: - assert(blk_is_available(lu->qdev.conf.blk)); break; case INQUIRY: - buflen = ufs_scsi_emulate_inquiry(req, outbuf, r->buflen); - if (buflen < 0) { - goto illegal_request; + len = ufs_emulate_wlun_inquiry(req, outbuf, sizeof(outbuf)); + if (len == SCSI_COMMAND_FAIL) { + scsi_build_sense(sense_buf, SENSE_CODE(INVALID_FIELD)); + scsi_status = CHECK_CONDITION; + } else { + scsi_status = GOOD; } break; - case MODE_SENSE_10: - buflen = ufs_scsi_emulate_mode_sense(r, outbuf); - if (buflen < 0) { - goto illegal_request; - } - break; - case READ_CAPACITY_10: - /* The normal LEN field for this command is zero. */ - memset(outbuf, 0, 8); - if (lu->qdev.max_lba > 0) { - last_block = lu->qdev.max_lba - 1; - }; - outbuf[0] = (last_block >> 24) & 0xff; - outbuf[1] = (last_block >> 16) & 0xff; - outbuf[2] = (last_block >> 8) & 0xff; - outbuf[3] = last_block & 0xff; - outbuf[4] = (lu->qdev.blocksize >> 24) & 0xff; - outbuf[5] = (lu->qdev.blocksize >> 16) & 0xff; - outbuf[6] = (lu->qdev.blocksize >> 8) & 0xff; - outbuf[7] = lu->qdev.blocksize & 0xff; - break; case REQUEST_SENSE: - /* Just return "NO SENSE". */ - buflen = scsi_convert_sense(NULL, 0, outbuf, r->buflen, - (req->cmd.buf[1] & 1) == 0); - if (buflen < 0) { - goto illegal_request; - } - break; - case SYNCHRONIZE_CACHE: - /* The request is used as the AIO opaque value, so add a ref. */ - scsi_req_ref(&r->req); - block_acct_start(blk_get_stats(lu->qdev.conf.blk), &r->acct, 0, - BLOCK_ACCT_FLUSH); - r->req.aiocb = blk_aio_flush(lu->qdev.conf.blk, scsi_aio_complete, r); - return 0; - case VERIFY_10: - trace_ufs_scsi_emulate_command_VERIFY((req->cmd.buf[1] >> 1) & 3); - if (req->cmd.buf[1] & 6) { - goto illegal_request; - } - break; - case SERVICE_ACTION_IN_16: - /* Service Action In subcommands. */ - if ((req->cmd.buf[1] & 31) == SAI_READ_CAPACITY_16) { - trace_ufs_scsi_emulate_command_SAI_16(); - memset(outbuf, 0, req->cmd.xfer); - - if (lu->qdev.max_lba > 0) { - last_block = lu->qdev.max_lba - 1; - }; - outbuf[0] = 0; - outbuf[1] = 0; - outbuf[2] = 0; - outbuf[3] = 0; - outbuf[4] = (last_block >> 24) & 0xff; - outbuf[5] = (last_block >> 16) & 0xff; - outbuf[6] = (last_block >> 8) & 0xff; - outbuf[7] = last_block & 0xff; - outbuf[8] = (lu->qdev.blocksize >> 24) & 0xff; - outbuf[9] = (lu->qdev.blocksize >> 16) & 0xff; - outbuf[10] = (lu->qdev.blocksize >> 8) & 0xff; - outbuf[11] = lu->qdev.blocksize & 0xff; - outbuf[12] = 0; - outbuf[13] = get_physical_block_exp(&lu->qdev.conf); - - if (lu->unit_desc.provisioning_type == 2 || - lu->unit_desc.provisioning_type == 3) { - outbuf[14] = 0x80; - } - /* Protection, exponent and lowest lba field left blank. */ - break; - } - trace_ufs_scsi_emulate_command_SAI_unsupported(); - goto illegal_request; - case MODE_SELECT_10: - trace_ufs_scsi_emulate_command_MODE_SELECT_10(r->req.cmd.xfer); + /* Just return no sense data */ + len = scsi_build_sense_buf(outbuf, sizeof(outbuf), SENSE_CODE(NO_SENSE), + true); + scsi_status = GOOD; break; case START_STOP: - /* - * TODO: START_STOP is not yet implemented. It always returns success. - * Revisit it when ufs power management is implemented. - */ - trace_ufs_scsi_emulate_command_START_STOP(); - break; - case FORMAT_UNIT: - trace_ufs_scsi_emulate_command_FORMAT_UNIT(); - break; - case SEND_DIAGNOSTIC: - trace_ufs_scsi_emulate_command_SEND_DIAGNOSTIC(); - break; + /* TODO: Revisit it when Power Management is implemented */ + if (lun == UFS_UPIU_UFS_DEVICE_WLUN) { + scsi_status = GOOD; + break; + } + /* fallthrough */ default: - trace_ufs_scsi_emulate_command_UNKNOWN(buf[0], - scsi_command_name(buf[0])); - scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE)); - return 0; - } - assert(!r->req.aiocb); - r->iov.iov_len = MIN(r->buflen, req->cmd.xfer); - if (r->iov.iov_len == 0) { - scsi_req_complete(&r->req, GOOD); - } - if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { - assert(r->iov.iov_len == req->cmd.xfer); - return -r->iov.iov_len; - } else { - return r->iov.iov_len; + scsi_build_sense(sense_buf, SENSE_CODE(INVALID_OPCODE)); + scsi_status = CHECK_CONDITION; } -illegal_request: - if (r->req.status == -1) { - scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); + len = MIN(len, (int)req->data_len); + if (scsi_status == GOOD && len > 0 && + dma_buf_read(outbuf, len, NULL, req->sg, MEMTXATTRS_UNSPECIFIED) != + MEMTX_OK) { + return UFS_REQUEST_FAIL; } - return 0; + + ufs_build_scsi_response_upiu(req, sense_buf, sizeof(sense_buf), len, + scsi_status); + return UFS_REQUEST_SUCCESS; } -static void ufs_scsi_emulate_read_data(SCSIRequest *req) +static UfsReqResult ufs_process_scsi_cmd(UfsLu *lu, UfsRequest *req) { - UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req); - int buflen = r->iov.iov_len; - - if (buflen) { - trace_ufs_scsi_emulate_read_data(buflen); - r->iov.iov_len = 0; - r->started = true; - scsi_req_data(&r->req, buflen); - return; - } - - /* This also clears the sense buffer for REQUEST SENSE. */ - scsi_req_complete(&r->req, GOOD); -} - -static int ufs_scsi_check_mode_select(UfsLu *lu, int page, uint8_t *inbuf, - int inlen) -{ - uint8_t mode_current[SCSI_MAX_MODE_LEN]; - uint8_t mode_changeable[SCSI_MAX_MODE_LEN]; - uint8_t *p; - int len, expected_len, changeable_len, i; + uint8_t task_tag = req->req_upiu.header.task_tag; /* - * The input buffer does not include the page header, so it is - * off by 2 bytes. + * Each ufs-lu has its own independent virtual SCSI bus. Therefore, we can't + * use scsi_target_emulate_report_luns() which gets all lu information over + * the SCSI bus. Therefore, we use ufs_emulate_scsi_cmd() like the + * well-known lu. */ - expected_len = inlen + 2; - if (expected_len > SCSI_MAX_MODE_LEN) { - return -1; + if (req->req_upiu.sc.cdb[0] == REPORT_LUNS) { + return ufs_emulate_scsi_cmd(lu, req); } - /* MODE_PAGE_ALLS is only valid for MODE SENSE commands */ - if (page == MODE_PAGE_ALLS) { - return -1; + SCSIRequest *scsi_req = + scsi_req_new(lu->scsi_dev, task_tag, lu->lun, req->req_upiu.sc.cdb, + UFS_CDB_SIZE, req); + + uint32_t len = scsi_req_enqueue(scsi_req); + if (len) { + scsi_req_continue(scsi_req); } - p = mode_current; - memset(mode_current, 0, inlen + 2); - len = mode_sense_page(lu, page, &p, 0); - if (len < 0 || len != expected_len) { - return -1; - } - - p = mode_changeable; - memset(mode_changeable, 0, inlen + 2); - changeable_len = mode_sense_page(lu, page, &p, 1); - assert(changeable_len == len); - - /* - * Check that unchangeable bits are the same as what MODE SENSE - * would return. - */ - for (i = 2; i < len; i++) { - if (((mode_current[i] ^ inbuf[i - 2]) & ~mode_changeable[i]) != 0) { - return -1; - } - } - return 0; -} - -static void ufs_scsi_apply_mode_select(UfsLu *lu, int page, uint8_t *p) -{ - switch (page) { - case MODE_PAGE_CACHING: - blk_set_enable_write_cache(lu->qdev.conf.blk, (p[0] & 4) != 0); - break; - - default: - break; - } -} - -static int mode_select_pages(UfsSCSIReq *r, uint8_t *p, int len, bool change) -{ - UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev); - - while (len > 0) { - int page, page_len; - - page = p[0] & 0x3f; - if (p[0] & 0x40) { - goto invalid_param; - } else { - if (len < 2) { - goto invalid_param_len; - } - page_len = p[1]; - p += 2; - len -= 2; - } - - if (page_len > len) { - goto invalid_param_len; - } - - if (!change) { - if (ufs_scsi_check_mode_select(lu, page, p, page_len) < 0) { - goto invalid_param; - } - } else { - ufs_scsi_apply_mode_select(lu, page, p); - } - - p += page_len; - len -= page_len; - } - return 0; - -invalid_param: - scsi_check_condition(r, SENSE_CODE(INVALID_PARAM)); - return -1; - -invalid_param_len: - scsi_check_condition(r, SENSE_CODE(INVALID_PARAM_LEN)); - return -1; -} - -static void ufs_scsi_emulate_mode_select(UfsSCSIReq *r, uint8_t *inbuf) -{ - UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev); - uint8_t *p = inbuf; - int len = r->req.cmd.xfer; - int hdr_len = 8; - int bd_len; - int pass; - - /* We only support PF=1, SP=0. */ - if ((r->req.cmd.buf[1] & 0x11) != 0x10) { - goto invalid_field; - } - - if (len < hdr_len) { - goto invalid_param_len; - } - - bd_len = lduw_be_p(&p[6]); - if (bd_len != 0) { - goto invalid_param; - } - - len -= hdr_len; - p += hdr_len; - - /* Ensure no change is made if there is an error! */ - for (pass = 0; pass < 2; pass++) { - if (mode_select_pages(r, p, len, pass == 1) < 0) { - assert(pass == 0); - return; - } - } - - if (!blk_enable_write_cache(lu->qdev.conf.blk)) { - /* The request is used as the AIO opaque value, so add a ref. */ - scsi_req_ref(&r->req); - block_acct_start(blk_get_stats(lu->qdev.conf.blk), &r->acct, 0, - BLOCK_ACCT_FLUSH); - r->req.aiocb = blk_aio_flush(lu->qdev.conf.blk, scsi_aio_complete, r); - return; - } - - scsi_req_complete(&r->req, GOOD); - return; - -invalid_param: - scsi_check_condition(r, SENSE_CODE(INVALID_PARAM)); - return; - -invalid_param_len: - scsi_check_condition(r, SENSE_CODE(INVALID_PARAM_LEN)); - return; - -invalid_field: - scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); -} - -/* block_num and nb_blocks expected to be in qdev blocksize */ -static inline bool check_lba_range(UfsLu *lu, uint64_t block_num, - uint32_t nb_blocks) -{ - /* - * The first line tests that no overflow happens when computing the last - * block. The second line tests that the last accessed block is in - * range. - * - * Careful, the computations should not underflow for nb_blocks == 0, - * and a 0-block read to the first LBA beyond the end of device is - * valid. - */ - return (block_num <= block_num + nb_blocks && - block_num + nb_blocks <= lu->qdev.max_lba + 1); -} - -static void ufs_scsi_emulate_write_data(SCSIRequest *req) -{ - UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req); - - if (r->iov.iov_len) { - int buflen = r->iov.iov_len; - trace_ufs_scsi_emulate_write_data(buflen); - r->iov.iov_len = 0; - scsi_req_data(&r->req, buflen); - return; - } - - switch (req->cmd.buf[0]) { - case MODE_SELECT_10: - /* This also clears the sense buffer for REQUEST SENSE. */ - ufs_scsi_emulate_mode_select(r, r->iov.iov_base); - break; - default: - abort(); - } -} - -/* Return a pointer to the data buffer. */ -static uint8_t *ufs_scsi_get_buf(SCSIRequest *req) -{ - UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req); - - return (uint8_t *)r->iov.iov_base; -} - -static int32_t ufs_scsi_dma_command(SCSIRequest *req, uint8_t *buf) -{ - UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req); - UfsLu *lu = DO_UPCAST(UfsLu, qdev, req->dev); - uint32_t len; - uint8_t command; - - command = buf[0]; - - if (!blk_is_available(lu->qdev.conf.blk)) { - scsi_check_condition(r, SENSE_CODE(NO_MEDIUM)); - return 0; - } - - len = scsi_data_cdb_xfer(r->req.cmd.buf); - switch (command) { - case READ_6: - case READ_10: - trace_ufs_scsi_dma_command_READ(r->req.cmd.lba, len); - if (r->req.cmd.buf[1] & 0xe0) { - goto illegal_request; - } - if (!check_lba_range(lu, r->req.cmd.lba, len)) { - goto illegal_lba; - } - r->sector = r->req.cmd.lba * (lu->qdev.blocksize / BDRV_SECTOR_SIZE); - r->sector_count = len * (lu->qdev.blocksize / BDRV_SECTOR_SIZE); - break; - case WRITE_6: - case WRITE_10: - trace_ufs_scsi_dma_command_WRITE(r->req.cmd.lba, len); - if (!blk_is_writable(lu->qdev.conf.blk)) { - scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED)); - return 0; - } - if (r->req.cmd.buf[1] & 0xe0) { - goto illegal_request; - } - if (!check_lba_range(lu, r->req.cmd.lba, len)) { - goto illegal_lba; - } - r->sector = r->req.cmd.lba * (lu->qdev.blocksize / BDRV_SECTOR_SIZE); - r->sector_count = len * (lu->qdev.blocksize / BDRV_SECTOR_SIZE); - break; - default: - abort(); - illegal_request: - scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); - return 0; - illegal_lba: - scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE)); - return 0; - } - r->need_fua_emulation = ((r->req.cmd.buf[1] & 8) != 0); - if (r->sector_count == 0) { - scsi_req_complete(&r->req, GOOD); - } - assert(r->iov.iov_len == 0); - if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { - return -r->sector_count * BDRV_SECTOR_SIZE; - } else { - return r->sector_count * BDRV_SECTOR_SIZE; - } -} - -static void scsi_write_do_fua(UfsSCSIReq *r) -{ - UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev); - - assert(r->req.aiocb == NULL); - assert(!r->req.io_canceled); - - if (r->need_fua_emulation) { - block_acct_start(blk_get_stats(lu->qdev.conf.blk), &r->acct, 0, - BLOCK_ACCT_FLUSH); - r->req.aiocb = blk_aio_flush(lu->qdev.conf.blk, scsi_aio_complete, r); - return; - } - - scsi_req_complete(&r->req, GOOD); - scsi_req_unref(&r->req); -} - -static void scsi_dma_complete_noio(UfsSCSIReq *r, int ret) -{ - assert(r->req.aiocb == NULL); - if (ufs_scsi_req_check_error(r, ret, false)) { - goto done; - } - - r->sector += r->sector_count; - r->sector_count = 0; - if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { - scsi_write_do_fua(r); - return; - } else { - scsi_req_complete(&r->req, GOOD); - } - -done: - scsi_req_unref(&r->req); -} - -static void scsi_dma_complete(void *opaque, int ret) -{ - UfsSCSIReq *r = (UfsSCSIReq *)opaque; - UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev); - - assert(r->req.aiocb != NULL); - r->req.aiocb = NULL; - - aio_context_acquire(blk_get_aio_context(lu->qdev.conf.blk)); - if (ret < 0) { - block_acct_failed(blk_get_stats(lu->qdev.conf.blk), &r->acct); - } else { - block_acct_done(blk_get_stats(lu->qdev.conf.blk), &r->acct); - } - scsi_dma_complete_noio(r, ret); - aio_context_release(blk_get_aio_context(lu->qdev.conf.blk)); -} - -static BlockAIOCB *scsi_dma_readv(int64_t offset, QEMUIOVector *iov, - BlockCompletionFunc *cb, void *cb_opaque, - void *opaque) -{ - UfsSCSIReq *r = opaque; - UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev); - return blk_aio_preadv(lu->qdev.conf.blk, offset, iov, 0, cb, cb_opaque); -} - -static void scsi_init_iovec(UfsSCSIReq *r, size_t size) -{ - UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev); - - if (!r->iov.iov_base) { - r->buflen = size; - r->iov.iov_base = blk_blockalign(lu->qdev.conf.blk, r->buflen); - } - r->iov.iov_len = MIN(r->sector_count * BDRV_SECTOR_SIZE, r->buflen); - qemu_iovec_init_external(&r->qiov, &r->iov, 1); -} - -static void scsi_read_complete_noio(UfsSCSIReq *r, int ret) -{ - uint32_t n; - - assert(r->req.aiocb == NULL); - if (ufs_scsi_req_check_error(r, ret, false)) { - goto done; - } - - n = r->qiov.size / BDRV_SECTOR_SIZE; - r->sector += n; - r->sector_count -= n; - scsi_req_data(&r->req, r->qiov.size); - -done: - scsi_req_unref(&r->req); -} - -static void scsi_read_complete(void *opaque, int ret) -{ - UfsSCSIReq *r = (UfsSCSIReq *)opaque; - UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev); - - assert(r->req.aiocb != NULL); - r->req.aiocb = NULL; - trace_ufs_scsi_read_data_count(r->sector_count); - aio_context_acquire(blk_get_aio_context(lu->qdev.conf.blk)); - if (ret < 0) { - block_acct_failed(blk_get_stats(lu->qdev.conf.blk), &r->acct); - } else { - block_acct_done(blk_get_stats(lu->qdev.conf.blk), &r->acct); - trace_ufs_scsi_read_complete(r->req.tag, r->qiov.size); - } - scsi_read_complete_noio(r, ret); - aio_context_release(blk_get_aio_context(lu->qdev.conf.blk)); -} - -/* Actually issue a read to the block device. */ -static void scsi_do_read(UfsSCSIReq *r, int ret) -{ - UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev); - - assert(r->req.aiocb == NULL); - if (ufs_scsi_req_check_error(r, ret, false)) { - goto done; - } - - /* The request is used as the AIO opaque value, so add a ref. */ - scsi_req_ref(&r->req); - - if (r->req.sg) { - dma_acct_start(lu->qdev.conf.blk, &r->acct, r->req.sg, BLOCK_ACCT_READ); - r->req.residual -= r->req.sg->size; - r->req.aiocb = dma_blk_io( - blk_get_aio_context(lu->qdev.conf.blk), r->req.sg, - r->sector << BDRV_SECTOR_BITS, BDRV_SECTOR_SIZE, scsi_dma_readv, r, - scsi_dma_complete, r, DMA_DIRECTION_FROM_DEVICE); - } else { - scsi_init_iovec(r, SCSI_DMA_BUF_SIZE); - block_acct_start(blk_get_stats(lu->qdev.conf.blk), &r->acct, - r->qiov.size, BLOCK_ACCT_READ); - r->req.aiocb = scsi_dma_readv(r->sector << BDRV_SECTOR_BITS, &r->qiov, - scsi_read_complete, r, r); - } - -done: - scsi_req_unref(&r->req); -} - -static void scsi_do_read_cb(void *opaque, int ret) -{ - UfsSCSIReq *r = (UfsSCSIReq *)opaque; - UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev); - - assert(r->req.aiocb != NULL); - r->req.aiocb = NULL; - - aio_context_acquire(blk_get_aio_context(lu->qdev.conf.blk)); - if (ret < 0) { - block_acct_failed(blk_get_stats(lu->qdev.conf.blk), &r->acct); - } else { - block_acct_done(blk_get_stats(lu->qdev.conf.blk), &r->acct); - } - scsi_do_read(opaque, ret); - aio_context_release(blk_get_aio_context(lu->qdev.conf.blk)); -} - -/* Read more data from scsi device into buffer. */ -static void scsi_read_data(SCSIRequest *req) -{ - UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req); - UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev); - bool first; - - trace_ufs_scsi_read_data_count(r->sector_count); - if (r->sector_count == 0) { - /* This also clears the sense buffer for REQUEST SENSE. */ - scsi_req_complete(&r->req, GOOD); - return; - } - - /* No data transfer may already be in progress */ - assert(r->req.aiocb == NULL); - - /* The request is used as the AIO opaque value, so add a ref. */ - scsi_req_ref(&r->req); - if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { - trace_ufs_scsi_read_data_invalid(); - scsi_read_complete_noio(r, -EINVAL); - return; - } - - if (!blk_is_available(req->dev->conf.blk)) { - scsi_read_complete_noio(r, -ENOMEDIUM); - return; - } - - first = !r->started; - r->started = true; - if (first && r->need_fua_emulation) { - block_acct_start(blk_get_stats(lu->qdev.conf.blk), &r->acct, 0, - BLOCK_ACCT_FLUSH); - r->req.aiocb = blk_aio_flush(lu->qdev.conf.blk, scsi_do_read_cb, r); - } else { - scsi_do_read(r, 0); - } -} - -static void scsi_write_complete_noio(UfsSCSIReq *r, int ret) -{ - uint32_t n; - - assert(r->req.aiocb == NULL); - if (ufs_scsi_req_check_error(r, ret, false)) { - goto done; - } - - n = r->qiov.size / BDRV_SECTOR_SIZE; - r->sector += n; - r->sector_count -= n; - if (r->sector_count == 0) { - scsi_write_do_fua(r); - return; - } else { - scsi_init_iovec(r, SCSI_DMA_BUF_SIZE); - trace_ufs_scsi_write_complete_noio(r->req.tag, r->qiov.size); - scsi_req_data(&r->req, r->qiov.size); - } - -done: - scsi_req_unref(&r->req); -} - -static void scsi_write_complete(void *opaque, int ret) -{ - UfsSCSIReq *r = (UfsSCSIReq *)opaque; - UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev); - - assert(r->req.aiocb != NULL); - r->req.aiocb = NULL; - - aio_context_acquire(blk_get_aio_context(lu->qdev.conf.blk)); - if (ret < 0) { - block_acct_failed(blk_get_stats(lu->qdev.conf.blk), &r->acct); - } else { - block_acct_done(blk_get_stats(lu->qdev.conf.blk), &r->acct); - } - scsi_write_complete_noio(r, ret); - aio_context_release(blk_get_aio_context(lu->qdev.conf.blk)); -} - -static BlockAIOCB *scsi_dma_writev(int64_t offset, QEMUIOVector *iov, - BlockCompletionFunc *cb, void *cb_opaque, - void *opaque) -{ - UfsSCSIReq *r = opaque; - UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev); - return blk_aio_pwritev(lu->qdev.conf.blk, offset, iov, 0, cb, cb_opaque); -} - -static void scsi_write_data(SCSIRequest *req) -{ - UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req); - UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev); - - /* No data transfer may already be in progress */ - assert(r->req.aiocb == NULL); - - /* The request is used as the AIO opaque value, so add a ref. */ - scsi_req_ref(&r->req); - if (r->req.cmd.mode != SCSI_XFER_TO_DEV) { - trace_ufs_scsi_write_data_invalid(); - scsi_write_complete_noio(r, -EINVAL); - return; - } - - if (!r->req.sg && !r->qiov.size) { - /* Called for the first time. Ask the driver to send us more data. */ - r->started = true; - scsi_write_complete_noio(r, 0); - return; - } - if (!blk_is_available(req->dev->conf.blk)) { - scsi_write_complete_noio(r, -ENOMEDIUM); - return; - } - - if (r->req.sg) { - dma_acct_start(lu->qdev.conf.blk, &r->acct, r->req.sg, - BLOCK_ACCT_WRITE); - r->req.residual -= r->req.sg->size; - r->req.aiocb = dma_blk_io( - blk_get_aio_context(lu->qdev.conf.blk), r->req.sg, - r->sector << BDRV_SECTOR_BITS, BDRV_SECTOR_SIZE, scsi_dma_writev, r, - scsi_dma_complete, r, DMA_DIRECTION_TO_DEVICE); - } else { - block_acct_start(blk_get_stats(lu->qdev.conf.blk), &r->acct, - r->qiov.size, BLOCK_ACCT_WRITE); - r->req.aiocb = scsi_dma_writev(r->sector << BDRV_SECTOR_BITS, &r->qiov, - scsi_write_complete, r, r); - } -} - -static const SCSIReqOps ufs_scsi_emulate_reqops = { - .size = sizeof(UfsSCSIReq), - .free_req = ufs_scsi_free_request, - .send_command = ufs_scsi_emulate_command, - .read_data = ufs_scsi_emulate_read_data, - .write_data = ufs_scsi_emulate_write_data, - .get_buf = ufs_scsi_get_buf, -}; - -static const SCSIReqOps ufs_scsi_dma_reqops = { - .size = sizeof(UfsSCSIReq), - .free_req = ufs_scsi_free_request, - .send_command = ufs_scsi_dma_command, - .read_data = scsi_read_data, - .write_data = scsi_write_data, - .get_buf = ufs_scsi_get_buf, -}; - -/* - * Following commands are not yet supported - * PRE_FETCH(10), - * UNMAP, - * WRITE_BUFFER, READ_BUFFER, - * SECURITY_PROTOCOL_IN, SECURITY_PROTOCOL_OUT - */ -static const SCSIReqOps *const ufs_scsi_reqops_dispatch[256] = { - [TEST_UNIT_READY] = &ufs_scsi_emulate_reqops, - [INQUIRY] = &ufs_scsi_emulate_reqops, - [MODE_SENSE_10] = &ufs_scsi_emulate_reqops, - [START_STOP] = &ufs_scsi_emulate_reqops, - [READ_CAPACITY_10] = &ufs_scsi_emulate_reqops, - [REQUEST_SENSE] = &ufs_scsi_emulate_reqops, - [SYNCHRONIZE_CACHE] = &ufs_scsi_emulate_reqops, - [MODE_SELECT_10] = &ufs_scsi_emulate_reqops, - [VERIFY_10] = &ufs_scsi_emulate_reqops, - [FORMAT_UNIT] = &ufs_scsi_emulate_reqops, - [SERVICE_ACTION_IN_16] = &ufs_scsi_emulate_reqops, - [SEND_DIAGNOSTIC] = &ufs_scsi_emulate_reqops, - - [READ_6] = &ufs_scsi_dma_reqops, - [READ_10] = &ufs_scsi_dma_reqops, - [WRITE_6] = &ufs_scsi_dma_reqops, - [WRITE_10] = &ufs_scsi_dma_reqops, -}; - -static SCSIRequest *scsi_new_request(SCSIDevice *dev, uint32_t tag, - uint32_t lun, uint8_t *buf, - void *hba_private) -{ - UfsLu *lu = DO_UPCAST(UfsLu, qdev, dev); - SCSIRequest *req; - const SCSIReqOps *ops; - uint8_t command; - - command = buf[0]; - ops = ufs_scsi_reqops_dispatch[command]; - if (!ops) { - ops = &ufs_scsi_emulate_reqops; - } - req = scsi_req_alloc(ops, &lu->qdev, tag, lun, hba_private); - - return req; + return UFS_REQUEST_NO_COMPLETE; } static Property ufs_lu_props[] = { - DEFINE_PROP_DRIVE("drive", UfsLu, qdev.conf.blk), + DEFINE_PROP_DRIVE("drive", UfsLu, conf.blk), + DEFINE_PROP_UINT8("lun", UfsLu, lun, 0), DEFINE_PROP_END_OF_LIST(), }; -static bool ufs_lu_brdv_init(UfsLu *lu, Error **errp) -{ - SCSIDevice *dev = &lu->qdev; - bool read_only; - - if (!lu->qdev.conf.blk) { - error_setg(errp, "drive property not set"); - return false; - } - - if (!blkconf_blocksizes(&lu->qdev.conf, errp)) { - return false; - } - - if (blk_get_aio_context(lu->qdev.conf.blk) != qemu_get_aio_context() && - !lu->qdev.hba_supports_iothread) { - error_setg(errp, "HBA does not support iothreads"); - return false; - } - - read_only = !blk_supports_write_perm(lu->qdev.conf.blk); - - if (!blkconf_apply_backend_options(&dev->conf, read_only, - dev->type == TYPE_DISK, errp)) { - return false; - } - - if (blk_is_sg(lu->qdev.conf.blk)) { - error_setg(errp, "unwanted /dev/sg*"); - return false; - } - - blk_iostatus_enable(lu->qdev.conf.blk); - return true; -} - static bool ufs_add_lu(UfsHc *u, UfsLu *lu, Error **errp) { - BlockBackend *blk = lu->qdev.conf.blk; + BlockBackend *blk = lu->conf.blk; int64_t brdv_len = blk_getlength(blk); uint64_t raw_dev_cap = be64_to_cpu(u->geometry_desc.total_raw_device_capacity); @@ -1288,156 +304,143 @@ static bool ufs_add_lu(UfsHc *u, UfsLu *lu, Error **errp) return true; } -static inline uint8_t ufs_log2(uint64_t input) +void ufs_init_wlu(UfsLu *wlu, uint8_t wlun) { - int log = 0; - while (input >>= 1) { - log++; - } - return log; + wlu->lun = wlun; + wlu->scsi_op = &ufs_emulate_scsi_cmd; } static void ufs_init_lu(UfsLu *lu) { - BlockBackend *blk = lu->qdev.conf.blk; + BlockBackend *blk = lu->conf.blk; int64_t brdv_len = blk_getlength(blk); - lu->lun = lu->qdev.lun; memset(&lu->unit_desc, 0, sizeof(lu->unit_desc)); lu->unit_desc.length = sizeof(UnitDescriptor); lu->unit_desc.descriptor_idn = UFS_QUERY_DESC_IDN_UNIT; lu->unit_desc.lu_enable = 0x01; - lu->unit_desc.logical_block_size = ufs_log2(lu->qdev.blocksize); - lu->unit_desc.unit_index = lu->qdev.lun; + lu->unit_desc.logical_block_size = UFS_BLOCK_SIZE_SHIFT; + lu->unit_desc.unit_index = lu->lun; lu->unit_desc.logical_block_count = cpu_to_be64(brdv_len / (1 << lu->unit_desc.logical_block_size)); + + lu->scsi_op = &ufs_process_scsi_cmd; } static bool ufs_lu_check_constraints(UfsLu *lu, Error **errp) { - if (!lu->qdev.conf.blk) { + if (!lu->conf.blk) { error_setg(errp, "drive property not set"); return false; } - if (lu->qdev.channel != 0) { - error_setg(errp, "ufs logical unit does not support channel"); - return false; - } - - if (lu->qdev.lun >= UFS_MAX_LUS) { - error_setg(errp, "lun must be between 1 and %d", UFS_MAX_LUS - 1); + if (lu->lun >= UFS_MAX_LUS) { + error_setg(errp, "lun must be between 0 and %d", UFS_MAX_LUS - 1); return false; } return true; } -static void ufs_lu_realize(SCSIDevice *dev, Error **errp) +static void ufs_init_scsi_device(UfsLu *lu, BlockBackend *blk, Error **errp) +{ + DeviceState *scsi_dev; + + scsi_bus_init(&lu->bus, sizeof(lu->bus), DEVICE(lu), &ufs_scsi_info); + + blk_ref(blk); + blk_detach_dev(blk, DEVICE(lu)); + lu->conf.blk = NULL; + + /* + * The ufs-lu is the device that is wrapping the scsi-hd. It owns a virtual + * SCSI bus that serves the scsi-hd. + */ + scsi_dev = qdev_new("scsi-hd"); + object_property_add_child(OBJECT(&lu->bus), "ufs-scsi", OBJECT(scsi_dev)); + + qdev_prop_set_uint32(scsi_dev, "physical_block_size", UFS_BLOCK_SIZE); + qdev_prop_set_uint32(scsi_dev, "logical_block_size", UFS_BLOCK_SIZE); + qdev_prop_set_uint32(scsi_dev, "scsi-id", 0); + qdev_prop_set_uint32(scsi_dev, "lun", lu->lun); + if (!qdev_prop_set_drive_err(scsi_dev, "drive", blk, errp)) { + object_unparent(OBJECT(scsi_dev)); + return; + } + + if (!qdev_realize_and_unref(scsi_dev, &lu->bus.qbus, errp)) { + object_unparent(OBJECT(scsi_dev)); + return; + } + + blk_unref(blk); + lu->scsi_dev = SCSI_DEVICE(scsi_dev); +} + +static void ufs_lu_realize(DeviceState *dev, Error **errp) { UfsLu *lu = DO_UPCAST(UfsLu, qdev, dev); - BusState *s = qdev_get_parent_bus(&dev->qdev); + BusState *s = qdev_get_parent_bus(dev); UfsHc *u = UFS(s->parent); - AioContext *ctx = NULL; - uint64_t nb_sectors, nb_blocks; + BlockBackend *blk = lu->conf.blk; if (!ufs_lu_check_constraints(lu, errp)) { return; } - ctx = blk_get_aio_context(lu->qdev.conf.blk); - aio_context_acquire(ctx); - if (!blkconf_blocksizes(&lu->qdev.conf, errp)) { - goto out; - } - - lu->qdev.blocksize = UFS_BLOCK_SIZE; - blk_get_geometry(lu->qdev.conf.blk, &nb_sectors); - nb_blocks = nb_sectors / (lu->qdev.blocksize / BDRV_SECTOR_SIZE); - if (nb_blocks > UINT32_MAX) { - nb_blocks = UINT32_MAX; - } - lu->qdev.max_lba = nb_blocks; - lu->qdev.type = TYPE_DISK; - - ufs_init_lu(lu); - if (!ufs_add_lu(u, lu, errp)) { - goto out; - } - - ufs_lu_brdv_init(lu, errp); - -out: - aio_context_release(ctx); -} - -static void ufs_lu_unrealize(SCSIDevice *dev) -{ - UfsLu *lu = DO_UPCAST(UfsLu, qdev, dev); - - blk_drain(lu->qdev.conf.blk); -} - -static void ufs_wlu_realize(DeviceState *qdev, Error **errp) -{ - UfsWLu *wlu = UFSWLU(qdev); - SCSIDevice *dev = &wlu->qdev; - - if (!is_wlun(dev->lun)) { - error_setg(errp, "not well-known logical unit number"); + if (!blk) { + error_setg(errp, "drive property not set"); return; } - QTAILQ_INIT(&dev->requests); + if (!blkconf_blocksizes(&lu->conf, errp)) { + return; + } + + if (!blkconf_apply_backend_options(&lu->conf, !blk_supports_write_perm(blk), + true, errp)) { + return; + } + + ufs_init_lu(lu); + if (!ufs_add_lu(u, lu, errp)) { + return; + } + + ufs_init_scsi_device(lu, blk, errp); +} + +static void ufs_lu_unrealize(DeviceState *dev) +{ + UfsLu *lu = DO_UPCAST(UfsLu, qdev, dev); + + if (lu->scsi_dev) { + object_unref(OBJECT(lu->scsi_dev)); + lu->scsi_dev = NULL; + } } static void ufs_lu_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); - SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(oc); - sc->realize = ufs_lu_realize; - sc->unrealize = ufs_lu_unrealize; - sc->alloc_req = scsi_new_request; + dc->realize = ufs_lu_realize; + dc->unrealize = ufs_lu_unrealize; dc->bus_type = TYPE_UFS_BUS; device_class_set_props(dc, ufs_lu_props); dc->desc = "Virtual UFS logical unit"; } -static void ufs_wlu_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(oc); - - /* - * The realize() function of TYPE_SCSI_DEVICE causes a segmentation fault - * if a block drive does not exist. Define a new realize function for - * well-known LUs that do not have a block drive. - */ - dc->realize = ufs_wlu_realize; - sc->alloc_req = scsi_new_request; - dc->bus_type = TYPE_UFS_BUS; - dc->desc = "Virtual UFS well-known logical unit"; -} - static const TypeInfo ufs_lu_info = { .name = TYPE_UFS_LU, - .parent = TYPE_SCSI_DEVICE, + .parent = TYPE_DEVICE, .class_init = ufs_lu_class_init, .instance_size = sizeof(UfsLu), }; -static const TypeInfo ufs_wlu_info = { - .name = TYPE_UFS_WLU, - .parent = TYPE_SCSI_DEVICE, - .class_init = ufs_wlu_class_init, - .instance_size = sizeof(UfsWLu), -}; - static void ufs_lu_register_types(void) { type_register_static(&ufs_lu_info); - type_register_static(&ufs_wlu_info); } type_init(ufs_lu_register_types) diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events index 1e55fb0d08..665e1a942b 100644 --- a/hw/ufs/trace-events +++ b/hw/ufs/trace-events @@ -12,31 +12,6 @@ ufs_exec_scsi_cmd(uint32_t slot, uint8_t lun, uint8_t opcode) "slot %"PRIu32", l ufs_exec_query_cmd(uint32_t slot, uint8_t opcode) "slot %"PRIu32", opcode 0x%"PRIx8"" ufs_process_uiccmd(uint32_t uiccmd, uint32_t ucmdarg1, uint32_t ucmdarg2, uint32_t ucmdarg3) "uiccmd 0x%"PRIx32", ucmdarg1 0x%"PRIx32", ucmdarg2 0x%"PRIx32", ucmdarg3 0x%"PRIx32"" -# lu.c -ufs_scsi_check_condition(uint32_t tag, uint8_t key, uint8_t asc, uint8_t ascq) "Command complete tag=0x%x sense=%d/%d/%d" -ufs_scsi_read_complete(uint32_t tag, size_t size) "Data ready tag=0x%x len=%zd" -ufs_scsi_read_data_count(uint32_t sector_count) "Read sector_count=%d" -ufs_scsi_read_data_invalid(void) "Data transfer direction invalid" -ufs_scsi_write_complete_noio(uint32_t tag, size_t size) "Write complete tag=0x%x more=%zd" -ufs_scsi_write_data_invalid(void) "Data transfer direction invalid" -ufs_scsi_emulate_vpd_page_00(size_t xfer) "Inquiry EVPD[Supported pages] buffer size %zd" -ufs_scsi_emulate_vpd_page_80_not_supported(void) "Inquiry EVPD[Serial number] not supported" -ufs_scsi_emulate_vpd_page_80(size_t xfer) "Inquiry EVPD[Serial number] buffer size %zd" -ufs_scsi_emulate_vpd_page_87(size_t xfer) "Inquiry EVPD[Mode Page Policy] buffer size %zd" -ufs_scsi_emulate_mode_sense(int cmd, int page, size_t xfer, int control) "Mode Sense(%d) (page %d, xfer %zd, page_control %d)" -ufs_scsi_emulate_read_data(int buflen) "Read buf_len=%d" -ufs_scsi_emulate_write_data(int buflen) "Write buf_len=%d" -ufs_scsi_emulate_command_START_STOP(void) "START STOP UNIT" -ufs_scsi_emulate_command_FORMAT_UNIT(void) "FORMAT UNIT" -ufs_scsi_emulate_command_SEND_DIAGNOSTIC(void) "SEND DIAGNOSTIC" -ufs_scsi_emulate_command_SAI_16(void) "SAI READ CAPACITY(16)" -ufs_scsi_emulate_command_SAI_unsupported(void) "Unsupported Service Action In" -ufs_scsi_emulate_command_MODE_SELECT_10(size_t xfer) "Mode Select(10) (len %zd)" -ufs_scsi_emulate_command_VERIFY(int bytchk) "Verify (bytchk %d)" -ufs_scsi_emulate_command_UNKNOWN(int cmd, const char *name) "Unknown SCSI command (0x%2.2x=%s)" -ufs_scsi_dma_command_READ(uint64_t lba, uint32_t len) "Read (block %" PRIu64 ", count %u)" -ufs_scsi_dma_command_WRITE(uint64_t lba, int len) "Write (block %" PRIu64 ", count %u)" - # error condition ufs_err_dma_read_utrd(uint32_t slot, uint64_t addr) "failed to read utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64"" ufs_err_dma_read_req_upiu(uint32_t slot, uint64_t addr) "failed to read req upiu. UTRLDBR slot %"PRIu32", request upiu addr %"PRIu64"" diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index 2e6d582cc3..68c5f1f6c9 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -24,6 +24,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "migration/vmstate.h" +#include "scsi/constants.h" #include "trace.h" #include "ufs.h" @@ -62,8 +63,6 @@ static MemTxResult ufs_addr_write(UfsHc *u, hwaddr addr, const void *buf, return pci_dma_write(PCI_DEVICE(u), addr, buf, size); } -static void ufs_complete_req(UfsRequest *req, UfsReqResult req_result); - static inline hwaddr ufs_get_utrd_addr(UfsHc *u, uint32_t slot) { hwaddr utrl_base_addr = (((hwaddr)u->reg.utrlbau) << 32) + u->reg.utrlba; @@ -163,11 +162,13 @@ static MemTxResult ufs_dma_read_prdt(UfsRequest *req) req->sg = g_malloc0(sizeof(QEMUSGList)); pci_dma_sglist_init(req->sg, PCI_DEVICE(u), prdt_len); + req->data_len = 0; for (uint16_t i = 0; i < prdt_len; ++i) { hwaddr data_dma_addr = le64_to_cpu(prd_entries[i].addr); uint32_t data_byte_count = le32_to_cpu(prd_entries[i].size) + 1; qemu_sglist_add(req->sg, data_dma_addr, data_byte_count); + req->data_len += data_byte_count; } return MEMTX_OK; } @@ -433,23 +434,10 @@ static const MemoryRegionOps ufs_mmio_ops = { }, }; -static QEMUSGList *ufs_get_sg_list(SCSIRequest *scsi_req) -{ - UfsRequest *req = scsi_req->hba_private; - return req->sg; -} -static void ufs_build_upiu_sense_data(UfsRequest *req, SCSIRequest *scsi_req) -{ - req->rsp_upiu.sr.sense_data_len = cpu_to_be16(scsi_req->sense_len); - assert(scsi_req->sense_len <= SCSI_SENSE_LEN); - memcpy(req->rsp_upiu.sr.sense_data, scsi_req->sense, scsi_req->sense_len); -} - -static void ufs_build_upiu_header(UfsRequest *req, uint8_t trans_type, - uint8_t flags, uint8_t response, - uint8_t scsi_status, - uint16_t data_segment_length) +void ufs_build_upiu_header(UfsRequest *req, uint8_t trans_type, uint8_t flags, + uint8_t response, uint8_t scsi_status, + uint16_t data_segment_length) { memcpy(&req->rsp_upiu.header, &req->req_upiu.header, sizeof(UtpUpiuHeader)); req->rsp_upiu.header.trans_type = trans_type; @@ -459,96 +447,38 @@ static void ufs_build_upiu_header(UfsRequest *req, uint8_t trans_type, req->rsp_upiu.header.data_segment_length = cpu_to_be16(data_segment_length); } -static void ufs_scsi_command_complete(SCSIRequest *scsi_req, size_t resid) -{ - UfsRequest *req = scsi_req->hba_private; - int16_t status = scsi_req->status; - uint32_t expected_len = be32_to_cpu(req->req_upiu.sc.exp_data_transfer_len); - uint32_t transfered_len = scsi_req->cmd.xfer - resid; - uint8_t flags = 0, response = UFS_COMMAND_RESULT_SUCESS; - uint16_t data_segment_length; - - if (expected_len > transfered_len) { - req->rsp_upiu.sr.residual_transfer_count = - cpu_to_be32(expected_len - transfered_len); - flags |= UFS_UPIU_FLAG_UNDERFLOW; - } else if (expected_len < transfered_len) { - req->rsp_upiu.sr.residual_transfer_count = - cpu_to_be32(transfered_len - expected_len); - flags |= UFS_UPIU_FLAG_OVERFLOW; - } - - if (status != 0) { - ufs_build_upiu_sense_data(req, scsi_req); - response = UFS_COMMAND_RESULT_FAIL; - } - - data_segment_length = cpu_to_be16(scsi_req->sense_len + - sizeof(req->rsp_upiu.sr.sense_data_len)); - ufs_build_upiu_header(req, UFS_UPIU_TRANSACTION_RESPONSE, flags, response, - status, data_segment_length); - - ufs_complete_req(req, UFS_REQUEST_SUCCESS); - - scsi_req->hba_private = NULL; - scsi_req_unref(scsi_req); -} - -static const struct SCSIBusInfo ufs_scsi_info = { - .tcq = true, - .max_target = 0, - .max_lun = UFS_MAX_LUS, - .max_channel = 0, - - .get_sg_list = ufs_get_sg_list, - .complete = ufs_scsi_command_complete, -}; - static UfsReqResult ufs_exec_scsi_cmd(UfsRequest *req) { UfsHc *u = req->hc; uint8_t lun = req->req_upiu.header.lun; - uint8_t task_tag = req->req_upiu.header.task_tag; - SCSIDevice *dev = NULL; + + UfsLu *lu = NULL; trace_ufs_exec_scsi_cmd(req->slot, lun, req->req_upiu.sc.cdb[0]); - if (!is_wlun(lun)) { - if (lun >= u->device_desc.number_lu) { - trace_ufs_err_scsi_cmd_invalid_lun(lun); - return UFS_REQUEST_FAIL; - } else if (u->lus[lun] == NULL) { - trace_ufs_err_scsi_cmd_invalid_lun(lun); - return UFS_REQUEST_FAIL; - } + if (!is_wlun(lun) && (lun >= UFS_MAX_LUS || u->lus[lun] == NULL)) { + trace_ufs_err_scsi_cmd_invalid_lun(lun); + return UFS_REQUEST_FAIL; } switch (lun) { case UFS_UPIU_REPORT_LUNS_WLUN: - dev = &u->report_wlu->qdev; + lu = &u->report_wlu; break; case UFS_UPIU_UFS_DEVICE_WLUN: - dev = &u->dev_wlu->qdev; + lu = &u->dev_wlu; break; case UFS_UPIU_BOOT_WLUN: - dev = &u->boot_wlu->qdev; + lu = &u->boot_wlu; break; case UFS_UPIU_RPMB_WLUN: - dev = &u->rpmb_wlu->qdev; + lu = &u->rpmb_wlu; break; default: - dev = &u->lus[lun]->qdev; + lu = u->lus[lun]; } - SCSIRequest *scsi_req = scsi_req_new( - dev, task_tag, lun, req->req_upiu.sc.cdb, UFS_CDB_SIZE, req); - - uint32_t len = scsi_req_enqueue(scsi_req); - if (len) { - scsi_req_continue(scsi_req); - } - - return UFS_REQUEST_NO_COMPLETE; + return lu->scsi_op(lu, req); } static UfsReqResult ufs_exec_nop_cmd(UfsRequest *req) @@ -1137,7 +1067,7 @@ static void ufs_process_req(void *opaque) } } -static void ufs_complete_req(UfsRequest *req, UfsReqResult req_result) +void ufs_complete_req(UfsRequest *req, UfsReqResult req_result) { UfsHc *u = req->hc; assert(req->state == UFS_REQUEST_RUNNING); @@ -1159,6 +1089,7 @@ static void ufs_clear_req(UfsRequest *req) qemu_sglist_destroy(req->sg); g_free(req->sg); req->sg = NULL; + req->data_len = 0; } memset(&req->utrd, 0, sizeof(req->utrd)); @@ -1317,28 +1248,6 @@ static void ufs_init_hc(UfsHc *u) u->flags.permanently_disable_fw_update = 1; } -static bool ufs_init_wlu(UfsHc *u, UfsWLu **wlu, uint8_t wlun, Error **errp) -{ - UfsWLu *new_wlu = UFSWLU(qdev_new(TYPE_UFS_WLU)); - - qdev_prop_set_uint32(DEVICE(new_wlu), "lun", wlun); - - /* - * The well-known lu shares the same bus as the normal lu. If the well-known - * lu writes the same channel value as the normal lu, the report will be - * made not only for the normal lu but also for the well-known lu at - * REPORT_LUN time. To prevent this, the channel value of normal lu is fixed - * to 0 and the channel value of well-known lu is fixed to 1. - */ - qdev_prop_set_uint32(DEVICE(new_wlu), "channel", 1); - if (!qdev_realize_and_unref(DEVICE(new_wlu), BUS(&u->bus), errp)) { - return false; - } - - *wlu = new_wlu; - return true; -} - static void ufs_realize(PCIDevice *pci_dev, Error **errp) { UfsHc *u = UFS(pci_dev); @@ -1349,53 +1258,21 @@ static void ufs_realize(PCIDevice *pci_dev, Error **errp) qbus_init(&u->bus, sizeof(UfsBus), TYPE_UFS_BUS, &pci_dev->qdev, u->parent_obj.qdev.id); - u->bus.parent_bus.info = &ufs_scsi_info; ufs_init_state(u); ufs_init_hc(u); ufs_init_pci(u, pci_dev); - if (!ufs_init_wlu(u, &u->report_wlu, UFS_UPIU_REPORT_LUNS_WLUN, errp)) { - return; - } - - if (!ufs_init_wlu(u, &u->dev_wlu, UFS_UPIU_UFS_DEVICE_WLUN, errp)) { - return; - } - - if (!ufs_init_wlu(u, &u->boot_wlu, UFS_UPIU_BOOT_WLUN, errp)) { - return; - } - - if (!ufs_init_wlu(u, &u->rpmb_wlu, UFS_UPIU_RPMB_WLUN, errp)) { - return; - } + ufs_init_wlu(&u->report_wlu, UFS_UPIU_REPORT_LUNS_WLUN); + ufs_init_wlu(&u->dev_wlu, UFS_UPIU_UFS_DEVICE_WLUN); + ufs_init_wlu(&u->boot_wlu, UFS_UPIU_BOOT_WLUN); + ufs_init_wlu(&u->rpmb_wlu, UFS_UPIU_RPMB_WLUN); } static void ufs_exit(PCIDevice *pci_dev) { UfsHc *u = UFS(pci_dev); - if (u->dev_wlu) { - object_unref(OBJECT(u->dev_wlu)); - u->dev_wlu = NULL; - } - - if (u->report_wlu) { - object_unref(OBJECT(u->report_wlu)); - u->report_wlu = NULL; - } - - if (u->rpmb_wlu) { - object_unref(OBJECT(u->rpmb_wlu)); - u->rpmb_wlu = NULL; - } - - if (u->boot_wlu) { - object_unref(OBJECT(u->boot_wlu)); - u->boot_wlu = NULL; - } - qemu_bh_delete(u->doorbell_bh); qemu_bh_delete(u->complete_bh); @@ -1437,43 +1314,18 @@ static void ufs_class_init(ObjectClass *oc, void *data) static bool ufs_bus_check_address(BusState *qbus, DeviceState *qdev, Error **errp) { - SCSIDevice *dev = SCSI_DEVICE(qdev); - UfsBusClass *ubc = UFS_BUS_GET_CLASS(qbus); - UfsHc *u = UFS(qbus->parent); - - if (strcmp(object_get_typename(OBJECT(dev)), TYPE_UFS_WLU) == 0) { - if (dev->lun != UFS_UPIU_REPORT_LUNS_WLUN && - dev->lun != UFS_UPIU_UFS_DEVICE_WLUN && - dev->lun != UFS_UPIU_BOOT_WLUN && dev->lun != UFS_UPIU_RPMB_WLUN) { - error_setg(errp, "bad well-known lun: %d", dev->lun); - return false; - } - - if ((dev->lun == UFS_UPIU_REPORT_LUNS_WLUN && u->report_wlu != NULL) || - (dev->lun == UFS_UPIU_UFS_DEVICE_WLUN && u->dev_wlu != NULL) || - (dev->lun == UFS_UPIU_BOOT_WLUN && u->boot_wlu != NULL) || - (dev->lun == UFS_UPIU_RPMB_WLUN && u->rpmb_wlu != NULL)) { - error_setg(errp, "well-known lun %d already exists", dev->lun); - return false; - } - - return true; - } - - if (strcmp(object_get_typename(OBJECT(dev)), TYPE_UFS_LU) != 0) { + if (strcmp(object_get_typename(OBJECT(qdev)), TYPE_UFS_LU) != 0) { error_setg(errp, "%s cannot be connected to ufs-bus", - object_get_typename(OBJECT(dev))); + object_get_typename(OBJECT(qdev))); return false; } - return ubc->parent_check_address(qbus, qdev, errp); + return true; } static void ufs_bus_class_init(ObjectClass *class, void *data) { BusClass *bc = BUS_CLASS(class); - UfsBusClass *ubc = UFS_BUS_CLASS(class); - ubc->parent_check_address = bc->check_address; bc->check_address = ufs_bus_check_address; } @@ -1487,7 +1339,7 @@ static const TypeInfo ufs_info = { static const TypeInfo ufs_bus_info = { .name = TYPE_UFS_BUS, - .parent = TYPE_SCSI_BUS, + .parent = TYPE_BUS, .class_init = ufs_bus_class_init, .class_size = sizeof(UfsBusClass), .instance_size = sizeof(UfsBus), diff --git a/hw/ufs/ufs.h b/hw/ufs/ufs.h index f244228617..8fda94f4ef 100644 --- a/hw/ufs/ufs.h +++ b/hw/ufs/ufs.h @@ -16,7 +16,8 @@ #include "block/ufs.h" #define UFS_MAX_LUS 32 -#define UFS_BLOCK_SIZE 4096 +#define UFS_BLOCK_SIZE_SHIFT 12 +#define UFS_BLOCK_SIZE (1 << UFS_BLOCK_SIZE_SHIFT) typedef struct UfsBusClass { BusClass parent_class; @@ -24,7 +25,7 @@ typedef struct UfsBusClass { } UfsBusClass; typedef struct UfsBus { - SCSIBus parent_bus; + BusState parent_bus; } UfsBus; #define TYPE_UFS_BUS "ufs-bus" @@ -55,19 +56,22 @@ typedef struct UfsRequest { /* for scsi command */ QEMUSGList *sg; + uint32_t data_len; } UfsRequest; +struct UfsLu; +typedef UfsReqResult (*UfsScsiOp)(struct UfsLu *, UfsRequest *); + typedef struct UfsLu { - SCSIDevice qdev; + DeviceState qdev; uint8_t lun; UnitDescriptor unit_desc; + SCSIBus bus; + SCSIDevice *scsi_dev; + BlockConf conf; + UfsScsiOp scsi_op; } UfsLu; -typedef struct UfsWLu { - SCSIDevice qdev; - uint8_t lun; -} UfsWLu; - typedef struct UfsParams { char *serial; uint8_t nutrs; /* Number of UTP Transfer Request Slots */ @@ -84,10 +88,10 @@ typedef struct UfsHc { UfsRequest *req_list; UfsLu *lus[UFS_MAX_LUS]; - UfsWLu *report_wlu; - UfsWLu *dev_wlu; - UfsWLu *boot_wlu; - UfsWLu *rpmb_wlu; + UfsLu report_wlu; + UfsLu dev_wlu; + UfsLu boot_wlu; + UfsLu rpmb_wlu; DeviceDescriptor device_desc; GeometryDescriptor geometry_desc; Attributes attributes; @@ -104,9 +108,6 @@ typedef struct UfsHc { #define TYPE_UFS_LU "ufs-lu" #define UFSLU(obj) OBJECT_CHECK(UfsLu, (obj), TYPE_UFS_LU) -#define TYPE_UFS_WLU "ufs-wlu" -#define UFSWLU(obj) OBJECT_CHECK(UfsWLu, (obj), TYPE_UFS_WLU) - typedef enum UfsQueryFlagPerm { UFS_QUERY_FLAG_NONE = 0x0, UFS_QUERY_FLAG_READ = 0x1, @@ -128,4 +129,9 @@ static inline bool is_wlun(uint8_t lun) lun == UFS_UPIU_RPMB_WLUN); } +void ufs_build_upiu_header(UfsRequest *req, uint8_t trans_type, uint8_t flags, + uint8_t response, uint8_t scsi_status, + uint16_t data_segment_length); +void ufs_complete_req(UfsRequest *req, UfsReqResult req_result); +void ufs_init_wlu(UfsLu *wlu, uint8_t wlun); #endif /* HW_UFS_UFS_H */ diff --git a/include/block/ufs.h b/include/block/ufs.h index 7631a5af10..0b6ec0814d 100644 --- a/include/block/ufs.h +++ b/include/block/ufs.h @@ -379,7 +379,7 @@ typedef struct Attributes { /* Command response result code */ typedef enum CommandRespCode { - UFS_COMMAND_RESULT_SUCESS = 0x00, + UFS_COMMAND_RESULT_SUCCESS = 0x00, UFS_COMMAND_RESULT_FAIL = 0x01, } CommandRespCode; diff --git a/tests/qtest/ufs-test.c b/tests/qtest/ufs-test.c index 15d467630c..5daf8c9c49 100644 --- a/tests/qtest/ufs-test.c +++ b/tests/qtest/ufs-test.c @@ -425,6 +425,9 @@ static void ufstest_init(void *obj, void *data, QGuestAllocator *alloc) const uint8_t test_unit_ready_cdb[UFS_CDB_SIZE] = { TEST_UNIT_READY, }; + const uint8_t request_sense_cdb[UFS_CDB_SIZE] = { + REQUEST_SENSE, + }; UtpTransferReqDesc utrd; UtpUpiuRsp rsp_upiu; @@ -440,6 +443,12 @@ static void ufstest_init(void *obj, void *data, QGuestAllocator *alloc) /* There is one logical unit whose lun is 0 */ g_assert_cmpuint(buf[9], ==, 0); + /* Clear Unit Attention */ + ufs_send_scsi_command(ufs, 0, 0, request_sense_cdb, NULL, 0, buf, + sizeof(buf), &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, CHECK_CONDITION); + /* Check TEST_UNIT_READY */ ufs_send_scsi_command(ufs, 0, 0, test_unit_ready_cdb, NULL, 0, NULL, 0, &utrd, &rsp_upiu); @@ -473,6 +482,9 @@ static void ufstest_read_write(void *obj, void *data, QGuestAllocator *alloc) 0x00, 0x00 }; + const uint8_t request_sense_cdb[UFS_CDB_SIZE] = { + REQUEST_SENSE, + }; const uint8_t read_cdb[UFS_CDB_SIZE] = { /* READ(10) to LBA 0, transfer length 1 */ READ_10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 @@ -484,32 +496,39 @@ static void ufstest_read_write(void *obj, void *data, QGuestAllocator *alloc) uint32_t block_size; UtpTransferReqDesc utrd; UtpUpiuRsp rsp_upiu; + const int test_lun = 1; ufs_init(ufs, alloc); + /* Clear Unit Attention */ + ufs_send_scsi_command(ufs, 0, test_lun, request_sense_cdb, NULL, 0, + read_buf, sizeof(read_buf), &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, CHECK_CONDITION); + /* Read capacity */ - ufs_send_scsi_command(ufs, 0, 1, read_capacity_cdb, NULL, 0, read_buf, - sizeof(read_buf), &utrd, &rsp_upiu); + ufs_send_scsi_command(ufs, 0, test_lun, read_capacity_cdb, NULL, 0, + read_buf, sizeof(read_buf), &utrd, &rsp_upiu); g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, - UFS_COMMAND_RESULT_SUCESS); + UFS_COMMAND_RESULT_SUCCESS); block_size = ldl_be_p(&read_buf[8]); g_assert_cmpuint(block_size, ==, 4096); /* Write data */ memset(write_buf, 0xab, block_size); - ufs_send_scsi_command(ufs, 0, 1, write_cdb, write_buf, block_size, NULL, 0, - &utrd, &rsp_upiu); + ufs_send_scsi_command(ufs, 0, test_lun, write_cdb, write_buf, block_size, + NULL, 0, &utrd, &rsp_upiu); g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, - UFS_COMMAND_RESULT_SUCESS); + UFS_COMMAND_RESULT_SUCCESS); /* Read data and verify */ - ufs_send_scsi_command(ufs, 0, 1, read_cdb, NULL, 0, read_buf, block_size, - &utrd, &rsp_upiu); + ufs_send_scsi_command(ufs, 0, test_lun, read_cdb, NULL, 0, read_buf, + block_size, &utrd, &rsp_upiu); g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, - UFS_COMMAND_RESULT_SUCESS); + UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpint(memcmp(read_buf, write_buf, block_size), ==, 0); ufs_exit(ufs, alloc); From d8a0f05478e9474dee4df3fe9ce402beab7c12be Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 18 Oct 2023 13:28:24 +0200 Subject: [PATCH 186/974] migration/doc: Add contents Reviewed-by: Fabiano Rosas Signed-off-by: Juan Quintela Message-ID: <20231018112827.1325-2-quintela@redhat.com> --- docs/devel/migration.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/devel/migration.rst b/docs/devel/migration.rst index c3e1400c0c..4d6a98ae58 100644 --- a/docs/devel/migration.rst +++ b/docs/devel/migration.rst @@ -28,6 +28,8 @@ the guest to be stopped. Typically the time that the guest is unresponsive during live migration is the low hundred of milliseconds (notice that this depends on a lot of things). +.. contents:: + Transports ========== From 1aefe2ca14235c475056816fc8c491f11291b332 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 18 Oct 2023 13:28:25 +0200 Subject: [PATCH 187/974] migration/doc: Add documentation for backwards compatiblity State what are the requeriments to get migration working between qemu versions. And once there explain how one is supposed to implement a new feature/default value and not break migration. Reviewed-by: Vladimir Sementsov-Ogievskiy Acked-by: Peter Xu Signed-off-by: Juan Quintela Message-ID: <20231018112827.1325-3-quintela@redhat.com> --- docs/devel/migration.rst | 219 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) diff --git a/docs/devel/migration.rst b/docs/devel/migration.rst index 4d6a98ae58..6fe275b1ec 100644 --- a/docs/devel/migration.rst +++ b/docs/devel/migration.rst @@ -919,3 +919,222 @@ versioned machine types to cut down on the combinations that will need support. This is also useful when newer versions of firmware outgrow the padding. + +Backwards compatibility +======================= + +How backwards compatibility works +--------------------------------- + +When we do migration, we have two QEMU processes: the source and the +target. There are two cases, they are the same version or they are +different versions. The easy case is when they are the same version. +The difficult one is when they are different versions. + +There are two things that are different, but they have very similar +names and sometimes get confused: + +- QEMU version +- machine type version + +Let's start with a practical example, we start with: + +- qemu-system-x86_64 (v5.2), from now on qemu-5.2. +- qemu-system-x86_64 (v5.1), from now on qemu-5.1. + +Related to this are the "latest" machine types defined on each of +them: + +- pc-q35-5.2 (newer one in qemu-5.2) from now on pc-5.2 +- pc-q35-5.1 (newer one in qemu-5.1) from now on pc-5.1 + +First of all, migration is only supposed to work if you use the same +machine type in both source and destination. The QEMU hardware +configuration needs to be the same also on source and destination. +Most aspects of the backend configuration can be changed at will, +except for a few cases where the backend features influence frontend +device feature exposure. But that is not relevant for this section. + +I am going to list the number of combinations that we can have. Let's +start with the trivial ones, QEMU is the same on source and +destination: + +1 - qemu-5.2 -M pc-5.2 -> migrates to -> qemu-5.2 -M pc-5.2 + + This is the latest QEMU with the latest machine type. + This have to work, and if it doesn't work it is a bug. + +2 - qemu-5.1 -M pc-5.1 -> migrates to -> qemu-5.1 -M pc-5.1 + + Exactly the same case than the previous one, but for 5.1. + Nothing to see here either. + +This are the easiest ones, we will not talk more about them in this +section. + +Now we start with the more interesting cases. Consider the case where +we have the same QEMU version in both sides (qemu-5.2) but we are using +the latest machine type for that version (pc-5.2) but one of an older +QEMU version, in this case pc-5.1. + +3 - qemu-5.2 -M pc-5.1 -> migrates to -> qemu-5.2 -M pc-5.1 + + It needs to use the definition of pc-5.1 and the devices as they + were configured on 5.1, but this should be easy in the sense that + both sides are the same QEMU and both sides have exactly the same + idea of what the pc-5.1 machine is. + +4 - qemu-5.1 -M pc-5.2 -> migrates to -> qemu-5.1 -M pc-5.2 + + This combination is not possible as the qemu-5.1 doen't understand + pc-5.2 machine type. So nothing to worry here. + +Now it comes the interesting ones, when both QEMU processes are +different. Notice also that the machine type needs to be pc-5.1, +because we have the limitation than qemu-5.1 doesn't know pc-5.2. So +the possible cases are: + +5 - qemu-5.2 -M pc-5.1 -> migrates to -> qemu-5.1 -M pc-5.1 + + This migration is known as newer to older. We need to make sure + when we are developing 5.2 we need to take care about not to break + migration to qemu-5.1. Notice that we can't make updates to + qemu-5.1 to understand whatever qemu-5.2 decides to change, so it is + in qemu-5.2 side to make the relevant changes. + +6 - qemu-5.1 -M pc-5.1 -> migrates to -> qemu-5.2 -M pc-5.1 + + This migration is known as older to newer. We need to make sure + than we are able to receive migrations from qemu-5.1. The problem is + similar to the previous one. + +If qemu-5.1 and qemu-5.2 were the same, there will not be any +compatibility problems. But the reason that we create qemu-5.2 is to +get new features, devices, defaults, etc. + +If we get a device that has a new feature, or change a default value, +we have a problem when we try to migrate between different QEMU +versions. + +So we need a way to tell qemu-5.2 that when we are using machine type +pc-5.1, it needs to **not** use the feature, to be able to migrate to +real qemu-5.1. + +And the equivalent part when migrating from qemu-5.1 to qemu-5.2. +qemu-5.2 has to expect that it is not going to get data for the new +feature, because qemu-5.1 doesn't know about it. + +How do we tell QEMU about these device feature changes? In +hw/core/machine.c:hw_compat_X_Y arrays. + +If we change a default value, we need to put back the old value on +that array. And the device, during initialization needs to look at +that array to see what value it needs to get for that feature. And +what are we going to put in that array, the value of a property. + +To create a property for a device, we need to use one of the +DEFINE_PROP_*() macros. See include/hw/qdev-properties.h to find the +macros that exist. With it, we set the default value for that +property, and that is what it is going to get in the latest released +version. But if we want a different value for a previous version, we +can change that in the hw_compat_X_Y arrays. + +hw_compat_X_Y is an array of registers that have the format: + +- name_device +- name_property +- value + +Let's see a practical example. + +In qemu-5.2 virtio-blk-device got multi queue support. This is a +change that is not backward compatible. In qemu-5.1 it has one +queue. In qemu-5.2 it has the same number of queues as the number of +cpus in the system. + +When we are doing migration, if we migrate from a device that has 4 +queues to a device that have only one queue, we don't know where to +put the extra information for the other 3 queues, and we fail +migration. + +Similar problem when we migrate from qemu-5.1 that has only one queue +to qemu-5.2, we only sent information for one queue, but destination +has 4, and we have 3 queues that are not properly initialized and +anything can happen. + +So, how can we address this problem. Easy, just convince qemu-5.2 +that when it is running pc-5.1, it needs to set the number of queues +for virtio-blk-devices to 1. + +That way we fix the cases 5 and 6. + +5 - qemu-5.2 -M pc-5.1 -> migrates to -> qemu-5.1 -M pc-5.1 + + qemu-5.2 -M pc-5.1 sets number of queues to be 1. + qemu-5.1 -M pc-5.1 expects number of queues to be 1. + + correct. migration works. + +6 - qemu-5.1 -M pc-5.1 -> migrates to -> qemu-5.2 -M pc-5.1 + + qemu-5.1 -M pc-5.1 sets number of queues to be 1. + qemu-5.2 -M pc-5.1 expects number of queues to be 1. + + correct. migration works. + +And now the other interesting case, case 3. In this case we have: + +3 - qemu-5.2 -M pc-5.1 -> migrates to -> qemu-5.2 -M pc-5.1 + + Here we have the same QEMU in both sides. So it doesn't matter a + lot if we have set the number of queues to 1 or not, because + they are the same. + + WRONG! + + Think what happens if we do one of this double migrations: + + A -> migrates -> B -> migrates -> C + + where: + + A: qemu-5.1 -M pc-5.1 + B: qemu-5.2 -M pc-5.1 + C: qemu-5.2 -M pc-5.1 + + migration A -> B is case 6, so number of queues needs to be 1. + + migration B -> C is case 3, so we don't care. But actually we + care because we haven't started the guest in qemu-5.2, it came + migrated from qemu-5.1. So to be in the safe place, we need to + always use number of queues 1 when we are using pc-5.1. + +Now, how was this done in reality? The following commit shows how it +was done:: + + commit 9445e1e15e66c19e42bea942ba810db28052cd05 + Author: Stefan Hajnoczi + Date: Tue Aug 18 15:33:47 2020 +0100 + + virtio-blk-pci: default num_queues to -smp N + +The relevant parts for migration are:: + + @@ -1281,7 +1284,8 @@ static Property virtio_blk_properties[] = { + #endif + DEFINE_PROP_BIT("request-merging", VirtIOBlock, conf.request_merging, 0, + true), + - DEFINE_PROP_UINT16("num-queues", VirtIOBlock, conf.num_queues, 1), + + DEFINE_PROP_UINT16("num-queues", VirtIOBlock, conf.num_queues, + + VIRTIO_BLK_AUTO_NUM_QUEUES), + DEFINE_PROP_UINT16("queue-size", VirtIOBlock, conf.queue_size, 256), + +It changes the default value of num_queues. But it fishes it for old +machine types to have the right value:: + + @@ -31,6 +31,7 @@ + GlobalProperty hw_compat_5_1[] = { + ... + + { "virtio-blk-device", "num-queues", "1"}, + ... + }; From 593c28c02c819abfb1191a451e038fead7bc5c07 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 18 Oct 2023 13:28:26 +0200 Subject: [PATCH 188/974] migration/doc: How to migrate when hosts have different features Sometimes devices have different features depending of things outside of qemu. For instance the kernel. Document how to handle that cases. Acked-by: Peter Xu Reviewed-by: Fabiano Rosas Signed-off-by: Juan Quintela Message-ID: <20231018112827.1325-4-quintela@redhat.com> --- docs/devel/migration.rst | 97 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/docs/devel/migration.rst b/docs/devel/migration.rst index 6fe275b1ec..974505e4a7 100644 --- a/docs/devel/migration.rst +++ b/docs/devel/migration.rst @@ -1138,3 +1138,100 @@ machine types to have the right value:: + { "virtio-blk-device", "num-queues", "1"}, ... }; + +A device with diferent features on both sides +--------------------------------------------- + +Let's assume that we are using the same QEMU binary on both sides, +just to make the things easier. But we have a device that has +different features on both sides of the migration. That can be +because the devices are different, because the kernel driver of both +devices have different features, whatever. + +How can we get this to work with migration. The way to do that is +"theoretically" easy. You have to get the features that the device +has in the source of the migration. The features that the device has +on the target of the migration, you get the intersection of the +features of both sides, and that is the way that you should launch +QEMU. + +Notice that this is not completely related to QEMU. The most +important thing here is that this should be handled by the managing +application that launches QEMU. If QEMU is configured correctly, the +migration will succeed. + +That said, actually doing it is complicated. Almost all devices are +bad at being able to be launched with only some features enabled. +With one big exception: cpus. + +You can read the documentation for QEMU x86 cpu models here: + +https://qemu-project.gitlab.io/qemu/system/qemu-cpu-models.html + +See when they talk about migration they recommend that one chooses the +newest cpu model that is supported for all cpus. + +Let's say that we have: + +Host A: + +Device X has the feature Y + +Host B: + +Device X has not the feature Y + +If we try to migrate without any care from host A to host B, it will +fail because when migration tries to load the feature Y on +destination, it will find that the hardware is not there. + +Doing this would be the equivalent of doing with cpus: + +Host A: + +$ qemu-system-x86_64 -cpu host + +Host B: + +$ qemu-system-x86_64 -cpu host + +When both hosts have different cpu features this is guaranteed to +fail. Especially if Host B has less features than host A. If host A +has less features than host B, sometimes it works. Important word of +last sentence is "sometimes". + +So, forgetting about cpu models and continuing with the -cpu host +example, let's see that the differences of the cpus is that Host A and +B have the following features: + +Features: 'pcid' 'stibp' 'taa-no' +Host A: X X +Host B: X + +And we want to migrate between them, the way configure both QEMU cpu +will be: + +Host A: + +$ qemu-system-x86_64 -cpu host,pcid=off,stibp=off + +Host B: + +$ qemu-system-x86_64 -cpu host,taa-no=off + +And you would be able to migrate between them. It is responsability +of the management application or of the user to make sure that the +configuration is correct. QEMU doesn't know how to look at this kind +of features in general. + +Notice that we don't recomend to use -cpu host for migration. It is +used in this example because it makes the example simpler. + +Other devices have worse control about individual features. If they +want to be able to migrate between hosts that show different features, +the device needs a way to configure which ones it is going to use. + +In this section we have considered that we are using the same QEMU +binary in both sides of the migration. If we use different QEMU +versions process, then we need to have into account all other +differences and the examples become even more complicated. From e77326179d61585457f88ba0c5152cea5bbaabf0 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 18 Oct 2023 13:28:27 +0200 Subject: [PATCH 189/974] migration/doc: We broke backwards compatibility When we detect that we have broken backwards compatibility in a released version, we can't do anything for that version. But once we fix that bug on the next released version, we can "mitigate" that problem when migrating to new versions to give a way out of that machine until it does a hard reboot. Acked-by: Peter Xu Reviewed-by: Fabiano Rosas Signed-off-by: Juan Quintela Message-ID: <20231018112827.1325-5-quintela@redhat.com> --- docs/devel/migration.rst | 202 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) diff --git a/docs/devel/migration.rst b/docs/devel/migration.rst index 974505e4a7..be913630c3 100644 --- a/docs/devel/migration.rst +++ b/docs/devel/migration.rst @@ -1235,3 +1235,205 @@ In this section we have considered that we are using the same QEMU binary in both sides of the migration. If we use different QEMU versions process, then we need to have into account all other differences and the examples become even more complicated. + +How to mitigate when we have a backward compatibility error +----------------------------------------------------------- + +We broke migration for old machine types continuously during +development. But as soon as we find that there is a problem, we fix +it. The problem is what happens when we detect after we have done a +release that something has gone wrong. + +Let see how it worked with one example. + +After the release of qemu-8.0 we found a problem when doing migration +of the machine type pc-7.2. + +- $ qemu-7.2 -M pc-7.2 -> qemu-7.2 -M pc-7.2 + + This migration works + +- $ qemu-8.0 -M pc-7.2 -> qemu-8.0 -M pc-7.2 + + This migration works + +- $ qemu-8.0 -M pc-7.2 -> qemu-7.2 -M pc-7.2 + + This migration fails + +- $ qemu-7.2 -M pc-7.2 -> qemu-8.0 -M pc-7.2 + + This migration fails + +So clearly something fails when migration between qemu-7.2 and +qemu-8.0 with machine type pc-7.2. The error messages, and git bisect +pointed to this commit. + +In qemu-8.0 we got this commit:: + + commit 010746ae1db7f52700cb2e2c46eb94f299cfa0d2 + Author: Jonathan Cameron + Date: Thu Mar 2 13:37:02 2023 +0000 + + hw/pci/aer: Implement PCI_ERR_UNCOR_MASK register + + +The relevant bits of the commit for our example are this ones:: + + --- a/hw/pci/pcie_aer.c + +++ b/hw/pci/pcie_aer.c + @@ -112,6 +112,10 @@ int pcie_aer_init(PCIDevice *dev, + + pci_set_long(dev->w1cmask + offset + PCI_ERR_UNCOR_STATUS, + PCI_ERR_UNC_SUPPORTED); + + pci_set_long(dev->config + offset + PCI_ERR_UNCOR_MASK, + + PCI_ERR_UNC_MASK_DEFAULT); + + pci_set_long(dev->wmask + offset + PCI_ERR_UNCOR_MASK, + + PCI_ERR_UNC_SUPPORTED); + + pci_set_long(dev->config + offset + PCI_ERR_UNCOR_SEVER, + PCI_ERR_UNC_SEVERITY_DEFAULT); + +The patch changes how we configure PCI space for AER. But QEMU fails +when the PCI space configuration is different between source and +destination. + +The following commit shows how this got fixed:: + + commit 5ed3dabe57dd9f4c007404345e5f5bf0e347317f + Author: Leonardo Bras + Date: Tue May 2 21:27:02 2023 -0300 + + hw/pci: Disable PCI_ERR_UNCOR_MASK register for machine type < 8.0 + + [...] + +The relevant parts of the fix in QEMU are as follow: + +First, we create a new property for the device to be able to configure +the old behaviour or the new behaviour:: + + diff --git a/hw/pci/pci.c b/hw/pci/pci.c + index 8a87ccc8b0..5153ad63d6 100644 + --- a/hw/pci/pci.c + +++ b/hw/pci/pci.c + @@ -79,6 +79,8 @@ static Property pci_props[] = { + DEFINE_PROP_STRING("failover_pair_id", PCIDevice, + failover_pair_id), + DEFINE_PROP_UINT32("acpi-index", PCIDevice, acpi_index, 0), + + DEFINE_PROP_BIT("x-pcie-err-unc-mask", PCIDevice, cap_present, + + QEMU_PCIE_ERR_UNC_MASK_BITNR, true), + DEFINE_PROP_END_OF_LIST() + }; + +Notice that we enable the feature for new machine types. + +Now we see how the fix is done. This is going to depend on what kind +of breakage happens, but in this case it is quite simple:: + + diff --git a/hw/pci/pcie_aer.c b/hw/pci/pcie_aer.c + index 103667c368..374d593ead 100644 + --- a/hw/pci/pcie_aer.c + +++ b/hw/pci/pcie_aer.c + @@ -112,10 +112,13 @@ int pcie_aer_init(PCIDevice *dev, uint8_t cap_ver, + uint16_t offset, + + pci_set_long(dev->w1cmask + offset + PCI_ERR_UNCOR_STATUS, + PCI_ERR_UNC_SUPPORTED); + - pci_set_long(dev->config + offset + PCI_ERR_UNCOR_MASK, + - PCI_ERR_UNC_MASK_DEFAULT); + - pci_set_long(dev->wmask + offset + PCI_ERR_UNCOR_MASK, + - PCI_ERR_UNC_SUPPORTED); + + + + if (dev->cap_present & QEMU_PCIE_ERR_UNC_MASK) { + + pci_set_long(dev->config + offset + PCI_ERR_UNCOR_MASK, + + PCI_ERR_UNC_MASK_DEFAULT); + + pci_set_long(dev->wmask + offset + PCI_ERR_UNCOR_MASK, + + PCI_ERR_UNC_SUPPORTED); + + } + + pci_set_long(dev->config + offset + PCI_ERR_UNCOR_SEVER, + PCI_ERR_UNC_SEVERITY_DEFAULT); + +I.e. If the property bit is enabled, we configure it as we did for +qemu-8.0. If the property bit is not set, we configure it as it was in 7.2. + +And now, everything that is missing is disabling the feature for old +machine types:: + + diff --git a/hw/core/machine.c b/hw/core/machine.c + index 47a34841a5..07f763eb2e 100644 + --- a/hw/core/machine.c + +++ b/hw/core/machine.c + @@ -48,6 +48,7 @@ GlobalProperty hw_compat_7_2[] = { + { "e1000e", "migrate-timadj", "off" }, + { "virtio-mem", "x-early-migration", "false" }, + { "migration", "x-preempt-pre-7-2", "true" }, + + { TYPE_PCI_DEVICE, "x-pcie-err-unc-mask", "off" }, + }; + const size_t hw_compat_7_2_len = G_N_ELEMENTS(hw_compat_7_2); + +And now, when qemu-8.0.1 is released with this fix, all combinations +are going to work as supposed. + +- $ qemu-7.2 -M pc-7.2 -> qemu-7.2 -M pc-7.2 (works) +- $ qemu-8.0.1 -M pc-7.2 -> qemu-8.0.1 -M pc-7.2 (works) +- $ qemu-8.0.1 -M pc-7.2 -> qemu-7.2 -M pc-7.2 (works) +- $ qemu-7.2 -M pc-7.2 -> qemu-8.0.1 -M pc-7.2 (works) + +So the normality has been restored and everything is ok, no? + +Not really, now our matrix is much bigger. We started with the easy +cases, migration from the same version to the same version always +works: + +- $ qemu-7.2 -M pc-7.2 -> qemu-7.2 -M pc-7.2 +- $ qemu-8.0 -M pc-7.2 -> qemu-8.0 -M pc-7.2 +- $ qemu-8.0.1 -M pc-7.2 -> qemu-8.0.1 -M pc-7.2 + +Now the interesting ones. When the QEMU processes versions are +different. For the 1st set, their fail and we can do nothing, both +versions are released and we can't change anything. + +- $ qemu-7.2 -M pc-7.2 -> qemu-8.0 -M pc-7.2 +- $ qemu-8.0 -M pc-7.2 -> qemu-7.2 -M pc-7.2 + +This two are the ones that work. The whole point of making the +change in qemu-8.0.1 release was to fix this issue: + +- $ qemu-7.2 -M pc-7.2 -> qemu-8.0.1 -M pc-7.2 +- $ qemu-8.0.1 -M pc-7.2 -> qemu-7.2 -M pc-7.2 + +But now we found that qemu-8.0 neither can migrate to qemu-7.2 not +qemu-8.0.1. + +- $ qemu-8.0 -M pc-7.2 -> qemu-8.0.1 -M pc-7.2 +- $ qemu-8.0.1 -M pc-7.2 -> qemu-8.0 -M pc-7.2 + +So, if we start a pc-7.2 machine in qemu-8.0 we can't migrate it to +anything except to qemu-8.0. + +Can we do better? + +Yeap. If we know that we are going to do this migration: + +- $ qemu-8.0 -M pc-7.2 -> qemu-8.0.1 -M pc-7.2 + +We can launch the appropriate devices with:: + + --device...,x-pci-e-err-unc-mask=on + +And now we can receive a migration from 8.0. And from now on, we can +do that migration to new machine types if we remember to enable that +property for pc-7.2. Notice that we need to remember, it is not +enough to know that the source of the migration is qemu-8.0. Think of +this example: + +$ qemu-8.0 -M pc-7.2 -> qemu-8.0.1 -M pc-7.2 -> qemu-8.2 -M pc-7.2 + +In the second migration, the source is not qemu-8.0, but we still have +that "problem" and have that property enabled. Notice that we need to +continue having this mark/property until we have this machine +rebooted. But it is not a normal reboot (that don't reload QEMU) we +need the machine to poweroff/poweron on a fixed QEMU. And from now +on we can use the proper real machine. From 413d64fedcc8e7f83b19ab4b79e3fd1f871f3564 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Thu, 19 Oct 2023 10:52:58 +0200 Subject: [PATCH 190/974] migration: Receiving a zero page non zero is an error We don't allow non zero compressed pages since: commit 3edcd7e6ebae3ef0ac178eed5f4225803159562d Author: Peter Lieven Date: Tue Mar 26 10:58:35 2013 +0100 migration: search for zero instead of dup pages RDMA case is a bit more complicated, but they don't handle it since: commit a1febc4950f2c6232c002f401d7cd409f6fa6a88 Author: Richard Henderson Date: Mon Aug 29 11:46:14 2016 -0700 cutils: Export only buffer_is_zero Reviewed-by: Fabiano Rosas Reviewed-by: Peter Xu Signed-off-by: Juan Quintela Message-ID: <20231019085259.13307-2-quintela@redhat.com> --- migration/ram.c | 15 +++++++++++---- migration/rdma.c | 6 +++++- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 92769902bb..4bfb20c94a 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -3715,16 +3715,18 @@ int ram_load_postcopy(QEMUFile *f, int channel) switch (flags & ~RAM_SAVE_FLAG_CONTINUE) { case RAM_SAVE_FLAG_ZERO: ch = qemu_get_byte(f); + if (ch != 0) { + error_report("Found a zero page with value %d", ch); + ret = -EINVAL; + break; + } /* * Can skip to set page_buffer when * this is a zero page and (block->page_size == TARGET_PAGE_SIZE). */ - if (ch || !matches_target_page_size) { + if (!matches_target_page_size) { memset(page_buffer, ch, TARGET_PAGE_SIZE); } - if (ch) { - tmp_page->all_zero = false; - } break; case RAM_SAVE_FLAG_PAGE: @@ -4030,6 +4032,11 @@ static int ram_load_precopy(QEMUFile *f) case RAM_SAVE_FLAG_ZERO: ch = qemu_get_byte(f); + if (ch != 0) { + error_report("Found a zero page with value %d", ch); + ret = -EINVAL; + break; + } ram_handle_compressed(host, ch, TARGET_PAGE_SIZE); break; diff --git a/migration/rdma.c b/migration/rdma.c index 2a1852ec7f..2d963fd147 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -3592,7 +3592,11 @@ int rdma_registration_handle(QEMUFile *f) host_addr = block->local_host_addr + (comp->offset - block->offset); - + if (comp->value) { + error_report("rdma: Zero page with non-zero (%d) value", + comp->value); + goto err; + } ram_handle_compressed(host_addr, comp->value, comp->length); break; From 7091dabeb41806132428ffe96164c8425854ea27 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Thu, 19 Oct 2023 10:52:59 +0200 Subject: [PATCH 191/974] migration: Rename ram_handle_compressed() to ram_handle_zero() Now that we know it only handles zero, we can remove the ch parameter. Reviewed-by: Fabiano Rosas Reviewed-by: Peter Xu Signed-off-by: Juan Quintela Message-ID: <20231019085259.13307-3-quintela@redhat.com> --- migration/ram.c | 10 +++++----- migration/ram.h | 2 +- migration/rdma.c | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 4bfb20c94a..e0ad732ee8 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -3446,7 +3446,7 @@ static inline void *colo_cache_from_block_offset(RAMBlock *block, } /** - * ram_handle_compressed: handle the zero page case + * ram_handle_zero: handle the zero page case * * If a page (or a whole RDMA chunk) has been * determined to be zero, then zap it. @@ -3455,10 +3455,10 @@ static inline void *colo_cache_from_block_offset(RAMBlock *block, * @ch: what the page is filled from. We only support zero * @size: size of the zero page */ -void ram_handle_compressed(void *host, uint8_t ch, uint64_t size) +void ram_handle_zero(void *host, uint64_t size) { - if (ch != 0 || !buffer_is_zero(host, size)) { - memset(host, ch, size); + if (!buffer_is_zero(host, size)) { + memset(host, 0, size); } } @@ -4037,7 +4037,7 @@ static int ram_load_precopy(QEMUFile *f) ret = -EINVAL; break; } - ram_handle_compressed(host, ch, TARGET_PAGE_SIZE); + ram_handle_zero(host, TARGET_PAGE_SIZE); break; case RAM_SAVE_FLAG_PAGE: diff --git a/migration/ram.h b/migration/ram.h index 145c915ca7..3f724b2f02 100644 --- a/migration/ram.h +++ b/migration/ram.h @@ -60,7 +60,7 @@ int ram_discard_range(const char *block_name, uint64_t start, size_t length); int ram_postcopy_incoming_init(MigrationIncomingState *mis); int ram_load_postcopy(QEMUFile *f, int channel); -void ram_handle_compressed(void *host, uint8_t ch, uint64_t size); +void ram_handle_zero(void *host, uint64_t size); void ram_transferred_add(uint64_t bytes); void ram_release_page(const char *rbname, uint64_t offset); diff --git a/migration/rdma.c b/migration/rdma.c index 2d963fd147..e3493e3b3e 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -3597,7 +3597,7 @@ int rdma_registration_handle(QEMUFile *f) comp->value); goto err; } - ram_handle_compressed(host_addr, comp->value, comp->length); + ram_handle_zero(host_addr, comp->length); break; case RDMA_CONTROL_REGISTER_FINISHED: From d869f6297522894ef6c184dbe47b923360faf9e5 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Thu, 19 Oct 2023 13:07:14 +0200 Subject: [PATCH 192/974] migration: Give one error if trying to set MULTIFD and XBZRLE Reviewed-by: Fabiano Rosas Signed-off-by: Juan Quintela Message-ID: <20231019110724.15324-2-quintela@redhat.com> --- migration/options.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/migration/options.c b/migration/options.c index 42fb818956..b8c3c3218d 100644 --- a/migration/options.c +++ b/migration/options.c @@ -618,6 +618,13 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp) } } + if (new_caps[MIGRATION_CAPABILITY_MULTIFD]) { + if (new_caps[MIGRATION_CAPABILITY_XBZRLE]) { + error_setg(errp, "Multifd is not compatible with xbzrle"); + return false; + } + } + return true; } From 0e195629969125e3a168a7e06e3c49d939c36ead Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Thu, 19 Oct 2023 13:07:15 +0200 Subject: [PATCH 193/974] migration: Give one error if trying to set COMPRESSION and XBZRLE Reviewed-by: Fabiano Rosas Signed-off-by: Juan Quintela Message-ID: <20231019110724.15324-3-quintela@redhat.com> --- migration/options.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/migration/options.c b/migration/options.c index b8c3c3218d..37fa1cfe74 100644 --- a/migration/options.c +++ b/migration/options.c @@ -625,6 +625,13 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp) } } + if (new_caps[MIGRATION_CAPABILITY_COMPRESS]) { + if (new_caps[MIGRATION_CAPABILITY_XBZRLE]) { + error_setg(errp, "Compression is not compatible with xbzrle"); + return false; + } + } + return true; } From 4e400f9091d496c513fcd513753a2d681be57a3b Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Thu, 19 Oct 2023 13:07:16 +0200 Subject: [PATCH 194/974] migration: Remove save_page_use_compression() After previous patch, we disable the posiblity that we use compression together with xbzrle. So we can use directly migrate_compress(). Once there, now we don't need the rs parameter, so remove it. Reviewed-by: Fabiano Rosas Signed-off-by: Juan Quintela Message-ID: <20231019110724.15324-4-quintela@redhat.com> --- migration/ram.c | 34 +++++++--------------------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index e0ad732ee8..8246663f64 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1291,8 +1291,6 @@ static int ram_save_multifd_page(QEMUFile *file, RAMBlock *block, return 1; } -static bool save_page_use_compression(RAMState *rs); - static int send_queued_data(CompressParam *param) { PageSearchStatus *pss = &ram_state->pss[RAM_CHANNEL_PRECOPY]; @@ -1329,9 +1327,9 @@ static int send_queued_data(CompressParam *param) return len; } -static void ram_flush_compressed_data(RAMState *rs) +static void ram_flush_compressed_data(void) { - if (!save_page_use_compression(rs)) { + if (!migrate_compress()) { return; } @@ -1393,7 +1391,7 @@ static int find_dirty_block(RAMState *rs, PageSearchStatus *pss) * Also If xbzrle is on, stop using the data compression at this * point. In theory, xbzrle can do better than compression. */ - ram_flush_compressed_data(rs); + ram_flush_compressed_data(); /* Hit the end of the list */ pss->block = QLIST_FIRST_RCU(&ram_list.blocks); @@ -2042,24 +2040,6 @@ int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len) return 0; } -static bool save_page_use_compression(RAMState *rs) -{ - if (!migrate_compress()) { - return false; - } - - /* - * If xbzrle is enabled (e.g., after first round of migration), stop - * using the data compression. In theory, xbzrle can do better than - * compression. - */ - if (rs->xbzrle_started) { - return false; - } - - return true; -} - /* * try to compress the page before posting it out, return true if the page * has been properly handled by compression, otherwise needs other @@ -2068,7 +2048,7 @@ static bool save_page_use_compression(RAMState *rs) static bool save_compress_page(RAMState *rs, PageSearchStatus *pss, ram_addr_t offset) { - if (!save_page_use_compression(rs)) { + if (!migrate_compress()) { return false; } @@ -2083,7 +2063,7 @@ static bool save_compress_page(RAMState *rs, PageSearchStatus *pss, * much CPU resource. */ if (pss->block != pss->last_sent_block) { - ram_flush_compressed_data(rs); + ram_flush_compressed_data(); return false; } @@ -3135,7 +3115,7 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) * page is sent in one chunk. */ if (migrate_postcopy_ram()) { - ram_flush_compressed_data(rs); + ram_flush_compressed_data(); } /* @@ -3236,7 +3216,7 @@ static int ram_save_complete(QEMUFile *f, void *opaque) } qemu_mutex_unlock(&rs->bitmap_mutex); - ram_flush_compressed_data(rs); + ram_flush_compressed_data(); int ret = rdma_registration_stop(f, RAM_CONTROL_FINISH); if (ret < 0) { From 83df387df7fbd4fb76f3beae56f925dead5bba5a Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Thu, 19 Oct 2023 13:07:17 +0200 Subject: [PATCH 195/974] migration: Make compress_data_with_multithreads return bool Reviewed-by: Lukas Straub Reviewed-by: Fabiano Rosas Signed-off-by: Juan Quintela Message-ID: <20231019110724.15324-5-quintela@redhat.com> --- migration/ram-compress.c | 17 ++++++++++------- migration/ram-compress.h | 4 ++-- migration/ram.c | 3 +-- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/migration/ram-compress.c b/migration/ram-compress.c index d037dfe6cf..ef03d60a6d 100644 --- a/migration/ram-compress.c +++ b/migration/ram-compress.c @@ -260,10 +260,13 @@ static inline void set_compress_params(CompressParam *param, RAMBlock *block, param->trigger = true; } -int compress_page_with_multi_thread(RAMBlock *block, ram_addr_t offset, - int (send_queued_data(CompressParam *))) +/* + * Return true when it compress a page + */ +bool compress_page_with_multi_thread(RAMBlock *block, ram_addr_t offset, + int (send_queued_data(CompressParam *))) { - int thread_count, pages = -1; + int thread_count; bool wait = migrate_compress_wait_thread(); thread_count = migrate_compress_threads(); @@ -281,8 +284,8 @@ retry: qemu_cond_signal(¶m->cond); qemu_mutex_unlock(¶m->mutex); - pages = 1; - break; + qemu_mutex_unlock(&comp_done_lock); + return true; } } @@ -290,13 +293,13 @@ retry: * wait for the free thread if the user specifies 'compress-wait-thread', * otherwise we will post the page out in the main thread as normal page. */ - if (pages < 0 && wait) { + if (wait) { qemu_cond_wait(&comp_done_cond, &comp_done_lock); goto retry; } qemu_mutex_unlock(&comp_done_lock); - return pages; + return false; } /* return the size after decompression, or negative value on error */ diff --git a/migration/ram-compress.h b/migration/ram-compress.h index e55d3b50bd..b228640092 100644 --- a/migration/ram-compress.h +++ b/migration/ram-compress.h @@ -60,8 +60,8 @@ void compress_threads_save_cleanup(void); int compress_threads_save_setup(void); void flush_compressed_data(int (send_queued_data(CompressParam *))); -int compress_page_with_multi_thread(RAMBlock *block, ram_addr_t offset, - int (send_queued_data(CompressParam *))); +bool compress_page_with_multi_thread(RAMBlock *block, ram_addr_t offset, + int (send_queued_data(CompressParam *))); int wait_for_decompress_done(void); void compress_threads_load_cleanup(void); diff --git a/migration/ram.c b/migration/ram.c index 8246663f64..63a575ae90 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -2067,8 +2067,7 @@ static bool save_compress_page(RAMState *rs, PageSearchStatus *pss, return false; } - if (compress_page_with_multi_thread(pss->block, offset, - send_queued_data) > 0) { + if (compress_page_with_multi_thread(pss->block, offset, send_queued_data)) { return true; } From b6e19b6de82987606b0cfe5c120dc1952ec582a5 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Thu, 19 Oct 2023 13:07:18 +0200 Subject: [PATCH 196/974] migration: Simplify compress_page_with_multithread() Move the goto to a while true. Reviewed-by: Lukas Straub Reviewed-by: Fabiano Rosas Signed-off-by: Juan Quintela Message-ID: <20231019110724.15324-6-quintela@redhat.com> --- migration/ram-compress.c | 50 ++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/migration/ram-compress.c b/migration/ram-compress.c index ef03d60a6d..a991b15b7a 100644 --- a/migration/ram-compress.c +++ b/migration/ram-compress.c @@ -271,35 +271,35 @@ bool compress_page_with_multi_thread(RAMBlock *block, ram_addr_t offset, thread_count = migrate_compress_threads(); qemu_mutex_lock(&comp_done_lock); -retry: - for (int i = 0; i < thread_count; i++) { - if (comp_param[i].done) { - CompressParam *param = &comp_param[i]; - qemu_mutex_lock(¶m->mutex); - param->done = false; - send_queued_data(param); - assert(qemu_file_buffer_empty(param->file)); - compress_reset_result(param); - set_compress_params(param, block, offset); - qemu_cond_signal(¶m->cond); - qemu_mutex_unlock(¶m->mutex); - qemu_mutex_unlock(&comp_done_lock); - return true; + while (true) { + for (int i = 0; i < thread_count; i++) { + if (comp_param[i].done) { + CompressParam *param = &comp_param[i]; + qemu_mutex_lock(¶m->mutex); + param->done = false; + send_queued_data(param); + assert(qemu_file_buffer_empty(param->file)); + compress_reset_result(param); + set_compress_params(param, block, offset); + + qemu_cond_signal(¶m->cond); + qemu_mutex_unlock(¶m->mutex); + qemu_mutex_unlock(&comp_done_lock); + return true; + } } - } - - /* - * wait for the free thread if the user specifies 'compress-wait-thread', - * otherwise we will post the page out in the main thread as normal page. - */ - if (wait) { + if (!wait) { + qemu_mutex_unlock(&comp_done_lock); + return false; + } + /* + * wait for a free thread if the user specifies + * 'compress-wait-thread', otherwise we will post the page out + * in the main thread as normal page. + */ qemu_cond_wait(&comp_done_cond, &comp_done_lock); - goto retry; } - qemu_mutex_unlock(&comp_done_lock); - - return false; } /* return the size after decompression, or negative value on error */ From 250b1d7ef62b26fde8dc0f8dacad406807d82f1a Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Thu, 19 Oct 2023 13:07:19 +0200 Subject: [PATCH 197/974] migration: Move busy++ to migrate_with_multithread And now we can simplify save_compress_page(). Reviewed-by: Lukas Straub Reviewed-by: Fabiano Rosas Signed-off-by: Juan Quintela Message-ID: <20231019110724.15324-7-quintela@redhat.com> --- migration/ram-compress.c | 1 + migration/ram.c | 8 ++------ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/migration/ram-compress.c b/migration/ram-compress.c index a991b15b7a..f56e1f8e69 100644 --- a/migration/ram-compress.c +++ b/migration/ram-compress.c @@ -291,6 +291,7 @@ bool compress_page_with_multi_thread(RAMBlock *block, ram_addr_t offset, } if (!wait) { qemu_mutex_unlock(&comp_done_lock); + compression_counters.busy++; return false; } /* diff --git a/migration/ram.c b/migration/ram.c index 63a575ae90..46209388ec 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -2067,12 +2067,8 @@ static bool save_compress_page(RAMState *rs, PageSearchStatus *pss, return false; } - if (compress_page_with_multi_thread(pss->block, offset, send_queued_data)) { - return true; - } - - compression_counters.busy++; - return false; + return compress_page_with_multi_thread(pss->block, offset, + send_queued_data); } /** From fb36fb275f455642599ac882fd5a96a8b00b062e Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Thu, 19 Oct 2023 13:07:20 +0200 Subject: [PATCH 198/974] migration: Create compress_update_rates() So we can move more compression_counters stuff to ram-compress.c. Create compression_counters struct to add the stuff that was on MigrationState. Reviewed-by: Lukas Straub Reviewed-by: Fabiano Rosas Signed-off-by: Juan Quintela Message-ID: <20231019110724.15324-8-quintela@redhat.com> --- migration/ram-compress.c | 42 +++++++++++++++++++++++++++++++++++++++- migration/ram-compress.h | 1 + migration/ram.c | 29 +-------------------------- migration/ram.h | 1 - 4 files changed, 43 insertions(+), 30 deletions(-) diff --git a/migration/ram-compress.c b/migration/ram-compress.c index f56e1f8e69..af42cab0fe 100644 --- a/migration/ram-compress.c +++ b/migration/ram-compress.c @@ -41,7 +41,20 @@ #include "ram.h" #include "migration-stats.h" -CompressionStats compression_counters; +static struct { + int64_t pages; + int64_t busy; + double busy_rate; + int64_t compressed_size; + double compression_rate; + /* compression statistics since the beginning of the period */ + /* amount of count that no free thread to compress data */ + uint64_t compress_thread_busy_prev; + /* amount bytes after compression */ + uint64_t compressed_size_prev; + /* amount of compressed pages */ + uint64_t compress_pages_prev; +} compression_counters; static CompressParam *comp_param; static QemuThread *compress_threads; @@ -518,3 +531,30 @@ void update_compress_thread_counts(const CompressParam *param, int bytes_xmit) compression_counters.pages++; } +void compress_update_rates(uint64_t page_count) +{ + if (!migrate_compress()) { + return; + } + compression_counters.busy_rate = (double)(compression_counters.busy - + compression_counters.compress_thread_busy_prev) / page_count; + compression_counters.compress_thread_busy_prev = + compression_counters.busy; + + double compressed_size = compression_counters.compressed_size - + compression_counters.compressed_size_prev; + if (compressed_size) { + double uncompressed_size = (compression_counters.pages - + compression_counters.compress_pages_prev) * + qemu_target_page_size(); + + /* Compression-Ratio = Uncompressed-size / Compressed-size */ + compression_counters.compression_rate = + uncompressed_size / compressed_size; + + compression_counters.compress_pages_prev = + compression_counters.pages; + compression_counters.compressed_size_prev = + compression_counters.compressed_size; + } +} diff --git a/migration/ram-compress.h b/migration/ram-compress.h index b228640092..76dacd3ec7 100644 --- a/migration/ram-compress.h +++ b/migration/ram-compress.h @@ -71,5 +71,6 @@ void decompress_data_with_multi_threads(QEMUFile *f, void *host, int len); void populate_compress(MigrationInfo *info); uint64_t ram_compressed_pages(void); void update_compress_thread_counts(const CompressParam *param, int bytes_xmit); +void compress_update_rates(uint64_t page_count); #endif diff --git a/migration/ram.c b/migration/ram.c index 46209388ec..f7daf2226e 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -369,13 +369,6 @@ struct RAMState { bool xbzrle_started; /* Are we on the last stage of migration */ bool last_stage; - /* compression statistics since the beginning of the period */ - /* amount of count that no free thread to compress data */ - uint64_t compress_thread_busy_prev; - /* amount bytes after compression */ - uint64_t compressed_size_prev; - /* amount of compressed pages */ - uint64_t compress_pages_prev; /* total handled target pages at the beginning of period */ uint64_t target_page_count_prev; @@ -945,7 +938,6 @@ uint64_t ram_get_total_transferred_pages(void) static void migration_update_rates(RAMState *rs, int64_t end_time) { uint64_t page_count = rs->target_page_count - rs->target_page_count_prev; - double compressed_size; /* calculate period counters */ stat64_set(&mig_stats.dirty_pages_rate, @@ -973,26 +965,7 @@ static void migration_update_rates(RAMState *rs, int64_t end_time) rs->xbzrle_pages_prev = xbzrle_counters.pages; rs->xbzrle_bytes_prev = xbzrle_counters.bytes; } - - if (migrate_compress()) { - compression_counters.busy_rate = (double)(compression_counters.busy - - rs->compress_thread_busy_prev) / page_count; - rs->compress_thread_busy_prev = compression_counters.busy; - - compressed_size = compression_counters.compressed_size - - rs->compressed_size_prev; - if (compressed_size) { - double uncompressed_size = (compression_counters.pages - - rs->compress_pages_prev) * TARGET_PAGE_SIZE; - - /* Compression-Ratio = Uncompressed-size / Compressed-size */ - compression_counters.compression_rate = - uncompressed_size / compressed_size; - - rs->compress_pages_prev = compression_counters.pages; - rs->compressed_size_prev = compression_counters.compressed_size; - } - } + compress_update_rates(page_count); } /* diff --git a/migration/ram.h b/migration/ram.h index 3f724b2f02..9f3ad1ee81 100644 --- a/migration/ram.h +++ b/migration/ram.h @@ -34,7 +34,6 @@ #include "io/channel.h" extern XBZRLECacheStats xbzrle_counters; -extern CompressionStats compression_counters; /* Should be holding either ram_list.mutex, or the RCU lock. */ #define RAMBLOCK_FOREACH_NOT_IGNORED(block) \ From 742ec5f338593f3bc9d526577d24976eb658dcc5 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Thu, 19 Oct 2023 13:07:21 +0200 Subject: [PATCH 199/974] migration: Export send_queued_data() This function is only used for compression. So we rename it as compress_send_queued_data(). We put it on ram-compress.h because we are moving it later to ram-compress.c. Reviewed-by: Fabiano Rosas Signed-off-by: Juan Quintela Message-ID: <20231019110724.15324-9-quintela@redhat.com> --- migration/ram-compress.h | 1 + migration/ram.c | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/migration/ram-compress.h b/migration/ram-compress.h index 76dacd3ec7..636281ed97 100644 --- a/migration/ram-compress.h +++ b/migration/ram-compress.h @@ -72,5 +72,6 @@ void populate_compress(MigrationInfo *info); uint64_t ram_compressed_pages(void); void update_compress_thread_counts(const CompressParam *param, int bytes_xmit); void compress_update_rates(uint64_t page_count); +int compress_send_queued_data(CompressParam *param); #endif diff --git a/migration/ram.c b/migration/ram.c index f7daf2226e..b6d485358e 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1264,7 +1264,7 @@ static int ram_save_multifd_page(QEMUFile *file, RAMBlock *block, return 1; } -static int send_queued_data(CompressParam *param) +int compress_send_queued_data(CompressParam *param) { PageSearchStatus *pss = &ram_state->pss[RAM_CHANNEL_PRECOPY]; MigrationState *ms = migrate_get_current(); @@ -1306,7 +1306,7 @@ static void ram_flush_compressed_data(void) return; } - flush_compressed_data(send_queued_data); + flush_compressed_data(compress_send_queued_data); } #define PAGE_ALL_CLEAN 0 @@ -2041,7 +2041,7 @@ static bool save_compress_page(RAMState *rs, PageSearchStatus *pss, } return compress_page_with_multi_thread(pss->block, offset, - send_queued_data); + compress_send_queued_data); } /** From 8020bc9a77086d4beebaa1d6d9a862fcc66d854f Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Thu, 19 Oct 2023 13:07:22 +0200 Subject: [PATCH 200/974] migration: Move ram_flush_compressed_data() to ram-compress.c As we export it, rename it compress_flush_data(). Reviewed-by: Fabiano Rosas Signed-off-by: Juan Quintela Message-ID: <20231019110724.15324-10-quintela@redhat.com> --- migration/ram-compress.c | 9 +++++++++ migration/ram-compress.h | 1 + migration/ram.c | 17 ++++------------- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/migration/ram-compress.c b/migration/ram-compress.c index af42cab0fe..1443a1cb45 100644 --- a/migration/ram-compress.c +++ b/migration/ram-compress.c @@ -558,3 +558,12 @@ void compress_update_rates(uint64_t page_count) compression_counters.compressed_size; } } + +void compress_flush_data(void) +{ + if (!migrate_compress()) { + return; + } + + flush_compressed_data(compress_send_queued_data); +} diff --git a/migration/ram-compress.h b/migration/ram-compress.h index 636281ed97..7ba01e2882 100644 --- a/migration/ram-compress.h +++ b/migration/ram-compress.h @@ -73,5 +73,6 @@ uint64_t ram_compressed_pages(void); void update_compress_thread_counts(const CompressParam *param, int bytes_xmit); void compress_update_rates(uint64_t page_count); int compress_send_queued_data(CompressParam *param); +void compress_flush_data(void); #endif diff --git a/migration/ram.c b/migration/ram.c index b6d485358e..849752ef29 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1300,15 +1300,6 @@ int compress_send_queued_data(CompressParam *param) return len; } -static void ram_flush_compressed_data(void) -{ - if (!migrate_compress()) { - return; - } - - flush_compressed_data(compress_send_queued_data); -} - #define PAGE_ALL_CLEAN 0 #define PAGE_TRY_AGAIN 1 #define PAGE_DIRTY_FOUND 2 @@ -1364,7 +1355,7 @@ static int find_dirty_block(RAMState *rs, PageSearchStatus *pss) * Also If xbzrle is on, stop using the data compression at this * point. In theory, xbzrle can do better than compression. */ - ram_flush_compressed_data(); + compress_flush_data(); /* Hit the end of the list */ pss->block = QLIST_FIRST_RCU(&ram_list.blocks); @@ -2036,7 +2027,7 @@ static bool save_compress_page(RAMState *rs, PageSearchStatus *pss, * much CPU resource. */ if (pss->block != pss->last_sent_block) { - ram_flush_compressed_data(); + compress_flush_data(); return false; } @@ -3083,7 +3074,7 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) * page is sent in one chunk. */ if (migrate_postcopy_ram()) { - ram_flush_compressed_data(); + compress_flush_data(); } /* @@ -3184,7 +3175,7 @@ static int ram_save_complete(QEMUFile *f, void *opaque) } qemu_mutex_unlock(&rs->bitmap_mutex); - ram_flush_compressed_data(); + compress_flush_data(); int ret = rdma_registration_stop(f, RAM_CONTROL_FINISH); if (ret < 0) { From f639cfe515b159c86fbb6c7a10d2146ca1b99b23 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Thu, 19 Oct 2023 13:07:23 +0200 Subject: [PATCH 201/974] migration: Merge flush_compressed_data() and compress_flush_data() Reviewed-by: Fabiano Rosas Signed-off-by: Juan Quintela Message-ID: <20231019110724.15324-11-quintela@redhat.com> --- migration/ram-compress.c | 17 ++++++----------- migration/ram-compress.h | 1 - 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/migration/ram-compress.c b/migration/ram-compress.c index 1443a1cb45..036e44085b 100644 --- a/migration/ram-compress.c +++ b/migration/ram-compress.c @@ -241,10 +241,14 @@ static inline void compress_reset_result(CompressParam *param) param->offset = 0; } -void flush_compressed_data(int (send_queued_data(CompressParam *))) +void compress_flush_data(void) { int thread_count = migrate_compress_threads(); + if (!migrate_compress()) { + return; + } + qemu_mutex_lock(&comp_done_lock); for (int i = 0; i < thread_count; i++) { while (!comp_param[i].done) { @@ -257,7 +261,7 @@ void flush_compressed_data(int (send_queued_data(CompressParam *))) qemu_mutex_lock(&comp_param[i].mutex); if (!comp_param[i].quit) { CompressParam *param = &comp_param[i]; - send_queued_data(param); + compress_send_queued_data(param); assert(qemu_file_buffer_empty(param->file)); compress_reset_result(param); } @@ -558,12 +562,3 @@ void compress_update_rates(uint64_t page_count) compression_counters.compressed_size; } } - -void compress_flush_data(void) -{ - if (!migrate_compress()) { - return; - } - - flush_compressed_data(compress_send_queued_data); -} diff --git a/migration/ram-compress.h b/migration/ram-compress.h index 7ba01e2882..e222887fb7 100644 --- a/migration/ram-compress.h +++ b/migration/ram-compress.h @@ -59,7 +59,6 @@ typedef struct CompressParam CompressParam; void compress_threads_save_cleanup(void); int compress_threads_save_setup(void); -void flush_compressed_data(int (send_queued_data(CompressParam *))); bool compress_page_with_multi_thread(RAMBlock *block, ram_addr_t offset, int (send_queued_data(CompressParam *))); From 8258f2fa06f71c7a48b95c39c896626564fe3474 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Thu, 19 Oct 2023 13:07:24 +0200 Subject: [PATCH 202/974] migration: Rename ram_compressed_pages() to compress_ram_pages() We are moving to have all functions exported from ram-compress.c to start with compress_. Reviewed-by: Fabiano Rosas Signed-off-by: Juan Quintela Message-ID: <20231019110724.15324-12-quintela@redhat.com> --- migration/ram-compress.c | 2 +- migration/ram-compress.h | 2 +- migration/ram.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/migration/ram-compress.c b/migration/ram-compress.c index 036e44085b..fa4388f6a6 100644 --- a/migration/ram-compress.c +++ b/migration/ram-compress.c @@ -516,7 +516,7 @@ void populate_compress(MigrationInfo *info) info->compression->compression_rate = compression_counters.compression_rate; } -uint64_t ram_compressed_pages(void) +uint64_t compress_ram_pages(void) { return compression_counters.pages; } diff --git a/migration/ram-compress.h b/migration/ram-compress.h index e222887fb7..0d89a2f55e 100644 --- a/migration/ram-compress.h +++ b/migration/ram-compress.h @@ -68,7 +68,7 @@ int compress_threads_load_setup(QEMUFile *f); void decompress_data_with_multi_threads(QEMUFile *f, void *host, int len); void populate_compress(MigrationInfo *info); -uint64_t ram_compressed_pages(void); +uint64_t compress_ram_pages(void); void update_compress_thread_counts(const CompressParam *param, int bytes_xmit); void compress_update_rates(uint64_t page_count); int compress_send_queued_data(CompressParam *param); diff --git a/migration/ram.c b/migration/ram.c index 849752ef29..6335564035 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -932,7 +932,7 @@ uint64_t ram_get_total_transferred_pages(void) { return stat64_get(&mig_stats.normal_pages) + stat64_get(&mig_stats.zero_pages) + - ram_compressed_pages() + xbzrle_counters.pages; + compress_ram_pages() + xbzrle_counters.pages; } static void migration_update_rates(RAMState *rs, int64_t end_time) From bc3d41a9f62905629d6da98f68cc15069d2374c6 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 24 Oct 2023 11:22:20 +0200 Subject: [PATCH 203/974] migration/ram: Fix compilation with -Wshadow=local Rename the variable here to avoid that it shadows a variable from the beginning of the function scope. With this change the code now successfully compiles with -Wshadow=local. Signed-off-by: Thomas Huth Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-ID: <20231024092220.55305-1-thuth@redhat.com> --- migration/ram.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 6335564035..024dedb6b1 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -3147,6 +3147,8 @@ static int ram_save_complete(QEMUFile *f, void *opaque) rs->last_stage = !migration_in_colo_state(); WITH_RCU_READ_LOCK_GUARD() { + int rdma_reg_ret; + if (!migration_in_postcopy()) { migration_bitmap_sync_precopy(rs, true); } @@ -3177,9 +3179,9 @@ static int ram_save_complete(QEMUFile *f, void *opaque) compress_flush_data(); - int ret = rdma_registration_stop(f, RAM_CONTROL_FINISH); - if (ret < 0) { - qemu_file_set_error(f, ret); + rdma_reg_ret = rdma_registration_stop(f, RAM_CONTROL_FINISH); + if (rdma_reg_ret < 0) { + qemu_file_set_error(f, rdma_reg_ret); } } From 4d8bdc2ae043a572a2ec6f8788123935a2de0943 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 24 Oct 2023 12:40:38 +0400 Subject: [PATCH 204/974] migration: rename vmstate_save_needed->vmstate_section_needed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function is used on save at this point. The following commits will use it on load. Signed-off-by: Marc-André Lureau Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-ID: <20231024084043.2926316-5-marcandre.lureau@redhat.com> --- include/migration/vmstate.h | 2 +- migration/savevm.c | 2 +- migration/vmstate.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index 1a31fb7293..1af181877c 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -1202,7 +1202,7 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, JSONWriter *vmdesc, int version_id, Error **errp); -bool vmstate_save_needed(const VMStateDescription *vmsd, void *opaque); +bool vmstate_section_needed(const VMStateDescription *vmsd, void *opaque); #define VMSTATE_INSTANCE_ID_ANY -1 diff --git a/migration/savevm.c b/migration/savevm.c index 8622f229e5..ca5c7cebe0 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -985,7 +985,7 @@ static int vmstate_save(QEMUFile *f, SaveStateEntry *se, JSONWriter *vmdesc) if ((!se->ops || !se->ops->save_state) && !se->vmsd) { return 0; } - if (se->vmsd && !vmstate_save_needed(se->vmsd, se->opaque)) { + if (se->vmsd && !vmstate_section_needed(se->vmsd, se->opaque)) { trace_savevm_section_skip(se->idstr, se->section_id); return 0; } diff --git a/migration/vmstate.c b/migration/vmstate.c index 1cf9e45b85..16e33a5d34 100644 --- a/migration/vmstate.c +++ b/migration/vmstate.c @@ -324,7 +324,7 @@ static void vmsd_desc_field_end(const VMStateDescription *vmsd, } -bool vmstate_save_needed(const VMStateDescription *vmsd, void *opaque) +bool vmstate_section_needed(const VMStateDescription *vmsd, void *opaque) { if (vmsd->needed && !vmsd->needed(opaque)) { /* optional section not needed */ @@ -522,7 +522,7 @@ static int vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd, trace_vmstate_subsection_save_top(vmsd->name); while (sub && *sub) { - if (vmstate_save_needed(*sub, opaque)) { + if (vmstate_section_needed(*sub, opaque)) { const VMStateDescription *vmsdsub = *sub; uint8_t len; From cd4c0da6dbeac5cc986a4a553c722cbf4b3d0300 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 24 Oct 2023 12:40:41 +0400 Subject: [PATCH 205/974] migration: set file error on subsection loading MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 13cde50889237 ("vmstate: Return error in case of error") sets QemuFile error to stop reading from it and report to the caller (checked by unit tests). We should do the same on subsection loading error. Signed-off-by: Marc-André Lureau Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-ID: <20231024084043.2926316-8-marcandre.lureau@redhat.com> --- migration/vmstate.c | 1 + 1 file changed, 1 insertion(+) diff --git a/migration/vmstate.c b/migration/vmstate.c index 16e33a5d34..9c36803c8a 100644 --- a/migration/vmstate.c +++ b/migration/vmstate.c @@ -179,6 +179,7 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, assert(field->flags == VMS_END); ret = vmstate_subsection_load(f, vmsd, opaque); if (ret != 0) { + qemu_file_set_error(f, ret); return ret; } if (vmsd->post_load) { From 02d9f5b6acfa494e756ed8a5b373412e4240afaf Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 27 Oct 2023 22:03:08 +0000 Subject: [PATCH 206/974] linux-user: Fix guest signal remapping after adjusting SIGABRT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The arithmetic within the loop was not adjusted properly after SIGRTMIN was stolen for the guest SIGABRT. The effect was that the guest libc could not send itself __SIGRTMIN to wake sleeping threads. Fixes: 38ee0a7dfb4b ("linux-user: Remap guest SIGABRT") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1967 Reviewed-by: Alex Bennée Signed-off-by: Richard Henderson --- linux-user/signal.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/linux-user/signal.c b/linux-user/signal.c index 3b8efec89f..b35d1e512f 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -536,11 +536,10 @@ static void signal_table_init(void) host_to_target_signal_table[SIGABRT] = 0; host_to_target_signal_table[hsig++] = TARGET_SIGABRT; - for (; hsig <= SIGRTMAX; hsig++) { - tsig = hsig - SIGRTMIN + TARGET_SIGRTMIN; - if (tsig <= TARGET_NSIG) { - host_to_target_signal_table[hsig] = tsig; - } + for (tsig = TARGET_SIGRTMIN; + hsig <= SIGRTMAX && tsig <= TARGET_NSIG; + hsig++, tsig++) { + host_to_target_signal_table[hsig] = tsig; } /* Invert the mapping that has already been assigned. */ From 7d2c5526ed85dcd7c81d91a3e2782db883366143 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 16 Jun 2021 18:59:57 -0700 Subject: [PATCH 207/974] linux-user: Introduce imgsrc_read, imgsrc_read_alloc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduced and initialized, but not yet really used. These will tidy the current tests vs BPRM_BUF_SIZE. Reviewed-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/linuxload.c | 90 ++++++++++++++++++++++++++++++++++++++++++ linux-user/loader.h | 61 +++++++++++++++++++++++----- 2 files changed, 142 insertions(+), 9 deletions(-) diff --git a/linux-user/linuxload.c b/linux-user/linuxload.c index 745cce70ab..3536dd8104 100644 --- a/linux-user/linuxload.c +++ b/linux-user/linuxload.c @@ -3,7 +3,9 @@ #include "qemu/osdep.h" #include "qemu.h" #include "user-internals.h" +#include "user-mmap.h" #include "loader.h" +#include "qapi/error.h" #define NGROUPS 32 @@ -76,6 +78,10 @@ static int prepare_binprm(struct linux_binprm *bprm) /* Make sure the rest of the loader won't read garbage. */ memset(bprm->buf + retval, 0, BPRM_BUF_SIZE - retval); } + + bprm->src.cache = bprm->buf; + bprm->src.cache_size = retval; + return retval; } @@ -139,6 +145,7 @@ int loader_exec(int fdexec, const char *filename, char **argv, char **envp, int retval; bprm->fd = fdexec; + bprm->src.fd = fdexec; bprm->filename = (char *)filename; bprm->argc = count(argv); bprm->argv = argv; @@ -173,3 +180,86 @@ int loader_exec(int fdexec, const char *filename, char **argv, char **envp, return retval; } + +bool imgsrc_read(void *dst, off_t offset, size_t len, + const ImageSource *img, Error **errp) +{ + ssize_t ret; + + if (offset + len <= img->cache_size) { + memcpy(dst, img->cache + offset, len); + return true; + } + + if (img->fd < 0) { + error_setg(errp, "read past end of buffer"); + return false; + } + + ret = pread(img->fd, dst, len, offset); + if (ret == len) { + return true; + } + if (ret < 0) { + error_setg_errno(errp, errno, "Error reading file header"); + } else { + error_setg(errp, "Incomplete read of file header"); + } + return false; +} + +void *imgsrc_read_alloc(off_t offset, size_t len, + const ImageSource *img, Error **errp) +{ + void *alloc = g_malloc(len); + bool ok = imgsrc_read(alloc, offset, len, img, errp); + + if (!ok) { + g_free(alloc); + alloc = NULL; + } + return alloc; +} + +abi_long imgsrc_mmap(abi_ulong start, abi_ulong len, int prot, + int flags, const ImageSource *src, abi_ulong offset) +{ + const int prot_write = PROT_READ | PROT_WRITE; + abi_long ret; + void *haddr; + + assert(flags == (MAP_PRIVATE | MAP_FIXED)); + + if (src->fd >= 0) { + return target_mmap(start, len, prot, flags, src->fd, offset); + } + + /* + * This case is for the vdso; we don't expect bad images. + * The mmap may extend beyond the end of the image, especially + * to the end of the page. Zero fill. + */ + assert(offset < src->cache_size); + + ret = target_mmap(start, len, prot_write, flags | MAP_ANON, -1, 0); + if (ret == -1) { + return ret; + } + + haddr = lock_user(VERIFY_WRITE, start, len, 0); + assert(haddr != NULL); + if (offset + len <= src->cache_size) { + memcpy(haddr, src->cache + offset, len); + } else { + size_t rest = src->cache_size - offset; + memcpy(haddr, src->cache + offset, rest); + memset(haddr + rest, 0, len - rest); + } + unlock_user(haddr, start, len); + + if (prot != prot_write) { + target_mprotect(start, len, prot); + } + + return ret; +} diff --git a/linux-user/loader.h b/linux-user/loader.h index 324e5c872a..da6591fff0 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -18,6 +18,48 @@ #ifndef LINUX_USER_LOADER_H #define LINUX_USER_LOADER_H +typedef struct { + const void *cache; + unsigned int cache_size; + int fd; +} ImageSource; + +/** + * imgsrc_read: Read from ImageSource + * @dst: destination for read + * @offset: offset within file for read + * @len: size of the read + * @img: ImageSource to read from + * @errp: Error details. + * + * Read into @dst, using the cache when possible. + */ +bool imgsrc_read(void *dst, off_t offset, size_t len, + const ImageSource *img, Error **errp); + +/** + * imgsrc_read_alloc: Read from ImageSource + * @offset: offset within file for read + * @size: size of the read + * @img: ImageSource to read from + * @errp: Error details. + * + * Read into newly allocated memory, using the cache when possible. + */ +void *imgsrc_read_alloc(off_t offset, size_t len, + const ImageSource *img, Error **errp); + +/** + * imgsrc_mmap: Map from ImageSource + * + * If @src has a file descriptor, pass on to target_mmap. Otherwise, + * this is "mapping" from a host buffer, which resolves to memcpy. + * Therefore, flags must be MAP_PRIVATE | MAP_FIXED; the argument is + * retained for clarity. + */ +abi_long imgsrc_mmap(abi_ulong start, abi_ulong len, int prot, + int flags, const ImageSource *src, abi_ulong offset); + /* * Read a good amount of data initially, to hopefully get all the * program headers loaded. @@ -29,15 +71,16 @@ * used when loading binaries. */ struct linux_binprm { - char buf[BPRM_BUF_SIZE] __attribute__((aligned)); - abi_ulong p; - int fd; - int e_uid, e_gid; - int argc, envc; - char **argv; - char **envp; - char *filename; /* Name of binary */ - int (*core_dump)(int, const CPUArchState *); /* coredump routine */ + char buf[BPRM_BUF_SIZE] __attribute__((aligned)); + ImageSource src; + abi_ulong p; + int fd; + int e_uid, e_gid; + int argc, envc; + char **argv; + char **envp; + char *filename; /* Name of binary */ + int (*core_dump)(int, const CPUArchState *); /* coredump routine */ }; void do_init_thread(struct target_pt_regs *regs, struct image_info *infop); From f485be725d4a08c950bf6996e33c178bfbd98509 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 16 Jun 2021 19:37:00 -0700 Subject: [PATCH 208/974] linux-user: Tidy loader_exec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reorg the if cases to reduce indentation. Test for 4 bytes in the file before checking the signatures. Reviewed-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/linuxload.c | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/linux-user/linuxload.c b/linux-user/linuxload.c index 3536dd8104..5b7e9ab983 100644 --- a/linux-user/linuxload.c +++ b/linux-user/linuxload.c @@ -154,31 +154,31 @@ int loader_exec(int fdexec, const char *filename, char **argv, char **envp, retval = prepare_binprm(bprm); - if (retval >= 0) { - if (bprm->buf[0] == 0x7f - && bprm->buf[1] == 'E' - && bprm->buf[2] == 'L' - && bprm->buf[3] == 'F') { - retval = load_elf_binary(bprm, infop); -#if defined(TARGET_HAS_BFLT) - } else if (bprm->buf[0] == 'b' - && bprm->buf[1] == 'F' - && bprm->buf[2] == 'L' - && bprm->buf[3] == 'T') { - retval = load_flt_binary(bprm, infop); -#endif - } else { - return -ENOEXEC; - } + if (retval < 4) { + return -ENOEXEC; } - - if (retval >= 0) { - /* success. Initialize important registers */ - do_init_thread(regs, infop); + if (bprm->buf[0] == 0x7f + && bprm->buf[1] == 'E' + && bprm->buf[2] == 'L' + && bprm->buf[3] == 'F') { + retval = load_elf_binary(bprm, infop); +#if defined(TARGET_HAS_BFLT) + } else if (bprm->buf[0] == 'b' + && bprm->buf[1] == 'F' + && bprm->buf[2] == 'L' + && bprm->buf[3] == 'T') { + retval = load_flt_binary(bprm, infop); +#endif + } else { + return -ENOEXEC; + } + if (retval < 0) { return retval; } - return retval; + /* Success. Initialize important registers. */ + do_init_thread(regs, infop); + return 0; } bool imgsrc_read(void *dst, off_t offset, size_t len, From 40d487eecfac331234cf7a5bfd26ff6f667b2b82 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 18 Jun 2021 19:27:43 -0700 Subject: [PATCH 209/974] linux-user: Do not clobber bprm_buf swapping ehdr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rearrange the allocation of storage for ehdr between load_elf_image and load_elf_binary. The same set of copies are done, but we don't modify bprm_buf, which will be important later. Reviewed-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/elfload.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 2e3809f03c..d5af354a78 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -3199,16 +3199,17 @@ static bool parse_elf_properties(int image_fd, On return: INFO values will be filled in, as necessary or available. */ static void load_elf_image(const char *image_name, int image_fd, - struct image_info *info, char **pinterp_name, + struct image_info *info, struct elfhdr *ehdr, + char **pinterp_name, char bprm_buf[BPRM_BUF_SIZE]) { - struct elfhdr *ehdr = (struct elfhdr *)bprm_buf; struct elf_phdr *phdr; abi_ulong load_addr, load_bias, loaddr, hiaddr, error; int i, retval, prot_exec; Error *err = NULL; /* First of all, some simple consistency checks */ + memcpy(ehdr, bprm_buf, sizeof(*ehdr)); if (!elf_check_ident(ehdr)) { error_setg(&err, "Invalid ELF image for this architecture"); goto exit_errmsg; @@ -3523,6 +3524,7 @@ static void load_elf_image(const char *image_name, int image_fd, static void load_elf_interp(const char *filename, struct image_info *info, char bprm_buf[BPRM_BUF_SIZE]) { + struct elfhdr ehdr; int fd, retval; Error *err = NULL; @@ -3544,7 +3546,7 @@ static void load_elf_interp(const char *filename, struct image_info *info, memset(bprm_buf + retval, 0, BPRM_BUF_SIZE - retval); } - load_elf_image(filename, fd, info, NULL, bprm_buf); + load_elf_image(filename, fd, info, &ehdr, NULL, bprm_buf); } static int symfind(const void *s0, const void *s1) @@ -3737,8 +3739,14 @@ uint32_t get_elf_eflags(int fd) int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) { + /* + * We need a copy of the elf header for passing to create_elf_tables. + * We will have overwritten the original when we re-use bprm->buf + * while loading the interpreter. Allocate the storage for this now + * and let elf_load_image do any swapping that may be required. + */ + struct elfhdr ehdr; struct image_info interp_info; - struct elfhdr elf_ex; char *elf_interpreter = NULL; char *scratch; @@ -3748,12 +3756,7 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) #endif load_elf_image(bprm->filename, bprm->fd, info, - &elf_interpreter, bprm->buf); - - /* ??? We need a copy of the elf header for passing to create_elf_tables. - If we do nothing, we'll have overwritten this when we re-use bprm->buf - when we load the interpreter. */ - elf_ex = *(struct elfhdr *)bprm->buf; + &ehdr, &elf_interpreter, bprm->buf); /* Do this so that we can load the interpreter, if need be. We will change some of these later */ @@ -3840,7 +3843,7 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) target_mprotect(tramp_page, TARGET_PAGE_SIZE, PROT_READ | PROT_EXEC); } - bprm->p = create_elf_tables(bprm->p, bprm->argc, bprm->envc, &elf_ex, + bprm->p = create_elf_tables(bprm->p, bprm->argc, bprm->envc, &ehdr, info, (elf_interpreter ? &interp_info : NULL)); info->start_stack = bprm->p; From 3bd0238638070274b9ce3464f0456cdf6ca0884d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 16 Jun 2021 20:32:56 -0700 Subject: [PATCH 210/974] linux-user: Use ImageSource in load_elf_image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change parse_elf_properties as well, as the bprm_buf argument ties the two functions closely. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/elfload.c | 128 +++++++++++++++++-------------------------- 1 file changed, 49 insertions(+), 79 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index d5af354a78..d763cb855a 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -3102,10 +3102,9 @@ static bool parse_elf_property(const uint32_t *data, int *off, int datasz, } /* Process NT_GNU_PROPERTY_TYPE_0. */ -static bool parse_elf_properties(int image_fd, +static bool parse_elf_properties(const ImageSource *src, struct image_info *info, const struct elf_phdr *phdr, - char bprm_buf[BPRM_BUF_SIZE], Error **errp) { union { @@ -3133,14 +3132,8 @@ static bool parse_elf_properties(int image_fd, return false; } - if (phdr->p_offset + n <= BPRM_BUF_SIZE) { - memcpy(¬e, bprm_buf + phdr->p_offset, n); - } else { - ssize_t len = pread(image_fd, ¬e, n, phdr->p_offset); - if (len != n) { - error_setg_errno(errp, errno, "Error reading file header"); - return false; - } + if (!imgsrc_read(¬e, phdr->p_offset, n, src, errp)) { + return false; } /* @@ -3186,30 +3179,34 @@ static bool parse_elf_properties(int image_fd, } } -/* Load an ELF image into the address space. +/** + * load_elf_image: Load an ELF image into the address space. + * @image_name: the filename of the image, to use in error messages. + * @src: the ImageSource from which to read. + * @info: info collected from the loaded image. + * @ehdr: the ELF header, not yet bswapped. + * @pinterp_name: record any PT_INTERP string found. + * + * On return: @info values will be filled in, as necessary or available. + */ - IMAGE_NAME is the filename of the image, to use in error messages. - IMAGE_FD is the open file descriptor for the image. - - BPRM_BUF is a copy of the beginning of the file; this of course - contains the elf file header at offset 0. It is assumed that this - buffer is sufficiently aligned to present no problems to the host - in accessing data at aligned offsets within the buffer. - - On return: INFO values will be filled in, as necessary or available. */ - -static void load_elf_image(const char *image_name, int image_fd, +static void load_elf_image(const char *image_name, const ImageSource *src, struct image_info *info, struct elfhdr *ehdr, - char **pinterp_name, - char bprm_buf[BPRM_BUF_SIZE]) + char **pinterp_name) { - struct elf_phdr *phdr; + g_autofree struct elf_phdr *phdr = NULL; abi_ulong load_addr, load_bias, loaddr, hiaddr, error; - int i, retval, prot_exec; + int i, prot_exec; Error *err = NULL; - /* First of all, some simple consistency checks */ - memcpy(ehdr, bprm_buf, sizeof(*ehdr)); + /* + * First of all, some simple consistency checks. + * Note that we rely on the bswapped ehdr staying in bprm_buf, + * for later use by load_elf_binary and create_elf_tables. + */ + if (!imgsrc_read(ehdr, 0, sizeof(*ehdr), src, &err)) { + goto exit_errmsg; + } if (!elf_check_ident(ehdr)) { error_setg(&err, "Invalid ELF image for this architecture"); goto exit_errmsg; @@ -3220,15 +3217,11 @@ static void load_elf_image(const char *image_name, int image_fd, goto exit_errmsg; } - i = ehdr->e_phnum * sizeof(struct elf_phdr); - if (ehdr->e_phoff + i <= BPRM_BUF_SIZE) { - phdr = (struct elf_phdr *)(bprm_buf + ehdr->e_phoff); - } else { - phdr = (struct elf_phdr *) alloca(i); - retval = pread(image_fd, phdr, i, ehdr->e_phoff); - if (retval != i) { - goto exit_read; - } + phdr = imgsrc_read_alloc(ehdr->e_phoff, + ehdr->e_phnum * sizeof(struct elf_phdr), + src, &err); + if (phdr == NULL) { + goto exit_errmsg; } bswap_phdr(phdr, ehdr->e_phnum); @@ -3265,17 +3258,10 @@ static void load_elf_image(const char *image_name, int image_fd, goto exit_errmsg; } - interp_name = g_malloc(eppnt->p_filesz); - - if (eppnt->p_offset + eppnt->p_filesz <= BPRM_BUF_SIZE) { - memcpy(interp_name, bprm_buf + eppnt->p_offset, - eppnt->p_filesz); - } else { - retval = pread(image_fd, interp_name, eppnt->p_filesz, - eppnt->p_offset); - if (retval != eppnt->p_filesz) { - goto exit_read; - } + interp_name = imgsrc_read_alloc(eppnt->p_offset, eppnt->p_filesz, + src, &err); + if (interp_name == NULL) { + goto exit_errmsg; } if (interp_name[eppnt->p_filesz - 1] != 0) { error_setg(&err, "Invalid PT_INTERP entry"); @@ -3283,7 +3269,7 @@ static void load_elf_image(const char *image_name, int image_fd, } *pinterp_name = g_steal_pointer(&interp_name); } else if (eppnt->p_type == PT_GNU_PROPERTY) { - if (!parse_elf_properties(image_fd, info, eppnt, bprm_buf, &err)) { + if (!parse_elf_properties(src, info, eppnt, &err)) { goto exit_errmsg; } } else if (eppnt->p_type == PT_GNU_STACK) { @@ -3436,9 +3422,9 @@ static void load_elf_image(const char *image_name, int image_fd, * but no backing file segment. */ if (eppnt->p_filesz != 0) { - error = target_mmap(vaddr_ps, eppnt->p_filesz + vaddr_po, + error = imgsrc_mmap(vaddr_ps, eppnt->p_filesz + vaddr_po, elf_prot, MAP_PRIVATE | MAP_FIXED, - image_fd, eppnt->p_offset - vaddr_po); + src, eppnt->p_offset - vaddr_po); if (error == -1) { goto exit_mmap; } @@ -3470,20 +3456,11 @@ static void load_elf_image(const char *image_name, int image_fd, #ifdef TARGET_MIPS } else if (eppnt->p_type == PT_MIPS_ABIFLAGS) { Mips_elf_abiflags_v0 abiflags; - if (eppnt->p_filesz < sizeof(Mips_elf_abiflags_v0)) { - error_setg(&err, "Invalid PT_MIPS_ABIFLAGS entry"); + + if (!imgsrc_read(&abiflags, eppnt->p_offset, sizeof(abiflags), + src, &err)) { goto exit_errmsg; } - if (eppnt->p_offset + eppnt->p_filesz <= BPRM_BUF_SIZE) { - memcpy(&abiflags, bprm_buf + eppnt->p_offset, - sizeof(Mips_elf_abiflags_v0)); - } else { - retval = pread(image_fd, &abiflags, sizeof(Mips_elf_abiflags_v0), - eppnt->p_offset); - if (retval != sizeof(Mips_elf_abiflags_v0)) { - goto exit_read; - } - } bswap_mips_abiflags(&abiflags); info->fp_abi = abiflags.fp_abi; #endif @@ -3496,23 +3473,16 @@ static void load_elf_image(const char *image_name, int image_fd, } if (qemu_log_enabled()) { - load_symbols(ehdr, image_fd, load_bias); + load_symbols(ehdr, src->fd, load_bias); } - debuginfo_report_elf(image_name, image_fd, load_bias); + debuginfo_report_elf(image_name, src->fd, load_bias); mmap_unlock(); - close(image_fd); + close(src->fd); return; - exit_read: - if (retval >= 0) { - error_setg(&err, "Incomplete read of file header"); - } else { - error_setg_errno(&err, errno, "Error reading file header"); - } - goto exit_errmsg; exit_mmap: error_setg_errno(&err, errno, "Error mapping file"); goto exit_errmsg; @@ -3525,6 +3495,7 @@ static void load_elf_interp(const char *filename, struct image_info *info, char bprm_buf[BPRM_BUF_SIZE]) { struct elfhdr ehdr; + ImageSource src; int fd, retval; Error *err = NULL; @@ -3542,11 +3513,11 @@ static void load_elf_interp(const char *filename, struct image_info *info, exit(-1); } - if (retval < BPRM_BUF_SIZE) { - memset(bprm_buf + retval, 0, BPRM_BUF_SIZE - retval); - } + src.fd = fd; + src.cache = bprm_buf; + src.cache_size = retval; - load_elf_image(filename, fd, info, &ehdr, NULL, bprm_buf); + load_elf_image(filename, &src, info, &ehdr, NULL); } static int symfind(const void *s0, const void *s1) @@ -3755,8 +3726,7 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) interp_info.fp_abi = MIPS_ABI_FP_UNKNOWN; #endif - load_elf_image(bprm->filename, bprm->fd, info, - &ehdr, &elf_interpreter, bprm->buf); + load_elf_image(bprm->filename, &bprm->src, info, &ehdr, &elf_interpreter); /* Do this so that we can load the interpreter, if need be. We will change some of these later */ From 86cf82dc9fa366d6758f4b1f9587c8d2f6062bad Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 16 Jun 2021 20:59:49 -0700 Subject: [PATCH 211/974] linux-user: Use ImageSource in load_symbols MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Aside from the section headers, we're unlikely to hit the ImageSource cache on guest executables. But the interface for imgsrc_read_* is better. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/elfload.c | 87 ++++++++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 39 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index d763cb855a..88c2b01402 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -2201,7 +2201,8 @@ static inline void bswap_mips_abiflags(Mips_elf_abiflags_v0 *abiflags) { } #ifdef USE_ELF_CORE_DUMP static int elf_core_dump(int, const CPUArchState *); #endif /* USE_ELF_CORE_DUMP */ -static void load_symbols(struct elfhdr *hdr, int fd, abi_ulong load_bias); +static void load_symbols(struct elfhdr *hdr, const ImageSource *src, + abi_ulong load_bias); /* Verify the portions of EHDR within E_IDENT for the target. This can be performed before bswapping the entire header. */ @@ -3473,7 +3474,7 @@ static void load_elf_image(const char *image_name, const ImageSource *src, } if (qemu_log_enabled()) { - load_symbols(ehdr, src->fd, load_bias); + load_symbols(ehdr, src, load_bias); } debuginfo_report_elf(image_name, src->fd, load_bias); @@ -3564,19 +3565,20 @@ static int symcmp(const void *s0, const void *s1) } /* Best attempt to load symbols from this ELF object. */ -static void load_symbols(struct elfhdr *hdr, int fd, abi_ulong load_bias) +static void load_symbols(struct elfhdr *hdr, const ImageSource *src, + abi_ulong load_bias) { int i, shnum, nsyms, sym_idx = 0, str_idx = 0; - uint64_t segsz; - struct elf_shdr *shdr; + g_autofree struct elf_shdr *shdr = NULL; char *strings = NULL; - struct syminfo *s = NULL; - struct elf_sym *new_syms, *syms = NULL; + struct elf_sym *syms = NULL; + struct elf_sym *new_syms; + uint64_t segsz; shnum = hdr->e_shnum; - i = shnum * sizeof(struct elf_shdr); - shdr = (struct elf_shdr *)alloca(i); - if (pread(fd, shdr, i, hdr->e_shoff) != i) { + shdr = imgsrc_read_alloc(hdr->e_shoff, shnum * sizeof(struct elf_shdr), + src, NULL); + if (shdr == NULL) { return; } @@ -3594,31 +3596,33 @@ static void load_symbols(struct elfhdr *hdr, int fd, abi_ulong load_bias) found: /* Now know where the strtab and symtab are. Snarf them. */ - s = g_try_new(struct syminfo, 1); - if (!s) { - goto give_up; - } segsz = shdr[str_idx].sh_size; - s->disas_strtab = strings = g_try_malloc(segsz); - if (!strings || - pread(fd, strings, segsz, shdr[str_idx].sh_offset) != segsz) { + strings = g_try_malloc(segsz); + if (!strings) { + goto give_up; + } + if (!imgsrc_read(strings, shdr[str_idx].sh_offset, segsz, src, NULL)) { goto give_up; } segsz = shdr[sym_idx].sh_size; - syms = g_try_malloc(segsz); - if (!syms || pread(fd, syms, segsz, shdr[sym_idx].sh_offset) != segsz) { - goto give_up; - } - if (segsz / sizeof(struct elf_sym) > INT_MAX) { - /* Implausibly large symbol table: give up rather than ploughing - * on with the number of symbols calculation overflowing + /* + * Implausibly large symbol table: give up rather than ploughing + * on with the number of symbols calculation overflowing. */ goto give_up; } nsyms = segsz / sizeof(struct elf_sym); + syms = g_try_malloc(segsz); + if (!syms) { + goto give_up; + } + if (!imgsrc_read(syms, shdr[sym_idx].sh_offset, segsz, src, NULL)) { + goto give_up; + } + for (i = 0; i < nsyms; ) { bswap_sym(syms + i); /* Throw away entries which we do not need. */ @@ -3643,10 +3647,12 @@ static void load_symbols(struct elfhdr *hdr, int fd, abi_ulong load_bias) goto give_up; } - /* Attempt to free the storage associated with the local symbols - that we threw away. Whether or not this has any effect on the - memory allocation depends on the malloc implementation and how - many symbols we managed to discard. */ + /* + * Attempt to free the storage associated with the local symbols + * that we threw away. Whether or not this has any effect on the + * memory allocation depends on the malloc implementation and how + * many symbols we managed to discard. + */ new_syms = g_try_renew(struct elf_sym, syms, nsyms); if (new_syms == NULL) { goto give_up; @@ -3655,20 +3661,23 @@ static void load_symbols(struct elfhdr *hdr, int fd, abi_ulong load_bias) qsort(syms, nsyms, sizeof(*syms), symcmp); - s->disas_num_syms = nsyms; -#if ELF_CLASS == ELFCLASS32 - s->disas_symtab.elf32 = syms; -#else - s->disas_symtab.elf64 = syms; -#endif - s->lookup_symbol = lookup_symbolxx; - s->next = syminfos; - syminfos = s; + { + struct syminfo *s = g_new(struct syminfo, 1); + s->disas_strtab = strings; + s->disas_num_syms = nsyms; +#if ELF_CLASS == ELFCLASS32 + s->disas_symtab.elf32 = syms; +#else + s->disas_symtab.elf64 = syms; +#endif + s->lookup_symbol = lookup_symbolxx; + s->next = syminfos; + syminfos = s; + } return; -give_up: - g_free(s); + give_up: g_free(strings); g_free(syms); } From d0b6b79323726e47b1e9e399870ab063883874d0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 16 Jun 2021 21:08:15 -0700 Subject: [PATCH 212/974] linux-user: Replace bprm->fd with bprm->src.fd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are only a couple of uses of bprm->fd remaining. Migrate to the other field. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/flatload.c | 8 ++++---- linux-user/linuxload.c | 5 ++--- linux-user/loader.h | 1 - 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/linux-user/flatload.c b/linux-user/flatload.c index fdcc4610fa..5b62aa0a2b 100644 --- a/linux-user/flatload.c +++ b/linux-user/flatload.c @@ -463,7 +463,7 @@ static int load_flat_file(struct linux_binprm * bprm, DBG_FLT("BINFMT_FLAT: ROM mapping of file (we hope)\n"); textpos = target_mmap(0, text_len, PROT_READ|PROT_EXEC, - MAP_PRIVATE, bprm->fd, 0); + MAP_PRIVATE, bprm->src.fd, 0); if (textpos == -1) { fprintf(stderr, "Unable to mmap process text\n"); return -1; @@ -490,7 +490,7 @@ static int load_flat_file(struct linux_binprm * bprm, } else #endif { - result = target_pread(bprm->fd, datapos, + result = target_pread(bprm->src.fd, datapos, data_len + (relocs * sizeof(abi_ulong)), fpos); } @@ -540,10 +540,10 @@ static int load_flat_file(struct linux_binprm * bprm, else #endif { - result = target_pread(bprm->fd, textpos, + result = target_pread(bprm->src.fd, textpos, text_len, 0); if (result >= 0) { - result = target_pread(bprm->fd, datapos, + result = target_pread(bprm->src.fd, datapos, data_len + (relocs * sizeof(abi_ulong)), ntohl(hdr->data_start)); } diff --git a/linux-user/linuxload.c b/linux-user/linuxload.c index 5b7e9ab983..4a794f8cea 100644 --- a/linux-user/linuxload.c +++ b/linux-user/linuxload.c @@ -39,7 +39,7 @@ static int prepare_binprm(struct linux_binprm *bprm) int mode; int retval; - if (fstat(bprm->fd, &st) < 0) { + if (fstat(bprm->src.fd, &st) < 0) { return -errno; } @@ -69,7 +69,7 @@ static int prepare_binprm(struct linux_binprm *bprm) bprm->e_gid = st.st_gid; } - retval = read(bprm->fd, bprm->buf, BPRM_BUF_SIZE); + retval = read(bprm->src.fd, bprm->buf, BPRM_BUF_SIZE); if (retval < 0) { perror("prepare_binprm"); exit(-1); @@ -144,7 +144,6 @@ int loader_exec(int fdexec, const char *filename, char **argv, char **envp, { int retval; - bprm->fd = fdexec; bprm->src.fd = fdexec; bprm->filename = (char *)filename; bprm->argc = count(argv); diff --git a/linux-user/loader.h b/linux-user/loader.h index da6591fff0..a0834290e7 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -74,7 +74,6 @@ struct linux_binprm { char buf[BPRM_BUF_SIZE] __attribute__((aligned)); ImageSource src; abi_ulong p; - int fd; int e_uid, e_gid; int argc, envc; char **argv; From c40f621a19640831e9eb825119581e54414ad1a0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 17 Jun 2021 09:03:19 -0700 Subject: [PATCH 213/974] linux-user: Load vdso image if available MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vdso image will be pre-processed into a C data array, with a simple list of relocations to perform, and identifying the location of signal trampolines. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/elfload.c | 90 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 81 insertions(+), 9 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 88c2b01402..b0723071e2 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -33,6 +33,19 @@ #undef ELF_ARCH #endif +#ifndef TARGET_ARCH_HAS_SIGTRAMP_PAGE +#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 0 +#endif + +typedef struct { + const uint8_t *image; + const uint32_t *relocs; + unsigned image_size; + unsigned reloc_count; + unsigned sigreturn_ofs; + unsigned rt_sigreturn_ofs; +} VdsoImageInfo; + #define ELF_OSABI ELFOSABI_SYSV /* from personality.h */ @@ -2471,7 +2484,8 @@ static abi_ulong loader_build_fdpic_loadmap(struct image_info *info, abi_ulong s static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, struct elfhdr *exec, struct image_info *info, - struct image_info *interp_info) + struct image_info *interp_info, + struct image_info *vdso_info) { abi_ulong sp; abi_ulong u_argc, u_argv, u_envp, u_auxv; @@ -2559,10 +2573,15 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, } size = (DLINFO_ITEMS + 1) * 2; - if (k_base_platform) + if (k_base_platform) { size += 2; - if (k_platform) + } + if (k_platform) { size += 2; + } + if (vdso_info) { + size += 2; + } #ifdef DLINFO_ARCH_ITEMS size += DLINFO_ARCH_ITEMS * 2; #endif @@ -2644,6 +2663,9 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, if (u_platform) { NEW_AUX_ENT(AT_PLATFORM, u_platform); } + if (vdso_info) { + NEW_AUX_ENT(AT_SYSINFO_EHDR, vdso_info->load_addr); + } NEW_AUX_ENT (AT_NULL, 0); #undef NEW_AUX_ENT @@ -3521,6 +3543,52 @@ static void load_elf_interp(const char *filename, struct image_info *info, load_elf_image(filename, &src, info, &ehdr, NULL); } +#ifdef VDSO_HEADER +#include VDSO_HEADER +#define vdso_image_info() &vdso_image_info +#else +#define vdso_image_info() NULL +#endif + +static void load_elf_vdso(struct image_info *info, const VdsoImageInfo *vdso) +{ + ImageSource src; + struct elfhdr ehdr; + abi_ulong load_bias, load_addr; + + src.fd = -1; + src.cache = vdso->image; + src.cache_size = vdso->image_size; + + load_elf_image("", &src, info, &ehdr, NULL); + load_addr = info->load_addr; + load_bias = info->load_bias; + + /* + * We need to relocate the VDSO image. The one built into the kernel + * is built for a fixed address. The one built for QEMU is not, since + * that requires close control of the guest address space. + * We pre-processed the image to locate all of the addresses that need + * to be updated. + */ + for (unsigned i = 0, n = vdso->reloc_count; i < n; i++) { + abi_ulong *addr = g2h_untagged(load_addr + vdso->relocs[i]); + *addr = tswapal(tswapal(*addr) + load_bias); + } + + /* Install signal trampolines, if present. */ + if (vdso->sigreturn_ofs) { + default_sigreturn = load_addr + vdso->sigreturn_ofs; + } + if (vdso->rt_sigreturn_ofs) { + default_rt_sigreturn = load_addr + vdso->rt_sigreturn_ofs; + } + + /* Remove write from VDSO segment. */ + target_mprotect(info->start_data, info->end_data - info->start_data, + PROT_READ | PROT_EXEC); +} + static int symfind(const void *s0, const void *s1) { struct elf_sym *sym = (struct elf_sym *)s1; @@ -3726,7 +3794,7 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) * and let elf_load_image do any swapping that may be required. */ struct elfhdr ehdr; - struct image_info interp_info; + struct image_info interp_info, vdso_info; char *elf_interpreter = NULL; char *scratch; @@ -3807,10 +3875,13 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) } /* - * TODO: load a vdso, which would also contain the signal trampolines. - * Otherwise, allocate a private page to hold them. + * Load a vdso if available, which will amongst other things contain the + * signal trampolines. Otherwise, allocate a separate page for them. */ - if (TARGET_ARCH_HAS_SIGTRAMP_PAGE) { + const VdsoImageInfo *vdso = vdso_image_info(); + if (vdso) { + load_elf_vdso(&vdso_info, vdso); + } else if (TARGET_ARCH_HAS_SIGTRAMP_PAGE) { abi_long tramp_page = target_mmap(0, TARGET_PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); @@ -3822,8 +3893,9 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) target_mprotect(tramp_page, TARGET_PAGE_SIZE, PROT_READ | PROT_EXEC); } - bprm->p = create_elf_tables(bprm->p, bprm->argc, bprm->envc, &ehdr, - info, (elf_interpreter ? &interp_info : NULL)); + bprm->p = create_elf_tables(bprm->p, bprm->argc, bprm->envc, &ehdr, info, + elf_interpreter ? &interp_info : NULL, + vdso ? &vdso_info : NULL); info->start_stack = bprm->p; /* If we have an interpreter, set that as the program's entry point. From 2fa536d1079772983b935ac86395a41adc391754 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 17 Jun 2021 14:30:09 -0700 Subject: [PATCH 214/974] linux-user: Add gen-vdso tool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This tool will be used for post-processing the linked vdso image, turning it into something that is easy to include into elfload.c. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/gen-vdso-elfn.c.inc | 307 +++++++++++++++++++++++++++++++++ linux-user/gen-vdso.c | 223 ++++++++++++++++++++++++ linux-user/meson.build | 6 +- 3 files changed, 535 insertions(+), 1 deletion(-) create mode 100644 linux-user/gen-vdso-elfn.c.inc create mode 100644 linux-user/gen-vdso.c diff --git a/linux-user/gen-vdso-elfn.c.inc b/linux-user/gen-vdso-elfn.c.inc new file mode 100644 index 0000000000..7034c36d5e --- /dev/null +++ b/linux-user/gen-vdso-elfn.c.inc @@ -0,0 +1,307 @@ +/* + * Post-process a vdso elf image for inclusion into qemu. + * Elf size specialization. + * + * Copyright 2023 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +static void elfN(bswap_ehdr)(ElfN(Ehdr) *ehdr) +{ + bswaps(&ehdr->e_type); /* Object file type */ + bswaps(&ehdr->e_machine); /* Architecture */ + bswaps(&ehdr->e_version); /* Object file version */ + bswaps(&ehdr->e_entry); /* Entry point virtual address */ + bswaps(&ehdr->e_phoff); /* Program header table file offset */ + bswaps(&ehdr->e_shoff); /* Section header table file offset */ + bswaps(&ehdr->e_flags); /* Processor-specific flags */ + bswaps(&ehdr->e_ehsize); /* ELF header size in bytes */ + bswaps(&ehdr->e_phentsize); /* Program header table entry size */ + bswaps(&ehdr->e_phnum); /* Program header table entry count */ + bswaps(&ehdr->e_shentsize); /* Section header table entry size */ + bswaps(&ehdr->e_shnum); /* Section header table entry count */ + bswaps(&ehdr->e_shstrndx); /* Section header string table index */ +} + +static void elfN(bswap_phdr)(ElfN(Phdr) *phdr) +{ + bswaps(&phdr->p_type); /* Segment type */ + bswaps(&phdr->p_flags); /* Segment flags */ + bswaps(&phdr->p_offset); /* Segment file offset */ + bswaps(&phdr->p_vaddr); /* Segment virtual address */ + bswaps(&phdr->p_paddr); /* Segment physical address */ + bswaps(&phdr->p_filesz); /* Segment size in file */ + bswaps(&phdr->p_memsz); /* Segment size in memory */ + bswaps(&phdr->p_align); /* Segment alignment */ +} + +static void elfN(bswap_shdr)(ElfN(Shdr) *shdr) +{ + bswaps(&shdr->sh_name); + bswaps(&shdr->sh_type); + bswaps(&shdr->sh_flags); + bswaps(&shdr->sh_addr); + bswaps(&shdr->sh_offset); + bswaps(&shdr->sh_size); + bswaps(&shdr->sh_link); + bswaps(&shdr->sh_info); + bswaps(&shdr->sh_addralign); + bswaps(&shdr->sh_entsize); +} + +static void elfN(bswap_sym)(ElfN(Sym) *sym) +{ + bswaps(&sym->st_name); + bswaps(&sym->st_value); + bswaps(&sym->st_size); + bswaps(&sym->st_shndx); +} + +static void elfN(bswap_dyn)(ElfN(Dyn) *dyn) +{ + bswaps(&dyn->d_tag); /* Dynamic type tag */ + bswaps(&dyn->d_un.d_ptr); /* Dynamic ptr or val, in union */ +} + +static void elfN(search_symtab)(ElfN(Shdr) *shdr, unsigned sym_idx, + void *buf, bool need_bswap) +{ + unsigned str_idx = shdr[sym_idx].sh_link; + ElfN(Sym) *sym = buf + shdr[sym_idx].sh_offset; + unsigned sym_n = shdr[sym_idx].sh_size / sizeof(*sym); + const char *str = buf + shdr[str_idx].sh_offset; + + for (unsigned i = 0; i < sym_n; ++i) { + const char *name; + + if (need_bswap) { + elfN(bswap_sym)(sym + i); + } + name = str + sym[i].st_name; + + if (sigreturn_sym && strcmp(sigreturn_sym, name) == 0) { + sigreturn_addr = sym[i].st_value; + } + if (rt_sigreturn_sym && strcmp(rt_sigreturn_sym, name) == 0) { + rt_sigreturn_addr = sym[i].st_value; + } + } +} + +static void elfN(process)(FILE *outf, void *buf, bool need_bswap) +{ + ElfN(Ehdr) *ehdr = buf; + ElfN(Phdr) *phdr; + ElfN(Shdr) *shdr; + unsigned phnum, shnum; + unsigned dynamic_ofs = 0; + unsigned dynamic_addr = 0; + unsigned symtab_idx = 0; + unsigned dynsym_idx = 0; + unsigned first_segsz = 0; + int errors = 0; + + if (need_bswap) { + elfN(bswap_ehdr)(ehdr); + } + + phnum = ehdr->e_phnum; + phdr = buf + ehdr->e_phoff; + if (need_bswap) { + for (unsigned i = 0; i < phnum; ++i) { + elfN(bswap_phdr)(phdr + i); + } + } + + shnum = ehdr->e_shnum; + shdr = buf + ehdr->e_shoff; + if (need_bswap) { + for (unsigned i = 0; i < shnum; ++i) { + elfN(bswap_shdr)(shdr + i); + } + } + for (unsigned i = 0; i < shnum; ++i) { + switch (shdr[i].sh_type) { + case SHT_SYMTAB: + symtab_idx = i; + break; + case SHT_DYNSYM: + dynsym_idx = i; + break; + } + } + + /* + * Validate the VDSO is created as we expect: that PT_PHDR, + * PT_DYNAMIC, and PT_NOTE located in a writable data segment. + * PHDR and DYNAMIC require relocation, and NOTE will get the + * linux version number. + */ + for (unsigned i = 0; i < phnum; ++i) { + if (phdr[i].p_type != PT_LOAD) { + continue; + } + if (first_segsz != 0) { + fprintf(stderr, "Multiple LOAD segments\n"); + errors++; + } + if (phdr[i].p_offset != 0) { + fprintf(stderr, "LOAD segment does not cover EHDR\n"); + errors++; + } + if (phdr[i].p_vaddr != 0) { + fprintf(stderr, "LOAD segment not loaded at address 0\n"); + errors++; + } + first_segsz = phdr[i].p_filesz; + if (first_segsz < ehdr->e_phoff + phnum * sizeof(*phdr)) { + fprintf(stderr, "LOAD segment does not cover PHDRs\n"); + errors++; + } + if ((phdr[i].p_flags & (PF_R | PF_W)) != (PF_R | PF_W)) { + fprintf(stderr, "LOAD segment is not read-write\n"); + errors++; + } + } + for (unsigned i = 0; i < phnum; ++i) { + const char *which; + + switch (phdr[i].p_type) { + case PT_PHDR: + which = "PT_PHDR"; + break; + case PT_NOTE: + which = "PT_NOTE"; + break; + case PT_DYNAMIC: + dynamic_ofs = phdr[i].p_offset; + dynamic_addr = phdr[i].p_vaddr; + which = "PT_DYNAMIC"; + break; + default: + continue; + } + if (first_segsz < phdr[i].p_vaddr + phdr[i].p_filesz) { + fprintf(stderr, "LOAD segment does not cover %s\n", which); + errors++; + } + } + if (errors) { + exit(EXIT_FAILURE); + } + + /* Relocate the program headers. */ + for (unsigned i = 0; i < phnum; ++i) { + output_reloc(outf, buf, &phdr[i].p_vaddr); + output_reloc(outf, buf, &phdr[i].p_paddr); + } + + /* Relocate the DYNAMIC entries. */ + if (dynamic_addr) { + ElfN(Dyn) *dyn = buf + dynamic_ofs; + __typeof(dyn->d_tag) tag; + + do { + + if (need_bswap) { + elfN(bswap_dyn)(dyn); + } + tag = dyn->d_tag; + + switch (tag) { + case DT_HASH: + case DT_SYMTAB: + case DT_STRTAB: + case DT_VERDEF: + case DT_VERSYM: + case DT_PLTGOT: + case DT_ADDRRNGLO ... DT_ADDRRNGHI: + /* These entries store an address in the entry. */ + output_reloc(outf, buf, &dyn->d_un.d_val); + break; + + case DT_NULL: + case DT_STRSZ: + case DT_SONAME: + case DT_DEBUG: + case DT_FLAGS: + case DT_FLAGS_1: + case DT_SYMBOLIC: + case DT_BIND_NOW: + case DT_VERDEFNUM: + case DT_VALRNGLO ... DT_VALRNGHI: + /* These entries store an integer in the entry. */ + break; + + case DT_SYMENT: + if (dyn->d_un.d_val != sizeof(ElfN(Sym))) { + fprintf(stderr, "VDSO has incorrect dynamic symbol size\n"); + errors++; + } + break; + + case DT_REL: + case DT_RELSZ: + case DT_RELA: + case DT_RELASZ: + /* + * These entries indicate that the VDSO was built incorrectly. + * It should not have any real relocations. + * ??? The RISC-V toolchain will emit these even when there + * are no relocations. Validate zeros. + */ + if (dyn->d_un.d_val != 0) { + fprintf(stderr, "VDSO has dynamic relocations\n"); + errors++; + } + break; + case DT_RELENT: + case DT_RELAENT: + case DT_TEXTREL: + /* These entries store an integer in the entry. */ + /* Should not be required; see above. */ + break; + + case DT_NEEDED: + case DT_VERNEED: + case DT_PLTREL: + case DT_JMPREL: + case DT_RPATH: + case DT_RUNPATH: + fprintf(stderr, "VDSO has external dependencies\n"); + errors++; + break; + + default: + /* This is probably something target specific. */ + fprintf(stderr, "VDSO has unknown DYNAMIC entry (%lx)\n", + (unsigned long)tag); + errors++; + break; + } + dyn++; + } while (tag != DT_NULL); + if (errors) { + exit(EXIT_FAILURE); + } + } + + /* Relocate the dynamic symbol table. */ + if (dynsym_idx) { + ElfN(Sym) *sym = buf + shdr[dynsym_idx].sh_offset; + unsigned sym_n = shdr[dynsym_idx].sh_size / sizeof(*sym); + + for (unsigned i = 0; i < sym_n; ++i) { + output_reloc(outf, buf, &sym[i].st_value); + } + } + + /* Search both dynsym and symtab for the signal return symbols. */ + if (dynsym_idx) { + elfN(search_symtab)(shdr, dynsym_idx, buf, need_bswap); + } + if (symtab_idx) { + elfN(search_symtab)(shdr, symtab_idx, buf, need_bswap); + } +} diff --git a/linux-user/gen-vdso.c b/linux-user/gen-vdso.c new file mode 100644 index 0000000000..31e333be80 --- /dev/null +++ b/linux-user/gen-vdso.c @@ -0,0 +1,223 @@ +/* + * Post-process a vdso elf image for inclusion into qemu. + * + * Copyright 2023 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "elf.h" + + +#define bswap_(p) _Generic(*(p), \ + uint16_t: __builtin_bswap16, \ + uint32_t: __builtin_bswap32, \ + uint64_t: __builtin_bswap64, \ + int16_t: __builtin_bswap16, \ + int32_t: __builtin_bswap32, \ + int64_t: __builtin_bswap64) +#define bswaps(p) (*(p) = bswap_(p)(*(p))) + +static void output_reloc(FILE *outf, void *buf, void *loc) +{ + fprintf(outf, " 0x%08tx,\n", loc - buf); +} + +static const char *sigreturn_sym; +static const char *rt_sigreturn_sym; + +static unsigned sigreturn_addr; +static unsigned rt_sigreturn_addr; + +#define N 32 +#define elfN(x) elf32_##x +#define ElfN(x) Elf32_##x +#include "gen-vdso-elfn.c.inc" +#undef N +#undef elfN +#undef ElfN + +#define N 64 +#define elfN(x) elf64_##x +#define ElfN(x) Elf64_##x +#include "gen-vdso-elfn.c.inc" +#undef N +#undef elfN +#undef ElfN + + +int main(int argc, char **argv) +{ + FILE *inf, *outf; + long total_len; + const char *prefix = "vdso"; + const char *inf_name; + const char *outf_name = NULL; + unsigned char *buf; + bool need_bswap; + + while (1) { + int opt = getopt(argc, argv, "o:p:r:s:"); + if (opt < 0) { + break; + } + switch (opt) { + case 'o': + outf_name = optarg; + break; + case 'p': + prefix = optarg; + break; + case 'r': + rt_sigreturn_sym = optarg; + break; + case 's': + sigreturn_sym = optarg; + break; + default: + usage: + fprintf(stderr, "usage: [-p prefix] [-r rt-sigreturn-name] " + "[-s sigreturn-name] -o output-file input-file\n"); + return EXIT_FAILURE; + } + } + + if (optind >= argc || outf_name == NULL) { + goto usage; + } + inf_name = argv[optind]; + + /* + * Open the input and output files. + */ + inf = fopen(inf_name, "rb"); + if (inf == NULL) { + goto perror_inf; + } + outf = fopen(outf_name, "w"); + if (outf == NULL) { + goto perror_outf; + } + + /* + * Read the input file into a buffer. + * We expect the vdso to be small, on the order of one page, + * therefore we do not expect a partial read. + */ + fseek(inf, 0, SEEK_END); + total_len = ftell(inf); + fseek(inf, 0, SEEK_SET); + + buf = malloc(total_len); + if (buf == NULL) { + goto perror_inf; + } + + errno = 0; + if (fread(buf, 1, total_len, inf) != total_len) { + if (errno) { + goto perror_inf; + } + fprintf(stderr, "%s: incomplete read\n", inf_name); + return EXIT_FAILURE; + } + fclose(inf); + + /* + * Write out the vdso image now, before we make local changes. + */ + + fprintf(outf, + "/* Automatically generated from linux-user/gen-vdso.c. */\n" + "\n" + "static const uint8_t %s_image[] = {", + prefix); + for (long i = 0; i < total_len; ++i) { + if (i % 12 == 0) { + fputs("\n ", outf); + } + fprintf(outf, " 0x%02x,", buf[i]); + } + fprintf(outf, "\n};\n\n"); + + /* + * Identify which elf flavor we're processing. + * The first 16 bytes of the file are e_ident. + */ + + if (buf[EI_MAG0] != ELFMAG0 || buf[EI_MAG1] != ELFMAG1 || + buf[EI_MAG2] != ELFMAG2 || buf[EI_MAG3] != ELFMAG3) { + fprintf(stderr, "%s: not an elf file\n", inf_name); + return EXIT_FAILURE; + } + switch (buf[EI_DATA]) { + case ELFDATA2LSB: + need_bswap = BYTE_ORDER != LITTLE_ENDIAN; + break; + case ELFDATA2MSB: + need_bswap = BYTE_ORDER != BIG_ENDIAN; + break; + default: + fprintf(stderr, "%s: invalid elf EI_DATA (%u)\n", + inf_name, buf[EI_DATA]); + return EXIT_FAILURE; + } + + /* + * We need to relocate the VDSO image. The one built into the kernel + * is built for a fixed address. The one we built for QEMU is not, + * since that requires close control of the guest address space. + * + * Output relocation addresses as we go. + */ + + fprintf(outf, "static const unsigned %s_relocs[] = {\n", prefix); + + switch (buf[EI_CLASS]) { + case ELFCLASS32: + elf32_process(outf, buf, need_bswap); + break; + case ELFCLASS64: + elf64_process(outf, buf, need_bswap); + break; + default: + fprintf(stderr, "%s: invalid elf EI_CLASS (%u)\n", + inf_name, buf[EI_CLASS]); + return EXIT_FAILURE; + } + + fprintf(outf, "};\n\n"); /* end vdso_relocs. */ + + fprintf(outf, "static const VdsoImageInfo %s_image_info = {\n", prefix); + fprintf(outf, " .image = %s_image,\n", prefix); + fprintf(outf, " .relocs = %s_relocs,\n", prefix); + fprintf(outf, " .image_size = sizeof(%s_image),\n", prefix); + fprintf(outf, " .reloc_count = ARRAY_SIZE(%s_relocs),\n", prefix); + fprintf(outf, " .sigreturn_ofs = 0x%x,\n", sigreturn_addr); + fprintf(outf, " .rt_sigreturn_ofs = 0x%x,\n", rt_sigreturn_addr); + fprintf(outf, "};\n"); + + /* + * Everything should have gone well. + */ + if (fclose(outf)) { + goto perror_outf; + } + return EXIT_SUCCESS; + + perror_inf: + perror(inf_name); + return EXIT_FAILURE; + + perror_outf: + perror(outf_name); + return EXIT_FAILURE; +} diff --git a/linux-user/meson.build b/linux-user/meson.build index 7171dc60be..e4cb70ed2d 100644 --- a/linux-user/meson.build +++ b/linux-user/meson.build @@ -28,9 +28,13 @@ linux_user_ss.add(when: 'TARGET_HAS_BFLT', if_true: files('flatload.c')) linux_user_ss.add(when: 'TARGET_I386', if_true: files('vm86.c')) linux_user_ss.add(when: 'CONFIG_ARM_COMPATIBLE_SEMIHOSTING', if_true: files('semihost.c')) - syscall_nr_generators = {} +gen_vdso_exe = executable('gen-vdso', 'gen-vdso.c', + native: true, build_by_default: false) +gen_vdso = generator(gen_vdso_exe, output: '@BASENAME@.c.inc', + arguments: ['-o', '@OUTPUT@', '@EXTRA_ARGS@', '@INPUT@']) + subdir('alpha') subdir('arm') subdir('hppa') From a1367443bac790c8385aca008f4b3bf9eaa8c137 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 1 Jul 2021 12:07:44 -0700 Subject: [PATCH 215/974] linux-user/i386: Add vdso MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1267 Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/elfload.c | 19 +++- linux-user/i386/Makefile.vdso | 11 +++ linux-user/i386/meson.build | 7 ++ linux-user/i386/signal.c | 11 +++ linux-user/i386/vdso-asmoffset.h | 6 ++ linux-user/i386/vdso.S | 143 +++++++++++++++++++++++++++++++ linux-user/i386/vdso.ld | 76 ++++++++++++++++ linux-user/i386/vdso.so | Bin 0 -> 2672 bytes 8 files changed, 271 insertions(+), 2 deletions(-) create mode 100644 linux-user/i386/Makefile.vdso create mode 100644 linux-user/i386/vdso-asmoffset.h create mode 100644 linux-user/i386/vdso.S create mode 100644 linux-user/i386/vdso.ld create mode 100755 linux-user/i386/vdso.so diff --git a/linux-user/elfload.c b/linux-user/elfload.c index b0723071e2..46f73a4166 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -305,12 +305,27 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUX86State *en (*regs)[15] = tswapreg(env->regs[R_ESP]); (*regs)[16] = tswapreg(env->segs[R_SS].selector & 0xffff); } -#endif + +/* + * i386 is the only target which supplies AT_SYSINFO for the vdso. + * All others only supply AT_SYSINFO_EHDR. + */ +#define DLINFO_ARCH_ITEMS (vdso_info != NULL) +#define ARCH_DLINFO \ + do { \ + if (vdso_info) { \ + NEW_AUX_ENT(AT_SYSINFO, vdso_info->entry); \ + } \ + } while (0) + +#define VDSO_HEADER "vdso.c.inc" + +#endif /* TARGET_X86_64 */ #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 -#endif +#endif /* TARGET_I386 */ #ifdef TARGET_ARM diff --git a/linux-user/i386/Makefile.vdso b/linux-user/i386/Makefile.vdso new file mode 100644 index 0000000000..95bc616f6d --- /dev/null +++ b/linux-user/i386/Makefile.vdso @@ -0,0 +1,11 @@ +include $(BUILD_DIR)/tests/tcg/i386-linux-user/config-target.mak + +SUBDIR = $(SRC_PATH)/linux-user/i386 +VPATH += $(SUBDIR) + +all: $(SUBDIR)/vdso.so + +$(SUBDIR)/vdso.so: vdso.S vdso.ld vdso-asmoffset.h + $(CC) -o $@ -m32 -nostdlib -shared -Wl,-h,linux-gate.so.1 \ + -Wl,--build-id=sha1 -Wl,--hash-style=both \ + -Wl,-T,$(SUBDIR)/vdso.ld $< diff --git a/linux-user/i386/meson.build b/linux-user/i386/meson.build index ee523019a5..d42fc6cbc9 100644 --- a/linux-user/i386/meson.build +++ b/linux-user/i386/meson.build @@ -3,3 +3,10 @@ syscall_nr_generators += { arguments: [ meson.current_source_dir() / 'syscallhdr.sh', '@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@' ], output: '@BASENAME@_nr.h') } + +vdso_inc = gen_vdso.process('vdso.so', extra_args: [ + '-s', '__kernel_sigreturn', + '-r', '__kernel_rt_sigreturn' + ]) + +linux_user_ss.add(when: 'TARGET_I386', if_true: vdso_inc) diff --git a/linux-user/i386/signal.c b/linux-user/i386/signal.c index 60fa07d6f9..bc5d45302e 100644 --- a/linux-user/i386/signal.c +++ b/linux-user/i386/signal.c @@ -214,6 +214,17 @@ struct rt_sigframe { }; #define TARGET_RT_SIGFRAME_FXSAVE_OFFSET ( \ offsetof(struct rt_sigframe, fpstate) + TARGET_FPSTATE_FXSAVE_OFFSET) + +/* + * Verify that vdso-asmoffset.h constants match. + */ +#include "i386/vdso-asmoffset.h" + +QEMU_BUILD_BUG_ON(offsetof(struct sigframe, sc.eip) + != SIGFRAME_SIGCONTEXT_eip); +QEMU_BUILD_BUG_ON(offsetof(struct rt_sigframe, uc.tuc_mcontext.eip) + != RT_SIGFRAME_SIGCONTEXT_eip); + #else struct rt_sigframe { diff --git a/linux-user/i386/vdso-asmoffset.h b/linux-user/i386/vdso-asmoffset.h new file mode 100644 index 0000000000..4e5ee0dd49 --- /dev/null +++ b/linux-user/i386/vdso-asmoffset.h @@ -0,0 +1,6 @@ +/* + * offsetof(struct sigframe, sc.eip) + * offsetof(struct rt_sigframe, uc.tuc_mcontext.eip) + */ +#define SIGFRAME_SIGCONTEXT_eip 64 +#define RT_SIGFRAME_SIGCONTEXT_eip 220 diff --git a/linux-user/i386/vdso.S b/linux-user/i386/vdso.S new file mode 100644 index 0000000000..e7a1f333a1 --- /dev/null +++ b/linux-user/i386/vdso.S @@ -0,0 +1,143 @@ +/* + * i386 linux replacement vdso. + * + * Copyright 2023 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include "vdso-asmoffset.h" + +.macro endf name + .globl \name + .type \name, @function + .size \name, . - \name +.endm + +.macro vdso_syscall1 name, nr +\name: + .cfi_startproc + mov %ebx, %edx + .cfi_register %ebx, %edx + mov 4(%esp), %ebx + mov $\nr, %eax + int $0x80 + mov %edx, %ebx + ret + .cfi_endproc +endf \name +.endm + +.macro vdso_syscall2 name, nr +\name: + .cfi_startproc + mov %ebx, %edx + .cfi_register %ebx, %edx + mov 4(%esp), %ebx + mov 8(%esp), %ecx + mov $\nr, %eax + int $0x80 + mov %edx, %ebx + ret + .cfi_endproc +endf \name +.endm + +.macro vdso_syscall3 name, nr +\name: + .cfi_startproc + push %ebx + .cfi_adjust_cfa_offset 4 + .cfi_rel_offset %ebx, 0 + mov 8(%esp), %ebx + mov 12(%esp), %ecx + mov 16(%esp), %edx + mov $\nr, %eax + int $0x80 + pop %ebx + .cfi_adjust_cfa_offset -4 + .cfi_restore %ebx + ret + .cfi_endproc +endf \name +.endm + +__kernel_vsyscall: + .cfi_startproc + int $0x80 + ret + .cfi_endproc +endf __kernel_vsyscall + +vdso_syscall2 __vdso_clock_gettime, __NR_clock_gettime +vdso_syscall2 __vdso_clock_gettime64, __NR_clock_gettime64 +vdso_syscall2 __vdso_clock_getres, __NR_clock_getres +vdso_syscall2 __vdso_gettimeofday, __NR_gettimeofday +vdso_syscall1 __vdso_time, __NR_time +vdso_syscall3 __vdso_getcpu, __NR_gettimeofday + +/* + * Signal return handlers. + */ + + .cfi_startproc simple + .cfi_signal_frame + +/* + * For convenience, put the cfa just above eip in sigcontext, and count + * offsets backward from there. Re-compute the cfa in the two contexts + * we have for signal unwinding. This is far simpler than the + * DW_CFA_expression form that the kernel uses, and is equally correct. + */ + + .cfi_def_cfa %esp, SIGFRAME_SIGCONTEXT_eip + 4 + + .cfi_offset %eip, -4 + /* err, -8 */ + /* trapno, -12 */ + .cfi_offset %eax, -16 + .cfi_offset %ecx, -20 + .cfi_offset %edx, -24 + .cfi_offset %ebx, -28 + .cfi_offset %esp, -32 + .cfi_offset %ebp, -36 + .cfi_offset %esi, -40 + .cfi_offset %edi, -44 + +/* + * While this frame is marked as a signal frame, that only applies to how + * the return address is handled for the outer frame. The return address + * that arrived here, from the inner frame, is not marked as a signal frame + * and so the unwinder still tries to subtract 1 to examine the presumed + * call insn. Thus we must extend the unwind info to a nop before the start. + */ + nop + +__kernel_sigreturn: + popl %eax /* pop sig */ + .cfi_adjust_cfa_offset -4 + movl $__NR_sigreturn, %eax + int $0x80 +endf __kernel_sigreturn + + .cfi_def_cfa_offset RT_SIGFRAME_SIGCONTEXT_eip + 4 + nop + +__kernel_rt_sigreturn: + movl $__NR_rt_sigreturn, %eax + int $0x80 +endf __kernel_rt_sigreturn + + .cfi_endproc + +/* + * TODO: Add elf notes. E.g. + * + * #include + * ELFNOTE_START(Linux, 0, "a") + * .long LINUX_VERSION_CODE + * ELFNOTE_END + * + * but what version number would we set for QEMU? + */ diff --git a/linux-user/i386/vdso.ld b/linux-user/i386/vdso.ld new file mode 100644 index 0000000000..326b7a8f98 --- /dev/null +++ b/linux-user/i386/vdso.ld @@ -0,0 +1,76 @@ +/* + * Linker script for linux i386 replacement vdso. + * + * Copyright 2023 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +ENTRY(__kernel_vsyscall) + +VERSION { + LINUX_2.6 { + global: + __vdso_clock_gettime; + __vdso_gettimeofday; + __vdso_time; + __vdso_clock_getres; + __vdso_clock_gettime64; + __vdso_getcpu; + }; + + LINUX_2.5 { + global: + __kernel_vsyscall; + __kernel_sigreturn; + __kernel_rt_sigreturn; + local: *; + }; +} + +PHDRS { + phdr PT_PHDR FLAGS(4) PHDRS; + load PT_LOAD FLAGS(7) FILEHDR PHDRS; /* FLAGS=RWX */ + dynamic PT_DYNAMIC FLAGS(4); + eh_frame_hdr PT_GNU_EH_FRAME; + note PT_NOTE FLAGS(4); +} + +SECTIONS { + . = SIZEOF_HEADERS; + + /* + * The following, including the FILEHDRS and PHDRS, are modified + * when we relocate the binary. We want them to be initially + * writable for the relocation; we'll force them read-only after. + */ + .note : { *(.note*) } :load :note + .dynamic : { *(.dynamic) } :load :dynamic + .dynsym : { *(.dynsym) } :load + .data : { + /* + * There ought not be any real read-write data. + * But since we manipulated the segment layout, + * we have to put these sections somewhere. + */ + *(.data*) + *(.sdata*) + *(.got.plt) *(.got) + *(.gnu.linkonce.d.*) + *(.bss*) + *(.dynbss*) + *(.gnu.linkonce.b.*) + } + + .rodata : { *(.rodata*) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .eh_frame_hdr : { *(.eh_frame_hdr) } :load :eh_frame_hdr + .eh_frame : { *(.eh_frame) } :load + + .text : { *(.text*) } :load =0x90909090 +} diff --git a/linux-user/i386/vdso.so b/linux-user/i386/vdso.so new file mode 100755 index 0000000000000000000000000000000000000000..bdece5dfcf8da036d013262d4e37208350283cfa GIT binary patch literal 2672 zcmbtWU1%It6u#5lG_z^4ZB?X_C>9o5sxCF8QBlyONt?nZ)?|y73e)ZGba%;SR(597 zjTXE8QIiHtY6YPbDhO>6s(mPS(FX(Z2Q`g?1xp_^KInr_=|f8&)b;yj=5}w=eGok1 z%XiMX=brmB_ny0ldSlx(O%qZAA|PnR8-(ZpuhYGj4)L&P5F5p65f&@qVw=PV(21ar zS~fT!zaSUUNMnrKqu7p`(Ouv?E|~S#J4vR z?eBj&+ji~rg~eZ!zf~VSJr_pXM}D8DFrl0ORPzVHn5LKmbB!e|qzOC^tO77~@mIB) zzl6kpgPDI3e2U8d6uIY2bq_`x0-wZWxDMLSfe$J6M%crOeHivUY<@1rr@`l7bI!EC z1WQ>fZdBic@iW@_P1t@f_$4LZXEGSfcpH2f?e~Kj&v`Zj+$T!NBlkb|jQhnHeiL+a z&lJblO18$ z-OkRAyW9oSmOj5{et#-4CVgruv?pIJqQxKI&ZY~dQ!H3DSHUrA-dxrymL5+h95ZU? zqfdy~j)7gfjrM5g9c>HN9>C7?j(;zF-_-GQuCsOgock95 z$CvrnLFHO513c5BE36+~5f2BEAVM30`47nj^o(oe;NgZND~~oF)87oe)%5l%caD!C zZUR`#4&CABw}%(BE9KdV>yxiUf|Id`UI}64SIQGVslJnSd`)$H14yd$t+Ysc2OQ(J+srhD+k<&>BHxRjq5)uze70i3T0nE36-;4MD zX|P(C`eCbeVGnF6>~F$Q>&)x0)jBo>TdiYfVXJlMbJ%KKV=Y$enlO4^9_Z@du@yHB zwchCMGqw$O_4gWmJ%e{HQ)lfOF0TyrZnl4$<)z$G>%HU>FE1lXy;xFj>Xa70cGRgB zZe?YqcLID@7{~XU=z-4s6vh$lz%9JdjN|)G?1a8N?pfc* zIKKY`&zcmDP2m_-KOf(Hg0)2EV}IgBfWHx_^Y;P~hc5SkbEE79n459x?;V(~$T$m@ zjElobxDGGOT`N`AK*n(#-do~0bSZKjGMDr(AZ`H$D-74d^xS8@>$IiJAn|o+fMq_` OKQ- Date: Thu, 17 Jun 2021 14:39:14 -0700 Subject: [PATCH 216/974] linux-user/x86_64: Add vdso MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/elfload.c | 4 +- linux-user/x86_64/Makefile.vdso | 11 +++++ linux-user/x86_64/meson.build | 4 ++ linux-user/x86_64/vdso.S | 78 ++++++++++++++++++++++++++++++++ linux-user/x86_64/vdso.ld | 73 ++++++++++++++++++++++++++++++ linux-user/x86_64/vdso.so | Bin 0 -> 2968 bytes 6 files changed, 168 insertions(+), 2 deletions(-) create mode 100644 linux-user/x86_64/Makefile.vdso create mode 100644 linux-user/x86_64/vdso.S create mode 100644 linux-user/x86_64/vdso.ld create mode 100755 linux-user/x86_64/vdso.so diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 46f73a4166..62a33481e1 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -318,10 +318,10 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUX86State *en } \ } while (0) -#define VDSO_HEADER "vdso.c.inc" - #endif /* TARGET_X86_64 */ +#define VDSO_HEADER "vdso.c.inc" + #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 diff --git a/linux-user/x86_64/Makefile.vdso b/linux-user/x86_64/Makefile.vdso new file mode 100644 index 0000000000..26552b66db --- /dev/null +++ b/linux-user/x86_64/Makefile.vdso @@ -0,0 +1,11 @@ +include $(BUILD_DIR)/tests/tcg/x86_64-linux-user/config-target.mak + +SUBDIR = $(SRC_PATH)/linux-user/x86_64 +VPATH += $(SUBDIR) + +all: $(SUBDIR)/vdso.so + +$(SUBDIR)/vdso.so: vdso.S vdso.ld + $(CC) -o $@ -nostdlib -shared -Wl,-h,linux-vdso.so.1 \ + -Wl,--build-id=sha1 -Wl,--hash-style=both \ + -Wl,-T,$(SUBDIR)/vdso.ld $< diff --git a/linux-user/x86_64/meson.build b/linux-user/x86_64/meson.build index 203af9a60c..8c60da7a60 100644 --- a/linux-user/x86_64/meson.build +++ b/linux-user/x86_64/meson.build @@ -3,3 +3,7 @@ syscall_nr_generators += { arguments: [ meson.current_source_dir() / 'syscallhdr.sh', '@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@' ], output: '@BASENAME@_nr.h') } + +vdso_inc = gen_vdso.process('vdso.so') + +linux_user_ss.add(when: 'TARGET_X86_64', if_true: vdso_inc) diff --git a/linux-user/x86_64/vdso.S b/linux-user/x86_64/vdso.S new file mode 100644 index 0000000000..47d16c00ab --- /dev/null +++ b/linux-user/x86_64/vdso.S @@ -0,0 +1,78 @@ +/* + * x86-64 linux replacement vdso. + * + * Copyright 2023 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +.macro endf name + .globl \name + .type \name, @function + .size \name, . - \name +.endm + +.macro weakalias name +\name = __vdso_\name + .weak \name +.endm + +.macro vdso_syscall name, nr +__vdso_\name: + mov $\nr, %eax + syscall + ret +endf __vdso_\name +weakalias \name +.endm + + .cfi_startproc + +vdso_syscall clock_gettime, __NR_clock_gettime +vdso_syscall clock_getres, __NR_clock_getres +vdso_syscall gettimeofday, __NR_gettimeofday +vdso_syscall time, __NR_time + +__vdso_getcpu: + /* + * There is no syscall number for this allocated on x64. + * We can handle this several ways: + * + * (1) Invent a syscall number for use within qemu. + * It should be easy enough to pick a number that + * is well out of the way of the kernel numbers. + * + * (2) Force the emulated cpu to support the rdtscp insn, + * and initialize the TSC_AUX value the appropriate value. + * + * (3) Pretend that we're always running on cpu 0. + * + * This last is the one that's implemented here, with the + * tiny bit of extra code to support rdtscp in place. + */ + xor %ecx, %ecx /* rdtscp w/ tsc_aux = 0 */ + + /* if (cpu != NULL) *cpu = (ecx & 0xfff); */ + test %rdi, %rdi + jz 1f + mov %ecx, %eax + and $0xfff, %eax + mov %eax, (%rdi) + + /* if (node != NULL) *node = (ecx >> 12); */ +1: test %rsi, %rsi + jz 2f + shr $12, %ecx + mov %ecx, (%rsi) + +2: xor %eax, %eax + ret +endf __vdso_getcpu + +weakalias getcpu + + .cfi_endproc + +/* TODO: Add elf note for LINUX_VERSION_CODE */ diff --git a/linux-user/x86_64/vdso.ld b/linux-user/x86_64/vdso.ld new file mode 100644 index 0000000000..ca6001cc3c --- /dev/null +++ b/linux-user/x86_64/vdso.ld @@ -0,0 +1,73 @@ +/* + * Linker script for linux x86-64 replacement vdso. + * + * Copyright 2023 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +VERSION { + LINUX_2.6 { + global: + clock_gettime; + __vdso_clock_gettime; + gettimeofday; + __vdso_gettimeofday; + getcpu; + __vdso_getcpu; + time; + __vdso_time; + clock_getres; + __vdso_clock_getres; + + local: *; + }; +} + + +PHDRS { + phdr PT_PHDR FLAGS(4) PHDRS; + load PT_LOAD FLAGS(7) FILEHDR PHDRS; /* FLAGS=RWX */ + dynamic PT_DYNAMIC FLAGS(4); + eh_frame_hdr PT_GNU_EH_FRAME; + note PT_NOTE FLAGS(4); +} + +SECTIONS { + . = SIZEOF_HEADERS; + + /* + * The following, including the FILEHDRS and PHDRS, are modified + * when we relocate the binary. We want them to be initially + * writable for the relocation; we'll force them read-only after. + */ + .note : { *(.note*) } :load :note + .dynamic : { *(.dynamic) } :load :dynamic + .dynsym : { *(.dynsym) } :load + .data : { + /* + * There ought not be any real read-write data. + * But since we manipulated the segment layout, + * we have to put these sections somewhere. + */ + *(.data*) + *(.sdata*) + *(.got.plt) *(.got) + *(.gnu.linkonce.d.*) + *(.bss*) + *(.dynbss*) + *(.gnu.linkonce.b.*) + } + + .rodata : { *(.rodata*) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .eh_frame_hdr : { *(.eh_frame_hdr) } :load :eh_frame_hdr + .eh_frame : { *(.eh_frame) } :load + + .text : { *(.text*) } :load =0x90909090 +} diff --git a/linux-user/x86_64/vdso.so b/linux-user/x86_64/vdso.so new file mode 100755 index 0000000000000000000000000000000000000000..c873d6ea580b393825506d2ffbddcf9827d89e14 GIT binary patch literal 2968 zcmcgtO=w(I6uvKUOl$t7l}J=nY6?Lgol-PVEvcEL6HP)om@-1c>vZO|6VjQnGcRQ# zQlk(xE{v3}x(Eh$E+iYFJ0pdXxDr=E=&lLvMs(4vwBz~aoiCl-j=CuHBzL}hzkANP z=iPhmJs%H_3^h28p){If=E2CAex2B8q6WA=6OrF`(`KF&`Uz?MI&EW@IwS5;Jq_-2rSodh0=h5M8yR&|8~q9xUzjR(|@2weye zz8a;S7@IIXlUIKD#o1b1+rN5odUE>L=z;rh$dv40C`m=ye*;62d-p2^;2j+^>^@b* z{zK-lF(OZeV2X;(id~9-RYHGjYX7&6)!h7{=_DaLK@yv17h==zIwme4kF^2|U3rG4QL%vz2K73uCxOLI|+`aN! z$F-%z;-Tzk@6K$E*Jv}RD{ao_%it)TlnNq<^Unr!fz4Lw$ zOy&HC$f92&rz(YWnba(~S{s>9e=uV%Oyy_Fufeq||4}nCJT{T^4!f^Qd<}K~WJ1Kz zzvUl)Z^gZPP8#nNzOg~(w`aa@FjYY9u^!IW^5APGzK1@7^HK>gD4>;p^R}arI;0o1?04=jpKWw!W)=?0Yt= zynUbER^IkMss3a7y}So_=McZ`$9S>#$BvH<4@j=b`|AzHy`jYM(LpaBOVlKlW^ujb z)P%J2ye6gV=6*G&ja!<{1*x-wK{3QEd-+1(8#gnXPvxf4)sE;I_k4cF&8AA(kO_(* zbIC83rV9C8121D-KkJ<idjF0yGj>9sy zEwzyLtiMFu4~T6avc|lrbF^o@CbB+LUq6S#>RVK)S)cWo$nWI(T!)Byg4)h8>pihg zSHk{7hu^QXIU^m!MEjk0M(-crN9uFmIL=%4U1~>sm5Pe?`en6ee(>HC`S#L{y??u6 vr;^|o8ppUAvD!gdKbRPND1BpVH#|fA;?X5$6$T literal 0 HcmV?d00001 From ee95fae075c68cf1ae0fc1ffb00acca685bfb2c8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 15 Jun 2021 10:21:37 -0700 Subject: [PATCH 217/974] linux-user/aarch64: Add vdso MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/aarch64/Makefile.vdso | 15 +++++++ linux-user/aarch64/meson.build | 11 +++++ linux-user/aarch64/vdso-be.so | Bin 0 -> 3216 bytes linux-user/aarch64/vdso-le.so | Bin 0 -> 3216 bytes linux-user/aarch64/vdso.S | 71 ++++++++++++++++++++++++++++++ linux-user/aarch64/vdso.ld | 72 +++++++++++++++++++++++++++++++ linux-user/elfload.c | 6 +++ linux-user/meson.build | 1 + 8 files changed, 176 insertions(+) create mode 100644 linux-user/aarch64/Makefile.vdso create mode 100644 linux-user/aarch64/meson.build create mode 100755 linux-user/aarch64/vdso-be.so create mode 100755 linux-user/aarch64/vdso-le.so create mode 100644 linux-user/aarch64/vdso.S create mode 100644 linux-user/aarch64/vdso.ld diff --git a/linux-user/aarch64/Makefile.vdso b/linux-user/aarch64/Makefile.vdso new file mode 100644 index 0000000000..599958116b --- /dev/null +++ b/linux-user/aarch64/Makefile.vdso @@ -0,0 +1,15 @@ +include $(BUILD_DIR)/tests/tcg/aarch64-linux-user/config-target.mak + +SUBDIR = $(SRC_PATH)/linux-user/aarch64 +VPATH += $(SUBDIR) + +all: $(SUBDIR)/vdso-be.so $(SUBDIR)/vdso-le.so + +LDFLAGS = -nostdlib -shared -Wl,-h,linux-vdso.so.1 -Wl,--build-id=sha1 \ + -Wl,--hash-style=both -Wl,-T,$(SUBDIR)/vdso.ld + +$(SUBDIR)/vdso-be.so: vdso.S vdso.ld + $(CC) -o $@ $(LDFLAGS) -mbig-endian $< + +$(SUBDIR)/vdso-le.so: vdso.S vdso.ld + $(CC) -o $@ $(LDFLAGS) -mlittle-endian $< diff --git a/linux-user/aarch64/meson.build b/linux-user/aarch64/meson.build new file mode 100644 index 0000000000..248c578d15 --- /dev/null +++ b/linux-user/aarch64/meson.build @@ -0,0 +1,11 @@ +# TARGET_BIG_ENDIAN is defined to 'n' for little-endian; which means it +# is always true as far as source_set.apply() is concerned. Always build +# both header files and include the right one via #if. + +vdso_be_inc = gen_vdso.process('vdso-be.so', + extra_args: ['-r', '__kernel_rt_sigreturn']) + +vdso_le_inc = gen_vdso.process('vdso-le.so', + extra_args: ['-r', '__kernel_rt_sigreturn']) + +linux_user_ss.add(when: 'TARGET_AARCH64', if_true: [vdso_be_inc, vdso_le_inc]) diff --git a/linux-user/aarch64/vdso-be.so b/linux-user/aarch64/vdso-be.so new file mode 100755 index 0000000000000000000000000000000000000000..6084f3d1a701316004894fcdd739c4e1e0463b68 GIT binary patch literal 3216 zcmc&$O>A355T57v+64N8Q&ABq6>X&mRj7RmMS`Gmu!EhX$W24qs-c&yv0q~6$41XD z#X$mc;KBh>)D!9f7jbE&o3KO1&W#3omsDqjgUAn$-Zyr zo0*+`yF2UmUcNA$N_k+#hcB@25ZaP4_Th`HO~N+FKpLJveLJ?m*pV~RgWjm21z{L< z$}wd4LC@ee4`!_DFeDmp#mw&i^z)Y(0;(W3!dXI>qX-{*VL(Jr;fPar2{J(&x^9ec+i^?x(BSN1)Vd z6xX6PwHzf$bv>#tm5ZAlwzO6+t%|J=9Y>808z-t!U5=w- z6pqiGSyV^E7sBUX!r9;i8&^=H^Slq6Ij8hSDe#8W?GM<704wzH#{6Rl z3kP~-l0hxTsW!QgLi`s#bF@9x%T9lbJQ zY!o|4aN}q$C{o`>LtdYI781M2@Zr_SAbWZB{G@i?^Z?>M}yPa6444u6aN z(@-i+W?!6HJsZ|xybR+_P`TIVvL}weg5`%7zsk?5>4ofxyqe7|fSM_soXQr|$?54+ z`P1rjcB+t9-4$r--d6bFQntST=8k}{vALcU-#|=agB9u3>PZA)d9zkruaW$*LZfyK0az`B= zOP9o-=tekTbo-S2gfC;0-yF_wz7ueqd5$=Y$YX0p;sews@%Q~V_LKN6qx+07iF&_y za+`8&63=yF?=kuAAb%2B2kO6y`P9?hbjKdryS z$@`Pr5;mESVJiH5fKy;kWM_r}wHqu6`)kkRea&Hpa463^!_;Pn&K^S8m+ zp?wpfSa~nu80C99^(GSZO}Up1l7AF+`VDc{mR$&j&a-ljQ9$NP`UtiG@xy#y!}}7* HXyg9^8}}&g literal 0 HcmV?d00001 diff --git a/linux-user/aarch64/vdso-le.so b/linux-user/aarch64/vdso-le.so new file mode 100755 index 0000000000000000000000000000000000000000..947d534ec1899740edbd6921da6bc6e70e2ecd09 GIT binary patch literal 3216 zcmcguL2MgU5FKYzk|v=|N~=Z+qD6{Og~_6L z9VDO!E}RJB#LXZsa7BWO3q5c^;)VcKz2t<11S;tPP82h|}KgVe=J;LxHx3r3FKB<#|V;X^n)Fz2- z%cM-;q^lZWOhj#+*XG^Ds~rB${uGqOuBG$Zd_T*Cz*Q~uCH;#!pUtzO6%jc6iOo2- zUEEGBEQ!`_!5rx^Nnd=)Smd95v^A8^_@B?;{q2QA>mys0rC%Ro5$%x&)1`=ocG5ko z6aD^Iry=8;Iu?IIw{$G$Bh;3SV;YGV-B3QNd{X(JooK$aERgAej@6vDbv&c;NbCQ9 zy6kcvjnN%lhwQabmYd)|D~|JcjqwS(-w~ZS$+3$5Eo+{GihpC_aoczwaVXo4J(oVt zLv%HS2r(>}=m;)N?7pOQiiV@`7Cn)F;GMkl%e$@jzP@~EJllTi@Po`JTV%{t&#!wc zrs4%bZPjZmmx~)cw6xMFU4<+G_Pu5g^#jwaReUd4^Xs%ytFN`5T`xBqF8`mS!u-P0 zMRU|W=01Od7sIQ^Pev*IefZVAM%JKvfiC4uzP+=NPvcQDQRF&JRZp-29I1zrXF6AH(sU{_$LFT%Cwyk#+LZ z;ng6&;~EaXMOzkXk;A9cMqe_Vo>WIFiraPM4aMzx@xJ0a>P4w^{aUtsrS3Lpx=hn8 zGTB!bGNW-|qICZ9i-J8#ZsW(zsfpMl-H?`HVJ zQ@N}AZ)ya&&5hNdc!hc3N4$z&y%BiimN)9f)mka+urBjrP$aifUvsO)W;MbCKSI_$ zzgcV4`v#^=u2(h7esR?^)iP^hL~h`<0^Y;_N)i`xuRvNNF2N-Z;)L%5uqU}Abr|7; zxC7_l3JG@?*rRSe%~JWM!awQ*_-14tu2H6fJ-)w38511-QAfZJvn)=q2Y-=;^38#N z)EjWrSput!}27rBsaAE89EDuX@lA>g=oKwq~LLwcEeotS^rIq;}{ z^!&pQ_yx9?I}66Bhu~O0Vg&Dx1uSz%)&%>9-4mgYeZyE{IBt*n3VsBN%5DE|sy*%y zs59VLKc=z$$9+Rb__iuu(-)I_ + +/* ??? These are in include/elf.h, which is not ready for inclusion in asm. */ +#define NT_GNU_PROPERTY_TYPE_0 5 +#define GNU_PROPERTY_AARCH64_FEATURE_1_AND 0xc0000000 +#define GNU_PROPERTY_AARCH64_FEATURE_1_BTI (1U << 0) +#define GNU_PROPERTY_AARCH64_FEATURE_1_PAC (1U << 1) + +#define GNU_PROPERTY_AARCH64_FEATURE_1_DEFAULT \ + (GNU_PROPERTY_AARCH64_FEATURE_1_BTI | GNU_PROPERTY_AARCH64_FEATURE_1_PAC) + + .section .note.gnu.property + .align 3 + .long 2f - 1f + .long 6f - 3f + .long NT_GNU_PROPERTY_TYPE_0 +1: .string "GNU" +2: .align 3 +3: .long GNU_PROPERTY_AARCH64_FEATURE_1_AND + .long 5f - 4f +4: .long GNU_PROPERTY_AARCH64_FEATURE_1_DEFAULT +5: .align 3 +6: + + .text + +.macro endf name + .globl \name + .type \name, @function + .size \name, . - \name +.endm + +.macro vdso_syscall name, nr +\name: + bti c + mov x8, #\nr + svc #0 + ret +endf \name +.endm + + .cfi_startproc + +vdso_syscall __kernel_gettimeofday, __NR_gettimeofday +vdso_syscall __kernel_clock_gettime, __NR_clock_gettime +vdso_syscall __kernel_clock_getres, __NR_clock_getres + + .cfi_endproc + + +/* + * TODO: The kernel makes a big deal of turning off the .cfi directives, + * because they cause libgcc to crash, but that's because they're wrong. + * + * For now, elide the unwind info for __kernel_rt_sigreturn and rely on + * the libgcc fallback routine as we have always done. This requires + * that the code sequence used be exact. + */ +__kernel_rt_sigreturn: + /* No BTI C insn here -- we arrive via RET. */ + mov x8, #__NR_rt_sigreturn + svc #0 +endf __kernel_rt_sigreturn diff --git a/linux-user/aarch64/vdso.ld b/linux-user/aarch64/vdso.ld new file mode 100644 index 0000000000..4c12f33352 --- /dev/null +++ b/linux-user/aarch64/vdso.ld @@ -0,0 +1,72 @@ +/* + * Linker script for linux aarch64 replacement vdso. + * + * Copyright 2021 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +VERSION { + LINUX_2.6.39 { + global: + __kernel_rt_sigreturn; + __kernel_gettimeofday; + __kernel_clock_gettime; + __kernel_clock_getres; + + local: *; + }; +} + + +PHDRS { + phdr PT_PHDR FLAGS(4) PHDRS; + load PT_LOAD FLAGS(7) FILEHDR PHDRS; + dynamic PT_DYNAMIC FLAGS(4); + eh_frame_hdr PT_GNU_EH_FRAME; + note PT_NOTE FLAGS(4); +} + +SECTIONS { + /* + * We can't prelink to any address without knowing something about + * the virtual memory space of the host, since that leaks over into + * the available memory space of the guest. + */ + . = SIZEOF_HEADERS; + + /* + * The following, including the FILEHDRS and PHDRS, are modified + * when we relocate the binary. We want them to be initially + * writable for the relocation; we'll force them read-only after. + */ + .note : { *(.note*) } :load :note + .dynamic : { *(.dynamic) } :load :dynamic + .dynsym : { *(.dynsym) } :load + /* + * There ought not be any real read-write data. + * But since we manipulated the segment layout, + * we have to put these sections somewhere. + */ + .data : { + *(.data*) + *(.sdata*) + *(.got.plt) *(.got) + *(.gnu.linkonce.d.*) + *(.bss*) + *(.dynbss*) + *(.gnu.linkonce.b.*) + } + + .rodata : { *(.rodata*) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .eh_frame_hdr : { *(.eh_frame_hdr) } :load :eh_frame_hdr + .eh_frame : { *(.eh_frame) } :load + + .text : { *(.text*) } :load =0xd503201f +} diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 62a33481e1..0a3a57018b 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -944,6 +944,12 @@ const char *elf_hwcap2_str(uint32_t bit) #undef GET_FEATURE_ID +#if TARGET_BIG_ENDIAN +# define VDSO_HEADER "vdso-be.c.inc" +#else +# define VDSO_HEADER "vdso-le.c.inc" +#endif + #endif /* not TARGET_AARCH64 */ #endif /* TARGET_ARM */ diff --git a/linux-user/meson.build b/linux-user/meson.build index e4cb70ed2d..dd24389052 100644 --- a/linux-user/meson.build +++ b/linux-user/meson.build @@ -35,6 +35,7 @@ gen_vdso_exe = executable('gen-vdso', 'gen-vdso.c', gen_vdso = generator(gen_vdso_exe, output: '@BASENAME@.c.inc', arguments: ['-o', '@OUTPUT@', '@EXTRA_ARGS@', '@INPUT@']) +subdir('aarch64') subdir('alpha') subdir('arm') subdir('hppa') From a9f495b93f1497ced94dbe51ebb733537e0fa1ab Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 6 Jul 2021 09:55:12 -0700 Subject: [PATCH 218/974] linux-user/arm: Add vdso Signed-off-by: Richard Henderson --- linux-user/arm/Makefile.vdso | 17 ++++ linux-user/arm/meson.build | 12 +++ linux-user/arm/signal.c | 55 ++++++---- linux-user/arm/vdso-asmoffset.h | 3 + linux-user/arm/vdso-be.so | Bin 0 -> 2648 bytes linux-user/arm/vdso-le.so | Bin 0 -> 2648 bytes linux-user/arm/vdso.S | 174 ++++++++++++++++++++++++++++++++ linux-user/arm/vdso.ld | 67 ++++++++++++ linux-user/elfload.c | 3 +- 9 files changed, 310 insertions(+), 21 deletions(-) create mode 100644 linux-user/arm/Makefile.vdso create mode 100644 linux-user/arm/vdso-asmoffset.h create mode 100755 linux-user/arm/vdso-be.so create mode 100755 linux-user/arm/vdso-le.so create mode 100644 linux-user/arm/vdso.S create mode 100644 linux-user/arm/vdso.ld diff --git a/linux-user/arm/Makefile.vdso b/linux-user/arm/Makefile.vdso new file mode 100644 index 0000000000..2d098a5748 --- /dev/null +++ b/linux-user/arm/Makefile.vdso @@ -0,0 +1,17 @@ +include $(BUILD_DIR)/tests/tcg/arm-linux-user/config-target.mak + +SUBDIR = $(SRC_PATH)/linux-user/arm +VPATH += $(SUBDIR) + +all: $(SUBDIR)/vdso-be.so $(SUBDIR)/vdso-le.so + +# Adding -use-blx disables unneeded interworking without actually using blx. +LDFLAGS = -nostdlib -shared -Wl,-use-blx \ + -Wl,-h,linux-vdso.so.1 -Wl,--build-id=sha1 \ + -Wl,--hash-style=both -Wl,-T,$(SUBDIR)/vdso.ld + +$(SUBDIR)/vdso-be.so: vdso.S vdso.ld vdso-asmoffset.h + $(CC) -o $@ $(LDFLAGS) -mbig-endian $< + +$(SUBDIR)/vdso-le.so: vdso.S vdso.ld vdso-asmoffset.h + $(CC) -o $@ $(LDFLAGS) -mlittle-endian $< diff --git a/linux-user/arm/meson.build b/linux-user/arm/meson.build index 5a93c925cf..c4bb9af5b8 100644 --- a/linux-user/arm/meson.build +++ b/linux-user/arm/meson.build @@ -5,3 +5,15 @@ syscall_nr_generators += { arguments: [ meson.current_source_dir() / 'syscallhdr.sh', '@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@' ], output: '@BASENAME@_nr.h') } + +# TARGET_BIG_ENDIAN is defined to 'n' for little-endian; which means it +# is always true as far as source_set.apply() is concerned. Always build +# both header files and include the right one via #if. + +vdso_be_inc = gen_vdso.process('vdso-be.so', + extra_args: ['-s', 'sigreturn_codes']) + +vdso_le_inc = gen_vdso.process('vdso-le.so', + extra_args: ['-s', 'sigreturn_codes']) + +linux_user_ss.add(when: 'TARGET_ARM', if_true: [vdso_be_inc, vdso_le_inc]) diff --git a/linux-user/arm/signal.c b/linux-user/arm/signal.c index cf99fd7b8a..e19b514f17 100644 --- a/linux-user/arm/signal.c +++ b/linux-user/arm/signal.c @@ -21,6 +21,7 @@ #include "user-internals.h" #include "signal-common.h" #include "linux-user/trace.h" +#include "vdso-asmoffset.h" struct target_sigcontext { abi_ulong trap_no; @@ -102,6 +103,11 @@ struct rt_sigframe struct sigframe sig; }; +QEMU_BUILD_BUG_ON(offsetof(struct sigframe, retcode[3]) + != SIGFRAME_RC3_OFFSET); +QEMU_BUILD_BUG_ON(offsetof(struct rt_sigframe, sig.retcode[3]) + != RT_SIGFRAME_RC3_OFFSET); + static abi_ptr sigreturn_fdpic_tramp; /* @@ -160,6 +166,9 @@ get_sigframe(struct target_sigaction *ka, CPUARMState *regs, int framesize) return (sp - framesize) & ~7; } +static void write_arm_sigreturn(uint32_t *rc, int syscall); +static void write_arm_fdpic_sigreturn(uint32_t *rc, int ofs); + static int setup_return(CPUARMState *env, struct target_sigaction *ka, int usig, struct sigframe *frame, abi_ulong sp_addr) @@ -167,9 +176,9 @@ setup_return(CPUARMState *env, struct target_sigaction *ka, int usig, abi_ulong handler = 0; abi_ulong handler_fdpic_GOT = 0; abi_ulong retcode; - int thumb, retcode_idx; - int is_fdpic = info_is_fdpic(((TaskState *)thread_cpu->opaque)->info); - bool copy_retcode; + bool is_fdpic = info_is_fdpic(((TaskState *)thread_cpu->opaque)->info); + bool is_rt = ka->sa_flags & TARGET_SA_SIGINFO; + bool thumb; if (is_fdpic) { /* In FDPIC mode, ka->_sa_handler points to a function @@ -184,9 +193,7 @@ setup_return(CPUARMState *env, struct target_sigaction *ka, int usig, } else { handler = ka->_sa_handler; } - thumb = handler & 1; - retcode_idx = thumb + (ka->sa_flags & TARGET_SA_SIGINFO ? 2 : 0); uint32_t cpsr = cpsr_read(env); @@ -202,24 +209,32 @@ setup_return(CPUARMState *env, struct target_sigaction *ka, int usig, cpsr &= ~CPSR_E; } - if (ka->sa_flags & TARGET_SA_RESTORER) { - if (is_fdpic) { - __put_user((abi_ulong)ka->sa_restorer, &frame->retcode[3]); - retcode = (sigreturn_fdpic_tramp + - retcode_idx * RETCODE_BYTES + thumb); - copy_retcode = true; - } else { - retcode = ka->sa_restorer; - copy_retcode = false; - } + /* Our vdso default_sigreturn label is a table of entry points. */ + retcode = default_sigreturn + (is_fdpic * 2 + is_rt) * 8; + + /* + * Put the sigreturn code on the stack no matter which return + * mechanism we use in order to remain ABI compliant. + * Because this is about ABI, always use the A32 instructions, + * despite the fact that our actual vdso trampoline is T16. + */ + if (is_fdpic) { + write_arm_fdpic_sigreturn(frame->retcode, + is_rt ? RT_SIGFRAME_RC3_OFFSET + : SIGFRAME_RC3_OFFSET); } else { - retcode = default_sigreturn + retcode_idx * RETCODE_BYTES + thumb; - copy_retcode = true; + write_arm_sigreturn(frame->retcode, + is_rt ? TARGET_NR_rt_sigreturn + : TARGET_NR_sigreturn); } - /* Copy the code to the stack slot for ABI compatibility. */ - if (copy_retcode) { - memcpy(frame->retcode, g2h_untagged(retcode & ~1), RETCODE_BYTES); + if (ka->sa_flags & TARGET_SA_RESTORER) { + if (is_fdpic) { + /* Place the function descriptor in slot 3. */ + __put_user((abi_ulong)ka->sa_restorer, &frame->retcode[3]); + } else { + retcode = ka->sa_restorer; + } } env->regs[0] = usig; diff --git a/linux-user/arm/vdso-asmoffset.h b/linux-user/arm/vdso-asmoffset.h new file mode 100644 index 0000000000..252a95c46e --- /dev/null +++ b/linux-user/arm/vdso-asmoffset.h @@ -0,0 +1,3 @@ +/* offsetof(struct sigframe, retcode[3]) */ +#define SIGFRAME_RC3_OFFSET 756 +#define RT_SIGFRAME_RC3_OFFSET 884 diff --git a/linux-user/arm/vdso-be.so b/linux-user/arm/vdso-be.so new file mode 100755 index 0000000000000000000000000000000000000000..69cafbb956e283e2975bac59a10491c0cbafca57 GIT binary patch literal 2648 zcmbtVO>A355T57%q>ketR9ezQ5D`VCwMu1`A`rrf?W8D4OPYuR5-*PZ5?ds8WWR(2 z@#8{h3oY#>2P%XJf@`Y?wNft?xqwg)z@Y~Y{2jstAq3+00GRLFx0^a47sMproBd{H zci-Ec8NZkr$XY&2*2OP(V{8mbk?=~e?h{E$mjvPKkU0Dy0KRoBV@w1z%zRKvVA+F& z^~zXK{%ArSV?xa@xZfTX@xgB(Y4`}`*ZKyuK{v)kv;S6bf8WrkT>1HI=J#C}-v0DM z`Ko86^ZqyN_qyPV0iyusSZkHW-@jr%ep?Va4UKQK=Am1G3a|}`R~n5!;a`RR3)~0& zBh+P#|F5L*D%>Y>i%1lDAGibEs`+;CC!09VIPho_XRMMAY5ar%&)0|_1Kt@mh37=U zx2hZAeHdX3?~e!ucLq!h=LA6obHD6}=FU|f2Mju6;lhx^e0ON;&FanQ51J*e74D=NAY3MoVW#|{6 zanIHg5CLf03mfxAtY?SC_gu{CjJA<-pB(_tzg1GP$SL1Bo+a)3f$z4k_rvzZ7WOgN zJ^><4>mP{+D1V6mJPr-x*-qNEh%uz;lUsF(DljGBI$8)UPyl zNA8oa>(@A1S|7FO(a>)CR!Yzxm1(PEZ z^0nw!=Z+iX1{o6A_<0B9K5Cff`2=TGT(rgQ}v7(FCIR|w_~Q7kFj14 zBZ!Vi+wgjT<<0Me=LBqBI1J9e$GjiLe7}dxdFc2N*p%pg@)YJb_opg2YsvH{IP`qcBbFX4yOh) zc7J+U?7o2`J*ffvNH%*cGh&aVdImCfQ%ledXhQ?p+?1Lc&4xy`MNd-wOA@t(X*V|x z?N&WqG^29GbtF+(DCee2c{M;Rbk5C5VzNA&DCTNK&+b+|%bZiKl`7>8#4bp}DcTd& z+_YmC3y3sK60S4vN+LBpn8>+qwKP8K;%NSp;J#I!GWS^*L^ohW=e*nj1M?ecr}Bk4 zTL*NGBb~hb-UH1%!+NYERZC;wjAO1cj{Wd}=2(yQrE07>zZplc7FcIoZ_R`31?PKE zOxz*-#gf@z*MNm_mp!O@FzdlipFC!M<3U$F z$RXJIz4Y__C=&qJ=6X0gRrAv?=0AXBj6aWAK literal 0 HcmV?d00001 diff --git a/linux-user/arm/vdso-le.so b/linux-user/arm/vdso-le.so new file mode 100755 index 0000000000000000000000000000000000000000..ad05a1251875ac0c76685e1f9190a7307a8444d1 GIT binary patch literal 2648 zcmbtWU1%It6h5<)-6ZYqW>ciL#)n9wmD&!qgeWwKo1aZ8NgB5ajR>7=b|=}zWT)&- zVj@~ojcv8s`j8h9Aw_&tf++N*n7)V<5g#h}py-1lh)4@s`cPfJ@6OyfN%J6hHs74{ zoqO(`JNMpmZeH#k$QXtpT1i52o2xxn z_w4-TgA;d}AB-ETNwkdyeSX4(|NOh=rtr=vd_Bc$r=V{@FGDxb z2kZxU9w^2OS&6)uG+N>b;d^!gTz~GJq>~M2BNq(%4+6geoBjRpeYb^w9KNpro(Vlp zJ8cNwc?>UY;!DRDQ>U9=Nxqs`iodq~%(kbOjXUR6Z#Cm zvm|}VAL(xez5tehI3THH^rb(du~@3Ho_bGROvU_$rfERUMa9H|n3#%TSIXk2wY?3G z$q$QHXf3Z>je#|^mu+hq{d*pq_J@JK@^1XvJDF79_km8|i$QZvZlWYc0%o**L}HOf zPW>*q9(YW(^Zp*tWds@U ze4~nI{qp10j!S2dfTpS@BEvx)kLlnIvCH7X0G0> z?Euj=zyNOnKaKabLPya55w-{O +#include "vdso-asmoffset.h" + +/* + * All supported cpus have T16 instructions: at least arm4t. + * + * We support user-user with m-profile cpus as an extension, because it + * is useful for testing gcc, which requires we avoid A32 instructions. + */ + .thumb + .arch armv4t + .eabi_attribute Tag_FP_arch, 0 + .eabi_attribute Tag_ARM_ISA_use, 0 + + .text + +.macro raw_syscall n + .ifne \n < 0x100 + mov r7, #\n + .elseif \n < 0x1ff + mov r7, #0xff + add r7, #(\n - 0xff) + .else + .err + .endif + swi #0 +.endm + +.macro fdpic_thunk ofs + ldr r3, [sp, #\ofs] + ldmia r2, {r2, r3} + mov r9, r3 + bx r2 +.endm + +.macro endf name + .globl \name + .type \name, %function + .size \name, . - \name +.endm + +/* + * We must save/restore r7 for the EABI syscall number. + * While we're doing that, we might as well save LR to get a free return, + * and a branch that is interworking back to ARMv5. + */ + +.macro SYSCALL name, nr +\name: + .cfi_startproc + push {r7, lr} + .cfi_adjust_cfa_offset 8 + .cfi_offset r7, -8 + .cfi_offset lr, -4 + raw_syscall \nr + pop {r7, pc} + .cfi_endproc +endf \name +.endm + +SYSCALL __vdso_clock_gettime, __NR_clock_gettime +SYSCALL __vdso_clock_gettime64, __NR_clock_gettime64 +SYSCALL __vdso_clock_getres, __NR_clock_getres +SYSCALL __vdso_gettimeofday, __NR_gettimeofday + + +/* + * We, like the real kernel, use a table of sigreturn trampolines. + * Unlike the real kernel, we do not attempt to pack this into as + * few bytes as possible -- simply use 8 bytes per slot. + * + * Within each slot, use the exact same code sequence as the kernel, + * lest we trip up someone doing code inspection. + */ + +.macro slot n + .balign 8 + .org sigreturn_codes + 8 * \n +.endm + +.macro cfi_fdpic_r9 ofs + /* + * fd = *(r13 + ofs) + * r9 = *(fd + 4) + * + * DW_CFA_expression r9, length (7), + * DW_OP_breg13, ofs, DW_OP_deref, + * DW_OP_plus_uconst, 4, DW_OP_deref + */ + .cfi_escape 0x10, 9, 7, 0x7d, (\ofs & 0x7f) + 0x80, (\ofs >> 7), 0x06, 0x23, 4, 0x06 +.endm + +.macro cfi_fdpic_pc ofs + /* + * fd = *(r13 + ofs) + * pc = *fd + * + * DW_CFA_expression lr (14), length (5), + * DW_OP_breg13, ofs, DW_OP_deref, DW_OP_deref + */ + .cfi_escape 0x10, 14, 5, 0x7d, (\ofs & 0x7f) + 0x80, (\ofs >> 7), 0x06, 0x06 +.endm + +/* + * Start the unwind info at least one instruction before the signal + * trampoline, because the unwinder will assume we are returning + * after a call site. + */ + .cfi_startproc simple + .cfi_signal_frame + .cfi_return_column 15 + + .cfi_def_cfa sp, 32 + 64 + .cfi_offset r0, -16 * 4 + .cfi_offset r1, -15 * 4 + .cfi_offset r2, -14 * 4 + .cfi_offset r3, -13 * 4 + .cfi_offset r4, -12 * 4 + .cfi_offset r5, -11 * 4 + .cfi_offset r6, -10 * 4 + .cfi_offset r7, -9 * 4 + .cfi_offset r8, -8 * 4 + .cfi_offset r9, -7 * 4 + .cfi_offset r10, -6 * 4 + .cfi_offset r11, -5 * 4 + .cfi_offset r12, -4 * 4 + .cfi_offset r13, -3 * 4 + .cfi_offset r14, -2 * 4 + .cfi_offset r15, -1 * 4 + + nop + + .balign 16 +sigreturn_codes: + /* [EO]ABI sigreturn */ + slot 0 + raw_syscall __NR_sigreturn + + .cfi_def_cfa_offset 160 + 64 + + /* [EO]ABI rt_sigreturn */ + slot 1 + raw_syscall __NR_rt_sigreturn + + .cfi_endproc + + /* FDPIC sigreturn */ + .cfi_startproc + cfi_fdpic_pc SIGFRAME_RC3_OFFSET + cfi_fdpic_r9 SIGFRAME_RC3_OFFSET + + slot 2 + fdpic_thunk SIGFRAME_RC3_OFFSET + .cfi_endproc + + /* FDPIC rt_sigreturn */ + .cfi_startproc + cfi_fdpic_pc RT_SIGFRAME_RC3_OFFSET + cfi_fdpic_r9 RT_SIGFRAME_RC3_OFFSET + + slot 3 + fdpic_thunk RT_SIGFRAME_RC3_OFFSET + .cfi_endproc + + .balign 16 +endf sigreturn_codes diff --git a/linux-user/arm/vdso.ld b/linux-user/arm/vdso.ld new file mode 100644 index 0000000000..3b00adf27a --- /dev/null +++ b/linux-user/arm/vdso.ld @@ -0,0 +1,67 @@ +/* + * Linker script for linux arm replacement vdso. + * + * Copyright 2023 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +VERSION { + LINUX_2.6 { + global: + __vdso_clock_gettime; + __vdso_gettimeofday; + __vdso_clock_getres; + __vdso_clock_gettime64; + + local: *; + }; +} + + +PHDRS { + phdr PT_PHDR FLAGS(4) PHDRS; + load PT_LOAD FLAGS(7) FILEHDR PHDRS; /* FLAGS=RWX */ + dynamic PT_DYNAMIC FLAGS(4); + eh_frame_hdr PT_GNU_EH_FRAME; + note PT_NOTE FLAGS(4); +} + +SECTIONS { + . = SIZEOF_HEADERS; + + /* + * The following, including the FILEHDRS and PHDRS, are modified + * when we relocate the binary. We want them to be initially + * writable for the relocation; we'll force them read-only after. + */ + .note : { *(.note*) } :load :note + .dynamic : { *(.dynamic) } :load :dynamic + .dynsym : { *(.dynsym) } :load + /* + * There ought not be any real read-write data. + * But since we manipulated the segment layout, + * we have to put these sections somewhere. + */ + .data : { + *(.data*) + *(.sdata*) + *(.got.plt) *(.got) + *(.gnu.linkonce.d.*) + *(.bss*) + *(.dynbss*) + *(.gnu.linkonce.b.*) + } + + .rodata : { *(.rodata*) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .eh_frame_hdr : { *(.eh_frame_hdr) } :load :eh_frame_hdr + .eh_frame : { *(.eh_frame) } :load + + .text : { *(.text*) } :load +} diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 0a3a57018b..7400ed0ca1 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -944,13 +944,14 @@ const char *elf_hwcap2_str(uint32_t bit) #undef GET_FEATURE_ID +#endif /* not TARGET_AARCH64 */ + #if TARGET_BIG_ENDIAN # define VDSO_HEADER "vdso-be.c.inc" #else # define VDSO_HEADER "vdso-le.c.inc" #endif -#endif /* not TARGET_AARCH64 */ #endif /* TARGET_ARM */ #ifdef TARGET_SPARC From c7bc2a8fb14650264ada7012af59d87207da590f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 18 Jun 2021 12:01:52 -0700 Subject: [PATCH 219/974] linux-user/hppa: Add vdso MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/elfload.c | 2 + linux-user/hppa/Makefile.vdso | 11 +++ linux-user/hppa/meson.build | 5 + linux-user/hppa/signal.c | 24 +++-- linux-user/hppa/vdso-asmoffset.h | 12 +++ linux-user/hppa/vdso.S | 165 +++++++++++++++++++++++++++++++ linux-user/hppa/vdso.ld | 77 +++++++++++++++ linux-user/hppa/vdso.so | Bin 0 -> 2104 bytes 8 files changed, 286 insertions(+), 10 deletions(-) create mode 100644 linux-user/hppa/Makefile.vdso create mode 100644 linux-user/hppa/vdso-asmoffset.h create mode 100644 linux-user/hppa/vdso.S create mode 100644 linux-user/hppa/vdso.ld create mode 100755 linux-user/hppa/vdso.so diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 7400ed0ca1..3306651be5 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1929,6 +1929,8 @@ static inline void init_thread(struct target_pt_regs *regs, #define STACK_GROWS_DOWN 0 #define STACK_ALIGNMENT 64 +#define VDSO_HEADER "vdso.c.inc" + static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) { diff --git a/linux-user/hppa/Makefile.vdso b/linux-user/hppa/Makefile.vdso new file mode 100644 index 0000000000..f4537ae716 --- /dev/null +++ b/linux-user/hppa/Makefile.vdso @@ -0,0 +1,11 @@ +include $(BUILD_DIR)/tests/tcg/hppa-linux-user/config-target.mak + +SUBDIR = $(SRC_PATH)/linux-user/hppa +VPATH += $(SUBDIR) + +all: $(SUBDIR)/vdso.so + +$(SUBDIR)/vdso.so: vdso.S vdso.ld vdso-asmoffset.h + $(CC) -o $@ -nostdlib -shared -Wl,-h,linux-vdso32.so.1 \ + -Wl,--build-id=sha1 -Wl,--hash-style=both \ + -Wl,-T,$(SUBDIR)/vdso.ld $< diff --git a/linux-user/hppa/meson.build b/linux-user/hppa/meson.build index 4709508a09..aa2d9a87a6 100644 --- a/linux-user/hppa/meson.build +++ b/linux-user/hppa/meson.build @@ -3,3 +3,8 @@ syscall_nr_generators += { arguments: [ meson.current_source_dir() / 'syscallhdr.sh', '@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@' ], output: '@BASENAME@_nr.h') } + +vdso_inc = gen_vdso.process('vdso.so', + extra_args: [ '-r', '__kernel_sigtramp_rt' ]) + +linux_user_ss.add(when: 'TARGET_HPPA', if_true: vdso_inc) diff --git a/linux-user/hppa/signal.c b/linux-user/hppa/signal.c index ec5f5412d1..17920e9ceb 100644 --- a/linux-user/hppa/signal.c +++ b/linux-user/hppa/signal.c @@ -21,6 +21,7 @@ #include "user-internals.h" #include "signal-common.h" #include "linux-user/trace.h" +#include "vdso-asmoffset.h" struct target_sigcontext { abi_ulong sc_flags; @@ -47,6 +48,19 @@ struct target_rt_sigframe { /* hidden location of upper halves of pa2.0 64-bit gregs */ }; +QEMU_BUILD_BUG_ON(sizeof(struct target_rt_sigframe) != sizeof_rt_sigframe); +QEMU_BUILD_BUG_ON(offsetof(struct target_rt_sigframe, uc.tuc_mcontext) + != offsetof_sigcontext); +QEMU_BUILD_BUG_ON(offsetof(struct target_sigcontext, sc_gr) + != offsetof_sigcontext_gr); +QEMU_BUILD_BUG_ON(offsetof(struct target_sigcontext, sc_fr) + != offsetof_sigcontext_fr); +QEMU_BUILD_BUG_ON(offsetof(struct target_sigcontext, sc_iaoq) + != offsetof_sigcontext_iaoq); +QEMU_BUILD_BUG_ON(offsetof(struct target_sigcontext, sc_sar) + != offsetof_sigcontext_sar); + + static void setup_sigcontext(struct target_sigcontext *sc, CPUArchState *env) { int i; @@ -91,16 +105,6 @@ static void restore_sigcontext(CPUArchState *env, struct target_sigcontext *sc) __get_user(env->cr[CR_SAR], &sc->sc_sar); } -#if TARGET_ABI_BITS == 32 -#define SIGFRAME 64 -#define FUNCTIONCALLFRAME 48 -#else -#define SIGFRAME 128 -#define FUNCTIONCALLFRAME 96 -#endif -#define PARISC_RT_SIGFRAME_SIZE32 \ - ((sizeof(struct target_rt_sigframe) + FUNCTIONCALLFRAME + SIGFRAME) & -SIGFRAME) - void setup_rt_frame(int sig, struct target_sigaction *ka, target_siginfo_t *info, target_sigset_t *set, CPUArchState *env) diff --git a/linux-user/hppa/vdso-asmoffset.h b/linux-user/hppa/vdso-asmoffset.h new file mode 100644 index 0000000000..c8b40c0332 --- /dev/null +++ b/linux-user/hppa/vdso-asmoffset.h @@ -0,0 +1,12 @@ +#define sizeof_rt_sigframe 584 +#define offsetof_sigcontext 160 +#define offsetof_sigcontext_gr 0x4 +#define offsetof_sigcontext_fr 0x88 +#define offsetof_sigcontext_iaoq 0x190 +#define offsetof_sigcontext_sar 0x198 + +/* arch/parisc/include/asm/rt_sigframe.h */ +#define SIGFRAME 64 +#define FUNCTIONCALLFRAME 48 +#define PARISC_RT_SIGFRAME_SIZE32 \ + (((sizeof_rt_sigframe) + FUNCTIONCALLFRAME + SIGFRAME) & -SIGFRAME) diff --git a/linux-user/hppa/vdso.S b/linux-user/hppa/vdso.S new file mode 100644 index 0000000000..5be14d2f70 --- /dev/null +++ b/linux-user/hppa/vdso.S @@ -0,0 +1,165 @@ +/* + * hppa linux kernel vdso replacement. + * + * Copyright 2023 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include "vdso-asmoffset.h" + + .text + + +/* + * arch/parisc/kernel/vdso32/sigtramp.S: + * Gdb expects the trampoline is on the stack and the pc is offset from + * a 64-byte boundary by 0, 4 or 5 instructions. Since the vdso trampoline + * is not on the stack, we need a new variant with different offsets and + * data to tell gdb where to find the signal context on the stack. + * + * Here we put the offset to the context data at the start of the trampoline + * region and offset the first trampoline by 2 instructions. Please do + * not change the trampoline as the code in gdb depends on the following + * instruction sequence exactly. + */ + +/* arch/parisc/kernel/asm-offsets.c */ +#define SIGFRAME_CONTEXT_REGS32 \ + (offsetof_sigcontext - PARISC_RT_SIGFRAME_SIZE32) + + .align 64 + .word SIGFRAME_CONTEXT_REGS32 + +/* + * All that said, we can provide a proper unwind record, which means that + * GDB should not actually need the offset magic. + * + * The return address that arrived here, from the inner frame, is + * not marked as a signal frame and so the unwinder still tries to + * subtract 1 to examine the presumed call insn. Thus we must + * extend the unwind info to a nop before the start. + */ + + .cfi_startproc simple + .cfi_signal_frame + + /* Compare pa32_fallback_frame_state from libgcc. */ + + /* + * Place the CFA at the start of sigcontext for convenience. + * The previous CFA will be restored from the saved stack pointer. + */ + .cfi_def_cfa 30, -PARISC_RT_SIGFRAME_SIZE32 + offsetof_sigcontext + + /* Record save offset of general registers. */ + .cfi_offset 1, offsetof_sigcontext_gr + 1 * 4 + .cfi_offset 2, offsetof_sigcontext_gr + 2 * 4 + .cfi_offset 3, offsetof_sigcontext_gr + 3 * 4 + .cfi_offset 4, offsetof_sigcontext_gr + 4 * 4 + .cfi_offset 5, offsetof_sigcontext_gr + 5 * 4 + .cfi_offset 6, offsetof_sigcontext_gr + 6 * 4 + .cfi_offset 7, offsetof_sigcontext_gr + 7 * 4 + .cfi_offset 8, offsetof_sigcontext_gr + 8 * 4 + .cfi_offset 9, offsetof_sigcontext_gr + 9 * 4 + .cfi_offset 10, offsetof_sigcontext_gr + 10 * 4 + .cfi_offset 11, offsetof_sigcontext_gr + 11 * 4 + .cfi_offset 12, offsetof_sigcontext_gr + 12 * 4 + .cfi_offset 13, offsetof_sigcontext_gr + 13 * 4 + .cfi_offset 14, offsetof_sigcontext_gr + 14 * 4 + .cfi_offset 15, offsetof_sigcontext_gr + 15 * 4 + .cfi_offset 16, offsetof_sigcontext_gr + 16 * 4 + .cfi_offset 17, offsetof_sigcontext_gr + 17 * 4 + .cfi_offset 18, offsetof_sigcontext_gr + 18 * 4 + .cfi_offset 19, offsetof_sigcontext_gr + 19 * 4 + .cfi_offset 20, offsetof_sigcontext_gr + 20 * 4 + .cfi_offset 21, offsetof_sigcontext_gr + 21 * 4 + .cfi_offset 22, offsetof_sigcontext_gr + 22 * 4 + .cfi_offset 23, offsetof_sigcontext_gr + 23 * 4 + .cfi_offset 24, offsetof_sigcontext_gr + 24 * 4 + .cfi_offset 25, offsetof_sigcontext_gr + 25 * 4 + .cfi_offset 26, offsetof_sigcontext_gr + 26 * 4 + .cfi_offset 27, offsetof_sigcontext_gr + 27 * 4 + .cfi_offset 28, offsetof_sigcontext_gr + 28 * 4 + .cfi_offset 29, offsetof_sigcontext_gr + 29 * 4 + .cfi_offset 30, offsetof_sigcontext_gr + 30 * 4 + .cfi_offset 31, offsetof_sigcontext_gr + 31 * 4 + + /* Record save offset of fp registers, left and right halves. */ + .cfi_offset 32, offsetof_sigcontext_fr + 4 * 8 + .cfi_offset 33, offsetof_sigcontext_fr + 4 * 8 + 4 + .cfi_offset 34, offsetof_sigcontext_fr + 5 * 8 + .cfi_offset 35, offsetof_sigcontext_fr + 5 * 8 + 4 + .cfi_offset 36, offsetof_sigcontext_fr + 6 * 8 + .cfi_offset 37, offsetof_sigcontext_fr + 6 * 8 + 4 + .cfi_offset 38, offsetof_sigcontext_fr + 7 * 8 + .cfi_offset 39, offsetof_sigcontext_fr + 7 * 8 + 4 + .cfi_offset 40, offsetof_sigcontext_fr + 8 * 8 + .cfi_offset 41, offsetof_sigcontext_fr + 8 * 8 + 4 + .cfi_offset 42, offsetof_sigcontext_fr + 9 * 8 + .cfi_offset 43, offsetof_sigcontext_fr + 9 * 8 + 4 + .cfi_offset 44, offsetof_sigcontext_fr + 10 * 8 + .cfi_offset 45, offsetof_sigcontext_fr + 10 * 8 + 4 + .cfi_offset 46, offsetof_sigcontext_fr + 11 * 8 + .cfi_offset 47, offsetof_sigcontext_fr + 11 * 8 + 4 + .cfi_offset 48, offsetof_sigcontext_fr + 12 * 8 + .cfi_offset 49, offsetof_sigcontext_fr + 12 * 8 + 4 + .cfi_offset 50, offsetof_sigcontext_fr + 13 * 8 + .cfi_offset 51, offsetof_sigcontext_fr + 13 * 8 + 4 + .cfi_offset 52, offsetof_sigcontext_fr + 14 * 8 + .cfi_offset 53, offsetof_sigcontext_fr + 14 * 8 + 4 + .cfi_offset 54, offsetof_sigcontext_fr + 15 * 8 + .cfi_offset 55, offsetof_sigcontext_fr + 15 * 8 + 4 + .cfi_offset 56, offsetof_sigcontext_fr + 16 * 8 + .cfi_offset 57, offsetof_sigcontext_fr + 16 * 8 + 4 + .cfi_offset 58, offsetof_sigcontext_fr + 17 * 8 + .cfi_offset 59, offsetof_sigcontext_fr + 17 * 8 + 4 + .cfi_offset 60, offsetof_sigcontext_fr + 18 * 8 + .cfi_offset 61, offsetof_sigcontext_fr + 18 * 8 + 4 + .cfi_offset 62, offsetof_sigcontext_fr + 19 * 8 + .cfi_offset 63, offsetof_sigcontext_fr + 19 * 8 + 4 + .cfi_offset 64, offsetof_sigcontext_fr + 20 * 8 + .cfi_offset 65, offsetof_sigcontext_fr + 20 * 8 + 4 + .cfi_offset 66, offsetof_sigcontext_fr + 21 * 8 + .cfi_offset 67, offsetof_sigcontext_fr + 21 * 8 + 4 + .cfi_offset 68, offsetof_sigcontext_fr + 22 * 8 + .cfi_offset 69, offsetof_sigcontext_fr + 22 * 8 + 4 + .cfi_offset 70, offsetof_sigcontext_fr + 23 * 8 + .cfi_offset 71, offsetof_sigcontext_fr + 23 * 8 + 4 + .cfi_offset 72, offsetof_sigcontext_fr + 24 * 8 + .cfi_offset 73, offsetof_sigcontext_fr + 24 * 8 + 4 + .cfi_offset 74, offsetof_sigcontext_fr + 25 * 8 + .cfi_offset 75, offsetof_sigcontext_fr + 25 * 8 + 4 + .cfi_offset 76, offsetof_sigcontext_fr + 26 * 8 + .cfi_offset 77, offsetof_sigcontext_fr + 26 * 8 + 4 + .cfi_offset 78, offsetof_sigcontext_fr + 27 * 8 + .cfi_offset 79, offsetof_sigcontext_fr + 27 * 8 + 4 + .cfi_offset 80, offsetof_sigcontext_fr + 28 * 8 + .cfi_offset 81, offsetof_sigcontext_fr + 28 * 8 + 4 + .cfi_offset 82, offsetof_sigcontext_fr + 29 * 8 + .cfi_offset 83, offsetof_sigcontext_fr + 29 * 8 + 4 + .cfi_offset 84, offsetof_sigcontext_fr + 30 * 8 + .cfi_offset 85, offsetof_sigcontext_fr + 30 * 8 + 4 + .cfi_offset 86, offsetof_sigcontext_fr + 31 * 8 + .cfi_offset 87, offsetof_sigcontext_fr + 31 * 8 + 4 + + /* Record save offset of %sar */ + .cfi_offset 88, offsetof_sigcontext_sar + + /* Record save offset of return address, iaoq[0]. */ + .cfi_return_column 89 + .cfi_offset 89, offsetof_sigcontext_iaoq + + nop + +__kernel_sigtramp_rt: + ldi 0, %r25 + ldi __NR_rt_sigreturn, %r20 + be,l 0x100(%sr2, %r0), %sr0, %r31 + nop + + .cfi_endproc + .size __kernel_sigtramp_rt, . - __kernel_sigtramp_rt + .type __kernel_sigtramp_rt, @function + .globl __kernel_sigtramp_rt diff --git a/linux-user/hppa/vdso.ld b/linux-user/hppa/vdso.ld new file mode 100644 index 0000000000..b17ad974f3 --- /dev/null +++ b/linux-user/hppa/vdso.ld @@ -0,0 +1,77 @@ +/* + * Linker script for linux hppa vdso. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +VERSION { + /* + * The kernel's vdso32.lds.S attempts to export + * __kernel_sigtramp_rt32 + * __kernel_restart_syscall32 + * except that those symbols don't exist. The actual symbols are + * __kernel_sigtramp_rt + * __kernel_restart_syscall + * which means that nothing is exported at all. + * QEMU handles syscall restart internally, so we don't + * need to implement __kernel_restart_syscall at all. + */ + LINUX_5.18 { + local: *; + }; +} + + +PHDRS { + phdr PT_PHDR FLAGS(4) PHDRS; + load PT_LOAD FLAGS(7) FILEHDR PHDRS; + dynamic PT_DYNAMIC FLAGS(4); + note PT_NOTE FLAGS(4); + eh_frame_hdr PT_GNU_EH_FRAME; +} + +SECTIONS { + . = SIZEOF_HEADERS; + + /* The following, including the FILEHDRS and PHDRS, are modified + when we relocate the binary. We want them to be initially + writable for the relocation; we'll force them read-only after. */ + .note : { *(.note*) } :load :note + .dynamic : { *(.dynamic) } :load :dynamic + .dynsym : { *(.dynsym) } :load + .data : { + /* There ought not be any real read-write data. + But since we manipulated the segment layout, + we have to put these sections somewhere. */ + *(.data*) + *(.sdata*) + *(.got.plt) *(.got) + *(.gnu.linkonce.d.*) + *(.bss*) + *(.dynbss*) + *(.gnu.linkonce.b.*) + } + + .rodata : { *(.rodata) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .eh_frame_hdr : { *(.eh_frame_hdr) } :load :eh_frame_hdr + .eh_frame : { *(.eh_frame) } :load + + .text : { *(.text*) } :load +} diff --git a/linux-user/hppa/vdso.so b/linux-user/hppa/vdso.so new file mode 100755 index 0000000000000000000000000000000000000000..e1ddd70c37e9eb6871c21d538dcce0a1e736a918 GIT binary patch literal 2104 zcmbtVTXR!Y6kaFkhEgcBP%l`hafWdil!Lv1RY03GNz;<1rD7>6(0JUG~~*U)EYXMdFQ(BmGb?r%x-$mIFC+3xTU<+0?(1N%-~JGte| zwo#0&1h`Mkv8G3g7ybaBfv&)R1jiYzaqu#r23QVo^SCSe7yscgz|X_x1OE;7zacM{ z)|dd5Qv4kHJ82ot+=CYbD;A=;?E| z+O@bm6U{_s6$j@&=K!(p?Mg~X zNxxOSx@O|YQQtBD{UyiM14~XUeXw+5*+XR~mp{DXk@8b3A6<32;<3udS3j}l$*QL= zd;0Qcu9&>?*|pDI^?daU>t0;{Qq9X(zY>_*^y+o5UH|&#H|#gJymiCdwbOO))W6&C z-q!cGeQ@K4jUV0g@y(xX|8&P^J3qhW%&lKEeHlC(`l|Ws@Hdf}=(jE3#lCMHW; zONHX9xm|@5D$=2%ovLNGigl^hq-yI{yLwc7k81B#i9XfQuR8at-M6W(+g0)o)qSVx zanzoDsyC(j22_8Vr_*Qt6`W`0eJzTL#S4K_@ijH9MKe`ddw-H+LR}U|=Qy2(>V?rY z1fBH)(sj*o{IovBFBJ9<$eQLCI%^7~`+_?Ao;v%3I{KO9);;u6S9h0zzRc5gxj|&Sy_i8rNTu72w>n-mm4>KjyI4HF5rW`17r(z$)~w!;~p-Ow|1nqfFBMc>aU8q_P9)|;yMpZzCl>C4=|Coz%^Y;{L;DN4)bKq{t{_fJydxBhI~~zzGScmI z2Sf3Qv!HS8{Ndq3>MpVKg`CR=jg%cJWW-J%&ZdTk?$!c)!Bd5l*!#1i_FyVM=-LZ8 z*K#P6%MXoY7bs3z?98AufIVfL!8AOLCUzk+R&dYvKfl%RW1g54)&;QvFy!T2u7QF1 zMOrSt&AewX$GSF{-%SAXhIp(aT}uJz^keQAkM9EVqIJY$eh5<^kS@K|J0P>p7;oBz z?knzt#CSv_-q=6E={Ms-0?_~K7xHZML!Ihc<@;@jdnC0XUJ;yrc(-*8a~%^8c8*D} zdE;G`CSg6AJLMeG0KnLAF#k;p-Us8y>={4wf$jxe@351M-+hqjhn)DwT}TsjLrtFG t#WH69ET#p-{eR>C0pl?r>_u}nn& Date: Tue, 6 Jul 2021 15:49:50 -0700 Subject: [PATCH 220/974] linux-user/riscv: Add vdso MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Acked-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/elfload.c | 2 + linux-user/meson.build | 1 + linux-user/riscv/Makefile.vdso | 15 +++ linux-user/riscv/meson.build | 7 ++ linux-user/riscv/signal.c | 8 ++ linux-user/riscv/vdso-32.so | Bin 0 -> 2900 bytes linux-user/riscv/vdso-64.so | Bin 0 -> 3856 bytes linux-user/riscv/vdso-asmoffset.h | 9 ++ linux-user/riscv/vdso.S | 187 ++++++++++++++++++++++++++++++ linux-user/riscv/vdso.ld | 74 ++++++++++++ 10 files changed, 303 insertions(+) create mode 100644 linux-user/riscv/Makefile.vdso create mode 100644 linux-user/riscv/meson.build create mode 100755 linux-user/riscv/vdso-32.so create mode 100755 linux-user/riscv/vdso-64.so create mode 100644 linux-user/riscv/vdso-asmoffset.h create mode 100644 linux-user/riscv/vdso.S create mode 100644 linux-user/riscv/vdso.ld diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 3306651be5..e2d90b84a9 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1892,8 +1892,10 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #ifdef TARGET_RISCV32 #define ELF_CLASS ELFCLASS32 +#define VDSO_HEADER "vdso-32.c.inc" #else #define ELF_CLASS ELFCLASS64 +#define VDSO_HEADER "vdso-64.c.inc" #endif #define ELF_HWCAP get_elf_hwcap() diff --git a/linux-user/meson.build b/linux-user/meson.build index dd24389052..3ff3bc5bbc 100644 --- a/linux-user/meson.build +++ b/linux-user/meson.build @@ -45,6 +45,7 @@ subdir('microblaze') subdir('mips64') subdir('mips') subdir('ppc') +subdir('riscv') subdir('s390x') subdir('sh4') subdir('sparc') diff --git a/linux-user/riscv/Makefile.vdso b/linux-user/riscv/Makefile.vdso new file mode 100644 index 0000000000..2c257dbfda --- /dev/null +++ b/linux-user/riscv/Makefile.vdso @@ -0,0 +1,15 @@ +include $(BUILD_DIR)/tests/tcg/riscv64-linux-user/config-target.mak + +SUBDIR = $(SRC_PATH)/linux-user/riscv +VPATH += $(SUBDIR) + +all: $(SUBDIR)/vdso-32.so $(SUBDIR)/vdso-64.so + +LDFLAGS = -nostdlib -shared -fpic -Wl,-h,linux-vdso.so.1 -Wl,--build-id=sha1 \ + -Wl,--hash-style=both -Wl,-T,$(SUBDIR)/vdso.ld + +$(SUBDIR)/vdso-32.so: vdso.S vdso.ld vdso-asmoffset.h + $(CC) -o $@ $(LDFLAGS) -mabi=ilp32d -march=rv32g $< + +$(SUBDIR)/vdso-64.so: vdso.S vdso.ld vdso-asmoffset.h + $(CC) -o $@ $(LDFLAGS) -mabi=lp64d -march=rv64g $< diff --git a/linux-user/riscv/meson.build b/linux-user/riscv/meson.build new file mode 100644 index 0000000000..beb989a7ca --- /dev/null +++ b/linux-user/riscv/meson.build @@ -0,0 +1,7 @@ +vdso_32_inc = gen_vdso.process('vdso-32.so', + extra_args: ['-r', '__vdso_rt_sigreturn']) +vdso_64_inc = gen_vdso.process('vdso-64.so', + extra_args: ['-r', '__vdso_rt_sigreturn']) + +linux_user_ss.add(when: 'TARGET_RISCV32', if_true: vdso_32_inc) +linux_user_ss.add(when: 'TARGET_RISCV64', if_true: vdso_64_inc) diff --git a/linux-user/riscv/signal.c b/linux-user/riscv/signal.c index f989f7f51f..941eadce87 100644 --- a/linux-user/riscv/signal.c +++ b/linux-user/riscv/signal.c @@ -21,6 +21,7 @@ #include "user-internals.h" #include "signal-common.h" #include "linux-user/trace.h" +#include "vdso-asmoffset.h" /* Signal handler invocation must be transparent for the code being interrupted. Complete CPU (hart) state is saved on entry and restored @@ -37,6 +38,8 @@ struct target_sigcontext { uint32_t fcsr; }; /* cf. riscv-linux:arch/riscv/include/uapi/asm/ptrace.h */ +QEMU_BUILD_BUG_ON(offsetof(struct target_sigcontext, fpr) != offsetof_freg0); + struct target_ucontext { abi_ulong uc_flags; abi_ptr uc_link; @@ -51,6 +54,11 @@ struct target_rt_sigframe { struct target_ucontext uc; }; +QEMU_BUILD_BUG_ON(sizeof(struct target_rt_sigframe) + != sizeof_rt_sigframe); +QEMU_BUILD_BUG_ON(offsetof(struct target_rt_sigframe, uc.uc_mcontext) + != offsetof_uc_mcontext); + static abi_ulong get_sigframe(struct target_sigaction *ka, CPURISCVState *regs, size_t framesize) { diff --git a/linux-user/riscv/vdso-32.so b/linux-user/riscv/vdso-32.so new file mode 100755 index 0000000000000000000000000000000000000000..1ad1e5cbbbb8b1fe36b0fe4bcb6c06fab8219ecd GIT binary patch literal 2900 zcmb_eTWl0n7(TPZVyUHWDVSm#Q)tBoakE`3X|QQ_x7~7aX*Y!`UgC5+yX~&9yUp%w zLmR@yBHoaTR0Kq&poogPiZ>L6L=zLABtH4zgAYa%jnRm{h}7?!nX^o_4?g&}-~Q*n z%s=PM`Oi1AEgb1m6h%l;#cx7dEpVPL6Ji#0i>Mc~MU$u!9%$NEaRFn3d4wv&aH|^w zTRLpb7;DS=wp%clxP}go5H6@1BuN~CP00Gu?~M2%+(e=gF+#?vFF7z%d_LkRAy#(x ziH@zeoN%Z87eg1Tzxkm)cKE@*AHS_c%m7%c6BGLV%2aj>G?#S_n$Mt!IhI531+E1! zb!!dKcxvxuzj?%SZXc$;s>gdwhJf9!SZZ}R-lbJ=rs0Q-uj8VuiV4WJ^OMpEfqLuY=G&6I30BbFy zA2MMnUcU|n0!nR#tLSzI;yVT&>K}h#=lu`gyKBQEcRjlPvEIj{Pjv5&Jh^7is=ZzN zI`@a33LI#kXnT6a!PaM%A8I-5KjM4Vdvw`zOP_CgVac)1$U+(5dfDqs-)MSsNwM**#c$W2x$&L4cW-#F=KbqGn16QOhn|lr&sBU}{z=*S z+2&l2Vs85Y_AN!<5)^*R-~LR437Z$IQOEpi4#W|L(nwbF}76{Kigu9gV0Xy8iVcR#yzl|T*jVra$ym+lRH0y?c_$G>(TsZ zB(~9;6^)yP)_G5NaP8_2p+$RpI>McOyli^#7r|0-*i5k#$9%$5;Zzlu;e3=3gOL-uHggF2 z{;O27K3SuT)P2F=8->$pX?kenYY!>u*#=6hC(_g{2g1p%}2$6tfXRaIWoe=_Y*egQvasH*+2{ zbI;8DW_Eul-0jdb5YgcZT&)54*l?-dDl9djvsjh<;ksr)B#kJLyo0*3$OAUhL zOF~G`5pGvS@fRdt62fMDFh8)RcK}XpyK&cy`P-tAbBl-nyz=dg=iu7KXo6+*LNP5U z$5HVj1)}`^&s?rt;D&kl;N!Za{W2v6g{Kz|TUA)XxQbEDx#Wk_Ov9%+Af&lsH!N8q^H2Rt%4sghpOC(nVzkE9O+F1g zq$9ubynoX&z4Ndq)q3>6+}R76HLK4AvYjixKCyOsPXY{MBwoxJLkZJNXA-$&Ji1qO zvEf|oezD9`77|6#VQ?&8l3H@OR7@G^STvTZG+!`{VtS~MFiV9j45zcD(OaooH~w#g z@YddeL8INh@irVVilge!(rNgG^mlsZ=cTm2TJe#+L*MQ|TWSA~^<*QV`6SU=^t)V$ zl&5BN>_lusj39<7kRBAK8PLc2K-;HjElV9`9qorp_{jPpzYX|YWJG}D^g_Ydofz!X zS{fX)3+sFn+JocTL;D^coiHECKRWza>hbs!yPw?k)Zl^sgONkw!#ziWPy3$foa{K- z{;cQNhUeBDcRk;BqVt)}W&98L6dgs}W*EYT0{)YEW&s*+u z8{S_3&boJRnRdN*^ZRWd-1K4VM>l-D>XYlwH(yx(Y2#V)tpkg7g^^9| zX>Wd`k@4m|MwCiPZ{BOf={1&)6$_*vOD7ZASOOhA7<3KMadL!r?XEh`w(Of~y$-^M z)%yATKz1GKDtue*kC~&*qlk{X$yZbFgjzhP-(%7^yATK?CegNE&>d8Yk9`f-JzyyXVxe^-e8XK4f4)*aL z07j&5M<5g&=nEN0U)%g!LPo#8D;zSA1@YWBLOn)zpMQJE=n3|Lu_e5t%O5s&ba(Fz z^^2p8_LzigF=s7euIAe+ZsnFL)NNzwijIgLz+K#%F{67B&4Ps~DP?nJ0^ISv*=Qym zD;LlgF=|G^J(MlEQ_*6|(whZKGmULg(7Fyb}TxINYzg!U12A^pp~5fp#&C6T%yoo_@T(!bm%1&OOkq +#include + +#if __riscv_xlen == 32 +# define TARGET_ABI32 +#endif +#include "vdso-asmoffset.h" + + .text + +.macro endf name + .globl \name + .type \name, @function + .size \name, . - \name +.endm + +.macro raw_syscall nr + li a7, \nr + ecall +.endm + +.macro vdso_syscall name, nr +\name: + raw_syscall \nr + ret +endf \name +.endm + +__vdso_gettimeofday: + .cfi_startproc +#ifdef __NR_gettimeofday + raw_syscall __NR_gettimeofday + ret +#else + /* No gettimeofday, fall back to clock_gettime64. */ + beq a1, zero, 1f + sw zero, 0(a1) /* tz->tz_minuteswest = 0 */ + sw zero, 4(a1) /* tz->tz_dsttime = 0 */ +1: addi sp, sp, -32 + .cfi_adjust_cfa_offset 32 + sw a0, 16(sp) /* save tv */ + mv a0, sp + raw_syscall __NR_clock_gettime64 + lw t0, 0(sp) /* timespec.tv_sec.low */ + lw t1, 4(sp) /* timespec.tv_sec.high */ + lw t2, 8(sp) /* timespec.tv_nsec.low */ + lw a1, 16(sp) /* restore tv */ + addi sp, sp, 32 + .cfi_adjust_cfa_offset -32 + bne a0, zero, 9f /* syscall error? */ + li a0, -EOVERFLOW + bne t1, zero, 9f /* y2038? */ + li a0, 0 + li t3, 1000 + divu t2, t2, t3 /* nsec -> usec */ + sw t0, 0(a1) /* tz->tv_sec */ + sw t2, 4(a1) /* tz->tv_usec */ +9: ret +#endif + .cfi_endproc +endf __vdso_gettimeofday + + .cfi_startproc + +#ifdef __NR_clock_gettime +vdso_syscall __vdso_clock_gettime, __NR_clock_gettime +#else +vdso_syscall __vdso_clock_gettime, __NR_clock_gettime64 +#endif + +#ifdef __NR_clock_getres +vdso_syscall __vdso_clock_getres, __NR_clock_getres +#else +vdso_syscall __vdso_clock_getres, __NR_clock_getres_time64 +#endif + +vdso_syscall __vdso_getcpu, __NR_getcpu + +__vdso_flush_icache: + /* qemu does not need to flush the icache */ + li a0, 0 + ret +endf __vdso_flush_icache + + .cfi_endproc + +/* + * Start the unwind info at least one instruction before the signal + * trampoline, because the unwinder will assume we are returning + * after a call site. + */ + + .cfi_startproc simple + .cfi_signal_frame + +#define sizeof_reg (__riscv_xlen / 4) +#define sizeof_freg 8 +#define B_GR (offsetof_uc_mcontext - sizeof_rt_sigframe) +#define B_FR (offsetof_uc_mcontext - sizeof_rt_sigframe + offsetof_freg0) + + .cfi_def_cfa 2, sizeof_rt_sigframe + + /* Return address */ + .cfi_return_column 64 + .cfi_offset 64, B_GR + 0 /* pc */ + + /* Integer registers */ + .cfi_offset 1, B_GR + 1 * sizeof_reg /* r1 (ra) */ + .cfi_offset 2, B_GR + 2 * sizeof_reg /* r2 (sp) */ + .cfi_offset 3, B_GR + 3 * sizeof_reg + .cfi_offset 4, B_GR + 4 * sizeof_reg + .cfi_offset 5, B_GR + 5 * sizeof_reg + .cfi_offset 6, B_GR + 6 * sizeof_reg + .cfi_offset 7, B_GR + 7 * sizeof_reg + .cfi_offset 8, B_GR + 8 * sizeof_reg + .cfi_offset 9, B_GR + 9 * sizeof_reg + .cfi_offset 10, B_GR + 10 * sizeof_reg + .cfi_offset 11, B_GR + 11 * sizeof_reg + .cfi_offset 12, B_GR + 12 * sizeof_reg + .cfi_offset 13, B_GR + 13 * sizeof_reg + .cfi_offset 14, B_GR + 14 * sizeof_reg + .cfi_offset 15, B_GR + 15 * sizeof_reg + .cfi_offset 16, B_GR + 16 * sizeof_reg + .cfi_offset 17, B_GR + 17 * sizeof_reg + .cfi_offset 18, B_GR + 18 * sizeof_reg + .cfi_offset 19, B_GR + 19 * sizeof_reg + .cfi_offset 20, B_GR + 20 * sizeof_reg + .cfi_offset 21, B_GR + 21 * sizeof_reg + .cfi_offset 22, B_GR + 22 * sizeof_reg + .cfi_offset 23, B_GR + 23 * sizeof_reg + .cfi_offset 24, B_GR + 24 * sizeof_reg + .cfi_offset 25, B_GR + 25 * sizeof_reg + .cfi_offset 26, B_GR + 26 * sizeof_reg + .cfi_offset 27, B_GR + 27 * sizeof_reg + .cfi_offset 28, B_GR + 28 * sizeof_reg + .cfi_offset 29, B_GR + 29 * sizeof_reg + .cfi_offset 30, B_GR + 30 * sizeof_reg + .cfi_offset 31, B_GR + 31 * sizeof_reg /* r31 */ + + .cfi_offset 32, B_FR + 0 /* f0 */ + .cfi_offset 33, B_FR + 1 * sizeof_freg /* f1 */ + .cfi_offset 34, B_FR + 2 * sizeof_freg + .cfi_offset 35, B_FR + 3 * sizeof_freg + .cfi_offset 36, B_FR + 4 * sizeof_freg + .cfi_offset 37, B_FR + 5 * sizeof_freg + .cfi_offset 38, B_FR + 6 * sizeof_freg + .cfi_offset 39, B_FR + 7 * sizeof_freg + .cfi_offset 40, B_FR + 8 * sizeof_freg + .cfi_offset 41, B_FR + 9 * sizeof_freg + .cfi_offset 42, B_FR + 10 * sizeof_freg + .cfi_offset 43, B_FR + 11 * sizeof_freg + .cfi_offset 44, B_FR + 12 * sizeof_freg + .cfi_offset 45, B_FR + 13 * sizeof_freg + .cfi_offset 46, B_FR + 14 * sizeof_freg + .cfi_offset 47, B_FR + 15 * sizeof_freg + .cfi_offset 48, B_FR + 16 * sizeof_freg + .cfi_offset 49, B_FR + 17 * sizeof_freg + .cfi_offset 50, B_FR + 18 * sizeof_freg + .cfi_offset 51, B_FR + 19 * sizeof_freg + .cfi_offset 52, B_FR + 20 * sizeof_freg + .cfi_offset 53, B_FR + 21 * sizeof_freg + .cfi_offset 54, B_FR + 22 * sizeof_freg + .cfi_offset 55, B_FR + 23 * sizeof_freg + .cfi_offset 56, B_FR + 24 * sizeof_freg + .cfi_offset 57, B_FR + 25 * sizeof_freg + .cfi_offset 58, B_FR + 26 * sizeof_freg + .cfi_offset 59, B_FR + 27 * sizeof_freg + .cfi_offset 60, B_FR + 28 * sizeof_freg + .cfi_offset 61, B_FR + 29 * sizeof_freg + .cfi_offset 62, B_FR + 30 * sizeof_freg + .cfi_offset 63, B_FR + 31 * sizeof_freg /* f31 */ + + nop + +__vdso_rt_sigreturn: + raw_syscall __NR_rt_sigreturn +endf __vdso_rt_sigreturn + + .cfi_endproc diff --git a/linux-user/riscv/vdso.ld b/linux-user/riscv/vdso.ld new file mode 100644 index 0000000000..aabe2b0ab3 --- /dev/null +++ b/linux-user/riscv/vdso.ld @@ -0,0 +1,74 @@ +/* + * Linker script for linux riscv replacement vdso. + * + * Copyright 2021 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +VERSION { + LINUX_4.15 { + global: + __vdso_rt_sigreturn; + __vdso_gettimeofday; + __vdso_clock_gettime; + __vdso_clock_getres; + __vdso_getcpu; + __vdso_flush_icache; + + local: *; + }; +} + + +PHDRS { + phdr PT_PHDR FLAGS(4) PHDRS; + load PT_LOAD FLAGS(7) FILEHDR PHDRS; + dynamic PT_DYNAMIC FLAGS(4); + eh_frame_hdr PT_GNU_EH_FRAME; + note PT_NOTE FLAGS(4); +} + +SECTIONS { + /* + * We can't prelink to any address without knowing something about + * the virtual memory space of the host, since that leaks over into + * the available memory space of the guest. + */ + . = SIZEOF_HEADERS; + + /* + * The following, including the FILEHDRS and PHDRS, are modified + * when we relocate the binary. We want them to be initially + * writable for the relocation; we'll force them read-only after. + */ + .note : { *(.note*) } :load :note + .dynamic : { *(.dynamic) } :load :dynamic + .dynsym : { *(.dynsym) } :load + /* + * There ought not be any real read-write data. + * But since we manipulated the segment layout, + * we have to put these sections somewhere. + */ + .data : { + *(.data*) + *(.sdata*) + *(.got.plt) *(.got) + *(.gnu.linkonce.d.*) + *(.bss*) + *(.dynbss*) + *(.gnu.linkonce.b.*) + } + + .rodata : { *(.rodata*) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .eh_frame_hdr : { *(.eh_frame_hdr) } :load :eh_frame_hdr + .eh_frame : { *(.eh_frame) } :load + + .text : { *(.text*) } :load =0xd503201f +} From 00cc2934b2f02c469bd28cae0f1ac09e289a5ae9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 14 Aug 2023 13:22:57 -0700 Subject: [PATCH 221/974] linux-user/loongarch64: Add vdso MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Requires a relatively recent binutils version in order to avoid spurious R_LARCH_NONE relocations. The presence of these relocs are diagnosed by our gen-vdso tool. Tested-by: Song Gao Reviewed-by: Song Gao Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/elfload.c | 2 + linux-user/loongarch64/Makefile.vdso | 11 ++ linux-user/loongarch64/meson.build | 4 + linux-user/loongarch64/signal.c | 17 +++- linux-user/loongarch64/vdso-asmoffset.h | 8 ++ linux-user/loongarch64/vdso.S | 130 ++++++++++++++++++++++++ linux-user/loongarch64/vdso.ld | 73 +++++++++++++ linux-user/loongarch64/vdso.so | Bin 0 -> 3560 bytes linux-user/meson.build | 1 + 9 files changed, 245 insertions(+), 1 deletion(-) create mode 100644 linux-user/loongarch64/Makefile.vdso create mode 100644 linux-user/loongarch64/meson.build create mode 100644 linux-user/loongarch64/vdso-asmoffset.h create mode 100644 linux-user/loongarch64/vdso.S create mode 100644 linux-user/loongarch64/vdso.ld create mode 100755 linux-user/loongarch64/vdso.so diff --git a/linux-user/elfload.c b/linux-user/elfload.c index e2d90b84a9..4e6e0059e6 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1197,6 +1197,8 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUPPCState *en #define elf_check_arch(x) ((x) == EM_LOONGARCH) +#define VDSO_HEADER "vdso.c.inc" + static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) { diff --git a/linux-user/loongarch64/Makefile.vdso b/linux-user/loongarch64/Makefile.vdso new file mode 100644 index 0000000000..369de13344 --- /dev/null +++ b/linux-user/loongarch64/Makefile.vdso @@ -0,0 +1,11 @@ +include $(BUILD_DIR)/tests/tcg/loongarch64-linux-user/config-target.mak + +SUBDIR = $(SRC_PATH)/linux-user/loongarch64 +VPATH += $(SUBDIR) + +all: $(SUBDIR)/vdso.so + +$(SUBDIR)/vdso.so: vdso.S vdso.ld vdso-asmoffset.h + $(CC) -o $@ -nostdlib -shared -fpic -Wl,-h,linux-vdso.so.1 \ + -Wl,--build-id=sha1 -Wl,--hash-style=both \ + -Wl,--no-warn-rwx-segments -Wl,-T,$(SUBDIR)/vdso.ld $< diff --git a/linux-user/loongarch64/meson.build b/linux-user/loongarch64/meson.build new file mode 100644 index 0000000000..17896535f0 --- /dev/null +++ b/linux-user/loongarch64/meson.build @@ -0,0 +1,4 @@ +vdso_inc = gen_vdso.process('vdso.so', + extra_args: ['-r', '__vdso_rt_sigreturn']) + +linux_user_ss.add(when: 'TARGET_LOONGARCH64', if_true: vdso_inc) diff --git a/linux-user/loongarch64/signal.c b/linux-user/loongarch64/signal.c index 39572c1190..afcee641a6 100644 --- a/linux-user/loongarch64/signal.c +++ b/linux-user/loongarch64/signal.c @@ -10,9 +10,9 @@ #include "user-internals.h" #include "signal-common.h" #include "linux-user/trace.h" - #include "target/loongarch/internals.h" #include "target/loongarch/vec.h" +#include "vdso-asmoffset.h" /* FP context was used */ #define SC_USED_FP (1 << 0) @@ -24,6 +24,11 @@ struct target_sigcontext { uint64_t sc_extcontext[0] QEMU_ALIGNED(16); }; +QEMU_BUILD_BUG_ON(sizeof(struct target_sigcontext) != sizeof_sigcontext); +QEMU_BUILD_BUG_ON(offsetof(struct target_sigcontext, sc_pc) + != offsetof_sigcontext_pc); +QEMU_BUILD_BUG_ON(offsetof(struct target_sigcontext, sc_regs) + != offsetof_sigcontext_gr); #define FPU_CTX_MAGIC 0x46505501 #define FPU_CTX_ALIGN 8 @@ -33,6 +38,9 @@ struct target_fpu_context { uint32_t fcsr; } QEMU_ALIGNED(FPU_CTX_ALIGN); +QEMU_BUILD_BUG_ON(offsetof(struct target_fpu_context, regs) + != offsetof_fpucontext_fr); + #define CONTEXT_INFO_ALIGN 16 struct target_sctx_info { uint32_t magic; @@ -40,6 +48,8 @@ struct target_sctx_info { uint64_t padding; } QEMU_ALIGNED(CONTEXT_INFO_ALIGN); +QEMU_BUILD_BUG_ON(sizeof(struct target_sctx_info) != sizeof_sctx_info); + struct target_ucontext { abi_ulong tuc_flags; abi_ptr tuc_link; @@ -54,6 +64,11 @@ struct target_rt_sigframe { struct target_ucontext rs_uc; }; +QEMU_BUILD_BUG_ON(sizeof(struct target_rt_sigframe) + != sizeof_rt_sigframe); +QEMU_BUILD_BUG_ON(offsetof(struct target_rt_sigframe, rs_uc.tuc_mcontext) + != offsetof_sigcontext); + /* * These two structures are not present in guest memory, are private * to the signal implementation, but are largely copied from the diff --git a/linux-user/loongarch64/vdso-asmoffset.h b/linux-user/loongarch64/vdso-asmoffset.h new file mode 100644 index 0000000000..60d113822f --- /dev/null +++ b/linux-user/loongarch64/vdso-asmoffset.h @@ -0,0 +1,8 @@ +#define sizeof_rt_sigframe 0x240 +#define sizeof_sigcontext 0x110 +#define sizeof_sctx_info 0x10 + +#define offsetof_sigcontext 0x130 +#define offsetof_sigcontext_pc 0 +#define offsetof_sigcontext_gr 8 +#define offsetof_fpucontext_fr 0 diff --git a/linux-user/loongarch64/vdso.S b/linux-user/loongarch64/vdso.S new file mode 100644 index 0000000000..780a5fda12 --- /dev/null +++ b/linux-user/loongarch64/vdso.S @@ -0,0 +1,130 @@ +/* + * Loongarch64 linux replacement vdso. + * + * Copyright 2023 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include "vdso-asmoffset.h" + + + .text + +.macro endf name + .globl \name + .type \name, @function + .size \name, . - \name +.endm + +.macro vdso_syscall name, nr +\name: + li.w $a7, \nr + syscall 0 + jr $ra +endf \name +.endm + + .cfi_startproc + +vdso_syscall __vdso_gettimeofday, __NR_gettimeofday +vdso_syscall __vdso_clock_gettime, __NR_clock_gettime +vdso_syscall __vdso_clock_getres, __NR_clock_getres +vdso_syscall __vdso_getcpu, __NR_getcpu + + .cfi_endproc + +/* + * Start the unwind info at least one instruction before the signal + * trampoline, because the unwinder will assume we are returning + * after a call site. + */ + + .cfi_startproc simple + .cfi_signal_frame + +#define B_GR offsetof_sigcontext_gr +#define B_FR sizeof_sigcontext + sizeof_sctx_info + offsetof_fpucontext_fr + + .cfi_def_cfa 2, offsetof_sigcontext + + /* Return address */ + .cfi_return_column 64 + .cfi_offset 64, offsetof_sigcontext_pc /* pc */ + + /* Integer registers */ + .cfi_offset 1, B_GR + 1 * 8 + .cfi_offset 2, B_GR + 2 * 8 + .cfi_offset 3, B_GR + 3 * 8 + .cfi_offset 4, B_GR + 4 * 8 + .cfi_offset 5, B_GR + 5 * 8 + .cfi_offset 6, B_GR + 6 * 8 + .cfi_offset 7, B_GR + 7 * 8 + .cfi_offset 8, B_GR + 8 * 8 + .cfi_offset 9, B_GR + 9 * 8 + .cfi_offset 10, B_GR + 10 * 8 + .cfi_offset 11, B_GR + 11 * 8 + .cfi_offset 12, B_GR + 12 * 8 + .cfi_offset 13, B_GR + 13 * 8 + .cfi_offset 14, B_GR + 14 * 8 + .cfi_offset 15, B_GR + 15 * 8 + .cfi_offset 16, B_GR + 16 * 8 + .cfi_offset 17, B_GR + 17 * 8 + .cfi_offset 18, B_GR + 18 * 8 + .cfi_offset 19, B_GR + 19 * 8 + .cfi_offset 20, B_GR + 20 * 8 + .cfi_offset 21, B_GR + 21 * 8 + .cfi_offset 22, B_GR + 22 * 8 + .cfi_offset 23, B_GR + 23 * 8 + .cfi_offset 24, B_GR + 24 * 8 + .cfi_offset 25, B_GR + 25 * 8 + .cfi_offset 26, B_GR + 26 * 8 + .cfi_offset 27, B_GR + 27 * 8 + .cfi_offset 28, B_GR + 28 * 8 + .cfi_offset 29, B_GR + 29 * 8 + .cfi_offset 30, B_GR + 30 * 8 + .cfi_offset 31, B_GR + 31 * 8 + + /* Floating point registers */ + .cfi_offset 32, B_FR + 0 + .cfi_offset 33, B_FR + 1 * 8 + .cfi_offset 34, B_FR + 2 * 8 + .cfi_offset 35, B_FR + 3 * 8 + .cfi_offset 36, B_FR + 4 * 8 + .cfi_offset 37, B_FR + 5 * 8 + .cfi_offset 38, B_FR + 6 * 8 + .cfi_offset 39, B_FR + 7 * 8 + .cfi_offset 40, B_FR + 8 * 8 + .cfi_offset 41, B_FR + 9 * 8 + .cfi_offset 42, B_FR + 10 * 8 + .cfi_offset 43, B_FR + 11 * 8 + .cfi_offset 44, B_FR + 12 * 8 + .cfi_offset 45, B_FR + 13 * 8 + .cfi_offset 46, B_FR + 14 * 8 + .cfi_offset 47, B_FR + 15 * 8 + .cfi_offset 48, B_FR + 16 * 8 + .cfi_offset 49, B_FR + 17 * 8 + .cfi_offset 50, B_FR + 18 * 8 + .cfi_offset 51, B_FR + 19 * 8 + .cfi_offset 52, B_FR + 20 * 8 + .cfi_offset 53, B_FR + 21 * 8 + .cfi_offset 54, B_FR + 22 * 8 + .cfi_offset 55, B_FR + 23 * 8 + .cfi_offset 56, B_FR + 24 * 8 + .cfi_offset 57, B_FR + 25 * 8 + .cfi_offset 58, B_FR + 26 * 8 + .cfi_offset 59, B_FR + 27 * 8 + .cfi_offset 60, B_FR + 28 * 8 + .cfi_offset 61, B_FR + 29 * 8 + .cfi_offset 62, B_FR + 30 * 8 + .cfi_offset 63, B_FR + 31 * 8 + + nop + +__vdso_rt_sigreturn: + li.w $a7, __NR_rt_sigreturn + syscall 0 + .cfi_endproc +endf __vdso_rt_sigreturn diff --git a/linux-user/loongarch64/vdso.ld b/linux-user/loongarch64/vdso.ld new file mode 100644 index 0000000000..682446ed0c --- /dev/null +++ b/linux-user/loongarch64/vdso.ld @@ -0,0 +1,73 @@ +/* + * Linker script for linux loongarch64 replacement vdso. + * + * Copyright 2023 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +VERSION { + LINUX_5.10 { + global: + __vdso_getcpu; + __vdso_clock_getres; + __vdso_clock_gettime; + __vdso_gettimeofday; + __vdso_rt_sigreturn; + + local: *; + }; +} + + +PHDRS { + phdr PT_PHDR FLAGS(4) PHDRS; + load PT_LOAD FLAGS(7) FILEHDR PHDRS; + dynamic PT_DYNAMIC FLAGS(4); + eh_frame_hdr PT_GNU_EH_FRAME; + note PT_NOTE FLAGS(4); +} + +SECTIONS { + /* + * We can't prelink to any address without knowing something about + * the virtual memory space of the host, since that leaks over into + * the available memory space of the guest. + */ + . = SIZEOF_HEADERS; + + /* + * The following, including the FILEHDRS and PHDRS, are modified + * when we relocate the binary. We want them to be initially + * writable for the relocation; we'll force them read-only after. + */ + .note : { *(.note*) } :load :note + .dynamic : { *(.dynamic) } :load :dynamic + .dynsym : { *(.dynsym) } :load + /* + * There ought not be any real read-write data. + * But since we manipulated the segment layout, + * we have to put these sections somewhere. + */ + .data : { + *(.data*) + *(.sdata*) + *(.got.plt) *(.got) + *(.gnu.linkonce.d.*) + *(.bss*) + *(.dynbss*) + *(.gnu.linkonce.b.*) + } + + .rodata : { *(.rodata*) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .eh_frame_hdr : { *(.eh_frame_hdr) } :load :eh_frame_hdr + .eh_frame : { *(.eh_frame) } :load + + .text : { *(.text*) } :load =0xd503201f +} diff --git a/linux-user/loongarch64/vdso.so b/linux-user/loongarch64/vdso.so new file mode 100755 index 0000000000000000000000000000000000000000..bfaa26f2bfe1aaa01d9a349b8b030ef6323e1f8e GIT binary patch literal 3560 zcmc&%|4$r66ra5Rj;76P?0 z*pkLHYK<{!)EKHwLri1P7+ZfbCjMajKaBm!V50tDu=w8YyuH2cp)oP>9lM{I&%8Hp zXLj~8gA7MHeVQiNbm6WE5mF5|x7&qwgpg0iJ4G?pw^^Ls9SZ#nWrv<>Jc~e?al4l|Sbrr}2@GURkPzG*L^O6Z1o0 zZQxR4^6J;qwR=BlPwiV9O)S*Bz9|f2E}l&r6A9CtoKB?29A1Vu*s!Ds2$jnW)gtYFeN| zw>bJ9NW=J%e$Dv}pkqi2l>63vMIiy&S@4|!eIB;WL6Hf?87P^7(iCi;g0dIDpM>&p zsEEUkQP?>GmCpm5hF#CW9mBBu8Q3!jcShl^eyHk$y%D(k1O&RFx)bi{fP35Ez7W*h zkWl-Tg!{jg@W2-m>eeJY_?d)#*Cagjsf7Kj5+1%P;lLFM^&d-USdnn>BMHF|B|P%J zgvR$IG`%C?(AyFoeM>^~n-UJcA>pytBpkUYq2*Nxk6)1R#Il5==Oi3klJMlMML52( z2v7a7XwT0Ej{O*3{0;c8oWtc+FKUE{_`iE4SN`5rUdj33Z0r68yRN3OAn5oGS#RAA zxJNnqpU>-#4(9V)=|YjCv-$qTj^5_;C64aq^QEGY{^|IovTo-4mpS`TKL77@N<+O^ zQtOucA&szjr7z6aS%us49#Q^Y-RwoakPgNa@6|8bq{!c^gXb0R)y;Pm@72d^$QRN< zVRW48Ywhi67e;^dWP7+{C>l0UD4uTZ3LAs1ZIQ5n+JZZ+5$-lRqpiJRqq`$2jIPMZ zw$_Mova@p_JeYIbQqTTIrj!agisuqHha-K=)30E5VcLw1ieT2v*qG!}Ds3i2FustA zO;3(l1@uLXnK2QZNX-V5v24=j&5X^=B{JE`bSkf6#6>WXG{!Tr>4cGtqbJuy&`ivm zIHCW61+Wj=b9mq9?~>78U%5oIuehxiE7rcFNM5&$d|u;yjrTCtW7>b|EW`Qvyu-Mz z`lWcv&GCGWIfNX2Dkx5t@p@-uf7R^7Gf#R)doSnb^BE(xlX3qX$MevFZNKALiR1Y^ z$GBCz)hXWERNoxW=RZb1@3Fr}7Q_i`*mUJ*y<+5jlAoXRFg}a%boS#q>l!1CkBrv{ zkKbm67{>xV6UTec;nnP@kmrqQTE^~pKF={Wdj{;yk6V`&&$7tpK1QB@&gbbaFE{%T t^8Y9M)xhXH4|s8YUVn6E Date: Mon, 14 Aug 2023 17:58:55 -0700 Subject: [PATCH 222/974] linux-user/ppc: Add vdso Add support in gen-vdso-elfn.c.inc for the DT_PPC64_OPT dynamic tag: this is an integer, so does not need relocation. Signed-off-by: Richard Henderson --- linux-user/elfload.c | 8 ++ linux-user/gen-vdso-elfn.c.inc | 7 + linux-user/ppc/Makefile.vdso | 20 +++ linux-user/ppc/meson.build | 12 ++ linux-user/ppc/signal.c | 31 +++-- linux-user/ppc/vdso-32.ld | 70 ++++++++++ linux-user/ppc/vdso-32.so | Bin 0 -> 3020 bytes linux-user/ppc/vdso-64.ld | 68 +++++++++ linux-user/ppc/vdso-64.so | Bin 0 -> 3896 bytes linux-user/ppc/vdso-64le.so | Bin 0 -> 3896 bytes linux-user/ppc/vdso-asmoffset.h | 20 +++ linux-user/ppc/vdso.S | 239 ++++++++++++++++++++++++++++++++ 12 files changed, 467 insertions(+), 8 deletions(-) create mode 100644 linux-user/ppc/Makefile.vdso create mode 100644 linux-user/ppc/vdso-32.ld create mode 100755 linux-user/ppc/vdso-32.so create mode 100644 linux-user/ppc/vdso-64.ld create mode 100755 linux-user/ppc/vdso-64.so create mode 100755 linux-user/ppc/vdso-64le.so create mode 100644 linux-user/ppc/vdso-asmoffset.h create mode 100644 linux-user/ppc/vdso.S diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 4e6e0059e6..26602516aa 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1187,6 +1187,14 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUPPCState *en #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 +#ifndef TARGET_PPC64 +# define VDSO_HEADER "vdso-32.c.inc" +#elif TARGET_BIG_ENDIAN +# define VDSO_HEADER "vdso-64.c.inc" +#else +# define VDSO_HEADER "vdso-64le.c.inc" +#endif + #endif #ifdef TARGET_LOONGARCH64 diff --git a/linux-user/gen-vdso-elfn.c.inc b/linux-user/gen-vdso-elfn.c.inc index 7034c36d5e..95856eb839 100644 --- a/linux-user/gen-vdso-elfn.c.inc +++ b/linux-user/gen-vdso-elfn.c.inc @@ -273,7 +273,14 @@ static void elfN(process)(FILE *outf, void *buf, bool need_bswap) errors++; break; + case PT_LOPROC + 3: + if (ehdr->e_machine == EM_PPC64) { + break; /* DT_PPC64_OPT: integer bitmask */ + } + goto do_default; + default: + do_default: /* This is probably something target specific. */ fprintf(stderr, "VDSO has unknown DYNAMIC entry (%lx)\n", (unsigned long)tag); diff --git a/linux-user/ppc/Makefile.vdso b/linux-user/ppc/Makefile.vdso new file mode 100644 index 0000000000..3ca3c6b83e --- /dev/null +++ b/linux-user/ppc/Makefile.vdso @@ -0,0 +1,20 @@ +include $(BUILD_DIR)/tests/tcg/ppc64-linux-user/config-target.mak + +SUBDIR = $(SRC_PATH)/linux-user/ppc +VPATH += $(SUBDIR) + +all: $(SUBDIR)/vdso-32.so $(SUBDIR)/vdso-64.so $(SUBDIR)/vdso-64le.so + +LDFLAGS32 = -nostdlib -shared -Wl,-T,$(SUBDIR)/vdso-32.ld \ + -Wl,-h,linux-vdso32.so.1 -Wl,--hash-style=both -Wl,--build-id=sha1 +LDFLAGS64 = -nostdlib -shared -Wl,-T,$(SUBDIR)/vdso-64.ld \ + -Wl,-h,linux-vdso64.so.1 -Wl,--hash-style=both -Wl,--build-id=sha1 + +$(SUBDIR)/vdso-32.so: vdso.S vdso-32.ld vdso-asmoffset.h + $(CC) -o $@ $(LDFLAGS32) -m32 $< + +$(SUBDIR)/vdso-64.so: vdso.S vdso-64.ld vdso-asmoffset.h + $(CC) -o $@ $(LDFLAGS64) -mbig-endian $< + +$(SUBDIR)/vdso-64le.so: vdso.S vdso-64.ld vdso-asmoffset.h + $(CC) -o $@ $(LDFLAGS64) -mlittle-endian $< diff --git a/linux-user/ppc/meson.build b/linux-user/ppc/meson.build index 19fead7bc8..80cacae396 100644 --- a/linux-user/ppc/meson.build +++ b/linux-user/ppc/meson.build @@ -3,3 +3,15 @@ syscall_nr_generators += { arguments: [ meson.current_source_dir() / 'syscallhdr.sh', '@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@' ], output: '@BASENAME@_nr.h') } + +vdso_32_inc = gen_vdso.process('vdso-32.so', extra_args: [ + '-s', '__kernel_sigtramp32', + '-r', '__kernel_sigtramp_rt32' + ]) +linux_user_ss.add(when: 'TARGET_PPC', if_true: vdso_32_inc) + +vdso_64_inc = gen_vdso.process('vdso-64.so', + extra_args: ['-r', '__kernel_sigtramp_rt64']) +vdso_64le_inc = gen_vdso.process('vdso-64le.so', + extra_args: ['-r', '__kernel_sigtramp_rt64']) +linux_user_ss.add(when: 'TARGET_PPC64', if_true: [vdso_64_inc, vdso_64le_inc]) diff --git a/linux-user/ppc/signal.c b/linux-user/ppc/signal.c index a616f20efb..7e7302823b 100644 --- a/linux-user/ppc/signal.c +++ b/linux-user/ppc/signal.c @@ -21,14 +21,7 @@ #include "user-internals.h" #include "signal-common.h" #include "linux-user/trace.h" - -/* Size of dummy stack frame allocated when calling signal handler. - See arch/powerpc/include/asm/ptrace.h. */ -#if defined(TARGET_PPC64) -#define SIGNAL_FRAMESIZE 128 -#else -#define SIGNAL_FRAMESIZE 64 -#endif +#include "vdso-asmoffset.h" /* See arch/powerpc/include/asm/ucontext.h. Only used for 32-bit PPC; on 64-bit PPC, sigcontext and mcontext are one and the same. */ @@ -73,6 +66,16 @@ struct target_mcontext { #endif }; +QEMU_BUILD_BUG_ON(offsetof(struct target_mcontext, mc_fregs) + != offsetof_mcontext_fregs); +#if defined(TARGET_PPC64) +QEMU_BUILD_BUG_ON(offsetof(struct target_mcontext, v_regs) + != offsetof_mcontext_vregs_ptr); +#else +QEMU_BUILD_BUG_ON(offsetof(struct target_mcontext, mc_vregs) + != offsetof_mcontext_vregs); +#endif + /* See arch/powerpc/include/asm/sigcontext.h. */ struct target_sigcontext { target_ulong _unused[4]; @@ -161,6 +164,7 @@ struct target_ucontext { #endif }; +#if !defined(TARGET_PPC64) /* See arch/powerpc/kernel/signal_32.c. */ struct target_sigframe { struct target_sigcontext sctx; @@ -168,6 +172,10 @@ struct target_sigframe { int32_t abigap[56]; }; +QEMU_BUILD_BUG_ON(offsetof(struct target_sigframe, mctx) + != offsetof_sigframe_mcontext); +#endif + #if defined(TARGET_PPC64) #define TARGET_TRAMP_SIZE 6 @@ -184,6 +192,10 @@ struct target_rt_sigframe { char abigap[288]; } __attribute__((aligned(16))); +QEMU_BUILD_BUG_ON(offsetof(struct target_rt_sigframe, + uc.tuc_sigcontext.mcontext) + != offsetof_rt_sigframe_mcontext); + #else struct target_rt_sigframe { @@ -192,6 +204,9 @@ struct target_rt_sigframe { int32_t abigap[56]; }; +QEMU_BUILD_BUG_ON(offsetof(struct target_rt_sigframe, uc.tuc_mcontext) + != offsetof_rt_sigframe_mcontext); + #endif #if defined(TARGET_PPC64) diff --git a/linux-user/ppc/vdso-32.ld b/linux-user/ppc/vdso-32.ld new file mode 100644 index 0000000000..6962696540 --- /dev/null +++ b/linux-user/ppc/vdso-32.ld @@ -0,0 +1,70 @@ +/* + * Linker script for linux powerpc64 replacement vdso. + * + * Copyright 2023 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +VERSION { + LINUX_2.6.15 { + global: + __kernel_gettimeofday; + __kernel_clock_gettime; + __kernel_clock_gettime64; + __kernel_clock_getres; + __kernel_time; + __kernel_sync_dicache; + __kernel_sigtramp32; + __kernel_sigtramp_rt32; + __kernel_getcpu; + local: *; + }; +} + +PHDRS { + phdr PT_PHDR FLAGS(4) PHDRS; + load PT_LOAD FLAGS(7) FILEHDR PHDRS; /* FLAGS=RWX */ + dynamic PT_DYNAMIC FLAGS(4); + eh_frame_hdr PT_GNU_EH_FRAME; + note PT_NOTE FLAGS(4); +} + +SECTIONS { + . = SIZEOF_HEADERS; + + /* + * The following, including the FILEHDRS and PHDRS, are modified + * when we relocate the binary. We want them to be initially + * writable for the relocation; we'll force them read-only after. + */ + .note : { *(.note*) } :load :note + .dynamic : { *(.dynamic) } :load :dynamic + .dynsym : { *(.dynsym) } :load + .data : { + /* + * There ought not be any real read-write data. + * But since we manipulated the segment layout, + * we have to put these sections somewhere. + */ + *(.data*) + *(.sdata*) + *(.got.plt) *(.got) + *(.gnu.linkonce.d.*) + *(.bss*) + *(.dynbss*) + *(.gnu.linkonce.b.*) + } + + .rodata : { *(.rodata*) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .eh_frame_hdr : { *(.eh_frame_hdr) } :load :eh_frame_hdr + .eh_frame : { *(.eh_frame) } :load + + .text : { *(.text*) } :load +} diff --git a/linux-user/ppc/vdso-32.so b/linux-user/ppc/vdso-32.so new file mode 100755 index 0000000000000000000000000000000000000000..b19baafb0d38e15b4a24def5c44a6d684714be45 GIT binary patch literal 3020 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 WQjSr;8y}D^=0BklzBu&{{(k}Ee8OG; literal 0 HcmV?d00001 diff --git a/linux-user/ppc/vdso-asmoffset.h b/linux-user/ppc/vdso-asmoffset.h new file mode 100644 index 0000000000..6844c8c81c --- /dev/null +++ b/linux-user/ppc/vdso-asmoffset.h @@ -0,0 +1,20 @@ +/* + * Size of dummy stack frame allocated when calling signal handler. + * See arch/powerpc/include/asm/ptrace.h. + */ +#ifdef TARGET_ABI32 +# define SIGNAL_FRAMESIZE 64 +#else +# define SIGNAL_FRAMESIZE 128 +#endif + +#ifdef TARGET_ABI32 +# define offsetof_sigframe_mcontext 0x20 +# define offsetof_rt_sigframe_mcontext 0x140 +# define offsetof_mcontext_fregs 0xc0 +# define offsetof_mcontext_vregs 0x1d0 +#else +# define offsetof_rt_sigframe_mcontext 0xe8 +# define offsetof_mcontext_fregs 0x180 +# define offsetof_mcontext_vregs_ptr 0x288 +#endif diff --git a/linux-user/ppc/vdso.S b/linux-user/ppc/vdso.S new file mode 100644 index 0000000000..689010db13 --- /dev/null +++ b/linux-user/ppc/vdso.S @@ -0,0 +1,239 @@ +/* + * PowerPC linux replacement vdso. + * + * Copyright 2023 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include + +#ifndef _ARCH_PPC64 +# define TARGET_ABI32 +#endif +#include "vdso-asmoffset.h" + + + .text + +.macro endf name + .globl \name + .size \name, .-\name + /* For PPC64, functions have special linkage; we export pointers. */ +#ifndef _ARCH_PPC64 + .type \name, @function +#endif +.endm + +.macro raw_syscall nr + addi 0, 0, \nr + sc +.endm + +.macro vdso_syscall name, nr +\name: + raw_syscall \nr + blr +endf \name +.endm + + .cfi_startproc + +vdso_syscall __kernel_gettimeofday, __NR_gettimeofday +vdso_syscall __kernel_clock_gettime, __NR_clock_gettime +vdso_syscall __kernel_clock_getres, __NR_clock_getres +vdso_syscall __kernel_getcpu, __NR_getcpu +vdso_syscall __kernel_time, __NR_time + +#ifdef __NR_clock_gettime64 +vdso_syscall __kernel_clock_gettime64, __NR_clock_gettime64 +#endif + +__kernel_sync_dicache: + /* qemu does not need to flush caches */ + blr +endf __kernel_sync_dicache + + .cfi_endproc + +/* + * TODO: __kernel_get_tbfreq + * This is probably a constant for QEMU. + */ + +/* + * Start the unwind info at least one instruction before the signal + * trampoline, because the unwinder will assume we are returning + * after a call site. + */ + + .cfi_startproc simple + .cfi_signal_frame + +#ifdef _ARCH_PPC64 +# define __kernel_sigtramp_rt __kernel_sigtramp_rt64 +# define sizeof_reg 8 +#else +# define __kernel_sigtramp_rt __kernel_sigtramp_rt32 +# define sizeof_reg 4 +#endif +#define sizeof_freg 8 +#define sizeof_vreg 16 + + .cfi_def_cfa 1, SIGNAL_FRAMESIZE + offsetof_rt_sigframe_mcontext + + /* Return address */ + .cfi_return_column 67 + .cfi_offset 67, 32 * sizeof_reg /* nip */ + + /* Integer registers */ + .cfi_offset 0, 0 * sizeof_reg + .cfi_offset 1, 1 * sizeof_reg + .cfi_offset 2, 2 * sizeof_reg + .cfi_offset 3, 3 * sizeof_reg + .cfi_offset 4, 4 * sizeof_reg + .cfi_offset 5, 5 * sizeof_reg + .cfi_offset 6, 6 * sizeof_reg + .cfi_offset 7, 7 * sizeof_reg + .cfi_offset 8, 8 * sizeof_reg + .cfi_offset 9, 9 * sizeof_reg + .cfi_offset 10, 10 * sizeof_reg + .cfi_offset 11, 11 * sizeof_reg + .cfi_offset 12, 12 * sizeof_reg + .cfi_offset 13, 13 * sizeof_reg + .cfi_offset 14, 14 * sizeof_reg + .cfi_offset 15, 15 * sizeof_reg + .cfi_offset 16, 16 * sizeof_reg + .cfi_offset 17, 17 * sizeof_reg + .cfi_offset 18, 18 * sizeof_reg + .cfi_offset 19, 19 * sizeof_reg + .cfi_offset 20, 20 * sizeof_reg + .cfi_offset 21, 21 * sizeof_reg + .cfi_offset 22, 22 * sizeof_reg + .cfi_offset 23, 23 * sizeof_reg + .cfi_offset 24, 24 * sizeof_reg + .cfi_offset 25, 25 * sizeof_reg + .cfi_offset 26, 26 * sizeof_reg + .cfi_offset 27, 27 * sizeof_reg + .cfi_offset 28, 28 * sizeof_reg + .cfi_offset 29, 29 * sizeof_reg + .cfi_offset 30, 30 * sizeof_reg + .cfi_offset 31, 31 * sizeof_reg + .cfi_offset 65, 36 * sizeof_reg /* lr */ + .cfi_offset 70, 38 * sizeof_reg /* ccr */ + + /* Floating point registers */ + .cfi_offset 32, offsetof_mcontext_fregs + .cfi_offset 33, offsetof_mcontext_fregs + 1 * sizeof_freg + .cfi_offset 34, offsetof_mcontext_fregs + 2 * sizeof_freg + .cfi_offset 35, offsetof_mcontext_fregs + 3 * sizeof_freg + .cfi_offset 36, offsetof_mcontext_fregs + 4 * sizeof_freg + .cfi_offset 37, offsetof_mcontext_fregs + 5 * sizeof_freg + .cfi_offset 38, offsetof_mcontext_fregs + 6 * sizeof_freg + .cfi_offset 39, offsetof_mcontext_fregs + 7 * sizeof_freg + .cfi_offset 40, offsetof_mcontext_fregs + 8 * sizeof_freg + .cfi_offset 41, offsetof_mcontext_fregs + 9 * sizeof_freg + .cfi_offset 42, offsetof_mcontext_fregs + 10 * sizeof_freg + .cfi_offset 43, offsetof_mcontext_fregs + 11 * sizeof_freg + .cfi_offset 44, offsetof_mcontext_fregs + 12 * sizeof_freg + .cfi_offset 45, offsetof_mcontext_fregs + 13 * sizeof_freg + .cfi_offset 46, offsetof_mcontext_fregs + 14 * sizeof_freg + .cfi_offset 47, offsetof_mcontext_fregs + 15 * sizeof_freg + .cfi_offset 48, offsetof_mcontext_fregs + 16 * sizeof_freg + .cfi_offset 49, offsetof_mcontext_fregs + 17 * sizeof_freg + .cfi_offset 50, offsetof_mcontext_fregs + 18 * sizeof_freg + .cfi_offset 51, offsetof_mcontext_fregs + 19 * sizeof_freg + .cfi_offset 52, offsetof_mcontext_fregs + 20 * sizeof_freg + .cfi_offset 53, offsetof_mcontext_fregs + 21 * sizeof_freg + .cfi_offset 54, offsetof_mcontext_fregs + 22 * sizeof_freg + .cfi_offset 55, offsetof_mcontext_fregs + 23 * sizeof_freg + .cfi_offset 56, offsetof_mcontext_fregs + 24 * sizeof_freg + .cfi_offset 57, offsetof_mcontext_fregs + 25 * sizeof_freg + .cfi_offset 58, offsetof_mcontext_fregs + 26 * sizeof_freg + .cfi_offset 59, offsetof_mcontext_fregs + 27 * sizeof_freg + .cfi_offset 60, offsetof_mcontext_fregs + 28 * sizeof_freg + .cfi_offset 61, offsetof_mcontext_fregs + 29 * sizeof_freg + .cfi_offset 62, offsetof_mcontext_fregs + 30 * sizeof_freg + .cfi_offset 63, offsetof_mcontext_fregs + 31 * sizeof_freg + + /* + * Unlike the kernel, unconditionally represent the Altivec/VSX regs. + * The space within the stack frame is always available, and most of + * our supported processors have them enabled. The only complication + * for PPC64 is the misalignment, so that we have to use indirection. + */ +.macro save_vreg_ofs reg, ofs +#ifdef _ARCH_PPC64 + /* + * vreg = *(cfa + offsetof(v_regs)) + ofs + * + * The CFA is input to the expression on the stack, so: + * DW_CFA_expression reg, length (7), + * DW_OP_plus_uconst (0x23), vreg_ptr, DW_OP_deref (0x06), + * DW_OP_plus_uconst (0x23), ofs + */ + .cfi_escape 0x10, 77 + \reg, 7, 0x23, (offsetof_mcontext_vregs_ptr & 0x7f) + 0x80, offsetof_mcontext_vregs_ptr >> 7, 0x06, 0x23, (\ofs & 0x7f) | 0x80, \ofs >> 7 +#else + .cfi_offset 77 + \reg, offsetof_mcontext_vregs + \ofs +#endif +.endm + +.macro save_vreg reg + save_vreg_ofs \reg, (\reg * sizeof_vreg) +.endm + + save_vreg 0 + save_vreg 1 + save_vreg 2 + save_vreg 3 + save_vreg 4 + save_vreg 5 + save_vreg 6 + save_vreg 7 + save_vreg 8 + save_vreg 9 + save_vreg 10 + save_vreg 11 + save_vreg 12 + save_vreg 13 + save_vreg 14 + save_vreg 15 + save_vreg 16 + save_vreg 17 + save_vreg 18 + save_vreg 19 + save_vreg 20 + save_vreg 21 + save_vreg 22 + save_vreg 23 + save_vreg 24 + save_vreg 25 + save_vreg 26 + save_vreg 27 + save_vreg 28 + save_vreg 29 + save_vreg 30 + save_vreg 31 + save_vreg 32 + save_vreg_ofs 33, (32 * sizeof_vreg + 12) + + nop + +__kernel_sigtramp_rt: + raw_syscall __NR_rt_sigreturn +endf __kernel_sigtramp_rt + +#ifndef _ARCH_PPC64 + /* + * The non-rt sigreturn has the same layout at a different offset. + * Move the CFA and leave all othe other descriptions the same. + */ + .cfi_def_cfa 1, SIGNAL_FRAMESIZE + offsetof_sigframe_mcontext + nop +__kernel_sigtramp32: + raw_syscall __NR_sigreturn +endf __kernel_sigtramp32 +#endif + + .cfi_endproc From ba02f1ea63baf262dae1d58cc3d2c577bea2c429 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 14 Aug 2023 18:26:05 -0700 Subject: [PATCH 223/974] linux-user/s390x: Rename __SIGNAL_FRAMESIZE to STACK_FRAME_OVERHEAD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/s390x/signal.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/linux-user/s390x/signal.c b/linux-user/s390x/signal.c index f72165576f..0f8b8e04bf 100644 --- a/linux-user/s390x/signal.c +++ b/linux-user/s390x/signal.c @@ -26,7 +26,8 @@ #define __NUM_FPRS 16 #define __NUM_ACRS 16 -#define __SIGNAL_FRAMESIZE 160 /* FIXME: 31-bit mode -> 96 */ +/* Minimum stack frame size */ +#define STACK_FRAME_OVERHEAD 160 #define _SIGCONTEXT_NSIG 64 #define _SIGCONTEXT_NSIG_BPW 64 /* FIXME: 31-bit mode -> 32 */ @@ -63,7 +64,7 @@ typedef struct { } target_sigcontext; typedef struct { - uint8_t callee_used_stack[__SIGNAL_FRAMESIZE]; + uint8_t callee_used_stack[STACK_FRAME_OVERHEAD]; target_sigcontext sc; target_sigregs sregs; int signo; @@ -83,7 +84,7 @@ struct target_ucontext { }; typedef struct { - uint8_t callee_used_stack[__SIGNAL_FRAMESIZE]; + uint8_t callee_used_stack[STACK_FRAME_OVERHEAD]; /* * This field is no longer initialized by the kernel, but it's still a part * of the ABI. From b63c6b97f8f5d357f383d8227316733c758c7ddd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 14 Aug 2023 18:56:17 -0700 Subject: [PATCH 224/974] linux-user/s390x: Add vdso MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Acked-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/elfload.c | 2 + linux-user/s390x/Makefile.vdso | 11 +++++ linux-user/s390x/meson.build | 6 +++ linux-user/s390x/signal.c | 4 +- linux-user/s390x/vdso-asmoffset.h | 2 + linux-user/s390x/vdso.S | 61 +++++++++++++++++++++++++ linux-user/s390x/vdso.ld | 72 ++++++++++++++++++++++++++++++ linux-user/s390x/vdso.so | Bin 0 -> 3464 bytes 8 files changed, 155 insertions(+), 3 deletions(-) create mode 100644 linux-user/s390x/Makefile.vdso create mode 100644 linux-user/s390x/vdso-asmoffset.h create mode 100644 linux-user/s390x/vdso.S create mode 100644 linux-user/s390x/vdso.ld create mode 100755 linux-user/s390x/vdso.so diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 26602516aa..a1583883fa 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1894,6 +1894,8 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 +#define VDSO_HEADER "vdso.c.inc" + #endif /* TARGET_S390X */ #ifdef TARGET_RISCV diff --git a/linux-user/s390x/Makefile.vdso b/linux-user/s390x/Makefile.vdso new file mode 100644 index 0000000000..e82bf9e29f --- /dev/null +++ b/linux-user/s390x/Makefile.vdso @@ -0,0 +1,11 @@ +include $(BUILD_DIR)/tests/tcg/s390x-linux-user/config-target.mak + +SUBDIR = $(SRC_PATH)/linux-user/s390x +VPATH += $(SUBDIR) + +all: $(SUBDIR)/vdso.so + +$(SUBDIR)/vdso.so: vdso.S vdso.ld vdso-asmoffset.h + $(CC) -o $@ -nostdlib -shared -Wl,-h,linux-vdso64.so.1 \ + -Wl,--build-id=sha1 -Wl,--hash-style=both \ + -Wl,-T,$(SUBDIR)/vdso.ld $< diff --git a/linux-user/s390x/meson.build b/linux-user/s390x/meson.build index 0781ccea1d..a7a25ed9ce 100644 --- a/linux-user/s390x/meson.build +++ b/linux-user/s390x/meson.build @@ -3,3 +3,9 @@ syscall_nr_generators += { arguments: [ meson.current_source_dir() / 'syscallhdr.sh', '@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@' ], output: '@BASENAME@_nr.h') } + +vdso_inc = gen_vdso.process('vdso.so', extra_args: [ + '-s', '__kernel_sigreturn', + '-r', '__kernel_rt_sigreturn' + ]) +linux_user_ss.add(when: 'TARGET_S390X', if_true: vdso_inc) diff --git a/linux-user/s390x/signal.c b/linux-user/s390x/signal.c index 0f8b8e04bf..b40f738a70 100644 --- a/linux-user/s390x/signal.c +++ b/linux-user/s390x/signal.c @@ -21,14 +21,12 @@ #include "user-internals.h" #include "signal-common.h" #include "linux-user/trace.h" +#include "vdso-asmoffset.h" #define __NUM_GPRS 16 #define __NUM_FPRS 16 #define __NUM_ACRS 16 -/* Minimum stack frame size */ -#define STACK_FRAME_OVERHEAD 160 - #define _SIGCONTEXT_NSIG 64 #define _SIGCONTEXT_NSIG_BPW 64 /* FIXME: 31-bit mode -> 32 */ #define _SIGCONTEXT_NSIG_WORDS (_SIGCONTEXT_NSIG / _SIGCONTEXT_NSIG_BPW) diff --git a/linux-user/s390x/vdso-asmoffset.h b/linux-user/s390x/vdso-asmoffset.h new file mode 100644 index 0000000000..27a062d6c1 --- /dev/null +++ b/linux-user/s390x/vdso-asmoffset.h @@ -0,0 +1,2 @@ +/* Minimum stack frame size */ +#define STACK_FRAME_OVERHEAD 160 diff --git a/linux-user/s390x/vdso.S b/linux-user/s390x/vdso.S new file mode 100644 index 0000000000..3332492477 --- /dev/null +++ b/linux-user/s390x/vdso.S @@ -0,0 +1,61 @@ +/* + * s390x linux replacement vdso. + * + * Copyright 2023 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include "vdso-asmoffset.h" + +.macro endf name + .globl \name + .type \name, @function + .size \name, . - \name +.endm + +.macro raw_syscall n + .ifne \n < 0x100 + svc \n + .else + lghi %r1, \n + svc 0 + .endif +.endm + +.macro vdso_syscall name, nr +\name: + .cfi_startproc + aghi %r15, -(STACK_FRAME_OVERHEAD + 16) + .cfi_adjust_cfa_offset STACK_FRAME_OVERHEAD + 16 + stg %r14, STACK_FRAME_OVERHEAD(%r15) + .cfi_rel_offset %r14, STACK_FRAME_OVERHEAD + raw_syscall \nr + lg %r14, STACK_FRAME_OVERHEAD(%r15) + aghi %r15, STACK_FRAME_OVERHEAD + 16 + .cfi_restore %r14 + .cfi_adjust_cfa_offset -(STACK_FRAME_OVERHEAD + 16) + br %r14 + .cfi_endproc +endf \name +.endm + +vdso_syscall __kernel_gettimeofday, __NR_gettimeofday +vdso_syscall __kernel_clock_gettime, __NR_clock_gettime +vdso_syscall __kernel_clock_getres, __NR_clock_getres +vdso_syscall __kernel_getcpu, __NR_getcpu + +/* + * TODO unwind info, though we're ok without it. + * The kernel supplies bogus empty unwind info, and it is likely ignored + * by all users. Without it we get the fallback signal frame handling. + */ + +__kernel_sigreturn: + raw_syscall __NR_sigreturn +endf __kernel_sigreturn + +__kernel_rt_sigreturn: + raw_syscall __NR_rt_sigreturn +endf __kernel_rt_sigreturn diff --git a/linux-user/s390x/vdso.ld b/linux-user/s390x/vdso.ld new file mode 100644 index 0000000000..d3f1d1b164 --- /dev/null +++ b/linux-user/s390x/vdso.ld @@ -0,0 +1,72 @@ +/* + * Linker script for linux s390x replacement vdso. + * + * Copyright 2023 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +VERSION { + LINUX_2.6.29 { + global: + __kernel_gettimeofday; + __kernel_clock_gettime; + __kernel_clock_getres; + __kernel_getcpu; + __kernel_rt_sigreturn; + __kernel_sigreturn; + /* + * QEMU handles syscall restart internally, so we don't + * need the __kernel_restart_syscall entry point. + */ + local: *; + }; +} + + +PHDRS { + phdr PT_PHDR FLAGS(4) PHDRS; + load PT_LOAD FLAGS(7) FILEHDR PHDRS; /* FLAGS=RWX */ + dynamic PT_DYNAMIC FLAGS(4); + eh_frame_hdr PT_GNU_EH_FRAME; + note PT_NOTE FLAGS(4); +} + +SECTIONS { + . = SIZEOF_HEADERS; + + /* + * The following, including the FILEHDRS and PHDRS, are modified + * when we relocate the binary. We want them to be initially + * writable for the relocation; we'll force them read-only after. + */ + .note : { *(.note*) } :load :note + .dynamic : { *(.dynamic) } :load :dynamic + .dynsym : { *(.dynsym) } :load + /* + * There ought not be any real read-write data. + * But since we manipulated the segment layout, + * we have to put these sections somewhere. + */ + .data : { + *(.data*) + *(.sdata*) + *(.got.plt) *(.got) + *(.gnu.linkonce.d.*) + *(.bss*) + *(.dynbss*) + *(.gnu.linkonce.b.*) + } + + .rodata : { *(.rodata*) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .eh_frame_hdr : { *(.eh_frame_hdr) } :load :eh_frame_hdr + .eh_frame : { *(.eh_frame) } :load + + .text : { *(.text*) } :load +} diff --git a/linux-user/s390x/vdso.so b/linux-user/s390x/vdso.so new file mode 100755 index 0000000000000000000000000000000000000000..64130f6f335269b03291653d006225b365be9cd9 GIT binary patch literal 3464 zcmcguUuaup6hAlrn#R#}B6HHJ6^h8v3s#wtDI{HzWs-HJcB~u1U2JZX*fw`0w>2I9 z>4SqkSsXs>VMq|PDvEm&1Rwf1RK!O?(CHKhzDz`4)-9g%edpXip_vbYU-<6tJHKy{lI$0|t_^w-9%V;CvX_ZVV@~UP z5ysh1P<-w|K6-fSIDGtl`ke<)|Mc0}bJcWb@yj2!fBy7s_8}PJ^{b5Jk0Yw$Io;cT zvAm@bv#$o5>1TjUWL@)+V!Xt77o&O(PPe!{_5F56Dq|HugaG54H=!y#f0xtl;=^ z{(|Oe9c6~jI}MdzM5-AY|8oAd5niPp<$=q-bPi}udYXw&GFgmg;C+@!7ys^{eeuHj z*T4PgtbX)Xd*k=9hda69Q>Wkk@x$ybu&hP9*|3+as_nYLm7yGF<^c5IqMK$gD%EM^dJJ&HRX_=UlHe3oHd-= zIM1UMJ^v4I(xAP;I6DJId&n5swY$R)f#T6@HFFH}{sl84Bv3LERZ;$NK58 z{sq)6-9i5f>gc{kWqnFM;93k>t zPf{jYi+obAVwW^}{2=pE|4KxA?w|60@a(yd##JBHtcojrD!7kfczq<42ikk5{qOhf zLwR_@?>o{q)cHH)^P&ED(&rPwe&_gnsNbFI)9)_$_Mtxep3ldxa1KVn%AK6b9zAjh zOC8^LR=#KzX0k`~RxvjN*5T5z$!y6wRwz85pS5PQlcl`n_k`ZI;q{zQa|v~hox4`3 zdw3>(zwsz90(Cq9v$eMDmS044n;L7fYB;V9W@W8WUarrp1==E(-7=Wf#)?@hw`!Vq zo0?g*o2|Oj2nkjNOuJ?+G|S7jRjZ(-NMO2l+m)mAUvLDS<$WUcH>WrB>T|p&K(J6A zLY*8iECCvFZapMYV$hoF3cmya_T!ZA42~~#E(lv_L;l=1Jb$XEi2%>>r49tu`x(u= zA@PZzo*QQk$Cth#sCp{ztt`c#Fpb|mfA{!rX*tRW8I*VOC$q_bj^j&u1tHXb&|dOD z{?B3jE-i3~KihkKi}DYWds%Kr-5;5sp;iO3^}^sP;K oq#^O?{L#B0m|N2GNEUhVg)ofre@WbfvycCv+~*tYC*b-21AbI{0ssI2 literal 0 HcmV?d00001 From 5d94c2ffa813d07678c59dea516be5153f85cb2f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 16 Aug 2023 10:54:57 -0700 Subject: [PATCH 225/974] linux-user: Show vdso address in /proc/pid/maps Tested-by: Helge Deller Reviewed-by: Ilya Leoshkevich Signed-off-by: Richard Henderson --- linux-user/elfload.c | 1 + linux-user/qemu.h | 1 + linux-user/syscall.c | 2 ++ 3 files changed, 4 insertions(+) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index a1583883fa..46832358b0 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -3919,6 +3919,7 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) const VdsoImageInfo *vdso = vdso_image_info(); if (vdso) { load_elf_vdso(&vdso_info, vdso); + info->vdso = vdso_info.load_bias; } else if (TARGET_ARCH_HAS_SIGTRAMP_PAGE) { abi_long tramp_page = target_mmap(0, TARGET_PAGE_SIZE, PROT_READ | PROT_WRITE, diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 12f638336a..4de9ec783f 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -32,6 +32,7 @@ struct image_info { abi_ulong brk; abi_ulong start_stack; abi_ulong stack_limit; + abi_ulong vdso; abi_ulong entry; abi_ulong code_offset; abi_ulong data_offset; diff --git a/linux-user/syscall.c b/linux-user/syscall.c index d49cd314a2..65ac3ac796 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -7992,6 +7992,8 @@ static void open_self_maps_4(const struct open_self_maps_data *d, path = "[stack]"; } else if (start == info->brk) { path = "[heap]"; + } else if (start == info->vdso) { + path = "[vdso]"; } /* Except null device (MAP_ANON), adjust offset for this fragment. */ From 335b8f700c42a011cf2855c47bf098be3d35bde4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Sep 2023 13:43:50 -0700 Subject: [PATCH 226/974] build: Add update-linux-vdso makefile rule MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is not ideal, since it requires all cross-compilers to be present rather than a simple subset. But since it is only run manually, should be good enough for now. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- Makefile | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Makefile b/Makefile index bfc4b2c8e9..676a4a54f4 100644 --- a/Makefile +++ b/Makefile @@ -283,6 +283,13 @@ include $(SRC_PATH)/tests/vm/Makefile.include print-help-run = printf " %-30s - %s\\n" "$1" "$2" print-help = @$(call print-help-run,$1,$2) +.PHONY: update-linux-vdso +update-linux-vdso: + @for m in $(SRC_PATH)/linux-user/*/Makefile.vdso; do \ + $(MAKE) $(SUBDIR_MAKEFLAGS) -C $$(dirname $$m) -f Makefile.vdso \ + SRC_PATH=$(SRC_PATH) BUILD_DIR=$(BUILD_DIR); \ + done + .PHONY: help help: @echo 'Generic targets:' @@ -303,6 +310,9 @@ endif $(call print-help,distclean,Remove all generated files) $(call print-help,dist,Build a distributable tarball) @echo '' + @echo 'Linux-user targets:' + $(call print-help,update-linux-vdso,Build linux-user vdso images) + @echo '' @echo 'Test targets:' $(call print-help,check,Run all tests (check-help for details)) $(call print-help,bench,Run all benchmarks) From bf1695c2523510ff8945d88c971b25baac66543d Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 18 Oct 2023 13:55:09 +0200 Subject: [PATCH 227/974] qemu-iotests: Filter warnings about block migration being deprecated Create a new filter that removes the two warnings for test 183. Reviewed-by: Hanna Czenczek Signed-off-by: Juan Quintela Message-ID: <20231018115513.2163-2-quintela@redhat.com> --- tests/qemu-iotests/183 | 2 +- tests/qemu-iotests/common.filter | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/qemu-iotests/183 b/tests/qemu-iotests/183 index ee62939e72..b85770458e 100755 --- a/tests/qemu-iotests/183 +++ b/tests/qemu-iotests/183 @@ -90,7 +90,7 @@ echo reply="$(_send_qemu_cmd $src \ "{ 'execute': 'migrate', 'arguments': { 'uri': 'unix:${MIG_SOCKET}', 'blk': true } }" \ - 'return\|error')" + 'return\|error' | _filter_migration_block_deprecated)" echo "$reply" if echo "$reply" | grep "compiled without old-style" > /dev/null; then _notrun "migrate -b support not compiled in" diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter index fc3c64bcb8..2846c83808 100644 --- a/tests/qemu-iotests/common.filter +++ b/tests/qemu-iotests/common.filter @@ -359,5 +359,12 @@ _filter_qcow2_compression_type_bit() -e 's/\(incompatible_features.*\), 3\(,.*\)/\1\2/' } +# filter warnings caused for block migration deprecation +_filter_migration_block_deprecated() +{ + gsed -e '/warning: parameter .blk. is deprecated; use blockdev-mirror with NBD instead/d' \ + -e '/warning: block migration is deprecated; use blockdev-mirror with NBD instead/d' +} + # make sure this script returns success true From 40101f320d6bf504663327cd12e99f36555a1f56 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 18 Oct 2023 13:55:10 +0200 Subject: [PATCH 228/974] migration: migrate 'inc' command option is deprecated. Use blockdev-mirror with NBD instead. Reviewed-by: Thomas Huth Acked-by: Stefan Hajnoczi Reviewed-by: Markus Armbruster Signed-off-by: Juan Quintela Message-ID: <20231018115513.2163-3-quintela@redhat.com> --- docs/about/deprecated.rst | 8 ++++++++ migration/migration-hmp-cmds.c | 5 +++++ migration/migration.c | 5 +++++ qapi/migration.json | 8 +++++++- 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 4e0eb2fe02..1c8c8c34c4 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -469,3 +469,11 @@ Migration ``skipped`` field in Migration stats has been deprecated. It hasn't been used for more than 10 years. +``inc`` migrate command option (since 8.2) +'''''''''''''''''''''''''''''''''''''''''' + +Use blockdev-mirror with NBD instead. + +As an intermediate step the ``inc`` functionality can be achieved by +setting the ``block-incremental`` migration parameter to ``true``. +But this parameter is also deprecated. diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index a82597f18e..83176f5bae 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -745,6 +745,11 @@ void hmp_migrate(Monitor *mon, const QDict *qdict) const char *uri = qdict_get_str(qdict, "uri"); Error *err = NULL; + if (inc) { + warn_report("option '-i' is deprecated;" + " use blockdev-mirror with NBD instead"); + } + qmp_migrate(uri, !!blk, blk, !!inc, inc, false, false, true, resume, &err); if (hmp_handle_error(mon, err)) { diff --git a/migration/migration.c b/migration/migration.c index 67547eb6a1..06a706ad90 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1620,6 +1620,11 @@ static bool migrate_prepare(MigrationState *s, bool blk, bool blk_inc, { Error *local_err = NULL; + if (blk_inc) { + warn_report("parameter 'inc' is deprecated;" + " use blockdev-mirror with NBD instead"); + } + if (resume) { if (s->state != MIGRATION_STATUS_POSTCOPY_PAUSED) { error_setg(errp, "Cannot resume if there is no " diff --git a/qapi/migration.json b/qapi/migration.json index db3df12d6c..fa7f4f2575 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -1524,6 +1524,11 @@ # # @resume: resume one paused migration, default "off". (since 3.0) # +# Features: +# +# @deprecated: Member @inc is deprecated. Use blockdev-mirror with +# NBD instead. +# # Returns: nothing on success # # Since: 0.14 @@ -1545,7 +1550,8 @@ # <- { "return": {} } ## { 'command': 'migrate', - 'data': {'uri': 'str', '*blk': 'bool', '*inc': 'bool', + 'data': {'uri': 'str', '*blk': 'bool', + '*inc': { 'type': 'bool', 'features': [ 'deprecated' ] }, '*detach': 'bool', '*resume': 'bool' } } ## From 8846b5bfca761d599974482c21fa6da3d3c13a70 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 18 Oct 2023 13:55:11 +0200 Subject: [PATCH 229/974] migration: migrate 'blk' command option is deprecated. Use blocked-mirror with NBD instead. Acked-by: Stefan Hajnoczi Reviewed-by: Thomas Huth Reviewed-by: Markus Armbruster Signed-off-by: Juan Quintela Message-ID: <20231018115513.2163-4-quintela@redhat.com> --- docs/about/deprecated.rst | 9 +++++++++ migration/migration-hmp-cmds.c | 5 +++++ migration/migration.c | 5 +++++ qapi/migration.json | 7 ++++--- 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 1c8c8c34c4..c1613468e6 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -477,3 +477,12 @@ Use blockdev-mirror with NBD instead. As an intermediate step the ``inc`` functionality can be achieved by setting the ``block-incremental`` migration parameter to ``true``. But this parameter is also deprecated. + +``blk`` migrate command option (since 8.2) +'''''''''''''''''''''''''''''''''''''''''' + +Use blockdev-mirror with NBD instead. + +As an intermediate step the ``blk`` functionality can be achieved by +setting the ``block`` migration capability to ``true``. But this +capability is also deprecated. diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index 83176f5bae..dfe98da355 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -750,6 +750,11 @@ void hmp_migrate(Monitor *mon, const QDict *qdict) " use blockdev-mirror with NBD instead"); } + if (blk) { + warn_report("option '-b' is deprecated;" + " use blockdev-mirror with NBD instead"); + } + qmp_migrate(uri, !!blk, blk, !!inc, inc, false, false, true, resume, &err); if (hmp_handle_error(mon, err)) { diff --git a/migration/migration.c b/migration/migration.c index 06a706ad90..cb2d7161b5 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1625,6 +1625,11 @@ static bool migrate_prepare(MigrationState *s, bool blk, bool blk_inc, " use blockdev-mirror with NBD instead"); } + if (blk) { + warn_report("parameter 'blk' is deprecated;" + " use blockdev-mirror with NBD instead"); + } + if (resume) { if (s->state != MIGRATION_STATUS_POSTCOPY_PAUSED) { error_setg(errp, "Cannot resume if there is no " diff --git a/qapi/migration.json b/qapi/migration.json index fa7f4f2575..3765c2b662 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -1526,8 +1526,8 @@ # # Features: # -# @deprecated: Member @inc is deprecated. Use blockdev-mirror with -# NBD instead. +# @deprecated: Members @inc and @blk are deprecated. Use +# blockdev-mirror with NBD instead. # # Returns: nothing on success # @@ -1550,7 +1550,8 @@ # <- { "return": {} } ## { 'command': 'migrate', - 'data': {'uri': 'str', '*blk': 'bool', + 'data': {'uri': 'str', + '*blk': { 'type': 'bool', 'features': [ 'deprecated' ] }, '*inc': { 'type': 'bool', 'features': [ 'deprecated' ] }, '*detach': 'bool', '*resume': 'bool' } } From 66db46ca83b8fb72f089e33be12e1a55b3b17eb6 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 18 Oct 2023 13:55:12 +0200 Subject: [PATCH 230/974] migration: Deprecate block migration It is obsolete. It is better to use driver-mirror with NBD instead. CC: Kevin Wolf CC: Eric Blake CC: Stefan Hajnoczi CC: Hanna Czenczek Acked-by: Stefan Hajnoczi Reviewed-by: Markus Armbruster Signed-off-by: Juan Quintela Message-ID: <20231018115513.2163-5-quintela@redhat.com> --- docs/about/deprecated.rst | 10 ++++++++++ migration/block.c | 3 +++ migration/options.c | 9 ++++++++- qapi/migration.json | 29 ++++++++++++++++++++++++----- 4 files changed, 45 insertions(+), 6 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index c1613468e6..35c8d7d1d4 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -486,3 +486,13 @@ Use blockdev-mirror with NBD instead. As an intermediate step the ``blk`` functionality can be achieved by setting the ``block`` migration capability to ``true``. But this capability is also deprecated. + +block migration (since 8.2) +''''''''''''''''''''''''''' + +Block migration is too inflexible. It needs to migrate all block +devices or none. + +Please see "QMP invocation for live storage migration with +``blockdev-mirror`` + NBD" in docs/interop/live-block-operations.rst +for a detailed explanation. diff --git a/migration/block.c b/migration/block.c index b60698d6e2..acffe88f84 100644 --- a/migration/block.c +++ b/migration/block.c @@ -731,6 +731,9 @@ static int block_save_setup(QEMUFile *f, void *opaque) trace_migration_block_save("setup", block_mig_state.submitted, block_mig_state.transferred); + warn_report("block migration is deprecated;" + " use blockdev-mirror with NBD instead"); + ret = init_blk_migration(f); if (ret < 0) { return ret; diff --git a/migration/options.c b/migration/options.c index 37fa1cfe74..ae8ab47e32 100644 --- a/migration/options.c +++ b/migration/options.c @@ -12,6 +12,7 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "exec/target_page.h" #include "qapi/clone-visitor.h" #include "qapi/error.h" @@ -473,10 +474,14 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp) if (new_caps[MIGRATION_CAPABILITY_BLOCK]) { error_setg(errp, "QEMU compiled without old-style (blk/-b, inc/-i) " "block migration"); - error_append_hint(errp, "Use drive_mirror+NBD instead.\n"); + error_append_hint(errp, "Use blockdev-mirror with NBD instead.\n"); return false; } #endif + if (new_caps[MIGRATION_CAPABILITY_BLOCK]) { + warn_report("block migration is deprecated;" + " use blockdev-mirror with NBD instead"); + } #ifndef CONFIG_REPLICATION if (new_caps[MIGRATION_CAPABILITY_X_COLO]) { @@ -1400,6 +1405,8 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp) } if (params->has_block_incremental) { + warn_report("block migration is deprecated;" + " use blockdev-mirror with NBD instead"); s->parameters.block_incremental = params->block_incremental; } if (params->has_multifd_channels) { diff --git a/qapi/migration.json b/qapi/migration.json index 3765c2b662..e3b00a215b 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -269,11 +269,15 @@ # average memory load of the virtual CPU indirectly. Note that # zero means guest doesn't dirty memory. (Since 8.1) # +# Features: +# +# @deprecated: Member @disk is deprecated because block migration is. +# # Since: 0.14 ## { 'struct': 'MigrationInfo', 'data': {'*status': 'MigrationStatus', '*ram': 'MigrationStats', - '*disk': 'MigrationStats', + '*disk': { 'type': 'MigrationStats', 'features': [ 'deprecated' ] }, '*vfio': 'VfioStats', '*xbzrle-cache': 'XBZRLECacheStats', '*total-time': 'int', @@ -525,6 +529,9 @@ # # Features: # +# @deprecated: Member @block is deprecated. Use blockdev-mirror with +# NBD instead. +# # @unstable: Members @x-colo and @x-ignore-shared are experimental. # # Since: 1.2 @@ -534,7 +541,8 @@ 'compress', 'events', 'postcopy-ram', { 'name': 'x-colo', 'features': [ 'unstable' ] }, 'release-ram', - 'block', 'return-path', 'pause-before-switchover', 'multifd', + { 'name': 'block', 'features': [ 'deprecated' ] }, + 'return-path', 'pause-before-switchover', 'multifd', 'dirty-bitmaps', 'postcopy-blocktime', 'late-block-activate', { 'name': 'x-ignore-shared', 'features': [ 'unstable' ] }, 'validate-uuid', 'background-snapshot', @@ -835,6 +843,9 @@ # # Features: # +# @deprecated: Member @block-incremental is deprecated. Use +# blockdev-mirror with NBD instead. +# # @unstable: Members @x-checkpoint-delay and @x-vcpu-dirty-limit-period # are experimental. # @@ -850,7 +861,7 @@ 'tls-creds', 'tls-hostname', 'tls-authz', 'max-bandwidth', 'avail-switchover-bandwidth', 'downtime-limit', { 'name': 'x-checkpoint-delay', 'features': [ 'unstable' ] }, - 'block-incremental', + { 'name': 'block-incremental', 'features': [ 'deprecated' ] }, 'multifd-channels', 'xbzrle-cache-size', 'max-postcopy-bandwidth', 'max-cpu-throttle', 'multifd-compression', @@ -1011,6 +1022,9 @@ # # Features: # +# @deprecated: Member @block-incremental is deprecated. Use +# blockdev-mirror with NBD instead. +# # @unstable: Members @x-checkpoint-delay and @x-vcpu-dirty-limit-period # are experimental. # @@ -1040,7 +1054,8 @@ '*downtime-limit': 'uint64', '*x-checkpoint-delay': { 'type': 'uint32', 'features': [ 'unstable' ] }, - '*block-incremental': 'bool', + '*block-incremental': { 'type': 'bool', + 'features': [ 'deprecated' ] }, '*multifd-channels': 'uint8', '*xbzrle-cache-size': 'size', '*max-postcopy-bandwidth': 'size', @@ -1225,6 +1240,9 @@ # # Features: # +# @deprecated: Member @block-incremental is deprecated. Use +# blockdev-mirror with NBD instead. +# # @unstable: Members @x-checkpoint-delay and @x-vcpu-dirty-limit-period # are experimental. # @@ -1251,7 +1269,8 @@ '*downtime-limit': 'uint64', '*x-checkpoint-delay': { 'type': 'uint32', 'features': [ 'unstable' ] }, - '*block-incremental': 'bool', + '*block-incremental': { 'type': 'bool', + 'features': [ 'deprecated' ] }, '*multifd-channels': 'uint8', '*xbzrle-cache-size': 'size', '*max-postcopy-bandwidth': 'size', From 864128df465a8b041e089475e903be8d7d0d06ef Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 18 Oct 2023 13:55:13 +0200 Subject: [PATCH 231/974] migration: Deprecate old compression method Acked-by: Stefan Hajnoczi Acked-by: Peter Xu Reviewed-by: Markus Armbruster Signed-off-by: Juan Quintela Message-ID: <20231018115513.2163-6-quintela@redhat.com> --- docs/about/deprecated.rst | 8 +++++ migration/options.c | 13 ++++++++ qapi/migration.json | 63 ++++++++++++++++++++++++++------------- 3 files changed, 64 insertions(+), 20 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 35c8d7d1d4..ecccd5d3fc 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -496,3 +496,11 @@ devices or none. Please see "QMP invocation for live storage migration with ``blockdev-mirror`` + NBD" in docs/interop/live-block-operations.rst for a detailed explanation. + +old compression method (since 8.2) +'''''''''''''''''''''''''''''''''' + +Compression method fails too much. Too many races. We are going to +remove it if nobody fixes it. For starters, migration-test +compression tests are disabled becase they fail randomly. If you need +compression, use multifd compression methods. diff --git a/migration/options.c b/migration/options.c index ae8ab47e32..9a39826ca5 100644 --- a/migration/options.c +++ b/migration/options.c @@ -483,6 +483,11 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp) " use blockdev-mirror with NBD instead"); } + if (new_caps[MIGRATION_CAPABILITY_COMPRESS]) { + warn_report("old compression method is deprecated;" + " use multifd compression methods instead"); + } + #ifndef CONFIG_REPLICATION if (new_caps[MIGRATION_CAPABILITY_X_COLO]) { error_setg(errp, "QEMU compiled without replication module" @@ -1335,18 +1340,26 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp) /* TODO use QAPI_CLONE() instead of duplicating it inline */ if (params->has_compress_level) { + warn_report("old compression is deprecated;" + " use multifd compression methods instead"); s->parameters.compress_level = params->compress_level; } if (params->has_compress_threads) { + warn_report("old compression is deprecated;" + " use multifd compression methods instead"); s->parameters.compress_threads = params->compress_threads; } if (params->has_compress_wait_thread) { + warn_report("old compression is deprecated;" + " use multifd compression methods instead"); s->parameters.compress_wait_thread = params->compress_wait_thread; } if (params->has_decompress_threads) { + warn_report("old compression is deprecated;" + " use multifd compression methods instead"); s->parameters.decompress_threads = params->decompress_threads; } diff --git a/qapi/migration.json b/qapi/migration.json index e3b00a215b..e6610af428 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -272,6 +272,10 @@ # Features: # # @deprecated: Member @disk is deprecated because block migration is. +# Member @compression is deprecated because it is unreliable and +# untested. It is recommended to use multifd migration, which +# offers an alternative compression implementation that is +# reliable and tested. # # Since: 0.14 ## @@ -289,7 +293,7 @@ '*blocked-reasons': ['str'], '*postcopy-blocktime': 'uint32', '*postcopy-vcpu-blocktime': ['uint32'], - '*compression': 'CompressionStats', + '*compression': { 'type': 'CompressionStats', 'features': [ 'deprecated' ] }, '*socket-address': ['SocketAddress'], '*dirty-limit-throttle-time-per-round': 'uint64', '*dirty-limit-ring-full-time': 'uint64'} } @@ -530,7 +534,10 @@ # Features: # # @deprecated: Member @block is deprecated. Use blockdev-mirror with -# NBD instead. +# NBD instead. Member @compression is deprecated because it is +# unreliable and untested. It is recommended to use multifd +# migration, which offers an alternative compression +# implementation that is reliable and tested. # # @unstable: Members @x-colo and @x-ignore-shared are experimental. # @@ -538,7 +545,8 @@ ## { 'enum': 'MigrationCapability', 'data': ['xbzrle', 'rdma-pin-all', 'auto-converge', 'zero-blocks', - 'compress', 'events', 'postcopy-ram', + { 'name': 'compress', 'features': [ 'deprecated' ] }, + 'events', 'postcopy-ram', { 'name': 'x-colo', 'features': [ 'unstable' ] }, 'release-ram', { 'name': 'block', 'features': [ 'deprecated' ] }, @@ -844,7 +852,9 @@ # Features: # # @deprecated: Member @block-incremental is deprecated. Use -# blockdev-mirror with NBD instead. +# blockdev-mirror with NBD instead. Members @compress-level, +# @compress-threads, @decompress-threads and @compress-wait-thread +# are deprecated because @compression is deprecated. # # @unstable: Members @x-checkpoint-delay and @x-vcpu-dirty-limit-period # are experimental. @@ -854,8 +864,11 @@ { 'enum': 'MigrationParameter', 'data': ['announce-initial', 'announce-max', 'announce-rounds', 'announce-step', - 'compress-level', 'compress-threads', 'decompress-threads', - 'compress-wait-thread', 'throttle-trigger-threshold', + { 'name': 'compress-level', 'features': [ 'deprecated' ] }, + { 'name': 'compress-threads', 'features': [ 'deprecated' ] }, + { 'name': 'decompress-threads', 'features': [ 'deprecated' ] }, + { 'name': 'compress-wait-thread', 'features': [ 'deprecated' ] }, + 'throttle-trigger-threshold', 'cpu-throttle-initial', 'cpu-throttle-increment', 'cpu-throttle-tailslow', 'tls-creds', 'tls-hostname', 'tls-authz', 'max-bandwidth', @@ -1023,7 +1036,9 @@ # Features: # # @deprecated: Member @block-incremental is deprecated. Use -# blockdev-mirror with NBD instead. +# blockdev-mirror with NBD instead. Members @compress-level, +# @compress-threads, @decompress-threads and @compress-wait-thread +# are deprecated because @compression is deprecated. # # @unstable: Members @x-checkpoint-delay and @x-vcpu-dirty-limit-period # are experimental. @@ -1038,10 +1053,14 @@ '*announce-max': 'size', '*announce-rounds': 'size', '*announce-step': 'size', - '*compress-level': 'uint8', - '*compress-threads': 'uint8', - '*compress-wait-thread': 'bool', - '*decompress-threads': 'uint8', + '*compress-level': { 'type': 'uint8', + 'features': [ 'deprecated' ] }, + '*compress-threads': { 'type': 'uint8', + 'features': [ 'deprecated' ] }, + '*compress-wait-thread': { 'type': 'bool', + 'features': [ 'deprecated' ] }, + '*decompress-threads': { 'type': 'uint8', + 'features': [ 'deprecated' ] }, '*throttle-trigger-threshold': 'uint8', '*cpu-throttle-initial': 'uint8', '*cpu-throttle-increment': 'uint8', @@ -1078,7 +1097,7 @@ # Example: # # -> { "execute": "migrate-set-parameters" , -# "arguments": { "compress-level": 1 } } +# "arguments": { "multifd-channels": 5 } } # <- { "return": {} } ## { 'command': 'migrate-set-parameters', 'boxed': true, @@ -1241,7 +1260,9 @@ # Features: # # @deprecated: Member @block-incremental is deprecated. Use -# blockdev-mirror with NBD instead. +# blockdev-mirror with NBD instead. Members @compress-level, +# @compress-threads, @decompress-threads and @compress-wait-thread +# are deprecated because @compression is deprecated. # # @unstable: Members @x-checkpoint-delay and @x-vcpu-dirty-limit-period # are experimental. @@ -1253,10 +1274,14 @@ '*announce-max': 'size', '*announce-rounds': 'size', '*announce-step': 'size', - '*compress-level': 'uint8', - '*compress-threads': 'uint8', - '*compress-wait-thread': 'bool', - '*decompress-threads': 'uint8', + '*compress-level': { 'type': 'uint8', + 'features': [ 'deprecated' ] }, + '*compress-threads': { 'type': 'uint8', + 'features': [ 'deprecated' ] }, + '*compress-wait-thread': { 'type': 'bool', + 'features': [ 'deprecated' ] }, + '*decompress-threads': { 'type': 'uint8', + 'features': [ 'deprecated' ] }, '*throttle-trigger-threshold': 'uint8', '*cpu-throttle-initial': 'uint8', '*cpu-throttle-increment': 'uint8', @@ -1296,10 +1321,8 @@ # # -> { "execute": "query-migrate-parameters" } # <- { "return": { -# "decompress-threads": 2, +# "multifd-channels": 2, # "cpu-throttle-increment": 10, -# "compress-threads": 8, -# "compress-level": 1, # "cpu-throttle-initial": 20, # "max-bandwidth": 33554432, # "downtime-limit": 300 From a2326705e5a6bb21338b3d84f1b0e0b4f46cbc82 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 24 Oct 2023 12:39:33 -0400 Subject: [PATCH 232/974] migration: Stop migration immediately in RDMA error paths In multiple places, RDMA errors are handled in a strange way, where it only sets qemu_file_set_error() but not stop the migration immediately. It's not obvious what will happen later if there is already an error. Make all such failures stop migration immediately. Cc: Zhijian Li (Fujitsu) Cc: Markus Armbruster Cc: Juan Quintela Cc: Fabiano Rosas Reported-by: Thomas Huth Signed-off-by: Peter Xu Reviewed-by: Juan Quintela Reviewed-by: Fabiano Rosas Signed-off-by: Juan Quintela Message-ID: <20231024163933.516546-1-peterx@redhat.com> --- migration/ram.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 024dedb6b1..20e6153114 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -2973,11 +2973,13 @@ static int ram_save_setup(QEMUFile *f, void *opaque) ret = rdma_registration_start(f, RAM_CONTROL_SETUP); if (ret < 0) { qemu_file_set_error(f, ret); + return ret; } ret = rdma_registration_stop(f, RAM_CONTROL_SETUP); if (ret < 0) { qemu_file_set_error(f, ret); + return ret; } migration_ops = g_malloc0(sizeof(MigrationOps)); @@ -3043,6 +3045,7 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) ret = rdma_registration_start(f, RAM_CONTROL_ROUND); if (ret < 0) { qemu_file_set_error(f, ret); + goto out; } t0 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); @@ -3147,8 +3150,6 @@ static int ram_save_complete(QEMUFile *f, void *opaque) rs->last_stage = !migration_in_colo_state(); WITH_RCU_READ_LOCK_GUARD() { - int rdma_reg_ret; - if (!migration_in_postcopy()) { migration_bitmap_sync_precopy(rs, true); } @@ -3156,6 +3157,7 @@ static int ram_save_complete(QEMUFile *f, void *opaque) ret = rdma_registration_start(f, RAM_CONTROL_FINISH); if (ret < 0) { qemu_file_set_error(f, ret); + return ret; } /* try transferring iterative blocks of memory */ @@ -3171,24 +3173,21 @@ static int ram_save_complete(QEMUFile *f, void *opaque) break; } if (pages < 0) { - ret = pages; - break; + qemu_mutex_unlock(&rs->bitmap_mutex); + return pages; } } qemu_mutex_unlock(&rs->bitmap_mutex); compress_flush_data(); - rdma_reg_ret = rdma_registration_stop(f, RAM_CONTROL_FINISH); - if (rdma_reg_ret < 0) { - qemu_file_set_error(f, rdma_reg_ret); + ret = rdma_registration_stop(f, RAM_CONTROL_FINISH); + if (ret < 0) { + qemu_file_set_error(f, ret); + return ret; } } - if (ret < 0) { - return ret; - } - ret = multifd_send_sync_main(rs->pss[RAM_CHANNEL_PRECOPY].pss_channel); if (ret < 0) { return ret; From cc8bf57d56ef22a66711100f5d94861a627e9b9f Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 25 Oct 2023 11:11:06 +0200 Subject: [PATCH 233/974] qemu-file: Don't increment qemu_file_transferred at qemu_file_fill_buffer We only call qemu_file_transferred_* on the sending side. Remove the increment at qemu_file_fill_buffer() and add asserts to qemu_file_transferred* functions. Reviewed-by: Fabiano Rosas Signed-off-by: Juan Quintela Message-ID: <20231025091117.6342-2-quintela@redhat.com> --- migration/qemu-file.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/migration/qemu-file.c b/migration/qemu-file.c index 3fb25148d1..6814c562e6 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -337,7 +337,6 @@ static ssize_t coroutine_mixed_fn qemu_fill_buffer(QEMUFile *f) if (len > 0) { f->buf_size += len; - f->total_transferred += len; } else if (len == 0) { qemu_file_set_error_obj(f, -EIO, local_error); } else { @@ -627,6 +626,8 @@ uint64_t qemu_file_transferred_noflush(QEMUFile *f) uint64_t ret = f->total_transferred; int i; + g_assert(qemu_file_is_writable(f)); + for (i = 0; i < f->iovcnt; i++) { ret += f->iov[i].iov_len; } @@ -636,6 +637,7 @@ uint64_t qemu_file_transferred_noflush(QEMUFile *f) uint64_t qemu_file_transferred(QEMUFile *f) { + g_assert(qemu_file_is_writable(f)); qemu_fflush(f); return f->total_transferred; } From 2d897237e01a7ed0dd8b9ac3b3a8234900145350 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 25 Oct 2023 11:11:07 +0200 Subject: [PATCH 234/974] qemu_file: Use a stat64 for qemu_file_transferred This way we can read it from any thread. I checked that it gives the same value as the current one. We never use two qemu_files at the same time. Reviewed-by: Fabiano Rosas Signed-off-by: Juan Quintela Message-ID: <20231025091117.6342-3-quintela@redhat.com> --- migration/migration-stats.h | 4 ++++ migration/qemu-file.c | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/migration/migration-stats.h b/migration/migration-stats.h index 2358caad63..b7795e7914 100644 --- a/migration/migration-stats.h +++ b/migration/migration-stats.h @@ -81,6 +81,10 @@ typedef struct { * Number of bytes sent during precopy stage. */ Stat64 precopy_bytes; + /* + * Number of bytes transferred with QEMUFile. + */ + Stat64 qemu_file_transferred; /* * Amount of transferred data at the start of current cycle. */ diff --git a/migration/qemu-file.c b/migration/qemu-file.c index 6814c562e6..384985f534 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -283,6 +283,7 @@ void qemu_fflush(QEMUFile *f) } else { uint64_t size = iov_size(f->iov, f->iovcnt); f->total_transferred += size; + stat64_add(&mig_stats.qemu_file_transferred, size); } qemu_iovec_release_ram(f); @@ -623,7 +624,7 @@ int coroutine_mixed_fn qemu_get_byte(QEMUFile *f) uint64_t qemu_file_transferred_noflush(QEMUFile *f) { - uint64_t ret = f->total_transferred; + uint64_t ret = stat64_get(&mig_stats.qemu_file_transferred); int i; g_assert(qemu_file_is_writable(f)); @@ -639,7 +640,7 @@ uint64_t qemu_file_transferred(QEMUFile *f) { g_assert(qemu_file_is_writable(f)); qemu_fflush(f); - return f->total_transferred; + return stat64_get(&mig_stats.qemu_file_transferred); } void qemu_put_be16(QEMUFile *f, unsigned int v) From 5e2652185b6a08fcf5c1acc2d81b59ddb9c7855d Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 25 Oct 2023 11:11:08 +0200 Subject: [PATCH 235/974] qemu_file: total_transferred is not used anymore Reviewed-by: Fabiano Rosas Signed-off-by: Juan Quintela Message-ID: <20231025091117.6342-4-quintela@redhat.com> --- migration/qemu-file.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/migration/qemu-file.c b/migration/qemu-file.c index 384985f534..641ab703cc 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -41,9 +41,6 @@ struct QEMUFile { QIOChannel *ioc; bool is_writable; - /* The sum of bytes transferred on the wire */ - uint64_t total_transferred; - int buf_index; int buf_size; /* 0 when writing */ uint8_t buf[IO_BUF_SIZE]; @@ -282,7 +279,6 @@ void qemu_fflush(QEMUFile *f) qemu_file_set_error_obj(f, -EIO, local_error); } else { uint64_t size = iov_size(f->iov, f->iovcnt); - f->total_transferred += size; stat64_add(&mig_stats.qemu_file_transferred, size); } From 737840e2c6ea76896ffd8d3f5474fd4a4aee62d6 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 25 Oct 2023 11:11:09 +0200 Subject: [PATCH 236/974] migration: Use the number of transferred bytes directly We only use migration_transferred_bytes() to calculate the rate_limit, for that we don't need to flush whatever is on the qemu_file buffer. Remember that the buffer is really small (normal case is 32K if we use iov's can be 64 * TARGET_PAGE_SIZE), so this is not relevant to calculations. Reviewed-by: Fabiano Rosas Signed-off-by: Juan Quintela Message-ID: <20231025091117.6342-5-quintela@redhat.com> --- migration/migration-stats.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migration/migration-stats.c b/migration/migration-stats.c index 4cc989d975..1d9197b4c3 100644 --- a/migration/migration-stats.c +++ b/migration/migration-stats.c @@ -63,7 +63,7 @@ uint64_t migration_transferred_bytes(QEMUFile *f) { uint64_t multifd = stat64_get(&mig_stats.multifd_bytes); uint64_t rdma = stat64_get(&mig_stats.rdma_bytes); - uint64_t qemu_file = qemu_file_transferred(f); + uint64_t qemu_file = stat64_get(&mig_stats.qemu_file_transferred); trace_migration_transferred_bytes(qemu_file, multifd, rdma); return qemu_file + multifd + rdma; From e833cad7e7ed4ee2b48237ff1a6a4ee95280b6ac Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 25 Oct 2023 11:11:10 +0200 Subject: [PATCH 237/974] qemu_file: Remove unused qemu_file_transferred() Reviewed-by: Fabiano Rosas Signed-off-by: Juan Quintela Message-ID: <20231025091117.6342-6-quintela@redhat.com> --- migration/qemu-file.c | 7 ------- migration/qemu-file.h | 18 ------------------ 2 files changed, 25 deletions(-) diff --git a/migration/qemu-file.c b/migration/qemu-file.c index 641ab703cc..efa5f11033 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -632,13 +632,6 @@ uint64_t qemu_file_transferred_noflush(QEMUFile *f) return ret; } -uint64_t qemu_file_transferred(QEMUFile *f) -{ - g_assert(qemu_file_is_writable(f)); - qemu_fflush(f); - return stat64_get(&mig_stats.qemu_file_transferred); -} - void qemu_put_be16(QEMUFile *f, unsigned int v) { qemu_put_byte(f, v >> 8); diff --git a/migration/qemu-file.h b/migration/qemu-file.h index a29c37b0d0..8b71152754 100644 --- a/migration/qemu-file.h +++ b/migration/qemu-file.h @@ -33,24 +33,6 @@ QEMUFile *qemu_file_new_input(QIOChannel *ioc); QEMUFile *qemu_file_new_output(QIOChannel *ioc); int qemu_fclose(QEMUFile *f); -/* - * qemu_file_transferred: - * - * Report the total number of bytes transferred with - * this file. - * - * For writable files, any pending buffers will be - * flushed, so the reported value will be equal to - * the number of bytes transferred on the wire. - * - * For readable files, the reported value will be - * equal to the number of bytes transferred on the - * wire. - * - * Returns: the total bytes transferred - */ -uint64_t qemu_file_transferred(QEMUFile *f); - /* * qemu_file_transferred_noflush: * From e9c0eed7c2c20f65904ffb33fc7b47a229ed5106 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 25 Oct 2023 11:11:11 +0200 Subject: [PATCH 238/974] qemu-file: Remove _noflush from qemu_file_transferred_noflush() qemu_file_transferred() don't exist anymore, so we can reuse the name. Reviewed-by: Fabiano Rosas Signed-off-by: Juan Quintela Message-ID: <20231025091117.6342-7-quintela@redhat.com> Signed-off-by: Juan Quintela --- migration/block.c | 4 ++-- migration/qemu-file.c | 2 +- migration/qemu-file.h | 9 ++++----- migration/savevm.c | 6 +++--- migration/vmstate.c | 4 ++-- 5 files changed, 12 insertions(+), 13 deletions(-) diff --git a/migration/block.c b/migration/block.c index acffe88f84..a15f9bddcb 100644 --- a/migration/block.c +++ b/migration/block.c @@ -755,7 +755,7 @@ static int block_save_setup(QEMUFile *f, void *opaque) static int block_save_iterate(QEMUFile *f, void *opaque) { int ret; - uint64_t last_bytes = qemu_file_transferred_noflush(f); + uint64_t last_bytes = qemu_file_transferred(f); trace_migration_block_save("iterate", block_mig_state.submitted, block_mig_state.transferred); @@ -807,7 +807,7 @@ static int block_save_iterate(QEMUFile *f, void *opaque) } qemu_put_be64(f, BLK_MIG_FLAG_EOS); - uint64_t delta_bytes = qemu_file_transferred_noflush(f) - last_bytes; + uint64_t delta_bytes = qemu_file_transferred(f) - last_bytes; return (delta_bytes > 0); } diff --git a/migration/qemu-file.c b/migration/qemu-file.c index efa5f11033..0158db2a54 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -618,7 +618,7 @@ int coroutine_mixed_fn qemu_get_byte(QEMUFile *f) return result; } -uint64_t qemu_file_transferred_noflush(QEMUFile *f) +uint64_t qemu_file_transferred(QEMUFile *f) { uint64_t ret = stat64_get(&mig_stats.qemu_file_transferred); int i; diff --git a/migration/qemu-file.h b/migration/qemu-file.h index 8b71152754..1b2f6b8d8f 100644 --- a/migration/qemu-file.h +++ b/migration/qemu-file.h @@ -34,15 +34,14 @@ QEMUFile *qemu_file_new_output(QIOChannel *ioc); int qemu_fclose(QEMUFile *f); /* - * qemu_file_transferred_noflush: + * qemu_file_transferred: * - * As qemu_file_transferred except for writable files, where no flush - * is performed and the reported amount will include the size of any - * queued buffers, on top of the amount actually transferred. + * No flush is performed and the reported amount will include the size + * of any queued buffers, on top of the amount actually transferred. * * Returns: the total bytes transferred and queued */ -uint64_t qemu_file_transferred_noflush(QEMUFile *f); +uint64_t qemu_file_transferred(QEMUFile *f); /* * put_buffer without copying the buffer. diff --git a/migration/savevm.c b/migration/savevm.c index ca5c7cebe0..9ec78abd53 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -927,9 +927,9 @@ static int vmstate_load(QEMUFile *f, SaveStateEntry *se) static void vmstate_save_old_style(QEMUFile *f, SaveStateEntry *se, JSONWriter *vmdesc) { - uint64_t old_offset = qemu_file_transferred_noflush(f); + uint64_t old_offset = qemu_file_transferred(f); se->ops->save_state(f, se->opaque); - uint64_t size = qemu_file_transferred_noflush(f) - old_offset; + uint64_t size = qemu_file_transferred(f) - old_offset; if (vmdesc) { json_writer_int64(vmdesc, "size", size); @@ -3053,7 +3053,7 @@ bool save_snapshot(const char *name, bool overwrite, const char *vmstate, goto the_end; } ret = qemu_savevm_state(f, errp); - vm_state_size = qemu_file_transferred_noflush(f); + vm_state_size = qemu_file_transferred(f); ret2 = qemu_fclose(f); if (ret < 0) { goto the_end; diff --git a/migration/vmstate.c b/migration/vmstate.c index 9c36803c8a..b7723a4187 100644 --- a/migration/vmstate.c +++ b/migration/vmstate.c @@ -387,7 +387,7 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, void *curr_elem = first_elem + size * i; vmsd_desc_field_start(vmsd, vmdesc_loop, field, i, n_elems); - old_offset = qemu_file_transferred_noflush(f); + old_offset = qemu_file_transferred(f); if (field->flags & VMS_ARRAY_OF_POINTER) { assert(curr_elem); curr_elem = *(void **)curr_elem; @@ -417,7 +417,7 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, return ret; } - written_bytes = qemu_file_transferred_noflush(f) - old_offset; + written_bytes = qemu_file_transferred(f) - old_offset; vmsd_desc_field_end(vmsd, vmdesc_loop, field, written_bytes, i); /* Compressed arrays only care about the first element */ From f57e5a6ce50f10941399014c628db8713c2bc3f9 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 25 Oct 2023 11:11:12 +0200 Subject: [PATCH 239/974] migration: migration_transferred_bytes() don't need the QEMUFile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Juan Quintela Message-ID: <20231025091117.6342-8-quintela@redhat.com> --- migration/migration-stats.c | 6 +++--- migration/migration-stats.h | 4 +--- migration/migration.c | 6 +++--- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/migration/migration-stats.c b/migration/migration-stats.c index 1d9197b4c3..4ae8c0c722 100644 --- a/migration/migration-stats.c +++ b/migration/migration-stats.c @@ -30,7 +30,7 @@ bool migration_rate_exceeded(QEMUFile *f) } uint64_t rate_limit_start = stat64_get(&mig_stats.rate_limit_start); - uint64_t rate_limit_current = migration_transferred_bytes(f); + uint64_t rate_limit_current = migration_transferred_bytes(); uint64_t rate_limit_used = rate_limit_current - rate_limit_start; if (rate_limit_max > 0 && rate_limit_used > rate_limit_max) { @@ -56,10 +56,10 @@ void migration_rate_set(uint64_t limit) void migration_rate_reset(QEMUFile *f) { - stat64_set(&mig_stats.rate_limit_start, migration_transferred_bytes(f)); + stat64_set(&mig_stats.rate_limit_start, migration_transferred_bytes()); } -uint64_t migration_transferred_bytes(QEMUFile *f) +uint64_t migration_transferred_bytes(void) { uint64_t multifd = stat64_get(&mig_stats.multifd_bytes); uint64_t rdma = stat64_get(&mig_stats.rdma_bytes); diff --git a/migration/migration-stats.h b/migration/migration-stats.h index b7795e7914..e3863bf9bb 100644 --- a/migration/migration-stats.h +++ b/migration/migration-stats.h @@ -137,11 +137,9 @@ void migration_rate_set(uint64_t new_rate); /** * migration_transferred_bytes: Return number of bytes transferred * - * @f: QEMUFile used for main migration channel - * * Returns how many bytes have we transferred since the beginning of * the migration. It accounts for bytes sent through any migration * channel, multifd, qemu_file, rdma, .... */ -uint64_t migration_transferred_bytes(QEMUFile *f); +uint64_t migration_transferred_bytes(void); #endif diff --git a/migration/migration.c b/migration/migration.c index cb2d7161b5..04d4546150 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2704,7 +2704,7 @@ static MigThrError migration_detect_error(MigrationState *s) static void migration_calculate_complete(MigrationState *s) { - uint64_t bytes = migration_transferred_bytes(s->to_dst_file); + uint64_t bytes = migration_transferred_bytes(); int64_t end_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); int64_t transfer_time; @@ -2730,7 +2730,7 @@ static void update_iteration_initial_status(MigrationState *s) * wrong speed calculation. */ s->iteration_start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); - s->iteration_initial_bytes = migration_transferred_bytes(s->to_dst_file); + s->iteration_initial_bytes = migration_transferred_bytes(); s->iteration_initial_pages = ram_get_total_transferred_pages(); } @@ -2749,7 +2749,7 @@ static void migration_update_counters(MigrationState *s, } switchover_bw = migrate_avail_switchover_bandwidth(); - current_bytes = migration_transferred_bytes(s->to_dst_file); + current_bytes = migration_transferred_bytes(); transferred = current_bytes - s->iteration_initial_bytes; time_spent = current_time - s->iteration_start_time; bandwidth = (double)transferred / time_spent; From 0743f41fd293b033de289c52faa45d06a5b1d544 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 25 Oct 2023 11:11:13 +0200 Subject: [PATCH 240/974] migration: migration_rate_limit_reset() don't need the QEMUFile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Juan Quintela Message-ID: <20231025091117.6342-9-quintela@redhat.com> --- migration/migration-stats.c | 2 +- migration/migration-stats.h | 4 +--- migration/migration.c | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/migration/migration-stats.c b/migration/migration-stats.c index 4ae8c0c722..f690b98a03 100644 --- a/migration/migration-stats.c +++ b/migration/migration-stats.c @@ -54,7 +54,7 @@ void migration_rate_set(uint64_t limit) stat64_set(&mig_stats.rate_limit_max, limit / XFER_LIMIT_RATIO); } -void migration_rate_reset(QEMUFile *f) +void migration_rate_reset(void) { stat64_set(&mig_stats.rate_limit_start, migration_transferred_bytes()); } diff --git a/migration/migration-stats.h b/migration/migration-stats.h index e3863bf9bb..68f3939188 100644 --- a/migration/migration-stats.h +++ b/migration/migration-stats.h @@ -120,10 +120,8 @@ uint64_t migration_rate_get(void); * migration_rate_reset: Reset the rate limit counter. * * This is called when we know we start a new transfer cycle. - * - * @f: QEMUFile used for main migration channel */ -void migration_rate_reset(QEMUFile *f); +void migration_rate_reset(void); /** * migration_rate_set: Set the maximum amount that can be transferred. diff --git a/migration/migration.c b/migration/migration.c index 04d4546150..a25a2f3c54 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2785,7 +2785,7 @@ static void migration_update_counters(MigrationState *s, stat64_get(&mig_stats.dirty_bytes_last_sync) / expected_bw_per_ms; } - migration_rate_reset(s->to_dst_file); + migration_rate_reset(); update_iteration_initial_status(s); From fc55cf318a19c385c25843865788ffdd05cde607 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 25 Oct 2023 11:11:14 +0200 Subject: [PATCH 241/974] qemu-file: Simplify qemu_file_get_error() If we pass a NULL error is the same that returning directly the value. Reviewed-by: Fabiano Rosas Signed-off-by: Juan Quintela Message-ID: <20231025091117.6342-10-quintela@redhat.com> --- migration/qemu-file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migration/qemu-file.c b/migration/qemu-file.c index 0158db2a54..7e738743ce 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -204,7 +204,7 @@ void qemu_file_set_error_obj(QEMUFile *f, int ret, Error *err) */ int qemu_file_get_error(QEMUFile *f) { - return qemu_file_get_error_obj(f, NULL); + return f->last_error; } /* From 897fd8bdce6c0939e0c35ae8fda4af09b5e5fb40 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 25 Oct 2023 11:11:15 +0200 Subject: [PATCH 242/974] migration: Use migration_transferred_bytes() There are only two differnces with the old value: - the amount of QEMUFile that hasn't yet been flushed. It can be discussed what is more exact, the new or the old one. - the amount of transferred bytes that we forgot to account for (the newer is better, i.e. exact). Notice that this two values are used to: a - present to the user b - calculate the rate_limit So a few KB here and there is not going to make a difference. Reviewed-by: Fabiano Rosas Signed-off-by: Juan Quintela Message-ID: <20231025091117.6342-11-quintela@redhat.com> --- migration/migration.c | 2 +- migration/ram.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index a25a2f3c54..aa7b791833 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -942,7 +942,7 @@ static void populate_ram_info(MigrationInfo *info, MigrationState *s) size_t page_size = qemu_target_page_size(); info->ram = g_malloc0(sizeof(*info->ram)); - info->ram->transferred = stat64_get(&mig_stats.transferred); + info->ram->transferred = migration_transferred_bytes(); info->ram->total = ram_bytes_total(); info->ram->duplicate = stat64_get(&mig_stats.zero_pages); /* legacy value. It is not used anymore */ diff --git a/migration/ram.c b/migration/ram.c index 20e6153114..8dd36e3d2b 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -557,7 +557,7 @@ void mig_throttle_counter_reset(void) rs->time_last_bitmap_sync = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); rs->num_dirty_pages_period = 0; - rs->bytes_xfer_prev = stat64_get(&mig_stats.transferred); + rs->bytes_xfer_prev = migration_transferred_bytes(); } /** @@ -1003,7 +1003,7 @@ static void migration_trigger_throttle(RAMState *rs) { uint64_t threshold = migrate_throttle_trigger_threshold(); uint64_t bytes_xfer_period = - stat64_get(&mig_stats.transferred) - rs->bytes_xfer_prev; + migration_transferred_bytes() - rs->bytes_xfer_prev; uint64_t bytes_dirty_period = rs->num_dirty_pages_period * TARGET_PAGE_SIZE; uint64_t bytes_dirty_threshold = bytes_xfer_period * threshold / 100; @@ -1073,7 +1073,7 @@ static void migration_bitmap_sync(RAMState *rs, bool last_stage) /* reset period counters */ rs->time_last_bitmap_sync = end_time; rs->num_dirty_pages_period = 0; - rs->bytes_xfer_prev = stat64_get(&mig_stats.transferred); + rs->bytes_xfer_prev = migration_transferred_bytes(); } if (migrate_events()) { uint64_t generation = stat64_get(&mig_stats.dirty_sync_count); From 0f8596180a304182d4fcf8686b73355de9f37a5d Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 25 Oct 2023 11:11:16 +0200 Subject: [PATCH 243/974] migration: Remove transferred atomic counter After last commit, it is a write only variable. Reviewed-by: Fabiano Rosas Signed-off-by: Juan Quintela Message-ID: <20231025091117.6342-12-quintela@redhat.com> --- migration/migration-stats.h | 4 ---- migration/multifd.c | 3 --- migration/ram.c | 1 - 3 files changed, 8 deletions(-) diff --git a/migration/migration-stats.h b/migration/migration-stats.h index 68f3939188..05290ade76 100644 --- a/migration/migration-stats.h +++ b/migration/migration-stats.h @@ -97,10 +97,6 @@ typedef struct { * Number of bytes sent through RDMA. */ Stat64 rdma_bytes; - /* - * Total number of bytes transferred. - */ - Stat64 transferred; /* * Number of pages transferred that were full of zeros. */ diff --git a/migration/multifd.c b/migration/multifd.c index e2a45c667a..ec58c58082 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -188,7 +188,6 @@ static int multifd_send_initial_packet(MultiFDSendParams *p, Error **errp) return -1; } stat64_add(&mig_stats.multifd_bytes, size); - stat64_add(&mig_stats.transferred, size); return 0; } @@ -733,8 +732,6 @@ static void *multifd_send_thread(void *opaque) stat64_add(&mig_stats.multifd_bytes, p->next_packet_size + p->packet_len); - stat64_add(&mig_stats.transferred, - p->next_packet_size + p->packet_len); p->next_packet_size = 0; qemu_mutex_lock(&p->mutex); p->pending_job--; diff --git a/migration/ram.c b/migration/ram.c index 8dd36e3d2b..cec9bd31d9 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -448,7 +448,6 @@ void ram_transferred_add(uint64_t bytes) } else { stat64_add(&mig_stats.downtime_bytes, bytes); } - stat64_add(&mig_stats.transferred, bytes); } struct MigrationOps { From be07a0ed22cf10ede7330efbb4818f5896cd6fe3 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 25 Oct 2023 11:11:17 +0200 Subject: [PATCH 244/974] qemu-file: Make qemu_fflush() return errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This let us simplify code of this shape. qemu_fflush(f); int ret = qemu_file_get_error(f); if (ret) { return ret; } into: int ret = qemu_fflush(f); if (ret) { return ret; } I updated all callers where there is any error check. qemu_fclose() don't need to check for f->last_error because qemu_fflush() returns it at the beggining of the function. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Fabiano Rosas Signed-off-by: Juan Quintela Message-ID: <20231025091117.6342-13-quintela@redhat.com> Signed-off-by: Juan Quintela --- migration/colo.c | 11 +++-------- migration/migration.c | 7 +------ migration/qemu-file.c | 23 +++++++---------------- migration/qemu-file.h | 2 +- migration/ram.c | 22 +++++++--------------- migration/rdma.c | 4 +--- migration/savevm.c | 3 +-- 7 files changed, 21 insertions(+), 51 deletions(-) diff --git a/migration/colo.c b/migration/colo.c index 72f4f7b37e..4447e34914 100644 --- a/migration/colo.c +++ b/migration/colo.c @@ -314,9 +314,7 @@ static void colo_send_message(QEMUFile *f, COLOMessage msg, return; } qemu_put_be32(f, msg); - qemu_fflush(f); - - ret = qemu_file_get_error(f); + ret = qemu_fflush(f); if (ret < 0) { error_setg_errno(errp, -ret, "Can't send COLO message"); } @@ -335,9 +333,7 @@ static void colo_send_message_value(QEMUFile *f, COLOMessage msg, return; } qemu_put_be64(f, value); - qemu_fflush(f); - - ret = qemu_file_get_error(f); + ret = qemu_fflush(f); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to send value for message:%s", COLOMessage_str(msg)); @@ -483,8 +479,7 @@ static int colo_do_checkpoint_transaction(MigrationState *s, } qemu_put_buffer(s->to_dst_file, bioc->data, bioc->usage); - qemu_fflush(s->to_dst_file); - ret = qemu_file_get_error(s->to_dst_file); + ret = qemu_fflush(s->to_dst_file); if (ret < 0) { goto out; } diff --git a/migration/migration.c b/migration/migration.c index aa7b791833..6abcbefd9c 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -305,12 +305,7 @@ static int migrate_send_rp_message(MigrationIncomingState *mis, qemu_put_be16(mis->to_src_file, (unsigned int)message_type); qemu_put_be16(mis->to_src_file, len); qemu_put_buffer(mis->to_src_file, data, len); - qemu_fflush(mis->to_src_file); - - /* It's possible that qemu file got error during sending */ - ret = qemu_file_get_error(mis->to_src_file); - - return ret; + return qemu_fflush(mis->to_src_file); } /* Request one page from the source VM at the given start address. diff --git a/migration/qemu-file.c b/migration/qemu-file.c index 7e738743ce..d64500310d 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -262,14 +262,14 @@ static void qemu_iovec_release_ram(QEMUFile *f) * This will flush all pending data. If data was only partially flushed, it * will set an error state. */ -void qemu_fflush(QEMUFile *f) +int qemu_fflush(QEMUFile *f) { if (!qemu_file_is_writable(f)) { - return; + return f->last_error; } - if (qemu_file_get_error(f)) { - return; + if (f->last_error) { + return f->last_error; } if (f->iovcnt > 0) { Error *local_error = NULL; @@ -287,6 +287,7 @@ void qemu_fflush(QEMUFile *f) f->buf_index = 0; f->iovcnt = 0; + return f->last_error; } /* @@ -353,22 +354,12 @@ static ssize_t coroutine_mixed_fn qemu_fill_buffer(QEMUFile *f) */ int qemu_fclose(QEMUFile *f) { - int ret, ret2; - qemu_fflush(f); - ret = qemu_file_get_error(f); - - ret2 = qio_channel_close(f->ioc, NULL); + int ret = qemu_fflush(f); + int ret2 = qio_channel_close(f->ioc, NULL); if (ret >= 0) { ret = ret2; } g_clear_pointer(&f->ioc, object_unref); - - /* If any error was spotted before closing, we should report it - * instead of the close() return value. - */ - if (f->last_error) { - ret = f->last_error; - } error_free(f->last_error_obj); g_free(f); trace_qemu_file_fclose(); diff --git a/migration/qemu-file.h b/migration/qemu-file.h index 1b2f6b8d8f..1774116f79 100644 --- a/migration/qemu-file.h +++ b/migration/qemu-file.h @@ -71,7 +71,7 @@ void qemu_file_set_error_obj(QEMUFile *f, int ret, Error *err); void qemu_file_set_error(QEMUFile *f, int ret); int qemu_file_shutdown(QEMUFile *f); QEMUFile *qemu_file_get_return_path(QEMUFile *f); -void qemu_fflush(QEMUFile *f); +int qemu_fflush(QEMUFile *f); void qemu_file_set_blocking(QEMUFile *f, bool block); int qemu_file_get_to_fd(QEMUFile *f, int fd, size_t size); diff --git a/migration/ram.c b/migration/ram.c index cec9bd31d9..34724e8fe8 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -305,17 +305,15 @@ int64_t ramblock_recv_bitmap_send(QEMUFile *file, qemu_put_be64(file, size); qemu_put_buffer(file, (const uint8_t *)le_bitmap, size); + g_free(le_bitmap); /* * Mark as an end, in case the middle part is screwed up due to * some "mysterious" reason. */ qemu_put_be64(file, RAMBLOCK_RECV_BITMAP_ENDING); - qemu_fflush(file); - - g_free(le_bitmap); - - if (qemu_file_get_error(file)) { - return qemu_file_get_error(file); + int ret = qemu_fflush(file); + if (ret) { + return ret; } return size + sizeof(size); @@ -2996,9 +2994,7 @@ static int ram_save_setup(QEMUFile *f, void *opaque) } qemu_put_be64(f, RAM_SAVE_FLAG_EOS); - qemu_fflush(f); - - return 0; + return qemu_fflush(f); } /** @@ -3118,10 +3114,8 @@ out: } qemu_put_be64(f, RAM_SAVE_FLAG_EOS); - qemu_fflush(f); ram_transferred_add(8); - - ret = qemu_file_get_error(f); + ret = qemu_fflush(f); } if (ret < 0) { return ret; @@ -3196,9 +3190,7 @@ static int ram_save_complete(QEMUFile *f, void *opaque) qemu_put_be64(f, RAM_SAVE_FLAG_MULTIFD_FLUSH); } qemu_put_be64(f, RAM_SAVE_FLAG_EOS); - qemu_fflush(f); - - return 0; + return qemu_fflush(f); } static void ram_state_pending_estimate(void *opaque, uint64_t *must_precopy, diff --git a/migration/rdma.c b/migration/rdma.c index e3493e3b3e..2938db4f64 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -3853,9 +3853,7 @@ int rdma_registration_start(QEMUFile *f, uint64_t flags) trace_rdma_registration_start(flags); qemu_put_be64(f, RAM_SAVE_FLAG_HOOK); - qemu_fflush(f); - - return 0; + return qemu_fflush(f); } /* diff --git a/migration/savevm.c b/migration/savevm.c index 9ec78abd53..c7835e9c73 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1583,8 +1583,7 @@ int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only, } flush: - qemu_fflush(f); - return 0; + return qemu_fflush(f); } /* Give an estimate of the amount left to be transferred, From 8b097fd6b06ec295faefd4f30f96f8709abc9605 Mon Sep 17 00:00:00 2001 From: Andrey Drobyshev Date: Tue, 19 Sep 2023 19:57:57 +0300 Subject: [PATCH 245/974] qemu-img: rebase: stop when reaching EOF of old backing file In case when we're rebasing within one backing chain, and when target image is larger than old backing file, bdrv_is_allocated_above() ends up setting *pnum = 0. As a result, target offset isn't getting incremented, and we get stuck in an infinite for loop. Let's detect this case and proceed further down the loop body, as the offsets beyond the old backing size need to be explicitly zeroed. Signed-off-by: Andrey Drobyshev Reviewed-by: Denis V. Lunev Reviewed-by: Hanna Czenczek Message-ID: <20230919165804.439110-2-andrey.drobyshev@virtuozzo.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- qemu-img.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/qemu-img.c b/qemu-img.c index 585b65640f..2b2a3a86ca 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -3814,6 +3814,8 @@ static int img_rebase(int argc, char **argv) } if (prefix_chain_bs) { + uint64_t bytes = n; + /* * If cluster wasn't changed since prefix_chain, we don't need * to take action @@ -3826,9 +3828,18 @@ static int img_rebase(int argc, char **argv) strerror(-ret)); goto out; } - if (!ret) { + if (!ret && n) { continue; } + if (!n) { + /* + * If we've reached EOF of the old backing, it means that + * offsets beyond the old backing size were read as zeroes. + * Now we will need to explicitly zero the cluster in + * order to preserve that state after the rebase. + */ + n = bytes; + } } /* From 827171c3180533f4ad0bc338ea166f401bb5d348 Mon Sep 17 00:00:00 2001 From: Andrey Drobyshev Date: Tue, 19 Sep 2023 19:57:58 +0300 Subject: [PATCH 246/974] qemu-iotests: 024: add rebasing test case for overlay_size > backing_size Before previous commit, rebase was getting infitely stuck in case of rebasing within the same backing chain and when overlay_size > backing_size. Let's add this case to the rebasing test 024 to make sure it doesn't break again. Signed-off-by: Andrey Drobyshev Reviewed-by: Denis V. Lunev Reviewed-by: Hanna Czenczek Message-ID: <20230919165804.439110-3-andrey.drobyshev@virtuozzo.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- tests/qemu-iotests/024 | 57 ++++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/024.out | 30 ++++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/tests/qemu-iotests/024 b/tests/qemu-iotests/024 index 25a564a150..98a7c8fd65 100755 --- a/tests/qemu-iotests/024 +++ b/tests/qemu-iotests/024 @@ -199,6 +199,63 @@ echo # $BASE_OLD and $BASE_NEW) $QEMU_IMG map "$OVERLAY" | _filter_qemu_img_map +# Check that rebase within the chain is working when +# overlay_size > old_backing_size +# +# base_new <-- base_old <-- overlay +# +# Backing (new): 11 11 11 11 11 +# Backing (old): 22 22 22 22 +# Overlay: -- -- -- -- -- +# +# As a result, overlay should contain data identical to base_old, with the +# last cluster remaining unallocated. + +echo +echo "=== Test rebase within one backing chain ===" +echo + +echo "Creating backing chain" +echo + +TEST_IMG=$BASE_NEW _make_test_img $(( CLUSTER_SIZE * 5 )) +TEST_IMG=$BASE_OLD _make_test_img -b "$BASE_NEW" -F $IMGFMT \ + $(( CLUSTER_SIZE * 4 )) +TEST_IMG=$OVERLAY _make_test_img -b "$BASE_OLD" -F $IMGFMT \ + $(( CLUSTER_SIZE * 5 )) + +echo +echo "Fill backing files with data" +echo + +$QEMU_IO "$BASE_NEW" -c "write -P 0x11 0 $(( CLUSTER_SIZE * 5 ))" \ + | _filter_qemu_io +$QEMU_IO "$BASE_OLD" -c "write -P 0x22 0 $(( CLUSTER_SIZE * 4 ))" \ + | _filter_qemu_io + +echo +echo "Check the last cluster is zeroed in overlay before the rebase" +echo +$QEMU_IO "$OVERLAY" -c "read -P 0x00 $(( CLUSTER_SIZE * 4 )) $CLUSTER_SIZE" \ + | _filter_qemu_io + +echo +echo "Rebase onto another image in the same chain" +echo + +$QEMU_IMG rebase -b "$BASE_NEW" -F $IMGFMT "$OVERLAY" + +echo "Verify that data is read the same before and after rebase" +echo + +# Verify the first 4 clusters are still read the same as in the old base +$QEMU_IO "$OVERLAY" -c "read -P 0x22 0 $(( CLUSTER_SIZE * 4 ))" \ + | _filter_qemu_io +# Verify the last cluster still reads as zeroes +$QEMU_IO "$OVERLAY" -c "read -P 0x00 $(( CLUSTER_SIZE * 4 )) $CLUSTER_SIZE" \ + | _filter_qemu_io + +echo # success, all done echo "*** done" diff --git a/tests/qemu-iotests/024.out b/tests/qemu-iotests/024.out index 973a5a3711..245fe8b1d1 100644 --- a/tests/qemu-iotests/024.out +++ b/tests/qemu-iotests/024.out @@ -171,4 +171,34 @@ read 65536/65536 bytes at offset 196608 Offset Length File 0 0x30000 TEST_DIR/subdir/t.IMGFMT 0x30000 0x10000 TEST_DIR/subdir/t.IMGFMT.base_new + +=== Test rebase within one backing chain === + +Creating backing chain + +Formatting 'TEST_DIR/subdir/t.IMGFMT.base_new', fmt=IMGFMT size=327680 +Formatting 'TEST_DIR/subdir/t.IMGFMT.base_old', fmt=IMGFMT size=262144 backing_file=TEST_DIR/subdir/t.IMGFMT.base_new backing_fmt=IMGFMT +Formatting 'TEST_DIR/subdir/t.IMGFMT', fmt=IMGFMT size=327680 backing_file=TEST_DIR/subdir/t.IMGFMT.base_old backing_fmt=IMGFMT + +Fill backing files with data + +wrote 327680/327680 bytes at offset 0 +320 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 262144/262144 bytes at offset 0 +256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +Check the last cluster is zeroed in overlay before the rebase + +read 65536/65536 bytes at offset 262144 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +Rebase onto another image in the same chain + +Verify that data is read the same before and after rebase + +read 262144/262144 bytes at offset 0 +256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 262144 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + *** done From ce8b8f9fe79b09fe6c1d207274b60fcb6a6e08a9 Mon Sep 17 00:00:00 2001 From: Andrey Drobyshev Date: Tue, 19 Sep 2023 19:57:59 +0300 Subject: [PATCH 247/974] qemu-img: rebase: use backing files' BlockBackend for buffer alignment Since commit bb1c05973cf ("qemu-img: Use qemu_blockalign"), buffers for the data read from the old and new backing files are aligned using BlockDriverState (or BlockBackend later on) referring to the target image. However, this isn't quite right, because buf_new is only being used for reading from the new backing, while buf_old is being used for both reading from the old backing and writing to the target. Let's take that into account and use more appropriate values as alignments. Signed-off-by: Andrey Drobyshev Message-ID: <20230919165804.439110-4-andrey.drobyshev@virtuozzo.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- qemu-img.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index 2b2a3a86ca..e61d996e0f 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -3759,8 +3759,13 @@ static int img_rebase(int argc, char **argv) int64_t n; float local_progress = 0; - buf_old = blk_blockalign(blk, IO_BUF_SIZE); - buf_new = blk_blockalign(blk, IO_BUF_SIZE); + if (blk_old_backing && bdrv_opt_mem_align(blk_bs(blk_old_backing)) > + bdrv_opt_mem_align(blk_bs(blk))) { + buf_old = blk_blockalign(blk_old_backing, IO_BUF_SIZE); + } else { + buf_old = blk_blockalign(blk, IO_BUF_SIZE); + } + buf_new = blk_blockalign(blk_new_backing, IO_BUF_SIZE); size = blk_getlength(blk); if (size < 0) { From c20c8ae70063a88964c14e651fd901d1290d26b2 Mon Sep 17 00:00:00 2001 From: Andrey Drobyshev Date: Tue, 19 Sep 2023 19:58:00 +0300 Subject: [PATCH 248/974] qemu-img: add chunk size parameter to compare_buffers() Add @chsize param to the function which, if non-zero, would represent the chunk size to be used for comparison. If it's zero, then BDRV_SECTOR_SIZE is used as default chunk size, which is the previous behaviour. In particular, we're going to use this param in img_rebase() to make the write requests aligned to a predefined alignment value. Signed-off-by: Andrey Drobyshev Reviewed-by: Eric Blake Reviewed-by: Hanna Czenczek Message-ID: <20230919165804.439110-5-andrey.drobyshev@virtuozzo.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- qemu-img.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index e61d996e0f..a64a664a37 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -1274,23 +1274,29 @@ static int is_allocated_sectors_min(const uint8_t *buf, int n, int *pnum, } /* - * Compares two buffers sector by sector. Returns 0 if the first - * sector of each buffer matches, non-zero otherwise. + * Compares two buffers chunk by chunk, where @chsize is the chunk size. + * If @chsize is 0, default chunk size of BDRV_SECTOR_SIZE is used. + * Returns 0 if the first chunk of each buffer matches, non-zero otherwise. * - * pnum is set to the sector-aligned size of the buffer prefix that - * has the same matching status as the first sector. + * @pnum is set to the size of the buffer prefix aligned to @chsize that + * has the same matching status as the first chunk. */ static int compare_buffers(const uint8_t *buf1, const uint8_t *buf2, - int64_t bytes, int64_t *pnum) + int64_t bytes, uint64_t chsize, int64_t *pnum) { bool res; - int64_t i = MIN(bytes, BDRV_SECTOR_SIZE); + int64_t i; assert(bytes > 0); + if (!chsize) { + chsize = BDRV_SECTOR_SIZE; + } + i = MIN(bytes, chsize); + res = !!memcmp(buf1, buf2, i); while (i < bytes) { - int64_t len = MIN(bytes - i, BDRV_SECTOR_SIZE); + int64_t len = MIN(bytes - i, chsize); if (!!memcmp(buf1 + i, buf2 + i, len) != res) { break; @@ -1559,7 +1565,7 @@ static int img_compare(int argc, char **argv) ret = 4; goto out; } - ret = compare_buffers(buf1, buf2, chunk, &pnum); + ret = compare_buffers(buf1, buf2, chunk, 0, &pnum); if (ret || pnum != chunk) { qprintf(quiet, "Content mismatch at offset %" PRId64 "!\n", offset + (ret ? 0 : pnum)); @@ -3887,7 +3893,7 @@ static int img_rebase(int argc, char **argv) int64_t pnum; if (compare_buffers(buf_old + written, buf_new + written, - n - written, &pnum)) + n - written, 0, &pnum)) { if (buf_old_is_zero) { ret = blk_pwrite_zeroes(blk, offset + written, pnum, 0); From 12df580b3b7f1c3d08254d4e50e787fe1cae56b4 Mon Sep 17 00:00:00 2001 From: Andrey Drobyshev Date: Tue, 19 Sep 2023 19:58:01 +0300 Subject: [PATCH 249/974] qemu-img: rebase: avoid unnecessary COW operations When rebasing an image from one backing file to another, we need to compare data from old and new backings. If the diff between that data happens to be unaligned to the target cluster size, we might end up doing partial writes, which would lead to copy-on-write and additional IO. Consider the following simple case (virtual_size == cluster_size == 64K): base <-- inc1 <-- inc2 qemu-io -c "write -P 0xaa 0 32K" base.qcow2 qemu-io -c "write -P 0xcc 32K 32K" base.qcow2 qemu-io -c "write -P 0xbb 0 32K" inc1.qcow2 qemu-io -c "write -P 0xcc 32K 32K" inc1.qcow2 qemu-img rebase -f qcow2 -b base.qcow2 -F qcow2 inc2.qcow2 While doing rebase, we'll write a half of the cluster to inc2, and block layer will have to read the 2nd half of the same cluster from the base image inc1 while doing this write operation, although the whole cluster is already read earlier to perform data comparison. In order to avoid these unnecessary IO cycles, let's make sure every write request is aligned to the overlay subcluster boundaries. Using subcluster size is universal as for the images which don't have them this size equals to the cluster size. so in any case we end up aligning to the smallest unit of allocation. Signed-off-by: Andrey Drobyshev Message-ID: <20230919165804.439110-6-andrey.drobyshev@virtuozzo.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- qemu-img.c | 74 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 20 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index a64a664a37..01de77295e 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -3530,6 +3530,7 @@ static int img_rebase(int argc, char **argv) uint8_t *buf_new = NULL; BlockDriverState *bs = NULL, *prefix_chain_bs = NULL; BlockDriverState *unfiltered_bs; + BlockDriverInfo bdi = {0}; char *filename; const char *fmt, *cache, *src_cache, *out_basefmt, *out_baseimg; int c, flags, src_flags, ret; @@ -3540,6 +3541,7 @@ static int img_rebase(int argc, char **argv) bool quiet = false; Error *local_err = NULL; bool image_opts = false; + int64_t write_align; /* Parse commandline parameters */ fmt = NULL; @@ -3663,6 +3665,20 @@ static int img_rebase(int argc, char **argv) } } + /* + * We need overlay subcluster size to make sure write requests are + * aligned. + */ + ret = bdrv_get_info(unfiltered_bs, &bdi); + if (ret < 0) { + error_report("could not get block driver info"); + goto out; + } else if (bdi.subcluster_size == 0) { + bdi.subcluster_size = 1; + } + + write_align = bdi.subcluster_size; + /* For safe rebasing we need to compare old and new backing file */ if (!unsafe) { QDict *options = NULL; @@ -3762,7 +3778,7 @@ static int img_rebase(int argc, char **argv) int64_t old_backing_size = 0; int64_t new_backing_size = 0; uint64_t offset; - int64_t n; + int64_t n, n_old = 0, n_new = 0; float local_progress = 0; if (blk_old_backing && bdrv_opt_mem_align(blk_bs(blk_old_backing)) > @@ -3808,7 +3824,8 @@ static int img_rebase(int argc, char **argv) } for (offset = 0; offset < size; offset += n) { - bool buf_old_is_zero = false; + bool old_backing_eof = false; + int64_t n_alloc; /* How many bytes can we handle with the next read? */ n = MIN(IO_BUF_SIZE, size - offset); @@ -3853,33 +3870,46 @@ static int img_rebase(int argc, char **argv) } } + /* + * At this point we know that the region [offset; offset + n) + * is unallocated within the target image. This region might be + * unaligned to the target image's (sub)cluster boundaries, as + * old backing may have smaller clusters (or have subclusters). + * We extend it to the aligned boundaries to avoid CoW on + * partial writes in blk_pwrite(), + */ + n += offset - QEMU_ALIGN_DOWN(offset, write_align); + offset = QEMU_ALIGN_DOWN(offset, write_align); + n += QEMU_ALIGN_UP(offset + n, write_align) - (offset + n); + n = MIN(n, size - offset); + assert(!bdrv_is_allocated(unfiltered_bs, offset, n, &n_alloc) && + n_alloc == n); + + /* + * Much like with the target image, we'll try to read as much + * of the old and new backings as we can. + */ + n_old = MIN(n, MAX(0, old_backing_size - (int64_t) offset)); + n_new = MIN(n, MAX(0, new_backing_size - (int64_t) offset)); + /* * Read old and new backing file and take into consideration that * backing files may be smaller than the COW image. */ - if (offset >= old_backing_size) { - memset(buf_old, 0, n); - buf_old_is_zero = true; + memset(buf_old + n_old, 0, n - n_old); + if (!n_old) { + old_backing_eof = true; } else { - if (offset + n > old_backing_size) { - n = old_backing_size - offset; - } - - ret = blk_pread(blk_old_backing, offset, n, buf_old, 0); + ret = blk_pread(blk_old_backing, offset, n_old, buf_old, 0); if (ret < 0) { error_report("error while reading from old backing file"); goto out; } } - if (offset >= new_backing_size || !blk_new_backing) { - memset(buf_new, 0, n); - } else { - if (offset + n > new_backing_size) { - n = new_backing_size - offset; - } - - ret = blk_pread(blk_new_backing, offset, n, buf_new, 0); + memset(buf_new + n_new, 0, n - n_new); + if (n_new) { + ret = blk_pread(blk_new_backing, offset, n_new, buf_new, 0); if (ret < 0) { error_report("error while reading from new backing file"); goto out; @@ -3893,11 +3923,12 @@ static int img_rebase(int argc, char **argv) int64_t pnum; if (compare_buffers(buf_old + written, buf_new + written, - n - written, 0, &pnum)) + n - written, write_align, &pnum)) { - if (buf_old_is_zero) { + if (old_backing_eof) { ret = blk_pwrite_zeroes(blk, offset + written, pnum, 0); } else { + assert(written + pnum <= IO_BUF_SIZE); ret = blk_pwrite(blk, offset + written, pnum, buf_old + written, 0); } @@ -3909,6 +3940,9 @@ static int img_rebase(int argc, char **argv) } written += pnum; + if (offset + written >= old_backing_size) { + old_backing_eof = true; + } } qemu_progress_print(local_progress, 100); } From f93e65ee5175a8f8449913553100747aaafecc4d Mon Sep 17 00:00:00 2001 From: Andrey Drobyshev Date: Tue, 19 Sep 2023 19:58:02 +0300 Subject: [PATCH 250/974] iotests/{024, 271}: add testcases for qemu-img rebase As the previous commit changes the logic of "qemu-img rebase" (it's using write alignment now), let's add a couple more test cases which would ensure it works correctly. In particular, the following scenarios: 024: add test case for rebase within one backing chain when the overlay cluster size > backings cluster size; 271: add test case for rebase images that contain subclusters. Check that no extra allocations are being made. Signed-off-by: Andrey Drobyshev Reviewed-by: Hanna Czenczek Message-ID: <20230919165804.439110-7-andrey.drobyshev@virtuozzo.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- tests/qemu-iotests/024 | 60 ++++++++++++++++++++++++++++++++++ tests/qemu-iotests/024.out | 43 +++++++++++++++++++++++++ tests/qemu-iotests/271 | 66 ++++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/271.out | 42 ++++++++++++++++++++++++ 4 files changed, 211 insertions(+) diff --git a/tests/qemu-iotests/024 b/tests/qemu-iotests/024 index 98a7c8fd65..285f17e79f 100755 --- a/tests/qemu-iotests/024 +++ b/tests/qemu-iotests/024 @@ -257,6 +257,66 @@ $QEMU_IO "$OVERLAY" -c "read -P 0x00 $(( CLUSTER_SIZE * 4 )) $CLUSTER_SIZE" \ echo +# Check that rebase within the chain is working when +# overlay cluster size > backings cluster size +# (here overlay cluster size == 2 * backings cluster size) +# +# base_new <-- base_old <-- overlay +# +# Backing (new): -- -- -- -- -- -- +# Backing (old): -- 11 -- -- 22 -- +# Overlay: |-- --|-- --|-- --| +# +# We should end up having 1st and 3rd cluster allocated, and their halves +# being read as zeroes. + +echo +echo "=== Test rebase with different cluster sizes ===" +echo + +echo "Creating backing chain" +echo + +TEST_IMG=$BASE_NEW _make_test_img $(( CLUSTER_SIZE * 6 )) +TEST_IMG=$BASE_OLD _make_test_img -b "$BASE_NEW" -F $IMGFMT \ + $(( CLUSTER_SIZE * 6 )) +CLUSTER_SIZE=$(( CLUSTER_SIZE * 2 )) TEST_IMG=$OVERLAY \ + _make_test_img -b "$BASE_OLD" -F $IMGFMT $(( CLUSTER_SIZE * 6 )) + +TEST_IMG=$OVERLAY _img_info + +echo +echo "Fill backing files with data" +echo + +$QEMU_IO "$BASE_OLD" -c "write -P 0x11 $CLUSTER_SIZE $CLUSTER_SIZE" \ + -c "write -P 0x22 $(( CLUSTER_SIZE * 4 )) $CLUSTER_SIZE" \ + | _filter_qemu_io + +echo +echo "Rebase onto another image in the same chain" +echo + +$QEMU_IMG rebase -b "$BASE_NEW" -F $IMGFMT "$OVERLAY" + +echo "Verify that data is read the same before and after rebase" +echo + +$QEMU_IO "$OVERLAY" -c "read -P 0x00 0 $CLUSTER_SIZE" \ + -c "read -P 0x11 $CLUSTER_SIZE $CLUSTER_SIZE" \ + -c "read -P 0x00 $(( CLUSTER_SIZE * 2 )) $(( CLUSTER_SIZE * 2 ))" \ + -c "read -P 0x22 $(( CLUSTER_SIZE * 4 )) $CLUSTER_SIZE" \ + -c "read -P 0x00 $(( CLUSTER_SIZE * 5 )) $CLUSTER_SIZE" \ + | _filter_qemu_io + +echo +echo "Verify that untouched cluster remains unallocated" +echo + +$QEMU_IMG map "$OVERLAY" | _filter_qemu_img_map + +echo + # success, all done echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/024.out b/tests/qemu-iotests/024.out index 245fe8b1d1..e1e8eea863 100644 --- a/tests/qemu-iotests/024.out +++ b/tests/qemu-iotests/024.out @@ -201,4 +201,47 @@ read 262144/262144 bytes at offset 0 read 65536/65536 bytes at offset 262144 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +=== Test rebase with different cluster sizes === + +Creating backing chain + +Formatting 'TEST_DIR/subdir/t.IMGFMT.base_new', fmt=IMGFMT size=393216 +Formatting 'TEST_DIR/subdir/t.IMGFMT.base_old', fmt=IMGFMT size=393216 backing_file=TEST_DIR/subdir/t.IMGFMT.base_new backing_fmt=IMGFMT +Formatting 'TEST_DIR/subdir/t.IMGFMT', fmt=IMGFMT size=393216 backing_file=TEST_DIR/subdir/t.IMGFMT.base_old backing_fmt=IMGFMT +image: TEST_DIR/subdir/t.IMGFMT +file format: IMGFMT +virtual size: 384 KiB (393216 bytes) +cluster_size: 131072 +backing file: TEST_DIR/subdir/t.IMGFMT.base_old +backing file format: IMGFMT + +Fill backing files with data + +wrote 65536/65536 bytes at offset 65536 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 262144 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +Rebase onto another image in the same chain + +Verify that data is read the same before and after rebase + +read 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 65536 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 131072/131072 bytes at offset 131072 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 262144 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 327680 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +Verify that untouched cluster remains unallocated + +Offset Length File +0 0x20000 TEST_DIR/subdir/t.IMGFMT +0x40000 0x20000 TEST_DIR/subdir/t.IMGFMT + *** done diff --git a/tests/qemu-iotests/271 b/tests/qemu-iotests/271 index c7c2cadda0..e243f57ba7 100755 --- a/tests/qemu-iotests/271 +++ b/tests/qemu-iotests/271 @@ -899,6 +899,72 @@ _concurrent_io | $QEMU_IO | _filter_qemu_io | \ sed -e 's/\(20480\|40960\)/OFFSET/' _concurrent_verify | $QEMU_IO | _filter_qemu_io +############################################################ +############################################################ +############################################################ + +echo +echo "### Rebase of qcow2 images with subclusters ###" +echo + +l2_offset=$((0x400000)) + +# Check that rebase operation preserve holes between allocated subclusters +# within one cluster (i.e. does not allocate extra space). Check that the +# data is preserved as well. +# +# Base (new backing): -- -- -- ... -- -- -- +# Mid (old backing): -- 11 -- ... -- 22 -- +# Top: -- -- -- ... -- -- -- + +echo "### Preservation of unallocated holes after rebase ###" +echo + +echo "# create backing chain" +echo + +TEST_IMG="$TEST_IMG.base" _make_test_img -o cluster_size=1M,extended_l2=on 1M +TEST_IMG="$TEST_IMG.mid" _make_test_img -o cluster_size=1M,extended_l2=on \ + -b "$TEST_IMG.base" -F qcow2 1M +TEST_IMG="$TEST_IMG.top" _make_test_img -o cluster_size=1M,extended_l2=on \ + -b "$TEST_IMG.mid" -F qcow2 1M + +echo +echo "# fill old backing with data (separate subclusters within cluster)" +echo + +$QEMU_IO -c "write -P 0x11 32k 32k" \ + -c "write -P 0x22 $(( 30 * 32 ))k 32k" \ + "$TEST_IMG.mid" | _filter_qemu_io + +echo +echo "# rebase topmost image onto the new backing" +echo + +$QEMU_IMG rebase -b "$TEST_IMG.base" -F qcow2 "$TEST_IMG.top" + +echo "# verify that data is read the same before and after rebase" +echo + +$QEMU_IO -c "read -P 0x00 0 32k" \ + -c "read -P 0x11 32k 32k" \ + -c "read -P 0x00 64k $(( 28 * 32 ))k" \ + -c "read -P 0x22 $(( 30 * 32 ))k 32k" \ + -c "read -P 0x00 $(( 31 * 32 ))k 32k" \ + "$TEST_IMG.top" | _filter_qemu_io + +echo +echo "# verify that only selected subclusters remain allocated" +echo + +$QEMU_IMG map "$TEST_IMG.top" | _filter_testdir + +echo +echo "# verify image bitmap" +echo + +TEST_IMG="$TEST_IMG.top" alloc="1 30" zero="" _verify_l2_bitmap 0 + # success, all done echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/271.out b/tests/qemu-iotests/271.out index 5be780de76..c335a6c608 100644 --- a/tests/qemu-iotests/271.out +++ b/tests/qemu-iotests/271.out @@ -723,4 +723,46 @@ wrote 2048/2048 bytes at offset OFFSET 2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 2048/2048 bytes at offset OFFSET 2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +### Rebase of qcow2 images with subclusters ### + +### Preservation of unallocated holes after rebase ### + +# create backing chain + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=1048576 +Formatting 'TEST_DIR/t.IMGFMT.mid', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT +Formatting 'TEST_DIR/t.IMGFMT.top', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.mid backing_fmt=IMGFMT + +# fill old backing with data (separate subclusters within cluster) + +wrote 32768/32768 bytes at offset 32768 +32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 32768/32768 bytes at offset 983040 +32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# rebase topmost image onto the new backing + +# verify that data is read the same before and after rebase + +read 32768/32768 bytes at offset 0 +32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 32768/32768 bytes at offset 32768 +32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 917504/917504 bytes at offset 65536 +896 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 32768/32768 bytes at offset 983040 +32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 32768/32768 bytes at offset 1015808 +32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# verify that only selected subclusters remain allocated + +Offset Length Mapped to File +0x8000 0x8000 0x508000 TEST_DIR/t.qcow2.top +0xf0000 0x8000 0x5f0000 TEST_DIR/t.qcow2.top + +# verify image bitmap + +L2 entry #0: 0x8000000000500000 0000000040000002 *** done From 26ea27898b2b4e322ab932fb61de8f32cd9e4d72 Mon Sep 17 00:00:00 2001 From: Andrey Drobyshev Date: Tue, 19 Sep 2023 19:58:03 +0300 Subject: [PATCH 251/974] qemu-img: add compression option to rebase subcommand If we rebase an image whose backing file has compressed clusters, we might end up wasting disk space since the copied clusters are now uncompressed. In order to have better control over this, let's add "--compress" option to the "qemu-img rebase" command. Note that this option affects only the clusters which are actually being copied from the original backing file. The clusters which were uncompressed in the target image will remain so. Signed-off-by: Andrey Drobyshev Reviewed-by: Denis V. Lunev Reviewed-by: Hanna Czenczek Message-ID: <20230919165804.439110-8-andrey.drobyshev@virtuozzo.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- docs/tools/qemu-img.rst | 6 ++++-- qemu-img-cmds.hx | 4 ++-- qemu-img.c | 26 ++++++++++++++++++++------ 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/docs/tools/qemu-img.rst b/docs/tools/qemu-img.rst index ca5a2773cf..4459c065f1 100644 --- a/docs/tools/qemu-img.rst +++ b/docs/tools/qemu-img.rst @@ -667,7 +667,7 @@ Command description: List, apply, create or delete snapshots in image *FILENAME*. -.. option:: rebase [--object OBJECTDEF] [--image-opts] [-U] [-q] [-f FMT] [-t CACHE] [-T SRC_CACHE] [-p] [-u] -b BACKING_FILE [-F BACKING_FMT] FILENAME +.. option:: rebase [--object OBJECTDEF] [--image-opts] [-U] [-q] [-f FMT] [-t CACHE] [-T SRC_CACHE] [-p] [-u] [-c] -b BACKING_FILE [-F BACKING_FMT] FILENAME Changes the backing file of an image. Only the formats ``qcow2`` and ``qed`` support changing the backing file. @@ -694,7 +694,9 @@ Command description: In order to achieve this, any clusters that differ between *BACKING_FILE* and the old backing file of *FILENAME* are merged - into *FILENAME* before actually changing the backing file. + into *FILENAME* before actually changing the backing file. With the + ``-c`` option specified, the clusters which are being merged (but not + the entire *FILENAME* image) are compressed when written. Note that the safe mode is an expensive operation, comparable to converting an image. It only works if the old backing file still diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index 1b1dab5b17..068692d13e 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -88,9 +88,9 @@ SRST ERST DEF("rebase", img_rebase, - "rebase [--object objectdef] [--image-opts] [-U] [-q] [-f fmt] [-t cache] [-T src_cache] [-p] [-u] -b backing_file [-F backing_fmt] filename") + "rebase [--object objectdef] [--image-opts] [-U] [-q] [-f fmt] [-t cache] [-T src_cache] [-p] [-u] [-c] -b backing_file [-F backing_fmt] filename") SRST -.. option:: rebase [--object OBJECTDEF] [--image-opts] [-U] [-q] [-f FMT] [-t CACHE] [-T SRC_CACHE] [-p] [-u] -b BACKING_FILE [-F BACKING_FMT] FILENAME +.. option:: rebase [--object OBJECTDEF] [--image-opts] [-U] [-q] [-f FMT] [-t CACHE] [-T SRC_CACHE] [-p] [-u] [-c] -b BACKING_FILE [-F BACKING_FMT] FILENAME ERST DEF("resize", img_resize, diff --git a/qemu-img.c b/qemu-img.c index 01de77295e..369c2e8ddf 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -3534,11 +3534,13 @@ static int img_rebase(int argc, char **argv) char *filename; const char *fmt, *cache, *src_cache, *out_basefmt, *out_baseimg; int c, flags, src_flags, ret; + BdrvRequestFlags write_flags = 0; bool writethrough, src_writethrough; int unsafe = 0; bool force_share = false; int progress = 0; bool quiet = false; + bool compress = false; Error *local_err = NULL; bool image_opts = false; int64_t write_align; @@ -3555,9 +3557,10 @@ static int img_rebase(int argc, char **argv) {"object", required_argument, 0, OPTION_OBJECT}, {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, {"force-share", no_argument, 0, 'U'}, + {"compress", no_argument, 0, 'c'}, {0, 0, 0, 0} }; - c = getopt_long(argc, argv, ":hf:F:b:upt:T:qU", + c = getopt_long(argc, argv, ":hf:F:b:upt:T:qUc", long_options, NULL); if (c == -1) { break; @@ -3605,6 +3608,9 @@ static int img_rebase(int argc, char **argv) case 'U': force_share = true; break; + case 'c': + compress = true; + break; } } @@ -3657,6 +3663,14 @@ static int img_rebase(int argc, char **argv) unfiltered_bs = bdrv_skip_filters(bs); + if (compress && !block_driver_can_compress(unfiltered_bs->drv)) { + error_report("Compression not supported for this file format"); + ret = -1; + goto out; + } else if (compress) { + write_flags |= BDRV_REQ_WRITE_COMPRESSED; + } + if (out_basefmt != NULL) { if (bdrv_find_format(out_basefmt) == NULL) { error_report("Invalid format name: '%s'", out_basefmt); @@ -3666,18 +3680,18 @@ static int img_rebase(int argc, char **argv) } /* - * We need overlay subcluster size to make sure write requests are - * aligned. + * We need overlay subcluster size (or cluster size in case writes are + * compressed) to make sure write requests are aligned. */ ret = bdrv_get_info(unfiltered_bs, &bdi); if (ret < 0) { error_report("could not get block driver info"); goto out; } else if (bdi.subcluster_size == 0) { - bdi.subcluster_size = 1; + bdi.cluster_size = bdi.subcluster_size = 1; } - write_align = bdi.subcluster_size; + write_align = compress ? bdi.cluster_size : bdi.subcluster_size; /* For safe rebasing we need to compare old and new backing file */ if (!unsafe) { @@ -3930,7 +3944,7 @@ static int img_rebase(int argc, char **argv) } else { assert(written + pnum <= IO_BUF_SIZE); ret = blk_pwrite(blk, offset + written, pnum, - buf_old + written, 0); + buf_old + written, write_flags); } if (ret < 0) { error_report("Error while writing to COW image: %s", From 87fe52ceca697e2ff8d42e881538219ced86f760 Mon Sep 17 00:00:00 2001 From: Andrey Drobyshev Date: Tue, 19 Sep 2023 19:58:04 +0300 Subject: [PATCH 252/974] iotests: add tests for "qemu-img rebase" with compression The test cases considered so far: 314 (new test suite): 1. Check that compression mode isn't compatible with "-f raw" (raw format doesn't support compression). 2. Check that rebasing an image onto no backing file preserves the data and writes the copied clusters actually compressed. 3. Same as 2, but with a raw backing file (i.e. the clusters copied from the backing are originally uncompressed -- we check they end up compressed after being merged). 4. Remove a single delta from a backing chain, perform the same checks as in 2. 5. Check that even when backing and overlay are initially uncompressed, copied clusters end up compressed when rebase with compression is performed. 271: 1. Check that when target image has subclusters, rebase with compression will make an entire cluster containing the written subcluster compressed. Signed-off-by: Andrey Drobyshev Reviewed-by: Hanna Czenczek Message-ID: <20230919165804.439110-9-andrey.drobyshev@virtuozzo.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- tests/qemu-iotests/271 | 65 +++++++++++++++ tests/qemu-iotests/271.out | 40 +++++++++ tests/qemu-iotests/314 | 165 +++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/314.out | 75 +++++++++++++++++ 4 files changed, 345 insertions(+) create mode 100755 tests/qemu-iotests/314 create mode 100644 tests/qemu-iotests/314.out diff --git a/tests/qemu-iotests/271 b/tests/qemu-iotests/271 index e243f57ba7..59a6fafa2f 100755 --- a/tests/qemu-iotests/271 +++ b/tests/qemu-iotests/271 @@ -965,6 +965,71 @@ echo TEST_IMG="$TEST_IMG.top" alloc="1 30" zero="" _verify_l2_bitmap 0 +# Check that rebase with compression works correctly with images containing +# subclusters. When compression is enabled and we allocate a new +# subcluster within the target (overlay) image, we expect the entire cluster +# containing that subcluster to become compressed. +# +# Here we expect 1st and 3rd clusters of the top (overlay) image to become +# compressed after the rebase, while cluster 2 to remain unallocated and +# be read from the base (new backing) image. +# +# Base (new backing): |-- -- .. -- --|11 11 .. 11 11|-- -- .. -- --| +# Mid (old backing): |-- -- .. -- 22|-- -- .. -- --|33 -- .. -- --| +# Top: |-- -- .. -- --|-- -- -- -- --|-- -- .. -- --| + +echo +echo "### Rebase with compression for images with subclusters ###" +echo + +echo "# create backing chain" +echo + +TEST_IMG="$TEST_IMG.base" _make_test_img -o cluster_size=1M,extended_l2=on 3M +TEST_IMG="$TEST_IMG.mid" _make_test_img -o cluster_size=1M,extended_l2=on \ + -b "$TEST_IMG.base" -F qcow2 3M +TEST_IMG="$TEST_IMG.top" _make_test_img -o cluster_size=1M,extended_l2=on \ + -b "$TEST_IMG.mid" -F qcow2 3M + +echo +echo "# fill old and new backing with data" +echo + +$QEMU_IO -c "write -P 0x11 1M 1M" "$TEST_IMG.base" | _filter_qemu_io +$QEMU_IO -c "write -P 0x22 $(( 31 * 32 ))k 32k" \ + -c "write -P 0x33 $(( 64 * 32 ))k 32k" \ + "$TEST_IMG.mid" | _filter_qemu_io + +echo +echo "# rebase topmost image onto the new backing, with compression" +echo + +$QEMU_IMG rebase -c -b "$TEST_IMG.base" -F qcow2 "$TEST_IMG.top" + +echo "# verify that the 1st and 3rd clusters've become compressed" +echo + +$QEMU_IMG map --output=json "$TEST_IMG.top" | _filter_testdir + +echo +echo "# verify that data is read the same before and after rebase" +echo + +$QEMU_IO -c "read -P 0x22 $(( 31 * 32 ))k 32k" \ + -c "read -P 0x11 1M 1M" \ + -c "read -P 0x33 $(( 64 * 32 ))k 32k" \ + "$TEST_IMG.top" | _filter_qemu_io + +echo +echo "# verify image bitmap" +echo + +# For compressed clusters bitmap is always 0. For unallocated cluster +# there should be no entry at all, thus bitmap is also 0. +TEST_IMG="$TEST_IMG.top" alloc="" zero="" _verify_l2_bitmap 0 +TEST_IMG="$TEST_IMG.top" alloc="" zero="" _verify_l2_bitmap 1 +TEST_IMG="$TEST_IMG.top" alloc="" zero="" _verify_l2_bitmap 2 + # success, all done echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/271.out b/tests/qemu-iotests/271.out index c335a6c608..0b24d50159 100644 --- a/tests/qemu-iotests/271.out +++ b/tests/qemu-iotests/271.out @@ -765,4 +765,44 @@ Offset Length Mapped to File # verify image bitmap L2 entry #0: 0x8000000000500000 0000000040000002 + +### Rebase with compression for images with subclusters ### + +# create backing chain + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=3145728 +Formatting 'TEST_DIR/t.IMGFMT.mid', fmt=IMGFMT size=3145728 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT +Formatting 'TEST_DIR/t.IMGFMT.top', fmt=IMGFMT size=3145728 backing_file=TEST_DIR/t.IMGFMT.mid backing_fmt=IMGFMT + +# fill old and new backing with data + +wrote 1048576/1048576 bytes at offset 1048576 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 32768/32768 bytes at offset 1015808 +32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 32768/32768 bytes at offset 2097152 +32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# rebase topmost image onto the new backing, with compression + +# verify that the 1st and 3rd clusters've become compressed + +[{ "start": 0, "length": 1048576, "depth": 0, "present": true, "zero": false, "data": true, "compressed": true}, +{ "start": 1048576, "length": 1048576, "depth": 1, "present": true, "zero": false, "data": true, "compressed": false, "offset": 5242880}, +{ "start": 2097152, "length": 1048576, "depth": 0, "present": true, "zero": false, "data": true, "compressed": true}] + +# verify that data is read the same before and after rebase + +read 32768/32768 bytes at offset 1015808 +32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1048576/1048576 bytes at offset 1048576 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 32768/32768 bytes at offset 2097152 +32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# verify image bitmap + +L2 entry #0: 0x4008000000500000 0000000000000000 +L2 entry #1: 0x0000000000000000 0000000000000000 +L2 entry #2: 0x400800000050040b 0000000000000000 *** done diff --git a/tests/qemu-iotests/314 b/tests/qemu-iotests/314 new file mode 100755 index 0000000000..96d7b4d258 --- /dev/null +++ b/tests/qemu-iotests/314 @@ -0,0 +1,165 @@ +#!/usr/bin/env bash +# group: rw backing auto quick +# +# Test qemu-img rebase with compression +# +# Copyright (c) 2023 Virtuozzo International GmbH. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# creator +owner=andrey.drobyshev@virtuozzo.com + +seq=`basename $0` +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img + _rm_test_img "$TEST_IMG.base" + _rm_test_img "$TEST_IMG.itmd" +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux + +# Want the size divisible by 2 and 3 +size=$(( 48 * 1024 * 1024 )) +half_size=$(( size / 2 )) +third_size=$(( size / 3 )) + +# 1. "qemu-img rebase -c" should refuse working with any format which doesn't +# support compression. We only check "-f raw" here. +echo +echo "=== Testing compressed rebase format compatibility ===" +echo + +$QEMU_IMG create -f raw "$TEST_IMG" "$size" | _filter_img_create +$QEMU_IMG rebase -c -f raw -b "" "$TEST_IMG" + +# 2. Write the 1st half of $size to backing file (compressed), 2nd half -- to +# the top image (also compressed). Rebase the top image onto no backing file, +# with compression (i.e. "qemu-img -c -b ''"). Check that the resulting image +# has the written data preserved, and "qemu-img check" reports 100% clusters +# as compressed. +echo +echo "=== Testing rebase with compression onto no backing file ===" +echo + +TEST_IMG="$TEST_IMG.base" _make_test_img $size +_make_test_img -b "$TEST_IMG.base" -F $IMGFMT $size + +$QEMU_IO -c "write -c -P 0xaa 0 $half_size" "$TEST_IMG.base" | _filter_qemu_io +$QEMU_IO -c "write -c -P 0xbb $half_size $half_size" "$TEST_IMG" \ + | _filter_qemu_io + +$QEMU_IMG rebase -c -f $IMGFMT -b "" "$TEST_IMG" + +$QEMU_IO -c "read -P 0xaa 0 $half_size" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0xbb $half_size $half_size" "$TEST_IMG" | _filter_qemu_io + +$QEMU_IMG check "$TEST_IMG" | _filter_testdir + +# 3. Same as the previous one, but with raw backing file (hence write to +# the backing is uncompressed). +echo +echo "=== Testing rebase with compression with raw backing file ===" +echo + +$QEMU_IMG create -f raw "$TEST_IMG.base" "$half_size" | _filter_img_create +_make_test_img -b "$TEST_IMG.base" -F raw $size + +$QEMU_IO -f raw -c "write -P 0xaa 0 $half_size" "$TEST_IMG.base" \ + | _filter_qemu_io +$QEMU_IO -c "write -c -P 0xbb $half_size $half_size" \ + "$TEST_IMG" | _filter_qemu_io + +$QEMU_IMG rebase -c -f $IMGFMT -b "" "$TEST_IMG" + +$QEMU_IO -c "read -P 0xaa 0 $half_size" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0xbb $half_size $half_size" "$TEST_IMG" | _filter_qemu_io + +$QEMU_IMG check "$TEST_IMG" | _filter_testdir + +# 4. Create a backing chain base<--itmd<--img, filling 1st, 2nd and 3rd +# thirds of them, respectively (with compression). Rebase img onto base, +# effectively deleting itmd from the chain, and check that written data is +# preserved in the resulting image. Also check that "qemu-img check" reports +# 100% clusters as compressed. +echo +echo "=== Testing compressed rebase removing single delta from the chain ===" +echo + +TEST_IMG="$TEST_IMG.base" _make_test_img $size +TEST_IMG="$TEST_IMG.itmd" _make_test_img -b "$TEST_IMG.base" -F $IMGFMT $size +_make_test_img -b "$TEST_IMG.itmd" -F $IMGFMT $size + +$QEMU_IO -c "write -c -P 0xaa 0 $third_size" \ + "$TEST_IMG.base" | _filter_qemu_io +$QEMU_IO -c "write -c -P 0xbb $third_size $third_size" \ + "$TEST_IMG.itmd" | _filter_qemu_io +$QEMU_IO -c "write -c -P 0xcc $((third_size * 2 )) $third_size" \ + "$TEST_IMG" | _filter_qemu_io + +$QEMU_IMG rebase -c -f $IMGFMT -b "$TEST_IMG.base" -F $IMGFMT "$TEST_IMG" + +$QEMU_IO -c "read -P 0xaa 0 $third_size" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0xbb $third_size $third_size" \ + "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0xcc $(( third_size * 2 )) $third_size" \ + "$TEST_IMG" | _filter_qemu_io + +$QEMU_IMG check "$TEST_IMG" | _filter_testdir + +# 5. Create one-cluster backing and overlay images, and fill only the first +# (half - 1) bytes of the backing with data (uncompressed). Rebase the +# overlay onto no backing file with compression. Check that data is still +# read correctly, and that cluster is now really compressed ("qemu-img check" +# reports 100% clusters as compressed. +echo +echo "=== Testing compressed rebase with unaligned unmerged data ===" +echo + +CLUSTER_SIZE=65536 + +TEST_IMG="$TEST_IMG.base" _make_test_img $CLUSTER_SIZE +_make_test_img -b "$TEST_IMG.base" -F $IMGFMT $CLUSTER_SIZE + +$QEMU_IO -c "write -P 0xaa 0 $(( CLUSTER_SIZE / 2 - 1 ))" $TEST_IMG.base \ + | _filter_qemu_io + +$QEMU_IMG rebase -c -f $IMGFMT -b "" "$TEST_IMG" + +$QEMU_IO -c "read -P 0xaa 0 $(( CLUSTER_SIZE / 2 - 1 ))" "$TEST_IMG" \ + | _filter_qemu_io +$QEMU_IO -c \ + "read -P 0x00 $(( CLUSTER_SIZE / 2 - 1 )) $(( CLUSTER_SIZE / 2 + 1 ))" \ + "$TEST_IMG" | _filter_qemu_io + +$QEMU_IMG check "$TEST_IMG" | _filter_testdir + +# success, all done +echo +echo '*** done' +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/314.out b/tests/qemu-iotests/314.out new file mode 100644 index 0000000000..ac9337a543 --- /dev/null +++ b/tests/qemu-iotests/314.out @@ -0,0 +1,75 @@ +QA output created by 314 + +=== Testing compressed rebase format compatibility === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=raw size=50331648 +qemu-img: Compression not supported for this file format + +=== Testing rebase with compression onto no backing file === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=50331648 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=50331648 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT +wrote 25165824/25165824 bytes at offset 0 +24 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 25165824/25165824 bytes at offset 25165824 +24 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 25165824/25165824 bytes at offset 0 +24 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 25165824/25165824 bytes at offset 25165824 +24 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. +768/768 = 100.00% allocated, 100.00% fragmented, 100.00% compressed clusters +Image end offset: 458752 + +=== Testing rebase with compression with raw backing file === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=raw size=25165824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=50331648 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=raw +wrote 25165824/25165824 bytes at offset 0 +24 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 25165824/25165824 bytes at offset 25165824 +24 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 25165824/25165824 bytes at offset 0 +24 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 25165824/25165824 bytes at offset 25165824 +24 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. +768/768 = 100.00% allocated, 100.00% fragmented, 100.00% compressed clusters +Image end offset: 458752 + +=== Testing compressed rebase removing single delta from the chain === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=50331648 +Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=50331648 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=50331648 backing_file=TEST_DIR/t.IMGFMT.itmd backing_fmt=IMGFMT +wrote 16777216/16777216 bytes at offset 0 +16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 16777216/16777216 bytes at offset 16777216 +16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 16777216/16777216 bytes at offset 33554432 +16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 16777216/16777216 bytes at offset 0 +16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 16777216/16777216 bytes at offset 16777216 +16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 16777216/16777216 bytes at offset 33554432 +16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. +512/768 = 66.67% allocated, 100.00% fragmented, 100.00% compressed clusters +Image end offset: 458752 + +=== Testing compressed rebase with unaligned unmerged data === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=65536 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65536 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT +wrote 32767/32767 bytes at offset 0 +31.999 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 32767/32767 bytes at offset 0 +31.999 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 32769/32769 bytes at offset 32767 +32.001 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. +1/1 = 100.00% allocated, 100.00% fragmented, 100.00% compressed clusters +Image end offset: 393216 + +*** done From fed824501501518b1ad3dc08a39f8f855508190d Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 13 Oct 2023 17:33:01 +0200 Subject: [PATCH 253/974] block: Fix locking in media change monitor commands blk_insert_bs() requires that the caller holds the AioContext lock for the node to be inserted. Since commit c066e808e11, neglecting to do so causes a crash when the child has to be moved to a different AioContext to attach it to the BlockBackend. This fixes qmp_blockdev_insert_anon_medium(), which is called for the QMP commands 'blockdev-insert-medium' and 'blockdev-change-medium', to correctly take the lock. Cc: qemu-stable@nongnu.org Fixes: https://issues.redhat.com/browse/RHEL-3922 Fixes: c066e808e11a5c181b625537b6c78e0de27a4801 Signed-off-by: Kevin Wolf Message-ID: <20231013153302.39234-2-kwolf@redhat.com> Reviewed-by: Hanna Czenczek Signed-off-by: Kevin Wolf --- block/qapi-sysemu.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/block/qapi-sysemu.c b/block/qapi-sysemu.c index 3f614cbc04..1618cd225a 100644 --- a/block/qapi-sysemu.c +++ b/block/qapi-sysemu.c @@ -237,6 +237,7 @@ static void qmp_blockdev_insert_anon_medium(BlockBackend *blk, BlockDriverState *bs, Error **errp) { Error *local_err = NULL; + AioContext *ctx; bool has_device; int ret; @@ -258,7 +259,11 @@ static void qmp_blockdev_insert_anon_medium(BlockBackend *blk, return; } + ctx = bdrv_get_aio_context(bs); + aio_context_acquire(ctx); ret = blk_insert_bs(blk, bs, errp); + aio_context_release(ctx); + if (ret < 0) { return; } From 3be5762294bb03f6a8a284ada6cca7af3a3aeb20 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 13 Oct 2023 17:33:02 +0200 Subject: [PATCH 254/974] iotests: Test media change with iothreads iotests case 118 already tests all relevant operations for media change with multiple devices, however never with iothreads. This changes the test so that the virtio-scsi tests run with an iothread. Signed-off-by: Kevin Wolf Message-ID: <20231013153302.39234-3-kwolf@redhat.com> Reviewed-by: Hanna Czenczek Signed-off-by: Kevin Wolf --- tests/qemu-iotests/118 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/qemu-iotests/118 b/tests/qemu-iotests/118 index 10dc47459f..6a4210c219 100755 --- a/tests/qemu-iotests/118 +++ b/tests/qemu-iotests/118 @@ -277,7 +277,8 @@ class TestInitiallyFilled(GeneralChangeTestsBaseClass): 'file.driver=file', 'file.filename=%s' % old_img ]) if self.interface == 'scsi': - self.vm.add_device('virtio-scsi-pci') + self.vm.add_object('iothread,id=iothread0') + self.vm.add_device('virtio-scsi-pci,iothread=iothread0') self.vm.add_device('%s,drive=drive0,id=%s' % (interface_to_device_name(self.interface), self.device_name)) @@ -312,7 +313,8 @@ class TestInitiallyEmpty(GeneralChangeTestsBaseClass): if self.use_drive: self.vm.add_drive(None, 'media=%s' % self.media, 'none') if self.interface == 'scsi': - self.vm.add_device('virtio-scsi-pci') + self.vm.add_object('iothread,id=iothread0') + self.vm.add_device('virtio-scsi-pci,iothread=iothread0') self.vm.add_device('%s,%sid=%s' % (interface_to_device_name(self.interface), 'drive=drive0,' if self.use_drive else '', From 67446e605dc2cc3e14a4d212bc97e6a2038e8f6a Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Thu, 19 Oct 2023 15:19:34 +0200 Subject: [PATCH 255/974] blockjob: drop AioContext lock before calling bdrv_graph_wrlock() Same rationale as in 31b2ddfea3 ("graph-lock: Unlock the AioContext while polling"). Otherwise, a deadlock can happen. The alternative would be to pass a BlockDriverState along to bdrv_graph_wrlock(), but there is no BlockDriverState readily available and it's also better conceptually, because the lock is held for the job. The function is always called with the job's AioContext lock held, via one of the .abort, .clean, .free or .prepare job driver functions. Thus, it's safe to drop it. While mirror_exit_common() does hold a second AioContext lock while calling block_job_remove_all_bdrv(), that is for the main thread's AioContext and does not need to be dropped (bdrv_graph_wrlock(bs) also skips dropping the lock if bdrv_get_aio_context(bs) == qemu_get_aio_context()). Suggested-by: Paolo Bonzini Signed-off-by: Fiona Ebner Message-ID: <20231019131936.414246-2-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- blockjob.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/blockjob.c b/blockjob.c index 807f992b59..953dc1b6dc 100644 --- a/blockjob.c +++ b/blockjob.c @@ -198,7 +198,9 @@ void block_job_remove_all_bdrv(BlockJob *job) * one to make sure that such a concurrent access does not attempt * to process an already freed BdrvChild. */ + aio_context_release(job->job.aio_context); bdrv_graph_wrlock(NULL); + aio_context_acquire(job->job.aio_context); while (job->nodes) { GSList *l = job->nodes; BdrvChild *c = l->data; From e462c6d27d0175a67cd373b3027ea5276d25dd4e Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Thu, 19 Oct 2023 15:19:35 +0200 Subject: [PATCH 256/974] block: avoid potential deadlock during bdrv_graph_wrlock() in bdrv_close() by passing the BlockDriverState along, so the held AioContext can be dropped before polling. See commit 31b2ddfea3 ("graph-lock: Unlock the AioContext while polling") which introduced this functionality for more information. The only way to reach bdrv_close() is via bdrv_unref() and for calling that the BlockDriverState's AioContext lock is supposed to be held. Signed-off-by: Fiona Ebner Message-ID: <20231019131936.414246-3-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block.c b/block.c index f9cf05ddcf..a527aa1a4c 100644 --- a/block.c +++ b/block.c @@ -5200,7 +5200,7 @@ static void bdrv_close(BlockDriverState *bs) bs->drv = NULL; } - bdrv_graph_wrlock(NULL); + bdrv_graph_wrlock(bs); QLIST_FOREACH_SAFE(child, &bs->children, next, next) { bdrv_unref_child(bs, child); } From 302823854b078f0525cf1babcc4fc9ff7b7d5f71 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Thu, 19 Oct 2023 15:19:36 +0200 Subject: [PATCH 257/974] blockdev: mirror: avoid potential deadlock when using iothread The bdrv_getlength() function is a generated co-wrapper and uses AIO_WAIT_WHILE() to wait for the spawned coroutine. AIO_WAIT_WHILE() expects the lock to be acquired exactly once. Fix a case where it may be acquired twice. This can happen when the source node is explicitly specified as the @replaces parameter or if the source node is a filter node. Signed-off-by: Fiona Ebner Message-ID: <20231019131936.414246-4-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- blockdev.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/blockdev.c b/blockdev.c index a01c62596b..877e3a26d4 100644 --- a/blockdev.c +++ b/blockdev.c @@ -2968,6 +2968,7 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, if (replaces) { BlockDriverState *to_replace_bs; + AioContext *aio_context; AioContext *replace_aio_context; int64_t bs_size, replace_size; @@ -2982,10 +2983,19 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, return; } + aio_context = bdrv_get_aio_context(bs); replace_aio_context = bdrv_get_aio_context(to_replace_bs); - aio_context_acquire(replace_aio_context); + /* + * bdrv_getlength() is a co-wrapper and uses AIO_WAIT_WHILE. Be sure not + * to acquire the same AioContext twice. + */ + if (replace_aio_context != aio_context) { + aio_context_acquire(replace_aio_context); + } replace_size = bdrv_getlength(to_replace_bs); - aio_context_release(replace_aio_context); + if (replace_aio_context != aio_context) { + aio_context_release(replace_aio_context); + } if (replace_size < 0) { error_setg_errno(errp, -replace_size, From 05c223cef7b50bcd07a388d45b45bdeb517c4082 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Sun, 29 Oct 2023 14:50:15 +0000 Subject: [PATCH 258/974] tests/vm/openbsd: Use the system dtc package MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can use the pre-packaged libfdt from the dtc package to avoid that we have to compile this code each time again and again. While we're at it, the "--python=python3" does not seemt to be necessary anymore, so we can drop it. Signed-off-by: Thomas Huth Message-Id: <20231016154049.37147-1-thuth@redhat.com> Signed-off-by: Alex Bennée Message-Id: <20231029145033.592566-2-alex.bennee@linaro.org> --- tests/vm/openbsd | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/vm/openbsd b/tests/vm/openbsd index 6b4fc29793..85c5bb3536 100755 --- a/tests/vm/openbsd +++ b/tests/vm/openbsd @@ -27,6 +27,7 @@ class OpenBSDVM(basevm.BaseVM): size = "20G" pkgs = [ # tools + "dtc", "git", "pkgconf", "bzip2", "xz", @@ -67,8 +68,9 @@ class OpenBSDVM(basevm.BaseVM): cd $(mktemp -d /home/qemu/qemu-test.XXXXXX); mkdir src build; cd src; tar -xf /dev/rsd1c; - cd ../build - ../src/configure --cc=cc --python=python3 {configure_opts}; + cd ../build; + ../src/configure --cc=cc --extra-cflags=-I/usr/local/include \ + --extra-ldflags=-L/usr/local/lib {configure_opts}; gmake --output-sync -j{jobs} {target} {verbose}; """ poweroff = "halt -p" From 580731dcc87eb27a2b0dc20ec331f1ce51864c97 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 29 Oct 2023 14:50:16 +0000 Subject: [PATCH 259/974] tests/tcg: Add -fno-stack-protector MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A build of GCC 13.2 will have stack protector enabled by default if it was configured with --enable-default-ssp option. For such a compiler, it is necessary to explicitly disable stack protector when linking without standard libraries. Signed-off-by: Akihiko Odaki Message-Id: <20230731091042.139159-3-akihiko.odaki@daynix.com> [AJB: fix comment string typo] Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20231029145033.592566-3-alex.bennee@linaro.org> --- tests/tcg/Makefile.target | 2 +- tests/tcg/aarch64/Makefile.target | 2 +- tests/tcg/arm/Makefile.target | 2 +- tests/tcg/cris/Makefile.target | 2 +- tests/tcg/hexagon/Makefile.target | 2 +- tests/tcg/i386/Makefile.target | 2 +- tests/tcg/minilib/Makefile.target | 2 +- tests/tcg/mips/Makefile.target | 2 +- tests/tcg/mips/hello-mips.c | 4 ++-- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/tcg/Makefile.target b/tests/tcg/Makefile.target index f3a189c9d4..8cf65f68dd 100644 --- a/tests/tcg/Makefile.target +++ b/tests/tcg/Makefile.target @@ -123,7 +123,7 @@ else # For system targets we include a different Makefile fragment as the # build options for bare programs are usually pretty different. They # are expected to provide their own build recipes. -EXTRA_CFLAGS += -ffreestanding +EXTRA_CFLAGS += -ffreestanding -fno-stack-protector -include $(SRC_PATH)/tests/tcg/minilib/Makefile.target -include $(SRC_PATH)/tests/tcg/multiarch/system/Makefile.softmmu-target -include $(SRC_PATH)/tests/tcg/$(TARGET_NAME)/Makefile.softmmu-target diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target index 62b38c792f..0c84b61ae0 100644 --- a/tests/tcg/aarch64/Makefile.target +++ b/tests/tcg/aarch64/Makefile.target @@ -53,7 +53,7 @@ endif # bti-1 tests the elf notes, so we require special compiler support. ifneq ($(CROSS_CC_HAS_ARMV8_BTI),) AARCH64_TESTS += bti-1 bti-3 -bti-1 bti-3: CFLAGS += -mbranch-protection=standard +bti-1 bti-3: CFLAGS += -fno-stack-protector -mbranch-protection=standard bti-1 bti-3: LDFLAGS += -nostdlib endif # bti-2 tests PROT_BTI, so no special compiler support required. diff --git a/tests/tcg/arm/Makefile.target b/tests/tcg/arm/Makefile.target index 0038cef02c..3473f4619e 100644 --- a/tests/tcg/arm/Makefile.target +++ b/tests/tcg/arm/Makefile.target @@ -12,7 +12,7 @@ float_madds: CFLAGS+=-mfpu=neon-vfpv4 # Basic Hello World ARM_TESTS = hello-arm -hello-arm: CFLAGS+=-marm -ffreestanding +hello-arm: CFLAGS+=-marm -ffreestanding -fno-stack-protector hello-arm: LDFLAGS+=-nostdlib # IWMXT floating point extensions diff --git a/tests/tcg/cris/Makefile.target b/tests/tcg/cris/Makefile.target index 43587d2769..713e2a5b6c 100644 --- a/tests/tcg/cris/Makefile.target +++ b/tests/tcg/cris/Makefile.target @@ -30,7 +30,7 @@ AS = $(CC) -x assembler-with-cpp LD = $(CC) # we rely on GCC inline:ing the stuff we tell it to in many places here. -CFLAGS = -Winline -Wall -g -O2 -static +CFLAGS = -Winline -Wall -g -O2 -static -fno-stack-protector NOSTDFLAGS = -nostartfiles -nostdlib ASFLAGS += -mcpu=v10 -g -Wa,-I,$(SRC_PATH)/tests/tcg/cris/bare CRT_FILES = crt.o sys.o diff --git a/tests/tcg/hexagon/Makefile.target b/tests/tcg/hexagon/Makefile.target index 87ed2c90b9..f839b2c0d5 100644 --- a/tests/tcg/hexagon/Makefile.target +++ b/tests/tcg/hexagon/Makefile.target @@ -19,7 +19,7 @@ EXTRA_RUNS = CFLAGS += -Wno-incompatible-pointer-types -Wno-undefined-internal -CFLAGS += -fno-unroll-loops +CFLAGS += -fno-unroll-loops -fno-stack-protector HEX_SRC=$(SRC_PATH)/tests/tcg/hexagon VPATH += $(HEX_SRC) diff --git a/tests/tcg/i386/Makefile.target b/tests/tcg/i386/Makefile.target index fdf757c6ce..3dec7c6c42 100644 --- a/tests/tcg/i386/Makefile.target +++ b/tests/tcg/i386/Makefile.target @@ -35,7 +35,7 @@ run-test-aes: QEMU_OPTS += -cpu max # # hello-i386 is a barebones app # -hello-i386: CFLAGS+=-ffreestanding +hello-i386: CFLAGS+=-ffreestanding -fno-stack-protector hello-i386: LDFLAGS+=-nostdlib # test-386 includes a couple of additional objects that need to be diff --git a/tests/tcg/minilib/Makefile.target b/tests/tcg/minilib/Makefile.target index c821d2806a..af0bf54be9 100644 --- a/tests/tcg/minilib/Makefile.target +++ b/tests/tcg/minilib/Makefile.target @@ -12,7 +12,7 @@ SYSTEM_MINILIB_SRC=$(SRC_PATH)/tests/tcg/minilib MINILIB_SRCS=$(wildcard $(SYSTEM_MINILIB_SRC)/*.c) MINILIB_OBJS=$(patsubst $(SYSTEM_MINILIB_SRC)/%.c, %.o, $(MINILIB_SRCS)) -MINILIB_CFLAGS+=-nostdlib -ggdb -O0 +MINILIB_CFLAGS+=-nostdlib -fno-stack-protector -ggdb -O0 MINILIB_INC=-isystem $(SYSTEM_MINILIB_SRC) .PRECIOUS: $(MINILIB_OBJS) diff --git a/tests/tcg/mips/Makefile.target b/tests/tcg/mips/Makefile.target index 1a994d5525..5d17c1706e 100644 --- a/tests/tcg/mips/Makefile.target +++ b/tests/tcg/mips/Makefile.target @@ -14,6 +14,6 @@ MIPS_TESTS=hello-mips TESTS += $(MIPS_TESTS) -hello-mips: CFLAGS+=-mno-abicalls -fno-PIC -mabi=32 +hello-mips: CFLAGS+=-mno-abicalls -fno-PIC -fno-stack-protector -mabi=32 hello-mips: LDFLAGS+=-nostdlib endif diff --git a/tests/tcg/mips/hello-mips.c b/tests/tcg/mips/hello-mips.c index 4e1cf501af..38e22d00e3 100644 --- a/tests/tcg/mips/hello-mips.c +++ b/tests/tcg/mips/hello-mips.c @@ -5,8 +5,8 @@ * http://www.linux-mips.org/wiki/MIPSABIHistory * http://www.linux.com/howtos/Assembly-HOWTO/mips.shtml * -* mipsel-linux-gcc -nostdlib -mno-abicalls -fno-PIC -mabi=32 \ -* -O2 -static -o hello-mips hello-mips.c +* mipsel-linux-gcc -nostdlib -mno-abicalls -fno-PIC -fno-stack-protector \ +* -mabi=32 -O2 -static -o hello-mips hello-mips.c * */ #define __NR_SYSCALL_BASE 4000 From aa39c4c098190e49eaf8854046dcc1827770f890 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Sun, 29 Oct 2023 14:50:17 +0000 Subject: [PATCH 260/974] gitlab: split alpha testing into a legacy container MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current bookworm compiler doesn't build the static binaries due to bug #1054412 and it might be awhile before it gets fixed. The problem of keeping older architecture compilers running isn't going to go away so lets prepare the ground. Create a legacy container and move some tests around so the others can get upgraded. Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20231029145033.592566-4-alex.bennee@linaro.org> --- .gitlab-ci.d/buildtest.yml | 19 ++++++-- .gitlab-ci.d/container-cross.yml | 6 +++ .../dockerfiles/debian-all-test-cross.docker | 4 +- .../debian-legacy-test-cross.docker | 46 +++++++++++++++++++ 4 files changed, 69 insertions(+), 6 deletions(-) create mode 100644 tests/docker/dockerfiles/debian-legacy-test-cross.docker diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 25af1bc41e..bb24e052f6 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -256,6 +256,7 @@ build-user: variables: IMAGE: debian-all-test-cross CONFIGURE_ARGS: --disable-tools --disable-system + --target-list-exclude=alpha-linux-user MAKE_CHECK_ARGS: check-tcg build-user-static: @@ -265,6 +266,18 @@ build-user-static: variables: IMAGE: debian-all-test-cross CONFIGURE_ARGS: --disable-tools --disable-system --static + --target-list-exclude=alpha-linux-user + MAKE_CHECK_ARGS: check-tcg + +# targets stuck on older compilers +build-legacy: + extends: .native_build_job_template + needs: + job: amd64-debian-legacy-cross-container + variables: + IMAGE: debian-legacy-test-cross + TARGETS: alpha-linux-user alpha-softmmu + CONFIGURE_ARGS: --disable-tools MAKE_CHECK_ARGS: check-tcg build-user-hexagon: @@ -285,7 +298,7 @@ build-some-softmmu: variables: IMAGE: debian-all-test-cross CONFIGURE_ARGS: --disable-tools --enable-debug - TARGETS: xtensa-softmmu arm-softmmu aarch64-softmmu alpha-softmmu + TARGETS: xtensa-softmmu arm-softmmu aarch64-softmmu MAKE_CHECK_ARGS: check-tcg # We build tricore in a very minimal tricore only container @@ -318,7 +331,7 @@ clang-user: variables: IMAGE: debian-all-test-cross CONFIGURE_ARGS: --cc=clang --cxx=clang++ --disable-system - --target-list-exclude=microblazeel-linux-user,aarch64_be-linux-user,i386-linux-user,m68k-linux-user,mipsn32el-linux-user,xtensaeb-linux-user + --target-list-exclude=alpha-linux-user,microblazeel-linux-user,aarch64_be-linux-user,i386-linux-user,m68k-linux-user,mipsn32el-linux-user,xtensaeb-linux-user --extra-cflags=-fsanitize=undefined --extra-cflags=-fno-sanitize-recover=undefined MAKE_CHECK_ARGS: check-unit check-tcg @@ -505,7 +518,7 @@ build-tci: variables: IMAGE: debian-all-test-cross script: - - TARGETS="aarch64 alpha arm hppa m68k microblaze ppc64 s390x x86_64" + - TARGETS="aarch64 arm hppa m68k microblaze ppc64 s390x x86_64" - mkdir build - cd build - ../configure --enable-tcg-interpreter --disable-docs --disable-gtk --disable-vnc diff --git a/.gitlab-ci.d/container-cross.yml b/.gitlab-ci.d/container-cross.yml index 2848166ba3..80c540230a 100644 --- a/.gitlab-ci.d/container-cross.yml +++ b/.gitlab-ci.d/container-cross.yml @@ -16,6 +16,12 @@ amd64-debian-user-cross-container: variables: NAME: debian-all-test-cross +amd64-debian-legacy-cross-container: + extends: .container_job_template + stage: containers + variables: + NAME: debian-legacy-test-cross + arm64-debian-cross-container: extends: .container_job_template stage: containers diff --git a/tests/docker/dockerfiles/debian-all-test-cross.docker b/tests/docker/dockerfiles/debian-all-test-cross.docker index 54e957d5e7..205173b0a5 100644 --- a/tests/docker/dockerfiles/debian-all-test-cross.docker +++ b/tests/docker/dockerfiles/debian-all-test-cross.docker @@ -28,8 +28,6 @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \ ninja-build \ gcc-aarch64-linux-gnu \ libc6-dev-arm64-cross \ - gcc-alpha-linux-gnu \ - libc6.1-dev-alpha-cross \ gcc-arm-linux-gnueabihf \ libc6-dev-armhf-cross \ gcc-hppa-linux-gnu \ @@ -66,7 +64,7 @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \ RUN /usr/bin/pip3 install tomli ENV QEMU_CONFIGURE_OPTS --disable-system --disable-docs --disable-tools -ENV DEF_TARGET_LIST aarch64-linux-user,alpha-linux-user,arm-linux-user,hppa-linux-user,i386-linux-user,m68k-linux-user,mips-linux-user,mips64-linux-user,mips64el-linux-user,mipsel-linux-user,ppc-linux-user,ppc64-linux-user,ppc64le-linux-user,riscv64-linux-user,s390x-linux-user,sh4-linux-user,sparc64-linux-user +ENV DEF_TARGET_LIST aarch64-linux-user,arm-linux-user,hppa-linux-user,i386-linux-user,m68k-linux-user,mips-linux-user,mips64-linux-user,mips64el-linux-user,mipsel-linux-user,ppc-linux-user,ppc64-linux-user,ppc64le-linux-user,riscv64-linux-user,s390x-linux-user,sh4-linux-user,sparc64-linux-user # As a final step configure the user (if env is defined) ARG USER ARG UID diff --git a/tests/docker/dockerfiles/debian-legacy-test-cross.docker b/tests/docker/dockerfiles/debian-legacy-test-cross.docker new file mode 100644 index 0000000000..763d36dfe3 --- /dev/null +++ b/tests/docker/dockerfiles/debian-legacy-test-cross.docker @@ -0,0 +1,46 @@ +# Docker legacy cross-compiler target (tests and minimal qemu) +# +# Compilers for some of our older targets which we cant currently +# upgrade. Currently: +# +# libc6.1-dev-alpha-cross: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1054412 +# +# As we are targeting check-tcg here we only need minimal qemu +# dependencies and the relevant cross compilers. + +FROM docker.io/library/debian:11-slim + +# Duplicate deb line as deb-src +RUN cat /etc/apt/sources.list | sed "s/^deb\ /deb-src /" >> /etc/apt/sources.list + +RUN export DEBIAN_FRONTEND=noninteractive && \ + apt-get update && \ + apt-get install -y eatmydata && \ + eatmydata apt-get dist-upgrade -y && \ + apt build-dep -yy qemu + +# Add extra build tools and as many cross compilers as we can for testing +RUN DEBIAN_FRONTEND=noninteractive eatmydata \ + apt install -y --no-install-recommends \ + bison \ + ccache \ + clang \ + flex \ + git \ + ninja-build \ + gcc-alpha-linux-gnu \ + libc6.1-dev-alpha-cross \ + python3-pip \ + python3-setuptools \ + python3-venv \ + python3-wheel + +RUN /usr/bin/pip3 install tomli + +ENV QEMU_CONFIGURE_OPTS --disable-system --disable-docs --disable-tools +ENV DEF_TARGET_LIST alpha-linux-user +# As a final step configure the user (if env is defined) +ARG USER +ARG UID +RUN if [ "${USER}" ]; then \ + id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi From 6f6c399976933d3c77cb0f516d5d8bbfc0fad2b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Sun, 29 Oct 2023 14:50:18 +0000 Subject: [PATCH 261/974] gitlab: clean-up build-soft-softmmu job MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Having dropped alpha we also now drop xtensa as we don't have the compiler in this image. It's not all doom and gloom though as a number of other targets have gained softmmu TCG tests so we can add them. We will take care of the other targets with their own containers in future commits. Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20231029145033.592566-5-alex.bennee@linaro.org> --- .gitlab-ci.d/buildtest.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index bb24e052f6..5e9cbf9385 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -290,7 +290,9 @@ build-user-hexagon: CONFIGURE_ARGS: --disable-tools --disable-docs --enable-debug-tcg MAKE_CHECK_ARGS: check-tcg -# Only build the softmmu targets we have check-tcg tests for +# Build the softmmu targets we have check-tcg tests and compilers in +# our omnibus all-test-cross container. Those targets that haven't got +# Debian cross compiler support need to use special containers. build-some-softmmu: extends: .native_build_job_template needs: @@ -298,7 +300,9 @@ build-some-softmmu: variables: IMAGE: debian-all-test-cross CONFIGURE_ARGS: --disable-tools --enable-debug - TARGETS: xtensa-softmmu arm-softmmu aarch64-softmmu + TARGETS: arm-softmmu aarch64-softmmu i386-softmmu riscv64-softmmu + s390x-softmmu x86_64-softmmu + MAKE_CHECK_ARGS: check-tcg MAKE_CHECK_ARGS: check-tcg # We build tricore in a very minimal tricore only container From cb8715bf0ce2d8347729bbaf19e236be95d58084 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Sun, 29 Oct 2023 14:50:19 +0000 Subject: [PATCH 262/974] gitlab: add build-loongarch to matrix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have the compiler and with a few updates a container that can build QEMU so we should at least run the check-tcg smoke tests. Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20231029145033.592566-6-alex.bennee@linaro.org> --- .gitlab-ci.d/buildtest.yml | 9 ++++++++ .gitlab-ci.d/container-cross.yml | 6 +++++ tests/docker/Makefile.include | 1 - .../dockerfiles/debian-loongarch-cross.docker | 22 ++++++++++++++++++- 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 5e9cbf9385..9500ea6e2c 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -303,6 +303,15 @@ build-some-softmmu: TARGETS: arm-softmmu aarch64-softmmu i386-softmmu riscv64-softmmu s390x-softmmu x86_64-softmmu MAKE_CHECK_ARGS: check-tcg + +build-loongarch64: + extends: .native_build_job_template + needs: + job: loongarch-debian-cross-container + variables: + IMAGE: debian-loongarch-cross + CONFIGURE_ARGS: --disable-tools --enable-debug + TARGETS: loongarch64-linux-user loongarch64-softmmu MAKE_CHECK_ARGS: check-tcg # We build tricore in a very minimal tricore only container diff --git a/.gitlab-ci.d/container-cross.yml b/.gitlab-ci.d/container-cross.yml index 80c540230a..d200f3e00d 100644 --- a/.gitlab-ci.d/container-cross.yml +++ b/.gitlab-ci.d/container-cross.yml @@ -52,6 +52,12 @@ hppa-debian-cross-container: variables: NAME: debian-hppa-cross +loongarch-debian-cross-container: + extends: .container_job_template + stage: containers + variables: + NAME: debian-loongarch-cross + m68k-debian-cross-container: extends: .container_job_template stage: containers diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index ab68b2dbad..5635d1537f 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -125,7 +125,6 @@ docker-image-debian-nios2-cross: $(DOCKER_FILES_DIR)/debian-toolchain.docker \ DOCKER_PARTIAL_IMAGES += debian-alpha-cross DOCKER_PARTIAL_IMAGES += debian-powerpc-test-cross DOCKER_PARTIAL_IMAGES += debian-hppa-cross -DOCKER_PARTIAL_IMAGES += debian-loongarch-cross DOCKER_PARTIAL_IMAGES += debian-m68k-cross debian-mips64-cross DOCKER_PARTIAL_IMAGES += debian-microblaze-cross DOCKER_PARTIAL_IMAGES += debian-mips-cross diff --git a/tests/docker/dockerfiles/debian-loongarch-cross.docker b/tests/docker/dockerfiles/debian-loongarch-cross.docker index b4bf265717..b25e779a2c 100644 --- a/tests/docker/dockerfiles/debian-loongarch-cross.docker +++ b/tests/docker/dockerfiles/debian-loongarch-cross.docker @@ -9,22 +9,42 @@ FROM docker.io/library/debian:11-slim # Duplicate deb line as deb-src RUN cat /etc/apt/sources.list | sed "s/^deb\ /deb-src /" >> /etc/apt/sources.list +RUN export DEBIAN_FRONTEND=noninteractive && \ + apt-get update && \ + apt-get install -y eatmydata && \ + eatmydata apt-get dist-upgrade -y && \ + apt build-dep -yy qemu + RUN apt-get update && \ DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata && \ DEBIAN_FRONTEND=noninteractive eatmydata \ apt-get install -y --no-install-recommends \ build-essential \ + bison \ ca-certificates \ + ccache \ + clang \ + flex \ curl \ gettext \ git \ - python3-minimal + ninja-build \ + python3-pip \ + python3-setuptools \ + python3-venv \ + python3-wheel + +RUN /usr/bin/pip3 install tomli RUN curl -#SL https://github.com/loongson/build-tools/releases/download/2023.08.08/CLFS-loongarch64-8.1-x86_64-cross-tools-gcc-glibc.tar.xz \ | tar -xJC /opt ENV PATH $PATH:/opt/cross-tools/bin ENV LD_LIBRARY_PATH /opt/cross-tools/lib:/opt/cross-tools/loongarch64-unknown-linux-gnu/lib:$LD_LIBRARY_PATH + +ENV QEMU_CONFIGURE_OPTS --disable-system --disable-docs --disable-tools +ENV DEF_TARGET_LIST loongarch64-linux-user,loongarch-softmmu + # As a final step configure the user (if env is defined) ARG USER ARG UID From d004e27b4e10dbe4741e81fa5f8ef9b7ea23818d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Sun, 29 Oct 2023 14:50:20 +0000 Subject: [PATCH 263/974] tests/docker: use debian-legacy-test-cross for alpha MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Maintaining two sets of containers for test building is silly. While it makes sense for the QEMU cross-compile targets to have their own fat containers built by lcitool we might as well merge the other random debian based compilers into the same one used on gitlab. Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20231029145033.592566-7-alex.bennee@linaro.org> --- .gitlab-ci.d/container-cross.yml | 6 ------ configure | 5 +++++ tests/docker/Makefile.include | 1 - .../dockerfiles/debian-alpha-cross.docker | 19 ------------------- 4 files changed, 5 insertions(+), 26 deletions(-) delete mode 100644 tests/docker/dockerfiles/debian-alpha-cross.docker diff --git a/.gitlab-ci.d/container-cross.yml b/.gitlab-ci.d/container-cross.yml index d200f3e00d..0de1929e74 100644 --- a/.gitlab-ci.d/container-cross.yml +++ b/.gitlab-ci.d/container-cross.yml @@ -1,9 +1,3 @@ -alpha-debian-cross-container: - extends: .container_job_template - stage: containers - variables: - NAME: debian-alpha-cross - amd64-debian-cross-container: extends: .container_job_template stage: containers diff --git a/configure b/configure index b4ea78c77d..c8d0d4f7cb 100755 --- a/configure +++ b/configure @@ -1281,6 +1281,11 @@ probe_target_compiler() { container_cross_prefix=aarch64-linux-gnu- container_cross_cc=${container_cross_prefix}gcc ;; + alpha) + container_image=debian-legacy-test-cross + container_cross_prefix=alpha-linux-gnu- + container_cross_cc=${container_cross_prefix}gcc + ;; arm) # We don't have any bigendian build tools so we only use this for ARM container_image=debian-armhf-cross diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 5635d1537f..8270bdff6c 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -122,7 +122,6 @@ docker-image-debian-nios2-cross: $(DOCKER_FILES_DIR)/debian-toolchain.docker \ $(call debian-toolchain, $@) # These images may be good enough for building tests but not for test builds -DOCKER_PARTIAL_IMAGES += debian-alpha-cross DOCKER_PARTIAL_IMAGES += debian-powerpc-test-cross DOCKER_PARTIAL_IMAGES += debian-hppa-cross DOCKER_PARTIAL_IMAGES += debian-m68k-cross debian-mips64-cross diff --git a/tests/docker/dockerfiles/debian-alpha-cross.docker b/tests/docker/dockerfiles/debian-alpha-cross.docker deleted file mode 100644 index 7fa7bf1bde..0000000000 --- a/tests/docker/dockerfiles/debian-alpha-cross.docker +++ /dev/null @@ -1,19 +0,0 @@ -# -# Docker cross-compiler target -# -# This docker target builds on the Debian Bullseye base image. -# -FROM docker.io/library/debian:11-slim - -RUN export DEBIAN_FRONTEND=noninteractive && \ - apt-get update && \ - apt-get install -y eatmydata && \ - eatmydata apt-get dist-upgrade -y && \ - eatmydata apt-get install --no-install-recommends -y \ - gcc-alpha-linux-gnu \ - libc6.1-dev-alpha-cross -# As a final step configure the user (if env is defined) -ARG USER -ARG UID -RUN if [ "${USER}" ]; then \ - id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi From 4e76d98ae962dc22408ac99f2e1c3bf2bce2ef9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Sun, 29 Oct 2023 14:50:21 +0000 Subject: [PATCH 264/974] tests/docker: move sh4 to use debian-legacy-test-cross MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sh4 is another target which doesn't work with bookworm compilers. To keep on buster move across to the debian-legacy-test-cross image and update accordingly. Signed-off-by: Alex Bennée Reviewed-by: Richard Henderson Message-Id: <20231030135715.800164-1-alex.bennee@linaro.org> --- .gitlab-ci.d/buildtest.yml | 6 +++--- .gitlab-ci.d/container-cross.yml | 6 ------ configure | 4 ++++ tests/docker/Makefile.include | 2 +- .../dockerfiles/debian-all-test-cross.docker | 4 +--- .../debian-legacy-test-cross.docker | 5 ++++- .../dockerfiles/debian-sh4-cross.docker | 19 ------------------- 7 files changed, 13 insertions(+), 33 deletions(-) delete mode 100644 tests/docker/dockerfiles/debian-sh4-cross.docker diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 9500ea6e2c..da72f7c690 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -256,7 +256,7 @@ build-user: variables: IMAGE: debian-all-test-cross CONFIGURE_ARGS: --disable-tools --disable-system - --target-list-exclude=alpha-linux-user + --target-list-exclude=alpha-linux-user,sh4-linux-user MAKE_CHECK_ARGS: check-tcg build-user-static: @@ -266,7 +266,7 @@ build-user-static: variables: IMAGE: debian-all-test-cross CONFIGURE_ARGS: --disable-tools --disable-system --static - --target-list-exclude=alpha-linux-user + --target-list-exclude=alpha-linux-user,sh4-linux-user MAKE_CHECK_ARGS: check-tcg # targets stuck on older compilers @@ -276,7 +276,7 @@ build-legacy: job: amd64-debian-legacy-cross-container variables: IMAGE: debian-legacy-test-cross - TARGETS: alpha-linux-user alpha-softmmu + TARGETS: alpha-linux-user alpha-softmmu sh4-linux-user CONFIGURE_ARGS: --disable-tools MAKE_CHECK_ARGS: check-tcg diff --git a/.gitlab-ci.d/container-cross.yml b/.gitlab-ci.d/container-cross.yml index 0de1929e74..3e6d741f62 100644 --- a/.gitlab-ci.d/container-cross.yml +++ b/.gitlab-ci.d/container-cross.yml @@ -116,12 +116,6 @@ s390x-debian-cross-container: variables: NAME: debian-s390x-cross -sh4-debian-cross-container: - extends: .container_job_template - stage: containers - variables: - NAME: debian-sh4-cross - sparc64-debian-cross-container: extends: .container_job_template stage: containers diff --git a/configure b/configure index c8d0d4f7cb..37d1b10b87 100755 --- a/configure +++ b/configure @@ -1334,6 +1334,10 @@ probe_target_compiler() { container_cross_prefix=powerpc${target_arch#ppc}-linux-gnu- container_cross_cc=${container_cross_prefix}gcc-10 ;; + sh4) + container_image=debian-legacy-test-cross + container_cross_prefix=sh4-linux-gnu- + ;; tricore) container_image=debian-tricore-cross container_cross_prefix=tricore- diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 8270bdff6c..f61d97102f 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -129,7 +129,7 @@ DOCKER_PARTIAL_IMAGES += debian-microblaze-cross DOCKER_PARTIAL_IMAGES += debian-mips-cross DOCKER_PARTIAL_IMAGES += debian-nios2-cross DOCKER_PARTIAL_IMAGES += debian-riscv64-test-cross -DOCKER_PARTIAL_IMAGES += debian-sh4-cross debian-sparc64-cross +DOCKER_PARTIAL_IMAGES += debian-sparc64-cross DOCKER_PARTIAL_IMAGES += debian-xtensa-cross DOCKER_PARTIAL_IMAGES += fedora-cris-cross diff --git a/tests/docker/dockerfiles/debian-all-test-cross.docker b/tests/docker/dockerfiles/debian-all-test-cross.docker index 205173b0a5..43cc083318 100644 --- a/tests/docker/dockerfiles/debian-all-test-cross.docker +++ b/tests/docker/dockerfiles/debian-all-test-cross.docker @@ -52,8 +52,6 @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \ libc6-dev-riscv64-cross \ gcc-s390x-linux-gnu \ libc6-dev-s390x-cross \ - gcc-sh4-linux-gnu \ - libc6-dev-sh4-cross \ gcc-sparc64-linux-gnu \ libc6-dev-sparc64-cross \ python3-pip \ @@ -64,7 +62,7 @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \ RUN /usr/bin/pip3 install tomli ENV QEMU_CONFIGURE_OPTS --disable-system --disable-docs --disable-tools -ENV DEF_TARGET_LIST aarch64-linux-user,arm-linux-user,hppa-linux-user,i386-linux-user,m68k-linux-user,mips-linux-user,mips64-linux-user,mips64el-linux-user,mipsel-linux-user,ppc-linux-user,ppc64-linux-user,ppc64le-linux-user,riscv64-linux-user,s390x-linux-user,sh4-linux-user,sparc64-linux-user +ENV DEF_TARGET_LIST aarch64-linux-user,arm-linux-user,hppa-linux-user,i386-linux-user,m68k-linux-user,mips-linux-user,mips64-linux-user,mips64el-linux-user,mipsel-linux-user,ppc-linux-user,ppc64-linux-user,ppc64le-linux-user,riscv64-linux-user,s390x-linux-user,sparc64-linux-user # As a final step configure the user (if env is defined) ARG USER ARG UID diff --git a/tests/docker/dockerfiles/debian-legacy-test-cross.docker b/tests/docker/dockerfiles/debian-legacy-test-cross.docker index 763d36dfe3..8cc68bc912 100644 --- a/tests/docker/dockerfiles/debian-legacy-test-cross.docker +++ b/tests/docker/dockerfiles/debian-legacy-test-cross.docker @@ -4,6 +4,7 @@ # upgrade. Currently: # # libc6.1-dev-alpha-cross: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1054412 +# sh4-linux-user: binaries don't run with bookworm compiler # # As we are targeting check-tcg here we only need minimal qemu # dependencies and the relevant cross compilers. @@ -30,6 +31,8 @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \ ninja-build \ gcc-alpha-linux-gnu \ libc6.1-dev-alpha-cross \ + gcc-sh4-linux-gnu \ + libc6-dev-sh4-cross \ python3-pip \ python3-setuptools \ python3-venv \ @@ -38,7 +41,7 @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \ RUN /usr/bin/pip3 install tomli ENV QEMU_CONFIGURE_OPTS --disable-system --disable-docs --disable-tools -ENV DEF_TARGET_LIST alpha-linux-user +ENV DEF_TARGET_LIST alpha-linux-user,sh4-linux-user # As a final step configure the user (if env is defined) ARG USER ARG UID diff --git a/tests/docker/dockerfiles/debian-sh4-cross.docker b/tests/docker/dockerfiles/debian-sh4-cross.docker deleted file mode 100644 index 6bd8171d33..0000000000 --- a/tests/docker/dockerfiles/debian-sh4-cross.docker +++ /dev/null @@ -1,19 +0,0 @@ -# -# Docker cross-compiler target -# -# This docker target builds on the Debian Bullseye base image. -# -FROM docker.io/library/debian:11-slim - -RUN export DEBIAN_FRONTEND=noninteractive && \ - apt-get update && \ - apt-get install -y eatmydata && \ - eatmydata apt-get dist-upgrade -y && \ - eatmydata apt-get install --no-install-recommends -y \ - gcc-sh4-linux-gnu \ - libc6-dev-sh4-cross -# As a final step configure the user (if env is defined) -ARG USER -ARG UID -RUN if [ "${USER}" ]; then \ - id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi From eb4cb4ed1f62ca01ebb23f5fdf89751a2486c96d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Sun, 29 Oct 2023 14:50:22 +0000 Subject: [PATCH 265/974] tests/docker: use debian-all-test-cross for power MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Maintaining two sets of containers for test building is silly. While it makes sense for the QEMU cross-compile targets to have their own fat containers built by lcitool we might as well merge the other random debian based compilers into the same one used on gitlab. Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20231029145033.592566-9-alex.bennee@linaro.org> --- .gitlab-ci.d/container-cross.yml | 6 ----- configure | 5 ++-- tests/docker/Makefile.include | 1 - .../debian-powerpc-test-cross.docker | 23 ------------------- 4 files changed, 2 insertions(+), 33 deletions(-) delete mode 100644 tests/docker/dockerfiles/debian-powerpc-test-cross.docker diff --git a/.gitlab-ci.d/container-cross.yml b/.gitlab-ci.d/container-cross.yml index 3e6d741f62..a1dac0a45f 100644 --- a/.gitlab-ci.d/container-cross.yml +++ b/.gitlab-ci.d/container-cross.yml @@ -82,12 +82,6 @@ mipsel-debian-cross-container: variables: NAME: debian-mipsel-cross -powerpc-test-cross-container: - extends: .container_job_template - stage: containers - variables: - NAME: debian-powerpc-test-cross - ppc64el-debian-cross-container: extends: .container_job_template stage: containers diff --git a/configure b/configure index 37d1b10b87..96b01a280c 100755 --- a/configure +++ b/configure @@ -1325,14 +1325,13 @@ probe_target_compiler() { container_cross_prefix=nios2-linux-gnu- ;; ppc) - container_image=debian-powerpc-test-cross + container_image=debian-all-test-cross container_cross_prefix=powerpc-linux-gnu- container_cross_cc=${container_cross_prefix}gcc ;; ppc64|ppc64le) - container_image=debian-powerpc-test-cross + container_image=debian-all-test-cross container_cross_prefix=powerpc${target_arch#ppc}-linux-gnu- - container_cross_cc=${container_cross_prefix}gcc-10 ;; sh4) container_image=debian-legacy-test-cross diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index f61d97102f..82e06d84b3 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -122,7 +122,6 @@ docker-image-debian-nios2-cross: $(DOCKER_FILES_DIR)/debian-toolchain.docker \ $(call debian-toolchain, $@) # These images may be good enough for building tests but not for test builds -DOCKER_PARTIAL_IMAGES += debian-powerpc-test-cross DOCKER_PARTIAL_IMAGES += debian-hppa-cross DOCKER_PARTIAL_IMAGES += debian-m68k-cross debian-mips64-cross DOCKER_PARTIAL_IMAGES += debian-microblaze-cross diff --git a/tests/docker/dockerfiles/debian-powerpc-test-cross.docker b/tests/docker/dockerfiles/debian-powerpc-test-cross.docker deleted file mode 100644 index 23779413d3..0000000000 --- a/tests/docker/dockerfiles/debian-powerpc-test-cross.docker +++ /dev/null @@ -1,23 +0,0 @@ -# -# Docker powerpc/ppc64/ppc64le cross-compiler target -# -# This docker target builds on the Debian Bullseye base image. -# -FROM docker.io/library/debian:11-slim - -RUN export DEBIAN_FRONTEND=noninteractive && \ - apt-get update && \ - apt-get install -y eatmydata && \ - eatmydata apt-get dist-upgrade -y && \ - eatmydata apt-get install --no-install-recommends -y \ - gcc-powerpc-linux-gnu \ - libc6-dev-powerpc-cross \ - gcc-10-powerpc64-linux-gnu \ - libc6-dev-ppc64-cross \ - gcc-10-powerpc64le-linux-gnu \ - libc6-dev-ppc64el-cross -# As a final step configure the user (if env is defined) -ARG USER -ARG UID -RUN if [ "${USER}" ]; then \ - id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi From 95f5bf952106f659b16d920dd74d118a4d36b0da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Sun, 29 Oct 2023 14:50:23 +0000 Subject: [PATCH 266/974] tests/docker: use debian-all-test-cross for hppa MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Maintaining two sets of containers for test building is silly. While it makes sense for the QEMU cross-compile targets to have their own fat containers built by lcitool we might as well merge the other random debian based compilers into the same one used on gitlab. Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20231029145033.592566-10-alex.bennee@linaro.org> --- .gitlab-ci.d/container-cross.yml | 6 ------ configure | 5 +++++ tests/docker/Makefile.include | 1 - .../dockerfiles/debian-hppa-cross.docker | 19 ------------------- 4 files changed, 5 insertions(+), 26 deletions(-) delete mode 100644 tests/docker/dockerfiles/debian-hppa-cross.docker diff --git a/.gitlab-ci.d/container-cross.yml b/.gitlab-ci.d/container-cross.yml index a1dac0a45f..66b8844246 100644 --- a/.gitlab-ci.d/container-cross.yml +++ b/.gitlab-ci.d/container-cross.yml @@ -40,12 +40,6 @@ hexagon-cross-container: variables: NAME: debian-hexagon-cross -hppa-debian-cross-container: - extends: .container_job_template - stage: containers - variables: - NAME: debian-hppa-cross - loongarch-debian-cross-container: extends: .container_job_template stage: containers diff --git a/configure b/configure index 96b01a280c..be7cfeddc0 100755 --- a/configure +++ b/configure @@ -1300,6 +1300,11 @@ probe_target_compiler() { container_cross_prefix=hexagon-unknown-linux-musl- container_cross_cc=${container_cross_prefix}clang ;; + hppa) + container_image=debian-all-test-cross + container_cross_prefix=hppa-linux-gnu- + container_cross_cc=${container_cross_prefix}gcc + ;; i386) container_image=fedora-i386-cross container_cross_prefix= diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 82e06d84b3..47e58a248e 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -122,7 +122,6 @@ docker-image-debian-nios2-cross: $(DOCKER_FILES_DIR)/debian-toolchain.docker \ $(call debian-toolchain, $@) # These images may be good enough for building tests but not for test builds -DOCKER_PARTIAL_IMAGES += debian-hppa-cross DOCKER_PARTIAL_IMAGES += debian-m68k-cross debian-mips64-cross DOCKER_PARTIAL_IMAGES += debian-microblaze-cross DOCKER_PARTIAL_IMAGES += debian-mips-cross diff --git a/tests/docker/dockerfiles/debian-hppa-cross.docker b/tests/docker/dockerfiles/debian-hppa-cross.docker deleted file mode 100644 index dd47ffdfa4..0000000000 --- a/tests/docker/dockerfiles/debian-hppa-cross.docker +++ /dev/null @@ -1,19 +0,0 @@ -# -# Docker cross-compiler target -# -# This docker target builds on the Debian Bullseye base image. -# -FROM docker.io/library/debian:11-slim - -RUN export DEBIAN_FRONTEND=noninteractive && \ - apt-get update && \ - apt-get install -y eatmydata && \ - eatmydata apt-get dist-upgrade -y && \ - eatmydata apt-get install --no-install-recommends -y \ - gcc-hppa-linux-gnu \ - libc6-dev-hppa-cross -# As a final step configure the user (if env is defined) -ARG USER -ARG UID -RUN if [ "${USER}" ]; then \ - id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi From 9d9a573612802a0da653add70a2b3de75fcdc1d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Sun, 29 Oct 2023 14:50:24 +0000 Subject: [PATCH 267/974] tests/docker: use debian-all-test-cross for m68k MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Maintaining two sets of containers for test building is silly. While it makes sense for the QEMU cross-compile targets to have their own fat containers built by lcitool we might as well merge the other random debian based compilers into the same one used on gitlab. Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20231029145033.592566-11-alex.bennee@linaro.org> --- .gitlab-ci.d/container-cross.yml | 6 ------ configure | 5 +++++ tests/docker/Makefile.include | 2 +- .../dockerfiles/debian-m68k-cross.docker | 19 ------------------- 4 files changed, 6 insertions(+), 26 deletions(-) delete mode 100644 tests/docker/dockerfiles/debian-m68k-cross.docker diff --git a/.gitlab-ci.d/container-cross.yml b/.gitlab-ci.d/container-cross.yml index 66b8844246..d42d89b26e 100644 --- a/.gitlab-ci.d/container-cross.yml +++ b/.gitlab-ci.d/container-cross.yml @@ -46,12 +46,6 @@ loongarch-debian-cross-container: variables: NAME: debian-loongarch-cross -m68k-debian-cross-container: - extends: .container_job_template - stage: containers - variables: - NAME: debian-m68k-cross - mips64-debian-cross-container: extends: .container_job_template stage: containers diff --git a/configure b/configure index be7cfeddc0..aa29cfb797 100755 --- a/configure +++ b/configure @@ -1313,6 +1313,11 @@ probe_target_compiler() { container_image=debian-loongarch-cross container_cross_prefix=loongarch64-unknown-linux-gnu- ;; + m68k) + container_image=debian-all-test-cross + container_cross_prefix=m68k-linux-gnu- + container_cross_cc=${container_cross_prefix}gcc + ;; microblaze) container_image=debian-microblaze-cross container_cross_prefix=microblaze-linux-musl- diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 47e58a248e..fb93eca537 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -122,7 +122,7 @@ docker-image-debian-nios2-cross: $(DOCKER_FILES_DIR)/debian-toolchain.docker \ $(call debian-toolchain, $@) # These images may be good enough for building tests but not for test builds -DOCKER_PARTIAL_IMAGES += debian-m68k-cross debian-mips64-cross +DOCKER_PARTIAL_IMAGES += debian-mips64-cross DOCKER_PARTIAL_IMAGES += debian-microblaze-cross DOCKER_PARTIAL_IMAGES += debian-mips-cross DOCKER_PARTIAL_IMAGES += debian-nios2-cross diff --git a/tests/docker/dockerfiles/debian-m68k-cross.docker b/tests/docker/dockerfiles/debian-m68k-cross.docker deleted file mode 100644 index 25dd1c1e68..0000000000 --- a/tests/docker/dockerfiles/debian-m68k-cross.docker +++ /dev/null @@ -1,19 +0,0 @@ -# -# Docker cross-compiler target -# -# This docker target builds on the Debian Bullseye base image. -# -FROM docker.io/library/debian:11-slim - -RUN export DEBIAN_FRONTEND=noninteractive && \ - apt-get update && \ - apt-get install -y eatmydata && \ - eatmydata apt-get dist-upgrade -y && \ - eatmydata apt-get install --no-install-recommends -y \ - gcc-m68k-linux-gnu \ - libc6-dev-m68k-cross -# As a final step configure the user (if env is defined) -ARG USER -ARG UID -RUN if [ "${USER}" ]; then \ - id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi From 92a3165e1a755ede53a08422eb0983d3e151073c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Sun, 29 Oct 2023 14:50:25 +0000 Subject: [PATCH 268/974] tests/docker: use debian-all-test-cross for mips64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Maintaining two sets of containers for test building is silly. While it makes sense for the QEMU cross-compile targets to have their own fat containers built by lcitool we might as well merge the other random debian based compilers into the same one used on gitlab. Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20231029145033.592566-12-alex.bennee@linaro.org> --- .gitlab-ci.d/container-cross.yml | 6 ------ configure | 2 +- tests/docker/Makefile.include | 1 - .../dockerfiles/debian-mips64-cross.docker | 19 ------------------- 4 files changed, 1 insertion(+), 27 deletions(-) delete mode 100644 tests/docker/dockerfiles/debian-mips64-cross.docker diff --git a/.gitlab-ci.d/container-cross.yml b/.gitlab-ci.d/container-cross.yml index d42d89b26e..df5eb3ccb6 100644 --- a/.gitlab-ci.d/container-cross.yml +++ b/.gitlab-ci.d/container-cross.yml @@ -46,12 +46,6 @@ loongarch-debian-cross-container: variables: NAME: debian-loongarch-cross -mips64-debian-cross-container: - extends: .container_job_template - stage: containers - variables: - NAME: debian-mips64-cross - mips64el-debian-cross-container: extends: .container_job_template stage: containers diff --git a/configure b/configure index aa29cfb797..2dddc3c82d 100755 --- a/configure +++ b/configure @@ -1327,7 +1327,7 @@ probe_target_compiler() { container_cross_prefix=mips64el-linux-gnuabi64- ;; mips64) - container_image=debian-mips64-cross + container_image=debian-all-test-cross container_cross_prefix=mips64-linux-gnuabi64- ;; nios2) diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index fb93eca537..5831744a39 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -122,7 +122,6 @@ docker-image-debian-nios2-cross: $(DOCKER_FILES_DIR)/debian-toolchain.docker \ $(call debian-toolchain, $@) # These images may be good enough for building tests but not for test builds -DOCKER_PARTIAL_IMAGES += debian-mips64-cross DOCKER_PARTIAL_IMAGES += debian-microblaze-cross DOCKER_PARTIAL_IMAGES += debian-mips-cross DOCKER_PARTIAL_IMAGES += debian-nios2-cross diff --git a/tests/docker/dockerfiles/debian-mips64-cross.docker b/tests/docker/dockerfiles/debian-mips64-cross.docker deleted file mode 100644 index ba965cf564..0000000000 --- a/tests/docker/dockerfiles/debian-mips64-cross.docker +++ /dev/null @@ -1,19 +0,0 @@ -# -# Docker cross-compiler target -# -# This docker target builds on the Debian Bullseye base image. -# -FROM docker.io/library/debian:11-slim - -RUN export DEBIAN_FRONTEND=noninteractive && \ - apt-get update && \ - apt-get install -y eatmydata && \ - eatmydata apt-get dist-upgrade -y && \ - eatmydata apt-get install --no-install-recommends -y \ - gcc-mips64-linux-gnuabi64 \ - libc6-dev-mips64-cross -# As a final step configure the user (if env is defined) -ARG USER -ARG UID -RUN if [ "${USER}" ]; then \ - id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi From b09bb6d1b82031969ca3a6e11b4168f55d83cdfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Sun, 29 Oct 2023 14:50:26 +0000 Subject: [PATCH 269/974] tests/docker: use debian-all-test-cross for mips MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Maintaining two sets of containers for test building is silly. While it makes sense for the QEMU cross-compile targets to have their own fat containers built by lcitool we might as well merge the other random debian based compilers into the same one used on gitlab. Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20231029145033.592566-13-alex.bennee@linaro.org> --- .gitlab-ci.d/container-cross.yml | 6 ------ configure | 4 ++++ tests/docker/Makefile.include | 1 - .../dockerfiles/debian-mips-cross.docker | 19 ------------------- 4 files changed, 4 insertions(+), 26 deletions(-) delete mode 100644 tests/docker/dockerfiles/debian-mips-cross.docker diff --git a/.gitlab-ci.d/container-cross.yml b/.gitlab-ci.d/container-cross.yml index df5eb3ccb6..053330d6f4 100644 --- a/.gitlab-ci.d/container-cross.yml +++ b/.gitlab-ci.d/container-cross.yml @@ -52,12 +52,6 @@ mips64el-debian-cross-container: variables: NAME: debian-mips64el-cross -mips-debian-cross-container: - extends: .container_job_template - stage: containers - variables: - NAME: debian-mips-cross - mipsel-debian-cross-container: extends: .container_job_template stage: containers diff --git a/configure b/configure index 2dddc3c82d..791c48c4e4 100755 --- a/configure +++ b/configure @@ -1330,6 +1330,10 @@ probe_target_compiler() { container_image=debian-all-test-cross container_cross_prefix=mips64-linux-gnuabi64- ;; + mips) + container_image=debian-all-test-cross + container_cross_prefix=mips-linux-gnu- + ;; nios2) container_image=debian-nios2-cross container_cross_prefix=nios2-linux-gnu- diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 5831744a39..dfa9617bb9 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -123,7 +123,6 @@ docker-image-debian-nios2-cross: $(DOCKER_FILES_DIR)/debian-toolchain.docker \ # These images may be good enough for building tests but not for test builds DOCKER_PARTIAL_IMAGES += debian-microblaze-cross -DOCKER_PARTIAL_IMAGES += debian-mips-cross DOCKER_PARTIAL_IMAGES += debian-nios2-cross DOCKER_PARTIAL_IMAGES += debian-riscv64-test-cross DOCKER_PARTIAL_IMAGES += debian-sparc64-cross diff --git a/tests/docker/dockerfiles/debian-mips-cross.docker b/tests/docker/dockerfiles/debian-mips-cross.docker deleted file mode 100644 index 2cbc568ed1..0000000000 --- a/tests/docker/dockerfiles/debian-mips-cross.docker +++ /dev/null @@ -1,19 +0,0 @@ -# -# Docker mips cross-compiler target -# -# This docker target builds on the Debian Bullseye base image. -# -FROM docker.io/library/debian:11-slim - -RUN export DEBIAN_FRONTEND=noninteractive && \ - apt-get update && \ - apt-get install -y eatmydata && \ - eatmydata apt-get dist-upgrade -y && \ - eatmydata apt-get install --no-install-recommends -y \ - gcc-mips-linux-gnu \ - libc6-dev-mips-cross -# As a final step configure the user (if env is defined) -ARG USER -ARG UID -RUN if [ "${USER}" ]; then \ - id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi From 26025d8e3689567121af3bfcd1dd73059b703790 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Sun, 29 Oct 2023 14:50:27 +0000 Subject: [PATCH 270/974] tests/docker: use debian-all-test-cross for riscv64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Maintaining two sets of containers for test building is silly. While it makes sense for the QEMU cross-compile targets to have their own fat containers built by lcitool we might as well merge the other random debian based compilers into the same one used on gitlab. Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20231029145033.592566-14-alex.bennee@linaro.org> --- .gitlab-ci.d/container-cross.yml | 7 ------- configure | 4 ++++ tests/docker/Makefile.include | 1 - .../debian-riscv64-test-cross.docker | 19 ------------------- 4 files changed, 4 insertions(+), 27 deletions(-) delete mode 100644 tests/docker/dockerfiles/debian-riscv64-test-cross.docker diff --git a/.gitlab-ci.d/container-cross.yml b/.gitlab-ci.d/container-cross.yml index 053330d6f4..98940d0f3b 100644 --- a/.gitlab-ci.d/container-cross.yml +++ b/.gitlab-ci.d/container-cross.yml @@ -73,13 +73,6 @@ riscv64-debian-cross-container: NAME: debian-riscv64-cross QEMU_JOB_OPTIONAL: 1 -# we can however build TCG tests using a non-sid base -riscv64-debian-test-cross-container: - extends: .container_job_template - stage: containers - variables: - NAME: debian-riscv64-test-cross - s390x-debian-cross-container: extends: .container_job_template stage: containers diff --git a/configure b/configure index 791c48c4e4..0e0f0c60b5 100755 --- a/configure +++ b/configure @@ -1347,6 +1347,10 @@ probe_target_compiler() { container_image=debian-all-test-cross container_cross_prefix=powerpc${target_arch#ppc}-linux-gnu- ;; + riscv64) + container_image=debian-all-test-cross + container_cross_prefix=riscv64-linux-gnu- + ;; sh4) container_image=debian-legacy-test-cross container_cross_prefix=sh4-linux-gnu- diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index dfa9617bb9..dc55ecf001 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -124,7 +124,6 @@ docker-image-debian-nios2-cross: $(DOCKER_FILES_DIR)/debian-toolchain.docker \ # These images may be good enough for building tests but not for test builds DOCKER_PARTIAL_IMAGES += debian-microblaze-cross DOCKER_PARTIAL_IMAGES += debian-nios2-cross -DOCKER_PARTIAL_IMAGES += debian-riscv64-test-cross DOCKER_PARTIAL_IMAGES += debian-sparc64-cross DOCKER_PARTIAL_IMAGES += debian-xtensa-cross DOCKER_PARTIAL_IMAGES += fedora-cris-cross diff --git a/tests/docker/dockerfiles/debian-riscv64-test-cross.docker b/tests/docker/dockerfiles/debian-riscv64-test-cross.docker deleted file mode 100644 index 6e631295bc..0000000000 --- a/tests/docker/dockerfiles/debian-riscv64-test-cross.docker +++ /dev/null @@ -1,19 +0,0 @@ -# -# Docker cross-compiler target -# -# This docker target builds on the Debian Bullseye base image. -# -FROM docker.io/library/debian:11-slim - -RUN export DEBIAN_FRONTEND=noninteractive && \ - apt-get update && \ - apt-get install -y eatmydata && \ - eatmydata apt-get dist-upgrade -y && \ - eatmydata apt-get install --no-install-recommends -y \ - gcc-riscv64-linux-gnu \ - libc6-dev-riscv64-cross -# As a final step configure the user (if env is defined) -ARG USER -ARG UID -RUN if [ "${USER}" ]; then \ - id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi From 7ccb4153fe7b977a0e3724676a7ab4543b58e496 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Sun, 29 Oct 2023 14:50:28 +0000 Subject: [PATCH 271/974] tests/docker: use debian-all-test-cross for sparc64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Maintaining two sets of containers for test building is silly. While it makes sense for the QEMU cross-compile targets to have their own fat containers built by lcitool we might as well merge the other random debian based compilers into the same one used on gitlab. Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20231029145033.592566-15-alex.bennee@linaro.org> --- .gitlab-ci.d/container-cross.yml | 6 ------ configure | 4 ++++ tests/docker/Makefile.include | 1 - .../dockerfiles/debian-sparc64-cross.docker | 19 ------------------- 4 files changed, 4 insertions(+), 26 deletions(-) delete mode 100644 tests/docker/dockerfiles/debian-sparc64-cross.docker diff --git a/.gitlab-ci.d/container-cross.yml b/.gitlab-ci.d/container-cross.yml index 98940d0f3b..463ac493ad 100644 --- a/.gitlab-ci.d/container-cross.yml +++ b/.gitlab-ci.d/container-cross.yml @@ -79,12 +79,6 @@ s390x-debian-cross-container: variables: NAME: debian-s390x-cross -sparc64-debian-cross-container: - extends: .container_job_template - stage: containers - variables: - NAME: debian-sparc64-cross - tricore-debian-cross-container: extends: .container_job_template stage: containers diff --git a/configure b/configure index 0e0f0c60b5..f1456f6123 100755 --- a/configure +++ b/configure @@ -1355,6 +1355,10 @@ probe_target_compiler() { container_image=debian-legacy-test-cross container_cross_prefix=sh4-linux-gnu- ;; + sparc64) + container_image=debian-all-test-cross + container_cross_prefix=sparc64-linux-gnu- + ;; tricore) container_image=debian-tricore-cross container_cross_prefix=tricore- diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index dc55ecf001..cd4688bf07 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -124,7 +124,6 @@ docker-image-debian-nios2-cross: $(DOCKER_FILES_DIR)/debian-toolchain.docker \ # These images may be good enough for building tests but not for test builds DOCKER_PARTIAL_IMAGES += debian-microblaze-cross DOCKER_PARTIAL_IMAGES += debian-nios2-cross -DOCKER_PARTIAL_IMAGES += debian-sparc64-cross DOCKER_PARTIAL_IMAGES += debian-xtensa-cross DOCKER_PARTIAL_IMAGES += fedora-cris-cross diff --git a/tests/docker/dockerfiles/debian-sparc64-cross.docker b/tests/docker/dockerfiles/debian-sparc64-cross.docker deleted file mode 100644 index 1ef735f223..0000000000 --- a/tests/docker/dockerfiles/debian-sparc64-cross.docker +++ /dev/null @@ -1,19 +0,0 @@ -# -# Docker cross-compiler target -# -# This docker target builds on the Debian Bullseye base image. -# -FROM docker.io/library/debian:11-slim - -RUN export DEBIAN_FRONTEND=noninteractive && \ - apt-get update && \ - apt-get install -y eatmydata && \ - eatmydata apt-get dist-upgrade -y && \ - eatmydata apt-get install --no-install-recommends -y \ - gcc-sparc64-linux-gnu \ - libc6-dev-sparc64-cross -# As a final step configure the user (if env is defined) -ARG USER -ARG UID -RUN if [ "${USER}" ]; then \ - id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi From aba77ac5dba609ccf17d2214a0f17b9a79cefad6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Sun, 29 Oct 2023 14:50:29 +0000 Subject: [PATCH 272/974] tests/docker: upgrade debian-all-test-cross to bookworm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This requires a few more tweaks than usual as: - the default sources format has changed - bring in python3-tomli from the repos - split base install from cross compilers - also include libclang-rt-dev for sanitiser builds Acked-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20231029145033.592566-16-alex.bennee@linaro.org> --- .../dockerfiles/debian-all-test-cross.docker | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/tests/docker/dockerfiles/debian-all-test-cross.docker b/tests/docker/dockerfiles/debian-all-test-cross.docker index 43cc083318..2cc7a24d4d 100644 --- a/tests/docker/dockerfiles/debian-all-test-cross.docker +++ b/tests/docker/dockerfiles/debian-all-test-cross.docker @@ -6,10 +6,10 @@ # basic compilers for as many targets as possible. We shall use this # to build and run linux-user tests on GitLab # -FROM docker.io/library/debian:11-slim +FROM docker.io/library/debian:12-slim # Duplicate deb line as deb-src -RUN cat /etc/apt/sources.list | sed "s/^deb\ /deb-src /" >> /etc/apt/sources.list +RUN sed -in "s/Types: deb/Types: deb deb-src/g" /etc/apt/sources.list.d/debian.sources RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ @@ -25,7 +25,16 @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \ clang \ flex \ git \ + libclang-rt-dev \ ninja-build \ + python3-pip \ + python3-setuptools \ + python3-tomli \ + python3-venv \ + python3-wheel + +RUN DEBIAN_FRONTEND=noninteractive eatmydata \ + apt install -y --no-install-recommends \ gcc-aarch64-linux-gnu \ libc6-dev-arm64-cross \ gcc-arm-linux-gnueabihf \ @@ -53,13 +62,8 @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \ gcc-s390x-linux-gnu \ libc6-dev-s390x-cross \ gcc-sparc64-linux-gnu \ - libc6-dev-sparc64-cross \ - python3-pip \ - python3-setuptools \ - python3-venv \ - python3-wheel + libc6-dev-sparc64-cross -RUN /usr/bin/pip3 install tomli ENV QEMU_CONFIGURE_OPTS --disable-system --disable-docs --disable-tools ENV DEF_TARGET_LIST aarch64-linux-user,arm-linux-user,hppa-linux-user,i386-linux-user,m68k-linux-user,mips-linux-user,mips64-linux-user,mips64el-linux-user,mipsel-linux-user,ppc-linux-user,ppc64-linux-user,ppc64le-linux-user,riscv64-linux-user,s390x-linux-user,sparc64-linux-user From dbd6623ce726878f4cbbad4a1af160fcd1d2fe7b Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 29 Oct 2023 14:50:30 +0000 Subject: [PATCH 273/974] gdbstub: Check if gdb_regs is NULL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cpu->gdb_regs may be NULL if no coprocessor is registered. Fixes: 73c392c26b ("gdbstub: Replace gdb_regs with an array") Signed-off-by: Akihiko Odaki Message-Id: <20231029145033.592566-17-alex.bennee@linaro.org> Message-Id: <20231019101030.128431-2-akihiko.odaki@daynix.com> Tested-by: Fabiano Rosas Reviewed-by: Richard Henderson Tested-by: Richard Henderson Signed-off-by: Alex Bennée --- gdbstub/gdbstub.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c index 1e96a71c0c..29540a0284 100644 --- a/gdbstub/gdbstub.c +++ b/gdbstub/gdbstub.c @@ -385,12 +385,14 @@ static const char *get_feature_xml(const char *p, const char **newp, xml, g_markup_printf_escaped("", cc->gdb_core_xml_file)); - for (guint i = 0; i < cpu->gdb_regs->len; i++) { - r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i); - g_ptr_array_add( - xml, - g_markup_printf_escaped("", - r->xml)); + if (cpu->gdb_regs) { + for (guint i = 0; i < cpu->gdb_regs->len; i++) { + r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i); + g_ptr_array_add( + xml, + g_markup_printf_escaped("", + r->xml)); + } } g_ptr_array_add(xml, g_strdup("")); g_ptr_array_add(xml, NULL); @@ -430,10 +432,12 @@ static int gdb_read_register(CPUState *cpu, GByteArray *buf, int reg) return cc->gdb_read_register(cpu, buf, reg); } - for (guint i = 0; i < cpu->gdb_regs->len; i++) { - r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i); - if (r->base_reg <= reg && reg < r->base_reg + r->num_regs) { - return r->get_reg(env, buf, reg - r->base_reg); + if (cpu->gdb_regs) { + for (guint i = 0; i < cpu->gdb_regs->len; i++) { + r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i); + if (r->base_reg <= reg && reg < r->base_reg + r->num_regs) { + return r->get_reg(env, buf, reg - r->base_reg); + } } } return 0; @@ -449,10 +453,12 @@ static int gdb_write_register(CPUState *cpu, uint8_t *mem_buf, int reg) return cc->gdb_write_register(cpu, mem_buf, reg); } - for (guint i = 0; i < cpu->gdb_regs->len; i++) { - r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i); - if (r->base_reg <= reg && reg < r->base_reg + r->num_regs) { - return r->set_reg(env, mem_buf, reg - r->base_reg); + if (cpu->gdb_regs) { + for (guint i = 0; i < cpu->gdb_regs->len; i++) { + r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i); + if (r->base_reg <= reg && reg < r->base_reg + r->num_regs) { + return r->set_reg(env, mem_buf, reg - r->base_reg); + } } } return 0; From 2eb71a0c20a6a77be128a76c1ef8fb5dc7028a8b Mon Sep 17 00:00:00 2001 From: Matheus Tavares Bernardino Date: Sun, 29 Oct 2023 14:50:31 +0000 Subject: [PATCH 274/974] semihosting: fix memleak at semihosting_arg_fallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We duplicate "cmd" as strtok may modify its argument, but we forgot to free it later. Furthermore, add_semihosting_arg doesn't take responsibility for this memory either (it strdup's the argument). Signed-off-by: Matheus Tavares Bernardino Reviewed-by: Philippe Mathieu-Daudé Message-Id: <03d81c56bfc3d08224e4106efca5949d8894cfa5.1697801632.git.quic_mathbern@quicinc.com> Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20231029145033.592566-18-alex.bennee@linaro.org> --- semihosting/config.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/semihosting/config.c b/semihosting/config.c index 249a377ae8..56283b5c3c 100644 --- a/semihosting/config.c +++ b/semihosting/config.c @@ -113,12 +113,13 @@ static int add_semihosting_arg(void *opaque, void semihosting_arg_fallback(const char *file, const char *cmd) { char *cmd_token; + g_autofree char *cmd_dup = g_strdup(cmd); /* argv[0] */ add_semihosting_arg(&semihosting, "arg", file, NULL); /* split -append and initialize argv[1..n] */ - cmd_token = strtok(g_strdup(cmd), " "); + cmd_token = strtok(cmd_dup, " "); while (cmd_token) { add_semihosting_arg(&semihosting, "arg", cmd_token, NULL); cmd_token = strtok(NULL, " "); From 946bf79e40a6296a46fa7cc4d8c06313ac012a95 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 29 Oct 2023 14:50:32 +0000 Subject: [PATCH 275/974] plugins: Remove an extra parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit copy_call() has an unused parameter so remove it. Signed-off-by: Akihiko Odaki Message-Id: <20231019101030.128431-7-akihiko.odaki@daynix.com> Reviewed-by: Richard Henderson Message-Id: <20231029145033.592566-19-alex.bennee@linaro.org> Signed-off-by: Alex Bennée --- accel/tcg/plugin-gen.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index 39b3c9351f..78b331b251 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -327,8 +327,7 @@ static TCGOp *copy_st_ptr(TCGOp **begin_op, TCGOp *op) return op; } -static TCGOp *copy_call(TCGOp **begin_op, TCGOp *op, void *empty_func, - void *func, int *cb_idx) +static TCGOp *copy_call(TCGOp **begin_op, TCGOp *op, void *func, int *cb_idx) { TCGOp *old_op; int func_idx; @@ -372,8 +371,7 @@ static TCGOp *append_udata_cb(const struct qemu_plugin_dyn_cb *cb, } /* call */ - op = copy_call(&begin_op, op, HELPER(plugin_vcpu_udata_cb), - cb->f.vcpu_udata, cb_idx); + op = copy_call(&begin_op, op, cb->f.vcpu_udata, cb_idx); return op; } @@ -420,8 +418,7 @@ static TCGOp *append_mem_cb(const struct qemu_plugin_dyn_cb *cb, if (type == PLUGIN_GEN_CB_MEM) { /* call */ - op = copy_call(&begin_op, op, HELPER(plugin_vcpu_mem_cb), - cb->f.vcpu_udata, cb_idx); + op = copy_call(&begin_op, op, cb->f.vcpu_udata, cb_idx); } return op; From 7f4527626910f21c9e4421236ee7a6383eb3ce2b Mon Sep 17 00:00:00 2001 From: Cong Liu Date: Sun, 29 Oct 2023 14:50:33 +0000 Subject: [PATCH 276/974] contrib/plugins: Close file descriptor on error return MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch closes the file descriptor fd on error return to avoid resource leak. Fixes: ec7ee95db909 ("contrib/plugins: fix coverity warning in lockstep") Signed-off-by: Cong Liu Message-Id: <20231018025225.1640122-1-liucong2@kylinos.cn> Signed-off-by: Alex Bennée Message-Id: <20231029145033.592566-20-alex.bennee@linaro.org> --- contrib/plugins/lockstep.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/plugins/lockstep.c b/contrib/plugins/lockstep.c index f0cb8792c6..237543b43a 100644 --- a/contrib/plugins/lockstep.c +++ b/contrib/plugins/lockstep.c @@ -257,6 +257,7 @@ static bool setup_socket(const char *path) sockaddr.sun_family = AF_UNIX; if (g_strlcpy(sockaddr.sun_path, path, pathlen) >= pathlen) { perror("bad path"); + close(fd); return false; } @@ -303,6 +304,7 @@ static bool connect_socket(const char *path) sockaddr.sun_family = AF_UNIX; if (g_strlcpy(sockaddr.sun_path, path, pathlen) >= pathlen) { perror("bad path"); + close(fd); return false; } From ccee48aa736b97f0ce4ab04ad41815f0e575d526 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Wed, 13 Sep 2023 16:00:42 -0400 Subject: [PATCH 277/974] block: rename blk_io_plug_call() API to defer_call() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prepare to move the blk_io_plug_call() API out of the block layer so that other subsystems call use this deferred call mechanism. Rename it to defer_call() but leave the code in block/plug.c. The next commit will move the code out of the block layer. Suggested-by: Ilya Maximets Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Paul Durrant Signed-off-by: Stefan Hajnoczi Message-ID: <20230913200045.1024233-2-stefanha@redhat.com> Reviewed-by: Michael S. Tsirkin Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/blkio.c | 8 +-- block/io_uring.c | 4 +- block/linux-aio.c | 4 +- block/nvme.c | 4 +- block/plug.c | 109 +++++++++++++++--------------- hw/block/dataplane/xen-block.c | 10 +-- hw/block/virtio-blk.c | 4 +- hw/scsi/virtio-scsi.c | 6 +- include/sysemu/block-backend-io.h | 6 +- 9 files changed, 76 insertions(+), 79 deletions(-) diff --git a/block/blkio.c b/block/blkio.c index 1dd495617c..7cf6d61f47 100644 --- a/block/blkio.c +++ b/block/blkio.c @@ -312,10 +312,10 @@ static void blkio_detach_aio_context(BlockDriverState *bs) } /* - * Called by blk_io_unplug() or immediately if not plugged. Called without - * blkio_lock. + * Called by defer_call_end() or immediately if not in a deferred section. + * Called without blkio_lock. */ -static void blkio_unplug_fn(void *opaque) +static void blkio_deferred_fn(void *opaque) { BDRVBlkioState *s = opaque; @@ -332,7 +332,7 @@ static void blkio_submit_io(BlockDriverState *bs) { BDRVBlkioState *s = bs->opaque; - blk_io_plug_call(blkio_unplug_fn, s); + defer_call(blkio_deferred_fn, s); } static int coroutine_fn diff --git a/block/io_uring.c b/block/io_uring.c index 69d9820928..8429f341be 100644 --- a/block/io_uring.c +++ b/block/io_uring.c @@ -306,7 +306,7 @@ static void ioq_init(LuringQueue *io_q) io_q->blocked = false; } -static void luring_unplug_fn(void *opaque) +static void luring_deferred_fn(void *opaque) { LuringState *s = opaque; trace_luring_unplug_fn(s, s->io_q.blocked, s->io_q.in_queue, @@ -367,7 +367,7 @@ static int luring_do_submit(int fd, LuringAIOCB *luringcb, LuringState *s, return ret; } - blk_io_plug_call(luring_unplug_fn, s); + defer_call(luring_deferred_fn, s); } return 0; } diff --git a/block/linux-aio.c b/block/linux-aio.c index 1a51503271..49a37174c2 100644 --- a/block/linux-aio.c +++ b/block/linux-aio.c @@ -353,7 +353,7 @@ static uint64_t laio_max_batch(LinuxAioState *s, uint64_t dev_max_batch) return max_batch; } -static void laio_unplug_fn(void *opaque) +static void laio_deferred_fn(void *opaque) { LinuxAioState *s = opaque; @@ -393,7 +393,7 @@ static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset, if (s->io_q.in_queue >= laio_max_batch(s, dev_max_batch)) { ioq_submit(s); } else { - blk_io_plug_call(laio_unplug_fn, s); + defer_call(laio_deferred_fn, s); } } diff --git a/block/nvme.c b/block/nvme.c index b6e95f0b7e..dfbd1085fd 100644 --- a/block/nvme.c +++ b/block/nvme.c @@ -476,7 +476,7 @@ static void nvme_trace_command(const NvmeCmd *cmd) } } -static void nvme_unplug_fn(void *opaque) +static void nvme_deferred_fn(void *opaque) { NVMeQueuePair *q = opaque; @@ -503,7 +503,7 @@ static void nvme_submit_command(NVMeQueuePair *q, NVMeRequest *req, q->need_kick++; qemu_mutex_unlock(&q->lock); - blk_io_plug_call(nvme_unplug_fn, q); + defer_call(nvme_deferred_fn, q); } static void nvme_admin_cmd_sync_cb(void *opaque, int ret) diff --git a/block/plug.c b/block/plug.c index 98a155d2f4..f26173559c 100644 --- a/block/plug.c +++ b/block/plug.c @@ -1,24 +1,21 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * Block I/O plugging + * Deferred calls * * Copyright Red Hat. * - * This API defers a function call within a blk_io_plug()/blk_io_unplug() + * This API defers a function call within a defer_call_begin()/defer_call_end() * section, allowing multiple calls to batch up. This is a performance * optimization that is used in the block layer to submit several I/O requests * at once instead of individually: * - * blk_io_plug(); <-- start of plugged region + * defer_call_begin(); <-- start of section * ... - * blk_io_plug_call(my_func, my_obj); <-- deferred my_func(my_obj) call - * blk_io_plug_call(my_func, my_obj); <-- another - * blk_io_plug_call(my_func, my_obj); <-- another + * defer_call(my_func, my_obj); <-- deferred my_func(my_obj) call + * defer_call(my_func, my_obj); <-- another + * defer_call(my_func, my_obj); <-- another * ... - * blk_io_unplug(); <-- end of plugged region, my_func(my_obj) is called once - * - * This code is actually generic and not tied to the block layer. If another - * subsystem needs this functionality, it could be renamed. + * defer_call_end(); <-- end of section, my_func(my_obj) is called once */ #include "qemu/osdep.h" @@ -27,66 +24,66 @@ #include "qemu/thread.h" #include "sysemu/block-backend.h" -/* A function call that has been deferred until unplug() */ +/* A function call that has been deferred until defer_call_end() */ typedef struct { void (*fn)(void *); void *opaque; -} UnplugFn; +} DeferredCall; /* Per-thread state */ typedef struct { - unsigned count; /* how many times has plug() been called? */ - GArray *unplug_fns; /* functions to call at unplug time */ -} Plug; + unsigned nesting_level; + GArray *deferred_call_array; +} DeferCallThreadState; -/* Use get_ptr_plug() to fetch this thread-local value */ -QEMU_DEFINE_STATIC_CO_TLS(Plug, plug); +/* Use get_ptr_defer_call_thread_state() to fetch this thread-local value */ +QEMU_DEFINE_STATIC_CO_TLS(DeferCallThreadState, defer_call_thread_state); /* Called at thread cleanup time */ -static void blk_io_plug_atexit(Notifier *n, void *value) +static void defer_call_atexit(Notifier *n, void *value) { - Plug *plug = get_ptr_plug(); - g_array_free(plug->unplug_fns, TRUE); + DeferCallThreadState *thread_state = get_ptr_defer_call_thread_state(); + g_array_free(thread_state->deferred_call_array, TRUE); } /* This won't involve coroutines, so use __thread */ -static __thread Notifier blk_io_plug_atexit_notifier; +static __thread Notifier defer_call_atexit_notifier; /** - * blk_io_plug_call: + * defer_call: * @fn: a function pointer to be invoked * @opaque: a user-defined argument to @fn() * - * Call @fn(@opaque) immediately if not within a blk_io_plug()/blk_io_unplug() - * section. + * Call @fn(@opaque) immediately if not within a + * defer_call_begin()/defer_call_end() section. * * Otherwise defer the call until the end of the outermost - * blk_io_plug()/blk_io_unplug() section in this thread. If the same + * defer_call_begin()/defer_call_end() section in this thread. If the same * @fn/@opaque pair has already been deferred, it will only be called once upon - * blk_io_unplug() so that accumulated calls are batched into a single call. + * defer_call_end() so that accumulated calls are batched into a single call. * * The caller must ensure that @opaque is not freed before @fn() is invoked. */ -void blk_io_plug_call(void (*fn)(void *), void *opaque) +void defer_call(void (*fn)(void *), void *opaque) { - Plug *plug = get_ptr_plug(); + DeferCallThreadState *thread_state = get_ptr_defer_call_thread_state(); - /* Call immediately if we're not plugged */ - if (plug->count == 0) { + /* Call immediately if we're not deferring calls */ + if (thread_state->nesting_level == 0) { fn(opaque); return; } - GArray *array = plug->unplug_fns; + GArray *array = thread_state->deferred_call_array; if (!array) { - array = g_array_new(FALSE, FALSE, sizeof(UnplugFn)); - plug->unplug_fns = array; - blk_io_plug_atexit_notifier.notify = blk_io_plug_atexit; - qemu_thread_atexit_add(&blk_io_plug_atexit_notifier); + array = g_array_new(FALSE, FALSE, sizeof(DeferredCall)); + thread_state->deferred_call_array = array; + defer_call_atexit_notifier.notify = defer_call_atexit; + qemu_thread_atexit_add(&defer_call_atexit_notifier); } - UnplugFn *fns = (UnplugFn *)array->data; - UnplugFn new_fn = { + DeferredCall *fns = (DeferredCall *)array->data; + DeferredCall new_fn = { .fn = fn, .opaque = opaque, }; @@ -106,46 +103,46 @@ void blk_io_plug_call(void (*fn)(void *), void *opaque) } /** - * blk_io_plug: Defer blk_io_plug_call() functions until blk_io_unplug() + * defer_call_begin: Defer defer_call() functions until defer_call_end() * - * blk_io_plug/unplug are thread-local operations. This means that multiple - * threads can simultaneously call plug/unplug, but the caller must ensure that - * each unplug() is called in the same thread of the matching plug(). + * defer_call_begin() and defer_call_end() are thread-local operations. The + * caller must ensure that each defer_call_begin() has a matching + * defer_call_end() in the same thread. * - * Nesting is supported. blk_io_plug_call() functions are only called at the - * outermost blk_io_unplug(). + * Nesting is supported. defer_call() functions are only called at the + * outermost defer_call_end(). */ -void blk_io_plug(void) +void defer_call_begin(void) { - Plug *plug = get_ptr_plug(); + DeferCallThreadState *thread_state = get_ptr_defer_call_thread_state(); - assert(plug->count < UINT32_MAX); + assert(thread_state->nesting_level < UINT32_MAX); - plug->count++; + thread_state->nesting_level++; } /** - * blk_io_unplug: Run any pending blk_io_plug_call() functions + * defer_call_end: Run any pending defer_call() functions * - * There must have been a matching blk_io_plug() call in the same thread prior - * to this blk_io_unplug() call. + * There must have been a matching defer_call_begin() call in the same thread + * prior to this defer_call_end() call. */ -void blk_io_unplug(void) +void defer_call_end(void) { - Plug *plug = get_ptr_plug(); + DeferCallThreadState *thread_state = get_ptr_defer_call_thread_state(); - assert(plug->count > 0); + assert(thread_state->nesting_level > 0); - if (--plug->count > 0) { + if (--thread_state->nesting_level > 0) { return; } - GArray *array = plug->unplug_fns; + GArray *array = thread_state->deferred_call_array; if (!array) { return; } - UnplugFn *fns = (UnplugFn *)array->data; + DeferredCall *fns = (DeferredCall *)array->data; for (guint i = 0; i < array->len; i++) { fns[i].fn(fns[i].opaque); diff --git a/hw/block/dataplane/xen-block.c b/hw/block/dataplane/xen-block.c index 3b6f2b0aa2..e9dd8f8a99 100644 --- a/hw/block/dataplane/xen-block.c +++ b/hw/block/dataplane/xen-block.c @@ -509,7 +509,7 @@ static int xen_block_get_request(XenBlockDataPlane *dataplane, /* * Threshold of in-flight requests above which we will start using - * blk_io_plug()/blk_io_unplug() to batch requests. + * defer_call_begin()/defer_call_end() to batch requests. */ #define IO_PLUG_THRESHOLD 1 @@ -537,7 +537,7 @@ static bool xen_block_handle_requests(XenBlockDataPlane *dataplane) * is below us. */ if (inflight_atstart > IO_PLUG_THRESHOLD) { - blk_io_plug(); + defer_call_begin(); } while (rc != rp) { /* pull request from ring */ @@ -577,12 +577,12 @@ static bool xen_block_handle_requests(XenBlockDataPlane *dataplane) if (inflight_atstart > IO_PLUG_THRESHOLD && batched >= inflight_atstart) { - blk_io_unplug(); + defer_call_end(); } xen_block_do_aio(request); if (inflight_atstart > IO_PLUG_THRESHOLD) { if (batched >= inflight_atstart) { - blk_io_plug(); + defer_call_begin(); batched = 0; } else { batched++; @@ -590,7 +590,7 @@ static bool xen_block_handle_requests(XenBlockDataPlane *dataplane) } } if (inflight_atstart > IO_PLUG_THRESHOLD) { - blk_io_unplug(); + defer_call_end(); } return done_something; diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index 39e7f23fab..6a45033d15 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -1134,7 +1134,7 @@ void virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq) bool suppress_notifications = virtio_queue_get_notification(vq); aio_context_acquire(blk_get_aio_context(s->blk)); - blk_io_plug(); + defer_call_begin(); do { if (suppress_notifications) { @@ -1158,7 +1158,7 @@ void virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq) virtio_blk_submit_multireq(s, &mrb); } - blk_io_unplug(); + defer_call_end(); aio_context_release(blk_get_aio_context(s->blk)); } diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index fa53f0902c..42fcbcb45f 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -799,7 +799,7 @@ static int virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req) return -ENOBUFS; } scsi_req_ref(req->sreq); - blk_io_plug(); + defer_call_begin(); object_unref(OBJECT(d)); return 0; } @@ -810,7 +810,7 @@ static void virtio_scsi_handle_cmd_req_submit(VirtIOSCSI *s, VirtIOSCSIReq *req) if (scsi_req_enqueue(sreq)) { scsi_req_continue(sreq); } - blk_io_unplug(); + defer_call_end(); scsi_req_unref(sreq); } @@ -836,7 +836,7 @@ static void virtio_scsi_handle_cmd_vq(VirtIOSCSI *s, VirtQueue *vq) while (!QTAILQ_EMPTY(&reqs)) { req = QTAILQ_FIRST(&reqs); QTAILQ_REMOVE(&reqs, req, next); - blk_io_unplug(); + defer_call_end(); scsi_req_unref(req->sreq); virtqueue_detach_element(req->vq, &req->elem, 0); virtio_scsi_free_req(req); diff --git a/include/sysemu/block-backend-io.h b/include/sysemu/block-backend-io.h index be4dcef59d..cfcfd85c1d 100644 --- a/include/sysemu/block-backend-io.h +++ b/include/sysemu/block-backend-io.h @@ -100,9 +100,9 @@ void blk_iostatus_set_err(BlockBackend *blk, int error); int blk_get_max_iov(BlockBackend *blk); int blk_get_max_hw_iov(BlockBackend *blk); -void blk_io_plug(void); -void blk_io_unplug(void); -void blk_io_plug_call(void (*fn)(void *), void *opaque); +void defer_call_begin(void); +void defer_call_end(void); +void defer_call(void (*fn)(void *), void *opaque); AioContext *blk_get_aio_context(BlockBackend *blk); BlockAcctStats *blk_get_stats(BlockBackend *blk); From 433fcea40c31ff355f84da22a46977c2a1b596c3 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Wed, 13 Sep 2023 16:00:43 -0400 Subject: [PATCH 278/974] util/defer-call: move defer_call() to util/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The networking subsystem may wish to use defer_call(), so move the code to util/ where it can be reused. As a reminder of what defer_call() does: This API defers a function call within a defer_call_begin()/defer_call_end() section, allowing multiple calls to batch up. This is a performance optimization that is used in the block layer to submit several I/O requests at once instead of individually: defer_call_begin(); <-- start of section ... defer_call(my_func, my_obj); <-- deferred my_func(my_obj) call defer_call(my_func, my_obj); <-- another defer_call(my_func, my_obj); <-- another ... defer_call_end(); <-- end of section, my_func(my_obj) is called once Suggested-by: Ilya Maximets Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Stefan Hajnoczi Message-ID: <20230913200045.1024233-3-stefanha@redhat.com> Reviewed-by: Michael S. Tsirkin Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- MAINTAINERS | 3 ++- block/blkio.c | 1 + block/io_uring.c | 1 + block/linux-aio.c | 1 + block/meson.build | 1 - block/nvme.c | 1 + hw/block/dataplane/xen-block.c | 1 + hw/block/virtio-blk.c | 1 + hw/scsi/virtio-scsi.c | 1 + include/qemu/defer-call.h | 16 ++++++++++++++++ include/sysemu/block-backend-io.h | 4 ---- block/plug.c => util/defer-call.c | 2 +- util/meson.build | 1 + 13 files changed, 27 insertions(+), 7 deletions(-) create mode 100644 include/qemu/defer-call.h rename block/plug.c => util/defer-call.c (99%) diff --git a/MAINTAINERS b/MAINTAINERS index cd8d6b140f..018ed62560 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2755,12 +2755,13 @@ S: Supported F: util/async.c F: util/aio-*.c F: util/aio-*.h +F: util/defer-call.c F: util/fdmon-*.c F: block/io.c -F: block/plug.c F: migration/block* F: include/block/aio.h F: include/block/aio-wait.h +F: include/qemu/defer-call.h F: scripts/qemugdb/aio.py F: tests/unit/test-fdmon-epoll.c T: git https://github.com/stefanha/qemu.git block diff --git a/block/blkio.c b/block/blkio.c index 7cf6d61f47..0a0a6c0f5f 100644 --- a/block/blkio.c +++ b/block/blkio.c @@ -13,6 +13,7 @@ #include "block/block_int.h" #include "exec/memory.h" #include "exec/cpu-common.h" /* for qemu_ram_get_fd() */ +#include "qemu/defer-call.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "qapi/qmp/qdict.h" diff --git a/block/io_uring.c b/block/io_uring.c index 8429f341be..3a1e1f45b3 100644 --- a/block/io_uring.c +++ b/block/io_uring.c @@ -15,6 +15,7 @@ #include "block/block.h" #include "block/raw-aio.h" #include "qemu/coroutine.h" +#include "qemu/defer-call.h" #include "qapi/error.h" #include "sysemu/block-backend.h" #include "trace.h" diff --git a/block/linux-aio.c b/block/linux-aio.c index 49a37174c2..a2670b3e46 100644 --- a/block/linux-aio.c +++ b/block/linux-aio.c @@ -14,6 +14,7 @@ #include "block/raw-aio.h" #include "qemu/event_notifier.h" #include "qemu/coroutine.h" +#include "qemu/defer-call.h" #include "qapi/error.h" #include "sysemu/block-backend.h" diff --git a/block/meson.build b/block/meson.build index f351b9d0d3..59ff6d380c 100644 --- a/block/meson.build +++ b/block/meson.build @@ -21,7 +21,6 @@ block_ss.add(files( 'mirror.c', 'nbd.c', 'null.c', - 'plug.c', 'preallocate.c', 'progress_meter.c', 'qapi.c', diff --git a/block/nvme.c b/block/nvme.c index dfbd1085fd..96b3f8f2fa 100644 --- a/block/nvme.c +++ b/block/nvme.c @@ -16,6 +16,7 @@ #include "qapi/error.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qstring.h" +#include "qemu/defer-call.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/module.h" diff --git a/hw/block/dataplane/xen-block.c b/hw/block/dataplane/xen-block.c index e9dd8f8a99..c4bb28c66f 100644 --- a/hw/block/dataplane/xen-block.c +++ b/hw/block/dataplane/xen-block.c @@ -19,6 +19,7 @@ */ #include "qemu/osdep.h" +#include "qemu/defer-call.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/memalign.h" diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index 6a45033d15..a1f8e15522 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -12,6 +12,7 @@ */ #include "qemu/osdep.h" +#include "qemu/defer-call.h" #include "qapi/error.h" #include "qemu/iov.h" #include "qemu/module.h" diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 42fcbcb45f..9c751bf296 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -18,6 +18,7 @@ #include "standard-headers/linux/virtio_ids.h" #include "hw/virtio/virtio-scsi.h" #include "migration/qemu-file-types.h" +#include "qemu/defer-call.h" #include "qemu/error-report.h" #include "qemu/iov.h" #include "qemu/module.h" diff --git a/include/qemu/defer-call.h b/include/qemu/defer-call.h new file mode 100644 index 0000000000..e2c1d24572 --- /dev/null +++ b/include/qemu/defer-call.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Deferred calls + * + * Copyright Red Hat. + */ + +#ifndef QEMU_DEFER_CALL_H +#define QEMU_DEFER_CALL_H + +/* See documentation in util/defer-call.c */ +void defer_call_begin(void); +void defer_call_end(void); +void defer_call(void (*fn)(void *), void *opaque); + +#endif /* QEMU_DEFER_CALL_H */ diff --git a/include/sysemu/block-backend-io.h b/include/sysemu/block-backend-io.h index cfcfd85c1d..d174275a5c 100644 --- a/include/sysemu/block-backend-io.h +++ b/include/sysemu/block-backend-io.h @@ -100,10 +100,6 @@ void blk_iostatus_set_err(BlockBackend *blk, int error); int blk_get_max_iov(BlockBackend *blk); int blk_get_max_hw_iov(BlockBackend *blk); -void defer_call_begin(void); -void defer_call_end(void); -void defer_call(void (*fn)(void *), void *opaque); - AioContext *blk_get_aio_context(BlockBackend *blk); BlockAcctStats *blk_get_stats(BlockBackend *blk); void *blk_aio_get(const AIOCBInfo *aiocb_info, BlockBackend *blk, diff --git a/block/plug.c b/util/defer-call.c similarity index 99% rename from block/plug.c rename to util/defer-call.c index f26173559c..037dc0abf0 100644 --- a/block/plug.c +++ b/util/defer-call.c @@ -22,7 +22,7 @@ #include "qemu/coroutine-tls.h" #include "qemu/notify.h" #include "qemu/thread.h" -#include "sysemu/block-backend.h" +#include "qemu/defer-call.h" /* A function call that has been deferred until defer_call_end() */ typedef struct { diff --git a/util/meson.build b/util/meson.build index c4827fd70a..769b24f2e0 100644 --- a/util/meson.build +++ b/util/meson.build @@ -28,6 +28,7 @@ util_ss.add(when: 'CONFIG_WIN32', if_true: pathcch) if glib_has_gslice util_ss.add(files('qtree.c')) endif +util_ss.add(files('defer-call.c')) util_ss.add(files('envlist.c', 'path.c', 'module.c')) util_ss.add(files('host-utils.c')) util_ss.add(files('bitmap.c', 'bitops.c')) From 84d61e5f36a73ed24742b7c7cf7b811e456dd024 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Wed, 13 Sep 2023 16:00:44 -0400 Subject: [PATCH 279/974] virtio: use defer_call() in virtio_irqfd_notify() virtio-blk and virtio-scsi invoke virtio_irqfd_notify() to send Used Buffer Notifications from an IOThread. This involves an eventfd write(2) syscall. Calling this repeatedly when completing multiple I/O requests in a row is wasteful. Use the defer_call() API to batch together virtio_irqfd_notify() calls made during thread pool (aio=threads), Linux AIO (aio=native), and io_uring (aio=io_uring) completion processing. Behavior is unchanged for emulated devices that do not use defer_call_begin()/defer_call_end() since defer_call() immediately invokes the callback when called outside a defer_call_begin()/defer_call_end() region. fio rw=randread bs=4k iodepth=64 numjobs=8 IOPS increases by ~9% with a single IOThread and 8 vCPUs. iodepth=1 decreases by ~1% but this could be noise. Detailed performance data and configuration specifics are available here: https://gitlab.com/stefanha/virt-playbooks/-/tree/blk_io_plug-irqfd This duplicates the BH that virtio-blk uses for batching. The next commit will remove it. Reviewed-by: Eric Blake Signed-off-by: Stefan Hajnoczi Message-ID: <20230913200045.1024233-4-stefanha@redhat.com> Reviewed-by: Michael S. Tsirkin Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/io_uring.c | 6 ++++++ block/linux-aio.c | 4 ++++ hw/virtio/trace-events | 1 + hw/virtio/virtio.c | 13 ++++++++++++- util/thread-pool.c | 5 +++++ 5 files changed, 28 insertions(+), 1 deletion(-) diff --git a/block/io_uring.c b/block/io_uring.c index 3a1e1f45b3..7cdd00e9f1 100644 --- a/block/io_uring.c +++ b/block/io_uring.c @@ -125,6 +125,9 @@ static void luring_process_completions(LuringState *s) { struct io_uring_cqe *cqes; int total_bytes; + + defer_call_begin(); + /* * Request completion callbacks can run the nested event loop. * Schedule ourselves so the nested event loop will "see" remaining @@ -217,7 +220,10 @@ end: aio_co_wake(luringcb->co); } } + qemu_bh_cancel(s->completion_bh); + + defer_call_end(); } static int ioq_submit(LuringState *s) diff --git a/block/linux-aio.c b/block/linux-aio.c index a2670b3e46..ec05d946f3 100644 --- a/block/linux-aio.c +++ b/block/linux-aio.c @@ -205,6 +205,8 @@ static void qemu_laio_process_completions(LinuxAioState *s) { struct io_event *events; + defer_call_begin(); + /* Reschedule so nested event loops see currently pending completions */ qemu_bh_schedule(s->completion_bh); @@ -231,6 +233,8 @@ static void qemu_laio_process_completions(LinuxAioState *s) * own `for` loop. If we are the last all counters dropped to zero. */ s->event_max = 0; s->event_idx = 0; + + defer_call_end(); } static void qemu_laio_process_completions_and_submit(LinuxAioState *s) diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index 1cb9027d1e..0af7a2886c 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -73,6 +73,7 @@ virtqueue_fill(void *vq, const void *elem, unsigned int len, unsigned int idx) " virtqueue_flush(void *vq, unsigned int count) "vq %p count %u" virtqueue_pop(void *vq, void *elem, unsigned int in_num, unsigned int out_num) "vq %p elem %p in_num %u out_num %u" virtio_queue_notify(void *vdev, int n, void *vq) "vdev %p n %d vq %p" +virtio_notify_irqfd_deferred_fn(void *vdev, void *vq) "vdev %p vq %p" virtio_notify_irqfd(void *vdev, void *vq) "vdev %p vq %p" virtio_notify(void *vdev, void *vq) "vdev %p vq %p" virtio_set_status(void *vdev, uint8_t val) "vdev %p val %u" diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index fb24bc927b..e5105571cf 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -15,6 +15,7 @@ #include "qapi/error.h" #include "qapi/qapi-commands-virtio.h" #include "trace.h" +#include "qemu/defer-call.h" #include "qemu/error-report.h" #include "qemu/log.h" #include "qemu/main-loop.h" @@ -2445,6 +2446,16 @@ static bool virtio_should_notify(VirtIODevice *vdev, VirtQueue *vq) } } +/* Batch irqs while inside a defer_call_begin()/defer_call_end() section */ +static void virtio_notify_irqfd_deferred_fn(void *opaque) +{ + EventNotifier *notifier = opaque; + VirtQueue *vq = container_of(notifier, VirtQueue, guest_notifier); + + trace_virtio_notify_irqfd_deferred_fn(vq->vdev, vq); + event_notifier_set(notifier); +} + void virtio_notify_irqfd(VirtIODevice *vdev, VirtQueue *vq) { WITH_RCU_READ_LOCK_GUARD() { @@ -2471,7 +2482,7 @@ void virtio_notify_irqfd(VirtIODevice *vdev, VirtQueue *vq) * to an atomic operation. */ virtio_set_isr(vq->vdev, 0x1); - event_notifier_set(&vq->guest_notifier); + defer_call(virtio_notify_irqfd_deferred_fn, &vq->guest_notifier); } static void virtio_irq(VirtQueue *vq) diff --git a/util/thread-pool.c b/util/thread-pool.c index 22f9ba3286..27eb777e85 100644 --- a/util/thread-pool.c +++ b/util/thread-pool.c @@ -15,6 +15,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ #include "qemu/osdep.h" +#include "qemu/defer-call.h" #include "qemu/queue.h" #include "qemu/thread.h" #include "qemu/coroutine.h" @@ -175,6 +176,8 @@ static void thread_pool_completion_bh(void *opaque) ThreadPool *pool = opaque; ThreadPoolElement *elem, *next; + defer_call_begin(); /* cb() may use defer_call() to coalesce work */ + restart: QLIST_FOREACH_SAFE(elem, &pool->head, all, next) { if (elem->state != THREAD_DONE) { @@ -208,6 +211,8 @@ restart: qemu_aio_unref(elem); } } + + defer_call_end(); } static void thread_pool_cancel(BlockAIOCB *acb) From 073458da5619c8914a91440ef243d1e2b888c1b7 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Wed, 13 Sep 2023 16:00:45 -0400 Subject: [PATCH 280/974] virtio-blk: remove batch notification BH There is a batching mechanism for virtio-blk Used Buffer Notifications that is no longer needed because the previous commit added batching to virtio_notify_irqfd(). Note that this mechanism was rarely used in practice because it is only enabled when EVENT_IDX is not negotiated by the driver. Modern drivers enable EVENT_IDX. Reviewed-by: Eric Blake Signed-off-by: Stefan Hajnoczi Message-ID: <20230913200045.1024233-5-stefanha@redhat.com> Reviewed-by: Michael S. Tsirkin Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- hw/block/dataplane/virtio-blk.c | 48 +-------------------------------- 1 file changed, 1 insertion(+), 47 deletions(-) diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c index da36fcfd0b..f83bb0f116 100644 --- a/hw/block/dataplane/virtio-blk.c +++ b/hw/block/dataplane/virtio-blk.c @@ -31,9 +31,6 @@ struct VirtIOBlockDataPlane { VirtIOBlkConf *conf; VirtIODevice *vdev; - QEMUBH *bh; /* bh for guest notification */ - unsigned long *batch_notify_vqs; - bool batch_notifications; /* Note that these EventNotifiers are assigned by value. This is * fine as long as you do not call event_notifier_cleanup on them @@ -47,36 +44,7 @@ struct VirtIOBlockDataPlane { /* Raise an interrupt to signal guest, if necessary */ void virtio_blk_data_plane_notify(VirtIOBlockDataPlane *s, VirtQueue *vq) { - if (s->batch_notifications) { - set_bit(virtio_get_queue_index(vq), s->batch_notify_vqs); - qemu_bh_schedule(s->bh); - } else { - virtio_notify_irqfd(s->vdev, vq); - } -} - -static void notify_guest_bh(void *opaque) -{ - VirtIOBlockDataPlane *s = opaque; - unsigned nvqs = s->conf->num_queues; - unsigned long bitmap[BITS_TO_LONGS(nvqs)]; - unsigned j; - - memcpy(bitmap, s->batch_notify_vqs, sizeof(bitmap)); - memset(s->batch_notify_vqs, 0, sizeof(bitmap)); - - for (j = 0; j < nvqs; j += BITS_PER_LONG) { - unsigned long bits = bitmap[j / BITS_PER_LONG]; - - while (bits != 0) { - unsigned i = j + ctzl(bits); - VirtQueue *vq = virtio_get_queue(s->vdev, i); - - virtio_notify_irqfd(s->vdev, vq); - - bits &= bits - 1; /* clear right-most bit */ - } - } + virtio_notify_irqfd(s->vdev, vq); } /* Context: QEMU global mutex held */ @@ -126,9 +94,6 @@ bool virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf, } else { s->ctx = qemu_get_aio_context(); } - s->bh = aio_bh_new_guarded(s->ctx, notify_guest_bh, s, - &DEVICE(vdev)->mem_reentrancy_guard); - s->batch_notify_vqs = bitmap_new(conf->num_queues); *dataplane = s; @@ -146,8 +111,6 @@ void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s) vblk = VIRTIO_BLK(s->vdev); assert(!vblk->dataplane_started); - g_free(s->batch_notify_vqs); - qemu_bh_delete(s->bh); if (s->iothread) { object_unref(OBJECT(s->iothread)); } @@ -173,12 +136,6 @@ int virtio_blk_data_plane_start(VirtIODevice *vdev) s->starting = true; - if (!virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { - s->batch_notifications = true; - } else { - s->batch_notifications = false; - } - /* Set up guest notifier (irq) */ r = k->set_guest_notifiers(qbus->parent, nvqs, true); if (r != 0) { @@ -370,9 +327,6 @@ void virtio_blk_data_plane_stop(VirtIODevice *vdev) aio_context_release(s->ctx); - qemu_bh_cancel(s->bh); - notify_guest_bh(s); /* final chance to notify guest */ - /* Clean up guest notifier (irq) */ k->set_guest_notifiers(qbus->parent, nvqs, false); From 61a3a5a76a993b74c85f92585b10250d12323c38 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Tue, 31 Oct 2023 14:54:22 +0100 Subject: [PATCH 281/974] blockjob: introduce block-job-change QMP command which will allow changing job-type-specific options after job creation. In the JobVerbTable, the same allow bits as for set-speed are used, because set-speed can be considered an existing change command. Signed-off-by: Fiona Ebner Reviewed-by: Vladimir Sementsov-Ogievskiy Message-ID: <20231031135431.393137-2-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- blockdev.c | 14 ++++++++++++++ blockjob.c | 20 ++++++++++++++++++++ include/block/blockjob.h | 11 +++++++++++ include/block/blockjob_int.h | 7 +++++++ job.c | 1 + qapi/block-core.json | 26 ++++++++++++++++++++++++++ qapi/job.json | 4 +++- 7 files changed, 82 insertions(+), 1 deletion(-) diff --git a/blockdev.c b/blockdev.c index 877e3a26d4..1517dc6210 100644 --- a/blockdev.c +++ b/blockdev.c @@ -3392,6 +3392,20 @@ void qmp_block_job_dismiss(const char *id, Error **errp) job_dismiss_locked(&job, errp); } +void qmp_block_job_change(BlockJobChangeOptions *opts, Error **errp) +{ + BlockJob *job; + + JOB_LOCK_GUARD(); + job = find_block_job_locked(opts->id, errp); + + if (!job) { + return; + } + + block_job_change_locked(job, opts, errp); +} + void qmp_change_backing_file(const char *device, const char *image_node_name, const char *backing_file, diff --git a/blockjob.c b/blockjob.c index 953dc1b6dc..f0505ad232 100644 --- a/blockjob.c +++ b/blockjob.c @@ -330,6 +330,26 @@ static bool block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) return block_job_set_speed_locked(job, speed, errp); } +void block_job_change_locked(BlockJob *job, BlockJobChangeOptions *opts, + Error **errp) +{ + const BlockJobDriver *drv = block_job_driver(job); + + GLOBAL_STATE_CODE(); + + if (job_apply_verb_locked(&job->job, JOB_VERB_CHANGE, errp)) { + return; + } + + if (drv->change) { + job_unlock(); + drv->change(job, opts, errp); + job_lock(); + } else { + error_setg(errp, "Job type does not support change"); + } +} + void block_job_ratelimit_processed_bytes(BlockJob *job, uint64_t n) { IO_CODE(); diff --git a/include/block/blockjob.h b/include/block/blockjob.h index 058b0c824c..95854f1477 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -172,6 +172,17 @@ bool block_job_has_bdrv(BlockJob *job, BlockDriverState *bs); */ bool block_job_set_speed_locked(BlockJob *job, int64_t speed, Error **errp); +/** + * block_job_change_locked: + * @job: The job to change. + * @opts: The new options. + * @errp: Error object. + * + * Change the job according to opts. + */ +void block_job_change_locked(BlockJob *job, BlockJobChangeOptions *opts, + Error **errp); + /** * block_job_query_locked: * @job: The job to get information about. diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h index 104824040c..a4656d4cb5 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -67,6 +67,13 @@ struct BlockJobDriver { void (*attached_aio_context)(BlockJob *job, AioContext *new_context); void (*set_speed)(BlockJob *job, int64_t speed); + + /* + * Change the @job's options according to @opts. + * + * Note that this can already be called before the job coroutine is running. + */ + void (*change)(BlockJob *job, BlockJobChangeOptions *opts, Error **errp); }; /* diff --git a/job.c b/job.c index 72d57f0934..99a2e54b54 100644 --- a/job.c +++ b/job.c @@ -80,6 +80,7 @@ bool JobVerbTable[JOB_VERB__MAX][JOB_STATUS__MAX] = { [JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0}, [JOB_VERB_FINALIZE] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}, [JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, + [JOB_VERB_CHANGE] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, }; /* Transactional group of jobs */ diff --git a/qapi/block-core.json b/qapi/block-core.json index 89751d81f2..c6f31a9399 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -3044,6 +3044,32 @@ { 'command': 'block-job-finalize', 'data': { 'id': 'str' }, 'allow-preconfig': true } +## +# @BlockJobChangeOptions: +# +# Block job options that can be changed after job creation. +# +# @id: The job identifier +# +# @type: The job type +# +# Since 8.2 +## +{ 'union': 'BlockJobChangeOptions', + 'base': { 'id': 'str', 'type': 'JobType' }, + 'discriminator': 'type', + 'data': {} } + +## +# @block-job-change: +# +# Change the block job's options. +# +# Since: 8.2 +## +{ 'command': 'block-job-change', + 'data': 'BlockJobChangeOptions', 'boxed': true } + ## # @BlockdevDiscardOptions: # diff --git a/qapi/job.json b/qapi/job.json index 7f0ba090de..b3957207a4 100644 --- a/qapi/job.json +++ b/qapi/job.json @@ -105,11 +105,13 @@ # # @finalize: see @job-finalize # +# @change: see @block-job-change (since 8.2) +# # Since: 2.12 ## { 'enum': 'JobVerb', 'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss', - 'finalize' ] } + 'finalize', 'change' ] } ## # @JOB_STATUS_CHANGE: From c45d0e1af0b9e8fa0b0abc30bd6a4ef8d37964b4 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Tue, 31 Oct 2023 14:54:23 +0100 Subject: [PATCH 282/974] block/mirror: set actively_synced even after the job is ready In preparation to allow switching from background to active mode. This ensures that setting actively_synced will not be missed when the switch happens after the job is ready. Signed-off-by: Fiona Ebner Reviewed-by: Vladimir Sementsov-Ogievskiy Message-ID: <20231031135431.393137-3-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/mirror.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index dcd88de2e3..1c2c00ee1d 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -1074,9 +1074,9 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) * the target in a consistent state. */ job_transition_to_ready(&s->common.job); - if (s->copy_mode != MIRROR_COPY_MODE_BACKGROUND) { - s->actively_synced = true; - } + } + if (s->copy_mode != MIRROR_COPY_MODE_BACKGROUND) { + s->actively_synced = true; } should_complete = s->should_complete || From 058cfca5645a9ed7cb2bdb77d15f2eacaf343694 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Tue, 31 Oct 2023 14:54:24 +0100 Subject: [PATCH 283/974] block/mirror: move dirty bitmap to filter In preparation to allow switching to active mode without draining. Initialization of the bitmap in mirror_dirty_init() still happens with the original/backing BlockDriverState, which should be fine, because the mirror top has the same length. Suggested-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Fiona Ebner Message-ID: <20231031135431.393137-4-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/mirror.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index 1c2c00ee1d..914d723446 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -1500,6 +1500,11 @@ bdrv_mirror_top_do_write(BlockDriverState *bs, MirrorMethod method, abort(); } + if (!copy_to_target && s->job && s->job->dirty_bitmap) { + s->job->actively_synced = false; + bdrv_set_dirty_bitmap(s->job->dirty_bitmap, offset, bytes); + } + if (ret < 0) { goto out; } @@ -1823,13 +1828,17 @@ static BlockJob *mirror_start_job( s->should_complete = true; } - s->dirty_bitmap = bdrv_create_dirty_bitmap(bs, granularity, NULL, errp); + s->dirty_bitmap = bdrv_create_dirty_bitmap(s->mirror_top_bs, granularity, + NULL, errp); if (!s->dirty_bitmap) { goto fail; } - if (s->copy_mode == MIRROR_COPY_MODE_WRITE_BLOCKING) { - bdrv_disable_dirty_bitmap(s->dirty_bitmap); - } + + /* + * The dirty bitmap is set by bdrv_mirror_top_do_write() when not in active + * mode. + */ + bdrv_disable_dirty_bitmap(s->dirty_bitmap); ret = block_job_add_bdrv(&s->common, "source", bs, 0, BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE | From 7b32ad2242d6bcb1d5840466bf846fada8ca42e8 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Tue, 31 Oct 2023 14:54:25 +0100 Subject: [PATCH 284/974] block/mirror: determine copy_to_target only once In preparation to allow changing the copy_mode via QMP. When running in an iothread, it could be that copy_mode is changed from the main thread in between reading copy_mode in bdrv_mirror_top_pwritev() and reading copy_mode in bdrv_mirror_top_do_write(), so they might end up disagreeing about whether copy_to_target is true or false. Avoid that scenario by determining copy_to_target only once and passing it to bdrv_mirror_top_do_write() as an argument. Signed-off-by: Fiona Ebner Reviewed-by: Vladimir Sementsov-Ogievskiy Message-ID: <20231031135431.393137-5-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/mirror.c | 41 ++++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index 914d723446..31da1526eb 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -1463,21 +1463,21 @@ bdrv_mirror_top_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags); } +static bool should_copy_to_target(MirrorBDSOpaque *s) +{ + return s->job && s->job->ret >= 0 && + !job_is_cancelled(&s->job->common.job) && + s->job->copy_mode == MIRROR_COPY_MODE_WRITE_BLOCKING; +} + static int coroutine_fn GRAPH_RDLOCK bdrv_mirror_top_do_write(BlockDriverState *bs, MirrorMethod method, - uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, - int flags) + bool copy_to_target, uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags) { MirrorOp *op = NULL; MirrorBDSOpaque *s = bs->opaque; int ret = 0; - bool copy_to_target = false; - - if (s->job) { - copy_to_target = s->job->ret >= 0 && - !job_is_cancelled(&s->job->common.job) && - s->job->copy_mode == MIRROR_COPY_MODE_WRITE_BLOCKING; - } if (copy_to_target) { op = active_write_prepare(s->job, offset, bytes); @@ -1524,17 +1524,10 @@ static int coroutine_fn GRAPH_RDLOCK bdrv_mirror_top_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { - MirrorBDSOpaque *s = bs->opaque; QEMUIOVector bounce_qiov; void *bounce_buf; int ret = 0; - bool copy_to_target = false; - - if (s->job) { - copy_to_target = s->job->ret >= 0 && - !job_is_cancelled(&s->job->common.job) && - s->job->copy_mode == MIRROR_COPY_MODE_WRITE_BLOCKING; - } + bool copy_to_target = should_copy_to_target(bs->opaque); if (copy_to_target) { /* The guest might concurrently modify the data to write; but @@ -1551,8 +1544,8 @@ bdrv_mirror_top_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, flags &= ~BDRV_REQ_REGISTERED_BUF; } - ret = bdrv_mirror_top_do_write(bs, MIRROR_METHOD_COPY, offset, bytes, qiov, - flags); + ret = bdrv_mirror_top_do_write(bs, MIRROR_METHOD_COPY, copy_to_target, + offset, bytes, qiov, flags); if (copy_to_target) { qemu_iovec_destroy(&bounce_qiov); @@ -1575,15 +1568,17 @@ static int coroutine_fn GRAPH_RDLOCK bdrv_mirror_top_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, BdrvRequestFlags flags) { - return bdrv_mirror_top_do_write(bs, MIRROR_METHOD_ZERO, offset, bytes, NULL, - flags); + bool copy_to_target = should_copy_to_target(bs->opaque); + return bdrv_mirror_top_do_write(bs, MIRROR_METHOD_ZERO, copy_to_target, + offset, bytes, NULL, flags); } static int coroutine_fn GRAPH_RDLOCK bdrv_mirror_top_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) { - return bdrv_mirror_top_do_write(bs, MIRROR_METHOD_DISCARD, offset, bytes, - NULL, 0); + bool copy_to_target = should_copy_to_target(bs->opaque); + return bdrv_mirror_top_do_write(bs, MIRROR_METHOD_DISCARD, copy_to_target, + offset, bytes, NULL, 0); } static void bdrv_mirror_top_refresh_filename(BlockDriverState *bs) From 2d400d15a02dca3b7b90761b2f0bb2322e99e11a Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Tue, 31 Oct 2023 14:54:26 +0100 Subject: [PATCH 285/974] mirror: implement mirror_change method which allows switching the @copy-mode from 'background' to 'write-blocking'. This is useful for management applications, so they can start out in background mode to avoid limiting guest write speed and switch to active mode when certain criteria are fulfilled. In presence of an iothread, the copy_mode member is now shared between the iothread and the main thread, so turn accesses to it atomic. Signed-off-by: Fiona Ebner Message-ID: <20231031135431.393137-6-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/mirror.c | 44 +++++++++++++++++++++++++++++++++++++++++--- qapi/block-core.json | 13 ++++++++++++- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index 31da1526eb..4016d89253 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -55,6 +55,10 @@ typedef struct MirrorBlockJob { BlockMirrorBackingMode backing_mode; /* Whether the target image requires explicit zero-initialization */ bool zero_target; + /* + * To be accesssed with atomics. Written only under the BQL (required by the + * current implementation of mirror_change()). + */ MirrorCopyMode copy_mode; BlockdevOnError on_source_error, on_target_error; /* Set when the target is synced (dirty bitmap is clean, nothing @@ -1075,7 +1079,7 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) */ job_transition_to_ready(&s->common.job); } - if (s->copy_mode != MIRROR_COPY_MODE_BACKGROUND) { + if (qatomic_read(&s->copy_mode) != MIRROR_COPY_MODE_BACKGROUND) { s->actively_synced = true; } @@ -1246,6 +1250,39 @@ static bool commit_active_cancel(Job *job, bool force) return force || !job_is_ready(job); } +static void mirror_change(BlockJob *job, BlockJobChangeOptions *opts, + Error **errp) +{ + MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); + BlockJobChangeOptionsMirror *change_opts = &opts->u.mirror; + MirrorCopyMode current; + + /* + * The implementation relies on the fact that copy_mode is only written + * under the BQL. Otherwise, further synchronization would be required. + */ + + GLOBAL_STATE_CODE(); + + if (qatomic_read(&s->copy_mode) == change_opts->copy_mode) { + return; + } + + if (change_opts->copy_mode != MIRROR_COPY_MODE_WRITE_BLOCKING) { + error_setg(errp, "Change to copy mode '%s' is not implemented", + MirrorCopyMode_str(change_opts->copy_mode)); + return; + } + + current = qatomic_cmpxchg(&s->copy_mode, MIRROR_COPY_MODE_BACKGROUND, + change_opts->copy_mode); + if (current != MIRROR_COPY_MODE_BACKGROUND) { + error_setg(errp, "Expected current copy mode '%s', got '%s'", + MirrorCopyMode_str(MIRROR_COPY_MODE_BACKGROUND), + MirrorCopyMode_str(current)); + } +} + static const BlockJobDriver mirror_job_driver = { .job_driver = { .instance_size = sizeof(MirrorBlockJob), @@ -1260,6 +1297,7 @@ static const BlockJobDriver mirror_job_driver = { .cancel = mirror_cancel, }, .drained_poll = mirror_drained_poll, + .change = mirror_change, }; static const BlockJobDriver commit_active_job_driver = { @@ -1467,7 +1505,7 @@ static bool should_copy_to_target(MirrorBDSOpaque *s) { return s->job && s->job->ret >= 0 && !job_is_cancelled(&s->job->common.job) && - s->job->copy_mode == MIRROR_COPY_MODE_WRITE_BLOCKING; + qatomic_read(&s->job->copy_mode) == MIRROR_COPY_MODE_WRITE_BLOCKING; } static int coroutine_fn GRAPH_RDLOCK @@ -1813,7 +1851,7 @@ static BlockJob *mirror_start_job( s->is_none_mode = is_none_mode; s->backing_mode = backing_mode; s->zero_target = zero_target; - s->copy_mode = copy_mode; + qatomic_set(&s->copy_mode, copy_mode); s->base = base; s->base_overlay = bdrv_find_overlay(bs, base); s->granularity = granularity; diff --git a/qapi/block-core.json b/qapi/block-core.json index c6f31a9399..6369207be2 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -3044,6 +3044,17 @@ { 'command': 'block-job-finalize', 'data': { 'id': 'str' }, 'allow-preconfig': true } +## +# @BlockJobChangeOptionsMirror: +# +# @copy-mode: Switch to this copy mode. Currently, only the switch +# from 'background' to 'write-blocking' is implemented. +# +# Since: 8.2 +## +{ 'struct': 'BlockJobChangeOptionsMirror', + 'data': { 'copy-mode' : 'MirrorCopyMode' } } + ## # @BlockJobChangeOptions: # @@ -3058,7 +3069,7 @@ { 'union': 'BlockJobChangeOptions', 'base': { 'id': 'str', 'type': 'JobType' }, 'discriminator': 'type', - 'data': {} } + 'data': { 'mirror': 'BlockJobChangeOptionsMirror' } } ## # @block-job-change: From d67c54d05fe05cf1b6a3b2cf36cc3832d2a0ce05 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Tue, 31 Oct 2023 14:54:27 +0100 Subject: [PATCH 286/974] qapi/block-core: use JobType for BlockJobInfo's type In preparation to turn BlockJobInfo into a union with @type as the discriminator. That requires it to be an enum. Even without that requirement, it's nicer to have an enum instead of a str here. No functional change is intended. Signed-off-by: Fiona Ebner Reviewed-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Markus Armbruster Message-ID: <20231031135431.393137-7-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/monitor/block-hmp-cmds.c | 4 ++-- blockjob.c | 2 +- qapi/block-core.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c index 7645c7e5fb..5b2c597e7a 100644 --- a/block/monitor/block-hmp-cmds.c +++ b/block/monitor/block-hmp-cmds.c @@ -846,7 +846,7 @@ void hmp_info_block_jobs(Monitor *mon, const QDict *qdict) } while (list) { - if (strcmp(list->value->type, "stream") == 0) { + if (list->value->type == JOB_TYPE_STREAM) { monitor_printf(mon, "Streaming device %s: Completed %" PRId64 " of %" PRId64 " bytes, speed limit %" PRId64 " bytes/s\n", @@ -858,7 +858,7 @@ void hmp_info_block_jobs(Monitor *mon, const QDict *qdict) monitor_printf(mon, "Type %s, device %s: Completed %" PRId64 " of %" PRId64 " bytes, speed limit %" PRId64 " bytes/s\n", - list->value->type, + JobType_str(list->value->type), list->value->device, list->value->offset, list->value->len, diff --git a/blockjob.c b/blockjob.c index f0505ad232..5b4786a70f 100644 --- a/blockjob.c +++ b/blockjob.c @@ -390,7 +390,7 @@ BlockJobInfo *block_job_query_locked(BlockJob *job, Error **errp) &progress_total); info = g_new0(BlockJobInfo, 1); - info->type = g_strdup(job_type_str(&job->job)); + info->type = job_type(&job->job); info->device = g_strdup(job->job.id); info->busy = job->job.busy; info->paused = job->job.pause_count > 0; diff --git a/qapi/block-core.json b/qapi/block-core.json index 6369207be2..9d03210664 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1396,7 +1396,7 @@ # Since: 1.1 ## { 'struct': 'BlockJobInfo', - 'data': {'type': 'str', 'device': 'str', 'len': 'int', + 'data': {'type': 'JobType', 'device': 'str', 'len': 'int', 'offset': 'int', 'busy': 'bool', 'paused': 'bool', 'speed': 'int', 'io-status': 'BlockDeviceIoStatus', 'ready': 'bool', 'status': 'JobStatus', From 701efc9f2d68707e20eb6f1f9e7781aa639cf0cb Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Tue, 31 Oct 2023 14:54:28 +0100 Subject: [PATCH 287/974] qapi/block-core: turn BlockJobInfo into a union In preparation to additionally return job-type-specific information. Signed-off-by: Fiona Ebner Reviewed-by: Vladimir Sementsov-Ogievskiy Message-ID: <20231031135431.393137-8-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- qapi/block-core.json | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index 9d03210664..dca0e94bb0 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1395,13 +1395,15 @@ # # Since: 1.1 ## -{ 'struct': 'BlockJobInfo', - 'data': {'type': 'JobType', 'device': 'str', 'len': 'int', +{ 'union': 'BlockJobInfo', + 'base': {'type': 'JobType', 'device': 'str', 'len': 'int', 'offset': 'int', 'busy': 'bool', 'paused': 'bool', 'speed': 'int', 'io-status': 'BlockDeviceIoStatus', 'ready': 'bool', 'status': 'JobStatus', 'auto-finalize': 'bool', 'auto-dismiss': 'bool', - '*error': 'str' } } + '*error': 'str' }, + 'discriminator': 'type', + 'data': {} } ## # @query-block-jobs: From 59fd82544dea5042deb02c26f61d6f39bd187c02 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Tue, 31 Oct 2023 14:54:29 +0100 Subject: [PATCH 288/974] blockjob: query driver-specific info via a new 'query' driver method Signed-off-by: Fiona Ebner Message-ID: <20231031135431.393137-9-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- blockjob.c | 6 ++++++ include/block/blockjob_int.h | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/blockjob.c b/blockjob.c index 5b4786a70f..5b24de356d 100644 --- a/blockjob.c +++ b/blockjob.c @@ -378,6 +378,7 @@ BlockJobInfo *block_job_query_locked(BlockJob *job, Error **errp) { BlockJobInfo *info; uint64_t progress_current, progress_total; + const BlockJobDriver *drv = block_job_driver(job); GLOBAL_STATE_CODE(); @@ -407,6 +408,11 @@ BlockJobInfo *block_job_query_locked(BlockJob *job, Error **errp) g_strdup(error_get_pretty(job->job.err)) : g_strdup(strerror(-job->job.ret)); } + if (drv->query) { + job_unlock(); + drv->query(job, info); + job_lock(); + } return info; } diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h index a4656d4cb5..18ee6f7bf0 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -74,6 +74,11 @@ struct BlockJobDriver { * Note that this can already be called before the job coroutine is running. */ void (*change)(BlockJob *job, BlockJobChangeOptions *opts, Error **errp); + + /* + * Query information specific to this kind of block job. + */ + void (*query)(BlockJob *job, BlockJobInfo *info); }; /* From 76cb2f2491abcf191439ea5052999afed514b3da Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Tue, 31 Oct 2023 14:54:30 +0100 Subject: [PATCH 289/974] mirror: return mirror-specific information upon query To start out, only actively-synced is returned. For example, this is useful for jobs that started out in background mode and switched to active mode. Once actively-synced is true, it's clear that the mode switch has been completed. Note that completion of the switch might happen much earlier, e.g. if the switch happens before the job is ready, once all background operations have finished. It's assumed that whether the disks are actively-synced or not is more interesting than whether the mode switch completed. That information can still be added if required in the future. In presence of an iothread, the actively_synced member is now shared between the iothread and the main thread, so turn accesses to it atomic. Requires to adapt the output for iotest 109. Signed-off-by: Fiona Ebner Message-ID: <20231031135431.393137-10-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/mirror.c | 31 +++++++++++++++++++++++-------- qapi/block-core.json | 16 +++++++++++++++- tests/qemu-iotests/109.out | 24 ++++++++++++------------ 3 files changed, 50 insertions(+), 21 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index 4016d89253..c839542774 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -61,8 +61,12 @@ typedef struct MirrorBlockJob { */ MirrorCopyMode copy_mode; BlockdevOnError on_source_error, on_target_error; - /* Set when the target is synced (dirty bitmap is clean, nothing - * in flight) and the job is running in active mode */ + /* + * To be accessed with atomics. + * + * Set when the target is synced (dirty bitmap is clean, nothing in flight) + * and the job is running in active mode. + */ bool actively_synced; bool should_complete; int64_t granularity; @@ -126,7 +130,7 @@ typedef enum MirrorMethod { static BlockErrorAction mirror_error_action(MirrorBlockJob *s, bool read, int error) { - s->actively_synced = false; + qatomic_set(&s->actively_synced, false); if (read) { return block_job_error_action(&s->common, s->on_source_error, true, error); @@ -966,7 +970,7 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) if (s->bdev_length == 0) { /* Transition to the READY state and wait for complete. */ job_transition_to_ready(&s->common.job); - s->actively_synced = true; + qatomic_set(&s->actively_synced, true); while (!job_cancel_requested(&s->common.job) && !s->should_complete) { job_yield(&s->common.job); } @@ -1080,7 +1084,7 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) job_transition_to_ready(&s->common.job); } if (qatomic_read(&s->copy_mode) != MIRROR_COPY_MODE_BACKGROUND) { - s->actively_synced = true; + qatomic_set(&s->actively_synced, true); } should_complete = s->should_complete || @@ -1283,6 +1287,15 @@ static void mirror_change(BlockJob *job, BlockJobChangeOptions *opts, } } +static void mirror_query(BlockJob *job, BlockJobInfo *info) +{ + MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); + + info->u.mirror = (BlockJobInfoMirror) { + .actively_synced = qatomic_read(&s->actively_synced), + }; +} + static const BlockJobDriver mirror_job_driver = { .job_driver = { .instance_size = sizeof(MirrorBlockJob), @@ -1298,6 +1311,7 @@ static const BlockJobDriver mirror_job_driver = { }, .drained_poll = mirror_drained_poll, .change = mirror_change, + .query = mirror_query, }; static const BlockJobDriver commit_active_job_driver = { @@ -1416,7 +1430,7 @@ do_sync_target_write(MirrorBlockJob *job, MirrorMethod method, bitmap_end = QEMU_ALIGN_UP(offset + bytes, job->granularity); bdrv_set_dirty_bitmap(job->dirty_bitmap, bitmap_offset, bitmap_end - bitmap_offset); - job->actively_synced = false; + qatomic_set(&job->actively_synced, false); action = mirror_error_action(job, false, -ret); if (action == BLOCK_ERROR_ACTION_REPORT) { @@ -1475,7 +1489,8 @@ static void coroutine_fn GRAPH_RDLOCK active_write_settle(MirrorOp *op) uint64_t end_chunk = DIV_ROUND_UP(op->offset + op->bytes, op->s->granularity); - if (!--op->s->in_active_write_counter && op->s->actively_synced) { + if (!--op->s->in_active_write_counter && + qatomic_read(&op->s->actively_synced)) { BdrvChild *source = op->s->mirror_top_bs->backing; if (QLIST_FIRST(&source->bs->parents) == source && @@ -1539,7 +1554,7 @@ bdrv_mirror_top_do_write(BlockDriverState *bs, MirrorMethod method, } if (!copy_to_target && s->job && s->job->dirty_bitmap) { - s->job->actively_synced = false; + qatomic_set(&s->job->actively_synced, false); bdrv_set_dirty_bitmap(s->job->dirty_bitmap, offset, bytes); } diff --git a/qapi/block-core.json b/qapi/block-core.json index dca0e94bb0..99961256f2 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1352,6 +1352,20 @@ { 'enum': 'MirrorCopyMode', 'data': ['background', 'write-blocking'] } +## +# @BlockJobInfoMirror: +# +# Information specific to mirror block jobs. +# +# @actively-synced: Whether the source is actively synced to the +# target, i.e. same data and new writes are done synchronously to +# both. +# +# Since 8.2 +## +{ 'struct': 'BlockJobInfoMirror', + 'data': { 'actively-synced': 'bool' } } + ## # @BlockJobInfo: # @@ -1403,7 +1417,7 @@ 'auto-finalize': 'bool', 'auto-dismiss': 'bool', '*error': 'str' }, 'discriminator': 'type', - 'data': {} } + 'data': { 'mirror': 'BlockJobInfoMirror' } } ## # @query-block-jobs: diff --git a/tests/qemu-iotests/109.out b/tests/qemu-iotests/109.out index 2611d6a40f..965c9a6a0a 100644 --- a/tests/qemu-iotests/109.out +++ b/tests/qemu-iotests/109.out @@ -38,7 +38,7 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} @@ -90,7 +90,7 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 197120, "offset": 197120, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 197120, "offset": 197120, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} @@ -142,7 +142,7 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} @@ -194,7 +194,7 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} @@ -246,7 +246,7 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 65536, "offset": 65536, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 65536, "offset": 65536, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} @@ -298,7 +298,7 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} @@ -349,7 +349,7 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} @@ -400,7 +400,7 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 31457280, "offset": 31457280, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 31457280, "offset": 31457280, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} @@ -451,7 +451,7 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} @@ -502,7 +502,7 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2048, "offset": 2048, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2048, "offset": 2048, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} @@ -533,7 +533,7 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} @@ -557,7 +557,7 @@ Images are identical. {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} From 900e7d413d630ebd3f5d64bae0e6249621ec0c7f Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Tue, 31 Oct 2023 14:54:31 +0100 Subject: [PATCH 290/974] iotests: add test for changing mirror's copy_mode One part of the test is using a throttled source to ensure that there are no obvious issues when changing the copy_mode while there are ongoing requests (source and target images are compared at the very end). The other part of the test is using a throttled target to ensure that the change to active mode actually happened. This is done by hitting the throttling limit, issuing a synchronous write and then immediately verifying the target side. QSD is used, because otherwise, a synchronous write would hang there. Signed-off-by: Fiona Ebner Message-ID: <20231031135431.393137-11-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- .../tests/mirror-change-copy-mode | 193 ++++++++++++++++++ .../tests/mirror-change-copy-mode.out | 5 + 2 files changed, 198 insertions(+) create mode 100755 tests/qemu-iotests/tests/mirror-change-copy-mode create mode 100644 tests/qemu-iotests/tests/mirror-change-copy-mode.out diff --git a/tests/qemu-iotests/tests/mirror-change-copy-mode b/tests/qemu-iotests/tests/mirror-change-copy-mode new file mode 100755 index 0000000000..51788b85c7 --- /dev/null +++ b/tests/qemu-iotests/tests/mirror-change-copy-mode @@ -0,0 +1,193 @@ +#!/usr/bin/env python3 +# group: rw +# +# Test for changing mirror copy mode from background to active +# +# Copyright (C) 2023 Proxmox Server Solutions GmbH +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +import os +import time + +import iotests +from iotests import qemu_img, QemuStorageDaemon + +iops_target = 8 +iops_source = iops_target * 2 +image_size = 1 * 1024 * 1024 +source_img = os.path.join(iotests.test_dir, 'source.' + iotests.imgfmt) +target_img = os.path.join(iotests.test_dir, 'target.' + iotests.imgfmt) +nbd_sock = os.path.join(iotests.sock_dir, 'nbd.sock') + +class TestMirrorChangeCopyMode(iotests.QMPTestCase): + + def setUp(self): + qemu_img('create', '-f', iotests.imgfmt, source_img, str(image_size)) + qemu_img('create', '-f', iotests.imgfmt, target_img, str(image_size)) + + self.qsd = QemuStorageDaemon('--nbd-server', + f'addr.type=unix,addr.path={nbd_sock}', + qmp=True) + + self.qsd.cmd('object-add', { + 'qom-type': 'throttle-group', + 'id': 'thrgr-target', + 'limits': { + 'iops-write': iops_target, + 'iops-write-max': iops_target + } + }) + + self.qsd.cmd('blockdev-add', { + 'node-name': 'target', + 'driver': 'throttle', + 'throttle-group': 'thrgr-target', + 'file': { + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'file', + 'filename': target_img + } + } + }) + + self.qsd.cmd('block-export-add', { + 'id': 'exp0', + 'type': 'nbd', + 'node-name': 'target', + 'writable': True + }) + + self.vm = iotests.VM() + self.vm.add_args('-drive', + f'file={source_img},if=none,format={iotests.imgfmt},' + f'iops_wr={iops_source},' + f'iops_wr_max={iops_source},' + 'id=source') + self.vm.launch() + + self.vm.cmd('blockdev-add', { + 'node-name': 'target', + 'driver': 'nbd', + 'export': 'target', + 'server': { + 'type': 'unix', + 'path': nbd_sock + } + }) + + + def tearDown(self): + self.vm.shutdown() + self.qsd.stop() + self.check_qemu_io_errors() + self.check_images_identical() + os.remove(source_img) + os.remove(target_img) + + # Once the VM is shut down we can parse the log and see if qemu-io ran + # without errors. + def check_qemu_io_errors(self): + self.assertFalse(self.vm.is_running()) + log = self.vm.get_log() + for line in log.split("\n"): + assert not line.startswith("Pattern verification failed") + + def check_images_identical(self): + qemu_img('compare', '-f', iotests.imgfmt, source_img, target_img) + + def start_mirror(self): + self.vm.cmd('blockdev-mirror', + job_id='mirror', + device='source', + target='target', + filter_node_name='mirror-top', + sync='full', + copy_mode='background') + + def test_background_to_active(self): + self.vm.hmp_qemu_io('source', f'write 0 {image_size}') + self.vm.hmp_qemu_io('target', f'write 0 {image_size}') + + self.start_mirror() + + result = self.vm.cmd('query-block-jobs') + assert not result[0]['actively-synced'] + + self.vm.event_wait('BLOCK_JOB_READY') + + result = self.vm.cmd('query-block-jobs') + assert not result[0]['actively-synced'] + + # Start some background requests. + reqs = 4 * iops_source + req_size = image_size // reqs + for i in range(0, reqs): + req = f'aio_write -P 7 {req_size * i} {req_size}' + self.vm.hmp_qemu_io('source', req) + + # Wait for the first few requests. + time.sleep(1) + self.vm.qtest(f'clock_step {1 * 1000 * 1000 * 1000}') + + result = self.vm.cmd('query-block-jobs') + # There should've been new requests. + assert result[0]['len'] > image_size + # To verify later that not all requests were completed at this point. + len_before_change = result[0]['len'] + + # Change the copy mode while requests are happening. + self.vm.cmd('block-job-change', + id='mirror', + type='mirror', + copy_mode='write-blocking') + + # Wait until image is actively synced. + while True: + time.sleep(0.1) + self.vm.qtest(f'clock_step {100 * 1000 * 1000}') + result = self.vm.cmd('query-block-jobs') + if result[0]['actively-synced']: + break + + # Because of throttling, not all requests should have been completed + # above. + result = self.vm.cmd('query-block-jobs') + assert result[0]['len'] > len_before_change + + # Issue enough requests for a few seconds only touching the first half + # of the image. + reqs = 4 * iops_target + req_size = image_size // 2 // reqs + for i in range(0, reqs): + req = f'aio_write -P 19 {req_size * i} {req_size}' + self.vm.hmp_qemu_io('source', req) + + # Now issue a synchronous write in the second half of the image and + # immediately verify that it was written to the target too. This would + # fail without switching the copy mode. Note that this only produces a + # log line and the actual checking happens during tearDown(). + req_args = f'-P 37 {3 * (image_size // 4)} {req_size}' + self.vm.hmp_qemu_io('source', f'write {req_args}') + self.vm.hmp_qemu_io('target', f'read {req_args}') + + self.vm.cmd('block-job-cancel', device='mirror') + while len(self.vm.cmd('query-block-jobs')) > 0: + time.sleep(0.1) + +if __name__ == '__main__': + iotests.main(supported_fmts=['qcow2', 'raw'], + supported_protocols=['file']) diff --git a/tests/qemu-iotests/tests/mirror-change-copy-mode.out b/tests/qemu-iotests/tests/mirror-change-copy-mode.out new file mode 100644 index 0000000000..ae1213e6f8 --- /dev/null +++ b/tests/qemu-iotests/tests/mirror-change-copy-mode.out @@ -0,0 +1,5 @@ +. +---------------------------------------------------------------------- +Ran 1 tests + +OK From b8fc6195504dfeca2d283f356e7c13a6fd391acb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 24 Oct 2023 10:30:04 +0200 Subject: [PATCH 291/974] hw/m68k/irqc: Pass CPU using QOM link property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid the interrupt controller directly access the 'first_cpu' global. Pass 'cpu' from the board code. Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20231024083010.12453-2-philmd@linaro.org> Signed-off-by: Thomas Huth --- hw/intc/m68k_irqc.c | 10 +++++++++- hw/m68k/virt.c | 2 ++ include/hw/intc/m68k_irqc.h | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/hw/intc/m68k_irqc.c b/hw/intc/m68k_irqc.c index 0c515e4ecb..e09705eeaf 100644 --- a/hw/intc/m68k_irqc.c +++ b/hw/intc/m68k_irqc.c @@ -11,6 +11,7 @@ #include "cpu.h" #include "migration/vmstate.h" #include "monitor/monitor.h" +#include "hw/qdev-properties.h" #include "hw/nmi.h" #include "hw/intc/intc.h" #include "hw/intc/m68k_irqc.h" @@ -35,7 +36,7 @@ static void m68k_irqc_print_info(InterruptStatsProvider *obj, Monitor *mon) static void m68k_set_irq(void *opaque, int irq, int level) { M68KIRQCState *s = opaque; - M68kCPU *cpu = M68K_CPU(first_cpu); + M68kCPU *cpu = M68K_CPU(s->cpu); int i; if (level) { @@ -85,12 +86,19 @@ static const VMStateDescription vmstate_m68k_irqc = { } }; +static Property m68k_irqc_properties[] = { + DEFINE_PROP_LINK("m68k-cpu", M68KIRQCState, cpu, + TYPE_M68K_CPU, ArchCPU *), + DEFINE_PROP_END_OF_LIST(), +}; + static void m68k_irqc_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); NMIClass *nc = NMI_CLASS(oc); InterruptStatsProviderClass *ic = INTERRUPT_STATS_PROVIDER_CLASS(oc); + device_class_set_props(dc, m68k_irqc_properties); nc->nmi_monitor_handler = m68k_nmi; dc->reset = m68k_irqc_reset; dc->vmsd = &vmstate_m68k_irqc; diff --git a/hw/m68k/virt.c b/hw/m68k/virt.c index 2dd3c99894..e7dc188855 100644 --- a/hw/m68k/virt.c +++ b/hw/m68k/virt.c @@ -155,6 +155,8 @@ static void virt_init(MachineState *machine) /* IRQ Controller */ irqc_dev = qdev_new(TYPE_M68K_IRQC); + object_property_set_link(OBJECT(irqc_dev), "m68k-cpu", + OBJECT(cpu), &error_abort); sysbus_realize_and_unref(SYS_BUS_DEVICE(irqc_dev), &error_fatal); /* diff --git a/include/hw/intc/m68k_irqc.h b/include/hw/intc/m68k_irqc.h index ef91f21812..693e33b0aa 100644 --- a/include/hw/intc/m68k_irqc.h +++ b/include/hw/intc/m68k_irqc.h @@ -33,6 +33,7 @@ typedef struct M68KIRQCState { SysBusDevice parent_obj; uint8_t ipr; + ArchCPU *cpu; /* statistics */ uint64_t stats_irq_count[M68K_IRQC_LEVEL_NUM]; From 989c8a46cc9ec211b94c4dfa0f8fd8662ab697da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 24 Oct 2023 10:30:05 +0200 Subject: [PATCH 292/974] hw/m68k/mcf5206: Pass CPU using QOM link property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid the interrupt controller directly access the first cpu via the qemu_get_cpu() call. Pass it as argument to mcf5206_init() from the board code. Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20231024083010.12453-3-philmd@linaro.org> Signed-off-by: Thomas Huth --- hw/m68k/an5206.c | 6 ++++-- hw/m68k/mcf5206.c | 9 ++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/hw/m68k/an5206.c b/hw/m68k/an5206.c index 11ae4c9795..1e8e64f8bd 100644 --- a/hw/m68k/an5206.c +++ b/hw/m68k/an5206.c @@ -20,12 +20,14 @@ #define AN5206_MBAR_ADDR 0x10000000 #define AN5206_RAMBAR_ADDR 0x20000000 -static void mcf5206_init(MemoryRegion *sysmem, uint32_t base) +static void mcf5206_init(M68kCPU *cpu, MemoryRegion *sysmem, uint32_t base) { DeviceState *dev; SysBusDevice *s; dev = qdev_new(TYPE_MCF5206_MBAR); + object_property_set_link(OBJECT(dev), "m68k-cpu", + OBJECT(cpu), &error_abort); s = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(s, &error_fatal); @@ -60,7 +62,7 @@ static void an5206_init(MachineState *machine) memory_region_init_ram(sram, NULL, "an5206.sram", 512, &error_fatal); memory_region_add_subregion(address_space_mem, AN5206_RAMBAR_ADDR, sram); - mcf5206_init(address_space_mem, AN5206_MBAR_ADDR); + mcf5206_init(cpu, address_space_mem, AN5206_MBAR_ADDR); /* Load kernel. */ if (!kernel_filename) { diff --git a/hw/m68k/mcf5206.c b/hw/m68k/mcf5206.c index 2ab1b4f059..f920ca2ceb 100644 --- a/hw/m68k/mcf5206.c +++ b/hw/m68k/mcf5206.c @@ -10,6 +10,7 @@ #include "qemu/error-report.h" #include "qemu/log.h" #include "cpu.h" +#include "hw/qdev-properties.h" #include "hw/boards.h" #include "hw/irq.h" #include "hw/m68k/mcf.h" @@ -601,13 +602,19 @@ static void mcf5206_mbar_realize(DeviceState *dev, Error **errp) s->timer[1] = m5206_timer_init(s->pic[10]); s->uart[0] = mcf_uart_init(s->pic[12], serial_hd(0)); s->uart[1] = mcf_uart_init(s->pic[13], serial_hd(1)); - s->cpu = M68K_CPU(qemu_get_cpu(0)); } +static Property mcf5206_mbar_properties[] = { + DEFINE_PROP_LINK("m68k-cpu", m5206_mbar_state, cpu, + TYPE_M68K_CPU, M68kCPU *), + DEFINE_PROP_END_OF_LIST(), +}; + static void mcf5206_mbar_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); + device_class_set_props(dc, mcf5206_mbar_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); dc->desc = "MCF5206 system integration module"; dc->realize = mcf5206_mbar_realize; From 4768968960f74db92d9fac360c17838cda39614b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 24 Oct 2023 10:30:06 +0200 Subject: [PATCH 293/974] hw/m68k/mcf_intc: Expose MMIO region via SysBus API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QOM objects shouldn't access each other internals fields except using the QOM API. Here the caller of mcf_intc_init() access the MMIO region from the MCF_INTC state. Avoid that by exposing that region via sysbus_init_mmio(), then get it with sysbus_mmio_get_region(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Tested-by: Thomas Huth Reviewed-by: Thomas Huth Message-ID: <20231024083010.12453-4-philmd@linaro.org> Signed-off-by: Thomas Huth --- hw/m68k/mcf_intc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/m68k/mcf_intc.c b/hw/m68k/mcf_intc.c index 4cd30188c0..1f74ea0e14 100644 --- a/hw/m68k/mcf_intc.c +++ b/hw/m68k/mcf_intc.c @@ -173,6 +173,7 @@ static void mcf_intc_instance_init(Object *obj) mcf_intc_state *s = MCF_INTC(obj); memory_region_init_io(&s->iomem, obj, &mcf_intc_ops, s, "mcf", 0x100); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); } static void mcf_intc_class_init(ObjectClass *oc, void *data) @@ -211,7 +212,8 @@ qemu_irq *mcf_intc_init(MemoryRegion *sysmem, s = MCF_INTC(dev); s->cpu = cpu; - memory_region_add_subregion(sysmem, base, &s->iomem); + memory_region_add_subregion(sysmem, base, + sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0)); return qemu_allocate_irqs(mcf_intc_set_irq, s, 64); } From 4c4ee593d636dc462b6fe15e00c31d6ff6c72bd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 24 Oct 2023 10:30:07 +0200 Subject: [PATCH 294/974] hw/m68k/mcf_intc: Pass CPU using QOM link property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QOM objects shouldn't access each other internals fields except using the QOM API. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Tested-by: Thomas Huth Reviewed-by: Thomas Huth Message-ID: <20231024083010.12453-5-philmd@linaro.org> Signed-off-by: Thomas Huth --- hw/m68k/mcf_intc.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/hw/m68k/mcf_intc.c b/hw/m68k/mcf_intc.c index 1f74ea0e14..1d3b34e18c 100644 --- a/hw/m68k/mcf_intc.c +++ b/hw/m68k/mcf_intc.c @@ -14,6 +14,7 @@ #include "hw/irq.h" #include "hw/sysbus.h" #include "hw/m68k/mcf.h" +#include "hw/qdev-properties.h" #include "qom/object.h" #define TYPE_MCF_INTC "mcf-intc" @@ -176,10 +177,17 @@ static void mcf_intc_instance_init(Object *obj) sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); } +static Property mcf_intc_properties[] = { + DEFINE_PROP_LINK("m68k-cpu", mcf_intc_state, cpu, + TYPE_M68K_CPU, M68kCPU *), + DEFINE_PROP_END_OF_LIST(), +}; + static void mcf_intc_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); + device_class_set_props(dc, mcf_intc_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); dc->reset = mcf_intc_reset; } @@ -204,16 +212,13 @@ qemu_irq *mcf_intc_init(MemoryRegion *sysmem, M68kCPU *cpu) { DeviceState *dev; - mcf_intc_state *s; dev = qdev_new(TYPE_MCF_INTC); + object_property_set_link(OBJECT(dev), "m68k-cpu", + OBJECT(cpu), &error_abort); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - - s = MCF_INTC(dev); - s->cpu = cpu; - memory_region_add_subregion(sysmem, base, sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0)); - return qemu_allocate_irqs(mcf_intc_set_irq, s, 64); + return qemu_allocate_irqs(mcf_intc_set_irq, dev, 64); } From b2897f7ed446aedf2e5ed33762ac823db13c2f56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 24 Oct 2023 10:30:08 +0200 Subject: [PATCH 295/974] hw/m68k/next-cube: Do not open-code sysbus_create_simple() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mechanical change using the following coccinelle script: @@ identifier dev; identifier sbd; expression qom_type; expression addr; @@ - dev = qdev_new(qom_type); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); + dev = sysbus_create_simple(qom_type, addr, NULL); then manually removing the 'dev' variable to avoid: error: variable 'dev' set but not used [-Werror,-Wunused-but-set-variable] Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Message-ID: <20231024083010.12453-6-philmd@linaro.org> Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 5d244b3b95..d17e6be8e1 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -950,7 +950,6 @@ static void next_cube_init(MachineState *machine) MemoryRegion *bmapm2 = g_new(MemoryRegion, 1); MemoryRegion *sysmem = get_system_memory(); const char *bios_name = machine->firmware ?: ROM_FILE; - DeviceState *dev; DeviceState *pcdev; /* Initialize the cpu core */ @@ -974,9 +973,7 @@ static void next_cube_init(MachineState *machine) memory_region_add_subregion(sysmem, 0x04000000, machine->ram); /* Framebuffer */ - dev = qdev_new(TYPE_NEXTFB); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x0B000000); + sysbus_create_simple(TYPE_NEXTFB, 0x0B000000, NULL); /* MMIO */ sysbus_mmio_map(SYS_BUS_DEVICE(pcdev), 0, 0x02000000); @@ -993,9 +990,7 @@ static void next_cube_init(MachineState *machine) memory_region_add_subregion(sysmem, 0x820c0000, bmapm2); /* KBD */ - dev = qdev_new(TYPE_NEXTKBD); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x0200e000); + sysbus_create_simple(TYPE_NEXTKBD, 0x0200e000, NULL); /* Load ROM here */ /* still not sure if the rom should also be mapped at 0x0*/ From 7f090ed7103df119fa33651ae15958ddb5863f56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 24 Oct 2023 10:30:09 +0200 Subject: [PATCH 296/974] hw/m68k/virt: Do not open-code sysbus_create_simple() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mechanical change using the following coccinelle script: @@ identifier dev; expression qom_type; expression addr; expression irq; @@ - dev = qdev_new(qom_type); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); + dev = sysbus_create_simple(qom_type, addr, irq); Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Message-ID: <20231024083010.12453-7-philmd@linaro.org> Signed-off-by: Thomas Huth --- hw/m68k/virt.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/hw/m68k/virt.c b/hw/m68k/virt.c index e7dc188855..2e49e262ee 100644 --- a/hw/m68k/virt.c +++ b/hw/m68k/virt.c @@ -201,11 +201,8 @@ static void virt_init(MachineState *machine) sysbus_connect_irq(sysbus, 0, PIC_GPIO(VIRT_GF_TTY_IRQ_BASE)); /* virt controller */ - dev = qdev_new(TYPE_VIRT_CTRL); - sysbus = SYS_BUS_DEVICE(dev); - sysbus_realize_and_unref(sysbus, &error_fatal); - sysbus_mmio_map(sysbus, 0, VIRT_CTRL_MMIO_BASE); - sysbus_connect_irq(sysbus, 0, PIC_GPIO(VIRT_CTRL_IRQ_BASE)); + dev = sysbus_create_simple(TYPE_VIRT_CTRL, VIRT_CTRL_MMIO_BASE, + PIC_GPIO(VIRT_CTRL_IRQ_BASE)); /* virtio-mmio */ io_base = VIRT_VIRTIO_MMIO_BASE; From f213ccc96831191ac492bd36b9935effa01f95fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 19 Oct 2023 12:49:29 +0200 Subject: [PATCH 297/974] hw/char/mcf_uart: Have mcf_uart_create() return DeviceState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no point in having mcf_uart_init() demote the DeviceState pointer and return a void one. Directly return the real typedef. mcf_uart_init() do both init + realize: rename as mcf_uart_create(). Similarly, mcf_uart_mm_init() do init / realize / mmap: rename as mcf_uart_create_mmap(). Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20231019104929.16517-1-philmd@linaro.org> Signed-off-by: Thomas Huth --- hw/char/mcf_uart.c | 13 +++++++------ hw/m68k/mcf5206.c | 6 +++--- hw/m68k/mcf5208.c | 6 +++--- include/hw/m68k/mcf.h | 4 ++-- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/hw/char/mcf_uart.c b/hw/char/mcf_uart.c index 6fa4ac502c..f9cbc9bdc4 100644 --- a/hw/char/mcf_uart.c +++ b/hw/char/mcf_uart.c @@ -342,25 +342,26 @@ static void mcf_uart_register(void) type_init(mcf_uart_register) -void *mcf_uart_init(qemu_irq irq, Chardev *chrdrv) +DeviceState *mcf_uart_create(qemu_irq irq, Chardev *chrdrv) { - DeviceState *dev; + DeviceState *dev; dev = qdev_new(TYPE_MCF_UART); if (chrdrv) { qdev_prop_set_chr(dev, "chardev", chrdrv); } sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); return dev; } -void mcf_uart_mm_init(hwaddr base, qemu_irq irq, Chardev *chrdrv) +DeviceState *mcf_uart_create_mmap(hwaddr base, qemu_irq irq, Chardev *chrdrv) { - DeviceState *dev; + DeviceState *dev; - dev = mcf_uart_init(irq, chrdrv); + dev = mcf_uart_create(irq, chrdrv); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + + return dev; } diff --git a/hw/m68k/mcf5206.c b/hw/m68k/mcf5206.c index f920ca2ceb..a46a23538d 100644 --- a/hw/m68k/mcf5206.c +++ b/hw/m68k/mcf5206.c @@ -168,7 +168,7 @@ typedef struct { MemoryRegion iomem; qemu_irq *pic; m5206_timer_state *timer[2]; - void *uart[2]; + DeviceState *uart[2]; uint8_t scr; uint8_t icr[14]; uint16_t imr; /* 1 == interrupt is masked. */ @@ -600,8 +600,8 @@ static void mcf5206_mbar_realize(DeviceState *dev, Error **errp) s->pic = qemu_allocate_irqs(m5206_mbar_set_irq, s, 14); s->timer[0] = m5206_timer_init(s->pic[9]); s->timer[1] = m5206_timer_init(s->pic[10]); - s->uart[0] = mcf_uart_init(s->pic[12], serial_hd(0)); - s->uart[1] = mcf_uart_init(s->pic[13], serial_hd(1)); + s->uart[0] = mcf_uart_create(s->pic[12], serial_hd(0)); + s->uart[1] = mcf_uart_create(s->pic[13], serial_hd(1)); } static Property mcf5206_mbar_properties[] = { diff --git a/hw/m68k/mcf5208.c b/hw/m68k/mcf5208.c index be1033f84f..d22d8536db 100644 --- a/hw/m68k/mcf5208.c +++ b/hw/m68k/mcf5208.c @@ -261,9 +261,9 @@ static void mcf5208evb_init(MachineState *machine) /* Internal peripherals. */ pic = mcf_intc_init(address_space_mem, 0xfc048000, cpu); - mcf_uart_mm_init(0xfc060000, pic[26], serial_hd(0)); - mcf_uart_mm_init(0xfc064000, pic[27], serial_hd(1)); - mcf_uart_mm_init(0xfc068000, pic[28], serial_hd(2)); + mcf_uart_create_mmap(0xfc060000, pic[26], serial_hd(0)); + mcf_uart_create_mmap(0xfc064000, pic[27], serial_hd(1)); + mcf_uart_create_mmap(0xfc068000, pic[28], serial_hd(2)); mcf5208_sys_init(address_space_mem, pic); diff --git a/include/hw/m68k/mcf.h b/include/hw/m68k/mcf.h index 8cbd587bbf..5d9f876ffe 100644 --- a/include/hw/m68k/mcf.h +++ b/include/hw/m68k/mcf.h @@ -10,8 +10,8 @@ uint64_t mcf_uart_read(void *opaque, hwaddr addr, unsigned size); void mcf_uart_write(void *opaque, hwaddr addr, uint64_t val, unsigned size); -void *mcf_uart_init(qemu_irq irq, Chardev *chr); -void mcf_uart_mm_init(hwaddr base, qemu_irq irq, Chardev *chr); +DeviceState *mcf_uart_create(qemu_irq irq, Chardev *chr); +DeviceState *mcf_uart_create_mmap(hwaddr base, qemu_irq irq, Chardev *chr); /* mcf_intc.c */ qemu_irq *mcf_intc_init(struct MemoryRegion *sysmem, From 6436db5a609c16c57e50e525e45e2b88cf925fff Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 20 Oct 2023 16:55:54 +0200 Subject: [PATCH 298/974] hw/ipmi: Don't call vmstate_register() from instance_init() functions instance_init() can be called multiple times, e.g. during introspection of the device. We should not install the vmstate handlers here. Do it in the realize() function instead. Signed-off-by: Thomas Huth Reviewed-by: Juan Quintela Acked-by: Corey Minyard Signed-off-by: Juan Quintela Message-ID: <20231020145554.662751-1-thuth@redhat.com> --- hw/ipmi/ipmi_bmc_extern.c | 29 ++++++++++++----------- hw/ipmi/isa_ipmi_bt.c | 34 +++++++++++++------------- hw/ipmi/isa_ipmi_kcs.c | 50 +++++++++++++++++++-------------------- 3 files changed, 57 insertions(+), 56 deletions(-) diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c index e232d35ba2..2117dad35a 100644 --- a/hw/ipmi/ipmi_bmc_extern.c +++ b/hw/ipmi/ipmi_bmc_extern.c @@ -453,19 +453,6 @@ static void ipmi_bmc_extern_handle_reset(IPMIBmc *b) continue_send(ibe); } -static void ipmi_bmc_extern_realize(DeviceState *dev, Error **errp) -{ - IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(dev); - - if (!qemu_chr_fe_backend_connected(&ibe->chr)) { - error_setg(errp, "IPMI external bmc requires chardev attribute"); - return; - } - - qemu_chr_fe_set_handlers(&ibe->chr, can_receive, receive, - chr_event, NULL, ibe, NULL, true); -} - static int ipmi_bmc_extern_post_migrate(void *opaque, int version_id) { IPMIBmcExtern *ibe = opaque; @@ -499,12 +486,26 @@ static const VMStateDescription vmstate_ipmi_bmc_extern = { } }; +static void ipmi_bmc_extern_realize(DeviceState *dev, Error **errp) +{ + IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(dev); + + if (!qemu_chr_fe_backend_connected(&ibe->chr)) { + error_setg(errp, "IPMI external bmc requires chardev attribute"); + return; + } + + qemu_chr_fe_set_handlers(&ibe->chr, can_receive, receive, + chr_event, NULL, ibe, NULL, true); + + vmstate_register(NULL, 0, &vmstate_ipmi_bmc_extern, ibe); +} + static void ipmi_bmc_extern_init(Object *obj) { IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(obj); ibe->extern_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, extern_timeout, ibe); - vmstate_register(NULL, 0, &vmstate_ipmi_bmc_extern, ibe); } static void ipmi_bmc_extern_finalize(Object *obj) diff --git a/hw/ipmi/isa_ipmi_bt.c b/hw/ipmi/isa_ipmi_bt.c index a83e7243d6..aec064d3cd 100644 --- a/hw/ipmi/isa_ipmi_bt.c +++ b/hw/ipmi/isa_ipmi_bt.c @@ -68,6 +68,21 @@ static void isa_ipmi_bt_lower_irq(IPMIBT *ib) qemu_irq_lower(iib->irq); } +static const VMStateDescription vmstate_ISAIPMIBTDevice = { + .name = TYPE_IPMI_INTERFACE_PREFIX "isa-bt", + .version_id = 2, + .minimum_version_id = 2, + /* + * Version 1 had messed up the array transfer, it's not even usable + * because it used VMSTATE_VBUFFER_UINT32, but it did not transfer + * the buffer length, so random things would happen. + */ + .fields = (VMStateField[]) { + VMSTATE_STRUCT(bt, ISAIPMIBTDevice, 1, vmstate_IPMIBT, IPMIBT), + VMSTATE_END_OF_LIST() + } +}; + static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp) { Error *err = NULL; @@ -102,30 +117,15 @@ static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp) qdev_set_legacy_instance_id(dev, iib->bt.io_base, iib->bt.io_length); isa_register_ioport(isadev, &iib->bt.io, iib->bt.io_base); -} -static const VMStateDescription vmstate_ISAIPMIBTDevice = { - .name = TYPE_IPMI_INTERFACE_PREFIX "isa-bt", - .version_id = 2, - .minimum_version_id = 2, - /* - * Version 1 had messed up the array transfer, it's not even usable - * because it used VMSTATE_VBUFFER_UINT32, but it did not transfer - * the buffer length, so random things would happen. - */ - .fields = (VMStateField[]) { - VMSTATE_STRUCT(bt, ISAIPMIBTDevice, 1, vmstate_IPMIBT, IPMIBT), - VMSTATE_END_OF_LIST() - } -}; + vmstate_register(NULL, 0, &vmstate_ISAIPMIBTDevice, dev); +} static void isa_ipmi_bt_init(Object *obj) { ISAIPMIBTDevice *iib = ISA_IPMI_BT(obj); ipmi_bmc_find_and_link(obj, (Object **) &iib->bt.bmc); - - vmstate_register(NULL, 0, &vmstate_ISAIPMIBTDevice, iib); } static void *isa_ipmi_bt_get_backend_data(IPMIInterface *ii) diff --git a/hw/ipmi/isa_ipmi_kcs.c b/hw/ipmi/isa_ipmi_kcs.c index b2ed70b9da..b5dcb64616 100644 --- a/hw/ipmi/isa_ipmi_kcs.c +++ b/hw/ipmi/isa_ipmi_kcs.c @@ -67,6 +67,24 @@ static void isa_ipmi_kcs_lower_irq(IPMIKCS *ik) qemu_irq_lower(iik->irq); } +static bool vmstate_kcs_before_version2(void *opaque, int version) +{ + return version <= 1; +} + +static const VMStateDescription vmstate_ISAIPMIKCSDevice = { + .name = TYPE_IPMI_INTERFACE, + .version_id = 2, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_VSTRUCT_TEST(kcs, ISAIPMIKCSDevice, vmstate_kcs_before_version2, + 0, vmstate_IPMIKCS, IPMIKCS, 1), + VMSTATE_VSTRUCT_V(kcs, ISAIPMIKCSDevice, 2, vmstate_IPMIKCS, + IPMIKCS, 2), + VMSTATE_END_OF_LIST() + } +}; + static void ipmi_isa_realize(DeviceState *dev, Error **errp) { Error *err = NULL; @@ -101,31 +119,6 @@ static void ipmi_isa_realize(DeviceState *dev, Error **errp) qdev_set_legacy_instance_id(dev, iik->kcs.io_base, iik->kcs.io_length); isa_register_ioport(isadev, &iik->kcs.io, iik->kcs.io_base); -} - -static bool vmstate_kcs_before_version2(void *opaque, int version) -{ - return version <= 1; -} - -static const VMStateDescription vmstate_ISAIPMIKCSDevice = { - .name = TYPE_IPMI_INTERFACE, - .version_id = 2, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_VSTRUCT_TEST(kcs, ISAIPMIKCSDevice, vmstate_kcs_before_version2, - 0, vmstate_IPMIKCS, IPMIKCS, 1), - VMSTATE_VSTRUCT_V(kcs, ISAIPMIKCSDevice, 2, vmstate_IPMIKCS, - IPMIKCS, 2), - VMSTATE_END_OF_LIST() - } -}; - -static void isa_ipmi_kcs_init(Object *obj) -{ - ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(obj); - - ipmi_bmc_find_and_link(obj, (Object **) &iik->kcs.bmc); /* * Version 1 had an incorrect name, it clashed with the BT @@ -135,6 +128,13 @@ static void isa_ipmi_kcs_init(Object *obj) vmstate_register(NULL, 0, &vmstate_ISAIPMIKCSDevice, iik); } +static void isa_ipmi_kcs_init(Object *obj) +{ + ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(obj); + + ipmi_bmc_find_and_link(obj, (Object **) &iik->kcs.bmc); +} + static void *isa_ipmi_kcs_get_backend_data(IPMIInterface *ii) { ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(ii); From 2fb40d1b949a98c1742a9277e716da9bc502ffe4 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 20 Oct 2023 17:05:52 +0200 Subject: [PATCH 299/974] hw/s390x/s390-skeys: Don't call register_savevm_live() during instance_init() Since the instance_init() function immediately tries to set the property to "true", the s390_skeys_set_migration_enabled() tries to register a savevm handler during instance_init(). However, instance_init() functions can be called multiple times, e.g. for introspection of devices. That means multiple instances of devices can be created during runtime (which is fine as long as they all don't get realized, too), so the "Prevent double registration of savevm handler" check in the s390_skeys_set_migration_enabled() function does not work at all as expected (since there could be more than one instance). Thus we must not call register_savevm_live() from an instance_init() function at all. Move this to the realize() function instead. This way we can also get rid of the property getter and setter functions completely, simplifying the code along the way quite a bit. Acked-by: David Hildenbrand Reviewed-by: Eric Farman Acked-by: Juan Quintela Signed-off-by: Thomas Huth Signed-off-by: Juan Quintela Message-ID: <20231020150554.664422-2-thuth@redhat.com> --- hw/s390x/s390-skeys.c | 36 +++++++++--------------------------- 1 file changed, 9 insertions(+), 27 deletions(-) diff --git a/hw/s390x/s390-skeys.c b/hw/s390x/s390-skeys.c index 5024faf411..8f5159d85d 100644 --- a/hw/s390x/s390-skeys.c +++ b/hw/s390x/s390-skeys.c @@ -12,6 +12,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "hw/boards.h" +#include "hw/qdev-properties.h" #include "hw/s390x/storage-keys.h" #include "qapi/error.h" #include "qapi/qapi-commands-misc-target.h" @@ -432,58 +433,39 @@ static int s390_storage_keys_load(QEMUFile *f, void *opaque, int version_id) return ret; } -static inline bool s390_skeys_get_migration_enabled(Object *obj, Error **errp) -{ - S390SKeysState *ss = S390_SKEYS(obj); - - return ss->migration_enabled; -} - static SaveVMHandlers savevm_s390_storage_keys = { .save_state = s390_storage_keys_save, .load_state = s390_storage_keys_load, }; -static inline void s390_skeys_set_migration_enabled(Object *obj, bool value, - Error **errp) +static void s390_skeys_realize(DeviceState *dev, Error **errp) { - S390SKeysState *ss = S390_SKEYS(obj); - - /* Prevent double registration of savevm handler */ - if (ss->migration_enabled == value) { - return; - } - - ss->migration_enabled = value; + S390SKeysState *ss = S390_SKEYS(dev); if (ss->migration_enabled) { register_savevm_live(TYPE_S390_SKEYS, 0, 1, &savevm_s390_storage_keys, ss); - } else { - unregister_savevm(VMSTATE_IF(ss), TYPE_S390_SKEYS, ss); } } -static void s390_skeys_instance_init(Object *obj) -{ - object_property_add_bool(obj, "migration-enabled", - s390_skeys_get_migration_enabled, - s390_skeys_set_migration_enabled); - object_property_set_bool(obj, "migration-enabled", true, NULL); -} +static Property s390_skeys_props[] = { + DEFINE_PROP_BOOL("migration-enabled", S390SKeysState, migration_enabled, true), + DEFINE_PROP_END_OF_LIST(), +}; static void s390_skeys_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); dc->hotpluggable = false; + dc->realize = s390_skeys_realize; + device_class_set_props(dc, s390_skeys_props); set_bit(DEVICE_CATEGORY_MISC, dc->categories); } static const TypeInfo s390_skeys_info = { .name = TYPE_S390_SKEYS, .parent = TYPE_DEVICE, - .instance_init = s390_skeys_instance_init, .instance_size = sizeof(S390SKeysState), .class_init = s390_skeys_class_init, .class_size = sizeof(S390SKeysClass), From bc1bf2ae2f76d451126fb6a15f00d8a2cb9c4e76 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 20 Oct 2023 17:05:53 +0200 Subject: [PATCH 300/974] hw/s390x/s390-stattrib: Simplify handling of the "migration-enabled" property There's no need for dedicated handlers here if they don't do anything special. Acked-by: David Hildenbrand Reviewed-by: Eric Farman Acked-by: Juan Quintela Signed-off-by: Thomas Huth Signed-off-by: Juan Quintela Message-ID: <20231020150554.664422-3-thuth@redhat.com> --- hw/s390x/s390-stattrib.c | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/hw/s390x/s390-stattrib.c b/hw/s390x/s390-stattrib.c index 220e845d12..4f1ab57437 100644 --- a/hw/s390x/s390-stattrib.c +++ b/hw/s390x/s390-stattrib.c @@ -13,6 +13,7 @@ #include "qemu/units.h" #include "migration/qemu-file.h" #include "migration/register.h" +#include "hw/qdev-properties.h" #include "hw/s390x/storage-attributes.h" #include "qemu/error-report.h" #include "exec/ram_addr.h" @@ -340,6 +341,11 @@ static void s390_stattrib_realize(DeviceState *dev, Error **errp) } } +static Property s390_stattrib_props[] = { + DEFINE_PROP_BOOL("migration-enabled", S390StAttribState, migration_enabled, true), + DEFINE_PROP_END_OF_LIST(), +}; + static void s390_stattrib_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -347,22 +353,7 @@ static void s390_stattrib_class_init(ObjectClass *oc, void *data) dc->hotpluggable = false; set_bit(DEVICE_CATEGORY_MISC, dc->categories); dc->realize = s390_stattrib_realize; -} - -static inline bool s390_stattrib_get_migration_enabled(Object *obj, - Error **errp) -{ - S390StAttribState *s = S390_STATTRIB(obj); - - return s->migration_enabled; -} - -static inline void s390_stattrib_set_migration_enabled(Object *obj, bool value, - Error **errp) -{ - S390StAttribState *s = S390_STATTRIB(obj); - - s->migration_enabled = value; + device_class_set_props(dc, s390_stattrib_props); } static SaveVMHandlers savevm_s390_stattrib_handlers = { @@ -383,10 +374,6 @@ static void s390_stattrib_instance_init(Object *obj) register_savevm_live(TYPE_S390_STATTRIB, 0, 0, &savevm_s390_stattrib_handlers, sas); - object_property_add_bool(obj, "migration-enabled", - s390_stattrib_get_migration_enabled, - s390_stattrib_set_migration_enabled); - object_property_set_bool(obj, "migration-enabled", true, NULL); sas->migration_cur_gfn = 0; } From 832685707fbe52b2525974a100a240338998e544 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 20 Oct 2023 17:05:54 +0200 Subject: [PATCH 301/974] hw/s390x/s390-stattrib: Don't call register_savevm_live() during instance_init() We must not call register_savevm_live() from an instance_init() function (since this could be called multiple times during device introspection). Move this to the realize() function instead. Acked-by: David Hildenbrand Reviewed-by: Eric Farman Signed-off-by: Juan Quintela Signed-off-by: Thomas Huth Signed-off-by: Juan Quintela Message-ID: <20231020150554.664422-4-thuth@redhat.com> --- hw/s390x/s390-stattrib.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/hw/s390x/s390-stattrib.c b/hw/s390x/s390-stattrib.c index 4f1ab57437..c483b62a9b 100644 --- a/hw/s390x/s390-stattrib.c +++ b/hw/s390x/s390-stattrib.c @@ -331,6 +331,17 @@ static const TypeInfo qemu_s390_stattrib_info = { /* Generic abstract object: */ +static SaveVMHandlers savevm_s390_stattrib_handlers = { + .save_setup = cmma_save_setup, + .save_live_iterate = cmma_save_iterate, + .save_live_complete_precopy = cmma_save_complete, + .state_pending_exact = cmma_state_pending, + .state_pending_estimate = cmma_state_pending, + .save_cleanup = cmma_save_cleanup, + .load_state = cmma_load, + .is_active = cmma_active, +}; + static void s390_stattrib_realize(DeviceState *dev, Error **errp) { bool ambiguous = false; @@ -338,7 +349,11 @@ static void s390_stattrib_realize(DeviceState *dev, Error **errp) object_resolve_path_type("", TYPE_S390_STATTRIB, &ambiguous); if (ambiguous) { error_setg(errp, "storage_attributes device already exists"); + return; } + + register_savevm_live(TYPE_S390_STATTRIB, 0, 0, + &savevm_s390_stattrib_handlers, dev); } static Property s390_stattrib_props[] = { @@ -356,24 +371,10 @@ static void s390_stattrib_class_init(ObjectClass *oc, void *data) device_class_set_props(dc, s390_stattrib_props); } -static SaveVMHandlers savevm_s390_stattrib_handlers = { - .save_setup = cmma_save_setup, - .save_live_iterate = cmma_save_iterate, - .save_live_complete_precopy = cmma_save_complete, - .state_pending_exact = cmma_state_pending, - .state_pending_estimate = cmma_state_pending, - .save_cleanup = cmma_save_cleanup, - .load_state = cmma_load, - .is_active = cmma_active, -}; - static void s390_stattrib_instance_init(Object *obj) { S390StAttribState *sas = S390_STATTRIB(obj); - register_savevm_live(TYPE_S390_STATTRIB, 0, 0, - &savevm_s390_stattrib_handlers, sas); - sas->migration_cur_gfn = 0; } From 71daf640d91d49a784ead059c9021070a2107008 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Fri, 20 Oct 2023 11:07:19 +0200 Subject: [PATCH 302/974] migration: Create vmstate_register_any() We have lots of cases where we are using an instance_id==0 when we should be using VMSTATE_INSTANCE_ID_ANY (-1). Basically everything that can have more than one needs to have a proper instance_id or -1 and the system will take one for it. vmstate_register_any(): We register with -1. Reviewed-by: Stefan Berger Signed-off-by: Juan Quintela Message-ID: <20231020090731.28701-2-quintela@redhat.com> --- include/migration/vmstate.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index 1af181877c..1ea97ccf2d 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -1230,6 +1230,23 @@ static inline int vmstate_register(VMStateIf *obj, int instance_id, opaque, -1, 0, NULL); } +/** + * vmstate_register_any() - legacy function to register state + * serialisation description and let the function choose the id + * + * New code shouldn't be using this function as QOM-ified devices have + * dc->vmsd to store the serialisation description. + * + * Returns: 0 on success, -1 on failure + */ +static inline int vmstate_register_any(VMStateIf *obj, + const VMStateDescription *vmsd, + void *opaque) +{ + return vmstate_register_with_alias_id(obj, VMSTATE_INSTANCE_ID_ANY, vmsd, + opaque, -1, 0, NULL); +} + void vmstate_unregister(VMStateIf *obj, const VMStateDescription *vmsd, void *opaque); From 99b16e8ee4789432cc543fde45902796385713c0 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Fri, 20 Oct 2023 11:07:20 +0200 Subject: [PATCH 303/974] migration: Use vmstate_register_any() This are the easiest cases, where we were already using VMSTATE_INSTANCE_ID_ANY. Reviewed-by: Stefan Berger Signed-off-by: Juan Quintela Message-ID: <20231020090731.28701-3-quintela@redhat.com> --- backends/dbus-vmstate.c | 3 +-- backends/tpm/tpm_emulator.c | 3 +-- hw/i2c/core.c | 2 +- hw/input/adb.c | 2 +- hw/input/ads7846.c | 2 +- hw/input/stellaris_input.c | 3 +-- hw/net/eepro100.c | 3 +-- hw/pci/pci.c | 2 +- hw/ppc/spapr_nvdimm.c | 3 +-- hw/timer/arm_timer.c | 2 +- hw/virtio/virtio-mem.c | 4 ++-- 11 files changed, 12 insertions(+), 17 deletions(-) diff --git a/backends/dbus-vmstate.c b/backends/dbus-vmstate.c index 57369ec0f2..a9d8cb0acd 100644 --- a/backends/dbus-vmstate.c +++ b/backends/dbus-vmstate.c @@ -426,8 +426,7 @@ dbus_vmstate_complete(UserCreatable *uc, Error **errp) return; } - if (vmstate_register(VMSTATE_IF(self), VMSTATE_INSTANCE_ID_ANY, - &dbus_vmstate, self) < 0) { + if (vmstate_register_any(VMSTATE_IF(self), &dbus_vmstate, self) < 0) { error_setg(errp, "Failed to register vmstate"); } } diff --git a/backends/tpm/tpm_emulator.c b/backends/tpm/tpm_emulator.c index bf1a90f5d7..f7f1b4ad7a 100644 --- a/backends/tpm/tpm_emulator.c +++ b/backends/tpm/tpm_emulator.c @@ -975,8 +975,7 @@ static void tpm_emulator_inst_init(Object *obj) qemu_add_vm_change_state_handler(tpm_emulator_vm_state_change, tpm_emu); - vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, - &vmstate_tpm_emulator, obj); + vmstate_register_any(NULL, &vmstate_tpm_emulator, obj); } /* diff --git a/hw/i2c/core.c b/hw/i2c/core.c index bed594fe59..879a1d45cb 100644 --- a/hw/i2c/core.c +++ b/hw/i2c/core.c @@ -64,7 +64,7 @@ I2CBus *i2c_init_bus(DeviceState *parent, const char *name) bus = I2C_BUS(qbus_new(TYPE_I2C_BUS, parent, name)); QLIST_INIT(&bus->current_devs); QSIMPLEQ_INIT(&bus->pending_masters); - vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, &vmstate_i2c_bus, bus); + vmstate_register_any(NULL, &vmstate_i2c_bus, bus); return bus; } diff --git a/hw/input/adb.c b/hw/input/adb.c index 214ae6f42b..8aed0da2cd 100644 --- a/hw/input/adb.c +++ b/hw/input/adb.c @@ -247,7 +247,7 @@ static void adb_bus_realize(BusState *qbus, Error **errp) adb_bus->autopoll_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, adb_autopoll, adb_bus); - vmstate_register(NULL, -1, &vmstate_adb_bus, adb_bus); + vmstate_register_any(NULL, &vmstate_adb_bus, adb_bus); } static void adb_bus_unrealize(BusState *qbus) diff --git a/hw/input/ads7846.c b/hw/input/ads7846.c index dc0998ac79..91116c6bdb 100644 --- a/hw/input/ads7846.c +++ b/hw/input/ads7846.c @@ -158,7 +158,7 @@ static void ads7846_realize(SSIPeripheral *d, Error **errp) ads7846_int_update(s); - vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, &vmstate_ads7846, s); + vmstate_register_any(NULL, &vmstate_ads7846, s); } static void ads7846_class_init(ObjectClass *klass, void *data) diff --git a/hw/input/stellaris_input.c b/hw/input/stellaris_input.c index e6ee5e11f1..a58721c8cd 100644 --- a/hw/input/stellaris_input.c +++ b/hw/input/stellaris_input.c @@ -88,6 +88,5 @@ void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode) } s->num_buttons = n; qemu_add_kbd_event_handler(stellaris_gamepad_put_key, s); - vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, - &vmstate_stellaris_gamepad, s); + vmstate_register_any(NULL, &vmstate_stellaris_gamepad, s); } diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c index dc07984ae9..94ce9e18ff 100644 --- a/hw/net/eepro100.c +++ b/hw/net/eepro100.c @@ -1883,8 +1883,7 @@ static void e100_nic_realize(PCIDevice *pci_dev, Error **errp) s->vmstate = g_memdup(&vmstate_eepro100, sizeof(vmstate_eepro100)); s->vmstate->name = qemu_get_queue(s->nic)->model; - vmstate_register(VMSTATE_IF(&pci_dev->qdev), VMSTATE_INSTANCE_ID_ANY, - s->vmstate, s); + vmstate_register_any(VMSTATE_IF(&pci_dev->qdev), s->vmstate, s); } static void eepro100_instance_init(Object *obj) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 7d09e1a39d..885c04b6f5 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -147,7 +147,7 @@ static void pci_bus_realize(BusState *qbus, Error **errp) bus->machine_done.notify = pcibus_machine_done; qemu_add_machine_init_done_notifier(&bus->machine_done); - vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, &vmstate_pcibus, bus); + vmstate_register_any(NULL, &vmstate_pcibus, bus); } static void pcie_bus_realize(BusState *qbus, Error **errp) diff --git a/hw/ppc/spapr_nvdimm.c b/hw/ppc/spapr_nvdimm.c index b2f009c816..ad7afe7544 100644 --- a/hw/ppc/spapr_nvdimm.c +++ b/hw/ppc/spapr_nvdimm.c @@ -876,8 +876,7 @@ static void spapr_nvdimm_realize(NVDIMMDevice *dimm, Error **errp) s_nvdimm->hcall_flush_required = true; } - vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, - &vmstate_spapr_nvdimm_states, dimm); + vmstate_register_any(NULL, &vmstate_spapr_nvdimm_states, dimm); } static void spapr_nvdimm_unrealize(NVDIMMDevice *dimm) diff --git a/hw/timer/arm_timer.c b/hw/timer/arm_timer.c index 69c8863472..9afe8da831 100644 --- a/hw/timer/arm_timer.c +++ b/hw/timer/arm_timer.c @@ -181,7 +181,7 @@ static arm_timer_state *arm_timer_init(uint32_t freq) s->control = TIMER_CTRL_IE; s->timer = ptimer_init(arm_timer_tick, s, PTIMER_POLICY_LEGACY); - vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, &vmstate_arm_timer, s); + vmstate_register_any(NULL, &vmstate_arm_timer, s); return s; } diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index 9dc3c61b5a..a5ea3be414 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -1119,8 +1119,8 @@ static void virtio_mem_device_realize(DeviceState *dev, Error **errp) host_memory_backend_set_mapped(vmem->memdev, true); vmstate_register_ram(&vmem->memdev->mr, DEVICE(vmem)); if (vmem->early_migration) { - vmstate_register(VMSTATE_IF(vmem), VMSTATE_INSTANCE_ID_ANY, - &vmstate_virtio_mem_device_early, vmem); + vmstate_register_any(VMSTATE_IF(vmem), + &vmstate_virtio_mem_device_early, vmem); } qemu_register_reset(virtio_mem_system_reset, vmem); From 1f52c7a80bf19fef5228abe85cdb845088e07274 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Fri, 20 Oct 2023 11:07:21 +0200 Subject: [PATCH 304/974] migration: Use vmstate_register_any() for isa-ide Otherwise qom-test fails. ok 4 /i386/qom/x-remote qemu-system-i386: savevm_state_handler_insert: Detected duplicate SaveStateEntry: id=isa-ide, instance_id=0x0 Broken pipe ../../../../../mnt/code/qemu/full/tests/qtest/libqtest.c:195: kill_qemu() tried to terminate QEMU process but encountered exit status 1 (expected 0) Aborted (core dumped) $ Reviewed-by: Stefan Berger Signed-off-by: Juan Quintela Message-ID: <20231020090731.28701-4-quintela@redhat.com> --- hw/ide/isa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/ide/isa.c b/hw/ide/isa.c index 95053e026f..ea60c08116 100644 --- a/hw/ide/isa.c +++ b/hw/ide/isa.c @@ -73,7 +73,7 @@ static void isa_ide_realizefn(DeviceState *dev, Error **errp) ide_bus_init(&s->bus, sizeof(s->bus), dev, 0, 2); ide_init_ioport(&s->bus, isadev, s->iobase, s->iobase2); ide_bus_init_output_irq(&s->bus, isa_get_irq(isadev, s->irqnum)); - vmstate_register(VMSTATE_IF(dev), 0, &vmstate_ide_isa, s); + vmstate_register_any(VMSTATE_IF(dev), &vmstate_ide_isa, s); ide_bus_register_restart_cb(&s->bus); } From b23db4cd8218345a7ec75a8ff810896ce3a391b1 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Fri, 20 Oct 2023 11:07:23 +0200 Subject: [PATCH 305/974] migration: Use VMSTATE_INSTANCE_ID_ANY for slirp Each user network conection create a new slirp instance. We register more than one slirp instance for number 0. qemu-system-x86_64: -netdev user,id=hs1: savevm_state_handler_insert: Detected duplicate SaveStateEntry: id=slirp, instance_id=0x0 Broken pipe ../../../../../mnt/code/qemu/full/tests/qtest/libqtest.c:195: kill_qemu() tried to terminate QEMU process but encountered exit status 1 (expected 0) Aborted (core dumped) Reviewed-by: Stefan Berger Signed-off-by: Juan Quintela Message-ID: <20231020090731.28701-6-quintela@redhat.com> --- net/slirp.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/slirp.c b/net/slirp.c index c33b3e02e7..25b49c4526 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -46,6 +46,7 @@ #include "qapi/qmp/qdict.h" #include "util.h" #include "migration/register.h" +#include "migration/vmstate.h" #include "migration/qemu-file-types.h" static int get_str_sep(char *buf, int buf_size, const char **pp, int sep) @@ -659,8 +660,8 @@ static int net_slirp_init(NetClientState *peer, const char *model, * specific version? */ g_assert(slirp_state_version() == 4); - register_savevm_live("slirp", 0, slirp_state_version(), - &savevm_slirp_state, s->slirp); + register_savevm_live("slirp", VMSTATE_INSTANCE_ID_ANY, + slirp_state_version(), &savevm_slirp_state, s->slirp); s->poll_notifier.notify = net_slirp_poll_notify; main_loop_poll_add_notifier(&s->poll_notifier); From 485fb95546e1cc94e77b93ff785bdbfa2c1d3ff1 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Fri, 20 Oct 2023 11:07:25 +0200 Subject: [PATCH 306/974] migration: Hack to maintain backwards compatibility for ppc Current code does: - register pre_2_10_vmstate_dummy_icp with "icp/server" and instance dependinfg on cpu number - for newer machines, it register vmstate_icp with "icp/server" name and instance 0 - now it unregisters "icp/server" for the 1st instance. This is wrong at many levels: - we shouldn't have two VMSTATEDescriptions with the same name - In case this is the only solution that we can came with, it needs to be: * register pre_2_10_vmstate_dummy_icp * unregister pre_2_10_vmstate_dummy_icp * register real vmstate_icp Created vmstate_replace_hack_for_ppc() with warnings left and right that it is a hack. CC: Cedric Le Goater CC: Daniel Henrique Barboza CC: David Gibson CC: Greg Kurz Reviewed-by: Nicholas Piggin Signed-off-by: Juan Quintela Message-ID: <20231020090731.28701-8-quintela@redhat.com> --- hw/intc/xics.c | 18 ++++++++++++++++-- hw/ppc/spapr.c | 25 +++++++++++++++++++++++-- include/migration/vmstate.h | 11 +++++++++++ migration/savevm.c | 18 ++++++++++++++++++ 4 files changed, 68 insertions(+), 4 deletions(-) diff --git a/hw/intc/xics.c b/hw/intc/xics.c index c7f8abd71e..c77e986136 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -335,8 +335,22 @@ static void icp_realize(DeviceState *dev, Error **errp) return; } } - - vmstate_register(NULL, icp->cs->cpu_index, &vmstate_icp_server, icp); + /* + * The way that pre_2_10_icp is handling is really, really hacky. + * We used to have here this call: + * + * vmstate_register(NULL, icp->cs->cpu_index, &vmstate_icp_server, icp); + * + * But we were doing: + * pre_2_10_vmstate_register_dummy_icp() + * this vmstate_register() + * pre_2_10_vmstate_unregister_dummy_icp() + * + * So for a short amount of time we had to vmstate entries with + * the same name. This fixes it. + */ + vmstate_replace_hack_for_ppc(NULL, icp->cs->cpu_index, + &vmstate_icp_server, icp); } static void icp_unrealize(DeviceState *dev) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index b25093be28..df09aa9d6a 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -143,6 +143,11 @@ static bool pre_2_10_vmstate_dummy_icp_needed(void *opaque) } static const VMStateDescription pre_2_10_vmstate_dummy_icp = { + /* + * Hack ahead. We can't have two devices with the same name and + * instance id. So I rename this to pass make check. + * Real help from people who knows the hardware is needed. + */ .name = "icp/server", .version_id = 1, .minimum_version_id = 1, @@ -155,16 +160,32 @@ static const VMStateDescription pre_2_10_vmstate_dummy_icp = { }, }; +/* + * See comment in hw/intc/xics.c:icp_realize() + * + * You have to remove vmstate_replace_hack_for_ppc() when you remove + * the machine types that need the following function. + */ static void pre_2_10_vmstate_register_dummy_icp(int i) { vmstate_register(NULL, i, &pre_2_10_vmstate_dummy_icp, (void *)(uintptr_t) i); } +/* + * See comment in hw/intc/xics.c:icp_realize() + * + * You have to remove vmstate_replace_hack_for_ppc() when you remove + * the machine types that need the following function. + */ static void pre_2_10_vmstate_unregister_dummy_icp(int i) { - vmstate_unregister(NULL, &pre_2_10_vmstate_dummy_icp, - (void *)(uintptr_t) i); + /* + * This used to be: + * + * vmstate_unregister(NULL, &pre_2_10_vmstate_dummy_icp, + * (void *)(uintptr_t) i); + */ } int spapr_max_server_number(SpaprMachineState *spapr) diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index 1ea97ccf2d..9821918631 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -1230,6 +1230,17 @@ static inline int vmstate_register(VMStateIf *obj, int instance_id, opaque, -1, 0, NULL); } +/** + * vmstate_replace_hack_for_ppc() - ppc used to abuse vmstate_register + * + * Don't even think about using this function in new code. + * + * Returns: 0 on success, -1 on failure + */ +int vmstate_replace_hack_for_ppc(VMStateIf *obj, int instance_id, + const VMStateDescription *vmsd, + void *opaque); + /** * vmstate_register_any() - legacy function to register state * serialisation description and let the function choose the id diff --git a/migration/savevm.c b/migration/savevm.c index c7835e9c73..c7596c3e9b 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -846,6 +846,24 @@ static void vmstate_check(const VMStateDescription *vmsd) } } +/* + * See comment in hw/intc/xics.c:icp_realize() + * + * This function can be removed when + * pre_2_10_vmstate_register_dummy_icp() is removed. + */ +int vmstate_replace_hack_for_ppc(VMStateIf *obj, int instance_id, + const VMStateDescription *vmsd, + void *opaque) +{ + SaveStateEntry *se = find_se(vmsd->name, instance_id); + + if (se) { + savevm_state_handler_remove(se); + } + return vmstate_register(obj, instance_id, vmsd, opaque); +} + int vmstate_register_with_alias_id(VMStateIf *obj, uint32_t instance_id, const VMStateDescription *vmsd, void *opaque, int alias_id, From caa91b3c44cdb2d2921e25eda554d38c527e6c47 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 20 Oct 2023 11:07:27 +0200 Subject: [PATCH 307/974] migration: Check in savevm_state_handler_insert for dups Before finally register one SaveStateEntry, we detect for duplicated entries. This could be helpful to notify us asap instead of get silent migration failures which could be hard to diagnose. For example, this patch will generate a message like this (if without previous fixes on x2apic) as long as we wants to boot a VM instance with "-smp 200,maxcpus=288,sockets=2,cores=72,threads=2" and QEMU will bail out even before VM starts: savevm_state_handler_insert: Detected duplicate SaveStateEntry: id=apic, instance_id=0x0 Suggested-by: Dr. David Alan Gilbert Signed-off-by: Peter Xu Reviewed-by: Juan Quintela Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Juan Quintela Message-ID: <20231020090731.28701-10-quintela@redhat.com> --- migration/savevm.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/migration/savevm.c b/migration/savevm.c index c7596c3e9b..2095ddd6f8 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -237,6 +237,8 @@ static SaveState savevm_state = { .global_section_id = 0, }; +static SaveStateEntry *find_se(const char *idstr, uint32_t instance_id); + static bool should_validate_capability(int capability) { assert(capability >= 0 && capability < MIGRATION_CAPABILITY__MAX); @@ -716,6 +718,18 @@ static void savevm_state_handler_insert(SaveStateEntry *nse) assert(priority <= MIG_PRI_MAX); + /* + * This should never happen otherwise migration will probably fail + * silently somewhere because we can be wrongly applying one + * object properties upon another one. Bail out ASAP. + */ + if (find_se(nse->idstr, nse->instance_id)) { + error_report("%s: Detected duplicate SaveStateEntry: " + "id=%s, instance_id=0x%"PRIx32, __func__, + nse->idstr, nse->instance_id); + exit(EXIT_FAILURE); + } + for (i = priority - 1; i >= 0; i--) { se = savevm_state.handler_pri_head[i]; if (se != NULL) { From 5b146be3e78d0290670cfc60179218e539a7774f Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Fri, 20 Oct 2023 11:07:28 +0200 Subject: [PATCH 308/974] migration: Improve example and documentation of vmstate_register() Reviewed-by: Stefan Berger Signed-off-by: Juan Quintela Message-ID: <20231020090731.28701-11-quintela@redhat.com> --- docs/devel/migration.rst | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/devel/migration.rst b/docs/devel/migration.rst index be913630c3..240eb16d90 100644 --- a/docs/devel/migration.rst +++ b/docs/devel/migration.rst @@ -167,13 +167,17 @@ An example (from hw/input/pckbd.c) } }; -We are declaring the state with name "pckbd". -The ``version_id`` is 3, and the fields are 4 uint8_t in a KBDState structure. -We registered this with: +We are declaring the state with name "pckbd". The ``version_id`` is +3, and there are 4 uint8_t fields in the KBDState structure. We +registered this ``VMSTATEDescription`` with one of the following +functions. The first one will generate a device ``instance_id`` +different for each registration. Use the second one if you already +have an id that is different for each instance of the device: .. code:: c - vmstate_register(NULL, 0, &vmstate_kbd, s); + vmstate_register_any(NULL, &vmstate_kbd, s); + vmstate_register(NULL, instance_id, &vmstate_kbd, s); For devices that are ``qdev`` based, we can register the device in the class init function: From a9500913ab96f59eb8f7e5fc79f80d5d66842102 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Fri, 20 Oct 2023 11:07:29 +0200 Subject: [PATCH 309/974] migration: Use vmstate_register_any() for audio We can have more than one audio backend. void audio_init_audiodevs(void) { AudiodevListEntry *e; QSIMPLEQ_FOREACH(e, &audiodevs, next) { audio_init(e->dev, &error_fatal); } } Reviewed-by: Stefan Berger Signed-off-by: Juan Quintela Message-ID: <20231020090731.28701-12-quintela@redhat.com> --- audio/audio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audio/audio.c b/audio/audio.c index e9815d6812..f91e05b72c 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -1781,7 +1781,7 @@ static AudioState *audio_init(Audiodev *dev, Error **errp) QTAILQ_INSERT_TAIL(&audio_states, s, list); QLIST_INIT (&s->card_head); - vmstate_register (NULL, 0, &vmstate_audio, s); + vmstate_register_any(NULL, &vmstate_audio, s); return s; out: From 7769fb81efeda6546f4c0fe3324bde91443aabec Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Fri, 20 Oct 2023 11:07:30 +0200 Subject: [PATCH 310/974] migration: Use vmstate_register_any() for eeprom93xx We can have more than one eeprom93xx. For instance: e100_nic_realize() -> eeprom93xx_new() Reviewed-by: Stefan Berger Signed-off-by: Juan Quintela Message-ID: <20231020090731.28701-13-quintela@redhat.com> --- hw/nvram/eeprom93xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/nvram/eeprom93xx.c b/hw/nvram/eeprom93xx.c index 1081e2cc0d..57d63638d7 100644 --- a/hw/nvram/eeprom93xx.c +++ b/hw/nvram/eeprom93xx.c @@ -321,7 +321,7 @@ eeprom_t *eeprom93xx_new(DeviceState *dev, uint16_t nwords) /* Output DO is tristate, read results in 1. */ eeprom->eedo = 1; logout("eeprom = 0x%p, nwords = %u\n", eeprom, nwords); - vmstate_register(VMSTATE_IF(dev), 0, &vmstate_eeprom, eeprom); + vmstate_register_any(VMSTATE_IF(dev), &vmstate_eeprom, eeprom); return eeprom; } From c64a59552e44fbae22a565eec3632df59e79d57c Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Fri, 20 Oct 2023 11:07:31 +0200 Subject: [PATCH 311/974] migration: Use vmstate_register_any() for vmware_vga I have no idea if we can have more than one vmware_vga device, so play it safe. Reviewed-by: Stefan Berger Reviewed-by: Thomas Huth Signed-off-by: Juan Quintela Message-ID: <20231020090731.28701-14-quintela@redhat.com> --- hw/display/vmware_vga.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c index 09591fbd39..7490d43881 100644 --- a/hw/display/vmware_vga.c +++ b/hw/display/vmware_vga.c @@ -1264,7 +1264,7 @@ static void vmsvga_init(DeviceState *dev, struct vmsvga_state_s *s, vga_common_init(&s->vga, OBJECT(dev), &error_fatal); vga_init(&s->vga, OBJECT(dev), address_space, io, true); - vmstate_register(NULL, 0, &vmstate_vga_common, &s->vga); + vmstate_register_any(NULL, &vmstate_vga_common, &s->vga); s->new_depth = 32; } From 62f5da7dd10a594fb30cebb5569dc738456f7131 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Mon, 30 Oct 2023 12:33:42 -0400 Subject: [PATCH 312/974] migration: Set downtime_start even for postcopy Postcopy calculates its downtime separately. It always sets MigrationState.downtime properly, but not MigrationState.downtime_start. Make postcopy do the same as other modes on properly recording the timestamp when the VM is going to be stopped. Drop the temporary variable in postcopy_start() along the way. Signed-off-by: Peter Xu Reviewed-by: Fabiano Rosas Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-ID: <20231030163346.765724-2-peterx@redhat.com> --- migration/migration.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 6abcbefd9c..6dcdc5be2b 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2126,7 +2126,6 @@ static int postcopy_start(MigrationState *ms, Error **errp) int ret; QIOChannelBuffer *bioc; QEMUFile *fb; - int64_t time_at_stop = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); uint64_t bandwidth = migrate_max_postcopy_bandwidth(); bool restart_block = false; int cur_state = MIGRATION_STATUS_ACTIVE; @@ -2148,6 +2147,8 @@ static int postcopy_start(MigrationState *ms, Error **errp) qemu_mutex_lock_iothread(); trace_postcopy_start_set_run(); + ms->downtime_start = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL); global_state_store(); ret = vm_stop_force_state(RUN_STATE_FINISH_MIGRATE); @@ -2250,7 +2251,7 @@ static int postcopy_start(MigrationState *ms, Error **errp) ms->postcopy_after_devices = true; migration_call_notifiers(ms); - ms->downtime = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - time_at_stop; + ms->downtime = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - ms->downtime_start; qemu_mutex_unlock_iothread(); From e22ffad03a32d3fc4717561f8af5ef7d9657728d Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Mon, 30 Oct 2023 12:33:43 -0400 Subject: [PATCH 313/974] migration: Add migration_downtime_start|end() helpers Unify the three users on recording downtimes with the same pair of helpers. Signed-off-by: Peter Xu Reviewed-by: Fabiano Rosas Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-ID: <20231030163346.765724-3-peterx@redhat.com> --- migration/migration.c | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 6dcdc5be2b..8aac0c753e 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -101,6 +101,24 @@ static int migration_maybe_pause(MigrationState *s, static void migrate_fd_cancel(MigrationState *s); static int close_return_path_on_source(MigrationState *s); +static void migration_downtime_start(MigrationState *s) +{ + s->downtime_start = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); +} + +static void migration_downtime_end(MigrationState *s) +{ + int64_t now = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + + /* + * If downtime already set, should mean that postcopy already set it, + * then that should be the real downtime already. + */ + if (!s->downtime) { + s->downtime = now - s->downtime_start; + } +} + static bool migration_needs_multiple_sockets(void) { return migrate_multifd() || migrate_postcopy_preempt(); @@ -2147,7 +2165,7 @@ static int postcopy_start(MigrationState *ms, Error **errp) qemu_mutex_lock_iothread(); trace_postcopy_start_set_run(); - ms->downtime_start = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + migration_downtime_start(ms); qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL); global_state_store(); @@ -2251,7 +2269,7 @@ static int postcopy_start(MigrationState *ms, Error **errp) ms->postcopy_after_devices = true; migration_call_notifiers(ms); - ms->downtime = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - ms->downtime_start; + migration_downtime_end(ms); qemu_mutex_unlock_iothread(); @@ -2347,7 +2365,7 @@ static int migration_completion_precopy(MigrationState *s, int ret; qemu_mutex_lock_iothread(); - s->downtime_start = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + migration_downtime_start(s); qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL); s->vm_old_state = runstate_get(); @@ -2704,15 +2722,8 @@ static void migration_calculate_complete(MigrationState *s) int64_t end_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); int64_t transfer_time; + migration_downtime_end(s); s->total_time = end_time - s->start_time; - if (!s->downtime) { - /* - * It's still not set, so we are precopy migration. For - * postcopy, downtime is calculated during postcopy_start(). - */ - s->downtime = end_time - s->downtime_start; - } - transfer_time = s->total_time - s->setup_time; if (transfer_time) { s->mbps = ((double) bytes * 8.0) / transfer_time / 1000; @@ -3131,7 +3142,7 @@ static void bg_migration_vm_start_bh(void *opaque) s->vm_start_bh = NULL; vm_start(); - s->downtime = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - s->downtime_start; + migration_downtime_end(s); } /** @@ -3198,7 +3209,7 @@ static void *bg_migration_thread(void *opaque) s->setup_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) - setup_start; trace_migration_thread_setup_complete(); - s->downtime_start = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + migration_downtime_start(s); qemu_mutex_lock_iothread(); From 3c80f14272057f77c87fb9971e8c4f603a03289a Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Mon, 30 Oct 2023 12:33:44 -0400 Subject: [PATCH 314/974] migration: Add per vmstate downtime tracepoints We have a bunch of savevm_section* tracepoints, they're good to analyze migration stream, but not always suitable if someone would like to analyze the migration downtime. Two major problems: - savevm_section* tracepoints are dumping all sections, we only care about the sections that contribute to the downtime - They don't have an identifier to show the type of sections, so no way to filter downtime information either easily. We can add type into the tracepoints, but instead of doing so, this patch kept them untouched, instead of adding a bunch of downtime specific tracepoints, so one can enable "vmstate_downtime*" tracepoints and get a full picture of how the downtime is distributed across iterative and non-iterative vmstate save/load. Note that here both save() and load() need to be traced, because both of them may contribute to the downtime. The contribution is not a simple "add them together", though: consider when the src is doing a save() of device1 while the dest can be load()ing for device2, so they can happen concurrently. Tracking both sides make sense because device load() and save() can be imbalanced, one device can save() super fast, but load() super slow, vice versa. We can't figure that out without tracing both. Signed-off-by: Peter Xu Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-ID: <20231030163346.765724-4-peterx@redhat.com> --- migration/savevm.c | 49 ++++++++++++++++++++++++++++++++++++++---- migration/trace-events | 2 ++ 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/migration/savevm.c b/migration/savevm.c index 2095ddd6f8..99ce42b6f3 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1491,6 +1491,7 @@ void qemu_savevm_state_complete_postcopy(QEMUFile *f) static int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy) { + int64_t start_ts_each, end_ts_each; SaveStateEntry *se; int ret; @@ -1507,6 +1508,8 @@ int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy) continue; } } + + start_ts_each = qemu_clock_get_us(QEMU_CLOCK_REALTIME); trace_savevm_section_start(se->idstr, se->section_id); save_section_header(f, se, QEMU_VM_SECTION_END); @@ -1518,6 +1521,9 @@ int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy) qemu_file_set_error(f, ret); return -1; } + end_ts_each = qemu_clock_get_us(QEMU_CLOCK_REALTIME); + trace_vmstate_downtime_save("iterable", se->idstr, se->instance_id, + end_ts_each - start_ts_each); } return 0; @@ -1528,6 +1534,7 @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, bool inactivate_disks) { MigrationState *ms = migrate_get_current(); + int64_t start_ts_each, end_ts_each; JSONWriter *vmdesc = ms->vmdesc; int vmdesc_len; SaveStateEntry *se; @@ -1539,11 +1546,17 @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, continue; } + start_ts_each = qemu_clock_get_us(QEMU_CLOCK_REALTIME); + ret = vmstate_save(f, se, vmdesc); if (ret) { qemu_file_set_error(f, ret); return ret; } + + end_ts_each = qemu_clock_get_us(QEMU_CLOCK_REALTIME); + trace_vmstate_downtime_save("non-iterable", se->idstr, se->instance_id, + end_ts_each - start_ts_each); } if (inactivate_disks) { @@ -2537,9 +2550,12 @@ static bool check_section_footer(QEMUFile *f, SaveStateEntry *se) } static int -qemu_loadvm_section_start_full(QEMUFile *f, MigrationIncomingState *mis) +qemu_loadvm_section_start_full(QEMUFile *f, MigrationIncomingState *mis, + uint8_t type) { + bool trace_downtime = (type == QEMU_VM_SECTION_FULL); uint32_t instance_id, version_id, section_id; + int64_t start_ts, end_ts; SaveStateEntry *se; char idstr[256]; int ret; @@ -2588,12 +2604,23 @@ qemu_loadvm_section_start_full(QEMUFile *f, MigrationIncomingState *mis) return -EINVAL; } + if (trace_downtime) { + start_ts = qemu_clock_get_us(QEMU_CLOCK_REALTIME); + } + ret = vmstate_load(f, se); if (ret < 0) { error_report("error while loading state for instance 0x%"PRIx32" of" " device '%s'", instance_id, idstr); return ret; } + + if (trace_downtime) { + end_ts = qemu_clock_get_us(QEMU_CLOCK_REALTIME); + trace_vmstate_downtime_load("non-iterable", se->idstr, + se->instance_id, end_ts - start_ts); + } + if (!check_section_footer(f, se)) { return -EINVAL; } @@ -2602,8 +2629,11 @@ qemu_loadvm_section_start_full(QEMUFile *f, MigrationIncomingState *mis) } static int -qemu_loadvm_section_part_end(QEMUFile *f, MigrationIncomingState *mis) +qemu_loadvm_section_part_end(QEMUFile *f, MigrationIncomingState *mis, + uint8_t type) { + bool trace_downtime = (type == QEMU_VM_SECTION_END); + int64_t start_ts, end_ts; uint32_t section_id; SaveStateEntry *se; int ret; @@ -2628,12 +2658,23 @@ qemu_loadvm_section_part_end(QEMUFile *f, MigrationIncomingState *mis) return -EINVAL; } + if (trace_downtime) { + start_ts = qemu_clock_get_us(QEMU_CLOCK_REALTIME); + } + ret = vmstate_load(f, se); if (ret < 0) { error_report("error while loading state section id %d(%s)", section_id, se->idstr); return ret; } + + if (trace_downtime) { + end_ts = qemu_clock_get_us(QEMU_CLOCK_REALTIME); + trace_vmstate_downtime_load("iterable", se->idstr, + se->instance_id, end_ts - start_ts); + } + if (!check_section_footer(f, se)) { return -EINVAL; } @@ -2822,14 +2863,14 @@ retry: switch (section_type) { case QEMU_VM_SECTION_START: case QEMU_VM_SECTION_FULL: - ret = qemu_loadvm_section_start_full(f, mis); + ret = qemu_loadvm_section_start_full(f, mis, section_type); if (ret < 0) { goto out; } break; case QEMU_VM_SECTION_PART: case QEMU_VM_SECTION_END: - ret = qemu_loadvm_section_part_end(f, mis); + ret = qemu_loadvm_section_part_end(f, mis, section_type); if (ret < 0) { goto out; } diff --git a/migration/trace-events b/migration/trace-events index fa9486dffe..5820add1f3 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -48,6 +48,8 @@ savevm_state_cleanup(void) "" savevm_state_complete_precopy(void) "" vmstate_save(const char *idstr, const char *vmsd_name) "%s, %s" vmstate_load(const char *idstr, const char *vmsd_name) "%s, %s" +vmstate_downtime_save(const char *type, const char *idstr, uint32_t instance_id, int64_t downtime) "type=%s idstr=%s instance_id=%d downtime=%"PRIi64 +vmstate_downtime_load(const char *type, const char *idstr, uint32_t instance_id, int64_t downtime) "type=%s idstr=%s instance_id=%d downtime=%"PRIi64 postcopy_pause_incoming(void) "" postcopy_pause_incoming_continued(void) "" postcopy_page_req_sync(void *host_addr) "sync page req %p" From 93bdf888fa5c1b3972e337c8c8fbe7d2bcd29900 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Mon, 30 Oct 2023 12:33:45 -0400 Subject: [PATCH 315/974] migration: migration_stop_vm() helper Provide a helper for non-COLO use case of migration to stop a VM. This prepares for adding some downtime relevant tracepoints to migration, where they may or may not apply to COLO. Signed-off-by: Peter Xu Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-ID: <20231030163346.765724-5-peterx@redhat.com> --- migration/migration.c | 11 ++++++++--- migration/migration.h | 2 ++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 8aac0c753e..3e38294485 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -149,6 +149,11 @@ static gint page_request_addr_cmp(gconstpointer ap, gconstpointer bp) return (a > b) - (a < b); } +int migration_stop_vm(RunState state) +{ + return vm_stop_force_state(state); +} + void migration_object_init(void) { /* This can only be called once. */ @@ -2169,7 +2174,7 @@ static int postcopy_start(MigrationState *ms, Error **errp) qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL); global_state_store(); - ret = vm_stop_force_state(RUN_STATE_FINISH_MIGRATE); + ret = migration_stop_vm(RUN_STATE_FINISH_MIGRATE); if (ret < 0) { goto fail; } @@ -2371,7 +2376,7 @@ static int migration_completion_precopy(MigrationState *s, s->vm_old_state = runstate_get(); global_state_store(); - ret = vm_stop_force_state(RUN_STATE_FINISH_MIGRATE); + ret = migration_stop_vm(RUN_STATE_FINISH_MIGRATE); trace_migration_completion_vm_stop(ret); if (ret < 0) { goto out_unlock; @@ -3222,7 +3227,7 @@ static void *bg_migration_thread(void *opaque) global_state_store(); /* Forcibly stop VM before saving state of vCPUs and devices */ - if (vm_stop_force_state(RUN_STATE_PAUSED)) { + if (migration_stop_vm(RUN_STATE_PAUSED)) { goto fail; } /* diff --git a/migration/migration.h b/migration/migration.h index ae82004892..5944107ad5 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -544,4 +544,6 @@ void migration_rp_wait(MigrationState *s); */ void migration_rp_kick(MigrationState *s); +int migration_stop_vm(RunState state); + #endif From 3e5f3bcdc281fed483b5bbe72050b63d4b41abf5 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Mon, 30 Oct 2023 12:33:46 -0400 Subject: [PATCH 316/974] migration: Add tracepoints for downtime checkpoints This patch is inspired by Joao Martin's patch here: https://lore.kernel.org/r/20230926161841.98464-1-joao.m.martins@oracle.com Add tracepoints for major downtime checkpoints on both src and dst. They share the same tracepoint with a string showing its stage. Besides the checkpoints in the previous patch, this patch also added destination checkpoints. On src, we have these checkpoints added: - src-downtime-start: right before vm stops on src - src-vm-stopped: after vm is fully stopped - src-iterable-saved: after all iterables saved (END sections) - src-non-iterable-saved: after all non-iterable saved (FULL sections) - src-downtime-stop: migration fully completed On dst, we have these checkpoints added: - dst-precopy-loadvm-completes: after loadvm all done for precopy - dst-precopy-bh-*: record BH steps to resume VM for precopy - dst-postcopy-bh-*: record BH steps to resume VM for postcopy On dst side, we don't have a good way to trace total time consumed by iterable or non-iterable for now. We can mark it by 1st time receiving a FULL / END section, but rather than that let's just rely on the other tracepoints added for vmstates to back up the information. With this patch, one can enable "vmstate_downtime*" tracepoints and it'll enable all tracepoints for downtime measurements necessary. Drop loadvm_postcopy_handle_run_bh() tracepoint alongside, because they service the same purpose, which was only for postcopy. We then have unified prefix for all downtime relevant tracepoints. Co-developed-by: Joao Martins Signed-off-by: Peter Xu Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-ID: <20231030163346.765724-6-peterx@redhat.com> --- migration/migration.c | 16 +++++++++++++++- migration/savevm.c | 14 +++++++++----- migration/trace-events | 2 +- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 3e38294485..c334b9effd 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -103,6 +103,7 @@ static int close_return_path_on_source(MigrationState *s); static void migration_downtime_start(MigrationState *s) { + trace_vmstate_downtime_checkpoint("src-downtime-start"); s->downtime_start = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); } @@ -117,6 +118,8 @@ static void migration_downtime_end(MigrationState *s) if (!s->downtime) { s->downtime = now - s->downtime_start; } + + trace_vmstate_downtime_checkpoint("src-downtime-end"); } static bool migration_needs_multiple_sockets(void) @@ -151,7 +154,11 @@ static gint page_request_addr_cmp(gconstpointer ap, gconstpointer bp) int migration_stop_vm(RunState state) { - return vm_stop_force_state(state); + int ret = vm_stop_force_state(state); + + trace_vmstate_downtime_checkpoint("src-vm-stopped"); + + return ret; } void migration_object_init(void) @@ -495,6 +502,8 @@ static void process_incoming_migration_bh(void *opaque) Error *local_err = NULL; MigrationIncomingState *mis = opaque; + trace_vmstate_downtime_checkpoint("dst-precopy-bh-enter"); + /* If capability late_block_activate is set: * Only fire up the block code now if we're going to restart the * VM, else 'cont' will do it. @@ -520,6 +529,8 @@ static void process_incoming_migration_bh(void *opaque) */ qemu_announce_self(&mis->announce_timer, migrate_announce_params()); + trace_vmstate_downtime_checkpoint("dst-precopy-bh-announced"); + multifd_load_shutdown(); dirty_bitmap_mig_before_vm_start(); @@ -537,6 +548,7 @@ static void process_incoming_migration_bh(void *opaque) } else { runstate_set(global_state_get_runstate()); } + trace_vmstate_downtime_checkpoint("dst-precopy-bh-vm-started"); /* * This must happen after any state changes since as soon as an external * observer sees this event they might start to prod at the VM assuming @@ -571,6 +583,8 @@ process_incoming_migration_co(void *opaque) ret = qemu_loadvm_state(mis->from_src_file); mis->loadvm_co = NULL; + trace_vmstate_downtime_checkpoint("dst-precopy-loadvm-completed"); + ps = postcopy_state_get(); trace_process_incoming_migration_co_end(ret, ps); if (ps != POSTCOPY_INCOMING_NONE) { diff --git a/migration/savevm.c b/migration/savevm.c index 99ce42b6f3..bc98c2ea6f 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1526,6 +1526,8 @@ int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy) end_ts_each - start_ts_each); } + trace_vmstate_downtime_checkpoint("src-iterable-saved"); + return 0; } @@ -1592,6 +1594,8 @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, json_writer_free(vmdesc); ms->vmdesc = NULL; + trace_vmstate_downtime_checkpoint("src-non-iterable-saved"); + return 0; } @@ -2133,18 +2137,18 @@ static void loadvm_postcopy_handle_run_bh(void *opaque) Error *local_err = NULL; MigrationIncomingState *mis = opaque; - trace_loadvm_postcopy_handle_run_bh("enter"); + trace_vmstate_downtime_checkpoint("dst-postcopy-bh-enter"); /* TODO we should move all of this lot into postcopy_ram.c or a shared code * in migration.c */ cpu_synchronize_all_post_init(); - trace_loadvm_postcopy_handle_run_bh("after cpu sync"); + trace_vmstate_downtime_checkpoint("dst-postcopy-bh-cpu-synced"); qemu_announce_self(&mis->announce_timer, migrate_announce_params()); - trace_loadvm_postcopy_handle_run_bh("after announce"); + trace_vmstate_downtime_checkpoint("dst-postcopy-bh-announced"); /* Make sure all file formats throw away their mutable metadata. * If we get an error here, just don't restart the VM yet. */ @@ -2155,7 +2159,7 @@ static void loadvm_postcopy_handle_run_bh(void *opaque) autostart = false; } - trace_loadvm_postcopy_handle_run_bh("after invalidate cache"); + trace_vmstate_downtime_checkpoint("dst-postcopy-bh-cache-invalidated"); dirty_bitmap_mig_before_vm_start(); @@ -2169,7 +2173,7 @@ static void loadvm_postcopy_handle_run_bh(void *opaque) qemu_bh_delete(mis->bh); - trace_loadvm_postcopy_handle_run_bh("return"); + trace_vmstate_downtime_checkpoint("dst-postcopy-bh-vm-started"); } /* After all discards we can start running and asking for pages */ diff --git a/migration/trace-events b/migration/trace-events index 5820add1f3..e54f317e3b 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -17,7 +17,6 @@ loadvm_handle_recv_bitmap(char *s) "%s" loadvm_postcopy_handle_advise(void) "" loadvm_postcopy_handle_listen(const char *str) "%s" loadvm_postcopy_handle_run(void) "" -loadvm_postcopy_handle_run_bh(const char *str) "%s" loadvm_postcopy_handle_resume(void) "" loadvm_postcopy_ram_handle_discard(void) "" loadvm_postcopy_ram_handle_discard_end(void) "" @@ -50,6 +49,7 @@ vmstate_save(const char *idstr, const char *vmsd_name) "%s, %s" vmstate_load(const char *idstr, const char *vmsd_name) "%s, %s" vmstate_downtime_save(const char *type, const char *idstr, uint32_t instance_id, int64_t downtime) "type=%s idstr=%s instance_id=%d downtime=%"PRIi64 vmstate_downtime_load(const char *type, const char *idstr, uint32_t instance_id, int64_t downtime) "type=%s idstr=%s instance_id=%d downtime=%"PRIi64 +vmstate_downtime_checkpoint(const char *checkpoint) "%s" postcopy_pause_incoming(void) "" postcopy_pause_incoming_continued(void) "" postcopy_page_req_sync(void *host_addr) "sync page req %p" From eea1e5c9d6fac211cf3fe2d3d7f3be2bf767d421 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 25 Oct 2023 12:44:24 -0700 Subject: [PATCH 317/974] migration: mode parameter Create a mode migration parameter that can be used to select alternate migration algorithms. The default mode is normal, representing the current migration algorithm, and does not need to be explicitly set. No functional change until a new mode is added, except that the mode is shown by the 'info migrate' command. Signed-off-by: Steve Sistare Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-ID: <1698263069-406971-2-git-send-email-steven.sistare@oracle.com> --- hw/core/qdev-properties-system.c | 14 ++++++++++++++ include/hw/qdev-properties-system.h | 4 ++++ include/migration/misc.h | 1 + migration/migration-hmp-cmds.c | 9 +++++++++ migration/options.c | 21 +++++++++++++++++++++ migration/options.h | 1 + qapi/migration.json | 27 ++++++++++++++++++++++++--- 7 files changed, 74 insertions(+), 3 deletions(-) diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index 7c6dfab128..b9179e8917 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -673,6 +673,20 @@ const PropertyInfo qdev_prop_multifd_compression = { .set_default_value = qdev_propinfo_set_default_value_enum, }; +/* --- MigMode --- */ + +QEMU_BUILD_BUG_ON(sizeof(MigMode) != sizeof(int)); + +const PropertyInfo qdev_prop_mig_mode = { + .name = "MigMode", + .description = "mig_mode values, " + "normal", + .enum_table = &MigMode_lookup, + .get = qdev_propinfo_get_enum, + .set = qdev_propinfo_set_enum, + .set_default_value = qdev_propinfo_set_default_value_enum, +}; + /* --- Reserved Region --- */ /* diff --git a/include/hw/qdev-properties-system.h b/include/hw/qdev-properties-system.h index e4f8a13afc..91f7a2452d 100644 --- a/include/hw/qdev-properties-system.h +++ b/include/hw/qdev-properties-system.h @@ -7,6 +7,7 @@ extern const PropertyInfo qdev_prop_chr; extern const PropertyInfo qdev_prop_macaddr; extern const PropertyInfo qdev_prop_reserved_region; extern const PropertyInfo qdev_prop_multifd_compression; +extern const PropertyInfo qdev_prop_mig_mode; extern const PropertyInfo qdev_prop_losttickpolicy; extern const PropertyInfo qdev_prop_blockdev_on_error; extern const PropertyInfo qdev_prop_bios_chs_trans; @@ -42,6 +43,9 @@ extern const PropertyInfo qdev_prop_cpus390entitlement; #define DEFINE_PROP_MULTIFD_COMPRESSION(_n, _s, _f, _d) \ DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_multifd_compression, \ MultiFDCompression) +#define DEFINE_PROP_MIG_MODE(_n, _s, _f, _d) \ + DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_mig_mode, \ + MigMode) #define DEFINE_PROP_LOSTTICKPOLICY(_n, _s, _f, _d) \ DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_losttickpolicy, \ LostTickPolicy) diff --git a/include/migration/misc.h b/include/migration/misc.h index 673ac490fb..1bc8902e6d 100644 --- a/include/migration/misc.h +++ b/include/migration/misc.h @@ -15,6 +15,7 @@ #define MIGRATION_MISC_H #include "qemu/notify.h" +#include "qapi/qapi-types-migration.h" #include "qapi/qapi-types-net.h" /* migration/ram.c */ diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index dfe98da355..a170440991 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -387,6 +387,11 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) monitor_printf(mon, "%s: %" PRIu64 " MB/s\n", MigrationParameter_str(MIGRATION_PARAMETER_VCPU_DIRTY_LIMIT), params->vcpu_dirty_limit); + + assert(params->has_mode); + monitor_printf(mon, "%s: %s\n", + MigrationParameter_str(MIGRATION_PARAMETER_MODE), + qapi_enum_lookup(&MigMode_lookup, params->mode)); } qapi_free_MigrationParameters(params); @@ -661,6 +666,10 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) p->has_vcpu_dirty_limit = true; visit_type_size(v, param, &p->vcpu_dirty_limit, &err); break; + case MIGRATION_PARAMETER_MODE: + p->has_mode = true; + visit_type_MigMode(v, param, &p->mode, &err); + break; default: assert(0); } diff --git a/migration/options.c b/migration/options.c index 9a39826ca5..8d8ec73ad9 100644 --- a/migration/options.c +++ b/migration/options.c @@ -176,6 +176,9 @@ Property migration_properties[] = { DEFINE_PROP_UINT64("vcpu-dirty-limit", MigrationState, parameters.vcpu_dirty_limit, DEFAULT_MIGRATE_VCPU_DIRTY_LIMIT), + DEFINE_PROP_MIG_MODE("mode", MigrationState, + parameters.mode, + MIG_MODE_NORMAL), /* Migration capabilities */ DEFINE_PROP_MIG_CAP("x-xbzrle", MIGRATION_CAPABILITY_XBZRLE), @@ -827,6 +830,13 @@ uint64_t migrate_max_postcopy_bandwidth(void) return s->parameters.max_postcopy_bandwidth; } +MigMode migrate_mode(void) +{ + MigrationState *s = migrate_get_current(); + + return s->parameters.mode; +} + int migrate_multifd_channels(void) { MigrationState *s = migrate_get_current(); @@ -999,6 +1009,8 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) params->x_vcpu_dirty_limit_period = s->parameters.x_vcpu_dirty_limit_period; params->has_vcpu_dirty_limit = true; params->vcpu_dirty_limit = s->parameters.vcpu_dirty_limit; + params->has_mode = true; + params->mode = s->parameters.mode; return params; } @@ -1034,6 +1046,7 @@ void migrate_params_init(MigrationParameters *params) params->has_announce_step = true; params->has_x_vcpu_dirty_limit_period = true; params->has_vcpu_dirty_limit = true; + params->has_mode = true; } /* @@ -1331,6 +1344,10 @@ static void migrate_params_test_apply(MigrateSetParameters *params, if (params->has_vcpu_dirty_limit) { dest->vcpu_dirty_limit = params->vcpu_dirty_limit; } + + if (params->has_mode) { + dest->mode = params->mode; + } } static void migrate_params_apply(MigrateSetParameters *params, Error **errp) @@ -1471,6 +1488,10 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp) if (params->has_vcpu_dirty_limit) { s->parameters.vcpu_dirty_limit = params->vcpu_dirty_limit; } + + if (params->has_mode) { + s->parameters.mode = params->mode; + } } void qmp_migrate_set_parameters(MigrateSetParameters *params, Error **errp) diff --git a/migration/options.h b/migration/options.h index 237f2d6b4a..246c160aee 100644 --- a/migration/options.h +++ b/migration/options.h @@ -83,6 +83,7 @@ uint8_t migrate_max_cpu_throttle(void); uint64_t migrate_max_bandwidth(void); uint64_t migrate_avail_switchover_bandwidth(void); uint64_t migrate_max_postcopy_bandwidth(void); +MigMode migrate_mode(void); int migrate_multifd_channels(void); MultiFDCompression migrate_multifd_compression(void); int migrate_multifd_zlib_level(void); diff --git a/qapi/migration.json b/qapi/migration.json index e6610af428..47c02a9346 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -631,6 +631,15 @@ 'data': [ 'none', 'zlib', { 'name': 'zstd', 'if': 'CONFIG_ZSTD' } ] } +## +# @MigMode: +# +# @normal: the original form of migration. (since 8.2) +# +## +{ 'enum': 'MigMode', + 'data': [ 'normal' ] } + ## # @BitmapMigrationBitmapAliasTransform: # @@ -849,6 +858,9 @@ # @vcpu-dirty-limit: Dirtyrate limit (MB/s) during live migration. # Defaults to 1. (Since 8.1) # +# @mode: Migration mode. See description in @MigMode. Default is 'normal'. +# (Since 8.2) +# # Features: # # @deprecated: Member @block-incremental is deprecated. Use @@ -881,7 +893,8 @@ 'multifd-zlib-level', 'multifd-zstd-level', 'block-bitmap-mapping', { 'name': 'x-vcpu-dirty-limit-period', 'features': ['unstable'] }, - 'vcpu-dirty-limit'] } + 'vcpu-dirty-limit', + 'mode'] } ## # @MigrateSetParameters: @@ -1033,6 +1046,9 @@ # @vcpu-dirty-limit: Dirtyrate limit (MB/s) during live migration. # Defaults to 1. (Since 8.1) # +# @mode: Migration mode. See description in @MigMode. Default is 'normal'. +# (Since 8.2) +# # Features: # # @deprecated: Member @block-incremental is deprecated. Use @@ -1085,7 +1101,8 @@ '*block-bitmap-mapping': [ 'BitmapMigrationNodeAlias' ], '*x-vcpu-dirty-limit-period': { 'type': 'uint64', 'features': [ 'unstable' ] }, - '*vcpu-dirty-limit': 'uint64'} } + '*vcpu-dirty-limit': 'uint64', + '*mode': 'MigMode'} } ## # @migrate-set-parameters: @@ -1257,6 +1274,9 @@ # @vcpu-dirty-limit: Dirtyrate limit (MB/s) during live migration. # Defaults to 1. (Since 8.1) # +# @mode: Migration mode. See description in @MigMode. Default is 'normal'. +# (Since 8.2) +# # Features: # # @deprecated: Member @block-incremental is deprecated. Use @@ -1306,7 +1326,8 @@ '*block-bitmap-mapping': [ 'BitmapMigrationNodeAlias' ], '*x-vcpu-dirty-limit-period': { 'type': 'uint64', 'features': [ 'unstable' ] }, - '*vcpu-dirty-limit': 'uint64'} } + '*vcpu-dirty-limit': 'uint64', + '*mode': 'MigMode'} } ## # @query-migrate-parameters: From fa3673e497a1119fbf2c1f948da71907a84385d9 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 25 Oct 2023 12:44:25 -0700 Subject: [PATCH 318/974] migration: per-mode blockers Extend the blocker interface so that a blocker can be registered for one or more migration modes. The existing interfaces register a blocker for all modes, and the new interfaces take a varargs list of modes. Internally, maintain a separate blocker list per mode. The same Error object may be added to multiple lists. When a block is deleted, it is removed from every list, and the Error is freed. No functional change until a new mode is added. Signed-off-by: Steve Sistare Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-ID: <1698263069-406971-3-git-send-email-steven.sistare@oracle.com> --- include/migration/blocker.h | 44 ++++++++++++++-- migration/migration.c | 101 ++++++++++++++++++++++++++++++------ stubs/migr-blocker.c | 10 ++++ 3 files changed, 135 insertions(+), 20 deletions(-) diff --git a/include/migration/blocker.h b/include/migration/blocker.h index b048f301b4..a687ac0efe 100644 --- a/include/migration/blocker.h +++ b/include/migration/blocker.h @@ -14,8 +14,12 @@ #ifndef MIGRATION_BLOCKER_H #define MIGRATION_BLOCKER_H +#include "qapi/qapi-types-migration.h" + +#define MIG_MODE_ALL MIG_MODE__MAX + /** - * @migrate_add_blocker - prevent migration from proceeding + * @migrate_add_blocker - prevent all modes of migration from proceeding * * @reasonp - address of an error to be returned whenever migration is attempted * @@ -30,8 +34,8 @@ int migrate_add_blocker(Error **reasonp, Error **errp); /** - * @migrate_add_blocker_internal - prevent migration from proceeding without - * only-migrate implications + * @migrate_add_blocker_internal - prevent all modes of migration from + * proceeding, but ignore -only-migratable * * @reasonp - address of an error to be returned whenever migration is attempted * @@ -50,7 +54,7 @@ int migrate_add_blocker(Error **reasonp, Error **errp); int migrate_add_blocker_internal(Error **reasonp, Error **errp); /** - * @migrate_del_blocker - remove a blocking error from migration and free it. + * @migrate_del_blocker - remove a migration blocker from all modes and free it. * * @reasonp - address of the error blocking migration * @@ -58,4 +62,36 @@ int migrate_add_blocker_internal(Error **reasonp, Error **errp); */ void migrate_del_blocker(Error **reasonp); +/** + * @migrate_add_blocker_normal - prevent normal migration mode from proceeding + * + * @reasonp - address of an error to be returned whenever migration is attempted + * + * @errp - [out] The reason (if any) we cannot block migration right now. + * + * @returns - 0 on success, -EBUSY/-EACCES on failure, with errp set. + * + * *@reasonp is freed and set to NULL if failure is returned. + * On success, the caller must not free @reasonp, except by + * calling migrate_del_blocker. + */ +int migrate_add_blocker_normal(Error **reasonp, Error **errp); + +/** + * @migrate_add_blocker_modes - prevent some modes of migration from proceeding + * + * @reasonp - address of an error to be returned whenever migration is attempted + * + * @errp - [out] The reason (if any) we cannot block migration right now. + * + * @mode - one or more migration modes to be blocked. The list is terminated + * by -1 or MIG_MODE_ALL. For the latter, all modes are blocked. + * + * @returns - 0 on success, -EBUSY/-EACCES on failure, with errp set. + * + * *@reasonp is freed and set to NULL if failure is returned. + * On success, the caller must not free *@reasonp before the blocker is removed. + */ +int migrate_add_blocker_modes(Error **reasonp, Error **errp, MigMode mode, ...); + #endif diff --git a/migration/migration.c b/migration/migration.c index c334b9effd..11c1490090 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -92,7 +92,7 @@ enum mig_rp_message_type { static MigrationState *current_migration; static MigrationIncomingState *current_incoming; -static GSList *migration_blockers; +static GSList *migration_blockers[MIG_MODE__MAX]; static bool migration_object_check(MigrationState *ms, Error **errp); static int migration_maybe_pause(MigrationState *s, @@ -1043,7 +1043,7 @@ static void fill_source_migration_info(MigrationInfo *info) { MigrationState *s = migrate_get_current(); int state = qatomic_read(&s->state); - GSList *cur_blocker = migration_blockers; + GSList *cur_blocker = migration_blockers[migrate_mode()]; info->blocked_reasons = NULL; @@ -1507,38 +1507,105 @@ int migrate_init(MigrationState *s, Error **errp) return 0; } -int migrate_add_blocker_internal(Error **reasonp, Error **errp) +static bool is_busy(Error **reasonp, Error **errp) { + ERRP_GUARD(); + /* Snapshots are similar to migrations, so check RUN_STATE_SAVE_VM too. */ if (runstate_check(RUN_STATE_SAVE_VM) || !migration_is_idle()) { error_propagate_prepend(errp, *reasonp, "disallowing migration blocker " "(migration/snapshot in progress) for: "); *reasonp = NULL; - return -EBUSY; + return true; } + return false; +} - migration_blockers = g_slist_prepend(migration_blockers, *reasonp); +static bool is_only_migratable(Error **reasonp, Error **errp, int modes) +{ + ERRP_GUARD(); + + if (only_migratable && (modes & BIT(MIG_MODE_NORMAL))) { + error_propagate_prepend(errp, *reasonp, + "disallowing migration blocker " + "(--only-migratable) for: "); + *reasonp = NULL; + return true; + } + return false; +} + +static int get_modes(MigMode mode, va_list ap) +{ + int modes = 0; + + while (mode != -1 && mode != MIG_MODE_ALL) { + assert(mode >= MIG_MODE_NORMAL && mode < MIG_MODE__MAX); + modes |= BIT(mode); + mode = va_arg(ap, MigMode); + } + if (mode == MIG_MODE_ALL) { + modes = BIT(MIG_MODE__MAX) - 1; + } + return modes; +} + +static int add_blockers(Error **reasonp, Error **errp, int modes) +{ + for (MigMode mode = 0; mode < MIG_MODE__MAX; mode++) { + if (modes & BIT(mode)) { + migration_blockers[mode] = g_slist_prepend(migration_blockers[mode], + *reasonp); + } + } return 0; } int migrate_add_blocker(Error **reasonp, Error **errp) { - if (only_migratable) { - error_propagate_prepend(errp, *reasonp, - "disallowing migration blocker " - "(--only-migratable) for: "); - *reasonp = NULL; - return -EACCES; - } + return migrate_add_blocker_modes(reasonp, errp, MIG_MODE_ALL); +} - return migrate_add_blocker_internal(reasonp, errp); +int migrate_add_blocker_normal(Error **reasonp, Error **errp) +{ + return migrate_add_blocker_modes(reasonp, errp, MIG_MODE_NORMAL, -1); +} + +int migrate_add_blocker_modes(Error **reasonp, Error **errp, MigMode mode, ...) +{ + int modes; + va_list ap; + + va_start(ap, mode); + modes = get_modes(mode, ap); + va_end(ap); + + if (is_only_migratable(reasonp, errp, modes)) { + return -EACCES; + } else if (is_busy(reasonp, errp)) { + return -EBUSY; + } + return add_blockers(reasonp, errp, modes); +} + +int migrate_add_blocker_internal(Error **reasonp, Error **errp) +{ + int modes = BIT(MIG_MODE__MAX) - 1; + + if (is_busy(reasonp, errp)) { + return -EBUSY; + } + return add_blockers(reasonp, errp, modes); } void migrate_del_blocker(Error **reasonp) { if (*reasonp) { - migration_blockers = g_slist_remove(migration_blockers, *reasonp); + for (MigMode mode = 0; mode < MIG_MODE__MAX; mode++) { + migration_blockers[mode] = g_slist_remove(migration_blockers[mode], + *reasonp); + } error_free(*reasonp); *reasonp = NULL; } @@ -1634,12 +1701,14 @@ void qmp_migrate_pause(Error **errp) bool migration_is_blocked(Error **errp) { + GSList *blockers = migration_blockers[migrate_mode()]; + if (qemu_savevm_state_blocked(errp)) { return true; } - if (migration_blockers) { - error_propagate(errp, error_copy(migration_blockers->data)); + if (blockers) { + error_propagate(errp, error_copy(blockers->data)); return true; } diff --git a/stubs/migr-blocker.c b/stubs/migr-blocker.c index 17a5dbf87b..11cbff268f 100644 --- a/stubs/migr-blocker.c +++ b/stubs/migr-blocker.c @@ -6,6 +6,16 @@ int migrate_add_blocker(Error **reasonp, Error **errp) return 0; } +int migrate_add_blocker_normal(Error **reasonp, Error **errp) +{ + return 0; +} + +int migrate_add_blocker_modes(Error **reasonp, Error **errp, MigMode mode, ...) +{ + return 0; +} + void migrate_del_blocker(Error **reasonp) { } From e0ee3a8ff74a47ae8ca147d8192e2c0745dfb51d Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 25 Oct 2023 12:44:26 -0700 Subject: [PATCH 319/974] cpr: relax blockdev migration blockers Some blockdevs block migration because they do not support sharing across hosts and/or do not support dirty bitmaps. These prohibitions do not apply if the old and new qemu processes do not run concurrently, and if new qemu starts on the same host as old, which is the case for cpr. Narrow the scope of these blockers so they only apply to normal mode. They will not block cpr modes when they are added in subsequent patches. No functional change until a new mode is added. Signed-off-by: Steve Sistare Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-ID: <1698263069-406971-4-git-send-email-steven.sistare@oracle.com> --- block/parallels.c | 2 +- block/qcow.c | 2 +- block/vdi.c | 2 +- block/vhdx.c | 2 +- block/vmdk.c | 2 +- block/vpc.c | 2 +- block/vvfat.c | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/block/parallels.c b/block/parallels.c index 1d695ce7fb..6318dd02e7 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -1369,7 +1369,7 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, bdrv_get_device_or_node_name(bs)); bdrv_graph_rdunlock_main_loop(); - ret = migrate_add_blocker(&s->migration_blocker, errp); + ret = migrate_add_blocker_normal(&s->migration_blocker, errp); if (ret < 0) { goto fail; } diff --git a/block/qcow.c b/block/qcow.c index fdd4c83948..eab68e387c 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -307,7 +307,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, bdrv_get_device_or_node_name(bs)); bdrv_graph_rdunlock_main_loop(); - ret = migrate_add_blocker(&s->migration_blocker, errp); + ret = migrate_add_blocker_normal(&s->migration_blocker, errp); if (ret < 0) { goto fail; } diff --git a/block/vdi.c b/block/vdi.c index fd7e365383..c647d72895 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -498,7 +498,7 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, bdrv_get_device_or_node_name(bs)); bdrv_graph_rdunlock_main_loop(); - ret = migrate_add_blocker(&s->migration_blocker, errp); + ret = migrate_add_blocker_normal(&s->migration_blocker, errp); if (ret < 0) { goto fail_free_bmap; } diff --git a/block/vhdx.c b/block/vhdx.c index e37f8c0926..a9d08742f9 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -1096,7 +1096,7 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags, error_setg(&s->migration_blocker, "The vhdx format used by node '%s' " "does not support live migration", bdrv_get_device_or_node_name(bs)); - ret = migrate_add_blocker(&s->migration_blocker, errp); + ret = migrate_add_blocker_normal(&s->migration_blocker, errp); if (ret < 0) { goto fail; } diff --git a/block/vmdk.c b/block/vmdk.c index 1335d39e16..85864b8045 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -1386,7 +1386,7 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags, error_setg(&s->migration_blocker, "The vmdk format used by node '%s' " "does not support live migration", bdrv_get_device_or_node_name(bs)); - ret = migrate_add_blocker(&s->migration_blocker, errp); + ret = migrate_add_blocker_normal(&s->migration_blocker, errp); if (ret < 0) { goto fail; } diff --git a/block/vpc.c b/block/vpc.c index c30cf8689a..aa1a48ae0e 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -452,7 +452,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, bdrv_get_device_or_node_name(bs)); bdrv_graph_rdunlock_main_loop(); - ret = migrate_add_blocker(&s->migration_blocker, errp); + ret = migrate_add_blocker_normal(&s->migration_blocker, errp); if (ret < 0) { goto fail; } diff --git a/block/vvfat.c b/block/vvfat.c index 266e036dcd..9d050ba3ae 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -1268,7 +1268,7 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags, "The vvfat (rw) format used by node '%s' " "does not support live migration", bdrv_get_device_or_node_name(bs)); - ret = migrate_add_blocker(&s->migration_blocker, errp); + ret = migrate_add_blocker_normal(&s->migration_blocker, errp); if (ret < 0) { goto fail; } From 89415796f688036faa88e6dc9ae2f3acfcdc39c8 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 25 Oct 2023 12:44:27 -0700 Subject: [PATCH 320/974] cpr: relax vhost migration blockers vhost blocks migration if logging is not supported to track dirty memory, and vhost-user blocks it if the log cannot be saved to a shm fd. vhost-vdpa blocks migration if both hosts do not support all the device's features using a shadow VQ, for tracking requests and dirty memory. vhost-scsi blocks migration if storage cannot be shared across hosts, or if state cannot be migrated. None of these conditions apply if the old and new qemu processes do not run concurrently, and if new qemu starts on the same host as old, which is the case for cpr. Narrow the scope of these blockers so they only apply to normal mode. They will not block cpr modes when they are added in subsequent patches. No functional change until a new mode is added. Signed-off-by: Steve Sistare Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-ID: <1698263069-406971-5-git-send-email-steven.sistare@oracle.com> --- hw/scsi/vhost-scsi.c | 2 +- hw/virtio/vhost.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c index 5d9e06a9bb..3126df9e1d 100644 --- a/hw/scsi/vhost-scsi.c +++ b/hw/scsi/vhost-scsi.c @@ -210,7 +210,7 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp) "When external environment supports it (Orchestrator migrates " "target SCSI device state or use shared storage over network), " "set 'migratable' property to true to enable migration."); - if (migrate_add_blocker(&vsc->migration_blocker, errp) < 0) { + if (migrate_add_blocker_normal(&vsc->migration_blocker, errp) < 0) { goto free_virtio; } } diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index aa7b272452..9c9ae7109e 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -1527,7 +1527,7 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, } if (hdev->migration_blocker != NULL) { - r = migrate_add_blocker(&hdev->migration_blocker, errp); + r = migrate_add_blocker_normal(&hdev->migration_blocker, errp); if (r < 0) { goto fail_busyloop; } From a87e64519b027c9b36d9badbd73b13f6475dfb3d Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 25 Oct 2023 12:44:28 -0700 Subject: [PATCH 321/974] cpr: reboot mode Add the cpr-reboot migration mode. Usage: $ qemu-system-$arch -monitor stdio ... QEMU 8.1.50 monitor - type 'help' for more information (qemu) migrate_set_capability x-ignore-shared on (qemu) migrate_set_parameter mode cpr-reboot (qemu) migrate -d file:vm.state (qemu) info status VM status: paused (postmigrate) (qemu) quit $ qemu-system-$arch -monitor stdio -incoming defer ... QEMU 8.1.50 monitor - type 'help' for more information (qemu) migrate_set_capability x-ignore-shared on (qemu) migrate_set_parameter mode cpr-reboot (qemu) migrate_incoming file:vm.state (qemu) info status VM status: running In this mode, the migrate command saves state to a file, allowing one to quit qemu, reboot to an updated kernel, and restart an updated version of qemu. The caller must specify a migration URI that writes to and reads from a file. Unlike normal mode, the use of certain local storage options does not block the migration, but the caller must not modify guest block devices between the quit and restart. To avoid saving guest RAM to the file, the memory backend must be shared, and the @x-ignore-shared migration capability must be set. Guest RAM must be non-volatile across reboot, such as by backing it with a dax device, but this is not enforced. The restarted qemu arguments must match those used to initially start qemu, plus the -incoming option. Signed-off-by: Steve Sistare Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-ID: <1698263069-406971-6-git-send-email-steven.sistare@oracle.com> --- hw/core/qdev-properties-system.c | 2 +- qapi/migration.json | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index b9179e8917..2f1dbb3fd7 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -680,7 +680,7 @@ QEMU_BUILD_BUG_ON(sizeof(MigMode) != sizeof(int)); const PropertyInfo qdev_prop_mig_mode = { .name = "MigMode", .description = "mig_mode values, " - "normal", + "normal,cpr-reboot", .enum_table = &MigMode_lookup, .get = qdev_propinfo_get_enum, .set = qdev_propinfo_set_enum, diff --git a/qapi/migration.json b/qapi/migration.json index 47c02a9346..3daeffc95d 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -636,9 +636,22 @@ # # @normal: the original form of migration. (since 8.2) # +# @cpr-reboot: The migrate command saves state to a file, allowing one to +# quit qemu, reboot to an updated kernel, and restart an updated +# version of qemu. The caller must specify a migration URI +# that writes to and reads from a file. Unlike normal mode, +# the use of certain local storage options does not block the +# migration, but the caller must not modify guest block devices +# between the quit and restart. To avoid saving guest RAM to the +# file, the memory backend must be shared, and the @x-ignore-shared +# migration capability must be set. Guest RAM must be non-volatile +# across reboot, such as by backing it with a dax device, but this +# is not enforced. The restarted qemu arguments must match those +# used to initially start qemu, plus the -incoming option. +# (since 8.2) ## { 'enum': 'MigMode', - 'data': [ 'normal' ] } + 'data': [ 'normal', 'cpr-reboot' ] } ## # @BitmapMigrationBitmapAliasTransform: From 87f4ba9ed3cfeab440c63169006e1bc19d031d21 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Sat, 30 Sep 2023 15:23:47 +0200 Subject: [PATCH 322/974] hw/m68k/next-cube: Mirror BIOS to address 0 The ROM is also available at address 0, so add a proper mirror for this address. Acked-by: Mark Cave-Ayland Message-ID: <20230930132351.30282-2-huth@tuxfamily.org> Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index d17e6be8e1..2c94c6a9c2 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -945,6 +945,7 @@ static void next_cube_init(MachineState *machine) M68kCPU *cpu; CPUM68KState *env; MemoryRegion *rom = g_new(MemoryRegion, 1); + MemoryRegion *rom2 = g_new(MemoryRegion, 1); MemoryRegion *dmamem = g_new(MemoryRegion, 1); MemoryRegion *bmapm1 = g_new(MemoryRegion, 1); MemoryRegion *bmapm2 = g_new(MemoryRegion, 1); @@ -993,9 +994,10 @@ static void next_cube_init(MachineState *machine) sysbus_create_simple(TYPE_NEXTKBD, 0x0200e000, NULL); /* Load ROM here */ - /* still not sure if the rom should also be mapped at 0x0*/ memory_region_init_rom(rom, NULL, "next.rom", 0x20000, &error_fatal); memory_region_add_subregion(sysmem, 0x01000000, rom); + memory_region_init_alias(rom2, NULL, "next.rom2", rom, 0x0, 0x20000); + memory_region_add_subregion(sysmem, 0x0, rom2); if (load_image_targphys(bios_name, 0x01000000, 0x20000) < 8) { if (!qtest_enabled()) { error_report("Failed to load firmware '%s'.", bios_name); From f2a80c6ede47c93771f6a9f6cd82e4c0a1f54f86 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Sat, 30 Sep 2023 15:23:49 +0200 Subject: [PATCH 323/974] m68k: Instantiate the ESP SCSI controller for the NeXTcube machine The NeXTcube uses a NCR 53C90 SCSI interface for its disks, so we should be able to use the ESP controller from QEMU here. The code here has been basically taken from Bryce Lanham's GSoC 2011 contribution, except for the next_scsi_init() function which has been rewritte as a replacement for the esp_init() function (that has been removed quite a while ago). Note that SCSI is not working yet. The ESP code likely needs some more fixes first and there still might be some bugs left in they way we wire it up for the NeXT-Cube machine. Signed-off-by: Mark Cave-Ayland Message-ID: <20230930132351.30282-4-huth@tuxfamily.org> Signed-off-by: Thomas Huth --- hw/m68k/next-cube.c | 117 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 110 insertions(+), 7 deletions(-) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 2c94c6a9c2..fabd861941 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -90,10 +90,13 @@ struct NeXTPC { uint32_t scr1; uint32_t scr2; - uint8_t scsi_csr_1; - uint8_t scsi_csr_2; uint32_t int_mask; uint32_t int_status; + uint8_t scsi_csr_1; + uint8_t scsi_csr_2; + + qemu_irq scsi_reset; + qemu_irq scsi_dma; NextRtc rtc; }; @@ -466,7 +469,7 @@ static void scr_writeb(NeXTPC *s, hwaddr addr, uint32_t value) DPRINTF("SCSICSR FIFO Flush\n"); /* will have to add another irq to the esp if this is needed */ /* esp_puflush_fifo(esp_g); */ - /* qemu_irq_pulse(s->scsi_dma); */ + qemu_irq_pulse(s->scsi_dma); } if (value & SCSICSR_ENABLE) { @@ -486,9 +489,9 @@ static void scr_writeb(NeXTPC *s, hwaddr addr, uint32_t value) if (value & SCSICSR_RESET) { DPRINTF("SCSICSR Reset\n"); /* I think this should set DMADIR. CPUDMA and INTMASK to 0 */ - /* qemu_irq_raise(s->scsi_reset); */ - /* s->scsi_csr_1 &= ~(SCSICSR_INTMASK |0x80|0x1); */ - + qemu_irq_raise(s->scsi_reset); + s->scsi_csr_1 &= ~(SCSICSR_INTMASK | 0x80 | 0x1); + qemu_irq_lower(s->scsi_reset); } if (value & SCSICSR_DMADIR) { DPRINTF("SCSICSR DMAdir\n"); @@ -496,10 +499,11 @@ static void scr_writeb(NeXTPC *s, hwaddr addr, uint32_t value) if (value & SCSICSR_CPUDMA) { DPRINTF("SCSICSR CPUDMA\n"); /* qemu_irq_raise(s->scsi_dma); */ - s->int_status |= 0x4000000; } else { + /* fprintf(stderr,"SCSICSR CPUDMA disabled\n"); */ s->int_status &= ~(0x4000000); + /* qemu_irq_lower(s->scsi_dma); */ } if (value & SCSICSR_INTMASK) { DPRINTF("SCSICSR INTMASK\n"); @@ -828,6 +832,103 @@ static void next_irq(void *opaque, int number, int level) } } +static void nextdma_write(void *opaque, uint8_t *buf, int size, int type) +{ + uint32_t base_addr; + int irq = 0; + uint8_t align = 16; + NeXTState *next_state = NEXT_MACHINE(qdev_get_machine()); + + if (type == NEXTDMA_ENRX || type == NEXTDMA_ENTX) { + align = 32; + } + /* Most DMA is supposedly 16 byte aligned */ + if ((size % align) != 0) { + size -= size % align; + size += align; + } + + /* + * prom sets the dma start using initbuf while the bootloader uses next + * so we check to see if initbuf is 0 + */ + if (next_state->dma[type].next_initbuf == 0) { + base_addr = next_state->dma[type].next; + } else { + base_addr = next_state->dma[type].next_initbuf; + } + + cpu_physical_memory_write(base_addr, buf, size); + + next_state->dma[type].next_initbuf = 0; + + /* saved limit is checked to calculate packet size by both, rom and netbsd */ + next_state->dma[type].saved_limit = (next_state->dma[type].next + size); + next_state->dma[type].saved_next = (next_state->dma[type].next); + + /* + * 32 bytes under savedbase seems to be some kind of register + * of which the purpose is unknown as of yet + */ + /* stl_phys(s->rx_dma.base-32,0xFFFFFFFF); */ + + if (!(next_state->dma[type].csr & DMA_SUPDATE)) { + next_state->dma[type].next = next_state->dma[type].start; + next_state->dma[type].limit = next_state->dma[type].stop; + } + + /* Set dma registers and raise an irq */ + next_state->dma[type].csr |= DMA_COMPLETE; /* DON'T CHANGE THIS! */ + + switch (type) { + case NEXTDMA_SCSI: + irq = NEXT_SCSI_DMA_I; + break; + } + + next_irq(opaque, irq, 1); + next_irq(opaque, irq, 0); +} + +static void nextscsi_read(void *opaque, uint8_t *buf, int len) +{ + DPRINTF("SCSI READ: %x\n", len); + abort(); +} + +static void nextscsi_write(void *opaque, uint8_t *buf, int size) +{ + DPRINTF("SCSI WRITE: %i\n", size); + nextdma_write(opaque, buf, size, NEXTDMA_SCSI); +} + +static void next_scsi_init(DeviceState *pcdev, M68kCPU *cpu) +{ + struct NeXTPC *next_pc = NEXT_PC(pcdev); + DeviceState *dev; + SysBusDevice *sysbusdev; + SysBusESPState *sysbus_esp; + ESPState *esp; + + dev = qdev_new(TYPE_SYSBUS_ESP); + sysbus_esp = SYSBUS_ESP(dev); + esp = &sysbus_esp->esp; + esp->dma_memory_read = nextscsi_read; + esp->dma_memory_write = nextscsi_write; + esp->dma_opaque = pcdev; + sysbus_esp->it_shift = 0; + esp->dma_enabled = 1; + sysbusdev = SYS_BUS_DEVICE(dev); + sysbus_realize_and_unref(sysbusdev, &error_fatal); + sysbus_connect_irq(sysbusdev, 0, qdev_get_gpio_in(pcdev, NEXT_SCSI_I)); + sysbus_mmio_map(sysbusdev, 0, 0x2114000); + + next_pc->scsi_reset = qdev_get_gpio_in(dev, 0); + next_pc->scsi_dma = qdev_get_gpio_in(dev, 1); + + scsi_bus_legacy_handle_cmdline(&esp->bus); +} + static void next_escc_init(DeviceState *pcdev) { DeviceState *dev; @@ -1021,6 +1122,7 @@ static void next_cube_init(MachineState *machine) /* TODO: */ /* Network */ /* SCSI */ + next_scsi_init(pcdev, cpu); /* DMA */ memory_region_init_io(dmamem, NULL, &dma_ops, machine, "next.dma", 0x5000); @@ -1033,6 +1135,7 @@ static void next_machine_class_init(ObjectClass *oc, void *data) mc->desc = "NeXT Cube"; mc->init = next_cube_init; + mc->block_default_type = IF_SCSI; mc->default_ram_size = RAM_SIZE; mc->default_ram_id = "next.ram"; mc->default_cpu_type = M68K_CPU_TYPE_NAME("m68040"); From a35c20961bb776ed8a4a7377f946b0f3149d3fe7 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 1 Nov 2023 21:19:34 +0100 Subject: [PATCH 324/974] tests/avocado/machine_m68k_nextcube: Fix the download URL for the ROM image If Avocado has to fetch this asset, the download fails with a 403 HTTP error. Use a different URL to fix the issue. Message-ID: <20231101201934.27637-1-huth@tuxfamily.org> Signed-off-by: Thomas Huth --- tests/avocado/machine_m68k_nextcube.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/avocado/machine_m68k_nextcube.py b/tests/avocado/machine_m68k_nextcube.py index d6da2fbb01..f1205d7fc0 100644 --- a/tests/avocado/machine_m68k_nextcube.py +++ b/tests/avocado/machine_m68k_nextcube.py @@ -30,8 +30,8 @@ class NextCubeMachine(QemuSystemTest): timeout = 15 def check_bootrom_framebuffer(self, screenshot_path): - rom_url = ('http://www.nextcomputers.org/NeXTfiles/Software/ROM_Files/' - '68040_Non-Turbo_Chipset/Rev_2.5_v66.BIN') + rom_url = ('https://sourceforge.net/p/previous/code/1350/tree/' + 'trunk/src/Rev_2.5_v66.BIN?format=raw') rom_hash = 'b3534796abae238a0111299fc406a9349f7fee24' rom_path = self.fetch_asset(rom_url, asset_hash=rom_hash) From e7b428d6bc06a0dfffa7e47fb3b3ee47f1c93499 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 25 Oct 2023 12:44:29 -0700 Subject: [PATCH 325/974] tests/qtest: migration: add reboot mode test [ Maintainer note: I put the test as flaky because our CI has problems with shared memory. We will remove the flaky bits as soon as we get a solution. ] Signed-off-by: Steve Sistare Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-ID: <1698263069-406971-7-git-send-email-steven.sistare@oracle.com> --- tests/qtest/migration-test.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index bc70a14642..b7ebc23903 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -2026,6 +2026,31 @@ static void test_precopy_file_offset_bad(void) test_file_common(&args, false); } +static void *test_mode_reboot_start(QTestState *from, QTestState *to) +{ + migrate_set_parameter_str(from, "mode", "cpr-reboot"); + migrate_set_parameter_str(to, "mode", "cpr-reboot"); + + migrate_set_capability(from, "x-ignore-shared", true); + migrate_set_capability(to, "x-ignore-shared", true); + + return NULL; +} + +static void test_mode_reboot(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, + FILE_TEST_FILENAME); + MigrateCommon args = { + .start.use_shmem = true, + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = test_mode_reboot_start + }; + + test_file_common(&args, true); +} + static void test_precopy_tcp_plain(void) { MigrateCommon args = { @@ -3096,6 +3121,14 @@ int main(int argc, char **argv) qtest_add_func("/migration/precopy/file/offset/bad", test_precopy_file_offset_bad); + /* + * Our CI system has problems with shared memory. + * Don't run this test until we find a workaround. + */ + if (getenv("QEMU_TEST_FLAKY_TESTS")) { + qtest_add_func("/migration/mode/reboot", test_mode_reboot); + } + #ifdef CONFIG_GNUTLS qtest_add_func("/migration/precopy/unix/tls/psk", test_precopy_unix_tls_psk); From 7aa6070d09c5a6c83490599a3c564c64d7e2520a Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 17 Oct 2023 16:26:29 -0400 Subject: [PATCH 326/974] migration: Refactor error handling in source return path rp_state.error was a boolean used to show error happened in return path thread. That's not only duplicating error reporting (migrate_set_error), but also not good enough in that we only do error_report() and set it to true, we never can keep a history of the exact error and show it in query-migrate. To make this better, a few things done: - Use error_setg() rather than error_report() across the whole lifecycle of return path thread, keeping the error in an Error*. - With above, no need to have mark_source_rp_bad(), remove it, alongside with rp_state.error itself. - Use migrate_set_error() to apply that captured error to the global migration object when error occured in this thread. - Do the same when detected qemufile error in source return path We need to re-export qemu_file_get_error_obj() to do the last one. Signed-off-by: Peter Xu Reviewed-by: Fabiano Rosas Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-ID: <20231017202633.296756-2-peterx@redhat.com> --- migration/migration.c | 121 ++++++++++++++++++----------------------- migration/migration.h | 1 - migration/qemu-file.c | 2 +- migration/qemu-file.h | 1 + migration/ram.c | 41 +++++++------- migration/ram.h | 5 +- migration/trace-events | 4 +- 7 files changed, 80 insertions(+), 95 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 11c1490090..455ddc896a 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -99,7 +99,7 @@ static int migration_maybe_pause(MigrationState *s, int *current_active_state, int new_state); static void migrate_fd_cancel(MigrationState *s); -static int close_return_path_on_source(MigrationState *s); +static bool close_return_path_on_source(MigrationState *s); static void migration_downtime_start(MigrationState *s) { @@ -1475,7 +1475,6 @@ int migrate_init(MigrationState *s, Error **errp) s->to_dst_file = NULL; s->state = MIGRATION_STATUS_NONE; s->rp_state.from_dst_file = NULL; - s->rp_state.error = false; s->mbps = 0.0; s->pages_per_second = 0.0; s->downtime = 0; @@ -1883,16 +1882,6 @@ void qmp_migrate_continue(MigrationStatus state, Error **errp) qemu_sem_post(&s->pause_sem); } -/* migration thread support */ -/* - * Something bad happened to the RP stream, mark an error - * The caller shall print or trace something to indicate why - */ -static void mark_source_rp_bad(MigrationState *s) -{ - s->rp_state.error = true; -} - void migration_rp_wait(MigrationState *s) { qemu_sem_wait(&s->rp_state.rp_sem); @@ -1923,8 +1912,9 @@ static struct rp_cmd_args { * We're allowed to send more than requested (e.g. to round to our page size) * and we don't need to send pages that have already been sent. */ -static void migrate_handle_rp_req_pages(MigrationState *ms, const char* rbname, - ram_addr_t start, size_t len) +static void +migrate_handle_rp_req_pages(MigrationState *ms, const char* rbname, + ram_addr_t start, size_t len, Error **errp) { long our_host_ps = qemu_real_host_page_size(); @@ -1936,37 +1926,36 @@ static void migrate_handle_rp_req_pages(MigrationState *ms, const char* rbname, */ if (!QEMU_IS_ALIGNED(start, our_host_ps) || !QEMU_IS_ALIGNED(len, our_host_ps)) { - error_report("%s: Misaligned page request, start: " RAM_ADDR_FMT - " len: %zd", __func__, start, len); - mark_source_rp_bad(ms); + error_setg(errp, "MIG_RP_MSG_REQ_PAGES: Misaligned page request, start:" + RAM_ADDR_FMT " len: %zd", start, len); return; } - if (ram_save_queue_pages(rbname, start, len)) { - mark_source_rp_bad(ms); - } + ram_save_queue_pages(rbname, start, len, errp); } -static int migrate_handle_rp_recv_bitmap(MigrationState *s, char *block_name) +static int migrate_handle_rp_recv_bitmap(MigrationState *s, char *block_name, + Error **errp) { RAMBlock *block = qemu_ram_block_by_name(block_name); if (!block) { - error_report("%s: invalid block name '%s'", __func__, block_name); + error_setg(errp, "MIG_RP_MSG_RECV_BITMAP has invalid block name '%s'", + block_name); return -EINVAL; } /* Fetch the received bitmap and refresh the dirty bitmap */ - return ram_dirty_bitmap_reload(s, block); + return ram_dirty_bitmap_reload(s, block, errp); } -static int migrate_handle_rp_resume_ack(MigrationState *s, uint32_t value) +static int migrate_handle_rp_resume_ack(MigrationState *s, + uint32_t value, Error **errp) { trace_source_return_path_thread_resume_ack(value); if (value != MIGRATION_RESUME_ACK_VALUE) { - error_report("%s: illegal resume_ack value %"PRIu32, - __func__, value); + error_setg(errp, "illegal resume_ack value %"PRIu32, value); return -1; } @@ -2025,48 +2014,46 @@ static void *source_return_path_thread(void *opaque) uint32_t tmp32, sibling_error; ram_addr_t start = 0; /* =0 to silence warning */ size_t len = 0, expected_len; + Error *err = NULL; int res; trace_source_return_path_thread_entry(); rcu_register_thread(); - while (!ms->rp_state.error && !qemu_file_get_error(rp) && - migration_is_setup_or_active(ms->state)) { + while (migration_is_setup_or_active(ms->state)) { trace_source_return_path_thread_loop_top(); + header_type = qemu_get_be16(rp); header_len = qemu_get_be16(rp); if (qemu_file_get_error(rp)) { - mark_source_rp_bad(ms); + qemu_file_get_error_obj(rp, &err); goto out; } if (header_type >= MIG_RP_MSG_MAX || header_type == MIG_RP_MSG_INVALID) { - error_report("RP: Received invalid message 0x%04x length 0x%04x", - header_type, header_len); - mark_source_rp_bad(ms); + error_setg(&err, "Received invalid message 0x%04x length 0x%04x", + header_type, header_len); goto out; } if ((rp_cmd_args[header_type].len != -1 && header_len != rp_cmd_args[header_type].len) || header_len > sizeof(buf)) { - error_report("RP: Received '%s' message (0x%04x) with" - "incorrect length %d expecting %zu", - rp_cmd_args[header_type].name, header_type, header_len, - (size_t)rp_cmd_args[header_type].len); - mark_source_rp_bad(ms); + error_setg(&err, "Received '%s' message (0x%04x) with" + "incorrect length %d expecting %zu", + rp_cmd_args[header_type].name, header_type, header_len, + (size_t)rp_cmd_args[header_type].len); goto out; } /* We know we've got a valid header by this point */ res = qemu_get_buffer(rp, buf, header_len); if (res != header_len) { - error_report("RP: Failed reading data for message 0x%04x" - " read %d expected %d", - header_type, res, header_len); - mark_source_rp_bad(ms); + error_setg(&err, "Failed reading data for message 0x%04x" + " read %d expected %d", + header_type, res, header_len); goto out; } @@ -2076,8 +2063,7 @@ static void *source_return_path_thread(void *opaque) sibling_error = ldl_be_p(buf); trace_source_return_path_thread_shut(sibling_error); if (sibling_error) { - error_report("RP: Sibling indicated error %d", sibling_error); - mark_source_rp_bad(ms); + error_setg(&err, "Sibling indicated error %d", sibling_error); } /* * We'll let the main thread deal with closing the RP @@ -2095,7 +2081,10 @@ static void *source_return_path_thread(void *opaque) case MIG_RP_MSG_REQ_PAGES: start = ldq_be_p(buf); len = ldl_be_p(buf + 8); - migrate_handle_rp_req_pages(ms, NULL, start, len); + migrate_handle_rp_req_pages(ms, NULL, start, len, &err); + if (err) { + goto out; + } break; case MIG_RP_MSG_REQ_PAGES_ID: @@ -2110,32 +2099,32 @@ static void *source_return_path_thread(void *opaque) expected_len += tmp32; } if (header_len != expected_len) { - error_report("RP: Req_Page_id with length %d expecting %zd", - header_len, expected_len); - mark_source_rp_bad(ms); + error_setg(&err, "Req_Page_id with length %d expecting %zd", + header_len, expected_len); + goto out; + } + migrate_handle_rp_req_pages(ms, (char *)&buf[13], start, len, + &err); + if (err) { goto out; } - migrate_handle_rp_req_pages(ms, (char *)&buf[13], start, len); break; case MIG_RP_MSG_RECV_BITMAP: if (header_len < 1) { - error_report("%s: missing block name", __func__); - mark_source_rp_bad(ms); + error_setg(&err, "MIG_RP_MSG_RECV_BITMAP missing block name"); goto out; } /* Format: len (1B) + idstr (<255B). This ends the idstr. */ buf[buf[0] + 1] = '\0'; - if (migrate_handle_rp_recv_bitmap(ms, (char *)(buf + 1))) { - mark_source_rp_bad(ms); + if (migrate_handle_rp_recv_bitmap(ms, (char *)(buf + 1), &err)) { goto out; } break; case MIG_RP_MSG_RESUME_ACK: tmp32 = ldl_be_p(buf); - if (migrate_handle_rp_resume_ack(ms, tmp32)) { - mark_source_rp_bad(ms); + if (migrate_handle_rp_resume_ack(ms, tmp32, &err)) { goto out; } break; @@ -2151,13 +2140,15 @@ static void *source_return_path_thread(void *opaque) } out: - if (qemu_file_get_error(rp)) { + if (err) { + migrate_set_error(ms, err); + error_free(err); trace_source_return_path_thread_bad_end(); - mark_source_rp_bad(ms); } trace_source_return_path_thread_end(); rcu_unregister_thread(); + return NULL; } @@ -2179,12 +2170,11 @@ static int open_return_path_on_source(MigrationState *ms) return 0; } -static int close_return_path_on_source(MigrationState *ms) +/* Return true if error detected, or false otherwise */ +static bool close_return_path_on_source(MigrationState *ms) { - int ret; - if (!ms->rp_state.rp_thread_created) { - return 0; + return false; } trace_migration_return_path_end_before(); @@ -2202,18 +2192,13 @@ static int close_return_path_on_source(MigrationState *ms) } } - trace_await_return_path_close_on_source_joining(); qemu_thread_join(&ms->rp_state.rp_thread); ms->rp_state.rp_thread_created = false; - trace_await_return_path_close_on_source_close(); - - ret = ms->rp_state.error; - ms->rp_state.error = false; - migration_release_dst_files(ms); + trace_migration_return_path_end_after(); - trace_migration_return_path_end_after(ret); - return ret; + /* Return path will persist the error in MigrationState when quit */ + return migrate_has_error(ms); } static inline void diff --git a/migration/migration.h b/migration/migration.h index 5944107ad5..615b517594 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -308,7 +308,6 @@ struct MigrationState { /* Protected by qemu_file_lock */ QEMUFile *from_dst_file; QemuThread rp_thread; - bool error; /* * We can also check non-zero of rp_thread, but there's no "official" * way to do this, so this bool makes it slightly more elegant. diff --git a/migration/qemu-file.c b/migration/qemu-file.c index d64500310d..94231ff295 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -137,7 +137,7 @@ QEMUFile *qemu_file_new_input(QIOChannel *ioc) * * If errp is specified, a verbose error message will be copied over. */ -static int qemu_file_get_error_obj(QEMUFile *f, Error **errp) +int qemu_file_get_error_obj(QEMUFile *f, Error **errp) { if (!f->last_error) { return 0; diff --git a/migration/qemu-file.h b/migration/qemu-file.h index 1774116f79..8aec9fabf7 100644 --- a/migration/qemu-file.h +++ b/migration/qemu-file.h @@ -68,6 +68,7 @@ int coroutine_mixed_fn qemu_peek_byte(QEMUFile *f, int offset); void qemu_file_skip(QEMUFile *f, int size); int qemu_file_get_error_obj_any(QEMUFile *f1, QEMUFile *f2, Error **errp); void qemu_file_set_error_obj(QEMUFile *f, int ret, Error *err); +int qemu_file_get_error_obj(QEMUFile *f, Error **errp); void qemu_file_set_error(QEMUFile *f, int ret); int qemu_file_shutdown(QEMUFile *f); QEMUFile *qemu_file_get_return_path(QEMUFile *f); diff --git a/migration/ram.c b/migration/ram.c index 34724e8fe8..d05ffddbc8 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1898,7 +1898,8 @@ static void migration_page_queue_free(RAMState *rs) * @start: starting address from the start of the RAMBlock * @len: length (in bytes) to send */ -int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len) +int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len, + Error **errp) { RAMBlock *ramblock; RAMState *rs = ram_state; @@ -1915,7 +1916,7 @@ int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len) * Shouldn't happen, we can't reuse the last RAMBlock if * it's the 1st request. */ - error_report("ram_save_queue_pages no previous block"); + error_setg(errp, "MIG_RP_MSG_REQ_PAGES has no previous block"); return -1; } } else { @@ -1923,16 +1924,17 @@ int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len) if (!ramblock) { /* We shouldn't be asked for a non-existent RAMBlock */ - error_report("ram_save_queue_pages no block '%s'", rbname); + error_setg(errp, "MIG_RP_MSG_REQ_PAGES has no block '%s'", rbname); return -1; } rs->last_req_rb = ramblock; } trace_ram_save_queue_pages(ramblock->idstr, start, len); if (!offset_in_ramblock(ramblock, start + len - 1)) { - error_report("%s request overrun start=" RAM_ADDR_FMT " len=" - RAM_ADDR_FMT " blocklen=" RAM_ADDR_FMT, - __func__, start, len, ramblock->used_length); + error_setg(errp, "MIG_RP_MSG_REQ_PAGES request overrun, " + "start=" RAM_ADDR_FMT " len=" + RAM_ADDR_FMT " blocklen=" RAM_ADDR_FMT, + start, len, ramblock->used_length); return -1; } @@ -1964,9 +1966,9 @@ int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len) assert(len % page_size == 0); while (len) { if (ram_save_host_page_urgent(pss)) { - error_report("%s: ram_save_host_page_urgent() failed: " - "ramblock=%s, start_addr=0x"RAM_ADDR_FMT, - __func__, ramblock->idstr, start); + error_setg(errp, "ram_save_host_page_urgent() failed: " + "ramblock=%s, start_addr=0x"RAM_ADDR_FMT, + ramblock->idstr, start); ret = -1; break; } @@ -4110,7 +4112,7 @@ static int ram_dirty_bitmap_sync_all(MigrationState *s, RAMState *rs) * This is only used when the postcopy migration is paused but wants * to resume from a middle point. */ -int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *block) +int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *block, Error **errp) { int ret = -EINVAL; /* from_dst_file is always valid because we're within rp_thread */ @@ -4124,8 +4126,8 @@ int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *block) trace_ram_dirty_bitmap_reload_begin(block->idstr); if (s->state != MIGRATION_STATUS_POSTCOPY_RECOVER) { - error_report("%s: incorrect state %s", __func__, - MigrationStatus_str(s->state)); + error_setg(errp, "Reload bitmap in incorrect state %s", + MigrationStatus_str(s->state)); return -EINVAL; } @@ -4142,9 +4144,8 @@ int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *block) /* The size of the bitmap should match with our ramblock */ if (size != local_size) { - error_report("%s: ramblock '%s' bitmap size mismatch " - "(0x%"PRIx64" != 0x%"PRIx64")", __func__, - block->idstr, size, local_size); + error_setg(errp, "ramblock '%s' bitmap size mismatch (0x%"PRIx64 + " != 0x%"PRIx64")", block->idstr, size, local_size); return -EINVAL; } @@ -4153,15 +4154,15 @@ int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *block) ret = qemu_file_get_error(file); if (ret || size != local_size) { - error_report("%s: read bitmap failed for ramblock '%s': %d" - " (size 0x%"PRIx64", got: 0x%"PRIx64")", - __func__, block->idstr, ret, local_size, size); + error_setg(errp, "read bitmap failed for ramblock '%s': %d" + " (size 0x%"PRIx64", got: 0x%"PRIx64")", + block->idstr, ret, local_size, size); return -EIO; } if (end_mark != RAMBLOCK_RECV_BITMAP_ENDING) { - error_report("%s: ramblock '%s' end mark incorrect: 0x%"PRIx64, - __func__, block->idstr, end_mark); + error_setg(errp, "ramblock '%s' end mark incorrect: 0x%"PRIx64, + block->idstr, end_mark); return -EINVAL; } diff --git a/migration/ram.h b/migration/ram.h index 9f3ad1ee81..6e2c2c1950 100644 --- a/migration/ram.h +++ b/migration/ram.h @@ -50,7 +50,8 @@ uint64_t ram_bytes_total(void); void mig_throttle_counter_reset(void); uint64_t ram_pagesize_summary(void); -int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len); +int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len, + Error **errp); void ram_postcopy_migrated_memory_release(MigrationState *ms); /* For outgoing discard bitmap */ void ram_postcopy_send_discard_bitmap(MigrationState *ms); @@ -70,7 +71,7 @@ void ramblock_recv_bitmap_set(RAMBlock *rb, void *host_addr); void ramblock_recv_bitmap_set_range(RAMBlock *rb, void *host_addr, size_t nr); int64_t ramblock_recv_bitmap_send(QEMUFile *file, const char *block_name); -int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *rb); +int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *rb, Error **errp); bool ramblock_page_is_discarded(RAMBlock *rb, ram_addr_t start); void postcopy_preempt_shutdown_file(MigrationState *s); void *postcopy_preempt_thread(void *opaque); diff --git a/migration/trace-events b/migration/trace-events index e54f317e3b..de4a743c8a 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -150,8 +150,6 @@ multifd_tls_outgoing_handshake_complete(void *ioc) "ioc=%p" multifd_set_outgoing_channel(void *ioc, const char *ioctype, const char *hostname) "ioc=%p ioctype=%s hostname=%s" # migration.c -await_return_path_close_on_source_close(void) "" -await_return_path_close_on_source_joining(void) "" migrate_set_state(const char *new_state) "new state %s" migrate_fd_cleanup(void) "" migrate_fd_error(const char *error_desc) "error=%s" @@ -168,7 +166,7 @@ migration_completion_postcopy_end_after_complete(void) "" migration_rate_limit_pre(int ms) "%d ms" migration_rate_limit_post(int urgent) "urgent: %d" migration_return_path_end_before(void) "" -migration_return_path_end_after(int rp_error) "%d" +migration_return_path_end_after(void) "" migration_thread_after_loop(void) "" migration_thread_file_err(void) "" migration_thread_setup_complete(void) "" From f8c543e808f20ba936d94cfa5fc592627d8d100c Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 17 Oct 2023 16:26:30 -0400 Subject: [PATCH 327/974] migration: Allow network to fail even during recovery Normally the postcopy recover phase should only exist for a super short period, that's the duration when QEMU is trying to recover from an interrupted postcopy migration, during which handshake will be carried out for continuing the procedure with state changes from PAUSED -> RECOVER -> POSTCOPY_ACTIVE again. Here RECOVER phase should be super small, that happens right after the admin specified a new but working network link for QEMU to reconnect to dest QEMU. However there can still be case where the channel is broken in this small RECOVER window. If it happens, with current code there's no way the src QEMU can got kicked out of RECOVER stage. No way either to retry the recover in another channel when established. This patch allows the RECOVER phase to fail itself too - we're mostly ready, just some small things missing, e.g. properly kick the main migration thread out when sleeping on rp_sem when we found that we're at RECOVER stage. When this happens, it fails the RECOVER itself, and rollback to PAUSED stage. Then the user can retry another round of recovery. To make it even stronger, teach QMP command migrate-pause to explicitly kick src/dst QEMU out when needed, so even if for some reason the migration thread didn't got kicked out already by a failing rethrn-path thread, the admin can also kick it out. This will be an super, super corner case, but still try to cover that. One can try to test this with two proxy channels for migration: (a) socat unix-listen:/tmp/src.sock,reuseaddr,fork tcp:localhost:10000 (b) socat tcp-listen:10000,reuseaddr,fork unix:/tmp/dst.sock So the migration channel will be: (a) (b) src -> /tmp/src.sock -> tcp:10000 -> /tmp/dst.sock -> dst Then to make QEMU hang at RECOVER stage, one can do below: (1) stop the postcopy using QMP command postcopy-pause (2) kill the 2nd proxy (b) (3) try to recover the postcopy using /tmp/src.sock on src (4) src QEMU will go into RECOVER stage but won't be able to continue from there, because the channel is actually broken at (b) Before this patch, step (4) will make src QEMU stuck in RECOVER stage, without a way to kick the QEMU out or continue the postcopy again. After this patch, (4) will quickly fail qemu and bounce back to PAUSED stage. Admin can also kick QEMU from (4) into PAUSED when needed using migrate-pause when needed. After bouncing back to PAUSED stage, one can recover again. Reported-by: Xiaohui Li Reviewed-by: Fabiano Rosas Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2111332 Reviewed-by: Juan Quintela Signed-off-by: Peter Xu Signed-off-by: Juan Quintela Message-ID: <20231017202633.296756-3-peterx@redhat.com> --- migration/migration.c | 63 +++++++++++++++++++++++++++++++++++++++---- migration/migration.h | 8 ++++-- migration/ram.c | 4 ++- 3 files changed, 67 insertions(+), 8 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 455ddc896a..e875ea0d6b 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1393,6 +1393,17 @@ bool migration_in_postcopy(void) } } +bool migration_postcopy_is_alive(int state) +{ + switch (state) { + case MIGRATION_STATUS_POSTCOPY_ACTIVE: + case MIGRATION_STATUS_POSTCOPY_RECOVER: + return true; + default: + return false; + } +} + bool migration_in_postcopy_after_devices(MigrationState *s) { return migration_in_postcopy() && s->postcopy_after_devices; @@ -1673,8 +1684,15 @@ void qmp_migrate_pause(Error **errp) MigrationIncomingState *mis = migration_incoming_get_current(); int ret = 0; - if (ms->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) { + if (migration_postcopy_is_alive(ms->state)) { /* Source side, during postcopy */ + Error *error = NULL; + + /* Tell the core migration that we're pausing */ + error_setg(&error, "Postcopy migration is paused by the user"); + migrate_set_error(ms, error); + error_free(error); + qemu_mutex_lock(&ms->qemu_file_lock); if (ms->to_dst_file) { ret = qemu_file_shutdown(ms->to_dst_file); @@ -1683,10 +1701,17 @@ void qmp_migrate_pause(Error **errp) if (ret) { error_setg(errp, "Failed to pause source migration"); } + + /* + * Kick the migration thread out of any waiting windows (on behalf + * of the rp thread). + */ + migration_rp_kick(ms); + return; } - if (mis->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) { + if (migration_postcopy_is_alive(mis->state)) { ret = qemu_file_shutdown(mis->from_src_file); if (ret) { error_setg(errp, "Failed to pause destination migration"); @@ -1695,7 +1720,7 @@ void qmp_migrate_pause(Error **errp) } error_setg(errp, "migrate-pause is currently only supported " - "during postcopy-active state"); + "during postcopy-active or postcopy-recover state"); } bool migration_is_blocked(Error **errp) @@ -1882,9 +1907,21 @@ void qmp_migrate_continue(MigrationStatus state, Error **errp) qemu_sem_post(&s->pause_sem); } -void migration_rp_wait(MigrationState *s) +int migration_rp_wait(MigrationState *s) { + /* If migration has failure already, ignore the wait */ + if (migrate_has_error(s)) { + return -1; + } + qemu_sem_wait(&s->rp_state.rp_sem); + + /* After wait, double check that there's no failure */ + if (migrate_has_error(s)) { + return -1; + } + + return 0; } void migration_rp_kick(MigrationState *s) @@ -2146,6 +2183,20 @@ out: trace_source_return_path_thread_bad_end(); } + if (ms->state == MIGRATION_STATUS_POSTCOPY_RECOVER) { + /* + * this will be extremely unlikely: that we got yet another network + * issue during recovering of the 1st network failure.. during this + * period the main migration thread can be waiting on rp_sem for + * this thread to sync with the other side. + * + * When this happens, explicitly kick the migration thread out of + * RECOVER stage and back to PAUSED, so the admin can try + * everything again. + */ + migration_rp_kick(ms); + } + trace_source_return_path_thread_end(); rcu_unregister_thread(); @@ -2611,7 +2662,9 @@ static int postcopy_resume_handshake(MigrationState *s) qemu_savevm_send_postcopy_resume(s->to_dst_file); while (s->state == MIGRATION_STATUS_POSTCOPY_RECOVER) { - migration_rp_wait(s); + if (migration_rp_wait(s)) { + return -1; + } } if (s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) { diff --git a/migration/migration.h b/migration/migration.h index 615b517594..af8c965b7f 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -494,6 +494,7 @@ int migrate_init(MigrationState *s, Error **errp); bool migration_is_blocked(Error **errp); /* True if outgoing migration has entered postcopy phase */ bool migration_in_postcopy(void); +bool migration_postcopy_is_alive(int state); MigrationState *migrate_get_current(void); uint64_t ram_get_total_transferred_pages(void); @@ -534,8 +535,11 @@ void migration_populate_vfio_info(MigrationInfo *info); void migration_reset_vfio_bytes_transferred(void); void postcopy_temp_page_reset(PostcopyTmpPage *tmp_page); -/* Migration thread waiting for return path thread. */ -void migration_rp_wait(MigrationState *s); +/* + * Migration thread waiting for return path thread. Return non-zero if an + * error is detected. + */ +int migration_rp_wait(MigrationState *s); /* * Kick the migration thread waiting for return path messages. NOTE: the * name can be slightly confusing (when read as "kick the rp thread"), just diff --git a/migration/ram.c b/migration/ram.c index d05ffddbc8..929cba08f4 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -4099,7 +4099,9 @@ static int ram_dirty_bitmap_sync_all(MigrationState *s, RAMState *rs) /* Wait until all the ramblocks' dirty bitmap synced */ while (qatomic_read(&rs->postcopy_bmap_sync_requested)) { - migration_rp_wait(s); + if (migration_rp_wait(s)) { + return -1; + } } trace_ram_dirty_bitmap_sync_complete(); From 7bca2bb7bfd2fe5241696fd09cc97f23661d4fc8 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Tue, 17 Oct 2023 16:26:31 -0400 Subject: [PATCH 328/974] tests/migration-test: Add a test for postcopy hangs during RECOVER To do so, create two paired sockets, but make them not providing real data. Feed those fake sockets to src/dst QEMUs for recovery to let them go into RECOVER stage without going out. Test that we can always kick it out and recover again with the right ports. This patch is based on Fabiano's version here: https://lore.kernel.org/r/877cowmdu0.fsf@suse.de Signed-off-by: Fabiano Rosas [peterx: write commit message, remove case 1, fix bugs, and more] Signed-off-by: Peter Xu Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-ID: <20231017202633.296756-4-peterx@redhat.com> --- tests/qtest/migration-test.c | 110 +++++++++++++++++++++++++++++++++-- 1 file changed, 104 insertions(+), 6 deletions(-) diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index b7ebc23903..047b7194df 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -728,6 +728,7 @@ typedef struct { /* Postcopy specific fields */ void *postcopy_data; bool postcopy_preempt; + bool postcopy_recovery_test_fail; } MigrateCommon; static int test_migrate_start(QTestState **from, QTestState **to, @@ -1404,6 +1405,80 @@ static void test_postcopy_preempt_tls_psk(void) } #endif +static void wait_for_postcopy_status(QTestState *one, const char *status) +{ + wait_for_migration_status(one, status, + (const char * []) { "failed", "active", + "completed", NULL }); +} + +#ifndef _WIN32 +static void postcopy_recover_fail(QTestState *from, QTestState *to) +{ + int ret, pair1[2], pair2[2]; + char c; + + /* Create two unrelated socketpairs */ + ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair1); + g_assert_cmpint(ret, ==, 0); + + ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair2); + g_assert_cmpint(ret, ==, 0); + + /* + * Give the guests unpaired ends of the sockets, so they'll all blocked + * at reading. This mimics a wrong channel established. + */ + qtest_qmp_fds_assert_success(from, &pair1[0], 1, + "{ 'execute': 'getfd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + qtest_qmp_fds_assert_success(to, &pair2[0], 1, + "{ 'execute': 'getfd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + + /* + * Write the 1st byte as QEMU_VM_COMMAND (0x8) for the dest socket, to + * emulate the 1st byte of a real recovery, but stops from there to + * keep dest QEMU in RECOVER. This is needed so that we can kick off + * the recover process on dest QEMU (by triggering the G_IO_IN event). + * + * NOTE: this trick is not needed on src QEMUs, because src doesn't + * rely on an pre-existing G_IO_IN event, so it will always trigger the + * upcoming recovery anyway even if it can read nothing. + */ +#define QEMU_VM_COMMAND 0x08 + c = QEMU_VM_COMMAND; + ret = send(pair2[1], &c, 1, 0); + g_assert_cmpint(ret, ==, 1); + + migrate_recover(to, "fd:fd-mig"); + migrate_qmp(from, "fd:fd-mig", "{'resume': true}"); + + /* + * Make sure both QEMU instances will go into RECOVER stage, then test + * kicking them out using migrate-pause. + */ + wait_for_postcopy_status(from, "postcopy-recover"); + wait_for_postcopy_status(to, "postcopy-recover"); + + /* + * This would be issued by the admin upon noticing the hang, we should + * make sure we're able to kick this out. + */ + migrate_pause(from); + wait_for_postcopy_status(from, "postcopy-paused"); + + /* Do the same test on dest */ + migrate_pause(to); + wait_for_postcopy_status(to, "postcopy-paused"); + + close(pair1[0]); + close(pair1[1]); + close(pair2[0]); + close(pair2[1]); +} +#endif /* _WIN32 */ + static void test_postcopy_recovery_common(MigrateCommon *args) { QTestState *from, *to; @@ -1439,9 +1514,19 @@ static void test_postcopy_recovery_common(MigrateCommon *args) * migrate-recover command can only succeed if destination machine * is in the paused state */ - wait_for_migration_status(to, "postcopy-paused", - (const char * []) { "failed", "active", - "completed", NULL }); + wait_for_postcopy_status(to, "postcopy-paused"); + wait_for_postcopy_status(from, "postcopy-paused"); + +#ifndef _WIN32 + if (args->postcopy_recovery_test_fail) { + /* + * Test when a wrong socket specified for recover, and then the + * ability to kick it out, and continue with a correct socket. + */ + postcopy_recover_fail(from, to); + /* continue with a good recovery */ + } +#endif /* _WIN32 */ /* * Create a new socket to emulate a new channel that is different @@ -1455,9 +1540,6 @@ static void test_postcopy_recovery_common(MigrateCommon *args) * Try to rebuild the migration channel using the resume flag and * the newly created channel */ - wait_for_migration_status(from, "postcopy-paused", - (const char * []) { "failed", "active", - "completed", NULL }); migrate_qmp(from, uri, "{'resume': true}"); /* Restore the postcopy bandwidth to unlimited */ @@ -1482,6 +1564,17 @@ static void test_postcopy_recovery_compress(void) test_postcopy_recovery_common(&args); } +#ifndef _WIN32 +static void test_postcopy_recovery_double_fail(void) +{ + MigrateCommon args = { + .postcopy_recovery_test_fail = true, + }; + + test_postcopy_recovery_common(&args); +} +#endif /* _WIN32 */ + #ifdef CONFIG_GNUTLS static void test_postcopy_recovery_tls_psk(void) { @@ -3093,6 +3186,11 @@ int main(int argc, char **argv) qtest_add_func("/migration/postcopy/recovery/compress/plain", test_postcopy_recovery_compress); } +#ifndef _WIN32 + qtest_add_func("/migration/postcopy/recovery/double-failures", + test_postcopy_recovery_double_fail); +#endif /* _WIN32 */ + } qtest_add_func("/migration/bad_dest", test_baddest); From 88577f32421cd6f52928b0c69573983d8d9acbd0 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 17 Oct 2023 16:26:32 -0400 Subject: [PATCH 329/974] migration: Change ram_dirty_bitmap_reload() retval to bool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now we have a Error** passed into the return path thread stack, which is even clearer than an int retval. Change ram_dirty_bitmap_reload() and the callers to use a bool instead to replace errnos. Suggested-by: Philippe Mathieu-Daudé Signed-off-by: Peter Xu Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-ID: <20231017202633.296756-5-peterx@redhat.com> --- migration/migration.c | 18 +++++++++--------- migration/ram.c | 24 ++++++++++++------------ migration/ram.h | 2 +- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index e875ea0d6b..ce9b7692ad 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1971,29 +1971,29 @@ migrate_handle_rp_req_pages(MigrationState *ms, const char* rbname, ram_save_queue_pages(rbname, start, len, errp); } -static int migrate_handle_rp_recv_bitmap(MigrationState *s, char *block_name, - Error **errp) +static bool migrate_handle_rp_recv_bitmap(MigrationState *s, char *block_name, + Error **errp) { RAMBlock *block = qemu_ram_block_by_name(block_name); if (!block) { error_setg(errp, "MIG_RP_MSG_RECV_BITMAP has invalid block name '%s'", block_name); - return -EINVAL; + return false; } /* Fetch the received bitmap and refresh the dirty bitmap */ return ram_dirty_bitmap_reload(s, block, errp); } -static int migrate_handle_rp_resume_ack(MigrationState *s, - uint32_t value, Error **errp) +static bool migrate_handle_rp_resume_ack(MigrationState *s, + uint32_t value, Error **errp) { trace_source_return_path_thread_resume_ack(value); if (value != MIGRATION_RESUME_ACK_VALUE) { error_setg(errp, "illegal resume_ack value %"PRIu32, value); - return -1; + return false; } /* Now both sides are active. */ @@ -2003,7 +2003,7 @@ static int migrate_handle_rp_resume_ack(MigrationState *s, /* Notify send thread that time to continue send pages */ migration_rp_kick(s); - return 0; + return true; } /* @@ -2154,14 +2154,14 @@ static void *source_return_path_thread(void *opaque) } /* Format: len (1B) + idstr (<255B). This ends the idstr. */ buf[buf[0] + 1] = '\0'; - if (migrate_handle_rp_recv_bitmap(ms, (char *)(buf + 1), &err)) { + if (!migrate_handle_rp_recv_bitmap(ms, (char *)(buf + 1), &err)) { goto out; } break; case MIG_RP_MSG_RESUME_ACK: tmp32 = ldl_be_p(buf); - if (migrate_handle_rp_resume_ack(ms, tmp32, &err)) { + if (!migrate_handle_rp_resume_ack(ms, tmp32, &err)) { goto out; } break; diff --git a/migration/ram.c b/migration/ram.c index 929cba08f4..a0f3b86663 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -4113,10 +4113,11 @@ static int ram_dirty_bitmap_sync_all(MigrationState *s, RAMState *rs) * Read the received bitmap, revert it as the initial dirty bitmap. * This is only used when the postcopy migration is paused but wants * to resume from a middle point. + * + * Returns true if succeeded, false for errors. */ -int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *block, Error **errp) +bool ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *block, Error **errp) { - int ret = -EINVAL; /* from_dst_file is always valid because we're within rp_thread */ QEMUFile *file = s->rp_state.from_dst_file; g_autofree unsigned long *le_bitmap = NULL; @@ -4130,7 +4131,7 @@ int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *block, Error **errp) if (s->state != MIGRATION_STATUS_POSTCOPY_RECOVER) { error_setg(errp, "Reload bitmap in incorrect state %s", MigrationStatus_str(s->state)); - return -EINVAL; + return false; } /* @@ -4148,24 +4149,23 @@ int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *block, Error **errp) if (size != local_size) { error_setg(errp, "ramblock '%s' bitmap size mismatch (0x%"PRIx64 " != 0x%"PRIx64")", block->idstr, size, local_size); - return -EINVAL; + return false; } size = qemu_get_buffer(file, (uint8_t *)le_bitmap, local_size); end_mark = qemu_get_be64(file); - ret = qemu_file_get_error(file); - if (ret || size != local_size) { - error_setg(errp, "read bitmap failed for ramblock '%s': %d" - " (size 0x%"PRIx64", got: 0x%"PRIx64")", - block->idstr, ret, local_size, size); - return -EIO; + if (qemu_file_get_error(file) || size != local_size) { + error_setg(errp, "read bitmap failed for ramblock '%s': " + "(size 0x%"PRIx64", got: 0x%"PRIx64")", + block->idstr, local_size, size); + return false; } if (end_mark != RAMBLOCK_RECV_BITMAP_ENDING) { error_setg(errp, "ramblock '%s' end mark incorrect: 0x%"PRIx64, block->idstr, end_mark); - return -EINVAL; + return false; } /* @@ -4197,7 +4197,7 @@ int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *block, Error **errp) */ migration_rp_kick(s); - return 0; + return true; } static int ram_resume_prepare(MigrationState *s, void *opaque) diff --git a/migration/ram.h b/migration/ram.h index 6e2c2c1950..9b937a446b 100644 --- a/migration/ram.h +++ b/migration/ram.h @@ -71,7 +71,7 @@ void ramblock_recv_bitmap_set(RAMBlock *rb, void *host_addr); void ramblock_recv_bitmap_set_range(RAMBlock *rb, void *host_addr, size_t nr); int64_t ramblock_recv_bitmap_send(QEMUFile *file, const char *block_name); -int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *rb, Error **errp); +bool ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *rb, Error **errp); bool ramblock_page_is_discarded(RAMBlock *rb, ram_addr_t start); void postcopy_preempt_shutdown_file(MigrationState *s); void *postcopy_preempt_thread(void *opaque); From e034f8836492d6a4c64a6a0079877fc3c7e09f36 Mon Sep 17 00:00:00 2001 From: Het Gala Date: Mon, 23 Oct 2023 15:20:40 -0300 Subject: [PATCH 330/974] migration: New QAPI type 'MigrateAddress' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch introduces well defined MigrateAddress struct and its related child objects. The existing argument of 'migrate' and 'migrate-incoming' QAPI - 'uri' is of type string. The current implementation follows double encoding scheme for fetching migration parameters like 'uri' and this is not an ideal design. Motive for intoducing struct level design is to prevent double encoding of QAPI arguments, as Qemu should be able to directly use the QAPI arguments without any level of encoding. Note: this commit only adds the type, and actual uses comes in later commits. Fabiano fixed for "file" transport. Suggested-by: Aravind Retnakaran Signed-off-by: Het Gala Reviewed-by: Juan Quintela Reviewed-by: Daniel P. Berrangé Acked-by: Markus Armbruster Signed-off-by: Fabiano Rosas Signed-off-by: Juan Quintela Message-ID: <20231023182053.8711-2-farosas@suse.de> Message-Id: <20231023182053.8711-3-farosas@suse.de> --- qapi/migration.json | 57 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/qapi/migration.json b/qapi/migration.json index 3daeffc95d..182b150b87 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -1584,6 +1584,63 @@ ## { 'command': 'migrate-continue', 'data': {'state': 'MigrationStatus'} } +## +# @MigrationAddressType: +# +# The migration stream transport mechanisms. +# +# @socket: Migrate via socket. +# +# @exec: Direct the migration stream to another process. +# +# @rdma: Migrate via RDMA. +# +# @file: Direct the migration stream to a file. +# +# Since 8.2 +## +{ 'enum': 'MigrationAddressType', + 'data': [ 'socket', 'exec', 'rdma', 'file' ] } + +## +# @FileMigrationArgs: +# +# @filename: The file to receive the migration stream +# +# @offset: The file offset where the migration stream will start +# +# Since 8.2 +## +{ 'struct': 'FileMigrationArgs', + 'data': { 'filename': 'str', + 'offset': 'uint64' } } + +## +# @MigrationExecCommand: +# +# @args: command (list head) and arguments to execute. +# +# Since 8.2 +## +{ 'struct': 'MigrationExecCommand', + 'data': {'args': [ 'str' ] } } + +## +# @MigrationAddress: +# +# Migration endpoint configuration. +# +# Since 8.2 +## +{ 'union': 'MigrationAddress', + 'base': { 'transport' : 'MigrationAddressType'}, + 'discriminator': 'transport', + 'data': { + 'socket': 'SocketAddress', + 'exec': 'MigrationExecCommand', + 'rdma': 'InetSocketAddress', + 'file': 'FileMigrationArgs' } } + ## # @migrate: # From 72a8192e225ceafd8494150eb9375c400da50dd6 Mon Sep 17 00:00:00 2001 From: Het Gala Date: Mon, 23 Oct 2023 15:20:42 -0300 Subject: [PATCH 331/974] migration: convert migration 'uri' into 'MigrateAddress' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch parses 'migrate' and 'migrate-incoming' QAPI's 'uri' string containing migration connection related information and stores them inside well defined 'MigrateAddress' struct. Fabiano fixed for "file" transport. Suggested-by: Aravind Retnakaran Signed-off-by: Het Gala Signed-off-by: Fabiano Rosas Reviewed-by: Daniel P. Berrangé Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-ID: <20231023182053.8711-4-farosas@suse.de> Message-ID: <20231023182053.8711-5-farosas@suse.de> --- migration/exec.c | 1 - migration/exec.h | 4 +++ migration/file.c | 2 +- migration/file.h | 1 + migration/migration.c | 63 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 69 insertions(+), 2 deletions(-) diff --git a/migration/exec.c b/migration/exec.c index 2bf882bbe1..32f5143dfd 100644 --- a/migration/exec.c +++ b/migration/exec.c @@ -27,7 +27,6 @@ #include "qemu/cutils.h" #ifdef WIN32 -const char *exec_get_cmd_path(void); const char *exec_get_cmd_path(void) { g_autofree char *detected_path = g_new(char, MAX_PATH); diff --git a/migration/exec.h b/migration/exec.h index b210ffde7a..736cd71028 100644 --- a/migration/exec.h +++ b/migration/exec.h @@ -19,6 +19,10 @@ #ifndef QEMU_MIGRATION_EXEC_H #define QEMU_MIGRATION_EXEC_H + +#ifdef WIN32 +const char *exec_get_cmd_path(void); +#endif void exec_start_incoming_migration(const char *host_port, Error **errp); void exec_start_outgoing_migration(MigrationState *s, const char *host_port, diff --git a/migration/file.c b/migration/file.c index cf5b1bf365..ec069ef329 100644 --- a/migration/file.c +++ b/migration/file.c @@ -19,7 +19,7 @@ /* Remove the offset option from @filespec and return it in @offsetp. */ -static int file_parse_offset(char *filespec, uint64_t *offsetp, Error **errp) +int file_parse_offset(char *filespec, uint64_t *offsetp, Error **errp) { char *option = strstr(filespec, OFFSET_OPTION); int ret; diff --git a/migration/file.h b/migration/file.h index 90fa4849e0..3888a57105 100644 --- a/migration/file.h +++ b/migration/file.h @@ -11,4 +11,5 @@ void file_start_incoming_migration(const char *filename, Error **errp); void file_start_outgoing_migration(MigrationState *s, const char *filename, Error **errp); +int file_parse_offset(char *filespec, uint64_t *offsetp, Error **errp); #endif diff --git a/migration/migration.c b/migration/migration.c index ce9b7692ad..a1bace259e 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -66,6 +66,7 @@ #include "sysemu/qtest.h" #include "options.h" #include "sysemu/dirtylimit.h" +#include "qemu/sockets.h" static NotifierList migration_state_notifiers = NOTIFIER_LIST_INITIALIZER(migration_state_notifiers); @@ -453,9 +454,62 @@ void migrate_add_address(SocketAddress *address) QAPI_CLONE(SocketAddress, address)); } +static bool migrate_uri_parse(const char *uri, + MigrationAddress **channel, + Error **errp) +{ + g_autoptr(MigrationAddress) addr = g_new0(MigrationAddress, 1); + SocketAddress *saddr = NULL; + InetSocketAddress *isock = &addr->u.rdma; + strList **tail = &addr->u.exec.args; + + if (strstart(uri, "exec:", NULL)) { + addr->transport = MIGRATION_ADDRESS_TYPE_EXEC; +#ifdef WIN32 + QAPI_LIST_APPEND(tail, g_strdup(exec_get_cmd_path())); + QAPI_LIST_APPEND(tail, g_strdup("/c")); +#else + QAPI_LIST_APPEND(tail, g_strdup("/bin/sh")); + QAPI_LIST_APPEND(tail, g_strdup("-c")); +#endif + QAPI_LIST_APPEND(tail, g_strdup(uri + strlen("exec:"))); + } else if (strstart(uri, "rdma:", NULL)) { + if (inet_parse(isock, uri + strlen("rdma:"), errp)) { + qapi_free_InetSocketAddress(isock); + return false; + } + addr->transport = MIGRATION_ADDRESS_TYPE_RDMA; + } else if (strstart(uri, "tcp:", NULL) || + strstart(uri, "unix:", NULL) || + strstart(uri, "vsock:", NULL) || + strstart(uri, "fd:", NULL)) { + addr->transport = MIGRATION_ADDRESS_TYPE_SOCKET; + saddr = socket_parse(uri, errp); + if (!saddr) { + return false; + } + addr->u.socket.type = saddr->type; + addr->u.socket.u = saddr->u; + } else if (strstart(uri, "file:", NULL)) { + addr->transport = MIGRATION_ADDRESS_TYPE_FILE; + addr->u.file.filename = g_strdup(uri + strlen("file:")); + if (file_parse_offset(addr->u.file.filename, &addr->u.file.offset, + errp)) { + return false; + } + } else { + error_setg(errp, "unknown migration protocol: %s", uri); + return false; + } + + *channel = g_steal_pointer(&addr); + return true; +} + static void qemu_start_incoming_migration(const char *uri, Error **errp) { const char *p = NULL; + g_autoptr(MigrationAddress) channel = NULL; MigrationIncomingState *mis = migration_incoming_get_current(); /* URI is not suitable for migration? */ @@ -463,6 +517,10 @@ static void qemu_start_incoming_migration(const char *uri, Error **errp) return; } + if (uri && !migrate_uri_parse(uri, &channel, errp)) { + return; + } + migrate_set_state(&mis->state, MIGRATION_STATUS_NONE, MIGRATION_STATUS_SETUP); @@ -1840,12 +1898,17 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk, Error *local_err = NULL; MigrationState *s = migrate_get_current(); const char *p = NULL; + g_autoptr(MigrationAddress) channel = NULL; /* URI is not suitable for migration? */ if (!migration_channels_and_uri_compatible(uri, errp)) { return; } + if (!migrate_uri_parse(uri, &channel, errp)) { + return; + } + resume_requested = has_resume && resume; if (!migrate_prepare(s, has_blk && blk, has_inc && inc, resume_requested, errp)) { From 34dfc5e4077039e93667b5a72a2fbbe2f1b96e4b Mon Sep 17 00:00:00 2001 From: Het Gala Date: Mon, 23 Oct 2023 15:20:44 -0300 Subject: [PATCH 332/974] migration: convert socket backend to accept MigrateAddress MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Socket transport backend for 'migrate'/'migrate-incoming' QAPIs accept new wire protocol of MigrateAddress struct. It is achived by parsing 'uri' string and storing migration parameters required for socket connection into well defined SocketAddress struct. Suggested-by: Aravind Retnakaran Signed-off-by: Het Gala Reviewed-by: Daniel P. Berrangé Signed-off-by: Fabiano Rosas Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-ID: <20231023182053.8711-6-farosas@suse.de> --- migration/migration.c | 30 ++++++++++++++++++------------ migration/socket.c | 39 +++++++++------------------------------ migration/socket.h | 7 ++++--- 3 files changed, 31 insertions(+), 45 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index a1bace259e..846877135a 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -524,10 +524,15 @@ static void qemu_start_incoming_migration(const char *uri, Error **errp) migrate_set_state(&mis->state, MIGRATION_STATUS_NONE, MIGRATION_STATUS_SETUP); - if (strstart(uri, "tcp:", &p) || - strstart(uri, "unix:", NULL) || - strstart(uri, "vsock:", NULL)) { - socket_start_incoming_migration(p ? p : uri, errp); + if (channel->transport == MIGRATION_ADDRESS_TYPE_SOCKET) { + SocketAddress *saddr = &channel->u.socket; + if (saddr->type == SOCKET_ADDRESS_TYPE_INET || + saddr->type == SOCKET_ADDRESS_TYPE_UNIX || + saddr->type == SOCKET_ADDRESS_TYPE_VSOCK) { + socket_start_incoming_migration(saddr, errp); + } else if (saddr->type == SOCKET_ADDRESS_TYPE_FD) { + fd_start_incoming_migration(saddr->u.fd.str, errp); + } #ifdef CONFIG_RDMA } else if (strstart(uri, "rdma:", &p)) { if (migrate_compress()) { @@ -546,8 +551,6 @@ static void qemu_start_incoming_migration(const char *uri, Error **errp) #endif } else if (strstart(uri, "exec:", &p)) { exec_start_incoming_migration(p, errp); - } else if (strstart(uri, "fd:", &p)) { - fd_start_incoming_migration(p, errp); } else if (strstart(uri, "file:", &p)) { file_start_incoming_migration(p, errp); } else { @@ -1922,18 +1925,21 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk, } } - if (strstart(uri, "tcp:", &p) || - strstart(uri, "unix:", NULL) || - strstart(uri, "vsock:", NULL)) { - socket_start_outgoing_migration(s, p ? p : uri, &local_err); + if (channel->transport == MIGRATION_ADDRESS_TYPE_SOCKET) { + SocketAddress *saddr = &channel->u.socket; + if (saddr->type == SOCKET_ADDRESS_TYPE_INET || + saddr->type == SOCKET_ADDRESS_TYPE_UNIX || + saddr->type == SOCKET_ADDRESS_TYPE_VSOCK) { + socket_start_outgoing_migration(s, saddr, &local_err); + } else if (saddr->type == SOCKET_ADDRESS_TYPE_FD) { + fd_start_outgoing_migration(s, saddr->u.fd.str, &local_err); + } #ifdef CONFIG_RDMA } else if (strstart(uri, "rdma:", &p)) { rdma_start_outgoing_migration(s, p, &local_err); #endif } else if (strstart(uri, "exec:", &p)) { exec_start_outgoing_migration(s, p, &local_err); - } else if (strstart(uri, "fd:", &p)) { - fd_start_outgoing_migration(s, p, &local_err); } else if (strstart(uri, "file:", &p)) { file_start_outgoing_migration(s, p, &local_err); } else { diff --git a/migration/socket.c b/migration/socket.c index 1b6f5baefb..98e3ea1514 100644 --- a/migration/socket.c +++ b/migration/socket.c @@ -28,6 +28,8 @@ #include "trace.h" #include "postcopy-ram.h" #include "options.h" +#include "qapi/clone-visitor.h" +#include "qapi/qapi-visit-sockets.h" struct SocketOutgoingArgs { SocketAddress *saddr; @@ -108,19 +110,19 @@ out: object_unref(OBJECT(sioc)); } -static void -socket_start_outgoing_migration_internal(MigrationState *s, - SocketAddress *saddr, - Error **errp) +void socket_start_outgoing_migration(MigrationState *s, + SocketAddress *saddr, + Error **errp) { QIOChannelSocket *sioc = qio_channel_socket_new(); struct SocketConnectData *data = g_new0(struct SocketConnectData, 1); + SocketAddress *addr = QAPI_CLONE(SocketAddress, saddr); data->s = s; /* in case previous migration leaked it */ qapi_free_SocketAddress(outgoing_args.saddr); - outgoing_args.saddr = saddr; + outgoing_args.saddr = addr; if (saddr->type == SOCKET_ADDRESS_TYPE_INET) { data->hostname = g_strdup(saddr->u.inet.host); @@ -135,18 +137,6 @@ socket_start_outgoing_migration_internal(MigrationState *s, NULL); } -void socket_start_outgoing_migration(MigrationState *s, - const char *str, - Error **errp) -{ - Error *err = NULL; - SocketAddress *saddr = socket_parse(str, &err); - if (!err) { - socket_start_outgoing_migration_internal(s, saddr, &err); - } - error_propagate(errp, err); -} - static void socket_accept_incoming_migration(QIONetListener *listener, QIOChannelSocket *cioc, gpointer opaque) @@ -172,9 +162,8 @@ socket_incoming_migration_end(void *opaque) object_unref(OBJECT(listener)); } -static void -socket_start_incoming_migration_internal(SocketAddress *saddr, - Error **errp) +void socket_start_incoming_migration(SocketAddress *saddr, + Error **errp) { QIONetListener *listener = qio_net_listener_new(); MigrationIncomingState *mis = migration_incoming_get_current(); @@ -213,13 +202,3 @@ socket_start_incoming_migration_internal(SocketAddress *saddr, } } -void socket_start_incoming_migration(const char *str, Error **errp) -{ - Error *err = NULL; - SocketAddress *saddr = socket_parse(str, &err); - if (!err) { - socket_start_incoming_migration_internal(saddr, &err); - } - qapi_free_SocketAddress(saddr); - error_propagate(errp, err); -} diff --git a/migration/socket.h b/migration/socket.h index dc54df4e6c..5e4c33b8ea 100644 --- a/migration/socket.h +++ b/migration/socket.h @@ -19,13 +19,14 @@ #include "io/channel.h" #include "io/task.h" +#include "qemu/sockets.h" void socket_send_channel_create(QIOTaskFunc f, void *data); QIOChannel *socket_send_channel_create_sync(Error **errp); int socket_send_channel_destroy(QIOChannel *send); -void socket_start_incoming_migration(const char *str, Error **errp); +void socket_start_incoming_migration(SocketAddress *saddr, Error **errp); -void socket_start_outgoing_migration(MigrationState *s, const char *str, - Error **errp); +void socket_start_outgoing_migration(MigrationState *s, + SocketAddress *saddr, Error **errp); #endif From 3fa9642ff7d51f7fc3ba68e6ccd13a939d5bd609 Mon Sep 17 00:00:00 2001 From: Het Gala Date: Mon, 23 Oct 2023 15:20:45 -0300 Subject: [PATCH 333/974] migration: convert rdma backend to accept MigrateAddress MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RDMA based transport backend for 'migrate'/'migrate-incoming' QAPIs accept new wire protocol of MigrateAddress struct. It is achived by parsing 'uri' string and storing migration parameters required for RDMA connection into well defined InetSocketAddress struct. Suggested-by: Aravind Retnakaran Signed-off-by: Het Gala Reviewed-by: Daniel P. Berrangé Signed-off-by: Fabiano Rosas Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-ID: <20231023182053.8711-7-farosas@suse.de> --- migration/migration.c | 8 ++++---- migration/rdma.c | 33 +++++++++++---------------------- migration/rdma.h | 6 ++++-- 3 files changed, 19 insertions(+), 28 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 846877135a..8c26672630 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -534,7 +534,7 @@ static void qemu_start_incoming_migration(const char *uri, Error **errp) fd_start_incoming_migration(saddr->u.fd.str, errp); } #ifdef CONFIG_RDMA - } else if (strstart(uri, "rdma:", &p)) { + } else if (channel->transport == MIGRATION_ADDRESS_TYPE_RDMA) { if (migrate_compress()) { error_setg(errp, "RDMA and compression can't be used together"); return; @@ -547,7 +547,7 @@ static void qemu_start_incoming_migration(const char *uri, Error **errp) error_setg(errp, "RDMA and multifd can't be used together"); return; } - rdma_start_incoming_migration(p, errp); + rdma_start_incoming_migration(&channel->u.rdma, errp); #endif } else if (strstart(uri, "exec:", &p)) { exec_start_incoming_migration(p, errp); @@ -1935,8 +1935,8 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk, fd_start_outgoing_migration(s, saddr->u.fd.str, &local_err); } #ifdef CONFIG_RDMA - } else if (strstart(uri, "rdma:", &p)) { - rdma_start_outgoing_migration(s, p, &local_err); + } else if (channel->transport == MIGRATION_ADDRESS_TYPE_RDMA) { + rdma_start_outgoing_migration(s, &channel->u.rdma, &local_err); #endif } else if (strstart(uri, "exec:", &p)) { exec_start_outgoing_migration(s, p, &local_err); diff --git a/migration/rdma.c b/migration/rdma.c index 2938db4f64..6a29e53daf 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -289,7 +289,6 @@ typedef struct RDMALocalBlocks { typedef struct RDMAContext { char *host; int port; - char *host_port; RDMAWorkRequestData wr_data[RDMA_WRID_MAX]; @@ -2431,9 +2430,7 @@ static void qemu_rdma_cleanup(RDMAContext *rdma) rdma->channel = NULL; } g_free(rdma->host); - g_free(rdma->host_port); rdma->host = NULL; - rdma->host_port = NULL; } @@ -2723,28 +2720,16 @@ static void qemu_rdma_return_path_dest_init(RDMAContext *rdma_return_path, rdma_return_path->is_return_path = true; } -static RDMAContext *qemu_rdma_data_init(const char *host_port, Error **errp) +static RDMAContext *qemu_rdma_data_init(InetSocketAddress *saddr, Error **errp) { RDMAContext *rdma = NULL; - InetSocketAddress *addr; rdma = g_new0(RDMAContext, 1); rdma->current_index = -1; rdma->current_chunk = -1; - addr = g_new(InetSocketAddress, 1); - if (!inet_parse(addr, host_port, NULL)) { - rdma->port = atoi(addr->port); - rdma->host = g_strdup(addr->host); - rdma->host_port = g_strdup(host_port); - } else { - error_setg(errp, "RDMA ERROR: bad RDMA migration address '%s'", - host_port); - g_free(rdma); - rdma = NULL; - } - - qapi_free_InetSocketAddress(addr); + rdma->host = g_strdup(saddr->host); + rdma->port = atoi(saddr->port); return rdma; } @@ -3353,6 +3338,7 @@ static int qemu_rdma_accept(RDMAContext *rdma) .private_data_len = sizeof(cap), }; RDMAContext *rdma_return_path = NULL; + g_autoptr(InetSocketAddress) isock = g_new0(InetSocketAddress, 1); struct rdma_cm_event *cm_event; struct ibv_context *verbs; int ret; @@ -3367,13 +3353,16 @@ static int qemu_rdma_accept(RDMAContext *rdma) goto err_rdma_dest_wait; } + isock->host = rdma->host; + isock->port = g_strdup_printf("%d", rdma->port); + /* * initialize the RDMAContext for return path for postcopy after first * connection request reached. */ if ((migrate_postcopy() || migrate_return_path()) && !rdma->is_return_path) { - rdma_return_path = qemu_rdma_data_init(rdma->host_port, NULL); + rdma_return_path = qemu_rdma_data_init(isock, NULL); if (rdma_return_path == NULL) { rdma_ack_cm_event(cm_event); goto err_rdma_dest_wait; @@ -4074,7 +4063,8 @@ static void rdma_accept_incoming_migration(void *opaque) } } -void rdma_start_incoming_migration(const char *host_port, Error **errp) +void rdma_start_incoming_migration(InetSocketAddress *host_port, + Error **errp) { MigrationState *s = migrate_get_current(); int ret; @@ -4118,13 +4108,12 @@ cleanup_rdma: err: if (rdma) { g_free(rdma->host); - g_free(rdma->host_port); } g_free(rdma); } void rdma_start_outgoing_migration(void *opaque, - const char *host_port, Error **errp) + InetSocketAddress *host_port, Error **errp) { MigrationState *s = opaque; RDMAContext *rdma_return_path = NULL; diff --git a/migration/rdma.h b/migration/rdma.h index 30b15b4466..a8d27f33b8 100644 --- a/migration/rdma.h +++ b/migration/rdma.h @@ -14,15 +14,17 @@ * */ +#include "qemu/sockets.h" + #ifndef QEMU_MIGRATION_RDMA_H #define QEMU_MIGRATION_RDMA_H #include "exec/memory.h" -void rdma_start_outgoing_migration(void *opaque, const char *host_port, +void rdma_start_outgoing_migration(void *opaque, InetSocketAddress *host_port, Error **errp); -void rdma_start_incoming_migration(const char *host_port, Error **errp); +void rdma_start_incoming_migration(InetSocketAddress *host_port, Error **errp); /* * Constants used by rdma return codes From cbab4face57b829e93e935b3bf52bf9f1bea0e42 Mon Sep 17 00:00:00 2001 From: Het Gala Date: Mon, 23 Oct 2023 15:20:46 -0300 Subject: [PATCH 334/974] migration: convert exec backend to accept MigrateAddress. Exec transport backend for 'migrate'/'migrate-incoming' QAPIs accept new wire protocol of MigrateAddress struct. It is achived by parsing 'uri' string and storing migration parameters required for exec connection into strList struct. Suggested-by: Aravind Retnakaran Signed-off-by: Het Gala Signed-off-by: Fabiano Rosas Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-ID: <20231023182053.8711-8-farosas@suse.de> --- migration/exec.c | 73 +++++++++++++++++++++++++++++++------------ migration/exec.h | 4 +-- migration/migration.c | 8 ++--- 3 files changed, 59 insertions(+), 26 deletions(-) diff --git a/migration/exec.c b/migration/exec.c index 32f5143dfd..47d2f3b8fb 100644 --- a/migration/exec.c +++ b/migration/exec.c @@ -39,20 +39,51 @@ const char *exec_get_cmd_path(void) } #endif -void exec_start_outgoing_migration(MigrationState *s, const char *command, Error **errp) +/* provides the length of strList */ +static int +str_list_length(strList *list) +{ + int len = 0; + strList *elem; + + for (elem = list; elem != NULL; elem = elem->next) { + len++; + } + + return len; +} + +static void +init_exec_array(strList *command, char **argv, Error **errp) +{ + int i = 0; + strList *lst; + + for (lst = command; lst; lst = lst->next) { + argv[i++] = lst->value; + } + + argv[i] = NULL; + return; +} + +void exec_start_outgoing_migration(MigrationState *s, strList *command, + Error **errp) { QIOChannel *ioc; -#ifdef WIN32 - const char *argv[] = { exec_get_cmd_path(), "/c", command, NULL }; -#else - const char *argv[] = { "/bin/sh", "-c", command, NULL }; -#endif + int length = str_list_length(command); + g_auto(GStrv) argv = (char **) g_new0(const char *, length + 1); - trace_migration_exec_outgoing(command); - ioc = QIO_CHANNEL(qio_channel_command_new_spawn(argv, - O_RDWR, - errp)); + init_exec_array(command, argv, errp); + g_autofree char *new_command = g_strjoinv(" ", (char **)argv); + + trace_migration_exec_outgoing(new_command); + ioc = QIO_CHANNEL( + qio_channel_command_new_spawn( + (const char * const *) g_steal_pointer(&argv), + O_RDWR, + errp)); if (!ioc) { return; } @@ -71,20 +102,22 @@ static gboolean exec_accept_incoming_migration(QIOChannel *ioc, return G_SOURCE_REMOVE; } -void exec_start_incoming_migration(const char *command, Error **errp) +void exec_start_incoming_migration(strList *command, Error **errp) { QIOChannel *ioc; -#ifdef WIN32 - const char *argv[] = { exec_get_cmd_path(), "/c", command, NULL }; -#else - const char *argv[] = { "/bin/sh", "-c", command, NULL }; -#endif + int length = str_list_length(command); + g_auto(GStrv) argv = (char **) g_new0(const char *, length + 1); - trace_migration_exec_incoming(command); - ioc = QIO_CHANNEL(qio_channel_command_new_spawn(argv, - O_RDWR, - errp)); + init_exec_array(command, argv, errp); + g_autofree char *new_command = g_strjoinv(" ", (char **)argv); + + trace_migration_exec_incoming(new_command); + ioc = QIO_CHANNEL( + qio_channel_command_new_spawn( + (const char * const *) g_steal_pointer(&argv), + O_RDWR, + errp)); if (!ioc) { return; } diff --git a/migration/exec.h b/migration/exec.h index 736cd71028..3107f205e3 100644 --- a/migration/exec.h +++ b/migration/exec.h @@ -23,8 +23,8 @@ #ifdef WIN32 const char *exec_get_cmd_path(void); #endif -void exec_start_incoming_migration(const char *host_port, Error **errp); +void exec_start_incoming_migration(strList *host_port, Error **errp); -void exec_start_outgoing_migration(MigrationState *s, const char *host_port, +void exec_start_outgoing_migration(MigrationState *s, strList *host_port, Error **errp); #endif diff --git a/migration/migration.c b/migration/migration.c index 8c26672630..0911fb310c 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -549,8 +549,8 @@ static void qemu_start_incoming_migration(const char *uri, Error **errp) } rdma_start_incoming_migration(&channel->u.rdma, errp); #endif - } else if (strstart(uri, "exec:", &p)) { - exec_start_incoming_migration(p, errp); + } else if (channel->transport == MIGRATION_ADDRESS_TYPE_EXEC) { + exec_start_incoming_migration(channel->u.exec.args, errp); } else if (strstart(uri, "file:", &p)) { file_start_incoming_migration(p, errp); } else { @@ -1938,8 +1938,8 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk, } else if (channel->transport == MIGRATION_ADDRESS_TYPE_RDMA) { rdma_start_outgoing_migration(s, &channel->u.rdma, &local_err); #endif - } else if (strstart(uri, "exec:", &p)) { - exec_start_outgoing_migration(s, p, &local_err); + } else if (channel->transport == MIGRATION_ADDRESS_TYPE_EXEC) { + exec_start_outgoing_migration(s, channel->u.exec.args, &local_err); } else if (strstart(uri, "file:", &p)) { file_start_outgoing_migration(s, p, &local_err); } else { From 02afba63e93729e3bdc2ffa900c4e8bc835e1950 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Mon, 23 Oct 2023 15:20:47 -0300 Subject: [PATCH 335/974] migration: Convert the file backend to the new QAPI syntax Convert the file: URI to accept a FileMigrationArgs to be compatible with the new migration QAPI. Signed-off-by: Fabiano Rosas Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-ID: <20231023182053.8711-9-farosas@suse.de> --- migration/file.c | 22 +++++++--------------- migration/file.h | 9 ++++++--- migration/migration.c | 10 ++++------ 3 files changed, 17 insertions(+), 24 deletions(-) diff --git a/migration/file.c b/migration/file.c index ec069ef329..5d4975f43e 100644 --- a/migration/file.c +++ b/migration/file.c @@ -36,20 +36,16 @@ int file_parse_offset(char *filespec, uint64_t *offsetp, Error **errp) return 0; } -void file_start_outgoing_migration(MigrationState *s, const char *filespec, - Error **errp) +void file_start_outgoing_migration(MigrationState *s, + FileMigrationArgs *file_args, Error **errp) { - g_autofree char *filename = g_strdup(filespec); g_autoptr(QIOChannelFile) fioc = NULL; - uint64_t offset = 0; + g_autofree char *filename = g_strdup(file_args->filename); + uint64_t offset = file_args->offset; QIOChannel *ioc; trace_migration_file_outgoing(filename); - if (file_parse_offset(filename, &offset, errp)) { - return; - } - fioc = qio_channel_file_new_path(filename, O_CREAT | O_WRONLY | O_TRUNC, 0600, errp); if (!fioc) { @@ -73,19 +69,15 @@ static gboolean file_accept_incoming_migration(QIOChannel *ioc, return G_SOURCE_REMOVE; } -void file_start_incoming_migration(const char *filespec, Error **errp) +void file_start_incoming_migration(FileMigrationArgs *file_args, Error **errp) { - g_autofree char *filename = g_strdup(filespec); + g_autofree char *filename = g_strdup(file_args->filename); QIOChannelFile *fioc = NULL; - uint64_t offset = 0; + uint64_t offset = file_args->offset; QIOChannel *ioc; trace_migration_file_incoming(filename); - if (file_parse_offset(filename, &offset, errp)) { - return; - } - fioc = qio_channel_file_new_path(filename, O_RDONLY, 0, errp); if (!fioc) { return; diff --git a/migration/file.h b/migration/file.h index 3888a57105..37d6a08bfc 100644 --- a/migration/file.h +++ b/migration/file.h @@ -7,9 +7,12 @@ #ifndef QEMU_MIGRATION_FILE_H #define QEMU_MIGRATION_FILE_H -void file_start_incoming_migration(const char *filename, Error **errp); -void file_start_outgoing_migration(MigrationState *s, const char *filename, - Error **errp); +#include "qapi/qapi-types-migration.h" + +void file_start_incoming_migration(FileMigrationArgs *file_args, Error **errp); + +void file_start_outgoing_migration(MigrationState *s, + FileMigrationArgs *file_args, Error **errp); int file_parse_offset(char *filespec, uint64_t *offsetp, Error **errp); #endif diff --git a/migration/migration.c b/migration/migration.c index 0911fb310c..04037b340c 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -508,7 +508,6 @@ static bool migrate_uri_parse(const char *uri, static void qemu_start_incoming_migration(const char *uri, Error **errp) { - const char *p = NULL; g_autoptr(MigrationAddress) channel = NULL; MigrationIncomingState *mis = migration_incoming_get_current(); @@ -551,8 +550,8 @@ static void qemu_start_incoming_migration(const char *uri, Error **errp) #endif } else if (channel->transport == MIGRATION_ADDRESS_TYPE_EXEC) { exec_start_incoming_migration(channel->u.exec.args, errp); - } else if (strstart(uri, "file:", &p)) { - file_start_incoming_migration(p, errp); + } else if (channel->transport == MIGRATION_ADDRESS_TYPE_FILE) { + file_start_incoming_migration(&channel->u.file, errp); } else { error_setg(errp, "unknown migration protocol: %s", uri); } @@ -1900,7 +1899,6 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk, bool resume_requested; Error *local_err = NULL; MigrationState *s = migrate_get_current(); - const char *p = NULL; g_autoptr(MigrationAddress) channel = NULL; /* URI is not suitable for migration? */ @@ -1940,8 +1938,8 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk, #endif } else if (channel->transport == MIGRATION_ADDRESS_TYPE_EXEC) { exec_start_outgoing_migration(s, channel->u.exec.args, &local_err); - } else if (strstart(uri, "file:", &p)) { - file_start_outgoing_migration(s, p, &local_err); + } else if (channel->transport == MIGRATION_ADDRESS_TYPE_FILE) { + file_start_outgoing_migration(s, &channel->u.file, &local_err); } else { error_setg(&local_err, QERR_INVALID_PARAMETER_VALUE, "uri", "a valid migration protocol"); From 074dbce5fcce597e61ff6c44d2977458f0ba2f29 Mon Sep 17 00:00:00 2001 From: Het Gala Date: Mon, 23 Oct 2023 15:20:48 -0300 Subject: [PATCH 336/974] migration: New migrate and migrate-incoming argument 'channels' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MigrateChannelList allows to connect accross multiple interfaces. Add MigrateChannelList struct as argument to migration QAPIs. We plan to include multiple channels in future, to connnect multiple interfaces. Hence, we choose 'MigrateChannelList' as the new argument over 'MigrateChannel' to make migration QAPIs future proof. Suggested-by: Aravind Retnakaran Signed-off-by: Het Gala Acked-by: Markus Armbruster Reviewed-by: Daniel P. Berrangé Signed-off-by: Fabiano Rosas Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-ID: <20231023182053.8711-10-farosas@suse.de> --- migration/migration-hmp-cmds.c | 6 +- migration/migration.c | 56 ++++++++++++++-- qapi/migration.json | 113 ++++++++++++++++++++++++++++++++- system/vl.c | 2 +- 4 files changed, 167 insertions(+), 10 deletions(-) diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index a170440991..6ec778a990 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -452,7 +452,7 @@ void hmp_migrate_incoming(Monitor *mon, const QDict *qdict) Error *err = NULL; const char *uri = qdict_get_str(qdict, "uri"); - qmp_migrate_incoming(uri, &err); + qmp_migrate_incoming(uri, false, NULL, &err); hmp_handle_error(mon, err); } @@ -764,8 +764,8 @@ void hmp_migrate(Monitor *mon, const QDict *qdict) " use blockdev-mirror with NBD instead"); } - qmp_migrate(uri, !!blk, blk, !!inc, inc, - false, false, true, resume, &err); + qmp_migrate(uri, false, NULL, !!blk, blk, !!inc, inc, + false, false, true, resume, &err); if (hmp_handle_error(mon, err)) { return; } diff --git a/migration/migration.c b/migration/migration.c index 04037b340c..a9cd5e2d5d 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -506,11 +506,34 @@ static bool migrate_uri_parse(const char *uri, return true; } -static void qemu_start_incoming_migration(const char *uri, Error **errp) +static void qemu_start_incoming_migration(const char *uri, bool has_channels, + MigrationChannelList *channels, + Error **errp) { g_autoptr(MigrationAddress) channel = NULL; MigrationIncomingState *mis = migration_incoming_get_current(); + /* + * Having preliminary checks for uri and channel + */ + if (has_channels) { + error_setg(errp, "'channels' argument should not be set yet."); + return; + } + + if (uri && has_channels) { + error_setg(errp, "'uri' and 'channels' arguments are mutually " + "exclusive; exactly one of the two should be present in " + "'migrate-incoming' qmp command "); + return; + } + + if (!uri && !has_channels) { + error_setg(errp, "neither 'uri' or 'channels' argument are " + "specified in 'migrate-incoming' qmp command "); + return; + } + /* URI is not suitable for migration? */ if (!migration_channels_and_uri_compatible(uri, errp)) { return; @@ -1681,7 +1704,8 @@ void migrate_del_blocker(Error **reasonp) } } -void qmp_migrate_incoming(const char *uri, Error **errp) +void qmp_migrate_incoming(const char *uri, bool has_channels, + MigrationChannelList *channels, Error **errp) { Error *local_err = NULL; static bool once = true; @@ -1699,7 +1723,7 @@ void qmp_migrate_incoming(const char *uri, Error **errp) return; } - qemu_start_incoming_migration(uri, &local_err); + qemu_start_incoming_migration(uri, has_channels, channels, &local_err); if (local_err) { yank_unregister_instance(MIGRATION_YANK_INSTANCE); @@ -1735,7 +1759,7 @@ void qmp_migrate_recover(const char *uri, Error **errp) * only re-setup the migration stream and poke existing migration * to continue using that newly established channel. */ - qemu_start_incoming_migration(uri, errp); + qemu_start_incoming_migration(uri, false, NULL, errp); } void qmp_migrate_pause(Error **errp) @@ -1892,7 +1916,8 @@ static bool migrate_prepare(MigrationState *s, bool blk, bool blk_inc, return true; } -void qmp_migrate(const char *uri, bool has_blk, bool blk, +void qmp_migrate(const char *uri, bool has_channels, + MigrationChannelList *channels, bool has_blk, bool blk, bool has_inc, bool inc, bool has_detach, bool detach, bool has_resume, bool resume, Error **errp) { @@ -1901,6 +1926,27 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk, MigrationState *s = migrate_get_current(); g_autoptr(MigrationAddress) channel = NULL; + /* + * Having preliminary checks for uri and channel + */ + if (has_channels) { + error_setg(errp, "'channels' argument should not be set yet."); + return; + } + + if (uri && has_channels) { + error_setg(errp, "'uri' and 'channels' arguments are mutually " + "exclusive; exactly one of the two should be present in " + "'migrate' qmp command "); + return; + } + + if (!uri && !has_channels) { + error_setg(errp, "neither 'uri' or 'channels' argument are " + "specified in 'migrate' qmp command "); + return; + } + /* URI is not suitable for migration? */ if (!migration_channels_and_uri_compatible(uri, errp)) { return; diff --git a/qapi/migration.json b/qapi/migration.json index 182b150b87..975761eebd 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -1641,6 +1641,34 @@ 'rdma': 'InetSocketAddress', 'file': 'FileMigrationArgs' } } +## +# @MigrationChannelType: +# +# The migration channel-type request options. +# +# @main: Main outbound migration channel. +# +# Since 8.1 +## +{ 'enum': 'MigrationChannelType', + 'data': [ 'main' ] } + +## +# @MigrationChannel: +# +# Migration stream channel parameters. +# +# @channel-type: Channel type for transfering packet information. +# +# @addr: Migration endpoint configuration on destination interface. +# +# Since 8.1 +## +{ 'struct': 'MigrationChannel', + 'data': { + 'channel-type': 'MigrationChannelType', + 'addr': 'MigrationAddress' } } + ## # @migrate: # @@ -1648,6 +1676,9 @@ # # @uri: the Uniform Resource Identifier of the destination VM # +# @channels: list of migration stream channels with each stream in the +# list connected to a destination interface endpoint. +# # @blk: do block migration (full disk copy) # # @inc: incremental disk copy migration @@ -1677,13 +1708,57 @@ # 3. The user Monitor's "detach" argument is invalid in QMP and should # not be used # +# 4. The uri argument should have the Uniform Resource Identifier of +# default destination VM. This connection will be bound to default +# network. +# +# 5. For now, number of migration streams is restricted to one, i.e +# number of items in 'channels' list is just 1. +# +# 6. The 'uri' and 'channels' arguments are mutually exclusive; +# exactly one of the two should be present. +# # Example: # # -> { "execute": "migrate", "arguments": { "uri": "tcp:0:4446" } } # <- { "return": {} } +# -> { "execute": "migrate", +# "arguments": { +# "channels": [ { "channel-type": "main", +# "addr": { "transport": "socket", +# "type": "inet", +# "host": "10.12.34.9", +# "port": "1050" } } ] } } +# <- { "return": {} } +# +# -> { "execute": "migrate", +# "arguments": { +# "channels": [ { "channel-type": "main", +# "addr": { "transport": "exec", +# "args": [ "/bin/nc", "-p", "6000", +# "/some/sock" ] } } ] } } +# <- { "return": {} } +# +# -> { "execute": "migrate", +# "arguments": { +# "channels": [ { "channel-type": "main", +# "addr": { "transport": "rdma", +# "host": "10.12.34.9", +# "port": "1050" } } ] } } +# <- { "return": {} } +# +# -> { "execute": "migrate", +# "arguments": { +# "channels": [ { "channel-type": "main", +# "addr": { "transport": "file", +# "filename": "/tmp/migfile", +# "offset": "0x1000" } } ] } } +# <- { "return": {} } +# ## { 'command': 'migrate', 'data': {'uri': 'str', + '*channels': [ 'MigrationChannel' ], '*blk': { 'type': 'bool', 'features': [ 'deprecated' ] }, '*inc': { 'type': 'bool', 'features': [ 'deprecated' ] }, '*detach': 'bool', '*resume': 'bool' } } @@ -1697,6 +1772,9 @@ # @uri: The Uniform Resource Identifier identifying the source or # address to listen on # +# @channels: list of migration stream channels with each stream in the +# list connected to a destination interface endpoint. +# # Returns: nothing on success # # Since: 2.3 @@ -1712,13 +1790,46 @@ # # 3. The uri format is the same as for -incoming # +# 5. For now, number of migration streams is restricted to one, i.e +# number of items in 'channels' list is just 1. +# +# 4. The 'uri' and 'channels' arguments are mutually exclusive; +# exactly one of the two should be present. +# # Example: # # -> { "execute": "migrate-incoming", # "arguments": { "uri": "tcp::4446" } } # <- { "return": {} } +# +# -> { "execute": "migrate", +# "arguments": { +# "channels": [ { "channel-type": "main", +# "addr": { "transport": "socket", +# "type": "inet", +# "host": "10.12.34.9", +# "port": "1050" } } ] } } +# <- { "return": {} } +# +# -> { "execute": "migrate", +# "arguments": { +# "channels": [ { "channel-type": "main", +# "addr": { "transport": "exec", +# "args": [ "/bin/nc", "-p", "6000", +# "/some/sock" ] } } ] } } +# <- { "return": {} } +# +# -> { "execute": "migrate", +# "arguments": { +# "channels": [ { "channel-type": "main", +# "addr": { "transport": "rdma", +# "host": "10.12.34.9", +# "port": "1050" } } ] } } +# <- { "return": {} } ## -{ 'command': 'migrate-incoming', 'data': {'uri': 'str' } } +{ 'command': 'migrate-incoming', + 'data': {'*uri': 'str', + '*channels': [ 'MigrationChannel' ] } } ## # @xen-save-devices-state: diff --git a/system/vl.c b/system/vl.c index 92d29bf521..3fb569254a 100644 --- a/system/vl.c +++ b/system/vl.c @@ -2702,7 +2702,7 @@ void qmp_x_exit_preconfig(Error **errp) if (incoming) { Error *local_err = NULL; if (strcmp(incoming, "defer") != 0) { - qmp_migrate_incoming(incoming, &local_err); + qmp_migrate_incoming(incoming, false, NULL, &local_err); if (local_err) { error_reportf_err(local_err, "-incoming %s: ", incoming); exit(1); From d95533e1cdcc06c383b5ca113bfde02152cb1ab5 Mon Sep 17 00:00:00 2001 From: Het Gala Date: Mon, 23 Oct 2023 15:20:50 -0300 Subject: [PATCH 337/974] migration: modify migration_channels_and_uri_compatible() for new QAPI syntax MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit migration_channels_and_uri_compatible() check for transport mechanism suitable for multifd migration gets executed when the caller calls old uri syntax. It needs it to be run when using the modern MigrateChannel QAPI syntax too. After URI -> 'MigrateChannel' : migration_channels_and_uri_compatible() -> migration_channels_and_transport_compatible() passes object as argument and check for valid transport mechanism. Suggested-by: Aravind Retnakaran Signed-off-by: Het Gala Reviewed-by: Daniel P. Berrangé Signed-off-by: Fabiano Rosas Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-ID: <20231023182053.8711-12-farosas@suse.de> --- migration/migration.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index a9cd5e2d5d..70725a89ab 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -128,17 +128,20 @@ static bool migration_needs_multiple_sockets(void) return migrate_multifd() || migrate_postcopy_preempt(); } -static bool uri_supports_multi_channels(const char *uri) +static bool transport_supports_multi_channels(SocketAddress *saddr) { - return strstart(uri, "tcp:", NULL) || strstart(uri, "unix:", NULL) || - strstart(uri, "vsock:", NULL); + return saddr->type == SOCKET_ADDRESS_TYPE_INET || + saddr->type == SOCKET_ADDRESS_TYPE_UNIX || + saddr->type == SOCKET_ADDRESS_TYPE_VSOCK; } static bool -migration_channels_and_uri_compatible(const char *uri, Error **errp) +migration_channels_and_transport_compatible(MigrationAddress *addr, + Error **errp) { if (migration_needs_multiple_sockets() && - !uri_supports_multi_channels(uri)) { + (addr->transport == MIGRATION_ADDRESS_TYPE_SOCKET) && + !transport_supports_multi_channels(&addr->u.socket)) { error_setg(errp, "Migration requires multi-channel URIs (e.g. tcp)"); return false; } @@ -534,12 +537,12 @@ static void qemu_start_incoming_migration(const char *uri, bool has_channels, return; } - /* URI is not suitable for migration? */ - if (!migration_channels_and_uri_compatible(uri, errp)) { + if (uri && !migrate_uri_parse(uri, &channel, errp)) { return; } - if (uri && !migrate_uri_parse(uri, &channel, errp)) { + /* transport mechanism not suitable for migration? */ + if (!migration_channels_and_transport_compatible(channel, errp)) { return; } @@ -1947,12 +1950,12 @@ void qmp_migrate(const char *uri, bool has_channels, return; } - /* URI is not suitable for migration? */ - if (!migration_channels_and_uri_compatible(uri, errp)) { + if (!migrate_uri_parse(uri, &channel, errp)) { return; } - if (!migrate_uri_parse(uri, &channel, errp)) { + /* transport mechanism not suitable for migration? */ + if (!migration_channels_and_transport_compatible(channel, errp)) { return; } From 5994024fd132cf2eb53ca2385fd198b1bd33d446 Mon Sep 17 00:00:00 2001 From: Het Gala Date: Mon, 23 Oct 2023 15:20:51 -0300 Subject: [PATCH 338/974] migration: Implement MigrateChannelList to qmp migration flow. Integrate MigrateChannelList with all transport backends (socket, exec and rdma) for both src and dest migration endpoints for qmp migration. For current series, limit the size of MigrateChannelList to single element (single interface) as runtime check. Suggested-by: Aravind Retnakaran Signed-off-by: Het Gala Signed-off-by: Fabiano Rosas Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-ID: <20231023182053.8711-13-farosas@suse.de> --- migration/migration.c | 101 +++++++++++++++++++++++------------------- 1 file changed, 55 insertions(+), 46 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 70725a89ab..10a6f2fb12 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -458,9 +458,10 @@ void migrate_add_address(SocketAddress *address) } static bool migrate_uri_parse(const char *uri, - MigrationAddress **channel, + MigrationChannel **channel, Error **errp) { + g_autoptr(MigrationChannel) val = g_new0(MigrationChannel, 1); g_autoptr(MigrationAddress) addr = g_new0(MigrationAddress, 1); SocketAddress *saddr = NULL; InetSocketAddress *isock = &addr->u.rdma; @@ -505,7 +506,9 @@ static bool migrate_uri_parse(const char *uri, return false; } - *channel = g_steal_pointer(&addr); + val->channel_type = MIGRATION_CHANNEL_TYPE_MAIN; + val->addr = g_steal_pointer(&addr); + *channel = g_steal_pointer(&val); return true; } @@ -513,44 +516,47 @@ static void qemu_start_incoming_migration(const char *uri, bool has_channels, MigrationChannelList *channels, Error **errp) { - g_autoptr(MigrationAddress) channel = NULL; + MigrationChannel *channel = NULL; + MigrationAddress *addr = NULL; MigrationIncomingState *mis = migration_incoming_get_current(); /* * Having preliminary checks for uri and channel */ - if (has_channels) { - error_setg(errp, "'channels' argument should not be set yet."); - return; - } - if (uri && has_channels) { error_setg(errp, "'uri' and 'channels' arguments are mutually " "exclusive; exactly one of the two should be present in " "'migrate-incoming' qmp command "); return; - } - - if (!uri && !has_channels) { + } else if (channels) { + /* To verify that Migrate channel list has only item */ + if (channels->next) { + error_setg(errp, "Channel list has more than one entries"); + return; + } + channel = channels->value; + } else if (uri) { + /* caller uses the old URI syntax */ + if (!migrate_uri_parse(uri, &channel, errp)) { + return; + } + } else { error_setg(errp, "neither 'uri' or 'channels' argument are " "specified in 'migrate-incoming' qmp command "); return; } - - if (uri && !migrate_uri_parse(uri, &channel, errp)) { - return; - } + addr = channel->addr; /* transport mechanism not suitable for migration? */ - if (!migration_channels_and_transport_compatible(channel, errp)) { + if (!migration_channels_and_transport_compatible(addr, errp)) { return; } migrate_set_state(&mis->state, MIGRATION_STATUS_NONE, MIGRATION_STATUS_SETUP); - if (channel->transport == MIGRATION_ADDRESS_TYPE_SOCKET) { - SocketAddress *saddr = &channel->u.socket; + if (addr->transport == MIGRATION_ADDRESS_TYPE_SOCKET) { + SocketAddress *saddr = &addr->u.socket; if (saddr->type == SOCKET_ADDRESS_TYPE_INET || saddr->type == SOCKET_ADDRESS_TYPE_UNIX || saddr->type == SOCKET_ADDRESS_TYPE_VSOCK) { @@ -559,7 +565,7 @@ static void qemu_start_incoming_migration(const char *uri, bool has_channels, fd_start_incoming_migration(saddr->u.fd.str, errp); } #ifdef CONFIG_RDMA - } else if (channel->transport == MIGRATION_ADDRESS_TYPE_RDMA) { + } else if (addr->transport == MIGRATION_ADDRESS_TYPE_RDMA) { if (migrate_compress()) { error_setg(errp, "RDMA and compression can't be used together"); return; @@ -572,12 +578,12 @@ static void qemu_start_incoming_migration(const char *uri, bool has_channels, error_setg(errp, "RDMA and multifd can't be used together"); return; } - rdma_start_incoming_migration(&channel->u.rdma, errp); + rdma_start_incoming_migration(&addr->u.rdma, errp); #endif - } else if (channel->transport == MIGRATION_ADDRESS_TYPE_EXEC) { - exec_start_incoming_migration(channel->u.exec.args, errp); - } else if (channel->transport == MIGRATION_ADDRESS_TYPE_FILE) { - file_start_incoming_migration(&channel->u.file, errp); + } else if (addr->transport == MIGRATION_ADDRESS_TYPE_EXEC) { + exec_start_incoming_migration(addr->u.exec.args, errp); + } else if (addr->transport == MIGRATION_ADDRESS_TYPE_FILE) { + file_start_incoming_migration(&addr->u.file, errp); } else { error_setg(errp, "unknown migration protocol: %s", uri); } @@ -1927,35 +1933,38 @@ void qmp_migrate(const char *uri, bool has_channels, bool resume_requested; Error *local_err = NULL; MigrationState *s = migrate_get_current(); - g_autoptr(MigrationAddress) channel = NULL; + MigrationChannel *channel = NULL; + MigrationAddress *addr = NULL; /* * Having preliminary checks for uri and channel */ - if (has_channels) { - error_setg(errp, "'channels' argument should not be set yet."); - return; - } - if (uri && has_channels) { error_setg(errp, "'uri' and 'channels' arguments are mutually " "exclusive; exactly one of the two should be present in " "'migrate' qmp command "); return; - } - - if (!uri && !has_channels) { + } else if (channels) { + /* To verify that Migrate channel list has only item */ + if (channels->next) { + error_setg(errp, "Channel list has more than one entries"); + return; + } + channel = channels->value; + } else if (uri) { + /* caller uses the old URI syntax */ + if (!migrate_uri_parse(uri, &channel, errp)) { + return; + } + } else { error_setg(errp, "neither 'uri' or 'channels' argument are " "specified in 'migrate' qmp command "); return; } - - if (!migrate_uri_parse(uri, &channel, errp)) { - return; - } + addr = channel->addr; /* transport mechanism not suitable for migration? */ - if (!migration_channels_and_transport_compatible(channel, errp)) { + if (!migration_channels_and_transport_compatible(addr, errp)) { return; } @@ -1972,8 +1981,8 @@ void qmp_migrate(const char *uri, bool has_channels, } } - if (channel->transport == MIGRATION_ADDRESS_TYPE_SOCKET) { - SocketAddress *saddr = &channel->u.socket; + if (addr->transport == MIGRATION_ADDRESS_TYPE_SOCKET) { + SocketAddress *saddr = &addr->u.socket; if (saddr->type == SOCKET_ADDRESS_TYPE_INET || saddr->type == SOCKET_ADDRESS_TYPE_UNIX || saddr->type == SOCKET_ADDRESS_TYPE_VSOCK) { @@ -1982,13 +1991,13 @@ void qmp_migrate(const char *uri, bool has_channels, fd_start_outgoing_migration(s, saddr->u.fd.str, &local_err); } #ifdef CONFIG_RDMA - } else if (channel->transport == MIGRATION_ADDRESS_TYPE_RDMA) { - rdma_start_outgoing_migration(s, &channel->u.rdma, &local_err); + } else if (addr->transport == MIGRATION_ADDRESS_TYPE_RDMA) { + rdma_start_outgoing_migration(s, &addr->u.rdma, &local_err); #endif - } else if (channel->transport == MIGRATION_ADDRESS_TYPE_EXEC) { - exec_start_outgoing_migration(s, channel->u.exec.args, &local_err); - } else if (channel->transport == MIGRATION_ADDRESS_TYPE_FILE) { - file_start_outgoing_migration(s, &channel->u.file, &local_err); + } else if (addr->transport == MIGRATION_ADDRESS_TYPE_EXEC) { + exec_start_outgoing_migration(s, addr->u.exec.args, &local_err); + } else if (addr->transport == MIGRATION_ADDRESS_TYPE_FILE) { + file_start_outgoing_migration(s, &addr->u.file, &local_err); } else { error_setg(&local_err, QERR_INVALID_PARAMETER_VALUE, "uri", "a valid migration protocol"); From 967f2de5c9ec224480b940d5c9fc207ca625b7d8 Mon Sep 17 00:00:00 2001 From: Het Gala Date: Mon, 23 Oct 2023 15:20:52 -0300 Subject: [PATCH 339/974] migration: Implement MigrateChannelList to hmp migration flow. Integrate MigrateChannelList with all transport backends (socket, exec and rdma) for both src and dest migration endpoints for hmp migration. Suggested-by: Aravind Retnakaran Signed-off-by: Het Gala Signed-off-by: Fabiano Rosas Message-ID: <20231023182053.8711-14-farosas@suse.de> Signed-off-by: Juan Quintela --- migration/migration-hmp-cmds.c | 23 +++++++++++++++++++++-- migration/migration.c | 5 ++--- migration/migration.h | 3 ++- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index 6ec778a990..86ae832176 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -451,9 +451,18 @@ void hmp_migrate_incoming(Monitor *mon, const QDict *qdict) { Error *err = NULL; const char *uri = qdict_get_str(qdict, "uri"); + MigrationChannelList *caps = NULL; + g_autoptr(MigrationChannel) channel = NULL; - qmp_migrate_incoming(uri, false, NULL, &err); + if (!migrate_uri_parse(uri, &channel, &err)) { + goto end; + } + QAPI_LIST_PREPEND(caps, g_steal_pointer(&channel)); + qmp_migrate_incoming(NULL, true, caps, &err); + qapi_free_MigrationChannelList(caps); + +end: hmp_handle_error(mon, err); } @@ -753,6 +762,8 @@ void hmp_migrate(Monitor *mon, const QDict *qdict) bool resume = qdict_get_try_bool(qdict, "resume", false); const char *uri = qdict_get_str(qdict, "uri"); Error *err = NULL; + MigrationChannelList *caps = NULL; + g_autoptr(MigrationChannel) channel = NULL; if (inc) { warn_report("option '-i' is deprecated;" @@ -764,12 +775,20 @@ void hmp_migrate(Monitor *mon, const QDict *qdict) " use blockdev-mirror with NBD instead"); } - qmp_migrate(uri, false, NULL, !!blk, blk, !!inc, inc, + if (!migrate_uri_parse(uri, &channel, &err)) { + hmp_handle_error(mon, err); + return; + } + QAPI_LIST_PREPEND(caps, g_steal_pointer(&channel)); + + qmp_migrate(NULL, true, caps, !!blk, blk, !!inc, inc, false, false, true, resume, &err); if (hmp_handle_error(mon, err)) { return; } + qapi_free_MigrationChannelList(caps); + if (!detach) { HMPMigrationStatus *status; diff --git a/migration/migration.c b/migration/migration.c index 10a6f2fb12..28a34c9068 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -457,9 +457,8 @@ void migrate_add_address(SocketAddress *address) QAPI_CLONE(SocketAddress, address)); } -static bool migrate_uri_parse(const char *uri, - MigrationChannel **channel, - Error **errp) +bool migrate_uri_parse(const char *uri, MigrationChannel **channel, + Error **errp) { g_autoptr(MigrationChannel) val = g_new0(MigrationChannel, 1); g_autoptr(MigrationAddress) addr = g_new0(MigrationAddress, 1); diff --git a/migration/migration.h b/migration/migration.h index af8c965b7f..cf2c9c88e0 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -520,7 +520,8 @@ bool check_dirty_bitmap_mig_alias_map(const BitmapMigrationNodeAliasList *bbm, Error **errp); void migrate_add_address(SocketAddress *address); - +bool migrate_uri_parse(const char *uri, MigrationChannel **channel, + Error **errp); int foreach_not_ignored_block(RAMBlockIterFunc func, void *opaque); #define qemu_ram_foreach_block \ From 8e3766eefbb4036cbc280c1f1a0d28537929f7fb Mon Sep 17 00:00:00 2001 From: Het Gala Date: Mon, 23 Oct 2023 15:20:53 -0300 Subject: [PATCH 340/974] migration: modify test_multifd_tcp_none() to use new QAPI syntax. modify multifd tcp common test to incorporate the new QAPI syntax defined. Suggested-by: Aravind Retnakaran Signed-off-by: Het Gala Signed-off-by: Fabiano Rosas Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-ID: <20231023182053.8711-15-farosas@suse.de> --- tests/qtest/migration-test.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 047b7194df..e803b46039 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -1310,7 +1310,12 @@ static int migrate_postcopy_prepare(QTestState **from_ptr, migrate_prepare_for_dirty_mem(from); qtest_qmp_assert_success(to, "{ 'execute': 'migrate-incoming'," - " 'arguments': { 'uri': 'tcp:127.0.0.1:0' }}"); + " 'arguments': { " + " 'channels': [ { 'channel-type': 'main'," + " 'addr': { 'transport': 'socket'," + " 'type': 'inet'," + " 'host': '127.0.0.1'," + " 'port': '0' } } ] } }"); /* Wait for the first serial output from the source */ wait_for_serial("src_serial"); From 5cfea2487d0efe03ea2b80654090eb7581cc26aa Mon Sep 17 00:00:00 2001 From: Marielle Novastrider Date: Sun, 29 Oct 2023 21:00:58 +0000 Subject: [PATCH 341/974] linux-user/elfload: Add missing arm64 hwcap values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Specifically DIT, LSE2, and MTE3. We already expose detection of these via the CPUID interface, but missed these from ELF hwcaps. Signed-off-by: Marielle Novastrider Reviewed-by: Philippe Mathieu-Daudé Message-id: 20231029210058.38986-1-marielle@novastrider.com Reviewed-by: Peter Maydell [PMM: fixed conflict with feature tests moving to cpu-features.h] Signed-off-by: Peter Maydell --- linux-user/elfload.c | 3 +++ target/arm/cpu-features.h | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 8761f9e26b..3f3975352a 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -800,12 +800,14 @@ uint32_t get_elf_hwcap(void) GET_FEATURE_ID(aa64_sm4, ARM_HWCAP_A64_SM4); GET_FEATURE_ID(aa64_fp16, ARM_HWCAP_A64_FPHP | ARM_HWCAP_A64_ASIMDHP); GET_FEATURE_ID(aa64_atomics, ARM_HWCAP_A64_ATOMICS); + GET_FEATURE_ID(aa64_lse2, ARM_HWCAP_A64_USCAT); GET_FEATURE_ID(aa64_rdm, ARM_HWCAP_A64_ASIMDRDM); GET_FEATURE_ID(aa64_dp, ARM_HWCAP_A64_ASIMDDP); GET_FEATURE_ID(aa64_fcma, ARM_HWCAP_A64_FCMA); GET_FEATURE_ID(aa64_sve, ARM_HWCAP_A64_SVE); GET_FEATURE_ID(aa64_pauth, ARM_HWCAP_A64_PACA | ARM_HWCAP_A64_PACG); GET_FEATURE_ID(aa64_fhm, ARM_HWCAP_A64_ASIMDFHM); + GET_FEATURE_ID(aa64_dit, ARM_HWCAP_A64_DIT); GET_FEATURE_ID(aa64_jscvt, ARM_HWCAP_A64_JSCVT); GET_FEATURE_ID(aa64_sb, ARM_HWCAP_A64_SB); GET_FEATURE_ID(aa64_condm_4, ARM_HWCAP_A64_FLAGM); @@ -839,6 +841,7 @@ uint32_t get_elf_hwcap2(void) GET_FEATURE_ID(aa64_rndr, ARM_HWCAP2_A64_RNG); GET_FEATURE_ID(aa64_bti, ARM_HWCAP2_A64_BTI); GET_FEATURE_ID(aa64_mte, ARM_HWCAP2_A64_MTE); + GET_FEATURE_ID(aa64_mte3, ARM_HWCAP2_A64_MTE3); GET_FEATURE_ID(aa64_sme, (ARM_HWCAP2_A64_SME | ARM_HWCAP2_A64_SME_F32F32 | ARM_HWCAP2_A64_SME_B16F32 | diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index 66212cd7ec..954d358268 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -669,6 +669,11 @@ static inline bool isar_feature_aa64_mte(const ARMISARegisters *id) return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, MTE) >= 2; } +static inline bool isar_feature_aa64_mte3(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, MTE) >= 3; +} + static inline bool isar_feature_aa64_sme(const ARMISARegisters *id) { return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, SME) != 0; From c45460decbda7f94d188d2e18ead91b7c60d4e24 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 30 Oct 2023 11:47:57 +0000 Subject: [PATCH 342/974] hw/input/stellaris_input: Rename to stellaris_gamepad MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This source file implements a stellaris gamepad device; rename it so that it is a closer match to the device name. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-id: 20231030114802.3671871-2-peter.maydell@linaro.org --- hw/arm/Kconfig | 2 +- hw/arm/stellaris.c | 2 +- hw/input/Kconfig | 2 +- hw/input/meson.build | 2 +- hw/input/{stellaris_input.c => stellaris_gamepad.c} | 2 +- include/hw/input/{gamepad.h => stellaris_gamepad.h} | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) rename hw/input/{stellaris_input.c => stellaris_gamepad.c} (98%) rename include/hw/input/{gamepad.h => stellaris_gamepad.h} (77%) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 7e68348440..841f3131ea 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -291,7 +291,7 @@ config STELLARIS select SSD0303 # OLED display select SSD0323 # OLED display select SSI_SD - select STELLARIS_INPUT + select STELLARIS_GAMEPAD select STELLARIS_ENET # ethernet select STELLARIS_GPTM # general purpose timer module select UNIMP diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index aa5b0ddfaa..96585dd710 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -23,7 +23,7 @@ #include "sysemu/sysemu.h" #include "hw/arm/armv7m.h" #include "hw/char/pl011.h" -#include "hw/input/gamepad.h" +#include "hw/input/stellaris_gamepad.h" #include "hw/irq.h" #include "hw/watchdog/cmsdk-apb-watchdog.h" #include "migration/vmstate.h" diff --git a/hw/input/Kconfig b/hw/input/Kconfig index 55865bb386..f86e98c829 100644 --- a/hw/input/Kconfig +++ b/hw/input/Kconfig @@ -20,7 +20,7 @@ config PL050 config PS2 bool -config STELLARIS_INPUT +config STELLARIS_GAMEPAD bool config TSC2005 diff --git a/hw/input/meson.build b/hw/input/meson.build index c0d4482180..640556bbbc 100644 --- a/hw/input/meson.build +++ b/hw/input/meson.build @@ -5,7 +5,7 @@ system_ss.add(when: 'CONFIG_LM832X', if_true: files('lm832x.c')) system_ss.add(when: 'CONFIG_PCKBD', if_true: files('pckbd.c')) system_ss.add(when: 'CONFIG_PL050', if_true: files('pl050.c')) system_ss.add(when: 'CONFIG_PS2', if_true: files('ps2.c')) -system_ss.add(when: 'CONFIG_STELLARIS_INPUT', if_true: files('stellaris_input.c')) +system_ss.add(when: 'CONFIG_STELLARIS_GAMEPAD', if_true: files('stellaris_gamepad.c')) system_ss.add(when: 'CONFIG_TSC2005', if_true: files('tsc2005.c')) system_ss.add(when: 'CONFIG_VIRTIO_INPUT', if_true: files('virtio-input.c')) diff --git a/hw/input/stellaris_input.c b/hw/input/stellaris_gamepad.c similarity index 98% rename from hw/input/stellaris_input.c rename to hw/input/stellaris_gamepad.c index e6ee5e11f1..3bab557cab 100644 --- a/hw/input/stellaris_input.c +++ b/hw/input/stellaris_gamepad.c @@ -8,7 +8,7 @@ */ #include "qemu/osdep.h" -#include "hw/input/gamepad.h" +#include "hw/input/stellaris_gamepad.h" #include "hw/irq.h" #include "migration/vmstate.h" #include "ui/console.h" diff --git a/include/hw/input/gamepad.h b/include/hw/input/stellaris_gamepad.h similarity index 77% rename from include/hw/input/gamepad.h rename to include/hw/input/stellaris_gamepad.h index 6f6aa2406a..23cfd3c95f 100644 --- a/include/hw/input/gamepad.h +++ b/include/hw/input/stellaris_gamepad.h @@ -8,11 +8,11 @@ * See the COPYING file in the top-level directory. */ -#ifndef HW_INPUT_GAMEPAD_H -#define HW_INPUT_GAMEPAD_H +#ifndef HW_INPUT_STELLARIS_GAMEPAD_H +#define HW_INPUT_STELLARIS_GAMEPAD_H -/* stellaris_input.c */ +/* stellaris_gamepad.c */ void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode); #endif From 281e461820db96a017ebf0fc36474d36feb33902 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 30 Oct 2023 11:47:58 +0000 Subject: [PATCH 343/974] hw/input/stellaris_gamepad: Rename structs to our usual convention MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the structs in stellaris_gamepad.c to our now-standard CamelCase convention. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-id: 20231030114802.3671871-3-peter.maydell@linaro.org --- hw/input/stellaris_gamepad.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/hw/input/stellaris_gamepad.c b/hw/input/stellaris_gamepad.c index 3bab557cab..377101a403 100644 --- a/hw/input/stellaris_gamepad.c +++ b/hw/input/stellaris_gamepad.c @@ -17,17 +17,17 @@ typedef struct { qemu_irq irq; int keycode; uint8_t pressed; -} gamepad_button; +} StellarisGamepadButton; typedef struct { - gamepad_button *buttons; + StellarisGamepadButton *buttons; int num_buttons; int extension; -} gamepad_state; +} StellarisGamepad; static void stellaris_gamepad_put_key(void * opaque, int keycode) { - gamepad_state *s = (gamepad_state *)opaque; + StellarisGamepad *s = (StellarisGamepad *)opaque; int i; int down; @@ -55,7 +55,7 @@ static const VMStateDescription vmstate_stellaris_button = { .version_id = 0, .minimum_version_id = 0, .fields = (VMStateField[]) { - VMSTATE_UINT8(pressed, gamepad_button), + VMSTATE_UINT8(pressed, StellarisGamepadButton), VMSTATE_END_OF_LIST() } }; @@ -65,11 +65,11 @@ static const VMStateDescription vmstate_stellaris_gamepad = { .version_id = 2, .minimum_version_id = 2, .fields = (VMStateField[]) { - VMSTATE_INT32(extension, gamepad_state), - VMSTATE_STRUCT_VARRAY_POINTER_INT32(buttons, gamepad_state, + VMSTATE_INT32(extension, StellarisGamepad), + VMSTATE_STRUCT_VARRAY_POINTER_INT32(buttons, StellarisGamepad, num_buttons, vmstate_stellaris_button, - gamepad_button), + StellarisGamepadButton), VMSTATE_END_OF_LIST() } }; @@ -77,11 +77,11 @@ static const VMStateDescription vmstate_stellaris_gamepad = { /* Returns an array of 5 output slots. */ void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode) { - gamepad_state *s; + StellarisGamepad *s; int i; - s = g_new0(gamepad_state, 1); - s->buttons = g_new0(gamepad_button, n); + s = g_new0(StellarisGamepad, 1); + s->buttons = g_new0(StellarisGamepadButton, n); for (i = 0; i < n; i++) { s->buttons[i].irq = irq[i]; s->buttons[i].keycode = keycode[i]; From 32400a7e872fe620ab52ec32521e839a71ffb54c Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Mon, 30 Oct 2023 11:47:59 +0000 Subject: [PATCH 344/974] qdev: Add qdev_prop_set_array() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of exposing the ugly hack of how we represent arrays in qdev (a static "foo-len" property and after it is set, dynamically created "foo[i]" properties) to boards, add an interface that allows setting the whole array at once. Once all internal users of devices with array properties have been converted to use this function, we can change the implementation to move away from this hack. Signed-off-by: Kevin Wolf Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell Tested-by: Philippe Mathieu-Daudé Message-id: 20231030114802.3671871-4-peter.maydell@linaro.org --- hw/core/qdev-properties.c | 21 +++++++++++++++++++++ include/hw/qdev-properties.h | 3 +++ 2 files changed, 24 insertions(+) diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 357b8761b5..950ef48e01 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -3,12 +3,14 @@ #include "qapi/error.h" #include "qapi/qapi-types-misc.h" #include "qapi/qmp/qerror.h" +#include "qapi/qmp/qlist.h" #include "qemu/ctype.h" #include "qemu/error-report.h" #include "qapi/visitor.h" #include "qemu/units.h" #include "qemu/cutils.h" #include "qdev-prop-internal.h" +#include "qom/qom-qobject.h" void qdev_prop_set_after_realize(DeviceState *dev, const char *name, Error **errp) @@ -739,6 +741,25 @@ void qdev_prop_set_enum(DeviceState *dev, const char *name, int value) &error_abort); } +void qdev_prop_set_array(DeviceState *dev, const char *name, QList *values) +{ + const QListEntry *entry; + g_autofree char *prop_len = g_strdup_printf("len-%s", name); + uint32_t i = 0; + + object_property_set_int(OBJECT(dev), prop_len, qlist_size(values), + &error_abort); + + QLIST_FOREACH_ENTRY(values, entry) { + g_autofree char *prop_idx = g_strdup_printf("%s[%u]", name, i); + object_property_set_qobject(OBJECT(dev), prop_idx, entry->value, + &error_abort); + i++; + } + + qobject_unref(values); +} + static GPtrArray *global_props(void) { static GPtrArray *gp; diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h index e1df08876c..7fa2fdb7c9 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -206,6 +206,9 @@ void qdev_prop_set_macaddr(DeviceState *dev, const char *name, const uint8_t *value); void qdev_prop_set_enum(DeviceState *dev, const char *name, int value); +/* Takes ownership of @values */ +void qdev_prop_set_array(DeviceState *dev, const char *name, QList *values); + void *object_field_prop_ptr(Object *obj, Property *prop); void qdev_prop_register_global(GlobalProperty *prop); From 5f8d505e3a60b957530c7ad9b1214cc440d8b951 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 30 Oct 2023 11:48:00 +0000 Subject: [PATCH 345/974] hw/input/stellaris_gamepad: Remove StellarisGamepadButton struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently for each button on the device we have a StellarisGamepadButton struct which has the irq, keycode and pressed state for it. When we convert to qdev, the qdev property and GPIO APIs are going to require that we have separate arrays for the irqs and keycodes. Convert from array-of-structs to three separate arrays in preparation. This is a migration compatibility break for the stellaris boards (lm3s6965evb, lm3s811evb). Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-id: 20231030114802.3671871-5-peter.maydell@linaro.org -- v1=>v2: mention migration compat break in commit message; bump version fields in vmstate --- hw/input/stellaris_gamepad.c | 47 ++++++++++++------------------------ 1 file changed, 16 insertions(+), 31 deletions(-) diff --git a/hw/input/stellaris_gamepad.c b/hw/input/stellaris_gamepad.c index 377101a403..82ddc47a26 100644 --- a/hw/input/stellaris_gamepad.c +++ b/hw/input/stellaris_gamepad.c @@ -14,15 +14,11 @@ #include "ui/console.h" typedef struct { - qemu_irq irq; - int keycode; - uint8_t pressed; -} StellarisGamepadButton; - -typedef struct { - StellarisGamepadButton *buttons; - int num_buttons; + uint32_t num_buttons; int extension; + qemu_irq *irqs; + uint32_t *keycodes; + uint8_t *pressed; } StellarisGamepad; static void stellaris_gamepad_put_key(void * opaque, int keycode) @@ -40,36 +36,23 @@ static void stellaris_gamepad_put_key(void * opaque, int keycode) keycode = (keycode & 0x7f) | s->extension; for (i = 0; i < s->num_buttons; i++) { - if (s->buttons[i].keycode == keycode - && s->buttons[i].pressed != down) { - s->buttons[i].pressed = down; - qemu_set_irq(s->buttons[i].irq, down); + if (s->keycodes[i] == keycode && s->pressed[i] != down) { + s->pressed[i] = down; + qemu_set_irq(s->irqs[i], down); } } s->extension = 0; } -static const VMStateDescription vmstate_stellaris_button = { - .name = "stellaris_button", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT8(pressed, StellarisGamepadButton), - VMSTATE_END_OF_LIST() - } -}; - static const VMStateDescription vmstate_stellaris_gamepad = { .name = "stellaris_gamepad", - .version_id = 2, - .minimum_version_id = 2, + .version_id = 3, + .minimum_version_id = 3, .fields = (VMStateField[]) { VMSTATE_INT32(extension, StellarisGamepad), - VMSTATE_STRUCT_VARRAY_POINTER_INT32(buttons, StellarisGamepad, - num_buttons, - vmstate_stellaris_button, - StellarisGamepadButton), + VMSTATE_VARRAY_UINT32(pressed, StellarisGamepad, num_buttons, + 0, vmstate_info_uint8, uint8_t), VMSTATE_END_OF_LIST() } }; @@ -81,10 +64,12 @@ void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode) int i; s = g_new0(StellarisGamepad, 1); - s->buttons = g_new0(StellarisGamepadButton, n); + s->irqs = g_new0(qemu_irq, n); + s->keycodes = g_new0(uint32_t, n); + s->pressed = g_new0(uint8_t, n); for (i = 0; i < n; i++) { - s->buttons[i].irq = irq[i]; - s->buttons[i].keycode = keycode[i]; + s->irqs[i] = irq[i]; + s->keycodes[i] = keycode[i]; } s->num_buttons = n; qemu_add_kbd_event_handler(stellaris_gamepad_put_key, s); From a75f336b97a643fc536ba847042f840890e9b378 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 30 Oct 2023 11:48:01 +0000 Subject: [PATCH 346/974] hw/input/stellaris_input: Convert to qdev MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the hw/input/stellaris_input device to qdev. The interface uses an array property for the board to specify the keycodes to use, so the s->keycodes memory is now allocated by the array-property machinery. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-id: 20231030114802.3671871-6-peter.maydell@linaro.org --- hw/arm/stellaris.c | 26 ++++++++--- hw/input/stellaris_gamepad.c | 70 +++++++++++++++++++--------- include/hw/input/stellaris_gamepad.h | 23 ++++++++- 3 files changed, 87 insertions(+), 32 deletions(-) diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index 96585dd710..707b0dae37 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -31,6 +31,7 @@ #include "hw/timer/stellaris-gptm.h" #include "hw/qdev-clock.h" #include "qom/object.h" +#include "qapi/qmp/qlist.h" #define GPIO_A 0 #define GPIO_B 1 @@ -1274,16 +1275,27 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) sysbus_connect_irq(SYS_BUS_DEVICE(enet), 0, qdev_get_gpio_in(nvic, 42)); } if (board->peripherals & BP_GAMEPAD) { - qemu_irq gpad_irq[5]; + QList *gpad_keycode_list = qlist_new(); static const int gpad_keycode[5] = { 0xc8, 0xd0, 0xcb, 0xcd, 0x1d }; + DeviceState *gpad; - gpad_irq[0] = qemu_irq_invert(gpio_in[GPIO_E][0]); /* up */ - gpad_irq[1] = qemu_irq_invert(gpio_in[GPIO_E][1]); /* down */ - gpad_irq[2] = qemu_irq_invert(gpio_in[GPIO_E][2]); /* left */ - gpad_irq[3] = qemu_irq_invert(gpio_in[GPIO_E][3]); /* right */ - gpad_irq[4] = qemu_irq_invert(gpio_in[GPIO_F][1]); /* select */ + gpad = qdev_new(TYPE_STELLARIS_GAMEPAD); + for (i = 0; i < ARRAY_SIZE(gpad_keycode); i++) { + qlist_append_int(gpad_keycode_list, gpad_keycode[i]); + } + qdev_prop_set_array(gpad, "keycodes", gpad_keycode_list); + sysbus_realize_and_unref(SYS_BUS_DEVICE(gpad), &error_fatal); - stellaris_gamepad_init(5, gpad_irq, gpad_keycode); + qdev_connect_gpio_out(gpad, 0, + qemu_irq_invert(gpio_in[GPIO_E][0])); /* up */ + qdev_connect_gpio_out(gpad, 1, + qemu_irq_invert(gpio_in[GPIO_E][1])); /* down */ + qdev_connect_gpio_out(gpad, 2, + qemu_irq_invert(gpio_in[GPIO_E][2])); /* left */ + qdev_connect_gpio_out(gpad, 3, + qemu_irq_invert(gpio_in[GPIO_E][3])); /* right */ + qdev_connect_gpio_out(gpad, 4, + qemu_irq_invert(gpio_in[GPIO_F][1])); /* select */ } for (i = 0; i < 7; i++) { if (board->dc4 & (1 << i)) { diff --git a/hw/input/stellaris_gamepad.c b/hw/input/stellaris_gamepad.c index 82ddc47a26..d42ba4f058 100644 --- a/hw/input/stellaris_gamepad.c +++ b/hw/input/stellaris_gamepad.c @@ -8,19 +8,13 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/input/stellaris_gamepad.h" #include "hw/irq.h" +#include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "ui/console.h" -typedef struct { - uint32_t num_buttons; - int extension; - qemu_irq *irqs; - uint32_t *keycodes; - uint8_t *pressed; -} StellarisGamepad; - static void stellaris_gamepad_put_key(void * opaque, int keycode) { StellarisGamepad *s = (StellarisGamepad *)opaque; @@ -57,22 +51,52 @@ static const VMStateDescription vmstate_stellaris_gamepad = { } }; -/* Returns an array of 5 output slots. */ -void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode) +static void stellaris_gamepad_realize(DeviceState *dev, Error **errp) { - StellarisGamepad *s; - int i; + StellarisGamepad *s = STELLARIS_GAMEPAD(dev); - s = g_new0(StellarisGamepad, 1); - s->irqs = g_new0(qemu_irq, n); - s->keycodes = g_new0(uint32_t, n); - s->pressed = g_new0(uint8_t, n); - for (i = 0; i < n; i++) { - s->irqs[i] = irq[i]; - s->keycodes[i] = keycode[i]; + if (s->num_buttons == 0) { + error_setg(errp, "keycodes property array must be set"); + return; } - s->num_buttons = n; - qemu_add_kbd_event_handler(stellaris_gamepad_put_key, s); - vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, - &vmstate_stellaris_gamepad, s); + + s->irqs = g_new0(qemu_irq, s->num_buttons); + s->pressed = g_new0(uint8_t, s->num_buttons); + qdev_init_gpio_out(dev, s->irqs, s->num_buttons); + qemu_add_kbd_event_handler(stellaris_gamepad_put_key, dev); } + +static void stellaris_gamepad_reset_enter(Object *obj, ResetType type) +{ + StellarisGamepad *s = STELLARIS_GAMEPAD(obj); + + memset(s->pressed, 0, s->num_buttons * sizeof(uint8_t)); +} + +static Property stellaris_gamepad_properties[] = { + DEFINE_PROP_ARRAY("keycodes", StellarisGamepad, num_buttons, + keycodes, qdev_prop_uint32, uint32_t), + DEFINE_PROP_END_OF_LIST(), +}; + +static void stellaris_gamepad_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.enter = stellaris_gamepad_reset_enter; + dc->realize = stellaris_gamepad_realize; + dc->vmsd = &vmstate_stellaris_gamepad; + device_class_set_props(dc, stellaris_gamepad_properties); +} + +static const TypeInfo stellaris_gamepad_info[] = { + { + .name = TYPE_STELLARIS_GAMEPAD, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(StellarisGamepad), + .class_init = stellaris_gamepad_class_init, + }, +}; + +DEFINE_TYPES(stellaris_gamepad_info); diff --git a/include/hw/input/stellaris_gamepad.h b/include/hw/input/stellaris_gamepad.h index 23cfd3c95f..d6a6aecb06 100644 --- a/include/hw/input/stellaris_gamepad.h +++ b/include/hw/input/stellaris_gamepad.h @@ -11,8 +11,27 @@ #ifndef HW_INPUT_STELLARIS_GAMEPAD_H #define HW_INPUT_STELLARIS_GAMEPAD_H +#include "hw/sysbus.h" +#include "qom/object.h" -/* stellaris_gamepad.c */ -void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode); +/* + * QEMU interface: + * + QOM array property "keycodes": uint32_t QEMU keycodes to handle + * + unnamed GPIO outputs: one per keycode, in the same order as the + * "keycodes" array property entries; asserted when key is down + */ + +#define TYPE_STELLARIS_GAMEPAD "stellaris-gamepad" +OBJECT_DECLARE_SIMPLE_TYPE(StellarisGamepad, STELLARIS_GAMEPAD) + +struct StellarisGamepad { + SysBusDevice parent_obj; + + uint32_t num_buttons; + qemu_irq *irqs; + uint32_t *keycodes; + uint8_t *pressed; + int extension; +}; #endif From 7c76f397fde313bcdfd3781d64fc28dae0e42df8 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 30 Oct 2023 11:48:02 +0000 Subject: [PATCH 347/974] hw/input/stellaris_gamepad: Convert to qemu_input_handler_register() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that we have converted to qdev, we can use the newer qemu_input_handler_register() API rather than the legacy qemu_add_kbd_event_handler(). Since we only have one user, take the opportunity to convert from scancodes to QCodes, rather than using qemu_input_key_value_to_scancode() (which adds an 0xe0 prefix and encodes up/down indication in the scancode, which our old handler function then had to reverse). That lets us drop the old state field which was tracking whether we were halfway through a two-byte scancode. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-id: 20231030114802.3671871-7-peter.maydell@linaro.org --- hw/arm/stellaris.c | 6 ++++- hw/input/stellaris_gamepad.c | 37 +++++++++++++--------------- include/hw/input/stellaris_gamepad.h | 2 +- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index 707b0dae37..dd90f686bf 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -32,6 +32,7 @@ #include "hw/qdev-clock.h" #include "qom/object.h" #include "qapi/qmp/qlist.h" +#include "ui/input.h" #define GPIO_A 0 #define GPIO_B 1 @@ -1276,7 +1277,10 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) } if (board->peripherals & BP_GAMEPAD) { QList *gpad_keycode_list = qlist_new(); - static const int gpad_keycode[5] = { 0xc8, 0xd0, 0xcb, 0xcd, 0x1d }; + static const int gpad_keycode[5] = { + Q_KEY_CODE_UP, Q_KEY_CODE_DOWN, Q_KEY_CODE_LEFT, + Q_KEY_CODE_RIGHT, Q_KEY_CODE_CTRL, + }; DeviceState *gpad; gpad = qdev_new(TYPE_STELLARIS_GAMEPAD); diff --git a/hw/input/stellaris_gamepad.c b/hw/input/stellaris_gamepad.c index d42ba4f058..06a0c0ce83 100644 --- a/hw/input/stellaris_gamepad.c +++ b/hw/input/stellaris_gamepad.c @@ -15,42 +15,39 @@ #include "migration/vmstate.h" #include "ui/console.h" -static void stellaris_gamepad_put_key(void * opaque, int keycode) +static void stellaris_gamepad_event(DeviceState *dev, QemuConsole *src, + InputEvent *evt) { - StellarisGamepad *s = (StellarisGamepad *)opaque; + StellarisGamepad *s = STELLARIS_GAMEPAD(dev); + InputKeyEvent *key = evt->u.key.data; + int qcode = qemu_input_key_value_to_qcode(key->key); int i; - int down; - - if (keycode == 0xe0 && !s->extension) { - s->extension = 0x80; - return; - } - - down = (keycode & 0x80) == 0; - keycode = (keycode & 0x7f) | s->extension; for (i = 0; i < s->num_buttons; i++) { - if (s->keycodes[i] == keycode && s->pressed[i] != down) { - s->pressed[i] = down; - qemu_set_irq(s->irqs[i], down); + if (s->keycodes[i] == qcode && s->pressed[i] != key->down) { + s->pressed[i] = key->down; + qemu_set_irq(s->irqs[i], key->down); } } - - s->extension = 0; } static const VMStateDescription vmstate_stellaris_gamepad = { .name = "stellaris_gamepad", - .version_id = 3, - .minimum_version_id = 3, + .version_id = 4, + .minimum_version_id = 4, .fields = (VMStateField[]) { - VMSTATE_INT32(extension, StellarisGamepad), VMSTATE_VARRAY_UINT32(pressed, StellarisGamepad, num_buttons, 0, vmstate_info_uint8, uint8_t), VMSTATE_END_OF_LIST() } }; +static const QemuInputHandler stellaris_gamepad_handler = { + .name = "Stellaris Gamepad", + .mask = INPUT_EVENT_MASK_KEY, + .event = stellaris_gamepad_event, +}; + static void stellaris_gamepad_realize(DeviceState *dev, Error **errp) { StellarisGamepad *s = STELLARIS_GAMEPAD(dev); @@ -63,7 +60,7 @@ static void stellaris_gamepad_realize(DeviceState *dev, Error **errp) s->irqs = g_new0(qemu_irq, s->num_buttons); s->pressed = g_new0(uint8_t, s->num_buttons); qdev_init_gpio_out(dev, s->irqs, s->num_buttons); - qemu_add_kbd_event_handler(stellaris_gamepad_put_key, dev); + qemu_input_handler_register(dev, &stellaris_gamepad_handler); } static void stellaris_gamepad_reset_enter(Object *obj, ResetType type) diff --git a/include/hw/input/stellaris_gamepad.h b/include/hw/input/stellaris_gamepad.h index d6a6aecb06..51085e166c 100644 --- a/include/hw/input/stellaris_gamepad.h +++ b/include/hw/input/stellaris_gamepad.h @@ -17,6 +17,7 @@ /* * QEMU interface: * + QOM array property "keycodes": uint32_t QEMU keycodes to handle + * (these are QCodes, ie the Q_KEY_* values) * + unnamed GPIO outputs: one per keycode, in the same order as the * "keycodes" array property entries; asserted when key is down */ @@ -31,7 +32,6 @@ struct StellarisGamepad { qemu_irq *irqs; uint32_t *keycodes; uint8_t *pressed; - int extension; }; #endif From 8472cc5dbe6438c62021c427574077bfd996ba8e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 27 Sep 2023 16:11:58 +0100 Subject: [PATCH 348/974] docs/specs/vmw_pvscsi-spec: Convert to rST Convert the docs/specs/vmw_pvscsi-spec.txt file to rST format. This conversion includes some minor wordsmithing of the text to fix some grammar nits. Signed-off-by: Peter Maydell Reviewed-by: Thomas Huth Message-id: 20230927151205.70930-2-peter.maydell@linaro.org --- MAINTAINERS | 1 + docs/specs/index.rst | 1 + docs/specs/vmw_pvscsi-spec.rst | 115 +++++++++++++++++++++++++++++++++ docs/specs/vmw_pvscsi-spec.txt | 92 -------------------------- 4 files changed, 117 insertions(+), 92 deletions(-) create mode 100644 docs/specs/vmw_pvscsi-spec.rst delete mode 100644 docs/specs/vmw_pvscsi-spec.txt diff --git a/MAINTAINERS b/MAINTAINERS index 018ed62560..73ec940bea 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2350,6 +2350,7 @@ S: Maintained F: hw/net/vmxnet* F: hw/scsi/vmw_pvscsi* F: tests/qtest/vmxnet3-test.c +F: docs/specs/vwm_pvscsi-spec.rst Rocker M: Jiri Pirko diff --git a/docs/specs/index.rst b/docs/specs/index.rst index e58be38c41..d23efbe248 100644 --- a/docs/specs/index.rst +++ b/docs/specs/index.rst @@ -24,3 +24,4 @@ guest hardware that is specific to QEMU. acpi_erst sev-guest-firmware fw_cfg + vmw_pvscsi-spec diff --git a/docs/specs/vmw_pvscsi-spec.rst b/docs/specs/vmw_pvscsi-spec.rst new file mode 100644 index 0000000000..b6f434a418 --- /dev/null +++ b/docs/specs/vmw_pvscsi-spec.rst @@ -0,0 +1,115 @@ +============================== +VMWare PVSCSI Device Interface +============================== + +.. + Created by Dmitry Fleytman (dmitry@daynix.com), Daynix Computing LTD. + +This document describes the VMWare PVSCSI device interface specification, +based on the source code of the PVSCSI Linux driver from kernel 3.0.4. + +Overview +======== + +The interface is based on a memory area shared between hypervisor and VM. +The memory area is obtained by driver as a device IO memory resource of +``PVSCSI_MEM_SPACE_SIZE`` length. +The shared memory consists of a registers area and a rings area. +The registers area is used to raise hypervisor interrupts and issue device +commands. The rings area is used to transfer data descriptors and SCSI +commands from VM to hypervisor and to transfer messages produced by +hypervisor to VM. Data itself is transferred via virtual scatter-gather DMA. + +PVSCSI Device Registers +======================= + +The length of the registers area is 1 page +(``PVSCSI_MEM_SPACE_COMMAND_NUM_PAGES``). The structure of the +registers area is described by the ``PVSCSIRegOffset`` enum. There +are registers to issue device commands (with optional short data), +issue device interrupts, and control interrupt masking. + +PVSCSI Device Rings +=================== + +There are three rings in shared memory: + +Request ring (``struct PVSCSIRingReqDesc *req_ring``) + ring for OS to device requests + +Completion ring (``struct PVSCSIRingCmpDesc *cmp_ring``) + ring for device request completions + +Message ring (``struct PVSCSIRingMsgDesc *msg_ring``) + ring for messages from device. This ring is optional and the + guest might not configure it. + +There is a control area (``struct PVSCSIRingsState *rings_state``) +used to control rings operation. + +PVSCSI Device to Host Interrupts +================================ + +The following interrupt types are supported by the PVSCSI device: + +Completion interrupts (completion ring notifications): + +- ``PVSCSI_INTR_CMPL_0`` +- ``PVSCSI_INTR_CMPL_1`` + +Message interrupts (message ring notifications): + +- ``PVSCSI_INTR_MSG_0`` +- ``PVSCSI_INTR_MSG_1`` + +Interrupts are controlled via the ``PVSCSI_REG_OFFSET_INTR_MASK`` +register. If a bit is set it means the interrupt is enabled, and if +it is clear then the interrupt is disabled. + +The interrupt modes supported are legacy, MSI and MSI-X. +In the case of legacy interrupts, the ``PVSCSI_REG_OFFSET_INTR_STATUS`` +register is used to check which interrupt has arrived. Interrupts are +acknowledged when the corresponding bit is written to the interrupt +status register. + +PVSCSI Device Operation Sequences +================================= + +Startup sequence +---------------- + +a. Issue ``PVSCSI_CMD_ADAPTER_RESET`` command +b. Windows driver reads interrupt status register here +c. Issue ``PVSCSI_CMD_SETUP_MSG_RING`` command with no additional data, + check status and disable device messages if error returned + (Omitted if device messages disabled by driver configuration) +d. Issue ``PVSCSI_CMD_SETUP_RINGS`` command, provide rings configuration + as ``struct PVSCSICmdDescSetupRings`` +e. Issue ``PVSCSI_CMD_SETUP_MSG_RING`` command again, provide + rings configuration as ``struct PVSCSICmdDescSetupMsgRing`` +f. Unmask completion and message (if device messages enabled) interrupts + +Shutdown sequence +----------------- + +a. Mask interrupts +b. Flush request ring using ``PVSCSI_REG_OFFSET_KICK_NON_RW_IO`` +c. Issue ``PVSCSI_CMD_ADAPTER_RESET`` command + +Send request +------------ + +a. Fill next free request ring descriptor +b. Issue ``PVSCSI_REG_OFFSET_KICK_RW_IO`` for R/W operations + or ``PVSCSI_REG_OFFSET_KICK_NON_RW_IO`` for other operations + +Abort command +------------- + +a. Issue ``PVSCSI_CMD_ABORT_CMD`` command + +Request completion processing +----------------------------- + +a. Upon completion interrupt arrival process completion + and message (if enabled) rings diff --git a/docs/specs/vmw_pvscsi-spec.txt b/docs/specs/vmw_pvscsi-spec.txt deleted file mode 100644 index 49affb2a42..0000000000 --- a/docs/specs/vmw_pvscsi-spec.txt +++ /dev/null @@ -1,92 +0,0 @@ -General Description -=================== - -This document describes VMWare PVSCSI device interface specification. -Created by Dmitry Fleytman (dmitry@daynix.com), Daynix Computing LTD. -Based on source code of PVSCSI Linux driver from kernel 3.0.4 - -PVSCSI Device Interface Overview -================================ - -The interface is based on memory area shared between hypervisor and VM. -Memory area is obtained by driver as device IO memory resource of -PVSCSI_MEM_SPACE_SIZE length. -The shared memory consists of registers area and rings area. -The registers area is used to raise hypervisor interrupts and issue device -commands. The rings area is used to transfer data descriptors and SCSI -commands from VM to hypervisor and to transfer messages produced by -hypervisor to VM. Data itself is transferred via virtual scatter-gather DMA. - -PVSCSI Device Registers -======================= - -The length of the registers area is 1 page (PVSCSI_MEM_SPACE_COMMAND_NUM_PAGES). -The structure of the registers area is described by the PVSCSIRegOffset enum. -There are registers to issue device command (with optional short data), -issue device interrupt, control interrupts masking. - -PVSCSI Device Rings -=================== - -There are three rings in shared memory: - - 1. Request ring (struct PVSCSIRingReqDesc *req_ring) - - ring for OS to device requests - 2. Completion ring (struct PVSCSIRingCmpDesc *cmp_ring) - - ring for device request completions - 3. Message ring (struct PVSCSIRingMsgDesc *msg_ring) - - ring for messages from device. - This ring is optional and the guest might not configure it. -There is a control area (struct PVSCSIRingsState *rings_state) used to control -rings operation. - -PVSCSI Device to Host Interrupts -================================ -There are following interrupt types supported by PVSCSI device: - 1. Completion interrupts (completion ring notifications): - PVSCSI_INTR_CMPL_0 - PVSCSI_INTR_CMPL_1 - 2. Message interrupts (message ring notifications): - PVSCSI_INTR_MSG_0 - PVSCSI_INTR_MSG_1 - -Interrupts are controlled via PVSCSI_REG_OFFSET_INTR_MASK register -Bit set means interrupt enabled, bit cleared - disabled - -Interrupt modes supported are legacy, MSI and MSI-X -In case of legacy interrupts, register PVSCSI_REG_OFFSET_INTR_STATUS -is used to check which interrupt has arrived. Interrupts are -acknowledged when the corresponding bit is written to the interrupt -status register. - -PVSCSI Device Operation Sequences -================================= - -1. Startup sequence: - a. Issue PVSCSI_CMD_ADAPTER_RESET command; - aa. Windows driver reads interrupt status register here; - b. Issue PVSCSI_CMD_SETUP_MSG_RING command with no additional data, - check status and disable device messages if error returned; - (Omitted if device messages disabled by driver configuration) - c. Issue PVSCSI_CMD_SETUP_RINGS command, provide rings configuration - as struct PVSCSICmdDescSetupRings; - d. Issue PVSCSI_CMD_SETUP_MSG_RING command again, provide - rings configuration as struct PVSCSICmdDescSetupMsgRing; - e. Unmask completion and message (if device messages enabled) interrupts. - -2. Shutdown sequences - a. Mask interrupts; - b. Flush request ring using PVSCSI_REG_OFFSET_KICK_NON_RW_IO; - c. Issue PVSCSI_CMD_ADAPTER_RESET command. - -3. Send request - a. Fill next free request ring descriptor; - b. Issue PVSCSI_REG_OFFSET_KICK_RW_IO for R/W operations; - or PVSCSI_REG_OFFSET_KICK_NON_RW_IO for other operations. - -4. Abort command - a. Issue PVSCSI_CMD_ABORT_CMD command; - -5. Request completion processing - a. Upon completion interrupt arrival process completion - and message (if enabled) rings. From 4df3f195ad8bfe9b444578c8b7b6af5d4492e44c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 27 Sep 2023 16:11:59 +0100 Subject: [PATCH 349/974] docs/specs/edu: Convert to rST Convert docs/specs/edu.txt to rST format. Signed-off-by: Peter Maydell Reviewed-by: Thomas Huth Message-id: 20230927151205.70930-3-peter.maydell@linaro.org --- MAINTAINERS | 1 + docs/specs/{edu.txt => edu.rst} | 84 ++++++++++++++++++++------------- docs/specs/index.rst | 1 + 3 files changed, 54 insertions(+), 32 deletions(-) rename docs/specs/{edu.txt => edu.rst} (64%) diff --git a/MAINTAINERS b/MAINTAINERS index 73ec940bea..8e10bd085f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1882,6 +1882,7 @@ EDU M: Jiri Slaby S: Maintained F: hw/misc/edu.c +F: docs/specs/edu.rst IDE M: John Snow diff --git a/docs/specs/edu.txt b/docs/specs/edu.rst similarity index 64% rename from docs/specs/edu.txt rename to docs/specs/edu.rst index 0876310809..ae72737dbb 100644 --- a/docs/specs/edu.txt +++ b/docs/specs/edu.rst @@ -2,9 +2,10 @@ EDU device ========== -Copyright (c) 2014-2015 Jiri Slaby +.. + Copyright (c) 2014-2015 Jiri Slaby -This document is licensed under the GPLv2 (or later). + This document is licensed under the GPLv2 (or later). This is an educational device for writing (kernel) drivers. Its original intention was to support the Linux kernel lectures taught at the Masaryk @@ -15,10 +16,11 @@ The devices behaves very similar to the PCI bridge present in the COMBO6 cards developed under the Liberouter wings. Both PCI device ID and PCI space is inherited from that device. -Command line switches: - -device edu[,dma_mask=mask] +Command line switches +--------------------- - dma_mask makes the virtual device work with DMA addresses with the given +``-device edu[,dma_mask=mask]`` + ``dma_mask`` makes the virtual device work with DMA addresses with the given mask. For educational purposes, the device supports only 28 bits (256 MiB) by default. Students shall set dma_mask for the device in the OS driver properly. @@ -26,7 +28,8 @@ Command line switches: PCI specs --------- -PCI ID: 1234:11e8 +PCI ID: + ``1234:11e8`` PCI Region 0: I/O memory, 1 MB in size. Users are supposed to communicate with the card @@ -35,24 +38,29 @@ PCI Region 0: MMIO area spec -------------- -Only size == 4 accesses are allowed for addresses < 0x80. size == 4 or -size == 8 for the rest. +Only ``size == 4`` accesses are allowed for addresses ``< 0x80``. +``size == 4`` or ``size == 8`` for the rest. -0x00 (RO) : identification (0xRRrr00edu) - RR -- major version - rr -- minor version +0x00 (RO) : identification + Value is in the form ``0xRRrr00edu`` where: + - ``RR`` -- major version + - ``rr`` -- minor version 0x04 (RW) : card liveness check - It is a simple value inversion (~ C operator). + It is a simple value inversion (``~`` C operator). 0x08 (RW) : factorial computation The stored value is taken and factorial of it is put back here. This happens only after factorial bit in the status register (0x20 below) is cleared. -0x20 (RW) : status register, bitwise OR - 0x01 -- computing factorial (RO) - 0x80 -- raise interrupt after finishing factorial computation +0x20 (RW) : status register + Bitwise OR of: + + 0x01 + computing factorial (RO) + 0x80 + raise interrupt after finishing factorial computation 0x24 (RO) : interrupt status register It contains values which raised the interrupt (see interrupt raise @@ -76,13 +84,19 @@ size == 8 for the rest. 0x90 (RW) : DMA transfer count The size of the area to perform the DMA on. -0x98 (RW) : DMA command register, bitwise OR - 0x01 -- start transfer - 0x02 -- direction (0: from RAM to EDU, 1: from EDU to RAM) - 0x04 -- raise interrupt 0x100 after finishing the DMA +0x98 (RW) : DMA command register + Bitwise OR of: + + 0x01 + start transfer + 0x02 + direction (0: from RAM to EDU, 1: from EDU to RAM) + 0x04 + raise interrupt 0x100 after finishing the DMA IRQ controller -------------- + An IRQ is generated when written to the interrupt raise register. The value appears in interrupt status register when the interrupt is raised and has to be written to the interrupt acknowledge register to lower it. @@ -94,22 +108,28 @@ routine. DMA controller -------------- + One has to specify, source, destination, size, and start the transfer. One 4096 bytes long buffer at offset 0x40000 is available in the EDU device. I.e. one can perform DMA to/from this space when programmed properly. Example of transferring a 100 byte block to and from the buffer using a given -PCI address 'addr': -addr -> DMA source address -0x40000 -> DMA destination address -100 -> DMA transfer count -1 -> DMA command register -while (DMA command register & 1) - ; +PCI address ``addr``: -0x40000 -> DMA source address -addr+100 -> DMA destination address -100 -> DMA transfer count -3 -> DMA command register -while (DMA command register & 1) - ; +:: + + addr -> DMA source address + 0x40000 -> DMA destination address + 100 -> DMA transfer count + 1 -> DMA command register + while (DMA command register & 1) + ; + +:: + + 0x40000 -> DMA source address + addr+100 -> DMA destination address + 100 -> DMA transfer count + 3 -> DMA command register + while (DMA command register & 1) + ; diff --git a/docs/specs/index.rst b/docs/specs/index.rst index d23efbe248..30a0cf3d47 100644 --- a/docs/specs/index.rst +++ b/docs/specs/index.rst @@ -25,3 +25,4 @@ guest hardware that is specific to QEMU. sev-guest-firmware fw_cfg vmw_pvscsi-spec + edu From bb1cff6ee044cb13e2e81609a0b9a86378f85f1f Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 27 Sep 2023 16:12:00 +0100 Subject: [PATCH 350/974] docs/specs/ivshmem-spec: Convert to rST MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert docs/specs/ivshmem-spec.txt to rST format. In converting, I have dropped the sections on the device's command line interface and usage, as they are already covered by the user-facing docs in system/devices/ivshmem.rst. I have also removed the reference to Memnic, because the URL is dead and a web search suggests that whatever this was it's pretty much sunk without trace. Signed-off-by: Peter Maydell Message-id: 20230927151205.70930-4-peter.maydell@linaro.org Reviewed-by: Philippe Mathieu-Daudé --- docs/specs/index.rst | 1 + .../{ivshmem-spec.txt => ivshmem-spec.rst} | 63 +++++++------------ docs/specs/pci-ids.rst | 2 +- docs/system/devices/ivshmem.rst | 2 +- 4 files changed, 26 insertions(+), 42 deletions(-) rename docs/specs/{ivshmem-spec.txt => ivshmem-spec.rst} (88%) diff --git a/docs/specs/index.rst b/docs/specs/index.rst index 30a0cf3d47..e60c837754 100644 --- a/docs/specs/index.rst +++ b/docs/specs/index.rst @@ -26,3 +26,4 @@ guest hardware that is specific to QEMU. fw_cfg vmw_pvscsi-spec edu + ivshmem-spec diff --git a/docs/specs/ivshmem-spec.txt b/docs/specs/ivshmem-spec.rst similarity index 88% rename from docs/specs/ivshmem-spec.txt rename to docs/specs/ivshmem-spec.rst index 1beb3a01ec..2d8e80055b 100644 --- a/docs/specs/ivshmem-spec.txt +++ b/docs/specs/ivshmem-spec.rst @@ -1,4 +1,6 @@ -= Device Specification for Inter-VM shared memory device = +====================================================== +Device Specification for Inter-VM shared memory device +====================================================== The Inter-VM shared memory device (ivshmem) is designed to share a memory region between multiple QEMU processes running different guests @@ -12,42 +14,17 @@ can obtain one from an ivshmem server. In the latter case, the device can additionally interrupt its peers, and get interrupted by its peers. +For information on configuring the ivshmem device on the QEMU +command line, see :doc:`../system/devices/ivshmem`. -== Configuring the ivshmem PCI device == - -There are two basic configurations: - -- Just shared memory: - - -device ivshmem-plain,memdev=HMB,... - - This uses host memory backend HMB. It should have option "share" - set. - -- Shared memory plus interrupts: - - -device ivshmem-doorbell,chardev=CHR,vectors=N,... - - An ivshmem server must already be running on the host. The device - connects to the server's UNIX domain socket via character device - CHR. - - Each peer gets assigned a unique ID by the server. IDs must be - between 0 and 65535. - - Interrupts are message-signaled (MSI-X). vectors=N configures the - number of vectors to use. - -For more details on ivshmem device properties, see the QEMU Emulator -user documentation. - - -== The ivshmem PCI device's guest interface == +The ivshmem PCI device's guest interface +======================================== The device has vendor ID 1af4, device ID 1110, revision 1. Before QEMU 2.6.0, it had revision 0. -=== PCI BARs === +PCI BARs +-------- The ivshmem PCI device has two or three BARs: @@ -59,8 +36,7 @@ There are two ways to use this device: - If you only need the shared memory part, BAR2 suffices. This way, you have access to the shared memory in the guest and can use it as - you see fit. Memnic, for example, uses ivshmem this way from guest - user space (see http://dpdk.org/browse/memnic). + you see fit. - If you additionally need the capability for peers to interrupt each other, you need BAR0 and BAR1. You will most likely want to write a @@ -77,10 +53,13 @@ accessing BAR2. Revision 0 of the device is not capable to tell guest software whether it is configured for interrupts. -=== PCI device registers === +PCI device registers +-------------------- BAR 0 contains the following registers: +:: + Offset Size Access On reset Function 0 4 read/write 0 Interrupt Mask bit 0: peer interrupt (rev 0) @@ -145,18 +124,20 @@ With multiple MSI-X vectors, different vectors can be used to indicate different events have occurred. The semantics of interrupt vectors are left to the application. - -== Interrupt infrastructure == +Interrupt infrastructure +======================== When configured for interrupts, the peers share eventfd objects in addition to shared memory. The shared resources are managed by an ivshmem server. -=== The ivshmem server === +The ivshmem server +------------------ The server listens on a UNIX domain socket. For each new client that connects to the server, the server + - picks an ID, - creates eventfd file descriptors for the interrupt vectors, - sends the ID and the file descriptor for the shared memory to the @@ -189,7 +170,8 @@ vectors. A standalone client is in contrib/ivshmem-client/. It can be useful for debugging. -=== The ivshmem Client-Server Protocol === +The ivshmem Client-Server Protocol +---------------------------------- An ivshmem device configured for interrupts connects to an ivshmem server. This section details the protocol between the two. @@ -245,7 +227,8 @@ Known bugs: * The protocol is poorly designed. -=== The ivshmem Client-Client Protocol === +The ivshmem Client-Client Protocol +---------------------------------- An ivshmem device configured for interrupts receives eventfd file descriptors for interrupting peers and getting interrupted by peers diff --git a/docs/specs/pci-ids.rst b/docs/specs/pci-ids.rst index d6707fa069..c0a3dec2e7 100644 --- a/docs/specs/pci-ids.rst +++ b/docs/specs/pci-ids.rst @@ -50,7 +50,7 @@ maintained as part of the virtio specification. by QEMU. 1af4:1110 - ivshmem device (shared memory, ``docs/specs/ivshmem-spec.txt``) + ivshmem device (:doc:`ivshmem-spec`) All other device IDs are reserved. diff --git a/docs/system/devices/ivshmem.rst b/docs/system/devices/ivshmem.rst index e7aaf34c20..ce71e25663 100644 --- a/docs/system/devices/ivshmem.rst +++ b/docs/system/devices/ivshmem.rst @@ -33,7 +33,7 @@ syntax when using the shared memory server is: When using the server, the guest will be assigned a VM ID (>=0) that allows guests using the same server to communicate via interrupts. Guests can read their VM ID from a device register (see -ivshmem-spec.txt). +:doc:`../../specs/ivshmem-spec`). Migration with ivshmem ~~~~~~~~~~~~~~~~~~~~~~ From 945f3fd4e46405ed37b035922eebe2bfad6bc11e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 27 Sep 2023 16:12:01 +0100 Subject: [PATCH 351/974] docs/specs/pvpanic: Convert to rST MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert docs/specs/pvpanic.txt to rST format. Signed-off-by: Peter Maydell Message-id: 20230927151205.70930-5-peter.maydell@linaro.org Reviewed-by: Philippe Mathieu-Daudé --- docs/specs/index.rst | 1 + docs/specs/{pvpanic.txt => pvpanic.rst} | 41 ++++++++++++++++--------- 2 files changed, 28 insertions(+), 14 deletions(-) rename docs/specs/{pvpanic.txt => pvpanic.rst} (64%) diff --git a/docs/specs/index.rst b/docs/specs/index.rst index e60c837754..52bfab2f68 100644 --- a/docs/specs/index.rst +++ b/docs/specs/index.rst @@ -27,3 +27,4 @@ guest hardware that is specific to QEMU. vmw_pvscsi-spec edu ivshmem-spec + pvpanic diff --git a/docs/specs/pvpanic.txt b/docs/specs/pvpanic.rst similarity index 64% rename from docs/specs/pvpanic.txt rename to docs/specs/pvpanic.rst index 8afcde11cc..f894bc1955 100644 --- a/docs/specs/pvpanic.txt +++ b/docs/specs/pvpanic.rst @@ -21,18 +21,21 @@ recognize. On write, the bits not recognized by the device are ignored. Software should set only bits both itself and the device recognize. Bit Definition --------------- -bit 0: a guest panic has happened and should be processed by the host -bit 1: a guest panic has happened and will be handled by the guest; - the host should record it or report it, but should not affect - the execution of the guest. +~~~~~~~~~~~~~~ + +bit 0 + a guest panic has happened and should be processed by the host +bit 1 + a guest panic has happened and will be handled by the guest; + the host should record it or report it, but should not affect + the execution of the guest. PCI Interface ------------- The PCI interface is similar to the ISA interface except that it uses an MMIO address space provided by its BAR0, 1 byte long. Any machine with a PCI bus -can enable a pvpanic device by adding '-device pvpanic-pci' to the command +can enable a pvpanic device by adding ``-device pvpanic-pci`` to the command line. ACPI Interface @@ -40,15 +43,25 @@ ACPI Interface pvpanic device is defined with ACPI ID "QEMU0001". Custom methods: -RDPT: To determine whether guest panic notification is supported. -Arguments: None -Return: Returns a byte, with the same semantics as the I/O port - interface. +RDPT +~~~~ -WRPT: To send a guest panic event -Arguments: Arg0 is a byte to be written, with the same semantics as - the I/O interface. -Return: None +To determine whether guest panic notification is supported. + +Arguments + None +Return + Returns a byte, with the same semantics as the I/O port interface. + +WRPT +~~~~ + +To send a guest panic event. + +Arguments + Arg0 is a byte to be written, with the same semantics as the I/O interface. +Return + None The ACPI device will automatically refer to the right port in case it is modified. From 68ed96bebf12100a78a211796c1231d66c868d33 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 27 Sep 2023 16:12:02 +0100 Subject: [PATCH 352/974] docs/specs/standard-vga: Convert to rST MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert docs/specs/standard-vga.txt to rST format. Signed-off-by: Peter Maydell Message-id: 20230927151205.70930-6-peter.maydell@linaro.org Reviewed-by: Philippe Mathieu-Daudé --- MAINTAINERS | 1 + docs/specs/index.rst | 1 + docs/specs/standard-vga.rst | 94 +++++++++++++++++++++++++++++++++++++ docs/specs/standard-vga.txt | 81 -------------------------------- hw/display/vga-isa.c | 2 +- hw/display/vga-pci.c | 2 +- 6 files changed, 98 insertions(+), 83 deletions(-) create mode 100644 docs/specs/standard-vga.rst delete mode 100644 docs/specs/standard-vga.txt diff --git a/MAINTAINERS b/MAINTAINERS index 8e10bd085f..b5e1765d7a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2468,6 +2468,7 @@ F: hw/display/vga* F: hw/display/bochs-display.c F: include/hw/display/vga.h F: include/hw/display/bochs-vbe.h +F: docs/specs/standard-vga.rst ramfb M: Gerd Hoffmann diff --git a/docs/specs/index.rst b/docs/specs/index.rst index 52bfab2f68..ee84b8109d 100644 --- a/docs/specs/index.rst +++ b/docs/specs/index.rst @@ -28,3 +28,4 @@ guest hardware that is specific to QEMU. edu ivshmem-spec pvpanic + standard-vga diff --git a/docs/specs/standard-vga.rst b/docs/specs/standard-vga.rst new file mode 100644 index 0000000000..992f429ced --- /dev/null +++ b/docs/specs/standard-vga.rst @@ -0,0 +1,94 @@ + +QEMU Standard VGA +================= + +Exists in two variants, for isa and pci. + +command line switches: + +``-vga std`` + picks isa for -M isapc, otherwise pci +``-device VGA`` + pci variant +``-device isa-vga`` + isa variant +``-device secondary-vga`` + legacy-free pci variant + + +PCI spec +-------- + +Applies to the pci variant only for obvious reasons. + +PCI ID + ``1234:1111`` + +PCI Region 0 + Framebuffer memory, 16 MB in size (by default). + Size is tunable via vga_mem_mb property. + +PCI Region 1 + Reserved (so we have the option to make the framebuffer bar 64bit). + +PCI Region 2 + MMIO bar, 4096 bytes in size (QEMU 1.3+) + +PCI ROM Region + Holds the vgabios (QEMU 0.14+). + + +The legacy-free variant has no ROM and has ``PCI_CLASS_DISPLAY_OTHER`` +instead of ``PCI_CLASS_DISPLAY_VGA``. + + +IO ports used +------------- + +Doesn't apply to the legacy-free pci variant, use the MMIO bar instead. + +``03c0 - 03df`` + standard vga ports +``01ce`` + bochs vbe interface index port +``01cf`` + bochs vbe interface data port (x86 only) +``01d0`` + bochs vbe interface data port + + +Memory regions used +------------------- + +``0xe0000000`` + Framebuffer memory, isa variant only. + +The pci variant used to mirror the framebuffer bar here, QEMU 0.14+ +stops doing that (except when in ``-M pc-$old`` compat mode). + + +MMIO area spec +-------------- + +Likewise applies to the pci variant only for obvious reasons. + +``0000 - 03ff`` + edid data blob. +``0400 - 041f`` + vga ioports (``0x3c0`` to ``0x3df``), remapped 1:1. Word access + is supported, bytes are written in little endian order (aka index + port first), so indexed registers can be updated with a single + mmio write (and thus only one vmexit). +``0500 - 0515`` + bochs dispi interface registers, mapped flat without index/data ports. + Use ``(index << 1)`` as offset for (16bit) register access. +``0600 - 0607`` + QEMU extended registers. QEMU 2.2+ only. + The pci revision is 2 (or greater) when these registers are present. + The registers are 32bit. +``0600`` + QEMU extended register region size, in bytes. +``0604`` + framebuffer endianness register. + - ``0xbebebebe`` indicates big endian. + - ``0x1e1e1e1e`` indicates little endian. diff --git a/docs/specs/standard-vga.txt b/docs/specs/standard-vga.txt deleted file mode 100644 index 18f75f1b30..0000000000 --- a/docs/specs/standard-vga.txt +++ /dev/null @@ -1,81 +0,0 @@ - -QEMU Standard VGA -================= - -Exists in two variants, for isa and pci. - -command line switches: - -vga std [ picks isa for -M isapc, otherwise pci ] - -device VGA [ pci variant ] - -device isa-vga [ isa variant ] - -device secondary-vga [ legacy-free pci variant ] - - -PCI spec --------- - -Applies to the pci variant only for obvious reasons. - -PCI ID: 1234:1111 - -PCI Region 0: - Framebuffer memory, 16 MB in size (by default). - Size is tunable via vga_mem_mb property. - -PCI Region 1: - Reserved (so we have the option to make the framebuffer bar 64bit). - -PCI Region 2: - MMIO bar, 4096 bytes in size (qemu 1.3+) - -PCI ROM Region: - Holds the vgabios (qemu 0.14+). - - -The legacy-free variant has no ROM and has PCI_CLASS_DISPLAY_OTHER -instead of PCI_CLASS_DISPLAY_VGA. - - -IO ports used -------------- - -Doesn't apply to the legacy-free pci variant, use the MMIO bar instead. - -03c0 - 03df : standard vga ports -01ce : bochs vbe interface index port -01cf : bochs vbe interface data port (x86 only) -01d0 : bochs vbe interface data port - - -Memory regions used -------------------- - -0xe0000000 : Framebuffer memory, isa variant only. - -The pci variant used to mirror the framebuffer bar here, qemu 0.14+ -stops doing that (except when in -M pc-$old compat mode). - - -MMIO area spec --------------- - -Likewise applies to the pci variant only for obvious reasons. - -0000 - 03ff : edid data blob. -0400 - 041f : vga ioports (0x3c0 -> 0x3df), remapped 1:1. - word access is supported, bytes are written - in little endia order (aka index port first), - so indexed registers can be updated with a - single mmio write (and thus only one vmexit). -0500 - 0515 : bochs dispi interface registers, mapped flat - without index/data ports. Use (index << 1) - as offset for (16bit) register access. - -0600 - 0607 : qemu extended registers. qemu 2.2+ only. - The pci revision is 2 (or greater) when - these registers are present. The registers - are 32bit. - 0600 : qemu extended register region size, in bytes. - 0604 : framebuffer endianness register. - - 0xbebebebe indicates big endian. - - 0x1e1e1e1e indicates little endian. diff --git a/hw/display/vga-isa.c b/hw/display/vga-isa.c index 2a5437d803..c096ec93e5 100644 --- a/hw/display/vga-isa.c +++ b/hw/display/vga-isa.c @@ -1,7 +1,7 @@ /* * QEMU ISA VGA Emulator. * - * see docs/specs/standard-vga.txt for virtual hardware specs. + * see docs/specs/standard-vga.rst for virtual hardware specs. * * Copyright (c) 2003 Fabrice Bellard * diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c index b351b8f299..e4f45b4476 100644 --- a/hw/display/vga-pci.c +++ b/hw/display/vga-pci.c @@ -1,7 +1,7 @@ /* * QEMU PCI VGA Emulator. * - * see docs/specs/standard-vga.txt for virtual hardware specs. + * see docs/specs/standard-vga.rst for virtual hardware specs. * * Copyright (c) 2003 Fabrice Bellard * From 6e0c849275250ff9d1b3b2ae32f6a4b1247ed745 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 27 Sep 2023 16:12:03 +0100 Subject: [PATCH 353/974] docs/specs/virt-ctlr: Convert to rST MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert docs/specs/virt-ctlr.txt to rST format. I added the name of the device to give readers a bit more idea of which device we're actually documenting here. Signed-off-by: Peter Maydell Message-id: 20230927151205.70930-7-peter.maydell@linaro.org Reviewed-by: Philippe Mathieu-Daudé --- MAINTAINERS | 1 + docs/specs/index.rst | 1 + docs/specs/{virt-ctlr.txt => virt-ctlr.rst} | 12 +++++------- 3 files changed, 7 insertions(+), 7 deletions(-) rename docs/specs/{virt-ctlr.txt => virt-ctlr.rst} (70%) diff --git a/MAINTAINERS b/MAINTAINERS index b5e1765d7a..11f3bdbfa7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1283,6 +1283,7 @@ F: include/hw/char/goldfish_tty.h F: include/hw/intc/goldfish_pic.h F: include/hw/intc/m68k_irqc.h F: include/hw/misc/virt_ctrl.h +F: docs/specs/virt-ctlr.rst MicroBlaze Machines ------------------- diff --git a/docs/specs/index.rst b/docs/specs/index.rst index ee84b8109d..8d30968650 100644 --- a/docs/specs/index.rst +++ b/docs/specs/index.rst @@ -29,3 +29,4 @@ guest hardware that is specific to QEMU. ivshmem-spec pvpanic standard-vga + virt-ctlr diff --git a/docs/specs/virt-ctlr.txt b/docs/specs/virt-ctlr.rst similarity index 70% rename from docs/specs/virt-ctlr.txt rename to docs/specs/virt-ctlr.rst index 24d38084f7..ad3edde82d 100644 --- a/docs/specs/virt-ctlr.txt +++ b/docs/specs/virt-ctlr.rst @@ -1,9 +1,9 @@ Virtual System Controller ========================= -This device is a simple interface defined for the pure virtual machine with no -hardware reference implementation to allow the guest kernel to send command -to the host hypervisor. +The ``virt-ctrl`` device is a simple interface defined for the pure +virtual machine with no hardware reference implementation to allow the +guest kernel to send command to the host hypervisor. The specification can evolve, the current state is defined as below. @@ -11,14 +11,12 @@ This is a MMIO mapped device using 256 bytes. Two 32bit registers are defined: -1- the features register (read-only, address 0x00) - +the features register (read-only, address 0x00) This register allows the device to report features supported by the controller. The only feature supported for the moment is power control (0x01). -2- the command register (write-only, address 0x04) - +the command register (write-only, address 0x04) This register allows the kernel to send the commands to the hypervisor. The implemented commands are part of the power control feature and are reset (1), halt (2) and panic (3). From 096d3ce2316253919cfcb2ac9fead1fd315b2c98 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 27 Sep 2023 16:12:04 +0100 Subject: [PATCH 354/974] docs/specs/vmcoreinfo: Convert to rST MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert docs/specs/vmcoreinfo.txt to rST format. Signed-off-by: Peter Maydell Message-id: 20230927151205.70930-8-peter.maydell@linaro.org Reviewed-by: Philippe Mathieu-Daudé Acked-by: Marc-André Lureau --- MAINTAINERS | 1 + docs/specs/index.rst | 1 + docs/specs/vmcoreinfo.rst | 54 +++++++++++++++++++++++++++++++++++++++ docs/specs/vmcoreinfo.txt | 53 -------------------------------------- 4 files changed, 56 insertions(+), 53 deletions(-) create mode 100644 docs/specs/vmcoreinfo.rst delete mode 100644 docs/specs/vmcoreinfo.txt diff --git a/MAINTAINERS b/MAINTAINERS index 11f3bdbfa7..6d654cc127 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2884,6 +2884,7 @@ F: include/sysemu/dump.h F: qapi/dump.json F: scripts/dump-guest-memory.py F: stubs/dump.c +F: docs/specs/vmcoreinfo.rst Error reporting M: Markus Armbruster diff --git a/docs/specs/index.rst b/docs/specs/index.rst index 8d30968650..7a56ccb215 100644 --- a/docs/specs/index.rst +++ b/docs/specs/index.rst @@ -30,3 +30,4 @@ guest hardware that is specific to QEMU. pvpanic standard-vga virt-ctlr + vmcoreinfo diff --git a/docs/specs/vmcoreinfo.rst b/docs/specs/vmcoreinfo.rst new file mode 100644 index 0000000000..6541aa116f --- /dev/null +++ b/docs/specs/vmcoreinfo.rst @@ -0,0 +1,54 @@ +================= +VMCoreInfo device +================= + +The ``-device vmcoreinfo`` will create a ``fw_cfg`` entry for a guest to +store dump details. + +``etc/vmcoreinfo`` +================== + +A guest may use this ``fw_cfg`` entry to add information details to QEMU +dumps. + +The entry of 16 bytes has the following layout, in little-endian:: + + #define VMCOREINFO_FORMAT_NONE 0x0 + #define VMCOREINFO_FORMAT_ELF 0x1 + + struct FWCfgVMCoreInfo { + uint16_t host_format; /* formats host supports */ + uint16_t guest_format; /* format guest supplies */ + uint32_t size; /* size of vmcoreinfo region */ + uint64_t paddr; /* physical address of vmcoreinfo region */ + }; + +Only full write (of 16 bytes) are considered valid for further +processing of entry values. + +A write of 0 in ``guest_format`` will disable further processing of +vmcoreinfo entry values & content. + +You may write a ``guest_format`` that is not supported by the host, in +which case the entry data can be ignored by QEMU (but you may still +access it through a debugger, via ``vmcoreinfo_realize::vmcoreinfo_state``). + +Format & content +================ + +As of QEMU 2.11, only ``VMCOREINFO_FORMAT_ELF`` is supported. + +The entry gives location and size of an ELF note that is appended in +qemu dumps. + +The note format/class must be of the target bitness and the size must +be less than 1Mb. + +If the ELF note name is ``VMCOREINFO``, it is expected to be the Linux +vmcoreinfo note (see `the kernel documentation for its format +`_). +In this case, qemu dump code will read the content +as a key=value text file, looking for ``NUMBER(phys_base)`` key +value. The value is expected to be more accurate than architecture +guess of the value. This is useful for KASLR-enabled guest with +ancient tools not handling the ``VMCOREINFO`` note. diff --git a/docs/specs/vmcoreinfo.txt b/docs/specs/vmcoreinfo.txt deleted file mode 100644 index bcbca6fe47..0000000000 --- a/docs/specs/vmcoreinfo.txt +++ /dev/null @@ -1,53 +0,0 @@ -================= -VMCoreInfo device -================= - -The `-device vmcoreinfo` will create a fw_cfg entry for a guest to -store dump details. - -etc/vmcoreinfo -************** - -A guest may use this fw_cfg entry to add information details to qemu -dumps. - -The entry of 16 bytes has the following layout, in little-endian:: - -#define VMCOREINFO_FORMAT_NONE 0x0 -#define VMCOREINFO_FORMAT_ELF 0x1 - - struct FWCfgVMCoreInfo { - uint16_t host_format; /* formats host supports */ - uint16_t guest_format; /* format guest supplies */ - uint32_t size; /* size of vmcoreinfo region */ - uint64_t paddr; /* physical address of vmcoreinfo region */ - }; - -Only full write (of 16 bytes) are considered valid for further -processing of entry values. - -A write of 0 in guest_format will disable further processing of -vmcoreinfo entry values & content. - -You may write a guest_format that is not supported by the host, in -which case the entry data can be ignored by qemu (but you may still -access it through a debugger, via vmcoreinfo_realize::vmcoreinfo_state). - -Format & content -**************** - -As of qemu 2.11, only VMCOREINFO_FORMAT_ELF is supported. - -The entry gives location and size of an ELF note that is appended in -qemu dumps. - -The note format/class must be of the target bitness and the size must -be less than 1Mb. - -If the ELF note name is "VMCOREINFO", it is expected to be the Linux -vmcoreinfo note (see Documentation/ABI/testing/sysfs-kernel-vmcoreinfo -in Linux source). In this case, qemu dump code will read the content -as a key=value text file, looking for "NUMBER(phys_base)" key -value. The value is expected to be more accurate than architecture -guess of the value. This is useful for KASLR-enabled guest with -ancient tools not handling the VMCOREINFO note. From 912fb3678b8a8469476718d187b62aa8e85d8161 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 27 Sep 2023 16:12:05 +0100 Subject: [PATCH 355/974] docs/specs/vmgenid: Convert to rST Convert docs/specs/vmgenid.txt to rST format. Reviewed-by: Ani Sinha Signed-off-by: Peter Maydell Message-id: 20230927151205.70930-9-peter.maydell@linaro.org --- MAINTAINERS | 2 +- docs/specs/index.rst | 1 + docs/specs/vmgenid.rst | 246 +++++++++++++++++++++++++++++++++++++++++ docs/specs/vmgenid.txt | 245 ---------------------------------------- 4 files changed, 248 insertions(+), 246 deletions(-) create mode 100644 docs/specs/vmgenid.rst delete mode 100644 docs/specs/vmgenid.txt diff --git a/MAINTAINERS b/MAINTAINERS index 6d654cc127..397c3967f6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2437,7 +2437,7 @@ S: Orphan R: Ani Sinha F: hw/acpi/vmgenid.c F: include/hw/acpi/vmgenid.h -F: docs/specs/vmgenid.txt +F: docs/specs/vmgenid.rst F: tests/qtest/vmgenid-test.c LED diff --git a/docs/specs/index.rst b/docs/specs/index.rst index 7a56ccb215..b3f482b0aa 100644 --- a/docs/specs/index.rst +++ b/docs/specs/index.rst @@ -31,3 +31,4 @@ guest hardware that is specific to QEMU. standard-vga virt-ctlr vmcoreinfo + vmgenid diff --git a/docs/specs/vmgenid.rst b/docs/specs/vmgenid.rst new file mode 100644 index 0000000000..9a3cefcd82 --- /dev/null +++ b/docs/specs/vmgenid.rst @@ -0,0 +1,246 @@ +Virtual Machine Generation ID Device +==================================== + +.. + Copyright (C) 2016 Red Hat, Inc. + Copyright (C) 2017 Skyport Systems, Inc. + + This work is licensed under the terms of the GNU GPL, version 2 or later. + See the COPYING file in the top-level directory. + +The VM generation ID (``vmgenid``) device is an emulated device which +exposes a 128-bit, cryptographically random, integer value identifier, +referred to as a Globally Unique Identifier, or GUID. + +This allows management applications (e.g. libvirt) to notify the guest +operating system when the virtual machine is executed with a different +configuration (e.g. snapshot execution or creation from a template). The +guest operating system notices the change, and is then able to react as +appropriate by marking its copies of distributed databases as dirty, +re-initializing its random number generator etc. + + +Requirements +------------ + +These requirements are extracted from the "How to implement virtual machine +generation ID support in a virtualization platform" section of +`the Microsoft Virtual Machine Generation ID specification +`_ dated August 1, 2012. + +- **R1a** The generation ID shall live in an 8-byte aligned buffer. + +- **R1b** The buffer holding the generation ID shall be in guest RAM, + ROM, or device MMIO range. + +- **R1c** The buffer holding the generation ID shall be kept separate from + areas used by the operating system. + +- **R1d** The buffer shall not be covered by an AddressRangeMemory or + AddressRangeACPI entry in the E820 or UEFI memory map. + +- **R1e** The generation ID shall not live in a page frame that could be + mapped with caching disabled. (In other words, regardless of whether the + generation ID lives in RAM, ROM or MMIO, it shall only be mapped as + cacheable.) + +- **R2** to **R5** [These AML requirements are isolated well enough in the + Microsoft specification for us to simply refer to them here.] + +- **R6** The hypervisor shall expose a _HID (hardware identifier) object + in the VMGenId device's scope that is unique to the hypervisor vendor. + + +QEMU Implementation +------------------- + +The above-mentioned specification does not dictate which ACPI descriptor table +will contain the VM Generation ID device. Other implementations (Hyper-V and +Xen) put it in the main descriptor table (Differentiated System Description +Table or DSDT). For ease of debugging and implementation, we have decided to +put it in its own Secondary System Description Table, or SSDT. + +The following is a dump of the contents from a running system:: + + # iasl -p ./SSDT -d /sys/firmware/acpi/tables/SSDT + + Intel ACPI Component Architecture + ASL+ Optimizing Compiler version 20150717-64 + Copyright (c) 2000 - 2015 Intel Corporation + + Reading ACPI table from file /sys/firmware/acpi/tables/SSDT - Length + 00000198 (0x0000C6) + ACPI: SSDT 0x0000000000000000 0000C6 (v01 BOCHS VMGENID 00000001 BXPC 00000001) + Acpi table [SSDT] successfully installed and loaded + Pass 1 parse of [SSDT] + Pass 2 parse of [SSDT] + Parsing Deferred Opcodes (Methods/Buffers/Packages/Regions) + + Parsing completed + Disassembly completed + ASL Output: ./SSDT.dsl - 1631 bytes + # cat SSDT.dsl + /* + * Intel ACPI Component Architecture + * AML/ASL+ Disassembler version 20150717-64 + * Copyright (c) 2000 - 2015 Intel Corporation + * + * Disassembling to symbolic ASL+ operators + * + * Disassembly of /sys/firmware/acpi/tables/SSDT, Sun Feb 5 00:19:37 2017 + * + * Original Table Header: + * Signature "SSDT" + * Length 0x000000CA (202) + * Revision 0x01 + * Checksum 0x4B + * OEM ID "BOCHS " + * OEM Table ID "VMGENID" + * OEM Revision 0x00000001 (1) + * Compiler ID "BXPC" + * Compiler Version 0x00000001 (1) + */ + DefinitionBlock ("/sys/firmware/acpi/tables/SSDT.aml", "SSDT", 1, "BOCHS ", "VMGENID", 0x00000001) + { + Name (VGIA, 0x07FFF000) + Scope (\_SB) + { + Device (VGEN) + { + Name (_HID, "QEMUVGID") // _HID: Hardware ID + Name (_CID, "VM_Gen_Counter") // _CID: Compatible ID + Name (_DDN, "VM_Gen_Counter") // _DDN: DOS Device Name + Method (_STA, 0, NotSerialized) // _STA: Status + { + Local0 = 0x0F + If ((VGIA == Zero)) + { + Local0 = Zero + } + + Return (Local0) + } + + Method (ADDR, 0, NotSerialized) + { + Local0 = Package (0x02) {} + Index (Local0, Zero) = (VGIA + 0x28) + Index (Local0, One) = Zero + Return (Local0) + } + } + } + + Method (\_GPE._E05, 0, NotSerialized) // _Exx: Edge-Triggered GPE + { + Notify (\_SB.VGEN, 0x80) // Status Change + } + } + + +Design Details: +--------------- + +Requirements R1a through R1e dictate that the memory holding the +VM Generation ID must be allocated and owned by the guest firmware, +in this case BIOS or UEFI. However, to be useful, QEMU must be able to +change the contents of the memory at runtime, specifically when starting a +backed-up or snapshotted image. In order to do this, QEMU must know the +address that has been allocated. + +The mechanism chosen for this memory sharing is writable fw_cfg blobs. +These are data object that are visible to both QEMU and guests, and are +addressable as sequential files. + +More information about fw_cfg can be found in :doc:`fw_cfg`. + +Two fw_cfg blobs are used in this case: + +``/etc/vmgenid_guid`` + +- contains the actual VM Generation ID GUID +- read-only to the guest + +``/etc/vmgenid_addr`` + +- contains the address of the downloaded vmgenid blob +- writable by the guest + + +QEMU sends the following commands to the guest at startup: + +1. Allocate memory for vmgenid_guid fw_cfg blob. +2. Write the address of vmgenid_guid into the SSDT (VGIA ACPI variable as + shown above in the iasl dump). Note that this change is not propagated + back to QEMU. +3. Write the address of vmgenid_guid back to QEMU's copy of vmgenid_addr + via the fw_cfg DMA interface. + +After step 3, QEMU is able to update the contents of vmgenid_guid at will. + +Since BIOS or UEFI does not necessarily run when we wish to change the GUID, +the value of VGIA is persisted via the VMState mechanism. + +As spelled out in the specification, any change to the GUID executes an +ACPI notification. The exact handler to use is not specified, so the vmgenid +device uses the first unused one: ``\_GPE._E05``. + + +Endian-ness Considerations: +--------------------------- + +Although not specified in Microsoft's document, it is assumed that the +device is expected to use little-endian format. + +All GUID passed in via command line or monitor are treated as big-endian. +GUID values displayed via monitor are shown in big-endian format. + + +GUID Storage Format: +-------------------- + +In order to implement an OVMF "SDT Header Probe Suppressor", the contents of +the vmgenid_guid fw_cfg blob are not simply a 128-bit GUID. There is also +significant padding in order to align and fill a memory page, as shown in the +following diagram:: + + +----------------------------------+ + | SSDT with OEM Table ID = VMGENID | + +----------------------------------+ + | ... | TOP OF PAGE + | VGIA dword object ---------------|-----> +---------------------------+ + | ... | | fw-allocated array for | + | _STA method referring to VGIA | | "etc/vmgenid_guid" | + | ... | +---------------------------+ + | ADDR method referring to VGIA | | 0: OVMF SDT Header probe | + | ... | | suppressor | + +----------------------------------+ | 36: padding for 8-byte | + | alignment | + | 40: GUID | + | 56: padding to page size | + +---------------------------+ + END OF PAGE + + +Device Usage: +------------- + +The device has one property, which may be only be set using the command line: + +``guid`` + sets the value of the GUID. A special value ``auto`` instructs + QEMU to generate a new random GUID. + +For example:: + + QEMU -device vmgenid,guid="324e6eaf-d1d1-4bf6-bf41-b9bb6c91fb87" + QEMU -device vmgenid,guid=auto + +The property may be queried via QMP/HMP:: + + (QEMU) query-vm-generation-id + {"return": {"guid": "324e6eaf-d1d1-4bf6-bf41-b9bb6c91fb87"}} + +Setting of this parameter is intentionally left out from the QMP/HMP +interfaces. There are no known use cases for changing the GUID once QEMU is +running, and adding this capability would greatly increase the complexity. diff --git a/docs/specs/vmgenid.txt b/docs/specs/vmgenid.txt deleted file mode 100644 index 80ff69f31c..0000000000 --- a/docs/specs/vmgenid.txt +++ /dev/null @@ -1,245 +0,0 @@ -VIRTUAL MACHINE GENERATION ID -============================= - -Copyright (C) 2016 Red Hat, Inc. -Copyright (C) 2017 Skyport Systems, Inc. - -This work is licensed under the terms of the GNU GPL, version 2 or later. -See the COPYING file in the top-level directory. - -=== - -The VM generation ID (vmgenid) device is an emulated device which -exposes a 128-bit, cryptographically random, integer value identifier, -referred to as a Globally Unique Identifier, or GUID. - -This allows management applications (e.g. libvirt) to notify the guest -operating system when the virtual machine is executed with a different -configuration (e.g. snapshot execution or creation from a template). The -guest operating system notices the change, and is then able to react as -appropriate by marking its copies of distributed databases as dirty, -re-initializing its random number generator etc. - - -Requirements ------------- - -These requirements are extracted from the "How to implement virtual machine -generation ID support in a virtualization platform" section of the -specification, dated August 1, 2012. - - -The document may be found on the web at: - http://go.microsoft.com/fwlink/?LinkId=260709 - -R1a. The generation ID shall live in an 8-byte aligned buffer. - -R1b. The buffer holding the generation ID shall be in guest RAM, ROM, or device - MMIO range. - -R1c. The buffer holding the generation ID shall be kept separate from areas - used by the operating system. - -R1d. The buffer shall not be covered by an AddressRangeMemory or - AddressRangeACPI entry in the E820 or UEFI memory map. - -R1e. The generation ID shall not live in a page frame that could be mapped with - caching disabled. (In other words, regardless of whether the generation ID - lives in RAM, ROM or MMIO, it shall only be mapped as cacheable.) - -R2 to R5. [These AML requirements are isolated well enough in the Microsoft - specification for us to simply refer to them here.] - -R6. The hypervisor shall expose a _HID (hardware identifier) object in the - VMGenId device's scope that is unique to the hypervisor vendor. - - -QEMU Implementation -------------------- - -The above-mentioned specification does not dictate which ACPI descriptor table -will contain the VM Generation ID device. Other implementations (Hyper-V and -Xen) put it in the main descriptor table (Differentiated System Description -Table or DSDT). For ease of debugging and implementation, we have decided to -put it in its own Secondary System Description Table, or SSDT. - -The following is a dump of the contents from a running system: - -# iasl -p ./SSDT -d /sys/firmware/acpi/tables/SSDT - -Intel ACPI Component Architecture -ASL+ Optimizing Compiler version 20150717-64 -Copyright (c) 2000 - 2015 Intel Corporation - -Reading ACPI table from file /sys/firmware/acpi/tables/SSDT - Length -00000198 (0x0000C6) -ACPI: SSDT 0x0000000000000000 0000C6 (v01 BOCHS VMGENID 00000001 BXPC -00000001) -Acpi table [SSDT] successfully installed and loaded -Pass 1 parse of [SSDT] -Pass 2 parse of [SSDT] -Parsing Deferred Opcodes (Methods/Buffers/Packages/Regions) - -Parsing completed -Disassembly completed -ASL Output: ./SSDT.dsl - 1631 bytes -# cat SSDT.dsl -/* - * Intel ACPI Component Architecture - * AML/ASL+ Disassembler version 20150717-64 - * Copyright (c) 2000 - 2015 Intel Corporation - * - * Disassembling to symbolic ASL+ operators - * - * Disassembly of /sys/firmware/acpi/tables/SSDT, Sun Feb 5 00:19:37 2017 - * - * Original Table Header: - * Signature "SSDT" - * Length 0x000000CA (202) - * Revision 0x01 - * Checksum 0x4B - * OEM ID "BOCHS " - * OEM Table ID "VMGENID" - * OEM Revision 0x00000001 (1) - * Compiler ID "BXPC" - * Compiler Version 0x00000001 (1) - */ -DefinitionBlock ("/sys/firmware/acpi/tables/SSDT.aml", "SSDT", 1, "BOCHS ", -"VMGENID", 0x00000001) -{ - Name (VGIA, 0x07FFF000) - Scope (\_SB) - { - Device (VGEN) - { - Name (_HID, "QEMUVGID") // _HID: Hardware ID - Name (_CID, "VM_Gen_Counter") // _CID: Compatible ID - Name (_DDN, "VM_Gen_Counter") // _DDN: DOS Device Name - Method (_STA, 0, NotSerialized) // _STA: Status - { - Local0 = 0x0F - If ((VGIA == Zero)) - { - Local0 = Zero - } - - Return (Local0) - } - - Method (ADDR, 0, NotSerialized) - { - Local0 = Package (0x02) {} - Index (Local0, Zero) = (VGIA + 0x28) - Index (Local0, One) = Zero - Return (Local0) - } - } - } - - Method (\_GPE._E05, 0, NotSerialized) // _Exx: Edge-Triggered GPE - { - Notify (\_SB.VGEN, 0x80) // Status Change - } -} - - -Design Details: ---------------- - -Requirements R1a through R1e dictate that the memory holding the -VM Generation ID must be allocated and owned by the guest firmware, -in this case BIOS or UEFI. However, to be useful, QEMU must be able to -change the contents of the memory at runtime, specifically when starting a -backed-up or snapshotted image. In order to do this, QEMU must know the -address that has been allocated. - -The mechanism chosen for this memory sharing is writable fw_cfg blobs. -These are data object that are visible to both QEMU and guests, and are -addressable as sequential files. - -More information about fw_cfg can be found in "docs/specs/fw_cfg.txt" - -Two fw_cfg blobs are used in this case: - -/etc/vmgenid_guid - contains the actual VM Generation ID GUID - - read-only to the guest -/etc/vmgenid_addr - contains the address of the downloaded vmgenid blob - - writable by the guest - - -QEMU sends the following commands to the guest at startup: - -1. Allocate memory for vmgenid_guid fw_cfg blob. -2. Write the address of vmgenid_guid into the SSDT (VGIA ACPI variable as - shown above in the iasl dump). Note that this change is not propagated - back to QEMU. -3. Write the address of vmgenid_guid back to QEMU's copy of vmgenid_addr - via the fw_cfg DMA interface. - -After step 3, QEMU is able to update the contents of vmgenid_guid at will. - -Since BIOS or UEFI does not necessarily run when we wish to change the GUID, -the value of VGIA is persisted via the VMState mechanism. - -As spelled out in the specification, any change to the GUID executes an -ACPI notification. The exact handler to use is not specified, so the vmgenid -device uses the first unused one: \_GPE._E05. - - -Endian-ness Considerations: ---------------------------- - -Although not specified in Microsoft's document, it is assumed that the -device is expected to use little-endian format. - -All GUID passed in via command line or monitor are treated as big-endian. -GUID values displayed via monitor are shown in big-endian format. - - -GUID Storage Format: --------------------- - -In order to implement an OVMF "SDT Header Probe Suppressor", the contents of -the vmgenid_guid fw_cfg blob are not simply a 128-bit GUID. There is also -significant padding in order to align and fill a memory page, as shown in the -following diagram: - -+----------------------------------+ -| SSDT with OEM Table ID = VMGENID | -+----------------------------------+ -| ... | TOP OF PAGE -| VGIA dword object ---------------|-----> +---------------------------+ -| ... | | fw-allocated array for | -| _STA method referring to VGIA | | "etc/vmgenid_guid" | -| ... | +---------------------------+ -| ADDR method referring to VGIA | | 0: OVMF SDT Header probe | -| ... | | suppressor | -+----------------------------------+ | 36: padding for 8-byte | - | alignment | - | 40: GUID | - | 56: padding to page size | - +---------------------------+ - END OF PAGE - - -Device Usage: -------------- - -The device has one property, which may be only be set using the command line: - - guid - sets the value of the GUID. A special value "auto" instructs - QEMU to generate a new random GUID. - -For example: - - QEMU -device vmgenid,guid="324e6eaf-d1d1-4bf6-bf41-b9bb6c91fb87" - QEMU -device vmgenid,guid=auto - -The property may be queried via QMP/HMP: - - (QEMU) query-vm-generation-id - {"return": {"guid": "324e6eaf-d1d1-4bf6-bf41-b9bb6c91fb87"}} - -Setting of this parameter is intentionally left out from the QMP/HMP -interfaces. There are no known use cases for changing the GUID once QEMU is -running, and adding this capability would greatly increase the complexity. From 662c3eba8f2325b948463ed8ea368f297e9fb81c Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 27 Oct 2023 08:07:09 +0200 Subject: [PATCH 356/974] MAINTAINERS: Make sure that gicv3_internal.h is covered, too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gic_internal.h is already covered by the "ARM cores" section. Let's adapt the entry with a wildcard to cover gicv3_internal.h, too. Signed-off-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Message-id: 20231027060709.242388-1-thuth@redhat.com Signed-off-by: Peter Maydell --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 397c3967f6..8e8a7d5be5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -687,7 +687,7 @@ M: Peter Maydell L: qemu-arm@nongnu.org S: Maintained F: hw/intc/arm* -F: hw/intc/gic_internal.h +F: hw/intc/gic*_internal.h F: hw/misc/a9scu.c F: hw/misc/arm11scu.c F: hw/misc/arm_l2x0.c From 7df9a2285761eadbaf3cc6c8850aeb894f91b4f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 30 Oct 2023 09:37:05 +0100 Subject: [PATCH 357/974] hw/arm/pxa2xx_gpio: Pass CPU using QOM link property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of passing the CPU index and resolving it, use a QOM link to directly pass the CPU. Signed-off-by: Philippe Mathieu-Daudé Message-id: 20231030083706.63685-1-philmd@linaro.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/pxa2xx_gpio.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/hw/arm/pxa2xx_gpio.c b/hw/arm/pxa2xx_gpio.c index e7c3d99224..c8db5e8e2b 100644 --- a/hw/arm/pxa2xx_gpio.c +++ b/hw/arm/pxa2xx_gpio.c @@ -32,7 +32,6 @@ struct PXA2xxGPIOInfo { MemoryRegion iomem; qemu_irq irq0, irq1, irqX; int lines; - int ncpu; ARMCPU *cpu; /* XXX: GNU C vectors are more suitable */ @@ -266,12 +265,11 @@ static const MemoryRegionOps pxa_gpio_ops = { DeviceState *pxa2xx_gpio_init(hwaddr base, ARMCPU *cpu, DeviceState *pic, int lines) { - CPUState *cs = CPU(cpu); DeviceState *dev; dev = qdev_new(TYPE_PXA2XX_GPIO); qdev_prop_set_int32(dev, "lines", lines); - qdev_prop_set_int32(dev, "ncpu", cs->cpu_index); + object_property_set_link(OBJECT(dev), "cpu", OBJECT(cpu), &error_abort); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); @@ -303,8 +301,6 @@ static void pxa2xx_gpio_realize(DeviceState *dev, Error **errp) { PXA2xxGPIOInfo *s = PXA2XX_GPIO(dev); - s->cpu = ARM_CPU(qemu_get_cpu(s->ncpu)); - qdev_init_gpio_in(dev, pxa2xx_gpio_set, s->lines); qdev_init_gpio_out(dev, s->handler, s->lines); } @@ -339,7 +335,7 @@ static const VMStateDescription vmstate_pxa2xx_gpio_regs = { static Property pxa2xx_gpio_properties[] = { DEFINE_PROP_INT32("lines", PXA2xxGPIOInfo, lines, 0), - DEFINE_PROP_INT32("ncpu", PXA2xxGPIOInfo, ncpu, 0), + DEFINE_PROP_LINK("cpu", PXA2xxGPIOInfo, cpu, TYPE_ARM_CPU, ARMCPU *), DEFINE_PROP_END_OF_LIST(), }; From 18736a21417818bf6561fb3ededd64f1f90790c3 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sat, 28 Oct 2023 14:24:10 +0200 Subject: [PATCH 358/974] hw/watchdog/wdt_imx2: Trace MMIO access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Message-id: 20231028122415.14869-2-shentey@gmail.com Signed-off-by: Peter Maydell --- hw/watchdog/trace-events | 4 ++++ hw/watchdog/wdt_imx2.c | 24 ++++++++++++++++++------ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/hw/watchdog/trace-events b/hw/watchdog/trace-events index 2739570652..874968cc06 100644 --- a/hw/watchdog/trace-events +++ b/hw/watchdog/trace-events @@ -17,6 +17,10 @@ cmsdk_apb_watchdog_lock(uint32_t lock) "CMSDK APB watchdog: lock %" PRIu32 aspeed_wdt_read(uint64_t addr, uint32_t size) "@0x%" PRIx64 " size=%d" aspeed_wdt_write(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size=%d value=0x%"PRIx64 +# wdt_imx2.c +imx2_wdt_read(uint32_t addr, uint16_t data) "[0x%" PRIx32 "] -> 0x%" PRIx16 +imx2_wdt_write(uint32_t addr, uint16_t data) "[0x%" PRIx32 "] <- 0x%" PRIx16 + # spapr_watchdog.c spapr_watchdog_start(uint64_t flags, uint64_t num, uint64_t timeout) "Flags 0x%" PRIx64 " num=%" PRId64 " %" PRIu64 "ms" spapr_watchdog_stop(uint64_t num, uint64_t ret) "num=%" PRIu64 " ret=%" PRId64 diff --git a/hw/watchdog/wdt_imx2.c b/hw/watchdog/wdt_imx2.c index e776a2fbd4..885ebd3978 100644 --- a/hw/watchdog/wdt_imx2.c +++ b/hw/watchdog/wdt_imx2.c @@ -17,6 +17,7 @@ #include "hw/qdev-properties.h" #include "hw/watchdog/wdt_imx2.h" +#include "trace.h" static void imx2_wdt_interrupt(void *opaque) { @@ -67,20 +68,29 @@ static void imx2_wdt_reset(DeviceState *dev) static uint64_t imx2_wdt_read(void *opaque, hwaddr addr, unsigned int size) { IMX2WdtState *s = IMX2_WDT(opaque); + uint16_t value = 0; switch (addr) { case IMX2_WDT_WCR: - return s->wcr; + value = s->wcr; + break; case IMX2_WDT_WSR: - return s->wsr; + value = s->wsr; + break; case IMX2_WDT_WRSR: - return s->wrsr; + value = s->wrsr; + break; case IMX2_WDT_WICR: - return s->wicr; + value = s->wicr; + break; case IMX2_WDT_WMCR: - return s->wmcr; + value = s->wmcr; + break; } - return 0; + + trace_imx2_wdt_read(addr, value); + + return value; } static void imx_wdt2_update_itimer(IMX2WdtState *s, bool start) @@ -137,6 +147,8 @@ static void imx2_wdt_write(void *opaque, hwaddr addr, { IMX2WdtState *s = IMX2_WDT(opaque); + trace_imx2_wdt_write(addr, value); + switch (addr) { case IMX2_WDT_WCR: if (s->wcr_locked) { From 88a9973e85f4a51a0270c760c03bf6707d8cb1fa Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sat, 28 Oct 2023 14:24:11 +0200 Subject: [PATCH 359/974] hw/watchdog/wdt_imx2: Trace timer activity Signed-off-by: Bernhard Beschow Message-id: 20231028122415.14869-3-shentey@gmail.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/watchdog/trace-events | 2 ++ hw/watchdog/wdt_imx2.c | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/hw/watchdog/trace-events b/hw/watchdog/trace-events index 874968cc06..ad3be1e9bd 100644 --- a/hw/watchdog/trace-events +++ b/hw/watchdog/trace-events @@ -20,6 +20,8 @@ aspeed_wdt_write(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " si # wdt_imx2.c imx2_wdt_read(uint32_t addr, uint16_t data) "[0x%" PRIx32 "] -> 0x%" PRIx16 imx2_wdt_write(uint32_t addr, uint16_t data) "[0x%" PRIx32 "] <- 0x%" PRIx16 +imx2_wdt_interrupt(void) "" +imx2_wdt_expired(void) "" # spapr_watchdog.c spapr_watchdog_start(uint64_t flags, uint64_t num, uint64_t timeout) "Flags 0x%" PRIx64 " num=%" PRId64 " %" PRIu64 "ms" diff --git a/hw/watchdog/wdt_imx2.c b/hw/watchdog/wdt_imx2.c index 885ebd3978..891d7beb2a 100644 --- a/hw/watchdog/wdt_imx2.c +++ b/hw/watchdog/wdt_imx2.c @@ -23,6 +23,8 @@ static void imx2_wdt_interrupt(void *opaque) { IMX2WdtState *s = IMX2_WDT(opaque); + trace_imx2_wdt_interrupt(); + s->wicr |= IMX2_WDT_WICR_WTIS; qemu_set_irq(s->irq, 1); } @@ -31,6 +33,8 @@ static void imx2_wdt_expired(void *opaque) { IMX2WdtState *s = IMX2_WDT(opaque); + trace_imx2_wdt_expired(); + s->wrsr = IMX2_WDT_WRSR_TOUT; /* Perform watchdog action if watchdog is enabled */ From bb2fc5b9958f183e63d839a6e6e10cdc4dad0981 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sat, 28 Oct 2023 14:24:12 +0200 Subject: [PATCH 360/974] hw/misc/imx7_snvs: Trace MMIO access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Message-id: 20231028122415.14869-4-shentey@gmail.com Signed-off-by: Peter Maydell --- hw/misc/imx7_snvs.c | 5 +++++ hw/misc/trace-events | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/hw/misc/imx7_snvs.c b/hw/misc/imx7_snvs.c index ee7698bd9c..a245f96cd4 100644 --- a/hw/misc/imx7_snvs.c +++ b/hw/misc/imx7_snvs.c @@ -16,9 +16,12 @@ #include "hw/misc/imx7_snvs.h" #include "qemu/module.h" #include "sysemu/runstate.h" +#include "trace.h" static uint64_t imx7_snvs_read(void *opaque, hwaddr offset, unsigned size) { + trace_imx7_snvs_read(offset, 0); + return 0; } @@ -28,6 +31,8 @@ static void imx7_snvs_write(void *opaque, hwaddr offset, const uint32_t value = v; const uint32_t mask = SNVS_LPCR_TOP | SNVS_LPCR_DP_EN; + trace_imx7_snvs_write(offset, value); + if (offset == SNVS_LPCR && ((value & mask) == mask)) { qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); } diff --git a/hw/misc/trace-events b/hw/misc/trace-events index 24ba7cc4d0..426a8472b6 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -115,6 +115,10 @@ msf2_sysreg_write_pll_status(void) "Invalid write to read only PLL status regist imx7_gpr_read(uint64_t offset) "addr 0x%08" PRIx64 imx7_gpr_write(uint64_t offset, uint64_t value) "addr 0x%08" PRIx64 "value 0x%08" PRIx64 +# imx7_snvs.c +imx7_snvs_read(uint64_t offset, uint32_t value) "addr 0x%08" PRIx64 "value 0x%08" PRIx32 +imx7_snvs_write(uint64_t offset, uint32_t value) "addr 0x%08" PRIx64 "value 0x%08" PRIx32 + # mos6522.c mos6522_set_counter(int index, unsigned int val) "T%d.counter=%d" mos6522_get_next_irq_time(uint16_t latch, int64_t d, int64_t delta) "latch=%d counter=0x%"PRIx64 " delta_next=0x%"PRIx64 From 3839aff84cfc6c87189afe8fa0589f2a85dc7b0c Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sat, 28 Oct 2023 14:24:13 +0200 Subject: [PATCH 361/974] hw/misc/imx6_ccm: Convert DPRINTF to trace events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Message-id: 20231028122415.14869-5-shentey@gmail.com [PMM: Add "Hz" unit indicator to frequency traces] Signed-off-by: Peter Maydell --- hw/misc/imx6_ccm.c | 41 ++++++++++++++--------------------------- hw/misc/trace-events | 15 +++++++++++++++ 2 files changed, 29 insertions(+), 27 deletions(-) diff --git a/hw/misc/imx6_ccm.c b/hw/misc/imx6_ccm.c index 4c830fd89a..85af466c2b 100644 --- a/hw/misc/imx6_ccm.c +++ b/hw/misc/imx6_ccm.c @@ -15,18 +15,7 @@ #include "migration/vmstate.h" #include "qemu/log.h" #include "qemu/module.h" - -#ifndef DEBUG_IMX6_CCM -#define DEBUG_IMX6_CCM 0 -#endif - -#define DPRINTF(fmt, args...) \ - do { \ - if (DEBUG_IMX6_CCM) { \ - fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX6_CCM, \ - __func__, ##args); \ - } \ - } while (0) +#include "trace.h" static const char *imx6_ccm_reg_name(uint32_t reg) { @@ -263,7 +252,7 @@ static uint64_t imx6_analog_get_pll2_clk(IMX6CCMState *dev) freq *= 20; } - DPRINTF("freq = %u\n", (uint32_t)freq); + trace_imx6_analog_get_pll2_clk(freq); return freq; } @@ -275,7 +264,7 @@ static uint64_t imx6_analog_get_pll2_pfd0_clk(IMX6CCMState *dev) freq = imx6_analog_get_pll2_clk(dev) * 18 / EXTRACT(dev->analog[CCM_ANALOG_PFD_528], PFD0_FRAC); - DPRINTF("freq = %u\n", (uint32_t)freq); + trace_imx6_analog_get_pll2_pfd0_clk(freq); return freq; } @@ -287,7 +276,7 @@ static uint64_t imx6_analog_get_pll2_pfd2_clk(IMX6CCMState *dev) freq = imx6_analog_get_pll2_clk(dev) * 18 / EXTRACT(dev->analog[CCM_ANALOG_PFD_528], PFD2_FRAC); - DPRINTF("freq = %u\n", (uint32_t)freq); + trace_imx6_analog_get_pll2_pfd2_clk(freq); return freq; } @@ -315,7 +304,7 @@ static uint64_t imx6_analog_get_periph_clk(IMX6CCMState *dev) break; } - DPRINTF("freq = %u\n", (uint32_t)freq); + trace_imx6_analog_get_periph_clk(freq); return freq; } @@ -327,7 +316,7 @@ static uint64_t imx6_ccm_get_ahb_clk(IMX6CCMState *dev) freq = imx6_analog_get_periph_clk(dev) / (1 + EXTRACT(dev->ccm[CCM_CBCDR], AHB_PODF)); - DPRINTF("freq = %u\n", (uint32_t)freq); + trace_imx6_ccm_get_ahb_clk(freq); return freq; } @@ -339,7 +328,7 @@ static uint64_t imx6_ccm_get_ipg_clk(IMX6CCMState *dev) freq = imx6_ccm_get_ahb_clk(dev) / (1 + EXTRACT(dev->ccm[CCM_CBCDR], IPG_PODF)); - DPRINTF("freq = %u\n", (uint32_t)freq); + trace_imx6_ccm_get_ipg_clk(freq); return freq; } @@ -351,7 +340,7 @@ static uint64_t imx6_ccm_get_per_clk(IMX6CCMState *dev) freq = imx6_ccm_get_ipg_clk(dev) / (1 + EXTRACT(dev->ccm[CCM_CSCMR1], PERCLK_PODF)); - DPRINTF("freq = %u\n", (uint32_t)freq); + trace_imx6_ccm_get_per_clk(freq); return freq; } @@ -385,7 +374,7 @@ static uint32_t imx6_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock) break; } - DPRINTF("Clock = %d) = %u\n", clock, freq); + trace_imx6_ccm_get_clock_frequency(clock, freq); return freq; } @@ -394,7 +383,7 @@ static void imx6_ccm_reset(DeviceState *dev) { IMX6CCMState *s = IMX6_CCM(dev); - DPRINTF("\n"); + trace_imx6_ccm_reset(); s->ccm[CCM_CCR] = 0x040116FF; s->ccm[CCM_CCDR] = 0x00000000; @@ -483,7 +472,7 @@ static uint64_t imx6_ccm_read(void *opaque, hwaddr offset, unsigned size) value = s->ccm[index]; - DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx6_ccm_reg_name(index), value); + trace_imx6_ccm_read(imx6_ccm_reg_name(index), value); return (uint64_t)value; } @@ -494,8 +483,7 @@ static void imx6_ccm_write(void *opaque, hwaddr offset, uint64_t value, uint32_t index = offset >> 2; IMX6CCMState *s = (IMX6CCMState *)opaque; - DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx6_ccm_reg_name(index), - (uint32_t)value); + trace_imx6_ccm_write(imx6_ccm_reg_name(index), (uint32_t)value); /* * We will do a better implementation later. In particular some bits @@ -591,7 +579,7 @@ static uint64_t imx6_analog_read(void *opaque, hwaddr offset, unsigned size) break; } - DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx6_analog_reg_name(index), value); + trace_imx6_analog_read(imx6_analog_reg_name(index), value); return (uint64_t)value; } @@ -602,8 +590,7 @@ static void imx6_analog_write(void *opaque, hwaddr offset, uint64_t value, uint32_t index = offset >> 2; IMX6CCMState *s = (IMX6CCMState *)opaque; - DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx6_analog_reg_name(index), - (uint32_t)value); + trace_imx6_analog_write(imx6_analog_reg_name(index), (uint32_t)value); switch (index) { case CCM_ANALOG_PLL_ARM_SET: diff --git a/hw/misc/trace-events b/hw/misc/trace-events index 426a8472b6..05ff692441 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -196,6 +196,21 @@ iotkit_secctl_s_write(uint32_t offset, uint64_t data, unsigned size) "IoTKit Sec iotkit_secctl_ns_read(uint32_t offset, uint64_t data, unsigned size) "IoTKit SecCtl NS regs read: offset 0x%x data 0x%" PRIx64 " size %u" iotkit_secctl_ns_write(uint32_t offset, uint64_t data, unsigned size) "IoTKit SecCtl NS regs write: offset 0x%x data 0x%" PRIx64 " size %u" +# imx6_ccm.c +imx6_analog_get_periph_clk(uint32_t freq) "freq = %u Hz" +imx6_analog_get_pll2_clk(uint32_t freq) "freq = %u Hz" +imx6_analog_get_pll2_pfd0_clk(uint32_t freq) "freq = %u Hz" +imx6_analog_get_pll2_pfd2_clk(uint32_t freq) "freq = %u Hz" +imx6_analog_read(const char *reg, uint32_t value) "reg[%s] => 0x%" PRIx32 +imx6_analog_write(const char *reg, uint32_t value) "reg[%s] <= 0x%" PRIx32 +imx6_ccm_get_ahb_clk(uint32_t freq) "freq = %u Hz" +imx6_ccm_get_ipg_clk(uint32_t freq) "freq = %u Hz" +imx6_ccm_get_per_clk(uint32_t freq) "freq = %u Hz" +imx6_ccm_get_clock_frequency(unsigned clock, uint32_t freq) "(Clock = %d) = %u" +imx6_ccm_read(const char *reg, uint32_t value) "reg[%s] => 0x%" PRIx32 +imx6_ccm_reset(void) "" +imx6_ccm_write(const char *reg, uint32_t value) "reg[%s] <= 0x%" PRIx32 + # imx6ul_ccm.c ccm_entry(void) "" ccm_freq(uint32_t freq) "freq = %d" From c6e1b31bce523710024bca1b529f2117033003a7 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sat, 28 Oct 2023 14:24:14 +0200 Subject: [PATCH 362/974] hw/i2c/pm_smbus: Convert DPRINTF to trace events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let the trace messages slightly deviate from the function names ("smb" -> "smbus") being traced in order to avoid conflights with the SMB protocol. Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Acked-by: Corey Minyard Message-id: 20231028122415.14869-6-shentey@gmail.com Signed-off-by: Peter Maydell --- hw/i2c/pm_smbus.c | 18 ++++-------------- hw/i2c/trace-events | 6 ++++++ 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/hw/i2c/pm_smbus.c b/hw/i2c/pm_smbus.c index 4e1b8a5182..78e7c229a8 100644 --- a/hw/i2c/pm_smbus.c +++ b/hw/i2c/pm_smbus.c @@ -23,6 +23,7 @@ #include "hw/i2c/pm_smbus.h" #include "hw/i2c/smbus_master.h" #include "migration/vmstate.h" +#include "trace.h" #define SMBHSTSTS 0x00 #define SMBHSTCNT 0x02 @@ -64,15 +65,6 @@ #define AUX_BLK (1 << 1) #define AUX_MASK 0x3 -/*#define DEBUG*/ - -#ifdef DEBUG -# define SMBUS_DPRINTF(format, ...) printf(format, ## __VA_ARGS__) -#else -# define SMBUS_DPRINTF(format, ...) do { } while (0) -#endif - - static void smb_transaction(PMSMBus *s) { uint8_t prot = (s->smb_ctl >> 2) & 0x07; @@ -82,7 +74,7 @@ static void smb_transaction(PMSMBus *s) I2CBus *bus = s->smbus; int ret; - SMBUS_DPRINTF("SMBus trans addr=0x%02x prot=0x%02x\n", addr, prot); + trace_smbus_transaction(addr, prot); /* Transaction isn't exec if STS_DEV_ERR bit set */ if ((s->smb_stat & STS_DEV_ERR) != 0) { goto error; @@ -258,8 +250,7 @@ static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val, PMSMBus *s = opaque; uint8_t clear_byte_done; - SMBUS_DPRINTF("SMB writeb port=0x%04" HWADDR_PRIx - " val=0x%02" PRIx64 "\n", addr, val); + trace_smbus_ioport_writeb(addr, val); switch(addr) { case SMBHSTSTS: clear_byte_done = s->smb_stat & val & STS_BYTE_DONE; @@ -429,8 +420,7 @@ static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width) val = 0; break; } - SMBUS_DPRINTF("SMB readb port=0x%04" HWADDR_PRIx " val=0x%02x\n", - addr, val); + trace_smbus_ioport_readb(addr, val); if (s->set_irq) { s->set_irq(s, smb_irq_value(s)); diff --git a/hw/i2c/trace-events b/hw/i2c/trace-events index d7b1e25858..6900e06eda 100644 --- a/hw/i2c/trace-events +++ b/hw/i2c/trace-events @@ -15,6 +15,12 @@ i2c_send_async(uint8_t address, uint8_t data) "send_async(addr:0x%02x) data:0x%0 i2c_recv(uint8_t address, uint8_t data) "recv(addr:0x%02x) data:0x%02x" i2c_ack(void) "" +# pm_smbus.c + +smbus_ioport_readb(uint16_t addr, uint8_t data) "[0x%04" PRIx16 "] -> val=0x%02x" +smbus_ioport_writeb(uint16_t addr, uint8_t data) "[0x%04" PRIx16 "] <- val=0x%02x" +smbus_transaction(uint8_t addr, uint8_t prot) "addr=0x%02x prot=0x%02x" + # allwinner_i2c.c allwinner_i2c_read(const char* reg_name, uint64_t offset, uint64_t value) "read %s [0x%" PRIx64 "]: -> 0x%" PRIx64 From a6b2c5a04ddae291417d5bfffaa25b13a7d8cd10 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 30 Oct 2023 17:39:58 +0000 Subject: [PATCH 363/974] target/arm: Enable FEAT_MOPS insns in user-mode emulation In user-mode emulation, we need to set the SCTLR_EL1.MSCEn bit to avoid all the FEAT_MOPS insns UNDEFing. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20231030174000.3792225-2-peter.maydell@linaro.org --- target/arm/cpu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 954328d72a..df6496b019 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -296,6 +296,8 @@ static void arm_cpu_reset_hold(Object *obj) env->cp15.sctlr_el[1] |= SCTLR_TSCXT; /* Disable access to Debug Communication Channel (DCC). */ env->cp15.mdscr_el1 |= 1 << 12; + /* Enable FEAT_MOPS */ + env->cp15.sctlr_el[1] |= SCTLR_MSCEN; #else /* Reset into the highest available EL */ if (arm_feature(env, ARM_FEATURE_EL3)) { From 63c1b7de0a66e4ff248bdb51a0c3914de4eff034 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 30 Oct 2023 17:39:59 +0000 Subject: [PATCH 364/974] linux-user: Report AArch64 hwcap2 fields above bit 31 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AArch64 ELF hwcap2 field is 64 bits, but our get_elf_hwcap2() works with uint32_t, so it accidentally fails to report any hwcaps over bit 31. Use uint64_t here. The Arm hwcap2 is only 32 bits (because the ELF format makes these fields be the size of "long" in the ABI), but since it shares the prototype declaration for get_elf_hwcap2() it is easier to also expand it to 64 bits. The only hwcap fields we implement already that are affected by this are the HBC and MOPS ones, neither of which were implemented in a previous release, so this doesn't need backporting to older stable branches. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20231030174000.3792225-3-peter.maydell@linaro.org --- linux-user/elfload.c | 8 ++++---- linux-user/loader.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 3f3975352a..4cd6891d7b 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -552,10 +552,10 @@ uint32_t get_elf_hwcap(void) return hwcaps; } -uint32_t get_elf_hwcap2(void) +uint64_t get_elf_hwcap2(void) { ARMCPU *cpu = ARM_CPU(thread_cpu); - uint32_t hwcaps = 0; + uint64_t hwcaps = 0; GET_FEATURE_ID(aa32_aes, ARM_HWCAP2_ARM_AES); GET_FEATURE_ID(aa32_pmull, ARM_HWCAP2_ARM_PMULL); @@ -818,10 +818,10 @@ uint32_t get_elf_hwcap(void) return hwcaps; } -uint32_t get_elf_hwcap2(void) +uint64_t get_elf_hwcap2(void) { ARMCPU *cpu = ARM_CPU(thread_cpu); - uint32_t hwcaps = 0; + uint64_t hwcaps = 0; GET_FEATURE_ID(aa64_dcpodp, ARM_HWCAP2_A64_DCPODP); GET_FEATURE_ID(aa64_sve2, ARM_HWCAP2_A64_SVE2); diff --git a/linux-user/loader.h b/linux-user/loader.h index a0834290e7..e102e6f410 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -103,7 +103,7 @@ uint32_t get_elf_hwcap(void); const char *elf_hwcap_str(uint32_t bit); #endif #if defined(TARGET_AARCH64) || defined(TARGET_ARM) -uint32_t get_elf_hwcap2(void); +uint64_t get_elf_hwcap2(void); const char *elf_hwcap2_str(uint32_t bit); #endif From 854c001f121578c96b023b5db0c5550250505a0e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 30 Oct 2023 17:40:00 +0000 Subject: [PATCH 365/974] target/arm: Make FEAT_MOPS SET* insns handle Xs == XZR correctly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most of the registers used by the FEAT_MOPS instructions cannot use 31 as a register field value; this is CONSTRAINED UNPREDICTABLE to NOP or UNDEF (we UNDEF). However, it is permitted for the "source value" register for the memset insns SET* to be 31, which (as usual for most data-processing insns) means it should be the zero register XZR. We forgot to handle this case, with the effect that trying to set memory to zero with a "SET* Xd, Xn, XZR" sets the memory to the value that happens to be in the low byte of SP. Handle XZR when getting the SET* data value from the register file. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20231030174000.3792225-4-peter.maydell@linaro.org --- target/arm/tcg/helper-a64.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index 84f54750fc..ce4800b8d1 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -1206,6 +1206,15 @@ static void check_setg_alignment(CPUARMState *env, uint64_t ptr, uint64_t size, } } +static uint64_t arm_reg_or_xzr(CPUARMState *env, int reg) +{ + /* + * Runtime equivalent of cpu_reg() -- return the CPU register value, + * for contexts when index 31 means XZR (not SP). + */ + return reg == 31 ? 0 : env->xregs[reg]; +} + /* * For the Memory Set operation, our implementation chooses * always to use "option A", where we update Xd to the final @@ -1226,7 +1235,7 @@ static void do_setp(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc, int rd = mops_destreg(syndrome); int rs = mops_srcreg(syndrome); int rn = mops_sizereg(syndrome); - uint8_t data = env->xregs[rs]; + uint8_t data = arm_reg_or_xzr(env, rs); uint32_t memidx = FIELD_EX32(mtedesc, MTEDESC, MIDX); uint64_t toaddr = env->xregs[rd]; uint64_t setsize = env->xregs[rn]; @@ -1286,7 +1295,7 @@ static void do_setm(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc, int rd = mops_destreg(syndrome); int rs = mops_srcreg(syndrome); int rn = mops_sizereg(syndrome); - uint8_t data = env->xregs[rs]; + uint8_t data = arm_reg_or_xzr(env, rs); uint64_t toaddr = env->xregs[rd] + env->xregs[rn]; uint64_t setsize = -env->xregs[rn]; uint32_t memidx = FIELD_EX32(mtedesc, MTEDESC, MIDX); @@ -1349,7 +1358,7 @@ static void do_sete(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc, int rd = mops_destreg(syndrome); int rs = mops_srcreg(syndrome); int rn = mops_sizereg(syndrome); - uint8_t data = env->xregs[rs]; + uint8_t data = arm_reg_or_xzr(env, rs); uint64_t toaddr = env->xregs[rd] + env->xregs[rn]; uint64_t setsize = -env->xregs[rn]; uint32_t memidx = FIELD_EX32(mtedesc, MTEDESC, MIDX); From b11293c212c2927fcea1befc50dabec9baba4fcc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 31 Oct 2023 07:32:15 -0700 Subject: [PATCH 366/974] target/arm: Fix SVE STR increment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous change missed updating one of the increments and one of the MemOps. Add a test case for all vector lengths. Cc: qemu-stable@nongnu.org Fixes: e6dd5e782be ("target/arm: Use tcg_gen_qemu_{ld, st}_i128 in gen_sve_{ld, st}r") Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20231031143215.29764-1-richard.henderson@linaro.org [PMM: fixed checkpatch nit] Signed-off-by: Peter Maydell --- target/arm/tcg/translate-sve.c | 5 ++-- tests/tcg/aarch64/Makefile.target | 6 +++- tests/tcg/aarch64/sve-str.c | 49 +++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 tests/tcg/aarch64/sve-str.c diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 7b39962f20..296e7d1ce2 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -4294,7 +4294,7 @@ void gen_sve_str(DisasContext *s, TCGv_ptr base, int vofs, t0 = tcg_temp_new_i64(); t1 = tcg_temp_new_i64(); t16 = tcg_temp_new_i128(); - for (i = 0; i < len_align; i += 8) { + for (i = 0; i < len_align; i += 16) { tcg_gen_ld_i64(t0, base, vofs + i); tcg_gen_ld_i64(t1, base, vofs + i + 8); tcg_gen_concat_i64_i128(t16, t0, t1); @@ -4320,7 +4320,8 @@ void gen_sve_str(DisasContext *s, TCGv_ptr base, int vofs, t16 = tcg_temp_new_i128(); tcg_gen_concat_i64_i128(t16, t0, t1); - tcg_gen_qemu_st_i128(t16, clean_addr, midx, MO_LEUQ); + tcg_gen_qemu_st_i128(t16, clean_addr, midx, + MO_LE | MO_128 | MO_ATOM_NONE); tcg_gen_addi_i64(clean_addr, clean_addr, 16); tcg_gen_brcondi_ptr(TCG_COND_LTU, i, len_align, loop); diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target index 0c84b61ae0..cded1d01fc 100644 --- a/tests/tcg/aarch64/Makefile.target +++ b/tests/tcg/aarch64/Makefile.target @@ -103,7 +103,11 @@ sha512-sve: CFLAGS=-O3 -march=armv8.1-a+sve sha512-sve: sha512.c $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) -TESTS += sha512-sve +sve-str: CFLAGS=-O1 -march=armv8.1-a+sve +sve-str: sve-str.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) + +TESTS += sha512-sve sve-str ifneq ($(GDB),) GDB_SCRIPT=$(SRC_PATH)/tests/guest-debug/run-test.py diff --git a/tests/tcg/aarch64/sve-str.c b/tests/tcg/aarch64/sve-str.c new file mode 100644 index 0000000000..ae271c9d87 --- /dev/null +++ b/tests/tcg/aarch64/sve-str.c @@ -0,0 +1,49 @@ +#include +#include + +#define N (256 + 16) + +static int __attribute__((noinline)) test(int vl) +{ + unsigned char buf[N]; + int err = 0; + + for (int i = 0; i < N; ++i) { + buf[i] = (unsigned char)i; + } + + asm volatile ( + "mov z0.b, #255\n\t" + "str z0, %0" + : : "m" (buf) : "z0", "memory"); + + for (int i = 0; i < vl; ++i) { + if (buf[i] != 0xff) { + fprintf(stderr, "vl %d, index %d, expected 255, got %d\n", + vl, i, buf[i]); + err = 1; + } + } + + for (int i = vl; i < N; ++i) { + if (buf[i] != (unsigned char)i) { + fprintf(stderr, "vl %d, index %d, expected %d, got %d\n", + vl, i, (unsigned char)i, buf[i]); + err = 1; + } + } + + return err; +} + +int main() +{ + int err = 0; + + for (int i = 16; i <= 256; i += 16) { + if (prctl(PR_SVE_SET_VL, i, 0, 0, 0, 0) == i) { + err |= test(i); + } + } + return err; +} From b80e20dbbfe4b1c609493a591c151407de8ee1bd Mon Sep 17 00:00:00 2001 From: Hans-Erik Floryd Date: Mon, 30 Oct 2023 16:15:16 +0100 Subject: [PATCH 367/974] hw/char/stm32f2xx_usart: Extract common IRQ update code to update_irq() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Signed-off-by: Hans-Erik Floryd Message-id: 20231030151528.1138131-2-hans-erik.floryd@rt-labs.com Signed-off-by: Peter Maydell --- hw/char/stm32f2xx_usart.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/hw/char/stm32f2xx_usart.c b/hw/char/stm32f2xx_usart.c index fde67f4f03..519d3461a3 100644 --- a/hw/char/stm32f2xx_usart.c +++ b/hw/char/stm32f2xx_usart.c @@ -53,6 +53,17 @@ static int stm32f2xx_usart_can_receive(void *opaque) return 0; } +static void stm32f2xx_update_irq(STM32F2XXUsartState *s) +{ + uint32_t mask = s->usart_sr & s->usart_cr1; + + if (mask & (USART_SR_TXE | USART_SR_TC | USART_SR_RXNE)) { + qemu_set_irq(s->irq, 1); + } else { + qemu_set_irq(s->irq, 0); + } +} + static void stm32f2xx_usart_receive(void *opaque, const uint8_t *buf, int size) { STM32F2XXUsartState *s = opaque; @@ -66,9 +77,7 @@ static void stm32f2xx_usart_receive(void *opaque, const uint8_t *buf, int size) s->usart_dr = *buf; s->usart_sr |= USART_SR_RXNE; - if (s->usart_cr1 & USART_CR1_RXNEIE) { - qemu_set_irq(s->irq, 1); - } + stm32f2xx_update_irq(s); DB_PRINT("Receiving: %c\n", s->usart_dr); } @@ -85,7 +94,7 @@ static void stm32f2xx_usart_reset(DeviceState *dev) s->usart_cr3 = 0x00000000; s->usart_gtpr = 0x00000000; - qemu_set_irq(s->irq, 0); + stm32f2xx_update_irq(s); } static uint64_t stm32f2xx_usart_read(void *opaque, hwaddr addr, @@ -106,7 +115,7 @@ static uint64_t stm32f2xx_usart_read(void *opaque, hwaddr addr, retvalue = s->usart_dr & 0x3FF; s->usart_sr &= ~USART_SR_RXNE; qemu_chr_fe_accept_input(&s->chr); - qemu_set_irq(s->irq, 0); + stm32f2xx_update_irq(s); return retvalue; case USART_BRR: return s->usart_brr; @@ -145,9 +154,7 @@ static void stm32f2xx_usart_write(void *opaque, hwaddr addr, } else { s->usart_sr &= value; } - if (!(s->usart_sr & USART_SR_RXNE)) { - qemu_set_irq(s->irq, 0); - } + stm32f2xx_update_irq(s); return; case USART_DR: if (value < 0xF000) { @@ -168,10 +175,7 @@ static void stm32f2xx_usart_write(void *opaque, hwaddr addr, return; case USART_CR1: s->usart_cr1 = value; - if (s->usart_cr1 & USART_CR1_RXNEIE && - s->usart_sr & USART_SR_RXNE) { - qemu_set_irq(s->irq, 1); - } + stm32f2xx_update_irq(s); return; case USART_CR2: s->usart_cr2 = value; From 2e9cb201a8ad00b8b5eda98260aadabd1fdd0b82 Mon Sep 17 00:00:00 2001 From: Hans-Erik Floryd Date: Mon, 30 Oct 2023 16:15:17 +0100 Subject: [PATCH 368/974] hw/char/stm32f2xx_usart: Update IRQ when DR is written MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Signed-off-by: Hans-Erik Floryd Message-id: 20231030151528.1138131-3-hans-erik.floryd@rt-labs.com Signed-off-by: Peter Maydell --- hw/char/stm32f2xx_usart.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/char/stm32f2xx_usart.c b/hw/char/stm32f2xx_usart.c index 519d3461a3..8753afeb2b 100644 --- a/hw/char/stm32f2xx_usart.c +++ b/hw/char/stm32f2xx_usart.c @@ -168,6 +168,7 @@ static void stm32f2xx_usart_write(void *opaque, hwaddr addr, clear TC by writing 0 to the SR register, so set it again on each write. */ s->usart_sr |= USART_SR_TC; + stm32f2xx_update_irq(s); } return; case USART_BRR: From e9f30b1e7f9e25e73f626596620778b2561f0c2e Mon Sep 17 00:00:00 2001 From: Hans-Erik Floryd Date: Mon, 30 Oct 2023 16:15:18 +0100 Subject: [PATCH 369/974] hw/char/stm32f2xx_usart: Add more definitions for CR1 register MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Acked-by: Alistair Francis Signed-off-by: Hans-Erik Floryd Message-id: 20231030151528.1138131-4-hans-erik.floryd@rt-labs.com Signed-off-by: Peter Maydell --- include/hw/char/stm32f2xx_usart.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/include/hw/char/stm32f2xx_usart.h b/include/hw/char/stm32f2xx_usart.h index 65bcc85470..fdfa7424a7 100644 --- a/include/hw/char/stm32f2xx_usart.h +++ b/include/hw/char/stm32f2xx_usart.h @@ -48,10 +48,12 @@ #define USART_SR_TC (1 << 6) #define USART_SR_RXNE (1 << 5) -#define USART_CR1_UE (1 << 13) -#define USART_CR1_RXNEIE (1 << 5) -#define USART_CR1_TE (1 << 3) -#define USART_CR1_RE (1 << 2) +#define USART_CR1_UE (1 << 13) +#define USART_CR1_TXEIE (1 << 7) +#define USART_CR1_TCEIE (1 << 6) +#define USART_CR1_RXNEIE (1 << 5) +#define USART_CR1_TE (1 << 3) +#define USART_CR1_RE (1 << 2) #define TYPE_STM32F2XX_USART "stm32f2xx-usart" OBJECT_DECLARE_SIMPLE_TYPE(STM32F2XXUsartState, STM32F2XX_USART) From 4c09abeae8704970ff03bf2196973f6bf08ab6f9 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 31 Oct 2023 17:37:23 +0000 Subject: [PATCH 370/974] target/arm: Correctly propagate stage 1 BTI guarded bit in a two-stage walk In a two-stage translation, the result of the BTI guarded bit should be the guarded bit from the first stage of translation, as there is no BTI guard information in stage two. Our code tried to do this, but got it wrong, because we currently have two fields where the GP bit information might live (ARMCacheAttrs::guarded and CPUTLBEntryFull::extra::arm::guarded), and we were storing the GP bit in the latter during the stage 1 walk but trying to copy the former in combine_cacheattrs(). Remove the duplicated storage, and always use the field in CPUTLBEntryFull; correctly propagate the stage 1 value to the output in get_phys_addr_twostage(). Note for stable backports: in v8.0 and earlier the field is named result->f.guarded, not result->f.extra.arm.guarded. Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1950 Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20231031173723.26582-1-peter.maydell@linaro.org --- target/arm/internals.h | 1 - target/arm/ptw.c | 7 +++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/target/arm/internals.h b/target/arm/internals.h index f7224e6f4d..c837506e44 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1181,7 +1181,6 @@ typedef struct ARMCacheAttrs { unsigned int attrs:8; unsigned int shareability:2; /* as in the SH field of the VMSAv8-64 PTEs */ bool is_s2_format:1; - bool guarded:1; /* guarded bit of the v8-64 PTE */ } ARMCacheAttrs; /* Fields that are valid upon success. */ diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 53713e0300..1762b058ae 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -3032,7 +3032,6 @@ static ARMCacheAttrs combine_cacheattrs(uint64_t hcr, assert(!s1.is_s2_format); ret.is_s2_format = false; - ret.guarded = s1.guarded; if (s1.attrs == 0xf0) { tagged = true; @@ -3175,7 +3174,7 @@ static bool get_phys_addr_twostage(CPUARMState *env, S1Translate *ptw, hwaddr ipa; int s1_prot, s1_lgpgsz; ARMSecuritySpace in_space = ptw->in_space; - bool ret, ipa_secure; + bool ret, ipa_secure, s1_guarded; ARMCacheAttrs cacheattrs1; ARMSecuritySpace ipa_space; uint64_t hcr; @@ -3202,6 +3201,7 @@ static bool get_phys_addr_twostage(CPUARMState *env, S1Translate *ptw, */ s1_prot = result->f.prot; s1_lgpgsz = result->f.lg_page_size; + s1_guarded = result->f.extra.arm.guarded; cacheattrs1 = result->cacheattrs; memset(result, 0, sizeof(*result)); @@ -3252,6 +3252,9 @@ static bool get_phys_addr_twostage(CPUARMState *env, S1Translate *ptw, result->cacheattrs = combine_cacheattrs(hcr, cacheattrs1, result->cacheattrs); + /* No BTI GP information in stage 2, we just use the S1 value */ + result->f.extra.arm.guarded = s1_guarded; + /* * Check if IPA translates to secure or non-secure PA space. * Note that VSTCR overrides VTCR and {N}SW overrides {N}SA. From 4d7dd4ed4f42e659a214e4f81c1a15ee991352df Mon Sep 17 00:00:00 2001 From: Stephen Brennan Date: Mon, 18 Sep 2023 16:32:31 -0700 Subject: [PATCH 371/974] dump: Pass DumpState to write_ functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For the next patch, we need a reference to DumpState when writing data. Signed-off-by: Stephen Brennan Reviewed-by: Daniel P. Berrangé Reviewed-by: Marc-André Lureau Message-Id: <20230918233233.1431858-2-stephen.s.brennan@oracle.com> --- dump/dump.c | 40 ++++++++++++++++++++-------------------- include/sysemu/dump.h | 2 +- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/dump/dump.c b/dump/dump.c index d355ada62e..eec34c4738 100644 --- a/dump/dump.c +++ b/dump/dump.c @@ -809,7 +809,7 @@ static void create_vmcore(DumpState *s, Error **errp) dump_end(s, errp); } -static int write_start_flat_header(int fd) +static int write_start_flat_header(DumpState *s) { MakedumpfileHeader *mh; int ret = 0; @@ -824,7 +824,7 @@ static int write_start_flat_header(int fd) mh->version = cpu_to_be64(VERSION_FLAT_HEADER); size_t written_size; - written_size = qemu_write_full(fd, mh, MAX_SIZE_MDF_HEADER); + written_size = qemu_write_full(s->fd, mh, MAX_SIZE_MDF_HEADER); if (written_size != MAX_SIZE_MDF_HEADER) { ret = -1; } @@ -833,7 +833,7 @@ static int write_start_flat_header(int fd) return ret; } -static int write_end_flat_header(int fd) +static int write_end_flat_header(DumpState *s) { MakedumpfileDataHeader mdh; @@ -841,7 +841,7 @@ static int write_end_flat_header(int fd) mdh.buf_size = END_FLAG_FLAT_HEADER; size_t written_size; - written_size = qemu_write_full(fd, &mdh, sizeof(mdh)); + written_size = qemu_write_full(s->fd, &mdh, sizeof(mdh)); if (written_size != sizeof(mdh)) { return -1; } @@ -849,7 +849,7 @@ static int write_end_flat_header(int fd) return 0; } -static int write_buffer(int fd, off_t offset, const void *buf, size_t size) +static int write_buffer(DumpState *s, off_t offset, const void *buf, size_t size) { size_t written_size; MakedumpfileDataHeader mdh; @@ -857,12 +857,12 @@ static int write_buffer(int fd, off_t offset, const void *buf, size_t size) mdh.offset = cpu_to_be64(offset); mdh.buf_size = cpu_to_be64(size); - written_size = qemu_write_full(fd, &mdh, sizeof(mdh)); + written_size = qemu_write_full(s->fd, &mdh, sizeof(mdh)); if (written_size != sizeof(mdh)) { return -1; } - written_size = qemu_write_full(fd, buf, size); + written_size = qemu_write_full(s->fd, buf, size); if (written_size != size) { return -1; } @@ -982,7 +982,7 @@ static void create_header32(DumpState *s, Error **errp) #endif dh->status = cpu_to_dump32(s, status); - if (write_buffer(s->fd, 0, dh, size) < 0) { + if (write_buffer(s, 0, dh, size) < 0) { error_setg(errp, "dump: failed to write disk dump header"); goto out; } @@ -1012,7 +1012,7 @@ static void create_header32(DumpState *s, Error **errp) kh->offset_note = cpu_to_dump64(s, offset_note); kh->note_size = cpu_to_dump32(s, s->note_size); - if (write_buffer(s->fd, DISKDUMP_HEADER_BLOCKS * + if (write_buffer(s, DISKDUMP_HEADER_BLOCKS * block_size, kh, size) < 0) { error_setg(errp, "dump: failed to write kdump sub header"); goto out; @@ -1027,7 +1027,7 @@ static void create_header32(DumpState *s, Error **errp) if (*errp) { goto out; } - if (write_buffer(s->fd, offset_note, s->note_buf, + if (write_buffer(s, offset_note, s->note_buf, s->note_size) < 0) { error_setg(errp, "dump: failed to write notes"); goto out; @@ -1093,7 +1093,7 @@ static void create_header64(DumpState *s, Error **errp) #endif dh->status = cpu_to_dump32(s, status); - if (write_buffer(s->fd, 0, dh, size) < 0) { + if (write_buffer(s, 0, dh, size) < 0) { error_setg(errp, "dump: failed to write disk dump header"); goto out; } @@ -1123,7 +1123,7 @@ static void create_header64(DumpState *s, Error **errp) kh->offset_note = cpu_to_dump64(s, offset_note); kh->note_size = cpu_to_dump64(s, s->note_size); - if (write_buffer(s->fd, DISKDUMP_HEADER_BLOCKS * + if (write_buffer(s, DISKDUMP_HEADER_BLOCKS * block_size, kh, size) < 0) { error_setg(errp, "dump: failed to write kdump sub header"); goto out; @@ -1139,7 +1139,7 @@ static void create_header64(DumpState *s, Error **errp) goto out; } - if (write_buffer(s->fd, offset_note, s->note_buf, + if (write_buffer(s, offset_note, s->note_buf, s->note_size) < 0) { error_setg(errp, "dump: failed to write notes"); goto out; @@ -1204,7 +1204,7 @@ static int set_dump_bitmap(uint64_t last_pfn, uint64_t pfn, bool value, while (old_offset < new_offset) { /* calculate the offset and write dump_bitmap */ offset_bitmap1 = s->offset_dump_bitmap + old_offset; - if (write_buffer(s->fd, offset_bitmap1, buf, + if (write_buffer(s, offset_bitmap1, buf, bitmap_bufsize) < 0) { return -1; } @@ -1212,7 +1212,7 @@ static int set_dump_bitmap(uint64_t last_pfn, uint64_t pfn, bool value, /* dump level 1 is chosen, so 1st and 2nd bitmap are same */ offset_bitmap2 = s->offset_dump_bitmap + s->len_dump_bitmap + old_offset; - if (write_buffer(s->fd, offset_bitmap2, buf, + if (write_buffer(s, offset_bitmap2, buf, bitmap_bufsize) < 0) { return -1; } @@ -1380,7 +1380,7 @@ out: static void prepare_data_cache(DataCache *data_cache, DumpState *s, off_t offset) { - data_cache->fd = s->fd; + data_cache->state = s; data_cache->data_size = 0; data_cache->buf_size = 4 * dump_bitmap_get_bufsize(s); data_cache->buf = g_malloc0(data_cache->buf_size); @@ -1399,11 +1399,11 @@ static int write_cache(DataCache *dc, const void *buf, size_t size, /* * if flag_sync is set, synchronize data in dc->buf into vmcore. * otherwise check if the space is enough for caching data in buf, if not, - * write the data in dc->buf to dc->fd and reset dc->buf + * write the data in dc->buf to dc->state->fd and reset dc->buf */ if ((!flag_sync && dc->data_size + size > dc->buf_size) || (flag_sync && dc->data_size > 0)) { - if (write_buffer(dc->fd, dc->offset, dc->buf, dc->data_size) < 0) { + if (write_buffer(dc->state, dc->offset, dc->buf, dc->data_size) < 0) { return -1; } @@ -1644,7 +1644,7 @@ static void create_kdump_vmcore(DumpState *s, Error **errp) * +------------------------------------------+ */ - ret = write_start_flat_header(s->fd); + ret = write_start_flat_header(s); if (ret < 0) { error_setg(errp, "dump: failed to write start flat header"); return; @@ -1665,7 +1665,7 @@ static void create_kdump_vmcore(DumpState *s, Error **errp) return; } - ret = write_end_flat_header(s->fd); + ret = write_end_flat_header(s); if (ret < 0) { error_setg(errp, "dump: failed to write end flat header"); return; diff --git a/include/sysemu/dump.h b/include/sysemu/dump.h index 7008d43d04..e27af8fb34 100644 --- a/include/sysemu/dump.h +++ b/include/sysemu/dump.h @@ -137,7 +137,7 @@ typedef struct QEMU_PACKED KdumpSubHeader64 { } KdumpSubHeader64; typedef struct DataCache { - int fd; /* fd of the file where to write the cached data */ + DumpState *state; /* dump state related to this data */ uint8_t *buf; /* buffer for cached data */ size_t buf_size; /* size of the buf */ size_t data_size; /* size of cached data in buf */ From d43a01db285fd10f9c429476eb9c63fa5e00f3cc Mon Sep 17 00:00:00 2001 From: Stephen Brennan Date: Mon, 18 Sep 2023 16:32:32 -0700 Subject: [PATCH 372/974] dump: Allow directly outputting raw kdump format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The flattened format (currently output by QEMU) is used by makedumpfile only when it is outputting a vmcore to a file which is not seekable. The flattened format functions essentially as a set of instructions of the form "seek to the given offset, then write the given bytes out". The flattened format can be reconstructed using makedumpfile -R, or makedumpfile-R.pl, but it is a slow process because it requires copying the entire vmcore. The flattened format can also be directly read by crash, but still, it requires a lengthy reassembly phase. To sum up, the flattened format is not an ideal one: it should only be used on files which are actually not seekable. This is the exact strategy which makedumpfile uses, as seen in the implementation of "write_buffer()" in makedumpfile [1]. However, QEMU has always used the flattened format. For compatibility it is best not to change the default output format without warning. So, add a flag to DumpState which changes the output to use the normal (i.e. raw) format. This flag will be added to the QMP and HMP commands in the next change. [1]: https://github.com/makedumpfile/makedumpfile/blob/f23bb943568188a2746dbf9b6692668f5a2ac3b6/makedumpfile.c#L5008-L5040 Signed-off-by: Stephen Brennan Reviewed-by: Daniel P. Berrangé [ Marc-André: replace loff_t with off_t ] Reviewed-by: Marc-André Lureau Message-Id: <20230918233233.1431858-3-stephen.s.brennan@oracle.com> --- dump/dump.c | 32 +++++++++++++++++++++++++------- include/sysemu/dump.h | 1 + 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/dump/dump.c b/dump/dump.c index eec34c4738..0f913e1f5c 100644 --- a/dump/dump.c +++ b/dump/dump.c @@ -814,6 +814,10 @@ static int write_start_flat_header(DumpState *s) MakedumpfileHeader *mh; int ret = 0; + if (s->kdump_raw) { + return 0; + } + QEMU_BUILD_BUG_ON(sizeof *mh > MAX_SIZE_MDF_HEADER); mh = g_malloc0(MAX_SIZE_MDF_HEADER); @@ -837,6 +841,10 @@ static int write_end_flat_header(DumpState *s) { MakedumpfileDataHeader mdh; + if (s->kdump_raw) { + return 0; + } + mdh.offset = END_FLAG_FLAT_HEADER; mdh.buf_size = END_FLAG_FLAT_HEADER; @@ -853,13 +861,21 @@ static int write_buffer(DumpState *s, off_t offset, const void *buf, size_t size { size_t written_size; MakedumpfileDataHeader mdh; + off_t seek_loc; - mdh.offset = cpu_to_be64(offset); - mdh.buf_size = cpu_to_be64(size); + if (s->kdump_raw) { + seek_loc = lseek(s->fd, offset, SEEK_SET); + if (seek_loc == (off_t) -1) { + return -1; + } + } else { + mdh.offset = cpu_to_be64(offset); + mdh.buf_size = cpu_to_be64(size); - written_size = qemu_write_full(s->fd, &mdh, sizeof(mdh)); - if (written_size != sizeof(mdh)) { - return -1; + written_size = qemu_write_full(s->fd, &mdh, sizeof(mdh)); + if (written_size != sizeof(mdh)) { + return -1; + } } written_size = qemu_write_full(s->fd, buf, size); @@ -1775,7 +1791,8 @@ static void vmcoreinfo_update_phys_base(DumpState *s) static void dump_init(DumpState *s, int fd, bool has_format, DumpGuestMemoryFormat format, bool paging, bool has_filter, - int64_t begin, int64_t length, Error **errp) + int64_t begin, int64_t length, bool kdump_raw, + Error **errp) { ERRP_GUARD(); VMCoreInfoState *vmci = vmcoreinfo_find(); @@ -1786,6 +1803,7 @@ static void dump_init(DumpState *s, int fd, bool has_format, s->has_format = has_format; s->format = format; s->written_size = 0; + s->kdump_raw = kdump_raw; /* kdump-compressed is conflict with paging and filter */ if (has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) { @@ -2168,7 +2186,7 @@ void qmp_dump_guest_memory(bool paging, const char *file, dump_state_prepare(s); dump_init(s, fd, has_format, format, paging, has_begin, - begin, length, errp); + begin, length, false, errp); if (*errp) { qatomic_set(&s->status, DUMP_STATUS_FAILED); return; diff --git a/include/sysemu/dump.h b/include/sysemu/dump.h index e27af8fb34..d702854853 100644 --- a/include/sysemu/dump.h +++ b/include/sysemu/dump.h @@ -157,6 +157,7 @@ typedef struct DumpState { MemoryMappingList list; bool resume; bool detached; + bool kdump_raw; hwaddr memory_offset; int fd; From e6549197f7edf2c590894f51aaed2fa81991becc Mon Sep 17 00:00:00 2001 From: Stephen Brennan Date: Mon, 18 Sep 2023 16:32:33 -0700 Subject: [PATCH 373/974] dump: Add command interface for kdump-raw formats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The QMP dump API represents the dump format as an enumeration. Add three new enumerators, one for each supported kdump compression, each named "kdump-raw-*". For the HMP command line, rather than adding a new flag corresponding to each format, it seems more human-friendly to add a single flag "-R" to switch the kdump formats to "raw" mode. The choice of "-R" also correlates nicely to the "makedumpfile -R" option, which would serve to reassemble a flattened vmcore. Signed-off-by: Stephen Brennan Reviewed-by: Daniel P. Berrangé [ Marc-André: replace loff_t with off_t, indent fixes ] Reviewed-by: Marc-André Lureau Message-Id: <20230918233233.1431858-4-stephen.s.brennan@oracle.com> --- dump/dump-hmp-cmds.c | 21 +++++++++++++++++---- dump/dump.c | 33 ++++++++++++++++++++++++++++++++- hmp-commands.hx | 9 +++++++-- qapi/dump.json | 24 ++++++++++++++++++++---- 4 files changed, 76 insertions(+), 11 deletions(-) diff --git a/dump/dump-hmp-cmds.c b/dump/dump-hmp-cmds.c index b038785fee..b428ec33df 100644 --- a/dump/dump-hmp-cmds.c +++ b/dump/dump-hmp-cmds.c @@ -19,6 +19,7 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict) bool paging = qdict_get_try_bool(qdict, "paging", false); bool zlib = qdict_get_try_bool(qdict, "zlib", false); bool lzo = qdict_get_try_bool(qdict, "lzo", false); + bool raw = qdict_get_try_bool(qdict, "raw", false); bool snappy = qdict_get_try_bool(qdict, "snappy", false); const char *file = qdict_get_str(qdict, "filename"); bool has_begin = qdict_haskey(qdict, "begin"); @@ -40,16 +41,28 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict) dump_format = DUMP_GUEST_MEMORY_FORMAT_WIN_DMP; } - if (zlib) { - dump_format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_ZLIB; + if (zlib && raw) { + if (raw) { + dump_format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_RAW_ZLIB; + } else { + dump_format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_ZLIB; + } } if (lzo) { - dump_format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_LZO; + if (raw) { + dump_format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_RAW_LZO; + } else { + dump_format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_LZO; + } } if (snappy) { - dump_format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_SNAPPY; + if (raw) { + dump_format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_RAW_SNAPPY; + } else { + dump_format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_SNAPPY; + } } if (has_begin) { diff --git a/dump/dump.c b/dump/dump.c index 0f913e1f5c..44b27fef45 100644 --- a/dump/dump.c +++ b/dump/dump.c @@ -2090,6 +2090,7 @@ void qmp_dump_guest_memory(bool paging, const char *file, int fd = -1; DumpState *s; bool detach_p = false; + bool kdump_raw = false; if (runstate_check(RUN_STATE_INMIGRATE)) { error_setg(errp, "Dump not allowed during incoming migration."); @@ -2103,6 +2104,29 @@ void qmp_dump_guest_memory(bool paging, const char *file, return; } + /* + * externally, we represent kdump-raw-* as separate formats, but internally + * they are handled the same, except for the "raw" flag + */ + if (has_format) { + switch (format) { + case DUMP_GUEST_MEMORY_FORMAT_KDUMP_RAW_ZLIB: + format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_ZLIB; + kdump_raw = true; + break; + case DUMP_GUEST_MEMORY_FORMAT_KDUMP_RAW_LZO: + format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_LZO; + kdump_raw = true; + break; + case DUMP_GUEST_MEMORY_FORMAT_KDUMP_RAW_SNAPPY: + format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_SNAPPY; + kdump_raw = true; + break; + default: + break; + } + } + /* * kdump-compressed format need the whole memory dumped, so paging or * filter is not supported here. @@ -2166,6 +2190,10 @@ void qmp_dump_guest_memory(bool paging, const char *file, error_setg(errp, QERR_INVALID_PARAMETER, "protocol"); return; } + if (kdump_raw && lseek(fd, 0, SEEK_CUR) == (off_t) -1) { + error_setg(errp, "kdump-raw formats require a seekable file"); + return; + } if (!dump_migration_blocker) { error_setg(&dump_migration_blocker, @@ -2186,7 +2214,7 @@ void qmp_dump_guest_memory(bool paging, const char *file, dump_state_prepare(s); dump_init(s, fd, has_format, format, paging, has_begin, - begin, length, false, errp); + begin, length, kdump_raw, errp); if (*errp) { qatomic_set(&s->status, DUMP_STATUS_FAILED); return; @@ -2214,15 +2242,18 @@ DumpGuestMemoryCapability *qmp_query_dump_guest_memory_capability(Error **errp) /* kdump-zlib is always available */ QAPI_LIST_APPEND(tail, DUMP_GUEST_MEMORY_FORMAT_KDUMP_ZLIB); + QAPI_LIST_APPEND(tail, DUMP_GUEST_MEMORY_FORMAT_KDUMP_RAW_ZLIB); /* add new item if kdump-lzo is available */ #ifdef CONFIG_LZO QAPI_LIST_APPEND(tail, DUMP_GUEST_MEMORY_FORMAT_KDUMP_LZO); + QAPI_LIST_APPEND(tail, DUMP_GUEST_MEMORY_FORMAT_KDUMP_RAW_LZO); #endif /* add new item if kdump-snappy is available */ #ifdef CONFIG_SNAPPY QAPI_LIST_APPEND(tail, DUMP_GUEST_MEMORY_FORMAT_KDUMP_SNAPPY); + QAPI_LIST_APPEND(tail, DUMP_GUEST_MEMORY_FORMAT_KDUMP_RAW_SNAPPY); #endif if (win_dump_available(NULL)) { diff --git a/hmp-commands.hx b/hmp-commands.hx index 63eac22734..c0a27688b6 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1085,14 +1085,16 @@ ERST { .name = "dump-guest-memory", - .args_type = "paging:-p,detach:-d,windmp:-w,zlib:-z,lzo:-l,snappy:-s,filename:F,begin:l?,length:l?", - .params = "[-p] [-d] [-z|-l|-s|-w] filename [begin length]", + .args_type = "paging:-p,detach:-d,windmp:-w,zlib:-z,lzo:-l,snappy:-s,raw:-R,filename:F,begin:l?,length:l?", + .params = "[-p] [-d] [-z|-l|-s|-w] [-R] filename [begin length]", .help = "dump guest memory into file 'filename'.\n\t\t\t" "-p: do paging to get guest's memory mapping.\n\t\t\t" "-d: return immediately (do not wait for completion).\n\t\t\t" "-z: dump in kdump-compressed format, with zlib compression.\n\t\t\t" "-l: dump in kdump-compressed format, with lzo compression.\n\t\t\t" "-s: dump in kdump-compressed format, with snappy compression.\n\t\t\t" + "-R: when using kdump (-z, -l, -s), use raw rather than makedumpfile-flattened\n\t\t\t" + " format\n\t\t\t" "-w: dump in Windows crashdump format (can be used instead of ELF-dump converting),\n\t\t\t" " for Windows x86 and x64 guests with vmcoreinfo driver only.\n\t\t\t" "begin: the starting physical address.\n\t\t\t" @@ -1115,6 +1117,9 @@ SRST dump in kdump-compressed format, with lzo compression. ``-s`` dump in kdump-compressed format, with snappy compression. + ``-R`` + when using kdump (-z, -l, -s), use raw rather than makedumpfile-flattened + format ``-w`` dump in Windows crashdump format (can be used instead of ELF-dump converting), for Windows x64 guests with vmcoreinfo driver only diff --git a/qapi/dump.json b/qapi/dump.json index 4ae1f722a9..5cbc237ad9 100644 --- a/qapi/dump.json +++ b/qapi/dump.json @@ -15,11 +15,23 @@ # # @elf: elf format # -# @kdump-zlib: kdump-compressed format with zlib-compressed +# @kdump-zlib: makedumpfile flattened, kdump-compressed format with zlib +# compression # -# @kdump-lzo: kdump-compressed format with lzo-compressed +# @kdump-lzo: makedumpfile flattened, kdump-compressed format with lzo +# compression # -# @kdump-snappy: kdump-compressed format with snappy-compressed +# @kdump-snappy: makedumpfile flattened, kdump-compressed format with snappy +# compression +# +# @kdump-raw-zlib: raw assembled kdump-compressed format with zlib compression +# (since 8.2) +# +# @kdump-raw-lzo: raw assembled kdump-compressed format with lzo compression +# (since 8.2) +# +# @kdump-raw-snappy: raw assembled kdump-compressed format with snappy +# compression (since 8.2) # # @win-dmp: Windows full crashdump format, can be used instead of ELF # converting (since 2.13) @@ -27,7 +39,11 @@ # Since: 2.0 ## { 'enum': 'DumpGuestMemoryFormat', - 'data': [ 'elf', 'kdump-zlib', 'kdump-lzo', 'kdump-snappy', 'win-dmp' ] } + 'data': [ + 'elf', + 'kdump-zlib', 'kdump-lzo', 'kdump-snappy', + 'kdump-raw-zlib', 'kdump-raw-lzo', 'kdump-raw-snappy', + 'win-dmp' ] } ## # @dump-guest-memory: From 8beaeed73496395f75a3e65191d45e0e299fb25b Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 31 Oct 2023 11:45:27 +0100 Subject: [PATCH 374/974] dump: Rename qmp_dump_guest_memory() parameter to match QAPI schema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The name of the second parameter differs between QAPI schema and C implementation: it's @protocol in the former and @file in the latter. Potentially confusing. Change the C implementation to match the QAPI schema. Signed-off-by: Markus Armbruster Reviewed-by: Marc-André Lureau Message-Id: <20231031104531.3169721-2-armbru@redhat.com> --- dump/dump.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/dump/dump.c b/dump/dump.c index 44b27fef45..9cdb4a2bf8 100644 --- a/dump/dump.c +++ b/dump/dump.c @@ -2079,11 +2079,12 @@ DumpQueryResult *qmp_query_dump(Error **errp) return result; } -void qmp_dump_guest_memory(bool paging, const char *file, +void qmp_dump_guest_memory(bool paging, const char *protocol, bool has_detach, bool detach, - bool has_begin, int64_t begin, bool has_length, - int64_t length, bool has_format, - DumpGuestMemoryFormat format, Error **errp) + bool has_begin, int64_t begin, + bool has_length, int64_t length, + bool has_format, DumpGuestMemoryFormat format, + Error **errp) { ERRP_GUARD(); const char *p; @@ -2170,7 +2171,7 @@ void qmp_dump_guest_memory(bool paging, const char *file, } #if !defined(WIN32) - if (strstart(file, "fd:", &p)) { + if (strstart(protocol, "fd:", &p)) { fd = monitor_get_fd(monitor_cur(), p, errp); if (fd == -1) { return; @@ -2178,7 +2179,7 @@ void qmp_dump_guest_memory(bool paging, const char *file, } #endif - if (strstart(file, "file:", &p)) { + if (strstart(protocol, "file:", &p)) { fd = qemu_open_old(p, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR); if (fd < 0) { error_setg_file_open(errp, errno, p); From 96afbc571c91b115ba51d9740352a0e45111edc9 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 31 Oct 2023 11:45:28 +0100 Subject: [PATCH 375/974] dump: Fix g_array_unref(NULL) in dump-guest-memory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When dump_init()'s check for non-zero @length fails, dump_cleanup() passes null s->string_table_buf to g_array_unref(), which spews "GLib: g_array_unref: assertion 'array' failed" to stderr. Guard the g_array_unref(). Signed-off-by: Markus Armbruster Reviewed-by: Marc-André Lureau Message-Id: <20231031104531.3169721-3-armbru@redhat.com> --- dump/dump.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dump/dump.c b/dump/dump.c index 9cdb4a2bf8..24c829e705 100644 --- a/dump/dump.c +++ b/dump/dump.c @@ -100,7 +100,7 @@ static int dump_cleanup(DumpState *s) memory_mapping_list_free(&s->list); close(s->fd); g_free(s->guest_note); - g_array_unref(s->string_table_buf); + g_clear_pointer(&s->string_table_buf, g_array_unref); s->guest_note = NULL; if (s->resume) { if (s->detached) { From f8c49724cbe13fa30b5893eff33f9ccee7e4466a Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 31 Oct 2023 11:45:29 +0100 Subject: [PATCH 376/974] dump: Recognize "fd:" protocols on Windows hosts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A few QMP command can work with named file descriptors. The only way to create a named file descriptor used to be QMP command getfd, which only works on POSIX hosts. Thus, named file descriptors were actually usable only there. They became usable on Windows hosts when we added QMP command get-win32-socket (commit 4cda177c601 "qmp: add 'get-win32-socket'"). Except in dump-guest-memory, because qmp_dump_guest_memory() compiles its named file descriptor code only #if !defined(WIN32). Compile it unconditionally, like we do for the other commands supporting them. Signed-off-by: Markus Armbruster Reviewed-by: Marc-André Lureau Message-Id: <20231031104531.3169721-4-armbru@redhat.com> --- dump/dump.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/dump/dump.c b/dump/dump.c index 24c829e705..2d0b5bd22b 100644 --- a/dump/dump.c +++ b/dump/dump.c @@ -2170,14 +2170,12 @@ void qmp_dump_guest_memory(bool paging, const char *protocol, return; } -#if !defined(WIN32) if (strstart(protocol, "fd:", &p)) { fd = monitor_get_fd(monitor_cur(), p, errp); if (fd == -1) { return; } } -#endif if (strstart(protocol, "file:", &p)) { fd = qemu_open_old(p, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR); From 28035bed1c565eace7db18971a7e960a8d1f7c44 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 31 Oct 2023 11:45:30 +0100 Subject: [PATCH 377/974] dump: Improve some dump-guest-memory error messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Zero @length is rejected with "Invalid parameter 'length'". Improve to "parameter 'length' expects a non-zero length". qemu_open_old() is a wrapper around qemu_open_internal() that throws away error information. Switch to the wrapper that doesn't: qemu_create(). Example improvement: (qemu) dump-guest-memory /dev/fdset/x 0 1 Error: Could not open '/dev/fdset/x': Invalid argument becomes Error: Could not parse fdset /dev/fdset/x @protocol values not starting with "fd:" or "file:" are rejected with "Invalid parameter 'protocol'". Improve to "parameter 'protocol' must start with 'file:' or 'fd:'". While there, make the conditional checking @protocol a little more obvious. Signed-off-by: Markus Armbruster Reviewed-by: Marc-André Lureau Message-Id: <20231031104531.3169721-5-armbru@redhat.com> --- dump/dump.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/dump/dump.c b/dump/dump.c index 2d0b5bd22b..ec2cfcf9f7 100644 --- a/dump/dump.c +++ b/dump/dump.c @@ -1828,7 +1828,7 @@ static void dump_init(DumpState *s, int fd, bool has_format, s->fd = fd; if (has_filter && !length) { - error_setg(errp, QERR_INVALID_PARAMETER, "length"); + error_setg(errp, "parameter 'length' expects a non-zero size"); goto cleanup; } s->filter_area_begin = begin; @@ -2088,7 +2088,7 @@ void qmp_dump_guest_memory(bool paging, const char *protocol, { ERRP_GUARD(); const char *p; - int fd = -1; + int fd; DumpState *s; bool detach_p = false; bool kdump_raw = false; @@ -2175,18 +2175,14 @@ void qmp_dump_guest_memory(bool paging, const char *protocol, if (fd == -1) { return; } - } - - if (strstart(protocol, "file:", &p)) { - fd = qemu_open_old(p, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR); + } else if (strstart(protocol, "file:", &p)) { + fd = qemu_create(p, O_WRONLY | O_TRUNC | O_BINARY, S_IRUSR, errp); if (fd < 0) { - error_setg_file_open(errp, errno, p); return; } - } - - if (fd == -1) { - error_setg(errp, QERR_INVALID_PARAMETER, "protocol"); + } else { + error_setg(errp, + "parameter 'protocol' must start with 'file:' or 'fd:'"); return; } if (kdump_raw && lseek(fd, 0, SEEK_CUR) == (off_t) -1) { From 4023839757b2c01e0350ce92902c6f74dce7d011 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 31 Oct 2023 11:45:31 +0100 Subject: [PATCH 378/974] dump: Drop redundant check for empty dump MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dump_init() first computes the size of the dump, taking the filter area into account, and fails if its zero. It then looks for memory in the filter area, and fails if there is none. This is redundant: if the size of the dump is zero, there is no memory, and vice versa. Delete this check. Signed-off-by: Markus Armbruster Reviewed-by: Marc-André Lureau Message-Id: <20231031104531.3169721-6-armbru@redhat.com> --- dump/dump.c | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/dump/dump.c b/dump/dump.c index ec2cfcf9f7..1c304cadfd 100644 --- a/dump/dump.c +++ b/dump/dump.c @@ -1688,26 +1688,6 @@ static void create_kdump_vmcore(DumpState *s, Error **errp) } } -static int validate_start_block(DumpState *s) -{ - GuestPhysBlock *block; - - if (!dump_has_filter(s)) { - return 0; - } - - QTAILQ_FOREACH(block, &s->guest_phys_blocks.head, next) { - /* This block is out of the range */ - if (block->target_start >= s->filter_area_begin + s->filter_area_length || - block->target_end <= s->filter_area_begin) { - continue; - } - return 0; - } - - return -1; -} - static void get_max_mapnr(DumpState *s) { GuestPhysBlock *last_block; @@ -1857,12 +1837,6 @@ static void dump_init(DumpState *s, int fd, bool has_format, goto cleanup; } - /* Is the filter filtering everything? */ - if (validate_start_block(s) == -1) { - error_setg(errp, QERR_INVALID_PARAMETER, "begin"); - goto cleanup; - } - /* get dump info: endian, class and architecture. * If the target architecture is not supported, cpu_get_dump_info() will * return -1. From 921923583fb2583a277e3b3d6cb8e8e13c622d48 Mon Sep 17 00:00:00 2001 From: Tong Ho Date: Tue, 31 Oct 2023 11:46:09 -0700 Subject: [PATCH 379/974] hw/misc: Introduce AMD/Xilix Versal TRNG device This adds a non-cryptographic grade implementation of the model for the True Random Number Generator (TRNG) component in AMD/Xilinx Versal device family. This implements all 3 modes defined by the actual hardware specs, all of which selectable by guest software at will at anytime: 1) PRNG mode, in which the generated sequence is required to be reproducible after reseeded by the same 384-bit value as supplied by guest software. 2) Test mode, in which the generated sequence is required to be reproducible ater reseeded by the same 128-bit test seed supplied by guest software. 3) TRNG mode, in which non-reproducible sequence is generated based on periodic reseed by a suitable entropy source. This model is only intended for non-real world testing of guest software, where cryptographically strong PRNG or TRNG is not needed. This model supports versions 1 & 2 of the device, with default to be version 2; the 'hw-version' uint32 property can be set to 0x0100 to override the default. Other implemented properties: - 'forced-prng', uint64 When set to non-zero, mode 3's entropy source is implemented as a deterministic sequence based on the given value and other deterministic parameters. This option allows the emulation to test guest software using mode 3 and to reproduce data-dependent defects. - 'fips-fault-events', uint32, bit-mask bit 3: Triggers the SP800-90B entropy health test fault irq bit 1: Triggers the FIPS 140-2 continuous test fault irq Signed-off-by: Tong Ho Message-id: 20231031184611.3029156-2-tong.ho@amd.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/misc/Kconfig | 3 + hw/misc/meson.build | 3 + hw/misc/xlnx-versal-trng.c | 717 +++++++++++++++++++++++++++++ include/hw/misc/xlnx-versal-trng.h | 58 +++ 4 files changed, 781 insertions(+) create mode 100644 hw/misc/xlnx-versal-trng.c create mode 100644 include/hw/misc/xlnx-versal-trng.h diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index dba41afe67..cc8a8c1418 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -197,4 +197,7 @@ config DJMEMC config IOSB bool +config XLNX_VERSAL_TRNG + bool + source macio/Kconfig diff --git a/hw/misc/meson.build b/hw/misc/meson.build index f60de33f9a..36c20d5637 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -104,6 +104,9 @@ system_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files( 'xlnx-cfi-if.c', 'xlnx-versal-cframe-reg.c', )) +system_ss.add(when: 'CONFIG_XLNX_VERSAL_TRNG', if_true: files( + 'xlnx-versal-trng.c', +)) system_ss.add(when: 'CONFIG_STM32F2XX_SYSCFG', if_true: files('stm32f2xx_syscfg.c')) system_ss.add(when: 'CONFIG_STM32F4XX_SYSCFG', if_true: files('stm32f4xx_syscfg.c')) system_ss.add(when: 'CONFIG_STM32F4XX_EXTI', if_true: files('stm32f4xx_exti.c')) diff --git a/hw/misc/xlnx-versal-trng.c b/hw/misc/xlnx-versal-trng.c new file mode 100644 index 0000000000..4d41c262c4 --- /dev/null +++ b/hw/misc/xlnx-versal-trng.c @@ -0,0 +1,717 @@ +/* + * Non-crypto strength model of the True Random Number Generator + * in the AMD/Xilinx Versal device family. + * + * Copyright (c) 2017-2020 Xilinx Inc. + * Copyright (c) 2023 Advanced Micro Devices, Inc. + * + * Written by Edgar E. Iglesias + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu/osdep.h" +#include "hw/misc/xlnx-versal-trng.h" + +#include "qemu/bitops.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "qemu/guest-random.h" +#include "qemu/timer.h" +#include "qapi/visitor.h" +#include "migration/vmstate.h" +#include "hw/qdev-properties.h" + +#ifndef XLNX_VERSAL_TRNG_ERR_DEBUG +#define XLNX_VERSAL_TRNG_ERR_DEBUG 0 +#endif + +REG32(INT_CTRL, 0x0) + FIELD(INT_CTRL, CERTF_RST, 5, 1) + FIELD(INT_CTRL, DTF_RST, 4, 1) + FIELD(INT_CTRL, DONE_RST, 3, 1) + FIELD(INT_CTRL, CERTF_EN, 2, 1) + FIELD(INT_CTRL, DTF_EN, 1, 1) + FIELD(INT_CTRL, DONE_EN, 0, 1) +REG32(STATUS, 0x4) + FIELD(STATUS, QCNT, 9, 3) + FIELD(STATUS, EAT, 4, 5) + FIELD(STATUS, CERTF, 3, 1) + FIELD(STATUS, DTF, 1, 1) + FIELD(STATUS, DONE, 0, 1) +REG32(CTRL, 0x8) + FIELD(CTRL, PERSODISABLE, 10, 1) + FIELD(CTRL, SINGLEGENMODE, 9, 1) + FIELD(CTRL, EUMODE, 8, 1) + FIELD(CTRL, PRNGMODE, 7, 1) + FIELD(CTRL, TSTMODE, 6, 1) + FIELD(CTRL, PRNGSTART, 5, 1) + FIELD(CTRL, EATAU, 4, 1) + FIELD(CTRL, PRNGXS, 3, 1) + FIELD(CTRL, TRSSEN, 2, 1) + FIELD(CTRL, QERTUEN, 1, 1) + FIELD(CTRL, PRNGSRST, 0, 1) +REG32(CTRL_2, 0xc) + FIELD(CTRL_2, REPCOUNTTESTCUTOFF, 8, 9) + FIELD(CTRL_2, RESERVED_7_5, 5, 3) + FIELD(CTRL_2, DIT, 0, 5) +REG32(CTRL_3, 0x10) + FIELD(CTRL_3, ADAPTPROPTESTCUTOFF, 8, 10) + FIELD(CTRL_3, DLEN, 0, 8) +REG32(CTRL_4, 0x14) + FIELD(CTRL_4, SINGLEBITRAW, 0, 1) +REG32(EXT_SEED_0, 0x40) +REG32(EXT_SEED_1, 0x44) +REG32(EXT_SEED_2, 0x48) +REG32(EXT_SEED_3, 0x4c) +REG32(EXT_SEED_4, 0x50) +REG32(EXT_SEED_5, 0x54) +REG32(EXT_SEED_6, 0x58) +REG32(EXT_SEED_7, 0x5c) +REG32(EXT_SEED_8, 0x60) +REG32(EXT_SEED_9, 0x64) +REG32(EXT_SEED_10, 0x68) +REG32(EXT_SEED_11, 0x6c) +REG32(PER_STRNG_0, 0x80) +REG32(PER_STRNG_1, 0x84) +REG32(PER_STRNG_2, 0x88) +REG32(PER_STRNG_3, 0x8c) +REG32(PER_STRNG_4, 0x90) +REG32(PER_STRNG_5, 0x94) +REG32(PER_STRNG_6, 0x98) +REG32(PER_STRNG_7, 0x9c) +REG32(PER_STRNG_8, 0xa0) +REG32(PER_STRNG_9, 0xa4) +REG32(PER_STRNG_10, 0xa8) +REG32(PER_STRNG_11, 0xac) +REG32(CORE_OUTPUT, 0xc0) +REG32(RESET, 0xd0) + FIELD(RESET, VAL, 0, 1) +REG32(OSC_EN, 0xd4) + FIELD(OSC_EN, VAL, 0, 1) +REG32(TRNG_ISR, 0xe0) + FIELD(TRNG_ISR, SLVERR, 1, 1) + FIELD(TRNG_ISR, CORE_INT, 0, 1) +REG32(TRNG_IMR, 0xe4) + FIELD(TRNG_IMR, SLVERR, 1, 1) + FIELD(TRNG_IMR, CORE_INT, 0, 1) +REG32(TRNG_IER, 0xe8) + FIELD(TRNG_IER, SLVERR, 1, 1) + FIELD(TRNG_IER, CORE_INT, 0, 1) +REG32(TRNG_IDR, 0xec) + FIELD(TRNG_IDR, SLVERR, 1, 1) + FIELD(TRNG_IDR, CORE_INT, 0, 1) +REG32(SLV_ERR_CTRL, 0xf0) + FIELD(SLV_ERR_CTRL, ENABLE, 0, 1) + +#define R_MAX (R_SLV_ERR_CTRL + 1) + +QEMU_BUILD_BUG_ON(R_MAX * 4 != sizeof_field(XlnxVersalTRng, regs)); + +#define TRNG_GUEST_ERROR(D, FMT, ...) \ + do { \ + g_autofree char *p = object_get_canonical_path(OBJECT(D)); \ + qemu_log_mask(LOG_GUEST_ERROR, "%s: " FMT, p, ## __VA_ARGS__); \ + } while (0) + +#define TRNG_WARN(D, FMT, ...) \ + do { \ + g_autofree char *p = object_get_canonical_path(OBJECT(D)); \ + warn_report("%s: " FMT, p, ## __VA_ARGS__); \ + } while (0) + +static bool trng_older_than_v2(XlnxVersalTRng *s) +{ + return s->hw_version < 0x0200; +} + +static bool trng_in_reset(XlnxVersalTRng *s) +{ + if (ARRAY_FIELD_EX32(s->regs, RESET, VAL)) { + return true; + } + if (ARRAY_FIELD_EX32(s->regs, CTRL, PRNGSRST)) { + return true; + } + + return false; +} + +static bool trng_test_enabled(XlnxVersalTRng *s) +{ + return ARRAY_FIELD_EX32(s->regs, CTRL, TSTMODE); +} + +static bool trng_trss_enabled(XlnxVersalTRng *s) +{ + if (trng_in_reset(s)) { + return false; + } + if (!ARRAY_FIELD_EX32(s->regs, CTRL, TRSSEN)) { + return false; + } + if (!ARRAY_FIELD_EX32(s->regs, OSC_EN, VAL)) { + return false; + } + + return true; +} + +static void trng_seed_128(uint32_t *seed, uint64_t h00, uint64_t h64) +{ + seed[0] = extract64(h00, 0, 32); + seed[1] = extract64(h00, 32, 32); + seed[2] = extract64(h64, 0, 32); + seed[3] = extract64(h64, 32, 32); +} + +static void trng_reseed(XlnxVersalTRng *s) +{ + bool ext_seed = ARRAY_FIELD_EX32(s->regs, CTRL, PRNGXS); + bool pers_disabled = ARRAY_FIELD_EX32(s->regs, CTRL, PERSODISABLE); + + enum { + U384_U8 = 384 / 8, + U384_U32 = 384 / 32, + }; + + /* + * Maximum seed length is len(personalized string) + len(ext seed). + * + * g_rand_set_seed_array() takes array of uint32 in host endian. + */ + guint32 gs[U384_U32 * 2], *seed = &gs[U384_U32]; + + /* + * A disabled personalized string is the same as + * a string with all zeros. + * + * The device's hardware spec defines 3 modes (all selectable + * by guest at will and at anytime): + * 1) External seeding + * This is a PRNG mode, in which the produced sequence shall + * be reproducible if reseeded by the same 384-bit seed, as + * supplied by guest software. + * 2) Test seeding + * This is a PRNG mode, in which the produced sequence shall + * be reproducible if reseeded by a 128-bit test seed, as + * supplied by guest software. + * 3) Truly-random seeding + * This is the TRNG mode, in which the produced sequence is + * periodically reseeded by a crypto-strength entropy source. + * + * To assist debugging of certain classes of software defects, + * this QEMU model implements a 4th mode, + * 4) Forced PRNG + * When in this mode, a reproducible sequence is generated + * if software has selected the TRNG mode (mode 2). + * + * This emulation-only mode can only be selected by setting + * the uint64 property 'forced-prng' to a non-zero value. + * Guest software cannot select this mode. + */ + memset(gs, 0, sizeof(gs)); + + if (!pers_disabled) { + memcpy(gs, &s->regs[R_PER_STRNG_0], U384_U8); + } + + if (ext_seed) { + memcpy(seed, &s->regs[R_EXT_SEED_0], U384_U8); + } else if (trng_test_enabled(s)) { + trng_seed_128(seed, s->tst_seed[0], s->tst_seed[1]); + } else if (s->forced_prng_seed) { + s->forced_prng_count++; + trng_seed_128(seed, s->forced_prng_count, s->forced_prng_seed); + } else { + qemu_guest_getrandom_nofail(seed, U384_U8); + } + + g_rand_set_seed_array(s->prng, gs, ARRAY_SIZE(gs)); + + s->rand_count = 0; + s->rand_reseed = 1ULL << 48; +} + +static void trng_regen(XlnxVersalTRng *s) +{ + if (s->rand_reseed == 0) { + TRNG_GUEST_ERROR(s, "Too many generations without a reseed"); + trng_reseed(s); + } + s->rand_reseed--; + + /* + * In real hardware, each regen creates 256 bits, but QCNT + * reports a max of 4. + */ + ARRAY_FIELD_DP32(s->regs, STATUS, QCNT, 4); + s->rand_count = 256 / 32; +} + +static uint32_t trng_rdout(XlnxVersalTRng *s) +{ + assert(s->rand_count); + + s->rand_count--; + if (s->rand_count < 4) { + ARRAY_FIELD_DP32(s->regs, STATUS, QCNT, s->rand_count); + } + + return g_rand_int(s->prng); +} + +static void trng_irq_update(XlnxVersalTRng *s) +{ + bool pending = s->regs[R_TRNG_ISR] & ~s->regs[R_TRNG_IMR]; + qemu_set_irq(s->irq, pending); +} + +static void trng_isr_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque); + trng_irq_update(s); +} + +static uint64_t trng_ier_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque); + uint32_t val = val64; + + s->regs[R_TRNG_IMR] &= ~val; + trng_irq_update(s); + return 0; +} + +static uint64_t trng_idr_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque); + uint32_t val = val64; + + s->regs[R_TRNG_IMR] |= val; + trng_irq_update(s); + return 0; +} + +static void trng_core_int_update(XlnxVersalTRng *s) +{ + bool pending = false; + uint32_t st = s->regs[R_STATUS]; + uint32_t en = s->regs[R_INT_CTRL]; + + if (FIELD_EX32(st, STATUS, CERTF) && FIELD_EX32(en, INT_CTRL, CERTF_EN)) { + pending = true; + } + + if (FIELD_EX32(st, STATUS, DTF) && FIELD_EX32(en, INT_CTRL, DTF_EN)) { + pending = true; + } + + if (FIELD_EX32(st, STATUS, DONE) && FIELD_EX32(en, INT_CTRL, DONE_EN)) { + pending = true; + } + + ARRAY_FIELD_DP32(s->regs, TRNG_ISR, CORE_INT, pending); + trng_irq_update(s); +} + +static void trng_int_ctrl_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque); + uint32_t v32 = val64; + uint32_t clr_mask = 0; + + if (FIELD_EX32(v32, INT_CTRL, CERTF_RST)) { + clr_mask |= R_STATUS_CERTF_MASK; + } + if (FIELD_EX32(v32, INT_CTRL, DTF_RST)) { + clr_mask |= R_STATUS_DTF_MASK; + } + if (FIELD_EX32(v32, INT_CTRL, DONE_RST)) { + clr_mask |= R_STATUS_DONE_MASK; + } + + s->regs[R_STATUS] &= ~clr_mask; + trng_core_int_update(s); +} + +static void trng_done(XlnxVersalTRng *s) +{ + ARRAY_FIELD_DP32(s->regs, STATUS, DONE, true); + trng_core_int_update(s); +} + +static void trng_fault_event_set(XlnxVersalTRng *s, uint32_t events) +{ + bool pending = false; + + /* Disabled TRSS cannot generate any fault event */ + if (!trng_trss_enabled(s)) { + return; + } + + if (FIELD_EX32(events, STATUS, CERTF)) { + /* In older version, ERTU must be enabled explicitly to get CERTF */ + if (trng_older_than_v2(s) && + !ARRAY_FIELD_EX32(s->regs, CTRL, QERTUEN)) { + TRNG_WARN(s, "CERTF injection ignored: ERTU disabled"); + } else { + ARRAY_FIELD_DP32(s->regs, STATUS, CERTF, true); + pending = true; + } + } + + if (FIELD_EX32(events, STATUS, DTF)) { + ARRAY_FIELD_DP32(s->regs, STATUS, DTF, true); + pending = true; + } + + if (pending) { + trng_core_int_update(s); + } +} + +static void trng_soft_reset(XlnxVersalTRng *s) +{ + s->rand_count = 0; + s->regs[R_STATUS] = 0; + + ARRAY_FIELD_DP32(s->regs, TRNG_ISR, CORE_INT, 0); +} + +static void trng_ctrl_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque); + + if (trng_in_reset(s)) { + return; + } + + if (FIELD_EX32(val64, CTRL, PRNGSRST)) { + trng_soft_reset(s); + trng_irq_update(s); + return; + } + + if (!FIELD_EX32(val64, CTRL, PRNGSTART)) { + return; + } + + if (FIELD_EX32(val64, CTRL, PRNGMODE)) { + trng_regen(s); + } else { + trng_reseed(s); + } + + trng_done(s); +} + +static void trng_ctrl4_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque); + + /* Only applies to test mode with TRSS enabled */ + if (!trng_test_enabled(s) || !trng_trss_enabled(s)) { + return; + } + + /* Shift in a single bit. */ + s->tst_seed[1] <<= 1; + s->tst_seed[1] |= s->tst_seed[0] >> 63; + s->tst_seed[0] <<= 1; + s->tst_seed[0] |= val64 & 1; + + trng_reseed(s); + trng_regen(s); +} + +static uint64_t trng_core_out_postr(RegisterInfo *reg, uint64_t val) +{ + XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque); + bool oneshot = ARRAY_FIELD_EX32(s->regs, CTRL, SINGLEGENMODE); + bool start = ARRAY_FIELD_EX32(s->regs, CTRL, PRNGSTART); + uint32_t r = 0xbad; + + if (trng_in_reset(s)) { + TRNG_GUEST_ERROR(s, "Reading random number while in reset!"); + return r; + } + + if (s->rand_count == 0) { + TRNG_GUEST_ERROR(s, "Reading random number when unavailable!"); + return r; + } + + r = trng_rdout(s); + + /* Automatic mode regenerates when half the output reg is empty. */ + if (!oneshot && start && s->rand_count <= 3) { + trng_regen(s); + } + + return r; +} + +static void trng_reset(XlnxVersalTRng *s) +{ + unsigned int i; + + s->forced_prng_count = 0; + + for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) { + register_reset(&s->regs_info[i]); + } + trng_soft_reset(s); + trng_irq_update(s); +} + +static uint64_t trng_reset_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque); + + if (!ARRAY_FIELD_EX32(s->regs, RESET, VAL) && + FIELD_EX32(val64, RESET, VAL)) { + trng_reset(s); + } + + return val64; +} + +static uint64_t trng_register_read(void *opaque, hwaddr addr, unsigned size) +{ + /* + * Guest provided seed and personalized strings cannot be + * read back, and read attempts return value of A_STATUS. + */ + switch (addr) { + case A_EXT_SEED_0 ... A_PER_STRNG_11: + addr = A_STATUS; + break; + } + + return register_read_memory(opaque, addr, size); +} + +static void trng_register_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + RegisterInfoArray *reg_array = opaque; + XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg_array->r[0]->opaque); + + if (trng_older_than_v2(s)) { + switch (addr) { + case A_CTRL: + value = FIELD_DP64(value, CTRL, PERSODISABLE, 0); + value = FIELD_DP64(value, CTRL, SINGLEGENMODE, 0); + break; + case A_CTRL_2: + case A_CTRL_3: + case A_CTRL_4: + return; + } + } else { + switch (addr) { + case A_CTRL: + value = FIELD_DP64(value, CTRL, EATAU, 0); + value = FIELD_DP64(value, CTRL, QERTUEN, 0); + break; + } + } + + register_write_memory(opaque, addr, value, size); +} + +static RegisterAccessInfo trng_regs_info[] = { + { .name = "INT_CTRL", .addr = A_INT_CTRL, + .post_write = trng_int_ctrl_postw, + },{ .name = "STATUS", .addr = A_STATUS, + .ro = 0xfff, + },{ .name = "CTRL", .addr = A_CTRL, + .post_write = trng_ctrl_postw, + },{ .name = "CTRL_2", .addr = A_CTRL_2, + .reset = 0x210c, + },{ .name = "CTRL_3", .addr = A_CTRL_3, + .reset = 0x26f09, + },{ .name = "CTRL_4", .addr = A_CTRL_4, + .post_write = trng_ctrl4_postw, + },{ .name = "EXT_SEED_0", .addr = A_EXT_SEED_0, + },{ .name = "EXT_SEED_1", .addr = A_EXT_SEED_1, + },{ .name = "EXT_SEED_2", .addr = A_EXT_SEED_2, + },{ .name = "EXT_SEED_3", .addr = A_EXT_SEED_3, + },{ .name = "EXT_SEED_4", .addr = A_EXT_SEED_4, + },{ .name = "EXT_SEED_5", .addr = A_EXT_SEED_5, + },{ .name = "EXT_SEED_6", .addr = A_EXT_SEED_6, + },{ .name = "EXT_SEED_7", .addr = A_EXT_SEED_7, + },{ .name = "EXT_SEED_8", .addr = A_EXT_SEED_8, + },{ .name = "EXT_SEED_9", .addr = A_EXT_SEED_9, + },{ .name = "EXT_SEED_10", .addr = A_EXT_SEED_10, + },{ .name = "EXT_SEED_11", .addr = A_EXT_SEED_11, + },{ .name = "PER_STRNG_0", .addr = A_PER_STRNG_0, + },{ .name = "PER_STRNG_1", .addr = A_PER_STRNG_1, + },{ .name = "PER_STRNG_2", .addr = A_PER_STRNG_2, + },{ .name = "PER_STRNG_3", .addr = A_PER_STRNG_3, + },{ .name = "PER_STRNG_4", .addr = A_PER_STRNG_4, + },{ .name = "PER_STRNG_5", .addr = A_PER_STRNG_5, + },{ .name = "PER_STRNG_6", .addr = A_PER_STRNG_6, + },{ .name = "PER_STRNG_7", .addr = A_PER_STRNG_7, + },{ .name = "PER_STRNG_8", .addr = A_PER_STRNG_8, + },{ .name = "PER_STRNG_9", .addr = A_PER_STRNG_9, + },{ .name = "PER_STRNG_10", .addr = A_PER_STRNG_10, + },{ .name = "PER_STRNG_11", .addr = A_PER_STRNG_11, + },{ .name = "CORE_OUTPUT", .addr = A_CORE_OUTPUT, + .ro = 0xffffffff, + .post_read = trng_core_out_postr, + },{ .name = "RESET", .addr = A_RESET, + .reset = 0x1, + .pre_write = trng_reset_prew, + },{ .name = "OSC_EN", .addr = A_OSC_EN, + },{ .name = "TRNG_ISR", .addr = A_TRNG_ISR, + .w1c = 0x3, + .post_write = trng_isr_postw, + },{ .name = "TRNG_IMR", .addr = A_TRNG_IMR, + .reset = 0x3, + .ro = 0x3, + },{ .name = "TRNG_IER", .addr = A_TRNG_IER, + .pre_write = trng_ier_prew, + },{ .name = "TRNG_IDR", .addr = A_TRNG_IDR, + .pre_write = trng_idr_prew, + },{ .name = "SLV_ERR_CTRL", .addr = A_SLV_ERR_CTRL, + } +}; + +static const MemoryRegionOps trng_ops = { + .read = trng_register_read, + .write = trng_register_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void trng_init(Object *obj) +{ + XlnxVersalTRng *s = XLNX_VERSAL_TRNG(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + RegisterInfoArray *reg_array; + + reg_array = + register_init_block32(DEVICE(obj), trng_regs_info, + ARRAY_SIZE(trng_regs_info), + s->regs_info, s->regs, + &trng_ops, + XLNX_VERSAL_TRNG_ERR_DEBUG, + R_MAX * 4); + + sysbus_init_mmio(sbd, ®_array->mem); + sysbus_init_irq(sbd, &s->irq); + + s->prng = g_rand_new(); +} + +static void trng_unrealize(DeviceState *dev) +{ + XlnxVersalTRng *s = XLNX_VERSAL_TRNG(dev); + + g_rand_free(s->prng); + s->prng = NULL; +} + +static void trng_reset_hold(Object *obj) +{ + trng_reset(XLNX_VERSAL_TRNG(obj)); +} + +static void trng_prop_fault_event_set(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + Property *prop = opaque; + uint32_t *events = object_field_prop_ptr(obj, prop); + + visit_type_uint32(v, name, events, errp); + if (*errp) { + return; + } + + trng_fault_event_set(XLNX_VERSAL_TRNG(obj), *events); +} + +static const PropertyInfo trng_prop_fault_events = { + .name = "uint32:bits", + .description = "Set to trigger TRNG fault events", + .set = trng_prop_fault_event_set, + .realized_set_allowed = true, +}; + +static PropertyInfo trng_prop_uint64; /* to extend qdev_prop_uint64 */ + +static Property trng_props[] = { + DEFINE_PROP_UINT64("forced-prng", XlnxVersalTRng, forced_prng_seed, 0), + DEFINE_PROP_UINT32("hw-version", XlnxVersalTRng, hw_version, 0x0200), + DEFINE_PROP("fips-fault-events", XlnxVersalTRng, forced_faults, + trng_prop_fault_events, uint32_t), + + DEFINE_PROP_END_OF_LIST(), +}; + +static const VMStateDescription vmstate_trng = { + .name = TYPE_XLNX_VERSAL_TRNG, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(rand_count, XlnxVersalTRng), + VMSTATE_UINT64(rand_reseed, XlnxVersalTRng), + VMSTATE_UINT64(forced_prng_count, XlnxVersalTRng), + VMSTATE_UINT64_ARRAY(tst_seed, XlnxVersalTRng, 2), + VMSTATE_UINT32_ARRAY(regs, XlnxVersalTRng, R_MAX), + VMSTATE_END_OF_LIST(), + } +}; + +static void trng_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + dc->vmsd = &vmstate_trng; + dc->unrealize = trng_unrealize; + rc->phases.hold = trng_reset_hold; + + /* Clone uint64 property with set allowed after realized */ + trng_prop_uint64 = qdev_prop_uint64; + trng_prop_uint64.realized_set_allowed = true; + trng_props[0].info = &trng_prop_uint64; + + device_class_set_props(dc, trng_props); +} + +static const TypeInfo trng_info = { + .name = TYPE_XLNX_VERSAL_TRNG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XlnxVersalTRng), + .class_init = trng_class_init, + .instance_init = trng_init, +}; + +static void trng_register_types(void) +{ + type_register_static(&trng_info); +} + +type_init(trng_register_types) diff --git a/include/hw/misc/xlnx-versal-trng.h b/include/hw/misc/xlnx-versal-trng.h new file mode 100644 index 0000000000..0bcef8a613 --- /dev/null +++ b/include/hw/misc/xlnx-versal-trng.h @@ -0,0 +1,58 @@ +/* + * Non-crypto strength model of the True Random Number Generator + * in the AMD/Xilinx Versal device family. + * + * Copyright (c) 2017-2020 Xilinx Inc. + * Copyright (c) 2023 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef XLNX_VERSAL_TRNG_H +#define XLNX_VERSAL_TRNG_H + +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "hw/register.h" + +#define TYPE_XLNX_VERSAL_TRNG "xlnx.versal-trng" +OBJECT_DECLARE_SIMPLE_TYPE(XlnxVersalTRng, XLNX_VERSAL_TRNG); + +#define RMAX_XLNX_VERSAL_TRNG ((0xf0 / 4) + 1) + +typedef struct XlnxVersalTRng { + SysBusDevice parent_obj; + qemu_irq irq; + GRand *prng; + + uint32_t hw_version; + uint32_t forced_faults; + + uint32_t rand_count; + uint64_t rand_reseed; + + uint64_t forced_prng_seed; + uint64_t forced_prng_count; + uint64_t tst_seed[2]; + + uint32_t regs[RMAX_XLNX_VERSAL_TRNG]; + RegisterInfo regs_info[RMAX_XLNX_VERSAL_TRNG]; +} XlnxVersalTRng; + +#undef RMAX_XLNX_VERSAL_TRNG +#endif From 3b22376ba48fff06039c90f56372610448fe60e8 Mon Sep 17 00:00:00 2001 From: Tong Ho Date: Tue, 31 Oct 2023 11:46:10 -0700 Subject: [PATCH 380/974] hw/arm: xlnx-versal-virt: Add AMD/Xilinx TRNG device Connect the support for Versal True Random Number Generator (TRNG) device. Warning: unlike the TRNG component in a real device from the Versal device familiy, the connected TRNG model is not of cryptographic grade and is not intended for use cases when cryptograpically strong TRNG is needed. Signed-off-by: Tong Ho Reviewed-by: Francisco Iglesias Reviewed-by: Peter Maydell Message-id: 20231031184611.3029156-3-tong.ho@amd.com Signed-off-by: Peter Maydell --- hw/arm/Kconfig | 1 + hw/arm/xlnx-versal.c | 16 ++++++++++++++++ include/hw/arm/xlnx-versal.h | 5 +++++ 3 files changed, 22 insertions(+) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 841f3131ea..e35007ed41 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -482,6 +482,7 @@ config XLNX_VERSAL select XLNX_BBRAM select XLNX_EFUSE_VERSAL select XLNX_USB_SUBSYS + select XLNX_VERSAL_TRNG config NPCM7XX bool diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index fa556d8764..4f74a64a0d 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -373,6 +373,21 @@ static void versal_create_rtc(Versal *s, qemu_irq *pic) qdev_get_gpio_in(DEVICE(&s->pmc.apb_irq_orgate), 0)); } +static void versal_create_trng(Versal *s, qemu_irq *pic) +{ + SysBusDevice *sbd; + MemoryRegion *mr; + + object_initialize_child(OBJECT(s), "trng", &s->pmc.trng, + TYPE_XLNX_VERSAL_TRNG); + sbd = SYS_BUS_DEVICE(&s->pmc.trng); + sysbus_realize(sbd, &error_fatal); + + mr = sysbus_mmio_get_region(sbd, 0); + memory_region_add_subregion(&s->mr_ps, MM_PMC_TRNG, mr); + sysbus_connect_irq(sbd, 0, pic[VERSAL_TRNG_IRQ]); +} + static void versal_create_xrams(Versal *s, qemu_irq *pic) { int nr_xrams = ARRAY_SIZE(s->lpd.xram.ctrl); @@ -909,6 +924,7 @@ static void versal_realize(DeviceState *dev, Error **errp) versal_create_sds(s, pic); versal_create_pmc_apb_irq_orgate(s, pic); versal_create_rtc(s, pic); + versal_create_trng(s, pic); versal_create_xrams(s, pic); versal_create_bbram(s, pic); versal_create_efuse(s, pic); diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index b710d71fb0..b24fa64557 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -30,6 +30,7 @@ #include "hw/dma/xlnx_csu_dma.h" #include "hw/misc/xlnx-versal-crl.h" #include "hw/misc/xlnx-versal-pmc-iou-slcr.h" +#include "hw/misc/xlnx-versal-trng.h" #include "hw/net/xlnx-versal-canfd.h" #include "hw/misc/xlnx-versal-cfu.h" #include "hw/misc/xlnx-versal-cframe-reg.h" @@ -115,6 +116,7 @@ struct Versal { } iou; XlnxZynqMPRTC rtc; + XlnxVersalTRng trng; XlnxBBRam bbram; XlnxEFuse efuse; XlnxVersalEFuseCtrl efuse_ctrl; @@ -159,6 +161,7 @@ struct Versal { #define VERSAL_OSPI_IRQ 124 #define VERSAL_SD0_IRQ_0 126 #define VERSAL_EFUSE_IRQ 139 +#define VERSAL_TRNG_IRQ 141 #define VERSAL_RTC_ALARM_IRQ 142 #define VERSAL_RTC_SECONDS_IRQ 143 @@ -328,4 +331,6 @@ struct Versal { #define MM_PMC_CRP_SIZE 0x10000 #define MM_PMC_RTC 0xf12a0000 #define MM_PMC_RTC_SIZE 0x10000 +#define MM_PMC_TRNG 0xf1230000 +#define MM_PMC_TRNG_SIZE 0x10000 #endif From 1c98a821a2b3620c516f3da0d74719ed6f33bced Mon Sep 17 00:00:00 2001 From: Tong Ho Date: Tue, 31 Oct 2023 11:46:11 -0700 Subject: [PATCH 381/974] tests/qtest: Introduce tests for AMD/Xilinx Versal TRNG device Signed-off-by: Tong Ho Message-id: 20231031184611.3029156-4-tong.ho@amd.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- tests/qtest/meson.build | 2 +- tests/qtest/xlnx-versal-trng-test.c | 485 ++++++++++++++++++++++++++++ 2 files changed, 486 insertions(+), 1 deletion(-) create mode 100644 tests/qtest/xlnx-versal-trng-test.c diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index d6022ebd64..c9945e69b1 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -216,7 +216,7 @@ qtests_aarch64 = \ (config_all.has_key('CONFIG_TCG') and config_all_devices.has_key('CONFIG_TPM_TIS_SYSBUS') ? \ ['tpm-tis-device-test', 'tpm-tis-device-swtpm-test'] : []) + \ (config_all_devices.has_key('CONFIG_XLNX_ZYNQMP_ARM') ? ['xlnx-can-test', 'fuzz-xlnx-dp-test'] : []) + \ - (config_all_devices.has_key('CONFIG_XLNX_VERSAL') ? ['xlnx-canfd-test'] : []) + \ + (config_all_devices.has_key('CONFIG_XLNX_VERSAL') ? ['xlnx-canfd-test', 'xlnx-versal-trng-test'] : []) + \ (config_all_devices.has_key('CONFIG_RASPI') ? ['bcm2835-dma-test'] : []) + \ (config_all.has_key('CONFIG_TCG') and \ config_all_devices.has_key('CONFIG_TPM_TIS_I2C') ? ['tpm-tis-i2c-test'] : []) + \ diff --git a/tests/qtest/xlnx-versal-trng-test.c b/tests/qtest/xlnx-versal-trng-test.c new file mode 100644 index 0000000000..cef4e575bb --- /dev/null +++ b/tests/qtest/xlnx-versal-trng-test.c @@ -0,0 +1,485 @@ +/* + * QTests for the Xilinx Versal True Random Number Generator device + * + * Copyright (c) 2023 Advanced Micro Devices, Inc. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" + +/* Base Address */ +#define TRNG_BASEADDR (0xf1230000) + +/* TRNG_INT_CTRL */ +#define R_TRNG_INT_CTRL (0x0000) +#define TRNG_INT_CTRL_CERTF_RST_MASK (1 << 5) +#define TRNG_INT_CTRL_DTF_RST_MASK (1 << 4) +#define TRNG_INT_CTRL_DONE_RST_MASK (1 << 3) +#define TRNG_INT_CTRL_CERTF_EN_MASK (1 << 2) +#define TRNG_INT_CTRL_DTF_EN_MASK (1 << 1) +#define TRNG_INT_CTRL_DONE_EN_MASK (1) + +/* TRNG_STATUS */ +#define R_TRNG_STATUS (0x0004) +#define TRNG_STATUS_QCNT_SHIFT (9) +#define TRNG_STATUS_QCNT_MASK (7 << TRNG_STATUS_QCNT_SHIFT) +#define TRNG_STATUS_CERTF_MASK (1 << 3) +#define TRNG_STATUS_DTF_MASK (1 << 1) +#define TRNG_STATUS_DONE_MASK (1) + +/* TRNG_CTRL */ +#define R_TRNG_CTRL (0x0008) +#define TRNG_CTRL_PERSODISABLE_MASK (1 << 10) +#define TRNG_CTRL_SINGLEGENMODE_MASK (1 << 9) +#define TRNG_CTRL_PRNGMODE_MASK (1 << 7) +#define TRNG_CTRL_TSTMODE_MASK (1 << 6) +#define TRNG_CTRL_PRNGSTART_MASK (1 << 5) +#define TRNG_CTRL_PRNGXS_MASK (1 << 3) +#define TRNG_CTRL_TRSSEN_MASK (1 << 2) +#define TRNG_CTRL_QERTUEN_MASK (1 << 1) +#define TRNG_CTRL_PRNGSRST_MASK (1) + +/* TRNG_EXT_SEED_0 ... _11 */ +#define R_TRNG_EXT_SEED_0 (0x0040) +#define R_TRNG_EXT_SEED_11 (R_TRNG_EXT_SEED_0 + 4 * 11) + +/* TRNG_PER_STRNG_0 ... 11 */ +#define R_TRNG_PER_STRNG_0 (0x0080) +#define R_TRNG_PER_STRNG_11 (R_TRNG_PER_STRNG_0 + 4 * 11) + +/* TRNG_CORE_OUTPUT */ +#define R_TRNG_CORE_OUTPUT (0x00c0) + +/* TRNG_RESET */ +#define R_TRNG_RESET (0x00d0) +#define TRNG_RESET_VAL_MASK (1) + +/* TRNG_OSC_EN */ +#define R_TRNG_OSC_EN (0x00d4) +#define TRNG_OSC_EN_VAL_MASK (1) + +/* TRNG_TRNG_ISR, _IMR, _IER, _IDR */ +#define R_TRNG_ISR (0x00e0) +#define R_TRNG_IMR (0x00e4) +#define R_TRNG_IER (0x00e8) +#define R_TRNG_IDR (0x00ec) +#define TRNG_IRQ_SLVERR_MASK (1 << 1) +#define TRNG_IRQ_CORE_INT_MASK (1) + +/* + * End test with a formatted error message, by embedding the message + * in a GError. + */ +#define TRNG_FAILED(FMT, ...) \ + do { \ + g_autoptr(GError) err = g_error_new( \ + g_quark_from_static_string(trng_qname), 0, \ + FMT, ## __VA_ARGS__); \ + g_assert_no_error(err); \ + } while (0) + +static const gchar trng_qname[] = "xlnx-versal-trng-test"; + +static const uint32_t prng_seed[12] = { + 0x01234567, 0x12345678, 0x23456789, 0x3456789a, 0x456789ab, 0x56789abc, + 0x76543210, 0x87654321, 0x98765432, 0xa9876543, 0xba987654, 0xfedcba98, +}; + +static const uint32_t pers_str[12] = { + 0x76543210, 0x87654321, 0x98765432, 0xa9876543, 0xba987654, 0xfedcba98, + 0x01234567, 0x12345678, 0x23456789, 0x3456789a, 0x456789ab, 0x56789abc, +}; + +static void trng_test_start(void) +{ + qtest_start("-machine xlnx-versal-virt"); +} + +static void trng_test_stop(void) +{ + qtest_end(); +} + +static void trng_test_set_uint_prop(const char *name, uint64_t value) +{ + const char *path = "/machine/xlnx-versal/trng"; + QDict *response; + + response = qmp("{ 'execute': 'qom-set'," + " 'arguments': {" + " 'path': %s," + " 'property': %s," + " 'value': %llu" + "} }", path, + name, (unsigned long long)value); + g_assert(qdict_haskey(response, "return")); + qobject_unref(response); +} + +static void trng_write(unsigned ra, uint32_t val) +{ + writel(TRNG_BASEADDR + ra, val); +} + +static uint32_t trng_read(unsigned ra) +{ + return readl(TRNG_BASEADDR + ra); +} + +static void trng_bit_set(unsigned ra, uint32_t bits) +{ + trng_write(ra, (trng_read(ra) | bits)); +} + +static void trng_bit_clr(unsigned ra, uint32_t bits) +{ + trng_write(ra, (trng_read(ra) & ~bits)); +} + +static void trng_ctrl_set(uint32_t bits) +{ + trng_bit_set(R_TRNG_CTRL, bits); +} + +static void trng_ctrl_clr(uint32_t bits) +{ + trng_bit_clr(R_TRNG_CTRL, bits); +} + +static uint32_t trng_status(void) +{ + return trng_read(R_TRNG_STATUS); +} + +static unsigned trng_qcnt(void) +{ + uint32_t sta = trng_status(); + + return (sta & TRNG_STATUS_QCNT_MASK) >> TRNG_STATUS_QCNT_SHIFT; +} + +static const char *trng_info(void) +{ + uint32_t sta = trng_status(); + uint32_t ctl = trng_read(R_TRNG_CTRL); + + static char info[64]; + + snprintf(info, sizeof(info), "; status=0x%x, ctrl=0x%x", sta, ctl); + return info; +} + +static void trng_check_status(uint32_t status_mask, const char *act) +{ + uint32_t clear_mask = 0; + uint32_t status; + + /* + * Only selected bits are events in R_TRNG_STATUS, and + * clear them needs to go through R_INT_CTRL. + */ + if (status_mask & TRNG_STATUS_CERTF_MASK) { + clear_mask |= TRNG_INT_CTRL_CERTF_RST_MASK; + } + if (status_mask & TRNG_STATUS_DTF_MASK) { + clear_mask |= TRNG_INT_CTRL_DTF_RST_MASK; + } + if (status_mask & TRNG_STATUS_DONE_MASK) { + clear_mask |= TRNG_INT_CTRL_DONE_RST_MASK; + } + + status = trng_status(); + if ((status & status_mask) != status_mask) { + TRNG_FAILED("%s: Status bitmask 0x%x failed to be 1%s", + act, status_mask, trng_info()); + } + + /* Remove event */ + trng_bit_set(R_TRNG_INT_CTRL, clear_mask); + + if (!!(trng_read(R_TRNG_STATUS) & status_mask)) { + TRNG_FAILED("%s: Event 0x%0x stuck at 1 after clear: %s", + act, status_mask, trng_info()); + } +} + +static void trng_check_done_status(const char *act) +{ + trng_check_status(TRNG_STATUS_DONE_MASK, act); +} + +static void trng_check_dtf_status(void) +{ + trng_check_status(TRNG_STATUS_DTF_MASK, "DTF injection"); +} + +static void trng_check_certf_status(void) +{ + trng_check_status(TRNG_STATUS_CERTF_MASK, "CERTF injection"); +} + +static void trng_reset(void) +{ + trng_write(R_TRNG_RESET, TRNG_RESET_VAL_MASK); + trng_write(R_TRNG_RESET, 0); +} + +static void trng_load(unsigned r0, const uint32_t *b384) +{ + static const uint32_t zero[12] = { 0 }; + unsigned k; + + if (!b384) { + b384 = zero; + } + + for (k = 0; k < 12; k++) { + trng_write(r0 + 4 * k, b384[k]); + } +} + +static void trng_reseed(const uint32_t *seed) +{ + const char *act; + uint32_t ctl; + + ctl = TRNG_CTRL_PRNGSTART_MASK | + TRNG_CTRL_PRNGXS_MASK | + TRNG_CTRL_TRSSEN_MASK; + + trng_ctrl_clr(ctl | TRNG_CTRL_PRNGMODE_MASK); + + if (seed) { + trng_load(R_TRNG_EXT_SEED_0, seed); + act = "Reseed PRNG"; + ctl &= ~TRNG_CTRL_TRSSEN_MASK; + } else { + trng_write(R_TRNG_OSC_EN, TRNG_OSC_EN_VAL_MASK); + act = "Reseed TRNG"; + ctl &= ~TRNG_CTRL_PRNGXS_MASK; + } + + trng_ctrl_set(ctl); + trng_check_done_status(act); + trng_ctrl_clr(TRNG_CTRL_PRNGSTART_MASK); +} + +static void trng_generate(bool auto_enb) +{ + uint32_t ctl; + + ctl = TRNG_CTRL_PRNGSTART_MASK | TRNG_CTRL_SINGLEGENMODE_MASK; + trng_ctrl_clr(ctl); + + if (auto_enb) { + ctl &= ~TRNG_CTRL_SINGLEGENMODE_MASK; + } + + trng_ctrl_set(ctl | TRNG_CTRL_PRNGMODE_MASK); + + trng_check_done_status("Generate"); + g_assert(trng_qcnt() != 7); +} + +static size_t trng_collect(uint32_t *rnd, size_t cnt) +{ + size_t i; + + for (i = 0; i < cnt; i++) { + if (trng_qcnt() == 0) { + return i; + } + + rnd[i] = trng_read(R_TRNG_CORE_OUTPUT); + } + + return i; +} + +static void trng_test_autogen(void) +{ + const size_t cnt = 512 / 32; + uint32_t rng[cnt], prng[cnt]; + size_t n; + + trng_reset(); + + /* PRNG run #1 */ + trng_reseed(prng_seed); + trng_generate(true); + + n = trng_collect(prng, cnt); + if (n != cnt) { + TRNG_FAILED("PRNG_1 Auto-gen test failed: expected = %u, got = %u", + (unsigned)cnt, (unsigned)n); + } + + /* TRNG, should not match PRNG */ + trng_reseed(NULL); + trng_generate(true); + + n = trng_collect(rng, cnt); + if (n != cnt) { + TRNG_FAILED("TRNG Auto-gen test failed: expected = %u, got = %u", + (unsigned)cnt, (unsigned)n); + } + + /* PRNG #2: should matches run #1 */ + trng_reseed(prng_seed); + trng_generate(true); + + n = trng_collect(rng, cnt); + if (n != cnt) { + TRNG_FAILED("PRNG_2 Auto-gen test failed: expected = %u, got = %u", + (unsigned)cnt, (unsigned)n); + } + + if (memcmp(rng, prng, sizeof(rng))) { + TRNG_FAILED("PRNG_2 Auto-gen test failed: does not match PRNG_1"); + } +} + +static void trng_test_oneshot(void) +{ + const size_t cnt = 512 / 32; + uint32_t rng[cnt]; + size_t n; + + trng_reset(); + + /* PRNG run #1 */ + trng_reseed(prng_seed); + trng_generate(false); + + n = trng_collect(rng, cnt); + if (n == cnt) { + TRNG_FAILED("PRNG_1 One-shot gen test failed"); + } + + /* TRNG, should not match PRNG */ + trng_reseed(NULL); + trng_generate(false); + + n = trng_collect(rng, cnt); + if (n == cnt) { + TRNG_FAILED("TRNG One-shot test failed"); + } +} + +static void trng_test_per_str(void) +{ + const size_t cnt = 512 / 32; + uint32_t rng[cnt], prng[cnt]; + size_t n; + + trng_reset(); + + /* #1: disabled */ + trng_ctrl_set(TRNG_CTRL_PERSODISABLE_MASK); + trng_reseed(prng_seed); + trng_ctrl_clr(TRNG_CTRL_PERSODISABLE_MASK); + + trng_generate(true); + n = trng_collect(prng, cnt); + g_assert_cmpuint(n, ==, cnt); + + /* #2: zero string should match personalization disabled */ + trng_load(R_TRNG_PER_STRNG_0, NULL); + trng_reseed(prng_seed); + + trng_generate(true); + n = trng_collect(rng, cnt); + g_assert_cmpuint(n, ==, cnt); + + if (memcmp(rng, prng, sizeof(rng))) { + TRNG_FAILED("Failed: PER_DISABLE != PER_STRNG_ALL_ZERO"); + } + + /* #3: non-zero string should not match personalization disabled */ + trng_load(R_TRNG_PER_STRNG_0, pers_str); + trng_reseed(prng_seed); + + trng_generate(true); + n = trng_collect(rng, cnt); + g_assert_cmpuint(n, ==, cnt); + + if (!memcmp(rng, prng, sizeof(rng))) { + TRNG_FAILED("Failed: PER_DISABLE == PER_STRNG_NON_ZERO"); + } +} + +static void trng_test_forced_prng(void) +{ + const char *prop = "forced-prng"; + const uint64_t seed = 0xdeadbeefbad1bad0ULL; + + const size_t cnt = 512 / 32; + uint32_t rng[cnt], prng[cnt]; + size_t n; + + trng_reset(); + trng_test_set_uint_prop(prop, seed); + + /* TRNG run #1 */ + trng_reset(); + trng_reseed(NULL); + trng_generate(true); + + n = trng_collect(prng, cnt); + g_assert_cmpuint(n, ==, cnt); + + /* TRNG run #2 should match run #1 */ + trng_reset(); + trng_reseed(NULL); + trng_generate(true); + + n = trng_collect(rng, cnt); + g_assert_cmpuint(n, ==, cnt); + + if (memcmp(rng, prng, sizeof(rng))) { + TRNG_FAILED("Forced-prng test failed: results do not match"); + } +} + +static void trng_test_fault_events(void) +{ + const char *prop = "fips-fault-events"; + + trng_reset(); + + /* Fault events only when TRSS is enabled */ + trng_write(R_TRNG_OSC_EN, TRNG_OSC_EN_VAL_MASK); + trng_ctrl_set(TRNG_CTRL_TRSSEN_MASK); + + trng_test_set_uint_prop(prop, TRNG_STATUS_CERTF_MASK); + trng_check_certf_status(); + + trng_test_set_uint_prop(prop, TRNG_STATUS_DTF_MASK); + trng_check_dtf_status(); + + trng_reset(); +} + +int main(int argc, char **argv) +{ + int rc; + + g_test_init(&argc, &argv, NULL); + + #define TRNG_TEST_ADD(n) \ + qtest_add_func("/hw/misc/xlnx-versal-trng/" #n, trng_test_ ## n); + TRNG_TEST_ADD(autogen); + TRNG_TEST_ADD(oneshot); + TRNG_TEST_ADD(per_str); + TRNG_TEST_ADD(forced_prng); + TRNG_TEST_ADD(fault_events); + #undef TRNG_TEST_ADD + + trng_test_start(); + rc = g_test_run(); + trng_test_stop(); + + return rc; +} From d6f077321ae3f9508d3e066455ae1278ab3213e5 Mon Sep 17 00:00:00 2001 From: Song Gao Date: Fri, 20 Oct 2023 16:49:23 +0800 Subject: [PATCH 382/974] target/loongarch: Add cpu model 'max' We use cpu la464 for the 'max' cpu. Signed-off-by: Song Gao Reviewed-by: Richard Henderson Message-Id: <20231020084925.3457084-2-gaosong@loongson.cn> --- target/loongarch/cpu.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index ef1bf89dac..ef6922e812 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -474,6 +474,12 @@ static void loongarch_la132_initfn(Object *obj) env->cpucfg[1] = data; } +static void loongarch_max_initfn(Object *obj) +{ + /* '-cpu max' for TCG: we use cpu la464. */ + loongarch_la464_initfn(obj); +} + static void loongarch_cpu_list_entry(gpointer data, gpointer user_data) { const char *typename = object_class_get_name(OBJECT_CLASS(data)); @@ -829,6 +835,7 @@ static const TypeInfo loongarch_cpu_type_infos[] = { }, DEFINE_LOONGARCH_CPU_TYPE(64, "la464", loongarch_la464_initfn), DEFINE_LOONGARCH_CPU_TYPE(32, "la132", loongarch_la132_initfn), + DEFINE_LOONGARCH_CPU_TYPE(64, "max", loongarch_max_initfn), }; DEFINE_TYPES(loongarch_cpu_type_infos) From 464136ceb65ddebbd60fecd486628cc82fd722ab Mon Sep 17 00:00:00 2001 From: Song Gao Date: Fri, 20 Oct 2023 16:49:24 +0800 Subject: [PATCH 383/974] target/loongarch: Allow user enable/disable LSX/LASX features Some users may not need LSX/LASX, this patch allows the user enable/disable LSX/LASX features. e.g '-cpu max,lsx=on,lasx=on' (default); '-cpu max,lsx=on,lasx=off' (enabled LSX); '-cpu max,lsx=off,lasx=on' (enabled LASX, LSX); '-cpu max,lsx=off' (disable LSX and LASX). Signed-off-by: Song Gao Reviewed-by: Richard Henderson Message-Id: <20231020084925.3457084-3-gaosong@loongson.cn> --- target/loongarch/cpu.c | 67 ++++++++++++++++++++++++++++++++++++++++++ target/loongarch/cpu.h | 2 ++ 2 files changed, 69 insertions(+) diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index ef6922e812..a60d07acd5 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -443,6 +443,7 @@ static void loongarch_la464_initfn(Object *obj) env->cpucfg[20] = data; env->CSR_ASID = FIELD_DP64(0, CSR_ASID, ASIDBITS, 0xa); + loongarch_cpu_post_init(obj); } static void loongarch_la132_initfn(Object *obj) @@ -622,6 +623,72 @@ static const MemoryRegionOps loongarch_qemu_ops = { }; #endif +static bool loongarch_get_lsx(Object *obj, Error **errp) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + bool ret; + + if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LSX)) { + ret = true; + } else { + ret = false; + } + return ret; +} + +static void loongarch_set_lsx(Object *obj, bool value, Error **errp) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + + if (value) { + cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, LSX, 1); + } else { + cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, LSX, 0); + cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, LASX, 0); + } +} + +static bool loongarch_get_lasx(Object *obj, Error **errp) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + bool ret; + + if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LASX)) { + ret = true; + } else { + ret = false; + } + return ret; +} + +static void loongarch_set_lasx(Object *obj, bool value, Error **errp) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + + if (value) { + if (!FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LSX)) { + cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, LSX, 1); + } + cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, LASX, 1); + } else { + cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, LASX, 0); + } +} + +void loongarch_cpu_post_init(Object *obj) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + + if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LSX)) { + object_property_add_bool(obj, "lsx", loongarch_get_lsx, + loongarch_set_lsx); + } + if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LASX)) { + object_property_add_bool(obj, "lasx", loongarch_get_lasx, + loongarch_set_lasx); + } +} + static void loongarch_cpu_init(Object *obj) { #ifndef CONFIG_USER_ONLY diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 8b54cf109c..9d0f79f814 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -486,4 +486,6 @@ void loongarch_cpu_list(void); #define LOONGARCH_CPU_TYPE_NAME(model) model LOONGARCH_CPU_TYPE_SUFFIX #define CPU_RESOLVING_TYPE TYPE_LOONGARCH_CPU +void loongarch_cpu_post_init(Object *obj); + #endif /* LOONGARCH_CPU_H */ From 31f694b91188d2077c5d85a5064b54b127635718 Mon Sep 17 00:00:00 2001 From: Song Gao Date: Fri, 20 Oct 2023 16:49:25 +0800 Subject: [PATCH 384/974] target/loongarch: Implement query-cpu-model-expansion Add support for the query-cpu-model-expansion QMP command to LoongArch. We support query the cpu features. e.g la464 and max cpu support LSX/LASX, default enable, la132 not support LSX/LASX. 1. start with '-cpu max,lasx=off' (QEMU) query-cpu-model-expansion type=static model={"name":"max"} {"return": {"model": {"name": "max", "props": {"lasx": false, "lsx": true}}}} 2. start with '-cpu la464,lasx=off' (QEMU) query-cpu-model-expansion type=static model={"name":"la464"} {"return": {"model": {"name": "max", "props": {"lasx": false, "lsx": true}}} 3. start with '-cpu la132,lasx=off' qemu-system-loongarch64: can't apply global la132-loongarch-cpu.lasx=off: Property 'la132-loongarch-cpu.lasx' not found 4. start with '-cpu max,lasx=off' or start with '-cpu la464,lasx=off' query cpu model la132 (QEMU) query-cpu-model-expansion type=static model={"name":"la132"} {"return": {"model": {"name": "la132"}}} Acked-by: Markus Armbruster Signed-off-by: Song Gao Reviewed-by: Richard Henderson Message-Id: <20231020084925.3457084-4-gaosong@loongson.cn> --- qapi/machine-target.json | 6 ++- target/loongarch/loongarch-qmp-cmds.c | 64 +++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/qapi/machine-target.json b/qapi/machine-target.json index 4e55adbe00..c8d7d9868d 100644 --- a/qapi/machine-target.json +++ b/qapi/machine-target.json @@ -230,7 +230,8 @@ 'data': { 'model': 'CpuModelInfo' }, 'if': { 'any': [ 'TARGET_S390X', 'TARGET_I386', - 'TARGET_ARM' ] } } + 'TARGET_ARM', + 'TARGET_LOONGARCH64' ] } } ## # @query-cpu-model-expansion: @@ -275,7 +276,8 @@ 'returns': 'CpuModelExpansionInfo', 'if': { 'any': [ 'TARGET_S390X', 'TARGET_I386', - 'TARGET_ARM' ] } } + 'TARGET_ARM', + 'TARGET_LOONGARCH64' ] } } ## # @CpuDefinitionInfo: diff --git a/target/loongarch/loongarch-qmp-cmds.c b/target/loongarch/loongarch-qmp-cmds.c index 6c25957881..645672ff59 100644 --- a/target/loongarch/loongarch-qmp-cmds.c +++ b/target/loongarch/loongarch-qmp-cmds.c @@ -7,8 +7,13 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "qapi/qapi-commands-machine-target.h" #include "cpu.h" +#include "qapi/qmp/qerror.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qobject-input-visitor.h" +#include "qom/qom-qobject.h" static void loongarch_cpu_add_definition(gpointer data, gpointer user_data) { @@ -35,3 +40,62 @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) return cpu_list; } + +static const char *cpu_model_advertised_features[] = { + "lsx", "lasx", NULL +}; + +CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, + CpuModelInfo *model, + Error **errp) +{ + CpuModelExpansionInfo *expansion_info; + QDict *qdict_out; + ObjectClass *oc; + Object *obj; + const char *name; + int i; + + if (type != CPU_MODEL_EXPANSION_TYPE_STATIC) { + error_setg(errp, "The requested expansion type is not supported"); + return NULL; + } + + oc = cpu_class_by_name(TYPE_LOONGARCH_CPU, model->name); + if (!oc) { + error_setg(errp, "The CPU type '%s' is not a recognized LoongArch CPU type", + model->name); + return NULL; + } + + obj = object_new(object_class_get_name(oc)); + + expansion_info = g_new0(CpuModelExpansionInfo, 1); + expansion_info->model = g_malloc0(sizeof(*expansion_info->model)); + expansion_info->model->name = g_strdup(model->name); + + qdict_out = qdict_new(); + + i = 0; + while ((name = cpu_model_advertised_features[i++]) != NULL) { + ObjectProperty *prop = object_property_find(obj, name); + if (prop) { + QObject *value; + + assert(prop->get); + value = object_property_get_qobject(obj, name, &error_abort); + + qdict_put_obj(qdict_out, name, value); + } + } + + if (!qdict_size(qdict_out)) { + qobject_unref(qdict_out); + } else { + expansion_info->model->props = QOBJECT(qdict_out); + } + + object_unref(obj); + + return expansion_info; +} From 1d832c19db1e895b7d8015c3b33fc6311e20a69c Mon Sep 17 00:00:00 2001 From: Song Gao Date: Mon, 23 Oct 2023 10:40:59 +0800 Subject: [PATCH 385/974] target/loongarch: Support 4K page size The LoongArch kernel supports 4K page size. Change TARGET_PAGE_BITS to 12. Signed-off-by: Song Gao Message-Id: <20231023024059.3858349-1-gaosong@loongson.cn> --- target/loongarch/cpu-param.h | 2 +- target/loongarch/tlb_helper.c | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/target/loongarch/cpu-param.h b/target/loongarch/cpu-param.h index 1265dc7cb5..cfe195db4e 100644 --- a/target/loongarch/cpu-param.h +++ b/target/loongarch/cpu-param.h @@ -12,6 +12,6 @@ #define TARGET_PHYS_ADDR_SPACE_BITS 48 #define TARGET_VIRT_ADDR_SPACE_BITS 48 -#define TARGET_PAGE_BITS 14 +#define TARGET_PAGE_BITS 12 #endif diff --git a/target/loongarch/tlb_helper.c b/target/loongarch/tlb_helper.c index c8b8b0497f..449043c68b 100644 --- a/target/loongarch/tlb_helper.c +++ b/target/loongarch/tlb_helper.c @@ -60,6 +60,9 @@ static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, tlb_rplv = 0; } + /* Remove sw bit between bit12 -- bit PS*/ + tlb_ppn = tlb_ppn & ~(((0x1UL << (tlb_ps - 12)) -1)); + /* Check access rights */ if (!tlb_v) { return TLBRET_INVALID; @@ -82,10 +85,6 @@ static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, return TLBRET_DIRTY; } - /* - * tlb_entry contains ppn[47:12] while 16KiB ppn is [47:15] - * need adjust. - */ *physical = (tlb_ppn << R_TLBENTRY_64_PPN_SHIFT) | (address & MAKE_64BIT_MASK(0, tlb_ps)); *prot = PAGE_READ; @@ -774,7 +773,7 @@ void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd, /* Move Global bit */ tmp0 = ((tmp0 & (1 << LOONGARCH_HGLOBAL_SHIFT)) >> LOONGARCH_HGLOBAL_SHIFT) << R_TLBENTRY_G_SHIFT | - (tmp0 & (~(1 << R_TLBENTRY_G_SHIFT))); + (tmp0 & (~(1 << LOONGARCH_HGLOBAL_SHIFT))); ps = ptbase + ptwidth - 1; if (odd) { tmp0 += MAKE_64BIT_MASK(ps, 1); From 91ffd93be614da080a6dd8826d999e3e4761f78b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 1 Nov 2023 11:08:11 +0800 Subject: [PATCH 386/974] linux-user/loongarch64: Use traps to track LSX/LASX usage Signed-off-by: Richard Henderson Message-Id: <20231101030816.2353416-2-gaosong@loongson.cn> Signed-off-by: Song Gao --- linux-user/loongarch64/cpu_loop.c | 13 +++++++++++++ target/loongarch/insn_trans/trans_vec.c.inc | 11 ----------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/linux-user/loongarch64/cpu_loop.c b/linux-user/loongarch64/cpu_loop.c index 894fdd111a..73d7b6796a 100644 --- a/linux-user/loongarch64/cpu_loop.c +++ b/linux-user/loongarch64/cpu_loop.c @@ -72,6 +72,19 @@ void cpu_loop(CPULoongArchState *env) case EXCCODE_BCE: force_sig_fault(TARGET_SIGSYS, TARGET_SI_KERNEL, env->pc); break; + + /* + * Begin with LSX and LASX disabled, then enable on the first trap. + * In this way we can tell if the unit is in use. This is used to + * choose the layout of any signal frame. + */ + case EXCCODE_SXD: + env->CSR_EUEN |= R_CSR_EUEN_SXE_MASK; + break; + case EXCCODE_ASXD: + env->CSR_EUEN |= R_CSR_EUEN_ASXE_MASK; + break; + case EXCP_ATOMIC: cpu_exec_step_atomic(cs); break; diff --git a/target/loongarch/insn_trans/trans_vec.c.inc b/target/loongarch/insn_trans/trans_vec.c.inc index 98f856bb29..92b1d22e28 100644 --- a/target/loongarch/insn_trans/trans_vec.c.inc +++ b/target/loongarch/insn_trans/trans_vec.c.inc @@ -4,8 +4,6 @@ * Copyright (c) 2022-2023 Loongson Technology Corporation Limited */ -#ifndef CONFIG_USER_ONLY - static bool check_vec(DisasContext *ctx, uint32_t oprsz) { if ((oprsz == 16) && ((ctx->base.tb->flags & HW_FLAGS_EUEN_SXE) == 0)) { @@ -21,15 +19,6 @@ static bool check_vec(DisasContext *ctx, uint32_t oprsz) return true; } -#else - -static bool check_vec(DisasContext *ctx, uint32_t oprsz) -{ - return true; -} - -#endif - static bool gen_vvvv_ptr_vl(DisasContext *ctx, arg_vvvv *a, uint32_t oprsz, gen_helper_gvec_4_ptr *fn) { From 420756c2dd1413cdd06e8c86b4bf80fba08f009f Mon Sep 17 00:00:00 2001 From: Song Gao Date: Wed, 1 Nov 2023 11:08:12 +0800 Subject: [PATCH 387/974] linux-user/loongarch64: Fix setup_extcontext alloc wrong fpu_context size See: https://github.com/torvalds/linux/blob/master/arch/loongarch/kernel/signal.c The alloc size is sizeof(struct target_fpu_context). Signed-off-by: Song Gao Reviewed-by: Richard Henderson Message-Id: <20231101030816.2353416-3-gaosong@loongson.cn> --- linux-user/loongarch64/signal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux-user/loongarch64/signal.c b/linux-user/loongarch64/signal.c index afcee641a6..6844f187ee 100644 --- a/linux-user/loongarch64/signal.c +++ b/linux-user/loongarch64/signal.c @@ -115,7 +115,7 @@ static abi_ptr setup_extcontext(struct extctx_layout *extctx, abi_ptr sp) /* For qemu, there is no lazy fp context switch, so fp always present. */ extctx->flags = SC_USED_FP; sp = extframe_alloc(extctx, &extctx->fpu, - sizeof(struct target_rt_sigframe), FPU_CTX_ALIGN, sp); + sizeof(struct target_fpu_context), FPU_CTX_ALIGN, sp); return sp; } From 01714edafd6a89066d827867e4d6671a68c2f546 Mon Sep 17 00:00:00 2001 From: Song Gao Date: Wed, 1 Nov 2023 11:08:13 +0800 Subject: [PATCH 388/974] linux-user/loongarch64: setup_sigframe() set 'end' context size 0 See: https://github.com/torvalds/linux/blob/master/arch/loongarch/kernel/signal.c The kernel setup_sigcontext() set end context size 0. Signed-off-by: Song Gao Reviewed-by: Richard Henderson Message-Id: <20231101030816.2353416-4-gaosong@loongson.cn> --- linux-user/loongarch64/signal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux-user/loongarch64/signal.c b/linux-user/loongarch64/signal.c index 6844f187ee..cc8324b4b3 100644 --- a/linux-user/loongarch64/signal.c +++ b/linux-user/loongarch64/signal.c @@ -154,7 +154,7 @@ static void setup_sigframe(CPULoongArchState *env, */ info = extctx->end.haddr; __put_user(0, &info->magic); - __put_user(extctx->end.size, &info->size); + __put_user(0, &info->size); } static bool parse_extcontext(struct extctx_layout *extctx, abi_ptr frame) From 7c0ea81ce6042150f180701acc826cc41592cac8 Mon Sep 17 00:00:00 2001 From: Song Gao Date: Wed, 1 Nov 2023 11:08:14 +0800 Subject: [PATCH 389/974] linux-user/loongarch64: Use abi_{ulong,uint} types Signed-off-by: Song Gao Reviewed-by: Richard Henderson Message-Id: <20231101030816.2353416-5-gaosong@loongson.cn> --- linux-user/loongarch64/signal.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/linux-user/loongarch64/signal.c b/linux-user/loongarch64/signal.c index cc8324b4b3..9336f40ffe 100644 --- a/linux-user/loongarch64/signal.c +++ b/linux-user/loongarch64/signal.c @@ -18,10 +18,10 @@ #define SC_USED_FP (1 << 0) struct target_sigcontext { - uint64_t sc_pc; - uint64_t sc_regs[32]; - uint32_t sc_flags; - uint64_t sc_extcontext[0] QEMU_ALIGNED(16); + abi_ulong sc_pc; + abi_ulong sc_regs[32]; + abi_uint sc_flags; + abi_ulong sc_extcontext[0] QEMU_ALIGNED(16); }; QEMU_BUILD_BUG_ON(sizeof(struct target_sigcontext) != sizeof_sigcontext); @@ -33,9 +33,9 @@ QEMU_BUILD_BUG_ON(offsetof(struct target_sigcontext, sc_regs) #define FPU_CTX_MAGIC 0x46505501 #define FPU_CTX_ALIGN 8 struct target_fpu_context { - uint64_t regs[32]; - uint64_t fcc; - uint32_t fcsr; + abi_ulong regs[32]; + abi_ulong fcc; + abi_uint fcsr; } QEMU_ALIGNED(FPU_CTX_ALIGN); QEMU_BUILD_BUG_ON(offsetof(struct target_fpu_context, regs) @@ -43,9 +43,9 @@ QEMU_BUILD_BUG_ON(offsetof(struct target_fpu_context, regs) #define CONTEXT_INFO_ALIGN 16 struct target_sctx_info { - uint32_t magic; - uint32_t size; - uint64_t padding; + abi_uint magic; + abi_uint size; + abi_ulong padding; } QEMU_ALIGNED(CONTEXT_INFO_ALIGN); QEMU_BUILD_BUG_ON(sizeof(struct target_sctx_info) != sizeof_sctx_info); @@ -162,7 +162,7 @@ static bool parse_extcontext(struct extctx_layout *extctx, abi_ptr frame) memset(extctx, 0, sizeof(*extctx)); while (1) { - uint32_t magic, size; + abi_uint magic, size; if (get_user_u32(magic, frame) || get_user_u32(size, frame + 4)) { return false; @@ -206,7 +206,7 @@ static void restore_sigframe(CPULoongArchState *env, if (extctx->fpu.haddr) { struct target_fpu_context *fpu_ctx = extctx->fpu.haddr + sizeof(struct target_sctx_info); - uint64_t fcc; + abi_ulong fcc; for (i = 0; i < 32; ++i) { __get_user(env->fpr[i].vreg.D(0), &fpu_ctx->regs[i]); From 90ea967d149b2b3420f41ecbd0909be1f902bd2b Mon Sep 17 00:00:00 2001 From: Song Gao Date: Wed, 1 Nov 2023 11:08:15 +0800 Subject: [PATCH 390/974] linux-user/loongarch64: Add LSX sigcontext save/restore Signed-off-by: Song Gao Reviewed-by: Richard Henderson Message-Id: <20231101030816.2353416-6-gaosong@loongson.cn> --- linux-user/loongarch64/signal.c | 111 +++++++++++++++++++++++++------- 1 file changed, 89 insertions(+), 22 deletions(-) diff --git a/linux-user/loongarch64/signal.c b/linux-user/loongarch64/signal.c index 9336f40ffe..9c9b446a91 100644 --- a/linux-user/loongarch64/signal.c +++ b/linux-user/loongarch64/signal.c @@ -41,6 +41,14 @@ struct target_fpu_context { QEMU_BUILD_BUG_ON(offsetof(struct target_fpu_context, regs) != offsetof_fpucontext_fr); +#define LSX_CTX_MAGIC 0x53580001 +#define LSX_CTX_ALIGN 16 +struct target_lsx_context { + abi_ulong regs[2 * 32]; + abi_ulong fcc; + abi_uint fcsr; +} QEMU_ALIGNED(LSX_CTX_ALIGN); + #define CONTEXT_INFO_ALIGN 16 struct target_sctx_info { abi_uint magic; @@ -81,9 +89,10 @@ struct ctx_layout { }; struct extctx_layout { - unsigned int size; + unsigned long size; unsigned int flags; struct ctx_layout fpu; + struct ctx_layout lsx; struct ctx_layout end; }; @@ -105,7 +114,8 @@ static abi_ptr extframe_alloc(struct extctx_layout *extctx, return sp; } -static abi_ptr setup_extcontext(struct extctx_layout *extctx, abi_ptr sp) +static abi_ptr setup_extcontext(CPULoongArchState *env, + struct extctx_layout *extctx, abi_ptr sp) { memset(extctx, 0, sizeof(struct extctx_layout)); @@ -114,8 +124,15 @@ static abi_ptr setup_extcontext(struct extctx_layout *extctx, abi_ptr sp) /* For qemu, there is no lazy fp context switch, so fp always present. */ extctx->flags = SC_USED_FP; - sp = extframe_alloc(extctx, &extctx->fpu, + + if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, SXE)) { + sp = extframe_alloc(extctx, &extctx->lsx, + sizeof(struct target_lsx_context), LSX_CTX_ALIGN, sp); + + } else { + sp = extframe_alloc(extctx, &extctx->fpu, sizeof(struct target_fpu_context), FPU_CTX_ALIGN, sp); + } return sp; } @@ -125,7 +142,6 @@ static void setup_sigframe(CPULoongArchState *env, struct extctx_layout *extctx) { struct target_sctx_info *info; - struct target_fpu_context *fpu_ctx; int i; __put_user(extctx->flags, &sc->sc_flags); @@ -136,18 +152,39 @@ static void setup_sigframe(CPULoongArchState *env, } /* - * Set fpu context + * Set extension context */ - info = extctx->fpu.haddr; - __put_user(FPU_CTX_MAGIC, &info->magic); - __put_user(extctx->fpu.size, &info->size); - fpu_ctx = (struct target_fpu_context *)(info + 1); - for (i = 0; i < 32; ++i) { - __put_user(env->fpr[i].vreg.D(0), &fpu_ctx->regs[i]); + if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, SXE)) { + struct target_lsx_context *lsx_ctx; + info = extctx->lsx.haddr; + + __put_user(LSX_CTX_MAGIC, &info->magic); + __put_user(extctx->lsx.size, &info->size); + + lsx_ctx = (struct target_lsx_context *)(info + 1); + + for (i = 0; i < 32; ++i) { + __put_user(env->fpr[i].vreg.UD(0), &lsx_ctx->regs[2 * i]); + __put_user(env->fpr[i].vreg.UD(1), &lsx_ctx->regs[2 * i + 1]); + } + __put_user(read_fcc(env), &lsx_ctx->fcc); + __put_user(env->fcsr0, &lsx_ctx->fcsr); + } else { + struct target_fpu_context *fpu_ctx; + info = extctx->fpu.haddr; + + __put_user(FPU_CTX_MAGIC, &info->magic); + __put_user(extctx->fpu.size, &info->size); + + fpu_ctx = (struct target_fpu_context *)(info + 1); + + for (i = 0; i < 32; ++i) { + __put_user(env->fpr[i].vreg.UD(0), &fpu_ctx->regs[i]); + } + __put_user(read_fcc(env), &fpu_ctx->fcc); + __put_user(env->fcsr0, &fpu_ctx->fcsr); } - __put_user(read_fcc(env), &fpu_ctx->fcc); - __put_user(env->fcsr0, &fpu_ctx->fcsr); /* * Set end context @@ -184,6 +221,15 @@ static bool parse_extcontext(struct extctx_layout *extctx, abi_ptr frame) extctx->fpu.size = size; extctx->size += size; break; + case LSX_CTX_MAGIC: + if (size < (sizeof(struct target_sctx_info) + + sizeof(struct target_lsx_context))) { + return false; + } + extctx->lsx.gaddr = frame; + extctx->lsx.size = size; + extctx->size += size; + break; default: return false; } @@ -197,19 +243,31 @@ static void restore_sigframe(CPULoongArchState *env, struct extctx_layout *extctx) { int i; + abi_ulong fcc; __get_user(env->pc, &sc->sc_pc); for (i = 1; i < 32; ++i) { __get_user(env->gpr[i], &sc->sc_regs[i]); } - if (extctx->fpu.haddr) { - struct target_fpu_context *fpu_ctx = - extctx->fpu.haddr + sizeof(struct target_sctx_info); - abi_ulong fcc; + if (extctx->lsx.haddr) { + struct target_lsx_context *lsx_ctx = + extctx->lsx.haddr + sizeof(struct target_sctx_info); for (i = 0; i < 32; ++i) { - __get_user(env->fpr[i].vreg.D(0), &fpu_ctx->regs[i]); + __get_user(env->fpr[i].vreg.UD(0), &lsx_ctx->regs[2 * i]); + __get_user(env->fpr[i].vreg.UD(1), &lsx_ctx->regs[2 * i + 1]); + } + __get_user(fcc, &lsx_ctx->fcc); + write_fcc(env, fcc); + __get_user(env->fcsr0, &lsx_ctx->fcsr); + restore_fp_status(env); + } else if (extctx->fpu.haddr) { + struct target_fpu_context *fpu_ctx = + extctx->fpu.haddr + sizeof(struct target_sctx_info); + + for (i = 0; i < 32; ++i) { + __get_user(env->fpr[i].vreg.UD(0), &fpu_ctx->regs[i]); } __get_user(fcc, &fpu_ctx->fcc); write_fcc(env, fcc); @@ -229,7 +287,7 @@ static abi_ptr get_sigframe(struct target_sigaction *ka, sp = target_sigsp(get_sp_from_cpustate(env), ka); sp = ROUND_DOWN(sp, 16); - sp = setup_extcontext(extctx, sp); + sp = setup_extcontext(env, extctx, sp); sp -= sizeof(struct target_rt_sigframe); assert(QEMU_IS_ALIGNED(sp, 16)); @@ -255,8 +313,14 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, force_sigsegv(sig); return; } - extctx.fpu.haddr = (void *)frame + (extctx.fpu.gaddr - frame_addr); - extctx.end.haddr = (void *)frame + (extctx.end.gaddr - frame_addr); + + if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, SXE)) { + extctx.lsx.haddr = (void *)frame + (extctx.lsx.gaddr - frame_addr); + extctx.end.haddr = (void *)frame + (extctx.end.gaddr - frame_addr); + } else { + extctx.fpu.haddr = (void *)frame + (extctx.fpu.gaddr - frame_addr); + extctx.end.haddr = (void *)frame + (extctx.end.gaddr - frame_addr); + } tswap_siginfo(&frame->rs_info, info); @@ -299,7 +363,10 @@ long do_rt_sigreturn(CPULoongArchState *env) if (!frame) { goto badframe; } - if (extctx.fpu.gaddr) { + + if (extctx.lsx.gaddr) { + extctx.lsx.haddr = (void *)frame + (extctx.lsx.gaddr - frame_addr); + } else if (extctx.fpu.gaddr) { extctx.fpu.haddr = (void *)frame + (extctx.fpu.gaddr - frame_addr); } From f7077737531b40aa879d4644837aeda0f7fc6aa8 Mon Sep 17 00:00:00 2001 From: Song Gao Date: Wed, 1 Nov 2023 11:08:16 +0800 Subject: [PATCH 391/974] linux-user/loongarch64: Add LASX sigcontext save/restore Signed-off-by: Song Gao Reviewed-by: Richard Henderson Message-Id: <20231101030816.2353416-7-gaosong@loongson.cn> --- linux-user/loongarch64/signal.c | 68 ++++++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 6 deletions(-) diff --git a/linux-user/loongarch64/signal.c b/linux-user/loongarch64/signal.c index 9c9b446a91..39ea82c814 100644 --- a/linux-user/loongarch64/signal.c +++ b/linux-user/loongarch64/signal.c @@ -49,6 +49,14 @@ struct target_lsx_context { abi_uint fcsr; } QEMU_ALIGNED(LSX_CTX_ALIGN); +#define LASX_CTX_MAGIC 0x41535801 +#define LASX_CTX_ALIGN 32 +struct target_lasx_context { + abi_ulong regs[4 * 32]; + abi_ulong fcc; + abi_uint fcsr; +} QEMU_ALIGNED(LASX_CTX_ALIGN); + #define CONTEXT_INFO_ALIGN 16 struct target_sctx_info { abi_uint magic; @@ -93,6 +101,7 @@ struct extctx_layout { unsigned int flags; struct ctx_layout fpu; struct ctx_layout lsx; + struct ctx_layout lasx; struct ctx_layout end; }; @@ -125,10 +134,12 @@ static abi_ptr setup_extcontext(CPULoongArchState *env, /* For qemu, there is no lazy fp context switch, so fp always present. */ extctx->flags = SC_USED_FP; - if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, SXE)) { + if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, ASXE)) { + sp = extframe_alloc(extctx, &extctx->lasx, + sizeof(struct target_lasx_context), LASX_CTX_ALIGN, sp); + } else if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, SXE)) { sp = extframe_alloc(extctx, &extctx->lsx, sizeof(struct target_lsx_context), LSX_CTX_ALIGN, sp); - } else { sp = extframe_alloc(extctx, &extctx->fpu, sizeof(struct target_fpu_context), FPU_CTX_ALIGN, sp); @@ -155,7 +166,24 @@ static void setup_sigframe(CPULoongArchState *env, * Set extension context */ - if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, SXE)) { + if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, ASXE)) { + struct target_lasx_context *lasx_ctx; + info = extctx->lasx.haddr; + + __put_user(LASX_CTX_MAGIC, &info->magic); + __put_user(extctx->lasx.size, &info->size); + + lasx_ctx = (struct target_lasx_context *)(info + 1); + + for (i = 0; i < 32; ++i) { + __put_user(env->fpr[i].vreg.UD(0), &lasx_ctx->regs[4 * i]); + __put_user(env->fpr[i].vreg.UD(1), &lasx_ctx->regs[4 * i + 1]); + __put_user(env->fpr[i].vreg.UD(2), &lasx_ctx->regs[4 * i + 2]); + __put_user(env->fpr[i].vreg.UD(3), &lasx_ctx->regs[4 * i + 3]); + } + __put_user(read_fcc(env), &lasx_ctx->fcc); + __put_user(env->fcsr0, &lasx_ctx->fcsr); + } else if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, SXE)) { struct target_lsx_context *lsx_ctx; info = extctx->lsx.haddr; @@ -230,6 +258,15 @@ static bool parse_extcontext(struct extctx_layout *extctx, abi_ptr frame) extctx->lsx.size = size; extctx->size += size; break; + case LASX_CTX_MAGIC: + if (size < (sizeof(struct target_sctx_info) + + sizeof(struct target_lasx_context))) { + return false; + } + extctx->lasx.gaddr = frame; + extctx->lasx.size = size; + extctx->size += size; + break; default: return false; } @@ -250,7 +287,21 @@ static void restore_sigframe(CPULoongArchState *env, __get_user(env->gpr[i], &sc->sc_regs[i]); } - if (extctx->lsx.haddr) { + if (extctx->lasx.haddr) { + struct target_lasx_context *lasx_ctx = + extctx->lasx.haddr + sizeof(struct target_sctx_info); + + for (i = 0; i < 32; ++i) { + __get_user(env->fpr[i].vreg.UD(0), &lasx_ctx->regs[4 * i]); + __get_user(env->fpr[i].vreg.UD(1), &lasx_ctx->regs[4 * i + 1]); + __get_user(env->fpr[i].vreg.UD(2), &lasx_ctx->regs[4 * i + 2]); + __get_user(env->fpr[i].vreg.UD(3), &lasx_ctx->regs[4 * i + 3]); + } + __get_user(fcc, &lasx_ctx->fcc); + write_fcc(env, fcc); + __get_user(env->fcsr0, &lasx_ctx->fcsr); + restore_fp_status(env); + } else if (extctx->lsx.haddr) { struct target_lsx_context *lsx_ctx = extctx->lsx.haddr + sizeof(struct target_sctx_info); @@ -314,7 +365,10 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, return; } - if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, SXE)) { + if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, ASXE)) { + extctx.lasx.haddr = (void *)frame + (extctx.lasx.gaddr - frame_addr); + extctx.end.haddr = (void *)frame + (extctx.end.gaddr - frame_addr); + } else if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, SXE)) { extctx.lsx.haddr = (void *)frame + (extctx.lsx.gaddr - frame_addr); extctx.end.haddr = (void *)frame + (extctx.end.gaddr - frame_addr); } else { @@ -364,7 +418,9 @@ long do_rt_sigreturn(CPULoongArchState *env) goto badframe; } - if (extctx.lsx.gaddr) { + if (extctx.lasx.gaddr) { + extctx.lasx.haddr = (void *)frame + (extctx.lasx.gaddr - frame_addr); + } else if (extctx.lsx.gaddr) { extctx.lsx.haddr = (void *)frame + (extctx.lsx.gaddr - frame_addr); } else if (extctx.fpu.gaddr) { extctx.fpu.haddr = (void *)frame + (extctx.fpu.gaddr - frame_addr); From cce10a1f0c827653c00e81b6d7865cf974275994 Mon Sep 17 00:00:00 2001 From: Hyman Huang Date: Wed, 1 Nov 2023 22:04:04 +0800 Subject: [PATCH 392/974] system/dirtylimit: Fix a race situation Fix a race situation for global variable dirtylimit_state. Also, replace usleep by g_usleep to increase platform accessibility to the sleep function. Signed-off-by: Hyman Huang Reviewed-by: Fabiano Rosas Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-ID: <27c86239e21eda03d11ce5a3d07da3c229f562e3.1698847223.git.yong.huang@smartx.com> --- system/dirtylimit.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/system/dirtylimit.c b/system/dirtylimit.c index fa959d7743..3666c4cb7c 100644 --- a/system/dirtylimit.c +++ b/system/dirtylimit.c @@ -411,12 +411,20 @@ void dirtylimit_set_all(uint64_t quota, void dirtylimit_vcpu_execute(CPUState *cpu) { - if (dirtylimit_in_service() && - dirtylimit_vcpu_get_state(cpu->cpu_index)->enabled && - cpu->throttle_us_per_full) { - trace_dirtylimit_vcpu_execute(cpu->cpu_index, - cpu->throttle_us_per_full); - usleep(cpu->throttle_us_per_full); + if (cpu->throttle_us_per_full) { + dirtylimit_state_lock(); + + if (dirtylimit_in_service() && + dirtylimit_vcpu_get_state(cpu->cpu_index)->enabled) { + dirtylimit_state_unlock(); + trace_dirtylimit_vcpu_execute(cpu->cpu_index, + cpu->throttle_us_per_full); + + g_usleep(cpu->throttle_us_per_full); + return; + } + + dirtylimit_state_unlock(); } } From 78a7ef15804c0c0a886fcf9f12e5464f8eddd20c Mon Sep 17 00:00:00 2001 From: Hyman Huang Date: Wed, 1 Nov 2023 22:04:05 +0800 Subject: [PATCH 393/974] system/dirtylimit: Drop the reduplicative check Checking if dirty limit is in service is done by the dirtylimit_query_all function, drop the reduplicative check in the qmp_query_vcpu_dirty_limit function. Signed-off-by: Hyman Huang Reviewed-by: Fabiano Rosas Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-ID: --- system/dirtylimit.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/system/dirtylimit.c b/system/dirtylimit.c index 3666c4cb7c..495c7a7082 100644 --- a/system/dirtylimit.c +++ b/system/dirtylimit.c @@ -652,10 +652,6 @@ static struct DirtyLimitInfoList *dirtylimit_query_all(void) struct DirtyLimitInfoList *qmp_query_vcpu_dirty_limit(Error **errp) { - if (!dirtylimit_in_service()) { - return NULL; - } - return dirtylimit_query_all(); } From 17257b90be4f51dd3040ca2e472201407b2327c4 Mon Sep 17 00:00:00 2001 From: Hyman Huang Date: Wed, 1 Nov 2023 22:04:06 +0800 Subject: [PATCH 394/974] tests: Add migration dirty-limit capability test Add migration dirty-limit capability test if kernel support dirty ring. Migration dirty-limit capability introduce dirty limit capability, two parameters: x-vcpu-dirty-limit-period and vcpu-dirty-limit are introduced to implement the live migration with dirty limit. The test case does the following things: 1. start src, dst vm and enable dirty-limit capability 2. start migrate and set cancel it to check if dirty limit stop working. 3. restart dst vm 4. start migrate and enable dirty-limit capability 5. check if migration satisfy the convergence condition during pre-switchover phase. Note that this test case involves many passes, so it runs in slow mode only. Signed-off-by: Hyman Huang Acked-by: Peter Xu Reviewed-by: Fabiano Rosas Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-ID: --- tests/qtest/migration-test.c | 164 +++++++++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index e803b46039..5752412b64 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -3091,6 +3091,166 @@ static void test_vcpu_dirty_limit(void) dirtylimit_stop_vm(vm); } +static void migrate_dirty_limit_wait_showup(QTestState *from, + const int64_t period, + const int64_t value) +{ + /* Enable dirty limit capability */ + migrate_set_capability(from, "dirty-limit", true); + + /* Set dirty limit parameters */ + migrate_set_parameter_int(from, "x-vcpu-dirty-limit-period", period); + migrate_set_parameter_int(from, "vcpu-dirty-limit", value); + + /* Make sure migrate can't converge */ + migrate_ensure_non_converge(from); + + /* To check limit rate after precopy */ + migrate_set_capability(from, "pause-before-switchover", true); + + /* Wait for the serial output from the source */ + wait_for_serial("src_serial"); +} + +/* + * This test does: + * source destination + * start vm + * start incoming vm + * migrate + * wait dirty limit to begin + * cancel migrate + * cancellation check + * restart incoming vm + * migrate + * wait dirty limit to begin + * wait pre-switchover event + * convergence condition check + * + * And see if dirty limit migration works correctly. + * This test case involves many passes, so it runs in slow mode only. + */ +static void test_migrate_dirty_limit(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + QTestState *from, *to; + int64_t remaining; + uint64_t throttle_us_per_full; + /* + * We want the test to be stable and as fast as possible. + * E.g., with 1Gb/s bandwith migration may pass without dirty limit, + * so we need to decrease a bandwidth. + */ + const int64_t dirtylimit_period = 1000, dirtylimit_value = 50; + const int64_t max_bandwidth = 400000000; /* ~400Mb/s */ + const int64_t downtime_limit = 250; /* 250ms */ + /* + * We migrate through unix-socket (> 500Mb/s). + * Thus, expected migration speed ~= bandwidth limit (< 500Mb/s). + * So, we can predict expected_threshold + */ + const int64_t expected_threshold = max_bandwidth * downtime_limit / 1000; + int max_try_count = 10; + MigrateCommon args = { + .start = { + .hide_stderr = true, + .use_dirty_ring = true, + }, + .listen_uri = uri, + .connect_uri = uri, + }; + + /* Start src, dst vm */ + if (test_migrate_start(&from, &to, args.listen_uri, &args.start)) { + return; + } + + /* Prepare for dirty limit migration and wait src vm show up */ + migrate_dirty_limit_wait_showup(from, dirtylimit_period, dirtylimit_value); + + /* Start migrate */ + migrate_qmp(from, uri, "{}"); + + /* Wait for dirty limit throttle begin */ + throttle_us_per_full = 0; + while (throttle_us_per_full == 0) { + throttle_us_per_full = + read_migrate_property_int(from, "dirty-limit-throttle-time-per-round"); + usleep(100); + g_assert_false(got_src_stop); + } + + /* Now cancel migrate and wait for dirty limit throttle switch off */ + migrate_cancel(from); + wait_for_migration_status(from, "cancelled", NULL); + + /* Check if dirty limit throttle switched off, set timeout 1ms */ + do { + throttle_us_per_full = + read_migrate_property_int(from, "dirty-limit-throttle-time-per-round"); + usleep(100); + g_assert_false(got_src_stop); + } while (throttle_us_per_full != 0 && --max_try_count); + + /* Assert dirty limit is not in service */ + g_assert_cmpint(throttle_us_per_full, ==, 0); + + args = (MigrateCommon) { + .start = { + .only_target = true, + .use_dirty_ring = true, + }, + .listen_uri = uri, + .connect_uri = uri, + }; + + /* Restart dst vm, src vm already show up so we needn't wait anymore */ + if (test_migrate_start(&from, &to, args.listen_uri, &args.start)) { + return; + } + + /* Start migrate */ + migrate_qmp(from, uri, "{}"); + + /* Wait for dirty limit throttle begin */ + throttle_us_per_full = 0; + while (throttle_us_per_full == 0) { + throttle_us_per_full = + read_migrate_property_int(from, "dirty-limit-throttle-time-per-round"); + usleep(100); + g_assert_false(got_src_stop); + } + + /* + * The dirty limit rate should equals the return value of + * query-vcpu-dirty-limit if dirty limit cap set + */ + g_assert_cmpint(dirtylimit_value, ==, get_limit_rate(from)); + + /* Now, we have tested if dirty limit works, let it converge */ + migrate_set_parameter_int(from, "downtime-limit", downtime_limit); + migrate_set_parameter_int(from, "max-bandwidth", max_bandwidth); + + /* + * Wait for pre-switchover status to check if migration + * satisfy the convergence condition + */ + wait_for_migration_status(from, "pre-switchover", NULL); + + remaining = read_ram_property_int(from, "remaining"); + g_assert_cmpint(remaining, <, + (expected_threshold + expected_threshold / 100)); + + migrate_continue(from, "pre-switchover"); + + qtest_qmp_eventwait(to, "RESUME"); + + wait_for_serial("dest_serial"); + wait_for_migration_complete(from); + + test_migrate_end(from, to, true); +} + static bool kvm_dirty_ring_supported(void) { #if defined(__linux__) && defined(HOST_X86_64) @@ -3301,6 +3461,10 @@ int main(int argc, char **argv) */ if (g_test_slow()) { qtest_add_func("/migration/auto_converge", test_migrate_auto_converge); + if (g_str_equal(arch, "x86_64") && + has_kvm && kvm_dirty_ring_supported()) { + qtest_add_func("/migration/dirty_limit", test_migrate_dirty_limit); + } } qtest_add_func("/migration/multifd/tcp/plain/none", test_multifd_tcp_none); From 4cc563d460b6e8a29d18b27381dab8ca0621f2da Mon Sep 17 00:00:00 2001 From: Hyman Huang Date: Wed, 1 Nov 2023 22:04:07 +0800 Subject: [PATCH 395/974] tests/migration: Introduce dirty-ring-size option into guestperf Dirty ring size configuration is not supported by guestperf tool. Introduce dirty-ring-size (ranges in [1024, 65536]) option so developers can play with dirty-ring and dirty-limit feature easier. To set dirty ring size with 4096 during migration test: $ ./tests/migration/guestperf.py --dirty-ring-size 4096 xxx Signed-off-by: Hyman Huang Reviewed-by: Fabiano Rosas Signed-off-by: Juan Quintela Message-ID: <8a388cec5c1f73a34d42515bbc43837e97ee3839.1698847223.git.yong.huang@smartx.com> --- tests/migration/guestperf/engine.py | 6 +++++- tests/migration/guestperf/hardware.py | 8 ++++++-- tests/migration/guestperf/shell.py | 6 +++++- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/tests/migration/guestperf/engine.py b/tests/migration/guestperf/engine.py index da96ca034a..aabf6de4d9 100644 --- a/tests/migration/guestperf/engine.py +++ b/tests/migration/guestperf/engine.py @@ -325,7 +325,6 @@ class Engine(object): cmdline = "'" + cmdline + "'" argv = [ - "-accel", "kvm", "-cpu", "host", "-kernel", self._kernel, "-initrd", self._initrd, @@ -333,6 +332,11 @@ class Engine(object): "-m", str((hardware._mem * 1024) + 512), "-smp", str(hardware._cpus), ] + if hardware._dirty_ring_size: + argv.extend(["-accel", "kvm,dirty-ring-size=%s" % + hardware._dirty_ring_size]) + else: + argv.extend(["-accel", "kvm"]) argv.extend(self._get_qemu_serial_args()) diff --git a/tests/migration/guestperf/hardware.py b/tests/migration/guestperf/hardware.py index 3145785ffd..f779cc050b 100644 --- a/tests/migration/guestperf/hardware.py +++ b/tests/migration/guestperf/hardware.py @@ -23,7 +23,8 @@ class Hardware(object): src_cpu_bind=None, src_mem_bind=None, dst_cpu_bind=None, dst_mem_bind=None, prealloc_pages = False, - huge_pages=False, locked_pages=False): + huge_pages=False, locked_pages=False, + dirty_ring_size=0): self._cpus = cpus self._mem = mem # GiB self._src_mem_bind = src_mem_bind # List of NUMA nodes @@ -33,6 +34,7 @@ class Hardware(object): self._prealloc_pages = prealloc_pages self._huge_pages = huge_pages self._locked_pages = locked_pages + self._dirty_ring_size = dirty_ring_size def serialize(self): @@ -46,6 +48,7 @@ class Hardware(object): "prealloc_pages": self._prealloc_pages, "huge_pages": self._huge_pages, "locked_pages": self._locked_pages, + "dirty_ring_size": self._dirty_ring_size, } @classmethod @@ -59,4 +62,5 @@ class Hardware(object): data["dst_mem_bind"], data["prealloc_pages"], data["huge_pages"], - data["locked_pages"]) + data["locked_pages"], + data["dirty_ring_size"]) diff --git a/tests/migration/guestperf/shell.py b/tests/migration/guestperf/shell.py index 8a809e3dda..7d6b8cd7cf 100644 --- a/tests/migration/guestperf/shell.py +++ b/tests/migration/guestperf/shell.py @@ -60,6 +60,8 @@ class BaseShell(object): parser.add_argument("--prealloc-pages", dest="prealloc_pages", default=False) parser.add_argument("--huge-pages", dest="huge_pages", default=False) parser.add_argument("--locked-pages", dest="locked_pages", default=False) + parser.add_argument("--dirty-ring-size", dest="dirty_ring_size", + default=0, type=int) self._parser = parser @@ -89,7 +91,9 @@ class BaseShell(object): locked_pages=args.locked_pages, huge_pages=args.huge_pages, - prealloc_pages=args.prealloc_pages) + prealloc_pages=args.prealloc_pages, + + dirty_ring_size=args.dirty_ring_size) class Shell(BaseShell): From 22b7cb2c79d2df0946ec1cf88dfc1c6973e6008d Mon Sep 17 00:00:00 2001 From: Hyman Huang Date: Wed, 1 Nov 2023 22:04:08 +0800 Subject: [PATCH 396/974] tests/migration: Introduce dirty-limit into guestperf Currently, guestperf does not cover the dirty-limit migration, support this feature. Note that dirty-limit requires 'dirty-ring-size' set. To enable dirty-limit, setting x-vcpu-dirty-limit-period as 500ms and x-vcpu-dirty-limit as 10MB/s: $ ./tests/migration/guestperf.py \ --dirty-ring-size 4096 \ --dirty-limit --x-vcpu-dirty-limit-period 500 \ --vcpu-dirty-limit 10 --output output.json \ To run the entire standardized set of dirty-limit-enabled comparisons, with unix migration: $ ./tests/migration/guestperf-batch.py \ --dirty-ring-size 4096 \ --dst-host localhost --transport unix \ --filter compr-dirty-limit* --output outputdir Signed-off-by: Hyman Huang Reviewed-by: Fabiano Rosas Message-Id: <516e7a55dfc6e33d33510be37eb24223de5dc072.1697815117.git.yong.huang@smartx.com> Message-ID: Signed-off-by: Juan Quintela --- tests/migration/guestperf/comparison.py | 23 +++++++++++++++++++++++ tests/migration/guestperf/engine.py | 17 +++++++++++++++++ tests/migration/guestperf/progress.py | 16 ++++++++++++++-- tests/migration/guestperf/scenario.py | 11 ++++++++++- tests/migration/guestperf/shell.py | 18 +++++++++++++++++- 5 files changed, 81 insertions(+), 4 deletions(-) diff --git a/tests/migration/guestperf/comparison.py b/tests/migration/guestperf/comparison.py index c03b3f6d7e..42cc0372d1 100644 --- a/tests/migration/guestperf/comparison.py +++ b/tests/migration/guestperf/comparison.py @@ -135,4 +135,27 @@ COMPARISONS = [ Scenario("compr-multifd-channels-64", multifd=True, multifd_channels=64), ]), + + # Looking at effect of dirty-limit with + # varying x_vcpu_dirty_limit_period + Comparison("compr-dirty-limit-period", scenarios = [ + Scenario("compr-dirty-limit-period-500", + dirty_limit=True, x_vcpu_dirty_limit_period=500), + Scenario("compr-dirty-limit-period-800", + dirty_limit=True, x_vcpu_dirty_limit_period=800), + Scenario("compr-dirty-limit-period-1000", + dirty_limit=True, x_vcpu_dirty_limit_period=1000), + ]), + + + # Looking at effect of dirty-limit with + # varying vcpu_dirty_limit + Comparison("compr-dirty-limit", scenarios = [ + Scenario("compr-dirty-limit-10MB", + dirty_limit=True, vcpu_dirty_limit=10), + Scenario("compr-dirty-limit-20MB", + dirty_limit=True, vcpu_dirty_limit=20), + Scenario("compr-dirty-limit-50MB", + dirty_limit=True, vcpu_dirty_limit=50), + ]), ] diff --git a/tests/migration/guestperf/engine.py b/tests/migration/guestperf/engine.py index aabf6de4d9..608d7270f6 100644 --- a/tests/migration/guestperf/engine.py +++ b/tests/migration/guestperf/engine.py @@ -102,6 +102,8 @@ class Engine(object): info.get("expected-downtime", 0), info.get("setup-time", 0), info.get("cpu-throttle-percentage", 0), + info.get("dirty-limit-throttle-time-per-round", 0), + info.get("dirty-limit-ring-full-time", 0), ) def _migrate(self, hardware, scenario, src, dst, connect_uri): @@ -203,6 +205,21 @@ class Engine(object): resp = dst.cmd("migrate-set-parameters", multifd_channels=scenario._multifd_channels) + if scenario._dirty_limit: + if not hardware._dirty_ring_size: + raise Exception("dirty ring size must be configured when " + "testing dirty limit migration") + + resp = src.cmd("migrate-set-capabilities", + capabilities = [ + { "capability": "dirty-limit", + "state": True } + ]) + resp = src.cmd("migrate-set-parameters", + x_vcpu_dirty_limit_period=scenario._x_vcpu_dirty_limit_period) + resp = src.cmd("migrate-set-parameters", + vcpu_dirty_limit=scenario._vcpu_dirty_limit) + resp = src.cmd("migrate", uri=connect_uri) post_copy = False diff --git a/tests/migration/guestperf/progress.py b/tests/migration/guestperf/progress.py index ab1ee57273..d490584217 100644 --- a/tests/migration/guestperf/progress.py +++ b/tests/migration/guestperf/progress.py @@ -81,7 +81,9 @@ class Progress(object): downtime, downtime_expected, setup_time, - throttle_pcent): + throttle_pcent, + dirty_limit_throttle_time_per_round, + dirty_limit_ring_full_time): self._status = status self._ram = ram @@ -91,6 +93,10 @@ class Progress(object): self._downtime_expected = downtime_expected self._setup_time = setup_time self._throttle_pcent = throttle_pcent + self._dirty_limit_throttle_time_per_round = \ + dirty_limit_throttle_time_per_round + self._dirty_limit_ring_full_time = \ + dirty_limit_ring_full_time def serialize(self): return { @@ -102,6 +108,10 @@ class Progress(object): "downtime_expected": self._downtime_expected, "setup_time": self._setup_time, "throttle_pcent": self._throttle_pcent, + "dirty_limit_throttle_time_per_round": + self._dirty_limit_throttle_time_per_round, + "dirty_limit_ring_full_time": + self._dirty_limit_ring_full_time, } @classmethod @@ -114,4 +124,6 @@ class Progress(object): data["downtime"], data["downtime_expected"], data["setup_time"], - data["throttle_pcent"]) + data["throttle_pcent"], + data["dirty_limit_throttle_time_per_round"], + data["dirty_limit_ring_full_time"]) diff --git a/tests/migration/guestperf/scenario.py b/tests/migration/guestperf/scenario.py index de70d9b2f5..154c4f5d5f 100644 --- a/tests/migration/guestperf/scenario.py +++ b/tests/migration/guestperf/scenario.py @@ -30,7 +30,9 @@ class Scenario(object): auto_converge=False, auto_converge_step=10, compression_mt=False, compression_mt_threads=1, compression_xbzrle=False, compression_xbzrle_cache=10, - multifd=False, multifd_channels=2): + multifd=False, multifd_channels=2, + dirty_limit=False, x_vcpu_dirty_limit_period=500, + vcpu_dirty_limit=1): self._name = name @@ -60,6 +62,10 @@ class Scenario(object): self._multifd = multifd self._multifd_channels = multifd_channels + self._dirty_limit = dirty_limit + self._x_vcpu_dirty_limit_period = x_vcpu_dirty_limit_period + self._vcpu_dirty_limit = vcpu_dirty_limit + def serialize(self): return { "name": self._name, @@ -79,6 +85,9 @@ class Scenario(object): "compression_xbzrle_cache": self._compression_xbzrle_cache, "multifd": self._multifd, "multifd_channels": self._multifd_channels, + "dirty_limit": self._dirty_limit, + "x_vcpu_dirty_limit_period": self._x_vcpu_dirty_limit_period, + "vcpu_dirty_limit": self._vcpu_dirty_limit, } @classmethod diff --git a/tests/migration/guestperf/shell.py b/tests/migration/guestperf/shell.py index 7d6b8cd7cf..c85d89efec 100644 --- a/tests/migration/guestperf/shell.py +++ b/tests/migration/guestperf/shell.py @@ -131,6 +131,17 @@ class Shell(BaseShell): parser.add_argument("--multifd-channels", dest="multifd_channels", default=2, type=int) + parser.add_argument("--dirty-limit", dest="dirty_limit", default=False, + action="store_true") + + parser.add_argument("--x-vcpu-dirty-limit-period", + dest="x_vcpu_dirty_limit_period", + default=500, type=int) + + parser.add_argument("--vcpu-dirty-limit", + dest="vcpu_dirty_limit", + default=1, type=int) + def get_scenario(self, args): return Scenario(name="perfreport", downtime=args.downtime, @@ -154,7 +165,12 @@ class Shell(BaseShell): compression_xbzrle_cache=args.compression_xbzrle_cache, multifd=args.multifd, - multifd_channels=args.multifd_channels) + multifd_channels=args.multifd_channels, + + dirty_limit=args.dirty_limit, + x_vcpu_dirty_limit_period=\ + args.x_vcpu_dirty_limit_period, + vcpu_dirty_limit=args.vcpu_dirty_limit) def run(self, argv): args = self._parser.parse_args(argv) From ceddc48278a006e3f13b8a1881676cfb3a1ca99a Mon Sep 17 00:00:00 2001 From: Hyman Huang Date: Wed, 1 Nov 2023 22:04:09 +0800 Subject: [PATCH 397/974] docs/migration: Add the dirty limit section The dirty limit feature has been introduced since the 8.1 QEMU release but has not reflected in the document, add a section for that. Signed-off-by: Hyman Huang Reviewed-by: Fabiano Rosas Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela Message-ID: <0f2b2c63fec22ea23e4926cdeb567b7a0ebd8152.1698847223.git.yong.huang@smartx.com> --- docs/devel/migration.rst | 71 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/docs/devel/migration.rst b/docs/devel/migration.rst index 240eb16d90..5adf4f12f7 100644 --- a/docs/devel/migration.rst +++ b/docs/devel/migration.rst @@ -594,6 +594,77 @@ path. Return path - opened by main thread, written by main thread AND postcopy thread (protected by rp_mutex) +Dirty limit +===================== +The dirty limit, short for dirty page rate upper limit, is a new capability +introduced in the 8.1 QEMU release that uses a new algorithm based on the KVM +dirty ring to throttle down the guest during live migration. + +The algorithm framework is as follows: + +:: + + ------------------------------------------------------------------------------ + main --------------> throttle thread ------------> PREPARE(1) <-------- + thread \ | | + \ | | + \ V | + -\ CALCULATE(2) | + \ | | + \ | | + \ V | + \ SET PENALTY(3) ----- + -\ | + \ | + \ V + -> virtual CPU thread -------> ACCEPT PENALTY(4) + ------------------------------------------------------------------------------ + +When the qmp command qmp_set_vcpu_dirty_limit is called for the first time, +the QEMU main thread starts the throttle thread. The throttle thread, once +launched, executes the loop, which consists of three steps: + + - PREPARE (1) + + The entire work of PREPARE (1) is preparation for the second stage, + CALCULATE(2), as the name implies. It involves preparing the dirty + page rate value and the corresponding upper limit of the VM: + The dirty page rate is calculated via the KVM dirty ring mechanism, + which tells QEMU how many dirty pages a virtual CPU has had since the + last KVM_EXIT_DIRTY_RING_FULL exception; The dirty page rate upper + limit is specified by caller, therefore fetch it directly. + + - CALCULATE (2) + + Calculate a suitable sleep period for each virtual CPU, which will be + used to determine the penalty for the target virtual CPU. The + computation must be done carefully in order to reduce the dirty page + rate progressively down to the upper limit without oscillation. To + achieve this, two strategies are provided: the first is to add or + subtract sleep time based on the ratio of the current dirty page rate + to the limit, which is used when the current dirty page rate is far + from the limit; the second is to add or subtract a fixed time when + the current dirty page rate is close to the limit. + + - SET PENALTY (3) + + Set the sleep time for each virtual CPU that should be penalized based + on the results of the calculation supplied by step CALCULATE (2). + +After completing the three above stages, the throttle thread loops back +to step PREPARE (1) until the dirty limit is reached. + +On the other hand, each virtual CPU thread reads the sleep duration and +sleeps in the path of the KVM_EXIT_DIRTY_RING_FULL exception handler, that +is ACCEPT PENALTY (4). Virtual CPUs tied with writing processes will +obviously exit to the path and get penalized, whereas virtual CPUs involved +with read processes will not. + +In summary, thanks to the KVM dirty ring technology, the dirty limit +algorithm will restrict virtual CPUs as needed to keep their dirty page +rate inside the limit. This leads to more steady reading performance during +live migration and can aid in improving large guest responsiveness. + Postcopy ======== From e8f433f80e831ecd81989ae0246f3f1e2d3ac480 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Thu, 19 Oct 2023 15:45:07 +0200 Subject: [PATCH 398/974] memory: Let ReservedRegion use Range MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A reserved region is a range tagged with a type. Let's directly use the Range type in the prospect to reuse some of the library helpers shipped with the Range type. Signed-off-by: Eric Auger Reviewed-by: David Hildenbrand Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Reviewed-by: "Michael S. Tsirkin" Signed-off-by: Cédric Le Goater --- hw/core/qdev-properties-system.c | 9 ++++++--- hw/virtio/virtio-iommu.c | 6 +++--- include/exec/memory.h | 4 ++-- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index 2f1dbb3fd7..b5ccafa29e 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -705,7 +705,7 @@ static void get_reserved_region(Object *obj, Visitor *v, const char *name, int rc; rc = snprintf(buffer, sizeof(buffer), "0x%"PRIx64":0x%"PRIx64":%u", - rr->low, rr->high, rr->type); + range_lob(&rr->range), range_upb(&rr->range), rr->type); assert(rc < sizeof(buffer)); visit_type_str(v, name, &p, errp); @@ -717,6 +717,7 @@ static void set_reserved_region(Object *obj, Visitor *v, const char *name, Property *prop = opaque; ReservedRegion *rr = object_field_prop_ptr(obj, prop); const char *endptr; + uint64_t lob, upb; char *str; int ret; @@ -724,7 +725,7 @@ static void set_reserved_region(Object *obj, Visitor *v, const char *name, return; } - ret = qemu_strtou64(str, &endptr, 16, &rr->low); + ret = qemu_strtou64(str, &endptr, 16, &lob); if (ret) { error_setg(errp, "start address of '%s'" " must be a hexadecimal integer", name); @@ -734,7 +735,7 @@ static void set_reserved_region(Object *obj, Visitor *v, const char *name, goto separator_error; } - ret = qemu_strtou64(endptr + 1, &endptr, 16, &rr->high); + ret = qemu_strtou64(endptr + 1, &endptr, 16, &upb); if (ret) { error_setg(errp, "end address of '%s'" " must be a hexadecimal integer", name); @@ -744,6 +745,8 @@ static void set_reserved_region(Object *obj, Visitor *v, const char *name, goto separator_error; } + range_set_bounds(&rr->range, lob, upb); + ret = qemu_strtoui(endptr + 1, &endptr, 10, &rr->type); if (ret) { error_setg(errp, "type of '%s'" diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index be51635895..e5e46e1b55 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -645,8 +645,8 @@ static ssize_t virtio_iommu_fill_resv_mem_prop(VirtIOIOMMU *s, uint32_t ep, prop.head.type = cpu_to_le16(VIRTIO_IOMMU_PROBE_T_RESV_MEM); prop.head.length = cpu_to_le16(length); prop.subtype = subtype; - prop.start = cpu_to_le64(s->reserved_regions[i].low); - prop.end = cpu_to_le64(s->reserved_regions[i].high); + prop.start = cpu_to_le64(range_lob(&s->reserved_regions[i].range)); + prop.end = cpu_to_le64(range_upb(&s->reserved_regions[i].range)); memcpy(buf, &prop, size); @@ -897,7 +897,7 @@ static IOMMUTLBEntry virtio_iommu_translate(IOMMUMemoryRegion *mr, hwaddr addr, for (i = 0; i < s->nb_reserved_regions; i++) { ReservedRegion *reg = &s->reserved_regions[i]; - if (addr >= reg->low && addr <= reg->high) { + if (range_contains(®->range, addr)) { switch (reg->type) { case VIRTIO_IOMMU_RESV_MEM_T_MSI: entry.perm = flag; diff --git a/include/exec/memory.h b/include/exec/memory.h index 9087d02769..d94314d6fc 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -24,6 +24,7 @@ #include "qemu/bswap.h" #include "qemu/queue.h" #include "qemu/int128.h" +#include "qemu/range.h" #include "qemu/notify.h" #include "qom/object.h" #include "qemu/rcu.h" @@ -79,8 +80,7 @@ extern unsigned int global_dirty_tracking; typedef struct MemoryRegionOps MemoryRegionOps; struct ReservedRegion { - hwaddr low; - hwaddr high; + Range range; unsigned type; }; From 51478a8ef5694cbd92b9a3436ec4489464210a8e Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Thu, 19 Oct 2023 15:45:08 +0200 Subject: [PATCH 399/974] memory: Introduce memory_region_iommu_set_iova_ranges MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This helper will allow to convey information about valid IOVA ranges to virtual IOMMUS. Signed-off-by: Eric Auger Acked-by: Peter Xu Reviewed-by: "Michael S. Tsirkin" [ clg: fixes in memory_region_iommu_set_iova_ranges() and iommu_set_iova_ranges() documentation ] Signed-off-by: Cédric Le Goater --- include/exec/memory.h | 32 ++++++++++++++++++++++++++++++++ system/memory.c | 13 +++++++++++++ 2 files changed, 45 insertions(+) diff --git a/include/exec/memory.h b/include/exec/memory.h index d94314d6fc..831f7c996d 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -527,6 +527,26 @@ struct IOMMUMemoryRegionClass { int (*iommu_set_page_size_mask)(IOMMUMemoryRegion *iommu, uint64_t page_size_mask, Error **errp); + /** + * @iommu_set_iova_ranges: + * + * Propagate information about the usable IOVA ranges for a given IOMMU + * memory region. Used for example to propagate host physical device + * reserved memory region constraints to the virtual IOMMU. + * + * Optional method: if this method is not provided, then the default IOVA + * aperture is used. + * + * @iommu: the IOMMUMemoryRegion + * + * @iova_ranges: list of ordered IOVA ranges (at least one range) + * + * Returns 0 on success, or a negative error. In case of failure, the error + * object must be created. + */ + int (*iommu_set_iova_ranges)(IOMMUMemoryRegion *iommu, + GList *iova_ranges, + Error **errp); }; typedef struct RamDiscardListener RamDiscardListener; @@ -1856,6 +1876,18 @@ int memory_region_iommu_set_page_size_mask(IOMMUMemoryRegion *iommu_mr, uint64_t page_size_mask, Error **errp); +/** + * memory_region_iommu_set_iova_ranges - Set the usable IOVA ranges + * for a given IOMMU MR region + * + * @iommu: IOMMU memory region + * @iova_ranges: list of ordered IOVA ranges (at least one range) + * @errp: pointer to Error*, to store an error if it happens. + */ +int memory_region_iommu_set_iova_ranges(IOMMUMemoryRegion *iommu, + GList *iova_ranges, + Error **errp); + /** * memory_region_name: get a memory region's name * diff --git a/system/memory.c b/system/memory.c index 4928f2525d..304fa843ea 100644 --- a/system/memory.c +++ b/system/memory.c @@ -1921,6 +1921,19 @@ int memory_region_iommu_set_page_size_mask(IOMMUMemoryRegion *iommu_mr, return ret; } +int memory_region_iommu_set_iova_ranges(IOMMUMemoryRegion *iommu_mr, + GList *iova_ranges, + Error **errp) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr); + int ret = 0; + + if (imrc->iommu_set_iova_ranges) { + ret = imrc->iommu_set_iova_ranges(iommu_mr, iova_ranges, errp); + } + return ret; +} + int memory_region_register_iommu_notifier(MemoryRegion *mr, IOMMUNotifier *n, Error **errp) { From e4a8ae09c538880440ba866174b0015f147c8c9e Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Thu, 19 Oct 2023 15:45:09 +0200 Subject: [PATCH 400/974] vfio: Collect container iova range info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Collect iova range information if VFIO_IOMMU_TYPE1_INFO_CAP_IOVA_RANGE capability is supported. This allows to propagate the information though the IOMMU MR set_iova_ranges() callback so that virtual IOMMUs get aware of those aperture constraints. This is only done if the info is available and the number of iova ranges is greater than 0. A new vfio_get_info_iova_range helper is introduced matching the coding style of existing vfio_get_info_dma_avail. The boolean returned value isn't used though. Code is aligned between both. Signed-off-by: Eric Auger Reviewed-by: Alex Williamson Tested-by: Yanghang Liu Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 9 ++++++++ hw/vfio/container.c | 42 ++++++++++++++++++++++++++++++++--- include/hw/vfio/vfio-common.h | 1 + 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index d806057b40..9c5c6433f2 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -693,6 +693,15 @@ static void vfio_listener_region_add(MemoryListener *listener, goto fail; } + if (container->iova_ranges) { + ret = memory_region_iommu_set_iova_ranges(giommu->iommu_mr, + container->iova_ranges, &err); + if (ret) { + g_free(giommu); + goto fail; + } + } + ret = memory_region_register_iommu_notifier(section->mr, &giommu->n, &err); if (ret) { diff --git a/hw/vfio/container.c b/hw/vfio/container.c index adc467210f..fc88222377 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -382,7 +382,7 @@ bool vfio_get_info_dma_avail(struct vfio_iommu_type1_info *info, /* If the capability cannot be found, assume no DMA limiting */ hdr = vfio_get_iommu_type1_info_cap(info, VFIO_IOMMU_TYPE1_INFO_DMA_AVAIL); - if (hdr == NULL) { + if (!hdr) { return false; } @@ -394,6 +394,32 @@ bool vfio_get_info_dma_avail(struct vfio_iommu_type1_info *info, return true; } +static bool vfio_get_info_iova_range(struct vfio_iommu_type1_info *info, + VFIOContainer *container) +{ + struct vfio_info_cap_header *hdr; + struct vfio_iommu_type1_info_cap_iova_range *cap; + + hdr = vfio_get_iommu_type1_info_cap(info, + VFIO_IOMMU_TYPE1_INFO_CAP_IOVA_RANGE); + if (!hdr) { + return false; + } + + cap = (void *)hdr; + + for (int i = 0; i < cap->nr_iovas; i++) { + Range *range = g_new(Range, 1); + + range_set_bounds(range, cap->iova_ranges[i].start, + cap->iova_ranges[i].end); + container->iova_ranges = + range_list_insert(container->iova_ranges, range); + } + + return true; +} + static void vfio_kvm_device_add_group(VFIOGroup *group) { Error *err = NULL; @@ -535,6 +561,12 @@ static void vfio_get_iommu_info_migration(VFIOContainer *container, } } +static void vfio_free_container(VFIOContainer *container) +{ + g_list_free_full(container->iova_ranges, g_free); + g_free(container); +} + static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, Error **errp) { @@ -616,6 +648,7 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, container->error = NULL; container->dirty_pages_supported = false; container->dma_max_mappings = 0; + container->iova_ranges = NULL; QLIST_INIT(&container->giommu_list); QLIST_INIT(&container->hostwin_list); QLIST_INIT(&container->vrdl_list); @@ -652,6 +685,9 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, if (!vfio_get_info_dma_avail(info, &container->dma_max_mappings)) { container->dma_max_mappings = 65535; } + + vfio_get_info_iova_range(info, container); + vfio_get_iommu_info_migration(container, info); g_free(info); @@ -765,7 +801,7 @@ enable_discards_exit: vfio_ram_block_discard_disable(container, false); free_container_exit: - g_free(container); + vfio_free_container(container); close_fd_exit: close(fd); @@ -819,7 +855,7 @@ static void vfio_disconnect_container(VFIOGroup *group) trace_vfio_disconnect_container(container->fd); close(container->fd); - g_free(container); + vfio_free_container(container); vfio_put_address_space(space); } diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 7780b9073a..0c3d390e8b 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -99,6 +99,7 @@ typedef struct VFIOContainer { QLIST_HEAD(, VFIORamDiscardListener) vrdl_list; QLIST_ENTRY(VFIOContainer) next; QLIST_HEAD(, VFIODevice) device_list; + GList *iova_ranges; } VFIOContainer; typedef struct VFIOGuestIOMMU { From 41cc70cdf53268cd1bc9719014acf739932b51e5 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Thu, 19 Oct 2023 15:45:10 +0200 Subject: [PATCH 401/974] virtio-iommu: Rename reserved_regions into prop_resv_regions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename VirtIOIOMMU (nb_)reserved_regions fields with the "prop_" prefix to highlight those fields are set through a property, at machine level. They are IOMMU wide. A subsequent patch will introduce per IOMMUDevice reserved regions that will include both those IOMMU wide property reserved regions plus, sometimes, host reserved regions, if the device is backed by a host device protected by a physical IOMMU. Also change nb_ prefix by nr_. Signed-off-by: Eric Auger Reviewed-by: Cédric Le Goater Reviewed-by: "Michael S. Tsirkin" Signed-off-by: Cédric Le Goater --- hw/virtio/virtio-iommu-pci.c | 8 ++++---- hw/virtio/virtio-iommu.c | 15 ++++++++------- include/hw/virtio/virtio-iommu.h | 4 ++-- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/hw/virtio/virtio-iommu-pci.c b/hw/virtio/virtio-iommu-pci.c index 7ef2f9dcdb..9459fbf6ed 100644 --- a/hw/virtio/virtio-iommu-pci.c +++ b/hw/virtio/virtio-iommu-pci.c @@ -37,7 +37,7 @@ struct VirtIOIOMMUPCI { static Property virtio_iommu_pci_properties[] = { DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), DEFINE_PROP_ARRAY("reserved-regions", VirtIOIOMMUPCI, - vdev.nb_reserved_regions, vdev.reserved_regions, + vdev.nr_prop_resv_regions, vdev.prop_resv_regions, qdev_prop_reserved_region, ReservedRegion), DEFINE_PROP_END_OF_LIST(), }; @@ -54,9 +54,9 @@ static void virtio_iommu_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) "for the virtio-iommu-pci device"); return; } - for (int i = 0; i < s->nb_reserved_regions; i++) { - if (s->reserved_regions[i].type != VIRTIO_IOMMU_RESV_MEM_T_RESERVED && - s->reserved_regions[i].type != VIRTIO_IOMMU_RESV_MEM_T_MSI) { + for (int i = 0; i < s->nr_prop_resv_regions; i++) { + if (s->prop_resv_regions[i].type != VIRTIO_IOMMU_RESV_MEM_T_RESERVED && + s->prop_resv_regions[i].type != VIRTIO_IOMMU_RESV_MEM_T_MSI) { error_setg(errp, "reserved region %d has an invalid type", i); error_append_hint(errp, "Valid values are 0 and 1\n"); return; diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index e5e46e1b55..979cdb5648 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -631,22 +631,23 @@ static ssize_t virtio_iommu_fill_resv_mem_prop(VirtIOIOMMU *s, uint32_t ep, size_t size = sizeof(prop), length = size - sizeof(prop.head), total; int i; - total = size * s->nb_reserved_regions; + total = size * s->nr_prop_resv_regions; if (total > free) { return -ENOSPC; } - for (i = 0; i < s->nb_reserved_regions; i++) { - unsigned subtype = s->reserved_regions[i].type; + for (i = 0; i < s->nr_prop_resv_regions; i++) { + unsigned subtype = s->prop_resv_regions[i].type; + Range *range = &s->prop_resv_regions[i].range; assert(subtype == VIRTIO_IOMMU_RESV_MEM_T_RESERVED || subtype == VIRTIO_IOMMU_RESV_MEM_T_MSI); prop.head.type = cpu_to_le16(VIRTIO_IOMMU_PROBE_T_RESV_MEM); prop.head.length = cpu_to_le16(length); prop.subtype = subtype; - prop.start = cpu_to_le64(range_lob(&s->reserved_regions[i].range)); - prop.end = cpu_to_le64(range_upb(&s->reserved_regions[i].range)); + prop.start = cpu_to_le64(range_lob(range)); + prop.end = cpu_to_le64(range_upb(range)); memcpy(buf, &prop, size); @@ -894,8 +895,8 @@ static IOMMUTLBEntry virtio_iommu_translate(IOMMUMemoryRegion *mr, hwaddr addr, goto unlock; } - for (i = 0; i < s->nb_reserved_regions; i++) { - ReservedRegion *reg = &s->reserved_regions[i]; + for (i = 0; i < s->nr_prop_resv_regions; i++) { + ReservedRegion *reg = &s->prop_resv_regions[i]; if (range_contains(®->range, addr)) { switch (reg->type) { diff --git a/include/hw/virtio/virtio-iommu.h b/include/hw/virtio/virtio-iommu.h index a93fc5383e..eea4564782 100644 --- a/include/hw/virtio/virtio-iommu.h +++ b/include/hw/virtio/virtio-iommu.h @@ -55,8 +55,8 @@ struct VirtIOIOMMU { GHashTable *as_by_busptr; IOMMUPciBus *iommu_pcibus_by_bus_num[PCI_BUS_MAX]; PCIBus *primary_bus; - ReservedRegion *reserved_regions; - uint32_t nb_reserved_regions; + ReservedRegion *prop_resv_regions; + uint32_t nr_prop_resv_regions; GTree *domains; QemuRecMutex mutex; GTree *endpoints; From 43f04cbeff863ae68b6ead432af5e771b92b934c Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Thu, 19 Oct 2023 15:45:11 +0200 Subject: [PATCH 402/974] range: Make range_compare() public MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let's expose range_compare() in the header so that it can be reused outside of util/range.c Signed-off-by: Eric Auger Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Cédric Le Goater --- include/qemu/range.h | 6 ++++++ util/range.c | 6 +----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/include/qemu/range.h b/include/qemu/range.h index 7e2b1cc447..aa671da143 100644 --- a/include/qemu/range.h +++ b/include/qemu/range.h @@ -217,6 +217,12 @@ static inline int ranges_overlap(uint64_t first1, uint64_t len1, return !(last2 < first1 || last1 < first2); } +/* + * Return -1 if @a < @b, 1 @a > @b, and 0 if they touch or overlap. + * Both @a and @b must not be empty. + */ +int range_compare(Range *a, Range *b); + GList *range_list_insert(GList *list, Range *data); #endif diff --git a/util/range.c b/util/range.c index 098d9d2dc0..782cb8b21c 100644 --- a/util/range.c +++ b/util/range.c @@ -20,11 +20,7 @@ #include "qemu/osdep.h" #include "qemu/range.h" -/* - * Return -1 if @a < @b, 1 @a > @b, and 0 if they touch or overlap. - * Both @a and @b must not be empty. - */ -static inline int range_compare(Range *a, Range *b) +int range_compare(Range *a, Range *b) { assert(!range_is_empty(a) && !range_is_empty(b)); From c3104847363f4ac5d4e76e8ed637280f7be1ee68 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Thu, 19 Oct 2023 15:45:12 +0200 Subject: [PATCH 403/974] util/reserved-region: Add new ReservedRegion helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce resv_region_list_insert() helper which inserts a new ReservedRegion into a sorted list of reserved region. In case of overlap, the new region has higher priority and hides the existing overlapped segments. If the overlap is partial, new regions are created for parts which are not overlapped. The new region has higher priority independently on the type of the regions. Signed-off-by: Eric Auger Reviewed-by: Jean-Philippe Brucker Tested-by: Yanghang Liu Signed-off-by: Cédric Le Goater --- include/qemu/reserved-region.h | 32 ++++++++++++ util/meson.build | 1 + util/reserved-region.c | 91 ++++++++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+) create mode 100644 include/qemu/reserved-region.h create mode 100644 util/reserved-region.c diff --git a/include/qemu/reserved-region.h b/include/qemu/reserved-region.h new file mode 100644 index 0000000000..8e6f0a97e2 --- /dev/null +++ b/include/qemu/reserved-region.h @@ -0,0 +1,32 @@ +/* + * QEMU ReservedRegion helpers + * + * Copyright (c) 2023 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef QEMU_RESERVED_REGION_H +#define QEMU_RESERVED_REGION_H + +#include "exec/memory.h" + +/* + * Insert a new region into a sorted list of reserved regions. In case + * there is overlap with existing regions, the new added region has + * higher priority and replaces the overlapped segment. + */ +GList *resv_region_list_insert(GList *list, ReservedRegion *reg); + +#endif diff --git a/util/meson.build b/util/meson.build index 769b24f2e0..c3a24af5c5 100644 --- a/util/meson.build +++ b/util/meson.build @@ -52,6 +52,7 @@ util_ss.add(files('qdist.c')) util_ss.add(files('qht.c')) util_ss.add(files('qsp.c')) util_ss.add(files('range.c')) +util_ss.add(files('reserved-region.c')) util_ss.add(files('stats64.c')) util_ss.add(files('systemd.c')) util_ss.add(files('transactions.c')) diff --git a/util/reserved-region.c b/util/reserved-region.c new file mode 100644 index 0000000000..18f83eb4c6 --- /dev/null +++ b/util/reserved-region.c @@ -0,0 +1,91 @@ +/* + * QEMU ReservedRegion helpers + * + * Copyright (c) 2023 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/range.h" +#include "qemu/reserved-region.h" + +GList *resv_region_list_insert(GList *list, ReservedRegion *reg) +{ + ReservedRegion *resv_iter, *new_reg; + Range *r = ®->range; + Range *range_iter; + GList *l; + + for (l = list; l ; ) { + resv_iter = (ReservedRegion *)l->data; + range_iter = &resv_iter->range; + + /* Skip all list elements strictly less than range to add */ + if (range_compare(range_iter, r) < 0) { + l = l->next; + } else if (range_compare(range_iter, r) > 0) { + return g_list_insert_before(list, l, reg); + } else { /* there is an overlap */ + if (range_contains_range(r, range_iter)) { + /* new range contains current item, simply remove this latter */ + GList *prev = l->prev; + g_free(l->data); + list = g_list_delete_link(list, l); + if (prev) { + l = prev->next; + } else { + l = list; + } + } else if (range_contains_range(range_iter, r)) { + /* new region is included in the current region */ + if (range_lob(range_iter) == range_lob(r)) { + /* adjacent on the left side, derives into 2 regions */ + range_set_bounds(range_iter, range_upb(r) + 1, + range_upb(range_iter)); + return g_list_insert_before(list, l, reg); + } else if (range_upb(range_iter) == range_upb(r)) { + /* adjacent on the right side, derives into 2 regions */ + range_set_bounds(range_iter, range_lob(range_iter), + range_lob(r) - 1); + l = l->next; + } else { + uint64_t lob = range_lob(range_iter); + /* + * the new range is in the middle of an existing one, + * split this latter into 3 regs instead + */ + range_set_bounds(range_iter, range_upb(r) + 1, + range_upb(range_iter)); + new_reg = g_new0(ReservedRegion, 1); + new_reg->type = resv_iter->type; + range_set_bounds(&new_reg->range, + lob, range_lob(r) - 1); + list = g_list_insert_before(list, l, new_reg); + return g_list_insert_before(list, l, reg); + } + } else if (range_lob(r) < range_lob(range_iter)) { + range_set_bounds(range_iter, range_upb(r) + 1, + range_upb(range_iter)); + return g_list_insert_before(list, l, reg); + } else { /* intersection on the upper range */ + range_set_bounds(range_iter, range_lob(range_iter), + range_lob(r) - 1); + l = l->next; + } + } /* overlap */ + } + return g_list_append(list, reg); +} + From 908cae0de4fd63a58f5a7dc447f843a5be9cff46 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Thu, 19 Oct 2023 15:45:13 +0200 Subject: [PATCH 404/974] virtio-iommu: Introduce per IOMMUDevice reserved regions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For the time being the per device reserved regions are just a duplicate of IOMMU wide reserved regions. Subsequent patches will combine those with host reserved regions, if any. Signed-off-by: Eric Auger Tested-by: Yanghang Liu Reviewed-by: "Michael S. Tsirkin" Signed-off-by: Cédric Le Goater --- hw/virtio/virtio-iommu.c | 37 +++++++++++++++++++++++++------- include/hw/virtio/virtio-iommu.h | 1 + 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index 979cdb5648..0e2370663d 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -26,6 +26,7 @@ #include "sysemu/kvm.h" #include "sysemu/reset.h" #include "sysemu/sysemu.h" +#include "qemu/reserved-region.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "trace.h" @@ -378,6 +379,19 @@ static void virtio_iommu_put_domain(gpointer data) g_free(domain); } +static void add_prop_resv_regions(IOMMUDevice *sdev) +{ + VirtIOIOMMU *s = sdev->viommu; + int i; + + for (i = 0; i < s->nr_prop_resv_regions; i++) { + ReservedRegion *reg = g_new0(ReservedRegion, 1); + + *reg = s->prop_resv_regions[i]; + sdev->resv_regions = resv_region_list_insert(sdev->resv_regions, reg); + } +} + static AddressSpace *virtio_iommu_find_add_as(PCIBus *bus, void *opaque, int devfn) { @@ -408,6 +422,7 @@ static AddressSpace *virtio_iommu_find_add_as(PCIBus *bus, void *opaque, memory_region_init(&sdev->root, OBJECT(s), name, UINT64_MAX); address_space_init(&sdev->as, &sdev->root, TYPE_VIRTIO_IOMMU); + add_prop_resv_regions(sdev); /* * Build the IOMMU disabled container with aliases to the @@ -629,17 +644,23 @@ static ssize_t virtio_iommu_fill_resv_mem_prop(VirtIOIOMMU *s, uint32_t ep, { struct virtio_iommu_probe_resv_mem prop = {}; size_t size = sizeof(prop), length = size - sizeof(prop.head), total; - int i; + IOMMUDevice *sdev; + GList *l; - total = size * s->nr_prop_resv_regions; + sdev = container_of(virtio_iommu_mr(s, ep), IOMMUDevice, iommu_mr); + if (!sdev) { + return -EINVAL; + } + total = size * g_list_length(sdev->resv_regions); if (total > free) { return -ENOSPC; } - for (i = 0; i < s->nr_prop_resv_regions; i++) { - unsigned subtype = s->prop_resv_regions[i].type; - Range *range = &s->prop_resv_regions[i].range; + for (l = sdev->resv_regions; l; l = l->next) { + ReservedRegion *reg = l->data; + unsigned subtype = reg->type; + Range *range = ®->range; assert(subtype == VIRTIO_IOMMU_RESV_MEM_T_RESERVED || subtype == VIRTIO_IOMMU_RESV_MEM_T_MSI); @@ -857,7 +878,7 @@ static IOMMUTLBEntry virtio_iommu_translate(IOMMUMemoryRegion *mr, hwaddr addr, bool bypass_allowed; int granule; bool found; - int i; + GList *l; interval.low = addr; interval.high = addr + 1; @@ -895,8 +916,8 @@ static IOMMUTLBEntry virtio_iommu_translate(IOMMUMemoryRegion *mr, hwaddr addr, goto unlock; } - for (i = 0; i < s->nr_prop_resv_regions; i++) { - ReservedRegion *reg = &s->prop_resv_regions[i]; + for (l = sdev->resv_regions; l; l = l->next) { + ReservedRegion *reg = l->data; if (range_contains(®->range, addr)) { switch (reg->type) { diff --git a/include/hw/virtio/virtio-iommu.h b/include/hw/virtio/virtio-iommu.h index eea4564782..70b8ace34d 100644 --- a/include/hw/virtio/virtio-iommu.h +++ b/include/hw/virtio/virtio-iommu.h @@ -39,6 +39,7 @@ typedef struct IOMMUDevice { AddressSpace as; MemoryRegion root; /* The root container of the device */ MemoryRegion bypass_mr; /* The alias of shared memory MR */ + GList *resv_regions; } IOMMUDevice; typedef struct IOMMUPciBus { From b439595a08d79120325de4684698bb7b6516aa8a Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Thu, 19 Oct 2023 15:45:14 +0200 Subject: [PATCH 405/974] range: Introduce range_inverse_array() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This helper reverses a list of regions within a [low, high] span, turning original regions into holes and original holes into actual regions, covering the whole UINT64_MAX span. Signed-off-by: Eric Auger Tested-by: Yanghang Liu Reviewed-by: "Michael S. Tsirkin" Signed-off-by: Cédric Le Goater --- include/qemu/range.h | 8 +++++++ util/range.c | 55 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/include/qemu/range.h b/include/qemu/range.h index aa671da143..205e1da76d 100644 --- a/include/qemu/range.h +++ b/include/qemu/range.h @@ -225,4 +225,12 @@ int range_compare(Range *a, Range *b); GList *range_list_insert(GList *list, Range *data); +/* + * Inverse an array of sorted ranges over the [low, high] span, ie. + * original ranges becomes holes in the newly allocated inv_ranges + */ +void range_inverse_array(GList *in_ranges, + GList **out_ranges, + uint64_t low, uint64_t high); + #endif diff --git a/util/range.c b/util/range.c index 782cb8b21c..9605ccfcbe 100644 --- a/util/range.c +++ b/util/range.c @@ -66,3 +66,58 @@ GList *range_list_insert(GList *list, Range *data) return list; } + +static inline +GList *append_new_range(GList *list, uint64_t lob, uint64_t upb) +{ + Range *new = g_new0(Range, 1); + + range_set_bounds(new, lob, upb); + return g_list_append(list, new); +} + + +void range_inverse_array(GList *in, GList **rev, + uint64_t low, uint64_t high) +{ + Range *r, *rn; + GList *l = in, *out = *rev; + + for (l = in; l && range_upb(l->data) < low; l = l->next) { + continue; + } + + if (!l) { + out = append_new_range(out, low, high); + goto exit; + } + r = (Range *)l->data; + + /* first range lob is greater than min, insert a first range */ + if (range_lob(r) > low) { + out = append_new_range(out, low, MIN(range_lob(r) - 1, high)); + } + + /* insert a range inbetween each original range until we reach high */ + for (; l->next; l = l->next) { + r = (Range *)l->data; + rn = (Range *)l->next->data; + if (range_lob(r) >= high) { + goto exit; + } + if (range_compare(r, rn)) { + out = append_new_range(out, range_upb(r) + 1, + MIN(range_lob(rn) - 1, high)); + } + } + + /* last range */ + r = (Range *)l->data; + + /* last range upb is less than max, insert a last range */ + if (range_upb(r) < high) { + out = append_new_range(out, range_upb(r) + 1, high); + } +exit: + *rev = out; +} From 09b4c3d6a2f098e64cc25aa63f388ea943990279 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Thu, 19 Oct 2023 15:45:15 +0200 Subject: [PATCH 406/974] virtio-iommu: Record whether a probe request has been issued MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an IOMMUDevice 'probe_done' flag to record that the driver already issued a probe request on that device. This will be useful to double check host reserved regions aren't notified after the probe and hence are not taken into account by the driver. Signed-off-by: Eric Auger Suggested-by: Jean-Philippe Brucker Reviewed-by: "Michael S. Tsirkin" Tested-by: Yanghang Liu Signed-off-by: Cédric Le Goater --- hw/virtio/virtio-iommu.c | 20 +++++++++++--------- include/hw/virtio/virtio-iommu.h | 1 + 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index 0e2370663d..13c3c087fe 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -639,19 +639,13 @@ static int virtio_iommu_unmap(VirtIOIOMMU *s, return ret; } -static ssize_t virtio_iommu_fill_resv_mem_prop(VirtIOIOMMU *s, uint32_t ep, +static ssize_t virtio_iommu_fill_resv_mem_prop(IOMMUDevice *sdev, uint32_t ep, uint8_t *buf, size_t free) { struct virtio_iommu_probe_resv_mem prop = {}; size_t size = sizeof(prop), length = size - sizeof(prop.head), total; - IOMMUDevice *sdev; GList *l; - sdev = container_of(virtio_iommu_mr(s, ep), IOMMUDevice, iommu_mr); - if (!sdev) { - return -EINVAL; - } - total = size * g_list_length(sdev->resv_regions); if (total > free) { return -ENOSPC; @@ -688,19 +682,27 @@ static int virtio_iommu_probe(VirtIOIOMMU *s, uint8_t *buf) { uint32_t ep_id = le32_to_cpu(req->endpoint); + IOMMUMemoryRegion *iommu_mr = virtio_iommu_mr(s, ep_id); size_t free = VIOMMU_PROBE_SIZE; + IOMMUDevice *sdev; ssize_t count; - if (!virtio_iommu_mr(s, ep_id)) { + if (!iommu_mr) { return VIRTIO_IOMMU_S_NOENT; } - count = virtio_iommu_fill_resv_mem_prop(s, ep_id, buf, free); + sdev = container_of(iommu_mr, IOMMUDevice, iommu_mr); + if (!sdev) { + return -EINVAL; + } + + count = virtio_iommu_fill_resv_mem_prop(sdev, ep_id, buf, free); if (count < 0) { return VIRTIO_IOMMU_S_INVAL; } buf += count; free -= count; + sdev->probe_done = true; return VIRTIO_IOMMU_S_OK; } diff --git a/include/hw/virtio/virtio-iommu.h b/include/hw/virtio/virtio-iommu.h index 70b8ace34d..1dd11ae81a 100644 --- a/include/hw/virtio/virtio-iommu.h +++ b/include/hw/virtio/virtio-iommu.h @@ -40,6 +40,7 @@ typedef struct IOMMUDevice { MemoryRegion root; /* The root container of the device */ MemoryRegion bypass_mr; /* The alias of shared memory MR */ GList *resv_regions; + bool probe_done; } IOMMUDevice; typedef struct IOMMUPciBus { From 30d40e39bdcb50e67f7cca7bee8bf59234c4ec12 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Thu, 19 Oct 2023 15:45:16 +0200 Subject: [PATCH 407/974] virtio-iommu: Implement set_iova_ranges() callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The implementation populates the array of per IOMMUDevice host reserved ranges. It is forbidden to have conflicting sets of host IOVA ranges to be applied onto the same IOMMU MR (implied by different host devices). In case the callback is called after the probe request has been issues by the driver, a warning is issued. Signed-off-by: Eric Auger Reviewed-by: "Michael S. Tsirkin" Tested-by: Yanghang Liu Signed-off-by: Cédric Le Goater --- hw/virtio/virtio-iommu.c | 67 ++++++++++++++++++++++++++++++++ include/hw/virtio/virtio-iommu.h | 1 + 2 files changed, 68 insertions(+) diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index 13c3c087fe..15aadd6fdd 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "qemu/iov.h" +#include "qemu/range.h" #include "exec/target_page.h" #include "hw/qdev-properties.h" #include "hw/virtio/virtio.h" @@ -1155,6 +1156,71 @@ static int virtio_iommu_set_page_size_mask(IOMMUMemoryRegion *mr, return 0; } +/** + * virtio_iommu_set_iova_ranges: Conveys the usable IOVA ranges + * + * The function turns those into reserved ranges. Once some + * reserved ranges have been set, new reserved regions cannot be + * added outside of the original ones. + * + * @mr: IOMMU MR + * @iova_ranges: list of usable IOVA ranges + * @errp: error handle + */ +static int virtio_iommu_set_iova_ranges(IOMMUMemoryRegion *mr, + GList *iova_ranges, + Error **errp) +{ + IOMMUDevice *sdev = container_of(mr, IOMMUDevice, iommu_mr); + GList *current_ranges = sdev->host_resv_ranges; + GList *l, *tmp, *new_ranges = NULL; + int ret = -EINVAL; + + /* check that each new resv region is included in an existing one */ + if (sdev->host_resv_ranges) { + range_inverse_array(iova_ranges, + &new_ranges, + 0, UINT64_MAX); + + for (tmp = new_ranges; tmp; tmp = tmp->next) { + Range *newr = (Range *)tmp->data; + bool included = false; + + for (l = current_ranges; l; l = l->next) { + Range * r = (Range *)l->data; + + if (range_contains_range(r, newr)) { + included = true; + break; + } + } + if (!included) { + goto error; + } + } + /* all new reserved ranges are included in existing ones */ + ret = 0; + goto out; + } + + if (sdev->probe_done) { + warn_report("%s: Notified about new host reserved regions after probe", + mr->parent_obj.name); + } + + range_inverse_array(iova_ranges, + &sdev->host_resv_ranges, + 0, UINT64_MAX); + + return 0; +error: + error_setg(errp, "IOMMU mr=%s Conflicting host reserved ranges set!", + mr->parent_obj.name); +out: + g_list_free_full(new_ranges, g_free); + return ret; +} + static void virtio_iommu_system_reset(void *opaque) { VirtIOIOMMU *s = opaque; @@ -1450,6 +1516,7 @@ static void virtio_iommu_memory_region_class_init(ObjectClass *klass, imrc->replay = virtio_iommu_replay; imrc->notify_flag_changed = virtio_iommu_notify_flag_changed; imrc->iommu_set_page_size_mask = virtio_iommu_set_page_size_mask; + imrc->iommu_set_iova_ranges = virtio_iommu_set_iova_ranges; } static const TypeInfo virtio_iommu_info = { diff --git a/include/hw/virtio/virtio-iommu.h b/include/hw/virtio/virtio-iommu.h index 1dd11ae81a..781ebaea8f 100644 --- a/include/hw/virtio/virtio-iommu.h +++ b/include/hw/virtio/virtio-iommu.h @@ -40,6 +40,7 @@ typedef struct IOMMUDevice { MemoryRegion root; /* The root container of the device */ MemoryRegion bypass_mr; /* The alias of shared memory MR */ GList *resv_regions; + GList *host_resv_ranges; bool probe_done; } IOMMUDevice; From 5c476ba3fad3b7659988f28aae8b1b8d5331effc Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Thu, 19 Oct 2023 15:45:17 +0200 Subject: [PATCH 408/974] virtio-iommu: Consolidate host reserved regions and property set ones MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up to now we were exposing to the RESV_MEM probe requests the reserved memory regions set though the reserved-regions array property. Combine those with the host reserved memory regions if any. Those latter are tagged as RESERVED. We don't have more information about them besides then cannot be mapped. Reserved regions set by property have higher priority. Signed-off-by: Eric Auger Reviewed-by: "Michael S. Tsirkin" Tested-by: Yanghang Liu Signed-off-by: Cédric Le Goater --- hw/virtio/trace-events | 1 + hw/virtio/virtio-iommu.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index 0af7a2886c..637cac4edf 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -135,6 +135,7 @@ virtio_iommu_notify_flag_add(const char *name) "add notifier to mr %s" virtio_iommu_notify_flag_del(const char *name) "del notifier from mr %s" virtio_iommu_switch_address_space(uint8_t bus, uint8_t slot, uint8_t fn, bool on) "Device %02x:%02x.%x switching address space (iommu enabled=%d)" virtio_iommu_freeze_granule(uint64_t page_size_mask) "granule set to 0x%"PRIx64 +virtio_iommu_host_resv_regions(const char *name, uint32_t index, uint64_t lob, uint64_t upb) "mr=%s host-resv-reg[%d] = [0x%"PRIx64",0x%"PRIx64"]" # virtio-mem.c virtio_mem_send_response(uint16_t type) "type=%" PRIu16 diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index 15aadd6fdd..dede0d41aa 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -21,6 +21,7 @@ #include "qemu/log.h" #include "qemu/iov.h" #include "qemu/range.h" +#include "qemu/reserved-region.h" #include "exec/target_page.h" #include "hw/qdev-properties.h" #include "hw/virtio/virtio.h" @@ -1156,6 +1157,40 @@ static int virtio_iommu_set_page_size_mask(IOMMUMemoryRegion *mr, return 0; } +/** + * rebuild_resv_regions: rebuild resv regions with both the + * info of host resv ranges and property set resv ranges + */ +static int rebuild_resv_regions(IOMMUDevice *sdev) +{ + GList *l; + int i = 0; + + /* free the existing list and rebuild it from scratch */ + g_list_free_full(sdev->resv_regions, g_free); + sdev->resv_regions = NULL; + + /* First add host reserved regions if any, all tagged as RESERVED */ + for (l = sdev->host_resv_ranges; l; l = l->next) { + ReservedRegion *reg = g_new0(ReservedRegion, 1); + Range *r = (Range *)l->data; + + reg->type = VIRTIO_IOMMU_RESV_MEM_T_RESERVED; + range_set_bounds(®->range, range_lob(r), range_upb(r)); + sdev->resv_regions = resv_region_list_insert(sdev->resv_regions, reg); + trace_virtio_iommu_host_resv_regions(sdev->iommu_mr.parent_obj.name, i, + range_lob(®->range), + range_upb(®->range)); + i++; + } + /* + * then add higher priority reserved regions set by the machine + * through properties + */ + add_prop_resv_regions(sdev); + return 0; +} + /** * virtio_iommu_set_iova_ranges: Conveys the usable IOVA ranges * @@ -1211,6 +1246,7 @@ static int virtio_iommu_set_iova_ranges(IOMMUMemoryRegion *mr, range_inverse_array(iova_ranges, &sdev->host_resv_ranges, 0, UINT64_MAX); + rebuild_resv_regions(sdev); return 0; error: From 71177490a87c08e7ac53ab00bad06d68f439a4cd Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Thu, 19 Oct 2023 15:45:18 +0200 Subject: [PATCH 409/974] test: Add some tests for range and resv-mem helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add unit tests for both resv_region_list_insert() and range_inverse_array(). Signed-off-by: Eric Auger Reviewed-by: "Michael S. Tsirkin" [ clg: Removal of unused variable in compare_ranges() ] Signed-off-by: Cédric Le Goater --- tests/unit/meson.build | 1 + tests/unit/test-resv-mem.c | 316 +++++++++++++++++++++++++++++++++++++ 2 files changed, 317 insertions(+) create mode 100644 tests/unit/test-resv-mem.c diff --git a/tests/unit/meson.build b/tests/unit/meson.build index f33ae64b8d..e6c51e7a86 100644 --- a/tests/unit/meson.build +++ b/tests/unit/meson.build @@ -21,6 +21,7 @@ tests = { 'test-opts-visitor': [testqapi], 'test-visitor-serialization': [testqapi], 'test-bitmap': [], + 'test-resv-mem': [], # all code tested by test-x86-cpuid is inside topology.h 'test-x86-cpuid': [], 'test-cutils': [], diff --git a/tests/unit/test-resv-mem.c b/tests/unit/test-resv-mem.c new file mode 100644 index 0000000000..5963274e2c --- /dev/null +++ b/tests/unit/test-resv-mem.c @@ -0,0 +1,316 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * reserved-region/range.c unit-tests. + * + * Copyright (C) 2023, Red Hat, Inc. + * + * Author: Eric Auger + */ + +#include "qemu/osdep.h" +#include "qemu/range.h" +#include "exec/memory.h" +#include "qemu/reserved-region.h" + +#define DEBUG 0 + +#if DEBUG +static void print_ranges(const char *prefix, GList *ranges) +{ + GList *l; + int i = 0; + + if (!g_list_length(ranges)) { + printf("%s is void\n", prefix); + return; + } + for (l = ranges; l; l = l->next) { + Range *r = (Range *)l->data; + + printf("%s rev[%i] = [0x%"PRIx64",0x%"PRIx64"]\n", + prefix, i, range_lob(r), range_upb(r)); + i++; + } +} +#endif + +static void compare_ranges(const char *prefix, GList *ranges, + GList *expected) +{ + GList *l, *e; + +#if DEBUG + print_ranges("out", ranges); + print_ranges("expected", expected); +#endif + g_assert_cmpint(g_list_length(ranges), ==, g_list_length(expected)); + for (l = ranges, e = expected; l ; l = l->next, e = e->next) { + Range *r = (Range *)l->data; + Range *er = (Range *)e->data; + + g_assert_true(range_lob(r) == range_lob(er) && + range_upb(r) == range_upb(er)); + } +} + +static GList *insert_sorted_range(GList *list, uint64_t lob, uint64_t upb) +{ + Range *new = g_new0(Range, 1); + + range_set_bounds(new, lob, upb); + return range_list_insert(list, new); +} + +static void reset(GList **in, GList **out, GList **expected) +{ + g_list_free_full(*in, g_free); + g_list_free_full(*out, g_free); + g_list_free_full(*expected, g_free); + *in = NULL; + *out = NULL; + *expected = NULL; +} + +static void +run_range_inverse_array(const char *prefix, GList **in, GList **expected, + uint64_t low, uint64_t high) +{ + GList *out = NULL; + range_inverse_array(*in, &out, low, high); + compare_ranges(prefix, out, *expected); + reset(in, &out, expected); +} + +static void check_range_reverse_array(void) +{ + GList *in = NULL, *expected = NULL; + + /* test 1 */ + + in = insert_sorted_range(in, 0x10000, UINT64_MAX); + expected = insert_sorted_range(expected, 0x0, 0xFFFF); + run_range_inverse_array("test1", &in, &expected, 0x0, UINT64_MAX); + + /* test 2 */ + + in = insert_sorted_range(in, 0x10000, 0xFFFFFFFFFFFF); + expected = insert_sorted_range(expected, 0x0, 0xFFFF); + expected = insert_sorted_range(expected, 0x1000000000000, UINT64_MAX); + run_range_inverse_array("test1", &in, &expected, 0x0, UINT64_MAX); + + /* test 3 */ + + in = insert_sorted_range(in, 0x0, 0xFFFF); + in = insert_sorted_range(in, 0x10000, 0x2FFFF); + expected = insert_sorted_range(expected, 0x30000, UINT64_MAX); + run_range_inverse_array("test1", &in, &expected, 0x0, UINT64_MAX); + + /* test 4 */ + + in = insert_sorted_range(in, 0x50000, 0x5FFFF); + in = insert_sorted_range(in, 0x60000, 0xFFFFFFFFFFFF); + expected = insert_sorted_range(expected, 0x0, 0x4FFFF); + expected = insert_sorted_range(expected, 0x1000000000000, UINT64_MAX); + run_range_inverse_array("test1", &in, &expected, 0x0, UINT64_MAX); + + /* test 5 */ + + in = insert_sorted_range(in, 0x0, UINT64_MAX); + run_range_inverse_array("test1", &in, &expected, 0x0, UINT64_MAX); + + /* test 6 */ + in = insert_sorted_range(in, 0x10000, 0x1FFFF); + in = insert_sorted_range(in, 0x30000, 0x6FFFF); + in = insert_sorted_range(in, 0x90000, UINT64_MAX); + expected = insert_sorted_range(expected, 0x0, 0xFFFF); + expected = insert_sorted_range(expected, 0x20000, 0x2FFFF); + expected = insert_sorted_range(expected, 0x70000, 0x8FFFF); + run_range_inverse_array("test1", &in, &expected, 0x0, UINT64_MAX); +} + +static void check_range_reverse_array_low_end(void) +{ + GList *in = NULL, *expected = NULL; + + /* test 1 */ + in = insert_sorted_range(in, 0x0, UINT64_MAX); + run_range_inverse_array("test1", &in, &expected, 0x10000, 0xFFFFFF); + + /* test 2 */ + + in = insert_sorted_range(in, 0x0, 0xFFFF); + in = insert_sorted_range(in, 0x20000, 0x2FFFF); + expected = insert_sorted_range(expected, 0x40000, 0xFFFFFFFFFFFF); + run_range_inverse_array("test2", &in, &expected, 0x40000, 0xFFFFFFFFFFFF); + + /* test 3 */ + in = insert_sorted_range(in, 0x0, 0xFFFF); + in = insert_sorted_range(in, 0x20000, 0x2FFFF); + in = insert_sorted_range(in, 0x1000000000000, UINT64_MAX); + expected = insert_sorted_range(expected, 0x40000, 0xFFFFFFFFFFFF); + run_range_inverse_array("test3", &in, &expected, 0x40000, 0xFFFFFFFFFFFF); + + /* test 4 */ + + in = insert_sorted_range(in, 0x0, 0xFFFF); + in = insert_sorted_range(in, 0x20000, 0x2FFFF); + in = insert_sorted_range(in, 0x1000000000000, UINT64_MAX); + expected = insert_sorted_range(expected, 0x30000, 0xFFFFFFFFFFFF); + run_range_inverse_array("test4", &in, &expected, 0x20000, 0xFFFFFFFFFFFF); + + /* test 5 */ + + in = insert_sorted_range(in, 0x2000, 0xFFFF); + in = insert_sorted_range(in, 0x20000, 0x2FFFF); + in = insert_sorted_range(in, 0x100000000, 0x1FFFFFFFF); + expected = insert_sorted_range(expected, 0x1000, 0x1FFF); + expected = insert_sorted_range(expected, 0x10000, 0x1FFFF); + expected = insert_sorted_range(expected, 0x30000, 0xFFFFFFFF); + expected = insert_sorted_range(expected, 0x200000000, 0xFFFFFFFFFFFF); + run_range_inverse_array("test5", &in, &expected, 0x1000, 0xFFFFFFFFFFFF); + + /* test 6 */ + + in = insert_sorted_range(in, 0x10000000 , 0x1FFFFFFF); + in = insert_sorted_range(in, 0x100000000, 0x1FFFFFFFF); + expected = insert_sorted_range(expected, 0x0, 0xFFFF); + run_range_inverse_array("test6", &in, &expected, 0x0, 0xFFFF); +} + +static ReservedRegion *alloc_resv_mem(unsigned type, uint64_t lob, uint64_t upb) +{ + ReservedRegion *r; + + r = g_new0(ReservedRegion, 1); + r->type = type; + range_set_bounds(&r->range, lob, upb); + return r; +} + +static void print_resv_region_list(const char *prefix, GList *list, + uint32_t expected_length) +{ + int i = g_list_length(list); + + g_assert_cmpint(i, ==, expected_length); +#if DEBUG + i = 0; + for (GList *l = list; l; l = l->next) { + ReservedRegion *r = (ReservedRegion *)l->data; + Range *range = &r->range; + + printf("%s item[%d]=[0x%x, 0x%"PRIx64", 0x%"PRIx64"]\n", + prefix, i++, r->type, range_lob(range), range_upb(range)); + } +#endif +} + +static void free_resv_region(gpointer data) +{ + ReservedRegion *reg = (ReservedRegion *)data; + + g_free(reg); +} + +static void check_resv_region_list_insert(void) +{ + ReservedRegion *r[10]; + GList *l = NULL; + + r[0] = alloc_resv_mem(0xA, 0, 0xFFFF); + r[1] = alloc_resv_mem(0xA, 0x20000, 0x2FFFF); + l = resv_region_list_insert(l, r[0]); + l = resv_region_list_insert(l, r[1]); + print_resv_region_list("test1", l, 2); + + /* adjacent on left */ + r[2] = alloc_resv_mem(0xB, 0x0, 0xFFF); + l = resv_region_list_insert(l, r[2]); + /* adjacent on right */ + r[3] = alloc_resv_mem(0xC, 0x21000, 0x2FFFF); + l = resv_region_list_insert(l, r[3]); + print_resv_region_list("test2", l, 4); + + /* exact overlap of D into C*/ + r[4] = alloc_resv_mem(0xD, 0x21000, 0x2FFFF); + l = resv_region_list_insert(l, r[4]); + print_resv_region_list("test3", l, 4); + + /* in the middle */ + r[5] = alloc_resv_mem(0xE, 0x22000, 0x23FFF); + l = resv_region_list_insert(l, r[5]); + print_resv_region_list("test4", l, 6); + + /* overwrites several existing ones */ + r[6] = alloc_resv_mem(0xF, 0x10000, 0x2FFFF); + l = resv_region_list_insert(l, r[6]); + print_resv_region_list("test5", l, 3); + + /* contiguous at the end */ + r[7] = alloc_resv_mem(0x0, 0x30000, 0x40000); + l = resv_region_list_insert(l, r[7]); + print_resv_region_list("test6", l, 4); + + g_list_free_full(l, free_resv_region); + l = NULL; + + r[0] = alloc_resv_mem(0x0, 0x10000, 0x1FFFF); + l = resv_region_list_insert(l, r[0]); + /* insertion before the 1st item */ + r[1] = alloc_resv_mem(0x1, 0x0, 0xFF); + l = resv_region_list_insert(l, r[1]); + print_resv_region_list("test8", l, 2); + + /* collision on the left side */ + r[2] = alloc_resv_mem(0xA, 0x1200, 0x11FFF); + l = resv_region_list_insert(l, r[2]); + print_resv_region_list("test9", l, 3); + + /* collision on the right side */ + r[3] = alloc_resv_mem(0xA, 0x1F000, 0x2FFFF); + l = resv_region_list_insert(l, r[3]); + print_resv_region_list("test10", l, 4); + + /* override everything */ + r[4] = alloc_resv_mem(0xF, 0x0, UINT64_MAX); + l = resv_region_list_insert(l, r[4]); + print_resv_region_list("test11", l, 1); + + g_list_free_full(l, free_resv_region); + l = NULL; + + r[0] = alloc_resv_mem(0xF, 0x1000000000000, UINT64_MAX); + l = resv_region_list_insert(l, r[0]); + print_resv_region_list("test12", l, 1); + + r[1] = alloc_resv_mem(0xA, 0x0, 0xFFFFFFF); + l = resv_region_list_insert(l, r[1]); + print_resv_region_list("test12", l, 2); + + r[2] = alloc_resv_mem(0xB, 0x100000000, 0x1FFFFFFFF); + l = resv_region_list_insert(l, r[2]); + print_resv_region_list("test12", l, 3); + + r[3] = alloc_resv_mem(0x0, 0x010000000, 0x2FFFFFFFF); + l = resv_region_list_insert(l, r[3]); + print_resv_region_list("test12", l, 3); + + g_list_free_full(l, free_resv_region); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/resv-mem/range_reverse_array", + check_range_reverse_array); + g_test_add_func("/resv-mem/range_reverse_array_low_end", + check_range_reverse_array_low_end); + g_test_add_func("/resv-mem/resv_region_list_insert", + check_resv_region_list_insert); + + g_test_run(); + + return 0; +} From ba7d12eb8ce2d7367615071c0569947457d36803 Mon Sep 17 00:00:00 2001 From: Yi Liu Date: Tue, 17 Oct 2023 18:14:04 +0200 Subject: [PATCH 410/974] hw/pci: modify pci_setup_iommu() to set PCIIOMMUOps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch modifies pci_setup_iommu() to set PCIIOMMUOps instead of setting PCIIOMMUFunc. PCIIOMMUFunc is used to get an address space for a PCI device in vendor specific way. The PCIIOMMUOps still offers this functionality. But using PCIIOMMUOps leaves space to add more iommu related vendor specific operations. Cc: Kevin Tian Cc: Jacob Pan Cc: Peter Xu Cc: Eric Auger Cc: Yi Sun Cc: David Gibson Cc: "Michael S. Tsirkin" Cc: Eric Auger Cc: Peter Maydell Cc: Paolo Bonzini Cc: Peter Xu Cc: Jason Wang Cc: Andrey Smirnov Cc: Helge Deller Cc: Hervé Poussineau Cc: Mark Cave-Ayland Cc: BALATON Zoltan Cc: Elena Ufimtseva Cc: Jagannathan Raman Cc: Matthew Rosato Cc: Eric Farman Cc: Halil Pasic Cc: Christian Borntraeger Cc: Thomas Huth Cc: Helge Deller Reviewed-by: David Gibson Reviewed-by: Peter Xu Signed-off-by: Yi Liu Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Eric Auger Acked-by: Michael S. Tsirkin [ clg: - refreshed on latest QEMU - included hw/remote/iommu.c - documentation update - asserts in pci_setup_iommu() - removed checks on iommu_bus->iommu_ops->get_address_space - included Elroy PCI host (PA-RISC) ] Signed-off-by: Cédric Le Goater --- docs/devel/index-api.rst | 1 + docs/devel/pci.rst | 8 ++++++++ hw/alpha/typhoon.c | 6 +++++- hw/arm/smmu-common.c | 6 +++++- hw/i386/amd_iommu.c | 6 +++++- hw/i386/intel_iommu.c | 6 +++++- hw/pci-host/astro.c | 6 +++++- hw/pci-host/designware.c | 6 +++++- hw/pci-host/dino.c | 6 +++++- hw/pci-host/pnv_phb3.c | 6 +++++- hw/pci-host/pnv_phb4.c | 6 +++++- hw/pci-host/ppce500.c | 6 +++++- hw/pci-host/raven.c | 6 +++++- hw/pci-host/sabre.c | 6 +++++- hw/pci/pci.c | 18 +++++++++++++----- hw/ppc/ppc440_pcix.c | 6 +++++- hw/ppc/spapr_pci.c | 6 +++++- hw/remote/iommu.c | 6 +++++- hw/s390x/s390-pci-bus.c | 8 ++++++-- hw/virtio/virtio-iommu.c | 6 +++++- include/hw/pci/pci.h | 36 ++++++++++++++++++++++++++++++++++-- include/hw/pci/pci_bus.h | 2 +- 22 files changed, 143 insertions(+), 26 deletions(-) create mode 100644 docs/devel/pci.rst diff --git a/docs/devel/index-api.rst b/docs/devel/index-api.rst index 539ad29c21..fe01b2b488 100644 --- a/docs/devel/index-api.rst +++ b/docs/devel/index-api.rst @@ -11,6 +11,7 @@ generated from in-code annotations to function prototypes. loads-stores memory modules + pci qom-api qdev-api ui diff --git a/docs/devel/pci.rst b/docs/devel/pci.rst new file mode 100644 index 0000000000..68739334f3 --- /dev/null +++ b/docs/devel/pci.rst @@ -0,0 +1,8 @@ +============= +PCI subsystem +============= + +API Reference +------------- + +.. kernel-doc:: include/hw/pci/pci.h diff --git a/hw/alpha/typhoon.c b/hw/alpha/typhoon.c index 49a80550c5..e8711ae16a 100644 --- a/hw/alpha/typhoon.c +++ b/hw/alpha/typhoon.c @@ -738,6 +738,10 @@ static AddressSpace *typhoon_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn) return &s->pchip.iommu_as; } +static const PCIIOMMUOps typhoon_iommu_ops = { + .get_address_space = typhoon_pci_dma_iommu, +}; + static void typhoon_set_irq(void *opaque, int irq, int level) { TyphoonState *s = opaque; @@ -897,7 +901,7 @@ PCIBus *typhoon_init(MemoryRegion *ram, qemu_irq *p_isa_irq, "iommu-typhoon", UINT64_MAX); address_space_init(&s->pchip.iommu_as, MEMORY_REGION(&s->pchip.iommu), "pchip0-pci"); - pci_setup_iommu(b, typhoon_pci_dma_iommu, s); + pci_setup_iommu(b, &typhoon_iommu_ops, s); /* Pchip0 PCI special/interrupt acknowledge, 0x801.F800.0000, 64MB. */ memory_region_init_io(&s->pchip.reg_iack, OBJECT(s), &alpha_pci_iack_ops, diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index f35ae9aa22..9a8ac45431 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -605,6 +605,10 @@ static AddressSpace *smmu_find_add_as(PCIBus *bus, void *opaque, int devfn) return &sdev->as; } +static const PCIIOMMUOps smmu_ops = { + .get_address_space = smmu_find_add_as, +}; + IOMMUMemoryRegion *smmu_iommu_mr(SMMUState *s, uint32_t sid) { uint8_t bus_n, devfn; @@ -661,7 +665,7 @@ static void smmu_base_realize(DeviceState *dev, Error **errp) s->smmu_pcibus_by_busptr = g_hash_table_new(NULL, NULL); if (s->primary_bus) { - pci_setup_iommu(s->primary_bus, smmu_find_add_as, s); + pci_setup_iommu(s->primary_bus, &smmu_ops, s); } else { error_setg(errp, "SMMU is not attached to any PCI bus!"); } diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 7965415b47..4203144da9 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -1450,6 +1450,10 @@ static AddressSpace *amdvi_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) return &iommu_as[devfn]->as; } +static const PCIIOMMUOps amdvi_iommu_ops = { + .get_address_space = amdvi_host_dma_iommu, +}; + static const MemoryRegionOps mmio_mem_ops = { .read = amdvi_mmio_read, .write = amdvi_mmio_write, @@ -1581,7 +1585,7 @@ static void amdvi_sysbus_realize(DeviceState *dev, Error **errp) AMDVI_MMIO_SIZE); memory_region_add_subregion(get_system_memory(), AMDVI_BASE_ADDR, &s->mmio); - pci_setup_iommu(bus, amdvi_host_dma_iommu, s); + pci_setup_iommu(bus, &amdvi_iommu_ops, s); amdvi_init(s); } diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 1c6c18622f..a6f1a7aa52 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -4088,6 +4088,10 @@ static AddressSpace *vtd_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) return &vtd_as->as; } +static PCIIOMMUOps vtd_iommu_ops = { + .get_address_space = vtd_host_dma_iommu, +}; + static bool vtd_decide_config(IntelIOMMUState *s, Error **errp) { X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(s); @@ -4210,7 +4214,7 @@ static void vtd_realize(DeviceState *dev, Error **errp) s->vtd_address_spaces = g_hash_table_new_full(vtd_as_hash, vtd_as_equal, g_free, g_free); vtd_init(s); - pci_setup_iommu(bus, vtd_host_dma_iommu, dev); + pci_setup_iommu(bus, &vtd_iommu_ops, dev); /* Pseudo address space under root PCI bus. */ x86ms->ioapic_as = vtd_host_dma_iommu(bus, s, Q35_PSEUDO_DEVFN_IOAPIC); qemu_add_machine_init_done_notifier(&vtd_machine_done_notify); diff --git a/hw/pci-host/astro.c b/hw/pci-host/astro.c index 4b2d7caf2d..84e0ca14ac 100644 --- a/hw/pci-host/astro.c +++ b/hw/pci-host/astro.c @@ -345,6 +345,10 @@ static AddressSpace *elroy_pcihost_set_iommu(PCIBus *bus, void *opaque, return &s->astro->iommu_as; } +static const PCIIOMMUOps elroy_pcihost_iommu_ops = { + .get_address_space = elroy_pcihost_set_iommu, +}; + /* * Encoding in IOSAPIC: * base_addr == 0xfffa0000, we want to get 0xa0ff0000. @@ -834,7 +838,7 @@ static void astro_realize(DeviceState *obj, Error **errp) &elroy->pci_io); /* Host memory as seen from the PCI side, via the IOMMU. */ - pci_setup_iommu(PCI_HOST_BRIDGE(elroy)->bus, elroy_pcihost_set_iommu, + pci_setup_iommu(PCI_HOST_BRIDGE(elroy)->bus, &elroy_pcihost_iommu_ops, elroy); } } diff --git a/hw/pci-host/designware.c b/hw/pci-host/designware.c index 6f5442f108..f477f97847 100644 --- a/hw/pci-host/designware.c +++ b/hw/pci-host/designware.c @@ -663,6 +663,10 @@ static AddressSpace *designware_pcie_host_set_iommu(PCIBus *bus, void *opaque, return &s->pci.address_space; } +static const PCIIOMMUOps designware_iommu_ops = { + .get_address_space = designware_pcie_host_set_iommu, +}; + static void designware_pcie_host_realize(DeviceState *dev, Error **errp) { PCIHostState *pci = PCI_HOST_BRIDGE(dev); @@ -705,7 +709,7 @@ static void designware_pcie_host_realize(DeviceState *dev, Error **errp) address_space_init(&s->pci.address_space, &s->pci.address_space_root, "pcie-bus-address-space"); - pci_setup_iommu(pci->bus, designware_pcie_host_set_iommu, s); + pci_setup_iommu(pci->bus, &designware_iommu_ops, s); qdev_realize(DEVICE(&s->root), BUS(pci->bus), &error_fatal); } diff --git a/hw/pci-host/dino.c b/hw/pci-host/dino.c index 82503229fa..5b0947a16c 100644 --- a/hw/pci-host/dino.c +++ b/hw/pci-host/dino.c @@ -354,6 +354,10 @@ static AddressSpace *dino_pcihost_set_iommu(PCIBus *bus, void *opaque, return &s->bm_as; } +static const PCIIOMMUOps dino_iommu_ops = { + .get_address_space = dino_pcihost_set_iommu, +}; + /* * Dino interrupts are connected as shown on Page 78, Table 23 * (Little-endian bit numbers) @@ -481,7 +485,7 @@ static void dino_pcihost_init(Object *obj) g_free(name); } - pci_setup_iommu(phb->bus, dino_pcihost_set_iommu, s); + pci_setup_iommu(phb->bus, &dino_iommu_ops, s); sysbus_init_mmio(sbd, &s->this_mem); diff --git a/hw/pci-host/pnv_phb3.c b/hw/pci-host/pnv_phb3.c index c5e58f4086..2a74dbe45f 100644 --- a/hw/pci-host/pnv_phb3.c +++ b/hw/pci-host/pnv_phb3.c @@ -968,6 +968,10 @@ static AddressSpace *pnv_phb3_dma_iommu(PCIBus *bus, void *opaque, int devfn) return &ds->dma_as; } +static PCIIOMMUOps pnv_phb3_iommu_ops = { + .get_address_space = pnv_phb3_dma_iommu, +}; + static void pnv_phb3_instance_init(Object *obj) { PnvPHB3 *phb = PNV_PHB3(obj); @@ -1012,7 +1016,7 @@ void pnv_phb3_bus_init(DeviceState *dev, PnvPHB3 *phb) object_property_set_int(OBJECT(pci->bus), "chip-id", phb->chip_id, &error_abort); - pci_setup_iommu(pci->bus, pnv_phb3_dma_iommu, phb); + pci_setup_iommu(pci->bus, &pnv_phb3_iommu_ops, phb); } static void pnv_phb3_realize(DeviceState *dev, Error **errp) diff --git a/hw/pci-host/pnv_phb4.c b/hw/pci-host/pnv_phb4.c index 29cb11a5d9..37c7afc18c 100644 --- a/hw/pci-host/pnv_phb4.c +++ b/hw/pci-host/pnv_phb4.c @@ -1518,6 +1518,10 @@ static void pnv_phb4_xscom_realize(PnvPHB4 *phb) &phb->phb_regs_mr); } +static PCIIOMMUOps pnv_phb4_iommu_ops = { + .get_address_space = pnv_phb4_dma_iommu, +}; + static void pnv_phb4_instance_init(Object *obj) { PnvPHB4 *phb = PNV_PHB4(obj); @@ -1557,7 +1561,7 @@ void pnv_phb4_bus_init(DeviceState *dev, PnvPHB4 *phb) object_property_set_int(OBJECT(pci->bus), "chip-id", phb->chip_id, &error_abort); - pci_setup_iommu(pci->bus, pnv_phb4_dma_iommu, phb); + pci_setup_iommu(pci->bus, &pnv_phb4_iommu_ops, phb); pci->bus->flags |= PCI_BUS_EXTENDED_CONFIG_SPACE; } diff --git a/hw/pci-host/ppce500.c b/hw/pci-host/ppce500.c index 38814247f2..453a4e6ed3 100644 --- a/hw/pci-host/ppce500.c +++ b/hw/pci-host/ppce500.c @@ -435,6 +435,10 @@ static AddressSpace *e500_pcihost_set_iommu(PCIBus *bus, void *opaque, return &s->bm_as; } +static const PCIIOMMUOps ppce500_iommu_ops = { + .get_address_space = e500_pcihost_set_iommu, +}; + static void e500_pcihost_realize(DeviceState *dev, Error **errp) { SysBusDevice *sbd = SYS_BUS_DEVICE(dev); @@ -469,7 +473,7 @@ static void e500_pcihost_realize(DeviceState *dev, Error **errp) memory_region_init(&s->bm, OBJECT(s), "bm-e500", UINT64_MAX); memory_region_add_subregion(&s->bm, 0x0, &s->busmem); address_space_init(&s->bm_as, &s->bm, "pci-bm"); - pci_setup_iommu(b, e500_pcihost_set_iommu, s); + pci_setup_iommu(b, &ppce500_iommu_ops, s); pci_create_simple(b, 0, "e500-host-bridge"); diff --git a/hw/pci-host/raven.c b/hw/pci-host/raven.c index 9a11ac4b2b..86c3a49087 100644 --- a/hw/pci-host/raven.c +++ b/hw/pci-host/raven.c @@ -223,6 +223,10 @@ static AddressSpace *raven_pcihost_set_iommu(PCIBus *bus, void *opaque, return &s->bm_as; } +static const PCIIOMMUOps raven_iommu_ops = { + .get_address_space = raven_pcihost_set_iommu, +}; + static void raven_change_gpio(void *opaque, int n, int level) { PREPPCIState *s = opaque; @@ -320,7 +324,7 @@ static void raven_pcihost_initfn(Object *obj) memory_region_add_subregion(&s->bm, 0 , &s->bm_pci_memory_alias); memory_region_add_subregion(&s->bm, 0x80000000, &s->bm_ram_alias); address_space_init(&s->bm_as, &s->bm, "raven-bm"); - pci_setup_iommu(&s->pci_bus, raven_pcihost_set_iommu, s); + pci_setup_iommu(&s->pci_bus, &raven_iommu_ops, s); h->bus = &s->pci_bus; diff --git a/hw/pci-host/sabre.c b/hw/pci-host/sabre.c index dcb2e230b6..d0851b48b0 100644 --- a/hw/pci-host/sabre.c +++ b/hw/pci-host/sabre.c @@ -112,6 +112,10 @@ static AddressSpace *sabre_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn) return &is->iommu_as; } +static const PCIIOMMUOps sabre_iommu_ops = { + .get_address_space = sabre_pci_dma_iommu, +}; + static void sabre_config_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { @@ -384,7 +388,7 @@ static void sabre_realize(DeviceState *dev, Error **errp) /* IOMMU */ memory_region_add_subregion_overlap(&s->sabre_config, 0x200, sysbus_mmio_get_region(SYS_BUS_DEVICE(s->iommu), 0), 1); - pci_setup_iommu(phb->bus, sabre_pci_dma_iommu, s->iommu); + pci_setup_iommu(phb->bus, &sabre_iommu_ops, s->iommu); /* APB secondary busses */ pci_dev = pci_new_multifunction(PCI_DEVFN(1, 0), TYPE_SIMBA_PCI_BRIDGE); diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 885c04b6f5..c49417abb2 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -2678,7 +2678,7 @@ AddressSpace *pci_device_iommu_address_space(PCIDevice *dev) PCIBus *iommu_bus = bus; uint8_t devfn = dev->devfn; - while (iommu_bus && !iommu_bus->iommu_fn && iommu_bus->parent_dev) { + while (iommu_bus && !iommu_bus->iommu_ops && iommu_bus->parent_dev) { PCIBus *parent_bus = pci_get_bus(iommu_bus->parent_dev); /* @@ -2717,15 +2717,23 @@ AddressSpace *pci_device_iommu_address_space(PCIDevice *dev) iommu_bus = parent_bus; } - if (!pci_bus_bypass_iommu(bus) && iommu_bus && iommu_bus->iommu_fn) { - return iommu_bus->iommu_fn(bus, iommu_bus->iommu_opaque, devfn); + if (!pci_bus_bypass_iommu(bus) && iommu_bus->iommu_ops) { + return iommu_bus->iommu_ops->get_address_space(bus, + iommu_bus->iommu_opaque, devfn); } return &address_space_memory; } -void pci_setup_iommu(PCIBus *bus, PCIIOMMUFunc fn, void *opaque) +void pci_setup_iommu(PCIBus *bus, const PCIIOMMUOps *ops, void *opaque) { - bus->iommu_fn = fn; + /* + * If called, pci_setup_iommu() should provide a minimum set of + * useful callbacks for the bus. + */ + assert(ops); + assert(ops->get_address_space); + + bus->iommu_ops = ops; bus->iommu_opaque = opaque; } diff --git a/hw/ppc/ppc440_pcix.c b/hw/ppc/ppc440_pcix.c index 672090de94..df4ee374d0 100644 --- a/hw/ppc/ppc440_pcix.c +++ b/hw/ppc/ppc440_pcix.c @@ -449,6 +449,10 @@ static AddressSpace *ppc440_pcix_set_iommu(PCIBus *b, void *opaque, int devfn) return &s->bm_as; } +static const PCIIOMMUOps ppc440_iommu_ops = { + .get_address_space = ppc440_pcix_set_iommu, +}; + /* * Some guests on sam460ex write all kinds of garbage here such as * missing enable bit and low bits set and still expect this to work @@ -503,7 +507,7 @@ static void ppc440_pcix_realize(DeviceState *dev, Error **errp) memory_region_init(&s->bm, OBJECT(s), "bm-ppc440-pcix", UINT64_MAX); memory_region_add_subregion(&s->bm, 0x0, &s->busmem); address_space_init(&s->bm_as, &s->bm, "pci-bm"); - pci_setup_iommu(h->bus, ppc440_pcix_set_iommu, s); + pci_setup_iommu(h->bus, &ppc440_iommu_ops, s); memory_region_init(&s->container, OBJECT(s), "pci-container", PCI_ALL_SIZE); memory_region_init_io(&h->conf_mem, OBJECT(s), &ppc440_pcix_host_conf_ops, diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 370c5a90f2..a27024e45a 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -780,6 +780,10 @@ static AddressSpace *spapr_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn) return &phb->iommu_as; } +static const PCIIOMMUOps spapr_iommu_ops = { + .get_address_space = spapr_pci_dma_iommu, +}; + static char *spapr_phb_vfio_get_loc_code(SpaprPhbState *sphb, PCIDevice *pdev) { g_autofree char *path = NULL; @@ -1978,7 +1982,7 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(&sphb->iommu_root, SPAPR_PCI_MSI_WINDOW, &sphb->msiwindow); - pci_setup_iommu(bus, spapr_pci_dma_iommu, sphb); + pci_setup_iommu(bus, &spapr_iommu_ops, sphb); pci_bus_set_route_irq_fn(bus, spapr_route_intx_pin_to_irq); diff --git a/hw/remote/iommu.c b/hw/remote/iommu.c index 1391dd712c..7c56aad0fc 100644 --- a/hw/remote/iommu.c +++ b/hw/remote/iommu.c @@ -100,6 +100,10 @@ static void remote_iommu_finalize(Object *obj) iommu->elem_by_devfn = NULL; } +static const PCIIOMMUOps remote_iommu_ops = { + .get_address_space = remote_iommu_find_add_as, +}; + void remote_iommu_setup(PCIBus *pci_bus) { RemoteIommu *iommu = NULL; @@ -108,7 +112,7 @@ void remote_iommu_setup(PCIBus *pci_bus) iommu = REMOTE_IOMMU(object_new(TYPE_REMOTE_IOMMU)); - pci_setup_iommu(pci_bus, remote_iommu_find_add_as, iommu); + pci_setup_iommu(pci_bus, &remote_iommu_ops, iommu); object_property_add_child(OBJECT(pci_bus), "remote-iommu", OBJECT(iommu)); diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 2ca36f9f3b..347580ebac 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -652,6 +652,10 @@ static AddressSpace *s390_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn) return &iommu->as; } +static const PCIIOMMUOps s390_iommu_ops = { + .get_address_space = s390_pci_dma_iommu, +}; + static uint8_t set_ind_atomic(uint64_t ind_loc, uint8_t to_be_set) { uint8_t expected, actual; @@ -839,7 +843,7 @@ static void s390_pcihost_realize(DeviceState *dev, Error **errp) b = pci_register_root_bus(dev, NULL, s390_pci_set_irq, s390_pci_map_irq, NULL, get_system_memory(), get_system_io(), 0, 64, TYPE_PCI_BUS); - pci_setup_iommu(b, s390_pci_dma_iommu, s); + pci_setup_iommu(b, &s390_iommu_ops, s); bus = BUS(b); qbus_set_hotplug_handler(bus, OBJECT(dev)); @@ -1058,7 +1062,7 @@ static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev, pdev = PCI_DEVICE(dev); pci_bridge_map_irq(pb, dev->id, s390_pci_map_irq); - pci_setup_iommu(&pb->sec_bus, s390_pci_dma_iommu, s); + pci_setup_iommu(&pb->sec_bus, &s390_iommu_ops, s); qbus_set_hotplug_handler(BUS(&pb->sec_bus), OBJECT(s)); diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index dede0d41aa..89fb5767d1 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -461,6 +461,10 @@ static AddressSpace *virtio_iommu_find_add_as(PCIBus *bus, void *opaque, return &sdev->as; } +static const PCIIOMMUOps virtio_iommu_ops = { + .get_address_space = virtio_iommu_find_add_as, +}; + static int virtio_iommu_attach(VirtIOIOMMU *s, struct virtio_iommu_req_attach *req) { @@ -1332,7 +1336,7 @@ static void virtio_iommu_device_realize(DeviceState *dev, Error **errp) s->as_by_busptr = g_hash_table_new_full(NULL, NULL, NULL, g_free); if (s->primary_bus) { - pci_setup_iommu(s->primary_bus, virtio_iommu_find_add_as, s); + pci_setup_iommu(s->primary_bus, &virtio_iommu_ops, s); } else { error_setg(errp, "VIRTIO-IOMMU is not attached to any PCI bus!"); } diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index ea5aff118b..fa6313aabc 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -363,10 +363,42 @@ void pci_bus_get_w64_range(PCIBus *bus, Range *range); void pci_device_deassert_intx(PCIDevice *dev); -typedef AddressSpace *(*PCIIOMMUFunc)(PCIBus *, void *, int); + +/** + * struct PCIIOMMUOps: callbacks structure for specific IOMMU handlers + * of a PCIBus + * + * Allows to modify the behavior of some IOMMU operations of the PCI + * framework for a set of devices on a PCI bus. + */ +typedef struct PCIIOMMUOps { + /** + * @get_address_space: get the address space for a set of devices + * on a PCI bus. + * + * Mandatory callback which returns a pointer to an #AddressSpace + * + * @bus: the #PCIBus being accessed. + * + * @opaque: the data passed to pci_setup_iommu(). + * + * @devfn: device and function number + */ + AddressSpace * (*get_address_space)(PCIBus *bus, void *opaque, int devfn); +} PCIIOMMUOps; AddressSpace *pci_device_iommu_address_space(PCIDevice *dev); -void pci_setup_iommu(PCIBus *bus, PCIIOMMUFunc fn, void *opaque); + +/** + * pci_setup_iommu: Initialize specific IOMMU handlers for a PCIBus + * + * Let PCI host bridges define specific operations. + * + * @bus: the #PCIBus being updated. + * @ops: the #PCIIOMMUOps + * @opaque: passed to callbacks of the @ops structure. + */ +void pci_setup_iommu(PCIBus *bus, const PCIIOMMUOps *ops, void *opaque); pcibus_t pci_bar_address(PCIDevice *d, int reg, uint8_t type, pcibus_t size); diff --git a/include/hw/pci/pci_bus.h b/include/hw/pci/pci_bus.h index 5653175957..2261312546 100644 --- a/include/hw/pci/pci_bus.h +++ b/include/hw/pci/pci_bus.h @@ -33,7 +33,7 @@ enum PCIBusFlags { struct PCIBus { BusState qbus; enum PCIBusFlags flags; - PCIIOMMUFunc iommu_fn; + const PCIIOMMUOps *iommu_ops; void *iommu_opaque; uint8_t devfn_min; uint32_t slot_reserved_mask; From 721da0396cfa0a4859cefb57e32cc79d19d80f54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 26 Oct 2023 09:06:34 +0200 Subject: [PATCH 411/974] util/uuid: Add UUID_STR_LEN definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qemu_uuid_unparse() includes a trailing NUL when writing the uuid string and the buffer size should be UUID_FMT_LEN + 1 bytes. Add a define for this size and use it where required. Cc: Fam Zheng Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Juan Quintela Reviewed-by: "Denis V. Lunev" Signed-off-by: Cédric Le Goater --- block/parallels-ext.c | 2 +- block/vdi.c | 2 +- hw/core/qdev-properties-system.c | 2 +- hw/hyperv/vmbus.c | 4 ++-- include/qemu/uuid.h | 1 + migration/savevm.c | 4 ++-- tests/unit/test-uuid.c | 2 +- util/uuid.c | 2 +- 8 files changed, 10 insertions(+), 9 deletions(-) diff --git a/block/parallels-ext.c b/block/parallels-ext.c index 8a109f005a..4d8ecf5047 100644 --- a/block/parallels-ext.c +++ b/block/parallels-ext.c @@ -130,7 +130,7 @@ static BdrvDirtyBitmap *parallels_load_bitmap(BlockDriverState *bs, g_autofree uint64_t *l1_table = NULL; BdrvDirtyBitmap *bitmap; QemuUUID uuid; - char uuidstr[UUID_FMT_LEN + 1]; + char uuidstr[UUID_STR_LEN]; int i; if (data_size < sizeof(bf)) { diff --git a/block/vdi.c b/block/vdi.c index c647d72895..7cfd12b50d 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -239,7 +239,7 @@ static void vdi_header_to_le(VdiHeader *header) static void vdi_header_print(VdiHeader *header) { - char uuidstr[37]; + char uuidstr[UUID_STR_LEN]; QemuUUID uuid; logout("text %s", header->text); logout("signature 0x%08x\n", header->signature); diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index b5ccafa29e..b46d16cd2c 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -1114,7 +1114,7 @@ static void get_uuid(Object *obj, Visitor *v, const char *name, void *opaque, { Property *prop = opaque; QemuUUID *uuid = object_field_prop_ptr(obj, prop); - char buffer[UUID_FMT_LEN + 1]; + char buffer[UUID_STR_LEN]; char *p = buffer; qemu_uuid_unparse(uuid, buffer); diff --git a/hw/hyperv/vmbus.c b/hw/hyperv/vmbus.c index 271289f902..c64eaa5a46 100644 --- a/hw/hyperv/vmbus.c +++ b/hw/hyperv/vmbus.c @@ -2271,7 +2271,7 @@ static void vmbus_dev_realize(DeviceState *dev, Error **errp) VMBus *vmbus = VMBUS(qdev_get_parent_bus(dev)); BusChild *child; Error *err = NULL; - char idstr[UUID_FMT_LEN + 1]; + char idstr[UUID_STR_LEN]; assert(!qemu_uuid_is_null(&vdev->instanceid)); @@ -2467,7 +2467,7 @@ static char *vmbus_get_dev_path(DeviceState *dev) static char *vmbus_get_fw_dev_path(DeviceState *dev) { VMBusDevice *vdev = VMBUS_DEVICE(dev); - char uuid[UUID_FMT_LEN + 1]; + char uuid[UUID_STR_LEN]; qemu_uuid_unparse(&vdev->instanceid, uuid); return g_strdup_printf("%s@%s", qdev_fw_name(dev), uuid); diff --git a/include/qemu/uuid.h b/include/qemu/uuid.h index e24a1099e4..4e7afaf1d5 100644 --- a/include/qemu/uuid.h +++ b/include/qemu/uuid.h @@ -79,6 +79,7 @@ typedef struct { "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx" #define UUID_FMT_LEN 36 +#define UUID_STR_LEN (UUID_FMT_LEN + 1) #define UUID_NONE "00000000-0000-0000-0000-000000000000" diff --git a/migration/savevm.c b/migration/savevm.c index bc98c2ea6f..eec5503a42 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -471,8 +471,8 @@ static bool vmstate_uuid_needed(void *opaque) static int vmstate_uuid_post_load(void *opaque, int version_id) { SaveState *state = opaque; - char uuid_src[UUID_FMT_LEN + 1]; - char uuid_dst[UUID_FMT_LEN + 1]; + char uuid_src[UUID_STR_LEN]; + char uuid_dst[UUID_STR_LEN]; if (!qemu_uuid_set) { /* diff --git a/tests/unit/test-uuid.c b/tests/unit/test-uuid.c index aedc125ae9..739b91583c 100644 --- a/tests/unit/test-uuid.c +++ b/tests/unit/test-uuid.c @@ -145,7 +145,7 @@ static void test_uuid_unparse(void) int i; for (i = 0; i < ARRAY_SIZE(uuid_test_data); i++) { - char out[37]; + char out[UUID_STR_LEN]; if (!uuid_test_data[i].check_unparse) { continue; diff --git a/util/uuid.c b/util/uuid.c index d71aa79e5e..234619dd5e 100644 --- a/util/uuid.c +++ b/util/uuid.c @@ -51,7 +51,7 @@ int qemu_uuid_is_equal(const QemuUUID *lhv, const QemuUUID *rhv) void qemu_uuid_unparse(const QemuUUID *uuid, char *out) { const unsigned char *uu = &uuid->data[0]; - snprintf(out, UUID_FMT_LEN + 1, UUID_FMT, + snprintf(out, UUID_STR_LEN, UUID_FMT, uu[0], uu[1], uu[2], uu[3], uu[4], uu[5], uu[6], uu[7], uu[8], uu[9], uu[10], uu[11], uu[12], uu[13], uu[14], uu[15]); } From f8d6f3b16c37bd516a026e92a31dade5d761d3a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 26 Oct 2023 09:06:35 +0200 Subject: [PATCH 412/974] vfio/pci: Fix buffer overrun when writing the VF token MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qemu_uuid_unparse() includes a trailing NUL when writing the uuid string and the buffer size should be UUID_FMT_LEN + 1 bytes. Use the recently added UUID_STR_LEN which defines the correct size. Fixes: CID 1522913 Fixes: 2dca1b37a760 ("vfio/pci: add support for VF token") Cc: Alex Williamson Reviewed-by: Alex Williamson Reviewed-by: Juan Quintela Reviewed-by: "Denis V. Lunev" Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index b27011cee7..c62c02f7b6 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3081,7 +3081,7 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) struct stat st; int i, ret; bool is_mdev; - char uuid[UUID_FMT_LEN]; + char uuid[UUID_STR_LEN]; char *name; if (!vbasedev->sysfsdev) { From 4ef9d97b1a37b8cfd152cc3ac5f9576e406868b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 26 Oct 2023 09:06:36 +0200 Subject: [PATCH 413/974] util/uuid: Remove UUID_FMT_LEN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dangerous and now unused. Cc: Fam Zheng Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: "Denis V. Lunev" Reviewed-by: Juan Quintela Signed-off-by: Cédric Le Goater --- include/qemu/uuid.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/qemu/uuid.h b/include/qemu/uuid.h index 4e7afaf1d5..356efe7b57 100644 --- a/include/qemu/uuid.h +++ b/include/qemu/uuid.h @@ -78,8 +78,7 @@ typedef struct { "%02hhx%02hhx-" \ "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx" -#define UUID_FMT_LEN 36 -#define UUID_STR_LEN (UUID_FMT_LEN + 1) +#define UUID_STR_LEN (36 + 1) #define UUID_NONE "00000000-0000-0000-0000-000000000000" From 5fe51934b1cd94a75007dd456fecc2ff6ee622e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 25 Oct 2023 12:12:44 +0200 Subject: [PATCH 414/974] util/uuid: Define UUID_STR_LEN from UUID_NONE string MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Fam Zheng Suggested-by: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Cédric Le Goater --- include/qemu/uuid.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/qemu/uuid.h b/include/qemu/uuid.h index 356efe7b57..869f84af09 100644 --- a/include/qemu/uuid.h +++ b/include/qemu/uuid.h @@ -78,9 +78,10 @@ typedef struct { "%02hhx%02hhx-" \ "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx" -#define UUID_STR_LEN (36 + 1) - #define UUID_NONE "00000000-0000-0000-0000-000000000000" +QEMU_BUILD_BUG_ON(sizeof(UUID_NONE) - 1 != 36); + +#define UUID_STR_LEN sizeof(UUID_NONE) void qemu_uuid_generate(QemuUUID *out); From 0983125b405c479a6e7eb49b81cfdae969e28cee Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Fri, 3 Nov 2023 08:42:45 +0100 Subject: [PATCH 415/974] migration: Unlock mutex in error case MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We were not unlocking bitmap mutex on the error case. To fix it forever change to enclose the code with WITH_QEMU_LOCK_GUARD(). Coverity CID 1523750. Fixes: a2326705e5 ("migration: Stop migration immediately in RDMA error paths") Reviewed-by: Alex Bennée Signed-off-by: Juan Quintela Message-ID: <20231103074245.55166-1-quintela@redhat.com> --- migration/ram.c | 110 ++++++++++++++++++++++++------------------------ 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index a0f3b86663..8c7886ab79 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -3030,71 +3030,71 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) * MAX_WAIT (if curious, further see commit 4508bd9ed8053ce) below, which * guarantees that we'll at least released it in a regular basis. */ - qemu_mutex_lock(&rs->bitmap_mutex); - WITH_RCU_READ_LOCK_GUARD() { - if (ram_list.version != rs->last_version) { - ram_state_reset(rs); - } - - /* Read version before ram_list.blocks */ - smp_rmb(); - - ret = rdma_registration_start(f, RAM_CONTROL_ROUND); - if (ret < 0) { - qemu_file_set_error(f, ret); - goto out; - } - - t0 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); - i = 0; - while ((ret = migration_rate_exceeded(f)) == 0 || - postcopy_has_request(rs)) { - int pages; - - if (qemu_file_get_error(f)) { - break; + WITH_QEMU_LOCK_GUARD(&rs->bitmap_mutex) { + WITH_RCU_READ_LOCK_GUARD() { + if (ram_list.version != rs->last_version) { + ram_state_reset(rs); } - pages = ram_find_and_save_block(rs); - /* no more pages to sent */ - if (pages == 0) { - done = 1; - break; + /* Read version before ram_list.blocks */ + smp_rmb(); + + ret = rdma_registration_start(f, RAM_CONTROL_ROUND); + if (ret < 0) { + qemu_file_set_error(f, ret); + goto out; } - if (pages < 0) { - qemu_file_set_error(f, pages); - break; - } + t0 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); + i = 0; + while ((ret = migration_rate_exceeded(f)) == 0 || + postcopy_has_request(rs)) { + int pages; - rs->target_page_count += pages; - - /* - * During postcopy, it is necessary to make sure one whole host - * page is sent in one chunk. - */ - if (migrate_postcopy_ram()) { - compress_flush_data(); - } - - /* - * we want to check in the 1st loop, just in case it was the 1st - * time and we had to sync the dirty bitmap. - * qemu_clock_get_ns() is a bit expensive, so we only check each - * some iterations - */ - if ((i & 63) == 0) { - uint64_t t1 = (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - t0) / - 1000000; - if (t1 > MAX_WAIT) { - trace_ram_save_iterate_big_wait(t1, i); + if (qemu_file_get_error(f)) { break; } + + pages = ram_find_and_save_block(rs); + /* no more pages to sent */ + if (pages == 0) { + done = 1; + break; + } + + if (pages < 0) { + qemu_file_set_error(f, pages); + break; + } + + rs->target_page_count += pages; + + /* + * During postcopy, it is necessary to make sure one whole host + * page is sent in one chunk. + */ + if (migrate_postcopy_ram()) { + compress_flush_data(); + } + + /* + * we want to check in the 1st loop, just in case it was the 1st + * time and we had to sync the dirty bitmap. + * qemu_clock_get_ns() is a bit expensive, so we only check each + * some iterations + */ + if ((i & 63) == 0) { + uint64_t t1 = (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - t0) / + 1000000; + if (t1 > MAX_WAIT) { + trace_ram_save_iterate_big_wait(t1, i); + break; + } + } + i++; } - i++; } } - qemu_mutex_unlock(&rs->bitmap_mutex); /* * Must occur before EOS (or any QEMUFile operation) From 6c1b28e9e405da8cfb7296b9efa2b85650086784 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Thu, 22 Jun 2023 12:18:23 +0200 Subject: [PATCH 416/974] memory-device: Support empty memory devices Let's support empty memory devices -- memory devices that don't have a memory device region in the current configuration. hv-balloon with an optional memdev is the primary use case. Signed-off-by: David Hildenbrand Signed-off-by: Maciej S. Szmigiero --- hw/mem/memory-device.c | 43 +++++++++++++++++++++++++++++++--- include/hw/mem/memory-device.h | 7 +++++- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/hw/mem/memory-device.c b/hw/mem/memory-device.c index ae38f48f16..db702ccad5 100644 --- a/hw/mem/memory-device.c +++ b/hw/mem/memory-device.c @@ -20,6 +20,22 @@ #include "exec/address-spaces.h" #include "trace.h" +static bool memory_device_is_empty(const MemoryDeviceState *md) +{ + const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(md); + Error *local_err = NULL; + MemoryRegion *mr; + + /* dropping const here is fine as we don't touch the memory region */ + mr = mdc->get_memory_region((MemoryDeviceState *)md, &local_err); + if (local_err) { + /* Not empty, we'll report errors later when ontaining the MR again. */ + error_free(local_err); + return false; + } + return !mr; +} + static gint memory_device_addr_sort(gconstpointer a, gconstpointer b) { const MemoryDeviceState *md_a = MEMORY_DEVICE(a); @@ -249,6 +265,10 @@ static uint64_t memory_device_get_free_addr(MachineState *ms, uint64_t next_addr; Range tmp; + if (memory_device_is_empty(md)) { + continue; + } + range_init_nofail(&tmp, mdc->get_addr(md), memory_device_get_region_size(md, &error_abort)); @@ -292,6 +312,7 @@ MemoryDeviceInfoList *qmp_memory_device_list(void) const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(item->data); MemoryDeviceInfo *info = g_new0(MemoryDeviceInfo, 1); + /* Let's query infotmation even for empty memory devices. */ mdc->fill_device_info(md, info); QAPI_LIST_APPEND(tail, info); @@ -311,7 +332,7 @@ static int memory_device_plugged_size(Object *obj, void *opaque) const MemoryDeviceState *md = MEMORY_DEVICE(obj); const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(obj); - if (dev->realized) { + if (dev->realized && !memory_device_is_empty(md)) { *size += mdc->get_plugged_size(md, &error_abort); } } @@ -337,6 +358,11 @@ void memory_device_pre_plug(MemoryDeviceState *md, MachineState *ms, uint64_t addr, align = 0; MemoryRegion *mr; + /* We support empty memory devices even without device memory. */ + if (memory_device_is_empty(md)) { + return; + } + if (!ms->device_memory) { error_setg(errp, "the configuration is not prepared for memory devices" " (e.g., for memory hotplug), consider specifying the" @@ -380,10 +406,17 @@ out: void memory_device_plug(MemoryDeviceState *md, MachineState *ms) { const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(md); - const unsigned int memslots = memory_device_get_memslots(md); - const uint64_t addr = mdc->get_addr(md); + unsigned int memslots; + uint64_t addr; MemoryRegion *mr; + if (memory_device_is_empty(md)) { + return; + } + + memslots = memory_device_get_memslots(md); + addr = mdc->get_addr(md); + /* * We expect that a previous call to memory_device_pre_plug() succeeded, so * it can't fail at this point. @@ -408,6 +441,10 @@ void memory_device_unplug(MemoryDeviceState *md, MachineState *ms) const unsigned int memslots = memory_device_get_memslots(md); MemoryRegion *mr; + if (memory_device_is_empty(md)) { + return; + } + /* * We expect that a previous call to memory_device_pre_plug() succeeded, so * it can't fail at this point. diff --git a/include/hw/mem/memory-device.h b/include/hw/mem/memory-device.h index 3354d6c166..a1d62cc551 100644 --- a/include/hw/mem/memory-device.h +++ b/include/hw/mem/memory-device.h @@ -38,6 +38,10 @@ typedef struct MemoryDeviceState MemoryDeviceState; * address in guest physical memory can either be specified explicitly * or get assigned automatically. * + * Some memory device might not own a memory region in certain device + * configurations. Such devices can logically get (un)plugged, however, + * empty memory devices are mostly ignored by the memory device code. + * * Conceptually, memory devices only span one memory region. If multiple * successive memory regions are used, a covering memory region has to * be provided. Scattered memory regions are not supported for single @@ -91,7 +95,8 @@ struct MemoryDeviceClass { uint64_t (*get_plugged_size)(const MemoryDeviceState *md, Error **errp); /* - * Return the memory region of the memory device. + * Return the memory region of the memory device. If the device is + * completely empty, returns NULL without an error. * * Called when (un)plugging the memory device, to (un)map the * memory region in guest physical memory, but also to detect the From a24fe909f30c7b6ad49848af1b15c1dc714081b5 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 26 Oct 2023 09:56:47 +0100 Subject: [PATCH 417/974] macfb: don't clear interrupts when writing to DAFB_RESET Traces from A/UX suggest that this register is only used to reset the framebuffer LUT (colour lookup table) and not any other device state. Signed-off-by: Mark Cave-Ayland Reviewed-by: Laurent Vivier Message-ID: <20231026085650.917663-2-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/display/macfb.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/hw/display/macfb.c b/hw/display/macfb.c index 2f8e016566..28db2e9f24 100644 --- a/hw/display/macfb.c +++ b/hw/display/macfb.c @@ -585,8 +585,6 @@ static void macfb_ctrl_write(void *opaque, break; case DAFB_RESET: s->palette_current = 0; - s->regs[DAFB_INTR_STAT >> 2] &= ~DAFB_INTR_VBL; - macfb_update_irq(s); break; case DAFB_LUT: s->color_palette[s->palette_current] = val; From 2fca4e7a710bc5783b1043ce354b0597b519f157 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 26 Oct 2023 09:56:48 +0100 Subject: [PATCH 418/974] macfb: rename DAFB_RESET to DAFB_LUT_INDEX When A/UX uses the MacOS Device Manager Status (GetEntries) call to read the contents of the CLUT, it is easy to see that the requested index is written to the DAFB_RESET register. Update the palette_current index with the requested value, and rename it to DAFB_LUT_INDEX to reflect its true purpose. Signed-off-by: Mark Cave-Ayland Reviewed-by: Laurent Vivier Message-ID: <20231026085650.917663-3-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/display/macfb.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/display/macfb.c b/hw/display/macfb.c index 28db2e9f24..eb4ce6b824 100644 --- a/hw/display/macfb.c +++ b/hw/display/macfb.c @@ -36,7 +36,7 @@ #define DAFB_INTR_MASK 0x104 #define DAFB_INTR_STAT 0x108 #define DAFB_INTR_CLEAR 0x10c -#define DAFB_RESET 0x200 +#define DAFB_LUT_INDEX 0x200 #define DAFB_LUT 0x213 #define DAFB_INTR_VBL 0x4 @@ -583,8 +583,8 @@ static void macfb_ctrl_write(void *opaque, s->regs[DAFB_INTR_STAT >> 2] &= ~DAFB_INTR_VBL; macfb_update_irq(s); break; - case DAFB_RESET: - s->palette_current = 0; + case DAFB_LUT_INDEX: + s->palette_current = (val & 0xff) * 3; break; case DAFB_LUT: s->color_palette[s->palette_current] = val; From ced64254d66c28e18e669318aaffa0338df6ae3a Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 26 Oct 2023 09:56:49 +0100 Subject: [PATCH 419/974] macfb: allow larger write accesses to the DAFB_LUT register The original tests with MacOS showed that only the bottom 8 bits of the DAFB_LUT register were used when writing to the LUT, however A/UX performs some of its writes using 4 byte accesses. Expand the address range for the DAFB_LUT register so that different size accesses write the correct value to the color_palette array. Signed-off-by: Mark Cave-Ayland Reviewed-by: Laurent Vivier Message-ID: <20231026085650.917663-4-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/display/macfb.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/display/macfb.c b/hw/display/macfb.c index eb4ce6b824..4a1c75d572 100644 --- a/hw/display/macfb.c +++ b/hw/display/macfb.c @@ -37,7 +37,7 @@ #define DAFB_INTR_STAT 0x108 #define DAFB_INTR_CLEAR 0x10c #define DAFB_LUT_INDEX 0x200 -#define DAFB_LUT 0x213 +#define DAFB_LUT 0x210 #define DAFB_INTR_VBL 0x4 @@ -586,8 +586,8 @@ static void macfb_ctrl_write(void *opaque, case DAFB_LUT_INDEX: s->palette_current = (val & 0xff) * 3; break; - case DAFB_LUT: - s->color_palette[s->palette_current] = val; + case DAFB_LUT ... DAFB_LUT + 3: + s->color_palette[s->palette_current] = val & 0xff; s->palette_current = (s->palette_current + 1) % ARRAY_SIZE(s->color_palette); if (s->palette_current % 3) { From 95f3943210416e054751bc230d4cec7d87995525 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 26 Oct 2023 09:56:50 +0100 Subject: [PATCH 420/974] macfb: allow reads from the DAFB_LUT register This enables A/UX to correctly retrieve the LUT entries when used with applications that use the MacOS Device Manager Status (GetEntries) call. Signed-off-by: Mark Cave-Ayland Reviewed-by: Laurent Vivier Message-ID: <20231026085650.917663-5-mark.cave-ayland@ilande.co.uk> Signed-off-by: Laurent Vivier --- hw/display/macfb.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/display/macfb.c b/hw/display/macfb.c index 4a1c75d572..d61541ccb5 100644 --- a/hw/display/macfb.c +++ b/hw/display/macfb.c @@ -537,6 +537,11 @@ static uint64_t macfb_ctrl_read(void *opaque, case DAFB_MODE_SENSE: val = macfb_sense_read(s); break; + case DAFB_LUT ... DAFB_LUT + 3: + val = s->color_palette[s->palette_current]; + s->palette_current = (s->palette_current + 1) % + ARRAY_SIZE(s->color_palette); + break; default: if (addr < MACFB_CTRL_TOPADDR) { val = s->regs[addr >> 2]; From b1fa27fcc88428f79de772825ab50cfca14bfc5f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 14 Oct 2023 14:01:08 -0700 Subject: [PATCH 421/974] target/sparc: Introduce cpu_put_psr_icc Isolate linux-user from changes to icc representation. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- linux-user/sparc/signal.c | 2 +- target/sparc/cpu.h | 1 + target/sparc/win_helper.c | 7 ++++++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/linux-user/sparc/signal.c b/linux-user/sparc/signal.c index 2be9000b9e..dfcae707e0 100644 --- a/linux-user/sparc/signal.c +++ b/linux-user/sparc/signal.c @@ -164,7 +164,7 @@ static void restore_pt_regs(struct target_pt_regs *regs, CPUSPARCState *env) */ uint32_t psr; __get_user(psr, ®s->psr); - env->psr = (psr & PSR_ICC) | (env->psr & ~PSR_ICC); + cpu_put_psr_icc(env, psr); #endif /* Note that pc and npc are handled in the caller. */ diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index 758a4e8aaa..955329f6c9 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -619,6 +619,7 @@ void sparc_restore_state_to_opc(CPUState *cs, /* win_helper.c */ target_ulong cpu_get_psr(CPUSPARCState *env1); void cpu_put_psr(CPUSPARCState *env1, target_ulong val); +void cpu_put_psr_icc(CPUSPARCState *env1, target_ulong val); void cpu_put_psr_raw(CPUSPARCState *env1, target_ulong val); #ifdef TARGET_SPARC64 void cpu_change_pstate(CPUSPARCState *env1, uint32_t new_pstate); diff --git a/target/sparc/win_helper.c b/target/sparc/win_helper.c index 3a7c0ff943..bf2c90c780 100644 --- a/target/sparc/win_helper.c +++ b/target/sparc/win_helper.c @@ -67,9 +67,14 @@ target_ulong cpu_get_psr(CPUSPARCState *env) #endif } -void cpu_put_psr_raw(CPUSPARCState *env, target_ulong val) +void cpu_put_psr_icc(CPUSPARCState *env, target_ulong val) { env->psr = val & PSR_ICC; +} + +void cpu_put_psr_raw(CPUSPARCState *env, target_ulong val) +{ + cpu_put_psr_icc(env, val); #if !defined(TARGET_SPARC64) env->psref = (val & PSR_EF) ? 1 : 0; env->psrpil = (val & PSR_PIL) >> 8; From 2a1905c79e1009600e96e7d1d0c592d573e94dbd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 14 Oct 2023 18:24:19 -0700 Subject: [PATCH 422/974] target/sparc: Split psr and xcc into components Step in removing CC_OP: change the representation of CC_OP_FLAGS. The 8 bits are distributed between 6 variables, which should make it easy to keep up to date. The code within cc_helper.c is quite ugly but is only temporary. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- linux-user/sparc/cpu_loop.c | 6 +- linux-user/sparc/target_cpu.h | 17 +- target/sparc/cc_helper.c | 51 +++-- target/sparc/cpu.h | 30 ++- target/sparc/machine.c | 45 ++++- target/sparc/translate.c | 362 +++++++++++++--------------------- target/sparc/win_helper.c | 52 ++++- 7 files changed, 291 insertions(+), 272 deletions(-) diff --git a/linux-user/sparc/cpu_loop.c b/linux-user/sparc/cpu_loop.c index b36bb2574b..c1a2362041 100644 --- a/linux-user/sparc/cpu_loop.c +++ b/linux-user/sparc/cpu_loop.c @@ -197,10 +197,8 @@ static uint32_t do_getpsr(CPUSPARCState *env) /* Avoid ifdefs below for the abi32 and abi64 paths. */ #ifdef TARGET_ABI32 #define TARGET_TT_SYSCALL (TT_TRAP + 0x10) /* t_linux */ -#define syscall_cc psr #else #define TARGET_TT_SYSCALL (TT_TRAP + 0x6d) /* tl0_linux64 */ -#define syscall_cc xcc #endif /* Avoid ifdefs below for the v9 and pre-v9 hw traps. */ @@ -240,10 +238,10 @@ void cpu_loop (CPUSPARCState *env) break; } if ((abi_ulong)ret >= (abi_ulong)(-515)) { - env->syscall_cc |= PSR_CARRY; + set_syscall_C(env, 1); ret = -ret; } else { - env->syscall_cc &= ~PSR_CARRY; + set_syscall_C(env, 0); } env->regwptr[0] = ret; /* next instruction */ diff --git a/linux-user/sparc/target_cpu.h b/linux-user/sparc/target_cpu.h index 1f4bed50f4..5f62c5eb75 100644 --- a/linux-user/sparc/target_cpu.h +++ b/linux-user/sparc/target_cpu.h @@ -26,6 +26,17 @@ # define TARGET_STACK_BIAS 0 #endif +static void set_syscall_C(CPUSPARCState *env, bool val) +{ +#ifndef TARGET_SPARC64 + env->icc_C = val; +#elif defined(TARGET_ABI32) + env->icc_C = (uint64_t)val << 32; +#else + env->xcc_C = val; +#endif +} + static inline void cpu_clone_regs_child(CPUSPARCState *env, target_ulong newsp, unsigned flags) { @@ -58,11 +69,7 @@ static inline void cpu_clone_regs_child(CPUSPARCState *env, target_ulong newsp, * do the pc advance twice. */ env->regwptr[WREG_O0] = 0; -#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) - env->xcc &= ~PSR_CARRY; -#else - env->psr &= ~PSR_CARRY; -#endif + set_syscall_C(env, 0); env->pc = env->npc; env->npc = env->npc + 4; } diff --git a/target/sparc/cc_helper.c b/target/sparc/cc_helper.c index 7ad5b9b29e..46bec69d96 100644 --- a/target/sparc/cc_helper.c +++ b/target/sparc/cc_helper.c @@ -21,16 +21,6 @@ #include "cpu.h" #include "exec/helper-proto.h" -static uint32_t compute_all_flags(CPUSPARCState *env) -{ - return env->psr & PSR_ICC; -} - -static uint32_t compute_C_flags(CPUSPARCState *env) -{ - return env->psr & PSR_CARRY; -} - static inline uint32_t get_NZ_icc(int32_t dst) { uint32_t ret = 0; @@ -44,16 +34,6 @@ static inline uint32_t get_NZ_icc(int32_t dst) } #ifdef TARGET_SPARC64 -static uint32_t compute_all_flags_xcc(CPUSPARCState *env) -{ - return env->xcc & PSR_ICC; -} - -static uint32_t compute_C_flags_xcc(CPUSPARCState *env) -{ - return env->xcc & PSR_CARRY; -} - static inline uint32_t get_NZ_xcc(target_long dst) { uint32_t ret = 0; @@ -422,7 +402,6 @@ typedef struct CCTable { static const CCTable icc_table[CC_OP_NB] = { /* CC_OP_DYNAMIC should never happen */ - [CC_OP_FLAGS] = { compute_all_flags, compute_C_flags }, [CC_OP_DIV] = { compute_all_div, compute_C_div }, [CC_OP_ADD] = { compute_all_add, compute_C_add }, [CC_OP_ADDX] = { compute_all_addx, compute_C_addx }, @@ -438,7 +417,6 @@ static const CCTable icc_table[CC_OP_NB] = { #ifdef TARGET_SPARC64 static const CCTable xcc_table[CC_OP_NB] = { /* CC_OP_DYNAMIC should never happen */ - [CC_OP_FLAGS] = { compute_all_flags_xcc, compute_C_flags_xcc }, [CC_OP_DIV] = { compute_all_logic_xcc, compute_C_logic }, [CC_OP_ADD] = { compute_all_add_xcc, compute_C_add_xcc }, [CC_OP_ADDX] = { compute_all_addx_xcc, compute_C_addx_xcc }, @@ -454,18 +432,37 @@ static const CCTable xcc_table[CC_OP_NB] = { void helper_compute_psr(CPUSPARCState *env) { - uint32_t new_psr; + if (CC_OP == CC_OP_FLAGS) { + return; + } - new_psr = icc_table[CC_OP].compute_all(env); - env->psr = new_psr; + uint32_t icc = icc_table[CC_OP].compute_all(env); #ifdef TARGET_SPARC64 - new_psr = xcc_table[CC_OP].compute_all(env); - env->xcc = new_psr; + uint32_t xcc = xcc_table[CC_OP].compute_all(env); + + env->cc_N = deposit64(-(icc & PSR_NEG), 32, 32, -(xcc & PSR_NEG)); + env->cc_V = deposit64(-(icc & PSR_OVF), 32, 32, -(xcc & PSR_OVF)); + env->icc_C = (uint64_t)icc << (32 - PSR_CARRY_SHIFT); + env->xcc_C = (xcc >> PSR_CARRY_SHIFT) & 1; + env->xcc_Z = ~xcc & PSR_ZERO; +#else + env->cc_N = -(icc & PSR_NEG); + env->cc_V = -(icc & PSR_OVF); + env->icc_C = (icc >> PSR_CARRY_SHIFT) & 1; #endif + env->icc_Z = ~icc & PSR_ZERO; + CC_OP = CC_OP_FLAGS; } uint32_t helper_compute_C_icc(CPUSPARCState *env) { + if (CC_OP == CC_OP_FLAGS) { +#ifdef TARGET_SPARC64 + return extract64(env->icc_C, 32, 1); +#else + return env->icc_C; +#endif + } return icc_table[CC_OP].compute_c(env) >> PSR_CARRY_SHIFT; } diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index 955329f6c9..ea8a04c6e3 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -149,7 +149,7 @@ enum { */ enum { CC_OP_DYNAMIC, /* must use dynamic code to get cc_op */ - CC_OP_FLAGS, /* all cc are back in status register */ + CC_OP_FLAGS, /* all cc are back in cc_*_[NZCV] registers */ CC_OP_DIV, /* modify N, Z and V, C = 0*/ CC_OP_ADD, /* modify all flags, CC_DST = res, CC_SRC = src1 */ CC_OP_ADDX, /* modify all flags, CC_DST = res, CC_SRC = src1 */ @@ -458,6 +458,32 @@ struct CPUArchState { target_ulong npc; /* next program counter */ target_ulong y; /* multiply/divide register */ + /* + * Bit 31 is for icc, bit 63 for xcc. + * Other bits are garbage. + */ + target_long cc_N; + target_long cc_V; + + /* + * Z is represented as == 0; any non-zero value is !Z. + * For sparc64, the high 32-bits of icc.Z are garbage. + */ + target_ulong icc_Z; +#ifdef TARGET_SPARC64 + target_ulong xcc_Z; +#endif + + /* + * For sparc32, icc.C is boolean. + * For sparc64, xcc.C is boolean; + * icc.C is bit 32 with other bits garbage. + */ + target_ulong icc_C; +#ifdef TARGET_SPARC64 + target_ulong xcc_C; +#endif + /* emulator internal flags handling */ target_ulong cc_src, cc_src2; target_ulong cc_dst; @@ -466,7 +492,6 @@ struct CPUArchState { target_ulong cond; /* conditional branch result (XXX: save it in a temporary register when possible) */ - uint32_t psr; /* processor state register */ target_ulong fsr; /* FPU state register */ CPU_DoubleU fpr[TARGET_DPREGS]; /* floating point registers */ uint32_t cwp; /* index of current register window (extracted @@ -522,7 +547,6 @@ struct CPUArchState { #define MAXTL_MAX 8 #define MAXTL_MASK (MAXTL_MAX - 1) trap_state ts[MAXTL_MAX]; - uint32_t xcc; /* Extended integer condition codes */ uint32_t asi; uint32_t pstate; uint32_t tl; diff --git a/target/sparc/machine.c b/target/sparc/machine.c index 274e1217df..44dfc07014 100644 --- a/target/sparc/machine.c +++ b/target/sparc/machine.c @@ -83,6 +83,42 @@ static const VMStateInfo vmstate_psr = { .put = put_psr, }; +#ifdef TARGET_SPARC64 +static int get_xcc(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field) +{ + SPARCCPU *cpu = opaque; + CPUSPARCState *env = &cpu->env; + uint32_t val = qemu_get_be32(f); + + /* Do not clobber icc.[NV] */ + env->cc_N = deposit64(env->cc_N, 32, 32, -(val & PSR_NEG)); + env->cc_V = deposit64(env->cc_V, 32, 32, -(val & PSR_OVF)); + env->xcc_Z = ~val & PSR_ZERO; + env->xcc_C = (val >> PSR_CARRY_SHIFT) & 1; + + return 0; +} + +static int put_xcc(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field, JSONWriter *vmdesc) +{ + SPARCCPU *cpu = opaque; + CPUSPARCState *env = &cpu->env; + uint32_t val = cpu_get_ccr(env); + + /* Extract just xcc out of ccr and shift into legacy position. */ + qemu_put_be32(f, (val & 0xf0) << (20 - 4)); + return 0; +} + +static const VMStateInfo vmstate_xcc = { + .name = "xcc", + .get = get_xcc, + .put = put_xcc, +}; +#endif + static int cpu_pre_save(void *opaque) { SPARCCPU *cpu = opaque; @@ -155,7 +191,14 @@ const VMStateDescription vmstate_sparc_cpu = { VMSTATE_UINT32(env.mmu_version, SPARCCPU), VMSTATE_STRUCT_ARRAY(env.ts, SPARCCPU, MAXTL_MAX, 0, vmstate_trap_state, trap_state), - VMSTATE_UINT32(env.xcc, SPARCCPU), + { + .name = "xcc", + .version_id = 0, + .size = sizeof(uint32_t), + .info = &vmstate_xcc, + .flags = VMS_SINGLE, + .offset = 0, + }, VMSTATE_UINT32(env.asi, SPARCCPU), VMSTATE_UINT32(env.pstate, SPARCCPU), VMSTATE_UINT32(env.tl, SPARCCPU), diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 986a88c4e1..261f142636 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -107,19 +107,35 @@ static TCGv_ptr cpu_regwptr; static TCGv cpu_cc_src, cpu_cc_src2, cpu_cc_dst; static TCGv_i32 cpu_cc_op; -static TCGv_i32 cpu_psr; static TCGv cpu_fsr, cpu_pc, cpu_npc; static TCGv cpu_regs[32]; static TCGv cpu_y; static TCGv cpu_tbr; static TCGv cpu_cond; +static TCGv cpu_cc_N; +static TCGv cpu_cc_V; +static TCGv cpu_icc_Z; +static TCGv cpu_icc_C; #ifdef TARGET_SPARC64 -static TCGv_i32 cpu_xcc, cpu_fprs; +static TCGv cpu_xcc_Z; +static TCGv cpu_xcc_C; +static TCGv_i32 cpu_fprs; static TCGv cpu_gsr; #else # define cpu_fprs ({ qemu_build_not_reached(); (TCGv)NULL; }) # define cpu_gsr ({ qemu_build_not_reached(); (TCGv)NULL; }) #endif + +#ifdef TARGET_SPARC64 +#define cpu_cc_Z cpu_xcc_Z +#define cpu_cc_C cpu_xcc_C +#else +#define cpu_cc_Z cpu_icc_Z +#define cpu_cc_C cpu_icc_C +#define cpu_xcc_Z ({ qemu_build_not_reached(); NULL; }) +#define cpu_xcc_C ({ qemu_build_not_reached(); NULL; }) +#endif + /* Floating point registers */ static TCGv_i64 cpu_fpr[TARGET_DPREGS]; @@ -366,31 +382,6 @@ static void gen_goto_tb(DisasContext *s, int tb_num, } } -// XXX suboptimal -static void gen_mov_reg_N(TCGv reg, TCGv_i32 src) -{ - tcg_gen_extu_i32_tl(reg, src); - tcg_gen_extract_tl(reg, reg, PSR_NEG_SHIFT, 1); -} - -static void gen_mov_reg_Z(TCGv reg, TCGv_i32 src) -{ - tcg_gen_extu_i32_tl(reg, src); - tcg_gen_extract_tl(reg, reg, PSR_ZERO_SHIFT, 1); -} - -static void gen_mov_reg_V(TCGv reg, TCGv_i32 src) -{ - tcg_gen_extu_i32_tl(reg, src); - tcg_gen_extract_tl(reg, reg, PSR_OVF_SHIFT, 1); -} - -static void gen_mov_reg_C(TCGv reg, TCGv_i32 src) -{ - tcg_gen_extu_i32_tl(reg, src); - tcg_gen_extract_tl(reg, reg, PSR_CARRY_SHIFT, 1); -} - static void gen_op_add_cc(TCGv dst, TCGv src1, TCGv src2) { tcg_gen_mov_tl(cpu_cc_src, src1); @@ -640,13 +631,11 @@ static void gen_op_mulscc(TCGv dst, TCGv src1, TCGv src2) tcg_gen_deposit_tl(cpu_y, t0, cpu_cc_src, 31, 1); // b1 = N ^ V; - gen_mov_reg_N(t0, cpu_psr); - gen_mov_reg_V(r_temp, cpu_psr); - tcg_gen_xor_tl(t0, t0, r_temp); + tcg_gen_xor_tl(t0, cpu_cc_N, cpu_cc_V); // T0 = (b1 << 31) | (T0 >> 1); // src1 = T0; - tcg_gen_shli_tl(t0, t0, 31); + tcg_gen_andi_tl(t0, t0, 1u << 31); tcg_gen_shri_tl(cpu_cc_src, cpu_cc_src, 1); tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, t0); @@ -825,114 +814,12 @@ static void gen_op_eval_ba(TCGv dst) tcg_gen_movi_tl(dst, 1); } -// Z -static void gen_op_eval_be(TCGv dst, TCGv_i32 src) -{ - gen_mov_reg_Z(dst, src); -} - -// Z | (N ^ V) -static void gen_op_eval_ble(TCGv dst, TCGv_i32 src) -{ - TCGv t0 = tcg_temp_new(); - gen_mov_reg_N(t0, src); - gen_mov_reg_V(dst, src); - tcg_gen_xor_tl(dst, dst, t0); - gen_mov_reg_Z(t0, src); - tcg_gen_or_tl(dst, dst, t0); -} - -// N ^ V -static void gen_op_eval_bl(TCGv dst, TCGv_i32 src) -{ - TCGv t0 = tcg_temp_new(); - gen_mov_reg_V(t0, src); - gen_mov_reg_N(dst, src); - tcg_gen_xor_tl(dst, dst, t0); -} - -// C | Z -static void gen_op_eval_bleu(TCGv dst, TCGv_i32 src) -{ - TCGv t0 = tcg_temp_new(); - gen_mov_reg_Z(t0, src); - gen_mov_reg_C(dst, src); - tcg_gen_or_tl(dst, dst, t0); -} - -// C -static void gen_op_eval_bcs(TCGv dst, TCGv_i32 src) -{ - gen_mov_reg_C(dst, src); -} - -// V -static void gen_op_eval_bvs(TCGv dst, TCGv_i32 src) -{ - gen_mov_reg_V(dst, src); -} - // 0 static void gen_op_eval_bn(TCGv dst) { tcg_gen_movi_tl(dst, 0); } -// N -static void gen_op_eval_bneg(TCGv dst, TCGv_i32 src) -{ - gen_mov_reg_N(dst, src); -} - -// !Z -static void gen_op_eval_bne(TCGv dst, TCGv_i32 src) -{ - gen_mov_reg_Z(dst, src); - tcg_gen_xori_tl(dst, dst, 0x1); -} - -// !(Z | (N ^ V)) -static void gen_op_eval_bg(TCGv dst, TCGv_i32 src) -{ - gen_op_eval_ble(dst, src); - tcg_gen_xori_tl(dst, dst, 0x1); -} - -// !(N ^ V) -static void gen_op_eval_bge(TCGv dst, TCGv_i32 src) -{ - gen_op_eval_bl(dst, src); - tcg_gen_xori_tl(dst, dst, 0x1); -} - -// !(C | Z) -static void gen_op_eval_bgu(TCGv dst, TCGv_i32 src) -{ - gen_op_eval_bleu(dst, src); - tcg_gen_xori_tl(dst, dst, 0x1); -} - -// !C -static void gen_op_eval_bcc(TCGv dst, TCGv_i32 src) -{ - gen_mov_reg_C(dst, src); - tcg_gen_xori_tl(dst, dst, 0x1); -} - -// !N -static void gen_op_eval_bpos(TCGv dst, TCGv_i32 src) -{ - gen_mov_reg_N(dst, src); - tcg_gen_xori_tl(dst, dst, 0x1); -} - -// !V -static void gen_op_eval_bvc(TCGv dst, TCGv_i32 src) -{ - gen_mov_reg_V(dst, src); - tcg_gen_xori_tl(dst, dst, 0x1); -} - /* FPSR bit field FCC1 | FCC0: 0 = @@ -1249,34 +1136,22 @@ static void gen_compare(DisasCompare *cmp, bool xcc, unsigned int cond, TCG_COND_ALWAYS, /* vc: !V -> 1 */ }; - TCGv_i32 r_src; - TCGv r_dst; + TCGv t1, t2; -#ifdef TARGET_SPARC64 - if (xcc) { - r_src = cpu_xcc; - } else { - r_src = cpu_psr; - } -#else - r_src = cpu_psr; -#endif + cmp->is_bool = false; switch (dc->cc_op) { case CC_OP_LOGIC: cmp->cond = logic_cond[cond]; do_compare_dst_0: - cmp->is_bool = false; cmp->c2 = tcg_constant_tl(0); -#ifdef TARGET_SPARC64 - if (!xcc) { - cmp->c1 = tcg_temp_new(); - tcg_gen_ext32s_tl(cmp->c1, cpu_cc_dst); - break; + if (TARGET_LONG_BITS == 32 || xcc) { + cmp->c1 = cpu_cc_dst; + } else { + cmp->c1 = t1 = tcg_temp_new(); + tcg_gen_ext32s_tl(t1, cpu_cc_dst); } -#endif - cmp->c1 = cpu_cc_dst; - break; + return; case CC_OP_SUB: switch (cond) { @@ -1287,92 +1162,127 @@ static void gen_compare(DisasCompare *cmp, bool xcc, unsigned int cond, case 7: /* overflow */ case 15: /* !overflow */ - goto do_dynamic; + break; default: cmp->cond = subcc_cond[cond]; - cmp->is_bool = false; -#ifdef TARGET_SPARC64 - if (!xcc) { + if (TARGET_LONG_BITS == 32 || xcc) { + cmp->c1 = cpu_cc_src; + cmp->c2 = cpu_cc_src2; + } else { /* Note that sign-extension works for unsigned compares as long as both operands are sign-extended. */ - cmp->c1 = tcg_temp_new(); - cmp->c2 = tcg_temp_new(); - tcg_gen_ext32s_tl(cmp->c1, cpu_cc_src); - tcg_gen_ext32s_tl(cmp->c2, cpu_cc_src2); - break; + cmp->c1 = t1 = tcg_temp_new(); + tcg_gen_ext32s_tl(t1, cpu_cc_src); + cmp->c2 = t2 = tcg_temp_new(); + tcg_gen_ext32s_tl(t2, cpu_cc_src2); } -#endif - cmp->c1 = cpu_cc_src; - cmp->c2 = cpu_cc_src2; - break; + return; } break; default: - do_dynamic: gen_helper_compute_psr(tcg_env); dc->cc_op = CC_OP_FLAGS; - /* FALLTHRU */ + break; case CC_OP_FLAGS: - /* We're going to generate a boolean result. */ - cmp->cond = TCG_COND_NE; - cmp->is_bool = true; - cmp->c1 = r_dst = tcg_temp_new(); - cmp->c2 = tcg_constant_tl(0); + break; + } - switch (cond) { - case 0x0: - gen_op_eval_bn(r_dst); - break; - case 0x1: - gen_op_eval_be(r_dst, r_src); - break; - case 0x2: - gen_op_eval_ble(r_dst, r_src); - break; - case 0x3: - gen_op_eval_bl(r_dst, r_src); - break; - case 0x4: - gen_op_eval_bleu(r_dst, r_src); - break; - case 0x5: - gen_op_eval_bcs(r_dst, r_src); - break; - case 0x6: - gen_op_eval_bneg(r_dst, r_src); - break; - case 0x7: - gen_op_eval_bvs(r_dst, r_src); - break; - case 0x8: - gen_op_eval_ba(r_dst); - break; - case 0x9: - gen_op_eval_bne(r_dst, r_src); - break; - case 0xa: - gen_op_eval_bg(r_dst, r_src); - break; - case 0xb: - gen_op_eval_bge(r_dst, r_src); - break; - case 0xc: - gen_op_eval_bgu(r_dst, r_src); - break; - case 0xd: - gen_op_eval_bcc(r_dst, r_src); - break; - case 0xe: - gen_op_eval_bpos(r_dst, r_src); - break; - case 0xf: - gen_op_eval_bvc(r_dst, r_src); - break; + cmp->c1 = t1 = tcg_temp_new(); + cmp->c2 = tcg_constant_tl(0); + + switch (cond & 7) { + case 0x0: /* never */ + cmp->cond = TCG_COND_NEVER; + cmp->c1 = cmp->c2; + break; + + case 0x1: /* eq: Z */ + cmp->cond = TCG_COND_EQ; + if (TARGET_LONG_BITS == 32 || xcc) { + tcg_gen_mov_tl(t1, cpu_cc_Z); + } else { + tcg_gen_ext32u_tl(t1, cpu_icc_Z); } break; + + case 0x2: /* le: Z | (N ^ V) */ + /* + * Simplify: + * cc_Z || (N ^ V) < 0 NE + * cc_Z && !((N ^ V) < 0) EQ + * cc_Z & ~((N ^ V) >> TLB) EQ + */ + cmp->cond = TCG_COND_EQ; + tcg_gen_xor_tl(t1, cpu_cc_N, cpu_cc_V); + tcg_gen_sextract_tl(t1, t1, xcc ? 63 : 31, 1); + tcg_gen_andc_tl(t1, xcc ? cpu_cc_Z : cpu_icc_Z, t1); + if (TARGET_LONG_BITS == 64 && !xcc) { + tcg_gen_ext32u_tl(t1, t1); + } + break; + + case 0x3: /* lt: N ^ V */ + cmp->cond = TCG_COND_LT; + tcg_gen_xor_tl(t1, cpu_cc_N, cpu_cc_V); + if (TARGET_LONG_BITS == 64 && !xcc) { + tcg_gen_ext32s_tl(t1, t1); + } + break; + + case 0x4: /* leu: Z | C */ + /* + * Simplify: + * cc_Z == 0 || cc_C != 0 NE + * cc_Z != 0 && cc_C == 0 EQ + * cc_Z & (cc_C ? 0 : -1) EQ + * cc_Z & (cc_C - 1) EQ + */ + cmp->cond = TCG_COND_EQ; + if (TARGET_LONG_BITS == 32 || xcc) { + tcg_gen_subi_tl(t1, cpu_cc_C, 1); + tcg_gen_and_tl(t1, t1, cpu_cc_Z); + } else { + tcg_gen_extract_tl(t1, cpu_icc_C, 32, 1); + tcg_gen_subi_tl(t1, t1, 1); + tcg_gen_and_tl(t1, t1, cpu_icc_Z); + tcg_gen_ext32u_tl(t1, t1); + } + break; + + case 0x5: /* ltu: C */ + cmp->cond = TCG_COND_NE; + cmp->is_bool = true; + if (TARGET_LONG_BITS == 32 || xcc) { + tcg_gen_mov_tl(t1, cpu_cc_C); + } else { + tcg_gen_extract_tl(t1, cpu_icc_C, 32, 1); + } + break; + + case 0x6: /* neg: N */ + cmp->cond = TCG_COND_LT; + if (TARGET_LONG_BITS == 32 || xcc) { + tcg_gen_mov_tl(t1, cpu_cc_N); + } else { + tcg_gen_ext32s_tl(t1, cpu_cc_N); + } + break; + + case 0x7: /* vs: V */ + cmp->cond = TCG_COND_LT; + if (TARGET_LONG_BITS == 32 || xcc) { + tcg_gen_mov_tl(t1, cpu_cc_V); + } else { + tcg_gen_ext32s_tl(t1, cpu_cc_V); + } + break; + } + if (cond & 8) { + cmp->cond = tcg_invert_cond(cmp->cond); + cmp->is_bool = false; } } @@ -5513,17 +5423,21 @@ void sparc_tcg_init(void) static const struct { TCGv_i32 *ptr; int off; const char *name; } r32[] = { #ifdef TARGET_SPARC64 - { &cpu_xcc, offsetof(CPUSPARCState, xcc), "xcc" }, { &cpu_fprs, offsetof(CPUSPARCState, fprs), "fprs" }, #endif { &cpu_cc_op, offsetof(CPUSPARCState, cc_op), "cc_op" }, - { &cpu_psr, offsetof(CPUSPARCState, psr), "psr" }, }; static const struct { TCGv *ptr; int off; const char *name; } rtl[] = { #ifdef TARGET_SPARC64 { &cpu_gsr, offsetof(CPUSPARCState, gsr), "gsr" }, + { &cpu_xcc_Z, offsetof(CPUSPARCState, xcc_Z), "xcc_Z" }, + { &cpu_xcc_C, offsetof(CPUSPARCState, xcc_C), "xcc_C" }, #endif + { &cpu_cc_N, offsetof(CPUSPARCState, cc_N), "cc_N" }, + { &cpu_cc_V, offsetof(CPUSPARCState, cc_V), "cc_V" }, + { &cpu_icc_Z, offsetof(CPUSPARCState, icc_Z), "icc_Z" }, + { &cpu_icc_C, offsetof(CPUSPARCState, icc_C), "icc_C" }, { &cpu_cond, offsetof(CPUSPARCState, cond), "cond" }, { &cpu_cc_src, offsetof(CPUSPARCState, cc_src), "cc_src" }, { &cpu_cc_src2, offsetof(CPUSPARCState, cc_src2), "cc_src2" }, diff --git a/target/sparc/win_helper.c b/target/sparc/win_helper.c index bf2c90c780..f0ff6bf5db 100644 --- a/target/sparc/win_helper.c +++ b/target/sparc/win_helper.c @@ -53,23 +53,44 @@ void cpu_set_cwp(CPUSPARCState *env, int new_cwp) target_ulong cpu_get_psr(CPUSPARCState *env) { + target_ulong icc = 0; + helper_compute_psr(env); + icc |= ((int32_t)env->cc_N < 0) << PSR_NEG_SHIFT; + icc |= ((int32_t)env->cc_V < 0) << PSR_OVF_SHIFT; + icc |= ((int32_t)env->icc_Z == 0) << PSR_ZERO_SHIFT; + if (TARGET_LONG_BITS == 64) { + icc |= extract64(env->icc_C, 32, 1) << PSR_CARRY_SHIFT; + } else { + icc |= env->icc_C << PSR_CARRY_SHIFT; + } + #if !defined(TARGET_SPARC64) - return env->version | (env->psr & PSR_ICC) | + return env->version | icc | (env->psref ? PSR_EF : 0) | (env->psrpil << 8) | (env->psrs ? PSR_S : 0) | (env->psrps ? PSR_PS : 0) | (env->psret ? PSR_ET : 0) | env->cwp; #else - return env->psr & PSR_ICC; + return icc; #endif } void cpu_put_psr_icc(CPUSPARCState *env, target_ulong val) { - env->psr = val & PSR_ICC; + if (TARGET_LONG_BITS == 64) { + /* Do not clobber xcc.[NV] */ + env->cc_N = deposit64(env->cc_N, 0, 32, -(val & PSR_NEG)); + env->cc_V = deposit64(env->cc_V, 0, 32, -(val & PSR_OVF)); + env->icc_C = -(val & PSR_CARRY); + } else { + env->cc_N = -(val & PSR_NEG); + env->cc_V = -(val & PSR_OVF); + env->icc_C = (val >> PSR_CARRY_SHIFT) & 1; + } + env->icc_Z = ~val & PSR_ZERO; } void cpu_put_psr_raw(CPUSPARCState *env, target_ulong val) @@ -249,17 +270,32 @@ void helper_restored(CPUSPARCState *env) target_ulong cpu_get_ccr(CPUSPARCState *env) { - target_ulong psr; + target_ulong ccr = 0; - psr = cpu_get_psr(env); + helper_compute_psr(env); - return ((env->xcc >> 20) << 4) | ((psr & PSR_ICC) >> 20); + ccr |= (env->icc_C >> 32) & 1; + ccr |= ((int32_t)env->cc_V < 0) << 1; + ccr |= ((int32_t)env->icc_Z == 0) << 2; + ccr |= ((int32_t)env->cc_N < 0) << 3; + + ccr |= env->xcc_C << 4; + ccr |= (env->cc_V < 0) << 5; + ccr |= (env->xcc_Z == 0) << 6; + ccr |= (env->cc_N < 0) << 7; + + return ccr; } void cpu_put_ccr(CPUSPARCState *env, target_ulong val) { - env->xcc = (val >> 4) << 20; - env->psr = (val & 0xf) << 20; + env->cc_N = deposit64(-(val & 0x08), 32, 32, -(val & 0x80)); + env->cc_V = deposit64(-(val & 0x02), 32, 32, -(val & 0x20)); + env->icc_C = (uint64_t)val << 32; + env->xcc_C = (val >> 4) & 1; + env->icc_Z = ~val & 0x04; + env->xcc_Z = ~val & 0x40; + CC_OP = CC_OP_FLAGS; } From 2a45b73658c383012a661cbfb89462c778c1ce45 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 14 Oct 2023 20:15:19 -0700 Subject: [PATCH 423/974] target/sparc: Remove CC_OP_LOGIC Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/cc_helper.c | 14 +-------- target/sparc/cpu.h | 1 - target/sparc/translate.c | 66 ++++++++++++++++------------------------ 3 files changed, 28 insertions(+), 53 deletions(-) diff --git a/target/sparc/cc_helper.c b/target/sparc/cc_helper.c index 46bec69d96..1622300a14 100644 --- a/target/sparc/cc_helper.c +++ b/target/sparc/cc_helper.c @@ -378,16 +378,6 @@ static uint32_t compute_all_tsubtv(CPUSPARCState *env) return ret; } -static uint32_t compute_all_logic(CPUSPARCState *env) -{ - return get_NZ_icc(CC_DST); -} - -static uint32_t compute_C_logic(CPUSPARCState *env) -{ - return 0; -} - #ifdef TARGET_SPARC64 static uint32_t compute_all_logic_xcc(CPUSPARCState *env) { @@ -411,13 +401,12 @@ static const CCTable icc_table[CC_OP_NB] = { [CC_OP_SUBX] = { compute_all_subx, compute_C_subx }, [CC_OP_TSUB] = { compute_all_tsub, compute_C_sub }, [CC_OP_TSUBTV] = { compute_all_tsubtv, compute_C_sub }, - [CC_OP_LOGIC] = { compute_all_logic, compute_C_logic }, }; #ifdef TARGET_SPARC64 static const CCTable xcc_table[CC_OP_NB] = { /* CC_OP_DYNAMIC should never happen */ - [CC_OP_DIV] = { compute_all_logic_xcc, compute_C_logic }, + [CC_OP_DIV] = { compute_all_logic_xcc, compute_C_div }, [CC_OP_ADD] = { compute_all_add_xcc, compute_C_add_xcc }, [CC_OP_ADDX] = { compute_all_addx_xcc, compute_C_addx_xcc }, [CC_OP_TADD] = { compute_all_add_xcc, compute_C_add_xcc }, @@ -426,7 +415,6 @@ static const CCTable xcc_table[CC_OP_NB] = { [CC_OP_SUBX] = { compute_all_subx_xcc, compute_C_subx_xcc }, [CC_OP_TSUB] = { compute_all_sub_xcc, compute_C_sub_xcc }, [CC_OP_TSUBTV] = { compute_all_sub_xcc, compute_C_sub_xcc }, - [CC_OP_LOGIC] = { compute_all_logic_xcc, compute_C_logic }, }; #endif diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index ea8a04c6e3..202c34f7ca 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -159,7 +159,6 @@ enum { CC_OP_SUBX, /* modify all flags, CC_DST = res, CC_SRC = src1 */ CC_OP_TSUB, /* modify all flags, CC_DST = res, CC_SRC = src1 */ CC_OP_TSUBTV, /* modify all flags except V, CC_DST = res, CC_SRC = src1 */ - CC_OP_LOGIC, /* modify N and Z, C = V = 0, CC_DST = res */ CC_OP_NB, }; diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 261f142636..b11d89343b 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -1117,48 +1117,24 @@ static void gen_compare(DisasCompare *cmp, bool xcc, unsigned int cond, -1, /* no overflow */ }; - static int logic_cond[16] = { - TCG_COND_NEVER, - TCG_COND_EQ, /* eq: Z */ - TCG_COND_LE, /* le: Z | (N ^ V) -> Z | N */ - TCG_COND_LT, /* lt: N ^ V -> N */ - TCG_COND_EQ, /* leu: C | Z -> Z */ - TCG_COND_NEVER, /* ltu: C -> 0 */ - TCG_COND_LT, /* neg: N */ - TCG_COND_NEVER, /* vs: V -> 0 */ - TCG_COND_ALWAYS, - TCG_COND_NE, /* ne: !Z */ - TCG_COND_GT, /* gt: !(Z | (N ^ V)) -> !(Z | N) */ - TCG_COND_GE, /* ge: !(N ^ V) -> !N */ - TCG_COND_NE, /* gtu: !(C | Z) -> !Z */ - TCG_COND_ALWAYS, /* geu: !C -> 1 */ - TCG_COND_GE, /* pos: !N */ - TCG_COND_ALWAYS, /* vc: !V -> 1 */ - }; - TCGv t1, t2; cmp->is_bool = false; switch (dc->cc_op) { - case CC_OP_LOGIC: - cmp->cond = logic_cond[cond]; - do_compare_dst_0: - cmp->c2 = tcg_constant_tl(0); - if (TARGET_LONG_BITS == 32 || xcc) { - cmp->c1 = cpu_cc_dst; - } else { - cmp->c1 = t1 = tcg_temp_new(); - tcg_gen_ext32s_tl(t1, cpu_cc_dst); - } - return; - case CC_OP_SUB: switch (cond) { case 6: /* neg */ case 14: /* pos */ cmp->cond = (cond == 6 ? TCG_COND_LT : TCG_COND_GE); - goto do_compare_dst_0; + cmp->c2 = tcg_constant_tl(0); + if (TARGET_LONG_BITS == 32 || xcc) { + cmp->c1 = cpu_cc_dst; + } else { + cmp->c1 = t1 = tcg_temp_new(); + tcg_gen_ext32s_tl(t1, cpu_cc_dst); + } + return; case 7: /* overflow */ case 15: /* !overflow */ @@ -3652,7 +3628,8 @@ TRANS(NOP_v9, 64, trans_NOP, a) static bool do_arith_int(DisasContext *dc, arg_r_r_ri_cc *a, int cc_op, void (*func)(TCGv, TCGv, TCGv), - void (*funci)(TCGv, TCGv, target_long)) + void (*funci)(TCGv, TCGv, target_long), + bool logic_cc) { TCGv dst, src1; @@ -3661,7 +3638,9 @@ static bool do_arith_int(DisasContext *dc, arg_r_r_ri_cc *a, int cc_op, return false; } - if (a->cc) { + if (logic_cc) { + dst = cpu_cc_N; + } else if (a->cc && cc_op > CC_OP_FLAGS) { dst = cpu_cc_dst; } else { dst = gen_dest_gpr(dc, a->rd); @@ -3677,6 +3656,17 @@ static bool do_arith_int(DisasContext *dc, arg_r_r_ri_cc *a, int cc_op, } else { func(dst, src1, cpu_regs[a->rs2_or_imm]); } + + if (logic_cc) { + if (TARGET_LONG_BITS == 64) { + tcg_gen_mov_tl(cpu_icc_Z, cpu_cc_N); + tcg_gen_movi_tl(cpu_icc_C, 0); + } + tcg_gen_mov_tl(cpu_cc_Z, cpu_cc_N); + tcg_gen_movi_tl(cpu_cc_C, 0); + tcg_gen_movi_tl(cpu_cc_V, 0); + } + gen_store_gpr(dc, a->rd, dst); if (a->cc) { @@ -3693,16 +3683,16 @@ static bool do_arith(DisasContext *dc, arg_r_r_ri_cc *a, int cc_op, { if (a->cc) { assert(cc_op >= 0); - return do_arith_int(dc, a, cc_op, func_cc, NULL); + return do_arith_int(dc, a, cc_op, func_cc, NULL, false); } - return do_arith_int(dc, a, cc_op, func, funci); + return do_arith_int(dc, a, cc_op, func, funci, false); } static bool do_logic(DisasContext *dc, arg_r_r_ri_cc *a, void (*func)(TCGv, TCGv, TCGv), void (*funci)(TCGv, TCGv, target_long)) { - return do_arith_int(dc, a, CC_OP_LOGIC, func, funci); + return do_arith_int(dc, a, CC_OP_FLAGS, func, funci, a->cc); } TRANS(ADD, ALL, do_arith, a, CC_OP_ADD, @@ -3754,7 +3744,6 @@ static bool trans_ADDC(DisasContext *dc, arg_r_r_ri_cc *a) { switch (dc->cc_op) { case CC_OP_DIV: - case CC_OP_LOGIC: /* Carry is known to be zero. Fall back to plain ADD. */ return do_arith(dc, a, CC_OP_ADD, tcg_gen_add_tl, tcg_gen_addi_tl, gen_op_add_cc); @@ -3778,7 +3767,6 @@ static bool trans_SUBC(DisasContext *dc, arg_r_r_ri_cc *a) { switch (dc->cc_op) { case CC_OP_DIV: - case CC_OP_LOGIC: /* Carry is known to be zero. Fall back to plain SUB. */ return do_arith(dc, a, CC_OP_SUB, tcg_gen_sub_tl, tcg_gen_subi_tl, gen_op_sub_cc); From 1326010322d6690a953722d8aee6465826ce7175 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 14 Oct 2023 19:44:03 -0700 Subject: [PATCH 424/974] target/sparc: Remove CC_OP_DIV Return both result and overflow from helper_[us]div. Compute all flags explicitly in gen_op_[us]divcc. Marginally improve the INT64_MIN special case in helper_sdiv. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/cc_helper.c | 33 ------------- target/sparc/cpu.h | 1 - target/sparc/helper.c | 101 ++++++++++++++------------------------- target/sparc/helper.h | 6 +-- target/sparc/translate.c | 70 ++++++++++++++++++++++----- 5 files changed, 97 insertions(+), 114 deletions(-) diff --git a/target/sparc/cc_helper.c b/target/sparc/cc_helper.c index 1622300a14..5400dfec15 100644 --- a/target/sparc/cc_helper.c +++ b/target/sparc/cc_helper.c @@ -47,30 +47,6 @@ static inline uint32_t get_NZ_xcc(target_long dst) } #endif -static inline uint32_t get_V_div_icc(target_ulong src2) -{ - uint32_t ret = 0; - - if (src2 != 0) { - ret = PSR_OVF; - } - return ret; -} - -static uint32_t compute_all_div(CPUSPARCState *env) -{ - uint32_t ret; - - ret = get_NZ_icc(CC_DST); - ret |= get_V_div_icc(CC_SRC2); - return ret; -} - -static uint32_t compute_C_div(CPUSPARCState *env) -{ - return 0; -} - static inline uint32_t get_C_add_icc(uint32_t dst, uint32_t src1) { uint32_t ret = 0; @@ -378,13 +354,6 @@ static uint32_t compute_all_tsubtv(CPUSPARCState *env) return ret; } -#ifdef TARGET_SPARC64 -static uint32_t compute_all_logic_xcc(CPUSPARCState *env) -{ - return get_NZ_xcc(CC_DST); -} -#endif - typedef struct CCTable { uint32_t (*compute_all)(CPUSPARCState *env); /* return all the flags */ uint32_t (*compute_c)(CPUSPARCState *env); /* return the C flag */ @@ -392,7 +361,6 @@ typedef struct CCTable { static const CCTable icc_table[CC_OP_NB] = { /* CC_OP_DYNAMIC should never happen */ - [CC_OP_DIV] = { compute_all_div, compute_C_div }, [CC_OP_ADD] = { compute_all_add, compute_C_add }, [CC_OP_ADDX] = { compute_all_addx, compute_C_addx }, [CC_OP_TADD] = { compute_all_tadd, compute_C_add }, @@ -406,7 +374,6 @@ static const CCTable icc_table[CC_OP_NB] = { #ifdef TARGET_SPARC64 static const CCTable xcc_table[CC_OP_NB] = { /* CC_OP_DYNAMIC should never happen */ - [CC_OP_DIV] = { compute_all_logic_xcc, compute_C_div }, [CC_OP_ADD] = { compute_all_add_xcc, compute_C_add_xcc }, [CC_OP_ADDX] = { compute_all_addx_xcc, compute_C_addx_xcc }, [CC_OP_TADD] = { compute_all_add_xcc, compute_C_add_xcc }, diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index 202c34f7ca..b16d53b91f 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -150,7 +150,6 @@ enum { enum { CC_OP_DYNAMIC, /* must use dynamic code to get cc_op */ CC_OP_FLAGS, /* all cc are back in cc_*_[NZCV] registers */ - CC_OP_DIV, /* modify N, Z and V, C = 0*/ CC_OP_ADD, /* modify all flags, CC_DST = res, CC_SRC = src1 */ CC_OP_ADDX, /* modify all flags, CC_DST = res, CC_SRC = src1 */ CC_OP_TADD, /* modify all flags, CC_DST = res, CC_SRC = src1 */ diff --git a/target/sparc/helper.c b/target/sparc/helper.c index 2bcdc81d54..53eec693dd 100644 --- a/target/sparc/helper.c +++ b/target/sparc/helper.c @@ -81,79 +81,52 @@ void helper_tick_set_limit(void *opaque, uint64_t limit) } #endif -static target_ulong do_udiv(CPUSPARCState *env, target_ulong a, - target_ulong b, int cc, uintptr_t ra) +uint64_t helper_udiv(CPUSPARCState *env, target_ulong a, target_ulong b) { - int overflow = 0; - uint64_t x0; - uint32_t x1; + uint64_t a64 = (uint32_t)a | ((uint64_t)env->y << 32); + uint32_t b32 = b; + uint32_t r; - x0 = (a & 0xffffffff) | ((int64_t) (env->y) << 32); - x1 = (b & 0xffffffff); - - if (x1 == 0) { - cpu_raise_exception_ra(env, TT_DIV_ZERO, ra); + if (b32 == 0) { + cpu_raise_exception_ra(env, TT_DIV_ZERO, GETPC()); } - x0 = x0 / x1; - if (x0 > UINT32_MAX) { - x0 = UINT32_MAX; - overflow = 1; + a64 /= b32; + r = a64; + if (unlikely(a64 > UINT32_MAX)) { + return -1; /* r = UINT32_MAX, v = 1 */ + } + return r; +} + +uint64_t helper_sdiv(CPUSPARCState *env, target_ulong a, target_ulong b) +{ + int64_t a64 = (uint32_t)a | ((uint64_t)env->y << 32); + int32_t b32 = b; + int32_t r; + + if (b32 == 0) { + cpu_raise_exception_ra(env, TT_DIV_ZERO, GETPC()); } - if (cc) { - env->cc_src2 = overflow; - } - return x0; -} - -target_ulong helper_udiv(CPUSPARCState *env, target_ulong a, target_ulong b) -{ - return do_udiv(env, a, b, 0, GETPC()); -} - -target_ulong helper_udiv_cc(CPUSPARCState *env, target_ulong a, target_ulong b) -{ - return do_udiv(env, a, b, 1, GETPC()); -} - -static target_ulong do_sdiv(CPUSPARCState *env, target_ulong a, - target_ulong b, int cc, uintptr_t ra) -{ - int overflow = 0; - int64_t x0; - int32_t x1; - - x0 = (a & 0xffffffff) | ((int64_t) (env->y) << 32); - x1 = (b & 0xffffffff); - - if (x1 == 0) { - cpu_raise_exception_ra(env, TT_DIV_ZERO, ra); - } else if (x1 == -1 && x0 == INT64_MIN) { - x0 = INT32_MAX; - overflow = 1; - } else { - x0 = x0 / x1; - if ((int32_t) x0 != x0) { - x0 = x0 < 0 ? INT32_MIN : INT32_MAX; - overflow = 1; - } + if (unlikely(a64 == INT64_MIN)) { + /* + * Special case INT64_MIN / -1 is required to avoid trap on x86 host. + * However, with a dividend of INT64_MIN, there is no 32-bit divisor + * which can yield a 32-bit result: + * INT64_MIN / INT32_MIN = 0x1_0000_0000 + * INT64_MIN / INT32_MAX = -0x1_0000_0002 + * Therefore we know we must overflow and saturate. + */ + return (uint32_t)(b32 < 0 ? INT32_MAX : INT32_MIN) | (-1ull << 32); } - if (cc) { - env->cc_src2 = overflow; + a64 /= b; + r = a64; + if (unlikely(r != a64)) { + return (uint32_t)(a64 < 0 ? INT32_MIN : INT32_MAX) | (-1ull << 32); } - return x0; -} - -target_ulong helper_sdiv(CPUSPARCState *env, target_ulong a, target_ulong b) -{ - return do_sdiv(env, a, b, 0, GETPC()); -} - -target_ulong helper_sdiv_cc(CPUSPARCState *env, target_ulong a, target_ulong b) -{ - return do_sdiv(env, a, b, 1, GETPC()); + return (uint32_t)r; } #ifdef TARGET_SPARC64 diff --git a/target/sparc/helper.h b/target/sparc/helper.h index dd1721a340..a7b0079c3b 100644 --- a/target/sparc/helper.h +++ b/target/sparc/helper.h @@ -27,10 +27,8 @@ DEF_HELPER_FLAGS_2(tick_set_limit, TCG_CALL_NO_RWG, void, ptr, i64) DEF_HELPER_1(debug, void, env) DEF_HELPER_1(save, void, env) DEF_HELPER_1(restore, void, env) -DEF_HELPER_3(udiv, tl, env, tl, tl) -DEF_HELPER_3(udiv_cc, tl, env, tl, tl) -DEF_HELPER_3(sdiv, tl, env, tl, tl) -DEF_HELPER_3(sdiv_cc, tl, env, tl, tl) +DEF_HELPER_FLAGS_3(udiv, TCG_CALL_NO_WG, i64, env, tl, tl) +DEF_HELPER_FLAGS_3(sdiv, TCG_CALL_NO_WG, i64, env, tl, tl) DEF_HELPER_3(taddcctv, tl, env, tl, tl) DEF_HELPER_3(tsubcctv, tl, env, tl, tl) #ifdef TARGET_SPARC64 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index b11d89343b..fa4bad6d1f 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -693,22 +693,76 @@ static void gen_op_sdivx(TCGv dst, TCGv src1, TCGv src2) static void gen_op_udiv(TCGv dst, TCGv src1, TCGv src2) { +#ifdef TARGET_SPARC64 gen_helper_udiv(dst, tcg_env, src1, src2); + tcg_gen_ext32u_tl(dst, dst); +#else + TCGv_i64 t64 = tcg_temp_new_i64(); + gen_helper_udiv(t64, tcg_env, src1, src2); + tcg_gen_trunc_i64_tl(dst, t64); +#endif } static void gen_op_sdiv(TCGv dst, TCGv src1, TCGv src2) { +#ifdef TARGET_SPARC64 gen_helper_sdiv(dst, tcg_env, src1, src2); + tcg_gen_ext32s_tl(dst, dst); +#else + TCGv_i64 t64 = tcg_temp_new_i64(); + gen_helper_sdiv(t64, tcg_env, src1, src2); + tcg_gen_trunc_i64_tl(dst, t64); +#endif } static void gen_op_udivcc(TCGv dst, TCGv src1, TCGv src2) { - gen_helper_udiv_cc(dst, tcg_env, src1, src2); + TCGv_i64 t64; + +#ifdef TARGET_SPARC64 + t64 = cpu_cc_V; +#else + t64 = tcg_temp_new_i64(); +#endif + + gen_helper_udiv(t64, tcg_env, src1, src2); + +#ifdef TARGET_SPARC64 + tcg_gen_ext32u_tl(cpu_cc_N, t64); + tcg_gen_shri_tl(cpu_cc_V, t64, 32); + tcg_gen_mov_tl(cpu_icc_Z, cpu_cc_N); + tcg_gen_movi_tl(cpu_icc_C, 0); +#else + tcg_gen_extr_i64_tl(cpu_cc_N, cpu_cc_V, t64); +#endif + tcg_gen_mov_tl(cpu_cc_Z, cpu_cc_N); + tcg_gen_movi_tl(cpu_cc_C, 0); + tcg_gen_mov_tl(dst, cpu_cc_N); } static void gen_op_sdivcc(TCGv dst, TCGv src1, TCGv src2) { - gen_helper_sdiv_cc(dst, tcg_env, src1, src2); + TCGv_i64 t64; + +#ifdef TARGET_SPARC64 + t64 = cpu_cc_V; +#else + t64 = tcg_temp_new_i64(); +#endif + + gen_helper_sdiv(t64, tcg_env, src1, src2); + +#ifdef TARGET_SPARC64 + tcg_gen_ext32s_tl(cpu_cc_N, t64); + tcg_gen_shri_tl(cpu_cc_V, t64, 32); + tcg_gen_mov_tl(cpu_icc_Z, cpu_cc_N); + tcg_gen_movi_tl(cpu_icc_C, 0); +#else + tcg_gen_extr_i64_tl(cpu_cc_N, cpu_cc_V, t64); +#endif + tcg_gen_mov_tl(cpu_cc_Z, cpu_cc_N); + tcg_gen_movi_tl(cpu_cc_C, 0); + tcg_gen_mov_tl(dst, cpu_cc_N); } static void gen_op_taddcctv(TCGv dst, TCGv src1, TCGv src2) @@ -3717,8 +3771,8 @@ TRANS(SMUL, MUL, do_logic, a, gen_op_smul, NULL) TRANS(UDIVX, 64, do_arith, a, -1, gen_op_udivx, NULL, NULL) TRANS(SDIVX, 64, do_arith, a, -1, gen_op_sdivx, NULL, NULL) -TRANS(UDIV, DIV, do_arith, a, CC_OP_DIV, gen_op_udiv, NULL, gen_op_udivcc) -TRANS(SDIV, DIV, do_arith, a, CC_OP_DIV, gen_op_sdiv, NULL, gen_op_sdivcc) +TRANS(UDIV, DIV, do_arith, a, CC_OP_FLAGS, gen_op_udiv, NULL, gen_op_udivcc) +TRANS(SDIV, DIV, do_arith, a, CC_OP_FLAGS, gen_op_sdiv, NULL, gen_op_sdivcc) /* TODO: Should have feature bit -- comes in with UltraSparc T2. */ TRANS(POPC, 64, do_arith, a, -1, gen_op_popc, NULL, NULL) @@ -3743,10 +3797,6 @@ static bool trans_OR(DisasContext *dc, arg_r_r_ri_cc *a) static bool trans_ADDC(DisasContext *dc, arg_r_r_ri_cc *a) { switch (dc->cc_op) { - case CC_OP_DIV: - /* Carry is known to be zero. Fall back to plain ADD. */ - return do_arith(dc, a, CC_OP_ADD, - tcg_gen_add_tl, tcg_gen_addi_tl, gen_op_add_cc); case CC_OP_ADD: case CC_OP_TADD: case CC_OP_TADDTV: @@ -3766,10 +3816,6 @@ static bool trans_ADDC(DisasContext *dc, arg_r_r_ri_cc *a) static bool trans_SUBC(DisasContext *dc, arg_r_r_ri_cc *a) { switch (dc->cc_op) { - case CC_OP_DIV: - /* Carry is known to be zero. Fall back to plain SUB. */ - return do_arith(dc, a, CC_OP_SUB, - tcg_gen_sub_tl, tcg_gen_subi_tl, gen_op_sub_cc); case CC_OP_ADD: case CC_OP_TADD: case CC_OP_TADDTV: From b989ce736e8b6bd8706bb3c93812b3fc12e97277 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 14 Oct 2023 21:03:26 -0700 Subject: [PATCH 425/974] target/sparc: Remove CC_OP_ADD, CC_OP_ADDX, CC_OP_TADD These are all related and implementable with common code. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/cc_helper.c | 92 --------------- target/sparc/cpu.h | 3 - target/sparc/translate.c | 247 ++++++++++++++------------------------- 3 files changed, 87 insertions(+), 255 deletions(-) diff --git a/target/sparc/cc_helper.c b/target/sparc/cc_helper.c index 5400dfec15..55bac722d2 100644 --- a/target/sparc/cc_helper.c +++ b/target/sparc/cc_helper.c @@ -57,28 +57,6 @@ static inline uint32_t get_C_add_icc(uint32_t dst, uint32_t src1) return ret; } -static inline uint32_t get_C_addx_icc(uint32_t dst, uint32_t src1, - uint32_t src2) -{ - uint32_t ret = 0; - - if (((src1 & src2) | (~dst & (src1 | src2))) & (1U << 31)) { - ret = PSR_CARRY; - } - return ret; -} - -static inline uint32_t get_V_add_icc(uint32_t dst, uint32_t src1, - uint32_t src2) -{ - uint32_t ret = 0; - - if (((src1 ^ src2 ^ -1) & (src1 ^ dst)) & (1U << 31)) { - ret = PSR_OVF; - } - return ret; -} - #ifdef TARGET_SPARC64 static inline uint32_t get_C_add_xcc(target_ulong dst, target_ulong src1) { @@ -90,17 +68,6 @@ static inline uint32_t get_C_add_xcc(target_ulong dst, target_ulong src1) return ret; } -static inline uint32_t get_C_addx_xcc(target_ulong dst, target_ulong src1, - target_ulong src2) -{ - uint32_t ret = 0; - - if (((src1 & src2) | (~dst & (src1 | src2))) & (1ULL << 63)) { - ret = PSR_CARRY; - } - return ret; -} - static inline uint32_t get_V_add_xcc(target_ulong dst, target_ulong src1, target_ulong src2) { @@ -128,53 +95,11 @@ static uint32_t compute_C_add_xcc(CPUSPARCState *env) } #endif -static uint32_t compute_all_add(CPUSPARCState *env) -{ - uint32_t ret; - - ret = get_NZ_icc(CC_DST); - ret |= get_C_add_icc(CC_DST, CC_SRC); - ret |= get_V_add_icc(CC_DST, CC_SRC, CC_SRC2); - return ret; -} - static uint32_t compute_C_add(CPUSPARCState *env) { return get_C_add_icc(CC_DST, CC_SRC); } -#ifdef TARGET_SPARC64 -static uint32_t compute_all_addx_xcc(CPUSPARCState *env) -{ - uint32_t ret; - - ret = get_NZ_xcc(CC_DST); - ret |= get_C_addx_xcc(CC_DST, CC_SRC, CC_SRC2); - ret |= get_V_add_xcc(CC_DST, CC_SRC, CC_SRC2); - return ret; -} - -static uint32_t compute_C_addx_xcc(CPUSPARCState *env) -{ - return get_C_addx_xcc(CC_DST, CC_SRC, CC_SRC2); -} -#endif - -static uint32_t compute_all_addx(CPUSPARCState *env) -{ - uint32_t ret; - - ret = get_NZ_icc(CC_DST); - ret |= get_C_addx_icc(CC_DST, CC_SRC, CC_SRC2); - ret |= get_V_add_icc(CC_DST, CC_SRC, CC_SRC2); - return ret; -} - -static uint32_t compute_C_addx(CPUSPARCState *env) -{ - return get_C_addx_icc(CC_DST, CC_SRC, CC_SRC2); -} - static inline uint32_t get_V_tag_icc(target_ulong src1, target_ulong src2) { uint32_t ret = 0; @@ -185,17 +110,6 @@ static inline uint32_t get_V_tag_icc(target_ulong src1, target_ulong src2) return ret; } -static uint32_t compute_all_tadd(CPUSPARCState *env) -{ - uint32_t ret; - - ret = get_NZ_icc(CC_DST); - ret |= get_C_add_icc(CC_DST, CC_SRC); - ret |= get_V_add_icc(CC_DST, CC_SRC, CC_SRC2); - ret |= get_V_tag_icc(CC_SRC, CC_SRC2); - return ret; -} - static uint32_t compute_all_taddtv(CPUSPARCState *env) { uint32_t ret; @@ -361,9 +275,6 @@ typedef struct CCTable { static const CCTable icc_table[CC_OP_NB] = { /* CC_OP_DYNAMIC should never happen */ - [CC_OP_ADD] = { compute_all_add, compute_C_add }, - [CC_OP_ADDX] = { compute_all_addx, compute_C_addx }, - [CC_OP_TADD] = { compute_all_tadd, compute_C_add }, [CC_OP_TADDTV] = { compute_all_taddtv, compute_C_add }, [CC_OP_SUB] = { compute_all_sub, compute_C_sub }, [CC_OP_SUBX] = { compute_all_subx, compute_C_subx }, @@ -374,9 +285,6 @@ static const CCTable icc_table[CC_OP_NB] = { #ifdef TARGET_SPARC64 static const CCTable xcc_table[CC_OP_NB] = { /* CC_OP_DYNAMIC should never happen */ - [CC_OP_ADD] = { compute_all_add_xcc, compute_C_add_xcc }, - [CC_OP_ADDX] = { compute_all_addx_xcc, compute_C_addx_xcc }, - [CC_OP_TADD] = { compute_all_add_xcc, compute_C_add_xcc }, [CC_OP_TADDTV] = { compute_all_add_xcc, compute_C_add_xcc }, [CC_OP_SUB] = { compute_all_sub_xcc, compute_C_sub_xcc }, [CC_OP_SUBX] = { compute_all_subx_xcc, compute_C_subx_xcc }, diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index b16d53b91f..4ee8e2dc92 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -150,9 +150,6 @@ enum { enum { CC_OP_DYNAMIC, /* must use dynamic code to get cc_op */ CC_OP_FLAGS, /* all cc are back in cc_*_[NZCV] registers */ - CC_OP_ADD, /* modify all flags, CC_DST = res, CC_SRC = src1 */ - CC_OP_ADDX, /* modify all flags, CC_DST = res, CC_SRC = src1 */ - CC_OP_TADD, /* modify all flags, CC_DST = res, CC_SRC = src1 */ CC_OP_TADDTV, /* modify all flags except V, CC_DST = res, CC_SRC = src1 */ CC_OP_SUB, /* modify all flags, CC_DST = res, CC_SRC = src1 */ CC_OP_SUBX, /* modify all flags, CC_DST = res, CC_SRC = src1 */ diff --git a/target/sparc/translate.c b/target/sparc/translate.c index fa4bad6d1f..cf121a237d 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -382,33 +382,71 @@ static void gen_goto_tb(DisasContext *s, int tb_num, } } -static void gen_op_add_cc(TCGv dst, TCGv src1, TCGv src2) +static TCGv gen_carry32(void) { - tcg_gen_mov_tl(cpu_cc_src, src1); - tcg_gen_mov_tl(cpu_cc_src2, src2); - tcg_gen_add_tl(cpu_cc_dst, cpu_cc_src, cpu_cc_src2); - tcg_gen_mov_tl(dst, cpu_cc_dst); + if (TARGET_LONG_BITS == 64) { + TCGv t = tcg_temp_new(); + tcg_gen_extract_tl(t, cpu_icc_C, 32, 1); + return t; + } + return cpu_icc_C; } -static TCGv_i32 gen_add32_carry32(void) +static void gen_op_addcc_int(TCGv dst, TCGv src1, TCGv src2, TCGv cin) { - TCGv_i32 carry_32, cc_src1_32, cc_src2_32; + TCGv z = tcg_constant_tl(0); - /* Carry is computed from a previous add: (dst < src) */ -#if TARGET_LONG_BITS == 64 - cc_src1_32 = tcg_temp_new_i32(); - cc_src2_32 = tcg_temp_new_i32(); - tcg_gen_extrl_i64_i32(cc_src1_32, cpu_cc_dst); - tcg_gen_extrl_i64_i32(cc_src2_32, cpu_cc_src); -#else - cc_src1_32 = cpu_cc_dst; - cc_src2_32 = cpu_cc_src; -#endif + if (cin) { + tcg_gen_add2_tl(cpu_cc_N, cpu_cc_C, src1, z, cin, z); + tcg_gen_add2_tl(cpu_cc_N, cpu_cc_C, cpu_cc_N, cpu_cc_C, src2, z); + } else { + tcg_gen_add2_tl(cpu_cc_N, cpu_cc_C, src1, z, src2, z); + } + tcg_gen_xor_tl(cpu_cc_Z, src1, src2); + tcg_gen_xor_tl(cpu_cc_V, cpu_cc_N, src2); + tcg_gen_andc_tl(cpu_cc_V, cpu_cc_V, cpu_cc_Z); + if (TARGET_LONG_BITS == 64) { + /* + * Carry-in to bit 32 is result ^ src1 ^ src2. + * We already have the src xor term in Z, from computation of V. + */ + tcg_gen_xor_tl(cpu_icc_C, cpu_cc_Z, cpu_cc_N); + tcg_gen_mov_tl(cpu_icc_Z, cpu_cc_N); + } + tcg_gen_mov_tl(cpu_cc_Z, cpu_cc_N); + tcg_gen_mov_tl(dst, cpu_cc_N); +} - carry_32 = tcg_temp_new_i32(); - tcg_gen_setcond_i32(TCG_COND_LTU, carry_32, cc_src1_32, cc_src2_32); +static void gen_op_addcc(TCGv dst, TCGv src1, TCGv src2) +{ + gen_op_addcc_int(dst, src1, src2, NULL); +} - return carry_32; +static void gen_op_taddcc(TCGv dst, TCGv src1, TCGv src2) +{ + TCGv t = tcg_temp_new(); + + /* Save the tag bits around modification of dst. */ + tcg_gen_or_tl(t, src1, src2); + + gen_op_addcc(dst, src1, src2); + + /* Incorprate tag bits into icc.V */ + tcg_gen_andi_tl(t, t, 3); + tcg_gen_neg_tl(t, t); + tcg_gen_ext32u_tl(t, t); + tcg_gen_or_tl(cpu_cc_V, cpu_cc_V, t); +} + +static void gen_op_addc(TCGv dst, TCGv src1, TCGv src2) +{ + tcg_gen_add_tl(dst, src1, src2); + tcg_gen_add_tl(dst, dst, gen_carry32()); +} + +static void gen_op_addccc(TCGv dst, TCGv src1, TCGv src2) +{ + gen_op_addcc_int(dst, src1, src2, gen_carry32()); } static TCGv_i32 gen_sub32_carry32(void) @@ -432,89 +470,6 @@ static TCGv_i32 gen_sub32_carry32(void) return carry_32; } -static void gen_op_addc_int(TCGv dst, TCGv src1, TCGv src2, - TCGv_i32 carry_32, bool update_cc) -{ - tcg_gen_add_tl(dst, src1, src2); - -#ifdef TARGET_SPARC64 - TCGv carry = tcg_temp_new(); - tcg_gen_extu_i32_tl(carry, carry_32); - tcg_gen_add_tl(dst, dst, carry); -#else - tcg_gen_add_i32(dst, dst, carry_32); -#endif - - if (update_cc) { - tcg_debug_assert(dst == cpu_cc_dst); - tcg_gen_mov_tl(cpu_cc_src, src1); - tcg_gen_mov_tl(cpu_cc_src2, src2); - } -} - -static void gen_op_addc_int_add(TCGv dst, TCGv src1, TCGv src2, bool update_cc) -{ - TCGv discard; - - if (TARGET_LONG_BITS == 64) { - gen_op_addc_int(dst, src1, src2, gen_add32_carry32(), update_cc); - return; - } - - /* - * We can re-use the host's hardware carry generation by using - * an ADD2 opcode. We discard the low part of the output. - * Ideally we'd combine this operation with the add that - * generated the carry in the first place. - */ - discard = tcg_temp_new(); - tcg_gen_add2_tl(discard, dst, cpu_cc_src, src1, cpu_cc_src2, src2); - - if (update_cc) { - tcg_debug_assert(dst == cpu_cc_dst); - tcg_gen_mov_tl(cpu_cc_src, src1); - tcg_gen_mov_tl(cpu_cc_src2, src2); - } -} - -static void gen_op_addc_add(TCGv dst, TCGv src1, TCGv src2) -{ - gen_op_addc_int_add(dst, src1, src2, false); -} - -static void gen_op_addccc_add(TCGv dst, TCGv src1, TCGv src2) -{ - gen_op_addc_int_add(dst, src1, src2, true); -} - -static void gen_op_addc_sub(TCGv dst, TCGv src1, TCGv src2) -{ - gen_op_addc_int(dst, src1, src2, gen_sub32_carry32(), false); -} - -static void gen_op_addccc_sub(TCGv dst, TCGv src1, TCGv src2) -{ - gen_op_addc_int(dst, src1, src2, gen_sub32_carry32(), true); -} - -static void gen_op_addc_int_generic(TCGv dst, TCGv src1, TCGv src2, - bool update_cc) -{ - TCGv_i32 carry_32 = tcg_temp_new_i32(); - gen_helper_compute_C_icc(carry_32, tcg_env); - gen_op_addc_int(dst, src1, src2, carry_32, update_cc); -} - -static void gen_op_addc_generic(TCGv dst, TCGv src1, TCGv src2) -{ - gen_op_addc_int_generic(dst, src1, src2, false); -} - -static void gen_op_addccc_generic(TCGv dst, TCGv src1, TCGv src2) -{ - gen_op_addc_int_generic(dst, src1, src2, true); -} - static void gen_op_sub_cc(TCGv dst, TCGv src1, TCGv src2) { tcg_gen_mov_tl(cpu_cc_src, src1); @@ -545,16 +500,6 @@ static void gen_op_subc_int(TCGv dst, TCGv src1, TCGv src2, } } -static void gen_op_subc_add(TCGv dst, TCGv src1, TCGv src2) -{ - gen_op_subc_int(dst, src1, src2, gen_add32_carry32(), false); -} - -static void gen_op_subccc_add(TCGv dst, TCGv src1, TCGv src2) -{ - gen_op_subc_int(dst, src1, src2, gen_add32_carry32(), true); -} - static void gen_op_subc_int_sub(TCGv dst, TCGv src1, TCGv src2, bool update_cc) { TCGv discard; @@ -609,39 +554,39 @@ static void gen_op_subccc_generic(TCGv dst, TCGv src1, TCGv src2) static void gen_op_mulscc(TCGv dst, TCGv src1, TCGv src2) { - TCGv r_temp, zero, t0; + TCGv zero = tcg_constant_tl(0); + TCGv t_src1 = tcg_temp_new(); + TCGv t_src2 = tcg_temp_new(); + TCGv t0 = tcg_temp_new(); - r_temp = tcg_temp_new(); - t0 = tcg_temp_new(); + tcg_gen_ext32u_tl(t_src1, src1); + tcg_gen_ext32u_tl(t_src2, src2); - /* old op: - if (!(env->y & 1)) - T1 = 0; - */ - zero = tcg_constant_tl(0); - tcg_gen_andi_tl(cpu_cc_src, src1, 0xffffffff); - tcg_gen_andi_tl(r_temp, cpu_y, 0x1); - tcg_gen_andi_tl(cpu_cc_src2, src2, 0xffffffff); - tcg_gen_movcond_tl(TCG_COND_EQ, cpu_cc_src2, r_temp, zero, - zero, cpu_cc_src2); + /* + * if (!(env->y & 1)) + * src2 = 0; + */ + tcg_gen_andi_tl(t0, cpu_y, 0x1); + tcg_gen_movcond_tl(TCG_COND_EQ, t_src2, t0, zero, zero, t_src2); - // b2 = T0 & 1; - // env->y = (b2 << 31) | (env->y >> 1); + /* + * b2 = src1 & 1; + * y = (b2 << 31) | (y >> 1); + */ tcg_gen_extract_tl(t0, cpu_y, 1, 31); - tcg_gen_deposit_tl(cpu_y, t0, cpu_cc_src, 31, 1); + tcg_gen_deposit_tl(cpu_y, t0, src1, 31, 1); // b1 = N ^ V; tcg_gen_xor_tl(t0, cpu_cc_N, cpu_cc_V); - // T0 = (b1 << 31) | (T0 >> 1); - // src1 = T0; + /* + * src1 = (b1 << 31) | (src1 >> 1) + */ tcg_gen_andi_tl(t0, t0, 1u << 31); - tcg_gen_shri_tl(cpu_cc_src, cpu_cc_src, 1); - tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, t0); + tcg_gen_shri_tl(t_src1, t_src1, 1); + tcg_gen_or_tl(t_src1, t_src1, t0); - tcg_gen_add_tl(cpu_cc_dst, cpu_cc_src, cpu_cc_src2); - - tcg_gen_mov_tl(dst, cpu_cc_dst); + gen_op_addcc(dst, t_src1, t_src2); } static void gen_op_multiply(TCGv dst, TCGv src1, TCGv src2, int sign_ext) @@ -3749,12 +3694,12 @@ static bool do_logic(DisasContext *dc, arg_r_r_ri_cc *a, return do_arith_int(dc, a, CC_OP_FLAGS, func, funci, a->cc); } -TRANS(ADD, ALL, do_arith, a, CC_OP_ADD, - tcg_gen_add_tl, tcg_gen_addi_tl, gen_op_add_cc) +TRANS(ADD, ALL, do_arith, a, CC_OP_FLAGS, + tcg_gen_add_tl, tcg_gen_addi_tl, gen_op_addcc) TRANS(SUB, ALL, do_arith, a, CC_OP_SUB, tcg_gen_sub_tl, tcg_gen_subi_tl, gen_op_sub_cc) -TRANS(TADDcc, ALL, do_arith, a, CC_OP_TADD, NULL, NULL, gen_op_add_cc) +TRANS(TADDcc, ALL, do_arith, a, CC_OP_FLAGS, NULL, NULL, gen_op_taddcc) TRANS(TSUBcc, ALL, do_arith, a, CC_OP_TSUB, NULL, NULL, gen_op_sub_cc) TRANS(TADDccTV, ALL, do_arith, a, CC_OP_TADDTV, NULL, NULL, gen_op_taddcctv) TRANS(TSUBccTV, ALL, do_arith, a, CC_OP_TSUBTV, NULL, NULL, gen_op_tsubcctv) @@ -3796,31 +3741,13 @@ static bool trans_OR(DisasContext *dc, arg_r_r_ri_cc *a) static bool trans_ADDC(DisasContext *dc, arg_r_r_ri_cc *a) { - switch (dc->cc_op) { - case CC_OP_ADD: - case CC_OP_TADD: - case CC_OP_TADDTV: - return do_arith(dc, a, CC_OP_ADDX, - gen_op_addc_add, NULL, gen_op_addccc_add); - case CC_OP_SUB: - case CC_OP_TSUB: - case CC_OP_TSUBTV: - return do_arith(dc, a, CC_OP_ADDX, - gen_op_addc_sub, NULL, gen_op_addccc_sub); - default: - return do_arith(dc, a, CC_OP_ADDX, - gen_op_addc_generic, NULL, gen_op_addccc_generic); - } + update_psr(dc); + return do_arith(dc, a, CC_OP_FLAGS, gen_op_addc, NULL, gen_op_addccc); } static bool trans_SUBC(DisasContext *dc, arg_r_r_ri_cc *a) { switch (dc->cc_op) { - case CC_OP_ADD: - case CC_OP_TADD: - case CC_OP_TADDTV: - return do_arith(dc, a, CC_OP_SUBX, - gen_op_subc_add, NULL, gen_op_subccc_add); case CC_OP_SUB: case CC_OP_TSUB: case CC_OP_TSUBTV: @@ -3835,7 +3762,7 @@ static bool trans_SUBC(DisasContext *dc, arg_r_r_ri_cc *a) static bool trans_MULScc(DisasContext *dc, arg_r_r_ri_cc *a) { update_psr(dc); - return do_arith(dc, a, CC_OP_ADD, NULL, NULL, gen_op_mulscc); + return do_arith(dc, a, CC_OP_FLAGS, NULL, NULL, gen_op_mulscc); } static bool gen_edge(DisasContext *dc, arg_r_r_r *a, From f828df744376609907f8d054219233227efb3a34 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 14 Oct 2023 21:23:27 -0700 Subject: [PATCH 426/974] target/sparc: Remove CC_OP_SUB, CC_OP_SUBX, CC_OP_TSUB These are all related and implementable with common code. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/cc_helper.c | 103 -------------------- target/sparc/cpu.h | 3 - target/sparc/translate.c | 203 +++++++++------------------------------ 3 files changed, 45 insertions(+), 264 deletions(-) diff --git a/target/sparc/cc_helper.c b/target/sparc/cc_helper.c index 55bac722d2..20d451aa65 100644 --- a/target/sparc/cc_helper.c +++ b/target/sparc/cc_helper.c @@ -100,16 +100,6 @@ static uint32_t compute_C_add(CPUSPARCState *env) return get_C_add_icc(CC_DST, CC_SRC); } -static inline uint32_t get_V_tag_icc(target_ulong src1, target_ulong src2) -{ - uint32_t ret = 0; - - if ((src1 | src2) & 0x3) { - ret = PSR_OVF; - } - return ret; -} - static uint32_t compute_all_taddtv(CPUSPARCState *env) { uint32_t ret; @@ -129,29 +119,6 @@ static inline uint32_t get_C_sub_icc(uint32_t src1, uint32_t src2) return ret; } -static inline uint32_t get_C_subx_icc(uint32_t dst, uint32_t src1, - uint32_t src2) -{ - uint32_t ret = 0; - - if (((~src1 & src2) | (dst & (~src1 | src2))) & (1U << 31)) { - ret = PSR_CARRY; - } - return ret; -} - -static inline uint32_t get_V_sub_icc(uint32_t dst, uint32_t src1, - uint32_t src2) -{ - uint32_t ret = 0; - - if (((src1 ^ src2) & (src1 ^ dst)) & (1U << 31)) { - ret = PSR_OVF; - } - return ret; -} - - #ifdef TARGET_SPARC64 static inline uint32_t get_C_sub_xcc(target_ulong src1, target_ulong src2) { @@ -163,17 +130,6 @@ static inline uint32_t get_C_sub_xcc(target_ulong src1, target_ulong src2) return ret; } -static inline uint32_t get_C_subx_xcc(target_ulong dst, target_ulong src1, - target_ulong src2) -{ - uint32_t ret = 0; - - if (((~src1 & src2) | (dst & (~src1 | src2))) & (1ULL << 63)) { - ret = PSR_CARRY; - } - return ret; -} - static inline uint32_t get_V_sub_xcc(target_ulong dst, target_ulong src1, target_ulong src2) { @@ -201,64 +157,11 @@ static uint32_t compute_C_sub_xcc(CPUSPARCState *env) } #endif -static uint32_t compute_all_sub(CPUSPARCState *env) -{ - uint32_t ret; - - ret = get_NZ_icc(CC_DST); - ret |= get_C_sub_icc(CC_SRC, CC_SRC2); - ret |= get_V_sub_icc(CC_DST, CC_SRC, CC_SRC2); - return ret; -} - static uint32_t compute_C_sub(CPUSPARCState *env) { return get_C_sub_icc(CC_SRC, CC_SRC2); } -#ifdef TARGET_SPARC64 -static uint32_t compute_all_subx_xcc(CPUSPARCState *env) -{ - uint32_t ret; - - ret = get_NZ_xcc(CC_DST); - ret |= get_C_subx_xcc(CC_DST, CC_SRC, CC_SRC2); - ret |= get_V_sub_xcc(CC_DST, CC_SRC, CC_SRC2); - return ret; -} - -static uint32_t compute_C_subx_xcc(CPUSPARCState *env) -{ - return get_C_subx_xcc(CC_DST, CC_SRC, CC_SRC2); -} -#endif - -static uint32_t compute_all_subx(CPUSPARCState *env) -{ - uint32_t ret; - - ret = get_NZ_icc(CC_DST); - ret |= get_C_subx_icc(CC_DST, CC_SRC, CC_SRC2); - ret |= get_V_sub_icc(CC_DST, CC_SRC, CC_SRC2); - return ret; -} - -static uint32_t compute_C_subx(CPUSPARCState *env) -{ - return get_C_subx_icc(CC_DST, CC_SRC, CC_SRC2); -} - -static uint32_t compute_all_tsub(CPUSPARCState *env) -{ - uint32_t ret; - - ret = get_NZ_icc(CC_DST); - ret |= get_C_sub_icc(CC_SRC, CC_SRC2); - ret |= get_V_sub_icc(CC_DST, CC_SRC, CC_SRC2); - ret |= get_V_tag_icc(CC_SRC, CC_SRC2); - return ret; -} - static uint32_t compute_all_tsubtv(CPUSPARCState *env) { uint32_t ret; @@ -276,9 +179,6 @@ typedef struct CCTable { static const CCTable icc_table[CC_OP_NB] = { /* CC_OP_DYNAMIC should never happen */ [CC_OP_TADDTV] = { compute_all_taddtv, compute_C_add }, - [CC_OP_SUB] = { compute_all_sub, compute_C_sub }, - [CC_OP_SUBX] = { compute_all_subx, compute_C_subx }, - [CC_OP_TSUB] = { compute_all_tsub, compute_C_sub }, [CC_OP_TSUBTV] = { compute_all_tsubtv, compute_C_sub }, }; @@ -286,9 +186,6 @@ static const CCTable icc_table[CC_OP_NB] = { static const CCTable xcc_table[CC_OP_NB] = { /* CC_OP_DYNAMIC should never happen */ [CC_OP_TADDTV] = { compute_all_add_xcc, compute_C_add_xcc }, - [CC_OP_SUB] = { compute_all_sub_xcc, compute_C_sub_xcc }, - [CC_OP_SUBX] = { compute_all_subx_xcc, compute_C_subx_xcc }, - [CC_OP_TSUB] = { compute_all_sub_xcc, compute_C_sub_xcc }, [CC_OP_TSUBTV] = { compute_all_sub_xcc, compute_C_sub_xcc }, }; #endif diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index 4ee8e2dc92..9884bd416a 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -151,9 +151,6 @@ enum { CC_OP_DYNAMIC, /* must use dynamic code to get cc_op */ CC_OP_FLAGS, /* all cc are back in cc_*_[NZCV] registers */ CC_OP_TADDTV, /* modify all flags except V, CC_DST = res, CC_SRC = src1 */ - CC_OP_SUB, /* modify all flags, CC_DST = res, CC_SRC = src1 */ - CC_OP_SUBX, /* modify all flags, CC_DST = res, CC_SRC = src1 */ - CC_OP_TSUB, /* modify all flags, CC_DST = res, CC_SRC = src1 */ CC_OP_TSUBTV, /* modify all flags except V, CC_DST = res, CC_SRC = src1 */ CC_OP_NB, }; diff --git a/target/sparc/translate.c b/target/sparc/translate.c index cf121a237d..d119ce4c94 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -449,107 +449,58 @@ static void gen_op_addccc(TCGv dst, TCGv src1, TCGv src2) gen_op_addcc_int(dst, src1, src2, gen_carry32()); } -static TCGv_i32 gen_sub32_carry32(void) +static void gen_op_subcc_int(TCGv dst, TCGv src1, TCGv src2, TCGv cin) { - TCGv_i32 carry_32, cc_src1_32, cc_src2_32; + TCGv z = tcg_constant_tl(0); - /* Carry is computed from a previous borrow: (src1 < src2) */ -#if TARGET_LONG_BITS == 64 - cc_src1_32 = tcg_temp_new_i32(); - cc_src2_32 = tcg_temp_new_i32(); - tcg_gen_extrl_i64_i32(cc_src1_32, cpu_cc_src); - tcg_gen_extrl_i64_i32(cc_src2_32, cpu_cc_src2); -#else - cc_src1_32 = cpu_cc_src; - cc_src2_32 = cpu_cc_src2; + if (cin) { + tcg_gen_sub2_tl(cpu_cc_N, cpu_cc_C, src1, z, cin, z); + tcg_gen_sub2_tl(cpu_cc_N, cpu_cc_C, cpu_cc_N, cpu_cc_C, src2, z); + } else { + tcg_gen_sub2_tl(cpu_cc_N, cpu_cc_C, src1, z, src2, z); + } + tcg_gen_neg_tl(cpu_cc_C, cpu_cc_C); + tcg_gen_xor_tl(cpu_cc_Z, src1, src2); + tcg_gen_xor_tl(cpu_cc_V, cpu_cc_N, src1); + tcg_gen_and_tl(cpu_cc_V, cpu_cc_V, cpu_cc_Z); +#ifdef TARGET_SPARC64 + tcg_gen_xor_tl(cpu_icc_C, cpu_cc_Z, cpu_cc_N); + tcg_gen_mov_tl(cpu_icc_Z, cpu_cc_N); #endif - - carry_32 = tcg_temp_new_i32(); - tcg_gen_setcond_i32(TCG_COND_LTU, carry_32, cc_src1_32, cc_src2_32); - - return carry_32; + tcg_gen_mov_tl(cpu_cc_Z, cpu_cc_N); + tcg_gen_mov_tl(dst, cpu_cc_N); } -static void gen_op_sub_cc(TCGv dst, TCGv src1, TCGv src2) +static void gen_op_subcc(TCGv dst, TCGv src1, TCGv src2) { - tcg_gen_mov_tl(cpu_cc_src, src1); - tcg_gen_mov_tl(cpu_cc_src2, src2); - tcg_gen_sub_tl(cpu_cc_dst, cpu_cc_src, cpu_cc_src2); - tcg_gen_mov_tl(dst, cpu_cc_dst); + gen_op_subcc_int(dst, src1, src2, NULL); } -static void gen_op_subc_int(TCGv dst, TCGv src1, TCGv src2, - TCGv_i32 carry_32, bool update_cc) +static void gen_op_tsubcc(TCGv dst, TCGv src1, TCGv src2) { - TCGv carry; + TCGv t = tcg_temp_new(); -#if TARGET_LONG_BITS == 64 - carry = tcg_temp_new(); - tcg_gen_extu_i32_i64(carry, carry_32); -#else - carry = carry_32; -#endif + /* Save the tag bits around modification of dst. */ + tcg_gen_or_tl(t, src1, src2); + gen_op_subcc(dst, src1, src2); + + /* Incorprate tag bits into icc.V */ + tcg_gen_andi_tl(t, t, 3); + tcg_gen_neg_tl(t, t); + tcg_gen_ext32u_tl(t, t); + tcg_gen_or_tl(cpu_cc_V, cpu_cc_V, t); +} + +static void gen_op_subc(TCGv dst, TCGv src1, TCGv src2) +{ tcg_gen_sub_tl(dst, src1, src2); - tcg_gen_sub_tl(dst, dst, carry); - - if (update_cc) { - tcg_debug_assert(dst == cpu_cc_dst); - tcg_gen_mov_tl(cpu_cc_src, src1); - tcg_gen_mov_tl(cpu_cc_src2, src2); - } + tcg_gen_sub_tl(dst, dst, gen_carry32()); } -static void gen_op_subc_int_sub(TCGv dst, TCGv src1, TCGv src2, bool update_cc) +static void gen_op_subccc(TCGv dst, TCGv src1, TCGv src2) { - TCGv discard; - - if (TARGET_LONG_BITS == 64) { - gen_op_subc_int(dst, src1, src2, gen_sub32_carry32(), update_cc); - return; - } - - /* - * We can re-use the host's hardware carry generation by using - * a SUB2 opcode. We discard the low part of the output. - */ - discard = tcg_temp_new(); - tcg_gen_sub2_tl(discard, dst, cpu_cc_src, src1, cpu_cc_src2, src2); - - if (update_cc) { - tcg_debug_assert(dst == cpu_cc_dst); - tcg_gen_mov_tl(cpu_cc_src, src1); - tcg_gen_mov_tl(cpu_cc_src2, src2); - } -} - -static void gen_op_subc_sub(TCGv dst, TCGv src1, TCGv src2) -{ - gen_op_subc_int_sub(dst, src1, src2, false); -} - -static void gen_op_subccc_sub(TCGv dst, TCGv src1, TCGv src2) -{ - gen_op_subc_int_sub(dst, src1, src2, true); -} - -static void gen_op_subc_int_generic(TCGv dst, TCGv src1, TCGv src2, - bool update_cc) -{ - TCGv_i32 carry_32 = tcg_temp_new_i32(); - - gen_helper_compute_C_icc(carry_32, tcg_env); - gen_op_subc_int(dst, src1, src2, carry_32, update_cc); -} - -static void gen_op_subc_generic(TCGv dst, TCGv src1, TCGv src2) -{ - gen_op_subc_int_generic(dst, src1, src2, false); -} - -static void gen_op_subccc_generic(TCGv dst, TCGv src1, TCGv src2) -{ - gen_op_subc_int_generic(dst, src1, src2, true); + gen_op_subcc_int(dst, src1, src2, gen_carry32()); } static void gen_op_mulscc(TCGv dst, TCGv src1, TCGv src2) @@ -1097,65 +1048,11 @@ static void gen_op_next_insn(void) static void gen_compare(DisasCompare *cmp, bool xcc, unsigned int cond, DisasContext *dc) { - static int subcc_cond[16] = { - TCG_COND_NEVER, - TCG_COND_EQ, - TCG_COND_LE, - TCG_COND_LT, - TCG_COND_LEU, - TCG_COND_LTU, - -1, /* neg */ - -1, /* overflow */ - TCG_COND_ALWAYS, - TCG_COND_NE, - TCG_COND_GT, - TCG_COND_GE, - TCG_COND_GTU, - TCG_COND_GEU, - -1, /* pos */ - -1, /* no overflow */ - }; - TCGv t1, t2; cmp->is_bool = false; switch (dc->cc_op) { - case CC_OP_SUB: - switch (cond) { - case 6: /* neg */ - case 14: /* pos */ - cmp->cond = (cond == 6 ? TCG_COND_LT : TCG_COND_GE); - cmp->c2 = tcg_constant_tl(0); - if (TARGET_LONG_BITS == 32 || xcc) { - cmp->c1 = cpu_cc_dst; - } else { - cmp->c1 = t1 = tcg_temp_new(); - tcg_gen_ext32s_tl(t1, cpu_cc_dst); - } - return; - - case 7: /* overflow */ - case 15: /* !overflow */ - break; - - default: - cmp->cond = subcc_cond[cond]; - if (TARGET_LONG_BITS == 32 || xcc) { - cmp->c1 = cpu_cc_src; - cmp->c2 = cpu_cc_src2; - } else { - /* Note that sign-extension works for unsigned compares as - long as both operands are sign-extended. */ - cmp->c1 = t1 = tcg_temp_new(); - tcg_gen_ext32s_tl(t1, cpu_cc_src); - cmp->c2 = t2 = tcg_temp_new(); - tcg_gen_ext32s_tl(t2, cpu_cc_src2); - } - return; - } - break; - default: gen_helper_compute_psr(tcg_env); dc->cc_op = CC_OP_FLAGS; @@ -3696,11 +3593,11 @@ static bool do_logic(DisasContext *dc, arg_r_r_ri_cc *a, TRANS(ADD, ALL, do_arith, a, CC_OP_FLAGS, tcg_gen_add_tl, tcg_gen_addi_tl, gen_op_addcc) -TRANS(SUB, ALL, do_arith, a, CC_OP_SUB, - tcg_gen_sub_tl, tcg_gen_subi_tl, gen_op_sub_cc) +TRANS(SUB, ALL, do_arith, a, CC_OP_FLAGS, + tcg_gen_sub_tl, tcg_gen_subi_tl, gen_op_subcc) TRANS(TADDcc, ALL, do_arith, a, CC_OP_FLAGS, NULL, NULL, gen_op_taddcc) -TRANS(TSUBcc, ALL, do_arith, a, CC_OP_TSUB, NULL, NULL, gen_op_sub_cc) +TRANS(TSUBcc, ALL, do_arith, a, CC_OP_FLAGS, NULL, NULL, gen_op_tsubcc) TRANS(TADDccTV, ALL, do_arith, a, CC_OP_TADDTV, NULL, NULL, gen_op_taddcctv) TRANS(TSUBccTV, ALL, do_arith, a, CC_OP_TSUBTV, NULL, NULL, gen_op_tsubcctv) @@ -3747,16 +3644,8 @@ static bool trans_ADDC(DisasContext *dc, arg_r_r_ri_cc *a) static bool trans_SUBC(DisasContext *dc, arg_r_r_ri_cc *a) { - switch (dc->cc_op) { - case CC_OP_SUB: - case CC_OP_TSUB: - case CC_OP_TSUBTV: - return do_arith(dc, a, CC_OP_SUBX, - gen_op_subc_sub, NULL, gen_op_subccc_sub); - default: - return do_arith(dc, a, CC_OP_SUBX, - gen_op_subc_generic, NULL, gen_op_subccc_generic); - } + update_psr(dc); + return do_arith(dc, a, CC_OP_FLAGS, gen_op_subc, NULL, gen_op_subccc); } static bool trans_MULScc(DisasContext *dc, arg_r_r_ri_cc *a) @@ -3777,11 +3666,9 @@ static bool gen_edge(DisasContext *dc, arg_r_r_r *a, s2 = gen_load_gpr(dc, a->rs2); if (cc) { - tcg_gen_mov_tl(cpu_cc_src, s1); - tcg_gen_mov_tl(cpu_cc_src2, s2); - tcg_gen_sub_tl(cpu_cc_dst, s1, s2); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_SUB); - dc->cc_op = CC_OP_SUB; + gen_op_subcc(cpu_cc_N, s1, s2); + tcg_gen_movi_i32(cpu_cc_op, CC_OP_FLAGS); + dc->cc_op = CC_OP_FLAGS; } /* From 68524e83f8fbe23dca37dc5b847fb558d75c8fab Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 14 Oct 2023 21:38:12 -0700 Subject: [PATCH 427/974] target/sparc: Remove CC_OP_TADDTV, CC_OP_TSUBTV Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/cc_helper.c | 190 +-------------------------------------- target/sparc/cpu.h | 2 - target/sparc/helper.c | 36 ++++++-- target/sparc/translate.c | 4 +- 4 files changed, 32 insertions(+), 200 deletions(-) diff --git a/target/sparc/cc_helper.c b/target/sparc/cc_helper.c index 20d451aa65..05f1479aea 100644 --- a/target/sparc/cc_helper.c +++ b/target/sparc/cc_helper.c @@ -21,198 +21,12 @@ #include "cpu.h" #include "exec/helper-proto.h" -static inline uint32_t get_NZ_icc(int32_t dst) -{ - uint32_t ret = 0; - - if (dst == 0) { - ret = PSR_ZERO; - } else if (dst < 0) { - ret = PSR_NEG; - } - return ret; -} - -#ifdef TARGET_SPARC64 -static inline uint32_t get_NZ_xcc(target_long dst) -{ - uint32_t ret = 0; - - if (!dst) { - ret = PSR_ZERO; - } else if (dst < 0) { - ret = PSR_NEG; - } - return ret; -} -#endif - -static inline uint32_t get_C_add_icc(uint32_t dst, uint32_t src1) -{ - uint32_t ret = 0; - - if (dst < src1) { - ret = PSR_CARRY; - } - return ret; -} - -#ifdef TARGET_SPARC64 -static inline uint32_t get_C_add_xcc(target_ulong dst, target_ulong src1) -{ - uint32_t ret = 0; - - if (dst < src1) { - ret = PSR_CARRY; - } - return ret; -} - -static inline uint32_t get_V_add_xcc(target_ulong dst, target_ulong src1, - target_ulong src2) -{ - uint32_t ret = 0; - - if (((src1 ^ src2 ^ -1) & (src1 ^ dst)) & (1ULL << 63)) { - ret = PSR_OVF; - } - return ret; -} - -static uint32_t compute_all_add_xcc(CPUSPARCState *env) -{ - uint32_t ret; - - ret = get_NZ_xcc(CC_DST); - ret |= get_C_add_xcc(CC_DST, CC_SRC); - ret |= get_V_add_xcc(CC_DST, CC_SRC, CC_SRC2); - return ret; -} - -static uint32_t compute_C_add_xcc(CPUSPARCState *env) -{ - return get_C_add_xcc(CC_DST, CC_SRC); -} -#endif - -static uint32_t compute_C_add(CPUSPARCState *env) -{ - return get_C_add_icc(CC_DST, CC_SRC); -} - -static uint32_t compute_all_taddtv(CPUSPARCState *env) -{ - uint32_t ret; - - ret = get_NZ_icc(CC_DST); - ret |= get_C_add_icc(CC_DST, CC_SRC); - return ret; -} - -static inline uint32_t get_C_sub_icc(uint32_t src1, uint32_t src2) -{ - uint32_t ret = 0; - - if (src1 < src2) { - ret = PSR_CARRY; - } - return ret; -} - -#ifdef TARGET_SPARC64 -static inline uint32_t get_C_sub_xcc(target_ulong src1, target_ulong src2) -{ - uint32_t ret = 0; - - if (src1 < src2) { - ret = PSR_CARRY; - } - return ret; -} - -static inline uint32_t get_V_sub_xcc(target_ulong dst, target_ulong src1, - target_ulong src2) -{ - uint32_t ret = 0; - - if (((src1 ^ src2) & (src1 ^ dst)) & (1ULL << 63)) { - ret = PSR_OVF; - } - return ret; -} - -static uint32_t compute_all_sub_xcc(CPUSPARCState *env) -{ - uint32_t ret; - - ret = get_NZ_xcc(CC_DST); - ret |= get_C_sub_xcc(CC_SRC, CC_SRC2); - ret |= get_V_sub_xcc(CC_DST, CC_SRC, CC_SRC2); - return ret; -} - -static uint32_t compute_C_sub_xcc(CPUSPARCState *env) -{ - return get_C_sub_xcc(CC_SRC, CC_SRC2); -} -#endif - -static uint32_t compute_C_sub(CPUSPARCState *env) -{ - return get_C_sub_icc(CC_SRC, CC_SRC2); -} - -static uint32_t compute_all_tsubtv(CPUSPARCState *env) -{ - uint32_t ret; - - ret = get_NZ_icc(CC_DST); - ret |= get_C_sub_icc(CC_SRC, CC_SRC2); - return ret; -} - -typedef struct CCTable { - uint32_t (*compute_all)(CPUSPARCState *env); /* return all the flags */ - uint32_t (*compute_c)(CPUSPARCState *env); /* return the C flag */ -} CCTable; - -static const CCTable icc_table[CC_OP_NB] = { - /* CC_OP_DYNAMIC should never happen */ - [CC_OP_TADDTV] = { compute_all_taddtv, compute_C_add }, - [CC_OP_TSUBTV] = { compute_all_tsubtv, compute_C_sub }, -}; - -#ifdef TARGET_SPARC64 -static const CCTable xcc_table[CC_OP_NB] = { - /* CC_OP_DYNAMIC should never happen */ - [CC_OP_TADDTV] = { compute_all_add_xcc, compute_C_add_xcc }, - [CC_OP_TSUBTV] = { compute_all_sub_xcc, compute_C_sub_xcc }, -}; -#endif - void helper_compute_psr(CPUSPARCState *env) { if (CC_OP == CC_OP_FLAGS) { return; } - - uint32_t icc = icc_table[CC_OP].compute_all(env); -#ifdef TARGET_SPARC64 - uint32_t xcc = xcc_table[CC_OP].compute_all(env); - - env->cc_N = deposit64(-(icc & PSR_NEG), 32, 32, -(xcc & PSR_NEG)); - env->cc_V = deposit64(-(icc & PSR_OVF), 32, 32, -(xcc & PSR_OVF)); - env->icc_C = (uint64_t)icc << (32 - PSR_CARRY_SHIFT); - env->xcc_C = (xcc >> PSR_CARRY_SHIFT) & 1; - env->xcc_Z = ~xcc & PSR_ZERO; -#else - env->cc_N = -(icc & PSR_NEG); - env->cc_V = -(icc & PSR_OVF); - env->icc_C = (icc >> PSR_CARRY_SHIFT) & 1; -#endif - env->icc_Z = ~icc & PSR_ZERO; - - CC_OP = CC_OP_FLAGS; + g_assert_not_reached(); } uint32_t helper_compute_C_icc(CPUSPARCState *env) @@ -224,5 +38,5 @@ uint32_t helper_compute_C_icc(CPUSPARCState *env) return env->icc_C; #endif } - return icc_table[CC_OP].compute_c(env) >> PSR_CARRY_SHIFT; + g_assert_not_reached(); } diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index 9884bd416a..a7999eaab5 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -150,8 +150,6 @@ enum { enum { CC_OP_DYNAMIC, /* must use dynamic code to get cc_op */ CC_OP_FLAGS, /* all cc are back in cc_*_[NZCV] registers */ - CC_OP_TADDTV, /* modify all flags except V, CC_DST = res, CC_SRC = src1 */ - CC_OP_TSUBTV, /* modify all flags except V, CC_DST = res, CC_SRC = src1 */ CC_OP_NB, }; diff --git a/target/sparc/helper.c b/target/sparc/helper.c index 53eec693dd..6117e99b55 100644 --- a/target/sparc/helper.c +++ b/target/sparc/helper.c @@ -156,7 +156,7 @@ uint64_t helper_udivx(CPUSPARCState *env, uint64_t a, uint64_t b) target_ulong helper_taddcctv(CPUSPARCState *env, target_ulong src1, target_ulong src2) { - target_ulong dst; + target_ulong dst, v; /* Tag overflow occurs if either input has bits 0 or 1 set. */ if ((src1 | src2) & 3) { @@ -166,13 +166,23 @@ target_ulong helper_taddcctv(CPUSPARCState *env, target_ulong src1, dst = src1 + src2; /* Tag overflow occurs if the addition overflows. */ - if (~(src1 ^ src2) & (src1 ^ dst) & (1u << 31)) { + v = ~(src1 ^ src2) & (src1 ^ dst); + if (v & (1u << 31)) { goto tag_overflow; } /* Only modify the CC after any exceptions have been generated. */ - env->cc_src = src1; - env->cc_src2 = src2; + env->cc_V = v; + env->cc_N = dst; + env->icc_Z = dst; +#ifdef TARGET_SPARC64 + env->xcc_Z = dst; + env->icc_C = dst ^ src1 ^ src2; + env->xcc_C = dst < src1; +#else + env->icc_C = dst < src1; +#endif + return dst; tag_overflow: @@ -182,7 +192,7 @@ target_ulong helper_taddcctv(CPUSPARCState *env, target_ulong src1, target_ulong helper_tsubcctv(CPUSPARCState *env, target_ulong src1, target_ulong src2) { - target_ulong dst; + target_ulong dst, v; /* Tag overflow occurs if either input has bits 0 or 1 set. */ if ((src1 | src2) & 3) { @@ -192,13 +202,23 @@ target_ulong helper_tsubcctv(CPUSPARCState *env, target_ulong src1, dst = src1 - src2; /* Tag overflow occurs if the subtraction overflows. */ - if ((src1 ^ src2) & (src1 ^ dst) & (1u << 31)) { + v = (src1 ^ src2) & (src1 ^ dst); + if (v & (1u << 31)) { goto tag_overflow; } /* Only modify the CC after any exceptions have been generated. */ - env->cc_src = src1; - env->cc_src2 = src2; + env->cc_V = v; + env->cc_N = dst; + env->icc_Z = dst; +#ifdef TARGET_SPARC64 + env->xcc_Z = dst; + env->icc_C = dst ^ src1 ^ src2; + env->xcc_C = src1 < src2; +#else + env->icc_C = src1 < src2; +#endif + return dst; tag_overflow: diff --git a/target/sparc/translate.c b/target/sparc/translate.c index d119ce4c94..7703166ebd 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -3598,8 +3598,8 @@ TRANS(SUB, ALL, do_arith, a, CC_OP_FLAGS, TRANS(TADDcc, ALL, do_arith, a, CC_OP_FLAGS, NULL, NULL, gen_op_taddcc) TRANS(TSUBcc, ALL, do_arith, a, CC_OP_FLAGS, NULL, NULL, gen_op_tsubcc) -TRANS(TADDccTV, ALL, do_arith, a, CC_OP_TADDTV, NULL, NULL, gen_op_taddcctv) -TRANS(TSUBccTV, ALL, do_arith, a, CC_OP_TSUBTV, NULL, NULL, gen_op_tsubcctv) +TRANS(TADDccTV, ALL, do_arith, a, CC_OP_FLAGS, NULL, NULL, gen_op_taddcctv) +TRANS(TSUBccTV, ALL, do_arith, a, CC_OP_FLAGS, NULL, NULL, gen_op_tsubcctv) TRANS(AND, ALL, do_logic, a, tcg_gen_and_tl, tcg_gen_andi_tl) TRANS(XOR, ALL, do_logic, a, tcg_gen_xor_tl, tcg_gen_xori_tl) From b597eedcce0de84ff573a6be2cd6a89c7fa0fd8e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 14 Oct 2023 21:55:27 -0700 Subject: [PATCH 428/974] target/sparc: Remove CC_OP leftovers All instructions have been converted to generate full condition codes explicitly. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- linux-user/sparc/cpu_loop.c | 5 -- target/sparc/cc_helper.c | 42 ------------- target/sparc/cpu.c | 1 - target/sparc/cpu.h | 21 ------- target/sparc/helper.h | 2 - target/sparc/int32_helper.c | 5 -- target/sparc/int64_helper.c | 5 -- target/sparc/meson.build | 1 - target/sparc/translate.c | 115 ++++++++---------------------------- target/sparc/win_helper.c | 7 --- 10 files changed, 26 insertions(+), 178 deletions(-) delete mode 100644 target/sparc/cc_helper.c diff --git a/linux-user/sparc/cpu_loop.c b/linux-user/sparc/cpu_loop.c index c1a2362041..3c1bde00dd 100644 --- a/linux-user/sparc/cpu_loop.c +++ b/linux-user/sparc/cpu_loop.c @@ -222,11 +222,6 @@ void cpu_loop (CPUSPARCState *env) cpu_exec_end(cs); process_queued_cpu_work(cs); - /* Compute PSR before exposing state. */ - if (env->cc_op != CC_OP_FLAGS) { - cpu_get_psr(env); - } - switch (trapnr) { case TARGET_TT_SYSCALL: ret = do_syscall (env, env->gregs[1], diff --git a/target/sparc/cc_helper.c b/target/sparc/cc_helper.c deleted file mode 100644 index 05f1479aea..0000000000 --- a/target/sparc/cc_helper.c +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Helpers for lazy condition code handling - * - * Copyright (c) 2003-2005 Fabrice Bellard - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#include "qemu/osdep.h" -#include "cpu.h" -#include "exec/helper-proto.h" - -void helper_compute_psr(CPUSPARCState *env) -{ - if (CC_OP == CC_OP_FLAGS) { - return; - } - g_assert_not_reached(); -} - -uint32_t helper_compute_C_icc(CPUSPARCState *env) -{ - if (CC_OP == CC_OP_FLAGS) { -#ifdef TARGET_SPARC64 - return extract64(env->icc_C, 32, 1); -#else - return env->icc_C; -#endif - } - g_assert_not_reached(); -} diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index bb1a155510..befa7fc4eb 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -46,7 +46,6 @@ static void sparc_cpu_reset_hold(Object *obj) env->wim = 1; #endif env->regwptr = env->regbase + (env->cwp * 16); - CC_OP = CC_OP_FLAGS; #if defined(CONFIG_USER_ONLY) #ifdef TARGET_SPARC64 env->cleanwin = env->nwindows - 2; diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index a7999eaab5..3e361a5b75 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -137,22 +137,6 @@ enum { #define PSR_CWP 0x1f #endif -#define CC_SRC (env->cc_src) -#define CC_SRC2 (env->cc_src2) -#define CC_DST (env->cc_dst) -#define CC_OP (env->cc_op) - -/* Even though lazy evaluation of CPU condition codes tends to be less - * important on RISC systems where condition codes are only updated - * when explicitly requested, SPARC uses it to update 32-bit and 64-bit - * condition codes. - */ -enum { - CC_OP_DYNAMIC, /* must use dynamic code to get cc_op */ - CC_OP_FLAGS, /* all cc are back in cc_*_[NZCV] registers */ - CC_OP_NB, -}; - /* Trap base register */ #define TBR_BASE_MASK 0xfffff000 @@ -474,11 +458,6 @@ struct CPUArchState { target_ulong xcc_C; #endif - /* emulator internal flags handling */ - target_ulong cc_src, cc_src2; - target_ulong cc_dst; - uint32_t cc_op; - target_ulong cond; /* conditional branch result (XXX: save it in a temporary register when possible) */ diff --git a/target/sparc/helper.h b/target/sparc/helper.h index a7b0079c3b..decd94c0d6 100644 --- a/target/sparc/helper.h +++ b/target/sparc/helper.h @@ -148,5 +148,3 @@ VIS_CMPHELPER(cmpne) #undef F_HELPER_0_1 #undef VIS_HELPER #undef VIS_CMPHELPER -DEF_HELPER_1(compute_psr, void, env) -DEF_HELPER_FLAGS_1(compute_C_icc, TCG_CALL_NO_WG_SE, i32, env) diff --git a/target/sparc/int32_helper.c b/target/sparc/int32_helper.c index 82e8418e46..1563613582 100644 --- a/target/sparc/int32_helper.c +++ b/target/sparc/int32_helper.c @@ -103,11 +103,6 @@ void sparc_cpu_do_interrupt(CPUState *cs) CPUSPARCState *env = &cpu->env; int cwp, intno = cs->exception_index; - /* Compute PSR before exposing state. */ - if (env->cc_op != CC_OP_FLAGS) { - cpu_get_psr(env); - } - if (qemu_loglevel_mask(CPU_LOG_INT)) { static int count; const char *name; diff --git a/target/sparc/int64_helper.c b/target/sparc/int64_helper.c index 793e57c536..1b4155f5f3 100644 --- a/target/sparc/int64_helper.c +++ b/target/sparc/int64_helper.c @@ -135,11 +135,6 @@ void sparc_cpu_do_interrupt(CPUState *cs) int intno = cs->exception_index; trap_state *tsptr; - /* Compute PSR before exposing state. */ - if (env->cc_op != CC_OP_FLAGS) { - cpu_get_psr(env); - } - #ifdef DEBUG_PCALL if (qemu_loglevel_mask(CPU_LOG_INT)) { static int count; diff --git a/target/sparc/meson.build b/target/sparc/meson.build index c316773db6..46289c8669 100644 --- a/target/sparc/meson.build +++ b/target/sparc/meson.build @@ -3,7 +3,6 @@ gen = decodetree.process('insns.decode') sparc_ss = ss.source_set() sparc_ss.add(gen) sparc_ss.add(files( - 'cc_helper.c', 'cpu.c', 'fop_helper.c', 'gdbstub.c', diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 7703166ebd..7c4fcf8326 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -105,8 +105,6 @@ /* global register indexes */ static TCGv_ptr cpu_regwptr; -static TCGv cpu_cc_src, cpu_cc_src2, cpu_cc_dst; -static TCGv_i32 cpu_cc_op; static TCGv cpu_fsr, cpu_pc, cpu_npc; static TCGv cpu_regs[32]; static TCGv cpu_y; @@ -172,7 +170,6 @@ typedef struct DisasContext { #endif #endif - uint32_t cc_op; /* current CC operation */ sparc_def_t *def; #ifdef TARGET_SPARC64 int fprs_dirty; @@ -962,14 +959,6 @@ static void save_npc(DisasContext *dc) } } -static void update_psr(DisasContext *dc) -{ - if (dc->cc_op != CC_OP_FLAGS) { - dc->cc_op = CC_OP_FLAGS; - gen_helper_compute_psr(tcg_env); - } -} - static void save_state(DisasContext *dc) { tcg_gen_movi_tl(cpu_pc, dc->pc); @@ -1048,20 +1037,9 @@ static void gen_op_next_insn(void) static void gen_compare(DisasCompare *cmp, bool xcc, unsigned int cond, DisasContext *dc) { - TCGv t1, t2; + TCGv t1; cmp->is_bool = false; - - switch (dc->cc_op) { - default: - gen_helper_compute_psr(tcg_env); - dc->cc_op = CC_OP_FLAGS; - break; - - case CC_OP_FLAGS: - break; - } - cmp->c1 = t1 = tcg_temp_new(); cmp->c2 = tcg_constant_tl(0); @@ -2739,7 +2717,6 @@ TRANS(RDASR17, ASR17, do_rd_special, true, a->rd, do_rd_leon3_config) static TCGv do_rdccr(DisasContext *dc, TCGv dst) { - update_psr(dc); gen_helper_rdccr(dst, tcg_env); return dst; } @@ -2852,7 +2829,6 @@ TRANS(RDSTRAND_STATUS, HYPV, do_rd_special, true, a->rd, do_rdstrand_status) static TCGv do_rdpsr(DisasContext *dc, TCGv dst) { - update_psr(dc); gen_helper_rdpsr(dst, tcg_env); return dst; } @@ -3257,8 +3233,6 @@ TRANS(WRPOWERDOWN, POWERDOWN, do_wr_special, a, supervisor(dc), do_wrpowerdown) static void do_wrpsr(DisasContext *dc, TCGv src) { gen_helper_wrpsr(tcg_env, src); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_FLAGS); - dc->cc_op = CC_OP_FLAGS; dc->base.is_jmp = DISAS_EXIT; } @@ -3522,7 +3496,7 @@ static bool trans_NOP(DisasContext *dc, arg_NOP *a) TRANS(NOP_v7, 32, trans_NOP, a) TRANS(NOP_v9, 64, trans_NOP, a) -static bool do_arith_int(DisasContext *dc, arg_r_r_ri_cc *a, int cc_op, +static bool do_arith_int(DisasContext *dc, arg_r_r_ri_cc *a, void (*func)(TCGv, TCGv, TCGv), void (*funci)(TCGv, TCGv, target_long), bool logic_cc) @@ -3536,8 +3510,6 @@ static bool do_arith_int(DisasContext *dc, arg_r_r_ri_cc *a, int cc_op, if (logic_cc) { dst = cpu_cc_N; - } else if (a->cc && cc_op > CC_OP_FLAGS) { - dst = cpu_cc_dst; } else { dst = gen_dest_gpr(dc, a->rd); } @@ -3564,42 +3536,36 @@ static bool do_arith_int(DisasContext *dc, arg_r_r_ri_cc *a, int cc_op, } gen_store_gpr(dc, a->rd, dst); - - if (a->cc) { - tcg_gen_movi_i32(cpu_cc_op, cc_op); - dc->cc_op = cc_op; - } return advance_pc(dc); } -static bool do_arith(DisasContext *dc, arg_r_r_ri_cc *a, int cc_op, +static bool do_arith(DisasContext *dc, arg_r_r_ri_cc *a, void (*func)(TCGv, TCGv, TCGv), void (*funci)(TCGv, TCGv, target_long), void (*func_cc)(TCGv, TCGv, TCGv)) { if (a->cc) { - assert(cc_op >= 0); - return do_arith_int(dc, a, cc_op, func_cc, NULL, false); + return do_arith_int(dc, a, func_cc, NULL, false); } - return do_arith_int(dc, a, cc_op, func, funci, false); + return do_arith_int(dc, a, func, funci, false); } static bool do_logic(DisasContext *dc, arg_r_r_ri_cc *a, void (*func)(TCGv, TCGv, TCGv), void (*funci)(TCGv, TCGv, target_long)) { - return do_arith_int(dc, a, CC_OP_FLAGS, func, funci, a->cc); + return do_arith_int(dc, a, func, funci, a->cc); } -TRANS(ADD, ALL, do_arith, a, CC_OP_FLAGS, - tcg_gen_add_tl, tcg_gen_addi_tl, gen_op_addcc) -TRANS(SUB, ALL, do_arith, a, CC_OP_FLAGS, - tcg_gen_sub_tl, tcg_gen_subi_tl, gen_op_subcc) +TRANS(ADD, ALL, do_arith, a, tcg_gen_add_tl, tcg_gen_addi_tl, gen_op_addcc) +TRANS(SUB, ALL, do_arith, a, tcg_gen_sub_tl, tcg_gen_subi_tl, gen_op_subcc) +TRANS(ADDC, ALL, do_arith, a, gen_op_addc, NULL, gen_op_addccc) +TRANS(SUBC, ALL, do_arith, a, gen_op_subc, NULL, gen_op_subccc) -TRANS(TADDcc, ALL, do_arith, a, CC_OP_FLAGS, NULL, NULL, gen_op_taddcc) -TRANS(TSUBcc, ALL, do_arith, a, CC_OP_FLAGS, NULL, NULL, gen_op_tsubcc) -TRANS(TADDccTV, ALL, do_arith, a, CC_OP_FLAGS, NULL, NULL, gen_op_taddcctv) -TRANS(TSUBccTV, ALL, do_arith, a, CC_OP_FLAGS, NULL, NULL, gen_op_tsubcctv) +TRANS(TADDcc, ALL, do_arith, a, NULL, NULL, gen_op_taddcc) +TRANS(TSUBcc, ALL, do_arith, a, NULL, NULL, gen_op_tsubcc) +TRANS(TADDccTV, ALL, do_arith, a, NULL, NULL, gen_op_taddcctv) +TRANS(TSUBccTV, ALL, do_arith, a, NULL, NULL, gen_op_tsubcctv) TRANS(AND, ALL, do_logic, a, tcg_gen_and_tl, tcg_gen_andi_tl) TRANS(XOR, ALL, do_logic, a, tcg_gen_xor_tl, tcg_gen_xori_tl) @@ -3607,17 +3573,18 @@ TRANS(ANDN, ALL, do_logic, a, tcg_gen_andc_tl, NULL) TRANS(ORN, ALL, do_logic, a, tcg_gen_orc_tl, NULL) TRANS(XORN, ALL, do_logic, a, tcg_gen_eqv_tl, NULL) -TRANS(MULX, 64, do_arith, a, -1, tcg_gen_mul_tl, tcg_gen_muli_tl, NULL) +TRANS(MULX, 64, do_arith, a, tcg_gen_mul_tl, tcg_gen_muli_tl, NULL) TRANS(UMUL, MUL, do_logic, a, gen_op_umul, NULL) TRANS(SMUL, MUL, do_logic, a, gen_op_smul, NULL) +TRANS(MULScc, ALL, do_arith, a, NULL, NULL, gen_op_mulscc) -TRANS(UDIVX, 64, do_arith, a, -1, gen_op_udivx, NULL, NULL) -TRANS(SDIVX, 64, do_arith, a, -1, gen_op_sdivx, NULL, NULL) -TRANS(UDIV, DIV, do_arith, a, CC_OP_FLAGS, gen_op_udiv, NULL, gen_op_udivcc) -TRANS(SDIV, DIV, do_arith, a, CC_OP_FLAGS, gen_op_sdiv, NULL, gen_op_sdivcc) +TRANS(UDIVX, 64, do_arith, a, gen_op_udivx, NULL, NULL) +TRANS(SDIVX, 64, do_arith, a, gen_op_sdivx, NULL, NULL) +TRANS(UDIV, DIV, do_arith, a, gen_op_udiv, NULL, gen_op_udivcc) +TRANS(SDIV, DIV, do_arith, a, gen_op_sdiv, NULL, gen_op_sdivcc) /* TODO: Should have feature bit -- comes in with UltraSparc T2. */ -TRANS(POPC, 64, do_arith, a, -1, gen_op_popc, NULL, NULL) +TRANS(POPC, 64, do_arith, a, gen_op_popc, NULL, NULL) static bool trans_OR(DisasContext *dc, arg_r_r_ri_cc *a) { @@ -3636,24 +3603,6 @@ static bool trans_OR(DisasContext *dc, arg_r_r_ri_cc *a) return do_logic(dc, a, tcg_gen_or_tl, tcg_gen_ori_tl); } -static bool trans_ADDC(DisasContext *dc, arg_r_r_ri_cc *a) -{ - update_psr(dc); - return do_arith(dc, a, CC_OP_FLAGS, gen_op_addc, NULL, gen_op_addccc); -} - -static bool trans_SUBC(DisasContext *dc, arg_r_r_ri_cc *a) -{ - update_psr(dc); - return do_arith(dc, a, CC_OP_FLAGS, gen_op_subc, NULL, gen_op_subccc); -} - -static bool trans_MULScc(DisasContext *dc, arg_r_r_ri_cc *a) -{ - update_psr(dc); - return do_arith(dc, a, CC_OP_FLAGS, NULL, NULL, gen_op_mulscc); -} - static bool gen_edge(DisasContext *dc, arg_r_r_r *a, int width, bool cc, bool left) { @@ -3667,8 +3616,6 @@ static bool gen_edge(DisasContext *dc, arg_r_r_r *a, if (cc) { gen_op_subcc(cpu_cc_N, s1, s2); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_FLAGS); - dc->cc_op = CC_OP_FLAGS; } /* @@ -5080,7 +5027,6 @@ static void sparc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) dc->pc = dc->base.pc_first; dc->npc = (target_ulong)dc->base.tb->cs_base; - dc->cc_op = CC_OP_DYNAMIC; dc->mem_idx = dc->base.tb->flags & TB_FLAG_MMU_MASK; dc->def = &env->def; dc->fpu_enabled = tb_fpu_enabled(dc->base.tb->flags); @@ -5269,13 +5215,6 @@ void sparc_tcg_init(void) "f48", "f50", "f52", "f54", "f56", "f58", "f60", "f62", }; - static const struct { TCGv_i32 *ptr; int off; const char *name; } r32[] = { -#ifdef TARGET_SPARC64 - { &cpu_fprs, offsetof(CPUSPARCState, fprs), "fprs" }, -#endif - { &cpu_cc_op, offsetof(CPUSPARCState, cc_op), "cc_op" }, - }; - static const struct { TCGv *ptr; int off; const char *name; } rtl[] = { #ifdef TARGET_SPARC64 { &cpu_gsr, offsetof(CPUSPARCState, gsr), "gsr" }, @@ -5287,9 +5226,6 @@ void sparc_tcg_init(void) { &cpu_icc_Z, offsetof(CPUSPARCState, icc_Z), "icc_Z" }, { &cpu_icc_C, offsetof(CPUSPARCState, icc_C), "icc_C" }, { &cpu_cond, offsetof(CPUSPARCState, cond), "cond" }, - { &cpu_cc_src, offsetof(CPUSPARCState, cc_src), "cc_src" }, - { &cpu_cc_src2, offsetof(CPUSPARCState, cc_src2), "cc_src2" }, - { &cpu_cc_dst, offsetof(CPUSPARCState, cc_dst), "cc_dst" }, { &cpu_fsr, offsetof(CPUSPARCState, fsr), "fsr" }, { &cpu_pc, offsetof(CPUSPARCState, pc), "pc" }, { &cpu_npc, offsetof(CPUSPARCState, npc), "npc" }, @@ -5303,10 +5239,6 @@ void sparc_tcg_init(void) offsetof(CPUSPARCState, regwptr), "regwptr"); - for (i = 0; i < ARRAY_SIZE(r32); ++i) { - *r32[i].ptr = tcg_global_mem_new_i32(tcg_env, r32[i].off, r32[i].name); - } - for (i = 0; i < ARRAY_SIZE(rtl); ++i) { *rtl[i].ptr = tcg_global_mem_new(tcg_env, rtl[i].off, rtl[i].name); } @@ -5329,6 +5261,11 @@ void sparc_tcg_init(void) offsetof(CPUSPARCState, fpr[i]), fregnames[i]); } + +#ifdef TARGET_SPARC64 + cpu_fprs = tcg_global_mem_new_i32(tcg_env, + offsetof(CPUSPARCState, fprs), "fprs"); +#endif } void sparc_restore_state_to_opc(CPUState *cs, diff --git a/target/sparc/win_helper.c b/target/sparc/win_helper.c index f0ff6bf5db..16d1c70fe7 100644 --- a/target/sparc/win_helper.c +++ b/target/sparc/win_helper.c @@ -55,8 +55,6 @@ target_ulong cpu_get_psr(CPUSPARCState *env) { target_ulong icc = 0; - helper_compute_psr(env); - icc |= ((int32_t)env->cc_N < 0) << PSR_NEG_SHIFT; icc |= ((int32_t)env->cc_V < 0) << PSR_OVF_SHIFT; icc |= ((int32_t)env->icc_Z == 0) << PSR_ZERO_SHIFT; @@ -103,7 +101,6 @@ void cpu_put_psr_raw(CPUSPARCState *env, target_ulong val) env->psrps = (val & PSR_PS) ? 1 : 0; env->psret = (val & PSR_ET) ? 1 : 0; #endif - env->cc_op = CC_OP_FLAGS; #if !defined(TARGET_SPARC64) cpu_set_cwp(env, val & PSR_CWP); #endif @@ -272,8 +269,6 @@ target_ulong cpu_get_ccr(CPUSPARCState *env) { target_ulong ccr = 0; - helper_compute_psr(env); - ccr |= (env->icc_C >> 32) & 1; ccr |= ((int32_t)env->cc_V < 0) << 1; ccr |= ((int32_t)env->icc_Z == 0) << 2; @@ -295,8 +290,6 @@ void cpu_put_ccr(CPUSPARCState *env, target_ulong val) env->xcc_C = (val >> 4) & 1; env->icc_Z = ~val & 0x04; env->xcc_Z = ~val & 0x40; - - CC_OP = CC_OP_FLAGS; } target_ulong cpu_get_cwp64(CPUSPARCState *env) From dd7dbfcc00dc4cc349fc8f4588cecc88dd149ca5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 16 Oct 2023 08:43:24 -0700 Subject: [PATCH 429/974] target/sparc: Remove DisasCompare.is_bool Since we're going to feed cpu_cond to another comparison, we don't reqire a boolean value -- anything non-zero is sufficient. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/translate.c | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 7c4fcf8326..464f1607e3 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -180,7 +180,6 @@ typedef struct DisasContext { typedef struct { TCGCond cond; - bool is_bool; TCGv c1, c2; } DisasCompare; @@ -1039,7 +1038,6 @@ static void gen_compare(DisasCompare *cmp, bool xcc, unsigned int cond, { TCGv t1; - cmp->is_bool = false; cmp->c1 = t1 = tcg_temp_new(); cmp->c2 = tcg_constant_tl(0); @@ -1104,7 +1102,6 @@ static void gen_compare(DisasCompare *cmp, bool xcc, unsigned int cond, case 0x5: /* ltu: C */ cmp->cond = TCG_COND_NE; - cmp->is_bool = true; if (TARGET_LONG_BITS == 32 || xcc) { tcg_gen_mov_tl(t1, cpu_cc_C); } else { @@ -1132,7 +1129,6 @@ static void gen_compare(DisasCompare *cmp, bool xcc, unsigned int cond, } if (cond & 8) { cmp->cond = tcg_invert_cond(cmp->cond); - cmp->is_bool = false; } } @@ -1143,7 +1139,6 @@ static void gen_fcompare(DisasCompare *cmp, unsigned int cc, unsigned int cond) /* For now we still generate a straight boolean result. */ cmp->cond = TCG_COND_NE; - cmp->is_bool = true; cmp->c1 = r_dst = tcg_temp_new(); cmp->c2 = tcg_constant_tl(0); @@ -1230,7 +1225,6 @@ static const TCGCond gen_tcg_cond_reg[8] = { static void gen_compare_reg(DisasCompare *cmp, int cond, TCGv r_src) { cmp->cond = tcg_invert_cond(gen_tcg_cond_reg[cond]); - cmp->is_bool = false; cmp->c1 = r_src; cmp->c2 = tcg_constant_tl(0); } @@ -2232,18 +2226,14 @@ static void gen_fmovs(DisasContext *dc, DisasCompare *cmp, int rd, int rs) { #ifdef TARGET_SPARC64 TCGv_i32 c32, zero, dst, s1, s2; + TCGv_i64 c64 = tcg_temp_new_i64(); /* We have two choices here: extend the 32 bit data and use movcond_i64, or fold the comparison down to 32 bits and use movcond_i32. Choose the later. */ c32 = tcg_temp_new_i32(); - if (cmp->is_bool) { - tcg_gen_extrl_i64_i32(c32, cmp->c1); - } else { - TCGv_i64 c64 = tcg_temp_new_i64(); - tcg_gen_setcond_i64(cmp->cond, c64, cmp->c1, cmp->c2); - tcg_gen_extrl_i64_i32(c32, c64); - } + tcg_gen_setcond_i64(cmp->cond, c64, cmp->c1, cmp->c2); + tcg_gen_extrl_i64_i32(c32, c64); s1 = gen_load_fpr_F(dc, rs); s2 = gen_load_fpr_F(dc, rd); @@ -2445,8 +2435,10 @@ static bool advance_jump_cond(DisasContext *dc, DisasCompare *cmp, dc->jump_pc[0] = dest; dc->jump_pc[1] = npc + 4; dc->npc = JUMP_PC; - if (cmp->is_bool) { - tcg_gen_mov_tl(cpu_cond, cmp->c1); + + /* The condition for cpu_cond is always NE -- normalize. */ + if (cmp->cond == TCG_COND_NE) { + tcg_gen_xor_tl(cpu_cond, cmp->c1, cmp->c2); } else { tcg_gen_setcond_tl(cmp->cond, cpu_cond, cmp->c1, cmp->c2); } From c8507ebf74fc5991cd822a5a34c8d653a6225a9b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 16 Oct 2023 08:55:51 -0700 Subject: [PATCH 430/974] target/sparc: Change DisasCompare.c2 to int We don't require c2 to be variable, so emphasize that. We don't currently require c2 to be non-zero, but that will change. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/translate.c | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 464f1607e3..a405512e6c 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -180,7 +180,8 @@ typedef struct DisasContext { typedef struct { TCGCond cond; - TCGv c1, c2; + TCGv c1; + int c2; } DisasCompare; // This function uses non-native bit order @@ -1039,12 +1040,12 @@ static void gen_compare(DisasCompare *cmp, bool xcc, unsigned int cond, TCGv t1; cmp->c1 = t1 = tcg_temp_new(); - cmp->c2 = tcg_constant_tl(0); + cmp->c2 = 0; switch (cond & 7) { case 0x0: /* never */ cmp->cond = TCG_COND_NEVER; - cmp->c1 = cmp->c2; + cmp->c1 = tcg_constant_tl(0); break; case 0x1: /* eq: Z */ @@ -1140,7 +1141,7 @@ static void gen_fcompare(DisasCompare *cmp, unsigned int cc, unsigned int cond) /* For now we still generate a straight boolean result. */ cmp->cond = TCG_COND_NE; cmp->c1 = r_dst = tcg_temp_new(); - cmp->c2 = tcg_constant_tl(0); + cmp->c2 = 0; switch (cc) { default: @@ -1226,7 +1227,7 @@ static void gen_compare_reg(DisasCompare *cmp, int cond, TCGv r_src) { cmp->cond = tcg_invert_cond(gen_tcg_cond_reg[cond]); cmp->c1 = r_src; - cmp->c2 = tcg_constant_tl(0); + cmp->c2 = 0; } static void gen_op_clear_ieee_excp_and_FTT(void) @@ -2232,7 +2233,7 @@ static void gen_fmovs(DisasContext *dc, DisasCompare *cmp, int rd, int rs) or fold the comparison down to 32 bits and use movcond_i32. Choose the later. */ c32 = tcg_temp_new_i32(); - tcg_gen_setcond_i64(cmp->cond, c64, cmp->c1, cmp->c2); + tcg_gen_setcondi_i64(cmp->cond, c64, cmp->c1, cmp->c2); tcg_gen_extrl_i64_i32(c32, c64); s1 = gen_load_fpr_F(dc, rs); @@ -2252,7 +2253,7 @@ static void gen_fmovd(DisasContext *dc, DisasCompare *cmp, int rd, int rs) { #ifdef TARGET_SPARC64 TCGv_i64 dst = gen_dest_fpr_D(dc, rd); - tcg_gen_movcond_i64(cmp->cond, dst, cmp->c1, cmp->c2, + tcg_gen_movcond_i64(cmp->cond, dst, cmp->c1, tcg_constant_tl(cmp->c2), gen_load_fpr_D(dc, rs), gen_load_fpr_D(dc, rd)); gen_store_fpr_D(dc, rd, dst); @@ -2266,10 +2267,11 @@ static void gen_fmovq(DisasContext *dc, DisasCompare *cmp, int rd, int rs) #ifdef TARGET_SPARC64 int qd = QFPREG(rd); int qs = QFPREG(rs); + TCGv c2 = tcg_constant_tl(cmp->c2); - tcg_gen_movcond_i64(cmp->cond, cpu_fpr[qd / 2], cmp->c1, cmp->c2, + tcg_gen_movcond_i64(cmp->cond, cpu_fpr[qd / 2], cmp->c1, c2, cpu_fpr[qs / 2], cpu_fpr[qd / 2]); - tcg_gen_movcond_i64(cmp->cond, cpu_fpr[qd / 2 + 1], cmp->c1, cmp->c2, + tcg_gen_movcond_i64(cmp->cond, cpu_fpr[qd / 2 + 1], cmp->c1, c2, cpu_fpr[qs / 2 + 1], cpu_fpr[qd / 2 + 1]); gen_update_fprs_dirty(dc, qd); @@ -2409,7 +2411,7 @@ static bool advance_jump_cond(DisasContext *dc, DisasCompare *cmp, if (annul) { TCGLabel *l1 = gen_new_label(); - tcg_gen_brcond_tl(tcg_invert_cond(cmp->cond), cmp->c1, cmp->c2, l1); + tcg_gen_brcondi_tl(tcg_invert_cond(cmp->cond), cmp->c1, cmp->c2, l1); gen_goto_tb(dc, 0, npc, dest); gen_set_label(l1); gen_goto_tb(dc, 1, npc + 4, npc + 8); @@ -2423,7 +2425,7 @@ static bool advance_jump_cond(DisasContext *dc, DisasCompare *cmp, tcg_gen_mov_tl(cpu_pc, cpu_npc); tcg_gen_addi_tl(cpu_npc, cpu_npc, 4); tcg_gen_movcond_tl(cmp->cond, cpu_npc, - cmp->c1, cmp->c2, + cmp->c1, tcg_constant_tl(cmp->c2), tcg_constant_tl(dest), cpu_npc); dc->pc = npc; break; @@ -2438,9 +2440,9 @@ static bool advance_jump_cond(DisasContext *dc, DisasCompare *cmp, /* The condition for cpu_cond is always NE -- normalize. */ if (cmp->cond == TCG_COND_NE) { - tcg_gen_xor_tl(cpu_cond, cmp->c1, cmp->c2); + tcg_gen_xori_tl(cpu_cond, cmp->c1, cmp->c2); } else { - tcg_gen_setcond_tl(cmp->cond, cpu_cond, cmp->c1, cmp->c2); + tcg_gen_setcondi_tl(cmp->cond, cpu_cond, cmp->c1, cmp->c2); } } } @@ -2612,7 +2614,7 @@ static bool do_tcc(DisasContext *dc, int cond, int cc, flush_cond(dc); lab = delay_exceptionv(dc, trap); gen_compare(&cmp, cc, cond, dc); - tcg_gen_brcond_tl(cmp.cond, cmp.c1, cmp.c2, lab); + tcg_gen_brcondi_tl(cmp.cond, cmp.c1, cmp.c2, lab); return advance_pc(dc); } @@ -3849,8 +3851,9 @@ static TCGv gen_rs2_or_imm(DisasContext *dc, bool imm, int rs2_or_imm) static bool do_mov_cond(DisasContext *dc, DisasCompare *cmp, int rd, TCGv src2) { TCGv dst = gen_load_gpr(dc, rd); + TCGv c2 = tcg_constant_tl(cmp->c2); - tcg_gen_movcond_tl(cmp->cond, dst, cmp->c1, cmp->c2, src2, dst); + tcg_gen_movcond_tl(cmp->cond, dst, cmp->c1, c2, src2, dst); gen_store_gpr(dc, rd, dst); return advance_pc(dc); } From 816f89b7d4830b8df30ad252a6f30a0e80d70d76 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 16 Oct 2023 09:03:10 -0700 Subject: [PATCH 431/974] target/sparc: Always copy conditions into a new temporary This will allow the condition to live across changes to the global cc variables. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/translate.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index a405512e6c..dd6d43d1f1 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -1226,8 +1226,9 @@ static const TCGCond gen_tcg_cond_reg[8] = { static void gen_compare_reg(DisasCompare *cmp, int cond, TCGv r_src) { cmp->cond = tcg_invert_cond(gen_tcg_cond_reg[cond]); - cmp->c1 = r_src; + cmp->c1 = tcg_temp_new(); cmp->c2 = 0; + tcg_gen_mov_tl(cmp->c1, r_src); } static void gen_op_clear_ieee_excp_and_FTT(void) From c76c804509e75ab0307f878282bc73b7852d5360 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 16 Oct 2023 21:55:40 -0700 Subject: [PATCH 432/974] target/sparc: Do flush_cond in advance_jump_cond Do this here instead of in each caller. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/translate.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index dd6d43d1f1..2e7deb5e33 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -2407,7 +2407,10 @@ static bool advance_jump_uncond_always(DisasContext *dc, bool annul, static bool advance_jump_cond(DisasContext *dc, DisasCompare *cmp, bool annul, target_ulong dest) { - target_ulong npc = dc->npc; + target_ulong npc; + + flush_cond(dc); + npc = dc->npc; if (annul) { TCGLabel *l1 = gen_new_label(); @@ -2481,8 +2484,6 @@ static bool do_bpcc(DisasContext *dc, arg_bcc *a) case 0x8: return advance_jump_uncond_always(dc, a->a, target); default: - flush_cond(dc); - gen_compare(&cmp, a->cc, a->cond, dc); return advance_jump_cond(dc, &cmp, a->a, target); } @@ -2505,8 +2506,6 @@ static bool do_fbpfcc(DisasContext *dc, arg_bcc *a) case 0x8: return advance_jump_uncond_always(dc, a->a, target); default: - flush_cond(dc); - gen_fcompare(&cmp, a->cc, a->cond); return advance_jump_cond(dc, &cmp, a->a, target); } @@ -2527,7 +2526,6 @@ static bool trans_BPr(DisasContext *dc, arg_BPr *a) return false; } - flush_cond(dc); gen_compare_reg(&cmp, a->cond, gen_load_gpr(dc, a->rs1)); return advance_jump_cond(dc, &cmp, a->a, target); } From 4a8d145d7177f42b7e7199175d1754fc55bc932f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 16 Oct 2023 10:32:50 -0700 Subject: [PATCH 433/974] target/sparc: Merge gen_branch2 into advance_pc The function had only one caller. Canonicalize the cpu_cond test to TCG_COND_NE, the "natural" sense of its value. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/translate.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 2e7deb5e33..e134ba8821 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -908,19 +908,6 @@ static void gen_op_eval_fbo(TCGv dst, TCGv src, unsigned int fcc_offset) tcg_gen_xori_tl(dst, dst, 0x1); } -static void gen_branch2(DisasContext *dc, target_ulong pc1, - target_ulong pc2, TCGv r_cond) -{ - TCGLabel *l1 = gen_new_label(); - - tcg_gen_brcondi_tl(TCG_COND_EQ, r_cond, 0, l1); - - gen_goto_tb(dc, 0, pc1, pc1 + 4); - - gen_set_label(l1); - gen_goto_tb(dc, 1, pc2, pc2 + 4); -} - static void gen_generic_branch(DisasContext *dc) { TCGv npc0 = tcg_constant_tl(dc->jump_pc[0]); @@ -2352,6 +2339,8 @@ static int extract_qfpreg(DisasContext *dc, int x) /* Default case for non jump instructions. */ static bool advance_pc(DisasContext *dc) { + TCGLabel *l1; + if (dc->npc & 3) { switch (dc->npc) { case DYNAMIC_PC: @@ -2359,11 +2348,22 @@ static bool advance_pc(DisasContext *dc) dc->pc = dc->npc; gen_op_next_insn(); break; + case JUMP_PC: /* we can do a static jump */ - gen_branch2(dc, dc->jump_pc[0], dc->jump_pc[1], cpu_cond); + l1 = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_NE, cpu_cond, 0, l1); + + /* jump not taken */ + gen_goto_tb(dc, 1, dc->jump_pc[1], dc->jump_pc[1] + 4); + + /* jump taken */ + gen_set_label(l1); + gen_goto_tb(dc, 0, dc->jump_pc[0], dc->jump_pc[0] + 4); + dc->base.is_jmp = DISAS_NORETURN; break; + default: g_assert_not_reached(); } From 2d9bb2371d97aedd3d07eb4bccca9586cf88cf69 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 16 Oct 2023 10:59:38 -0700 Subject: [PATCH 434/974] target/sparc: Merge advance_jump_uncond_{never,always} into advance_jump_cond Handle these via TCG_COND_{ALWAYS,NEVER}. Allow dc->npc to be variable, using gen_mov_pc_npc. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/translate.c | 74 ++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 44 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index e134ba8821..cbee5435a3 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -2378,37 +2378,37 @@ static bool advance_pc(DisasContext *dc) * Major opcodes 00 and 01 -- branches, call, and sethi */ -static bool advance_jump_uncond_never(DisasContext *dc, bool annul) -{ - if (annul) { - dc->pc = dc->npc + 4; - dc->npc = dc->pc + 4; - } else { - dc->pc = dc->npc; - dc->npc = dc->pc + 4; - } - return true; -} - -static bool advance_jump_uncond_always(DisasContext *dc, bool annul, - target_ulong dest) -{ - if (annul) { - dc->pc = dest; - dc->npc = dest + 4; - } else { - dc->pc = dc->npc; - dc->npc = dest; - tcg_gen_mov_tl(cpu_pc, cpu_npc); - } - return true; -} - static bool advance_jump_cond(DisasContext *dc, DisasCompare *cmp, bool annul, target_ulong dest) { target_ulong npc; + if (cmp->cond == TCG_COND_ALWAYS) { + if (annul) { + dc->pc = dest; + dc->npc = dest + 4; + } else { + gen_mov_pc_npc(dc); + dc->npc = dest; + } + return true; + } + + if (cmp->cond == TCG_COND_NEVER) { + npc = dc->npc; + if (npc & 3) { + gen_mov_pc_npc(dc); + if (annul) { + tcg_gen_addi_tl(cpu_pc, cpu_pc, 4); + } + tcg_gen_addi_tl(cpu_npc, cpu_pc, 4); + } else { + dc->pc = npc + (annul ? 4 : 0); + dc->npc = dc->pc + 4; + } + return true; + } + flush_cond(dc); npc = dc->npc; @@ -2478,15 +2478,8 @@ static bool do_bpcc(DisasContext *dc, arg_bcc *a) target_long target = address_mask_i(dc, dc->pc + a->i * 4); DisasCompare cmp; - switch (a->cond) { - case 0x0: - return advance_jump_uncond_never(dc, a->a); - case 0x8: - return advance_jump_uncond_always(dc, a->a, target); - default: - gen_compare(&cmp, a->cc, a->cond, dc); - return advance_jump_cond(dc, &cmp, a->a, target); - } + gen_compare(&cmp, a->cc, a->cond, dc); + return advance_jump_cond(dc, &cmp, a->a, target); } TRANS(Bicc, ALL, do_bpcc, a) @@ -2500,15 +2493,8 @@ static bool do_fbpfcc(DisasContext *dc, arg_bcc *a) if (gen_trap_ifnofpu(dc)) { return true; } - switch (a->cond) { - case 0x0: - return advance_jump_uncond_never(dc, a->a); - case 0x8: - return advance_jump_uncond_always(dc, a->a, target); - default: - gen_fcompare(&cmp, a->cc, a->cond); - return advance_jump_cond(dc, &cmp, a->a, target); - } + gen_fcompare(&cmp, a->cc, a->cond); + return advance_jump_cond(dc, &cmp, a->a, target); } TRANS(FBPfcc, 64, do_fbpfcc, a) From 3951b7a87d7dfc6533e367562890b557bfb51bdc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 21 Oct 2023 00:31:41 -0700 Subject: [PATCH 435/974] target/sparc: Pass displacement to advance_jump_cond Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/translate.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index cbee5435a3..1233911b69 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -2379,8 +2379,9 @@ static bool advance_pc(DisasContext *dc) */ static bool advance_jump_cond(DisasContext *dc, DisasCompare *cmp, - bool annul, target_ulong dest) + bool annul, int disp) { + target_ulong dest = address_mask_i(dc, dc->pc + disp * 4); target_ulong npc; if (cmp->cond == TCG_COND_ALWAYS) { @@ -2475,11 +2476,10 @@ static bool gen_trap_float128(DisasContext *dc) static bool do_bpcc(DisasContext *dc, arg_bcc *a) { - target_long target = address_mask_i(dc, dc->pc + a->i * 4); DisasCompare cmp; gen_compare(&cmp, a->cc, a->cond, dc); - return advance_jump_cond(dc, &cmp, a->a, target); + return advance_jump_cond(dc, &cmp, a->a, a->i); } TRANS(Bicc, ALL, do_bpcc, a) @@ -2487,14 +2487,13 @@ TRANS(BPcc, 64, do_bpcc, a) static bool do_fbpfcc(DisasContext *dc, arg_bcc *a) { - target_long target = address_mask_i(dc, dc->pc + a->i * 4); DisasCompare cmp; if (gen_trap_ifnofpu(dc)) { return true; } gen_fcompare(&cmp, a->cc, a->cond); - return advance_jump_cond(dc, &cmp, a->a, target); + return advance_jump_cond(dc, &cmp, a->a, a->i); } TRANS(FBPfcc, 64, do_fbpfcc, a) @@ -2502,7 +2501,6 @@ TRANS(FBfcc, ALL, do_fbpfcc, a) static bool trans_BPr(DisasContext *dc, arg_BPr *a) { - target_long target = address_mask_i(dc, dc->pc + a->i * 4); DisasCompare cmp; if (!avail_64(dc)) { @@ -2513,7 +2511,7 @@ static bool trans_BPr(DisasContext *dc, arg_BPr *a) } gen_compare_reg(&cmp, a->cond, gen_load_gpr(dc, a->rs1)); - return advance_jump_cond(dc, &cmp, a->a, target); + return advance_jump_cond(dc, &cmp, a->a, a->i); } static bool trans_CALL(DisasContext *dc, arg_CALL *a) From 444d8b300a7e8eb9b6f8bc52032155cd42df7844 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 16 Oct 2023 11:07:31 -0700 Subject: [PATCH 436/974] target/sparc: Merge gen_op_next_insn into only caller Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/translate.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 1233911b69..0bbe4cff3b 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -1015,12 +1015,6 @@ static void gen_mov_pc_npc(DisasContext *dc) } } -static void gen_op_next_insn(void) -{ - tcg_gen_mov_tl(cpu_pc, cpu_npc); - tcg_gen_addi_tl(cpu_npc, cpu_npc, 4); -} - static void gen_compare(DisasCompare *cmp, bool xcc, unsigned int cond, DisasContext *dc) { @@ -2346,7 +2340,8 @@ static bool advance_pc(DisasContext *dc) case DYNAMIC_PC: case DYNAMIC_PC_LOOKUP: dc->pc = dc->npc; - gen_op_next_insn(); + tcg_gen_mov_tl(cpu_pc, cpu_npc); + tcg_gen_addi_tl(cpu_npc, cpu_npc, 4); break; case JUMP_PC: From 533f042f1460e4f369b72ed05e8b4b773a1868c0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 16 Oct 2023 11:24:32 -0700 Subject: [PATCH 437/974] target/sparc: Record entire jump condition in DisasContext Use the original condition instead of consuming cpu_cond, which will now only be live along exception paths. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/translate.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 0bbe4cff3b..5c9a3d45fa 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -146,6 +146,12 @@ static TCGv_i64 cpu_fpr[TARGET_DPREGS]; # define env64_field_offsetof(X) ({ qemu_build_not_reached(); 0; }) #endif +typedef struct DisasCompare { + TCGCond cond; + TCGv c1; + int c2; +} DisasCompare; + typedef struct DisasDelayException { struct DisasDelayException *next; TCGLabel *lab; @@ -159,7 +165,11 @@ typedef struct DisasContext { DisasContextBase base; target_ulong pc; /* current Program Counter: integer or DYNAMIC_PC */ target_ulong npc; /* next PC: integer or DYNAMIC_PC or JUMP_PC */ - target_ulong jump_pc[2]; /* used when JUMP_PC pc value is used */ + + /* Used when JUMP_PC value is used. */ + DisasCompare jump; + target_ulong jump_pc[2]; + int mem_idx; bool fpu_enabled; bool address_mask_32bit; @@ -178,12 +188,6 @@ typedef struct DisasContext { DisasDelayException *delay_excp_list; } DisasContext; -typedef struct { - TCGCond cond; - TCGv c1; - int c2; -} DisasCompare; - // This function uses non-native bit order #define GET_FIELD(X, FROM, TO) \ ((X) >> (31 - (TO)) & ((1 << ((TO) - (FROM) + 1)) - 1)) @@ -912,9 +916,9 @@ static void gen_generic_branch(DisasContext *dc) { TCGv npc0 = tcg_constant_tl(dc->jump_pc[0]); TCGv npc1 = tcg_constant_tl(dc->jump_pc[1]); - TCGv zero = tcg_constant_tl(0); + TCGv c2 = tcg_constant_tl(dc->jump.c2); - tcg_gen_movcond_tl(TCG_COND_NE, cpu_npc, cpu_cond, zero, npc0, npc1); + tcg_gen_movcond_tl(dc->jump.cond, cpu_npc, dc->jump.c1, c2, npc0, npc1); } /* call this function before using the condition register as it may @@ -2347,7 +2351,7 @@ static bool advance_pc(DisasContext *dc) case JUMP_PC: /* we can do a static jump */ l1 = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_NE, cpu_cond, 0, l1); + tcg_gen_brcondi_tl(dc->jump.cond, dc->jump.c1, dc->jump.c2, l1); /* jump not taken */ gen_goto_tb(dc, 1, dc->jump_pc[1], dc->jump_pc[1] + 4); @@ -2434,9 +2438,10 @@ static bool advance_jump_cond(DisasContext *dc, DisasCompare *cmp, } } else { dc->pc = npc; + dc->npc = JUMP_PC; + dc->jump = *cmp; dc->jump_pc[0] = dest; dc->jump_pc[1] = npc + 4; - dc->npc = JUMP_PC; /* The condition for cpu_cond is always NE -- normalize. */ if (cmp->cond == TCG_COND_NE) { From 89527e3a75c714e12fa054efa6a80e5111d89354 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 16 Oct 2023 12:24:23 -0700 Subject: [PATCH 438/974] target/sparc: Discard cpu_cond at the end of each insn If the insn raises no exceptions, there will be no path in which cpu_cond is used, and so the computation may be optimized away. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/translate.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 5c9a3d45fa..3564c6032e 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -171,6 +171,7 @@ typedef struct DisasContext { target_ulong jump_pc[2]; int mem_idx; + bool cpu_cond_live; bool fpu_enabled; bool address_mask_32bit; #ifndef CONFIG_USER_ONLY @@ -912,6 +913,19 @@ static void gen_op_eval_fbo(TCGv dst, TCGv src, unsigned int fcc_offset) tcg_gen_xori_tl(dst, dst, 0x1); } +static void finishing_insn(DisasContext *dc) +{ + /* + * From here, there is no future path through an unwinding exception. + * If the current insn cannot raise an exception, the computation of + * cpu_cond may be able to be elided. + */ + if (dc->cpu_cond_live) { + tcg_gen_discard_tl(cpu_cond); + dc->cpu_cond_live = false; + } +} + static void gen_generic_branch(DisasContext *dc) { TCGv npc0 = tcg_constant_tl(dc->jump_pc[0]); @@ -958,6 +972,7 @@ static void save_state(DisasContext *dc) static void gen_exception(DisasContext *dc, int which) { + finishing_insn(dc); save_state(dc); gen_helper_raise_exception(tcg_env, tcg_constant_i32(which)); dc->base.is_jmp = DISAS_NORETURN; @@ -999,6 +1014,8 @@ static void gen_check_align(DisasContext *dc, TCGv addr, int mask) static void gen_mov_pc_npc(DisasContext *dc) { + finishing_insn(dc); + if (dc->npc & 3) { switch (dc->npc) { case JUMP_PC: @@ -2339,6 +2356,8 @@ static bool advance_pc(DisasContext *dc) { TCGLabel *l1; + finishing_insn(dc); + if (dc->npc & 3) { switch (dc->npc) { case DYNAMIC_PC: @@ -2383,6 +2402,8 @@ static bool advance_jump_cond(DisasContext *dc, DisasCompare *cmp, target_ulong dest = address_mask_i(dc, dc->pc + disp * 4); target_ulong npc; + finishing_insn(dc); + if (cmp->cond == TCG_COND_ALWAYS) { if (annul) { dc->pc = dest; @@ -2449,6 +2470,7 @@ static bool advance_jump_cond(DisasContext *dc, DisasCompare *cmp, } else { tcg_gen_setcondi_tl(cmp->cond, cpu_cond, cmp->c1, cmp->c2); } + dc->cpu_cond_live = true; } } return true; @@ -2585,6 +2607,8 @@ static bool do_tcc(DisasContext *dc, int cond, int cc, tcg_gen_addi_i32(trap, trap, TT_TRAP); } + finishing_insn(dc); + /* Trap always. */ if (cond == 8) { save_state(dc); @@ -3201,6 +3225,7 @@ TRANS(WRSTICK_CMPR, 64, do_wr_special, a, supervisor(dc), do_wrstick_cmpr) static void do_wrpowerdown(DisasContext *dc, TCGv src) { + finishing_insn(dc); save_state(dc); gen_helper_power_down(tcg_env); } @@ -5080,6 +5105,8 @@ static void sparc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) DisasDelayException *e, *e_next; bool may_lookup; + finishing_insn(dc); + switch (dc->base.is_jmp) { case DISAS_NEXT: case DISAS_TOO_MANY: From f3141174dd990672e28db0d952e13ae19f08e825 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 16 Oct 2023 14:35:05 -0700 Subject: [PATCH 439/974] target/sparc: Implement UDIVX and SDIVX inline Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/helper.c | 24 --------- target/sparc/helper.h | 4 -- target/sparc/insns.decode | 4 +- target/sparc/translate.c | 109 +++++++++++++++++++++++++++++++++----- 4 files changed, 97 insertions(+), 44 deletions(-) diff --git a/target/sparc/helper.c b/target/sparc/helper.c index 6117e99b55..bd10b60e4b 100644 --- a/target/sparc/helper.c +++ b/target/sparc/helper.c @@ -129,30 +129,6 @@ uint64_t helper_sdiv(CPUSPARCState *env, target_ulong a, target_ulong b) return (uint32_t)r; } -#ifdef TARGET_SPARC64 -int64_t helper_sdivx(CPUSPARCState *env, int64_t a, int64_t b) -{ - if (b == 0) { - /* Raise divide by zero trap. */ - cpu_raise_exception_ra(env, TT_DIV_ZERO, GETPC()); - } else if (b == -1) { - /* Avoid overflow trap with i386 divide insn. */ - return -a; - } else { - return a / b; - } -} - -uint64_t helper_udivx(CPUSPARCState *env, uint64_t a, uint64_t b) -{ - if (b == 0) { - /* Raise divide by zero trap. */ - cpu_raise_exception_ra(env, TT_DIV_ZERO, GETPC()); - } - return a / b; -} -#endif - target_ulong helper_taddcctv(CPUSPARCState *env, target_ulong src1, target_ulong src2) { diff --git a/target/sparc/helper.h b/target/sparc/helper.h index decd94c0d6..55eff66283 100644 --- a/target/sparc/helper.h +++ b/target/sparc/helper.h @@ -31,10 +31,6 @@ DEF_HELPER_FLAGS_3(udiv, TCG_CALL_NO_WG, i64, env, tl, tl) DEF_HELPER_FLAGS_3(sdiv, TCG_CALL_NO_WG, i64, env, tl, tl) DEF_HELPER_3(taddcctv, tl, env, tl, tl) DEF_HELPER_3(tsubcctv, tl, env, tl, tl) -#ifdef TARGET_SPARC64 -DEF_HELPER_FLAGS_3(sdivx, TCG_CALL_NO_WG, s64, env, s64, s64) -DEF_HELPER_FLAGS_3(udivx, TCG_CALL_NO_WG, i64, env, i64, i64) -#endif #if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) DEF_HELPER_FLAGS_4(ld_asi, TCG_CALL_NO_WG, i64, env, tl, int, i32) DEF_HELPER_FLAGS_5(st_asi, TCG_CALL_NO_WG, void, env, tl, i64, int, i32) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 0552f1447d..52f54b87cc 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -182,8 +182,8 @@ UMUL 10 ..... 0.1010 ..... . ............. @r_r_ri_cc SMUL 10 ..... 0.1011 ..... . ............. @r_r_ri_cc MULScc 10 ..... 100100 ..... . ............. @r_r_ri_cc1 -UDIVX 10 ..... 001101 ..... . ............. @r_r_ri_cc0 -SDIVX 10 ..... 101101 ..... . ............. @r_r_ri_cc0 +UDIVX 10 ..... 001101 ..... . ............. @r_r_ri +SDIVX 10 ..... 101101 ..... . ............. @r_r_ri UDIV 10 ..... 0.1110 ..... . ............. @r_r_ri_cc SDIV 10 ..... 0.1111 ..... . ............. @r_r_ri_cc diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 3564c6032e..95cc4c71f4 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -51,12 +51,10 @@ # define gen_helper_restored(E) qemu_build_not_reached() # define gen_helper_retry(E) qemu_build_not_reached() # define gen_helper_saved(E) qemu_build_not_reached() -# define gen_helper_sdivx(D, E, A, B) qemu_build_not_reached() # define gen_helper_set_softint(E, S) qemu_build_not_reached() # define gen_helper_tick_get_count(D, E, T, C) qemu_build_not_reached() # define gen_helper_tick_set_count(P, S) qemu_build_not_reached() # define gen_helper_tick_set_limit(P, S) qemu_build_not_reached() -# define gen_helper_udivx(D, E, A, B) qemu_build_not_reached() # define gen_helper_wrccr(E, S) qemu_build_not_reached() # define gen_helper_wrcwp(E, S) qemu_build_not_reached() # define gen_helper_wrgl(E, S) qemu_build_not_reached() @@ -579,16 +577,6 @@ static void gen_op_smul(TCGv dst, TCGv src1, TCGv src2) gen_op_multiply(dst, src1, src2, 1); } -static void gen_op_udivx(TCGv dst, TCGv src1, TCGv src2) -{ - gen_helper_udivx(dst, tcg_env, src1, src2); -} - -static void gen_op_sdivx(TCGv dst, TCGv src1, TCGv src2) -{ - gen_helper_sdivx(dst, tcg_env, src1, src2); -} - static void gen_op_udiv(TCGv dst, TCGv src1, TCGv src2) { #ifdef TARGET_SPARC64 @@ -3580,8 +3568,6 @@ TRANS(UMUL, MUL, do_logic, a, gen_op_umul, NULL) TRANS(SMUL, MUL, do_logic, a, gen_op_smul, NULL) TRANS(MULScc, ALL, do_arith, a, NULL, NULL, gen_op_mulscc) -TRANS(UDIVX, 64, do_arith, a, gen_op_udivx, NULL, NULL) -TRANS(SDIVX, 64, do_arith, a, gen_op_sdivx, NULL, NULL) TRANS(UDIV, DIV, do_arith, a, gen_op_udiv, NULL, gen_op_udivcc) TRANS(SDIV, DIV, do_arith, a, gen_op_sdiv, NULL, gen_op_sdivcc) @@ -3605,6 +3591,101 @@ static bool trans_OR(DisasContext *dc, arg_r_r_ri_cc *a) return do_logic(dc, a, tcg_gen_or_tl, tcg_gen_ori_tl); } +static bool trans_UDIVX(DisasContext *dc, arg_r_r_ri *a) +{ + TCGv dst, src1, src2; + + if (!avail_64(dc)) { + return false; + } + /* For simplicity, we under-decoded the rs2 form. */ + if (!a->imm && a->rs2_or_imm & ~0x1f) { + return false; + } + + if (unlikely(a->rs2_or_imm == 0)) { + gen_exception(dc, TT_DIV_ZERO); + return true; + } + + if (a->imm) { + src2 = tcg_constant_tl(a->rs2_or_imm); + } else { + TCGLabel *lab; + + finishing_insn(dc); + flush_cond(dc); + + lab = delay_exception(dc, TT_DIV_ZERO); + src2 = cpu_regs[a->rs2_or_imm]; + tcg_gen_brcondi_tl(TCG_COND_EQ, src2, 0, lab); + } + + dst = gen_dest_gpr(dc, a->rd); + src1 = gen_load_gpr(dc, a->rs1); + + tcg_gen_divu_tl(dst, src1, src2); + gen_store_gpr(dc, a->rd, dst); + return advance_pc(dc); +} + +static bool trans_SDIVX(DisasContext *dc, arg_r_r_ri *a) +{ + TCGv dst, src1, src2; + + if (!avail_64(dc)) { + return false; + } + /* For simplicity, we under-decoded the rs2 form. */ + if (!a->imm && a->rs2_or_imm & ~0x1f) { + return false; + } + + if (unlikely(a->rs2_or_imm == 0)) { + gen_exception(dc, TT_DIV_ZERO); + return true; + } + + dst = gen_dest_gpr(dc, a->rd); + src1 = gen_load_gpr(dc, a->rs1); + + if (a->imm) { + if (unlikely(a->rs2_or_imm == -1)) { + tcg_gen_neg_tl(dst, src1); + gen_store_gpr(dc, a->rd, dst); + return advance_pc(dc); + } + src2 = tcg_constant_tl(a->rs2_or_imm); + } else { + TCGLabel *lab; + TCGv t1, t2; + + finishing_insn(dc); + flush_cond(dc); + + lab = delay_exception(dc, TT_DIV_ZERO); + src2 = cpu_regs[a->rs2_or_imm]; + tcg_gen_brcondi_tl(TCG_COND_EQ, src2, 0, lab); + + /* + * Need to avoid INT64_MIN / -1, which will trap on x86 host. + * Set SRC2 to 1 as a new divisor, to produce the correct result. + */ + t1 = tcg_temp_new(); + t2 = tcg_temp_new(); + tcg_gen_setcondi_tl(TCG_COND_EQ, t1, src1, (target_long)INT64_MIN); + tcg_gen_setcondi_tl(TCG_COND_EQ, t2, src2, -1); + tcg_gen_and_tl(t1, t1, t2); + tcg_gen_movcond_tl(TCG_COND_NE, t1, t1, tcg_constant_tl(0), + tcg_constant_tl(1), src2); + src2 = t1; + } + + tcg_gen_div_tl(dst, src1, src2); + gen_store_gpr(dc, a->rd, dst); + return advance_pc(dc); +} + static bool gen_edge(DisasContext *dc, arg_r_r_r *a, int width, bool cc, bool left) { From 3a6b8de3e2b5b7e37e0a71fd01a4e5f58d635573 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 16 Oct 2023 16:16:05 -0700 Subject: [PATCH 440/974] target/sparc: Implement UDIV inline Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 3 +- target/sparc/translate.c | 67 +++++++++++++++++++++++++++++++-------- 2 files changed, 56 insertions(+), 14 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 52f54b87cc..2d26404cb2 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -184,7 +184,8 @@ MULScc 10 ..... 100100 ..... . ............. @r_r_ri_cc1 UDIVX 10 ..... 001101 ..... . ............. @r_r_ri SDIVX 10 ..... 101101 ..... . ............. @r_r_ri -UDIV 10 ..... 0.1110 ..... . ............. @r_r_ri_cc +UDIV 10 ..... 001110 ..... . ............. @r_r_ri +UDIVcc 10 ..... 011110 ..... . ............. @r_r_ri_cc1 SDIV 10 ..... 0.1111 ..... . ............. @r_r_ri_cc TADDcc 10 ..... 100000 ..... . ............. @r_r_ri_cc1 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 95cc4c71f4..4b7d943bae 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -577,18 +577,6 @@ static void gen_op_smul(TCGv dst, TCGv src1, TCGv src2) gen_op_multiply(dst, src1, src2, 1); } -static void gen_op_udiv(TCGv dst, TCGv src1, TCGv src2) -{ -#ifdef TARGET_SPARC64 - gen_helper_udiv(dst, tcg_env, src1, src2); - tcg_gen_ext32u_tl(dst, dst); -#else - TCGv_i64 t64 = tcg_temp_new_i64(); - gen_helper_udiv(t64, tcg_env, src1, src2); - tcg_gen_trunc_i64_tl(dst, t64); -#endif -} - static void gen_op_sdiv(TCGv dst, TCGv src1, TCGv src2) { #ifdef TARGET_SPARC64 @@ -3568,7 +3556,7 @@ TRANS(UMUL, MUL, do_logic, a, gen_op_umul, NULL) TRANS(SMUL, MUL, do_logic, a, gen_op_smul, NULL) TRANS(MULScc, ALL, do_arith, a, NULL, NULL, gen_op_mulscc) -TRANS(UDIV, DIV, do_arith, a, gen_op_udiv, NULL, gen_op_udivcc) +TRANS(UDIVcc, DIV, do_arith, a, NULL, NULL, gen_op_udivcc) TRANS(SDIV, DIV, do_arith, a, gen_op_sdiv, NULL, gen_op_sdivcc) /* TODO: Should have feature bit -- comes in with UltraSparc T2. */ @@ -3591,6 +3579,59 @@ static bool trans_OR(DisasContext *dc, arg_r_r_ri_cc *a) return do_logic(dc, a, tcg_gen_or_tl, tcg_gen_ori_tl); } +static bool trans_UDIV(DisasContext *dc, arg_r_r_ri *a) +{ + TCGv_i64 t1, t2; + TCGv dst; + + if (!avail_DIV(dc)) { + return false; + } + /* For simplicity, we under-decoded the rs2 form. */ + if (!a->imm && a->rs2_or_imm & ~0x1f) { + return false; + } + + if (unlikely(a->rs2_or_imm == 0)) { + gen_exception(dc, TT_DIV_ZERO); + return true; + } + + if (a->imm) { + t2 = tcg_constant_i64((uint32_t)a->rs2_or_imm); + } else { + TCGLabel *lab; + TCGv_i32 n2; + + finishing_insn(dc); + flush_cond(dc); + + n2 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(n2, cpu_regs[a->rs2_or_imm]); + + lab = delay_exception(dc, TT_DIV_ZERO); + tcg_gen_brcondi_i32(TCG_COND_EQ, n2, 0, lab); + + t2 = tcg_temp_new_i64(); +#ifdef TARGET_SPARC64 + tcg_gen_ext32u_i64(t2, cpu_regs[a->rs2_or_imm]); +#else + tcg_gen_extu_i32_i64(t2, cpu_regs[a->rs2_or_imm]); +#endif + } + + t1 = tcg_temp_new_i64(); + tcg_gen_concat_tl_i64(t1, gen_load_gpr(dc, a->rs1), cpu_y); + + tcg_gen_divu_i64(t1, t1, t2); + tcg_gen_umin_i64(t1, t1, tcg_constant_i64(UINT32_MAX)); + + dst = gen_dest_gpr(dc, a->rd); + tcg_gen_trunc_i64_tl(dst, t1); + gen_store_gpr(dc, a->rd, dst); + return advance_pc(dc); +} + static bool trans_UDIVX(DisasContext *dc, arg_r_r_ri *a) { TCGv dst, src1, src2; From 2c4f56c9aa7e1e8a34428c4efe17788be11fb73f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 21 Oct 2023 00:46:33 -0700 Subject: [PATCH 441/974] target/sparc: Check for invalid cond in gen_compare_reg Consolidate the test here; drop the "inverted logic". Fix MOVr and FMOVR, which were missing the invalid test. Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/translate.c | 45 +++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 4b7d943bae..6fc333a6b8 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -1189,24 +1189,29 @@ static void gen_fcompare(DisasCompare *cmp, unsigned int cc, unsigned int cond) } } -// Inverted logic -static const TCGCond gen_tcg_cond_reg[8] = { - TCG_COND_NEVER, /* reserved */ - TCG_COND_NE, - TCG_COND_GT, - TCG_COND_GE, - TCG_COND_NEVER, /* reserved */ - TCG_COND_EQ, - TCG_COND_LE, - TCG_COND_LT, -}; - -static void gen_compare_reg(DisasCompare *cmp, int cond, TCGv r_src) +static bool gen_compare_reg(DisasCompare *cmp, int cond, TCGv r_src) { - cmp->cond = tcg_invert_cond(gen_tcg_cond_reg[cond]); + static const TCGCond cond_reg[4] = { + TCG_COND_NEVER, /* reserved */ + TCG_COND_EQ, + TCG_COND_LE, + TCG_COND_LT, + }; + TCGCond tcond; + + if ((cond & 3) == 0) { + return false; + } + tcond = cond_reg[cond & 3]; + if (cond & 4) { + tcond = tcg_invert_cond(tcond); + } + + cmp->cond = tcond; cmp->c1 = tcg_temp_new(); cmp->c2 = 0; tcg_gen_mov_tl(cmp->c1, r_src); + return true; } static void gen_op_clear_ieee_excp_and_FTT(void) @@ -2504,11 +2509,9 @@ static bool trans_BPr(DisasContext *dc, arg_BPr *a) if (!avail_64(dc)) { return false; } - if (gen_tcg_cond_reg[a->cond] == TCG_COND_NEVER) { + if (!gen_compare_reg(&cmp, a->cond, gen_load_gpr(dc, a->rs1))) { return false; } - - gen_compare_reg(&cmp, a->cond, gen_load_gpr(dc, a->rs1)); return advance_jump_cond(dc, &cmp, a->a, a->i); } @@ -4020,7 +4023,9 @@ static bool trans_MOVR(DisasContext *dc, arg_MOVR *a) if (src2 == NULL) { return false; } - gen_compare_reg(&cmp, a->cond, gen_load_gpr(dc, a->rs1)); + if (!gen_compare_reg(&cmp, a->cond, gen_load_gpr(dc, a->rs1))) { + return false; + } return do_mov_cond(dc, &cmp, a->rd, src2); } @@ -5007,6 +5012,9 @@ static bool do_fmovr(DisasContext *dc, arg_FMOVRs *a, bool is_128, { DisasCompare cmp; + if (!gen_compare_reg(&cmp, a->cond, gen_load_gpr(dc, a->rs1))) { + return false; + } if (gen_trap_ifnofpu(dc)) { return true; } @@ -5015,7 +5023,6 @@ static bool do_fmovr(DisasContext *dc, arg_FMOVRs *a, bool is_128, } gen_op_clear_ieee_excp_and_FTT(); - gen_compare_reg(&cmp, a->cond, gen_load_gpr(dc, a->rs1)); func(dc, &cmp, a->rd, a->rs2); return advance_pc(dc); } From e969f992c6562222e245dd8557f5b132a11ec29c Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 8 Aug 2023 17:58:46 +0100 Subject: [PATCH 442/974] i386/xen: Don't advertise XENFEAT_supervisor_mode_kernel This confuses lscpu into thinking it's running in PVH mode. Cc: qemu-stable@nongnu.org Fixes: bedcc139248 ("i386/xen: implement HYPERVISOR_xen_version") Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- target/i386/kvm/xen-emu.c | 1 - 1 file changed, 1 deletion(-) diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index 76348f9d5d..0055441b2e 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -267,7 +267,6 @@ static bool kvm_xen_hcall_xen_version(struct kvm_xen_exit *exit, X86CPU *cpu, fi.submap |= 1 << XENFEAT_writable_page_tables | 1 << XENFEAT_writable_descriptor_tables | 1 << XENFEAT_auto_translated_physmap | - 1 << XENFEAT_supervisor_mode_kernel | 1 << XENFEAT_hvm_callback_vector | 1 << XENFEAT_hvm_safe_pvclock | 1 << XENFEAT_hvm_pirqs; From e7dbb62ff19ce55548c785d76e814e7b144e6217 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 11 Oct 2023 23:30:08 +0100 Subject: [PATCH 443/974] i386/xen: fix per-vCPU upcall vector for Xen emulation The per-vCPU upcall vector support had three problems. Firstly it was using the wrong hypercall argument and would always return -EFAULT when the guest tried to set it up. Secondly it was using the wrong ioctl() to pass the vector to the kernel and thus the *kernel* would always return -EINVAL. Finally, even when delivering the event directly from userspace with an MSI, it put the destination CPU ID into the wrong bits of the MSI address. Linux doesn't (yet) use this mode so it went without decent testing for a while. Cc: qemu-stable@nongnu.org Fixes: 105b47fdf2d0 ("i386/xen: implement HVMOP_set_evtchn_upcall_vector") Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- target/i386/kvm/xen-emu.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index 0055441b2e..7c504d9fa4 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -306,7 +306,7 @@ static int kvm_xen_set_vcpu_callback_vector(CPUState *cs) trace_kvm_xen_set_vcpu_callback(cs->cpu_index, vector); - return kvm_vcpu_ioctl(cs, KVM_XEN_HVM_SET_ATTR, &xva); + return kvm_vcpu_ioctl(cs, KVM_XEN_VCPU_SET_ATTR, &xva); } static void do_set_vcpu_callback_vector(CPUState *cs, run_on_cpu_data data) @@ -440,7 +440,8 @@ void kvm_xen_inject_vcpu_callback_vector(uint32_t vcpu_id, int type) * deliver it as an MSI. */ MSIMessage msg = { - .address = APIC_DEFAULT_ADDRESS | X86_CPU(cs)->apic_id, + .address = APIC_DEFAULT_ADDRESS | + (X86_CPU(cs)->apic_id << MSI_ADDR_DEST_ID_SHIFT), .data = vector | (1UL << MSI_DATA_LEVEL_SHIFT), }; kvm_irqchip_send_msi(kvm_state, msg); @@ -849,8 +850,7 @@ static bool kvm_xen_hcall_hvm_op(struct kvm_xen_exit *exit, X86CPU *cpu, int ret = -ENOSYS; switch (cmd) { case HVMOP_set_evtchn_upcall_vector: - ret = kvm_xen_hcall_evtchn_upcall_vector(exit, cpu, - exit->u.hcall.params[0]); + ret = kvm_xen_hcall_evtchn_upcall_vector(exit, cpu, arg); break; case HVMOP_pagetable_dying: From 18e83f28bf39ffd2784aeb2e4e229096a86d349b Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 12 Oct 2023 00:06:26 +0100 Subject: [PATCH 444/974] hw/xen: select kernel mode for per-vCPU event channel upcall vector A guest which has configured the per-vCPU upcall vector may set the HVM_PARAM_CALLBACK_IRQ param to fairly much anything other than zero. For example, Linux v6.0+ after commit b1c3497e604 ("x86/xen: Add support for HVMOP_set_evtchn_upcall_vector") will just do this after setting the vector: /* Trick toolstack to think we are enlightened. */ if (!cpu) rc = xen_set_callback_via(1); That's explicitly setting the delivery to GSI#1, but it's supposed to be overridden by the per-vCPU vector setting. This mostly works in Qemu *except* for the logic to enable the in-kernel handling of event channels, which falsely determines that the kernel cannot accelerate GSI delivery in this case. Add a kvm_xen_has_vcpu_callback_vector() to report whether vCPU#0 has the vector set, and use that in xen_evtchn_set_callback_param() to enable the kernel acceleration features even when the param *appears* to be set to target a GSI. Preserve the Xen behaviour that when HVM_PARAM_CALLBACK_IRQ is set to *zero* the event channel delivery is disabled completely. (Which is what that bizarre guest behaviour is working round in the first place.) Cc: qemu-stable@nongnu.org Fixes: 91cce756179 ("hw/xen: Add xen_evtchn device for event channel emulation") Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/kvm/xen_evtchn.c | 6 ++++++ include/sysemu/kvm_xen.h | 1 + target/i386/kvm/xen-emu.c | 7 +++++++ 3 files changed, 14 insertions(+) diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c index a731738411..3d6f4b4a0a 100644 --- a/hw/i386/kvm/xen_evtchn.c +++ b/hw/i386/kvm/xen_evtchn.c @@ -490,6 +490,12 @@ int xen_evtchn_set_callback_param(uint64_t param) break; } + /* If the guest has set a per-vCPU callback vector, prefer that. */ + if (gsi && kvm_xen_has_vcpu_callback_vector()) { + in_kernel = kvm_xen_has_cap(EVTCHN_SEND); + gsi = 0; + } + if (!ret) { /* If vector delivery was turned *off* then tell the kernel */ if ((s->callback_param >> CALLBACK_VIA_TYPE_SHIFT) == diff --git a/include/sysemu/kvm_xen.h b/include/sysemu/kvm_xen.h index 595abfbe40..961c702c4e 100644 --- a/include/sysemu/kvm_xen.h +++ b/include/sysemu/kvm_xen.h @@ -22,6 +22,7 @@ int kvm_xen_soft_reset(void); uint32_t kvm_xen_get_caps(void); void *kvm_xen_get_vcpu_info_hva(uint32_t vcpu_id); +bool kvm_xen_has_vcpu_callback_vector(void); void kvm_xen_inject_vcpu_callback_vector(uint32_t vcpu_id, int type); void kvm_xen_set_callback_asserted(void); int kvm_xen_set_vcpu_virq(uint32_t vcpu_id, uint16_t virq, uint16_t port); diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index 7c504d9fa4..75b2c557b9 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -424,6 +424,13 @@ void kvm_xen_set_callback_asserted(void) } } +bool kvm_xen_has_vcpu_callback_vector(void) +{ + CPUState *cs = qemu_get_cpu(0); + + return cs && !!X86_CPU(cs)->env.xen_vcpu_callback_vector; +} + void kvm_xen_inject_vcpu_callback_vector(uint32_t vcpu_id, int type) { CPUState *cs = qemu_get_cpu(vcpu_id); From 3de75ed352411899dbc9222e82fe164890c77e78 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 18 Oct 2023 13:31:20 +0100 Subject: [PATCH 445/974] hw/xen: don't clear map_track[] in xen_gnttab_reset() The refcounts actually correspond to 'active_ref' structures stored in a GHashTable per "user" on the backend side (mostly, per XenDevice). If we zero map_track[] on reset, then when the backend drivers get torn down and release their mapping we hit the assert(s->map_track[ref] != 0) in gnt_unref(). So leave them in place. Each backend driver will disconnect and reconnect as the guest comes back up again and reconnects, and it all works out OK in the end as the old refs get dropped. Cc: qemu-stable@nongnu.org Fixes: de26b2619789 ("hw/xen: Implement soft reset for emulated gnttab") Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/kvm/xen_gnttab.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/hw/i386/kvm/xen_gnttab.c b/hw/i386/kvm/xen_gnttab.c index 21c30e3659..839ec920a1 100644 --- a/hw/i386/kvm/xen_gnttab.c +++ b/hw/i386/kvm/xen_gnttab.c @@ -541,7 +541,5 @@ int xen_gnttab_reset(void) s->entries.v1[GNTTAB_RESERVED_XENSTORE].flags = GTF_permit_access; s->entries.v1[GNTTAB_RESERVED_XENSTORE].frame = XEN_SPECIAL_PFN(XENSTORE); - memset(s->map_track, 0, s->max_frames * ENTRIES_PER_FRAME_V1); - return 0; } From 4a5780f52095f1daf23618dc6198a2a1665ea505 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 17 Oct 2023 13:34:18 +0100 Subject: [PATCH 446/974] hw/xen: fix XenStore watch delivery to guest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When fire_watch_cb() found the response buffer empty, it would call deliver_watch() to generate the XS_WATCH_EVENT message in the response buffer and send an event channel notification to the guest… without actually *copying* the response buffer into the ring. So there was nothing for the guest to see. The pending response didn't actually get processed into the ring until the guest next triggered some activity from its side. Add the missing call to put_rsp(). It might have been slightly nicer to call xen_xenstore_event() here, which would *almost* have worked. Except for the fact that it calls xen_be_evtchn_pending() to check that it really does have an event pending (and clear the eventfd for next time). And under Xen it's defined that setting that fd to O_NONBLOCK isn't guaranteed to work, so the emu implementation follows suit. This fixes Xen device hot-unplug. Cc: qemu-stable@nongnu.org Fixes: 0254c4d19df ("hw/xen: Add xenstore wire implementation and implementation stubs") Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/kvm/xen_xenstore.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hw/i386/kvm/xen_xenstore.c b/hw/i386/kvm/xen_xenstore.c index 660d0b72f9..8e716a7009 100644 --- a/hw/i386/kvm/xen_xenstore.c +++ b/hw/i386/kvm/xen_xenstore.c @@ -1357,10 +1357,12 @@ static void fire_watch_cb(void *opaque, const char *path, const char *token) } else { deliver_watch(s, path, token); /* - * If the message was queued because there was already ring activity, - * no need to wake the guest. But if not, we need to send the evtchn. + * Attempt to queue the message into the actual ring, and send + * the event channel notification if any bytes are copied. */ - xen_be_evtchn_notify(s->eh, s->be_port); + if (s->rsp_pending && put_rsp(s) > 0) { + xen_be_evtchn_notify(s->eh, s->be_port); + } } } From debc995e883b05c2fd02fb797a61ab1328e5bae2 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 24 Oct 2023 22:22:47 +0100 Subject: [PATCH 447/974] hw/xen: take iothread mutex in xen_evtchn_reset_op() The xen_evtchn_soft_reset() function requires the iothread mutex, but is also called for the EVTCHNOP_reset hypercall. Ensure the mutex is taken in that case. Cc: qemu-stable@nongnu.org Fixes: a15b10978fe6 ("hw/xen: Implement EVTCHNOP_reset") Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/kvm/xen_evtchn.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c index 3d6f4b4a0a..b2b4be9983 100644 --- a/hw/i386/kvm/xen_evtchn.c +++ b/hw/i386/kvm/xen_evtchn.c @@ -1135,6 +1135,7 @@ int xen_evtchn_reset_op(struct evtchn_reset *reset) return -ESRCH; } + QEMU_IOTHREAD_LOCK_GUARD(); return xen_evtchn_soft_reset(); } From a1c1082908dde4867b1ac55f546bea0c17d52318 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 20 Oct 2023 18:00:18 +0100 Subject: [PATCH 448/974] hw/xen: use correct default protocol for xen-block on x86 Even on x86_64 the default protocol is the x86-32 one if the guest doesn't specifically ask for x86-64. Cc: qemu-stable@nongnu.org Fixes: b6af8926fb85 ("xen: add implementations of xen-block connect and disconnect functions...") Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/block/xen-block.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/hw/block/xen-block.c b/hw/block/xen-block.c index a07cd7eb5d..bfa53960c3 100644 --- a/hw/block/xen-block.c +++ b/hw/block/xen-block.c @@ -115,9 +115,13 @@ static void xen_block_connect(XenDevice *xendev, Error **errp) return; } - if (xen_device_frontend_scanf(xendev, "protocol", "%ms", - &str) != 1) { - protocol = BLKIF_PROTOCOL_NATIVE; + if (xen_device_frontend_scanf(xendev, "protocol", "%ms", &str) != 1) { + /* x86 defaults to the 32-bit protocol even for 64-bit guests. */ + if (object_dynamic_cast(OBJECT(qdev_get_machine()), "x86-machine")) { + protocol = BLKIF_PROTOCOL_X86_32; + } else { + protocol = BLKIF_PROTOCOL_NATIVE; + } } else { if (strcmp(str, XEN_IO_PROTO_ABI_X86_32) == 0) { protocol = BLKIF_PROTOCOL_X86_32; From 9d9ae0f07bc5f7c7f51832c6446dd3e115531427 Mon Sep 17 00:00:00 2001 From: Cong Liu Date: Tue, 31 Oct 2023 09:25:15 +0800 Subject: [PATCH 449/974] virtio-gpu-rutabaga: Add empty interface to fix arm64 crash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an empty element to the interfaces array, which is consistent with the behavior of other devices in qemu and fixes the crash on arm64. 0 0x0000fffff5c18550 in () at /usr/lib64/libc.so.6 1 0x0000fffff6c9cd6c in g_strdup () at /usr/lib64/libglib-2.0.so.0 2 0x0000aaaaab4945d8 in g_strdup_inline (str=) at /usr/include/glib-2.0/glib/gstrfuncs.h:321 3 type_new (info=info@entry=0xaaaaabc1b2c8 ) at ../qom/object.c:133 4 0x0000aaaaab494f14 in type_register_internal (info=0xaaaaabc1b2c8 ) at ../qom/object.c:143 5 type_register (info=0xaaaaabc1b2c8 ) at ../qom/object.c:152 6 type_register_static (info=0xaaaaabc1b2c8 ) at ../qom/object.c:157 7 type_register_static_array (infos=, nr_infos=) at ../qom/object.c:165 8 0x0000aaaaab6147e8 in module_call_init (type=type@entry=MODULE_INIT_QOM) at ../util/module.c:109 9 0x0000aaaaab10a0ec in qemu_init_subsystems () at ../system/runstate.c:817 10 0x0000aaaaab10d334 in qemu_init (argc=13, argv=0xfffffffff198) at ../system/vl.c:2760 11 0x0000aaaaaae4da6c in main (argc=, argv=) at ../system/main.c:47 Signed-off-by: Cong Liu Reviewed-by: Gurchetan Singh Reviewed-by: Marc-André Lureau Message-Id: <20231031012515.15504-1-liucong2@kylinos.cn> --- hw/display/virtio-gpu-pci-rutabaga.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/display/virtio-gpu-pci-rutabaga.c b/hw/display/virtio-gpu-pci-rutabaga.c index c96729e198..abbb898c65 100644 --- a/hw/display/virtio-gpu-pci-rutabaga.c +++ b/hw/display/virtio-gpu-pci-rutabaga.c @@ -36,6 +36,7 @@ static const TypeInfo virtio_gpu_rutabaga_pci_info[] = { .instance_init = virtio_gpu_rutabaga_initfn, .interfaces = (InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, } }, }; From f7ecde051dd73fad8265e83c26ea69ae0a86e1d4 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Wed, 1 Nov 2023 21:45:36 +0100 Subject: [PATCH 450/974] ati-vga: Fix aperture sizes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apparently these should be half the memory region sizes confirmed at least by Radeon FCocde ROM while Rage 128 Pro ROMs don't seem to use these. Linux r100 DRM driver also checks for a bit in HOST_PATH_CNTL so we also add that even though the FCode ROM does not seem to set it. Signed-off-by: BALATON Zoltan Reviewed-by: Marc-André Lureau Message-ID: --- hw/display/ati.c | 7 +++++-- hw/display/ati_dbg.c | 1 + hw/display/ati_regs.h | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/hw/display/ati.c b/hw/display/ati.c index 6e38e00502..9a9ea754bd 100644 --- a/hw/display/ati.c +++ b/hw/display/ati.c @@ -349,14 +349,17 @@ static uint64_t ati_mm_read(void *opaque, hwaddr addr, unsigned int size) PCI_BASE_ADDRESS_0, size) & 0xfffffff0; break; case CONFIG_APER_SIZE: - val = s->vga.vram_size; + val = s->vga.vram_size / 2; break; case CONFIG_REG_1_BASE: val = pci_default_read_config(&s->dev, PCI_BASE_ADDRESS_2, size) & 0xfffffff0; break; case CONFIG_REG_APER_SIZE: - val = memory_region_size(&s->mm); + val = memory_region_size(&s->mm) / 2; + break; + case HOST_PATH_CNTL: + val = BIT(23); /* Radeon HDP_APER_CNTL */ break; case MC_STATUS: val = 5; diff --git a/hw/display/ati_dbg.c b/hw/display/ati_dbg.c index bd0ecd48c7..4aec1c383a 100644 --- a/hw/display/ati_dbg.c +++ b/hw/display/ati_dbg.c @@ -38,6 +38,7 @@ static struct ati_regdesc ati_reg_names[] = { {"CONFIG_APER_SIZE", 0x0108}, {"CONFIG_REG_1_BASE", 0x010c}, {"CONFIG_REG_APER_SIZE", 0x0110}, + {"HOST_PATH_CNTL", 0x0130}, {"MEM_CNTL", 0x0140}, {"MC_FB_LOCATION", 0x0148}, {"MC_AGP_LOCATION", 0x014C}, diff --git a/hw/display/ati_regs.h b/hw/display/ati_regs.h index d6282b2ef2..c697b328da 100644 --- a/hw/display/ati_regs.h +++ b/hw/display/ati_regs.h @@ -56,6 +56,7 @@ #define CONFIG_APER_SIZE 0x0108 #define CONFIG_REG_1_BASE 0x010c #define CONFIG_REG_APER_SIZE 0x0110 +#define HOST_PATH_CNTL 0x0130 #define MEM_CNTL 0x0140 #define MC_FB_LOCATION 0x0148 #define MC_AGP_LOCATION 0x014C From e876b3400a01c5f3947de34d6c10388f43192dc9 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Wed, 1 Nov 2023 21:45:37 +0100 Subject: [PATCH 451/974] ati-vga: Support unaligned access to GPIO DDC registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The GPIO_VGA_DDC and GPIO_DVI_DDC registers are used on Radeon for DDC access. Some drivers like the PPC Mac FCode ROM uses unaligned writes to these registers so implement this the same way as already done for GPIO_MONID which is used the same way for the Rage 128 Pro. Signed-off-by: BALATON Zoltan Acked-by: Marc-André Lureau Message-ID: --- hw/display/ati.c | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/hw/display/ati.c b/hw/display/ati.c index 9a9ea754bd..538651c233 100644 --- a/hw/display/ati.c +++ b/hw/display/ati.c @@ -319,11 +319,13 @@ static uint64_t ati_mm_read(void *opaque, hwaddr addr, unsigned int size) case DAC_CNTL: val = s->regs.dac_cntl; break; - case GPIO_VGA_DDC: - val = s->regs.gpio_vga_ddc; + case GPIO_VGA_DDC ... GPIO_VGA_DDC + 3: + val = ati_reg_read_offs(s->regs.gpio_vga_ddc, + addr - GPIO_VGA_DDC, size); break; - case GPIO_DVI_DDC: - val = s->regs.gpio_dvi_ddc; + case GPIO_DVI_DDC ... GPIO_DVI_DDC + 3: + val = ati_reg_read_offs(s->regs.gpio_dvi_ddc, + addr - GPIO_DVI_DDC, size); break; case GPIO_MONID ... GPIO_MONID + 3: val = ati_reg_read_offs(s->regs.gpio_monid, @@ -615,29 +617,34 @@ static void ati_mm_write(void *opaque, hwaddr addr, s->regs.dac_cntl = data & 0xffffe3ff; s->vga.dac_8bit = !!(data & DAC_8BIT_EN); break; - case GPIO_VGA_DDC: + /* + * GPIO regs for DDC access. Because some drivers access these via + * multiple byte writes we have to be careful when we send bits to + * avoid spurious changes in bitbang_i2c state. Only do it when either + * the enable bits are changed or output bits changed while enabled. + */ + case GPIO_VGA_DDC ... GPIO_VGA_DDC + 3: if (s->dev_id != PCI_DEVICE_ID_ATI_RAGE128_PF) { /* FIXME: Maybe add a property to select VGA or DVI port? */ } break; - case GPIO_DVI_DDC: + case GPIO_DVI_DDC ... GPIO_DVI_DDC + 3: if (s->dev_id != PCI_DEVICE_ID_ATI_RAGE128_PF) { - s->regs.gpio_dvi_ddc = ati_i2c(&s->bbi2c, data, 0); + ati_reg_write_offs(&s->regs.gpio_dvi_ddc, + addr - GPIO_DVI_DDC, data, size); + if ((addr <= GPIO_DVI_DDC + 2 && addr + size > GPIO_DVI_DDC + 2) || + (addr == GPIO_DVI_DDC && (s->regs.gpio_dvi_ddc & 0x30000))) { + s->regs.gpio_dvi_ddc = ati_i2c(&s->bbi2c, + s->regs.gpio_dvi_ddc, 0); + } } break; case GPIO_MONID ... GPIO_MONID + 3: /* FIXME What does Radeon have here? */ if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) { + /* Rage128p accesses DDC via MONID(1-2) with additional mask bit */ ati_reg_write_offs(&s->regs.gpio_monid, addr - GPIO_MONID, data, size); - /* - * Rage128p accesses DDC used to get EDID via these bits. - * Because some drivers access this via multiple byte writes - * we have to be careful when we send bits to avoid spurious - * changes in bitbang_i2c state. So only do it when mask is set - * and either the enable bits are changed or output bits changed - * while enabled. - */ if ((s->regs.gpio_monid & BIT(25)) && ((addr <= GPIO_MONID + 2 && addr + size > GPIO_MONID + 2) || (addr == GPIO_MONID && (s->regs.gpio_monid & 0x60000)))) { From bf9ac62a92ac087e44958ec18f795edfd2bf020a Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Wed, 1 Nov 2023 21:45:38 +0100 Subject: [PATCH 452/974] ati-vga: Add 30 bit palette access register MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Radeon cards have a 30 bit DAC and corresponding palette register to access it. We only use 8 bits but let the guests use 10 bit color values for those that access it through this register. Signed-off-by: BALATON Zoltan Reviewed-by: Marc-André Lureau Message-ID: <9fa19eec95d1563cc65853cf26912f230c702b32.1698871239.git.balaton@eik.bme.hu> --- hw/display/ati.c | 9 +++++++++ hw/display/ati_dbg.c | 1 + hw/display/ati_int.h | 1 + hw/display/ati_regs.h | 1 + 4 files changed, 12 insertions(+) diff --git a/hw/display/ati.c b/hw/display/ati.c index 538651c233..4868f95cf2 100644 --- a/hw/display/ati.c +++ b/hw/display/ati.c @@ -339,6 +339,9 @@ static uint64_t ati_mm_read(void *opaque, hwaddr addr, unsigned int size) case PALETTE_DATA: val = vga_ioport_read(&s->vga, VGA_PEL_D); break; + case PALETTE_30_DATA: + val = s->regs.palette[vga_ioport_read(&s->vga, VGA_PEL_IR)]; + break; case CNFG_CNTL: val = s->regs.config_cntl; break; @@ -673,6 +676,12 @@ static void ati_mm_write(void *opaque, hwaddr addr, data >>= 8; vga_ioport_write(&s->vga, VGA_PEL_D, data & 0xff); break; + case PALETTE_30_DATA: + s->regs.palette[vga_ioport_read(&s->vga, VGA_PEL_IW)] = data; + vga_ioport_write(&s->vga, VGA_PEL_D, (data >> 22) & 0xff); + vga_ioport_write(&s->vga, VGA_PEL_D, (data >> 12) & 0xff); + vga_ioport_write(&s->vga, VGA_PEL_D, (data >> 2) & 0xff); + break; case CNFG_CNTL: s->regs.config_cntl = data; break; diff --git a/hw/display/ati_dbg.c b/hw/display/ati_dbg.c index 4aec1c383a..3ffa7f35df 100644 --- a/hw/display/ati_dbg.c +++ b/hw/display/ati_dbg.c @@ -30,6 +30,7 @@ static struct ati_regdesc ati_reg_names[] = { {"AMCGPIO_EN_MIR", 0x00a8}, {"PALETTE_INDEX", 0x00b0}, {"PALETTE_DATA", 0x00b4}, + {"PALETTE_30_DATA", 0x00b8}, {"CNFG_CNTL", 0x00e0}, {"GEN_RESET_CNTL", 0x00f0}, {"CNFG_MEMSIZE", 0x00f8}, diff --git a/hw/display/ati_int.h b/hw/display/ati_int.h index e8d3c7af75..8abb873f01 100644 --- a/hw/display/ati_int.h +++ b/hw/display/ati_int.h @@ -44,6 +44,7 @@ typedef struct ATIVGARegs { uint32_t gpio_dvi_ddc; uint32_t gpio_monid; uint32_t config_cntl; + uint32_t palette[256]; uint32_t crtc_h_total_disp; uint32_t crtc_h_sync_strt_wid; uint32_t crtc_v_total_disp; diff --git a/hw/display/ati_regs.h b/hw/display/ati_regs.h index c697b328da..d7127748ff 100644 --- a/hw/display/ati_regs.h +++ b/hw/display/ati_regs.h @@ -48,6 +48,7 @@ #define AMCGPIO_EN_MIR 0x00a8 #define PALETTE_INDEX 0x00b0 #define PALETTE_DATA 0x00b4 +#define PALETTE_30_DATA 0x00b8 #define CNFG_CNTL 0x00e0 #define GEN_RESET_CNTL 0x00f0 #define CNFG_MEMSIZE 0x00f8 From 08730ee0cc01c3fceb907a93436d15170a7556c4 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Wed, 1 Nov 2023 21:45:39 +0100 Subject: [PATCH 453/974] ati-vga: Implement fallback for pixman routines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pixman routines can fail if no implementation is available and it will become optional soon so add fallbacks when pixman does not work. Signed-off-by: BALATON Zoltan Acked-by: Marc-André Lureau Message-ID: --- hw/display/ati.c | 8 +++++ hw/display/ati_2d.c | 75 +++++++++++++++++++++++++++++++------------- hw/display/ati_int.h | 1 + 3 files changed, 62 insertions(+), 22 deletions(-) diff --git a/hw/display/ati.c b/hw/display/ati.c index 4868f95cf2..9a87a5504a 100644 --- a/hw/display/ati.c +++ b/hw/display/ati.c @@ -1033,6 +1033,7 @@ static Property ati_vga_properties[] = { DEFINE_PROP_UINT16("x-device-id", ATIVGAState, dev_id, PCI_DEVICE_ID_ATI_RAGE128_PF), DEFINE_PROP_BOOL("guest_hwcursor", ATIVGAState, cursor_guest_mode, false), + DEFINE_PROP_UINT8("x-pixman", ATIVGAState, use_pixman, 3), DEFINE_PROP_END_OF_LIST() }; @@ -1054,11 +1055,18 @@ static void ati_vga_class_init(ObjectClass *klass, void *data) k->exit = ati_vga_exit; } +static void ati_vga_init(Object *o) +{ + object_property_set_description(o, "x-pixman", "Use pixman for: " + "1: fill, 2: blit"); +} + static const TypeInfo ati_vga_info = { .name = TYPE_ATI_VGA, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(ATIVGAState), .class_init = ati_vga_class_init, + .instance_init = ati_vga_init, .interfaces = (InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, diff --git a/hw/display/ati_2d.c b/hw/display/ati_2d.c index 7d786653e8..0e6b8e4367 100644 --- a/hw/display/ati_2d.c +++ b/hw/display/ati_2d.c @@ -92,6 +92,7 @@ void ati_2d_blt(ATIVGAState *s) switch (s->regs.dp_mix & GMC_ROP3_MASK) { case ROP3_SRCCOPY: { + bool fallback = false; unsigned src_x = (s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT ? s->regs.src_x : s->regs.src_x + 1 - s->regs.dst_width); unsigned src_y = (s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM ? @@ -122,27 +123,50 @@ void ati_2d_blt(ATIVGAState *s) src_bits, dst_bits, src_stride, dst_stride, bpp, bpp, src_x, src_y, dst_x, dst_y, s->regs.dst_width, s->regs.dst_height); - if (s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT && + if ((s->use_pixman & BIT(1)) && + s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT && s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM) { - pixman_blt((uint32_t *)src_bits, (uint32_t *)dst_bits, - src_stride, dst_stride, bpp, bpp, - src_x, src_y, dst_x, dst_y, - s->regs.dst_width, s->regs.dst_height); - } else { + fallback = !pixman_blt((uint32_t *)src_bits, (uint32_t *)dst_bits, + src_stride, dst_stride, bpp, bpp, + src_x, src_y, dst_x, dst_y, + s->regs.dst_width, s->regs.dst_height); + } else if (s->use_pixman & BIT(1)) { /* FIXME: We only really need a temporary if src and dst overlap */ int llb = s->regs.dst_width * (bpp / 8); int tmp_stride = DIV_ROUND_UP(llb, sizeof(uint32_t)); uint32_t *tmp = g_malloc(tmp_stride * sizeof(uint32_t) * s->regs.dst_height); - pixman_blt((uint32_t *)src_bits, tmp, - src_stride, tmp_stride, bpp, bpp, - src_x, src_y, 0, 0, - s->regs.dst_width, s->regs.dst_height); - pixman_blt(tmp, (uint32_t *)dst_bits, - tmp_stride, dst_stride, bpp, bpp, - 0, 0, dst_x, dst_y, - s->regs.dst_width, s->regs.dst_height); + fallback = !pixman_blt((uint32_t *)src_bits, tmp, + src_stride, tmp_stride, bpp, bpp, + src_x, src_y, 0, 0, + s->regs.dst_width, s->regs.dst_height); + if (!fallback) { + fallback = !pixman_blt(tmp, (uint32_t *)dst_bits, + tmp_stride, dst_stride, bpp, bpp, + 0, 0, dst_x, dst_y, + s->regs.dst_width, s->regs.dst_height); + } g_free(tmp); + } else { + fallback = true; + } + if (fallback) { + unsigned int y, i, j, bypp = bpp / 8; + unsigned int src_pitch = src_stride * sizeof(uint32_t); + unsigned int dst_pitch = dst_stride * sizeof(uint32_t); + + for (y = 0; y < s->regs.dst_height; y++) { + i = dst_x * bypp; + j = src_x * bypp; + if (s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM) { + i += (dst_y + y) * dst_pitch; + j += (src_y + y) * src_pitch; + } else { + i += (dst_y + s->regs.dst_height - 1 - y) * dst_pitch; + j += (src_y + s->regs.dst_height - 1 - y) * src_pitch; + } + memmove(&dst_bits[i], &src_bits[j], s->regs.dst_width * bypp); + } } if (dst_bits >= s->vga.vram_ptr + s->vga.vbe_start_addr && dst_bits < s->vga.vram_ptr + s->vga.vbe_start_addr + @@ -180,14 +204,21 @@ void ati_2d_blt(ATIVGAState *s) dst_stride /= sizeof(uint32_t); DPRINTF("pixman_fill(%p, %d, %d, %d, %d, %d, %d, %x)\n", - dst_bits, dst_stride, bpp, - dst_x, dst_y, - s->regs.dst_width, s->regs.dst_height, - filler); - pixman_fill((uint32_t *)dst_bits, dst_stride, bpp, - dst_x, dst_y, - s->regs.dst_width, s->regs.dst_height, - filler); + dst_bits, dst_stride, bpp, dst_x, dst_y, + s->regs.dst_width, s->regs.dst_height, filler); + if (!(s->use_pixman & BIT(0)) || + !pixman_fill((uint32_t *)dst_bits, dst_stride, bpp, dst_x, dst_y, + s->regs.dst_width, s->regs.dst_height, filler)) { + /* fallback when pixman failed or we don't want to call it */ + unsigned int x, y, i, bypp = bpp / 8; + unsigned int dst_pitch = dst_stride * sizeof(uint32_t); + for (y = 0; y < s->regs.dst_height; y++) { + i = dst_x * bypp + (dst_y + y) * dst_pitch; + for (x = 0; x < s->regs.dst_width; x++, i += bypp) { + stn_he_p(&dst_bits[i], bypp, filler); + } + } + } if (dst_bits >= s->vga.vram_ptr + s->vga.vbe_start_addr && dst_bits < s->vga.vram_ptr + s->vga.vbe_start_addr + s->vga.vbe_regs[VBE_DISPI_INDEX_YRES] * s->vga.vbe_line_offset) { diff --git a/hw/display/ati_int.h b/hw/display/ati_int.h index 8abb873f01..f5a47b82b0 100644 --- a/hw/display/ati_int.h +++ b/hw/display/ati_int.h @@ -90,6 +90,7 @@ struct ATIVGAState { char *model; uint16_t dev_id; uint8_t mode; + uint8_t use_pixman; bool cursor_guest_mode; uint16_t cursor_size; uint32_t cursor_offset; From 9c549ab6895a43ad0cb33e684e11cdb0b5400897 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 6 Sep 2023 17:00:22 +0400 Subject: [PATCH 454/974] virtio-gpu: block migration of VMs with blob=true MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "blob" resources don't have an associated pixman image: #0 pixman_image_get_stride (image=0x0) at ../pixman/pixman-image.c:921 #1 0x0000562327c25236 in virtio_gpu_save (f=0x56232bb13b00, opaque=0x56232b555a60, size=0, field=0x5623289ab6c8 <__compound_literal.3+104>, vmdesc=0x56232ab59fe0) at ../hw/display/virtio-gpu.c:1225 Related to: https://bugzilla.redhat.com/show_bug.cgi?id=2236353 Signed-off-by: Marc-André Lureau Acked-by: Peter Xu --- hw/display/virtio-gpu.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 4265316cbb..dd9965c2ef 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -26,6 +26,7 @@ #include "hw/virtio/virtio-gpu-pixman.h" #include "hw/virtio/virtio-bus.h" #include "hw/qdev-properties.h" +#include "migration/blocker.h" #include "qemu/log.h" #include "qemu/module.h" #include "qapi/error.h" @@ -40,6 +41,8 @@ virtio_gpu_find_check_resource(VirtIOGPU *g, uint32_t resource_id, static void virtio_gpu_reset_bh(void *opaque); +static Error *blob_mig_blocker; + void virtio_gpu_update_cursor_data(VirtIOGPU *g, struct virtio_gpu_scanout *s, uint32_t resource_id) @@ -1373,6 +1376,14 @@ void virtio_gpu_device_realize(DeviceState *qdev, Error **errp) error_setg(errp, "blobs and virgl are not compatible (yet)"); return; } + + if (!blob_mig_blocker) { + error_setg(&blob_mig_blocker, + "virtio-gpu blob VMs are currently not migratable."); + } + if (migrate_add_blocker(blob_mig_blocker, errp)) { + return; + } } if (!virtio_gpu_base_device_realize(qdev, @@ -1399,6 +1410,9 @@ static void virtio_gpu_device_unrealize(DeviceState *qdev) { VirtIOGPU *g = VIRTIO_GPU(qdev); + if (virtio_gpu_blob_enabled(g->parent_obj.conf)) { + migrate_del_blocker(blob_mig_blocker); + } g_clear_pointer(&g->ctrl_bh, qemu_bh_delete); g_clear_pointer(&g->cursor_bh, qemu_bh_delete); g_clear_pointer(&g->reset_bh, qemu_bh_delete); From e92ffae6ba28f27329752017b59b6133af27d6da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 7 Sep 2023 17:00:53 +0400 Subject: [PATCH 455/974] virtio-gpu: factor out restore mapping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The same function is going to be used next to restore "blob" resources. Signed-off-by: Marc-André Lureau Acked-by: Peter Xu --- hw/display/virtio-gpu.c | 60 ++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index dd9965c2ef..8efabae7ee 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -1233,6 +1233,35 @@ static int virtio_gpu_save(QEMUFile *f, void *opaque, size_t size, return vmstate_save_state(f, &vmstate_virtio_gpu_scanouts, g, NULL); } +static bool virtio_gpu_load_restore_mapping(VirtIOGPU *g, + struct virtio_gpu_simple_resource *res) +{ + int i; + + for (i = 0; i < res->iov_cnt; i++) { + hwaddr len = res->iov[i].iov_len; + res->iov[i].iov_base = + dma_memory_map(VIRTIO_DEVICE(g)->dma_as, res->addrs[i], &len, + DMA_DIRECTION_TO_DEVICE, MEMTXATTRS_UNSPECIFIED); + + if (!res->iov[i].iov_base || len != res->iov[i].iov_len) { + /* Clean up the half-a-mapping we just created... */ + if (res->iov[i].iov_base) { + dma_memory_unmap(VIRTIO_DEVICE(g)->dma_as, res->iov[i].iov_base, + len, DMA_DIRECTION_TO_DEVICE, 0); + } + /* ...and the mappings for previous loop iterations */ + res->iov_cnt = i; + virtio_gpu_cleanup_mapping(g, res); + return false; + } + } + + QTAILQ_INSERT_HEAD(&g->reslist, res, next); + g->hostmem += res->hostmem; + return true; +} + static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size, const VMStateField *field) { @@ -1297,35 +1326,12 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size, qemu_get_buffer(f, (void *)pixman_image_get_data(res->image), pixman_image_get_stride(res->image) * res->height); - /* restore mapping */ - for (i = 0; i < res->iov_cnt; i++) { - hwaddr len = res->iov[i].iov_len; - res->iov[i].iov_base = - dma_memory_map(VIRTIO_DEVICE(g)->dma_as, res->addrs[i], &len, - DMA_DIRECTION_TO_DEVICE, - MEMTXATTRS_UNSPECIFIED); - - if (!res->iov[i].iov_base || len != res->iov[i].iov_len) { - /* Clean up the half-a-mapping we just created... */ - if (res->iov[i].iov_base) { - dma_memory_unmap(VIRTIO_DEVICE(g)->dma_as, - res->iov[i].iov_base, - len, - DMA_DIRECTION_TO_DEVICE, - 0); - } - /* ...and the mappings for previous loop iterations */ - res->iov_cnt = i; - virtio_gpu_cleanup_mapping(g, res); - pixman_image_unref(res->image); - g_free(res); - return -EINVAL; - } + if (!virtio_gpu_load_restore_mapping(g, res)) { + pixman_image_unref(res->image); + g_free(res); + return -EINVAL; } - QTAILQ_INSERT_HEAD(&g->reslist, res, next); - g->hostmem += res->hostmem; - resource_id = qemu_get_be32(f); } From 0f2a301db322a7636edf9a759deb46dd8caa8824 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 7 Sep 2023 17:02:53 +0400 Subject: [PATCH 456/974] virtio-gpu: move scanout restoration to post_load MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As we are going to introduce an extra subsection for "blob" resources, scanout have to be restored after. Signed-off-by: Marc-André Lureau Acked-by: Peter Xu --- hw/display/virtio-gpu.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 8efabae7ee..26065c6466 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -1267,7 +1267,6 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size, { VirtIOGPU *g = opaque; struct virtio_gpu_simple_resource *res; - struct virtio_gpu_scanout *scanout; uint32_t resource_id, pformat; void *bits = NULL; int i; @@ -1337,6 +1336,17 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size, /* load & apply scanout state */ vmstate_load_state(f, &vmstate_virtio_gpu_scanouts, g, 1); + + return 0; +} + +static int virtio_gpu_post_load(void *opaque, int version_id) +{ + VirtIOGPU *g = opaque; + struct virtio_gpu_scanout *scanout; + struct virtio_gpu_simple_resource *res; + int i; + for (i = 0; i < g->parent_obj.conf.max_outputs; i++) { /* FIXME: should take scanout.r.{x,y} into account */ scanout = &g->parent_obj.scanout[i]; @@ -1520,6 +1530,7 @@ static const VMStateDescription vmstate_virtio_gpu = { } /* device */, VMSTATE_END_OF_LIST() }, + .post_load = virtio_gpu_post_load, }; static Property virtio_gpu_properties[] = { From 54876d25fe298e0761556253f1c07af6160d5c10 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Thu, 2 Nov 2023 15:12:22 +0800 Subject: [PATCH 457/974] vfio/container: Move IBM EEH related functions into spapr_pci_vfio.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With vfio_eeh_as_ok/vfio_eeh_as_op moved and made static, vfio.h becomes empty and is deleted. No functional changes intended. Suggested-by: Cédric Le Goater Signed-off-by: Zhenzhong Duan Acked-by: Eric Farman Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/ppc/spapr_pci_vfio.c | 100 +++++++++++++++++++++++++++++++++++++++- hw/vfio/ap.c | 1 - hw/vfio/ccw.c | 1 - hw/vfio/common.c | 1 - hw/vfio/container.c | 98 --------------------------------------- hw/vfio/helpers.c | 1 - include/hw/vfio/vfio.h | 7 --- 7 files changed, 99 insertions(+), 110 deletions(-) delete mode 100644 include/hw/vfio/vfio.h diff --git a/hw/ppc/spapr_pci_vfio.c b/hw/ppc/spapr_pci_vfio.c index 9016720547..f283f7e38d 100644 --- a/hw/ppc/spapr_pci_vfio.c +++ b/hw/ppc/spapr_pci_vfio.c @@ -18,14 +18,112 @@ */ #include "qemu/osdep.h" +#include #include #include "hw/ppc/spapr.h" #include "hw/pci-host/spapr.h" #include "hw/pci/msix.h" #include "hw/pci/pci_device.h" -#include "hw/vfio/vfio.h" +#include "hw/vfio/vfio-common.h" #include "qemu/error-report.h" +/* + * Interfaces for IBM EEH (Enhanced Error Handling) + */ +static bool vfio_eeh_container_ok(VFIOContainer *container) +{ + /* + * As of 2016-03-04 (linux-4.5) the host kernel EEH/VFIO + * implementation is broken if there are multiple groups in a + * container. The hardware works in units of Partitionable + * Endpoints (== IOMMU groups) and the EEH operations naively + * iterate across all groups in the container, without any logic + * to make sure the groups have their state synchronized. For + * certain operations (ENABLE) that might be ok, until an error + * occurs, but for others (GET_STATE) it's clearly broken. + */ + + /* + * XXX Once fixed kernels exist, test for them here + */ + + if (QLIST_EMPTY(&container->group_list)) { + return false; + } + + if (QLIST_NEXT(QLIST_FIRST(&container->group_list), container_next)) { + return false; + } + + return true; +} + +static int vfio_eeh_container_op(VFIOContainer *container, uint32_t op) +{ + struct vfio_eeh_pe_op pe_op = { + .argsz = sizeof(pe_op), + .op = op, + }; + int ret; + + if (!vfio_eeh_container_ok(container)) { + error_report("vfio/eeh: EEH_PE_OP 0x%x: " + "kernel requires a container with exactly one group", op); + return -EPERM; + } + + ret = ioctl(container->fd, VFIO_EEH_PE_OP, &pe_op); + if (ret < 0) { + error_report("vfio/eeh: EEH_PE_OP 0x%x failed: %m", op); + return -errno; + } + + return ret; +} + +static VFIOContainer *vfio_eeh_as_container(AddressSpace *as) +{ + VFIOAddressSpace *space = vfio_get_address_space(as); + VFIOContainer *container = NULL; + + if (QLIST_EMPTY(&space->containers)) { + /* No containers to act on */ + goto out; + } + + container = QLIST_FIRST(&space->containers); + + if (QLIST_NEXT(container, next)) { + /* + * We don't yet have logic to synchronize EEH state across + * multiple containers + */ + container = NULL; + goto out; + } + +out: + vfio_put_address_space(space); + return container; +} + +static bool vfio_eeh_as_ok(AddressSpace *as) +{ + VFIOContainer *container = vfio_eeh_as_container(as); + + return (container != NULL) && vfio_eeh_container_ok(container); +} + +static int vfio_eeh_as_op(AddressSpace *as, uint32_t op) +{ + VFIOContainer *container = vfio_eeh_as_container(as); + + if (!container) { + return -ENODEV; + } + return vfio_eeh_container_op(container, op); +} + bool spapr_phb_eeh_available(SpaprPhbState *sphb) { return vfio_eeh_as_ok(&sphb->iommu_as); diff --git a/hw/vfio/ap.c b/hw/vfio/ap.c index 5f257bffb9..bbf69ff55a 100644 --- a/hw/vfio/ap.c +++ b/hw/vfio/ap.c @@ -14,7 +14,6 @@ #include #include #include "qapi/error.h" -#include "hw/vfio/vfio.h" #include "hw/vfio/vfio-common.h" #include "hw/s390x/ap-device.h" #include "qemu/error-report.h" diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index 6623ae237b..d857bb8d0f 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -20,7 +20,6 @@ #include #include "qapi/error.h" -#include "hw/vfio/vfio.h" #include "hw/vfio/vfio-common.h" #include "hw/s390x/s390-ccw.h" #include "hw/s390x/vfio-ccw.h" diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 9c5c6433f2..e72055e752 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -26,7 +26,6 @@ #include #include "hw/vfio/vfio-common.h" -#include "hw/vfio/vfio.h" #include "hw/vfio/pci.h" #include "exec/address-spaces.h" #include "exec/memory.h" diff --git a/hw/vfio/container.c b/hw/vfio/container.c index fc88222377..83c0f05bba 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -26,7 +26,6 @@ #include #include "hw/vfio/vfio-common.h" -#include "hw/vfio/vfio.h" #include "exec/address-spaces.h" #include "exec/memory.h" #include "exec/ram_addr.h" @@ -1011,103 +1010,6 @@ static void vfio_put_base_device(VFIODevice *vbasedev) close(vbasedev->fd); } -/* - * Interfaces for IBM EEH (Enhanced Error Handling) - */ -static bool vfio_eeh_container_ok(VFIOContainer *container) -{ - /* - * As of 2016-03-04 (linux-4.5) the host kernel EEH/VFIO - * implementation is broken if there are multiple groups in a - * container. The hardware works in units of Partitionable - * Endpoints (== IOMMU groups) and the EEH operations naively - * iterate across all groups in the container, without any logic - * to make sure the groups have their state synchronized. For - * certain operations (ENABLE) that might be ok, until an error - * occurs, but for others (GET_STATE) it's clearly broken. - */ - - /* - * XXX Once fixed kernels exist, test for them here - */ - - if (QLIST_EMPTY(&container->group_list)) { - return false; - } - - if (QLIST_NEXT(QLIST_FIRST(&container->group_list), container_next)) { - return false; - } - - return true; -} - -static int vfio_eeh_container_op(VFIOContainer *container, uint32_t op) -{ - struct vfio_eeh_pe_op pe_op = { - .argsz = sizeof(pe_op), - .op = op, - }; - int ret; - - if (!vfio_eeh_container_ok(container)) { - error_report("vfio/eeh: EEH_PE_OP 0x%x: " - "kernel requires a container with exactly one group", op); - return -EPERM; - } - - ret = ioctl(container->fd, VFIO_EEH_PE_OP, &pe_op); - if (ret < 0) { - error_report("vfio/eeh: EEH_PE_OP 0x%x failed: %m", op); - return -errno; - } - - return ret; -} - -static VFIOContainer *vfio_eeh_as_container(AddressSpace *as) -{ - VFIOAddressSpace *space = vfio_get_address_space(as); - VFIOContainer *container = NULL; - - if (QLIST_EMPTY(&space->containers)) { - /* No containers to act on */ - goto out; - } - - container = QLIST_FIRST(&space->containers); - - if (QLIST_NEXT(container, next)) { - /* - * We don't yet have logic to synchronize EEH state across - * multiple containers - */ - container = NULL; - goto out; - } - -out: - vfio_put_address_space(space); - return container; -} - -bool vfio_eeh_as_ok(AddressSpace *as) -{ - VFIOContainer *container = vfio_eeh_as_container(as); - - return (container != NULL) && vfio_eeh_container_ok(container); -} - -int vfio_eeh_as_op(AddressSpace *as, uint32_t op) -{ - VFIOContainer *container = vfio_eeh_as_container(as); - - if (!container) { - return -ENODEV; - } - return vfio_eeh_container_op(container, op); -} - static int vfio_device_groupid(VFIODevice *vbasedev, Error **errp) { char *tmp, group_path[PATH_MAX], *group_name; diff --git a/hw/vfio/helpers.c b/hw/vfio/helpers.c index 7e5da21b31..168847e7c5 100644 --- a/hw/vfio/helpers.c +++ b/hw/vfio/helpers.c @@ -23,7 +23,6 @@ #include #include "hw/vfio/vfio-common.h" -#include "hw/vfio/vfio.h" #include "hw/hw.h" #include "trace.h" #include "qapi/error.h" diff --git a/include/hw/vfio/vfio.h b/include/hw/vfio/vfio.h deleted file mode 100644 index 86248f5436..0000000000 --- a/include/hw/vfio/vfio.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef HW_VFIO_H -#define HW_VFIO_H - -bool vfio_eeh_as_ok(AddressSpace *as); -int vfio_eeh_as_op(AddressSpace *as, uint32_t op); - -#endif From 521c8f4ebc5923576436228211f8947f306b9d82 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Thu, 2 Nov 2023 15:12:23 +0800 Subject: [PATCH 458/974] vfio/container: Move vfio_container_add/del_section_window into spapr.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vfio_container_add/del_section_window are spapr specific functions, so move them into spapr.c to make container.c cleaner. No functional changes intended. Suggested-by: Cédric Le Goater Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/container.c | 90 --------------------------------------------- hw/vfio/spapr.c | 90 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 90 deletions(-) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 83c0f05bba..7a3f005d1b 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -20,9 +20,6 @@ #include "qemu/osdep.h" #include -#ifdef CONFIG_KVM -#include -#endif #include #include "hw/vfio/vfio-common.h" @@ -32,7 +29,6 @@ #include "hw/hw.h" #include "qemu/error-report.h" #include "qemu/range.h" -#include "sysemu/kvm.h" #include "sysemu/reset.h" #include "trace.h" #include "qapi/error.h" @@ -204,92 +200,6 @@ int vfio_dma_map(VFIOContainer *container, hwaddr iova, return -errno; } -int vfio_container_add_section_window(VFIOContainer *container, - MemoryRegionSection *section, - Error **errp) -{ - VFIOHostDMAWindow *hostwin; - hwaddr pgsize = 0; - int ret; - - if (container->iommu_type != VFIO_SPAPR_TCE_v2_IOMMU) { - return 0; - } - - /* For now intersections are not allowed, we may relax this later */ - QLIST_FOREACH(hostwin, &container->hostwin_list, hostwin_next) { - if (ranges_overlap(hostwin->min_iova, - hostwin->max_iova - hostwin->min_iova + 1, - section->offset_within_address_space, - int128_get64(section->size))) { - error_setg(errp, - "region [0x%"PRIx64",0x%"PRIx64"] overlaps with existing" - "host DMA window [0x%"PRIx64",0x%"PRIx64"]", - section->offset_within_address_space, - section->offset_within_address_space + - int128_get64(section->size) - 1, - hostwin->min_iova, hostwin->max_iova); - return -EINVAL; - } - } - - ret = vfio_spapr_create_window(container, section, &pgsize); - if (ret) { - error_setg_errno(errp, -ret, "Failed to create SPAPR window"); - return ret; - } - - vfio_host_win_add(container, section->offset_within_address_space, - section->offset_within_address_space + - int128_get64(section->size) - 1, pgsize); -#ifdef CONFIG_KVM - if (kvm_enabled()) { - VFIOGroup *group; - IOMMUMemoryRegion *iommu_mr = IOMMU_MEMORY_REGION(section->mr); - struct kvm_vfio_spapr_tce param; - struct kvm_device_attr attr = { - .group = KVM_DEV_VFIO_GROUP, - .attr = KVM_DEV_VFIO_GROUP_SET_SPAPR_TCE, - .addr = (uint64_t)(unsigned long)¶m, - }; - - if (!memory_region_iommu_get_attr(iommu_mr, IOMMU_ATTR_SPAPR_TCE_FD, - ¶m.tablefd)) { - QLIST_FOREACH(group, &container->group_list, container_next) { - param.groupfd = group->fd; - if (ioctl(vfio_kvm_device_fd, KVM_SET_DEVICE_ATTR, &attr)) { - error_setg_errno(errp, errno, - "vfio: failed GROUP_SET_SPAPR_TCE for " - "KVM VFIO device %d and group fd %d", - param.tablefd, param.groupfd); - return -errno; - } - trace_vfio_spapr_group_attach(param.groupfd, param.tablefd); - } - } - } -#endif - return 0; -} - -void vfio_container_del_section_window(VFIOContainer *container, - MemoryRegionSection *section) -{ - if (container->iommu_type != VFIO_SPAPR_TCE_v2_IOMMU) { - return; - } - - vfio_spapr_remove_window(container, - section->offset_within_address_space); - if (vfio_host_win_del(container, - section->offset_within_address_space, - section->offset_within_address_space + - int128_get64(section->size) - 1) < 0) { - hw_error("%s: Cannot delete missing window at %"HWADDR_PRIx, - __func__, section->offset_within_address_space); - } -} - int vfio_set_dirty_page_tracking(VFIOContainer *container, bool start) { int ret; diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c index 9ec1e95f6d..9a7517c042 100644 --- a/hw/vfio/spapr.c +++ b/hw/vfio/spapr.c @@ -11,6 +11,10 @@ #include "qemu/osdep.h" #include #include +#ifdef CONFIG_KVM +#include +#endif +#include "sysemu/kvm.h" #include "hw/vfio/vfio-common.h" #include "hw/hw.h" @@ -253,3 +257,89 @@ int vfio_spapr_remove_window(VFIOContainer *container, return 0; } + +int vfio_container_add_section_window(VFIOContainer *container, + MemoryRegionSection *section, + Error **errp) +{ + VFIOHostDMAWindow *hostwin; + hwaddr pgsize = 0; + int ret; + + if (container->iommu_type != VFIO_SPAPR_TCE_v2_IOMMU) { + return 0; + } + + /* For now intersections are not allowed, we may relax this later */ + QLIST_FOREACH(hostwin, &container->hostwin_list, hostwin_next) { + if (ranges_overlap(hostwin->min_iova, + hostwin->max_iova - hostwin->min_iova + 1, + section->offset_within_address_space, + int128_get64(section->size))) { + error_setg(errp, + "region [0x%"PRIx64",0x%"PRIx64"] overlaps with existing" + "host DMA window [0x%"PRIx64",0x%"PRIx64"]", + section->offset_within_address_space, + section->offset_within_address_space + + int128_get64(section->size) - 1, + hostwin->min_iova, hostwin->max_iova); + return -EINVAL; + } + } + + ret = vfio_spapr_create_window(container, section, &pgsize); + if (ret) { + error_setg_errno(errp, -ret, "Failed to create SPAPR window"); + return ret; + } + + vfio_host_win_add(container, section->offset_within_address_space, + section->offset_within_address_space + + int128_get64(section->size) - 1, pgsize); +#ifdef CONFIG_KVM + if (kvm_enabled()) { + VFIOGroup *group; + IOMMUMemoryRegion *iommu_mr = IOMMU_MEMORY_REGION(section->mr); + struct kvm_vfio_spapr_tce param; + struct kvm_device_attr attr = { + .group = KVM_DEV_VFIO_GROUP, + .attr = KVM_DEV_VFIO_GROUP_SET_SPAPR_TCE, + .addr = (uint64_t)(unsigned long)¶m, + }; + + if (!memory_region_iommu_get_attr(iommu_mr, IOMMU_ATTR_SPAPR_TCE_FD, + ¶m.tablefd)) { + QLIST_FOREACH(group, &container->group_list, container_next) { + param.groupfd = group->fd; + if (ioctl(vfio_kvm_device_fd, KVM_SET_DEVICE_ATTR, &attr)) { + error_setg_errno(errp, errno, + "vfio: failed GROUP_SET_SPAPR_TCE for " + "KVM VFIO device %d and group fd %d", + param.tablefd, param.groupfd); + return -errno; + } + trace_vfio_spapr_group_attach(param.groupfd, param.tablefd); + } + } + } +#endif + return 0; +} + +void vfio_container_del_section_window(VFIOContainer *container, + MemoryRegionSection *section) +{ + if (container->iommu_type != VFIO_SPAPR_TCE_v2_IOMMU) { + return; + } + + vfio_spapr_remove_window(container, + section->offset_within_address_space); + if (vfio_host_win_del(container, + section->offset_within_address_space, + section->offset_within_address_space + + int128_get64(section->size) - 1) < 0) { + hw_error("%s: Cannot delete missing window at %"HWADDR_PRIx, + __func__, section->offset_within_address_space); + } +} From 770c3b6e431e5160b81f889ccb76c07dcfeb5da5 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Thu, 2 Nov 2023 15:12:24 +0800 Subject: [PATCH 459/974] vfio/container: Move spapr specific init/deinit into spapr.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move spapr specific init/deinit code into spapr.c and wrap them with vfio_spapr_container_init/deinit, this way footprint of spapr is further reduced, vfio_prereg_listener could also be made static. vfio_listener_release is unnecessary when prereg_listener is moved out, so have it removed. No functional changes intended. Suggested-by: Cédric Le Goater Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/container.c | 82 +++++------------------------------ hw/vfio/spapr.c | 81 +++++++++++++++++++++++++++++++++- include/hw/vfio/vfio-common.h | 4 +- 3 files changed, 95 insertions(+), 72 deletions(-) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 7a3f005d1b..204b244b11 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -264,14 +264,6 @@ int vfio_query_dirty_bitmap(VFIOContainer *container, VFIOBitmap *vbmap, return ret; } -static void vfio_listener_release(VFIOContainer *container) -{ - memory_listener_unregister(&container->listener); - if (container->iommu_type == VFIO_SPAPR_TCE_v2_IOMMU) { - memory_listener_unregister(&container->prereg_listener); - } -} - static struct vfio_info_cap_header * vfio_get_iommu_type1_info_cap(struct vfio_iommu_type1_info *info, uint16_t id) { @@ -612,69 +604,11 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, case VFIO_SPAPR_TCE_v2_IOMMU: case VFIO_SPAPR_TCE_IOMMU: { - struct vfio_iommu_spapr_tce_info info; - bool v2 = container->iommu_type == VFIO_SPAPR_TCE_v2_IOMMU; - - /* - * The host kernel code implementing VFIO_IOMMU_DISABLE is called - * when container fd is closed so we do not call it explicitly - * in this file. - */ - if (!v2) { - ret = ioctl(fd, VFIO_IOMMU_ENABLE); - if (ret) { - error_setg_errno(errp, errno, "failed to enable container"); - ret = -errno; - goto enable_discards_exit; - } - } else { - container->prereg_listener = vfio_prereg_listener; - - memory_listener_register(&container->prereg_listener, - &address_space_memory); - if (container->error) { - memory_listener_unregister(&container->prereg_listener); - ret = -1; - error_propagate_prepend(errp, container->error, - "RAM memory listener initialization failed: "); - goto enable_discards_exit; - } - } - - info.argsz = sizeof(info); - ret = ioctl(fd, VFIO_IOMMU_SPAPR_TCE_GET_INFO, &info); + ret = vfio_spapr_container_init(container, errp); if (ret) { - error_setg_errno(errp, errno, - "VFIO_IOMMU_SPAPR_TCE_GET_INFO failed"); - ret = -errno; - if (v2) { - memory_listener_unregister(&container->prereg_listener); - } goto enable_discards_exit; } - - if (v2) { - container->pgsizes = info.ddw.pgsizes; - /* - * There is a default window in just created container. - * To make region_add/del simpler, we better remove this - * window now and let those iommu_listener callbacks - * create/remove them when needed. - */ - ret = vfio_spapr_remove_window(container, info.dma32_window_start); - if (ret) { - error_setg_errno(errp, -ret, - "failed to remove existing window"); - goto enable_discards_exit; - } - } else { - /* The default table uses 4K pages */ - container->pgsizes = 0x1000; - vfio_host_win_add(container, info.dma32_window_start, - info.dma32_window_start + - info.dma32_window_size - 1, - 0x1000); - } + break; } } @@ -704,7 +638,11 @@ listener_release_exit: QLIST_REMOVE(group, container_next); QLIST_REMOVE(container, next); vfio_kvm_device_del_group(group); - vfio_listener_release(container); + memory_listener_unregister(&container->listener); + if (container->iommu_type == VFIO_SPAPR_TCE_v2_IOMMU || + container->iommu_type == VFIO_SPAPR_TCE_IOMMU) { + vfio_spapr_container_deinit(container); + } enable_discards_exit: vfio_ram_block_discard_disable(container, false); @@ -734,7 +672,11 @@ static void vfio_disconnect_container(VFIOGroup *group) * group. */ if (QLIST_EMPTY(&container->group_list)) { - vfio_listener_release(container); + memory_listener_unregister(&container->listener); + if (container->iommu_type == VFIO_SPAPR_TCE_v2_IOMMU || + container->iommu_type == VFIO_SPAPR_TCE_IOMMU) { + vfio_spapr_container_deinit(container); + } } if (ioctl(group->fd, VFIO_GROUP_UNSET_CONTAINER, &container->fd)) { diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c index 9a7517c042..00dbd7af11 100644 --- a/hw/vfio/spapr.c +++ b/hw/vfio/spapr.c @@ -15,6 +15,7 @@ #include #endif #include "sysemu/kvm.h" +#include "exec/address-spaces.h" #include "hw/vfio/vfio-common.h" #include "hw/hw.h" @@ -139,7 +140,7 @@ static void vfio_prereg_listener_region_del(MemoryListener *listener, trace_vfio_prereg_unregister(reg.vaddr, reg.size, ret ? -errno : 0); } -const MemoryListener vfio_prereg_listener = { +static const MemoryListener vfio_prereg_listener = { .name = "vfio-pre-reg", .region_add = vfio_prereg_listener_region_add, .region_del = vfio_prereg_listener_region_del, @@ -343,3 +344,81 @@ void vfio_container_del_section_window(VFIOContainer *container, __func__, section->offset_within_address_space); } } + +int vfio_spapr_container_init(VFIOContainer *container, Error **errp) +{ + struct vfio_iommu_spapr_tce_info info; + bool v2 = container->iommu_type == VFIO_SPAPR_TCE_v2_IOMMU; + int ret, fd = container->fd; + + /* + * The host kernel code implementing VFIO_IOMMU_DISABLE is called + * when container fd is closed so we do not call it explicitly + * in this file. + */ + if (!v2) { + ret = ioctl(fd, VFIO_IOMMU_ENABLE); + if (ret) { + error_setg_errno(errp, errno, "failed to enable container"); + return -errno; + } + } else { + container->prereg_listener = vfio_prereg_listener; + + memory_listener_register(&container->prereg_listener, + &address_space_memory); + if (container->error) { + ret = -1; + error_propagate_prepend(errp, container->error, + "RAM memory listener initialization failed: "); + goto listener_unregister_exit; + } + } + + info.argsz = sizeof(info); + ret = ioctl(fd, VFIO_IOMMU_SPAPR_TCE_GET_INFO, &info); + if (ret) { + error_setg_errno(errp, errno, + "VFIO_IOMMU_SPAPR_TCE_GET_INFO failed"); + ret = -errno; + goto listener_unregister_exit; + } + + if (v2) { + container->pgsizes = info.ddw.pgsizes; + /* + * There is a default window in just created container. + * To make region_add/del simpler, we better remove this + * window now and let those iommu_listener callbacks + * create/remove them when needed. + */ + ret = vfio_spapr_remove_window(container, info.dma32_window_start); + if (ret) { + error_setg_errno(errp, -ret, + "failed to remove existing window"); + goto listener_unregister_exit; + } + } else { + /* The default table uses 4K pages */ + container->pgsizes = 0x1000; + vfio_host_win_add(container, info.dma32_window_start, + info.dma32_window_start + + info.dma32_window_size - 1, + 0x1000); + } + + return 0; + +listener_unregister_exit: + if (v2) { + memory_listener_unregister(&container->prereg_listener); + } + return ret; +} + +void vfio_spapr_container_deinit(VFIOContainer *container) +{ + if (container->iommu_type == VFIO_SPAPR_TCE_v2_IOMMU) { + memory_listener_unregister(&container->prereg_listener); + } +} diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 0c3d390e8b..ed5a8e4754 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -225,11 +225,14 @@ int vfio_set_dirty_page_tracking(VFIOContainer *container, bool start); int vfio_query_dirty_bitmap(VFIOContainer *container, VFIOBitmap *vbmap, hwaddr iova, hwaddr size); +/* SPAPR specific */ int vfio_container_add_section_window(VFIOContainer *container, MemoryRegionSection *section, Error **errp); void vfio_container_del_section_window(VFIOContainer *container, MemoryRegionSection *section); +int vfio_spapr_container_init(VFIOContainer *container, Error **errp); +void vfio_spapr_container_deinit(VFIOContainer *container); void vfio_disable_irqindex(VFIODevice *vbasedev, int index); void vfio_unmask_single_irqindex(VFIODevice *vbasedev, int index); @@ -289,7 +292,6 @@ vfio_get_device_info_cap(struct vfio_device_info *info, uint16_t id); struct vfio_info_cap_header * vfio_get_cap(void *ptr, uint32_t cap_offset, uint16_t id); #endif -extern const MemoryListener vfio_prereg_listener; int vfio_spapr_create_window(VFIOContainer *container, MemoryRegionSection *section, From a17879f0e2e82c5e85440eb1c3e8a3eeef469a3e Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Thu, 2 Nov 2023 15:12:25 +0800 Subject: [PATCH 460/974] vfio/spapr: Make vfio_spapr_create/remove_window static MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vfio_spapr_create_window calls vfio_spapr_remove_window, With reoder of definition of the two, we can make vfio_spapr_create/remove_window static. No functional changes intended. Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/spapr.c | 48 +++++++++++++++++------------------ include/hw/vfio/vfio-common.h | 6 ----- 2 files changed, 24 insertions(+), 30 deletions(-) diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c index 00dbd7af11..4428990c28 100644 --- a/hw/vfio/spapr.c +++ b/hw/vfio/spapr.c @@ -146,9 +146,30 @@ static const MemoryListener vfio_prereg_listener = { .region_del = vfio_prereg_listener_region_del, }; -int vfio_spapr_create_window(VFIOContainer *container, - MemoryRegionSection *section, - hwaddr *pgsize) +static int vfio_spapr_remove_window(VFIOContainer *container, + hwaddr offset_within_address_space) +{ + struct vfio_iommu_spapr_tce_remove remove = { + .argsz = sizeof(remove), + .start_addr = offset_within_address_space, + }; + int ret; + + ret = ioctl(container->fd, VFIO_IOMMU_SPAPR_TCE_REMOVE, &remove); + if (ret) { + error_report("Failed to remove window at %"PRIx64, + (uint64_t)remove.start_addr); + return -errno; + } + + trace_vfio_spapr_remove_window(offset_within_address_space); + + return 0; +} + +static int vfio_spapr_create_window(VFIOContainer *container, + MemoryRegionSection *section, + hwaddr *pgsize) { int ret = 0; IOMMUMemoryRegion *iommu_mr = IOMMU_MEMORY_REGION(section->mr); @@ -238,27 +259,6 @@ int vfio_spapr_create_window(VFIOContainer *container, return 0; } -int vfio_spapr_remove_window(VFIOContainer *container, - hwaddr offset_within_address_space) -{ - struct vfio_iommu_spapr_tce_remove remove = { - .argsz = sizeof(remove), - .start_addr = offset_within_address_space, - }; - int ret; - - ret = ioctl(container->fd, VFIO_IOMMU_SPAPR_TCE_REMOVE, &remove); - if (ret) { - error_report("Failed to remove window at %"PRIx64, - (uint64_t)remove.start_addr); - return -errno; - } - - trace_vfio_spapr_remove_window(offset_within_address_space); - - return 0; -} - int vfio_container_add_section_window(VFIOContainer *container, MemoryRegionSection *section, Error **errp) diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index ed5a8e4754..87848982bd 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -293,12 +293,6 @@ struct vfio_info_cap_header * vfio_get_cap(void *ptr, uint32_t cap_offset, uint16_t id); #endif -int vfio_spapr_create_window(VFIOContainer *container, - MemoryRegionSection *section, - hwaddr *pgsize); -int vfio_spapr_remove_window(VFIOContainer *container, - hwaddr offset_within_address_space); - bool vfio_migration_realize(VFIODevice *vbasedev, Error **errp); void vfio_migration_exit(VFIODevice *vbasedev); From a2347c60a86a7c2a227ebab186a195d16e1e3901 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Thu, 2 Nov 2023 15:12:26 +0800 Subject: [PATCH 461/974] vfio/common: Move vfio_host_win_add/del into spapr.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only spapr supports a customed host window list, other vfio driver assume 64bit host window. So remove the check in listener callback and move vfio_host_win_add/del into spapr.c and make it static. With the check removed, we still need to do the same check for VFIO_SPAPR_TCE_IOMMU which allows a single host window range [dma32_window_start, dma32_window_size). Move vfio_find_hostwin into spapr.c and do same check in vfio_container_add_section_window instead. When mapping a ram device section, if it's unaligned with hostwin->iova_pgsizes, this mapping is bypassed. With hostwin moved into spapr, we changed to check container->pgsizes. Suggested-by: Alex Williamson Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 70 +---------------------------- hw/vfio/container.c | 16 ------- hw/vfio/spapr.c | 83 +++++++++++++++++++++++++++++++++++ include/hw/vfio/vfio-common.h | 5 --- 4 files changed, 85 insertions(+), 89 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index e72055e752..e70fdf5e0c 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -245,44 +245,6 @@ bool vfio_devices_all_running_and_mig_active(VFIOContainer *container) return true; } -void vfio_host_win_add(VFIOContainer *container, hwaddr min_iova, - hwaddr max_iova, uint64_t iova_pgsizes) -{ - VFIOHostDMAWindow *hostwin; - - QLIST_FOREACH(hostwin, &container->hostwin_list, hostwin_next) { - if (ranges_overlap(hostwin->min_iova, - hostwin->max_iova - hostwin->min_iova + 1, - min_iova, - max_iova - min_iova + 1)) { - hw_error("%s: Overlapped IOMMU are not enabled", __func__); - } - } - - hostwin = g_malloc0(sizeof(*hostwin)); - - hostwin->min_iova = min_iova; - hostwin->max_iova = max_iova; - hostwin->iova_pgsizes = iova_pgsizes; - QLIST_INSERT_HEAD(&container->hostwin_list, hostwin, hostwin_next); -} - -int vfio_host_win_del(VFIOContainer *container, - hwaddr min_iova, hwaddr max_iova) -{ - VFIOHostDMAWindow *hostwin; - - QLIST_FOREACH(hostwin, &container->hostwin_list, hostwin_next) { - if (hostwin->min_iova == min_iova && hostwin->max_iova == max_iova) { - QLIST_REMOVE(hostwin, hostwin_next); - g_free(hostwin); - return 0; - } - } - - return -1; -} - static bool vfio_listener_skipped_section(MemoryRegionSection *section) { return (!memory_region_is_ram(section->mr) && @@ -531,22 +493,6 @@ static void vfio_unregister_ram_discard_listener(VFIOContainer *container, g_free(vrdl); } -static VFIOHostDMAWindow *vfio_find_hostwin(VFIOContainer *container, - hwaddr iova, hwaddr end) -{ - VFIOHostDMAWindow *hostwin; - bool hostwin_found = false; - - QLIST_FOREACH(hostwin, &container->hostwin_list, hostwin_next) { - if (hostwin->min_iova <= iova && end <= hostwin->max_iova) { - hostwin_found = true; - break; - } - } - - return hostwin_found ? hostwin : NULL; -} - static bool vfio_known_safe_misalignment(MemoryRegionSection *section) { MemoryRegion *mr = section->mr; @@ -625,7 +571,6 @@ static void vfio_listener_region_add(MemoryListener *listener, Int128 llend, llsize; void *vaddr; int ret; - VFIOHostDMAWindow *hostwin; Error *err = NULL; if (!vfio_listener_valid_section(section, "region_add")) { @@ -647,13 +592,6 @@ static void vfio_listener_region_add(MemoryListener *listener, goto fail; } - hostwin = vfio_find_hostwin(container, iova, end); - if (!hostwin) { - error_setg(&err, "Container %p can't map guest IOVA region" - " 0x%"HWADDR_PRIx"..0x%"HWADDR_PRIx, container, iova, end); - goto fail; - } - memory_region_ref(section->mr); if (memory_region_is_iommu(section->mr)) { @@ -734,7 +672,7 @@ static void vfio_listener_region_add(MemoryListener *listener, llsize = int128_sub(llend, int128_make64(iova)); if (memory_region_is_ram_device(section->mr)) { - hwaddr pgmask = (1ULL << ctz64(hostwin->iova_pgsizes)) - 1; + hwaddr pgmask = (1ULL << ctz64(container->pgsizes)) - 1; if ((iova & pgmask) || (int128_get64(llsize) & pgmask)) { trace_vfio_listener_region_add_no_dma_map( @@ -833,12 +771,8 @@ static void vfio_listener_region_del(MemoryListener *listener, if (memory_region_is_ram_device(section->mr)) { hwaddr pgmask; - VFIOHostDMAWindow *hostwin; - hostwin = vfio_find_hostwin(container, iova, end); - assert(hostwin); /* or region_add() would have failed */ - - pgmask = (1ULL << ctz64(hostwin->iova_pgsizes)) - 1; + pgmask = (1ULL << ctz64(container->pgsizes)) - 1; try_unmap = !((iova & pgmask) || (int128_get64(llsize) & pgmask)); } else if (memory_region_has_ram_discard_manager(section->mr)) { vfio_unregister_ram_discard_listener(container, section); diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 204b244b11..242010036a 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -551,7 +551,6 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, container->dma_max_mappings = 0; container->iova_ranges = NULL; QLIST_INIT(&container->giommu_list); - QLIST_INIT(&container->hostwin_list); QLIST_INIT(&container->vrdl_list); ret = vfio_init_container(container, group->fd, errp); @@ -591,14 +590,6 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, vfio_get_iommu_info_migration(container, info); g_free(info); - - /* - * FIXME: We should parse VFIO_IOMMU_TYPE1_INFO_CAP_IOVA_RANGE - * information to get the actual window extent rather than assume - * a 64-bit IOVA address space. - */ - vfio_host_win_add(container, 0, (hwaddr)-1, container->pgsizes); - break; } case VFIO_SPAPR_TCE_v2_IOMMU: @@ -687,7 +678,6 @@ static void vfio_disconnect_container(VFIOGroup *group) if (QLIST_EMPTY(&container->group_list)) { VFIOAddressSpace *space = container->space; VFIOGuestIOMMU *giommu, *tmp; - VFIOHostDMAWindow *hostwin, *next; QLIST_REMOVE(container, next); @@ -698,12 +688,6 @@ static void vfio_disconnect_container(VFIOGroup *group) g_free(giommu); } - QLIST_FOREACH_SAFE(hostwin, &container->hostwin_list, hostwin_next, - next) { - QLIST_REMOVE(hostwin, hostwin_next); - g_free(hostwin); - } - trace_vfio_disconnect_container(container->fd); close(container->fd); vfio_free_container(container); diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c index 4428990c28..83da2f7ec2 100644 --- a/hw/vfio/spapr.c +++ b/hw/vfio/spapr.c @@ -146,6 +146,60 @@ static const MemoryListener vfio_prereg_listener = { .region_del = vfio_prereg_listener_region_del, }; +static void vfio_host_win_add(VFIOContainer *container, hwaddr min_iova, + hwaddr max_iova, uint64_t iova_pgsizes) +{ + VFIOHostDMAWindow *hostwin; + + QLIST_FOREACH(hostwin, &container->hostwin_list, hostwin_next) { + if (ranges_overlap(hostwin->min_iova, + hostwin->max_iova - hostwin->min_iova + 1, + min_iova, + max_iova - min_iova + 1)) { + hw_error("%s: Overlapped IOMMU are not enabled", __func__); + } + } + + hostwin = g_malloc0(sizeof(*hostwin)); + + hostwin->min_iova = min_iova; + hostwin->max_iova = max_iova; + hostwin->iova_pgsizes = iova_pgsizes; + QLIST_INSERT_HEAD(&container->hostwin_list, hostwin, hostwin_next); +} + +static int vfio_host_win_del(VFIOContainer *container, + hwaddr min_iova, hwaddr max_iova) +{ + VFIOHostDMAWindow *hostwin; + + QLIST_FOREACH(hostwin, &container->hostwin_list, hostwin_next) { + if (hostwin->min_iova == min_iova && hostwin->max_iova == max_iova) { + QLIST_REMOVE(hostwin, hostwin_next); + g_free(hostwin); + return 0; + } + } + + return -1; +} + +static VFIOHostDMAWindow *vfio_find_hostwin(VFIOContainer *container, + hwaddr iova, hwaddr end) +{ + VFIOHostDMAWindow *hostwin; + bool hostwin_found = false; + + QLIST_FOREACH(hostwin, &container->hostwin_list, hostwin_next) { + if (hostwin->min_iova <= iova && end <= hostwin->max_iova) { + hostwin_found = true; + break; + } + } + + return hostwin_found ? hostwin : NULL; +} + static int vfio_spapr_remove_window(VFIOContainer *container, hwaddr offset_within_address_space) { @@ -267,6 +321,26 @@ int vfio_container_add_section_window(VFIOContainer *container, hwaddr pgsize = 0; int ret; + /* + * VFIO_SPAPR_TCE_IOMMU supports a single host window between + * [dma32_window_start, dma32_window_size), we need to ensure + * the section fall in this range. + */ + if (container->iommu_type == VFIO_SPAPR_TCE_IOMMU) { + hwaddr iova, end; + + iova = section->offset_within_address_space; + end = iova + int128_get64(section->size) - 1; + + if (!vfio_find_hostwin(container, iova, end)) { + error_setg(errp, "Container %p can't map guest IOVA region" + " 0x%"HWADDR_PRIx"..0x%"HWADDR_PRIx, container, + iova, end); + return -EINVAL; + } + return 0; + } + if (container->iommu_type != VFIO_SPAPR_TCE_v2_IOMMU) { return 0; } @@ -351,6 +425,8 @@ int vfio_spapr_container_init(VFIOContainer *container, Error **errp) bool v2 = container->iommu_type == VFIO_SPAPR_TCE_v2_IOMMU; int ret, fd = container->fd; + QLIST_INIT(&container->hostwin_list); + /* * The host kernel code implementing VFIO_IOMMU_DISABLE is called * when container fd is closed so we do not call it explicitly @@ -418,7 +494,14 @@ listener_unregister_exit: void vfio_spapr_container_deinit(VFIOContainer *container) { + VFIOHostDMAWindow *hostwin, *next; + if (container->iommu_type == VFIO_SPAPR_TCE_v2_IOMMU) { memory_listener_unregister(&container->prereg_listener); } + QLIST_FOREACH_SAFE(hostwin, &container->hostwin_list, hostwin_next, + next) { + QLIST_REMOVE(hostwin, hostwin_next); + g_free(hostwin); + } } diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 87848982bd..a4a22accb9 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -207,11 +207,6 @@ typedef struct { hwaddr pages; } VFIOBitmap; -void vfio_host_win_add(VFIOContainer *container, - hwaddr min_iova, hwaddr max_iova, - uint64_t iova_pgsizes); -int vfio_host_win_del(VFIOContainer *container, hwaddr min_iova, - hwaddr max_iova); VFIOAddressSpace *vfio_get_address_space(AddressSpace *as); void vfio_put_address_space(VFIOAddressSpace *space); bool vfio_devices_all_running_and_saving(VFIOContainer *container); From 2d7f1081864790eb1000e6ef34e202dae66a03d2 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Mon, 6 Nov 2023 13:53:59 +0100 Subject: [PATCH 462/974] Revert "hw/virtio/virtio-pmem: Replace impossible check by assertion" This reverts commit 5960f254dbb46f0c7a9f5f44bf4d27c19c34cb97 since the previous commit made this situation possible again. Signed-off-by: Maciej S. Szmigiero --- hw/virtio/virtio-pmem.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/virtio/virtio-pmem.c b/hw/virtio/virtio-pmem.c index cc24812d2e..c3512c2dae 100644 --- a/hw/virtio/virtio-pmem.c +++ b/hw/virtio/virtio-pmem.c @@ -147,7 +147,10 @@ static void virtio_pmem_fill_device_info(const VirtIOPMEM *pmem, static MemoryRegion *virtio_pmem_get_memory_region(VirtIOPMEM *pmem, Error **errp) { - assert(pmem->memdev); + if (!pmem->memdev) { + error_setg(errp, "'%s' property must be set", VIRTIO_PMEM_MEMDEV_PROP); + return NULL; + } return &pmem->memdev->mr; } From eb1b7c4bd4135648a96449d4607063e80692fd0c Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Thu, 22 Jun 2023 12:49:19 +0200 Subject: [PATCH 463/974] memory-device: Drop size alignment check There is no strong requirement that the size has to be multiples of the requested alignment, let's drop it. This is a preparation for hv-baloon. Signed-off-by: David Hildenbrand Signed-off-by: Maciej S. Szmigiero --- hw/mem/memory-device.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/hw/mem/memory-device.c b/hw/mem/memory-device.c index db702ccad5..e0704b8dc3 100644 --- a/hw/mem/memory-device.c +++ b/hw/mem/memory-device.c @@ -236,12 +236,6 @@ static uint64_t memory_device_get_free_addr(MachineState *ms, return 0; } - if (!QEMU_IS_ALIGNED(size, align)) { - error_setg(errp, "backend memory size must be multiple of 0x%" - PRIx64, align); - return 0; - } - if (hint) { if (range_init(&new, *hint, size) || !range_contains_range(&as, &new)) { error_setg(errp, "can't add memory device [0x%" PRIx64 ":0x%" PRIx64 From 4f80cd2f033e73bb65adf69bd337ae3052629fdf Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Mon, 12 Jun 2023 16:00:53 +0200 Subject: [PATCH 464/974] Add Hyper-V Dynamic Memory Protocol definitions This commit adds Hyper-V Dynamic Memory Protocol definitions, taken from hv_balloon Linux kernel driver, adapted to the QEMU coding style and definitions. Acked-by: David Hildenbrand Signed-off-by: Maciej S. Szmigiero --- include/hw/hyperv/dynmem-proto.h | 423 +++++++++++++++++++++++++++++++ 1 file changed, 423 insertions(+) create mode 100644 include/hw/hyperv/dynmem-proto.h diff --git a/include/hw/hyperv/dynmem-proto.h b/include/hw/hyperv/dynmem-proto.h new file mode 100644 index 0000000000..d0f9090ac4 --- /dev/null +++ b/include/hw/hyperv/dynmem-proto.h @@ -0,0 +1,423 @@ +#ifndef HW_HYPERV_DYNMEM_PROTO_H +#define HW_HYPERV_DYNMEM_PROTO_H + +/* + * Hyper-V Dynamic Memory Protocol definitions + * + * Copyright (C) 2020-2023 Oracle and/or its affiliates. + * + * Based on drivers/hv/hv_balloon.c from Linux kernel: + * Copyright (c) 2012, Microsoft Corporation. + * + * Author: K. Y. Srinivasan + * + * This work is licensed under the terms of the GNU GPL, version 2. + * See the COPYING file in the top-level directory. + */ + +/* + * Protocol versions. The low word is the minor version, the high word the major + * version. + * + * History: + * Initial version 1.0 + * Changed to 0.1 on 2009/03/25 + * Changes to 0.2 on 2009/05/14 + * Changes to 0.3 on 2009/12/03 + * Changed to 1.0 on 2011/04/05 + * Changed to 2.0 on 2019/12/10 + */ + +#define DYNMEM_MAKE_VERSION(Major, Minor) ((uint32_t)(((Major) << 16) | (Minor))) +#define DYNMEM_MAJOR_VERSION(Version) ((uint32_t)(Version) >> 16) +#define DYNMEM_MINOR_VERSION(Version) ((uint32_t)(Version) & 0xff) + +enum { + DYNMEM_PROTOCOL_VERSION_1 = DYNMEM_MAKE_VERSION(0, 3), + DYNMEM_PROTOCOL_VERSION_2 = DYNMEM_MAKE_VERSION(1, 0), + DYNMEM_PROTOCOL_VERSION_3 = DYNMEM_MAKE_VERSION(2, 0), + + DYNMEM_PROTOCOL_VERSION_WIN7 = DYNMEM_PROTOCOL_VERSION_1, + DYNMEM_PROTOCOL_VERSION_WIN8 = DYNMEM_PROTOCOL_VERSION_2, + DYNMEM_PROTOCOL_VERSION_WIN10 = DYNMEM_PROTOCOL_VERSION_3, + + DYNMEM_PROTOCOL_VERSION_CURRENT = DYNMEM_PROTOCOL_VERSION_WIN10 +}; + + + +/* + * Message Types + */ + +enum dm_message_type { + /* + * Version 0.3 + */ + DM_ERROR = 0, + DM_VERSION_REQUEST = 1, + DM_VERSION_RESPONSE = 2, + DM_CAPABILITIES_REPORT = 3, + DM_CAPABILITIES_RESPONSE = 4, + DM_STATUS_REPORT = 5, + DM_BALLOON_REQUEST = 6, + DM_BALLOON_RESPONSE = 7, + DM_UNBALLOON_REQUEST = 8, + DM_UNBALLOON_RESPONSE = 9, + DM_MEM_HOT_ADD_REQUEST = 10, + DM_MEM_HOT_ADD_RESPONSE = 11, + DM_VERSION_03_MAX = 11, + /* + * Version 1.0. + */ + DM_INFO_MESSAGE = 12, + DM_VERSION_1_MAX = 12, + + /* + * Version 2.0 + */ + DM_MEM_HOT_REMOVE_REQUEST = 13, + DM_MEM_HOT_REMOVE_RESPONSE = 14 +}; + + +/* + * Structures defining the dynamic memory management + * protocol. + */ + +union dm_version { + struct { + uint16_t minor_version; + uint16_t major_version; + }; + uint32_t version; +} QEMU_PACKED; + + +union dm_caps { + struct { + uint64_t balloon:1; + uint64_t hot_add:1; + /* + * To support guests that may have alignment + * limitations on hot-add, the guest can specify + * its alignment requirements; a value of n + * represents an alignment of 2^n in mega bytes. + */ + uint64_t hot_add_alignment:4; + uint64_t hot_remove:1; + uint64_t reservedz:57; + } cap_bits; + uint64_t caps; +} QEMU_PACKED; + +union dm_mem_page_range { + struct { + /* + * The PFN number of the first page in the range. + * 40 bits is the architectural limit of a PFN + * number for AMD64. + */ + uint64_t start_page:40; + /* + * The number of pages in the range. + */ + uint64_t page_cnt:24; + } finfo; + uint64_t page_range; +} QEMU_PACKED; + + + +/* + * The header for all dynamic memory messages: + * + * type: Type of the message. + * size: Size of the message in bytes; including the header. + * trans_id: The guest is responsible for manufacturing this ID. + */ + +struct dm_header { + uint16_t type; + uint16_t size; + uint32_t trans_id; +} QEMU_PACKED; + +/* + * A generic message format for dynamic memory. + * Specific message formats are defined later in the file. + */ + +struct dm_message { + struct dm_header hdr; + uint8_t data[]; /* enclosed message */ +} QEMU_PACKED; + + +/* + * Specific message types supporting the dynamic memory protocol. + */ + +/* + * Version negotiation message. Sent from the guest to the host. + * The guest is free to try different versions until the host + * accepts the version. + * + * dm_version: The protocol version requested. + * is_last_attempt: If TRUE, this is the last version guest will request. + * reservedz: Reserved field, set to zero. + */ + +struct dm_version_request { + struct dm_header hdr; + union dm_version version; + uint32_t is_last_attempt:1; + uint32_t reservedz:31; +} QEMU_PACKED; + +/* + * Version response message; Host to Guest and indicates + * if the host has accepted the version sent by the guest. + * + * is_accepted: If TRUE, host has accepted the version and the guest + * should proceed to the next stage of the protocol. FALSE indicates that + * guest should re-try with a different version. + * + * reservedz: Reserved field, set to zero. + */ + +struct dm_version_response { + struct dm_header hdr; + uint64_t is_accepted:1; + uint64_t reservedz:63; +} QEMU_PACKED; + +/* + * Message reporting capabilities. This is sent from the guest to the + * host. + */ + +struct dm_capabilities { + struct dm_header hdr; + union dm_caps caps; + uint64_t min_page_cnt; + uint64_t max_page_number; +} QEMU_PACKED; + +/* + * Response to the capabilities message. This is sent from the host to the + * guest. This message notifies if the host has accepted the guest's + * capabilities. If the host has not accepted, the guest must shutdown + * the service. + * + * is_accepted: Indicates if the host has accepted guest's capabilities. + * reservedz: Must be 0. + */ + +struct dm_capabilities_resp_msg { + struct dm_header hdr; + uint64_t is_accepted:1; + uint64_t hot_remove:1; + uint64_t suppress_pressure_reports:1; + uint64_t reservedz:61; +} QEMU_PACKED; + +/* + * This message is used to report memory pressure from the guest. + * This message is not part of any transaction and there is no + * response to this message. + * + * num_avail: Available memory in pages. + * num_committed: Committed memory in pages. + * page_file_size: The accumulated size of all page files + * in the system in pages. + * zero_free: The nunber of zero and free pages. + * page_file_writes: The writes to the page file in pages. + * io_diff: An indicator of file cache efficiency or page file activity, + * calculated as File Cache Page Fault Count - Page Read Count. + * This value is in pages. + * + * Some of these metrics are Windows specific and fortunately + * the algorithm on the host side that computes the guest memory + * pressure only uses num_committed value. + */ + +struct dm_status { + struct dm_header hdr; + uint64_t num_avail; + uint64_t num_committed; + uint64_t page_file_size; + uint64_t zero_free; + uint32_t page_file_writes; + uint32_t io_diff; +} QEMU_PACKED; + + +/* + * Message to ask the guest to allocate memory - balloon up message. + * This message is sent from the host to the guest. The guest may not be + * able to allocate as much memory as requested. + * + * num_pages: number of pages to allocate. + */ + +struct dm_balloon { + struct dm_header hdr; + uint32_t num_pages; + uint32_t reservedz; +} QEMU_PACKED; + + +/* + * Balloon response message; this message is sent from the guest + * to the host in response to the balloon message. + * + * reservedz: Reserved; must be set to zero. + * more_pages: If FALSE, this is the last message of the transaction. + * if TRUE there will atleast one more message from the guest. + * + * range_count: The number of ranges in the range array. + * + * range_array: An array of page ranges returned to the host. + * + */ + +struct dm_balloon_response { + struct dm_header hdr; + uint32_t reservedz; + uint32_t more_pages:1; + uint32_t range_count:31; + union dm_mem_page_range range_array[]; +} QEMU_PACKED; + +/* + * Un-balloon message; this message is sent from the host + * to the guest to give guest more memory. + * + * more_pages: If FALSE, this is the last message of the transaction. + * if TRUE there will atleast one more message from the guest. + * + * reservedz: Reserved; must be set to zero. + * + * range_count: The number of ranges in the range array. + * + * range_array: An array of page ranges returned to the host. + * + */ + +struct dm_unballoon_request { + struct dm_header hdr; + uint32_t more_pages:1; + uint32_t reservedz:31; + uint32_t range_count; + union dm_mem_page_range range_array[]; +} QEMU_PACKED; + +/* + * Un-balloon response message; this message is sent from the guest + * to the host in response to an unballoon request. + * + */ + +struct dm_unballoon_response { + struct dm_header hdr; +} QEMU_PACKED; + + +/* + * Hot add request message. Message sent from the host to the guest. + * + * mem_range: Memory range to hot add. + * + */ + +struct dm_hot_add { + struct dm_header hdr; + union dm_mem_page_range range; +} QEMU_PACKED; + +/* + * Hot add response message. + * This message is sent by the guest to report the status of a hot add request. + * If page_count is less than the requested page count, then the host should + * assume all further hot add requests will fail, since this indicates that + * the guest has hit an upper physical memory barrier. + * + * Hot adds may also fail due to low resources; in this case, the guest must + * not complete this message until the hot add can succeed, and the host must + * not send a new hot add request until the response is sent. + * If VSC fails to hot add memory DYNMEM_NUMBER_OF_UNSUCCESSFUL_HOTADD_ATTEMPTS + * times it fails the request. + * + * + * page_count: number of pages that were successfully hot added. + * + * result: result of the operation 1: success, 0: failure. + * + */ + +struct dm_hot_add_response { + struct dm_header hdr; + uint32_t page_count; + uint32_t result; +} QEMU_PACKED; + +struct dm_hot_remove { + struct dm_header hdr; + uint32_t virtual_node; + uint32_t page_count; + uint32_t qos_flags; + uint32_t reservedZ; +} QEMU_PACKED; + +struct dm_hot_remove_response { + struct dm_header hdr; + uint32_t result; + uint32_t range_count; + uint64_t more_pages:1; + uint64_t reservedz:63; + union dm_mem_page_range range_array[]; +} QEMU_PACKED; + +#define DM_REMOVE_QOS_LARGE (1 << 0) +#define DM_REMOVE_QOS_LOCAL (1 << 1) +#define DM_REMOVE_QOS_MASK (0x3) + +/* + * Types of information sent from host to the guest. + */ + +enum dm_info_type { + INFO_TYPE_MAX_PAGE_CNT = 0, + MAX_INFO_TYPE +}; + + +/* + * Header for the information message. + */ + +struct dm_info_header { + enum dm_info_type type; + uint32_t data_size; + uint8_t data[]; +} QEMU_PACKED; + +/* + * This message is sent from the host to the guest to pass + * some relevant information (win8 addition). + * + * reserved: no used. + * info_size: size of the information blob. + * info: information blob. + */ + +struct dm_info_msg { + struct dm_header hdr; + uint32_t reserved; + uint32_t info_size; + uint8_t info[]; +}; + +#endif From 0d9e8c0b670b7856d36ed155d43548d2491230e7 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Mon, 12 Jun 2023 16:00:54 +0200 Subject: [PATCH 465/974] Add Hyper-V Dynamic Memory Protocol driver (hv-balloon) base This driver is like virtio-balloon on steroids: it allows both changing the guest memory allocation via ballooning and (in the next patch) inserting pieces of extra RAM into it on demand from a provided memory backend. The actual resizing is done via ballooning interface (for example, via the "balloon" HMP command). This includes resizing the guest past its boot size - that is, hot-adding additional memory in granularity limited only by the guest alignment requirements, as provided by the next patch. In contrast with ACPI DIMM hotplug where one can only request to unplug a whole DIMM stick this driver allows removing memory from guest in single page (4k) units via ballooning. After a VM reboot the guest is back to its original (boot) size. In the future, the guest boot memory size might be changed on reboot instead, taking into account the effective size that VM had before that reboot (much like Hyper-V does). For performance reasons, the guest-released memory is tracked in a few range trees, as a series of (start, count) ranges. Each time a new page range is inserted into such tree its neighbors are checked as candidates for possible merging with it. Besides performance reasons, the Dynamic Memory protocol itself uses page ranges as the data structure in its messages, so relevant pages need to be merged into such ranges anyway. One has to be careful when tracking the guest-released pages, since the guest can maliciously report returning pages outside its current address space, which later clash with the address range of newly added memory. Similarly, the guest can report freeing the same page twice. The above design results in much better ballooning performance than when using virtio-balloon with the same guest: 230 GB / minute with this driver versus 70 GB / minute with virtio-balloon. During a ballooning operation most of time is spent waiting for the guest to come up with newly freed page ranges, processing the received ranges on the host side (in QEMU and KVM) is nearly instantaneous. The unballoon operation is also pretty much instantaneous: thanks to the merging of the ballooned out page ranges 200 GB of memory can be returned to the guest in about 1 second. With virtio-balloon this operation takes about 2.5 minutes. These tests were done against a Windows Server 2019 guest running on a Xeon E5-2699, after dirtying the whole memory inside guest before each balloon operation. Using a range tree instead of a bitmap to track the removed memory also means that the solution scales well with the guest size: even a 1 TB range takes just a few bytes of such metadata. Since the required GTree operations aren't present in every Glib version a check for them was added to the meson build script, together with new "--enable-hv-balloon" and "--disable-hv-balloon" configure arguments. If these GTree operations are missing in the system's Glib version this driver will be skipped during QEMU build. An optional "status-report=on" device parameter requests memory status events from the guest (typically sent every second), which allow the host to learn both the guest memory available and the guest memory in use counts. Following commits will add support for their external emission as "HV_BALLOON_STATUS_REPORT" QMP events. The driver is named hv-balloon since the Linux kernel client driver for the Dynamic Memory Protocol is named as such and to follow the naming pattern established by the virtio-balloon driver. The whole protocol runs over Hyper-V VMBus. The driver was tested against Windows Server 2012 R2, Windows Server 2016 and Windows Server 2019 guests and obeys the guest alignment requirements reported to the host via DM_CAPABILITIES_REPORT message. Acked-by: David Hildenbrand Signed-off-by: Maciej S. Szmigiero --- Kconfig.host | 3 + hw/hyperv/Kconfig | 10 + hw/hyperv/hv-balloon-internal.h | 33 + hw/hyperv/hv-balloon-page_range_tree.c | 228 +++++ hw/hyperv/hv-balloon-page_range_tree.h | 118 +++ hw/hyperv/hv-balloon.c | 1160 ++++++++++++++++++++++++ hw/hyperv/meson.build | 1 + hw/hyperv/trace-events | 13 + include/hw/hyperv/hv-balloon.h | 18 + meson.build | 28 +- meson_options.txt | 2 + scripts/meson-buildoptions.sh | 3 + 12 files changed, 1616 insertions(+), 1 deletion(-) create mode 100644 hw/hyperv/hv-balloon-internal.h create mode 100644 hw/hyperv/hv-balloon-page_range_tree.c create mode 100644 hw/hyperv/hv-balloon-page_range_tree.h create mode 100644 hw/hyperv/hv-balloon.c create mode 100644 include/hw/hyperv/hv-balloon.h diff --git a/Kconfig.host b/Kconfig.host index d763d89269..2ee71578f3 100644 --- a/Kconfig.host +++ b/Kconfig.host @@ -46,3 +46,6 @@ config FUZZ config VFIO_USER_SERVER_ALLOWED bool imply VFIO_USER_SERVER + +config HV_BALLOON_POSSIBLE + bool diff --git a/hw/hyperv/Kconfig b/hw/hyperv/Kconfig index fcf65903bd..41dd827c84 100644 --- a/hw/hyperv/Kconfig +++ b/hw/hyperv/Kconfig @@ -16,3 +16,13 @@ config SYNDBG bool default y depends on VMBUS + +config HV_BALLOON_SUPPORTED + bool + +config HV_BALLOON + bool + default y + depends on VMBUS + depends on HV_BALLOON_POSSIBLE + depends on HV_BALLOON_SUPPORTED diff --git a/hw/hyperv/hv-balloon-internal.h b/hw/hyperv/hv-balloon-internal.h new file mode 100644 index 0000000000..164c2e5825 --- /dev/null +++ b/hw/hyperv/hv-balloon-internal.h @@ -0,0 +1,33 @@ +/* + * QEMU Hyper-V Dynamic Memory Protocol driver + * + * Copyright (C) 2020-2023 Oracle and/or its affiliates. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef HW_HYPERV_HV_BALLOON_INTERNAL_H +#define HW_HYPERV_HV_BALLOON_INTERNAL_H + +#include "qemu/osdep.h" + +#define HV_BALLOON_PFN_SHIFT 12 +#define HV_BALLOON_PAGE_SIZE (1 << HV_BALLOON_PFN_SHIFT) + +#define SUM_OVERFLOW_U64(in1, in2) ((in1) > UINT64_MAX - (in2)) +#define SUM_SATURATE_U64(in1, in2) \ + ({ \ + uint64_t _in1 = (in1), _in2 = (in2); \ + uint64_t _result; \ + \ + if (!SUM_OVERFLOW_U64(_in1, _in2)) { \ + _result = _in1 + _in2; \ + } else { \ + _result = UINT64_MAX; \ + } \ + \ + _result; \ + }) + +#endif diff --git a/hw/hyperv/hv-balloon-page_range_tree.c b/hw/hyperv/hv-balloon-page_range_tree.c new file mode 100644 index 0000000000..e178d8b413 --- /dev/null +++ b/hw/hyperv/hv-balloon-page_range_tree.c @@ -0,0 +1,228 @@ +/* + * QEMU Hyper-V Dynamic Memory Protocol driver + * + * Copyright (C) 2020-2023 Oracle and/or its affiliates. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "hv-balloon-internal.h" +#include "hv-balloon-page_range_tree.h" + +/* + * temporarily avoid warnings about enhanced GTree API usage requiring a + * too recent Glib version until GLIB_VERSION_MAX_ALLOWED finally reaches + * the Glib version with this API + */ +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + +/* PageRangeTree */ +static gint page_range_tree_key_compare(gconstpointer leftp, + gconstpointer rightp, + gpointer user_data) +{ + const uint64_t *left = leftp, *right = rightp; + + if (*left < *right) { + return -1; + } else if (*left > *right) { + return 1; + } else { /* *left == *right */ + return 0; + } +} + +static GTreeNode *page_range_tree_insert_new(PageRangeTree tree, + uint64_t start, uint64_t count) +{ + uint64_t *key = g_malloc(sizeof(*key)); + PageRange *range = g_malloc(sizeof(*range)); + + assert(count > 0); + + *key = range->start = start; + range->count = count; + + return g_tree_insert_node(tree.t, key, range); +} + +void hvb_page_range_tree_insert(PageRangeTree tree, + uint64_t start, uint64_t count, + uint64_t *dupcount) +{ + GTreeNode *node; + bool joinable; + uint64_t intersection; + PageRange *range; + + assert(!SUM_OVERFLOW_U64(start, count)); + if (count == 0) { + return; + } + + node = g_tree_upper_bound(tree.t, &start); + if (node) { + node = g_tree_node_previous(node); + } else { + node = g_tree_node_last(tree.t); + } + + if (node) { + range = g_tree_node_value(node); + assert(range); + intersection = page_range_intersection_size(range, start, count); + joinable = page_range_joinable_right(range, start, count); + } + + if (!node || + (!intersection && !joinable)) { + /* + * !node case: the tree is empty or the very first node in the tree + * already has a higher key (the start of its range). + * the other case: there is a gap in the tree between the new range + * and the previous one. + * anyway, let's just insert the new range into the tree. + */ + node = page_range_tree_insert_new(tree, start, count); + assert(node); + range = g_tree_node_value(node); + assert(range); + } else { + /* + * the previous range in the tree either partially covers the new + * range or ends just at its beginning - extend it + */ + if (dupcount) { + *dupcount += intersection; + } + + count += start - range->start; + range->count = MAX(range->count, count); + } + + /* check next nodes for possible merging */ + for (node = g_tree_node_next(node); node; ) { + PageRange *rangecur; + + rangecur = g_tree_node_value(node); + assert(rangecur); + + intersection = page_range_intersection_size(rangecur, + range->start, range->count); + joinable = page_range_joinable_left(rangecur, + range->start, range->count); + if (!intersection && !joinable) { + /* the current node is disjoint */ + break; + } + + if (dupcount) { + *dupcount += intersection; + } + + count = rangecur->count + (rangecur->start - range->start); + range->count = MAX(range->count, count); + + /* the current node was merged in, remove it */ + start = rangecur->start; + node = g_tree_node_next(node); + /* no hinted removal in GTree... */ + g_tree_remove(tree.t, &start); + } +} + +bool hvb_page_range_tree_pop(PageRangeTree tree, PageRange *out, + uint64_t maxcount) +{ + GTreeNode *node; + PageRange *range; + + node = g_tree_node_last(tree.t); + if (!node) { + return false; + } + + range = g_tree_node_value(node); + assert(range); + + out->start = range->start; + + /* can't modify range->start as it is the node key */ + if (range->count > maxcount) { + out->start += range->count - maxcount; + out->count = maxcount; + range->count -= maxcount; + } else { + out->count = range->count; + /* no hinted removal in GTree... */ + g_tree_remove(tree.t, &out->start); + } + + return true; +} + +bool hvb_page_range_tree_intree_any(PageRangeTree tree, + uint64_t start, uint64_t count) +{ + GTreeNode *node; + + if (count == 0) { + return false; + } + + /* find the first node that can possibly intersect our range */ + node = g_tree_upper_bound(tree.t, &start); + if (node) { + /* + * a NULL node below means that the very first node in the tree + * already has a higher key (the start of its range). + */ + node = g_tree_node_previous(node); + } else { + /* a NULL node below means that the tree is empty */ + node = g_tree_node_last(tree.t); + } + /* node range start <= range start */ + + if (!node) { + /* node range start > range start */ + node = g_tree_node_first(tree.t); + } + + for ( ; node; node = g_tree_node_next(node)) { + PageRange *range = g_tree_node_value(node); + + assert(range); + /* + * if this node starts beyond or at the end of our range so does + * every next one + */ + if (range->start >= start + count) { + break; + } + + if (page_range_intersection_size(range, start, count) > 0) { + return true; + } + } + + return false; +} + +void hvb_page_range_tree_init(PageRangeTree *tree) +{ + tree->t = g_tree_new_full(page_range_tree_key_compare, NULL, + g_free, g_free); +} + +void hvb_page_range_tree_destroy(PageRangeTree *tree) +{ + /* g_tree_destroy() is not NULL-safe */ + if (!tree->t) { + return; + } + + g_tree_destroy(tree->t); + tree->t = NULL; +} diff --git a/hw/hyperv/hv-balloon-page_range_tree.h b/hw/hyperv/hv-balloon-page_range_tree.h new file mode 100644 index 0000000000..07a9ae0da6 --- /dev/null +++ b/hw/hyperv/hv-balloon-page_range_tree.h @@ -0,0 +1,118 @@ +/* + * QEMU Hyper-V Dynamic Memory Protocol driver + * + * Copyright (C) 2020-2023 Oracle and/or its affiliates. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef HW_HYPERV_HV_BALLOON_PAGE_RANGE_TREE_H +#define HW_HYPERV_HV_BALLOON_PAGE_RANGE_TREE_H + +#include "qemu/osdep.h" + +/* PageRange */ +typedef struct PageRange { + uint64_t start; + uint64_t count; +} PageRange; + +/* return just the part of range before (start) */ +static inline void page_range_part_before(const PageRange *range, + uint64_t start, PageRange *out) +{ + uint64_t endr = range->start + range->count; + uint64_t end = MIN(endr, start); + + out->start = range->start; + if (end > out->start) { + out->count = end - out->start; + } else { + out->count = 0; + } +} + +/* return just the part of range after (start, count) */ +static inline void page_range_part_after(const PageRange *range, + uint64_t start, uint64_t count, + PageRange *out) +{ + uint64_t end = range->start + range->count; + uint64_t ends = start + count; + + out->start = MAX(range->start, ends); + if (end > out->start) { + out->count = end - out->start; + } else { + out->count = 0; + } +} + +static inline void page_range_intersect(const PageRange *range, + uint64_t start, uint64_t count, + PageRange *out) +{ + uint64_t end1 = range->start + range->count; + uint64_t end2 = start + count; + uint64_t end = MIN(end1, end2); + + out->start = MAX(range->start, start); + out->count = out->start < end ? end - out->start : 0; +} + +static inline uint64_t page_range_intersection_size(const PageRange *range, + uint64_t start, uint64_t count) +{ + PageRange trange; + + page_range_intersect(range, start, count, &trange); + return trange.count; +} + +static inline bool page_range_joinable_left(const PageRange *range, + uint64_t start, uint64_t count) +{ + return start + count == range->start; +} + +static inline bool page_range_joinable_right(const PageRange *range, + uint64_t start, uint64_t count) +{ + return range->start + range->count == start; +} + +static inline bool page_range_joinable(const PageRange *range, + uint64_t start, uint64_t count) +{ + return page_range_joinable_left(range, start, count) || + page_range_joinable_right(range, start, count); +} + +/* PageRangeTree */ +/* type safety */ +typedef struct PageRangeTree { + GTree *t; +} PageRangeTree; + +static inline bool page_range_tree_is_empty(PageRangeTree tree) +{ + guint nnodes = g_tree_nnodes(tree.t); + + return nnodes == 0; +} + +void hvb_page_range_tree_init(PageRangeTree *tree); +void hvb_page_range_tree_destroy(PageRangeTree *tree); + +bool hvb_page_range_tree_intree_any(PageRangeTree tree, + uint64_t start, uint64_t count); + +bool hvb_page_range_tree_pop(PageRangeTree tree, PageRange *out, + uint64_t maxcount); + +void hvb_page_range_tree_insert(PageRangeTree tree, + uint64_t start, uint64_t count, + uint64_t *dupcount); + +#endif diff --git a/hw/hyperv/hv-balloon.c b/hw/hyperv/hv-balloon.c new file mode 100644 index 0000000000..5f3674ba06 --- /dev/null +++ b/hw/hyperv/hv-balloon.c @@ -0,0 +1,1160 @@ +/* + * QEMU Hyper-V Dynamic Memory Protocol driver + * + * Copyright (C) 2020-2023 Oracle and/or its affiliates. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "hv-balloon-internal.h" + +#include "exec/address-spaces.h" +#include "exec/cpu-common.h" +#include "exec/ramblock.h" +#include "hw/boards.h" +#include "hw/hyperv/dynmem-proto.h" +#include "hw/hyperv/hv-balloon.h" +#include "hw/hyperv/vmbus.h" +#include "hw/mem/memory-device.h" +#include "hw/mem/pc-dimm.h" +#include "hw/qdev-core.h" +#include "hw/qdev-properties.h" +#include "monitor/qdev.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-machine.h" +#include "qapi/qapi-events-machine.h" +#include "qapi/qapi-types-machine.h" +#include "qapi/qmp/qdict.h" +#include "qapi/visitor.h" +#include "qemu/error-report.h" +#include "qemu/module.h" +#include "qemu/units.h" +#include "qemu/timer.h" +#include "sysemu/balloon.h" +#include "sysemu/hostmem.h" +#include "sysemu/reset.h" +#include "hv-balloon-page_range_tree.h" +#include "trace.h" + +#define HV_BALLOON_GUID "525074DC-8985-46e2-8057-A307DC18A502" + +/* + * Some Windows versions (at least Server 2019) will crash with various + * error codes when receiving DM protocol requests (at least + * DM_MEM_HOT_ADD_REQUEST) immediately after boot. + * + * It looks like Hyper-V from Server 2016 uses a 50-second after-boot + * delay, probably to workaround this issue, so we'll use this value, too. + */ +#define HV_BALLOON_POST_INIT_WAIT (50 * 1000) + +#define HV_BALLOON_HA_CHUNK_SIZE (2 * GiB) +#define HV_BALLOON_HA_CHUNK_PAGES (HV_BALLOON_HA_CHUNK_SIZE / HV_BALLOON_PAGE_SIZE) + +#define HV_BALLOON_HR_CHUNK_PAGES 585728 +/* + * ^ that's the maximum number of pages + * that Windows returns in one hot remove response + * + * If the number requested is too high Windows will no longer honor + * these requests + */ + +struct HvBalloonClass { + VMBusDeviceClass parent_class; +} HvBalloonClass; + +typedef enum State { + /* not a real state */ + S_NO_CHANGE = 0, + + S_WAIT_RESET, + S_POST_RESET_CLOSED, + + /* init flow */ + S_VERSION, + S_CAPS, + S_POST_INIT_WAIT, + + S_IDLE, + + /* balloon op flow */ + S_BALLOON_POSTING, + S_BALLOON_RB_WAIT, + S_BALLOON_REPLY_WAIT, + + /* unballoon + hot add ops flow */ + S_UNBALLOON_POSTING, + S_UNBALLOON_RB_WAIT, + S_UNBALLOON_REPLY_WAIT, +} State; + +typedef struct StateDesc { + State state; + const char *desc; +} StateDesc; + +typedef struct HvBalloon { + VMBusDevice parent; + State state; + + union dm_version version; + union dm_caps caps; + + QEMUTimer post_init_timer; + + unsigned int trans_id; + + struct { + bool enabled; + bool received; + uint64_t committed; + uint64_t available; + } status_report; + + /* Guest target size */ + uint64_t target; + bool target_changed; + + /* Current (un)balloon */ + union { + uint64_t balloon_diff; + + struct { + uint64_t unballoon_diff; + }; + }; + + /* Nominal size of each memslot (the last one might be smaller) */ + uint64_t memslot_size; + + PageRangeTree removed_guest, removed_both; + + uint64_t removed_guest_ctr, removed_both_ctr; +} HvBalloon; + +OBJECT_DEFINE_TYPE_WITH_INTERFACES(HvBalloon, hv_balloon, HV_BALLOON, VMBUS_DEVICE, \ + { }) + +#define HV_BALLOON_SET_STATE(hvb, news) \ + do { \ + assert(news != S_NO_CHANGE); \ + hv_balloon_state_set(hvb, news, # news); \ + } while (0) + +#define HV_BALLOON_STATE_DESC_SET(stdesc, news) \ + _hv_balloon_state_desc_set(stdesc, news, # news) + +#define HV_BALLOON_STATE_DESC_INIT \ + { \ + .state = S_NO_CHANGE, \ + } + +typedef struct HvBalloonReq { + VMBusChanReq vmreq; +} HvBalloonReq; + +/* TODO: unify the code below with virtio-balloon and cache the value */ +static int build_dimm_list(Object *obj, void *opaque) +{ + GSList **list = opaque; + + if (object_dynamic_cast(obj, TYPE_PC_DIMM)) { + DeviceState *dev = DEVICE(obj); + if (dev->realized) { /* only realized DIMMs matter */ + *list = g_slist_prepend(*list, dev); + } + } + + object_child_foreach(obj, build_dimm_list, opaque); + return 0; +} + +static ram_addr_t get_current_ram_size(void) +{ + GSList *list = NULL, *item; + ram_addr_t size = current_machine->ram_size; + + build_dimm_list(qdev_get_machine(), &list); + for (item = list; item; item = g_slist_next(item)) { + Object *obj = OBJECT(item->data); + if (!strcmp(object_get_typename(obj), TYPE_PC_DIMM)) + size += object_property_get_int(obj, PC_DIMM_SIZE_PROP, + &error_abort); + } + g_slist_free(list); + + return size; +} + +/* total RAM includes memory currently removed from the guest */ +static uint64_t hv_balloon_total_ram(HvBalloon *balloon) +{ + ram_addr_t ram_size = get_current_ram_size(); + uint64_t ram_size_pages = ram_size >> HV_BALLOON_PFN_SHIFT; + + assert(ram_size_pages > 0); + + return ram_size_pages; +} + +/* + * calculating the total RAM size is a slow operation, + * avoid it as much as possible + */ +static uint64_t hv_balloon_total_removed_rs(HvBalloon *balloon, + uint64_t ram_size_pages) +{ + uint64_t total_removed; + + total_removed = SUM_SATURATE_U64(balloon->removed_guest_ctr, + balloon->removed_both_ctr); + + /* possible if guest returns pages outside actual RAM */ + if (total_removed > ram_size_pages) { + total_removed = ram_size_pages; + } + + return total_removed; +} + +/* Returns whether the state has actually changed */ +static bool hv_balloon_state_set(HvBalloon *balloon, + State newst, const char *newststr) +{ + if (newst == S_NO_CHANGE || balloon->state == newst) { + return false; + } + + balloon->state = newst; + trace_hv_balloon_state_change(newststr); + return true; +} + +static void _hv_balloon_state_desc_set(StateDesc *stdesc, + State newst, const char *newststr) +{ + /* state setting is only permitted on a freshly init desc */ + assert(stdesc->state == S_NO_CHANGE); + + assert(newst != S_NO_CHANGE); + + stdesc->state = newst; + stdesc->desc = newststr; +} + +static VMBusChannel *hv_balloon_get_channel_maybe(HvBalloon *balloon) +{ + return vmbus_device_channel(&balloon->parent, 0); +} + +static VMBusChannel *hv_balloon_get_channel(HvBalloon *balloon) +{ + VMBusChannel *chan; + + chan = hv_balloon_get_channel_maybe(balloon); + assert(chan != NULL); + return chan; +} + +static ssize_t hv_balloon_send_packet(VMBusChannel *chan, + struct dm_message *msg) +{ + int ret; + + ret = vmbus_channel_reserve(chan, 0, msg->hdr.size); + if (ret < 0) { + return ret; + } + + return vmbus_channel_send(chan, VMBUS_PACKET_DATA_INBAND, + NULL, 0, msg, msg->hdr.size, false, + msg->hdr.trans_id); +} + +static bool hv_balloon_unballoon_get_source(HvBalloon *balloon, + PageRangeTree *dtree, + uint64_t **dctr) +{ + if (g_tree_nnodes(balloon->removed_guest.t) > 0) { + *dtree = balloon->removed_guest; + *dctr = &balloon->removed_guest_ctr; + } else if (g_tree_nnodes(balloon->removed_both.t) > 0) { + *dtree = balloon->removed_both; + *dctr = &balloon->removed_both_ctr; + } else { + return false; + } + + return true; +} + +static void hv_balloon_unballoon_rb_wait(HvBalloon *balloon, StateDesc *stdesc) +{ + VMBusChannel *chan = hv_balloon_get_channel(balloon); + struct dm_unballoon_request *ur; + size_t ur_size = sizeof(*ur) + sizeof(ur->range_array[0]); + + assert(balloon->state == S_UNBALLOON_RB_WAIT); + + if (vmbus_channel_reserve(chan, 0, ur_size) < 0) { + return; + } + + HV_BALLOON_STATE_DESC_SET(stdesc, S_UNBALLOON_POSTING); +} + +static void hv_balloon_unballoon_posting(HvBalloon *balloon, StateDesc *stdesc) +{ + VMBusChannel *chan = hv_balloon_get_channel(balloon); + PageRangeTree dtree; + uint64_t *dctr; + struct dm_unballoon_request *ur; + size_t ur_size = sizeof(*ur) + sizeof(ur->range_array[0]); + PageRange range; + bool bret; + ssize_t ret; + + assert(balloon->state == S_UNBALLOON_POSTING); + assert(balloon->unballoon_diff > 0); + + if (!hv_balloon_unballoon_get_source(balloon, &dtree, &dctr)) { + error_report("trying to unballoon but nothing seems to be ballooned"); + /* + * there is little we can do as we might have already + * sent the guest a partial request we can't cancel + */ + return; + } + + assert(dtree.t); + assert(dctr); + + ur = alloca(ur_size); + memset(ur, 0, ur_size); + ur->hdr.type = DM_UNBALLOON_REQUEST; + ur->hdr.size = ur_size; + ur->hdr.trans_id = balloon->trans_id; + + bret = hvb_page_range_tree_pop(dtree, &range, MIN(balloon->unballoon_diff, + HV_BALLOON_HA_CHUNK_PAGES)); + assert(bret); + /* TODO: madvise? */ + + *dctr -= range.count; + balloon->unballoon_diff -= range.count; + + ur->range_count = 1; + ur->range_array[0].finfo.start_page = range.start; + ur->range_array[0].finfo.page_cnt = range.count; + ur->more_pages = balloon->unballoon_diff > 0; + + trace_hv_balloon_outgoing_unballoon(ur->hdr.trans_id, + range.count, range.start, + balloon->unballoon_diff); + + if (ur->more_pages) { + HV_BALLOON_STATE_DESC_SET(stdesc, S_UNBALLOON_RB_WAIT); + } else { + HV_BALLOON_STATE_DESC_SET(stdesc, S_UNBALLOON_REPLY_WAIT); + } + + ret = vmbus_channel_send(chan, VMBUS_PACKET_DATA_INBAND, + NULL, 0, ur, ur_size, false, + ur->hdr.trans_id); + if (ret <= 0) { + error_report("error %zd when posting unballoon msg, expect problems", + ret); + } +} + +static void hv_balloon_balloon_rb_wait(HvBalloon *balloon, StateDesc *stdesc) +{ + VMBusChannel *chan = hv_balloon_get_channel(balloon); + size_t bl_size = sizeof(struct dm_balloon); + + assert(balloon->state == S_BALLOON_RB_WAIT); + + if (vmbus_channel_reserve(chan, 0, bl_size) < 0) { + return; + } + + HV_BALLOON_STATE_DESC_SET(stdesc, S_BALLOON_POSTING); +} + +static void hv_balloon_balloon_posting(HvBalloon *balloon, StateDesc *stdesc) +{ + VMBusChannel *chan = hv_balloon_get_channel(balloon); + struct dm_balloon bl; + size_t bl_size = sizeof(bl); + ssize_t ret; + + assert(balloon->state == S_BALLOON_POSTING); + assert(balloon->balloon_diff > 0); + + memset(&bl, 0, sizeof(bl)); + bl.hdr.type = DM_BALLOON_REQUEST; + bl.hdr.size = bl_size; + bl.hdr.trans_id = balloon->trans_id; + bl.num_pages = MIN(balloon->balloon_diff, HV_BALLOON_HR_CHUNK_PAGES); + + trace_hv_balloon_outgoing_balloon(bl.hdr.trans_id, bl.num_pages, + balloon->balloon_diff); + + ret = vmbus_channel_send(chan, VMBUS_PACKET_DATA_INBAND, + NULL, 0, &bl, bl_size, false, + bl.hdr.trans_id); + if (ret <= 0) { + error_report("error %zd when posting balloon msg, expect problems", + ret); + } + + HV_BALLOON_STATE_DESC_SET(stdesc, S_BALLOON_REPLY_WAIT); +} + +static void hv_balloon_idle_state_process_target(HvBalloon *balloon, + StateDesc *stdesc) +{ + bool can_balloon = balloon->caps.cap_bits.balloon; + uint64_t ram_size_pages, total_removed; + + ram_size_pages = hv_balloon_total_ram(balloon); + total_removed = hv_balloon_total_removed_rs(balloon, ram_size_pages); + + /* + * we need to cache the values computed from the balloon target value when + * starting the adjustment procedure in case someone changes the target when + * the procedure is in progress + */ + if (balloon->target > ram_size_pages - total_removed) { + uint64_t target_diff = balloon->target - + (ram_size_pages - total_removed); + + balloon->unballoon_diff = MIN(target_diff, total_removed); + + if (balloon->unballoon_diff > 0) { + assert(can_balloon); + HV_BALLOON_STATE_DESC_SET(stdesc, S_UNBALLOON_RB_WAIT); + } + } else if (can_balloon && + balloon->target < ram_size_pages - total_removed) { + balloon->balloon_diff = ram_size_pages - total_removed - + balloon->target; + HV_BALLOON_STATE_DESC_SET(stdesc, S_BALLOON_RB_WAIT); + } +} + +static void hv_balloon_idle_state(HvBalloon *balloon, + StateDesc *stdesc) +{ + assert(balloon->state == S_IDLE); + + if (balloon->target_changed) { + balloon->target_changed = false; + hv_balloon_idle_state_process_target(balloon, stdesc); + return; + } +} + +static const struct { + void (*handler)(HvBalloon *balloon, StateDesc *stdesc); +} state_handlers[] = { + [S_IDLE].handler = hv_balloon_idle_state, + [S_BALLOON_POSTING].handler = hv_balloon_balloon_posting, + [S_BALLOON_RB_WAIT].handler = hv_balloon_balloon_rb_wait, + [S_UNBALLOON_POSTING].handler = hv_balloon_unballoon_posting, + [S_UNBALLOON_RB_WAIT].handler = hv_balloon_unballoon_rb_wait, +}; + +static void hv_balloon_handle_state(HvBalloon *balloon, StateDesc *stdesc) +{ + if (balloon->state >= ARRAY_SIZE(state_handlers) || + !state_handlers[balloon->state].handler) { + return; + } + + state_handlers[balloon->state].handler(balloon, stdesc); +} + +static void hv_balloon_remove_response_insert_range(PageRangeTree tree, + const PageRange *range, + uint64_t *ctr1, + uint64_t *ctr2, + uint64_t *ctr3) +{ + uint64_t dupcount, effcount; + + if (range->count == 0) { + return; + } + + dupcount = 0; + hvb_page_range_tree_insert(tree, range->start, range->count, &dupcount); + + assert(dupcount <= range->count); + effcount = range->count - dupcount; + + *ctr1 += effcount; + *ctr2 += effcount; + if (ctr3) { + *ctr3 += effcount; + } +} + +static void hv_balloon_remove_response_handle_range(HvBalloon *balloon, + PageRange *range, + bool both, + uint64_t *removedctr) +{ + PageRangeTree globaltree = + both ? balloon->removed_both : balloon->removed_guest; + uint64_t *globalctr = + both ? &balloon->removed_both_ctr : &balloon->removed_guest_ctr; + + trace_hv_balloon_remove_response(range->count, range->start, both); + + if (range->count > 0) { + hv_balloon_remove_response_insert_range(globaltree, range, + globalctr, removedctr, NULL); + trace_hv_balloon_remove_response_remainder(range->count, range->start, + both); + range->count = 0; + } +} + +static void hv_balloon_remove_response_handle_pages(HvBalloon *balloon, + PageRange *range, + uint64_t start, + uint64_t count, + bool both, + uint64_t *removedctr) +{ + assert(count > 0); + + /* + * if there is an existing range that the new range can't be joined to + * dump it into tree(s) + */ + if (range->count > 0 && !page_range_joinable(range, start, count)) { + hv_balloon_remove_response_handle_range(balloon, range, both, + removedctr); + } + + if (range->count == 0) { + range->start = start; + range->count = count; + } else if (page_range_joinable_left(range, start, count)) { + range->start = start; + range->count += count; + } else { /* page_range_joinable_right() */ + range->count += count; + } +} + +static gboolean hv_balloon_handle_remove_host_addr_node(gpointer key, + gpointer value, + gpointer data) +{ + PageRange *range = value; + uint64_t pageoff; + + for (pageoff = 0; pageoff < range->count; ) { + uint64_t addr_64 = (range->start + pageoff) * HV_BALLOON_PAGE_SIZE; + void *addr; + RAMBlock *rb; + ram_addr_t rb_offset; + size_t rb_page_size; + size_t discard_size; + + assert(addr_64 <= UINTPTR_MAX); + addr = (void *)((uintptr_t)addr_64); + rb = qemu_ram_block_from_host(addr, false, &rb_offset); + rb_page_size = qemu_ram_pagesize(rb); + + if (rb_page_size != HV_BALLOON_PAGE_SIZE) { + /* TODO: these should end in "removed_guest" */ + warn_report("guest reported removed page backed by unsupported page size %zu", + rb_page_size); + pageoff++; + continue; + } + + discard_size = MIN(range->count - pageoff, + (rb->max_length - rb_offset) / + HV_BALLOON_PAGE_SIZE); + discard_size = MAX(discard_size, 1); + + if (ram_block_discard_range(rb, rb_offset, discard_size * + HV_BALLOON_PAGE_SIZE) != 0) { + warn_report("guest reported removed page failed discard"); + } + + pageoff += discard_size; + } + + return false; +} + +static void hv_balloon_handle_remove_host_addr_tree(PageRangeTree tree) +{ + g_tree_foreach(tree.t, hv_balloon_handle_remove_host_addr_node, NULL); +} + +static int hv_balloon_handle_remove_section(PageRangeTree tree, + const MemoryRegionSection *section, + uint64_t count) +{ + void *addr = memory_region_get_ram_ptr(section->mr) + + section->offset_within_region; + uint64_t addr_page; + + assert(count > 0); + + if ((uintptr_t)addr % HV_BALLOON_PAGE_SIZE) { + warn_report("guest reported removed pages at an unaligned host addr %p", + addr); + return -EINVAL; + } + + addr_page = (uintptr_t)addr / HV_BALLOON_PAGE_SIZE; + hvb_page_range_tree_insert(tree, addr_page, count, NULL); + + return 0; +} + +static void hv_balloon_handle_remove_ranges(HvBalloon *balloon, + union dm_mem_page_range ranges[], + uint32_t count) +{ + uint64_t removedcnt; + PageRangeTree removed_host_addr; + PageRange range_guest, range_both; + + hvb_page_range_tree_init(&removed_host_addr); + range_guest.count = range_both.count = removedcnt = 0; + for (unsigned int ctr = 0; ctr < count; ctr++) { + union dm_mem_page_range *mr = &ranges[ctr]; + hwaddr pa; + MemoryRegionSection section; + + for (unsigned int offset = 0; offset < mr->finfo.page_cnt; ) { + int ret; + uint64_t pageno = mr->finfo.start_page + offset; + uint64_t pagecnt = 1; + + pa = (hwaddr)pageno << HV_BALLOON_PFN_SHIFT; + section = memory_region_find(get_system_memory(), pa, + (mr->finfo.page_cnt - offset) * + HV_BALLOON_PAGE_SIZE); + if (!section.mr) { + warn_report("guest reported removed page %"PRIu64" not found in RAM", + pageno); + ret = -EINVAL; + goto finish_page; + } + + pagecnt = int128_get64(section.size) / HV_BALLOON_PAGE_SIZE; + if (pagecnt <= 0) { + warn_report("guest reported removed page %"PRIu64" in a section smaller than page size", + pageno); + pagecnt = 1; /* skip the whole page */ + ret = -EINVAL; + goto finish_page; + } + + if (!memory_region_is_ram(section.mr) || + memory_region_is_rom(section.mr) || + memory_region_is_romd(section.mr)) { + warn_report("guest reported removed page %"PRIu64" in a section that is not an ordinary RAM", + pageno); + ret = -EINVAL; + goto finish_page; + } + + ret = hv_balloon_handle_remove_section(removed_host_addr, §ion, + pagecnt); + + finish_page: + if (ret == 0) { + hv_balloon_remove_response_handle_pages(balloon, + &range_both, + pageno, pagecnt, + true, &removedcnt); + } else { + hv_balloon_remove_response_handle_pages(balloon, + &range_guest, + pageno, pagecnt, + false, &removedcnt); + } + + if (section.mr) { + memory_region_unref(section.mr); + } + + offset += pagecnt; + } + } + + hv_balloon_remove_response_handle_range(balloon, &range_both, true, + &removedcnt); + hv_balloon_remove_response_handle_range(balloon, &range_guest, false, + &removedcnt); + + hv_balloon_handle_remove_host_addr_tree(removed_host_addr); + hvb_page_range_tree_destroy(&removed_host_addr); + + if (removedcnt > balloon->balloon_diff) { + warn_report("guest reported more pages removed than currently pending (%"PRIu64" vs %"PRIu64")", + removedcnt, balloon->balloon_diff); + balloon->balloon_diff = 0; + } else { + balloon->balloon_diff -= removedcnt; + } +} + +static bool hv_balloon_handle_msg_size(HvBalloonReq *req, size_t minsize, + const char *msgname) +{ + VMBusChanReq *vmreq = &req->vmreq; + uint32_t msglen = vmreq->msglen; + + if (msglen >= minsize) { + return true; + } + + warn_report("%s message too short (%u vs %zu), ignoring", msgname, + (unsigned int)msglen, minsize); + return false; +} + +static void hv_balloon_handle_version_request(HvBalloon *balloon, + HvBalloonReq *req, + StateDesc *stdesc) +{ + VMBusChanReq *vmreq = &req->vmreq; + struct dm_version_request *msgVr = vmreq->msg; + struct dm_version_response respVr; + + if (balloon->state != S_VERSION) { + warn_report("unexpected DM_VERSION_REQUEST in %d state", + balloon->state); + return; + } + + if (!hv_balloon_handle_msg_size(req, sizeof(*msgVr), + "DM_VERSION_REQUEST")) { + return; + } + + trace_hv_balloon_incoming_version(msgVr->version.major_version, + msgVr->version.minor_version); + + memset(&respVr, 0, sizeof(respVr)); + respVr.hdr.type = DM_VERSION_RESPONSE; + respVr.hdr.size = sizeof(respVr); + respVr.hdr.trans_id = msgVr->hdr.trans_id; + respVr.is_accepted = msgVr->version.version >= DYNMEM_PROTOCOL_VERSION_1 && + msgVr->version.version <= DYNMEM_PROTOCOL_VERSION_3; + + hv_balloon_send_packet(vmreq->chan, (struct dm_message *)&respVr); + + if (respVr.is_accepted) { + HV_BALLOON_STATE_DESC_SET(stdesc, S_CAPS); + } +} + +static void hv_balloon_handle_caps_report(HvBalloon *balloon, + HvBalloonReq *req, + StateDesc *stdesc) +{ + VMBusChanReq *vmreq = &req->vmreq; + struct dm_capabilities *msgCap = vmreq->msg; + struct dm_capabilities_resp_msg respCap; + + if (balloon->state != S_CAPS) { + warn_report("unexpected DM_CAPABILITIES_REPORT in %d state", + balloon->state); + return; + } + + if (!hv_balloon_handle_msg_size(req, sizeof(*msgCap), + "DM_CAPABILITIES_REPORT")) { + return; + } + + trace_hv_balloon_incoming_caps(msgCap->caps.caps); + balloon->caps = msgCap->caps; + + memset(&respCap, 0, sizeof(respCap)); + respCap.hdr.type = DM_CAPABILITIES_RESPONSE; + respCap.hdr.size = sizeof(respCap); + respCap.hdr.trans_id = msgCap->hdr.trans_id; + respCap.is_accepted = 1; + respCap.hot_remove = 1; + respCap.suppress_pressure_reports = !balloon->status_report.enabled; + hv_balloon_send_packet(vmreq->chan, (struct dm_message *)&respCap); + + timer_mod(&balloon->post_init_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + + HV_BALLOON_POST_INIT_WAIT); + + HV_BALLOON_STATE_DESC_SET(stdesc, S_POST_INIT_WAIT); +} + +static void hv_balloon_handle_status_report(HvBalloon *balloon, + HvBalloonReq *req) +{ + VMBusChanReq *vmreq = &req->vmreq; + struct dm_status *msgStatus = vmreq->msg; + + if (!hv_balloon_handle_msg_size(req, sizeof(*msgStatus), + "DM_STATUS_REPORT")) { + return; + } + + if (!balloon->status_report.enabled) { + return; + } + + balloon->status_report.committed = msgStatus->num_committed; + balloon->status_report.committed *= HV_BALLOON_PAGE_SIZE; + balloon->status_report.available = msgStatus->num_avail; + balloon->status_report.available *= HV_BALLOON_PAGE_SIZE; + balloon->status_report.received = true; + + /* report event */ +} + +static void hv_balloon_handle_unballoon_response(HvBalloon *balloon, + HvBalloonReq *req, + StateDesc *stdesc) +{ + VMBusChanReq *vmreq = &req->vmreq; + struct dm_unballoon_response *msgUrR = vmreq->msg; + + if (balloon->state != S_UNBALLOON_REPLY_WAIT) { + warn_report("unexpected DM_UNBALLOON_RESPONSE in %d state", + balloon->state); + return; + } + + if (!hv_balloon_handle_msg_size(req, sizeof(*msgUrR), + "DM_UNBALLOON_RESPONSE")) + return; + + trace_hv_balloon_incoming_unballoon(msgUrR->hdr.trans_id); + + balloon->trans_id++; + + HV_BALLOON_STATE_DESC_SET(stdesc, S_IDLE); +} + +static void hv_balloon_handle_balloon_response(HvBalloon *balloon, + HvBalloonReq *req, + StateDesc *stdesc) +{ + VMBusChanReq *vmreq = &req->vmreq; + struct dm_balloon_response *msgBR = vmreq->msg; + + if (balloon->state != S_BALLOON_REPLY_WAIT) { + warn_report("unexpected DM_BALLOON_RESPONSE in %d state", + balloon->state); + return; + } + + if (!hv_balloon_handle_msg_size(req, sizeof(*msgBR), + "DM_BALLOON_RESPONSE")) + return; + + trace_hv_balloon_incoming_balloon(msgBR->hdr.trans_id, msgBR->range_count, + msgBR->more_pages); + + if (vmreq->msglen < sizeof(*msgBR) + + (uint64_t)sizeof(msgBR->range_array[0]) * msgBR->range_count) { + warn_report("DM_BALLOON_RESPONSE too short for the range count"); + return; + } + + if (msgBR->range_count == 0) { + /* The guest is already at its minimum size */ + balloon->balloon_diff = 0; + goto ret_end_trans; + } else { + hv_balloon_handle_remove_ranges(balloon, + msgBR->range_array, + msgBR->range_count); + } + + /* More responses expected? */ + if (msgBR->more_pages) { + return; + } + +ret_end_trans: + balloon->trans_id++; + + if (balloon->balloon_diff > 0) { + HV_BALLOON_STATE_DESC_SET(stdesc, S_BALLOON_RB_WAIT); + } else { + HV_BALLOON_STATE_DESC_SET(stdesc, S_IDLE); + } +} + +static void hv_balloon_handle_packet(HvBalloon *balloon, HvBalloonReq *req, + StateDesc *stdesc) +{ + VMBusChanReq *vmreq = &req->vmreq; + struct dm_message *msg = vmreq->msg; + + if (vmreq->msglen < sizeof(msg->hdr)) { + return; + } + + switch (msg->hdr.type) { + case DM_VERSION_REQUEST: + hv_balloon_handle_version_request(balloon, req, stdesc); + break; + + case DM_CAPABILITIES_REPORT: + hv_balloon_handle_caps_report(balloon, req, stdesc); + break; + + case DM_STATUS_REPORT: + hv_balloon_handle_status_report(balloon, req); + break; + + case DM_UNBALLOON_RESPONSE: + hv_balloon_handle_unballoon_response(balloon, req, stdesc); + break; + + case DM_BALLOON_RESPONSE: + hv_balloon_handle_balloon_response(balloon, req, stdesc); + break; + + default: + warn_report("unknown DM message %u", msg->hdr.type); + break; + } +} + +static bool hv_balloon_recv_channel(HvBalloon *balloon, StateDesc *stdesc) +{ + VMBusChannel *chan; + HvBalloonReq *req; + + if (balloon->state == S_WAIT_RESET || + balloon->state == S_POST_RESET_CLOSED) { + return false; + } + + chan = hv_balloon_get_channel(balloon); + if (vmbus_channel_recv_start(chan)) { + return false; + } + + while ((req = vmbus_channel_recv_peek(chan, sizeof(*req)))) { + hv_balloon_handle_packet(balloon, req, stdesc); + vmbus_free_req(req); + vmbus_channel_recv_pop(chan); + + if (stdesc->state != S_NO_CHANGE) { + break; + } + } + + return vmbus_channel_recv_done(chan) > 0; +} + +/* old state handler -> new state transition (potential) */ +static bool hv_balloon_event_loop_state(HvBalloon *balloon) +{ + StateDesc state_new = HV_BALLOON_STATE_DESC_INIT; + + hv_balloon_handle_state(balloon, &state_new); + return hv_balloon_state_set(balloon, state_new.state, state_new.desc); +} + +/* VMBus message -> new state transition (potential) */ +static bool hv_balloon_event_loop_recv(HvBalloon *balloon) +{ + StateDesc state_new = HV_BALLOON_STATE_DESC_INIT; + bool any_recv, state_changed; + + any_recv = hv_balloon_recv_channel(balloon, &state_new); + state_changed = hv_balloon_state_set(balloon, + state_new.state, state_new.desc); + + return state_changed || any_recv; +} + +static void hv_balloon_event_loop(HvBalloon *balloon) +{ + bool state_repeat, recv_repeat; + + do { + state_repeat = hv_balloon_event_loop_state(balloon); + recv_repeat = hv_balloon_event_loop_recv(balloon); + } while (state_repeat || recv_repeat); +} + +static void hv_balloon_vmdev_chan_notify(VMBusChannel *chan) +{ + HvBalloon *balloon = HV_BALLOON(vmbus_channel_device(chan)); + + hv_balloon_event_loop(balloon); +} + +static void hv_balloon_stat(void *opaque, BalloonInfo *info) +{ + HvBalloon *balloon = opaque; + info->actual = (hv_balloon_total_ram(balloon) - balloon->removed_both_ctr) + << HV_BALLOON_PFN_SHIFT; +} + +static void hv_balloon_to_target(void *opaque, ram_addr_t target) +{ + HvBalloon *balloon = opaque; + uint64_t target_pages = target >> HV_BALLOON_PFN_SHIFT; + + if (!target_pages) { + return; + } + + /* + * always set target_changed, even with unchanged target, as the user + * might be asking us to try again reaching it + */ + balloon->target = target_pages; + balloon->target_changed = true; + + hv_balloon_event_loop(balloon); +} + +static int hv_balloon_vmdev_open_channel(VMBusChannel *chan) +{ + HvBalloon *balloon = HV_BALLOON(vmbus_channel_device(chan)); + + if (balloon->state != S_POST_RESET_CLOSED) { + warn_report("guest trying to open a DM channel in invalid %d state", + balloon->state); + return -EINVAL; + } + + HV_BALLOON_SET_STATE(balloon, S_VERSION); + hv_balloon_event_loop(balloon); + + return 0; +} + +static void hv_balloon_vmdev_close_channel(VMBusChannel *chan) +{ + HvBalloon *balloon = HV_BALLOON(vmbus_channel_device(chan)); + + timer_del(&balloon->post_init_timer); + + /* Don't report stale data */ + balloon->status_report.received = false; + + HV_BALLOON_SET_STATE(balloon, S_WAIT_RESET); + hv_balloon_event_loop(balloon); +} + +static void hv_balloon_post_init_timer(void *opaque) +{ + HvBalloon *balloon = opaque; + + if (balloon->state != S_POST_INIT_WAIT) { + return; + } + + HV_BALLOON_SET_STATE(balloon, S_IDLE); + hv_balloon_event_loop(balloon); +} + +static void hv_balloon_vmdev_realize(VMBusDevice *vdev, Error **errp) +{ + ERRP_GUARD(); + HvBalloon *balloon = HV_BALLOON(vdev); + int ret; + + balloon->state = S_WAIT_RESET; + + ret = qemu_add_balloon_handler(hv_balloon_to_target, hv_balloon_stat, + balloon); + if (ret < 0) { + /* This also protects against having multiple hv-balloon instances */ + error_setg(errp, "Only one balloon device is supported"); + return; + } + + timer_init_ms(&balloon->post_init_timer, QEMU_CLOCK_VIRTUAL, + hv_balloon_post_init_timer, balloon); +} + +/* + * VMBus device reset has to be implemented in case the guest decides to + * disconnect and reconnect to the VMBus without rebooting the whole system. + */ +static void hv_balloon_vmdev_reset(VMBusDevice *vdev) +{ + HvBalloon *balloon = HV_BALLOON(vdev); + + if (balloon->state == S_POST_RESET_CLOSED) { + return; + } + + hvb_page_range_tree_destroy(&balloon->removed_guest); + hvb_page_range_tree_destroy(&balloon->removed_both); + hvb_page_range_tree_init(&balloon->removed_guest); + hvb_page_range_tree_init(&balloon->removed_both); + + balloon->trans_id = 0; + balloon->removed_guest_ctr = 0; + balloon->removed_both_ctr = 0; + + HV_BALLOON_SET_STATE(balloon, S_POST_RESET_CLOSED); + hv_balloon_event_loop(balloon); +} + +static void hv_balloon_vmdev_unrealize(VMBusDevice *vdev) +{ + HvBalloon *balloon = HV_BALLOON(vdev); + + qemu_remove_balloon_handler(balloon); + + hvb_page_range_tree_destroy(&balloon->removed_guest); + hvb_page_range_tree_destroy(&balloon->removed_both); +} + +static void hv_balloon_init(Object *obj) +{ +} + +static void hv_balloon_finalize(Object *obj) +{ +} + +static Property hv_balloon_properties[] = { + DEFINE_PROP_BOOL("status-report", HvBalloon, + status_report.enabled, false), + + DEFINE_PROP_END_OF_LIST(), +}; + +static void hv_balloon_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VMBusDeviceClass *vdc = VMBUS_DEVICE_CLASS(klass); + + device_class_set_props(dc, hv_balloon_properties); + qemu_uuid_parse(HV_BALLOON_GUID, &vdc->classid); + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + vdc->vmdev_realize = hv_balloon_vmdev_realize; + vdc->vmdev_unrealize = hv_balloon_vmdev_unrealize; + vdc->vmdev_reset = hv_balloon_vmdev_reset; + vdc->open_channel = hv_balloon_vmdev_open_channel; + vdc->close_channel = hv_balloon_vmdev_close_channel; + vdc->chan_notify_cb = hv_balloon_vmdev_chan_notify; +} diff --git a/hw/hyperv/meson.build b/hw/hyperv/meson.build index b43f119ea5..b5be1cbb1a 100644 --- a/hw/hyperv/meson.build +++ b/hw/hyperv/meson.build @@ -2,3 +2,4 @@ specific_ss.add(when: 'CONFIG_HYPERV', if_true: files('hyperv.c')) specific_ss.add(when: 'CONFIG_HYPERV_TESTDEV', if_true: files('hyperv_testdev.c')) specific_ss.add(when: 'CONFIG_VMBUS', if_true: files('vmbus.c')) specific_ss.add(when: 'CONFIG_SYNDBG', if_true: files('syndbg.c')) +specific_ss.add(when: 'CONFIG_HV_BALLOON', if_true: files('hv-balloon.c', 'hv-balloon-page_range_tree.c')) diff --git a/hw/hyperv/trace-events b/hw/hyperv/trace-events index b4c35ca8e3..86c724ba66 100644 --- a/hw/hyperv/trace-events +++ b/hw/hyperv/trace-events @@ -16,3 +16,16 @@ vmbus_gpadl_torndown(uint32_t gpadl_id) "gpadl #%d" vmbus_open_channel(uint32_t chan_id, uint32_t gpadl_id, uint32_t target_vp) "channel #%d gpadl #%d target vp %d" vmbus_channel_open(uint32_t chan_id, uint32_t status) "channel #%d status %d" vmbus_close_channel(uint32_t chan_id) "channel #%d" + +# hv-balloon +hv_balloon_state_change(const char *tostr) "-> %s" +hv_balloon_incoming_version(uint16_t major, uint16_t minor) "incoming proto version %u.%u" +hv_balloon_incoming_caps(uint32_t caps) "incoming caps 0x%x" +hv_balloon_outgoing_unballoon(uint32_t trans_id, uint64_t count, uint64_t start, uint64_t rempages) "posting unballoon %"PRIu32" for %"PRIu64" @ 0x%"PRIx64", remaining %"PRIu64 +hv_balloon_incoming_unballoon(uint32_t trans_id) "incoming unballoon response %"PRIu32 +hv_balloon_outgoing_balloon(uint32_t trans_id, uint64_t count, uint64_t rempages) "posting balloon %"PRIu32" for %"PRIu64", remaining %"PRIu64 +hv_balloon_incoming_balloon(uint32_t trans_id, uint32_t range_count, uint32_t more_pages) "incoming balloon response %"PRIu32", ranges %"PRIu32", more %"PRIu32 +hv_balloon_remove_response(uint64_t count, uint64_t start, unsigned int both) "processing remove response range %"PRIu64" @ 0x%"PRIx64", both %u" +hv_balloon_remove_response_hole(uint64_t counthole, uint64_t starthole, uint64_t countrange, uint64_t startrange, uint64_t starthpr, unsigned int both) "response range hole %"PRIu64" @ 0x%"PRIx64" from range %"PRIu64" @ 0x%"PRIx64", before our start 0x%"PRIx64", both %u" +hv_balloon_remove_response_common(uint64_t countcommon, uint64_t startcommon, uint64_t countrange, uint64_t startrange, uint64_t counthpr, uint64_t starthpr, uint64_t removed, unsigned int both) "response common range %"PRIu64" @ 0x%"PRIx64" from range %"PRIu64" @ 0x%"PRIx64" with our %"PRIu64" @ 0x%"PRIx64", removed %"PRIu64", both %u" +hv_balloon_remove_response_remainder(uint64_t count, uint64_t start, unsigned int both) "remove response remaining range %"PRIu64" @ 0x%"PRIx64", both %u" diff --git a/include/hw/hyperv/hv-balloon.h b/include/hw/hyperv/hv-balloon.h new file mode 100644 index 0000000000..c1efe70fc2 --- /dev/null +++ b/include/hw/hyperv/hv-balloon.h @@ -0,0 +1,18 @@ +/* + * QEMU Hyper-V Dynamic Memory Protocol driver + * + * Copyright (C) 2020-2023 Oracle and/or its affiliates. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef HW_HV_BALLOON_H +#define HW_HV_BALLOON_H + +#include "qom/object.h" + +#define TYPE_HV_BALLOON "hv-balloon" +OBJECT_DECLARE_SIMPLE_TYPE(HvBalloon, HV_BALLOON) + +#endif diff --git a/meson.build b/meson.build index dcef8b1e79..51a51075db 100644 --- a/meson.build +++ b/meson.build @@ -1323,6 +1323,30 @@ if not get_option('glusterfs').auto() or have_block endif endif +hv_balloon = false +if get_option('hv_balloon').allowed() and have_system + if cc.links(''' + #include + #include + int main(void) { + GTree *tree; + + tree = g_tree_new((GCompareFunc)strcmp); + (void)g_tree_node_first(tree); + g_tree_destroy(tree); + return 0; + } + ''', dependencies: glib) + hv_balloon = true + else + if get_option('hv_balloon').enabled() + error('could not enable hv-balloon, update your glib') + else + warning('could not find glib support for hv-balloon, disabling') + endif + endif +endif + libssh = not_found if not get_option('libssh').auto() or have_block libssh = dependency('libssh', version: '>=0.8.7', @@ -2855,7 +2879,8 @@ host_kconfig = \ (targetos == 'linux' ? ['CONFIG_LINUX=y'] : []) + \ (have_pvrdma ? ['CONFIG_PVRDMA=y'] : []) + \ (multiprocess_allowed ? ['CONFIG_MULTIPROCESS_ALLOWED=y'] : []) + \ - (vfio_user_server_allowed ? ['CONFIG_VFIO_USER_SERVER_ALLOWED=y'] : []) + (vfio_user_server_allowed ? ['CONFIG_VFIO_USER_SERVER_ALLOWED=y'] : []) + \ + (hv_balloon ? ['CONFIG_HV_BALLOON_POSSIBLE=y'] : []) ignored = [ 'TARGET_XML_FILES', 'TARGET_ABI_DIR', 'TARGET_ARCH' ] @@ -4321,6 +4346,7 @@ if targetos == 'windows' endif summary_info += {'seccomp support': seccomp} summary_info += {'GlusterFS support': glusterfs} +summary_info += {'hv-balloon support': hv_balloon} summary_info += {'TPM support': have_tpm} summary_info += {'libssh support': libssh} summary_info += {'lzo support': lzo} diff --git a/meson_options.txt b/meson_options.txt index 3c7398f3c6..5c212fcd45 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -150,6 +150,8 @@ option('gio', type : 'feature', value : 'auto', description: 'use libgio for D-Bus support') option('glusterfs', type : 'feature', value : 'auto', description: 'Glusterfs block device driver') +option('hv_balloon', type : 'feature', value : 'auto', + description: 'hv-balloon driver (requires Glib 2.68+ GTree API)') option('libdw', type : 'feature', value : 'auto', description: 'debuginfo support') option('libiscsi', type : 'feature', value : 'auto', diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index 7ca4b77eae..e9d6d39279 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -123,6 +123,7 @@ meson_options_help() { printf "%s\n" ' gtk-clipboard clipboard support for the gtk UI (EXPERIMENTAL, MAY HANG)' printf "%s\n" ' guest-agent Build QEMU Guest Agent' printf "%s\n" ' guest-agent-msi Build MSI package for the QEMU Guest Agent' + printf "%s\n" ' hv-balloon hv-balloon driver (requires Glib 2.68+ GTree API)' printf "%s\n" ' hvf HVF acceleration support' printf "%s\n" ' iconv Font glyph conversion support' printf "%s\n" ' jack JACK sound support' @@ -333,6 +334,8 @@ _meson_option_parse() { --disable-guest-agent-msi) printf "%s" -Dguest_agent_msi=disabled ;; --enable-hexagon-idef-parser) printf "%s" -Dhexagon_idef_parser=true ;; --disable-hexagon-idef-parser) printf "%s" -Dhexagon_idef_parser=false ;; + --enable-hv-balloon) printf "%s" -Dhv_balloon=enabled ;; + --disable-hv-balloon) printf "%s" -Dhv_balloon=disabled ;; --enable-hvf) printf "%s" -Dhvf=enabled ;; --disable-hvf) printf "%s" -Dhvf=disabled ;; --iasl=*) quote_sh "-Diasl=$2" ;; From 99a4706ae81efa51b21871af643626730a6719d4 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Sun, 22 Oct 2023 17:20:20 +0200 Subject: [PATCH 466/974] Add Hyper-V Dynamic Memory Protocol driver (hv-balloon) hot-add support One of advantages of using this protocol over ACPI-based PC DIMM hotplug is that it allows hot-adding memory in much smaller granularity because the ACPI DIMM slot limit does not apply. In order to enable this functionality a new memory backend needs to be created and provided to the driver via the "memdev" parameter. This can be achieved by, for example, adding "-object memory-backend-ram,id=mem1,size=32G" to the QEMU command line and then instantiating the driver with "memdev=mem1" parameter. The device will try to use multiple memslots to cover the memory backend in order to reduce the size of metadata for the not-yet-hot-added part of the memory backend. Co-developed-by: David Hildenbrand Acked-by: David Hildenbrand Signed-off-by: Maciej S. Szmigiero --- hw/hyperv/hv-balloon-our_range_memslots.c | 201 ++++++++ hw/hyperv/hv-balloon-our_range_memslots.h | 110 +++++ hw/hyperv/hv-balloon.c | 566 +++++++++++++++++++++- hw/hyperv/meson.build | 2 +- hw/hyperv/trace-events | 5 + 5 files changed, 878 insertions(+), 6 deletions(-) create mode 100644 hw/hyperv/hv-balloon-our_range_memslots.c create mode 100644 hw/hyperv/hv-balloon-our_range_memslots.h diff --git a/hw/hyperv/hv-balloon-our_range_memslots.c b/hw/hyperv/hv-balloon-our_range_memslots.c new file mode 100644 index 0000000000..99bae870f3 --- /dev/null +++ b/hw/hyperv/hv-balloon-our_range_memslots.c @@ -0,0 +1,201 @@ +/* + * QEMU Hyper-V Dynamic Memory Protocol driver + * + * Copyright (C) 2020-2023 Oracle and/or its affiliates. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "hv-balloon-internal.h" +#include "hv-balloon-our_range_memslots.h" +#include "trace.h" + +/* OurRange */ +static void our_range_init(OurRange *our_range, uint64_t start, uint64_t count) +{ + assert(count <= UINT64_MAX - start); + our_range->range.start = start; + our_range->range.count = count; + + hvb_page_range_tree_init(&our_range->removed_guest); + hvb_page_range_tree_init(&our_range->removed_both); + + /* mark the whole range as unused but for potential use */ + our_range->added = 0; + our_range->unusable_tail = 0; +} + +static void our_range_destroy(OurRange *our_range) +{ + hvb_page_range_tree_destroy(&our_range->removed_guest); + hvb_page_range_tree_destroy(&our_range->removed_both); +} + +void hvb_our_range_clear_removed_trees(OurRange *our_range) +{ + hvb_page_range_tree_destroy(&our_range->removed_guest); + hvb_page_range_tree_destroy(&our_range->removed_both); + hvb_page_range_tree_init(&our_range->removed_guest); + hvb_page_range_tree_init(&our_range->removed_both); +} + +void hvb_our_range_mark_added(OurRange *our_range, uint64_t additional_size) +{ + assert(additional_size <= UINT64_MAX - our_range->added); + + our_range->added += additional_size; + + assert(our_range->added <= UINT64_MAX - our_range->unusable_tail); + assert(our_range->added + our_range->unusable_tail <= + our_range->range.count); +} + +/* OurRangeMemslots */ +static void our_range_memslots_init_slots(OurRangeMemslots *our_range, + MemoryRegion *backing_mr, + Object *memslot_owner) +{ + OurRangeMemslotsSlots *memslots = &our_range->slots; + unsigned int idx; + uint64_t memslot_offset; + + assert(memslots->count > 0); + memslots->slots = g_new0(MemoryRegion, memslots->count); + + /* Initialize our memslots, but don't map them yet. */ + assert(memslots->size_each > 0); + for (idx = 0, memslot_offset = 0; idx < memslots->count; + idx++, memslot_offset += memslots->size_each) { + uint64_t memslot_size; + g_autofree char *name = NULL; + + /* The size of the last memslot might be smaller. */ + if (idx == memslots->count - 1) { + uint64_t region_size; + + assert(our_range->mr); + region_size = memory_region_size(our_range->mr); + memslot_size = region_size - memslot_offset; + } else { + memslot_size = memslots->size_each; + } + + name = g_strdup_printf("memslot-%u", idx); + memory_region_init_alias(&memslots->slots[idx], memslot_owner, name, + backing_mr, memslot_offset, memslot_size); + /* + * We want to be able to atomically and efficiently activate/deactivate + * individual memslots without affecting adjacent memslots in memory + * notifiers. + */ + memory_region_set_unmergeable(&memslots->slots[idx], true); + } + + memslots->mapped_count = 0; +} + +OurRangeMemslots *hvb_our_range_memslots_new(uint64_t addr, + MemoryRegion *parent_mr, + MemoryRegion *backing_mr, + Object *memslot_owner, + unsigned int memslot_count, + uint64_t memslot_size) +{ + OurRangeMemslots *our_range; + + our_range = g_malloc(sizeof(*our_range)); + our_range_init(&our_range->range, + addr / HV_BALLOON_PAGE_SIZE, + memory_region_size(parent_mr) / HV_BALLOON_PAGE_SIZE); + our_range->slots.size_each = memslot_size; + our_range->slots.count = memslot_count; + our_range->mr = parent_mr; + our_range_memslots_init_slots(our_range, backing_mr, memslot_owner); + + return our_range; +} + +static void our_range_memslots_free_memslots(OurRangeMemslots *our_range) +{ + OurRangeMemslotsSlots *memslots = &our_range->slots; + unsigned int idx; + uint64_t offset; + + memory_region_transaction_begin(); + for (idx = 0, offset = 0; idx < memslots->mapped_count; + idx++, offset += memslots->size_each) { + trace_hv_balloon_unmap_slot(idx, memslots->count, offset); + assert(memory_region_is_mapped(&memslots->slots[idx])); + memory_region_del_subregion(our_range->mr, &memslots->slots[idx]); + } + memory_region_transaction_commit(); + + for (idx = 0; idx < memslots->count; idx++) { + object_unparent(OBJECT(&memslots->slots[idx])); + } + + g_clear_pointer(&our_range->slots.slots, g_free); +} + +void hvb_our_range_memslots_free(OurRangeMemslots *our_range) +{ + OurRangeMemslotsSlots *memslots = &our_range->slots; + MemoryRegion *hostmem_mr; + RAMBlock *rb; + + assert(our_range->slots.count > 0); + assert(our_range->slots.slots); + + hostmem_mr = memslots->slots[0].alias; + rb = hostmem_mr->ram_block; + ram_block_discard_range(rb, 0, qemu_ram_get_used_length(rb)); + + our_range_memslots_free_memslots(our_range); + our_range_destroy(&our_range->range); + g_free(our_range); +} + +void hvb_our_range_memslots_ensure_mapped_additional(OurRangeMemslots *our_range, + uint64_t additional_map_size) +{ + OurRangeMemslotsSlots *memslots = &our_range->slots; + uint64_t total_map_size; + unsigned int idx; + uint64_t offset; + + total_map_size = (our_range->range.added + additional_map_size) * + HV_BALLOON_PAGE_SIZE; + idx = memslots->mapped_count; + assert(memslots->size_each > 0); + offset = idx * memslots->size_each; + + /* + * Activate all memslots covered by the newly added region in a single + * transaction. + */ + memory_region_transaction_begin(); + for ( ; idx < memslots->count; + idx++, offset += memslots->size_each) { + /* + * If this memslot starts beyond or at the end of the range to map so + * does every next one. + */ + if (offset >= total_map_size) { + break; + } + + /* + * Instead of enabling/disabling memslot, we add/remove them. This + * should make address space updates faster, because we don't have to + * loop over many disabled subregions. + */ + trace_hv_balloon_map_slot(idx, memslots->count, offset); + assert(!memory_region_is_mapped(&memslots->slots[idx])); + memory_region_add_subregion(our_range->mr, offset, + &memslots->slots[idx]); + + memslots->mapped_count++; + } + memory_region_transaction_commit(); +} diff --git a/hw/hyperv/hv-balloon-our_range_memslots.h b/hw/hyperv/hv-balloon-our_range_memslots.h new file mode 100644 index 0000000000..b6f592d34b --- /dev/null +++ b/hw/hyperv/hv-balloon-our_range_memslots.h @@ -0,0 +1,110 @@ +/* + * QEMU Hyper-V Dynamic Memory Protocol driver + * + * Copyright (C) 2020-2023 Oracle and/or its affiliates. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef HW_HYPERV_HV_BALLOON_OUR_RANGE_MEMSLOTS_H +#define HW_HYPERV_HV_BALLOON_OUR_RANGE_MEMSLOTS_H + +#include "qemu/osdep.h" + +#include "exec/memory.h" +#include "qom/object.h" +#include "hv-balloon-page_range_tree.h" + +/* OurRange */ +#define OUR_RANGE(ptr) ((OurRange *)(ptr)) + +/* "our range" means the memory range owned by this driver (for hot-adding) */ +typedef struct OurRange { + PageRange range; + + /* How many pages were hot-added to the guest */ + uint64_t added; + + /* Pages at the end not currently usable */ + uint64_t unusable_tail; + + /* Memory removed from the guest */ + PageRangeTree removed_guest, removed_both; +} OurRange; + +static inline uint64_t our_range_get_remaining_start(OurRange *our_range) +{ + return our_range->range.start + our_range->added; +} + +static inline uint64_t our_range_get_remaining_size(OurRange *our_range) +{ + return our_range->range.count - our_range->added - our_range->unusable_tail; +} + +void hvb_our_range_mark_added(OurRange *our_range, uint64_t additional_size); + +static inline void our_range_mark_remaining_unusable(OurRange *our_range) +{ + our_range->unusable_tail = our_range->range.count - our_range->added; +} + +static inline PageRangeTree our_range_get_removed_tree(OurRange *our_range, + bool both) +{ + if (both) { + return our_range->removed_both; + } else { + return our_range->removed_guest; + } +} + +static inline bool our_range_is_removed_tree_empty(OurRange *our_range, + bool both) +{ + if (both) { + return page_range_tree_is_empty(our_range->removed_both); + } else { + return page_range_tree_is_empty(our_range->removed_guest); + } +} + +void hvb_our_range_clear_removed_trees(OurRange *our_range); + +/* OurRangeMemslots */ +typedef struct OurRangeMemslotsSlots { + /* Nominal size of each memslot (the last one might be smaller) */ + uint64_t size_each; + + /* Slots array and its element count */ + MemoryRegion *slots; + unsigned int count; + + /* How many slots are currently mapped */ + unsigned int mapped_count; +} OurRangeMemslotsSlots; + +typedef struct OurRangeMemslots { + OurRange range; + + /* Memslots covering our range */ + OurRangeMemslotsSlots slots; + + MemoryRegion *mr; +} OurRangeMemslots; + +OurRangeMemslots *hvb_our_range_memslots_new(uint64_t addr, + MemoryRegion *parent_mr, + MemoryRegion *backing_mr, + Object *memslot_owner, + unsigned int memslot_count, + uint64_t memslot_size); +void hvb_our_range_memslots_free(OurRangeMemslots *our_range); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(OurRangeMemslots, hvb_our_range_memslots_free) + +void hvb_our_range_memslots_ensure_mapped_additional(OurRangeMemslots *our_range, + uint64_t additional_map_size); + +#endif diff --git a/hw/hyperv/hv-balloon.c b/hw/hyperv/hv-balloon.c index 5f3674ba06..5999f1127d 100644 --- a/hw/hyperv/hv-balloon.c +++ b/hw/hyperv/hv-balloon.c @@ -34,9 +34,12 @@ #include "sysemu/balloon.h" #include "sysemu/hostmem.h" #include "sysemu/reset.h" +#include "hv-balloon-our_range_memslots.h" #include "hv-balloon-page_range_tree.h" #include "trace.h" +#define HV_BALLOON_ADDR_PROP "addr" +#define HV_BALLOON_MEMDEV_PROP "memdev" #define HV_BALLOON_GUID "525074DC-8985-46e2-8057-A307DC18A502" /* @@ -52,6 +55,8 @@ #define HV_BALLOON_HA_CHUNK_SIZE (2 * GiB) #define HV_BALLOON_HA_CHUNK_PAGES (HV_BALLOON_HA_CHUNK_SIZE / HV_BALLOON_PAGE_SIZE) +#define HV_BALLOON_HA_MEMSLOT_SIZE_ALIGN (128 * MiB) + #define HV_BALLOON_HR_CHUNK_PAGES 585728 /* * ^ that's the maximum number of pages @@ -88,6 +93,10 @@ typedef enum State { S_UNBALLOON_POSTING, S_UNBALLOON_RB_WAIT, S_UNBALLOON_REPLY_WAIT, + S_HOT_ADD_SETUP, + S_HOT_ADD_RB_WAIT, + S_HOT_ADD_POSTING, + S_HOT_ADD_REPLY_WAIT, } State; typedef struct StateDesc { @@ -117,25 +126,43 @@ typedef struct HvBalloon { uint64_t target; bool target_changed; - /* Current (un)balloon */ + /* Current (un)balloon / hot-add operation parameters */ union { uint64_t balloon_diff; struct { uint64_t unballoon_diff; + uint64_t hot_add_diff; + }; + + struct { + PageRange hot_add_range; + uint64_t ha_current_count; }; }; + OurRangeMemslots *our_range; + + /* Count of memslots covering our memory */ + unsigned int memslot_count; + /* Nominal size of each memslot (the last one might be smaller) */ uint64_t memslot_size; + /* Non-ours removed memory */ PageRangeTree removed_guest, removed_both; + /* Grand totals of removed memory (both ours and non-ours) */ uint64_t removed_guest_ctr, removed_both_ctr; + + /* MEMORY_DEVICE props */ + uint64_t addr; + HostMemoryBackend *hostmem; + MemoryRegion *mr; } HvBalloon; OBJECT_DEFINE_TYPE_WITH_INTERFACES(HvBalloon, hv_balloon, HV_BALLOON, VMBUS_DEVICE, \ - { }) + { TYPE_MEMORY_DEVICE }, { }) #define HV_BALLOON_SET_STATE(hvb, news) \ do { \ @@ -155,6 +182,16 @@ typedef struct HvBalloonReq { VMBusChanReq vmreq; } HvBalloonReq; +/* total our memory includes parts currently removed from the guest */ +static uint64_t hv_balloon_total_our_ram(HvBalloon *balloon) +{ + if (!balloon->our_range) { + return 0; + } + + return balloon->our_range->range.added; +} + /* TODO: unify the code below with virtio-balloon and cache the value */ static int build_dimm_list(Object *obj, void *opaque) { @@ -193,10 +230,11 @@ static uint64_t hv_balloon_total_ram(HvBalloon *balloon) { ram_addr_t ram_size = get_current_ram_size(); uint64_t ram_size_pages = ram_size >> HV_BALLOON_PFN_SHIFT; + uint64_t our_ram_size_pages = hv_balloon_total_our_ram(balloon); assert(ram_size_pages > 0); - return ram_size_pages; + return SUM_SATURATE_U64(ram_size_pages, our_ram_size_pages); } /* @@ -275,14 +313,30 @@ static ssize_t hv_balloon_send_packet(VMBusChannel *chan, static bool hv_balloon_unballoon_get_source(HvBalloon *balloon, PageRangeTree *dtree, - uint64_t **dctr) + uint64_t **dctr, + bool *is_our_range) { + OurRange *our_range = OUR_RANGE(balloon->our_range); + + /* Try the boot memory first */ if (g_tree_nnodes(balloon->removed_guest.t) > 0) { *dtree = balloon->removed_guest; *dctr = &balloon->removed_guest_ctr; + *is_our_range = false; } else if (g_tree_nnodes(balloon->removed_both.t) > 0) { *dtree = balloon->removed_both; *dctr = &balloon->removed_both_ctr; + *is_our_range = false; + } else if (!our_range) { + return false; + } else if (!our_range_is_removed_tree_empty(our_range, false)) { + *dtree = our_range_get_removed_tree(our_range, false); + *dctr = &balloon->removed_guest_ctr; + *is_our_range = true; + } else if (!our_range_is_removed_tree_empty(our_range, true)) { + *dtree = our_range_get_removed_tree(our_range, true); + *dctr = &balloon->removed_both_ctr; + *is_our_range = true; } else { return false; } @@ -310,6 +364,7 @@ static void hv_balloon_unballoon_posting(HvBalloon *balloon, StateDesc *stdesc) VMBusChannel *chan = hv_balloon_get_channel(balloon); PageRangeTree dtree; uint64_t *dctr; + bool our_range; struct dm_unballoon_request *ur; size_t ur_size = sizeof(*ur) + sizeof(ur->range_array[0]); PageRange range; @@ -319,7 +374,7 @@ static void hv_balloon_unballoon_posting(HvBalloon *balloon, StateDesc *stdesc) assert(balloon->state == S_UNBALLOON_POSTING); assert(balloon->unballoon_diff > 0); - if (!hv_balloon_unballoon_get_source(balloon, &dtree, &dctr)) { + if (!hv_balloon_unballoon_get_source(balloon, &dtree, &dctr, &our_range)) { error_report("trying to unballoon but nothing seems to be ballooned"); /* * there is little we can do as we might have already @@ -328,6 +383,7 @@ static void hv_balloon_unballoon_posting(HvBalloon *balloon, StateDesc *stdesc) return; } + assert(balloon->our_range || !our_range); assert(dtree.t); assert(dctr); @@ -369,6 +425,166 @@ static void hv_balloon_unballoon_posting(HvBalloon *balloon, StateDesc *stdesc) } } +static bool hv_balloon_our_range_ensure(HvBalloon *balloon) +{ + uint64_t align; + MemoryRegion *hostmem_mr; + g_autoptr(OurRangeMemslots) our_range_memslots = NULL; + OurRange *our_range; + + if (balloon->our_range) { + return true; + } + + if (!balloon->hostmem) { + return false; + } + + align = (1 << balloon->caps.cap_bits.hot_add_alignment) * MiB; + assert(QEMU_IS_ALIGNED(balloon->addr, align)); + + hostmem_mr = host_memory_backend_get_memory(balloon->hostmem); + + our_range_memslots = hvb_our_range_memslots_new(balloon->addr, + balloon->mr, hostmem_mr, + OBJECT(balloon), + balloon->memslot_count, + balloon->memslot_size); + our_range = OUR_RANGE(our_range_memslots); + + if (hvb_page_range_tree_intree_any(balloon->removed_guest, + our_range->range.start, + our_range->range.count) || + hvb_page_range_tree_intree_any(balloon->removed_both, + our_range->range.start, + our_range->range.count)) { + error_report("some parts of the memory backend were already returned by the guest. this should not happen, please reboot the guest and try again"); + return false; + } + + trace_hv_balloon_our_range_add(our_range->range.count, + our_range->range.start); + + balloon->our_range = g_steal_pointer(&our_range_memslots); + return true; +} + +static void hv_balloon_hot_add_setup(HvBalloon *balloon, StateDesc *stdesc) +{ + /* need to make copy since it is in union with hot_add_range */ + uint64_t hot_add_diff = balloon->hot_add_diff; + PageRange *hot_add_range = &balloon->hot_add_range; + uint64_t align, our_range_remaining; + OurRange *our_range; + + assert(balloon->state == S_HOT_ADD_SETUP); + assert(hot_add_diff > 0); + + if (!hv_balloon_our_range_ensure(balloon)) { + goto ret_idle; + } + + our_range = OUR_RANGE(balloon->our_range); + + align = (1 << balloon->caps.cap_bits.hot_add_alignment) * + (MiB / HV_BALLOON_PAGE_SIZE); + + /* Absolute GPA in pages */ + hot_add_range->start = our_range_get_remaining_start(our_range); + assert(QEMU_IS_ALIGNED(hot_add_range->start, align)); + + our_range_remaining = our_range_get_remaining_size(our_range); + hot_add_range->count = MIN(our_range_remaining, hot_add_diff); + hot_add_range->count = QEMU_ALIGN_DOWN(hot_add_range->count, align); + if (hot_add_range->count == 0) { + goto ret_idle; + } + + hvb_our_range_memslots_ensure_mapped_additional(balloon->our_range, + hot_add_range->count); + + HV_BALLOON_STATE_DESC_SET(stdesc, S_HOT_ADD_RB_WAIT); + return; + +ret_idle: + HV_BALLOON_STATE_DESC_SET(stdesc, S_IDLE); +} + +static void hv_balloon_hot_add_rb_wait(HvBalloon *balloon, StateDesc *stdesc) +{ + VMBusChannel *chan = hv_balloon_get_channel(balloon); + struct dm_hot_add *ha; + size_t ha_size = sizeof(*ha) + sizeof(ha->range); + + assert(balloon->state == S_HOT_ADD_RB_WAIT); + + if (vmbus_channel_reserve(chan, 0, ha_size) < 0) { + return; + } + + HV_BALLOON_STATE_DESC_SET(stdesc, S_HOT_ADD_POSTING); +} + +static void hv_balloon_hot_add_posting(HvBalloon *balloon, StateDesc *stdesc) +{ + PageRange *hot_add_range = &balloon->hot_add_range; + uint64_t *current_count = &balloon->ha_current_count; + VMBusChannel *chan = hv_balloon_get_channel(balloon); + struct dm_hot_add *ha; + size_t ha_size = sizeof(*ha) + sizeof(ha->range); + union dm_mem_page_range *ha_region; + uint64_t align, chunk_max_size; + ssize_t ret; + + assert(balloon->state == S_HOT_ADD_POSTING); + assert(hot_add_range->count > 0); + + align = (1 << balloon->caps.cap_bits.hot_add_alignment) * + (MiB / HV_BALLOON_PAGE_SIZE); + if (align >= HV_BALLOON_HA_CHUNK_PAGES) { + /* + * If the required alignment is higher than the chunk size we let it + * override that size. + */ + chunk_max_size = align; + } else { + chunk_max_size = QEMU_ALIGN_DOWN(HV_BALLOON_HA_CHUNK_PAGES, align); + } + + /* + * hot_add_range->count starts aligned in hv_balloon_hot_add_setup(), + * then it is either reduced by subtracting aligned current_count or + * further hot-adds are prevented by marking the whole remaining our range + * as unusable in hv_balloon_handle_hot_add_response(). + */ + *current_count = MIN(hot_add_range->count, chunk_max_size); + + ha = alloca(ha_size); + ha_region = &(&ha->range)[1]; + memset(ha, 0, ha_size); + ha->hdr.type = DM_MEM_HOT_ADD_REQUEST; + ha->hdr.size = ha_size; + ha->hdr.trans_id = balloon->trans_id; + + ha->range.finfo.start_page = hot_add_range->start; + ha->range.finfo.page_cnt = *current_count; + ha_region->finfo.start_page = hot_add_range->start; + ha_region->finfo.page_cnt = ha->range.finfo.page_cnt; + + trace_hv_balloon_outgoing_hot_add(ha->hdr.trans_id, + *current_count, hot_add_range->start); + + ret = vmbus_channel_send(chan, VMBUS_PACKET_DATA_INBAND, + NULL, 0, ha, ha_size, false, + ha->hdr.trans_id); + if (ret <= 0) { + error_report("error %zd when posting hot add msg, expect problems", + ret); + } + + HV_BALLOON_STATE_DESC_SET(stdesc, S_HOT_ADD_REPLY_WAIT); +} + static void hv_balloon_balloon_rb_wait(HvBalloon *balloon, StateDesc *stdesc) { VMBusChannel *chan = hv_balloon_get_channel(balloon); @@ -428,14 +644,23 @@ static void hv_balloon_idle_state_process_target(HvBalloon *balloon, * the procedure is in progress */ if (balloon->target > ram_size_pages - total_removed) { + bool can_hot_add = balloon->caps.cap_bits.hot_add; uint64_t target_diff = balloon->target - (ram_size_pages - total_removed); balloon->unballoon_diff = MIN(target_diff, total_removed); + if (can_hot_add) { + balloon->hot_add_diff = target_diff - balloon->unballoon_diff; + } else { + balloon->hot_add_diff = 0; + } + if (balloon->unballoon_diff > 0) { assert(can_balloon); HV_BALLOON_STATE_DESC_SET(stdesc, S_UNBALLOON_RB_WAIT); + } else if (balloon->hot_add_diff > 0) { + HV_BALLOON_STATE_DESC_SET(stdesc, S_HOT_ADD_SETUP); } } else if (can_balloon && balloon->target < ram_size_pages - total_removed) { @@ -465,6 +690,9 @@ static const struct { [S_BALLOON_RB_WAIT].handler = hv_balloon_balloon_rb_wait, [S_UNBALLOON_POSTING].handler = hv_balloon_unballoon_posting, [S_UNBALLOON_RB_WAIT].handler = hv_balloon_unballoon_rb_wait, + [S_HOT_ADD_SETUP].handler = hv_balloon_hot_add_setup, + [S_HOT_ADD_RB_WAIT].handler = hv_balloon_hot_add_rb_wait, + [S_HOT_ADD_POSTING].handler = hv_balloon_hot_add_posting, }; static void hv_balloon_handle_state(HvBalloon *balloon, StateDesc *stdesc) @@ -507,13 +735,64 @@ static void hv_balloon_remove_response_handle_range(HvBalloon *balloon, bool both, uint64_t *removedctr) { + OurRange *our_range = OUR_RANGE(balloon->our_range); PageRangeTree globaltree = both ? balloon->removed_both : balloon->removed_guest; uint64_t *globalctr = both ? &balloon->removed_both_ctr : &balloon->removed_guest_ctr; + PageRange rangeeff; + + if (range->count == 0) { + return; + } trace_hv_balloon_remove_response(range->count, range->start, both); + if (our_range) { + /* Includes the not-yet-hot-added and unusable parts. */ + rangeeff = our_range->range; + } else { + rangeeff.start = rangeeff.count = 0; + } + + if (page_range_intersection_size(range, rangeeff.start, rangeeff.count) > 0) { + PageRangeTree ourtree = our_range_get_removed_tree(our_range, both); + PageRange rangehole, rangecommon; + uint64_t ourremoved = 0; + + /* process the hole before our range, if it exists */ + page_range_part_before(range, rangeeff.start, &rangehole); + hv_balloon_remove_response_insert_range(globaltree, &rangehole, + globalctr, removedctr, NULL); + if (rangehole.count > 0) { + trace_hv_balloon_remove_response_hole(rangehole.count, + rangehole.start, + range->count, range->start, + rangeeff.start, both); + } + + /* process our part */ + page_range_intersect(range, rangeeff.start, rangeeff.count, + &rangecommon); + hv_balloon_remove_response_insert_range(ourtree, &rangecommon, + globalctr, removedctr, + &ourremoved); + if (rangecommon.count > 0) { + trace_hv_balloon_remove_response_common(rangecommon.count, + rangecommon.start, + range->count, range->start, + rangeeff.count, + rangeeff.start, ourremoved, + both); + } + + /* calculate what's left after our range */ + rangecommon = *range; + page_range_part_after(&rangecommon, rangeeff.start, rangeeff.count, + range); + } + + /* process the remainder of the range that lies after our range */ if (range->count > 0) { hv_balloon_remove_response_insert_range(globaltree, range, globalctr, removedctr, NULL); @@ -847,6 +1126,72 @@ static void hv_balloon_handle_unballoon_response(HvBalloon *balloon, balloon->trans_id++; + if (balloon->hot_add_diff > 0) { + bool can_hot_add = balloon->caps.cap_bits.hot_add; + + assert(can_hot_add); + HV_BALLOON_STATE_DESC_SET(stdesc, S_HOT_ADD_SETUP); + } else { + HV_BALLOON_STATE_DESC_SET(stdesc, S_IDLE); + } +} + +static void hv_balloon_handle_hot_add_response(HvBalloon *balloon, + HvBalloonReq *req, + StateDesc *stdesc) +{ + PageRange *hot_add_range = &balloon->hot_add_range; + VMBusChanReq *vmreq = &req->vmreq; + struct dm_hot_add_response *msgHaR = vmreq->msg; + OurRange *our_range; + + if (balloon->state != S_HOT_ADD_REPLY_WAIT) { + warn_report("unexpected DM_HOT_ADD_RESPONSE in %d state", + balloon->state); + return; + } + + assert(balloon->our_range); + our_range = OUR_RANGE(balloon->our_range); + + if (!hv_balloon_handle_msg_size(req, sizeof(*msgHaR), + "DM_HOT_ADD_RESPONSE")) + return; + + trace_hv_balloon_incoming_hot_add(msgHaR->hdr.trans_id, msgHaR->result, + msgHaR->page_count); + + balloon->trans_id++; + + if (msgHaR->result) { + if (msgHaR->page_count > balloon->ha_current_count) { + warn_report("DM_HOT_ADD_RESPONSE page count higher than requested (%"PRIu32" vs %"PRIu64")", + msgHaR->page_count, balloon->ha_current_count); + msgHaR->page_count = balloon->ha_current_count; + } + + hvb_our_range_mark_added(our_range, msgHaR->page_count); + hot_add_range->start += msgHaR->page_count; + hot_add_range->count -= msgHaR->page_count; + } + + if (!msgHaR->result || msgHaR->page_count < balloon->ha_current_count) { + /* + * the current planned range was only partially hot-added, take note + * how much of it remains and don't attempt any further hot adds + */ + our_range_mark_remaining_unusable(our_range); + + goto ret_idle; + } + + /* any pages remaining to hot-add in our range? */ + if (hot_add_range->count > 0) { + HV_BALLOON_STATE_DESC_SET(stdesc, S_HOT_ADD_RB_WAIT); + return; + } + +ret_idle: HV_BALLOON_STATE_DESC_SET(stdesc, S_IDLE); } @@ -924,6 +1269,10 @@ static void hv_balloon_handle_packet(HvBalloon *balloon, HvBalloonReq *req, hv_balloon_handle_status_report(balloon, req); break; + case DM_MEM_HOT_ADD_RESPONSE: + hv_balloon_handle_hot_add_response(balloon, req, stdesc); + break; + case DM_UNBALLOON_RESPONSE: hv_balloon_handle_unballoon_response(balloon, req, stdesc); break; @@ -1072,6 +1421,61 @@ static void hv_balloon_post_init_timer(void *opaque) hv_balloon_event_loop(balloon); } +static void hv_balloon_system_reset_unrealize_common(HvBalloon *balloon) +{ + g_clear_pointer(&balloon->our_range, hvb_our_range_memslots_free); +} + +static void hv_balloon_system_reset(void *opaque) +{ + HvBalloon *balloon = HV_BALLOON(opaque); + + hv_balloon_system_reset_unrealize_common(balloon); +} + +static void hv_balloon_ensure_mr(HvBalloon *balloon) +{ + MemoryRegion *hostmem_mr; + + assert(balloon->hostmem); + + if (balloon->mr) { + return; + } + + hostmem_mr = host_memory_backend_get_memory(balloon->hostmem); + + balloon->mr = g_new0(MemoryRegion, 1); + memory_region_init(balloon->mr, OBJECT(balloon), TYPE_HV_BALLOON, + memory_region_size(hostmem_mr)); + + /* + * The VM can indicate an alignment up to 32 GiB. Memory device core can + * usually only handle/guarantee 1 GiB alignment. The user will have to + * specify a larger maxmem eventually. + * + * The memory device core will warn the user in case maxmem might have to be + * increased and will fail plugging the device if there is not sufficient + * space after alignment. + * + * TODO: we could do the alignment ourselves in a slightly bigger region. + * But this feels better, although the warning might be annoying. Maybe + * we can optimize that in the future (e.g., with such a device on the + * cmdline place/size the device memory region differently. + */ + balloon->mr->align = MAX(32 * GiB, memory_region_get_alignment(hostmem_mr)); +} + +static void hv_balloon_free_mr(HvBalloon *balloon) +{ + if (!balloon->mr) { + return; + } + + object_unparent(OBJECT(balloon->mr)); + g_clear_pointer(&balloon->mr, g_free); +} + static void hv_balloon_vmdev_realize(VMBusDevice *vdev, Error **errp) { ERRP_GUARD(); @@ -1088,13 +1492,52 @@ static void hv_balloon_vmdev_realize(VMBusDevice *vdev, Error **errp) return; } + if (balloon->hostmem) { + if (host_memory_backend_is_mapped(balloon->hostmem)) { + Object *obj = OBJECT(balloon->hostmem); + + error_setg(errp, "'%s' property specifies a busy memdev: %s", + HV_BALLOON_MEMDEV_PROP, + object_get_canonical_path_component(obj)); + goto out_balloon_handler; + } + + hv_balloon_ensure_mr(balloon); + + /* This is rather unlikely to happen, but let's still check for it. */ + if (!QEMU_IS_ALIGNED(memory_region_size(balloon->mr), + HV_BALLOON_PAGE_SIZE)) { + error_setg(errp, "'%s' property memdev size has to be a multiple of 0x%" PRIx64, + HV_BALLOON_MEMDEV_PROP, (uint64_t)HV_BALLOON_PAGE_SIZE); + goto out_balloon_handler; + } + + host_memory_backend_set_mapped(balloon->hostmem, true); + vmstate_register_ram(host_memory_backend_get_memory(balloon->hostmem), + DEVICE(balloon)); + } else if (balloon->addr) { + error_setg(errp, "'%s' property must not be set without a memdev", + HV_BALLOON_MEMDEV_PROP); + goto out_balloon_handler; + } + timer_init_ms(&balloon->post_init_timer, QEMU_CLOCK_VIRTUAL, hv_balloon_post_init_timer, balloon); + + qemu_register_reset(hv_balloon_system_reset, balloon); + + return; + +out_balloon_handler: + qemu_remove_balloon_handler(balloon); } /* * VMBus device reset has to be implemented in case the guest decides to * disconnect and reconnect to the VMBus without rebooting the whole system. + * + * However, the hot-added memory can't be removed here as Windows keeps on using + * it until the system is restarted, even after disconnecting from the VMBus. */ static void hv_balloon_vmdev_reset(VMBusDevice *vdev) { @@ -1104,6 +1547,10 @@ static void hv_balloon_vmdev_reset(VMBusDevice *vdev) return; } + if (balloon->our_range) { + hvb_our_range_clear_removed_trees(OUR_RANGE(balloon->our_range)); + } + hvb_page_range_tree_destroy(&balloon->removed_guest); hvb_page_range_tree_destroy(&balloon->removed_both); hvb_page_range_tree_init(&balloon->removed_guest); @@ -1117,14 +1564,106 @@ static void hv_balloon_vmdev_reset(VMBusDevice *vdev) hv_balloon_event_loop(balloon); } +/* + * Clean up things that were (possibly) allocated pre-realization, for example + * from memory_device_pre_plug(), so we don't leak them if the device don't + * actually get realized in the end. + */ +static void hv_balloon_unrealize_finalize_common(HvBalloon *balloon) +{ + hv_balloon_free_mr(balloon); + balloon->addr = 0; + + balloon->memslot_count = 0; +} + static void hv_balloon_vmdev_unrealize(VMBusDevice *vdev) { HvBalloon *balloon = HV_BALLOON(vdev); + qemu_unregister_reset(hv_balloon_system_reset, balloon); + + hv_balloon_system_reset_unrealize_common(balloon); + qemu_remove_balloon_handler(balloon); + if (balloon->hostmem) { + vmstate_unregister_ram(host_memory_backend_get_memory(balloon->hostmem), + DEVICE(balloon)); + host_memory_backend_set_mapped(balloon->hostmem, false); + } + hvb_page_range_tree_destroy(&balloon->removed_guest); hvb_page_range_tree_destroy(&balloon->removed_both); + + hv_balloon_unrealize_finalize_common(balloon); +} + +static uint64_t hv_balloon_md_get_addr(const MemoryDeviceState *md) +{ + return object_property_get_uint(OBJECT(md), HV_BALLOON_ADDR_PROP, + &error_abort); +} + +static void hv_balloon_md_set_addr(MemoryDeviceState *md, uint64_t addr, + Error **errp) +{ + object_property_set_uint(OBJECT(md), HV_BALLOON_ADDR_PROP, addr, errp); +} + +static MemoryRegion *hv_balloon_md_get_memory_region(MemoryDeviceState *md, + Error **errp) +{ + HvBalloon *balloon = HV_BALLOON(md); + + if (!balloon->hostmem) { + return NULL; + } + + hv_balloon_ensure_mr(balloon); + + return balloon->mr; +} + +static void hv_balloon_decide_memslots(MemoryDeviceState *md, + unsigned int limit) +{ + HvBalloon *balloon = HV_BALLOON(md); + MemoryRegion *hostmem_mr; + uint64_t region_size, memslot_size, memslots; + + /* We're called exactly once, before realizing the device. */ + assert(!balloon->memslot_count); + + /* We should not be called if we don't have a memory backend */ + assert(balloon->hostmem); + + hostmem_mr = host_memory_backend_get_memory(balloon->hostmem); + region_size = memory_region_size(hostmem_mr); + + assert(region_size > 0); + memslot_size = QEMU_ALIGN_UP(region_size / limit, + HV_BALLOON_HA_MEMSLOT_SIZE_ALIGN); + memslots = QEMU_ALIGN_UP(region_size, memslot_size) / memslot_size; + + if (memslots > 1) { + balloon->memslot_size = memslot_size; + } else { + balloon->memslot_size = region_size; + } + + assert(memslots <= UINT_MAX); + balloon->memslot_count = memslots; +} + +static unsigned int hv_balloon_get_memslots(MemoryDeviceState *md) +{ + const HvBalloon *balloon = HV_BALLOON(md); + + /* We're called after setting the suggested limit. */ + assert(balloon->memslot_count > 0); + + return balloon->memslot_count; } static void hv_balloon_init(Object *obj) @@ -1133,12 +1672,20 @@ static void hv_balloon_init(Object *obj) static void hv_balloon_finalize(Object *obj) { + HvBalloon *balloon = HV_BALLOON(obj); + + hv_balloon_unrealize_finalize_common(balloon); } static Property hv_balloon_properties[] = { DEFINE_PROP_BOOL("status-report", HvBalloon, status_report.enabled, false), + /* MEMORY_DEVICE props */ + DEFINE_PROP_LINK(HV_BALLOON_MEMDEV_PROP, HvBalloon, hostmem, + TYPE_MEMORY_BACKEND, HostMemoryBackend *), + DEFINE_PROP_UINT64(HV_BALLOON_ADDR_PROP, HvBalloon, addr, 0), + DEFINE_PROP_END_OF_LIST(), }; @@ -1146,6 +1693,7 @@ static void hv_balloon_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VMBusDeviceClass *vdc = VMBUS_DEVICE_CLASS(klass); + MemoryDeviceClass *mdc = MEMORY_DEVICE_CLASS(klass); device_class_set_props(dc, hv_balloon_properties); qemu_uuid_parse(HV_BALLOON_GUID, &vdc->classid); @@ -1157,4 +1705,12 @@ static void hv_balloon_class_init(ObjectClass *klass, void *data) vdc->open_channel = hv_balloon_vmdev_open_channel; vdc->close_channel = hv_balloon_vmdev_close_channel; vdc->chan_notify_cb = hv_balloon_vmdev_chan_notify; + + mdc->get_addr = hv_balloon_md_get_addr; + mdc->set_addr = hv_balloon_md_set_addr; + mdc->get_plugged_size = memory_device_get_region_size; + mdc->get_memory_region = hv_balloon_md_get_memory_region; + mdc->decide_memslots = hv_balloon_decide_memslots; + mdc->get_memslots = hv_balloon_get_memslots; + /* implement fill_device_info */ } diff --git a/hw/hyperv/meson.build b/hw/hyperv/meson.build index b5be1cbb1a..852d4f4a2e 100644 --- a/hw/hyperv/meson.build +++ b/hw/hyperv/meson.build @@ -2,4 +2,4 @@ specific_ss.add(when: 'CONFIG_HYPERV', if_true: files('hyperv.c')) specific_ss.add(when: 'CONFIG_HYPERV_TESTDEV', if_true: files('hyperv_testdev.c')) specific_ss.add(when: 'CONFIG_VMBUS', if_true: files('vmbus.c')) specific_ss.add(when: 'CONFIG_SYNDBG', if_true: files('syndbg.c')) -specific_ss.add(when: 'CONFIG_HV_BALLOON', if_true: files('hv-balloon.c', 'hv-balloon-page_range_tree.c')) +specific_ss.add(when: 'CONFIG_HV_BALLOON', if_true: files('hv-balloon.c', 'hv-balloon-page_range_tree.c', 'hv-balloon-our_range_memslots.c')) diff --git a/hw/hyperv/trace-events b/hw/hyperv/trace-events index 86c724ba66..7963c215b1 100644 --- a/hw/hyperv/trace-events +++ b/hw/hyperv/trace-events @@ -23,9 +23,14 @@ hv_balloon_incoming_version(uint16_t major, uint16_t minor) "incoming proto vers hv_balloon_incoming_caps(uint32_t caps) "incoming caps 0x%x" hv_balloon_outgoing_unballoon(uint32_t trans_id, uint64_t count, uint64_t start, uint64_t rempages) "posting unballoon %"PRIu32" for %"PRIu64" @ 0x%"PRIx64", remaining %"PRIu64 hv_balloon_incoming_unballoon(uint32_t trans_id) "incoming unballoon response %"PRIu32 +hv_balloon_outgoing_hot_add(uint32_t trans_id, uint64_t count, uint64_t start) "posting hot add %"PRIu32" for %"PRIu64" @ 0x%"PRIx64 +hv_balloon_incoming_hot_add(uint32_t trans_id, uint32_t result, uint32_t count) "incoming hot add response %"PRIu32", result %"PRIu32", count %"PRIu32 hv_balloon_outgoing_balloon(uint32_t trans_id, uint64_t count, uint64_t rempages) "posting balloon %"PRIu32" for %"PRIu64", remaining %"PRIu64 hv_balloon_incoming_balloon(uint32_t trans_id, uint32_t range_count, uint32_t more_pages) "incoming balloon response %"PRIu32", ranges %"PRIu32", more %"PRIu32 +hv_balloon_our_range_add(uint64_t count, uint64_t start) "adding our range %"PRIu64" @ 0x%"PRIx64 hv_balloon_remove_response(uint64_t count, uint64_t start, unsigned int both) "processing remove response range %"PRIu64" @ 0x%"PRIx64", both %u" hv_balloon_remove_response_hole(uint64_t counthole, uint64_t starthole, uint64_t countrange, uint64_t startrange, uint64_t starthpr, unsigned int both) "response range hole %"PRIu64" @ 0x%"PRIx64" from range %"PRIu64" @ 0x%"PRIx64", before our start 0x%"PRIx64", both %u" hv_balloon_remove_response_common(uint64_t countcommon, uint64_t startcommon, uint64_t countrange, uint64_t startrange, uint64_t counthpr, uint64_t starthpr, uint64_t removed, unsigned int both) "response common range %"PRIu64" @ 0x%"PRIx64" from range %"PRIu64" @ 0x%"PRIx64" with our %"PRIu64" @ 0x%"PRIx64", removed %"PRIu64", both %u" hv_balloon_remove_response_remainder(uint64_t count, uint64_t start, unsigned int both) "remove response remaining range %"PRIu64" @ 0x%"PRIx64", both %u" +hv_balloon_map_slot(unsigned int idx, unsigned int total_slots, uint64_t offset) "mapping memslot %u / %u @ 0x%"PRIx64 +hv_balloon_unmap_slot(unsigned int idx, unsigned int total_slots, uint64_t offset) "unmapping memslot %u / %u @ 0x%"PRIx64 From 16dff2f9bb877bd1e147b5c5d9966d5a1d336c8c Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Wed, 23 Aug 2023 23:31:35 +0200 Subject: [PATCH 467/974] qapi: Add query-memory-devices support to hv-balloon Used by the driver to report its provided memory state information. Co-developed-by: David Hildenbrand Reviewed-by: David Hildenbrand Acked-by: Markus Armbruster Signed-off-by: Maciej S. Szmigiero --- hw/core/machine-hmp-cmds.c | 15 +++++++++++++++ hw/hyperv/hv-balloon.c | 27 +++++++++++++++++++++++++- qapi/machine.json | 39 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 78 insertions(+), 3 deletions(-) diff --git a/hw/core/machine-hmp-cmds.c b/hw/core/machine-hmp-cmds.c index 9a4b59c6f2..a6ff6a4875 100644 --- a/hw/core/machine-hmp-cmds.c +++ b/hw/core/machine-hmp-cmds.c @@ -253,6 +253,7 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict) MemoryDeviceInfo *value; PCDIMMDeviceInfo *di; SgxEPCDeviceInfo *se; + HvBalloonDeviceInfo *hi; for (info = info_list; info; info = info->next) { value = info->value; @@ -310,6 +311,20 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict) monitor_printf(mon, " node: %" PRId64 "\n", se->node); monitor_printf(mon, " memdev: %s\n", se->memdev); break; + case MEMORY_DEVICE_INFO_KIND_HV_BALLOON: + hi = value->u.hv_balloon.data; + monitor_printf(mon, "Memory device [%s]: \"%s\"\n", + MemoryDeviceInfoKind_str(value->type), + hi->id ? hi->id : ""); + if (hi->has_memaddr) { + monitor_printf(mon, " memaddr: 0x%" PRIx64 "\n", + hi->memaddr); + } + monitor_printf(mon, " max-size: %" PRIu64 "\n", hi->max_size); + if (hi->memdev) { + monitor_printf(mon, " memdev: %s\n", hi->memdev); + } + break; default: g_assert_not_reached(); } diff --git a/hw/hyperv/hv-balloon.c b/hw/hyperv/hv-balloon.c index 5999f1127d..44a8d15cc8 100644 --- a/hw/hyperv/hv-balloon.c +++ b/hw/hyperv/hv-balloon.c @@ -1625,6 +1625,31 @@ static MemoryRegion *hv_balloon_md_get_memory_region(MemoryDeviceState *md, return balloon->mr; } +static void hv_balloon_md_fill_device_info(const MemoryDeviceState *md, + MemoryDeviceInfo *info) +{ + HvBalloonDeviceInfo *hi = g_new0(HvBalloonDeviceInfo, 1); + const HvBalloon *balloon = HV_BALLOON(md); + DeviceState *dev = DEVICE(md); + + if (dev->id) { + hi->id = g_strdup(dev->id); + } + + if (balloon->hostmem) { + hi->memdev = object_get_canonical_path(OBJECT(balloon->hostmem)); + hi->memaddr = balloon->addr; + hi->has_memaddr = true; + hi->max_size = memory_region_size(balloon->mr); + /* TODO: expose current provided size or something else? */ + } else { + hi->max_size = 0; + } + + info->u.hv_balloon.data = hi; + info->type = MEMORY_DEVICE_INFO_KIND_HV_BALLOON; +} + static void hv_balloon_decide_memslots(MemoryDeviceState *md, unsigned int limit) { @@ -1712,5 +1737,5 @@ static void hv_balloon_class_init(ObjectClass *klass, void *data) mdc->get_memory_region = hv_balloon_md_get_memory_region; mdc->decide_memslots = hv_balloon_decide_memslots; mdc->get_memslots = hv_balloon_get_memslots; - /* implement fill_device_info */ + mdc->fill_device_info = hv_balloon_md_fill_device_info; } diff --git a/qapi/machine.json b/qapi/machine.json index 6c9d2f6dcf..2985d043c0 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1289,6 +1289,29 @@ } } +## +# @HvBalloonDeviceInfo: +# +# hv-balloon provided memory state information +# +# @id: device's ID +# +# @memaddr: physical address in memory, where device is mapped +# +# @max-size: the maximum size of memory that the device can provide +# +# @memdev: memory backend linked with device +# +# Since: 8.2 +## +{ 'struct': 'HvBalloonDeviceInfo', + 'data': { '*id': 'str', + '*memaddr': 'size', + 'max-size': 'size', + '*memdev': 'str' + } +} + ## # @MemoryDeviceInfoKind: # @@ -1300,10 +1323,13 @@ # # @sgx-epc: since 6.2. # +# @hv-balloon: since 8.2. +# # Since: 2.1 ## { 'enum': 'MemoryDeviceInfoKind', - 'data': [ 'dimm', 'nvdimm', 'virtio-pmem', 'virtio-mem', 'sgx-epc' ] } + 'data': [ 'dimm', 'nvdimm', 'virtio-pmem', 'virtio-mem', 'sgx-epc', + 'hv-balloon' ] } ## # @PCDIMMDeviceInfoWrapper: @@ -1337,6 +1363,14 @@ { 'struct': 'SgxEPCDeviceInfoWrapper', 'data': { 'data': 'SgxEPCDeviceInfo' } } +## +# @HvBalloonDeviceInfoWrapper: +# +# Since: 8.2 +## +{ 'struct': 'HvBalloonDeviceInfoWrapper', + 'data': { 'data': 'HvBalloonDeviceInfo' } } + ## # @MemoryDeviceInfo: # @@ -1351,7 +1385,8 @@ 'nvdimm': 'PCDIMMDeviceInfoWrapper', 'virtio-pmem': 'VirtioPMEMDeviceInfoWrapper', 'virtio-mem': 'VirtioMEMDeviceInfoWrapper', - 'sgx-epc': 'SgxEPCDeviceInfoWrapper' + 'sgx-epc': 'SgxEPCDeviceInfoWrapper', + 'hv-balloon': 'HvBalloonDeviceInfoWrapper' } } From 259ebed45a2cd2ae6a5820fbc2e51578d2ad4eb7 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Wed, 23 Aug 2023 23:34:14 +0200 Subject: [PATCH 468/974] qapi: Add HV_BALLOON_STATUS_REPORT event and its QMP query command Used by the hv-balloon driver for (optional) guest memory status reports. Acked-by: David Hildenbrand Signed-off-by: Maciej S. Szmigiero --- hw/hyperv/hv-balloon-stub.c | 19 ++++++++++++ hw/hyperv/hv-balloon.c | 30 +++++++++++++++++- hw/hyperv/meson.build | 2 +- monitor/monitor.c | 1 + qapi/machine.json | 62 +++++++++++++++++++++++++++++++++++++ tests/qtest/qmp-cmd-test.c | 1 + 6 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 hw/hyperv/hv-balloon-stub.c diff --git a/hw/hyperv/hv-balloon-stub.c b/hw/hyperv/hv-balloon-stub.c new file mode 100644 index 0000000000..a47412d4a8 --- /dev/null +++ b/hw/hyperv/hv-balloon-stub.c @@ -0,0 +1,19 @@ +/* + * QEMU Hyper-V Dynamic Memory Protocol driver + * + * Copyright (C) 2023 Oracle and/or its affiliates. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-machine.h" +#include "qapi/qapi-types-machine.h" + +HvBalloonInfo *qmp_query_hv_balloon_status_report(Error **errp) +{ + error_setg(errp, "hv-balloon device not enabled in this build"); + return NULL; +} diff --git a/hw/hyperv/hv-balloon.c b/hw/hyperv/hv-balloon.c index 44a8d15cc8..66f297c1d7 100644 --- a/hw/hyperv/hv-balloon.c +++ b/hw/hyperv/hv-balloon.c @@ -1102,7 +1102,35 @@ static void hv_balloon_handle_status_report(HvBalloon *balloon, balloon->status_report.available *= HV_BALLOON_PAGE_SIZE; balloon->status_report.received = true; - /* report event */ + qapi_event_send_hv_balloon_status_report(balloon->status_report.committed, + balloon->status_report.available); +} + +HvBalloonInfo *qmp_query_hv_balloon_status_report(Error **errp) +{ + HvBalloon *balloon; + HvBalloonInfo *info; + + balloon = HV_BALLOON(object_resolve_path_type("", TYPE_HV_BALLOON, NULL)); + if (!balloon) { + error_setg(errp, "no %s device present", TYPE_HV_BALLOON); + return NULL; + } + + if (!balloon->status_report.enabled) { + error_setg(errp, "guest memory status reporting not enabled"); + return NULL; + } + + if (!balloon->status_report.received) { + error_setg(errp, "no guest memory status report received yet"); + return NULL; + } + + info = g_malloc0(sizeof(*info)); + info->committed = balloon->status_report.committed; + info->available = balloon->status_report.available; + return info; } static void hv_balloon_handle_unballoon_response(HvBalloon *balloon, diff --git a/hw/hyperv/meson.build b/hw/hyperv/meson.build index 852d4f4a2e..d3d2668c71 100644 --- a/hw/hyperv/meson.build +++ b/hw/hyperv/meson.build @@ -2,4 +2,4 @@ specific_ss.add(when: 'CONFIG_HYPERV', if_true: files('hyperv.c')) specific_ss.add(when: 'CONFIG_HYPERV_TESTDEV', if_true: files('hyperv_testdev.c')) specific_ss.add(when: 'CONFIG_VMBUS', if_true: files('vmbus.c')) specific_ss.add(when: 'CONFIG_SYNDBG', if_true: files('syndbg.c')) -specific_ss.add(when: 'CONFIG_HV_BALLOON', if_true: files('hv-balloon.c', 'hv-balloon-page_range_tree.c', 'hv-balloon-our_range_memslots.c')) +specific_ss.add(when: 'CONFIG_HV_BALLOON', if_true: files('hv-balloon.c', 'hv-balloon-page_range_tree.c', 'hv-balloon-our_range_memslots.c'), if_false: files('hv-balloon-stub.c')) diff --git a/monitor/monitor.c b/monitor/monitor.c index 941f87815a..01ede1babd 100644 --- a/monitor/monitor.c +++ b/monitor/monitor.c @@ -315,6 +315,7 @@ static MonitorQAPIEventConf monitor_qapi_event_conf[QAPI_EVENT__MAX] = { [QAPI_EVENT_QUORUM_FAILURE] = { 1000 * SCALE_MS }, [QAPI_EVENT_VSERPORT_CHANGE] = { 1000 * SCALE_MS }, [QAPI_EVENT_MEMORY_DEVICE_SIZE_CHANGE] = { 1000 * SCALE_MS }, + [QAPI_EVENT_HV_BALLOON_STATUS_REPORT] = { 1000 * SCALE_MS }, }; /* diff --git a/qapi/machine.json b/qapi/machine.json index 2985d043c0..b6d634b30d 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1137,6 +1137,68 @@ { 'event': 'BALLOON_CHANGE', 'data': { 'actual': 'int' } } +## +# @HvBalloonInfo: +# +# hv-balloon guest-provided memory status information. +# +# @committed: the amount of memory in use inside the guest plus the +# amount of the memory unusable inside the guest (ballooned out, +# offline, etc.) +# +# @available: the amount of the memory inside the guest available for +# new allocations ("free") +# +# Since: 8.2 +## +{ 'struct': 'HvBalloonInfo', + 'data': { 'committed': 'size', 'available': 'size' } } + +## +# @query-hv-balloon-status-report: +# +# Returns the hv-balloon driver data contained in the last received "STATUS" +# message from the guest. +# +# Returns: +# - @HvBalloonInfo on success +# - If no hv-balloon device is present, guest memory status reporting +# is not enabled or no guest memory status report received yet, +# GenericError +# +# Since: 8.2 +# +# Example: +# +# -> { "execute": "query-hv-balloon-status-report" } +# <- { "return": { +# "committed": 816640000, +# "available": 3333054464 +# } +# } +## +{ 'command': 'query-hv-balloon-status-report', 'returns': 'HvBalloonInfo' } + +## +# @HV_BALLOON_STATUS_REPORT: +# +# Emitted when the hv-balloon driver receives a "STATUS" message from +# the guest. +# +# Note: this event is rate-limited. +# +# Since: 8.2 +# +# Example: +# +# <- { "event": "HV_BALLOON_STATUS_REPORT", +# "data": { "committed": 816640000, "available": 3333054464 }, +# "timestamp": { "seconds": 1600295492, "microseconds": 661044 } } +# +## +{ 'event': 'HV_BALLOON_STATUS_REPORT', + 'data': 'HvBalloonInfo' } + ## # @MemoryInfo: # diff --git a/tests/qtest/qmp-cmd-test.c b/tests/qtest/qmp-cmd-test.c index 73a670e8fa..2c15f60958 100644 --- a/tests/qtest/qmp-cmd-test.c +++ b/tests/qtest/qmp-cmd-test.c @@ -45,6 +45,7 @@ static int query_error_class(const char *cmd) { "query-acpi-ospm-status", ERROR_CLASS_GENERIC_ERROR }, { "query-balloon", ERROR_CLASS_DEVICE_NOT_ACTIVE }, { "query-hotpluggable-cpus", ERROR_CLASS_GENERIC_ERROR }, + { "query-hv-balloon-status-report", ERROR_CLASS_GENERIC_ERROR }, { "query-vm-generation-id", ERROR_CLASS_GENERIC_ERROR }, /* Only valid with a USB bus added */ { "x-query-usb", ERROR_CLASS_GENERIC_ERROR }, From 9a52aa40dc039c2e047d86886a2f66ce03e2b98c Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Wed, 23 Aug 2023 23:36:35 +0200 Subject: [PATCH 469/974] hw/i386/pc: Support hv-balloon Add the necessary plumbing for the hv-balloon driver to the PC machine. Co-developed-by: David Hildenbrand Reviewed-by: David Hildenbrand Signed-off-by: Maciej S. Szmigiero --- hw/i386/Kconfig | 1 + hw/i386/pc.c | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index 94772c726b..55850791df 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -45,6 +45,7 @@ config PC select ACPI_VMGENID select VIRTIO_PMEM_SUPPORTED select VIRTIO_MEM_SUPPORTED + select HV_BALLOON_SUPPORTED config PC_PCI bool diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 6031234a73..1aef21aa2c 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -27,6 +27,7 @@ #include "hw/i386/pc.h" #include "hw/char/serial.h" #include "hw/char/parallel.h" +#include "hw/hyperv/hv-balloon.h" #include "hw/i386/fw_cfg.h" #include "hw/i386/vmport.h" #include "sysemu/cpus.h" @@ -57,6 +58,7 @@ #include "hw/i386/kvm/xen_evtchn.h" #include "hw/i386/kvm/xen_gnttab.h" #include "hw/i386/kvm/xen_xenstore.h" +#include "hw/mem/memory-device.h" #include "e820_memory_layout.h" #include "trace.h" #include CONFIG_DEVICES @@ -1422,6 +1424,21 @@ static void pc_memory_unplug(HotplugHandler *hotplug_dev, error_propagate(errp, local_err); } +static void pc_hv_balloon_pre_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + /* The vmbus handler has no hotplug handler; we should never end up here. */ + g_assert(!dev->hotplugged); + memory_device_pre_plug(MEMORY_DEVICE(dev), MACHINE(hotplug_dev), NULL, + errp); +} + +static void pc_hv_balloon_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + memory_device_plug(MEMORY_DEVICE(dev), MACHINE(hotplug_dev)); +} + static void pc_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { @@ -1452,6 +1469,8 @@ static void pc_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, return; } pcms->iommu = dev; + } else if (object_dynamic_cast(OBJECT(dev), TYPE_HV_BALLOON)) { + pc_hv_balloon_pre_plug(hotplug_dev, dev, errp); } } @@ -1464,6 +1483,8 @@ static void pc_machine_device_plug_cb(HotplugHandler *hotplug_dev, x86_cpu_plug(hotplug_dev, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_PCI)) { virtio_md_pci_plug(VIRTIO_MD_PCI(dev), MACHINE(hotplug_dev), errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_HV_BALLOON)) { + pc_hv_balloon_plug(hotplug_dev, dev, errp); } } @@ -1505,6 +1526,7 @@ static HotplugHandler *pc_get_hotplug_handler(MachineState *machine, object_dynamic_cast(OBJECT(dev), TYPE_CPU) || object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_PCI) || object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI) || + object_dynamic_cast(OBJECT(dev), TYPE_HV_BALLOON) || object_dynamic_cast(OBJECT(dev), TYPE_X86_IOMMU_DEVICE)) { return HOTPLUG_HANDLER(machine); } From 00313b517d09c0b141fb32997791f911c28fd3ff Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Sat, 21 Oct 2023 23:14:53 +0200 Subject: [PATCH 470/974] MAINTAINERS: Add an entry for Hyper-V Dynamic Memory Protocol Acked-by: David Hildenbrand Signed-off-by: Maciej S. Szmigiero --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 8e8a7d5be5..d4a480ce5a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2656,6 +2656,14 @@ F: hw/usb/canokey.c F: hw/usb/canokey.h F: docs/system/devices/canokey.rst +Hyper-V Dynamic Memory Protocol +M: Maciej S. Szmigiero +S: Supported +F: hw/hyperv/hv-balloon*.c +F: hw/hyperv/hv-balloon*.h +F: include/hw/hyperv/dynmem-proto.h +F: include/hw/hyperv/hv-balloon.h + Subsystems ---------- Overall Audio backends From f66767f75c9c2ddebbf17aab37bfd752716a96b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 7 Sep 2023 17:04:31 +0400 Subject: [PATCH 471/974] virtio-gpu: add virtio-gpu/blob vmstate subsection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Acked-by: Peter Xu --- hw/display/virtio-gpu.c | 101 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 26065c6466..1ab662db72 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -1216,6 +1216,9 @@ static int virtio_gpu_save(QEMUFile *f, void *opaque, size_t size, assert(QTAILQ_EMPTY(&g->cmdq)); QTAILQ_FOREACH(res, &g->reslist, next) { + if (res->blob_size) { + continue; + } qemu_put_be32(f, res->resource_id); qemu_put_be32(f, res->width); qemu_put_be32(f, res->height); @@ -1340,6 +1343,74 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size, return 0; } +static int virtio_gpu_blob_save(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field, JSONWriter *vmdesc) +{ + VirtIOGPU *g = opaque; + struct virtio_gpu_simple_resource *res; + int i; + + /* in 2d mode we should never find unprocessed commands here */ + assert(QTAILQ_EMPTY(&g->cmdq)); + + QTAILQ_FOREACH(res, &g->reslist, next) { + if (!res->blob_size) { + continue; + } + qemu_put_be32(f, res->resource_id); + qemu_put_be32(f, res->blob_size); + qemu_put_be32(f, res->iov_cnt); + for (i = 0; i < res->iov_cnt; i++) { + qemu_put_be64(f, res->addrs[i]); + qemu_put_be32(f, res->iov[i].iov_len); + } + } + qemu_put_be32(f, 0); /* end of list */ + + return 0; +} + +static int virtio_gpu_blob_load(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field) +{ + VirtIOGPU *g = opaque; + struct virtio_gpu_simple_resource *res; + uint32_t resource_id; + int i; + + resource_id = qemu_get_be32(f); + while (resource_id != 0) { + res = virtio_gpu_find_resource(g, resource_id); + if (res) { + return -EINVAL; + } + + res = g_new0(struct virtio_gpu_simple_resource, 1); + res->resource_id = resource_id; + res->blob_size = qemu_get_be32(f); + res->iov_cnt = qemu_get_be32(f); + res->addrs = g_new(uint64_t, res->iov_cnt); + res->iov = g_new(struct iovec, res->iov_cnt); + + /* read data */ + for (i = 0; i < res->iov_cnt; i++) { + res->addrs[i] = qemu_get_be64(f); + res->iov[i].iov_len = qemu_get_be32(f); + } + + if (!virtio_gpu_load_restore_mapping(g, res)) { + g_free(res); + return -EINVAL; + } + + virtio_gpu_init_udmabuf(res); + + resource_id = qemu_get_be32(f); + } + + return 0; +} + static int virtio_gpu_post_load(void *opaque, int version_id) { VirtIOGPU *g = opaque; @@ -1505,6 +1576,32 @@ virtio_gpu_set_config(VirtIODevice *vdev, const uint8_t *config) } } +static bool virtio_gpu_blob_state_needed(void *opaque) +{ + VirtIOGPU *g = VIRTIO_GPU(opaque); + + return virtio_gpu_blob_enabled(g->parent_obj.conf); +} + +const VMStateDescription vmstate_virtio_gpu_blob_state = { + .name = "virtio-gpu/blob", + .minimum_version_id = VIRTIO_GPU_VM_VERSION, + .version_id = VIRTIO_GPU_VM_VERSION, + .needed = virtio_gpu_blob_state_needed, + .fields = (const VMStateField[]){ + { + .name = "virtio-gpu/blob", + .info = &(const VMStateInfo) { + .name = "blob", + .get = virtio_gpu_blob_load, + .put = virtio_gpu_blob_save, + }, + .flags = VMS_SINGLE, + } /* device */, + VMSTATE_END_OF_LIST() + }, +}; + /* * For historical reasons virtio_gpu does not adhere to virtio migration * scheme as described in doc/virtio-migration.txt, in a sense that no @@ -1530,6 +1627,10 @@ static const VMStateDescription vmstate_virtio_gpu = { } /* device */, VMSTATE_END_OF_LIST() }, + .subsections = (const VMStateDescription * []) { + &vmstate_virtio_gpu_blob_state, + NULL + }, .post_load = virtio_gpu_post_load, }; From 10b9ddbc83b94986cbdf989e26fb7269fb2e9f72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 7 Sep 2023 17:04:58 +0400 Subject: [PATCH 472/974] Revert "virtio-gpu: block migration of VMs with blob=true" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If we decide to apply this patch (for easier backporting reasons), we can now revert it. Signed-off-by: Marc-André Lureau Acked-by: Peter Xu --- hw/display/virtio-gpu.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 1ab662db72..2707bceea8 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -26,7 +26,6 @@ #include "hw/virtio/virtio-gpu-pixman.h" #include "hw/virtio/virtio-bus.h" #include "hw/qdev-properties.h" -#include "migration/blocker.h" #include "qemu/log.h" #include "qemu/module.h" #include "qapi/error.h" @@ -41,8 +40,6 @@ virtio_gpu_find_check_resource(VirtIOGPU *g, uint32_t resource_id, static void virtio_gpu_reset_bh(void *opaque); -static Error *blob_mig_blocker; - void virtio_gpu_update_cursor_data(VirtIOGPU *g, struct virtio_gpu_scanout *s, uint32_t resource_id) @@ -1463,14 +1460,6 @@ void virtio_gpu_device_realize(DeviceState *qdev, Error **errp) error_setg(errp, "blobs and virgl are not compatible (yet)"); return; } - - if (!blob_mig_blocker) { - error_setg(&blob_mig_blocker, - "virtio-gpu blob VMs are currently not migratable."); - } - if (migrate_add_blocker(blob_mig_blocker, errp)) { - return; - } } if (!virtio_gpu_base_device_realize(qdev, @@ -1497,9 +1486,6 @@ static void virtio_gpu_device_unrealize(DeviceState *qdev) { VirtIOGPU *g = VIRTIO_GPU(qdev); - if (virtio_gpu_blob_enabled(g->parent_obj.conf)) { - migrate_del_blocker(blob_mig_blocker); - } g_clear_pointer(&g->ctrl_bh, qemu_bh_delete); g_clear_pointer(&g->cursor_bh, qemu_bh_delete); g_clear_pointer(&g->reset_bh, qemu_bh_delete); From fa68ecb330dbdfeefa7f4d11e30c939da13853b0 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 6 Nov 2023 15:00:26 +0000 Subject: [PATCH 473/974] hw/arm/virt: fix PMU IRQ registration Since commit 9036e917f8 ("{include/}hw/arm: refactor virt PPI logic") PMU IRQ registration fails for arm64 guests: [ 0.563689] hw perfevents: unable to request IRQ14 for ARM PMU counters [ 0.565160] armv8-pmu: probe of pmu failed with error -22 That commit re-defined VIRTUAL_PMU_IRQ to be a INTID but missed a case where the PMU IRQ is actually referred by its PPI index. Fix that by using INTID_TO_PPI() in that case. Fixes: 9036e917f8 ("{include/}hw/arm: refactor virt PPI logic") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1960 Signed-off-by: Sebastian Ott Reviewed-by: Peter Maydell Message-id: 475d918d-ab0e-f717-7206-57a5beb28c7b@redhat.com Signed-off-by: Peter Maydell --- hw/arm/virt.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 92085d2d8f..0a16ab3095 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -631,7 +631,8 @@ static void fdt_add_pmu_nodes(const VirtMachineState *vms) qemu_fdt_setprop(ms->fdt, "/pmu", "compatible", compat, sizeof(compat)); qemu_fdt_setprop_cells(ms->fdt, "/pmu", "interrupts", - GIC_FDT_IRQ_TYPE_PPI, VIRTUAL_PMU_IRQ, irqflags); + GIC_FDT_IRQ_TYPE_PPI, + INTID_TO_PPI(VIRTUAL_PMU_IRQ), irqflags); } } From 1eb2888ecd55ace57b37009492f11cd53a6f4148 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 6 Nov 2023 15:00:26 +0000 Subject: [PATCH 474/974] tests/qtest/bios-tables-test: Allow changes to virt SPCR and DBG2 Allow changes to the virt board SPCR and DBG2 -- we are going to fix an error in the UART descriptions there. Signed-off-by: Peter Maydell --- tests/qtest/bios-tables-test-allowed-diff.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..6673e2c4c1 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,3 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/virt/SPCR", +"tests/data/acpi/virt/DBG2", From 41f7b58b634ec3b60ae874375d2bbb61d790971e Mon Sep 17 00:00:00 2001 From: Udo Steinberg Date: Mon, 6 Nov 2023 15:00:26 +0000 Subject: [PATCH 475/974] hw/arm/virt: Report correct register sizes in ACPI DBG2/SPCR tables. Documentation for using the GAS in ACPI tables to report debug UART addresses at https://learn.microsoft.com/en-us/windows-hardware/drivers/bringup/acpi-debug-port-table states the following: - The Register Bit Width field contains the register stride and must be a power of 2 that is at least as large as the access size. On 32-bit platforms this value cannot exceed 32. On 64-bit platforms this value cannot exceed 64. - The Access Size field is used to determine whether byte, WORD, DWORD, or QWORD accesses are to be used. QWORD accesses are only valid on 64-bit architectures. Documentation for the ARM PL011 at https://developer.arm.com/documentation/ddi0183/latest/ states that the registers are: - spaced 4 bytes apart (see Table 3-2), so register stride must be 32. - 16 bits in size in some cases (see individual registers), so access size must be at least 2. Linux doesn't seem to care about this error in the table, but it does affect at least the NOVA microhypervisor. In theory we therefore have a choice between reporting the access size as 2 (16 bit accesses) or 3 (32-bit accesses). In practice, Linux does not correctly handle the case where the table reports the access size as 2: as of kernel commit 750b95887e5678, the code in acpi_parse_spcr() tries to tell the serial driver to use 16 bit accesses by passing "mmio16" in the option string, but the PL011 driver code in pl011_console_match() only recognizes "mmio" or "mmio32". The result is that unless the user has enabled 'earlycon' there is no console output from the guest kernel. We therefore choose to report the access size as 32 bits; this works for NOVA and also for Linux. It is also what the UEFI firmware on a Raspberry Pi 4 reports, so we're in line with existing real-world practice. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1938 Signed-off-by: Udo Steinberg Reviewed-by: Peter Maydell [PMM: minor commit message tweaks; use 32 bit accesses] Signed-off-by: Peter Maydell --- hw/arm/virt-acpi-build.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 9ce136cd88..8bc35a483c 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -482,7 +482,7 @@ build_spcr(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) build_append_int_noprefix(table_data, 3, 1); /* ARM PL011 UART */ build_append_int_noprefix(table_data, 0, 3); /* Reserved */ /* Base Address */ - build_append_gas(table_data, AML_AS_SYSTEM_MEMORY, 8, 0, 1, + build_append_gas(table_data, AML_AS_SYSTEM_MEMORY, 32, 0, 3, vms->memmap[VIRT_UART].base); /* Interrupt Type */ build_append_int_noprefix(table_data, @@ -673,7 +673,7 @@ build_dbg2(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) build_append_int_noprefix(table_data, 34, 2); /* BaseAddressRegister[] */ - build_append_gas(table_data, AML_AS_SYSTEM_MEMORY, 8, 0, 1, + build_append_gas(table_data, AML_AS_SYSTEM_MEMORY, 32, 0, 3, vms->memmap[VIRT_UART].base); /* AddressSize[] */ From 806f71eecf643d029099e57fae2199f678845d98 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 6 Nov 2023 15:00:27 +0000 Subject: [PATCH 476/974] tests/qtest/bios-tables-test: Update virt SPCR and DBG2 golden references Update the virt SPCR and DBG2 golden reference files to have the fix for the description of the UART. Diffs from iasl: @@ -1,57 +1,57 @@ /* * Intel ACPI Component Architecture * AML/ASL+ Disassembler version 20200925 (64-bit version) * Copyright (c) 2000 - 2020 Intel Corporation * - * Disassembly of tests/data/acpi/virt/SPCR, Fri Nov 3 14:12:06 2023 + * Disassembly of /tmp/aml-E6YUD2, Fri Nov 3 14:12:06 2023 * * ACPI Data Table [SPCR] * * Format: [HexOffset DecimalOffset ByteLength] FieldName : FieldValue */ [000h 0000 4] Signature : "SPCR" [Serial Port Console Redirection table] [004h 0004 4] Table Length : 00000050 [008h 0008 1] Revision : 02 -[009h 0009 1] Checksum : CB +[009h 0009 1] Checksum : B1 [00Ah 0010 6] Oem ID : "BOCHS " [010h 0016 8] Oem Table ID : "BXPC " [018h 0024 4] Oem Revision : 00000001 [01Ch 0028 4] Asl Compiler ID : "BXPC" [020h 0032 4] Asl Compiler Revision : 00000001 [024h 0036 1] Interface Type : 03 [025h 0037 3] Reserved : 000000 [028h 0040 12] Serial Port Register : [Generic Address Structure] [028h 0040 1] Space ID : 00 [SystemMemory] -[029h 0041 1] Bit Width : 08 +[029h 0041 1] Bit Width : 20 [02Ah 0042 1] Bit Offset : 00 -[02Bh 0043 1] Encoded Access Width : 01 [Byte Access:8] +[02Bh 0043 1] Encoded Access Width : 03 [DWord Access:32] [02Ch 0044 8] Address : 0000000009000000 [034h 0052 1] Interrupt Type : 08 [035h 0053 1] PCAT-compatible IRQ : 00 [036h 0054 4] Interrupt : 00000021 [03Ah 0058 1] Baud Rate : 03 [03Bh 0059 1] Parity : 00 [03Ch 0060 1] Stop Bits : 01 [03Dh 0061 1] Flow Control : 02 [03Eh 0062 1] Terminal Type : 00 [04Ch 0076 1] Reserved : 00 [040h 0064 2] PCI Device ID : FFFF [042h 0066 2] PCI Vendor ID : FFFF [044h 0068 1] PCI Bus : 00 [045h 0069 1] PCI Device : 00 [046h 0070 1] PCI Function : 00 [047h 0071 4] PCI Flags : 00000000 [04Bh 0075 1] PCI Segment : 00 [04Ch 0076 4] Reserved : 00000000 Raw Table Data: Length 80 (0x50) - 0000: 53 50 43 52 50 00 00 00 02 CB 42 4F 43 48 53 20 // SPCRP.....BOCHS + 0000: 53 50 43 52 50 00 00 00 02 B1 42 4F 43 48 53 20 // SPCRP.....BOCHS 0010: 42 58 50 43 20 20 20 20 01 00 00 00 42 58 50 43 // BXPC ....BXPC - 0020: 01 00 00 00 03 00 00 00 00 08 00 01 00 00 00 09 // ................ + 0020: 01 00 00 00 03 00 00 00 00 20 00 03 00 00 00 09 // ......... ...... 0030: 00 00 00 00 08 00 21 00 00 00 03 00 01 02 00 00 // ......!......... 0040: FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 // ................ @@ -1,57 +1,57 @@ /* * Intel ACPI Component Architecture * AML/ASL+ Disassembler version 20200925 (64-bit version) * Copyright (c) 2000 - 2020 Intel Corporation * - * Disassembly of tests/data/acpi/virt/DBG2, Fri Nov 3 14:12:06 2023 + * Disassembly of /tmp/aml-V1YUD2, Fri Nov 3 14:12:06 2023 * * ACPI Data Table [DBG2] * * Format: [HexOffset DecimalOffset ByteLength] FieldName : FieldValue */ [000h 0000 4] Signature : "DBG2" [Debug Port table type 2] [004h 0004 4] Table Length : 00000057 [008h 0008 1] Revision : 00 -[009h 0009 1] Checksum : CF +[009h 0009 1] Checksum : B5 [00Ah 0010 6] Oem ID : "BOCHS " [010h 0016 8] Oem Table ID : "BXPC " [018h 0024 4] Oem Revision : 00000001 [01Ch 0028 4] Asl Compiler ID : "BXPC" [020h 0032 4] Asl Compiler Revision : 00000001 [024h 0036 4] Info Offset : 0000002C [028h 0040 4] Info Count : 00000001 [02Ch 0044 1] Revision : 00 [02Dh 0045 2] Length : 002B [02Fh 0047 1] Register Count : 01 [030h 0048 2] Namepath Length : 0005 [032h 0050 2] Namepath Offset : 0026 [034h 0052 2] OEM Data Length : 0000 [Optional field not present] [036h 0054 2] OEM Data Offset : 0000 [Optional field not present] [038h 0056 2] Port Type : 8000 [03Ah 0058 2] Port Subtype : 0003 [03Ch 0060 2] Reserved : 0000 [03Eh 0062 2] Base Address Offset : 0016 [040h 0064 2] Address Size Offset : 0022 [042h 0066 12] Base Address Register : [Generic Address Structure] [042h 0066 1] Space ID : 00 [SystemMemory] -[043h 0067 1] Bit Width : 08 +[043h 0067 1] Bit Width : 20 [044h 0068 1] Bit Offset : 00 -[045h 0069 1] Encoded Access Width : 01 [Byte Access:8] +[045h 0069 1] Encoded Access Width : 03 [DWord Access:32] [046h 0070 8] Address : 0000000009000000 [04Eh 0078 4] Address Size : 00001000 [052h 0082 5] Namepath : "COM0" Raw Table Data: Length 87 (0x57) - 0000: 44 42 47 32 57 00 00 00 00 CF 42 4F 43 48 53 20 // DBG2W.....BOCHS + 0000: 44 42 47 32 57 00 00 00 00 B5 42 4F 43 48 53 20 // DBG2W.....BOCHS 0010: 42 58 50 43 20 20 20 20 01 00 00 00 42 58 50 43 // BXPC ....BXPC 0020: 01 00 00 00 2C 00 00 00 01 00 00 00 00 2B 00 01 // ....,........+.. 0030: 05 00 26 00 00 00 00 00 00 80 03 00 00 00 16 00 // ..&............. - 0040: 22 00 00 08 00 01 00 00 00 09 00 00 00 00 00 10 // "............... + 0040: 22 00 00 20 00 03 00 00 00 09 00 00 00 00 00 10 // ".. ............ 0050: 00 00 43 4F 4D 30 00 // ..COM0. Signed-off-by: Peter Maydell --- tests/data/acpi/virt/DBG2 | Bin 87 -> 87 bytes tests/data/acpi/virt/SPCR | Bin 80 -> 80 bytes tests/qtest/bios-tables-test-allowed-diff.h | 2 -- 3 files changed, 2 deletions(-) diff --git a/tests/data/acpi/virt/DBG2 b/tests/data/acpi/virt/DBG2 index 86e6314f7b0235ef8ed3e0221e09f996c41f5e98..0a05e1a47f9c303c6a6c9ca8414c62ec4ac90f98 100644 GIT binary patch delta 37 ncmWF!=W=m!HwtF}f~^y|EJYL;n1M`A5T8MSfx+3|*MI>4b2kL{ delta 37 ncmWF!=W=m!HwtF}g7Xu(EJZjN7=cVq5T8MSfx+3|*MI>4bG-!j diff --git a/tests/data/acpi/virt/SPCR b/tests/data/acpi/virt/SPCR index 24e0a579e7d73f432a614380e29aa95113344186..cf0f2b75226515097c08d2e2016a83a4f08812ba 100644 GIT binary patch delta 23 ecmWFt;0g|K4hmpkU|`xfkxQOgfq{9VjtT%gOa!L@ delta 23 ecmWFt;0g|K4hmpkU|>2ukxQPLgMo3PjtT%g(gddf diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index 6673e2c4c1..dfb8523c8b 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1,3 +1 @@ /* List of comma-separated changed AML files to ignore */ -"tests/data/acpi/virt/SPCR", -"tests/data/acpi/virt/DBG2", From 212c5fe1914a192b01f337b7392fca75a7ab4071 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Mon, 6 Nov 2023 15:00:27 +0000 Subject: [PATCH 477/974] hw/i386/intel_iommu: vtd_slpte_nonzero_rsvd(): assert no overflow We support only 3- and 4-level page-tables, which is firstly checked in vtd_decide_config(), then setup in vtd_init(). Than level fields are checked by vtd_is_level_supported(). So here we can't have level out from 1..4 inclusive range. Let's assert it. That also explains Coverity that we are not going to overflow the array. CID: 1487158, 1487186 Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Peter Maydell Reviewed-by: Maksim Davydov Message-id: 20231017125941.810461-2-vsementsov@yandex-team.ru Signed-off-by: Peter Maydell --- hw/i386/intel_iommu.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 1c6c18622f..1a44ef696c 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -1045,18 +1045,35 @@ static dma_addr_t vtd_get_iova_pgtbl_base(IntelIOMMUState *s, * Rsvd field masks for spte: * vtd_spte_rsvd 4k pages * vtd_spte_rsvd_large large pages + * + * We support only 3-level and 4-level page tables (see vtd_init() which + * sets only VTD_CAP_SAGAW_39bit and maybe VTD_CAP_SAGAW_48bit bits in s->cap). */ -static uint64_t vtd_spte_rsvd[5]; -static uint64_t vtd_spte_rsvd_large[5]; +#define VTD_SPTE_RSVD_LEN 5 +static uint64_t vtd_spte_rsvd[VTD_SPTE_RSVD_LEN]; +static uint64_t vtd_spte_rsvd_large[VTD_SPTE_RSVD_LEN]; static bool vtd_slpte_nonzero_rsvd(uint64_t slpte, uint32_t level) { - uint64_t rsvd_mask = vtd_spte_rsvd[level]; + uint64_t rsvd_mask; + + /* + * We should have caught a guest-mis-programmed level earlier, + * via vtd_is_level_supported. + */ + assert(level < VTD_SPTE_RSVD_LEN); + /* + * Zero level doesn't exist. The smallest level is VTD_SL_PT_LEVEL=1 and + * checked by vtd_is_last_slpte(). + */ + assert(level); if ((level == VTD_SL_PD_LEVEL || level == VTD_SL_PDP_LEVEL) && (slpte & VTD_SL_PT_PAGE_SIZE_MASK)) { /* large page */ rsvd_mask = vtd_spte_rsvd_large[level]; + } else { + rsvd_mask = vtd_spte_rsvd[level]; } return slpte & rsvd_mask; From 2e12dd405c6607b4f4566d4a93b79422213ba6a3 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Mon, 6 Nov 2023 15:00:27 +0000 Subject: [PATCH 478/974] util/filemonitor-inotify: qemu_file_monitor_watch(): assert no overflow Prefer clear assertions instead of [im]possible array overflow. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Peter Maydell Reviewed-by: Maksim Davydov Message-id: 20231017125941.810461-3-vsementsov@yandex-team.ru Signed-off-by: Peter Maydell --- util/filemonitor-inotify.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/util/filemonitor-inotify.c b/util/filemonitor-inotify.c index 2c45f7f176..2121111f38 100644 --- a/util/filemonitor-inotify.c +++ b/util/filemonitor-inotify.c @@ -81,16 +81,25 @@ static void qemu_file_monitor_watch(void *arg) /* Loop over all events in the buffer */ while (used < len) { - struct inotify_event *ev = - (struct inotify_event *)(buf + used); - const char *name = ev->len ? ev->name : ""; - QFileMonitorDir *dir = g_hash_table_lookup(mon->idmap, - GINT_TO_POINTER(ev->wd)); - uint32_t iev = ev->mask & - (IN_CREATE | IN_MODIFY | IN_DELETE | IN_IGNORED | - IN_MOVED_TO | IN_MOVED_FROM | IN_ATTRIB); + const char *name; + QFileMonitorDir *dir; + uint32_t iev; int qev; gsize i; + struct inotify_event *ev = (struct inotify_event *)(buf + used); + + /* + * We trust the kenel to provide valid buffer with complete event + * records. + */ + assert(len - used >= sizeof(struct inotify_event)); + assert(len - used - sizeof(struct inotify_event) >= ev->len); + + name = ev->len ? ev->name : ""; + dir = g_hash_table_lookup(mon->idmap, GINT_TO_POINTER(ev->wd)); + iev = ev->mask & + (IN_CREATE | IN_MODIFY | IN_DELETE | IN_IGNORED | + IN_MOVED_TO | IN_MOVED_FROM | IN_ATTRIB); used += sizeof(struct inotify_event) + ev->len; From 394bca2fa443cd1c1c3dad931fcce4fa96f88941 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Mon, 6 Nov 2023 15:00:27 +0000 Subject: [PATCH 479/974] mc146818rtc: rtc_set_time(): initialize tm to zeroes set_time() function doesn't set all the fields, so it's better to initialize tm structure. And Coverity will be happier about it. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Peter Maydell Reviewed-by: Maksim Davydov Message-id: 20231017125941.810461-4-vsementsov@yandex-team.ru Signed-off-by: Peter Maydell --- hw/rtc/mc146818rtc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c index c27c362db9..2d391a8396 100644 --- a/hw/rtc/mc146818rtc.c +++ b/hw/rtc/mc146818rtc.c @@ -599,7 +599,7 @@ static void rtc_get_time(MC146818RtcState *s, struct tm *tm) static void rtc_set_time(MC146818RtcState *s) { - struct tm tm; + struct tm tm = {}; g_autofree const char *qom_path = object_get_canonical_path(OBJECT(s)); rtc_get_time(s, &tm); From cc8fb0c3ae3c950eb40e969607e17ff16a7519ac Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Mon, 6 Nov 2023 15:00:28 +0000 Subject: [PATCH 480/974] block/nvme: nvme_process_completion() fix bound for cid NVMeQueuePair::reqs has length NVME_NUM_REQS, which less than NVME_QUEUE_SIZE by 1. Fixes: 1086e95da17050 ("block/nvme: switch to a NVMeRequest freelist") Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Stefan Hajnoczi Reviewed-by: Maksim Davydov Message-id: 20231017125941.810461-5-vsementsov@yandex-team.ru Signed-off-by: Peter Maydell --- block/nvme.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/block/nvme.c b/block/nvme.c index 96b3f8f2fa..0a0a0a6b36 100644 --- a/block/nvme.c +++ b/block/nvme.c @@ -417,9 +417,10 @@ static bool nvme_process_completion(NVMeQueuePair *q) q->cq_phase = !q->cq_phase; } cid = le16_to_cpu(c->cid); - if (cid == 0 || cid > NVME_QUEUE_SIZE) { - warn_report("NVMe: Unexpected CID in completion queue: %"PRIu32", " - "queue size: %u", cid, NVME_QUEUE_SIZE); + if (cid == 0 || cid > NVME_NUM_REQS) { + warn_report("NVMe: Unexpected CID in completion queue: %" PRIu32 + ", should be within: 1..%u inclusively", cid, + NVME_NUM_REQS); continue; } trace_nvme_complete_command(s, q->index, cid); From 59a3aff685fdb930244c7aa439e121b60e50f266 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Mon, 6 Nov 2023 15:00:28 +0000 Subject: [PATCH 481/974] hw/core/loader: gunzip(): initialize z_stream Coverity signals that variable as being used uninitialized. And really, when work with external APIs that's better to zero out the structure, where we set some fields by hand. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Peter Maydell Reviewed-by: Maksim Davydov Message-id: 20231017125941.810461-6-vsementsov@yandex-team.ru Signed-off-by: Peter Maydell --- hw/core/loader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/core/loader.c b/hw/core/loader.c index 4dd5a71fb7..b7bb44b7f7 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -558,7 +558,7 @@ static void zfree(void *x, void *addr) ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src, size_t srclen) { - z_stream s; + z_stream s = {}; ssize_t dstbytes; int r, i, flags; From 35bafa95da671f5a902e87fcc301f76f82cd0831 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Mon, 6 Nov 2023 15:00:28 +0000 Subject: [PATCH 482/974] io/channel-socket: qio_channel_socket_flush(): improve msg validation For SO_EE_ORIGIN_ZEROCOPY the 32-bit notification range is encoded as [ee_info, ee_data] inclusively, so ee_info should be less or equal to ee_data. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Maksim Davydov Message-id: 20231017125941.810461-7-vsementsov@yandex-team.ru Signed-off-by: Peter Maydell --- io/channel-socket.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/io/channel-socket.c b/io/channel-socket.c index 02ffb51e99..3a899b0608 100644 --- a/io/channel-socket.c +++ b/io/channel-socket.c @@ -782,6 +782,11 @@ static int qio_channel_socket_flush(QIOChannel *ioc, "Error not from zero copy"); return -1; } + if (serr->ee_data < serr->ee_info) { + error_setg_errno(errp, serr->ee_origin, + "Wrong notification bounds"); + return -1; + } /* No errors, count successfully finished sendmsg()*/ sioc->zero_copy_sent += serr->ee_data - serr->ee_info + 1; From 13edcf591e74fd8a0e69f01b8b09f53397562103 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 6 Nov 2023 15:00:29 +0000 Subject: [PATCH 483/974] hw/arm/vexpress-a9: Remove useless mapping of RAM at address 0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On the vexpress-a9 board we try to map both RAM and flash to address 0, as seen in "info mtree": address-space: memory 0000000000000000-ffffffffffffffff (prio 0, i/o): system 0000000000000000-0000000003ffffff (prio 0, romd): alias vexpress.flashalias @vexpress.flash0 0000000000000000-0000000003ffffff 0000000000000000-0000000003ffffff (prio 0, ram): alias vexpress.lowmem @vexpress.highmem 0000000000000000-0000000003ffffff 0000000010000000-0000000010000fff (prio 0, i/o): arm-sysctl 0000000010004000-0000000010004fff (prio 0, i/o): pl041 (etc) The flash "wins" and the RAM mapping is useless (but also harmless). This happened as a result of commit 6ec1588e in 2014, which changed "we always map the RAM to the low addresses for vexpress-a9" to "we always map flash in the low addresses", but forgot to stop mapping the RAM. In real hardware, this low part of memory is remappable, both at runtime by the guest writing to a control register, and configurably as to what you get out of reset -- you can have the first flash device, or the second, or the DDR2 RAM, or the external AXI bus (which for QEMU means "nothing there"). In an ideal world we would support that remapping both at runtime and via a machine property to select the out-of-reset behaviour. Pending anybody caring enough to implement the full remapping behaviour: * remove the useless mapped-but-inaccessible lowram MR * document that QEMU doesn't support remapping of low memory Fixes: 6ec1588e ("hw/arm/vexpress: Alias NOR flash at 0 for vexpress-a9") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1761 Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20231103185602.875849-1-peter.maydell@linaro.org --- docs/system/arm/vexpress.rst | 3 +++ hw/arm/vexpress.c | 14 +++----------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/docs/system/arm/vexpress.rst b/docs/system/arm/vexpress.rst index 3e3839e923..38f29c73e7 100644 --- a/docs/system/arm/vexpress.rst +++ b/docs/system/arm/vexpress.rst @@ -58,6 +58,9 @@ Other differences between the hardware and the QEMU model: ``vexpress-a15``, and have IRQs from 40 upwards. If a dtb is provided on the command line then QEMU will edit it to include suitable entries describing these transports for the guest. +- QEMU does not currently support either dynamic or static remapping + of the area of memory at address 0: it is always mapped to alias + the first flash bank Booting a Linux kernel ---------------------- diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index 8ff37f52ca..c08ea34e92 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -177,7 +177,6 @@ struct VexpressMachineState { MemoryRegion vram; MemoryRegion sram; MemoryRegion flashalias; - MemoryRegion lowram; MemoryRegion a15sram; bool secure; bool virt; @@ -276,7 +275,6 @@ static void a9_daughterboard_init(VexpressMachineState *vms, { MachineState *machine = MACHINE(vms); MemoryRegion *sysmem = get_system_memory(); - ram_addr_t low_ram_size; if (ram_size > 0x40000000) { /* 1GB is the maximum the address space permits */ @@ -284,17 +282,11 @@ static void a9_daughterboard_init(VexpressMachineState *vms, exit(1); } - low_ram_size = ram_size; - if (low_ram_size > 0x4000000) { - low_ram_size = 0x4000000; - } - /* RAM is from 0x60000000 upwards. The bottom 64MB of the + /* + * RAM is from 0x60000000 upwards. The bottom 64MB of the * address space should in theory be remappable to various - * things including ROM or RAM; we always map the RAM there. + * things including ROM or RAM; we always map the flash there. */ - memory_region_init_alias(&vms->lowram, NULL, "vexpress.lowmem", - machine->ram, 0, low_ram_size); - memory_region_add_subregion(sysmem, 0x0, &vms->lowram); memory_region_add_subregion(sysmem, 0x60000000, machine->ram); /* 0x1e000000 A9MPCore (SCU) private memory region */ From 5722fc471296d5f042df4b005a851cc8008df0c9 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 6 Nov 2023 15:00:29 +0000 Subject: [PATCH 484/974] target/arm: Fix A64 LDRA immediate decode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit be23a049 in the conversion to decodetree we broke the decoding of the immediate value in the LDRA instruction. This should be a 10 bit signed value that is scaled by 8, but in the conversion we incorrectly ended up scaling it only by 2. Fix the scaling factor. Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1970 Fixes: be23a049 ("target/arm: Convert load (pointer auth) insns to decodetree") Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20231106113445.1163063-1-peter.maydell@linaro.org --- target/arm/tcg/a64.decode | 2 +- target/arm/tcg/translate.h | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 0cf1147074..8a20dce3c8 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -462,7 +462,7 @@ LDAPR sz:2 111 0 00 1 0 1 11111 1100 00 rn:5 rt:5 # Load/store register (pointer authentication) # LDRA immediate is 10 bits signed and scaled, but the bits aren't all contiguous -%ldra_imm 22:s1 12:9 !function=times_2 +%ldra_imm 22:s1 12:9 !function=times_8 LDRA 11 111 0 00 m:1 . 1 ......... w:1 1 rn:5 rt:5 imm=%ldra_imm diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index 9efe00cf6c..3c3bb3431a 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -205,6 +205,11 @@ static inline int times_4(DisasContext *s, int x) return x * 4; } +static inline int times_8(DisasContext *s, int x) +{ + return x * 8; +} + static inline int times_2_plus_1(DisasContext *s, int x) { return x * 2 + 1; From b2b109041ecd1095384f5be5bb9badd13c1cf286 Mon Sep 17 00:00:00 2001 From: Jean-Louis Dupond Date: Tue, 3 Oct 2023 14:52:37 +0200 Subject: [PATCH 485/974] qcow2: keep reference on zeroize with discard-no-unref enabled When the discard-no-unref flag is enabled, we keep the reference for normal discard requests. But when a discard is executed on a snapshot/qcow2 image with backing, the discards are saved as zero clusters in the snapshot image. When committing the snapshot to the backing file, not discard_in_l2_slice is called but zero_in_l2_slice. Which did not had any logic to keep the reference when discard-no-unref is enabled. Therefor we add logic in the zero_in_l2_slice call to keep the reference on commit. Fixes: https://gitlab.com/qemu-project/qemu/-/issues/1621 Signed-off-by: Jean-Louis Dupond Message-Id: <20231003125236.216473-2-jean-louis@dupond.be> [hreitz: Made the documentation change more verbose, as discussed on-list] Signed-off-by: Hanna Czenczek --- block/qcow2-cluster.c | 22 ++++++++++++++++++---- qapi/block-core.json | 24 ++++++++++++++---------- qemu-options.hx | 10 +++++++--- 3 files changed, 39 insertions(+), 17 deletions(-) diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 904f00d1b3..5af439bd11 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -1983,7 +1983,7 @@ discard_in_l2_slice(BlockDriverState *bs, uint64_t offset, uint64_t nb_clusters, /* If we keep the reference, pass on the discard still */ bdrv_pdiscard(s->data_file, old_l2_entry & L2E_OFFSET_MASK, s->cluster_size); - } + } } qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); @@ -2061,9 +2061,15 @@ zero_in_l2_slice(BlockDriverState *bs, uint64_t offset, QCow2ClusterType type = qcow2_get_cluster_type(bs, old_l2_entry); bool unmap = (type == QCOW2_CLUSTER_COMPRESSED) || ((flags & BDRV_REQ_MAY_UNMAP) && qcow2_cluster_is_allocated(type)); - uint64_t new_l2_entry = unmap ? 0 : old_l2_entry; + bool keep_reference = + (s->discard_no_unref && type != QCOW2_CLUSTER_COMPRESSED); + uint64_t new_l2_entry = old_l2_entry; uint64_t new_l2_bitmap = old_l2_bitmap; + if (unmap && !keep_reference) { + new_l2_entry = 0; + } + if (has_subclusters(s)) { new_l2_bitmap = QCOW_L2_BITMAP_ALL_ZEROES; } else { @@ -2081,9 +2087,17 @@ zero_in_l2_slice(BlockDriverState *bs, uint64_t offset, set_l2_bitmap(s, l2_slice, l2_index + i, new_l2_bitmap); } - /* Then decrease the refcount */ if (unmap) { - qcow2_free_any_cluster(bs, old_l2_entry, QCOW2_DISCARD_REQUEST); + if (!keep_reference) { + /* Then decrease the refcount */ + qcow2_free_any_cluster(bs, old_l2_entry, QCOW2_DISCARD_REQUEST); + } else if (s->discard_passthrough[QCOW2_DISCARD_REQUEST] && + (type == QCOW2_CLUSTER_NORMAL || + type == QCOW2_CLUSTER_ZERO_ALLOC)) { + /* If we keep the reference, pass on the discard still */ + bdrv_pdiscard(s->data_file, old_l2_entry & L2E_OFFSET_MASK, + s->cluster_size); + } } } diff --git a/qapi/block-core.json b/qapi/block-core.json index 99961256f2..ca390c5700 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -3528,16 +3528,20 @@ # @pass-discard-other: whether discard requests for the data source # should be issued on other occasions where a cluster gets freed # -# @discard-no-unref: when enabled, discards from the guest will not -# cause cluster allocations to be relinquished. This prevents -# qcow2 fragmentation that would be caused by such discards. -# Besides potential performance degradation, such fragmentation -# can lead to increased allocation of clusters past the end of the -# image file, resulting in image files whose file length can grow -# much larger than their guest disk size would suggest. If image -# file length is of concern (e.g. when storing qcow2 images -# directly on block devices), you should consider enabling this -# option. (since 8.1) +# @discard-no-unref: when enabled, data clusters will remain +# preallocated when they are no longer used, e.g. because they are +# discarded or converted to zero clusters. As usual, whether the +# old data is discarded or kept on the protocol level (i.e. in the +# image file) depends on the setting of the pass-discard-request +# option. Keeping the clusters preallocated prevents qcow2 +# fragmentation that would otherwise be caused by freeing and +# re-allocating them later. Besides potential performance +# degradation, such fragmentation can lead to increased allocation +# of clusters past the end of the image file, resulting in image +# files whose file length can grow much larger than their guest disk +# size would suggest. If image file length is of concern (e.g. when +# storing qcow2 images directly on block devices), you should +# consider enabling this option. (since 8.1) # # @overlap-check: which overlap checks to perform for writes to the # image, defaults to 'cached' (since 2.2) diff --git a/qemu-options.hx b/qemu-options.hx index e26230bac5..7809036d8c 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1457,9 +1457,13 @@ SRST (on/off; default: off) ``discard-no-unref`` - When enabled, discards from the guest will not cause cluster - allocations to be relinquished. This prevents qcow2 fragmentation - that would be caused by such discards. Besides potential + When enabled, data clusters will remain preallocated when they are + no longer used, e.g. because they are discarded or converted to + zero clusters. As usual, whether the old data is discarded or kept + on the protocol level (i.e. in the image file) depends on the + setting of the pass-discard-request option. Keeping the clusters + preallocated prevents qcow2 fragmentation that would otherwise be + caused by freeing and re-allocating them later. Besides potential performance degradation, such fragmentation can lead to increased allocation of clusters past the end of the image file, resulting in image files whose file length can grow much larger From 10b9e0802a074c991e1ce485631d75641d0b0f9e Mon Sep 17 00:00:00 2001 From: Sam Li Date: Fri, 25 Aug 2023 12:05:56 +0800 Subject: [PATCH 486/974] block/file-posix: fix update_zones_wp() caller When the zoned request fail, it needs to update only the wp of the target zones for not disrupting the in-flight writes on these other zones. The wp is updated successfully after the request completes. Fixed the callers with right offset and nr_zones. Signed-off-by: Sam Li Message-Id: <20230825040556.4217-1-faithilikerun@gmail.com> Reviewed-by: Stefan Hajnoczi [hreitz: Rebased and fixed comment spelling] Signed-off-by: Hanna Czenczek --- block/file-posix.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/block/file-posix.c b/block/file-posix.c index 50e2b20d5c..3c0ce9c258 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -2523,7 +2523,10 @@ out: } } } else { - update_zones_wp(bs, s->fd, 0, 1); + /* + * write and append write are not allowed to cross zone boundaries + */ + update_zones_wp(bs, s->fd, offset, 1); } qemu_co_mutex_unlock(&wps->colock); @@ -3470,7 +3473,7 @@ static int coroutine_fn raw_co_zone_mgmt(BlockDriverState *bs, BlockZoneOp op, len >> BDRV_SECTOR_BITS); ret = raw_thread_pool_submit(handle_aiocb_zone_mgmt, &acb); if (ret != 0) { - update_zones_wp(bs, s->fd, offset, i); + update_zones_wp(bs, s->fd, offset, nrz); error_report("ioctl %s failed %d", op_name, ret); return ret; } From ad4feaca61d76fecad784e6d5e7bae40d0411c46 Mon Sep 17 00:00:00 2001 From: Naohiro Aota Date: Mon, 30 Oct 2023 16:38:53 +0900 Subject: [PATCH 487/974] file-posix: fix over-writing of returning zone_append offset raw_co_zone_append() sets "s->offset" where "BDRVRawState *s". This pointer is used later at raw_co_prw() to save the block address where the data is written. When multiple IOs are on-going at the same time, a later IO's raw_co_zone_append() call over-writes a former IO's offset address before raw_co_prw() completes. As a result, the former zone append IO returns the initial value (= the start address of the writing zone), instead of the proper address. Fix the issue by passing the offset pointer to raw_co_prw() instead of passing it through s->offset. Also, remove "offset" from BDRVRawState as there is no usage anymore. Fixes: 4751d09adcc3 ("block: introduce zone append write for zoned devices") Signed-off-by: Naohiro Aota Message-Id: <20231030073853.2601162-1-naohiro.aota@wdc.com> Reviewed-by: Sam Li Reviewed-by: Stefan Hajnoczi Signed-off-by: Hanna Czenczek --- block/file-posix.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/block/file-posix.c b/block/file-posix.c index 3c0ce9c258..b862406c71 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -160,7 +160,6 @@ typedef struct BDRVRawState { bool has_write_zeroes:1; bool use_linux_aio:1; bool use_linux_io_uring:1; - int64_t *offset; /* offset of zone append operation */ int page_cache_inconsistent; /* errno from fdatasync failure */ bool has_fallocate; bool needs_alignment; @@ -2445,12 +2444,13 @@ static bool bdrv_qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov) return true; } -static int coroutine_fn raw_co_prw(BlockDriverState *bs, uint64_t offset, +static int coroutine_fn raw_co_prw(BlockDriverState *bs, int64_t *offset_ptr, uint64_t bytes, QEMUIOVector *qiov, int type) { BDRVRawState *s = bs->opaque; RawPosixAIOData acb; int ret; + uint64_t offset = *offset_ptr; if (fd_open(bs) < 0) return -EIO; @@ -2513,8 +2513,8 @@ out: uint64_t *wp = &wps->wp[offset / bs->bl.zone_size]; if (!BDRV_ZT_IS_CONV(*wp)) { if (type & QEMU_AIO_ZONE_APPEND) { - *s->offset = *wp; - trace_zbd_zone_append_complete(bs, *s->offset + *offset_ptr = *wp; + trace_zbd_zone_append_complete(bs, *offset_ptr >> BDRV_SECTOR_BITS); } /* Advance the wp if needed */ @@ -2539,14 +2539,14 @@ static int coroutine_fn raw_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { - return raw_co_prw(bs, offset, bytes, qiov, QEMU_AIO_READ); + return raw_co_prw(bs, &offset, bytes, qiov, QEMU_AIO_READ); } static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { - return raw_co_prw(bs, offset, bytes, qiov, QEMU_AIO_WRITE); + return raw_co_prw(bs, &offset, bytes, qiov, QEMU_AIO_WRITE); } static int coroutine_fn raw_co_flush_to_disk(BlockDriverState *bs) @@ -3509,8 +3509,6 @@ static int coroutine_fn raw_co_zone_append(BlockDriverState *bs, int64_t zone_size_mask = bs->bl.zone_size - 1; int64_t iov_len = 0; int64_t len = 0; - BDRVRawState *s = bs->opaque; - s->offset = offset; if (*offset & zone_size_mask) { error_report("sector offset %" PRId64 " is not aligned to zone size " @@ -3531,7 +3529,7 @@ static int coroutine_fn raw_co_zone_append(BlockDriverState *bs, } trace_zbd_zone_append(bs, *offset >> BDRV_SECTOR_BITS); - return raw_co_prw(bs, *offset, len, qiov, QEMU_AIO_ZONE_APPEND); + return raw_co_prw(bs, offset, len, qiov, QEMU_AIO_ZONE_APPEND); } #endif From 24a4d59aa7fdc337eef1c2b589478ea998e54373 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 3 Jul 2023 15:22:38 +0200 Subject: [PATCH 488/974] accel/tcg: Move HMP info jit and info opcount code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move all of it into accel/tcg/monitor.c. This puts everything about tcg that is only used by the monitor in the same place. Tested-by: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alex Bennée Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 15 ---- accel/tcg/internal-common.h | 2 - accel/tcg/monitor.c | 154 ++++++++++++++++++++++++++++++++++++ accel/tcg/translate-all.c | 127 ----------------------------- include/exec/cputlb.h | 1 - include/tcg/tcg.h | 3 - tcg/tcg.c | 10 --- 7 files changed, 154 insertions(+), 158 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index b8c5e345b8..13986820fe 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -321,21 +321,6 @@ static void flush_all_helper(CPUState *src, run_on_cpu_func fn, } } -void tlb_flush_counts(size_t *pfull, size_t *ppart, size_t *pelide) -{ - CPUState *cpu; - size_t full = 0, part = 0, elide = 0; - - CPU_FOREACH(cpu) { - full += qatomic_read(&cpu->neg.tlb.c.full_flush_count); - part += qatomic_read(&cpu->neg.tlb.c.part_flush_count); - elide += qatomic_read(&cpu->neg.tlb.c.elide_flush_count); - } - *pfull = full; - *ppart = part; - *pelide = elide; -} - static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data) { uint16_t asked = data.host_int; diff --git a/accel/tcg/internal-common.h b/accel/tcg/internal-common.h index 3b2277e6e9..edefd0dcb7 100644 --- a/accel/tcg/internal-common.h +++ b/accel/tcg/internal-common.h @@ -14,8 +14,6 @@ extern int64_t max_delay; extern int64_t max_advance; -void dump_exec_info(GString *buf); - /* * Return true if CS is not running in parallel with other cpus, either * because there are no other cpus or we are within an exclusive context. diff --git a/accel/tcg/monitor.c b/accel/tcg/monitor.c index caf1189e0b..093efe9714 100644 --- a/accel/tcg/monitor.c +++ b/accel/tcg/monitor.c @@ -8,6 +8,7 @@ #include "qemu/osdep.h" #include "qemu/accel.h" +#include "qemu/qht.h" #include "qapi/error.h" #include "qapi/type-helpers.h" #include "qapi/qapi-commands-machine.h" @@ -17,6 +18,7 @@ #include "sysemu/tcg.h" #include "tcg/tcg.h" #include "internal-common.h" +#include "tb-context.h" static void dump_drift_info(GString *buf) @@ -50,6 +52,153 @@ static void dump_accel_info(GString *buf) one_insn_per_tb ? "on" : "off"); } +static void print_qht_statistics(struct qht_stats hst, GString *buf) +{ + uint32_t hgram_opts; + size_t hgram_bins; + char *hgram; + + if (!hst.head_buckets) { + return; + } + g_string_append_printf(buf, "TB hash buckets %zu/%zu " + "(%0.2f%% head buckets used)\n", + hst.used_head_buckets, hst.head_buckets, + (double)hst.used_head_buckets / + hst.head_buckets * 100); + + hgram_opts = QDIST_PR_BORDER | QDIST_PR_LABELS; + hgram_opts |= QDIST_PR_100X | QDIST_PR_PERCENT; + if (qdist_xmax(&hst.occupancy) - qdist_xmin(&hst.occupancy) == 1) { + hgram_opts |= QDIST_PR_NODECIMAL; + } + hgram = qdist_pr(&hst.occupancy, 10, hgram_opts); + g_string_append_printf(buf, "TB hash occupancy %0.2f%% avg chain occ. " + "Histogram: %s\n", + qdist_avg(&hst.occupancy) * 100, hgram); + g_free(hgram); + + hgram_opts = QDIST_PR_BORDER | QDIST_PR_LABELS; + hgram_bins = qdist_xmax(&hst.chain) - qdist_xmin(&hst.chain); + if (hgram_bins > 10) { + hgram_bins = 10; + } else { + hgram_bins = 0; + hgram_opts |= QDIST_PR_NODECIMAL | QDIST_PR_NOBINRANGE; + } + hgram = qdist_pr(&hst.chain, hgram_bins, hgram_opts); + g_string_append_printf(buf, "TB hash avg chain %0.3f buckets. " + "Histogram: %s\n", + qdist_avg(&hst.chain), hgram); + g_free(hgram); +} + +struct tb_tree_stats { + size_t nb_tbs; + size_t host_size; + size_t target_size; + size_t max_target_size; + size_t direct_jmp_count; + size_t direct_jmp2_count; + size_t cross_page; +}; + +static gboolean tb_tree_stats_iter(gpointer key, gpointer value, gpointer data) +{ + const TranslationBlock *tb = value; + struct tb_tree_stats *tst = data; + + tst->nb_tbs++; + tst->host_size += tb->tc.size; + tst->target_size += tb->size; + if (tb->size > tst->max_target_size) { + tst->max_target_size = tb->size; + } + if (tb->page_addr[1] != -1) { + tst->cross_page++; + } + if (tb->jmp_reset_offset[0] != TB_JMP_OFFSET_INVALID) { + tst->direct_jmp_count++; + if (tb->jmp_reset_offset[1] != TB_JMP_OFFSET_INVALID) { + tst->direct_jmp2_count++; + } + } + return false; +} + +static void tlb_flush_counts(size_t *pfull, size_t *ppart, size_t *pelide) +{ + CPUState *cpu; + size_t full = 0, part = 0, elide = 0; + + CPU_FOREACH(cpu) { + full += qatomic_read(&cpu->neg.tlb.c.full_flush_count); + part += qatomic_read(&cpu->neg.tlb.c.part_flush_count); + elide += qatomic_read(&cpu->neg.tlb.c.elide_flush_count); + } + *pfull = full; + *ppart = part; + *pelide = elide; +} + +static void tcg_dump_info(GString *buf) +{ + g_string_append_printf(buf, "[TCG profiler not compiled]\n"); +} + +static void dump_exec_info(GString *buf) +{ + struct tb_tree_stats tst = {}; + struct qht_stats hst; + size_t nb_tbs, flush_full, flush_part, flush_elide; + + tcg_tb_foreach(tb_tree_stats_iter, &tst); + nb_tbs = tst.nb_tbs; + /* XXX: avoid using doubles ? */ + g_string_append_printf(buf, "Translation buffer state:\n"); + /* + * Report total code size including the padding and TB structs; + * otherwise users might think "-accel tcg,tb-size" is not honoured. + * For avg host size we use the precise numbers from tb_tree_stats though. + */ + g_string_append_printf(buf, "gen code size %zu/%zu\n", + tcg_code_size(), tcg_code_capacity()); + g_string_append_printf(buf, "TB count %zu\n", nb_tbs); + g_string_append_printf(buf, "TB avg target size %zu max=%zu bytes\n", + nb_tbs ? tst.target_size / nb_tbs : 0, + tst.max_target_size); + g_string_append_printf(buf, "TB avg host size %zu bytes " + "(expansion ratio: %0.1f)\n", + nb_tbs ? tst.host_size / nb_tbs : 0, + tst.target_size ? + (double)tst.host_size / tst.target_size : 0); + g_string_append_printf(buf, "cross page TB count %zu (%zu%%)\n", + tst.cross_page, + nb_tbs ? (tst.cross_page * 100) / nb_tbs : 0); + g_string_append_printf(buf, "direct jump count %zu (%zu%%) " + "(2 jumps=%zu %zu%%)\n", + tst.direct_jmp_count, + nb_tbs ? (tst.direct_jmp_count * 100) / nb_tbs : 0, + tst.direct_jmp2_count, + nb_tbs ? (tst.direct_jmp2_count * 100) / nb_tbs : 0); + + qht_statistics_init(&tb_ctx.htable, &hst); + print_qht_statistics(hst, buf); + qht_statistics_destroy(&hst); + + g_string_append_printf(buf, "\nStatistics:\n"); + g_string_append_printf(buf, "TB flush count %u\n", + qatomic_read(&tb_ctx.tb_flush_count)); + g_string_append_printf(buf, "TB invalidate count %u\n", + qatomic_read(&tb_ctx.tb_phys_invalidate_count)); + + tlb_flush_counts(&flush_full, &flush_part, &flush_elide); + g_string_append_printf(buf, "TLB full flushes %zu\n", flush_full); + g_string_append_printf(buf, "TLB partial flushes %zu\n", flush_part); + g_string_append_printf(buf, "TLB elided flushes %zu\n", flush_elide); + tcg_dump_info(buf); +} + HumanReadableText *qmp_x_query_jit(Error **errp) { g_autoptr(GString) buf = g_string_new(""); @@ -66,6 +215,11 @@ HumanReadableText *qmp_x_query_jit(Error **errp) return human_readable_text_from_str(buf); } +static void tcg_dump_op_count(GString *buf) +{ + g_string_append_printf(buf, "[TCG profiler not compiled]\n"); +} + HumanReadableText *qmp_x_query_opcount(Error **errp) { g_autoptr(GString) buf = g_string_new(""); diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 8cb6ad3511..e579b0891d 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -645,133 +645,6 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr) cpu_loop_exit_noexc(cpu); } -static void print_qht_statistics(struct qht_stats hst, GString *buf) -{ - uint32_t hgram_opts; - size_t hgram_bins; - char *hgram; - - if (!hst.head_buckets) { - return; - } - g_string_append_printf(buf, "TB hash buckets %zu/%zu " - "(%0.2f%% head buckets used)\n", - hst.used_head_buckets, hst.head_buckets, - (double)hst.used_head_buckets / - hst.head_buckets * 100); - - hgram_opts = QDIST_PR_BORDER | QDIST_PR_LABELS; - hgram_opts |= QDIST_PR_100X | QDIST_PR_PERCENT; - if (qdist_xmax(&hst.occupancy) - qdist_xmin(&hst.occupancy) == 1) { - hgram_opts |= QDIST_PR_NODECIMAL; - } - hgram = qdist_pr(&hst.occupancy, 10, hgram_opts); - g_string_append_printf(buf, "TB hash occupancy %0.2f%% avg chain occ. " - "Histogram: %s\n", - qdist_avg(&hst.occupancy) * 100, hgram); - g_free(hgram); - - hgram_opts = QDIST_PR_BORDER | QDIST_PR_LABELS; - hgram_bins = qdist_xmax(&hst.chain) - qdist_xmin(&hst.chain); - if (hgram_bins > 10) { - hgram_bins = 10; - } else { - hgram_bins = 0; - hgram_opts |= QDIST_PR_NODECIMAL | QDIST_PR_NOBINRANGE; - } - hgram = qdist_pr(&hst.chain, hgram_bins, hgram_opts); - g_string_append_printf(buf, "TB hash avg chain %0.3f buckets. " - "Histogram: %s\n", - qdist_avg(&hst.chain), hgram); - g_free(hgram); -} - -struct tb_tree_stats { - size_t nb_tbs; - size_t host_size; - size_t target_size; - size_t max_target_size; - size_t direct_jmp_count; - size_t direct_jmp2_count; - size_t cross_page; -}; - -static gboolean tb_tree_stats_iter(gpointer key, gpointer value, gpointer data) -{ - const TranslationBlock *tb = value; - struct tb_tree_stats *tst = data; - - tst->nb_tbs++; - tst->host_size += tb->tc.size; - tst->target_size += tb->size; - if (tb->size > tst->max_target_size) { - tst->max_target_size = tb->size; - } - if (tb_page_addr1(tb) != -1) { - tst->cross_page++; - } - if (tb->jmp_reset_offset[0] != TB_JMP_OFFSET_INVALID) { - tst->direct_jmp_count++; - if (tb->jmp_reset_offset[1] != TB_JMP_OFFSET_INVALID) { - tst->direct_jmp2_count++; - } - } - return false; -} - -void dump_exec_info(GString *buf) -{ - struct tb_tree_stats tst = {}; - struct qht_stats hst; - size_t nb_tbs, flush_full, flush_part, flush_elide; - - tcg_tb_foreach(tb_tree_stats_iter, &tst); - nb_tbs = tst.nb_tbs; - /* XXX: avoid using doubles ? */ - g_string_append_printf(buf, "Translation buffer state:\n"); - /* - * Report total code size including the padding and TB structs; - * otherwise users might think "-accel tcg,tb-size" is not honoured. - * For avg host size we use the precise numbers from tb_tree_stats though. - */ - g_string_append_printf(buf, "gen code size %zu/%zu\n", - tcg_code_size(), tcg_code_capacity()); - g_string_append_printf(buf, "TB count %zu\n", nb_tbs); - g_string_append_printf(buf, "TB avg target size %zu max=%zu bytes\n", - nb_tbs ? tst.target_size / nb_tbs : 0, - tst.max_target_size); - g_string_append_printf(buf, "TB avg host size %zu bytes " - "(expansion ratio: %0.1f)\n", - nb_tbs ? tst.host_size / nb_tbs : 0, - tst.target_size ? - (double)tst.host_size / tst.target_size : 0); - g_string_append_printf(buf, "cross page TB count %zu (%zu%%)\n", - tst.cross_page, - nb_tbs ? (tst.cross_page * 100) / nb_tbs : 0); - g_string_append_printf(buf, "direct jump count %zu (%zu%%) " - "(2 jumps=%zu %zu%%)\n", - tst.direct_jmp_count, - nb_tbs ? (tst.direct_jmp_count * 100) / nb_tbs : 0, - tst.direct_jmp2_count, - nb_tbs ? (tst.direct_jmp2_count * 100) / nb_tbs : 0); - - qht_statistics_init(&tb_ctx.htable, &hst); - print_qht_statistics(hst, buf); - qht_statistics_destroy(&hst); - - g_string_append_printf(buf, "\nStatistics:\n"); - g_string_append_printf(buf, "TB flush count %u\n", - qatomic_read(&tb_ctx.tb_flush_count)); - g_string_append_printf(buf, "TB invalidate count %u\n", - qatomic_read(&tb_ctx.tb_phys_invalidate_count)); - - tlb_flush_counts(&flush_full, &flush_part, &flush_elide); - g_string_append_printf(buf, "TLB full flushes %zu\n", flush_full); - g_string_append_printf(buf, "TLB partial flushes %zu\n", flush_part); - g_string_append_printf(buf, "TLB elided flushes %zu\n", flush_elide); - tcg_dump_info(buf); -} - #else /* CONFIG_USER_ONLY */ void cpu_interrupt(CPUState *cpu, int mask) diff --git a/include/exec/cputlb.h b/include/exec/cputlb.h index 19b16e58f8..6da1462c4f 100644 --- a/include/exec/cputlb.h +++ b/include/exec/cputlb.h @@ -26,6 +26,5 @@ /* cputlb.c */ void tlb_protect_code(ram_addr_t ram_addr); void tlb_unprotect_code(ram_addr_t ram_addr); -void tlb_flush_counts(size_t *full, size_t *part, size_t *elide); #endif #endif diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index a9282cdcc6..3a4c0f124f 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -846,9 +846,6 @@ static inline TCGv_ptr tcg_temp_new_ptr(void) return temp_tcgv_ptr(t); } -void tcg_dump_info(GString *buf); -void tcg_dump_op_count(GString *buf); - #define TCG_CT_CONST 1 /* any constant of register size */ typedef struct TCGArgConstraint { diff --git a/tcg/tcg.c b/tcg/tcg.c index 35158a0846..847d749a7e 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -5927,11 +5927,6 @@ static void tcg_out_st_helper_args(TCGContext *s, const TCGLabelQemuLdst *ldst, tcg_out_helper_load_common_args(s, ldst, parm, info, next_arg); } -void tcg_dump_op_count(GString *buf) -{ - g_string_append_printf(buf, "[TCG profiler not compiled]\n"); -} - int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) { int i, start_words, num_insns; @@ -6128,11 +6123,6 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) return tcg_current_code_size(s); } -void tcg_dump_info(GString *buf) -{ - g_string_append_printf(buf, "[TCG profiler not compiled]\n"); -} - #ifdef ELF_HOST_MACHINE /* In order to use this feature, the backend needs to do three things: From fa645b48d3da093165406dce2c36f967fac6bd9d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 16 Sep 2023 15:01:45 -0700 Subject: [PATCH 489/974] tcg: Add C_N2_I1 Constraint with two outputs, both in new registers. Signed-off-by: Richard Henderson Reviewed-by: Jiajie Chen Message-Id: <20230916220151.526140-2-richard.henderson@linaro.org> --- tcg/tcg.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tcg/tcg.c b/tcg/tcg.c index 847d749a7e..6766b60b8a 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -653,6 +653,7 @@ static void tcg_out_movext3(TCGContext *s, const TCGMovExtend *i1, #define C_O1_I4(O1, I1, I2, I3, I4) C_PFX5(c_o1_i4_, O1, I1, I2, I3, I4), #define C_N1_I2(O1, I1, I2) C_PFX3(c_n1_i2_, O1, I1, I2), +#define C_N2_I1(O1, O2, I1) C_PFX3(c_n2_i1_, O1, O2, I1), #define C_O2_I1(O1, O2, I1) C_PFX3(c_o2_i1_, O1, O2, I1), #define C_O2_I2(O1, O2, I1, I2) C_PFX4(c_o2_i2_, O1, O2, I1, I2), @@ -675,6 +676,7 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode); #undef C_O1_I3 #undef C_O1_I4 #undef C_N1_I2 +#undef C_N2_I1 #undef C_O2_I1 #undef C_O2_I2 #undef C_O2_I3 @@ -694,6 +696,7 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode); #define C_O1_I4(O1, I1, I2, I3, I4) { .args_ct_str = { #O1, #I1, #I2, #I3, #I4 } }, #define C_N1_I2(O1, I1, I2) { .args_ct_str = { "&" #O1, #I1, #I2 } }, +#define C_N2_I1(O1, O2, I1) { .args_ct_str = { "&" #O1, "&" #O2, #I1 } }, #define C_O2_I1(O1, O2, I1) { .args_ct_str = { #O1, #O2, #I1 } }, #define C_O2_I2(O1, O2, I1, I2) { .args_ct_str = { #O1, #O2, #I1, #I2 } }, @@ -715,6 +718,7 @@ static const TCGTargetOpDef constraint_sets[] = { #undef C_O1_I3 #undef C_O1_I4 #undef C_N1_I2 +#undef C_N2_I1 #undef C_O2_I1 #undef C_O2_I2 #undef C_O2_I3 @@ -734,6 +738,7 @@ static const TCGTargetOpDef constraint_sets[] = { #define C_O1_I4(O1, I1, I2, I3, I4) C_PFX5(c_o1_i4_, O1, I1, I2, I3, I4) #define C_N1_I2(O1, I1, I2) C_PFX3(c_n1_i2_, O1, I1, I2) +#define C_N2_I1(O1, O2, I1) C_PFX3(c_n2_i1_, O1, O2, I1) #define C_O2_I1(O1, O2, I1) C_PFX3(c_o2_i1_, O1, O2, I1) #define C_O2_I2(O1, O2, I1, I2) C_PFX4(c_o2_i2_, O1, O2, I1, I2) From 2b2ae0a42e67a85273c0976466f1a634ede084dc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 16 Sep 2023 15:01:46 -0700 Subject: [PATCH 490/974] tcg/loongarch64: Use C_N2_I1 for INDEX_op_qemu_ld_a*_i128 Use new registers for the output, so that we never overlap the input address, which could happen for user-only. This avoids a "tmp = addr + 0" in that case. Signed-off-by: Richard Henderson Reviewed-by: Jiajie Chen Message-Id: <20230916220151.526140-3-richard.henderson@linaro.org> --- tcg/loongarch64/tcg-target-con-set.h | 2 +- tcg/loongarch64/tcg-target.c.inc | 17 +++++++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/tcg/loongarch64/tcg-target-con-set.h b/tcg/loongarch64/tcg-target-con-set.h index 77d62e38e7..cae6c2aad6 100644 --- a/tcg/loongarch64/tcg-target-con-set.h +++ b/tcg/loongarch64/tcg-target-con-set.h @@ -38,4 +38,4 @@ C_O1_I2(w, w, wM) C_O1_I2(w, w, wA) C_O1_I3(w, w, w, w) C_O1_I4(r, rZ, rJ, rZ, rZ) -C_O2_I1(r, r, r) +C_N2_I1(r, r, r) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index ccf133db4b..a1a387df31 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1103,13 +1103,18 @@ static void tcg_out_qemu_ldst_i128(TCGContext *s, TCGReg data_lo, TCGReg data_hi } } else { /* Otherwise use a pair of LD/ST. */ - tcg_out_opc_add_d(s, TCG_REG_TMP0, h.base, h.index); + TCGReg base = h.base; + if (h.index != TCG_REG_ZERO) { + base = TCG_REG_TMP0; + tcg_out_opc_add_d(s, base, h.base, h.index); + } if (is_ld) { - tcg_out_opc_ld_d(s, data_lo, TCG_REG_TMP0, 0); - tcg_out_opc_ld_d(s, data_hi, TCG_REG_TMP0, 8); + tcg_debug_assert(base != data_lo); + tcg_out_opc_ld_d(s, data_lo, base, 0); + tcg_out_opc_ld_d(s, data_hi, base, 8); } else { - tcg_out_opc_st_d(s, data_lo, TCG_REG_TMP0, 0); - tcg_out_opc_st_d(s, data_hi, TCG_REG_TMP0, 8); + tcg_out_opc_st_d(s, data_lo, base, 0); + tcg_out_opc_st_d(s, data_hi, base, 8); } } @@ -2049,7 +2054,7 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_qemu_ld_a32_i128: case INDEX_op_qemu_ld_a64_i128: - return C_O2_I1(r, r, r); + return C_N2_I1(r, r, r); case INDEX_op_qemu_st_a32_i128: case INDEX_op_qemu_st_a64_i128: From 0885f1221e0add5529dada1e7948d2c00189cb8b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 16 Sep 2023 15:01:47 -0700 Subject: [PATCH 491/974] util: Add cpuinfo for loongarch64 Signed-off-by: Richard Henderson Reviewed-by: Jiajie Chen Message-Id: <20230916220151.526140-4-richard.henderson@linaro.org> --- host/include/loongarch64/host/cpuinfo.h | 21 +++++++++++++++ util/cpuinfo-loongarch.c | 35 +++++++++++++++++++++++++ util/meson.build | 2 ++ 3 files changed, 58 insertions(+) create mode 100644 host/include/loongarch64/host/cpuinfo.h create mode 100644 util/cpuinfo-loongarch.c diff --git a/host/include/loongarch64/host/cpuinfo.h b/host/include/loongarch64/host/cpuinfo.h new file mode 100644 index 0000000000..fab664a10b --- /dev/null +++ b/host/include/loongarch64/host/cpuinfo.h @@ -0,0 +1,21 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Host specific cpu identification for LoongArch + */ + +#ifndef HOST_CPUINFO_H +#define HOST_CPUINFO_H + +#define CPUINFO_ALWAYS (1u << 0) /* so cpuinfo is nonzero */ +#define CPUINFO_LSX (1u << 1) + +/* Initialized with a constructor. */ +extern unsigned cpuinfo; + +/* + * We cannot rely on constructor ordering, so other constructors must + * use the function interface rather than the variable above. + */ +unsigned cpuinfo_init(void); + +#endif /* HOST_CPUINFO_H */ diff --git a/util/cpuinfo-loongarch.c b/util/cpuinfo-loongarch.c new file mode 100644 index 0000000000..08b6d7460c --- /dev/null +++ b/util/cpuinfo-loongarch.c @@ -0,0 +1,35 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Host specific cpu identification for LoongArch. + */ + +#include "qemu/osdep.h" +#include "host/cpuinfo.h" + +#ifdef CONFIG_GETAUXVAL +# include +#else +# include "elf.h" +#endif +#include + +unsigned cpuinfo; + +/* Called both as constructor and (possibly) via other constructors. */ +unsigned __attribute__((constructor)) cpuinfo_init(void) +{ + unsigned info = cpuinfo; + unsigned long hwcap; + + if (info) { + return info; + } + + hwcap = qemu_getauxval(AT_HWCAP); + + info = CPUINFO_ALWAYS; + info |= (hwcap & HWCAP_LOONGARCH_LSX ? CPUINFO_LSX : 0); + + cpuinfo = info; + return info; +} diff --git a/util/meson.build b/util/meson.build index 769b24f2e0..1bfff81087 100644 --- a/util/meson.build +++ b/util/meson.build @@ -113,6 +113,8 @@ if cpu == 'aarch64' util_ss.add(files('cpuinfo-aarch64.c')) elif cpu in ['x86', 'x86_64'] util_ss.add(files('cpuinfo-i386.c')) +elif cpu == 'loongarch64' + util_ss.add(files('cpuinfo-loongarch.c')) elif cpu in ['ppc', 'ppc64'] util_ss.add(files('cpuinfo-ppc.c')) endif From f2a553481e520e359f735ea6f4a9435e52b3b446 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 16 Sep 2023 15:01:48 -0700 Subject: [PATCH 492/974] tcg/loongarch64: Use cpuinfo.h Signed-off-by: Richard Henderson Reviewed-by: Jiajie Chen Message-Id: <20230916220151.526140-5-richard.henderson@linaro.org> --- tcg/loongarch64/tcg-target.c.inc | 8 +------- tcg/loongarch64/tcg-target.h | 8 ++++---- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index a1a387df31..ae13c1f666 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -32,8 +32,6 @@ #include "../tcg-ldst.c.inc" #include -bool use_lsx_instructions; - #ifdef CONFIG_DEBUG_TCG static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { "zero", @@ -2314,10 +2312,6 @@ static void tcg_target_init(TCGContext *s) exit(EXIT_FAILURE); } - if (hwcap & HWCAP_LOONGARCH_LSX) { - use_lsx_instructions = 1; - } - tcg_target_available_regs[TCG_TYPE_I32] = ALL_GENERAL_REGS; tcg_target_available_regs[TCG_TYPE_I64] = ALL_GENERAL_REGS; @@ -2333,7 +2327,7 @@ static void tcg_target_init(TCGContext *s) tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_S8); tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_S9); - if (use_lsx_instructions) { + if (cpuinfo & CPUINFO_LSX) { tcg_target_available_regs[TCG_TYPE_V128] = ALL_VECTOR_REGS; tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_V24); tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_V25); diff --git a/tcg/loongarch64/tcg-target.h b/tcg/loongarch64/tcg-target.h index 03017672f6..1bea15b02e 100644 --- a/tcg/loongarch64/tcg-target.h +++ b/tcg/loongarch64/tcg-target.h @@ -29,6 +29,8 @@ #ifndef LOONGARCH_TCG_TARGET_H #define LOONGARCH_TCG_TARGET_H +#include "host/cpuinfo.h" + #define TCG_TARGET_INSN_UNIT_SIZE 4 #define TCG_TARGET_NB_REGS 64 @@ -85,8 +87,6 @@ typedef enum { TCG_VEC_TMP0 = TCG_REG_V23, } TCGReg; -extern bool use_lsx_instructions; - /* used for function call generation */ #define TCG_REG_CALL_STACK TCG_REG_SP #define TCG_TARGET_STACK_ALIGN 16 @@ -171,10 +171,10 @@ extern bool use_lsx_instructions; #define TCG_TARGET_HAS_muluh_i64 1 #define TCG_TARGET_HAS_mulsh_i64 1 -#define TCG_TARGET_HAS_qemu_ldst_i128 use_lsx_instructions +#define TCG_TARGET_HAS_qemu_ldst_i128 (cpuinfo & CPUINFO_LSX) #define TCG_TARGET_HAS_v64 0 -#define TCG_TARGET_HAS_v128 use_lsx_instructions +#define TCG_TARGET_HAS_v128 (cpuinfo & CPUINFO_LSX) #define TCG_TARGET_HAS_v256 0 #define TCG_TARGET_HAS_not_vec 1 From adc8467e697db988878cec645ae52aa6b51ce4c4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 16 Sep 2023 15:01:49 -0700 Subject: [PATCH 493/974] host/include/loongarch64: Add atomic16 load and store While loongarch64 does not have a 128-bit cmpxchg, it does have 128-bit atomic load and store via the vector unit. Signed-off-by: Richard Henderson Message-Id: <20230916220151.526140-6-richard.henderson@linaro.org> --- .../include/loongarch64/host/atomic128-ldst.h | 52 +++++++++++++++++++ .../loongarch64/host/load-extract-al16-al8.h | 39 ++++++++++++++ .../loongarch64/host/store-insert-al16.h | 12 +++++ 3 files changed, 103 insertions(+) create mode 100644 host/include/loongarch64/host/atomic128-ldst.h create mode 100644 host/include/loongarch64/host/load-extract-al16-al8.h create mode 100644 host/include/loongarch64/host/store-insert-al16.h diff --git a/host/include/loongarch64/host/atomic128-ldst.h b/host/include/loongarch64/host/atomic128-ldst.h new file mode 100644 index 0000000000..9a4a8f8b9e --- /dev/null +++ b/host/include/loongarch64/host/atomic128-ldst.h @@ -0,0 +1,52 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Load/store for 128-bit atomic operations, LoongArch version. + * + * See docs/devel/atomics.rst for discussion about the guarantees each + * atomic primitive is meant to provide. + */ + +#ifndef LOONGARCH_ATOMIC128_LDST_H +#define LOONGARCH_ATOMIC128_LDST_H + +#include "host/cpuinfo.h" +#include "tcg/debug-assert.h" + +#define HAVE_ATOMIC128_RO likely(cpuinfo & CPUINFO_LSX) +#define HAVE_ATOMIC128_RW HAVE_ATOMIC128_RO + +/* + * As of gcc 13 and clang 16, there is no compiler support for LSX at all. + * Use inline assembly throughout. + */ + +static inline Int128 atomic16_read_ro(const Int128 *ptr) +{ + uint64_t l, h; + + tcg_debug_assert(HAVE_ATOMIC128_RO); + asm("vld $vr0, %2, 0\n\t" + "vpickve2gr.d %0, $vr0, 0\n\t" + "vpickve2gr.d %1, $vr0, 1" + : "=r"(l), "=r"(h) : "r"(ptr), "m"(*ptr) : "f0"); + + return int128_make128(l, h); +} + +static inline Int128 atomic16_read_rw(Int128 *ptr) +{ + return atomic16_read_ro(ptr); +} + +static inline void atomic16_set(Int128 *ptr, Int128 val) +{ + uint64_t l = int128_getlo(val), h = int128_gethi(val); + + tcg_debug_assert(HAVE_ATOMIC128_RW); + asm("vinsgr2vr.d $vr0, %1, 0\n\t" + "vinsgr2vr.d $vr0, %2, 1\n\t" + "vst $vr0, %3, 0" + : "=m"(*ptr) : "r"(l), "r"(h), "r"(ptr) : "f0"); +} + +#endif /* LOONGARCH_ATOMIC128_LDST_H */ diff --git a/host/include/loongarch64/host/load-extract-al16-al8.h b/host/include/loongarch64/host/load-extract-al16-al8.h new file mode 100644 index 0000000000..d1fb59d8af --- /dev/null +++ b/host/include/loongarch64/host/load-extract-al16-al8.h @@ -0,0 +1,39 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Atomic extract 64 from 128-bit, LoongArch version. + * + * Copyright (C) 2023 Linaro, Ltd. + */ + +#ifndef LOONGARCH_LOAD_EXTRACT_AL16_AL8_H +#define LOONGARCH_LOAD_EXTRACT_AL16_AL8_H + +#include "host/cpuinfo.h" +#include "tcg/debug-assert.h" + +/** + * load_atom_extract_al16_or_al8: + * @pv: host address + * @s: object size in bytes, @s <= 8. + * + * Load @s bytes from @pv, when pv % s != 0. If [p, p+s-1] does not + * cross an 16-byte boundary then the access must be 16-byte atomic, + * otherwise the access must be 8-byte atomic. + */ +static inline uint64_t load_atom_extract_al16_or_al8(void *pv, int s) +{ + uintptr_t pi = (uintptr_t)pv; + Int128 *ptr_align = (Int128 *)(pi & ~7); + int shr = (pi & 7) * 8; + uint64_t l, h; + + tcg_debug_assert(HAVE_ATOMIC128_RO); + asm("vld $vr0, %2, 0\n\t" + "vpickve2gr.d %0, $vr0, 0\n\t" + "vpickve2gr.d %1, $vr0, 1" + : "=r"(l), "=r"(h) : "r"(ptr_align), "m"(*ptr_align) : "f0"); + + return (l >> shr) | (h << (-shr & 63)); +} + +#endif /* LOONGARCH_LOAD_EXTRACT_AL16_AL8_H */ diff --git a/host/include/loongarch64/host/store-insert-al16.h b/host/include/loongarch64/host/store-insert-al16.h new file mode 100644 index 0000000000..919fd8d744 --- /dev/null +++ b/host/include/loongarch64/host/store-insert-al16.h @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Atomic store insert into 128-bit, LoongArch version. + */ + +#ifndef LOONGARCH_STORE_INSERT_AL16_H +#define LOONGARCH_STORE_INSERT_AL16_H + +void store_atom_insert_al16(Int128 *ps, Int128 val, Int128 msk) + QEMU_ERROR("unsupported atomic"); + +#endif /* LOONGARCH_STORE_INSERT_AL16_H */ From 8b1b3db71a1df58a6e28956b72f143e8cf38bdf6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 16 Sep 2023 15:01:50 -0700 Subject: [PATCH 494/974] accel/tcg: Remove redundant case in store_atom_16 We handled the HAVE_ATOMIC128_RW case with atomic16_set at the top of the function; the only thing left for a host without that support is to fall through to cpu_loop_exit_atomic. Signed-off-by: Richard Henderson Message-Id: <20230916220151.526140-7-richard.henderson@linaro.org> --- accel/tcg/ldst_atomicity.c.inc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/accel/tcg/ldst_atomicity.c.inc b/accel/tcg/ldst_atomicity.c.inc index 1cf5b92166..e8f97506fa 100644 --- a/accel/tcg/ldst_atomicity.c.inc +++ b/accel/tcg/ldst_atomicity.c.inc @@ -1103,10 +1103,6 @@ static void store_atom_16(CPUState *cpu, uintptr_t ra, } break; case MO_128: - if (HAVE_ATOMIC128_RW) { - atomic16_set(pv, val); - return; - } break; default: g_assert_not_reached(); From 6046f6e94d8d530ecc28176232479889abbee47e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 16 Sep 2023 15:01:51 -0700 Subject: [PATCH 495/974] accel/tcg: Fix condition for store_atom_insert_al16 Store bytes under a mask is fundamentally a cmpxchg, not a straight store. Use HAVE_CMPXCHG128 instead of HAVE_ATOMIC128_RW. Signed-off-by: Richard Henderson Message-Id: <20230916220151.526140-8-richard.henderson@linaro.org> --- accel/tcg/cputlb.c | 2 +- accel/tcg/ldst_atomicity.c.inc | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 13986820fe..f35c5f359b 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -2691,7 +2691,7 @@ static uint64_t do_st16_leN(CPUState *cpu, MMULookupPageData *p, case MO_ATOM_WITHIN16_PAIR: /* Since size > 8, this is the half that must be atomic. */ - if (!HAVE_ATOMIC128_RW) { + if (!HAVE_CMPXCHG128) { cpu_loop_exit_atomic(cpu, ra); } return store_whole_le16(p->haddr, p->size, val_le); diff --git a/accel/tcg/ldst_atomicity.c.inc b/accel/tcg/ldst_atomicity.c.inc index e8f97506fa..33a04dec52 100644 --- a/accel/tcg/ldst_atomicity.c.inc +++ b/accel/tcg/ldst_atomicity.c.inc @@ -825,7 +825,7 @@ static uint64_t store_whole_le16(void *pv, int size, Int128 val_le) int sh = o * 8; Int128 m, v; - qemu_build_assert(HAVE_ATOMIC128_RW); + qemu_build_assert(HAVE_CMPXCHG128); /* Like MAKE_64BIT_MASK(0, sz), but larger. */ if (sz <= 64) { @@ -887,7 +887,7 @@ static void store_atom_2(CPUState *cpu, uintptr_t ra, return; } } else if ((pi & 15) == 7) { - if (HAVE_ATOMIC128_RW) { + if (HAVE_CMPXCHG128) { Int128 v = int128_lshift(int128_make64(val), 56); Int128 m = int128_lshift(int128_make64(0xffff), 56); store_atom_insert_al16(pv - 7, v, m); @@ -956,7 +956,7 @@ static void store_atom_4(CPUState *cpu, uintptr_t ra, return; } } else { - if (HAVE_ATOMIC128_RW) { + if (HAVE_CMPXCHG128) { store_whole_le16(pv, 4, int128_make64(cpu_to_le32(val))); return; } @@ -1021,7 +1021,7 @@ static void store_atom_8(CPUState *cpu, uintptr_t ra, } break; case MO_64: - if (HAVE_ATOMIC128_RW) { + if (HAVE_CMPXCHG128) { store_whole_le16(pv, 8, int128_make64(cpu_to_le64(val))); return; } @@ -1076,7 +1076,7 @@ static void store_atom_16(CPUState *cpu, uintptr_t ra, } break; case -MO_64: - if (HAVE_ATOMIC128_RW) { + if (HAVE_CMPXCHG128) { uint64_t val_le; int s2 = pi & 15; int s1 = 16 - s2; From ecfa1877f7e9d300b57492552afed2932ed954d7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 29 Oct 2023 14:08:38 -0700 Subject: [PATCH 496/974] tcg: Mark tcg_gen_op* as noinline Encourage the compiler to tail-call rather than inline across the dozens of opcode expanders. Signed-off-by: Richard Henderson Message-Id: <20231029210848.78234-2-richard.henderson@linaro.org> --- tcg/tcg-op.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 828eb9ee46..9aed19e957 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -31,20 +31,26 @@ #include "tcg-internal.h" -void tcg_gen_op1(TCGOpcode opc, TCGArg a1) +/* + * Encourage the compiler to tail-call to a function, rather than inlining. + * Minimizes code size across 99 bottles of beer on the wall. + */ +#define NI __attribute__((noinline)) + +void NI tcg_gen_op1(TCGOpcode opc, TCGArg a1) { TCGOp *op = tcg_emit_op(opc, 1); op->args[0] = a1; } -void tcg_gen_op2(TCGOpcode opc, TCGArg a1, TCGArg a2) +void NI tcg_gen_op2(TCGOpcode opc, TCGArg a1, TCGArg a2) { TCGOp *op = tcg_emit_op(opc, 2); op->args[0] = a1; op->args[1] = a2; } -void tcg_gen_op3(TCGOpcode opc, TCGArg a1, TCGArg a2, TCGArg a3) +void NI tcg_gen_op3(TCGOpcode opc, TCGArg a1, TCGArg a2, TCGArg a3) { TCGOp *op = tcg_emit_op(opc, 3); op->args[0] = a1; @@ -52,7 +58,7 @@ void tcg_gen_op3(TCGOpcode opc, TCGArg a1, TCGArg a2, TCGArg a3) op->args[2] = a3; } -void tcg_gen_op4(TCGOpcode opc, TCGArg a1, TCGArg a2, TCGArg a3, TCGArg a4) +void NI tcg_gen_op4(TCGOpcode opc, TCGArg a1, TCGArg a2, TCGArg a3, TCGArg a4) { TCGOp *op = tcg_emit_op(opc, 4); op->args[0] = a1; @@ -61,8 +67,8 @@ void tcg_gen_op4(TCGOpcode opc, TCGArg a1, TCGArg a2, TCGArg a3, TCGArg a4) op->args[3] = a4; } -void tcg_gen_op5(TCGOpcode opc, TCGArg a1, TCGArg a2, TCGArg a3, - TCGArg a4, TCGArg a5) +void NI tcg_gen_op5(TCGOpcode opc, TCGArg a1, TCGArg a2, TCGArg a3, + TCGArg a4, TCGArg a5) { TCGOp *op = tcg_emit_op(opc, 5); op->args[0] = a1; @@ -72,8 +78,8 @@ void tcg_gen_op5(TCGOpcode opc, TCGArg a1, TCGArg a2, TCGArg a3, op->args[4] = a5; } -void tcg_gen_op6(TCGOpcode opc, TCGArg a1, TCGArg a2, TCGArg a3, - TCGArg a4, TCGArg a5, TCGArg a6) +void NI tcg_gen_op6(TCGOpcode opc, TCGArg a1, TCGArg a2, TCGArg a3, + TCGArg a4, TCGArg a5, TCGArg a6) { TCGOp *op = tcg_emit_op(opc, 6); op->args[0] = a1; From 6fc75d50a58bdf15fcc09e89880ba5125d572a6d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 29 Oct 2023 14:08:39 -0700 Subject: [PATCH 497/974] tcg: Move tcg_gen_op* out of line In addition to moving out of line, with CONFIG_DEBUG_TCG mark them all noinline. Signed-off-by: Richard Henderson Message-Id: <20231029210848.78234-3-richard.henderson@linaro.org> --- include/tcg/tcg-op-common.h | 252 +++++++----------------------------- tcg/tcg-op.c | 208 +++++++++++++++++++++++++++++ 2 files changed, 252 insertions(+), 208 deletions(-) diff --git a/include/tcg/tcg-op-common.h b/include/tcg/tcg-op-common.h index 677aea6dd1..e093f541a7 100644 --- a/include/tcg/tcg-op-common.h +++ b/include/tcg/tcg-op-common.h @@ -25,214 +25,50 @@ void vec_gen_2(TCGOpcode, TCGType, unsigned, TCGArg, TCGArg); void vec_gen_3(TCGOpcode, TCGType, unsigned, TCGArg, TCGArg, TCGArg); void vec_gen_4(TCGOpcode, TCGType, unsigned, TCGArg, TCGArg, TCGArg, TCGArg); -static inline void tcg_gen_op1_i32(TCGOpcode opc, TCGv_i32 a1) -{ - tcg_gen_op1(opc, tcgv_i32_arg(a1)); -} - -static inline void tcg_gen_op1_i64(TCGOpcode opc, TCGv_i64 a1) -{ - tcg_gen_op1(opc, tcgv_i64_arg(a1)); -} - -static inline void tcg_gen_op1i(TCGOpcode opc, TCGArg a1) -{ - tcg_gen_op1(opc, a1); -} - -static inline void tcg_gen_op2_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2) -{ - tcg_gen_op2(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2)); -} - -static inline void tcg_gen_op2_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2) -{ - tcg_gen_op2(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2)); -} - -static inline void tcg_gen_op2i_i32(TCGOpcode opc, TCGv_i32 a1, TCGArg a2) -{ - tcg_gen_op2(opc, tcgv_i32_arg(a1), a2); -} - -static inline void tcg_gen_op2i_i64(TCGOpcode opc, TCGv_i64 a1, TCGArg a2) -{ - tcg_gen_op2(opc, tcgv_i64_arg(a1), a2); -} - -static inline void tcg_gen_op2ii(TCGOpcode opc, TCGArg a1, TCGArg a2) -{ - tcg_gen_op2(opc, a1, a2); -} - -static inline void tcg_gen_op3_i32(TCGOpcode opc, TCGv_i32 a1, - TCGv_i32 a2, TCGv_i32 a3) -{ - tcg_gen_op3(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), tcgv_i32_arg(a3)); -} - -static inline void tcg_gen_op3_i64(TCGOpcode opc, TCGv_i64 a1, - TCGv_i64 a2, TCGv_i64 a3) -{ - tcg_gen_op3(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), tcgv_i64_arg(a3)); -} - -static inline void tcg_gen_op3i_i32(TCGOpcode opc, TCGv_i32 a1, - TCGv_i32 a2, TCGArg a3) -{ - tcg_gen_op3(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), a3); -} - -static inline void tcg_gen_op3i_i64(TCGOpcode opc, TCGv_i64 a1, - TCGv_i64 a2, TCGArg a3) -{ - tcg_gen_op3(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), a3); -} - -static inline void tcg_gen_ldst_op_i32(TCGOpcode opc, TCGv_i32 val, - TCGv_ptr base, TCGArg offset) -{ - tcg_gen_op3(opc, tcgv_i32_arg(val), tcgv_ptr_arg(base), offset); -} - -static inline void tcg_gen_ldst_op_i64(TCGOpcode opc, TCGv_i64 val, - TCGv_ptr base, TCGArg offset) -{ - tcg_gen_op3(opc, tcgv_i64_arg(val), tcgv_ptr_arg(base), offset); -} - -static inline void tcg_gen_op4_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, - TCGv_i32 a3, TCGv_i32 a4) -{ - tcg_gen_op4(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), - tcgv_i32_arg(a3), tcgv_i32_arg(a4)); -} - -static inline void tcg_gen_op4_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, - TCGv_i64 a3, TCGv_i64 a4) -{ - tcg_gen_op4(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), - tcgv_i64_arg(a3), tcgv_i64_arg(a4)); -} - -static inline void tcg_gen_op4i_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, - TCGv_i32 a3, TCGArg a4) -{ - tcg_gen_op4(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), - tcgv_i32_arg(a3), a4); -} - -static inline void tcg_gen_op4i_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, - TCGv_i64 a3, TCGArg a4) -{ - tcg_gen_op4(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), - tcgv_i64_arg(a3), a4); -} - -static inline void tcg_gen_op4ii_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, - TCGArg a3, TCGArg a4) -{ - tcg_gen_op4(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), a3, a4); -} - -static inline void tcg_gen_op4ii_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, - TCGArg a3, TCGArg a4) -{ - tcg_gen_op4(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), a3, a4); -} - -static inline void tcg_gen_op5_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, - TCGv_i32 a3, TCGv_i32 a4, TCGv_i32 a5) -{ - tcg_gen_op5(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), - tcgv_i32_arg(a3), tcgv_i32_arg(a4), tcgv_i32_arg(a5)); -} - -static inline void tcg_gen_op5_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, - TCGv_i64 a3, TCGv_i64 a4, TCGv_i64 a5) -{ - tcg_gen_op5(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), - tcgv_i64_arg(a3), tcgv_i64_arg(a4), tcgv_i64_arg(a5)); -} - -static inline void tcg_gen_op5i_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, - TCGv_i32 a3, TCGv_i32 a4, TCGArg a5) -{ - tcg_gen_op5(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), - tcgv_i32_arg(a3), tcgv_i32_arg(a4), a5); -} - -static inline void tcg_gen_op5i_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, - TCGv_i64 a3, TCGv_i64 a4, TCGArg a5) -{ - tcg_gen_op5(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), - tcgv_i64_arg(a3), tcgv_i64_arg(a4), a5); -} - -static inline void tcg_gen_op5ii_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, - TCGv_i32 a3, TCGArg a4, TCGArg a5) -{ - tcg_gen_op5(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), - tcgv_i32_arg(a3), a4, a5); -} - -static inline void tcg_gen_op5ii_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, - TCGv_i64 a3, TCGArg a4, TCGArg a5) -{ - tcg_gen_op5(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), - tcgv_i64_arg(a3), a4, a5); -} - -static inline void tcg_gen_op6_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, - TCGv_i32 a3, TCGv_i32 a4, - TCGv_i32 a5, TCGv_i32 a6) -{ - tcg_gen_op6(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), - tcgv_i32_arg(a3), tcgv_i32_arg(a4), tcgv_i32_arg(a5), - tcgv_i32_arg(a6)); -} - -static inline void tcg_gen_op6_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, - TCGv_i64 a3, TCGv_i64 a4, - TCGv_i64 a5, TCGv_i64 a6) -{ - tcg_gen_op6(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), - tcgv_i64_arg(a3), tcgv_i64_arg(a4), tcgv_i64_arg(a5), - tcgv_i64_arg(a6)); -} - -static inline void tcg_gen_op6i_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, - TCGv_i32 a3, TCGv_i32 a4, - TCGv_i32 a5, TCGArg a6) -{ - tcg_gen_op6(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), - tcgv_i32_arg(a3), tcgv_i32_arg(a4), tcgv_i32_arg(a5), a6); -} - -static inline void tcg_gen_op6i_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, - TCGv_i64 a3, TCGv_i64 a4, - TCGv_i64 a5, TCGArg a6) -{ - tcg_gen_op6(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), - tcgv_i64_arg(a3), tcgv_i64_arg(a4), tcgv_i64_arg(a5), a6); -} - -static inline void tcg_gen_op6ii_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, - TCGv_i32 a3, TCGv_i32 a4, - TCGArg a5, TCGArg a6) -{ - tcg_gen_op6(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), - tcgv_i32_arg(a3), tcgv_i32_arg(a4), a5, a6); -} - -static inline void tcg_gen_op6ii_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, - TCGv_i64 a3, TCGv_i64 a4, - TCGArg a5, TCGArg a6) -{ - tcg_gen_op6(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), - tcgv_i64_arg(a3), tcgv_i64_arg(a4), a5, a6); -} - +void tcg_gen_op1_i32(TCGOpcode, TCGv_i32); +void tcg_gen_op1_i64(TCGOpcode, TCGv_i64); +void tcg_gen_op1i(TCGOpcode, TCGArg); +void tcg_gen_op2_i32(TCGOpcode, TCGv_i32, TCGv_i32); +void tcg_gen_op2_i64(TCGOpcode, TCGv_i64, TCGv_i64); +void tcg_gen_op2i_i32(TCGOpcode, TCGv_i32, TCGArg); +void tcg_gen_op2i_i64(TCGOpcode, TCGv_i64, TCGArg); +void tcg_gen_op2ii(TCGOpcode, TCGArg, TCGArg); +void tcg_gen_op3_i32(TCGOpcode, TCGv_i32, TCGv_i32, TCGv_i32); +void tcg_gen_op3_i64(TCGOpcode, TCGv_i64, TCGv_i64, TCGv_i64); +void tcg_gen_op3i_i32(TCGOpcode, TCGv_i32, TCGv_i32, TCGArg); +void tcg_gen_op3i_i64(TCGOpcode, TCGv_i64, TCGv_i64, TCGArg); +void tcg_gen_ldst_op_i32(TCGOpcode, TCGv_i32, TCGv_ptr, TCGArg); +void tcg_gen_ldst_op_i64(TCGOpcode, TCGv_i64, TCGv_ptr, TCGArg); +void tcg_gen_op4_i32(TCGOpcode, TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32); +void tcg_gen_op4_i64(TCGOpcode, TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64); +void tcg_gen_op4i_i32(TCGOpcode, TCGv_i32, TCGv_i32, TCGv_i32, TCGArg); +void tcg_gen_op4i_i64(TCGOpcode, TCGv_i64, TCGv_i64, TCGv_i64, TCGArg); +void tcg_gen_op4ii_i32(TCGOpcode, TCGv_i32, TCGv_i32, TCGArg, TCGArg); +void tcg_gen_op4ii_i64(TCGOpcode, TCGv_i64, TCGv_i64, TCGArg, TCGArg); +void tcg_gen_op5_i32(TCGOpcode, TCGv_i32, TCGv_i32, TCGv_i32, + TCGv_i32, TCGv_i32); +void tcg_gen_op5_i64(TCGOpcode, TCGv_i64, TCGv_i64, TCGv_i64, + TCGv_i64, TCGv_i64); +void tcg_gen_op5i_i32(TCGOpcode, TCGv_i32, TCGv_i32, TCGv_i32, + TCGv_i32, TCGArg); +void tcg_gen_op5i_i64(TCGOpcode, TCGv_i64, TCGv_i64, TCGv_i64, + TCGv_i64, TCGArg); +void tcg_gen_op5ii_i32(TCGOpcode, TCGv_i32, TCGv_i32, TCGv_i32, + TCGArg, TCGArg); +void tcg_gen_op5ii_i64(TCGOpcode, TCGv_i64, TCGv_i64, TCGv_i64, + TCGArg, TCGArg); +void tcg_gen_op6_i32(TCGOpcode, TCGv_i32, TCGv_i32, TCGv_i32, + TCGv_i32, TCGv_i32, TCGv_i32); +void tcg_gen_op6_i64(TCGOpcode, TCGv_i64, TCGv_i64, TCGv_i64, + TCGv_i64, TCGv_i64, TCGv_i64); +void tcg_gen_op6i_i32(TCGOpcode, TCGv_i32, TCGv_i32, TCGv_i32, + TCGv_i32, TCGv_i32, TCGArg); +void tcg_gen_op6i_i64(TCGOpcode, TCGv_i64, TCGv_i64, TCGv_i64, + TCGv_i64, TCGv_i64, TCGArg); +void tcg_gen_op6ii_i32(TCGOpcode, TCGv_i32, TCGv_i32, TCGv_i32, + TCGv_i32, TCGArg, TCGArg); +void tcg_gen_op6ii_i64(TCGOpcode, TCGv_i64, TCGv_i64, TCGv_i64, + TCGv_i64, TCGArg, TCGArg); /* Generic ops. */ diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 9aed19e957..6c826b46b0 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -90,6 +90,214 @@ void NI tcg_gen_op6(TCGOpcode opc, TCGArg a1, TCGArg a2, TCGArg a3, op->args[5] = a6; } +/* + * With CONFIG_DEBUG_TCG, tcgv_*_tmp via tcgv_*_arg, is an out-of-line + * assertion check. Force tail calls to avoid too much code expansion. + */ +#ifdef CONFIG_DEBUG_TCG +# define DNI NI +#else +# define DNI +#endif + +void DNI tcg_gen_op1_i32(TCGOpcode opc, TCGv_i32 a1) +{ + tcg_gen_op1(opc, tcgv_i32_arg(a1)); +} + +void DNI tcg_gen_op1_i64(TCGOpcode opc, TCGv_i64 a1) +{ + tcg_gen_op1(opc, tcgv_i64_arg(a1)); +} + +void DNI tcg_gen_op1i(TCGOpcode opc, TCGArg a1) +{ + tcg_gen_op1(opc, a1); +} + +void DNI tcg_gen_op2_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2) +{ + tcg_gen_op2(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2)); +} + +void DNI tcg_gen_op2_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2) +{ + tcg_gen_op2(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2)); +} + +void DNI tcg_gen_op2i_i32(TCGOpcode opc, TCGv_i32 a1, TCGArg a2) +{ + tcg_gen_op2(opc, tcgv_i32_arg(a1), a2); +} + +void DNI tcg_gen_op2i_i64(TCGOpcode opc, TCGv_i64 a1, TCGArg a2) +{ + tcg_gen_op2(opc, tcgv_i64_arg(a1), a2); +} + +void DNI tcg_gen_op2ii(TCGOpcode opc, TCGArg a1, TCGArg a2) +{ + tcg_gen_op2(opc, a1, a2); +} + +void DNI tcg_gen_op3_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, TCGv_i32 a3) +{ + tcg_gen_op3(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), tcgv_i32_arg(a3)); +} + +void DNI tcg_gen_op3_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, TCGv_i64 a3) +{ + tcg_gen_op3(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), tcgv_i64_arg(a3)); +} + +void DNI tcg_gen_op3i_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, TCGArg a3) +{ + tcg_gen_op3(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), a3); +} + +void DNI tcg_gen_op3i_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, TCGArg a3) +{ + tcg_gen_op3(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), a3); +} + +void DNI tcg_gen_ldst_op_i32(TCGOpcode opc, TCGv_i32 val, + TCGv_ptr base, TCGArg offset) +{ + tcg_gen_op3(opc, tcgv_i32_arg(val), tcgv_ptr_arg(base), offset); +} + +void DNI tcg_gen_ldst_op_i64(TCGOpcode opc, TCGv_i64 val, + TCGv_ptr base, TCGArg offset) +{ + tcg_gen_op3(opc, tcgv_i64_arg(val), tcgv_ptr_arg(base), offset); +} + +void DNI tcg_gen_op4_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGv_i32 a4) +{ + tcg_gen_op4(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), + tcgv_i32_arg(a3), tcgv_i32_arg(a4)); +} + +void DNI tcg_gen_op4_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGv_i64 a3, TCGv_i64 a4) +{ + tcg_gen_op4(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), + tcgv_i64_arg(a3), tcgv_i64_arg(a4)); +} + +void DNI tcg_gen_op4i_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGArg a4) +{ + tcg_gen_op4(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), + tcgv_i32_arg(a3), a4); +} + +void DNI tcg_gen_op4i_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGv_i64 a3, TCGArg a4) +{ + tcg_gen_op4(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), + tcgv_i64_arg(a3), a4); +} + +void DNI tcg_gen_op4ii_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGArg a3, TCGArg a4) +{ + tcg_gen_op4(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), a3, a4); +} + +void DNI tcg_gen_op4ii_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGArg a3, TCGArg a4) +{ + tcg_gen_op4(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), a3, a4); +} + +void DNI tcg_gen_op5_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGv_i32 a4, TCGv_i32 a5) +{ + tcg_gen_op5(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), + tcgv_i32_arg(a3), tcgv_i32_arg(a4), tcgv_i32_arg(a5)); +} + +void DNI tcg_gen_op5_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGv_i64 a3, TCGv_i64 a4, TCGv_i64 a5) +{ + tcg_gen_op5(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), + tcgv_i64_arg(a3), tcgv_i64_arg(a4), tcgv_i64_arg(a5)); +} + +void DNI tcg_gen_op5i_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGv_i32 a4, TCGArg a5) +{ + tcg_gen_op5(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), + tcgv_i32_arg(a3), tcgv_i32_arg(a4), a5); +} + +void DNI tcg_gen_op5i_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGv_i64 a3, TCGv_i64 a4, TCGArg a5) +{ + tcg_gen_op5(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), + tcgv_i64_arg(a3), tcgv_i64_arg(a4), a5); +} + +void DNI tcg_gen_op5ii_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGArg a4, TCGArg a5) +{ + tcg_gen_op5(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), + tcgv_i32_arg(a3), a4, a5); +} + +void DNI tcg_gen_op5ii_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGv_i64 a3, TCGArg a4, TCGArg a5) +{ + tcg_gen_op5(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), + tcgv_i64_arg(a3), a4, a5); +} + +void DNI tcg_gen_op6_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, TCGv_i32 a3, + TCGv_i32 a4, TCGv_i32 a5, TCGv_i32 a6) +{ + tcg_gen_op6(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), + tcgv_i32_arg(a3), tcgv_i32_arg(a4), tcgv_i32_arg(a5), + tcgv_i32_arg(a6)); +} + +void DNI tcg_gen_op6_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, TCGv_i64 a3, + TCGv_i64 a4, TCGv_i64 a5, TCGv_i64 a6) +{ + tcg_gen_op6(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), + tcgv_i64_arg(a3), tcgv_i64_arg(a4), tcgv_i64_arg(a5), + tcgv_i64_arg(a6)); +} + +void DNI tcg_gen_op6i_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, TCGv_i32 a3, + TCGv_i32 a4, TCGv_i32 a5, TCGArg a6) +{ + tcg_gen_op6(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), + tcgv_i32_arg(a3), tcgv_i32_arg(a4), tcgv_i32_arg(a5), a6); +} + +void DNI tcg_gen_op6i_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, TCGv_i64 a3, + TCGv_i64 a4, TCGv_i64 a5, TCGArg a6) +{ + tcg_gen_op6(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), + tcgv_i64_arg(a3), tcgv_i64_arg(a4), tcgv_i64_arg(a5), a6); +} + +void DNI tcg_gen_op6ii_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGv_i32 a4, TCGArg a5, TCGArg a6) +{ + tcg_gen_op6(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), + tcgv_i32_arg(a3), tcgv_i32_arg(a4), a5, a6); +} + +void DNI tcg_gen_op6ii_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGv_i64 a3, TCGv_i64 a4, TCGArg a5, TCGArg a6) +{ + tcg_gen_op6(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), + tcgv_i64_arg(a3), tcgv_i64_arg(a4), a5, a6); +} + /* Generic ops. */ static void add_last_as_label_use(TCGLabel *l) From 01bbb6e3eb3368b40384a7e044c9a2d342b8039b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 29 Oct 2023 14:08:40 -0700 Subject: [PATCH 498/974] tcg: Move generic expanders out of line Signed-off-by: Richard Henderson Message-Id: <20231029210848.78234-4-richard.henderson@linaro.org> --- include/tcg/tcg-op-common.h | 19 +++---------------- tcg/tcg-op.c | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/include/tcg/tcg-op-common.h b/include/tcg/tcg-op-common.h index e093f541a7..2134961a98 100644 --- a/include/tcg/tcg-op-common.h +++ b/include/tcg/tcg-op-common.h @@ -72,12 +72,7 @@ void tcg_gen_op6ii_i64(TCGOpcode, TCGv_i64, TCGv_i64, TCGv_i64, /* Generic ops. */ -static inline void gen_set_label(TCGLabel *l) -{ - l->present = 1; - tcg_gen_op1(INDEX_op_set_label, label_arg(l)); -} - +void gen_set_label(TCGLabel *l); void tcg_gen_br(TCGLabel *l); void tcg_gen_mb(TCGBar); @@ -121,16 +116,8 @@ void tcg_gen_goto_tb(unsigned idx); */ void tcg_gen_lookup_and_goto_ptr(void); -static inline void tcg_gen_plugin_cb_start(unsigned from, unsigned type, - unsigned wr) -{ - tcg_gen_op3(INDEX_op_plugin_cb_start, from, type, wr); -} - -static inline void tcg_gen_plugin_cb_end(void) -{ - tcg_emit_op(INDEX_op_plugin_cb_end, 0); -} +void tcg_gen_plugin_cb_start(unsigned from, unsigned type, unsigned wr); +void tcg_gen_plugin_cb_end(void); /* 32 bit ops */ diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 6c826b46b0..a8cbad212d 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -300,6 +300,12 @@ void DNI tcg_gen_op6ii_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, /* Generic ops. */ +void gen_set_label(TCGLabel *l) +{ + l->present = 1; + tcg_gen_op1(INDEX_op_set_label, label_arg(l)); +} + static void add_last_as_label_use(TCGLabel *l) { TCGLabelUse *u = tcg_malloc(sizeof(TCGLabelUse)); @@ -333,6 +339,16 @@ void tcg_gen_mb(TCGBar mb_type) } } +void tcg_gen_plugin_cb_start(unsigned from, unsigned type, unsigned wr) +{ + tcg_gen_op3(INDEX_op_plugin_cb_start, from, type, wr); +} + +void tcg_gen_plugin_cb_end(void) +{ + tcg_emit_op(INDEX_op_plugin_cb_end, 0); +} + /* 32 bit ops */ void tcg_gen_movi_i32(TCGv_i32 ret, int32_t arg) From 09607d35f5a9a6aa1c57302c1e27066ad2309e63 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 29 Oct 2023 14:08:41 -0700 Subject: [PATCH 499/974] tcg: Move 32-bit expanders out of line Signed-off-by: Richard Henderson Message-Id: <20231029210848.78234-5-richard.henderson@linaro.org> --- include/tcg/tcg-op-common.h | 140 ++++++------------------------------ tcg/tcg-op.c | 116 ++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 119 deletions(-) diff --git a/include/tcg/tcg-op-common.h b/include/tcg/tcg-op-common.h index 2134961a98..cdaa1415d1 100644 --- a/include/tcg/tcg-op-common.h +++ b/include/tcg/tcg-op-common.h @@ -197,128 +197,30 @@ void tcg_gen_abs_i32(TCGv_i32, TCGv_i32); /* Replicate a value of size @vece from @in to all the lanes in @out */ void tcg_gen_dup_i32(unsigned vece, TCGv_i32 out, TCGv_i32 in); -static inline void tcg_gen_discard_i32(TCGv_i32 arg) -{ - tcg_gen_op1_i32(INDEX_op_discard, arg); -} +void tcg_gen_discard_i32(TCGv_i32 arg); +void tcg_gen_mov_i32(TCGv_i32 ret, TCGv_i32 arg); -static inline void tcg_gen_mov_i32(TCGv_i32 ret, TCGv_i32 arg) -{ - if (ret != arg) { - tcg_gen_op2_i32(INDEX_op_mov_i32, ret, arg); - } -} +void tcg_gen_ld8u_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_ld8s_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_ld16u_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_ld16s_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_ld_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset); -static inline void tcg_gen_ld8u_i32(TCGv_i32 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i32(INDEX_op_ld8u_i32, ret, arg2, offset); -} +void tcg_gen_st8_i32(TCGv_i32 arg1, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_st16_i32(TCGv_i32 arg1, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_st_i32(TCGv_i32 arg1, TCGv_ptr arg2, tcg_target_long offset); -static inline void tcg_gen_ld8s_i32(TCGv_i32 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i32(INDEX_op_ld8s_i32, ret, arg2, offset); -} - -static inline void tcg_gen_ld16u_i32(TCGv_i32 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i32(INDEX_op_ld16u_i32, ret, arg2, offset); -} - -static inline void tcg_gen_ld16s_i32(TCGv_i32 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i32(INDEX_op_ld16s_i32, ret, arg2, offset); -} - -static inline void tcg_gen_ld_i32(TCGv_i32 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i32(INDEX_op_ld_i32, ret, arg2, offset); -} - -static inline void tcg_gen_st8_i32(TCGv_i32 arg1, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i32(INDEX_op_st8_i32, arg1, arg2, offset); -} - -static inline void tcg_gen_st16_i32(TCGv_i32 arg1, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i32(INDEX_op_st16_i32, arg1, arg2, offset); -} - -static inline void tcg_gen_st_i32(TCGv_i32 arg1, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i32(INDEX_op_st_i32, arg1, arg2, offset); -} - -static inline void tcg_gen_add_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_add_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_sub_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_sub_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_and_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_and_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_or_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_or_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_xor_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_xor_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_shl_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_shl_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_shr_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_shr_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_sar_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_sar_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_mul_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_mul_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_neg_i32(TCGv_i32 ret, TCGv_i32 arg) -{ - if (TCG_TARGET_HAS_neg_i32) { - tcg_gen_op2_i32(INDEX_op_neg_i32, ret, arg); - } else { - tcg_gen_subfi_i32(ret, 0, arg); - } -} - -static inline void tcg_gen_not_i32(TCGv_i32 ret, TCGv_i32 arg) -{ - if (TCG_TARGET_HAS_not_i32) { - tcg_gen_op2_i32(INDEX_op_not_i32, ret, arg); - } else { - tcg_gen_xori_i32(ret, arg, -1); - } -} +void tcg_gen_add_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_sub_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_and_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_or_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_xor_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_shl_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_shr_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_sar_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_mul_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_neg_i32(TCGv_i32 ret, TCGv_i32 arg); +void tcg_gen_not_i32(TCGv_i32 ret, TCGv_i32 arg); /* 64 bit ops */ diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index a8cbad212d..ab6281ebec 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -351,11 +351,28 @@ void tcg_gen_plugin_cb_end(void) /* 32 bit ops */ +void tcg_gen_discard_i32(TCGv_i32 arg) +{ + tcg_gen_op1_i32(INDEX_op_discard, arg); +} + +void tcg_gen_mov_i32(TCGv_i32 ret, TCGv_i32 arg) +{ + if (ret != arg) { + tcg_gen_op2_i32(INDEX_op_mov_i32, ret, arg); + } +} + void tcg_gen_movi_i32(TCGv_i32 ret, int32_t arg) { tcg_gen_mov_i32(ret, tcg_constant_i32(arg)); } +void tcg_gen_add_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_add_i32, ret, arg1, arg2); +} + void tcg_gen_addi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) { /* some cases can be optimized here */ @@ -366,6 +383,11 @@ void tcg_gen_addi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) } } +void tcg_gen_sub_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_sub_i32, ret, arg1, arg2); +} + void tcg_gen_subfi_i32(TCGv_i32 ret, int32_t arg1, TCGv_i32 arg2) { if (arg1 == 0 && TCG_TARGET_HAS_neg_i32) { @@ -386,6 +408,20 @@ void tcg_gen_subi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) } } +void tcg_gen_neg_i32(TCGv_i32 ret, TCGv_i32 arg) +{ + if (TCG_TARGET_HAS_neg_i32) { + tcg_gen_op2_i32(INDEX_op_neg_i32, ret, arg); + } else { + tcg_gen_subfi_i32(ret, 0, arg); + } +} + +void tcg_gen_and_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_and_i32, ret, arg1, arg2); +} + void tcg_gen_andi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) { /* Some cases can be optimized here. */ @@ -414,6 +450,11 @@ void tcg_gen_andi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) tcg_gen_and_i32(ret, arg1, tcg_constant_i32(arg2)); } +void tcg_gen_or_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_or_i32, ret, arg1, arg2); +} + void tcg_gen_ori_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) { /* Some cases can be optimized here. */ @@ -426,6 +467,11 @@ void tcg_gen_ori_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) } } +void tcg_gen_xor_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_xor_i32, ret, arg1, arg2); +} + void tcg_gen_xori_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) { /* Some cases can be optimized here. */ @@ -439,6 +485,20 @@ void tcg_gen_xori_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) } } +void tcg_gen_not_i32(TCGv_i32 ret, TCGv_i32 arg) +{ + if (TCG_TARGET_HAS_not_i32) { + tcg_gen_op2_i32(INDEX_op_not_i32, ret, arg); + } else { + tcg_gen_xori_i32(ret, arg, -1); + } +} + +void tcg_gen_shl_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_shl_i32, ret, arg1, arg2); +} + void tcg_gen_shli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) { tcg_debug_assert(arg2 >= 0 && arg2 < 32); @@ -449,6 +509,11 @@ void tcg_gen_shli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) } } +void tcg_gen_shr_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_shr_i32, ret, arg1, arg2); +} + void tcg_gen_shri_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) { tcg_debug_assert(arg2 >= 0 && arg2 < 32); @@ -459,6 +524,11 @@ void tcg_gen_shri_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) } } +void tcg_gen_sar_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_sar_i32, ret, arg1, arg2); +} + void tcg_gen_sari_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) { tcg_debug_assert(arg2 >= 0 && arg2 < 32); @@ -527,6 +597,11 @@ void tcg_gen_negsetcondi_i32(TCGCond cond, TCGv_i32 ret, tcg_gen_negsetcond_i32(cond, ret, arg1, tcg_constant_i32(arg2)); } +void tcg_gen_mul_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_mul_i32, ret, arg1, arg2); +} + void tcg_gen_muli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) { if (arg2 == 0) { @@ -1385,6 +1460,47 @@ void tcg_gen_abs_i32(TCGv_i32 ret, TCGv_i32 a) tcg_temp_free_i32(t); } +void tcg_gen_ld8u_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset) +{ + tcg_gen_ldst_op_i32(INDEX_op_ld8u_i32, ret, arg2, offset); +} + +void tcg_gen_ld8s_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset) +{ + tcg_gen_ldst_op_i32(INDEX_op_ld8s_i32, ret, arg2, offset); +} + +void tcg_gen_ld16u_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset) +{ + tcg_gen_ldst_op_i32(INDEX_op_ld16u_i32, ret, arg2, offset); +} + +void tcg_gen_ld16s_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset) +{ + tcg_gen_ldst_op_i32(INDEX_op_ld16s_i32, ret, arg2, offset); +} + +void tcg_gen_ld_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset) +{ + tcg_gen_ldst_op_i32(INDEX_op_ld_i32, ret, arg2, offset); +} + +void tcg_gen_st8_i32(TCGv_i32 arg1, TCGv_ptr arg2, tcg_target_long offset) +{ + tcg_gen_ldst_op_i32(INDEX_op_st8_i32, arg1, arg2, offset); +} + +void tcg_gen_st16_i32(TCGv_i32 arg1, TCGv_ptr arg2, tcg_target_long offset) +{ + tcg_gen_ldst_op_i32(INDEX_op_st16_i32, arg1, arg2, offset); +} + +void tcg_gen_st_i32(TCGv_i32 arg1, TCGv_ptr arg2, tcg_target_long offset) +{ + tcg_gen_ldst_op_i32(INDEX_op_st_i32, arg1, arg2, offset); +} + + /* 64-bit ops */ #if TCG_TARGET_REG_BITS == 32 From e0de2f558067400e32fc1082468c0664314f3b34 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 29 Oct 2023 14:08:42 -0700 Subject: [PATCH 500/974] tcg: Move 64-bit expanders out of line This one is more complicated, combining 32-bit and 64-bit expansion with C if instead of preprocessor #if. Signed-off-by: Richard Henderson Message-Id: <20231029210848.78234-6-richard.henderson@linaro.org> --- include/tcg/tcg-op-common.h | 144 +--------------------- tcg/tcg-op.c | 231 +++++++++++++++++++++++++----------- 2 files changed, 169 insertions(+), 206 deletions(-) diff --git a/include/tcg/tcg-op-common.h b/include/tcg/tcg-op-common.h index cdaa1415d1..f5ec54f874 100644 --- a/include/tcg/tcg-op-common.h +++ b/include/tcg/tcg-op-common.h @@ -305,130 +305,6 @@ void tcg_gen_abs_i64(TCGv_i64, TCGv_i64); /* Replicate a value of size @vece from @in to all the lanes in @out */ void tcg_gen_dup_i64(unsigned vece, TCGv_i64 out, TCGv_i64 in); -#if TCG_TARGET_REG_BITS == 64 -static inline void tcg_gen_discard_i64(TCGv_i64 arg) -{ - tcg_gen_op1_i64(INDEX_op_discard, arg); -} - -static inline void tcg_gen_mov_i64(TCGv_i64 ret, TCGv_i64 arg) -{ - if (ret != arg) { - tcg_gen_op2_i64(INDEX_op_mov_i64, ret, arg); - } -} - -static inline void tcg_gen_ld8u_i64(TCGv_i64 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_ld8u_i64, ret, arg2, offset); -} - -static inline void tcg_gen_ld8s_i64(TCGv_i64 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_ld8s_i64, ret, arg2, offset); -} - -static inline void tcg_gen_ld16u_i64(TCGv_i64 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_ld16u_i64, ret, arg2, offset); -} - -static inline void tcg_gen_ld16s_i64(TCGv_i64 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_ld16s_i64, ret, arg2, offset); -} - -static inline void tcg_gen_ld32u_i64(TCGv_i64 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_ld32u_i64, ret, arg2, offset); -} - -static inline void tcg_gen_ld32s_i64(TCGv_i64 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_ld32s_i64, ret, arg2, offset); -} - -static inline void tcg_gen_ld_i64(TCGv_i64 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_ld_i64, ret, arg2, offset); -} - -static inline void tcg_gen_st8_i64(TCGv_i64 arg1, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_st8_i64, arg1, arg2, offset); -} - -static inline void tcg_gen_st16_i64(TCGv_i64 arg1, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_st16_i64, arg1, arg2, offset); -} - -static inline void tcg_gen_st32_i64(TCGv_i64 arg1, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_st32_i64, arg1, arg2, offset); -} - -static inline void tcg_gen_st_i64(TCGv_i64 arg1, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_st_i64, arg1, arg2, offset); -} - -static inline void tcg_gen_add_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - tcg_gen_op3_i64(INDEX_op_add_i64, ret, arg1, arg2); -} - -static inline void tcg_gen_sub_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - tcg_gen_op3_i64(INDEX_op_sub_i64, ret, arg1, arg2); -} - -static inline void tcg_gen_and_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - tcg_gen_op3_i64(INDEX_op_and_i64, ret, arg1, arg2); -} - -static inline void tcg_gen_or_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - tcg_gen_op3_i64(INDEX_op_or_i64, ret, arg1, arg2); -} - -static inline void tcg_gen_xor_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - tcg_gen_op3_i64(INDEX_op_xor_i64, ret, arg1, arg2); -} - -static inline void tcg_gen_shl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - tcg_gen_op3_i64(INDEX_op_shl_i64, ret, arg1, arg2); -} - -static inline void tcg_gen_shr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - tcg_gen_op3_i64(INDEX_op_shr_i64, ret, arg1, arg2); -} - -static inline void tcg_gen_sar_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - tcg_gen_op3_i64(INDEX_op_sar_i64, ret, arg1, arg2); -} - -static inline void tcg_gen_mul_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - tcg_gen_op3_i64(INDEX_op_mul_i64, ret, arg1, arg2); -} -#else /* TCG_TARGET_REG_BITS == 32 */ void tcg_gen_st8_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset); void tcg_gen_st16_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset); void tcg_gen_st32_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset); @@ -453,16 +329,8 @@ void tcg_gen_shl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); void tcg_gen_shr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); void tcg_gen_sar_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); void tcg_gen_mul_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -#endif /* TCG_TARGET_REG_BITS */ +void tcg_gen_neg_i64(TCGv_i64 ret, TCGv_i64 arg); -static inline void tcg_gen_neg_i64(TCGv_i64 ret, TCGv_i64 arg) -{ - if (TCG_TARGET_HAS_neg_i64) { - tcg_gen_op2_i64(INDEX_op_neg_i64, ret, arg); - } else { - tcg_gen_subfi_i64(ret, 0, arg); - } -} /* Size changing operations. */ @@ -473,19 +341,17 @@ void tcg_gen_extrl_i64_i32(TCGv_i32 ret, TCGv_i64 arg); void tcg_gen_extrh_i64_i32(TCGv_i32 ret, TCGv_i64 arg); void tcg_gen_extr_i64_i32(TCGv_i32 lo, TCGv_i32 hi, TCGv_i64 arg); void tcg_gen_extr32_i64(TCGv_i64 lo, TCGv_i64 hi, TCGv_i64 arg); +void tcg_gen_concat32_i64(TCGv_i64 ret, TCGv_i64 lo, TCGv_i64 hi); -void tcg_gen_mov_i128(TCGv_i128 dst, TCGv_i128 src); void tcg_gen_extr_i128_i64(TCGv_i64 lo, TCGv_i64 hi, TCGv_i128 arg); void tcg_gen_concat_i64_i128(TCGv_i128 ret, TCGv_i64 lo, TCGv_i64 hi); +/* 128 bit ops */ + +void tcg_gen_mov_i128(TCGv_i128 dst, TCGv_i128 src); void tcg_gen_ld_i128(TCGv_i128 ret, TCGv_ptr base, tcg_target_long offset); void tcg_gen_st_i128(TCGv_i128 val, TCGv_ptr base, tcg_target_long offset); -static inline void tcg_gen_concat32_i64(TCGv_i64 ret, TCGv_i64 lo, TCGv_i64 hi) -{ - tcg_gen_deposit_i64(ret, lo, hi, 32, 32); -} - /* Local load/store bit ops */ void tcg_gen_qemu_ld_i32_chk(TCGv_i32, TCGTemp *, TCGArg, MemOp, TCGType); diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index ab6281ebec..579a2aab15 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -1503,152 +1503,238 @@ void tcg_gen_st_i32(TCGv_i32 arg1, TCGv_ptr arg2, tcg_target_long offset) /* 64-bit ops */ -#if TCG_TARGET_REG_BITS == 32 -/* These are all inline for TCG_TARGET_REG_BITS == 64. */ - void tcg_gen_discard_i64(TCGv_i64 arg) { - tcg_gen_discard_i32(TCGV_LOW(arg)); - tcg_gen_discard_i32(TCGV_HIGH(arg)); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_op1_i64(INDEX_op_discard, arg); + } else { + tcg_gen_discard_i32(TCGV_LOW(arg)); + tcg_gen_discard_i32(TCGV_HIGH(arg)); + } } void tcg_gen_mov_i64(TCGv_i64 ret, TCGv_i64 arg) { - TCGTemp *ts = tcgv_i64_temp(arg); - - /* Canonicalize TCGv_i64 TEMP_CONST into TCGv_i32 TEMP_CONST. */ - if (ts->kind == TEMP_CONST) { - tcg_gen_movi_i64(ret, ts->val); + if (ret == arg) { + return; + } + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_op2_i64(INDEX_op_mov_i64, ret, arg); } else { - tcg_gen_mov_i32(TCGV_LOW(ret), TCGV_LOW(arg)); - tcg_gen_mov_i32(TCGV_HIGH(ret), TCGV_HIGH(arg)); + TCGTemp *ts = tcgv_i64_temp(arg); + + /* Canonicalize TCGv_i64 TEMP_CONST into TCGv_i32 TEMP_CONST. */ + if (ts->kind == TEMP_CONST) { + tcg_gen_movi_i64(ret, ts->val); + } else { + tcg_gen_mov_i32(TCGV_LOW(ret), TCGV_LOW(arg)); + tcg_gen_mov_i32(TCGV_HIGH(ret), TCGV_HIGH(arg)); + } } } void tcg_gen_movi_i64(TCGv_i64 ret, int64_t arg) { - tcg_gen_movi_i32(TCGV_LOW(ret), arg); - tcg_gen_movi_i32(TCGV_HIGH(ret), arg >> 32); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_mov_i64(ret, tcg_constant_i64(arg)); + } else { + tcg_gen_movi_i32(TCGV_LOW(ret), arg); + tcg_gen_movi_i32(TCGV_HIGH(ret), arg >> 32); + } } void tcg_gen_ld8u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) { - tcg_gen_ld8u_i32(TCGV_LOW(ret), arg2, offset); - tcg_gen_movi_i32(TCGV_HIGH(ret), 0); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_ldst_op_i64(INDEX_op_ld8u_i64, ret, arg2, offset); + } else { + tcg_gen_ld8u_i32(TCGV_LOW(ret), arg2, offset); + tcg_gen_movi_i32(TCGV_HIGH(ret), 0); + } } void tcg_gen_ld8s_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) { - tcg_gen_ld8s_i32(TCGV_LOW(ret), arg2, offset); - tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_ldst_op_i64(INDEX_op_ld8s_i64, ret, arg2, offset); + } else { + tcg_gen_ld8s_i32(TCGV_LOW(ret), arg2, offset); + tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); + } } void tcg_gen_ld16u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) { - tcg_gen_ld16u_i32(TCGV_LOW(ret), arg2, offset); - tcg_gen_movi_i32(TCGV_HIGH(ret), 0); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_ldst_op_i64(INDEX_op_ld16u_i64, ret, arg2, offset); + } else { + tcg_gen_ld16u_i32(TCGV_LOW(ret), arg2, offset); + tcg_gen_movi_i32(TCGV_HIGH(ret), 0); + } } void tcg_gen_ld16s_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) { - tcg_gen_ld16s_i32(TCGV_LOW(ret), arg2, offset); - tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_ldst_op_i64(INDEX_op_ld16s_i64, ret, arg2, offset); + } else { + tcg_gen_ld16s_i32(TCGV_LOW(ret), arg2, offset); + tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); + } } void tcg_gen_ld32u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) { - tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset); - tcg_gen_movi_i32(TCGV_HIGH(ret), 0); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_ldst_op_i64(INDEX_op_ld32u_i64, ret, arg2, offset); + } else { + tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset); + tcg_gen_movi_i32(TCGV_HIGH(ret), 0); + } } void tcg_gen_ld32s_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) { - tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset); - tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_ldst_op_i64(INDEX_op_ld32s_i64, ret, arg2, offset); + } else { + tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset); + tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); + } } void tcg_gen_ld_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) { - /* Since arg2 and ret have different types, - they cannot be the same temporary */ -#if HOST_BIG_ENDIAN - tcg_gen_ld_i32(TCGV_HIGH(ret), arg2, offset); - tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset + 4); -#else - tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset); - tcg_gen_ld_i32(TCGV_HIGH(ret), arg2, offset + 4); -#endif + /* + * For 32-bit host, since arg2 and ret have different types, + * they cannot be the same temporary -- no chance of overlap. + */ + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_ldst_op_i64(INDEX_op_ld_i64, ret, arg2, offset); + } else if (HOST_BIG_ENDIAN) { + tcg_gen_ld_i32(TCGV_HIGH(ret), arg2, offset); + tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset + 4); + } else { + tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset); + tcg_gen_ld_i32(TCGV_HIGH(ret), arg2, offset + 4); + } } void tcg_gen_st8_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset) { - tcg_gen_st8_i32(TCGV_LOW(arg1), arg2, offset); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_ldst_op_i64(INDEX_op_st8_i64, arg1, arg2, offset); + } else { + tcg_gen_st8_i32(TCGV_LOW(arg1), arg2, offset); + } } void tcg_gen_st16_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset) { - tcg_gen_st16_i32(TCGV_LOW(arg1), arg2, offset); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_ldst_op_i64(INDEX_op_st16_i64, arg1, arg2, offset); + } else { + tcg_gen_st16_i32(TCGV_LOW(arg1), arg2, offset); + } } void tcg_gen_st32_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset) { - tcg_gen_st_i32(TCGV_LOW(arg1), arg2, offset); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_ldst_op_i64(INDEX_op_st32_i64, arg1, arg2, offset); + } else { + tcg_gen_st_i32(TCGV_LOW(arg1), arg2, offset); + } } void tcg_gen_st_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset) { -#if HOST_BIG_ENDIAN - tcg_gen_st_i32(TCGV_HIGH(arg1), arg2, offset); - tcg_gen_st_i32(TCGV_LOW(arg1), arg2, offset + 4); -#else - tcg_gen_st_i32(TCGV_LOW(arg1), arg2, offset); - tcg_gen_st_i32(TCGV_HIGH(arg1), arg2, offset + 4); -#endif + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_ldst_op_i64(INDEX_op_st_i64, arg1, arg2, offset); + } else if (HOST_BIG_ENDIAN) { + tcg_gen_st_i32(TCGV_HIGH(arg1), arg2, offset); + tcg_gen_st_i32(TCGV_LOW(arg1), arg2, offset + 4); + } else { + tcg_gen_st_i32(TCGV_LOW(arg1), arg2, offset); + tcg_gen_st_i32(TCGV_HIGH(arg1), arg2, offset + 4); + } } void tcg_gen_add_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - tcg_gen_add2_i32(TCGV_LOW(ret), TCGV_HIGH(ret), TCGV_LOW(arg1), - TCGV_HIGH(arg1), TCGV_LOW(arg2), TCGV_HIGH(arg2)); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_op3_i64(INDEX_op_add_i64, ret, arg1, arg2); + } else { + tcg_gen_add2_i32(TCGV_LOW(ret), TCGV_HIGH(ret), TCGV_LOW(arg1), + TCGV_HIGH(arg1), TCGV_LOW(arg2), TCGV_HIGH(arg2)); + } } void tcg_gen_sub_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - tcg_gen_sub2_i32(TCGV_LOW(ret), TCGV_HIGH(ret), TCGV_LOW(arg1), - TCGV_HIGH(arg1), TCGV_LOW(arg2), TCGV_HIGH(arg2)); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_op3_i64(INDEX_op_sub_i64, ret, arg1, arg2); + } else { + tcg_gen_sub2_i32(TCGV_LOW(ret), TCGV_HIGH(ret), TCGV_LOW(arg1), + TCGV_HIGH(arg1), TCGV_LOW(arg2), TCGV_HIGH(arg2)); + } } void tcg_gen_and_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - tcg_gen_and_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); - tcg_gen_and_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_op3_i64(INDEX_op_and_i64, ret, arg1, arg2); + } else { + tcg_gen_and_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); + tcg_gen_and_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); + } } void tcg_gen_or_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - tcg_gen_or_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); - tcg_gen_or_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_op3_i64(INDEX_op_or_i64, ret, arg1, arg2); + } else { + tcg_gen_or_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); + tcg_gen_or_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); + } } void tcg_gen_xor_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - tcg_gen_xor_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); - tcg_gen_xor_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_op3_i64(INDEX_op_xor_i64, ret, arg1, arg2); + } else { + tcg_gen_xor_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); + tcg_gen_xor_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); + } } void tcg_gen_shl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - gen_helper_shl_i64(ret, arg1, arg2); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_op3_i64(INDEX_op_shl_i64, ret, arg1, arg2); + } else { + gen_helper_shl_i64(ret, arg1, arg2); + } } void tcg_gen_shr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - gen_helper_shr_i64(ret, arg1, arg2); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_op3_i64(INDEX_op_shr_i64, ret, arg1, arg2); + } else { + gen_helper_shr_i64(ret, arg1, arg2); + } } void tcg_gen_sar_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - gen_helper_sar_i64(ret, arg1, arg2); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_op3_i64(INDEX_op_sar_i64, ret, arg1, arg2); + } else { + gen_helper_sar_i64(ret, arg1, arg2); + } } void tcg_gen_mul_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) @@ -1656,6 +1742,12 @@ void tcg_gen_mul_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) TCGv_i64 t0; TCGv_i32 t1; + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_op3_i64(INDEX_op_mul_i64, ret, arg1, arg2); + return; + } + + t0 = tcg_temp_ebb_new_i64(); t1 = tcg_temp_ebb_new_i32(); @@ -1672,15 +1764,6 @@ void tcg_gen_mul_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) tcg_temp_free_i32(t1); } -#else - -void tcg_gen_movi_i64(TCGv_i64 ret, int64_t arg) -{ - tcg_gen_mov_i64(ret, tcg_constant_i64(arg)); -} - -#endif /* TCG_TARGET_REG_SIZE == 32 */ - void tcg_gen_addi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) { /* some cases can be optimized here */ @@ -1723,6 +1806,15 @@ void tcg_gen_subi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) } } +void tcg_gen_neg_i64(TCGv_i64 ret, TCGv_i64 arg) +{ + if (TCG_TARGET_HAS_neg_i64) { + tcg_gen_op2_i64(INDEX_op_neg_i64, ret, arg); + } else { + tcg_gen_subfi_i64(ret, 0, arg); + } +} + void tcg_gen_andi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) { if (TCG_TARGET_REG_BITS == 32) { @@ -3218,6 +3310,11 @@ void tcg_gen_extr32_i64(TCGv_i64 lo, TCGv_i64 hi, TCGv_i64 arg) tcg_gen_shri_i64(hi, arg, 32); } +void tcg_gen_concat32_i64(TCGv_i64 ret, TCGv_i64 lo, TCGv_i64 hi) +{ + tcg_gen_deposit_i64(ret, lo, hi, 32, 32); +} + void tcg_gen_extr_i128_i64(TCGv_i64 lo, TCGv_i64 hi, TCGv_i128 arg) { tcg_gen_mov_i64(lo, TCGV128_LOW(arg)); From 27c758fd22a825783228dbdd339fd55da08a5ff3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 29 Oct 2023 14:08:43 -0700 Subject: [PATCH 501/974] tcg: Move vec_gen_* declarations to tcg-internal.h These are used within tcg-op-vec.c and tcg/host/tcg-target.c.inc. There are no uses outside tcg/. Signed-off-by: Richard Henderson Message-Id: <20231029210848.78234-7-richard.henderson@linaro.org> --- include/tcg/tcg-op-common.h | 4 ---- tcg/tcg-internal.h | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/tcg/tcg-op-common.h b/include/tcg/tcg-op-common.h index f5ec54f874..3f8b214376 100644 --- a/include/tcg/tcg-op-common.h +++ b/include/tcg/tcg-op-common.h @@ -21,10 +21,6 @@ void tcg_gen_op4(TCGOpcode, TCGArg, TCGArg, TCGArg, TCGArg); void tcg_gen_op5(TCGOpcode, TCGArg, TCGArg, TCGArg, TCGArg, TCGArg); void tcg_gen_op6(TCGOpcode, TCGArg, TCGArg, TCGArg, TCGArg, TCGArg, TCGArg); -void vec_gen_2(TCGOpcode, TCGType, unsigned, TCGArg, TCGArg); -void vec_gen_3(TCGOpcode, TCGType, unsigned, TCGArg, TCGArg, TCGArg); -void vec_gen_4(TCGOpcode, TCGType, unsigned, TCGArg, TCGArg, TCGArg, TCGArg); - void tcg_gen_op1_i32(TCGOpcode, TCGv_i32); void tcg_gen_op1_i64(TCGOpcode, TCGv_i64); void tcg_gen_op1i(TCGOpcode, TCGArg); diff --git a/tcg/tcg-internal.h b/tcg/tcg-internal.h index 40a69e6e6e..f18d282abb 100644 --- a/tcg/tcg-internal.h +++ b/tcg/tcg-internal.h @@ -83,4 +83,8 @@ static inline TCGv_i64 TCGV128_HIGH(TCGv_i128 t) bool tcg_target_has_memory_bswap(MemOp memop); +void vec_gen_2(TCGOpcode, TCGType, unsigned, TCGArg, TCGArg); +void vec_gen_3(TCGOpcode, TCGType, unsigned, TCGArg, TCGArg, TCGArg); +void vec_gen_4(TCGOpcode, TCGType, unsigned, TCGArg, TCGArg, TCGArg, TCGArg); + #endif /* TCG_INTERNAL_H */ From 1d67bf545fd6321d14150eac00851782073a17e9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 29 Oct 2023 14:08:44 -0700 Subject: [PATCH 502/974] tcg: Move tcg_gen_opN declarations to tcg-internal.h These are used within tcg-op.c and tcg-op-ldst.c. There are no uses outside tcg/. Signed-off-by: Richard Henderson Message-Id: <20231029210848.78234-8-richard.henderson@linaro.org> --- include/tcg/tcg-op-common.h | 7 ------- tcg/tcg-internal.h | 7 +++++++ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/tcg/tcg-op-common.h b/include/tcg/tcg-op-common.h index 3f8b214376..b922545118 100644 --- a/include/tcg/tcg-op-common.h +++ b/include/tcg/tcg-op-common.h @@ -14,13 +14,6 @@ /* Basic output routines. Not for general consumption. */ -void tcg_gen_op1(TCGOpcode, TCGArg); -void tcg_gen_op2(TCGOpcode, TCGArg, TCGArg); -void tcg_gen_op3(TCGOpcode, TCGArg, TCGArg, TCGArg); -void tcg_gen_op4(TCGOpcode, TCGArg, TCGArg, TCGArg, TCGArg); -void tcg_gen_op5(TCGOpcode, TCGArg, TCGArg, TCGArg, TCGArg, TCGArg); -void tcg_gen_op6(TCGOpcode, TCGArg, TCGArg, TCGArg, TCGArg, TCGArg, TCGArg); - void tcg_gen_op1_i32(TCGOpcode, TCGv_i32); void tcg_gen_op1_i64(TCGOpcode, TCGv_i64); void tcg_gen_op1i(TCGOpcode, TCGArg); diff --git a/tcg/tcg-internal.h b/tcg/tcg-internal.h index f18d282abb..c9ac34fc3d 100644 --- a/tcg/tcg-internal.h +++ b/tcg/tcg-internal.h @@ -83,6 +83,13 @@ static inline TCGv_i64 TCGV128_HIGH(TCGv_i128 t) bool tcg_target_has_memory_bswap(MemOp memop); +void tcg_gen_op1(TCGOpcode, TCGArg); +void tcg_gen_op2(TCGOpcode, TCGArg, TCGArg); +void tcg_gen_op3(TCGOpcode, TCGArg, TCGArg, TCGArg); +void tcg_gen_op4(TCGOpcode, TCGArg, TCGArg, TCGArg, TCGArg); +void tcg_gen_op5(TCGOpcode, TCGArg, TCGArg, TCGArg, TCGArg, TCGArg); +void tcg_gen_op6(TCGOpcode, TCGArg, TCGArg, TCGArg, TCGArg, TCGArg, TCGArg); + void vec_gen_2(TCGOpcode, TCGType, unsigned, TCGArg, TCGArg); void vec_gen_3(TCGOpcode, TCGType, unsigned, TCGArg, TCGArg, TCGArg); void vec_gen_4(TCGOpcode, TCGType, unsigned, TCGArg, TCGArg, TCGArg, TCGArg); From 17b9fadb1d93edd79ac1aec7f48b66ceff325cbd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 29 Oct 2023 14:08:45 -0700 Subject: [PATCH 503/974] tcg: Unexport tcg_gen_op*_{i32,i64} These functions are no longer used outside tcg-op.c. There are several that are completely unused, so remove them. Signed-off-by: Richard Henderson Message-Id: <20231029210848.78234-9-richard.henderson@linaro.org> --- include/tcg/tcg-op-common.h | 47 ------------- tcg/tcg-op.c | 131 ++++++++++++++---------------------- 2 files changed, 52 insertions(+), 126 deletions(-) diff --git a/include/tcg/tcg-op-common.h b/include/tcg/tcg-op-common.h index b922545118..760c67683b 100644 --- a/include/tcg/tcg-op-common.h +++ b/include/tcg/tcg-op-common.h @@ -12,53 +12,6 @@ #include "exec/helper-proto-common.h" #include "exec/helper-gen-common.h" -/* Basic output routines. Not for general consumption. */ - -void tcg_gen_op1_i32(TCGOpcode, TCGv_i32); -void tcg_gen_op1_i64(TCGOpcode, TCGv_i64); -void tcg_gen_op1i(TCGOpcode, TCGArg); -void tcg_gen_op2_i32(TCGOpcode, TCGv_i32, TCGv_i32); -void tcg_gen_op2_i64(TCGOpcode, TCGv_i64, TCGv_i64); -void tcg_gen_op2i_i32(TCGOpcode, TCGv_i32, TCGArg); -void tcg_gen_op2i_i64(TCGOpcode, TCGv_i64, TCGArg); -void tcg_gen_op2ii(TCGOpcode, TCGArg, TCGArg); -void tcg_gen_op3_i32(TCGOpcode, TCGv_i32, TCGv_i32, TCGv_i32); -void tcg_gen_op3_i64(TCGOpcode, TCGv_i64, TCGv_i64, TCGv_i64); -void tcg_gen_op3i_i32(TCGOpcode, TCGv_i32, TCGv_i32, TCGArg); -void tcg_gen_op3i_i64(TCGOpcode, TCGv_i64, TCGv_i64, TCGArg); -void tcg_gen_ldst_op_i32(TCGOpcode, TCGv_i32, TCGv_ptr, TCGArg); -void tcg_gen_ldst_op_i64(TCGOpcode, TCGv_i64, TCGv_ptr, TCGArg); -void tcg_gen_op4_i32(TCGOpcode, TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32); -void tcg_gen_op4_i64(TCGOpcode, TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64); -void tcg_gen_op4i_i32(TCGOpcode, TCGv_i32, TCGv_i32, TCGv_i32, TCGArg); -void tcg_gen_op4i_i64(TCGOpcode, TCGv_i64, TCGv_i64, TCGv_i64, TCGArg); -void tcg_gen_op4ii_i32(TCGOpcode, TCGv_i32, TCGv_i32, TCGArg, TCGArg); -void tcg_gen_op4ii_i64(TCGOpcode, TCGv_i64, TCGv_i64, TCGArg, TCGArg); -void tcg_gen_op5_i32(TCGOpcode, TCGv_i32, TCGv_i32, TCGv_i32, - TCGv_i32, TCGv_i32); -void tcg_gen_op5_i64(TCGOpcode, TCGv_i64, TCGv_i64, TCGv_i64, - TCGv_i64, TCGv_i64); -void tcg_gen_op5i_i32(TCGOpcode, TCGv_i32, TCGv_i32, TCGv_i32, - TCGv_i32, TCGArg); -void tcg_gen_op5i_i64(TCGOpcode, TCGv_i64, TCGv_i64, TCGv_i64, - TCGv_i64, TCGArg); -void tcg_gen_op5ii_i32(TCGOpcode, TCGv_i32, TCGv_i32, TCGv_i32, - TCGArg, TCGArg); -void tcg_gen_op5ii_i64(TCGOpcode, TCGv_i64, TCGv_i64, TCGv_i64, - TCGArg, TCGArg); -void tcg_gen_op6_i32(TCGOpcode, TCGv_i32, TCGv_i32, TCGv_i32, - TCGv_i32, TCGv_i32, TCGv_i32); -void tcg_gen_op6_i64(TCGOpcode, TCGv_i64, TCGv_i64, TCGv_i64, - TCGv_i64, TCGv_i64, TCGv_i64); -void tcg_gen_op6i_i32(TCGOpcode, TCGv_i32, TCGv_i32, TCGv_i32, - TCGv_i32, TCGv_i32, TCGArg); -void tcg_gen_op6i_i64(TCGOpcode, TCGv_i64, TCGv_i64, TCGv_i64, - TCGv_i64, TCGv_i64, TCGArg); -void tcg_gen_op6ii_i32(TCGOpcode, TCGv_i32, TCGv_i32, TCGv_i32, - TCGv_i32, TCGArg, TCGArg); -void tcg_gen_op6ii_i64(TCGOpcode, TCGv_i64, TCGv_i64, TCGv_i64, - TCGv_i64, TCGArg, TCGArg); - /* Generic ops. */ void gen_set_label(TCGLabel *l); diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 579a2aab15..9aba103590 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -100,204 +100,177 @@ void NI tcg_gen_op6(TCGOpcode opc, TCGArg a1, TCGArg a2, TCGArg a3, # define DNI #endif -void DNI tcg_gen_op1_i32(TCGOpcode opc, TCGv_i32 a1) +static void DNI tcg_gen_op1_i32(TCGOpcode opc, TCGv_i32 a1) { tcg_gen_op1(opc, tcgv_i32_arg(a1)); } -void DNI tcg_gen_op1_i64(TCGOpcode opc, TCGv_i64 a1) +static void DNI tcg_gen_op1_i64(TCGOpcode opc, TCGv_i64 a1) { tcg_gen_op1(opc, tcgv_i64_arg(a1)); } -void DNI tcg_gen_op1i(TCGOpcode opc, TCGArg a1) +static void DNI tcg_gen_op1i(TCGOpcode opc, TCGArg a1) { tcg_gen_op1(opc, a1); } -void DNI tcg_gen_op2_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2) +static void DNI tcg_gen_op2_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2) { tcg_gen_op2(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2)); } -void DNI tcg_gen_op2_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2) +static void DNI tcg_gen_op2_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2) { tcg_gen_op2(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2)); } -void DNI tcg_gen_op2i_i32(TCGOpcode opc, TCGv_i32 a1, TCGArg a2) -{ - tcg_gen_op2(opc, tcgv_i32_arg(a1), a2); -} - -void DNI tcg_gen_op2i_i64(TCGOpcode opc, TCGv_i64 a1, TCGArg a2) -{ - tcg_gen_op2(opc, tcgv_i64_arg(a1), a2); -} - -void DNI tcg_gen_op2ii(TCGOpcode opc, TCGArg a1, TCGArg a2) -{ - tcg_gen_op2(opc, a1, a2); -} - -void DNI tcg_gen_op3_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, TCGv_i32 a3) +static void DNI tcg_gen_op3_i32(TCGOpcode opc, TCGv_i32 a1, + TCGv_i32 a2, TCGv_i32 a3) { tcg_gen_op3(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), tcgv_i32_arg(a3)); } -void DNI tcg_gen_op3_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, TCGv_i64 a3) +static void DNI tcg_gen_op3_i64(TCGOpcode opc, TCGv_i64 a1, + TCGv_i64 a2, TCGv_i64 a3) { tcg_gen_op3(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), tcgv_i64_arg(a3)); } -void DNI tcg_gen_op3i_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, TCGArg a3) +static void DNI tcg_gen_op3i_i32(TCGOpcode opc, TCGv_i32 a1, + TCGv_i32 a2, TCGArg a3) { tcg_gen_op3(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), a3); } -void DNI tcg_gen_op3i_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, TCGArg a3) +static void DNI tcg_gen_op3i_i64(TCGOpcode opc, TCGv_i64 a1, + TCGv_i64 a2, TCGArg a3) { tcg_gen_op3(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), a3); } -void DNI tcg_gen_ldst_op_i32(TCGOpcode opc, TCGv_i32 val, - TCGv_ptr base, TCGArg offset) +static void DNI tcg_gen_ldst_op_i32(TCGOpcode opc, TCGv_i32 val, + TCGv_ptr base, TCGArg offset) { tcg_gen_op3(opc, tcgv_i32_arg(val), tcgv_ptr_arg(base), offset); } -void DNI tcg_gen_ldst_op_i64(TCGOpcode opc, TCGv_i64 val, - TCGv_ptr base, TCGArg offset) +static void DNI tcg_gen_ldst_op_i64(TCGOpcode opc, TCGv_i64 val, + TCGv_ptr base, TCGArg offset) { tcg_gen_op3(opc, tcgv_i64_arg(val), tcgv_ptr_arg(base), offset); } -void DNI tcg_gen_op4_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, - TCGv_i32 a3, TCGv_i32 a4) +static void DNI tcg_gen_op4_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGv_i32 a4) { tcg_gen_op4(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), tcgv_i32_arg(a3), tcgv_i32_arg(a4)); } -void DNI tcg_gen_op4_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, - TCGv_i64 a3, TCGv_i64 a4) +static void DNI tcg_gen_op4_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGv_i64 a3, TCGv_i64 a4) { tcg_gen_op4(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), tcgv_i64_arg(a3), tcgv_i64_arg(a4)); } -void DNI tcg_gen_op4i_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, - TCGv_i32 a3, TCGArg a4) +static void DNI tcg_gen_op4i_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGArg a4) { tcg_gen_op4(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), tcgv_i32_arg(a3), a4); } -void DNI tcg_gen_op4i_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, - TCGv_i64 a3, TCGArg a4) +static void DNI tcg_gen_op4i_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGv_i64 a3, TCGArg a4) { tcg_gen_op4(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), tcgv_i64_arg(a3), a4); } -void DNI tcg_gen_op4ii_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, - TCGArg a3, TCGArg a4) +static void DNI tcg_gen_op4ii_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGArg a3, TCGArg a4) { tcg_gen_op4(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), a3, a4); } -void DNI tcg_gen_op4ii_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, - TCGArg a3, TCGArg a4) +static void DNI tcg_gen_op4ii_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGArg a3, TCGArg a4) { tcg_gen_op4(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), a3, a4); } -void DNI tcg_gen_op5_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, - TCGv_i32 a3, TCGv_i32 a4, TCGv_i32 a5) +static void DNI tcg_gen_op5_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGv_i32 a4, TCGv_i32 a5) { tcg_gen_op5(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), tcgv_i32_arg(a3), tcgv_i32_arg(a4), tcgv_i32_arg(a5)); } -void DNI tcg_gen_op5_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, - TCGv_i64 a3, TCGv_i64 a4, TCGv_i64 a5) +static void DNI tcg_gen_op5_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGv_i64 a3, TCGv_i64 a4, TCGv_i64 a5) { tcg_gen_op5(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), tcgv_i64_arg(a3), tcgv_i64_arg(a4), tcgv_i64_arg(a5)); } -void DNI tcg_gen_op5i_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, - TCGv_i32 a3, TCGv_i32 a4, TCGArg a5) -{ - tcg_gen_op5(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), - tcgv_i32_arg(a3), tcgv_i32_arg(a4), a5); -} - -void DNI tcg_gen_op5i_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, - TCGv_i64 a3, TCGv_i64 a4, TCGArg a5) -{ - tcg_gen_op5(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), - tcgv_i64_arg(a3), tcgv_i64_arg(a4), a5); -} - -void DNI tcg_gen_op5ii_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, - TCGv_i32 a3, TCGArg a4, TCGArg a5) +static void DNI tcg_gen_op5ii_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGArg a4, TCGArg a5) { tcg_gen_op5(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), tcgv_i32_arg(a3), a4, a5); } -void DNI tcg_gen_op5ii_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, - TCGv_i64 a3, TCGArg a4, TCGArg a5) +static void DNI tcg_gen_op5ii_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGv_i64 a3, TCGArg a4, TCGArg a5) { tcg_gen_op5(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), tcgv_i64_arg(a3), a4, a5); } -void DNI tcg_gen_op6_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, TCGv_i32 a3, - TCGv_i32 a4, TCGv_i32 a5, TCGv_i32 a6) +static void DNI tcg_gen_op6_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGv_i32 a4, + TCGv_i32 a5, TCGv_i32 a6) { tcg_gen_op6(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), tcgv_i32_arg(a3), tcgv_i32_arg(a4), tcgv_i32_arg(a5), tcgv_i32_arg(a6)); } -void DNI tcg_gen_op6_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, TCGv_i64 a3, - TCGv_i64 a4, TCGv_i64 a5, TCGv_i64 a6) +static void DNI tcg_gen_op6_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGv_i64 a3, TCGv_i64 a4, + TCGv_i64 a5, TCGv_i64 a6) { tcg_gen_op6(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), tcgv_i64_arg(a3), tcgv_i64_arg(a4), tcgv_i64_arg(a5), tcgv_i64_arg(a6)); } -void DNI tcg_gen_op6i_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, TCGv_i32 a3, - TCGv_i32 a4, TCGv_i32 a5, TCGArg a6) +static void DNI tcg_gen_op6i_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGv_i32 a4, + TCGv_i32 a5, TCGArg a6) { tcg_gen_op6(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), tcgv_i32_arg(a3), tcgv_i32_arg(a4), tcgv_i32_arg(a5), a6); } -void DNI tcg_gen_op6i_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, TCGv_i64 a3, - TCGv_i64 a4, TCGv_i64 a5, TCGArg a6) +static void DNI tcg_gen_op6i_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGv_i64 a3, TCGv_i64 a4, + TCGv_i64 a5, TCGArg a6) { tcg_gen_op6(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), tcgv_i64_arg(a3), tcgv_i64_arg(a4), tcgv_i64_arg(a5), a6); } -void DNI tcg_gen_op6ii_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, - TCGv_i32 a3, TCGv_i32 a4, TCGArg a5, TCGArg a6) +static void DNI tcg_gen_op6ii_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGv_i32 a4, + TCGArg a5, TCGArg a6) { tcg_gen_op6(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), tcgv_i32_arg(a3), tcgv_i32_arg(a4), a5, a6); } -void DNI tcg_gen_op6ii_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, - TCGv_i64 a3, TCGv_i64 a4, TCGArg a5, TCGArg a6) -{ - tcg_gen_op6(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), - tcgv_i64_arg(a3), tcgv_i64_arg(a4), a5, a6); -} - /* Generic ops. */ void gen_set_label(TCGLabel *l) From 16edaee720139892dbff97cfcb89bf18eb70d227 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 29 Oct 2023 14:08:46 -0700 Subject: [PATCH 504/974] tcg: Move tcg_constant_* out of line Signed-off-by: Richard Henderson Message-Id: <20231029210848.78234-10-richard.henderson@linaro.org> --- include/tcg/tcg-op-common.h | 8 ++++++++ include/tcg/tcg.h | 26 -------------------------- tcg/tcg-internal.h | 7 +++++++ tcg/tcg.c | 15 +++++++++++++++ 4 files changed, 30 insertions(+), 26 deletions(-) diff --git a/include/tcg/tcg-op-common.h b/include/tcg/tcg-op-common.h index 760c67683b..dddf93067e 100644 --- a/include/tcg/tcg-op-common.h +++ b/include/tcg/tcg-op-common.h @@ -12,6 +12,11 @@ #include "exec/helper-proto-common.h" #include "exec/helper-gen-common.h" +TCGv_i32 tcg_constant_i32(int32_t val); +TCGv_i64 tcg_constant_i64(int64_t val); +TCGv_vec tcg_constant_vec(TCGType type, unsigned vece, int64_t val); +TCGv_vec tcg_constant_vec_matching(TCGv_vec match, unsigned vece, int64_t val); + /* Generic ops. */ void gen_set_label(TCGLabel *l); @@ -459,6 +464,9 @@ void tcg_gen_stl_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset, TCGType t); # define NAT TCGv_i64 #endif +TCGv_ptr tcg_constant_ptr_int(intptr_t x); +#define tcg_constant_ptr(X) tcg_constant_ptr_int((intptr_t)(X)) + static inline void tcg_gen_ld_ptr(TCGv_ptr r, TCGv_ptr a, intptr_t o) { glue(tcg_gen_ld_,PTR)((NAT)r, a, o); diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 3a4c0f124f..1ae131c242 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -934,32 +934,6 @@ void tcg_remove_ops_after(TCGOp *op); void tcg_optimize(TCGContext *s); -/* - * Locate or create a read-only temporary that is a constant. - * This kind of temporary need not be freed, but for convenience - * will be silently ignored by tcg_temp_free_*. - */ -TCGTemp *tcg_constant_internal(TCGType type, int64_t val); - -static inline TCGv_i32 tcg_constant_i32(int32_t val) -{ - return temp_tcgv_i32(tcg_constant_internal(TCG_TYPE_I32, val)); -} - -static inline TCGv_i64 tcg_constant_i64(int64_t val) -{ - return temp_tcgv_i64(tcg_constant_internal(TCG_TYPE_I64, val)); -} - -TCGv_vec tcg_constant_vec(TCGType type, unsigned vece, int64_t val); -TCGv_vec tcg_constant_vec_matching(TCGv_vec match, unsigned vece, int64_t val); - -#if UINTPTR_MAX == UINT32_MAX -# define tcg_constant_ptr(x) ((TCGv_ptr)tcg_constant_i32((intptr_t)(x))) -#else -# define tcg_constant_ptr(x) ((TCGv_ptr)tcg_constant_i64((intptr_t)(x))) -#endif - TCGLabel *gen_new_label(void); /** diff --git a/tcg/tcg-internal.h b/tcg/tcg-internal.h index c9ac34fc3d..6c9d9e48db 100644 --- a/tcg/tcg-internal.h +++ b/tcg/tcg-internal.h @@ -83,6 +83,13 @@ static inline TCGv_i64 TCGV128_HIGH(TCGv_i128 t) bool tcg_target_has_memory_bswap(MemOp memop); +/* + * Locate or create a read-only temporary that is a constant. + * This kind of temporary need not be freed, but for convenience + * will be silently ignored by tcg_temp_free_*. + */ +TCGTemp *tcg_constant_internal(TCGType type, int64_t val); + void tcg_gen_op1(TCGOpcode, TCGArg); void tcg_gen_op2(TCGOpcode, TCGArg, TCGArg); void tcg_gen_op3(TCGOpcode, TCGArg, TCGArg, TCGArg); diff --git a/tcg/tcg.c b/tcg/tcg.c index 6766b60b8a..ab0d227c00 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1806,6 +1806,21 @@ TCGTemp *tcg_constant_internal(TCGType type, int64_t val) return ts; } +TCGv_i32 tcg_constant_i32(int32_t val) +{ + return temp_tcgv_i32(tcg_constant_internal(TCG_TYPE_I32, val)); +} + +TCGv_i64 tcg_constant_i64(int64_t val) +{ + return temp_tcgv_i64(tcg_constant_internal(TCG_TYPE_I64, val)); +} + +TCGv_ptr tcg_constant_ptr_int(intptr_t val) +{ + return temp_tcgv_ptr(tcg_constant_internal(TCG_TYPE_PTR, val)); +} + TCGv_vec tcg_constant_vec(TCGType type, unsigned vece, int64_t val) { val = dup_const(vece, val); From 4643f3e07edd82f55788d55d30f58239c5468e6f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 29 Oct 2023 14:08:47 -0700 Subject: [PATCH 505/974] tcg: Move tcg_temp_new_*, tcg_global_mem_new_* out of line Signed-off-by: Richard Henderson Message-Id: <20231029210848.78234-11-richard.henderson@linaro.org> --- include/tcg/tcg-op-common.h | 11 ++++++ include/tcg/tcg-temp-internal.h | 27 +++----------- include/tcg/tcg.h | 51 -------------------------- tcg/tcg.c | 64 +++++++++++++++++++++++++++++++-- 4 files changed, 76 insertions(+), 77 deletions(-) diff --git a/include/tcg/tcg-op-common.h b/include/tcg/tcg-op-common.h index dddf93067e..2d932a515e 100644 --- a/include/tcg/tcg-op-common.h +++ b/include/tcg/tcg-op-common.h @@ -17,6 +17,17 @@ TCGv_i64 tcg_constant_i64(int64_t val); TCGv_vec tcg_constant_vec(TCGType type, unsigned vece, int64_t val); TCGv_vec tcg_constant_vec_matching(TCGv_vec match, unsigned vece, int64_t val); +TCGv_i32 tcg_temp_new_i32(void); +TCGv_i64 tcg_temp_new_i64(void); +TCGv_ptr tcg_temp_new_ptr(void); +TCGv_i128 tcg_temp_new_i128(void); +TCGv_vec tcg_temp_new_vec(TCGType type); +TCGv_vec tcg_temp_new_vec_matching(TCGv_vec match); + +TCGv_i32 tcg_global_mem_new_i32(TCGv_ptr reg, intptr_t off, const char *name); +TCGv_i64 tcg_global_mem_new_i64(TCGv_ptr reg, intptr_t off, const char *name); +TCGv_ptr tcg_global_mem_new_ptr(TCGv_ptr reg, intptr_t off, const char *name); + /* Generic ops. */ void gen_set_label(TCGLabel *l); diff --git a/include/tcg/tcg-temp-internal.h b/include/tcg/tcg-temp-internal.h index dded2917e5..2d45cc45d2 100644 --- a/include/tcg/tcg-temp-internal.h +++ b/include/tcg/tcg-temp-internal.h @@ -56,28 +56,9 @@ static inline void tcg_temp_free_vec(TCGv_vec arg) tcg_temp_free_internal(tcgv_vec_temp(arg)); } -static inline TCGv_i32 tcg_temp_ebb_new_i32(void) -{ - TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_I32, TEMP_EBB); - return temp_tcgv_i32(t); -} - -static inline TCGv_i64 tcg_temp_ebb_new_i64(void) -{ - TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_I64, TEMP_EBB); - return temp_tcgv_i64(t); -} - -static inline TCGv_i128 tcg_temp_ebb_new_i128(void) -{ - TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_I128, TEMP_EBB); - return temp_tcgv_i128(t); -} - -static inline TCGv_ptr tcg_temp_ebb_new_ptr(void) -{ - TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_PTR, TEMP_EBB); - return temp_tcgv_ptr(t); -} +TCGv_i32 tcg_temp_ebb_new_i32(void); +TCGv_i64 tcg_temp_ebb_new_i64(void); +TCGv_ptr tcg_temp_ebb_new_ptr(void); +TCGv_i128 tcg_temp_ebb_new_i128(void); #endif /* TCG_TEMP_FREE_H */ diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 1ae131c242..fc218fd381 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -795,57 +795,6 @@ void tb_target_set_jmp_target(const TranslationBlock *, int, void tcg_set_frame(TCGContext *s, TCGReg reg, intptr_t start, intptr_t size); -TCGTemp *tcg_global_mem_new_internal(TCGType, TCGv_ptr, - intptr_t, const char *); -TCGTemp *tcg_temp_new_internal(TCGType, TCGTempKind); -TCGv_vec tcg_temp_new_vec(TCGType type); -TCGv_vec tcg_temp_new_vec_matching(TCGv_vec match); - -static inline TCGv_i32 tcg_global_mem_new_i32(TCGv_ptr reg, intptr_t offset, - const char *name) -{ - TCGTemp *t = tcg_global_mem_new_internal(TCG_TYPE_I32, reg, offset, name); - return temp_tcgv_i32(t); -} - -static inline TCGv_i32 tcg_temp_new_i32(void) -{ - TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_I32, TEMP_TB); - return temp_tcgv_i32(t); -} - -static inline TCGv_i64 tcg_global_mem_new_i64(TCGv_ptr reg, intptr_t offset, - const char *name) -{ - TCGTemp *t = tcg_global_mem_new_internal(TCG_TYPE_I64, reg, offset, name); - return temp_tcgv_i64(t); -} - -static inline TCGv_i64 tcg_temp_new_i64(void) -{ - TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_I64, TEMP_TB); - return temp_tcgv_i64(t); -} - -static inline TCGv_i128 tcg_temp_new_i128(void) -{ - TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_I128, TEMP_TB); - return temp_tcgv_i128(t); -} - -static inline TCGv_ptr tcg_global_mem_new_ptr(TCGv_ptr reg, intptr_t offset, - const char *name) -{ - TCGTemp *t = tcg_global_mem_new_internal(TCG_TYPE_PTR, reg, offset, name); - return temp_tcgv_ptr(t); -} - -static inline TCGv_ptr tcg_temp_new_ptr(void) -{ - TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_PTR, TEMP_TB); - return temp_tcgv_ptr(t); -} - #define TCG_CT_CONST 1 /* any constant of register size */ typedef struct TCGArgConstraint { diff --git a/tcg/tcg.c b/tcg/tcg.c index ab0d227c00..ec358ce5c0 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1572,8 +1572,8 @@ void tcg_set_frame(TCGContext *s, TCGReg reg, intptr_t start, intptr_t size) = tcg_global_reg_new_internal(s, TCG_TYPE_PTR, reg, "_frame"); } -TCGTemp *tcg_global_mem_new_internal(TCGType type, TCGv_ptr base, - intptr_t offset, const char *name) +static TCGTemp *tcg_global_mem_new_internal(TCGv_ptr base, intptr_t offset, + const char *name, TCGType type) { TCGContext *s = tcg_ctx; TCGTemp *base_ts = tcgv_ptr_temp(base); @@ -1632,7 +1632,25 @@ TCGTemp *tcg_global_mem_new_internal(TCGType type, TCGv_ptr base, return ts; } -TCGTemp *tcg_temp_new_internal(TCGType type, TCGTempKind kind) +TCGv_i32 tcg_global_mem_new_i32(TCGv_ptr reg, intptr_t off, const char *name) +{ + TCGTemp *ts = tcg_global_mem_new_internal(reg, off, name, TCG_TYPE_I32); + return temp_tcgv_i32(ts); +} + +TCGv_i64 tcg_global_mem_new_i64(TCGv_ptr reg, intptr_t off, const char *name) +{ + TCGTemp *ts = tcg_global_mem_new_internal(reg, off, name, TCG_TYPE_I64); + return temp_tcgv_i64(ts); +} + +TCGv_ptr tcg_global_mem_new_ptr(TCGv_ptr reg, intptr_t off, const char *name) +{ + TCGTemp *ts = tcg_global_mem_new_internal(reg, off, name, TCG_TYPE_PTR); + return temp_tcgv_ptr(ts); +} + +static TCGTemp *tcg_temp_new_internal(TCGType type, TCGTempKind kind) { TCGContext *s = tcg_ctx; TCGTemp *ts; @@ -1696,6 +1714,46 @@ TCGTemp *tcg_temp_new_internal(TCGType type, TCGTempKind kind) return ts; } +TCGv_i32 tcg_temp_new_i32(void) +{ + return temp_tcgv_i32(tcg_temp_new_internal(TCG_TYPE_I32, TEMP_TB)); +} + +TCGv_i32 tcg_temp_ebb_new_i32(void) +{ + return temp_tcgv_i32(tcg_temp_new_internal(TCG_TYPE_I32, TEMP_EBB)); +} + +TCGv_i64 tcg_temp_new_i64(void) +{ + return temp_tcgv_i64(tcg_temp_new_internal(TCG_TYPE_I64, TEMP_TB)); +} + +TCGv_i64 tcg_temp_ebb_new_i64(void) +{ + return temp_tcgv_i64(tcg_temp_new_internal(TCG_TYPE_I64, TEMP_EBB)); +} + +TCGv_ptr tcg_temp_new_ptr(void) +{ + return temp_tcgv_ptr(tcg_temp_new_internal(TCG_TYPE_PTR, TEMP_TB)); +} + +TCGv_ptr tcg_temp_ebb_new_ptr(void) +{ + return temp_tcgv_ptr(tcg_temp_new_internal(TCG_TYPE_PTR, TEMP_EBB)); +} + +TCGv_i128 tcg_temp_new_i128(void) +{ + return temp_tcgv_i128(tcg_temp_new_internal(TCG_TYPE_I128, TEMP_TB)); +} + +TCGv_i128 tcg_temp_ebb_new_i128(void) +{ + return temp_tcgv_i128(tcg_temp_new_internal(TCG_TYPE_I128, TEMP_EBB)); +} + TCGv_vec tcg_temp_new_vec(TCGType type) { TCGTemp *t; From 58b797130c659ca084ad298e92ad4ee114c37a06 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 29 Oct 2023 14:08:48 -0700 Subject: [PATCH 506/974] tcg: Move tcg_temp_free_* out of line Signed-off-by: Richard Henderson Message-Id: <20231029210848.78234-12-richard.henderson@linaro.org> --- include/tcg/tcg-temp-internal.h | 29 +++++------------------------ tcg/tcg.c | 25 +++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/include/tcg/tcg-temp-internal.h b/include/tcg/tcg-temp-internal.h index 2d45cc45d2..44192c55a9 100644 --- a/include/tcg/tcg-temp-internal.h +++ b/include/tcg/tcg-temp-internal.h @@ -31,30 +31,11 @@ void tcg_temp_free_internal(TCGTemp *); -static inline void tcg_temp_free_i32(TCGv_i32 arg) -{ - tcg_temp_free_internal(tcgv_i32_temp(arg)); -} - -static inline void tcg_temp_free_i64(TCGv_i64 arg) -{ - tcg_temp_free_internal(tcgv_i64_temp(arg)); -} - -static inline void tcg_temp_free_i128(TCGv_i128 arg) -{ - tcg_temp_free_internal(tcgv_i128_temp(arg)); -} - -static inline void tcg_temp_free_ptr(TCGv_ptr arg) -{ - tcg_temp_free_internal(tcgv_ptr_temp(arg)); -} - -static inline void tcg_temp_free_vec(TCGv_vec arg) -{ - tcg_temp_free_internal(tcgv_vec_temp(arg)); -} +void tcg_temp_free_i32(TCGv_i32 arg); +void tcg_temp_free_i64(TCGv_i64 arg); +void tcg_temp_free_i128(TCGv_i128 arg); +void tcg_temp_free_ptr(TCGv_ptr arg); +void tcg_temp_free_vec(TCGv_vec arg); TCGv_i32 tcg_temp_ebb_new_i32(void); TCGv_i64 tcg_temp_ebb_new_i64(void); diff --git a/tcg/tcg.c b/tcg/tcg.c index ec358ce5c0..258bd1c10b 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1809,6 +1809,31 @@ void tcg_temp_free_internal(TCGTemp *ts) } } +void tcg_temp_free_i32(TCGv_i32 arg) +{ + tcg_temp_free_internal(tcgv_i32_temp(arg)); +} + +void tcg_temp_free_i64(TCGv_i64 arg) +{ + tcg_temp_free_internal(tcgv_i64_temp(arg)); +} + +void tcg_temp_free_i128(TCGv_i128 arg) +{ + tcg_temp_free_internal(tcgv_i128_temp(arg)); +} + +void tcg_temp_free_ptr(TCGv_ptr arg) +{ + tcg_temp_free_internal(tcgv_ptr_temp(arg)); +} + +void tcg_temp_free_vec(TCGv_vec arg) +{ + tcg_temp_free_internal(tcgv_vec_temp(arg)); +} + TCGTemp *tcg_constant_internal(TCGType type, int64_t val) { TCGContext *s = tcg_ctx; From 42221a64da3ade66b01952a84307acc7061c1a05 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 25 Oct 2023 21:13:59 -0700 Subject: [PATCH 507/974] tcg/mips: Split out tcg_out_setcond_int Return the temp and a set of flags, to be used as a primitive for setcond, brcond, movcond. Signed-off-by: Richard Henderson Message-Id: <20231026041404.1229328-2-richard.henderson@linaro.org> --- tcg/mips/tcg-target.c.inc | 302 +++++++++++++++----------------------- 1 file changed, 118 insertions(+), 184 deletions(-) diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 328984ccff..89681f00fe 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -871,71 +871,83 @@ static void tcg_out_addsub2(TCGContext *s, TCGReg rl, TCGReg rh, TCGReg al, } } -/* Bit 0 set if inversion required; bit 1 set if swapping required. */ -#define MIPS_CMP_INV 1 -#define MIPS_CMP_SWAP 2 +#define SETCOND_INV TCG_TARGET_NB_REGS +#define SETCOND_NEZ (SETCOND_INV << 1) +#define SETCOND_FLAGS (SETCOND_INV | SETCOND_NEZ) -static const uint8_t mips_cmp_map[16] = { - [TCG_COND_LT] = 0, - [TCG_COND_LTU] = 0, - [TCG_COND_GE] = MIPS_CMP_INV, - [TCG_COND_GEU] = MIPS_CMP_INV, - [TCG_COND_LE] = MIPS_CMP_INV | MIPS_CMP_SWAP, - [TCG_COND_LEU] = MIPS_CMP_INV | MIPS_CMP_SWAP, - [TCG_COND_GT] = MIPS_CMP_SWAP, - [TCG_COND_GTU] = MIPS_CMP_SWAP, -}; +static int tcg_out_setcond_int(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg arg1, TCGReg arg2) +{ + int flags = 0; + + switch (cond) { + case TCG_COND_EQ: /* -> NE */ + case TCG_COND_GE: /* -> LT */ + case TCG_COND_GEU: /* -> LTU */ + case TCG_COND_LE: /* -> GT */ + case TCG_COND_LEU: /* -> GTU */ + cond = tcg_invert_cond(cond); + flags ^= SETCOND_INV; + break; + default: + break; + } + + switch (cond) { + case TCG_COND_NE: + flags |= SETCOND_NEZ; + if (arg2 == 0) { + return arg1 | flags; + } + tcg_out_opc_reg(s, OPC_XOR, ret, arg1, arg2); + break; + case TCG_COND_LT: + tcg_out_opc_reg(s, OPC_SLT, ret, arg1, arg2); + break; + case TCG_COND_LTU: + tcg_out_opc_reg(s, OPC_SLTU, ret, arg1, arg2); + break; + case TCG_COND_GT: + tcg_out_opc_reg(s, OPC_SLT, ret, arg2, arg1); + break; + case TCG_COND_GTU: + tcg_out_opc_reg(s, OPC_SLTU, ret, arg2, arg1); + break; + default: + g_assert_not_reached(); + } + return ret | flags; +} + +static void tcg_out_setcond_end(TCGContext *s, TCGReg ret, int tmpflags) +{ + if (tmpflags != ret) { + TCGReg tmp = tmpflags & ~SETCOND_FLAGS; + + switch (tmpflags & SETCOND_FLAGS) { + case SETCOND_INV: + /* Intermediate result is boolean: simply invert. */ + tcg_out_opc_imm(s, OPC_XORI, ret, tmp, 1); + break; + case SETCOND_NEZ: + /* Intermediate result is zero/non-zero: test != 0. */ + tcg_out_opc_reg(s, OPC_SLTU, ret, TCG_REG_ZERO, tmp); + break; + case SETCOND_NEZ | SETCOND_INV: + /* Intermediate result is zero/non-zero: test == 0. */ + tcg_out_opc_imm(s, OPC_SLTIU, ret, tmp, 1); + break; + default: + g_assert_not_reached(); + } + } +} static void tcg_out_setcond(TCGContext *s, TCGCond cond, TCGReg ret, TCGReg arg1, TCGReg arg2) { - MIPSInsn s_opc = OPC_SLTU; - int cmp_map; - - switch (cond) { - case TCG_COND_EQ: - if (arg2 != 0) { - tcg_out_opc_reg(s, OPC_XOR, ret, arg1, arg2); - arg1 = ret; - } - tcg_out_opc_imm(s, OPC_SLTIU, ret, arg1, 1); - break; - - case TCG_COND_NE: - if (arg2 != 0) { - tcg_out_opc_reg(s, OPC_XOR, ret, arg1, arg2); - arg1 = ret; - } - tcg_out_opc_reg(s, OPC_SLTU, ret, TCG_REG_ZERO, arg1); - break; - - case TCG_COND_LT: - case TCG_COND_GE: - case TCG_COND_LE: - case TCG_COND_GT: - s_opc = OPC_SLT; - /* FALLTHRU */ - - case TCG_COND_LTU: - case TCG_COND_GEU: - case TCG_COND_LEU: - case TCG_COND_GTU: - cmp_map = mips_cmp_map[cond]; - if (cmp_map & MIPS_CMP_SWAP) { - TCGReg t = arg1; - arg1 = arg2; - arg2 = t; - } - tcg_out_opc_reg(s, s_opc, ret, arg1, arg2); - if (cmp_map & MIPS_CMP_INV) { - tcg_out_opc_imm(s, OPC_XORI, ret, ret, 1); - } - break; - - default: - g_assert_not_reached(); - break; - } + int tmpflags = tcg_out_setcond_int(s, cond, ret, arg1, arg2); + tcg_out_setcond_end(s, ret, tmpflags); } static void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGReg arg1, @@ -948,9 +960,7 @@ static void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGReg arg1, [TCG_COND_GE] = OPC_BGEZ, }; - MIPSInsn s_opc = OPC_SLTU; - MIPSInsn b_opc; - int cmp_map; + MIPSInsn b_opc = 0; switch (cond) { case TCG_COND_EQ: @@ -959,7 +969,6 @@ static void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGReg arg1, case TCG_COND_NE: b_opc = OPC_BNE; break; - case TCG_COND_LT: case TCG_COND_GT: case TCG_COND_LE: @@ -968,133 +977,76 @@ static void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGReg arg1, b_opc = b_zero[cond]; arg2 = arg1; arg1 = 0; - break; } - s_opc = OPC_SLT; - /* FALLTHRU */ - - case TCG_COND_LTU: - case TCG_COND_GTU: - case TCG_COND_LEU: - case TCG_COND_GEU: - cmp_map = mips_cmp_map[cond]; - if (cmp_map & MIPS_CMP_SWAP) { - TCGReg t = arg1; - arg1 = arg2; - arg2 = t; - } - tcg_out_opc_reg(s, s_opc, TCG_TMP0, arg1, arg2); - b_opc = (cmp_map & MIPS_CMP_INV ? OPC_BEQ : OPC_BNE); - arg1 = TCG_TMP0; - arg2 = TCG_REG_ZERO; break; - default: - g_assert_not_reached(); break; } + if (b_opc == 0) { + int tmpflags = tcg_out_setcond_int(s, cond, TCG_TMP0, arg1, arg2); + + arg2 = TCG_REG_ZERO; + arg1 = tmpflags & ~SETCOND_FLAGS; + b_opc = tmpflags & SETCOND_INV ? OPC_BEQ : OPC_BNE; + } + + tcg_out_reloc(s, s->code_ptr, R_MIPS_PC16, l, 0); tcg_out_opc_br(s, b_opc, arg1, arg2); - tcg_out_reloc(s, s->code_ptr - 1, R_MIPS_PC16, l, 0); tcg_out_nop(s); } -static TCGReg tcg_out_reduce_eq2(TCGContext *s, TCGReg tmp0, TCGReg tmp1, - TCGReg al, TCGReg ah, - TCGReg bl, TCGReg bh) +static int tcg_out_setcond2_int(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg al, TCGReg ah, TCGReg bl, TCGReg bh) { - /* Merge highpart comparison into AH. */ - if (bh != 0) { - if (ah != 0) { - tcg_out_opc_reg(s, OPC_XOR, tmp0, ah, bh); - ah = tmp0; - } else { - ah = bh; - } + int flags = 0; + + switch (cond) { + case TCG_COND_EQ: + flags |= SETCOND_INV; + /* fall through */ + case TCG_COND_NE: + flags |= SETCOND_NEZ; + tcg_out_opc_reg(s, OPC_XOR, TCG_TMP0, al, bl); + tcg_out_opc_reg(s, OPC_XOR, TCG_TMP1, ah, bh); + tcg_out_opc_reg(s, OPC_OR, ret, TCG_TMP0, TCG_TMP1); + break; + + default: + tcg_out_setcond(s, TCG_COND_EQ, TCG_TMP0, ah, bh); + tcg_out_setcond(s, tcg_unsigned_cond(cond), TCG_TMP1, al, bl); + tcg_out_opc_reg(s, OPC_AND, TCG_TMP1, TCG_TMP1, TCG_TMP0); + tcg_out_setcond(s, tcg_high_cond(cond), TCG_TMP0, ah, bh); + tcg_out_opc_reg(s, OPC_OR, ret, TCG_TMP0, TCG_TMP1); + break; } - /* Merge lowpart comparison into AL. */ - if (bl != 0) { - if (al != 0) { - tcg_out_opc_reg(s, OPC_XOR, tmp1, al, bl); - al = tmp1; - } else { - al = bl; - } - } - /* Merge high and low part comparisons into AL. */ - if (ah != 0) { - if (al != 0) { - tcg_out_opc_reg(s, OPC_OR, tmp0, ah, al); - al = tmp0; - } else { - al = ah; - } - } - return al; + return ret | flags; } static void tcg_out_setcond2(TCGContext *s, TCGCond cond, TCGReg ret, TCGReg al, TCGReg ah, TCGReg bl, TCGReg bh) { - TCGReg tmp0 = TCG_TMP0; - TCGReg tmp1 = ret; - - tcg_debug_assert(ret != TCG_TMP0); - if (ret == ah || ret == bh) { - tcg_debug_assert(ret != TCG_TMP1); - tmp1 = TCG_TMP1; - } - - switch (cond) { - case TCG_COND_EQ: - case TCG_COND_NE: - tmp1 = tcg_out_reduce_eq2(s, tmp0, tmp1, al, ah, bl, bh); - tcg_out_setcond(s, cond, ret, tmp1, TCG_REG_ZERO); - break; - - default: - tcg_out_setcond(s, TCG_COND_EQ, tmp0, ah, bh); - tcg_out_setcond(s, tcg_unsigned_cond(cond), tmp1, al, bl); - tcg_out_opc_reg(s, OPC_AND, tmp1, tmp1, tmp0); - tcg_out_setcond(s, tcg_high_cond(cond), tmp0, ah, bh); - tcg_out_opc_reg(s, OPC_OR, ret, tmp1, tmp0); - break; - } + int tmpflags = tcg_out_setcond2_int(s, cond, ret, al, ah, bl, bh); + tcg_out_setcond_end(s, ret, tmpflags); } static void tcg_out_brcond2(TCGContext *s, TCGCond cond, TCGReg al, TCGReg ah, TCGReg bl, TCGReg bh, TCGLabel *l) { - TCGCond b_cond = TCG_COND_NE; - TCGReg tmp = TCG_TMP1; + int tmpflags = tcg_out_setcond2_int(s, cond, TCG_TMP0, al, ah, bl, bh); + TCGReg tmp = tmpflags & ~SETCOND_FLAGS; + MIPSInsn b_opc = tmpflags & SETCOND_INV ? OPC_BEQ : OPC_BNE; - /* With branches, we emit between 4 and 9 insns with 2 or 3 branches. - With setcond, we emit between 3 and 10 insns and only 1 branch, - which ought to get better branch prediction. */ - switch (cond) { - case TCG_COND_EQ: - case TCG_COND_NE: - b_cond = cond; - tmp = tcg_out_reduce_eq2(s, TCG_TMP0, TCG_TMP1, al, ah, bl, bh); - break; - - default: - /* Minimize code size by preferring a compare not requiring INV. */ - if (mips_cmp_map[cond] & MIPS_CMP_INV) { - cond = tcg_invert_cond(cond); - b_cond = TCG_COND_EQ; - } - tcg_out_setcond2(s, cond, tmp, al, ah, bl, bh); - break; - } - - tcg_out_brcond(s, b_cond, tmp, TCG_REG_ZERO, l); + tcg_out_reloc(s, s->code_ptr, R_MIPS_PC16, l, 0); + tcg_out_opc_br(s, b_opc, tmp, TCG_REG_ZERO); + tcg_out_nop(s); } static void tcg_out_movcond(TCGContext *s, TCGCond cond, TCGReg ret, TCGReg c1, TCGReg c2, TCGReg v1, TCGReg v2) { - bool eqz = false; + int tmpflags; + bool eqz; /* If one of the values is zero, put it last to match SEL*Z instructions */ if (use_mips32r6_instructions && v1 == 0) { @@ -1103,27 +1055,9 @@ static void tcg_out_movcond(TCGContext *s, TCGCond cond, TCGReg ret, cond = tcg_invert_cond(cond); } - switch (cond) { - case TCG_COND_EQ: - eqz = true; - /* FALLTHRU */ - case TCG_COND_NE: - if (c2 != 0) { - tcg_out_opc_reg(s, OPC_XOR, TCG_TMP0, c1, c2); - c1 = TCG_TMP0; - } - break; - - default: - /* Minimize code size by preferring a compare not requiring INV. */ - if (mips_cmp_map[cond] & MIPS_CMP_INV) { - cond = tcg_invert_cond(cond); - eqz = true; - } - tcg_out_setcond(s, cond, TCG_TMP0, c1, c2); - c1 = TCG_TMP0; - break; - } + tmpflags = tcg_out_setcond_int(s, cond, TCG_TMP0, c1, c2); + c1 = tmpflags & ~SETCOND_FLAGS; + eqz = tmpflags & SETCOND_INV; if (use_mips32r6_instructions) { MIPSInsn m_opc_t = eqz ? OPC_SELEQZ : OPC_SELNEZ; From 2cff741da81416ba7d4d8f2470e75d0e13bccae4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 25 Oct 2023 21:14:00 -0700 Subject: [PATCH 508/974] tcg/mips: Always implement movcond Expand as branch over move if not supported in the ISA. Signed-off-by: Richard Henderson Message-Id: <20231026041404.1229328-3-richard.henderson@linaro.org> --- tcg/mips/tcg-target.c.inc | 19 ++++++++++++++----- tcg/mips/tcg-target.h | 4 ++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 89681f00fe..82b078b9c5 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1070,13 +1070,22 @@ static void tcg_out_movcond(TCGContext *s, TCGCond cond, TCGReg ret, if (v2 != 0) { tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_TMP1); } - } else { + return; + } + + /* This should be guaranteed via constraints */ + tcg_debug_assert(v2 == ret); + + if (use_movnz_instructions) { MIPSInsn m_opc = eqz ? OPC_MOVZ : OPC_MOVN; - tcg_out_opc_reg(s, m_opc, ret, v1, c1); - - /* This should be guaranteed via constraints */ - tcg_debug_assert(v2 == ret); + } else { + /* Invert the condition in order to branch over the move. */ + MIPSInsn b_opc = eqz ? OPC_BNE : OPC_BEQ; + tcg_out_opc_imm(s, b_opc, c1, TCG_REG_ZERO, 2); + tcg_out_nop(s); + /* Open-code tcg_out_mov, without the nop-move check. */ + tcg_out_opc_reg(s, OPC_OR, ret, v1, TCG_REG_ZERO); } } diff --git a/tcg/mips/tcg-target.h b/tcg/mips/tcg-target.h index c0576f66d7..0a4083f0d9 100644 --- a/tcg/mips/tcg-target.h +++ b/tcg/mips/tcg-target.h @@ -154,7 +154,7 @@ extern bool use_mips32r2_instructions; #endif /* optional instructions detected at runtime */ -#define TCG_TARGET_HAS_movcond_i32 use_movnz_instructions +#define TCG_TARGET_HAS_movcond_i32 1 #define TCG_TARGET_HAS_bswap16_i32 use_mips32r2_instructions #define TCG_TARGET_HAS_deposit_i32 use_mips32r2_instructions #define TCG_TARGET_HAS_extract_i32 use_mips32r2_instructions @@ -169,7 +169,7 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_qemu_st8_i32 0 #if TCG_TARGET_REG_BITS == 64 -#define TCG_TARGET_HAS_movcond_i64 use_movnz_instructions +#define TCG_TARGET_HAS_movcond_i64 1 #define TCG_TARGET_HAS_bswap16_i64 use_mips32r2_instructions #define TCG_TARGET_HAS_bswap32_i64 use_mips32r2_instructions #define TCG_TARGET_HAS_bswap64_i64 use_mips32r2_instructions From 3871be753f3351c21c8e384432f7798c3eed9de9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 25 Oct 2023 21:14:01 -0700 Subject: [PATCH 509/974] tcg: Remove TCG_TARGET_HAS_movcond_{i32,i64} The movcond opcode is now mandatory for backends to implement. Signed-off-by: Richard Henderson Message-Id: <20231026041404.1229328-4-richard.henderson@linaro.org> --- include/tcg/tcg-opc.h | 4 +-- include/tcg/tcg.h | 1 - tcg/aarch64/tcg-target.h | 2 -- tcg/arm/tcg-target.h | 1 - tcg/i386/tcg-target.h | 2 -- tcg/loongarch64/tcg-target.h | 2 -- tcg/mips/tcg-target.h | 2 -- tcg/ppc/tcg-target.h | 2 -- tcg/riscv/tcg-target.h | 2 -- tcg/s390x/tcg-target.h | 2 -- tcg/sparc64/tcg-target.h | 2 -- tcg/tcg-op.c | 50 ++++++++---------------------------- tcg/tcg.c | 6 ++--- tcg/tci/tcg-target.h | 2 -- 14 files changed, 14 insertions(+), 66 deletions(-) diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 6eff3d9106..ecd08db0de 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -47,7 +47,7 @@ DEF(mb, 0, 0, 1, 0) DEF(mov_i32, 1, 1, 0, TCG_OPF_NOT_PRESENT) DEF(setcond_i32, 1, 2, 1, 0) DEF(negsetcond_i32, 1, 2, 1, IMPL(TCG_TARGET_HAS_negsetcond_i32)) -DEF(movcond_i32, 1, 4, 1, IMPL(TCG_TARGET_HAS_movcond_i32)) +DEF(movcond_i32, 1, 4, 1, 0) /* load/store */ DEF(ld8u_i32, 1, 1, 1, 0) DEF(ld8s_i32, 1, 1, 1, 0) @@ -113,7 +113,7 @@ DEF(ctpop_i32, 1, 1, 0, IMPL(TCG_TARGET_HAS_ctpop_i32)) DEF(mov_i64, 1, 1, 0, TCG_OPF_64BIT | TCG_OPF_NOT_PRESENT) DEF(setcond_i64, 1, 2, 1, IMPL64) DEF(negsetcond_i64, 1, 2, 1, IMPL64 | IMPL(TCG_TARGET_HAS_negsetcond_i64)) -DEF(movcond_i64, 1, 4, 1, IMPL64 | IMPL(TCG_TARGET_HAS_movcond_i64)) +DEF(movcond_i64, 1, 4, 1, IMPL64) /* load/store */ DEF(ld8u_i64, 1, 1, 1, IMPL64) DEF(ld8s_i64, 1, 1, 1, IMPL64) diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index fc218fd381..32a208a02e 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -96,7 +96,6 @@ typedef uint64_t TCGRegSet; #define TCG_TARGET_HAS_extract_i64 0 #define TCG_TARGET_HAS_sextract_i64 0 #define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_movcond_i64 0 #define TCG_TARGET_HAS_negsetcond_i64 0 #define TCG_TARGET_HAS_add2_i64 0 #define TCG_TARGET_HAS_sub2_i64 0 diff --git a/tcg/aarch64/tcg-target.h b/tcg/aarch64/tcg-target.h index 98727ea53b..352e19aba8 100644 --- a/tcg/aarch64/tcg-target.h +++ b/tcg/aarch64/tcg-target.h @@ -85,7 +85,6 @@ typedef enum { #define TCG_TARGET_HAS_extract_i32 1 #define TCG_TARGET_HAS_sextract_i32 1 #define TCG_TARGET_HAS_extract2_i32 1 -#define TCG_TARGET_HAS_movcond_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 @@ -122,7 +121,6 @@ typedef enum { #define TCG_TARGET_HAS_extract_i64 1 #define TCG_TARGET_HAS_sextract_i64 1 #define TCG_TARGET_HAS_extract2_i64 1 -#define TCG_TARGET_HAS_movcond_i64 1 #define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 diff --git a/tcg/arm/tcg-target.h b/tcg/arm/tcg-target.h index 311a985209..439898efb3 100644 --- a/tcg/arm/tcg-target.h +++ b/tcg/arm/tcg-target.h @@ -115,7 +115,6 @@ extern bool use_neon_instructions; #define TCG_TARGET_HAS_extract_i32 use_armv7_instructions #define TCG_TARGET_HAS_sextract_i32 use_armv7_instructions #define TCG_TARGET_HAS_extract2_i32 1 -#define TCG_TARGET_HAS_movcond_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_mulu2_i32 1 #define TCG_TARGET_HAS_muls2_i32 1 diff --git a/tcg/i386/tcg-target.h b/tcg/i386/tcg-target.h index 8417ea4899..7522ce7575 100644 --- a/tcg/i386/tcg-target.h +++ b/tcg/i386/tcg-target.h @@ -149,7 +149,6 @@ typedef enum { #define TCG_TARGET_HAS_extract_i32 1 #define TCG_TARGET_HAS_sextract_i32 1 #define TCG_TARGET_HAS_extract2_i32 1 -#define TCG_TARGET_HAS_movcond_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 @@ -186,7 +185,6 @@ typedef enum { #define TCG_TARGET_HAS_extract_i64 1 #define TCG_TARGET_HAS_sextract_i64 0 #define TCG_TARGET_HAS_extract2_i64 1 -#define TCG_TARGET_HAS_movcond_i64 1 #define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 diff --git a/tcg/loongarch64/tcg-target.h b/tcg/loongarch64/tcg-target.h index 1bea15b02e..486898c2d3 100644 --- a/tcg/loongarch64/tcg-target.h +++ b/tcg/loongarch64/tcg-target.h @@ -97,7 +97,6 @@ typedef enum { #define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL /* optional instructions */ -#define TCG_TARGET_HAS_movcond_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 0 #define TCG_TARGET_HAS_div_i32 1 #define TCG_TARGET_HAS_rem_i32 1 @@ -134,7 +133,6 @@ typedef enum { #define TCG_TARGET_HAS_qemu_st8_i32 0 /* 64-bit operations */ -#define TCG_TARGET_HAS_movcond_i64 1 #define TCG_TARGET_HAS_negsetcond_i64 0 #define TCG_TARGET_HAS_div_i64 1 #define TCG_TARGET_HAS_rem_i64 1 diff --git a/tcg/mips/tcg-target.h b/tcg/mips/tcg-target.h index 0a4083f0d9..5b3fdcc726 100644 --- a/tcg/mips/tcg-target.h +++ b/tcg/mips/tcg-target.h @@ -154,7 +154,6 @@ extern bool use_mips32r2_instructions; #endif /* optional instructions detected at runtime */ -#define TCG_TARGET_HAS_movcond_i32 1 #define TCG_TARGET_HAS_bswap16_i32 use_mips32r2_instructions #define TCG_TARGET_HAS_deposit_i32 use_mips32r2_instructions #define TCG_TARGET_HAS_extract_i32 use_mips32r2_instructions @@ -169,7 +168,6 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_qemu_st8_i32 0 #if TCG_TARGET_REG_BITS == 64 -#define TCG_TARGET_HAS_movcond_i64 1 #define TCG_TARGET_HAS_bswap16_i64 use_mips32r2_instructions #define TCG_TARGET_HAS_bswap32_i64 use_mips32r2_instructions #define TCG_TARGET_HAS_bswap64_i64 use_mips32r2_instructions diff --git a/tcg/ppc/tcg-target.h b/tcg/ppc/tcg-target.h index 8bfb14998e..a2856afd4d 100644 --- a/tcg/ppc/tcg-target.h +++ b/tcg/ppc/tcg-target.h @@ -96,7 +96,6 @@ typedef enum { #define TCG_TARGET_HAS_extract_i32 1 #define TCG_TARGET_HAS_sextract_i32 0 #define TCG_TARGET_HAS_extract2_i32 0 -#define TCG_TARGET_HAS_movcond_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_mulu2_i32 0 #define TCG_TARGET_HAS_muls2_i32 0 @@ -134,7 +133,6 @@ typedef enum { #define TCG_TARGET_HAS_extract_i64 1 #define TCG_TARGET_HAS_sextract_i64 0 #define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_movcond_i64 1 #define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 diff --git a/tcg/riscv/tcg-target.h b/tcg/riscv/tcg-target.h index c1132d178f..f3644a8bc1 100644 --- a/tcg/riscv/tcg-target.h +++ b/tcg/riscv/tcg-target.h @@ -87,7 +87,6 @@ extern bool have_zbb; #endif /* optional instructions */ -#define TCG_TARGET_HAS_movcond_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_div_i32 1 #define TCG_TARGET_HAS_rem_i32 1 @@ -123,7 +122,6 @@ extern bool have_zbb; #define TCG_TARGET_HAS_setcond2 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 -#define TCG_TARGET_HAS_movcond_i64 1 #define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_div_i64 1 #define TCG_TARGET_HAS_rem_i64 1 diff --git a/tcg/s390x/tcg-target.h b/tcg/s390x/tcg-target.h index 50e12ef9d6..2c936c1bcb 100644 --- a/tcg/s390x/tcg-target.h +++ b/tcg/s390x/tcg-target.h @@ -95,7 +95,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_extract_i32 1 #define TCG_TARGET_HAS_sextract_i32 0 #define TCG_TARGET_HAS_extract2_i32 0 -#define TCG_TARGET_HAS_movcond_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 @@ -131,7 +130,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_extract_i64 1 #define TCG_TARGET_HAS_sextract_i64 0 #define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_movcond_i64 1 #define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 diff --git a/tcg/sparc64/tcg-target.h b/tcg/sparc64/tcg-target.h index 5cfc4b4679..4c286c6006 100644 --- a/tcg/sparc64/tcg-target.h +++ b/tcg/sparc64/tcg-target.h @@ -105,7 +105,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_extract_i32 0 #define TCG_TARGET_HAS_sextract_i32 0 #define TCG_TARGET_HAS_extract2_i32 0 -#define TCG_TARGET_HAS_movcond_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 @@ -142,7 +141,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_extract_i64 0 #define TCG_TARGET_HAS_sextract_i64 0 #define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_movcond_i64 1 #define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 9aba103590..26bcd090c1 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -1142,17 +1142,8 @@ void tcg_gen_movcond_i32(TCGCond cond, TCGv_i32 ret, TCGv_i32 c1, tcg_gen_mov_i32(ret, v1); } else if (cond == TCG_COND_NEVER) { tcg_gen_mov_i32(ret, v2); - } else if (TCG_TARGET_HAS_movcond_i32) { - tcg_gen_op6i_i32(INDEX_op_movcond_i32, ret, c1, c2, v1, v2, cond); } else { - TCGv_i32 t0 = tcg_temp_ebb_new_i32(); - TCGv_i32 t1 = tcg_temp_ebb_new_i32(); - tcg_gen_negsetcond_i32(cond, t0, c1, c2); - tcg_gen_and_i32(t1, v1, t0); - tcg_gen_andc_i32(ret, v2, t0); - tcg_gen_or_i32(ret, ret, t1); - tcg_temp_free_i32(t0); - tcg_temp_free_i32(t1); + tcg_gen_op6i_i32(INDEX_op_movcond_i32, ret, c1, c2, v1, v2, cond); } } @@ -3011,43 +3002,22 @@ void tcg_gen_movcond_i64(TCGCond cond, TCGv_i64 ret, TCGv_i64 c1, tcg_gen_mov_i64(ret, v1); } else if (cond == TCG_COND_NEVER) { tcg_gen_mov_i64(ret, v2); - } else if (TCG_TARGET_REG_BITS == 32) { + } else if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_op6i_i64(INDEX_op_movcond_i64, ret, c1, c2, v1, v2, cond); + } else { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); - TCGv_i32 t1 = tcg_temp_ebb_new_i32(); + TCGv_i32 zero = tcg_constant_i32(0); + tcg_gen_op6i_i32(INDEX_op_setcond2_i32, t0, TCGV_LOW(c1), TCGV_HIGH(c1), TCGV_LOW(c2), TCGV_HIGH(c2), cond); - if (TCG_TARGET_HAS_movcond_i32) { - tcg_gen_movi_i32(t1, 0); - tcg_gen_movcond_i32(TCG_COND_NE, TCGV_LOW(ret), t0, t1, - TCGV_LOW(v1), TCGV_LOW(v2)); - tcg_gen_movcond_i32(TCG_COND_NE, TCGV_HIGH(ret), t0, t1, - TCGV_HIGH(v1), TCGV_HIGH(v2)); - } else { - tcg_gen_neg_i32(t0, t0); + tcg_gen_movcond_i32(TCG_COND_NE, TCGV_LOW(ret), t0, zero, + TCGV_LOW(v1), TCGV_LOW(v2)); + tcg_gen_movcond_i32(TCG_COND_NE, TCGV_HIGH(ret), t0, zero, + TCGV_HIGH(v1), TCGV_HIGH(v2)); - tcg_gen_and_i32(t1, TCGV_LOW(v1), t0); - tcg_gen_andc_i32(TCGV_LOW(ret), TCGV_LOW(v2), t0); - tcg_gen_or_i32(TCGV_LOW(ret), TCGV_LOW(ret), t1); - - tcg_gen_and_i32(t1, TCGV_HIGH(v1), t0); - tcg_gen_andc_i32(TCGV_HIGH(ret), TCGV_HIGH(v2), t0); - tcg_gen_or_i32(TCGV_HIGH(ret), TCGV_HIGH(ret), t1); - } tcg_temp_free_i32(t0); - tcg_temp_free_i32(t1); - } else if (TCG_TARGET_HAS_movcond_i64) { - tcg_gen_op6i_i64(INDEX_op_movcond_i64, ret, c1, c2, v1, v2, cond); - } else { - TCGv_i64 t0 = tcg_temp_ebb_new_i64(); - TCGv_i64 t1 = tcg_temp_ebb_new_i64(); - tcg_gen_negsetcond_i64(cond, t0, c1, c2); - tcg_gen_and_i64(t1, v1, t0); - tcg_gen_andc_i64(ret, v2, t0); - tcg_gen_or_i64(ret, ret, t1); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); } } diff --git a/tcg/tcg.c b/tcg/tcg.c index 258bd1c10b..d59ff14f0f 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1977,6 +1977,7 @@ bool tcg_op_supported(TCGOpcode op) case INDEX_op_mov_i32: case INDEX_op_setcond_i32: case INDEX_op_brcond_i32: + case INDEX_op_movcond_i32: case INDEX_op_ld8u_i32: case INDEX_op_ld8s_i32: case INDEX_op_ld16u_i32: @@ -1998,8 +1999,6 @@ bool tcg_op_supported(TCGOpcode op) case INDEX_op_negsetcond_i32: return TCG_TARGET_HAS_negsetcond_i32; - case INDEX_op_movcond_i32: - return TCG_TARGET_HAS_movcond_i32; case INDEX_op_div_i32: case INDEX_op_divu_i32: return TCG_TARGET_HAS_div_i32; @@ -2072,6 +2071,7 @@ bool tcg_op_supported(TCGOpcode op) case INDEX_op_mov_i64: case INDEX_op_setcond_i64: case INDEX_op_brcond_i64: + case INDEX_op_movcond_i64: case INDEX_op_ld8u_i64: case INDEX_op_ld8s_i64: case INDEX_op_ld16u_i64: @@ -2098,8 +2098,6 @@ bool tcg_op_supported(TCGOpcode op) case INDEX_op_negsetcond_i64: return TCG_TARGET_HAS_negsetcond_i64; - case INDEX_op_movcond_i64: - return TCG_TARGET_HAS_movcond_i64; case INDEX_op_div_i64: case INDEX_op_divu_i64: return TCG_TARGET_HAS_div_i64; diff --git a/tcg/tci/tcg-target.h b/tcg/tci/tcg-target.h index 91ca33b616..3503fc4a4c 100644 --- a/tcg/tci/tcg-target.h +++ b/tcg/tci/tcg-target.h @@ -69,7 +69,6 @@ #define TCG_TARGET_HAS_not_i32 1 #define TCG_TARGET_HAS_orc_i32 1 #define TCG_TARGET_HAS_rot_i32 1 -#define TCG_TARGET_HAS_movcond_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 0 #define TCG_TARGET_HAS_muls2_i32 1 #define TCG_TARGET_HAS_muluh_i32 0 @@ -104,7 +103,6 @@ #define TCG_TARGET_HAS_not_i64 1 #define TCG_TARGET_HAS_orc_i64 1 #define TCG_TARGET_HAS_rot_i64 1 -#define TCG_TARGET_HAS_movcond_i64 1 #define TCG_TARGET_HAS_negsetcond_i64 0 #define TCG_TARGET_HAS_muls2_i64 1 #define TCG_TARGET_HAS_add2_i32 1 From e0448a8b71624c97af0422df085c66d147104061 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 25 Oct 2023 21:14:02 -0700 Subject: [PATCH 510/974] tcg/mips: Implement neg opcodes Signed-off-by: Richard Henderson Message-Id: <20231026041404.1229328-5-richard.henderson@linaro.org> --- tcg/mips/tcg-target.c.inc | 8 ++++++++ tcg/mips/tcg-target.h | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 82b078b9c5..8328dbdecc 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1920,6 +1920,12 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, tcg_out_opc_reg(s, OPC_MFHI, a1, 0, 0); break; + case INDEX_op_neg_i32: + i1 = OPC_SUBU; + goto do_unary; + case INDEX_op_neg_i64: + i1 = OPC_DSUBU; + goto do_unary; case INDEX_op_not_i32: case INDEX_op_not_i64: i1 = OPC_NOR; @@ -2144,6 +2150,7 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_ld16u_i32: case INDEX_op_ld16s_i32: case INDEX_op_ld_i32: + case INDEX_op_neg_i32: case INDEX_op_not_i32: case INDEX_op_bswap16_i32: case INDEX_op_bswap32_i32: @@ -2157,6 +2164,7 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_ld32s_i64: case INDEX_op_ld32u_i64: case INDEX_op_ld_i64: + case INDEX_op_neg_i64: case INDEX_op_not_i64: case INDEX_op_bswap16_i64: case INDEX_op_bswap32_i64: diff --git a/tcg/mips/tcg-target.h b/tcg/mips/tcg-target.h index 5b3fdcc726..20c14224fb 100644 --- a/tcg/mips/tcg-target.h +++ b/tcg/mips/tcg-target.h @@ -184,12 +184,12 @@ extern bool use_mips32r2_instructions; #endif /* optional instructions automatically implemented */ -#define TCG_TARGET_HAS_neg_i32 0 /* sub rd, zero, rt */ +#define TCG_TARGET_HAS_neg_i32 1 #define TCG_TARGET_HAS_ext8u_i32 0 /* andi rt, rs, 0xff */ #define TCG_TARGET_HAS_ext16u_i32 0 /* andi rt, rs, 0xffff */ #if TCG_TARGET_REG_BITS == 64 -#define TCG_TARGET_HAS_neg_i64 0 /* sub rd, zero, rt */ +#define TCG_TARGET_HAS_neg_i64 1 #define TCG_TARGET_HAS_ext8u_i64 0 /* andi rt, rs, 0xff */ #define TCG_TARGET_HAS_ext16u_i64 0 /* andi rt, rs, 0xffff */ #endif From 0fbee2b76441db5452cde98148f600aba907f4f7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 25 Oct 2023 21:14:03 -0700 Subject: [PATCH 511/974] tcg/loongarch64: Implement neg opcodes Signed-off-by: Richard Henderson Message-Id: <20231026041404.1229328-6-richard.henderson@linaro.org> --- tcg/loongarch64/tcg-target.c.inc | 9 +++++++++ tcg/loongarch64/tcg-target.h | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index ae13c1f666..a588fb3085 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1441,6 +1441,13 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, } break; + case INDEX_op_neg_i32: + tcg_out_opc_sub_w(s, a0, TCG_REG_ZERO, a1); + break; + case INDEX_op_neg_i64: + tcg_out_opc_sub_d(s, a0, TCG_REG_ZERO, a1); + break; + case INDEX_op_mul_i32: tcg_out_opc_mul_w(s, a0, a1, a2); break; @@ -2076,6 +2083,8 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_extrl_i64_i32: case INDEX_op_extrh_i64_i32: case INDEX_op_ext_i32_i64: + case INDEX_op_neg_i32: + case INDEX_op_neg_i64: case INDEX_op_not_i32: case INDEX_op_not_i64: case INDEX_op_extract_i32: diff --git a/tcg/loongarch64/tcg-target.h b/tcg/loongarch64/tcg-target.h index 486898c2d3..189997644a 100644 --- a/tcg/loongarch64/tcg-target.h +++ b/tcg/loongarch64/tcg-target.h @@ -119,7 +119,7 @@ typedef enum { #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_neg_i32 0 +#define TCG_TARGET_HAS_neg_i32 1 #define TCG_TARGET_HAS_andc_i32 1 #define TCG_TARGET_HAS_orc_i32 1 #define TCG_TARGET_HAS_eqv_i32 0 @@ -153,7 +153,7 @@ typedef enum { #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_neg_i64 0 +#define TCG_TARGET_HAS_neg_i64 1 #define TCG_TARGET_HAS_andc_i64 1 #define TCG_TARGET_HAS_orc_i64 1 #define TCG_TARGET_HAS_eqv_i64 0 From b701f195d3ed39429fab5787df7c6b1c9377ab94 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 25 Oct 2023 21:14:04 -0700 Subject: [PATCH 512/974] tcg: Remove TCG_TARGET_HAS_neg_{i32,i64} The movcond opcode is now mandatory for backends to implement. Signed-off-by: Richard Henderson Message-Id: <20231026041404.1229328-7-richard.henderson@linaro.org> --- include/tcg/tcg-opc.h | 4 ++-- include/tcg/tcg.h | 1 - tcg/aarch64/tcg-target.h | 2 -- tcg/arm/tcg-target.h | 1 - tcg/i386/tcg-target.h | 2 -- tcg/loongarch64/tcg-target.h | 2 -- tcg/mips/tcg-target.h | 2 -- tcg/optimize.c | 4 ++-- tcg/ppc/tcg-target.h | 2 -- tcg/riscv/tcg-target.h | 2 -- tcg/s390x/tcg-target.h | 2 -- tcg/sparc64/tcg-target.h | 2 -- tcg/tcg-op.c | 22 +++++++++------------- tcg/tcg.c | 6 ++---- tcg/tci.c | 2 -- tcg/tci/tcg-target.h | 2 -- 16 files changed, 15 insertions(+), 43 deletions(-) diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index ecd08db0de..b80227fa1c 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -100,7 +100,7 @@ DEF(ext16u_i32, 1, 1, 0, IMPL(TCG_TARGET_HAS_ext16u_i32)) DEF(bswap16_i32, 1, 1, 1, IMPL(TCG_TARGET_HAS_bswap16_i32)) DEF(bswap32_i32, 1, 1, 1, IMPL(TCG_TARGET_HAS_bswap32_i32)) DEF(not_i32, 1, 1, 0, IMPL(TCG_TARGET_HAS_not_i32)) -DEF(neg_i32, 1, 1, 0, IMPL(TCG_TARGET_HAS_neg_i32)) +DEF(neg_i32, 1, 1, 0, 0) DEF(andc_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_andc_i32)) DEF(orc_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_orc_i32)) DEF(eqv_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_eqv_i32)) @@ -171,7 +171,7 @@ DEF(bswap16_i64, 1, 1, 1, IMPL64 | IMPL(TCG_TARGET_HAS_bswap16_i64)) DEF(bswap32_i64, 1, 1, 1, IMPL64 | IMPL(TCG_TARGET_HAS_bswap32_i64)) DEF(bswap64_i64, 1, 1, 1, IMPL64 | IMPL(TCG_TARGET_HAS_bswap64_i64)) DEF(not_i64, 1, 1, 0, IMPL64 | IMPL(TCG_TARGET_HAS_not_i64)) -DEF(neg_i64, 1, 1, 0, IMPL64 | IMPL(TCG_TARGET_HAS_neg_i64)) +DEF(neg_i64, 1, 1, 0, IMPL64) DEF(andc_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_andc_i64)) DEF(orc_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_orc_i64)) DEF(eqv_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_eqv_i64)) diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 32a208a02e..daf2a5bf9e 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -82,7 +82,6 @@ typedef uint64_t TCGRegSet; #define TCG_TARGET_HAS_bswap16_i64 0 #define TCG_TARGET_HAS_bswap32_i64 0 #define TCG_TARGET_HAS_bswap64_i64 0 -#define TCG_TARGET_HAS_neg_i64 0 #define TCG_TARGET_HAS_not_i64 0 #define TCG_TARGET_HAS_andc_i64 0 #define TCG_TARGET_HAS_orc_i64 0 diff --git a/tcg/aarch64/tcg-target.h b/tcg/aarch64/tcg-target.h index 352e19aba8..33f15a564a 100644 --- a/tcg/aarch64/tcg-target.h +++ b/tcg/aarch64/tcg-target.h @@ -71,7 +71,6 @@ typedef enum { #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_neg_i32 1 #define TCG_TARGET_HAS_rot_i32 1 #define TCG_TARGET_HAS_andc_i32 1 #define TCG_TARGET_HAS_orc_i32 1 @@ -107,7 +106,6 @@ typedef enum { #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_neg_i64 1 #define TCG_TARGET_HAS_rot_i64 1 #define TCG_TARGET_HAS_andc_i64 1 #define TCG_TARGET_HAS_orc_i64 1 diff --git a/tcg/arm/tcg-target.h b/tcg/arm/tcg-target.h index 439898efb3..a712cc80ad 100644 --- a/tcg/arm/tcg-target.h +++ b/tcg/arm/tcg-target.h @@ -101,7 +101,6 @@ extern bool use_neon_instructions; #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_neg_i32 1 #define TCG_TARGET_HAS_rot_i32 1 #define TCG_TARGET_HAS_andc_i32 1 #define TCG_TARGET_HAS_orc_i32 0 diff --git a/tcg/i386/tcg-target.h b/tcg/i386/tcg-target.h index 7522ce7575..fa34deec47 100644 --- a/tcg/i386/tcg-target.h +++ b/tcg/i386/tcg-target.h @@ -135,7 +135,6 @@ typedef enum { #define TCG_TARGET_HAS_ext16u_i32 1 #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_neg_i32 1 #define TCG_TARGET_HAS_not_i32 1 #define TCG_TARGET_HAS_andc_i32 have_bmi1 #define TCG_TARGET_HAS_orc_i32 0 @@ -171,7 +170,6 @@ typedef enum { #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 -#define TCG_TARGET_HAS_neg_i64 1 #define TCG_TARGET_HAS_not_i64 1 #define TCG_TARGET_HAS_andc_i64 have_bmi1 #define TCG_TARGET_HAS_orc_i64 0 diff --git a/tcg/loongarch64/tcg-target.h b/tcg/loongarch64/tcg-target.h index 189997644a..9c70ebfefc 100644 --- a/tcg/loongarch64/tcg-target.h +++ b/tcg/loongarch64/tcg-target.h @@ -119,7 +119,6 @@ typedef enum { #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_neg_i32 1 #define TCG_TARGET_HAS_andc_i32 1 #define TCG_TARGET_HAS_orc_i32 1 #define TCG_TARGET_HAS_eqv_i32 0 @@ -153,7 +152,6 @@ typedef enum { #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_neg_i64 1 #define TCG_TARGET_HAS_andc_i64 1 #define TCG_TARGET_HAS_orc_i64 1 #define TCG_TARGET_HAS_eqv_i64 0 diff --git a/tcg/mips/tcg-target.h b/tcg/mips/tcg-target.h index 20c14224fb..b98ffae1d0 100644 --- a/tcg/mips/tcg-target.h +++ b/tcg/mips/tcg-target.h @@ -184,12 +184,10 @@ extern bool use_mips32r2_instructions; #endif /* optional instructions automatically implemented */ -#define TCG_TARGET_HAS_neg_i32 1 #define TCG_TARGET_HAS_ext8u_i32 0 /* andi rt, rs, 0xff */ #define TCG_TARGET_HAS_ext16u_i32 0 /* andi rt, rs, 0xffff */ #if TCG_TARGET_REG_BITS == 64 -#define TCG_TARGET_HAS_neg_i64 1 #define TCG_TARGET_HAS_ext8u_i64 0 /* andi rt, rs, 0xff */ #define TCG_TARGET_HAS_ext16u_i64 0 /* andi rt, rs, 0xffff */ #endif diff --git a/tcg/optimize.c b/tcg/optimize.c index 2db5177c32..6b072d4cdb 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2001,11 +2001,11 @@ static bool fold_sub_to_neg(OptContext *ctx, TCGOp *op) switch (ctx->type) { case TCG_TYPE_I32: neg_op = INDEX_op_neg_i32; - have_neg = TCG_TARGET_HAS_neg_i32; + have_neg = true; break; case TCG_TYPE_I64: neg_op = INDEX_op_neg_i64; - have_neg = TCG_TARGET_HAS_neg_i64; + have_neg = true; break; case TCG_TYPE_V64: case TCG_TYPE_V128: diff --git a/tcg/ppc/tcg-target.h b/tcg/ppc/tcg-target.h index a2856afd4d..5295e4f9ab 100644 --- a/tcg/ppc/tcg-target.h +++ b/tcg/ppc/tcg-target.h @@ -83,7 +83,6 @@ typedef enum { #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_neg_i32 1 #define TCG_TARGET_HAS_andc_i32 1 #define TCG_TARGET_HAS_orc_i32 1 #define TCG_TARGET_HAS_eqv_i32 1 @@ -120,7 +119,6 @@ typedef enum { #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_neg_i64 1 #define TCG_TARGET_HAS_andc_i64 1 #define TCG_TARGET_HAS_orc_i64 1 #define TCG_TARGET_HAS_eqv_i64 1 diff --git a/tcg/riscv/tcg-target.h b/tcg/riscv/tcg-target.h index f3644a8bc1..a4edc3dc74 100644 --- a/tcg/riscv/tcg-target.h +++ b/tcg/riscv/tcg-target.h @@ -109,7 +109,6 @@ extern bool have_zbb; #define TCG_TARGET_HAS_bswap16_i32 have_zbb #define TCG_TARGET_HAS_bswap32_i32 have_zbb #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_neg_i32 1 #define TCG_TARGET_HAS_andc_i32 have_zbb #define TCG_TARGET_HAS_orc_i32 have_zbb #define TCG_TARGET_HAS_eqv_i32 have_zbb @@ -142,7 +141,6 @@ extern bool have_zbb; #define TCG_TARGET_HAS_bswap32_i64 have_zbb #define TCG_TARGET_HAS_bswap64_i64 have_zbb #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_neg_i64 1 #define TCG_TARGET_HAS_andc_i64 have_zbb #define TCG_TARGET_HAS_orc_i64 have_zbb #define TCG_TARGET_HAS_eqv_i64 have_zbb diff --git a/tcg/s390x/tcg-target.h b/tcg/s390x/tcg-target.h index 2c936c1bcb..e69b0d2ddd 100644 --- a/tcg/s390x/tcg-target.h +++ b/tcg/s390x/tcg-target.h @@ -82,7 +82,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 HAVE_FACILITY(MISC_INSN_EXT3) -#define TCG_TARGET_HAS_neg_i32 1 #define TCG_TARGET_HAS_andc_i32 HAVE_FACILITY(MISC_INSN_EXT3) #define TCG_TARGET_HAS_orc_i32 HAVE_FACILITY(MISC_INSN_EXT3) #define TCG_TARGET_HAS_eqv_i32 HAVE_FACILITY(MISC_INSN_EXT3) @@ -117,7 +116,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_not_i64 HAVE_FACILITY(MISC_INSN_EXT3) -#define TCG_TARGET_HAS_neg_i64 1 #define TCG_TARGET_HAS_andc_i64 HAVE_FACILITY(MISC_INSN_EXT3) #define TCG_TARGET_HAS_orc_i64 HAVE_FACILITY(MISC_INSN_EXT3) #define TCG_TARGET_HAS_eqv_i64 HAVE_FACILITY(MISC_INSN_EXT3) diff --git a/tcg/sparc64/tcg-target.h b/tcg/sparc64/tcg-target.h index 4c286c6006..f8cf145266 100644 --- a/tcg/sparc64/tcg-target.h +++ b/tcg/sparc64/tcg-target.h @@ -91,7 +91,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_ext16u_i32 0 #define TCG_TARGET_HAS_bswap16_i32 0 #define TCG_TARGET_HAS_bswap32_i32 0 -#define TCG_TARGET_HAS_neg_i32 1 #define TCG_TARGET_HAS_not_i32 1 #define TCG_TARGET_HAS_andc_i32 1 #define TCG_TARGET_HAS_orc_i32 1 @@ -127,7 +126,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_bswap16_i64 0 #define TCG_TARGET_HAS_bswap32_i64 0 #define TCG_TARGET_HAS_bswap64_i64 0 -#define TCG_TARGET_HAS_neg_i64 1 #define TCG_TARGET_HAS_not_i64 1 #define TCG_TARGET_HAS_andc_i64 1 #define TCG_TARGET_HAS_orc_i64 1 diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 26bcd090c1..de096a6f93 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -363,9 +363,8 @@ void tcg_gen_sub_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) void tcg_gen_subfi_i32(TCGv_i32 ret, int32_t arg1, TCGv_i32 arg2) { - if (arg1 == 0 && TCG_TARGET_HAS_neg_i32) { - /* Don't recurse with tcg_gen_neg_i32. */ - tcg_gen_op2_i32(INDEX_op_neg_i32, ret, arg2); + if (arg1 == 0) { + tcg_gen_neg_i32(ret, arg2); } else { tcg_gen_sub_i32(ret, tcg_constant_i32(arg1), arg2); } @@ -383,11 +382,7 @@ void tcg_gen_subi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) void tcg_gen_neg_i32(TCGv_i32 ret, TCGv_i32 arg) { - if (TCG_TARGET_HAS_neg_i32) { - tcg_gen_op2_i32(INDEX_op_neg_i32, ret, arg); - } else { - tcg_gen_subfi_i32(ret, 0, arg); - } + tcg_gen_op2_i32(INDEX_op_neg_i32, ret, arg); } void tcg_gen_and_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) @@ -1744,9 +1739,8 @@ void tcg_gen_addi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) void tcg_gen_subfi_i64(TCGv_i64 ret, int64_t arg1, TCGv_i64 arg2) { - if (arg1 == 0 && TCG_TARGET_HAS_neg_i64) { - /* Don't recurse with tcg_gen_neg_i64. */ - tcg_gen_op2_i64(INDEX_op_neg_i64, ret, arg2); + if (arg1 == 0) { + tcg_gen_neg_i64(ret, arg2); } else if (TCG_TARGET_REG_BITS == 64) { tcg_gen_sub_i64(ret, tcg_constant_i64(arg1), arg2); } else { @@ -1772,10 +1766,12 @@ void tcg_gen_subi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) void tcg_gen_neg_i64(TCGv_i64 ret, TCGv_i64 arg) { - if (TCG_TARGET_HAS_neg_i64) { + if (TCG_TARGET_REG_BITS == 64) { tcg_gen_op2_i64(INDEX_op_neg_i64, ret, arg); } else { - tcg_gen_subfi_i64(ret, 0, arg); + TCGv_i32 zero = tcg_constant_i32(0); + tcg_gen_sub2_i32(TCGV_LOW(ret), TCGV_HIGH(ret), + zero, zero, TCGV_LOW(arg), TCGV_HIGH(arg)); } } diff --git a/tcg/tcg.c b/tcg/tcg.c index d59ff14f0f..d2ea22b397 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1988,6 +1988,7 @@ bool tcg_op_supported(TCGOpcode op) case INDEX_op_st_i32: case INDEX_op_add_i32: case INDEX_op_sub_i32: + case INDEX_op_neg_i32: case INDEX_op_mul_i32: case INDEX_op_and_i32: case INDEX_op_or_i32: @@ -2045,8 +2046,6 @@ bool tcg_op_supported(TCGOpcode op) return TCG_TARGET_HAS_bswap32_i32; case INDEX_op_not_i32: return TCG_TARGET_HAS_not_i32; - case INDEX_op_neg_i32: - return TCG_TARGET_HAS_neg_i32; case INDEX_op_andc_i32: return TCG_TARGET_HAS_andc_i32; case INDEX_op_orc_i32: @@ -2085,6 +2084,7 @@ bool tcg_op_supported(TCGOpcode op) case INDEX_op_st_i64: case INDEX_op_add_i64: case INDEX_op_sub_i64: + case INDEX_op_neg_i64: case INDEX_op_mul_i64: case INDEX_op_and_i64: case INDEX_op_or_i64: @@ -2141,8 +2141,6 @@ bool tcg_op_supported(TCGOpcode op) return TCG_TARGET_HAS_bswap64_i64; case INDEX_op_not_i64: return TCG_TARGET_HAS_not_i64; - case INDEX_op_neg_i64: - return TCG_TARGET_HAS_neg_i64; case INDEX_op_andc_i64: return TCG_TARGET_HAS_andc_i64; case INDEX_op_orc_i64: diff --git a/tcg/tci.c b/tcg/tci.c index 4640902c88..3cc851b7bd 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -733,12 +733,10 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, regs[r0] = ~regs[r1]; break; #endif -#if TCG_TARGET_HAS_neg_i32 || TCG_TARGET_HAS_neg_i64 CASE_32_64(neg) tci_args_rr(insn, &r0, &r1); regs[r0] = -regs[r1]; break; -#endif #if TCG_TARGET_REG_BITS == 64 /* Load/store operations (64 bit). */ diff --git a/tcg/tci/tcg-target.h b/tcg/tci/tcg-target.h index 3503fc4a4c..2a13816c8e 100644 --- a/tcg/tci/tcg-target.h +++ b/tcg/tci/tcg-target.h @@ -65,7 +65,6 @@ #define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 1 #define TCG_TARGET_HAS_ctpop_i32 1 -#define TCG_TARGET_HAS_neg_i32 1 #define TCG_TARGET_HAS_not_i32 1 #define TCG_TARGET_HAS_orc_i32 1 #define TCG_TARGET_HAS_rot_i32 1 @@ -99,7 +98,6 @@ #define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 1 #define TCG_TARGET_HAS_ctpop_i64 1 -#define TCG_TARGET_HAS_neg_i64 1 #define TCG_TARGET_HAS_not_i64 1 #define TCG_TARGET_HAS_orc_i64 1 #define TCG_TARGET_HAS_rot_i64 1 From 9628d008bd2461e654ca47b7576002bd501d7a7d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 23 Aug 2023 20:35:05 -0700 Subject: [PATCH 513/974] tcg: Don't free vector results Avoid reusing vector temporaries so that we may re-use them when propagating stores to loads. Reviewed-by: Song Gao Signed-off-by: Richard Henderson --- tcg/tcg-op-gvec.c | 112 ++++++++++++++++------------------------------ 1 file changed, 38 insertions(+), 74 deletions(-) diff --git a/tcg/tcg-op-gvec.c b/tcg/tcg-op-gvec.c index feb2d3686b..bb88943f79 100644 --- a/tcg/tcg-op-gvec.c +++ b/tcg/tcg-op-gvec.c @@ -561,7 +561,6 @@ static void do_dup(unsigned vece, uint32_t dofs, uint32_t oprsz, tcg_gen_dupi_vec(vece, t_vec, in_c); } do_dup_store(type, dofs, oprsz, maxsz, t_vec); - tcg_temp_free_vec(t_vec); return; } @@ -1024,11 +1023,10 @@ static void expand_2_vec(unsigned vece, uint32_t dofs, uint32_t aofs, bool load_dest, void (*fni)(unsigned, TCGv_vec, TCGv_vec)) { - TCGv_vec t0 = tcg_temp_new_vec(type); - TCGv_vec t1 = tcg_temp_new_vec(type); - uint32_t i; + for (uint32_t i = 0; i < oprsz; i += tysz) { + TCGv_vec t0 = tcg_temp_new_vec(type); + TCGv_vec t1 = tcg_temp_new_vec(type); - for (i = 0; i < oprsz; i += tysz) { tcg_gen_ld_vec(t0, tcg_env, aofs + i); if (load_dest) { tcg_gen_ld_vec(t1, tcg_env, dofs + i); @@ -1036,8 +1034,6 @@ static void expand_2_vec(unsigned vece, uint32_t dofs, uint32_t aofs, fni(vece, t1, t0); tcg_gen_st_vec(t1, tcg_env, dofs + i); } - tcg_temp_free_vec(t0); - tcg_temp_free_vec(t1); } /* Expand OPSZ bytes worth of two-vector operands and an immediate operand @@ -1047,11 +1043,10 @@ static void expand_2i_vec(unsigned vece, uint32_t dofs, uint32_t aofs, int64_t c, bool load_dest, void (*fni)(unsigned, TCGv_vec, TCGv_vec, int64_t)) { - TCGv_vec t0 = tcg_temp_new_vec(type); - TCGv_vec t1 = tcg_temp_new_vec(type); - uint32_t i; + for (uint32_t i = 0; i < oprsz; i += tysz) { + TCGv_vec t0 = tcg_temp_new_vec(type); + TCGv_vec t1 = tcg_temp_new_vec(type); - for (i = 0; i < oprsz; i += tysz) { tcg_gen_ld_vec(t0, tcg_env, aofs + i); if (load_dest) { tcg_gen_ld_vec(t1, tcg_env, dofs + i); @@ -1059,8 +1054,6 @@ static void expand_2i_vec(unsigned vece, uint32_t dofs, uint32_t aofs, fni(vece, t1, t0, c); tcg_gen_st_vec(t1, tcg_env, dofs + i); } - tcg_temp_free_vec(t0); - tcg_temp_free_vec(t1); } static void expand_2s_vec(unsigned vece, uint32_t dofs, uint32_t aofs, @@ -1068,11 +1061,10 @@ static void expand_2s_vec(unsigned vece, uint32_t dofs, uint32_t aofs, TCGv_vec c, bool scalar_first, void (*fni)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec)) { - TCGv_vec t0 = tcg_temp_new_vec(type); - TCGv_vec t1 = tcg_temp_new_vec(type); - uint32_t i; + for (uint32_t i = 0; i < oprsz; i += tysz) { + TCGv_vec t0 = tcg_temp_new_vec(type); + TCGv_vec t1 = tcg_temp_new_vec(type); - for (i = 0; i < oprsz; i += tysz) { tcg_gen_ld_vec(t0, tcg_env, aofs + i); if (scalar_first) { fni(vece, t1, c, t0); @@ -1081,8 +1073,6 @@ static void expand_2s_vec(unsigned vece, uint32_t dofs, uint32_t aofs, } tcg_gen_st_vec(t1, tcg_env, dofs + i); } - tcg_temp_free_vec(t0); - tcg_temp_free_vec(t1); } /* Expand OPSZ bytes worth of three-operand operations using host vectors. */ @@ -1091,12 +1081,11 @@ static void expand_3_vec(unsigned vece, uint32_t dofs, uint32_t aofs, uint32_t tysz, TCGType type, bool load_dest, void (*fni)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec)) { - TCGv_vec t0 = tcg_temp_new_vec(type); - TCGv_vec t1 = tcg_temp_new_vec(type); - TCGv_vec t2 = tcg_temp_new_vec(type); - uint32_t i; + for (uint32_t i = 0; i < oprsz; i += tysz) { + TCGv_vec t0 = tcg_temp_new_vec(type); + TCGv_vec t1 = tcg_temp_new_vec(type); + TCGv_vec t2 = tcg_temp_new_vec(type); - for (i = 0; i < oprsz; i += tysz) { tcg_gen_ld_vec(t0, tcg_env, aofs + i); tcg_gen_ld_vec(t1, tcg_env, bofs + i); if (load_dest) { @@ -1105,9 +1094,6 @@ static void expand_3_vec(unsigned vece, uint32_t dofs, uint32_t aofs, fni(vece, t2, t0, t1); tcg_gen_st_vec(t2, tcg_env, dofs + i); } - tcg_temp_free_vec(t2); - tcg_temp_free_vec(t1); - tcg_temp_free_vec(t0); } /* @@ -1120,12 +1106,11 @@ static void expand_3i_vec(unsigned vece, uint32_t dofs, uint32_t aofs, void (*fni)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec, int64_t)) { - TCGv_vec t0 = tcg_temp_new_vec(type); - TCGv_vec t1 = tcg_temp_new_vec(type); - TCGv_vec t2 = tcg_temp_new_vec(type); - uint32_t i; + for (uint32_t i = 0; i < oprsz; i += tysz) { + TCGv_vec t0 = tcg_temp_new_vec(type); + TCGv_vec t1 = tcg_temp_new_vec(type); + TCGv_vec t2 = tcg_temp_new_vec(type); - for (i = 0; i < oprsz; i += tysz) { tcg_gen_ld_vec(t0, tcg_env, aofs + i); tcg_gen_ld_vec(t1, tcg_env, bofs + i); if (load_dest) { @@ -1134,9 +1119,6 @@ static void expand_3i_vec(unsigned vece, uint32_t dofs, uint32_t aofs, fni(vece, t2, t0, t1, c); tcg_gen_st_vec(t2, tcg_env, dofs + i); } - tcg_temp_free_vec(t0); - tcg_temp_free_vec(t1); - tcg_temp_free_vec(t2); } /* Expand OPSZ bytes worth of four-operand operations using host vectors. */ @@ -1146,13 +1128,12 @@ static void expand_4_vec(unsigned vece, uint32_t dofs, uint32_t aofs, void (*fni)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec, TCGv_vec)) { - TCGv_vec t0 = tcg_temp_new_vec(type); - TCGv_vec t1 = tcg_temp_new_vec(type); - TCGv_vec t2 = tcg_temp_new_vec(type); - TCGv_vec t3 = tcg_temp_new_vec(type); - uint32_t i; + for (uint32_t i = 0; i < oprsz; i += tysz) { + TCGv_vec t0 = tcg_temp_new_vec(type); + TCGv_vec t1 = tcg_temp_new_vec(type); + TCGv_vec t2 = tcg_temp_new_vec(type); + TCGv_vec t3 = tcg_temp_new_vec(type); - for (i = 0; i < oprsz; i += tysz) { tcg_gen_ld_vec(t1, tcg_env, aofs + i); tcg_gen_ld_vec(t2, tcg_env, bofs + i); tcg_gen_ld_vec(t3, tcg_env, cofs + i); @@ -1162,10 +1143,6 @@ static void expand_4_vec(unsigned vece, uint32_t dofs, uint32_t aofs, tcg_gen_st_vec(t1, tcg_env, aofs + i); } } - tcg_temp_free_vec(t3); - tcg_temp_free_vec(t2); - tcg_temp_free_vec(t1); - tcg_temp_free_vec(t0); } /* @@ -1178,23 +1155,18 @@ static void expand_4i_vec(unsigned vece, uint32_t dofs, uint32_t aofs, void (*fni)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec, TCGv_vec, int64_t)) { - TCGv_vec t0 = tcg_temp_new_vec(type); - TCGv_vec t1 = tcg_temp_new_vec(type); - TCGv_vec t2 = tcg_temp_new_vec(type); - TCGv_vec t3 = tcg_temp_new_vec(type); - uint32_t i; + for (uint32_t i = 0; i < oprsz; i += tysz) { + TCGv_vec t0 = tcg_temp_new_vec(type); + TCGv_vec t1 = tcg_temp_new_vec(type); + TCGv_vec t2 = tcg_temp_new_vec(type); + TCGv_vec t3 = tcg_temp_new_vec(type); - for (i = 0; i < oprsz; i += tysz) { tcg_gen_ld_vec(t1, tcg_env, aofs + i); tcg_gen_ld_vec(t2, tcg_env, bofs + i); tcg_gen_ld_vec(t3, tcg_env, cofs + i); fni(vece, t0, t1, t2, t3, c); tcg_gen_st_vec(t0, tcg_env, dofs + i); } - tcg_temp_free_vec(t3); - tcg_temp_free_vec(t2); - tcg_temp_free_vec(t1); - tcg_temp_free_vec(t0); } /* Expand a vector two-operand operation. */ @@ -1732,7 +1704,6 @@ void tcg_gen_gvec_dup_mem(unsigned vece, uint32_t dofs, uint32_t aofs, TCGv_vec t_vec = tcg_temp_new_vec(type); tcg_gen_dup_mem_vec(vece, t_vec, tcg_env, aofs); do_dup_store(type, dofs, oprsz, maxsz, t_vec); - tcg_temp_free_vec(t_vec); } else if (vece <= MO_32) { TCGv_i32 in = tcg_temp_ebb_new_i32(); switch (vece) { @@ -1766,7 +1737,6 @@ void tcg_gen_gvec_dup_mem(unsigned vece, uint32_t dofs, uint32_t aofs, for (i = (aofs == dofs) * 16; i < oprsz; i += 16) { tcg_gen_st_vec(in, tcg_env, dofs + i); } - tcg_temp_free_vec(in); } else { TCGv_i64 in0 = tcg_temp_ebb_new_i64(); TCGv_i64 in1 = tcg_temp_ebb_new_i64(); @@ -1796,7 +1766,6 @@ void tcg_gen_gvec_dup_mem(unsigned vece, uint32_t dofs, uint32_t aofs, for (i = (aofs == dofs) * 32; i < oprsz; i += 32) { tcg_gen_st_vec(in, tcg_env, dofs + i); } - tcg_temp_free_vec(in); } else if (TCG_TARGET_HAS_v128) { TCGv_vec in0 = tcg_temp_new_vec(TCG_TYPE_V128); TCGv_vec in1 = tcg_temp_new_vec(TCG_TYPE_V128); @@ -1807,8 +1776,6 @@ void tcg_gen_gvec_dup_mem(unsigned vece, uint32_t dofs, uint32_t aofs, tcg_gen_st_vec(in0, tcg_env, dofs + i); tcg_gen_st_vec(in1, tcg_env, dofs + i + 16); } - tcg_temp_free_vec(in0); - tcg_temp_free_vec(in1); } else { TCGv_i64 in[4]; int j; @@ -3136,15 +3103,14 @@ static void expand_2sh_vec(unsigned vece, uint32_t dofs, uint32_t aofs, TCGv_i32 shift, void (*fni)(unsigned, TCGv_vec, TCGv_vec, TCGv_i32)) { - TCGv_vec t0 = tcg_temp_new_vec(type); - uint32_t i; + for (uint32_t i = 0; i < oprsz; i += tysz) { + TCGv_vec t0 = tcg_temp_new_vec(type); + TCGv_vec t1 = tcg_temp_new_vec(type); - for (i = 0; i < oprsz; i += tysz) { tcg_gen_ld_vec(t0, tcg_env, aofs + i); - fni(vece, t0, t0, shift); - tcg_gen_st_vec(t0, tcg_env, dofs + i); + fni(vece, t1, t0, shift); + tcg_gen_st_vec(t1, tcg_env, dofs + i); } - tcg_temp_free_vec(t0); } static void @@ -3720,18 +3686,16 @@ static void expand_cmp_vec(unsigned vece, uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t oprsz, uint32_t tysz, TCGType type, TCGCond cond) { - TCGv_vec t0 = tcg_temp_new_vec(type); - TCGv_vec t1 = tcg_temp_new_vec(type); - uint32_t i; + for (uint32_t i = 0; i < oprsz; i += tysz) { + TCGv_vec t0 = tcg_temp_new_vec(type); + TCGv_vec t1 = tcg_temp_new_vec(type); + TCGv_vec t2 = tcg_temp_new_vec(type); - for (i = 0; i < oprsz; i += tysz) { tcg_gen_ld_vec(t0, tcg_env, aofs + i); tcg_gen_ld_vec(t1, tcg_env, bofs + i); - tcg_gen_cmp_vec(cond, vece, t0, t0, t1); - tcg_gen_st_vec(t0, tcg_env, dofs + i); + tcg_gen_cmp_vec(cond, vece, t2, t0, t1); + tcg_gen_st_vec(t2, tcg_env, dofs + i); } - tcg_temp_free_vec(t1); - tcg_temp_free_vec(t0); } void tcg_gen_gvec_cmp(TCGCond cond, unsigned vece, uint32_t dofs, From 986cac1d2a773f6b2d8f1051504a8af512688cbd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 9 Jan 2023 13:59:35 -0800 Subject: [PATCH 514/974] tcg/optimize: Pipe OptContext into reset_ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Will be needed in the next patch. Reviewed-by: Song Gao Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 6b072d4cdb..cbb095b241 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -123,7 +123,7 @@ static inline bool ts_is_copy(TCGTemp *ts) } /* Reset TEMP's state, possibly removing the temp for the list of copies. */ -static void reset_ts(TCGTemp *ts) +static void reset_ts(OptContext *ctx, TCGTemp *ts) { TempOptInfo *ti = ts_info(ts); TempOptInfo *pi = ts_info(ti->prev_copy); @@ -138,9 +138,9 @@ static void reset_ts(TCGTemp *ts) ti->s_mask = 0; } -static void reset_temp(TCGArg arg) +static void reset_temp(OptContext *ctx, TCGArg arg) { - reset_ts(arg_temp(arg)); + reset_ts(ctx, arg_temp(arg)); } /* Initialize and activate a temporary. */ @@ -239,7 +239,7 @@ static bool tcg_opt_gen_mov(OptContext *ctx, TCGOp *op, TCGArg dst, TCGArg src) return true; } - reset_ts(dst_ts); + reset_ts(ctx, dst_ts); di = ts_info(dst_ts); si = ts_info(src_ts); @@ -702,7 +702,7 @@ static void finish_folding(OptContext *ctx, TCGOp *op) nb_oargs = def->nb_oargs; for (i = 0; i < nb_oargs; i++) { TCGTemp *ts = arg_temp(op->args[i]); - reset_ts(ts); + reset_ts(ctx, ts); /* * Save the corresponding known-zero/sign bits mask for the * first output argument (only one supported so far). @@ -1215,14 +1215,14 @@ static bool fold_call(OptContext *ctx, TCGOp *op) for (i = 0; i < nb_globals; i++) { if (test_bit(i, ctx->temps_used.l)) { - reset_ts(&ctx->tcg->temps[i]); + reset_ts(ctx, &ctx->tcg->temps[i]); } } } /* Reset temp data for outputs. */ for (i = 0; i < nb_oargs; i++) { - reset_temp(op->args[i]); + reset_temp(ctx, op->args[i]); } /* Stop optimizing MB across calls. */ From 9f75e52828a967e6c95eb73e726ef1eff4c05496 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 2 Nov 2023 13:37:46 -0700 Subject: [PATCH 515/974] tcg/optimize: Split out cmp_better_copy Compare two temps for "better", split out from finding the best from a whole list. Use TCGKind, which already gives the proper priority. Signed-off-by: Richard Henderson --- tcg/optimize.c | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index cbb095b241..118561f56d 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -122,6 +122,11 @@ static inline bool ts_is_copy(TCGTemp *ts) return ts_info(ts)->next_copy != ts; } +static TCGTemp *cmp_better_copy(TCGTemp *a, TCGTemp *b) +{ + return a->kind < b->kind ? b : a; +} + /* Reset TEMP's state, possibly removing the temp for the list of copies. */ static void reset_ts(OptContext *ctx, TCGTemp *ts) { @@ -174,30 +179,20 @@ static void init_ts_info(OptContext *ctx, TCGTemp *ts) } } -static TCGTemp *find_better_copy(TCGContext *s, TCGTemp *ts) +static TCGTemp *find_better_copy(TCGTemp *ts) { - TCGTemp *i, *g, *l; + TCGTemp *i, *ret; /* If this is already readonly, we can't do better. */ if (temp_readonly(ts)) { return ts; } - g = l = NULL; + ret = ts; for (i = ts_info(ts)->next_copy; i != ts; i = ts_info(i)->next_copy) { - if (temp_readonly(i)) { - return i; - } else if (i->kind > ts->kind) { - if (i->kind == TEMP_GLOBAL) { - g = i; - } else if (i->kind == TEMP_TB) { - l = i; - } - } + ret = cmp_better_copy(ret, i); } - - /* If we didn't find a better representation, return the same temp. */ - return g ? g : l ? l : ts; + return ret; } static bool ts_are_copies(TCGTemp *ts1, TCGTemp *ts2) @@ -672,12 +667,10 @@ static void init_arguments(OptContext *ctx, TCGOp *op, int nb_args) static void copy_propagate(OptContext *ctx, TCGOp *op, int nb_oargs, int nb_iargs) { - TCGContext *s = ctx->tcg; - for (int i = nb_oargs; i < nb_oargs + nb_iargs; i++) { TCGTemp *ts = arg_temp(op->args[i]); if (ts_is_copy(ts)) { - op->args[i] = temp_arg(find_better_copy(s, ts)); + op->args[i] = temp_arg(find_better_copy(ts)); } } } From ab84dc398b3b702b0c692538b947ef65dbbdf52f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 23 Aug 2023 23:04:24 -0700 Subject: [PATCH 516/974] tcg/optimize: Optimize env memory operations Propagate stores to loads, loads to loads. Reviewed-by: Song Gao Signed-off-by: Richard Henderson --- tcg/optimize.c | 264 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 243 insertions(+), 21 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 118561f56d..b32ef0be0f 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -25,6 +25,7 @@ #include "qemu/osdep.h" #include "qemu/int128.h" +#include "qemu/interval-tree.h" #include "tcg/tcg-op-common.h" #include "tcg-internal.h" @@ -37,10 +38,18 @@ glue(glue(case INDEX_op_, x), _i64): \ glue(glue(case INDEX_op_, x), _vec) +typedef struct MemCopyInfo { + IntervalTreeNode itree; + QSIMPLEQ_ENTRY (MemCopyInfo) next; + TCGTemp *ts; + TCGType type; +} MemCopyInfo; + typedef struct TempOptInfo { bool is_const; TCGTemp *prev_copy; TCGTemp *next_copy; + QSIMPLEQ_HEAD(, MemCopyInfo) mem_copy; uint64_t val; uint64_t z_mask; /* mask bit is 0 if and only if value bit is 0 */ uint64_t s_mask; /* a left-aligned mask of clrsb(value) bits. */ @@ -51,6 +60,9 @@ typedef struct OptContext { TCGOp *prev_mb; TCGTempSet temps_used; + IntervalTreeRoot mem_copy; + QSIMPLEQ_HEAD(, MemCopyInfo) mem_free; + /* In flight values from optimization. */ uint64_t a_mask; /* mask bit is 0 iff value identical to first input */ uint64_t z_mask; /* mask bit is 0 iff value bit is 0 */ @@ -127,27 +139,6 @@ static TCGTemp *cmp_better_copy(TCGTemp *a, TCGTemp *b) return a->kind < b->kind ? b : a; } -/* Reset TEMP's state, possibly removing the temp for the list of copies. */ -static void reset_ts(OptContext *ctx, TCGTemp *ts) -{ - TempOptInfo *ti = ts_info(ts); - TempOptInfo *pi = ts_info(ti->prev_copy); - TempOptInfo *ni = ts_info(ti->next_copy); - - ni->prev_copy = ti->prev_copy; - pi->next_copy = ti->next_copy; - ti->next_copy = ts; - ti->prev_copy = ts; - ti->is_const = false; - ti->z_mask = -1; - ti->s_mask = 0; -} - -static void reset_temp(OptContext *ctx, TCGArg arg) -{ - reset_ts(ctx, arg_temp(arg)); -} - /* Initialize and activate a temporary. */ static void init_ts_info(OptContext *ctx, TCGTemp *ts) { @@ -167,6 +158,7 @@ static void init_ts_info(OptContext *ctx, TCGTemp *ts) ti->next_copy = ts; ti->prev_copy = ts; + QSIMPLEQ_INIT(&ti->mem_copy); if (ts->kind == TEMP_CONST) { ti->is_const = true; ti->val = ts->val; @@ -179,6 +171,45 @@ static void init_ts_info(OptContext *ctx, TCGTemp *ts) } } +static MemCopyInfo *mem_copy_first(OptContext *ctx, intptr_t s, intptr_t l) +{ + IntervalTreeNode *r = interval_tree_iter_first(&ctx->mem_copy, s, l); + return r ? container_of(r, MemCopyInfo, itree) : NULL; +} + +static MemCopyInfo *mem_copy_next(MemCopyInfo *mem, intptr_t s, intptr_t l) +{ + IntervalTreeNode *r = interval_tree_iter_next(&mem->itree, s, l); + return r ? container_of(r, MemCopyInfo, itree) : NULL; +} + +static void remove_mem_copy(OptContext *ctx, MemCopyInfo *mc) +{ + TCGTemp *ts = mc->ts; + TempOptInfo *ti = ts_info(ts); + + interval_tree_remove(&mc->itree, &ctx->mem_copy); + QSIMPLEQ_REMOVE(&ti->mem_copy, mc, MemCopyInfo, next); + QSIMPLEQ_INSERT_TAIL(&ctx->mem_free, mc, next); +} + +static void remove_mem_copy_in(OptContext *ctx, intptr_t s, intptr_t l) +{ + while (true) { + MemCopyInfo *mc = mem_copy_first(ctx, s, l); + if (!mc) { + break; + } + remove_mem_copy(ctx, mc); + } +} + +static void remove_mem_copy_all(OptContext *ctx) +{ + remove_mem_copy_in(ctx, 0, -1); + tcg_debug_assert(interval_tree_is_empty(&ctx->mem_copy)); +} + static TCGTemp *find_better_copy(TCGTemp *ts) { TCGTemp *i, *ret; @@ -195,6 +226,80 @@ static TCGTemp *find_better_copy(TCGTemp *ts) return ret; } +static void move_mem_copies(TCGTemp *dst_ts, TCGTemp *src_ts) +{ + TempOptInfo *si = ts_info(src_ts); + TempOptInfo *di = ts_info(dst_ts); + MemCopyInfo *mc; + + QSIMPLEQ_FOREACH(mc, &si->mem_copy, next) { + tcg_debug_assert(mc->ts == src_ts); + mc->ts = dst_ts; + } + QSIMPLEQ_CONCAT(&di->mem_copy, &si->mem_copy); +} + +/* Reset TEMP's state, possibly removing the temp for the list of copies. */ +static void reset_ts(OptContext *ctx, TCGTemp *ts) +{ + TempOptInfo *ti = ts_info(ts); + TCGTemp *pts = ti->prev_copy; + TCGTemp *nts = ti->next_copy; + TempOptInfo *pi = ts_info(pts); + TempOptInfo *ni = ts_info(nts); + + ni->prev_copy = ti->prev_copy; + pi->next_copy = ti->next_copy; + ti->next_copy = ts; + ti->prev_copy = ts; + ti->is_const = false; + ti->z_mask = -1; + ti->s_mask = 0; + + if (!QSIMPLEQ_EMPTY(&ti->mem_copy)) { + if (ts == nts) { + /* Last temp copy being removed, the mem copies die. */ + MemCopyInfo *mc; + QSIMPLEQ_FOREACH(mc, &ti->mem_copy, next) { + interval_tree_remove(&mc->itree, &ctx->mem_copy); + } + QSIMPLEQ_CONCAT(&ctx->mem_free, &ti->mem_copy); + } else { + move_mem_copies(find_better_copy(nts), ts); + } + } +} + +static void reset_temp(OptContext *ctx, TCGArg arg) +{ + reset_ts(ctx, arg_temp(arg)); +} + +static void record_mem_copy(OptContext *ctx, TCGType type, + TCGTemp *ts, intptr_t start, intptr_t last) +{ + MemCopyInfo *mc; + TempOptInfo *ti; + + mc = QSIMPLEQ_FIRST(&ctx->mem_free); + if (mc) { + QSIMPLEQ_REMOVE_HEAD(&ctx->mem_free, next); + } else { + mc = tcg_malloc(sizeof(*mc)); + } + + memset(mc, 0, sizeof(*mc)); + mc->itree.start = start; + mc->itree.last = last; + mc->type = type; + interval_tree_insert(&mc->itree, &ctx->mem_copy); + + ts = find_better_copy(ts); + ti = ts_info(ts); + mc->ts = ts; + QSIMPLEQ_INSERT_TAIL(&ti->mem_copy, mc, next); +} + static bool ts_are_copies(TCGTemp *ts1, TCGTemp *ts2) { TCGTemp *i; @@ -221,6 +326,18 @@ static bool args_are_copies(TCGArg arg1, TCGArg arg2) return ts_are_copies(arg_temp(arg1), arg_temp(arg2)); } +static TCGTemp *find_mem_copy_for(OptContext *ctx, TCGType type, intptr_t s) +{ + MemCopyInfo *mc; + + for (mc = mem_copy_first(ctx, s, s); mc; mc = mem_copy_next(mc, s, s)) { + if (mc->itree.start == s && mc->type == type) { + return find_better_copy(mc->ts); + } + } + return NULL; +} + static bool tcg_opt_gen_mov(OptContext *ctx, TCGOp *op, TCGArg dst, TCGArg src) { TCGTemp *dst_ts = arg_temp(dst); @@ -270,6 +387,11 @@ static bool tcg_opt_gen_mov(OptContext *ctx, TCGOp *op, TCGArg dst, TCGArg src) si->next_copy = dst_ts; di->is_const = si->is_const; di->val = si->val; + + if (!QSIMPLEQ_EMPTY(&si->mem_copy) + && cmp_better_copy(src_ts, dst_ts) == dst_ts) { + move_mem_copies(dst_ts, src_ts); + } } return true; } @@ -688,6 +810,7 @@ static void finish_folding(OptContext *ctx, TCGOp *op) ctx->prev_mb = NULL; if (!(def->flags & TCG_OPF_COND_BRANCH)) { memset(&ctx->temps_used, 0, sizeof(ctx->temps_used)); + remove_mem_copy_all(ctx); } return; } @@ -1213,6 +1336,11 @@ static bool fold_call(OptContext *ctx, TCGOp *op) } } + /* If the function has side effects, reset mem data. */ + if (!(flags & TCG_CALL_NO_SIDE_EFFECTS)) { + remove_mem_copy_all(ctx); + } + /* Reset temp data for outputs. */ for (i = 0; i < nb_oargs; i++) { reset_temp(ctx, op->args[i]); @@ -2070,6 +2198,83 @@ static bool fold_tcg_ld(OptContext *ctx, TCGOp *op) return false; } +static bool fold_tcg_ld_memcopy(OptContext *ctx, TCGOp *op) +{ + TCGTemp *dst, *src; + intptr_t ofs; + TCGType type; + + if (op->args[1] != tcgv_ptr_arg(tcg_env)) { + return false; + } + + type = ctx->type; + ofs = op->args[2]; + dst = arg_temp(op->args[0]); + src = find_mem_copy_for(ctx, type, ofs); + if (src && src->base_type == type) { + return tcg_opt_gen_mov(ctx, op, temp_arg(dst), temp_arg(src)); + } + + reset_ts(ctx, dst); + record_mem_copy(ctx, type, dst, ofs, ofs + tcg_type_size(type) - 1); + return true; +} + +static bool fold_tcg_st(OptContext *ctx, TCGOp *op) +{ + intptr_t ofs = op->args[2]; + intptr_t lm1; + + if (op->args[1] != tcgv_ptr_arg(tcg_env)) { + remove_mem_copy_all(ctx); + return false; + } + + switch (op->opc) { + CASE_OP_32_64(st8): + lm1 = 0; + break; + CASE_OP_32_64(st16): + lm1 = 1; + break; + case INDEX_op_st32_i64: + case INDEX_op_st_i32: + lm1 = 3; + break; + case INDEX_op_st_i64: + lm1 = 7; + break; + case INDEX_op_st_vec: + lm1 = tcg_type_size(ctx->type) - 1; + break; + default: + g_assert_not_reached(); + } + remove_mem_copy_in(ctx, ofs, ofs + lm1); + return false; +} + +static bool fold_tcg_st_memcopy(OptContext *ctx, TCGOp *op) +{ + TCGTemp *src; + intptr_t ofs, last; + TCGType type; + + if (op->args[1] != tcgv_ptr_arg(tcg_env)) { + fold_tcg_st(ctx, op); + return false; + } + + src = arg_temp(op->args[0]); + ofs = op->args[2]; + type = ctx->type; + last = ofs + tcg_type_size(type) - 1; + remove_mem_copy_in(ctx, ofs, last); + record_mem_copy(ctx, type, src, ofs, last); + return false; +} + static bool fold_xor(OptContext *ctx, TCGOp *op) { if (fold_const2_commutative(ctx, op) || @@ -2093,6 +2298,8 @@ void tcg_optimize(TCGContext *s) TCGOp *op, *op_next; OptContext ctx = { .tcg = s }; + QSIMPLEQ_INIT(&ctx.mem_free); + /* Array VALS has an element for each temp. If this temp holds a constant then its value is kept in VALS' element. If this temp is a copy of other ones then the other copies are @@ -2214,6 +2421,21 @@ void tcg_optimize(TCGContext *s) case INDEX_op_ld32u_i64: done = fold_tcg_ld(&ctx, op); break; + case INDEX_op_ld_i32: + case INDEX_op_ld_i64: + case INDEX_op_ld_vec: + done = fold_tcg_ld_memcopy(&ctx, op); + break; + CASE_OP_32_64(st8): + CASE_OP_32_64(st16): + case INDEX_op_st32_i64: + done = fold_tcg_st(&ctx, op); + break; + case INDEX_op_st_i32: + case INDEX_op_st_i64: + case INDEX_op_st_vec: + done = fold_tcg_st_memcopy(&ctx, op); + break; case INDEX_op_mb: done = fold_mb(&ctx, op); break; From 3eaadaeb4ee9cc6a1267882b3e31c893cd99bb9e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 23 Aug 2023 23:13:06 -0700 Subject: [PATCH 517/974] tcg: Eliminate duplicate env store operations Notice when a constant is stored to the same location twice. Reviewed-by: Song Gao Signed-off-by: Richard Henderson --- tcg/optimize.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tcg/optimize.c b/tcg/optimize.c index b32ef0be0f..a4fe9ee9bb 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2269,6 +2269,19 @@ static bool fold_tcg_st_memcopy(OptContext *ctx, TCGOp *op) src = arg_temp(op->args[0]); ofs = op->args[2]; type = ctx->type; + + /* + * Eliminate duplicate stores of a constant. + * This happens frequently when the target ISA zero-extends. + */ + if (ts_is_const(src)) { + TCGTemp *prev = find_mem_copy_for(ctx, type, ofs); + if (src == prev) { + tcg_op_remove(ctx->tcg, op); + return true; + } + } + last = ofs + tcg_type_size(type) - 1; remove_mem_copy_in(ctx, ofs, last); record_mem_copy(ctx, type, src, ofs, last); From 26aac97c849f28e23bc818035bac38be34e62983 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 23 Oct 2023 12:31:57 -0700 Subject: [PATCH 518/974] tcg/optimize: Split out arg_new_constant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes a bug wherein raw uses of tcg_constant_internal do not have their TempOptInfo initialized. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index a4fe9ee9bb..d8e437c826 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -338,6 +338,21 @@ static TCGTemp *find_mem_copy_for(OptContext *ctx, TCGType type, intptr_t s) return NULL; } +static TCGArg arg_new_constant(OptContext *ctx, uint64_t val) +{ + TCGType type = ctx->type; + TCGTemp *ts; + + if (type == TCG_TYPE_I32) { + val = (int32_t)val; + } + + ts = tcg_constant_internal(type, val); + init_ts_info(ctx, ts); + + return temp_arg(ts); +} + static bool tcg_opt_gen_mov(OptContext *ctx, TCGOp *op, TCGArg dst, TCGArg src) { TCGTemp *dst_ts = arg_temp(dst); @@ -399,16 +414,8 @@ static bool tcg_opt_gen_mov(OptContext *ctx, TCGOp *op, TCGArg dst, TCGArg src) static bool tcg_opt_gen_movi(OptContext *ctx, TCGOp *op, TCGArg dst, uint64_t val) { - TCGTemp *tv; - - if (ctx->type == TCG_TYPE_I32) { - val = (int32_t)val; - } - /* Convert movi to mov with constant temp. */ - tv = tcg_constant_internal(ctx->type, val); - init_ts_info(ctx, tv); - return tcg_opt_gen_mov(ctx, op, dst, temp_arg(tv)); + return tcg_opt_gen_mov(ctx, op, dst, arg_new_constant(ctx, val)); } static uint64_t do_constant_folding_2(TCGOpcode op, uint64_t x, uint64_t y) @@ -1431,7 +1438,7 @@ static bool fold_deposit(OptContext *ctx, TCGOp *op) op->opc = and_opc; op->args[1] = op->args[2]; - op->args[2] = temp_arg(tcg_constant_internal(ctx->type, mask)); + op->args[2] = arg_new_constant(ctx, mask); ctx->z_mask = mask & arg_info(op->args[1])->z_mask; return false; } @@ -1442,7 +1449,7 @@ static bool fold_deposit(OptContext *ctx, TCGOp *op) uint64_t mask = deposit64(-1, op->args[3], op->args[4], 0); op->opc = and_opc; - op->args[2] = temp_arg(tcg_constant_internal(ctx->type, mask)); + op->args[2] = arg_new_constant(ctx, mask); ctx->z_mask = mask & arg_info(op->args[1])->z_mask; return false; } From 1551004eeb4436a163472c3b5b108a60766e74cb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 25 Oct 2023 18:39:42 -0700 Subject: [PATCH 519/974] tcg: Canonicalize subi to addi during opcode generation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Suggested-by: Paolo Bonzini Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20231026013945.1152174-2-richard.henderson@linaro.org> --- tcg/tcg-op.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index de096a6f93..aa6bc6f57d 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -372,12 +372,7 @@ void tcg_gen_subfi_i32(TCGv_i32 ret, int32_t arg1, TCGv_i32 arg2) void tcg_gen_subi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) { - /* some cases can be optimized here */ - if (arg2 == 0) { - tcg_gen_mov_i32(ret, arg1); - } else { - tcg_gen_sub_i32(ret, arg1, tcg_constant_i32(arg2)); - } + tcg_gen_addi_i32(ret, arg1, -arg2); } void tcg_gen_neg_i32(TCGv_i32 ret, TCGv_i32 arg) @@ -1752,16 +1747,7 @@ void tcg_gen_subfi_i64(TCGv_i64 ret, int64_t arg1, TCGv_i64 arg2) void tcg_gen_subi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) { - /* some cases can be optimized here */ - if (arg2 == 0) { - tcg_gen_mov_i64(ret, arg1); - } else if (TCG_TARGET_REG_BITS == 64) { - tcg_gen_sub_i64(ret, arg1, tcg_constant_i64(arg2)); - } else { - tcg_gen_sub2_i32(TCGV_LOW(ret), TCGV_HIGH(ret), - TCGV_LOW(arg1), TCGV_HIGH(arg1), - tcg_constant_i32(arg2), tcg_constant_i32(arg2 >> 32)); - } + tcg_gen_addi_i64(ret, arg1, -arg2); } void tcg_gen_neg_i64(TCGv_i64 ret, TCGv_i64 arg) From 6334a968eec3f2498a992a7b96c1bfcaf100066f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 25 Oct 2023 18:39:43 -0700 Subject: [PATCH 520/974] tcg/optimize: Canonicalize subi to addi during optimization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20231026013945.1152174-3-richard.henderson@linaro.org> --- tcg/optimize.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index d8e437c826..468f827399 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2166,7 +2166,19 @@ static bool fold_sub_vec(OptContext *ctx, TCGOp *op) static bool fold_sub(OptContext *ctx, TCGOp *op) { - return fold_const2(ctx, op) || fold_sub_vec(ctx, op); + if (fold_const2(ctx, op) || fold_sub_vec(ctx, op)) { + return true; + } + + /* Fold sub r,x,i to add r,x,-i */ + if (arg_is_const(op->args[2])) { + uint64_t val = arg_info(op->args[2])->val; + + op->opc = (ctx->type == TCG_TYPE_I32 + ? INDEX_op_add_i32 : INDEX_op_add_i64); + op->args[2] = arg_new_constant(ctx, -val); + } + return false; } static bool fold_sub2(OptContext *ctx, TCGOp *op) From f245757701ccd2d4f1c9ee0ed349e3a1d8049996 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 25 Oct 2023 18:39:44 -0700 Subject: [PATCH 521/974] tcg/optimize: Canonicalize sub2 with constants to add2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20231026013945.1152174-4-richard.henderson@linaro.org> --- tcg/optimize.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 468f827399..f2d01654c5 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1044,8 +1044,10 @@ static bool fold_add_vec(OptContext *ctx, TCGOp *op) static bool fold_addsub2(OptContext *ctx, TCGOp *op, bool add) { - if (arg_is_const(op->args[2]) && arg_is_const(op->args[3]) && - arg_is_const(op->args[4]) && arg_is_const(op->args[5])) { + bool a_const = arg_is_const(op->args[2]) && arg_is_const(op->args[3]); + bool b_const = arg_is_const(op->args[4]) && arg_is_const(op->args[5]); + + if (a_const && b_const) { uint64_t al = arg_info(op->args[2])->val; uint64_t ah = arg_info(op->args[3])->val; uint64_t bl = arg_info(op->args[4])->val; @@ -1089,6 +1091,21 @@ static bool fold_addsub2(OptContext *ctx, TCGOp *op, bool add) tcg_opt_gen_movi(ctx, op2, rh, ah); return true; } + + /* Fold sub2 r,x,i to add2 r,x,-i */ + if (!add && b_const) { + uint64_t bl = arg_info(op->args[4])->val; + uint64_t bh = arg_info(op->args[5])->val; + + /* Negate the two parts without assembling and disassembling. */ + bl = -bl; + bh = ~bh + !bl; + + op->opc = (ctx->type == TCG_TYPE_I32 + ? INDEX_op_add2_i32 : INDEX_op_add2_i64); + op->args[4] = arg_new_constant(ctx, bl); + op->args[5] = arg_new_constant(ctx, bh); + } return false; } From d36ce28be424385fc9f7273bf5c15ce815b5cf4e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 24 Oct 2023 14:17:07 -0700 Subject: [PATCH 522/974] tcg/sparc64: Implement tcg_out_extrl_i64_i32 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Build fix for missing symbol. Cc: qemu-stable@nongnu.org Fixes: dad2f2f5af ("tcg/sparc64: Disable TCG_TARGET_HAS_extr_i64_i32") Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/sparc64/tcg-target.c.inc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 19d9df4a09..a91defd0ac 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -529,6 +529,11 @@ static void tcg_out_extu_i32_i64(TCGContext *s, TCGReg rd, TCGReg rs) tcg_out_ext32u(s, rd, rs); } +static void tcg_out_extrl_i64_i32(TCGContext *s, TCGReg rd, TCGReg rs) +{ + tcg_out_ext32u(s, rd, rs); +} + static bool tcg_out_xchg(TCGContext *s, TCGType type, TCGReg r1, TCGReg r2) { return false; From 12b12a14c73ee200c7cff42aab6113a0649fa132 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 12 Oct 2023 13:46:01 -0300 Subject: [PATCH 523/974] target/riscv: rename ext_ifencei to ext_zifencei Add a leading 'z' to improve grepping. When one wants to search for uses of zifencei they're more likely to do 'grep -i zifencei' than 'grep -i ifencei'. Suggested-by: Andrew Jones Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Reviewed-by: Andrew Jones Message-ID: <20231012164604.398496-2-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 22 +++++++++++----------- target/riscv/cpu_cfg.h | 2 +- target/riscv/insn_trans/trans_rvi.c.inc | 2 +- target/riscv/tcg/tcg-cpu.c | 8 ++++---- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index ac4a6c7eec..3693eabb34 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -80,7 +80,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zicboz, PRIV_VERSION_1_12_0, ext_icboz), ISA_EXT_DATA_ENTRY(zicond, PRIV_VERSION_1_12_0, ext_zicond), ISA_EXT_DATA_ENTRY(zicsr, PRIV_VERSION_1_10_0, ext_icsr), - ISA_EXT_DATA_ENTRY(zifencei, PRIV_VERSION_1_10_0, ext_ifencei), + ISA_EXT_DATA_ENTRY(zifencei, PRIV_VERSION_1_10_0, ext_zifencei), ISA_EXT_DATA_ENTRY(zihintntl, PRIV_VERSION_1_10_0, ext_zihintntl), ISA_EXT_DATA_ENTRY(zihintpause, PRIV_VERSION_1_10_0, ext_zihintpause), ISA_EXT_DATA_ENTRY(zmmul, PRIV_VERSION_1_12_0, ext_zmmul), @@ -382,7 +382,7 @@ static void riscv_any_cpu_init(Object *obj) env->priv_ver = PRIV_VERSION_LATEST; /* inherited from parent obj via riscv_cpu_init() */ - cpu->cfg.ext_ifencei = true; + cpu->cfg.ext_zifencei = true; cpu->cfg.ext_icsr = true; cpu->cfg.mmu = true; cpu->cfg.pmp = true; @@ -430,7 +430,7 @@ static void rv64_sifive_u_cpu_init(Object *obj) #endif /* inherited from parent obj via riscv_cpu_init() */ - cpu->cfg.ext_ifencei = true; + cpu->cfg.ext_zifencei = true; cpu->cfg.ext_icsr = true; cpu->cfg.mmu = true; cpu->cfg.pmp = true; @@ -448,7 +448,7 @@ static void rv64_sifive_e_cpu_init(Object *obj) #endif /* inherited from parent obj via riscv_cpu_init() */ - cpu->cfg.ext_ifencei = true; + cpu->cfg.ext_zifencei = true; cpu->cfg.ext_icsr = true; cpu->cfg.pmp = true; } @@ -494,7 +494,7 @@ static void rv64_veyron_v1_cpu_init(Object *obj) /* Enable ISA extensions */ cpu->cfg.mmu = true; - cpu->cfg.ext_ifencei = true; + cpu->cfg.ext_zifencei = true; cpu->cfg.ext_icsr = true; cpu->cfg.pmp = true; cpu->cfg.ext_icbom = true; @@ -566,7 +566,7 @@ static void rv32_sifive_u_cpu_init(Object *obj) #endif /* inherited from parent obj via riscv_cpu_init() */ - cpu->cfg.ext_ifencei = true; + cpu->cfg.ext_zifencei = true; cpu->cfg.ext_icsr = true; cpu->cfg.mmu = true; cpu->cfg.pmp = true; @@ -584,7 +584,7 @@ static void rv32_sifive_e_cpu_init(Object *obj) #endif /* inherited from parent obj via riscv_cpu_init() */ - cpu->cfg.ext_ifencei = true; + cpu->cfg.ext_zifencei = true; cpu->cfg.ext_icsr = true; cpu->cfg.pmp = true; } @@ -602,7 +602,7 @@ static void rv32_ibex_cpu_init(Object *obj) cpu->cfg.epmp = true; /* inherited from parent obj via riscv_cpu_init() */ - cpu->cfg.ext_ifencei = true; + cpu->cfg.ext_zifencei = true; cpu->cfg.ext_icsr = true; cpu->cfg.pmp = true; } @@ -619,7 +619,7 @@ static void rv32_imafcu_nommu_cpu_init(Object *obj) #endif /* inherited from parent obj via riscv_cpu_init() */ - cpu->cfg.ext_ifencei = true; + cpu->cfg.ext_zifencei = true; cpu->cfg.ext_icsr = true; cpu->cfg.pmp = true; } @@ -1242,7 +1242,7 @@ const char *riscv_get_misa_ext_description(uint32_t bit) const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = { /* Defaults for standard extensions */ MULTI_EXT_CFG_BOOL("sscofpmf", ext_sscofpmf, false), - MULTI_EXT_CFG_BOOL("zifencei", ext_ifencei, true), + MULTI_EXT_CFG_BOOL("zifencei", ext_zifencei, true), MULTI_EXT_CFG_BOOL("zicsr", ext_icsr, true), MULTI_EXT_CFG_BOOL("zihintntl", ext_zihintntl, true), MULTI_EXT_CFG_BOOL("zihintpause", ext_zihintpause, true), @@ -1347,7 +1347,7 @@ const RISCVCPUMultiExtConfig riscv_cpu_experimental_exts[] = { /* Deprecated entries marked for future removal */ const RISCVCPUMultiExtConfig riscv_cpu_deprecated_exts[] = { - MULTI_EXT_CFG_BOOL("Zifencei", ext_ifencei, true), + MULTI_EXT_CFG_BOOL("Zifencei", ext_zifencei, true), MULTI_EXT_CFG_BOOL("Zicsr", ext_icsr, true), MULTI_EXT_CFG_BOOL("Zihintntl", ext_zihintntl, true), MULTI_EXT_CFG_BOOL("Zihintpause", ext_zihintpause, true), diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index 0e6a0f245c..a3f96eb878 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -61,7 +61,7 @@ struct RISCVCPUConfig { bool ext_zksed; bool ext_zksh; bool ext_zkt; - bool ext_ifencei; + bool ext_zifencei; bool ext_icsr; bool ext_icbom; bool ext_icboz; diff --git a/target/riscv/insn_trans/trans_rvi.c.inc b/target/riscv/insn_trans/trans_rvi.c.inc index 25cb60558a..faf6d65064 100644 --- a/target/riscv/insn_trans/trans_rvi.c.inc +++ b/target/riscv/insn_trans/trans_rvi.c.inc @@ -799,7 +799,7 @@ static bool trans_fence(DisasContext *ctx, arg_fence *a) static bool trans_fence_i(DisasContext *ctx, arg_fence_i *a) { - if (!ctx->cfg_ptr->ext_ifencei) { + if (!ctx->cfg_ptr->ext_zifencei) { return false; } diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index a28918ab30..9b8f3f54a7 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -278,7 +278,7 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) !(riscv_has_ext(env, RVI) && riscv_has_ext(env, RVM) && riscv_has_ext(env, RVA) && riscv_has_ext(env, RVF) && riscv_has_ext(env, RVD) && - cpu->cfg.ext_icsr && cpu->cfg.ext_ifencei)) { + cpu->cfg.ext_icsr && cpu->cfg.ext_zifencei)) { if (cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_icsr)) && !cpu->cfg.ext_icsr) { @@ -286,15 +286,15 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) return; } - if (cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_ifencei)) && - !cpu->cfg.ext_ifencei) { + if (cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_zifencei)) && + !cpu->cfg.ext_zifencei) { error_setg(errp, "RVG requires Zifencei but user set " "Zifencei to false"); return; } cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_icsr), true); - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_ifencei), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zifencei), true); env->misa_ext |= RVI | RVM | RVA | RVF | RVD; env->misa_ext_mask |= RVI | RVM | RVA | RVF | RVD; From 960b389b7d15f2ebb2b4d75d98d5ffec2c6a8348 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 12 Oct 2023 13:46:02 -0300 Subject: [PATCH 524/974] target/riscv: rename ext_icsr to ext_zicsr Add a leading 'z' to improve grepping. When one wants to search for uses of zicsr they're more likely to do 'grep -i zicsr' than 'grep -i icsr'. Suggested-by: Andrew Jones Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Reviewed-by: Andrew Jones Message-ID: <20231012164604.398496-3-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/boot.c | 2 +- target/riscv/cpu.c | 22 +++++++++++----------- target/riscv/cpu_cfg.h | 2 +- target/riscv/csr.c | 2 +- target/riscv/gdbstub.c | 2 +- target/riscv/tcg/tcg-cpu.c | 14 +++++++------- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c index 52bf8e67de..0ffca05189 100644 --- a/hw/riscv/boot.c +++ b/hw/riscv/boot.c @@ -414,7 +414,7 @@ void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts reset_vec[4] = 0x0182b283; /* ld t0, 24(t0) */ } - if (!harts->harts[0].cfg.ext_icsr) { + if (!harts->harts[0].cfg.ext_zicsr) { /* * The Zicsr extension has been disabled, so let's ensure we don't * run the CSR instruction. Let's fill the address with a non diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 3693eabb34..566b7545e8 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -79,7 +79,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zicbom, PRIV_VERSION_1_12_0, ext_icbom), ISA_EXT_DATA_ENTRY(zicboz, PRIV_VERSION_1_12_0, ext_icboz), ISA_EXT_DATA_ENTRY(zicond, PRIV_VERSION_1_12_0, ext_zicond), - ISA_EXT_DATA_ENTRY(zicsr, PRIV_VERSION_1_10_0, ext_icsr), + ISA_EXT_DATA_ENTRY(zicsr, PRIV_VERSION_1_10_0, ext_zicsr), ISA_EXT_DATA_ENTRY(zifencei, PRIV_VERSION_1_10_0, ext_zifencei), ISA_EXT_DATA_ENTRY(zihintntl, PRIV_VERSION_1_10_0, ext_zihintntl), ISA_EXT_DATA_ENTRY(zihintpause, PRIV_VERSION_1_10_0, ext_zihintpause), @@ -383,7 +383,7 @@ static void riscv_any_cpu_init(Object *obj) /* inherited from parent obj via riscv_cpu_init() */ cpu->cfg.ext_zifencei = true; - cpu->cfg.ext_icsr = true; + cpu->cfg.ext_zicsr = true; cpu->cfg.mmu = true; cpu->cfg.pmp = true; } @@ -431,7 +431,7 @@ static void rv64_sifive_u_cpu_init(Object *obj) /* inherited from parent obj via riscv_cpu_init() */ cpu->cfg.ext_zifencei = true; - cpu->cfg.ext_icsr = true; + cpu->cfg.ext_zicsr = true; cpu->cfg.mmu = true; cpu->cfg.pmp = true; } @@ -449,7 +449,7 @@ static void rv64_sifive_e_cpu_init(Object *obj) /* inherited from parent obj via riscv_cpu_init() */ cpu->cfg.ext_zifencei = true; - cpu->cfg.ext_icsr = true; + cpu->cfg.ext_zicsr = true; cpu->cfg.pmp = true; } @@ -495,7 +495,7 @@ static void rv64_veyron_v1_cpu_init(Object *obj) /* Enable ISA extensions */ cpu->cfg.mmu = true; cpu->cfg.ext_zifencei = true; - cpu->cfg.ext_icsr = true; + cpu->cfg.ext_zicsr = true; cpu->cfg.pmp = true; cpu->cfg.ext_icbom = true; cpu->cfg.cbom_blocksize = 64; @@ -567,7 +567,7 @@ static void rv32_sifive_u_cpu_init(Object *obj) /* inherited from parent obj via riscv_cpu_init() */ cpu->cfg.ext_zifencei = true; - cpu->cfg.ext_icsr = true; + cpu->cfg.ext_zicsr = true; cpu->cfg.mmu = true; cpu->cfg.pmp = true; } @@ -585,7 +585,7 @@ static void rv32_sifive_e_cpu_init(Object *obj) /* inherited from parent obj via riscv_cpu_init() */ cpu->cfg.ext_zifencei = true; - cpu->cfg.ext_icsr = true; + cpu->cfg.ext_zicsr = true; cpu->cfg.pmp = true; } @@ -603,7 +603,7 @@ static void rv32_ibex_cpu_init(Object *obj) /* inherited from parent obj via riscv_cpu_init() */ cpu->cfg.ext_zifencei = true; - cpu->cfg.ext_icsr = true; + cpu->cfg.ext_zicsr = true; cpu->cfg.pmp = true; } @@ -620,7 +620,7 @@ static void rv32_imafcu_nommu_cpu_init(Object *obj) /* inherited from parent obj via riscv_cpu_init() */ cpu->cfg.ext_zifencei = true; - cpu->cfg.ext_icsr = true; + cpu->cfg.ext_zicsr = true; cpu->cfg.pmp = true; } #endif @@ -1243,7 +1243,7 @@ const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = { /* Defaults for standard extensions */ MULTI_EXT_CFG_BOOL("sscofpmf", ext_sscofpmf, false), MULTI_EXT_CFG_BOOL("zifencei", ext_zifencei, true), - MULTI_EXT_CFG_BOOL("zicsr", ext_icsr, true), + MULTI_EXT_CFG_BOOL("zicsr", ext_zicsr, true), MULTI_EXT_CFG_BOOL("zihintntl", ext_zihintntl, true), MULTI_EXT_CFG_BOOL("zihintpause", ext_zihintpause, true), MULTI_EXT_CFG_BOOL("zawrs", ext_zawrs, true), @@ -1348,7 +1348,7 @@ const RISCVCPUMultiExtConfig riscv_cpu_experimental_exts[] = { /* Deprecated entries marked for future removal */ const RISCVCPUMultiExtConfig riscv_cpu_deprecated_exts[] = { MULTI_EXT_CFG_BOOL("Zifencei", ext_zifencei, true), - MULTI_EXT_CFG_BOOL("Zicsr", ext_icsr, true), + MULTI_EXT_CFG_BOOL("Zicsr", ext_zicsr, true), MULTI_EXT_CFG_BOOL("Zihintntl", ext_zihintntl, true), MULTI_EXT_CFG_BOOL("Zihintpause", ext_zihintpause, true), MULTI_EXT_CFG_BOOL("Zawrs", ext_zawrs, true), diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index a3f96eb878..9ea30da7e0 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -62,7 +62,7 @@ struct RISCVCPUConfig { bool ext_zksh; bool ext_zkt; bool ext_zifencei; - bool ext_icsr; + bool ext_zicsr; bool ext_icbom; bool ext_icboz; bool ext_zicond; diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 4b4ab56c40..30cc21e979 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -3858,7 +3858,7 @@ static inline RISCVException riscv_csrrw_check(CPURISCVState *env, int csr_min_priv = csr_ops[csrno].min_priv_ver; /* ensure the CSR extension is enabled */ - if (!riscv_cpu_cfg(env)->ext_icsr) { + if (!riscv_cpu_cfg(env)->ext_zicsr) { return RISCV_EXCP_ILLEGAL_INST; } diff --git a/target/riscv/gdbstub.c b/target/riscv/gdbstub.c index 524bede865..58b3ace0fe 100644 --- a/target/riscv/gdbstub.c +++ b/target/riscv/gdbstub.c @@ -342,7 +342,7 @@ void riscv_cpu_register_gdb_regs_for_features(CPUState *cs) g_assert_not_reached(); } - if (cpu->cfg.ext_icsr) { + if (cpu->cfg.ext_zicsr) { int base_reg = cs->gdb_num_regs; gdb_register_coprocessor(cs, riscv_gdb_get_csr, riscv_gdb_set_csr, riscv_gen_dynamic_csr_xml(cs, base_reg), diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 9b8f3f54a7..418b040d6d 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -278,10 +278,10 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) !(riscv_has_ext(env, RVI) && riscv_has_ext(env, RVM) && riscv_has_ext(env, RVA) && riscv_has_ext(env, RVF) && riscv_has_ext(env, RVD) && - cpu->cfg.ext_icsr && cpu->cfg.ext_zifencei)) { + cpu->cfg.ext_zicsr && cpu->cfg.ext_zifencei)) { - if (cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_icsr)) && - !cpu->cfg.ext_icsr) { + if (cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_zicsr)) && + !cpu->cfg.ext_zicsr) { error_setg(errp, "RVG requires Zicsr but user set Zicsr to false"); return; } @@ -293,7 +293,7 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) return; } - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_icsr), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zicsr), true); cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zifencei), true); env->misa_ext |= RVI | RVM | RVA | RVF | RVD; @@ -329,7 +329,7 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) return; } - if (riscv_has_ext(env, RVF) && !cpu->cfg.ext_icsr) { + if (riscv_has_ext(env, RVF) && !cpu->cfg.ext_zicsr) { error_setg(errp, "F extension requires Zicsr"); return; } @@ -434,7 +434,7 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) } if (cpu->cfg.ext_zfinx) { - if (!cpu->cfg.ext_icsr) { + if (!cpu->cfg.ext_zicsr) { error_setg(errp, "Zfinx extension requires Zicsr"); return; } @@ -494,7 +494,7 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) return; } - if (cpu->cfg.ext_zcmt && !cpu->cfg.ext_icsr) { + if (cpu->cfg.ext_zcmt && !cpu->cfg.ext_zicsr) { error_setg(errp, "Zcmt extension requires Zicsr extension"); return; } From a326a2b0b2afae9285126cb1c56e71926d3702c7 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 12 Oct 2023 13:46:03 -0300 Subject: [PATCH 525/974] target/riscv: rename ext_icbom to ext_zicbom Add a leading 'z' to improve grepping. When one wants to search for uses of zicbom they're more likely to do 'grep -i zicbom' than 'grep -i icbom'. Suggested-by: Andrew Jones Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Reviewed-by: Andrew Jones Message-ID: <20231012164604.398496-4-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 2 +- target/riscv/cpu.c | 6 +++--- target/riscv/cpu_cfg.h | 2 +- target/riscv/insn_trans/trans_rvzicbo.c.inc | 8 ++++---- target/riscv/kvm/kvm-cpu.c | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 9de578c756..54e0fe8ecc 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -263,7 +263,7 @@ static void create_fdt_socket_cpus(RISCVVirtState *s, int socket, qemu_fdt_setprop_string(ms->fdt, cpu_name, "riscv,isa", name); g_free(name); - if (cpu_ptr->cfg.ext_icbom) { + if (cpu_ptr->cfg.ext_zicbom) { qemu_fdt_setprop_cell(ms->fdt, cpu_name, "riscv,cbom-block-size", cpu_ptr->cfg.cbom_blocksize); } diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 566b7545e8..943d5ecbfb 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -76,7 +76,7 @@ const uint32_t misa_bits[] = {RVI, RVE, RVM, RVA, RVF, RVD, RVV, * instead. */ const RISCVIsaExtData isa_edata_arr[] = { - ISA_EXT_DATA_ENTRY(zicbom, PRIV_VERSION_1_12_0, ext_icbom), + ISA_EXT_DATA_ENTRY(zicbom, PRIV_VERSION_1_12_0, ext_zicbom), ISA_EXT_DATA_ENTRY(zicboz, PRIV_VERSION_1_12_0, ext_icboz), ISA_EXT_DATA_ENTRY(zicond, PRIV_VERSION_1_12_0, ext_zicond), ISA_EXT_DATA_ENTRY(zicsr, PRIV_VERSION_1_10_0, ext_zicsr), @@ -497,7 +497,7 @@ static void rv64_veyron_v1_cpu_init(Object *obj) cpu->cfg.ext_zifencei = true; cpu->cfg.ext_zicsr = true; cpu->cfg.pmp = true; - cpu->cfg.ext_icbom = true; + cpu->cfg.ext_zicbom = true; cpu->cfg.cbom_blocksize = 64; cpu->cfg.cboz_blocksize = 64; cpu->cfg.ext_icboz = true; @@ -1284,7 +1284,7 @@ const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = { MULTI_EXT_CFG_BOOL("zhinx", ext_zhinx, false), MULTI_EXT_CFG_BOOL("zhinxmin", ext_zhinxmin, false), - MULTI_EXT_CFG_BOOL("zicbom", ext_icbom, true), + MULTI_EXT_CFG_BOOL("zicbom", ext_zicbom, true), MULTI_EXT_CFG_BOOL("zicboz", ext_icboz, true), MULTI_EXT_CFG_BOOL("zmmul", ext_zmmul, false), diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index 9ea30da7e0..e6bef0070f 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -63,7 +63,7 @@ struct RISCVCPUConfig { bool ext_zkt; bool ext_zifencei; bool ext_zicsr; - bool ext_icbom; + bool ext_zicbom; bool ext_icboz; bool ext_zicond; bool ext_zihintntl; diff --git a/target/riscv/insn_trans/trans_rvzicbo.c.inc b/target/riscv/insn_trans/trans_rvzicbo.c.inc index e5a7704f54..e6ed548376 100644 --- a/target/riscv/insn_trans/trans_rvzicbo.c.inc +++ b/target/riscv/insn_trans/trans_rvzicbo.c.inc @@ -16,10 +16,10 @@ * this program. If not, see . */ -#define REQUIRE_ZICBOM(ctx) do { \ - if (!ctx->cfg_ptr->ext_icbom) { \ - return false; \ - } \ +#define REQUIRE_ZICBOM(ctx) do { \ + if (!ctx->cfg_ptr->ext_zicbom) { \ + return false; \ + } \ } while (0) #define REQUIRE_ZICBOZ(ctx) do { \ diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 26e68c7ab4..4d4c17fd77 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -213,7 +213,7 @@ static void kvm_riscv_update_cpu_misa_ext(RISCVCPU *cpu, CPUState *cs) .kvm_reg_id = _reg_id} static KVMCPUConfig kvm_multi_ext_cfgs[] = { - KVM_EXT_CFG("zicbom", ext_icbom, KVM_RISCV_ISA_EXT_ZICBOM), + KVM_EXT_CFG("zicbom", ext_zicbom, KVM_RISCV_ISA_EXT_ZICBOM), KVM_EXT_CFG("zicboz", ext_icboz, KVM_RISCV_ISA_EXT_ZICBOZ), KVM_EXT_CFG("zihintpause", ext_zihintpause, KVM_RISCV_ISA_EXT_ZIHINTPAUSE), KVM_EXT_CFG("zbb", ext_zbb, KVM_RISCV_ISA_EXT_ZBB), @@ -804,7 +804,7 @@ static void kvm_riscv_read_multiext_legacy(RISCVCPU *cpu, kvm_cpu_cfg_set(cpu, multi_ext_cfg, val); } - if (cpu->cfg.ext_icbom) { + if (cpu->cfg.ext_zicbom) { kvm_riscv_read_cbomz_blksize(cpu, kvmcpu, &kvm_cbom_blocksize); } @@ -897,7 +897,7 @@ static void kvm_riscv_init_multiext_cfg(RISCVCPU *cpu, KVMScratchCPU *kvmcpu) kvm_cpu_cfg_set(cpu, multi_ext_cfg, val); } - if (cpu->cfg.ext_icbom) { + if (cpu->cfg.ext_zicbom) { kvm_riscv_read_cbomz_blksize(cpu, kvmcpu, &kvm_cbom_blocksize); } From e57039ddab55bb0f711b3bf46d39f887663243a0 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 12 Oct 2023 13:46:04 -0300 Subject: [PATCH 526/974] target/riscv: rename ext_icboz to ext_zicboz Add a leading 'z' to improve grepping. When one wants to search for uses of zicboz they're more likely to do 'grep -i zicboz' than 'grep -i icboz'. Suggested-by: Andrew Jones Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Reviewed-by: Andrew Jones Message-ID: <20231012164604.398496-5-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 2 +- target/riscv/cpu.c | 6 +++--- target/riscv/cpu_cfg.h | 2 +- target/riscv/insn_trans/trans_rvzicbo.c.inc | 8 ++++---- target/riscv/kvm/kvm-cpu.c | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 54e0fe8ecc..1732c42915 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -268,7 +268,7 @@ static void create_fdt_socket_cpus(RISCVVirtState *s, int socket, cpu_ptr->cfg.cbom_blocksize); } - if (cpu_ptr->cfg.ext_icboz) { + if (cpu_ptr->cfg.ext_zicboz) { qemu_fdt_setprop_cell(ms->fdt, cpu_name, "riscv,cboz-block-size", cpu_ptr->cfg.cboz_blocksize); } diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 943d5ecbfb..efafc0ba0b 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -77,7 +77,7 @@ const uint32_t misa_bits[] = {RVI, RVE, RVM, RVA, RVF, RVD, RVV, */ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zicbom, PRIV_VERSION_1_12_0, ext_zicbom), - ISA_EXT_DATA_ENTRY(zicboz, PRIV_VERSION_1_12_0, ext_icboz), + ISA_EXT_DATA_ENTRY(zicboz, PRIV_VERSION_1_12_0, ext_zicboz), ISA_EXT_DATA_ENTRY(zicond, PRIV_VERSION_1_12_0, ext_zicond), ISA_EXT_DATA_ENTRY(zicsr, PRIV_VERSION_1_10_0, ext_zicsr), ISA_EXT_DATA_ENTRY(zifencei, PRIV_VERSION_1_10_0, ext_zifencei), @@ -500,7 +500,7 @@ static void rv64_veyron_v1_cpu_init(Object *obj) cpu->cfg.ext_zicbom = true; cpu->cfg.cbom_blocksize = 64; cpu->cfg.cboz_blocksize = 64; - cpu->cfg.ext_icboz = true; + cpu->cfg.ext_zicboz = true; cpu->cfg.ext_smaia = true; cpu->cfg.ext_ssaia = true; cpu->cfg.ext_sscofpmf = true; @@ -1285,7 +1285,7 @@ const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = { MULTI_EXT_CFG_BOOL("zhinxmin", ext_zhinxmin, false), MULTI_EXT_CFG_BOOL("zicbom", ext_zicbom, true), - MULTI_EXT_CFG_BOOL("zicboz", ext_icboz, true), + MULTI_EXT_CFG_BOOL("zicboz", ext_zicboz, true), MULTI_EXT_CFG_BOOL("zmmul", ext_zmmul, false), diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index e6bef0070f..208cac1c7c 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -64,7 +64,7 @@ struct RISCVCPUConfig { bool ext_zifencei; bool ext_zicsr; bool ext_zicbom; - bool ext_icboz; + bool ext_zicboz; bool ext_zicond; bool ext_zihintntl; bool ext_zihintpause; diff --git a/target/riscv/insn_trans/trans_rvzicbo.c.inc b/target/riscv/insn_trans/trans_rvzicbo.c.inc index e6ed548376..d5d7095903 100644 --- a/target/riscv/insn_trans/trans_rvzicbo.c.inc +++ b/target/riscv/insn_trans/trans_rvzicbo.c.inc @@ -22,10 +22,10 @@ } \ } while (0) -#define REQUIRE_ZICBOZ(ctx) do { \ - if (!ctx->cfg_ptr->ext_icboz) { \ - return false; \ - } \ +#define REQUIRE_ZICBOZ(ctx) do { \ + if (!ctx->cfg_ptr->ext_zicboz) { \ + return false; \ + } \ } while (0) static bool trans_cbo_clean(DisasContext *ctx, arg_cbo_clean *a) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 4d4c17fd77..6e1678542b 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -214,7 +214,7 @@ static void kvm_riscv_update_cpu_misa_ext(RISCVCPU *cpu, CPUState *cs) static KVMCPUConfig kvm_multi_ext_cfgs[] = { KVM_EXT_CFG("zicbom", ext_zicbom, KVM_RISCV_ISA_EXT_ZICBOM), - KVM_EXT_CFG("zicboz", ext_icboz, KVM_RISCV_ISA_EXT_ZICBOZ), + KVM_EXT_CFG("zicboz", ext_zicboz, KVM_RISCV_ISA_EXT_ZICBOZ), KVM_EXT_CFG("zihintpause", ext_zihintpause, KVM_RISCV_ISA_EXT_ZIHINTPAUSE), KVM_EXT_CFG("zbb", ext_zbb, KVM_RISCV_ISA_EXT_ZBB), KVM_EXT_CFG("ssaia", ext_ssaia, KVM_RISCV_ISA_EXT_SSAIA), @@ -808,7 +808,7 @@ static void kvm_riscv_read_multiext_legacy(RISCVCPU *cpu, kvm_riscv_read_cbomz_blksize(cpu, kvmcpu, &kvm_cbom_blocksize); } - if (cpu->cfg.ext_icboz) { + if (cpu->cfg.ext_zicboz) { kvm_riscv_read_cbomz_blksize(cpu, kvmcpu, &kvm_cboz_blocksize); } } @@ -901,7 +901,7 @@ static void kvm_riscv_init_multiext_cfg(RISCVCPU *cpu, KVMScratchCPU *kvmcpu) kvm_riscv_read_cbomz_blksize(cpu, kvmcpu, &kvm_cbom_blocksize); } - if (cpu->cfg.ext_icboz) { + if (cpu->cfg.ext_zicboz) { kvm_riscv_read_cbomz_blksize(cpu, kvmcpu, &kvm_cboz_blocksize); } } From a7b69170254b15b5a40b318ed5559084ccfc466b Mon Sep 17 00:00:00 2001 From: Rajnesh Kanwal Date: Mon, 16 Oct 2023 12:17:31 +0100 Subject: [PATCH 527/974] target/riscv: Without H-mode mask all HS mode inturrupts in mie. Signed-off-by: Rajnesh Kanwal Reviewed-by: Alistair Francis Message-ID: <20231016111736.28721-2-rkanwal@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 30cc21e979..4847b47a98 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -1525,7 +1525,7 @@ static RISCVException rmw_mie64(CPURISCVState *env, int csrno, env->mie = (env->mie & ~mask) | (new_val & mask); if (!riscv_has_ext(env, RVH)) { - env->mie &= ~((uint64_t)MIP_SGEIP); + env->mie &= ~((uint64_t)HS_MODE_INTERRUPTS); } return RISCV_EXCP_NONE; From d17bcae5f7e9f949052a1f126a7f23e7279b6d96 Mon Sep 17 00:00:00 2001 From: Rajnesh Kanwal Date: Mon, 16 Oct 2023 12:17:32 +0100 Subject: [PATCH 528/974] target/riscv: Check for async flag in case of RISCV_EXCP_SEMIHOST. RISCV_EXCP_SEMIHOST is set to 0x10, which can be a local interrupt id as well. This change moves RISCV_EXCP_SEMIHOST to switch case so that async flag check is performed before invoking semihosting logic. Signed-off-by: Rajnesh Kanwal Reviewed-by: Alistair Francis Message-ID: <20231016111736.28721-3-rkanwal@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/cpu_helper.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 8c28241c18..aaeb1d0d5c 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -1605,15 +1605,13 @@ void riscv_cpu_do_interrupt(CPUState *cs) target_ulong htval = 0; target_ulong mtval2 = 0; - if (cause == RISCV_EXCP_SEMIHOST) { - do_common_semihosting(cs); - env->pc += 4; - return; - } - if (!async) { /* set tval to badaddr for traps with address information */ switch (cause) { + case RISCV_EXCP_SEMIHOST: + do_common_semihosting(cs); + env->pc += 4; + return; case RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT: case RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT: case RISCV_EXCP_LOAD_ADDR_MIS: From b901c7eb701a8f4d512be3a70958150fc5d0cd90 Mon Sep 17 00:00:00 2001 From: Rajnesh Kanwal Date: Mon, 16 Oct 2023 12:17:33 +0100 Subject: [PATCH 529/974] target/riscv: Set VS* bits to one in mideleg when H-Ext is enabled With H-Ext supported, VS bits are all hardwired to one in MIDELEG denoting always delegated interrupts. This is being done in rmw_mideleg but given mideleg is used in other places when routing interrupts this change initializes it in riscv_cpu_realize to be on the safe side. Signed-off-by: Rajnesh Kanwal Reviewed-by: Alistair Francis Message-ID: <20231016111736.28721-4-rkanwal@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/tcg/tcg-cpu.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 418b040d6d..bbce254ee1 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -618,7 +618,12 @@ static bool tcg_cpu_realize(CPUState *cs, Error **errp) cpu->pmu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, riscv_pmu_timer_cb, cpu); } - } + } + + /* With H-Ext, VSSIP, VSTIP, VSEIP and SGEIP are hardwired to one. */ + if (riscv_has_ext(env, RVH)) { + env->mideleg = MIP_VSSIP | MIP_VSTIP | MIP_VSEIP | MIP_SGEIP; + } #endif return true; From 1ebad505f3d5108513bf150b901344caceb3a7c1 Mon Sep 17 00:00:00 2001 From: Rajnesh Kanwal Date: Mon, 16 Oct 2023 12:17:34 +0100 Subject: [PATCH 530/974] target/riscv: Split interrupt logic from riscv_cpu_update_mip. This is to allow virtual interrupts to be inserted into S and VS modes. Given virtual interrupts will be maintained in separate mvip and hvip CSRs, riscv_cpu_update_mip will no longer be in the path and interrupts need to be triggered for these cases from rmw_hvip64 and rmw_mvip64 functions. Signed-off-by: Rajnesh Kanwal Reviewed-by: Alistair Francis Message-ID: <20231016111736.28721-5-rkanwal@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 1 + target/riscv/cpu_helper.c | 25 ++++++++++++++++++------- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index f8ffa5ee38..6fe32e6b38 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -463,6 +463,7 @@ void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env); int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts); uint64_t riscv_cpu_update_mip(CPURISCVState *env, uint64_t mask, uint64_t value); +void riscv_cpu_interrupt(CPURISCVState *env); #define BOOL_TO_MASK(x) (-!!(x)) /* helper for riscv_cpu_update_mip value */ void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(void *), void *arg); diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index aaeb1d0d5c..581b8c6380 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -620,11 +620,12 @@ int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts) } } -uint64_t riscv_cpu_update_mip(CPURISCVState *env, uint64_t mask, - uint64_t value) +void riscv_cpu_interrupt(CPURISCVState *env) { + uint64_t gein, vsgein = 0, vstip = 0; CPUState *cs = env_cpu(env); - uint64_t gein, vsgein = 0, vstip = 0, old = env->mip; + + QEMU_IOTHREAD_LOCK_GUARD(); if (env->virt_enabled) { gein = get_field(env->hstatus, HSTATUS_VGEIN); @@ -633,15 +634,25 @@ uint64_t riscv_cpu_update_mip(CPURISCVState *env, uint64_t mask, vstip = env->vstime_irq ? MIP_VSTIP : 0; - QEMU_IOTHREAD_LOCK_GUARD(); - - env->mip = (env->mip & ~mask) | (value & mask); - if (env->mip | vsgein | vstip) { cpu_interrupt(cs, CPU_INTERRUPT_HARD); } else { cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); } +} + +uint64_t riscv_cpu_update_mip(CPURISCVState *env, uint64_t mask, uint64_t value) +{ + uint64_t old = env->mip; + + /* No need to update mip for VSTIP */ + mask = ((mask == MIP_VSTIP) && env->vstime_irq) ? 0 : mask; + + QEMU_IOTHREAD_LOCK_GUARD(); + + env->mip = (env->mip & ~mask) | (value & mask); + + riscv_cpu_interrupt(env); return old; } From 1697837ed98cf56a6f65edd06128151f83b99403 Mon Sep 17 00:00:00 2001 From: Rajnesh Kanwal Date: Mon, 16 Oct 2023 12:17:35 +0100 Subject: [PATCH 531/974] target/riscv: Add M-mode virtual interrupt and IRQ filtering support. This change adds support for inserting virtual interrupts from M-mode into S-mode using mvien and mvip csrs. IRQ filtering is a use case of this change, i-e M-mode can stop delegating an interrupt to S-mode and instead enable it in MIE and receive those interrupts in M-mode and then selectively inject the interrupt using mvien and mvip. Also, the spec doesn't mandate the interrupt to be actually supported in hardware. Which allows M-mode to assert virtual interrupts to S-mode that have no connection to any real interrupt events. This is defined as part of the AIA specification [0], "5.3 Interrupt filtering and virtual interrupts for supervisor level". [0]: https://github.com/riscv/riscv-aia/releases/download/1.0/riscv-interrupts-1.0.pdf Signed-off-by: Rajnesh Kanwal Reviewed-by: Daniel Henrique Barboza Message-ID: <20231016111736.28721-6-rkanwal@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 3 +- target/riscv/cpu.h | 8 ++ target/riscv/cpu_bits.h | 6 + target/riscv/cpu_helper.c | 26 +++- target/riscv/csr.c | 279 ++++++++++++++++++++++++++++++++++---- target/riscv/machine.c | 7 +- 6 files changed, 291 insertions(+), 38 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index efafc0ba0b..859ac59c6c 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -813,7 +813,8 @@ static bool riscv_cpu_has_work(CPUState *cs) * Definition of the WFI instruction requires it to ignore the privilege * mode and delegation registers, but respect individual enables */ - return riscv_cpu_all_pending(env) != 0; + return riscv_cpu_all_pending(env) != 0 || + riscv_cpu_sirq_pending(env) != RISCV_EXCP_NONE; #else return true; #endif diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 6fe32e6b38..30f9481f45 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -202,6 +202,12 @@ struct CPUArchState { uint64_t mie; uint64_t mideleg; + /* + * When mideleg[i]=0 and mvien[i]=1, sie[i] is no more + * alias of mie[i] and needs to be maintained separatly. + */ + uint64_t sie; + target_ulong satp; /* since: priv-1.10.0 */ target_ulong stval; target_ulong medeleg; @@ -222,6 +228,8 @@ struct CPUArchState { /* AIA CSRs */ target_ulong miselect; target_ulong siselect; + uint64_t mvien; + uint64_t mvip; /* Hypervisor CSRs */ target_ulong hstatus; diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 3d6ffaabc7..ebd7917d49 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -735,6 +735,12 @@ typedef enum RISCVException { #define MIE_SSIE (1 << IRQ_S_SOFT) #define MIE_USIE (1 << IRQ_U_SOFT) +/* Machine constants */ +#define M_MODE_INTERRUPTS ((uint64_t)(MIP_MSIP | MIP_MTIP | MIP_MEIP)) +#define S_MODE_INTERRUPTS ((uint64_t)(MIP_SSIP | MIP_STIP | MIP_SEIP)) +#define VS_MODE_INTERRUPTS ((uint64_t)(MIP_VSSIP | MIP_VSTIP | MIP_VSEIP)) +#define HS_MODE_INTERRUPTS ((uint64_t)(MIP_SGEIP | VS_MODE_INTERRUPTS)) + /* General PointerMasking CSR bits */ #define PM_ENABLE 0x00000001ULL #define PM_CURRENT 0x00000002ULL diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 581b8c6380..b36161708a 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -376,6 +376,10 @@ static int riscv_cpu_pending_to_irq(CPURISCVState *env, return best_irq; } +/* + * Doesn't report interrupts inserted using mvip from M-mode firmware. Those + * are returned in riscv_cpu_sirq_pending(). + */ uint64_t riscv_cpu_all_pending(CPURISCVState *env) { uint32_t gein = get_field(env->hstatus, HSTATUS_VGEIN); @@ -398,9 +402,10 @@ int riscv_cpu_sirq_pending(CPURISCVState *env) { uint64_t irqs = riscv_cpu_all_pending(env) & env->mideleg & ~(MIP_VSSIP | MIP_VSTIP | MIP_VSEIP); + uint64_t irqs_f = env->mvip & env->mvien & ~env->mideleg & env->sie; return riscv_cpu_pending_to_irq(env, IRQ_S_EXT, IPRIO_DEFAULT_S, - irqs, env->siprio); + irqs | irqs_f, env->siprio); } int riscv_cpu_vsirq_pending(CPURISCVState *env) @@ -414,8 +419,8 @@ int riscv_cpu_vsirq_pending(CPURISCVState *env) static int riscv_cpu_local_irq_pending(CPURISCVState *env) { + uint64_t irqs, pending, mie, hsie, vsie, irqs_f; int virq; - uint64_t irqs, pending, mie, hsie, vsie; /* Determine interrupt enable state of all privilege modes */ if (env->virt_enabled) { @@ -441,8 +446,11 @@ static int riscv_cpu_local_irq_pending(CPURISCVState *env) irqs, env->miprio); } + /* Check for virtual S-mode interrupts. */ + irqs_f = env->mvip & (env->mvien & ~env->mideleg) & env->sie; + /* Check HS-mode interrupts */ - irqs = pending & env->mideleg & ~env->hideleg & -hsie; + irqs = ((pending & env->mideleg & ~env->hideleg) | irqs_f) & -hsie; if (irqs) { return riscv_cpu_pending_to_irq(env, IRQ_S_EXT, IPRIO_DEFAULT_S, irqs, env->siprio); @@ -622,7 +630,7 @@ int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts) void riscv_cpu_interrupt(CPURISCVState *env) { - uint64_t gein, vsgein = 0, vstip = 0; + uint64_t gein, vsgein = 0, vstip = 0, irqf = 0; CPUState *cs = env_cpu(env); QEMU_IOTHREAD_LOCK_GUARD(); @@ -630,11 +638,13 @@ void riscv_cpu_interrupt(CPURISCVState *env) if (env->virt_enabled) { gein = get_field(env->hstatus, HSTATUS_VGEIN); vsgein = (env->hgeip & (1ULL << gein)) ? MIP_VSEIP : 0; + } else { + irqf = env->mvien & env->mvip & env->sie; } vstip = env->vstime_irq ? MIP_VSTIP : 0; - if (env->mip | vsgein | vstip) { + if (env->mip | vsgein | vstip | irqf) { cpu_interrupt(cs, CPU_INTERRUPT_HARD); } else { cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); @@ -1611,6 +1621,8 @@ void riscv_cpu_do_interrupt(CPUState *cs) bool async = !!(cs->exception_index & RISCV_EXCP_INT_FLAG); target_ulong cause = cs->exception_index & RISCV_EXCP_INT_MASK; uint64_t deleg = async ? env->mideleg : env->medeleg; + bool s_injected = env->mvip & (1 << cause) & env->mvien && + !(env->mip & (1 << cause)); target_ulong tval = 0; target_ulong tinst = 0; target_ulong htval = 0; @@ -1699,8 +1711,8 @@ void riscv_cpu_do_interrupt(CPUState *cs) __func__, env->mhartid, async, cause, env->pc, tval, riscv_cpu_get_trap_name(cause, async)); - if (env->priv <= PRV_S && - cause < TARGET_LONG_BITS && ((deleg >> cause) & 1)) { + if (env->priv <= PRV_S && cause < 64 && + (((deleg >> cause) & 1) || s_injected)) { /* handle the trap in S-mode */ if (riscv_has_ext(env, RVH)) { uint64_t hdeleg = async ? env->hideleg : env->hedeleg; diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 4847b47a98..645f30f028 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -1117,21 +1117,16 @@ static RISCVException write_stimecmph(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } -/* Machine constants */ - -#define M_MODE_INTERRUPTS ((uint64_t)(MIP_MSIP | MIP_MTIP | MIP_MEIP)) -#define S_MODE_INTERRUPTS ((uint64_t)(MIP_SSIP | MIP_STIP | MIP_SEIP | \ - MIP_LCOFIP)) -#define VS_MODE_INTERRUPTS ((uint64_t)(MIP_VSSIP | MIP_VSTIP | MIP_VSEIP)) -#define HS_MODE_INTERRUPTS ((uint64_t)(MIP_SGEIP | VS_MODE_INTERRUPTS)) - #define VSTOPI_NUM_SRCS 5 -static const uint64_t delegable_ints = S_MODE_INTERRUPTS | - VS_MODE_INTERRUPTS; -static const uint64_t vs_delegable_ints = VS_MODE_INTERRUPTS; +#define LOCAL_INTERRUPTS (~0x1FFF) + +static const uint64_t delegable_ints = + S_MODE_INTERRUPTS | VS_MODE_INTERRUPTS | MIP_LCOFIP; +static const uint64_t vs_delegable_ints = + (VS_MODE_INTERRUPTS | LOCAL_INTERRUPTS) & ~MIP_LCOFIP; static const uint64_t all_ints = M_MODE_INTERRUPTS | S_MODE_INTERRUPTS | - HS_MODE_INTERRUPTS; + HS_MODE_INTERRUPTS | LOCAL_INTERRUPTS; #define DELEGABLE_EXCPS ((1ULL << (RISCV_EXCP_INST_ADDR_MIS)) | \ (1ULL << (RISCV_EXCP_INST_ACCESS_FAULT)) | \ (1ULL << (RISCV_EXCP_ILLEGAL_INST)) | \ @@ -1162,12 +1157,30 @@ static const target_ulong vs_delegable_excps = DELEGABLE_EXCPS & static const target_ulong sstatus_v1_10_mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_UIE | SSTATUS_UPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_XS | SSTATUS_SUM | SSTATUS_MXR | SSTATUS_VS; -static const target_ulong sip_writable_mask = SIP_SSIP | MIP_USIP | MIP_UEIP | - SIP_LCOFIP; + +/* + * Spec allows for bits 13:63 to be either read-only or writable. + * So far we have interrupt LCOFIP in that region which is writable. + * + * Also, spec allows to inject virtual interrupts in this region even + * without any hardware interrupts for that interrupt number. + * + * For now interrupt in 13:63 region are all kept writable. 13 being + * LCOFIP and 14:63 being virtual only. Change this in future if we + * introduce more interrupts that are not writable. + */ + +/* Bit STIP can be an alias of mip.STIP that's why it's writable in mvip. */ +static const target_ulong mvip_writable_mask = MIP_SSIP | MIP_STIP | MIP_SEIP | + LOCAL_INTERRUPTS; +static const target_ulong mvien_writable_mask = MIP_SSIP | MIP_SEIP | + LOCAL_INTERRUPTS; + +static const target_ulong sip_writable_mask = SIP_SSIP | LOCAL_INTERRUPTS; static const target_ulong hip_writable_mask = MIP_VSSIP; static const target_ulong hvip_writable_mask = MIP_VSSIP | MIP_VSTIP | - MIP_VSEIP; -static const target_ulong vsip_writable_mask = MIP_VSSIP; + MIP_VSEIP | LOCAL_INTERRUPTS; +static const target_ulong vsip_writable_mask = MIP_VSSIP | LOCAL_INTERRUPTS; const bool valid_vm_1_10_32[16] = { [VM_1_10_MBARE] = true, @@ -1562,6 +1575,52 @@ static RISCVException rmw_mieh(CPURISCVState *env, int csrno, return ret; } +static RISCVException rmw_mvien64(CPURISCVState *env, int csrno, + uint64_t *ret_val, + uint64_t new_val, uint64_t wr_mask) +{ + uint64_t mask = wr_mask & mvien_writable_mask; + + if (ret_val) { + *ret_val = env->mvien; + } + + env->mvien = (env->mvien & ~mask) | (new_val & mask); + + return RISCV_EXCP_NONE; +} + +static RISCVException rmw_mvien(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) +{ + uint64_t rval; + RISCVException ret; + + ret = rmw_mvien64(env, csrno, &rval, new_val, wr_mask); + if (ret_val) { + *ret_val = rval; + } + + return ret; +} + +static RISCVException rmw_mvienh(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) +{ + uint64_t rval; + RISCVException ret; + + ret = rmw_mvien64(env, csrno, &rval, + ((uint64_t)new_val) << 32, ((uint64_t)wr_mask) << 32); + if (ret_val) { + *ret_val = rval >> 32; + } + + return ret; +} + static int read_mtopi(CPURISCVState *env, int csrno, target_ulong *val) { int irq; @@ -1703,6 +1762,11 @@ static int rmw_xireg(CPURISCVState *env, int csrno, target_ulong *val, priv = PRV_M; break; case CSR_SIREG: + if (env->priv == PRV_S && env->mvien & MIP_SEIP && + env->siselect >= ISELECT_IMSIC_EIDELIVERY && + env->siselect <= ISELECT_IMSIC_EIE63) { + goto done; + } iprio = env->siprio; isel = env->siselect; priv = PRV_S; @@ -1769,6 +1833,9 @@ static int rmw_xtopei(CPURISCVState *env, int csrno, target_ulong *val, priv = PRV_M; break; case CSR_STOPEI: + if (env->mvien & MIP_SEIP && env->priv == PRV_S) { + goto done; + } priv = PRV_S; break; case CSR_VSTOPEI: @@ -2360,6 +2427,143 @@ static RISCVException rmw_miph(CPURISCVState *env, int csrno, return ret; } +/* + * The function is written for two use-cases: + * 1- To access mvip csr as is for m-mode access. + * 2- To access sip as a combination of mip and mvip for s-mode. + * + * Both report bits 1, 5, 9 and 13:63 but with the exception of + * STIP being read-only zero in case of mvip when sstc extension + * is present. + * Also, sip needs to be read-only zero when both mideleg[i] and + * mvien[i] are zero but mvip needs to be an alias of mip. + */ +static RISCVException rmw_mvip64(CPURISCVState *env, int csrno, + uint64_t *ret_val, + uint64_t new_val, uint64_t wr_mask) +{ + RISCVCPU *cpu = env_archcpu(env); + target_ulong ret_mip = 0; + RISCVException ret; + uint64_t old_mvip; + + /* + * mideleg[i] mvien[i] + * 0 0 No delegation. mvip[i] is alias of mip[i]. + * 0 1 mvip[i] becomes source of interrupt, mip bypassed. + * 1 X mip[i] is source of interrupt and mvip[i] aliases + * mip[i]. + * + * So alias condition would be for bits: + * ((S_MODE_INTERRUPTS | LOCAL_INTERRUPTS) & (mideleg | ~mvien)) | + * (!sstc & MIP_STIP) + * + * Non-alias condition will be for bits: + * (S_MODE_INTERRUPTS | LOCAL_INTERRUPTS) & (~mideleg & mvien) + * + * alias_mask denotes the bits that come from mip nalias_mask denotes bits + * that come from hvip. + */ + uint64_t alias_mask = ((S_MODE_INTERRUPTS | LOCAL_INTERRUPTS) & + (env->mideleg | ~env->mvien)) | MIP_STIP; + uint64_t nalias_mask = (S_MODE_INTERRUPTS | LOCAL_INTERRUPTS) & + (~env->mideleg & env->mvien); + uint64_t wr_mask_mvip; + uint64_t wr_mask_mip; + + /* + * mideleg[i] mvien[i] + * 0 0 sip[i] read-only zero. + * 0 1 sip[i] alias of mvip[i]. + * 1 X sip[i] alias of mip[i]. + * + * Both alias and non-alias mask remain same for sip except for bits + * which are zero in both mideleg and mvien. + */ + if (csrno == CSR_SIP) { + /* Remove bits that are zero in both mideleg and mvien. */ + alias_mask &= (env->mideleg | env->mvien); + nalias_mask &= (env->mideleg | env->mvien); + } + + /* + * If sstc is present, mvip.STIP is not an alias of mip.STIP so clear + * that our in mip returned value. + */ + if (cpu->cfg.ext_sstc && (env->priv == PRV_M) && + get_field(env->menvcfg, MENVCFG_STCE)) { + alias_mask &= ~MIP_STIP; + } + + wr_mask_mip = wr_mask & alias_mask & mvip_writable_mask; + wr_mask_mvip = wr_mask & nalias_mask & mvip_writable_mask; + + /* + * For bits set in alias_mask, mvip needs to be alias of mip, so forward + * this to rmw_mip. + */ + ret = rmw_mip(env, CSR_MIP, &ret_mip, new_val, wr_mask_mip); + if (ret != RISCV_EXCP_NONE) { + return ret; + } + + old_mvip = env->mvip; + + /* + * Write to mvip. Update only non-alias bits. Alias bits were updated + * in mip in rmw_mip above. + */ + if (wr_mask_mvip) { + env->mvip = (env->mvip & ~wr_mask_mvip) | (new_val & wr_mask_mvip); + + /* + * Given mvip is separate source from mip, we need to trigger interrupt + * from here separately. Normally this happen from riscv_cpu_update_mip. + */ + riscv_cpu_interrupt(env); + } + + if (ret_val) { + ret_mip &= alias_mask; + old_mvip &= nalias_mask; + + *ret_val = old_mvip | ret_mip; + } + + return RISCV_EXCP_NONE; +} + +static RISCVException rmw_mvip(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) +{ + uint64_t rval; + RISCVException ret; + + ret = rmw_mvip64(env, csrno, &rval, new_val, wr_mask); + if (ret_val) { + *ret_val = rval; + } + + return ret; +} + +static RISCVException rmw_mviph(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) +{ + uint64_t rval; + RISCVException ret; + + ret = rmw_mvip64(env, csrno, &rval, + ((uint64_t)new_val) << 32, ((uint64_t)wr_mask) << 32); + if (ret_val) { + *ret_val = rval >> 32; + } + + return ret; +} + /* Supervisor Trap Setup */ static RISCVException read_sstatus_i128(CPURISCVState *env, int csrno, Int128 *val) @@ -2454,20 +2658,37 @@ static RISCVException rmw_sie64(CPURISCVState *env, int csrno, uint64_t *ret_val, uint64_t new_val, uint64_t wr_mask) { + uint64_t nalias_mask = (S_MODE_INTERRUPTS | LOCAL_INTERRUPTS) & + (~env->mideleg & env->mvien); + uint64_t alias_mask = (S_MODE_INTERRUPTS | LOCAL_INTERRUPTS) & env->mideleg; + uint64_t sie_mask = wr_mask & nalias_mask; RISCVException ret; - uint64_t mask = env->mideleg & S_MODE_INTERRUPTS; + /* + * mideleg[i] mvien[i] + * 0 0 sie[i] read-only zero. + * 0 1 sie[i] is a separate writable bit. + * 1 X sie[i] alias of mie[i]. + * + * Both alias and non-alias mask remain same for sip except for bits + * which are zero in both mideleg and mvien. + */ if (env->virt_enabled) { if (env->hvictl & HVICTL_VTI) { return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; } ret = rmw_vsie64(env, CSR_VSIE, ret_val, new_val, wr_mask); + if (ret_val) { + *ret_val &= alias_mask; + } } else { - ret = rmw_mie64(env, csrno, ret_val, new_val, wr_mask & mask); - } + ret = rmw_mie64(env, csrno, ret_val, new_val, wr_mask & alias_mask); + if (ret_val) { + *ret_val &= alias_mask; + *ret_val |= env->sie & nalias_mask; + } - if (ret_val) { - *ret_val &= mask; + env->sie = (env->sie & ~sie_mask) | (new_val & sie_mask); } return ret; @@ -2665,7 +2886,7 @@ static RISCVException rmw_sip64(CPURISCVState *env, int csrno, uint64_t new_val, uint64_t wr_mask) { RISCVException ret; - uint64_t mask = env->mideleg & sip_writable_mask; + uint64_t mask = (env->mideleg | env->mvien) & sip_writable_mask; if (env->virt_enabled) { if (env->hvictl & HVICTL_VTI) { @@ -2673,11 +2894,12 @@ static RISCVException rmw_sip64(CPURISCVState *env, int csrno, } ret = rmw_vsip64(env, CSR_VSIP, ret_val, new_val, wr_mask); } else { - ret = rmw_mip64(env, csrno, ret_val, new_val, wr_mask & mask); + ret = rmw_mvip64(env, csrno, ret_val, new_val, wr_mask & mask); } if (ret_val) { - *ret_val &= env->mideleg & S_MODE_INTERRUPTS; + *ret_val &= (env->mideleg | env->mvien) & + (S_MODE_INTERRUPTS | LOCAL_INTERRUPTS); } return ret; @@ -2842,6 +3064,7 @@ static int read_vstopi(CPURISCVState *env, int csrno, target_ulong *val) *val = (iid & TOPI_IID_MASK) << TOPI_IID_SHIFT; *val |= iprio; + return RISCV_EXCP_NONE; } @@ -4165,14 +4388,14 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MTOPI] = { "mtopi", aia_any, read_mtopi }, /* Virtual Interrupts for Supervisor Level (AIA) */ - [CSR_MVIEN] = { "mvien", aia_any, read_zero, write_ignore }, - [CSR_MVIP] = { "mvip", aia_any, read_zero, write_ignore }, + [CSR_MVIEN] = { "mvien", aia_any, NULL, NULL, rmw_mvien }, + [CSR_MVIP] = { "mvip", aia_any, NULL, NULL, rmw_mvip }, /* Machine-Level High-Half CSRs (AIA) */ [CSR_MIDELEGH] = { "midelegh", aia_any32, NULL, NULL, rmw_midelegh }, [CSR_MIEH] = { "mieh", aia_any32, NULL, NULL, rmw_mieh }, - [CSR_MVIENH] = { "mvienh", aia_any32, read_zero, write_ignore }, - [CSR_MVIPH] = { "mviph", aia_any32, read_zero, write_ignore }, + [CSR_MVIENH] = { "mvienh", aia_any32, NULL, NULL, rmw_mvienh }, + [CSR_MVIPH] = { "mviph", aia_any32, NULL, NULL, rmw_mviph }, [CSR_MIPH] = { "miph", aia_any32, NULL, NULL, rmw_miph }, /* Execution environment configuration */ diff --git a/target/riscv/machine.c b/target/riscv/machine.c index c7c862cdd3..f65a95f9e7 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -351,8 +351,8 @@ static const VMStateDescription vmstate_jvt = { const VMStateDescription vmstate_riscv_cpu = { .name = "cpu", - .version_id = 8, - .minimum_version_id = 8, + .version_id = 9, + .minimum_version_id = 9, .post_load = riscv_cpu_post_load, .fields = (VMStateField[]) { VMSTATE_UINTTL_ARRAY(env.gpr, RISCVCPU, 32), @@ -379,6 +379,9 @@ const VMStateDescription vmstate_riscv_cpu = { VMSTATE_UINT64(env.mip, RISCVCPU), VMSTATE_UINT64(env.miclaim, RISCVCPU), VMSTATE_UINT64(env.mie, RISCVCPU), + VMSTATE_UINT64(env.mvien, RISCVCPU), + VMSTATE_UINT64(env.mvip, RISCVCPU), + VMSTATE_UINT64(env.sie, RISCVCPU), VMSTATE_UINT64(env.mideleg, RISCVCPU), VMSTATE_UINTTL(env.satp, RISCVCPU), VMSTATE_UINTTL(env.stval, RISCVCPU), From 40336d5b1d4c6b8b8b38c77fda254457d44fe90b Mon Sep 17 00:00:00 2001 From: Rajnesh Kanwal Date: Mon, 16 Oct 2023 12:17:36 +0100 Subject: [PATCH 532/974] target/riscv: Add HS-mode virtual interrupt and IRQ filtering support. This change adds support for inserting virtual interrupts from HS-mode into VS-mode using hvien and hvip csrs. This also allows for IRQ filtering from HS-mode. Also, the spec doesn't mandate the interrupt to be actually supported in hardware. Which allows HS-mode to assert virtual interrupts to VS-mode that have no connection to any real interrupt events. This is defined as part of the AIA specification [0], "6.3.2 Virtual interrupts for VS level". [0]: https://github.com/riscv/riscv-aia/releases/download/1.0/riscv-interrupts-1.0.pdf Signed-off-by: Rajnesh Kanwal Reviewed-by: Daniel Henrique Barboza Message-ID: <20231016111736.28721-7-rkanwal@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 3 +- target/riscv/cpu.h | 14 +++ target/riscv/cpu_helper.c | 48 +++++++--- target/riscv/csr.c | 194 ++++++++++++++++++++++++++++++++++---- target/riscv/machine.c | 7 +- 5 files changed, 235 insertions(+), 31 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 859ac59c6c..2f98ce56e0 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -814,7 +814,8 @@ static bool riscv_cpu_has_work(CPUState *cs) * mode and delegation registers, but respect individual enables */ return riscv_cpu_all_pending(env) != 0 || - riscv_cpu_sirq_pending(env) != RISCV_EXCP_NONE; + riscv_cpu_sirq_pending(env) != RISCV_EXCP_NONE || + riscv_cpu_vsirq_pending(env) != RISCV_EXCP_NONE; #else return true; #endif diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 30f9481f45..7f61e17202 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -208,6 +208,12 @@ struct CPUArchState { */ uint64_t sie; + /* + * When hideleg[i]=0 and hvien[i]=1, vsie[i] is no more + * alias of sie[i] (mie[i]) and needs to be maintained separatly. + */ + uint64_t vsie; + target_ulong satp; /* since: priv-1.10.0 */ target_ulong stval; target_ulong medeleg; @@ -242,6 +248,14 @@ struct CPUArchState { target_ulong hgeie; target_ulong hgeip; uint64_t htimedelta; + uint64_t hvien; + + /* + * Bits VSSIP, VSTIP and VSEIP in hvip are maintained in mip. Other bits + * from 0:12 are reserved. Bits 13:63 are not aliased and must be separately + * maintain in hvip. + */ + uint64_t hvip; /* Hypervisor controlled virtual interrupt priorities */ target_ulong hvictl; diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index b36161708a..b7af69de53 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -377,8 +377,9 @@ static int riscv_cpu_pending_to_irq(CPURISCVState *env, } /* - * Doesn't report interrupts inserted using mvip from M-mode firmware. Those - * are returned in riscv_cpu_sirq_pending(). + * Doesn't report interrupts inserted using mvip from M-mode firmware or + * using hvip bits 13:63 from HS-mode. Those are returned in + * riscv_cpu_sirq_pending() and riscv_cpu_vsirq_pending(). */ uint64_t riscv_cpu_all_pending(CPURISCVState *env) { @@ -410,16 +411,23 @@ int riscv_cpu_sirq_pending(CPURISCVState *env) int riscv_cpu_vsirq_pending(CPURISCVState *env) { - uint64_t irqs = riscv_cpu_all_pending(env) & env->mideleg & - (MIP_VSSIP | MIP_VSTIP | MIP_VSEIP); + uint64_t irqs = riscv_cpu_all_pending(env) & env->mideleg & env->hideleg; + uint64_t irqs_f_vs = env->hvip & env->hvien & ~env->hideleg & env->vsie; + uint64_t vsbits; + + /* Bring VS-level bits to correct position */ + vsbits = irqs & VS_MODE_INTERRUPTS; + irqs &= ~VS_MODE_INTERRUPTS; + irqs |= vsbits >> 1; return riscv_cpu_pending_to_irq(env, IRQ_S_EXT, IPRIO_DEFAULT_S, - irqs >> 1, env->hviprio); + (irqs | irqs_f_vs), env->hviprio); } static int riscv_cpu_local_irq_pending(CPURISCVState *env) { - uint64_t irqs, pending, mie, hsie, vsie, irqs_f; + uint64_t irqs, pending, mie, hsie, vsie, irqs_f, irqs_f_vs; + uint64_t vsbits, irq_delegated; int virq; /* Determine interrupt enable state of all privilege modes */ @@ -456,12 +464,26 @@ static int riscv_cpu_local_irq_pending(CPURISCVState *env) irqs, env->siprio); } + /* Check for virtual VS-mode interrupts. */ + irqs_f_vs = env->hvip & env->hvien & ~env->hideleg & env->vsie; + /* Check VS-mode interrupts */ - irqs = pending & env->mideleg & env->hideleg & -vsie; + irq_delegated = pending & env->mideleg & env->hideleg; + + /* Bring VS-level bits to correct position */ + vsbits = irq_delegated & VS_MODE_INTERRUPTS; + irq_delegated &= ~VS_MODE_INTERRUPTS; + irq_delegated |= vsbits >> 1; + + irqs = (irq_delegated | irqs_f_vs) & -vsie; if (irqs) { virq = riscv_cpu_pending_to_irq(env, IRQ_S_EXT, IPRIO_DEFAULT_S, - irqs >> 1, env->hviprio); - return (virq <= 0) ? virq : virq + 1; + irqs, env->hviprio); + if (virq <= 0 || (virq > 12 && virq <= 63)) { + return virq; + } else { + return virq + 1; + } } /* Indicate no pending interrupt */ @@ -638,6 +660,7 @@ void riscv_cpu_interrupt(CPURISCVState *env) if (env->virt_enabled) { gein = get_field(env->hstatus, HSTATUS_VGEIN); vsgein = (env->hgeip & (1ULL << gein)) ? MIP_VSEIP : 0; + irqf = env->hvien & env->hvip & env->vsie; } else { irqf = env->mvien & env->mvip & env->sie; } @@ -1623,6 +1646,8 @@ void riscv_cpu_do_interrupt(CPUState *cs) uint64_t deleg = async ? env->mideleg : env->medeleg; bool s_injected = env->mvip & (1 << cause) & env->mvien && !(env->mip & (1 << cause)); + bool vs_injected = env->hvip & (1 << cause) & env->hvien && + !(env->mip & (1 << cause)); target_ulong tval = 0; target_ulong tinst = 0; target_ulong htval = 0; @@ -1712,12 +1737,13 @@ void riscv_cpu_do_interrupt(CPUState *cs) riscv_cpu_get_trap_name(cause, async)); if (env->priv <= PRV_S && cause < 64 && - (((deleg >> cause) & 1) || s_injected)) { + (((deleg >> cause) & 1) || s_injected || vs_injected)) { /* handle the trap in S-mode */ if (riscv_has_ext(env, RVH)) { uint64_t hdeleg = async ? env->hideleg : env->hedeleg; - if (env->virt_enabled && ((hdeleg >> cause) & 1)) { + if (env->virt_enabled && + (((hdeleg >> cause) & 1) || vs_injected)) { /* Trap to VS mode */ /* * See if we need to adjust cause. Yes if its VS mode interrupt diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 645f30f028..a5be1c202c 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -30,6 +30,7 @@ #include "qemu/guest-random.h" #include "qapi/error.h" + /* CSR function table public API */ void riscv_get_csr_ops(int csrno, riscv_csr_operations *ops) { @@ -1180,6 +1181,8 @@ static const target_ulong sip_writable_mask = SIP_SSIP | LOCAL_INTERRUPTS; static const target_ulong hip_writable_mask = MIP_VSSIP; static const target_ulong hvip_writable_mask = MIP_VSSIP | MIP_VSTIP | MIP_VSEIP | LOCAL_INTERRUPTS; +static const target_ulong hvien_writable_mask = LOCAL_INTERRUPTS; + static const target_ulong vsip_writable_mask = MIP_VSSIP | LOCAL_INTERRUPTS; const bool valid_vm_1_10_32[16] = { @@ -2608,16 +2611,36 @@ static RISCVException rmw_vsie64(CPURISCVState *env, int csrno, uint64_t *ret_val, uint64_t new_val, uint64_t wr_mask) { + uint64_t alias_mask = (LOCAL_INTERRUPTS | VS_MODE_INTERRUPTS) & + env->hideleg; + uint64_t nalias_mask = LOCAL_INTERRUPTS & (~env->hideleg & env->hvien); + uint64_t rval, rval_vs, vsbits; + uint64_t wr_mask_vsie; + uint64_t wr_mask_mie; RISCVException ret; - uint64_t rval, mask = env->hideleg & VS_MODE_INTERRUPTS; /* Bring VS-level bits to correct position */ - new_val = (new_val & (VS_MODE_INTERRUPTS >> 1)) << 1; - wr_mask = (wr_mask & (VS_MODE_INTERRUPTS >> 1)) << 1; + vsbits = new_val & (VS_MODE_INTERRUPTS >> 1); + new_val &= ~(VS_MODE_INTERRUPTS >> 1); + new_val |= vsbits << 1; + + vsbits = wr_mask & (VS_MODE_INTERRUPTS >> 1); + wr_mask &= ~(VS_MODE_INTERRUPTS >> 1); + wr_mask |= vsbits << 1; + + wr_mask_mie = wr_mask & alias_mask; + wr_mask_vsie = wr_mask & nalias_mask; + + ret = rmw_mie64(env, csrno, &rval, new_val, wr_mask_mie); + + rval_vs = env->vsie & nalias_mask; + env->vsie = (env->vsie & ~wr_mask_vsie) | (new_val & wr_mask_vsie); - ret = rmw_mie64(env, csrno, &rval, new_val, wr_mask & mask); if (ret_val) { - *ret_val = (rval & mask) >> 1; + rval &= alias_mask; + vsbits = rval & VS_MODE_INTERRUPTS; + rval &= ~VS_MODE_INTERRUPTS; + *ret_val = rval | (vsbits >> 1) | rval_vs; } return ret; @@ -2830,21 +2853,36 @@ static RISCVException write_stval(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +static RISCVException rmw_hvip64(CPURISCVState *env, int csrno, + uint64_t *ret_val, + uint64_t new_val, uint64_t wr_mask); + static RISCVException rmw_vsip64(CPURISCVState *env, int csrno, uint64_t *ret_val, uint64_t new_val, uint64_t wr_mask) { RISCVException ret; uint64_t rval, mask = env->hideleg & VS_MODE_INTERRUPTS; + uint64_t vsbits; + + /* Add virtualized bits into vsip mask. */ + mask |= env->hvien & ~env->hideleg; /* Bring VS-level bits to correct position */ - new_val = (new_val & (VS_MODE_INTERRUPTS >> 1)) << 1; - wr_mask = (wr_mask & (VS_MODE_INTERRUPTS >> 1)) << 1; + vsbits = new_val & (VS_MODE_INTERRUPTS >> 1); + new_val &= ~(VS_MODE_INTERRUPTS >> 1); + new_val |= vsbits << 1; + vsbits = wr_mask & (VS_MODE_INTERRUPTS >> 1); + wr_mask &= ~(VS_MODE_INTERRUPTS >> 1); + wr_mask |= vsbits << 1; - ret = rmw_mip64(env, csrno, &rval, new_val, - wr_mask & mask & vsip_writable_mask); + ret = rmw_hvip64(env, csrno, &rval, new_val, + wr_mask & mask & vsip_writable_mask); if (ret_val) { - *ret_val = (rval & mask) >> 1; + rval &= mask; + vsbits = rval & VS_MODE_INTERRUPTS; + rval &= ~VS_MODE_INTERRUPTS; + *ret_val = rval | (vsbits >> 1); } return ret; @@ -3136,6 +3174,52 @@ static RISCVException write_hedeleg(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +static RISCVException rmw_hvien64(CPURISCVState *env, int csrno, + uint64_t *ret_val, + uint64_t new_val, uint64_t wr_mask) +{ + uint64_t mask = wr_mask & hvien_writable_mask; + + if (ret_val) { + *ret_val = env->hvien; + } + + env->hvien = (env->hvien & ~mask) | (new_val & mask); + + return RISCV_EXCP_NONE; +} + +static RISCVException rmw_hvien(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) +{ + uint64_t rval; + RISCVException ret; + + ret = rmw_hvien64(env, csrno, &rval, new_val, wr_mask); + if (ret_val) { + *ret_val = rval; + } + + return ret; +} + +static RISCVException rmw_hvienh(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) +{ + uint64_t rval; + RISCVException ret; + + ret = rmw_hvien64(env, csrno, &rval, + ((uint64_t)new_val) << 32, ((uint64_t)wr_mask) << 32); + if (ret_val) { + *ret_val = rval >> 32; + } + + return ret; +} + static RISCVException rmw_hideleg64(CPURISCVState *env, int csrno, uint64_t *ret_val, uint64_t new_val, uint64_t wr_mask) @@ -3181,16 +3265,94 @@ static RISCVException rmw_hidelegh(CPURISCVState *env, int csrno, return ret; } +/* + * The function is written for two use-cases: + * 1- To access hvip csr as is for HS-mode access. + * 2- To access vsip as a combination of hvip, and mip for vs-mode. + * + * Both report bits 2, 6, 10 and 13:63. + * vsip needs to be read-only zero when both hideleg[i] and + * hvien[i] are zero. + */ static RISCVException rmw_hvip64(CPURISCVState *env, int csrno, uint64_t *ret_val, uint64_t new_val, uint64_t wr_mask) { RISCVException ret; + uint64_t old_hvip; + uint64_t ret_mip; + + /* + * For bits 10, 6 and 2, vsip[i] is an alias of hip[i]. These bits are + * present in hip, hvip and mip. Where mip[i] is alias of hip[i] and hvip[i] + * is OR'ed in hip[i] to inject virtual interrupts from hypervisor. These + * bits are actually being maintained in mip so we read them from there. + * This way we have a single source of truth and allows for easier + * implementation. + * + * For bits 13:63 we have: + * + * hideleg[i] hvien[i] + * 0 0 No delegation. vsip[i] readonly zero. + * 0 1 vsip[i] is alias of hvip[i], sip bypassed. + * 1 X vsip[i] is alias of sip[i], hvip bypassed. + * + * alias_mask denotes the bits that come from sip (mip here given we + * maintain all bits there). nalias_mask denotes bits that come from + * hvip. + */ + uint64_t alias_mask = (env->hideleg | ~env->hvien) | VS_MODE_INTERRUPTS; + uint64_t nalias_mask = (~env->hideleg & env->hvien); + uint64_t wr_mask_hvip; + uint64_t wr_mask_mip; + + /* + * Both alias and non-alias mask remain same for vsip except: + * 1- For VS* bits if they are zero in hideleg. + * 2- For 13:63 bits if they are zero in both hideleg and hvien. + */ + if (csrno == CSR_VSIP) { + /* zero-out VS* bits that are not delegated to VS mode. */ + alias_mask &= (env->hideleg | ~VS_MODE_INTERRUPTS); + + /* + * zero-out 13:63 bits that are zero in both hideleg and hvien. + * nalias_mask mask can not contain any VS* bits so only second + * condition applies on it. + */ + nalias_mask &= (env->hideleg | env->hvien); + alias_mask &= (env->hideleg | env->hvien); + } + + wr_mask_hvip = wr_mask & nalias_mask & hvip_writable_mask; + wr_mask_mip = wr_mask & alias_mask & hvip_writable_mask; + + /* Aliased bits, bits 10, 6, 2 need to come from mip. */ + ret = rmw_mip64(env, csrno, &ret_mip, new_val, wr_mask_mip); + if (ret != RISCV_EXCP_NONE) { + return ret; + } + + old_hvip = env->hvip; + + if (wr_mask_hvip) { + env->hvip = (env->hvip & ~wr_mask_hvip) | (new_val & wr_mask_hvip); + + /* + * Given hvip is separate source from mip, we need to trigger interrupt + * from here separately. Normally this happen from riscv_cpu_update_mip. + */ + riscv_cpu_interrupt(env); + } - ret = rmw_mip64(env, csrno, ret_val, new_val, - wr_mask & hvip_writable_mask); if (ret_val) { - *ret_val &= VS_MODE_INTERRUPTS; + /* Only take VS* bits from mip. */ + ret_mip &= alias_mask; + + /* Take in non-delegated 13:63 bits from hvip. */ + old_hvip &= nalias_mask; + + *ret_val = ret_mip | old_hvip; } return ret; @@ -4569,14 +4731,13 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { .min_priv_ver = PRIV_VERSION_1_12_0 }, /* Virtual Interrupts and Interrupt Priorities (H-extension with AIA) */ - [CSR_HVIEN] = { "hvien", aia_hmode, read_zero, write_ignore }, + [CSR_HVIEN] = { "hvien", aia_hmode, NULL, NULL, rmw_hvien }, [CSR_HVICTL] = { "hvictl", aia_hmode, read_hvictl, write_hvictl }, [CSR_HVIPRIO1] = { "hviprio1", aia_hmode, read_hviprio1, write_hviprio1 }, [CSR_HVIPRIO2] = { "hviprio2", aia_hmode, read_hviprio2, write_hviprio2 }, - /* * VS-Level Window to Indirectly Accessed Registers (H-extension with AIA) */ @@ -4591,8 +4752,7 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { /* Hypervisor and VS-Level High-Half CSRs (H-extension with AIA) */ [CSR_HIDELEGH] = { "hidelegh", aia_hmode32, NULL, NULL, rmw_hidelegh }, - [CSR_HVIENH] = { "hvienh", aia_hmode32, read_zero, - write_ignore }, + [CSR_HVIENH] = { "hvienh", aia_hmode32, NULL, NULL, rmw_hvienh }, [CSR_HVIPH] = { "hviph", aia_hmode32, NULL, NULL, rmw_hviph }, [CSR_HVIPRIO1H] = { "hviprio1h", aia_hmode32, read_hviprio1h, write_hviprio1h }, diff --git a/target/riscv/machine.c b/target/riscv/machine.c index f65a95f9e7..14bb2d7819 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -79,8 +79,8 @@ static bool hyper_needed(void *opaque) static const VMStateDescription vmstate_hyper = { .name = "cpu/hyper", - .version_id = 2, - .minimum_version_id = 2, + .version_id = 3, + .minimum_version_id = 3, .needed = hyper_needed, .fields = (VMStateField[]) { VMSTATE_UINTTL(env.hstatus, RISCVCPU), @@ -92,6 +92,8 @@ static const VMStateDescription vmstate_hyper = { VMSTATE_UINTTL(env.hgatp, RISCVCPU), VMSTATE_UINTTL(env.hgeie, RISCVCPU), VMSTATE_UINTTL(env.hgeip, RISCVCPU), + VMSTATE_UINT64(env.hvien, RISCVCPU), + VMSTATE_UINT64(env.hvip, RISCVCPU), VMSTATE_UINT64(env.htimedelta, RISCVCPU), VMSTATE_UINT64(env.vstimecmp, RISCVCPU), @@ -106,6 +108,7 @@ static const VMStateDescription vmstate_hyper = { VMSTATE_UINTTL(env.vstval, RISCVCPU), VMSTATE_UINTTL(env.vsatp, RISCVCPU), VMSTATE_UINTTL(env.vsiselect, RISCVCPU), + VMSTATE_UINT64(env.vsie, RISCVCPU), VMSTATE_UINTTL(env.mtval2, RISCVCPU), VMSTATE_UINTTL(env.mtinst, RISCVCPU), From 4d84cc5887c5ed9a630f5af87e56d64ee0a98c4b Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Fri, 20 Oct 2023 04:45:01 -0300 Subject: [PATCH 533/974] linux-user/riscv: change default cpu to 'max' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit f57d5f8004 deprecated the 'any' CPU type but failed to change the default CPU for linux-user. The result is that all linux-users invocations that doesn't specify a different CPU started to show a deprecation warning: $ ./build/qemu-riscv64 ./foo-novect.out qemu-riscv64: warning: The 'any' CPU is deprecated and will be removed in the future. Change the default CPU for RISC-V linux-user from 'any' to 'max'. Reported-by: Richard Henderson Fixes: f57d5f8004 ("target/riscv: deprecate the 'any' CPU type") Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-ID: <20231020074501.283063-1-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- linux-user/riscv/target_elf.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/linux-user/riscv/target_elf.h b/linux-user/riscv/target_elf.h index 9dd65652ee..dedd5956f3 100644 --- a/linux-user/riscv/target_elf.h +++ b/linux-user/riscv/target_elf.h @@ -9,7 +9,6 @@ #define RISCV_TARGET_ELF_H static inline const char *cpu_get_model(uint32_t eflags) { - /* TYPE_RISCV_CPU_ANY */ - return "any"; + return "max"; } #endif From 257cfaed47e3263ee3c379ec7a766f5050daa920 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Fri, 20 Oct 2023 17:02:47 -0300 Subject: [PATCH 534/974] docs/system/riscv: update 'virt' machine core limit The 'virt' RISC-V machine does not have a 8 core limit. The current limit is set in include/hw/riscv/virt.h, VIRT_CPUS_MAX, set to 512 at this moment. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1945 Signed-off-by: Daniel Henrique Barboza Message-ID: <20231020200247.334403-2-dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis Signed-off-by: Alistair Francis --- docs/system/riscv/virt.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/system/riscv/virt.rst b/docs/system/riscv/virt.rst index f9a2eac544..f5fa7b8b29 100644 --- a/docs/system/riscv/virt.rst +++ b/docs/system/riscv/virt.rst @@ -12,7 +12,7 @@ Supported devices The ``virt`` machine supports the following devices: -* Up to 8 generic RV32GC/RV64GC cores, with optional extensions +* Up to 512 generic RV32GC/RV64GC cores, with optional extensions * Core Local Interruptor (CLINT) * Platform-Level Interrupt Controller (PLIC) * CFI parallel NOR flash memory From 456a65546f6761aab09275ed7055d113608e3bb2 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 18 Oct 2023 16:56:33 -0300 Subject: [PATCH 535/974] target/riscv/kvm/kvm-cpu.c: add missing property getters() We got along without property getters in the KVM driver because we never needed them. But the incoming query-cpu-model-expansion API will use property getters and setters to retrieve the CPU characteristics. Add the missing getters for the KVM driver for both MISA and multi-letter extension properties. We're also adding an special getter for absent multi-letter properties that KVM doesn't implement that always return false. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20231018195638.211151-2-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/kvm/kvm-cpu.c | 40 +++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 6e1678542b..6bf035f39c 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -140,6 +140,19 @@ static KVMCPUConfig kvm_misa_ext_cfgs[] = { KVM_MISA_CFG(RVM, KVM_RISCV_ISA_EXT_M), }; +static void kvm_cpu_get_misa_ext_cfg(Object *obj, Visitor *v, + const char *name, + void *opaque, Error **errp) +{ + KVMCPUConfig *misa_ext_cfg = opaque; + target_ulong misa_bit = misa_ext_cfg->offset; + RISCVCPU *cpu = RISCV_CPU(obj); + CPURISCVState *env = &cpu->env; + bool value = env->misa_ext_mask & misa_bit; + + visit_type_bool(v, name, &value, errp); +} + static void kvm_cpu_set_misa_ext_cfg(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) @@ -244,6 +257,17 @@ static uint32_t kvm_cpu_cfg_get(RISCVCPU *cpu, return *ext_enabled; } +static void kvm_cpu_get_multi_ext_cfg(Object *obj, Visitor *v, + const char *name, + void *opaque, Error **errp) +{ + KVMCPUConfig *multi_ext_cfg = opaque; + RISCVCPU *cpu = RISCV_CPU(obj); + bool value = kvm_cpu_cfg_get(cpu, multi_ext_cfg); + + visit_type_bool(v, name, &value, errp); +} + static void kvm_cpu_set_multi_ext_cfg(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) @@ -346,6 +370,15 @@ static void kvm_riscv_update_cpu_cfg_isa_ext(RISCVCPU *cpu, CPUState *cs) } } +static void cpu_get_cfg_unavailable(Object *obj, Visitor *v, + const char *name, + void *opaque, Error **errp) +{ + bool value = false; + + visit_type_bool(v, name, &value, errp); +} + static void cpu_set_cfg_unavailable(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) @@ -376,7 +409,8 @@ static void riscv_cpu_add_kvm_unavail_prop(Object *obj, const char *prop_name) * to enable any of them. */ object_property_add(obj, prop_name, "bool", - NULL, cpu_set_cfg_unavailable, + cpu_get_cfg_unavailable, + cpu_set_cfg_unavailable, NULL, (void *)prop_name); } @@ -406,7 +440,7 @@ static void kvm_riscv_add_cpu_user_properties(Object *cpu_obj) misa_cfg->description = riscv_get_misa_ext_description(bit); object_property_add(cpu_obj, misa_cfg->name, "bool", - NULL, + kvm_cpu_get_misa_ext_cfg, kvm_cpu_set_misa_ext_cfg, NULL, misa_cfg); object_property_set_description(cpu_obj, misa_cfg->name, @@ -422,7 +456,7 @@ static void kvm_riscv_add_cpu_user_properties(Object *cpu_obj) KVMCPUConfig *multi_cfg = &kvm_multi_ext_cfgs[i]; object_property_add(cpu_obj, multi_cfg->name, "bool", - NULL, + kvm_cpu_get_multi_ext_cfg, kvm_cpu_set_multi_ext_cfg, NULL, multi_cfg); } From aeb2bc5950bda70f97c6a7fcbdf1ab2b167d4fa4 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 18 Oct 2023 16:56:34 -0300 Subject: [PATCH 536/974] qapi,risc-v: add query-cpu-model-expansion This API is used to inspect the characteristics of a given CPU model. It also allows users to validate a CPU model with a certain configuration, e.g. if "-cpu X,a=true,b=false" is a valid setup for a given QEMU binary. We'll start implementing the first part. The second requires more changes in RISC-V CPU boot flow. The implementation is inspired by the existing ARM query-cpu-model-expansion impl in target/arm/arm-qmp-cmds.c. We'll create a RISCVCPU object with the required model, fetch its existing properties, add a couple of relevant boolean options (pmp and mmu) and display it to users. Here's an usage example: ./build/qemu-system-riscv64 -S -M virt -display none \ -qmp tcp:localhost:1234,server,wait=off ./scripts/qmp/qmp-shell localhost:1234 Welcome to the QMP low-level shell! Connected to QEMU 8.1.50 (QEMU) query-cpu-model-expansion type=full model={"name":"rv64"} {"return": {"model": {"name": "rv64", "props": {"zicond": false, "x-zvfh": false, "mmu": true, "x-zvfbfwma": false, "x-zvfbfmin": false, "xtheadbs": false, "xtheadbb": false, "xtheadba": false, "xtheadmemidx": false, "smstateen": false, "zfinx": false, "Zve64f": false, "Zve32f": false, "x-zvfhmin": false, "xventanacondops": false, "xtheadcondmov": false, "svpbmt": false, "zbs": true, "zbc": true, "zbb": true, "zba": true, "zicboz": true, "xtheadmac": false, "Zfh": false, "Zfa": true, "zbkx": false, "zbkc": false, "zbkb": false, "Zve64d": false, "x-zfbfmin": false, "zk": false, "x-epmp": false, "xtheadmempair": false, "zkt": false, "zks": false, "zkr": false, "zkn": false, "Zfhmin": false, "zksh": false, "zknh": false, "zkne": false, "zknd": false, "zhinx": false, "Zicsr": true, "sscofpmf": false, "Zihintntl": true, "sstc": true, "xtheadcmo": false, "x-zvbb": false, "zksed": false, "x-zvkned": false, "xtheadsync": false, "x-zvkg": false, "zhinxmin": false, "svadu": true, "xtheadfmv": false, "x-zvksed": false, "svnapot": false, "pmp": true, "x-zvknhb": false, "x-zvknha": false, "xtheadfmemidx": false, "x-zvksh": false, "zdinx": false, "zicbom": true, "Zihintpause": true, "svinval": false, "zcf": false, "zce": false, "zcd": false, "zcb": false, "zca": false, "x-ssaia": false, "x-smaia": false, "zmmul": false, "x-zvbc": false, "Zifencei": true, "zcmt": false, "zcmp": false, "Zawrs": true}}}} Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20231018195638.211151-3-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- qapi/machine-target.json | 6 ++- target/riscv/riscv-qmp-cmds.c | 75 +++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/qapi/machine-target.json b/qapi/machine-target.json index c8d7d9868d..7b7149f81c 100644 --- a/qapi/machine-target.json +++ b/qapi/machine-target.json @@ -231,7 +231,8 @@ 'if': { 'any': [ 'TARGET_S390X', 'TARGET_I386', 'TARGET_ARM', - 'TARGET_LOONGARCH64' ] } } + 'TARGET_LOONGARCH64', + 'TARGET_RISCV' ] } } ## # @query-cpu-model-expansion: @@ -277,7 +278,8 @@ 'if': { 'any': [ 'TARGET_S390X', 'TARGET_I386', 'TARGET_ARM', - 'TARGET_LOONGARCH64' ] } } + 'TARGET_LOONGARCH64', + 'TARGET_RISCV' ] } } ## # @CpuDefinitionInfo: diff --git a/target/riscv/riscv-qmp-cmds.c b/target/riscv/riscv-qmp-cmds.c index 5ecff1afb3..2170562e3a 100644 --- a/target/riscv/riscv-qmp-cmds.c +++ b/target/riscv/riscv-qmp-cmds.c @@ -24,8 +24,12 @@ #include "qemu/osdep.h" +#include "qapi/error.h" #include "qapi/qapi-commands-machine-target.h" +#include "qapi/qmp/qdict.h" +#include "qom/qom-qobject.h" #include "cpu-qom.h" +#include "cpu.h" static void riscv_cpu_add_definition(gpointer data, gpointer user_data) { @@ -55,3 +59,74 @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) return cpu_list; } + +static void riscv_obj_add_qdict_prop(Object *obj, QDict *qdict_out, + const char *name) +{ + ObjectProperty *prop = object_property_find(obj, name); + + if (prop) { + QObject *value; + + assert(prop->get); + value = object_property_get_qobject(obj, name, &error_abort); + + qdict_put_obj(qdict_out, name, value); + } +} + +static void riscv_obj_add_multiext_props(Object *obj, QDict *qdict_out, + const RISCVCPUMultiExtConfig *arr) +{ + for (int i = 0; arr[i].name != NULL; i++) { + riscv_obj_add_qdict_prop(obj, qdict_out, arr[i].name); + } +} + +CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, + CpuModelInfo *model, + Error **errp) +{ + CpuModelExpansionInfo *expansion_info; + QDict *qdict_out; + ObjectClass *oc; + Object *obj; + + if (type != CPU_MODEL_EXPANSION_TYPE_FULL) { + error_setg(errp, "The requested expansion type is not supported"); + return NULL; + } + + oc = cpu_class_by_name(TYPE_RISCV_CPU, model->name); + if (!oc) { + error_setg(errp, "The CPU type '%s' is not a known RISC-V CPU type", + model->name); + return NULL; + } + + obj = object_new(object_class_get_name(oc)); + + expansion_info = g_new0(CpuModelExpansionInfo, 1); + expansion_info->model = g_malloc0(sizeof(*expansion_info->model)); + expansion_info->model->name = g_strdup(model->name); + + qdict_out = qdict_new(); + + riscv_obj_add_multiext_props(obj, qdict_out, riscv_cpu_extensions); + riscv_obj_add_multiext_props(obj, qdict_out, riscv_cpu_experimental_exts); + riscv_obj_add_multiext_props(obj, qdict_out, riscv_cpu_vendor_exts); + + /* Add our CPU boolean options too */ + riscv_obj_add_qdict_prop(obj, qdict_out, "mmu"); + riscv_obj_add_qdict_prop(obj, qdict_out, "pmp"); + + if (!qdict_size(qdict_out)) { + qobject_unref(qdict_out); + } else { + expansion_info->model->props = QOBJECT(qdict_out); + } + + object_unref(obj); + + return expansion_info; +} From a13a6082c7488d4393453d383676ae423b38b4d8 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 18 Oct 2023 16:56:35 -0300 Subject: [PATCH 537/974] target/riscv/tcg: add tcg_cpu_finalize_features() The query-cpu-model-expansion API is capable of passing extra properties to a given CPU model and tell callers if this custom configuration is valid. The RISC-V version of the API is not quite there yet. The reason is the realize() flow in the TCG driver, where most of the validation is done in tcg_cpu_realizefn(). riscv_cpu_finalize_features() is then used to validate satp_mode for both TCG and KVM CPUs. Our ARM friends uses a concept of 'finalize_features()', a step done in the end of realize() where the CPU features are validated. We have a riscv_cpu_finalize_features() helper that, at this moment, is only validating satp_mode. Re-use this existing helper to do all CPU extension validation we required after at the end of realize(). Make it public to allow APIs to use it. At this moment only the TCG driver requires a realize() time validation, thus, to avoid adding accelerator specific helpers in the API, riscv_cpu_finalize_features() uses riscv_tcg_cpu_finalize_features() if we are running TCG. The API will then use riscv_cpu_finalize_features() regardless of the current accelerator. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20231018195638.211151-4-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 18 +++++++++-- target/riscv/cpu.h | 1 + target/riscv/tcg/tcg-cpu.c | 63 +++++++++++++++++++++----------------- target/riscv/tcg/tcg-cpu.h | 1 + 4 files changed, 53 insertions(+), 30 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 2f98ce56e0..02db0834dd 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -34,6 +34,7 @@ #include "sysemu/kvm.h" #include "sysemu/tcg.h" #include "kvm/kvm_riscv.h" +#include "tcg/tcg-cpu.h" #include "tcg/tcg.h" /* RISC-V CPU definitions */ @@ -998,11 +999,24 @@ static void riscv_cpu_satp_mode_finalize(RISCVCPU *cpu, Error **errp) } #endif -static void riscv_cpu_finalize_features(RISCVCPU *cpu, Error **errp) +void riscv_cpu_finalize_features(RISCVCPU *cpu, Error **errp) { -#ifndef CONFIG_USER_ONLY Error *local_err = NULL; + /* + * KVM accel does not have a specialized finalize() + * callback because its extensions are validated + * in the get()/set() callbacks of each property. + */ + if (tcg_enabled()) { + riscv_tcg_cpu_finalize_features(cpu, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } + } + +#ifndef CONFIG_USER_ONLY riscv_cpu_satp_mode_finalize(cpu, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 7f61e17202..8c9ec59d82 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -756,6 +756,7 @@ typedef struct isa_ext_data { extern const RISCVIsaExtData isa_edata_arr[]; char *riscv_cpu_get_name(RISCVCPU *cpu); +void riscv_cpu_finalize_features(RISCVCPU *cpu, Error **errp); void riscv_add_satp_mode_properties(Object *obj); /* CSR function table */ diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index bbce254ee1..21a46f2a0e 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -548,6 +548,39 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) riscv_cpu_disable_priv_spec_isa_exts(cpu); } +void riscv_tcg_cpu_finalize_features(RISCVCPU *cpu, Error **errp) +{ + CPURISCVState *env = &cpu->env; + Error *local_err = NULL; + + riscv_cpu_validate_priv_spec(cpu, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } + + riscv_cpu_validate_misa_priv(env, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } + + if (cpu->cfg.epmp && !cpu->cfg.pmp) { + /* + * Enhanced PMP should only be available + * on harts with PMP support + */ + error_setg(errp, "Invalid configuration: EPMP requires PMP support"); + return; + } + + riscv_cpu_validate_set_extensions(cpu, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } +} + static bool riscv_cpu_is_generic(Object *cpu_obj) { return object_dynamic_cast(cpu_obj, TYPE_RISCV_DYNAMIC_CPU) != NULL; @@ -563,7 +596,6 @@ static bool riscv_cpu_is_generic(Object *cpu_obj) static bool tcg_cpu_realize(CPUState *cs, Error **errp) { RISCVCPU *cpu = RISCV_CPU(cs); - CPURISCVState *env = &cpu->env; Error *local_err = NULL; if (object_dynamic_cast(OBJECT(cpu), TYPE_RISCV_CPU_HOST)) { @@ -579,34 +611,9 @@ static bool tcg_cpu_realize(CPUState *cs, Error **errp) return false; } - riscv_cpu_validate_priv_spec(cpu, &local_err); - if (local_err != NULL) { - error_propagate(errp, local_err); - return false; - } - - riscv_cpu_validate_misa_priv(env, &local_err); - if (local_err != NULL) { - error_propagate(errp, local_err); - return false; - } - - if (cpu->cfg.epmp && !cpu->cfg.pmp) { - /* - * Enhanced PMP should only be available - * on harts with PMP support - */ - error_setg(errp, "Invalid configuration: EPMP requires PMP support"); - return false; - } - - riscv_cpu_validate_set_extensions(cpu, &local_err); - if (local_err != NULL) { - error_propagate(errp, local_err); - return false; - } - #ifndef CONFIG_USER_ONLY + CPURISCVState *env = &cpu->env; + CPU(cs)->tcg_cflags |= CF_PCREL; if (cpu->cfg.ext_sstc) { diff --git a/target/riscv/tcg/tcg-cpu.h b/target/riscv/tcg/tcg-cpu.h index 630184759d..aa00fbc253 100644 --- a/target/riscv/tcg/tcg-cpu.h +++ b/target/riscv/tcg/tcg-cpu.h @@ -23,5 +23,6 @@ #include "cpu.h" void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp); +void riscv_tcg_cpu_finalize_features(RISCVCPU *cpu, Error **errp); #endif From 1df4f540d6352131cefa7ca48b637ccb774ce9e0 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 18 Oct 2023 16:56:36 -0300 Subject: [PATCH 538/974] target/riscv: handle custom props in qmp_query_cpu_model_expansion Callers can add 'props' when querying for a cpu model expansion to see if a given CPU model supports a certain criteria, and what's the resulting CPU object. If we have 'props' to handle, gather it in a QDict and use the new riscv_cpuobj_validate_qdict_in() helper to validate it. This helper will add the custom properties in the CPU object and validate it using riscv_cpu_finalize_features(). Users will be aware of validation errors if any occur, if not a CPU object with 'props' will be returned. Here's an example with the veyron-v1 vendor CPU. Disabling vendor CPU extensions is allowed, assuming the final config is valid. Disabling 'smstateen' is a valid expansion: (QEMU) query-cpu-model-expansion type=full model={"name":"veyron-v1","props":{"smstateen":false}} {"return": {"model": {"name": "veyron-v1", "props": {"zicond": false, ..., "smstateen": false, ...} But enabling extensions isn't allowed for vendor CPUs. E.g. enabling 'V' for the veyron-v1 CPU isn't allowed: (QEMU) query-cpu-model-expansion type=full model={"name":"veyron-v1","props":{"v":true}} {"error": {"class": "GenericError", "desc": "'veyron-v1' CPU does not allow enabling extensions"}} Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20231018195638.211151-5-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/riscv-qmp-cmds.c | 65 +++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/target/riscv/riscv-qmp-cmds.c b/target/riscv/riscv-qmp-cmds.c index 2170562e3a..5b2d186c83 100644 --- a/target/riscv/riscv-qmp-cmds.c +++ b/target/riscv/riscv-qmp-cmds.c @@ -27,6 +27,9 @@ #include "qapi/error.h" #include "qapi/qapi-commands-machine-target.h" #include "qapi/qmp/qdict.h" +#include "qapi/qmp/qerror.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/visitor.h" #include "qom/qom-qobject.h" #include "cpu-qom.h" #include "cpu.h" @@ -83,14 +86,58 @@ static void riscv_obj_add_multiext_props(Object *obj, QDict *qdict_out, } } +static void riscv_cpuobj_validate_qdict_in(Object *obj, QObject *props, + const QDict *qdict_in, + Error **errp) +{ + const QDictEntry *qe; + Visitor *visitor; + Error *local_err = NULL; + + visitor = qobject_input_visitor_new(props); + if (!visit_start_struct(visitor, NULL, NULL, 0, &local_err)) { + goto err; + } + + for (qe = qdict_first(qdict_in); qe; qe = qdict_next(qdict_in, qe)) { + object_property_find_err(obj, qe->key, &local_err); + if (local_err) { + goto err; + } + + object_property_set(obj, qe->key, visitor, &local_err); + if (local_err) { + goto err; + } + } + + visit_check_struct(visitor, &local_err); + if (local_err) { + goto err; + } + + riscv_cpu_finalize_features(RISCV_CPU(obj), &local_err); + if (local_err) { + goto err; + } + + visit_end_struct(visitor, NULL); + +err: + error_propagate(errp, local_err); + visit_free(visitor); +} + CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, CpuModelInfo *model, Error **errp) { CpuModelExpansionInfo *expansion_info; + const QDict *qdict_in = NULL; QDict *qdict_out; ObjectClass *oc; Object *obj; + Error *local_err = NULL; if (type != CPU_MODEL_EXPANSION_TYPE_FULL) { error_setg(errp, "The requested expansion type is not supported"); @@ -104,8 +151,26 @@ CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, return NULL; } + if (model->props) { + qdict_in = qobject_to(QDict, model->props); + if (!qdict_in) { + error_setg(errp, QERR_INVALID_PARAMETER_TYPE, "props", "dict"); + return NULL; + } + } + obj = object_new(object_class_get_name(oc)); + if (qdict_in) { + riscv_cpuobj_validate_qdict_in(obj, model->props, qdict_in, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + object_unref(obj); + return NULL; + } + } + expansion_info = g_new0(CpuModelExpansionInfo, 1); expansion_info->model = g_malloc0(sizeof(*expansion_info->model)); expansion_info->model->name = g_strdup(model->name); From ef58fad0fdc7aba2c2196f9c35a89889286ef92b Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 18 Oct 2023 16:56:37 -0300 Subject: [PATCH 539/974] target/riscv: add riscv_cpu_accelerator_compatible() Add an API to check if a given CPU is compatible with the current accelerator. This will allow query-cpu-model-expansion to work properly in conditions where QEMU supports both accelerators (TCG and KVM), QEMU is then launched using TCG, and the API requests information about a KVM only CPU (e.g. 'host' CPU). KVM doesn't have such restrictions and, at least in theory, all CPUs models should work with KVM. We will revisit this API in case we decide to restrict the amount of KVM CPUs we support. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20231018195638.211151-6-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 9 +++++++++ target/riscv/cpu.h | 1 + target/riscv/tcg/tcg-cpu.c | 7 ++++++- target/riscv/tcg/tcg-cpu.h | 1 + 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 02db0834dd..8e0abe33d3 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1063,6 +1063,15 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) mcc->parent_realize(dev, errp); } +bool riscv_cpu_accelerator_compatible(RISCVCPU *cpu) +{ + if (tcg_enabled()) { + return riscv_cpu_tcg_compatible(cpu); + } + + return true; +} + #ifndef CONFIG_USER_ONLY static void cpu_riscv_get_satp(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 8c9ec59d82..8efc4d83ec 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -758,6 +758,7 @@ char *riscv_cpu_get_name(RISCVCPU *cpu); void riscv_cpu_finalize_features(RISCVCPU *cpu, Error **errp); void riscv_add_satp_mode_properties(Object *obj); +bool riscv_cpu_accelerator_compatible(RISCVCPU *cpu); /* CSR function table */ extern riscv_csr_operations csr_ops[CSR_TABLE_SIZE]; diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 21a46f2a0e..6771617226 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -581,6 +581,11 @@ void riscv_tcg_cpu_finalize_features(RISCVCPU *cpu, Error **errp) } } +bool riscv_cpu_tcg_compatible(RISCVCPU *cpu) +{ + return object_dynamic_cast(OBJECT(cpu), TYPE_RISCV_CPU_HOST) == NULL; +} + static bool riscv_cpu_is_generic(Object *cpu_obj) { return object_dynamic_cast(cpu_obj, TYPE_RISCV_DYNAMIC_CPU) != NULL; @@ -598,7 +603,7 @@ static bool tcg_cpu_realize(CPUState *cs, Error **errp) RISCVCPU *cpu = RISCV_CPU(cs); Error *local_err = NULL; - if (object_dynamic_cast(OBJECT(cpu), TYPE_RISCV_CPU_HOST)) { + if (!riscv_cpu_tcg_compatible(cpu)) { g_autofree char *name = riscv_cpu_get_name(cpu); error_setg(errp, "'%s' CPU is not compatible with TCG acceleration", name); diff --git a/target/riscv/tcg/tcg-cpu.h b/target/riscv/tcg/tcg-cpu.h index aa00fbc253..f7b32417f8 100644 --- a/target/riscv/tcg/tcg-cpu.h +++ b/target/riscv/tcg/tcg-cpu.h @@ -24,5 +24,6 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp); void riscv_tcg_cpu_finalize_features(RISCVCPU *cpu, Error **errp); +bool riscv_cpu_tcg_compatible(RISCVCPU *cpu); #endif From a3abecbef0ad5e121c0d3f8f26568ab0466d9a6a Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 18 Oct 2023 16:56:38 -0300 Subject: [PATCH 540/974] target/riscv/riscv-qmp-cmds.c: check CPU accel in query-cpu-model-expansion Use the recently added riscv_cpu_accelerator_compatible() to filter unavailable CPUs for a given accelerator. At this moment this is the case for a QEMU built with KVM and TCG support querying a binary running with TCG: qemu-system-riscv64 -S -M virt,accel=tcg -display none -qmp tcp:localhost:1234,server,wait=off ./qemu/scripts/qmp/qmp-shell localhost:1234 (QEMU) query-cpu-model-expansion type=full model={"name":"host"} {"error": {"class": "GenericError", "desc": "'host' CPU not available with tcg"}} Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20231018195638.211151-7-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/riscv-qmp-cmds.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/target/riscv/riscv-qmp-cmds.c b/target/riscv/riscv-qmp-cmds.c index 5b2d186c83..2f2dbae7c8 100644 --- a/target/riscv/riscv-qmp-cmds.c +++ b/target/riscv/riscv-qmp-cmds.c @@ -31,6 +31,8 @@ #include "qapi/qobject-input-visitor.h" #include "qapi/visitor.h" #include "qom/qom-qobject.h" +#include "sysemu/kvm.h" +#include "sysemu/tcg.h" #include "cpu-qom.h" #include "cpu.h" @@ -63,6 +65,17 @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) return cpu_list; } +static void riscv_check_if_cpu_available(RISCVCPU *cpu, Error **errp) +{ + if (!riscv_cpu_accelerator_compatible(cpu)) { + g_autofree char *name = riscv_cpu_get_name(cpu); + const char *accel = kvm_enabled() ? "kvm" : "tcg"; + + error_setg(errp, "'%s' CPU not available with %s", name, accel); + return; + } +} + static void riscv_obj_add_qdict_prop(Object *obj, QDict *qdict_out, const char *name) { @@ -161,6 +174,13 @@ CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, obj = object_new(object_class_get_name(oc)); + riscv_check_if_cpu_available(RISCV_CPU(obj), &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + object_unref(obj); + return NULL; + } + if (qdict_in) { riscv_cpuobj_validate_qdict_in(obj, model->props, qdict_in, &local_err); From 095fe72a128b34d4f9317c2798c6fa7762a9e3e6 Mon Sep 17 00:00:00 2001 From: Himanshu Chauhan Date: Thu, 19 Oct 2023 12:25:46 +0530 Subject: [PATCH 541/974] Add epmp to extensions list and rename it to smepmp Smepmp is a ratified extension which qemu refers to as epmp. Rename epmp to smepmp and add it to extension list so that it is added to the isa string. Signed-off-by: Himanshu Chauhan Signed-off-by: Mayuresh Chitale Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20231019065546.1431579-1-mchitale@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 8 +++----- target/riscv/cpu_cfg.h | 2 +- target/riscv/csr.c | 6 +++--- target/riscv/pmp.c | 12 ++++++------ target/riscv/tcg/tcg-cpu.c | 4 ++-- 5 files changed, 15 insertions(+), 17 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 8e0abe33d3..0c58c8571f 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -134,7 +134,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zhinx, PRIV_VERSION_1_12_0, ext_zhinx), ISA_EXT_DATA_ENTRY(zhinxmin, PRIV_VERSION_1_12_0, ext_zhinxmin), ISA_EXT_DATA_ENTRY(smaia, PRIV_VERSION_1_12_0, ext_smaia), - ISA_EXT_DATA_ENTRY(smepmp, PRIV_VERSION_1_12_0, epmp), + ISA_EXT_DATA_ENTRY(smepmp, PRIV_VERSION_1_12_0, ext_smepmp), ISA_EXT_DATA_ENTRY(smstateen, PRIV_VERSION_1_12_0, ext_smstateen), ISA_EXT_DATA_ENTRY(ssaia, PRIV_VERSION_1_12_0, ext_ssaia), ISA_EXT_DATA_ENTRY(sscofpmf, PRIV_VERSION_1_12_0, ext_sscofpmf), @@ -600,12 +600,11 @@ static void rv32_ibex_cpu_init(Object *obj) #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(cpu, VM_1_10_MBARE); #endif - cpu->cfg.epmp = true; - /* inherited from parent obj via riscv_cpu_init() */ cpu->cfg.ext_zifencei = true; cpu->cfg.ext_zicsr = true; cpu->cfg.pmp = true; + cpu->cfg.ext_smepmp = true; } static void rv32_imafcu_nommu_cpu_init(Object *obj) @@ -1280,6 +1279,7 @@ const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = { MULTI_EXT_CFG_BOOL("zve64d", ext_zve64d, false), MULTI_EXT_CFG_BOOL("sstc", ext_sstc, true), + MULTI_EXT_CFG_BOOL("smepmp", ext_smepmp, false), MULTI_EXT_CFG_BOOL("smstateen", ext_smstateen, false), MULTI_EXT_CFG_BOOL("svadu", ext_svadu, true), MULTI_EXT_CFG_BOOL("svinval", ext_svinval, false), @@ -1345,8 +1345,6 @@ const RISCVCPUMultiExtConfig riscv_cpu_vendor_exts[] = { /* These are experimental so mark with 'x-' */ const RISCVCPUMultiExtConfig riscv_cpu_experimental_exts[] = { - /* ePMP 0.9.3 */ - MULTI_EXT_CFG_BOOL("x-epmp", epmp, false), MULTI_EXT_CFG_BOOL("x-smaia", ext_smaia, false), MULTI_EXT_CFG_BOOL("x-ssaia", ext_ssaia, false), diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index 208cac1c7c..e7ce977189 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -102,6 +102,7 @@ struct RISCVCPUConfig { bool ext_smaia; bool ext_ssaia; bool ext_sscofpmf; + bool ext_smepmp; bool rvv_ta_all_1s; bool rvv_ma_all_1s; @@ -134,7 +135,6 @@ struct RISCVCPUConfig { uint16_t cboz_blocksize; bool mmu; bool pmp; - bool epmp; bool debug; bool misa_w; diff --git a/target/riscv/csr.c b/target/riscv/csr.c index a5be1c202c..f4e0a3962f 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -524,9 +524,9 @@ static RISCVException pmp(CPURISCVState *env, int csrno) return RISCV_EXCP_ILLEGAL_INST; } -static RISCVException epmp(CPURISCVState *env, int csrno) +static RISCVException smepmp(CPURISCVState *env, int csrno) { - if (riscv_cpu_cfg(env)->epmp) { + if (riscv_cpu_cfg(env)->ext_smepmp) { return RISCV_EXCP_NONE; } @@ -4762,7 +4762,7 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_VSIPH] = { "vsiph", aia_hmode32, NULL, NULL, rmw_vsiph }, /* Physical Memory Protection */ - [CSR_MSECCFG] = { "mseccfg", epmp, read_mseccfg, write_mseccfg, + [CSR_MSECCFG] = { "mseccfg", smepmp, read_mseccfg, write_mseccfg, .min_priv_ver = PRIV_VERSION_1_11_0 }, [CSR_PMPCFG0] = { "pmpcfg0", pmp, read_pmpcfg, write_pmpcfg }, [CSR_PMPCFG1] = { "pmpcfg1", pmp, read_pmpcfg, write_pmpcfg }, diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index 5e60c26031..21d2489e27 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -91,7 +91,7 @@ static bool pmp_write_cfg(CPURISCVState *env, uint32_t pmp_index, uint8_t val) if (pmp_index < MAX_RISCV_PMPS) { bool locked = true; - if (riscv_cpu_cfg(env)->epmp) { + if (riscv_cpu_cfg(env)->ext_smepmp) { /* mseccfg.RLB is set */ if (MSECCFG_RLB_ISSET(env)) { locked = false; @@ -340,9 +340,9 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, /* * Convert the PMP permissions to match the truth table in the - * ePMP spec. + * Smepmp spec. */ - const uint8_t epmp_operation = + const uint8_t smepmp_operation = ((env->pmp_state.pmp[i].cfg_reg & PMP_LOCK) >> 4) | ((env->pmp_state.pmp[i].cfg_reg & PMP_READ) << 2) | (env->pmp_state.pmp[i].cfg_reg & PMP_WRITE) | @@ -367,7 +367,7 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, * If mseccfg.MML Bit set, do the enhanced pmp priv check */ if (mode == PRV_M) { - switch (epmp_operation) { + switch (smepmp_operation) { case 0: case 1: case 4: @@ -398,7 +398,7 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, g_assert_not_reached(); } } else { - switch (epmp_operation) { + switch (smepmp_operation) { case 0: case 8: case 9: @@ -574,7 +574,7 @@ void mseccfg_csr_write(CPURISCVState *env, target_ulong val) } } - if (riscv_cpu_cfg(env)->epmp) { + if (riscv_cpu_cfg(env)->ext_smepmp) { /* Sticky bits */ val |= (env->mseccfg & (MSECCFG_MMWP | MSECCFG_MML)); if ((val ^ env->mseccfg) & (MSECCFG_MMWP | MSECCFG_MML)) { diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 6771617226..c5ff03efce 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -565,12 +565,12 @@ void riscv_tcg_cpu_finalize_features(RISCVCPU *cpu, Error **errp) return; } - if (cpu->cfg.epmp && !cpu->cfg.pmp) { + if (cpu->cfg.ext_smepmp && !cpu->cfg.pmp) { /* * Enhanced PMP should only be available * on harts with PMP support */ - error_setg(errp, "Invalid configuration: EPMP requires PMP support"); + error_setg(errp, "Invalid configuration: Smepmp requires PMP support"); return; } From 4bf501dc0118a28699e28c01acb34e28ddeb0acc Mon Sep 17 00:00:00 2001 From: Mayuresh Chitale Date: Thu, 19 Oct 2023 12:26:44 +0530 Subject: [PATCH 542/974] target/riscv: pmp: Clear pmp/smepmp bits on reset As per the Priv and Smepmp specifications, certain bits such as the 'L' bit of pmp entries and mseccfg.MML can only be cleared upon reset and it is necessary to do so to allow 'M' mode firmware to correctly reinitialize the pmp/smpemp state across reboots. As required by the spec, also clear the 'A' field of pmp entries. Signed-off-by: Mayuresh Chitale Reviewed-by: Alistair Francis Message-ID: <20231019065644.1431798-1-mchitale@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 11 +++++++++++ target/riscv/pmp.c | 10 ++++++++++ target/riscv/pmp.h | 2 ++ 3 files changed, 23 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 0c58c8571f..a2881bfa38 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -884,6 +884,17 @@ static void riscv_cpu_reset_hold(Object *obj) } /* mmte is supposed to have pm.current hardwired to 1 */ env->mmte |= (EXT_STATUS_INITIAL | MMTE_M_PM_CURRENT); + + /* + * Clear mseccfg and unlock all the PMP entries upon reset. + * This is allowed as per the priv and smepmp specifications + * and is needed to clear stale entries across reboots. + */ + if (riscv_cpu_cfg(env)->ext_smepmp) { + env->mseccfg = 0; + } + + pmp_unlock_entries(env); #endif env->xl = riscv_cpu_mxl(env); riscv_cpu_update_mask(env); diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index 21d2489e27..4dfaa28fce 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -135,6 +135,16 @@ static bool pmp_write_cfg(CPURISCVState *env, uint32_t pmp_index, uint8_t val) return false; } +void pmp_unlock_entries(CPURISCVState *env) +{ + uint32_t pmp_num = pmp_get_num_rules(env); + int i; + + for (i = 0; i < pmp_num; i++) { + env->pmp_state.pmp[i].cfg_reg &= ~(PMP_LOCK | PMP_AMATCH); + } +} + static void pmp_decode_napot(target_ulong a, target_ulong *sa, target_ulong *ea) { diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h index cf5c99f8e6..9af8614cd4 100644 --- a/target/riscv/pmp.h +++ b/target/riscv/pmp.h @@ -28,6 +28,7 @@ typedef enum { PMP_READ = 1 << 0, PMP_WRITE = 1 << 1, PMP_EXEC = 1 << 2, + PMP_AMATCH = (3 << 3), PMP_LOCK = 1 << 7 } pmp_priv_t; @@ -81,6 +82,7 @@ void pmp_update_rule_addr(CPURISCVState *env, uint32_t pmp_index); void pmp_update_rule_nums(CPURISCVState *env); uint32_t pmp_get_num_rules(CPURISCVState *env); int pmp_priv_to_page_prot(pmp_priv_t pmp_priv); +void pmp_unlock_entries(CPURISCVState *env); #define MSECCFG_MML_ISSET(env) get_field(env->mseccfg, MSECCFG_MML) #define MSECCFG_MMWP_ISSET(env) get_field(env->mseccfg, MSECCFG_MMWP) From ac66f2f0d12d7ebb69bb45d5eb7f73fb0542bae5 Mon Sep 17 00:00:00 2001 From: Mayuresh Chitale Date: Thu, 19 Oct 2023 12:27:05 +0530 Subject: [PATCH 543/974] target/riscv: pmp: Ignore writes when RW=01 As per the Priv spec: "The R, W, and X fields form a collective WARL field for which the combinations with R=0 and W=1 are reserved." However currently such writes are not ignored as ought to be. The combinations with RW=01 are allowed only when the Smepmp extension is enabled and mseccfg.MML is set. Signed-off-by: Mayuresh Chitale Reviewed-by: Alistair Francis Message-ID: <20231019065705.1431868-1-mchitale@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/pmp.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index 4dfaa28fce..162e88a90a 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -123,6 +123,11 @@ static bool pmp_write_cfg(CPURISCVState *env, uint32_t pmp_index, uint8_t val) if (locked) { qemu_log_mask(LOG_GUEST_ERROR, "ignoring pmpcfg write - locked\n"); } else if (env->pmp_state.pmp[pmp_index].cfg_reg != val) { + /* If !mseccfg.MML then ignore writes with encoding RW=01 */ + if ((val & PMP_WRITE) && !(val & PMP_READ) && + !MSECCFG_MML_ISSET(env)) { + val &= ~(PMP_WRITE | PMP_READ); + } env->pmp_state.pmp[pmp_index].cfg_reg = val; pmp_update_rule_addr(env, pmp_index); return true; From c004099330c2bb9c94984ac917815572fcd55bd0 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Mon, 23 Oct 2023 12:39:24 -0300 Subject: [PATCH 544/974] target/riscv: add zicntr extension flag for TCG zicntr is the Base Counters and Timers extension described in chapter 12 of the unprivileged spec. It describes support for RDCYCLE, RDTIME and RDINSTRET. QEMU already implements it in TCG way before it was a discrete extension. zicntr is part of the RVA22 profile, so let's add it to QEMU to make the future profile implementation flag complete. Given than it represents an already existing feature, default it to 'true' for all CPUs. For TCG, we need a way to disable zicntr if the user wants to. This is done by restricting access to the CYCLE, TIME, and INSTRET counters via the 'ctr()' predicate when we're about to access them. Disabling zicntr happens via the command line or if its dependency, zicsr, happens to be disabled. We'll check for zicsr during realize() and, in case it's absent, disable zicntr. However, if the user was explicit about having zicntr support, error out instead of disabling it. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20231023153927.435083-2-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 12 ++++++++++++ target/riscv/cpu_cfg.h | 1 + target/riscv/csr.c | 4 ++++ target/riscv/tcg/tcg-cpu.c | 8 ++++++++ 4 files changed, 25 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index a2881bfa38..69d64ec4ca 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -80,6 +80,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zicbom, PRIV_VERSION_1_12_0, ext_zicbom), ISA_EXT_DATA_ENTRY(zicboz, PRIV_VERSION_1_12_0, ext_zicboz), ISA_EXT_DATA_ENTRY(zicond, PRIV_VERSION_1_12_0, ext_zicond), + ISA_EXT_DATA_ENTRY(zicntr, PRIV_VERSION_1_12_0, ext_zicntr), ISA_EXT_DATA_ENTRY(zicsr, PRIV_VERSION_1_10_0, ext_zicsr), ISA_EXT_DATA_ENTRY(zifencei, PRIV_VERSION_1_10_0, ext_zifencei), ISA_EXT_DATA_ENTRY(zihintntl, PRIV_VERSION_1_10_0, ext_zihintntl), @@ -1208,6 +1209,15 @@ static void riscv_cpu_init(Object *obj) qdev_init_gpio_in(DEVICE(obj), riscv_cpu_set_irq, IRQ_LOCAL_MAX + IRQ_LOCAL_GUEST_MAX); #endif /* CONFIG_USER_ONLY */ + + /* + * The timer and performance counters extensions were supported + * in QEMU before they were added as discrete extensions in the + * ISA. To keep compatibility we'll always default them to 'true' + * for all CPUs. Each accelerator will decide what to do when + * users disable them. + */ + RISCV_CPU(obj)->cfg.ext_zicntr = true; } typedef struct misa_ext_info { @@ -1297,6 +1307,8 @@ const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = { MULTI_EXT_CFG_BOOL("svnapot", ext_svnapot, false), MULTI_EXT_CFG_BOOL("svpbmt", ext_svpbmt, false), + MULTI_EXT_CFG_BOOL("zicntr", ext_zicntr, true), + MULTI_EXT_CFG_BOOL("zba", ext_zba, true), MULTI_EXT_CFG_BOOL("zbb", ext_zbb, true), MULTI_EXT_CFG_BOOL("zbc", ext_zbc, true), diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index e7ce977189..73fd4b3231 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -62,6 +62,7 @@ struct RISCVCPUConfig { bool ext_zksh; bool ext_zkt; bool ext_zifencei; + bool ext_zicntr; bool ext_zicsr; bool ext_zicbom; bool ext_zicboz; diff --git a/target/riscv/csr.c b/target/riscv/csr.c index f4e0a3962f..4ca96ddd1d 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -122,6 +122,10 @@ static RISCVException ctr(CPURISCVState *env, int csrno) if ((csrno >= CSR_CYCLE && csrno <= CSR_INSTRET) || (csrno >= CSR_CYCLEH && csrno <= CSR_INSTRETH)) { + if (!riscv_cpu_cfg(env)->ext_zicntr) { + return RISCV_EXCP_ILLEGAL_INST; + } + goto skip_ext_pmu_check; } diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index c5ff03efce..a1e4ed2e24 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -541,6 +541,14 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zksh), true); } + if (cpu->cfg.ext_zicntr && !cpu->cfg.ext_zicsr) { + if (cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_zicntr))) { + error_setg(errp, "zicntr requires zicsr"); + return; + } + cpu->cfg.ext_zicntr = false; + } + /* * Disable isa extensions based on priv spec after we * validated and set everything we need. From b31dee8a7d3e515b5129f8ec57fb38cc193fac6e Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Mon, 23 Oct 2023 12:39:25 -0300 Subject: [PATCH 545/974] target/riscv/kvm: add zicntr reg Add zicntr support in the KVM driver now that QEMU supports it. This reg was added in Linux 6.6. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20231023153927.435083-3-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/kvm/kvm-cpu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 6bf035f39c..0c5c0e957b 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -228,6 +228,7 @@ static void kvm_riscv_update_cpu_misa_ext(RISCVCPU *cpu, CPUState *cs) static KVMCPUConfig kvm_multi_ext_cfgs[] = { KVM_EXT_CFG("zicbom", ext_zicbom, KVM_RISCV_ISA_EXT_ZICBOM), KVM_EXT_CFG("zicboz", ext_zicboz, KVM_RISCV_ISA_EXT_ZICBOZ), + KVM_EXT_CFG("zicntr", ext_zicntr, KVM_RISCV_ISA_EXT_ZICNTR), KVM_EXT_CFG("zihintpause", ext_zihintpause, KVM_RISCV_ISA_EXT_ZIHINTPAUSE), KVM_EXT_CFG("zbb", ext_zbb, KVM_RISCV_ISA_EXT_ZBB), KVM_EXT_CFG("ssaia", ext_ssaia, KVM_RISCV_ISA_EXT_SSAIA), From 082412166044a96c69c174e262f282cc6d73f019 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Mon, 23 Oct 2023 12:39:26 -0300 Subject: [PATCH 546/974] target/riscv: add zihpm extension flag for TCG zihpm is the Hardware Performance Counters extension described in chapter 12 of the unprivileged spec. It describes support for 29 unprivileged performance counters, hpmcounter3-hpmcounter31. As with zicntr, QEMU already implements zihpm before it was even an extension. zihpm is also part of the RVA22 profile, so add it to QEMU to complement the future profile implementation. Default it to 'true' for all existing CPUs since it was always present in the code. As for disabling it, there is already code in place in target/riscv/csr.c in all predicates for these counters (ctr() and mctr()) that disables them if cpu->cfg.pmu_num is zero. Thus, setting cpu->cfg.pmu_num to zero if 'zihpm=false' is enough to disable the extension. Set cpu->pmu_avail_ctrs mask to zero as well since this is also checked to verify if the counters exist. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20231023153927.435083-4-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 3 +++ target/riscv/cpu_cfg.h | 1 + target/riscv/tcg/tcg-cpu.c | 13 +++++++++++++ 3 files changed, 17 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 69d64ec4ca..f40da4c661 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -85,6 +85,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zifencei, PRIV_VERSION_1_10_0, ext_zifencei), ISA_EXT_DATA_ENTRY(zihintntl, PRIV_VERSION_1_10_0, ext_zihintntl), ISA_EXT_DATA_ENTRY(zihintpause, PRIV_VERSION_1_10_0, ext_zihintpause), + ISA_EXT_DATA_ENTRY(zihpm, PRIV_VERSION_1_12_0, ext_zihpm), ISA_EXT_DATA_ENTRY(zmmul, PRIV_VERSION_1_12_0, ext_zmmul), ISA_EXT_DATA_ENTRY(zawrs, PRIV_VERSION_1_12_0, ext_zawrs), ISA_EXT_DATA_ENTRY(zfa, PRIV_VERSION_1_12_0, ext_zfa), @@ -1218,6 +1219,7 @@ static void riscv_cpu_init(Object *obj) * users disable them. */ RISCV_CPU(obj)->cfg.ext_zicntr = true; + RISCV_CPU(obj)->cfg.ext_zihpm = true; } typedef struct misa_ext_info { @@ -1308,6 +1310,7 @@ const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = { MULTI_EXT_CFG_BOOL("svpbmt", ext_svpbmt, false), MULTI_EXT_CFG_BOOL("zicntr", ext_zicntr, true), + MULTI_EXT_CFG_BOOL("zihpm", ext_zihpm, true), MULTI_EXT_CFG_BOOL("zba", ext_zba, true), MULTI_EXT_CFG_BOOL("zbb", ext_zbb, true), diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index 73fd4b3231..6eef4a51ea 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -69,6 +69,7 @@ struct RISCVCPUConfig { bool ext_zicond; bool ext_zihintntl; bool ext_zihintpause; + bool ext_zihpm; bool ext_smstateen; bool ext_sstc; bool ext_svadu; diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index a1e4ed2e24..093bda2e75 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -549,6 +549,19 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) cpu->cfg.ext_zicntr = false; } + if (cpu->cfg.ext_zihpm && !cpu->cfg.ext_zicsr) { + if (cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_zihpm))) { + error_setg(errp, "zihpm requires zicsr"); + return; + } + cpu->cfg.ext_zihpm = false; + } + + if (!cpu->cfg.ext_zihpm) { + cpu->cfg.pmu_num = 0; + cpu->pmu_avail_ctrs = 0; + } + /* * Disable isa extensions based on priv spec after we * validated and set everything we need. From b4ceb3f2f37bffab379c8aa531730d6ec31b9930 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Mon, 23 Oct 2023 12:39:27 -0300 Subject: [PATCH 547/974] target/riscv/kvm: add zihpm reg Add zihpm support in the KVM driver now that QEMU supports it. This reg was added in Linux 6.6. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20231023153927.435083-5-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/kvm/kvm-cpu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 0c5c0e957b..a11c0e4a99 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -230,6 +230,7 @@ static KVMCPUConfig kvm_multi_ext_cfgs[] = { KVM_EXT_CFG("zicboz", ext_zicboz, KVM_RISCV_ISA_EXT_ZICBOZ), KVM_EXT_CFG("zicntr", ext_zicntr, KVM_RISCV_ISA_EXT_ZICNTR), KVM_EXT_CFG("zihintpause", ext_zihintpause, KVM_RISCV_ISA_EXT_ZIHINTPAUSE), + KVM_EXT_CFG("zihpm", ext_zihpm, KVM_RISCV_ISA_EXT_ZIHPM), KVM_EXT_CFG("zbb", ext_zbb, KVM_RISCV_ISA_EXT_ZBB), KVM_EXT_CFG("ssaia", ext_ssaia, KVM_RISCV_ISA_EXT_SSAIA), KVM_EXT_CFG("sstc", ext_sstc, KVM_RISCV_ISA_EXT_SSTC), From 672ec6061f13af21cbfa560ab2c0eea1a600d950 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 31 Oct 2023 17:51:50 -0300 Subject: [PATCH 548/974] target/riscv/kvm: add zicsr, zifencei, zba, zbs, svnapot These regs were added in Linux 6.6. Signed-off-by: Daniel Henrique Barboza Acked-by: Alistair Francis Message-ID: <20231031205150.208405-1-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/kvm/kvm-cpu.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index a11c0e4a99..78fa1fa162 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -229,12 +229,17 @@ static KVMCPUConfig kvm_multi_ext_cfgs[] = { KVM_EXT_CFG("zicbom", ext_zicbom, KVM_RISCV_ISA_EXT_ZICBOM), KVM_EXT_CFG("zicboz", ext_zicboz, KVM_RISCV_ISA_EXT_ZICBOZ), KVM_EXT_CFG("zicntr", ext_zicntr, KVM_RISCV_ISA_EXT_ZICNTR), + KVM_EXT_CFG("zicsr", ext_zicsr, KVM_RISCV_ISA_EXT_ZICSR), + KVM_EXT_CFG("zifencei", ext_zifencei, KVM_RISCV_ISA_EXT_ZIFENCEI), KVM_EXT_CFG("zihintpause", ext_zihintpause, KVM_RISCV_ISA_EXT_ZIHINTPAUSE), KVM_EXT_CFG("zihpm", ext_zihpm, KVM_RISCV_ISA_EXT_ZIHPM), + KVM_EXT_CFG("zba", ext_zba, KVM_RISCV_ISA_EXT_ZBA), KVM_EXT_CFG("zbb", ext_zbb, KVM_RISCV_ISA_EXT_ZBB), + KVM_EXT_CFG("zbs", ext_zbs, KVM_RISCV_ISA_EXT_ZBS), KVM_EXT_CFG("ssaia", ext_ssaia, KVM_RISCV_ISA_EXT_SSAIA), KVM_EXT_CFG("sstc", ext_sstc, KVM_RISCV_ISA_EXT_SSTC), KVM_EXT_CFG("svinval", ext_svinval, KVM_RISCV_ISA_EXT_SVINVAL), + KVM_EXT_CFG("svnapot", ext_svnapot, KVM_RISCV_ISA_EXT_SVNAPOT), KVM_EXT_CFG("svpbmt", ext_svpbmt, KVM_RISCV_ISA_EXT_SVPBMT), }; From 2f32dcabc2f0dfe7ee8c604dafb8ef450d78f452 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Mon, 30 Oct 2023 12:21:05 +0200 Subject: [PATCH 549/974] target/riscv: correct csr_ops[CSR_MSECCFG] The CSR register mseccfg is used by multiple extensions: Smepm and Zkr. Consider this when checking the existence of the register. Fixes: 77442380ecbe ("target/riscv: rvk: add CSR support for Zkr") Signed-off-by: Heinrich Schuchardt Reviewed-by: Alistair Francis Message-ID: <20231030102105.19501-1-heinrich.schuchardt@canonical.com> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 4ca96ddd1d..fc26b52c88 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -528,11 +528,14 @@ static RISCVException pmp(CPURISCVState *env, int csrno) return RISCV_EXCP_ILLEGAL_INST; } -static RISCVException smepmp(CPURISCVState *env, int csrno) +static RISCVException have_mseccfg(CPURISCVState *env, int csrno) { if (riscv_cpu_cfg(env)->ext_smepmp) { return RISCV_EXCP_NONE; } + if (riscv_cpu_cfg(env)->ext_zkr) { + return RISCV_EXCP_NONE; + } return RISCV_EXCP_ILLEGAL_INST; } @@ -4766,7 +4769,7 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_VSIPH] = { "vsiph", aia_hmode32, NULL, NULL, rmw_vsiph }, /* Physical Memory Protection */ - [CSR_MSECCFG] = { "mseccfg", smepmp, read_mseccfg, write_mseccfg, + [CSR_MSECCFG] = { "mseccfg", have_mseccfg, read_mseccfg, write_mseccfg, .min_priv_ver = PRIV_VERSION_1_11_0 }, [CSR_PMPCFG0] = { "pmpcfg0", pmp, read_pmpcfg, write_pmpcfg }, [CSR_PMPCFG1] = { "pmpcfg1", pmp, read_pmpcfg, write_pmpcfg }, From c0ce1f2a88c7fb7c923e1764d7af53f4cc815486 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Mon, 30 Oct 2023 16:16:07 +0800 Subject: [PATCH 550/974] MAINTAINERS: update mail address for Weiwei Li MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit My Iscas mail account will be disabled soon, change to my personal gmail account. Signed-off-by: Weiwei Li Reviewed-by: Bin Meng Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: LIU Zhiwei Reviewed-by: Alistair Francis Message-ID: <20231030081607.115118-2-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 8e8a7d5be5..1de7f381e8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -323,7 +323,7 @@ RISC-V TCG CPUs M: Palmer Dabbelt M: Alistair Francis M: Bin Meng -R: Weiwei Li +R: Weiwei Li R: Daniel Henrique Barboza R: Liu Zhiwei L: qemu-riscv@nongnu.org From 5ddbc83ff2f830f2e96c933d78ccf59c1507eab6 Mon Sep 17 00:00:00 2001 From: Max Chou Date: Thu, 26 Oct 2023 23:18:08 +0800 Subject: [PATCH 551/974] target/riscv: Add cfg property for Zvkt extension Vector crypto spec defines the Zvkt extension that included all of the instructions of Zvbb & Zvbc extensions and some vector instructions. Signed-off-by: Max Chou Acked-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-ID: <20231026151828.754279-2-max.chou@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu_cfg.h | 1 + target/riscv/tcg/tcg-cpu.c | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index 6eef4a51ea..1f0dac5c8a 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -96,6 +96,7 @@ struct RISCVCPUConfig { bool ext_zvknhb; bool ext_zvksed; bool ext_zvksh; + bool ext_zvkt; bool ext_zmmul; bool ext_zvfbfmin; bool ext_zvfbfwma; diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 093bda2e75..87baae56a1 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -499,6 +499,11 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) return; } + if (cpu->cfg.ext_zvkt) { + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvbb), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvbc), true); + } + /* * In principle Zve*x would also suffice here, were they supported * in qemu From 1c32b6306648ad46002a0ec13ec6719f5e9e82cb Mon Sep 17 00:00:00 2001 From: Max Chou Date: Thu, 26 Oct 2023 23:18:09 +0800 Subject: [PATCH 552/974] target/riscv: Expose Zvkt extension property Signed-off-by: Max Chou Acked-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-ID: <20231026151828.754279-3-max.chou@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index f40da4c661..9604ba6171 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -133,6 +133,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zvknhb, PRIV_VERSION_1_12_0, ext_zvknhb), ISA_EXT_DATA_ENTRY(zvksed, PRIV_VERSION_1_12_0, ext_zvksed), ISA_EXT_DATA_ENTRY(zvksh, PRIV_VERSION_1_12_0, ext_zvksh), + ISA_EXT_DATA_ENTRY(zvkt, PRIV_VERSION_1_12_0, ext_zvkt), ISA_EXT_DATA_ENTRY(zhinx, PRIV_VERSION_1_12_0, ext_zhinx), ISA_EXT_DATA_ENTRY(zhinxmin, PRIV_VERSION_1_12_0, ext_zhinxmin), ISA_EXT_DATA_ENTRY(smaia, PRIV_VERSION_1_12_0, ext_smaia), @@ -1390,6 +1391,7 @@ const RISCVCPUMultiExtConfig riscv_cpu_experimental_exts[] = { MULTI_EXT_CFG_BOOL("x-zvknhb", ext_zvknhb, false), MULTI_EXT_CFG_BOOL("x-zvksed", ext_zvksed, false), MULTI_EXT_CFG_BOOL("x-zvksh", ext_zvksh, false), + MULTI_EXT_CFG_BOOL("x-zvkt", ext_zvkt, false), DEFINE_PROP_END_OF_LIST(), }; From 389b2e70141fda7009bcd7c2755fd84176d543b1 Mon Sep 17 00:00:00 2001 From: Max Chou Date: Thu, 26 Oct 2023 23:18:10 +0800 Subject: [PATCH 553/974] target/riscv: Add cfg property for Zvkb extension After vector crypto spec v1.0.0-rc3 release, the Zvkb extension is defined as a proper subset of the Zvbb extension. And both the Zvkn and Zvks shorthand extensions replace the included Zvbb extension by Zvkb extnesion. Signed-off-by: Max Chou Acked-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-ID: <20231026151828.754279-4-max.chou@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu_cfg.h | 1 + target/riscv/tcg/tcg-cpu.c | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index 1f0dac5c8a..c4b2fec024 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -90,6 +90,7 @@ struct RISCVCPUConfig { bool ext_zve64d; bool ext_zvbb; bool ext_zvbc; + bool ext_zvkb; bool ext_zvkg; bool ext_zvkned; bool ext_zvknha; diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 87baae56a1..ad7a183a74 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -508,9 +508,9 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) * In principle Zve*x would also suffice here, were they supported * in qemu */ - if ((cpu->cfg.ext_zvbb || cpu->cfg.ext_zvkg || cpu->cfg.ext_zvkned || - cpu->cfg.ext_zvknha || cpu->cfg.ext_zvksed || cpu->cfg.ext_zvksh) && - !cpu->cfg.ext_zve32f) { + if ((cpu->cfg.ext_zvbb || cpu->cfg.ext_zvkb || cpu->cfg.ext_zvkg || + cpu->cfg.ext_zvkned || cpu->cfg.ext_zvknha || cpu->cfg.ext_zvksed || + cpu->cfg.ext_zvksh) && !cpu->cfg.ext_zve32f) { error_setg(errp, "Vector crypto extensions require V or Zve* extensions"); return; From 1db699f8c2a4c42def5a1ad2d5b48371d28b6278 Mon Sep 17 00:00:00 2001 From: Max Chou Date: Thu, 26 Oct 2023 23:18:11 +0800 Subject: [PATCH 554/974] target/riscv: Replace Zvbb checking by Zvkb The Zvkb extension is a proper subset of the Zvbb extension and includes following instructions: * vandn.[vv,vx] * vbrev8.v * vrev8.v * vrol.[vv,vx] * vror.[vv,vx,vi] Signed-off-by: Max Chou Reviewed-by: Daniel Henrique Barboza Message-ID: <20231026151828.754279-5-max.chou@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvvk.c.inc | 37 +++++++++++++++--------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/target/riscv/insn_trans/trans_rvvk.c.inc b/target/riscv/insn_trans/trans_rvvk.c.inc index e691519ed7..3801c16829 100644 --- a/target/riscv/insn_trans/trans_rvvk.c.inc +++ b/target/riscv/insn_trans/trans_rvvk.c.inc @@ -112,24 +112,27 @@ GEN_VX_MASKED_TRANS(vclmulh_vx, vclmul_vx_check) return false; \ } -static bool zvbb_vv_check(DisasContext *s, arg_rmrr *a) +static bool zvkb_vv_check(DisasContext *s, arg_rmrr *a) { - return opivv_check(s, a) && s->cfg_ptr->ext_zvbb == true; + return opivv_check(s, a) && + (s->cfg_ptr->ext_zvbb == true || s->cfg_ptr->ext_zvkb == true); } -static bool zvbb_vx_check(DisasContext *s, arg_rmrr *a) +static bool zvkb_vx_check(DisasContext *s, arg_rmrr *a) { - return opivx_check(s, a) && s->cfg_ptr->ext_zvbb == true; + return opivx_check(s, a) && + (s->cfg_ptr->ext_zvbb == true || s->cfg_ptr->ext_zvkb == true); } /* vrol.v[vx] */ -GEN_OPIVV_GVEC_TRANS_CHECK(vrol_vv, rotlv, zvbb_vv_check) -GEN_OPIVX_GVEC_SHIFT_TRANS_CHECK(vrol_vx, rotls, zvbb_vx_check) +GEN_OPIVV_GVEC_TRANS_CHECK(vrol_vv, rotlv, zvkb_vv_check) +GEN_OPIVX_GVEC_SHIFT_TRANS_CHECK(vrol_vx, rotls, zvkb_vx_check) /* vror.v[vxi] */ -GEN_OPIVV_GVEC_TRANS_CHECK(vror_vv, rotrv, zvbb_vv_check) -GEN_OPIVX_GVEC_SHIFT_TRANS_CHECK(vror_vx, rotrs, zvbb_vx_check) -GEN_OPIVI_GVEC_TRANS_CHECK(vror_vi, IMM_TRUNC_SEW, vror_vx, rotri, zvbb_vx_check) +GEN_OPIVV_GVEC_TRANS_CHECK(vror_vv, rotrv, zvkb_vv_check) +GEN_OPIVX_GVEC_SHIFT_TRANS_CHECK(vror_vx, rotrs, zvkb_vx_check) +GEN_OPIVI_GVEC_TRANS_CHECK(vror_vi, IMM_TRUNC_SEW, vror_vx, rotri, + zvkb_vx_check) #define GEN_OPIVX_GVEC_TRANS_CHECK(NAME, SUF, CHECK) \ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ @@ -147,8 +150,8 @@ GEN_OPIVI_GVEC_TRANS_CHECK(vror_vi, IMM_TRUNC_SEW, vror_vx, rotri, zvbb_vx_check } /* vandn.v[vx] */ -GEN_OPIVV_GVEC_TRANS_CHECK(vandn_vv, andc, zvbb_vv_check) -GEN_OPIVX_GVEC_TRANS_CHECK(vandn_vx, andcs, zvbb_vx_check) +GEN_OPIVV_GVEC_TRANS_CHECK(vandn_vv, andc, zvkb_vv_check) +GEN_OPIVX_GVEC_TRANS_CHECK(vandn_vx, andcs, zvkb_vx_check) #define GEN_OPIV_TRANS(NAME, CHECK) \ static bool trans_##NAME(DisasContext *s, arg_rmr *a) \ @@ -188,8 +191,16 @@ static bool zvbb_opiv_check(DisasContext *s, arg_rmr *a) vext_check_ss(s, a->rd, a->rs2, a->vm); } -GEN_OPIV_TRANS(vbrev8_v, zvbb_opiv_check) -GEN_OPIV_TRANS(vrev8_v, zvbb_opiv_check) +static bool zvkb_opiv_check(DisasContext *s, arg_rmr *a) +{ + return (s->cfg_ptr->ext_zvbb == true || s->cfg_ptr->ext_zvkb == true) && + require_rvv(s) && + vext_check_isa_ill(s) && + vext_check_ss(s, a->rd, a->rs2, a->vm); +} + +GEN_OPIV_TRANS(vbrev8_v, zvkb_opiv_check) +GEN_OPIV_TRANS(vrev8_v, zvkb_opiv_check) GEN_OPIV_TRANS(vbrev_v, zvbb_opiv_check) GEN_OPIV_TRANS(vclz_v, zvbb_opiv_check) GEN_OPIV_TRANS(vctz_v, zvbb_opiv_check) From f209cb0a83cc57bb172bff4b862932a408d46b58 Mon Sep 17 00:00:00 2001 From: Max Chou Date: Thu, 26 Oct 2023 23:18:12 +0800 Subject: [PATCH 555/974] target/riscv: Expose Zvkb extension property Signed-off-by: Max Chou Acked-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-ID: <20231026151828.754279-6-max.chou@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 9604ba6171..3eedf8b7ce 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -127,6 +127,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zvfbfwma, PRIV_VERSION_1_12_0, ext_zvfbfwma), ISA_EXT_DATA_ENTRY(zvfh, PRIV_VERSION_1_12_0, ext_zvfh), ISA_EXT_DATA_ENTRY(zvfhmin, PRIV_VERSION_1_12_0, ext_zvfhmin), + ISA_EXT_DATA_ENTRY(zvkb, PRIV_VERSION_1_12_0, ext_zvkb), ISA_EXT_DATA_ENTRY(zvkg, PRIV_VERSION_1_12_0, ext_zvkg), ISA_EXT_DATA_ENTRY(zvkned, PRIV_VERSION_1_12_0, ext_zvkned), ISA_EXT_DATA_ENTRY(zvknha, PRIV_VERSION_1_12_0, ext_zvknha), @@ -1385,6 +1386,7 @@ const RISCVCPUMultiExtConfig riscv_cpu_experimental_exts[] = { /* Vector cryptography extensions */ MULTI_EXT_CFG_BOOL("x-zvbb", ext_zvbb, false), MULTI_EXT_CFG_BOOL("x-zvbc", ext_zvbc, false), + MULTI_EXT_CFG_BOOL("x-zvkb", ext_zvkg, false), MULTI_EXT_CFG_BOOL("x-zvkg", ext_zvkg, false), MULTI_EXT_CFG_BOOL("x-zvkned", ext_zvkned, false), MULTI_EXT_CFG_BOOL("x-zvknha", ext_zvknha, false), From 7cdc8ddb080863c7883762961e5012f08487ce98 Mon Sep 17 00:00:00 2001 From: Max Chou Date: Thu, 26 Oct 2023 23:18:13 +0800 Subject: [PATCH 556/974] target/riscv: Add cfg properties for Zvkn[c|g] extensions Vector crypto spec defines the NIST algorithm suite related extensions (Zvkn, Zvknc, Zvkng) combined by several vector crypto extensions. Signed-off-by: Max Chou Acked-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-ID: <20231026151828.754279-7-max.chou@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu_cfg.h | 3 +++ target/riscv/tcg/tcg-cpu.c | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index c4b2fec024..08733002a7 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -98,6 +98,9 @@ struct RISCVCPUConfig { bool ext_zvksed; bool ext_zvksh; bool ext_zvkt; + bool ext_zvkn; + bool ext_zvknc; + bool ext_zvkng; bool ext_zmmul; bool ext_zvfbfmin; bool ext_zvfbfwma; diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index ad7a183a74..9540d1df4e 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -499,6 +499,26 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) return; } + /* + * Shorthand vector crypto extensions + */ + if (cpu->cfg.ext_zvknc) { + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvkn), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvbc), true); + } + + if (cpu->cfg.ext_zvkng) { + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvkn), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvkg), true); + } + + if (cpu->cfg.ext_zvkn) { + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvkned), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvknhb), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvkb), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvkt), true); + } + if (cpu->cfg.ext_zvkt) { cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvbb), true); cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvbc), true); From 23aaefb9c9900c385c74b3490e60da2040f8200e Mon Sep 17 00:00:00 2001 From: Max Chou Date: Thu, 26 Oct 2023 23:18:14 +0800 Subject: [PATCH 557/974] target/riscv: Expose Zvkn[c|g] extnesion properties Expose the properties of NIST Algorithm Suite related extensions (Zvkn, Zvknc, Zvkng). Signed-off-by: Max Chou Acked-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-ID: <20231026151828.754279-8-max.chou@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 3eedf8b7ce..208faffbbf 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -129,7 +129,10 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zvfhmin, PRIV_VERSION_1_12_0, ext_zvfhmin), ISA_EXT_DATA_ENTRY(zvkb, PRIV_VERSION_1_12_0, ext_zvkb), ISA_EXT_DATA_ENTRY(zvkg, PRIV_VERSION_1_12_0, ext_zvkg), + ISA_EXT_DATA_ENTRY(zvkn, PRIV_VERSION_1_12_0, ext_zvkn), + ISA_EXT_DATA_ENTRY(zvknc, PRIV_VERSION_1_12_0, ext_zvknc), ISA_EXT_DATA_ENTRY(zvkned, PRIV_VERSION_1_12_0, ext_zvkned), + ISA_EXT_DATA_ENTRY(zvkng, PRIV_VERSION_1_12_0, ext_zvkng), ISA_EXT_DATA_ENTRY(zvknha, PRIV_VERSION_1_12_0, ext_zvknha), ISA_EXT_DATA_ENTRY(zvknhb, PRIV_VERSION_1_12_0, ext_zvknhb), ISA_EXT_DATA_ENTRY(zvksed, PRIV_VERSION_1_12_0, ext_zvksed), @@ -1394,6 +1397,9 @@ const RISCVCPUMultiExtConfig riscv_cpu_experimental_exts[] = { MULTI_EXT_CFG_BOOL("x-zvksed", ext_zvksed, false), MULTI_EXT_CFG_BOOL("x-zvksh", ext_zvksh, false), MULTI_EXT_CFG_BOOL("x-zvkt", ext_zvkt, false), + MULTI_EXT_CFG_BOOL("x-zvkn", ext_zvkn, false), + MULTI_EXT_CFG_BOOL("x-zvknc", ext_zvknc, false), + MULTI_EXT_CFG_BOOL("x-zvkng", ext_zvkng, false), DEFINE_PROP_END_OF_LIST(), }; From 8f913d1004766c8ee96bcab78a6c39764aa5ee52 Mon Sep 17 00:00:00 2001 From: Max Chou Date: Thu, 26 Oct 2023 23:18:15 +0800 Subject: [PATCH 558/974] target/riscv: Add cfg properties for Zvks[c|g] extensions Vector crypto spec defines the ShangMi algorithm suite related extensions (Zvks, Zvksc, Zvksg) combined by several vector crypto extensions. Signed-off-by: Max Chou Acked-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-ID: <20231026151828.754279-9-max.chou@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu_cfg.h | 3 +++ target/riscv/tcg/tcg-cpu.c | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index 08733002a7..634ff673b3 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -101,6 +101,9 @@ struct RISCVCPUConfig { bool ext_zvkn; bool ext_zvknc; bool ext_zvkng; + bool ext_zvks; + bool ext_zvksc; + bool ext_zvksg; bool ext_zmmul; bool ext_zvfbfmin; bool ext_zvfbfwma; diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 9540d1df4e..1a3351b142 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -519,6 +519,23 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvkt), true); } + if (cpu->cfg.ext_zvksc) { + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvks), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvbc), true); + } + + if (cpu->cfg.ext_zvksg) { + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvks), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvkg), true); + } + + if (cpu->cfg.ext_zvks) { + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvksed), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvksh), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvkb), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvkt), true); + } + if (cpu->cfg.ext_zvkt) { cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvbb), true); cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvbc), true); From b43419f2dc5a95f6861156ecff5654ad48fec010 Mon Sep 17 00:00:00 2001 From: Max Chou Date: Thu, 26 Oct 2023 23:18:16 +0800 Subject: [PATCH 559/974] target/riscv: Expose Zvks[c|g] extnesion properties Expose the properties of ShangMi Algorithm Suite related extensions (Zvks, Zvksc, Zvksg). Signed-off-by: Max Chou Acked-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-ID: <20231026151828.754279-10-max.chou@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 208faffbbf..f61ed7cf60 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -135,7 +135,10 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zvkng, PRIV_VERSION_1_12_0, ext_zvkng), ISA_EXT_DATA_ENTRY(zvknha, PRIV_VERSION_1_12_0, ext_zvknha), ISA_EXT_DATA_ENTRY(zvknhb, PRIV_VERSION_1_12_0, ext_zvknhb), + ISA_EXT_DATA_ENTRY(zvks, PRIV_VERSION_1_12_0, ext_zvks), + ISA_EXT_DATA_ENTRY(zvksc, PRIV_VERSION_1_12_0, ext_zvksc), ISA_EXT_DATA_ENTRY(zvksed, PRIV_VERSION_1_12_0, ext_zvksed), + ISA_EXT_DATA_ENTRY(zvksg, PRIV_VERSION_1_12_0, ext_zvksg), ISA_EXT_DATA_ENTRY(zvksh, PRIV_VERSION_1_12_0, ext_zvksh), ISA_EXT_DATA_ENTRY(zvkt, PRIV_VERSION_1_12_0, ext_zvkt), ISA_EXT_DATA_ENTRY(zhinx, PRIV_VERSION_1_12_0, ext_zhinx), @@ -1400,6 +1403,9 @@ const RISCVCPUMultiExtConfig riscv_cpu_experimental_exts[] = { MULTI_EXT_CFG_BOOL("x-zvkn", ext_zvkn, false), MULTI_EXT_CFG_BOOL("x-zvknc", ext_zvknc, false), MULTI_EXT_CFG_BOOL("x-zvkng", ext_zvkng, false), + MULTI_EXT_CFG_BOOL("x-zvks", ext_zvks, false), + MULTI_EXT_CFG_BOOL("x-zvksc", ext_zvksc, false), + MULTI_EXT_CFG_BOOL("x-zvksg", ext_zvksg, false), DEFINE_PROP_END_OF_LIST(), }; From ea61ef7097d05b55cc0ff3e8826eea7fbbe0c0a9 Mon Sep 17 00:00:00 2001 From: Max Chou Date: Thu, 26 Oct 2023 23:18:17 +0800 Subject: [PATCH 560/974] target/riscv: Move vector crypto extensions to riscv_cpu_extensions Because the vector crypto specification is ratified, so move theses extensions from riscv_cpu_experimental_exts to riscv_cpu_extensions. Signed-off-by: Max Chou Acked-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-ID: <20231026151828.754279-11-max.chou@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index f61ed7cf60..d73e1da2a2 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1357,6 +1357,24 @@ const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = { MULTI_EXT_CFG_BOOL("zcmt", ext_zcmt, false), MULTI_EXT_CFG_BOOL("zicond", ext_zicond, false), + /* Vector cryptography extensions */ + MULTI_EXT_CFG_BOOL("zvbb", ext_zvbb, false), + MULTI_EXT_CFG_BOOL("zvbc", ext_zvbc, false), + MULTI_EXT_CFG_BOOL("zvkb", ext_zvkg, false), + MULTI_EXT_CFG_BOOL("zvkg", ext_zvkg, false), + MULTI_EXT_CFG_BOOL("zvkned", ext_zvkned, false), + MULTI_EXT_CFG_BOOL("zvknha", ext_zvknha, false), + MULTI_EXT_CFG_BOOL("zvknhb", ext_zvknhb, false), + MULTI_EXT_CFG_BOOL("zvksed", ext_zvksed, false), + MULTI_EXT_CFG_BOOL("zvksh", ext_zvksh, false), + MULTI_EXT_CFG_BOOL("zvkt", ext_zvkt, false), + MULTI_EXT_CFG_BOOL("zvkn", ext_zvkn, false), + MULTI_EXT_CFG_BOOL("zvknc", ext_zvknc, false), + MULTI_EXT_CFG_BOOL("zvkng", ext_zvkng, false), + MULTI_EXT_CFG_BOOL("zvks", ext_zvks, false), + MULTI_EXT_CFG_BOOL("zvksc", ext_zvksc, false), + MULTI_EXT_CFG_BOOL("zvksg", ext_zvksg, false), + DEFINE_PROP_END_OF_LIST(), }; @@ -1389,24 +1407,6 @@ const RISCVCPUMultiExtConfig riscv_cpu_experimental_exts[] = { MULTI_EXT_CFG_BOOL("x-zvfbfmin", ext_zvfbfmin, false), MULTI_EXT_CFG_BOOL("x-zvfbfwma", ext_zvfbfwma, false), - /* Vector cryptography extensions */ - MULTI_EXT_CFG_BOOL("x-zvbb", ext_zvbb, false), - MULTI_EXT_CFG_BOOL("x-zvbc", ext_zvbc, false), - MULTI_EXT_CFG_BOOL("x-zvkb", ext_zvkg, false), - MULTI_EXT_CFG_BOOL("x-zvkg", ext_zvkg, false), - MULTI_EXT_CFG_BOOL("x-zvkned", ext_zvkned, false), - MULTI_EXT_CFG_BOOL("x-zvknha", ext_zvknha, false), - MULTI_EXT_CFG_BOOL("x-zvknhb", ext_zvknhb, false), - MULTI_EXT_CFG_BOOL("x-zvksed", ext_zvksed, false), - MULTI_EXT_CFG_BOOL("x-zvksh", ext_zvksh, false), - MULTI_EXT_CFG_BOOL("x-zvkt", ext_zvkt, false), - MULTI_EXT_CFG_BOOL("x-zvkn", ext_zvkn, false), - MULTI_EXT_CFG_BOOL("x-zvknc", ext_zvknc, false), - MULTI_EXT_CFG_BOOL("x-zvkng", ext_zvkng, false), - MULTI_EXT_CFG_BOOL("x-zvks", ext_zvks, false), - MULTI_EXT_CFG_BOOL("x-zvksc", ext_zvksc, false), - MULTI_EXT_CFG_BOOL("x-zvksg", ext_zvksg, false), - DEFINE_PROP_END_OF_LIST(), }; From ea363626ff65b2b8e6c590812f89546d5779612f Mon Sep 17 00:00:00 2001 From: Max Chou Date: Thu, 26 Oct 2023 23:18:18 +0800 Subject: [PATCH 561/974] disas/riscv: Add rv_fmt_vd_vs2_uimm format Add rv_fmt_vd_vs2_uimm format for vector crypto instructions. Signed-off-by: Max Chou Acked-by: Alistair Francis Message-ID: <20231026151828.754279-12-max.chou@sifive.com> Signed-off-by: Alistair Francis --- disas/riscv.h | 1 + 1 file changed, 1 insertion(+) diff --git a/disas/riscv.h b/disas/riscv.h index 8abb578b51..b242d73b25 100644 --- a/disas/riscv.h +++ b/disas/riscv.h @@ -274,6 +274,7 @@ enum { #define rv_fmt_vd_vs2_fs1_vm "O\tD,F,4m" #define rv_fmt_vd_vs2_imm_vl "O\tD,F,il" #define rv_fmt_vd_vs2_imm_vm "O\tD,F,im" +#define rv_fmt_vd_vs2_uimm "O\tD,F,u" #define rv_fmt_vd_vs2_uimm_vm "O\tD,F,um" #define rv_fmt_vd_vs1_vs2_vm "O\tD,E,Fm" #define rv_fmt_vd_rs1_vs2_vm "O\tD,1,Fm" From 434c609bef445e0dd13d514c5b12f8e47a73cd1d Mon Sep 17 00:00:00 2001 From: Max Chou Date: Thu, 26 Oct 2023 23:18:19 +0800 Subject: [PATCH 562/974] disas/riscv: Add rv_codec_vror_vi for vror.vi Add rv_codec_vror_vi for the vector crypto instruction - vror.vi. The rotate amount of vror.vi is defined by combining seperated bits. Signed-off-by: Max Chou Acked-by: Alistair Francis Message-ID: <20231026151828.754279-13-max.chou@sifive.com> Signed-off-by: Alistair Francis --- disas/riscv.c | 14 +++++++++++++- disas/riscv.h | 1 + 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/disas/riscv.c b/disas/riscv.c index 8e89e1d115..ec33e447f5 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -4011,6 +4011,12 @@ static uint32_t operand_vzimm10(rv_inst inst) return (inst << 34) >> 54; } +static uint32_t operand_vzimm6(rv_inst inst) +{ + return ((inst << 37) >> 63) << 5 | + ((inst << 44) >> 59); +} + static uint32_t operand_bs(rv_inst inst) { return (inst << 32) >> 62; @@ -4393,6 +4399,12 @@ static void decode_inst_operands(rv_decode *dec, rv_isa isa) dec->imm = operand_vimm(inst); dec->vm = operand_vm(inst); break; + case rv_codec_vror_vi: + dec->rd = operand_rd(inst); + dec->rs2 = operand_rs2(inst); + dec->imm = operand_vzimm6(inst); + dec->vm = operand_vm(inst); + break; case rv_codec_vsetvli: dec->rd = operand_rd(inst); dec->rs1 = operand_rs1(inst); @@ -4677,7 +4689,7 @@ static void format_inst(char *buf, size_t buflen, size_t tab, rv_decode *dec) append(buf, tmp, buflen); break; case 'u': - snprintf(tmp, sizeof(tmp), "%u", ((uint32_t)dec->imm & 0b11111)); + snprintf(tmp, sizeof(tmp), "%u", ((uint32_t)dec->imm & 0b111111)); append(buf, tmp, buflen); break; case 'j': diff --git a/disas/riscv.h b/disas/riscv.h index b242d73b25..19e5ed2ce6 100644 --- a/disas/riscv.h +++ b/disas/riscv.h @@ -152,6 +152,7 @@ typedef enum { rv_codec_v_i, rv_codec_vsetvli, rv_codec_vsetivli, + rv_codec_vror_vi, rv_codec_zcb_ext, rv_codec_zcb_mul, rv_codec_zcb_lb, From 9d92f56d4a44a14ec099e9af5148c4c9c85fd59e Mon Sep 17 00:00:00 2001 From: Max Chou Date: Thu, 26 Oct 2023 23:18:20 +0800 Subject: [PATCH 563/974] disas/riscv: Add support for vector crypto extensions This patch adds following v1.0.0 ratified vector crypto extensions support to the RISC-V disassembler. - Zvbb - Zvbc - Zvkb - Zvkg - Zvkned - Zvknha - Zvknhb - Zvksed - Zvksh Signed-off-by: Max Chou Message-ID: <20231026151828.754279-14-max.chou@sifive.com> Signed-off-by: Alistair Francis --- disas/riscv.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) diff --git a/disas/riscv.c b/disas/riscv.c index ec33e447f5..7ea6ea050e 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -862,6 +862,47 @@ typedef enum { rv_op_fltq_q = 831, rv_op_fleq_h = 832, rv_op_fltq_h = 833, + rv_op_vaesdf_vv = 834, + rv_op_vaesdf_vs = 835, + rv_op_vaesdm_vv = 836, + rv_op_vaesdm_vs = 837, + rv_op_vaesef_vv = 838, + rv_op_vaesef_vs = 839, + rv_op_vaesem_vv = 840, + rv_op_vaesem_vs = 841, + rv_op_vaeskf1_vi = 842, + rv_op_vaeskf2_vi = 843, + rv_op_vaesz_vs = 844, + rv_op_vandn_vv = 845, + rv_op_vandn_vx = 846, + rv_op_vbrev_v = 847, + rv_op_vbrev8_v = 848, + rv_op_vclmul_vv = 849, + rv_op_vclmul_vx = 850, + rv_op_vclmulh_vv = 851, + rv_op_vclmulh_vx = 852, + rv_op_vclz_v = 853, + rv_op_vcpop_v = 854, + rv_op_vctz_v = 855, + rv_op_vghsh_vv = 856, + rv_op_vgmul_vv = 857, + rv_op_vrev8_v = 858, + rv_op_vrol_vv = 859, + rv_op_vrol_vx = 860, + rv_op_vror_vv = 861, + rv_op_vror_vx = 862, + rv_op_vror_vi = 863, + rv_op_vsha2ch_vv = 864, + rv_op_vsha2cl_vv = 865, + rv_op_vsha2ms_vv = 866, + rv_op_vsm3c_vi = 867, + rv_op_vsm3me_vv = 868, + rv_op_vsm4k_vi = 869, + rv_op_vsm4r_vv = 870, + rv_op_vsm4r_vs = 871, + rv_op_vwsll_vv = 872, + rv_op_vwsll_vx = 873, + rv_op_vwsll_vi = 874, } rv_op; /* register names */ @@ -2008,6 +2049,47 @@ const rv_opcode_data rvi_opcode_data[] = { { "fltq.q", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, { "fleq.h", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, { "fltq.h", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, + { "vaesdf.vv", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vaesdf.vs", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vaesdm.vv", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vaesdm.vs", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vaesef.vv", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vaesef.vs", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vaesem.vv", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vaesem.vs", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vaeskf1.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm, NULL, 0, 0, 0 }, + { "vaeskf2.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm, NULL, 0, 0, 0 }, + { "vaesz.vs", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vandn.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vandn.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vbrev.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vbrev8.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vclmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vclmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vclmulh.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vclmulh.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vclz.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vcpop.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vctz.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vghsh.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, 0, 0, 0 }, + { "vgmul.vv", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vrev8.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vrol.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vrol.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vror.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vror.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vror.vi", rv_codec_vror_vi, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vsha2ch.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, 0, 0, 0 }, + { "vsha2cl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, 0, 0, 0 }, + { "vsha2ms.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, 0, 0, 0 }, + { "vsm3c.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm, NULL, 0, 0, 0 }, + { "vsm3me.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, 0, 0, 0 }, + { "vsm4k.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm, NULL, 0, 0, 0 }, + { "vsm4r.vv", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vsm4r.vs", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vwsll.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vwsll.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vwsll.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, }; /* CSR names */ @@ -3176,6 +3258,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 0: switch ((inst >> 26) & 0b111111) { case 0: op = rv_op_vadd_vv; break; + case 1: op = rv_op_vandn_vv; break; case 2: op = rv_op_vsub_vv; break; case 4: op = rv_op_vminu_vv; break; case 5: op = rv_op_vmin_vv; break; @@ -3198,6 +3281,8 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 19: op = rv_op_vmsbc_vvm; break; + case 20: op = rv_op_vror_vv; break; + case 21: op = rv_op_vrol_vv; break; case 23: if (((inst >> 20) & 0b111111) == 32) op = rv_op_vmv_v_v; @@ -3226,6 +3311,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 47: op = rv_op_vnclip_wv; break; case 48: op = rv_op_vwredsumu_vs; break; case 49: op = rv_op_vwredsum_vs; break; + case 53: op = rv_op_vwsll_vv; break; } break; case 1: @@ -3323,6 +3409,8 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 9: op = rv_op_vaadd_vv; break; case 10: op = rv_op_vasubu_vv; break; case 11: op = rv_op_vasub_vv; break; + case 12: op = rv_op_vclmul_vv; break; + case 13: op = rv_op_vclmulh_vv; break; case 16: switch ((inst >> 15) & 0b11111) { case 0: if ((inst >> 25) & 1) op = rv_op_vmv_x_s; break; @@ -3338,6 +3426,12 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 5: op = rv_op_vsext_vf4; break; case 6: op = rv_op_vzext_vf2; break; case 7: op = rv_op_vsext_vf2; break; + case 8: op = rv_op_vbrev8_v; break; + case 9: op = rv_op_vrev8_v; break; + case 10: op = rv_op_vbrev_v; break; + case 12: op = rv_op_vclz_v; break; + case 13: op = rv_op_vctz_v; break; + case 14: op = rv_op_vcpop_v; break; } break; case 20: @@ -3406,6 +3500,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 17: op = rv_op_vmadc_vim; break; + case 20: case 21: op = rv_op_vror_vi; break; case 23: if (((inst >> 20) & 0b111111) == 32) op = rv_op_vmv_v_i; @@ -3437,11 +3532,13 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 45: op = rv_op_vnsra_wi; break; case 46: op = rv_op_vnclipu_wi; break; case 47: op = rv_op_vnclip_wi; break; + case 53: op = rv_op_vwsll_vi; break; } break; case 4: switch ((inst >> 26) & 0b111111) { case 0: op = rv_op_vadd_vx; break; + case 1: op = rv_op_vandn_vx; break; case 2: op = rv_op_vsub_vx; break; case 3: op = rv_op_vrsub_vx; break; case 4: op = rv_op_vminu_vx; break; @@ -3466,6 +3563,8 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 19: op = rv_op_vmsbc_vxm; break; + case 20: op = rv_op_vror_vx; break; + case 21: op = rv_op_vrol_vx; break; case 23: if (((inst >> 20) & 0b111111) == 32) op = rv_op_vmv_v_x; @@ -3494,6 +3593,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 45: op = rv_op_vnsra_wx; break; case 46: op = rv_op_vnclipu_wx; break; case 47: op = rv_op_vnclip_wx; break; + case 53: op = rv_op_vwsll_vx; break; } break; case 5: @@ -3554,6 +3654,8 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 9: op = rv_op_vaadd_vx; break; case 10: op = rv_op_vasubu_vx; break; case 11: op = rv_op_vasub_vx; break; + case 12: op = rv_op_vclmul_vx; break; + case 13: op = rv_op_vclmulh_vx; break; case 14: op = rv_op_vslide1up_vx; break; case 15: op = rv_op_vslide1down_vx; break; case 16: @@ -3686,6 +3788,41 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 7: op = rv_op_csrrci; break; } break; + case 29: + if (((inst >> 25) & 1) == 1 && ((inst >> 12) & 0b111) == 2) { + switch ((inst >> 26) & 0b111111) { + case 32: op = rv_op_vsm3me_vv; break; + case 33: op = rv_op_vsm4k_vi; break; + case 34: op = rv_op_vaeskf1_vi; break; + case 40: + switch ((inst >> 15) & 0b11111) { + case 0: op = rv_op_vaesdm_vv; break; + case 1: op = rv_op_vaesdf_vv; break; + case 2: op = rv_op_vaesem_vv; break; + case 3: op = rv_op_vaesef_vv; break; + case 16: op = rv_op_vsm4r_vv; break; + case 17: op = rv_op_vgmul_vv; break; + } + break; + case 41: + switch ((inst >> 15) & 0b11111) { + case 0: op = rv_op_vaesdm_vs; break; + case 1: op = rv_op_vaesdf_vs; break; + case 2: op = rv_op_vaesem_vs; break; + case 3: op = rv_op_vaesef_vs; break; + case 7: op = rv_op_vaesz_vs; break; + case 16: op = rv_op_vsm4r_vs; break; + } + break; + case 42: op = rv_op_vaeskf2_vi; break; + case 43: op = rv_op_vsm3c_vi; break; + case 44: op = rv_op_vghsh_vv; break; + case 45: op = rv_op_vsha2ms_vv; break; + case 46: op = rv_op_vsha2ch_vv; break; + case 47: op = rv_op_vsha2cl_vv; break; + } + } + break; case 30: switch (((inst >> 22) & 0b1111111000) | ((inst >> 12) & 0b0000000111)) { From 251385fd4480c0715f3d2c76a3c76534a42570fc Mon Sep 17 00:00:00 2001 From: Max Chou Date: Thu, 26 Oct 2023 23:18:21 +0800 Subject: [PATCH 564/974] disas/riscv: Replace TABs with space Replaces TABs with spaces, making sure to have a consistent coding style of 4 space indentations. Signed-off-by: Max Chou Acked-by: Alistair Francis Message-ID: <20231026151828.754279-15-max.chou@sifive.com> Signed-off-by: Alistair Francis --- disas/riscv.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 7ea6ea050e..e9458e574b 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -3136,12 +3136,12 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 89: - switch (((inst >> 12) & 0b111)) { + switch (((inst >> 12) & 0b111)) { case 0: op = rv_op_fmvp_d_x; break; } break; case 91: - switch (((inst >> 12) & 0b111)) { + switch (((inst >> 12) & 0b111)) { case 0: op = rv_op_fmvp_q_x; break; } break; @@ -4579,7 +4579,7 @@ static void decode_inst_operands(rv_decode *dec, rv_isa isa) break; case rv_codec_zcmt_jt: dec->imm = operand_tbl_index(inst); - break; + break; case rv_codec_fli: dec->rd = operand_rd(inst); dec->imm = operand_rs1(inst); From d53ead72066b1502bc3989dd11f1565d472e431d Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Thu, 2 Nov 2023 10:34:23 +1000 Subject: [PATCH 565/974] hw/ssi: ibex_spi_host: Clear the interrupt even if disabled We currently don't clear the interrupts if they are disabled. This means that if an interrupt occurs and the guest disables interrupts the QEMU IRQ will remain high. This doesn't immediately affect guests, but if the guest re-enables interrupts it's possible that we will miss an interrupt as it always remains set. Let's update the logic to always call qemu_set_irq() even if the interrupts are disabled to ensure we set the level low. The level will never be high unless interrupts are enabled, so we won't generate interrupts when we shouldn't. Signed-off-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-ID: <20231102003424.2003428-2-alistair.francis@wdc.com> Signed-off-by: Alistair Francis --- hw/ssi/ibex_spi_host.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/ssi/ibex_spi_host.c b/hw/ssi/ibex_spi_host.c index 1ee7d88c22..c300ec294d 100644 --- a/hw/ssi/ibex_spi_host.c +++ b/hw/ssi/ibex_spi_host.c @@ -205,9 +205,10 @@ static void ibex_spi_host_irq(IbexSPIHostState *s) if (err_irq) { s->regs[IBEX_SPI_HOST_INTR_STATE] |= R_INTR_STATE_ERROR_MASK; } - qemu_set_irq(s->host_err, err_irq); } + qemu_set_irq(s->host_err, err_irq); + /* Event IRQ Enabled and Event IRQ Cleared */ if (event_en && !status_pending) { if (FIELD_EX32(intr_test_reg, INTR_STATE, SPI_EVENT)) { @@ -229,8 +230,9 @@ static void ibex_spi_host_irq(IbexSPIHostState *s) if (event_irq) { s->regs[IBEX_SPI_HOST_INTR_STATE] |= R_INTR_STATE_SPI_EVENT_MASK; } - qemu_set_irq(s->event, event_irq); } + + qemu_set_irq(s->event, event_irq); } static void ibex_spi_host_transfer(IbexSPIHostState *s) From c541b07de79daa293e9ccc07f3c98f575ad09f2a Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Thu, 2 Nov 2023 10:34:24 +1000 Subject: [PATCH 566/974] target/riscv: cpu: Set the OpenTitan priv to 1.12.0 Set the Ibex CPU priv to 1.12.0 to ensure that smepmp/epmp is correctly enabled. Signed-off-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-ID: <20231102003424.2003428-3-alistair.francis@wdc.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index d73e1da2a2..70c0a78c6c 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -606,7 +606,7 @@ static void rv32_ibex_cpu_init(Object *obj) RISCVCPU *cpu = RISCV_CPU(obj); riscv_cpu_set_misa(env, MXL_RV32, RVI | RVM | RVC | RVU); - env->priv_ver = PRIV_VERSION_1_11_0; + env->priv_ver = PRIV_VERSION_1_12_0; #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(cpu, VM_1_10_MBARE); #endif From 755b41d09f516109f5ddc49aae86358c72e656d5 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Tue, 31 Oct 2023 15:37:13 +0000 Subject: [PATCH 567/974] target/riscv: Propagate error from PMU setup More closely follow the QEMU style by returning an Error and propagating it there is an error relating to the PMU setup. Further simplify the function by removing the num_counters parameter as this is available from the passed in cpu pointer. Signed-off-by: Rob Bradford Reviewed-by: Alistair Francis Reviewed-by: LIU Zhiwei Reviewed-by: Atish Patra Message-ID: <20231031154000.18134-2-rbradford@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/pmu.c | 19 +++++++++---------- target/riscv/pmu.h | 3 ++- target/riscv/tcg/tcg-cpu.c | 8 +++++++- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/target/riscv/pmu.c b/target/riscv/pmu.c index 36f6307d28..13801ccb78 100644 --- a/target/riscv/pmu.c +++ b/target/riscv/pmu.c @@ -434,22 +434,21 @@ int riscv_pmu_setup_timer(CPURISCVState *env, uint64_t value, uint32_t ctr_idx) } -int riscv_pmu_init(RISCVCPU *cpu, int num_counters) +void riscv_pmu_init(RISCVCPU *cpu, Error **errp) { - if (num_counters > (RV_MAX_MHPMCOUNTERS - 3)) { - return -1; + uint8_t pmu_num = cpu->cfg.pmu_num; + + if (pmu_num > (RV_MAX_MHPMCOUNTERS - 3)) { + error_setg(errp, "Number of counters exceeds maximum available"); + return; } cpu->pmu_event_ctr_map = g_hash_table_new(g_direct_hash, g_direct_equal); if (!cpu->pmu_event_ctr_map) { - /* PMU support can not be enabled */ - qemu_log_mask(LOG_UNIMP, "PMU events can't be supported\n"); - cpu->cfg.pmu_num = 0; - return -1; + error_setg(errp, "Unable to allocate PMU event hash table"); + return; } /* Create a bitmask of available programmable counters */ - cpu->pmu_avail_ctrs = MAKE_32BIT_MASK(3, num_counters); - - return 0; + cpu->pmu_avail_ctrs = MAKE_32BIT_MASK(3, pmu_num); } diff --git a/target/riscv/pmu.h b/target/riscv/pmu.h index 2bfb71ba87..88e0713296 100644 --- a/target/riscv/pmu.h +++ b/target/riscv/pmu.h @@ -17,13 +17,14 @@ */ #include "cpu.h" +#include "qapi/error.h" bool riscv_pmu_ctr_monitor_instructions(CPURISCVState *env, uint32_t target_ctr); bool riscv_pmu_ctr_monitor_cycles(CPURISCVState *env, uint32_t target_ctr); void riscv_pmu_timer_cb(void *priv); -int riscv_pmu_init(RISCVCPU *cpu, int num_counters); +void riscv_pmu_init(RISCVCPU *cpu, Error **errp); int riscv_pmu_update_event_map(CPURISCVState *env, uint64_t value, uint32_t ctr_idx); int riscv_pmu_incr_ctr(RISCVCPU *cpu, enum riscv_pmu_event_idx event_idx); diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 1a3351b142..144bdac1b2 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -689,7 +689,13 @@ static bool tcg_cpu_realize(CPUState *cs, Error **errp) } if (cpu->cfg.pmu_num) { - if (!riscv_pmu_init(cpu, cpu->cfg.pmu_num) && cpu->cfg.ext_sscofpmf) { + riscv_pmu_init(cpu, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return false; + } + + if (cpu->cfg.ext_sscofpmf) { cpu->pmu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, riscv_pmu_timer_cb, cpu); } From 7c1bb1d8d412f03ec7d6042971892bd0dff222b7 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Tue, 31 Oct 2023 15:37:14 +0000 Subject: [PATCH 568/974] target/riscv: Don't assume PMU counters are continuous Check the PMU available bitmask when checking if a counter is valid rather than comparing the index against the number of PMUs. Signed-off-by: Rob Bradford Reviewed-by: LIU Zhiwei Reviewed-by: Alistair Francis Reviewed-by: Atish Patra Message-ID: <20231031154000.18134-3-rbradford@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index fc26b52c88..fde7ce1a53 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -188,7 +188,8 @@ static RISCVException zcmt(CPURISCVState *env, int csrno) #if !defined(CONFIG_USER_ONLY) static RISCVException mctr(CPURISCVState *env, int csrno) { - int pmu_num = riscv_cpu_cfg(env)->pmu_num; + RISCVCPU *cpu = env_archcpu(env); + uint32_t pmu_avail_ctrs = cpu->pmu_avail_ctrs; int ctr_index; int base_csrno = CSR_MHPMCOUNTER3; @@ -197,7 +198,7 @@ static RISCVException mctr(CPURISCVState *env, int csrno) base_csrno += 0x80; } ctr_index = csrno - base_csrno; - if (!pmu_num || ctr_index >= pmu_num) { + if ((BIT(ctr_index) & pmu_avail_ctrs >> 3) == 0) { /* The PMU is not enabled or counter is out of range */ return RISCV_EXCP_ILLEGAL_INST; } From 2571a6427c4ded57e44d4c6cf92376d869a75a5c Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Tue, 31 Oct 2023 15:37:15 +0000 Subject: [PATCH 569/974] target/riscv: Use existing PMU counter mask in FDT generation During the FDT generation use the existing mask containing the enabled counters rather then generating a new one. Using the existing mask will support the use of discontinuous counters. Signed-off-by: Rob Bradford Reviewed-by: LIU Zhiwei Reviewed-by: Alistair Francis Reviewed-by: Atish Patra Message-ID: <20231031154000.18134-4-rbradford@rivosinc.com> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 2 +- target/riscv/pmu.c | 6 +----- target/riscv/pmu.h | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 1732c42915..c7fc97e273 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -722,7 +722,7 @@ static void create_fdt_pmu(RISCVVirtState *s) pmu_name = g_strdup_printf("/pmu"); qemu_fdt_add_subnode(ms->fdt, pmu_name); qemu_fdt_setprop_string(ms->fdt, pmu_name, "compatible", "riscv,pmu"); - riscv_pmu_generate_fdt_node(ms->fdt, hart.cfg.pmu_num, pmu_name); + riscv_pmu_generate_fdt_node(ms->fdt, hart.pmu_avail_ctrs, pmu_name); g_free(pmu_name); } diff --git a/target/riscv/pmu.c b/target/riscv/pmu.c index 13801ccb78..7ddf4977b1 100644 --- a/target/riscv/pmu.c +++ b/target/riscv/pmu.c @@ -34,13 +34,9 @@ * to provide the correct value as well. Heterogeneous PMU per hart is not * supported yet. Thus, number of counters are same across all harts. */ -void riscv_pmu_generate_fdt_node(void *fdt, int num_ctrs, char *pmu_name) +void riscv_pmu_generate_fdt_node(void *fdt, uint32_t cmask, char *pmu_name) { uint32_t fdt_event_ctr_map[15] = {}; - uint32_t cmask; - - /* All the programmable counters can map to any event */ - cmask = MAKE_32BIT_MASK(3, num_ctrs); /* * The event encoding is specified in the SBI specification diff --git a/target/riscv/pmu.h b/target/riscv/pmu.h index 88e0713296..505fc850d3 100644 --- a/target/riscv/pmu.h +++ b/target/riscv/pmu.h @@ -28,6 +28,6 @@ void riscv_pmu_init(RISCVCPU *cpu, Error **errp); int riscv_pmu_update_event_map(CPURISCVState *env, uint64_t value, uint32_t ctr_idx); int riscv_pmu_incr_ctr(RISCVCPU *cpu, enum riscv_pmu_event_idx event_idx); -void riscv_pmu_generate_fdt_node(void *fdt, int num_counters, char *pmu_name); +void riscv_pmu_generate_fdt_node(void *fdt, uint32_t cmask, char *pmu_name); int riscv_pmu_setup_timer(CPURISCVState *env, uint64_t value, uint32_t ctr_idx); From 69b3849bff4bd5ab82241ad09426cd9ff7078449 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Tue, 31 Oct 2023 15:37:16 +0000 Subject: [PATCH 570/974] target/riscv: Add "pmu-mask" property to replace "pmu-num" Using a mask instead of the number of PMU devices supports the accurate emulation of platforms that have a discontinuous set of PMU counters. The "pmu-num" property now generates a warning when used by the user on the command line. Rather than storing the value for "pmu-num" convert it directly to the mask if it is specified (overwriting the default "pmu-mask" value) likewise the value is calculated from the mask if the property value is obtained. In the unusual situation that both "pmu-mask" and "pmu-num" are provided then then the order on the command line determines which takes precedence (later overwriting earlier.) Signed-off-by: Rob Bradford Reviewed-by: Alistair Francis Message-ID: <20231031154000.18134-5-rbradford@rivosinc.com> [Changes by AF - Fixup ext_zihpm logic after rebase ] Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 40 +++++++++++++++++++++++++++++++++++++- target/riscv/cpu_cfg.h | 2 +- target/riscv/machine.c | 2 +- target/riscv/pmu.c | 15 +++++++------- target/riscv/tcg/tcg-cpu.c | 4 ++-- 5 files changed, 51 insertions(+), 12 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 70c0a78c6c..02db2760d1 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1427,8 +1427,46 @@ const RISCVCPUMultiExtConfig riscv_cpu_deprecated_exts[] = { DEFINE_PROP_END_OF_LIST(), }; +static void prop_pmu_num_set(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + RISCVCPU *cpu = RISCV_CPU(obj); + uint8_t pmu_num; + + visit_type_uint8(v, name, &pmu_num, errp); + + if (pmu_num > (RV_MAX_MHPMCOUNTERS - 3)) { + error_setg(errp, "Number of counters exceeds maximum available"); + return; + } + + if (pmu_num == 0) { + cpu->cfg.pmu_mask = 0; + } else { + cpu->cfg.pmu_mask = MAKE_64BIT_MASK(3, pmu_num); + } + + warn_report("\"pmu-num\" property is deprecated; use \"pmu-mask\""); +} + +static void prop_pmu_num_get(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + RISCVCPU *cpu = RISCV_CPU(obj); + uint8_t pmu_num = ctpop32(cpu->cfg.pmu_mask); + + visit_type_uint8(v, name, &pmu_num, errp); +} + +const PropertyInfo prop_pmu_num = { + .name = "pmu-num", + .get = prop_pmu_num_get, + .set = prop_pmu_num_set, +}; + Property riscv_cpu_options[] = { - DEFINE_PROP_UINT8("pmu-num", RISCVCPU, cfg.pmu_num, 16), + DEFINE_PROP_UINT32("pmu-mask", RISCVCPU, cfg.pmu_mask, MAKE_64BIT_MASK(3, 16)), + {.name = "pmu-num", .info = &prop_pmu_num}, /* Deprecated */ DEFINE_PROP_BOOL("mmu", RISCVCPU, cfg.mmu, true), DEFINE_PROP_BOOL("pmp", RISCVCPU, cfg.pmp, true), diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index 634ff673b3..f4605fb190 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -134,7 +134,7 @@ struct RISCVCPUConfig { bool ext_xtheadsync; bool ext_XVentanaCondOps; - uint8_t pmu_num; + uint32_t pmu_mask; char *priv_spec; char *user_spec; char *bext_spec; diff --git a/target/riscv/machine.c b/target/riscv/machine.c index 14bb2d7819..fdde243e04 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -316,7 +316,7 @@ static bool pmu_needed(void *opaque) { RISCVCPU *cpu = opaque; - return cpu->cfg.pmu_num; + return (cpu->cfg.pmu_mask > 0); } static const VMStateDescription vmstate_pmu_ctr_state = { diff --git a/target/riscv/pmu.c b/target/riscv/pmu.c index 7ddf4977b1..0e7d58b8a5 100644 --- a/target/riscv/pmu.c +++ b/target/riscv/pmu.c @@ -18,14 +18,13 @@ #include "qemu/osdep.h" #include "qemu/log.h" +#include "qemu/error-report.h" #include "cpu.h" #include "pmu.h" #include "sysemu/cpu-timers.h" #include "sysemu/device_tree.h" #define RISCV_TIMEBASE_FREQ 1000000000 /* 1Ghz */ -#define MAKE_32BIT_MASK(shift, length) \ - (((uint32_t)(~0UL) >> (32 - (length))) << (shift)) /* * To keep it simple, any event can be mapped to any programmable counters in @@ -184,7 +183,7 @@ int riscv_pmu_incr_ctr(RISCVCPU *cpu, enum riscv_pmu_event_idx event_idx) CPURISCVState *env = &cpu->env; gpointer value; - if (!cpu->cfg.pmu_num) { + if (!cpu->cfg.pmu_mask) { return 0; } value = g_hash_table_lookup(cpu->pmu_event_ctr_map, @@ -432,9 +431,12 @@ int riscv_pmu_setup_timer(CPURISCVState *env, uint64_t value, uint32_t ctr_idx) void riscv_pmu_init(RISCVCPU *cpu, Error **errp) { - uint8_t pmu_num = cpu->cfg.pmu_num; + if (cpu->cfg.pmu_mask & (COUNTEREN_CY | COUNTEREN_TM | COUNTEREN_IR)) { + error_setg(errp, "\"pmu-mask\" contains invalid bits (0-2) set"); + return; + } - if (pmu_num > (RV_MAX_MHPMCOUNTERS - 3)) { + if (ctpop32(cpu->cfg.pmu_mask) > (RV_MAX_MHPMCOUNTERS - 3)) { error_setg(errp, "Number of counters exceeds maximum available"); return; } @@ -445,6 +447,5 @@ void riscv_pmu_init(RISCVCPU *cpu, Error **errp) return; } - /* Create a bitmask of available programmable counters */ - cpu->pmu_avail_ctrs = MAKE_32BIT_MASK(3, pmu_num); + cpu->pmu_avail_ctrs = cpu->cfg.pmu_mask; } diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 144bdac1b2..08adad304d 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -600,7 +600,7 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) } if (!cpu->cfg.ext_zihpm) { - cpu->cfg.pmu_num = 0; + cpu->cfg.pmu_mask = 0; cpu->pmu_avail_ctrs = 0; } @@ -688,7 +688,7 @@ static bool tcg_cpu_realize(CPUState *cs, Error **errp) riscv_timer_init(cpu); } - if (cpu->cfg.pmu_num) { + if (cpu->cfg.pmu_mask) { riscv_pmu_init(cpu, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); From bc5e8445342fee35b35f2ed9a9f2249e060b8776 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Tue, 31 Oct 2023 15:37:17 +0000 Subject: [PATCH 571/974] docs/about/deprecated: Document RISC-V "pmu-num" deprecation This has been replaced by a "pmu-mask" property that provides much more flexibility. Signed-off-by: Rob Bradford Acked-by: LIU Zhiwei Reviewed-by: Alistair Francis Reviewed-by: Atish Patra Message-ID: <20231031154000.18134-6-rbradford@rivosinc.com> Signed-off-by: Alistair Francis --- docs/about/deprecated.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index ecccd5d3fc..78550c07bf 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -413,6 +413,18 @@ Specifying the iSCSI password in plain text on the command line using the used instead, to refer to a ``--object secret...`` instance that provides a password via a file, or encrypted. +CPU device properties +''''''''''''''''''''' + +``pmu-num=n`` on RISC-V CPUs (since 8.2) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In order to support more flexible counter configurations this has been replaced +by a ``pmu-mask`` property. If set of counters is continuous then the mask can +be calculated with ``((2 ^ n) - 1) << 3``. The least significant three bits +must be left clear. + + Backwards compatibility ----------------------- From bb67ec32a0bb90a4d392a778f97f1e86ed75f0a9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 1 Nov 2023 15:17:04 -0700 Subject: [PATCH 572/974] target/hppa: Include PSW_P in tb flags and mmu index Use a separate mmu index for PSW_P enabled vs disabled. This means we can elide the tlb flush in cpu_hppa_put_psw when PSW_P changes. This turns out to be the majority of all tlb flushes. Signed-off-by: Richard Henderson --- target/hppa/cpu.h | 36 ++++++++++++++++++++++++------------ target/hppa/helper.c | 8 -------- target/hppa/mem_helper.c | 6 ++---- target/hppa/translate.c | 5 +++-- 4 files changed, 29 insertions(+), 26 deletions(-) diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 798d0c26d7..48d735929e 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -30,21 +30,33 @@ basis. It's probably easier to fall back to a strong memory model. */ #define TCG_GUEST_DEFAULT_MO TCG_MO_ALL -#define MMU_KERNEL_IDX 11 -#define MMU_PL1_IDX 12 -#define MMU_PL2_IDX 13 -#define MMU_USER_IDX 14 -#define MMU_PHYS_IDX 15 +#define MMU_KERNEL_IDX 7 +#define MMU_KERNEL_P_IDX 8 +#define MMU_PL1_IDX 9 +#define MMU_PL1_P_IDX 10 +#define MMU_PL2_IDX 11 +#define MMU_PL2_P_IDX 12 +#define MMU_USER_IDX 13 +#define MMU_USER_P_IDX 14 +#define MMU_PHYS_IDX 15 -#define PRIV_TO_MMU_IDX(priv) (MMU_KERNEL_IDX + (priv)) -#define MMU_IDX_TO_PRIV(mmu_idx) ((mmu_idx) - MMU_KERNEL_IDX) +#define MMU_IDX_TO_PRIV(MIDX) (((MIDX) - MMU_KERNEL_IDX) / 2) +#define MMU_IDX_TO_P(MIDX) (((MIDX) - MMU_KERNEL_IDX) & 1) +#define PRIV_P_TO_MMU_IDX(PRIV, P) ((PRIV) * 2 + !!(P) + MMU_KERNEL_IDX) #define TARGET_INSN_START_EXTRA_WORDS 1 /* No need to flush MMU_PHYS_IDX */ #define HPPA_MMU_FLUSH_MASK \ - (1 << MMU_KERNEL_IDX | 1 << MMU_PL1_IDX | \ - 1 << MMU_PL2_IDX | 1 << MMU_USER_IDX) + (1 << MMU_KERNEL_IDX | 1 << MMU_KERNEL_P_IDX | \ + 1 << MMU_PL1_IDX | 1 << MMU_PL1_P_IDX | \ + 1 << MMU_PL2_IDX | 1 << MMU_PL2_P_IDX | \ + 1 << MMU_USER_IDX | 1 << MMU_USER_P_IDX) + +/* Indicies to flush for access_id changes. */ +#define HPPA_MMU_FLUSH_P_MASK \ + (1 << MMU_KERNEL_P_IDX | 1 << MMU_PL1_P_IDX | \ + 1 << MMU_PL2_P_IDX | 1 << MMU_USER_P_IDX) /* Hardware exceptions, interrupts, faults, and traps. */ #define EXCP_HPMC 1 /* high priority machine check */ @@ -249,7 +261,7 @@ static inline int cpu_mmu_index(CPUHPPAState *env, bool ifetch) return MMU_USER_IDX; #else if (env->psw & (ifetch ? PSW_C : PSW_D)) { - return PRIV_TO_MMU_IDX(env->iaoq_f & 3); + return PRIV_P_TO_MMU_IDX(env->iaoq_f & 3, env->psw & PSW_P); } return MMU_PHYS_IDX; /* mmu disabled */ #endif @@ -299,8 +311,8 @@ static inline void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, *cs_base = env->iaoq_b & -4; flags |= TB_FLAG_UNALIGN * !env_cpu(env)->prctl_unalign_sigbus; #else - /* ??? E, T, H, L, B, P bits need to be here, when implemented. */ - flags |= env->psw & (PSW_W | PSW_C | PSW_D); + /* ??? E, T, H, L, B bits need to be here, when implemented. */ + flags |= env->psw & (PSW_W | PSW_C | PSW_D | PSW_P); flags |= (env->iaoq_f & 3) << TB_FLAG_PRIV_SHIFT; *pc = (env->psw & PSW_C diff --git a/target/hppa/helper.c b/target/hppa/helper.c index a8d3f456ee..cba8160b3d 100644 --- a/target/hppa/helper.c +++ b/target/hppa/helper.c @@ -51,7 +51,6 @@ target_ureg cpu_hppa_get_psw(CPUHPPAState *env) void cpu_hppa_put_psw(CPUHPPAState *env, target_ureg psw) { - target_ureg old_psw = env->psw; target_ureg cb = 0; env->psw = psw & ~(PSW_N | PSW_V | PSW_CB); @@ -67,13 +66,6 @@ void cpu_hppa_put_psw(CPUHPPAState *env, target_ureg psw) cb |= ((psw >> 9) & 1) << 8; cb |= ((psw >> 8) & 1) << 4; env->psw_cb = cb; - - /* If PSW_P changes, it affects how we translate addresses. */ - if ((psw ^ old_psw) & PSW_P) { -#ifndef CONFIG_USER_ONLY - tlb_flush_by_mmuidx(env_cpu(env), HPPA_MMU_FLUSH_MASK); -#endif - } } void hppa_cpu_dump_state(CPUState *cs, FILE *f, int flags) diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c index 350485f619..729032288d 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -144,7 +144,7 @@ int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx, } /* access_id == 0 means public page and no check is performed */ - if ((env->psw & PSW_P) && ent->access_id) { + if (ent->access_id && MMU_IDX_TO_P(mmu_idx)) { /* If bits [31:1] match, and bit 0 is set, suppress write. */ int match = ent->access_id * 2 + 1; @@ -373,9 +373,7 @@ void HELPER(ptlbe)(CPUHPPAState *env) void cpu_hppa_change_prot_id(CPUHPPAState *env) { - if (env->psw & PSW_P) { - tlb_flush_by_mmuidx(env_cpu(env), HPPA_MMU_FLUSH_MASK); - } + tlb_flush_by_mmuidx(env_cpu(env), HPPA_MMU_FLUSH_P_MASK); } void HELPER(change_prot_id)(CPUHPPAState *env) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 9f3ba9f42f..f6a656325c 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -4071,8 +4071,9 @@ static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->unalign = (ctx->tb_flags & TB_FLAG_UNALIGN ? MO_UNALN : MO_ALIGN); #else ctx->privilege = (ctx->tb_flags >> TB_FLAG_PRIV_SHIFT) & 3; - ctx->mmu_idx = (ctx->tb_flags & PSW_D ? - PRIV_TO_MMU_IDX(ctx->privilege) : MMU_PHYS_IDX); + ctx->mmu_idx = (ctx->tb_flags & PSW_D + ? PRIV_P_TO_MMU_IDX(ctx->privilege, ctx->tb_flags & PSW_P) + : MMU_PHYS_IDX); /* Recover the IAOQ values from the GVA + PRIV. */ uint64_t cs_base = ctx->base.tb->cs_base; From 729cd3506dbf2dde010486de913eae56a615422c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 26 Oct 2023 22:13:12 -0700 Subject: [PATCH 573/974] target/hppa: Rename hppa_tlb_entry to HPPATLBEntry Rename to CamelCase per coding style. Signed-off-by: Richard Henderson --- target/hppa/cpu.h | 8 ++++---- target/hppa/machine.c | 6 +++--- target/hppa/mem_helper.c | 30 +++++++++++++++--------------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 48d735929e..22edfc955d 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -174,7 +174,7 @@ typedef int64_t target_sreg; #define TREG_FMT_ld "%"PRId64 #endif -typedef struct { +typedef struct HPPATLBEntry { uint64_t va_b; uint64_t va_e; target_ureg pa; @@ -188,7 +188,7 @@ typedef struct { unsigned ar_pl2 : 2; unsigned entry_valid : 1; unsigned access_id : 16; -} hppa_tlb_entry; +} HPPATLBEntry; typedef struct CPUArchState { target_ureg iaoq_f; /* front */ @@ -234,7 +234,7 @@ typedef struct CPUArchState { /* ??? Implement a unified itlb/dtlb for the moment. */ /* ??? We should use a more intelligent data structure. */ - hppa_tlb_entry tlb[HPPA_TLB_ENTRIES]; + HPPATLBEntry tlb[HPPA_TLB_ENTRIES]; uint32_t tlb_last; } CPUHPPAState; @@ -362,7 +362,7 @@ void hppa_cpu_do_interrupt(CPUState *cpu); bool hppa_cpu_exec_interrupt(CPUState *cpu, int int_req); int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx, int type, hwaddr *pphys, int *pprot, - hppa_tlb_entry **tlb_entry); + HPPATLBEntry **tlb_entry); extern const MemoryRegionOps hppa_io_eir_ops; extern const VMStateDescription vmstate_hppa_cpu; void hppa_cpu_alarm_timer(void *); diff --git a/target/hppa/machine.c b/target/hppa/machine.c index 905991d7f9..1d3f9b639d 100644 --- a/target/hppa/machine.c +++ b/target/hppa/machine.c @@ -69,7 +69,7 @@ static const VMStateInfo vmstate_psw = { static int get_tlb(QEMUFile *f, void *opaque, size_t size, const VMStateField *field) { - hppa_tlb_entry *ent = opaque; + HPPATLBEntry *ent = opaque; uint32_t val; memset(ent, 0, sizeof(*ent)); @@ -95,7 +95,7 @@ static int get_tlb(QEMUFile *f, void *opaque, size_t size, static int put_tlb(QEMUFile *f, void *opaque, size_t size, const VMStateField *field, JSONWriter *vmdesc) { - hppa_tlb_entry *ent = opaque; + HPPATLBEntry *ent = opaque; uint32_t val = 0; if (ent->entry_valid) { @@ -153,7 +153,7 @@ static VMStateField vmstate_env_fields[] = { VMSTATE_UINT32(fr0_shadow, CPUHPPAState), VMSTATE_ARRAY(tlb, CPUHPPAState, ARRAY_SIZE(((CPUHPPAState *)0)->tlb), - 0, vmstate_tlb, hppa_tlb_entry), + 0, vmstate_tlb, HPPATLBEntry), VMSTATE_UINT32(tlb_last, CPUHPPAState), VMSTATE_END_OF_LIST() diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c index 729032288d..a22de81a48 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -25,12 +25,12 @@ #include "hw/core/cpu.h" #include "trace.h" -static hppa_tlb_entry *hppa_find_tlb(CPUHPPAState *env, vaddr addr) +static HPPATLBEntry *hppa_find_tlb(CPUHPPAState *env, vaddr addr) { int i; for (i = 0; i < ARRAY_SIZE(env->tlb); ++i) { - hppa_tlb_entry *ent = &env->tlb[i]; + HPPATLBEntry *ent = &env->tlb[i]; if (ent->va_b <= addr && addr <= ent->va_e) { trace_hppa_tlb_find_entry(env, ent + i, ent->entry_valid, ent->va_b, ent->va_e, ent->pa); @@ -41,7 +41,7 @@ static hppa_tlb_entry *hppa_find_tlb(CPUHPPAState *env, vaddr addr) return NULL; } -static void hppa_flush_tlb_ent(CPUHPPAState *env, hppa_tlb_entry *ent, +static void hppa_flush_tlb_ent(CPUHPPAState *env, HPPATLBEntry *ent, bool force_flush_btlb) { CPUState *cs = env_cpu(env); @@ -65,9 +65,9 @@ static void hppa_flush_tlb_ent(CPUHPPAState *env, hppa_tlb_entry *ent, ent->va_b = -1; } -static hppa_tlb_entry *hppa_alloc_tlb_ent(CPUHPPAState *env) +static HPPATLBEntry *hppa_alloc_tlb_ent(CPUHPPAState *env) { - hppa_tlb_entry *ent; + HPPATLBEntry *ent; uint32_t i; if (env->tlb_last < HPPA_BTLB_ENTRIES || env->tlb_last >= ARRAY_SIZE(env->tlb)) { @@ -86,11 +86,11 @@ static hppa_tlb_entry *hppa_alloc_tlb_ent(CPUHPPAState *env) int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx, int type, hwaddr *pphys, int *pprot, - hppa_tlb_entry **tlb_entry) + HPPATLBEntry **tlb_entry) { hwaddr phys; int prot, r_prot, w_prot, x_prot, priv; - hppa_tlb_entry *ent; + HPPATLBEntry *ent; int ret = -1; if (tlb_entry) { @@ -231,7 +231,7 @@ bool hppa_cpu_tlb_fill(CPUState *cs, vaddr addr, int size, { HPPACPU *cpu = HPPA_CPU(cs); CPUHPPAState *env = &cpu->env; - hppa_tlb_entry *ent; + HPPATLBEntry *ent; int prot, excp, a_prot; hwaddr phys; @@ -275,12 +275,12 @@ bool hppa_cpu_tlb_fill(CPUState *cs, vaddr addr, int size, /* Insert (Insn/Data) TLB Address. Note this is PA 1.1 only. */ void HELPER(itlba)(CPUHPPAState *env, target_ulong addr, target_ureg reg) { - hppa_tlb_entry *empty = NULL; + HPPATLBEntry *empty = NULL; int i; /* Zap any old entries covering ADDR; notice empty entries on the way. */ for (i = HPPA_BTLB_ENTRIES; i < ARRAY_SIZE(env->tlb); ++i) { - hppa_tlb_entry *ent = &env->tlb[i]; + HPPATLBEntry *ent = &env->tlb[i]; if (ent->va_b <= addr && addr <= ent->va_e) { if (ent->entry_valid) { hppa_flush_tlb_ent(env, ent, false); @@ -303,7 +303,7 @@ void HELPER(itlba)(CPUHPPAState *env, target_ulong addr, target_ureg reg) trace_hppa_tlb_itlba(env, empty, empty->va_b, empty->va_e, empty->pa); } -static void set_access_bits(CPUHPPAState *env, hppa_tlb_entry *ent, target_ureg reg) +static void set_access_bits(CPUHPPAState *env, HPPATLBEntry *ent, target_ureg reg) { ent->access_id = extract32(reg, 1, 18); ent->u = extract32(reg, 19, 1); @@ -321,7 +321,7 @@ static void set_access_bits(CPUHPPAState *env, hppa_tlb_entry *ent, target_ureg /* Insert (Insn/Data) TLB Protection. Note this is PA 1.1 only. */ void HELPER(itlbp)(CPUHPPAState *env, target_ulong addr, target_ureg reg) { - hppa_tlb_entry *ent = hppa_find_tlb(env, addr); + HPPATLBEntry *ent = hppa_find_tlb(env, addr); if (unlikely(ent == NULL)) { qemu_log_mask(LOG_GUEST_ERROR, "ITLBP not following ITLBA\n"); @@ -337,7 +337,7 @@ static void ptlb_work(CPUState *cpu, run_on_cpu_data data) { CPUHPPAState *env = cpu_env(cpu); target_ulong addr = (target_ulong) data.target_ptr; - hppa_tlb_entry *ent = hppa_find_tlb(env, addr); + HPPATLBEntry *ent = hppa_find_tlb(env, addr); if (ent && ent->entry_valid) { hppa_flush_tlb_ent(env, ent, false); @@ -407,7 +407,7 @@ target_ureg HELPER(lpa)(CPUHPPAState *env, target_ulong addr) /* Return the ar_type of the TLB at VADDR, or -1. */ int hppa_artype_for_page(CPUHPPAState *env, target_ulong vaddr) { - hppa_tlb_entry *ent = hppa_find_tlb(env, vaddr); + HPPATLBEntry *ent = hppa_find_tlb(env, vaddr); return ent ? ent->ar_type : -1; } @@ -422,7 +422,7 @@ void HELPER(diag_btlb)(CPUHPPAState *env) unsigned int phys_page, len, slot; int mmu_idx = cpu_mmu_index(env, 0); uintptr_t ra = GETPC(); - hppa_tlb_entry *btlb; + HPPATLBEntry *btlb; uint64_t virt_page; uint32_t *vaddr; From 66866cc74fc77e6f08eaeb1054bf1e2454fa4721 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 26 Oct 2023 22:21:47 -0700 Subject: [PATCH 574/974] target/hppa: Use IntervalTreeNode in HPPATLBEntry Replace the va_b and va_b fields with the interval tree node. The actual interval tree is not yet used. Signed-off-by: Richard Henderson --- target/hppa/cpu.h | 5 +++-- target/hppa/machine.c | 6 +++--- target/hppa/mem_helper.c | 31 +++++++++++++++++-------------- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 22edfc955d..84bb6edc60 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -23,6 +23,7 @@ #include "cpu-qom.h" #include "exec/cpu-defs.h" #include "qemu/cpu-float.h" +#include "qemu/interval-tree.h" /* PA-RISC 1.x processors have a strong memory model. */ /* ??? While we do not yet implement PA-RISC 2.0, those processors have @@ -175,8 +176,8 @@ typedef int64_t target_sreg; #endif typedef struct HPPATLBEntry { - uint64_t va_b; - uint64_t va_e; + IntervalTreeNode itree; + target_ureg pa; unsigned u : 1; unsigned t : 1; diff --git a/target/hppa/machine.c b/target/hppa/machine.c index 1d3f9b639d..4535195ca2 100644 --- a/target/hppa/machine.c +++ b/target/hppa/machine.c @@ -74,7 +74,7 @@ static int get_tlb(QEMUFile *f, void *opaque, size_t size, memset(ent, 0, sizeof(*ent)); - ent->va_b = qemu_get_be64(f); + ent->itree.start = qemu_get_be64(f); ent->pa = qemu_get_betr(f); val = qemu_get_be32(f); @@ -88,7 +88,7 @@ static int get_tlb(QEMUFile *f, void *opaque, size_t size, ent->d = extract32(val, 28, 1); ent->t = extract32(val, 29, 1); - ent->va_e = ent->va_b + TARGET_PAGE_SIZE - 1; + ent->itree.last = ent->itree.start + TARGET_PAGE_SIZE - 1; return 0; } @@ -110,7 +110,7 @@ static int put_tlb(QEMUFile *f, void *opaque, size_t size, val = deposit32(val, 29, 1, ent->t); } - qemu_put_be64(f, ent->va_b); + qemu_put_be64(f, ent->itree.start); qemu_put_betr(f, ent->pa); qemu_put_be32(f, val); return 0; diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c index a22de81a48..687ae44ed0 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -31,9 +31,10 @@ static HPPATLBEntry *hppa_find_tlb(CPUHPPAState *env, vaddr addr) for (i = 0; i < ARRAY_SIZE(env->tlb); ++i) { HPPATLBEntry *ent = &env->tlb[i]; - if (ent->va_b <= addr && addr <= ent->va_e) { + if (ent->itree.start <= addr && addr <= ent->itree.last) { trace_hppa_tlb_find_entry(env, ent + i, ent->entry_valid, - ent->va_b, ent->va_e, ent->pa); + ent->itree.start, ent->itree.last, + ent->pa); return ent; } } @@ -50,11 +51,12 @@ static void hppa_flush_tlb_ent(CPUHPPAState *env, HPPATLBEntry *ent, return; } - trace_hppa_tlb_flush_ent(env, ent, ent->va_b, ent->va_e, ent->pa); + trace_hppa_tlb_flush_ent(env, ent, ent->itree.start, + ent->itree.last, ent->pa); - tlb_flush_range_by_mmuidx(cs, ent->va_b, - ent->va_e - ent->va_b + 1, - HPPA_MMU_FLUSH_MASK, TARGET_LONG_BITS); + tlb_flush_range_by_mmuidx(cs, ent->itree.start, + ent->itree.last - ent->itree.start + 1, + HPPA_MMU_FLUSH_MASK, TARGET_LONG_BITS); /* never clear BTLBs, unless forced to do so. */ if (ent < &env->tlb[HPPA_BTLB_ENTRIES] && !force_flush_btlb) { @@ -62,7 +64,7 @@ static void hppa_flush_tlb_ent(CPUHPPAState *env, HPPATLBEntry *ent, } memset(ent, 0, sizeof(*ent)); - ent->va_b = -1; + ent->itree.start = -1; } static HPPATLBEntry *hppa_alloc_tlb_ent(CPUHPPAState *env) @@ -118,7 +120,7 @@ int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx, } /* We now know the physical address. */ - phys = ent->pa + (addr - ent->va_b); + phys = ent->pa + (addr - ent->itree.start); /* Map TLB access_rights field to QEMU protection. */ priv = MMU_IDX_TO_PRIV(mmu_idx); @@ -281,7 +283,7 @@ void HELPER(itlba)(CPUHPPAState *env, target_ulong addr, target_ureg reg) /* Zap any old entries covering ADDR; notice empty entries on the way. */ for (i = HPPA_BTLB_ENTRIES; i < ARRAY_SIZE(env->tlb); ++i) { HPPATLBEntry *ent = &env->tlb[i]; - if (ent->va_b <= addr && addr <= ent->va_e) { + if (ent->itree.start <= addr && addr <= ent->itree.last) { if (ent->entry_valid) { hppa_flush_tlb_ent(env, ent, false); } @@ -297,10 +299,11 @@ void HELPER(itlba)(CPUHPPAState *env, target_ulong addr, target_ureg reg) } /* Note that empty->entry_valid == 0 already. */ - empty->va_b = addr & TARGET_PAGE_MASK; - empty->va_e = empty->va_b + TARGET_PAGE_SIZE - 1; + empty->itree.start = addr & TARGET_PAGE_MASK; + empty->itree.last = empty->itree.start + TARGET_PAGE_SIZE - 1; empty->pa = extract32(reg, 5, 20) << TARGET_PAGE_BITS; - trace_hppa_tlb_itlba(env, empty, empty->va_b, empty->va_e, empty->pa); + trace_hppa_tlb_itlba(env, empty, empty->itree.start, + empty->itree.last, empty->pa); } static void set_access_bits(CPUHPPAState *env, HPPATLBEntry *ent, target_ureg reg) @@ -467,8 +470,8 @@ void HELPER(diag_btlb)(CPUHPPAState *env) /* force flush of possibly existing BTLB entry */ hppa_flush_tlb_ent(env, btlb, true); /* create new BTLB entry */ - btlb->va_b = virt_page << TARGET_PAGE_BITS; - btlb->va_e = btlb->va_b + len * TARGET_PAGE_SIZE - 1; + btlb->itree.start = virt_page << TARGET_PAGE_BITS; + btlb->itree.last = btlb->itree.start + len * TARGET_PAGE_SIZE - 1; btlb->pa = phys_page << TARGET_PAGE_BITS; set_access_bits(env, btlb, env->gr[20]); btlb->t = 0; From f8cda28b8dd6f9d52042a3d45558de81e4c54e36 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 27 Oct 2023 01:09:21 -0700 Subject: [PATCH 575/974] target/hppa: Always report one page to tlb_set_page No need to trigger the large_page_mask code unnecessarily. Drop the now unused HPPATLBEntry.page_size field. Signed-off-by: Richard Henderson --- target/hppa/cpu.h | 5 +++-- target/hppa/mem_helper.c | 11 +++++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 84bb6edc60..1480d0237a 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -179,15 +179,16 @@ typedef struct HPPATLBEntry { IntervalTreeNode itree; target_ureg pa; + + unsigned entry_valid : 1; + unsigned u : 1; unsigned t : 1; unsigned d : 1; unsigned b : 1; - unsigned page_size : 4; unsigned ar_type : 3; unsigned ar_pl1 : 2; unsigned ar_pl2 : 2; - unsigned entry_valid : 1; unsigned access_id : 16; } HPPATLBEntry; diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c index 687ae44ed0..60cae646cc 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -268,9 +268,16 @@ bool hppa_cpu_tlb_fill(CPUState *cs, vaddr addr, int size, trace_hppa_tlb_fill_success(env, addr & TARGET_PAGE_MASK, phys & TARGET_PAGE_MASK, size, type, mmu_idx); - /* Success! Store the translation into the QEMU TLB. */ + + /* + * Success! Store the translation into the QEMU TLB. + * Note that we always install a single-page entry, because that + * is what works best with softmmu -- anything else will trigger + * the large page protection mask. We do not require this, + * because we record the large page here in the hppa tlb. + */ tlb_set_page(cs, addr & TARGET_PAGE_MASK, phys & TARGET_PAGE_MASK, - prot, mmu_idx, TARGET_PAGE_SIZE << (ent ? 2 * ent->page_size : 0)); + prot, mmu_idx, TARGET_PAGE_SIZE); return true; } From 09cae8255ffc6d133b32073b6d8d99b56b3379b5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 1 Nov 2023 16:07:48 -0700 Subject: [PATCH 576/974] target/hppa: Split out hppa_flush_tlb_range Signed-off-by: Richard Henderson --- target/hppa/mem_helper.c | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c index 60cae646cc..828cceb29c 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -67,6 +67,25 @@ static void hppa_flush_tlb_ent(CPUHPPAState *env, HPPATLBEntry *ent, ent->itree.start = -1; } +static HPPATLBEntry *hppa_flush_tlb_range(CPUHPPAState *env, + vaddr va_b, vaddr va_e) +{ + HPPATLBEntry *empty = NULL; + + /* Zap any old entries covering ADDR; notice empty entries on the way. */ + for (int i = HPPA_BTLB_ENTRIES; i < ARRAY_SIZE(env->tlb); ++i) { + HPPATLBEntry *ent = &env->tlb[i]; + + if (!ent->entry_valid) { + empty = ent; + } else if (va_e >= ent->itree.start && va_b <= ent->itree.last) { + hppa_flush_tlb_ent(env, ent, false); + empty = ent; + } + } + return empty; +} + static HPPATLBEntry *hppa_alloc_tlb_ent(CPUHPPAState *env) { HPPATLBEntry *ent; @@ -284,21 +303,11 @@ bool hppa_cpu_tlb_fill(CPUState *cs, vaddr addr, int size, /* Insert (Insn/Data) TLB Address. Note this is PA 1.1 only. */ void HELPER(itlba)(CPUHPPAState *env, target_ulong addr, target_ureg reg) { - HPPATLBEntry *empty = NULL; - int i; + HPPATLBEntry *empty; /* Zap any old entries covering ADDR; notice empty entries on the way. */ - for (i = HPPA_BTLB_ENTRIES; i < ARRAY_SIZE(env->tlb); ++i) { - HPPATLBEntry *ent = &env->tlb[i]; - if (ent->itree.start <= addr && addr <= ent->itree.last) { - if (ent->entry_valid) { - hppa_flush_tlb_ent(env, ent, false); - } - if (!empty) { - empty = ent; - } - } - } + addr &= TARGET_PAGE_MASK; + empty = hppa_flush_tlb_range(env, addr, addr + TARGET_PAGE_SIZE - 1); /* If we didn't see an empty entry, evict one. */ if (empty == NULL) { @@ -306,8 +315,8 @@ void HELPER(itlba)(CPUHPPAState *env, target_ulong addr, target_ureg reg) } /* Note that empty->entry_valid == 0 already. */ - empty->itree.start = addr & TARGET_PAGE_MASK; - empty->itree.last = empty->itree.start + TARGET_PAGE_SIZE - 1; + empty->itree.start = addr; + empty->itree.last = addr + TARGET_PAGE_SIZE - 1; empty->pa = extract32(reg, 5, 20) << TARGET_PAGE_BITS; trace_hppa_tlb_itlba(env, empty, empty->itree.start, empty->itree.last, empty->pa); From d7553f3591bbf495b4c35355179c793d03e106dc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 27 Oct 2023 00:24:30 -0700 Subject: [PATCH 577/974] target/hppa: Populate an interval tree with valid tlb entries Complete the data structure conversion started earlier. This reduces the perf overhead of hppa_get_physical_address from ~5% to ~0.25%. Signed-off-by: Richard Henderson --- target/hppa/cpu.c | 2 + target/hppa/cpu.h | 24 +++++- target/hppa/machine.c | 51 ++++++++++++- target/hppa/mem_helper.c | 161 +++++++++++++++++++++++---------------- 4 files changed, 167 insertions(+), 71 deletions(-) diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 1644297bf8..5e1240c631 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -137,8 +137,10 @@ static void hppa_cpu_realizefn(DeviceState *dev, Error **errp) #ifndef CONFIG_USER_ONLY { HPPACPU *cpu = HPPA_CPU(cs); + cpu->alarm_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, hppa_cpu_alarm_timer, cpu); + hppa_ptlbe(&cpu->env); } #endif } diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 1480d0237a..08de894393 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -176,7 +176,10 @@ typedef int64_t target_sreg; #endif typedef struct HPPATLBEntry { - IntervalTreeNode itree; + union { + IntervalTreeNode itree; + struct HPPATLBEntry *unused_next; + }; target_ureg pa; @@ -234,10 +237,22 @@ typedef struct CPUArchState { #define HPPA_TLB_ENTRIES 256 #define HPPA_BTLB_ENTRIES (HPPA_BTLB_FIXED + HPPA_BTLB_VARIABLE) - /* ??? Implement a unified itlb/dtlb for the moment. */ - /* ??? We should use a more intelligent data structure. */ - HPPATLBEntry tlb[HPPA_TLB_ENTRIES]; + /* Index for round-robin tlb eviction. */ uint32_t tlb_last; + + /* + * For pa1.x, the partial initialized, still invalid tlb entry + * which has had ITLBA performed, but not yet ITLBP. + */ + HPPATLBEntry *tlb_partial; + + /* Linked list of all invalid (unused) tlb entries. */ + HPPATLBEntry *tlb_unused; + + /* Root of the search tree for all valid tlb entries. */ + IntervalTreeRoot tlb_root; + + HPPATLBEntry tlb[HPPA_TLB_ENTRIES]; } CPUHPPAState; /** @@ -356,6 +371,7 @@ int hppa_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int hppa_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void hppa_cpu_dump_state(CPUState *cs, FILE *f, int); #ifndef CONFIG_USER_ONLY +void hppa_ptlbe(CPUHPPAState *env); hwaddr hppa_cpu_get_phys_page_debug(CPUState *cs, vaddr addr); bool hppa_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, diff --git a/target/hppa/machine.c b/target/hppa/machine.c index 4535195ca2..ab3e8c81fa 100644 --- a/target/hppa/machine.c +++ b/target/hppa/machine.c @@ -72,8 +72,6 @@ static int get_tlb(QEMUFile *f, void *opaque, size_t size, HPPATLBEntry *ent = opaque; uint32_t val; - memset(ent, 0, sizeof(*ent)); - ent->itree.start = qemu_get_be64(f); ent->pa = qemu_get_betr(f); val = qemu_get_be32(f); @@ -122,6 +120,53 @@ static const VMStateInfo vmstate_tlb = { .put = put_tlb, }; +static int tlb_pre_load(void *opaque) +{ + CPUHPPAState *env = opaque; + + /* + * Zap the entire tlb, on-the-side data structures and all. + * Each tlb entry will have data re-filled by put_tlb. + */ + memset(env->tlb, 0, sizeof(env->tlb)); + memset(&env->tlb_root, 0, sizeof(env->tlb_root)); + env->tlb_unused = NULL; + env->tlb_partial = NULL; + + return 0; +} + +static int tlb_post_load(void *opaque, int version_id) +{ + CPUHPPAState *env = opaque; + HPPATLBEntry **unused = &env->tlb_unused; + HPPATLBEntry *partial = NULL; + + /* + * Re-create the interval tree from the valid entries. + * Truely invalid entries should have start == end == 0. + * Otherwise it should be the in-flight tlb_partial entry. + */ + for (uint32_t i = 0; i < ARRAY_SIZE(env->tlb); ++i) { + HPPATLBEntry *e = &env->tlb[i]; + + if (e->entry_valid) { + interval_tree_insert(&e->itree, &env->tlb_root); + } else if (i < HPPA_BTLB_ENTRIES) { + /* btlb not in unused list */ + } else if (partial == NULL && e->itree.start < e->itree.last) { + partial = e; + } else { + *unused = e; + unused = &e->unused_next; + } + } + env->tlb_partial = partial; + *unused = NULL; + + return 0; +} + static VMStateField vmstate_env_fields[] = { VMSTATE_UINTTR_ARRAY(gr, CPUHPPAState, 32), VMSTATE_UINT64_ARRAY(fr, CPUHPPAState, 32), @@ -164,6 +209,8 @@ static const VMStateDescription vmstate_env = { .version_id = 1, .minimum_version_id = 1, .fields = vmstate_env_fields, + .pre_load = tlb_pre_load, + .post_load = tlb_post_load, }; static VMStateField vmstate_cpu_fields[] = { diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c index 828cceb29c..b1773ece61 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -27,16 +27,13 @@ static HPPATLBEntry *hppa_find_tlb(CPUHPPAState *env, vaddr addr) { - int i; + IntervalTreeNode *i = interval_tree_iter_first(&env->tlb_root, addr, addr); - for (i = 0; i < ARRAY_SIZE(env->tlb); ++i) { - HPPATLBEntry *ent = &env->tlb[i]; - if (ent->itree.start <= addr && addr <= ent->itree.last) { - trace_hppa_tlb_find_entry(env, ent + i, ent->entry_valid, - ent->itree.start, ent->itree.last, - ent->pa); - return ent; - } + if (i) { + HPPATLBEntry *ent = container_of(i, HPPATLBEntry, itree); + trace_hppa_tlb_find_entry(env, ent, ent->entry_valid, + ent->itree.start, ent->itree.last, ent->pa); + return ent; } trace_hppa_tlb_find_entry_not_found(env, addr); return NULL; @@ -46,6 +43,7 @@ static void hppa_flush_tlb_ent(CPUHPPAState *env, HPPATLBEntry *ent, bool force_flush_btlb) { CPUState *cs = env_cpu(env); + bool is_btlb; if (!ent->entry_valid) { return; @@ -58,50 +56,55 @@ static void hppa_flush_tlb_ent(CPUHPPAState *env, HPPATLBEntry *ent, ent->itree.last - ent->itree.start + 1, HPPA_MMU_FLUSH_MASK, TARGET_LONG_BITS); - /* never clear BTLBs, unless forced to do so. */ - if (ent < &env->tlb[HPPA_BTLB_ENTRIES] && !force_flush_btlb) { + /* Never clear BTLBs, unless forced to do so. */ + is_btlb = ent < &env->tlb[HPPA_BTLB_ENTRIES]; + if (is_btlb && !force_flush_btlb) { return; } + interval_tree_remove(&ent->itree, &env->tlb_root); memset(ent, 0, sizeof(*ent)); - ent->itree.start = -1; + + if (!is_btlb) { + ent->unused_next = env->tlb_unused; + env->tlb_unused = ent; + } } -static HPPATLBEntry *hppa_flush_tlb_range(CPUHPPAState *env, - vaddr va_b, vaddr va_e) +static void hppa_flush_tlb_range(CPUHPPAState *env, vaddr va_b, vaddr va_e) { - HPPATLBEntry *empty = NULL; + IntervalTreeNode *i, *n; - /* Zap any old entries covering ADDR; notice empty entries on the way. */ - for (int i = HPPA_BTLB_ENTRIES; i < ARRAY_SIZE(env->tlb); ++i) { - HPPATLBEntry *ent = &env->tlb[i]; + i = interval_tree_iter_first(&env->tlb_root, va_b, va_e); + for (; i ; i = n) { + HPPATLBEntry *ent = container_of(i, HPPATLBEntry, itree); - if (!ent->entry_valid) { - empty = ent; - } else if (va_e >= ent->itree.start && va_b <= ent->itree.last) { - hppa_flush_tlb_ent(env, ent, false); - empty = ent; - } + /* + * Find the next entry now: In the normal case the current entry + * will be removed, but in the BTLB case it will remain. + */ + n = interval_tree_iter_next(i, va_b, va_e); + hppa_flush_tlb_ent(env, ent, false); } - return empty; } static HPPATLBEntry *hppa_alloc_tlb_ent(CPUHPPAState *env) { - HPPATLBEntry *ent; - uint32_t i; + HPPATLBEntry *ent = env->tlb_unused; - if (env->tlb_last < HPPA_BTLB_ENTRIES || env->tlb_last >= ARRAY_SIZE(env->tlb)) { - i = HPPA_BTLB_ENTRIES; - env->tlb_last = HPPA_BTLB_ENTRIES + 1; - } else { - i = env->tlb_last; - env->tlb_last++; + if (ent == NULL) { + uint32_t i = env->tlb_last; + + if (i < HPPA_BTLB_ENTRIES || i >= ARRAY_SIZE(env->tlb)) { + i = HPPA_BTLB_ENTRIES; + } + env->tlb_last = i + 1; + + ent = &env->tlb[i]; + hppa_flush_tlb_ent(env, ent, false); } - ent = &env->tlb[i]; - - hppa_flush_tlb_ent(env, ent, false); + env->tlb_unused = ent->unused_next; return ent; } @@ -127,7 +130,7 @@ int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx, /* Find a valid tlb entry that matches the virtual address. */ ent = hppa_find_tlb(env, addr); - if (ent == NULL || !ent->entry_valid) { + if (ent == NULL) { phys = 0; prot = 0; ret = (type == PAGE_EXEC) ? EXCP_ITLB_MISS : EXCP_DTLB_MISS; @@ -303,23 +306,23 @@ bool hppa_cpu_tlb_fill(CPUState *cs, vaddr addr, int size, /* Insert (Insn/Data) TLB Address. Note this is PA 1.1 only. */ void HELPER(itlba)(CPUHPPAState *env, target_ulong addr, target_ureg reg) { - HPPATLBEntry *empty; + HPPATLBEntry *ent; - /* Zap any old entries covering ADDR; notice empty entries on the way. */ + /* Zap any old entries covering ADDR. */ addr &= TARGET_PAGE_MASK; - empty = hppa_flush_tlb_range(env, addr, addr + TARGET_PAGE_SIZE - 1); + hppa_flush_tlb_range(env, addr, addr + TARGET_PAGE_SIZE - 1); - /* If we didn't see an empty entry, evict one. */ - if (empty == NULL) { - empty = hppa_alloc_tlb_ent(env); + ent = env->tlb_partial; + if (ent == NULL) { + ent = hppa_alloc_tlb_ent(env); + env->tlb_partial = ent; } - /* Note that empty->entry_valid == 0 already. */ - empty->itree.start = addr; - empty->itree.last = addr + TARGET_PAGE_SIZE - 1; - empty->pa = extract32(reg, 5, 20) << TARGET_PAGE_BITS; - trace_hppa_tlb_itlba(env, empty, empty->itree.start, - empty->itree.last, empty->pa); + /* Note that ent->entry_valid == 0 already. */ + ent->itree.start = addr; + ent->itree.last = addr + TARGET_PAGE_SIZE - 1; + ent->pa = extract32(reg, 5, 20) << TARGET_PAGE_BITS; + trace_hppa_tlb_itlba(env, ent, ent->itree.start, ent->itree.last, ent->pa); } static void set_access_bits(CPUHPPAState *env, HPPATLBEntry *ent, target_ureg reg) @@ -333,6 +336,8 @@ static void set_access_bits(CPUHPPAState *env, HPPATLBEntry *ent, target_ureg re ent->d = extract32(reg, 28, 1); ent->t = extract32(reg, 29, 1); ent->entry_valid = 1; + + interval_tree_insert(&ent->itree, &env->tlb_root); trace_hppa_tlb_itlbp(env, ent, ent->access_id, ent->u, ent->ar_pl2, ent->ar_pl1, ent->ar_type, ent->b, ent->d, ent->t); } @@ -340,14 +345,16 @@ static void set_access_bits(CPUHPPAState *env, HPPATLBEntry *ent, target_ureg re /* Insert (Insn/Data) TLB Protection. Note this is PA 1.1 only. */ void HELPER(itlbp)(CPUHPPAState *env, target_ulong addr, target_ureg reg) { - HPPATLBEntry *ent = hppa_find_tlb(env, addr); + HPPATLBEntry *ent = env->tlb_partial; - if (unlikely(ent == NULL)) { - qemu_log_mask(LOG_GUEST_ERROR, "ITLBP not following ITLBA\n"); - return; + if (ent) { + env->tlb_partial = NULL; + if (ent->itree.start <= addr && addr <= ent->itree.last) { + set_access_bits(env, ent, reg); + return; + } } - - set_access_bits(env, ent, reg); + qemu_log_mask(LOG_GUEST_ERROR, "ITLBP not following ITLBA\n"); } /* Purge (Insn/Data) TLB. This is explicitly page-based, and is @@ -356,17 +363,15 @@ static void ptlb_work(CPUState *cpu, run_on_cpu_data data) { CPUHPPAState *env = cpu_env(cpu); target_ulong addr = (target_ulong) data.target_ptr; - HPPATLBEntry *ent = hppa_find_tlb(env, addr); - if (ent && ent->entry_valid) { - hppa_flush_tlb_ent(env, ent, false); - } + hppa_flush_tlb_range(env, addr, addr); } void HELPER(ptlb)(CPUHPPAState *env, target_ulong addr) { CPUState *src = env_cpu(env); CPUState *cpu; + trace_hppa_tlb_ptlb(env); run_on_cpu_data data = RUN_ON_CPU_TARGET_PTR(addr); @@ -378,16 +383,40 @@ void HELPER(ptlb)(CPUHPPAState *env, target_ulong addr) async_safe_run_on_cpu(src, ptlb_work, data); } +void hppa_ptlbe(CPUHPPAState *env) +{ + uint32_t i; + + /* Zap the (non-btlb) tlb entries themselves. */ + memset(&env->tlb[HPPA_BTLB_ENTRIES], 0, + sizeof(env->tlb) - HPPA_BTLB_ENTRIES * sizeof(env->tlb[0])); + env->tlb_last = HPPA_BTLB_ENTRIES; + env->tlb_partial = NULL; + + /* Put them all onto the unused list. */ + env->tlb_unused = &env->tlb[HPPA_BTLB_ENTRIES]; + for (i = HPPA_BTLB_ENTRIES; i < ARRAY_SIZE(env->tlb) - 1; ++i) { + env->tlb[i].unused_next = &env->tlb[i + 1]; + } + + /* Re-initialize the interval tree with only the btlb entries. */ + memset(&env->tlb_root, 0, sizeof(env->tlb_root)); + for (i = 0; i < HPPA_BTLB_ENTRIES; ++i) { + if (env->tlb[i].entry_valid) { + interval_tree_insert(&env->tlb[i].itree, &env->tlb_root); + } + } + + tlb_flush_by_mmuidx(env_cpu(env), HPPA_MMU_FLUSH_MASK); +} + /* Purge (Insn/Data) TLB entry. This affects an implementation-defined number of pages/entries (we choose all), and is local to the cpu. */ void HELPER(ptlbe)(CPUHPPAState *env) { trace_hppa_tlb_ptlbe(env); qemu_log_mask(CPU_LOG_MMU, "FLUSH ALL TLB ENTRIES\n"); - memset(&env->tlb[HPPA_BTLB_ENTRIES], 0, - sizeof(env->tlb) - HPPA_BTLB_ENTRIES * sizeof(env->tlb[0])); - env->tlb_last = HPPA_BTLB_ENTRIES; - tlb_flush_by_mmuidx(env_cpu(env), HPPA_MMU_FLUSH_MASK); + hppa_ptlbe(env); } void cpu_hppa_change_prot_id(CPUHPPAState *env) @@ -483,9 +512,11 @@ void HELPER(diag_btlb)(CPUHPPAState *env) (long long) virt_page, phys_page, len, slot); if (slot < HPPA_BTLB_ENTRIES) { btlb = &env->tlb[slot]; - /* force flush of possibly existing BTLB entry */ + + /* Force flush of possibly existing BTLB entry. */ hppa_flush_tlb_ent(env, btlb, true); - /* create new BTLB entry */ + + /* Create new BTLB entry */ btlb->itree.start = virt_page << TARGET_PAGE_BITS; btlb->itree.last = btlb->itree.start + len * TARGET_PAGE_SIZE - 1; btlb->pa = phys_page << TARGET_PAGE_BITS; From e12c63090be5b5eda50a9f30c8292c17823c6fb0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 16 Sep 2023 21:02:42 -0700 Subject: [PATCH 578/974] target/hppa: Remove get_temp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace with tcg_temp_new without recording into ctx. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hppa/translate.c | 76 +++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 45 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index f6a656325c..99b9fc0561 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -254,8 +254,7 @@ typedef struct DisasContext { target_ureg iaoq_n; TCGv_reg iaoq_n_var; - int ntempr, ntempl; - TCGv_reg tempr[8]; + int ntempl; TCGv_tl templ[4]; DisasCond null_cond; @@ -492,13 +491,6 @@ static void cond_free(DisasCond *cond) } } -static TCGv_reg get_temp(DisasContext *ctx) -{ - unsigned i = ctx->ntempr++; - g_assert(i < ARRAY_SIZE(ctx->tempr)); - return ctx->tempr[i] = tcg_temp_new(); -} - #ifndef CONFIG_USER_ONLY static TCGv_tl get_temp_tl(DisasContext *ctx) { @@ -510,7 +502,7 @@ static TCGv_tl get_temp_tl(DisasContext *ctx) static TCGv_reg load_const(DisasContext *ctx, target_sreg v) { - TCGv_reg t = get_temp(ctx); + TCGv_reg t = tcg_temp_new(); tcg_gen_movi_reg(t, v); return t; } @@ -518,7 +510,7 @@ static TCGv_reg load_const(DisasContext *ctx, target_sreg v) static TCGv_reg load_gpr(DisasContext *ctx, unsigned reg) { if (reg == 0) { - TCGv_reg t = get_temp(ctx); + TCGv_reg t = tcg_temp_new(); tcg_gen_movi_reg(t, 0); return t; } else { @@ -529,7 +521,7 @@ static TCGv_reg load_gpr(DisasContext *ctx, unsigned reg) static TCGv_reg dest_gpr(DisasContext *ctx, unsigned reg) { if (reg == 0 || ctx->null_cond.c != TCG_COND_NEVER) { - return get_temp(ctx); + return tcg_temp_new(); } else { return cpu_gr[reg]; } @@ -1071,7 +1063,7 @@ static DisasCond do_unit_cond(unsigned cf, TCGv_reg res, static TCGv_reg do_add_sv(DisasContext *ctx, TCGv_reg res, TCGv_reg in1, TCGv_reg in2) { - TCGv_reg sv = get_temp(ctx); + TCGv_reg sv = tcg_temp_new(); TCGv_reg tmp = tcg_temp_new(); tcg_gen_xor_reg(sv, res, in1); @@ -1085,7 +1077,7 @@ static TCGv_reg do_add_sv(DisasContext *ctx, TCGv_reg res, static TCGv_reg do_sub_sv(DisasContext *ctx, TCGv_reg res, TCGv_reg in1, TCGv_reg in2) { - TCGv_reg sv = get_temp(ctx); + TCGv_reg sv = tcg_temp_new(); TCGv_reg tmp = tcg_temp_new(); tcg_gen_xor_reg(sv, res, in1); @@ -1108,20 +1100,20 @@ static void do_add(DisasContext *ctx, unsigned rt, TCGv_reg in1, cb_msb = NULL; if (shift) { - tmp = get_temp(ctx); + tmp = tcg_temp_new(); tcg_gen_shli_reg(tmp, in1, shift); in1 = tmp; } if (!is_l || cond_need_cb(c)) { TCGv_reg zero = tcg_constant_reg(0); - cb_msb = get_temp(ctx); + cb_msb = tcg_temp_new(); tcg_gen_add2_reg(dest, cb_msb, in1, zero, in2, zero); if (is_c) { tcg_gen_add2_reg(dest, cb_msb, dest, cb_msb, cpu_psw_cb_msb, zero); } if (!is_l) { - cb = get_temp(ctx); + cb = tcg_temp_new(); tcg_gen_xor_reg(cb, in1, in2); tcg_gen_xor_reg(cb, cb, dest); } @@ -1414,11 +1406,11 @@ static void form_gva(DisasContext *ctx, TCGv_tl *pgva, TCGv_reg *pofs, /* Note that RX is mutually exclusive with DISP. */ if (rx) { - ofs = get_temp(ctx); + ofs = tcg_temp_new(); tcg_gen_shli_reg(ofs, cpu_gr[rx], scale); tcg_gen_add_reg(ofs, ofs, base); } else if (disp || modify) { - ofs = get_temp(ctx); + ofs = tcg_temp_new(); tcg_gen_addi_reg(ofs, base, disp); } else { ofs = base; @@ -1538,7 +1530,7 @@ static bool do_load(DisasContext *ctx, unsigned rt, unsigned rb, dest = dest_gpr(ctx, rt); } else { /* Make sure if RT == RB, we see the result of the load. */ - dest = get_temp(ctx); + dest = tcg_temp_new(); } do_load_reg(ctx, dest, rb, rx, scale, disp, sp, modify, mop); save_gpr(ctx, rt, dest); @@ -1854,7 +1846,7 @@ static bool do_ibranch(DisasContext *ctx, TCGv_reg dest, if (link != 0) { copy_iaoq_entry(cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); } - next = get_temp(ctx); + next = tcg_temp_new(); tcg_gen_mov_reg(next, dest); if (is_n) { if (use_nullify_skip(ctx)) { @@ -1896,7 +1888,7 @@ static bool do_ibranch(DisasContext *ctx, TCGv_reg dest, a1 = ctx->null_cond.a1; tmp = tcg_temp_new(); - next = get_temp(ctx); + next = tcg_temp_new(); copy_iaoq_entry(tmp, ctx->iaoq_n, ctx->iaoq_n_var); tcg_gen_movcond_reg(c, next, a0, a1, tmp, dest); @@ -1938,11 +1930,11 @@ static TCGv_reg do_ibranch_priv(DisasContext *ctx, TCGv_reg offset) return offset; case 3: /* Privilege 3 is minimum and is never allowed to increase. */ - dest = get_temp(ctx); + dest = tcg_temp_new(); tcg_gen_ori_reg(dest, offset, 3); break; default: - dest = get_temp(ctx); + dest = tcg_temp_new(); tcg_gen_andi_reg(dest, offset, -4); tcg_gen_ori_reg(dest, dest, ctx->privilege); tcg_gen_movcond_reg(TCG_COND_GTU, dest, dest, offset, dest, offset); @@ -2104,7 +2096,7 @@ static bool trans_mfctl(DisasContext *ctx, arg_mfctl *a) break; } - tmp = get_temp(ctx); + tmp = tcg_temp_new(); tcg_gen_ld_reg(tmp, tcg_env, offsetof(CPUHPPAState, cr[ctl])); save_gpr(ctx, rt, tmp); @@ -2177,7 +2169,7 @@ static bool trans_mtctl(DisasContext *ctx, arg_mtctl *a) case CR_IIAOQ: /* FIXME: Respect PSW_Q bit */ /* The write advances the queue and stores to the back element. */ - tmp = get_temp(ctx); + tmp = tcg_temp_new(); tcg_gen_ld_reg(tmp, tcg_env, offsetof(CPUHPPAState, cr_back[ctl - CR_IIASQ])); tcg_gen_st_reg(tmp, tcg_env, offsetof(CPUHPPAState, cr[ctl])); @@ -2243,7 +2235,7 @@ static bool trans_rsm(DisasContext *ctx, arg_rsm *a) nullify_over(ctx); - tmp = get_temp(ctx); + tmp = tcg_temp_new(); tcg_gen_ld_reg(tmp, tcg_env, offsetof(CPUHPPAState, psw)); tcg_gen_andi_reg(tmp, tmp, ~a->i); gen_helper_swap_system_mask(tmp, tcg_env, tmp); @@ -2263,7 +2255,7 @@ static bool trans_ssm(DisasContext *ctx, arg_ssm *a) nullify_over(ctx); - tmp = get_temp(ctx); + tmp = tcg_temp_new(); tcg_gen_ld_reg(tmp, tcg_env, offsetof(CPUHPPAState, psw)); tcg_gen_ori_reg(tmp, tmp, a->i); gen_helper_swap_system_mask(tmp, tcg_env, tmp); @@ -2283,7 +2275,7 @@ static bool trans_mtsm(DisasContext *ctx, arg_mtsm *a) nullify_over(ctx); reg = load_gpr(ctx, a->r); - tmp = get_temp(ctx); + tmp = tcg_temp_new(); gen_helper_swap_system_mask(tmp, tcg_env, reg); /* Exit the TB to recognize new interrupts. */ @@ -2692,7 +2684,7 @@ static bool do_uaddcm(DisasContext *ctx, arg_rrr_cf *a, bool is_tc) } tcg_r1 = load_gpr(ctx, a->r1); tcg_r2 = load_gpr(ctx, a->r2); - tmp = get_temp(ctx); + tmp = tcg_temp_new(); tcg_gen_not_reg(tmp, tcg_r2); do_unit(ctx, a->t, tcg_r1, tmp, a->cf, is_tc, tcg_gen_add_reg); return nullify_end(ctx); @@ -2714,7 +2706,7 @@ static bool do_dcor(DisasContext *ctx, arg_rr_cf *a, bool is_i) nullify_over(ctx); - tmp = get_temp(ctx); + tmp = tcg_temp_new(); tcg_gen_shri_reg(tmp, cpu_psw_cb, 3); if (!is_i) { tcg_gen_not_reg(tmp, tmp); @@ -2866,7 +2858,7 @@ static bool trans_ldc(DisasContext *ctx, arg_ldst *a) if (a->m) { /* Base register modification. Make sure if RT == RB, we see the result of the load. */ - dest = get_temp(ctx); + dest = tcg_temp_new(); } else { dest = dest_gpr(ctx, a->t); } @@ -2992,7 +2984,7 @@ static bool do_cmpb(DisasContext *ctx, unsigned r, TCGv_reg in1, DisasCond cond; in2 = load_gpr(ctx, r); - dest = get_temp(ctx); + dest = tcg_temp_new(); tcg_gen_sub_reg(dest, in1, in2); @@ -3029,7 +3021,7 @@ static bool do_addb(DisasContext *ctx, unsigned r, TCGv_reg in1, cb_msb = NULL; if (cond_need_cb(c)) { - cb_msb = get_temp(ctx); + cb_msb = tcg_temp_new(); tcg_gen_movi_reg(cb_msb, 0); tcg_gen_add2_reg(dest, cb_msb, in1, cb_msb, in2, cb_msb); } else { @@ -3388,7 +3380,7 @@ static bool trans_be(DisasContext *ctx, arg_be *a) nullify_over(ctx); #endif - tmp = get_temp(ctx); + tmp = tcg_temp_new(); tcg_gen_addi_reg(tmp, load_gpr(ctx, a->b), a->disp); tmp = do_ibranch_priv(ctx, tmp); @@ -3485,7 +3477,7 @@ static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a) static bool trans_blr(DisasContext *ctx, arg_blr *a) { if (a->x) { - TCGv_reg tmp = get_temp(ctx); + TCGv_reg tmp = tcg_temp_new(); tcg_gen_shli_reg(tmp, load_gpr(ctx, a->x), 3); tcg_gen_addi_reg(tmp, tmp, ctx->iaoq_f + 8); /* The computation here never changes privilege level. */ @@ -3503,7 +3495,7 @@ static bool trans_bv(DisasContext *ctx, arg_bv *a) if (a->x == 0) { dest = load_gpr(ctx, a->b); } else { - dest = get_temp(ctx); + dest = tcg_temp_new(); tcg_gen_shli_reg(dest, load_gpr(ctx, a->x), 3); tcg_gen_add_reg(dest, dest, load_gpr(ctx, a->b)); } @@ -3834,7 +3826,7 @@ static bool trans_ftest(DisasContext *ctx, arg_ftest *a) nullify_over(ctx); - t = get_temp(ctx); + t = tcg_temp_new(); tcg_gen_ld32u_reg(t, tcg_env, offsetof(CPUHPPAState, fr0_shadow)); if (a->y == 1) { @@ -4090,9 +4082,7 @@ static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) bound = -(ctx->base.pc_first | TARGET_PAGE_MASK) / 4; ctx->base.max_insns = MIN(ctx->base.max_insns, bound); - ctx->ntempr = 0; ctx->ntempl = 0; - memset(ctx->tempr, 0, sizeof(ctx->tempr)); memset(ctx->templ, 0, sizeof(ctx->templ)); } @@ -4141,7 +4131,7 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) This will be overwritten by a branch. */ if (ctx->iaoq_b == -1) { ctx->iaoq_n = -1; - ctx->iaoq_n_var = get_temp(ctx); + ctx->iaoq_n_var = tcg_temp_new(); tcg_gen_addi_reg(ctx->iaoq_n_var, cpu_iaoq_b, 4); } else { ctx->iaoq_n = ctx->iaoq_b + 4; @@ -4162,13 +4152,9 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) } /* Forget any temporaries allocated. */ - for (i = 0, n = ctx->ntempr; i < n; ++i) { - ctx->tempr[i] = NULL; - } for (i = 0, n = ctx->ntempl; i < n; ++i) { ctx->templ[i] = NULL; } - ctx->ntempr = 0; ctx->ntempl = 0; /* Advance the insn queue. Note that this check also detects From a6779861fd7d5dc8f90e89bd1fae3e689f9b8f65 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 16 Sep 2023 21:05:03 -0700 Subject: [PATCH 579/974] target/hppa: Remove get_temp_tl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace with tcg_temp_new_tl without recording into ctx. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hppa/translate.c | 28 +++------------------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 99b9fc0561..3c4a759628 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -254,9 +254,6 @@ typedef struct DisasContext { target_ureg iaoq_n; TCGv_reg iaoq_n_var; - int ntempl; - TCGv_tl templ[4]; - DisasCond null_cond; TCGLabel *null_lab; @@ -491,15 +488,6 @@ static void cond_free(DisasCond *cond) } } -#ifndef CONFIG_USER_ONLY -static TCGv_tl get_temp_tl(DisasContext *ctx) -{ - unsigned i = ctx->ntempl++; - g_assert(i < ARRAY_SIZE(ctx->templ)); - return ctx->templ[i] = tcg_temp_new_tl(); -} -#endif - static TCGv_reg load_const(DisasContext *ctx, target_sreg v) { TCGv_reg t = tcg_temp_new(); @@ -1374,7 +1362,7 @@ static TCGv_i64 space_select(DisasContext *ctx, int sp, TCGv_reg base) if (sp < 0) { sp = ~sp; } - spc = get_temp_tl(ctx); + spc = tcg_temp_new_tl(); load_spr(ctx, spc, sp); return spc; } @@ -1384,7 +1372,7 @@ static TCGv_i64 space_select(DisasContext *ctx, int sp, TCGv_reg base) ptr = tcg_temp_new_ptr(); tmp = tcg_temp_new(); - spc = get_temp_tl(ctx); + spc = tcg_temp_new_tl(); tcg_gen_shri_reg(tmp, base, TARGET_REGISTER_BITS - 5); tcg_gen_andi_reg(tmp, tmp, 030); @@ -1420,7 +1408,7 @@ static void form_gva(DisasContext *ctx, TCGv_tl *pgva, TCGv_reg *pofs, #ifdef CONFIG_USER_ONLY *pgva = (modify <= 0 ? ofs : base); #else - TCGv_tl addr = get_temp_tl(ctx); + TCGv_tl addr = tcg_temp_new_tl(); tcg_gen_extu_reg_tl(addr, modify <= 0 ? ofs : base); if (ctx->tb_flags & PSW_W) { tcg_gen_andi_tl(addr, addr, 0x3fffffffffffffffull); @@ -4081,9 +4069,6 @@ static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) /* Bound the number of instructions by those left on the page. */ bound = -(ctx->base.pc_first | TARGET_PAGE_MASK) / 4; ctx->base.max_insns = MIN(ctx->base.max_insns, bound); - - ctx->ntempl = 0; - memset(ctx->templ, 0, sizeof(ctx->templ)); } static void hppa_tr_tb_start(DisasContextBase *dcbase, CPUState *cs) @@ -4112,7 +4097,6 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) DisasContext *ctx = container_of(dcbase, DisasContext, base); CPUHPPAState *env = cpu_env(cs); DisasJumpType ret; - int i, n; /* Execute one insn. */ #ifdef CONFIG_USER_ONLY @@ -4151,12 +4135,6 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) } } - /* Forget any temporaries allocated. */ - for (i = 0, n = ctx->ntempl; i < n; ++i) { - ctx->templ[i] = NULL; - } - ctx->ntempl = 0; - /* Advance the insn queue. Note that this check also detects a priority change within the instruction queue. */ if (ret == DISAS_NEXT && ctx->iaoq_b != ctx->iaoq_f + 4) { From d4e5803316a85dcff78490e6c18a0a84371220e6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 20 Sep 2023 11:47:02 +0200 Subject: [PATCH 580/974] target/hppa: Remove load_const MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace with tcg_constant_reg. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hppa/translate.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 3c4a759628..c8384fccd9 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -488,13 +488,6 @@ static void cond_free(DisasCond *cond) } } -static TCGv_reg load_const(DisasContext *ctx, target_sreg v) -{ - TCGv_reg t = tcg_temp_new(); - tcg_gen_movi_reg(t, v); - return t; -} - static TCGv_reg load_gpr(DisasContext *ctx, unsigned reg) { if (reg == 0) { @@ -1164,7 +1157,7 @@ static bool do_add_imm(DisasContext *ctx, arg_rri_cf *a, if (a->cf) { nullify_over(ctx); } - tcg_im = load_const(ctx, a->i); + tcg_im = tcg_constant_reg(a->i); tcg_r2 = load_gpr(ctx, a->r); do_add(ctx, a->t, tcg_im, tcg_r2, 0, 0, is_tsv, is_tc, 0, a->cf); return nullify_end(ctx); @@ -1253,7 +1246,7 @@ static bool do_sub_imm(DisasContext *ctx, arg_rri_cf *a, bool is_tsv) if (a->cf) { nullify_over(ctx); } - tcg_im = load_const(ctx, a->i); + tcg_im = tcg_constant_reg(a->i); tcg_r2 = load_gpr(ctx, a->r); do_sub(ctx, a->t, tcg_im, tcg_r2, is_tsv, 0, 0, a->cf); return nullify_end(ctx); @@ -2808,7 +2801,7 @@ static bool trans_cmpiclr(DisasContext *ctx, arg_rri_cf *a) nullify_over(ctx); } - tcg_im = load_const(ctx, a->i); + tcg_im = tcg_constant_reg(a->i); tcg_r2 = load_gpr(ctx, a->r); do_cmpclr(ctx, a->t, tcg_im, tcg_r2, a->cf); @@ -2994,7 +2987,7 @@ static bool trans_cmpb(DisasContext *ctx, arg_cmpb *a) static bool trans_cmpbi(DisasContext *ctx, arg_cmpbi *a) { nullify_over(ctx); - return do_cmpb(ctx, a->r, load_const(ctx, a->i), a->c, a->f, a->n, a->disp); + return do_cmpb(ctx, a->r, tcg_constant_reg(a->i), a->c, a->f, a->n, a->disp); } static bool do_addb(DisasContext *ctx, unsigned r, TCGv_reg in1, @@ -3033,7 +3026,7 @@ static bool trans_addb(DisasContext *ctx, arg_addb *a) static bool trans_addbi(DisasContext *ctx, arg_addbi *a) { nullify_over(ctx); - return do_addb(ctx, a->r, load_const(ctx, a->i), a->c, a->f, a->n, a->disp); + return do_addb(ctx, a->r, tcg_constant_reg(a->i), a->c, a->f, a->n, a->disp); } static bool trans_bb_sar(DisasContext *ctx, arg_bb_sar *a) @@ -3345,7 +3338,7 @@ static bool trans_depwi_sar(DisasContext *ctx, arg_depwi_sar *a) if (a->c) { nullify_over(ctx); } - return do_depw_sar(ctx, a->t, a->c, a->nz, a->clen, load_const(ctx, a->i)); + return do_depw_sar(ctx, a->t, a->c, a->nz, a->clen, tcg_constant_reg(a->i)); } static bool trans_be(DisasContext *ctx, arg_be *a) @@ -3852,7 +3845,7 @@ static bool trans_ftest(DisasContext *ctx, arg_ftest *a) return true; } if (inv) { - TCGv_reg c = load_const(ctx, mask); + TCGv_reg c = tcg_constant_reg(mask); tcg_gen_or_reg(t, t, c); ctx->null_cond = cond_make(TCG_COND_EQ, t, c); } else { From 0238e678ebdfeb3a74f947f2c3cde401ae60267f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 16 Sep 2023 16:19:27 -0700 Subject: [PATCH 581/974] target/hppa: Fix hppa64 case in machine.c Typo of VMSTATE_UINTTR_V and VMSTATE_UINTTR_ARRAY_V macros. Signed-off-by: Richard Henderson --- target/hppa/machine.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/hppa/machine.c b/target/hppa/machine.c index ab3e8c81fa..61ae942ff1 100644 --- a/target/hppa/machine.c +++ b/target/hppa/machine.c @@ -24,9 +24,9 @@ #if TARGET_REGISTER_BITS == 64 #define qemu_put_betr qemu_put_be64 #define qemu_get_betr qemu_get_be64 -#define VMSTATE_UINTTL_V(_f, _s, _v) \ +#define VMSTATE_UINTTR_V(_f, _s, _v) \ VMSTATE_UINT64_V(_f, _s, _v) -#define VMSTATE_UINTTL_ARRAY_V(_f, _s, _n, _v) \ +#define VMSTATE_UINTTR_ARRAY_V(_f, _s, _n, _v) \ VMSTATE_UINT64_ARRAY_V(_f, _s, _n, _v) #else #define qemu_put_betr qemu_put_be32 From c1f55d9795b69f17f002b7d6e8580bcd23f49be5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 16 Sep 2023 16:20:28 -0700 Subject: [PATCH 582/974] target/hppa: Fix load in do_load_32 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The destination is TCGv_i32, so use tcg_gen_qemu_ld_i32 not tcg_gen_qemu_ld_reg. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hppa/translate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index c8384fccd9..20e44ed528 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1430,7 +1430,7 @@ static void do_load_32(DisasContext *ctx, TCGv_i32 dest, unsigned rb, form_gva(ctx, &addr, &ofs, rb, rx, scale, disp, sp, modify, ctx->mmu_idx == MMU_PHYS_IDX); - tcg_gen_qemu_ld_reg(dest, addr, ctx->mmu_idx, mop | UNALIGN(ctx)); + tcg_gen_qemu_ld_i32(dest, addr, ctx->mmu_idx, mop | UNALIGN(ctx)); if (modify) { save_gpr(ctx, rb, ofs); } From e1d635e871f4e0dcea7ec08309509bbb82c2047c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 16 Sep 2023 16:22:14 -0700 Subject: [PATCH 583/974] target/hppa: Truncate rotate count in trans_shrpw_sar When forcing rotate by i32, the shift count must be as well. Signed-off-by: Richard Henderson --- target/hppa/translate.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 20e44ed528..d6ccce020a 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -3105,8 +3105,11 @@ static bool trans_shrpw_sar(DisasContext *ctx, arg_shrpw_sar *a) tcg_gen_shr_reg(dest, dest, cpu_sar); } else if (a->r1 == a->r2) { TCGv_i32 t32 = tcg_temp_new_i32(); + TCGv_i32 s32 = tcg_temp_new_i32(); + tcg_gen_trunc_reg_i32(t32, load_gpr(ctx, a->r2)); - tcg_gen_rotr_i32(t32, t32, cpu_sar); + tcg_gen_trunc_reg_i32(s32, cpu_sar); + tcg_gen_rotr_i32(t32, t32, s32); tcg_gen_extu_i32_reg(dest, t32); } else { TCGv_i64 t = tcg_temp_new_i64(); From 72ca87535e5055852c3fa5e3913c9190e0f11911 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 16 Sep 2023 16:28:23 -0700 Subject: [PATCH 584/974] target/hppa: Fix trans_ds for hppa64 This instruction always uses the input carry from bit 32, but produces all 16 output carry bits. Signed-off-by: Richard Henderson --- target/hppa/translate.c | 48 +++++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index d6ccce020a..8ba95ae320 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -803,6 +803,12 @@ static bool cond_need_cb(int c) return c == 4 || c == 5; } +/* Need extensions from TCGv_i32 to TCGv_reg. */ +static bool cond_need_ext(DisasContext *ctx, bool d) +{ + return TARGET_REGISTER_BITS == 64 && !d; +} + /* * Compute conditional for arithmetic. See Page 5-3, Table 5-1, of * the Parisc 1.1 Architecture Reference Manual for details. @@ -1040,6 +1046,22 @@ static DisasCond do_unit_cond(unsigned cf, TCGv_reg res, return cond; } +static TCGv_reg get_carry(DisasContext *ctx, bool d, + TCGv_reg cb, TCGv_reg cb_msb) +{ + if (cond_need_ext(ctx, d)) { + TCGv_reg t = tcg_temp_new(); + tcg_gen_extract_reg(t, cb, 32, 1); + return t; + } + return cb_msb; +} + +static TCGv_reg get_psw_carry(DisasContext *ctx, bool d) +{ + return get_carry(ctx, d, cpu_psw_cb, cpu_psw_cb_msb); +} + /* Compute signed overflow for addition. */ static TCGv_reg do_add_sv(DisasContext *ctx, TCGv_reg res, TCGv_reg in1, TCGv_reg in2) @@ -2712,6 +2734,7 @@ static bool trans_dcor_i(DisasContext *ctx, arg_rr_cf *a) static bool trans_ds(DisasContext *ctx, arg_rrr_cf *a) { TCGv_reg dest, add1, add2, addc, zero, in1, in2; + TCGv_reg cout; nullify_over(ctx); @@ -2726,18 +2749,20 @@ static bool trans_ds(DisasContext *ctx, arg_rrr_cf *a) /* Form R1 << 1 | PSW[CB]{8}. */ tcg_gen_add_reg(add1, in1, in1); - tcg_gen_add_reg(add1, add1, cpu_psw_cb_msb); + tcg_gen_add_reg(add1, add1, get_psw_carry(ctx, false)); - /* Add or subtract R2, depending on PSW[V]. Proper computation of - carry{8} requires that we subtract via + ~R2 + 1, as described in - the manual. By extracting and masking V, we can produce the - proper inputs to the addition without movcond. */ - tcg_gen_sari_reg(addc, cpu_psw_v, TARGET_REGISTER_BITS - 1); + /* + * Add or subtract R2, depending on PSW[V]. Proper computation of + * carry requires that we subtract via + ~R2 + 1, as described in + * the manual. By extracting and masking V, we can produce the + * proper inputs to the addition without movcond. + */ + tcg_gen_sextract_reg(addc, cpu_psw_v, 31, 1); tcg_gen_xor_reg(add2, in2, addc); tcg_gen_andi_reg(addc, addc, 1); - /* ??? This is only correct for 32-bit. */ - tcg_gen_add2_i32(dest, cpu_psw_cb_msb, add1, zero, add2, zero); - tcg_gen_add2_i32(dest, cpu_psw_cb_msb, dest, cpu_psw_cb_msb, addc, zero); + + tcg_gen_add2_reg(dest, cpu_psw_cb_msb, add1, zero, add2, zero); + tcg_gen_add2_reg(dest, cpu_psw_cb_msb, dest, cpu_psw_cb_msb, addc, zero); /* Write back the result register. */ save_gpr(ctx, a->t, dest); @@ -2747,7 +2772,8 @@ static bool trans_ds(DisasContext *ctx, arg_rrr_cf *a) tcg_gen_xor_reg(cpu_psw_cb, cpu_psw_cb, dest); /* Write back PSW[V] for the division step. */ - tcg_gen_neg_reg(cpu_psw_v, cpu_psw_cb_msb); + cout = get_psw_carry(ctx, false); + tcg_gen_neg_reg(cpu_psw_v, cout); tcg_gen_xor_reg(cpu_psw_v, cpu_psw_v, in2); /* Install the new nullification. */ @@ -2757,7 +2783,7 @@ static bool trans_ds(DisasContext *ctx, arg_rrr_cf *a) /* ??? The lshift is supposed to contribute to overflow. */ sv = do_add_sv(ctx, dest, add1, add2); } - ctx->null_cond = do_cond(a->cf, dest, cpu_psw_cb_msb, sv); + ctx->null_cond = do_cond(a->cf, dest, cout, sv); } return nullify_end(ctx); From bdcccc17acb3f4cbf85cc81601edc1f87e76e088 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 17 Oct 2023 16:16:03 -0700 Subject: [PATCH 585/974] target/hppa: Fix do_add, do_sub for hppa64 Select the proper carry bit for input to the arithmetic and for output for the condition. Signed-off-by: Richard Henderson --- target/hppa/translate.c | 50 ++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 8ba95ae320..b0cd12a2d0 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1094,13 +1094,15 @@ static void do_add(DisasContext *ctx, unsigned rt, TCGv_reg in1, TCGv_reg in2, unsigned shift, bool is_l, bool is_tsv, bool is_tc, bool is_c, unsigned cf) { - TCGv_reg dest, cb, cb_msb, sv, tmp; + TCGv_reg dest, cb, cb_msb, cb_cond, sv, tmp; unsigned c = cf >> 1; DisasCond cond; + bool d = false; dest = tcg_temp_new(); cb = NULL; cb_msb = NULL; + cb_cond = NULL; if (shift) { tmp = tcg_temp_new(); @@ -1111,19 +1113,22 @@ static void do_add(DisasContext *ctx, unsigned rt, TCGv_reg in1, if (!is_l || cond_need_cb(c)) { TCGv_reg zero = tcg_constant_reg(0); cb_msb = tcg_temp_new(); + cb = tcg_temp_new(); + tcg_gen_add2_reg(dest, cb_msb, in1, zero, in2, zero); if (is_c) { - tcg_gen_add2_reg(dest, cb_msb, dest, cb_msb, cpu_psw_cb_msb, zero); + tcg_gen_add2_reg(dest, cb_msb, dest, cb_msb, + get_psw_carry(ctx, d), zero); } - if (!is_l) { - cb = tcg_temp_new(); - tcg_gen_xor_reg(cb, in1, in2); - tcg_gen_xor_reg(cb, cb, dest); + tcg_gen_xor_reg(cb, in1, in2); + tcg_gen_xor_reg(cb, cb, dest); + if (cond_need_cb(c)) { + cb_cond = get_carry(ctx, d, cb, cb_msb); } } else { tcg_gen_add_reg(dest, in1, in2); if (is_c) { - tcg_gen_add_reg(dest, dest, cpu_psw_cb_msb); + tcg_gen_add_reg(dest, dest, get_psw_carry(ctx, d)); } } @@ -1138,7 +1143,7 @@ static void do_add(DisasContext *ctx, unsigned rt, TCGv_reg in1, } /* Emit any conditional trap before any writeback. */ - cond = do_cond(cf, dest, cb_msb, sv); + cond = do_cond(cf, dest, cb_cond, sv); if (is_tc) { tmp = tcg_temp_new(); tcg_gen_setcond_reg(cond.c, tmp, cond.a0, cond.a1); @@ -1192,6 +1197,7 @@ static void do_sub(DisasContext *ctx, unsigned rt, TCGv_reg in1, TCGv_reg dest, sv, cb, cb_msb, zero, tmp; unsigned c = cf >> 1; DisasCond cond; + bool d = false; dest = tcg_temp_new(); cb = tcg_temp_new(); @@ -1201,15 +1207,17 @@ static void do_sub(DisasContext *ctx, unsigned rt, TCGv_reg in1, if (is_b) { /* DEST,C = IN1 + ~IN2 + C. */ tcg_gen_not_reg(cb, in2); - tcg_gen_add2_reg(dest, cb_msb, in1, zero, cpu_psw_cb_msb, zero); + tcg_gen_add2_reg(dest, cb_msb, in1, zero, get_psw_carry(ctx, d), zero); tcg_gen_add2_reg(dest, cb_msb, dest, cb_msb, cb, zero); tcg_gen_xor_reg(cb, cb, in1); tcg_gen_xor_reg(cb, cb, dest); } else { - /* DEST,C = IN1 + ~IN2 + 1. We can produce the same result in fewer - operations by seeding the high word with 1 and subtracting. */ - tcg_gen_movi_reg(cb_msb, 1); - tcg_gen_sub2_reg(dest, cb_msb, in1, cb_msb, in2, zero); + /* + * DEST,C = IN1 + ~IN2 + 1. We can produce the same result in fewer + * operations by seeding the high word with 1 and subtracting. + */ + TCGv_reg one = tcg_constant_reg(1); + tcg_gen_sub2_reg(dest, cb_msb, in1, one, in2, zero); tcg_gen_eqv_reg(cb, in1, in2); tcg_gen_xor_reg(cb, cb, dest); } @@ -1227,7 +1235,7 @@ static void do_sub(DisasContext *ctx, unsigned rt, TCGv_reg in1, if (!is_b) { cond = do_sub_cond(cf, dest, in1, in2, sv); } else { - cond = do_cond(cf, dest, cb_msb, sv); + cond = do_cond(cf, dest, get_carry(ctx, d, cb, cb_msb), sv); } /* Emit any conditional trap before any writeback. */ @@ -3019,18 +3027,24 @@ static bool trans_cmpbi(DisasContext *ctx, arg_cmpbi *a) static bool do_addb(DisasContext *ctx, unsigned r, TCGv_reg in1, unsigned c, unsigned f, unsigned n, int disp) { - TCGv_reg dest, in2, sv, cb_msb; + TCGv_reg dest, in2, sv, cb_cond; DisasCond cond; + bool d = false; in2 = load_gpr(ctx, r); dest = tcg_temp_new(); sv = NULL; - cb_msb = NULL; + cb_cond = NULL; if (cond_need_cb(c)) { - cb_msb = tcg_temp_new(); + TCGv_reg cb = tcg_temp_new(); + TCGv_reg cb_msb = tcg_temp_new(); + tcg_gen_movi_reg(cb_msb, 0); tcg_gen_add2_reg(dest, cb_msb, in1, cb_msb, in2, cb_msb); + tcg_gen_xor_reg(cb, in1, in2); + tcg_gen_xor_reg(cb, cb, dest); + cb_cond = get_carry(ctx, d, cb, cb_msb); } else { tcg_gen_add_reg(dest, in1, in2); } @@ -3038,7 +3052,7 @@ static bool do_addb(DisasContext *ctx, unsigned r, TCGv_reg in1, sv = do_add_sv(ctx, dest, in1, in2); } - cond = do_cond(c * 2 + f, dest, cb_msb, sv); + cond = do_cond(c * 2 + f, dest, cb_cond, sv); save_gpr(ctx, r, dest); return do_cbranch(ctx, disp, n, &cond); } From 1e9ab9fbe06b6b343409029f5be2f184d6b69fde Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 17 Oct 2023 16:39:47 -0700 Subject: [PATCH 586/974] target/hppa: Fix bb_sar for hppa64 Signed-off-by: Richard Henderson --- target/hppa/translate.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index b0cd12a2d0..ffa367b91f 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -3073,14 +3073,21 @@ static bool trans_bb_sar(DisasContext *ctx, arg_bb_sar *a) { TCGv_reg tmp, tcg_r; DisasCond cond; + bool d = false; nullify_over(ctx); tmp = tcg_temp_new(); tcg_r = load_gpr(ctx, a->r); - tcg_gen_shl_reg(tmp, tcg_r, cpu_sar); + if (cond_need_ext(ctx, d)) { + /* Force shift into [32,63] */ + tcg_gen_ori_reg(tmp, cpu_sar, 32); + tcg_gen_shl_reg(tmp, tcg_r, tmp); + } else { + tcg_gen_shl_reg(tmp, tcg_r, cpu_sar); + } - cond = cond_make_0(a->c ? TCG_COND_GE : TCG_COND_LT, tmp); + cond = cond_make_0_tmp(a->c ? TCG_COND_GE : TCG_COND_LT, tmp); return do_cbranch(ctx, a->disp, a->n, &cond); } @@ -3088,12 +3095,15 @@ static bool trans_bb_imm(DisasContext *ctx, arg_bb_imm *a) { TCGv_reg tmp, tcg_r; DisasCond cond; + bool d = false; + int p; nullify_over(ctx); tmp = tcg_temp_new(); tcg_r = load_gpr(ctx, a->r); - tcg_gen_shli_reg(tmp, tcg_r, a->p); + p = a->p | (cond_need_ext(ctx, d) ? 32 : 0); + tcg_gen_shli_reg(tmp, tcg_r, p); cond = cond_make_0(a->c ? TCG_COND_GE : TCG_COND_LT, tmp); return do_cbranch(ctx, a->disp, a->n, &cond); From d781cb7798e41141f377784257e27f615041603d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 19 Sep 2023 16:07:14 +0200 Subject: [PATCH 587/974] target/hppa: Fix extrw and depw with sar for hppa64 These are 32-bit operations regardless of processor. Signed-off-by: Richard Henderson --- target/hppa/translate.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index ffa367b91f..ed88f724ce 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -3230,7 +3230,9 @@ static bool trans_extrw_sar(DisasContext *ctx, arg_extrw_sar *a) tmp = tcg_temp_new(); /* Recall that SAR is using big-endian bit numbering. */ - tcg_gen_xori_reg(tmp, cpu_sar, TARGET_REGISTER_BITS - 1); + tcg_gen_andi_reg(tmp, cpu_sar, 31); + tcg_gen_xori_reg(tmp, tmp, 31); + if (a->se) { tcg_gen_sar_reg(dest, src, tmp); tcg_gen_sextract_reg(dest, dest, 0, len); @@ -3355,7 +3357,8 @@ static bool do_depw_sar(DisasContext *ctx, unsigned rt, unsigned c, tmp = tcg_temp_new(); /* Convert big-endian bit numbering in SAR to left-shift. */ - tcg_gen_xori_reg(shift, cpu_sar, TARGET_REGISTER_BITS - 1); + tcg_gen_andi_reg(shift, cpu_sar, 31); + tcg_gen_xori_reg(shift, shift, 31); mask = tcg_temp_new(); tcg_gen_movi_reg(mask, msb + (msb - 1)); From bd6243a33fed93844ea24d77ed62d35f13d644e7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 17 Sep 2023 15:31:47 -0700 Subject: [PATCH 588/974] target/hppa: Introduce TYPE_HPPA64_CPU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prepare for the qemu binary supporting both pa10 and pa20 at the same time. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hppa/cpu-qom.h | 1 + target/hppa/cpu.c | 33 ++++++++++++++++++--------------- target/hppa/cpu.h | 5 +++++ target/hppa/translate.c | 2 ++ 4 files changed, 26 insertions(+), 15 deletions(-) diff --git a/target/hppa/cpu-qom.h b/target/hppa/cpu-qom.h index b96e0318c7..4a85ebf5e0 100644 --- a/target/hppa/cpu-qom.h +++ b/target/hppa/cpu-qom.h @@ -24,6 +24,7 @@ #include "qom/object.h" #define TYPE_HPPA_CPU "hppa-cpu" +#define TYPE_HPPA64_CPU "hppa64-cpu" OBJECT_DECLARE_CPU_TYPE(HPPACPU, HPPACPUClass, HPPA_CPU) diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 5e1240c631..07fae42bb8 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -209,20 +209,23 @@ static void hppa_cpu_class_init(ObjectClass *oc, void *data) cc->tcg_ops = &hppa_tcg_ops; } -static const TypeInfo hppa_cpu_type_info = { - .name = TYPE_HPPA_CPU, - .parent = TYPE_CPU, - .instance_size = sizeof(HPPACPU), - .instance_align = __alignof(HPPACPU), - .instance_init = hppa_cpu_initfn, - .abstract = false, - .class_size = sizeof(HPPACPUClass), - .class_init = hppa_cpu_class_init, +static const TypeInfo hppa_cpu_type_infos[] = { + { + .name = TYPE_HPPA_CPU, + .parent = TYPE_CPU, + .instance_size = sizeof(HPPACPU), + .instance_align = __alignof(HPPACPU), + .instance_init = hppa_cpu_initfn, + .abstract = false, + .class_size = sizeof(HPPACPUClass), + .class_init = hppa_cpu_class_init, + }, +#ifdef TARGET_HPPA64 + { + .name = TYPE_HPPA64_CPU, + .parent = TYPE_HPPA_CPU, + }, +#endif }; -static void hppa_cpu_register_types(void) -{ - type_register_static(&hppa_cpu_type_info); -} - -type_init(hppa_cpu_register_types) +DEFINE_TYPES(hppa_cpu_type_infos) diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 08de894393..0ac307e0e9 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -272,6 +272,11 @@ struct ArchCPU { #include "exec/cpu-all.h" +static inline bool hppa_is_pa20(CPUHPPAState *env) +{ + return object_dynamic_cast(OBJECT(env_cpu(env)), TYPE_HPPA64_CPU) != NULL; +} + static inline int cpu_mmu_index(CPUHPPAState *env, bool ifetch) { #ifdef CONFIG_USER_ONLY diff --git a/target/hppa/translate.c b/target/hppa/translate.c index ed88f724ce..44c9911720 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -262,6 +262,7 @@ typedef struct DisasContext { int mmu_idx; int privilege; bool psw_n_nonzero; + bool is_pa20; #ifdef CONFIG_USER_ONLY MemOp unalign; @@ -4091,6 +4092,7 @@ static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->cs = cs; ctx->tb_flags = ctx->base.tb->flags; + ctx->is_pa20 = hppa_is_pa20(cpu_env(cs)); #ifdef CONFIG_USER_ONLY ctx->privilege = MMU_IDX_TO_PRIV(MMU_USER_IDX); From 9cf2112be4fe84d41083435e44fa146d13d3f8d7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 12 Oct 2023 17:46:55 -0700 Subject: [PATCH 589/974] target/hppa: Make HPPA_BTLB_ENTRIES variable Depend on hppa_is_pa20. Signed-off-by: Richard Henderson --- hw/hppa/machine.c | 9 +++------ target/hppa/cpu.h | 19 +++++++++++-------- target/hppa/machine.c | 3 ++- target/hppa/mem_helper.c | 40 ++++++++++++++++++++++------------------ 4 files changed, 38 insertions(+), 33 deletions(-) diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index 67d4d1b5e0..85682e6bab 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -185,6 +185,7 @@ static FWCfgState *create_fw_cfg(MachineState *ms, PCIBus *pci_bus) uint64_t val; const char qemu_version[] = QEMU_VERSION; MachineClass *mc = MACHINE_GET_CLASS(ms); + int btlb_entries = HPPA_BTLB_ENTRIES(&cpu[0]->env); int len; fw_cfg = fw_cfg_init_mem(FW_CFG_IO_BASE, FW_CFG_IO_BASE + 4); @@ -196,11 +197,11 @@ static FWCfgState *create_fw_cfg(MachineState *ms, PCIBus *pci_bus) fw_cfg_add_file(fw_cfg, "/etc/firmware-min-version", g_memdup(&val, sizeof(val)), sizeof(val)); - val = cpu_to_le64(HPPA_TLB_ENTRIES - HPPA_BTLB_ENTRIES); + val = cpu_to_le64(HPPA_TLB_ENTRIES - btlb_entries); fw_cfg_add_file(fw_cfg, "/etc/cpu/tlb_entries", g_memdup(&val, sizeof(val)), sizeof(val)); - val = cpu_to_le64(HPPA_BTLB_ENTRIES); + val = cpu_to_le64(btlb_entries); fw_cfg_add_file(fw_cfg, "/etc/cpu/btlb_entries", g_memdup(&val, sizeof(val)), sizeof(val)); @@ -608,10 +609,6 @@ static void hppa_machine_reset(MachineState *ms, ShutdownCause reason) cs->exception_index = -1; cs->halted = 0; - - /* clear any existing TLB and BTLB entries */ - memset(cpu[i]->env.tlb, 0, sizeof(cpu[i]->env.tlb)); - cpu[i]->env.tlb_last = HPPA_BTLB_ENTRIES; } /* already initialized by machine_hppa_init()? */ diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 0ac307e0e9..48ddcffb8a 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -227,15 +227,13 @@ typedef struct CPUArchState { target_ureg cr_back[2]; /* back of cr17/cr18 */ target_ureg shadow[7]; /* shadow registers */ - /* ??? The number of entries isn't specified by the architecture. */ -#ifdef TARGET_HPPA64 -#define HPPA_BTLB_FIXED 0 /* BTLBs are not supported in 64-bit machines */ -#else -#define HPPA_BTLB_FIXED 16 -#endif -#define HPPA_BTLB_VARIABLE 0 + /* + * ??? The number of entries isn't specified by the architecture. + * BTLBs are not supported in 64-bit machines. + */ +#define PA10_BTLB_FIXED 16 +#define PA10_BTLB_VARIABLE 0 #define HPPA_TLB_ENTRIES 256 -#define HPPA_BTLB_ENTRIES (HPPA_BTLB_FIXED + HPPA_BTLB_VARIABLE) /* Index for round-robin tlb eviction. */ uint32_t tlb_last; @@ -277,6 +275,11 @@ static inline bool hppa_is_pa20(CPUHPPAState *env) return object_dynamic_cast(OBJECT(env_cpu(env)), TYPE_HPPA64_CPU) != NULL; } +static inline int HPPA_BTLB_ENTRIES(CPUHPPAState *env) +{ + return hppa_is_pa20(env) ? 0 : PA10_BTLB_FIXED + PA10_BTLB_VARIABLE; +} + static inline int cpu_mmu_index(CPUHPPAState *env, bool ifetch) { #ifdef CONFIG_USER_ONLY diff --git a/target/hppa/machine.c b/target/hppa/machine.c index 61ae942ff1..473305ffea 100644 --- a/target/hppa/machine.c +++ b/target/hppa/machine.c @@ -139,6 +139,7 @@ static int tlb_pre_load(void *opaque) static int tlb_post_load(void *opaque, int version_id) { CPUHPPAState *env = opaque; + uint32_t btlb_entries = HPPA_BTLB_ENTRIES(env); HPPATLBEntry **unused = &env->tlb_unused; HPPATLBEntry *partial = NULL; @@ -152,7 +153,7 @@ static int tlb_post_load(void *opaque, int version_id) if (e->entry_valid) { interval_tree_insert(&e->itree, &env->tlb_root); - } else if (i < HPPA_BTLB_ENTRIES) { + } else if (i < btlb_entries) { /* btlb not in unused list */ } else if (partial == NULL && e->itree.start < e->itree.last) { partial = e; diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c index b1773ece61..327fb20c17 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -57,7 +57,7 @@ static void hppa_flush_tlb_ent(CPUHPPAState *env, HPPATLBEntry *ent, HPPA_MMU_FLUSH_MASK, TARGET_LONG_BITS); /* Never clear BTLBs, unless forced to do so. */ - is_btlb = ent < &env->tlb[HPPA_BTLB_ENTRIES]; + is_btlb = ent < &env->tlb[HPPA_BTLB_ENTRIES(env)]; if (is_btlb && !force_flush_btlb) { return; } @@ -93,10 +93,11 @@ static HPPATLBEntry *hppa_alloc_tlb_ent(CPUHPPAState *env) HPPATLBEntry *ent = env->tlb_unused; if (ent == NULL) { + uint32_t btlb_entries = HPPA_BTLB_ENTRIES(env); uint32_t i = env->tlb_last; - if (i < HPPA_BTLB_ENTRIES || i >= ARRAY_SIZE(env->tlb)) { - i = HPPA_BTLB_ENTRIES; + if (i < btlb_entries || i >= ARRAY_SIZE(env->tlb)) { + i = btlb_entries; } env->tlb_last = i + 1; @@ -385,23 +386,24 @@ void HELPER(ptlb)(CPUHPPAState *env, target_ulong addr) void hppa_ptlbe(CPUHPPAState *env) { + uint32_t btlb_entries = HPPA_BTLB_ENTRIES(env); uint32_t i; /* Zap the (non-btlb) tlb entries themselves. */ - memset(&env->tlb[HPPA_BTLB_ENTRIES], 0, - sizeof(env->tlb) - HPPA_BTLB_ENTRIES * sizeof(env->tlb[0])); - env->tlb_last = HPPA_BTLB_ENTRIES; + memset(&env->tlb[btlb_entries], 0, + sizeof(env->tlb) - btlb_entries * sizeof(env->tlb[0])); + env->tlb_last = btlb_entries; env->tlb_partial = NULL; /* Put them all onto the unused list. */ - env->tlb_unused = &env->tlb[HPPA_BTLB_ENTRIES]; - for (i = HPPA_BTLB_ENTRIES; i < ARRAY_SIZE(env->tlb) - 1; ++i) { + env->tlb_unused = &env->tlb[btlb_entries]; + for (i = btlb_entries; i < ARRAY_SIZE(env->tlb) - 1; ++i) { env->tlb[i].unused_next = &env->tlb[i + 1]; } /* Re-initialize the interval tree with only the btlb entries. */ memset(&env->tlb_root, 0, sizeof(env->tlb_root)); - for (i = 0; i < HPPA_BTLB_ENTRIES; ++i) { + for (i = 0; i < btlb_entries; ++i) { if (env->tlb[i].entry_valid) { interval_tree_insert(&env->tlb[i].itree, &env->tlb_root); } @@ -473,12 +475,14 @@ void HELPER(diag_btlb)(CPUHPPAState *env) HPPATLBEntry *btlb; uint64_t virt_page; uint32_t *vaddr; + uint32_t btlb_entries = HPPA_BTLB_ENTRIES(env); -#ifdef TARGET_HPPA64 /* BTLBs are not supported on 64-bit CPUs */ - env->gr[28] = -1; /* nonexistent procedure */ - return; -#endif + if (btlb_entries == 0) { + env->gr[28] = -1; /* nonexistent procedure */ + return; + } + env->gr[28] = 0; /* PDC_OK */ switch (env->gr[25]) { @@ -492,8 +496,8 @@ void HELPER(diag_btlb)(CPUHPPAState *env) } else { vaddr[0] = cpu_to_be32(1); vaddr[1] = cpu_to_be32(16 * 1024); - vaddr[2] = cpu_to_be32(HPPA_BTLB_FIXED); - vaddr[3] = cpu_to_be32(HPPA_BTLB_VARIABLE); + vaddr[2] = cpu_to_be32(PA10_BTLB_FIXED); + vaddr[3] = cpu_to_be32(PA10_BTLB_VARIABLE); } break; case 1: @@ -510,7 +514,7 @@ void HELPER(diag_btlb)(CPUHPPAState *env) (long long) virt_page << TARGET_PAGE_BITS, (long long) (virt_page + len) << TARGET_PAGE_BITS, (long long) virt_page, phys_page, len, slot); - if (slot < HPPA_BTLB_ENTRIES) { + if (slot < btlb_entries) { btlb = &env->tlb[slot]; /* Force flush of possibly existing BTLB entry. */ @@ -532,7 +536,7 @@ void HELPER(diag_btlb)(CPUHPPAState *env) slot = env->gr[22]; qemu_log_mask(CPU_LOG_MMU, "PDC_BLOCK_TLB: PDC_BTLB_PURGE slot %d\n", slot); - if (slot < HPPA_BTLB_ENTRIES) { + if (slot < btlb_entries) { btlb = &env->tlb[slot]; hppa_flush_tlb_ent(env, btlb, true); } else { @@ -542,7 +546,7 @@ void HELPER(diag_btlb)(CPUHPPAState *env) case 3: /* Purge all BTLB entries */ qemu_log_mask(CPU_LOG_MMU, "PDC_BLOCK_TLB: PDC_BTLB_PURGE_ALL\n"); - for (slot = 0; slot < HPPA_BTLB_ENTRIES; slot++) { + for (slot = 0; slot < btlb_entries; slot++) { btlb = &env->tlb[slot]; hppa_flush_tlb_ent(env, btlb, true); } From d3ae32d4d208bd73c8e1bc81d76017268e2455fc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 17 Sep 2023 18:38:59 -0700 Subject: [PATCH 590/974] target/hppa: Implement cpu_list Signed-off-by: Richard Henderson --- target/hppa/cpu.c | 24 ++++++++++++++++++++++++ target/hppa/cpu.h | 5 +++++ 2 files changed, 29 insertions(+) diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 07fae42bb8..27c74f0d27 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -161,6 +161,30 @@ static ObjectClass *hppa_cpu_class_by_name(const char *cpu_model) return object_class_by_name(TYPE_HPPA_CPU); } +static void hppa_cpu_list_entry(gpointer data, gpointer user_data) +{ + ObjectClass *oc = data; + CPUClass *cc = CPU_CLASS(oc); + const char *tname = object_class_get_name(oc); + g_autofree char *name = g_strndup(tname, strchr(tname, '-') - tname); + + if (cc->deprecation_note) { + qemu_printf(" %s (deprecated)\n", name); + } else { + qemu_printf(" %s\n", name); + } +} + +void hppa_cpu_list(void) +{ + GSList *list; + + list = object_class_get_list_sorted(TYPE_HPPA_CPU, false); + qemu_printf("Available CPUs:\n"); + g_slist_foreach(list, hppa_cpu_list_entry, NULL); + g_slist_free(list); +} + #ifndef CONFIG_USER_ONLY #include "hw/core/sysemu-cpu-ops.h" diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 48ddcffb8a..301c82114a 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -396,4 +396,9 @@ int hppa_artype_for_page(CPUHPPAState *env, target_ulong vaddr); #endif G_NORETURN void hppa_dynamic_excp(CPUHPPAState *env, int excp, uintptr_t ra); +#define CPU_RESOLVING_TYPE TYPE_HPPA_CPU + +#define cpu_list hppa_cpu_list +void hppa_cpu_list(void); + #endif /* HPPA_CPU_H */ From ca4c2008f53cd08dd82789384aeba87754d15152 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 17 Sep 2023 18:42:27 -0700 Subject: [PATCH 591/974] target/hppa: Implement hppa_cpu_class_by_name Signed-off-by: Richard Henderson --- linux-user/hppa/target_elf.h | 2 +- target/hppa/cpu.c | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/linux-user/hppa/target_elf.h b/linux-user/hppa/target_elf.h index 82b4e9535e..19cae8bd65 100644 --- a/linux-user/hppa/target_elf.h +++ b/linux-user/hppa/target_elf.h @@ -9,6 +9,6 @@ #define HPPA_TARGET_ELF_H static inline const char *cpu_get_model(uint32_t eflags) { - return "any"; + return "hppa"; } #endif diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 27c74f0d27..70ce0c3b99 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -158,7 +158,15 @@ static void hppa_cpu_initfn(Object *obj) static ObjectClass *hppa_cpu_class_by_name(const char *cpu_model) { - return object_class_by_name(TYPE_HPPA_CPU); + g_autofree char *typename = g_strconcat(cpu_model, "-cpu", NULL); + ObjectClass *oc = object_class_by_name(typename); + + if (oc && + !object_class_is_abstract(oc) && + object_class_dynamic_cast(oc, TYPE_HPPA_CPU)) { + return oc; + } + return NULL; } static void hppa_cpu_list_entry(gpointer data, gpointer user_data) From 931adff3147826a27748c9cdc862cacfd0807a92 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 21 Sep 2023 10:13:35 +0200 Subject: [PATCH 592/974] target/hppa: Update cpu_hppa_get/put_psw for hppa64 With 64-bit registers, there are 16 carry bits in the PSW. Clear reserved bits based on cpu revision. Signed-off-by: Richard Henderson --- target/hppa/helper.c | 63 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 52 insertions(+), 11 deletions(-) diff --git a/target/hppa/helper.c b/target/hppa/helper.c index cba8160b3d..fa17fe6931 100644 --- a/target/hppa/helper.c +++ b/target/hppa/helper.c @@ -28,19 +28,35 @@ target_ureg cpu_hppa_get_psw(CPUHPPAState *env) { target_ureg psw; + target_ureg mask1 = (target_ureg)-1 / 0xf; + target_ureg maskf = (target_ureg)-1 / 0xffff * 0xf; /* Fold carry bits down to 8 consecutive bits. */ - /* ??? Needs tweaking for hppa64. */ - /* .......b...c...d...e...f...g...h */ - psw = (env->psw_cb >> 4) & 0x01111111; - /* .......b..bc..cd..de..ef..fg..gh */ + /* ^^^b^^^c^^^d^^^e^^^f^^^g^^^h^^^i^^^j^^^k^^^l^^^m^^^n^^^o^^^p^^^^ */ + /* ^^^b^^^c^^^d^^^e^^^f^^^g^^^h^^^^ */ + psw = (env->psw_cb >> 4) & mask1; + /* .......b...c...d...e...f...g...h...i...j...k...l...m...n...o...p */ + /* .......b...c...d...e...f...g...h */ psw |= psw >> 3; - /* .............bcd............efgh */ - psw |= (psw >> 6) & 0x000f000f; - /* .........................bcdefgh */ - psw |= (psw >> 12) & 0xf; - psw |= env->psw_cb_msb << 7; - psw = (psw & 0xff) << 8; + /* .......b..bc..cd..de..ef..fg..gh..hi..ij..jk..kl..lm..mn..no..op */ + /* .......b..bc..cd..de..ef..fg..gh */ + psw |= psw >> 6; + psw &= maskf; + /* .............bcd............efgh............ijkl............mnop */ + /* .............bcd............efgh */ + psw |= psw >> 12; + /* .............bcd.........bcdefgh........efghijkl........ijklmnop */ + /* .............bcd.........bcdefgh */ + psw |= env->psw_cb_msb << (TARGET_REGISTER_BITS == 64 ? 39 : 7); + /* .............bcd........abcdefgh........efghijkl........ijklmnop */ + /* .............bcd........abcdefgh */ + + /* For hppa64, the two 8-bit fields are discontiguous. */ + if (hppa_is_pa20(env)) { + psw = (psw & 0xff00000000ull) | ((psw & 0xff) << 8); + } else { + psw = (psw & 0xff) << 8; + } psw |= env->psw_n * PSW_N; psw |= (env->psw_v < 0) * PSW_V; @@ -51,13 +67,38 @@ target_ureg cpu_hppa_get_psw(CPUHPPAState *env) void cpu_hppa_put_psw(CPUHPPAState *env, target_ureg psw) { + uint64_t reserved; target_ureg cb = 0; + /* Do not allow reserved bits to be set. */ + if (hppa_is_pa20(env)) { + reserved = MAKE_64BIT_MASK(40, 24) | MAKE_64BIT_MASK(28, 4); + reserved |= PSW_G; /* PA1.x only */ + reserved |= PSW_E; /* not implemented */ + } else { + reserved = MAKE_64BIT_MASK(32, 32) | MAKE_64BIT_MASK(28, 2); + reserved |= PSW_O | PSW_W; /* PA2.0 only */ + reserved |= PSW_E | PSW_Y | PSW_Z; /* not implemented */ + } + psw &= ~reserved; + env->psw = psw & ~(PSW_N | PSW_V | PSW_CB); env->psw_n = (psw / PSW_N) & 1; env->psw_v = -((psw / PSW_V) & 1); - env->psw_cb_msb = (psw >> 15) & 1; +#if TARGET_REGISTER_BITS == 32 + env->psw_cb_msb = (psw >> 15) & 1; +#else + env->psw_cb_msb = (psw >> 39) & 1; + cb |= ((psw >> 38) & 1) << 60; + cb |= ((psw >> 37) & 1) << 56; + cb |= ((psw >> 36) & 1) << 52; + cb |= ((psw >> 35) & 1) << 48; + cb |= ((psw >> 34) & 1) << 44; + cb |= ((psw >> 33) & 1) << 40; + cb |= ((psw >> 32) & 1) << 36; + cb |= ((psw >> 15) & 1) << 32; +#endif cb |= ((psw >> 14) & 1) << 28; cb |= ((psw >> 13) & 1) << 24; cb |= ((psw >> 12) & 1) << 20; From ccdf741c48db62319539a31bb5ae73a67316b295 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 17 Sep 2023 14:54:16 -0700 Subject: [PATCH 593/974] target/hppa: Handle absolute addresses for pa2.0 With pa2.0, absolute addresses are not the same as physical addresses, and undergo a transformation based on PSW_W. Signed-off-by: Richard Henderson --- target/hppa/cpu.h | 3 +++ target/hppa/mem_helper.c | 43 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 301c82114a..2999df9ff9 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -313,6 +313,9 @@ static inline target_ulong hppa_form_gva(CPUHPPAState *env, uint64_t spc, return hppa_form_gva_psw(env->psw, spc, off); } +hwaddr hppa_abs_to_phys_pa2_w0(vaddr addr); +hwaddr hppa_abs_to_phys_pa2_w1(vaddr addr); + /* * Since PSW_{I,CB} will never need to be in tb->flags, reuse them. * TB_FLAG_SR_SAME indicates that SR4 through SR7 all contain the diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c index 327fb20c17..420b43a0f6 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -25,6 +25,45 @@ #include "hw/core/cpu.h" #include "trace.h" +hwaddr hppa_abs_to_phys_pa2_w1(vaddr addr) +{ + if (likely(extract64(addr, 58, 4) != 0xf)) { + /* Memory address space */ + return addr & MAKE_64BIT_MASK(0, 62); + } + if (extract64(addr, 54, 4) != 0) { + /* I/O address space */ + return addr | MAKE_64BIT_MASK(62, 2); + } + /* PDC address space */ + return (addr & MAKE_64BIT_MASK(0, 54)) | MAKE_64BIT_MASK(60, 4); +} + +hwaddr hppa_abs_to_phys_pa2_w0(vaddr addr) +{ + if (likely(extract32(addr, 28, 4) != 0xf)) { + /* Memory address space */ + return addr & MAKE_64BIT_MASK(0, 32); + } + if (extract32(addr, 24, 4) != 0) { + /* I/O address space */ + return addr | MAKE_64BIT_MASK(32, 32); + } + /* PDC address space */ + return (addr & MAKE_64BIT_MASK(0, 24)) | MAKE_64BIT_MASK(60, 4); +} + +static hwaddr hppa_abs_to_phys(CPUHPPAState *env, vaddr addr) +{ + if (!hppa_is_pa20(env)) { + return addr; + } else if (env->psw & PSW_W) { + return hppa_abs_to_phys_pa2_w1(addr); + } else { + return hppa_abs_to_phys_pa2_w0(addr); + } +} + static HPPATLBEntry *hppa_find_tlb(CPUHPPAState *env, vaddr addr) { IntervalTreeNode *i = interval_tree_iter_first(&env->tlb_root, addr, addr); @@ -222,7 +261,7 @@ int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx, } egress: - *pphys = phys; + *pphys = phys = hppa_abs_to_phys(env, phys); *pprot = prot; trace_hppa_tlb_get_physical_address(env, ret, prot, addr, phys); return ret; @@ -238,7 +277,7 @@ hwaddr hppa_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) /* ??? We really ought to know if the code mmu is disabled too, in order to get the correct debugging dumps. */ if (!(cpu->env.psw & PSW_D)) { - return addr; + return hppa_abs_to_phys(&cpu->env, addr); } excp = hppa_get_physical_address(&cpu->env, addr, MMU_KERNEL_IDX, 0, From 5718fe4cfe947cb46b188ccd9bc5f9673ad9dd5b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 19 Sep 2023 15:43:57 +0200 Subject: [PATCH 594/974] target/hppa: Adjust hppa_cpu_dump_state for hppa64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dump all 64 bits for pa2.0 and low 32 bits for pa1.x. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hppa/helper.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/target/hppa/helper.c b/target/hppa/helper.c index fa17fe6931..c973b65bea 100644 --- a/target/hppa/helper.c +++ b/target/hppa/helper.c @@ -111,18 +111,26 @@ void cpu_hppa_put_psw(CPUHPPAState *env, target_ureg psw) void hppa_cpu_dump_state(CPUState *cs, FILE *f, int flags) { - HPPACPU *cpu = HPPA_CPU(cs); - CPUHPPAState *env = &cpu->env; + CPUHPPAState *env = cpu_env(cs); target_ureg psw = cpu_hppa_get_psw(env); target_ureg psw_cb; char psw_c[20]; - int i; + int i, w; + uint64_t m; + + if (hppa_is_pa20(env)) { + w = 16; + m = UINT64_MAX; + } else { + w = 8; + m = UINT32_MAX; + } qemu_fprintf(f, "IA_F " TARGET_FMT_lx " IA_B " TARGET_FMT_lx - " IIR " TREG_FMT_lx "\n", + " IIR %0*" PRIx64 "\n", hppa_form_gva_psw(psw, env->iasq_f, env->iaoq_f), hppa_form_gva_psw(psw, env->iasq_b, env->iaoq_b), - env->cr[CR_IIR]); + w, m & env->cr[CR_IIR]); psw_c[0] = (psw & PSW_W ? 'W' : '-'); psw_c[1] = (psw & PSW_E ? 'E' : '-'); @@ -143,13 +151,15 @@ void hppa_cpu_dump_state(CPUState *cs, FILE *f, int flags) psw_c[16] = (psw & PSW_D ? 'D' : '-'); psw_c[17] = (psw & PSW_I ? 'I' : '-'); psw_c[18] = '\0'; - psw_cb = ((env->psw_cb >> 4) & 0x01111111) | (env->psw_cb_msb << 28); + psw_cb = ((env->psw_cb >> 4) & ((target_ureg)-1 / 0xf)) + | (env->psw_cb_msb << (TARGET_REGISTER_BITS - 4)); - qemu_fprintf(f, "PSW " TREG_FMT_lx " CB " TREG_FMT_lx " %s\n", - psw, psw_cb, psw_c); + qemu_fprintf(f, "PSW %0*" PRIx64 " CB %0*" PRIx64 " %s\n", + w, m & psw, w, m & psw_cb, psw_c); for (i = 0; i < 32; i++) { - qemu_fprintf(f, "GR%02d " TREG_FMT_lx "%c", i, env->gr[i], + qemu_fprintf(f, "GR%02d %0*" PRIx64 "%c", + i, w, m & env->gr[i], (i & 3) == 3 ? '\n' : ' '); } #ifndef CONFIG_USER_ONLY From 698240d19b0d8c880a6e8c1baeec6d6048d2ca4b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 16 Sep 2023 16:52:51 -0700 Subject: [PATCH 595/974] target/hppa: Fix hppa64 addressing In form_gva and cpu_get_tb_cpu_state, we must truncate when PSW_W == 0. In space_select, the bits that choose the space depend on PSW_W. Signed-off-by: Richard Henderson --- target/hppa/cpu.h | 7 +++---- target/hppa/translate.c | 22 +++++++++++++--------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 2999df9ff9..cb838defb0 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -302,7 +302,7 @@ static inline target_ulong hppa_form_gva_psw(target_ureg psw, uint64_t spc, #ifdef CONFIG_USER_ONLY return off; #else - off &= (psw & PSW_W ? 0x3fffffffffffffffull : 0xffffffffull); + off &= psw & PSW_W ? MAKE_64BIT_MASK(0, 62) : MAKE_64BIT_MASK(0, 32); return spc | off; #endif } @@ -343,9 +343,8 @@ static inline void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, flags |= env->psw & (PSW_W | PSW_C | PSW_D | PSW_P); flags |= (env->iaoq_f & 3) << TB_FLAG_PRIV_SHIFT; - *pc = (env->psw & PSW_C - ? hppa_form_gva_psw(env->psw, env->iasq_f, env->iaoq_f & -4) - : env->iaoq_f & -4); + *pc = hppa_form_gva_psw(env->psw, (env->psw & PSW_C ? env->iasq_f : 0), + env->iaoq_f & -4); *cs_base = env->iasq_f; /* Insert a difference between IAOQ_B and IAOQ_F within the otherwise zero diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 44c9911720..4e0bc48b09 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -710,6 +710,13 @@ static bool nullify_end(DisasContext *ctx) return true; } +static target_ureg gva_offset_mask(DisasContext *ctx) +{ + return (ctx->tb_flags & PSW_W + ? MAKE_64BIT_MASK(0, 62) + : MAKE_64BIT_MASK(0, 32)); +} + static void copy_iaoq_entry(TCGv_reg dest, target_ureg ival, TCGv_reg vval) { if (unlikely(ival == -1)) { @@ -1398,7 +1405,8 @@ static TCGv_i64 space_select(DisasContext *ctx, int sp, TCGv_reg base) tmp = tcg_temp_new(); spc = tcg_temp_new_tl(); - tcg_gen_shri_reg(tmp, base, TARGET_REGISTER_BITS - 5); + /* Extract top 2 bits of the address, shift left 3 for uint64_t index. */ + tcg_gen_shri_reg(tmp, base, (ctx->tb_flags & PSW_W ? 64 : 32) - 5); tcg_gen_andi_reg(tmp, tmp, 030); tcg_gen_trunc_reg_ptr(ptr, tmp); @@ -1415,6 +1423,7 @@ static void form_gva(DisasContext *ctx, TCGv_tl *pgva, TCGv_reg *pofs, { TCGv_reg base = load_gpr(ctx, rb); TCGv_reg ofs; + TCGv_tl addr; /* Note that RX is mutually exclusive with DISP. */ if (rx) { @@ -1429,18 +1438,13 @@ static void form_gva(DisasContext *ctx, TCGv_tl *pgva, TCGv_reg *pofs, } *pofs = ofs; -#ifdef CONFIG_USER_ONLY - *pgva = (modify <= 0 ? ofs : base); -#else - TCGv_tl addr = tcg_temp_new_tl(); + *pgva = addr = tcg_temp_new_tl(); tcg_gen_extu_reg_tl(addr, modify <= 0 ? ofs : base); - if (ctx->tb_flags & PSW_W) { - tcg_gen_andi_tl(addr, addr, 0x3fffffffffffffffull); - } + tcg_gen_andi_tl(addr, addr, gva_offset_mask(ctx)); +#ifndef CONFIG_USER_ONLY if (!is_phys) { tcg_gen_or_tl(addr, addr, space_select(ctx, sp, base)); } - *pgva = addr; #endif } From 741322f471627b829fc1ac01c16ae40cacb0c133 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 26 Oct 2023 18:34:01 -0700 Subject: [PATCH 596/974] target/hppa: Pass DisasContext to copy_iaoq_entry Interface change only, no functional effect. Signed-off-by: Richard Henderson --- target/hppa/translate.c | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 4e0bc48b09..e342cc1d08 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -717,7 +717,8 @@ static target_ureg gva_offset_mask(DisasContext *ctx) : MAKE_64BIT_MASK(0, 32)); } -static void copy_iaoq_entry(TCGv_reg dest, target_ureg ival, TCGv_reg vval) +static void copy_iaoq_entry(DisasContext *ctx, TCGv_reg dest, + target_ureg ival, TCGv_reg vval) { if (unlikely(ival == -1)) { tcg_gen_mov_reg(dest, vval); @@ -738,8 +739,8 @@ static void gen_excp_1(int exception) static void gen_excp(DisasContext *ctx, int exception) { - copy_iaoq_entry(cpu_iaoq_f, ctx->iaoq_f, cpu_iaoq_f); - copy_iaoq_entry(cpu_iaoq_b, ctx->iaoq_b, cpu_iaoq_b); + copy_iaoq_entry(ctx, cpu_iaoq_f, ctx->iaoq_f, cpu_iaoq_f); + copy_iaoq_entry(ctx, cpu_iaoq_b, ctx->iaoq_b, cpu_iaoq_b); nullify_save(ctx); gen_excp_1(exception); ctx->base.is_jmp = DISAS_NORETURN; @@ -795,8 +796,8 @@ static void gen_goto_tb(DisasContext *ctx, int which, tcg_gen_movi_reg(cpu_iaoq_b, b); tcg_gen_exit_tb(ctx->base.tb, which); } else { - copy_iaoq_entry(cpu_iaoq_f, f, cpu_iaoq_b); - copy_iaoq_entry(cpu_iaoq_b, b, ctx->iaoq_n_var); + copy_iaoq_entry(ctx, cpu_iaoq_f, f, cpu_iaoq_b); + copy_iaoq_entry(ctx, cpu_iaoq_b, b, ctx->iaoq_n_var); tcg_gen_lookup_and_goto_ptr(); } } @@ -1752,7 +1753,7 @@ static bool do_dbranch(DisasContext *ctx, target_ureg dest, { if (ctx->null_cond.c == TCG_COND_NEVER && ctx->null_lab == NULL) { if (link != 0) { - copy_iaoq_entry(cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); + copy_iaoq_entry(ctx, cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); } ctx->iaoq_n = dest; if (is_n) { @@ -1762,7 +1763,7 @@ static bool do_dbranch(DisasContext *ctx, target_ureg dest, nullify_over(ctx); if (link != 0) { - copy_iaoq_entry(cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); + copy_iaoq_entry(ctx, cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); } if (is_n && use_nullify_skip(ctx)) { @@ -1860,7 +1861,7 @@ static bool do_ibranch(DisasContext *ctx, TCGv_reg dest, if (ctx->null_cond.c == TCG_COND_NEVER) { if (link != 0) { - copy_iaoq_entry(cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); + copy_iaoq_entry(ctx, cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); } next = tcg_temp_new(); tcg_gen_mov_reg(next, dest); @@ -1906,7 +1907,7 @@ static bool do_ibranch(DisasContext *ctx, TCGv_reg dest, tmp = tcg_temp_new(); next = tcg_temp_new(); - copy_iaoq_entry(tmp, ctx->iaoq_n, ctx->iaoq_n_var); + copy_iaoq_entry(ctx, tmp, ctx->iaoq_n, ctx->iaoq_n_var); tcg_gen_movcond_reg(c, next, a0, a1, tmp, dest); ctx->iaoq_n = -1; ctx->iaoq_n_var = next; @@ -2643,8 +2644,8 @@ static bool trans_or(DisasContext *ctx, arg_rrr_cf *a) nullify_over(ctx); /* Advance the instruction queue. */ - copy_iaoq_entry(cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b); - copy_iaoq_entry(cpu_iaoq_b, ctx->iaoq_n, ctx->iaoq_n_var); + copy_iaoq_entry(ctx, cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b); + copy_iaoq_entry(ctx, cpu_iaoq_b, ctx->iaoq_n, ctx->iaoq_n_var); nullify_set(ctx, 0); /* Tell the qemu main loop to halt until this cpu has work. */ @@ -3433,7 +3434,7 @@ static bool trans_be(DisasContext *ctx, arg_be *a) load_spr(ctx, new_spc, a->sp); if (a->l) { - copy_iaoq_entry(cpu_gr[31], ctx->iaoq_n, ctx->iaoq_n_var); + copy_iaoq_entry(ctx, cpu_gr[31], ctx->iaoq_n, ctx->iaoq_n_var); tcg_gen_mov_i64(cpu_sr[0], cpu_iasq_f); } if (a->n && use_nullify_skip(ctx)) { @@ -3442,7 +3443,7 @@ static bool trans_be(DisasContext *ctx, arg_be *a) tcg_gen_mov_i64(cpu_iasq_f, new_spc); tcg_gen_mov_i64(cpu_iasq_b, cpu_iasq_f); } else { - copy_iaoq_entry(cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b); + copy_iaoq_entry(ctx, cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b); if (ctx->iaoq_b == -1) { tcg_gen_mov_i64(cpu_iasq_f, cpu_iasq_b); } @@ -3556,14 +3557,14 @@ static bool trans_bve(DisasContext *ctx, arg_bve *a) nullify_over(ctx); dest = do_ibranch_priv(ctx, load_gpr(ctx, a->b)); - copy_iaoq_entry(cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b); + copy_iaoq_entry(ctx, cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b); if (ctx->iaoq_b == -1) { tcg_gen_mov_i64(cpu_iasq_f, cpu_iasq_b); } - copy_iaoq_entry(cpu_iaoq_b, -1, dest); + copy_iaoq_entry(ctx, cpu_iaoq_b, -1, dest); tcg_gen_mov_i64(cpu_iasq_b, space_select(ctx, 0, dest)); if (a->l) { - copy_iaoq_entry(cpu_gr[a->l], ctx->iaoq_n, ctx->iaoq_n_var); + copy_iaoq_entry(ctx, cpu_gr[a->l], ctx->iaoq_n, ctx->iaoq_n_var); } nullify_set(ctx, a->n); tcg_gen_lookup_and_goto_ptr(); @@ -4218,7 +4219,7 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) case DISAS_IAQ_N_STALE_EXIT: if (ctx->iaoq_f == -1) { tcg_gen_mov_reg(cpu_iaoq_f, cpu_iaoq_b); - copy_iaoq_entry(cpu_iaoq_b, ctx->iaoq_n, ctx->iaoq_n_var); + copy_iaoq_entry(ctx, cpu_iaoq_b, ctx->iaoq_n, ctx->iaoq_n_var); #ifndef CONFIG_USER_ONLY tcg_gen_mov_i64(cpu_iasq_f, cpu_iasq_b); #endif @@ -4247,8 +4248,8 @@ static void hppa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) case DISAS_TOO_MANY: case DISAS_IAQ_N_STALE: case DISAS_IAQ_N_STALE_EXIT: - copy_iaoq_entry(cpu_iaoq_f, ctx->iaoq_f, cpu_iaoq_f); - copy_iaoq_entry(cpu_iaoq_b, ctx->iaoq_b, cpu_iaoq_b); + copy_iaoq_entry(ctx, cpu_iaoq_f, ctx->iaoq_f, cpu_iaoq_f); + copy_iaoq_entry(ctx, cpu_iaoq_b, ctx->iaoq_b, cpu_iaoq_b); nullify_save(ctx); /* FALLTHRU */ case DISAS_IAQ_N_UPDATED: From a01809737e07e2f882cf09746f348424f7c04526 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 26 Oct 2023 18:51:25 -0700 Subject: [PATCH 597/974] target/hppa: Always use copy_iaoq_entry to set cpu_iaoq_[fb] This will be how we ensure that the IAOQ is always valid per PSW.W, therefore all stores to these two variables must be done with this function. Use third argument -1 if the destination is always dynamic, and fourth argument NULL if the destination is always static. Signed-off-by: Richard Henderson --- target/hppa/translate.c | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index e342cc1d08..348fdb75e5 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -792,8 +792,8 @@ static void gen_goto_tb(DisasContext *ctx, int which, { if (f != -1 && b != -1 && use_goto_tb(ctx, f)) { tcg_gen_goto_tb(which); - tcg_gen_movi_reg(cpu_iaoq_f, f); - tcg_gen_movi_reg(cpu_iaoq_b, b); + copy_iaoq_entry(ctx, cpu_iaoq_f, f, NULL); + copy_iaoq_entry(ctx, cpu_iaoq_b, b, NULL); tcg_gen_exit_tb(ctx->base.tb, which); } else { copy_iaoq_entry(ctx, cpu_iaoq_f, f, cpu_iaoq_b); @@ -1867,8 +1867,9 @@ static bool do_ibranch(DisasContext *ctx, TCGv_reg dest, tcg_gen_mov_reg(next, dest); if (is_n) { if (use_nullify_skip(ctx)) { - tcg_gen_mov_reg(cpu_iaoq_f, next); - tcg_gen_addi_reg(cpu_iaoq_b, next, 4); + copy_iaoq_entry(ctx, cpu_iaoq_f, -1, next); + tcg_gen_addi_reg(next, next, 4); + copy_iaoq_entry(ctx, cpu_iaoq_b, -1, next); nullify_set(ctx, 0); ctx->base.is_jmp = DISAS_IAQ_N_UPDATED; return true; @@ -1890,8 +1891,10 @@ static bool do_ibranch(DisasContext *ctx, TCGv_reg dest, /* We do have to handle the non-local temporary, DEST, before branching. Since IOAQ_F is not really live at this point, we can simply store DEST optimistically. Similarly with IAOQ_B. */ - tcg_gen_mov_reg(cpu_iaoq_f, dest); - tcg_gen_addi_reg(cpu_iaoq_b, dest, 4); + copy_iaoq_entry(ctx, cpu_iaoq_f, -1, dest); + next = tcg_temp_new(); + tcg_gen_addi_reg(next, dest, 4); + copy_iaoq_entry(ctx, cpu_iaoq_b, -1, next); nullify_over(ctx); if (link != 0) { @@ -1970,6 +1973,8 @@ static TCGv_reg do_ibranch_priv(DisasContext *ctx, TCGv_reg offset) aforementioned BE. */ static void do_page_zero(DisasContext *ctx) { + TCGv_reg tmp; + /* If by some means we get here with PSW[N]=1, that implies that the B,GATE instruction would be skipped, and we'd fault on the next insn within the privileged page. */ @@ -2006,8 +2011,11 @@ static void do_page_zero(DisasContext *ctx) case 0xe0: /* SET_THREAD_POINTER */ tcg_gen_st_reg(cpu_gr[26], tcg_env, offsetof(CPUHPPAState, cr[27])); - tcg_gen_ori_reg(cpu_iaoq_f, cpu_gr[31], 3); - tcg_gen_addi_reg(cpu_iaoq_b, cpu_iaoq_f, 4); + tmp = tcg_temp_new(); + tcg_gen_ori_reg(tmp, cpu_gr[31], 3); + copy_iaoq_entry(ctx, cpu_iaoq_f, -1, tmp); + tcg_gen_addi_reg(tmp, tmp, 4); + copy_iaoq_entry(ctx, cpu_iaoq_b, -1, tmp); ctx->base.is_jmp = DISAS_IAQ_N_UPDATED; break; @@ -3438,8 +3446,9 @@ static bool trans_be(DisasContext *ctx, arg_be *a) tcg_gen_mov_i64(cpu_sr[0], cpu_iasq_f); } if (a->n && use_nullify_skip(ctx)) { - tcg_gen_mov_reg(cpu_iaoq_f, tmp); - tcg_gen_addi_reg(cpu_iaoq_b, cpu_iaoq_f, 4); + copy_iaoq_entry(ctx, cpu_iaoq_f, -1, tmp); + tcg_gen_addi_reg(tmp, tmp, 4); + copy_iaoq_entry(ctx, cpu_iaoq_b, -1, tmp); tcg_gen_mov_i64(cpu_iasq_f, new_spc); tcg_gen_mov_i64(cpu_iasq_b, cpu_iasq_f); } else { @@ -3447,7 +3456,7 @@ static bool trans_be(DisasContext *ctx, arg_be *a) if (ctx->iaoq_b == -1) { tcg_gen_mov_i64(cpu_iasq_f, cpu_iasq_b); } - tcg_gen_mov_reg(cpu_iaoq_b, tmp); + copy_iaoq_entry(ctx, cpu_iaoq_b, -1, tmp); tcg_gen_mov_i64(cpu_iasq_b, new_spc); nullify_set(ctx, a->n); } @@ -4218,7 +4227,7 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) case DISAS_IAQ_N_STALE: case DISAS_IAQ_N_STALE_EXIT: if (ctx->iaoq_f == -1) { - tcg_gen_mov_reg(cpu_iaoq_f, cpu_iaoq_b); + copy_iaoq_entry(ctx, cpu_iaoq_f, -1, cpu_iaoq_b); copy_iaoq_entry(ctx, cpu_iaoq_b, ctx->iaoq_n, ctx->iaoq_n_var); #ifndef CONFIG_USER_ONLY tcg_gen_mov_i64(cpu_iasq_f, cpu_iasq_b); @@ -4228,7 +4237,7 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) ? DISAS_EXIT : DISAS_IAQ_N_UPDATED); } else if (ctx->iaoq_b == -1) { - tcg_gen_mov_reg(cpu_iaoq_b, ctx->iaoq_n_var); + copy_iaoq_entry(ctx, cpu_iaoq_b, -1, ctx->iaoq_n_var); } break; From 9a91dd845277dd1d7c4c8f37662aa076e685dccc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 26 Oct 2023 18:55:54 -0700 Subject: [PATCH 598/974] target/hppa: Use copy_iaoq_entry for link in do_ibranch We need to make sure the link is masked properly along the use_nullify_skip path. The other three settings of a link register already use this. Signed-off-by: Richard Henderson --- target/hppa/translate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 348fdb75e5..c2db2782f4 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1898,7 +1898,7 @@ static bool do_ibranch(DisasContext *ctx, TCGv_reg dest, nullify_over(ctx); if (link != 0) { - tcg_gen_movi_reg(cpu_gr[link], ctx->iaoq_n); + copy_iaoq_entry(ctx, cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); } tcg_gen_lookup_and_goto_ptr(); return nullify_end(ctx); From f13bf343ccdb7df14233133f42670e1b16bb6b20 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 26 Oct 2023 19:03:34 -0700 Subject: [PATCH 599/974] target/hppa: Mask inputs in copy_iaoq_entry Ensure that the destination is always a valid GVA offset. Signed-off-by: Richard Henderson --- target/hppa/translate.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index c2db2782f4..cf05d8b6e4 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -720,10 +720,22 @@ static target_ureg gva_offset_mask(DisasContext *ctx) static void copy_iaoq_entry(DisasContext *ctx, TCGv_reg dest, target_ureg ival, TCGv_reg vval) { - if (unlikely(ival == -1)) { + target_ureg mask = gva_offset_mask(ctx); + + if (ival != -1) { + tcg_gen_movi_reg(dest, ival & mask); + return; + } + tcg_debug_assert(vval != NULL); + + /* + * We know that the IAOQ is already properly masked. + * This optimization is primarily for "iaoq_f = iaoq_b". + */ + if (vval == cpu_iaoq_f || vval == cpu_iaoq_b) { tcg_gen_mov_reg(dest, vval); } else { - tcg_gen_movi_reg(dest, ival); + tcg_gen_andi_reg(dest, vval, mask); } } From f3618f59f3559eae69c34e0fe621685614b4350d Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Mon, 16 Oct 2023 14:43:18 +0200 Subject: [PATCH 600/974] target/hppa: sar register allows only 5 bits on 32-bit CPU The sar shift amount register is limited to 5 bits when running a 32-bit CPU. Strip off the remaining bits. The interesting part is, that this register allows to detect at runtime if a physical CPU is capable to execute PA2.0 (64-bit) instructions. Signed-off-by: Helge Deller --- target/hppa/translate.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index cf05d8b6e4..1694b988ae 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -2176,7 +2176,7 @@ static bool trans_mtctl(DisasContext *ctx, arg_mtctl *a) if (ctl == CR_SAR) { reg = load_gpr(ctx, a->r); tmp = tcg_temp_new(); - tcg_gen_andi_reg(tmp, reg, TARGET_REGISTER_BITS - 1); + tcg_gen_andi_reg(tmp, reg, ctx->is_pa20 ? 63 : 31); save_or_nullify(ctx, cpu_sar, tmp); cond_free(&ctx->null_cond); @@ -2237,7 +2237,7 @@ static bool trans_mtsarcm(DisasContext *ctx, arg_mtsarcm *a) TCGv_reg tmp = tcg_temp_new(); tcg_gen_not_reg(tmp, load_gpr(ctx, a->r)); - tcg_gen_andi_reg(tmp, tmp, TARGET_REGISTER_BITS - 1); + tcg_gen_andi_reg(tmp, tmp, ctx->is_pa20 ? 63 : 31); save_or_nullify(ctx, cpu_sar, tmp); cond_free(&ctx->null_cond); From a751eb31b669d4daa27cd31996f3cd26a47b26eb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 16 Sep 2023 19:26:15 -0700 Subject: [PATCH 601/974] target/hppa: Pass d to do_cond Hoist the resolution of d up one level above do_cond. Signed-off-by: Richard Henderson --- target/hppa/translate.c | 82 +++++++++++++++++++++++++++-------------- 1 file changed, 54 insertions(+), 28 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 1694b988ae..7b0e48c42b 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -827,7 +827,7 @@ static bool cond_need_cb(int c) /* Need extensions from TCGv_i32 to TCGv_reg. */ static bool cond_need_ext(DisasContext *ctx, bool d) { - return TARGET_REGISTER_BITS == 64 && !d; + return TARGET_REGISTER_BITS == 64 && !(ctx->is_pa20 && d); } /* @@ -835,8 +835,8 @@ static bool cond_need_ext(DisasContext *ctx, bool d) * the Parisc 1.1 Architecture Reference Manual for details. */ -static DisasCond do_cond(unsigned cf, TCGv_reg res, - TCGv_reg cb_msb, TCGv_reg sv) +static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d, + TCGv_reg res, TCGv_reg cb_msb, TCGv_reg sv) { DisasCond cond; TCGv_reg tmp; @@ -846,11 +846,19 @@ static DisasCond do_cond(unsigned cf, TCGv_reg res, cond = cond_make_f(); break; case 1: /* = / <> (Z / !Z) */ + if (cond_need_ext(ctx, d)) { + tmp = tcg_temp_new(); + tcg_gen_ext32u_reg(tmp, res); + res = tmp; + } cond = cond_make_0(TCG_COND_EQ, res); break; case 2: /* < / >= (N ^ V / !(N ^ V) */ tmp = tcg_temp_new(); tcg_gen_xor_reg(tmp, res, sv); + if (cond_need_ext(ctx, d)) { + tcg_gen_ext32s_reg(tmp, tmp); + } cond = cond_make_0_tmp(TCG_COND_LT, tmp); break; case 3: /* <= / > (N ^ V) | Z / !((N ^ V) | Z) */ @@ -865,20 +873,35 @@ static DisasCond do_cond(unsigned cf, TCGv_reg res, */ tmp = tcg_temp_new(); tcg_gen_eqv_reg(tmp, res, sv); - tcg_gen_sari_reg(tmp, tmp, TARGET_REGISTER_BITS - 1); - tcg_gen_and_reg(tmp, tmp, res); + if (cond_need_ext(ctx, d)) { + tcg_gen_sextract_reg(tmp, tmp, 31, 1); + tcg_gen_and_reg(tmp, tmp, res); + tcg_gen_ext32u_reg(tmp, tmp); + } else { + tcg_gen_sari_reg(tmp, tmp, TARGET_REGISTER_BITS - 1); + tcg_gen_and_reg(tmp, tmp, res); + } cond = cond_make_0_tmp(TCG_COND_EQ, tmp); break; case 4: /* NUV / UV (!C / C) */ + /* Only bit 0 of cb_msb is ever set. */ cond = cond_make_0(TCG_COND_EQ, cb_msb); break; case 5: /* ZNV / VNZ (!C | Z / C & !Z) */ tmp = tcg_temp_new(); tcg_gen_neg_reg(tmp, cb_msb); tcg_gen_and_reg(tmp, tmp, res); + if (cond_need_ext(ctx, d)) { + tcg_gen_ext32u_reg(tmp, tmp); + } cond = cond_make_0_tmp(TCG_COND_EQ, tmp); break; case 6: /* SV / NSV (V / !V) */ + if (cond_need_ext(ctx, d)) { + tmp = tcg_temp_new(); + tcg_gen_ext32s_reg(tmp, sv); + sv = tmp; + } cond = cond_make_0(TCG_COND_LT, sv); break; case 7: /* OD / EV */ @@ -900,10 +923,11 @@ static DisasCond do_cond(unsigned cf, TCGv_reg res, can use the inputs directly. This can allow other computation to be deleted as unused. */ -static DisasCond do_sub_cond(unsigned cf, TCGv_reg res, +static DisasCond do_sub_cond(DisasContext *ctx, unsigned cf, TCGv_reg res, TCGv_reg in1, TCGv_reg in2, TCGv_reg sv) { DisasCond cond; + bool d = false; switch (cf >> 1) { case 1: /* = / <> */ @@ -922,7 +946,7 @@ static DisasCond do_sub_cond(unsigned cf, TCGv_reg res, cond = cond_make(TCG_COND_LEU, in1, in2); break; default: - return do_cond(cf, res, NULL, sv); + return do_cond(ctx, cf, d, res, NULL, sv); } if (cf & 1) { cond.c = tcg_invert_cond(cond.c); @@ -940,8 +964,10 @@ static DisasCond do_sub_cond(unsigned cf, TCGv_reg res, * how cases c={2,3} are treated. */ -static DisasCond do_log_cond(unsigned cf, TCGv_reg res) +static DisasCond do_log_cond(DisasContext *ctx, unsigned cf, TCGv_reg res) { + bool d = false; + switch (cf) { case 0: /* never */ case 9: /* undef, C */ @@ -970,7 +996,7 @@ static DisasCond do_log_cond(unsigned cf, TCGv_reg res) case 14: /* OD */ case 15: /* EV */ - return do_cond(cf, res, NULL, NULL); + return do_cond(ctx, cf, d, res, NULL, NULL); default: g_assert_not_reached(); @@ -979,7 +1005,7 @@ static DisasCond do_log_cond(unsigned cf, TCGv_reg res) /* Similar, but for shift/extract/deposit conditions. */ -static DisasCond do_sed_cond(unsigned orig, TCGv_reg res) +static DisasCond do_sed_cond(DisasContext *ctx, unsigned orig, TCGv_reg res) { unsigned c, f; @@ -992,7 +1018,7 @@ static DisasCond do_sed_cond(unsigned orig, TCGv_reg res) } f = (orig & 4) / 4; - return do_log_cond(c * 2 + f, res); + return do_log_cond(ctx, c * 2 + f, res); } /* Similar, but for unit conditions. */ @@ -1164,7 +1190,7 @@ static void do_add(DisasContext *ctx, unsigned rt, TCGv_reg in1, } /* Emit any conditional trap before any writeback. */ - cond = do_cond(cf, dest, cb_cond, sv); + cond = do_cond(ctx, cf, d, dest, cb_cond, sv); if (is_tc) { tmp = tcg_temp_new(); tcg_gen_setcond_reg(cond.c, tmp, cond.a0, cond.a1); @@ -1254,9 +1280,9 @@ static void do_sub(DisasContext *ctx, unsigned rt, TCGv_reg in1, /* Compute the condition. We cannot use the special case for borrow. */ if (!is_b) { - cond = do_sub_cond(cf, dest, in1, in2, sv); + cond = do_sub_cond(ctx, cf, dest, in1, in2, sv); } else { - cond = do_cond(cf, dest, get_carry(ctx, d, cb, cb_msb), sv); + cond = do_cond(ctx, cf, d, dest, get_carry(ctx, d, cb, cb_msb), sv); } /* Emit any conditional trap before any writeback. */ @@ -1319,7 +1345,7 @@ static void do_cmpclr(DisasContext *ctx, unsigned rt, TCGv_reg in1, } /* Form the condition for the compare. */ - cond = do_sub_cond(cf, dest, in1, in2, sv); + cond = do_sub_cond(ctx, cf, dest, in1, in2, sv); /* Clear. */ tcg_gen_movi_reg(dest, 0); @@ -1343,7 +1369,7 @@ static void do_log(DisasContext *ctx, unsigned rt, TCGv_reg in1, /* Install the new nullification. */ cond_free(&ctx->null_cond); if (cf) { - ctx->null_cond = do_log_cond(cf, dest); + ctx->null_cond = do_log_cond(ctx, cf, dest); } } @@ -2817,7 +2843,7 @@ static bool trans_ds(DisasContext *ctx, arg_rrr_cf *a) /* ??? The lshift is supposed to contribute to overflow. */ sv = do_add_sv(ctx, dest, add1, add2); } - ctx->null_cond = do_cond(a->cf, dest, cout, sv); + ctx->null_cond = do_cond(ctx, a->cf, false, dest, cout, sv); } return nullify_end(ctx); @@ -3034,7 +3060,7 @@ static bool do_cmpb(DisasContext *ctx, unsigned r, TCGv_reg in1, sv = do_sub_sv(ctx, dest, in1, in2); } - cond = do_sub_cond(c * 2 + f, dest, in1, in2, sv); + cond = do_sub_cond(ctx, c * 2 + f, dest, in1, in2, sv); return do_cbranch(ctx, disp, n, &cond); } @@ -3078,7 +3104,7 @@ static bool do_addb(DisasContext *ctx, unsigned r, TCGv_reg in1, sv = do_add_sv(ctx, dest, in1, in2); } - cond = do_cond(c * 2 + f, dest, cb_cond, sv); + cond = do_cond(ctx, c * 2 + f, d, dest, cb_cond, sv); save_gpr(ctx, r, dest); return do_cbranch(ctx, disp, n, &cond); } @@ -3149,7 +3175,7 @@ static bool trans_movb(DisasContext *ctx, arg_movb *a) tcg_gen_mov_reg(dest, cpu_gr[a->r1]); } - cond = do_sed_cond(a->c, dest); + cond = do_sed_cond(ctx, a->c, dest); return do_cbranch(ctx, a->disp, a->n, &cond); } @@ -3163,7 +3189,7 @@ static bool trans_movbi(DisasContext *ctx, arg_movbi *a) dest = dest_gpr(ctx, a->r); tcg_gen_movi_reg(dest, a->i); - cond = do_sed_cond(a->c, dest); + cond = do_sed_cond(ctx, a->c, dest); return do_cbranch(ctx, a->disp, a->n, &cond); } @@ -3201,7 +3227,7 @@ static bool trans_shrpw_sar(DisasContext *ctx, arg_shrpw_sar *a) /* Install the new nullification. */ cond_free(&ctx->null_cond); if (a->c) { - ctx->null_cond = do_sed_cond(a->c, dest); + ctx->null_cond = do_sed_cond(ctx, a->c, dest); } return nullify_end(ctx); } @@ -3237,7 +3263,7 @@ static bool trans_shrpw_imm(DisasContext *ctx, arg_shrpw_imm *a) /* Install the new nullification. */ cond_free(&ctx->null_cond); if (a->c) { - ctx->null_cond = do_sed_cond(a->c, dest); + ctx->null_cond = do_sed_cond(ctx, a->c, dest); } return nullify_end(ctx); } @@ -3271,7 +3297,7 @@ static bool trans_extrw_sar(DisasContext *ctx, arg_extrw_sar *a) /* Install the new nullification. */ cond_free(&ctx->null_cond); if (a->c) { - ctx->null_cond = do_sed_cond(a->c, dest); + ctx->null_cond = do_sed_cond(ctx, a->c, dest); } return nullify_end(ctx); } @@ -3298,7 +3324,7 @@ static bool trans_extrw_imm(DisasContext *ctx, arg_extrw_imm *a) /* Install the new nullification. */ cond_free(&ctx->null_cond); if (a->c) { - ctx->null_cond = do_sed_cond(a->c, dest); + ctx->null_cond = do_sed_cond(ctx, a->c, dest); } return nullify_end(ctx); } @@ -3335,7 +3361,7 @@ static bool trans_depwi_imm(DisasContext *ctx, arg_depwi_imm *a) /* Install the new nullification. */ cond_free(&ctx->null_cond); if (a->c) { - ctx->null_cond = do_sed_cond(a->c, dest); + ctx->null_cond = do_sed_cond(ctx, a->c, dest); } return nullify_end(ctx); } @@ -3365,7 +3391,7 @@ static bool trans_depw_imm(DisasContext *ctx, arg_depw_imm *a) /* Install the new nullification. */ cond_free(&ctx->null_cond); if (a->c) { - ctx->null_cond = do_sed_cond(a->c, dest); + ctx->null_cond = do_sed_cond(ctx, a->c, dest); } return nullify_end(ctx); } @@ -3402,7 +3428,7 @@ static bool do_depw_sar(DisasContext *ctx, unsigned rt, unsigned c, /* Install the new nullification. */ cond_free(&ctx->null_cond); if (c) { - ctx->null_cond = do_sed_cond(c, dest); + ctx->null_cond = do_sed_cond(ctx, c, dest); } return nullify_end(ctx); } From 4fe9533accedd2729667aa7568c9b5a690c15d1d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 16 Sep 2023 19:28:12 -0700 Subject: [PATCH 602/974] target/hppa: Pass d to do_sub_cond Hoist the resolution of d up one level above do_sub_cond. Signed-off-by: Richard Henderson --- target/hppa/translate.c | 77 ++++++++++++++++++++++++++--------------- 1 file changed, 49 insertions(+), 28 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 7b0e48c42b..72971097bb 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -446,12 +446,15 @@ static DisasCond cond_make_n(void) }; } -static DisasCond cond_make_0_tmp(TCGCond c, TCGv_reg a0) +static DisasCond cond_make_tmp(TCGCond c, TCGv_reg a0, TCGv_reg a1) { assert (c != TCG_COND_NEVER && c != TCG_COND_ALWAYS); - return (DisasCond){ - .c = c, .a0 = a0, .a1 = tcg_constant_reg(0) - }; + return (DisasCond){ .c = c, .a0 = a0, .a1 = a1 }; +} + +static DisasCond cond_make_0_tmp(TCGCond c, TCGv_reg a0) +{ + return cond_make_tmp(c, a0, tcg_constant_reg(0)); } static DisasCond cond_make_0(TCGCond c, TCGv_reg a0) @@ -463,15 +466,12 @@ static DisasCond cond_make_0(TCGCond c, TCGv_reg a0) static DisasCond cond_make(TCGCond c, TCGv_reg a0, TCGv_reg a1) { - DisasCond r = { .c = c }; + TCGv_reg t0 = tcg_temp_new(); + TCGv_reg t1 = tcg_temp_new(); - assert (c != TCG_COND_NEVER && c != TCG_COND_ALWAYS); - r.a0 = tcg_temp_new(); - tcg_gen_mov_reg(r.a0, a0); - r.a1 = tcg_temp_new(); - tcg_gen_mov_reg(r.a1, a1); - - return r; + tcg_gen_mov_reg(t0, a0); + tcg_gen_mov_reg(t1, a1); + return cond_make_tmp(c, t0, t1); } static void cond_free(DisasCond *cond) @@ -923,36 +923,55 @@ static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d, can use the inputs directly. This can allow other computation to be deleted as unused. */ -static DisasCond do_sub_cond(DisasContext *ctx, unsigned cf, TCGv_reg res, - TCGv_reg in1, TCGv_reg in2, TCGv_reg sv) +static DisasCond do_sub_cond(DisasContext *ctx, unsigned cf, bool d, + TCGv_reg res, TCGv_reg in1, + TCGv_reg in2, TCGv_reg sv) { - DisasCond cond; - bool d = false; + TCGCond tc; + bool ext_uns; switch (cf >> 1) { case 1: /* = / <> */ - cond = cond_make(TCG_COND_EQ, in1, in2); + tc = TCG_COND_EQ; + ext_uns = true; break; case 2: /* < / >= */ - cond = cond_make(TCG_COND_LT, in1, in2); + tc = TCG_COND_LT; + ext_uns = false; break; case 3: /* <= / > */ - cond = cond_make(TCG_COND_LE, in1, in2); + tc = TCG_COND_LE; + ext_uns = false; break; case 4: /* << / >>= */ - cond = cond_make(TCG_COND_LTU, in1, in2); + tc = TCG_COND_LTU; + ext_uns = true; break; case 5: /* <<= / >> */ - cond = cond_make(TCG_COND_LEU, in1, in2); + tc = TCG_COND_LEU; + ext_uns = true; break; default: return do_cond(ctx, cf, d, res, NULL, sv); } - if (cf & 1) { - cond.c = tcg_invert_cond(cond.c); - } - return cond; + if (cf & 1) { + tc = tcg_invert_cond(tc); + } + if (cond_need_ext(ctx, d)) { + TCGv_reg t1 = tcg_temp_new(); + TCGv_reg t2 = tcg_temp_new(); + + if (ext_uns) { + tcg_gen_ext32u_reg(t1, in1); + tcg_gen_ext32u_reg(t2, in2); + } else { + tcg_gen_ext32s_reg(t1, in1); + tcg_gen_ext32s_reg(t2, in2); + } + return cond_make_tmp(tc, t1, t2); + } + return cond_make(tc, in1, in2); } /* @@ -1280,7 +1299,7 @@ static void do_sub(DisasContext *ctx, unsigned rt, TCGv_reg in1, /* Compute the condition. We cannot use the special case for borrow. */ if (!is_b) { - cond = do_sub_cond(ctx, cf, dest, in1, in2, sv); + cond = do_sub_cond(ctx, cf, d, dest, in1, in2, sv); } else { cond = do_cond(ctx, cf, d, dest, get_carry(ctx, d, cb, cb_msb), sv); } @@ -1334,6 +1353,7 @@ static void do_cmpclr(DisasContext *ctx, unsigned rt, TCGv_reg in1, { TCGv_reg dest, sv; DisasCond cond; + bool d = false; dest = tcg_temp_new(); tcg_gen_sub_reg(dest, in1, in2); @@ -1345,7 +1365,7 @@ static void do_cmpclr(DisasContext *ctx, unsigned rt, TCGv_reg in1, } /* Form the condition for the compare. */ - cond = do_sub_cond(ctx, cf, dest, in1, in2, sv); + cond = do_sub_cond(ctx, cf, d, dest, in1, in2, sv); /* Clear. */ tcg_gen_movi_reg(dest, 0); @@ -3049,6 +3069,7 @@ static bool do_cmpb(DisasContext *ctx, unsigned r, TCGv_reg in1, { TCGv_reg dest, in2, sv; DisasCond cond; + bool d = false; in2 = load_gpr(ctx, r); dest = tcg_temp_new(); @@ -3060,7 +3081,7 @@ static bool do_cmpb(DisasContext *ctx, unsigned r, TCGv_reg in1, sv = do_sub_sv(ctx, dest, in1, in2); } - cond = do_sub_cond(ctx, c * 2 + f, dest, in1, in2, sv); + cond = do_sub_cond(ctx, c * 2 + f, d, dest, in1, in2, sv); return do_cbranch(ctx, disp, n, &cond); } From b5af84233d423fba25569f4c2df338eb2be30415 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 16 Sep 2023 19:30:10 -0700 Subject: [PATCH 603/974] target/hppa: Pass d to do_log_cond Hoist the resolution of d up one level above do_log_cond. Signed-off-by: Richard Henderson --- target/hppa/translate.c | 48 ++++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 72971097bb..ac7f1f048c 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -983,9 +983,11 @@ static DisasCond do_sub_cond(DisasContext *ctx, unsigned cf, bool d, * how cases c={2,3} are treated. */ -static DisasCond do_log_cond(DisasContext *ctx, unsigned cf, TCGv_reg res) +static DisasCond do_log_cond(DisasContext *ctx, unsigned cf, bool d, + TCGv_reg res) { - bool d = false; + TCGCond tc; + bool ext_uns; switch (cf) { case 0: /* never */ @@ -1001,17 +1003,29 @@ static DisasCond do_log_cond(DisasContext *ctx, unsigned cf, TCGv_reg res) return cond_make_t(); case 2: /* == */ - return cond_make_0(TCG_COND_EQ, res); + tc = TCG_COND_EQ; + ext_uns = true; + break; case 3: /* <> */ - return cond_make_0(TCG_COND_NE, res); + tc = TCG_COND_NE; + ext_uns = true; + break; case 4: /* < */ - return cond_make_0(TCG_COND_LT, res); + tc = TCG_COND_LT; + ext_uns = false; + break; case 5: /* >= */ - return cond_make_0(TCG_COND_GE, res); + tc = TCG_COND_GE; + ext_uns = false; + break; case 6: /* <= */ - return cond_make_0(TCG_COND_LE, res); + tc = TCG_COND_LE; + ext_uns = false; + break; case 7: /* > */ - return cond_make_0(TCG_COND_GT, res); + tc = TCG_COND_GT; + ext_uns = false; + break; case 14: /* OD */ case 15: /* EV */ @@ -1020,6 +1034,18 @@ static DisasCond do_log_cond(DisasContext *ctx, unsigned cf, TCGv_reg res) default: g_assert_not_reached(); } + + if (cond_need_ext(ctx, d)) { + TCGv_reg tmp = tcg_temp_new(); + + if (ext_uns) { + tcg_gen_ext32u_reg(tmp, res); + } else { + tcg_gen_ext32s_reg(tmp, res); + } + return cond_make_0_tmp(tc, tmp); + } + return cond_make_0(tc, res); } /* Similar, but for shift/extract/deposit conditions. */ @@ -1027,6 +1053,7 @@ static DisasCond do_log_cond(DisasContext *ctx, unsigned cf, TCGv_reg res) static DisasCond do_sed_cond(DisasContext *ctx, unsigned orig, TCGv_reg res) { unsigned c, f; + bool d = false; /* Convert the compressed condition codes to standard. 0-2 are the same as logicals (nv,<,<=), while 3 is OD. @@ -1037,7 +1064,7 @@ static DisasCond do_sed_cond(DisasContext *ctx, unsigned orig, TCGv_reg res) } f = (orig & 4) / 4; - return do_log_cond(ctx, c * 2 + f, res); + return do_log_cond(ctx, c * 2 + f, d, res); } /* Similar, but for unit conditions. */ @@ -1381,6 +1408,7 @@ static void do_log(DisasContext *ctx, unsigned rt, TCGv_reg in1, void (*fn)(TCGv_reg, TCGv_reg, TCGv_reg)) { TCGv_reg dest = dest_gpr(ctx, rt); + bool d = false; /* Perform the operation, and writeback. */ fn(dest, in1, in2); @@ -1389,7 +1417,7 @@ static void do_log(DisasContext *ctx, unsigned rt, TCGv_reg in1, /* Install the new nullification. */ cond_free(&ctx->null_cond); if (cf) { - ctx->null_cond = do_log_cond(ctx, cf, dest); + ctx->null_cond = do_log_cond(ctx, cf, d, dest); } } From 4fa52edf912115e0ad7011da9f9b2e43fd99f3aa Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 16 Sep 2023 19:34:39 -0700 Subject: [PATCH 604/974] target/hppa: Pass d to do_sed_cond Hoist the resolution of d up one level above do_sed_cond. The MOVB comparison and the existing shift/extract/deposit are all 32-bit. Signed-off-by: Richard Henderson --- target/hppa/translate.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index ac7f1f048c..eb4605a9c7 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1050,10 +1050,10 @@ static DisasCond do_log_cond(DisasContext *ctx, unsigned cf, bool d, /* Similar, but for shift/extract/deposit conditions. */ -static DisasCond do_sed_cond(DisasContext *ctx, unsigned orig, TCGv_reg res) +static DisasCond do_sed_cond(DisasContext *ctx, unsigned orig, bool d, + TCGv_reg res) { unsigned c, f; - bool d = false; /* Convert the compressed condition codes to standard. 0-2 are the same as logicals (nv,<,<=), while 3 is OD. @@ -3224,7 +3224,8 @@ static bool trans_movb(DisasContext *ctx, arg_movb *a) tcg_gen_mov_reg(dest, cpu_gr[a->r1]); } - cond = do_sed_cond(ctx, a->c, dest); + /* All MOVB conditions are 32-bit. */ + cond = do_sed_cond(ctx, a->c, false, dest); return do_cbranch(ctx, a->disp, a->n, &cond); } @@ -3238,7 +3239,8 @@ static bool trans_movbi(DisasContext *ctx, arg_movbi *a) dest = dest_gpr(ctx, a->r); tcg_gen_movi_reg(dest, a->i); - cond = do_sed_cond(ctx, a->c, dest); + /* All MOVBI conditions are 32-bit. */ + cond = do_sed_cond(ctx, a->c, false, dest); return do_cbranch(ctx, a->disp, a->n, &cond); } @@ -3276,7 +3278,7 @@ static bool trans_shrpw_sar(DisasContext *ctx, arg_shrpw_sar *a) /* Install the new nullification. */ cond_free(&ctx->null_cond); if (a->c) { - ctx->null_cond = do_sed_cond(ctx, a->c, dest); + ctx->null_cond = do_sed_cond(ctx, a->c, false, dest); } return nullify_end(ctx); } @@ -3312,7 +3314,7 @@ static bool trans_shrpw_imm(DisasContext *ctx, arg_shrpw_imm *a) /* Install the new nullification. */ cond_free(&ctx->null_cond); if (a->c) { - ctx->null_cond = do_sed_cond(ctx, a->c, dest); + ctx->null_cond = do_sed_cond(ctx, a->c, false, dest); } return nullify_end(ctx); } @@ -3346,7 +3348,7 @@ static bool trans_extrw_sar(DisasContext *ctx, arg_extrw_sar *a) /* Install the new nullification. */ cond_free(&ctx->null_cond); if (a->c) { - ctx->null_cond = do_sed_cond(ctx, a->c, dest); + ctx->null_cond = do_sed_cond(ctx, a->c, false, dest); } return nullify_end(ctx); } @@ -3373,7 +3375,7 @@ static bool trans_extrw_imm(DisasContext *ctx, arg_extrw_imm *a) /* Install the new nullification. */ cond_free(&ctx->null_cond); if (a->c) { - ctx->null_cond = do_sed_cond(ctx, a->c, dest); + ctx->null_cond = do_sed_cond(ctx, a->c, false, dest); } return nullify_end(ctx); } @@ -3410,7 +3412,7 @@ static bool trans_depwi_imm(DisasContext *ctx, arg_depwi_imm *a) /* Install the new nullification. */ cond_free(&ctx->null_cond); if (a->c) { - ctx->null_cond = do_sed_cond(ctx, a->c, dest); + ctx->null_cond = do_sed_cond(ctx, a->c, false, dest); } return nullify_end(ctx); } @@ -3440,7 +3442,7 @@ static bool trans_depw_imm(DisasContext *ctx, arg_depw_imm *a) /* Install the new nullification. */ cond_free(&ctx->null_cond); if (a->c) { - ctx->null_cond = do_sed_cond(ctx, a->c, dest); + ctx->null_cond = do_sed_cond(ctx, a->c, false, dest); } return nullify_end(ctx); } @@ -3477,7 +3479,7 @@ static bool do_depw_sar(DisasContext *ctx, unsigned rt, unsigned c, /* Install the new nullification. */ cond_free(&ctx->null_cond); if (c) { - ctx->null_cond = do_sed_cond(ctx, c, dest); + ctx->null_cond = do_sed_cond(ctx, c, false, dest); } return nullify_end(ctx); } From 59963d8fdf42244b8688b60b778a6209a1359fc2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 16 Sep 2023 19:52:46 -0700 Subject: [PATCH 605/974] target/hppa: Pass d to do_unit_cond Hoist the resolution of d up one level above do_unit_cond. All computations are logical, and are simplified by using a mask of the correct width, after which the result may be compared with zero. Signed-off-by: Richard Henderson --- target/hppa/translate.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index eb4605a9c7..41f4e06841 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1069,11 +1069,12 @@ static DisasCond do_sed_cond(DisasContext *ctx, unsigned orig, bool d, /* Similar, but for unit conditions. */ -static DisasCond do_unit_cond(unsigned cf, TCGv_reg res, +static DisasCond do_unit_cond(unsigned cf, bool d, TCGv_reg res, TCGv_reg in1, TCGv_reg in2) { DisasCond cond; TCGv_reg tmp, cb = NULL; + target_ureg d_repl = d ? 0x0000000100000001ull : 1; if (cf & 8) { /* Since we want to test lots of carry-out bits all at once, do not @@ -1100,32 +1101,32 @@ static DisasCond do_unit_cond(unsigned cf, TCGv_reg res, * https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord */ tmp = tcg_temp_new(); - tcg_gen_subi_reg(tmp, res, 0x01010101u); + tcg_gen_subi_reg(tmp, res, d_repl * 0x01010101u); tcg_gen_andc_reg(tmp, tmp, res); - tcg_gen_andi_reg(tmp, tmp, 0x80808080u); + tcg_gen_andi_reg(tmp, tmp, d_repl * 0x80808080u); cond = cond_make_0(TCG_COND_NE, tmp); break; case 3: /* SHZ / NHZ */ tmp = tcg_temp_new(); - tcg_gen_subi_reg(tmp, res, 0x00010001u); + tcg_gen_subi_reg(tmp, res, d_repl * 0x00010001u); tcg_gen_andc_reg(tmp, tmp, res); - tcg_gen_andi_reg(tmp, tmp, 0x80008000u); + tcg_gen_andi_reg(tmp, tmp, d_repl * 0x80008000u); cond = cond_make_0(TCG_COND_NE, tmp); break; case 4: /* SDC / NDC */ - tcg_gen_andi_reg(cb, cb, 0x88888888u); + tcg_gen_andi_reg(cb, cb, d_repl * 0x88888888u); cond = cond_make_0(TCG_COND_NE, cb); break; case 6: /* SBC / NBC */ - tcg_gen_andi_reg(cb, cb, 0x80808080u); + tcg_gen_andi_reg(cb, cb, d_repl * 0x80808080u); cond = cond_make_0(TCG_COND_NE, cb); break; case 7: /* SHC / NHC */ - tcg_gen_andi_reg(cb, cb, 0x80008000u); + tcg_gen_andi_reg(cb, cb, d_repl * 0x80008000u); cond = cond_make_0(TCG_COND_NE, cb); break; @@ -1441,6 +1442,7 @@ static void do_unit(DisasContext *ctx, unsigned rt, TCGv_reg in1, { TCGv_reg dest; DisasCond cond; + bool d = false; if (cf == 0) { dest = dest_gpr(ctx, rt); @@ -1451,7 +1453,7 @@ static void do_unit(DisasContext *ctx, unsigned rt, TCGv_reg in1, dest = tcg_temp_new(); fn(dest, in1, in2); - cond = do_unit_cond(cf, dest, in1, in2); + cond = do_unit_cond(cf, d, dest, in1, in2); if (is_tc) { TCGv_reg tmp = tcg_temp_new(); From 0c01f9ba2df42cef26596f5fa0ff5e741fb3c37f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 17 Oct 2023 14:09:58 -0700 Subject: [PATCH 606/974] linux-user/hppa: Fixes for TARGET_ABI32 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid target_ulong and use abi_* types. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/hppa/signal.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/linux-user/hppa/signal.c b/linux-user/hppa/signal.c index 17920e9ceb..d08a97dae6 100644 --- a/linux-user/hppa/signal.c +++ b/linux-user/hppa/signal.c @@ -86,7 +86,7 @@ static void setup_sigcontext(struct target_sigcontext *sc, CPUArchState *env) static void restore_sigcontext(CPUArchState *env, struct target_sigcontext *sc) { - target_ulong psw; + abi_ulong psw; int i; __get_user(psw, &sc->sc_gr[0]); @@ -150,10 +150,10 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, haddr = ka->_sa_handler; if (haddr & 2) { /* Function descriptor. */ - target_ulong *fdesc, dest; + abi_ptr *fdesc, dest; haddr &= -4; - fdesc = lock_user(VERIFY_READ, haddr, 2 * sizeof(target_ulong), 1); + fdesc = lock_user(VERIFY_READ, haddr, 2 * sizeof(abi_ptr), 1); if (!fdesc) { goto give_sigsegv; } From e207b4aa718ebe65f76775bc360408605b139a86 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 17 Oct 2023 20:48:38 -0700 Subject: [PATCH 607/974] target/hppa: Drop attempted gdbstub support for hppa64 There is no support for hppa64 in gdb. Any attempt to provide the data for the larger hppa64 registers results in an error from gdb. Mask CR_SAR writes to the width of the register: 5 or 6 bits. Signed-off-by: Richard Henderson --- target/hppa/gdbstub.c | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/target/hppa/gdbstub.c b/target/hppa/gdbstub.c index 48a514384f..4a965b38d7 100644 --- a/target/hppa/gdbstub.c +++ b/target/hppa/gdbstub.c @@ -21,11 +21,16 @@ #include "cpu.h" #include "gdbstub/helpers.h" +/* + * GDB 15 only supports PA1.0 via the remote protocol, and ignores + * any provided xml. Which means that any attempt to provide more + * data results in "Remote 'g' packet reply is too long". + */ + int hppa_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { - HPPACPU *cpu = HPPA_CPU(cs); - CPUHPPAState *env = &cpu->env; - target_ureg val; + CPUHPPAState *env = cpu_env(cs); + uint32_t val; switch (n) { case 0: @@ -139,24 +144,13 @@ int hppa_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) break; } - if (TARGET_REGISTER_BITS == 64) { - return gdb_get_reg64(mem_buf, val); - } else { - return gdb_get_reg32(mem_buf, val); - } + return gdb_get_reg32(mem_buf, val); } int hppa_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) { - HPPACPU *cpu = HPPA_CPU(cs); - CPUHPPAState *env = &cpu->env; - target_ureg val; - - if (TARGET_REGISTER_BITS == 64) { - val = ldq_p(mem_buf); - } else { - val = ldl_p(mem_buf); - } + CPUHPPAState *env = cpu_env(cs); + uint32_t val = ldl_p(mem_buf); switch (n) { case 0: @@ -166,7 +160,7 @@ int hppa_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) env->gr[n] = val; break; case 32: - env->cr[CR_SAR] = val; + env->cr[CR_SAR] = val & (hppa_is_pa20(env) ? 63 : 31); break; case 33: env->iaoq_f = val; @@ -278,5 +272,5 @@ int hppa_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) } break; } - return sizeof(target_ureg); + return 4; } From 08db1785449614ab35c521e365868435b0a0debd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 16 Sep 2023 21:06:49 -0700 Subject: [PATCH 608/974] target/hppa: Remove TARGET_HPPA64 Allow both user-only and system mode to run pa2.0 cpus. Avoid creating a separate qemu-system-hppa64 binary; force the qemu-hppa binary to use TARGET_ABI32. Signed-off-by: Richard Henderson --- configs/targets/hppa-linux-user.mak | 1 + target/hppa/cpu-param.h | 23 +++++++---------------- target/hppa/cpu.c | 2 -- target/hppa/cpu.h | 9 --------- target/hppa/translate.c | 2 -- 5 files changed, 8 insertions(+), 29 deletions(-) diff --git a/configs/targets/hppa-linux-user.mak b/configs/targets/hppa-linux-user.mak index 361ea39d71..8e0a80492f 100644 --- a/configs/targets/hppa-linux-user.mak +++ b/configs/targets/hppa-linux-user.mak @@ -1,4 +1,5 @@ TARGET_ARCH=hppa +TARGET_ABI32=y TARGET_SYSTBL_ABI=common,32 TARGET_SYSTBL=syscall.tbl TARGET_BIG_ENDIAN=y diff --git a/target/hppa/cpu-param.h b/target/hppa/cpu-param.h index c2791ae5f2..2fb8e7924b 100644 --- a/target/hppa/cpu-param.h +++ b/target/hppa/cpu-param.h @@ -8,26 +8,17 @@ #ifndef HPPA_CPU_PARAM_H #define HPPA_CPU_PARAM_H -#ifdef TARGET_HPPA64 -# define TARGET_LONG_BITS 64 -# define TARGET_REGISTER_BITS 64 -# define TARGET_VIRT_ADDR_SPACE_BITS 64 -# define TARGET_PHYS_ADDR_SPACE_BITS 64 -#elif defined(CONFIG_USER_ONLY) -# define TARGET_LONG_BITS 32 -# define TARGET_REGISTER_BITS 32 +#define TARGET_LONG_BITS 64 +#define TARGET_REGISTER_BITS 64 + +#if defined(CONFIG_USER_ONLY) && defined(TARGET_ABI32) +# define TARGET_PHYS_ADDR_SPACE_BITS 32 # define TARGET_VIRT_ADDR_SPACE_BITS 32 -# define TARGET_PHYS_ADDR_SPACE_BITS 32 #else -/* - * In order to form the GVA from space:offset, - * we need a 64-bit virtual address space. - */ -# define TARGET_LONG_BITS 64 -# define TARGET_REGISTER_BITS 32 +# define TARGET_PHYS_ADDR_SPACE_BITS 64 # define TARGET_VIRT_ADDR_SPACE_BITS 64 -# define TARGET_PHYS_ADDR_SPACE_BITS 32 #endif + #define TARGET_PAGE_BITS 12 #endif diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 70ce0c3b99..9582619be2 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -252,12 +252,10 @@ static const TypeInfo hppa_cpu_type_infos[] = { .class_size = sizeof(HPPACPUClass), .class_init = hppa_cpu_class_init, }, -#ifdef TARGET_HPPA64 { .name = TYPE_HPPA64_CPU, .parent = TYPE_HPPA_CPU, }, -#endif }; DEFINE_TYPES(hppa_cpu_type_infos) diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index cb838defb0..77ddb20ac2 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -120,11 +120,7 @@ #define PSW_T 0x01000000 #define PSW_S 0x02000000 #define PSW_E 0x04000000 -#ifdef TARGET_HPPA64 #define PSW_W 0x08000000 /* PA2.0 only */ -#else -#define PSW_W 0 -#endif #define PSW_Z 0x40000000 /* PA1.x only */ #define PSW_Y 0x80000000 /* PA1.x only */ @@ -137,13 +133,8 @@ #define PSW_SM_P PSW_P #define PSW_SM_Q PSW_Q /* Enable Interrupt State Collection */ #define PSW_SM_R PSW_R /* Enable Recover Counter Trap */ -#ifdef TARGET_HPPA64 #define PSW_SM_E 0x100 #define PSW_SM_W 0x200 /* PA2.0 only : Enable Wide Mode */ -#else -#define PSW_SM_E 0 -#define PSW_SM_W 0 -#endif #define CR_RC 0 #define CR_PID1 8 diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 41f4e06841..187d47f4c3 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -2177,7 +2177,6 @@ static bool trans_mfctl(DisasContext *ctx, arg_mfctl *a) switch (ctl) { case CR_SAR: -#ifdef TARGET_HPPA64 if (a->e == 0) { /* MFSAR without ,W masks low 5 bits. */ tmp = dest_gpr(ctx, rt); @@ -2185,7 +2184,6 @@ static bool trans_mfctl(DisasContext *ctx, arg_mfctl *a) save_gpr(ctx, rt, tmp); goto done; } -#endif save_gpr(ctx, rt, cpu_sar); goto done; case CR_IT: /* Interval Timer */ From fa8e3bed3885522260f796ed9d2a17f693c85381 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 16 Sep 2023 20:21:14 -0700 Subject: [PATCH 609/974] target/hppa: Decode d for logical instructions Signed-off-by: Richard Henderson --- target/hppa/insns.decode | 10 ++++++---- target/hppa/translate.c | 15 +++++++-------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode index aebe03ccfd..26ca9f1063 100644 --- a/target/hppa/insns.decode +++ b/target/hppa/insns.decode @@ -61,6 +61,7 @@ &rr_cf t r cf &rrr_cf t r1 r2 cf +&rrr_cf_d t r1 r2 cf d &rrr_cf_sh t r1 r2 cf sh &rri_cf t r i cf @@ -73,6 +74,7 @@ @rr_cf ...... r:5 ..... cf:4 ....... t:5 &rr_cf @rrr_cf ...... r2:5 r1:5 cf:4 ....... t:5 &rrr_cf +@rrr_cf_d ...... r2:5 r1:5 cf:4 ...... d:1 t:5 &rrr_cf_d @rrr_cf_sh ...... r2:5 r1:5 cf:4 .... sh:2 . t:5 &rrr_cf_sh @rrr_cf_sh0 ...... r2:5 r1:5 cf:4 ....... t:5 &rrr_cf_sh sh=0 @rri_cf ...... r:5 t:5 cf:4 . ........... &rri_cf i=%lowsign_11 @@ -150,10 +152,10 @@ lci 000001 ----- ----- -- 01001100 0 t:5 # Arith/Log #### -andcm 000010 ..... ..... .... 000000 - ..... @rrr_cf -and 000010 ..... ..... .... 001000 - ..... @rrr_cf -or 000010 ..... ..... .... 001001 - ..... @rrr_cf -xor 000010 ..... ..... .... 001010 0 ..... @rrr_cf +andcm 000010 ..... ..... .... 000000 . ..... @rrr_cf_d +and 000010 ..... ..... .... 001000 . ..... @rrr_cf_d +or 000010 ..... ..... .... 001001 . ..... @rrr_cf_d +xor 000010 ..... ..... .... 001010 . ..... @rrr_cf_d uxor 000010 ..... ..... .... 001110 0 ..... @rrr_cf ds 000010 ..... ..... .... 010001 0 ..... @rrr_cf cmpclr 000010 ..... ..... .... 100010 0 ..... @rrr_cf diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 187d47f4c3..227d59b263 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1405,11 +1405,10 @@ static void do_cmpclr(DisasContext *ctx, unsigned rt, TCGv_reg in1, } static void do_log(DisasContext *ctx, unsigned rt, TCGv_reg in1, - TCGv_reg in2, unsigned cf, + TCGv_reg in2, unsigned cf, bool d, void (*fn)(TCGv_reg, TCGv_reg, TCGv_reg)) { TCGv_reg dest = dest_gpr(ctx, rt); - bool d = false; /* Perform the operation, and writeback. */ fn(dest, in1, in2); @@ -1422,7 +1421,7 @@ static void do_log(DisasContext *ctx, unsigned rt, TCGv_reg in1, } } -static bool do_log_reg(DisasContext *ctx, arg_rrr_cf *a, +static bool do_log_reg(DisasContext *ctx, arg_rrr_cf_d *a, void (*fn)(TCGv_reg, TCGv_reg, TCGv_reg)) { TCGv_reg tcg_r1, tcg_r2; @@ -1432,7 +1431,7 @@ static bool do_log_reg(DisasContext *ctx, arg_rrr_cf *a, } tcg_r1 = load_gpr(ctx, a->r1); tcg_r2 = load_gpr(ctx, a->r2); - do_log(ctx, a->t, tcg_r1, tcg_r2, a->cf, fn); + do_log(ctx, a->t, tcg_r1, tcg_r2, a->cf, a->d, fn); return nullify_end(ctx); } @@ -2693,17 +2692,17 @@ static bool trans_sub_b_tsv(DisasContext *ctx, arg_rrr_cf *a) return do_sub_reg(ctx, a, true, true, false); } -static bool trans_andcm(DisasContext *ctx, arg_rrr_cf *a) +static bool trans_andcm(DisasContext *ctx, arg_rrr_cf_d *a) { return do_log_reg(ctx, a, tcg_gen_andc_reg); } -static bool trans_and(DisasContext *ctx, arg_rrr_cf *a) +static bool trans_and(DisasContext *ctx, arg_rrr_cf_d *a) { return do_log_reg(ctx, a, tcg_gen_and_reg); } -static bool trans_or(DisasContext *ctx, arg_rrr_cf *a) +static bool trans_or(DisasContext *ctx, arg_rrr_cf_d *a) { if (a->cf == 0) { unsigned r2 = a->r2; @@ -2755,7 +2754,7 @@ static bool trans_or(DisasContext *ctx, arg_rrr_cf *a) return do_log_reg(ctx, a, tcg_gen_or_reg); } -static bool trans_xor(DisasContext *ctx, arg_rrr_cf *a) +static bool trans_xor(DisasContext *ctx, arg_rrr_cf_d *a) { return do_log_reg(ctx, a, tcg_gen_xor_reg); } From af240753331940d0f3f8be6fe625c00fc64c4398 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 16 Sep 2023 20:32:37 -0700 Subject: [PATCH 610/974] target/hppa: Decode d for unit instructions Signed-off-by: Richard Henderson --- target/hppa/insns.decode | 14 +++++++------- target/hppa/translate.c | 25 ++++++++++++------------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode index 26ca9f1063..03b1a11cac 100644 --- a/target/hppa/insns.decode +++ b/target/hppa/insns.decode @@ -59,7 +59,7 @@ # All insns that need to form a virtual address should use this set. &ldst t b x disp sp m scale size -&rr_cf t r cf +&rr_cf_d t r cf d &rrr_cf t r1 r2 cf &rrr_cf_d t r1 r2 cf d &rrr_cf_sh t r1 r2 cf sh @@ -72,7 +72,7 @@ # Format definitions #### -@rr_cf ...... r:5 ..... cf:4 ....... t:5 &rr_cf +@rr_cf_d ...... r:5 ..... cf:4 ...... d:1 t:5 &rr_cf_d @rrr_cf ...... r2:5 r1:5 cf:4 ....... t:5 &rrr_cf @rrr_cf_d ...... r2:5 r1:5 cf:4 ...... d:1 t:5 &rrr_cf_d @rrr_cf_sh ...... r2:5 r1:5 cf:4 .... sh:2 . t:5 &rrr_cf_sh @@ -156,13 +156,13 @@ andcm 000010 ..... ..... .... 000000 . ..... @rrr_cf_d and 000010 ..... ..... .... 001000 . ..... @rrr_cf_d or 000010 ..... ..... .... 001001 . ..... @rrr_cf_d xor 000010 ..... ..... .... 001010 . ..... @rrr_cf_d -uxor 000010 ..... ..... .... 001110 0 ..... @rrr_cf +uxor 000010 ..... ..... .... 001110 . ..... @rrr_cf_d ds 000010 ..... ..... .... 010001 0 ..... @rrr_cf cmpclr 000010 ..... ..... .... 100010 0 ..... @rrr_cf -uaddcm 000010 ..... ..... .... 100110 0 ..... @rrr_cf -uaddcm_tc 000010 ..... ..... .... 100111 0 ..... @rrr_cf -dcor 000010 ..... 00000 .... 101110 0 ..... @rr_cf -dcor_i 000010 ..... 00000 .... 101111 0 ..... @rr_cf +uaddcm 000010 ..... ..... .... 100110 . ..... @rrr_cf_d +uaddcm_tc 000010 ..... ..... .... 100111 . ..... @rrr_cf_d +dcor 000010 ..... 00000 .... 101110 . ..... @rr_cf_d +dcor_i 000010 ..... 00000 .... 101111 . ..... @rr_cf_d add 000010 ..... ..... .... 0110.. - ..... @rrr_cf_sh add_l 000010 ..... ..... .... 1010.. 0 ..... @rrr_cf_sh diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 227d59b263..a0785bb32c 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1436,12 +1436,11 @@ static bool do_log_reg(DisasContext *ctx, arg_rrr_cf_d *a, } static void do_unit(DisasContext *ctx, unsigned rt, TCGv_reg in1, - TCGv_reg in2, unsigned cf, bool is_tc, + TCGv_reg in2, unsigned cf, bool d, bool is_tc, void (*fn)(TCGv_reg, TCGv_reg, TCGv_reg)) { TCGv_reg dest; DisasCond cond; - bool d = false; if (cf == 0) { dest = dest_gpr(ctx, rt); @@ -2772,7 +2771,7 @@ static bool trans_cmpclr(DisasContext *ctx, arg_rrr_cf *a) return nullify_end(ctx); } -static bool trans_uxor(DisasContext *ctx, arg_rrr_cf *a) +static bool trans_uxor(DisasContext *ctx, arg_rrr_cf_d *a) { TCGv_reg tcg_r1, tcg_r2; @@ -2781,11 +2780,11 @@ static bool trans_uxor(DisasContext *ctx, arg_rrr_cf *a) } tcg_r1 = load_gpr(ctx, a->r1); tcg_r2 = load_gpr(ctx, a->r2); - do_unit(ctx, a->t, tcg_r1, tcg_r2, a->cf, false, tcg_gen_xor_reg); + do_unit(ctx, a->t, tcg_r1, tcg_r2, a->cf, a->d, false, tcg_gen_xor_reg); return nullify_end(ctx); } -static bool do_uaddcm(DisasContext *ctx, arg_rrr_cf *a, bool is_tc) +static bool do_uaddcm(DisasContext *ctx, arg_rrr_cf_d *a, bool is_tc) { TCGv_reg tcg_r1, tcg_r2, tmp; @@ -2796,21 +2795,21 @@ static bool do_uaddcm(DisasContext *ctx, arg_rrr_cf *a, bool is_tc) tcg_r2 = load_gpr(ctx, a->r2); tmp = tcg_temp_new(); tcg_gen_not_reg(tmp, tcg_r2); - do_unit(ctx, a->t, tcg_r1, tmp, a->cf, is_tc, tcg_gen_add_reg); + do_unit(ctx, a->t, tcg_r1, tmp, a->cf, a->d, is_tc, tcg_gen_add_reg); return nullify_end(ctx); } -static bool trans_uaddcm(DisasContext *ctx, arg_rrr_cf *a) +static bool trans_uaddcm(DisasContext *ctx, arg_rrr_cf_d *a) { return do_uaddcm(ctx, a, false); } -static bool trans_uaddcm_tc(DisasContext *ctx, arg_rrr_cf *a) +static bool trans_uaddcm_tc(DisasContext *ctx, arg_rrr_cf_d *a) { return do_uaddcm(ctx, a, true); } -static bool do_dcor(DisasContext *ctx, arg_rr_cf *a, bool is_i) +static bool do_dcor(DisasContext *ctx, arg_rr_cf_d *a, bool is_i) { TCGv_reg tmp; @@ -2821,19 +2820,19 @@ static bool do_dcor(DisasContext *ctx, arg_rr_cf *a, bool is_i) if (!is_i) { tcg_gen_not_reg(tmp, tmp); } - tcg_gen_andi_reg(tmp, tmp, 0x11111111); + tcg_gen_andi_reg(tmp, tmp, (target_ureg)0x1111111111111111ull); tcg_gen_muli_reg(tmp, tmp, 6); - do_unit(ctx, a->t, load_gpr(ctx, a->r), tmp, a->cf, false, + do_unit(ctx, a->t, load_gpr(ctx, a->r), tmp, a->cf, a->d, false, is_i ? tcg_gen_add_reg : tcg_gen_sub_reg); return nullify_end(ctx); } -static bool trans_dcor(DisasContext *ctx, arg_rr_cf *a) +static bool trans_dcor(DisasContext *ctx, arg_rr_cf_d *a) { return do_dcor(ctx, a, false); } -static bool trans_dcor_i(DisasContext *ctx, arg_rr_cf *a) +static bool trans_dcor_i(DisasContext *ctx, arg_rr_cf_d *a) { return do_dcor(ctx, a, true); } From 345aa35f151a446e325dc120d0b6a6bebafc4d69 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 16 Sep 2023 20:40:23 -0700 Subject: [PATCH 611/974] target/hppa: Decode d for cmpclr instructions Signed-off-by: Richard Henderson --- target/hppa/insns.decode | 6 ++++-- target/hppa/translate.c | 11 +++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode index 03b1a11cac..d4a03b0299 100644 --- a/target/hppa/insns.decode +++ b/target/hppa/insns.decode @@ -64,6 +64,7 @@ &rrr_cf_d t r1 r2 cf d &rrr_cf_sh t r1 r2 cf sh &rri_cf t r i cf +&rri_cf_d t r i cf d &rrb_c_f disp n c f r1 r2 &rib_c_f disp n c f r i @@ -78,6 +79,7 @@ @rrr_cf_sh ...... r2:5 r1:5 cf:4 .... sh:2 . t:5 &rrr_cf_sh @rrr_cf_sh0 ...... r2:5 r1:5 cf:4 ....... t:5 &rrr_cf_sh sh=0 @rri_cf ...... r:5 t:5 cf:4 . ........... &rri_cf i=%lowsign_11 +@rri_cf_d ...... r:5 t:5 cf:4 d:1 ........... &rri_cf_d i=%lowsign_11 @rrb_cf ...... r2:5 r1:5 c:3 ........... n:1 . \ &rrb_c_f disp=%assemble_12 @@ -158,7 +160,7 @@ or 000010 ..... ..... .... 001001 . ..... @rrr_cf_d xor 000010 ..... ..... .... 001010 . ..... @rrr_cf_d uxor 000010 ..... ..... .... 001110 . ..... @rrr_cf_d ds 000010 ..... ..... .... 010001 0 ..... @rrr_cf -cmpclr 000010 ..... ..... .... 100010 0 ..... @rrr_cf +cmpclr 000010 ..... ..... .... 100010 . ..... @rrr_cf_d uaddcm 000010 ..... ..... .... 100110 . ..... @rrr_cf_d uaddcm_tc 000010 ..... ..... .... 100111 . ..... @rrr_cf_d dcor 000010 ..... 00000 .... 101110 . ..... @rr_cf_d @@ -189,7 +191,7 @@ addi_tc_tsv 101100 ..... ..... .... 1 ........... @rri_cf subi 100101 ..... ..... .... 0 ........... @rri_cf subi_tsv 100101 ..... ..... .... 1 ........... @rri_cf -cmpiclr 100100 ..... ..... .... 0 ........... @rri_cf +cmpiclr 100100 ..... ..... .... . ........... @rri_cf_d #### # Index Mem diff --git a/target/hppa/translate.c b/target/hppa/translate.c index a0785bb32c..8301d007ff 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1377,11 +1377,10 @@ static bool do_sub_imm(DisasContext *ctx, arg_rri_cf *a, bool is_tsv) } static void do_cmpclr(DisasContext *ctx, unsigned rt, TCGv_reg in1, - TCGv_reg in2, unsigned cf) + TCGv_reg in2, unsigned cf, bool d) { TCGv_reg dest, sv; DisasCond cond; - bool d = false; dest = tcg_temp_new(); tcg_gen_sub_reg(dest, in1, in2); @@ -2758,7 +2757,7 @@ static bool trans_xor(DisasContext *ctx, arg_rrr_cf_d *a) return do_log_reg(ctx, a, tcg_gen_xor_reg); } -static bool trans_cmpclr(DisasContext *ctx, arg_rrr_cf *a) +static bool trans_cmpclr(DisasContext *ctx, arg_rrr_cf_d *a) { TCGv_reg tcg_r1, tcg_r2; @@ -2767,7 +2766,7 @@ static bool trans_cmpclr(DisasContext *ctx, arg_rrr_cf *a) } tcg_r1 = load_gpr(ctx, a->r1); tcg_r2 = load_gpr(ctx, a->r2); - do_cmpclr(ctx, a->t, tcg_r1, tcg_r2, a->cf); + do_cmpclr(ctx, a->t, tcg_r1, tcg_r2, a->cf, a->d); return nullify_end(ctx); } @@ -2925,7 +2924,7 @@ static bool trans_subi_tsv(DisasContext *ctx, arg_rri_cf *a) return do_sub_imm(ctx, a, true); } -static bool trans_cmpiclr(DisasContext *ctx, arg_rri_cf *a) +static bool trans_cmpiclr(DisasContext *ctx, arg_rri_cf_d *a) { TCGv_reg tcg_im, tcg_r2; @@ -2935,7 +2934,7 @@ static bool trans_cmpiclr(DisasContext *ctx, arg_rri_cf *a) tcg_im = tcg_constant_reg(a->i); tcg_r2 = load_gpr(ctx, a->r); - do_cmpclr(ctx, a->t, tcg_im, tcg_r2, a->cf); + do_cmpclr(ctx, a->t, tcg_im, tcg_r2, a->cf, a->d); return nullify_end(ctx); } From faf97ba1577755eba593c59601f7a9ed93cf6728 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 16 Sep 2023 20:51:13 -0700 Subject: [PATCH 612/974] target/hppa: Decode d for add instructions Signed-off-by: Richard Henderson --- target/hppa/insns.decode | 16 ++++++++-------- target/hppa/translate.c | 21 +++++++++++---------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode index d4a03b0299..0f29869949 100644 --- a/target/hppa/insns.decode +++ b/target/hppa/insns.decode @@ -62,7 +62,7 @@ &rr_cf_d t r cf d &rrr_cf t r1 r2 cf &rrr_cf_d t r1 r2 cf d -&rrr_cf_sh t r1 r2 cf sh +&rrr_cf_d_sh t r1 r2 cf d sh &rri_cf t r i cf &rri_cf_d t r i cf d @@ -76,8 +76,8 @@ @rr_cf_d ...... r:5 ..... cf:4 ...... d:1 t:5 &rr_cf_d @rrr_cf ...... r2:5 r1:5 cf:4 ....... t:5 &rrr_cf @rrr_cf_d ...... r2:5 r1:5 cf:4 ...... d:1 t:5 &rrr_cf_d -@rrr_cf_sh ...... r2:5 r1:5 cf:4 .... sh:2 . t:5 &rrr_cf_sh -@rrr_cf_sh0 ...... r2:5 r1:5 cf:4 ....... t:5 &rrr_cf_sh sh=0 +@rrr_cf_d_sh ...... r2:5 r1:5 cf:4 .... sh:2 d:1 t:5 &rrr_cf_d_sh +@rrr_cf_d_sh0 ...... r2:5 r1:5 cf:4 ...... d:1 t:5 &rrr_cf_d_sh sh=0 @rri_cf ...... r:5 t:5 cf:4 . ........... &rri_cf i=%lowsign_11 @rri_cf_d ...... r:5 t:5 cf:4 d:1 ........... &rri_cf_d i=%lowsign_11 @@ -166,11 +166,11 @@ uaddcm_tc 000010 ..... ..... .... 100111 . ..... @rrr_cf_d dcor 000010 ..... 00000 .... 101110 . ..... @rr_cf_d dcor_i 000010 ..... 00000 .... 101111 . ..... @rr_cf_d -add 000010 ..... ..... .... 0110.. - ..... @rrr_cf_sh -add_l 000010 ..... ..... .... 1010.. 0 ..... @rrr_cf_sh -add_tsv 000010 ..... ..... .... 1110.. 0 ..... @rrr_cf_sh -add_c 000010 ..... ..... .... 011100 0 ..... @rrr_cf_sh0 -add_c_tsv 000010 ..... ..... .... 111100 0 ..... @rrr_cf_sh0 +add 000010 ..... ..... .... 0110.. . ..... @rrr_cf_d_sh +add_l 000010 ..... ..... .... 1010.. . ..... @rrr_cf_d_sh +add_tsv 000010 ..... ..... .... 1110.. . ..... @rrr_cf_d_sh +add_c 000010 ..... ..... .... 011100 . ..... @rrr_cf_d_sh0 +add_c_tsv 000010 ..... ..... .... 111100 . ..... @rrr_cf_d_sh0 sub 000010 ..... ..... .... 010000 - ..... @rrr_cf sub_tsv 000010 ..... ..... .... 110000 0 ..... @rrr_cf diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 8301d007ff..2f5cc597ad 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1186,12 +1186,11 @@ static TCGv_reg do_sub_sv(DisasContext *ctx, TCGv_reg res, static void do_add(DisasContext *ctx, unsigned rt, TCGv_reg in1, TCGv_reg in2, unsigned shift, bool is_l, - bool is_tsv, bool is_tc, bool is_c, unsigned cf) + bool is_tsv, bool is_tc, bool is_c, unsigned cf, bool d) { TCGv_reg dest, cb, cb_msb, cb_cond, sv, tmp; unsigned c = cf >> 1; DisasCond cond; - bool d = false; dest = tcg_temp_new(); cb = NULL; @@ -1256,7 +1255,7 @@ static void do_add(DisasContext *ctx, unsigned rt, TCGv_reg in1, ctx->null_cond = cond; } -static bool do_add_reg(DisasContext *ctx, arg_rrr_cf_sh *a, +static bool do_add_reg(DisasContext *ctx, arg_rrr_cf_d_sh *a, bool is_l, bool is_tsv, bool is_tc, bool is_c) { TCGv_reg tcg_r1, tcg_r2; @@ -1266,7 +1265,8 @@ static bool do_add_reg(DisasContext *ctx, arg_rrr_cf_sh *a, } tcg_r1 = load_gpr(ctx, a->r1); tcg_r2 = load_gpr(ctx, a->r2); - do_add(ctx, a->t, tcg_r1, tcg_r2, a->sh, is_l, is_tsv, is_tc, is_c, a->cf); + do_add(ctx, a->t, tcg_r1, tcg_r2, a->sh, is_l, + is_tsv, is_tc, is_c, a->cf, a->d); return nullify_end(ctx); } @@ -1280,7 +1280,8 @@ static bool do_add_imm(DisasContext *ctx, arg_rri_cf *a, } tcg_im = tcg_constant_reg(a->i); tcg_r2 = load_gpr(ctx, a->r); - do_add(ctx, a->t, tcg_im, tcg_r2, 0, 0, is_tsv, is_tc, 0, a->cf); + /* All ADDI conditions are 32-bit. */ + do_add(ctx, a->t, tcg_im, tcg_r2, 0, 0, is_tsv, is_tc, 0, a->cf, false); return nullify_end(ctx); } @@ -2635,27 +2636,27 @@ static bool trans_lci(DisasContext *ctx, arg_lci *a) return true; } -static bool trans_add(DisasContext *ctx, arg_rrr_cf_sh *a) +static bool trans_add(DisasContext *ctx, arg_rrr_cf_d_sh *a) { return do_add_reg(ctx, a, false, false, false, false); } -static bool trans_add_l(DisasContext *ctx, arg_rrr_cf_sh *a) +static bool trans_add_l(DisasContext *ctx, arg_rrr_cf_d_sh *a) { return do_add_reg(ctx, a, true, false, false, false); } -static bool trans_add_tsv(DisasContext *ctx, arg_rrr_cf_sh *a) +static bool trans_add_tsv(DisasContext *ctx, arg_rrr_cf_d_sh *a) { return do_add_reg(ctx, a, false, true, false, false); } -static bool trans_add_c(DisasContext *ctx, arg_rrr_cf_sh *a) +static bool trans_add_c(DisasContext *ctx, arg_rrr_cf_d_sh *a) { return do_add_reg(ctx, a, false, false, false, true); } -static bool trans_add_c_tsv(DisasContext *ctx, arg_rrr_cf_sh *a) +static bool trans_add_c_tsv(DisasContext *ctx, arg_rrr_cf_d_sh *a) { return do_add_reg(ctx, a, false, true, false, true); } From 63c427c615ea927fd93901f2dfeebb62ad3bf2bc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 16 Sep 2023 21:25:44 -0700 Subject: [PATCH 613/974] target/hppa: Decode d for sub instructions Signed-off-by: Richard Henderson --- target/hppa/insns.decode | 12 ++++++------ target/hppa/translate.c | 22 +++++++++++----------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode index 0f29869949..ad454adcbb 100644 --- a/target/hppa/insns.decode +++ b/target/hppa/insns.decode @@ -172,12 +172,12 @@ add_tsv 000010 ..... ..... .... 1110.. . ..... @rrr_cf_d_sh add_c 000010 ..... ..... .... 011100 . ..... @rrr_cf_d_sh0 add_c_tsv 000010 ..... ..... .... 111100 . ..... @rrr_cf_d_sh0 -sub 000010 ..... ..... .... 010000 - ..... @rrr_cf -sub_tsv 000010 ..... ..... .... 110000 0 ..... @rrr_cf -sub_tc 000010 ..... ..... .... 010011 0 ..... @rrr_cf -sub_tsv_tc 000010 ..... ..... .... 110011 0 ..... @rrr_cf -sub_b 000010 ..... ..... .... 010100 0 ..... @rrr_cf -sub_b_tsv 000010 ..... ..... .... 110100 0 ..... @rrr_cf +sub 000010 ..... ..... .... 010000 . ..... @rrr_cf_d +sub_tsv 000010 ..... ..... .... 110000 . ..... @rrr_cf_d +sub_tc 000010 ..... ..... .... 010011 . ..... @rrr_cf_d +sub_tsv_tc 000010 ..... ..... .... 110011 . ..... @rrr_cf_d +sub_b 000010 ..... ..... .... 010100 . ..... @rrr_cf_d +sub_b_tsv 000010 ..... ..... .... 110100 . ..... @rrr_cf_d ldil 001000 t:5 ..................... i=%assemble_21 addil 001010 r:5 ..................... i=%assemble_21 diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 2f5cc597ad..f2b2933c88 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1287,12 +1287,11 @@ static bool do_add_imm(DisasContext *ctx, arg_rri_cf *a, static void do_sub(DisasContext *ctx, unsigned rt, TCGv_reg in1, TCGv_reg in2, bool is_tsv, bool is_b, - bool is_tc, unsigned cf) + bool is_tc, unsigned cf, bool d) { TCGv_reg dest, sv, cb, cb_msb, zero, tmp; unsigned c = cf >> 1; DisasCond cond; - bool d = false; dest = tcg_temp_new(); cb = tcg_temp_new(); @@ -1350,7 +1349,7 @@ static void do_sub(DisasContext *ctx, unsigned rt, TCGv_reg in1, ctx->null_cond = cond; } -static bool do_sub_reg(DisasContext *ctx, arg_rrr_cf *a, +static bool do_sub_reg(DisasContext *ctx, arg_rrr_cf_d *a, bool is_tsv, bool is_b, bool is_tc) { TCGv_reg tcg_r1, tcg_r2; @@ -1360,7 +1359,7 @@ static bool do_sub_reg(DisasContext *ctx, arg_rrr_cf *a, } tcg_r1 = load_gpr(ctx, a->r1); tcg_r2 = load_gpr(ctx, a->r2); - do_sub(ctx, a->t, tcg_r1, tcg_r2, is_tsv, is_b, is_tc, a->cf); + do_sub(ctx, a->t, tcg_r1, tcg_r2, is_tsv, is_b, is_tc, a->cf, a->d); return nullify_end(ctx); } @@ -1373,7 +1372,8 @@ static bool do_sub_imm(DisasContext *ctx, arg_rri_cf *a, bool is_tsv) } tcg_im = tcg_constant_reg(a->i); tcg_r2 = load_gpr(ctx, a->r); - do_sub(ctx, a->t, tcg_im, tcg_r2, is_tsv, 0, 0, a->cf); + /* All SUBI conditions are 32-bit. */ + do_sub(ctx, a->t, tcg_im, tcg_r2, is_tsv, 0, 0, a->cf, false); return nullify_end(ctx); } @@ -2661,32 +2661,32 @@ static bool trans_add_c_tsv(DisasContext *ctx, arg_rrr_cf_d_sh *a) return do_add_reg(ctx, a, false, true, false, true); } -static bool trans_sub(DisasContext *ctx, arg_rrr_cf *a) +static bool trans_sub(DisasContext *ctx, arg_rrr_cf_d *a) { return do_sub_reg(ctx, a, false, false, false); } -static bool trans_sub_tsv(DisasContext *ctx, arg_rrr_cf *a) +static bool trans_sub_tsv(DisasContext *ctx, arg_rrr_cf_d *a) { return do_sub_reg(ctx, a, true, false, false); } -static bool trans_sub_tc(DisasContext *ctx, arg_rrr_cf *a) +static bool trans_sub_tc(DisasContext *ctx, arg_rrr_cf_d *a) { return do_sub_reg(ctx, a, false, false, true); } -static bool trans_sub_tsv_tc(DisasContext *ctx, arg_rrr_cf *a) +static bool trans_sub_tsv_tc(DisasContext *ctx, arg_rrr_cf_d *a) { return do_sub_reg(ctx, a, true, false, true); } -static bool trans_sub_b(DisasContext *ctx, arg_rrr_cf *a) +static bool trans_sub_b(DisasContext *ctx, arg_rrr_cf_d *a) { return do_sub_reg(ctx, a, false, true, false); } -static bool trans_sub_b_tsv(DisasContext *ctx, arg_rrr_cf *a) +static bool trans_sub_b_tsv(DisasContext *ctx, arg_rrr_cf_d *a) { return do_sub_reg(ctx, a, true, true, false); } From 84e224d4226001ed73b8da42e4ad155cf80b1eef Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 16 Sep 2023 21:41:32 -0700 Subject: [PATCH 614/974] target/hppa: Decode d for bb instructions Manipulate the shift count so that the bit to be tested is always placed at the MSB. Signed-off-by: Richard Henderson --- target/hppa/insns.decode | 4 ++-- target/hppa/translate.c | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode index ad454adcbb..b185523021 100644 --- a/target/hppa/insns.decode +++ b/target/hppa/insns.decode @@ -290,8 +290,8 @@ fmpysub_d 100110 ..... ..... ..... ..... 1 ..... @mpyadd # Conditional Branches #### -bb_sar 110000 00000 r:5 c:1 10 ........... n:1 . disp=%assemble_12 -bb_imm 110001 p:5 r:5 c:1 10 ........... n:1 . disp=%assemble_12 +bb_sar 110000 00000 r:5 c:1 1 d:1 ........... n:1 . disp=%assemble_12 +bb_imm 110001 p:5 r:5 c:1 1 d:1 ........... n:1 . disp=%assemble_12 movb 110010 ..... ..... ... ........... . . @rrb_cf f=0 movbi 110011 ..... ..... ... ........... . . @rib_cf f=0 diff --git a/target/hppa/translate.c b/target/hppa/translate.c index f2b2933c88..e326f63866 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -3172,13 +3172,12 @@ static bool trans_bb_sar(DisasContext *ctx, arg_bb_sar *a) { TCGv_reg tmp, tcg_r; DisasCond cond; - bool d = false; nullify_over(ctx); tmp = tcg_temp_new(); tcg_r = load_gpr(ctx, a->r); - if (cond_need_ext(ctx, d)) { + if (cond_need_ext(ctx, a->d)) { /* Force shift into [32,63] */ tcg_gen_ori_reg(tmp, cpu_sar, 32); tcg_gen_shl_reg(tmp, tcg_r, tmp); @@ -3194,14 +3193,13 @@ static bool trans_bb_imm(DisasContext *ctx, arg_bb_imm *a) { TCGv_reg tmp, tcg_r; DisasCond cond; - bool d = false; int p; nullify_over(ctx); tmp = tcg_temp_new(); tcg_r = load_gpr(ctx, a->r); - p = a->p | (cond_need_ext(ctx, d) ? 32 : 0); + p = a->p | (cond_need_ext(ctx, a->d) ? 32 : 0); tcg_gen_shli_reg(tmp, tcg_r, p); cond = cond_make_0(a->c ? TCG_COND_GE : TCG_COND_LT, tmp); From e9efd4bcdc5d94eb9e1bbc53e2d635438ba166a7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 16 Sep 2023 21:56:39 -0700 Subject: [PATCH 615/974] target/hppa: Decode d for cmpb instructions Signed-off-by: Richard Henderson --- target/hppa/insns.decode | 9 +++++++-- target/hppa/translate.c | 12 ++++++++---- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode index b185523021..fc327e2bb3 100644 --- a/target/hppa/insns.decode +++ b/target/hppa/insns.decode @@ -67,6 +67,7 @@ &rri_cf_d t r i cf d &rrb_c_f disp n c f r1 r2 +&rrb_c_d_f disp n c d f r1 r2 &rib_c_f disp n c f r i #### @@ -83,6 +84,8 @@ @rrb_cf ...... r2:5 r1:5 c:3 ........... n:1 . \ &rrb_c_f disp=%assemble_12 +@rrb_cdf ...... r2:5 r1:5 c:3 ........... n:1 . \ + &rrb_c_d_f disp=%assemble_12 @rib_cf ...... r:5 ..... c:3 ........... n:1 . \ &rib_c_f disp=%assemble_12 i=%im5_16 @@ -296,8 +299,10 @@ bb_imm 110001 p:5 r:5 c:1 1 d:1 ........... n:1 . disp=%assemble_12 movb 110010 ..... ..... ... ........... . . @rrb_cf f=0 movbi 110011 ..... ..... ... ........... . . @rib_cf f=0 -cmpb 100000 ..... ..... ... ........... . . @rrb_cf f=0 -cmpb 100010 ..... ..... ... ........... . . @rrb_cf f=1 +cmpb 100000 ..... ..... ... ........... . . @rrb_cdf d=0 f=0 +cmpb 100010 ..... ..... ... ........... . . @rrb_cdf d=0 f=1 +cmpb 100111 ..... ..... ... ........... . . @rrb_cdf d=1 f=0 +cmpb 101111 ..... ..... ... ........... . . @rrb_cdf d=1 f=1 cmpbi 100001 ..... ..... ... ........... . . @rib_cf f=0 cmpbi 100011 ..... ..... ... ........... . . @rib_cf f=1 diff --git a/target/hppa/translate.c b/target/hppa/translate.c index e326f63866..6cd06fbc0d 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -3091,11 +3091,10 @@ static bool trans_ldo(DisasContext *ctx, arg_ldo *a) } static bool do_cmpb(DisasContext *ctx, unsigned r, TCGv_reg in1, - unsigned c, unsigned f, unsigned n, int disp) + unsigned c, unsigned f, bool d, unsigned n, int disp) { TCGv_reg dest, in2, sv; DisasCond cond; - bool d = false; in2 = load_gpr(ctx, r); dest = tcg_temp_new(); @@ -3113,14 +3112,19 @@ static bool do_cmpb(DisasContext *ctx, unsigned r, TCGv_reg in1, static bool trans_cmpb(DisasContext *ctx, arg_cmpb *a) { + if (!ctx->is_pa20 && a->d) { + return false; + } nullify_over(ctx); - return do_cmpb(ctx, a->r2, load_gpr(ctx, a->r1), a->c, a->f, a->n, a->disp); + return do_cmpb(ctx, a->r2, load_gpr(ctx, a->r1), + a->c, a->f, a->d, a->n, a->disp); } static bool trans_cmpbi(DisasContext *ctx, arg_cmpbi *a) { nullify_over(ctx); - return do_cmpb(ctx, a->r, tcg_constant_reg(a->i), a->c, a->f, a->n, a->disp); + return do_cmpb(ctx, a->r, tcg_constant_reg(a->i), + a->c, a->f, false, a->n, a->disp); } static bool do_addb(DisasContext *ctx, unsigned r, TCGv_reg in1, From c65c3ee16b6e5382a93e3ed28fccf2fa545794e8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 16 Sep 2023 22:06:04 -0700 Subject: [PATCH 616/974] target/hppa: Decode CMPIB double-word Signed-off-by: Richard Henderson --- target/hppa/insns.decode | 10 ++++++++-- target/hppa/translate.c | 11 ++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode index fc327e2bb3..48f09c9b06 100644 --- a/target/hppa/insns.decode +++ b/target/hppa/insns.decode @@ -51,6 +51,7 @@ %pos_to_m 0:1 !function=pos_to_m %neg_to_m 0:1 !function=neg_to_m %a_to_m 2:1 !function=neg_to_m +%cmpbid_c 13:2 !function=cmpbid_c #### # Argument set definitions @@ -69,6 +70,7 @@ &rrb_c_f disp n c f r1 r2 &rrb_c_d_f disp n c d f r1 r2 &rib_c_f disp n c f r i +&rib_c_d_f disp n c d f r i #### # Format definitions @@ -88,6 +90,8 @@ &rrb_c_d_f disp=%assemble_12 @rib_cf ...... r:5 ..... c:3 ........... n:1 . \ &rib_c_f disp=%assemble_12 i=%im5_16 +@rib_cdf ...... r:5 ..... c:3 ........... n:1 . \ + &rib_c_d_f disp=%assemble_12 i=%im5_16 #### # System @@ -303,8 +307,10 @@ cmpb 100000 ..... ..... ... ........... . . @rrb_cdf d=0 f=0 cmpb 100010 ..... ..... ... ........... . . @rrb_cdf d=0 f=1 cmpb 100111 ..... ..... ... ........... . . @rrb_cdf d=1 f=0 cmpb 101111 ..... ..... ... ........... . . @rrb_cdf d=1 f=1 -cmpbi 100001 ..... ..... ... ........... . . @rib_cf f=0 -cmpbi 100011 ..... ..... ... ........... . . @rib_cf f=1 +cmpbi 100001 ..... ..... ... ........... . . @rib_cdf d=0 f=0 +cmpbi 100011 ..... ..... ... ........... . . @rib_cdf d=0 f=1 +cmpbi 111011 r:5 ..... f:1 .. ........... n:1 . \ + &rib_c_d_f d=1 disp=%assemble_12 c=%cmpbid_c i=%im5_16 addb 101000 ..... ..... ... ........... . . @rrb_cf f=0 addb 101010 ..... ..... ... ........... . . @rrb_cf f=1 diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 6cd06fbc0d..7f767fea64 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -329,6 +329,12 @@ static int expand_shl11(DisasContext *ctx, int val) return val << 11; } +/* Translate CMPI doubleword conditions to standard. */ +static int cmpbid_c(DisasContext *ctx, int val) +{ + return val ? val : 4; /* 0 == "*<<" */ +} + /* Include the auto-generated decoder. */ #include "decode-insns.c.inc" @@ -3122,9 +3128,12 @@ static bool trans_cmpb(DisasContext *ctx, arg_cmpb *a) static bool trans_cmpbi(DisasContext *ctx, arg_cmpbi *a) { + if (!ctx->is_pa20 && a->d) { + return false; + } nullify_over(ctx); return do_cmpb(ctx, a->r, tcg_constant_reg(a->i), - a->c, a->f, false, a->n, a->disp); + a->c, a->f, a->d, a->n, a->disp); } static bool do_addb(DisasContext *ctx, unsigned r, TCGv_reg in1, From f25d316098dd52ac74adf9416713cfe68c72f66e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 16 Sep 2023 22:25:30 -0700 Subject: [PATCH 617/974] target/hppa: Decode ADDB double-word Signed-off-by: Richard Henderson --- target/hppa/translate.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 7f767fea64..1b4fa401ba 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -3143,6 +3143,17 @@ static bool do_addb(DisasContext *ctx, unsigned r, TCGv_reg in1, DisasCond cond; bool d = false; + /* + * For hppa64, the ADDB conditions change with PSW.W, + * dropping ZNV, SV, OD in favor of double-word EQ, LT, LE. + */ + if (ctx->tb_flags & PSW_W) { + d = c >= 5; + if (d) { + c &= 3; + } + } + in2 = load_gpr(ctx, r); dest = tcg_temp_new(); sv = NULL; From 51416c4e41a2cfc084a08d8c78c8b90f14ea8661 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 16 Sep 2023 23:47:42 -0700 Subject: [PATCH 618/974] target/hppa: Implement LDD, LDCD, LDDA, STD, STDA Signed-off-by: Richard Henderson --- target/hppa/insns.decode | 15 +++++++++++---- target/hppa/translate.c | 4 ++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode index 48f09c9b06..33eec3f4c3 100644 --- a/target/hppa/insns.decode +++ b/target/hppa/insns.decode @@ -215,9 +215,14 @@ ld 000011 ..... ..... .. . 0 -- 00 size:2 ...... @ldstx st 000011 ..... ..... .. . 1 -- 10 size:2 ...... @stim5 ldc 000011 ..... ..... .. . 1 -- 0111 ...... @ldim5 size=2 ldc 000011 ..... ..... .. . 0 -- 0111 ...... @ldstx size=2 +ldc 000011 ..... ..... .. . 1 -- 0101 ...... @ldim5 size=3 +ldc 000011 ..... ..... .. . 0 -- 0101 ...... @ldstx size=3 lda 000011 ..... ..... .. . 1 -- 0110 ...... @ldim5 size=2 lda 000011 ..... ..... .. . 0 -- 0110 ...... @ldstx size=2 +lda 000011 ..... ..... .. . 1 -- 0100 ...... @ldim5 size=3 +lda 000011 ..... ..... .. . 0 -- 0100 ...... @ldstx size=3 sta 000011 ..... ..... .. . 1 -- 1110 ...... @stim5 size=2 +sta 000011 ..... ..... .. . 1 -- 1111 ...... @stim5 size=3 stby 000011 b:5 r:5 sp:2 a:1 1 -- 1100 m:1 ..... disp=%im5_0 @fldstwx ...... b:5 x:5 sp:2 scale:1 ....... m:1 ..... \ @@ -244,6 +249,8 @@ fstd 001011 ..... ..... .. . 1 -- 100 0 . ..... @fldstdi # Offset Mem #### +@ldstim11 ...... b:5 t:5 sp:2 .............. \ + &ldst disp=%assemble_11a m=%ma2_to_m x=0 scale=0 size=3 @ldstim14 ...... b:5 t:5 sp:2 .............. \ &ldst disp=%lowsign_14 x=0 scale=0 m=0 @ldstim14m ...... b:5 t:5 sp:2 .............. \ @@ -275,11 +282,11 @@ fstw 011110 b:5 ..... sp:2 .............. \ fstw 011111 b:5 ..... sp:2 ...........0.. \ &ldst disp=%assemble_12a t=%rm64 m=0 x=0 scale=0 size=2 -fldd 010100 b:5 t:5 sp:2 .......... .. 1 . \ - &ldst disp=%assemble_11a m=%ma2_to_m x=0 scale=0 size=3 +ld 010100 ..... ..... .. ............0. @ldstim11 +fldd 010100 ..... ..... .. ............1. @ldstim11 -fstd 011100 b:5 t:5 sp:2 .......... .. 1 . \ - &ldst disp=%assemble_11a m=%ma2_to_m x=0 scale=0 size=3 +st 011100 ..... ..... .. ............0. @ldstim11 +fstd 011100 ..... ..... .. ............1. @ldstim11 #### # Floating-point Multiply Add diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 1b4fa401ba..4562f865f4 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -2972,6 +2972,10 @@ static bool trans_ldc(DisasContext *ctx, arg_ldst *a) TCGv_reg zero, dest, ofs; TCGv_tl addr; + if (unlikely(TARGET_REGISTER_BITS == 32 && a->size > MO_32)) { + return gen_illegal(ctx); + } + nullify_over(ctx); if (a->m) { From 72ae4f2b82acbcbe32d30432e41e12b7b8f2785d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 20 Sep 2023 11:44:17 +0200 Subject: [PATCH 619/974] target/hppa: Implement DEPD, DEPDI Signed-off-by: Richard Henderson --- target/hppa/insns.decode | 19 ++++++++-- target/hppa/translate.c | 80 +++++++++++++++++++++++++++------------- 2 files changed, 69 insertions(+), 30 deletions(-) diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode index 33eec3f4c3..12684b590e 100644 --- a/target/hppa/insns.decode +++ b/target/hppa/insns.decode @@ -46,6 +46,10 @@ %im5_0 0:s1 1:4 %im5_16 16:s1 17:4 +%len5 0:5 !function=assemble_6 +%len6_8 8:1 0:5 !function=assemble_6 +%len6_12 12:1 0:5 !function=assemble_6 +%cpos6_11 11:1 5:5 %ma_to_m 5:1 13:1 !function=ma_to_m %ma2_to_m 2:2 !function=ma_to_m %pos_to_m 0:1 !function=pos_to_m @@ -334,10 +338,17 @@ shrpw_imm 110100 r2:5 r1:5 c:3 01 0 cpos:5 t:5 extrw_sar 110100 r:5 t:5 c:3 10 se:1 00000 clen:5 extrw_imm 110100 r:5 t:5 c:3 11 se:1 pos:5 clen:5 -depw_sar 110101 t:5 r:5 c:3 00 nz:1 00000 clen:5 -depw_imm 110101 t:5 r:5 c:3 01 nz:1 cpos:5 clen:5 -depwi_sar 110101 t:5 ..... c:3 10 nz:1 00000 clen:5 i=%im5_16 -depwi_imm 110101 t:5 ..... c:3 11 nz:1 cpos:5 clen:5 i=%im5_16 +dep_sar 110101 t:5 r:5 c:3 00 nz:1 00 000 ..... d=0 len=%len5 +dep_sar 110101 t:5 r:5 c:3 00 nz:1 1. 000 ..... d=1 len=%len6_8 +dep_imm 110101 t:5 r:5 c:3 01 nz:1 cpos:5 ..... d=0 len=%len5 +dep_imm 111100 t:5 r:5 c:3 .. nz:1 ..... ..... \ + d=1 len=%len6_12 cpos=%cpos6_11 +depi_sar 110101 t:5 ..... c:3 10 nz:1 d:1 . 000 ..... \ + i=%im5_16 len=%len6_8 +depi_imm 110101 t:5 ..... c:3 11 nz:1 cpos:5 ..... \ + d=0 i=%im5_16 len=%len5 +depi_imm 111101 t:5 ..... c:3 .. nz:1 ..... ..... \ + d=1 i=%im5_16 len=%len6_12 cpos=%cpos6_11 #### # Branch External diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 4562f865f4..ea2150cc55 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -329,6 +329,17 @@ static int expand_shl11(DisasContext *ctx, int val) return val << 11; } +static int assemble_6(DisasContext *ctx, int val) +{ + /* + * Officially, 32 * x + 32 - y. + * Here, x is already in bit 5, and y is [4:0]. + * Since -y = ~y + 1, in 5 bits 32 - y => y ^ 31 + 1, + * with the overflow from bit 4 summing with x. + */ + return (val ^ 31) + 1; +} + /* Translate CMPI doubleword conditions to standard. */ static int cmpbid_c(DisasContext *ctx, int val) { @@ -3404,17 +3415,23 @@ static bool trans_extrw_imm(DisasContext *ctx, arg_extrw_imm *a) return nullify_end(ctx); } -static bool trans_depwi_imm(DisasContext *ctx, arg_depwi_imm *a) +static bool trans_depi_imm(DisasContext *ctx, arg_depi_imm *a) { - unsigned len = 32 - a->clen; + unsigned len, width; target_sreg mask0, mask1; TCGv_reg dest; + if (!ctx->is_pa20 && a->d) { + return false; + } if (a->c) { nullify_over(ctx); } - if (a->cpos + len > 32) { - len = 32 - a->cpos; + + len = a->len; + width = a->d ? 64 : 32; + if (a->cpos + len > width) { + len = width - a->cpos; } dest = dest_gpr(ctx, a->t); @@ -3423,11 +3440,8 @@ static bool trans_depwi_imm(DisasContext *ctx, arg_depwi_imm *a) if (a->nz) { TCGv_reg src = load_gpr(ctx, a->t); - if (mask1 != -1) { - tcg_gen_andi_reg(dest, src, mask1); - src = dest; - } - tcg_gen_ori_reg(dest, src, mask0); + tcg_gen_andi_reg(dest, src, mask1); + tcg_gen_ori_reg(dest, dest, mask0); } else { tcg_gen_movi_reg(dest, mask0); } @@ -3436,22 +3450,28 @@ static bool trans_depwi_imm(DisasContext *ctx, arg_depwi_imm *a) /* Install the new nullification. */ cond_free(&ctx->null_cond); if (a->c) { - ctx->null_cond = do_sed_cond(ctx, a->c, false, dest); + ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest); } return nullify_end(ctx); } -static bool trans_depw_imm(DisasContext *ctx, arg_depw_imm *a) +static bool trans_dep_imm(DisasContext *ctx, arg_dep_imm *a) { unsigned rs = a->nz ? a->t : 0; - unsigned len = 32 - a->clen; + unsigned len, width; TCGv_reg dest, val; + if (!ctx->is_pa20 && a->d) { + return false; + } if (a->c) { nullify_over(ctx); } - if (a->cpos + len > 32) { - len = 32 - a->cpos; + + len = a->len; + width = a->d ? 64 : 32; + if (a->cpos + len > width) { + len = width - a->cpos; } dest = dest_gpr(ctx, a->t); @@ -3466,26 +3486,26 @@ static bool trans_depw_imm(DisasContext *ctx, arg_depw_imm *a) /* Install the new nullification. */ cond_free(&ctx->null_cond); if (a->c) { - ctx->null_cond = do_sed_cond(ctx, a->c, false, dest); + ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest); } return nullify_end(ctx); } -static bool do_depw_sar(DisasContext *ctx, unsigned rt, unsigned c, - unsigned nz, unsigned clen, TCGv_reg val) +static bool do_dep_sar(DisasContext *ctx, unsigned rt, unsigned c, + bool d, bool nz, unsigned len, TCGv_reg val) { unsigned rs = nz ? rt : 0; - unsigned len = 32 - clen; + unsigned widthm1 = d ? 63 : 31; TCGv_reg mask, tmp, shift, dest; - unsigned msb = 1U << (len - 1); + target_ureg msb = 1ULL << (len - 1); dest = dest_gpr(ctx, rt); shift = tcg_temp_new(); tmp = tcg_temp_new(); /* Convert big-endian bit numbering in SAR to left-shift. */ - tcg_gen_andi_reg(shift, cpu_sar, 31); - tcg_gen_xori_reg(shift, shift, 31); + tcg_gen_andi_reg(shift, cpu_sar, widthm1); + tcg_gen_xori_reg(shift, shift, widthm1); mask = tcg_temp_new(); tcg_gen_movi_reg(mask, msb + (msb - 1)); @@ -3503,25 +3523,33 @@ static bool do_depw_sar(DisasContext *ctx, unsigned rt, unsigned c, /* Install the new nullification. */ cond_free(&ctx->null_cond); if (c) { - ctx->null_cond = do_sed_cond(ctx, c, false, dest); + ctx->null_cond = do_sed_cond(ctx, c, d, dest); } return nullify_end(ctx); } -static bool trans_depw_sar(DisasContext *ctx, arg_depw_sar *a) +static bool trans_dep_sar(DisasContext *ctx, arg_dep_sar *a) { + if (!ctx->is_pa20 && a->d) { + return false; + } if (a->c) { nullify_over(ctx); } - return do_depw_sar(ctx, a->t, a->c, a->nz, a->clen, load_gpr(ctx, a->r)); + return do_dep_sar(ctx, a->t, a->c, a->d, a->nz, a->len, + load_gpr(ctx, a->r)); } -static bool trans_depwi_sar(DisasContext *ctx, arg_depwi_sar *a) +static bool trans_depi_sar(DisasContext *ctx, arg_depi_sar *a) { + if (!ctx->is_pa20 && a->d) { + return false; + } if (a->c) { nullify_over(ctx); } - return do_depw_sar(ctx, a->t, a->c, a->nz, a->clen, tcg_constant_reg(a->i)); + return do_dep_sar(ctx, a->t, a->c, a->d, a->nz, a->len, + tcg_constant_reg(a->i)); } static bool trans_be(DisasContext *ctx, arg_be *a) From bd792da3548cb8fcbfb58b37343f0cbc8500dc5f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 20 Sep 2023 12:04:29 +0200 Subject: [PATCH 620/974] target/hppa: Implement EXTRD Signed-off-by: Richard Henderson --- target/hppa/insns.decode | 7 +++++-- target/hppa/translate.c | 42 +++++++++++++++++++++++++++++----------- 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode index 12684b590e..7b51f39b9e 100644 --- a/target/hppa/insns.decode +++ b/target/hppa/insns.decode @@ -335,8 +335,11 @@ addbi 101011 ..... ..... ... ........... . . @rib_cf f=1 shrpw_sar 110100 r2:5 r1:5 c:3 00 0 00000 t:5 shrpw_imm 110100 r2:5 r1:5 c:3 01 0 cpos:5 t:5 -extrw_sar 110100 r:5 t:5 c:3 10 se:1 00000 clen:5 -extrw_imm 110100 r:5 t:5 c:3 11 se:1 pos:5 clen:5 +extr_sar 110100 r:5 t:5 c:3 10 se:1 00 000 ..... d=0 len=%len5 +extr_sar 110100 r:5 t:5 c:3 10 se:1 1. 000 ..... d=1 len=%len6_8 +extr_imm 110100 r:5 t:5 c:3 11 se:1 pos:5 ..... d=0 len=%len5 +extr_imm 110110 r:5 t:5 c:3 .. se:1 ..... ..... \ + d=1 len=%len6_12 pos=%cpos6_11 dep_sar 110101 t:5 r:5 c:3 00 nz:1 00 000 ..... d=0 len=%len5 dep_sar 110101 t:5 r:5 c:3 00 nz:1 1. 000 ..... d=1 len=%len6_8 diff --git a/target/hppa/translate.c b/target/hppa/translate.c index ea2150cc55..533e29879e 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -3354,11 +3354,14 @@ static bool trans_shrpw_imm(DisasContext *ctx, arg_shrpw_imm *a) return nullify_end(ctx); } -static bool trans_extrw_sar(DisasContext *ctx, arg_extrw_sar *a) +static bool trans_extr_sar(DisasContext *ctx, arg_extr_sar *a) { - unsigned len = 32 - a->clen; + unsigned widthm1 = a->d ? 63 : 31; TCGv_reg dest, src, tmp; + if (!ctx->is_pa20 && a->d) { + return false; + } if (a->c) { nullify_over(ctx); } @@ -3368,36 +3371,53 @@ static bool trans_extrw_sar(DisasContext *ctx, arg_extrw_sar *a) tmp = tcg_temp_new(); /* Recall that SAR is using big-endian bit numbering. */ - tcg_gen_andi_reg(tmp, cpu_sar, 31); - tcg_gen_xori_reg(tmp, tmp, 31); + tcg_gen_andi_reg(tmp, cpu_sar, widthm1); + tcg_gen_xori_reg(tmp, tmp, widthm1); if (a->se) { + if (!a->d) { + tcg_gen_ext32s_reg(dest, src); + src = dest; + } tcg_gen_sar_reg(dest, src, tmp); - tcg_gen_sextract_reg(dest, dest, 0, len); + tcg_gen_sextract_reg(dest, dest, 0, a->len); } else { + if (!a->d) { + tcg_gen_ext32u_reg(dest, src); + src = dest; + } tcg_gen_shr_reg(dest, src, tmp); - tcg_gen_extract_reg(dest, dest, 0, len); + tcg_gen_extract_reg(dest, dest, 0, a->len); } save_gpr(ctx, a->t, dest); /* Install the new nullification. */ cond_free(&ctx->null_cond); if (a->c) { - ctx->null_cond = do_sed_cond(ctx, a->c, false, dest); + ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest); } return nullify_end(ctx); } -static bool trans_extrw_imm(DisasContext *ctx, arg_extrw_imm *a) +static bool trans_extr_imm(DisasContext *ctx, arg_extr_imm *a) { - unsigned len = 32 - a->clen; - unsigned cpos = 31 - a->pos; + unsigned len, cpos, width; TCGv_reg dest, src; + if (!ctx->is_pa20 && a->d) { + return false; + } if (a->c) { nullify_over(ctx); } + len = a->len; + width = a->d ? 64 : 32; + cpos = width - 1 - a->pos; + if (cpos + len > width) { + len = width - cpos; + } + dest = dest_gpr(ctx, a->t); src = load_gpr(ctx, a->r); if (a->se) { @@ -3410,7 +3430,7 @@ static bool trans_extrw_imm(DisasContext *ctx, arg_extrw_imm *a) /* Install the new nullification. */ cond_free(&ctx->null_cond); if (a->c) { - ctx->null_cond = do_sed_cond(ctx, a->c, false, dest); + ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest); } return nullify_end(ctx); } From f7b775a9c0750873ae68d33721df5becaf62a59a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 20 Sep 2023 12:44:23 +0200 Subject: [PATCH 621/974] target/hppa: Implement SHRPD Signed-off-by: Richard Henderson --- target/hppa/insns.decode | 6 ++- target/hppa/translate.c | 97 ++++++++++++++++++++++++++++------------ 2 files changed, 72 insertions(+), 31 deletions(-) diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode index 7b51f39b9e..6f0c3f6ea5 100644 --- a/target/hppa/insns.decode +++ b/target/hppa/insns.decode @@ -332,8 +332,10 @@ addbi 101011 ..... ..... ... ........... . . @rib_cf f=1 # Shift, Extract, Deposit #### -shrpw_sar 110100 r2:5 r1:5 c:3 00 0 00000 t:5 -shrpw_imm 110100 r2:5 r1:5 c:3 01 0 cpos:5 t:5 +shrp_sar 110100 r2:5 r1:5 c:3 00 0 d:1 0000 t:5 +shrp_imm 110100 r2:5 r1:5 c:3 01 0 cpos:5 t:5 d=0 +shrp_imm 110100 r2:5 r1:5 c:3 0. 1 ..... t:5 \ + d=1 cpos=%cpos6_11 extr_sar 110100 r:5 t:5 c:3 10 se:1 00 000 ..... d=0 len=%len5 extr_sar 110100 r:5 t:5 c:3 10 se:1 1. 000 ..... d=1 len=%len6_8 diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 533e29879e..897b44d7e3 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -3279,34 +3279,64 @@ static bool trans_movbi(DisasContext *ctx, arg_movbi *a) return do_cbranch(ctx, a->disp, a->n, &cond); } -static bool trans_shrpw_sar(DisasContext *ctx, arg_shrpw_sar *a) +static bool trans_shrp_sar(DisasContext *ctx, arg_shrp_sar *a) { - TCGv_reg dest; + TCGv_reg dest, src2; + if (!ctx->is_pa20 && a->d) { + return false; + } if (a->c) { nullify_over(ctx); } dest = dest_gpr(ctx, a->t); + src2 = load_gpr(ctx, a->r2); if (a->r1 == 0) { - tcg_gen_ext32u_reg(dest, load_gpr(ctx, a->r2)); - tcg_gen_shr_reg(dest, dest, cpu_sar); + if (a->d) { + tcg_gen_shr_reg(dest, src2, cpu_sar); + } else { + TCGv_reg tmp = tcg_temp_new(); + + tcg_gen_ext32u_reg(dest, src2); + tcg_gen_andi_reg(tmp, cpu_sar, 31); + tcg_gen_shr_reg(dest, dest, tmp); + } } else if (a->r1 == a->r2) { - TCGv_i32 t32 = tcg_temp_new_i32(); - TCGv_i32 s32 = tcg_temp_new_i32(); + if (a->d) { + tcg_gen_rotr_reg(dest, src2, cpu_sar); + } else { + TCGv_i32 t32 = tcg_temp_new_i32(); + TCGv_i32 s32 = tcg_temp_new_i32(); - tcg_gen_trunc_reg_i32(t32, load_gpr(ctx, a->r2)); - tcg_gen_trunc_reg_i32(s32, cpu_sar); - tcg_gen_rotr_i32(t32, t32, s32); - tcg_gen_extu_i32_reg(dest, t32); + tcg_gen_trunc_reg_i32(t32, src2); + tcg_gen_trunc_reg_i32(s32, cpu_sar); + tcg_gen_andi_i32(s32, s32, 31); + tcg_gen_rotr_i32(t32, t32, s32); + tcg_gen_extu_i32_reg(dest, t32); + } } else { - TCGv_i64 t = tcg_temp_new_i64(); - TCGv_i64 s = tcg_temp_new_i64(); + TCGv_reg src1 = load_gpr(ctx, a->r1); - tcg_gen_concat_reg_i64(t, load_gpr(ctx, a->r2), load_gpr(ctx, a->r1)); - tcg_gen_extu_reg_i64(s, cpu_sar); - tcg_gen_shr_i64(t, t, s); - tcg_gen_trunc_i64_reg(dest, t); + if (a->d) { + TCGv_reg t = tcg_temp_new(); + TCGv_reg n = tcg_temp_new(); + + tcg_gen_xori_reg(n, cpu_sar, 63); + tcg_gen_shl_reg(t, src2, n); + tcg_gen_shli_reg(t, t, 1); + tcg_gen_shr_reg(dest, src1, cpu_sar); + tcg_gen_or_reg(dest, dest, t); + } else { + TCGv_i64 t = tcg_temp_new_i64(); + TCGv_i64 s = tcg_temp_new_i64(); + + tcg_gen_concat_reg_i64(t, src2, src1); + tcg_gen_extu_reg_i64(s, cpu_sar); + tcg_gen_andi_i64(s, s, 31); + tcg_gen_shr_i64(t, t, s); + tcg_gen_trunc_i64_reg(dest, t); + } } save_gpr(ctx, a->t, dest); @@ -3318,31 +3348,40 @@ static bool trans_shrpw_sar(DisasContext *ctx, arg_shrpw_sar *a) return nullify_end(ctx); } -static bool trans_shrpw_imm(DisasContext *ctx, arg_shrpw_imm *a) +static bool trans_shrp_imm(DisasContext *ctx, arg_shrp_imm *a) { - unsigned sa = 31 - a->cpos; + unsigned width, sa; TCGv_reg dest, t2; + if (!ctx->is_pa20 && a->d) { + return false; + } if (a->c) { nullify_over(ctx); } + width = a->d ? 64 : 32; + sa = width - 1 - a->cpos; + dest = dest_gpr(ctx, a->t); t2 = load_gpr(ctx, a->r2); if (a->r1 == 0) { - tcg_gen_extract_reg(dest, t2, sa, 32 - sa); - } else if (TARGET_REGISTER_BITS == 32) { + tcg_gen_extract_reg(dest, t2, sa, width - sa); + } else if (width == TARGET_REGISTER_BITS) { tcg_gen_extract2_reg(dest, t2, cpu_gr[a->r1], sa); - } else if (a->r1 == a->r2) { - TCGv_i32 t32 = tcg_temp_new_i32(); - tcg_gen_trunc_reg_i32(t32, t2); - tcg_gen_rotri_i32(t32, t32, sa); - tcg_gen_extu_i32_reg(dest, t32); } else { - TCGv_i64 t64 = tcg_temp_new_i64(); - tcg_gen_concat_reg_i64(t64, t2, cpu_gr[a->r1]); - tcg_gen_shri_i64(t64, t64, sa); - tcg_gen_trunc_i64_reg(dest, t64); + assert(!a->d); + if (a->r1 == a->r2) { + TCGv_i32 t32 = tcg_temp_new_i32(); + tcg_gen_trunc_reg_i32(t32, t2); + tcg_gen_rotri_i32(t32, t32, sa); + tcg_gen_extu_i32_reg(dest, t32); + } else { + TCGv_i64 t64 = tcg_temp_new_i64(); + tcg_gen_concat_reg_i64(t64, t2, cpu_gr[a->r1]); + tcg_gen_shri_i64(t64, t64, sa); + tcg_gen_trunc_i64_reg(dest, t64); + } } save_gpr(ctx, a->t, dest); From a8966ba70a6fdf5602d7235f24215a75aea5c98a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 20 Sep 2023 13:11:27 +0200 Subject: [PATCH 622/974] target/hppa: Implement CLRBTS, POPBTS, PUSHBTS, PUSHNOM Signed-off-by: Richard Henderson --- target/hppa/insns.decode | 2 ++ target/hppa/translate.c | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode index 6f0c3f6ea5..ba7731b517 100644 --- a/target/hppa/insns.decode +++ b/target/hppa/insns.decode @@ -381,6 +381,8 @@ bl 111010 ..... ..... 101 ........... n:1 . &BL l=2 \ disp=%assemble_22 b_gate 111010 ..... ..... 001 ........... . . @bl blr 111010 l:5 x:5 010 00000000000 n:1 0 +nopbts 111010 00000 00000 010 0---------1 0 1 # clrbts/popbts +nopbts 111010 00000 ----- 010 00000000000 0 1 # pushbts/pushnom bv 111010 b:5 x:5 110 00000000000 n:1 0 bve 111010 b:5 00000 110 10000000000 n:1 - l=0 bve 111010 b:5 00000 111 10000000000 n:1 - l=2 diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 897b44d7e3..91249d89ca 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -3782,6 +3782,12 @@ static bool trans_bve(DisasContext *ctx, arg_bve *a) #endif } +static bool trans_nopbts(DisasContext *ctx, arg_nopbts *a) +{ + /* All branch target stack instructions implement as nop. */ + return ctx->is_pa20; +} + /* * Float class 0 */ From 25460fc5a71ef2bf6679d263e16f86ed7bb341a5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 20 Sep 2023 17:00:23 +0200 Subject: [PATCH 623/974] target/hppa: Implement STDBY Signed-off-by: Richard Henderson --- target/hppa/helper.h | 5 ++ target/hppa/insns.decode | 1 + target/hppa/op_helper.c | 178 +++++++++++++++++++++++++++++++++++++-- target/hppa/translate.c | 34 ++++++++ 4 files changed, 213 insertions(+), 5 deletions(-) diff --git a/target/hppa/helper.h b/target/hppa/helper.h index 647f043c85..9920d38ded 100644 --- a/target/hppa/helper.h +++ b/target/hppa/helper.h @@ -16,6 +16,11 @@ DEF_HELPER_FLAGS_3(stby_b_parallel, TCG_CALL_NO_WG, void, env, tl, tr) DEF_HELPER_FLAGS_3(stby_e, TCG_CALL_NO_WG, void, env, tl, tr) DEF_HELPER_FLAGS_3(stby_e_parallel, TCG_CALL_NO_WG, void, env, tl, tr) +DEF_HELPER_FLAGS_3(stdby_b, TCG_CALL_NO_WG, void, env, tl, tr) +DEF_HELPER_FLAGS_3(stdby_b_parallel, TCG_CALL_NO_WG, void, env, tl, tr) +DEF_HELPER_FLAGS_3(stdby_e, TCG_CALL_NO_WG, void, env, tl, tr) +DEF_HELPER_FLAGS_3(stdby_e_parallel, TCG_CALL_NO_WG, void, env, tl, tr) + DEF_HELPER_FLAGS_1(ldc_check, TCG_CALL_NO_RWG, void, tl) DEF_HELPER_FLAGS_4(probe, TCG_CALL_NO_WG, tr, env, tl, i32, i32) diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode index ba7731b517..9d8c6a1a16 100644 --- a/target/hppa/insns.decode +++ b/target/hppa/insns.decode @@ -228,6 +228,7 @@ lda 000011 ..... ..... .. . 0 -- 0100 ...... @ldstx size=3 sta 000011 ..... ..... .. . 1 -- 1110 ...... @stim5 size=2 sta 000011 ..... ..... .. . 1 -- 1111 ...... @stim5 size=3 stby 000011 b:5 r:5 sp:2 a:1 1 -- 1100 m:1 ..... disp=%im5_0 +stdby 000011 b:5 r:5 sp:2 a:1 1 -- 1101 m:1 ..... disp=%im5_0 @fldstwx ...... b:5 x:5 sp:2 scale:1 ....... m:1 ..... \ &ldst t=%rt64 disp=0 size=2 diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c index 837e2b3117..b5b45f5120 100644 --- a/target/hppa/op_helper.c +++ b/target/hppa/op_helper.c @@ -56,11 +56,11 @@ void HELPER(tcond)(CPUHPPAState *env, target_ureg cond) } } -static void atomic_store_3(CPUHPPAState *env, target_ulong addr, - uint32_t val, uintptr_t ra) +static void atomic_store_mask32(CPUHPPAState *env, target_ulong addr, + uint32_t val, uint32_t mask, uintptr_t ra) { int mmu_idx = cpu_mmu_index(env, 0); - uint32_t old, new, cmp, mask, *haddr; + uint32_t old, new, cmp, *haddr; void *vaddr; vaddr = probe_access(env, addr, 3, MMU_DATA_STORE, mmu_idx, ra); @@ -81,6 +81,35 @@ static void atomic_store_3(CPUHPPAState *env, target_ulong addr, } } +static void atomic_store_mask64(CPUHPPAState *env, target_ulong addr, + uint64_t val, uint64_t mask, + int size, uintptr_t ra) +{ +#ifdef CONFIG_ATOMIC64 + int mmu_idx = cpu_mmu_index(env, 0); + uint64_t old, new, cmp, *haddr; + void *vaddr; + + vaddr = probe_access(env, addr, size, MMU_DATA_STORE, mmu_idx, ra); + if (vaddr == NULL) { + cpu_loop_exit_atomic(env_cpu(env), ra); + } + haddr = (uint64_t *)((uintptr_t)vaddr & -8); + + old = *haddr; + while (1) { + new = be32_to_cpu((cpu_to_be32(old) & ~mask) | (val & mask)); + cmp = qatomic_cmpxchg__nocheck(haddr, old, new); + if (cmp == old) { + return; + } + old = cmp; + } +#else + cpu_loop_exit_atomic(env_cpu(env), ra); +#endif +} + static void do_stby_b(CPUHPPAState *env, target_ulong addr, target_ureg val, bool parallel, uintptr_t ra) { @@ -94,7 +123,7 @@ static void do_stby_b(CPUHPPAState *env, target_ulong addr, target_ureg val, case 1: /* The 3 byte store must appear atomic. */ if (parallel) { - atomic_store_3(env, addr, val, ra); + atomic_store_mask32(env, addr, val, 0x00ffffffu, ra); } else { cpu_stb_data_ra(env, addr, val >> 16, ra); cpu_stw_data_ra(env, addr + 1, val, ra); @@ -106,6 +135,62 @@ static void do_stby_b(CPUHPPAState *env, target_ulong addr, target_ureg val, } } +static void do_stdby_b(CPUHPPAState *env, target_ulong addr, uint64_t val, + bool parallel, uintptr_t ra) +{ + switch (addr & 7) { + case 7: + cpu_stb_data_ra(env, addr, val, ra); + break; + case 6: + cpu_stw_data_ra(env, addr, val, ra); + break; + case 5: + /* The 3 byte store must appear atomic. */ + if (parallel) { + atomic_store_mask32(env, addr, val, 0x00ffffffu, ra); + } else { + cpu_stb_data_ra(env, addr, val >> 16, ra); + cpu_stw_data_ra(env, addr + 1, val, ra); + } + break; + case 4: + cpu_stl_data_ra(env, addr, val, ra); + break; + case 3: + /* The 5 byte store must appear atomic. */ + if (parallel) { + atomic_store_mask64(env, addr, val, 0x000000ffffffffffull, 5, ra); + } else { + cpu_stb_data_ra(env, addr, val >> 32, ra); + cpu_stl_data_ra(env, addr + 1, val, ra); + } + break; + case 2: + /* The 6 byte store must appear atomic. */ + if (parallel) { + atomic_store_mask64(env, addr, val, 0x0000ffffffffffffull, 6, ra); + } else { + cpu_stw_data_ra(env, addr, val >> 32, ra); + cpu_stl_data_ra(env, addr + 2, val, ra); + } + break; + case 1: + /* The 7 byte store must appear atomic. */ + if (parallel) { + atomic_store_mask64(env, addr, val, 0x00ffffffffffffffull, 7, ra); + } else { + cpu_stb_data_ra(env, addr, val >> 48, ra); + cpu_stw_data_ra(env, addr + 1, val >> 32, ra); + cpu_stl_data_ra(env, addr + 3, val, ra); + } + break; + default: + cpu_stq_data_ra(env, addr, val, ra); + break; + } +} + void HELPER(stby_b)(CPUHPPAState *env, target_ulong addr, target_ureg val) { do_stby_b(env, addr, val, false, GETPC()); @@ -117,6 +202,17 @@ void HELPER(stby_b_parallel)(CPUHPPAState *env, target_ulong addr, do_stby_b(env, addr, val, true, GETPC()); } +void HELPER(stdby_b)(CPUHPPAState *env, target_ulong addr, target_ureg val) +{ + do_stdby_b(env, addr, val, false, GETPC()); +} + +void HELPER(stdby_b_parallel)(CPUHPPAState *env, target_ulong addr, + target_ureg val) +{ + do_stdby_b(env, addr, val, true, GETPC()); +} + static void do_stby_e(CPUHPPAState *env, target_ulong addr, target_ureg val, bool parallel, uintptr_t ra) { @@ -124,7 +220,68 @@ static void do_stby_e(CPUHPPAState *env, target_ulong addr, target_ureg val, case 3: /* The 3 byte store must appear atomic. */ if (parallel) { - atomic_store_3(env, addr - 3, val, ra); + atomic_store_mask32(env, addr - 3, val, 0xffffff00u, ra); + } else { + cpu_stw_data_ra(env, addr - 3, val >> 16, ra); + cpu_stb_data_ra(env, addr - 1, val >> 8, ra); + } + break; + case 2: + cpu_stw_data_ra(env, addr - 2, val >> 16, ra); + break; + case 1: + cpu_stb_data_ra(env, addr - 1, val >> 24, ra); + break; + default: + /* Nothing is stored, but protection is checked and the + cacheline is marked dirty. */ + probe_write(env, addr, 0, cpu_mmu_index(env, 0), ra); + break; + } +} + +static void do_stdby_e(CPUHPPAState *env, target_ulong addr, uint64_t val, + bool parallel, uintptr_t ra) +{ + switch (addr & 7) { + case 7: + /* The 7 byte store must appear atomic. */ + if (parallel) { + atomic_store_mask64(env, addr - 7, val, + 0xffffffffffffff00ull, 7, ra); + } else { + cpu_stl_data_ra(env, addr - 7, val >> 32, ra); + cpu_stw_data_ra(env, addr - 3, val >> 16, ra); + cpu_stb_data_ra(env, addr - 1, val >> 8, ra); + } + break; + case 6: + /* The 6 byte store must appear atomic. */ + if (parallel) { + atomic_store_mask64(env, addr - 6, val, + 0xffffffffffff0000ull, 6, ra); + } else { + cpu_stl_data_ra(env, addr - 6, val >> 32, ra); + cpu_stw_data_ra(env, addr - 2, val >> 16, ra); + } + break; + case 5: + /* The 5 byte store must appear atomic. */ + if (parallel) { + atomic_store_mask64(env, addr - 5, val, + 0xffffffffff000000ull, 5, ra); + } else { + cpu_stl_data_ra(env, addr - 5, val >> 32, ra); + cpu_stb_data_ra(env, addr - 1, val >> 24, ra); + } + break; + case 4: + cpu_stl_data_ra(env, addr - 4, val >> 32, ra); + break; + case 3: + /* The 3 byte store must appear atomic. */ + if (parallel) { + atomic_store_mask32(env, addr - 3, val, 0xffffff00u, ra); } else { cpu_stw_data_ra(env, addr - 3, val >> 16, ra); cpu_stb_data_ra(env, addr - 1, val >> 8, ra); @@ -155,6 +312,17 @@ void HELPER(stby_e_parallel)(CPUHPPAState *env, target_ulong addr, do_stby_e(env, addr, val, true, GETPC()); } +void HELPER(stdby_e)(CPUHPPAState *env, target_ulong addr, target_ureg val) +{ + do_stdby_e(env, addr, val, false, GETPC()); +} + +void HELPER(stdby_e_parallel)(CPUHPPAState *env, target_ulong addr, + target_ureg val) +{ + do_stdby_e(env, addr, val, true, GETPC()); +} + void HELPER(ldc_check)(target_ulong addr) { if (unlikely(addr & 0xf)) { diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 91249d89ca..485251bded 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -3052,6 +3052,40 @@ static bool trans_stby(DisasContext *ctx, arg_stby *a) return nullify_end(ctx); } +static bool trans_stdby(DisasContext *ctx, arg_stby *a) +{ + TCGv_reg ofs, val; + TCGv_tl addr; + + if (!ctx->is_pa20) { + return false; + } + nullify_over(ctx); + + form_gva(ctx, &addr, &ofs, a->b, 0, 0, a->disp, a->sp, a->m, + ctx->mmu_idx == MMU_PHYS_IDX); + val = load_gpr(ctx, a->r); + if (a->a) { + if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { + gen_helper_stdby_e_parallel(tcg_env, addr, val); + } else { + gen_helper_stdby_e(tcg_env, addr, val); + } + } else { + if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { + gen_helper_stdby_b_parallel(tcg_env, addr, val); + } else { + gen_helper_stdby_b(tcg_env, addr, val); + } + } + if (a->m) { + tcg_gen_andi_reg(ofs, ofs, ~7); + save_gpr(ctx, a->b, ofs); + } + + return nullify_end(ctx); +} + static bool trans_lda(DisasContext *ctx, arg_ldst *a) { int hold_mmu_idx = ctx->mmu_idx; From 8577f354792414a2b24ef72c64730ed0f6bb071e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 12 Oct 2023 17:55:12 -0700 Subject: [PATCH 624/974] target/hppa: Implement IDTLBT, IITLBT Rename the existing insert tlb helpers to emphasize that they are for pa1.1 cpus. Implement a combined i/d tlb for pa2.0. Still missing is the new 'P' tlb bit. Signed-off-by: Richard Henderson --- target/hppa/helper.h | 6 ++-- target/hppa/insns.decode | 4 +++ target/hppa/mem_helper.c | 61 ++++++++++++++++++++++++++++++++++++---- target/hppa/translate.c | 42 +++++++++++++++++++++++---- 4 files changed, 100 insertions(+), 13 deletions(-) diff --git a/target/hppa/helper.h b/target/hppa/helper.h index 9920d38ded..0b346e24f3 100644 --- a/target/hppa/helper.h +++ b/target/hppa/helper.h @@ -94,8 +94,10 @@ DEF_HELPER_FLAGS_2(write_interval_timer, TCG_CALL_NO_RWG, void, env, tr) DEF_HELPER_FLAGS_2(write_eirr, TCG_CALL_NO_RWG, void, env, tr) DEF_HELPER_FLAGS_2(write_eiem, TCG_CALL_NO_RWG, void, env, tr) DEF_HELPER_FLAGS_2(swap_system_mask, TCG_CALL_NO_RWG, tr, env, tr) -DEF_HELPER_FLAGS_3(itlba, TCG_CALL_NO_RWG, void, env, tl, tr) -DEF_HELPER_FLAGS_3(itlbp, TCG_CALL_NO_RWG, void, env, tl, tr) +DEF_HELPER_FLAGS_3(itlba_pa11, TCG_CALL_NO_RWG, void, env, tl, tr) +DEF_HELPER_FLAGS_3(itlbp_pa11, TCG_CALL_NO_RWG, void, env, tl, tr) +DEF_HELPER_FLAGS_3(idtlbt_pa20, TCG_CALL_NO_RWG, void, env, tr, tr) +DEF_HELPER_FLAGS_3(iitlbt_pa20, TCG_CALL_NO_RWG, void, env, tr, tr) DEF_HELPER_FLAGS_2(ptlb, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_1(ptlbe, TCG_CALL_NO_RWG, void, env) DEF_HELPER_FLAGS_2(lpa, TCG_CALL_NO_WG, tr, env, tl) diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode index 9d8c6a1a16..820049b0c5 100644 --- a/target/hppa/insns.decode +++ b/target/hppa/insns.decode @@ -145,6 +145,7 @@ nop_addrx 000001 ..... ..... -- 01001110 . 00000 @addrx # pdc probe 000001 b:5 ri:5 sp:2 imm:1 100011 write:1 0 t:5 +# pa1.x tlb insert instructions ixtlbx 000001 b:5 r:5 sp:2 0100000 addr:1 0 00000 data=1 ixtlbx 000001 b:5 r:5 ... 000000 addr:1 0 00000 \ sp=%assemble_sr3x data=0 @@ -152,6 +153,9 @@ ixtlbx 000001 b:5 r:5 ... 000000 addr:1 0 00000 \ # pcxl and pcxl2 Fast TLB Insert instructions ixtlbxf 000001 00000 r:5 00 0 data:1 01000 addr:1 0 00000 +# pa2.0 tlb insert idtlbt and iitlbt instructions +ixtlbt 000001 r2:5 r1:5 000 data:1 100000 0 00000 # idtlbt + pxtlbx 000001 b:5 x:5 sp:2 0100100 local:1 m:1 ----- data=1 pxtlbx 000001 b:5 x:5 ... 000100 local:1 m:1 ----- \ sp=%assemble_sr3x data=0 diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c index 420b43a0f6..d5d2d62f4a 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -344,7 +344,7 @@ bool hppa_cpu_tlb_fill(CPUState *cs, vaddr addr, int size, } /* Insert (Insn/Data) TLB Address. Note this is PA 1.1 only. */ -void HELPER(itlba)(CPUHPPAState *env, target_ulong addr, target_ureg reg) +void HELPER(itlba_pa11)(CPUHPPAState *env, target_ulong addr, target_ureg reg) { HPPATLBEntry *ent; @@ -365,7 +365,8 @@ void HELPER(itlba)(CPUHPPAState *env, target_ulong addr, target_ureg reg) trace_hppa_tlb_itlba(env, ent, ent->itree.start, ent->itree.last, ent->pa); } -static void set_access_bits(CPUHPPAState *env, HPPATLBEntry *ent, target_ureg reg) +static void set_access_bits_pa11(CPUHPPAState *env, HPPATLBEntry *ent, + target_ureg reg) { ent->access_id = extract32(reg, 1, 18); ent->u = extract32(reg, 19, 1); @@ -383,20 +384,70 @@ static void set_access_bits(CPUHPPAState *env, HPPATLBEntry *ent, target_ureg re } /* Insert (Insn/Data) TLB Protection. Note this is PA 1.1 only. */ -void HELPER(itlbp)(CPUHPPAState *env, target_ulong addr, target_ureg reg) +void HELPER(itlbp_pa11)(CPUHPPAState *env, target_ulong addr, target_ureg reg) { HPPATLBEntry *ent = env->tlb_partial; if (ent) { env->tlb_partial = NULL; if (ent->itree.start <= addr && addr <= ent->itree.last) { - set_access_bits(env, ent, reg); + set_access_bits_pa11(env, ent, reg); return; } } qemu_log_mask(LOG_GUEST_ERROR, "ITLBP not following ITLBA\n"); } +static void itlbt_pa20(CPUHPPAState *env, target_ureg r1, + target_ureg r2, vaddr va_b) +{ + HPPATLBEntry *ent; + vaddr va_e; + uint64_t va_size; + int mask_shift; + + mask_shift = 2 * (r1 & 0xf); + va_size = TARGET_PAGE_SIZE << mask_shift; + va_b &= -va_size; + va_e = va_b + va_size - 1; + + hppa_flush_tlb_range(env, va_b, va_e); + ent = hppa_alloc_tlb_ent(env); + + ent->itree.start = va_b; + ent->itree.last = va_e; + ent->pa = (r1 << 7) & (TARGET_PAGE_MASK << mask_shift); + ent->t = extract64(r2, 61, 1); + ent->d = extract64(r2, 60, 1); + ent->b = extract64(r2, 59, 1); + ent->ar_type = extract64(r2, 56, 3); + ent->ar_pl1 = extract64(r2, 54, 2); + ent->ar_pl2 = extract64(r2, 52, 2); + ent->u = extract64(r2, 51, 1); + /* o = bit 50 */ + /* p = bit 49 */ + ent->access_id = extract64(r2, 1, 31); + ent->entry_valid = 1; + + interval_tree_insert(&ent->itree, &env->tlb_root); + trace_hppa_tlb_itlba(env, ent, ent->itree.start, ent->itree.last, ent->pa); + trace_hppa_tlb_itlbp(env, ent, ent->access_id, ent->u, + ent->ar_pl2, ent->ar_pl1, ent->ar_type, + ent->b, ent->d, ent->t); +} + +void HELPER(idtlbt_pa20)(CPUHPPAState *env, target_ureg r1, target_ureg r2) +{ + vaddr va_b = deposit64(env->cr[CR_IOR], 32, 32, env->cr[CR_ISR]); + itlbt_pa20(env, r1, r2, va_b); +} + +void HELPER(iitlbt_pa20)(CPUHPPAState *env, target_ureg r1, target_ureg r2) +{ + vaddr va_b = deposit64(env->cr[CR_IIAOQ], 32, 32, env->cr[CR_IIASQ]); + itlbt_pa20(env, r1, r2, va_b); +} + /* Purge (Insn/Data) TLB. This is explicitly page-based, and is synchronous across all processors. */ static void ptlb_work(CPUState *cpu, run_on_cpu_data data) @@ -563,7 +614,7 @@ void HELPER(diag_btlb)(CPUHPPAState *env) btlb->itree.start = virt_page << TARGET_PAGE_BITS; btlb->itree.last = btlb->itree.start + len * TARGET_PAGE_SIZE - 1; btlb->pa = phys_page << TARGET_PAGE_BITS; - set_access_bits(env, btlb, env->gr[20]); + set_access_bits_pa11(env, btlb, env->gr[20]); btlb->t = 0; btlb->d = 1; } else { diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 485251bded..29d3bbb3d7 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -2514,6 +2514,9 @@ static bool trans_probe(DisasContext *ctx, arg_probe *a) static bool trans_ixtlbx(DisasContext *ctx, arg_ixtlbx *a) { + if (ctx->is_pa20) { + return false; + } CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); #ifndef CONFIG_USER_ONLY TCGv_tl addr; @@ -2524,9 +2527,9 @@ static bool trans_ixtlbx(DisasContext *ctx, arg_ixtlbx *a) form_gva(ctx, &addr, &ofs, a->b, 0, 0, 0, a->sp, 0, false); reg = load_gpr(ctx, a->r); if (a->addr) { - gen_helper_itlba(tcg_env, addr, reg); + gen_helper_itlba_pa11(tcg_env, addr, reg); } else { - gen_helper_itlbp(tcg_env, addr, reg); + gen_helper_itlbp_pa11(tcg_env, addr, reg); } /* Exit TB for TLB change if mmu is enabled. */ @@ -2572,6 +2575,9 @@ static bool trans_pxtlbx(DisasContext *ctx, arg_pxtlbx *a) */ static bool trans_ixtlbxf(DisasContext *ctx, arg_ixtlbxf *a) { + if (ctx->is_pa20) { + return false; + } CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); #ifndef CONFIG_USER_ONLY TCGv_tl addr, atl, stl; @@ -2583,8 +2589,6 @@ static bool trans_ixtlbxf(DisasContext *ctx, arg_ixtlbxf *a) * FIXME: * if (not (pcxl or pcxl2)) * return gen_illegal(ctx); - * - * Note for future: these are 32-bit systems; no hppa64. */ atl = tcg_temp_new_tl(); @@ -2602,9 +2606,9 @@ static bool trans_ixtlbxf(DisasContext *ctx, arg_ixtlbxf *a) reg = load_gpr(ctx, a->r); if (a->addr) { - gen_helper_itlba(tcg_env, addr, reg); + gen_helper_itlba_pa11(tcg_env, addr, reg); } else { - gen_helper_itlbp(tcg_env, addr, reg); + gen_helper_itlbp_pa11(tcg_env, addr, reg); } /* Exit TB for TLB change if mmu is enabled. */ @@ -2615,6 +2619,32 @@ static bool trans_ixtlbxf(DisasContext *ctx, arg_ixtlbxf *a) #endif } +static bool trans_ixtlbt(DisasContext *ctx, arg_ixtlbt *a) +{ + if (!ctx->is_pa20) { + return false; + } + CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); +#ifndef CONFIG_USER_ONLY + nullify_over(ctx); + { + TCGv_i64 src1 = load_gpr(ctx, a->r1); + TCGv_i64 src2 = load_gpr(ctx, a->r2); + + if (a->data) { + gen_helper_idtlbt_pa20(tcg_env, src1, src2); + } else { + gen_helper_iitlbt_pa20(tcg_env, src1, src2); + } + } + /* Exit TB for TLB change if mmu is enabled. */ + if (ctx->tb_flags & PSW_C) { + ctx->base.is_jmp = DISAS_IAQ_N_STALE; + } + return nullify_end(ctx); +#endif +} + static bool trans_lpa(DisasContext *ctx, arg_ldst *a) { CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); From a4529fa83b3c1e8cd3303161a5895a5adc01a076 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 20 Oct 2023 10:04:22 -0700 Subject: [PATCH 625/974] hw/hppa: Use uint32_t instead of target_ureg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The size of target_ureg is going to change. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- hw/hppa/machine.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index 85682e6bab..1f09b4b490 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -391,9 +391,9 @@ static void machine_HP_common_init_tail(MachineState *machine, PCIBus *pci_bus) true, EM_PARISC, 0, 0); /* Unfortunately, load_elf sign-extends reading elf32. */ - firmware_entry = (target_ureg)firmware_entry; - firmware_low = (target_ureg)firmware_low; - firmware_high = (target_ureg)firmware_high; + firmware_entry = (uint32_t)firmware_entry; + firmware_low = (uint32_t)firmware_low; + firmware_high = (uint32_t)firmware_high; if (size < 0) { error_report("could not load firmware '%s'", firmware_filename); @@ -420,9 +420,9 @@ static void machine_HP_common_init_tail(MachineState *machine, PCIBus *pci_bus) true, EM_PARISC, 0, 0); /* Unfortunately, load_elf sign-extends reading elf32. */ - kernel_entry = (target_ureg) cpu_hppa_to_phys(NULL, kernel_entry); - kernel_low = (target_ureg)kernel_low; - kernel_high = (target_ureg)kernel_high; + kernel_entry = (uint32_t) cpu_hppa_to_phys(NULL, kernel_entry); + kernel_low = (uint32_t)kernel_low; + kernel_high = (uint32_t)kernel_high; if (size < 0) { error_report("could not load kernel '%s'", kernel_filename); From c53e401ed9ffe4a5f5fe914828c0bfe9bf813cff Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 17 Oct 2023 21:11:19 -0700 Subject: [PATCH 626/974] target/hppa: Remove TARGET_REGISTER_BITS Rely only on TARGET_LONG_BITS, fixed at 64, and hppa_is_pa20. Signed-off-by: Richard Henderson --- target/hppa/cpu-param.h | 1 - target/hppa/cpu.c | 2 +- target/hppa/cpu.h | 50 ++++------- target/hppa/helper.c | 32 +++---- target/hppa/helper.h | 51 +++++------ target/hppa/int_helper.c | 17 ++-- target/hppa/machine.c | 45 +++------- target/hppa/mem_helper.c | 16 ++-- target/hppa/op_helper.c | 30 +++---- target/hppa/sys_helper.c | 4 +- target/hppa/translate.c | 186 ++++++++------------------------------- 11 files changed, 135 insertions(+), 299 deletions(-) diff --git a/target/hppa/cpu-param.h b/target/hppa/cpu-param.h index 2fb8e7924b..6746869a3b 100644 --- a/target/hppa/cpu-param.h +++ b/target/hppa/cpu-param.h @@ -9,7 +9,6 @@ #define HPPA_CPU_PARAM_H #define TARGET_LONG_BITS 64 -#define TARGET_REGISTER_BITS 64 #if defined(CONFIG_USER_ONLY) && defined(TARGET_ABI32) # define TARGET_PHYS_ADDR_SPACE_BITS 32 diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 9582619be2..e1597ba8a5 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -77,7 +77,7 @@ static void hppa_restore_state_to_opc(CPUState *cs, HPPACPU *cpu = HPPA_CPU(cs); cpu->env.iaoq_f = data[0]; - if (data[1] != (target_ureg)-1) { + if (data[1] != (target_ulong)-1) { cpu->env.iaoq_b = data[1]; } /* diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 77ddb20ac2..ea676ba062 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -154,25 +154,13 @@ #define CR_IPSW 22 #define CR_EIRR 23 -#if TARGET_REGISTER_BITS == 32 -typedef uint32_t target_ureg; -typedef int32_t target_sreg; -#define TREG_FMT_lx "%08"PRIx32 -#define TREG_FMT_ld "%"PRId32 -#else -typedef uint64_t target_ureg; -typedef int64_t target_sreg; -#define TREG_FMT_lx "%016"PRIx64 -#define TREG_FMT_ld "%"PRId64 -#endif - typedef struct HPPATLBEntry { union { IntervalTreeNode itree; struct HPPATLBEntry *unused_next; }; - target_ureg pa; + target_ulong pa; unsigned entry_valid : 1; @@ -187,16 +175,16 @@ typedef struct HPPATLBEntry { } HPPATLBEntry; typedef struct CPUArchState { - target_ureg iaoq_f; /* front */ - target_ureg iaoq_b; /* back, aka next instruction */ + target_ulong iaoq_f; /* front */ + target_ulong iaoq_b; /* back, aka next instruction */ - target_ureg gr[32]; + target_ulong gr[32]; uint64_t fr[32]; uint64_t sr[8]; /* stored shifted into place for gva */ - target_ureg psw; /* All psw bits except the following: */ - target_ureg psw_n; /* boolean */ - target_sreg psw_v; /* in most significant bit */ + target_ulong psw; /* All psw bits except the following: */ + target_ulong psw_n; /* boolean */ + target_long psw_v; /* in most significant bit */ /* Splitting the carry-borrow field into the MSB and "the rest", allows * for "the rest" to be deleted when it is unused, but the MSB is in use. @@ -205,8 +193,8 @@ typedef struct CPUArchState { * host has the appropriate add-with-carry insn to compute the msb). * Therefore the carry bits are stored as: cb_msb : cb & 0x11111110. */ - target_ureg psw_cb; /* in least significant bit of next nibble */ - target_ureg psw_cb_msb; /* boolean */ + target_ulong psw_cb; /* in least significant bit of next nibble */ + target_ulong psw_cb_msb; /* boolean */ uint64_t iasq_f; uint64_t iasq_b; @@ -214,9 +202,9 @@ typedef struct CPUArchState { uint32_t fr0_shadow; /* flags, c, ca/cq, rm, d, enables */ float_status fp_status; - target_ureg cr[32]; /* control registers */ - target_ureg cr_back[2]; /* back of cr17/cr18 */ - target_ureg shadow[7]; /* shadow registers */ + target_ulong cr[32]; /* control registers */ + target_ulong cr_back[2]; /* back of cr17/cr18 */ + target_ulong shadow[7]; /* shadow registers */ /* * ??? The number of entries isn't specified by the architecture. @@ -287,8 +275,8 @@ void hppa_translate_init(void); #define CPU_RESOLVING_TYPE TYPE_HPPA_CPU -static inline target_ulong hppa_form_gva_psw(target_ureg psw, uint64_t spc, - target_ureg off) +static inline target_ulong hppa_form_gva_psw(target_ulong psw, uint64_t spc, + target_ulong off) { #ifdef CONFIG_USER_ONLY return off; @@ -299,7 +287,7 @@ static inline target_ulong hppa_form_gva_psw(target_ureg psw, uint64_t spc, } static inline target_ulong hppa_form_gva(CPUHPPAState *env, uint64_t spc, - target_ureg off) + target_ulong off) { return hppa_form_gva_psw(env->psw, spc, off); } @@ -343,8 +331,8 @@ static inline void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, which is the primary case we care about -- using goto_tb within a page. Failure is indicated by a zero difference. */ if (env->iasq_f == env->iasq_b) { - target_sreg diff = env->iaoq_b - env->iaoq_f; - if (TARGET_REGISTER_BITS == 32 || diff == (int32_t)diff) { + target_long diff = env->iaoq_b - env->iaoq_f; + if (diff == (int32_t)diff) { *cs_base |= (uint32_t)diff; } } @@ -358,8 +346,8 @@ static inline void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, *pflags = flags; } -target_ureg cpu_hppa_get_psw(CPUHPPAState *env); -void cpu_hppa_put_psw(CPUHPPAState *env, target_ureg); +target_ulong cpu_hppa_get_psw(CPUHPPAState *env); +void cpu_hppa_put_psw(CPUHPPAState *env, target_ulong); void cpu_hppa_loaded_fr0(CPUHPPAState *env); #ifdef CONFIG_USER_ONLY diff --git a/target/hppa/helper.c b/target/hppa/helper.c index c973b65bea..859644c47a 100644 --- a/target/hppa/helper.c +++ b/target/hppa/helper.c @@ -25,31 +25,25 @@ #include "exec/helper-proto.h" #include "qemu/qemu-print.h" -target_ureg cpu_hppa_get_psw(CPUHPPAState *env) +target_ulong cpu_hppa_get_psw(CPUHPPAState *env) { - target_ureg psw; - target_ureg mask1 = (target_ureg)-1 / 0xf; - target_ureg maskf = (target_ureg)-1 / 0xffff * 0xf; + target_ulong psw; + target_ulong mask1 = (target_ulong)-1 / 0xf; + target_ulong maskf = (target_ulong)-1 / 0xffff * 0xf; /* Fold carry bits down to 8 consecutive bits. */ /* ^^^b^^^c^^^d^^^e^^^f^^^g^^^h^^^i^^^j^^^k^^^l^^^m^^^n^^^o^^^p^^^^ */ - /* ^^^b^^^c^^^d^^^e^^^f^^^g^^^h^^^^ */ psw = (env->psw_cb >> 4) & mask1; /* .......b...c...d...e...f...g...h...i...j...k...l...m...n...o...p */ - /* .......b...c...d...e...f...g...h */ psw |= psw >> 3; /* .......b..bc..cd..de..ef..fg..gh..hi..ij..jk..kl..lm..mn..no..op */ - /* .......b..bc..cd..de..ef..fg..gh */ psw |= psw >> 6; psw &= maskf; /* .............bcd............efgh............ijkl............mnop */ - /* .............bcd............efgh */ psw |= psw >> 12; /* .............bcd.........bcdefgh........efghijkl........ijklmnop */ - /* .............bcd.........bcdefgh */ - psw |= env->psw_cb_msb << (TARGET_REGISTER_BITS == 64 ? 39 : 7); + psw |= env->psw_cb_msb << 39; /* .............bcd........abcdefgh........efghijkl........ijklmnop */ - /* .............bcd........abcdefgh */ /* For hppa64, the two 8-bit fields are discontiguous. */ if (hppa_is_pa20(env)) { @@ -65,10 +59,10 @@ target_ureg cpu_hppa_get_psw(CPUHPPAState *env) return psw; } -void cpu_hppa_put_psw(CPUHPPAState *env, target_ureg psw) +void cpu_hppa_put_psw(CPUHPPAState *env, target_ulong psw) { uint64_t reserved; - target_ureg cb = 0; + target_ulong cb = 0; /* Do not allow reserved bits to be set. */ if (hppa_is_pa20(env)) { @@ -86,9 +80,6 @@ void cpu_hppa_put_psw(CPUHPPAState *env, target_ureg psw) env->psw_n = (psw / PSW_N) & 1; env->psw_v = -((psw / PSW_V) & 1); -#if TARGET_REGISTER_BITS == 32 - env->psw_cb_msb = (psw >> 15) & 1; -#else env->psw_cb_msb = (psw >> 39) & 1; cb |= ((psw >> 38) & 1) << 60; cb |= ((psw >> 37) & 1) << 56; @@ -98,7 +89,6 @@ void cpu_hppa_put_psw(CPUHPPAState *env, target_ureg psw) cb |= ((psw >> 33) & 1) << 40; cb |= ((psw >> 32) & 1) << 36; cb |= ((psw >> 15) & 1) << 32; -#endif cb |= ((psw >> 14) & 1) << 28; cb |= ((psw >> 13) & 1) << 24; cb |= ((psw >> 12) & 1) << 20; @@ -112,8 +102,8 @@ void cpu_hppa_put_psw(CPUHPPAState *env, target_ureg psw) void hppa_cpu_dump_state(CPUState *cs, FILE *f, int flags) { CPUHPPAState *env = cpu_env(cs); - target_ureg psw = cpu_hppa_get_psw(env); - target_ureg psw_cb; + target_ulong psw = cpu_hppa_get_psw(env); + target_ulong psw_cb; char psw_c[20]; int i, w; uint64_t m; @@ -151,8 +141,8 @@ void hppa_cpu_dump_state(CPUState *cs, FILE *f, int flags) psw_c[16] = (psw & PSW_D ? 'D' : '-'); psw_c[17] = (psw & PSW_I ? 'I' : '-'); psw_c[18] = '\0'; - psw_cb = ((env->psw_cb >> 4) & ((target_ureg)-1 / 0xf)) - | (env->psw_cb_msb << (TARGET_REGISTER_BITS - 4)); + psw_cb = ((env->psw_cb >> 4) & 0x1111111111111111ull) + | (env->psw_cb_msb << 60); qemu_fprintf(f, "PSW %0*" PRIx64 " CB %0*" PRIx64 " %s\n", w, m & psw, w, m & psw_cb, psw_c); diff --git a/target/hppa/helper.h b/target/hppa/helper.h index 0b346e24f3..57ea5447b6 100644 --- a/target/hppa/helper.h +++ b/target/hppa/helper.h @@ -1,29 +1,20 @@ -#if TARGET_REGISTER_BITS == 64 -# define dh_alias_tr i64 -# define dh_typecode_tr dh_typecode_i64 -#else -# define dh_alias_tr i32 -# define dh_typecode_tr dh_typecode_i32 -#endif -#define dh_ctype_tr target_ureg - DEF_HELPER_2(excp, noreturn, env, int) -DEF_HELPER_FLAGS_2(tsv, TCG_CALL_NO_WG, void, env, tr) -DEF_HELPER_FLAGS_2(tcond, TCG_CALL_NO_WG, void, env, tr) +DEF_HELPER_FLAGS_2(tsv, TCG_CALL_NO_WG, void, env, tl) +DEF_HELPER_FLAGS_2(tcond, TCG_CALL_NO_WG, void, env, tl) -DEF_HELPER_FLAGS_3(stby_b, TCG_CALL_NO_WG, void, env, tl, tr) -DEF_HELPER_FLAGS_3(stby_b_parallel, TCG_CALL_NO_WG, void, env, tl, tr) -DEF_HELPER_FLAGS_3(stby_e, TCG_CALL_NO_WG, void, env, tl, tr) -DEF_HELPER_FLAGS_3(stby_e_parallel, TCG_CALL_NO_WG, void, env, tl, tr) +DEF_HELPER_FLAGS_3(stby_b, TCG_CALL_NO_WG, void, env, tl, tl) +DEF_HELPER_FLAGS_3(stby_b_parallel, TCG_CALL_NO_WG, void, env, tl, tl) +DEF_HELPER_FLAGS_3(stby_e, TCG_CALL_NO_WG, void, env, tl, tl) +DEF_HELPER_FLAGS_3(stby_e_parallel, TCG_CALL_NO_WG, void, env, tl, tl) -DEF_HELPER_FLAGS_3(stdby_b, TCG_CALL_NO_WG, void, env, tl, tr) -DEF_HELPER_FLAGS_3(stdby_b_parallel, TCG_CALL_NO_WG, void, env, tl, tr) -DEF_HELPER_FLAGS_3(stdby_e, TCG_CALL_NO_WG, void, env, tl, tr) -DEF_HELPER_FLAGS_3(stdby_e_parallel, TCG_CALL_NO_WG, void, env, tl, tr) +DEF_HELPER_FLAGS_3(stdby_b, TCG_CALL_NO_WG, void, env, tl, tl) +DEF_HELPER_FLAGS_3(stdby_b_parallel, TCG_CALL_NO_WG, void, env, tl, tl) +DEF_HELPER_FLAGS_3(stdby_e, TCG_CALL_NO_WG, void, env, tl, tl) +DEF_HELPER_FLAGS_3(stdby_e_parallel, TCG_CALL_NO_WG, void, env, tl, tl) DEF_HELPER_FLAGS_1(ldc_check, TCG_CALL_NO_RWG, void, tl) -DEF_HELPER_FLAGS_4(probe, TCG_CALL_NO_WG, tr, env, tl, i32, i32) +DEF_HELPER_FLAGS_4(probe, TCG_CALL_NO_WG, tl, env, tl, i32, i32) DEF_HELPER_FLAGS_1(loaded_fr0, TCG_CALL_NO_RWG, void, env) @@ -82,7 +73,7 @@ DEF_HELPER_FLAGS_4(fmpynfadd_s, TCG_CALL_NO_RWG, i32, env, i32, i32, i32) DEF_HELPER_FLAGS_4(fmpyfadd_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64) DEF_HELPER_FLAGS_4(fmpynfadd_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64) -DEF_HELPER_FLAGS_0(read_interval_timer, TCG_CALL_NO_RWG, tr) +DEF_HELPER_FLAGS_0(read_interval_timer, TCG_CALL_NO_RWG, tl) #ifndef CONFIG_USER_ONLY DEF_HELPER_1(halt, noreturn, env) @@ -90,17 +81,17 @@ DEF_HELPER_1(reset, noreturn, env) DEF_HELPER_1(getshadowregs, void, env) DEF_HELPER_1(rfi, void, env) DEF_HELPER_1(rfi_r, void, env) -DEF_HELPER_FLAGS_2(write_interval_timer, TCG_CALL_NO_RWG, void, env, tr) -DEF_HELPER_FLAGS_2(write_eirr, TCG_CALL_NO_RWG, void, env, tr) -DEF_HELPER_FLAGS_2(write_eiem, TCG_CALL_NO_RWG, void, env, tr) -DEF_HELPER_FLAGS_2(swap_system_mask, TCG_CALL_NO_RWG, tr, env, tr) -DEF_HELPER_FLAGS_3(itlba_pa11, TCG_CALL_NO_RWG, void, env, tl, tr) -DEF_HELPER_FLAGS_3(itlbp_pa11, TCG_CALL_NO_RWG, void, env, tl, tr) -DEF_HELPER_FLAGS_3(idtlbt_pa20, TCG_CALL_NO_RWG, void, env, tr, tr) -DEF_HELPER_FLAGS_3(iitlbt_pa20, TCG_CALL_NO_RWG, void, env, tr, tr) +DEF_HELPER_FLAGS_2(write_interval_timer, TCG_CALL_NO_RWG, void, env, tl) +DEF_HELPER_FLAGS_2(write_eirr, TCG_CALL_NO_RWG, void, env, tl) +DEF_HELPER_FLAGS_2(write_eiem, TCG_CALL_NO_RWG, void, env, tl) +DEF_HELPER_FLAGS_2(swap_system_mask, TCG_CALL_NO_RWG, tl, env, tl) +DEF_HELPER_FLAGS_3(itlba_pa11, TCG_CALL_NO_RWG, void, env, tl, tl) +DEF_HELPER_FLAGS_3(itlbp_pa11, TCG_CALL_NO_RWG, void, env, tl, tl) +DEF_HELPER_FLAGS_3(idtlbt_pa20, TCG_CALL_NO_RWG, void, env, tl, tl) +DEF_HELPER_FLAGS_3(iitlbt_pa20, TCG_CALL_NO_RWG, void, env, tl, tl) DEF_HELPER_FLAGS_2(ptlb, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_1(ptlbe, TCG_CALL_NO_RWG, void, env) -DEF_HELPER_FLAGS_2(lpa, TCG_CALL_NO_WG, tr, env, tl) +DEF_HELPER_FLAGS_2(lpa, TCG_CALL_NO_WG, tl, env, tl) DEF_HELPER_FLAGS_1(change_prot_id, TCG_CALL_NO_RWG, void, env) DEF_HELPER_1(diag_btlb, void, env) #endif diff --git a/target/hppa/int_helper.c b/target/hppa/int_helper.c index 3ab9934a1d..f355c4c76b 100644 --- a/target/hppa/int_helper.c +++ b/target/hppa/int_helper.c @@ -52,9 +52,9 @@ static void io_eir_write(void *opaque, hwaddr addr, uint64_t data, unsigned size) { HPPACPU *cpu = opaque; - int le_bit = ~data & (TARGET_REGISTER_BITS - 1); + int le_bit = ~data & 31; - cpu->env.cr[CR_EIRR] |= (target_ureg)1 << le_bit; + cpu->env.cr[CR_EIRR] |= (target_ulong)1 << le_bit; eval_interrupt(cpu); } @@ -73,7 +73,7 @@ void hppa_cpu_alarm_timer(void *opaque) io_eir_write(opaque, 0, 0, 4); } -void HELPER(write_eirr)(CPUHPPAState *env, target_ureg val) +void HELPER(write_eirr)(CPUHPPAState *env, target_ulong val) { env->cr[CR_EIRR] &= ~val; qemu_mutex_lock_iothread(); @@ -81,7 +81,7 @@ void HELPER(write_eirr)(CPUHPPAState *env, target_ureg val) qemu_mutex_unlock_iothread(); } -void HELPER(write_eiem)(CPUHPPAState *env, target_ureg val) +void HELPER(write_eiem)(CPUHPPAState *env, target_ulong val) { env->cr[CR_EIEM] = val; qemu_mutex_lock_iothread(); @@ -94,12 +94,11 @@ void hppa_cpu_do_interrupt(CPUState *cs) HPPACPU *cpu = HPPA_CPU(cs); CPUHPPAState *env = &cpu->env; int i = cs->exception_index; - target_ureg iaoq_f = env->iaoq_f; - target_ureg iaoq_b = env->iaoq_b; + target_ulong iaoq_f = env->iaoq_f; + target_ulong iaoq_b = env->iaoq_b; uint64_t iasq_f = env->iasq_f; uint64_t iasq_b = env->iasq_b; - - target_ureg old_psw; + target_ulong old_psw; /* As documented in pa2.0 -- interruption handling. */ /* step 1 */ @@ -240,7 +239,7 @@ void hppa_cpu_do_interrupt(CPUState *cs) name = unknown; } qemu_log("INT %6d: %s @ " TARGET_FMT_lx "," TARGET_FMT_lx - " -> " TREG_FMT_lx " " TARGET_FMT_lx "\n", + " -> " TARGET_FMT_lx " " TARGET_FMT_lx "\n", ++count, name, hppa_form_gva(env, iasq_f, iaoq_f), hppa_form_gva(env, iasq_b, iaoq_b), diff --git a/target/hppa/machine.c b/target/hppa/machine.c index 473305ffea..f6df4deac5 100644 --- a/target/hppa/machine.c +++ b/target/hppa/machine.c @@ -21,33 +21,12 @@ #include "cpu.h" #include "migration/cpu.h" -#if TARGET_REGISTER_BITS == 64 -#define qemu_put_betr qemu_put_be64 -#define qemu_get_betr qemu_get_be64 -#define VMSTATE_UINTTR_V(_f, _s, _v) \ - VMSTATE_UINT64_V(_f, _s, _v) -#define VMSTATE_UINTTR_ARRAY_V(_f, _s, _n, _v) \ - VMSTATE_UINT64_ARRAY_V(_f, _s, _n, _v) -#else -#define qemu_put_betr qemu_put_be32 -#define qemu_get_betr qemu_get_be32 -#define VMSTATE_UINTTR_V(_f, _s, _v) \ - VMSTATE_UINT32_V(_f, _s, _v) -#define VMSTATE_UINTTR_ARRAY_V(_f, _s, _n, _v) \ - VMSTATE_UINT32_ARRAY_V(_f, _s, _n, _v) -#endif - -#define VMSTATE_UINTTR(_f, _s) \ - VMSTATE_UINTTR_V(_f, _s, 0) -#define VMSTATE_UINTTR_ARRAY(_f, _s, _n) \ - VMSTATE_UINTTR_ARRAY_V(_f, _s, _n, 0) - static int get_psw(QEMUFile *f, void *opaque, size_t size, const VMStateField *field) { CPUHPPAState *env = opaque; - cpu_hppa_put_psw(env, qemu_get_betr(f)); + cpu_hppa_put_psw(env, qemu_get_be64(f)); return 0; } @@ -55,7 +34,7 @@ static int put_psw(QEMUFile *f, void *opaque, size_t size, const VMStateField *field, JSONWriter *vmdesc) { CPUHPPAState *env = opaque; - qemu_put_betr(f, cpu_hppa_get_psw(env)); + qemu_put_be64(f, cpu_hppa_get_psw(env)); return 0; } @@ -73,7 +52,7 @@ static int get_tlb(QEMUFile *f, void *opaque, size_t size, uint32_t val; ent->itree.start = qemu_get_be64(f); - ent->pa = qemu_get_betr(f); + ent->pa = qemu_get_be64(f); val = qemu_get_be32(f); ent->entry_valid = extract32(val, 0, 1); @@ -109,7 +88,7 @@ static int put_tlb(QEMUFile *f, void *opaque, size_t size, } qemu_put_be64(f, ent->itree.start); - qemu_put_betr(f, ent->pa); + qemu_put_be64(f, ent->pa); qemu_put_be32(f, val); return 0; } @@ -169,12 +148,12 @@ static int tlb_post_load(void *opaque, int version_id) } static VMStateField vmstate_env_fields[] = { - VMSTATE_UINTTR_ARRAY(gr, CPUHPPAState, 32), + VMSTATE_UINT64_ARRAY(gr, CPUHPPAState, 32), VMSTATE_UINT64_ARRAY(fr, CPUHPPAState, 32), VMSTATE_UINT64_ARRAY(sr, CPUHPPAState, 8), - VMSTATE_UINTTR_ARRAY(cr, CPUHPPAState, 32), - VMSTATE_UINTTR_ARRAY(cr_back, CPUHPPAState, 2), - VMSTATE_UINTTR_ARRAY(shadow, CPUHPPAState, 7), + VMSTATE_UINT64_ARRAY(cr, CPUHPPAState, 32), + VMSTATE_UINT64_ARRAY(cr_back, CPUHPPAState, 2), + VMSTATE_UINT64_ARRAY(shadow, CPUHPPAState, 7), /* Save the architecture value of the psw, not the internally expanded version. Since this architecture value does not @@ -191,8 +170,8 @@ static VMStateField vmstate_env_fields[] = { .offset = 0 }, - VMSTATE_UINTTR(iaoq_f, CPUHPPAState), - VMSTATE_UINTTR(iaoq_b, CPUHPPAState), + VMSTATE_UINT64(iaoq_f, CPUHPPAState), + VMSTATE_UINT64(iaoq_b, CPUHPPAState), VMSTATE_UINT64(iasq_f, CPUHPPAState), VMSTATE_UINT64(iasq_b, CPUHPPAState), @@ -207,8 +186,8 @@ static VMStateField vmstate_env_fields[] = { static const VMStateDescription vmstate_env = { .name = "env", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = vmstate_env_fields, .pre_load = tlb_pre_load, .post_load = tlb_post_load, diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c index d5d2d62f4a..9be68b860b 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -344,7 +344,7 @@ bool hppa_cpu_tlb_fill(CPUState *cs, vaddr addr, int size, } /* Insert (Insn/Data) TLB Address. Note this is PA 1.1 only. */ -void HELPER(itlba_pa11)(CPUHPPAState *env, target_ulong addr, target_ureg reg) +void HELPER(itlba_pa11)(CPUHPPAState *env, target_ulong addr, target_ulong reg) { HPPATLBEntry *ent; @@ -366,7 +366,7 @@ void HELPER(itlba_pa11)(CPUHPPAState *env, target_ulong addr, target_ureg reg) } static void set_access_bits_pa11(CPUHPPAState *env, HPPATLBEntry *ent, - target_ureg reg) + target_ulong reg) { ent->access_id = extract32(reg, 1, 18); ent->u = extract32(reg, 19, 1); @@ -384,7 +384,7 @@ static void set_access_bits_pa11(CPUHPPAState *env, HPPATLBEntry *ent, } /* Insert (Insn/Data) TLB Protection. Note this is PA 1.1 only. */ -void HELPER(itlbp_pa11)(CPUHPPAState *env, target_ulong addr, target_ureg reg) +void HELPER(itlbp_pa11)(CPUHPPAState *env, target_ulong addr, target_ulong reg) { HPPATLBEntry *ent = env->tlb_partial; @@ -398,8 +398,8 @@ void HELPER(itlbp_pa11)(CPUHPPAState *env, target_ulong addr, target_ureg reg) qemu_log_mask(LOG_GUEST_ERROR, "ITLBP not following ITLBA\n"); } -static void itlbt_pa20(CPUHPPAState *env, target_ureg r1, - target_ureg r2, vaddr va_b) +static void itlbt_pa20(CPUHPPAState *env, target_ulong r1, + target_ulong r2, vaddr va_b) { HPPATLBEntry *ent; vaddr va_e; @@ -436,13 +436,13 @@ static void itlbt_pa20(CPUHPPAState *env, target_ureg r1, ent->b, ent->d, ent->t); } -void HELPER(idtlbt_pa20)(CPUHPPAState *env, target_ureg r1, target_ureg r2) +void HELPER(idtlbt_pa20)(CPUHPPAState *env, target_ulong r1, target_ulong r2) { vaddr va_b = deposit64(env->cr[CR_IOR], 32, 32, env->cr[CR_ISR]); itlbt_pa20(env, r1, r2, va_b); } -void HELPER(iitlbt_pa20)(CPUHPPAState *env, target_ureg r1, target_ureg r2) +void HELPER(iitlbt_pa20)(CPUHPPAState *env, target_ulong r1, target_ulong r2) { vaddr va_b = deposit64(env->cr[CR_IIAOQ], 32, 32, env->cr[CR_IIASQ]); itlbt_pa20(env, r1, r2, va_b); @@ -521,7 +521,7 @@ void HELPER(change_prot_id)(CPUHPPAState *env) cpu_hppa_change_prot_id(env); } -target_ureg HELPER(lpa)(CPUHPPAState *env, target_ulong addr) +target_ulong HELPER(lpa)(CPUHPPAState *env, target_ulong addr) { hwaddr phys; int prot, excp; diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c index b5b45f5120..a25e6df7e4 100644 --- a/target/hppa/op_helper.c +++ b/target/hppa/op_helper.c @@ -42,14 +42,14 @@ G_NORETURN void hppa_dynamic_excp(CPUHPPAState *env, int excp, uintptr_t ra) cpu_loop_exit_restore(cs, ra); } -void HELPER(tsv)(CPUHPPAState *env, target_ureg cond) +void HELPER(tsv)(CPUHPPAState *env, target_ulong cond) { - if (unlikely((target_sreg)cond < 0)) { + if (unlikely((target_long)cond < 0)) { hppa_dynamic_excp(env, EXCP_OVERFLOW, GETPC()); } } -void HELPER(tcond)(CPUHPPAState *env, target_ureg cond) +void HELPER(tcond)(CPUHPPAState *env, target_ulong cond) { if (unlikely(cond)) { hppa_dynamic_excp(env, EXCP_COND, GETPC()); @@ -110,7 +110,7 @@ static void atomic_store_mask64(CPUHPPAState *env, target_ulong addr, #endif } -static void do_stby_b(CPUHPPAState *env, target_ulong addr, target_ureg val, +static void do_stby_b(CPUHPPAState *env, target_ulong addr, target_ulong val, bool parallel, uintptr_t ra) { switch (addr & 3) { @@ -191,29 +191,29 @@ static void do_stdby_b(CPUHPPAState *env, target_ulong addr, uint64_t val, } } -void HELPER(stby_b)(CPUHPPAState *env, target_ulong addr, target_ureg val) +void HELPER(stby_b)(CPUHPPAState *env, target_ulong addr, target_ulong val) { do_stby_b(env, addr, val, false, GETPC()); } void HELPER(stby_b_parallel)(CPUHPPAState *env, target_ulong addr, - target_ureg val) + target_ulong val) { do_stby_b(env, addr, val, true, GETPC()); } -void HELPER(stdby_b)(CPUHPPAState *env, target_ulong addr, target_ureg val) +void HELPER(stdby_b)(CPUHPPAState *env, target_ulong addr, target_ulong val) { do_stdby_b(env, addr, val, false, GETPC()); } void HELPER(stdby_b_parallel)(CPUHPPAState *env, target_ulong addr, - target_ureg val) + target_ulong val) { do_stdby_b(env, addr, val, true, GETPC()); } -static void do_stby_e(CPUHPPAState *env, target_ulong addr, target_ureg val, +static void do_stby_e(CPUHPPAState *env, target_ulong addr, target_ulong val, bool parallel, uintptr_t ra) { switch (addr & 3) { @@ -301,24 +301,24 @@ static void do_stdby_e(CPUHPPAState *env, target_ulong addr, uint64_t val, } } -void HELPER(stby_e)(CPUHPPAState *env, target_ulong addr, target_ureg val) +void HELPER(stby_e)(CPUHPPAState *env, target_ulong addr, target_ulong val) { do_stby_e(env, addr, val, false, GETPC()); } void HELPER(stby_e_parallel)(CPUHPPAState *env, target_ulong addr, - target_ureg val) + target_ulong val) { do_stby_e(env, addr, val, true, GETPC()); } -void HELPER(stdby_e)(CPUHPPAState *env, target_ulong addr, target_ureg val) +void HELPER(stdby_e)(CPUHPPAState *env, target_ulong addr, target_ulong val) { do_stdby_e(env, addr, val, false, GETPC()); } void HELPER(stdby_e_parallel)(CPUHPPAState *env, target_ulong addr, - target_ureg val) + target_ulong val) { do_stdby_e(env, addr, val, true, GETPC()); } @@ -332,7 +332,7 @@ void HELPER(ldc_check)(target_ulong addr) } } -target_ureg HELPER(probe)(CPUHPPAState *env, target_ulong addr, +target_ulong HELPER(probe)(CPUHPPAState *env, target_ulong addr, uint32_t level, uint32_t want) { #ifdef CONFIG_USER_ONLY @@ -364,7 +364,7 @@ target_ureg HELPER(probe)(CPUHPPAState *env, target_ulong addr, #endif } -target_ureg HELPER(read_interval_timer)(void) +target_ulong HELPER(read_interval_timer)(void) { #ifdef CONFIG_USER_ONLY /* In user-mode, QEMU_CLOCK_VIRTUAL doesn't exist. diff --git a/target/hppa/sys_helper.c b/target/hppa/sys_helper.c index 4bb4cf611c..8850576ac3 100644 --- a/target/hppa/sys_helper.c +++ b/target/hppa/sys_helper.c @@ -24,7 +24,7 @@ #include "qemu/timer.h" #include "sysemu/runstate.h" -void HELPER(write_interval_timer)(CPUHPPAState *env, target_ureg val) +void HELPER(write_interval_timer)(CPUHPPAState *env, target_ulong val) { HPPACPU *cpu = env_archcpu(env); uint64_t current = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); @@ -58,7 +58,7 @@ void HELPER(reset)(CPUHPPAState *env) helper_excp(env, EXCP_HLT); } -target_ureg HELPER(swap_system_mask)(CPUHPPAState *env, target_ureg nsm) +target_ulong HELPER(swap_system_mask)(CPUHPPAState *env, target_ulong nsm) { target_ulong psw = env->psw; /* diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 29d3bbb3d7..0172c2f898 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -40,21 +40,10 @@ #undef tcg_temp_new #undef tcg_global_mem_new -#if TARGET_LONG_BITS == 64 #define TCGv_tl TCGv_i64 #define tcg_temp_new_tl tcg_temp_new_i64 -#if TARGET_REGISTER_BITS == 64 #define tcg_gen_extu_reg_tl tcg_gen_mov_i64 -#else -#define tcg_gen_extu_reg_tl tcg_gen_extu_i32_i64 -#endif -#else -#define TCGv_tl TCGv_i32 -#define tcg_temp_new_tl tcg_temp_new_i32 -#define tcg_gen_extu_reg_tl tcg_gen_mov_i32 -#endif -#if TARGET_REGISTER_BITS == 64 #define TCGv_reg TCGv_i64 #define tcg_temp_new tcg_temp_new_i64 @@ -147,98 +136,6 @@ #define tcg_gen_qemu_st_reg tcg_gen_qemu_st_i64 #define tcg_gen_atomic_xchg_reg tcg_gen_atomic_xchg_i64 #define tcg_gen_trunc_reg_ptr tcg_gen_trunc_i64_ptr -#else -#define TCGv_reg TCGv_i32 -#define tcg_temp_new tcg_temp_new_i32 -#define tcg_global_mem_new tcg_global_mem_new_i32 - -#define tcg_gen_movi_reg tcg_gen_movi_i32 -#define tcg_gen_mov_reg tcg_gen_mov_i32 -#define tcg_gen_ld8u_reg tcg_gen_ld8u_i32 -#define tcg_gen_ld8s_reg tcg_gen_ld8s_i32 -#define tcg_gen_ld16u_reg tcg_gen_ld16u_i32 -#define tcg_gen_ld16s_reg tcg_gen_ld16s_i32 -#define tcg_gen_ld32u_reg tcg_gen_ld_i32 -#define tcg_gen_ld32s_reg tcg_gen_ld_i32 -#define tcg_gen_ld_reg tcg_gen_ld_i32 -#define tcg_gen_st8_reg tcg_gen_st8_i32 -#define tcg_gen_st16_reg tcg_gen_st16_i32 -#define tcg_gen_st32_reg tcg_gen_st32_i32 -#define tcg_gen_st_reg tcg_gen_st_i32 -#define tcg_gen_add_reg tcg_gen_add_i32 -#define tcg_gen_addi_reg tcg_gen_addi_i32 -#define tcg_gen_sub_reg tcg_gen_sub_i32 -#define tcg_gen_neg_reg tcg_gen_neg_i32 -#define tcg_gen_subfi_reg tcg_gen_subfi_i32 -#define tcg_gen_subi_reg tcg_gen_subi_i32 -#define tcg_gen_and_reg tcg_gen_and_i32 -#define tcg_gen_andi_reg tcg_gen_andi_i32 -#define tcg_gen_or_reg tcg_gen_or_i32 -#define tcg_gen_ori_reg tcg_gen_ori_i32 -#define tcg_gen_xor_reg tcg_gen_xor_i32 -#define tcg_gen_xori_reg tcg_gen_xori_i32 -#define tcg_gen_not_reg tcg_gen_not_i32 -#define tcg_gen_shl_reg tcg_gen_shl_i32 -#define tcg_gen_shli_reg tcg_gen_shli_i32 -#define tcg_gen_shr_reg tcg_gen_shr_i32 -#define tcg_gen_shri_reg tcg_gen_shri_i32 -#define tcg_gen_sar_reg tcg_gen_sar_i32 -#define tcg_gen_sari_reg tcg_gen_sari_i32 -#define tcg_gen_brcond_reg tcg_gen_brcond_i32 -#define tcg_gen_brcondi_reg tcg_gen_brcondi_i32 -#define tcg_gen_setcond_reg tcg_gen_setcond_i32 -#define tcg_gen_setcondi_reg tcg_gen_setcondi_i32 -#define tcg_gen_mul_reg tcg_gen_mul_i32 -#define tcg_gen_muli_reg tcg_gen_muli_i32 -#define tcg_gen_div_reg tcg_gen_div_i32 -#define tcg_gen_rem_reg tcg_gen_rem_i32 -#define tcg_gen_divu_reg tcg_gen_divu_i32 -#define tcg_gen_remu_reg tcg_gen_remu_i32 -#define tcg_gen_discard_reg tcg_gen_discard_i32 -#define tcg_gen_trunc_reg_i32 tcg_gen_mov_i32 -#define tcg_gen_trunc_i64_reg tcg_gen_extrl_i64_i32 -#define tcg_gen_extu_i32_reg tcg_gen_mov_i32 -#define tcg_gen_ext_i32_reg tcg_gen_mov_i32 -#define tcg_gen_extu_reg_i64 tcg_gen_extu_i32_i64 -#define tcg_gen_ext_reg_i64 tcg_gen_ext_i32_i64 -#define tcg_gen_ext8u_reg tcg_gen_ext8u_i32 -#define tcg_gen_ext8s_reg tcg_gen_ext8s_i32 -#define tcg_gen_ext16u_reg tcg_gen_ext16u_i32 -#define tcg_gen_ext16s_reg tcg_gen_ext16s_i32 -#define tcg_gen_ext32u_reg tcg_gen_mov_i32 -#define tcg_gen_ext32s_reg tcg_gen_mov_i32 -#define tcg_gen_bswap16_reg tcg_gen_bswap16_i32 -#define tcg_gen_bswap32_reg tcg_gen_bswap32_i32 -#define tcg_gen_concat_reg_i64 tcg_gen_concat_i32_i64 -#define tcg_gen_andc_reg tcg_gen_andc_i32 -#define tcg_gen_eqv_reg tcg_gen_eqv_i32 -#define tcg_gen_nand_reg tcg_gen_nand_i32 -#define tcg_gen_nor_reg tcg_gen_nor_i32 -#define tcg_gen_orc_reg tcg_gen_orc_i32 -#define tcg_gen_clz_reg tcg_gen_clz_i32 -#define tcg_gen_ctz_reg tcg_gen_ctz_i32 -#define tcg_gen_clzi_reg tcg_gen_clzi_i32 -#define tcg_gen_ctzi_reg tcg_gen_ctzi_i32 -#define tcg_gen_clrsb_reg tcg_gen_clrsb_i32 -#define tcg_gen_ctpop_reg tcg_gen_ctpop_i32 -#define tcg_gen_rotl_reg tcg_gen_rotl_i32 -#define tcg_gen_rotli_reg tcg_gen_rotli_i32 -#define tcg_gen_rotr_reg tcg_gen_rotr_i32 -#define tcg_gen_rotri_reg tcg_gen_rotri_i32 -#define tcg_gen_deposit_reg tcg_gen_deposit_i32 -#define tcg_gen_deposit_z_reg tcg_gen_deposit_z_i32 -#define tcg_gen_extract_reg tcg_gen_extract_i32 -#define tcg_gen_sextract_reg tcg_gen_sextract_i32 -#define tcg_gen_extract2_reg tcg_gen_extract2_i32 -#define tcg_constant_reg tcg_constant_i32 -#define tcg_gen_movcond_reg tcg_gen_movcond_i32 -#define tcg_gen_add2_reg tcg_gen_add2_i32 -#define tcg_gen_sub2_reg tcg_gen_sub2_i32 -#define tcg_gen_qemu_ld_reg tcg_gen_qemu_ld_i32 -#define tcg_gen_qemu_st_reg tcg_gen_qemu_st_i32 -#define tcg_gen_atomic_xchg_reg tcg_gen_atomic_xchg_i32 -#define tcg_gen_trunc_reg_ptr tcg_gen_ext_i32_ptr -#endif /* TARGET_REGISTER_BITS */ typedef struct DisasCond { TCGCond c; @@ -249,9 +146,9 @@ typedef struct DisasContext { DisasContextBase base; CPUState *cs; - target_ureg iaoq_f; - target_ureg iaoq_b; - target_ureg iaoq_n; + uint64_t iaoq_f; + uint64_t iaoq_b; + uint64_t iaoq_n; TCGv_reg iaoq_n_var; DisasCond null_cond; @@ -727,7 +624,7 @@ static bool nullify_end(DisasContext *ctx) return true; } -static target_ureg gva_offset_mask(DisasContext *ctx) +static uint64_t gva_offset_mask(DisasContext *ctx) { return (ctx->tb_flags & PSW_W ? MAKE_64BIT_MASK(0, 62) @@ -735,9 +632,9 @@ static target_ureg gva_offset_mask(DisasContext *ctx) } static void copy_iaoq_entry(DisasContext *ctx, TCGv_reg dest, - target_ureg ival, TCGv_reg vval) + uint64_t ival, TCGv_reg vval) { - target_ureg mask = gva_offset_mask(ctx); + uint64_t mask = gva_offset_mask(ctx); if (ival != -1) { tcg_gen_movi_reg(dest, ival & mask); @@ -756,7 +653,7 @@ static void copy_iaoq_entry(DisasContext *ctx, TCGv_reg dest, } } -static inline target_ureg iaoq_dest(DisasContext *ctx, target_sreg disp) +static inline uint64_t iaoq_dest(DisasContext *ctx, int64_t disp) { return ctx->iaoq_f + disp + 8; } @@ -801,7 +698,7 @@ static bool gen_illegal(DisasContext *ctx) } while (0) #endif -static bool use_goto_tb(DisasContext *ctx, target_ureg dest) +static bool use_goto_tb(DisasContext *ctx, uint64_t dest) { return translator_use_goto_tb(&ctx->base, dest); } @@ -817,7 +714,7 @@ static bool use_nullify_skip(DisasContext *ctx) } static void gen_goto_tb(DisasContext *ctx, int which, - target_ureg f, target_ureg b) + uint64_t f, uint64_t b) { if (f != -1 && b != -1 && use_goto_tb(ctx, f)) { tcg_gen_goto_tb(which); @@ -844,7 +741,7 @@ static bool cond_need_cb(int c) /* Need extensions from TCGv_i32 to TCGv_reg. */ static bool cond_need_ext(DisasContext *ctx, bool d) { - return TARGET_REGISTER_BITS == 64 && !(ctx->is_pa20 && d); + return !(ctx->is_pa20 && d); } /* @@ -895,7 +792,7 @@ static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d, tcg_gen_and_reg(tmp, tmp, res); tcg_gen_ext32u_reg(tmp, tmp); } else { - tcg_gen_sari_reg(tmp, tmp, TARGET_REGISTER_BITS - 1); + tcg_gen_sari_reg(tmp, tmp, 63); tcg_gen_and_reg(tmp, tmp, res); } cond = cond_make_0_tmp(TCG_COND_EQ, tmp); @@ -1091,7 +988,7 @@ static DisasCond do_unit_cond(unsigned cf, bool d, TCGv_reg res, { DisasCond cond; TCGv_reg tmp, cb = NULL; - target_ureg d_repl = d ? 0x0000000100000001ull : 1; + uint64_t d_repl = d ? 0x0000000100000001ull : 1; if (cf & 8) { /* Since we want to test lots of carry-out bits all at once, do not @@ -1522,7 +1419,7 @@ static TCGv_i64 space_select(DisasContext *ctx, int sp, TCGv_reg base) #endif static void form_gva(DisasContext *ctx, TCGv_tl *pgva, TCGv_reg *pofs, - unsigned rb, unsigned rx, int scale, target_sreg disp, + unsigned rb, unsigned rx, int scale, int64_t disp, unsigned sp, int modify, bool is_phys) { TCGv_reg base = load_gpr(ctx, rb); @@ -1558,7 +1455,7 @@ static void form_gva(DisasContext *ctx, TCGv_tl *pgva, TCGv_reg *pofs, * = 0 for no base register update. */ static void do_load_32(DisasContext *ctx, TCGv_i32 dest, unsigned rb, - unsigned rx, int scale, target_sreg disp, + unsigned rx, int scale, int64_t disp, unsigned sp, int modify, MemOp mop) { TCGv_reg ofs; @@ -1576,7 +1473,7 @@ static void do_load_32(DisasContext *ctx, TCGv_i32 dest, unsigned rb, } static void do_load_64(DisasContext *ctx, TCGv_i64 dest, unsigned rb, - unsigned rx, int scale, target_sreg disp, + unsigned rx, int scale, int64_t disp, unsigned sp, int modify, MemOp mop) { TCGv_reg ofs; @@ -1594,7 +1491,7 @@ static void do_load_64(DisasContext *ctx, TCGv_i64 dest, unsigned rb, } static void do_store_32(DisasContext *ctx, TCGv_i32 src, unsigned rb, - unsigned rx, int scale, target_sreg disp, + unsigned rx, int scale, int64_t disp, unsigned sp, int modify, MemOp mop) { TCGv_reg ofs; @@ -1612,7 +1509,7 @@ static void do_store_32(DisasContext *ctx, TCGv_i32 src, unsigned rb, } static void do_store_64(DisasContext *ctx, TCGv_i64 src, unsigned rb, - unsigned rx, int scale, target_sreg disp, + unsigned rx, int scale, int64_t disp, unsigned sp, int modify, MemOp mop) { TCGv_reg ofs; @@ -1629,16 +1526,11 @@ static void do_store_64(DisasContext *ctx, TCGv_i64 src, unsigned rb, } } -#if TARGET_REGISTER_BITS == 64 #define do_load_reg do_load_64 #define do_store_reg do_store_64 -#else -#define do_load_reg do_load_32 -#define do_store_reg do_store_32 -#endif static bool do_load(DisasContext *ctx, unsigned rt, unsigned rb, - unsigned rx, int scale, target_sreg disp, + unsigned rx, int scale, int64_t disp, unsigned sp, int modify, MemOp mop) { TCGv_reg dest; @@ -1659,7 +1551,7 @@ static bool do_load(DisasContext *ctx, unsigned rt, unsigned rb, } static bool do_floadw(DisasContext *ctx, unsigned rt, unsigned rb, - unsigned rx, int scale, target_sreg disp, + unsigned rx, int scale, int64_t disp, unsigned sp, int modify) { TCGv_i32 tmp; @@ -1684,7 +1576,7 @@ static bool trans_fldw(DisasContext *ctx, arg_ldst *a) } static bool do_floadd(DisasContext *ctx, unsigned rt, unsigned rb, - unsigned rx, int scale, target_sreg disp, + unsigned rx, int scale, int64_t disp, unsigned sp, int modify) { TCGv_i64 tmp; @@ -1709,7 +1601,7 @@ static bool trans_fldd(DisasContext *ctx, arg_ldst *a) } static bool do_store(DisasContext *ctx, unsigned rt, unsigned rb, - target_sreg disp, unsigned sp, + int64_t disp, unsigned sp, int modify, MemOp mop) { nullify_over(ctx); @@ -1718,7 +1610,7 @@ static bool do_store(DisasContext *ctx, unsigned rt, unsigned rb, } static bool do_fstorew(DisasContext *ctx, unsigned rt, unsigned rb, - unsigned rx, int scale, target_sreg disp, + unsigned rx, int scale, int64_t disp, unsigned sp, int modify) { TCGv_i32 tmp; @@ -1738,7 +1630,7 @@ static bool trans_fstw(DisasContext *ctx, arg_ldst *a) } static bool do_fstored(DisasContext *ctx, unsigned rt, unsigned rb, - unsigned rx, int scale, target_sreg disp, + unsigned rx, int scale, int64_t disp, unsigned sp, int modify) { TCGv_i64 tmp; @@ -1851,7 +1743,7 @@ static bool do_fop_dedd(DisasContext *ctx, unsigned rt, /* Emit an unconditional branch to a direct target, which may or may not have already had nullification handled. */ -static bool do_dbranch(DisasContext *ctx, target_ureg dest, +static bool do_dbranch(DisasContext *ctx, uint64_t dest, unsigned link, bool is_n) { if (ctx->null_cond.c == TCG_COND_NEVER && ctx->null_lab == NULL) { @@ -1888,10 +1780,10 @@ static bool do_dbranch(DisasContext *ctx, target_ureg dest, /* Emit a conditional branch to a direct target. If the branch itself is nullified, we should have already used nullify_over. */ -static bool do_cbranch(DisasContext *ctx, target_sreg disp, bool is_n, +static bool do_cbranch(DisasContext *ctx, int64_t disp, bool is_n, DisasCond *cond) { - target_ureg dest = iaoq_dest(ctx, disp); + uint64_t dest = iaoq_dest(ctx, disp); TCGLabel *taken = NULL; TCGCond c = cond->c; bool n; @@ -2867,7 +2759,7 @@ static bool do_dcor(DisasContext *ctx, arg_rr_cf_d *a, bool is_i) if (!is_i) { tcg_gen_not_reg(tmp, tmp); } - tcg_gen_andi_reg(tmp, tmp, (target_ureg)0x1111111111111111ull); + tcg_gen_andi_reg(tmp, tmp, (uint64_t)0x1111111111111111ull); tcg_gen_muli_reg(tmp, tmp, 6); do_unit(ctx, a->t, load_gpr(ctx, a->r), tmp, a->cf, a->d, false, is_i ? tcg_gen_add_reg : tcg_gen_sub_reg); @@ -2989,22 +2881,20 @@ static bool trans_cmpiclr(DisasContext *ctx, arg_rri_cf_d *a) static bool trans_ld(DisasContext *ctx, arg_ldst *a) { - if (unlikely(TARGET_REGISTER_BITS == 32 && a->size > MO_32)) { + if (!ctx->is_pa20 && a->size > MO_32) { return gen_illegal(ctx); - } else { - return do_load(ctx, a->t, a->b, a->x, a->scale ? a->size : 0, - a->disp, a->sp, a->m, a->size | MO_TE); } + return do_load(ctx, a->t, a->b, a->x, a->scale ? a->size : 0, + a->disp, a->sp, a->m, a->size | MO_TE); } static bool trans_st(DisasContext *ctx, arg_ldst *a) { assert(a->x == 0 && a->scale == 0); - if (unlikely(TARGET_REGISTER_BITS == 32 && a->size > MO_32)) { + if (!ctx->is_pa20 && a->size > MO_32) { return gen_illegal(ctx); - } else { - return do_store(ctx, a->t, a->b, a->disp, a->sp, a->m, a->size | MO_TE); } + return do_store(ctx, a->t, a->b, a->disp, a->sp, a->m, a->size | MO_TE); } static bool trans_ldc(DisasContext *ctx, arg_ldst *a) @@ -3013,7 +2903,7 @@ static bool trans_ldc(DisasContext *ctx, arg_ldst *a) TCGv_reg zero, dest, ofs; TCGv_tl addr; - if (unlikely(TARGET_REGISTER_BITS == 32 && a->size > MO_32)) { + if (!ctx->is_pa20 && a->size > MO_32) { return gen_illegal(ctx); } @@ -3431,7 +3321,7 @@ static bool trans_shrp_imm(DisasContext *ctx, arg_shrp_imm *a) t2 = load_gpr(ctx, a->r2); if (a->r1 == 0) { tcg_gen_extract_reg(dest, t2, sa, width - sa); - } else if (width == TARGET_REGISTER_BITS) { + } else if (width == TARGET_LONG_BITS) { tcg_gen_extract2_reg(dest, t2, cpu_gr[a->r1], sa); } else { assert(!a->d); @@ -3541,7 +3431,7 @@ static bool trans_extr_imm(DisasContext *ctx, arg_extr_imm *a) static bool trans_depi_imm(DisasContext *ctx, arg_depi_imm *a) { unsigned len, width; - target_sreg mask0, mask1; + uint64_t mask0, mask1; TCGv_reg dest; if (!ctx->is_pa20 && a->d) { @@ -3620,7 +3510,7 @@ static bool do_dep_sar(DisasContext *ctx, unsigned rt, unsigned c, unsigned rs = nz ? rt : 0; unsigned widthm1 = d ? 63 : 31; TCGv_reg mask, tmp, shift, dest; - target_ureg msb = 1ULL << (len - 1); + uint64_t msb = 1ULL << (len - 1); dest = dest_gpr(ctx, rt); shift = tcg_temp_new(); @@ -3737,7 +3627,7 @@ static bool trans_bl(DisasContext *ctx, arg_bl *a) static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a) { - target_ureg dest = iaoq_dest(ctx, a->disp); + uint64_t dest = iaoq_dest(ctx, a->disp); nullify_over(ctx); @@ -3865,7 +3755,7 @@ static bool trans_fid_f(DisasContext *ctx, arg_fid_f *a) { uint64_t ret; - if (TARGET_REGISTER_BITS == 64) { + if (ctx->is_pa20) { ret = 0x13080000000000ULL; /* PA8700 (PCX-W2) */ } else { ret = 0x0f080000000000ULL; /* PA7300LC (PCX-L2) */ From 6fd0c7bc91d81244c8a0f418ac8d19bca0c20577 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 17 Oct 2023 21:31:08 -0700 Subject: [PATCH 627/974] target/hppa: Remove most of the TARGET_REGISTER_BITS redirections Remove all but those intended to change type to or from i64. Signed-off-by: Richard Henderson --- target/hppa/translate.c | 910 ++++++++++++++++++---------------------- 1 file changed, 406 insertions(+), 504 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 0172c2f898..ec3f70e46e 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -36,110 +36,15 @@ /* Since we have a distinction between register size and address size, we need to redefine all of these. */ -#undef TCGv -#undef tcg_temp_new -#undef tcg_global_mem_new - -#define TCGv_tl TCGv_i64 -#define tcg_temp_new_tl tcg_temp_new_i64 #define tcg_gen_extu_reg_tl tcg_gen_mov_i64 - -#define TCGv_reg TCGv_i64 - -#define tcg_temp_new tcg_temp_new_i64 -#define tcg_global_mem_new tcg_global_mem_new_i64 - -#define tcg_gen_movi_reg tcg_gen_movi_i64 -#define tcg_gen_mov_reg tcg_gen_mov_i64 -#define tcg_gen_ld8u_reg tcg_gen_ld8u_i64 -#define tcg_gen_ld8s_reg tcg_gen_ld8s_i64 -#define tcg_gen_ld16u_reg tcg_gen_ld16u_i64 -#define tcg_gen_ld16s_reg tcg_gen_ld16s_i64 -#define tcg_gen_ld32u_reg tcg_gen_ld32u_i64 -#define tcg_gen_ld32s_reg tcg_gen_ld32s_i64 -#define tcg_gen_ld_reg tcg_gen_ld_i64 -#define tcg_gen_st8_reg tcg_gen_st8_i64 -#define tcg_gen_st16_reg tcg_gen_st16_i64 -#define tcg_gen_st32_reg tcg_gen_st32_i64 -#define tcg_gen_st_reg tcg_gen_st_i64 -#define tcg_gen_add_reg tcg_gen_add_i64 -#define tcg_gen_addi_reg tcg_gen_addi_i64 -#define tcg_gen_sub_reg tcg_gen_sub_i64 -#define tcg_gen_neg_reg tcg_gen_neg_i64 -#define tcg_gen_subfi_reg tcg_gen_subfi_i64 -#define tcg_gen_subi_reg tcg_gen_subi_i64 -#define tcg_gen_and_reg tcg_gen_and_i64 -#define tcg_gen_andi_reg tcg_gen_andi_i64 -#define tcg_gen_or_reg tcg_gen_or_i64 -#define tcg_gen_ori_reg tcg_gen_ori_i64 -#define tcg_gen_xor_reg tcg_gen_xor_i64 -#define tcg_gen_xori_reg tcg_gen_xori_i64 -#define tcg_gen_not_reg tcg_gen_not_i64 -#define tcg_gen_shl_reg tcg_gen_shl_i64 -#define tcg_gen_shli_reg tcg_gen_shli_i64 -#define tcg_gen_shr_reg tcg_gen_shr_i64 -#define tcg_gen_shri_reg tcg_gen_shri_i64 -#define tcg_gen_sar_reg tcg_gen_sar_i64 -#define tcg_gen_sari_reg tcg_gen_sari_i64 -#define tcg_gen_brcond_reg tcg_gen_brcond_i64 -#define tcg_gen_brcondi_reg tcg_gen_brcondi_i64 -#define tcg_gen_setcond_reg tcg_gen_setcond_i64 -#define tcg_gen_setcondi_reg tcg_gen_setcondi_i64 -#define tcg_gen_mul_reg tcg_gen_mul_i64 -#define tcg_gen_muli_reg tcg_gen_muli_i64 -#define tcg_gen_div_reg tcg_gen_div_i64 -#define tcg_gen_rem_reg tcg_gen_rem_i64 -#define tcg_gen_divu_reg tcg_gen_divu_i64 -#define tcg_gen_remu_reg tcg_gen_remu_i64 -#define tcg_gen_discard_reg tcg_gen_discard_i64 -#define tcg_gen_trunc_reg_i32 tcg_gen_extrl_i64_i32 #define tcg_gen_trunc_i64_reg tcg_gen_mov_i64 -#define tcg_gen_extu_i32_reg tcg_gen_extu_i32_i64 -#define tcg_gen_ext_i32_reg tcg_gen_ext_i32_i64 #define tcg_gen_extu_reg_i64 tcg_gen_mov_i64 #define tcg_gen_ext_reg_i64 tcg_gen_mov_i64 -#define tcg_gen_ext8u_reg tcg_gen_ext8u_i64 -#define tcg_gen_ext8s_reg tcg_gen_ext8s_i64 -#define tcg_gen_ext16u_reg tcg_gen_ext16u_i64 -#define tcg_gen_ext16s_reg tcg_gen_ext16s_i64 -#define tcg_gen_ext32u_reg tcg_gen_ext32u_i64 -#define tcg_gen_ext32s_reg tcg_gen_ext32s_i64 -#define tcg_gen_bswap16_reg tcg_gen_bswap16_i64 -#define tcg_gen_bswap32_reg tcg_gen_bswap32_i64 -#define tcg_gen_bswap64_reg tcg_gen_bswap64_i64 -#define tcg_gen_concat_reg_i64 tcg_gen_concat32_i64 -#define tcg_gen_andc_reg tcg_gen_andc_i64 -#define tcg_gen_eqv_reg tcg_gen_eqv_i64 -#define tcg_gen_nand_reg tcg_gen_nand_i64 -#define tcg_gen_nor_reg tcg_gen_nor_i64 -#define tcg_gen_orc_reg tcg_gen_orc_i64 -#define tcg_gen_clz_reg tcg_gen_clz_i64 -#define tcg_gen_ctz_reg tcg_gen_ctz_i64 -#define tcg_gen_clzi_reg tcg_gen_clzi_i64 -#define tcg_gen_ctzi_reg tcg_gen_ctzi_i64 -#define tcg_gen_clrsb_reg tcg_gen_clrsb_i64 -#define tcg_gen_ctpop_reg tcg_gen_ctpop_i64 -#define tcg_gen_rotl_reg tcg_gen_rotl_i64 -#define tcg_gen_rotli_reg tcg_gen_rotli_i64 -#define tcg_gen_rotr_reg tcg_gen_rotr_i64 -#define tcg_gen_rotri_reg tcg_gen_rotri_i64 -#define tcg_gen_deposit_reg tcg_gen_deposit_i64 -#define tcg_gen_deposit_z_reg tcg_gen_deposit_z_i64 -#define tcg_gen_extract_reg tcg_gen_extract_i64 -#define tcg_gen_sextract_reg tcg_gen_sextract_i64 -#define tcg_gen_extract2_reg tcg_gen_extract2_i64 -#define tcg_constant_reg tcg_constant_i64 -#define tcg_gen_movcond_reg tcg_gen_movcond_i64 -#define tcg_gen_add2_reg tcg_gen_add2_i64 -#define tcg_gen_sub2_reg tcg_gen_sub2_i64 -#define tcg_gen_qemu_ld_reg tcg_gen_qemu_ld_i64 -#define tcg_gen_qemu_st_reg tcg_gen_qemu_st_i64 -#define tcg_gen_atomic_xchg_reg tcg_gen_atomic_xchg_i64 -#define tcg_gen_trunc_reg_ptr tcg_gen_trunc_i64_ptr + typedef struct DisasCond { TCGCond c; - TCGv_reg a0, a1; + TCGv_i64 a0, a1; } DisasCond; typedef struct DisasContext { @@ -149,7 +54,7 @@ typedef struct DisasContext { uint64_t iaoq_f; uint64_t iaoq_b; uint64_t iaoq_n; - TCGv_reg iaoq_n_var; + TCGv_i64 iaoq_n_var; DisasCond null_cond; TCGLabel *null_lab; @@ -261,24 +166,24 @@ static int cmpbid_c(DisasContext *ctx, int val) #define DISAS_EXIT DISAS_TARGET_3 /* global register indexes */ -static TCGv_reg cpu_gr[32]; +static TCGv_i64 cpu_gr[32]; static TCGv_i64 cpu_sr[4]; static TCGv_i64 cpu_srH; -static TCGv_reg cpu_iaoq_f; -static TCGv_reg cpu_iaoq_b; +static TCGv_i64 cpu_iaoq_f; +static TCGv_i64 cpu_iaoq_b; static TCGv_i64 cpu_iasq_f; static TCGv_i64 cpu_iasq_b; -static TCGv_reg cpu_sar; -static TCGv_reg cpu_psw_n; -static TCGv_reg cpu_psw_v; -static TCGv_reg cpu_psw_cb; -static TCGv_reg cpu_psw_cb_msb; +static TCGv_i64 cpu_sar; +static TCGv_i64 cpu_psw_n; +static TCGv_i64 cpu_psw_v; +static TCGv_i64 cpu_psw_cb; +static TCGv_i64 cpu_psw_cb_msb; void hppa_translate_init(void) { #define DEF_VAR(V) { &cpu_##V, #V, offsetof(CPUHPPAState, V) } - typedef struct { TCGv_reg *var; const char *name; int ofs; } GlobalVar; + typedef struct { TCGv_i64 *var; const char *name; int ofs; } GlobalVar; static const GlobalVar vars[] = { { &cpu_sar, "sar", offsetof(CPUHPPAState, cr[CR_SAR]) }, DEF_VAR(psw_n), @@ -356,35 +261,35 @@ static DisasCond cond_make_n(void) return (DisasCond){ .c = TCG_COND_NE, .a0 = cpu_psw_n, - .a1 = tcg_constant_reg(0) + .a1 = tcg_constant_i64(0) }; } -static DisasCond cond_make_tmp(TCGCond c, TCGv_reg a0, TCGv_reg a1) +static DisasCond cond_make_tmp(TCGCond c, TCGv_i64 a0, TCGv_i64 a1) { assert (c != TCG_COND_NEVER && c != TCG_COND_ALWAYS); return (DisasCond){ .c = c, .a0 = a0, .a1 = a1 }; } -static DisasCond cond_make_0_tmp(TCGCond c, TCGv_reg a0) +static DisasCond cond_make_0_tmp(TCGCond c, TCGv_i64 a0) { - return cond_make_tmp(c, a0, tcg_constant_reg(0)); + return cond_make_tmp(c, a0, tcg_constant_i64(0)); } -static DisasCond cond_make_0(TCGCond c, TCGv_reg a0) +static DisasCond cond_make_0(TCGCond c, TCGv_i64 a0) { - TCGv_reg tmp = tcg_temp_new(); - tcg_gen_mov_reg(tmp, a0); + TCGv_i64 tmp = tcg_temp_new(); + tcg_gen_mov_i64(tmp, a0); return cond_make_0_tmp(c, tmp); } -static DisasCond cond_make(TCGCond c, TCGv_reg a0, TCGv_reg a1) +static DisasCond cond_make(TCGCond c, TCGv_i64 a0, TCGv_i64 a1) { - TCGv_reg t0 = tcg_temp_new(); - TCGv_reg t1 = tcg_temp_new(); + TCGv_i64 t0 = tcg_temp_new(); + TCGv_i64 t1 = tcg_temp_new(); - tcg_gen_mov_reg(t0, a0); - tcg_gen_mov_reg(t1, a1); + tcg_gen_mov_i64(t0, a0); + tcg_gen_mov_i64(t1, a1); return cond_make_tmp(c, t0, t1); } @@ -403,18 +308,18 @@ static void cond_free(DisasCond *cond) } } -static TCGv_reg load_gpr(DisasContext *ctx, unsigned reg) +static TCGv_i64 load_gpr(DisasContext *ctx, unsigned reg) { if (reg == 0) { - TCGv_reg t = tcg_temp_new(); - tcg_gen_movi_reg(t, 0); + TCGv_i64 t = tcg_temp_new(); + tcg_gen_movi_i64(t, 0); return t; } else { return cpu_gr[reg]; } } -static TCGv_reg dest_gpr(DisasContext *ctx, unsigned reg) +static TCGv_i64 dest_gpr(DisasContext *ctx, unsigned reg) { if (reg == 0 || ctx->null_cond.c != TCG_COND_NEVER) { return tcg_temp_new(); @@ -423,17 +328,17 @@ static TCGv_reg dest_gpr(DisasContext *ctx, unsigned reg) } } -static void save_or_nullify(DisasContext *ctx, TCGv_reg dest, TCGv_reg t) +static void save_or_nullify(DisasContext *ctx, TCGv_i64 dest, TCGv_i64 t) { if (ctx->null_cond.c != TCG_COND_NEVER) { - tcg_gen_movcond_reg(ctx->null_cond.c, dest, ctx->null_cond.a0, + tcg_gen_movcond_i64(ctx->null_cond.c, dest, ctx->null_cond.a0, ctx->null_cond.a1, dest, t); } else { - tcg_gen_mov_reg(dest, t); + tcg_gen_mov_i64(dest, t); } } -static void save_gpr(DisasContext *ctx, unsigned reg, TCGv_reg t) +static void save_gpr(DisasContext *ctx, unsigned reg, TCGv_i64 t) { if (reg != 0) { save_or_nullify(ctx, cpu_gr[reg], t); @@ -542,17 +447,17 @@ static void nullify_over(DisasContext *ctx) /* If we're using PSW[N], copy it to a temp because... */ if (ctx->null_cond.a0 == cpu_psw_n) { ctx->null_cond.a0 = tcg_temp_new(); - tcg_gen_mov_reg(ctx->null_cond.a0, cpu_psw_n); + tcg_gen_mov_i64(ctx->null_cond.a0, cpu_psw_n); } /* ... we clear it before branching over the implementation, so that (1) it's clear after nullifying this insn and (2) if this insn nullifies the next, PSW[N] is valid. */ if (ctx->psw_n_nonzero) { ctx->psw_n_nonzero = false; - tcg_gen_movi_reg(cpu_psw_n, 0); + tcg_gen_movi_i64(cpu_psw_n, 0); } - tcg_gen_brcond_reg(ctx->null_cond.c, ctx->null_cond.a0, + tcg_gen_brcond_i64(ctx->null_cond.c, ctx->null_cond.a0, ctx->null_cond.a1, ctx->null_lab); cond_free(&ctx->null_cond); } @@ -563,12 +468,12 @@ static void nullify_save(DisasContext *ctx) { if (ctx->null_cond.c == TCG_COND_NEVER) { if (ctx->psw_n_nonzero) { - tcg_gen_movi_reg(cpu_psw_n, 0); + tcg_gen_movi_i64(cpu_psw_n, 0); } return; } if (ctx->null_cond.a0 != cpu_psw_n) { - tcg_gen_setcond_reg(ctx->null_cond.c, cpu_psw_n, + tcg_gen_setcond_i64(ctx->null_cond.c, cpu_psw_n, ctx->null_cond.a0, ctx->null_cond.a1); ctx->psw_n_nonzero = true; } @@ -581,7 +486,7 @@ static void nullify_save(DisasContext *ctx) static void nullify_set(DisasContext *ctx, bool x) { if (ctx->psw_n_nonzero || x) { - tcg_gen_movi_reg(cpu_psw_n, x); + tcg_gen_movi_i64(cpu_psw_n, x); } } @@ -631,13 +536,13 @@ static uint64_t gva_offset_mask(DisasContext *ctx) : MAKE_64BIT_MASK(0, 32)); } -static void copy_iaoq_entry(DisasContext *ctx, TCGv_reg dest, - uint64_t ival, TCGv_reg vval) +static void copy_iaoq_entry(DisasContext *ctx, TCGv_i64 dest, + uint64_t ival, TCGv_i64 vval) { uint64_t mask = gva_offset_mask(ctx); if (ival != -1) { - tcg_gen_movi_reg(dest, ival & mask); + tcg_gen_movi_i64(dest, ival & mask); return; } tcg_debug_assert(vval != NULL); @@ -647,9 +552,9 @@ static void copy_iaoq_entry(DisasContext *ctx, TCGv_reg dest, * This optimization is primarily for "iaoq_f = iaoq_b". */ if (vval == cpu_iaoq_f || vval == cpu_iaoq_b) { - tcg_gen_mov_reg(dest, vval); + tcg_gen_mov_i64(dest, vval); } else { - tcg_gen_andi_reg(dest, vval, mask); + tcg_gen_andi_i64(dest, vval, mask); } } @@ -675,7 +580,7 @@ static void gen_excp(DisasContext *ctx, int exception) static bool gen_excp_iir(DisasContext *ctx, int exc) { nullify_over(ctx); - tcg_gen_st_reg(tcg_constant_reg(ctx->insn), + tcg_gen_st_i64(tcg_constant_i64(ctx->insn), tcg_env, offsetof(CPUHPPAState, cr[CR_IIR])); gen_excp(ctx, exc); return nullify_end(ctx); @@ -738,7 +643,7 @@ static bool cond_need_cb(int c) return c == 4 || c == 5; } -/* Need extensions from TCGv_i32 to TCGv_reg. */ +/* Need extensions from TCGv_i32 to TCGv_i64. */ static bool cond_need_ext(DisasContext *ctx, bool d) { return !(ctx->is_pa20 && d); @@ -750,10 +655,10 @@ static bool cond_need_ext(DisasContext *ctx, bool d) */ static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d, - TCGv_reg res, TCGv_reg cb_msb, TCGv_reg sv) + TCGv_i64 res, TCGv_i64 cb_msb, TCGv_i64 sv) { DisasCond cond; - TCGv_reg tmp; + TCGv_i64 tmp; switch (cf >> 1) { case 0: /* Never / TR (0 / 1) */ @@ -762,16 +667,16 @@ static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d, case 1: /* = / <> (Z / !Z) */ if (cond_need_ext(ctx, d)) { tmp = tcg_temp_new(); - tcg_gen_ext32u_reg(tmp, res); + tcg_gen_ext32u_i64(tmp, res); res = tmp; } cond = cond_make_0(TCG_COND_EQ, res); break; case 2: /* < / >= (N ^ V / !(N ^ V) */ tmp = tcg_temp_new(); - tcg_gen_xor_reg(tmp, res, sv); + tcg_gen_xor_i64(tmp, res, sv); if (cond_need_ext(ctx, d)) { - tcg_gen_ext32s_reg(tmp, tmp); + tcg_gen_ext32s_i64(tmp, tmp); } cond = cond_make_0_tmp(TCG_COND_LT, tmp); break; @@ -786,14 +691,14 @@ static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d, * !(~(res ^ sv) >> 31 & res) */ tmp = tcg_temp_new(); - tcg_gen_eqv_reg(tmp, res, sv); + tcg_gen_eqv_i64(tmp, res, sv); if (cond_need_ext(ctx, d)) { - tcg_gen_sextract_reg(tmp, tmp, 31, 1); - tcg_gen_and_reg(tmp, tmp, res); - tcg_gen_ext32u_reg(tmp, tmp); + tcg_gen_sextract_i64(tmp, tmp, 31, 1); + tcg_gen_and_i64(tmp, tmp, res); + tcg_gen_ext32u_i64(tmp, tmp); } else { - tcg_gen_sari_reg(tmp, tmp, 63); - tcg_gen_and_reg(tmp, tmp, res); + tcg_gen_sari_i64(tmp, tmp, 63); + tcg_gen_and_i64(tmp, tmp, res); } cond = cond_make_0_tmp(TCG_COND_EQ, tmp); break; @@ -803,24 +708,24 @@ static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d, break; case 5: /* ZNV / VNZ (!C | Z / C & !Z) */ tmp = tcg_temp_new(); - tcg_gen_neg_reg(tmp, cb_msb); - tcg_gen_and_reg(tmp, tmp, res); + tcg_gen_neg_i64(tmp, cb_msb); + tcg_gen_and_i64(tmp, tmp, res); if (cond_need_ext(ctx, d)) { - tcg_gen_ext32u_reg(tmp, tmp); + tcg_gen_ext32u_i64(tmp, tmp); } cond = cond_make_0_tmp(TCG_COND_EQ, tmp); break; case 6: /* SV / NSV (V / !V) */ if (cond_need_ext(ctx, d)) { tmp = tcg_temp_new(); - tcg_gen_ext32s_reg(tmp, sv); + tcg_gen_ext32s_i64(tmp, sv); sv = tmp; } cond = cond_make_0(TCG_COND_LT, sv); break; case 7: /* OD / EV */ tmp = tcg_temp_new(); - tcg_gen_andi_reg(tmp, res, 1); + tcg_gen_andi_i64(tmp, res, 1); cond = cond_make_0_tmp(TCG_COND_NE, tmp); break; default: @@ -838,8 +743,8 @@ static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d, deleted as unused. */ static DisasCond do_sub_cond(DisasContext *ctx, unsigned cf, bool d, - TCGv_reg res, TCGv_reg in1, - TCGv_reg in2, TCGv_reg sv) + TCGv_i64 res, TCGv_i64 in1, + TCGv_i64 in2, TCGv_i64 sv) { TCGCond tc; bool ext_uns; @@ -873,15 +778,15 @@ static DisasCond do_sub_cond(DisasContext *ctx, unsigned cf, bool d, tc = tcg_invert_cond(tc); } if (cond_need_ext(ctx, d)) { - TCGv_reg t1 = tcg_temp_new(); - TCGv_reg t2 = tcg_temp_new(); + TCGv_i64 t1 = tcg_temp_new(); + TCGv_i64 t2 = tcg_temp_new(); if (ext_uns) { - tcg_gen_ext32u_reg(t1, in1); - tcg_gen_ext32u_reg(t2, in2); + tcg_gen_ext32u_i64(t1, in1); + tcg_gen_ext32u_i64(t2, in2); } else { - tcg_gen_ext32s_reg(t1, in1); - tcg_gen_ext32s_reg(t2, in2); + tcg_gen_ext32s_i64(t1, in1); + tcg_gen_ext32s_i64(t2, in2); } return cond_make_tmp(tc, t1, t2); } @@ -898,7 +803,7 @@ static DisasCond do_sub_cond(DisasContext *ctx, unsigned cf, bool d, */ static DisasCond do_log_cond(DisasContext *ctx, unsigned cf, bool d, - TCGv_reg res) + TCGv_i64 res) { TCGCond tc; bool ext_uns; @@ -950,12 +855,12 @@ static DisasCond do_log_cond(DisasContext *ctx, unsigned cf, bool d, } if (cond_need_ext(ctx, d)) { - TCGv_reg tmp = tcg_temp_new(); + TCGv_i64 tmp = tcg_temp_new(); if (ext_uns) { - tcg_gen_ext32u_reg(tmp, res); + tcg_gen_ext32u_i64(tmp, res); } else { - tcg_gen_ext32s_reg(tmp, res); + tcg_gen_ext32s_i64(tmp, res); } return cond_make_0_tmp(tc, tmp); } @@ -965,7 +870,7 @@ static DisasCond do_log_cond(DisasContext *ctx, unsigned cf, bool d, /* Similar, but for shift/extract/deposit conditions. */ static DisasCond do_sed_cond(DisasContext *ctx, unsigned orig, bool d, - TCGv_reg res) + TCGv_i64 res) { unsigned c, f; @@ -983,11 +888,11 @@ static DisasCond do_sed_cond(DisasContext *ctx, unsigned orig, bool d, /* Similar, but for unit conditions. */ -static DisasCond do_unit_cond(unsigned cf, bool d, TCGv_reg res, - TCGv_reg in1, TCGv_reg in2) +static DisasCond do_unit_cond(unsigned cf, bool d, TCGv_i64 res, + TCGv_i64 in1, TCGv_i64 in2) { DisasCond cond; - TCGv_reg tmp, cb = NULL; + TCGv_i64 tmp, cb = NULL; uint64_t d_repl = d ? 0x0000000100000001ull : 1; if (cf & 8) { @@ -997,10 +902,10 @@ static DisasCond do_unit_cond(unsigned cf, bool d, TCGv_reg res, */ cb = tcg_temp_new(); tmp = tcg_temp_new(); - tcg_gen_or_reg(cb, in1, in2); - tcg_gen_and_reg(tmp, in1, in2); - tcg_gen_andc_reg(cb, cb, res); - tcg_gen_or_reg(cb, cb, tmp); + tcg_gen_or_i64(cb, in1, in2); + tcg_gen_and_i64(tmp, in1, in2); + tcg_gen_andc_i64(cb, cb, res); + tcg_gen_or_i64(cb, cb, tmp); } switch (cf >> 1) { @@ -1015,32 +920,32 @@ static DisasCond do_unit_cond(unsigned cf, bool d, TCGv_reg res, * https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord */ tmp = tcg_temp_new(); - tcg_gen_subi_reg(tmp, res, d_repl * 0x01010101u); - tcg_gen_andc_reg(tmp, tmp, res); - tcg_gen_andi_reg(tmp, tmp, d_repl * 0x80808080u); + tcg_gen_subi_i64(tmp, res, d_repl * 0x01010101u); + tcg_gen_andc_i64(tmp, tmp, res); + tcg_gen_andi_i64(tmp, tmp, d_repl * 0x80808080u); cond = cond_make_0(TCG_COND_NE, tmp); break; case 3: /* SHZ / NHZ */ tmp = tcg_temp_new(); - tcg_gen_subi_reg(tmp, res, d_repl * 0x00010001u); - tcg_gen_andc_reg(tmp, tmp, res); - tcg_gen_andi_reg(tmp, tmp, d_repl * 0x80008000u); + tcg_gen_subi_i64(tmp, res, d_repl * 0x00010001u); + tcg_gen_andc_i64(tmp, tmp, res); + tcg_gen_andi_i64(tmp, tmp, d_repl * 0x80008000u); cond = cond_make_0(TCG_COND_NE, tmp); break; case 4: /* SDC / NDC */ - tcg_gen_andi_reg(cb, cb, d_repl * 0x88888888u); + tcg_gen_andi_i64(cb, cb, d_repl * 0x88888888u); cond = cond_make_0(TCG_COND_NE, cb); break; case 6: /* SBC / NBC */ - tcg_gen_andi_reg(cb, cb, d_repl * 0x80808080u); + tcg_gen_andi_i64(cb, cb, d_repl * 0x80808080u); cond = cond_make_0(TCG_COND_NE, cb); break; case 7: /* SHC / NHC */ - tcg_gen_andi_reg(cb, cb, d_repl * 0x80008000u); + tcg_gen_andi_i64(cb, cb, d_repl * 0x80008000u); cond = cond_make_0(TCG_COND_NE, cb); break; @@ -1054,55 +959,55 @@ static DisasCond do_unit_cond(unsigned cf, bool d, TCGv_reg res, return cond; } -static TCGv_reg get_carry(DisasContext *ctx, bool d, - TCGv_reg cb, TCGv_reg cb_msb) +static TCGv_i64 get_carry(DisasContext *ctx, bool d, + TCGv_i64 cb, TCGv_i64 cb_msb) { if (cond_need_ext(ctx, d)) { - TCGv_reg t = tcg_temp_new(); - tcg_gen_extract_reg(t, cb, 32, 1); + TCGv_i64 t = tcg_temp_new(); + tcg_gen_extract_i64(t, cb, 32, 1); return t; } return cb_msb; } -static TCGv_reg get_psw_carry(DisasContext *ctx, bool d) +static TCGv_i64 get_psw_carry(DisasContext *ctx, bool d) { return get_carry(ctx, d, cpu_psw_cb, cpu_psw_cb_msb); } /* Compute signed overflow for addition. */ -static TCGv_reg do_add_sv(DisasContext *ctx, TCGv_reg res, - TCGv_reg in1, TCGv_reg in2) +static TCGv_i64 do_add_sv(DisasContext *ctx, TCGv_i64 res, + TCGv_i64 in1, TCGv_i64 in2) { - TCGv_reg sv = tcg_temp_new(); - TCGv_reg tmp = tcg_temp_new(); + TCGv_i64 sv = tcg_temp_new(); + TCGv_i64 tmp = tcg_temp_new(); - tcg_gen_xor_reg(sv, res, in1); - tcg_gen_xor_reg(tmp, in1, in2); - tcg_gen_andc_reg(sv, sv, tmp); + tcg_gen_xor_i64(sv, res, in1); + tcg_gen_xor_i64(tmp, in1, in2); + tcg_gen_andc_i64(sv, sv, tmp); return sv; } /* Compute signed overflow for subtraction. */ -static TCGv_reg do_sub_sv(DisasContext *ctx, TCGv_reg res, - TCGv_reg in1, TCGv_reg in2) +static TCGv_i64 do_sub_sv(DisasContext *ctx, TCGv_i64 res, + TCGv_i64 in1, TCGv_i64 in2) { - TCGv_reg sv = tcg_temp_new(); - TCGv_reg tmp = tcg_temp_new(); + TCGv_i64 sv = tcg_temp_new(); + TCGv_i64 tmp = tcg_temp_new(); - tcg_gen_xor_reg(sv, res, in1); - tcg_gen_xor_reg(tmp, in1, in2); - tcg_gen_and_reg(sv, sv, tmp); + tcg_gen_xor_i64(sv, res, in1); + tcg_gen_xor_i64(tmp, in1, in2); + tcg_gen_and_i64(sv, sv, tmp); return sv; } -static void do_add(DisasContext *ctx, unsigned rt, TCGv_reg in1, - TCGv_reg in2, unsigned shift, bool is_l, +static void do_add(DisasContext *ctx, unsigned rt, TCGv_i64 in1, + TCGv_i64 in2, unsigned shift, bool is_l, bool is_tsv, bool is_tc, bool is_c, unsigned cf, bool d) { - TCGv_reg dest, cb, cb_msb, cb_cond, sv, tmp; + TCGv_i64 dest, cb, cb_msb, cb_cond, sv, tmp; unsigned c = cf >> 1; DisasCond cond; @@ -1113,29 +1018,29 @@ static void do_add(DisasContext *ctx, unsigned rt, TCGv_reg in1, if (shift) { tmp = tcg_temp_new(); - tcg_gen_shli_reg(tmp, in1, shift); + tcg_gen_shli_i64(tmp, in1, shift); in1 = tmp; } if (!is_l || cond_need_cb(c)) { - TCGv_reg zero = tcg_constant_reg(0); + TCGv_i64 zero = tcg_constant_i64(0); cb_msb = tcg_temp_new(); cb = tcg_temp_new(); - tcg_gen_add2_reg(dest, cb_msb, in1, zero, in2, zero); + tcg_gen_add2_i64(dest, cb_msb, in1, zero, in2, zero); if (is_c) { - tcg_gen_add2_reg(dest, cb_msb, dest, cb_msb, + tcg_gen_add2_i64(dest, cb_msb, dest, cb_msb, get_psw_carry(ctx, d), zero); } - tcg_gen_xor_reg(cb, in1, in2); - tcg_gen_xor_reg(cb, cb, dest); + tcg_gen_xor_i64(cb, in1, in2); + tcg_gen_xor_i64(cb, cb, dest); if (cond_need_cb(c)) { cb_cond = get_carry(ctx, d, cb, cb_msb); } } else { - tcg_gen_add_reg(dest, in1, in2); + tcg_gen_add_i64(dest, in1, in2); if (is_c) { - tcg_gen_add_reg(dest, dest, get_psw_carry(ctx, d)); + tcg_gen_add_i64(dest, dest, get_psw_carry(ctx, d)); } } @@ -1153,7 +1058,7 @@ static void do_add(DisasContext *ctx, unsigned rt, TCGv_reg in1, cond = do_cond(ctx, cf, d, dest, cb_cond, sv); if (is_tc) { tmp = tcg_temp_new(); - tcg_gen_setcond_reg(cond.c, tmp, cond.a0, cond.a1); + tcg_gen_setcond_i64(cond.c, tmp, cond.a0, cond.a1); gen_helper_tcond(tcg_env, tmp); } @@ -1172,7 +1077,7 @@ static void do_add(DisasContext *ctx, unsigned rt, TCGv_reg in1, static bool do_add_reg(DisasContext *ctx, arg_rrr_cf_d_sh *a, bool is_l, bool is_tsv, bool is_tc, bool is_c) { - TCGv_reg tcg_r1, tcg_r2; + TCGv_i64 tcg_r1, tcg_r2; if (a->cf) { nullify_over(ctx); @@ -1187,23 +1092,23 @@ static bool do_add_reg(DisasContext *ctx, arg_rrr_cf_d_sh *a, static bool do_add_imm(DisasContext *ctx, arg_rri_cf *a, bool is_tsv, bool is_tc) { - TCGv_reg tcg_im, tcg_r2; + TCGv_i64 tcg_im, tcg_r2; if (a->cf) { nullify_over(ctx); } - tcg_im = tcg_constant_reg(a->i); + tcg_im = tcg_constant_i64(a->i); tcg_r2 = load_gpr(ctx, a->r); /* All ADDI conditions are 32-bit. */ do_add(ctx, a->t, tcg_im, tcg_r2, 0, 0, is_tsv, is_tc, 0, a->cf, false); return nullify_end(ctx); } -static void do_sub(DisasContext *ctx, unsigned rt, TCGv_reg in1, - TCGv_reg in2, bool is_tsv, bool is_b, +static void do_sub(DisasContext *ctx, unsigned rt, TCGv_i64 in1, + TCGv_i64 in2, bool is_tsv, bool is_b, bool is_tc, unsigned cf, bool d) { - TCGv_reg dest, sv, cb, cb_msb, zero, tmp; + TCGv_i64 dest, sv, cb, cb_msb, zero, tmp; unsigned c = cf >> 1; DisasCond cond; @@ -1211,23 +1116,23 @@ static void do_sub(DisasContext *ctx, unsigned rt, TCGv_reg in1, cb = tcg_temp_new(); cb_msb = tcg_temp_new(); - zero = tcg_constant_reg(0); + zero = tcg_constant_i64(0); if (is_b) { /* DEST,C = IN1 + ~IN2 + C. */ - tcg_gen_not_reg(cb, in2); - tcg_gen_add2_reg(dest, cb_msb, in1, zero, get_psw_carry(ctx, d), zero); - tcg_gen_add2_reg(dest, cb_msb, dest, cb_msb, cb, zero); - tcg_gen_xor_reg(cb, cb, in1); - tcg_gen_xor_reg(cb, cb, dest); + tcg_gen_not_i64(cb, in2); + tcg_gen_add2_i64(dest, cb_msb, in1, zero, get_psw_carry(ctx, d), zero); + tcg_gen_add2_i64(dest, cb_msb, dest, cb_msb, cb, zero); + tcg_gen_xor_i64(cb, cb, in1); + tcg_gen_xor_i64(cb, cb, dest); } else { /* * DEST,C = IN1 + ~IN2 + 1. We can produce the same result in fewer * operations by seeding the high word with 1 and subtracting. */ - TCGv_reg one = tcg_constant_reg(1); - tcg_gen_sub2_reg(dest, cb_msb, in1, one, in2, zero); - tcg_gen_eqv_reg(cb, in1, in2); - tcg_gen_xor_reg(cb, cb, dest); + TCGv_i64 one = tcg_constant_i64(1); + tcg_gen_sub2_i64(dest, cb_msb, in1, one, in2, zero); + tcg_gen_eqv_i64(cb, in1, in2); + tcg_gen_xor_i64(cb, cb, dest); } /* Compute signed overflow if required. */ @@ -1249,7 +1154,7 @@ static void do_sub(DisasContext *ctx, unsigned rt, TCGv_reg in1, /* Emit any conditional trap before any writeback. */ if (is_tc) { tmp = tcg_temp_new(); - tcg_gen_setcond_reg(cond.c, tmp, cond.a0, cond.a1); + tcg_gen_setcond_i64(cond.c, tmp, cond.a0, cond.a1); gen_helper_tcond(tcg_env, tmp); } @@ -1266,7 +1171,7 @@ static void do_sub(DisasContext *ctx, unsigned rt, TCGv_reg in1, static bool do_sub_reg(DisasContext *ctx, arg_rrr_cf_d *a, bool is_tsv, bool is_b, bool is_tc) { - TCGv_reg tcg_r1, tcg_r2; + TCGv_i64 tcg_r1, tcg_r2; if (a->cf) { nullify_over(ctx); @@ -1279,26 +1184,26 @@ static bool do_sub_reg(DisasContext *ctx, arg_rrr_cf_d *a, static bool do_sub_imm(DisasContext *ctx, arg_rri_cf *a, bool is_tsv) { - TCGv_reg tcg_im, tcg_r2; + TCGv_i64 tcg_im, tcg_r2; if (a->cf) { nullify_over(ctx); } - tcg_im = tcg_constant_reg(a->i); + tcg_im = tcg_constant_i64(a->i); tcg_r2 = load_gpr(ctx, a->r); /* All SUBI conditions are 32-bit. */ do_sub(ctx, a->t, tcg_im, tcg_r2, is_tsv, 0, 0, a->cf, false); return nullify_end(ctx); } -static void do_cmpclr(DisasContext *ctx, unsigned rt, TCGv_reg in1, - TCGv_reg in2, unsigned cf, bool d) +static void do_cmpclr(DisasContext *ctx, unsigned rt, TCGv_i64 in1, + TCGv_i64 in2, unsigned cf, bool d) { - TCGv_reg dest, sv; + TCGv_i64 dest, sv; DisasCond cond; dest = tcg_temp_new(); - tcg_gen_sub_reg(dest, in1, in2); + tcg_gen_sub_i64(dest, in1, in2); /* Compute signed overflow if required. */ sv = NULL; @@ -1310,7 +1215,7 @@ static void do_cmpclr(DisasContext *ctx, unsigned rt, TCGv_reg in1, cond = do_sub_cond(ctx, cf, d, dest, in1, in2, sv); /* Clear. */ - tcg_gen_movi_reg(dest, 0); + tcg_gen_movi_i64(dest, 0); save_gpr(ctx, rt, dest); /* Install the new nullification. */ @@ -1318,11 +1223,11 @@ static void do_cmpclr(DisasContext *ctx, unsigned rt, TCGv_reg in1, ctx->null_cond = cond; } -static void do_log(DisasContext *ctx, unsigned rt, TCGv_reg in1, - TCGv_reg in2, unsigned cf, bool d, - void (*fn)(TCGv_reg, TCGv_reg, TCGv_reg)) +static void do_log(DisasContext *ctx, unsigned rt, TCGv_i64 in1, + TCGv_i64 in2, unsigned cf, bool d, + void (*fn)(TCGv_i64, TCGv_i64, TCGv_i64)) { - TCGv_reg dest = dest_gpr(ctx, rt); + TCGv_i64 dest = dest_gpr(ctx, rt); /* Perform the operation, and writeback. */ fn(dest, in1, in2); @@ -1336,9 +1241,9 @@ static void do_log(DisasContext *ctx, unsigned rt, TCGv_reg in1, } static bool do_log_reg(DisasContext *ctx, arg_rrr_cf_d *a, - void (*fn)(TCGv_reg, TCGv_reg, TCGv_reg)) + void (*fn)(TCGv_i64, TCGv_i64, TCGv_i64)) { - TCGv_reg tcg_r1, tcg_r2; + TCGv_i64 tcg_r1, tcg_r2; if (a->cf) { nullify_over(ctx); @@ -1349,11 +1254,11 @@ static bool do_log_reg(DisasContext *ctx, arg_rrr_cf_d *a, return nullify_end(ctx); } -static void do_unit(DisasContext *ctx, unsigned rt, TCGv_reg in1, - TCGv_reg in2, unsigned cf, bool d, bool is_tc, - void (*fn)(TCGv_reg, TCGv_reg, TCGv_reg)) +static void do_unit(DisasContext *ctx, unsigned rt, TCGv_i64 in1, + TCGv_i64 in2, unsigned cf, bool d, bool is_tc, + void (*fn)(TCGv_i64, TCGv_i64, TCGv_i64)) { - TCGv_reg dest; + TCGv_i64 dest; DisasCond cond; if (cf == 0) { @@ -1368,8 +1273,8 @@ static void do_unit(DisasContext *ctx, unsigned rt, TCGv_reg in1, cond = do_unit_cond(cf, d, dest, in1, in2); if (is_tc) { - TCGv_reg tmp = tcg_temp_new(); - tcg_gen_setcond_reg(cond.c, tmp, cond.a0, cond.a1); + TCGv_i64 tmp = tcg_temp_new(); + tcg_gen_setcond_i64(cond.c, tmp, cond.a0, cond.a1); gen_helper_tcond(tcg_env, tmp); } save_gpr(ctx, rt, dest); @@ -1384,17 +1289,17 @@ static void do_unit(DisasContext *ctx, unsigned rt, TCGv_reg in1, from the top 2 bits of the base register. There are a few system instructions that have a 3-bit space specifier, for which SR0 is not special. To handle this, pass ~SP. */ -static TCGv_i64 space_select(DisasContext *ctx, int sp, TCGv_reg base) +static TCGv_i64 space_select(DisasContext *ctx, int sp, TCGv_i64 base) { TCGv_ptr ptr; - TCGv_reg tmp; + TCGv_i64 tmp; TCGv_i64 spc; if (sp != 0) { if (sp < 0) { sp = ~sp; } - spc = tcg_temp_new_tl(); + spc = tcg_temp_new_i64(); load_spr(ctx, spc, sp); return spc; } @@ -1404,12 +1309,12 @@ static TCGv_i64 space_select(DisasContext *ctx, int sp, TCGv_reg base) ptr = tcg_temp_new_ptr(); tmp = tcg_temp_new(); - spc = tcg_temp_new_tl(); + spc = tcg_temp_new_i64(); /* Extract top 2 bits of the address, shift left 3 for uint64_t index. */ - tcg_gen_shri_reg(tmp, base, (ctx->tb_flags & PSW_W ? 64 : 32) - 5); - tcg_gen_andi_reg(tmp, tmp, 030); - tcg_gen_trunc_reg_ptr(ptr, tmp); + tcg_gen_shri_i64(tmp, base, (ctx->tb_flags & PSW_W ? 64 : 32) - 5); + tcg_gen_andi_i64(tmp, tmp, 030); + tcg_gen_trunc_i64_ptr(ptr, tmp); tcg_gen_add_ptr(ptr, ptr, tcg_env); tcg_gen_ld_i64(spc, ptr, offsetof(CPUHPPAState, sr[4])); @@ -1418,28 +1323,28 @@ static TCGv_i64 space_select(DisasContext *ctx, int sp, TCGv_reg base) } #endif -static void form_gva(DisasContext *ctx, TCGv_tl *pgva, TCGv_reg *pofs, +static void form_gva(DisasContext *ctx, TCGv_i64 *pgva, TCGv_i64 *pofs, unsigned rb, unsigned rx, int scale, int64_t disp, unsigned sp, int modify, bool is_phys) { - TCGv_reg base = load_gpr(ctx, rb); - TCGv_reg ofs; - TCGv_tl addr; + TCGv_i64 base = load_gpr(ctx, rb); + TCGv_i64 ofs; + TCGv_i64 addr; /* Note that RX is mutually exclusive with DISP. */ if (rx) { ofs = tcg_temp_new(); - tcg_gen_shli_reg(ofs, cpu_gr[rx], scale); - tcg_gen_add_reg(ofs, ofs, base); + tcg_gen_shli_i64(ofs, cpu_gr[rx], scale); + tcg_gen_add_i64(ofs, ofs, base); } else if (disp || modify) { ofs = tcg_temp_new(); - tcg_gen_addi_reg(ofs, base, disp); + tcg_gen_addi_i64(ofs, base, disp); } else { ofs = base; } *pofs = ofs; - *pgva = addr = tcg_temp_new_tl(); + *pgva = addr = tcg_temp_new_i64(); tcg_gen_extu_reg_tl(addr, modify <= 0 ? ofs : base); tcg_gen_andi_tl(addr, addr, gva_offset_mask(ctx)); #ifndef CONFIG_USER_ONLY @@ -1458,8 +1363,8 @@ static void do_load_32(DisasContext *ctx, TCGv_i32 dest, unsigned rb, unsigned rx, int scale, int64_t disp, unsigned sp, int modify, MemOp mop) { - TCGv_reg ofs; - TCGv_tl addr; + TCGv_i64 ofs; + TCGv_i64 addr; /* Caller uses nullify_over/nullify_end. */ assert(ctx->null_cond.c == TCG_COND_NEVER); @@ -1476,8 +1381,8 @@ static void do_load_64(DisasContext *ctx, TCGv_i64 dest, unsigned rb, unsigned rx, int scale, int64_t disp, unsigned sp, int modify, MemOp mop) { - TCGv_reg ofs; - TCGv_tl addr; + TCGv_i64 ofs; + TCGv_i64 addr; /* Caller uses nullify_over/nullify_end. */ assert(ctx->null_cond.c == TCG_COND_NEVER); @@ -1494,8 +1399,8 @@ static void do_store_32(DisasContext *ctx, TCGv_i32 src, unsigned rb, unsigned rx, int scale, int64_t disp, unsigned sp, int modify, MemOp mop) { - TCGv_reg ofs; - TCGv_tl addr; + TCGv_i64 ofs; + TCGv_i64 addr; /* Caller uses nullify_over/nullify_end. */ assert(ctx->null_cond.c == TCG_COND_NEVER); @@ -1512,8 +1417,8 @@ static void do_store_64(DisasContext *ctx, TCGv_i64 src, unsigned rb, unsigned rx, int scale, int64_t disp, unsigned sp, int modify, MemOp mop) { - TCGv_reg ofs; - TCGv_tl addr; + TCGv_i64 ofs; + TCGv_i64 addr; /* Caller uses nullify_over/nullify_end. */ assert(ctx->null_cond.c == TCG_COND_NEVER); @@ -1526,14 +1431,11 @@ static void do_store_64(DisasContext *ctx, TCGv_i64 src, unsigned rb, } } -#define do_load_reg do_load_64 -#define do_store_reg do_store_64 - static bool do_load(DisasContext *ctx, unsigned rt, unsigned rb, unsigned rx, int scale, int64_t disp, unsigned sp, int modify, MemOp mop) { - TCGv_reg dest; + TCGv_i64 dest; nullify_over(ctx); @@ -1544,7 +1446,7 @@ static bool do_load(DisasContext *ctx, unsigned rt, unsigned rb, /* Make sure if RT == RB, we see the result of the load. */ dest = tcg_temp_new(); } - do_load_reg(ctx, dest, rb, rx, scale, disp, sp, modify, mop); + do_load_64(ctx, dest, rb, rx, scale, disp, sp, modify, mop); save_gpr(ctx, rt, dest); return nullify_end(ctx); @@ -1605,7 +1507,7 @@ static bool do_store(DisasContext *ctx, unsigned rt, unsigned rb, int modify, MemOp mop) { nullify_over(ctx); - do_store_reg(ctx, load_gpr(ctx, rt), rb, 0, 0, disp, sp, modify, mop); + do_store_64(ctx, load_gpr(ctx, rt), rb, 0, 0, disp, sp, modify, mop); return nullify_end(ctx); } @@ -1799,7 +1701,7 @@ static bool do_cbranch(DisasContext *ctx, int64_t disp, bool is_n, } taken = gen_new_label(); - tcg_gen_brcond_reg(c, cond->a0, cond->a1, taken); + tcg_gen_brcond_i64(c, cond->a0, cond->a1, taken); cond_free(cond); /* Not taken: Condition not satisfied; nullify on backward branches. */ @@ -1816,7 +1718,7 @@ static bool do_cbranch(DisasContext *ctx, int64_t disp, bool is_n, if (ctx->iaoq_n == -1) { /* The temporary iaoq_n_var died at the branch above. Regenerate it here instead of saving it. */ - tcg_gen_addi_reg(ctx->iaoq_n_var, cpu_iaoq_b, 4); + tcg_gen_addi_i64(ctx->iaoq_n_var, cpu_iaoq_b, 4); } gen_goto_tb(ctx, 0, ctx->iaoq_b, ctx->iaoq_n); } @@ -1846,10 +1748,10 @@ static bool do_cbranch(DisasContext *ctx, int64_t disp, bool is_n, /* Emit an unconditional branch to an indirect target. This handles nullification of the branch itself. */ -static bool do_ibranch(DisasContext *ctx, TCGv_reg dest, +static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, unsigned link, bool is_n) { - TCGv_reg a0, a1, next, tmp; + TCGv_i64 a0, a1, next, tmp; TCGCond c; assert(ctx->null_lab == NULL); @@ -1859,11 +1761,11 @@ static bool do_ibranch(DisasContext *ctx, TCGv_reg dest, copy_iaoq_entry(ctx, cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); } next = tcg_temp_new(); - tcg_gen_mov_reg(next, dest); + tcg_gen_mov_i64(next, dest); if (is_n) { if (use_nullify_skip(ctx)) { copy_iaoq_entry(ctx, cpu_iaoq_f, -1, next); - tcg_gen_addi_reg(next, next, 4); + tcg_gen_addi_i64(next, next, 4); copy_iaoq_entry(ctx, cpu_iaoq_b, -1, next); nullify_set(ctx, 0); ctx->base.is_jmp = DISAS_IAQ_N_UPDATED; @@ -1888,7 +1790,7 @@ static bool do_ibranch(DisasContext *ctx, TCGv_reg dest, can simply store DEST optimistically. Similarly with IAOQ_B. */ copy_iaoq_entry(ctx, cpu_iaoq_f, -1, dest); next = tcg_temp_new(); - tcg_gen_addi_reg(next, dest, 4); + tcg_gen_addi_i64(next, dest, 4); copy_iaoq_entry(ctx, cpu_iaoq_b, -1, next); nullify_over(ctx); @@ -1906,19 +1808,19 @@ static bool do_ibranch(DisasContext *ctx, TCGv_reg dest, next = tcg_temp_new(); copy_iaoq_entry(ctx, tmp, ctx->iaoq_n, ctx->iaoq_n_var); - tcg_gen_movcond_reg(c, next, a0, a1, tmp, dest); + tcg_gen_movcond_i64(c, next, a0, a1, tmp, dest); ctx->iaoq_n = -1; ctx->iaoq_n_var = next; if (link != 0) { - tcg_gen_movcond_reg(c, cpu_gr[link], a0, a1, cpu_gr[link], tmp); + tcg_gen_movcond_i64(c, cpu_gr[link], a0, a1, cpu_gr[link], tmp); } if (is_n) { /* The branch nullifies the next insn, which means the state of N after the branch is the inverse of the state of N that applied to the branch. */ - tcg_gen_setcond_reg(tcg_invert_cond(c), cpu_psw_n, a0, a1); + tcg_gen_setcond_i64(tcg_invert_cond(c), cpu_psw_n, a0, a1); cond_free(&ctx->null_cond); ctx->null_cond = cond_make_n(); ctx->psw_n_nonzero = true; @@ -1936,9 +1838,9 @@ static bool do_ibranch(DisasContext *ctx, TCGv_reg dest, * IAOQ_Next{30..31} ← IAOQ_Front{30..31}; * which keeps the privilege level from being increased. */ -static TCGv_reg do_ibranch_priv(DisasContext *ctx, TCGv_reg offset) +static TCGv_i64 do_ibranch_priv(DisasContext *ctx, TCGv_i64 offset) { - TCGv_reg dest; + TCGv_i64 dest; switch (ctx->privilege) { case 0: /* Privilege 0 is maximum and is allowed to decrease. */ @@ -1946,13 +1848,13 @@ static TCGv_reg do_ibranch_priv(DisasContext *ctx, TCGv_reg offset) case 3: /* Privilege 3 is minimum and is never allowed to increase. */ dest = tcg_temp_new(); - tcg_gen_ori_reg(dest, offset, 3); + tcg_gen_ori_i64(dest, offset, 3); break; default: dest = tcg_temp_new(); - tcg_gen_andi_reg(dest, offset, -4); - tcg_gen_ori_reg(dest, dest, ctx->privilege); - tcg_gen_movcond_reg(TCG_COND_GTU, dest, dest, offset, dest, offset); + tcg_gen_andi_i64(dest, offset, -4); + tcg_gen_ori_i64(dest, dest, ctx->privilege); + tcg_gen_movcond_i64(TCG_COND_GTU, dest, dest, offset, dest, offset); break; } return dest; @@ -1968,7 +1870,7 @@ static TCGv_reg do_ibranch_priv(DisasContext *ctx, TCGv_reg offset) aforementioned BE. */ static void do_page_zero(DisasContext *ctx) { - TCGv_reg tmp; + TCGv_i64 tmp; /* If by some means we get here with PSW[N]=1, that implies that the B,GATE instruction would be skipped, and we'd fault on the @@ -1977,7 +1879,7 @@ static void do_page_zero(DisasContext *ctx) case TCG_COND_NEVER: break; case TCG_COND_ALWAYS: - tcg_gen_movi_reg(cpu_psw_n, 0); + tcg_gen_movi_i64(cpu_psw_n, 0); goto do_sigill; default: /* Since this is always the first (and only) insn within the @@ -2005,11 +1907,11 @@ static void do_page_zero(DisasContext *ctx) break; case 0xe0: /* SET_THREAD_POINTER */ - tcg_gen_st_reg(cpu_gr[26], tcg_env, offsetof(CPUHPPAState, cr[27])); + tcg_gen_st_i64(cpu_gr[26], tcg_env, offsetof(CPUHPPAState, cr[27])); tmp = tcg_temp_new(); - tcg_gen_ori_reg(tmp, cpu_gr[31], 3); + tcg_gen_ori_i64(tmp, cpu_gr[31], 3); copy_iaoq_entry(ctx, cpu_iaoq_f, -1, tmp); - tcg_gen_addi_reg(tmp, tmp, 4); + tcg_gen_addi_i64(tmp, tmp, 4); copy_iaoq_entry(ctx, cpu_iaoq_b, -1, tmp); ctx->base.is_jmp = DISAS_IAQ_N_UPDATED; break; @@ -2051,8 +1953,8 @@ static bool trans_sync(DisasContext *ctx, arg_sync *a) static bool trans_mfia(DisasContext *ctx, arg_mfia *a) { unsigned rt = a->t; - TCGv_reg tmp = dest_gpr(ctx, rt); - tcg_gen_movi_reg(tmp, ctx->iaoq_f); + TCGv_i64 tmp = dest_gpr(ctx, rt); + tcg_gen_movi_i64(tmp, ctx->iaoq_f); save_gpr(ctx, rt, tmp); cond_free(&ctx->null_cond); @@ -2064,7 +1966,7 @@ static bool trans_mfsp(DisasContext *ctx, arg_mfsp *a) unsigned rt = a->t; unsigned rs = a->sp; TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_reg t1 = tcg_temp_new(); + TCGv_i64 t1 = tcg_temp_new(); load_spr(ctx, t0, rs); tcg_gen_shri_i64(t0, t0, 32); @@ -2080,14 +1982,14 @@ static bool trans_mfctl(DisasContext *ctx, arg_mfctl *a) { unsigned rt = a->t; unsigned ctl = a->r; - TCGv_reg tmp; + TCGv_i64 tmp; switch (ctl) { case CR_SAR: if (a->e == 0) { /* MFSAR without ,W masks low 5 bits. */ tmp = dest_gpr(ctx, rt); - tcg_gen_andi_reg(tmp, cpu_sar, 31); + tcg_gen_andi_i64(tmp, cpu_sar, 31); save_gpr(ctx, rt, tmp); goto done; } @@ -2115,7 +2017,7 @@ static bool trans_mfctl(DisasContext *ctx, arg_mfctl *a) } tmp = tcg_temp_new(); - tcg_gen_ld_reg(tmp, tcg_env, offsetof(CPUHPPAState, cr[ctl])); + tcg_gen_ld_i64(tmp, tcg_env, offsetof(CPUHPPAState, cr[ctl])); save_gpr(ctx, rt, tmp); done: @@ -2151,13 +2053,13 @@ static bool trans_mtsp(DisasContext *ctx, arg_mtsp *a) static bool trans_mtctl(DisasContext *ctx, arg_mtctl *a) { unsigned ctl = a->t; - TCGv_reg reg; - TCGv_reg tmp; + TCGv_i64 reg; + TCGv_i64 tmp; if (ctl == CR_SAR) { reg = load_gpr(ctx, a->r); tmp = tcg_temp_new(); - tcg_gen_andi_reg(tmp, reg, ctx->is_pa20 ? 63 : 31); + tcg_gen_andi_i64(tmp, reg, ctx->is_pa20 ? 63 : 31); save_or_nullify(ctx, cpu_sar, tmp); cond_free(&ctx->null_cond); @@ -2188,10 +2090,10 @@ static bool trans_mtctl(DisasContext *ctx, arg_mtctl *a) /* FIXME: Respect PSW_Q bit */ /* The write advances the queue and stores to the back element. */ tmp = tcg_temp_new(); - tcg_gen_ld_reg(tmp, tcg_env, + tcg_gen_ld_i64(tmp, tcg_env, offsetof(CPUHPPAState, cr_back[ctl - CR_IIASQ])); - tcg_gen_st_reg(tmp, tcg_env, offsetof(CPUHPPAState, cr[ctl])); - tcg_gen_st_reg(reg, tcg_env, + tcg_gen_st_i64(tmp, tcg_env, offsetof(CPUHPPAState, cr[ctl])); + tcg_gen_st_i64(reg, tcg_env, offsetof(CPUHPPAState, cr_back[ctl - CR_IIASQ])); break; @@ -2199,14 +2101,14 @@ static bool trans_mtctl(DisasContext *ctx, arg_mtctl *a) case CR_PID2: case CR_PID3: case CR_PID4: - tcg_gen_st_reg(reg, tcg_env, offsetof(CPUHPPAState, cr[ctl])); + tcg_gen_st_i64(reg, tcg_env, offsetof(CPUHPPAState, cr[ctl])); #ifndef CONFIG_USER_ONLY gen_helper_change_prot_id(tcg_env); #endif break; default: - tcg_gen_st_reg(reg, tcg_env, offsetof(CPUHPPAState, cr[ctl])); + tcg_gen_st_i64(reg, tcg_env, offsetof(CPUHPPAState, cr[ctl])); break; } return nullify_end(ctx); @@ -2215,10 +2117,10 @@ static bool trans_mtctl(DisasContext *ctx, arg_mtctl *a) static bool trans_mtsarcm(DisasContext *ctx, arg_mtsarcm *a) { - TCGv_reg tmp = tcg_temp_new(); + TCGv_i64 tmp = tcg_temp_new(); - tcg_gen_not_reg(tmp, load_gpr(ctx, a->r)); - tcg_gen_andi_reg(tmp, tmp, ctx->is_pa20 ? 63 : 31); + tcg_gen_not_i64(tmp, load_gpr(ctx, a->r)); + tcg_gen_andi_i64(tmp, tmp, ctx->is_pa20 ? 63 : 31); save_or_nullify(ctx, cpu_sar, tmp); cond_free(&ctx->null_cond); @@ -2227,11 +2129,11 @@ static bool trans_mtsarcm(DisasContext *ctx, arg_mtsarcm *a) static bool trans_ldsid(DisasContext *ctx, arg_ldsid *a) { - TCGv_reg dest = dest_gpr(ctx, a->t); + TCGv_i64 dest = dest_gpr(ctx, a->t); #ifdef CONFIG_USER_ONLY /* We don't implement space registers in user mode. */ - tcg_gen_movi_reg(dest, 0); + tcg_gen_movi_i64(dest, 0); #else TCGv_i64 t0 = tcg_temp_new_i64(); @@ -2249,13 +2151,13 @@ static bool trans_rsm(DisasContext *ctx, arg_rsm *a) { CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); #ifndef CONFIG_USER_ONLY - TCGv_reg tmp; + TCGv_i64 tmp; nullify_over(ctx); tmp = tcg_temp_new(); - tcg_gen_ld_reg(tmp, tcg_env, offsetof(CPUHPPAState, psw)); - tcg_gen_andi_reg(tmp, tmp, ~a->i); + tcg_gen_ld_i64(tmp, tcg_env, offsetof(CPUHPPAState, psw)); + tcg_gen_andi_i64(tmp, tmp, ~a->i); gen_helper_swap_system_mask(tmp, tcg_env, tmp); save_gpr(ctx, a->t, tmp); @@ -2269,13 +2171,13 @@ static bool trans_ssm(DisasContext *ctx, arg_ssm *a) { CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); #ifndef CONFIG_USER_ONLY - TCGv_reg tmp; + TCGv_i64 tmp; nullify_over(ctx); tmp = tcg_temp_new(); - tcg_gen_ld_reg(tmp, tcg_env, offsetof(CPUHPPAState, psw)); - tcg_gen_ori_reg(tmp, tmp, a->i); + tcg_gen_ld_i64(tmp, tcg_env, offsetof(CPUHPPAState, psw)); + tcg_gen_ori_i64(tmp, tmp, a->i); gen_helper_swap_system_mask(tmp, tcg_env, tmp); save_gpr(ctx, a->t, tmp); @@ -2289,7 +2191,7 @@ static bool trans_mtsm(DisasContext *ctx, arg_mtsm *a) { CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); #ifndef CONFIG_USER_ONLY - TCGv_reg tmp, reg; + TCGv_i64 tmp, reg; nullify_over(ctx); reg = load_gpr(ctx, a->r); @@ -2366,12 +2268,12 @@ static bool trans_getshadowregs(DisasContext *ctx, arg_getshadowregs *a) static bool trans_nop_addrx(DisasContext *ctx, arg_ldst *a) { if (a->m) { - TCGv_reg dest = dest_gpr(ctx, a->b); - TCGv_reg src1 = load_gpr(ctx, a->b); - TCGv_reg src2 = load_gpr(ctx, a->x); + TCGv_i64 dest = dest_gpr(ctx, a->b); + TCGv_i64 src1 = load_gpr(ctx, a->b); + TCGv_i64 src2 = load_gpr(ctx, a->x); /* The only thing we need to do is the base register modification. */ - tcg_gen_add_reg(dest, src1, src2); + tcg_gen_add_i64(dest, src1, src2); save_gpr(ctx, a->b, dest); } cond_free(&ctx->null_cond); @@ -2380,9 +2282,9 @@ static bool trans_nop_addrx(DisasContext *ctx, arg_ldst *a) static bool trans_probe(DisasContext *ctx, arg_probe *a) { - TCGv_reg dest, ofs; + TCGv_i64 dest, ofs; TCGv_i32 level, want; - TCGv_tl addr; + TCGv_i64 addr; nullify_over(ctx); @@ -2393,7 +2295,7 @@ static bool trans_probe(DisasContext *ctx, arg_probe *a) level = tcg_constant_i32(a->ri); } else { level = tcg_temp_new_i32(); - tcg_gen_trunc_reg_i32(level, load_gpr(ctx, a->ri)); + tcg_gen_extrl_i64_i32(level, load_gpr(ctx, a->ri)); tcg_gen_andi_i32(level, level, 3); } want = tcg_constant_i32(a->write ? PAGE_WRITE : PAGE_READ); @@ -2411,8 +2313,8 @@ static bool trans_ixtlbx(DisasContext *ctx, arg_ixtlbx *a) } CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); #ifndef CONFIG_USER_ONLY - TCGv_tl addr; - TCGv_reg ofs, reg; + TCGv_i64 addr; + TCGv_i64 ofs, reg; nullify_over(ctx); @@ -2436,8 +2338,8 @@ static bool trans_pxtlbx(DisasContext *ctx, arg_pxtlbx *a) { CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); #ifndef CONFIG_USER_ONLY - TCGv_tl addr; - TCGv_reg ofs; + TCGv_i64 addr; + TCGv_i64 ofs; nullify_over(ctx); @@ -2472,8 +2374,8 @@ static bool trans_ixtlbxf(DisasContext *ctx, arg_ixtlbxf *a) } CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); #ifndef CONFIG_USER_ONLY - TCGv_tl addr, atl, stl; - TCGv_reg reg; + TCGv_i64 addr, atl, stl; + TCGv_i64 reg; nullify_over(ctx); @@ -2483,9 +2385,9 @@ static bool trans_ixtlbxf(DisasContext *ctx, arg_ixtlbxf *a) * return gen_illegal(ctx); */ - atl = tcg_temp_new_tl(); - stl = tcg_temp_new_tl(); - addr = tcg_temp_new_tl(); + atl = tcg_temp_new_i64(); + stl = tcg_temp_new_i64(); + addr = tcg_temp_new_i64(); tcg_gen_ld32u_i64(stl, tcg_env, a->data ? offsetof(CPUHPPAState, cr[CR_ISR]) @@ -2541,8 +2443,8 @@ static bool trans_lpa(DisasContext *ctx, arg_ldst *a) { CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); #ifndef CONFIG_USER_ONLY - TCGv_tl vaddr; - TCGv_reg ofs, paddr; + TCGv_i64 vaddr; + TCGv_i64 ofs, paddr; nullify_over(ctx); @@ -2569,7 +2471,7 @@ static bool trans_lci(DisasContext *ctx, arg_lci *a) physical address. Two addresses with the same CI have a coherent view of the cache. Our implementation is to return 0 for all, since the entire address space is coherent. */ - save_gpr(ctx, a->t, tcg_constant_reg(0)); + save_gpr(ctx, a->t, tcg_constant_i64(0)); cond_free(&ctx->null_cond); return true; @@ -2632,12 +2534,12 @@ static bool trans_sub_b_tsv(DisasContext *ctx, arg_rrr_cf_d *a) static bool trans_andcm(DisasContext *ctx, arg_rrr_cf_d *a) { - return do_log_reg(ctx, a, tcg_gen_andc_reg); + return do_log_reg(ctx, a, tcg_gen_andc_i64); } static bool trans_and(DisasContext *ctx, arg_rrr_cf_d *a) { - return do_log_reg(ctx, a, tcg_gen_and_reg); + return do_log_reg(ctx, a, tcg_gen_and_i64); } static bool trans_or(DisasContext *ctx, arg_rrr_cf_d *a) @@ -2653,8 +2555,8 @@ static bool trans_or(DisasContext *ctx, arg_rrr_cf_d *a) } if (r2 == 0) { /* COPY */ if (r1 == 0) { - TCGv_reg dest = dest_gpr(ctx, rt); - tcg_gen_movi_reg(dest, 0); + TCGv_i64 dest = dest_gpr(ctx, rt); + tcg_gen_movi_i64(dest, 0); save_gpr(ctx, rt, dest); } else { save_gpr(ctx, rt, cpu_gr[r1]); @@ -2689,17 +2591,17 @@ static bool trans_or(DisasContext *ctx, arg_rrr_cf_d *a) } #endif } - return do_log_reg(ctx, a, tcg_gen_or_reg); + return do_log_reg(ctx, a, tcg_gen_or_i64); } static bool trans_xor(DisasContext *ctx, arg_rrr_cf_d *a) { - return do_log_reg(ctx, a, tcg_gen_xor_reg); + return do_log_reg(ctx, a, tcg_gen_xor_i64); } static bool trans_cmpclr(DisasContext *ctx, arg_rrr_cf_d *a) { - TCGv_reg tcg_r1, tcg_r2; + TCGv_i64 tcg_r1, tcg_r2; if (a->cf) { nullify_over(ctx); @@ -2712,20 +2614,20 @@ static bool trans_cmpclr(DisasContext *ctx, arg_rrr_cf_d *a) static bool trans_uxor(DisasContext *ctx, arg_rrr_cf_d *a) { - TCGv_reg tcg_r1, tcg_r2; + TCGv_i64 tcg_r1, tcg_r2; if (a->cf) { nullify_over(ctx); } tcg_r1 = load_gpr(ctx, a->r1); tcg_r2 = load_gpr(ctx, a->r2); - do_unit(ctx, a->t, tcg_r1, tcg_r2, a->cf, a->d, false, tcg_gen_xor_reg); + do_unit(ctx, a->t, tcg_r1, tcg_r2, a->cf, a->d, false, tcg_gen_xor_i64); return nullify_end(ctx); } static bool do_uaddcm(DisasContext *ctx, arg_rrr_cf_d *a, bool is_tc) { - TCGv_reg tcg_r1, tcg_r2, tmp; + TCGv_i64 tcg_r1, tcg_r2, tmp; if (a->cf) { nullify_over(ctx); @@ -2733,8 +2635,8 @@ static bool do_uaddcm(DisasContext *ctx, arg_rrr_cf_d *a, bool is_tc) tcg_r1 = load_gpr(ctx, a->r1); tcg_r2 = load_gpr(ctx, a->r2); tmp = tcg_temp_new(); - tcg_gen_not_reg(tmp, tcg_r2); - do_unit(ctx, a->t, tcg_r1, tmp, a->cf, a->d, is_tc, tcg_gen_add_reg); + tcg_gen_not_i64(tmp, tcg_r2); + do_unit(ctx, a->t, tcg_r1, tmp, a->cf, a->d, is_tc, tcg_gen_add_i64); return nullify_end(ctx); } @@ -2750,19 +2652,19 @@ static bool trans_uaddcm_tc(DisasContext *ctx, arg_rrr_cf_d *a) static bool do_dcor(DisasContext *ctx, arg_rr_cf_d *a, bool is_i) { - TCGv_reg tmp; + TCGv_i64 tmp; nullify_over(ctx); tmp = tcg_temp_new(); - tcg_gen_shri_reg(tmp, cpu_psw_cb, 3); + tcg_gen_shri_i64(tmp, cpu_psw_cb, 3); if (!is_i) { - tcg_gen_not_reg(tmp, tmp); + tcg_gen_not_i64(tmp, tmp); } - tcg_gen_andi_reg(tmp, tmp, (uint64_t)0x1111111111111111ull); - tcg_gen_muli_reg(tmp, tmp, 6); + tcg_gen_andi_i64(tmp, tmp, (uint64_t)0x1111111111111111ull); + tcg_gen_muli_i64(tmp, tmp, 6); do_unit(ctx, a->t, load_gpr(ctx, a->r), tmp, a->cf, a->d, false, - is_i ? tcg_gen_add_reg : tcg_gen_sub_reg); + is_i ? tcg_gen_add_i64 : tcg_gen_sub_i64); return nullify_end(ctx); } @@ -2778,8 +2680,8 @@ static bool trans_dcor_i(DisasContext *ctx, arg_rr_cf_d *a) static bool trans_ds(DisasContext *ctx, arg_rrr_cf *a) { - TCGv_reg dest, add1, add2, addc, zero, in1, in2; - TCGv_reg cout; + TCGv_i64 dest, add1, add2, addc, zero, in1, in2; + TCGv_i64 cout; nullify_over(ctx); @@ -2790,11 +2692,11 @@ static bool trans_ds(DisasContext *ctx, arg_rrr_cf *a) add2 = tcg_temp_new(); addc = tcg_temp_new(); dest = tcg_temp_new(); - zero = tcg_constant_reg(0); + zero = tcg_constant_i64(0); /* Form R1 << 1 | PSW[CB]{8}. */ - tcg_gen_add_reg(add1, in1, in1); - tcg_gen_add_reg(add1, add1, get_psw_carry(ctx, false)); + tcg_gen_add_i64(add1, in1, in1); + tcg_gen_add_i64(add1, add1, get_psw_carry(ctx, false)); /* * Add or subtract R2, depending on PSW[V]. Proper computation of @@ -2802,28 +2704,28 @@ static bool trans_ds(DisasContext *ctx, arg_rrr_cf *a) * the manual. By extracting and masking V, we can produce the * proper inputs to the addition without movcond. */ - tcg_gen_sextract_reg(addc, cpu_psw_v, 31, 1); - tcg_gen_xor_reg(add2, in2, addc); - tcg_gen_andi_reg(addc, addc, 1); + tcg_gen_sextract_i64(addc, cpu_psw_v, 31, 1); + tcg_gen_xor_i64(add2, in2, addc); + tcg_gen_andi_i64(addc, addc, 1); - tcg_gen_add2_reg(dest, cpu_psw_cb_msb, add1, zero, add2, zero); - tcg_gen_add2_reg(dest, cpu_psw_cb_msb, dest, cpu_psw_cb_msb, addc, zero); + tcg_gen_add2_i64(dest, cpu_psw_cb_msb, add1, zero, add2, zero); + tcg_gen_add2_i64(dest, cpu_psw_cb_msb, dest, cpu_psw_cb_msb, addc, zero); /* Write back the result register. */ save_gpr(ctx, a->t, dest); /* Write back PSW[CB]. */ - tcg_gen_xor_reg(cpu_psw_cb, add1, add2); - tcg_gen_xor_reg(cpu_psw_cb, cpu_psw_cb, dest); + tcg_gen_xor_i64(cpu_psw_cb, add1, add2); + tcg_gen_xor_i64(cpu_psw_cb, cpu_psw_cb, dest); /* Write back PSW[V] for the division step. */ cout = get_psw_carry(ctx, false); - tcg_gen_neg_reg(cpu_psw_v, cout); - tcg_gen_xor_reg(cpu_psw_v, cpu_psw_v, in2); + tcg_gen_neg_i64(cpu_psw_v, cout); + tcg_gen_xor_i64(cpu_psw_v, cpu_psw_v, in2); /* Install the new nullification. */ if (a->cf) { - TCGv_reg sv = NULL; + TCGv_i64 sv = NULL; if (cond_need_sv(a->cf >> 1)) { /* ??? The lshift is supposed to contribute to overflow. */ sv = do_add_sv(ctx, dest, add1, add2); @@ -2866,13 +2768,13 @@ static bool trans_subi_tsv(DisasContext *ctx, arg_rri_cf *a) static bool trans_cmpiclr(DisasContext *ctx, arg_rri_cf_d *a) { - TCGv_reg tcg_im, tcg_r2; + TCGv_i64 tcg_im, tcg_r2; if (a->cf) { nullify_over(ctx); } - tcg_im = tcg_constant_reg(a->i); + tcg_im = tcg_constant_i64(a->i); tcg_r2 = load_gpr(ctx, a->r); do_cmpclr(ctx, a->t, tcg_im, tcg_r2, a->cf, a->d); @@ -2900,8 +2802,8 @@ static bool trans_st(DisasContext *ctx, arg_ldst *a) static bool trans_ldc(DisasContext *ctx, arg_ldst *a) { MemOp mop = MO_TE | MO_ALIGN | a->size; - TCGv_reg zero, dest, ofs; - TCGv_tl addr; + TCGv_i64 zero, dest, ofs; + TCGv_i64 addr; if (!ctx->is_pa20 && a->size > MO_32) { return gen_illegal(ctx); @@ -2930,8 +2832,8 @@ static bool trans_ldc(DisasContext *ctx, arg_ldst *a) */ gen_helper_ldc_check(addr); - zero = tcg_constant_reg(0); - tcg_gen_atomic_xchg_reg(dest, addr, zero, ctx->mmu_idx, mop); + zero = tcg_constant_i64(0); + tcg_gen_atomic_xchg_i64(dest, addr, zero, ctx->mmu_idx, mop); if (a->m) { save_gpr(ctx, a->b, ofs); @@ -2943,8 +2845,8 @@ static bool trans_ldc(DisasContext *ctx, arg_ldst *a) static bool trans_stby(DisasContext *ctx, arg_stby *a) { - TCGv_reg ofs, val; - TCGv_tl addr; + TCGv_i64 ofs, val; + TCGv_i64 addr; nullify_over(ctx); @@ -2965,7 +2867,7 @@ static bool trans_stby(DisasContext *ctx, arg_stby *a) } } if (a->m) { - tcg_gen_andi_reg(ofs, ofs, ~3); + tcg_gen_andi_i64(ofs, ofs, ~3); save_gpr(ctx, a->b, ofs); } @@ -2974,8 +2876,8 @@ static bool trans_stby(DisasContext *ctx, arg_stby *a) static bool trans_stdby(DisasContext *ctx, arg_stby *a) { - TCGv_reg ofs, val; - TCGv_tl addr; + TCGv_i64 ofs, val; + TCGv_i64 addr; if (!ctx->is_pa20) { return false; @@ -2999,7 +2901,7 @@ static bool trans_stdby(DisasContext *ctx, arg_stby *a) } } if (a->m) { - tcg_gen_andi_reg(ofs, ofs, ~7); + tcg_gen_andi_i64(ofs, ofs, ~7); save_gpr(ctx, a->b, ofs); } @@ -3030,9 +2932,9 @@ static bool trans_sta(DisasContext *ctx, arg_ldst *a) static bool trans_ldil(DisasContext *ctx, arg_ldil *a) { - TCGv_reg tcg_rt = dest_gpr(ctx, a->t); + TCGv_i64 tcg_rt = dest_gpr(ctx, a->t); - tcg_gen_movi_reg(tcg_rt, a->i); + tcg_gen_movi_i64(tcg_rt, a->i); save_gpr(ctx, a->t, tcg_rt); cond_free(&ctx->null_cond); return true; @@ -3040,10 +2942,10 @@ static bool trans_ldil(DisasContext *ctx, arg_ldil *a) static bool trans_addil(DisasContext *ctx, arg_addil *a) { - TCGv_reg tcg_rt = load_gpr(ctx, a->r); - TCGv_reg tcg_r1 = dest_gpr(ctx, 1); + TCGv_i64 tcg_rt = load_gpr(ctx, a->r); + TCGv_i64 tcg_r1 = dest_gpr(ctx, 1); - tcg_gen_addi_reg(tcg_r1, tcg_rt, a->i); + tcg_gen_addi_i64(tcg_r1, tcg_rt, a->i); save_gpr(ctx, 1, tcg_r1); cond_free(&ctx->null_cond); return true; @@ -3051,30 +2953,30 @@ static bool trans_addil(DisasContext *ctx, arg_addil *a) static bool trans_ldo(DisasContext *ctx, arg_ldo *a) { - TCGv_reg tcg_rt = dest_gpr(ctx, a->t); + TCGv_i64 tcg_rt = dest_gpr(ctx, a->t); /* Special case rb == 0, for the LDI pseudo-op. The COPY pseudo-op is handled for free within tcg_gen_addi_tl. */ if (a->b == 0) { - tcg_gen_movi_reg(tcg_rt, a->i); + tcg_gen_movi_i64(tcg_rt, a->i); } else { - tcg_gen_addi_reg(tcg_rt, cpu_gr[a->b], a->i); + tcg_gen_addi_i64(tcg_rt, cpu_gr[a->b], a->i); } save_gpr(ctx, a->t, tcg_rt); cond_free(&ctx->null_cond); return true; } -static bool do_cmpb(DisasContext *ctx, unsigned r, TCGv_reg in1, +static bool do_cmpb(DisasContext *ctx, unsigned r, TCGv_i64 in1, unsigned c, unsigned f, bool d, unsigned n, int disp) { - TCGv_reg dest, in2, sv; + TCGv_i64 dest, in2, sv; DisasCond cond; in2 = load_gpr(ctx, r); dest = tcg_temp_new(); - tcg_gen_sub_reg(dest, in1, in2); + tcg_gen_sub_i64(dest, in1, in2); sv = NULL; if (cond_need_sv(c)) { @@ -3101,14 +3003,14 @@ static bool trans_cmpbi(DisasContext *ctx, arg_cmpbi *a) return false; } nullify_over(ctx); - return do_cmpb(ctx, a->r, tcg_constant_reg(a->i), + return do_cmpb(ctx, a->r, tcg_constant_i64(a->i), a->c, a->f, a->d, a->n, a->disp); } -static bool do_addb(DisasContext *ctx, unsigned r, TCGv_reg in1, +static bool do_addb(DisasContext *ctx, unsigned r, TCGv_i64 in1, unsigned c, unsigned f, unsigned n, int disp) { - TCGv_reg dest, in2, sv, cb_cond; + TCGv_i64 dest, in2, sv, cb_cond; DisasCond cond; bool d = false; @@ -3129,16 +3031,16 @@ static bool do_addb(DisasContext *ctx, unsigned r, TCGv_reg in1, cb_cond = NULL; if (cond_need_cb(c)) { - TCGv_reg cb = tcg_temp_new(); - TCGv_reg cb_msb = tcg_temp_new(); + TCGv_i64 cb = tcg_temp_new(); + TCGv_i64 cb_msb = tcg_temp_new(); - tcg_gen_movi_reg(cb_msb, 0); - tcg_gen_add2_reg(dest, cb_msb, in1, cb_msb, in2, cb_msb); - tcg_gen_xor_reg(cb, in1, in2); - tcg_gen_xor_reg(cb, cb, dest); + tcg_gen_movi_i64(cb_msb, 0); + tcg_gen_add2_i64(dest, cb_msb, in1, cb_msb, in2, cb_msb); + tcg_gen_xor_i64(cb, in1, in2); + tcg_gen_xor_i64(cb, cb, dest); cb_cond = get_carry(ctx, d, cb, cb_msb); } else { - tcg_gen_add_reg(dest, in1, in2); + tcg_gen_add_i64(dest, in1, in2); } if (cond_need_sv(c)) { sv = do_add_sv(ctx, dest, in1, in2); @@ -3158,12 +3060,12 @@ static bool trans_addb(DisasContext *ctx, arg_addb *a) static bool trans_addbi(DisasContext *ctx, arg_addbi *a) { nullify_over(ctx); - return do_addb(ctx, a->r, tcg_constant_reg(a->i), a->c, a->f, a->n, a->disp); + return do_addb(ctx, a->r, tcg_constant_i64(a->i), a->c, a->f, a->n, a->disp); } static bool trans_bb_sar(DisasContext *ctx, arg_bb_sar *a) { - TCGv_reg tmp, tcg_r; + TCGv_i64 tmp, tcg_r; DisasCond cond; nullify_over(ctx); @@ -3172,10 +3074,10 @@ static bool trans_bb_sar(DisasContext *ctx, arg_bb_sar *a) tcg_r = load_gpr(ctx, a->r); if (cond_need_ext(ctx, a->d)) { /* Force shift into [32,63] */ - tcg_gen_ori_reg(tmp, cpu_sar, 32); - tcg_gen_shl_reg(tmp, tcg_r, tmp); + tcg_gen_ori_i64(tmp, cpu_sar, 32); + tcg_gen_shl_i64(tmp, tcg_r, tmp); } else { - tcg_gen_shl_reg(tmp, tcg_r, cpu_sar); + tcg_gen_shl_i64(tmp, tcg_r, cpu_sar); } cond = cond_make_0_tmp(a->c ? TCG_COND_GE : TCG_COND_LT, tmp); @@ -3184,7 +3086,7 @@ static bool trans_bb_sar(DisasContext *ctx, arg_bb_sar *a) static bool trans_bb_imm(DisasContext *ctx, arg_bb_imm *a) { - TCGv_reg tmp, tcg_r; + TCGv_i64 tmp, tcg_r; DisasCond cond; int p; @@ -3193,7 +3095,7 @@ static bool trans_bb_imm(DisasContext *ctx, arg_bb_imm *a) tmp = tcg_temp_new(); tcg_r = load_gpr(ctx, a->r); p = a->p | (cond_need_ext(ctx, a->d) ? 32 : 0); - tcg_gen_shli_reg(tmp, tcg_r, p); + tcg_gen_shli_i64(tmp, tcg_r, p); cond = cond_make_0(a->c ? TCG_COND_GE : TCG_COND_LT, tmp); return do_cbranch(ctx, a->disp, a->n, &cond); @@ -3201,16 +3103,16 @@ static bool trans_bb_imm(DisasContext *ctx, arg_bb_imm *a) static bool trans_movb(DisasContext *ctx, arg_movb *a) { - TCGv_reg dest; + TCGv_i64 dest; DisasCond cond; nullify_over(ctx); dest = dest_gpr(ctx, a->r2); if (a->r1 == 0) { - tcg_gen_movi_reg(dest, 0); + tcg_gen_movi_i64(dest, 0); } else { - tcg_gen_mov_reg(dest, cpu_gr[a->r1]); + tcg_gen_mov_i64(dest, cpu_gr[a->r1]); } /* All MOVB conditions are 32-bit. */ @@ -3220,13 +3122,13 @@ static bool trans_movb(DisasContext *ctx, arg_movb *a) static bool trans_movbi(DisasContext *ctx, arg_movbi *a) { - TCGv_reg dest; + TCGv_i64 dest; DisasCond cond; nullify_over(ctx); dest = dest_gpr(ctx, a->r); - tcg_gen_movi_reg(dest, a->i); + tcg_gen_movi_i64(dest, a->i); /* All MOVBI conditions are 32-bit. */ cond = do_sed_cond(ctx, a->c, false, dest); @@ -3235,7 +3137,7 @@ static bool trans_movbi(DisasContext *ctx, arg_movbi *a) static bool trans_shrp_sar(DisasContext *ctx, arg_shrp_sar *a) { - TCGv_reg dest, src2; + TCGv_i64 dest, src2; if (!ctx->is_pa20 && a->d) { return false; @@ -3248,44 +3150,44 @@ static bool trans_shrp_sar(DisasContext *ctx, arg_shrp_sar *a) src2 = load_gpr(ctx, a->r2); if (a->r1 == 0) { if (a->d) { - tcg_gen_shr_reg(dest, src2, cpu_sar); + tcg_gen_shr_i64(dest, src2, cpu_sar); } else { - TCGv_reg tmp = tcg_temp_new(); + TCGv_i64 tmp = tcg_temp_new(); - tcg_gen_ext32u_reg(dest, src2); - tcg_gen_andi_reg(tmp, cpu_sar, 31); - tcg_gen_shr_reg(dest, dest, tmp); + tcg_gen_ext32u_i64(dest, src2); + tcg_gen_andi_i64(tmp, cpu_sar, 31); + tcg_gen_shr_i64(dest, dest, tmp); } } else if (a->r1 == a->r2) { if (a->d) { - tcg_gen_rotr_reg(dest, src2, cpu_sar); + tcg_gen_rotr_i64(dest, src2, cpu_sar); } else { TCGv_i32 t32 = tcg_temp_new_i32(); TCGv_i32 s32 = tcg_temp_new_i32(); - tcg_gen_trunc_reg_i32(t32, src2); - tcg_gen_trunc_reg_i32(s32, cpu_sar); + tcg_gen_extrl_i64_i32(t32, src2); + tcg_gen_extrl_i64_i32(s32, cpu_sar); tcg_gen_andi_i32(s32, s32, 31); tcg_gen_rotr_i32(t32, t32, s32); - tcg_gen_extu_i32_reg(dest, t32); + tcg_gen_extu_i32_i64(dest, t32); } } else { - TCGv_reg src1 = load_gpr(ctx, a->r1); + TCGv_i64 src1 = load_gpr(ctx, a->r1); if (a->d) { - TCGv_reg t = tcg_temp_new(); - TCGv_reg n = tcg_temp_new(); + TCGv_i64 t = tcg_temp_new(); + TCGv_i64 n = tcg_temp_new(); - tcg_gen_xori_reg(n, cpu_sar, 63); - tcg_gen_shl_reg(t, src2, n); - tcg_gen_shli_reg(t, t, 1); - tcg_gen_shr_reg(dest, src1, cpu_sar); - tcg_gen_or_reg(dest, dest, t); + tcg_gen_xori_i64(n, cpu_sar, 63); + tcg_gen_shl_i64(t, src2, n); + tcg_gen_shli_i64(t, t, 1); + tcg_gen_shr_i64(dest, src1, cpu_sar); + tcg_gen_or_i64(dest, dest, t); } else { TCGv_i64 t = tcg_temp_new_i64(); TCGv_i64 s = tcg_temp_new_i64(); - tcg_gen_concat_reg_i64(t, src2, src1); + tcg_gen_concat32_i64(t, src2, src1); tcg_gen_extu_reg_i64(s, cpu_sar); tcg_gen_andi_i64(s, s, 31); tcg_gen_shr_i64(t, t, s); @@ -3305,7 +3207,7 @@ static bool trans_shrp_sar(DisasContext *ctx, arg_shrp_sar *a) static bool trans_shrp_imm(DisasContext *ctx, arg_shrp_imm *a) { unsigned width, sa; - TCGv_reg dest, t2; + TCGv_i64 dest, t2; if (!ctx->is_pa20 && a->d) { return false; @@ -3320,19 +3222,19 @@ static bool trans_shrp_imm(DisasContext *ctx, arg_shrp_imm *a) dest = dest_gpr(ctx, a->t); t2 = load_gpr(ctx, a->r2); if (a->r1 == 0) { - tcg_gen_extract_reg(dest, t2, sa, width - sa); + tcg_gen_extract_i64(dest, t2, sa, width - sa); } else if (width == TARGET_LONG_BITS) { - tcg_gen_extract2_reg(dest, t2, cpu_gr[a->r1], sa); + tcg_gen_extract2_i64(dest, t2, cpu_gr[a->r1], sa); } else { assert(!a->d); if (a->r1 == a->r2) { TCGv_i32 t32 = tcg_temp_new_i32(); - tcg_gen_trunc_reg_i32(t32, t2); + tcg_gen_extrl_i64_i32(t32, t2); tcg_gen_rotri_i32(t32, t32, sa); - tcg_gen_extu_i32_reg(dest, t32); + tcg_gen_extu_i32_i64(dest, t32); } else { TCGv_i64 t64 = tcg_temp_new_i64(); - tcg_gen_concat_reg_i64(t64, t2, cpu_gr[a->r1]); + tcg_gen_concat32_i64(t64, t2, cpu_gr[a->r1]); tcg_gen_shri_i64(t64, t64, sa); tcg_gen_trunc_i64_reg(dest, t64); } @@ -3350,7 +3252,7 @@ static bool trans_shrp_imm(DisasContext *ctx, arg_shrp_imm *a) static bool trans_extr_sar(DisasContext *ctx, arg_extr_sar *a) { unsigned widthm1 = a->d ? 63 : 31; - TCGv_reg dest, src, tmp; + TCGv_i64 dest, src, tmp; if (!ctx->is_pa20 && a->d) { return false; @@ -3364,23 +3266,23 @@ static bool trans_extr_sar(DisasContext *ctx, arg_extr_sar *a) tmp = tcg_temp_new(); /* Recall that SAR is using big-endian bit numbering. */ - tcg_gen_andi_reg(tmp, cpu_sar, widthm1); - tcg_gen_xori_reg(tmp, tmp, widthm1); + tcg_gen_andi_i64(tmp, cpu_sar, widthm1); + tcg_gen_xori_i64(tmp, tmp, widthm1); if (a->se) { if (!a->d) { - tcg_gen_ext32s_reg(dest, src); + tcg_gen_ext32s_i64(dest, src); src = dest; } - tcg_gen_sar_reg(dest, src, tmp); - tcg_gen_sextract_reg(dest, dest, 0, a->len); + tcg_gen_sar_i64(dest, src, tmp); + tcg_gen_sextract_i64(dest, dest, 0, a->len); } else { if (!a->d) { - tcg_gen_ext32u_reg(dest, src); + tcg_gen_ext32u_i64(dest, src); src = dest; } - tcg_gen_shr_reg(dest, src, tmp); - tcg_gen_extract_reg(dest, dest, 0, a->len); + tcg_gen_shr_i64(dest, src, tmp); + tcg_gen_extract_i64(dest, dest, 0, a->len); } save_gpr(ctx, a->t, dest); @@ -3395,7 +3297,7 @@ static bool trans_extr_sar(DisasContext *ctx, arg_extr_sar *a) static bool trans_extr_imm(DisasContext *ctx, arg_extr_imm *a) { unsigned len, cpos, width; - TCGv_reg dest, src; + TCGv_i64 dest, src; if (!ctx->is_pa20 && a->d) { return false; @@ -3414,9 +3316,9 @@ static bool trans_extr_imm(DisasContext *ctx, arg_extr_imm *a) dest = dest_gpr(ctx, a->t); src = load_gpr(ctx, a->r); if (a->se) { - tcg_gen_sextract_reg(dest, src, cpos, len); + tcg_gen_sextract_i64(dest, src, cpos, len); } else { - tcg_gen_extract_reg(dest, src, cpos, len); + tcg_gen_extract_i64(dest, src, cpos, len); } save_gpr(ctx, a->t, dest); @@ -3432,7 +3334,7 @@ static bool trans_depi_imm(DisasContext *ctx, arg_depi_imm *a) { unsigned len, width; uint64_t mask0, mask1; - TCGv_reg dest; + TCGv_i64 dest; if (!ctx->is_pa20 && a->d) { return false; @@ -3452,11 +3354,11 @@ static bool trans_depi_imm(DisasContext *ctx, arg_depi_imm *a) mask1 = deposit64(-1, a->cpos, len, a->i); if (a->nz) { - TCGv_reg src = load_gpr(ctx, a->t); - tcg_gen_andi_reg(dest, src, mask1); - tcg_gen_ori_reg(dest, dest, mask0); + TCGv_i64 src = load_gpr(ctx, a->t); + tcg_gen_andi_i64(dest, src, mask1); + tcg_gen_ori_i64(dest, dest, mask0); } else { - tcg_gen_movi_reg(dest, mask0); + tcg_gen_movi_i64(dest, mask0); } save_gpr(ctx, a->t, dest); @@ -3472,7 +3374,7 @@ static bool trans_dep_imm(DisasContext *ctx, arg_dep_imm *a) { unsigned rs = a->nz ? a->t : 0; unsigned len, width; - TCGv_reg dest, val; + TCGv_i64 dest, val; if (!ctx->is_pa20 && a->d) { return false; @@ -3490,9 +3392,9 @@ static bool trans_dep_imm(DisasContext *ctx, arg_dep_imm *a) dest = dest_gpr(ctx, a->t); val = load_gpr(ctx, a->r); if (rs == 0) { - tcg_gen_deposit_z_reg(dest, val, a->cpos, len); + tcg_gen_deposit_z_i64(dest, val, a->cpos, len); } else { - tcg_gen_deposit_reg(dest, cpu_gr[rs], val, a->cpos, len); + tcg_gen_deposit_i64(dest, cpu_gr[rs], val, a->cpos, len); } save_gpr(ctx, a->t, dest); @@ -3505,11 +3407,11 @@ static bool trans_dep_imm(DisasContext *ctx, arg_dep_imm *a) } static bool do_dep_sar(DisasContext *ctx, unsigned rt, unsigned c, - bool d, bool nz, unsigned len, TCGv_reg val) + bool d, bool nz, unsigned len, TCGv_i64 val) { unsigned rs = nz ? rt : 0; unsigned widthm1 = d ? 63 : 31; - TCGv_reg mask, tmp, shift, dest; + TCGv_i64 mask, tmp, shift, dest; uint64_t msb = 1ULL << (len - 1); dest = dest_gpr(ctx, rt); @@ -3517,19 +3419,19 @@ static bool do_dep_sar(DisasContext *ctx, unsigned rt, unsigned c, tmp = tcg_temp_new(); /* Convert big-endian bit numbering in SAR to left-shift. */ - tcg_gen_andi_reg(shift, cpu_sar, widthm1); - tcg_gen_xori_reg(shift, shift, widthm1); + tcg_gen_andi_i64(shift, cpu_sar, widthm1); + tcg_gen_xori_i64(shift, shift, widthm1); mask = tcg_temp_new(); - tcg_gen_movi_reg(mask, msb + (msb - 1)); - tcg_gen_and_reg(tmp, val, mask); + tcg_gen_movi_i64(mask, msb + (msb - 1)); + tcg_gen_and_i64(tmp, val, mask); if (rs) { - tcg_gen_shl_reg(mask, mask, shift); - tcg_gen_shl_reg(tmp, tmp, shift); - tcg_gen_andc_reg(dest, cpu_gr[rs], mask); - tcg_gen_or_reg(dest, dest, tmp); + tcg_gen_shl_i64(mask, mask, shift); + tcg_gen_shl_i64(tmp, tmp, shift); + tcg_gen_andc_i64(dest, cpu_gr[rs], mask); + tcg_gen_or_i64(dest, dest, tmp); } else { - tcg_gen_shl_reg(dest, tmp, shift); + tcg_gen_shl_i64(dest, tmp, shift); } save_gpr(ctx, rt, dest); @@ -3562,12 +3464,12 @@ static bool trans_depi_sar(DisasContext *ctx, arg_depi_sar *a) nullify_over(ctx); } return do_dep_sar(ctx, a->t, a->c, a->d, a->nz, a->len, - tcg_constant_reg(a->i)); + tcg_constant_i64(a->i)); } static bool trans_be(DisasContext *ctx, arg_be *a) { - TCGv_reg tmp; + TCGv_i64 tmp; #ifdef CONFIG_USER_ONLY /* ??? It seems like there should be a good way of using @@ -3586,7 +3488,7 @@ static bool trans_be(DisasContext *ctx, arg_be *a) #endif tmp = tcg_temp_new(); - tcg_gen_addi_reg(tmp, load_gpr(ctx, a->b), a->disp); + tcg_gen_addi_i64(tmp, load_gpr(ctx, a->b), a->disp); tmp = do_ibranch_priv(ctx, tmp); #ifdef CONFIG_USER_ONLY @@ -3601,7 +3503,7 @@ static bool trans_be(DisasContext *ctx, arg_be *a) } if (a->n && use_nullify_skip(ctx)) { copy_iaoq_entry(ctx, cpu_iaoq_f, -1, tmp); - tcg_gen_addi_reg(tmp, tmp, 4); + tcg_gen_addi_i64(tmp, tmp, 4); copy_iaoq_entry(ctx, cpu_iaoq_b, -1, tmp); tcg_gen_mov_i64(cpu_iasq_f, new_spc); tcg_gen_mov_i64(cpu_iasq_b, cpu_iasq_f); @@ -3669,11 +3571,11 @@ static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a) #endif if (a->l) { - TCGv_reg tmp = dest_gpr(ctx, a->l); + TCGv_i64 tmp = dest_gpr(ctx, a->l); if (ctx->privilege < 3) { - tcg_gen_andi_reg(tmp, tmp, -4); + tcg_gen_andi_i64(tmp, tmp, -4); } - tcg_gen_ori_reg(tmp, tmp, ctx->privilege); + tcg_gen_ori_i64(tmp, tmp, ctx->privilege); save_gpr(ctx, a->l, tmp); } @@ -3683,9 +3585,9 @@ static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a) static bool trans_blr(DisasContext *ctx, arg_blr *a) { if (a->x) { - TCGv_reg tmp = tcg_temp_new(); - tcg_gen_shli_reg(tmp, load_gpr(ctx, a->x), 3); - tcg_gen_addi_reg(tmp, tmp, ctx->iaoq_f + 8); + TCGv_i64 tmp = tcg_temp_new(); + tcg_gen_shli_i64(tmp, load_gpr(ctx, a->x), 3); + tcg_gen_addi_i64(tmp, tmp, ctx->iaoq_f + 8); /* The computation here never changes privilege level. */ return do_ibranch(ctx, tmp, a->l, a->n); } else { @@ -3696,14 +3598,14 @@ static bool trans_blr(DisasContext *ctx, arg_blr *a) static bool trans_bv(DisasContext *ctx, arg_bv *a) { - TCGv_reg dest; + TCGv_i64 dest; if (a->x == 0) { dest = load_gpr(ctx, a->b); } else { dest = tcg_temp_new(); - tcg_gen_shli_reg(dest, load_gpr(ctx, a->x), 3); - tcg_gen_add_reg(dest, dest, load_gpr(ctx, a->b)); + tcg_gen_shli_i64(dest, load_gpr(ctx, a->x), 3); + tcg_gen_add_i64(dest, dest, load_gpr(ctx, a->b)); } dest = do_ibranch_priv(ctx, dest); return do_ibranch(ctx, dest, 0, a->n); @@ -3711,7 +3613,7 @@ static bool trans_bv(DisasContext *ctx, arg_bv *a) static bool trans_bve(DisasContext *ctx, arg_bve *a) { - TCGv_reg dest; + TCGv_i64 dest; #ifdef CONFIG_USER_ONLY dest = do_ibranch_priv(ctx, load_gpr(ctx, a->b)); @@ -4034,12 +3936,12 @@ static bool trans_fcmp_d(DisasContext *ctx, arg_fclass2 *a) static bool trans_ftest(DisasContext *ctx, arg_ftest *a) { - TCGv_reg t; + TCGv_i64 t; nullify_over(ctx); t = tcg_temp_new(); - tcg_gen_ld32u_reg(t, tcg_env, offsetof(CPUHPPAState, fr0_shadow)); + tcg_gen_ld32u_i64(t, tcg_env, offsetof(CPUHPPAState, fr0_shadow)); if (a->y == 1) { int mask; @@ -4047,7 +3949,7 @@ static bool trans_ftest(DisasContext *ctx, arg_ftest *a) switch (a->c) { case 0: /* simple */ - tcg_gen_andi_reg(t, t, 0x4000000); + tcg_gen_andi_i64(t, t, 0x4000000); ctx->null_cond = cond_make_0(TCG_COND_NE, t); goto done; case 2: /* rej */ @@ -4076,17 +3978,17 @@ static bool trans_ftest(DisasContext *ctx, arg_ftest *a) return true; } if (inv) { - TCGv_reg c = tcg_constant_reg(mask); - tcg_gen_or_reg(t, t, c); + TCGv_i64 c = tcg_constant_i64(mask); + tcg_gen_or_i64(t, t, c); ctx->null_cond = cond_make(TCG_COND_EQ, t, c); } else { - tcg_gen_andi_reg(t, t, mask); + tcg_gen_andi_i64(t, t, mask); ctx->null_cond = cond_make_0(TCG_COND_EQ, t); } } else { unsigned cbit = (a->y ^ 1) - 1; - tcg_gen_extract_reg(t, t, 21 - cbit, 1); + tcg_gen_extract_i64(t, t, 21 - cbit, 1); ctx->null_cond = cond_make_0(TCG_COND_NE, t); } @@ -4341,7 +4243,7 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) if (ctx->iaoq_b == -1) { ctx->iaoq_n = -1; ctx->iaoq_n_var = tcg_temp_new(); - tcg_gen_addi_reg(ctx->iaoq_n_var, cpu_iaoq_b, 4); + tcg_gen_addi_i64(ctx->iaoq_n_var, cpu_iaoq_b, 4); } else { ctx->iaoq_n = ctx->iaoq_b + 4; ctx->iaoq_n_var = NULL; From 967662cd5a0c05544c5d4dc1ba20b24fa42af6c4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 17 Oct 2023 21:42:55 -0700 Subject: [PATCH 628/974] target/hppa: Remove remaining TARGET_REGISTER_BITS redirections The conversions to/from i64 can be eliminated entirely, folding computation into adjacent operations. Signed-off-by: Richard Henderson --- target/hppa/translate.c | 46 ++++++++++++----------------------------- 1 file changed, 13 insertions(+), 33 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index ec3f70e46e..0024c38c84 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -33,15 +33,6 @@ #undef HELPER_H -/* Since we have a distinction between register size and address size, - we need to redefine all of these. */ - -#define tcg_gen_extu_reg_tl tcg_gen_mov_i64 -#define tcg_gen_trunc_i64_reg tcg_gen_mov_i64 -#define tcg_gen_extu_reg_i64 tcg_gen_mov_i64 -#define tcg_gen_ext_reg_i64 tcg_gen_mov_i64 - - typedef struct DisasCond { TCGCond c; TCGv_i64 a0, a1; @@ -1345,8 +1336,7 @@ static void form_gva(DisasContext *ctx, TCGv_i64 *pgva, TCGv_i64 *pofs, *pofs = ofs; *pgva = addr = tcg_temp_new_i64(); - tcg_gen_extu_reg_tl(addr, modify <= 0 ? ofs : base); - tcg_gen_andi_tl(addr, addr, gva_offset_mask(ctx)); + tcg_gen_andi_tl(addr, modify <= 0 ? ofs : base, gva_offset_mask(ctx)); #ifndef CONFIG_USER_ONLY if (!is_phys) { tcg_gen_or_tl(addr, addr, space_select(ctx, sp, base)); @@ -1966,13 +1956,11 @@ static bool trans_mfsp(DisasContext *ctx, arg_mfsp *a) unsigned rt = a->t; unsigned rs = a->sp; TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new(); load_spr(ctx, t0, rs); tcg_gen_shri_i64(t0, t0, 32); - tcg_gen_trunc_i64_reg(t1, t0); - save_gpr(ctx, rt, t1); + save_gpr(ctx, rt, t0); cond_free(&ctx->null_cond); return true; @@ -2029,22 +2017,21 @@ static bool trans_mtsp(DisasContext *ctx, arg_mtsp *a) { unsigned rr = a->r; unsigned rs = a->sp; - TCGv_i64 t64; + TCGv_i64 tmp; if (rs >= 5) { CHECK_MOST_PRIVILEGED(EXCP_PRIV_REG); } nullify_over(ctx); - t64 = tcg_temp_new_i64(); - tcg_gen_extu_reg_i64(t64, load_gpr(ctx, rr)); - tcg_gen_shli_i64(t64, t64, 32); + tmp = tcg_temp_new_i64(); + tcg_gen_shli_i64(tmp, load_gpr(ctx, rr), 32); if (rs >= 4) { - tcg_gen_st_i64(t64, tcg_env, offsetof(CPUHPPAState, sr[rs])); + tcg_gen_st_i64(tmp, tcg_env, offsetof(CPUHPPAState, sr[rs])); ctx->tb_flags &= ~TB_FLAG_SR_SAME; } else { - tcg_gen_mov_i64(cpu_sr[rs], t64); + tcg_gen_mov_i64(cpu_sr[rs], tmp); } return nullify_end(ctx); @@ -2135,11 +2122,8 @@ static bool trans_ldsid(DisasContext *ctx, arg_ldsid *a) /* We don't implement space registers in user mode. */ tcg_gen_movi_i64(dest, 0); #else - TCGv_i64 t0 = tcg_temp_new_i64(); - - tcg_gen_mov_i64(t0, space_select(ctx, a->sp, load_gpr(ctx, a->b))); - tcg_gen_shri_i64(t0, t0, 32); - tcg_gen_trunc_i64_reg(dest, t0); + tcg_gen_mov_i64(dest, space_select(ctx, a->sp, load_gpr(ctx, a->b))); + tcg_gen_shri_i64(dest, dest, 32); #endif save_gpr(ctx, a->t, dest); @@ -3188,10 +3172,8 @@ static bool trans_shrp_sar(DisasContext *ctx, arg_shrp_sar *a) TCGv_i64 s = tcg_temp_new_i64(); tcg_gen_concat32_i64(t, src2, src1); - tcg_gen_extu_reg_i64(s, cpu_sar); - tcg_gen_andi_i64(s, s, 31); - tcg_gen_shr_i64(t, t, s); - tcg_gen_trunc_i64_reg(dest, t); + tcg_gen_andi_i64(s, cpu_sar, 31); + tcg_gen_shr_i64(dest, t, s); } } save_gpr(ctx, a->t, dest); @@ -3233,10 +3215,8 @@ static bool trans_shrp_imm(DisasContext *ctx, arg_shrp_imm *a) tcg_gen_rotri_i32(t32, t32, sa); tcg_gen_extu_i32_i64(dest, t32); } else { - TCGv_i64 t64 = tcg_temp_new_i64(); - tcg_gen_concat32_i64(t64, t2, cpu_gr[a->r1]); - tcg_gen_shri_i64(t64, t64, sa); - tcg_gen_trunc_i64_reg(dest, t64); + tcg_gen_concat32_i64(dest, t2, cpu_gr[a->r1]); + tcg_gen_extract_i64(dest, dest, sa, 32); } } save_gpr(ctx, a->t, dest); From ea6c40b0f13e8aa79ffe27031ad260cb91b6a935 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 26 Oct 2023 10:42:21 -0700 Subject: [PATCH 629/974] target/hppa: Adjust vmstate_env for pa2.0 tlb Split out the tlb to a subsection so that it can be separately versioned -- the format is only partially following the architecture and is partially guided by the qemu implementation. Signed-off-by: Richard Henderson --- target/hppa/machine.c | 93 ++++++++++++++++++++++++++----------------- 1 file changed, 57 insertions(+), 36 deletions(-) diff --git a/target/hppa/machine.c b/target/hppa/machine.c index f6df4deac5..2f8e8cc5a1 100644 --- a/target/hppa/machine.c +++ b/target/hppa/machine.c @@ -44,28 +44,30 @@ static const VMStateInfo vmstate_psw = { .put = put_psw, }; -/* FIXME: Use the PA2.0 format, which is a superset of the PA1.1 format. */ static int get_tlb(QEMUFile *f, void *opaque, size_t size, const VMStateField *field) { HPPATLBEntry *ent = opaque; - uint32_t val; + uint64_t val; ent->itree.start = qemu_get_be64(f); + ent->itree.last = qemu_get_be64(f); ent->pa = qemu_get_be64(f); - val = qemu_get_be32(f); + val = qemu_get_be64(f); - ent->entry_valid = extract32(val, 0, 1); - ent->access_id = extract32(val, 1, 18); - ent->u = extract32(val, 19, 1); - ent->ar_pl2 = extract32(val, 20, 2); - ent->ar_pl1 = extract32(val, 22, 2); - ent->ar_type = extract32(val, 24, 3); - ent->b = extract32(val, 27, 1); - ent->d = extract32(val, 28, 1); - ent->t = extract32(val, 29, 1); - - ent->itree.last = ent->itree.start + TARGET_PAGE_SIZE - 1; + if (val) { + ent->t = extract64(val, 61, 1); + ent->d = extract64(val, 60, 1); + ent->b = extract64(val, 59, 1); + ent->ar_type = extract64(val, 56, 3); + ent->ar_pl1 = extract64(val, 54, 2); + ent->ar_pl2 = extract64(val, 52, 2); + ent->u = extract64(val, 51, 1); + /* o = bit 50 */ + /* p = bit 49 */ + ent->access_id = extract64(val, 1, 31); + ent->entry_valid = 1; + } return 0; } @@ -73,27 +75,30 @@ static int put_tlb(QEMUFile *f, void *opaque, size_t size, const VMStateField *field, JSONWriter *vmdesc) { HPPATLBEntry *ent = opaque; - uint32_t val = 0; + uint64_t val = 0; if (ent->entry_valid) { val = 1; - val = deposit32(val, 1, 18, ent->access_id); - val = deposit32(val, 19, 1, ent->u); - val = deposit32(val, 20, 2, ent->ar_pl2); - val = deposit32(val, 22, 2, ent->ar_pl1); - val = deposit32(val, 24, 3, ent->ar_type); - val = deposit32(val, 27, 1, ent->b); - val = deposit32(val, 28, 1, ent->d); - val = deposit32(val, 29, 1, ent->t); + val = deposit64(val, 61, 1, ent->t); + val = deposit64(val, 60, 1, ent->d); + val = deposit64(val, 59, 1, ent->b); + val = deposit64(val, 56, 3, ent->ar_type); + val = deposit64(val, 54, 2, ent->ar_pl1); + val = deposit64(val, 52, 2, ent->ar_pl2); + val = deposit64(val, 51, 1, ent->u); + /* o = bit 50 */ + /* p = bit 49 */ + val = deposit64(val, 1, 31, ent->access_id); } qemu_put_be64(f, ent->itree.start); + qemu_put_be64(f, ent->itree.last); qemu_put_be64(f, ent->pa); - qemu_put_be32(f, val); + qemu_put_be64(f, val); return 0; } -static const VMStateInfo vmstate_tlb = { +static const VMStateInfo vmstate_tlb_entry = { .name = "tlb entry", .get = get_tlb, .put = put_tlb, @@ -147,7 +152,24 @@ static int tlb_post_load(void *opaque, int version_id) return 0; } -static VMStateField vmstate_env_fields[] = { +static const VMStateField vmstate_tlb_fields[] = { + VMSTATE_ARRAY(tlb, CPUHPPAState, + ARRAY_SIZE(((CPUHPPAState *)0)->tlb), + 0, vmstate_tlb_entry, HPPATLBEntry), + VMSTATE_UINT32(tlb_last, CPUHPPAState), + VMSTATE_END_OF_LIST() +}; + +static const VMStateDescription vmstate_tlb = { + .name = "env/tlb", + .version_id = 1, + .minimum_version_id = 1, + .fields = vmstate_tlb_fields, + .pre_load = tlb_pre_load, + .post_load = tlb_post_load, +}; + +static const VMStateField vmstate_env_fields[] = { VMSTATE_UINT64_ARRAY(gr, CPUHPPAState, 32), VMSTATE_UINT64_ARRAY(fr, CPUHPPAState, 32), VMSTATE_UINT64_ARRAY(sr, CPUHPPAState, 8), @@ -176,24 +198,23 @@ static VMStateField vmstate_env_fields[] = { VMSTATE_UINT64(iasq_b, CPUHPPAState), VMSTATE_UINT32(fr0_shadow, CPUHPPAState), - - VMSTATE_ARRAY(tlb, CPUHPPAState, ARRAY_SIZE(((CPUHPPAState *)0)->tlb), - 0, vmstate_tlb, HPPATLBEntry), - VMSTATE_UINT32(tlb_last, CPUHPPAState), - VMSTATE_END_OF_LIST() }; +static const VMStateDescription *vmstate_env_subsections[] = { + &vmstate_tlb, + NULL +}; + static const VMStateDescription vmstate_env = { .name = "env", - .version_id = 2, - .minimum_version_id = 2, + .version_id = 3, + .minimum_version_id = 3, .fields = vmstate_env_fields, - .pre_load = tlb_pre_load, - .post_load = tlb_post_load, + .subsections = vmstate_env_subsections, }; -static VMStateField vmstate_cpu_fields[] = { +static const VMStateField vmstate_cpu_fields[] = { VMSTATE_CPU(), VMSTATE_STRUCT(env, HPPACPU, 1, vmstate_env, CPUHPPAState), VMSTATE_END_OF_LIST() From aac0f603de963fe6dc8a4b1c83dc43006635f1bc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 17 Oct 2023 21:47:02 -0700 Subject: [PATCH 630/974] target/hppa: Use tcg_temp_new_i64 not tcg_temp_new Signed-off-by: Richard Henderson --- target/hppa/translate.c | 162 ++++++++++++++++++++-------------------- 1 file changed, 82 insertions(+), 80 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 0024c38c84..c8c702ac03 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -32,6 +32,8 @@ #include "exec/helper-info.c.inc" #undef HELPER_H +/* Choose to use explicit sizes within this file. */ +#undef tcg_temp_new typedef struct DisasCond { TCGCond c; @@ -269,15 +271,15 @@ static DisasCond cond_make_0_tmp(TCGCond c, TCGv_i64 a0) static DisasCond cond_make_0(TCGCond c, TCGv_i64 a0) { - TCGv_i64 tmp = tcg_temp_new(); + TCGv_i64 tmp = tcg_temp_new_i64(); tcg_gen_mov_i64(tmp, a0); return cond_make_0_tmp(c, tmp); } static DisasCond cond_make(TCGCond c, TCGv_i64 a0, TCGv_i64 a1) { - TCGv_i64 t0 = tcg_temp_new(); - TCGv_i64 t1 = tcg_temp_new(); + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); tcg_gen_mov_i64(t0, a0); tcg_gen_mov_i64(t1, a1); @@ -302,7 +304,7 @@ static void cond_free(DisasCond *cond) static TCGv_i64 load_gpr(DisasContext *ctx, unsigned reg) { if (reg == 0) { - TCGv_i64 t = tcg_temp_new(); + TCGv_i64 t = tcg_temp_new_i64(); tcg_gen_movi_i64(t, 0); return t; } else { @@ -313,7 +315,7 @@ static TCGv_i64 load_gpr(DisasContext *ctx, unsigned reg) static TCGv_i64 dest_gpr(DisasContext *ctx, unsigned reg) { if (reg == 0 || ctx->null_cond.c != TCG_COND_NEVER) { - return tcg_temp_new(); + return tcg_temp_new_i64(); } else { return cpu_gr[reg]; } @@ -437,7 +439,7 @@ static void nullify_over(DisasContext *ctx) /* If we're using PSW[N], copy it to a temp because... */ if (ctx->null_cond.a0 == cpu_psw_n) { - ctx->null_cond.a0 = tcg_temp_new(); + ctx->null_cond.a0 = tcg_temp_new_i64(); tcg_gen_mov_i64(ctx->null_cond.a0, cpu_psw_n); } /* ... we clear it before branching over the implementation, @@ -657,14 +659,14 @@ static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d, break; case 1: /* = / <> (Z / !Z) */ if (cond_need_ext(ctx, d)) { - tmp = tcg_temp_new(); + tmp = tcg_temp_new_i64(); tcg_gen_ext32u_i64(tmp, res); res = tmp; } cond = cond_make_0(TCG_COND_EQ, res); break; case 2: /* < / >= (N ^ V / !(N ^ V) */ - tmp = tcg_temp_new(); + tmp = tcg_temp_new_i64(); tcg_gen_xor_i64(tmp, res, sv); if (cond_need_ext(ctx, d)) { tcg_gen_ext32s_i64(tmp, tmp); @@ -681,7 +683,7 @@ static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d, * !(~(res ^ sv) >> 31) | !res * !(~(res ^ sv) >> 31 & res) */ - tmp = tcg_temp_new(); + tmp = tcg_temp_new_i64(); tcg_gen_eqv_i64(tmp, res, sv); if (cond_need_ext(ctx, d)) { tcg_gen_sextract_i64(tmp, tmp, 31, 1); @@ -698,7 +700,7 @@ static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d, cond = cond_make_0(TCG_COND_EQ, cb_msb); break; case 5: /* ZNV / VNZ (!C | Z / C & !Z) */ - tmp = tcg_temp_new(); + tmp = tcg_temp_new_i64(); tcg_gen_neg_i64(tmp, cb_msb); tcg_gen_and_i64(tmp, tmp, res); if (cond_need_ext(ctx, d)) { @@ -708,14 +710,14 @@ static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d, break; case 6: /* SV / NSV (V / !V) */ if (cond_need_ext(ctx, d)) { - tmp = tcg_temp_new(); + tmp = tcg_temp_new_i64(); tcg_gen_ext32s_i64(tmp, sv); sv = tmp; } cond = cond_make_0(TCG_COND_LT, sv); break; case 7: /* OD / EV */ - tmp = tcg_temp_new(); + tmp = tcg_temp_new_i64(); tcg_gen_andi_i64(tmp, res, 1); cond = cond_make_0_tmp(TCG_COND_NE, tmp); break; @@ -769,8 +771,8 @@ static DisasCond do_sub_cond(DisasContext *ctx, unsigned cf, bool d, tc = tcg_invert_cond(tc); } if (cond_need_ext(ctx, d)) { - TCGv_i64 t1 = tcg_temp_new(); - TCGv_i64 t2 = tcg_temp_new(); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); if (ext_uns) { tcg_gen_ext32u_i64(t1, in1); @@ -846,7 +848,7 @@ static DisasCond do_log_cond(DisasContext *ctx, unsigned cf, bool d, } if (cond_need_ext(ctx, d)) { - TCGv_i64 tmp = tcg_temp_new(); + TCGv_i64 tmp = tcg_temp_new_i64(); if (ext_uns) { tcg_gen_ext32u_i64(tmp, res); @@ -891,8 +893,8 @@ static DisasCond do_unit_cond(unsigned cf, bool d, TCGv_i64 res, * do our normal thing and compute carry-in of bit B+1 since that * leaves us with carry bits spread across two words. */ - cb = tcg_temp_new(); - tmp = tcg_temp_new(); + cb = tcg_temp_new_i64(); + tmp = tcg_temp_new_i64(); tcg_gen_or_i64(cb, in1, in2); tcg_gen_and_i64(tmp, in1, in2); tcg_gen_andc_i64(cb, cb, res); @@ -910,7 +912,7 @@ static DisasCond do_unit_cond(unsigned cf, bool d, TCGv_i64 res, /* See hasless(v,1) from * https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord */ - tmp = tcg_temp_new(); + tmp = tcg_temp_new_i64(); tcg_gen_subi_i64(tmp, res, d_repl * 0x01010101u); tcg_gen_andc_i64(tmp, tmp, res); tcg_gen_andi_i64(tmp, tmp, d_repl * 0x80808080u); @@ -918,7 +920,7 @@ static DisasCond do_unit_cond(unsigned cf, bool d, TCGv_i64 res, break; case 3: /* SHZ / NHZ */ - tmp = tcg_temp_new(); + tmp = tcg_temp_new_i64(); tcg_gen_subi_i64(tmp, res, d_repl * 0x00010001u); tcg_gen_andc_i64(tmp, tmp, res); tcg_gen_andi_i64(tmp, tmp, d_repl * 0x80008000u); @@ -954,7 +956,7 @@ static TCGv_i64 get_carry(DisasContext *ctx, bool d, TCGv_i64 cb, TCGv_i64 cb_msb) { if (cond_need_ext(ctx, d)) { - TCGv_i64 t = tcg_temp_new(); + TCGv_i64 t = tcg_temp_new_i64(); tcg_gen_extract_i64(t, cb, 32, 1); return t; } @@ -970,8 +972,8 @@ static TCGv_i64 get_psw_carry(DisasContext *ctx, bool d) static TCGv_i64 do_add_sv(DisasContext *ctx, TCGv_i64 res, TCGv_i64 in1, TCGv_i64 in2) { - TCGv_i64 sv = tcg_temp_new(); - TCGv_i64 tmp = tcg_temp_new(); + TCGv_i64 sv = tcg_temp_new_i64(); + TCGv_i64 tmp = tcg_temp_new_i64(); tcg_gen_xor_i64(sv, res, in1); tcg_gen_xor_i64(tmp, in1, in2); @@ -984,8 +986,8 @@ static TCGv_i64 do_add_sv(DisasContext *ctx, TCGv_i64 res, static TCGv_i64 do_sub_sv(DisasContext *ctx, TCGv_i64 res, TCGv_i64 in1, TCGv_i64 in2) { - TCGv_i64 sv = tcg_temp_new(); - TCGv_i64 tmp = tcg_temp_new(); + TCGv_i64 sv = tcg_temp_new_i64(); + TCGv_i64 tmp = tcg_temp_new_i64(); tcg_gen_xor_i64(sv, res, in1); tcg_gen_xor_i64(tmp, in1, in2); @@ -1002,21 +1004,21 @@ static void do_add(DisasContext *ctx, unsigned rt, TCGv_i64 in1, unsigned c = cf >> 1; DisasCond cond; - dest = tcg_temp_new(); + dest = tcg_temp_new_i64(); cb = NULL; cb_msb = NULL; cb_cond = NULL; if (shift) { - tmp = tcg_temp_new(); + tmp = tcg_temp_new_i64(); tcg_gen_shli_i64(tmp, in1, shift); in1 = tmp; } if (!is_l || cond_need_cb(c)) { TCGv_i64 zero = tcg_constant_i64(0); - cb_msb = tcg_temp_new(); - cb = tcg_temp_new(); + cb_msb = tcg_temp_new_i64(); + cb = tcg_temp_new_i64(); tcg_gen_add2_i64(dest, cb_msb, in1, zero, in2, zero); if (is_c) { @@ -1048,7 +1050,7 @@ static void do_add(DisasContext *ctx, unsigned rt, TCGv_i64 in1, /* Emit any conditional trap before any writeback. */ cond = do_cond(ctx, cf, d, dest, cb_cond, sv); if (is_tc) { - tmp = tcg_temp_new(); + tmp = tcg_temp_new_i64(); tcg_gen_setcond_i64(cond.c, tmp, cond.a0, cond.a1); gen_helper_tcond(tcg_env, tmp); } @@ -1103,9 +1105,9 @@ static void do_sub(DisasContext *ctx, unsigned rt, TCGv_i64 in1, unsigned c = cf >> 1; DisasCond cond; - dest = tcg_temp_new(); - cb = tcg_temp_new(); - cb_msb = tcg_temp_new(); + dest = tcg_temp_new_i64(); + cb = tcg_temp_new_i64(); + cb_msb = tcg_temp_new_i64(); zero = tcg_constant_i64(0); if (is_b) { @@ -1144,7 +1146,7 @@ static void do_sub(DisasContext *ctx, unsigned rt, TCGv_i64 in1, /* Emit any conditional trap before any writeback. */ if (is_tc) { - tmp = tcg_temp_new(); + tmp = tcg_temp_new_i64(); tcg_gen_setcond_i64(cond.c, tmp, cond.a0, cond.a1); gen_helper_tcond(tcg_env, tmp); } @@ -1193,7 +1195,7 @@ static void do_cmpclr(DisasContext *ctx, unsigned rt, TCGv_i64 in1, TCGv_i64 dest, sv; DisasCond cond; - dest = tcg_temp_new(); + dest = tcg_temp_new_i64(); tcg_gen_sub_i64(dest, in1, in2); /* Compute signed overflow if required. */ @@ -1258,13 +1260,13 @@ static void do_unit(DisasContext *ctx, unsigned rt, TCGv_i64 in1, save_gpr(ctx, rt, dest); cond_free(&ctx->null_cond); } else { - dest = tcg_temp_new(); + dest = tcg_temp_new_i64(); fn(dest, in1, in2); cond = do_unit_cond(cf, d, dest, in1, in2); if (is_tc) { - TCGv_i64 tmp = tcg_temp_new(); + TCGv_i64 tmp = tcg_temp_new_i64(); tcg_gen_setcond_i64(cond.c, tmp, cond.a0, cond.a1); gen_helper_tcond(tcg_env, tmp); } @@ -1299,7 +1301,7 @@ static TCGv_i64 space_select(DisasContext *ctx, int sp, TCGv_i64 base) } ptr = tcg_temp_new_ptr(); - tmp = tcg_temp_new(); + tmp = tcg_temp_new_i64(); spc = tcg_temp_new_i64(); /* Extract top 2 bits of the address, shift left 3 for uint64_t index. */ @@ -1324,11 +1326,11 @@ static void form_gva(DisasContext *ctx, TCGv_i64 *pgva, TCGv_i64 *pofs, /* Note that RX is mutually exclusive with DISP. */ if (rx) { - ofs = tcg_temp_new(); + ofs = tcg_temp_new_i64(); tcg_gen_shli_i64(ofs, cpu_gr[rx], scale); tcg_gen_add_i64(ofs, ofs, base); } else if (disp || modify) { - ofs = tcg_temp_new(); + ofs = tcg_temp_new_i64(); tcg_gen_addi_i64(ofs, base, disp); } else { ofs = base; @@ -1434,7 +1436,7 @@ static bool do_load(DisasContext *ctx, unsigned rt, unsigned rb, dest = dest_gpr(ctx, rt); } else { /* Make sure if RT == RB, we see the result of the load. */ - dest = tcg_temp_new(); + dest = tcg_temp_new_i64(); } do_load_64(ctx, dest, rb, rx, scale, disp, sp, modify, mop); save_gpr(ctx, rt, dest); @@ -1750,7 +1752,7 @@ static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, if (link != 0) { copy_iaoq_entry(ctx, cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); } - next = tcg_temp_new(); + next = tcg_temp_new_i64(); tcg_gen_mov_i64(next, dest); if (is_n) { if (use_nullify_skip(ctx)) { @@ -1779,7 +1781,7 @@ static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, branching. Since IOAQ_F is not really live at this point, we can simply store DEST optimistically. Similarly with IAOQ_B. */ copy_iaoq_entry(ctx, cpu_iaoq_f, -1, dest); - next = tcg_temp_new(); + next = tcg_temp_new_i64(); tcg_gen_addi_i64(next, dest, 4); copy_iaoq_entry(ctx, cpu_iaoq_b, -1, next); @@ -1794,8 +1796,8 @@ static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, a0 = ctx->null_cond.a0; a1 = ctx->null_cond.a1; - tmp = tcg_temp_new(); - next = tcg_temp_new(); + tmp = tcg_temp_new_i64(); + next = tcg_temp_new_i64(); copy_iaoq_entry(ctx, tmp, ctx->iaoq_n, ctx->iaoq_n_var); tcg_gen_movcond_i64(c, next, a0, a1, tmp, dest); @@ -1837,11 +1839,11 @@ static TCGv_i64 do_ibranch_priv(DisasContext *ctx, TCGv_i64 offset) return offset; case 3: /* Privilege 3 is minimum and is never allowed to increase. */ - dest = tcg_temp_new(); + dest = tcg_temp_new_i64(); tcg_gen_ori_i64(dest, offset, 3); break; default: - dest = tcg_temp_new(); + dest = tcg_temp_new_i64(); tcg_gen_andi_i64(dest, offset, -4); tcg_gen_ori_i64(dest, dest, ctx->privilege); tcg_gen_movcond_i64(TCG_COND_GTU, dest, dest, offset, dest, offset); @@ -1898,7 +1900,7 @@ static void do_page_zero(DisasContext *ctx) case 0xe0: /* SET_THREAD_POINTER */ tcg_gen_st_i64(cpu_gr[26], tcg_env, offsetof(CPUHPPAState, cr[27])); - tmp = tcg_temp_new(); + tmp = tcg_temp_new_i64(); tcg_gen_ori_i64(tmp, cpu_gr[31], 3); copy_iaoq_entry(ctx, cpu_iaoq_f, -1, tmp); tcg_gen_addi_i64(tmp, tmp, 4); @@ -2004,7 +2006,7 @@ static bool trans_mfctl(DisasContext *ctx, arg_mfctl *a) break; } - tmp = tcg_temp_new(); + tmp = tcg_temp_new_i64(); tcg_gen_ld_i64(tmp, tcg_env, offsetof(CPUHPPAState, cr[ctl])); save_gpr(ctx, rt, tmp); @@ -2045,7 +2047,7 @@ static bool trans_mtctl(DisasContext *ctx, arg_mtctl *a) if (ctl == CR_SAR) { reg = load_gpr(ctx, a->r); - tmp = tcg_temp_new(); + tmp = tcg_temp_new_i64(); tcg_gen_andi_i64(tmp, reg, ctx->is_pa20 ? 63 : 31); save_or_nullify(ctx, cpu_sar, tmp); @@ -2076,7 +2078,7 @@ static bool trans_mtctl(DisasContext *ctx, arg_mtctl *a) case CR_IIAOQ: /* FIXME: Respect PSW_Q bit */ /* The write advances the queue and stores to the back element. */ - tmp = tcg_temp_new(); + tmp = tcg_temp_new_i64(); tcg_gen_ld_i64(tmp, tcg_env, offsetof(CPUHPPAState, cr_back[ctl - CR_IIASQ])); tcg_gen_st_i64(tmp, tcg_env, offsetof(CPUHPPAState, cr[ctl])); @@ -2104,7 +2106,7 @@ static bool trans_mtctl(DisasContext *ctx, arg_mtctl *a) static bool trans_mtsarcm(DisasContext *ctx, arg_mtsarcm *a) { - TCGv_i64 tmp = tcg_temp_new(); + TCGv_i64 tmp = tcg_temp_new_i64(); tcg_gen_not_i64(tmp, load_gpr(ctx, a->r)); tcg_gen_andi_i64(tmp, tmp, ctx->is_pa20 ? 63 : 31); @@ -2139,7 +2141,7 @@ static bool trans_rsm(DisasContext *ctx, arg_rsm *a) nullify_over(ctx); - tmp = tcg_temp_new(); + tmp = tcg_temp_new_i64(); tcg_gen_ld_i64(tmp, tcg_env, offsetof(CPUHPPAState, psw)); tcg_gen_andi_i64(tmp, tmp, ~a->i); gen_helper_swap_system_mask(tmp, tcg_env, tmp); @@ -2159,7 +2161,7 @@ static bool trans_ssm(DisasContext *ctx, arg_ssm *a) nullify_over(ctx); - tmp = tcg_temp_new(); + tmp = tcg_temp_new_i64(); tcg_gen_ld_i64(tmp, tcg_env, offsetof(CPUHPPAState, psw)); tcg_gen_ori_i64(tmp, tmp, a->i); gen_helper_swap_system_mask(tmp, tcg_env, tmp); @@ -2179,7 +2181,7 @@ static bool trans_mtsm(DisasContext *ctx, arg_mtsm *a) nullify_over(ctx); reg = load_gpr(ctx, a->r); - tmp = tcg_temp_new(); + tmp = tcg_temp_new_i64(); gen_helper_swap_system_mask(tmp, tcg_env, reg); /* Exit the TB to recognize new interrupts. */ @@ -2434,7 +2436,7 @@ static bool trans_lpa(DisasContext *ctx, arg_ldst *a) form_gva(ctx, &vaddr, &ofs, a->b, a->x, 0, 0, a->sp, a->m, false); - paddr = tcg_temp_new(); + paddr = tcg_temp_new_i64(); gen_helper_lpa(paddr, tcg_env, vaddr); /* Note that physical address result overrides base modification. */ @@ -2618,7 +2620,7 @@ static bool do_uaddcm(DisasContext *ctx, arg_rrr_cf_d *a, bool is_tc) } tcg_r1 = load_gpr(ctx, a->r1); tcg_r2 = load_gpr(ctx, a->r2); - tmp = tcg_temp_new(); + tmp = tcg_temp_new_i64(); tcg_gen_not_i64(tmp, tcg_r2); do_unit(ctx, a->t, tcg_r1, tmp, a->cf, a->d, is_tc, tcg_gen_add_i64); return nullify_end(ctx); @@ -2640,7 +2642,7 @@ static bool do_dcor(DisasContext *ctx, arg_rr_cf_d *a, bool is_i) nullify_over(ctx); - tmp = tcg_temp_new(); + tmp = tcg_temp_new_i64(); tcg_gen_shri_i64(tmp, cpu_psw_cb, 3); if (!is_i) { tcg_gen_not_i64(tmp, tmp); @@ -2672,10 +2674,10 @@ static bool trans_ds(DisasContext *ctx, arg_rrr_cf *a) in1 = load_gpr(ctx, a->r1); in2 = load_gpr(ctx, a->r2); - add1 = tcg_temp_new(); - add2 = tcg_temp_new(); - addc = tcg_temp_new(); - dest = tcg_temp_new(); + add1 = tcg_temp_new_i64(); + add2 = tcg_temp_new_i64(); + addc = tcg_temp_new_i64(); + dest = tcg_temp_new_i64(); zero = tcg_constant_i64(0); /* Form R1 << 1 | PSW[CB]{8}. */ @@ -2798,7 +2800,7 @@ static bool trans_ldc(DisasContext *ctx, arg_ldst *a) if (a->m) { /* Base register modification. Make sure if RT == RB, we see the result of the load. */ - dest = tcg_temp_new(); + dest = tcg_temp_new_i64(); } else { dest = dest_gpr(ctx, a->t); } @@ -2958,7 +2960,7 @@ static bool do_cmpb(DisasContext *ctx, unsigned r, TCGv_i64 in1, DisasCond cond; in2 = load_gpr(ctx, r); - dest = tcg_temp_new(); + dest = tcg_temp_new_i64(); tcg_gen_sub_i64(dest, in1, in2); @@ -3010,13 +3012,13 @@ static bool do_addb(DisasContext *ctx, unsigned r, TCGv_i64 in1, } in2 = load_gpr(ctx, r); - dest = tcg_temp_new(); + dest = tcg_temp_new_i64(); sv = NULL; cb_cond = NULL; if (cond_need_cb(c)) { - TCGv_i64 cb = tcg_temp_new(); - TCGv_i64 cb_msb = tcg_temp_new(); + TCGv_i64 cb = tcg_temp_new_i64(); + TCGv_i64 cb_msb = tcg_temp_new_i64(); tcg_gen_movi_i64(cb_msb, 0); tcg_gen_add2_i64(dest, cb_msb, in1, cb_msb, in2, cb_msb); @@ -3054,7 +3056,7 @@ static bool trans_bb_sar(DisasContext *ctx, arg_bb_sar *a) nullify_over(ctx); - tmp = tcg_temp_new(); + tmp = tcg_temp_new_i64(); tcg_r = load_gpr(ctx, a->r); if (cond_need_ext(ctx, a->d)) { /* Force shift into [32,63] */ @@ -3076,7 +3078,7 @@ static bool trans_bb_imm(DisasContext *ctx, arg_bb_imm *a) nullify_over(ctx); - tmp = tcg_temp_new(); + tmp = tcg_temp_new_i64(); tcg_r = load_gpr(ctx, a->r); p = a->p | (cond_need_ext(ctx, a->d) ? 32 : 0); tcg_gen_shli_i64(tmp, tcg_r, p); @@ -3136,7 +3138,7 @@ static bool trans_shrp_sar(DisasContext *ctx, arg_shrp_sar *a) if (a->d) { tcg_gen_shr_i64(dest, src2, cpu_sar); } else { - TCGv_i64 tmp = tcg_temp_new(); + TCGv_i64 tmp = tcg_temp_new_i64(); tcg_gen_ext32u_i64(dest, src2); tcg_gen_andi_i64(tmp, cpu_sar, 31); @@ -3159,8 +3161,8 @@ static bool trans_shrp_sar(DisasContext *ctx, arg_shrp_sar *a) TCGv_i64 src1 = load_gpr(ctx, a->r1); if (a->d) { - TCGv_i64 t = tcg_temp_new(); - TCGv_i64 n = tcg_temp_new(); + TCGv_i64 t = tcg_temp_new_i64(); + TCGv_i64 n = tcg_temp_new_i64(); tcg_gen_xori_i64(n, cpu_sar, 63); tcg_gen_shl_i64(t, src2, n); @@ -3243,7 +3245,7 @@ static bool trans_extr_sar(DisasContext *ctx, arg_extr_sar *a) dest = dest_gpr(ctx, a->t); src = load_gpr(ctx, a->r); - tmp = tcg_temp_new(); + tmp = tcg_temp_new_i64(); /* Recall that SAR is using big-endian bit numbering. */ tcg_gen_andi_i64(tmp, cpu_sar, widthm1); @@ -3395,14 +3397,14 @@ static bool do_dep_sar(DisasContext *ctx, unsigned rt, unsigned c, uint64_t msb = 1ULL << (len - 1); dest = dest_gpr(ctx, rt); - shift = tcg_temp_new(); - tmp = tcg_temp_new(); + shift = tcg_temp_new_i64(); + tmp = tcg_temp_new_i64(); /* Convert big-endian bit numbering in SAR to left-shift. */ tcg_gen_andi_i64(shift, cpu_sar, widthm1); tcg_gen_xori_i64(shift, shift, widthm1); - mask = tcg_temp_new(); + mask = tcg_temp_new_i64(); tcg_gen_movi_i64(mask, msb + (msb - 1)); tcg_gen_and_i64(tmp, val, mask); if (rs) { @@ -3467,7 +3469,7 @@ static bool trans_be(DisasContext *ctx, arg_be *a) nullify_over(ctx); #endif - tmp = tcg_temp_new(); + tmp = tcg_temp_new_i64(); tcg_gen_addi_i64(tmp, load_gpr(ctx, a->b), a->disp); tmp = do_ibranch_priv(ctx, tmp); @@ -3565,7 +3567,7 @@ static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a) static bool trans_blr(DisasContext *ctx, arg_blr *a) { if (a->x) { - TCGv_i64 tmp = tcg_temp_new(); + TCGv_i64 tmp = tcg_temp_new_i64(); tcg_gen_shli_i64(tmp, load_gpr(ctx, a->x), 3); tcg_gen_addi_i64(tmp, tmp, ctx->iaoq_f + 8); /* The computation here never changes privilege level. */ @@ -3583,7 +3585,7 @@ static bool trans_bv(DisasContext *ctx, arg_bv *a) if (a->x == 0) { dest = load_gpr(ctx, a->b); } else { - dest = tcg_temp_new(); + dest = tcg_temp_new_i64(); tcg_gen_shli_i64(dest, load_gpr(ctx, a->x), 3); tcg_gen_add_i64(dest, dest, load_gpr(ctx, a->b)); } @@ -3920,7 +3922,7 @@ static bool trans_ftest(DisasContext *ctx, arg_ftest *a) nullify_over(ctx); - t = tcg_temp_new(); + t = tcg_temp_new_i64(); tcg_gen_ld32u_i64(t, tcg_env, offsetof(CPUHPPAState, fr0_shadow)); if (a->y == 1) { @@ -4222,7 +4224,7 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) This will be overwritten by a branch. */ if (ctx->iaoq_b == -1) { ctx->iaoq_n = -1; - ctx->iaoq_n_var = tcg_temp_new(); + ctx->iaoq_n_var = tcg_temp_new_i64(); tcg_gen_addi_i64(ctx->iaoq_n_var, cpu_iaoq_b, 4); } else { ctx->iaoq_n = ctx->iaoq_b + 4; From d265360f07b83f658431425a384a27c303feab9e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 17 Oct 2023 21:49:54 -0700 Subject: [PATCH 631/974] target/hppa: Replace tcg_gen_*_tl with tcg_gen_*_i64 Signed-off-by: Richard Henderson --- target/hppa/translate.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index c8c702ac03..1d749e2a78 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1338,10 +1338,10 @@ static void form_gva(DisasContext *ctx, TCGv_i64 *pgva, TCGv_i64 *pofs, *pofs = ofs; *pgva = addr = tcg_temp_new_i64(); - tcg_gen_andi_tl(addr, modify <= 0 ? ofs : base, gva_offset_mask(ctx)); + tcg_gen_andi_i64(addr, modify <= 0 ? ofs : base, gva_offset_mask(ctx)); #ifndef CONFIG_USER_ONLY if (!is_phys) { - tcg_gen_or_tl(addr, addr, space_select(ctx, sp, base)); + tcg_gen_or_i64(addr, addr, space_select(ctx, sp, base)); } #endif } @@ -2382,7 +2382,7 @@ static bool trans_ixtlbxf(DisasContext *ctx, arg_ixtlbxf *a) a->data ? offsetof(CPUHPPAState, cr[CR_IOR]) : offsetof(CPUHPPAState, cr[CR_IIAOQ])); tcg_gen_shli_i64(stl, stl, 32); - tcg_gen_or_tl(addr, atl, stl); + tcg_gen_or_i64(addr, atl, stl); reg = load_gpr(ctx, a->r); if (a->addr) { @@ -2942,7 +2942,7 @@ static bool trans_ldo(DisasContext *ctx, arg_ldo *a) TCGv_i64 tcg_rt = dest_gpr(ctx, a->t); /* Special case rb == 0, for the LDI pseudo-op. - The COPY pseudo-op is handled for free within tcg_gen_addi_tl. */ + The COPY pseudo-op is handled for free within tcg_gen_addi_i64. */ if (a->b == 0) { tcg_gen_movi_i64(tcg_rt, a->i); } else { From 0843563f3ef42db0ccaa1ac219439413f8083d34 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 20 Sep 2023 13:50:01 +0200 Subject: [PATCH 632/974] target/hppa: Implement HADD Signed-off-by: Richard Henderson --- target/hppa/helper.h | 3 +++ target/hppa/insns.decode | 8 +++++++- target/hppa/op_helper.c | 32 ++++++++++++++++++++++++++++++++ target/hppa/translate.c | 37 +++++++++++++++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 1 deletion(-) diff --git a/target/hppa/helper.h b/target/hppa/helper.h index 57ea5447b6..db662f0c33 100644 --- a/target/hppa/helper.h +++ b/target/hppa/helper.h @@ -14,6 +14,9 @@ DEF_HELPER_FLAGS_3(stdby_e_parallel, TCG_CALL_NO_WG, void, env, tl, tl) DEF_HELPER_FLAGS_1(ldc_check, TCG_CALL_NO_RWG, void, tl) +DEF_HELPER_FLAGS_2(hadd_ss, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(hadd_us, TCG_CALL_NO_RWG_SE, i64, i64, i64) + DEF_HELPER_FLAGS_4(probe, TCG_CALL_NO_WG, tl, env, tl, i32, i32) DEF_HELPER_FLAGS_1(loaded_fr0, TCG_CALL_NO_RWG, void, env) diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode index 820049b0c5..4bcfc94b1c 100644 --- a/target/hppa/insns.decode +++ b/target/hppa/insns.decode @@ -65,6 +65,7 @@ &ldst t b x disp sp m scale size &rr_cf_d t r cf d +&rrr t r1 r2 &rrr_cf t r1 r2 cf &rrr_cf_d t r1 r2 cf d &rrr_cf_d_sh t r1 r2 cf d sh @@ -81,6 +82,7 @@ #### @rr_cf_d ...... r:5 ..... cf:4 ...... d:1 t:5 &rr_cf_d +@rrr ...... r2:5 r1:5 .... ....... t:5 &rrr @rrr_cf ...... r2:5 r1:5 cf:4 ....... t:5 &rrr_cf @rrr_cf_d ...... r2:5 r1:5 cf:4 ...... d:1 t:5 &rrr_cf_d @rrr_cf_d_sh ...... r2:5 r1:5 cf:4 .... sh:2 d:1 t:5 &rrr_cf_d_sh @@ -208,6 +210,10 @@ subi_tsv 100101 ..... ..... .... 1 ........... @rri_cf cmpiclr 100100 ..... ..... .... . ........... @rri_cf_d +hadd 000010 ..... ..... 00000011 11 0 ..... @rrr +hadd_ss 000010 ..... ..... 00000011 01 0 ..... @rrr +hadd_us 000010 ..... ..... 00000011 00 0 ..... @rrr + #### # Index Mem #### @@ -429,7 +435,7 @@ fmpyfadd_d 101110 rm1:5 rm2:5 ... 0 1 ..0 0 0 neg:1 t:5 ra3=%rc32 @f0e_f_3 ...... ..... ..... ... .0 110 ..0 ..... \ &fclass3 r1=%ra64 r2=%rb64 t=%rt64 -@f0e_d_3 ...... r1:5 r2:5 ... 01 110 000 t:5 +@f0e_d_3 ...... r1:5 r2:5 ... 01 110 000 t:5 &fclass3 # Floating point class 0 diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c index a25e6df7e4..d93801ca47 100644 --- a/target/hppa/op_helper.c +++ b/target/hppa/op_helper.c @@ -377,3 +377,35 @@ target_ulong HELPER(read_interval_timer)(void) return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) >> 2; #endif } + +uint64_t HELPER(hadd_ss)(uint64_t r1, uint64_t r2) +{ + uint64_t ret = 0; + + for (int i = 0; i < 64; i += 16) { + int f1 = sextract64(r1, i, 16); + int f2 = sextract64(r2, i, 16); + int fr = f1 + f2; + + fr = MIN(fr, INT16_MAX); + fr = MAX(fr, INT16_MIN); + ret = deposit64(ret, i, 16, fr); + } + return ret; +} + +uint64_t HELPER(hadd_us)(uint64_t r1, uint64_t r2) +{ + uint64_t ret = 0; + + for (int i = 0; i < 64; i += 16) { + int f1 = extract64(r1, i, 16); + int f2 = sextract64(r2, i, 16); + int fr = f1 + f2; + + fr = MIN(fr, UINT16_MAX); + fr = MAX(fr, 0); + ret = deposit64(ret, i, 16, fr); + } + return ret; +} diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 1d749e2a78..dc8e37f99c 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -23,6 +23,7 @@ #include "qemu/host-utils.h" #include "exec/exec-all.h" #include "tcg/tcg-op.h" +#include "tcg/tcg-op-gvec.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" #include "exec/translator.h" @@ -2767,6 +2768,42 @@ static bool trans_cmpiclr(DisasContext *ctx, arg_rri_cf_d *a) return nullify_end(ctx); } +static bool do_multimedia(DisasContext *ctx, arg_rrr *a, + void (*fn)(TCGv_i64, TCGv_i64, TCGv_i64)) +{ + TCGv_i64 r1, r2, dest; + + if (!ctx->is_pa20) { + return false; + } + + nullify_over(ctx); + + r1 = load_gpr(ctx, a->r1); + r2 = load_gpr(ctx, a->r2); + dest = dest_gpr(ctx, a->t); + + fn(dest, r1, r2); + save_gpr(ctx, a->t, dest); + + return nullify_end(ctx); +} + +static bool trans_hadd(DisasContext *ctx, arg_rrr *a) +{ + return do_multimedia(ctx, a, tcg_gen_vec_add16_i64); +} + +static bool trans_hadd_ss(DisasContext *ctx, arg_rrr *a) +{ + return do_multimedia(ctx, a, gen_helper_hadd_ss); +} + +static bool trans_hadd_us(DisasContext *ctx, arg_rrr *a) +{ + return do_multimedia(ctx, a, gen_helper_hadd_us); +} + static bool trans_ld(DisasContext *ctx, arg_ldst *a) { if (!ctx->is_pa20 && a->size > MO_32) { From 10c9e58d5c317d8bc6a0a2b36e2f130bad6b8cea Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 20 Sep 2023 16:30:41 +0200 Subject: [PATCH 633/974] target/hppa: Implement HSUB Signed-off-by: Richard Henderson --- target/hppa/helper.h | 2 ++ target/hppa/insns.decode | 4 ++++ target/hppa/op_helper.c | 32 ++++++++++++++++++++++++++++++++ target/hppa/translate.c | 15 +++++++++++++++ 4 files changed, 53 insertions(+) diff --git a/target/hppa/helper.h b/target/hppa/helper.h index db662f0c33..64fd1ef1ef 100644 --- a/target/hppa/helper.h +++ b/target/hppa/helper.h @@ -16,6 +16,8 @@ DEF_HELPER_FLAGS_1(ldc_check, TCG_CALL_NO_RWG, void, tl) DEF_HELPER_FLAGS_2(hadd_ss, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(hadd_us, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(hsub_ss, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(hsub_us, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_4(probe, TCG_CALL_NO_WG, tl, env, tl, i32, i32) diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode index 4bcfc94b1c..29b49c6cf4 100644 --- a/target/hppa/insns.decode +++ b/target/hppa/insns.decode @@ -214,6 +214,10 @@ hadd 000010 ..... ..... 00000011 11 0 ..... @rrr hadd_ss 000010 ..... ..... 00000011 01 0 ..... @rrr hadd_us 000010 ..... ..... 00000011 00 0 ..... @rrr +hsub 000010 ..... ..... 00000001 11 0 ..... @rrr +hsub_ss 000010 ..... ..... 00000001 01 0 ..... @rrr +hsub_us 000010 ..... ..... 00000001 00 0 ..... @rrr + #### # Index Mem #### diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c index d93801ca47..de51905428 100644 --- a/target/hppa/op_helper.c +++ b/target/hppa/op_helper.c @@ -409,3 +409,35 @@ uint64_t HELPER(hadd_us)(uint64_t r1, uint64_t r2) } return ret; } + +uint64_t HELPER(hsub_ss)(uint64_t r1, uint64_t r2) +{ + uint64_t ret = 0; + + for (int i = 0; i < 64; i += 16) { + int f1 = sextract64(r1, i, 16); + int f2 = sextract64(r2, i, 16); + int fr = f1 - f2; + + fr = MIN(fr, INT16_MAX); + fr = MAX(fr, INT16_MIN); + ret = deposit64(ret, i, 16, fr); + } + return ret; +} + +uint64_t HELPER(hsub_us)(uint64_t r1, uint64_t r2) +{ + uint64_t ret = 0; + + for (int i = 0; i < 64; i += 16) { + int f1 = extract64(r1, i, 16); + int f2 = sextract64(r2, i, 16); + int fr = f1 - f2; + + fr = MIN(fr, UINT16_MAX); + fr = MAX(fr, 0); + ret = deposit64(ret, i, 16, fr); + } + return ret; +} diff --git a/target/hppa/translate.c b/target/hppa/translate.c index dc8e37f99c..e5a3873d25 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -2804,6 +2804,21 @@ static bool trans_hadd_us(DisasContext *ctx, arg_rrr *a) return do_multimedia(ctx, a, gen_helper_hadd_us); } +static bool trans_hsub(DisasContext *ctx, arg_rrr *a) +{ + return do_multimedia(ctx, a, tcg_gen_vec_sub16_i64); +} + +static bool trans_hsub_ss(DisasContext *ctx, arg_rrr *a) +{ + return do_multimedia(ctx, a, gen_helper_hsub_ss); +} + +static bool trans_hsub_us(DisasContext *ctx, arg_rrr *a) +{ + return do_multimedia(ctx, a, gen_helper_hsub_us); +} + static bool trans_ld(DisasContext *ctx, arg_ldst *a) { if (!ctx->is_pa20 && a->size > MO_32) { From 1b3cb7c874bd92d6022f4f99b16531f66148d905 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 20 Sep 2023 17:11:06 +0200 Subject: [PATCH 634/974] target/hppa: Implement HAVG Signed-off-by: Richard Henderson --- target/hppa/helper.h | 1 + target/hppa/insns.decode | 2 ++ target/hppa/op_helper.c | 14 ++++++++++++++ target/hppa/translate.c | 5 +++++ 4 files changed, 22 insertions(+) diff --git a/target/hppa/helper.h b/target/hppa/helper.h index 64fd1ef1ef..3b3a543216 100644 --- a/target/hppa/helper.h +++ b/target/hppa/helper.h @@ -16,6 +16,7 @@ DEF_HELPER_FLAGS_1(ldc_check, TCG_CALL_NO_RWG, void, tl) DEF_HELPER_FLAGS_2(hadd_ss, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(hadd_us, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(havg, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(hsub_ss, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(hsub_us, TCG_CALL_NO_RWG_SE, i64, i64, i64) diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode index 29b49c6cf4..6959555bf3 100644 --- a/target/hppa/insns.decode +++ b/target/hppa/insns.decode @@ -214,6 +214,8 @@ hadd 000010 ..... ..... 00000011 11 0 ..... @rrr hadd_ss 000010 ..... ..... 00000011 01 0 ..... @rrr hadd_us 000010 ..... ..... 00000011 00 0 ..... @rrr +havg 000010 ..... ..... 00000010 11 0 ..... @rrr + hsub 000010 ..... ..... 00000001 11 0 ..... @rrr hsub_ss 000010 ..... ..... 00000001 01 0 ..... @rrr hsub_us 000010 ..... ..... 00000001 00 0 ..... @rrr diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c index de51905428..e76f201472 100644 --- a/target/hppa/op_helper.c +++ b/target/hppa/op_helper.c @@ -410,6 +410,20 @@ uint64_t HELPER(hadd_us)(uint64_t r1, uint64_t r2) return ret; } +uint64_t HELPER(havg)(uint64_t r1, uint64_t r2) +{ + uint64_t ret = 0; + + for (int i = 0; i < 64; i += 16) { + int f1 = extract64(r1, i, 16); + int f2 = extract64(r2, i, 16); + int fr = f1 + f2; + + ret = deposit64(ret, i, 16, (fr >> 1) | (fr & 1)); + } + return ret; +} + uint64_t HELPER(hsub_ss)(uint64_t r1, uint64_t r2) { uint64_t ret = 0; diff --git a/target/hppa/translate.c b/target/hppa/translate.c index e5a3873d25..e20ce40fe3 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -2804,6 +2804,11 @@ static bool trans_hadd_us(DisasContext *ctx, arg_rrr *a) return do_multimedia(ctx, a, gen_helper_hadd_us); } +static bool trans_havg(DisasContext *ctx, arg_rrr *a) +{ + return do_multimedia(ctx, a, gen_helper_havg); +} + static bool trans_hsub(DisasContext *ctx, arg_rrr *a) { return do_multimedia(ctx, a, tcg_gen_vec_sub16_i64); From 151f309b989fd84bec0cbd5bc84dbe83bbe0b2f4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 21 Sep 2023 08:56:04 +0200 Subject: [PATCH 635/974] target/hppa: Implement HSHL, HSHR Signed-off-by: Richard Henderson --- target/hppa/insns.decode | 5 +++++ target/hppa/translate.c | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode index 6959555bf3..bb5cd267b0 100644 --- a/target/hppa/insns.decode +++ b/target/hppa/insns.decode @@ -69,6 +69,7 @@ &rrr_cf t r1 r2 cf &rrr_cf_d t r1 r2 cf d &rrr_cf_d_sh t r1 r2 cf d sh +&rri t r i &rri_cf t r i cf &rri_cf_d t r i cf d @@ -216,6 +217,10 @@ hadd_us 000010 ..... ..... 00000011 00 0 ..... @rrr havg 000010 ..... ..... 00000010 11 0 ..... @rrr +hshl 111110 00000 r:5 100010 i:4 0 t:5 &rri +hshr_s 111110 r:5 00000 110011 i:4 0 t:5 &rri +hshr_u 111110 r:5 00000 110010 i:4 0 t:5 &rri + hsub 000010 ..... ..... 00000001 11 0 ..... @rrr hsub_ss 000010 ..... ..... 00000001 01 0 ..... @rrr hsub_us 000010 ..... ..... 00000001 00 0 ..... @rrr diff --git a/target/hppa/translate.c b/target/hppa/translate.c index e20ce40fe3..a3a12d63f8 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -2789,6 +2789,26 @@ static bool do_multimedia(DisasContext *ctx, arg_rrr *a, return nullify_end(ctx); } +static bool do_multimedia_sh(DisasContext *ctx, arg_rri *a, + void (*fn)(TCGv_i64, TCGv_i64, int64_t)) +{ + TCGv_i64 r, dest; + + if (!ctx->is_pa20) { + return false; + } + + nullify_over(ctx); + + r = load_gpr(ctx, a->r); + dest = dest_gpr(ctx, a->t); + + fn(dest, r, a->i); + save_gpr(ctx, a->t, dest); + + return nullify_end(ctx); +} + static bool trans_hadd(DisasContext *ctx, arg_rrr *a) { return do_multimedia(ctx, a, tcg_gen_vec_add16_i64); @@ -2809,6 +2829,21 @@ static bool trans_havg(DisasContext *ctx, arg_rrr *a) return do_multimedia(ctx, a, gen_helper_havg); } +static bool trans_hshl(DisasContext *ctx, arg_rri *a) +{ + return do_multimedia_sh(ctx, a, tcg_gen_vec_shl16i_i64); +} + +static bool trans_hshr_s(DisasContext *ctx, arg_rri *a) +{ + return do_multimedia_sh(ctx, a, tcg_gen_vec_sar16i_i64); +} + +static bool trans_hshr_u(DisasContext *ctx, arg_rri *a) +{ + return do_multimedia_sh(ctx, a, tcg_gen_vec_shr16i_i64); +} + static bool trans_hsub(DisasContext *ctx, arg_rrr *a) { return do_multimedia(ctx, a, tcg_gen_vec_sub16_i64); From 3bbb8e4832b56cea29a61eb32cfb4931e00244c1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 21 Sep 2023 09:15:25 +0200 Subject: [PATCH 636/974] target/hppa: Implement HSHLADD, HSHRADD Signed-off-by: Richard Henderson --- target/hppa/helper.h | 2 ++ target/hppa/insns.decode | 12 ++++++++++-- target/hppa/op_helper.c | 32 ++++++++++++++++++++++++++++++++ target/hppa/translate.c | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 76 insertions(+), 2 deletions(-) diff --git a/target/hppa/helper.h b/target/hppa/helper.h index 3b3a543216..d586be3f15 100644 --- a/target/hppa/helper.h +++ b/target/hppa/helper.h @@ -17,6 +17,8 @@ DEF_HELPER_FLAGS_1(ldc_check, TCG_CALL_NO_RWG, void, tl) DEF_HELPER_FLAGS_2(hadd_ss, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(hadd_us, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(havg, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_3(hshladd, TCG_CALL_NO_RWG_SE, i64, i64, i64, i32) +DEF_HELPER_FLAGS_3(hshradd, TCG_CALL_NO_RWG_SE, i64, i64, i64, i32) DEF_HELPER_FLAGS_2(hsub_ss, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(hsub_us, TCG_CALL_NO_RWG_SE, i64, i64, i64) diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode index bb5cd267b0..87db726d9e 100644 --- a/target/hppa/insns.decode +++ b/target/hppa/insns.decode @@ -68,6 +68,7 @@ &rrr t r1 r2 &rrr_cf t r1 r2 cf &rrr_cf_d t r1 r2 cf d +&rrr_sh t r1 r2 sh &rrr_cf_d_sh t r1 r2 cf d sh &rri t r i &rri_cf t r i cf @@ -86,6 +87,7 @@ @rrr ...... r2:5 r1:5 .... ....... t:5 &rrr @rrr_cf ...... r2:5 r1:5 cf:4 ....... t:5 &rrr_cf @rrr_cf_d ...... r2:5 r1:5 cf:4 ...... d:1 t:5 &rrr_cf_d +@rrr_sh ...... r2:5 r1:5 ........ sh:2 . t:5 &rrr_sh @rrr_cf_d_sh ...... r2:5 r1:5 cf:4 .... sh:2 d:1 t:5 &rrr_cf_d_sh @rrr_cf_d_sh0 ...... r2:5 r1:5 cf:4 ...... d:1 t:5 &rrr_cf_d_sh sh=0 @rri_cf ...... r:5 t:5 cf:4 . ........... &rri_cf i=%lowsign_11 @@ -187,14 +189,20 @@ dcor_i 000010 ..... 00000 .... 101111 . ..... @rr_cf_d add 000010 ..... ..... .... 0110.. . ..... @rrr_cf_d_sh add_l 000010 ..... ..... .... 1010.. . ..... @rrr_cf_d_sh add_tsv 000010 ..... ..... .... 1110.. . ..... @rrr_cf_d_sh -add_c 000010 ..... ..... .... 011100 . ..... @rrr_cf_d_sh0 +{ + add_c 000010 ..... ..... .... 011100 . ..... @rrr_cf_d_sh0 + hshladd 000010 ..... ..... 0000 0111.. 0 ..... @rrr_sh +} add_c_tsv 000010 ..... ..... .... 111100 . ..... @rrr_cf_d_sh0 sub 000010 ..... ..... .... 010000 . ..... @rrr_cf_d sub_tsv 000010 ..... ..... .... 110000 . ..... @rrr_cf_d sub_tc 000010 ..... ..... .... 010011 . ..... @rrr_cf_d sub_tsv_tc 000010 ..... ..... .... 110011 . ..... @rrr_cf_d -sub_b 000010 ..... ..... .... 010100 . ..... @rrr_cf_d +{ + sub_b 000010 ..... ..... .... 010100 . ..... @rrr_cf_d + hshradd 000010 ..... ..... 0000 0101.. 0 ..... @rrr_sh +} sub_b_tsv 000010 ..... ..... .... 110100 . ..... @rrr_cf_d ldil 001000 t:5 ..................... i=%assemble_21 diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c index e76f201472..a0e31c0c25 100644 --- a/target/hppa/op_helper.c +++ b/target/hppa/op_helper.c @@ -455,3 +455,35 @@ uint64_t HELPER(hsub_us)(uint64_t r1, uint64_t r2) } return ret; } + +uint64_t HELPER(hshladd)(uint64_t r1, uint64_t r2, uint32_t sh) +{ + uint64_t ret = 0; + + for (int i = 0; i < 64; i += 16) { + int f1 = sextract64(r1, i, 16); + int f2 = sextract64(r2, i, 16); + int fr = (f1 << sh) + f2; + + fr = MIN(fr, INT16_MAX); + fr = MAX(fr, INT16_MIN); + ret = deposit64(ret, i, 16, fr); + } + return ret; +} + +uint64_t HELPER(hshradd)(uint64_t r1, uint64_t r2, uint32_t sh) +{ + uint64_t ret = 0; + + for (int i = 0; i < 64; i += 16) { + int f1 = sextract64(r1, i, 16); + int f2 = sextract64(r2, i, 16); + int fr = (f1 >> sh) + f2; + + fr = MIN(fr, INT16_MAX); + fr = MAX(fr, INT16_MIN); + ret = deposit64(ret, i, 16, fr); + } + return ret; +} diff --git a/target/hppa/translate.c b/target/hppa/translate.c index a3a12d63f8..648c37fb28 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -2809,6 +2809,28 @@ static bool do_multimedia_sh(DisasContext *ctx, arg_rri *a, return nullify_end(ctx); } +static bool do_multimedia_shadd(DisasContext *ctx, arg_rrr_sh *a, + void (*fn)(TCGv_i64, TCGv_i64, + TCGv_i64, TCGv_i32)) +{ + TCGv_i64 r1, r2, dest; + + if (!ctx->is_pa20) { + return false; + } + + nullify_over(ctx); + + r1 = load_gpr(ctx, a->r1); + r2 = load_gpr(ctx, a->r2); + dest = dest_gpr(ctx, a->t); + + fn(dest, r1, r2, tcg_constant_i32(a->sh)); + save_gpr(ctx, a->t, dest); + + return nullify_end(ctx); +} + static bool trans_hadd(DisasContext *ctx, arg_rrr *a) { return do_multimedia(ctx, a, tcg_gen_vec_add16_i64); @@ -2844,6 +2866,16 @@ static bool trans_hshr_u(DisasContext *ctx, arg_rri *a) return do_multimedia_sh(ctx, a, tcg_gen_vec_shr16i_i64); } +static bool trans_hshladd(DisasContext *ctx, arg_rrr_sh *a) +{ + return do_multimedia_shadd(ctx, a, gen_helper_hshladd); +} + +static bool trans_hshradd(DisasContext *ctx, arg_rrr_sh *a) +{ + return do_multimedia_shadd(ctx, a, gen_helper_hshradd); +} + static bool trans_hsub(DisasContext *ctx, arg_rrr *a) { return do_multimedia(ctx, a, tcg_gen_vec_sub16_i64); From c2a7ee3f9d7605f494d738825beef1b792bfb357 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 21 Sep 2023 09:37:10 +0200 Subject: [PATCH 637/974] target/hppa: Implement MIXH, MIXW Signed-off-by: Richard Henderson --- target/hppa/insns.decode | 5 ++++ target/hppa/translate.c | 55 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode index 87db726d9e..22ec07f892 100644 --- a/target/hppa/insns.decode +++ b/target/hppa/insns.decode @@ -233,6 +233,11 @@ hsub 000010 ..... ..... 00000001 11 0 ..... @rrr hsub_ss 000010 ..... ..... 00000001 01 0 ..... @rrr hsub_us 000010 ..... ..... 00000001 00 0 ..... @rrr +mixh_l 111110 ..... ..... 1 00 00100000 ..... @rrr +mixh_r 111110 ..... ..... 1 10 00100000 ..... @rrr +mixw_l 111110 ..... ..... 1 00 00000000 ..... @rrr +mixw_r 111110 ..... ..... 1 10 00000000 ..... @rrr + #### # Index Mem #### diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 648c37fb28..2b471444d0 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -2891,6 +2891,61 @@ static bool trans_hsub_us(DisasContext *ctx, arg_rrr *a) return do_multimedia(ctx, a, gen_helper_hsub_us); } +static void gen_mixh_l(TCGv_i64 dst, TCGv_i64 r1, TCGv_i64 r2) +{ + uint64_t mask = 0xffff0000ffff0000ull; + TCGv_i64 tmp = tcg_temp_new_i64(); + + tcg_gen_andi_i64(tmp, r2, mask); + tcg_gen_andi_i64(dst, r1, mask); + tcg_gen_shri_i64(tmp, tmp, 16); + tcg_gen_or_i64(dst, dst, tmp); +} + +static bool trans_mixh_l(DisasContext *ctx, arg_rrr *a) +{ + return do_multimedia(ctx, a, gen_mixh_l); +} + +static void gen_mixh_r(TCGv_i64 dst, TCGv_i64 r1, TCGv_i64 r2) +{ + uint64_t mask = 0x0000ffff0000ffffull; + TCGv_i64 tmp = tcg_temp_new_i64(); + + tcg_gen_andi_i64(tmp, r1, mask); + tcg_gen_andi_i64(dst, r2, mask); + tcg_gen_shli_i64(tmp, tmp, 16); + tcg_gen_or_i64(dst, dst, tmp); +} + +static bool trans_mixh_r(DisasContext *ctx, arg_rrr *a) +{ + return do_multimedia(ctx, a, gen_mixh_r); +} + +static void gen_mixw_l(TCGv_i64 dst, TCGv_i64 r1, TCGv_i64 r2) +{ + TCGv_i64 tmp = tcg_temp_new_i64(); + + tcg_gen_shri_i64(tmp, r2, 32); + tcg_gen_deposit_i64(dst, r1, tmp, 0, 32); +} + +static bool trans_mixw_l(DisasContext *ctx, arg_rrr *a) +{ + return do_multimedia(ctx, a, gen_mixw_l); +} + +static void gen_mixw_r(TCGv_i64 dst, TCGv_i64 r1, TCGv_i64 r2) +{ + tcg_gen_deposit_i64(dst, r2, r1, 32, 32); +} + +static bool trans_mixw_r(DisasContext *ctx, arg_rrr *a) +{ + return do_multimedia(ctx, a, gen_mixw_r); +} + static bool trans_ld(DisasContext *ctx, arg_ldst *a) { if (!ctx->is_pa20 && a->size > MO_32) { From 4e7abdb120d7456aaa754a7101ef43a5916ed8a0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 21 Sep 2023 12:07:22 +0200 Subject: [PATCH 638/974] target/hppa: Implement PERMH Signed-off-by: Richard Henderson --- target/hppa/insns.decode | 2 ++ target/hppa/translate.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode index 22ec07f892..19e537df24 100644 --- a/target/hppa/insns.decode +++ b/target/hppa/insns.decode @@ -238,6 +238,8 @@ mixh_r 111110 ..... ..... 1 10 00100000 ..... @rrr mixw_l 111110 ..... ..... 1 00 00000000 ..... @rrr mixw_r 111110 ..... ..... 1 10 00000000 ..... @rrr +permh 111110 r1:5 r2:5 0 c0:2 0 c1:2 c2:2 c3:2 0 t:5 + #### # Index Mem #### diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 2b471444d0..ffdd306d31 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -2946,6 +2946,35 @@ static bool trans_mixw_r(DisasContext *ctx, arg_rrr *a) return do_multimedia(ctx, a, gen_mixw_r); } +static bool trans_permh(DisasContext *ctx, arg_permh *a) +{ + TCGv_i64 r, t0, t1, t2, t3; + + if (!ctx->is_pa20) { + return false; + } + + nullify_over(ctx); + + r = load_gpr(ctx, a->r1); + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + t3 = tcg_temp_new_i64(); + + tcg_gen_extract_i64(t0, r, (3 - a->c0) * 16, 16); + tcg_gen_extract_i64(t1, r, (3 - a->c1) * 16, 16); + tcg_gen_extract_i64(t2, r, (3 - a->c2) * 16, 16); + tcg_gen_extract_i64(t3, r, (3 - a->c3) * 16, 16); + + tcg_gen_deposit_i64(t0, t1, t0, 16, 48); + tcg_gen_deposit_i64(t2, t3, t2, 16, 48); + tcg_gen_deposit_i64(t0, t2, t0, 32, 32); + + save_gpr(ctx, a->t, t0); + return nullify_end(ctx); +} + static bool trans_ld(DisasContext *ctx, arg_ldst *a) { if (!ctx->is_pa20 && a->size > MO_32) { From ab9af359c175378c6aa716de0f8e2acbcbcba376 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Tue, 17 Oct 2023 11:36:37 +0200 Subject: [PATCH 639/974] target/hppa: Fix interruption based on default PSW The default PSW is set by the operating system with the PDC_PSW firmware call. Use that setting to decide if wide mode is to be enabled for interruptions and EIRR usage. Signed-off-by: Helge Deller Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson --- target/hppa/cpu.h | 2 ++ target/hppa/int_helper.c | 18 ++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index ea676ba062..ea8e7e99a4 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -137,6 +137,8 @@ #define PSW_SM_W 0x200 /* PA2.0 only : Enable Wide Mode */ #define CR_RC 0 +#define CR_PSW_DEFAULT 6 /* see SeaBIOS PDC_PSW firmware call */ +#define PDC_PSW_WIDE_BIT 2 #define CR_PID1 8 #define CR_PID2 9 #define CR_PID3 12 diff --git a/target/hppa/int_helper.c b/target/hppa/int_helper.c index f355c4c76b..a11d607b31 100644 --- a/target/hppa/int_helper.c +++ b/target/hppa/int_helper.c @@ -52,9 +52,17 @@ static void io_eir_write(void *opaque, hwaddr addr, uint64_t data, unsigned size) { HPPACPU *cpu = opaque; - int le_bit = ~data & 31; + CPUHPPAState *env = &cpu->env; + int widthm1 = 31; + int le_bit; - cpu->env.cr[CR_EIRR] |= (target_ulong)1 << le_bit; + /* The default PSW.W controls the width of EIRR. */ + if (hppa_is_pa20(env) && env->cr[CR_PSW_DEFAULT] & PDC_PSW_WIDE_BIT) { + widthm1 = 63; + } + le_bit = ~data & widthm1; + + env->cr[CR_EIRR] |= 1ull << le_bit; eval_interrupt(cpu); } @@ -104,8 +112,10 @@ void hppa_cpu_do_interrupt(CPUState *cs) /* step 1 */ env->cr[CR_IPSW] = old_psw = cpu_hppa_get_psw(env); - /* step 2 -- note PSW_W == 0 for !HPPA64. */ - cpu_hppa_put_psw(env, PSW_W | (i == EXCP_HPMC ? PSW_M : 0)); + /* step 2 -- Note PSW_W is masked out again for pa1.x */ + cpu_hppa_put_psw(env, + (env->cr[CR_PSW_DEFAULT] & PDC_PSW_WIDE_BIT ? PSW_W : 0) | + (i == EXCP_HPMC ? PSW_M : 0)); /* step 3 */ env->cr[CR_IIASQ] = iasq_f >> 32; From a4db4a7811e1be790012ab000707c40004f237e0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 18 Oct 2023 10:24:33 -0700 Subject: [PATCH 640/974] target/hppa: Precompute zero into DisasContext MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reduce the number of times we look for the constant 0. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hppa/translate.c | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index ffdd306d31..b04a5bc444 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -53,6 +53,8 @@ typedef struct DisasContext { DisasCond null_cond; TCGLabel *null_lab; + TCGv_i64 zero; + uint32_t insn; uint32_t tb_flags; int mmu_idx; @@ -1017,14 +1019,13 @@ static void do_add(DisasContext *ctx, unsigned rt, TCGv_i64 in1, } if (!is_l || cond_need_cb(c)) { - TCGv_i64 zero = tcg_constant_i64(0); cb_msb = tcg_temp_new_i64(); cb = tcg_temp_new_i64(); - tcg_gen_add2_i64(dest, cb_msb, in1, zero, in2, zero); + tcg_gen_add2_i64(dest, cb_msb, in1, ctx->zero, in2, ctx->zero); if (is_c) { tcg_gen_add2_i64(dest, cb_msb, dest, cb_msb, - get_psw_carry(ctx, d), zero); + get_psw_carry(ctx, d), ctx->zero); } tcg_gen_xor_i64(cb, in1, in2); tcg_gen_xor_i64(cb, cb, dest); @@ -1102,7 +1103,7 @@ static void do_sub(DisasContext *ctx, unsigned rt, TCGv_i64 in1, TCGv_i64 in2, bool is_tsv, bool is_b, bool is_tc, unsigned cf, bool d) { - TCGv_i64 dest, sv, cb, cb_msb, zero, tmp; + TCGv_i64 dest, sv, cb, cb_msb, tmp; unsigned c = cf >> 1; DisasCond cond; @@ -1110,12 +1111,12 @@ static void do_sub(DisasContext *ctx, unsigned rt, TCGv_i64 in1, cb = tcg_temp_new_i64(); cb_msb = tcg_temp_new_i64(); - zero = tcg_constant_i64(0); if (is_b) { /* DEST,C = IN1 + ~IN2 + C. */ tcg_gen_not_i64(cb, in2); - tcg_gen_add2_i64(dest, cb_msb, in1, zero, get_psw_carry(ctx, d), zero); - tcg_gen_add2_i64(dest, cb_msb, dest, cb_msb, cb, zero); + tcg_gen_add2_i64(dest, cb_msb, in1, ctx->zero, + get_psw_carry(ctx, d), ctx->zero); + tcg_gen_add2_i64(dest, cb_msb, dest, cb_msb, cb, ctx->zero); tcg_gen_xor_i64(cb, cb, in1); tcg_gen_xor_i64(cb, cb, dest); } else { @@ -1124,7 +1125,7 @@ static void do_sub(DisasContext *ctx, unsigned rt, TCGv_i64 in1, * operations by seeding the high word with 1 and subtracting. */ TCGv_i64 one = tcg_constant_i64(1); - tcg_gen_sub2_i64(dest, cb_msb, in1, one, in2, zero); + tcg_gen_sub2_i64(dest, cb_msb, in1, one, in2, ctx->zero); tcg_gen_eqv_i64(cb, in1, in2); tcg_gen_xor_i64(cb, cb, dest); } @@ -2458,7 +2459,7 @@ static bool trans_lci(DisasContext *ctx, arg_lci *a) physical address. Two addresses with the same CI have a coherent view of the cache. Our implementation is to return 0 for all, since the entire address space is coherent. */ - save_gpr(ctx, a->t, tcg_constant_i64(0)); + save_gpr(ctx, a->t, ctx->zero); cond_free(&ctx->null_cond); return true; @@ -2667,7 +2668,7 @@ static bool trans_dcor_i(DisasContext *ctx, arg_rr_cf_d *a) static bool trans_ds(DisasContext *ctx, arg_rrr_cf *a) { - TCGv_i64 dest, add1, add2, addc, zero, in1, in2; + TCGv_i64 dest, add1, add2, addc, in1, in2; TCGv_i64 cout; nullify_over(ctx); @@ -2679,7 +2680,6 @@ static bool trans_ds(DisasContext *ctx, arg_rrr_cf *a) add2 = tcg_temp_new_i64(); addc = tcg_temp_new_i64(); dest = tcg_temp_new_i64(); - zero = tcg_constant_i64(0); /* Form R1 << 1 | PSW[CB]{8}. */ tcg_gen_add_i64(add1, in1, in1); @@ -2695,8 +2695,9 @@ static bool trans_ds(DisasContext *ctx, arg_rrr_cf *a) tcg_gen_xor_i64(add2, in2, addc); tcg_gen_andi_i64(addc, addc, 1); - tcg_gen_add2_i64(dest, cpu_psw_cb_msb, add1, zero, add2, zero); - tcg_gen_add2_i64(dest, cpu_psw_cb_msb, dest, cpu_psw_cb_msb, addc, zero); + tcg_gen_add2_i64(dest, cpu_psw_cb_msb, add1, ctx->zero, add2, ctx->zero); + tcg_gen_add2_i64(dest, cpu_psw_cb_msb, dest, cpu_psw_cb_msb, + addc, ctx->zero); /* Write back the result register. */ save_gpr(ctx, a->t, dest); @@ -2996,7 +2997,7 @@ static bool trans_st(DisasContext *ctx, arg_ldst *a) static bool trans_ldc(DisasContext *ctx, arg_ldst *a) { MemOp mop = MO_TE | MO_ALIGN | a->size; - TCGv_i64 zero, dest, ofs; + TCGv_i64 dest, ofs; TCGv_i64 addr; if (!ctx->is_pa20 && a->size > MO_32) { @@ -3026,8 +3027,7 @@ static bool trans_ldc(DisasContext *ctx, arg_ldst *a) */ gen_helper_ldc_check(addr); - zero = tcg_constant_i64(0); - tcg_gen_atomic_xchg_i64(dest, addr, zero, ctx->mmu_idx, mop); + tcg_gen_atomic_xchg_i64(dest, addr, ctx->zero, ctx->mmu_idx, mop); if (a->m) { save_gpr(ctx, a->b, ofs); @@ -4383,6 +4383,8 @@ static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->iaoq_n = -1; ctx->iaoq_n_var = NULL; + ctx->zero = tcg_constant_i64(0); + /* Bound the number of instructions by those left on the page. */ bound = -(ctx->base.pc_first | TARGET_PAGE_MASK) / 4; ctx->base.max_insns = MIN(ctx->base.max_insns, bound); From bc3da3cf6237dea2d91affe2116529d4c580c288 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 18 Oct 2023 10:34:13 -0700 Subject: [PATCH 641/974] target/hppa: Return zero for r0 from load_gpr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hppa/translate.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index b04a5bc444..ba15cf6ab8 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -307,9 +307,7 @@ static void cond_free(DisasCond *cond) static TCGv_i64 load_gpr(DisasContext *ctx, unsigned reg) { if (reg == 0) { - TCGv_i64 t = tcg_temp_new_i64(); - tcg_gen_movi_i64(t, 0); - return t; + return ctx->zero; } else { return cpu_gr[reg]; } From e1fee58fea3a8d215029269235d82cf6f79c1749 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 19 Sep 2023 16:25:54 +0200 Subject: [PATCH 642/974] include/hw/elf: Remove truncating signed casts There's nothing about elf that specifically requires signed vs unsigned. This is very much a target-specific preference. In the meantime, casting low and high from uint64_t back to Elf_SWord to uint64_t discards high bits that might have been set by translate_fn. Signed-off-by: Richard Henderson --- include/hw/elf_ops.h | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/include/hw/elf_ops.h b/include/hw/elf_ops.h index dffb0e73d2..0a5c258fe6 100644 --- a/include/hw/elf_ops.h +++ b/include/hw/elf_ops.h @@ -385,10 +385,11 @@ static ssize_t glue(load_elf, SZ)(const char *name, int fd, } if (pflags) { - *pflags = (elf_word)ehdr.e_flags; + *pflags = ehdr.e_flags; + } + if (pentry) { + *pentry = ehdr.e_entry; } - if (pentry) - *pentry = (uint64_t)(elf_sword)ehdr.e_entry; glue(load_symbols, SZ)(&ehdr, fd, must_swab, clear_lsb, sym_cb); @@ -610,10 +611,12 @@ static ssize_t glue(load_elf, SZ)(const char *name, int fd, } } - if (lowaddr) - *lowaddr = (uint64_t)(elf_sword)low; - if (highaddr) - *highaddr = (uint64_t)(elf_sword)high; + if (lowaddr) { + *lowaddr = low; + } + if (highaddr) { + *highaddr = high; + } ret = total_size; fail: if (mapped_file) { From f386a16e45867c4476a5bbbc06cfa1ebf91297f8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 17 Sep 2023 18:17:31 -0700 Subject: [PATCH 643/974] hw/hppa: Translate phys addresses for the cpu Hack the machine to use pa2.0 physical layout when required, using the PSW.W=0 absolute to physical mapping. Signed-off-by: Richard Henderson --- hw/hppa/machine.c | 117 ++++++++++++++++++++++++++++------------------ 1 file changed, 71 insertions(+), 46 deletions(-) diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index 1f09b4b490..43c7afb89d 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -87,7 +87,7 @@ static const MemoryRegionOps hppa_pci_ignore_ops = { }, }; -static ISABus *hppa_isa_bus(void) +static ISABus *hppa_isa_bus(hwaddr addr) { ISABus *isa_bus; qemu_irq *isa_irqs; @@ -96,8 +96,7 @@ static ISABus *hppa_isa_bus(void) isa_region = g_new(MemoryRegion, 1); memory_region_init_io(isa_region, NULL, &hppa_pci_ignore_ops, NULL, "isa-io", 0x800); - memory_region_add_subregion(get_system_memory(), IDE_HPA, - isa_region); + memory_region_add_subregion(get_system_memory(), addr, isa_region); isa_bus = isa_bus_new(NULL, get_system_memory(), isa_region, &error_abort); @@ -163,13 +162,24 @@ static const MemoryRegionOps hppa_io_helper_ops = { }, }; +typedef uint64_t TranslateFn(void *opaque, uint64_t addr); -static uint64_t cpu_hppa_to_phys(void *opaque, uint64_t addr) +static uint64_t linux_kernel_virt_to_phys(void *opaque, uint64_t addr) { addr &= (0x10000000 - 1); return addr; } +static uint64_t translate_pa10(void *dummy, uint64_t addr) +{ + return (uint32_t)addr; +} + +static uint64_t translate_pa20(void *dummy, uint64_t addr) +{ + return hppa_abs_to_phys_pa2_w0(addr); +} + static HPPACPU *cpu[HPPA_MAX_CPUS]; static uint64_t firmware_entry; @@ -179,7 +189,8 @@ static void fw_cfg_boot_set(void *opaque, const char *boot_device, fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]); } -static FWCfgState *create_fw_cfg(MachineState *ms, PCIBus *pci_bus) +static FWCfgState *create_fw_cfg(MachineState *ms, PCIBus *pci_bus, + hwaddr addr) { FWCfgState *fw_cfg; uint64_t val; @@ -188,7 +199,7 @@ static FWCfgState *create_fw_cfg(MachineState *ms, PCIBus *pci_bus) int btlb_entries = HPPA_BTLB_ENTRIES(&cpu[0]->env); int len; - fw_cfg = fw_cfg_init_mem(FW_CFG_IO_BASE, FW_CFG_IO_BASE + 4); + fw_cfg = fw_cfg_init_mem(addr, addr + 4); fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, ms->smp.cpus); fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, HPPA_MAX_CPUS); fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, ms->ram_size); @@ -258,32 +269,45 @@ static DinoState *dino_init(MemoryRegion *addr_space) /* * Step 1: Create CPUs and Memory */ -static void machine_HP_common_init_cpus(MachineState *machine) +static TranslateFn *machine_HP_common_init_cpus(MachineState *machine) { MemoryRegion *addr_space = get_system_memory(); - MemoryRegion *cpu_region; - long i; unsigned int smp_cpus = machine->smp.cpus; - char *name; + TranslateFn *translate; + MemoryRegion *cpu_region; /* Create CPUs. */ - for (i = 0; i < smp_cpus; i++) { - name = g_strdup_printf("cpu%ld-io-eir", i); + for (unsigned int i = 0; i < smp_cpus; i++) { cpu[i] = HPPA_CPU(cpu_create(machine->cpu_type)); + } + + /* + * For now, treat address layout as if PSW_W is clear. + * TODO: create a proper hppa64 board model and load elf64 firmware. + */ + if (hppa_is_pa20(&cpu[0]->env)) { + translate = translate_pa20; + } else { + translate = translate_pa10; + } + + for (unsigned int i = 0; i < smp_cpus; i++) { + g_autofree char *name = g_strdup_printf("cpu%u-io-eir", i); cpu_region = g_new(MemoryRegion, 1); memory_region_init_io(cpu_region, OBJECT(cpu[i]), &hppa_io_eir_ops, cpu[i], name, 4); - memory_region_add_subregion(addr_space, CPU_HPA + i * 0x1000, + memory_region_add_subregion(addr_space, + translate(NULL, CPU_HPA + i * 0x1000), cpu_region); - g_free(name); } /* RTC and DebugOutputPort on CPU #0 */ cpu_region = g_new(MemoryRegion, 1); memory_region_init_io(cpu_region, OBJECT(cpu[0]), &hppa_io_helper_ops, cpu[0], "cpu0-io-rtc", 2 * sizeof(uint64_t)); - memory_region_add_subregion(addr_space, CPU_HPA + 16, cpu_region); + memory_region_add_subregion(addr_space, translate(NULL, CPU_HPA + 16), + cpu_region); /* Main memory region. */ if (machine->ram_size > 3 * GiB) { @@ -291,12 +315,15 @@ static void machine_HP_common_init_cpus(MachineState *machine) exit(EXIT_FAILURE); } memory_region_add_subregion_overlap(addr_space, 0, machine->ram, -1); + + return translate; } /* * Last creation step: Add SCSI discs, NICs, graphics & load firmware */ -static void machine_HP_common_init_tail(MachineState *machine, PCIBus *pci_bus) +static void machine_HP_common_init_tail(MachineState *machine, PCIBus *pci_bus, + TranslateFn *translate) { const char *kernel_filename = machine->kernel_filename; const char *kernel_cmdline = machine->kernel_cmdline; @@ -324,13 +351,13 @@ static void machine_HP_common_init_tail(MachineState *machine, PCIBus *pci_bus) dev = qdev_new("artist"); s = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(s, &error_fatal); - sysbus_mmio_map(s, 0, LASI_GFX_HPA); - sysbus_mmio_map(s, 1, ARTIST_FB_ADDR); + sysbus_mmio_map(s, 0, translate(NULL, LASI_GFX_HPA)); + sysbus_mmio_map(s, 1, translate(NULL, ARTIST_FB_ADDR)); } /* Network setup. */ if (enable_lasi_lan()) { - lasi_82596_init(addr_space, LASI_LAN_HPA, + lasi_82596_init(addr_space, translate(NULL, LASI_LAN_HPA), qdev_get_gpio_in(lasi_dev, LASI_IRQ_LAN_HPA)); } @@ -374,7 +401,7 @@ static void machine_HP_common_init_tail(MachineState *machine, PCIBus *pci_bus) qemu_register_powerdown_notifier(&hppa_system_powerdown_notifier); /* fw_cfg configuration interface */ - create_fw_cfg(machine, pci_bus); + create_fw_cfg(machine, pci_bus, translate(NULL, FW_CFG_IO_BASE)); /* Load firmware. Given that this is not "real" firmware, but one explicitly written for the emulation, we might as @@ -386,15 +413,10 @@ static void machine_HP_common_init_tail(MachineState *machine, PCIBus *pci_bus) exit(1); } - size = load_elf(firmware_filename, NULL, NULL, NULL, + size = load_elf(firmware_filename, NULL, translate, NULL, &firmware_entry, &firmware_low, &firmware_high, NULL, true, EM_PARISC, 0, 0); - /* Unfortunately, load_elf sign-extends reading elf32. */ - firmware_entry = (uint32_t)firmware_entry; - firmware_low = (uint32_t)firmware_low; - firmware_high = (uint32_t)firmware_high; - if (size < 0) { error_report("could not load firmware '%s'", firmware_filename); exit(1); @@ -402,7 +424,8 @@ static void machine_HP_common_init_tail(MachineState *machine, PCIBus *pci_bus) qemu_log_mask(CPU_LOG_PAGE, "Firmware loaded at 0x%08" PRIx64 "-0x%08" PRIx64 ", entry at 0x%08" PRIx64 ".\n", firmware_low, firmware_high, firmware_entry); - if (firmware_low < FIRMWARE_START || firmware_high >= FIRMWARE_END) { + if (firmware_low < translate(NULL, FIRMWARE_START) || + firmware_high >= translate(NULL, FIRMWARE_END)) { error_report("Firmware overlaps with memory or IO space"); exit(1); } @@ -411,18 +434,16 @@ static void machine_HP_common_init_tail(MachineState *machine, PCIBus *pci_bus) rom_region = g_new(MemoryRegion, 1); memory_region_init_ram(rom_region, NULL, "firmware", (FIRMWARE_END - FIRMWARE_START), &error_fatal); - memory_region_add_subregion(addr_space, FIRMWARE_START, rom_region); + memory_region_add_subregion(addr_space, + translate(NULL, FIRMWARE_START), rom_region); /* Load kernel */ if (kernel_filename) { - size = load_elf(kernel_filename, NULL, &cpu_hppa_to_phys, + size = load_elf(kernel_filename, NULL, linux_kernel_virt_to_phys, NULL, &kernel_entry, &kernel_low, &kernel_high, NULL, true, EM_PARISC, 0, 0); - /* Unfortunately, load_elf sign-extends reading elf32. */ - kernel_entry = (uint32_t) cpu_hppa_to_phys(NULL, kernel_entry); - kernel_low = (uint32_t)kernel_low; - kernel_high = (uint32_t)kernel_high; + kernel_entry = linux_kernel_virt_to_phys(NULL, kernel_entry); if (size < 0) { error_report("could not load kernel '%s'", kernel_filename); @@ -500,41 +521,42 @@ static void machine_HP_B160L_init(MachineState *machine) { DeviceState *dev, *dino_dev; MemoryRegion *addr_space = get_system_memory(); + TranslateFn *translate; ISABus *isa_bus; PCIBus *pci_bus; /* Create CPUs and RAM. */ - machine_HP_common_init_cpus(machine); + translate = machine_HP_common_init_cpus(machine); /* Init Lasi chip */ lasi_dev = DEVICE(lasi_init()); - memory_region_add_subregion(addr_space, LASI_HPA, + memory_region_add_subregion(addr_space, translate(NULL, LASI_HPA), sysbus_mmio_get_region( SYS_BUS_DEVICE(lasi_dev), 0)); /* Init Dino (PCI host bus chip). */ dino_dev = DEVICE(dino_init(addr_space)); - memory_region_add_subregion(addr_space, DINO_HPA, + memory_region_add_subregion(addr_space, translate(NULL, DINO_HPA), sysbus_mmio_get_region( SYS_BUS_DEVICE(dino_dev), 0)); pci_bus = PCI_BUS(qdev_get_child_bus(dino_dev, "pci")); assert(pci_bus); /* Create ISA bus, needed for PS/2 kbd/mouse port emulation */ - isa_bus = hppa_isa_bus(); + isa_bus = hppa_isa_bus(translate(NULL, IDE_HPA)); assert(isa_bus); /* Serial ports: Lasi and Dino use a 7.272727 MHz clock. */ - serial_mm_init(addr_space, LASI_UART_HPA + 0x800, 0, + serial_mm_init(addr_space, translate(NULL, LASI_UART_HPA + 0x800), 0, qdev_get_gpio_in(lasi_dev, LASI_IRQ_UART_HPA), 7272727 / 16, serial_hd(0), DEVICE_BIG_ENDIAN); - serial_mm_init(addr_space, DINO_UART_HPA + 0x800, 0, + serial_mm_init(addr_space, translate(NULL, DINO_UART_HPA + 0x800), 0, qdev_get_gpio_in(dino_dev, DINO_IRQ_RS232INT), 7272727 / 16, serial_hd(1), DEVICE_BIG_ENDIAN); /* Parallel port */ - parallel_mm_init(addr_space, LASI_LPT_HPA + 0x800, 0, + parallel_mm_init(addr_space, translate(NULL, LASI_LPT_HPA + 0x800), 0, qdev_get_gpio_in(lasi_dev, LASI_IRQ_LAN_HPA), parallel_hds[0]); @@ -543,15 +565,17 @@ static void machine_HP_B160L_init(MachineState *machine) sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(lasi_dev, LASI_IRQ_PS2KBD_HPA)); - memory_region_add_subregion(addr_space, LASI_PS2KBD_HPA, + memory_region_add_subregion(addr_space, + translate(NULL, LASI_PS2KBD_HPA), sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0)); - memory_region_add_subregion(addr_space, LASI_PS2KBD_HPA + 0x100, + memory_region_add_subregion(addr_space, + translate(NULL, LASI_PS2KBD_HPA + 0x100), sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1)); /* Add SCSI discs, NICs, graphics & load firmware */ - machine_HP_common_init_tail(machine, pci_bus); + machine_HP_common_init_tail(machine, pci_bus, translate); } static AstroState *astro_init(void) @@ -573,21 +597,22 @@ static void machine_HP_C3700_init(MachineState *machine) AstroState *astro; DeviceState *astro_dev; MemoryRegion *addr_space = get_system_memory(); + TranslateFn *translate; /* Create CPUs and RAM. */ - machine_HP_common_init_cpus(machine); + translate = machine_HP_common_init_cpus(machine); /* Init Astro and the Elroys (PCI host bus chips). */ astro = astro_init(); astro_dev = DEVICE(astro); - memory_region_add_subregion(addr_space, ASTRO_HPA, + memory_region_add_subregion(addr_space, translate(NULL, ASTRO_HPA), sysbus_mmio_get_region( SYS_BUS_DEVICE(astro_dev), 0)); pci_bus = PCI_BUS(qdev_get_child_bus(DEVICE(astro->elroy[0]), "pci")); assert(pci_bus); /* Add SCSI discs, NICs, graphics & load firmware */ - machine_HP_common_init_tail(machine, pci_bus); + machine_HP_common_init_tail(machine, pci_bus, translate); } static void hppa_machine_reset(MachineState *ms, ShutdownCause reason) From 54111124fb9097635ec784131ceec26798e2ba77 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 20 Oct 2023 13:07:21 -0700 Subject: [PATCH 644/974] linux-user/hppa: Drop EXCP_DUMP from handled exceptions Signed-off-by: Richard Henderson --- linux-user/hppa/cpu_loop.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/linux-user/hppa/cpu_loop.c b/linux-user/hppa/cpu_loop.c index 8ab1335106..d5232f37fe 100644 --- a/linux-user/hppa/cpu_loop.c +++ b/linux-user/hppa/cpu_loop.c @@ -147,12 +147,10 @@ void cpu_loop(CPUHPPAState *env) force_sig_fault(TARGET_SIGSEGV, TARGET_SEGV_MAPERR, env->iaoq_f); break; case EXCP_ILL: - EXCP_DUMP(env, "qemu: EXCP_ILL exception %#x\n", trapnr); force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, env->iaoq_f); break; case EXCP_PRIV_OPR: /* check for glibc ABORT_INSTRUCTION "iitlbp %r0,(%sr0, %r0)" */ - EXCP_DUMP(env, "qemu: EXCP_PRIV_OPR exception %#x\n", trapnr); if (env->cr[CR_IIR] == 0x04000000) { force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, env->iaoq_f); } else { @@ -160,7 +158,6 @@ void cpu_loop(CPUHPPAState *env) } break; case EXCP_PRIV_REG: - EXCP_DUMP(env, "qemu: EXCP_PRIV_REG exception %#x\n", trapnr); force_sig_fault(TARGET_SIGILL, TARGET_ILL_PRVREG, env->iaoq_f); break; case EXCP_OVERFLOW: @@ -173,7 +170,6 @@ void cpu_loop(CPUHPPAState *env) force_sig_fault(TARGET_SIGFPE, 0, env->iaoq_f); break; case EXCP_BREAK: - EXCP_DUMP(env, "qemu: EXCP_BREAK exception %#x\n", trapnr); force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->iaoq_f & ~3); break; case EXCP_DEBUG: From b5caa17cdaf153fca500cf8bb0fa3a14c02def6e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 26 Oct 2023 20:46:13 -0700 Subject: [PATCH 645/974] target/hppa: Implement pa2.0 data prefetch instructions These are aliased onto the normal integer loads to %g0. Since we don't emulate caches, prefetch is a nop. Signed-off-by: Richard Henderson --- target/hppa/translate.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index ba15cf6ab8..e7f379d648 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -2976,7 +2976,15 @@ static bool trans_permh(DisasContext *ctx, arg_permh *a) static bool trans_ld(DisasContext *ctx, arg_ldst *a) { - if (!ctx->is_pa20 && a->size > MO_32) { + if (ctx->is_pa20) { + /* + * With pa20, LDB, LDH, LDW, LDD to %g0 are prefetches. + * Any base modification still occurs. + */ + if (a->t == 0) { + return trans_nop_addrx(ctx, a); + } + } else if (a->size > MO_32) { return gen_illegal(ctx); } return do_load(ctx, a->t, a->b, a->x, a->scale ? a->size : 0, From eb25d10f4d601f29169c876f9463e37db674b132 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Thu, 26 Oct 2023 21:41:41 -0700 Subject: [PATCH 646/974] target/hppa: Add pa2.0 cpu local tlb flushes The previous decoding misnamed the bit it called "local". Other than the name, the implementation was correct for pa1.x. Rename this field to "tlbe". PA2.0 adds (a real) local bit to PxTLB, and also adds a range of pages to flush in GR[b]. Signed-off-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/helper.h | 1 + target/hppa/insns.decode | 20 ++++++++++++++--- target/hppa/mem_helper.c | 26 ++++++++++++++++++---- target/hppa/trace-events | 1 + target/hppa/translate.c | 48 +++++++++++++++++++++++++++++++++++----- 5 files changed, 84 insertions(+), 12 deletions(-) diff --git a/target/hppa/helper.h b/target/hppa/helper.h index d586be3f15..20698f68ed 100644 --- a/target/hppa/helper.h +++ b/target/hppa/helper.h @@ -98,6 +98,7 @@ DEF_HELPER_FLAGS_3(itlbp_pa11, TCG_CALL_NO_RWG, void, env, tl, tl) DEF_HELPER_FLAGS_3(idtlbt_pa20, TCG_CALL_NO_RWG, void, env, tl, tl) DEF_HELPER_FLAGS_3(iitlbt_pa20, TCG_CALL_NO_RWG, void, env, tl, tl) DEF_HELPER_FLAGS_2(ptlb, TCG_CALL_NO_RWG, void, env, tl) +DEF_HELPER_FLAGS_2(ptlb_l, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_1(ptlbe, TCG_CALL_NO_RWG, void, env) DEF_HELPER_FLAGS_2(lpa, TCG_CALL_NO_WG, tl, env, tl) DEF_HELPER_FLAGS_1(change_prot_id, TCG_CALL_NO_RWG, void, env) diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode index 19e537df24..f5a3f02fd1 100644 --- a/target/hppa/insns.decode +++ b/target/hppa/insns.decode @@ -161,9 +161,23 @@ ixtlbxf 000001 00000 r:5 00 0 data:1 01000 addr:1 0 00000 # pa2.0 tlb insert idtlbt and iitlbt instructions ixtlbt 000001 r2:5 r1:5 000 data:1 100000 0 00000 # idtlbt -pxtlbx 000001 b:5 x:5 sp:2 0100100 local:1 m:1 ----- data=1 -pxtlbx 000001 b:5 x:5 ... 000100 local:1 m:1 ----- \ - sp=%assemble_sr3x data=0 +# pdtlb, pitlb +pxtlb 000001 b:5 x:5 sp:2 01001000 m:1 ----- \ + &ldst disp=0 scale=0 size=0 t=0 +pxtlb 000001 b:5 x:5 ... 0001000 m:1 ----- \ + &ldst disp=0 scale=0 size=0 t=0 sp=%assemble_sr3x + +# ... pa20 local +pxtlb_l 000001 b:5 x:5 sp:2 01011000 m:1 ----- \ + &ldst disp=0 scale=0 size=0 t=0 +pxtlb_l 000001 b:5 x:5 ... 0011000 m:1 ----- \ + &ldst disp=0 scale=0 size=0 t=0 sp=%assemble_sr3x + +# pdtlbe, pitlbe +pxtlbe 000001 b:5 x:5 sp:2 01001001 m:1 ----- \ + &ldst disp=0 scale=0 size=0 t=0 +pxtlbe 000001 b:5 x:5 ... 0001001 m:1 ----- \ + &ldst disp=0 scale=0 size=0 t=0 sp=%assemble_sr3x lpa 000001 b:5 x:5 sp:2 01001101 m:1 t:5 \ &ldst disp=0 scale=0 size=0 diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c index 9be68b860b..7132ea221c 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -448,16 +448,34 @@ void HELPER(iitlbt_pa20)(CPUHPPAState *env, target_ulong r1, target_ulong r2) itlbt_pa20(env, r1, r2, va_b); } -/* Purge (Insn/Data) TLB. This is explicitly page-based, and is - synchronous across all processors. */ +/* Purge (Insn/Data) TLB. */ static void ptlb_work(CPUState *cpu, run_on_cpu_data data) { CPUHPPAState *env = cpu_env(cpu); - target_ulong addr = (target_ulong) data.target_ptr; + vaddr start = data.target_ptr; + vaddr end; - hppa_flush_tlb_range(env, addr, addr); + /* + * PA2.0 allows a range of pages encoded into GR[b], which we have + * copied into the bottom bits of the otherwise page-aligned address. + * PA1.x will always provide zero here, for a single page flush. + */ + end = start & 0xf; + start &= TARGET_PAGE_MASK; + end = TARGET_PAGE_SIZE << (2 * end); + end = start + end - 1; + + hppa_flush_tlb_range(env, start, end); } +/* This is local to the current cpu. */ +void HELPER(ptlb_l)(CPUHPPAState *env, target_ulong addr) +{ + trace_hppa_tlb_ptlb_local(env); + ptlb_work(env_cpu(env), RUN_ON_CPU_TARGET_PTR(addr)); +} + +/* This is synchronous across all processors. */ void HELPER(ptlb)(CPUHPPAState *env, target_ulong addr) { CPUState *src = env_cpu(env); diff --git a/target/hppa/trace-events b/target/hppa/trace-events index 8931517890..a10ba73d5d 100644 --- a/target/hppa/trace-events +++ b/target/hppa/trace-events @@ -10,6 +10,7 @@ disable hppa_tlb_fill_success(void *env, uint64_t addr, uint64_t phys, int size, disable hppa_tlb_itlba(void *env, void *ent, uint64_t va_b, uint64_t va_e, uint64_t pa) "env=%p ent=%p va_b=0x%lx va_e=0x%lx pa=0x%lx" disable hppa_tlb_itlbp(void *env, void *ent, int access_id, int u, int pl2, int pl1, int type, int b, int d, int t) "env=%p ent=%p access_id=%x u=%d pl2=%d pl1=%d type=%d b=%d d=%d t=%d" disable hppa_tlb_ptlb(void *env) "env=%p" +disable hppa_tlb_ptlb_local(void *env) "env=%p" disable hppa_tlb_ptlbe(void *env) "env=%p" disable hppa_tlb_lpa_success(void *env, uint64_t addr, uint64_t phys) "env=%p addr=0x%lx phys=0x%lx" disable hppa_tlb_lpa_failed(void *env, uint64_t addr) "env=%p addr=0x%lx" diff --git a/target/hppa/translate.c b/target/hppa/translate.c index e7f379d648..9f129a230b 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -2320,7 +2320,7 @@ static bool trans_ixtlbx(DisasContext *ctx, arg_ixtlbx *a) #endif } -static bool trans_pxtlbx(DisasContext *ctx, arg_pxtlbx *a) +static bool do_pxtlb(DisasContext *ctx, arg_ldst *a, bool local) { CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); #ifndef CONFIG_USER_ONLY @@ -2330,15 +2330,53 @@ static bool trans_pxtlbx(DisasContext *ctx, arg_pxtlbx *a) nullify_over(ctx); form_gva(ctx, &addr, &ofs, a->b, a->x, 0, 0, a->sp, a->m, false); - if (a->m) { - save_gpr(ctx, a->b, ofs); + + /* + * Page align now, rather than later, so that we can add in the + * page_size field from pa2.0 from the low 4 bits of GR[b]. + */ + tcg_gen_andi_i64(addr, addr, TARGET_PAGE_MASK); + if (ctx->is_pa20) { + tcg_gen_deposit_i64(addr, addr, load_gpr(ctx, a->b), 0, 4); } - if (a->local) { - gen_helper_ptlbe(tcg_env); + + if (local) { + gen_helper_ptlb_l(tcg_env, addr); } else { gen_helper_ptlb(tcg_env, addr); } + if (a->m) { + save_gpr(ctx, a->b, ofs); + } + + /* Exit TB for TLB change if mmu is enabled. */ + if (ctx->tb_flags & PSW_C) { + ctx->base.is_jmp = DISAS_IAQ_N_STALE; + } + return nullify_end(ctx); +#endif +} + +static bool trans_pxtlb(DisasContext *ctx, arg_ldst *a) +{ + return do_pxtlb(ctx, a, false); +} + +static bool trans_pxtlb_l(DisasContext *ctx, arg_ldst *a) +{ + return ctx->is_pa20 && do_pxtlb(ctx, a, true); +} + +static bool trans_pxtlbe(DisasContext *ctx, arg_ldst *a) +{ + CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); +#ifndef CONFIG_USER_ONLY + nullify_over(ctx); + + trans_nop_addrx(ctx, a); + gen_helper_ptlbe(tcg_env); + /* Exit TB for TLB change if mmu is enabled. */ if (ctx->tb_flags & PSW_C) { ctx->base.is_jmp = DISAS_IAQ_N_STALE; From 34a0d9eefe7dd6161c100e6ffaf25c6c6f4a7282 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 1 Nov 2023 09:56:42 -0700 Subject: [PATCH 647/974] target/hppa: Avoid async_safe_run_on_cpu on uniprocessor system Signed-off-by: Richard Henderson --- target/hppa/mem_helper.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c index 7132ea221c..602e6c809f 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -480,6 +480,7 @@ void HELPER(ptlb)(CPUHPPAState *env, target_ulong addr) { CPUState *src = env_cpu(env); CPUState *cpu; + bool wait = false; trace_hppa_tlb_ptlb(env); run_on_cpu_data data = RUN_ON_CPU_TARGET_PTR(addr); @@ -487,9 +488,14 @@ void HELPER(ptlb)(CPUHPPAState *env, target_ulong addr) CPU_FOREACH(cpu) { if (cpu != src) { async_run_on_cpu(cpu, ptlb_work, data); + wait = true; } } - async_safe_run_on_cpu(src, ptlb_work, data); + if (wait) { + async_safe_run_on_cpu(src, ptlb_work, data); + } else { + ptlb_work(src, data); + } } void hppa_ptlbe(CPUHPPAState *env) From 4c34bab0d3dc59fef4ebce831fbca784a1e3e06a Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Thu, 26 Oct 2023 21:49:48 -0700 Subject: [PATCH 648/974] target/hppa: Clear upper bits in mtctl for pa1.x Signed-off-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 9f129a230b..4102f5faf3 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -2060,7 +2060,13 @@ static bool trans_mtctl(DisasContext *ctx, arg_mtctl *a) #ifndef CONFIG_USER_ONLY nullify_over(ctx); - reg = load_gpr(ctx, a->r); + + if (ctx->is_pa20) { + reg = load_gpr(ctx, a->r); + } else { + reg = tcg_temp_new_i64(); + tcg_gen_ext32u_i64(reg, load_gpr(ctx, a->r)); + } switch (ctl) { case CR_IT: From f5b5c85760c6d3cf073d8e0ee85d170c569d3ddf Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 27 Oct 2023 02:46:44 -0700 Subject: [PATCH 649/974] target/hppa: Add unwind_breg to CPUHPPAState Fill in the insn_start value during form_gva, and copy it out to the env field in hppa_restore_state_to_opc. The value is not yet consumed. Signed-off-by: Richard Henderson --- target/hppa/cpu.c | 1 + target/hppa/cpu.h | 8 +++++++- target/hppa/translate.c | 13 ++++++++++++- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index e1597ba8a5..04de1689d7 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -80,6 +80,7 @@ static void hppa_restore_state_to_opc(CPUState *cs, if (data[1] != (target_ulong)-1) { cpu->env.iaoq_b = data[1]; } + cpu->env.unwind_breg = data[2]; /* * Since we were executing the instruction at IAOQ_F, and took some * sort of action that provoked the cpu_restore_state, we can infer diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index ea8e7e99a4..144794d089 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -45,7 +45,7 @@ #define MMU_IDX_TO_P(MIDX) (((MIDX) - MMU_KERNEL_IDX) & 1) #define PRIV_P_TO_MMU_IDX(PRIV, P) ((PRIV) * 2 + !!(P) + MMU_KERNEL_IDX) -#define TARGET_INSN_START_EXTRA_WORDS 1 +#define TARGET_INSN_START_EXTRA_WORDS 2 /* No need to flush MMU_PHYS_IDX */ #define HPPA_MMU_FLUSH_MASK \ @@ -208,6 +208,12 @@ typedef struct CPUArchState { target_ulong cr_back[2]; /* back of cr17/cr18 */ target_ulong shadow[7]; /* shadow registers */ + /* + * During unwind of a memory insn, the base register of the address. + * This is used to construct CR_IOR for pa2.0. + */ + uint32_t unwind_breg; + /* * ??? The number of entries isn't specified by the architecture. * BTLBs are not supported in 64-bit machines. diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 4102f5faf3..bcce65d587 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -44,6 +44,7 @@ typedef struct DisasCond { typedef struct DisasContext { DisasContextBase base; CPUState *cs; + TCGOp *insn_start; uint64_t iaoq_f; uint64_t iaoq_b; @@ -234,6 +235,13 @@ void hppa_translate_init(void) "iasq_b"); } +static void set_insn_breg(DisasContext *ctx, int breg) +{ + assert(ctx->insn_start != NULL); + tcg_set_insn_start_param(ctx->insn_start, 2, breg); + ctx->insn_start = NULL; +} + static DisasCond cond_make_f(void) { return (DisasCond){ @@ -1324,6 +1332,8 @@ static void form_gva(DisasContext *ctx, TCGv_i64 *pgva, TCGv_i64 *pofs, TCGv_i64 ofs; TCGv_i64 addr; + set_insn_breg(ctx, rb); + /* Note that RX is mutually exclusive with DISP. */ if (rx) { ofs = tcg_temp_new_i64(); @@ -4458,7 +4468,8 @@ static void hppa_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); - tcg_gen_insn_start(ctx->iaoq_f, ctx->iaoq_b); + tcg_gen_insn_start(ctx->iaoq_f, ctx->iaoq_b, 0); + ctx->insn_start = tcg_last_op(); } static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) From 8a02b9a68e4cded2b8751d803a3e3aedfce93609 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 27 Oct 2023 03:35:57 -0700 Subject: [PATCH 650/974] target/hppa: Create raise_exception_with_ior Handle pa2.0 logic for filling in ISR+IOR. Signed-off-by: Richard Henderson --- target/hppa/mem_helper.c | 64 ++++++++++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 13 deletions(-) diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c index 602e6c809f..858ce6ec7f 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -289,6 +289,53 @@ hwaddr hppa_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) return excp == EXCP_DTLB_MISS ? -1 : phys; } +G_NORETURN static void +raise_exception_with_ior(CPUHPPAState *env, int excp, uintptr_t retaddr, + vaddr addr, bool mmu_disabled) +{ + CPUState *cs = env_cpu(env); + + cs->exception_index = excp; + + if (env->psw & PSW_Q) { + /* + * For pa1.x, the offset and space never overlap, and so we + * simply extract the high and low part of the virtual address. + * + * For pa2.0, the formation of these are described in section + * "Interruption Parameter Registers", page 2-15. + */ + env->cr[CR_IOR] = (uint32_t)addr; + env->cr[CR_ISR] = addr >> 32; + + if (hppa_is_pa20(env)) { + if (mmu_disabled) { + /* + * If data translation was disabled, the ISR contains + * the upper portion of the abs address, zero-extended. + */ + env->cr[CR_ISR] &= 0x3fffffff; + } else { + /* + * If data translation was enabled, the upper two bits + * of the IOR (the b field) are equal to the two space + * bits from the base register used to form the gva. + */ + uint64_t b; + + cpu_restore_state(cs, retaddr); + + b = env->gr[env->unwind_breg]; + b >>= (env->psw & PSW_W ? 62 : 30); + env->cr[CR_IOR] |= b << 62; + + cpu_loop_exit(cs); + } + } + } + cpu_loop_exit_restore(cs, retaddr); +} + bool hppa_cpu_tlb_fill(CPUState *cs, vaddr addr, int size, MMUAccessType type, int mmu_idx, bool probe, uintptr_t retaddr) @@ -318,14 +365,10 @@ bool hppa_cpu_tlb_fill(CPUState *cs, vaddr addr, int size, return false; } trace_hppa_tlb_fill_excp(env, addr, size, type, mmu_idx); + /* Failure. Raise the indicated exception. */ - cs->exception_index = excp; - if (cpu->env.psw & PSW_Q) { - /* ??? Needs tweaking for hppa64. */ - cpu->env.cr[CR_IOR] = addr; - cpu->env.cr[CR_ISR] = addr >> 32; - } - cpu_loop_exit_restore(cs, retaddr); + raise_exception_with_ior(env, excp, retaddr, + addr, mmu_idx == MMU_PHYS_IDX); } trace_hppa_tlb_fill_success(env, addr & TARGET_PAGE_MASK, @@ -553,16 +596,11 @@ target_ulong HELPER(lpa)(CPUHPPAState *env, target_ulong addr) excp = hppa_get_physical_address(env, addr, MMU_KERNEL_IDX, 0, &phys, &prot, NULL); if (excp >= 0) { - if (env->psw & PSW_Q) { - /* ??? Needs tweaking for hppa64. */ - env->cr[CR_IOR] = addr; - env->cr[CR_ISR] = addr >> 32; - } if (excp == EXCP_DTLB_MISS) { excp = EXCP_NA_DTLB_MISS; } trace_hppa_tlb_lpa_failed(env, addr); - hppa_dynamic_excp(env, excp, GETPC()); + raise_exception_with_ior(env, excp, GETPC(), addr, false); } trace_hppa_tlb_lpa_success(env, addr, phys); return phys; From b10700d826c864872deae6c28aca041fc97df79f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 27 Oct 2023 04:10:45 -0700 Subject: [PATCH 651/974] target/hppa: Update IIAOQ, IIASQ for pa2.0 These registers have a different format for pa2.0. Signed-off-by: Richard Henderson --- target/hppa/int_helper.c | 46 ++++++++++++++++++++++++---------------- target/hppa/sys_helper.c | 10 +++++++++ 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/target/hppa/int_helper.c b/target/hppa/int_helper.c index a11d607b31..54875442e7 100644 --- a/target/hppa/int_helper.c +++ b/target/hppa/int_helper.c @@ -102,11 +102,7 @@ void hppa_cpu_do_interrupt(CPUState *cs) HPPACPU *cpu = HPPA_CPU(cs); CPUHPPAState *env = &cpu->env; int i = cs->exception_index; - target_ulong iaoq_f = env->iaoq_f; - target_ulong iaoq_b = env->iaoq_b; - uint64_t iasq_f = env->iasq_f; - uint64_t iasq_b = env->iasq_b; - target_ulong old_psw; + uint64_t old_psw; /* As documented in pa2.0 -- interruption handling. */ /* step 1 */ @@ -118,10 +114,25 @@ void hppa_cpu_do_interrupt(CPUState *cs) (i == EXCP_HPMC ? PSW_M : 0)); /* step 3 */ - env->cr[CR_IIASQ] = iasq_f >> 32; - env->cr_back[0] = iasq_b >> 32; - env->cr[CR_IIAOQ] = iaoq_f; - env->cr_back[1] = iaoq_b; + /* + * For pa1.x, IIASQ is simply a copy of IASQ. + * For pa2.0, IIASQ is the top bits of the virtual address, + * or zero if translation is disabled. + */ + if (!hppa_is_pa20(env)) { + env->cr[CR_IIASQ] = env->iasq_f >> 32; + env->cr_back[0] = env->iasq_b >> 32; + } else if (old_psw & PSW_C) { + env->cr[CR_IIASQ] = + hppa_form_gva_psw(old_psw, env->iasq_f, env->iaoq_f) >> 32; + env->cr_back[0] = + hppa_form_gva_psw(old_psw, env->iasq_f, env->iaoq_f) >> 32; + } else { + env->cr[CR_IIASQ] = 0; + env->cr_back[0] = 0; + } + env->cr[CR_IIAOQ] = env->iaoq_f; + env->cr_back[1] = env->iaoq_b; if (old_psw & PSW_Q) { /* step 5 */ @@ -154,14 +165,13 @@ void hppa_cpu_do_interrupt(CPUState *cs) /* ??? An alternate fool-proof method would be to store the instruction data into the unwind info. That's probably a bit too much in the way of extra storage required. */ - vaddr vaddr; - hwaddr paddr; + vaddr vaddr = env->iaoq_f & -4; + hwaddr paddr = vaddr; - paddr = vaddr = iaoq_f & -4; if (old_psw & PSW_C) { int prot, t; - vaddr = hppa_form_gva_psw(old_psw, iasq_f, vaddr); + vaddr = hppa_form_gva_psw(old_psw, env->iasq_f, vaddr); t = hppa_get_physical_address(env, vaddr, MMU_KERNEL_IDX, 0, &paddr, &prot, NULL); if (t >= 0) { @@ -191,14 +201,14 @@ void hppa_cpu_do_interrupt(CPUState *cs) /* step 7 */ if (i == EXCP_TOC) { - env->iaoq_f = FIRMWARE_START; + env->iaoq_f = hppa_form_gva(env, 0, FIRMWARE_START); /* help SeaBIOS and provide iaoq_b and iasq_back in shadow regs */ env->gr[24] = env->cr_back[0]; env->gr[25] = env->cr_back[1]; } else { - env->iaoq_f = env->cr[CR_IVA] + 32 * i; + env->iaoq_f = hppa_form_gva(env, 0, env->cr[CR_IVA] + 32 * i); } - env->iaoq_b = env->iaoq_f + 4; + env->iaoq_b = hppa_form_gva(env, 0, env->iaoq_f + 4); env->iasq_f = 0; env->iasq_b = 0; @@ -251,8 +261,8 @@ void hppa_cpu_do_interrupt(CPUState *cs) qemu_log("INT %6d: %s @ " TARGET_FMT_lx "," TARGET_FMT_lx " -> " TARGET_FMT_lx " " TARGET_FMT_lx "\n", ++count, name, - hppa_form_gva(env, iasq_f, iaoq_f), - hppa_form_gva(env, iasq_b, iaoq_b), + hppa_form_gva(env, env->iasq_f, env->iaoq_f), + hppa_form_gva(env, env->iasq_b, env->iaoq_b), env->iaoq_f, hppa_form_gva(env, (uint64_t)env->cr[CR_ISR] << 32, env->cr[CR_IOR])); diff --git a/target/hppa/sys_helper.c b/target/hppa/sys_helper.c index 8850576ac3..a59245eed3 100644 --- a/target/hppa/sys_helper.c +++ b/target/hppa/sys_helper.c @@ -80,6 +80,16 @@ void HELPER(rfi)(CPUHPPAState *env) env->iasq_b = (uint64_t)env->cr_back[0] << 32; env->iaoq_f = env->cr[CR_IIAOQ]; env->iaoq_b = env->cr_back[1]; + + /* + * For pa2.0, IIASQ is the top bits of the virtual address. + * To recreate the space identifier, remove the offset bits. + */ + if (hppa_is_pa20(env)) { + env->iasq_f &= ~env->iaoq_f; + env->iasq_b &= ~env->iaoq_b; + } + cpu_hppa_put_psw(env, env->cr[CR_IPSW]); } From 5dd5c003661c364d5c339675532c3d5c60207994 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 1 Nov 2023 10:33:58 -0700 Subject: [PATCH 652/974] target/hppa: Improve interrupt logging Signed-off-by: Richard Henderson --- target/hppa/int_helper.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/target/hppa/int_helper.c b/target/hppa/int_helper.c index 54875442e7..467ee7daf5 100644 --- a/target/hppa/int_helper.c +++ b/target/hppa/int_helper.c @@ -258,14 +258,10 @@ void hppa_cpu_do_interrupt(CPUState *cs) snprintf(unknown, sizeof(unknown), "unknown %d", i); name = unknown; } - qemu_log("INT %6d: %s @ " TARGET_FMT_lx "," TARGET_FMT_lx - " -> " TARGET_FMT_lx " " TARGET_FMT_lx "\n", - ++count, name, - hppa_form_gva(env, env->iasq_f, env->iaoq_f), - hppa_form_gva(env, env->iasq_b, env->iaoq_b), - env->iaoq_f, - hppa_form_gva(env, (uint64_t)env->cr[CR_ISR] << 32, - env->cr[CR_IOR])); + qemu_log("INT %6d: %s @ " TARGET_FMT_lx ":" TARGET_FMT_lx + " for " TARGET_FMT_lx ":" TARGET_FMT_lx "\n", + ++count, name, env->cr[CR_IIASQ], env->cr[CR_IIAOQ], + env->cr[CR_ISR], env->cr[CR_IOR]); } cs->exception_index = -1; } From fd842b2f4ce2da082b400f9b9278fa6bd45d0864 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Sat, 21 Oct 2023 15:41:02 +0200 Subject: [PATCH 653/974] hw/pci-host/astro: Map Astro chip into 64-bit I/O memory region Map Astro into high F-region and add alias for 32-bit OS in low region. Signed-off-by: Helge Deller --- hw/pci-host/astro.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/hw/pci-host/astro.c b/hw/pci-host/astro.c index 4b2d7caf2d..df61386bd9 100644 --- a/hw/pci-host/astro.c +++ b/hw/pci-host/astro.c @@ -19,6 +19,8 @@ #define TYPE_ASTRO_IOMMU_MEMORY_REGION "astro-iommu-memory-region" +#define F_EXTEND(addr) ((addr) | MAKE_64BIT_MASK(32, 32)) + #include "qemu/osdep.h" #include "qemu/module.h" #include "qemu/units.h" @@ -821,15 +823,16 @@ static void astro_realize(DeviceState *obj, Error **errp) /* map elroys mmio */ map_size = LMMIO_DIST_BASE_SIZE / ROPES_PER_IOC; - map_addr = (uint32_t) (LMMIO_DIST_BASE_ADDR + rope * map_size); + map_addr = F_EXTEND(LMMIO_DIST_BASE_ADDR + rope * map_size); memory_region_init_alias(&elroy->pci_mmio_alias, OBJECT(elroy), "pci-mmio-alias", - &elroy->pci_mmio, map_addr, map_size); + &elroy->pci_mmio, (uint32_t) map_addr, map_size); memory_region_add_subregion(get_system_memory(), map_addr, &elroy->pci_mmio_alias); + /* map elroys io */ map_size = IOS_DIST_BASE_SIZE / ROPES_PER_IOC; - map_addr = (uint32_t) (IOS_DIST_BASE_ADDR + rope * map_size); + map_addr = F_EXTEND(IOS_DIST_BASE_ADDR + rope * map_size); memory_region_add_subregion(get_system_memory(), map_addr, &elroy->pci_io); From 64bf09674a5911fd70a3b4050c2d7081b0353cf5 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Wed, 25 Oct 2023 21:46:39 +0200 Subject: [PATCH 654/974] hw/pci-host/astro: Trigger CPU irq on CPU HPA in high memory The CPU HPA is in the high F-region on PA2.0 CPUs, so use F_EXTEND() to trigger interrupt request at the right CPU HPA address. Note that the cpu_hpa value comes out of the IRT, which doesn't store the higher addresss bits. Signed-off-by: Helge Deller --- hw/pci-host/astro.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/pci-host/astro.c b/hw/pci-host/astro.c index df61386bd9..b19f0917c5 100644 --- a/hw/pci-host/astro.c +++ b/hw/pci-host/astro.c @@ -384,7 +384,7 @@ static void elroy_set_irq(void *opaque, int irq, int level) uint32_t ena = bit & ~old_ilr; s->ilr = old_ilr | bit; if (ena != 0) { - stl_be_phys(&address_space_memory, cpu_hpa, val & 63); + stl_be_phys(&address_space_memory, F_EXTEND(cpu_hpa), val & 63); } } else { s->ilr = old_ilr & ~bit; From fd9b04bf9299e8db36db3b677bc0a433fc3a34b3 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Sat, 21 Oct 2023 13:40:55 +0200 Subject: [PATCH 655/974] hw/hppa: Turn on 64-bit CPU for C3700 machine Signed-off-by: Helge Deller --- hw/hppa/machine.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index 43c7afb89d..da9ca85806 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -696,7 +696,7 @@ static void HP_C3700_machine_init_class_init(ObjectClass *oc, void *data) NMIClass *nc = NMI_CLASS(oc); mc->desc = "HP C3700 workstation"; - mc->default_cpu_type = TYPE_HPPA_CPU; + mc->default_cpu_type = TYPE_HPPA64_CPU; mc->init = machine_HP_C3700_init; mc->reset = hppa_machine_reset; mc->block_default_type = IF_SCSI; From 3d1611bfa129182d2e867e8a9da7d2fc6efefce5 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Wed, 25 Oct 2023 20:10:21 +0200 Subject: [PATCH 656/974] hw/hppa: Allow C3700 with 64-bit and B160L with 32-bit CPU only Prevent that users try to boot a 64-bit only C3700 machine with a 32-bit CPU, and to boot a 32-bit only B160L machine with a 64-bit CPU. Signed-off-by: Helge Deller --- hw/hppa/machine.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index da9ca85806..a3222d3a96 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -528,6 +528,12 @@ static void machine_HP_B160L_init(MachineState *machine) /* Create CPUs and RAM. */ translate = machine_HP_common_init_cpus(machine); + if (hppa_is_pa20(&cpu[0]->env)) { + error_report("The HP B160L workstation requires a 32-bit " + "CPU. Use '-machine C3700' instead."); + exit(1); + } + /* Init Lasi chip */ lasi_dev = DEVICE(lasi_init()); memory_region_add_subregion(addr_space, translate(NULL, LASI_HPA), @@ -602,6 +608,12 @@ static void machine_HP_C3700_init(MachineState *machine) /* Create CPUs and RAM. */ translate = machine_HP_common_init_cpus(machine); + if (!hppa_is_pa20(&cpu[0]->env)) { + error_report("The HP C3000 workstation requires a 64-bit CPU. " + "Use '-machine B160L' instead."); + exit(1); + } + /* Init Astro and the Elroys (PCI host bus chips). */ astro = astro_init(); astro_dev = DEVICE(astro); @@ -659,6 +671,11 @@ static void hppa_nmi(NMIState *n, int cpu_index, Error **errp) } } +static const char *HP_B160L_machine_valid_cpu_types[] = { + TYPE_HPPA_CPU, + NULL +}; + static void HP_B160L_machine_init_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -666,6 +683,7 @@ static void HP_B160L_machine_init_class_init(ObjectClass *oc, void *data) mc->desc = "HP B160L workstation"; mc->default_cpu_type = TYPE_HPPA_CPU; + mc->valid_cpu_types = HP_B160L_machine_valid_cpu_types; mc->init = machine_HP_B160L_init; mc->reset = hppa_machine_reset; mc->block_default_type = IF_SCSI; @@ -690,6 +708,11 @@ static const TypeInfo HP_B160L_machine_init_typeinfo = { }, }; +static const char *HP_C3700_machine_valid_cpu_types[] = { + TYPE_HPPA64_CPU, + NULL +}; + static void HP_C3700_machine_init_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -697,6 +720,7 @@ static void HP_C3700_machine_init_class_init(ObjectClass *oc, void *data) mc->desc = "HP C3700 workstation"; mc->default_cpu_type = TYPE_HPPA64_CPU; + mc->valid_cpu_types = HP_C3700_machine_valid_cpu_types; mc->init = machine_HP_C3700_init; mc->reset = hppa_machine_reset; mc->block_default_type = IF_SCSI; From 565f85a9c293818a91a3d3414311303de7e00cec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 17 Oct 2023 15:16:42 +0400 Subject: [PATCH 657/974] ui/gtk: force realization of drawing area MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes the GL context creation from a widget that isn't yet realized (in a hidden tab for example). Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1727 Signed-off-by: Marc-André Lureau Reviewed-by: Antonio Caggiano Message-Id: <20231017111642.1155545-1-marcandre.lureau@redhat.com> --- ui/gtk.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ui/gtk.c b/ui/gtk.c index 935de1209b..2a4c9b84ba 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -2371,6 +2371,7 @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts) GdkDisplay *window_display; GtkIconTheme *theme; char *dir; + int idx; if (!gtkinit) { fprintf(stderr, "gtk initialization failed\n"); @@ -2434,6 +2435,15 @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts) gtk_container_add(GTK_CONTAINER(s->window), s->vbox); gtk_widget_show_all(s->window); + + for (idx = 0;; idx++) { + QemuConsole *con = qemu_console_lookup_by_index(idx); + if (!con) { + break; + } + gtk_widget_realize(s->vc[idx].gfx.drawing_area); + } + if (opts->u.gtk.has_show_menubar && !opts->u.gtk.show_menubar) { gtk_widget_hide(s->menu_bar); From 6f189a08c1b0085808af1bfbf4567f0da193ecc1 Mon Sep 17 00:00:00 2001 From: Antonio Caggiano Date: Mon, 16 Oct 2023 14:32:15 +0200 Subject: [PATCH 658/974] ui/gtk-egl: Check EGLSurface before doing scanout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first time gd_egl_scanout_texture() is called, there's a possibility that the GTK drawing area might not be realized yet, in which case its associated GdkWindow is NULL. This means gd_egl_init() was also skipped and the EGLContext and EGLSurface stored in the VirtualGfxConsole are not valid yet. Continuing with the scanout in this conditions would result in hitting an assert in libepoxy: "Couldn't find current GLX or EGL context". A possible workaround is to just ignore the scanout request, giving the the GTK drawing area some time to finish its realization. At that point, the gd_egl_init() will succeed and the EGLContext and EGLSurface stored in the VirtualGfxConsole will be valid. Signed-off-by: Antonio Caggiano Reviewed-by: Marc-André Lureau Message-Id: <20231016123215.2699269-1-quic_acaggian@quicinc.com> --- ui/gtk-egl.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c index a1060fd80f..3e8d1c1d02 100644 --- a/ui/gtk-egl.c +++ b/ui/gtk-egl.c @@ -243,12 +243,19 @@ void gd_egl_scanout_texture(DisplayChangeListener *dcl, vc->gfx.h = h; vc->gfx.y0_top = backing_y_0_top; - eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, - vc->gfx.esurface, vc->gfx.ectx); + if (!vc->gfx.esurface) { + gd_egl_init(vc); + if (!vc->gfx.esurface) { + return; + } - gtk_egl_set_scanout_mode(vc, true); - egl_fb_setup_for_tex(&vc->gfx.guest_fb, backing_width, backing_height, - backing_id, false); + eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, + vc->gfx.esurface, vc->gfx.ectx); + + gtk_egl_set_scanout_mode(vc, true); + egl_fb_setup_for_tex(&vc->gfx.guest_fb, backing_width, backing_height, + backing_id, false); + } } void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl, From 47fd6ab1e334962890bc3e8d2e32857f6594e1c1 Mon Sep 17 00:00:00 2001 From: Dongwon Kim Date: Thu, 12 Oct 2023 15:26:43 -0700 Subject: [PATCH 659/974] ui/gtk-egl: apply scale factor when calculating window's dimension MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Scale factor needs to be applied when calculating width/height of the GTK windows. Cc: Marc-André Lureau Signed-off-by: Dongwon Kim Reviewed-by: Marc-André Lureau Message-Id: <20231012222643.13996-1-dongwon.kim@intel.com> --- ui/gtk-egl.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c index 3e8d1c1d02..cd2f176502 100644 --- a/ui/gtk-egl.c +++ b/ui/gtk-egl.c @@ -69,15 +69,16 @@ void gd_egl_draw(VirtualConsole *vc) #ifdef CONFIG_GBM QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf; #endif - int ww, wh; + int ww, wh, ws; if (!vc->gfx.gls) { return; } window = gtk_widget_get_window(vc->gfx.drawing_area); - ww = gdk_window_get_width(window); - wh = gdk_window_get_height(window); + ws = gdk_window_get_scale_factor(window); + ww = gdk_window_get_width(window) * ws; + wh = gdk_window_get_height(window) * ws; if (vc->gfx.scanout_mode) { #ifdef CONFIG_GBM @@ -319,7 +320,7 @@ void gd_egl_scanout_flush(DisplayChangeListener *dcl, { VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); GdkWindow *window; - int ww, wh; + int ww, wh, ws; if (!vc->gfx.scanout_mode) { return; @@ -332,8 +333,9 @@ void gd_egl_scanout_flush(DisplayChangeListener *dcl, vc->gfx.esurface, vc->gfx.ectx); window = gtk_widget_get_window(vc->gfx.drawing_area); - ww = gdk_window_get_width(window); - wh = gdk_window_get_height(window); + ws = gdk_window_get_scale_factor(window); + ww = gdk_window_get_width(window) * ws; + wh = gdk_window_get_height(window) * ws; egl_fb_setup_default(&vc->gfx.win_fb, ww, wh); if (vc->gfx.cursor_fb.texture) { egl_texture_blit(vc->gfx.gls, &vc->gfx.win_fb, &vc->gfx.guest_fb, From 5ec0898b057971f0e03d2ecc8b62c58b50f4cb99 Mon Sep 17 00:00:00 2001 From: Carwyn Ellis Date: Fri, 27 Oct 2023 16:49:20 +0100 Subject: [PATCH 660/974] ui/cocoa: add zoom-to-fit display option Provides a display option, zoom-to-fit, that enables scaling of the display when full-screen mode is enabled. Also ensures that the corresponding menu item is marked as enabled when the option is set to on. Signed-off-by: Carwyn Ellis Reviewed-by: Akihiko Odaki Message-Id: <20231027154920.80626-2-carwynellis@gmail.com> --- qapi/ui.json | 7 ++++++- ui/cocoa.m | 32 ++++++++++++++++++-------------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/qapi/ui.json b/qapi/ui.json index 006616aa77..3718d40fcf 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -1409,13 +1409,18 @@ # codes match their position on non-Mac keyboards and you can use # Meta/Super and Alt where you expect them. (default: off) # +# @zoom-to-fit: Zoom guest display to fit into the host window. When +# turned off the host window will be resized instead. Defaults to +# "off". (Since 8.2) +# # Since: 7.0 ## { 'struct': 'DisplayCocoa', 'data': { '*left-command-key': 'bool', '*full-grab': 'bool', - '*swap-opt-cmd': 'bool' + '*swap-opt-cmd': 'bool', + '*zoom-to-fit': 'bool' } } ## diff --git a/ui/cocoa.m b/ui/cocoa.m index d95276013c..cd069da696 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -1247,7 +1247,6 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven [normalWindow makeKeyAndOrderFront:self]; [normalWindow center]; [normalWindow setDelegate: self]; - stretch_video = false; /* Used for displaying pause on the screen */ pauseLabel = [NSTextField new]; @@ -1671,7 +1670,9 @@ static void create_initial_menus(void) // View menu menu = [[NSMenu alloc] initWithTitle:@"View"]; [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"Enter Fullscreen" action:@selector(doToggleFullScreen:) keyEquivalent:@"f"] autorelease]]; // Fullscreen - [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"Zoom To Fit" action:@selector(zoomToFit:) keyEquivalent:@""] autorelease]]; + menuItem = [[[NSMenuItem alloc] initWithTitle:@"Zoom To Fit" action:@selector(zoomToFit:) keyEquivalent:@""] autorelease]; + [menuItem setState: stretch_video ? NSControlStateValueOn : NSControlStateValueOff]; + [menu addItem: menuItem]; menuItem = [[[NSMenuItem alloc] initWithTitle:@"View" action:nil keyEquivalent:@""] autorelease]; [menuItem setSubmenu:menu]; [[NSApp mainMenu] addItem:menuItem]; @@ -2041,18 +2042,6 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts) [QemuApplication sharedApplication]; - create_initial_menus(); - - /* - * Create the menu entries which depend on QEMU state (for consoles - * and removable devices). These make calls back into QEMU functions, - * which is OK because at this point we know that the second thread - * holds the iothread lock and is synchronously waiting for us to - * finish. - */ - add_console_menu_entries(); - addRemovableDevicesMenuItems(); - // Create an Application controller QemuCocoaAppController *controller = [[QemuCocoaAppController alloc] init]; [NSApp setDelegate:controller]; @@ -2077,6 +2066,21 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts) left_command_key_enabled = 0; } + if (opts->u.cocoa.has_zoom_to_fit && opts->u.cocoa.zoom_to_fit) { + stretch_video = true; + } + + create_initial_menus(); + /* + * Create the menu entries which depend on QEMU state (for consoles + * and removable devices). These make calls back into QEMU functions, + * which is OK because at this point we know that the second thread + * holds the iothread lock and is synchronously waiting for us to + * finish. + */ + add_console_menu_entries(); + addRemovableDevicesMenuItems(); + // register vga output callbacks register_displaychangelistener(&dcl); From fb93569e422d4f4b5953dd953c92ba7838309972 Mon Sep 17 00:00:00 2001 From: Sergey Mironov Date: Thu, 12 Oct 2023 13:44:48 +0300 Subject: [PATCH 661/974] ui: Replacing pointer in function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit At the end of the first if we see 'vc->gfx.surface = NULL;', further checking of it is pointless. In the second if, ectx is taken. Found by Linux Verification Center (linuxtesting.org) with SVACE. Co-developed-by: Linux Verification Center Signed-off-by: Sergey Mironov Message-ID: <20231012104448.1251039-1-mironov@fintech.ru> Reviewed-by: Marc-André Lureau --- ui/gtk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/gtk.c b/ui/gtk.c index 2a4c9b84ba..be047a41ad 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -1400,7 +1400,7 @@ static void gd_menu_untabify(GtkMenuItem *item, void *opaque) eglDestroySurface(qemu_egl_display, vc->gfx.esurface); vc->gfx.esurface = NULL; } - if (vc->gfx.esurface) { + if (vc->gfx.ectx) { eglDestroyContext(qemu_egl_display, vc->gfx.ectx); vc->gfx.ectx = NULL; } From c7f21816612879efdae3d9b67f024a6671494c62 Mon Sep 17 00:00:00 2001 From: Hanna Czenczek Date: Mon, 16 Oct 2023 15:42:37 +0200 Subject: [PATCH 662/974] vhost-user.rst: Improve [GS]ET_VRING_BASE doc GET_VRING_BASE does not mention that it stops the respective ring. Fix that. Furthermore, it is not fully clear what the "base offset" these commands' documentation refers to is; an offset could be many things. Be more precise and verbose about it, especially given that these commands use different payload structures depending on whether the vring is split or packed. Signed-off-by: Hanna Czenczek Message-Id: <20231016134243.68248-2-hreitz@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- docs/interop/vhost-user.rst | 77 +++++++++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 4 deletions(-) diff --git a/docs/interop/vhost-user.rst b/docs/interop/vhost-user.rst index 768fb5c28c..9202b167dd 100644 --- a/docs/interop/vhost-user.rst +++ b/docs/interop/vhost-user.rst @@ -108,6 +108,43 @@ A vring state description :num: a 32-bit number +A vring descriptor index for split virtqueues +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ++-------------+---------------------+ +| vring index | index in avail ring | ++-------------+---------------------+ + +:vring index: 32-bit index of the respective virtqueue + +:index in avail ring: 32-bit value, of which currently only the lower 16 + bits are used: + + - Bits 0–15: Index of the next *Available Ring* descriptor that the + back-end will process. This is a free-running index that is not + wrapped by the ring size. + - Bits 16–31: Reserved (set to zero) + +Vring descriptor indices for packed virtqueues +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ++-------------+--------------------+ +| vring index | descriptor indices | ++-------------+--------------------+ + +:vring index: 32-bit index of the respective virtqueue + +:descriptor indices: 32-bit value: + + - Bits 0–14: Index of the next *Available Ring* descriptor that the + back-end will process. This is a free-running index that is not + wrapped by the ring size. + - Bit 15: Driver (Available) Ring Wrap Counter + - Bits 16–30: Index of the entry in the *Used Ring* where the back-end + will place the next descriptor. This is a free-running index that + is not wrapped by the ring size. + - Bit 31: Device (Used) Ring Wrap Counter + A vring address description ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1042,18 +1079,50 @@ Front-end message types ``VHOST_USER_SET_VRING_BASE`` :id: 10 :equivalent ioctl: ``VHOST_SET_VRING_BASE`` - :request payload: vring state description + :request payload: vring descriptor index/indices :reply payload: N/A - Sets the base offset in the available vring. + Sets the next index to use for descriptors in this vring: + + * For a split virtqueue, sets only the next descriptor index to + process in the *Available Ring*. The device is supposed to read the + next index in the *Used Ring* from the respective vring structure in + guest memory. + + * For a packed virtqueue, both indices are supplied, as they are not + explicitly available in memory. + + Consequently, the payload type is specific to the type of virt queue + (*a vring descriptor index for split virtqueues* vs. *vring descriptor + indices for packed virtqueues*). ``VHOST_USER_GET_VRING_BASE`` :id: 11 :equivalent ioctl: ``VHOST_USER_GET_VRING_BASE`` :request payload: vring state description - :reply payload: vring state description + :reply payload: vring descriptor index/indices - Get the available vring base offset. + Stops the vring and returns the current descriptor index or indices: + + * For a split virtqueue, returns only the 16-bit next descriptor + index to process in the *Available Ring*. Note that this may + differ from the available ring index in the vring structure in + memory, which points to where the driver will put new available + descriptors. For the *Used Ring*, the device only needs the next + descriptor index at which to put new descriptors, which is the + value in the vring structure in memory, so this value is not + covered by this message. + + * For a packed virtqueue, neither index is explicitly available to + read from memory, so both indices (as maintained by the device) are + returned. + + Consequently, the payload type is specific to the type of virt queue + (*a vring descriptor index for split virtqueues* vs. *vring descriptor + indices for packed virtqueues*). + + The request payload’s *num* field is currently reserved and must be + set to 0. ``VHOST_USER_SET_VRING_KICK`` :id: 12 From eae69cc36b278a3e84bb013909135e138b37855d Mon Sep 17 00:00:00 2001 From: Hanna Czenczek Date: Mon, 16 Oct 2023 15:42:38 +0200 Subject: [PATCH 663/974] vhost-user.rst: Clarify enabling/disabling vrings Currently, the vhost-user documentation says that rings are to be initialized in a disabled state when VHOST_USER_F_PROTOCOL_FEATURES is negotiated. However, by the time of feature negotiation, all rings have already been initialized, so it is not entirely clear what this means. At least the vhost-user-backend Rust crate's implementation interpreted it to mean that whenever this feature is negotiated, all rings are to put into a disabled state, which means that every SET_FEATURES call would disable all rings, effectively halting the device. This is problematic because the VHOST_F_LOG_ALL feature is also set or cleared this way, which happens during migration. Doing so should not halt the device. Other implementations have interpreted this to mean that the device is to be initialized with all rings disabled, and a subsequent SET_FEATURES call that does not set VHOST_USER_F_PROTOCOL_FEATURES will enable all of them. Here, SET_FEATURES will never disable any ring. This interpretation does not suffer the problem of unintentionally halting the device whenever features are set or cleared, so it seems better and more reasonable. We can clarify this in the documentation by making it explicit that the enabled/disabled state is tracked even while the vring is stopped. Every vring is initialized in a disabled state, and SET_FEATURES without VHOST_USER_F_PROTOCOL_FEATURES simply becomes one way to enable all vrings. Reviewed-by: Stefan Hajnoczi Signed-off-by: Hanna Czenczek Message-Id: <20231016134243.68248-3-hreitz@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- docs/interop/vhost-user.rst | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/docs/interop/vhost-user.rst b/docs/interop/vhost-user.rst index 9202b167dd..e5a04c04ed 100644 --- a/docs/interop/vhost-user.rst +++ b/docs/interop/vhost-user.rst @@ -411,31 +411,33 @@ negotiation. Ring states ----------- -Rings can be in one of three states: +Rings have two independent states: started/stopped, and enabled/disabled. -* stopped: the back-end must not process the ring at all. +* While a ring is stopped, the back-end must not process the ring at + all, regardless of whether it is enabled or disabled. The + enabled/disabled state should still be tracked, though, so it can come + into effect once the ring is started. -* started but disabled: the back-end must process the ring without +* started and disabled: The back-end must process the ring without causing any side effects. For example, for a networking device, in the disabled state the back-end must not supply any new RX packets, but must process and discard any TX packets. -* started and enabled. +* started and enabled: The back-end must process the ring normally, i.e. + process all requests and execute them. -Each ring is initialized in a stopped state. The back-end must start -ring upon receiving a kick (that is, detecting that file descriptor is -readable) on the descriptor specified by ``VHOST_USER_SET_VRING_KICK`` -or receiving the in-band message ``VHOST_USER_VRING_KICK`` if negotiated, -and stop ring upon receiving ``VHOST_USER_GET_VRING_BASE``. +Each ring is initialized in a stopped and disabled state. The back-end +must start a ring upon receiving a kick (that is, detecting that file +descriptor is readable) on the descriptor specified by +``VHOST_USER_SET_VRING_KICK`` or receiving the in-band message +``VHOST_USER_VRING_KICK`` if negotiated, and stop a ring upon receiving +``VHOST_USER_GET_VRING_BASE``. Rings can be enabled or disabled by ``VHOST_USER_SET_VRING_ENABLE``. -If ``VHOST_USER_F_PROTOCOL_FEATURES`` has not been negotiated, the -ring starts directly in the enabled state. - -If ``VHOST_USER_F_PROTOCOL_FEATURES`` has been negotiated, the ring is -initialized in a disabled state and is enabled by -``VHOST_USER_SET_VRING_ENABLE`` with parameter 1. +In addition, upon receiving a ``VHOST_USER_SET_FEATURES`` message from +the front-end without ``VHOST_USER_F_PROTOCOL_FEATURES`` set, the +back-end must enable all rings immediately. While processing the rings (whether they are enabled or not), the back-end must support changing some configuration aspects on the fly. From a6e76dd3c3b98db86802b4b08984f2ac5ec1faea Mon Sep 17 00:00:00 2001 From: Hanna Czenczek Date: Mon, 16 Oct 2023 15:42:39 +0200 Subject: [PATCH 664/974] vhost-user.rst: Introduce suspended state In vDPA, GET_VRING_BASE does not stop the queried vring, which is why SUSPEND was introduced so that the returned index would be stable. In vhost-user, it does stop the vring, so under the same reasoning, it can get away without SUSPEND. Still, we do want to clarify that if the device is completely stopped, i.e. all vrings are stopped, the back-end should cease to modify any state relating to the guest. Do this by calling it "suspended". Suggested-by: Stefan Hajnoczi Reviewed-by: Stefan Hajnoczi Signed-off-by: Hanna Czenczek Message-Id: <20231016134243.68248-4-hreitz@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- docs/interop/vhost-user.rst | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/docs/interop/vhost-user.rst b/docs/interop/vhost-user.rst index e5a04c04ed..035a23ed35 100644 --- a/docs/interop/vhost-user.rst +++ b/docs/interop/vhost-user.rst @@ -442,6 +442,19 @@ back-end must enable all rings immediately. While processing the rings (whether they are enabled or not), the back-end must support changing some configuration aspects on the fly. +.. _suspended_device_state: + +Suspended device state +^^^^^^^^^^^^^^^^^^^^^^ + +While all vrings are stopped, the device is *suspended*. In addition to +not processing any vring (because they are stopped), the device must: + +* not write to any guest memory regions, +* not send any notifications to the guest, +* not send any messages to the front-end, +* still process and reply to messages from the front-end. + Multiple queue support ---------------------- @@ -529,7 +542,8 @@ ancillary data, it may be used to inform the front-end that the log has been modified. Once the source has finished migration, rings will be stopped by the -source. No further update must be done before rings are restarted. +source (:ref:`Suspended device state `). No +further update must be done before rings are restarted. In postcopy migration the back-end is started before all the memory has been received from the source host, and care must be taken to avoid @@ -1123,6 +1137,10 @@ Front-end message types (*a vring descriptor index for split virtqueues* vs. *vring descriptor indices for packed virtqueues*). + When and as long as all of a device’s vrings are stopped, it is + *suspended*, see :ref:`Suspended device state + `. + The request payload’s *num* field is currently reserved and must be set to 0. From 019233096c03b826e0e677115b6e3c550a54a48d Mon Sep 17 00:00:00 2001 From: Hanna Czenczek Date: Mon, 16 Oct 2023 15:42:40 +0200 Subject: [PATCH 665/974] vhost-user.rst: Migrating back-end-internal state For vhost-user devices, qemu can migrate the virtio state, but not the back-end's internal state. To do so, we need to be able to transfer this internal state between front-end (qemu) and back-end. At this point, this new feature is added for the purpose of virtio-fs migration. Because virtiofsd's internal state will not be too large, we believe it is best to transfer it as a single binary blob after the streaming phase. These are the additions to the protocol: - New vhost-user protocol feature VHOST_USER_PROTOCOL_F_DEVICE_STATE - SET_DEVICE_STATE_FD function: Front-end and back-end negotiate a file descriptor over which to transfer the state. - CHECK_DEVICE_STATE: After the state has been transferred through the file descriptor, the front-end invokes this function to verify success. There is no in-band way (through the file descriptor) to indicate failure, so we need to check explicitly. Once the transfer FD has been established via SET_DEVICE_STATE_FD (which includes establishing the direction of transfer and migration phase), the sending side writes its data into it, and the reading side reads it until it sees an EOF. Then, the front-end will check for success via CHECK_DEVICE_STATE, which on the destination side includes checking for integrity (i.e. errors during deserialization). Reviewed-by: Stefan Hajnoczi Signed-off-by: Hanna Czenczek Message-Id: <20231016134243.68248-5-hreitz@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- docs/interop/vhost-user.rst | 172 ++++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) diff --git a/docs/interop/vhost-user.rst b/docs/interop/vhost-user.rst index 035a23ed35..9f1103f85a 100644 --- a/docs/interop/vhost-user.rst +++ b/docs/interop/vhost-user.rst @@ -322,6 +322,32 @@ VhostUserShared :UUID: 16 bytes UUID, whose first three components (a 32-bit value, then two 16-bit values) are stored in big endian. +Device state transfer parameters +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ++--------------------+-----------------+ +| transfer direction | migration phase | ++--------------------+-----------------+ + +:transfer direction: a 32-bit enum, describing the direction in which + the state is transferred: + + - 0: Save: Transfer the state from the back-end to the front-end, + which happens on the source side of migration + - 1: Load: Transfer the state from the front-end to the back-end, + which happens on the destination side of migration + +:migration phase: a 32-bit enum, describing the state in which the VM + guest and devices are: + + - 0: Stopped (in the period after the transfer of memory-mapped + regions before switch-over to the destination): The VM guest is + stopped, and the vhost-user device is suspended (see + :ref:`Suspended device state `). + + In the future, additional phases might be added e.g. to allow + iterative migration while the device is running. + C structure ----------- @@ -381,6 +407,7 @@ in the ancillary data: * ``VHOST_USER_SET_VRING_ERR`` * ``VHOST_USER_SET_BACKEND_REQ_FD`` (previous name ``VHOST_USER_SET_SLAVE_REQ_FD``) * ``VHOST_USER_SET_INFLIGHT_FD`` (if ``VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD``) +* ``VHOST_USER_SET_DEVICE_STATE_FD`` If *front-end* is unable to send the full message or receives a wrong reply it will close the connection. An optional reconnection mechanism @@ -555,6 +582,80 @@ it performs WAKE ioctl's on the userfaultfd to wake the stalled back-end. The front-end indicates support for this via the ``VHOST_USER_PROTOCOL_F_PAGEFAULT`` feature. +.. _migrating_backend_state: + +Migrating back-end state +^^^^^^^^^^^^^^^^^^^^^^^^ + +Migrating device state involves transferring the state from one +back-end, called the source, to another back-end, called the +destination. After migration, the destination transparently resumes +operation without requiring the driver to re-initialize the device at +the VIRTIO level. If the migration fails, then the source can +transparently resume operation until another migration attempt is made. + +Generally, the front-end is connected to a virtual machine guest (which +contains the driver), which has its own state to transfer between source +and destination, and therefore will have an implementation-specific +mechanism to do so. The ``VHOST_USER_PROTOCOL_F_DEVICE_STATE`` feature +provides functionality to have the front-end include the back-end's +state in this transfer operation so the back-end does not need to +implement its own mechanism, and so the virtual machine may have its +complete state, including vhost-user devices' states, contained within a +single stream of data. + +To do this, the back-end state is transferred from back-end to front-end +on the source side, and vice versa on the destination side. This +transfer happens over a channel that is negotiated using the +``VHOST_USER_SET_DEVICE_STATE_FD`` message. This message has two +parameters: + +* Direction of transfer: On the source, the data is saved, transferring + it from the back-end to the front-end. On the destination, the data + is loaded, transferring it from the front-end to the back-end. + +* Migration phase: Currently, the only supported phase is the period + after the transfer of memory-mapped regions before switch-over to the + destination, when both the source and destination devices are + suspended (:ref:`Suspended device state `). + In the future, additional phases might be supported to allow iterative + migration while the device is running. + +The nature of the channel is implementation-defined, but it must +generally behave like a pipe: The writing end will write all the data it +has into it, signalling the end of data by closing its end. The reading +end must read all of this data (until encountering the end of file) and +process it. + +* When saving, the writing end is the source back-end, and the reading + end is the source front-end. After reading the state data from the + channel, the source front-end must transfer it to the destination + front-end through an implementation-defined mechanism. + +* When loading, the writing end is the destination front-end, and the + reading end is the destination back-end. After reading the state data + from the channel, the destination back-end must deserialize its + internal state from that data and set itself up to allow the driver to + seamlessly resume operation on the VIRTIO level. + +Seamlessly resuming operation means that the migration must be +transparent to the guest driver, which operates on the VIRTIO level. +This driver will not perform any re-initialization steps, but continue +to use the device as if no migration had occurred. The vhost-user +front-end, however, will re-initialize the vhost state on the +destination, following the usual protocol for establishing a connection +to a vhost-user back-end: This includes, for example, setting up memory +mappings and kick and call FDs as necessary, negotiating protocol +features, or setting the initial vring base indices (to the same value +as on the source side, so that operation can resume). + +Both on the source and on the destination side, after the respective +front-end has seen all data transferred (when the transfer FD has been +closed), it sends the ``VHOST_USER_CHECK_DEVICE_STATE`` message to +verify that data transfer was successful in the back-end, too. The +back-end responds once it knows whether the transfer and processing was +successful or not. + Memory access ------------- @@ -949,6 +1050,7 @@ Protocol features #define VHOST_USER_PROTOCOL_F_STATUS 16 #define VHOST_USER_PROTOCOL_F_XEN_MMAP 17 #define VHOST_USER_PROTOCOL_F_SHARED_OBJECT 18 + #define VHOST_USER_PROTOCOL_F_DEVICE_STATE 19 Front-end message types ----------------------- @@ -1553,6 +1655,76 @@ Front-end message types the requested UUID. Back-end will reply passing the fd when the operation is successful, or no fd otherwise. +``VHOST_USER_SET_DEVICE_STATE_FD`` + :id: 42 + :equivalent ioctl: N/A + :request payload: device state transfer parameters + :reply payload: ``u64`` + + Front-end and back-end negotiate a channel over which to transfer the + back-end’s internal state during migration. Either side (front-end or + back-end) may create the channel. The nature of this channel is not + restricted or defined in this document, but whichever side creates it + must create a file descriptor that is provided to the respectively + other side, allowing access to the channel. This FD must behave as + follows: + + * For the writing end, it must allow writing the whole back-end state + sequentially. Closing the file descriptor signals the end of + transfer. + + * For the reading end, it must allow reading the whole back-end state + sequentially. The end of file signals the end of the transfer. + + For example, the channel may be a pipe, in which case the two ends of + the pipe fulfill these requirements respectively. + + Initially, the front-end creates a channel along with such an FD. It + passes the FD to the back-end as ancillary data of a + ``VHOST_USER_SET_DEVICE_STATE_FD`` message. The back-end may create a + different transfer channel, passing the respective FD back to the + front-end as ancillary data of the reply. If so, the front-end must + then discard its channel and use the one provided by the back-end. + + Whether the back-end should decide to use its own channel is decided + based on efficiency: If the channel is a pipe, both ends will most + likely need to copy data into and out of it. Any channel that allows + for more efficient processing on at least one end, e.g. through + zero-copy, is considered more efficient and thus preferred. If the + back-end can provide such a channel, it should decide to use it. + + The request payload contains parameters for the subsequent data + transfer, as described in the :ref:`Migrating back-end state + ` section. + + The value returned is both an indication for success, and whether a + file descriptor for a back-end-provided channel is returned: Bits 0–7 + are 0 on success, and non-zero on error. Bit 8 is the invalid FD + flag; this flag is set when there is no file descriptor returned. + When this flag is not set, the front-end must use the returned file + descriptor as its end of the transfer channel. The back-end must not + both indicate an error and return a file descriptor. + + Using this function requires prior negotiation of the + ``VHOST_USER_PROTOCOL_F_DEVICE_STATE`` feature. + +``VHOST_USER_CHECK_DEVICE_STATE`` + :id: 43 + :equivalent ioctl: N/A + :request payload: N/A + :reply payload: ``u64`` + + After transferring the back-end’s internal state during migration (see + the :ref:`Migrating back-end state ` + section), check whether the back-end was able to successfully fully + process the state. + + The value returned indicates success or error; 0 is success, any + non-zero value is an error. + + Using this function requires prior negotiation of the + ``VHOST_USER_PROTOCOL_F_DEVICE_STATE`` feature. + Back-end message types ---------------------- From cda83adc62b6108afc8a82d0f54d9a9a861e7aa8 Mon Sep 17 00:00:00 2001 From: Hanna Czenczek Date: Mon, 16 Oct 2023 15:42:41 +0200 Subject: [PATCH 666/974] vhost-user: Interface for migration state transfer Add the interface for transferring the back-end's state during migration as defined previously in vhost-user.rst. Reviewed-by: Stefan Hajnoczi Signed-off-by: Hanna Czenczek Message-Id: <20231016134243.68248-6-hreitz@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost-user.c | 146 ++++++++++++++++++++++++++++++ hw/virtio/vhost.c | 37 ++++++++ include/hw/virtio/vhost-backend.h | 24 +++++ include/hw/virtio/vhost-user.h | 1 + include/hw/virtio/vhost.h | 78 ++++++++++++++++ 5 files changed, 286 insertions(+) diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 7b42ae8aae..f214df804b 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -103,6 +103,8 @@ typedef enum VhostUserRequest { VHOST_USER_SET_STATUS = 39, VHOST_USER_GET_STATUS = 40, VHOST_USER_GET_SHARED_OBJECT = 41, + VHOST_USER_SET_DEVICE_STATE_FD = 42, + VHOST_USER_CHECK_DEVICE_STATE = 43, VHOST_USER_MAX } VhostUserRequest; @@ -201,6 +203,12 @@ typedef struct { uint32_t size; /* the following payload size */ } QEMU_PACKED VhostUserHeader; +/* Request payload of VHOST_USER_SET_DEVICE_STATE_FD */ +typedef struct VhostUserTransferDeviceState { + uint32_t direction; + uint32_t phase; +} VhostUserTransferDeviceState; + typedef union { #define VHOST_USER_VRING_IDX_MASK (0xff) #define VHOST_USER_VRING_NOFD_MASK (0x1 << 8) @@ -216,6 +224,7 @@ typedef union { VhostUserVringArea area; VhostUserInflight inflight; VhostUserShared object; + VhostUserTransferDeviceState transfer_state; } VhostUserPayload; typedef struct VhostUserMsg { @@ -2855,6 +2864,140 @@ static void vhost_user_reset_status(struct vhost_dev *dev) } } +static bool vhost_user_supports_device_state(struct vhost_dev *dev) +{ + return virtio_has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_DEVICE_STATE); +} + +static int vhost_user_set_device_state_fd(struct vhost_dev *dev, + VhostDeviceStateDirection direction, + VhostDeviceStatePhase phase, + int fd, + int *reply_fd, + Error **errp) +{ + int ret; + struct vhost_user *vu = dev->opaque; + VhostUserMsg msg = { + .hdr = { + .request = VHOST_USER_SET_DEVICE_STATE_FD, + .flags = VHOST_USER_VERSION, + .size = sizeof(msg.payload.transfer_state), + }, + .payload.transfer_state = { + .direction = direction, + .phase = phase, + }, + }; + + *reply_fd = -1; + + if (!vhost_user_supports_device_state(dev)) { + close(fd); + error_setg(errp, "Back-end does not support migration state transfer"); + return -ENOTSUP; + } + + ret = vhost_user_write(dev, &msg, &fd, 1); + close(fd); + if (ret < 0) { + error_setg_errno(errp, -ret, + "Failed to send SET_DEVICE_STATE_FD message"); + return ret; + } + + ret = vhost_user_read(dev, &msg); + if (ret < 0) { + error_setg_errno(errp, -ret, + "Failed to receive SET_DEVICE_STATE_FD reply"); + return ret; + } + + if (msg.hdr.request != VHOST_USER_SET_DEVICE_STATE_FD) { + error_setg(errp, + "Received unexpected message type, expected %d, received %d", + VHOST_USER_SET_DEVICE_STATE_FD, msg.hdr.request); + return -EPROTO; + } + + if (msg.hdr.size != sizeof(msg.payload.u64)) { + error_setg(errp, + "Received bad message size, expected %zu, received %" PRIu32, + sizeof(msg.payload.u64), msg.hdr.size); + return -EPROTO; + } + + if ((msg.payload.u64 & 0xff) != 0) { + error_setg(errp, "Back-end did not accept migration state transfer"); + return -EIO; + } + + if (!(msg.payload.u64 & VHOST_USER_VRING_NOFD_MASK)) { + *reply_fd = qemu_chr_fe_get_msgfd(vu->user->chr); + if (*reply_fd < 0) { + error_setg(errp, + "Failed to get back-end-provided transfer pipe FD"); + *reply_fd = -1; + return -EIO; + } + } + + return 0; +} + +static int vhost_user_check_device_state(struct vhost_dev *dev, Error **errp) +{ + int ret; + VhostUserMsg msg = { + .hdr = { + .request = VHOST_USER_CHECK_DEVICE_STATE, + .flags = VHOST_USER_VERSION, + .size = 0, + }, + }; + + if (!vhost_user_supports_device_state(dev)) { + error_setg(errp, "Back-end does not support migration state transfer"); + return -ENOTSUP; + } + + ret = vhost_user_write(dev, &msg, NULL, 0); + if (ret < 0) { + error_setg_errno(errp, -ret, + "Failed to send CHECK_DEVICE_STATE message"); + return ret; + } + + ret = vhost_user_read(dev, &msg); + if (ret < 0) { + error_setg_errno(errp, -ret, + "Failed to receive CHECK_DEVICE_STATE reply"); + return ret; + } + + if (msg.hdr.request != VHOST_USER_CHECK_DEVICE_STATE) { + error_setg(errp, + "Received unexpected message type, expected %d, received %d", + VHOST_USER_CHECK_DEVICE_STATE, msg.hdr.request); + return -EPROTO; + } + + if (msg.hdr.size != sizeof(msg.payload.u64)) { + error_setg(errp, + "Received bad message size, expected %zu, received %" PRIu32, + sizeof(msg.payload.u64), msg.hdr.size); + return -EPROTO; + } + + if (msg.payload.u64 != 0) { + error_setg(errp, "Back-end failed to process its internal state"); + return -EIO; + } + + return 0; +} + const VhostOps user_ops = { .backend_type = VHOST_BACKEND_TYPE_USER, .vhost_backend_init = vhost_user_backend_init, @@ -2890,4 +3033,7 @@ const VhostOps user_ops = { .vhost_set_inflight_fd = vhost_user_set_inflight_fd, .vhost_dev_start = vhost_user_dev_start, .vhost_reset_status = vhost_user_reset_status, + .vhost_supports_device_state = vhost_user_supports_device_state, + .vhost_set_device_state_fd = vhost_user_set_device_state_fd, + .vhost_check_device_state = vhost_user_check_device_state, }; diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 9c9ae7109e..4db9dbfd64 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -2159,3 +2159,40 @@ int vhost_reset_device(struct vhost_dev *hdev) return -ENOSYS; } + +bool vhost_supports_device_state(struct vhost_dev *dev) +{ + if (dev->vhost_ops->vhost_supports_device_state) { + return dev->vhost_ops->vhost_supports_device_state(dev); + } + + return false; +} + +int vhost_set_device_state_fd(struct vhost_dev *dev, + VhostDeviceStateDirection direction, + VhostDeviceStatePhase phase, + int fd, + int *reply_fd, + Error **errp) +{ + if (dev->vhost_ops->vhost_set_device_state_fd) { + return dev->vhost_ops->vhost_set_device_state_fd(dev, direction, phase, + fd, reply_fd, errp); + } + + error_setg(errp, + "vhost transport does not support migration state transfer"); + return -ENOSYS; +} + +int vhost_check_device_state(struct vhost_dev *dev, Error **errp) +{ + if (dev->vhost_ops->vhost_check_device_state) { + return dev->vhost_ops->vhost_check_device_state(dev, errp); + } + + error_setg(errp, + "vhost transport does not support migration state transfer"); + return -ENOSYS; +} diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h index 96ccc18cd3..a86d103f82 100644 --- a/include/hw/virtio/vhost-backend.h +++ b/include/hw/virtio/vhost-backend.h @@ -26,6 +26,18 @@ typedef enum VhostSetConfigType { VHOST_SET_CONFIG_TYPE_MIGRATION = 1, } VhostSetConfigType; +typedef enum VhostDeviceStateDirection { + /* Transfer state from back-end (device) to front-end */ + VHOST_TRANSFER_STATE_DIRECTION_SAVE = 0, + /* Transfer state from front-end to back-end (device) */ + VHOST_TRANSFER_STATE_DIRECTION_LOAD = 1, +} VhostDeviceStateDirection; + +typedef enum VhostDeviceStatePhase { + /* The device (and all its vrings) is stopped */ + VHOST_TRANSFER_STATE_PHASE_STOPPED = 0, +} VhostDeviceStatePhase; + struct vhost_inflight; struct vhost_dev; struct vhost_log; @@ -129,6 +141,15 @@ typedef int (*vhost_set_config_call_op)(struct vhost_dev *dev, typedef void (*vhost_reset_status_op)(struct vhost_dev *dev); +typedef bool (*vhost_supports_device_state_op)(struct vhost_dev *dev); +typedef int (*vhost_set_device_state_fd_op)(struct vhost_dev *dev, + VhostDeviceStateDirection direction, + VhostDeviceStatePhase phase, + int fd, + int *reply_fd, + Error **errp); +typedef int (*vhost_check_device_state_op)(struct vhost_dev *dev, Error **errp); + typedef struct VhostOps { VhostBackendType backend_type; vhost_backend_init vhost_backend_init; @@ -176,6 +197,9 @@ typedef struct VhostOps { vhost_force_iommu_op vhost_force_iommu; vhost_set_config_call_op vhost_set_config_call; vhost_reset_status_op vhost_reset_status; + vhost_supports_device_state_op vhost_supports_device_state; + vhost_set_device_state_fd_op vhost_set_device_state_fd; + vhost_check_device_state_op vhost_check_device_state; } VhostOps; int vhost_backend_update_device_iotlb(struct vhost_dev *dev, diff --git a/include/hw/virtio/vhost-user.h b/include/hw/virtio/vhost-user.h index 20b69d8e85..d7c09ffd34 100644 --- a/include/hw/virtio/vhost-user.h +++ b/include/hw/virtio/vhost-user.h @@ -31,6 +31,7 @@ enum VhostUserProtocolFeature { VHOST_USER_PROTOCOL_F_STATUS = 16, /* Feature 17 reserved for VHOST_USER_PROTOCOL_F_XEN_MMAP. */ VHOST_USER_PROTOCOL_F_SHARED_OBJECT = 18, + VHOST_USER_PROTOCOL_F_DEVICE_STATE = 19, VHOST_USER_PROTOCOL_F_MAX }; diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index 5e8183f64a..b6ee6da6ce 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -351,4 +351,82 @@ static inline int vhost_reset_device(struct vhost_dev *hdev) } #endif /* CONFIG_VHOST */ +/** + * vhost_supports_device_state(): Checks whether the back-end supports + * transferring internal device state for the purpose of migration. + * Support for this feature is required for vhost_set_device_state_fd() + * and vhost_check_device_state(). + * + * @dev: The vhost device + * + * Returns true if the device supports these commands, and false if it + * does not. + */ +bool vhost_supports_device_state(struct vhost_dev *dev); + +/** + * vhost_set_device_state_fd(): Begin transfer of internal state from/to + * the back-end for the purpose of migration. Data is to be transferred + * over a pipe according to @direction and @phase. The sending end must + * only write to the pipe, and the receiving end must only read from it. + * Once the sending end is done, it closes its FD. The receiving end + * must take this as the end-of-transfer signal and close its FD, too. + * + * @fd is the back-end's end of the pipe: The write FD for SAVE, and the + * read FD for LOAD. This function transfers ownership of @fd to the + * back-end, i.e. closes it in the front-end. + * + * The back-end may optionally reply with an FD of its own, if this + * improves efficiency on its end. In this case, the returned FD is + * stored in *reply_fd. The back-end will discard the FD sent to it, + * and the front-end must use *reply_fd for transferring state to/from + * the back-end. + * + * @dev: The vhost device + * @direction: The direction in which the state is to be transferred. + * For outgoing migrations, this is SAVE, and data is read + * from the back-end and stored by the front-end in the + * migration stream. + * For incoming migrations, this is LOAD, and data is read + * by the front-end from the migration stream and sent to + * the back-end to restore the saved state. + * @phase: Which migration phase we are in. Currently, there is only + * STOPPED (device and all vrings are stopped), in the future, + * more phases such as PRE_COPY or POST_COPY may be added. + * @fd: Back-end's end of the pipe through which to transfer state; note + * that ownership is transferred to the back-end, so this function + * closes @fd in the front-end. + * @reply_fd: If the back-end wishes to use a different pipe for state + * transfer, this will contain an FD for the front-end to + * use. Otherwise, -1 is stored here. + * @errp: Potential error description + * + * Returns 0 on success, and -errno on failure. + */ +int vhost_set_device_state_fd(struct vhost_dev *dev, + VhostDeviceStateDirection direction, + VhostDeviceStatePhase phase, + int fd, + int *reply_fd, + Error **errp); + +/** + * vhost_set_device_state_fd(): After transferring state from/to the + * back-end via vhost_set_device_state_fd(), i.e. once the sending end + * has closed the pipe, inquire the back-end to report any potential + * errors that have occurred on its side. This allows to sense errors + * like: + * - During outgoing migration, when the source side had already started + * to produce its state, something went wrong and it failed to finish + * - During incoming migration, when the received state is somehow + * invalid and cannot be processed by the back-end + * + * @dev: The vhost device + * @errp: Potential error description + * + * Returns 0 when the back-end reports successful state transfer and + * processing, and -errno when an error occurred somewhere. + */ +int vhost_check_device_state(struct vhost_dev *dev, Error **errp); + #endif From 4a00d5d7f4b65ba99b33d5a0d6f8c563895839ea Mon Sep 17 00:00:00 2001 From: Hanna Czenczek Date: Mon, 16 Oct 2023 15:42:42 +0200 Subject: [PATCH 667/974] vhost: Add high-level state save/load functions vhost_save_backend_state() and vhost_load_backend_state() can be used by vhost front-ends to easily save and load the back-end's state to/from the migration stream. Because we do not know the full state size ahead of time, vhost_save_backend_state() simply reads the data in 1 MB chunks, and writes each chunk consecutively into the migration stream, prefixed by its length. EOF is indicated by a 0-length chunk. Reviewed-by: Stefan Hajnoczi Signed-off-by: Hanna Czenczek Message-Id: <20231016134243.68248-7-hreitz@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost.c | 204 ++++++++++++++++++++++++++++++++++++++ include/hw/virtio/vhost.h | 35 +++++++ 2 files changed, 239 insertions(+) diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 4db9dbfd64..2c9ac79468 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -2196,3 +2196,207 @@ int vhost_check_device_state(struct vhost_dev *dev, Error **errp) "vhost transport does not support migration state transfer"); return -ENOSYS; } + +int vhost_save_backend_state(struct vhost_dev *dev, QEMUFile *f, Error **errp) +{ + /* Maximum chunk size in which to transfer the state */ + const size_t chunk_size = 1 * 1024 * 1024; + g_autofree void *transfer_buf = NULL; + g_autoptr(GError) g_err = NULL; + int pipe_fds[2], read_fd = -1, write_fd = -1, reply_fd = -1; + int ret; + + /* [0] for reading (our end), [1] for writing (back-end's end) */ + if (!g_unix_open_pipe(pipe_fds, FD_CLOEXEC, &g_err)) { + error_setg(errp, "Failed to set up state transfer pipe: %s", + g_err->message); + ret = -EINVAL; + goto fail; + } + + read_fd = pipe_fds[0]; + write_fd = pipe_fds[1]; + + /* + * VHOST_TRANSFER_STATE_PHASE_STOPPED means the device must be stopped. + * Ideally, it is suspended, but SUSPEND/RESUME currently do not exist for + * vhost-user, so just check that it is stopped at all. + */ + assert(!dev->started); + + /* Transfer ownership of write_fd to the back-end */ + ret = vhost_set_device_state_fd(dev, + VHOST_TRANSFER_STATE_DIRECTION_SAVE, + VHOST_TRANSFER_STATE_PHASE_STOPPED, + write_fd, + &reply_fd, + errp); + if (ret < 0) { + error_prepend(errp, "Failed to initiate state transfer: "); + goto fail; + } + + /* If the back-end wishes to use a different pipe, switch over */ + if (reply_fd >= 0) { + close(read_fd); + read_fd = reply_fd; + } + + transfer_buf = g_malloc(chunk_size); + + while (true) { + ssize_t read_ret; + + read_ret = RETRY_ON_EINTR(read(read_fd, transfer_buf, chunk_size)); + if (read_ret < 0) { + ret = -errno; + error_setg_errno(errp, -ret, "Failed to receive state"); + goto fail; + } + + assert(read_ret <= chunk_size); + qemu_put_be32(f, read_ret); + + if (read_ret == 0) { + /* EOF */ + break; + } + + qemu_put_buffer(f, transfer_buf, read_ret); + } + + /* + * Back-end will not really care, but be clean and close our end of the pipe + * before inquiring the back-end about whether transfer was successful + */ + close(read_fd); + read_fd = -1; + + /* Also, verify that the device is still stopped */ + assert(!dev->started); + + ret = vhost_check_device_state(dev, errp); + if (ret < 0) { + goto fail; + } + + ret = 0; +fail: + if (read_fd >= 0) { + close(read_fd); + } + + return ret; +} + +int vhost_load_backend_state(struct vhost_dev *dev, QEMUFile *f, Error **errp) +{ + size_t transfer_buf_size = 0; + g_autofree void *transfer_buf = NULL; + g_autoptr(GError) g_err = NULL; + int pipe_fds[2], read_fd = -1, write_fd = -1, reply_fd = -1; + int ret; + + /* [0] for reading (back-end's end), [1] for writing (our end) */ + if (!g_unix_open_pipe(pipe_fds, FD_CLOEXEC, &g_err)) { + error_setg(errp, "Failed to set up state transfer pipe: %s", + g_err->message); + ret = -EINVAL; + goto fail; + } + + read_fd = pipe_fds[0]; + write_fd = pipe_fds[1]; + + /* + * VHOST_TRANSFER_STATE_PHASE_STOPPED means the device must be stopped. + * Ideally, it is suspended, but SUSPEND/RESUME currently do not exist for + * vhost-user, so just check that it is stopped at all. + */ + assert(!dev->started); + + /* Transfer ownership of read_fd to the back-end */ + ret = vhost_set_device_state_fd(dev, + VHOST_TRANSFER_STATE_DIRECTION_LOAD, + VHOST_TRANSFER_STATE_PHASE_STOPPED, + read_fd, + &reply_fd, + errp); + if (ret < 0) { + error_prepend(errp, "Failed to initiate state transfer: "); + goto fail; + } + + /* If the back-end wishes to use a different pipe, switch over */ + if (reply_fd >= 0) { + close(write_fd); + write_fd = reply_fd; + } + + while (true) { + size_t this_chunk_size = qemu_get_be32(f); + ssize_t write_ret; + const uint8_t *transfer_pointer; + + if (this_chunk_size == 0) { + /* End of state */ + break; + } + + if (transfer_buf_size < this_chunk_size) { + transfer_buf = g_realloc(transfer_buf, this_chunk_size); + transfer_buf_size = this_chunk_size; + } + + if (qemu_get_buffer(f, transfer_buf, this_chunk_size) < + this_chunk_size) + { + error_setg(errp, "Failed to read state"); + ret = -EINVAL; + goto fail; + } + + transfer_pointer = transfer_buf; + while (this_chunk_size > 0) { + write_ret = RETRY_ON_EINTR( + write(write_fd, transfer_pointer, this_chunk_size) + ); + if (write_ret < 0) { + ret = -errno; + error_setg_errno(errp, -ret, "Failed to send state"); + goto fail; + } else if (write_ret == 0) { + error_setg(errp, "Failed to send state: Connection is closed"); + ret = -ECONNRESET; + goto fail; + } + + assert(write_ret <= this_chunk_size); + this_chunk_size -= write_ret; + transfer_pointer += write_ret; + } + } + + /* + * Close our end, thus ending transfer, before inquiring the back-end about + * whether transfer was successful + */ + close(write_fd); + write_fd = -1; + + /* Also, verify that the device is still stopped */ + assert(!dev->started); + + ret = vhost_check_device_state(dev, errp); + if (ret < 0) { + goto fail; + } + + ret = 0; +fail: + if (write_fd >= 0) { + close(write_fd); + } + + return ret; +} diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index b6ee6da6ce..05d7204a08 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -429,4 +429,39 @@ int vhost_set_device_state_fd(struct vhost_dev *dev, */ int vhost_check_device_state(struct vhost_dev *dev, Error **errp); +/** + * vhost_save_backend_state(): High-level function to receive a vhost + * back-end's state, and save it in @f. Uses + * `vhost_set_device_state_fd()` to get the data from the back-end, and + * stores it in consecutive chunks that are each prefixed by their + * respective length (be32). The end is marked by a 0-length chunk. + * + * Must only be called while the device and all its vrings are stopped + * (`VHOST_TRANSFER_STATE_PHASE_STOPPED`). + * + * @dev: The vhost device from which to save the state + * @f: Migration stream in which to save the state + * @errp: Potential error message + * + * Returns 0 on success, and -errno otherwise. + */ +int vhost_save_backend_state(struct vhost_dev *dev, QEMUFile *f, Error **errp); + +/** + * vhost_load_backend_state(): High-level function to load a vhost + * back-end's state from @f, and send it over to the back-end. Reads + * the data from @f in the format used by `vhost_save_state()`, and uses + * `vhost_set_device_state_fd()` to transfer it to the back-end. + * + * Must only be called while the device and all its vrings are stopped + * (`VHOST_TRANSFER_STATE_PHASE_STOPPED`). + * + * @dev: The vhost device to which to send the sate + * @f: Migration stream from which to load the state + * @errp: Potential error message + * + * Returns 0 on success, and -errno otherwise. + */ +int vhost_load_backend_state(struct vhost_dev *dev, QEMUFile *f, Error **errp); + #endif From bca3e2a13814253b4ed878a3313688554edd1b66 Mon Sep 17 00:00:00 2001 From: Hanna Czenczek Date: Mon, 16 Oct 2023 15:42:43 +0200 Subject: [PATCH 668/974] vhost-user-fs: Implement internal migration A virtio-fs device's VM state consists of: - the virtio device (vring) state (VMSTATE_VIRTIO_DEVICE) - the back-end's (virtiofsd's) internal state We get/set the latter via the new vhost operations to transfer migratory state. It is its own dedicated subsection, so that for external migration, it can be disabled. Reviewed-by: Stefan Hajnoczi Signed-off-by: Hanna Czenczek Message-Id: <20231016134243.68248-8-hreitz@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost-user-fs.c | 101 +++++++++++++++++++++++++++++++++++++- 1 file changed, 100 insertions(+), 1 deletion(-) diff --git a/hw/virtio/vhost-user-fs.c b/hw/virtio/vhost-user-fs.c index 49d699ffc2..eb91723855 100644 --- a/hw/virtio/vhost-user-fs.c +++ b/hw/virtio/vhost-user-fs.c @@ -298,9 +298,108 @@ static struct vhost_dev *vuf_get_vhost(VirtIODevice *vdev) return &fs->vhost_dev; } +/** + * Fetch the internal state from virtiofsd and save it to `f`. + */ +static int vuf_save_state(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, JSONWriter *vmdesc) +{ + VirtIODevice *vdev = pv; + VHostUserFS *fs = VHOST_USER_FS(vdev); + Error *local_error = NULL; + int ret; + + ret = vhost_save_backend_state(&fs->vhost_dev, f, &local_error); + if (ret < 0) { + error_reportf_err(local_error, + "Error saving back-end state of %s device %s " + "(tag: \"%s\"): ", + vdev->name, vdev->parent_obj.canonical_path, + fs->conf.tag ?: ""); + return ret; + } + + return 0; +} + +/** + * Load virtiofsd's internal state from `f` and send it over to virtiofsd. + */ +static int vuf_load_state(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) +{ + VirtIODevice *vdev = pv; + VHostUserFS *fs = VHOST_USER_FS(vdev); + Error *local_error = NULL; + int ret; + + ret = vhost_load_backend_state(&fs->vhost_dev, f, &local_error); + if (ret < 0) { + error_reportf_err(local_error, + "Error loading back-end state of %s device %s " + "(tag: \"%s\"): ", + vdev->name, vdev->parent_obj.canonical_path, + fs->conf.tag ?: ""); + return ret; + } + + return 0; +} + +static bool vuf_is_internal_migration(void *opaque) +{ + /* TODO: Return false when an external migration is requested */ + return true; +} + +static int vuf_check_migration_support(void *opaque) +{ + VirtIODevice *vdev = opaque; + VHostUserFS *fs = VHOST_USER_FS(vdev); + + if (!vhost_supports_device_state(&fs->vhost_dev)) { + error_report("Back-end of %s device %s (tag: \"%s\") does not support " + "migration through qemu", + vdev->name, vdev->parent_obj.canonical_path, + fs->conf.tag ?: ""); + return -ENOTSUP; + } + + return 0; +} + +static const VMStateDescription vuf_backend_vmstate; + static const VMStateDescription vuf_vmstate = { .name = "vhost-user-fs", - .unmigratable = 1, + .version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_VIRTIO_DEVICE, + VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * []) { + &vuf_backend_vmstate, + NULL, + } +}; + +static const VMStateDescription vuf_backend_vmstate = { + .name = "vhost-user-fs-backend", + .version_id = 0, + .needed = vuf_is_internal_migration, + .pre_load = vuf_check_migration_support, + .pre_save = vuf_check_migration_support, + .fields = (VMStateField[]) { + { + .name = "back-end", + .info = &(const VMStateInfo) { + .name = "virtio-fs back-end state", + .get = vuf_load_state, + .put = vuf_save_state, + }, + }, + VMSTATE_END_OF_LIST() + }, }; static Property vuf_properties[] = { From 2880e676c000a62828d3d9ece7b2ec7a513560a2 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Mon, 23 Oct 2023 15:03:18 +0300 Subject: [PATCH 669/974] Add virtio-sound device stub MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new VIRTIO device for the virtio sound device id. Functionality will be added in the following commits. Based-on: https://github.com/OpenSynergy/qemu/commit/5a2f350eec5d157b90d9c7b40a8e603f4da92471 Signed-off-by: Igor Skalkin Signed-off-by: Anton Yakovlev Signed-off-by: Manos Pitsidianakis Reviewed-by: Alex Bennée Tested-by: Alex Bennée Message-Id: Acked-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- MAINTAINERS | 7 + hw/audio/Kconfig | 5 + hw/audio/meson.build | 1 + hw/audio/trace-events | 9 ++ hw/audio/virtio-snd.c | 233 ++++++++++++++++++++++++++++++++++ include/hw/audio/virtio-snd.h | 79 ++++++++++++ system/qdev-monitor.c | 1 + 7 files changed, 335 insertions(+) create mode 100644 hw/audio/virtio-snd.c create mode 100644 include/hw/audio/virtio-snd.h diff --git a/MAINTAINERS b/MAINTAINERS index 8e8a7d5be5..d3ee463d21 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2310,6 +2310,13 @@ F: hw/virtio/virtio-mem-pci.h F: hw/virtio/virtio-mem-pci.c F: include/hw/virtio/virtio-mem.h +virtio-snd +M: Gerd Hoffmann +R: Manos Pitsidianakis +S: Supported +F: hw/audio/virtio-snd.c +F: include/hw/audio/virtio-snd.h + nvme M: Keith Busch M: Klaus Jensen diff --git a/hw/audio/Kconfig b/hw/audio/Kconfig index d0993514a1..daf060e1be 100644 --- a/hw/audio/Kconfig +++ b/hw/audio/Kconfig @@ -50,3 +50,8 @@ config CS4231 config ASC bool + +config VIRTIO_SND + bool + default y + depends on VIRTIO diff --git a/hw/audio/meson.build b/hw/audio/meson.build index 8805322f5c..7a503be1fd 100644 --- a/hw/audio/meson.build +++ b/hw/audio/meson.build @@ -13,3 +13,4 @@ system_ss.add(when: 'CONFIG_PL041', if_true: files('pl041.c', 'lm4549.c')) system_ss.add(when: 'CONFIG_SB16', if_true: files('sb16.c')) system_ss.add(when: 'CONFIG_VT82C686', if_true: files('via-ac97.c')) system_ss.add(when: 'CONFIG_WM8750', if_true: files('wm8750.c')) +system_ss.add(when: ['CONFIG_VIRTIO_SND', 'CONFIG_VIRTIO'], if_true: files('virtio-snd.c')) diff --git a/hw/audio/trace-events b/hw/audio/trace-events index 059ce451f5..525ced2b34 100644 --- a/hw/audio/trace-events +++ b/hw/audio/trace-events @@ -38,3 +38,12 @@ asc_write_fifo(const char fifo, int reg, unsigned size, int wrptr, int cnt, uint asc_write_reg(int reg, unsigned size, uint64_t value) "reg=0x%03x size=%u value=0x%"PRIx64 asc_write_extreg(const char fifo, int reg, unsigned size, uint64_t value) "fifo %c reg=0x%03x size=%u value=0x%"PRIx64 asc_update_irq(int irq, int a, int b) "set IRQ to %d (A: 0x%x B: 0x%x)" + +#virtio-snd.c +virtio_snd_get_config(void *vdev, uint32_t jacks, uint32_t streams, uint32_t chmaps) "snd %p: get_config jacks=%"PRIu32" streams=%"PRIu32" chmaps=%"PRIu32"" +virtio_snd_set_config(void *vdev, uint32_t jacks, uint32_t new_jacks, uint32_t streams, uint32_t new_streams, uint32_t chmaps, uint32_t new_chmaps) "snd %p: set_config jacks from %"PRIu32"->%"PRIu32", streams from %"PRIu32"->%"PRIu32", chmaps from %"PRIu32"->%"PRIu32 +virtio_snd_get_features(void *vdev, uint64_t features) "snd %p: get_features 0x%"PRIx64 +virtio_snd_vm_state_running(void) "vm state running" +virtio_snd_vm_state_stopped(void) "vm state stopped" +virtio_snd_realize(void *snd) "snd %p: realize" +virtio_snd_unrealize(void *snd) "snd %p: unrealize" diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c new file mode 100644 index 0000000000..3a4b441c20 --- /dev/null +++ b/hw/audio/virtio-snd.c @@ -0,0 +1,233 @@ +/* + * VIRTIO Sound Device conforming to + * + * "Virtual I/O Device (VIRTIO) Version 1.2 + * Committee Specification Draft 01 + * 09 May 2022" + * + * + * + * Copyright (c) 2023 Emmanouil Pitsidianakis + * Copyright (C) 2019 OpenSynergy GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/iov.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "include/qemu/lockable.h" +#include "sysemu/runstate.h" +#include "trace.h" +#include "qapi/error.h" +#include "hw/audio/virtio-snd.h" +#include "hw/core/cpu.h" + +#define VIRTIO_SOUND_VM_VERSION 1 +#define VIRTIO_SOUND_JACK_DEFAULT 0 +#define VIRTIO_SOUND_STREAM_DEFAULT 1 +#define VIRTIO_SOUND_CHMAP_DEFAULT 0 +#define VIRTIO_SOUND_HDA_FN_NID 0 + +static const VMStateDescription vmstate_virtio_snd_device = { + .name = TYPE_VIRTIO_SND, + .version_id = VIRTIO_SOUND_VM_VERSION, + .minimum_version_id = VIRTIO_SOUND_VM_VERSION, +}; + +static const VMStateDescription vmstate_virtio_snd = { + .name = TYPE_VIRTIO_SND, + .minimum_version_id = VIRTIO_SOUND_VM_VERSION, + .version_id = VIRTIO_SOUND_VM_VERSION, + .fields = (VMStateField[]) { + VMSTATE_VIRTIO_DEVICE, + VMSTATE_END_OF_LIST() + }, +}; + +static Property virtio_snd_properties[] = { + DEFINE_AUDIO_PROPERTIES(VirtIOSound, card), + DEFINE_PROP_UINT32("jacks", VirtIOSound, snd_conf.jacks, + VIRTIO_SOUND_JACK_DEFAULT), + DEFINE_PROP_UINT32("streams", VirtIOSound, snd_conf.streams, + VIRTIO_SOUND_STREAM_DEFAULT), + DEFINE_PROP_UINT32("chmaps", VirtIOSound, snd_conf.chmaps, + VIRTIO_SOUND_CHMAP_DEFAULT), + DEFINE_PROP_END_OF_LIST(), +}; + +static void +virtio_snd_get_config(VirtIODevice *vdev, uint8_t *config) +{ + VirtIOSound *s = VIRTIO_SND(vdev); + virtio_snd_config *sndconfig = + (virtio_snd_config *)config; + trace_virtio_snd_get_config(vdev, + s->snd_conf.jacks, + s->snd_conf.streams, + s->snd_conf.chmaps); + + memcpy(sndconfig, &s->snd_conf, sizeof(s->snd_conf)); + cpu_to_le32s(&sndconfig->jacks); + cpu_to_le32s(&sndconfig->streams); + cpu_to_le32s(&sndconfig->chmaps); + +} + +static void +virtio_snd_set_config(VirtIODevice *vdev, const uint8_t *config) +{ + VirtIOSound *s = VIRTIO_SND(vdev); + const virtio_snd_config *sndconfig = + (const virtio_snd_config *)config; + + + trace_virtio_snd_set_config(vdev, + s->snd_conf.jacks, + sndconfig->jacks, + s->snd_conf.streams, + sndconfig->streams, + s->snd_conf.chmaps, + sndconfig->chmaps); + + memcpy(&s->snd_conf, sndconfig, sizeof(virtio_snd_config)); + le32_to_cpus(&s->snd_conf.jacks); + le32_to_cpus(&s->snd_conf.streams); + le32_to_cpus(&s->snd_conf.chmaps); + +} + +/* + * Queue handler stub. + * + * @vdev: VirtIOSound device + * @vq: virtqueue + */ +static void virtio_snd_handle_queue(VirtIODevice *vdev, VirtQueue *vq) {} + +static uint64_t get_features(VirtIODevice *vdev, uint64_t features, + Error **errp) +{ + /* + * virtio-v1.2-csd01, 5.14.3, + * Feature Bits + * None currently defined. + */ + VirtIOSound *s = VIRTIO_SND(vdev); + features |= s->features; + + trace_virtio_snd_get_features(vdev, features); + + return features; +} + +static void +virtio_snd_vm_state_change(void *opaque, bool running, + RunState state) +{ + if (running) { + trace_virtio_snd_vm_state_running(); + } else { + trace_virtio_snd_vm_state_stopped(); + } +} + +static void virtio_snd_realize(DeviceState *dev, Error **errp) +{ + ERRP_GUARD(); + VirtIOSound *vsnd = VIRTIO_SND(dev); + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + + vsnd->vmstate = + qemu_add_vm_change_state_handler(virtio_snd_vm_state_change, vsnd); + + trace_virtio_snd_realize(vsnd); + + virtio_init(vdev, VIRTIO_ID_SOUND, sizeof(virtio_snd_config)); + virtio_add_feature(&vsnd->features, VIRTIO_F_VERSION_1); + + /* set number of jacks and streams */ + if (vsnd->snd_conf.jacks > 8) { + error_setg(errp, + "Invalid number of jacks: %"PRIu32, + vsnd->snd_conf.jacks); + return; + } + if (vsnd->snd_conf.streams < 1 || vsnd->snd_conf.streams > 10) { + error_setg(errp, + "Invalid number of streams: %"PRIu32, + vsnd->snd_conf.streams); + return; + } + + if (vsnd->snd_conf.chmaps > VIRTIO_SND_CHMAP_MAX_SIZE) { + error_setg(errp, + "Invalid number of channel maps: %"PRIu32, + vsnd->snd_conf.chmaps); + return; + } + + AUD_register_card("virtio-sound", &vsnd->card, errp); + + vsnd->queues[VIRTIO_SND_VQ_CONTROL] = + virtio_add_queue(vdev, 64, virtio_snd_handle_queue); + vsnd->queues[VIRTIO_SND_VQ_EVENT] = + virtio_add_queue(vdev, 64, virtio_snd_handle_queue); + vsnd->queues[VIRTIO_SND_VQ_TX] = + virtio_add_queue(vdev, 64, virtio_snd_handle_queue); + vsnd->queues[VIRTIO_SND_VQ_RX] = + virtio_add_queue(vdev, 64, virtio_snd_handle_queue); +} + +static void virtio_snd_unrealize(DeviceState *dev) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VirtIOSound *vsnd = VIRTIO_SND(dev); + + qemu_del_vm_change_state_handler(vsnd->vmstate); + trace_virtio_snd_unrealize(vsnd); + + AUD_remove_card(&vsnd->card); + virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_CONTROL]); + virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_EVENT]); + virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_TX]); + virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_RX]); + virtio_cleanup(vdev); +} + + +static void virtio_snd_reset(VirtIODevice *vdev) {} + +static void virtio_snd_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); + + + set_bit(DEVICE_CATEGORY_SOUND, dc->categories); + device_class_set_props(dc, virtio_snd_properties); + + dc->vmsd = &vmstate_virtio_snd; + vdc->vmsd = &vmstate_virtio_snd_device; + vdc->realize = virtio_snd_realize; + vdc->unrealize = virtio_snd_unrealize; + vdc->get_config = virtio_snd_get_config; + vdc->set_config = virtio_snd_set_config; + vdc->get_features = get_features; + vdc->reset = virtio_snd_reset; + vdc->legacy_features = 0; +} + +static const TypeInfo virtio_snd_types[] = { + { + .name = TYPE_VIRTIO_SND, + .parent = TYPE_VIRTIO_DEVICE, + .instance_size = sizeof(VirtIOSound), + .class_init = virtio_snd_class_init, + } +}; + +DEFINE_TYPES(virtio_snd_types) diff --git a/include/hw/audio/virtio-snd.h b/include/hw/audio/virtio-snd.h new file mode 100644 index 0000000000..d08065941c --- /dev/null +++ b/include/hw/audio/virtio-snd.h @@ -0,0 +1,79 @@ +/* + * VIRTIO Sound Device conforming to + * + * "Virtual I/O Device (VIRTIO) Version 1.2 + * Committee Specification Draft 01 + * 09 May 2022" + * + * Copyright (c) 2023 Emmanouil Pitsidianakis + * Copyright (C) 2019 OpenSynergy GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#ifndef QEMU_VIRTIO_SOUND_H +#define QEMU_VIRTIO_SOUND_H + +#include "hw/virtio/virtio.h" +#include "audio/audio.h" +#include "standard-headers/linux/virtio_ids.h" +#include "standard-headers/linux/virtio_snd.h" + +#define TYPE_VIRTIO_SND "virtio-sound-device" +#define VIRTIO_SND(obj) \ + OBJECT_CHECK(VirtIOSound, (obj), TYPE_VIRTIO_SND) + +/* CONFIGURATION SPACE */ + +typedef struct virtio_snd_config virtio_snd_config; + +/* COMMON DEFINITIONS */ + +/* common header for request/response*/ +typedef struct virtio_snd_hdr virtio_snd_hdr; + +/* event notification */ +typedef struct virtio_snd_event virtio_snd_event; + +/* common control request to query an item information */ +typedef struct virtio_snd_query_info virtio_snd_query_info; + +/* JACK CONTROL MESSAGES */ + +typedef struct virtio_snd_jack_hdr virtio_snd_jack_hdr; + +/* jack information structure */ +typedef struct virtio_snd_jack_info virtio_snd_jack_info; + +/* jack remapping control request */ +typedef struct virtio_snd_jack_remap virtio_snd_jack_remap; + +/* + * PCM CONTROL MESSAGES + */ +typedef struct virtio_snd_pcm_hdr virtio_snd_pcm_hdr; + +/* PCM stream info structure */ +typedef struct virtio_snd_pcm_info virtio_snd_pcm_info; + +/* set PCM stream params */ +typedef struct virtio_snd_pcm_set_params virtio_snd_pcm_set_params; + +/* I/O request header */ +typedef struct virtio_snd_pcm_xfer virtio_snd_pcm_xfer; + +/* I/O request status */ +typedef struct virtio_snd_pcm_status virtio_snd_pcm_status; + +typedef struct VirtIOSound { + VirtIODevice parent_obj; + + VirtQueue *queues[VIRTIO_SND_VQ_MAX]; + uint64_t features; + QEMUSoundCard card; + VMChangeStateEntry *vmstate; + virtio_snd_config snd_conf; +} VirtIOSound; +#endif diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c index 1b8005ae55..3c0aabec4b 100644 --- a/system/qdev-monitor.c +++ b/system/qdev-monitor.c @@ -111,6 +111,7 @@ static const QDevAlias qdev_alias_table[] = { { "virtio-serial-device", "virtio-serial", QEMU_ARCH_VIRTIO_MMIO }, { "virtio-serial-ccw", "virtio-serial", QEMU_ARCH_VIRTIO_CCW }, { "virtio-serial-pci", "virtio-serial", QEMU_ARCH_VIRTIO_PCI}, + { "virtio-sound-device", "virtio-sound", QEMU_ARCH_VIRTIO_MMIO }, { "virtio-tablet-device", "virtio-tablet", QEMU_ARCH_VIRTIO_MMIO }, { "virtio-tablet-ccw", "virtio-tablet", QEMU_ARCH_VIRTIO_CCW }, { "virtio-tablet-pci", "virtio-tablet", QEMU_ARCH_VIRTIO_PCI }, From 2426908590c1d8d9ffb741c5552ad45d9d3ba9f8 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Mon, 23 Oct 2023 15:03:19 +0300 Subject: [PATCH 670/974] Add virtio-sound-pci device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds a PCI wrapper device for the virtio-sound device. It is necessary to instantiate a virtio-snd device in a guest. All sound logic will be added to the virtio-snd device in the following commits. To add this device with a guest, you'll need a >=5.13 kernel compiled with CONFIG_SND_VIRTIO=y, which at the time of writing most distros have off by default. Use with following flags in the invocation: Pulseaudio: -audio driver=pa,model=virtio or -audio driver=pa,model=virtio,server=/run/user/1000/pulse/native sdl: -audio driver=sdl,model=virtio coreaudio (macos/darwin): -audio driver=coreaudio,model=virtio etc. Based-on: https://github.com/OpenSynergy/qemu/commit/5a2f350eec5d157b90d9c7b40a8e603f4da92471 Signed-off-by: Igor Skalkin Signed-off-by: Anton Yakovlev Signed-off-by: Manos Pitsidianakis Reviewed-by: Alex Bennée Tested-by: Alex Bennée Message-Id: Acked-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- MAINTAINERS | 1 + hw/audio/meson.build | 1 + hw/audio/virtio-snd-pci.c | 93 +++++++++++++++++++++++++++++++++++++++ system/qdev-monitor.c | 1 + 4 files changed, 96 insertions(+) create mode 100644 hw/audio/virtio-snd-pci.c diff --git a/MAINTAINERS b/MAINTAINERS index d3ee463d21..c09bb8cf41 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2315,6 +2315,7 @@ M: Gerd Hoffmann R: Manos Pitsidianakis S: Supported F: hw/audio/virtio-snd.c +F: hw/audio/virtio-snd-pci.c F: include/hw/audio/virtio-snd.h nvme diff --git a/hw/audio/meson.build b/hw/audio/meson.build index 7a503be1fd..2990974449 100644 --- a/hw/audio/meson.build +++ b/hw/audio/meson.build @@ -14,3 +14,4 @@ system_ss.add(when: 'CONFIG_SB16', if_true: files('sb16.c')) system_ss.add(when: 'CONFIG_VT82C686', if_true: files('via-ac97.c')) system_ss.add(when: 'CONFIG_WM8750', if_true: files('wm8750.c')) system_ss.add(when: ['CONFIG_VIRTIO_SND', 'CONFIG_VIRTIO'], if_true: files('virtio-snd.c')) +system_ss.add(when: ['CONFIG_VIRTIO_SND', 'CONFIG_VIRTIO', 'CONFIG_VIRTIO_PCI'], if_true: files('virtio-snd-pci.c')) diff --git a/hw/audio/virtio-snd-pci.c b/hw/audio/virtio-snd-pci.c new file mode 100644 index 0000000000..0f92e0752b --- /dev/null +++ b/hw/audio/virtio-snd-pci.c @@ -0,0 +1,93 @@ +/* + * VIRTIO Sound Device PCI Bindings + * + * Copyright (c) 2023 Emmanouil Pitsidianakis + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#include "qemu/osdep.h" +#include "qom/object.h" +#include "qapi/error.h" +#include "hw/audio/soundhw.h" +#include "hw/virtio/virtio-pci.h" +#include "hw/audio/virtio-snd.h" + +/* + * virtio-snd-pci: This extends VirtioPCIProxy. + */ +#define TYPE_VIRTIO_SND_PCI "virtio-sound-pci" +OBJECT_DECLARE_SIMPLE_TYPE(VirtIOSoundPCI, VIRTIO_SND_PCI) + +struct VirtIOSoundPCI { + VirtIOPCIProxy parent_obj; + + VirtIOSound vdev; +}; + +static Property virtio_snd_pci_properties[] = { + DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, + VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_snd_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +{ + VirtIOSoundPCI *dev = VIRTIO_SND_PCI(vpci_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + + virtio_pci_force_virtio_1(vpci_dev); + qdev_realize(vdev, BUS(&vpci_dev->bus), errp); +} + +static void virtio_snd_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioPCIClass *vpciklass = VIRTIO_PCI_CLASS(klass); + + device_class_set_props(dc, virtio_snd_pci_properties); + dc->desc = "Virtio Sound"; + set_bit(DEVICE_CATEGORY_SOUND, dc->categories); + + vpciklass->realize = virtio_snd_pci_realize; +} + +static void virtio_snd_pci_instance_init(Object *obj) +{ + VirtIOSoundPCI *dev = VIRTIO_SND_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VIRTIO_SND); +} + +static const VirtioPCIDeviceTypeInfo virtio_snd_pci_info = { + .generic_name = TYPE_VIRTIO_SND_PCI, + .instance_size = sizeof(VirtIOSoundPCI), + .instance_init = virtio_snd_pci_instance_init, + .class_init = virtio_snd_pci_class_init, +}; + +/* Create a Virtio Sound PCI device, so '-audio driver,model=virtio' works. */ +static int virtio_snd_pci_init(PCIBus *bus, const char *audiodev) +{ + DeviceState *vdev = NULL; + VirtIOSoundPCI *dev = NULL; + + vdev = qdev_new(TYPE_VIRTIO_SND_PCI); + assert(vdev); + dev = VIRTIO_SND_PCI(vdev); + qdev_prop_set_string(DEVICE(&dev->vdev), "audiodev", audiodev); + qdev_realize_and_unref(vdev, BUS(bus), &error_fatal); + return 0; +} + +static void virtio_snd_pci_register(void) +{ + virtio_pci_types_register(&virtio_snd_pci_info); + pci_register_soundhw("virtio", "Virtio Sound", virtio_snd_pci_init); +} + +type_init(virtio_snd_pci_register); diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c index 3c0aabec4b..a13db763e5 100644 --- a/system/qdev-monitor.c +++ b/system/qdev-monitor.c @@ -112,6 +112,7 @@ static const QDevAlias qdev_alias_table[] = { { "virtio-serial-ccw", "virtio-serial", QEMU_ARCH_VIRTIO_CCW }, { "virtio-serial-pci", "virtio-serial", QEMU_ARCH_VIRTIO_PCI}, { "virtio-sound-device", "virtio-sound", QEMU_ARCH_VIRTIO_MMIO }, + { "virtio-sound-pci", "virtio-sound", QEMU_ARCH_VIRTIO_PCI }, { "virtio-tablet-device", "virtio-tablet", QEMU_ARCH_VIRTIO_MMIO }, { "virtio-tablet-ccw", "virtio-tablet", QEMU_ARCH_VIRTIO_CCW }, { "virtio-tablet-pci", "virtio-tablet", QEMU_ARCH_VIRTIO_PCI }, From eb9ad377bb94d2d98d18cfccc431a028361385c6 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Mon, 23 Oct 2023 15:03:20 +0300 Subject: [PATCH 671/974] virtio-sound: handle control messages and streams MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Receive guest requests in the control (CTRL) queue of the virtio sound device and reply with a NOT SUPPORTED error to all control commands. The receiving handler is virtio_snd_handle_ctrl(). It stores all control messages in the queue in the device's command queue. Then it calls virtio_snd_process_cmdq() to handle each message. The handler is process_cmd() which replies with VIRTIO_SND_S_NOT_SUPP. Based-on: https://github.com/OpenSynergy/qemu/commit/5a2f350eec5d157b90d9c7b40a8e603f4da92471 Signed-off-by: Igor Skalkin Signed-off-by: Anton Yakovlev Signed-off-by: Manos Pitsidianakis Reviewed-by: Alex Bennée Tested-by: Alex Bennée Message-Id: <3224aff87e7c4f2777bfe1bbbbca93b72525992c.1698062525.git.manos.pitsidianakis@linaro.org> Acked-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/audio/trace-events | 4 + hw/audio/virtio-snd.c | 487 +++++++++++++++++++++++++++++++++- include/hw/audio/virtio-snd.h | 113 +++++++- 3 files changed, 595 insertions(+), 9 deletions(-) diff --git a/hw/audio/trace-events b/hw/audio/trace-events index 525ced2b34..122d1403ef 100644 --- a/hw/audio/trace-events +++ b/hw/audio/trace-events @@ -47,3 +47,7 @@ virtio_snd_vm_state_running(void) "vm state running" virtio_snd_vm_state_stopped(void) "vm state stopped" virtio_snd_realize(void *snd) "snd %p: realize" virtio_snd_unrealize(void *snd) "snd %p: unrealize" +virtio_snd_handle_ctrl(void *vdev, void *vq) "snd %p: handle ctrl event for queue %p" +virtio_snd_handle_code(uint32_t val, const char *code) "ctrl code msg val = %"PRIu32" == %s" +virtio_snd_handle_chmap_info(void) "VIRTIO_SND_CHMAP_INFO called" +virtio_snd_handle_event(void) "event queue callback called" diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c index 3a4b441c20..4eae76f638 100644 --- a/hw/audio/virtio-snd.c +++ b/hw/audio/virtio-snd.c @@ -32,6 +32,29 @@ #define VIRTIO_SOUND_CHMAP_DEFAULT 0 #define VIRTIO_SOUND_HDA_FN_NID 0 +static uint32_t supported_formats = BIT(VIRTIO_SND_PCM_FMT_S8) + | BIT(VIRTIO_SND_PCM_FMT_U8) + | BIT(VIRTIO_SND_PCM_FMT_S16) + | BIT(VIRTIO_SND_PCM_FMT_U16) + | BIT(VIRTIO_SND_PCM_FMT_S32) + | BIT(VIRTIO_SND_PCM_FMT_U32) + | BIT(VIRTIO_SND_PCM_FMT_FLOAT); + +static uint32_t supported_rates = BIT(VIRTIO_SND_PCM_RATE_5512) + | BIT(VIRTIO_SND_PCM_RATE_8000) + | BIT(VIRTIO_SND_PCM_RATE_11025) + | BIT(VIRTIO_SND_PCM_RATE_16000) + | BIT(VIRTIO_SND_PCM_RATE_22050) + | BIT(VIRTIO_SND_PCM_RATE_32000) + | BIT(VIRTIO_SND_PCM_RATE_44100) + | BIT(VIRTIO_SND_PCM_RATE_48000) + | BIT(VIRTIO_SND_PCM_RATE_64000) + | BIT(VIRTIO_SND_PCM_RATE_88200) + | BIT(VIRTIO_SND_PCM_RATE_96000) + | BIT(VIRTIO_SND_PCM_RATE_176400) + | BIT(VIRTIO_SND_PCM_RATE_192000) + | BIT(VIRTIO_SND_PCM_RATE_384000); + static const VMStateDescription vmstate_virtio_snd_device = { .name = TYPE_VIRTIO_SND, .version_id = VIRTIO_SOUND_VM_VERSION, @@ -100,13 +123,397 @@ virtio_snd_set_config(VirtIODevice *vdev, const uint8_t *config) } +static void +virtio_snd_ctrl_cmd_free(virtio_snd_ctrl_command *cmd) +{ + g_free(cmd->elem); + g_free(cmd); +} + /* - * Queue handler stub. + * Get a specific stream from the virtio sound card device. + * Returns NULL if @stream_id is invalid or not allocated. + * + * @s: VirtIOSound device + * @stream_id: stream id + */ +static VirtIOSoundPCMStream *virtio_snd_pcm_get_stream(VirtIOSound *s, + uint32_t stream_id) +{ + return stream_id >= s->snd_conf.streams ? NULL : + s->pcm->streams[stream_id]; +} + +/* + * Get params for a specific stream. + * + * @s: VirtIOSound device + * @stream_id: stream id + */ +static virtio_snd_pcm_set_params *virtio_snd_pcm_get_params(VirtIOSound *s, + uint32_t stream_id) +{ + return stream_id >= s->snd_conf.streams ? NULL + : &s->pcm->pcm_params[stream_id]; +} + +/* + * Set the given stream params. + * Called by both virtio_snd_handle_pcm_set_params and during device + * initialization. + * Returns the response status code. (VIRTIO_SND_S_*). + * + * @s: VirtIOSound device + * @params: The PCM params as defined in the virtio specification + */ +static +uint32_t virtio_snd_set_pcm_params(VirtIOSound *s, + uint32_t stream_id, + virtio_snd_pcm_set_params *params) +{ + virtio_snd_pcm_set_params *st_params; + + if (stream_id >= s->snd_conf.streams || s->pcm->pcm_params == NULL) { + /* + * TODO: do we need to set DEVICE_NEEDS_RESET? + */ + virtio_error(VIRTIO_DEVICE(s), "Streams have not been initialized.\n"); + return cpu_to_le32(VIRTIO_SND_S_BAD_MSG); + } + + st_params = virtio_snd_pcm_get_params(s, stream_id); + + if (params->channels < 1 || params->channels > AUDIO_MAX_CHANNELS) { + error_report("Number of channels is not supported."); + return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); + } + if (!(supported_formats & BIT(params->format))) { + error_report("Stream format is not supported."); + return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); + } + if (!(supported_rates & BIT(params->rate))) { + error_report("Stream rate is not supported."); + return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); + } + + st_params->buffer_bytes = le32_to_cpu(params->buffer_bytes); + st_params->period_bytes = le32_to_cpu(params->period_bytes); + st_params->features = le32_to_cpu(params->features); + /* the following are uint8_t, so there's no need to bswap the values. */ + st_params->channels = params->channels; + st_params->format = params->format; + st_params->rate = params->rate; + + return cpu_to_le32(VIRTIO_SND_S_OK); +} + +/* + * Get a QEMU Audiosystem compatible format value from a VIRTIO_SND_PCM_FMT_* + */ +static AudioFormat virtio_snd_get_qemu_format(uint32_t format) +{ + #define CASE(FMT) \ + case VIRTIO_SND_PCM_FMT_##FMT: \ + return AUDIO_FORMAT_##FMT; + + switch (format) { + CASE(U8) + CASE(S8) + CASE(U16) + CASE(S16) + CASE(U32) + CASE(S32) + case VIRTIO_SND_PCM_FMT_FLOAT: + return AUDIO_FORMAT_F32; + default: + g_assert_not_reached(); + } + + #undef CASE +} + +/* + * Get a QEMU Audiosystem compatible frequency value from a + * VIRTIO_SND_PCM_RATE_* + */ +static uint32_t virtio_snd_get_qemu_freq(uint32_t rate) +{ + #define CASE(RATE) \ + case VIRTIO_SND_PCM_RATE_##RATE: \ + return RATE; + + switch (rate) { + CASE(5512) + CASE(8000) + CASE(11025) + CASE(16000) + CASE(22050) + CASE(32000) + CASE(44100) + CASE(48000) + CASE(64000) + CASE(88200) + CASE(96000) + CASE(176400) + CASE(192000) + CASE(384000) + default: + g_assert_not_reached(); + } + + #undef CASE +} + +/* + * Get QEMU Audiosystem compatible audsettings from virtio based pcm stream + * params. + */ +static void virtio_snd_get_qemu_audsettings(audsettings *as, + virtio_snd_pcm_set_params *params) +{ + as->nchannels = MIN(AUDIO_MAX_CHANNELS, params->channels); + as->fmt = virtio_snd_get_qemu_format(params->format); + as->freq = virtio_snd_get_qemu_freq(params->rate); + as->endianness = target_words_bigendian() ? 1 : 0; +} + +/* + * Close a stream and free all its resources. + * + * @stream: VirtIOSoundPCMStream *stream + */ +static void virtio_snd_pcm_close(VirtIOSoundPCMStream *stream) +{ +} + +/* + * Prepares a VirtIOSound card stream. + * Returns the response status code. (VIRTIO_SND_S_*). + * + * @s: VirtIOSound device + * @stream_id: stream id + */ +static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, uint32_t stream_id) +{ + audsettings as; + virtio_snd_pcm_set_params *params; + VirtIOSoundPCMStream *stream; + + if (s->pcm->streams == NULL || + s->pcm->pcm_params == NULL || + stream_id >= s->snd_conf.streams) { + return cpu_to_le32(VIRTIO_SND_S_BAD_MSG); + } + + params = virtio_snd_pcm_get_params(s, stream_id); + if (params == NULL) { + return cpu_to_le32(VIRTIO_SND_S_BAD_MSG); + } + + stream = virtio_snd_pcm_get_stream(s, stream_id); + if (stream == NULL) { + stream = g_new0(VirtIOSoundPCMStream, 1); + stream->active = false; + stream->id = stream_id; + stream->pcm = s->pcm; + stream->s = s; + + /* + * stream_id >= s->snd_conf.streams was checked before so this is + * in-bounds + */ + s->pcm->streams[stream_id] = stream; + } + + virtio_snd_get_qemu_audsettings(&as, params); + stream->info.direction = stream_id < s->snd_conf.streams / 2 + + (s->snd_conf.streams & 1) ? VIRTIO_SND_D_OUTPUT : VIRTIO_SND_D_INPUT; + stream->info.hdr.hda_fn_nid = VIRTIO_SOUND_HDA_FN_NID; + stream->info.features = 0; + stream->info.channels_min = 1; + stream->info.channels_max = as.nchannels; + stream->info.formats = supported_formats; + stream->info.rates = supported_rates; + stream->params = *params; + + stream->positions[0] = VIRTIO_SND_CHMAP_FL; + stream->positions[1] = VIRTIO_SND_CHMAP_FR; + stream->as = as; + + return cpu_to_le32(VIRTIO_SND_S_OK); +} + +static const char *print_code(uint32_t code) +{ + #define CASE(CODE) \ + case VIRTIO_SND_R_##CODE: \ + return "VIRTIO_SND_R_"#CODE + + switch (code) { + CASE(JACK_INFO); + CASE(JACK_REMAP); + CASE(PCM_INFO); + CASE(PCM_SET_PARAMS); + CASE(PCM_PREPARE); + CASE(PCM_RELEASE); + CASE(PCM_START); + CASE(PCM_STOP); + CASE(CHMAP_INFO); + default: + return "invalid code"; + } + + #undef CASE +}; + +/* + * The actual processing done in virtio_snd_process_cmdq(). + * + * @s: VirtIOSound device + * @cmd: control command request + */ +static inline void +process_cmd(VirtIOSound *s, virtio_snd_ctrl_command *cmd) +{ + uint32_t code; + size_t msg_sz = iov_to_buf(cmd->elem->out_sg, + cmd->elem->out_num, + 0, + &cmd->ctrl, + sizeof(virtio_snd_hdr)); + + if (msg_sz != sizeof(virtio_snd_hdr)) { + /* + * TODO: do we need to set DEVICE_NEEDS_RESET? + */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: virtio-snd command size incorrect %zu vs \ + %zu\n", __func__, msg_sz, sizeof(virtio_snd_hdr)); + return; + } + + code = le32_to_cpu(cmd->ctrl.code); + + trace_virtio_snd_handle_code(code, print_code(code)); + + switch (code) { + case VIRTIO_SND_R_JACK_INFO: + case VIRTIO_SND_R_JACK_REMAP: + qemu_log_mask(LOG_UNIMP, + "virtio_snd: jack functionality is unimplemented.\n"); + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); + break; + case VIRTIO_SND_R_PCM_INFO: + case VIRTIO_SND_R_PCM_SET_PARAMS: + case VIRTIO_SND_R_PCM_PREPARE: + case VIRTIO_SND_R_PCM_START: + case VIRTIO_SND_R_PCM_STOP: + case VIRTIO_SND_R_PCM_RELEASE: + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); + break; + case VIRTIO_SND_R_CHMAP_INFO: + qemu_log_mask(LOG_UNIMP, + "virtio_snd: chmap info functionality is unimplemented.\n"); + trace_virtio_snd_handle_chmap_info(); + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); + break; + default: + /* error */ + error_report("virtio snd header not recognized: %"PRIu32, code); + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); + } + + iov_from_buf(cmd->elem->in_sg, + cmd->elem->in_num, + 0, + &cmd->resp, + sizeof(virtio_snd_hdr)); + virtqueue_push(cmd->vq, cmd->elem, sizeof(virtio_snd_hdr)); + virtio_notify(VIRTIO_DEVICE(s), cmd->vq); +} + +/* + * Consume all elements in command queue. + * + * @s: VirtIOSound device + */ +static void virtio_snd_process_cmdq(VirtIOSound *s) +{ + virtio_snd_ctrl_command *cmd; + + if (unlikely(qatomic_read(&s->processing_cmdq))) { + return; + } + + WITH_QEMU_LOCK_GUARD(&s->cmdq_mutex) { + qatomic_set(&s->processing_cmdq, true); + while (!QTAILQ_EMPTY(&s->cmdq)) { + cmd = QTAILQ_FIRST(&s->cmdq); + + /* process command */ + process_cmd(s, cmd); + + QTAILQ_REMOVE(&s->cmdq, cmd, next); + + virtio_snd_ctrl_cmd_free(cmd); + } + qatomic_set(&s->processing_cmdq, false); + } +} + +/* + * The control message handler. Pops an element from the control virtqueue, + * and stores them to VirtIOSound's cmdq queue and finally calls + * virtio_snd_process_cmdq() for processing. + * + * @vdev: VirtIOSound device + * @vq: Control virtqueue + */ +static void virtio_snd_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) +{ + VirtIOSound *s = VIRTIO_SND(vdev); + VirtQueueElement *elem; + virtio_snd_ctrl_command *cmd; + + trace_virtio_snd_handle_ctrl(vdev, vq); + + if (!virtio_queue_ready(vq)) { + return; + } + + elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); + while (elem) { + cmd = g_new0(virtio_snd_ctrl_command, 1); + cmd->elem = elem; + cmd->vq = vq; + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK); + QTAILQ_INSERT_TAIL(&s->cmdq, cmd, next); + elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); + } + + virtio_snd_process_cmdq(s); +} + +/* + * The event virtqueue handler. + * Not implemented yet. + * + * @vdev: VirtIOSound device + * @vq: event vq + */ +static void virtio_snd_handle_event(VirtIODevice *vdev, VirtQueue *vq) +{ + qemu_log_mask(LOG_UNIMP, "virtio_snd: event queue is unimplemented.\n"); + trace_virtio_snd_handle_event(); +} + +/* + * Stub buffer virtqueue handler. * * @vdev: VirtIOSound device * @vq: virtqueue */ -static void virtio_snd_handle_queue(VirtIODevice *vdev, VirtQueue *vq) {} +static void virtio_snd_handle_xfer(VirtIODevice *vdev, VirtQueue *vq) {} static uint64_t get_features(VirtIODevice *vdev, uint64_t features, Error **errp) @@ -140,12 +547,22 @@ static void virtio_snd_realize(DeviceState *dev, Error **errp) ERRP_GUARD(); VirtIOSound *vsnd = VIRTIO_SND(dev); VirtIODevice *vdev = VIRTIO_DEVICE(dev); + virtio_snd_pcm_set_params default_params = { 0 }; + uint32_t status; + vsnd->pcm = NULL; vsnd->vmstate = qemu_add_vm_change_state_handler(virtio_snd_vm_state_change, vsnd); trace_virtio_snd_realize(vsnd); + vsnd->pcm = g_new0(VirtIOSoundPCM, 1); + vsnd->pcm->snd = vsnd; + vsnd->pcm->streams = + g_new0(VirtIOSoundPCMStream *, vsnd->snd_conf.streams); + vsnd->pcm->pcm_params = + g_new0(virtio_snd_pcm_set_params, vsnd->snd_conf.streams); + virtio_init(vdev, VIRTIO_ID_SOUND, sizeof(virtio_snd_config)); virtio_add_feature(&vsnd->features, VIRTIO_F_VERSION_1); @@ -172,25 +589,69 @@ static void virtio_snd_realize(DeviceState *dev, Error **errp) AUD_register_card("virtio-sound", &vsnd->card, errp); + /* set default params for all streams */ + default_params.features = 0; + default_params.buffer_bytes = cpu_to_le32(8192); + default_params.period_bytes = cpu_to_le32(2048); + default_params.channels = 2; + default_params.format = VIRTIO_SND_PCM_FMT_S16; + default_params.rate = VIRTIO_SND_PCM_RATE_48000; vsnd->queues[VIRTIO_SND_VQ_CONTROL] = - virtio_add_queue(vdev, 64, virtio_snd_handle_queue); + virtio_add_queue(vdev, 64, virtio_snd_handle_ctrl); vsnd->queues[VIRTIO_SND_VQ_EVENT] = - virtio_add_queue(vdev, 64, virtio_snd_handle_queue); + virtio_add_queue(vdev, 64, virtio_snd_handle_event); vsnd->queues[VIRTIO_SND_VQ_TX] = - virtio_add_queue(vdev, 64, virtio_snd_handle_queue); + virtio_add_queue(vdev, 64, virtio_snd_handle_xfer); vsnd->queues[VIRTIO_SND_VQ_RX] = - virtio_add_queue(vdev, 64, virtio_snd_handle_queue); + virtio_add_queue(vdev, 64, virtio_snd_handle_xfer); + qemu_mutex_init(&vsnd->cmdq_mutex); + QTAILQ_INIT(&vsnd->cmdq); + + for (uint32_t i = 0; i < vsnd->snd_conf.streams; i++) { + status = virtio_snd_set_pcm_params(vsnd, i, &default_params); + if (status != cpu_to_le32(VIRTIO_SND_S_OK)) { + error_setg(errp, + "Can't initalize stream params, device responded with %s.", + print_code(status)); + return; + } + status = virtio_snd_pcm_prepare(vsnd, i); + if (status != cpu_to_le32(VIRTIO_SND_S_OK)) { + error_setg(errp, + "Can't prepare streams, device responded with %s.", + print_code(status)); + return; + } + } } static void virtio_snd_unrealize(DeviceState *dev) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtIOSound *vsnd = VIRTIO_SND(dev); + VirtIOSoundPCMStream *stream; qemu_del_vm_change_state_handler(vsnd->vmstate); trace_virtio_snd_unrealize(vsnd); + if (vsnd->pcm) { + if (vsnd->pcm->streams) { + for (uint32_t i = 0; i < vsnd->snd_conf.streams; i++) { + stream = vsnd->pcm->streams[i]; + if (stream) { + virtio_snd_process_cmdq(stream->s); + virtio_snd_pcm_close(stream); + g_free(stream); + } + } + g_free(vsnd->pcm->streams); + } + g_free(vsnd->pcm->pcm_params); + g_free(vsnd->pcm); + vsnd->pcm = NULL; + } AUD_remove_card(&vsnd->card); + qemu_mutex_destroy(&vsnd->cmdq_mutex); virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_CONTROL]); virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_EVENT]); virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_TX]); @@ -199,7 +660,19 @@ static void virtio_snd_unrealize(DeviceState *dev) } -static void virtio_snd_reset(VirtIODevice *vdev) {} +static void virtio_snd_reset(VirtIODevice *vdev) +{ + VirtIOSound *s = VIRTIO_SND(vdev); + virtio_snd_ctrl_command *cmd; + + WITH_QEMU_LOCK_GUARD(&s->cmdq_mutex) { + while (!QTAILQ_EMPTY(&s->cmdq)) { + cmd = QTAILQ_FIRST(&s->cmdq); + QTAILQ_REMOVE(&s->cmdq, cmd, next); + virtio_snd_ctrl_cmd_free(cmd); + } + } +} static void virtio_snd_class_init(ObjectClass *klass, void *data) { diff --git a/include/hw/audio/virtio-snd.h b/include/hw/audio/virtio-snd.h index d08065941c..cc14c875ed 100644 --- a/include/hw/audio/virtio-snd.h +++ b/include/hw/audio/virtio-snd.h @@ -67,13 +67,122 @@ typedef struct virtio_snd_pcm_xfer virtio_snd_pcm_xfer; /* I/O request status */ typedef struct virtio_snd_pcm_status virtio_snd_pcm_status; -typedef struct VirtIOSound { +/* device structs */ + +typedef struct VirtIOSound VirtIOSound; + +typedef struct VirtIOSoundPCMStream VirtIOSoundPCMStream; + +typedef struct virtio_snd_ctrl_command virtio_snd_ctrl_command; + +typedef struct VirtIOSoundPCM VirtIOSoundPCM; + +struct VirtIOSoundPCM { + VirtIOSound *snd; + /* + * PCM parameters are a separate field instead of a VirtIOSoundPCMStream + * field, because the operation of PCM control requests is first + * VIRTIO_SND_R_PCM_SET_PARAMS and then VIRTIO_SND_R_PCM_PREPARE; this + * means that some times we get parameters without having an allocated + * stream yet. + */ + virtio_snd_pcm_set_params *pcm_params; + VirtIOSoundPCMStream **streams; +}; + +struct VirtIOSoundPCMStream { + VirtIOSoundPCM *pcm; + virtio_snd_pcm_info info; + virtio_snd_pcm_set_params params; + uint32_t id; + /* channel position values (VIRTIO_SND_CHMAP_XXX) */ + uint8_t positions[VIRTIO_SND_CHMAP_MAX_SIZE]; + VirtIOSound *s; + bool flushing; + audsettings as; + union { + SWVoiceIn *in; + SWVoiceOut *out; + } voice; + bool active; +}; + +/* + * PCM stream state machine. + * ------------------------- + * + * 5.14.6.6.1 PCM Command Lifecycle + * ================================ + * + * A PCM stream has the following command lifecycle: + * - `SET PARAMETERS` + * The driver negotiates the stream parameters (format, transport, etc) with + * the device. + * Possible valid transitions: `SET PARAMETERS`, `PREPARE`. + * - `PREPARE` + * The device prepares the stream (allocates resources, etc). + * Possible valid transitions: `SET PARAMETERS`, `PREPARE`, `START`, + * `RELEASE`. Output only: the driver transfers data for pre-buffing. + * - `START` + * The device starts the stream (unmute, putting into running state, etc). + * Possible valid transitions: `STOP`. + * The driver transfers data to/from the stream. + * - `STOP` + * The device stops the stream (mute, putting into non-running state, etc). + * Possible valid transitions: `START`, `RELEASE`. + * - `RELEASE` + * The device releases the stream (frees resources, etc). + * Possible valid transitions: `SET PARAMETERS`, `PREPARE`. + * + * +---------------+ +---------+ +---------+ +-------+ +-------+ + * | SetParameters | | Prepare | | Release | | Start | | Stop | + * +---------------+ +---------+ +---------+ +-------+ +-------+ + * |- | | | | + * || | | | | + * |< | | | | + * |------------->| | | | + * |<-------------| | | | + * | |- | | | + * | || | | | + * | |< | | | + * | |--------------------->| | + * | |---------->| | | + * | | | |-------->| + * | | | |<--------| + * | | |<-------------------| + * |<-------------------------| | | + * | |<----------| | | + * + * CTRL in the VirtIOSound device + * ============================== + * + * The control messages that affect the state of a stream arrive in the + * `virtio_snd_handle_ctrl()` queue callback and are of type `struct + * virtio_snd_ctrl_command`. They are stored in a queue field in the device + * type, `VirtIOSound`. This allows deferring the CTRL request completion if + * it's not immediately possible due to locking/state reasons. + * + * The CTRL message is finally handled in `process_cmd()`. + */ +struct VirtIOSound { VirtIODevice parent_obj; VirtQueue *queues[VIRTIO_SND_VQ_MAX]; uint64_t features; + VirtIOSoundPCM *pcm; QEMUSoundCard card; VMChangeStateEntry *vmstate; virtio_snd_config snd_conf; -} VirtIOSound; + QemuMutex cmdq_mutex; + QTAILQ_HEAD(, virtio_snd_ctrl_command) cmdq; + bool processing_cmdq; +}; + +struct virtio_snd_ctrl_command { + VirtQueueElement *elem; + VirtQueue *vq; + virtio_snd_hdr ctrl; + virtio_snd_hdr resp; + QTAILQ_ENTRY(virtio_snd_ctrl_command) next; +}; #endif From 0ff05dd209f153f037a48e4039a033885b9ab5e3 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Mon, 23 Oct 2023 15:03:21 +0300 Subject: [PATCH 672/974] virtio-sound: handle VIRTIO_SND_R_PCM_INFO request MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Respond to the VIRTIO_SND_R_PCM_INFO control request with the parameters of each requested PCM stream. Based-on: https://github.com/OpenSynergy/qemu/commit/5a2f350eec5d157b90d9c7b40a8e603f4da92471 Signed-off-by: Igor Skalkin Signed-off-by: Anton Yakovlev Signed-off-by: Manos Pitsidianakis Reviewed-by: Alex Bennée Tested-by: Alex Bennée Message-Id: <5ecea6ba2fb0e3957d7d90bc4dbac521a3d1f678.1698062525.git.manos.pitsidianakis@linaro.org> Acked-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/audio/trace-events | 1 + hw/audio/virtio-snd.c | 82 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/hw/audio/trace-events b/hw/audio/trace-events index 122d1403ef..6def414f96 100644 --- a/hw/audio/trace-events +++ b/hw/audio/trace-events @@ -48,6 +48,7 @@ virtio_snd_vm_state_stopped(void) "vm state stopped" virtio_snd_realize(void *snd) "snd %p: realize" virtio_snd_unrealize(void *snd) "snd %p: unrealize" virtio_snd_handle_ctrl(void *vdev, void *vq) "snd %p: handle ctrl event for queue %p" +virtio_snd_handle_pcm_info(uint32_t stream) "VIRTIO_SND_R_PCM_INFO called for stream %"PRIu32 virtio_snd_handle_code(uint32_t val, const char *code) "ctrl code msg val = %"PRIu32" == %s" virtio_snd_handle_chmap_info(void) "VIRTIO_SND_CHMAP_INFO called" virtio_snd_handle_event(void) "event queue callback called" diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c index 4eae76f638..fd0c50de6e 100644 --- a/hw/audio/virtio-snd.c +++ b/hw/audio/virtio-snd.c @@ -157,6 +157,86 @@ static virtio_snd_pcm_set_params *virtio_snd_pcm_get_params(VirtIOSound *s, : &s->pcm->pcm_params[stream_id]; } +/* + * Handle the VIRTIO_SND_R_PCM_INFO request. + * The function writes the info structs to the request element. + * + * @s: VirtIOSound device + * @cmd: The request command queue element from VirtIOSound cmdq field + */ +static void virtio_snd_handle_pcm_info(VirtIOSound *s, + virtio_snd_ctrl_command *cmd) +{ + uint32_t stream_id, start_id, count, size; + virtio_snd_pcm_info val; + virtio_snd_query_info req; + VirtIOSoundPCMStream *stream = NULL; + g_autofree virtio_snd_pcm_info *pcm_info = NULL; + size_t msg_sz = iov_to_buf(cmd->elem->out_sg, + cmd->elem->out_num, + 0, + &req, + sizeof(virtio_snd_query_info)); + + if (msg_sz != sizeof(virtio_snd_query_info)) { + /* + * TODO: do we need to set DEVICE_NEEDS_RESET? + */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: virtio-snd command size incorrect %zu vs \ + %zu\n", __func__, msg_sz, sizeof(virtio_snd_query_info)); + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); + return; + } + + start_id = le32_to_cpu(req.start_id); + count = le32_to_cpu(req.count); + size = le32_to_cpu(req.size); + + if (iov_size(cmd->elem->in_sg, cmd->elem->in_num) < + sizeof(virtio_snd_hdr) + size * count) { + /* + * TODO: do we need to set DEVICE_NEEDS_RESET? + */ + error_report("pcm info: buffer too small, got: %zu, needed: %zu", + iov_size(cmd->elem->in_sg, cmd->elem->in_num), + sizeof(virtio_snd_pcm_info)); + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); + return; + } + + pcm_info = g_new0(virtio_snd_pcm_info, count); + for (uint32_t i = 0; i < count; i++) { + stream_id = i + start_id; + trace_virtio_snd_handle_pcm_info(stream_id); + stream = virtio_snd_pcm_get_stream(s, stream_id); + if (!stream) { + error_report("Invalid stream id: %"PRIu32, stream_id); + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); + return; + } + val = stream->info; + val.hdr.hda_fn_nid = cpu_to_le32(val.hdr.hda_fn_nid); + val.features = cpu_to_le32(val.features); + val.formats = cpu_to_le64(val.formats); + val.rates = cpu_to_le64(val.rates); + /* + * 5.14.6.6.2.1 Device Requirements: Stream Information The device MUST + * NOT set undefined feature, format, rate and direction values. The + * device MUST initialize the padding bytes to 0. + */ + pcm_info[i] = val; + memset(&pcm_info[i].padding, 0, 5); + } + + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK); + iov_from_buf(cmd->elem->in_sg, + cmd->elem->in_num, + sizeof(virtio_snd_hdr), + pcm_info, + sizeof(virtio_snd_pcm_info) * count); +} + /* * Set the given stream params. * Called by both virtio_snd_handle_pcm_set_params and during device @@ -404,6 +484,8 @@ process_cmd(VirtIOSound *s, virtio_snd_ctrl_command *cmd) cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); break; case VIRTIO_SND_R_PCM_INFO: + virtio_snd_handle_pcm_info(s, cmd); + break; case VIRTIO_SND_R_PCM_SET_PARAMS: case VIRTIO_SND_R_PCM_PREPARE: case VIRTIO_SND_R_PCM_START: From fa131d4a8277ef3522384d33d3b6b7963b5e7de9 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Mon, 23 Oct 2023 15:03:22 +0300 Subject: [PATCH 673/974] virtio-sound: handle VIRTIO_SND_R_PCM_{START,STOP} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Handle the start and stop control messages for a stream_id. This request does nothing at the moment except for replying to it. Audio playback or capture will be started/stopped here in follow-up commits. Based-on: https://github.com/OpenSynergy/qemu/commit/5a2f350eec5d157b90d9c7b40a8e603f4da92471 Signed-off-by: Igor Skalkin Signed-off-by: Anton Yakovlev Signed-off-by: Manos Pitsidianakis Reviewed-by: Alex Bennée Tested-by: Alex Bennée Message-Id: <9657dbfe3cb4a48ceb033ceb5977dc08669dfefd.1698062525.git.manos.pitsidianakis@linaro.org> Acked-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/audio/trace-events | 1 + hw/audio/virtio-snd.c | 49 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/hw/audio/trace-events b/hw/audio/trace-events index 6def414f96..db48ff04fe 100644 --- a/hw/audio/trace-events +++ b/hw/audio/trace-events @@ -49,6 +49,7 @@ virtio_snd_realize(void *snd) "snd %p: realize" virtio_snd_unrealize(void *snd) "snd %p: unrealize" virtio_snd_handle_ctrl(void *vdev, void *vq) "snd %p: handle ctrl event for queue %p" virtio_snd_handle_pcm_info(uint32_t stream) "VIRTIO_SND_R_PCM_INFO called for stream %"PRIu32 +virtio_snd_handle_pcm_start_stop(const char *code, uint32_t stream) "%s called for stream %"PRIu32 virtio_snd_handle_code(uint32_t val, const char *code) "ctrl code msg val = %"PRIu32" == %s" virtio_snd_handle_chmap_info(void) "VIRTIO_SND_CHMAP_INFO called" virtio_snd_handle_event(void) "event queue callback called" diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c index fd0c50de6e..e6791de6c6 100644 --- a/hw/audio/virtio-snd.c +++ b/hw/audio/virtio-snd.c @@ -446,6 +446,47 @@ static const char *print_code(uint32_t code) #undef CASE }; +/* + * Handles VIRTIO_SND_R_PCM_START. + * + * @s: VirtIOSound device + * @cmd: The request command queue element from VirtIOSound cmdq field + * @start: whether to start or stop the device + */ +static void virtio_snd_handle_pcm_start_stop(VirtIOSound *s, + virtio_snd_ctrl_command *cmd, + bool start) +{ + VirtIOSoundPCMStream *stream; + virtio_snd_pcm_hdr req; + uint32_t stream_id; + size_t msg_sz = iov_to_buf(cmd->elem->out_sg, + cmd->elem->out_num, + 0, + &req, + sizeof(virtio_snd_pcm_hdr)); + + if (msg_sz != sizeof(virtio_snd_pcm_hdr)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: virtio-snd command size incorrect %zu vs \ + %zu\n", __func__, msg_sz, sizeof(virtio_snd_pcm_hdr)); + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); + return; + } + + stream_id = le32_to_cpu(req.stream_id); + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK); + trace_virtio_snd_handle_pcm_start_stop(start ? "VIRTIO_SND_R_PCM_START" : + "VIRTIO_SND_R_PCM_STOP", stream_id); + stream = virtio_snd_pcm_get_stream(s, stream_id); + if (stream == NULL) { + error_report("Invalid stream id: %"PRIu32, req.stream_id); + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); + return; + } + stream->active = start; +} + /* * The actual processing done in virtio_snd_process_cmdq(). * @@ -486,10 +527,14 @@ process_cmd(VirtIOSound *s, virtio_snd_ctrl_command *cmd) case VIRTIO_SND_R_PCM_INFO: virtio_snd_handle_pcm_info(s, cmd); break; + case VIRTIO_SND_R_PCM_START: + virtio_snd_handle_pcm_start_stop(s, cmd, true); + break; + case VIRTIO_SND_R_PCM_STOP: + virtio_snd_handle_pcm_start_stop(s, cmd, false); + break; case VIRTIO_SND_R_PCM_SET_PARAMS: case VIRTIO_SND_R_PCM_PREPARE: - case VIRTIO_SND_R_PCM_START: - case VIRTIO_SND_R_PCM_STOP: case VIRTIO_SND_R_PCM_RELEASE: cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); break; From 64704ce04b0a62184d49f899e7f300f67480a2a6 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Mon, 23 Oct 2023 15:03:23 +0300 Subject: [PATCH 674/974] virtio-sound: handle VIRTIO_SND_R_PCM_SET_PARAMS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Handle the set parameters control request. It reconfigures a stream based on a guest's preference if the values are valid and supported. Based-on: https://github.com/OpenSynergy/qemu/commit/5a2f350eec5d157b90d9c7b40a8e603f4da92471 Signed-off-by: Igor Skalkin Signed-off-by: Anton Yakovlev Signed-off-by: Manos Pitsidianakis Reviewed-by: Alex Bennée Tested-by: Alex Bennée Message-Id: Acked-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/audio/trace-events | 1 + hw/audio/virtio-snd.c | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/hw/audio/trace-events b/hw/audio/trace-events index db48ff04fe..3badcab2e8 100644 --- a/hw/audio/trace-events +++ b/hw/audio/trace-events @@ -47,6 +47,7 @@ virtio_snd_vm_state_running(void) "vm state running" virtio_snd_vm_state_stopped(void) "vm state stopped" virtio_snd_realize(void *snd) "snd %p: realize" virtio_snd_unrealize(void *snd) "snd %p: unrealize" +virtio_snd_handle_pcm_set_params(uint32_t stream) "VIRTIO_SND_PCM_SET_PARAMS called for stream %"PRIu32 virtio_snd_handle_ctrl(void *vdev, void *vq) "snd %p: handle ctrl event for queue %p" virtio_snd_handle_pcm_info(uint32_t stream) "VIRTIO_SND_R_PCM_INFO called for stream %"PRIu32 virtio_snd_handle_pcm_start_stop(const char *code, uint32_t stream) "%s called for stream %"PRIu32 diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c index e6791de6c6..084890e52b 100644 --- a/hw/audio/virtio-snd.c +++ b/hw/audio/virtio-snd.c @@ -287,6 +287,38 @@ uint32_t virtio_snd_set_pcm_params(VirtIOSound *s, return cpu_to_le32(VIRTIO_SND_S_OK); } +/* + * Handles the VIRTIO_SND_R_PCM_SET_PARAMS request. + * + * @s: VirtIOSound device + * @cmd: The request command queue element from VirtIOSound cmdq field + */ +static void virtio_snd_handle_pcm_set_params(VirtIOSound *s, + virtio_snd_ctrl_command *cmd) +{ + virtio_snd_pcm_set_params req = { 0 }; + uint32_t stream_id; + size_t msg_sz = iov_to_buf(cmd->elem->out_sg, + cmd->elem->out_num, + 0, + &req, + sizeof(virtio_snd_pcm_set_params)); + + if (msg_sz != sizeof(virtio_snd_pcm_set_params)) { + /* + * TODO: do we need to set DEVICE_NEEDS_RESET? + */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: virtio-snd command size incorrect %zu vs \ + %zu\n", __func__, msg_sz, sizeof(virtio_snd_pcm_set_params)); + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); + return; + } + stream_id = le32_to_cpu(req.hdr.stream_id); + trace_virtio_snd_handle_pcm_set_params(stream_id); + cmd->resp.code = virtio_snd_set_pcm_params(s, stream_id, &req); +} + /* * Get a QEMU Audiosystem compatible format value from a VIRTIO_SND_PCM_FMT_* */ @@ -534,6 +566,8 @@ process_cmd(VirtIOSound *s, virtio_snd_ctrl_command *cmd) virtio_snd_handle_pcm_start_stop(s, cmd, false); break; case VIRTIO_SND_R_PCM_SET_PARAMS: + virtio_snd_handle_pcm_set_params(s, cmd); + break; case VIRTIO_SND_R_PCM_PREPARE: case VIRTIO_SND_R_PCM_RELEASE: cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); From e5788b8fbf9361c978eb8347471d8f855580777f Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Mon, 23 Oct 2023 15:03:24 +0300 Subject: [PATCH 675/974] virtio-sound: handle VIRTIO_SND_R_PCM_PREPARE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Handles the PCM prepare control request. It initializes a PCM stream when the guests asks for it. Based-on: https://github.com/OpenSynergy/qemu/commit/5a2f350eec5d157b90d9c7b40a8e603f4da92471 Signed-off-by: Igor Skalkin Signed-off-by: Anton Yakovlev Signed-off-by: Manos Pitsidianakis Reviewed-by: Alex Bennée Tested-by: Alex Bennée Message-Id: Acked-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/audio/virtio-snd.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c index 084890e52b..31a1942754 100644 --- a/hw/audio/virtio-snd.c +++ b/hw/audio/virtio-snd.c @@ -478,6 +478,28 @@ static const char *print_code(uint32_t code) #undef CASE }; +/* + * Handles VIRTIO_SND_R_PCM_PREPARE. + * + * @s: VirtIOSound device + * @cmd: The request command queue element from VirtIOSound cmdq field + */ +static void virtio_snd_handle_pcm_prepare(VirtIOSound *s, + virtio_snd_ctrl_command *cmd) +{ + uint32_t stream_id; + size_t msg_sz = iov_to_buf(cmd->elem->out_sg, + cmd->elem->out_num, + sizeof(virtio_snd_hdr), + &stream_id, + sizeof(stream_id)); + + stream_id = le32_to_cpu(stream_id); + cmd->resp.code = msg_sz == sizeof(stream_id) + ? virtio_snd_pcm_prepare(s, stream_id) + : cpu_to_le32(VIRTIO_SND_S_BAD_MSG); +} + /* * Handles VIRTIO_SND_R_PCM_START. * @@ -569,6 +591,8 @@ process_cmd(VirtIOSound *s, virtio_snd_ctrl_command *cmd) virtio_snd_handle_pcm_set_params(s, cmd); break; case VIRTIO_SND_R_PCM_PREPARE: + virtio_snd_handle_pcm_prepare(s, cmd); + break; case VIRTIO_SND_R_PCM_RELEASE: cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); break; From d48800d740d1eaea5be65accbf37d82f0effcfb5 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Mon, 23 Oct 2023 15:03:25 +0300 Subject: [PATCH 676/974] virtio-sound: handle VIRTIO_SND_R_PCM_RELEASE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Handle the PCM release control request, which is necessary for flushing pending sound IO. No IO is handled yet so currently it only replies to the request. Based-on: https://github.com/OpenSynergy/qemu/commit/5a2f350eec5d157b90d9c7b40a8e603f4da92471 Signed-off-by: Igor Skalkin Signed-off-by: Anton Yakovlev Signed-off-by: Manos Pitsidianakis Reviewed-by: Alex Bennée Tested-by: Alex Bennée Message-Id: Acked-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/audio/trace-events | 1 + hw/audio/virtio-snd.c | 48 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/hw/audio/trace-events b/hw/audio/trace-events index 3badcab2e8..33e24d0011 100644 --- a/hw/audio/trace-events +++ b/hw/audio/trace-events @@ -51,6 +51,7 @@ virtio_snd_handle_pcm_set_params(uint32_t stream) "VIRTIO_SND_PCM_SET_PARAMS cal virtio_snd_handle_ctrl(void *vdev, void *vq) "snd %p: handle ctrl event for queue %p" virtio_snd_handle_pcm_info(uint32_t stream) "VIRTIO_SND_R_PCM_INFO called for stream %"PRIu32 virtio_snd_handle_pcm_start_stop(const char *code, uint32_t stream) "%s called for stream %"PRIu32 +virtio_snd_handle_pcm_release(uint32_t stream) "VIRTIO_SND_PCM_RELEASE called for stream %"PRIu32 virtio_snd_handle_code(uint32_t val, const char *code) "ctrl code msg val = %"PRIu32" == %s" virtio_snd_handle_chmap_info(void) "VIRTIO_SND_CHMAP_INFO called" virtio_snd_handle_event(void) "event queue callback called" diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c index 31a1942754..9cff724f62 100644 --- a/hw/audio/virtio-snd.c +++ b/hw/audio/virtio-snd.c @@ -541,6 +541,52 @@ static void virtio_snd_handle_pcm_start_stop(VirtIOSound *s, stream->active = start; } +/* + * Handles VIRTIO_SND_R_PCM_RELEASE. Releases the buffer resources allocated to + * a stream. + * + * @s: VirtIOSound device + * @cmd: The request command queue element from VirtIOSound cmdq field + */ +static void virtio_snd_handle_pcm_release(VirtIOSound *s, + virtio_snd_ctrl_command *cmd) +{ + uint32_t stream_id; + VirtIOSoundPCMStream *stream; + size_t msg_sz = iov_to_buf(cmd->elem->out_sg, + cmd->elem->out_num, + sizeof(virtio_snd_hdr), + &stream_id, + sizeof(stream_id)); + + if (msg_sz != sizeof(stream_id)) { + /* + * TODO: do we need to set DEVICE_NEEDS_RESET? + */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: virtio-snd command size incorrect %zu vs \ + %zu\n", __func__, msg_sz, sizeof(stream_id)); + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); + return; + } + + stream_id = le32_to_cpu(stream_id); + trace_virtio_snd_handle_pcm_release(stream_id); + stream = virtio_snd_pcm_get_stream(s, stream_id); + if (stream == NULL) { + /* + * TODO: do we need to set DEVICE_NEEDS_RESET? + */ + error_report("already released stream %"PRIu32, stream_id); + virtio_error(VIRTIO_DEVICE(s), + "already released stream %"PRIu32, + stream_id); + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); + return; + } + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK); +} + /* * The actual processing done in virtio_snd_process_cmdq(). * @@ -594,7 +640,7 @@ process_cmd(VirtIOSound *s, virtio_snd_ctrl_command *cmd) virtio_snd_handle_pcm_prepare(s, cmd); break; case VIRTIO_SND_R_PCM_RELEASE: - cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); + virtio_snd_handle_pcm_release(s, cmd); break; case VIRTIO_SND_R_CHMAP_INFO: qemu_log_mask(LOG_UNIMP, From 18a752810f04e3f1933682addd0a6c978efc5cdf Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Mon, 23 Oct 2023 15:03:26 +0300 Subject: [PATCH 677/974] virtio-sound: implement audio output (TX) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Handle output IO messages in the transmit (TX) virtqueue. It allocates a VirtIOSoundPCMBuffer for each IO message and copies the data buffer to it. When the IO buffer is written to the host's sound card, the guest will be notified that it has been consumed. The lifetime of an IO message is: 1. Guest sends IO message to TX virtqueue. 2. QEMU adds it to the appropriate stream's IO buffer queue. 3. Sometime later, the host audio backend calls the output callback, virtio_snd_pcm_out_cb(), which is defined with an AUD_open_out() call. The callback gets an available number of bytes the backend can receive. Then it writes data from the IO buffer queue to the backend. If at any time a buffer is exhausted, it is returned to the guest as completed. 4. If the guest releases the stream, its buffer queue is flushed by attempting to write any leftover data to the audio backend and releasing all IO messages back to the guest. This is how according to the spec the guest knows the release was successful. Based-on: https://github.com/OpenSynergy/qemu/commit/5a2f350eec5d157b90d9c7b40a8e603f4da92471 Signed-off-by: Igor Skalkin Signed-off-by: Anton Yakovlev Signed-off-by: Manos Pitsidianakis Reviewed-by: Alex Bennée Tested-by: Alex Bennée Message-Id: Acked-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/audio/trace-events | 2 + hw/audio/virtio-snd.c | 288 +++++++++++++++++++++++++++++++++- include/hw/audio/virtio-snd.h | 47 ++++++ 3 files changed, 332 insertions(+), 5 deletions(-) diff --git a/hw/audio/trace-events b/hw/audio/trace-events index 33e24d0011..884108129b 100644 --- a/hw/audio/trace-events +++ b/hw/audio/trace-events @@ -55,3 +55,5 @@ virtio_snd_handle_pcm_release(uint32_t stream) "VIRTIO_SND_PCM_RELEASE called fo virtio_snd_handle_code(uint32_t val, const char *code) "ctrl code msg val = %"PRIu32" == %s" virtio_snd_handle_chmap_info(void) "VIRTIO_SND_CHMAP_INFO called" virtio_snd_handle_event(void) "event queue callback called" +virtio_snd_pcm_stream_flush(uint32_t stream) "flushing stream %"PRIu32 +virtio_snd_handle_xfer(void) "tx/rx queue callback called" diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c index 9cff724f62..6c91d0a740 100644 --- a/hw/audio/virtio-snd.c +++ b/hw/audio/virtio-snd.c @@ -32,6 +32,10 @@ #define VIRTIO_SOUND_CHMAP_DEFAULT 0 #define VIRTIO_SOUND_HDA_FN_NID 0 +static void virtio_snd_pcm_out_cb(void *data, int available); +static void virtio_snd_process_cmdq(VirtIOSound *s); +static void virtio_snd_pcm_flush(VirtIOSoundPCMStream *stream); + static uint32_t supported_formats = BIT(VIRTIO_SND_PCM_FMT_S8) | BIT(VIRTIO_SND_PCM_FMT_U8) | BIT(VIRTIO_SND_PCM_FMT_S16) @@ -123,6 +127,13 @@ virtio_snd_set_config(VirtIODevice *vdev, const uint8_t *config) } +static void +virtio_snd_pcm_buffer_free(VirtIOSoundPCMBuffer *buffer) +{ + g_free(buffer->elem); + g_free(buffer); +} + static void virtio_snd_ctrl_cmd_free(virtio_snd_ctrl_command *cmd) { @@ -396,6 +407,13 @@ static void virtio_snd_get_qemu_audsettings(audsettings *as, */ static void virtio_snd_pcm_close(VirtIOSoundPCMStream *stream) { + if (stream) { + if (stream->info.direction == VIRTIO_SND_D_OUTPUT) { + virtio_snd_pcm_flush(stream); + AUD_close_out(&stream->pcm->snd->card, stream->voice.out); + stream->voice.out = NULL; + } + } } /* @@ -429,6 +447,9 @@ static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, uint32_t stream_id) stream->id = stream_id; stream->pcm = s->pcm; stream->s = s; + qemu_mutex_init(&stream->queue_mutex); + QSIMPLEQ_INIT(&stream->queue); + QSIMPLEQ_INIT(&stream->invalid); /* * stream_id >= s->snd_conf.streams was checked before so this is @@ -452,6 +473,18 @@ static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, uint32_t stream_id) stream->positions[1] = VIRTIO_SND_CHMAP_FR; stream->as = as; + if (stream->info.direction == VIRTIO_SND_D_OUTPUT) { + stream->voice.out = AUD_open_out(&s->card, + stream->voice.out, + "virtio-sound.out", + stream, + virtio_snd_pcm_out_cb, + &as); + AUD_set_volume_out(stream->voice.out, 0, 255, 255); + } else { + qemu_log_mask(LOG_UNIMP, "virtio_snd: input/capture is unimplemented."); + } + return cpu_to_le32(VIRTIO_SND_S_OK); } @@ -532,9 +565,17 @@ static void virtio_snd_handle_pcm_start_stop(VirtIOSound *s, cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK); trace_virtio_snd_handle_pcm_start_stop(start ? "VIRTIO_SND_R_PCM_START" : "VIRTIO_SND_R_PCM_STOP", stream_id); + stream = virtio_snd_pcm_get_stream(s, stream_id); - if (stream == NULL) { - error_report("Invalid stream id: %"PRIu32, req.stream_id); + if (stream) { + WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { + stream->active = start; + } + if (stream->info.direction == VIRTIO_SND_D_OUTPUT) { + AUD_set_active_out(stream->voice.out, start); + } + } else { + error_report("Invalid stream id: %"PRIu32, stream_id); cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); return; } @@ -542,8 +583,28 @@ static void virtio_snd_handle_pcm_start_stop(VirtIOSound *s, } /* - * Handles VIRTIO_SND_R_PCM_RELEASE. Releases the buffer resources allocated to - * a stream. + * Returns the number of I/O messages that are being processed. + * + * @stream: VirtIOSoundPCMStream + */ +static size_t virtio_snd_pcm_get_io_msgs_count(VirtIOSoundPCMStream *stream) +{ + VirtIOSoundPCMBuffer *buffer, *next; + size_t count = 0; + + WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { + QSIMPLEQ_FOREACH_SAFE(buffer, &stream->queue, entry, next) { + count += 1; + } + QSIMPLEQ_FOREACH_SAFE(buffer, &stream->invalid, entry, next) { + count += 1; + } + } + return count; +} + +/* + * Handles VIRTIO_SND_R_PCM_RELEASE. * * @s: VirtIOSound device * @cmd: The request command queue element from VirtIOSound cmdq field @@ -584,6 +645,21 @@ static void virtio_snd_handle_pcm_release(VirtIOSound *s, cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); return; } + + if (virtio_snd_pcm_get_io_msgs_count(stream)) { + /* + * virtio-v1.2-csd01, 5.14.6.6.5.1, + * Device Requirements: Stream Release + * + * - The device MUST complete all pending I/O messages for the + * specified stream ID. + * - The device MUST NOT complete the control request while there + * are pending I/O messages for the specified stream ID. + */ + trace_virtio_snd_pcm_stream_flush(stream_id); + virtio_snd_pcm_flush(stream); + } + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK); } @@ -738,6 +814,108 @@ static void virtio_snd_handle_event(VirtIODevice *vdev, VirtQueue *vq) trace_virtio_snd_handle_event(); } +/* + * The tx virtqueue handler. Makes the buffers available to their respective + * streams for consumption. + * + * @vdev: VirtIOSound device + * @vq: tx virtqueue + */ +static void virtio_snd_handle_tx(VirtIODevice *vdev, VirtQueue *vq) +{ + VirtIOSound *s = VIRTIO_SND(vdev); + VirtIOSoundPCMStream *stream = NULL; + VirtIOSoundPCMBuffer *buffer; + VirtQueueElement *elem; + size_t msg_sz, size; + virtio_snd_pcm_xfer hdr; + virtio_snd_pcm_status resp = { 0 }; + uint32_t stream_id; + /* + * If any of the I/O messages are invalid, put them in stream->invalid and + * return them after the for loop. + */ + bool must_empty_invalid_queue = false; + + if (!virtio_queue_ready(vq)) { + return; + } + trace_virtio_snd_handle_xfer(); + + for (;;) { + elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); + if (!elem) { + break; + } + /* get the message hdr object */ + msg_sz = iov_to_buf(elem->out_sg, + elem->out_num, + 0, + &hdr, + sizeof(virtio_snd_pcm_xfer)); + if (msg_sz != sizeof(virtio_snd_pcm_xfer)) { + goto tx_err; + } + stream_id = le32_to_cpu(hdr.stream_id); + + if (stream_id >= s->snd_conf.streams + || s->pcm->streams[stream_id] == NULL) { + goto tx_err; + } + + stream = s->pcm->streams[stream_id]; + if (stream->info.direction != VIRTIO_SND_D_OUTPUT) { + goto tx_err; + } + + WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { + size = iov_size(elem->out_sg, elem->out_num) - msg_sz; + + buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size); + buffer->elem = elem; + buffer->populated = false; + buffer->vq = vq; + buffer->size = size; + buffer->offset = 0; + + QSIMPLEQ_INSERT_TAIL(&stream->queue, buffer, entry); + } + continue; + +tx_err: + WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { + must_empty_invalid_queue = true; + buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer)); + buffer->elem = elem; + buffer->vq = vq; + QSIMPLEQ_INSERT_TAIL(&stream->invalid, buffer, entry); + } + } + + if (must_empty_invalid_queue) { + WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { + while (!QSIMPLEQ_EMPTY(&stream->invalid)) { + buffer = QSIMPLEQ_FIRST(&stream->invalid); + + resp.status = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); + iov_from_buf(buffer->elem->in_sg, + buffer->elem->in_num, + 0, + &resp, + sizeof(virtio_snd_pcm_status)); + virtqueue_push(vq, buffer->elem, sizeof(virtio_snd_pcm_status)); + QSIMPLEQ_REMOVE_HEAD(&stream->invalid, entry); + virtio_snd_pcm_buffer_free(buffer); + } + /* + * Notify vq about virtio_snd_pcm_status responses. + * Buffer responses must be notified separately later. + */ + virtio_notify(vdev, vq); + } + } +} + /* * Stub buffer virtqueue handler. * @@ -832,7 +1010,7 @@ static void virtio_snd_realize(DeviceState *dev, Error **errp) vsnd->queues[VIRTIO_SND_VQ_EVENT] = virtio_add_queue(vdev, 64, virtio_snd_handle_event); vsnd->queues[VIRTIO_SND_VQ_TX] = - virtio_add_queue(vdev, 64, virtio_snd_handle_xfer); + virtio_add_queue(vdev, 64, virtio_snd_handle_tx); vsnd->queues[VIRTIO_SND_VQ_RX] = virtio_add_queue(vdev, 64, virtio_snd_handle_xfer); qemu_mutex_init(&vsnd->cmdq_mutex); @@ -856,6 +1034,105 @@ static void virtio_snd_realize(DeviceState *dev, Error **errp) } } +static inline void return_tx_buffer(VirtIOSoundPCMStream *stream, + VirtIOSoundPCMBuffer *buffer) +{ + virtio_snd_pcm_status resp = { 0 }; + resp.status = cpu_to_le32(VIRTIO_SND_S_OK); + resp.latency_bytes = cpu_to_le32((uint32_t)buffer->size); + iov_from_buf(buffer->elem->in_sg, + buffer->elem->in_num, + 0, + &resp, + sizeof(virtio_snd_pcm_status)); + virtqueue_push(buffer->vq, + buffer->elem, + sizeof(virtio_snd_pcm_status)); + virtio_notify(VIRTIO_DEVICE(stream->s), buffer->vq); + QSIMPLEQ_REMOVE(&stream->queue, + buffer, + VirtIOSoundPCMBuffer, + entry); + virtio_snd_pcm_buffer_free(buffer); +} + +/* + * AUD_* output callback. + * + * @data: VirtIOSoundPCMStream stream + * @available: number of bytes that can be written with AUD_write() + */ +static void virtio_snd_pcm_out_cb(void *data, int available) +{ + VirtIOSoundPCMStream *stream = data; + VirtIOSoundPCMBuffer *buffer; + size_t size; + + WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { + while (!QSIMPLEQ_EMPTY(&stream->queue)) { + buffer = QSIMPLEQ_FIRST(&stream->queue); + if (!virtio_queue_ready(buffer->vq)) { + return; + } + if (!stream->active) { + /* Stream has stopped, so do not perform AUD_write. */ + return_tx_buffer(stream, buffer); + continue; + } + if (!buffer->populated) { + iov_to_buf(buffer->elem->out_sg, + buffer->elem->out_num, + sizeof(virtio_snd_pcm_xfer), + buffer->data, + buffer->size); + buffer->populated = true; + } + for (;;) { + size = AUD_write(stream->voice.out, + buffer->data + buffer->offset, + MIN(buffer->size, available)); + assert(size <= MIN(buffer->size, available)); + if (size == 0) { + /* break out of both loops */ + available = 0; + break; + } + buffer->size -= size; + buffer->offset += size; + available -= size; + if (buffer->size < 1) { + return_tx_buffer(stream, buffer); + break; + } + if (!available) { + break; + } + } + if (!available) { + break; + } + } + } +} + +/* + * Flush all buffer data from this stream's queue into the driver's virtual + * queue. + * + * @stream: VirtIOSoundPCMStream *stream + */ +static inline void virtio_snd_pcm_flush(VirtIOSoundPCMStream *stream) +{ + VirtIOSoundPCMBuffer *buffer; + + WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { + while (!QSIMPLEQ_EMPTY(&stream->queue)) { + buffer = QSIMPLEQ_FIRST(&stream->queue); + return_tx_buffer(stream, buffer); + } + } +} + static void virtio_snd_unrealize(DeviceState *dev) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); @@ -872,6 +1149,7 @@ static void virtio_snd_unrealize(DeviceState *dev) if (stream) { virtio_snd_process_cmdq(stream->s); virtio_snd_pcm_close(stream); + qemu_mutex_destroy(&stream->queue_mutex); g_free(stream); } } diff --git a/include/hw/audio/virtio-snd.h b/include/hw/audio/virtio-snd.h index cc14c875ed..c3767f442b 100644 --- a/include/hw/audio/virtio-snd.h +++ b/include/hw/audio/virtio-snd.h @@ -77,6 +77,50 @@ typedef struct virtio_snd_ctrl_command virtio_snd_ctrl_command; typedef struct VirtIOSoundPCM VirtIOSoundPCM; +typedef struct VirtIOSoundPCMBuffer VirtIOSoundPCMBuffer; + +/* + * The VirtIO sound spec reuses layouts and values from the High Definition + * Audio spec (virtio/v1.2: 5.14 Sound Device). This struct handles each I/O + * message's buffer (virtio/v1.2: 5.14.6.8 PCM I/O Messages). + * + * In the case of TX (i.e. playback) buffers, we defer reading the raw PCM data + * from the virtqueue until QEMU's sound backsystem calls the output callback. + * This is tracked by the `bool populated;` field, which is set to true when + * data has been read into our own buffer for consumption. + * + * VirtIOSoundPCMBuffer has a dynamic size since it includes the raw PCM data + * in its allocation. It must be initialized and destroyed as follows: + * + * size_t size = [[derived from owned VQ element descriptor sizes]]; + * buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size); + * buffer->elem = [[owned VQ element]]; + * + * [..] + * + * g_free(buffer->elem); + * g_free(buffer); + */ +struct VirtIOSoundPCMBuffer { + QSIMPLEQ_ENTRY(VirtIOSoundPCMBuffer) entry; + VirtQueueElement *elem; + VirtQueue *vq; + size_t size; + /* + * In TX / Plaback, `offset` represents the first unused position inside + * `data`. If `offset == size` then there are no unused data left. + */ + uint64_t offset; + /* Used for the TX queue for lazy I/O copy from `elem` */ + bool populated; + /* + * VirtIOSoundPCMBuffer is an unsized type because it ends with an array of + * bytes. The size of `data` is determined from the I/O message's read-only + * or write-only size when allocating VirtIOSoundPCMBuffer. + */ + uint8_t data[]; +}; + struct VirtIOSoundPCM { VirtIOSound *snd; /* @@ -104,7 +148,10 @@ struct VirtIOSoundPCMStream { SWVoiceIn *in; SWVoiceOut *out; } voice; + QemuMutex queue_mutex; bool active; + QSIMPLEQ_HEAD(, VirtIOSoundPCMBuffer) queue; + QSIMPLEQ_HEAD(, VirtIOSoundPCMBuffer) invalid; }; /* From d8d64acbecfa131153e5b96fb24b4f95f55be881 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Mon, 23 Oct 2023 15:03:27 +0300 Subject: [PATCH 678/974] virtio-sound: implement audio capture (RX) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To perform audio capture we duplicate the TX logic of the previous commit with the following difference: we receive data from the QEMU audio backend and write it in the virt queue IO buffers the guest sends to QEMU. When they are full (i.e. they have `period_bytes` amount of data) or when recording stops in QEMU's audio backend, the buffer is returned to the guest by notifying it. Signed-off-by: Manos Pitsidianakis Tested-by: Alex Bennée Reviewed-by: Alex Bennée Message-Id: Acked-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/audio/trace-events | 3 +- hw/audio/virtio-snd.c | 262 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 230 insertions(+), 35 deletions(-) diff --git a/hw/audio/trace-events b/hw/audio/trace-events index 884108129b..b1870ff224 100644 --- a/hw/audio/trace-events +++ b/hw/audio/trace-events @@ -56,4 +56,5 @@ virtio_snd_handle_code(uint32_t val, const char *code) "ctrl code msg val = %"PR virtio_snd_handle_chmap_info(void) "VIRTIO_SND_CHMAP_INFO called" virtio_snd_handle_event(void) "event queue callback called" virtio_snd_pcm_stream_flush(uint32_t stream) "flushing stream %"PRIu32 -virtio_snd_handle_xfer(void) "tx/rx queue callback called" +virtio_snd_handle_tx_xfer(void) "tx queue callback called" +virtio_snd_handle_rx_xfer(void) "rx queue callback called" diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c index 6c91d0a740..a18a9949a7 100644 --- a/hw/audio/virtio-snd.c +++ b/hw/audio/virtio-snd.c @@ -28,13 +28,14 @@ #define VIRTIO_SOUND_VM_VERSION 1 #define VIRTIO_SOUND_JACK_DEFAULT 0 -#define VIRTIO_SOUND_STREAM_DEFAULT 1 +#define VIRTIO_SOUND_STREAM_DEFAULT 2 #define VIRTIO_SOUND_CHMAP_DEFAULT 0 #define VIRTIO_SOUND_HDA_FN_NID 0 static void virtio_snd_pcm_out_cb(void *data, int available); static void virtio_snd_process_cmdq(VirtIOSound *s); static void virtio_snd_pcm_flush(VirtIOSoundPCMStream *stream); +static void virtio_snd_pcm_in_cb(void *data, int available); static uint32_t supported_formats = BIT(VIRTIO_SND_PCM_FMT_S8) | BIT(VIRTIO_SND_PCM_FMT_U8) @@ -408,10 +409,13 @@ static void virtio_snd_get_qemu_audsettings(audsettings *as, static void virtio_snd_pcm_close(VirtIOSoundPCMStream *stream) { if (stream) { + virtio_snd_pcm_flush(stream); if (stream->info.direction == VIRTIO_SND_D_OUTPUT) { - virtio_snd_pcm_flush(stream); AUD_close_out(&stream->pcm->snd->card, stream->voice.out); stream->voice.out = NULL; + } else if (stream->info.direction == VIRTIO_SND_D_INPUT) { + AUD_close_in(&stream->pcm->snd->card, stream->voice.in); + stream->voice.in = NULL; } } } @@ -482,7 +486,13 @@ static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, uint32_t stream_id) &as); AUD_set_volume_out(stream->voice.out, 0, 255, 255); } else { - qemu_log_mask(LOG_UNIMP, "virtio_snd: input/capture is unimplemented."); + stream->voice.in = AUD_open_in(&s->card, + stream->voice.in, + "virtio-sound.in", + stream, + virtio_snd_pcm_in_cb, + &as); + AUD_set_volume_in(stream->voice.in, 0, 255, 255); } return cpu_to_le32(VIRTIO_SND_S_OK); @@ -573,6 +583,8 @@ static void virtio_snd_handle_pcm_start_stop(VirtIOSound *s, } if (stream->info.direction == VIRTIO_SND_D_OUTPUT) { AUD_set_active_out(stream->voice.out, start); + } else { + AUD_set_active_in(stream->voice.in, start); } } else { error_report("Invalid stream id: %"PRIu32, stream_id); @@ -814,6 +826,49 @@ static void virtio_snd_handle_event(VirtIODevice *vdev, VirtQueue *vq) trace_virtio_snd_handle_event(); } +static inline void empty_invalid_queue(VirtIODevice *vdev, VirtQueue *vq) +{ + VirtIOSoundPCMBuffer *buffer = NULL; + VirtIOSoundPCMStream *stream = NULL; + virtio_snd_pcm_status resp = { 0 }; + VirtIOSound *vsnd = VIRTIO_SND(vdev); + bool any = false; + + for (uint32_t i = 0; i < vsnd->snd_conf.streams; i++) { + stream = vsnd->pcm->streams[i]; + if (stream) { + any = false; + WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { + while (!QSIMPLEQ_EMPTY(&stream->invalid)) { + buffer = QSIMPLEQ_FIRST(&stream->invalid); + if (buffer->vq != vq) { + break; + } + any = true; + resp.status = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); + iov_from_buf(buffer->elem->in_sg, + buffer->elem->in_num, + 0, + &resp, + sizeof(virtio_snd_pcm_status)); + virtqueue_push(vq, + buffer->elem, + sizeof(virtio_snd_pcm_status)); + QSIMPLEQ_REMOVE_HEAD(&stream->invalid, entry); + virtio_snd_pcm_buffer_free(buffer); + } + if (any) { + /* + * Notify vq about virtio_snd_pcm_status responses. + * Buffer responses must be notified separately later. + */ + virtio_notify(vdev, vq); + } + } + } + } +} + /* * The tx virtqueue handler. Makes the buffers available to their respective * streams for consumption. @@ -821,7 +876,7 @@ static void virtio_snd_handle_event(VirtIODevice *vdev, VirtQueue *vq) * @vdev: VirtIOSound device * @vq: tx virtqueue */ -static void virtio_snd_handle_tx(VirtIODevice *vdev, VirtQueue *vq) +static void virtio_snd_handle_tx_xfer(VirtIODevice *vdev, VirtQueue *vq) { VirtIOSound *s = VIRTIO_SND(vdev); VirtIOSoundPCMStream *stream = NULL; @@ -829,7 +884,6 @@ static void virtio_snd_handle_tx(VirtIODevice *vdev, VirtQueue *vq) VirtQueueElement *elem; size_t msg_sz, size; virtio_snd_pcm_xfer hdr; - virtio_snd_pcm_status resp = { 0 }; uint32_t stream_id; /* * If any of the I/O messages are invalid, put them in stream->invalid and @@ -840,7 +894,7 @@ static void virtio_snd_handle_tx(VirtIODevice *vdev, VirtQueue *vq) if (!virtio_queue_ready(vq)) { return; } - trace_virtio_snd_handle_xfer(); + trace_virtio_snd_handle_tx_xfer(); for (;;) { elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); @@ -893,36 +947,88 @@ tx_err: } if (must_empty_invalid_queue) { - WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { - while (!QSIMPLEQ_EMPTY(&stream->invalid)) { - buffer = QSIMPLEQ_FIRST(&stream->invalid); - - resp.status = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); - iov_from_buf(buffer->elem->in_sg, - buffer->elem->in_num, - 0, - &resp, - sizeof(virtio_snd_pcm_status)); - virtqueue_push(vq, buffer->elem, sizeof(virtio_snd_pcm_status)); - QSIMPLEQ_REMOVE_HEAD(&stream->invalid, entry); - virtio_snd_pcm_buffer_free(buffer); - } - /* - * Notify vq about virtio_snd_pcm_status responses. - * Buffer responses must be notified separately later. - */ - virtio_notify(vdev, vq); - } + empty_invalid_queue(vdev, vq); } } /* - * Stub buffer virtqueue handler. + * The rx virtqueue handler. Makes the buffers available to their respective + * streams for consumption. * * @vdev: VirtIOSound device - * @vq: virtqueue + * @vq: rx virtqueue */ -static void virtio_snd_handle_xfer(VirtIODevice *vdev, VirtQueue *vq) {} +static void virtio_snd_handle_rx_xfer(VirtIODevice *vdev, VirtQueue *vq) +{ + VirtIOSound *s = VIRTIO_SND(vdev); + VirtIOSoundPCMStream *stream = NULL; + VirtIOSoundPCMBuffer *buffer; + VirtQueueElement *elem; + size_t msg_sz, size; + virtio_snd_pcm_xfer hdr; + uint32_t stream_id; + /* + * if any of the I/O messages are invalid, put them in stream->invalid and + * return them after the for loop. + */ + bool must_empty_invalid_queue = false; + + if (!virtio_queue_ready(vq)) { + return; + } + trace_virtio_snd_handle_rx_xfer(); + + for (;;) { + elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); + if (!elem) { + break; + } + /* get the message hdr object */ + msg_sz = iov_to_buf(elem->out_sg, + elem->out_num, + 0, + &hdr, + sizeof(virtio_snd_pcm_xfer)); + if (msg_sz != sizeof(virtio_snd_pcm_xfer)) { + goto rx_err; + } + stream_id = le32_to_cpu(hdr.stream_id); + + if (stream_id >= s->snd_conf.streams + || !s->pcm->streams[stream_id]) { + goto rx_err; + } + + stream = s->pcm->streams[stream_id]; + if (stream == NULL || stream->info.direction != VIRTIO_SND_D_INPUT) { + goto rx_err; + } + WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { + size = iov_size(elem->in_sg, elem->in_num) - + sizeof(virtio_snd_pcm_status); + buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size); + buffer->elem = elem; + buffer->vq = vq; + buffer->size = 0; + buffer->offset = 0; + QSIMPLEQ_INSERT_TAIL(&stream->queue, buffer, entry); + } + continue; + +rx_err: + WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { + must_empty_invalid_queue = true; + buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer)); + buffer->elem = elem; + buffer->vq = vq; + QSIMPLEQ_INSERT_TAIL(&stream->invalid, buffer, entry); + } + } + + if (must_empty_invalid_queue) { + empty_invalid_queue(vdev, vq); + } +} static uint64_t get_features(VirtIODevice *vdev, uint64_t features, Error **errp) @@ -1010,9 +1116,9 @@ static void virtio_snd_realize(DeviceState *dev, Error **errp) vsnd->queues[VIRTIO_SND_VQ_EVENT] = virtio_add_queue(vdev, 64, virtio_snd_handle_event); vsnd->queues[VIRTIO_SND_VQ_TX] = - virtio_add_queue(vdev, 64, virtio_snd_handle_tx); + virtio_add_queue(vdev, 64, virtio_snd_handle_tx_xfer); vsnd->queues[VIRTIO_SND_VQ_RX] = - virtio_add_queue(vdev, 64, virtio_snd_handle_xfer); + virtio_add_queue(vdev, 64, virtio_snd_handle_rx_xfer); qemu_mutex_init(&vsnd->cmdq_mutex); QTAILQ_INIT(&vsnd->cmdq); @@ -1116,19 +1222,107 @@ static void virtio_snd_pcm_out_cb(void *data, int available) } /* - * Flush all buffer data from this stream's queue into the driver's virtual - * queue. + * Flush all buffer data from this input stream's queue into the driver's + * virtual queue. + * + * @stream: VirtIOSoundPCMStream *stream + */ +static inline void return_rx_buffer(VirtIOSoundPCMStream *stream, + VirtIOSoundPCMBuffer *buffer) +{ + virtio_snd_pcm_status resp = { 0 }; + resp.status = cpu_to_le32(VIRTIO_SND_S_OK); + resp.latency_bytes = 0; + /* Copy data -if any- to guest */ + iov_from_buf(buffer->elem->in_sg, + buffer->elem->in_num, + 0, + buffer->data, + buffer->size); + iov_from_buf(buffer->elem->in_sg, + buffer->elem->in_num, + buffer->size, + &resp, + sizeof(virtio_snd_pcm_status)); + virtqueue_push(buffer->vq, + buffer->elem, + sizeof(virtio_snd_pcm_status) + buffer->size); + virtio_notify(VIRTIO_DEVICE(stream->s), buffer->vq); + QSIMPLEQ_REMOVE(&stream->queue, + buffer, + VirtIOSoundPCMBuffer, + entry); + virtio_snd_pcm_buffer_free(buffer); +} + + +/* + * AUD_* input callback. + * + * @data: VirtIOSoundPCMStream stream + * @available: number of bytes that can be read with AUD_read() + */ +static void virtio_snd_pcm_in_cb(void *data, int available) +{ + VirtIOSoundPCMStream *stream = data; + VirtIOSoundPCMBuffer *buffer; + size_t size; + + WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { + while (!QSIMPLEQ_EMPTY(&stream->queue)) { + buffer = QSIMPLEQ_FIRST(&stream->queue); + if (!virtio_queue_ready(buffer->vq)) { + return; + } + if (!stream->active) { + /* Stream has stopped, so do not perform AUD_read. */ + return_rx_buffer(stream, buffer); + continue; + } + + for (;;) { + size = AUD_read(stream->voice.in, + buffer->data + buffer->size, + MIN(available, (stream->params.period_bytes - + buffer->size))); + if (!size) { + available = 0; + break; + } + buffer->size += size; + available -= size; + if (buffer->size >= stream->params.period_bytes) { + return_rx_buffer(stream, buffer); + break; + } + if (!available) { + break; + } + } + if (!available) { + break; + } + } + } +} + +/* + * Flush all buffer data from this output stream's queue into the driver's + * virtual queue. * * @stream: VirtIOSoundPCMStream *stream */ static inline void virtio_snd_pcm_flush(VirtIOSoundPCMStream *stream) { VirtIOSoundPCMBuffer *buffer; + void (*cb)(VirtIOSoundPCMStream *, VirtIOSoundPCMBuffer *) = + (stream->info.direction == VIRTIO_SND_D_OUTPUT) ? return_tx_buffer : + return_rx_buffer; WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { while (!QSIMPLEQ_EMPTY(&stream->queue)) { buffer = QSIMPLEQ_FIRST(&stream->queue); - return_tx_buffer(stream, buffer); + cb(stream, buffer); } } } From f54fea113cc48000836b95681f3b9fdbe43c7d96 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Mon, 23 Oct 2023 15:03:28 +0300 Subject: [PATCH 679/974] docs/system: add basic virtio-snd documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds basic documentation for using virtio-snd. Signed-off-by: Manos Pitsidianakis Reviewed-by: Alex Bennée Tested-by: Alex Bennée Message-Id: Acked-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- MAINTAINERS | 1 + docs/system/device-emulation.rst | 1 + docs/system/devices/virtio-snd.rst | 49 ++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 docs/system/devices/virtio-snd.rst diff --git a/MAINTAINERS b/MAINTAINERS index c09bb8cf41..4fad272d73 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2317,6 +2317,7 @@ S: Supported F: hw/audio/virtio-snd.c F: hw/audio/virtio-snd-pci.c F: include/hw/audio/virtio-snd.h +F: docs/system/devices/virtio-snd.rst nvme M: Keith Busch diff --git a/docs/system/device-emulation.rst b/docs/system/device-emulation.rst index 1167f3a9f2..d1f3277cb0 100644 --- a/docs/system/device-emulation.rst +++ b/docs/system/device-emulation.rst @@ -93,6 +93,7 @@ Emulated Devices devices/vhost-user.rst devices/virtio-gpu.rst devices/virtio-pmem.rst + devices/virtio-snd.rst devices/vhost-user-rng.rst devices/canokey.rst devices/usb-u2f.rst diff --git a/docs/system/devices/virtio-snd.rst b/docs/system/devices/virtio-snd.rst new file mode 100644 index 0000000000..2a9187fd70 --- /dev/null +++ b/docs/system/devices/virtio-snd.rst @@ -0,0 +1,49 @@ +virtio sound +============ + +This document explains the setup and usage of the Virtio sound device. +The Virtio sound device is a paravirtualized sound card device. + +Linux kernel support +-------------------- + +Virtio sound requires a guest Linux kernel built with the +``CONFIG_SND_VIRTIO`` option. + +Description +----------- + +Virtio sound implements capture and playback from inside a guest using the +configured audio backend of the host machine. + +Device properties +----------------- + +The Virtio sound device can be configured with the following properties: + + * ``jacks`` number of physical jacks (Unimplemented). + * ``streams`` number of PCM streams. At the moment, no stream configuration is supported: the first one will always be a playback stream, an optional second will always be a capture stream. Adding more will cycle stream directions from playback to capture. + * ``chmaps`` number of channel maps (Unimplemented). + +All streams are stereo and have the default channel positions ``Front left, right``. + +Examples +-------- + +Add an audio device and an audio backend at once with ``-audio`` and ``model=virtio``: + + * pulseaudio: ``-audio driver=pa,model=virtio`` + or ``-audio driver=pa,model=virtio,server=/run/user/1000/pulse/native`` + * sdl: ``-audio driver=sdl,model=virtio`` + * coreaudio: ``-audio driver=coreaudio,model=virtio`` + +etc. + +To specifically add virtualized sound devices, you have to specify a PCI device +and an audio backend listed with ``-audio driver=help`` that works on your host +machine, e.g.: + +:: + + -device virtio-sound-pci,audiodev=my_audiodev \ + -audiodev alsa,id=my_audiodev From 8b98c15f22fca34f23846e70752a70e4a2469317 Mon Sep 17 00:00:00 2001 From: Hawkins Jiawei Date: Wed, 25 Oct 2023 09:02:24 +0800 Subject: [PATCH 680/974] vdpa: Restore hash calculation state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch introduces vhost_vdpa_net_load_rss() to restore the hash calculation state at device's startup. Signed-off-by: Hawkins Jiawei Message-Id: Acked-by: Eugenio Pérez Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- net/vhost-vdpa.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index 7a226c93bc..e59d40b8ae 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -818,6 +818,88 @@ static int vhost_vdpa_net_load_mac(VhostVDPAState *s, const VirtIONet *n, return 0; } +static int vhost_vdpa_net_load_rss(VhostVDPAState *s, const VirtIONet *n, + struct iovec *out_cursor, + struct iovec *in_cursor) +{ + struct virtio_net_rss_config cfg = {}; + ssize_t r; + g_autofree uint16_t *table = NULL; + + /* + * According to VirtIO standard, "Initially the device has all hash + * types disabled and reports only VIRTIO_NET_HASH_REPORT_NONE.". + * + * Therefore, there is no need to send this CVQ command if the + * driver disables the all hash types, which aligns with + * the device's defaults. + * + * Note that the device's defaults can mismatch the driver's + * configuration only at live migration. + */ + if (!n->rss_data.enabled || + n->rss_data.hash_types == VIRTIO_NET_HASH_REPORT_NONE) { + return 0; + } + + table = g_malloc_n(n->rss_data.indirections_len, + sizeof(n->rss_data.indirections_table[0])); + cfg.hash_types = cpu_to_le32(n->rss_data.hash_types); + + /* + * According to VirtIO standard, "Field reserved MUST contain zeroes. + * It is defined to make the structure to match the layout of + * virtio_net_rss_config structure, defined in 5.1.6.5.7.". + * + * Therefore, we need to zero the fields in + * struct virtio_net_rss_config, which corresponds to the + * `reserved` field in struct virtio_net_hash_config. + * + * Note that all other fields are zeroed at their definitions, + * except for the `indirection_table` field, where the actual data + * is stored in the `table` variable to ensure compatibility + * with RSS case. Therefore, we need to zero the `table` variable here. + */ + table[0] = 0; + + /* + * Considering that virtio_net_handle_rss() currently does not restore + * the hash key length parsed from the CVQ command sent from the guest + * into n->rss_data and uses the maximum key length in other code, so + * we also employ the maximum key length here. + */ + cfg.hash_key_length = sizeof(n->rss_data.key); + + const struct iovec data[] = { + { + .iov_base = &cfg, + .iov_len = offsetof(struct virtio_net_rss_config, + indirection_table), + }, { + .iov_base = table, + .iov_len = n->rss_data.indirections_len * + sizeof(n->rss_data.indirections_table[0]), + }, { + .iov_base = &cfg.max_tx_vq, + .iov_len = offsetof(struct virtio_net_rss_config, hash_key_data) - + offsetof(struct virtio_net_rss_config, max_tx_vq), + }, { + .iov_base = (void *)n->rss_data.key, + .iov_len = sizeof(n->rss_data.key), + } + }; + + r = vhost_vdpa_net_load_cmd(s, out_cursor, in_cursor, + VIRTIO_NET_CTRL_MQ, + VIRTIO_NET_CTRL_MQ_HASH_CONFIG, + data, ARRAY_SIZE(data)); + if (unlikely(r < 0)) { + return r; + } + + return 0; +} + static int vhost_vdpa_net_load_mq(VhostVDPAState *s, const VirtIONet *n, struct iovec *out_cursor, @@ -843,6 +925,15 @@ static int vhost_vdpa_net_load_mq(VhostVDPAState *s, return r; } + if (!virtio_vdev_has_feature(&n->parent_obj, VIRTIO_NET_F_HASH_REPORT)) { + return 0; + } + + r = vhost_vdpa_net_load_rss(s, n, out_cursor, in_cursor); + if (unlikely(r < 0)) { + return r; + } + return 0; } From 556b67d413a699431eadb71642033864649ea934 Mon Sep 17 00:00:00 2001 From: Hawkins Jiawei Date: Wed, 25 Oct 2023 09:02:25 +0800 Subject: [PATCH 681/974] vdpa: Allow VIRTIO_NET_F_HASH_REPORT in SVQ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable SVQ with VIRTIO_NET_F_HASH_REPORT feature. Signed-off-by: Hawkins Jiawei Message-Id: Acked-by: Eugenio Pérez Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- net/vhost-vdpa.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index e59d40b8ae..54f748d49d 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -121,6 +121,7 @@ static const uint64_t vdpa_svq_device_features = BIT_ULL(VIRTIO_NET_F_CTRL_MAC_ADDR) | /* VHOST_F_LOG_ALL is exposed by SVQ */ BIT_ULL(VHOST_F_LOG_ALL) | + BIT_ULL(VIRTIO_NET_F_HASH_REPORT) | BIT_ULL(VIRTIO_NET_F_RSC_EXT) | BIT_ULL(VIRTIO_NET_F_STANDBY) | BIT_ULL(VIRTIO_NET_F_SPEED_DUPLEX); From d1fd2d3118a42f36d86efae7ffbdce79f7024584 Mon Sep 17 00:00:00 2001 From: Hawkins Jiawei Date: Wed, 25 Oct 2023 09:08:04 +0800 Subject: [PATCH 682/974] vdpa: Add SetSteeringEBPF method for NetClientState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit At present, to enable the VIRTIO_NET_F_RSS feature, eBPF must be loaded for the vhost backend. Given that vhost-vdpa is one of the vhost backend, we need to implement the SetSteeringEBPF method to support RSS for vhost-vdpa, even if vhost-vdpa calculates the rss hash in the hardware device instead of in the kernel by eBPF. Although this requires QEMU to be compiled with `--enable-bpf` configuration even if the vdpa device does not use eBPF to calculate the rss hash, this can avoid adding the specific conditional statements for vDPA case to enable the VIRTIO_NET_F_RSS feature, which reduces code maintainbility. Suggested-by: Eugenio Pérez Signed-off-by: Hawkins Jiawei Message-Id: <280e20ddce55b6de60f1552ba0865bffffe909b2.1698195059.git.yin31149@gmail.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- net/vhost-vdpa.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index 54f748d49d..3466936b87 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -241,6 +241,12 @@ static void vhost_vdpa_cleanup(NetClientState *nc) } } +/** Dummy SetSteeringEBPF to support RSS for vhost-vdpa backend */ +static bool vhost_vdpa_set_steering_ebpf(NetClientState *nc, int prog_fd) +{ + return true; +} + static bool vhost_vdpa_has_vnet_hdr(NetClientState *nc) { assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); @@ -423,6 +429,7 @@ static NetClientInfo net_vhost_vdpa_info = { .has_vnet_hdr = vhost_vdpa_has_vnet_hdr, .has_ufo = vhost_vdpa_has_ufo, .check_peer_type = vhost_vdpa_check_peer_type, + .set_steering_ebpf = vhost_vdpa_set_steering_ebpf, }; static int64_t vhost_vdpa_get_vring_group(int device_fd, unsigned vq_index, @@ -1258,6 +1265,7 @@ static NetClientInfo net_vhost_vdpa_cvq_info = { .has_vnet_hdr = vhost_vdpa_has_vnet_hdr, .has_ufo = vhost_vdpa_has_ufo, .check_peer_type = vhost_vdpa_check_peer_type, + .set_steering_ebpf = vhost_vdpa_set_steering_ebpf, }; /* From b3c09106559f9d84a940d2a27c4cfc81c9e15347 Mon Sep 17 00:00:00 2001 From: Hawkins Jiawei Date: Wed, 25 Oct 2023 09:08:05 +0800 Subject: [PATCH 683/974] vdpa: Restore receive-side scaling state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch reuses vhost_vdpa_net_load_rss() with some refactorings to restore the receive-side scaling state at device's startup. Signed-off-by: Hawkins Jiawei Message-Id: Acked-by: Eugenio Pérez Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- net/vhost-vdpa.c | 67 +++++++++++++++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 23 deletions(-) diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index 3466936b87..a4cc1381fc 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -828,7 +828,7 @@ static int vhost_vdpa_net_load_mac(VhostVDPAState *s, const VirtIONet *n, static int vhost_vdpa_net_load_rss(VhostVDPAState *s, const VirtIONet *n, struct iovec *out_cursor, - struct iovec *in_cursor) + struct iovec *in_cursor, bool do_rss) { struct virtio_net_rss_config cfg = {}; ssize_t r; @@ -854,21 +854,35 @@ static int vhost_vdpa_net_load_rss(VhostVDPAState *s, const VirtIONet *n, sizeof(n->rss_data.indirections_table[0])); cfg.hash_types = cpu_to_le32(n->rss_data.hash_types); - /* - * According to VirtIO standard, "Field reserved MUST contain zeroes. - * It is defined to make the structure to match the layout of - * virtio_net_rss_config structure, defined in 5.1.6.5.7.". - * - * Therefore, we need to zero the fields in - * struct virtio_net_rss_config, which corresponds to the - * `reserved` field in struct virtio_net_hash_config. - * - * Note that all other fields are zeroed at their definitions, - * except for the `indirection_table` field, where the actual data - * is stored in the `table` variable to ensure compatibility - * with RSS case. Therefore, we need to zero the `table` variable here. - */ - table[0] = 0; + if (do_rss) { + /* + * According to VirtIO standard, "Number of entries in indirection_table + * is (indirection_table_mask + 1)". + */ + cfg.indirection_table_mask = cpu_to_le16(n->rss_data.indirections_len - + 1); + cfg.unclassified_queue = cpu_to_le16(n->rss_data.default_queue); + for (int i = 0; i < n->rss_data.indirections_len; ++i) { + table[i] = cpu_to_le16(n->rss_data.indirections_table[i]); + } + cfg.max_tx_vq = cpu_to_le16(n->curr_queue_pairs); + } else { + /* + * According to VirtIO standard, "Field reserved MUST contain zeroes. + * It is defined to make the structure to match the layout of + * virtio_net_rss_config structure, defined in 5.1.6.5.7.". + * + * Therefore, we need to zero the fields in + * struct virtio_net_rss_config, which corresponds to the + * `reserved` field in struct virtio_net_hash_config. + * + * Note that all other fields are zeroed at their definitions, + * except for the `indirection_table` field, where the actual data + * is stored in the `table` variable to ensure compatibility + * with RSS case. Therefore, we need to zero the `table` variable here. + */ + table[0] = 0; + } /* * Considering that virtio_net_handle_rss() currently does not restore @@ -899,6 +913,7 @@ static int vhost_vdpa_net_load_rss(VhostVDPAState *s, const VirtIONet *n, r = vhost_vdpa_net_load_cmd(s, out_cursor, in_cursor, VIRTIO_NET_CTRL_MQ, + do_rss ? VIRTIO_NET_CTRL_MQ_RSS_CONFIG : VIRTIO_NET_CTRL_MQ_HASH_CONFIG, data, ARRAY_SIZE(data)); if (unlikely(r < 0)) { @@ -933,13 +948,19 @@ static int vhost_vdpa_net_load_mq(VhostVDPAState *s, return r; } - if (!virtio_vdev_has_feature(&n->parent_obj, VIRTIO_NET_F_HASH_REPORT)) { - return 0; - } - - r = vhost_vdpa_net_load_rss(s, n, out_cursor, in_cursor); - if (unlikely(r < 0)) { - return r; + if (virtio_vdev_has_feature(&n->parent_obj, VIRTIO_NET_F_RSS)) { + /* load the receive-side scaling state */ + r = vhost_vdpa_net_load_rss(s, n, out_cursor, in_cursor, true); + if (unlikely(r < 0)) { + return r; + } + } else if (virtio_vdev_has_feature(&n->parent_obj, + VIRTIO_NET_F_HASH_REPORT)) { + /* load the hash calculation state */ + r = vhost_vdpa_net_load_rss(s, n, out_cursor, in_cursor, false); + if (unlikely(r < 0)) { + return r; + } } return 0; From 07eba9493d14b3ba371e64392effc384246892ea Mon Sep 17 00:00:00 2001 From: Hawkins Jiawei Date: Wed, 25 Oct 2023 09:08:06 +0800 Subject: [PATCH 684/974] vdpa: Allow VIRTIO_NET_F_RSS in SVQ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable SVQ with VIRTIO_NET_F_RSS feature. Signed-off-by: Hawkins Jiawei Message-Id: <626449eb303207de408126b3dc7c155cd72b028b.1698195059.git.yin31149@gmail.com> Acked-by: Eugenio Pérez Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- net/vhost-vdpa.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index a4cc1381fc..d0614d7954 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -122,6 +122,7 @@ static const uint64_t vdpa_svq_device_features = /* VHOST_F_LOG_ALL is exposed by SVQ */ BIT_ULL(VHOST_F_LOG_ALL) | BIT_ULL(VIRTIO_NET_F_HASH_REPORT) | + BIT_ULL(VIRTIO_NET_F_RSS) | BIT_ULL(VIRTIO_NET_F_RSC_EXT) | BIT_ULL(VIRTIO_NET_F_STANDBY) | BIT_ULL(VIRTIO_NET_F_SPEED_DUPLEX); From 7d5936791ea349d9cef856babcce28470966bfa4 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 23 Oct 2023 17:46:20 +0800 Subject: [PATCH 685/974] tests: test-smp-parse: Add the test for cores/threads per socket helpers Use the different ways to calculate cores/threads per socket, so that the new CPU topology levels won't be missed in these 2 helpes: * machine_topo_get_cores_per_socket() * machine_topo_get_threads_per_socket() Test the commit a1d027be95bc3 ("machine: Add helpers to get cores/ threads per socket"). Suggested-by: Igor Mammedov Signed-off-by: Zhao Liu Acked-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Message-Id: <20231023094635.1588282-2-zhao1.liu@linux.intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/unit/test-smp-parse.c | 67 ++++++++++++++++++++++++++++++------- 1 file changed, 54 insertions(+), 13 deletions(-) diff --git a/tests/unit/test-smp-parse.c b/tests/unit/test-smp-parse.c index fdc39a846c..24972666a7 100644 --- a/tests/unit/test-smp-parse.c +++ b/tests/unit/test-smp-parse.c @@ -394,20 +394,47 @@ static char *smp_config_to_string(const SMPConfiguration *config) config->has_maxcpus ? "true" : "false", config->maxcpus); } -static char *cpu_topology_to_string(const CpuTopology *topo) +/* Use the different calculation than machine_topo_get_threads_per_socket(). */ +static unsigned int cpu_topology_get_threads_per_socket(const CpuTopology *topo) +{ + /* Check the divisor to avoid invalid topology examples causing SIGFPE. */ + if (!topo->sockets) { + return 0; + } else { + return topo->max_cpus / topo->sockets; + } +} + +/* Use the different calculation than machine_topo_get_cores_per_socket(). */ +static unsigned int cpu_topology_get_cores_per_socket(const CpuTopology *topo) +{ + /* Check the divisor to avoid invalid topology examples causing SIGFPE. */ + if (!topo->threads) { + return 0; + } else { + return cpu_topology_get_threads_per_socket(topo) / topo->threads; + } +} + +static char *cpu_topology_to_string(const CpuTopology *topo, + unsigned int threads_per_socket, + unsigned int cores_per_socket) { return g_strdup_printf( "(CpuTopology) {\n" - " .cpus = %u,\n" - " .sockets = %u,\n" - " .dies = %u,\n" - " .clusters = %u,\n" - " .cores = %u,\n" - " .threads = %u,\n" - " .max_cpus = %u,\n" + " .cpus = %u,\n" + " .sockets = %u,\n" + " .dies = %u,\n" + " .clusters = %u,\n" + " .cores = %u,\n" + " .threads = %u,\n" + " .max_cpus = %u,\n" + " .threads_per_socket = %u,\n" + " .cores_per_socket = %u,\n" "}", topo->cpus, topo->sockets, topo->dies, topo->clusters, - topo->cores, topo->threads, topo->max_cpus); + topo->cores, topo->threads, topo->max_cpus, + threads_per_socket, cores_per_socket); } static void check_parse(MachineState *ms, const SMPConfiguration *config, @@ -415,14 +442,26 @@ static void check_parse(MachineState *ms, const SMPConfiguration *config, bool is_valid) { g_autofree char *config_str = smp_config_to_string(config); - g_autofree char *expect_topo_str = cpu_topology_to_string(expect_topo); - g_autofree char *output_topo_str = NULL; + g_autofree char *expect_topo_str = NULL, *output_topo_str = NULL; + unsigned int expect_threads_per_socket, expect_cores_per_socket; + unsigned int ms_threads_per_socket, ms_cores_per_socket; Error *err = NULL; + expect_threads_per_socket = + cpu_topology_get_threads_per_socket(expect_topo); + expect_cores_per_socket = + cpu_topology_get_cores_per_socket(expect_topo); + expect_topo_str = cpu_topology_to_string(expect_topo, + expect_threads_per_socket, + expect_cores_per_socket); + /* call the generic parser */ machine_parse_smp_config(ms, config, &err); - output_topo_str = cpu_topology_to_string(&ms->smp); + ms_threads_per_socket = machine_topo_get_threads_per_socket(ms); + ms_cores_per_socket = machine_topo_get_cores_per_socket(ms); + output_topo_str = cpu_topology_to_string(&ms->smp, ms_threads_per_socket, + ms_cores_per_socket); /* when the configuration is supposed to be valid */ if (is_valid) { @@ -433,7 +472,9 @@ static void check_parse(MachineState *ms, const SMPConfiguration *config, (ms->smp.clusters == expect_topo->clusters) && (ms->smp.cores == expect_topo->cores) && (ms->smp.threads == expect_topo->threads) && - (ms->smp.max_cpus == expect_topo->max_cpus)) { + (ms->smp.max_cpus == expect_topo->max_cpus) && + (ms_threads_per_socket == expect_threads_per_socket) && + (ms_cores_per_socket == expect_cores_per_socket)) { return; } From 6c7937ece909d3df0d2b4e94909f57388f5ed666 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 23 Oct 2023 17:46:21 +0800 Subject: [PATCH 686/974] tests: bios-tables-test: Prepare the ACPI table change for smbios type4 count test Following the guidelines in tests/qtest/bios-tables-test.c, this is step 1 - 3. List the ACPI tables that will be added to test the type 4 count. Signed-off-by: Zhao Liu Reviewed-by: Michael S. Tsirkin Message-Id: <20231023094635.1588282-3-zhao1.liu@linux.intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/q35/APIC.type4-count | 0 tests/data/acpi/q35/DSDT.type4-count | 0 tests/data/acpi/q35/FACP.type4-count | 0 tests/qtest/bios-tables-test-allowed-diff.h | 3 +++ 4 files changed, 3 insertions(+) create mode 100644 tests/data/acpi/q35/APIC.type4-count create mode 100644 tests/data/acpi/q35/DSDT.type4-count create mode 100644 tests/data/acpi/q35/FACP.type4-count diff --git a/tests/data/acpi/q35/APIC.type4-count b/tests/data/acpi/q35/APIC.type4-count new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/data/acpi/q35/DSDT.type4-count b/tests/data/acpi/q35/DSDT.type4-count new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/data/acpi/q35/FACP.type4-count b/tests/data/acpi/q35/FACP.type4-count new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..0ce6f8fc72 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,4 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/q35/APIC.type4-count", +"tests/data/acpi/q35/DSDT.type4-count", +"tests/data/acpi/q35/FACP.type4-count", From df210963a10e5ca884908addeb07aa081c5b7ad2 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 23 Oct 2023 17:46:22 +0800 Subject: [PATCH 687/974] tests: bios-tables-test: Add test for smbios type4 count This tests the commit d79a284a44bb7 ("hw/smbios: Fix smbios_smp_sockets calculation"). In smbios_get_tables() (hw/smbios/smbios.c), smbios type4 table is built for each socket, so the count of type4 tables should be equal to the number of sockets. Thus for the topology in this case, there're the following considerations: 1. The topology should include multiple sockets to ensure smbios could create type4 tables for each socket. 2. In addition to sockets, for the more general topology, we should also configure as many topology levels as possible (multiple dies, no module since x86 hasn't supported it), to ensure that smbios is able to exclude the effect of other topology levels to create the type4 tables only for sockets. 3. The original miscalculation bug also misused "smp.cpus", so it's necessary to configure "cpus" (presented threads for machine) and "maxcpus" (total threads for machine) as well to make sure that configuring unpluged CPUs in smp (cpus < maxcpus) does not affect the correctness of the count of type4 tables. Based on these considerations, select the topology as the follow: -smp cpus=100,maxcpus=120,sockets=5,dies=2,cores=4,threads=3 The expected count of type4 tables = sockets (5). Suggested-by: Igor Mammedov Signed-off-by: Zhao Liu Reviewed-by: Michael S. Tsirkin Message-Id: <20231023094635.1588282-4-zhao1.liu@linux.intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index 9f4bc15aab..cdbfb51559 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -97,6 +97,7 @@ typedef struct { uint16_t smbios_core_count2; uint8_t *required_struct_types; int required_struct_types_len; + int type4_count; QTestState *qts; } test_data; @@ -673,12 +674,21 @@ static void smbios_cpu_test(test_data *data, uint32_t addr, } } +static void smbios_type4_count_test(test_data *data, int type4_count) +{ + int expected_type4_count = data->type4_count; + + if (expected_type4_count) { + g_assert_cmpuint(type4_count, ==, expected_type4_count); + } +} + static void test_smbios_structs(test_data *data, SmbiosEntryPointType ep_type) { DECLARE_BITMAP(struct_bitmap, SMBIOS_MAX_TYPE+1) = { 0 }; SmbiosEntryPoint *ep_table = &data->smbios_ep_table; - int i = 0, len, max_len = 0; + int i = 0, len, max_len = 0, type4_count = 0; uint8_t type, prv, crt; uint64_t addr; @@ -704,6 +714,7 @@ static void test_smbios_structs(test_data *data, SmbiosEntryPointType ep_type) if (type == 4) { smbios_cpu_test(data, addr, ep_type); + type4_count++; } /* seek to end of unformatted string area of this struct ("\0\0") */ @@ -747,6 +758,8 @@ static void test_smbios_structs(test_data *data, SmbiosEntryPointType ep_type) for (i = 0; i < data->required_struct_types_len; i++) { g_assert(test_bit(data->required_struct_types[i], struct_bitmap)); } + + smbios_type4_count_test(data, type4_count); } static void test_acpi_load_tables(test_data *data) @@ -970,6 +983,22 @@ static void test_acpi_q35_tcg(void) free_test_data(&data); } +static void test_acpi_q35_tcg_type4_count(void) +{ + test_data data = { + .machine = MACHINE_Q35, + .variant = ".type4-count", + .required_struct_types = base_required_struct_types, + .required_struct_types_len = ARRAY_SIZE(base_required_struct_types), + .type4_count = 5, + }; + + test_acpi_one("-machine smbios-entry-point-type=64 " + "-smp cpus=100,maxcpus=120,sockets=5," + "dies=2,cores=4,threads=3", &data); + free_test_data(&data); +} + static void test_acpi_q35_tcg_core_count2(void) { test_data data = { @@ -2147,6 +2176,8 @@ int main(int argc, char *argv[]) if (has_kvm) { qtest_add_func("acpi/q35/kvm/xapic", test_acpi_q35_kvm_xapic); qtest_add_func("acpi/q35/kvm/dmar", test_acpi_q35_kvm_dmar); + qtest_add_func("acpi/q35/type4-count", + test_acpi_q35_tcg_type4_count); qtest_add_func("acpi/q35/core-count2", test_acpi_q35_tcg_core_count2); } From c1cd1d360db857d6f76071e8de6af97cdab2bdfe Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 23 Oct 2023 17:46:23 +0800 Subject: [PATCH 688/974] tests: bios-tables-test: Add ACPI table binaries for smbios type4 count test Following the guidelines in tests/qtest/bios-tables-test.c, this is step 5 and 6. Changes in the tables: FACP: +/* + * Intel ACPI Component Architecture + * AML/ASL+ Disassembler version 20200925 (64-bit version) + * Copyright (c) 2000 - 2020 Intel Corporation + * + * Disassembly of /tmp/aml-W37791, Wed Aug 23 10:36:32 2023 + * + * ACPI Data Table [FACP] + * + * Format: [HexOffset DecimalOffset ByteLength] FieldName : FieldValue + */ + +[000h 0000 4] Signature : "FACP" [Fixed ACPI Description Table (FADT)] +[004h 0004 4] Table Length : 000000F4 +[008h 0008 1] Revision : 03 +[009h 0009 1] Checksum : B3 +[00Ah 0010 6] Oem ID : "BOCHS " +[010h 0016 8] Oem Table ID : "BXPC " +[018h 0024 4] Oem Revision : 00000001 +[01Ch 0028 4] Asl Compiler ID : "BXPC" +[020h 0032 4] Asl Compiler Revision : 00000001 + +[024h 0036 4] FACS Address : 00000000 +[028h 0040 4] DSDT Address : 00000000 +[02Ch 0044 1] Model : 01 +[02Dh 0045 1] PM Profile : 00 [Unspecified] +[02Eh 0046 2] SCI Interrupt : 0009 +[030h 0048 4] SMI Command Port : 000000B2 +[034h 0052 1] ACPI Enable Value : 02 +[035h 0053 1] ACPI Disable Value : 03 +[036h 0054 1] S4BIOS Command : 00 +[037h 0055 1] P-State Control : 00 +[038h 0056 4] PM1A Event Block Address : 00000600 +[03Ch 0060 4] PM1B Event Block Address : 00000000 +[040h 0064 4] PM1A Control Block Address : 00000604 +[044h 0068 4] PM1B Control Block Address : 00000000 +[048h 0072 4] PM2 Control Block Address : 00000000 +[04Ch 0076 4] PM Timer Block Address : 00000608 +[050h 0080 4] GPE0 Block Address : 00000620 +[054h 0084 4] GPE1 Block Address : 00000000 +[058h 0088 1] PM1 Event Block Length : 04 +[059h 0089 1] PM1 Control Block Length : 02 +[05Ah 0090 1] PM2 Control Block Length : 00 +[05Bh 0091 1] PM Timer Block Length : 04 +[05Ch 0092 1] GPE0 Block Length : 10 +[05Dh 0093 1] GPE1 Block Length : 00 +[05Eh 0094 1] GPE1 Base Offset : 00 +[05Fh 0095 1] _CST Support : 00 +[060h 0096 2] C2 Latency : 0FFF +[062h 0098 2] C3 Latency : 0FFF +[064h 0100 2] CPU Cache Size : 0000 +[066h 0102 2] Cache Flush Stride : 0000 +[068h 0104 1] Duty Cycle Offset : 00 +[069h 0105 1] Duty Cycle Width : 00 +[06Ah 0106 1] RTC Day Alarm Index : 00 +[06Bh 0107 1] RTC Month Alarm Index : 00 +[06Ch 0108 1] RTC Century Index : 32 +[06Dh 0109 2] Boot Flags (decoded below) : 0002 + Legacy Devices Supported (V2) : 0 + 8042 Present on ports 60/64 (V2) : 1 + VGA Not Present (V4) : 0 + MSI Not Supported (V4) : 0 + PCIe ASPM Not Supported (V4) : 0 + CMOS RTC Not Present (V5) : 0 +[06Fh 0111 1] Reserved : 00 +[070h 0112 4] Flags (decoded below) : 000484A5 + WBINVD instruction is operational (V1) : 1 + WBINVD flushes all caches (V1) : 0 + All CPUs support C1 (V1) : 1 + C2 works on MP system (V1) : 0 + Control Method Power Button (V1) : 0 + Control Method Sleep Button (V1) : 1 + RTC wake not in fixed reg space (V1) : 0 + RTC can wake system from S4 (V1) : 1 + 32-bit PM Timer (V1) : 0 + Docking Supported (V1) : 0 + Reset Register Supported (V2) : 1 + Sealed Case (V3) : 0 + Headless - No Video (V3) : 0 + Use native instr after SLP_TYPx (V3) : 0 + PCIEXP_WAK Bits Supported (V4) : 0 + Use Platform Timer (V4) : 1 + RTC_STS valid on S4 wake (V4) : 0 + Remote Power-on capable (V4) : 0 + Use APIC Cluster Model (V4) : 1 + Use APIC Physical Destination Mode (V4) : 0 + Hardware Reduced (V5) : 0 + Low Power S0 Idle (V5) : 0 + +[074h 0116 12] Reset Register : [Generic Address Structure] +[074h 0116 1] Space ID : 01 [SystemIO] +[075h 0117 1] Bit Width : 08 +[076h 0118 1] Bit Offset : 00 +[077h 0119 1] Encoded Access Width : 00 [Undefined/Legacy] +[078h 0120 8] Address : 0000000000000CF9 + +[080h 0128 1] Value to cause reset : 0F +[081h 0129 2] ARM Flags (decoded below) : 0000 + PSCI Compliant : 0 + Must use HVC for PSCI : 0 + +[083h 0131 1] FADT Minor Revision : 00 +[084h 0132 8] FACS Address : 0000000000000000 +[08Ch 0140 8] DSDT Address : 0000000000000000 +[094h 0148 12] PM1A Event Block : [Generic Address Structure] +[094h 0148 1] Space ID : 01 [SystemIO] +[095h 0149 1] Bit Width : 20 +[096h 0150 1] Bit Offset : 00 +[097h 0151 1] Encoded Access Width : 00 [Undefined/Legacy] +[098h 0152 8] Address : 0000000000000600 + +[0A0h 0160 12] PM1B Event Block : [Generic Address Structure] +[0A0h 0160 1] Space ID : 00 [SystemMemory] +[0A1h 0161 1] Bit Width : 00 +[0A2h 0162 1] Bit Offset : 00 +[0A3h 0163 1] Encoded Access Width : 00 [Undefined/Legacy] +[0A4h 0164 8] Address : 0000000000000000 + +[0ACh 0172 12] PM1A Control Block : [Generic Address Structure] +[0ACh 0172 1] Space ID : 01 [SystemIO] +[0ADh 0173 1] Bit Width : 10 +[0AEh 0174 1] Bit Offset : 00 +[0AFh 0175 1] Encoded Access Width : 00 [Undefined/Legacy] +[0B0h 0176 8] Address : 0000000000000604 + +[0B8h 0184 12] PM1B Control Block : [Generic Address Structure] +[0B8h 0184 1] Space ID : 00 [SystemMemory] +[0B9h 0185 1] Bit Width : 00 +[0BAh 0186 1] Bit Offset : 00 +[0BBh 0187 1] Encoded Access Width : 00 [Undefined/Legacy] +[0BCh 0188 8] Address : 0000000000000000 + +[0C4h 0196 12] PM2 Control Block : [Generic Address Structure] +[0C4h 0196 1] Space ID : 00 [SystemMemory] +[0C5h 0197 1] Bit Width : 00 +[0C6h 0198 1] Bit Offset : 00 +[0C7h 0199 1] Encoded Access Width : 00 [Undefined/Legacy] +[0C8h 0200 8] Address : 0000000000000000 + +[0D0h 0208 12] PM Timer Block : [Generic Address Structure] +[0D0h 0208 1] Space ID : 01 [SystemIO] +[0D1h 0209 1] Bit Width : 20 +[0D2h 0210 1] Bit Offset : 00 +[0D3h 0211 1] Encoded Access Width : 00 [Undefined/Legacy] +[0D4h 0212 8] Address : 0000000000000608 + +[0DCh 0220 12] GPE0 Block : [Generic Address Structure] +[0DCh 0220 1] Space ID : 01 [SystemIO] +[0DDh 0221 1] Bit Width : 80 +[0DEh 0222 1] Bit Offset : 00 +[0DFh 0223 1] Encoded Access Width : 00 [Undefined/Legacy] +[0E0h 0224 8] Address : 0000000000000620 + +[0E8h 0232 12] GPE1 Block : [Generic Address Structure] +[0E8h 0232 1] Space ID : 00 [SystemMemory] +[0E9h 0233 1] Bit Width : 00 +[0EAh 0234 1] Bit Offset : 00 +[0EBh 0235 1] Encoded Access Width : 00 [Undefined/Legacy] +[0ECh 0236 8] Address : 0000000000000000 + ... APIC: +/* + * Intel ACPI Component Architecture + * AML/ASL+ Disassembler version 20200925 (64-bit version) + * Copyright (c) 2000 - 2020 Intel Corporation + * + * Disassembly of /tmp/aml-687791, Wed Aug 23 10:36:32 2023 + * + * ACPI Data Table [APIC] + * + * Format: [HexOffset DecimalOffset ByteLength] FieldName : FieldValue + */ + +[000h 0000 4] Signature : "APIC" [Multiple APIC Description Table (MADT)] +[004h 0004 4] Table Length : 00000430 +[008h 0008 1] Revision : 03 +[009h 0009 1] Checksum : C5 +[00Ah 0010 6] Oem ID : "BOCHS " +[010h 0016 8] Oem Table ID : "BXPC " +[018h 0024 4] Oem Revision : 00000001 +[01Ch 0028 4] Asl Compiler ID : "BXPC" +[020h 0032 4] Asl Compiler Revision : 00000001 + +[024h 0036 4] Local Apic Address : FEE00000 +[028h 0040 4] Flags (decoded below) : 00000001 + PC-AT Compatibility : 1 + +[02Ch 0044 1] Subtable Type : 00 [Processor Local APIC] +[02Dh 0045 1] Length : 08 +[02Eh 0046 1] Processor ID : 00 +[02Fh 0047 1] Local Apic ID : 00 +[030h 0048 4] Flags (decoded below) : 00000001 + Processor Enabled : 1 + Runtime Online Capable : 0 + +[034h 0052 1] Subtable Type : 00 [Processor Local APIC] +[035h 0053 1] Length : 08 +[036h 0054 1] Processor ID : 01 +[037h 0055 1] Local Apic ID : 01 +[038h 0056 4] Flags (decoded below) : 00000001 + Processor Enabled : 1 + Runtime Online Capable : 0 [snip] +[3E4h 0996 1] Subtable Type : 00 [Processor Local APIC] +[3E5h 0997 1] Length : 08 +[3E6h 0998 1] Processor ID : 77 +[3E7h 0999 1] Local Apic ID : 9E +[3E8h 1000 4] Flags (decoded below) : 00000000 + Processor Enabled : 0 + Runtime Online Capable : 0 + +[3ECh 1004 1] Subtable Type : 01 [I/O APIC] +[3EDh 1005 1] Length : 0C +[3EEh 1006 1] I/O Apic ID : 00 +[3EFh 1007 1] Reserved : 00 +[3F0h 1008 4] Address : FEC00000 +[3F4h 1012 4] Interrupt : 00000000 + +[3F8h 1016 1] Subtable Type : 02 [Interrupt Source Override] +[3F9h 1017 1] Length : 0A +[3FAh 1018 1] Bus : 00 +[3FBh 1019 1] Source : 00 +[3FCh 1020 4] Interrupt : 00000002 +[400h 1024 2] Flags (decoded below) : 0000 + Polarity : 0 + Trigger Mode : 0 + +[402h 1026 1] Subtable Type : 02 [Interrupt Source Override] +[403h 1027 1] Length : 0A +[404h 1028 1] Bus : 00 +[405h 1029 1] Source : 05 +[406h 1030 4] Interrupt : 00000005 +[40Ah 1034 2] Flags (decoded below) : 000D + Polarity : 1 + Trigger Mode : 3 + +[40Ch 1036 1] Subtable Type : 02 [Interrupt Source Override] +[40Dh 1037 1] Length : 0A +[40Eh 1038 1] Bus : 00 +[40Fh 1039 1] Source : 09 +[410h 1040 4] Interrupt : 00000009 +[414h 1044 2] Flags (decoded below) : 000D + Polarity : 1 + Trigger Mode : 3 + +[416h 1046 1] Subtable Type : 02 [Interrupt Source Override] +[417h 1047 1] Length : 0A +[418h 1048 1] Bus : 00 +[419h 1049 1] Source : 0A +[41Ah 1050 4] Interrupt : 0000000A +[41Eh 1054 2] Flags (decoded below) : 000D + Polarity : 1 + Trigger Mode : 3 + +[420h 1056 1] Subtable Type : 02 [Interrupt Source Override] +[421h 1057 1] Length : 0A +[422h 1058 1] Bus : 00 +[423h 1059 1] Source : 0B +[424h 1060 4] Interrupt : 0000000B +[428h 1064 2] Flags (decoded below) : 000D + Polarity : 1 + Trigger Mode : 3 + +[42Ah 1066 1] Subtable Type : 04 [Local APIC NMI] +[42Bh 1067 1] Length : 06 +[42Ch 1068 1] Processor ID : FF +[42Dh 1069 2] Flags (decoded below) : 0000 + Polarity : 0 + Trigger Mode : 0 +[42Fh 1071 1] Interrupt Input LINT : 01 + ... DSDT: +/* + * Intel ACPI Component Architecture + * AML/ASL+ Disassembler version 20200925 (64-bit version) + * Copyright (c) 2000 - 2020 Intel Corporation + * + * Disassembling to symbolic ASL+ operators + * + * Disassembly of /tmp/aml-8G8791, Wed Aug 23 10:36:32 2023 + * + * Original Table Header: + * Signature "DSDT" + * Length 0x0000489D (18589) + * Revision 0x01 **** 32-bit table (V1), no 64-bit math support + * Checksum 0xDB + * OEM ID "BOCHS " + * OEM Table ID "BXPC " + * OEM Revision 0x00000001 (1) + * Compiler ID "BXPC" + * Compiler Version 0x00000001 (1) + */ +DefinitionBlock ("", "DSDT", 1, "BOCHS ", "BXPC ", 0x00000001) +{ + Scope (\) + { + OperationRegion (DBG, SystemIO, 0x0402, One) + Field (DBG, ByteAcc, NoLock, Preserve) + { + DBGB, 8 + } + + Method (DBUG, 1, NotSerialized) + { + ToHexString (Arg0, Local0) + ToBuffer (Local0, Local0) + Local1 = (SizeOf (Local0) - One) + Local2 = Zero + While ((Local2 < Local1)) + { + DBGB = DerefOf (Local0 [Local2]) + Local2++ + } + + DBGB = 0x0A + } + } + [snip] + + Processor (C000, 0x00, 0x00000000, 0x00) + { + Method (_STA, 0, Serialized) // _STA: Status + { + Return (CSTA (Zero)) + } + + Name (_MAT, Buffer (0x08) // _MAT: Multiple APIC Table Entry + { + 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 // ........ + }) + Method (_OST, 3, Serialized) // _OST: OSPM Status Indication + { + COST (Zero, Arg0, Arg1, Arg2) + } + } + + Processor (C001, 0x01, 0x00000000, 0x00) + { + Method (_STA, 0, Serialized) // _STA: Status + { + Return (CSTA (One)) + } + + Name (_MAT, Buffer (0x08) // _MAT: Multiple APIC Table Entry + { + 0x00, 0x08, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00 // ........ + }) + Method (_EJ0, 1, NotSerialized) // _EJx: Eject Device, x=0-9 + { + CEJ0 (One) + } + + Method (_OST, 3, Serialized) // _OST: OSPM Status Indication + { + COST (One, Arg0, Arg1, Arg2) + } + } [snip] + Processor (C077, 0x77, 0x00000000, 0x00) + { + Method (_STA, 0, Serialized) // _STA: Status + { + Return (CSTA (0x77)) + } + + Name (_MAT, Buffer (0x08) // _MAT: Multiple APIC Table Entry + { + 0x00, 0x08, 0x77, 0x9E, 0x01, 0x00, 0x00, 0x00 // ..w..... + }) + Method (_EJ0, 1, NotSerialized) // _EJx: Eject Device, x=0-9 + { + CEJ0 (0x77) + } + + Method (_OST, 3, Serialized) // _OST: OSPM Status Indication + { + COST (0x77, Arg0, Arg1, Arg2) + } + } + } + } + ... Signed-off-by: Zhao Liu Reviewed-by: Michael S. Tsirkin Message-Id: <20231023094635.1588282-5-zhao1.liu@linux.intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/q35/APIC.type4-count | Bin 0 -> 1072 bytes tests/data/acpi/q35/DSDT.type4-count | Bin 0 -> 18589 bytes tests/data/acpi/q35/FACP.type4-count | Bin 0 -> 244 bytes tests/qtest/bios-tables-test-allowed-diff.h | 3 --- 4 files changed, 3 deletions(-) diff --git a/tests/data/acpi/q35/APIC.type4-count b/tests/data/acpi/q35/APIC.type4-count index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ab60a6ef065d8ce53ae93d311d3777d2d4afb9f6 100644 GIT binary patch literal 1072 zcmXxjX)mKu7{>9_YU}A{8~a-OZpzsAwwBgf>(n~-!C>rL5ClOG1VQjwyzxDJ5_Ev|p!(#Wi9Ts`1gb+$r6yp8Et0V-fRH#;?j|Meb z)apP55|BH3=GAfaQqR0!I2mejiE6Z7K`EDe+elf zo_%BjMkQf%GRCA}Y#PR;qa_35voIkW6LT=hipjZ{l834Jm{x%4g_u!{nI)K2irM9u zQ-MD#(OQkUHq5i*uNus+#ezC4bYhVUiyN?{2}_%?tOd*6SkZ=+?O4@`)m>=oM!N?c zJy_F=wf$H(fc1mu975MHHjH577&eV#^8~g`V(S#Tr?G7Y+h?(34m;8of!Q`;?#f0%hq@0RR91 literal 0 HcmV?d00001 diff --git a/tests/data/acpi/q35/DSDT.type4-count b/tests/data/acpi/q35/DSDT.type4-count index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..edc23198cdb47a981bcbc82bc8e392b815abb554 100644 GIT binary patch literal 18589 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 literal 0 HcmV?d00001 diff --git a/tests/data/acpi/q35/FACP.type4-count b/tests/data/acpi/q35/FACP.type4-count index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..31fa5dd19c213034eef4eeefa6a04e61dadd8a2a 100644 GIT binary patch literal 244 zcmZ>BbPo8!z`($~*~#D8BUr&HBEVSz2pEB4AU24G0Y(N+hD|^Y6El!tgNU*~X%LSC z$X0-fGcm9T0LA|E|L2FOWMD92VqjR>!otAF!NBm72Obk1 YBHITON2VDSAnpK(F*YFF1LDH~0O^Si0RR91 literal 0 HcmV?d00001 diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index 0ce6f8fc72..dfb8523c8b 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1,4 +1 @@ /* List of comma-separated changed AML files to ignore */ -"tests/data/acpi/q35/APIC.type4-count", -"tests/data/acpi/q35/DSDT.type4-count", -"tests/data/acpi/q35/FACP.type4-count", From 623d26ad9affaeca03633880623961434e60e77b Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 23 Oct 2023 17:46:24 +0800 Subject: [PATCH 689/974] tests: bios-tables-test: Prepare the ACPI table change for smbios type4 core count test Following the guidelines in tests/qtest/bios-tables-test.c, this is step 1 - 3. List the ACPI tables that will be added to test the type 4 core count field. Signed-off-by: Zhao Liu Reviewed-by: Michael S. Tsirkin Message-Id: <20231023094635.1588282-6-zhao1.liu@linux.intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/q35/APIC.core-count | 0 tests/data/acpi/q35/DSDT.core-count | 0 tests/data/acpi/q35/FACP.core-count | 0 tests/qtest/bios-tables-test-allowed-diff.h | 3 +++ 4 files changed, 3 insertions(+) create mode 100644 tests/data/acpi/q35/APIC.core-count create mode 100644 tests/data/acpi/q35/DSDT.core-count create mode 100644 tests/data/acpi/q35/FACP.core-count diff --git a/tests/data/acpi/q35/APIC.core-count b/tests/data/acpi/q35/APIC.core-count new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/data/acpi/q35/DSDT.core-count b/tests/data/acpi/q35/DSDT.core-count new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/data/acpi/q35/FACP.core-count b/tests/data/acpi/q35/FACP.core-count new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..b9bc196130 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,4 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/q35/APIC.core-count", +"tests/data/acpi/q35/DSDT.core-count", +"tests/data/acpi/q35/FACP.core-count", From 148a8a1d5fdbdb0ba2a9883c6182e3135cb417b2 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 23 Oct 2023 17:46:25 +0800 Subject: [PATCH 690/974] tests: bios-tables-test: Add test for smbios type4 core count This tests the commit 196ea60a734c3 ("hw/smbios: Fix core count in type4"). In smbios_build_type_4_table() (hw/smbios/smbios.c), if the number of cores in the socket is not more than 255, then smbios type4 table encodes cores per socket into the core count field. So for the topology in this case, there're the following considerations: 1. cores per socket should be not more than 255 to ensure we could cover the core count field. 2. The original bug was that cores per socket was miscalculated, so now we should include as many topology levels as possible (mutiple sockets & dies, no module since x86 hasn't supported it) to cover more general topology scenarios, to ensure that the cores per socket encoded in the core count field is correct. Based on these considerations, select the topology with multiple sockets and dies: -smp 54,sockets=2,dies=3,cores=3,threads=3 The expected core count = cores per socket = cores (3) * dies (3) = 9. Suggested-by: Igor Mammedov Signed-off-by: Zhao Liu Reviewed-by: Michael S. Tsirkin Message-Id: <20231023094635.1588282-7-zhao1.liu@linux.intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index cdbfb51559..c20f6f73d0 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -999,6 +999,23 @@ static void test_acpi_q35_tcg_type4_count(void) free_test_data(&data); } +static void test_acpi_q35_tcg_core_count(void) +{ + test_data data = { + .machine = MACHINE_Q35, + .variant = ".core-count", + .required_struct_types = base_required_struct_types, + .required_struct_types_len = ARRAY_SIZE(base_required_struct_types), + .smbios_core_count = 9, + .smbios_core_count2 = 9, + }; + + test_acpi_one("-machine smbios-entry-point-type=64 " + "-smp 54,sockets=2,dies=3,cores=3,threads=3", + &data); + free_test_data(&data); +} + static void test_acpi_q35_tcg_core_count2(void) { test_data data = { @@ -2178,6 +2195,8 @@ int main(int argc, char *argv[]) qtest_add_func("acpi/q35/kvm/dmar", test_acpi_q35_kvm_dmar); qtest_add_func("acpi/q35/type4-count", test_acpi_q35_tcg_type4_count); + qtest_add_func("acpi/q35/core-count", + test_acpi_q35_tcg_core_count); qtest_add_func("acpi/q35/core-count2", test_acpi_q35_tcg_core_count2); } From 61ace1d77280b26f0d6ed32b27a1f532f85af9f5 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 23 Oct 2023 17:46:26 +0800 Subject: [PATCH 691/974] tests: bios-tables-test: Add ACPI table binaries for smbios type4 core count test Following the guidelines in tests/qtest/bios-tables-test.c, this is step 5 and 6. Changes in the tables: FACP: +/* + * Intel ACPI Component Architecture + * AML/ASL+ Disassembler version 20200925 (64-bit version) + * Copyright (c) 2000 - 2020 Intel Corporation + * + * Disassembly of /tmp/aml-Y6WW91, Wed Aug 23 15:43:43 2023 + * + * ACPI Data Table [FACP] + * + * Format: [HexOffset DecimalOffset ByteLength] FieldName : FieldValue + */ + +[000h 0000 4] Signature : "FACP" [Fixed ACPI Description Table (FADT)] +[004h 0004 4] Table Length : 000000F4 +[008h 0008 1] Revision : 03 +[009h 0009 1] Checksum : B3 +[00Ah 0010 6] Oem ID : "BOCHS " +[010h 0016 8] Oem Table ID : "BXPC " +[018h 0024 4] Oem Revision : 00000001 +[01Ch 0028 4] Asl Compiler ID : "BXPC" +[020h 0032 4] Asl Compiler Revision : 00000001 + +[024h 0036 4] FACS Address : 00000000 +[028h 0040 4] DSDT Address : 00000000 +[02Ch 0044 1] Model : 01 +[02Dh 0045 1] PM Profile : 00 [Unspecified] +[02Eh 0046 2] SCI Interrupt : 0009 +[030h 0048 4] SMI Command Port : 000000B2 +[034h 0052 1] ACPI Enable Value : 02 +[035h 0053 1] ACPI Disable Value : 03 +[036h 0054 1] S4BIOS Command : 00 +[037h 0055 1] P-State Control : 00 +[038h 0056 4] PM1A Event Block Address : 00000600 +[03Ch 0060 4] PM1B Event Block Address : 00000000 +[040h 0064 4] PM1A Control Block Address : 00000604 +[044h 0068 4] PM1B Control Block Address : 00000000 +[048h 0072 4] PM2 Control Block Address : 00000000 +[04Ch 0076 4] PM Timer Block Address : 00000608 +[050h 0080 4] GPE0 Block Address : 00000620 +[054h 0084 4] GPE1 Block Address : 00000000 +[058h 0088 1] PM1 Event Block Length : 04 +[059h 0089 1] PM1 Control Block Length : 02 +[05Ah 0090 1] PM2 Control Block Length : 00 +[05Bh 0091 1] PM Timer Block Length : 04 +[05Ch 0092 1] GPE0 Block Length : 10 +[05Dh 0093 1] GPE1 Block Length : 00 +[05Eh 0094 1] GPE1 Base Offset : 00 +[05Fh 0095 1] _CST Support : 00 +[060h 0096 2] C2 Latency : 0FFF +[062h 0098 2] C3 Latency : 0FFF +[064h 0100 2] CPU Cache Size : 0000 +[066h 0102 2] Cache Flush Stride : 0000 +[068h 0104 1] Duty Cycle Offset : 00 +[069h 0105 1] Duty Cycle Width : 00 +[06Ah 0106 1] RTC Day Alarm Index : 00 +[06Bh 0107 1] RTC Month Alarm Index : 00 +[06Ch 0108 1] RTC Century Index : 32 +[06Dh 0109 2] Boot Flags (decoded below) : 0002 + Legacy Devices Supported (V2) : 0 + 8042 Present on ports 60/64 (V2) : 1 + VGA Not Present (V4) : 0 + MSI Not Supported (V4) : 0 + PCIe ASPM Not Supported (V4) : 0 + CMOS RTC Not Present (V5) : 0 +[06Fh 0111 1] Reserved : 00 +[070h 0112 4] Flags (decoded below) : 000484A5 + WBINVD instruction is operational (V1) : 1 + WBINVD flushes all caches (V1) : 0 + All CPUs support C1 (V1) : 1 + C2 works on MP system (V1) : 0 + Control Method Power Button (V1) : 0 + Control Method Sleep Button (V1) : 1 + RTC wake not in fixed reg space (V1) : 0 + RTC can wake system from S4 (V1) : 1 + 32-bit PM Timer (V1) : 0 + Docking Supported (V1) : 0 + Reset Register Supported (V2) : 1 + Sealed Case (V3) : 0 + Headless - No Video (V3) : 0 + Use native instr after SLP_TYPx (V3) : 0 + PCIEXP_WAK Bits Supported (V4) : 0 + Use Platform Timer (V4) : 1 + RTC_STS valid on S4 wake (V4) : 0 + Remote Power-on capable (V4) : 0 + Use APIC Cluster Model (V4) : 1 + Use APIC Physical Destination Mode (V4) : 0 + Hardware Reduced (V5) : 0 + Low Power S0 Idle (V5) : 0 + +[074h 0116 12] Reset Register : [Generic Address Structure] +[074h 0116 1] Space ID : 01 [SystemIO] +[075h 0117 1] Bit Width : 08 +[076h 0118 1] Bit Offset : 00 +[077h 0119 1] Encoded Access Width : 00 [Undefined/Legacy] +[078h 0120 8] Address : 0000000000000CF9 + +[080h 0128 1] Value to cause reset : 0F +[081h 0129 2] ARM Flags (decoded below) : 0000 + PSCI Compliant : 0 + Must use HVC for PSCI : 0 + +[083h 0131 1] FADT Minor Revision : 00 +[084h 0132 8] FACS Address : 0000000000000000 +[08Ch 0140 8] DSDT Address : 0000000000000000 +[094h 0148 12] PM1A Event Block : [Generic Address Structure] +[094h 0148 1] Space ID : 01 [SystemIO] +[095h 0149 1] Bit Width : 20 +[096h 0150 1] Bit Offset : 00 +[097h 0151 1] Encoded Access Width : 00 [Undefined/Legacy] +[098h 0152 8] Address : 0000000000000600 + +[0A0h 0160 12] PM1B Event Block : [Generic Address Structure] +[0A0h 0160 1] Space ID : 00 [SystemMemory] +[0A1h 0161 1] Bit Width : 00 +[0A2h 0162 1] Bit Offset : 00 +[0A3h 0163 1] Encoded Access Width : 00 [Undefined/Legacy] +[0A4h 0164 8] Address : 0000000000000000 + +[0ACh 0172 12] PM1A Control Block : [Generic Address Structure] +[0ACh 0172 1] Space ID : 01 [SystemIO] +[0ADh 0173 1] Bit Width : 10 +[0AEh 0174 1] Bit Offset : 00 +[0AFh 0175 1] Encoded Access Width : 00 [Undefined/Legacy] +[0B0h 0176 8] Address : 0000000000000604 + +[0B8h 0184 12] PM1B Control Block : [Generic Address Structure] +[0B8h 0184 1] Space ID : 00 [SystemMemory] +[0B9h 0185 1] Bit Width : 00 +[0BAh 0186 1] Bit Offset : 00 +[0BBh 0187 1] Encoded Access Width : 00 [Undefined/Legacy] +[0BCh 0188 8] Address : 0000000000000000 + +[0C4h 0196 12] PM2 Control Block : [Generic Address Structure] +[0C4h 0196 1] Space ID : 00 [SystemMemory] +[0C5h 0197 1] Bit Width : 00 +[0C6h 0198 1] Bit Offset : 00 +[0C7h 0199 1] Encoded Access Width : 00 [Undefined/Legacy] +[0C8h 0200 8] Address : 0000000000000000 + +[0D0h 0208 12] PM Timer Block : [Generic Address Structure] +[0D0h 0208 1] Space ID : 01 [SystemIO] +[0D1h 0209 1] Bit Width : 20 +[0D2h 0210 1] Bit Offset : 00 +[0D3h 0211 1] Encoded Access Width : 00 [Undefined/Legacy] +[0D4h 0212 8] Address : 0000000000000608 + +[0DCh 0220 12] GPE0 Block : [Generic Address Structure] +[0DCh 0220 1] Space ID : 01 [SystemIO] +[0DDh 0221 1] Bit Width : 80 +[0DEh 0222 1] Bit Offset : 00 +[0DFh 0223 1] Encoded Access Width : 00 [Undefined/Legacy] +[0E0h 0224 8] Address : 0000000000000620 + +[0E8h 0232 12] GPE1 Block : [Generic Address Structure] +[0E8h 0232 1] Space ID : 00 [SystemMemory] +[0E9h 0233 1] Bit Width : 00 +[0EAh 0234 1] Bit Offset : 00 +[0EBh 0235 1] Encoded Access Width : 00 [Undefined/Legacy] +[0ECh 0236 8] Address : 0000000000000000 ... APIC: +/* + * Intel ACPI Component Architecture + * AML/ASL+ Disassembler version 20200925 (64-bit version) + * Copyright (c) 2000 - 2020 Intel Corporation + * + * Disassembly of /tmp/aml-FFXW91, Wed Aug 23 15:43:43 2023 + * + * ACPI Data Table [APIC] + * + * Format: [HexOffset DecimalOffset ByteLength] FieldName : FieldValue + */ + +[000h 0000 4] Signature : "APIC" [Multiple APIC Description Table (MADT)] +[004h 0004 4] Table Length : 00000220 +[008h 0008 1] Revision : 03 +[009h 0009 1] Checksum : 3C +[00Ah 0010 6] Oem ID : "BOCHS " +[010h 0016 8] Oem Table ID : "BXPC " +[018h 0024 4] Oem Revision : 00000001 +[01Ch 0028 4] Asl Compiler ID : "BXPC" +[020h 0032 4] Asl Compiler Revision : 00000001 + +[024h 0036 4] Local Apic Address : FEE00000 +[028h 0040 4] Flags (decoded below) : 00000001 + PC-AT Compatibility : 1 + +[02Ch 0044 1] Subtable Type : 00 [Processor Local APIC] +[02Dh 0045 1] Length : 08 +[02Eh 0046 1] Processor ID : 00 +[02Fh 0047 1] Local Apic ID : 00 +[030h 0048 4] Flags (decoded below) : 00000001 + Processor Enabled : 1 + Runtime Online Capable : 0 [snip] +[1D4h 0468 1] Subtable Type : 00 [Processor Local APIC] +[1D5h 0469 1] Length : 08 +[1D6h 0470 1] Processor ID : 35 +[1D7h 0471 1] Local Apic ID : 6A +[1D8h 0472 4] Flags (decoded below) : 00000001 + Processor Enabled : 1 + Runtime Online Capable : 0 + +[1DCh 0476 1] Subtable Type : 01 [I/O APIC] +[1DDh 0477 1] Length : 0C +[1DEh 0478 1] I/O Apic ID : 00 +[1DFh 0479 1] Reserved : 00 +[1E0h 0480 4] Address : FEC00000 +[1E4h 0484 4] Interrupt : 00000000 + +[1E8h 0488 1] Subtable Type : 02 [Interrupt Source Override] +[1E9h 0489 1] Length : 0A +[1EAh 0490 1] Bus : 00 +[1EBh 0491 1] Source : 00 +[1ECh 0492 4] Interrupt : 00000002 +[1F0h 0496 2] Flags (decoded below) : 0000 + Polarity : 0 + Trigger Mode : 0 + +[1F2h 0498 1] Subtable Type : 02 [Interrupt Source Override] +[1F3h 0499 1] Length : 0A +[1F4h 0500 1] Bus : 00 +[1F5h 0501 1] Source : 05 +[1F6h 0502 4] Interrupt : 00000005 +[1FAh 0506 2] Flags (decoded below) : 000D + Polarity : 1 + Trigger Mode : 3 + +[1FCh 0508 1] Subtable Type : 02 [Interrupt Source Override] +[1FDh 0509 1] Length : 0A +[1FEh 0510 1] Bus : 00 +[1FFh 0511 1] Source : 09 +[200h 0512 4] Interrupt : 00000009 +[204h 0516 2] Flags (decoded below) : 000D + Polarity : 1 + Trigger Mode : 3 + +[206h 0518 1] Subtable Type : 02 [Interrupt Source Override] +[207h 0519 1] Length : 0A +[208h 0520 1] Bus : 00 +[209h 0521 1] Source : 0A +[20Ah 0522 4] Interrupt : 0000000A +[20Eh 0526 2] Flags (decoded below) : 000D + Polarity : 1 + Trigger Mode : 3 + +[210h 0528 1] Subtable Type : 02 [Interrupt Source Override] +[211h 0529 1] Length : 0A +[212h 0530 1] Bus : 00 +[213h 0531 1] Source : 0B +[214h 0532 4] Interrupt : 0000000B +[218h 0536 2] Flags (decoded below) : 000D + Polarity : 1 + Trigger Mode : 3 + +[21Ah 0538 1] Subtable Type : 04 [Local APIC NMI] +[21Bh 0539 1] Length : 06 +[21Ch 0540 1] Processor ID : FF +[21Dh 0541 2] Flags (decoded below) : 0000 + Polarity : 0 + Trigger Mode : 0 +[21Fh 0543 1] Interrupt Input LINT : 01 ... DSDT: +/* + * Intel ACPI Component Architecture + * AML/ASL+ Disassembler version 20200925 (64-bit version) + * Copyright (c) 2000 - 2020 Intel Corporation + * + * Disassembling to symbolic ASL+ operators + * + * Disassembly of /tmp/aml-9ZXW91, Wed Aug 23 15:43:43 2023 + * + * Original Table Header: + * Signature "DSDT" + * Length 0x00003271 (12913) + * Revision 0x01 **** 32-bit table (V1), no 64-bit math support + * Checksum 0xAF + * OEM ID "BOCHS " + * OEM Table ID "BXPC " + * OEM Revision 0x00000001 (1) + * Compiler ID "BXPC" + * Compiler Version 0x00000001 (1) + */ +DefinitionBlock ("", "DSDT", 1, "BOCHS ", "BXPC ", 0x00000001) +{ + Scope (\) + { + OperationRegion (DBG, SystemIO, 0x0402, One) + Field (DBG, ByteAcc, NoLock, Preserve) + { + DBGB, 8 + } + + Method (DBUG, 1, NotSerialized) + { + ToHexString (Arg0, Local0) + ToBuffer (Local0, Local0) + Local1 = (SizeOf (Local0) - One) + Local2 = Zero + While ((Local2 < Local1)) + { + DBGB = DerefOf (Local0 [Local2]) + Local2++ + } + + DBGB = 0x0A + } + } [snip] + Device (\_SB.CPUS) + { + Name (_HID, "ACPI0010" /* Processor Container Device */) // _HID: Hardware ID + Name (_CID, EisaId ("PNP0A05") /* Generic Container Device */) // _CID: Compatible ID + Method (CTFY, 2, NotSerialized) + { + If ((Arg0 == Zero)) + { + Notify (C000, Arg1) + } + + If ((Arg0 == One)) + { + Notify (C001, Arg1) + } [snip] + If ((Arg0 == 0x35)) + { + Notify (C035, Arg1) + } + } [snip] + Processor (C000, 0x00, 0x00000000, 0x00) + { + Method (_STA, 0, Serialized) // _STA: Status + { + Return (CSTA (Zero)) + } + + Name (_MAT, Buffer (0x08) // _MAT: Multiple APIC Table Entry + { + 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 // ........ + }) + Method (_OST, 3, Serialized) // _OST: OSPM Status Indication + { + COST (Zero, Arg0, Arg1, Arg2) + } + } + + Processor (C001, 0x01, 0x00000000, 0x00) + { + Method (_STA, 0, Serialized) // _STA: Status + { + Return (CSTA (One)) + } + + Name (_MAT, Buffer (0x08) // _MAT: Multiple APIC Table Entry + { + 0x00, 0x08, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00 // ........ + }) + Method (_EJ0, 1, NotSerialized) // _EJx: Eject Device, x=0-9 + { + CEJ0 (One) + } + + Method (_OST, 3, Serialized) // _OST: OSPM Status Indication + { + COST (One, Arg0, Arg1, Arg2) + } + } [snip] + Processor (C035, 0x35, 0x00000000, 0x00) + { + Method (_STA, 0, Serialized) // _STA: Status + { + Return (CSTA (0x35)) + } + + Name (_MAT, Buffer (0x08) // _MAT: Multiple APIC Table Entry + { + 0x00, 0x08, 0x35, 0x6A, 0x01, 0x00, 0x00, 0x00 // ..5j.... + }) + Method (_EJ0, 1, NotSerialized) // _EJx: Eject Device, x=0-9 + { + CEJ0 (0x35) + } + + Method (_OST, 3, Serialized) // _OST: OSPM Status Indication + { + COST (0x35, Arg0, Arg1, Arg2) + } + } ... Signed-off-by: Zhao Liu Reviewed-by: Michael S. Tsirkin Message-Id: <20231023094635.1588282-8-zhao1.liu@linux.intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/q35/APIC.core-count | Bin 0 -> 544 bytes tests/data/acpi/q35/DSDT.core-count | Bin 0 -> 12913 bytes tests/data/acpi/q35/FACP.core-count | Bin 0 -> 244 bytes tests/qtest/bios-tables-test-allowed-diff.h | 3 --- 4 files changed, 3 deletions(-) diff --git a/tests/data/acpi/q35/APIC.core-count b/tests/data/acpi/q35/APIC.core-count index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..d9d7ca9a896159791f6e74842d02786dc2608cb3 100644 GIT binary patch literal 544 zcmXxh*-pYh7(n5_rLA2+0RaKGf`EYgcISn(YQP%{EZ zC}OsRxiZdIaG{F%8W!qUO#f5d#RmIQ6U!~Ev~j6})h^b0SnuQV8LkX)HNv%XT))8Z z5*tHoj&S1&TM4$u*qPwwEq3p)caQxkZav`kBknxm?lbPa;QlKfyy4+H9(~~PC!Tz1 jjjVpV@0ngrUrimlY+ISr<$3?*s{?!sg0w8>S6%T3A7&l_ literal 0 HcmV?d00001 diff --git a/tests/data/acpi/q35/DSDT.core-count b/tests/data/acpi/q35/DSDT.core-count index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..a24b04cbdbf09383b933a42a2a15182545543a87 100644 GIT binary patch literal 12913 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 YBHITON2VDSAnpK(F*YFF1LDH~0O^Si0RR91 literal 0 HcmV?d00001 diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index b9bc196130..dfb8523c8b 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1,4 +1 @@ /* List of comma-separated changed AML files to ignore */ -"tests/data/acpi/q35/APIC.core-count", -"tests/data/acpi/q35/DSDT.core-count", -"tests/data/acpi/q35/FACP.core-count", From c63fcb2c10810e8294712f6e09657ef5bdd78833 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 23 Oct 2023 17:46:27 +0800 Subject: [PATCH 692/974] tests: bios-tables-test: Prepare the ACPI table change for smbios type4 core count2 test Following the guidelines in tests/qtest/bios-tables-test.c, this is step 1 - 3. List the ACPI tables that will be changed about the type 4 core count2 test case. Signed-off-by: Zhao Liu Reviewed-by: Michael S. Tsirkin Message-Id: <20231023094635.1588282-9-zhao1.liu@linux.intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test-allowed-diff.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..0f95d1344b 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,3 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/q35/APIC.core-count2", +"tests/data/acpi/q35/DSDT.core-count2", From 6dc82e32226dd3455a1b82bb2ef37d938570084f Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 23 Oct 2023 17:46:28 +0800 Subject: [PATCH 693/974] tests: bios-tables-test: Extend smbios core count2 test to cover general topology The commit 196ea60a734c3 ("hw/smbios: Fix core count in type4") fixed the miscalculation of cores per socket. The original core count2 test (with the topology configured by "-smp 275") didn't recognize that topology-related but because it just created a special topology with only one socket and one die by default, ignoring the effect of more topology levels (between socket and core) on the cores per socket calculation. So for the topology in this case, there're the following considerations: 1. cores per socket should be more than 255 to ensure we could cover the core count2 field. 2. The original bug was that cores per socket was miscalculated, so now we should include as many topology levels as possible (multiple sockets or dies, no module since x86 hasn't supported it) to cover more general topology scenarios, to ensure that the cores per socket encoded in the core count2 field is correct. Based on these considerations, select the topology with multiple dies: -smp 260,dies=2,cores=130,threads=1 Note, here we doesn't configure multiple sockets to avoid the error ("kvm_init_vcpu: kvm_get_vcpu failed (*): Too many open files") if user uses the default ulimit seeting on his machine. And the cores per socket calculation for multiple sockets has already been covered by the core count test case, so that only multiple dies configuration is enough. The expected core count2 = cores per socket = cores (130) * dies (2) = 260. Suggested-by: Igor Mammedov Signed-off-by: Zhao Liu Acked-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Message-Id: <20231023094635.1588282-10-zhao1.liu@linux.intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index c20f6f73d0..f3af20cf2c 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -1024,10 +1024,12 @@ static void test_acpi_q35_tcg_core_count2(void) .required_struct_types = base_required_struct_types, .required_struct_types_len = ARRAY_SIZE(base_required_struct_types), .smbios_core_count = 0xFF, - .smbios_core_count2 = 275, + .smbios_core_count2 = 260, }; - test_acpi_one("-machine smbios-entry-point-type=64 -smp 275", &data); + test_acpi_one("-machine smbios-entry-point-type=64 " + "-smp 260,dies=2,cores=130,threads=1", + &data); free_test_data(&data); } From f03359a85b4c29858c95d043961288c5e6dc662b Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 23 Oct 2023 17:46:29 +0800 Subject: [PATCH 694/974] tests: bios-tables-test: Update ACPI table binaries for smbios core count2 test Change the core count2 from 275 to 260. Following the guidelines in tests/qtest/bios-tables-test.c, this is step 5 and 6. Changes in the tables: APIC: /* * Intel ACPI Component Architecture * AML/ASL+ Disassembler version 20200925 (64-bit version) * Copyright (c) 2000 - 2020 Intel Corporation * - * Disassembly of tests/data/acpi/q35/APIC.core-count2, Wed Aug 23 16:29:51 2023 + * Disassembly of /tmp/aml-KQDX91, Wed Aug 23 16:29:51 2023 * * ACPI Data Table [APIC] * * Format: [HexOffset DecimalOffset ByteLength] FieldName : FieldValue */ [000h 0000 4] Signature : "APIC" [Multiple APIC Description Table (MADT)] -[004h 0004 4] Table Length : 000009AE +[004h 0004 4] Table Length : 00000CA6 [008h 0008 1] Revision : 03 -[009h 0009 1] Checksum : CE +[009h 0009 1] Checksum : FA [00Ah 0010 6] Oem ID : "BOCHS " [010h 0016 8] Oem Table ID : "BXPC " [018h 0024 4] Oem Revision : 00000001 [01Ch 0028 4] Asl Compiler ID : "BXPC" [020h 0032 4] Asl Compiler Revision : 00000001 [024h 0036 4] Local Apic Address : FEE00000 [028h 0040 4] Flags (decoded below) : 00000001 PC-AT Compatibility : 1 [02Ch 0044 1] Subtable Type : 00 [Processor Local APIC] [02Dh 0045 1] Length : 08 [02Eh 0046 1] Processor ID : 00 [02Fh 0047 1] Local Apic ID : 00 [030h 0048 4] Flags (decoded below) : 00000001 Processor Enabled : 1 @@ -1051,1256 +1051,1136 @@ [42Ch 1068 1] Subtable Type : 00 [Processor Local APIC] [42Dh 1069 1] Length : 08 [42Eh 1070 1] Processor ID : 80 [42Fh 1071 1] Local Apic ID : 80 [430h 1072 4] Flags (decoded below) : 00000001 Processor Enabled : 1 Runtime Online Capable : 0 [434h 1076 1] Subtable Type : 00 [Processor Local APIC] [435h 1077 1] Length : 08 [436h 1078 1] Processor ID : 81 [437h 1079 1] Local Apic ID : 81 [438h 1080 4] Flags (decoded below) : 00000001 Processor Enabled : 1 Runtime Online Capable : 0 -[43Ch 1084 1] Subtable Type : 00 [Processor Local APIC] -[43Dh 1085 1] Length : 08 -[43Eh 1086 1] Processor ID : 82 -[43Fh 1087 1] Local Apic ID : 82 -[440h 1088 4] Flags (decoded below) : 00000001 - Processor Enabled : 1 - Runtime Online Capable : 0 - -[444h 1092 1] Subtable Type : 00 [Processor Local APIC] -[445h 1093 1] Length : 08 -[446h 1094 1] Processor ID : 83 -[447h 1095 1] Local Apic ID : 83 -[448h 1096 4] Flags (decoded below) : 00000001 - Processor Enabled : 1 - Runtime Online Capable : 0 [snip] - -[964h 2404 1] Subtable Type : 01 [I/O APIC] -[965h 2405 1] Length : 0C -[966h 2406 1] I/O Apic ID : 00 -[967h 2407 1] Reserved : 00 -[968h 2408 4] Address : FEC00000 -[96Ch 2412 4] Interrupt : 00000000 - -[970h 2416 1] Subtable Type : 02 [Interrupt Source Override] -[971h 2417 1] Length : 0A -[972h 2418 1] Bus : 00 -[973h 2419 1] Source : 00 -[974h 2420 4] Interrupt : 00000002 -[978h 2424 2] Flags (decoded below) : 0000 +[43Ch 1084 1] Subtable Type : 09 [Processor Local x2APIC] +[43Dh 1085 1] Length : 10 +[43Eh 1086 2] Reserved : 0000 +[440h 1088 4] Processor x2Apic ID : 00000100 +[444h 1092 4] Flags (decoded below) : 00000001 + Processor Enabled : 1 +[448h 1096 4] Processor UID : 00000082 + +[44Ch 1100 1] Subtable Type : 09 [Processor Local x2APIC] +[44Dh 1101 1] Length : 10 +[44Eh 1102 2] Reserved : 0000 +[450h 1104 4] Processor x2Apic ID : 00000101 +[454h 1108 4] Flags (decoded below) : 00000001 + Processor Enabled : 1 +[458h 1112 4] Processor UID : 00000083 + [snip] + +[C68h 3176 1] Subtable Type : 02 [Interrupt Source Override] +[C69h 3177 1] Length : 0A +[C6Ah 3178 1] Bus : 00 +[C6Bh 3179 1] Source : 00 +[C6Ch 3180 4] Interrupt : 00000002 +[C70h 3184 2] Flags (decoded below) : 0000 Polarity : 0 Trigger Mode : 0 -[97Ah 2426 1] Subtable Type : 02 [Interrupt Source Override] -[97Bh 2427 1] Length : 0A -[97Ch 2428 1] Bus : 00 -[97Dh 2429 1] Source : 05 -[97Eh 2430 4] Interrupt : 00000005 -[982h 2434 2] Flags (decoded below) : 000D +[C72h 3186 1] Subtable Type : 02 [Interrupt Source Override] +[C73h 3187 1] Length : 0A +[C74h 3188 1] Bus : 00 +[C75h 3189 1] Source : 05 +[C76h 3190 4] Interrupt : 00000005 +[C7Ah 3194 2] Flags (decoded below) : 000D Polarity : 1 Trigger Mode : 3 -[984h 2436 1] Subtable Type : 02 [Interrupt Source Override] -[985h 2437 1] Length : 0A -[986h 2438 1] Bus : 00 -[987h 2439 1] Source : 09 -[988h 2440 4] Interrupt : 00000009 -[98Ch 2444 2] Flags (decoded below) : 000D +[C7Ch 3196 1] Subtable Type : 02 [Interrupt Source Override] +[C7Dh 3197 1] Length : 0A +[C7Eh 3198 1] Bus : 00 +[C7Fh 3199 1] Source : 09 +[C80h 3200 4] Interrupt : 00000009 +[C84h 3204 2] Flags (decoded below) : 000D Polarity : 1 Trigger Mode : 3 -[98Eh 2446 1] Subtable Type : 02 [Interrupt Source Override] -[98Fh 2447 1] Length : 0A -[990h 2448 1] Bus : 00 -[991h 2449 1] Source : 0A -[992h 2450 4] Interrupt : 0000000A -[996h 2454 2] Flags (decoded below) : 000D +[C86h 3206 1] Subtable Type : 02 [Interrupt Source Override] +[C87h 3207 1] Length : 0A +[C88h 3208 1] Bus : 00 +[C89h 3209 1] Source : 0A +[C8Ah 3210 4] Interrupt : 0000000A +[C8Eh 3214 2] Flags (decoded below) : 000D Polarity : 1 Trigger Mode : 3 -[998h 2456 1] Subtable Type : 02 [Interrupt Source Override] -[999h 2457 1] Length : 0A -[99Ah 2458 1] Bus : 00 -[99Bh 2459 1] Source : 0B -[99Ch 2460 4] Interrupt : 0000000B -[9A0h 2464 2] Flags (decoded below) : 000D +[C90h 3216 1] Subtable Type : 02 [Interrupt Source Override] +[C91h 3217 1] Length : 0A +[C92h 3218 1] Bus : 00 +[C93h 3219 1] Source : 0B +[C94h 3220 4] Interrupt : 0000000B +[C98h 3224 2] Flags (decoded below) : 000D Polarity : 1 Trigger Mode : 3 -[9A2h 2466 1] Subtable Type : 0A [Local x2APIC NMI] -[9A3h 2467 1] Length : 0C -[9A4h 2468 2] Flags (decoded below) : 0000 +[C9Ah 3226 1] Subtable Type : 0A [Local x2APIC NMI] +[C9Bh 3227 1] Length : 0C +[C9Ch 3228 2] Flags (decoded below) : 0000 Polarity : 0 Trigger Mode : 0 -[9A6h 2470 4] Processor UID : FFFFFFFF -[9AAh 2474 1] Interrupt Input LINT : 01 -[9ABh 2475 3] Reserved : 000000 +[C9Eh 3230 4] Processor UID : FFFFFFFF +[CA2h 3234 1] Interrupt Input LINT : 01 +[CA3h 3235 3] Reserved : 000000 ... DSDT: /* * Intel ACPI Component Architecture * AML/ASL+ Disassembler version 20200925 (64-bit version) * Copyright (c) 2000 - 2020 Intel Corporation * * Disassembling to symbolic ASL+ operators * - * Disassembly of tests/data/acpi/q35/DSDT.core-count2, Wed Aug 23 16:29:51 2023 + * Disassembly of /tmp/aml-6DDX91, Wed Aug 23 16:29:51 2023 * * Original Table Header: * Signature "DSDT" - * Length 0x00007EEF (32495) + * Length 0x000083EA (33770) * Revision 0x01 **** 32-bit table (V1), no 64-bit math support - * Checksum 0x52 + * Checksum 0x01 * OEM ID "BOCHS " * OEM Table ID "BXPC " * OEM Revision 0x00000001 (1) * Compiler ID "BXPC" * Compiler Version 0x00000001 (1) */ DefinitionBlock ("", "DSDT", 1, "BOCHS ", "BXPC ", 0x00000001) { Scope (\) { OperationRegion (DBG, SystemIO, 0x0402, One) Field (DBG, ByteAcc, NoLock, Preserve) { DBGB, 8 } @@ -4196,107 +4196,32 @@ } If ((Arg0 == 0x0101)) { Notify (C101, Arg1) } If ((Arg0 == 0x0102)) { Notify (C102, Arg1) } If ((Arg0 == 0x0103)) { Notify (C103, Arg1) } - - If ((Arg0 == 0x0104)) - { - Notify (C104, Arg1) - } - - If ((Arg0 == 0x0105)) - { - Notify (C105, Arg1) - } - - If ((Arg0 == 0x0106)) - { - Notify (C106, Arg1) - } - [snip] - If ((Arg0 == 0x0112)) - { - Notify (C112, Arg1) - } } Method (CSTA, 1, Serialized) { Acquire (\_SB.PCI0.PRES.CPLK, 0xFFFF) \_SB.PCI0.PRES.CSEL = Arg0 Local0 = Zero If ((\_SB.PCI0.PRES.CPEN == One)) { Local0 = 0x0F } Release (\_SB.PCI0.PRES.CPLK) Return (Local0) } @@ -4306,33 +4231,33 @@ \_SB.PCI0.PRES.CSEL = Arg0 \_SB.PCI0.PRES.CEJ0 = One Release (\_SB.PCI0.PRES.CPLK) } Method (CSCN, 0, Serialized) { Acquire (\_SB.PCI0.PRES.CPLK, 0xFFFF) Name (CNEW, Package (0xFF) {}) Local3 = Zero Local4 = One While ((Local4 == One)) { Local4 = Zero Local0 = One Local1 = Zero - While (((Local0 == One) && (Local3 < 0x0113))) + While (((Local0 == One) && (Local3 < 0x0104))) { Local0 = Zero \_SB.PCI0.PRES.CSEL = Local3 \_SB.PCI0.PRES.CCMD = Zero If ((\_SB.PCI0.PRES.CDAT < Local3)) { Break } If ((Local1 == 0xFF)) { Local4 = One Break } Local3 = \_SB.PCI0.PRES.CDAT @@ -7220,3281 +7145,3281 @@ Name (_MAT, Buffer (0x08) // _MAT: Multiple APIC Table Entry { 0x00, 0x08, 0x81, 0x81, 0x01, 0x00, 0x00, 0x00 // ........ }) Method (_EJ0, 1, NotSerialized) // _EJx: Eject Device, x=0-9 { CEJ0 (0x81) } Method (_OST, 3, Serialized) // _OST: OSPM Status Indication { COST (0x81, Arg0, Arg1, Arg2) } } - Processor (C082, 0x82, 0x00000000, 0x00) + Device (C082) { + Name (_HID, "ACPI0007" /* Processor Device */) // _HID: Hardware ID + Name (_UID, 0x82) // _UID: Unique ID Method (_STA, 0, Serialized) // _STA: Status { Return (CSTA (0x82)) } - Name (_MAT, Buffer (0x08) // _MAT: Multiple APIC Table Entry + Name (_MAT, Buffer (0x10) // _MAT: Multiple APIC Table Entry { - 0x00, 0x08, 0x82, 0x82, 0x01, 0x00, 0x00, 0x00 // ........ + /* 0000 */ 0x09, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // ........ + /* 0008 */ 0x01, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00 // ........ }) Method (_EJ0, 1, NotSerialized) // _EJx: Eject Device, x=0-9 { CEJ0 (0x82) } Method (_OST, 3, Serialized) // _OST: OSPM Status Indication { COST (0x82, Arg0, Arg1, Arg2) } } - Processor (C083, 0x83, 0x00000000, 0x00) + Device (C083) { + Name (_HID, "ACPI0007" /* Processor Device */) // _HID: Hardware ID + Name (_UID, 0x83) // _UID: Unique ID Method (_STA, 0, Serialized) // _STA: Status { Return (CSTA (0x83)) } - Name (_MAT, Buffer (0x08) // _MAT: Multiple APIC Table Entry + Name (_MAT, Buffer (0x10) // _MAT: Multiple APIC Table Entry { - 0x00, 0x08, 0x83, 0x83, 0x01, 0x00, 0x00, 0x00 // ........ + /* 0000 */ 0x09, 0x10, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, // ........ + /* 0008 */ 0x01, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00 // ........ }) Method (_EJ0, 1, NotSerialized) // _EJx: Eject Device, x=0-9 { CEJ0 (0x83) } Method (_OST, 3, Serialized) // _OST: OSPM Status Indication { COST (0x83, Arg0, Arg1, Arg2) } } - Processor (C084, 0x84, 0x00000000, 0x00) + Device (C084) { + Name (_HID, "ACPI0007" /* Processor Device */) // _HID: Hardware ID + Name (_UID, 0x84) // _UID: Unique ID Method (_STA, 0, Serialized) // _STA: Status { Return (CSTA (0x84)) } - Name (_MAT, Buffer (0x08) // _MAT: Multiple APIC Table Entry + Name (_MAT, Buffer (0x10) // _MAT: Multiple APIC Table Entry { - 0x00, 0x08, 0x84, 0x84, 0x01, 0x00, 0x00, 0x00 // ........ + /* 0000 */ 0x09, 0x10, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, // ........ + /* 0008 */ 0x01, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00 // ........ }) Method (_EJ0, 1, NotSerialized) // _EJx: Eject Device, x=0-9 { CEJ0 (0x84) } Method (_OST, 3, Serialized) // _OST: OSPM Status Indication { COST (0x84, Arg0, Arg1, Arg2) } } [snip] - Processor (C0FE, 0xFE, 0x00000000, 0x00) + Device (C0FE) { + Name (_HID, "ACPI0007" /* Processor Device */) // _HID: Hardware ID + Name (_UID, 0xFE) // _UID: Unique ID Method (_STA, 0, Serialized) // _STA: Status { Return (CSTA (0xFE)) } - Name (_MAT, Buffer (0x08) // _MAT: Multiple APIC Table Entry + Name (_MAT, Buffer (0x10) // _MAT: Multiple APIC Table Entry { - 0x00, 0x08, 0xFE, 0xFE, 0x01, 0x00, 0x00, 0x00 // ........ + /* 0000 */ 0x09, 0x10, 0x00, 0x00, 0x7C, 0x01, 0x00, 0x00, // ....|... + /* 0008 */ 0x01, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00 // ........ }) Method (_EJ0, 1, NotSerialized) // _EJx: Eject Device, x=0-9 { CEJ0 (0xFE) } Method (_OST, 3, Serialized) // _OST: OSPM Status Indication { COST (0xFE, Arg0, Arg1, Arg2) } } ... Signed-off-by: Zhao Liu Reviewed-by: Michael S. Tsirkin Message-Id: <20231023094635.1588282-11-zhao1.liu@linux.intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/q35/APIC.core-count2 | Bin 2478 -> 3238 bytes tests/data/acpi/q35/DSDT.core-count2 | Bin 32495 -> 33770 bytes tests/qtest/bios-tables-test-allowed-diff.h | 2 -- 3 files changed, 2 deletions(-) diff --git a/tests/data/acpi/q35/APIC.core-count2 b/tests/data/acpi/q35/APIC.core-count2 index f5da2eb1e8a93d961b39f665f2e8b02acf1aeb3c..4f2428443430b7da8321e9edd816219deb7929a2 100644 GIT binary patch literal 3238 zcmXxmby$@L7>4l=5IyGriroq-c6Un%f(43!4Ju%FCoFWS>+W#f-LAWJ9j?3EZMyH~ zdB%0*{(jUSTwd=(R#8FDzT_ZCIGkOWlV6;cT~d^j7QV#=LBKN&f4Y) zR}&J{TD8=~M74HpwN4#15>ca3H5OBolGNm6H6=x@TUV`DPfbl#>(^HsG*BBhR2wx? z8#h*)G*O#2RhuSZmv`|~NR9m%DTenu*v{Bo(RrPxbBI)UJyLM{(_G*U?YDR|I zv7_3lliInn+NF!ywX52#o7%m*+M|csv!~jtm)g6x+NY1&x3Ai-pW45_I$(f0aG*M9 zkUDsRrAyUi z%hct|)fFq$l`GX%tJKx2)irC>wQJRN>(uq@)eRfejT_ZXo7Byl)h%1pty|S?+tlsb z)g3$3ojcX4Dz&EHirqHX>*|PSvH3X zpKWun@HsYz3!iIq!0>rChYX)@bI|YwHir#gXmjB3MK*^HUu<*m@Fg~f4_|6?0P$ru zhY(+Gr`cE7>GqX22QqvGSJ@m&e6`KN#QW?F`x=`A%IB}OIi&bHn}dq4w>hl%2Aczm zZ?rkI_$Hf!i*L3$y!aNI1B`FAImGxjn}dvRw>ixC4x0mw@3cA8_%54+jqkQO-1r`w z1CH;tIpp|0n}d$;w>j+i0hL z0};N0Cv1iSKWQ@<_$fQre%j8npRpMdzW!O8LBY@23=4kVW?=9OHbaA7v>6=ylFjho zmu&_JzhW~)_*I)h!mrs36Mo%hpzr~kp~44k1`EGoGhFyhn*qab*$f$e+h)-4J2u0H z-?bSy{GQFw;rDF@4}V}YeE36~0mO%Fh7f;bm)alOW%egF0~x-8Pi=-0e`Yh7_;b75 z{=%-XzqBjuuWSZ2d<9?I3@iS|W?=ERHbaZQvl(3cz0L6AA8ZB~|7bJB_$Qk|#y{H( zGycV9pz*IZLydp48EpK!&2ZyCYz7?vX*1;bFPlNfhi!%(|7|ny_#d00$N$<4K0abI z{P?KN9{`wl>^*-7V0Io?+3Y;7wrh4inQz?Bj}GEv;XfXDN=yu!!c#Q*|5Ggd0(~qg Qspcsbe)rMvE&PGOe_jvc0RR91 literal 2478 zcmXZeWl$DT6oBC+KKO#RP*m&=4D137us{*TMz9O9Td}*lI}y7Jv9Q1{3{VuiJJ3D6 zcW3rK=bgE`Kkn?0^$7~_i#2JQO`>n0pMP*Z-_RhxeEMajX`0NUrln+LYSc8evO;TX zw6Q^3TQnHZ&JOME(P%`I3C(7-SkS=%9Ualh37wtM#RXkm(ajCBWWlUiFfD+ZQ7vv0p!oio*W=alil^I1mR7!oh=a z$PgSl6o(DN;lpvn2pl;QM~%YKqjAg_96J`HqjB6g96uf>Ou&f~andB5JQ=48~5zNy?b%rKHR?_4;;XQ2l3D$JbW0B9KoYU@z^muejHDnz>_C2E)Gwf!uWVR zeHzc4!Lw)a+&Mgd9upGq!Ueo|5iecB%a`%W6--RTq$Iq06|Y^x>(}wd4ZL|1Z{5P% zxAD#$yn7e#-NXC$F*z9@Jivz!@zEoE{1{VGFf|pQJi(_=F)awzkI>3U-8>F{Qez({J@_-@z*c>{Tu)M z!M}eoBSSQ~XxcwrnMG-d%su)dZKYb2wpJ}l+o%?$ZB>iX2GydpooZ3qUbQG~R4q!I zREyGP)uOaTwJ7bNT9kHFElNA77Nwn4i_$KtMQK;nqO_apOda&|(&92?wKnUw3^ExE cx{flL^j|P0v%Z1JV#%D$`qTgPOMjvEANV#K5C8xG diff --git a/tests/data/acpi/q35/DSDT.core-count2 b/tests/data/acpi/q35/DSDT.core-count2 index b47891ec10be131a59bf404242241c054ac902f8..3a0cb8c581c8cc630a2ec21712b7f8b75fcad1c8 100644 GIT binary patch delta 7005 zcmZA0cUaYR6vuHcmD)`M6Dpcop=68FRGPxQSQ+I2Hwg|D$rMA)wuyop<*2xEptuoH zapMFxZrr$095})Kguios=lOl^^YGliczw_J`#aCOJ%_q}6KALiUeBHm>ly%joe&wv9npJESpSH!iMaFibUbVckJpBA>RUFW;g2TVT zClndy?K{A$NU%Zdp_&wR(`he=Qe;Mo750h$58R0;z8L& z#nU`hbqzHT)Id<~q6X4!Ri7Ga5U4?*o)R^PzG`aTu-+5pkpL=z`uFj+CD35~uli5^ zD0Se@J~r_bQL)|$z@eZ)>S3xssd<3T=}X-QPWV})$?-1DC!<6ZwmO>c++SYJQ@aj zh#F1HRdH9zpprp(ib_rnGe>b&1c(4t6-3ln6=yXD)EH1Nh#EsWyrgN&e zvnimafO=Kb6#B50`7Wa~->IOcf_hEVRO;7Cx(lEu-K7Fb1@yY0RF~bQT6dSKy9?y* zrqT6Qy1O^zh-p;0wcIw4yPFP!r^DbkMNOxcs<^uupk{!oE@}o%Q^n!U1T_;>4N)_x zSQUqt1}Y8ITcXmaZX5Gm9NsKYvp~HqY8L&Yio=@?YBs2MM9rqHsyMuKQ0bt2MWwqe zFWtJlbj$LL@a9m%wz|BUa>N`8Z)-l1!P=?FyGOWAH(A@=bc#CONxbE&lIbty_2$wGt#NlPa;7l0&k*G{6RmI^g z0ks5_zo;eD@F#N?hnEE^3)IJ=vS_F(4sR)_rJz0$wUiF1;_#M%S_Z0)sAcpwf=;#9<<*lTR?tHo%;Bws!7E{K zeNii^QH1$O4sR8xRiGM(T1BH&ad@jitp*h!YBe2I#o?_1wFcB@qSnwmk>(9KytSa# zg8E$4TI!*S!&?Vx9jGrvt)mr@5?+uIUJjrfKwk>VaoJstb$2B9ad832F+W><%z~HY%ZJ=?gIJ}LZHiBv>Y9pOe#o=uNwFy+9s7+M6qj^IPZ!@UP zpn^ngrrxSJye*)%fO3f1LhDpQ1etb;kcZ7_Hn4E{#cHoBmS!`lvOJE(6(Z72WE<|8@09iVoA`cBji z`a>0mw-eM(P>n_Hq|K^0yj`Gnf%;z5E_y!7ydj6T8`N%4A)nI7&VT6|tC?C)d zg7RH~2DcJ*gc4M7 zct=4U1=U*AQQD)5!#f7*7^pU)j?pXK%o}oe$3Yzj)mGGT>a2>xI|1qhsCJ@G&>~eF z-bqj=L4}JtNq4(Rcupg{LO_LpeiBsZvb#d-?h18x!5rQxijC3TwU;AK(eN1gGQk|) zX&8JO21kfGO@~x*cxOPJ0Tn6g3|0TxoWLltsJyylxT>y0fRA*5a=-ck*yEwceP(`4kL>0L#ugJQ*BFplO@GjE&?z+4# za>PYCu7f$eOECBn4DKrG64m_0ye@}V45}DZw5Vc=RmI_526Y)!H&K^ql`0PJ3aBff zVnkh`r+b(;{jqpkUl>quhP>IX#O02sp z(cLxT@UD|*Pu*P)IpR9K-&5Y$h{L-9gKxm#o}zA0UsW95O;9&M#frK~>s4`hw?N$j z)l1YZvd5Y?(_VCF(IX ph->5iw~Z*SqwOCmjf=AVOX0nvZ2f6t`R{+^*Qa(?$e%Nx`XBpVSA+ln delta 7056 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 diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index 0f95d1344b..dfb8523c8b 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1,3 +1 @@ /* List of comma-separated changed AML files to ignore */ -"tests/data/acpi/q35/APIC.core-count2", -"tests/data/acpi/q35/DSDT.core-count2", From 85ccbe1275b58efe9e30d229c942d33144b5ef6f Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 23 Oct 2023 17:46:30 +0800 Subject: [PATCH 695/974] tests: bios-tables-test: Prepare the ACPI table change for smbios type4 thread count test Following the guidelines in tests/qtest/bios-tables-test.c, this is step 1 - 3. List the ACPI tables that will be added to test the thread count field of smbios type4 table. Signed-off-by: Zhao Liu Reviewed-by: Michael S. Tsirkin Message-Id: <20231023094635.1588282-12-zhao1.liu@linux.intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/q35/APIC.thread-count | 0 tests/data/acpi/q35/DSDT.thread-count | 0 tests/data/acpi/q35/FACP.thread-count | 0 tests/qtest/bios-tables-test-allowed-diff.h | 3 +++ 4 files changed, 3 insertions(+) create mode 100644 tests/data/acpi/q35/APIC.thread-count create mode 100644 tests/data/acpi/q35/DSDT.thread-count create mode 100644 tests/data/acpi/q35/FACP.thread-count diff --git a/tests/data/acpi/q35/APIC.thread-count b/tests/data/acpi/q35/APIC.thread-count new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/data/acpi/q35/DSDT.thread-count b/tests/data/acpi/q35/DSDT.thread-count new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/data/acpi/q35/FACP.thread-count b/tests/data/acpi/q35/FACP.thread-count new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..4d139d7f6b 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,4 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/q35/APIC.thread-count", +"tests/data/acpi/q35/DSDT.thread-count", +"tests/data/acpi/q35/FACP.thread-count", From 7ee18dcef14540c79892381d7af0a2c54ec11484 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 23 Oct 2023 17:46:31 +0800 Subject: [PATCH 696/974] tests: bios-tables-test: Add test for smbios type4 thread count This tests the commit 7298fd7de5551 ("hw/smbios: Fix thread count in type4"). In smbios_build_type_4_table() (hw/smbios/smbios.c), if the number of threads in the socket is not more than 255, then smbios type4 table encodes threads per socket into the thread count field. So for the topology in this case, there're the following considerations: 1. threads per socket should be not more than 255 to ensure we could cover the thread count field. 2. The original bug was that threads per socket was miscalculated, so now we should configure as many topology levels as possible (multiple sockets & dies, no module since x86 hasn't supported it) to cover more general topology scenarios, to ensure that the threads per socket encoded in the thread count field is correct. 3. For the more general topology, we should also add "cpus" (presented threads for machine) and "maxcpus" (total threads for machine) to make sure that configuring unpluged CPUs in smp (cpus < maxcpus) does not affect the correctness of threads per socket for thread count field. Based on these considerations, select the topology as the follow: -smp cpus=15,maxcpus=54,sockets=2,dies=3,cores=3,threads=3 The expected thread count = threads per socket = threads (3) * cores (3) * dies (3) = 27. Suggested-by: Igor Mammedov Signed-off-by: Zhao Liu Reviewed-by: Michael S. Tsirkin Message-Id: <20231023094635.1588282-13-zhao1.liu@linux.intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index f3af20cf2c..395ed7f9ff 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -95,6 +95,7 @@ typedef struct { uint16_t smbios_cpu_curr_speed; uint8_t smbios_core_count; uint16_t smbios_core_count2; + uint8_t smbios_thread_count; uint8_t *required_struct_types; int required_struct_types_len; int type4_count; @@ -640,6 +641,7 @@ static void smbios_cpu_test(test_data *data, uint32_t addr, SmbiosEntryPointType ep_type) { uint8_t core_count, expected_core_count = data->smbios_core_count; + uint8_t thread_count, expected_thread_count = data->smbios_thread_count; uint16_t speed, expected_speed[2]; uint16_t core_count2, expected_core_count2 = data->smbios_core_count2; int offset[2]; @@ -663,6 +665,13 @@ static void smbios_cpu_test(test_data *data, uint32_t addr, g_assert_cmpuint(core_count, ==, expected_core_count); } + thread_count = qtest_readb(data->qts, + addr + offsetof(struct smbios_type_4, thread_count)); + + if (expected_thread_count) { + g_assert_cmpuint(thread_count, ==, expected_thread_count); + } + if (ep_type == SMBIOS_ENTRY_POINT_TYPE_64) { core_count2 = qtest_readw(data->qts, addr + offsetof(struct smbios_type_4, core_count2)); @@ -1033,6 +1042,22 @@ static void test_acpi_q35_tcg_core_count2(void) free_test_data(&data); } +static void test_acpi_q35_tcg_thread_count(void) +{ + test_data data = { + .machine = MACHINE_Q35, + .variant = ".thread-count", + .required_struct_types = base_required_struct_types, + .required_struct_types_len = ARRAY_SIZE(base_required_struct_types), + .smbios_thread_count = 27, + }; + + test_acpi_one("-machine smbios-entry-point-type=64 " + "-smp cpus=15,maxcpus=54,sockets=2,dies=3,cores=3,threads=3", + &data); + free_test_data(&data); +} + static void test_acpi_q35_tcg_bridge(void) { test_data data = {}; @@ -2201,6 +2226,8 @@ int main(int argc, char *argv[]) test_acpi_q35_tcg_core_count); qtest_add_func("acpi/q35/core-count2", test_acpi_q35_tcg_core_count2); + qtest_add_func("acpi/q35/thread-count", + test_acpi_q35_tcg_thread_count); } if (qtest_has_device("virtio-iommu-pci")) { qtest_add_func("acpi/q35/viot", test_acpi_q35_viot); From a775cb191e85c86a1b43f0d082cf9fa7f6d80464 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 23 Oct 2023 17:46:32 +0800 Subject: [PATCH 697/974] tests: bios-tables-test: Add ACPI table binaries for smbios type4 thread count test Following the guidelines in tests/qtest/bios-tables-test.c, this is step 5 and 6. Changes in the tables: FACP: +/* + * Intel ACPI Component Architecture + * AML/ASL+ Disassembler version 20200925 (64-bit version) + * Copyright (c) 2000 - 2020 Intel Corporation + * + * Disassembly of /tmp/aml-1NP791, Wed Aug 23 21:51:31 2023 + * + * ACPI Data Table [FACP] + * + * Format: [HexOffset DecimalOffset ByteLength] FieldName : FieldValue + */ + +[000h 0000 4] Signature : "FACP" [Fixed ACPI Description Table (FADT)] +[004h 0004 4] Table Length : 000000F4 +[008h 0008 1] Revision : 03 +[009h 0009 1] Checksum : B3 +[00Ah 0010 6] Oem ID : "BOCHS " +[010h 0016 8] Oem Table ID : "BXPC " +[018h 0024 4] Oem Revision : 00000001 +[01Ch 0028 4] Asl Compiler ID : "BXPC" +[020h 0032 4] Asl Compiler Revision : 00000001 + +[024h 0036 4] FACS Address : 00000000 +[028h 0040 4] DSDT Address : 00000000 +[02Ch 0044 1] Model : 01 +[02Dh 0045 1] PM Profile : 00 [Unspecified] +[02Eh 0046 2] SCI Interrupt : 0009 +[030h 0048 4] SMI Command Port : 000000B2 +[034h 0052 1] ACPI Enable Value : 02 +[035h 0053 1] ACPI Disable Value : 03 +[036h 0054 1] S4BIOS Command : 00 +[037h 0055 1] P-State Control : 00 +[038h 0056 4] PM1A Event Block Address : 00000600 +[03Ch 0060 4] PM1B Event Block Address : 00000000 +[040h 0064 4] PM1A Control Block Address : 00000604 +[044h 0068 4] PM1B Control Block Address : 00000000 +[048h 0072 4] PM2 Control Block Address : 00000000 +[04Ch 0076 4] PM Timer Block Address : 00000608 +[050h 0080 4] GPE0 Block Address : 00000620 +[054h 0084 4] GPE1 Block Address : 00000000 +[058h 0088 1] PM1 Event Block Length : 04 +[059h 0089 1] PM1 Control Block Length : 02 +[05Ah 0090 1] PM2 Control Block Length : 00 +[05Bh 0091 1] PM Timer Block Length : 04 +[05Ch 0092 1] GPE0 Block Length : 10 +[05Dh 0093 1] GPE1 Block Length : 00 +[05Eh 0094 1] GPE1 Base Offset : 00 +[05Fh 0095 1] _CST Support : 00 +[060h 0096 2] C2 Latency : 0FFF +[062h 0098 2] C3 Latency : 0FFF +[064h 0100 2] CPU Cache Size : 0000 +[066h 0102 2] Cache Flush Stride : 0000 +[068h 0104 1] Duty Cycle Offset : 00 +[069h 0105 1] Duty Cycle Width : 00 +[06Ah 0106 1] RTC Day Alarm Index : 00 +[06Bh 0107 1] RTC Month Alarm Index : 00 +[06Ch 0108 1] RTC Century Index : 32 +[06Dh 0109 2] Boot Flags (decoded below) : 0002 + Legacy Devices Supported (V2) : 0 + 8042 Present on ports 60/64 (V2) : 1 + VGA Not Present (V4) : 0 + MSI Not Supported (V4) : 0 + PCIe ASPM Not Supported (V4) : 0 + CMOS RTC Not Present (V5) : 0 +[06Fh 0111 1] Reserved : 00 +[070h 0112 4] Flags (decoded below) : 000484A5 + WBINVD instruction is operational (V1) : 1 + WBINVD flushes all caches (V1) : 0 + All CPUs support C1 (V1) : 1 + C2 works on MP system (V1) : 0 + Control Method Power Button (V1) : 0 + Control Method Sleep Button (V1) : 1 + RTC wake not in fixed reg space (V1) : 0 + RTC can wake system from S4 (V1) : 1 + 32-bit PM Timer (V1) : 0 + Docking Supported (V1) : 0 + Reset Register Supported (V2) : 1 + Sealed Case (V3) : 0 + Headless - No Video (V3) : 0 + Use native instr after SLP_TYPx (V3) : 0 + PCIEXP_WAK Bits Supported (V4) : 0 + Use Platform Timer (V4) : 1 + RTC_STS valid on S4 wake (V4) : 0 + Remote Power-on capable (V4) : 0 + Use APIC Cluster Model (V4) : 1 + Use APIC Physical Destination Mode (V4) : 0 + Hardware Reduced (V5) : 0 + Low Power S0 Idle (V5) : 0 + +[074h 0116 12] Reset Register : [Generic Address Structure] +[074h 0116 1] Space ID : 01 [SystemIO] +[075h 0117 1] Bit Width : 08 +[076h 0118 1] Bit Offset : 00 +[077h 0119 1] Encoded Access Width : 00 [Undefined/Legacy] +[078h 0120 8] Address : 0000000000000CF9 + +[080h 0128 1] Value to cause reset : 0F +[081h 0129 2] ARM Flags (decoded below) : 0000 + PSCI Compliant : 0 + Must use HVC for PSCI : 0 + +[083h 0131 1] FADT Minor Revision : 00 +[084h 0132 8] FACS Address : 0000000000000000 +[08Ch 0140 8] DSDT Address : 0000000000000000 +[094h 0148 12] PM1A Event Block : [Generic Address Structure] +[094h 0148 1] Space ID : 01 [SystemIO] +[095h 0149 1] Bit Width : 20 +[096h 0150 1] Bit Offset : 00 +[097h 0151 1] Encoded Access Width : 00 [Undefined/Legacy] +[098h 0152 8] Address : 0000000000000600 + +[0A0h 0160 12] PM1B Event Block : [Generic Address Structure] +[0A0h 0160 1] Space ID : 00 [SystemMemory] +[0A1h 0161 1] Bit Width : 00 +[0A2h 0162 1] Bit Offset : 00 +[0A3h 0163 1] Encoded Access Width : 00 [Undefined/Legacy] +[0A4h 0164 8] Address : 0000000000000000 + +[0ACh 0172 12] PM1A Control Block : [Generic Address Structure] +[0ACh 0172 1] Space ID : 01 [SystemIO] +[0ADh 0173 1] Bit Width : 10 +[0AEh 0174 1] Bit Offset : 00 +[0AFh 0175 1] Encoded Access Width : 00 [Undefined/Legacy] +[0B0h 0176 8] Address : 0000000000000604 + +[0B8h 0184 12] PM1B Control Block : [Generic Address Structure] +[0B8h 0184 1] Space ID : 00 [SystemMemory] +[0B9h 0185 1] Bit Width : 00 +[0BAh 0186 1] Bit Offset : 00 +[0BBh 0187 1] Encoded Access Width : 00 [Undefined/Legacy] +[0BCh 0188 8] Address : 0000000000000000 + +[0C4h 0196 12] PM2 Control Block : [Generic Address Structure] +[0C4h 0196 1] Space ID : 00 [SystemMemory] +[0C5h 0197 1] Bit Width : 00 +[0C6h 0198 1] Bit Offset : 00 +[0C7h 0199 1] Encoded Access Width : 00 [Undefined/Legacy] +[0C8h 0200 8] Address : 0000000000000000 + +[0D0h 0208 12] PM Timer Block : [Generic Address Structure] +[0D0h 0208 1] Space ID : 01 [SystemIO] +[0D1h 0209 1] Bit Width : 20 +[0D2h 0210 1] Bit Offset : 00 +[0D3h 0211 1] Encoded Access Width : 00 [Undefined/Legacy] +[0D4h 0212 8] Address : 0000000000000608 + +[0DCh 0220 12] GPE0 Block : [Generic Address Structure] +[0DCh 0220 1] Space ID : 01 [SystemIO] +[0DDh 0221 1] Bit Width : 80 +[0DEh 0222 1] Bit Offset : 00 +[0DFh 0223 1] Encoded Access Width : 00 [Undefined/Legacy] +[0E0h 0224 8] Address : 0000000000000620 + +[0E8h 0232 12] GPE1 Block : [Generic Address Structure] +[0E8h 0232 1] Space ID : 00 [SystemMemory] +[0E9h 0233 1] Bit Width : 00 +[0EAh 0234 1] Bit Offset : 00 +[0EBh 0235 1] Encoded Access Width : 00 [Undefined/Legacy] +[0ECh 0236 8] Address : 0000000000000000 ... APIC: +/* + * Intel ACPI Component Architecture + * AML/ASL+ Disassembler version 20200925 (64-bit version) + * Copyright (c) 2000 - 2020 Intel Corporation + * + * Disassembly of /tmp/aml-2JP791, Wed Aug 23 21:51:31 2023 + * + * ACPI Data Table [APIC] + * + * Format: [HexOffset DecimalOffset ByteLength] FieldName : FieldValue + */ + +[000h 0000 4] Signature : "APIC" [Multiple APIC Description Table (MADT)] +[004h 0004 4] Table Length : 00000220 +[008h 0008 1] Revision : 03 +[009h 0009 1] Checksum : 63 +[00Ah 0010 6] Oem ID : "BOCHS " +[010h 0016 8] Oem Table ID : "BXPC " +[018h 0024 4] Oem Revision : 00000001 +[01Ch 0028 4] Asl Compiler ID : "BXPC" +[020h 0032 4] Asl Compiler Revision : 00000001 + +[024h 0036 4] Local Apic Address : FEE00000 +[028h 0040 4] Flags (decoded below) : 00000001 + PC-AT Compatibility : 1 + +[02Ch 0044 1] Subtable Type : 00 [Processor Local APIC] +[02Dh 0045 1] Length : 08 +[02Eh 0046 1] Processor ID : 00 +[02Fh 0047 1] Local Apic ID : 00 +[030h 0048 4] Flags (decoded below) : 00000001 + Processor Enabled : 1 + Runtime Online Capable : 0 + +[034h 0052 1] Subtable Type : 00 [Processor Local APIC] +[035h 0053 1] Length : 08 +[036h 0054 1] Processor ID : 01 +[037h 0055 1] Local Apic ID : 01 +[038h 0056 4] Flags (decoded below) : 00000001 + Processor Enabled : 1 + Runtime Online Capable : 0 [snip] +[1D4h 0468 1] Subtable Type : 00 [Processor Local APIC] +[1D5h 0469 1] Length : 08 +[1D6h 0470 1] Processor ID : 35 +[1D7h 0471 1] Local Apic ID : 6A +[1D8h 0472 4] Flags (decoded below) : 00000000 + Processor Enabled : 0 + Runtime Online Capable : 0 + +[1DCh 0476 1] Subtable Type : 01 [I/O APIC] +[1DDh 0477 1] Length : 0C +[1DEh 0478 1] I/O Apic ID : 00 +[1DFh 0479 1] Reserved : 00 +[1E0h 0480 4] Address : FEC00000 +[1E4h 0484 4] Interrupt : 00000000 + +[1E8h 0488 1] Subtable Type : 02 [Interrupt Source Override] +[1E9h 0489 1] Length : 0A +[1EAh 0490 1] Bus : 00 +[1EBh 0491 1] Source : 00 +[1ECh 0492 4] Interrupt : 00000002 +[1F0h 0496 2] Flags (decoded below) : 0000 + Polarity : 0 + Trigger Mode : 0 + +[1F2h 0498 1] Subtable Type : 02 [Interrupt Source Override] +[1F3h 0499 1] Length : 0A +[1F4h 0500 1] Bus : 00 +[1F5h 0501 1] Source : 05 +[1F6h 0502 4] Interrupt : 00000005 +[1FAh 0506 2] Flags (decoded below) : 000D + Polarity : 1 + Trigger Mode : 3 + +[1FCh 0508 1] Subtable Type : 02 [Interrupt Source Override] +[1FDh 0509 1] Length : 0A +[1FEh 0510 1] Bus : 00 +[1FFh 0511 1] Source : 09 +[200h 0512 4] Interrupt : 00000009 +[204h 0516 2] Flags (decoded below) : 000D + Polarity : 1 + Trigger Mode : 3 + +[206h 0518 1] Subtable Type : 02 [Interrupt Source Override] +[207h 0519 1] Length : 0A +[208h 0520 1] Bus : 00 +[209h 0521 1] Source : 0A +[20Ah 0522 4] Interrupt : 0000000A +[20Eh 0526 2] Flags (decoded below) : 000D + Polarity : 1 + Trigger Mode : 3 + +[210h 0528 1] Subtable Type : 02 [Interrupt Source Override] +[211h 0529 1] Length : 0A +[212h 0530 1] Bus : 00 +[213h 0531 1] Source : 0B +[214h 0532 4] Interrupt : 0000000B +[218h 0536 2] Flags (decoded below) : 000D + Polarity : 1 + Trigger Mode : 3 + +[21Ah 0538 1] Subtable Type : 04 [Local APIC NMI] +[21Bh 0539 1] Length : 06 +[21Ch 0540 1] Processor ID : FF +[21Dh 0541 2] Flags (decoded below) : 0000 + Polarity : 0 + Trigger Mode : 0 +[21Fh 0543 1] Interrupt Input LINT : 01 ... DSDT: +/* + * Intel ACPI Component Architecture + * AML/ASL+ Disassembler version 20200925 (64-bit version) + * Copyright (c) 2000 - 2020 Intel Corporation + * + * Disassembling to symbolic ASL+ operators + * + * Disassembly of /tmp/aml-00O791, Wed Aug 23 21:51:31 2023 + * + * Original Table Header: + * Signature "DSDT" + * Length 0x00003271 (12913) + * Revision 0x01 **** 32-bit table (V1), no 64-bit math support + * Checksum 0xAF + * OEM ID "BOCHS " + * OEM Table ID "BXPC " + * OEM Revision 0x00000001 (1) + * Compiler ID "BXPC" + * Compiler Version 0x00000001 (1) + */ +DefinitionBlock ("", "DSDT", 1, "BOCHS ", "BXPC ", 0x00000001) +{ + Scope (\) + { + OperationRegion (DBG, SystemIO, 0x0402, One) + Field (DBG, ByteAcc, NoLock, Preserve) + { + DBGB, 8 + } + + Method (DBUG, 1, NotSerialized) + { + ToHexString (Arg0, Local0) + ToBuffer (Local0, Local0) + Local1 = (SizeOf (Local0) - One) + Local2 = Zero + While ((Local2 < Local1)) + { + DBGB = DerefOf (Local0 [Local2]) + Local2++ + } + + DBGB = 0x0A + } + } [snip] + Processor (C000, 0x00, 0x00000000, 0x00) + { + Method (_STA, 0, Serialized) // _STA: Status + { + Return (CSTA (Zero)) + } + + Name (_MAT, Buffer (0x08) // _MAT: Multiple APIC Table Entry + { + 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 // ........ + }) + Method (_OST, 3, Serialized) // _OST: OSPM Status Indication + { + COST (Zero, Arg0, Arg1, Arg2) + } + } + + Processor (C001, 0x01, 0x00000000, 0x00) + { + Method (_STA, 0, Serialized) // _STA: Status + { + Return (CSTA (One)) + } + + Name (_MAT, Buffer (0x08) // _MAT: Multiple APIC Table Entry + { + 0x00, 0x08, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00 // ........ + }) + Method (_EJ0, 1, NotSerialized) // _EJx: Eject Device, x=0-9 + { + CEJ0 (One) + } + + Method (_OST, 3, Serialized) // _OST: OSPM Status Indication + { + COST (One, Arg0, Arg1, Arg2) + } + } [snip] + Processor (C035, 0x35, 0x00000000, 0x00) + { + Method (_STA, 0, Serialized) // _STA: Status + { + Return (CSTA (0x35)) + } + + Name (_MAT, Buffer (0x08) // _MAT: Multiple APIC Table Entry + { + 0x00, 0x08, 0x35, 0x6A, 0x01, 0x00, 0x00, 0x00 // ..5j.... + }) + Method (_EJ0, 1, NotSerialized) // _EJx: Eject Device, x=0-9 + { + CEJ0 (0x35) + } + + Method (_OST, 3, Serialized) // _OST: OSPM Status Indication + { + COST (0x35, Arg0, Arg1, Arg2) + } + } ... Signed-off-by: Zhao Liu Reviewed-by: Michael S. Tsirkin Message-Id: <20231023094635.1588282-14-zhao1.liu@linux.intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/q35/APIC.thread-count | Bin 0 -> 544 bytes tests/data/acpi/q35/DSDT.thread-count | Bin 0 -> 12913 bytes tests/data/acpi/q35/FACP.thread-count | Bin 0 -> 244 bytes tests/qtest/bios-tables-test-allowed-diff.h | 3 --- 4 files changed, 3 deletions(-) diff --git a/tests/data/acpi/q35/APIC.thread-count b/tests/data/acpi/q35/APIC.thread-count index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..c27e87fcf1c04a2e75f9a20f2bc6a28f19aadf66 100644 GIT binary patch literal 544 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 YBHITON2VDSAnpK(F*YFF1LDH~0O^Si0RR91 literal 0 HcmV?d00001 diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index 4d139d7f6b..dfb8523c8b 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1,4 +1 @@ /* List of comma-separated changed AML files to ignore */ -"tests/data/acpi/q35/APIC.thread-count", -"tests/data/acpi/q35/DSDT.thread-count", -"tests/data/acpi/q35/FACP.thread-count", From 7cb953ca1956d4ac5d8ad798321fe3fde765befc Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 23 Oct 2023 17:46:33 +0800 Subject: [PATCH 698/974] tests: bios-tables-test: Prepare the ACPI table change for smbios type4 thread count2 test Following the guidelines in tests/qtest/bios-tables-test.c, this is step 1 - 3. List the ACPI tables that will be added to test the thread count2 field of smbios type4 table. Signed-off-by: Zhao Liu Reviewed-by: Michael S. Tsirkin Message-Id: <20231023094635.1588282-15-zhao1.liu@linux.intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/q35/APIC.thread-count2 | 0 tests/data/acpi/q35/DSDT.thread-count2 | 0 tests/data/acpi/q35/FACP.thread-count2 | 0 tests/qtest/bios-tables-test-allowed-diff.h | 3 +++ 4 files changed, 3 insertions(+) create mode 100644 tests/data/acpi/q35/APIC.thread-count2 create mode 100644 tests/data/acpi/q35/DSDT.thread-count2 create mode 100644 tests/data/acpi/q35/FACP.thread-count2 diff --git a/tests/data/acpi/q35/APIC.thread-count2 b/tests/data/acpi/q35/APIC.thread-count2 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/data/acpi/q35/DSDT.thread-count2 b/tests/data/acpi/q35/DSDT.thread-count2 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/data/acpi/q35/FACP.thread-count2 b/tests/data/acpi/q35/FACP.thread-count2 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..d17d80e21a 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,4 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/q35/APIC.thread-count2", +"tests/data/acpi/q35/DSDT.thread-count2", +"tests/data/acpi/q35/FACP.thread-count2", From 198eee0cc17d74f17ecd54ddf72681f421f7bc43 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 23 Oct 2023 17:46:34 +0800 Subject: [PATCH 699/974] tests: bios-tables-test: Add test for smbios type4 thread count2 This tests the commit 7298fd7de5551 ("hw/smbios: Fix thread count in type4"). In smbios_build_type_4_table() (hw/smbios/smbios.c), if the number of threads in the socket is more than 255, then smbios type4 table encodes threads per socket into the thread count2 field. So for the topology in this case, there're the following considerations: 1. threads per socket should be more than 255 to ensure we could cover the thread count2 field. 2. The original bug was that threads per socket was miscalculated, so now we should configure as many topology levels as possible (multiple dies, no module since x86 hasn't supported it) to cover more general topology scenarios, to ensure that the threads per socket encoded in the thread count2 field is correct. 3. For the more general topology, we should also add "cpus" (presented threads for machine) and "maxcpus" (total threads for machine) to make sure that configuring unpluged CPUs in smp (cpus < maxcpus) does not affect the correctness of threads per socket for thread count2 field. Note we don't consider the topology with multiple sockets since this topology would create too many vCPUs (more than 255 threads per socket with at least 2 sockets, which may cause the failure "Number of hotpluggable cpus requested (*) exceeds the maximum cpus supported by KVM (*) socket_accept failed: Resource temporarily unavailable"), and the calculation of threads per socket has already been covered by "thread count" test case. Based on these considerations, select the topology as the follow: -smp cpus=210,maxcpus=260,dies=2,cores=65,threads=2 The expected thread count2 = threads per socket = threads (2) * cores (65) * dies (2) = 260. Suggested-by: Igor Mammedov Signed-off-by: Zhao Liu Message-Id: <20231023094635.1588282-16-zhao1.liu@linux.intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index 395ed7f9ff..71af5cf69f 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -96,6 +96,7 @@ typedef struct { uint8_t smbios_core_count; uint16_t smbios_core_count2; uint8_t smbios_thread_count; + uint16_t smbios_thread_count2; uint8_t *required_struct_types; int required_struct_types_len; int type4_count; @@ -644,6 +645,7 @@ static void smbios_cpu_test(test_data *data, uint32_t addr, uint8_t thread_count, expected_thread_count = data->smbios_thread_count; uint16_t speed, expected_speed[2]; uint16_t core_count2, expected_core_count2 = data->smbios_core_count2; + uint16_t thread_count2, expected_thread_count2 = data->smbios_thread_count2; int offset[2]; int i; @@ -680,6 +682,15 @@ static void smbios_cpu_test(test_data *data, uint32_t addr, if (expected_core_count == 0xFF && expected_core_count2) { g_assert_cmpuint(core_count2, ==, expected_core_count2); } + + thread_count2 = qtest_readw(data->qts, + addr + offsetof(struct smbios_type_4, + thread_count2)); + + /* Thread Count has reached its limit, checking Thread Count 2 */ + if (expected_thread_count == 0xFF && expected_thread_count2) { + g_assert_cmpuint(thread_count2, ==, expected_thread_count2); + } } } @@ -1050,6 +1061,7 @@ static void test_acpi_q35_tcg_thread_count(void) .required_struct_types = base_required_struct_types, .required_struct_types_len = ARRAY_SIZE(base_required_struct_types), .smbios_thread_count = 27, + .smbios_thread_count2 = 27, }; test_acpi_one("-machine smbios-entry-point-type=64 " @@ -1058,6 +1070,23 @@ static void test_acpi_q35_tcg_thread_count(void) free_test_data(&data); } +static void test_acpi_q35_tcg_thread_count2(void) +{ + test_data data = { + .machine = MACHINE_Q35, + .variant = ".thread-count2", + .required_struct_types = base_required_struct_types, + .required_struct_types_len = ARRAY_SIZE(base_required_struct_types), + .smbios_thread_count = 0xFF, + .smbios_thread_count2 = 260, + }; + + test_acpi_one("-machine smbios-entry-point-type=64 " + "-smp cpus=210,maxcpus=260,dies=2,cores=65,threads=2", + &data); + free_test_data(&data); +} + static void test_acpi_q35_tcg_bridge(void) { test_data data = {}; @@ -2228,6 +2257,8 @@ int main(int argc, char *argv[]) test_acpi_q35_tcg_core_count2); qtest_add_func("acpi/q35/thread-count", test_acpi_q35_tcg_thread_count); + qtest_add_func("acpi/q35/thread-count2", + test_acpi_q35_tcg_thread_count2); } if (qtest_has_device("virtio-iommu-pci")) { qtest_add_func("acpi/q35/viot", test_acpi_q35_viot); From f58db4eeb1c7f850de86d4efece653d1018f6eae Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 23 Oct 2023 17:46:35 +0800 Subject: [PATCH 700/974] tests: bios-tables-test: Add ACPI table binaries for smbios type4 thread count2 test Following the guidelines in tests/qtest/bios-tables-test.c, this is step 5 and 6. Changes in the tables: FACP: +/* + * Intel ACPI Component Architecture + * AML/ASL+ Disassembler version 20200925 (64-bit version) + * Copyright (c) 2000 - 2020 Intel Corporation + * + * Disassembly of /tmp/aml-CNE3C2, Mon Oct 23 15:25:01 2023 + * + * ACPI Data Table [FACP] + * + * Format: [HexOffset DecimalOffset ByteLength] FieldName : FieldValue + */ + +[000h 0000 4] Signature : "FACP" [Fixed ACPI Description Table (FADT)] +[004h 0004 4] Table Length : 000000F4 +[008h 0008 1] Revision : 03 +[009h 0009 1] Checksum : B3 +[00Ah 0010 6] Oem ID : "BOCHS " +[010h 0016 8] Oem Table ID : "BXPC " +[018h 0024 4] Oem Revision : 00000001 +[01Ch 0028 4] Asl Compiler ID : "BXPC" +[020h 0032 4] Asl Compiler Revision : 00000001 + +[024h 0036 4] FACS Address : 00000000 +[028h 0040 4] DSDT Address : 00000000 +[02Ch 0044 1] Model : 01 +[02Dh 0045 1] PM Profile : 00 [Unspecified] +[02Eh 0046 2] SCI Interrupt : 0009 +[030h 0048 4] SMI Command Port : 000000B2 +[034h 0052 1] ACPI Enable Value : 02 +[035h 0053 1] ACPI Disable Value : 03 +[036h 0054 1] S4BIOS Command : 00 +[037h 0055 1] P-State Control : 00 +[038h 0056 4] PM1A Event Block Address : 00000600 +[03Ch 0060 4] PM1B Event Block Address : 00000000 +[040h 0064 4] PM1A Control Block Address : 00000604 +[044h 0068 4] PM1B Control Block Address : 00000000 +[048h 0072 4] PM2 Control Block Address : 00000000 +[04Ch 0076 4] PM Timer Block Address : 00000608 +[050h 0080 4] GPE0 Block Address : 00000620 +[054h 0084 4] GPE1 Block Address : 00000000 +[058h 0088 1] PM1 Event Block Length : 04 +[059h 0089 1] PM1 Control Block Length : 02 +[05Ah 0090 1] PM2 Control Block Length : 00 +[05Bh 0091 1] PM Timer Block Length : 04 +[05Ch 0092 1] GPE0 Block Length : 10 +[05Dh 0093 1] GPE1 Block Length : 00 +[05Eh 0094 1] GPE1 Base Offset : 00 +[05Fh 0095 1] _CST Support : 00 +[060h 0096 2] C2 Latency : 0FFF +[062h 0098 2] C3 Latency : 0FFF +[064h 0100 2] CPU Cache Size : 0000 +[066h 0102 2] Cache Flush Stride : 0000 +[068h 0104 1] Duty Cycle Offset : 00 +[069h 0105 1] Duty Cycle Width : 00 +[06Ah 0106 1] RTC Day Alarm Index : 00 +[06Bh 0107 1] RTC Month Alarm Index : 00 +[06Ch 0108 1] RTC Century Index : 32 +[06Dh 0109 2] Boot Flags (decoded below) : 0002 + Legacy Devices Supported (V2) : 0 + 8042 Present on ports 60/64 (V2) : 1 + VGA Not Present (V4) : 0 + MSI Not Supported (V4) : 0 + PCIe ASPM Not Supported (V4) : 0 + CMOS RTC Not Present (V5) : 0 +[06Fh 0111 1] Reserved : 00 +[070h 0112 4] Flags (decoded below) : 000484A5 + WBINVD instruction is operational (V1) : 1 + WBINVD flushes all caches (V1) : 0 + All CPUs support C1 (V1) : 1 + C2 works on MP system (V1) : 0 + Control Method Power Button (V1) : 0 + Control Method Sleep Button (V1) : 1 + RTC wake not in fixed reg space (V1) : 0 + RTC can wake system from S4 (V1) : 1 + 32-bit PM Timer (V1) : 0 + Docking Supported (V1) : 0 + Reset Register Supported (V2) : 1 + Sealed Case (V3) : 0 + Headless - No Video (V3) : 0 + Use native instr after SLP_TYPx (V3) : 0 + PCIEXP_WAK Bits Supported (V4) : 0 + Use Platform Timer (V4) : 1 + RTC_STS valid on S4 wake (V4) : 0 + Remote Power-on capable (V4) : 0 + Use APIC Cluster Model (V4) : 1 + Use APIC Physical Destination Mode (V4) : 0 + Hardware Reduced (V5) : 0 + Low Power S0 Idle (V5) : 0 + +[074h 0116 12] Reset Register : [Generic Address Structure] +[074h 0116 1] Space ID : 01 [SystemIO] +[075h 0117 1] Bit Width : 08 +[076h 0118 1] Bit Offset : 00 +[077h 0119 1] Encoded Access Width : 00 [Undefined/Legacy] +[078h 0120 8] Address : 0000000000000CF9 + +[080h 0128 1] Value to cause reset : 0F +[081h 0129 2] ARM Flags (decoded below) : 0000 + PSCI Compliant : 0 + Must use HVC for PSCI : 0 + +[083h 0131 1] FADT Minor Revision : 00 +[084h 0132 8] FACS Address : 0000000000000000 +[08Ch 0140 8] DSDT Address : 0000000000000000 +[094h 0148 12] PM1A Event Block : [Generic Address Structure] +[094h 0148 1] Space ID : 01 [SystemIO] +[095h 0149 1] Bit Width : 20 +[096h 0150 1] Bit Offset : 00 +[097h 0151 1] Encoded Access Width : 00 [Undefined/Legacy] +[098h 0152 8] Address : 0000000000000600 + +[0A0h 0160 12] PM1B Event Block : [Generic Address Structure] +[0A0h 0160 1] Space ID : 00 [SystemMemory] +[0A1h 0161 1] Bit Width : 00 +[0A2h 0162 1] Bit Offset : 00 +[0A3h 0163 1] Encoded Access Width : 00 [Undefined/Legacy] +[0A4h 0164 8] Address : 0000000000000000 + +[0ACh 0172 12] PM1A Control Block : [Generic Address Structure] +[0ACh 0172 1] Space ID : 01 [SystemIO] +[0ADh 0173 1] Bit Width : 10 +[0AEh 0174 1] Bit Offset : 00 +[0AFh 0175 1] Encoded Access Width : 00 [Undefined/Legacy] +[0B0h 0176 8] Address : 0000000000000604 + +[0B8h 0184 12] PM1B Control Block : [Generic Address Structure] +[0B8h 0184 1] Space ID : 00 [SystemMemory] +[0B9h 0185 1] Bit Width : 00 +[0BAh 0186 1] Bit Offset : 00 +[0BBh 0187 1] Encoded Access Width : 00 [Undefined/Legacy] +[0BCh 0188 8] Address : 0000000000000000 + +[0C4h 0196 12] PM2 Control Block : [Generic Address Structure] +[0C4h 0196 1] Space ID : 00 [SystemMemory] +[0C5h 0197 1] Bit Width : 00 +[0C6h 0198 1] Bit Offset : 00 +[0C7h 0199 1] Encoded Access Width : 00 [Undefined/Legacy] +[0C8h 0200 8] Address : 0000000000000000 + +[0D0h 0208 12] PM Timer Block : [Generic Address Structure] +[0D0h 0208 1] Space ID : 01 [SystemIO] +[0D1h 0209 1] Bit Width : 20 +[0D2h 0210 1] Bit Offset : 00 +[0D3h 0211 1] Encoded Access Width : 00 [Undefined/Legacy] +[0D4h 0212 8] Address : 0000000000000608 + +[0DCh 0220 12] GPE0 Block : [Generic Address Structure] +[0DCh 0220 1] Space ID : 01 [SystemIO] +[0DDh 0221 1] Bit Width : 80 +[0DEh 0222 1] Bit Offset : 00 +[0DFh 0223 1] Encoded Access Width : 00 [Undefined/Legacy] +[0E0h 0224 8] Address : 0000000000000620 + +[0E8h 0232 12] GPE1 Block : [Generic Address Structure] +[0E8h 0232 1] Space ID : 00 [SystemMemory] +[0E9h 0233 1] Bit Width : 00 +[0EAh 0234 1] Bit Offset : 00 +[0EBh 0235 1] Encoded Access Width : 00 [Undefined/Legacy] +[0ECh 0236 8] Address : 0000000000000000 ... APIC: +/* + * Intel ACPI Component Architecture + * AML/ASL+ Disassembler version 20200925 (64-bit version) + * Copyright (c) 2000 - 2020 Intel Corporation + * + * Disassembly of /tmp/aml-WKE3C2, Mon Oct 23 15:25:01 2023 + * + * ACPI Data Table [APIC] + * + * Format: [HexOffset DecimalOffset ByteLength] FieldName : FieldValue + */ + +[000h 0000 4] Signature : "APIC" [Multiple APIC Description Table (MADT)] +[004h 0004 4] Table Length : 00000CA6 +[008h 0008 1] Revision : 03 +[009h 0009 1] Checksum : 2C +[00Ah 0010 6] Oem ID : "BOCHS " +[010h 0016 8] Oem Table ID : "BXPC " +[018h 0024 4] Oem Revision : 00000001 +[01Ch 0028 4] Asl Compiler ID : "BXPC" +[020h 0032 4] Asl Compiler Revision : 00000001 + +[024h 0036 4] Local Apic Address : FEE00000 +[028h 0040 4] Flags (decoded below) : 00000001 + PC-AT Compatibility : 1 + +[02Ch 0044 1] Subtable Type : 00 [Processor Local APIC] +[02Dh 0045 1] Length : 08 +[02Eh 0046 1] Processor ID : 00 +[02Fh 0047 1] Local Apic ID : 00 +[030h 0048 4] Flags (decoded below) : 00000001 + Processor Enabled : 1 + Runtime Online Capable : 0 + +[034h 0052 1] Subtable Type : 00 [Processor Local APIC] +[035h 0053 1] Length : 08 +[036h 0054 1] Processor ID : 01 +[037h 0055 1] Local Apic ID : 01 +[038h 0056 4] Flags (decoded below) : 00000001 + Processor Enabled : 1 + Runtime Online Capable : 0 [snip] +[434h 1076 1] Subtable Type : 00 [Processor Local APIC] +[435h 1077 1] Length : 08 +[436h 1078 1] Processor ID : 81 +[437h 1079 1] Local Apic ID : 81 +[438h 1080 4] Flags (decoded below) : 00000001 + Processor Enabled : 1 + Runtime Online Capable : 0 + +[43Ch 1084 1] Subtable Type : 09 [Processor Local x2APIC] +[43Dh 1085 1] Length : 10 +[43Eh 1086 2] Reserved : 0000 +[440h 1088 4] Processor x2Apic ID : 00000100 +[444h 1092 4] Flags (decoded below) : 00000001 + Processor Enabled : 1 +[448h 1096 4] Processor UID : 00000082 [snip] +[C4Ch 3148 1] Subtable Type : 09 [Processor Local x2APIC] +[C4Dh 3149 1] Length : 10 +[C4Eh 3150 2] Reserved : 0000 +[C50h 3152 4] Processor x2Apic ID : 00000181 +[C54h 3156 4] Flags (decoded below) : 00000000 + Processor Enabled : 0 +[C58h 3160 4] Processor UID : 00000103 + +[C5Ch 3164 1] Subtable Type : 01 [I/O APIC] +[C5Dh 3165 1] Length : 0C +[C5Eh 3166 1] I/O Apic ID : 00 +[C5Fh 3167 1] Reserved : 00 +[C60h 3168 4] Address : FEC00000 +[C64h 3172 4] Interrupt : 00000000 + +[C68h 3176 1] Subtable Type : 02 [Interrupt Source Override] +[C69h 3177 1] Length : 0A +[C6Ah 3178 1] Bus : 00 +[C6Bh 3179 1] Source : 00 +[C6Ch 3180 4] Interrupt : 00000002 +[C70h 3184 2] Flags (decoded below) : 0000 + Polarity : 0 + Trigger Mode : 0 [snip] +[C90h 3216 1] Subtable Type : 02 [Interrupt Source Override] +[C91h 3217 1] Length : 0A +[C92h 3218 1] Bus : 00 +[C93h 3219 1] Source : 0B +[C94h 3220 4] Interrupt : 0000000B +[C98h 3224 2] Flags (decoded below) : 000D + Polarity : 1 + Trigger Mode : 3 + +[C9Ah 3226 1] Subtable Type : 0A [Local x2APIC NMI] +[C9Bh 3227 1] Length : 0C +[C9Ch 3228 2] Flags (decoded below) : 0000 + Polarity : 0 + Trigger Mode : 0 +[C9Eh 3230 4] Processor UID : FFFFFFFF +[CA2h 3234 1] Interrupt Input LINT : 01 +[CA3h 3235 3] Reserved : 000000 ... DSDT: +/* + * Intel ACPI Component Architecture + * AML/ASL+ Disassembler version 20200925 (64-bit version) + * Copyright (c) 2000 - 2020 Intel Corporation + * + * Disassembling to symbolic ASL+ operators + * + * Disassembly of /tmp/aml-CDE3C2, Mon Oct 23 15:25:01 2023 + * + * Original Table Header: + * Signature "DSDT" + * Length 0x000083EA (33770) + * Revision 0x01 **** 32-bit table (V1), no 64-bit math support + * Checksum 0x01 + * OEM ID "BOCHS " + * OEM Table ID "BXPC " + * OEM Revision 0x00000001 (1) + * Compiler ID "BXPC" + * Compiler Version 0x00000001 (1) + */ +DefinitionBlock ("", "DSDT", 1, "BOCHS ", "BXPC ", 0x00000001) +{ + Scope (\) + { + OperationRegion (DBG, SystemIO, 0x0402, One) + Field (DBG, ByteAcc, NoLock, Preserve) + { + DBGB, 8 + } + + Method (DBUG, 1, NotSerialized) + { + ToHexString (Arg0, Local0) + ToBuffer (Local0, Local0) + Local1 = (SizeOf (Local0) - One) + Local2 = Zero + While ((Local2 < Local1)) + { + DBGB = DerefOf (Local0 [Local2]) + Local2++ + } + + DBGB = 0x0A + } + } [snip] + Processor (C000, 0x00, 0x00000000, 0x00) + { + Method (_STA, 0, Serialized) // _STA: Status + { + Return (CSTA (Zero)) + } + + Name (_MAT, Buffer (0x08) // _MAT: Multiple APIC Table Entry + { + 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 // ........ + }) + Method (_OST, 3, Serialized) // _OST: OSPM Status Indication + { + COST (Zero, Arg0, Arg1, Arg2) + } + } [snip] + Processor (C081, 0x81, 0x00000000, 0x00) + { + Method (_STA, 0, Serialized) // _STA: Status + { + Return (CSTA (0x81)) + } + + Name (_MAT, Buffer (0x08) // _MAT: Multiple APIC Table Entry + { + 0x00, 0x08, 0x81, 0x81, 0x01, 0x00, 0x00, 0x00 // ........ + }) + Method (_EJ0, 1, NotSerialized) // _EJx: Eject Device, x=0-9 + { + CEJ0 (0x81) + } + + Method (_OST, 3, Serialized) // _OST: OSPM Status Indication + { + COST (0x81, Arg0, Arg1, Arg2) + } + } ... Signed-off-by: Zhao Liu Message-Id: <20231023094635.1588282-17-zhao1.liu@linux.intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/q35/APIC.thread-count2 | Bin 0 -> 3238 bytes tests/data/acpi/q35/DSDT.thread-count2 | Bin 0 -> 33770 bytes tests/data/acpi/q35/FACP.thread-count2 | Bin 0 -> 244 bytes tests/qtest/bios-tables-test-allowed-diff.h | 3 --- 4 files changed, 3 deletions(-) diff --git a/tests/data/acpi/q35/APIC.thread-count2 b/tests/data/acpi/q35/APIC.thread-count2 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ac200ab7aa6e2fc4b6995ba0dfc1a77ae32e0412 100644 GIT binary patch literal 3238 zcmXxmWmuJI9E9-!M9(>ZVz+{diQP>If(43!4Ju%FCv513Zg;2K-R*X_Zg+Qg=sv^D za)I~zqdtJwf5Zy^48jY&4n3|NNCMT;YDQf-tYJ&!9YO30>q1vdC+PJaWq>0+JsoJcW z+Pt~iqJ`SBrP`{M+Pbycrj6RRt=g`g+P=N2p9>=C>2ik-YR8Uhr%q}{hT6Ha+NF!y zwX52#o7%m*+M|b>nW^^dsrKrn_U^6r>7(}TtM==s_V2F_7@!Uus16#W4j!xy8KMpy zsty~b4j-|Fsg4?@jvlRMWvSWOYEF)ttN$mFF=OPsJT*UG9XnPnC{POv)p6t0 z@#EDA6V!%^G#>T6Ntzb^UsE!v=NZMs?FBb@OI*%NBL(R(0Dpb^CU8#}0MpPIcEV zwYplZsbQn3;h*rp=cDX6`)Hd7f*(J|=AodEwRte;-8K&gy~pMOp^vk9Na*8j9u)cn zn}>xy(dL1n_u4!(^gf#hhd#;X;h|5qd4T9sY#t)|RGSBhKF#J~qEEMZpy)Gf9xD1w zn+Jw84 zY#u`TQajDQ%ucs2w|OALE3UA4DCsM09!z?_onc>P^MLZ>SKBoX9^e%$6@&`;QT_LFwL{glli;pabXb5Q7KYz_DfPo)u4KeVetw9EUvNg=W&$b2{_{G*x1HalDY~VLr!wvjyYrug&Yz;Z^ zr>#K;{<1afz~8n89{9)B&;$S48hqfet>Fib*!lw?ApG3<`_&%;fp)cjAKZC_*i)31D_HTLsR$^jsE`>3!k9(B_-89#llw~2`~JH{{h;7;sF2v literal 0 HcmV?d00001 diff --git a/tests/data/acpi/q35/DSDT.thread-count2 b/tests/data/acpi/q35/DSDT.thread-count2 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..3a0cb8c581c8cc630a2ec21712b7f8b75fcad1c8 100644 GIT binary patch literal 33770 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 literal 0 HcmV?d00001 diff --git a/tests/data/acpi/q35/FACP.thread-count2 b/tests/data/acpi/q35/FACP.thread-count2 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..31fa5dd19c213034eef4eeefa6a04e61dadd8a2a 100644 GIT binary patch literal 244 zcmZ>BbPo8!z`($~*~#D8BUr&HBEVSz2pEB4AU24G0Y(N+hD|^Y6El!tgNU*~X%LSC z$X0-fGcm9T0LA|E|L2FOWMD92VqjR>!otAF!NBm72Obk1 YBHITON2VDSAnpK(F*YFF1LDH~0O^Si0RR91 literal 0 HcmV?d00001 diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index d17d80e21a..dfb8523c8b 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1,4 +1 @@ /* List of comma-separated changed AML files to ignore */ -"tests/data/acpi/q35/APIC.thread-count2", -"tests/data/acpi/q35/DSDT.thread-count2", -"tests/data/acpi/q35/FACP.thread-count2", From 629df5cc23cc9aec5d115cc9be3456458e2b44fa Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Mon, 23 Oct 2023 15:02:06 +0100 Subject: [PATCH 701/974] hw/cxl: Use a switch to explicitly check size in caps_reg_read() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bring this read function inline with the others that do check for unexpected size values. Also reduces line lengths to sub 80 chars. Signed-off-by: Jonathan Cameron Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Fan Ni Message-Id: <20231023140210.3089-2-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-device-utils.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/hw/cxl/cxl-device-utils.c b/hw/cxl/cxl-device-utils.c index bd68328032..eb7195272e 100644 --- a/hw/cxl/cxl-device-utils.c +++ b/hw/cxl/cxl-device-utils.c @@ -32,10 +32,13 @@ static uint64_t caps_reg_read(void *opaque, hwaddr offset, unsigned size) { CXLDeviceState *cxl_dstate = opaque; - if (size == 4) { - return cxl_dstate->caps_reg_state32[offset / sizeof(*cxl_dstate->caps_reg_state32)]; - } else { - return cxl_dstate->caps_reg_state64[offset / sizeof(*cxl_dstate->caps_reg_state64)]; + switch (size) { + case 4: + return cxl_dstate->caps_reg_state32[offset / size]; + case 8: + return cxl_dstate->caps_reg_state64[offset / size]; + default: + g_assert_not_reached(); } } From 388d6b574e282b02e8180f4cba428316a404deea Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Mon, 23 Oct 2023 15:02:07 +0100 Subject: [PATCH 702/974] hw/cxl: Use switch statements for read and write of cachemem registers Establishing that only register accesses of size 4 and 8 can occur using these functions requires looking at their callers. Make it easier to see that by using switch statements. Assertions are used to enforce that the register storage is of the matching size, allowing fixed values to be used for divisors of the array indices. Suggested-by: Michael Tokarev Signed-off-by: Jonathan Cameron Reviewed-by: Fan Ni Message-Id: <20231023140210.3089-3-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-component-utils.c | 66 +++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 23 deletions(-) diff --git a/hw/cxl/cxl-component-utils.c b/hw/cxl/cxl-component-utils.c index f3bbf0fd13..9d4f4bc8d4 100644 --- a/hw/cxl/cxl-component-utils.c +++ b/hw/cxl/cxl-component-utils.c @@ -67,16 +67,24 @@ static uint64_t cxl_cache_mem_read_reg(void *opaque, hwaddr offset, CXLComponentState *cxl_cstate = opaque; ComponentRegisters *cregs = &cxl_cstate->crb; - if (size == 8) { + switch (size) { + case 4: + if (cregs->special_ops && cregs->special_ops->read) { + return cregs->special_ops->read(cxl_cstate, offset, 4); + } else { + QEMU_BUILD_BUG_ON(sizeof(*cregs->cache_mem_registers) != 4); + return cregs->cache_mem_registers[offset / 4]; + } + case 8: qemu_log_mask(LOG_UNIMP, "CXL 8 byte cache mem registers not implemented\n"); return 0; - } - - if (cregs->special_ops && cregs->special_ops->read) { - return cregs->special_ops->read(cxl_cstate, offset, size); - } else { - return cregs->cache_mem_registers[offset / sizeof(*cregs->cache_mem_registers)]; + default: + /* + * In line with specifiction limitaions on access sizes, this + * routine is not called with other sizes. + */ + g_assert_not_reached(); } } @@ -117,25 +125,37 @@ static void cxl_cache_mem_write_reg(void *opaque, hwaddr offset, uint64_t value, ComponentRegisters *cregs = &cxl_cstate->crb; uint32_t mask; - if (size == 8) { + switch (size) { + case 4: { + QEMU_BUILD_BUG_ON(sizeof(*cregs->cache_mem_regs_write_mask) != 4); + QEMU_BUILD_BUG_ON(sizeof(*cregs->cache_mem_registers) != 4); + mask = cregs->cache_mem_regs_write_mask[offset / 4]; + value &= mask; + /* RO bits should remain constant. Done by reading existing value */ + value |= ~mask & cregs->cache_mem_registers[offset / 4]; + if (cregs->special_ops && cregs->special_ops->write) { + cregs->special_ops->write(cxl_cstate, offset, value, size); + return; + } + + if (offset >= A_CXL_HDM_DECODER_CAPABILITY && + offset <= A_CXL_HDM_DECODER3_TARGET_LIST_HI) { + dumb_hdm_handler(cxl_cstate, offset, value); + } else { + cregs->cache_mem_registers[offset / 4] = value; + } + return; + } + case 8: qemu_log_mask(LOG_UNIMP, "CXL 8 byte cache mem registers not implemented\n"); return; - } - mask = cregs->cache_mem_regs_write_mask[offset / sizeof(*cregs->cache_mem_regs_write_mask)]; - value &= mask; - /* RO bits should remain constant. Done by reading existing value */ - value |= ~mask & cregs->cache_mem_registers[offset / sizeof(*cregs->cache_mem_registers)]; - if (cregs->special_ops && cregs->special_ops->write) { - cregs->special_ops->write(cxl_cstate, offset, value, size); - return; - } - - if (offset >= A_CXL_HDM_DECODER_CAPABILITY && - offset <= A_CXL_HDM_DECODER3_TARGET_LIST_HI) { - dumb_hdm_handler(cxl_cstate, offset, value); - } else { - cregs->cache_mem_registers[offset / sizeof(*cregs->cache_mem_registers)] = value; + default: + /* + * In line with specifiction limitaions on access sizes, this + * routine is not called with other sizes. + */ + g_assert_not_reached(); } } From b34ae3c9064a976e718dc96e454d32c1d8409eba Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Mon, 23 Oct 2023 15:02:08 +0100 Subject: [PATCH 703/974] hw/cxl: CXLDVSECPortExtensions renamed to CXLDVSECPortExt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Done to reduce line lengths where this is used. Ext seems sufficiently obvious that it need not be spelt out fully. Signed-off-by: Jonathan Cameron Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Fan Ni Message-Id: <20231023140210.3089-4-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-component-utils.c | 49 ++++++++++++++++++++-------------- hw/pci-bridge/cxl_downstream.c | 2 +- hw/pci-bridge/cxl_root_port.c | 2 +- hw/pci-bridge/cxl_upstream.c | 2 +- include/hw/cxl/cxl_pci.h | 6 ++--- 5 files changed, 35 insertions(+), 26 deletions(-) diff --git a/hw/cxl/cxl-component-utils.c b/hw/cxl/cxl-component-utils.c index 9d4f4bc8d4..1f4ea11640 100644 --- a/hw/cxl/cxl-component-utils.c +++ b/hw/cxl/cxl-component-utils.c @@ -393,26 +393,35 @@ void cxl_component_create_dvsec(CXLComponentState *cxl, case NON_CXL_FUNCTION_MAP_DVSEC: break; /* Not yet implemented */ case EXTENSIONS_PORT_DVSEC: - wmask[offset + offsetof(CXLDVSECPortExtensions, control)] = 0x0F; - wmask[offset + offsetof(CXLDVSECPortExtensions, control) + 1] = 0x40; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_bus_base)] = 0xFF; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_bus_limit)] = 0xFF; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_memory_base)] = 0xF0; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_memory_base) + 1] = 0xFF; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_memory_limit)] = 0xF0; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_memory_limit) + 1] = 0xFF; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_base)] = 0xF0; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_base) + 1] = 0xFF; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_limit)] = 0xF0; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_limit) + 1] = 0xFF; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_base_high)] = 0xFF; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_base_high) + 1] = 0xFF; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_base_high) + 2] = 0xFF; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_base_high) + 3] = 0xFF; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_limit_high)] = 0xFF; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_limit_high) + 1] = 0xFF; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_limit_high) + 2] = 0xFF; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_limit_high) + 3] = 0xFF; + wmask[offset + offsetof(CXLDVSECPortExt, control)] = 0x0F; + wmask[offset + offsetof(CXLDVSECPortExt, control) + 1] = 0x40; + wmask[offset + offsetof(CXLDVSECPortExt, alt_bus_base)] = 0xFF; + wmask[offset + offsetof(CXLDVSECPortExt, alt_bus_limit)] = 0xFF; + wmask[offset + offsetof(CXLDVSECPortExt, alt_memory_base)] = 0xF0; + wmask[offset + offsetof(CXLDVSECPortExt, alt_memory_base) + 1] = 0xFF; + wmask[offset + offsetof(CXLDVSECPortExt, alt_memory_limit)] = 0xF0; + wmask[offset + offsetof(CXLDVSECPortExt, alt_memory_limit) + 1] = 0xFF; + wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_base)] = 0xF0; + wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_base) + 1] = 0xFF; + wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_limit)] = 0xF0; + wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_limit) + 1] = + 0xFF; + wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_base_high)] = + 0xFF; + wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_base_high) + 1] = + 0xFF; + wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_base_high) + 2] = + 0xFF; + wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_base_high) + 3] = + 0xFF; + wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_limit_high)] = + 0xFF; + wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_limit_high) + 1] = + 0xFF; + wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_limit_high) + 2] = + 0xFF; + wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_limit_high) + 3] = + 0xFF; break; case GPF_PORT_DVSEC: wmask[offset + offsetof(CXLDVSECPortGPF, phase1_ctrl)] = 0x0F; diff --git a/hw/pci-bridge/cxl_downstream.c b/hw/pci-bridge/cxl_downstream.c index 5a2b749c8e..8c0f759add 100644 --- a/hw/pci-bridge/cxl_downstream.c +++ b/hw/pci-bridge/cxl_downstream.c @@ -98,7 +98,7 @@ static void build_dvsecs(CXLComponentState *cxl) { uint8_t *dvsec; - dvsec = (uint8_t *)&(CXLDVSECPortExtensions){ 0 }; + dvsec = (uint8_t *)&(CXLDVSECPortExt){ 0 }; cxl_component_create_dvsec(cxl, CXL2_DOWNSTREAM_PORT, EXTENSIONS_PORT_DVSEC_LENGTH, EXTENSIONS_PORT_DVSEC, diff --git a/hw/pci-bridge/cxl_root_port.c b/hw/pci-bridge/cxl_root_port.c index 7dfd20aa67..8f97697631 100644 --- a/hw/pci-bridge/cxl_root_port.c +++ b/hw/pci-bridge/cxl_root_port.c @@ -107,7 +107,7 @@ static void build_dvsecs(CXLComponentState *cxl) { uint8_t *dvsec; - dvsec = (uint8_t *)&(CXLDVSECPortExtensions){ 0 }; + dvsec = (uint8_t *)&(CXLDVSECPortExt){ 0 }; cxl_component_create_dvsec(cxl, CXL2_ROOT_PORT, EXTENSIONS_PORT_DVSEC_LENGTH, EXTENSIONS_PORT_DVSEC, diff --git a/hw/pci-bridge/cxl_upstream.c b/hw/pci-bridge/cxl_upstream.c index a57806fb31..b81bb5fec9 100644 --- a/hw/pci-bridge/cxl_upstream.c +++ b/hw/pci-bridge/cxl_upstream.c @@ -116,7 +116,7 @@ static void build_dvsecs(CXLComponentState *cxl) { uint8_t *dvsec; - dvsec = (uint8_t *)&(CXLDVSECPortExtensions){ + dvsec = (uint8_t *)&(CXLDVSECPortExt){ .status = 0x1, /* Port Power Management Init Complete */ }; cxl_component_create_dvsec(cxl, CXL2_UPSTREAM_PORT, diff --git a/include/hw/cxl/cxl_pci.h b/include/hw/cxl/cxl_pci.h index 407be95b9e..ddf01a543b 100644 --- a/include/hw/cxl/cxl_pci.h +++ b/include/hw/cxl/cxl_pci.h @@ -86,7 +86,7 @@ typedef struct CXLDVSECDevice { QEMU_BUILD_BUG_ON(sizeof(CXLDVSECDevice) != 0x38); /* CXL 2.0 - 8.1.5 (ID 0003) */ -typedef struct CXLDVSECPortExtensions { +typedef struct CXLDVSECPortExt { DVSECHeader hdr; uint16_t status; uint16_t control; @@ -100,8 +100,8 @@ typedef struct CXLDVSECPortExtensions { uint32_t alt_prefetch_limit_high; uint32_t rcrb_base; uint32_t rcrb_base_high; -} CXLDVSECPortExtensions; -QEMU_BUILD_BUG_ON(sizeof(CXLDVSECPortExtensions) != 0x28); +} CXLDVSECPortExt; +QEMU_BUILD_BUG_ON(sizeof(CXLDVSECPortExt) != 0x28); #define PORT_CONTROL_OFFSET 0xc #define PORT_CONTROL_UNMASK_SBR 1 From b342489ae795f5c2a9f7a565bac8443ccb11b0ce Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Mon, 23 Oct 2023 15:02:09 +0100 Subject: [PATCH 704/974] hw/cxl: Line length reductions Michael Tsirkin observed that there were some unnecessarily long lines in the CXL code in a recent review. This patch is intended to rectify that where it does not hurt readability. Signed-off-by: Jonathan Cameron Reviewed-by: Michael Tokarev Reviewed-by: Fan Ni Message-Id: <20231023140210.3089-5-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-cdat.c | 3 ++- hw/cxl/cxl-component-utils.c | 14 ++++++++------ hw/cxl/cxl-events.c | 9 ++++++--- hw/cxl/cxl-mailbox-utils.c | 21 ++++++++++++++------- hw/mem/cxl_type3.c | 31 +++++++++++++++++++------------ hw/mem/cxl_type3_stubs.c | 5 +++-- include/hw/cxl/cxl_component.h | 3 ++- include/hw/cxl/cxl_device.h | 5 +++-- include/hw/cxl/cxl_events.h | 3 ++- 9 files changed, 59 insertions(+), 35 deletions(-) diff --git a/hw/cxl/cxl-cdat.c b/hw/cxl/cxl-cdat.c index d246d6885b..639a2db3e1 100644 --- a/hw/cxl/cxl-cdat.c +++ b/hw/cxl/cxl-cdat.c @@ -60,7 +60,8 @@ static void ct3_build_cdat(CDATObject *cdat, Error **errp) return; } - cdat->built_buf_len = cdat->build_cdat_table(&cdat->built_buf, cdat->private); + cdat->built_buf_len = cdat->build_cdat_table(&cdat->built_buf, + cdat->private); if (!cdat->built_buf_len) { /* Build later as not all data available yet */ diff --git a/hw/cxl/cxl-component-utils.c b/hw/cxl/cxl-component-utils.c index 1f4ea11640..5ebd81daf3 100644 --- a/hw/cxl/cxl-component-utils.c +++ b/hw/cxl/cxl-component-utils.c @@ -241,7 +241,8 @@ static void hdm_init_common(uint32_t *reg_state, uint32_t *write_msk, ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, TARGET_COUNT, 1); ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, INTERLEAVE_256B, 1); ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, INTERLEAVE_4K, 1); - ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, POISON_ON_ERR_CAP, 0); + ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, + POISON_ON_ERR_CAP, 0); ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_GLOBAL_CONTROL, HDM_DECODER_ENABLE, 0); write_msk[R_CXL_HDM_DECODER_GLOBAL_CONTROL] = 0x3; @@ -264,15 +265,16 @@ static void hdm_init_common(uint32_t *reg_state, uint32_t *write_msk, } } -void cxl_component_register_init_common(uint32_t *reg_state, uint32_t *write_msk, +void cxl_component_register_init_common(uint32_t *reg_state, + uint32_t *write_msk, enum reg_type type) { int caps = 0; /* - * In CXL 2.0 the capabilities required for each CXL component are such that, - * with the ordering chosen here, a single number can be used to define - * which capabilities should be provided. + * In CXL 2.0 the capabilities required for each CXL component are such + * that, with the ordering chosen here, a single number can be used to + * define which capabilities should be provided. */ switch (type) { case CXL2_DOWNSTREAM_PORT: @@ -449,7 +451,7 @@ void cxl_component_create_dvsec(CXLComponentState *cxl, default: /* Registers are RO for other component types */ break; } - /* There are rw1cs bits in the status register but never set currently */ + /* There are rw1cs bits in the status register but never set */ break; } diff --git a/hw/cxl/cxl-events.c b/hw/cxl/cxl-events.c index 3ddd6369ad..e2172b94b9 100644 --- a/hw/cxl/cxl-events.c +++ b/hw/cxl/cxl-events.c @@ -170,8 +170,10 @@ CXLRetCode cxl_event_get_records(CXLDeviceState *cxlds, CXLGetEventPayload *pl, if (log->overflow_err_count) { pl->flags |= CXL_GET_EVENT_FLAG_OVERFLOW; pl->overflow_err_count = cpu_to_le16(log->overflow_err_count); - pl->first_overflow_timestamp = cpu_to_le64(log->first_overflow_timestamp); - pl->last_overflow_timestamp = cpu_to_le64(log->last_overflow_timestamp); + pl->first_overflow_timestamp = + cpu_to_le64(log->first_overflow_timestamp); + pl->last_overflow_timestamp = + cpu_to_le64(log->last_overflow_timestamp); } pl->record_count = cpu_to_le16(nr); @@ -180,7 +182,8 @@ CXLRetCode cxl_event_get_records(CXLDeviceState *cxlds, CXLGetEventPayload *pl, return CXL_MBOX_SUCCESS; } -CXLRetCode cxl_event_clear_records(CXLDeviceState *cxlds, CXLClearEventPayload *pl) +CXLRetCode cxl_event_clear_records(CXLDeviceState *cxlds, + CXLClearEventPayload *pl) { CXLEventLog *log; uint8_t log_type; diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 434ccc5f6e..ab082ec9de 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -366,9 +366,12 @@ static CXLRetCode cmd_identify_memory_device(struct cxl_cmd *cmd, snprintf(id->fw_revision, 0x10, "BWFW VERSION %02d", 0); - stq_le_p(&id->total_capacity, cxl_dstate->mem_size / CXL_CAPACITY_MULTIPLIER); - stq_le_p(&id->persistent_capacity, cxl_dstate->pmem_size / CXL_CAPACITY_MULTIPLIER); - stq_le_p(&id->volatile_capacity, cxl_dstate->vmem_size / CXL_CAPACITY_MULTIPLIER); + stq_le_p(&id->total_capacity, + cxl_dstate->mem_size / CXL_CAPACITY_MULTIPLIER); + stq_le_p(&id->persistent_capacity, + cxl_dstate->pmem_size / CXL_CAPACITY_MULTIPLIER); + stq_le_p(&id->volatile_capacity, + cxl_dstate->vmem_size / CXL_CAPACITY_MULTIPLIER); stl_le_p(&id->lsa_size, cvc->get_lsa_size(ct3d)); /* 256 poison records */ st24_le_p(id->poison_list_max_mer, 256); @@ -396,13 +399,15 @@ static CXLRetCode cmd_ccls_get_partition_info(struct cxl_cmd *cmd, return CXL_MBOX_INTERNAL_ERROR; } - stq_le_p(&part_info->active_vmem, cxl_dstate->vmem_size / CXL_CAPACITY_MULTIPLIER); + stq_le_p(&part_info->active_vmem, + cxl_dstate->vmem_size / CXL_CAPACITY_MULTIPLIER); /* * When both next_vmem and next_pmem are 0, there is no pending change to * partitioning. */ stq_le_p(&part_info->next_vmem, 0); - stq_le_p(&part_info->active_pmem, cxl_dstate->pmem_size / CXL_CAPACITY_MULTIPLIER); + stq_le_p(&part_info->active_pmem, + cxl_dstate->pmem_size / CXL_CAPACITY_MULTIPLIER); stq_le_p(&part_info->next_pmem, 0); *len = sizeof(*part_info); @@ -681,8 +686,10 @@ static struct cxl_cmd cxl_cmd_set[256][256] = { [FIRMWARE_UPDATE][GET_INFO] = { "FIRMWARE_UPDATE_GET_INFO", cmd_firmware_update_get_info, 0, 0 }, [TIMESTAMP][GET] = { "TIMESTAMP_GET", cmd_timestamp_get, 0, 0 }, - [TIMESTAMP][SET] = { "TIMESTAMP_SET", cmd_timestamp_set, 8, IMMEDIATE_POLICY_CHANGE }, - [LOGS][GET_SUPPORTED] = { "LOGS_GET_SUPPORTED", cmd_logs_get_supported, 0, 0 }, + [TIMESTAMP][SET] = { "TIMESTAMP_SET", cmd_timestamp_set, + 8, IMMEDIATE_POLICY_CHANGE }, + [LOGS][GET_SUPPORTED] = { "LOGS_GET_SUPPORTED", cmd_logs_get_supported, + 0, 0 }, [LOGS][GET_LOG] = { "LOGS_GET_LOG", cmd_logs_get_log, 0x18, 0 }, [IDENTIFY][MEMORY_DEVICE] = { "IDENTIFY_MEMORY_DEVICE", cmd_identify_memory_device, 0, 0 }, diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index c02be4ce45..18ad853f5b 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -208,10 +208,9 @@ static int ct3_build_cdat_table(CDATSubHeader ***cdat_table, void *priv) } if (nonvolatile_mr) { + uint64_t base = volatile_mr ? memory_region_size(volatile_mr) : 0; rc = ct3_build_cdat_entries_for_mr(&(table[cur_ent]), dsmad_handle++, - nonvolatile_mr, true, - (volatile_mr ? - memory_region_size(volatile_mr) : 0)); + nonvolatile_mr, true, base); if (rc < 0) { goto error_cleanup; } @@ -514,7 +513,8 @@ static void ct3d_reg_write(void *opaque, hwaddr offset, uint64_t value, case A_CXL_RAS_UNC_ERR_STATUS: { uint32_t capctrl = ldl_le_p(cache_mem + R_CXL_RAS_ERR_CAP_CTRL); - uint32_t fe = FIELD_EX32(capctrl, CXL_RAS_ERR_CAP_CTRL, FIRST_ERROR_POINTER); + uint32_t fe = FIELD_EX32(capctrl, CXL_RAS_ERR_CAP_CTRL, + FIRST_ERROR_POINTER); CXLError *cxl_err; uint32_t unc_err; @@ -533,7 +533,8 @@ static void ct3d_reg_write(void *opaque, hwaddr offset, uint64_t value, * closest to behavior of hardware not capable of multiple * header recording. */ - QTAILQ_FOREACH_SAFE(cxl_err, &ct3d->error_list, node, cxl_next) { + QTAILQ_FOREACH_SAFE(cxl_err, &ct3d->error_list, node, + cxl_next) { if ((1 << cxl_err->type) & value) { QTAILQ_REMOVE(&ct3d->error_list, cxl_err, node); g_free(cxl_err); @@ -1072,7 +1073,8 @@ void qmp_cxl_inject_poison(const char *path, uint64_t start, uint64_t length, if (((start >= p->start) && (start < p->start + p->length)) || ((start + length > p->start) && (start + length <= p->start + p->length))) { - error_setg(errp, "Overlap with existing poisoned region not supported"); + error_setg(errp, + "Overlap with existing poisoned region not supported"); return; } } @@ -1085,7 +1087,8 @@ void qmp_cxl_inject_poison(const char *path, uint64_t start, uint64_t length, p = g_new0(CXLPoison, 1); p->length = length; p->start = start; - p->type = CXL_POISON_TYPE_INTERNAL; /* Different from injected via the mbox */ + /* Different from injected via the mbox */ + p->type = CXL_POISON_TYPE_INTERNAL; QLIST_INSERT_HEAD(&ct3d->poison_list, p, node); ct3d->poison_list_cnt++; @@ -1222,7 +1225,8 @@ void qmp_cxl_inject_correctable_error(const char *path, CxlCorErrorType type, return; } /* If the error is masked, nothting to do here */ - if (!((1 << cxl_err_type) & ~ldl_le_p(reg_state + R_CXL_RAS_COR_ERR_MASK))) { + if (!((1 << cxl_err_type) & + ~ldl_le_p(reg_state + R_CXL_RAS_COR_ERR_MASK))) { return; } @@ -1372,7 +1376,8 @@ void qmp_cxl_inject_dram_event(const char *path, CxlEventLog log, uint8_t flags, bool has_bank, uint8_t bank, bool has_row, uint32_t row, bool has_column, uint16_t column, - bool has_correction_mask, uint64List *correction_mask, + bool has_correction_mask, + uint64List *correction_mask, Error **errp) { Object *obj = object_resolve_path(path, NULL); @@ -1473,7 +1478,7 @@ void qmp_cxl_inject_memory_module_event(const char *path, CxlEventLog log, int16_t temperature, uint32_t dirty_shutdown_count, uint32_t corrected_volatile_error_count, - uint32_t corrected_persistent_error_count, + uint32_t corrected_persist_error_count, Error **errp) { Object *obj = object_resolve_path(path, NULL); @@ -1513,8 +1518,10 @@ void qmp_cxl_inject_memory_module_event(const char *path, CxlEventLog log, module.life_used = life_used; stw_le_p(&module.temperature, temperature); stl_le_p(&module.dirty_shutdown_count, dirty_shutdown_count); - stl_le_p(&module.corrected_volatile_error_count, corrected_volatile_error_count); - stl_le_p(&module.corrected_persistent_error_count, corrected_persistent_error_count); + stl_le_p(&module.corrected_volatile_error_count, + corrected_volatile_error_count); + stl_le_p(&module.corrected_persistent_error_count, + corrected_persist_error_count); if (cxl_event_insert(cxlds, enc_log, (CXLEventRecordRaw *)&module)) { cxl_event_irq_assert(ct3d); diff --git a/hw/mem/cxl_type3_stubs.c b/hw/mem/cxl_type3_stubs.c index 8ba5d3d1f7..3e1851e32b 100644 --- a/hw/mem/cxl_type3_stubs.c +++ b/hw/mem/cxl_type3_stubs.c @@ -33,7 +33,8 @@ void qmp_cxl_inject_dram_event(const char *path, CxlEventLog log, uint8_t flags, bool has_bank, uint8_t bank, bool has_row, uint32_t row, bool has_column, uint16_t column, - bool has_correction_mask, uint64List *correction_mask, + bool has_correction_mask, + uint64List *correction_mask, Error **errp) {} void qmp_cxl_inject_memory_module_event(const char *path, CxlEventLog log, @@ -45,7 +46,7 @@ void qmp_cxl_inject_memory_module_event(const char *path, CxlEventLog log, int16_t temperature, uint32_t dirty_shutdown_count, uint32_t corrected_volatile_error_count, - uint32_t corrected_persistent_error_count, + uint32_t corrected_persist_error_count, Error **errp) {} void qmp_cxl_inject_poison(const char *path, uint64_t start, uint64_t length, diff --git a/include/hw/cxl/cxl_component.h b/include/hw/cxl/cxl_component.h index 3c795a6278..e52dd8d2b9 100644 --- a/include/hw/cxl/cxl_component.h +++ b/include/hw/cxl/cxl_component.h @@ -175,7 +175,8 @@ HDM_DECODER_INIT(3); (CXL_IDE_REGISTERS_OFFSET + CXL_IDE_REGISTERS_SIZE) #define CXL_SNOOP_REGISTERS_SIZE 0x8 -QEMU_BUILD_BUG_MSG((CXL_SNOOP_REGISTERS_OFFSET + CXL_SNOOP_REGISTERS_SIZE) >= 0x1000, +QEMU_BUILD_BUG_MSG((CXL_SNOOP_REGISTERS_OFFSET + + CXL_SNOOP_REGISTERS_SIZE) >= 0x1000, "No space for registers"); typedef struct component_registers { diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index 51cd0d9ce3..007ddaf078 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -192,7 +192,7 @@ void cxl_device_register_init_common(CXLDeviceState *dev); * Documented as a 128 bit register, but 64 bit accesses and the second * 64 bits are currently reserved. */ -REG64(CXL_DEV_CAP_ARRAY, 0) /* Documented as 128 bit register but 64 byte accesses */ +REG64(CXL_DEV_CAP_ARRAY, 0) FIELD(CXL_DEV_CAP_ARRAY, CAP_ID, 0, 16) FIELD(CXL_DEV_CAP_ARRAY, CAP_VERSION, 16, 8) FIELD(CXL_DEV_CAP_ARRAY, CAP_COUNT, 32, 16) @@ -361,7 +361,8 @@ struct CXLType3Class { uint64_t offset); void (*set_lsa)(CXLType3Dev *ct3d, const void *buf, uint64_t size, uint64_t offset); - bool (*set_cacheline)(CXLType3Dev *ct3d, uint64_t dpa_offset, uint8_t *data); + bool (*set_cacheline)(CXLType3Dev *ct3d, uint64_t dpa_offset, + uint8_t *data); }; MemTxResult cxl_type3_read(PCIDevice *d, hwaddr host_addr, uint64_t *data, diff --git a/include/hw/cxl/cxl_events.h b/include/hw/cxl/cxl_events.h index 089ba2091f..d778487b7e 100644 --- a/include/hw/cxl/cxl_events.h +++ b/include/hw/cxl/cxl_events.h @@ -92,7 +92,8 @@ typedef enum CXLEventIntMode { CXL_INT_RES = 0x03, } CXLEventIntMode; #define CXL_EVENT_INT_MODE_MASK 0x3 -#define CXL_EVENT_INT_SETTING(vector) ((((uint8_t)vector & 0xf) << 4) | CXL_INT_MSI_MSIX) +#define CXL_EVENT_INT_SETTING(vector) \ + ((((uint8_t)vector & 0xf) << 4) | CXL_INT_MSI_MSIX) typedef struct CXLEventInterruptPolicy { uint8_t info_settings; uint8_t warn_settings; From 45234c2dd2920f16f768bda1ec8353bda8c5a929 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Mon, 23 Oct 2023 15:02:10 +0100 Subject: [PATCH 705/974] hw/cxl: Fix a QEMU_BUILD_BUG_ON() in switch statement scope issue. As _Static_assert is a declaration, it can't follow a label until C23. Some older versions of GCC trip up on this one. This check has no obvious purpose so just remove it. Reported-by: Jeongtae Park Signed-off-by: Jonathan Cameron Message-Id: <20231023140210.3089-6-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-component-utils.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/cxl/cxl-component-utils.c b/hw/cxl/cxl-component-utils.c index 5ebd81daf3..d0245cc55d 100644 --- a/hw/cxl/cxl-component-utils.c +++ b/hw/cxl/cxl-component-utils.c @@ -305,7 +305,6 @@ void cxl_component_register_init_common(uint32_t *reg_state, ARRAY_FIELD_DP32(reg_state, CXL_CAPABILITY_HEADER, ARRAY_SIZE, caps); #define init_cap_reg(reg, id, version) \ - QEMU_BUILD_BUG_ON(CXL_##reg##_REGISTERS_OFFSET == 0); \ do { \ int which = R_CXL_##reg##_CAPABILITY_HEADER; \ reg_state[which] = FIELD_DP32(reg_state[which], \ From a7bb53b1eee6836d29003a8ffbd2c2c4c55a40c2 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Mon, 23 Oct 2023 17:07:50 +0100 Subject: [PATCH 706/974] hw/cxl/mbox: Pull the payload out of struct cxl_cmd and make instances constant Putting the pointer in the structure for command handling puts a single variable element inside an otherwise constant structure. Move it out as a directly passed variable and take the cxl_cmd structures constant. Signed-off-by: Jonathan Cameron Reviewed-by: Fan Ni Message-Id: <20231023160806.13206-2-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-mailbox-utils.c | 121 +++++++++++++++++++----------------- include/hw/cxl/cxl_device.h | 13 ++++ 2 files changed, 78 insertions(+), 56 deletions(-) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index ab082ec9de..c02de06943 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -69,18 +69,9 @@ enum { #define CLEAR_POISON 0x2 }; -struct cxl_cmd; -typedef CXLRetCode (*opcode_handler)(struct cxl_cmd *cmd, - CXLDeviceState *cxl_dstate, uint16_t *len); -struct cxl_cmd { - const char *name; - opcode_handler handler; - ssize_t in; - uint16_t effect; /* Reported in CEL */ - uint8_t *payload; -}; -static CXLRetCode cmd_events_get_records(struct cxl_cmd *cmd, +static CXLRetCode cmd_events_get_records(const struct cxl_cmd *cmd, + uint8_t *payload, CXLDeviceState *cxlds, uint16_t *len) { @@ -92,9 +83,9 @@ static CXLRetCode cmd_events_get_records(struct cxl_cmd *cmd, return CXL_MBOX_INVALID_INPUT; } - log_type = *((uint8_t *)cmd->payload); + log_type = payload[0]; - pl = (CXLGetEventPayload *)cmd->payload; + pl = (CXLGetEventPayload *)payload; memset(pl, 0, sizeof(*pl)); max_recs = (cxlds->payload_size - CXL_EVENT_PAYLOAD_HDR_SIZE) / @@ -106,25 +97,27 @@ static CXLRetCode cmd_events_get_records(struct cxl_cmd *cmd, return cxl_event_get_records(cxlds, pl, log_type, max_recs, len); } -static CXLRetCode cmd_events_clear_records(struct cxl_cmd *cmd, +static CXLRetCode cmd_events_clear_records(const struct cxl_cmd *cmd, + uint8_t *payload, CXLDeviceState *cxlds, uint16_t *len) { CXLClearEventPayload *pl; - pl = (CXLClearEventPayload *)cmd->payload; + pl = (CXLClearEventPayload *)payload; *len = 0; return cxl_event_clear_records(cxlds, pl); } -static CXLRetCode cmd_events_get_interrupt_policy(struct cxl_cmd *cmd, +static CXLRetCode cmd_events_get_interrupt_policy(const struct cxl_cmd *cmd, + uint8_t *payload, CXLDeviceState *cxlds, uint16_t *len) { CXLEventInterruptPolicy *policy; CXLEventLog *log; - policy = (CXLEventInterruptPolicy *)cmd->payload; + policy = (CXLEventInterruptPolicy *)payload; memset(policy, 0, sizeof(*policy)); log = &cxlds->event_logs[CXL_EVENT_TYPE_INFO]; @@ -157,7 +150,8 @@ static CXLRetCode cmd_events_get_interrupt_policy(struct cxl_cmd *cmd, return CXL_MBOX_SUCCESS; } -static CXLRetCode cmd_events_set_interrupt_policy(struct cxl_cmd *cmd, +static CXLRetCode cmd_events_set_interrupt_policy(const struct cxl_cmd *cmd, + uint8_t *payload, CXLDeviceState *cxlds, uint16_t *len) { @@ -168,7 +162,7 @@ static CXLRetCode cmd_events_set_interrupt_policy(struct cxl_cmd *cmd, return CXL_MBOX_INVALID_PAYLOAD_LENGTH; } - policy = (CXLEventInterruptPolicy *)cmd->payload; + policy = (CXLEventInterruptPolicy *)payload; log = &cxlds->event_logs[CXL_EVENT_TYPE_INFO]; log->irq_enabled = (policy->info_settings & CXL_EVENT_INT_MODE_MASK) == @@ -200,7 +194,8 @@ static CXLRetCode cmd_events_set_interrupt_policy(struct cxl_cmd *cmd, } /* 8.2.9.2.1 */ -static CXLRetCode cmd_firmware_update_get_info(struct cxl_cmd *cmd, +static CXLRetCode cmd_firmware_update_get_info(const struct cxl_cmd *cmd, + uint8_t *payload, CXLDeviceState *cxl_dstate, uint16_t *len) { @@ -221,7 +216,7 @@ static CXLRetCode cmd_firmware_update_get_info(struct cxl_cmd *cmd, return CXL_MBOX_INTERNAL_ERROR; } - fw_info = (void *)cmd->payload; + fw_info = (void *)payload; memset(fw_info, 0, sizeof(*fw_info)); fw_info->slots_supported = 2; @@ -234,27 +229,29 @@ static CXLRetCode cmd_firmware_update_get_info(struct cxl_cmd *cmd, } /* 8.2.9.3.1 */ -static CXLRetCode cmd_timestamp_get(struct cxl_cmd *cmd, +static CXLRetCode cmd_timestamp_get(const struct cxl_cmd *cmd, + uint8_t *payload, CXLDeviceState *cxl_dstate, uint16_t *len) { uint64_t final_time = cxl_device_get_timestamp(cxl_dstate); - stq_le_p(cmd->payload, final_time); + stq_le_p(payload, final_time); *len = 8; return CXL_MBOX_SUCCESS; } /* 8.2.9.3.2 */ -static CXLRetCode cmd_timestamp_set(struct cxl_cmd *cmd, - CXLDeviceState *cxl_dstate, - uint16_t *len) +static CXLRetCode cmd_timestamp_set(const struct cxl_cmd *cmd, + uint8_t *payload, + CXLDeviceState *cxl_dstate, + uint16_t *len) { cxl_dstate->timestamp.set = true; cxl_dstate->timestamp.last_set = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - cxl_dstate->timestamp.host_set = le64_to_cpu(*(uint64_t *)cmd->payload); + cxl_dstate->timestamp.host_set = le64_to_cpu(*(uint64_t *)payload); *len = 0; return CXL_MBOX_SUCCESS; @@ -267,7 +264,8 @@ static const QemuUUID cel_uuid = { }; /* 8.2.9.4.1 */ -static CXLRetCode cmd_logs_get_supported(struct cxl_cmd *cmd, +static CXLRetCode cmd_logs_get_supported(const struct cxl_cmd *cmd, + uint8_t *payload, CXLDeviceState *cxl_dstate, uint16_t *len) { @@ -278,7 +276,7 @@ static CXLRetCode cmd_logs_get_supported(struct cxl_cmd *cmd, QemuUUID uuid; uint32_t size; } log_entries[1]; - } QEMU_PACKED *supported_logs = (void *)cmd->payload; + } QEMU_PACKED *supported_logs = (void *)payload; QEMU_BUILD_BUG_ON(sizeof(*supported_logs) != 0x1c); supported_logs->entries = 1; @@ -290,7 +288,8 @@ static CXLRetCode cmd_logs_get_supported(struct cxl_cmd *cmd, } /* 8.2.9.4.2 */ -static CXLRetCode cmd_logs_get_log(struct cxl_cmd *cmd, +static CXLRetCode cmd_logs_get_log(const struct cxl_cmd *cmd, + uint8_t *payload, CXLDeviceState *cxl_dstate, uint16_t *len) { @@ -298,7 +297,9 @@ static CXLRetCode cmd_logs_get_log(struct cxl_cmd *cmd, QemuUUID uuid; uint32_t offset; uint32_t length; - } QEMU_PACKED QEMU_ALIGNED(16) *get_log = (void *)cmd->payload; + } QEMU_PACKED QEMU_ALIGNED(16) *get_log; + + get_log = (void *)payload; /* * 8.2.9.4.2 @@ -324,14 +325,15 @@ static CXLRetCode cmd_logs_get_log(struct cxl_cmd *cmd, /* Store off everything to local variables so we can wipe out the payload */ *len = get_log->length; - memmove(cmd->payload, cxl_dstate->cel_log + get_log->offset, + memmove(payload, cxl_dstate->cel_log + get_log->offset, get_log->length); return CXL_MBOX_SUCCESS; } /* 8.2.9.5.1.1 */ -static CXLRetCode cmd_identify_memory_device(struct cxl_cmd *cmd, +static CXLRetCode cmd_identify_memory_device(const struct cxl_cmd *cmd, + uint8_t *payload, CXLDeviceState *cxl_dstate, uint16_t *len) { @@ -361,7 +363,7 @@ static CXLRetCode cmd_identify_memory_device(struct cxl_cmd *cmd, return CXL_MBOX_INTERNAL_ERROR; } - id = (void *)cmd->payload; + id = (void *)payload; memset(id, 0, sizeof(*id)); snprintf(id->fw_revision, 0x10, "BWFW VERSION %02d", 0); @@ -382,7 +384,8 @@ static CXLRetCode cmd_identify_memory_device(struct cxl_cmd *cmd, return CXL_MBOX_SUCCESS; } -static CXLRetCode cmd_ccls_get_partition_info(struct cxl_cmd *cmd, +static CXLRetCode cmd_ccls_get_partition_info(const struct cxl_cmd *cmd, + uint8_t *payload, CXLDeviceState *cxl_dstate, uint16_t *len) { @@ -391,7 +394,7 @@ static CXLRetCode cmd_ccls_get_partition_info(struct cxl_cmd *cmd, uint64_t active_pmem; uint64_t next_vmem; uint64_t next_pmem; - } QEMU_PACKED *part_info = (void *)cmd->payload; + } QEMU_PACKED *part_info = (void *)payload; QEMU_BUILD_BUG_ON(sizeof(*part_info) != 0x20); if ((!QEMU_IS_ALIGNED(cxl_dstate->vmem_size, CXL_CAPACITY_MULTIPLIER)) || @@ -414,7 +417,8 @@ static CXLRetCode cmd_ccls_get_partition_info(struct cxl_cmd *cmd, return CXL_MBOX_SUCCESS; } -static CXLRetCode cmd_ccls_get_lsa(struct cxl_cmd *cmd, +static CXLRetCode cmd_ccls_get_lsa(const struct cxl_cmd *cmd, + uint8_t *payload, CXLDeviceState *cxl_dstate, uint16_t *len) { @@ -426,7 +430,7 @@ static CXLRetCode cmd_ccls_get_lsa(struct cxl_cmd *cmd, CXLType3Class *cvc = CXL_TYPE3_GET_CLASS(ct3d); uint32_t offset, length; - get_lsa = (void *)cmd->payload; + get_lsa = (void *)payload; offset = get_lsa->offset; length = get_lsa->length; @@ -439,7 +443,8 @@ static CXLRetCode cmd_ccls_get_lsa(struct cxl_cmd *cmd, return CXL_MBOX_SUCCESS; } -static CXLRetCode cmd_ccls_set_lsa(struct cxl_cmd *cmd, +static CXLRetCode cmd_ccls_set_lsa(const struct cxl_cmd *cmd, + uint8_t *payload, CXLDeviceState *cxl_dstate, uint16_t *len) { @@ -448,7 +453,7 @@ static CXLRetCode cmd_ccls_set_lsa(struct cxl_cmd *cmd, uint32_t rsvd; uint8_t data[]; } QEMU_PACKED; - struct set_lsa_pl *set_lsa_payload = (void *)cmd->payload; + struct set_lsa_pl *set_lsa_payload = (void *)payload; CXLType3Dev *ct3d = container_of(cxl_dstate, CXLType3Dev, cxl_dstate); CXLType3Class *cvc = CXL_TYPE3_GET_CLASS(ct3d); const size_t hdr_len = offsetof(struct set_lsa_pl, data); @@ -474,7 +479,8 @@ static CXLRetCode cmd_ccls_set_lsa(struct cxl_cmd *cmd, * make this stateful. We may want to allow longer poison lists to aid * testing that kernel functionality. */ -static CXLRetCode cmd_media_get_poison_list(struct cxl_cmd *cmd, +static CXLRetCode cmd_media_get_poison_list(const struct cxl_cmd *cmd, + uint8_t *payload, CXLDeviceState *cxl_dstate, uint16_t *len) { @@ -496,8 +502,8 @@ static CXLRetCode cmd_media_get_poison_list(struct cxl_cmd *cmd, } QEMU_PACKED records[]; } QEMU_PACKED; - struct get_poison_list_pl *in = (void *)cmd->payload; - struct get_poison_list_out_pl *out = (void *)cmd->payload; + struct get_poison_list_pl *in = (void *)payload; + struct get_poison_list_out_pl *out = (void *)payload; CXLType3Dev *ct3d = container_of(cxl_dstate, CXLType3Dev, cxl_dstate); uint16_t record_count = 0, i = 0; uint64_t query_start, query_length; @@ -550,7 +556,8 @@ static CXLRetCode cmd_media_get_poison_list(struct cxl_cmd *cmd, return CXL_MBOX_SUCCESS; } -static CXLRetCode cmd_media_inject_poison(struct cxl_cmd *cmd, +static CXLRetCode cmd_media_inject_poison(const struct cxl_cmd *cmd, + uint8_t *payload, CXLDeviceState *cxl_dstate, uint16_t *len_unused) { @@ -560,7 +567,7 @@ static CXLRetCode cmd_media_inject_poison(struct cxl_cmd *cmd, struct inject_poison_pl { uint64_t dpa; }; - struct inject_poison_pl *in = (void *)cmd->payload; + struct inject_poison_pl *in = (void *)payload; uint64_t dpa = ldq_le_p(&in->dpa); CXLPoison *p; @@ -589,7 +596,8 @@ static CXLRetCode cmd_media_inject_poison(struct cxl_cmd *cmd, return CXL_MBOX_SUCCESS; } -static CXLRetCode cmd_media_clear_poison(struct cxl_cmd *cmd, +static CXLRetCode cmd_media_clear_poison(const struct cxl_cmd *cmd, + uint8_t *payload, CXLDeviceState *cxl_dstate, uint16_t *len_unused) { @@ -603,7 +611,7 @@ static CXLRetCode cmd_media_clear_poison(struct cxl_cmd *cmd, CXLPoison *ent; uint64_t dpa; - struct clear_poison_pl *in = (void *)cmd->payload; + struct clear_poison_pl *in = (void *)payload; dpa = ldq_le_p(&in->dpa); if (dpa + CXL_CACHE_LINE_SIZE > cxl_dstate->mem_size) { @@ -673,7 +681,7 @@ static CXLRetCode cmd_media_clear_poison(struct cxl_cmd *cmd, #define IMMEDIATE_POLICY_CHANGE (1 << 3) #define IMMEDIATE_LOG_CHANGE (1 << 4) -static struct cxl_cmd cxl_cmd_set[256][256] = { +static const struct cxl_cmd cxl_cmd_set[256][256] = { [EVENTS][GET_RECORDS] = { "EVENTS_GET_RECORDS", cmd_events_get_records, 1, 0 }, [EVENTS][CLEAR_RECORDS] = { "EVENTS_CLEAR_RECORDS", @@ -709,21 +717,21 @@ static struct cxl_cmd cxl_cmd_set[256][256] = { void cxl_process_mailbox(CXLDeviceState *cxl_dstate) { uint16_t ret = CXL_MBOX_SUCCESS; - struct cxl_cmd *cxl_cmd; - uint64_t status_reg; + const struct cxl_cmd *cxl_cmd; + uint64_t status_reg = 0; opcode_handler h; uint64_t command_reg = cxl_dstate->mbox_reg_state64[R_CXL_DEV_MAILBOX_CMD]; uint8_t set = FIELD_EX64(command_reg, CXL_DEV_MAILBOX_CMD, COMMAND_SET); uint8_t cmd = FIELD_EX64(command_reg, CXL_DEV_MAILBOX_CMD, COMMAND); uint16_t len = FIELD_EX64(command_reg, CXL_DEV_MAILBOX_CMD, LENGTH); - cxl_cmd = &cxl_cmd_set[set][cmd]; + uint8_t *pl = cxl_dstate->mbox_reg_state + A_CXL_DEV_CMD_PAYLOAD; + + cxl_cmd = &cxl_dstate->cxl_cmd_set[set][cmd]; h = cxl_cmd->handler; if (h) { if (len == cxl_cmd->in || cxl_cmd->in == ~0) { - cxl_cmd->payload = cxl_dstate->mbox_reg_state + - A_CXL_DEV_CMD_PAYLOAD; - ret = (*h)(cxl_cmd, cxl_dstate, &len); + ret = (*h)(cxl_cmd, pl, cxl_dstate, &len); assert(len <= cxl_dstate->payload_size); } else { ret = CXL_MBOX_INVALID_PAYLOAD_LENGTH; @@ -752,10 +760,11 @@ void cxl_process_mailbox(CXLDeviceState *cxl_dstate) void cxl_initialize_mailbox(CXLDeviceState *cxl_dstate) { + cxl_dstate->cxl_cmd_set = cxl_cmd_set; for (int set = 0; set < 256; set++) { for (int cmd = 0; cmd < 256; cmd++) { - if (cxl_cmd_set[set][cmd].handler) { - struct cxl_cmd *c = &cxl_cmd_set[set][cmd]; + if (cxl_dstate->cxl_cmd_set[set][cmd].handler) { + const struct cxl_cmd *c = &cxl_dstate->cxl_cmd_set[set][cmd]; struct cel_log *log = &cxl_dstate->cel_log[cxl_dstate->cel_size]; diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index 007ddaf078..556953469c 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -111,6 +111,18 @@ typedef enum { CXL_MBOX_MAX = 0x17 } CXLRetCode; +typedef struct cxl_device_state CXLDeviceState; +struct cxl_cmd; +typedef CXLRetCode (*opcode_handler)(const struct cxl_cmd *cmd, + uint8_t *payload, + CXLDeviceState *cxl_dstate, uint16_t *len); +struct cxl_cmd { + const char *name; + opcode_handler handler; + ssize_t in; + uint16_t effect; /* Reported in CEL */ +}; + typedef struct CXLEvent { CXLEventRecordRaw data; QSIMPLEQ_ENTRY(CXLEvent) node; @@ -178,6 +190,7 @@ typedef struct cxl_device_state { uint64_t pmem_size; uint64_t vmem_size; + const struct cxl_cmd (*cxl_cmd_set)[256]; CXLEventLog event_logs[CXL_EVENT_TYPE_MAX]; } CXLDeviceState; From 6f59274e937576fbb2623b687aa2556e115a712f Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Mon, 23 Oct 2023 17:07:51 +0100 Subject: [PATCH 707/974] hw/cxl/mbox: Split mailbox command payload into separate input and output New CCI types that will be supported shortly do not have a single buffer used in both directions. As such, split it up. To avoid the complexities of implementing all commands to handle potential aliasing, take a copy of the input before use. Signed-off-by: Jonathan Cameron Message-Id: <20231023160806.13206-3-Jonathan.Cameron@huawei.com> Reviewed-by: Fan Ni Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-events.c | 2 +- hw/cxl/cxl-mailbox-utils.c | 230 +++++++++++++++++++++--------------- include/hw/cxl/cxl_device.h | 7 +- 3 files changed, 140 insertions(+), 99 deletions(-) diff --git a/hw/cxl/cxl-events.c b/hw/cxl/cxl-events.c index e2172b94b9..bee6dfaf14 100644 --- a/hw/cxl/cxl-events.c +++ b/hw/cxl/cxl-events.c @@ -143,7 +143,7 @@ bool cxl_event_insert(CXLDeviceState *cxlds, CXLEventLogType log_type, CXLRetCode cxl_event_get_records(CXLDeviceState *cxlds, CXLGetEventPayload *pl, uint8_t log_type, int max_recs, - uint16_t *len) + size_t *len) { CXLEventLog *log; CXLEvent *entry; diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index c02de06943..e5ddce37c7 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -71,9 +71,9 @@ enum { static CXLRetCode cmd_events_get_records(const struct cxl_cmd *cmd, - uint8_t *payload, - CXLDeviceState *cxlds, - uint16_t *len) + uint8_t *payload_in, size_t len_in, + uint8_t *payload_out, size_t *len_out, + CXLDeviceState *cxlds) { CXLGetEventPayload *pl; uint8_t log_type; @@ -83,9 +83,9 @@ static CXLRetCode cmd_events_get_records(const struct cxl_cmd *cmd, return CXL_MBOX_INVALID_INPUT; } - log_type = payload[0]; + log_type = payload_in[0]; - pl = (CXLGetEventPayload *)payload; + pl = (CXLGetEventPayload *)payload_out; memset(pl, 0, sizeof(*pl)); max_recs = (cxlds->payload_size - CXL_EVENT_PAYLOAD_HDR_SIZE) / @@ -94,30 +94,34 @@ static CXLRetCode cmd_events_get_records(const struct cxl_cmd *cmd, max_recs = 0xFFFF; } - return cxl_event_get_records(cxlds, pl, log_type, max_recs, len); + return cxl_event_get_records(cxlds, pl, log_type, max_recs, len_out); } static CXLRetCode cmd_events_clear_records(const struct cxl_cmd *cmd, - uint8_t *payload, - CXLDeviceState *cxlds, - uint16_t *len) + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLDeviceState *cxlds) { CXLClearEventPayload *pl; - pl = (CXLClearEventPayload *)payload; - *len = 0; + pl = (CXLClearEventPayload *)payload_in; + *len_out = 0; return cxl_event_clear_records(cxlds, pl); } static CXLRetCode cmd_events_get_interrupt_policy(const struct cxl_cmd *cmd, - uint8_t *payload, - CXLDeviceState *cxlds, - uint16_t *len) + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLDeviceState *cxlds) { CXLEventInterruptPolicy *policy; CXLEventLog *log; - policy = (CXLEventInterruptPolicy *)payload; + policy = (CXLEventInterruptPolicy *)payload_out; memset(policy, 0, sizeof(*policy)); log = &cxlds->event_logs[CXL_EVENT_TYPE_INFO]; @@ -146,23 +150,25 @@ static CXLRetCode cmd_events_get_interrupt_policy(const struct cxl_cmd *cmd, policy->dyn_cap_settings = CXL_INT_MSI_MSIX; } - *len = sizeof(*policy); + *len_out = sizeof(*policy); return CXL_MBOX_SUCCESS; } static CXLRetCode cmd_events_set_interrupt_policy(const struct cxl_cmd *cmd, - uint8_t *payload, - CXLDeviceState *cxlds, - uint16_t *len) + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLDeviceState *cxlds) { CXLEventInterruptPolicy *policy; CXLEventLog *log; - if (*len < CXL_EVENT_INT_SETTING_MIN_LEN) { + if (len_in < CXL_EVENT_INT_SETTING_MIN_LEN) { return CXL_MBOX_INVALID_PAYLOAD_LENGTH; } - policy = (CXLEventInterruptPolicy *)payload; + policy = (CXLEventInterruptPolicy *)payload_in; log = &cxlds->event_logs[CXL_EVENT_TYPE_INFO]; log->irq_enabled = (policy->info_settings & CXL_EVENT_INT_MODE_MASK) == @@ -181,7 +187,7 @@ static CXLRetCode cmd_events_set_interrupt_policy(const struct cxl_cmd *cmd, CXL_INT_MSI_MSIX; /* DCD is optional */ - if (*len < sizeof(*policy)) { + if (len_in < sizeof(*policy)) { return CXL_MBOX_SUCCESS; } @@ -189,15 +195,17 @@ static CXLRetCode cmd_events_set_interrupt_policy(const struct cxl_cmd *cmd, log->irq_enabled = (policy->dyn_cap_settings & CXL_EVENT_INT_MODE_MASK) == CXL_INT_MSI_MSIX; - *len = sizeof(*policy); + *len_out = 0; return CXL_MBOX_SUCCESS; } /* 8.2.9.2.1 */ static CXLRetCode cmd_firmware_update_get_info(const struct cxl_cmd *cmd, - uint8_t *payload, - CXLDeviceState *cxl_dstate, - uint16_t *len) + uint8_t *payload_in, + size_t len, + uint8_t *payload_out, + size_t *len_out, + CXLDeviceState *cxl_dstate) { struct { uint8_t slots_supported; @@ -216,7 +224,7 @@ static CXLRetCode cmd_firmware_update_get_info(const struct cxl_cmd *cmd, return CXL_MBOX_INTERNAL_ERROR; } - fw_info = (void *)payload; + fw_info = (void *)payload_out; memset(fw_info, 0, sizeof(*fw_info)); fw_info->slots_supported = 2; @@ -224,36 +232,40 @@ static CXLRetCode cmd_firmware_update_get_info(const struct cxl_cmd *cmd, fw_info->caps = 0; pstrcpy(fw_info->fw_rev1, sizeof(fw_info->fw_rev1), "BWFW VERSION 0"); - *len = sizeof(*fw_info); + *len_out = sizeof(*fw_info); return CXL_MBOX_SUCCESS; } /* 8.2.9.3.1 */ static CXLRetCode cmd_timestamp_get(const struct cxl_cmd *cmd, - uint8_t *payload, - CXLDeviceState *cxl_dstate, - uint16_t *len) + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLDeviceState *cxl_dstate) { uint64_t final_time = cxl_device_get_timestamp(cxl_dstate); - stq_le_p(payload, final_time); - *len = 8; + stq_le_p(payload_out, final_time); + *len_out = 8; return CXL_MBOX_SUCCESS; } /* 8.2.9.3.2 */ static CXLRetCode cmd_timestamp_set(const struct cxl_cmd *cmd, - uint8_t *payload, - CXLDeviceState *cxl_dstate, - uint16_t *len) + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLDeviceState *cxl_dstate) { cxl_dstate->timestamp.set = true; cxl_dstate->timestamp.last_set = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - cxl_dstate->timestamp.host_set = le64_to_cpu(*(uint64_t *)payload); + cxl_dstate->timestamp.host_set = le64_to_cpu(*(uint64_t *)payload_in); - *len = 0; + *len_out = 0; return CXL_MBOX_SUCCESS; } @@ -265,9 +277,11 @@ static const QemuUUID cel_uuid = { /* 8.2.9.4.1 */ static CXLRetCode cmd_logs_get_supported(const struct cxl_cmd *cmd, - uint8_t *payload, - CXLDeviceState *cxl_dstate, - uint16_t *len) + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLDeviceState *cxl_dstate) { struct { uint16_t entries; @@ -276,22 +290,24 @@ static CXLRetCode cmd_logs_get_supported(const struct cxl_cmd *cmd, QemuUUID uuid; uint32_t size; } log_entries[1]; - } QEMU_PACKED *supported_logs = (void *)payload; + } QEMU_PACKED *supported_logs = (void *)payload_out; QEMU_BUILD_BUG_ON(sizeof(*supported_logs) != 0x1c); supported_logs->entries = 1; supported_logs->log_entries[0].uuid = cel_uuid; supported_logs->log_entries[0].size = 4 * cxl_dstate->cel_size; - *len = sizeof(*supported_logs); + *len_out = sizeof(*supported_logs); return CXL_MBOX_SUCCESS; } /* 8.2.9.4.2 */ static CXLRetCode cmd_logs_get_log(const struct cxl_cmd *cmd, - uint8_t *payload, - CXLDeviceState *cxl_dstate, - uint16_t *len) + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLDeviceState *cxl_dstate) { struct { QemuUUID uuid; @@ -299,7 +315,7 @@ static CXLRetCode cmd_logs_get_log(const struct cxl_cmd *cmd, uint32_t length; } QEMU_PACKED QEMU_ALIGNED(16) *get_log; - get_log = (void *)payload; + get_log = (void *)payload_in; /* * 8.2.9.4.2 @@ -323,19 +339,21 @@ static CXLRetCode cmd_logs_get_log(const struct cxl_cmd *cmd, } /* Store off everything to local variables so we can wipe out the payload */ - *len = get_log->length; + *len_out = get_log->length; - memmove(payload, cxl_dstate->cel_log + get_log->offset, - get_log->length); + memmove(payload_out, cxl_dstate->cel_log + get_log->offset, + get_log->length); return CXL_MBOX_SUCCESS; } /* 8.2.9.5.1.1 */ static CXLRetCode cmd_identify_memory_device(const struct cxl_cmd *cmd, - uint8_t *payload, - CXLDeviceState *cxl_dstate, - uint16_t *len) + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLDeviceState *cxl_dstate) { struct { char fw_revision[0x10]; @@ -363,7 +381,7 @@ static CXLRetCode cmd_identify_memory_device(const struct cxl_cmd *cmd, return CXL_MBOX_INTERNAL_ERROR; } - id = (void *)payload; + id = (void *)payload_out; memset(id, 0, sizeof(*id)); snprintf(id->fw_revision, 0x10, "BWFW VERSION %02d", 0); @@ -380,21 +398,23 @@ static CXLRetCode cmd_identify_memory_device(const struct cxl_cmd *cmd, /* No limit - so limited by main poison record limit */ stw_le_p(&id->inject_poison_limit, 0); - *len = sizeof(*id); + *len_out = sizeof(*id); return CXL_MBOX_SUCCESS; } static CXLRetCode cmd_ccls_get_partition_info(const struct cxl_cmd *cmd, - uint8_t *payload, - CXLDeviceState *cxl_dstate, - uint16_t *len) + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLDeviceState *cxl_dstate) { struct { uint64_t active_vmem; uint64_t active_pmem; uint64_t next_vmem; uint64_t next_pmem; - } QEMU_PACKED *part_info = (void *)payload; + } QEMU_PACKED *part_info = (void *)payload_out; QEMU_BUILD_BUG_ON(sizeof(*part_info) != 0x20); if ((!QEMU_IS_ALIGNED(cxl_dstate->vmem_size, CXL_CAPACITY_MULTIPLIER)) || @@ -413,14 +433,16 @@ static CXLRetCode cmd_ccls_get_partition_info(const struct cxl_cmd *cmd, cxl_dstate->pmem_size / CXL_CAPACITY_MULTIPLIER); stq_le_p(&part_info->next_pmem, 0); - *len = sizeof(*part_info); + *len_out = sizeof(*part_info); return CXL_MBOX_SUCCESS; } static CXLRetCode cmd_ccls_get_lsa(const struct cxl_cmd *cmd, - uint8_t *payload, - CXLDeviceState *cxl_dstate, - uint16_t *len) + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLDeviceState *cxl_dstate) { struct { uint32_t offset; @@ -430,46 +452,47 @@ static CXLRetCode cmd_ccls_get_lsa(const struct cxl_cmd *cmd, CXLType3Class *cvc = CXL_TYPE3_GET_CLASS(ct3d); uint32_t offset, length; - get_lsa = (void *)payload; + get_lsa = (void *)payload_in; offset = get_lsa->offset; length = get_lsa->length; if (offset + length > cvc->get_lsa_size(ct3d)) { - *len = 0; + *len_out = 0; return CXL_MBOX_INVALID_INPUT; } - *len = cvc->get_lsa(ct3d, get_lsa, length, offset); + *len_out = cvc->get_lsa(ct3d, payload_out, length, offset); return CXL_MBOX_SUCCESS; } static CXLRetCode cmd_ccls_set_lsa(const struct cxl_cmd *cmd, - uint8_t *payload, - CXLDeviceState *cxl_dstate, - uint16_t *len) + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLDeviceState *cxl_dstate) { struct set_lsa_pl { uint32_t offset; uint32_t rsvd; uint8_t data[]; } QEMU_PACKED; - struct set_lsa_pl *set_lsa_payload = (void *)payload; + struct set_lsa_pl *set_lsa_payload = (void *)payload_in; CXLType3Dev *ct3d = container_of(cxl_dstate, CXLType3Dev, cxl_dstate); CXLType3Class *cvc = CXL_TYPE3_GET_CLASS(ct3d); const size_t hdr_len = offsetof(struct set_lsa_pl, data); - uint16_t plen = *len; - *len = 0; - if (!plen) { + *len_out = 0; + if (!len_in) { return CXL_MBOX_SUCCESS; } - if (set_lsa_payload->offset + plen > cvc->get_lsa_size(ct3d) + hdr_len) { + if (set_lsa_payload->offset + len_in > cvc->get_lsa_size(ct3d) + hdr_len) { return CXL_MBOX_INVALID_INPUT; } - plen -= hdr_len; + len_in -= hdr_len; - cvc->set_lsa(ct3d, set_lsa_payload->data, plen, set_lsa_payload->offset); + cvc->set_lsa(ct3d, set_lsa_payload->data, len_in, set_lsa_payload->offset); return CXL_MBOX_SUCCESS; } @@ -480,9 +503,11 @@ static CXLRetCode cmd_ccls_set_lsa(const struct cxl_cmd *cmd, * testing that kernel functionality. */ static CXLRetCode cmd_media_get_poison_list(const struct cxl_cmd *cmd, - uint8_t *payload, - CXLDeviceState *cxl_dstate, - uint16_t *len) + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLDeviceState *cxl_dstate) { struct get_poison_list_pl { uint64_t pa; @@ -502,8 +527,8 @@ static CXLRetCode cmd_media_get_poison_list(const struct cxl_cmd *cmd, } QEMU_PACKED records[]; } QEMU_PACKED; - struct get_poison_list_pl *in = (void *)payload; - struct get_poison_list_out_pl *out = (void *)payload; + struct get_poison_list_pl *in = (void *)payload_in; + struct get_poison_list_out_pl *out = (void *)payload_out; CXLType3Dev *ct3d = container_of(cxl_dstate, CXLType3Dev, cxl_dstate); uint16_t record_count = 0, i = 0; uint64_t query_start, query_length; @@ -552,14 +577,16 @@ static CXLRetCode cmd_media_get_poison_list(const struct cxl_cmd *cmd, stq_le_p(&out->overflow_timestamp, ct3d->poison_list_overflow_ts); } stw_le_p(&out->count, record_count); - *len = out_pl_len; + *len_out = out_pl_len; return CXL_MBOX_SUCCESS; } static CXLRetCode cmd_media_inject_poison(const struct cxl_cmd *cmd, - uint8_t *payload, - CXLDeviceState *cxl_dstate, - uint16_t *len_unused) + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLDeviceState *cxl_dstate) { CXLType3Dev *ct3d = container_of(cxl_dstate, CXLType3Dev, cxl_dstate); CXLPoisonList *poison_list = &ct3d->poison_list; @@ -567,7 +594,7 @@ static CXLRetCode cmd_media_inject_poison(const struct cxl_cmd *cmd, struct inject_poison_pl { uint64_t dpa; }; - struct inject_poison_pl *in = (void *)payload; + struct inject_poison_pl *in = (void *)payload_in; uint64_t dpa = ldq_le_p(&in->dpa); CXLPoison *p; @@ -592,14 +619,17 @@ static CXLRetCode cmd_media_inject_poison(const struct cxl_cmd *cmd, */ QLIST_INSERT_HEAD(poison_list, p, node); ct3d->poison_list_cnt++; + *len_out = 0; return CXL_MBOX_SUCCESS; } static CXLRetCode cmd_media_clear_poison(const struct cxl_cmd *cmd, - uint8_t *payload, - CXLDeviceState *cxl_dstate, - uint16_t *len_unused) + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLDeviceState *cxl_dstate) { CXLType3Dev *ct3d = container_of(cxl_dstate, CXLType3Dev, cxl_dstate); CXLPoisonList *poison_list = &ct3d->poison_list; @@ -611,7 +641,7 @@ static CXLRetCode cmd_media_clear_poison(const struct cxl_cmd *cmd, CXLPoison *ent; uint64_t dpa; - struct clear_poison_pl *in = (void *)payload; + struct clear_poison_pl *in = (void *)payload_in; dpa = ldq_le_p(&in->dpa); if (dpa + CXL_CACHE_LINE_SIZE > cxl_dstate->mem_size) { @@ -672,6 +702,7 @@ static CXLRetCode cmd_media_clear_poison(const struct cxl_cmd *cmd, } /* Any fragments have been added, free original entry */ g_free(ent); + *len_out = 0; return CXL_MBOX_SUCCESS; } @@ -724,15 +755,24 @@ void cxl_process_mailbox(CXLDeviceState *cxl_dstate) uint8_t set = FIELD_EX64(command_reg, CXL_DEV_MAILBOX_CMD, COMMAND_SET); uint8_t cmd = FIELD_EX64(command_reg, CXL_DEV_MAILBOX_CMD, COMMAND); - uint16_t len = FIELD_EX64(command_reg, CXL_DEV_MAILBOX_CMD, LENGTH); + uint16_t len_in = FIELD_EX64(command_reg, CXL_DEV_MAILBOX_CMD, LENGTH); uint8_t *pl = cxl_dstate->mbox_reg_state + A_CXL_DEV_CMD_PAYLOAD; + /* + * Copy taken to avoid need for individual command handlers to care + * about aliasing. + */ + g_autofree uint8_t *pl_in_copy = NULL; + size_t len_out = 0; + pl_in_copy = g_memdup2(pl, len_in); + /* Avoid stale data - including from earlier commands */ + memset(pl, 0, CXL_MAILBOX_MAX_PAYLOAD_SIZE); cxl_cmd = &cxl_dstate->cxl_cmd_set[set][cmd]; h = cxl_cmd->handler; if (h) { - if (len == cxl_cmd->in || cxl_cmd->in == ~0) { - ret = (*h)(cxl_cmd, pl, cxl_dstate, &len); - assert(len <= cxl_dstate->payload_size); + if (len_in == cxl_cmd->in || cxl_cmd->in == ~0) { + ret = (*h)(cxl_cmd, pl_in_copy, len_in, pl, &len_out, cxl_dstate); + assert(len_out <= cxl_dstate->payload_size); } else { ret = CXL_MBOX_INVALID_PAYLOAD_LENGTH; } @@ -748,7 +788,7 @@ void cxl_process_mailbox(CXLDeviceState *cxl_dstate) /* Set the return length */ command_reg = FIELD_DP64(command_reg, CXL_DEV_MAILBOX_CMD, COMMAND_SET, 0); command_reg = FIELD_DP64(command_reg, CXL_DEV_MAILBOX_CMD, COMMAND, 0); - command_reg = FIELD_DP64(command_reg, CXL_DEV_MAILBOX_CMD, LENGTH, len); + command_reg = FIELD_DP64(command_reg, CXL_DEV_MAILBOX_CMD, LENGTH, len_out); cxl_dstate->mbox_reg_state64[R_CXL_DEV_MAILBOX_CMD] = command_reg; cxl_dstate->mbox_reg_state64[R_CXL_DEV_MAILBOX_STS] = status_reg; diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index 556953469c..d7a2c4009e 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -114,8 +114,9 @@ typedef enum { typedef struct cxl_device_state CXLDeviceState; struct cxl_cmd; typedef CXLRetCode (*opcode_handler)(const struct cxl_cmd *cmd, - uint8_t *payload, - CXLDeviceState *cxl_dstate, uint16_t *len); + uint8_t *payload_in, size_t len_in, + uint8_t *payload_out, size_t *len_out, + CXLDeviceState *cxl_dstate); struct cxl_cmd { const char *name; opcode_handler handler; @@ -390,7 +391,7 @@ bool cxl_event_insert(CXLDeviceState *cxlds, CXLEventLogType log_type, CXLEventRecordRaw *event); CXLRetCode cxl_event_get_records(CXLDeviceState *cxlds, CXLGetEventPayload *pl, uint8_t log_type, int max_recs, - uint16_t *len); + size_t *len); CXLRetCode cxl_event_clear_records(CXLDeviceState *cxlds, CXLClearEventPayload *pl); From cac36a8faffc62ba6b07d8e9dfdc9fbf15c7d1bf Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Mon, 23 Oct 2023 17:07:52 +0100 Subject: [PATCH 708/974] hw/cxl/mbox: Pull the CCI definition out of the CXLDeviceState Enables having multiple CCIs per devices. Each CCI (mailbox) has it's own state and command list, so they can't share a single structure. Signed-off-by: Jonathan Cameron Message-Id: <20231023160806.13206-4-Jonathan.Cameron@huawei.com> Reviewed-by: Fan Ni Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-device-utils.c | 31 +++++++++--- hw/cxl/cxl-mailbox-utils.c | 94 ++++++++++++++++++++++--------------- hw/mem/cxl_type3.c | 5 +- include/hw/cxl/cxl_device.h | 35 ++++++++++---- 4 files changed, 109 insertions(+), 56 deletions(-) diff --git a/hw/cxl/cxl-device-utils.c b/hw/cxl/cxl-device-utils.c index eb7195272e..327949a805 100644 --- a/hw/cxl/cxl-device-utils.c +++ b/hw/cxl/cxl-device-utils.c @@ -62,7 +62,14 @@ static uint64_t dev_reg_read(void *opaque, hwaddr offset, unsigned size) static uint64_t mailbox_reg_read(void *opaque, hwaddr offset, unsigned size) { - CXLDeviceState *cxl_dstate = opaque; + CXLDeviceState *cxl_dstate; + CXLCCI *cci = opaque; + + if (object_dynamic_cast(OBJECT(cci->intf), TYPE_CXL_TYPE3)) { + cxl_dstate = &CXL_TYPE3(cci->intf)->cxl_dstate; + } else { + return 0; + } switch (size) { case 1: @@ -123,7 +130,14 @@ static void mailbox_mem_writeq(uint64_t *reg_state, hwaddr offset, static void mailbox_reg_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { - CXLDeviceState *cxl_dstate = opaque; + CXLDeviceState *cxl_dstate; + CXLCCI *cci = opaque; + + if (object_dynamic_cast(OBJECT(cci->intf), TYPE_CXL_TYPE3)) { + cxl_dstate = &CXL_TYPE3(cci->intf)->cxl_dstate; + } else { + return; + } if (offset >= A_CXL_DEV_CMD_PAYLOAD) { memcpy(cxl_dstate->mbox_reg_state + offset, &value, size); @@ -143,7 +157,7 @@ static void mailbox_reg_write(void *opaque, hwaddr offset, uint64_t value, if (ARRAY_FIELD_EX32(cxl_dstate->mbox_reg_state32, CXL_DEV_MAILBOX_CTRL, DOORBELL)) { - cxl_process_mailbox(cxl_dstate); + cxl_process_mailbox(cci); } } @@ -223,7 +237,8 @@ static const MemoryRegionOps caps_ops = { }, }; -void cxl_device_register_block_init(Object *obj, CXLDeviceState *cxl_dstate) +void cxl_device_register_block_init(Object *obj, CXLDeviceState *cxl_dstate, + CXLCCI *cci) { /* This will be a BAR, so needs to be rounded up to pow2 for PCI spec */ memory_region_init(&cxl_dstate->device_registers, obj, "device-registers", @@ -233,7 +248,7 @@ void cxl_device_register_block_init(Object *obj, CXLDeviceState *cxl_dstate) "cap-array", CXL_CAPS_SIZE); memory_region_init_io(&cxl_dstate->device, obj, &dev_ops, cxl_dstate, "device-status", CXL_DEVICE_STATUS_REGISTERS_LENGTH); - memory_region_init_io(&cxl_dstate->mailbox, obj, &mailbox_ops, cxl_dstate, + memory_region_init_io(&cxl_dstate->mailbox, obj, &mailbox_ops, cci, "mailbox", CXL_MAILBOX_REGISTERS_LENGTH); memory_region_init_io(&cxl_dstate->memory_device, obj, &mdev_ops, cxl_dstate, "memory device caps", @@ -284,8 +299,9 @@ static void mailbox_reg_init_common(CXLDeviceState *cxl_dstate) static void memdev_reg_init_common(CXLDeviceState *cxl_dstate) { } -void cxl_device_register_init_common(CXLDeviceState *cxl_dstate) +void cxl_device_register_init_t3(CXLType3Dev *ct3d) { + CXLDeviceState *cxl_dstate = &ct3d->cxl_dstate; uint64_t *cap_h = cxl_dstate->caps_reg_state64; const int cap_count = 3; @@ -303,7 +319,8 @@ void cxl_device_register_init_common(CXLDeviceState *cxl_dstate) cxl_device_cap_init(cxl_dstate, MEMORY_DEVICE, 0x4000, 1); memdev_reg_init_common(cxl_dstate); - cxl_initialize_mailbox(cxl_dstate); + cxl_initialize_mailbox_t3(&ct3d->cci, DEVICE(ct3d), + CXL_MAILBOX_MAX_PAYLOAD_SIZE); } uint64_t cxl_device_get_timestamp(CXLDeviceState *cxl_dstate) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index e5ddce37c7..5484dfbbf1 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -73,8 +73,9 @@ enum { static CXLRetCode cmd_events_get_records(const struct cxl_cmd *cmd, uint8_t *payload_in, size_t len_in, uint8_t *payload_out, size_t *len_out, - CXLDeviceState *cxlds) + CXLCCI *cci) { + CXLDeviceState *cxlds = &CXL_TYPE3(cci->d)->cxl_dstate; CXLGetEventPayload *pl; uint8_t log_type; int max_recs; @@ -102,8 +103,9 @@ static CXLRetCode cmd_events_clear_records(const struct cxl_cmd *cmd, size_t len_in, uint8_t *payload_out, size_t *len_out, - CXLDeviceState *cxlds) + CXLCCI *cci) { + CXLDeviceState *cxlds = &CXL_TYPE3(cci->d)->cxl_dstate; CXLClearEventPayload *pl; pl = (CXLClearEventPayload *)payload_in; @@ -116,8 +118,9 @@ static CXLRetCode cmd_events_get_interrupt_policy(const struct cxl_cmd *cmd, size_t len_in, uint8_t *payload_out, size_t *len_out, - CXLDeviceState *cxlds) + CXLCCI *cci) { + CXLDeviceState *cxlds = &CXL_TYPE3(cci->d)->cxl_dstate; CXLEventInterruptPolicy *policy; CXLEventLog *log; @@ -159,8 +162,9 @@ static CXLRetCode cmd_events_set_interrupt_policy(const struct cxl_cmd *cmd, size_t len_in, uint8_t *payload_out, size_t *len_out, - CXLDeviceState *cxlds) + CXLCCI *cci) { + CXLDeviceState *cxlds = &CXL_TYPE3(cci->d)->cxl_dstate; CXLEventInterruptPolicy *policy; CXLEventLog *log; @@ -205,8 +209,9 @@ static CXLRetCode cmd_firmware_update_get_info(const struct cxl_cmd *cmd, size_t len, uint8_t *payload_out, size_t *len_out, - CXLDeviceState *cxl_dstate) + CXLCCI *cci) { + CXLDeviceState *cxl_dstate = &CXL_TYPE3(cci->d)->cxl_dstate; struct { uint8_t slots_supported; uint8_t slot_info; @@ -242,8 +247,9 @@ static CXLRetCode cmd_timestamp_get(const struct cxl_cmd *cmd, size_t len_in, uint8_t *payload_out, size_t *len_out, - CXLDeviceState *cxl_dstate) + CXLCCI *cci) { + CXLDeviceState *cxl_dstate = &CXL_TYPE3(cci->d)->cxl_dstate; uint64_t final_time = cxl_device_get_timestamp(cxl_dstate); stq_le_p(payload_out, final_time); @@ -258,8 +264,10 @@ static CXLRetCode cmd_timestamp_set(const struct cxl_cmd *cmd, size_t len_in, uint8_t *payload_out, size_t *len_out, - CXLDeviceState *cxl_dstate) + CXLCCI *cci) { + CXLDeviceState *cxl_dstate = &CXL_TYPE3(cci->d)->cxl_dstate; + cxl_dstate->timestamp.set = true; cxl_dstate->timestamp.last_set = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); @@ -281,7 +289,7 @@ static CXLRetCode cmd_logs_get_supported(const struct cxl_cmd *cmd, size_t len_in, uint8_t *payload_out, size_t *len_out, - CXLDeviceState *cxl_dstate) + CXLCCI *cci) { struct { uint16_t entries; @@ -295,7 +303,7 @@ static CXLRetCode cmd_logs_get_supported(const struct cxl_cmd *cmd, supported_logs->entries = 1; supported_logs->log_entries[0].uuid = cel_uuid; - supported_logs->log_entries[0].size = 4 * cxl_dstate->cel_size; + supported_logs->log_entries[0].size = 4 * cci->cel_size; *len_out = sizeof(*supported_logs); return CXL_MBOX_SUCCESS; @@ -307,7 +315,7 @@ static CXLRetCode cmd_logs_get_log(const struct cxl_cmd *cmd, size_t len_in, uint8_t *payload_out, size_t *len_out, - CXLDeviceState *cxl_dstate) + CXLCCI *cci) { struct { QemuUUID uuid; @@ -330,7 +338,7 @@ static CXLRetCode cmd_logs_get_log(const struct cxl_cmd *cmd, * the only possible failure would be if the mailbox itself isn't big * enough. */ - if (get_log->offset + get_log->length > cxl_dstate->payload_size) { + if (get_log->offset + get_log->length > cci->payload_max) { return CXL_MBOX_INVALID_INPUT; } @@ -341,8 +349,7 @@ static CXLRetCode cmd_logs_get_log(const struct cxl_cmd *cmd, /* Store off everything to local variables so we can wipe out the payload */ *len_out = get_log->length; - memmove(payload_out, cxl_dstate->cel_log + get_log->offset, - get_log->length); + memmove(payload_out, cci->cel_log + get_log->offset, get_log->length); return CXL_MBOX_SUCCESS; } @@ -353,7 +360,7 @@ static CXLRetCode cmd_identify_memory_device(const struct cxl_cmd *cmd, size_t len_in, uint8_t *payload_out, size_t *len_out, - CXLDeviceState *cxl_dstate) + CXLCCI *cci) { struct { char fw_revision[0x10]; @@ -372,9 +379,9 @@ static CXLRetCode cmd_identify_memory_device(const struct cxl_cmd *cmd, uint8_t qos_telemetry_caps; } QEMU_PACKED *id; QEMU_BUILD_BUG_ON(sizeof(*id) != 0x43); - - CXLType3Dev *ct3d = container_of(cxl_dstate, CXLType3Dev, cxl_dstate); + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); CXLType3Class *cvc = CXL_TYPE3_GET_CLASS(ct3d); + CXLDeviceState *cxl_dstate = &ct3d->cxl_dstate; if ((!QEMU_IS_ALIGNED(cxl_dstate->vmem_size, CXL_CAPACITY_MULTIPLIER)) || (!QEMU_IS_ALIGNED(cxl_dstate->pmem_size, CXL_CAPACITY_MULTIPLIER))) { @@ -407,8 +414,9 @@ static CXLRetCode cmd_ccls_get_partition_info(const struct cxl_cmd *cmd, size_t len_in, uint8_t *payload_out, size_t *len_out, - CXLDeviceState *cxl_dstate) + CXLCCI *cci) { + CXLDeviceState *cxl_dstate = &CXL_TYPE3(cci->d)->cxl_dstate; struct { uint64_t active_vmem; uint64_t active_pmem; @@ -442,13 +450,13 @@ static CXLRetCode cmd_ccls_get_lsa(const struct cxl_cmd *cmd, size_t len_in, uint8_t *payload_out, size_t *len_out, - CXLDeviceState *cxl_dstate) + CXLCCI *cci) { struct { uint32_t offset; uint32_t length; } QEMU_PACKED *get_lsa; - CXLType3Dev *ct3d = container_of(cxl_dstate, CXLType3Dev, cxl_dstate); + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); CXLType3Class *cvc = CXL_TYPE3_GET_CLASS(ct3d); uint32_t offset, length; @@ -470,7 +478,7 @@ static CXLRetCode cmd_ccls_set_lsa(const struct cxl_cmd *cmd, size_t len_in, uint8_t *payload_out, size_t *len_out, - CXLDeviceState *cxl_dstate) + CXLCCI *cci) { struct set_lsa_pl { uint32_t offset; @@ -478,7 +486,7 @@ static CXLRetCode cmd_ccls_set_lsa(const struct cxl_cmd *cmd, uint8_t data[]; } QEMU_PACKED; struct set_lsa_pl *set_lsa_payload = (void *)payload_in; - CXLType3Dev *ct3d = container_of(cxl_dstate, CXLType3Dev, cxl_dstate); + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); CXLType3Class *cvc = CXL_TYPE3_GET_CLASS(ct3d); const size_t hdr_len = offsetof(struct set_lsa_pl, data); @@ -507,7 +515,7 @@ static CXLRetCode cmd_media_get_poison_list(const struct cxl_cmd *cmd, size_t len_in, uint8_t *payload_out, size_t *len_out, - CXLDeviceState *cxl_dstate) + CXLCCI *cci) { struct get_poison_list_pl { uint64_t pa; @@ -529,7 +537,7 @@ static CXLRetCode cmd_media_get_poison_list(const struct cxl_cmd *cmd, struct get_poison_list_pl *in = (void *)payload_in; struct get_poison_list_out_pl *out = (void *)payload_out; - CXLType3Dev *ct3d = container_of(cxl_dstate, CXLType3Dev, cxl_dstate); + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); uint16_t record_count = 0, i = 0; uint64_t query_start, query_length; CXLPoisonList *poison_list = &ct3d->poison_list; @@ -586,9 +594,9 @@ static CXLRetCode cmd_media_inject_poison(const struct cxl_cmd *cmd, size_t len_in, uint8_t *payload_out, size_t *len_out, - CXLDeviceState *cxl_dstate) + CXLCCI *cci) { - CXLType3Dev *ct3d = container_of(cxl_dstate, CXLType3Dev, cxl_dstate); + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); CXLPoisonList *poison_list = &ct3d->poison_list; CXLPoison *ent; struct inject_poison_pl { @@ -629,9 +637,10 @@ static CXLRetCode cmd_media_clear_poison(const struct cxl_cmd *cmd, size_t len_in, uint8_t *payload_out, size_t *len_out, - CXLDeviceState *cxl_dstate) + CXLCCI *cci) { - CXLType3Dev *ct3d = container_of(cxl_dstate, CXLType3Dev, cxl_dstate); + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + CXLDeviceState *cxl_dstate = &ct3d->cxl_dstate; CXLPoisonList *poison_list = &ct3d->poison_list; CXLType3Class *cvc = CXL_TYPE3_GET_CLASS(ct3d); struct clear_poison_pl { @@ -745,12 +754,13 @@ static const struct cxl_cmd cxl_cmd_set[256][256] = { cmd_media_clear_poison, 72, 0 }, }; -void cxl_process_mailbox(CXLDeviceState *cxl_dstate) +void cxl_process_mailbox(CXLCCI *cci) { uint16_t ret = CXL_MBOX_SUCCESS; const struct cxl_cmd *cxl_cmd; uint64_t status_reg = 0; opcode_handler h; + CXLDeviceState *cxl_dstate = &CXL_TYPE3(cci->d)->cxl_dstate; uint64_t command_reg = cxl_dstate->mbox_reg_state64[R_CXL_DEV_MAILBOX_CMD]; uint8_t set = FIELD_EX64(command_reg, CXL_DEV_MAILBOX_CMD, COMMAND_SET); @@ -767,12 +777,12 @@ void cxl_process_mailbox(CXLDeviceState *cxl_dstate) pl_in_copy = g_memdup2(pl, len_in); /* Avoid stale data - including from earlier commands */ memset(pl, 0, CXL_MAILBOX_MAX_PAYLOAD_SIZE); - cxl_cmd = &cxl_dstate->cxl_cmd_set[set][cmd]; + cxl_cmd = &cci->cxl_cmd_set[set][cmd]; h = cxl_cmd->handler; if (h) { if (len_in == cxl_cmd->in || cxl_cmd->in == ~0) { - ret = (*h)(cxl_cmd, pl_in_copy, len_in, pl, &len_out, cxl_dstate); - assert(len_out <= cxl_dstate->payload_size); + ret = (*h)(cxl_cmd, pl, len_in, pl, &len_out, cci); + assert(len_out <= cci->payload_max); } else { ret = CXL_MBOX_INVALID_PAYLOAD_LENGTH; } @@ -798,20 +808,30 @@ void cxl_process_mailbox(CXLDeviceState *cxl_dstate) DOORBELL, 0); } -void cxl_initialize_mailbox(CXLDeviceState *cxl_dstate) +void cxl_init_cci(CXLCCI *cci, size_t payload_max) { - cxl_dstate->cxl_cmd_set = cxl_cmd_set; + cci->payload_max = payload_max; for (int set = 0; set < 256; set++) { for (int cmd = 0; cmd < 256; cmd++) { - if (cxl_dstate->cxl_cmd_set[set][cmd].handler) { - const struct cxl_cmd *c = &cxl_dstate->cxl_cmd_set[set][cmd]; + if (cci->cxl_cmd_set[set][cmd].handler) { + const struct cxl_cmd *c = &cci->cxl_cmd_set[set][cmd]; struct cel_log *log = - &cxl_dstate->cel_log[cxl_dstate->cel_size]; + &cci->cel_log[cci->cel_size]; log->opcode = (set << 8) | cmd; log->effect = c->effect; - cxl_dstate->cel_size++; + cci->cel_size++; } } } } + +void cxl_initialize_mailbox_t3(CXLCCI *cci, DeviceState *d, size_t payload_max) +{ + cci->cxl_cmd_set = cxl_cmd_set; + cci->d = d; + + /* No separation for PCI MB as protocol handled in PCI device */ + cci->intf = d; + cxl_init_cci(cci, payload_max); +} diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index 18ad853f5b..0529745786 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -716,7 +716,8 @@ static void ct3_realize(PCIDevice *pci_dev, Error **errp) pci_dev, CXL_COMPONENT_REG_BAR_IDX, PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64, mr); - cxl_device_register_block_init(OBJECT(pci_dev), &ct3d->cxl_dstate); + cxl_device_register_block_init(OBJECT(pci_dev), &ct3d->cxl_dstate, + &ct3d->cci); pci_register_bar(pci_dev, CXL_DEVICE_REG_BAR_IDX, PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64, @@ -922,7 +923,7 @@ static void ct3d_reset(DeviceState *dev) uint32_t *write_msk = ct3d->cxl_cstate.crb.cache_mem_regs_write_mask; cxl_component_register_init_common(reg_state, write_msk, CXL2_TYPE3_DEVICE); - cxl_device_register_init_common(&ct3d->cxl_dstate); + cxl_device_register_init_t3(ct3d); } static Property ct3_props[] = { diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index d7a2c4009e..779ca85319 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -111,12 +111,13 @@ typedef enum { CXL_MBOX_MAX = 0x17 } CXLRetCode; +typedef struct CXLCCI CXLCCI; typedef struct cxl_device_state CXLDeviceState; struct cxl_cmd; typedef CXLRetCode (*opcode_handler)(const struct cxl_cmd *cmd, uint8_t *payload_in, size_t len_in, uint8_t *payload_out, size_t *len_out, - CXLDeviceState *cxl_dstate); + CXLCCI *cci); struct cxl_cmd { const char *name; opcode_handler handler; @@ -140,6 +141,21 @@ typedef struct CXLEventLog { QSIMPLEQ_HEAD(, CXLEvent) events; } CXLEventLog; +typedef struct CXLCCI { + const struct cxl_cmd (*cxl_cmd_set)[256]; + struct cel_log { + uint16_t opcode; + uint16_t effect; + } cel_log[1 << 16]; + size_t cel_size; + + size_t payload_max; + /* Pointer to device hosting the CCI */ + DeviceState *d; + /* Pointer to the device hosting the protocol conversion */ + DeviceState *intf; +} CXLCCI; + typedef struct cxl_device_state { MemoryRegion device_registers; @@ -173,11 +189,6 @@ typedef struct cxl_device_state { uint32_t mbox_reg_state32[CXL_MAILBOX_REGISTERS_LENGTH / 4]; uint64_t mbox_reg_state64[CXL_MAILBOX_REGISTERS_LENGTH / 8]; }; - struct cel_log { - uint16_t opcode; - uint16_t effect; - } cel_log[1 << 16]; - size_t cel_size; }; struct { @@ -196,10 +207,12 @@ typedef struct cxl_device_state { } CXLDeviceState; /* Initialize the register block for a device */ -void cxl_device_register_block_init(Object *obj, CXLDeviceState *dev); +void cxl_device_register_block_init(Object *obj, CXLDeviceState *dev, + CXLCCI *cci); +typedef struct CXLType3Dev CXLType3Dev; /* Set up default values for the register block */ -void cxl_device_register_init_common(CXLDeviceState *dev); +void cxl_device_register_init_t3(CXLType3Dev *ct3d); /* * CXL 2.0 - 8.2.8.1 including errata F4 @@ -245,8 +258,9 @@ CXL_DEVICE_CAPABILITY_HEADER_REGISTER(MEMORY_DEVICE, CXL_DEVICE_CAP_HDR1_OFFSET + CXL_DEVICE_CAP_REG_SIZE * 2) -void cxl_initialize_mailbox(CXLDeviceState *cxl_dstate); -void cxl_process_mailbox(CXLDeviceState *cxl_dstate); +void cxl_initialize_mailbox_t3(CXLCCI *cci, DeviceState *d, size_t payload_max); +void cxl_init_cci(CXLCCI *cci, size_t payload_max); +void cxl_process_mailbox(CXLCCI *cci); #define cxl_device_cap_init(dstate, reg, cap_id, ver) \ do { \ @@ -347,6 +361,7 @@ struct CXLType3Dev { AddressSpace hostpmem_as; CXLComponentState cxl_cstate; CXLDeviceState cxl_dstate; + CXLCCI cci; /* Primary PCI mailbox CCI */ /* DOE */ DOECap doe_cdat; From c9460561edbd8b2d4adbf1f7c5cb4ad4d210de4c Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Mon, 23 Oct 2023 17:07:53 +0100 Subject: [PATCH 709/974] hw/cxl/mbox: Generalize the CCI command processing By moving the parts of the mailbox command handling that are CCI type specific out to the caller, make the main handling code generic. Rename it to cxl_process_cci_message() to reflect this new generality. Change the type3 mailbox handling (reused shortly for the switch mailbox CCI) to take a snapshot of the mailbox input data rather than operating on it in place. This reduces the chance of bugs due to aliasing going forwars. Signed-off-by: Jonathan Cameron Message-Id: <20231023160806.13206-5-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-device-utils.c | 44 +++++++++++++++++++++++++++++++- hw/cxl/cxl-mailbox-utils.c | 51 ++++++++----------------------------- include/hw/cxl/cxl_device.h | 5 +++- 3 files changed, 57 insertions(+), 43 deletions(-) diff --git a/hw/cxl/cxl-device-utils.c b/hw/cxl/cxl-device-utils.c index 327949a805..eb86634250 100644 --- a/hw/cxl/cxl-device-utils.c +++ b/hw/cxl/cxl-device-utils.c @@ -157,7 +157,49 @@ static void mailbox_reg_write(void *opaque, hwaddr offset, uint64_t value, if (ARRAY_FIELD_EX32(cxl_dstate->mbox_reg_state32, CXL_DEV_MAILBOX_CTRL, DOORBELL)) { - cxl_process_mailbox(cci); + uint64_t command_reg = + cxl_dstate->mbox_reg_state64[R_CXL_DEV_MAILBOX_CMD]; + uint8_t cmd_set = FIELD_EX64(command_reg, CXL_DEV_MAILBOX_CMD, + COMMAND_SET); + uint8_t cmd = FIELD_EX64(command_reg, CXL_DEV_MAILBOX_CMD, COMMAND); + size_t len_in = FIELD_EX64(command_reg, CXL_DEV_MAILBOX_CMD, LENGTH); + uint8_t *pl = cxl_dstate->mbox_reg_state + A_CXL_DEV_CMD_PAYLOAD; + /* + * Copy taken to avoid need for individual command handlers to care + * about aliasing. + */ + g_autofree uint8_t *pl_in_copy = NULL; + size_t len_out = 0; + uint64_t status_reg; + bool bg_started = false; + int rc; + + pl_in_copy = g_memdup2(pl, len_in); + if (len_in == 0 || pl_in_copy) { + /* Avoid stale data - including from earlier cmds */ + memset(pl, 0, CXL_MAILBOX_MAX_PAYLOAD_SIZE); + rc = cxl_process_cci_message(cci, cmd_set, cmd, len_in, pl_in_copy, + &len_out, pl, &bg_started); + } else { + rc = CXL_MBOX_INTERNAL_ERROR; + } + + /* Set bg and the return code */ + status_reg = FIELD_DP64(0, CXL_DEV_MAILBOX_STS, BG_OP, + bg_started ? 1 : 0); + status_reg = FIELD_DP64(status_reg, CXL_DEV_MAILBOX_STS, ERRNO, rc); + /* Set the return length */ + command_reg = FIELD_DP64(0, CXL_DEV_MAILBOX_CMD, COMMAND_SET, cmd_set); + command_reg = FIELD_DP64(command_reg, CXL_DEV_MAILBOX_CMD, + COMMAND, cmd); + command_reg = FIELD_DP64(command_reg, CXL_DEV_MAILBOX_CMD, + LENGTH, len_out); + + cxl_dstate->mbox_reg_state64[R_CXL_DEV_MAILBOX_CMD] = command_reg; + cxl_dstate->mbox_reg_state64[R_CXL_DEV_MAILBOX_STS] = status_reg; + /* Tell the host we're done */ + ARRAY_FIELD_DP32(cxl_dstate->mbox_reg_state32, CXL_DEV_MAILBOX_CTRL, + DOORBELL, 0); } } diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 5484dfbbf1..239acc659d 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -754,58 +754,27 @@ static const struct cxl_cmd cxl_cmd_set[256][256] = { cmd_media_clear_poison, 72, 0 }, }; -void cxl_process_mailbox(CXLCCI *cci) +int cxl_process_cci_message(CXLCCI *cci, uint8_t set, uint8_t cmd, + size_t len_in, uint8_t *pl_in, size_t *len_out, + uint8_t *pl_out, bool *bg_started) { - uint16_t ret = CXL_MBOX_SUCCESS; const struct cxl_cmd *cxl_cmd; - uint64_t status_reg = 0; opcode_handler h; - CXLDeviceState *cxl_dstate = &CXL_TYPE3(cci->d)->cxl_dstate; - uint64_t command_reg = cxl_dstate->mbox_reg_state64[R_CXL_DEV_MAILBOX_CMD]; - uint8_t set = FIELD_EX64(command_reg, CXL_DEV_MAILBOX_CMD, COMMAND_SET); - uint8_t cmd = FIELD_EX64(command_reg, CXL_DEV_MAILBOX_CMD, COMMAND); - uint16_t len_in = FIELD_EX64(command_reg, CXL_DEV_MAILBOX_CMD, LENGTH); - uint8_t *pl = cxl_dstate->mbox_reg_state + A_CXL_DEV_CMD_PAYLOAD; - /* - * Copy taken to avoid need for individual command handlers to care - * about aliasing. - */ - g_autofree uint8_t *pl_in_copy = NULL; - size_t len_out = 0; - - pl_in_copy = g_memdup2(pl, len_in); - /* Avoid stale data - including from earlier commands */ - memset(pl, 0, CXL_MAILBOX_MAX_PAYLOAD_SIZE); + *len_out = 0; cxl_cmd = &cci->cxl_cmd_set[set][cmd]; h = cxl_cmd->handler; - if (h) { - if (len_in == cxl_cmd->in || cxl_cmd->in == ~0) { - ret = (*h)(cxl_cmd, pl, len_in, pl, &len_out, cci); - assert(len_out <= cci->payload_max); - } else { - ret = CXL_MBOX_INVALID_PAYLOAD_LENGTH; - } - } else { + if (!h) { qemu_log_mask(LOG_UNIMP, "Command %04xh not implemented\n", set << 8 | cmd); - ret = CXL_MBOX_UNSUPPORTED; + return CXL_MBOX_UNSUPPORTED; } - /* Set the return code */ - status_reg = FIELD_DP64(0, CXL_DEV_MAILBOX_STS, ERRNO, ret); + if (len_in != cxl_cmd->in && cxl_cmd->in != ~0) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } - /* Set the return length */ - command_reg = FIELD_DP64(command_reg, CXL_DEV_MAILBOX_CMD, COMMAND_SET, 0); - command_reg = FIELD_DP64(command_reg, CXL_DEV_MAILBOX_CMD, COMMAND, 0); - command_reg = FIELD_DP64(command_reg, CXL_DEV_MAILBOX_CMD, LENGTH, len_out); - - cxl_dstate->mbox_reg_state64[R_CXL_DEV_MAILBOX_CMD] = command_reg; - cxl_dstate->mbox_reg_state64[R_CXL_DEV_MAILBOX_STS] = status_reg; - - /* Tell the host we're done */ - ARRAY_FIELD_DP32(cxl_dstate->mbox_reg_state32, CXL_DEV_MAILBOX_CTRL, - DOORBELL, 0); + return (*h)(cxl_cmd, pl_in, len_in, pl_out, len_out, cci); } void cxl_init_cci(CXLCCI *cci, size_t payload_max) diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index 779ca85319..6f8040b5ff 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -260,7 +260,10 @@ CXL_DEVICE_CAPABILITY_HEADER_REGISTER(MEMORY_DEVICE, void cxl_initialize_mailbox_t3(CXLCCI *cci, DeviceState *d, size_t payload_max); void cxl_init_cci(CXLCCI *cci, size_t payload_max); -void cxl_process_mailbox(CXLCCI *cci); +int cxl_process_cci_message(CXLCCI *cci, uint8_t set, uint8_t cmd, + size_t len_in, uint8_t *pl_in, + size_t *len_out, uint8_t *pl_out, + bool *bg_started); #define cxl_device_cap_init(dstate, reg, cap_id, ver) \ do { \ From 2710d49a7c8b9b117a46847c7ace5eb21d48e882 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Mon, 23 Oct 2023 17:07:54 +0100 Subject: [PATCH 710/974] hw/pci-bridge/cxl_upstream: Move defintion of device to header. To avoid repetition of switch upstream port specific data in the CXLDeviceState structure it will be necessary to access the switch USP specific data from mailbox callbacks. Hence move it to cxl_device.h so it is no longer an opaque structure. Signed-off-by: Jonathan Cameron Reviewed-by: Fan Ni Message-Id: <20231023160806.13206-6-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci-bridge/cxl_upstream.c | 11 +---------- include/hw/pci-bridge/cxl_upstream_port.h | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 10 deletions(-) create mode 100644 include/hw/pci-bridge/cxl_upstream_port.h diff --git a/hw/pci-bridge/cxl_upstream.c b/hw/pci-bridge/cxl_upstream.c index b81bb5fec9..36737189c6 100644 --- a/hw/pci-bridge/cxl_upstream.c +++ b/hw/pci-bridge/cxl_upstream.c @@ -14,6 +14,7 @@ #include "hw/pci/msi.h" #include "hw/pci/pcie.h" #include "hw/pci/pcie_port.h" +#include "hw/pci-bridge/cxl_upstream_port.h" /* * Null value of all Fs suggested by IEEE RA guidelines for use of * EU, OUI and CID @@ -30,16 +31,6 @@ #define CXL_UPSTREAM_PORT_DVSEC_OFFSET \ (CXL_UPSTREAM_PORT_SN_OFFSET + PCI_EXT_CAP_DSN_SIZEOF) -typedef struct CXLUpstreamPort { - /*< private >*/ - PCIEPort parent_obj; - - /*< public >*/ - CXLComponentState cxl_cstate; - DOECap doe_cdat; - uint64_t sn; -} CXLUpstreamPort; - CXLComponentState *cxl_usp_to_cstate(CXLUpstreamPort *usp) { return &usp->cxl_cstate; diff --git a/include/hw/pci-bridge/cxl_upstream_port.h b/include/hw/pci-bridge/cxl_upstream_port.h new file mode 100644 index 0000000000..b02aa8f659 --- /dev/null +++ b/include/hw/pci-bridge/cxl_upstream_port.h @@ -0,0 +1,18 @@ + +#ifndef CXL_USP_H +#define CXL_USP_H +#include "hw/pci/pcie.h" +#include "hw/pci/pcie_port.h" +#include "hw/cxl/cxl.h" + +typedef struct CXLUpstreamPort { + /*< private >*/ + PCIEPort parent_obj; + + /*< public >*/ + CXLComponentState cxl_cstate; + DOECap doe_cdat; + uint64_t sn; +} CXLUpstreamPort; + +#endif /* CXL_SUP_H */ From 4a58330343e6a16f6828e225fd0c054c8d1916bd Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Mon, 23 Oct 2023 17:07:55 +0100 Subject: [PATCH 711/974] hw/cxl: Add a switch mailbox CCI function CXL switch CCIs were added in CXL r3.0. They are a PCI function, identified by class code that provides a CXL mailbox (identical to that previously defined for CXL type 3 memory devices) over which various FM-API commands may be used. Whilst the intent of this feature is enable switch control from a BMC attached to a switch upstream port, it is also useful to allow emulation of this feature on the upstream port connected to a host using the CXL devices as this greatly simplifies testing. Signed-off-by: Jonathan Cameron Message-Id: <20231023160806.13206-7-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-device-utils.c | 27 ++++++ hw/cxl/cxl-mailbox-utils.c | 18 ++++ hw/cxl/meson.build | 1 + hw/cxl/switch-mailbox-cci.c | 111 ++++++++++++++++++++++ include/hw/cxl/cxl_component.h | 3 +- include/hw/cxl/cxl_device.h | 15 +++ include/hw/pci-bridge/cxl_upstream_port.h | 1 + 7 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 hw/cxl/switch-mailbox-cci.c diff --git a/hw/cxl/cxl-device-utils.c b/hw/cxl/cxl-device-utils.c index eb86634250..f8938678c7 100644 --- a/hw/cxl/cxl-device-utils.c +++ b/hw/cxl/cxl-device-utils.c @@ -67,6 +67,9 @@ static uint64_t mailbox_reg_read(void *opaque, hwaddr offset, unsigned size) if (object_dynamic_cast(OBJECT(cci->intf), TYPE_CXL_TYPE3)) { cxl_dstate = &CXL_TYPE3(cci->intf)->cxl_dstate; + } else if (object_dynamic_cast(OBJECT(cci->intf), + TYPE_CXL_SWITCH_MAILBOX_CCI)) { + cxl_dstate = &CXL_SWITCH_MAILBOX_CCI(cci->intf)->cxl_dstate; } else { return 0; } @@ -135,6 +138,9 @@ static void mailbox_reg_write(void *opaque, hwaddr offset, uint64_t value, if (object_dynamic_cast(OBJECT(cci->intf), TYPE_CXL_TYPE3)) { cxl_dstate = &CXL_TYPE3(cci->intf)->cxl_dstate; + } else if (object_dynamic_cast(OBJECT(cci->intf), + TYPE_CXL_SWITCH_MAILBOX_CCI)) { + cxl_dstate = &CXL_SWITCH_MAILBOX_CCI(cci->intf)->cxl_dstate; } else { return; } @@ -365,6 +371,27 @@ void cxl_device_register_init_t3(CXLType3Dev *ct3d) CXL_MAILBOX_MAX_PAYLOAD_SIZE); } +void cxl_device_register_init_swcci(CSWMBCCIDev *sw) +{ + CXLDeviceState *cxl_dstate = &sw->cxl_dstate; + uint64_t *cap_h = cxl_dstate->caps_reg_state64; + const int cap_count = 3; + + /* CXL Device Capabilities Array Register */ + ARRAY_FIELD_DP64(cap_h, CXL_DEV_CAP_ARRAY, CAP_ID, 0); + ARRAY_FIELD_DP64(cap_h, CXL_DEV_CAP_ARRAY, CAP_VERSION, 1); + ARRAY_FIELD_DP64(cap_h, CXL_DEV_CAP_ARRAY, CAP_COUNT, cap_count); + + cxl_device_cap_init(cxl_dstate, DEVICE_STATUS, 1, 2); + device_reg_init_common(cxl_dstate); + + cxl_device_cap_init(cxl_dstate, MAILBOX, 2, 1); + mailbox_reg_init_common(cxl_dstate); + + cxl_device_cap_init(cxl_dstate, MEMORY_DEVICE, 0x4000, 1); + memdev_reg_init_common(cxl_dstate); +} + uint64_t cxl_device_get_timestamp(CXLDeviceState *cxl_dstate) { uint64_t time, delta; diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 239acc659d..28ea02fcbe 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -754,6 +754,15 @@ static const struct cxl_cmd cxl_cmd_set[256][256] = { cmd_media_clear_poison, 72, 0 }, }; +static const struct cxl_cmd cxl_cmd_set_sw[256][256] = { + [TIMESTAMP][GET] = { "TIMESTAMP_GET", cmd_timestamp_get, 0, 0 }, + [TIMESTAMP][SET] = { "TIMESTAMP_SET", cmd_timestamp_set, 0, + IMMEDIATE_POLICY_CHANGE }, + [LOGS][GET_SUPPORTED] = { "LOGS_GET_SUPPORTED", cmd_logs_get_supported, 0, + 0 }, + [LOGS][GET_LOG] = { "LOGS_GET_LOG", cmd_logs_get_log, 0x18, 0 }, +}; + int cxl_process_cci_message(CXLCCI *cci, uint8_t set, uint8_t cmd, size_t len_in, uint8_t *pl_in, size_t *len_out, uint8_t *pl_out, bool *bg_started) @@ -795,6 +804,15 @@ void cxl_init_cci(CXLCCI *cci, size_t payload_max) } } +void cxl_initialize_mailbox_swcci(CXLCCI *cci, DeviceState *intf, + DeviceState *d, size_t payload_max) +{ + cci->cxl_cmd_set = cxl_cmd_set_sw; + cci->d = d; + cci->intf = intf; + cxl_init_cci(cci, payload_max); +} + void cxl_initialize_mailbox_t3(CXLCCI *cci, DeviceState *d, size_t payload_max) { cci->cxl_cmd_set = cxl_cmd_set; diff --git a/hw/cxl/meson.build b/hw/cxl/meson.build index e261ff3881..ea0aebf6e3 100644 --- a/hw/cxl/meson.build +++ b/hw/cxl/meson.build @@ -6,6 +6,7 @@ system_ss.add(when: 'CONFIG_CXL', 'cxl-host.c', 'cxl-cdat.c', 'cxl-events.c', + 'switch-mailbox-cci.c', ), if_false: files( 'cxl-host-stubs.c', diff --git a/hw/cxl/switch-mailbox-cci.c b/hw/cxl/switch-mailbox-cci.c new file mode 100644 index 0000000000..ba399c6240 --- /dev/null +++ b/hw/cxl/switch-mailbox-cci.c @@ -0,0 +1,111 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Emulation of a CXL Switch Mailbox CCI PCIe function. + * + * Copyright (c) 2023 Huawei Technologies. + * + * From www.computeexpresslink.org + * Compute Express Link (CXL) Specification revision 3.0 Version 1.0 + */ +#include "qemu/osdep.h" +#include "hw/pci/pci.h" +#include "hw/pci-bridge/cxl_upstream_port.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "hw/qdev-properties.h" +#include "hw/cxl/cxl.h" + +static void cswmbcci_reset(DeviceState *dev) +{ + CSWMBCCIDev *cswmb = CXL_SWITCH_MAILBOX_CCI(dev); + cxl_device_register_init_swcci(cswmb); +} + +static void cswbcci_realize(PCIDevice *pci_dev, Error **errp) +{ + CSWMBCCIDev *cswmb = CXL_SWITCH_MAILBOX_CCI(pci_dev); + CXLComponentState *cxl_cstate = &cswmb->cxl_cstate; + CXLDeviceState *cxl_dstate = &cswmb->cxl_dstate; + CXLDVSECRegisterLocator *regloc_dvsec; + CXLUpstreamPort *usp; + + if (!cswmb->target) { + error_setg(errp, "Target not set"); + return; + } + usp = CXL_USP(cswmb->target); + + pcie_endpoint_cap_init(pci_dev, 0x80); + cxl_cstate->dvsec_offset = 0x100; + cxl_cstate->pdev = pci_dev; + cswmb->cci = &usp->swcci; + cxl_device_register_block_init(OBJECT(pci_dev), cxl_dstate, cswmb->cci); + pci_register_bar(pci_dev, 0, + PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_TYPE_64, + &cxl_dstate->device_registers); + regloc_dvsec = &(CXLDVSECRegisterLocator) { + .rsvd = 0, + .reg0_base_lo = RBI_CXL_DEVICE_REG | 0, + .reg0_base_hi = 0, + }; + cxl_component_create_dvsec(cxl_cstate, CXL3_SWITCH_MAILBOX_CCI, + REG_LOC_DVSEC_LENGTH, REG_LOC_DVSEC, + REG_LOC_DVSEC_REVID, (uint8_t *)regloc_dvsec); + + cxl_initialize_mailbox_swcci(cswmb->cci, DEVICE(pci_dev), + DEVICE(cswmb->target), + CXL_MAILBOX_MAX_PAYLOAD_SIZE); +} + +static void cswmbcci_exit(PCIDevice *pci_dev) +{ + /* Nothing to do here yet */ +} + +static Property cxl_switch_cci_props[] = { + DEFINE_PROP_LINK("target", CSWMBCCIDev, + target, TYPE_CXL_USP, PCIDevice *), + DEFINE_PROP_END_OF_LIST(), +}; + +static void cswmbcci_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc); + + pc->realize = cswbcci_realize; + pc->exit = cswmbcci_exit; + /* Serial bus, CXL Switch CCI */ + pc->class_id = 0x0c0b; + /* + * Huawei Technologies + * CXL Switch Mailbox CCI - DID assigned for emulation only. + * No real hardware will ever use this ID. + */ + pc->vendor_id = 0x19e5; + pc->device_id = 0xa123; + pc->revision = 0; + dc->desc = "CXL Switch Mailbox CCI"; + dc->reset = cswmbcci_reset; + device_class_set_props(dc, cxl_switch_cci_props); +} + +static const TypeInfo cswmbcci_info = { + .name = TYPE_CXL_SWITCH_MAILBOX_CCI, + .parent = TYPE_PCI_DEVICE, + .class_init = cswmbcci_class_init, + .instance_size = sizeof(CSWMBCCIDev), + .interfaces = (InterfaceInfo[]) { + { INTERFACE_PCIE_DEVICE }, + { } + }, +}; + +static void cxl_switch_mailbox_cci_register(void) +{ + type_register_static(&cswmbcci_info); +} +type_init(cxl_switch_mailbox_cci_register); diff --git a/include/hw/cxl/cxl_component.h b/include/hw/cxl/cxl_component.h index e52dd8d2b9..5227a8e833 100644 --- a/include/hw/cxl/cxl_component.h +++ b/include/hw/cxl/cxl_component.h @@ -26,7 +26,8 @@ enum reg_type { CXL2_LOGICAL_DEVICE, CXL2_ROOT_PORT, CXL2_UPSTREAM_PORT, - CXL2_DOWNSTREAM_PORT + CXL2_DOWNSTREAM_PORT, + CXL3_SWITCH_MAILBOX_CCI, }; /* diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index 6f8040b5ff..fa73ed03e5 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -211,8 +211,10 @@ void cxl_device_register_block_init(Object *obj, CXLDeviceState *dev, CXLCCI *cci); typedef struct CXLType3Dev CXLType3Dev; +typedef struct CSWMBCCIDev CSWMBCCIDev; /* Set up default values for the register block */ void cxl_device_register_init_t3(CXLType3Dev *ct3d); +void cxl_device_register_init_swcci(CSWMBCCIDev *sw); /* * CXL 2.0 - 8.2.8.1 including errata F4 @@ -259,6 +261,8 @@ CXL_DEVICE_CAPABILITY_HEADER_REGISTER(MEMORY_DEVICE, CXL_DEVICE_CAP_REG_SIZE * 2) void cxl_initialize_mailbox_t3(CXLCCI *cci, DeviceState *d, size_t payload_max); +void cxl_initialize_mailbox_swcci(CXLCCI *cci, DeviceState *intf, + DeviceState *d, size_t payload_max); void cxl_init_cci(CXLCCI *cci, size_t payload_max); int cxl_process_cci_message(CXLCCI *cci, uint8_t set, uint8_t cmd, size_t len_in, uint8_t *pl_in, @@ -397,6 +401,17 @@ struct CXLType3Class { uint8_t *data); }; +struct CSWMBCCIDev { + PCIDevice parent_obj; + PCIDevice *target; + CXLComponentState cxl_cstate; + CXLDeviceState cxl_dstate; + CXLCCI *cci; +}; + +#define TYPE_CXL_SWITCH_MAILBOX_CCI "cxl-switch-mailbox-cci" +OBJECT_DECLARE_TYPE(CSWMBCCIDev, CSWMBCCIClass, CXL_SWITCH_MAILBOX_CCI) + MemTxResult cxl_type3_read(PCIDevice *d, hwaddr host_addr, uint64_t *data, unsigned size, MemTxAttrs attrs); MemTxResult cxl_type3_write(PCIDevice *d, hwaddr host_addr, uint64_t data, diff --git a/include/hw/pci-bridge/cxl_upstream_port.h b/include/hw/pci-bridge/cxl_upstream_port.h index b02aa8f659..12635139f6 100644 --- a/include/hw/pci-bridge/cxl_upstream_port.h +++ b/include/hw/pci-bridge/cxl_upstream_port.h @@ -11,6 +11,7 @@ typedef struct CXLUpstreamPort { /*< public >*/ CXLComponentState cxl_cstate; + CXLCCI swcci; DOECap doe_cdat; uint64_t sn; } CXLUpstreamPort; From 6cf416c176f11bc093fcc1818d6c9a95136f4c59 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Mon, 23 Oct 2023 17:07:56 +0100 Subject: [PATCH 712/974] hw/cxl/mbox: Add Information and Status / Identify command Add this command that is only available via out of band CCIs. It replicates information that can be discovered inband via PCI config space. Signed-off-by: Jonathan Cameron Message-Id: <20231023160806.13206-8-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-mailbox-utils.c | 55 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 28ea02fcbe..6741698ee7 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -11,6 +11,7 @@ #include "hw/cxl/cxl.h" #include "hw/cxl/cxl_events.h" #include "hw/pci/pci.h" +#include "hw/pci-bridge/cxl_upstream_port.h" #include "qemu/cutils.h" #include "qemu/log.h" #include "qemu/units.h" @@ -44,6 +45,8 @@ */ enum { + INFOSTAT = 0x00, + #define IS_IDENTIFY 0x1 EVENTS = 0x01, #define GET_RECORDS 0x0 #define CLEAR_RECORDS 0x1 @@ -203,6 +206,57 @@ static CXLRetCode cmd_events_set_interrupt_policy(const struct cxl_cmd *cmd, return CXL_MBOX_SUCCESS; } +/* CXL r3.0 section 8.2.9.1.1: Identify (Opcode 0001h) */ +static CXLRetCode cmd_infostat_identify(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + PCIDeviceClass *class = PCI_DEVICE_GET_CLASS(cci->d); + struct { + uint16_t pcie_vid; + uint16_t pcie_did; + uint16_t pcie_subsys_vid; + uint16_t pcie_subsys_id; + uint64_t sn; + uint8_t max_message_size; + uint8_t component_type; + } QEMU_PACKED *is_identify; + QEMU_BUILD_BUG_ON(sizeof(*is_identify) != 18); + + is_identify = (void *)payload_out; + memset(is_identify, 0, sizeof(*is_identify)); + is_identify->pcie_vid = class->vendor_id; + is_identify->pcie_did = class->device_id; + if (object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_USP)) { + is_identify->sn = CXL_USP(cci->d)->sn; + /* Subsystem info not defined for a USP */ + is_identify->pcie_subsys_vid = 0; + is_identify->pcie_subsys_id = 0; + is_identify->component_type = 0x0; /* Switch */ + } else if (object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_TYPE3)) { + PCIDevice *pci_dev = PCI_DEVICE(cci->d); + + is_identify->sn = CXL_TYPE3(cci->d)->sn; + /* + * We can't always use class->subsystem_vendor_id as + * it is not set if the defaults are used. + */ + is_identify->pcie_subsys_vid = + pci_get_word(pci_dev->config + PCI_SUBSYSTEM_VENDOR_ID); + is_identify->pcie_subsys_id = + pci_get_word(pci_dev->config + PCI_SUBSYSTEM_ID); + is_identify->component_type = 0x3; /* Type 3 */ + } + + /* TODO: Allow this to vary across different CCIs */ + is_identify->max_message_size = 9; /* 512 bytes - MCTP_CXL_MAILBOX_BYTES */ + *len_out = sizeof(*is_identify); + return CXL_MBOX_SUCCESS; +} + /* 8.2.9.2.1 */ static CXLRetCode cmd_firmware_update_get_info(const struct cxl_cmd *cmd, uint8_t *payload_in, @@ -755,6 +809,7 @@ static const struct cxl_cmd cxl_cmd_set[256][256] = { }; static const struct cxl_cmd cxl_cmd_set_sw[256][256] = { + [INFOSTAT][IS_IDENTIFY] = { "IDENTIFY", cmd_infostat_identify, 0, 0 }, [TIMESTAMP][GET] = { "TIMESTAMP_GET", cmd_timestamp_get, 0, 0 }, [TIMESTAMP][SET] = { "TIMESTAMP_SET", cmd_timestamp_set, 0, IMMEDIATE_POLICY_CHANGE }, From 3314efd276ada18cc0b8beb70b8943f8deb872b7 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Mon, 23 Oct 2023 17:07:57 +0100 Subject: [PATCH 713/974] hw/cxl/mbox: Add Physical Switch Identify command. Enable it for the switch CCI. Signed-off-by: Jonathan Cameron Message-Id: <20231023160806.13206-9-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-mailbox-utils.c | 65 ++++++++++++++++++++++++++++++++++ hw/pci-bridge/cxl_downstream.c | 4 +-- include/hw/cxl/cxl.h | 6 ++++ 3 files changed, 72 insertions(+), 3 deletions(-) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 6741698ee7..6ada49d37c 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -70,6 +70,8 @@ enum { #define GET_POISON_LIST 0x0 #define INJECT_POISON 0x1 #define CLEAR_POISON 0x2 + PHYSICAL_SWITCH = 0x51, + #define IDENTIFY_SWITCH_DEVICE 0x0 }; @@ -257,6 +259,67 @@ static CXLRetCode cmd_infostat_identify(const struct cxl_cmd *cmd, return CXL_MBOX_SUCCESS; } +static void cxl_set_dsp_active_bm(PCIBus *b, PCIDevice *d, + void *private) +{ + uint8_t *bm = private; + if (object_dynamic_cast(OBJECT(d), TYPE_CXL_DSP)) { + uint8_t port = PCIE_PORT(d)->port; + bm[port / 8] |= 1 << (port % 8); + } +} + +/* CXL r3 8.2.9.1.1 */ +static CXLRetCode cmd_identify_switch_device(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + PCIEPort *usp = PCIE_PORT(cci->d); + PCIBus *bus = &PCI_BRIDGE(cci->d)->sec_bus; + int num_phys_ports = pcie_count_ds_ports(bus); + + struct cxl_fmapi_ident_switch_dev_resp_pl { + uint8_t ingress_port_id; + uint8_t rsvd; + uint8_t num_physical_ports; + uint8_t num_vcss; + uint8_t active_port_bitmask[0x20]; + uint8_t active_vcs_bitmask[0x20]; + uint16_t total_vppbs; + uint16_t bound_vppbs; + uint8_t num_hdm_decoders_per_usp; + } QEMU_PACKED *out; + QEMU_BUILD_BUG_ON(sizeof(*out) != 0x49); + + out = (struct cxl_fmapi_ident_switch_dev_resp_pl *)payload_out; + *out = (struct cxl_fmapi_ident_switch_dev_resp_pl) { + .num_physical_ports = num_phys_ports + 1, /* 1 USP */ + .num_vcss = 1, /* Not yet support multiple VCS - potentialy tricky */ + .active_vcs_bitmask[0] = 0x1, + .total_vppbs = num_phys_ports + 1, + .bound_vppbs = num_phys_ports + 1, + .num_hdm_decoders_per_usp = 4, + }; + + /* Depends on the CCI type */ + if (object_dynamic_cast(OBJECT(cci->intf), TYPE_PCIE_PORT)) { + out->ingress_port_id = PCIE_PORT(cci->intf)->port; + } else { + /* MCTP? */ + out->ingress_port_id = 0; + } + + pci_for_each_device_under_bus(bus, cxl_set_dsp_active_bm, + out->active_port_bitmask); + out->active_port_bitmask[usp->port / 8] |= (1 << usp->port % 8); + + *len_out = sizeof(*out); + + return CXL_MBOX_SUCCESS; +} /* 8.2.9.2.1 */ static CXLRetCode cmd_firmware_update_get_info(const struct cxl_cmd *cmd, uint8_t *payload_in, @@ -816,6 +879,8 @@ static const struct cxl_cmd cxl_cmd_set_sw[256][256] = { [LOGS][GET_SUPPORTED] = { "LOGS_GET_SUPPORTED", cmd_logs_get_supported, 0, 0 }, [LOGS][GET_LOG] = { "LOGS_GET_LOG", cmd_logs_get_log, 0x18, 0 }, + [PHYSICAL_SWITCH][IDENTIFY_SWITCH_DEVICE] = { "IDENTIFY_SWITCH_DEVICE", + cmd_identify_switch_device, 0, 0 }, }; int cxl_process_cci_message(CXLCCI *cci, uint8_t set, uint8_t cmd, diff --git a/hw/pci-bridge/cxl_downstream.c b/hw/pci-bridge/cxl_downstream.c index 8c0f759add..8d99e1e96d 100644 --- a/hw/pci-bridge/cxl_downstream.c +++ b/hw/pci-bridge/cxl_downstream.c @@ -13,6 +13,7 @@ #include "hw/pci/msi.h" #include "hw/pci/pcie.h" #include "hw/pci/pcie_port.h" +#include "hw/cxl/cxl.h" #include "qapi/error.h" typedef struct CXLDownstreamPort { @@ -23,9 +24,6 @@ typedef struct CXLDownstreamPort { CXLComponentState cxl_cstate; } CXLDownstreamPort; -#define TYPE_CXL_DSP "cxl-downstream" -DECLARE_INSTANCE_CHECKER(CXLDownstreamPort, CXL_DSP, TYPE_CXL_DSP) - #define CXL_DOWNSTREAM_PORT_MSI_OFFSET 0x70 #define CXL_DOWNSTREAM_PORT_MSI_NR_VECTOR 1 #define CXL_DOWNSTREAM_PORT_EXP_OFFSET 0x90 diff --git a/include/hw/cxl/cxl.h b/include/hw/cxl/cxl.h index 4944725849..75e47b6864 100644 --- a/include/hw/cxl/cxl.h +++ b/include/hw/cxl/cxl.h @@ -61,4 +61,10 @@ OBJECT_DECLARE_SIMPLE_TYPE(CXLHost, PXB_CXL_HOST) typedef struct CXLUpstreamPort CXLUpstreamPort; DECLARE_INSTANCE_CHECKER(CXLUpstreamPort, CXL_USP, TYPE_CXL_USP) CXLComponentState *cxl_usp_to_cstate(CXLUpstreamPort *usp); + +#define TYPE_CXL_DSP "cxl-downstream" + +typedef struct CXLDownstreamPort CXLDownstreamPort; +DECLARE_INSTANCE_CHECKER(CXLDownstreamPort, CXL_DSP, TYPE_CXL_DSP) + #endif From 314f5033c639ebe8218078a17513935747f15d9d Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Mon, 23 Oct 2023 17:07:58 +0100 Subject: [PATCH 714/974] hw/pci-bridge/cxl_downstream: Set default link width and link speed Without these being set the PCIE Link Capabilities register has invalid values in these two fields. Signed-off-by: Jonathan Cameron Message-Id: <20231023160806.13206-10-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci-bridge/cxl_downstream.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/hw/pci-bridge/cxl_downstream.c b/hw/pci-bridge/cxl_downstream.c index 8d99e1e96d..405a133eef 100644 --- a/hw/pci-bridge/cxl_downstream.c +++ b/hw/pci-bridge/cxl_downstream.c @@ -210,6 +210,19 @@ static void cxl_dsp_exitfn(PCIDevice *d) pci_bridge_exitfn(d); } +static void cxl_dsp_instance_post_init(Object *obj) +{ + PCIESlot *s = PCIE_SLOT(obj); + + if (!s->speed) { + s->speed = QEMU_PCI_EXP_LNK_2_5GT; + } + + if (!s->width) { + s->width = QEMU_PCI_EXP_LNK_X1; + } +} + static void cxl_dsp_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -230,6 +243,7 @@ static const TypeInfo cxl_dsp_info = { .name = TYPE_CXL_DSP, .instance_size = sizeof(CXLDownstreamPort), .parent = TYPE_PCIE_SLOT, + .instance_post_init = cxl_dsp_instance_post_init, .class_init = cxl_dsp_class_init, .interfaces = (InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, From 892e3479d7283a38b50e06f42624f5a1ce61d24a Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Mon, 23 Oct 2023 17:07:59 +0100 Subject: [PATCH 715/974] hw/cxl: Implement Physical Ports status retrieval Add this command for both the Switch CCI in switch upstream ports. Signed-off-by: Jonathan Cameron Message-Id: <20231023160806.13206-11-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-mailbox-utils.c | 128 +++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 6ada49d37c..de63fc1a28 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -72,6 +72,7 @@ enum { #define CLEAR_POISON 0x2 PHYSICAL_SWITCH = 0x51, #define IDENTIFY_SWITCH_DEVICE 0x0 + #define GET_PHYSICAL_PORT_STATE 0x1 }; @@ -320,6 +321,131 @@ static CXLRetCode cmd_identify_switch_device(const struct cxl_cmd *cmd, return CXL_MBOX_SUCCESS; } + +/* CXL r3.0 Section 7.6.7.1.2: Get Physical Port State (Opcode 5101h) */ +static CXLRetCode cmd_get_physical_port_state(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + /* CXL r3.0 Table 7-18: Get Physical Port State Request Payload */ + struct cxl_fmapi_get_phys_port_state_req_pl { + uint8_t num_ports; + uint8_t ports[]; + } QEMU_PACKED *in; + + /* + * CXL r3.0 Table 7-20: Get Physical Port State Port Information Block + * Format + */ + struct cxl_fmapi_port_state_info_block { + uint8_t port_id; + uint8_t config_state; + uint8_t connected_device_cxl_version; + uint8_t rsv1; + uint8_t connected_device_type; + uint8_t port_cxl_version_bitmask; + uint8_t max_link_width; + uint8_t negotiated_link_width; + uint8_t supported_link_speeds_vector; + uint8_t max_link_speed; + uint8_t current_link_speed; + uint8_t ltssm_state; + uint8_t first_lane_num; + uint16_t link_state; + uint8_t supported_ld_count; + } QEMU_PACKED; + + /* CXL r3.0 Table 7-19: Get Physical Port State Response Payload */ + struct cxl_fmapi_get_phys_port_state_resp_pl { + uint8_t num_ports; + uint8_t rsv1[3]; + struct cxl_fmapi_port_state_info_block ports[]; + } QEMU_PACKED *out; + PCIBus *bus = &PCI_BRIDGE(cci->d)->sec_bus; + PCIEPort *usp = PCIE_PORT(cci->d); + size_t pl_size; + int i; + + in = (struct cxl_fmapi_get_phys_port_state_req_pl *)payload_in; + out = (struct cxl_fmapi_get_phys_port_state_resp_pl *)payload_out; + + /* Check if what was requested can fit */ + if (sizeof(*out) + sizeof(*out->ports) * in->num_ports > cci->payload_max) { + return CXL_MBOX_INVALID_INPUT; + } + + /* For success there should be a match for each requested */ + out->num_ports = in->num_ports; + + for (i = 0; i < in->num_ports; i++) { + struct cxl_fmapi_port_state_info_block *port; + /* First try to match on downstream port */ + PCIDevice *port_dev; + uint16_t lnkcap, lnkcap2, lnksta; + + port = &out->ports[i]; + + port_dev = pcie_find_port_by_pn(bus, in->ports[i]); + if (port_dev) { /* DSP */ + PCIDevice *ds_dev = pci_bridge_get_sec_bus(PCI_BRIDGE(port_dev)) + ->devices[0]; + port->config_state = 3; + if (ds_dev) { + if (object_dynamic_cast(OBJECT(ds_dev), TYPE_CXL_TYPE3)) { + port->connected_device_type = 5; /* Assume MLD for now */ + } else { + port->connected_device_type = 1; + } + } else { + port->connected_device_type = 0; + } + port->supported_ld_count = 3; + } else if (usp->port == in->ports[i]) { /* USP */ + port_dev = PCI_DEVICE(usp); + port->config_state = 4; + port->connected_device_type = 0; + } else { + return CXL_MBOX_INVALID_INPUT; + } + + port->port_id = in->ports[i]; + /* Information on status of this port in lnksta, lnkcap */ + if (!port_dev->exp.exp_cap) { + return CXL_MBOX_INTERNAL_ERROR; + } + lnksta = port_dev->config_read(port_dev, + port_dev->exp.exp_cap + PCI_EXP_LNKSTA, + sizeof(lnksta)); + lnkcap = port_dev->config_read(port_dev, + port_dev->exp.exp_cap + PCI_EXP_LNKCAP, + sizeof(lnkcap)); + lnkcap2 = port_dev->config_read(port_dev, + port_dev->exp.exp_cap + PCI_EXP_LNKCAP2, + sizeof(lnkcap2)); + + port->max_link_width = (lnkcap & PCI_EXP_LNKCAP_MLW) >> 4; + port->negotiated_link_width = (lnksta & PCI_EXP_LNKSTA_NLW) >> 4; + /* No definition for SLS field in linux/pci_regs.h */ + port->supported_link_speeds_vector = (lnkcap2 & 0xFE) >> 1; + port->max_link_speed = lnkcap & PCI_EXP_LNKCAP_SLS; + port->current_link_speed = lnksta & PCI_EXP_LNKSTA_CLS; + /* TODO: Track down if we can get the rest of the info */ + port->ltssm_state = 0x7; + port->first_lane_num = 0; + port->link_state = 0; + port->port_cxl_version_bitmask = 0x2; + port->connected_device_cxl_version = 0x2; + } + + pl_size = sizeof(*out) + sizeof(*out->ports) * in->num_ports; + *len_out = pl_size; + + return CXL_MBOX_SUCCESS; +} + /* 8.2.9.2.1 */ static CXLRetCode cmd_firmware_update_get_info(const struct cxl_cmd *cmd, uint8_t *payload_in, @@ -881,6 +1007,8 @@ static const struct cxl_cmd cxl_cmd_set_sw[256][256] = { [LOGS][GET_LOG] = { "LOGS_GET_LOG", cmd_logs_get_log, 0x18, 0 }, [PHYSICAL_SWITCH][IDENTIFY_SWITCH_DEVICE] = { "IDENTIFY_SWITCH_DEVICE", cmd_identify_switch_device, 0, 0 }, + [PHYSICAL_SWITCH][GET_PHYSICAL_PORT_STATE] = { "SWITCH_PHYSICAL_PORT_STATS", + cmd_get_physical_port_state, ~0, 0 }, }; int cxl_process_cci_message(CXLCCI *cci, uint8_t set, uint8_t cmd, From 221d2cfbdb5301f8f0cfbf26baf76544a5d71c27 Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Mon, 23 Oct 2023 17:08:00 +0100 Subject: [PATCH 716/974] hw/cxl/mbox: Add support for background operations Support background commands in the mailbox, and update cmd_infostat_bg_op_sts() accordingly. This patch does not implement mbox interrupts upon completion, so the kernel driver must rely on polling to know when the operation is done. Signed-off-by: Davidlohr Bueso Signed-off-by: Jonathan Cameron Message-Id: <20231023160806.13206-12-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-device-utils.c | 24 ++++++++-- hw/cxl/cxl-mailbox-utils.c | 92 ++++++++++++++++++++++++++++++++++++- include/hw/cxl/cxl_device.h | 10 ++++ 3 files changed, 122 insertions(+), 4 deletions(-) diff --git a/hw/cxl/cxl-device-utils.c b/hw/cxl/cxl-device-utils.c index f8938678c7..51466a626b 100644 --- a/hw/cxl/cxl-device-utils.c +++ b/hw/cxl/cxl-device-utils.c @@ -82,6 +82,25 @@ static uint64_t mailbox_reg_read(void *opaque, hwaddr offset, unsigned size) case 4: return cxl_dstate->mbox_reg_state32[offset / size]; case 8: + if (offset == A_CXL_DEV_BG_CMD_STS) { + uint64_t bg_status_reg; + bg_status_reg = FIELD_DP64(0, CXL_DEV_BG_CMD_STS, OP, + cci->bg.opcode); + bg_status_reg = FIELD_DP64(bg_status_reg, CXL_DEV_BG_CMD_STS, + PERCENTAGE_COMP, cci->bg.complete_pct); + bg_status_reg = FIELD_DP64(bg_status_reg, CXL_DEV_BG_CMD_STS, + RET_CODE, cci->bg.ret_code); + /* endian? */ + cxl_dstate->mbox_reg_state64[offset / size] = bg_status_reg; + } + if (offset == A_CXL_DEV_MAILBOX_STS) { + uint64_t status_reg = cxl_dstate->mbox_reg_state64[offset / size]; + if (cci->bg.complete_pct) { + status_reg = FIELD_DP64(status_reg, CXL_DEV_MAILBOX_STS, BG_OP, + 0); + cxl_dstate->mbox_reg_state64[offset / size] = status_reg; + } + } return cxl_dstate->mbox_reg_state64[offset / size]; default: g_assert_not_reached(); @@ -114,8 +133,7 @@ static void mailbox_mem_writeq(uint64_t *reg_state, hwaddr offset, case A_CXL_DEV_MAILBOX_CMD: break; case A_CXL_DEV_BG_CMD_STS: - /* BG not supported */ - /* fallthrough */ + break; case A_CXL_DEV_MAILBOX_STS: /* Read only register, will get updated by the state machine */ return; @@ -339,7 +357,7 @@ static void device_reg_init_common(CXLDeviceState *cxl_dstate) static void mailbox_reg_init_common(CXLDeviceState *cxl_dstate) { - /* 2048 payload size, with no interrupt or background support */ + /* 2048 payload size, with no interrupt */ ARRAY_FIELD_DP32(cxl_dstate->mbox_reg_state32, CXL_DEV_MAILBOX_CAP, PAYLOAD_SIZE, CXL_MAILBOX_PAYLOAD_SHIFT); cxl_dstate->payload_size = CXL_MAILBOX_MAX_PAYLOAD_SIZE; diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index de63fc1a28..f1226f8f39 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -963,6 +963,8 @@ static CXLRetCode cmd_media_clear_poison(const struct cxl_cmd *cmd, #define IMMEDIATE_DATA_CHANGE (1 << 2) #define IMMEDIATE_POLICY_CHANGE (1 << 3) #define IMMEDIATE_LOG_CHANGE (1 << 4) +#define SECURITY_STATE_CHANGE (1 << 5) +#define BACKGROUND_OPERATION (1 << 6) static const struct cxl_cmd cxl_cmd_set[256][256] = { [EVENTS][GET_RECORDS] = { "EVENTS_GET_RECORDS", @@ -1011,10 +1013,19 @@ static const struct cxl_cmd cxl_cmd_set_sw[256][256] = { cmd_get_physical_port_state, ~0, 0 }, }; +/* + * While the command is executing in the background, the device should + * update the percentage complete in the Background Command Status Register + * at least once per second. + */ + +#define CXL_MBOX_BG_UPDATE_FREQ 1000UL + int cxl_process_cci_message(CXLCCI *cci, uint8_t set, uint8_t cmd, size_t len_in, uint8_t *pl_in, size_t *len_out, uint8_t *pl_out, bool *bg_started) { + int ret; const struct cxl_cmd *cxl_cmd; opcode_handler h; @@ -1031,7 +1042,81 @@ int cxl_process_cci_message(CXLCCI *cci, uint8_t set, uint8_t cmd, return CXL_MBOX_INVALID_PAYLOAD_LENGTH; } - return (*h)(cxl_cmd, pl_in, len_in, pl_out, len_out, cci); + /* Only one bg command at a time */ + if ((cxl_cmd->effect & BACKGROUND_OPERATION) && + cci->bg.runtime > 0) { + return CXL_MBOX_BUSY; + } + + ret = (*h)(cxl_cmd, pl_in, len_in, pl_out, len_out, cci); + if ((cxl_cmd->effect & BACKGROUND_OPERATION) && + ret == CXL_MBOX_BG_STARTED) { + *bg_started = true; + } else { + *bg_started = false; + } + + /* Set bg and the return code */ + if (*bg_started) { + uint64_t now; + + cci->bg.opcode = (set << 8) | cmd; + + cci->bg.complete_pct = 0; + cci->bg.ret_code = 0; + + now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL); + cci->bg.starttime = now; + timer_mod(cci->bg.timer, now + CXL_MBOX_BG_UPDATE_FREQ); + } + + return ret; +} + +static void bg_timercb(void *opaque) +{ + CXLCCI *cci = opaque; + CXLDeviceState *cxl_dstate = &CXL_TYPE3(cci->d)->cxl_dstate; + uint64_t bg_status_reg = 0; + uint64_t now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL); + uint64_t total_time = cci->bg.starttime + cci->bg.runtime; + + assert(cci->bg.runtime > 0); + bg_status_reg = FIELD_DP64(bg_status_reg, CXL_DEV_BG_CMD_STS, + OP, cci->bg.opcode); + + if (now >= total_time) { /* we are done */ + uint64_t status_reg; + uint16_t ret = CXL_MBOX_SUCCESS; + + cci->bg.complete_pct = 100; + /* Clear bg */ + status_reg = FIELD_DP64(0, CXL_DEV_MAILBOX_STS, BG_OP, 0); + cxl_dstate->mbox_reg_state64[R_CXL_DEV_MAILBOX_STS] = status_reg; + + bg_status_reg = FIELD_DP64(bg_status_reg, CXL_DEV_BG_CMD_STS, + RET_CODE, ret); + + /* TODO add ad-hoc cmd succesful completion handling */ + + qemu_log("Background command %04xh finished: %s\n", + cci->bg.opcode, + ret == CXL_MBOX_SUCCESS ? "success" : "aborted"); + } else { + /* estimate only */ + cci->bg.complete_pct = 100 * now / total_time; + timer_mod(cci->bg.timer, now + CXL_MBOX_BG_UPDATE_FREQ); + } + + bg_status_reg = FIELD_DP64(bg_status_reg, CXL_DEV_BG_CMD_STS, + PERCENTAGE_COMP, cci->bg.complete_pct); + cxl_dstate->mbox_reg_state64[R_CXL_DEV_BG_CMD_STS] = bg_status_reg; + + if (cci->bg.complete_pct == 100) { + cci->bg.starttime = 0; + /* registers are updated, allow new bg-capable cmds */ + cci->bg.runtime = 0; + } } void cxl_init_cci(CXLCCI *cci, size_t payload_max) @@ -1050,6 +1135,11 @@ void cxl_init_cci(CXLCCI *cci, size_t payload_max) } } } + cci->bg.complete_pct = 0; + cci->bg.starttime = 0; + cci->bg.runtime = 0; + cci->bg.timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, + bg_timercb, cci); } void cxl_initialize_mailbox_swcci(CXLCCI *cci, DeviceState *intf, diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index fa73ed03e5..124ff969ec 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -149,6 +149,16 @@ typedef struct CXLCCI { } cel_log[1 << 16]; size_t cel_size; + /* background command handling (times in ms) */ + struct { + uint16_t opcode; + uint16_t complete_pct; + uint16_t ret_code; /* Current value of retcode */ + uint64_t starttime; + /* set by each bg cmd, cleared by the bg_timer when complete */ + uint64_t runtime; + QEMUTimer *timer; + } bg; size_t payload_max; /* Pointer to device hosting the CCI */ DeviceState *d; From 43efb0bfad2b81b32fc19da442cbea2835cf38d4 Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Mon, 23 Oct 2023 17:08:01 +0100 Subject: [PATCH 717/974] hw/cxl/mbox: Wire up interrupts for background completion Notify when the background operation is done. Note that for now background commands are only supported on the main Type 3 mailbox. Signed-off-by: Davidlohr Bueso Signed-off-by: Jonathan Cameron Message-Id: <20231023160806.13206-13-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-device-utils.c | 10 +++++++++- hw/cxl/cxl-mailbox-utils.c | 31 ++++++++++++++----------------- include/hw/cxl/cxl_device.h | 1 + 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/hw/cxl/cxl-device-utils.c b/hw/cxl/cxl-device-utils.c index 51466a626b..61a3c4dc2e 100644 --- a/hw/cxl/cxl-device-utils.c +++ b/hw/cxl/cxl-device-utils.c @@ -357,10 +357,18 @@ static void device_reg_init_common(CXLDeviceState *cxl_dstate) static void mailbox_reg_init_common(CXLDeviceState *cxl_dstate) { - /* 2048 payload size, with no interrupt */ + const uint8_t msi_n = 9; + + /* 2048 payload size */ ARRAY_FIELD_DP32(cxl_dstate->mbox_reg_state32, CXL_DEV_MAILBOX_CAP, PAYLOAD_SIZE, CXL_MAILBOX_PAYLOAD_SHIFT); cxl_dstate->payload_size = CXL_MAILBOX_MAX_PAYLOAD_SIZE; + /* irq support */ + ARRAY_FIELD_DP32(cxl_dstate->mbox_reg_state32, CXL_DEV_MAILBOX_CAP, + BG_INT_CAP, 1); + ARRAY_FIELD_DP32(cxl_dstate->mbox_reg_state32, CXL_DEV_MAILBOX_CAP, + MSI_N, msi_n); + cxl_dstate->mbox_msi_n = msi_n; } static void memdev_reg_init_common(CXLDeviceState *cxl_dstate) { } diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index f1226f8f39..f3fd97deb5 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -8,6 +8,8 @@ */ #include "qemu/osdep.h" +#include "hw/pci/msi.h" +#include "hw/pci/msix.h" #include "hw/cxl/cxl.h" #include "hw/cxl/cxl_events.h" #include "hw/pci/pci.h" @@ -1076,28 +1078,16 @@ int cxl_process_cci_message(CXLCCI *cci, uint8_t set, uint8_t cmd, static void bg_timercb(void *opaque) { CXLCCI *cci = opaque; - CXLDeviceState *cxl_dstate = &CXL_TYPE3(cci->d)->cxl_dstate; - uint64_t bg_status_reg = 0; uint64_t now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL); uint64_t total_time = cci->bg.starttime + cci->bg.runtime; assert(cci->bg.runtime > 0); - bg_status_reg = FIELD_DP64(bg_status_reg, CXL_DEV_BG_CMD_STS, - OP, cci->bg.opcode); if (now >= total_time) { /* we are done */ - uint64_t status_reg; uint16_t ret = CXL_MBOX_SUCCESS; cci->bg.complete_pct = 100; - /* Clear bg */ - status_reg = FIELD_DP64(0, CXL_DEV_MAILBOX_STS, BG_OP, 0); - cxl_dstate->mbox_reg_state64[R_CXL_DEV_MAILBOX_STS] = status_reg; - - bg_status_reg = FIELD_DP64(bg_status_reg, CXL_DEV_BG_CMD_STS, - RET_CODE, ret); - - /* TODO add ad-hoc cmd succesful completion handling */ + cci->bg.ret_code = ret; qemu_log("Background command %04xh finished: %s\n", cci->bg.opcode, @@ -1108,14 +1098,21 @@ static void bg_timercb(void *opaque) timer_mod(cci->bg.timer, now + CXL_MBOX_BG_UPDATE_FREQ); } - bg_status_reg = FIELD_DP64(bg_status_reg, CXL_DEV_BG_CMD_STS, - PERCENTAGE_COMP, cci->bg.complete_pct); - cxl_dstate->mbox_reg_state64[R_CXL_DEV_BG_CMD_STS] = bg_status_reg; - if (cci->bg.complete_pct == 100) { + /* TODO: generalize to switch CCI */ + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + CXLDeviceState *cxl_dstate = &ct3d->cxl_dstate; + PCIDevice *pdev = PCI_DEVICE(cci->d); + cci->bg.starttime = 0; /* registers are updated, allow new bg-capable cmds */ cci->bg.runtime = 0; + + if (msix_enabled(pdev)) { + msix_notify(pdev, cxl_dstate->mbox_msi_n); + } else if (msi_enabled(pdev)) { + msi_notify(pdev, cxl_dstate->mbox_msi_n); + } } } diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index 124ff969ec..2a813cdddd 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -193,6 +193,7 @@ typedef struct cxl_device_state { struct { MemoryRegion mailbox; uint16_t payload_size; + uint8_t mbox_msi_n; union { uint8_t mbox_reg_state[CXL_MAILBOX_REGISTERS_LENGTH]; uint16_t mbox_reg_state16[CXL_MAILBOX_REGISTERS_LENGTH / 2]; From 25a52959f99d6860a186175bda898e3bdb605f91 Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Mon, 23 Oct 2023 17:08:02 +0100 Subject: [PATCH 718/974] hw/cxl: Add support for device sanitation Make use of the background operations through the sanitize command, per CXL 3.0 specs. Traditionally run times can be rather long, depending on the size of the media. Estimate times based on: https://pmem.io/documents/NVDIMM_DSM_Interface-V1.8.pdf Signed-off-by: Davidlohr Bueso Signed-off-by: Jonathan Cameron Message-Id: <20231023160806.13206-14-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-mailbox-utils.c | 140 ++++++++++++++++++++++++++++++++++++ hw/mem/cxl_type3.c | 10 +++ include/hw/cxl/cxl_device.h | 17 +++++ 3 files changed, 167 insertions(+) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index f3fd97deb5..2463f239af 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -18,6 +18,7 @@ #include "qemu/log.h" #include "qemu/units.h" #include "qemu/uuid.h" +#include "sysemu/hostmem.h" #define CXL_CAPACITY_MULTIPLIER (256 * MiB) @@ -68,6 +69,9 @@ enum { #define GET_PARTITION_INFO 0x0 #define GET_LSA 0x2 #define SET_LSA 0x3 + SANITIZE = 0x44, + #define OVERWRITE 0x0 + #define SECURE_ERASE 0x1 MEDIA_AND_POISON = 0x43, #define GET_POISON_LIST 0x0 #define INJECT_POISON 0x1 @@ -749,6 +753,108 @@ static CXLRetCode cmd_ccls_set_lsa(const struct cxl_cmd *cmd, return CXL_MBOX_SUCCESS; } +/* Perform the actual device zeroing */ +static void __do_sanitization(CXLType3Dev *ct3d) +{ + MemoryRegion *mr; + + if (ct3d->hostvmem) { + mr = host_memory_backend_get_memory(ct3d->hostvmem); + if (mr) { + void *hostmem = memory_region_get_ram_ptr(mr); + memset(hostmem, 0, memory_region_size(mr)); + } + } + + if (ct3d->hostpmem) { + mr = host_memory_backend_get_memory(ct3d->hostpmem); + if (mr) { + void *hostmem = memory_region_get_ram_ptr(mr); + memset(hostmem, 0, memory_region_size(mr)); + } + } + if (ct3d->lsa) { + mr = host_memory_backend_get_memory(ct3d->lsa); + if (mr) { + void *lsa = memory_region_get_ram_ptr(mr); + memset(lsa, 0, memory_region_size(mr)); + } + } +} + +/* + * CXL 3.0 spec section 8.2.9.8.5.1 - Sanitize. + * + * Once the Sanitize command has started successfully, the device shall be + * placed in the media disabled state. If the command fails or is interrupted + * by a reset or power failure, it shall remain in the media disabled state + * until a successful Sanitize command has been completed. During this state: + * + * 1. Memory writes to the device will have no effect, and all memory reads + * will return random values (no user data returned, even for locations that + * the failed Sanitize operation didn’t sanitize yet). + * + * 2. Mailbox commands shall still be processed in the disabled state, except + * that commands that access Sanitized areas shall fail with the Media Disabled + * error code. + */ +static CXLRetCode cmd_sanitize_overwrite(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + uint64_t total_mem; /* in Mb */ + int secs; + + total_mem = (ct3d->cxl_dstate.vmem_size + ct3d->cxl_dstate.pmem_size) >> 20; + if (total_mem <= 512) { + secs = 4; + } else if (total_mem <= 1024) { + secs = 8; + } else if (total_mem <= 2 * 1024) { + secs = 15; + } else if (total_mem <= 4 * 1024) { + secs = 30; + } else if (total_mem <= 8 * 1024) { + secs = 60; + } else if (total_mem <= 16 * 1024) { + secs = 2 * 60; + } else if (total_mem <= 32 * 1024) { + secs = 4 * 60; + } else if (total_mem <= 64 * 1024) { + secs = 8 * 60; + } else if (total_mem <= 128 * 1024) { + secs = 15 * 60; + } else if (total_mem <= 256 * 1024) { + secs = 30 * 60; + } else if (total_mem <= 512 * 1024) { + secs = 60 * 60; + } else if (total_mem <= 1024 * 1024) { + secs = 120 * 60; + } else { + secs = 240 * 60; /* max 4 hrs */ + } + + /* EBUSY other bg cmds as of now */ + cci->bg.runtime = secs * 1000UL; + *len_out = 0; + + cxl_dev_disable_media(&ct3d->cxl_dstate); + + if (secs > 2) { + /* sanitize when done */ + return CXL_MBOX_BG_STARTED; + } else { + __do_sanitization(ct3d); + cxl_dev_enable_media(&ct3d->cxl_dstate); + + return CXL_MBOX_SUCCESS; + } +} + /* * This is very inefficient, but good enough for now! * Also the payload will always fit, so no need to handle the MORE flag and @@ -993,6 +1099,8 @@ static const struct cxl_cmd cxl_cmd_set[256][256] = { [CCLS][GET_LSA] = { "CCLS_GET_LSA", cmd_ccls_get_lsa, 8, 0 }, [CCLS][SET_LSA] = { "CCLS_SET_LSA", cmd_ccls_set_lsa, ~0, IMMEDIATE_CONFIG_CHANGE | IMMEDIATE_DATA_CHANGE }, + [SANITIZE][OVERWRITE] = { "SANITIZE_OVERWRITE", cmd_sanitize_overwrite, 0, + IMMEDIATE_DATA_CHANGE | SECURITY_STATE_CHANGE | BACKGROUND_OPERATION }, [MEDIA_AND_POISON][GET_POISON_LIST] = { "MEDIA_AND_POISON_GET_POISON_LIST", cmd_media_get_poison_list, 16, 0 }, [MEDIA_AND_POISON][INJECT_POISON] = { "MEDIA_AND_POISON_INJECT_POISON", @@ -1050,6 +1158,21 @@ int cxl_process_cci_message(CXLCCI *cci, uint8_t set, uint8_t cmd, return CXL_MBOX_BUSY; } + /* forbid any selected commands while overwriting */ + if (sanitize_running(cci)) { + if (h == cmd_events_get_records || + h == cmd_ccls_get_partition_info || + h == cmd_ccls_set_lsa || + h == cmd_ccls_get_lsa || + h == cmd_logs_get_log || + h == cmd_media_get_poison_list || + h == cmd_media_inject_poison || + h == cmd_media_clear_poison || + h == cmd_sanitize_overwrite) { + return CXL_MBOX_MEDIA_DISABLED; + } + } + ret = (*h)(cxl_cmd, pl_in, len_in, pl_out, len_out, cci); if ((cxl_cmd->effect & BACKGROUND_OPERATION) && ret == CXL_MBOX_BG_STARTED) { @@ -1088,6 +1211,23 @@ static void bg_timercb(void *opaque) cci->bg.complete_pct = 100; cci->bg.ret_code = ret; + if (ret == CXL_MBOX_SUCCESS) { + switch (cci->bg.opcode) { + case 0x4400: /* sanitize */ + { + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + + __do_sanitization(ct3d); + cxl_dev_enable_media(&ct3d->cxl_dstate); + } + break; + case 0x4304: /* TODO: scan media */ + break; + default: + __builtin_unreachable(); + break; + } + } qemu_log("Background command %04xh finished: %s\n", cci->bg.opcode, diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index 0529745786..cc8220592f 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -23,6 +23,7 @@ #include "qemu/pmem.h" #include "qemu/range.h" #include "qemu/rcu.h" +#include "qemu/guest-random.h" #include "sysemu/hostmem.h" #include "sysemu/numa.h" #include "hw/cxl/cxl.h" @@ -897,6 +898,11 @@ MemTxResult cxl_type3_read(PCIDevice *d, hwaddr host_addr, uint64_t *data, return MEMTX_ERROR; } + if (sanitize_running(&CXL_TYPE3(d)->cci)) { + qemu_guest_getrandom_nofail(data, size); + return MEMTX_OK; + } + return address_space_read(as, dpa_offset, attrs, data, size); } @@ -913,6 +919,10 @@ MemTxResult cxl_type3_write(PCIDevice *d, hwaddr host_addr, uint64_t data, return MEMTX_ERROR; } + if (sanitize_running(&CXL_TYPE3(d)->cci)) { + return MEMTX_OK; + } + return address_space_write(as, dpa_offset, attrs, &data, size); } diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index 2a813cdddd..70aca9024c 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -343,6 +343,23 @@ REG64(CXL_MEM_DEV_STS, 0) FIELD(CXL_MEM_DEV_STS, MBOX_READY, 4, 1) FIELD(CXL_MEM_DEV_STS, RESET_NEEDED, 5, 3) +static inline void __toggle_media(CXLDeviceState *cxl_dstate, int val) +{ + uint64_t dev_status_reg; + + dev_status_reg = FIELD_DP64(0, CXL_MEM_DEV_STS, MEDIA_STATUS, val); + cxl_dstate->mbox_reg_state64[R_CXL_MEM_DEV_STS] = dev_status_reg; +} +#define cxl_dev_disable_media(cxlds) \ + do { __toggle_media((cxlds), 0x3); } while (0) +#define cxl_dev_enable_media(cxlds) \ + do { __toggle_media((cxlds), 0x1); } while (0) + +static inline bool sanitize_running(CXLCCI *cci) +{ + return !!cci->bg.runtime && cci->bg.opcode == 0x4400; +} + typedef struct CXLError { QTAILQ_ENTRY(CXLError) node; int type; /* Error code as per FE definition */ From 9dd15ab6e6b35db6ff166e5016b0c2042c5439c6 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Mon, 23 Oct 2023 17:08:03 +0100 Subject: [PATCH 719/974] hw/cxl/mbox: Add Get Background Operation Status Command For now, provide this command on type 3 main mailbox only. Signed-off-by: Jonathan Cameron Message-Id: <20231023160806.13206-15-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-mailbox-utils.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 2463f239af..2b78136588 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -50,6 +50,7 @@ enum { INFOSTAT = 0x00, #define IS_IDENTIFY 0x1 + #define BACKGROUND_OPERATION_STATUS 0x2 EVENTS = 0x01, #define GET_RECORDS 0x0 #define CLEAR_RECORDS 0x1 @@ -452,6 +453,36 @@ static CXLRetCode cmd_get_physical_port_state(const struct cxl_cmd *cmd, return CXL_MBOX_SUCCESS; } +/* CXL r3.0 8.2.9.1.2 */ +static CXLRetCode cmd_infostat_bg_op_sts(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + struct { + uint8_t status; + uint8_t rsvd; + uint16_t opcode; + uint16_t returncode; + uint16_t vendor_ext_status; + } QEMU_PACKED *bg_op_status; + QEMU_BUILD_BUG_ON(sizeof(*bg_op_status) != 8); + + bg_op_status = (void *)payload_out; + memset(bg_op_status, 0, sizeof(*bg_op_status)); + bg_op_status->status = cci->bg.complete_pct << 1; + if (cci->bg.runtime > 0) { + bg_op_status->status |= 1U << 0; + } + bg_op_status->opcode = cci->bg.opcode; + bg_op_status->returncode = cci->bg.ret_code; + *len_out = sizeof(*bg_op_status); + + return CXL_MBOX_SUCCESS; +} + /* 8.2.9.2.1 */ static CXLRetCode cmd_firmware_update_get_info(const struct cxl_cmd *cmd, uint8_t *payload_in, @@ -1111,6 +1142,8 @@ static const struct cxl_cmd cxl_cmd_set[256][256] = { static const struct cxl_cmd cxl_cmd_set_sw[256][256] = { [INFOSTAT][IS_IDENTIFY] = { "IDENTIFY", cmd_infostat_identify, 0, 0 }, + [INFOSTAT][BACKGROUND_OPERATION_STATUS] = { "BACKGROUND_OPERATION_STATUS", + cmd_infostat_bg_op_sts, 0, 0 }, [TIMESTAMP][GET] = { "TIMESTAMP_GET", cmd_timestamp_get, 0, 0 }, [TIMESTAMP][SET] = { "TIMESTAMP_SET", cmd_timestamp_set, 0, IMMEDIATE_POLICY_CHANGE }, From ede604d505d711e45abcc37dd4389967fda410d7 Mon Sep 17 00:00:00 2001 From: Gregory Price Date: Mon, 23 Oct 2023 17:08:04 +0100 Subject: [PATCH 720/974] hw/cxl/type3: Cleanup multiple CXL_TYPE3() calls in read/write functions Call CXL_TYPE3 once at top of function to avoid multiple invocations. Signed-off-by: Gregory Price Signed-off-by: Jonathan Cameron Message-Id: <20231023160806.13206-16-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/mem/cxl_type3.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index cc8220592f..a766c64575 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -888,17 +888,18 @@ static int cxl_type3_hpa_to_as_and_dpa(CXLType3Dev *ct3d, MemTxResult cxl_type3_read(PCIDevice *d, hwaddr host_addr, uint64_t *data, unsigned size, MemTxAttrs attrs) { + CXLType3Dev *ct3d = CXL_TYPE3(d); uint64_t dpa_offset = 0; AddressSpace *as = NULL; int res; - res = cxl_type3_hpa_to_as_and_dpa(CXL_TYPE3(d), host_addr, size, + res = cxl_type3_hpa_to_as_and_dpa(ct3d, host_addr, size, &as, &dpa_offset); if (res) { return MEMTX_ERROR; } - if (sanitize_running(&CXL_TYPE3(d)->cci)) { + if (sanitize_running(&ct3d->cci)) { qemu_guest_getrandom_nofail(data, size); return MEMTX_OK; } @@ -909,17 +910,18 @@ MemTxResult cxl_type3_read(PCIDevice *d, hwaddr host_addr, uint64_t *data, MemTxResult cxl_type3_write(PCIDevice *d, hwaddr host_addr, uint64_t data, unsigned size, MemTxAttrs attrs) { + CXLType3Dev *ct3d = CXL_TYPE3(d); uint64_t dpa_offset = 0; AddressSpace *as = NULL; int res; - res = cxl_type3_hpa_to_as_and_dpa(CXL_TYPE3(d), host_addr, size, + res = cxl_type3_hpa_to_as_and_dpa(ct3d, host_addr, size, &as, &dpa_offset); if (res) { return MEMTX_ERROR; } - if (sanitize_running(&CXL_TYPE3(d)->cci)) { + if (sanitize_running(&ct3d->cci)) { return MEMTX_OK; } From 44e4b316e4bf8f7327f3917c25aae38172695680 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Mon, 23 Oct 2023 17:08:05 +0100 Subject: [PATCH 721/974] hw/cxl: Add dummy security state get Needed to allow the santize comamnds to be tested with proposed Linux Kernel support. Default value + no control of the security state will work for now. Signed-off-by: Jonathan Cameron Message-Id: <20231023160806.13206-17-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-mailbox-utils.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 2b78136588..693c2cbdcd 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -73,6 +73,8 @@ enum { SANITIZE = 0x44, #define OVERWRITE 0x0 #define SECURE_ERASE 0x1 + PERSISTENT_MEM = 0x45, + #define GET_SECURITY_STATE 0x0 MEDIA_AND_POISON = 0x43, #define GET_POISON_LIST 0x0 #define INJECT_POISON 0x1 @@ -886,6 +888,19 @@ static CXLRetCode cmd_sanitize_overwrite(const struct cxl_cmd *cmd, } } +static CXLRetCode cmd_get_security_state(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + uint32_t *state = (uint32_t *)payload_out; + + *state = 0; + *len_out = 4; + return CXL_MBOX_SUCCESS; +} /* * This is very inefficient, but good enough for now! * Also the payload will always fit, so no need to handle the MORE flag and @@ -1132,6 +1147,8 @@ static const struct cxl_cmd cxl_cmd_set[256][256] = { ~0, IMMEDIATE_CONFIG_CHANGE | IMMEDIATE_DATA_CHANGE }, [SANITIZE][OVERWRITE] = { "SANITIZE_OVERWRITE", cmd_sanitize_overwrite, 0, IMMEDIATE_DATA_CHANGE | SECURITY_STATE_CHANGE | BACKGROUND_OPERATION }, + [PERSISTENT_MEM][GET_SECURITY_STATE] = { "GET_SECURITY_STATE", + cmd_get_security_state, 0, 0 }, [MEDIA_AND_POISON][GET_POISON_LIST] = { "MEDIA_AND_POISON_GET_POISON_LIST", cmd_media_get_poison_list, 16, 0 }, [MEDIA_AND_POISON][INJECT_POISON] = { "MEDIA_AND_POISON_INJECT_POISON", From 004e3a93b814ca2d13ee2feb1f9ebacef6c83b9e Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Mon, 23 Oct 2023 17:08:06 +0100 Subject: [PATCH 722/974] hw/cxl: Add tunneled command support to mailbox for switch cci. This implementation of tunneling makes the choice that our Type 3 device is a Logical Device (LD) of a Multi-Logical Device (MLD) that just happens to only have one LD for now. Tunneling is supported from a Switch Mailbox CCI (and shortly via MCTP over I2C connected to the switch MCTP CCI) via an outer level to the FM owned LD in the MLD Type 3 device. From there an inner tunnel may be used to access particular LDs. Protocol wise, the following is what happens in a real system but we don't emulate the transports - just the destinations and the payloads. ( Host -> Switch Mailbox CCI - in band FM-API mailbox command or Host -> Switch MCTP CCI - MCTP over I2C using the CXL FM-API MCTP Binding. ) then (if a tunnel command) Switch -> Type 3 FM Owned LD - MCTP over PCI VDM using the CXL FM-API binding (addressed by switch port) then (if unwrapped command also a tunnel command) Type 3 FM Owned LD to LD0 via internal transport (addressed by LD number) or (added shortly) Host to Type 3 FM Owned MCTP CCI - MCTP over I2C using the CXL FM-API MCTP Binding. then (if unwrapped comand is a tunnel comamnd) Type 3 FM Owned LD to LD0 via internal transport. (addressed by LD number) It is worth noting that the tunneling commands over PCI VDM presumably use the appropriate MCTP binding depending on opcode. This may be the CXL FMAPI binding or the CXL Memory Device Binding. Additional commands will need to be added to make this useful beyond testing the tunneling works. Signed-off-by: Jonathan Cameron Message-Id: <20231023160806.13206-18-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-mailbox-utils.c | 162 ++++++++++++++++++++++++++++++++++++ hw/mem/cxl_type3.c | 11 +++ include/hw/cxl/cxl_device.h | 9 ++ 3 files changed, 182 insertions(+) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 693c2cbdcd..b365575097 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -82,8 +82,132 @@ enum { PHYSICAL_SWITCH = 0x51, #define IDENTIFY_SWITCH_DEVICE 0x0 #define GET_PHYSICAL_PORT_STATE 0x1 + TUNNEL = 0x53, + #define MANAGEMENT_COMMAND 0x0 }; +/* CCI Message Format CXL r3.0 Figure 7-19 */ +typedef struct CXLCCIMessage { + uint8_t category; +#define CXL_CCI_CAT_REQ 0 +#define CXL_CCI_CAT_RSP 1 + uint8_t tag; + uint8_t resv1; + uint8_t command; + uint8_t command_set; + uint8_t pl_length[3]; + uint16_t rc; + uint16_t vendor_specific; + uint8_t payload[]; +} QEMU_PACKED CXLCCIMessage; + +/* This command is only defined to an MLD FM Owned LD or an MHD */ +static CXLRetCode cmd_tunnel_management_cmd(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + PCIDevice *tunnel_target; + CXLCCI *target_cci; + struct { + uint8_t port_or_ld_id; + uint8_t target_type; + uint16_t size; + CXLCCIMessage ccimessage; + } QEMU_PACKED *in; + struct { + uint16_t resp_len; + uint8_t resv[2]; + CXLCCIMessage ccimessage; + } QEMU_PACKED *out; + size_t pl_length, length_out; + bool bg_started; + int rc; + + if (cmd->in < sizeof(*in)) { + return CXL_MBOX_INVALID_INPUT; + } + in = (void *)payload_in; + out = (void *)payload_out; + + /* Enough room for minimum sized message - no payload */ + if (in->size < sizeof(in->ccimessage)) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } + /* Length of input payload should be in->size + a wrapping tunnel header */ + if (in->size != len_in - offsetof(typeof(*out), ccimessage)) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } + if (in->ccimessage.category != CXL_CCI_CAT_REQ) { + return CXL_MBOX_INVALID_INPUT; + } + + if (in->target_type != 0) { + qemu_log_mask(LOG_UNIMP, + "Tunneled Command sent to non existent FM-LD"); + return CXL_MBOX_INVALID_INPUT; + } + + /* + * Target of a tunnel unfortunately depends on type of CCI readint + * the message. + * If in a switch, then it's the port number. + * If in an MLD it is the ld number. + * If in an MHD target type indicate where we are going. + */ + if (object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_TYPE3)) { + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + if (in->port_or_ld_id != 0) { + /* Only pretending to have one for now! */ + return CXL_MBOX_INVALID_INPUT; + } + target_cci = &ct3d->ld0_cci; + } else if (object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_USP)) { + CXLUpstreamPort *usp = CXL_USP(cci->d); + + tunnel_target = pcie_find_port_by_pn(&PCI_BRIDGE(usp)->sec_bus, + in->port_or_ld_id); + if (!tunnel_target) { + return CXL_MBOX_INVALID_INPUT; + } + tunnel_target = + pci_bridge_get_sec_bus(PCI_BRIDGE(tunnel_target))->devices[0]; + if (!tunnel_target) { + return CXL_MBOX_INVALID_INPUT; + } + if (object_dynamic_cast(OBJECT(tunnel_target), TYPE_CXL_TYPE3)) { + CXLType3Dev *ct3d = CXL_TYPE3(tunnel_target); + /* Tunneled VDMs always land on FM Owned LD */ + target_cci = &ct3d->vdm_fm_owned_ld_mctp_cci; + } else { + return CXL_MBOX_INVALID_INPUT; + } + } else { + return CXL_MBOX_INVALID_INPUT; + } + + pl_length = in->ccimessage.pl_length[2] << 16 | + in->ccimessage.pl_length[1] << 8 | in->ccimessage.pl_length[0]; + rc = cxl_process_cci_message(target_cci, + in->ccimessage.command_set, + in->ccimessage.command, + pl_length, in->ccimessage.payload, + &length_out, out->ccimessage.payload, + &bg_started); + /* Payload should be in place. Rest of CCI header and needs filling */ + out->resp_len = length_out + sizeof(CXLCCIMessage); + st24_le_p(out->ccimessage.pl_length, length_out); + out->ccimessage.rc = rc; + out->ccimessage.category = CXL_CCI_CAT_RSP; + out->ccimessage.command = in->ccimessage.command; + out->ccimessage.command_set = in->ccimessage.command_set; + out->ccimessage.tag = in->ccimessage.tag; + *len_out = length_out + sizeof(*out); + + return CXL_MBOX_SUCCESS; +} static CXLRetCode cmd_events_get_records(const struct cxl_cmd *cmd, uint8_t *payload_in, size_t len_in, @@ -1171,6 +1295,8 @@ static const struct cxl_cmd cxl_cmd_set_sw[256][256] = { cmd_identify_switch_device, 0, 0 }, [PHYSICAL_SWITCH][GET_PHYSICAL_PORT_STATE] = { "SWITCH_PHYSICAL_PORT_STATS", cmd_get_physical_port_state, ~0, 0 }, + [TUNNEL][MANAGEMENT_COMMAND] = { "TUNNEL_MANAGEMENT_COMMAND", + cmd_tunnel_management_cmd, ~0, 0 }, }; /* @@ -1347,3 +1473,39 @@ void cxl_initialize_mailbox_t3(CXLCCI *cci, DeviceState *d, size_t payload_max) cci->intf = d; cxl_init_cci(cci, payload_max); } + +static const struct cxl_cmd cxl_cmd_set_t3_ld[256][256] = { + [INFOSTAT][IS_IDENTIFY] = { "IDENTIFY", cmd_infostat_identify, 0, 0 }, + [LOGS][GET_SUPPORTED] = { "LOGS_GET_SUPPORTED", cmd_logs_get_supported, 0, + 0 }, + [LOGS][GET_LOG] = { "LOGS_GET_LOG", cmd_logs_get_log, 0x18, 0 }, +}; + +void cxl_initialize_t3_ld_cci(CXLCCI *cci, DeviceState *d, DeviceState *intf, + size_t payload_max) +{ + cci->cxl_cmd_set = cxl_cmd_set_t3_ld; + cci->d = d; + cci->intf = intf; + cxl_init_cci(cci, payload_max); +} + +static const struct cxl_cmd cxl_cmd_set_t3_fm_owned_ld_mctp[256][256] = { + [INFOSTAT][IS_IDENTIFY] = { "IDENTIFY", cmd_infostat_identify, 0, 0}, + [LOGS][GET_SUPPORTED] = { "LOGS_GET_SUPPORTED", cmd_logs_get_supported, 0, + 0 }, + [LOGS][GET_LOG] = { "LOGS_GET_LOG", cmd_logs_get_log, 0x18, 0 }, + [TIMESTAMP][GET] = { "TIMESTAMP_GET", cmd_timestamp_get, 0, 0 }, + [TUNNEL][MANAGEMENT_COMMAND] = { "TUNNEL_MANAGEMENT_COMMAND", + cmd_tunnel_management_cmd, ~0, 0 }, +}; + +void cxl_initialize_t3_fm_owned_ld_mctpcci(CXLCCI *cci, DeviceState *d, + DeviceState *intf, + size_t payload_max) +{ + cci->cxl_cmd_set = cxl_cmd_set_t3_fm_owned_ld_mctp; + cci->d = d; + cci->intf = intf; + cxl_init_cci(cci, payload_max); +} diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index a766c64575..52647b4ac7 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -936,6 +936,17 @@ static void ct3d_reset(DeviceState *dev) cxl_component_register_init_common(reg_state, write_msk, CXL2_TYPE3_DEVICE); cxl_device_register_init_t3(ct3d); + + /* + * Bring up an endpoint to target with MCTP over VDM. + * This device is emulating an MLD with single LD for now. + */ + cxl_initialize_t3_fm_owned_ld_mctpcci(&ct3d->vdm_fm_owned_ld_mctp_cci, + DEVICE(ct3d), DEVICE(ct3d), + 512); /* Max payload made up */ + cxl_initialize_t3_ld_cci(&ct3d->ld0_cci, DEVICE(ct3d), DEVICE(ct3d), + 512); /* Max payload made up */ + } static Property ct3_props[] = { diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index 70aca9024c..61b7f897f7 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -279,6 +279,12 @@ int cxl_process_cci_message(CXLCCI *cci, uint8_t set, uint8_t cmd, size_t len_in, uint8_t *pl_in, size_t *len_out, uint8_t *pl_out, bool *bg_started); +void cxl_initialize_t3_fm_owned_ld_mctpcci(CXLCCI *cci, DeviceState *d, + DeviceState *intf, + size_t payload_max); + +void cxl_initialize_t3_ld_cci(CXLCCI *cci, DeviceState *d, + DeviceState *intf, size_t payload_max); #define cxl_device_cap_init(dstate, reg, cap_id, ver) \ do { \ @@ -397,6 +403,9 @@ struct CXLType3Dev { CXLComponentState cxl_cstate; CXLDeviceState cxl_dstate; CXLCCI cci; /* Primary PCI mailbox CCI */ + /* Always intialized as no way to know if a VDM might show up */ + CXLCCI vdm_fm_owned_ld_mctp_cci; + CXLCCI ld0_cci; /* DOE */ DOECap doe_cdat; From a874ddc95a2b8b4b6303366c6ac6a2be53c41be4 Mon Sep 17 00:00:00 2001 From: Ani Sinha Date: Fri, 27 Oct 2023 08:51:18 +0530 Subject: [PATCH 723/974] acpi/tests/avocado/bits: enforce 32-bit SMBIOS entry point QEMU defaults to 64-bit entry point since the following commit bf376f3020 ("hw/i386/pc: Default to use SMBIOS 3.0 for newer machine models") The above change is applicable for all newer machine versions from version 8.1 and newer. i440fx and q35 machine versions 8.0 and older still use 32-bit entry points. Unfortunately, bits currently does not recognize 64-bit entry points and hence is not able to parse SMBIOS tables. Therefore, we need to enforce 32-bit SMBIOS entry point in QEMU command line so that bits is able to parse the SMBIOS tables. Once we implement the support in bits to parse 64-bit entry points, we can remove the extra command line that is passed to enforce a 32-bit entry point. The support can be added to the following smbios test script: tests/avocado/acpi-bits/bits-tests/smbios.py2 in QEMU repository. CC: jusual@redhat.com CC: imammedo@redhat.com Signed-off-by: Ani Sinha Message-Id: <20231027032120.6012-2-anisinha@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/avocado/acpi-bits.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/avocado/acpi-bits.py b/tests/avocado/acpi-bits.py index eca13dc518..042007b0b8 100644 --- a/tests/avocado/acpi-bits.py +++ b/tests/avocado/acpi-bits.py @@ -380,6 +380,11 @@ class AcpiBitsTest(QemuBaseTest): #pylint: disable=too-many-instance-attributes # consistent in terms of timing. smilatency tests have consistent # timing requirements. self._vm.add_args('-icount', 'auto') + # currently there is no support in bits for recognizing 64-bit SMBIOS + # entry points. QEMU defaults to 64-bit entry points since the + # upstream commit bf376f3020 ("hw/i386/pc: Default to use SMBIOS 3.0 + # for newer machine models"). Therefore, enforce 32-bit entry point. + self._vm.add_args('-machine', 'smbios-entry-point-type=32') args = " ".join(str(arg) for arg in self._vm.base_args()) + \ " " + " ".join(str(arg) for arg in self._vm.args) From 94cd94f1c0137b56000c01208e03d0907ad34910 Mon Sep 17 00:00:00 2001 From: Ani Sinha Date: Fri, 27 Oct 2023 08:51:19 +0530 Subject: [PATCH 724/974] acpi/tests/avocado/bits: enable console logging from bits VM Console logs from the VM can be useful for debugging when things go wrong. Other avocado tests enables them. This change enables console logging with the following changes: - point to the newer bios bits image that actually enabled VM console. - change the bits test to drain the console logs from the VM and write the logs. - wait for SHUTDOWN event from QEMU so that console logs can be drained out of the socket before it is closed as a part of vm.wait(). Additionally, following two cosmetic changes have been made: - Removed VM QEMU command line logging as avocado framework already logs it. This is a minor cleanup along the way. - Update my email to my work email in the avocado acpi bios bits test. CC: jsnow@redhat.com Signed-off-by: Ani Sinha Message-Id: <20231027032120.6012-3-anisinha@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/avocado/acpi-bits.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/tests/avocado/acpi-bits.py b/tests/avocado/acpi-bits.py index 042007b0b8..68b9e98d4e 100644 --- a/tests/avocado/acpi-bits.py +++ b/tests/avocado/acpi-bits.py @@ -18,7 +18,7 @@ # # # Author: -# Ani Sinha +# Ani Sinha # pylint: disable=invalid-name # pylint: disable=consider-using-f-string @@ -48,6 +48,7 @@ from typing import ( ) from qemu.machine import QEMUMachine from avocado import skipIf +from avocado.utils import datadrainer as drainer from avocado_qemu import QemuBaseTest deps = ["xorriso", "mformat"] # dependent tools needed in the test setup/box. @@ -141,12 +142,12 @@ class AcpiBitsTest(QemuBaseTest): #pylint: disable=too-many-instance-attributes self._baseDir = None # following are some standard configuration constants - self._bitsInternalVer = 2020 - self._bitsCommitHash = 'b48b88ff' # commit hash must match + self._bitsInternalVer = 2020 # gitlab CI does shallow clones of depth 20 + self._bitsCommitHash = 'c7920d2b' # commit hash must match # the artifact tag below - self._bitsTag = "qemu-bits-10182022" # this is the latest bits + self._bitsTag = "qemu-bits-10262023" # this is the latest bits # release as of today. - self._bitsArtSHA1Hash = 'b04790ac9b99b5662d0416392c73b97580641fe5' + self._bitsArtSHA1Hash = 'b22cdfcfc7453875297d06d626f5474ee36a343f' self._bitsArtURL = ("https://gitlab.com/qemu-project/" "biosbits-bits/-/jobs/artifacts/%s/" "download?job=qemu-bits-build" %self._bitsTag) @@ -386,15 +387,20 @@ class AcpiBitsTest(QemuBaseTest): #pylint: disable=too-many-instance-attributes # for newer machine models"). Therefore, enforce 32-bit entry point. self._vm.add_args('-machine', 'smbios-entry-point-type=32') - args = " ".join(str(arg) for arg in self._vm.base_args()) + \ - " " + " ".join(str(arg) for arg in self._vm.args) - - self.logger.info("launching QEMU vm with the following arguments: %s", - args) - + # enable console logging + self._vm.set_console() self._vm.launch() + + self.logger.debug("Console output from bits VM follows ...") + c_drainer = drainer.LineLogger(self._vm.console_socket.fileno(), + logger=self.logger.getChild("console"), + stop_check=(lambda : + not self._vm.is_running())) + c_drainer.start() + # biosbits has been configured to run all the specified test suites # in batch mode and then automatically initiate a vm shutdown. # Rely on avocado's unit test timeout. + self._vm.event_wait('SHUTDOWN') self._vm.wait(timeout=None) self.parse_log() From 547c9757ddc4f83358e7187a6db9c05125dda2a2 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 23 Aug 2023 12:40:45 +0100 Subject: [PATCH 725/974] i386/xen: Ignore VCPU_SSHOTTMR_future flag in set_singleshot_timer() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Upstream Xen now ignores this flag¹, since the only guest kernel ever to use it was buggy. ¹ https://xenbits.xen.org/gitweb/?p=xen.git;a=commitdiff;h=19c6cbd909 Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- target/i386/kvm/xen-emu.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index 75b2c557b9..1dc9ab0d91 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -1077,17 +1077,13 @@ static int vcpuop_stop_periodic_timer(CPUState *target) * Must always be called with xen_timers_lock held. */ static int do_set_singleshot_timer(CPUState *cs, uint64_t timeout_abs, - bool future, bool linux_wa) + bool linux_wa) { CPUX86State *env = &X86_CPU(cs)->env; int64_t now = kvm_get_current_ns(); int64_t qemu_now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); int64_t delta = timeout_abs - now; - if (future && timeout_abs < now) { - return -ETIME; - } - if (linux_wa && unlikely((int64_t)timeout_abs < 0 || (delta > 0 && (uint32_t)(delta >> 50) != 0))) { /* @@ -1129,9 +1125,13 @@ static int vcpuop_set_singleshot_timer(CPUState *cs, uint64_t arg) } QEMU_LOCK_GUARD(&X86_CPU(cs)->env.xen_timers_lock); - return do_set_singleshot_timer(cs, sst.timeout_abs_ns, - !!(sst.flags & VCPU_SSHOTTMR_future), - false); + + /* + * We ignore the VCPU_SSHOTTMR_future flag, just as Xen now does. + * The only guest that ever used it, got it wrong. + * https://xenbits.xen.org/gitweb/?p=xen.git;a=commitdiff;h=19c6cbd909 + */ + return do_set_singleshot_timer(cs, sst.timeout_abs_ns, false); } static int vcpuop_stop_singleshot_timer(CPUState *cs) @@ -1156,7 +1156,7 @@ static bool kvm_xen_hcall_set_timer_op(struct kvm_xen_exit *exit, X86CPU *cpu, err = vcpuop_stop_singleshot_timer(CPU(cpu)); } else { QEMU_LOCK_GUARD(&X86_CPU(cpu)->env.xen_timers_lock); - err = do_set_singleshot_timer(CPU(cpu), timeout, false, true); + err = do_set_singleshot_timer(CPU(cpu), timeout, true); } exit->u.hcall.result = err; return true; @@ -1844,7 +1844,7 @@ int kvm_put_xen_state(CPUState *cs) QEMU_LOCK_GUARD(&env->xen_timers_lock); if (env->xen_singleshot_timer_ns) { ret = do_set_singleshot_timer(cs, env->xen_singleshot_timer_ns, - false, false); + false); if (ret < 0) { return ret; } From be15509882f2a745ec81a52d023864f077c13182 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 2 Aug 2023 17:04:49 +0100 Subject: [PATCH 726/974] hw/xen: Clean up event channel 'type_val' handling to use union A previous implementation of this stuff used a 64-bit field for all of the port information (vcpu/type/type_val) and did atomic exchanges on them. When I implemented that in Qemu I regretted my life choices and just kept it simple with locking instead. So there's no need for the XenEvtchnPort to be so simplistic. We can use a union for the pirq/virq/interdomain information, which lets us keep a separate bit for the 'remote domain' in interdomain ports. A single bit is enough since the only possible targets are loopback or qemu itself. So now we can ditch PORT_INFO_TYPEVAL_REMOTE_QEMU and the horrid manual masking, although the in-memory representation is identical so there's no change in the saved state ABI. Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/kvm/xen_evtchn.c | 151 ++++++++++++++++++--------------------- 1 file changed, 70 insertions(+), 81 deletions(-) diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c index b2b4be9983..02b8cbf8df 100644 --- a/hw/i386/kvm/xen_evtchn.c +++ b/hw/i386/kvm/xen_evtchn.c @@ -58,7 +58,15 @@ OBJECT_DECLARE_SIMPLE_TYPE(XenEvtchnState, XEN_EVTCHN) typedef struct XenEvtchnPort { uint32_t vcpu; /* Xen/ACPI vcpu_id */ uint16_t type; /* EVTCHNSTAT_xxxx */ - uint16_t type_val; /* pirq# / virq# / remote port according to type */ + union { + uint16_t val; /* raw value for serialization etc. */ + uint16_t pirq; + uint16_t virq; + struct { + uint16_t port:15; + uint16_t to_qemu:1; /* Only two targets; qemu or loopback */ + } interdomain; + } u; } XenEvtchnPort; /* 32-bit compatibility definitions, also used natively in 32-bit build */ @@ -105,14 +113,6 @@ struct xenevtchn_handle { int fd; }; -/* - * For unbound/interdomain ports there are only two possible remote - * domains; self and QEMU. Use a single high bit in type_val for that, - * and the low bits for the remote port number (or 0 for unbound). - */ -#define PORT_INFO_TYPEVAL_REMOTE_QEMU 0x8000 -#define PORT_INFO_TYPEVAL_REMOTE_PORT_MASK 0x7FFF - /* * These 'emuirq' values are used by Xen in the LM stream... and yes, I am * insane enough to think about guest-transparent live migration from actual @@ -210,16 +210,16 @@ static int xen_evtchn_post_load(void *opaque, int version_id) XenEvtchnPort *p = &s->port_table[i]; if (p->type == EVTCHNSTAT_pirq) { - assert(p->type_val); - assert(p->type_val < s->nr_pirqs); + assert(p->u.pirq); + assert(p->u.pirq < s->nr_pirqs); /* * Set the gsi to IRQ_UNBOUND; it may be changed to an actual * GSI# below, or to IRQ_MSI_EMU when the MSI table snooping * catches up with it. */ - s->pirq[p->type_val].gsi = IRQ_UNBOUND; - s->pirq[p->type_val].port = i; + s->pirq[p->u.pirq].gsi = IRQ_UNBOUND; + s->pirq[p->u.pirq].port = i; } } /* Rebuild s->pirq[].gsi mapping */ @@ -243,7 +243,7 @@ static const VMStateDescription xen_evtchn_port_vmstate = { .fields = (VMStateField[]) { VMSTATE_UINT32(vcpu, XenEvtchnPort), VMSTATE_UINT16(type, XenEvtchnPort), - VMSTATE_UINT16(type_val, XenEvtchnPort), + VMSTATE_UINT16(u.val, XenEvtchnPort), VMSTATE_END_OF_LIST() } }; @@ -605,14 +605,13 @@ static void unbind_backend_ports(XenEvtchnState *s) for (i = 1; i < s->nr_ports; i++) { p = &s->port_table[i]; - if (p->type == EVTCHNSTAT_interdomain && - (p->type_val & PORT_INFO_TYPEVAL_REMOTE_QEMU)) { - evtchn_port_t be_port = p->type_val & PORT_INFO_TYPEVAL_REMOTE_PORT_MASK; + if (p->type == EVTCHNSTAT_interdomain && p->u.interdomain.to_qemu) { + evtchn_port_t be_port = p->u.interdomain.port; if (s->be_handles[be_port]) { /* This part will be overwritten on the load anyway. */ p->type = EVTCHNSTAT_unbound; - p->type_val = PORT_INFO_TYPEVAL_REMOTE_QEMU; + p->u.interdomain.port = 0; /* Leave the backend port open and unbound too. */ if (kvm_xen_has_cap(EVTCHN_SEND)) { @@ -650,30 +649,22 @@ int xen_evtchn_status_op(struct evtchn_status *status) switch (p->type) { case EVTCHNSTAT_unbound: - if (p->type_val & PORT_INFO_TYPEVAL_REMOTE_QEMU) { - status->u.unbound.dom = DOMID_QEMU; - } else { - status->u.unbound.dom = xen_domid; - } + status->u.unbound.dom = p->u.interdomain.to_qemu ? DOMID_QEMU + : xen_domid; break; case EVTCHNSTAT_interdomain: - if (p->type_val & PORT_INFO_TYPEVAL_REMOTE_QEMU) { - status->u.interdomain.dom = DOMID_QEMU; - } else { - status->u.interdomain.dom = xen_domid; - } - - status->u.interdomain.port = p->type_val & - PORT_INFO_TYPEVAL_REMOTE_PORT_MASK; + status->u.interdomain.dom = p->u.interdomain.to_qemu ? DOMID_QEMU + : xen_domid; + status->u.interdomain.port = p->u.interdomain.port; break; case EVTCHNSTAT_pirq: - status->u.pirq = p->type_val; + status->u.pirq = p->u.pirq; break; case EVTCHNSTAT_virq: - status->u.virq = p->type_val; + status->u.virq = p->u.virq; break; } @@ -989,7 +980,7 @@ static int clear_port_pending(XenEvtchnState *s, evtchn_port_t port) static void free_port(XenEvtchnState *s, evtchn_port_t port) { s->port_table[port].type = EVTCHNSTAT_closed; - s->port_table[port].type_val = 0; + s->port_table[port].u.val = 0; s->port_table[port].vcpu = 0; if (s->nr_ports == port + 1) { @@ -1012,7 +1003,7 @@ static int allocate_port(XenEvtchnState *s, uint32_t vcpu, uint16_t type, if (s->port_table[p].type == EVTCHNSTAT_closed) { s->port_table[p].vcpu = vcpu; s->port_table[p].type = type; - s->port_table[p].type_val = val; + s->port_table[p].u.val = val; *port = p; @@ -1053,15 +1044,15 @@ static int close_port(XenEvtchnState *s, evtchn_port_t port, return -ENOENT; case EVTCHNSTAT_pirq: - s->pirq[p->type_val].port = 0; - if (s->pirq[p->type_val].is_translated) { + s->pirq[p->u.pirq].port = 0; + if (s->pirq[p->u.pirq].is_translated) { *flush_kvm_routes = true; } break; case EVTCHNSTAT_virq: - kvm_xen_set_vcpu_virq(virq_is_global(p->type_val) ? 0 : p->vcpu, - p->type_val, 0); + kvm_xen_set_vcpu_virq(virq_is_global(p->u.virq) ? 0 : p->vcpu, + p->u.virq, 0); break; case EVTCHNSTAT_ipi: @@ -1071,8 +1062,8 @@ static int close_port(XenEvtchnState *s, evtchn_port_t port, break; case EVTCHNSTAT_interdomain: - if (p->type_val & PORT_INFO_TYPEVAL_REMOTE_QEMU) { - uint16_t be_port = p->type_val & ~PORT_INFO_TYPEVAL_REMOTE_QEMU; + if (p->u.interdomain.to_qemu) { + uint16_t be_port = p->u.interdomain.port; struct xenevtchn_handle *xc = s->be_handles[be_port]; if (xc) { if (kvm_xen_has_cap(EVTCHN_SEND)) { @@ -1082,14 +1073,15 @@ static int close_port(XenEvtchnState *s, evtchn_port_t port, } } else { /* Loopback interdomain */ - XenEvtchnPort *rp = &s->port_table[p->type_val]; - if (!valid_port(p->type_val) || rp->type_val != port || + XenEvtchnPort *rp = &s->port_table[p->u.interdomain.port]; + if (!valid_port(p->u.interdomain.port) || + rp->u.interdomain.port != port || rp->type != EVTCHNSTAT_interdomain) { error_report("Inconsistent state for interdomain unbind"); } else { /* Set the other end back to unbound */ rp->type = EVTCHNSTAT_unbound; - rp->type_val = 0; + rp->u.interdomain.port = 0; } } break; @@ -1214,7 +1206,7 @@ int xen_evtchn_bind_vcpu_op(struct evtchn_bind_vcpu *vcpu) if (p->type == EVTCHNSTAT_interdomain || p->type == EVTCHNSTAT_unbound || p->type == EVTCHNSTAT_pirq || - (p->type == EVTCHNSTAT_virq && virq_is_global(p->type_val))) { + (p->type == EVTCHNSTAT_virq && virq_is_global(p->u.virq))) { /* * unmask_port() with do_unmask==false will just raise the event * on the new vCPU if the port was already pending. @@ -1359,19 +1351,15 @@ int xen_evtchn_bind_ipi_op(struct evtchn_bind_ipi *ipi) int xen_evtchn_bind_interdomain_op(struct evtchn_bind_interdomain *interdomain) { XenEvtchnState *s = xen_evtchn_singleton; - uint16_t type_val; int ret; if (!s) { return -ENOTSUP; } - if (interdomain->remote_dom == DOMID_QEMU) { - type_val = PORT_INFO_TYPEVAL_REMOTE_QEMU; - } else if (interdomain->remote_dom == DOMID_SELF || - interdomain->remote_dom == xen_domid) { - type_val = 0; - } else { + if (interdomain->remote_dom != DOMID_QEMU && + interdomain->remote_dom != DOMID_SELF && + interdomain->remote_dom != xen_domid) { return -ESRCH; } @@ -1382,8 +1370,8 @@ int xen_evtchn_bind_interdomain_op(struct evtchn_bind_interdomain *interdomain) qemu_mutex_lock(&s->port_lock); /* The newly allocated port starts out as unbound */ - ret = allocate_port(s, 0, EVTCHNSTAT_unbound, type_val, - &interdomain->local_port); + ret = allocate_port(s, 0, EVTCHNSTAT_unbound, 0, &interdomain->local_port); + if (ret) { goto out; } @@ -1408,7 +1396,8 @@ int xen_evtchn_bind_interdomain_op(struct evtchn_bind_interdomain *interdomain) assign_kernel_eventfd(lp->type, xc->guest_port, xc->fd); } lp->type = EVTCHNSTAT_interdomain; - lp->type_val = PORT_INFO_TYPEVAL_REMOTE_QEMU | interdomain->remote_port; + lp->u.interdomain.to_qemu = 1; + lp->u.interdomain.port = interdomain->remote_port; ret = 0; } else { /* Loopback */ @@ -1416,19 +1405,18 @@ int xen_evtchn_bind_interdomain_op(struct evtchn_bind_interdomain *interdomain) XenEvtchnPort *lp = &s->port_table[interdomain->local_port]; /* - * The 'remote' port for loopback must be an unbound port allocated for - * communication with the local domain (as indicated by rp->type_val - * being zero, not PORT_INFO_TYPEVAL_REMOTE_QEMU), and must *not* be - * the port that was just allocated for the local end. + * The 'remote' port for loopback must be an unbound port allocated + * for communication with the local domain, and must *not* be the + * port that was just allocated for the local end. */ if (interdomain->local_port != interdomain->remote_port && - rp->type == EVTCHNSTAT_unbound && rp->type_val == 0) { + rp->type == EVTCHNSTAT_unbound && !rp->u.interdomain.to_qemu) { rp->type = EVTCHNSTAT_interdomain; - rp->type_val = interdomain->local_port; + rp->u.interdomain.port = interdomain->local_port; lp->type = EVTCHNSTAT_interdomain; - lp->type_val = interdomain->remote_port; + lp->u.interdomain.port = interdomain->remote_port; } else { ret = -EINVAL; } @@ -1447,7 +1435,6 @@ int xen_evtchn_bind_interdomain_op(struct evtchn_bind_interdomain *interdomain) int xen_evtchn_alloc_unbound_op(struct evtchn_alloc_unbound *alloc) { XenEvtchnState *s = xen_evtchn_singleton; - uint16_t type_val; int ret; if (!s) { @@ -1458,18 +1445,20 @@ int xen_evtchn_alloc_unbound_op(struct evtchn_alloc_unbound *alloc) return -ESRCH; } - if (alloc->remote_dom == DOMID_QEMU) { - type_val = PORT_INFO_TYPEVAL_REMOTE_QEMU; - } else if (alloc->remote_dom == DOMID_SELF || - alloc->remote_dom == xen_domid) { - type_val = 0; - } else { + if (alloc->remote_dom != DOMID_QEMU && + alloc->remote_dom != DOMID_SELF && + alloc->remote_dom != xen_domid) { return -EPERM; } qemu_mutex_lock(&s->port_lock); - ret = allocate_port(s, 0, EVTCHNSTAT_unbound, type_val, &alloc->port); + ret = allocate_port(s, 0, EVTCHNSTAT_unbound, 0, &alloc->port); + + if (!ret && alloc->remote_dom == DOMID_QEMU) { + XenEvtchnPort *p = &s->port_table[alloc->port]; + p->u.interdomain.to_qemu = 1; + } qemu_mutex_unlock(&s->port_lock); @@ -1496,12 +1485,12 @@ int xen_evtchn_send_op(struct evtchn_send *send) switch (p->type) { case EVTCHNSTAT_interdomain: - if (p->type_val & PORT_INFO_TYPEVAL_REMOTE_QEMU) { + if (p->u.interdomain.to_qemu) { /* * This is an event from the guest to qemu itself, which is * serving as the driver domain. */ - uint16_t be_port = p->type_val & ~PORT_INFO_TYPEVAL_REMOTE_QEMU; + uint16_t be_port = p->u.interdomain.port; struct xenevtchn_handle *xc = s->be_handles[be_port]; if (xc) { eventfd_write(xc->fd, 1); @@ -1511,7 +1500,7 @@ int xen_evtchn_send_op(struct evtchn_send *send) } } else { /* Loopback interdomain ports; just a complex IPI */ - set_port_pending(s, p->type_val); + set_port_pending(s, p->u.interdomain.port); } break; @@ -1553,8 +1542,7 @@ int xen_evtchn_set_port(uint16_t port) /* QEMU has no business sending to anything but these */ if (p->type == EVTCHNSTAT_virq || - (p->type == EVTCHNSTAT_interdomain && - (p->type_val & PORT_INFO_TYPEVAL_REMOTE_QEMU))) { + (p->type == EVTCHNSTAT_interdomain && p->u.interdomain.to_qemu)) { set_port_pending(s, port); ret = 0; } @@ -2064,7 +2052,7 @@ int xen_be_evtchn_bind_interdomain(struct xenevtchn_handle *xc, uint32_t domid, switch (gp->type) { case EVTCHNSTAT_interdomain: /* Allow rebinding after migration, preserve port # if possible */ - be_port = gp->type_val & ~PORT_INFO_TYPEVAL_REMOTE_QEMU; + be_port = gp->u.interdomain.port; assert(be_port != 0); if (!s->be_handles[be_port]) { s->be_handles[be_port] = xc; @@ -2085,7 +2073,8 @@ int xen_be_evtchn_bind_interdomain(struct xenevtchn_handle *xc, uint32_t domid, } gp->type = EVTCHNSTAT_interdomain; - gp->type_val = be_port | PORT_INFO_TYPEVAL_REMOTE_QEMU; + gp->u.interdomain.to_qemu = 1; + gp->u.interdomain.port = be_port; xc->guest_port = guest_port; if (kvm_xen_has_cap(EVTCHN_SEND)) { assign_kernel_eventfd(gp->type, guest_port, xc->fd); @@ -2130,7 +2119,7 @@ int xen_be_evtchn_unbind(struct xenevtchn_handle *xc, evtchn_port_t port) /* This should never *not* be true */ if (gp->type == EVTCHNSTAT_interdomain) { gp->type = EVTCHNSTAT_unbound; - gp->type_val = PORT_INFO_TYPEVAL_REMOTE_QEMU; + gp->u.interdomain.port = 0; } if (kvm_xen_has_cap(EVTCHN_SEND)) { @@ -2284,11 +2273,11 @@ EvtchnInfoList *qmp_xen_event_list(Error **errp) info->type = p->type; if (p->type == EVTCHNSTAT_interdomain) { - info->remote_domain = g_strdup((p->type_val & PORT_INFO_TYPEVAL_REMOTE_QEMU) ? + info->remote_domain = g_strdup(p->u.interdomain.to_qemu ? "qemu" : "loopback"); - info->target = p->type_val & PORT_INFO_TYPEVAL_REMOTE_PORT_MASK; + info->target = p->u.interdomain.port; } else { - info->target = p->type_val; + info->target = p->u.val; /* pirq# or virq# */ } info->vcpu = p->vcpu; info->pending = test_bit(i, pending); From 8ac98aeddaac1778e7c725609f1fd5eaa38e2285 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 11 Oct 2023 23:47:30 +0100 Subject: [PATCH 727/974] include: update Xen public headers to Xen 4.17.2 release ... in order to advertise the XEN_HVM_CPUID_UPCALL_VECTOR feature, which will come in a subsequent commit. Signed-off-by: David Woodhouse Acked-by: Paul Durrant --- hw/i386/kvm/xen_xenstore.c | 2 +- include/hw/xen/interface/arch-arm.h | 37 +++++++------- include/hw/xen/interface/arch-x86/cpuid.h | 31 +++++------- .../hw/xen/interface/arch-x86/xen-x86_32.h | 19 +------ .../hw/xen/interface/arch-x86/xen-x86_64.h | 19 +------ include/hw/xen/interface/arch-x86/xen.h | 26 ++-------- include/hw/xen/interface/event_channel.h | 19 +------ include/hw/xen/interface/features.h | 19 +------ include/hw/xen/interface/grant_table.h | 19 +------ include/hw/xen/interface/hvm/hvm_op.h | 19 +------ include/hw/xen/interface/hvm/params.h | 19 +------ include/hw/xen/interface/io/blkif.h | 27 ++++------ include/hw/xen/interface/io/console.h | 19 +------ include/hw/xen/interface/io/fbif.h | 19 +------ include/hw/xen/interface/io/kbdif.h | 19 +------ include/hw/xen/interface/io/netif.h | 25 +++------- include/hw/xen/interface/io/protocols.h | 19 +------ include/hw/xen/interface/io/ring.h | 49 ++++++++++--------- include/hw/xen/interface/io/usbif.h | 19 +------ include/hw/xen/interface/io/xenbus.h | 19 +------ include/hw/xen/interface/io/xs_wire.h | 36 ++++++-------- include/hw/xen/interface/memory.h | 30 +++++------- include/hw/xen/interface/physdev.h | 23 ++------- include/hw/xen/interface/sched.h | 19 +------ include/hw/xen/interface/trace.h | 19 +------ include/hw/xen/interface/vcpu.h | 19 +------ include/hw/xen/interface/version.h | 19 +------ include/hw/xen/interface/xen-compat.h | 19 +------ include/hw/xen/interface/xen.h | 19 +------ 29 files changed, 124 insertions(+), 523 deletions(-) diff --git a/hw/i386/kvm/xen_xenstore.c b/hw/i386/kvm/xen_xenstore.c index 8e716a7009..831da535fc 100644 --- a/hw/i386/kvm/xen_xenstore.c +++ b/hw/i386/kvm/xen_xenstore.c @@ -331,7 +331,7 @@ static void xs_error(XenXenstoreState *s, unsigned int id, const char *errstr = NULL; for (unsigned int i = 0; i < ARRAY_SIZE(xsd_errors); i++) { - struct xsd_errors *xsd_error = &xsd_errors[i]; + const struct xsd_errors *xsd_error = &xsd_errors[i]; if (xsd_error->errnum == errnum) { errstr = xsd_error->errstring; diff --git a/include/hw/xen/interface/arch-arm.h b/include/hw/xen/interface/arch-arm.h index 94b31511dd..1528ced509 100644 --- a/include/hw/xen/interface/arch-arm.h +++ b/include/hw/xen/interface/arch-arm.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * arch-arm.h * * Guest OS interface to ARM Xen. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * * Copyright 2011 (C) Citrix Systems */ @@ -361,6 +344,7 @@ typedef uint64_t xen_callback_t; #define PSR_DBG_MASK (1<<9) /* arm64: Debug Exception mask */ #define PSR_IT_MASK (0x0600fc00) /* Thumb If-Then Mask */ #define PSR_JAZELLE (1<<24) /* Jazelle Mode */ +#define PSR_Z (1<<30) /* Zero condition flag */ /* 32 bit modes */ #define PSR_MODE_USR 0x10 @@ -383,7 +367,15 @@ typedef uint64_t xen_callback_t; #define PSR_MODE_EL1t 0x04 #define PSR_MODE_EL0t 0x00 -#define PSR_GUEST32_INIT (PSR_ABT_MASK|PSR_FIQ_MASK|PSR_IRQ_MASK|PSR_MODE_SVC) +/* + * We set PSR_Z to be able to boot Linux kernel versions with an invalid + * encoding of the first 8 NOP instructions. See commit a92882a4d270 in + * Linux. + * + * Note that PSR_Z is also set by U-Boot and QEMU -kernel when loading + * zImage kernels on aarch32. + */ +#define PSR_GUEST32_INIT (PSR_Z|PSR_ABT_MASK|PSR_FIQ_MASK|PSR_IRQ_MASK|PSR_MODE_SVC) #define PSR_GUEST64_INIT (PSR_ABT_MASK|PSR_FIQ_MASK|PSR_IRQ_MASK|PSR_MODE_EL1h) #define SCTLR_GUEST_INIT xen_mk_ullong(0x00c50078) @@ -398,6 +390,10 @@ typedef uint64_t xen_callback_t; /* Physical Address Space */ +/* Virtio MMIO mappings */ +#define GUEST_VIRTIO_MMIO_BASE xen_mk_ullong(0x02000000) +#define GUEST_VIRTIO_MMIO_SIZE xen_mk_ullong(0x00100000) + /* * vGIC mappings: Only one set of mapping is used by the guest. * Therefore they can overlap. @@ -484,6 +480,9 @@ typedef uint64_t xen_callback_t; #define GUEST_VPL011_SPI 32 +#define GUEST_VIRTIO_MMIO_SPI_FIRST 33 +#define GUEST_VIRTIO_MMIO_SPI_LAST 43 + /* PSCI functions */ #define PSCI_cpu_suspend 0 #define PSCI_cpu_off 1 diff --git a/include/hw/xen/interface/arch-x86/cpuid.h b/include/hw/xen/interface/arch-x86/cpuid.h index ce46305bee..7ecd16ae05 100644 --- a/include/hw/xen/interface/arch-x86/cpuid.h +++ b/include/hw/xen/interface/arch-x86/cpuid.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * arch-x86/cpuid.h * * CPUID interface to Xen. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * * Copyright (c) 2007 Citrix Systems, Inc. * * Authors: @@ -102,6 +85,18 @@ #define XEN_HVM_CPUID_IOMMU_MAPPINGS (1u << 2) #define XEN_HVM_CPUID_VCPU_ID_PRESENT (1u << 3) /* vcpu id is present in EBX */ #define XEN_HVM_CPUID_DOMID_PRESENT (1u << 4) /* domid is present in ECX */ +/* + * With interrupt format set to 0 (non-remappable) bits 55:49 from the + * IO-APIC RTE and bits 11:5 from the MSI address can be used to store + * high bits for the Destination ID. This expands the Destination ID + * field from 8 to 15 bits, allowing to target APIC IDs up 32768. + */ +#define XEN_HVM_CPUID_EXT_DEST_ID (1u << 5) +/* + * Per-vCPU event channel upcalls work correctly with physical IRQs + * bound to event channels. + */ +#define XEN_HVM_CPUID_UPCALL_VECTOR (1u << 6) /* * Leaf 6 (0x40000x05) diff --git a/include/hw/xen/interface/arch-x86/xen-x86_32.h b/include/hw/xen/interface/arch-x86/xen-x86_32.h index 19d7388633..139438e835 100644 --- a/include/hw/xen/interface/arch-x86/xen-x86_32.h +++ b/include/hw/xen/interface/arch-x86/xen-x86_32.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * xen-x86_32.h * * Guest OS interface to x86 32-bit Xen. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * * Copyright (c) 2004-2007, K A Fraser */ diff --git a/include/hw/xen/interface/arch-x86/xen-x86_64.h b/include/hw/xen/interface/arch-x86/xen-x86_64.h index 40aed14366..5d9035ed22 100644 --- a/include/hw/xen/interface/arch-x86/xen-x86_64.h +++ b/include/hw/xen/interface/arch-x86/xen-x86_64.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * xen-x86_64.h * * Guest OS interface to x86 64-bit Xen. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * * Copyright (c) 2004-2006, K A Fraser */ diff --git a/include/hw/xen/interface/arch-x86/xen.h b/include/hw/xen/interface/arch-x86/xen.h index 7acd94c8eb..c0f4551247 100644 --- a/include/hw/xen/interface/arch-x86/xen.h +++ b/include/hw/xen/interface/arch-x86/xen.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * arch-x86/xen.h * * Guest OS interface to x86 Xen. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * * Copyright (c) 2004-2006, K A Fraser */ @@ -320,12 +303,9 @@ struct xen_arch_domainconfig { uint32_t misc_flags; }; -/* Location of online VCPU bitmap. */ -#define XEN_ACPI_CPU_MAP 0xaf00 -#define XEN_ACPI_CPU_MAP_LEN ((HVM_MAX_VCPUS + 7) / 8) +/* Max XEN_X86_* constant. Used for ABI checking. */ +#define XEN_X86_MISC_FLAGS_MAX XEN_X86_MSR_RELAXED -/* GPE0 bit set during CPU hotplug */ -#define XEN_ACPI_GPE0_CPUHP_BIT 2 #endif /* diff --git a/include/hw/xen/interface/event_channel.h b/include/hw/xen/interface/event_channel.h index 73c9f38ce1..0d91a1c4af 100644 --- a/include/hw/xen/interface/event_channel.h +++ b/include/hw/xen/interface/event_channel.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * event_channel.h * * Event channels between domains. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * * Copyright (c) 2003-2004, K A Fraser. */ diff --git a/include/hw/xen/interface/features.h b/include/hw/xen/interface/features.h index 9ee2f760ef..d2a9175aae 100644 --- a/include/hw/xen/interface/features.h +++ b/include/hw/xen/interface/features.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * features.h * * Feature flags, reported by XENVER_get_features. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * * Copyright (c) 2006, Keir Fraser */ diff --git a/include/hw/xen/interface/grant_table.h b/include/hw/xen/interface/grant_table.h index 7934d7b718..1dfa17a6d0 100644 --- a/include/hw/xen/interface/grant_table.h +++ b/include/hw/xen/interface/grant_table.h @@ -1,27 +1,10 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * grant_table.h * * Interface for granting foreign access to page frames, and receiving * page-ownership transfers. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * * Copyright (c) 2004, K A Fraser */ diff --git a/include/hw/xen/interface/hvm/hvm_op.h b/include/hw/xen/interface/hvm/hvm_op.h index 870ec52060..e22adf0319 100644 --- a/include/hw/xen/interface/hvm/hvm_op.h +++ b/include/hw/xen/interface/hvm/hvm_op.h @@ -1,22 +1,5 @@ +/* SPDX-License-Identifier: MIT */ /* - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * * Copyright (c) 2007, Keir Fraser */ diff --git a/include/hw/xen/interface/hvm/params.h b/include/hw/xen/interface/hvm/params.h index c9d6e70d7b..a22b4ed45d 100644 --- a/include/hw/xen/interface/hvm/params.h +++ b/include/hw/xen/interface/hvm/params.h @@ -1,22 +1,5 @@ +/* SPDX-License-Identifier: MIT */ /* - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * * Copyright (c) 2007, Keir Fraser */ diff --git a/include/hw/xen/interface/io/blkif.h b/include/hw/xen/interface/io/blkif.h index 4cdba79aba..22f1eef0c0 100644 --- a/include/hw/xen/interface/io/blkif.h +++ b/include/hw/xen/interface/io/blkif.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * blkif.h * * Unified block-device I/O interface for Xen guest OSes. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * * Copyright (c) 2003-2004, Keir Fraser * Copyright (c) 2012, Spectra Logic Corporation */ @@ -363,6 +346,14 @@ * that the frontend requires that the logical block size is 512 as it * is hardcoded (which is the case in some frontend implementations). * + * trusted + * Values: 0/1 (boolean) + * Default value: 1 + * + * A value of "0" indicates that the frontend should not trust the + * backend, and should deploy whatever measures available to protect from + * a malicious backend on the other end. + * *------------------------- Virtual Device Properties ------------------------- * * device-type diff --git a/include/hw/xen/interface/io/console.h b/include/hw/xen/interface/io/console.h index 4811f47220..4509b4b689 100644 --- a/include/hw/xen/interface/io/console.h +++ b/include/hw/xen/interface/io/console.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * console.h * * Console I/O interface for Xen guest OSes. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * * Copyright (c) 2005, Keir Fraser */ diff --git a/include/hw/xen/interface/io/fbif.h b/include/hw/xen/interface/io/fbif.h index cc25aab32e..93c73195d8 100644 --- a/include/hw/xen/interface/io/fbif.h +++ b/include/hw/xen/interface/io/fbif.h @@ -1,24 +1,7 @@ +/* SPDX-License-Identifier: MIT */ /* * fbif.h -- Xen virtual frame buffer device * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * * Copyright (C) 2005 Anthony Liguori * Copyright (C) 2006 Red Hat, Inc., Markus Armbruster */ diff --git a/include/hw/xen/interface/io/kbdif.h b/include/hw/xen/interface/io/kbdif.h index a6b01c52c7..4bde6b3821 100644 --- a/include/hw/xen/interface/io/kbdif.h +++ b/include/hw/xen/interface/io/kbdif.h @@ -1,24 +1,7 @@ +/* SPDX-License-Identifier: MIT */ /* * kbdif.h -- Xen virtual keyboard/mouse * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * * Copyright (C) 2005 Anthony Liguori * Copyright (C) 2006 Red Hat, Inc., Markus Armbruster */ diff --git a/include/hw/xen/interface/io/netif.h b/include/hw/xen/interface/io/netif.h index 00dd258712..c13b85061d 100644 --- a/include/hw/xen/interface/io/netif.h +++ b/include/hw/xen/interface/io/netif.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * netif.h * * Unified network-device I/O interface for Xen guest OSes. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * * Copyright (c) 2003-2004, Keir Fraser */ @@ -160,6 +143,12 @@ * be applied if it is set. */ +/* + * The setting of "trusted" node to "0" in the frontend path signals that the + * frontend should not trust the backend, and should deploy whatever measures + * available to protect from a malicious backend on the other end. + */ + /* * Control ring * ============ diff --git a/include/hw/xen/interface/io/protocols.h b/include/hw/xen/interface/io/protocols.h index 52b4de0f81..7815e1ff0f 100644 --- a/include/hw/xen/interface/io/protocols.h +++ b/include/hw/xen/interface/io/protocols.h @@ -1,24 +1,7 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * protocols.h * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * * Copyright (c) 2008, Keir Fraser */ diff --git a/include/hw/xen/interface/io/ring.h b/include/hw/xen/interface/io/ring.h index c486c457e0..025939278b 100644 --- a/include/hw/xen/interface/io/ring.h +++ b/include/hw/xen/interface/io/ring.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * ring.h * * Shared producer-consumer ring macros. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * * Tim Deegan and Andrew Warfield November 2004. */ @@ -95,9 +78,8 @@ typedef unsigned int RING_IDX; * of the shared memory area (PAGE_SIZE, for instance). To initialise * the front half: * - * mytag_front_ring_t front_ring; - * SHARED_RING_INIT((mytag_sring_t *)shared_page); - * FRONT_RING_INIT(&front_ring, (mytag_sring_t *)shared_page, PAGE_SIZE); + * mytag_front_ring_t ring; + * XEN_FRONT_RING_INIT(&ring, (mytag_sring_t *)shared_page, PAGE_SIZE); * * Initializing the back follows similarly (note that only the front * initializes the shared ring): @@ -184,6 +166,11 @@ typedef struct __name##_back_ring __name##_back_ring_t #define FRONT_RING_INIT(_r, _s, __size) FRONT_RING_ATTACH(_r, _s, 0, __size) +#define XEN_FRONT_RING_INIT(r, s, size) do { \ + SHARED_RING_INIT(s); \ + FRONT_RING_INIT(r, s, size); \ +} while (0) + #define BACK_RING_ATTACH(_r, _s, _i, __size) do { \ (_r)->rsp_prod_pvt = (_i); \ (_r)->req_cons = (_i); \ @@ -208,11 +195,11 @@ typedef struct __name##_back_ring __name##_back_ring_t (RING_FREE_REQUESTS(_r) == 0) /* Test if there are outstanding messages to be processed on a ring. */ -#define RING_HAS_UNCONSUMED_RESPONSES(_r) \ +#define XEN_RING_NR_UNCONSUMED_RESPONSES(_r) \ ((_r)->sring->rsp_prod - (_r)->rsp_cons) #ifdef __GNUC__ -#define RING_HAS_UNCONSUMED_REQUESTS(_r) ({ \ +#define XEN_RING_NR_UNCONSUMED_REQUESTS(_r) ({ \ unsigned int req = (_r)->sring->req_prod - (_r)->req_cons; \ unsigned int rsp = RING_SIZE(_r) - \ ((_r)->req_cons - (_r)->rsp_prod_pvt); \ @@ -220,13 +207,27 @@ typedef struct __name##_back_ring __name##_back_ring_t }) #else /* Same as above, but without the nice GCC ({ ... }) syntax. */ -#define RING_HAS_UNCONSUMED_REQUESTS(_r) \ +#define XEN_RING_NR_UNCONSUMED_REQUESTS(_r) \ ((((_r)->sring->req_prod - (_r)->req_cons) < \ (RING_SIZE(_r) - ((_r)->req_cons - (_r)->rsp_prod_pvt))) ? \ ((_r)->sring->req_prod - (_r)->req_cons) : \ (RING_SIZE(_r) - ((_r)->req_cons - (_r)->rsp_prod_pvt))) #endif +#ifdef XEN_RING_HAS_UNCONSUMED_IS_BOOL +/* + * These variants should only be used in case no caller is abusing them for + * obtaining the number of unconsumed responses/requests. + */ +#define RING_HAS_UNCONSUMED_RESPONSES(_r) \ + (!!XEN_RING_NR_UNCONSUMED_RESPONSES(_r)) +#define RING_HAS_UNCONSUMED_REQUESTS(_r) \ + (!!XEN_RING_NR_UNCONSUMED_REQUESTS(_r)) +#else +#define RING_HAS_UNCONSUMED_RESPONSES(_r) XEN_RING_NR_UNCONSUMED_RESPONSES(_r) +#define RING_HAS_UNCONSUMED_REQUESTS(_r) XEN_RING_NR_UNCONSUMED_REQUESTS(_r) +#endif + /* Direct access to individual ring elements, by index. */ #define RING_GET_REQUEST(_r, _idx) \ (&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].req)) diff --git a/include/hw/xen/interface/io/usbif.h b/include/hw/xen/interface/io/usbif.h index c0a552e195..875af0dc7c 100644 --- a/include/hw/xen/interface/io/usbif.h +++ b/include/hw/xen/interface/io/usbif.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: MIT */ /* * usbif.h * @@ -5,24 +6,6 @@ * * Copyright (C) 2009, FUJITSU LABORATORIES LTD. * Author: Noboru Iwamatsu - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. */ #ifndef __XEN_PUBLIC_IO_USBIF_H__ diff --git a/include/hw/xen/interface/io/xenbus.h b/include/hw/xen/interface/io/xenbus.h index 927f9db552..9cd0cd7c67 100644 --- a/include/hw/xen/interface/io/xenbus.h +++ b/include/hw/xen/interface/io/xenbus.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /***************************************************************************** * xenbus.h * * Xenbus protocol details. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * * Copyright (C) 2005 XenSource Ltd. */ diff --git a/include/hw/xen/interface/io/xs_wire.h b/include/hw/xen/interface/io/xs_wire.h index 4dd6632669..04e6849feb 100644 --- a/include/hw/xen/interface/io/xs_wire.h +++ b/include/hw/xen/interface/io/xs_wire.h @@ -1,25 +1,8 @@ +/* SPDX-License-Identifier: MIT */ /* * Details of the "wire" protocol between Xen Store Daemon and client * library or guest kernel. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * * Copyright (C) 2005 Rusty Russell IBM Corporation */ @@ -71,11 +54,12 @@ struct xsd_errors #ifdef EINVAL #define XSD_ERROR(x) { x, #x } /* LINTED: static unused */ -static struct xsd_errors xsd_errors[] +static const struct xsd_errors xsd_errors[] #if defined(__GNUC__) __attribute__((unused)) #endif = { + /* /!\ New errors should be added at the end of the array. */ XSD_ERROR(EINVAL), XSD_ERROR(EACCES), XSD_ERROR(EEXIST), @@ -90,7 +74,8 @@ __attribute__((unused)) XSD_ERROR(EBUSY), XSD_ERROR(EAGAIN), XSD_ERROR(EISCONN), - XSD_ERROR(E2BIG) + XSD_ERROR(E2BIG), + XSD_ERROR(EPERM), }; #endif @@ -124,6 +109,7 @@ struct xenstore_domain_interface { XENSTORE_RING_IDX rsp_cons, rsp_prod; uint32_t server_features; /* Bitmap of features supported by the server */ uint32_t connection; + uint32_t error; }; /* Violating this is very bad. See docs/misc/xenstore.txt. */ @@ -135,10 +121,18 @@ struct xenstore_domain_interface { /* The ability to reconnect a ring */ #define XENSTORE_SERVER_FEATURE_RECONNECTION 1 +/* The presence of the "error" field in the ring page */ +#define XENSTORE_SERVER_FEATURE_ERROR 2 /* Valid values for the connection field */ #define XENSTORE_CONNECTED 0 /* the steady-state */ -#define XENSTORE_RECONNECT 1 /* guest has initiated a reconnect */ +#define XENSTORE_RECONNECT 1 /* reconnect in progress */ + +/* Valid values for the error field */ +#define XENSTORE_ERROR_NONE 0 /* No error */ +#define XENSTORE_ERROR_COMM 1 /* Communication problem */ +#define XENSTORE_ERROR_RINGIDX 2 /* Invalid ring index */ +#define XENSTORE_ERROR_PROTO 3 /* Protocol violation (payload too long) */ #endif /* _XS_WIRE_H */ diff --git a/include/hw/xen/interface/memory.h b/include/hw/xen/interface/memory.h index 383a9468c3..29cf5c8239 100644 --- a/include/hw/xen/interface/memory.h +++ b/include/hw/xen/interface/memory.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * memory.h * * Memory reservation and information. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * * Copyright (c) 2005, Keir Fraser */ @@ -541,12 +524,14 @@ struct xen_mem_sharing_op { uint32_t gref; /* IN: gref to debug */ } u; } debug; - struct mem_sharing_op_fork { /* OP_FORK */ + struct mem_sharing_op_fork { /* OP_FORK{,_RESET} */ domid_t parent_domain; /* IN: parent's domain id */ /* Only makes sense for short-lived forks */ #define XENMEM_FORK_WITH_IOMMU_ALLOWED (1u << 0) /* Only makes sense for short-lived forks */ #define XENMEM_FORK_BLOCK_INTERRUPTS (1u << 1) +#define XENMEM_FORK_RESET_STATE (1u << 2) +#define XENMEM_FORK_RESET_MEMORY (1u << 3) uint16_t flags; /* IN: optional settings */ uint32_t pad; /* Must be set to 0 */ } fork; @@ -662,6 +647,13 @@ struct xen_mem_acquire_resource { * two calls. */ uint32_t nr_frames; + /* + * Padding field, must be zero on input. + * In a previous version this was an output field with the lowest bit + * named XENMEM_rsrc_acq_caller_owned. Future versions of this interface + * will not reuse this bit as an output with the field being zero on + * input. + */ uint32_t pad; /* * IN - the index of the initial frame to be mapped. This parameter diff --git a/include/hw/xen/interface/physdev.h b/include/hw/xen/interface/physdev.h index d271766ad0..f0c0d4727c 100644 --- a/include/hw/xen/interface/physdev.h +++ b/include/hw/xen/interface/physdev.h @@ -1,22 +1,5 @@ +/* SPDX-License-Identifier: MIT */ /* - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * * Copyright (c) 2006, Keir Fraser */ @@ -211,8 +194,8 @@ struct physdev_manage_pci_ext { /* IN */ uint8_t bus; uint8_t devfn; - unsigned is_extfn; - unsigned is_virtfn; + uint32_t is_extfn; + uint32_t is_virtfn; struct { uint8_t bus; uint8_t devfn; diff --git a/include/hw/xen/interface/sched.h b/include/hw/xen/interface/sched.h index 811bd87c82..b4362c6a1d 100644 --- a/include/hw/xen/interface/sched.h +++ b/include/hw/xen/interface/sched.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * sched.h * * Scheduler state interactions * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * * Copyright (c) 2005, Keir Fraser */ diff --git a/include/hw/xen/interface/trace.h b/include/hw/xen/interface/trace.h index d5fa4aea8d..62a179971d 100644 --- a/include/hw/xen/interface/trace.h +++ b/include/hw/xen/interface/trace.h @@ -1,24 +1,7 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * include/public/trace.h * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * * Mark Williamson, (C) 2004 Intel Research Cambridge * Copyright (C) 2005 Bin Ren */ diff --git a/include/hw/xen/interface/vcpu.h b/include/hw/xen/interface/vcpu.h index 3623af932f..81a3b3a743 100644 --- a/include/hw/xen/interface/vcpu.h +++ b/include/hw/xen/interface/vcpu.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * vcpu.h * * VCPU initialisation, query, and hotplug. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * * Copyright (c) 2005, Keir Fraser */ diff --git a/include/hw/xen/interface/version.h b/include/hw/xen/interface/version.h index 17a81e23cd..9c78b4f3b6 100644 --- a/include/hw/xen/interface/version.h +++ b/include/hw/xen/interface/version.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * version.h * * Xen version, type, and compile information. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * * Copyright (c) 2005, Nguyen Anh Quynh * Copyright (c) 2005, Keir Fraser */ diff --git a/include/hw/xen/interface/xen-compat.h b/include/hw/xen/interface/xen-compat.h index e1c027a95c..97fe698498 100644 --- a/include/hw/xen/interface/xen-compat.h +++ b/include/hw/xen/interface/xen-compat.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * xen-compat.h * * Guest OS interface to Xen. Compatibility layer. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * * Copyright (c) 2006, Christian Limpach */ diff --git a/include/hw/xen/interface/xen.h b/include/hw/xen/interface/xen.h index e373592c33..920567e006 100644 --- a/include/hw/xen/interface/xen.h +++ b/include/hw/xen/interface/xen.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * xen.h * * Guest OS interface to Xen. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * * Copyright (c) 2004, K A Fraser */ From 8473607bcfef62fd8f6bb98b27807bbfce30a81a Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 11 Oct 2023 23:50:02 +0100 Subject: [PATCH 728/974] i386/xen: advertise XEN_HVM_CPUID_UPCALL_VECTOR in CPUID This will allow Linux guests (since v6.0) to use the per-vCPU upcall vector delivered as MSI through the local APIC. Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- target/i386/kvm/kvm.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 770e81d56e..11b8177eff 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -1837,6 +1837,10 @@ int kvm_arch_init_vcpu(CPUState *cs) c->eax |= XEN_HVM_CPUID_VCPU_ID_PRESENT; c->ebx = cs->cpu_index; } + + if (cs->kvm_state->xen_version >= XEN_VERSION(4, 17)) { + c->eax |= XEN_HVM_CPUID_UPCALL_VECTOR; + } } r = kvm_xen_init_vcpu(cs); From d388c9f53b971fd185ee1dc2f4f3ca88abda906d Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 12 Oct 2023 10:59:45 +0100 Subject: [PATCH 729/974] hw/xen: populate store frontend nodes with XenStore PFN/port This is kind of redundant since without being able to get these through some other method (HVMOP_get_param) the guest wouldn't be able to access XenStore in order to find them. Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/kvm/xen_xenstore.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/hw/i386/kvm/xen_xenstore.c b/hw/i386/kvm/xen_xenstore.c index 831da535fc..b7c0407765 100644 --- a/hw/i386/kvm/xen_xenstore.c +++ b/hw/i386/kvm/xen_xenstore.c @@ -1434,6 +1434,7 @@ static void alloc_guest_port(XenXenstoreState *s) int xen_xenstore_reset(void) { XenXenstoreState *s = xen_xenstore_singleton; + GList *perms; int err; if (!s) { @@ -1461,6 +1462,16 @@ int xen_xenstore_reset(void) } s->be_port = err; + /* Create frontend store nodes */ + perms = g_list_append(NULL, xs_perm_as_string(XS_PERM_NONE, DOMID_QEMU)); + perms = g_list_append(perms, xs_perm_as_string(XS_PERM_READ, xen_domid)); + + relpath_printf(s, perms, "store/port", "%u", s->guest_port); + relpath_printf(s, perms, "store/ring-ref", "%lu", + XEN_SPECIAL_PFN(XENSTORE)); + + g_list_free_full(perms, g_free); + /* * We don't actually access the guest's page through the grant, because * this isn't real Xen, and we can just use the page we gave it in the From d3256f88d988809ae006aa28d6df88b37b253bc1 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 16 Oct 2023 13:01:39 +0100 Subject: [PATCH 730/974] hw/xen: automatically assign device index to block devices There's no need to force the user to assign a vdev. We can automatically assign one, starting at xvda and searching until we find the first disk name that's unused. This means we can now allow '-drive if=xen,file=xxx' to work without an explicit separate -driver argument, just like if=virtio. Rip out the legacy handling from the xenpv machine, which was scribbling over any disks configured by the toolstack, and didn't work with anything but raw images. Signed-off-by: David Woodhouse Acked-by: Kevin Wolf Reviewed-by: Paul Durrant --- blockdev.c | 15 +++- hw/block/xen-block.c | 118 ++++++++++++++++++++++++++-- hw/xen/xen_devconfig.c | 28 ------- hw/xenpv/xen_machine_pv.c | 9 --- include/hw/xen/xen-legacy-backend.h | 1 - 5 files changed, 125 insertions(+), 46 deletions(-) diff --git a/blockdev.c b/blockdev.c index 1517dc6210..e9b7e38dc4 100644 --- a/blockdev.c +++ b/blockdev.c @@ -255,13 +255,13 @@ void drive_check_orphaned(void) * Ignore default drives, because we create certain default * drives unconditionally, then leave them unclaimed. Not the * users fault. - * Ignore IF_VIRTIO, because it gets desugared into -device, - * so we can leave failing to -device. + * Ignore IF_VIRTIO or IF_XEN, because it gets desugared into + * -device, so we can leave failing to -device. * Ignore IF_NONE, because leaving unclaimed IF_NONE remains * available for device_add is a feature. */ if (dinfo->is_default || dinfo->type == IF_VIRTIO - || dinfo->type == IF_NONE) { + || dinfo->type == IF_XEN || dinfo->type == IF_NONE) { continue; } if (!blk_get_attached_dev(blk)) { @@ -977,6 +977,15 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type, qemu_opt_set(devopts, "driver", "virtio-blk", &error_abort); qemu_opt_set(devopts, "drive", qdict_get_str(bs_opts, "id"), &error_abort); + } else if (type == IF_XEN) { + QemuOpts *devopts; + devopts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, + &error_abort); + qemu_opt_set(devopts, "driver", + (media == MEDIA_CDROM) ? "xen-cdrom" : "xen-disk", + &error_abort); + qemu_opt_set(devopts, "drive", qdict_get_str(bs_opts, "id"), + &error_abort); } filename = qemu_opt_get(legacy_opts, "file"); diff --git a/hw/block/xen-block.c b/hw/block/xen-block.c index bfa53960c3..6d64ede94f 100644 --- a/hw/block/xen-block.c +++ b/hw/block/xen-block.c @@ -27,13 +27,119 @@ #include "sysemu/block-backend.h" #include "sysemu/iothread.h" #include "dataplane/xen-block.h" +#include "hw/xen/interface/io/xs_wire.h" #include "trace.h" +#define XVDA_MAJOR 202 +#define XVDQ_MAJOR (1 << 20) +#define XVDBGQCV_MAJOR ((1 << 21) - 1) +#define HDA_MAJOR 3 +#define HDC_MAJOR 22 +#define SDA_MAJOR 8 + + +static int vdev_to_diskno(unsigned int vdev_nr) +{ + switch (vdev_nr >> 8) { + case XVDA_MAJOR: + case SDA_MAJOR: + return (vdev_nr >> 4) & 0x15; + + case HDA_MAJOR: + return (vdev_nr >> 6) & 1; + + case HDC_MAJOR: + return ((vdev_nr >> 6) & 1) + 2; + + case XVDQ_MAJOR ... XVDBGQCV_MAJOR: + return (vdev_nr >> 8) & 0xfffff; + + default: + return -1; + } +} + +#define MAX_AUTO_VDEV 4096 + +/* + * Find a free device name in the xvda → xvdfan range and set it in + * blockdev->props.vdev. Our definition of "free" is that there must + * be no other disk or partition with the same disk number. + * + * You are technically permitted to have all of hda, hda1, sda, sda1, + * xvda and xvda1 as *separate* PV block devices with separate backing + * stores. That doesn't make it a good idea. This code will skip xvda + * if *any* of those "conflicting" devices already exists. + * + * The limit of xvdfan (disk 4095) is fairly arbitrary just to avoid a + * stupidly sized bitmap, but Linux as of v6.6 doesn't support anything + * higher than that anyway. + */ +static bool xen_block_find_free_vdev(XenBlockDevice *blockdev, Error **errp) +{ + XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(blockdev))); + unsigned long used_devs[BITS_TO_LONGS(MAX_AUTO_VDEV)]; + XenBlockVdev *vdev = &blockdev->props.vdev; + char fe_path[XENSTORE_ABS_PATH_MAX + 1]; + char **existing_frontends; + unsigned int nr_existing = 0; + unsigned int vdev_nr; + int i, disk = 0; + + snprintf(fe_path, sizeof(fe_path), "/local/domain/%u/device/vbd", + blockdev->xendev.frontend_id); + + existing_frontends = qemu_xen_xs_directory(xenbus->xsh, XBT_NULL, fe_path, + &nr_existing); + if (!existing_frontends && errno != ENOENT) { + error_setg_errno(errp, errno, "cannot read %s", fe_path); + return false; + } + + memset(used_devs, 0, sizeof(used_devs)); + for (i = 0; i < nr_existing; i++) { + if (qemu_strtoui(existing_frontends[i], NULL, 10, &vdev_nr)) { + free(existing_frontends[i]); + continue; + } + + free(existing_frontends[i]); + + disk = vdev_to_diskno(vdev_nr); + if (disk < 0 || disk >= MAX_AUTO_VDEV) { + continue; + } + + set_bit(disk, used_devs); + } + free(existing_frontends); + + disk = find_first_zero_bit(used_devs, MAX_AUTO_VDEV); + if (disk == MAX_AUTO_VDEV) { + error_setg(errp, "cannot find device vdev for block device"); + return false; + } + + vdev->type = XEN_BLOCK_VDEV_TYPE_XVD; + vdev->partition = 0; + vdev->disk = disk; + if (disk < (1 << 4)) { + vdev->number = (XVDA_MAJOR << 8) | (disk << 4); + } else { + vdev->number = (XVDQ_MAJOR << 8) | (disk << 8); + } + return true; +} + static char *xen_block_get_name(XenDevice *xendev, Error **errp) { XenBlockDevice *blockdev = XEN_BLOCK_DEVICE(xendev); XenBlockVdev *vdev = &blockdev->props.vdev; + if (vdev->type == XEN_BLOCK_VDEV_TYPE_INVALID && + !xen_block_find_free_vdev(blockdev, errp)) { + return NULL; + } return g_strdup_printf("%lu", vdev->number); } @@ -482,10 +588,10 @@ static void xen_block_set_vdev(Object *obj, Visitor *v, const char *name, case XEN_BLOCK_VDEV_TYPE_DP: case XEN_BLOCK_VDEV_TYPE_XVD: if (vdev->disk < (1 << 4) && vdev->partition < (1 << 4)) { - vdev->number = (202 << 8) | (vdev->disk << 4) | + vdev->number = (XVDA_MAJOR << 8) | (vdev->disk << 4) | vdev->partition; } else if (vdev->disk < (1 << 20) && vdev->partition < (1 << 8)) { - vdev->number = (1 << 28) | (vdev->disk << 8) | + vdev->number = (XVDQ_MAJOR << 8) | (vdev->disk << 8) | vdev->partition; } else { goto invalid; @@ -495,10 +601,11 @@ static void xen_block_set_vdev(Object *obj, Visitor *v, const char *name, case XEN_BLOCK_VDEV_TYPE_HD: if ((vdev->disk == 0 || vdev->disk == 1) && vdev->partition < (1 << 6)) { - vdev->number = (3 << 8) | (vdev->disk << 6) | vdev->partition; + vdev->number = (HDA_MAJOR << 8) | (vdev->disk << 6) | + vdev->partition; } else if ((vdev->disk == 2 || vdev->disk == 3) && vdev->partition < (1 << 6)) { - vdev->number = (22 << 8) | ((vdev->disk - 2) << 6) | + vdev->number = (HDC_MAJOR << 8) | ((vdev->disk - 2) << 6) | vdev->partition; } else { goto invalid; @@ -507,7 +614,8 @@ static void xen_block_set_vdev(Object *obj, Visitor *v, const char *name, case XEN_BLOCK_VDEV_TYPE_SD: if (vdev->disk < (1 << 4) && vdev->partition < (1 << 4)) { - vdev->number = (8 << 8) | (vdev->disk << 4) | vdev->partition; + vdev->number = (SDA_MAJOR << 8) | (vdev->disk << 4) | + vdev->partition; } else { goto invalid; } diff --git a/hw/xen/xen_devconfig.c b/hw/xen/xen_devconfig.c index 9b7304e544..3f77c675c6 100644 --- a/hw/xen/xen_devconfig.c +++ b/hw/xen/xen_devconfig.c @@ -46,34 +46,6 @@ static int xen_config_dev_all(char *fe, char *be) /* ------------------------------------------------------------- */ -int xen_config_dev_blk(DriveInfo *disk) -{ - char fe[256], be[256], device_name[32]; - int vdev = 202 * 256 + 16 * disk->unit; - int cdrom = disk->media_cd; - const char *devtype = cdrom ? "cdrom" : "disk"; - const char *mode = cdrom ? "r" : "w"; - const char *filename = qemu_opt_get(disk->opts, "file"); - - snprintf(device_name, sizeof(device_name), "xvd%c", 'a' + disk->unit); - xen_pv_printf(NULL, 1, "config disk %d [%s]: %s\n", - disk->unit, device_name, filename); - xen_config_dev_dirs("vbd", "qdisk", vdev, fe, be, sizeof(fe)); - - /* frontend */ - xenstore_write_int(fe, "virtual-device", vdev); - xenstore_write_str(fe, "device-type", devtype); - - /* backend */ - xenstore_write_str(be, "dev", device_name); - xenstore_write_str(be, "type", "file"); - xenstore_write_str(be, "params", filename); - xenstore_write_str(be, "mode", mode); - - /* common stuff */ - return xen_config_dev_all(fe, be); -} - int xen_config_dev_nic(NICInfo *nic) { char fe[256], be[256]; diff --git a/hw/xenpv/xen_machine_pv.c b/hw/xenpv/xen_machine_pv.c index 17cda5ec13..1533f5dfb4 100644 --- a/hw/xenpv/xen_machine_pv.c +++ b/hw/xenpv/xen_machine_pv.c @@ -32,7 +32,6 @@ static void xen_init_pv(MachineState *machine) { - DriveInfo *dinfo; int i; setup_xen_backend_ops(); @@ -64,14 +63,6 @@ static void xen_init_pv(MachineState *machine) vga_interface_created = true; } - /* configure disks */ - for (i = 0; i < 16; i++) { - dinfo = drive_get(IF_XEN, 0, i); - if (!dinfo) - continue; - xen_config_dev_blk(dinfo); - } - /* configure nics */ for (i = 0; i < nb_nics; i++) { if (!nd_table[i].model || 0 != strcmp(nd_table[i].model, "xen")) diff --git a/include/hw/xen/xen-legacy-backend.h b/include/hw/xen/xen-legacy-backend.h index 6c307c5f2c..fc42146bc2 100644 --- a/include/hw/xen/xen-legacy-backend.h +++ b/include/hw/xen/xen-legacy-backend.h @@ -81,7 +81,6 @@ extern struct XenDevOps xen_usb_ops; /* xen-usb.c */ /* configuration (aka xenbus setup) */ void xen_config_cleanup(void); -int xen_config_dev_blk(DriveInfo *disk); int xen_config_dev_nic(NICInfo *nic); int xen_config_dev_vfb(int vdev, const char *type); int xen_config_dev_vkbd(int vdev); From 523b6b3abac928ff3574d8a45df58d29bd8ae454 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sat, 14 Oct 2023 16:53:23 +0100 Subject: [PATCH 731/974] hw/xen: add get_frontend_path() method to XenDeviceClass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The primary Xen console is special. The guest's side is set up for it by the toolstack automatically and not by the standard PV init sequence. Accordingly, its *frontend* doesn't appear in …/device/console/0 either; instead it appears under …/console in the guest's XenStore node. To allow the Xen console driver to override the frontend path for the primary console, add a method to the XenDeviceClass which can be used instead of the standard xen_device_get_frontend_path() Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/xen/xen-bus.c | 11 ++++++++++- include/hw/xen/xen-bus.h | 2 ++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/hw/xen/xen-bus.c b/hw/xen/xen-bus.c index ece8ec40cd..12ff782005 100644 --- a/hw/xen/xen-bus.c +++ b/hw/xen/xen-bus.c @@ -711,8 +711,17 @@ static void xen_device_frontend_create(XenDevice *xendev, Error **errp) { ERRP_GUARD(); XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); + XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev); - xendev->frontend_path = xen_device_get_frontend_path(xendev); + if (xendev_class->get_frontend_path) { + xendev->frontend_path = xendev_class->get_frontend_path(xendev, errp); + if (!xendev->frontend_path) { + error_prepend(errp, "failed to create frontend: "); + return; + } + } else { + xendev->frontend_path = xen_device_get_frontend_path(xendev); + } /* * The frontend area may have already been created by a legacy diff --git a/include/hw/xen/xen-bus.h b/include/hw/xen/xen-bus.h index f435898164..eb440880b5 100644 --- a/include/hw/xen/xen-bus.h +++ b/include/hw/xen/xen-bus.h @@ -33,6 +33,7 @@ struct XenDevice { }; typedef struct XenDevice XenDevice; +typedef char *(*XenDeviceGetFrontendPath)(XenDevice *xendev, Error **errp); typedef char *(*XenDeviceGetName)(XenDevice *xendev, Error **errp); typedef void (*XenDeviceRealize)(XenDevice *xendev, Error **errp); typedef void (*XenDeviceFrontendChanged)(XenDevice *xendev, @@ -46,6 +47,7 @@ struct XenDeviceClass { /*< public >*/ const char *backend; const char *device; + XenDeviceGetFrontendPath get_frontend_path; XenDeviceGetName get_name; XenDeviceRealize realize; XenDeviceFrontendChanged frontend_changed; From eb6ae7a6829385ca41227e878f5ca3d1faf31d52 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 16 Oct 2023 10:28:17 +0100 Subject: [PATCH 732/974] hw/xen: do not repeatedly try to create a failing backend device If xen_backend_device_create() fails to instantiate a device, the XenBus code will just keep trying over and over again each time the bus is re-enumerated, as long as the backend appears online and in XenbusStateInitialising. The only thing which prevents the XenBus code from recreating duplicates of devices which already exist, is the fact that xen_device_realize() sets the backend state to XenbusStateInitWait. If the attempt to create the device doesn't get *that* far, that's when it will keep getting retried. My first thought was to handle errors by setting the backend state to XenbusStateClosed, but that doesn't work for XenConsole which wants to *ignore* any device of type != "ioemu" completely. So, make xen_backend_device_create() *keep* the XenBackendInstance for a failed device, and provide a new xen_backend_exists() function to allow xen_bus_type_enumerate() to check whether one already exists before creating a new one. Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/xen/xen-backend.c | 27 +++++++++++++++++++++------ hw/xen/xen-bus.c | 3 ++- include/hw/xen/xen-backend.h | 1 + 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/hw/xen/xen-backend.c b/hw/xen/xen-backend.c index 5b0fb76eae..b9bf70a9f5 100644 --- a/hw/xen/xen-backend.c +++ b/hw/xen/xen-backend.c @@ -101,6 +101,24 @@ static XenBackendInstance *xen_backend_list_find(XenDevice *xendev) return NULL; } +bool xen_backend_exists(const char *type, const char *name) +{ + const XenBackendImpl *impl = xen_backend_table_lookup(type); + XenBackendInstance *backend; + + if (!impl) { + return false; + } + + QLIST_FOREACH(backend, &backend_list, entry) { + if (backend->impl == impl && !strcmp(backend->name, name)) { + return true; + } + } + + return false; +} + static void xen_backend_list_remove(XenBackendInstance *backend) { QLIST_REMOVE(backend, entry); @@ -122,11 +140,6 @@ void xen_backend_device_create(XenBus *xenbus, const char *type, backend->name = g_strdup(name); impl->create(backend, opts, errp); - if (*errp) { - g_free(backend->name); - g_free(backend); - return; - } backend->impl = impl; xen_backend_list_add(backend); @@ -165,7 +178,9 @@ bool xen_backend_try_device_destroy(XenDevice *xendev, Error **errp) } impl = backend->impl; - impl->destroy(backend, errp); + if (backend->xendev) { + impl->destroy(backend, errp); + } xen_backend_list_remove(backend); g_free(backend->name); diff --git a/hw/xen/xen-bus.c b/hw/xen/xen-bus.c index 12ff782005..3ffd1a5333 100644 --- a/hw/xen/xen-bus.c +++ b/hw/xen/xen-bus.c @@ -209,7 +209,8 @@ static void xen_bus_type_enumerate(XenBus *xenbus, const char *type) NULL, "%u", &online) != 1) online = 0; - if (online && state == XenbusStateInitialising) { + if (online && state == XenbusStateInitialising && + !xen_backend_exists(type, backend[i])) { Error *local_err = NULL; xen_bus_backend_create(xenbus, type, backend[i], backend_path, diff --git a/include/hw/xen/xen-backend.h b/include/hw/xen/xen-backend.h index aac2fd454d..0f01631ae7 100644 --- a/include/hw/xen/xen-backend.h +++ b/include/hw/xen/xen-backend.h @@ -33,6 +33,7 @@ XenDevice *xen_backend_get_device(XenBackendInstance *backend); void xen_backend_register(const XenBackendInfo *info); const char **xen_backend_get_types(unsigned int *nr); +bool xen_backend_exists(const char *type, const char *name); void xen_backend_device_create(XenBus *xenbus, const char *type, const char *name, QDict *opts, Error **errp); bool xen_backend_try_device_destroy(XenDevice *xendev, Error **errp); From 9b773746908072bf0ec64aec12d02515ebcd717b Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 17 Oct 2023 22:20:28 +0100 Subject: [PATCH 733/974] hw/xen: update Xen console to XenDevice model This allows (non-primary) console devices to be created on the command line and hotplugged. Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/char/trace-events | 8 + hw/char/xen_console.c | 538 +++++++++++++++++++++++++++--------- hw/xen/xen-legacy-backend.c | 1 - 3 files changed, 414 insertions(+), 133 deletions(-) diff --git a/hw/char/trace-events b/hw/char/trace-events index babf4d35ea..7a398c82a5 100644 --- a/hw/char/trace-events +++ b/hw/char/trace-events @@ -105,3 +105,11 @@ cadence_uart_baudrate(unsigned baudrate) "baudrate %u" # sh_serial.c sh_serial_read(char *id, unsigned size, uint64_t offs, uint64_t val) " %s size %d offs 0x%02" PRIx64 " -> 0x%02" PRIx64 sh_serial_write(char *id, unsigned size, uint64_t offs, uint64_t val) "%s size %d offs 0x%02" PRIx64 " <- 0x%02" PRIx64 + +# xen_console.c +xen_console_connect(unsigned int idx, unsigned int ring_ref, unsigned int port, unsigned int limit) "idx %u ring_ref %u port %u limit %u" +xen_console_disconnect(unsigned int idx) "idx %u" +xen_console_unrealize(unsigned int idx) "idx %u" +xen_console_realize(unsigned int idx, const char *chrdev) "idx %u chrdev %s" +xen_console_device_create(unsigned int idx) "idx %u" +xen_console_device_destroy(unsigned int idx) "idx %u" diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c index 810dae3f44..4a419dc287 100644 --- a/hw/char/xen_console.c +++ b/hw/char/xen_console.c @@ -20,15 +20,20 @@ */ #include "qemu/osdep.h" +#include "qemu/cutils.h" #include #include #include "qapi/error.h" #include "sysemu/sysemu.h" #include "chardev/char-fe.h" -#include "hw/xen/xen-legacy-backend.h" - +#include "hw/xen/xen-backend.h" +#include "hw/xen/xen-bus-helper.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" #include "hw/xen/interface/io/console.h" +#include "hw/xen/interface/io/xs_wire.h" +#include "trace.h" struct buffer { uint8_t *data; @@ -39,16 +44,22 @@ struct buffer { }; struct XenConsole { - struct XenLegacyDevice xendev; /* must be first */ + struct XenDevice xendev; /* must be first */ + XenEventChannel *event_channel; + int dev; struct buffer buffer; - char console[XEN_BUFSIZE]; - int ring_ref; + char *fe_path; + unsigned int ring_ref; void *sring; CharBackend chr; int backlog; }; +typedef struct XenConsole XenConsole; -static void buffer_append(struct XenConsole *con) +#define TYPE_XEN_CONSOLE_DEVICE "xen-console" +OBJECT_DECLARE_SIMPLE_TYPE(XenConsole, XEN_CONSOLE_DEVICE) + +static bool buffer_append(XenConsole *con) { struct buffer *buffer = &con->buffer; XENCONS_RING_IDX cons, prod, size; @@ -60,7 +71,7 @@ static void buffer_append(struct XenConsole *con) size = prod - cons; if ((size == 0) || (size > sizeof(intf->out))) - return; + return false; if ((buffer->capacity - buffer->size) < size) { buffer->capacity += (size + 1024); @@ -73,7 +84,7 @@ static void buffer_append(struct XenConsole *con) xen_mb(); intf->out_cons = cons; - xen_pv_send_notify(&con->xendev); + xen_device_notify_event_channel(XEN_DEVICE(con), con->event_channel, NULL); if (buffer->max_capacity && buffer->size > buffer->max_capacity) { @@ -89,6 +100,7 @@ static void buffer_append(struct XenConsole *con) if (buffer->consumed > buffer->max_capacity - over) buffer->consumed = buffer->max_capacity - over; } + return true; } static void buffer_advance(struct buffer *buffer, size_t len) @@ -100,7 +112,7 @@ static void buffer_advance(struct buffer *buffer, size_t len) } } -static int ring_free_bytes(struct XenConsole *con) +static int ring_free_bytes(XenConsole *con) { struct xencons_interface *intf = con->sring; XENCONS_RING_IDX cons, prod, space; @@ -118,13 +130,13 @@ static int ring_free_bytes(struct XenConsole *con) static int xencons_can_receive(void *opaque) { - struct XenConsole *con = opaque; + XenConsole *con = opaque; return ring_free_bytes(con); } static void xencons_receive(void *opaque, const uint8_t *buf, int len) { - struct XenConsole *con = opaque; + XenConsole *con = opaque; struct xencons_interface *intf = con->sring; XENCONS_RING_IDX prod; int i, max; @@ -141,10 +153,10 @@ static void xencons_receive(void *opaque, const uint8_t *buf, int len) } xen_wmb(); intf->in_prod = prod; - xen_pv_send_notify(&con->xendev); + xen_device_notify_event_channel(XEN_DEVICE(con), con->event_channel, NULL); } -static void xencons_send(struct XenConsole *con) +static bool xencons_send(XenConsole *con) { ssize_t len, size; @@ -159,174 +171,436 @@ static void xencons_send(struct XenConsole *con) if (len < 1) { if (!con->backlog) { con->backlog = 1; - xen_pv_printf(&con->xendev, 1, - "backlog piling up, nobody listening?\n"); } } else { buffer_advance(&con->buffer, len); if (con->backlog && len == size) { con->backlog = 0; - xen_pv_printf(&con->xendev, 1, "backlog is gone\n"); } } + return len > 0; } /* -------------------------------------------------------------------- */ -static int store_con_info(struct XenConsole *con) +static bool con_event(void *_xendev) { - Chardev *cs = qemu_chr_fe_get_driver(&con->chr); - char *pts = NULL; - char *dom_path; - g_autoptr(GString) path = NULL; + XenConsole *con = XEN_CONSOLE_DEVICE(_xendev); + bool done_something; - /* Only continue if we're talking to a pty. */ - if (!CHARDEV_IS_PTY(cs)) { - return 0; - } - pts = cs->filename + 4; - - dom_path = qemu_xen_xs_get_domain_path(xenstore, xen_domid); - if (!dom_path) { - return 0; + if (xen_device_backend_get_state(&con->xendev) != XenbusStateConnected) { + return false; } - path = g_string_new(dom_path); - free(dom_path); + done_something = buffer_append(con); - if (con->xendev.dev) { - g_string_append_printf(path, "/device/console/%d", con->xendev.dev); - } else { - g_string_append(path, "/console"); + if (con->buffer.size - con->buffer.consumed) { + done_something |= xencons_send(con); } - g_string_append(path, "/tty"); - - if (xenstore_write_str(con->console, path->str, pts)) { - fprintf(stderr, "xenstore_write_str for '%s' fail", path->str); - return -1; - } - return 0; + return done_something; } -static int con_init(struct XenLegacyDevice *xendev) +/* -------------------------------------------------------------------- */ + +static bool xen_console_connect(XenDevice *xendev, Error **errp) { - struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); - char *type, *dom, label[32]; - int ret = 0; - const char *output; + XenConsole *con = XEN_CONSOLE_DEVICE(xendev); + unsigned int port, limit; - /* setup */ - dom = qemu_xen_xs_get_domain_path(xenstore, con->xendev.dom); - if (!xendev->dev) { - snprintf(con->console, sizeof(con->console), "%s/console", dom); - } else { - snprintf(con->console, sizeof(con->console), "%s/device/console/%d", dom, xendev->dev); - } - free(dom); - - type = xenstore_read_str(con->console, "type"); - if (!type || strcmp(type, "ioemu") != 0) { - xen_pv_printf(xendev, 1, "not for me (type=%s)\n", type); - ret = -1; - goto out; + if (xen_device_frontend_scanf(xendev, "ring-ref", "%u", + &con->ring_ref) != 1) { + error_setg(errp, "failed to read ring-ref"); + return false; } - output = xenstore_read_str(con->console, "output"); - - /* no Xen override, use qemu output device */ - if (output == NULL) { - if (con->xendev.dev) { - qemu_chr_fe_init(&con->chr, serial_hd(con->xendev.dev), - &error_abort); - } - } else { - snprintf(label, sizeof(label), "xencons%d", con->xendev.dev); - qemu_chr_fe_init(&con->chr, - /* - * FIXME: sure we want to support implicit - * muxed monitors here? - */ - qemu_chr_new_mux_mon(label, output, NULL), - &error_abort); + if (xen_device_frontend_scanf(xendev, "port", "%u", &port) != 1) { + error_setg(errp, "failed to read remote port"); + return false; } - store_con_info(con); - -out: - g_free(type); - return ret; -} - -static int con_initialise(struct XenLegacyDevice *xendev) -{ - struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); - int limit; - - if (xenstore_read_int(con->console, "ring-ref", &con->ring_ref) == -1) - return -1; - if (xenstore_read_int(con->console, "port", &con->xendev.remote_port) == -1) - return -1; - if (xenstore_read_int(con->console, "limit", &limit) == 0) + if (xen_device_frontend_scanf(xendev, "limit", "%u", &limit) == 1) { con->buffer.max_capacity = limit; + } - if (!xendev->dev) { - xen_pfn_t mfn = con->ring_ref; - con->sring = qemu_xen_foreignmem_map(con->xendev.dom, NULL, + con->event_channel = xen_device_bind_event_channel(xendev, port, + con_event, + con, + errp); + if (!con->event_channel) { + return false; + } + + if (!con->dev) { + xen_pfn_t mfn = (xen_pfn_t)con->ring_ref; + con->sring = qemu_xen_foreignmem_map(xendev->frontend_id, NULL, PROT_READ | PROT_WRITE, 1, &mfn, NULL); + if (!con->sring) { + error_setg(errp, "failed to map console page"); + return false; + } } else { - con->sring = xen_be_map_grant_ref(xendev, con->ring_ref, - PROT_READ | PROT_WRITE); + con->sring = xen_device_map_grant_refs(xendev, + &con->ring_ref, 1, + PROT_READ | PROT_WRITE, + errp); + if (!con->sring) { + error_prepend(errp, "failed to map grant ref: "); + return false; + } } - if (!con->sring) - return -1; - xen_be_bind_evtchn(&con->xendev); + trace_xen_console_connect(con->dev, con->ring_ref, port, + con->buffer.max_capacity); + qemu_chr_fe_set_handlers(&con->chr, xencons_can_receive, - xencons_receive, NULL, NULL, con, NULL, true); - - xen_pv_printf(xendev, 1, - "ring mfn %d, remote port %d, local port %d, limit %zd\n", - con->ring_ref, - con->xendev.remote_port, - con->xendev.local_port, - con->buffer.max_capacity); - return 0; + xencons_receive, NULL, NULL, con, NULL, + true); + return true; } -static void con_disconnect(struct XenLegacyDevice *xendev) +static void xen_console_disconnect(XenDevice *xendev, Error **errp) { - struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); + XenConsole *con = XEN_CONSOLE_DEVICE(xendev); - qemu_chr_fe_deinit(&con->chr, false); - xen_pv_unbind_evtchn(&con->xendev); + trace_xen_console_disconnect(con->dev); + + qemu_chr_fe_set_handlers(&con->chr, NULL, NULL, NULL, NULL, + con, NULL, true); + + if (con->event_channel) { + xen_device_unbind_event_channel(xendev, con->event_channel, + errp); + con->event_channel = NULL; + } if (con->sring) { - if (!xendev->dev) { + if (!con->dev) { qemu_xen_foreignmem_unmap(con->sring, 1); } else { - xen_be_unmap_grant_ref(xendev, con->sring, con->ring_ref); + xen_device_unmap_grant_refs(xendev, con->sring, + &con->ring_ref, 1, errp); } con->sring = NULL; } } -static void con_event(struct XenLegacyDevice *xendev) +static void xen_console_frontend_changed(XenDevice *xendev, + enum xenbus_state frontend_state, + Error **errp) { - struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); + ERRP_GUARD(); + enum xenbus_state backend_state = xen_device_backend_get_state(xendev); - buffer_append(con); - if (con->buffer.size - con->buffer.consumed) - xencons_send(con); + switch (frontend_state) { + case XenbusStateInitialised: + case XenbusStateConnected: + if (backend_state == XenbusStateConnected) { + break; + } + + xen_console_disconnect(xendev, errp); + if (*errp) { + break; + } + + if (!xen_console_connect(xendev, errp)) { + xen_device_backend_set_state(xendev, XenbusStateClosing); + break; + } + + xen_device_backend_set_state(xendev, XenbusStateConnected); + break; + + case XenbusStateClosing: + xen_device_backend_set_state(xendev, XenbusStateClosing); + break; + + case XenbusStateClosed: + case XenbusStateUnknown: + xen_console_disconnect(xendev, errp); + if (*errp) { + break; + } + + xen_device_backend_set_state(xendev, XenbusStateClosed); + break; + + default: + break; + } } -/* -------------------------------------------------------------------- */ +static char *xen_console_get_name(XenDevice *xendev, Error **errp) +{ + XenConsole *con = XEN_CONSOLE_DEVICE(xendev); -struct XenDevOps xen_console_ops = { - .size = sizeof(struct XenConsole), - .flags = DEVOPS_FLAG_IGNORE_STATE|DEVOPS_FLAG_NEED_GNTDEV, - .init = con_init, - .initialise = con_initialise, - .event = con_event, - .disconnect = con_disconnect, + if (con->dev == -1) { + XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); + char fe_path[XENSTORE_ABS_PATH_MAX + 1]; + char *value; + int idx = 1; + + /* Theoretically we could go up to INT_MAX here but that's overkill */ + while (idx < 100) { + snprintf(fe_path, sizeof(fe_path), + "/local/domain/%u/device/console/%u", + xendev->frontend_id, idx); + value = qemu_xen_xs_read(xenbus->xsh, XBT_NULL, fe_path, NULL); + if (!value) { + if (errno == ENOENT) { + con->dev = idx; + goto found; + } + error_setg(errp, "cannot read %s: %s", fe_path, + strerror(errno)); + return NULL; + } + free(value); + idx++; + } + error_setg(errp, "cannot find device index for console device"); + return NULL; + } + found: + return g_strdup_printf("%u", con->dev); +} + +static void xen_console_unrealize(XenDevice *xendev) +{ + XenConsole *con = XEN_CONSOLE_DEVICE(xendev); + + trace_xen_console_unrealize(con->dev); + + /* Disconnect from the frontend in case this has not already happened */ + xen_console_disconnect(xendev, NULL); + + qemu_chr_fe_deinit(&con->chr, false); +} + +static void xen_console_realize(XenDevice *xendev, Error **errp) +{ + ERRP_GUARD(); + XenConsole *con = XEN_CONSOLE_DEVICE(xendev); + Chardev *cs = qemu_chr_fe_get_driver(&con->chr); + unsigned int u; + + if (!cs) { + error_setg(errp, "no backing character device"); + return; + } + + if (con->dev == -1) { + error_setg(errp, "no device index provided"); + return; + } + + /* + * The Xen primary console is special. The ring-ref is actually a GFN to + * be mapped directly as foreignmem (not a grant ref), and the guest port + * was allocated *for* the guest by the toolstack. The guest gets these + * through HVMOP_get_param and can use the console long before it's got + * XenStore up and running. We cannot create those for a Xen guest. + */ + if (!con->dev) { + if (xen_device_frontend_scanf(xendev, "ring-ref", "%u", &u) != 1 || + xen_device_frontend_scanf(xendev, "port", "%u", &u) != 1) { + error_setg(errp, "cannot create primary Xen console"); + return; + } + } + + trace_xen_console_realize(con->dev, object_get_typename(OBJECT(cs))); + + if (CHARDEV_IS_PTY(cs)) { + /* Strip the leading 'pty:' */ + xen_device_frontend_printf(xendev, "tty", "%s", cs->filename + 4); + } + + /* No normal PV driver initialization for the primary console */ + if (!con->dev) { + xen_console_connect(xendev, errp); + } +} + +static char *console_frontend_path(struct qemu_xs_handle *xenstore, + unsigned int dom_id, unsigned int dev) +{ + if (!dev) { + return g_strdup_printf("/local/domain/%u/console", dom_id); + } else { + return g_strdup_printf("/local/domain/%u/device/console/%u", dom_id, + dev); + } +} + +static char *xen_console_get_frontend_path(XenDevice *xendev, Error **errp) +{ + XenConsole *con = XEN_CONSOLE_DEVICE(xendev); + XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); + char *ret = console_frontend_path(xenbus->xsh, xendev->frontend_id, + con->dev); + + if (!ret) { + error_setg(errp, "failed to create frontend path"); + } + return ret; +} + + +static Property xen_console_properties[] = { + DEFINE_PROP_CHR("chardev", XenConsole, chr), + DEFINE_PROP_INT32("idx", XenConsole, dev, -1), + DEFINE_PROP_END_OF_LIST(), }; + +static void xen_console_class_init(ObjectClass *class, void *data) +{ + DeviceClass *dev_class = DEVICE_CLASS(class); + XenDeviceClass *xendev_class = XEN_DEVICE_CLASS(class); + + xendev_class->backend = "console"; + xendev_class->device = "console"; + xendev_class->get_name = xen_console_get_name; + xendev_class->realize = xen_console_realize; + xendev_class->frontend_changed = xen_console_frontend_changed; + xendev_class->unrealize = xen_console_unrealize; + xendev_class->get_frontend_path = xen_console_get_frontend_path; + + device_class_set_props(dev_class, xen_console_properties); +} + +static const TypeInfo xen_console_type_info = { + .name = TYPE_XEN_CONSOLE_DEVICE, + .parent = TYPE_XEN_DEVICE, + .instance_size = sizeof(XenConsole), + .class_init = xen_console_class_init, +}; + +static void xen_console_register_types(void) +{ + type_register_static(&xen_console_type_info); +} + +type_init(xen_console_register_types) + +/* Called to instantiate a XenConsole when the backend is detected. */ +static void xen_console_device_create(XenBackendInstance *backend, + QDict *opts, Error **errp) +{ + ERRP_GUARD(); + XenBus *xenbus = xen_backend_get_bus(backend); + const char *name = xen_backend_get_name(backend); + unsigned long number; + char *fe = NULL, *type = NULL, *output = NULL; + char label[32]; + XenDevice *xendev = NULL; + XenConsole *con; + Chardev *cd = NULL; + struct qemu_xs_handle *xsh = xenbus->xsh; + + if (qemu_strtoul(name, NULL, 10, &number) || number > INT_MAX) { + error_setg(errp, "failed to parse name '%s'", name); + goto fail; + } + + trace_xen_console_device_create(number); + + fe = console_frontend_path(xsh, xen_domid, number); + if (fe == NULL) { + error_setg(errp, "failed to generate frontend path"); + goto fail; + } + + if (xs_node_scanf(xsh, XBT_NULL, fe, "type", errp, "%ms", &type) != 1) { + error_prepend(errp, "failed to read console device type: "); + goto fail; + } + + if (strcmp(type, "ioemu")) { + error_setg(errp, "declining to handle console type '%s'", + type); + goto fail; + } + + xendev = XEN_DEVICE(qdev_new(TYPE_XEN_CONSOLE_DEVICE)); + con = XEN_CONSOLE_DEVICE(xendev); + + con->dev = number; + + snprintf(label, sizeof(label), "xencons%ld", number); + + if (xs_node_scanf(xsh, XBT_NULL, fe, "output", NULL, "%ms", &output) == 1) { + /* + * FIXME: sure we want to support implicit + * muxed monitors here? + */ + cd = qemu_chr_new_mux_mon(label, output, NULL); + if (!cd) { + error_setg(errp, "console: No valid chardev found at '%s': ", + output); + goto fail; + } + } else if (number) { + cd = serial_hd(number); + if (!cd) { + error_prepend(errp, "console: No serial device #%ld found: ", + number); + goto fail; + } + } else { + /* No 'output' node on primary console: use null. */ + cd = qemu_chr_new(label, "null", NULL); + if (!cd) { + error_setg(errp, "console: failed to create null device"); + goto fail; + } + } + + if (!qemu_chr_fe_init(&con->chr, cd, errp)) { + error_prepend(errp, "console: failed to initialize backing chardev: "); + goto fail; + } + + if (qdev_realize_and_unref(DEVICE(xendev), BUS(xenbus), errp)) { + xen_backend_set_device(backend, xendev); + goto done; + } + + error_prepend(errp, "realization of console device %lu failed: ", + number); + + fail: + if (xendev) { + object_unparent(OBJECT(xendev)); + } + done: + g_free(fe); + free(type); + free(output); +} + +static void xen_console_device_destroy(XenBackendInstance *backend, + Error **errp) +{ + ERRP_GUARD(); + XenDevice *xendev = xen_backend_get_device(backend); + XenConsole *con = XEN_CONSOLE_DEVICE(xendev); + + trace_xen_console_device_destroy(con->dev); + + object_unparent(OBJECT(xendev)); +} + +static const XenBackendInfo xen_console_backend_info = { + .type = "console", + .create = xen_console_device_create, + .destroy = xen_console_device_destroy, +}; + +static void xen_console_register_backend(void) +{ + xen_backend_register(&xen_console_backend_info); +} + +xen_backend_init(xen_console_register_backend); diff --git a/hw/xen/xen-legacy-backend.c b/hw/xen/xen-legacy-backend.c index 4ded3cec23..124dd5f3d6 100644 --- a/hw/xen/xen-legacy-backend.c +++ b/hw/xen/xen-legacy-backend.c @@ -623,7 +623,6 @@ void xen_be_init(void) xen_set_dynamic_sysbus(); - xen_be_register("console", &xen_console_ops); xen_be_register("vkbd", &xen_kbdmouse_ops); #ifdef CONFIG_VIRTFS xen_be_register("9pfs", &xen_9pfs_ops); From a72ccc7fc449748d79914439ddf5c12a6e9fd6a6 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 16 Oct 2023 16:00:23 +0100 Subject: [PATCH 734/974] hw/xen: add support for Xen primary console in emulated mode The primary console is special because the toolstack maps a page into the guest for its ring, and also allocates the guest-side event channel. The guest's grant table is even primed to export that page using a known grant ref#. Add support for all that in emulated mode, so that we can have a primary console. For reasons unclear, the backends running under real Xen don't just use a mapping of the well-known GNTTAB_RESERVED_CONSOLE grant ref (which would also be in the ring-ref node in XenStore). Instead, the toolstack sets the ring-ref node of the primary console to the GFN of the guest page. The backend is expected to handle that special case and map it with foreignmem operations instead. We don't have an implementation of foreignmem ops for emulated Xen mode, so just make it map GNTTAB_RESERVED_CONSOLE instead. This would probably work for real Xen too, but we can't work out how to make real Xen create a primary console of type "ioemu" to make QEMU drive it, so we can't test that; might as well leave it as it is for now under Xen. Now at last we can boot the Xen PV shim and run PV kernels in QEMU. Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/char/xen_console.c | 78 ++++++++---- hw/i386/kvm/meson.build | 1 + hw/i386/kvm/trace-events | 2 + hw/i386/kvm/xen-stubs.c | 8 ++ hw/i386/kvm/xen_gnttab.c | 7 +- hw/i386/kvm/xen_primary_console.c | 193 ++++++++++++++++++++++++++++++ hw/i386/kvm/xen_primary_console.h | 23 ++++ hw/i386/kvm/xen_xenstore.c | 10 ++ hw/xen/xen-bus.c | 5 + include/hw/xen/xen-bus.h | 1 + target/i386/kvm/xen-emu.c | 23 +++- 11 files changed, 328 insertions(+), 23 deletions(-) create mode 100644 hw/i386/kvm/xen_primary_console.c create mode 100644 hw/i386/kvm/xen_primary_console.h diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c index 4a419dc287..5cbee2f184 100644 --- a/hw/char/xen_console.c +++ b/hw/char/xen_console.c @@ -33,6 +33,8 @@ #include "hw/qdev-properties-system.h" #include "hw/xen/interface/io/console.h" #include "hw/xen/interface/io/xs_wire.h" +#include "hw/xen/interface/grant_table.h" +#include "hw/i386/kvm/xen_primary_console.h" #include "trace.h" struct buffer { @@ -230,24 +232,47 @@ static bool xen_console_connect(XenDevice *xendev, Error **errp) return false; } - if (!con->dev) { - xen_pfn_t mfn = (xen_pfn_t)con->ring_ref; - con->sring = qemu_xen_foreignmem_map(xendev->frontend_id, NULL, - PROT_READ | PROT_WRITE, - 1, &mfn, NULL); - if (!con->sring) { - error_setg(errp, "failed to map console page"); - return false; + switch (con->dev) { + case 0: + /* + * The primary console is special. For real Xen the ring-ref is + * actually a GFN which needs to be mapped as foreignmem. + */ + if (xen_mode != XEN_EMULATE) { + xen_pfn_t mfn = (xen_pfn_t)con->ring_ref; + con->sring = qemu_xen_foreignmem_map(xendev->frontend_id, NULL, + PROT_READ | PROT_WRITE, + 1, &mfn, NULL); + if (!con->sring) { + error_setg(errp, "failed to map console page"); + return false; + } + break; } - } else { + + /* + * For Xen emulation, we still follow the convention of ring-ref + * holding the GFN, but we map the fixed GNTTAB_RESERVED_CONSOLE + * grant ref because there is no implementation of foreignmem + * operations for emulated mode. The emulation code which handles + * the guest-side page and event channel also needs to be informed + * of the backend event channel port, in order to reconnect to it + * after a soft reset. + */ + xen_primary_console_set_be_port( + xen_event_channel_get_local_port(con->event_channel)); + con->ring_ref = GNTTAB_RESERVED_CONSOLE; + /* fallthrough */ + default: con->sring = xen_device_map_grant_refs(xendev, &con->ring_ref, 1, PROT_READ | PROT_WRITE, errp); if (!con->sring) { - error_prepend(errp, "failed to map grant ref: "); + error_prepend(errp, "failed to map console grant ref: "); return false; } + break; } trace_xen_console_connect(con->dev, con->ring_ref, port, @@ -272,10 +297,14 @@ static void xen_console_disconnect(XenDevice *xendev, Error **errp) xen_device_unbind_event_channel(xendev, con->event_channel, errp); con->event_channel = NULL; + + if (xen_mode == XEN_EMULATE && !con->dev) { + xen_primary_console_set_be_port(0); + } } if (con->sring) { - if (!con->dev) { + if (!con->dev && xen_mode != XEN_EMULATE) { qemu_xen_foreignmem_unmap(con->sring, 1); } else { xen_device_unmap_grant_refs(xendev, con->sring, @@ -338,14 +367,19 @@ static char *xen_console_get_name(XenDevice *xendev, Error **errp) if (con->dev == -1) { XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); char fe_path[XENSTORE_ABS_PATH_MAX + 1]; + int idx = (xen_mode == XEN_EMULATE) ? 0 : 1; char *value; - int idx = 1; /* Theoretically we could go up to INT_MAX here but that's overkill */ while (idx < 100) { - snprintf(fe_path, sizeof(fe_path), - "/local/domain/%u/device/console/%u", - xendev->frontend_id, idx); + if (!idx) { + snprintf(fe_path, sizeof(fe_path), + "/local/domain/%u/console", xendev->frontend_id); + } else { + snprintf(fe_path, sizeof(fe_path), + "/local/domain/%u/device/console/%u", + xendev->frontend_id, idx); + } value = qemu_xen_xs_read(xenbus->xsh, XBT_NULL, fe_path, NULL); if (!value) { if (errno == ENOENT) { @@ -400,11 +434,15 @@ static void xen_console_realize(XenDevice *xendev, Error **errp) * be mapped directly as foreignmem (not a grant ref), and the guest port * was allocated *for* the guest by the toolstack. The guest gets these * through HVMOP_get_param and can use the console long before it's got - * XenStore up and running. We cannot create those for a Xen guest. + * XenStore up and running. We cannot create those for a true Xen guest, + * but we can for Xen emulation. */ if (!con->dev) { - if (xen_device_frontend_scanf(xendev, "ring-ref", "%u", &u) != 1 || - xen_device_frontend_scanf(xendev, "port", "%u", &u) != 1) { + if (xen_mode == XEN_EMULATE) { + xen_primary_console_create(); + } else if (xen_device_frontend_scanf(xendev, "ring-ref", "%u", &u) + != 1 || + xen_device_frontend_scanf(xendev, "port", "%u", &u) != 1) { error_setg(errp, "cannot create primary Xen console"); return; } @@ -417,8 +455,8 @@ static void xen_console_realize(XenDevice *xendev, Error **errp) xen_device_frontend_printf(xendev, "tty", "%s", cs->filename + 4); } - /* No normal PV driver initialization for the primary console */ - if (!con->dev) { + /* No normal PV driver initialization for the primary console under Xen */ + if (!con->dev && xen_mode != XEN_EMULATE) { xen_console_connect(xendev, errp); } } diff --git a/hw/i386/kvm/meson.build b/hw/i386/kvm/meson.build index ab143d6474..a4a2e23c06 100644 --- a/hw/i386/kvm/meson.build +++ b/hw/i386/kvm/meson.build @@ -9,6 +9,7 @@ i386_kvm_ss.add(when: 'CONFIG_XEN_EMU', if_true: files( 'xen_evtchn.c', 'xen_gnttab.c', 'xen_xenstore.c', + 'xen_primary_console.c', 'xenstore_impl.c', )) diff --git a/hw/i386/kvm/trace-events b/hw/i386/kvm/trace-events index e4c82de6f3..67bf7f174e 100644 --- a/hw/i386/kvm/trace-events +++ b/hw/i386/kvm/trace-events @@ -18,3 +18,5 @@ xenstore_watch(const char *path, const char *token) "path %s token %s" xenstore_unwatch(const char *path, const char *token) "path %s token %s" xenstore_reset_watches(void) "" xenstore_watch_event(const char *path, const char *token) "path %s token %s" +xen_primary_console_create(void) "" +xen_primary_console_reset(int port) "port %u" diff --git a/hw/i386/kvm/xen-stubs.c b/hw/i386/kvm/xen-stubs.c index ae406e0b02..d03131e686 100644 --- a/hw/i386/kvm/xen-stubs.c +++ b/hw/i386/kvm/xen-stubs.c @@ -15,6 +15,7 @@ #include "qapi/qapi-commands-misc-target.h" #include "xen_evtchn.h" +#include "xen_primary_console.h" void xen_evtchn_snoop_msi(PCIDevice *dev, bool is_msix, unsigned int vector, uint64_t addr, uint32_t data, bool is_masked) @@ -30,6 +31,13 @@ bool xen_evtchn_deliver_pirq_msi(uint64_t address, uint32_t data) return false; } +void xen_primary_console_create(void) +{ +} + +void xen_primary_console_set_be_port(uint16_t port) +{ +} #ifdef TARGET_I386 EvtchnInfoList *qmp_xen_event_list(Error **errp) { diff --git a/hw/i386/kvm/xen_gnttab.c b/hw/i386/kvm/xen_gnttab.c index 839ec920a1..0a24f53f20 100644 --- a/hw/i386/kvm/xen_gnttab.c +++ b/hw/i386/kvm/xen_gnttab.c @@ -25,6 +25,7 @@ #include "hw/xen/xen_backend_ops.h" #include "xen_overlay.h" #include "xen_gnttab.h" +#include "xen_primary_console.h" #include "sysemu/kvm.h" #include "sysemu/kvm_xen.h" @@ -537,9 +538,13 @@ int xen_gnttab_reset(void) s->nr_frames = 0; memset(s->entries.v1, 0, XEN_PAGE_SIZE * s->max_frames); - s->entries.v1[GNTTAB_RESERVED_XENSTORE].flags = GTF_permit_access; s->entries.v1[GNTTAB_RESERVED_XENSTORE].frame = XEN_SPECIAL_PFN(XENSTORE); + if (xen_primary_console_get_pfn()) { + s->entries.v1[GNTTAB_RESERVED_CONSOLE].flags = GTF_permit_access; + s->entries.v1[GNTTAB_RESERVED_CONSOLE].frame = XEN_SPECIAL_PFN(CONSOLE); + } + return 0; } diff --git a/hw/i386/kvm/xen_primary_console.c b/hw/i386/kvm/xen_primary_console.c new file mode 100644 index 0000000000..abe79f565b --- /dev/null +++ b/hw/i386/kvm/xen_primary_console.c @@ -0,0 +1,193 @@ +/* + * QEMU Xen emulation: Primary console support + * + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Authors: David Woodhouse + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "qapi/error.h" + +#include "hw/sysbus.h" +#include "hw/xen/xen.h" +#include "hw/xen/xen_backend_ops.h" +#include "xen_evtchn.h" +#include "xen_overlay.h" +#include "xen_primary_console.h" + +#include "sysemu/kvm.h" +#include "sysemu/kvm_xen.h" + +#include "trace.h" + +#include "hw/xen/interface/event_channel.h" +#include "hw/xen/interface/grant_table.h" + +#define TYPE_XEN_PRIMARY_CONSOLE "xen-primary-console" +OBJECT_DECLARE_SIMPLE_TYPE(XenPrimaryConsoleState, XEN_PRIMARY_CONSOLE) + +struct XenPrimaryConsoleState { + /*< private >*/ + SysBusDevice busdev; + /*< public >*/ + + MemoryRegion console_page; + void *cp; + + evtchn_port_t guest_port; + evtchn_port_t be_port; + + struct xengntdev_handle *gt; + void *granted_xs; +}; + +struct XenPrimaryConsoleState *xen_primary_console_singleton; + +static void xen_primary_console_realize(DeviceState *dev, Error **errp) +{ + XenPrimaryConsoleState *s = XEN_PRIMARY_CONSOLE(dev); + + if (xen_mode != XEN_EMULATE) { + error_setg(errp, "Xen primary console support is for Xen emulation"); + return; + } + + memory_region_init_ram(&s->console_page, OBJECT(dev), "xen:console_page", + XEN_PAGE_SIZE, &error_abort); + memory_region_set_enabled(&s->console_page, true); + s->cp = memory_region_get_ram_ptr(&s->console_page); + memset(s->cp, 0, XEN_PAGE_SIZE); + + /* We can't map it this early as KVM isn't ready */ + xen_primary_console_singleton = s; +} + +static void xen_primary_console_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = xen_primary_console_realize; +} + +static const TypeInfo xen_primary_console_info = { + .name = TYPE_XEN_PRIMARY_CONSOLE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XenPrimaryConsoleState), + .class_init = xen_primary_console_class_init, +}; + + +void xen_primary_console_create(void) +{ + DeviceState *dev = sysbus_create_simple(TYPE_XEN_PRIMARY_CONSOLE, -1, NULL); + + trace_xen_primary_console_create(); + + xen_primary_console_singleton = XEN_PRIMARY_CONSOLE(dev); + + /* + * Defer the init (xen_primary_console_reset()) until KVM is set up and the + * overlay page can be mapped. + */ +} + +static void xen_primary_console_register_types(void) +{ + type_register_static(&xen_primary_console_info); +} + +type_init(xen_primary_console_register_types) + +uint16_t xen_primary_console_get_port(void) +{ + XenPrimaryConsoleState *s = xen_primary_console_singleton; + if (!s) { + return 0; + } + return s->guest_port; +} + +void xen_primary_console_set_be_port(uint16_t port) +{ + XenPrimaryConsoleState *s = xen_primary_console_singleton; + if (s) { + s->be_port = port; + } +} + +uint64_t xen_primary_console_get_pfn(void) +{ + XenPrimaryConsoleState *s = xen_primary_console_singleton; + if (!s) { + return 0; + } + return XEN_SPECIAL_PFN(CONSOLE); +} + +void *xen_primary_console_get_map(void) +{ + XenPrimaryConsoleState *s = xen_primary_console_singleton; + if (!s) { + return 0; + } + return s->cp; +} + +static void alloc_guest_port(XenPrimaryConsoleState *s) +{ + struct evtchn_alloc_unbound alloc = { + .dom = DOMID_SELF, + .remote_dom = DOMID_QEMU, + }; + + if (!xen_evtchn_alloc_unbound_op(&alloc)) { + s->guest_port = alloc.port; + } +} + +static void rebind_guest_port(XenPrimaryConsoleState *s) +{ + struct evtchn_bind_interdomain inter = { + .remote_dom = DOMID_QEMU, + .remote_port = s->be_port, + }; + + if (!xen_evtchn_bind_interdomain_op(&inter)) { + s->guest_port = inter.local_port; + } + + s->be_port = 0; +} + +int xen_primary_console_reset(void) +{ + XenPrimaryConsoleState *s = xen_primary_console_singleton; + if (!s) { + return 0; + } + + if (!memory_region_is_mapped(&s->console_page)) { + uint64_t gpa = XEN_SPECIAL_PFN(CONSOLE) << TARGET_PAGE_BITS; + xen_overlay_do_map_page(&s->console_page, gpa); + } + + if (s->be_port) { + rebind_guest_port(s); + } else { + alloc_guest_port(s); + } + + trace_xen_primary_console_reset(s->guest_port); + + s->gt = qemu_xen_gnttab_open(); + uint32_t xs_gntref = GNTTAB_RESERVED_CONSOLE; + s->granted_xs = qemu_xen_gnttab_map_refs(s->gt, 1, xen_domid, &xs_gntref, + PROT_READ | PROT_WRITE); + + return 0; +} diff --git a/hw/i386/kvm/xen_primary_console.h b/hw/i386/kvm/xen_primary_console.h new file mode 100644 index 0000000000..7e2989ea0d --- /dev/null +++ b/hw/i386/kvm/xen_primary_console.h @@ -0,0 +1,23 @@ +/* + * QEMU Xen emulation: Primary console support + * + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Authors: David Woodhouse + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef QEMU_XEN_PRIMARY_CONSOLE_H +#define QEMU_XEN_PRIMARY_CONSOLE_H + +void xen_primary_console_create(void); +int xen_primary_console_reset(void); + +uint16_t xen_primary_console_get_port(void); +void xen_primary_console_set_be_port(uint16_t port); +uint64_t xen_primary_console_get_pfn(void); +void *xen_primary_console_get_map(void); + +#endif /* QEMU_XEN_PRIMARY_CONSOLE_H */ diff --git a/hw/i386/kvm/xen_xenstore.c b/hw/i386/kvm/xen_xenstore.c index b7c0407765..6e651960b3 100644 --- a/hw/i386/kvm/xen_xenstore.c +++ b/hw/i386/kvm/xen_xenstore.c @@ -25,6 +25,7 @@ #include "hw/xen/xen_backend_ops.h" #include "xen_overlay.h" #include "xen_evtchn.h" +#include "xen_primary_console.h" #include "xen_xenstore.h" #include "sysemu/kvm.h" @@ -1434,6 +1435,7 @@ static void alloc_guest_port(XenXenstoreState *s) int xen_xenstore_reset(void) { XenXenstoreState *s = xen_xenstore_singleton; + int console_port; GList *perms; int err; @@ -1470,6 +1472,14 @@ int xen_xenstore_reset(void) relpath_printf(s, perms, "store/ring-ref", "%lu", XEN_SPECIAL_PFN(XENSTORE)); + console_port = xen_primary_console_get_port(); + if (console_port) { + relpath_printf(s, perms, "console/ring-ref", "%lu", + XEN_SPECIAL_PFN(CONSOLE)); + relpath_printf(s, perms, "console/port", "%u", console_port); + relpath_printf(s, perms, "console/state", "%u", XenbusStateInitialised); + } + g_list_free_full(perms, g_free); /* diff --git a/hw/xen/xen-bus.c b/hw/xen/xen-bus.c index 3ffd1a5333..cc6f1b362f 100644 --- a/hw/xen/xen-bus.c +++ b/hw/xen/xen-bus.c @@ -922,6 +922,11 @@ void xen_device_notify_event_channel(XenDevice *xendev, } } +unsigned int xen_event_channel_get_local_port(XenEventChannel *channel) +{ + return channel->local_port; +} + void xen_device_unbind_event_channel(XenDevice *xendev, XenEventChannel *channel, Error **errp) diff --git a/include/hw/xen/xen-bus.h b/include/hw/xen/xen-bus.h index eb440880b5..38d40afa37 100644 --- a/include/hw/xen/xen-bus.h +++ b/include/hw/xen/xen-bus.h @@ -131,5 +131,6 @@ void xen_device_notify_event_channel(XenDevice *xendev, void xen_device_unbind_event_channel(XenDevice *xendev, XenEventChannel *channel, Error **errp); +unsigned int xen_event_channel_get_local_port(XenEventChannel *channel); #endif /* HW_XEN_BUS_H */ diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index 1dc9ab0d91..c0631f9cf4 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -28,6 +28,7 @@ #include "hw/i386/kvm/xen_overlay.h" #include "hw/i386/kvm/xen_evtchn.h" #include "hw/i386/kvm/xen_gnttab.h" +#include "hw/i386/kvm/xen_primary_console.h" #include "hw/i386/kvm/xen_xenstore.h" #include "hw/xen/interface/version.h" @@ -182,7 +183,8 @@ int kvm_xen_init(KVMState *s, uint32_t hypercall_msr) return ret; } - /* The page couldn't be overlaid until KVM was initialized */ + /* The pages couldn't be overlaid until KVM was initialized */ + xen_primary_console_reset(); xen_xenstore_reset(); return 0; @@ -812,11 +814,23 @@ static bool handle_get_param(struct kvm_xen_exit *exit, X86CPU *cpu, case HVM_PARAM_STORE_EVTCHN: hp.value = xen_xenstore_get_port(); break; + case HVM_PARAM_CONSOLE_PFN: + hp.value = xen_primary_console_get_pfn(); + if (!hp.value) { + err = -EINVAL; + } + break; + case HVM_PARAM_CONSOLE_EVTCHN: + hp.value = xen_primary_console_get_port(); + if (!hp.value) { + err = -EINVAL; + } + break; default: return false; } - if (kvm_copy_to_gva(cs, arg, &hp, sizeof(hp))) { + if (!err && kvm_copy_to_gva(cs, arg, &hp, sizeof(hp))) { err = -EFAULT; } out: @@ -1427,6 +1441,11 @@ int kvm_xen_soft_reset(void) return err; } + err = xen_primary_console_reset(); + if (err) { + return err; + } + err = xen_xenstore_reset(); if (err) { return err; From 25511f3e8c8e0c5a888d1c5de7891f7d287d2dd3 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 17 Oct 2023 13:32:50 +0100 Subject: [PATCH 735/974] hw/xen: only remove peers of PCI NICs on unplug When the Xen guest asks to unplug *emulated* NICs, it's kind of unhelpful also to unplug the peer of the *Xen* PV NIC. Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/xen/xen_platform.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/hw/i386/xen/xen_platform.c b/hw/i386/xen/xen_platform.c index 17457ff3de..e2dd1b536a 100644 --- a/hw/i386/xen/xen_platform.c +++ b/hw/i386/xen/xen_platform.c @@ -140,9 +140,14 @@ static void unplug_nic(PCIBus *b, PCIDevice *d, void *o) /* Remove the peer of the NIC device. Normally, this would be a tap device. */ static void del_nic_peer(NICState *nic, void *opaque) { - NetClientState *nc; + NetClientState *nc = qemu_get_queue(nic); + ObjectClass *klass = module_object_class_by_name(nc->model); + + /* Only delete peers of PCI NICs that we're about to delete */ + if (!klass || !object_class_dynamic_cast(klass, TYPE_PCI_DEVICE)) { + return; + } - nc = qemu_get_queue(nic); if (nc->peer) qemu_del_net_client(nc->peer); } From 25967ff69f61461cedf3289d5dafd1f6980cc894 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 17 Oct 2023 13:58:03 +0100 Subject: [PATCH 736/974] hw/xen: update Xen PV NIC to XenDevice model This allows us to use Xen PV networking with emulated Xen guests, and to add them on the command line or hotplug. Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/net/meson.build | 2 +- hw/net/trace-events | 11 + hw/net/xen_nic.c | 488 +++++++++++++++++++++++++++++--------- hw/xenpv/xen_machine_pv.c | 1 - 4 files changed, 383 insertions(+), 119 deletions(-) diff --git a/hw/net/meson.build b/hw/net/meson.build index 2632634df3..f64651c467 100644 --- a/hw/net/meson.build +++ b/hw/net/meson.build @@ -1,5 +1,5 @@ system_ss.add(when: 'CONFIG_DP8393X', if_true: files('dp8393x.c')) -system_ss.add(when: 'CONFIG_XEN', if_true: files('xen_nic.c')) +system_ss.add(when: 'CONFIG_XEN_BUS', if_true: files('xen_nic.c')) system_ss.add(when: 'CONFIG_NE2000_COMMON', if_true: files('ne2000.c')) # PCI network cards diff --git a/hw/net/trace-events b/hw/net/trace-events index 3abfd65e5b..3097742cc0 100644 --- a/hw/net/trace-events +++ b/hw/net/trace-events @@ -482,3 +482,14 @@ dp8393x_receive_oversize(int size) "oversize packet, pkt_size is %d" dp8393x_receive_not_netcard(void) "packet not for netcard" dp8393x_receive_packet(int crba) "Receive packet at 0x%"PRIx32 dp8393x_receive_write_status(int crba) "Write status at 0x%"PRIx32 + +# xen_nic.c +xen_netdev_realize(int dev, const char *info, const char *peer) "vif%u info '%s' peer '%s'" +xen_netdev_unrealize(int dev) "vif%u" +xen_netdev_create(int dev) "vif%u" +xen_netdev_destroy(int dev) "vif%u" +xen_netdev_disconnect(int dev) "vif%u" +xen_netdev_connect(int dev, unsigned int tx, unsigned int rx, int port) "vif%u tx %u rx %u port %u" +xen_netdev_frontend_changed(const char *dev, int state) "vif%s state %d" +xen_netdev_tx(int dev, int ref, int off, int len, unsigned int flags, const char *c, const char *d, const char *m, const char *e) "vif%u ref %u off %u len %u flags 0x%x%s%s%s%s" +xen_netdev_rx(int dev, int idx, int status, int flags) "vif%u idx %d status %d flags 0x%x" diff --git a/hw/net/xen_nic.c b/hw/net/xen_nic.c index 9bbf6599fc..af4ba3f1e6 100644 --- a/hw/net/xen_nic.c +++ b/hw/net/xen_nic.c @@ -20,6 +20,13 @@ */ #include "qemu/osdep.h" +#include "qemu/main-loop.h" +#include "qemu/cutils.h" +#include "qemu/log.h" +#include "qemu/qemu-print.h" +#include "qapi/qmp/qdict.h" +#include "qapi/error.h" + #include #include #include @@ -27,18 +34,26 @@ #include "net/net.h" #include "net/checksum.h" #include "net/util.h" -#include "hw/xen/xen-legacy-backend.h" + +#include "hw/xen/xen-backend.h" +#include "hw/xen/xen-bus-helper.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" #include "hw/xen/interface/io/netif.h" +#include "hw/xen/interface/io/xs_wire.h" + +#include "trace.h" /* ------------------------------------------------------------- */ struct XenNetDev { - struct XenLegacyDevice xendev; /* must be first */ - char *mac; + struct XenDevice xendev; /* must be first */ + XenEventChannel *event_channel; + int dev; int tx_work; - int tx_ring_ref; - int rx_ring_ref; + unsigned int tx_ring_ref; + unsigned int rx_ring_ref; struct netif_tx_sring *txs; struct netif_rx_sring *rxs; netif_tx_back_ring_t tx_ring; @@ -47,6 +62,11 @@ struct XenNetDev { NICState *nic; }; +typedef struct XenNetDev XenNetDev; + +#define TYPE_XEN_NET_DEVICE "xen-net-device" +OBJECT_DECLARE_SIMPLE_TYPE(XenNetDev, XEN_NET_DEVICE) + /* ------------------------------------------------------------- */ static void net_tx_response(struct XenNetDev *netdev, netif_tx_request_t *txp, int8_t st) @@ -68,7 +88,8 @@ static void net_tx_response(struct XenNetDev *netdev, netif_tx_request_t *txp, i netdev->tx_ring.rsp_prod_pvt = ++i; RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->tx_ring, notify); if (notify) { - xen_pv_send_notify(&netdev->xendev); + xen_device_notify_event_channel(XEN_DEVICE(netdev), + netdev->event_channel, NULL); } if (i == netdev->tx_ring.req_cons) { @@ -104,13 +125,16 @@ static void net_tx_error(struct XenNetDev *netdev, netif_tx_request_t *txp, RING #endif } -static void net_tx_packets(struct XenNetDev *netdev) +static bool net_tx_packets(struct XenNetDev *netdev) { + bool done_something = false; netif_tx_request_t txreq; RING_IDX rc, rp; void *page; void *tmpbuf = NULL; + assert(qemu_mutex_iothread_locked()); + for (;;) { rc = netdev->tx_ring.req_cons; rp = netdev->tx_ring.sring->req_prod; @@ -122,49 +146,52 @@ static void net_tx_packets(struct XenNetDev *netdev) } memcpy(&txreq, RING_GET_REQUEST(&netdev->tx_ring, rc), sizeof(txreq)); netdev->tx_ring.req_cons = ++rc; + done_something = true; #if 1 /* should not happen in theory, we don't announce the * * feature-{sg,gso,whatelse} flags in xenstore (yet?) */ if (txreq.flags & NETTXF_extra_info) { - xen_pv_printf(&netdev->xendev, 0, "FIXME: extra info flag\n"); + qemu_log_mask(LOG_UNIMP, "vif%u: FIXME: extra info flag\n", + netdev->dev); net_tx_error(netdev, &txreq, rc); continue; } if (txreq.flags & NETTXF_more_data) { - xen_pv_printf(&netdev->xendev, 0, "FIXME: more data flag\n"); + qemu_log_mask(LOG_UNIMP, "vif%u: FIXME: more data flag\n", + netdev->dev); net_tx_error(netdev, &txreq, rc); continue; } #endif if (txreq.size < 14) { - xen_pv_printf(&netdev->xendev, 0, "bad packet size: %d\n", - txreq.size); + qemu_log_mask(LOG_GUEST_ERROR, "vif%u: bad packet size: %d\n", + netdev->dev, txreq.size); net_tx_error(netdev, &txreq, rc); continue; } if ((txreq.offset + txreq.size) > XEN_PAGE_SIZE) { - xen_pv_printf(&netdev->xendev, 0, "error: page crossing\n"); + qemu_log_mask(LOG_GUEST_ERROR, "vif%u: error: page crossing\n", + netdev->dev); net_tx_error(netdev, &txreq, rc); continue; } - xen_pv_printf(&netdev->xendev, 3, - "tx packet ref %d, off %d, len %d, flags 0x%x%s%s%s%s\n", - txreq.gref, txreq.offset, txreq.size, txreq.flags, - (txreq.flags & NETTXF_csum_blank) ? " csum_blank" : "", - (txreq.flags & NETTXF_data_validated) ? " data_validated" : "", - (txreq.flags & NETTXF_more_data) ? " more_data" : "", - (txreq.flags & NETTXF_extra_info) ? " extra_info" : ""); + trace_xen_netdev_tx(netdev->dev, txreq.gref, txreq.offset, + txreq.size, txreq.flags, + (txreq.flags & NETTXF_csum_blank) ? " csum_blank" : "", + (txreq.flags & NETTXF_data_validated) ? " data_validated" : "", + (txreq.flags & NETTXF_more_data) ? " more_data" : "", + (txreq.flags & NETTXF_extra_info) ? " extra_info" : ""); - page = xen_be_map_grant_ref(&netdev->xendev, txreq.gref, - PROT_READ); + page = xen_device_map_grant_refs(&netdev->xendev, &txreq.gref, 1, + PROT_READ, NULL); if (page == NULL) { - xen_pv_printf(&netdev->xendev, 0, - "error: tx gref dereference failed (%d)\n", - txreq.gref); + qemu_log_mask(LOG_GUEST_ERROR, + "vif%u: tx gref dereference failed (%d)\n", + netdev->dev, txreq.gref); net_tx_error(netdev, &txreq, rc); continue; } @@ -181,7 +208,8 @@ static void net_tx_packets(struct XenNetDev *netdev) qemu_send_packet(qemu_get_queue(netdev->nic), page + txreq.offset, txreq.size); } - xen_be_unmap_grant_ref(&netdev->xendev, page, txreq.gref); + xen_device_unmap_grant_refs(&netdev->xendev, page, &txreq.gref, 1, + NULL); net_tx_response(netdev, &txreq, NETIF_RSP_OKAY); } if (!netdev->tx_work) { @@ -190,6 +218,7 @@ static void net_tx_packets(struct XenNetDev *netdev) netdev->tx_work = 0; } g_free(tmpbuf); + return done_something; } /* ------------------------------------------------------------- */ @@ -212,14 +241,13 @@ static void net_rx_response(struct XenNetDev *netdev, resp->status = (int16_t)st; } - xen_pv_printf(&netdev->xendev, 3, - "rx response: idx %d, status %d, flags 0x%x\n", - i, resp->status, resp->flags); + trace_xen_netdev_rx(netdev->dev, i, resp->status, resp->flags); netdev->rx_ring.rsp_prod_pvt = ++i; RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->rx_ring, notify); if (notify) { - xen_pv_send_notify(&netdev->xendev); + xen_device_notify_event_channel(XEN_DEVICE(netdev), + netdev->event_channel, NULL); } } @@ -232,7 +260,9 @@ static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size RING_IDX rc, rp; void *page; - if (netdev->xendev.be_state != XenbusStateConnected) { + assert(qemu_mutex_iothread_locked()); + + if (xen_device_backend_get_state(&netdev->xendev) != XenbusStateConnected) { return -1; } @@ -244,24 +274,26 @@ static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size return 0; } if (size > XEN_PAGE_SIZE - NET_IP_ALIGN) { - xen_pv_printf(&netdev->xendev, 0, "packet too big (%lu > %ld)", - (unsigned long)size, XEN_PAGE_SIZE - NET_IP_ALIGN); + qemu_log_mask(LOG_GUEST_ERROR, "vif%u: packet too big (%lu > %ld)", + netdev->dev, (unsigned long)size, + XEN_PAGE_SIZE - NET_IP_ALIGN); return -1; } memcpy(&rxreq, RING_GET_REQUEST(&netdev->rx_ring, rc), sizeof(rxreq)); netdev->rx_ring.req_cons = ++rc; - page = xen_be_map_grant_ref(&netdev->xendev, rxreq.gref, PROT_WRITE); + page = xen_device_map_grant_refs(&netdev->xendev, &rxreq.gref, 1, + PROT_WRITE, NULL); if (page == NULL) { - xen_pv_printf(&netdev->xendev, 0, - "error: rx gref dereference failed (%d)\n", - rxreq.gref); + qemu_log_mask(LOG_GUEST_ERROR, + "vif%u: rx gref dereference failed (%d)\n", + netdev->dev, rxreq.gref); net_rx_response(netdev, &rxreq, NETIF_RSP_ERROR, 0, 0, 0); return -1; } memcpy(page + NET_IP_ALIGN, buf, size); - xen_be_unmap_grant_ref(&netdev->xendev, page, rxreq.gref); + xen_device_unmap_grant_refs(&netdev->xendev, page, &rxreq.gref, 1, NULL); net_rx_response(netdev, &rxreq, NETIF_RSP_OKAY, NET_IP_ALIGN, size, 0); return size; @@ -275,139 +307,361 @@ static NetClientInfo net_xen_info = { .receive = net_rx_packet, }; -static int net_init(struct XenLegacyDevice *xendev) +static void xen_netdev_realize(XenDevice *xendev, Error **errp) { - struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); + ERRP_GUARD(); + XenNetDev *netdev = XEN_NET_DEVICE(xendev); + NetClientState *nc; - /* read xenstore entries */ - if (netdev->mac == NULL) { - netdev->mac = xenstore_read_be_str(&netdev->xendev, "mac"); - } + qemu_macaddr_default_if_unset(&netdev->conf.macaddr); - /* do we have all we need? */ - if (netdev->mac == NULL) { - return -1; - } - - if (net_parse_macaddr(netdev->conf.macaddr.a, netdev->mac) < 0) { - return -1; - } + xen_device_frontend_printf(xendev, "mac", "%02x:%02x:%02x:%02x:%02x:%02x", + netdev->conf.macaddr.a[0], + netdev->conf.macaddr.a[1], + netdev->conf.macaddr.a[2], + netdev->conf.macaddr.a[3], + netdev->conf.macaddr.a[4], + netdev->conf.macaddr.a[5]); netdev->nic = qemu_new_nic(&net_xen_info, &netdev->conf, - "xen", NULL, netdev); + object_get_typename(OBJECT(xendev)), + DEVICE(xendev)->id, netdev); - qemu_set_info_str(qemu_get_queue(netdev->nic), - "nic: xenbus vif macaddr=%s", netdev->mac); + nc = qemu_get_queue(netdev->nic); + qemu_format_nic_info_str(nc, netdev->conf.macaddr.a); /* fill info */ - xenstore_write_be_int(&netdev->xendev, "feature-rx-copy", 1); - xenstore_write_be_int(&netdev->xendev, "feature-rx-flip", 0); + xen_device_backend_printf(xendev, "feature-rx-copy", "%u", 1); + xen_device_backend_printf(xendev, "feature-rx-flip", "%u", 0); - return 0; + trace_xen_netdev_realize(netdev->dev, nc->info_str, nc->peer ? + nc->peer->name : "(none)"); } -static int net_connect(struct XenLegacyDevice *xendev) +static bool net_event(void *_xendev) { - struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); - int rx_copy; + XenNetDev *netdev = XEN_NET_DEVICE(_xendev); + bool done_something; - if (xenstore_read_fe_int(&netdev->xendev, "tx-ring-ref", - &netdev->tx_ring_ref) == -1) { - return -1; - } - if (xenstore_read_fe_int(&netdev->xendev, "rx-ring-ref", - &netdev->rx_ring_ref) == -1) { - return 1; - } - if (xenstore_read_fe_int(&netdev->xendev, "event-channel", - &netdev->xendev.remote_port) == -1) { - return -1; + done_something = net_tx_packets(netdev); + qemu_flush_queued_packets(qemu_get_queue(netdev->nic)); + return done_something; +} + +static bool xen_netdev_connect(XenDevice *xendev, Error **errp) +{ + XenNetDev *netdev = XEN_NET_DEVICE(xendev); + unsigned int port, rx_copy; + + assert(qemu_mutex_iothread_locked()); + + if (xen_device_frontend_scanf(xendev, "tx-ring-ref", "%u", + &netdev->tx_ring_ref) != 1) { + error_setg(errp, "failed to read tx-ring-ref"); + return false; } - if (xenstore_read_fe_int(&netdev->xendev, "request-rx-copy", &rx_copy) == -1) { + if (xen_device_frontend_scanf(xendev, "rx-ring-ref", "%u", + &netdev->rx_ring_ref) != 1) { + error_setg(errp, "failed to read rx-ring-ref"); + return false; + } + + if (xen_device_frontend_scanf(xendev, "event-channel", "%u", + &port) != 1) { + error_setg(errp, "failed to read event-channel"); + return false; + } + + if (xen_device_frontend_scanf(xendev, "request-rx-copy", "%u", + &rx_copy) != 1) { rx_copy = 0; } if (rx_copy == 0) { - xen_pv_printf(&netdev->xendev, 0, - "frontend doesn't support rx-copy.\n"); - return -1; + error_setg(errp, "frontend doesn't support rx-copy"); + return false; } - netdev->txs = xen_be_map_grant_ref(&netdev->xendev, - netdev->tx_ring_ref, - PROT_READ | PROT_WRITE); + netdev->txs = xen_device_map_grant_refs(xendev, + &netdev->tx_ring_ref, 1, + PROT_READ | PROT_WRITE, + errp); if (!netdev->txs) { - return -1; + error_prepend(errp, "failed to map tx grant ref: "); + return false; } - netdev->rxs = xen_be_map_grant_ref(&netdev->xendev, - netdev->rx_ring_ref, - PROT_READ | PROT_WRITE); + + netdev->rxs = xen_device_map_grant_refs(xendev, + &netdev->rx_ring_ref, 1, + PROT_READ | PROT_WRITE, + errp); if (!netdev->rxs) { - xen_be_unmap_grant_ref(&netdev->xendev, netdev->txs, - netdev->tx_ring_ref); - netdev->txs = NULL; - return -1; + error_prepend(errp, "failed to map rx grant ref: "); + return false; } + BACK_RING_INIT(&netdev->tx_ring, netdev->txs, XEN_PAGE_SIZE); BACK_RING_INIT(&netdev->rx_ring, netdev->rxs, XEN_PAGE_SIZE); - xen_be_bind_evtchn(&netdev->xendev); + netdev->event_channel = xen_device_bind_event_channel(xendev, port, + net_event, + netdev, + errp); + if (!netdev->event_channel) { + return false; + } - xen_pv_printf(&netdev->xendev, 1, "ok: tx-ring-ref %d, rx-ring-ref %d, " - "remote port %d, local port %d\n", - netdev->tx_ring_ref, netdev->rx_ring_ref, - netdev->xendev.remote_port, netdev->xendev.local_port); + trace_xen_netdev_connect(netdev->dev, netdev->tx_ring_ref, + netdev->rx_ring_ref, port); net_tx_packets(netdev); - return 0; + return true; } -static void net_disconnect(struct XenLegacyDevice *xendev) +static void xen_netdev_disconnect(XenDevice *xendev, Error **errp) { - struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); + XenNetDev *netdev = XEN_NET_DEVICE(xendev); - xen_pv_unbind_evtchn(&netdev->xendev); + trace_xen_netdev_disconnect(netdev->dev); + assert(qemu_mutex_iothread_locked()); + + netdev->tx_ring.sring = NULL; + netdev->rx_ring.sring = NULL; + + if (netdev->event_channel) { + xen_device_unbind_event_channel(xendev, netdev->event_channel, + errp); + netdev->event_channel = NULL; + } if (netdev->txs) { - xen_be_unmap_grant_ref(&netdev->xendev, netdev->txs, - netdev->tx_ring_ref); + xen_device_unmap_grant_refs(xendev, netdev->txs, + &netdev->tx_ring_ref, 1, errp); netdev->txs = NULL; } if (netdev->rxs) { - xen_be_unmap_grant_ref(&netdev->xendev, netdev->rxs, - netdev->rx_ring_ref); + xen_device_unmap_grant_refs(xendev, netdev->rxs, + &netdev->rx_ring_ref, 1, errp); netdev->rxs = NULL; } } -static void net_event(struct XenLegacyDevice *xendev) +/* -------------------------------------------------------------------- */ + + +static void xen_netdev_frontend_changed(XenDevice *xendev, + enum xenbus_state frontend_state, + Error **errp) { - struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); - net_tx_packets(netdev); - qemu_flush_queued_packets(qemu_get_queue(netdev->nic)); + ERRP_GUARD(); + enum xenbus_state backend_state = xen_device_backend_get_state(xendev); + + trace_xen_netdev_frontend_changed(xendev->name, frontend_state); + + switch (frontend_state) { + case XenbusStateConnected: + if (backend_state == XenbusStateConnected) { + break; + } + + xen_netdev_disconnect(xendev, errp); + if (*errp) { + break; + } + + if (!xen_netdev_connect(xendev, errp)) { + xen_netdev_disconnect(xendev, NULL); + xen_device_backend_set_state(xendev, XenbusStateClosing); + break; + } + + xen_device_backend_set_state(xendev, XenbusStateConnected); + break; + + case XenbusStateClosing: + xen_device_backend_set_state(xendev, XenbusStateClosing); + break; + + case XenbusStateClosed: + case XenbusStateUnknown: + xen_netdev_disconnect(xendev, errp); + if (*errp) { + break; + } + + xen_device_backend_set_state(xendev, XenbusStateClosed); + break; + + case XenbusStateInitialised: + /* + * Linux netback does nothing on the frontend going (back) to + * XenbusStateInitialised, so do the same here. + */ + default: + break; + } } -static int net_free(struct XenLegacyDevice *xendev) +static char *xen_netdev_get_name(XenDevice *xendev, Error **errp) { - struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); + XenNetDev *netdev = XEN_NET_DEVICE(xendev); + + if (netdev->dev == -1) { + XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); + char fe_path[XENSTORE_ABS_PATH_MAX + 1]; + int idx = (xen_mode == XEN_EMULATE) ? 0 : 1; + char *value; + + /* Theoretically we could go up to INT_MAX here but that's overkill */ + while (idx < 100) { + snprintf(fe_path, sizeof(fe_path), + "/local/domain/%u/device/vif/%u", + xendev->frontend_id, idx); + value = qemu_xen_xs_read(xenbus->xsh, XBT_NULL, fe_path, NULL); + if (!value) { + if (errno == ENOENT) { + netdev->dev = idx; + goto found; + } + error_setg(errp, "cannot read %s: %s", fe_path, + strerror(errno)); + return NULL; + } + free(value); + idx++; + } + error_setg(errp, "cannot find device index for netdev device"); + return NULL; + } + found: + return g_strdup_printf("%u", netdev->dev); +} + +static void xen_netdev_unrealize(XenDevice *xendev) +{ + XenNetDev *netdev = XEN_NET_DEVICE(xendev); + + trace_xen_netdev_unrealize(netdev->dev); + + /* Disconnect from the frontend in case this has not already happened */ + xen_netdev_disconnect(xendev, NULL); if (netdev->nic) { qemu_del_nic(netdev->nic); - netdev->nic = NULL; } - g_free(netdev->mac); - netdev->mac = NULL; - return 0; } /* ------------------------------------------------------------- */ -struct XenDevOps xen_netdev_ops = { - .size = sizeof(struct XenNetDev), - .flags = DEVOPS_FLAG_NEED_GNTDEV, - .init = net_init, - .initialise = net_connect, - .event = net_event, - .disconnect = net_disconnect, - .free = net_free, +static Property xen_netdev_properties[] = { + DEFINE_NIC_PROPERTIES(XenNetDev, conf), + DEFINE_PROP_INT32("idx", XenNetDev, dev, -1), + DEFINE_PROP_END_OF_LIST(), }; + +static void xen_netdev_class_init(ObjectClass *class, void *data) +{ + DeviceClass *dev_class = DEVICE_CLASS(class); + XenDeviceClass *xendev_class = XEN_DEVICE_CLASS(class); + + xendev_class->backend = "qnic"; + xendev_class->device = "vif"; + xendev_class->get_name = xen_netdev_get_name; + xendev_class->realize = xen_netdev_realize; + xendev_class->frontend_changed = xen_netdev_frontend_changed; + xendev_class->unrealize = xen_netdev_unrealize; + set_bit(DEVICE_CATEGORY_NETWORK, dev_class->categories); + dev_class->user_creatable = true; + + device_class_set_props(dev_class, xen_netdev_properties); +} + +static const TypeInfo xen_net_type_info = { + .name = TYPE_XEN_NET_DEVICE, + .parent = TYPE_XEN_DEVICE, + .instance_size = sizeof(XenNetDev), + .class_init = xen_netdev_class_init, +}; + +static void xen_net_register_types(void) +{ + type_register_static(&xen_net_type_info); +} + +type_init(xen_net_register_types) + +/* Called to instantiate a XenNetDev when the backend is detected. */ +static void xen_net_device_create(XenBackendInstance *backend, + QDict *opts, Error **errp) +{ + ERRP_GUARD(); + XenBus *xenbus = xen_backend_get_bus(backend); + const char *name = xen_backend_get_name(backend); + XenDevice *xendev = NULL; + unsigned long number; + const char *macstr; + XenNetDev *net; + MACAddr mac; + + if (qemu_strtoul(name, NULL, 10, &number) || number >= INT_MAX) { + error_setg(errp, "failed to parse name '%s'", name); + goto fail; + } + + trace_xen_netdev_create(number); + + macstr = qdict_get_try_str(opts, "mac"); + if (macstr == NULL) { + error_setg(errp, "no MAC address found"); + goto fail; + } + + if (net_parse_macaddr(mac.a, macstr) < 0) { + error_setg(errp, "failed to parse MAC address"); + goto fail; + } + + xendev = XEN_DEVICE(qdev_new(TYPE_XEN_NET_DEVICE)); + net = XEN_NET_DEVICE(xendev); + + net->dev = number; + memcpy(&net->conf.macaddr, &mac, sizeof(mac)); + + if (qdev_realize_and_unref(DEVICE(xendev), BUS(xenbus), errp)) { + xen_backend_set_device(backend, xendev); + return; + } + + error_prepend(errp, "realization of net device %lu failed: ", + number); + + fail: + if (xendev) { + object_unparent(OBJECT(xendev)); + } +} + +static void xen_net_device_destroy(XenBackendInstance *backend, + Error **errp) +{ + ERRP_GUARD(); + XenDevice *xendev = xen_backend_get_device(backend); + XenNetDev *netdev = XEN_NET_DEVICE(xendev); + + trace_xen_netdev_destroy(netdev->dev); + + object_unparent(OBJECT(xendev)); +} + +static const XenBackendInfo xen_net_backend_info = { + .type = "qnic", + .create = xen_net_device_create, + .destroy = xen_net_device_destroy, +}; + +static void xen_net_register_backend(void) +{ + xen_backend_register(&xen_net_backend_info); +} + +xen_backend_init(xen_net_register_backend); diff --git a/hw/xenpv/xen_machine_pv.c b/hw/xenpv/xen_machine_pv.c index 1533f5dfb4..9f9f137f99 100644 --- a/hw/xenpv/xen_machine_pv.c +++ b/hw/xenpv/xen_machine_pv.c @@ -54,7 +54,6 @@ static void xen_init_pv(MachineState *machine) } xen_be_register("vfb", &xen_framebuffer_ops); - xen_be_register("qnic", &xen_netdev_ops); /* configure framebuffer */ if (vga_interface_type == VGA_XENFB) { From c10b4b3c0dcae2fe1836e534059b69e8bfce0e9f Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 17 Oct 2023 17:53:58 +0100 Subject: [PATCH 737/974] hw/i386/pc: support '-nic' for xen-net-device The default NIC creation seems a bit hackish to me. I don't understand why each platform has to call pci_nic_init_nofail() from a point in the code where it actually has a pointer to the PCI bus, and then we have the special cases for things like ne2k_isa. If qmp_device_add() can *find* the appropriate bus and instantiate the device on it, why can't we just do that from generic code for creating the default NICs too? But that isn't a yak I want to shave today. Add a xenbus field to the PCMachineState so that it can make its way from pc_basic_device_init() to pc_nic_init() and be handled as a special case like ne2k_isa is. Now we can launch emulated Xen guests with '-nic user'. Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/pc.c | 11 ++++++++--- hw/i386/pc_piix.c | 2 +- hw/i386/pc_q35.c | 2 +- hw/xen/xen-bus.c | 4 +++- include/hw/i386/pc.h | 4 +++- include/hw/xen/xen-bus.h | 2 +- 6 files changed, 17 insertions(+), 8 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 1aef21aa2c..188bc9d0f8 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1261,7 +1261,7 @@ void pc_basic_device_init(struct PCMachineState *pcms, if (pcms->bus) { pci_create_simple(pcms->bus, -1, "xen-platform"); } - xen_bus_init(); + pcms->xenbus = xen_bus_init(); xen_be_init(); } #endif @@ -1289,7 +1289,8 @@ void pc_basic_device_init(struct PCMachineState *pcms, pcms->vmport != ON_OFF_AUTO_ON); } -void pc_nic_init(PCMachineClass *pcmc, ISABus *isa_bus, PCIBus *pci_bus) +void pc_nic_init(PCMachineClass *pcmc, ISABus *isa_bus, PCIBus *pci_bus, + BusState *xen_bus) { MachineClass *mc = MACHINE_CLASS(pcmc); int i; @@ -1299,7 +1300,11 @@ void pc_nic_init(PCMachineClass *pcmc, ISABus *isa_bus, PCIBus *pci_bus) NICInfo *nd = &nd_table[i]; const char *model = nd->model ? nd->model : mc->default_nic; - if (g_str_equal(model, "ne2k_isa")) { + if (xen_bus && (!nd->model || g_str_equal(model, "xen-net-device"))) { + DeviceState *dev = qdev_new("xen-net-device"); + qdev_set_nic_properties(dev, nd); + qdev_realize_and_unref(dev, xen_bus, &error_fatal); + } else if (g_str_equal(model, "ne2k_isa")) { pc_init_ne2k_isa(isa_bus, nd); } else { pci_nic_init_nofail(nd, pci_bus, model, NULL); diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 26e161beb9..eace854335 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -342,7 +342,7 @@ static void pc_init1(MachineState *machine, pc_basic_device_init(pcms, isa_bus, x86ms->gsi, rtc_state, true, 0x4); - pc_nic_init(pcmc, isa_bus, pci_bus); + pc_nic_init(pcmc, isa_bus, pci_bus, pcms->xenbus); if (pcmc->pci_enabled) { pc_cmos_init(pcms, idebus[0], idebus[1], rtc_state); diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 597943ff1b..4f3e5412f6 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -340,7 +340,7 @@ static void pc_q35_init(MachineState *machine) /* the rest devices to which pci devfn is automatically assigned */ pc_vga_init(isa_bus, host_bus); - pc_nic_init(pcmc, isa_bus, host_bus); + pc_nic_init(pcmc, isa_bus, host_bus, pcms->xenbus); if (machine->nvdimms_state->is_enabled) { nvdimm_init_acpi_state(machine->nvdimms_state, system_io, diff --git a/hw/xen/xen-bus.c b/hw/xen/xen-bus.c index cc6f1b362f..4973e7d9c9 100644 --- a/hw/xen/xen-bus.c +++ b/hw/xen/xen-bus.c @@ -1133,11 +1133,13 @@ static void xen_register_types(void) type_init(xen_register_types) -void xen_bus_init(void) +BusState *xen_bus_init(void) { DeviceState *dev = qdev_new(TYPE_XEN_BRIDGE); BusState *bus = qbus_new(TYPE_XEN_BUS, dev, NULL); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); qbus_set_bus_hotplug_handler(bus); + + return bus; } diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 29a9724524..a10ceeabbf 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -33,6 +33,7 @@ typedef struct PCMachineState { /* Pointers to devices and objects: */ PCIBus *bus; + BusState *xenbus; I2CBus *smbus; PFlashCFI01 *flash[2]; ISADevice *pcspk; @@ -184,7 +185,8 @@ void pc_basic_device_init(struct PCMachineState *pcms, void pc_cmos_init(PCMachineState *pcms, BusState *ide0, BusState *ide1, ISADevice *s); -void pc_nic_init(PCMachineClass *pcmc, ISABus *isa_bus, PCIBus *pci_bus); +void pc_nic_init(PCMachineClass *pcmc, ISABus *isa_bus, PCIBus *pci_bus, + BusState *xen_bus); void pc_i8259_create(ISABus *isa_bus, qemu_irq *i8259_irqs); diff --git a/include/hw/xen/xen-bus.h b/include/hw/xen/xen-bus.h index 38d40afa37..334ddd1ff6 100644 --- a/include/hw/xen/xen-bus.h +++ b/include/hw/xen/xen-bus.h @@ -75,7 +75,7 @@ struct XenBusClass { OBJECT_DECLARE_TYPE(XenBus, XenBusClass, XEN_BUS) -void xen_bus_init(void); +BusState *xen_bus_init(void); void xen_device_backend_set_state(XenDevice *xendev, enum xenbus_state state); From a73049953b7259a94d068c1b453a73b8905b1a6f Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 19 Oct 2023 12:56:42 +0100 Subject: [PATCH 738/974] xen-platform: unplug AHCI disks To support Xen guests using the Q35 chipset, the unplug protocol needs to also remove AHCI disks. Make pci_xen_ide_unplug() more generic, iterating over the children of the PCI device and destroying the "ide-hd" devices. That works the same for both AHCI and IDE, as does the detection of the primary disk as unit 0 on the bus named "ide.0". Then pci_xen_ide_unplug() can be used for both AHCI and IDE devices. Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/xen/xen_platform.c | 76 ++++++++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 27 deletions(-) diff --git a/hw/i386/xen/xen_platform.c b/hw/i386/xen/xen_platform.c index e2dd1b536a..ef7d3fc05f 100644 --- a/hw/i386/xen/xen_platform.c +++ b/hw/i386/xen/xen_platform.c @@ -169,39 +169,60 @@ static void pci_unplug_nics(PCIBus *bus) * * [1] https://xenbits.xen.org/gitweb/?p=xen.git;a=blob;f=docs/misc/hvm-emulated-unplug.pandoc */ -static void pci_xen_ide_unplug(PCIDevice *d, bool aux) +struct ide_unplug_state { + bool aux; + int nr_unplugged; +}; + +static int ide_dev_unplug(DeviceState *dev, void *_st) { - DeviceState *dev = DEVICE(d); - PCIIDEState *pci_ide; - int i; + struct ide_unplug_state *st = _st; IDEDevice *idedev; IDEBus *idebus; BlockBackend *blk; + int unit; - pci_ide = PCI_IDE(dev); - - for (i = aux ? 1 : 0; i < 4; i++) { - idebus = &pci_ide->bus[i / 2]; - blk = idebus->ifs[i % 2].blk; - - if (blk && idebus->ifs[i % 2].drive_kind != IDE_CD) { - if (!(i % 2)) { - idedev = idebus->master; - } else { - idedev = idebus->slave; - } - - blk_drain(blk); - blk_flush(blk); - - blk_detach_dev(blk, DEVICE(idedev)); - idebus->ifs[i % 2].blk = NULL; - idedev->conf.blk = NULL; - monitor_remove_blk(blk); - blk_unref(blk); - } + idedev = IDE_DEVICE(object_dynamic_cast(OBJECT(dev), "ide-hd")); + if (!idedev) { + return 0; + } + + idebus = IDE_BUS(qdev_get_parent_bus(dev)); + + unit = (idedev == idebus->slave); + assert(unit || idedev == idebus->master); + + if (st->aux && !unit && !strcmp(BUS(idebus)->name, "ide.0")) { + return 0; + } + + blk = idebus->ifs[unit].blk; + if (blk) { + blk_drain(blk); + blk_flush(blk); + + blk_detach_dev(blk, DEVICE(idedev)); + idebus->ifs[unit].blk = NULL; + idedev->conf.blk = NULL; + monitor_remove_blk(blk); + blk_unref(blk); + } + + object_unparent(OBJECT(dev)); + st->nr_unplugged++; + + return 0; +} + +static void pci_xen_ide_unplug(PCIDevice *d, bool aux) +{ + struct ide_unplug_state st = { aux, 0 }; + DeviceState *dev = DEVICE(d); + + qdev_walk_children(dev, NULL, NULL, ide_dev_unplug, NULL, &st); + if (st.nr_unplugged) { + pci_device_reset(d); } - pci_device_reset(d); } static void unplug_disks(PCIBus *b, PCIDevice *d, void *opaque) @@ -216,6 +237,7 @@ static void unplug_disks(PCIBus *b, PCIDevice *d, void *opaque) switch (pci_get_word(d->config + PCI_CLASS_DEVICE)) { case PCI_CLASS_STORAGE_IDE: + case PCI_CLASS_STORAGE_SATA: pci_xen_ide_unplug(d, aux); break; From cc9d10b9e89f0325c1a14955534d6b28ea586fba Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 19 Oct 2023 15:30:23 +0100 Subject: [PATCH 739/974] docs: update Xen-on-KVM documentation Add notes about console and network support, and how to launch PV guests. Clean up the disk configuration examples now that that's simpler, and remove the comment about IDE unplug on q35/AHCI now that it's fixed. Update the -initrd option documentation to explain how to quote commas in module command lines, and reference it when documenting PV guests. Also update stale avocado test filename in MAINTAINERS. Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- MAINTAINERS | 2 +- docs/system/i386/xen.rst | 107 +++++++++++++++++++++++++++++---------- qemu-options.hx | 12 ++++- 3 files changed, 90 insertions(+), 31 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 59b92ee640..fd6b362311 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -490,7 +490,7 @@ S: Supported F: include/sysemu/kvm_xen.h F: target/i386/kvm/xen* F: hw/i386/kvm/xen* -F: tests/avocado/xen_guest.py +F: tests/avocado/kvm_xen_guest.py Guest CPU Cores (other accelerators) ------------------------------------ diff --git a/docs/system/i386/xen.rst b/docs/system/i386/xen.rst index f06765e88c..81898768ba 100644 --- a/docs/system/i386/xen.rst +++ b/docs/system/i386/xen.rst @@ -15,46 +15,24 @@ Setup ----- Xen mode is enabled by setting the ``xen-version`` property of the KVM -accelerator, for example for Xen 4.10: +accelerator, for example for Xen 4.17: .. parsed-literal:: - |qemu_system| --accel kvm,xen-version=0x4000a,kernel-irqchip=split + |qemu_system| --accel kvm,xen-version=0x40011,kernel-irqchip=split Additionally, virtual APIC support can be advertised to the guest through the ``xen-vapic`` CPU flag: .. parsed-literal:: - |qemu_system| --accel kvm,xen-version=0x4000a,kernel-irqchip=split --cpu host,+xen_vapic + |qemu_system| --accel kvm,xen-version=0x40011,kernel-irqchip=split --cpu host,+xen-vapic When Xen support is enabled, QEMU changes hypervisor identification (CPUID 0x40000000..0x4000000A) to Xen. The KVM identification and features are not advertised to a Xen guest. If Hyper-V is also enabled, the Xen identification moves to leaves 0x40000100..0x4000010A. -The Xen platform device is enabled automatically for a Xen guest. This allows -a guest to unplug all emulated devices, in order to use Xen PV block and network -drivers instead. Under Xen, the boot disk is typically available both via IDE -emulation, and as a PV block device. Guest bootloaders typically use IDE to load -the guest kernel, which then unplugs the IDE and continues with the Xen PV block -device. - -This configuration can be achieved as follows - -.. parsed-literal:: - - |qemu_system| -M pc --accel kvm,xen-version=0x4000a,kernel-irqchip=split \\ - -drive file=${GUEST_IMAGE},if=none,id=disk,file.locking=off -device xen-disk,drive=disk,vdev=xvda \\ - -drive file=${GUEST_IMAGE},index=2,media=disk,file.locking=off,if=ide - -It is necessary to use the pc machine type, as the q35 machine uses AHCI instead -of legacy IDE, and AHCI disks are not unplugged through the Xen PV unplug -mechanism. - -VirtIO devices can also be used; Linux guests may need to be dissuaded from -umplugging them by adding 'xen_emul_unplug=never' on their command line. - Properties ---------- @@ -63,7 +41,10 @@ The following properties exist on the KVM accelerator object: ``xen-version`` This property contains the Xen version in ``XENVER_version`` form, with the major version in the top 16 bits and the minor version in the low 16 bits. - Setting this property enables the Xen guest support. + Setting this property enables the Xen guest support. If Xen version 4.5 or + greater is specified, the HVM leaf in Xen CPUID is populated. Xen version + 4.6 enables the vCPU ID in CPUID, and version 4.17 advertises vCPU upcall + vector support to the guest. ``xen-evtchn-max-pirq`` Xen PIRQs represent an emulated physical interrupt, either GSI or MSI, which @@ -83,8 +64,78 @@ The following properties exist on the KVM accelerator object: through simultaneous grants. For guests with large numbers of PV devices and high throughput, it may be desirable to increase this value. -OS requirements ---------------- +Xen paravirtual devices +----------------------- + +The Xen PCI platform device is enabled automatically for a Xen guest. This +allows a guest to unplug all emulated devices, in order to use paravirtual +block and network drivers instead. + +Those paravirtual Xen block, network (and console) devices can be created +through the command line, and/or hot-plugged. + +To provide a Xen console device, define a character device and then a device +of type ``xen-console`` to connect to it. For the Xen console equivalent of +the handy ``-serial mon:stdio`` option, for example: + +.. parsed-literal:: + -chardev stdio,mux=on,id=char0,signal=off -mon char0 \\ + -device xen-console,chardev=char0 + +The Xen network device is ``xen-net-device``, which becomes the default NIC +model for emulated Xen guests, meaning that just the default NIC provided +by QEMU should automatically work and present a Xen network device to the +guest. + +Disks can be configured with '``-drive file=${GUEST_IMAGE},if=xen``' and will +appear to the guest as ``xvda`` onwards. + +Under Xen, the boot disk is typically available both via IDE emulation, and +as a PV block device. Guest bootloaders typically use IDE to load the guest +kernel, which then unplugs the IDE and continues with the Xen PV block device. + +This configuration can be achieved as follows: + +.. parsed-literal:: + + |qemu_system| --accel kvm,xen-version=0x40011,kernel-irqchip=split \\ + -drive file=${GUEST_IMAGE},if=xen \\ + -drive file=${GUEST_IMAGE},file.locking=off,if=ide + +VirtIO devices can also be used; Linux guests may need to be dissuaded from +umplugging them by adding '``xen_emul_unplug=never``' on their command line. + +Booting Xen PV guests +--------------------- + +Booting PV guest kernels is possible by using the Xen PV shim (a version of Xen +itself, designed to run inside a Xen HVM guest and provide memory management +services for one guest alone). + +The Xen binary is provided as the ``-kernel`` and the guest kernel itself (or +PV Grub image) as the ``-initrd`` image, which actually just means the first +multiboot "module". For example: + +.. parsed-literal:: + + |qemu_system| --accel kvm,xen-version=0x40011,kernel-irqchip=split \\ + -chardev stdio,id=char0 -device xen-console,chardev=char0 \\ + -display none -m 1G -kernel xen -initrd bzImage \\ + -append "pv-shim console=xen,pv -- console=hvc0 root=/dev/xvda1" \\ + -drive file=${GUEST_IMAGE},if=xen + +The Xen image must be built with the ``CONFIG_XEN_GUEST`` and ``CONFIG_PV_SHIM`` +options, and as of Xen 4.17, Xen's PV shim mode does not support using a serial +port; it must have a Xen console or it will panic. + +The example above provides the guest kernel command line after a separator +(" ``--`` ") on the Xen command line, and does not provide the guest kernel +with an actual initramfs, which would need to listed as a second multiboot +module. For more complicated alternatives, see the command line +documentation for the ``-initrd`` option. + +Host OS requirements +-------------------- The minimal Xen support in the KVM accelerator requires the host to be running Linux v5.12 or newer. Later versions add optimisations: Linux v5.17 added diff --git a/qemu-options.hx b/qemu-options.hx index 7809036d8c..3eee3c33eb 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -3986,14 +3986,22 @@ ERST DEF("initrd", HAS_ARG, QEMU_OPTION_initrd, \ "-initrd file use 'file' as initial ram disk\n", QEMU_ARCH_ALL) SRST + ``-initrd file`` Use file as initial ram disk. ``-initrd "file1 arg=foo,file2"`` This syntax is only available with multiboot. - Use file1 and file2 as modules and pass arg=foo as parameter to the - first module. + Use file1 and file2 as modules and pass ``arg=foo`` as parameter to the + first module. Commas can be provided in module parameters by doubling + them on the command line to escape them: + +``-initrd "bzImage earlyprintk=xen,,keep root=/dev/xvda1,initrd.img"`` + Multiboot only. Use bzImage as the first module with + "``earlyprintk=xen,keep root=/dev/xvda1``" as its command line, + and initrd.img as the second module. + ERST DEF("dtb", HAS_ARG, QEMU_OPTION_dtb, \ From cca157568615465887a6f741d939865659a559ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:38:25 +0400 Subject: [PATCH 740/974] build-sys: add a "pixman" feature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For now, pixman is mandatory, but we set config_host.h and Kconfig. Once compilation is fixed, "pixman" will become actually optional. Signed-off-by: Marc-André Lureau Reviewed-by: Thomas Huth --- Kconfig.host | 3 +++ include/ui/qemu-pixman.h | 2 ++ meson.build | 10 ++++++++-- meson_options.txt | 2 ++ scripts/meson-buildoptions.sh | 3 +++ 5 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Kconfig.host b/Kconfig.host index 2ee71578f3..f496475f8e 100644 --- a/Kconfig.host +++ b/Kconfig.host @@ -11,6 +11,9 @@ config OPENGL config X11 bool +config PIXMAN + bool + config SPICE bool diff --git a/include/ui/qemu-pixman.h b/include/ui/qemu-pixman.h index e587c48b1f..d37feb5e3c 100644 --- a/include/ui/qemu-pixman.h +++ b/include/ui/qemu-pixman.h @@ -6,11 +6,13 @@ #ifndef QEMU_PIXMAN_H #define QEMU_PIXMAN_H +#ifdef CONFIG_PIXMAN /* pixman-0.16.0 headers have a redundant declaration */ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wredundant-decls" #include #pragma GCC diagnostic pop +#endif /* * pixman image formats are defined to be native endian, diff --git a/meson.build b/meson.build index 51a51075db..505cc591b9 100644 --- a/meson.build +++ b/meson.build @@ -813,10 +813,14 @@ if 'ust' in get_option('trace_backends') method: 'pkg-config') endif pixman = not_found -if have_system or have_tools - pixman = dependency('pixman-1', required: have_system, version:'>=0.21.8', +if not get_option('pixman').auto() or have_system or have_tools + pixman = dependency('pixman-1', required: get_option('pixman'), version:'>=0.21.8', method: 'pkg-config') endif +if not pixman.found() and (have_system or have_tools) + error('FIXME: pixman is currently required') +endif + zlib = dependency('zlib', required: true) libaio = not_found @@ -2149,6 +2153,7 @@ config_host_data.set('CONFIG_SECCOMP', seccomp.found()) if seccomp.found() config_host_data.set('CONFIG_SECCOMP_SYSRAWRC', seccomp_has_sysrawrc) endif +config_host_data.set('CONFIG_PIXMAN', pixman.found()) config_host_data.set('CONFIG_SNAPPY', snappy.found()) config_host_data.set('CONFIG_SOLARIS', targetos == 'sunos') if get_option('tcg').allowed() @@ -2868,6 +2873,7 @@ have_ivshmem = config_host_data.get('CONFIG_EVENTFD') host_kconfig = \ (get_option('fuzzing') ? ['CONFIG_FUZZ=y'] : []) + \ (have_tpm ? ['CONFIG_TPM=y'] : []) + \ + (pixman.found() ? ['CONFIG_PIXMAN=y'] : []) + \ (spice.found() ? ['CONFIG_SPICE=y'] : []) + \ (have_ivshmem ? ['CONFIG_IVSHMEM=y'] : []) + \ (opengl.found() ? ['CONFIG_OPENGL=y'] : []) + \ diff --git a/meson_options.txt b/meson_options.txt index 5c212fcd45..c9baeda639 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -226,6 +226,8 @@ option('l2tpv3', type : 'feature', value : 'auto', description: 'l2tpv3 network backend support') option('netmap', type : 'feature', value : 'auto', description: 'netmap network backend support') +option('pixman', type : 'feature', value : 'auto', + description: 'pixman support') option('slirp', type: 'feature', value: 'auto', description: 'libslirp user mode network backend support') option('vde', type : 'feature', value : 'auto', diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index e9d6d39279..680fa3f581 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -160,6 +160,7 @@ meson_options_help() { printf "%s\n" ' pa PulseAudio sound support' printf "%s\n" ' parallels parallels image format support' printf "%s\n" ' pipewire PipeWire sound support' + printf "%s\n" ' pixman pixman support' printf "%s\n" ' plugins TCG plugins via shared library loading' printf "%s\n" ' png PNG support with libpng' printf "%s\n" ' pvrdma Enable PVRDMA support' @@ -419,6 +420,8 @@ _meson_option_parse() { --disable-parallels) printf "%s" -Dparallels=disabled ;; --enable-pipewire) printf "%s" -Dpipewire=enabled ;; --disable-pipewire) printf "%s" -Dpipewire=disabled ;; + --enable-pixman) printf "%s" -Dpixman=enabled ;; + --disable-pixman) printf "%s" -Dpixman=disabled ;; --with-pkgversion=*) quote_sh "-Dpkgversion=$2" ;; --enable-plugins) printf "%s" -Dplugins=true ;; --disable-plugins) printf "%s" -Dplugins=false ;; From ce59c54c4927d22e7d564f68f29c697d77ba9b86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 25 Oct 2023 16:04:45 +0400 Subject: [PATCH 741/974] build-sys: drop needless warning pragmas for old pixman MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 236f282c1c7 ("configure: check for pixman-1 version"), QEMU requires >= 0.21.8. Suggested-by: Thomas Huth Signed-off-by: Marc-André Lureau Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- include/ui/qemu-pixman.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/ui/qemu-pixman.h b/include/ui/qemu-pixman.h index d37feb5e3c..4bfa8fae0c 100644 --- a/include/ui/qemu-pixman.h +++ b/include/ui/qemu-pixman.h @@ -7,11 +7,7 @@ #define QEMU_PIXMAN_H #ifdef CONFIG_PIXMAN -/* pixman-0.16.0 headers have a redundant declaration */ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wredundant-decls" #include -#pragma GCC diagnostic pop #endif /* From b3ec48cf9241efd443caebb732587ac9cb33094b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:38:26 +0400 Subject: [PATCH 742/974] ui: compile out some qemu-pixman functions when !PIXMAN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Those functions require the PIXMAN library. Signed-off-by: Marc-André Lureau Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- include/ui/qemu-pixman.h | 7 +++++-- ui/qemu-pixman.c | 6 ++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/include/ui/qemu-pixman.h b/include/ui/qemu-pixman.h index 4bfa8fae0c..c140cd84b6 100644 --- a/include/ui/qemu-pixman.h +++ b/include/ui/qemu-pixman.h @@ -72,17 +72,17 @@ pixman_format_code_t qemu_default_pixman_format(int bpp, bool native_endian); pixman_format_code_t qemu_drm_format_to_pixman(uint32_t drm_format); uint32_t qemu_pixman_to_drm_format(pixman_format_code_t pixman); int qemu_pixman_get_type(int rshift, int gshift, int bshift); -pixman_format_code_t qemu_pixman_get_format(PixelFormat *pf); bool qemu_pixman_check_format(DisplayChangeListener *dcl, pixman_format_code_t format); +#ifdef CONFIG_PIXMAN +pixman_format_code_t qemu_pixman_get_format(PixelFormat *pf); pixman_image_t *qemu_pixman_linebuf_create(pixman_format_code_t format, int width); void qemu_pixman_linebuf_fill(pixman_image_t *linebuf, pixman_image_t *fb, int width, int x, int y); pixman_image_t *qemu_pixman_mirror_create(pixman_format_code_t format, pixman_image_t *image); -void qemu_pixman_image_unref(pixman_image_t *image); pixman_image_t *qemu_pixman_glyph_from_vgafont(int height, const uint8_t *font, unsigned int ch); @@ -91,6 +91,9 @@ void qemu_pixman_glyph_render(pixman_image_t *glyph, pixman_color_t *fgcol, pixman_color_t *bgcol, int x, int y, int cw, int ch); +#endif + +void qemu_pixman_image_unref(pixman_image_t *image); G_DEFINE_AUTOPTR_CLEANUP_FUNC(pixman_image_t, qemu_pixman_image_unref) diff --git a/ui/qemu-pixman.c b/ui/qemu-pixman.c index b43ec38bf0..5ca55dd199 100644 --- a/ui/qemu-pixman.c +++ b/ui/qemu-pixman.c @@ -145,6 +145,7 @@ int qemu_pixman_get_type(int rshift, int gshift, int bshift) return type; } +#ifdef CONFIG_PIXMAN pixman_format_code_t qemu_pixman_get_format(PixelFormat *pf) { pixman_format_code_t format; @@ -158,6 +159,7 @@ pixman_format_code_t qemu_pixman_get_format(PixelFormat *pf) } return format; } +#endif /* * Return true for known-good pixman conversions. @@ -186,6 +188,7 @@ bool qemu_pixman_check_format(DisplayChangeListener *dcl, } } +#ifdef CONFIG_PIXMAN pixman_image_t *qemu_pixman_linebuf_create(pixman_format_code_t format, int width) { @@ -211,6 +214,7 @@ pixman_image_t *qemu_pixman_mirror_create(pixman_format_code_t format, NULL, pixman_image_get_stride(image)); } +#endif void qemu_pixman_image_unref(pixman_image_t *image) { @@ -220,6 +224,7 @@ void qemu_pixman_image_unref(pixman_image_t *image) pixman_image_unref(image); } +#ifdef CONFIG_PIXMAN pixman_image_t *qemu_pixman_glyph_from_vgafont(int height, const uint8_t *font, unsigned int ch) { @@ -262,3 +267,4 @@ void qemu_pixman_glyph_render(pixman_image_t *glyph, pixman_image_unref(ifg); pixman_image_unref(ibg); } +#endif /* CONFIG_PIXMAN */ From 79a1f32559cbd1f2d8c6fc96c581fd460317a284 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 25 Oct 2023 16:15:24 +0400 Subject: [PATCH 743/974] ui: add pixman-minimal.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a tiny subset of PIXMAN API that is used pervasively in QEMU codebase to manage images and identify the underlying format. It doesn't seems worth to wrap this in a QEMU-specific API. Signed-off-by: Marc-André Lureau Acked-by: Thomas Huth --- include/ui/pixman-minimal.h | 195 ++++++++++++++++++++++++++++++++++++ include/ui/qemu-pixman.h | 2 + 2 files changed, 197 insertions(+) create mode 100644 include/ui/pixman-minimal.h diff --git a/include/ui/pixman-minimal.h b/include/ui/pixman-minimal.h new file mode 100644 index 0000000000..efcf570c9e --- /dev/null +++ b/include/ui/pixman-minimal.h @@ -0,0 +1,195 @@ +/* + * SPDX-License-Identifier: MIT + * + * Tiny subset of PIXMAN API commonly used by QEMU. + * + * Copyright 1987, 1988, 1989, 1998 The Open Group + * Copyright 1987, 1988, 1989 Digital Equipment Corporation + * Copyright 1999, 2004, 2008 Keith Packard + * Copyright 2000 SuSE, Inc. + * Copyright 2000 Keith Packard, member of The XFree86 Project, Inc. + * Copyright 2004, 2005, 2007, 2008, 2009, 2010 Red Hat, Inc. + * Copyright 2004 Nicholas Miell + * Copyright 2005 Lars Knoll & Zack Rusin, Trolltech + * Copyright 2005 Trolltech AS + * Copyright 2007 Luca Barbato + * Copyright 2008 Aaron Plattner, NVIDIA Corporation + * Copyright 2008 Rodrigo Kumpera + * Copyright 2008 André Tupinambá + * Copyright 2008 Mozilla Corporation + * Copyright 2008 Frederic Plourde + * Copyright 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright 2009, 2010 Nokia Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef PIXMAN_MINIMAL_H +#define PIXMAN_MINIMAL_H + +#define PIXMAN_TYPE_OTHER 0 +#define PIXMAN_TYPE_ARGB 2 +#define PIXMAN_TYPE_ABGR 3 +#define PIXMAN_TYPE_BGRA 8 +#define PIXMAN_TYPE_RGBA 9 + +#define PIXMAN_FORMAT(bpp, type, a, r, g, b) (((bpp) << 24) | \ + ((type) << 16) | \ + ((a) << 12) | \ + ((r) << 8) | \ + ((g) << 4) | \ + ((b))) + +#define PIXMAN_FORMAT_RESHIFT(val, ofs, num) \ + (((val >> (ofs)) & ((1 << (num)) - 1)) << ((val >> 22) & 3)) + +#define PIXMAN_FORMAT_BPP(f) PIXMAN_FORMAT_RESHIFT(f, 24, 8) +#define PIXMAN_FORMAT_TYPE(f) (((f) >> 16) & 0x3f) +#define PIXMAN_FORMAT_A(f) PIXMAN_FORMAT_RESHIFT(f, 12, 4) +#define PIXMAN_FORMAT_R(f) PIXMAN_FORMAT_RESHIFT(f, 8, 4) +#define PIXMAN_FORMAT_G(f) PIXMAN_FORMAT_RESHIFT(f, 4, 4) +#define PIXMAN_FORMAT_B(f) PIXMAN_FORMAT_RESHIFT(f, 0, 4) +#define PIXMAN_FORMAT_DEPTH(f) (PIXMAN_FORMAT_A(f) + \ + PIXMAN_FORMAT_R(f) + \ + PIXMAN_FORMAT_G(f) + \ + PIXMAN_FORMAT_B(f)) + +typedef enum { + /* 32bpp formats */ + PIXMAN_a8r8g8b8 = PIXMAN_FORMAT(32, PIXMAN_TYPE_ARGB, 8, 8, 8, 8), + PIXMAN_x8r8g8b8 = PIXMAN_FORMAT(32, PIXMAN_TYPE_ARGB, 0, 8, 8, 8), + PIXMAN_a8b8g8r8 = PIXMAN_FORMAT(32, PIXMAN_TYPE_ABGR, 8, 8, 8, 8), + PIXMAN_x8b8g8r8 = PIXMAN_FORMAT(32, PIXMAN_TYPE_ABGR, 0, 8, 8, 8), + PIXMAN_b8g8r8a8 = PIXMAN_FORMAT(32, PIXMAN_TYPE_BGRA, 8, 8, 8, 8), + PIXMAN_b8g8r8x8 = PIXMAN_FORMAT(32, PIXMAN_TYPE_BGRA, 0, 8, 8, 8), + PIXMAN_r8g8b8a8 = PIXMAN_FORMAT(32, PIXMAN_TYPE_RGBA, 8, 8, 8, 8), + PIXMAN_r8g8b8x8 = PIXMAN_FORMAT(32, PIXMAN_TYPE_RGBA, 0, 8, 8, 8), + /* 24bpp formats */ + PIXMAN_r8g8b8 = PIXMAN_FORMAT(24, PIXMAN_TYPE_ARGB, 0, 8, 8, 8), + PIXMAN_b8g8r8 = PIXMAN_FORMAT(24, PIXMAN_TYPE_ABGR, 0, 8, 8, 8), + /* 16bpp formats */ + PIXMAN_r5g6b5 = PIXMAN_FORMAT(16, PIXMAN_TYPE_ARGB, 0, 5, 6, 5), + PIXMAN_a1r5g5b5 = PIXMAN_FORMAT(16, PIXMAN_TYPE_ARGB, 1, 5, 5, 5), + PIXMAN_x1r5g5b5 = PIXMAN_FORMAT(16, PIXMAN_TYPE_ARGB, 0, 5, 5, 5), +} pixman_format_code_t; + +typedef struct pixman_image pixman_image_t; + +typedef void (*pixman_image_destroy_func_t)(pixman_image_t *image, void *data); + +struct pixman_image { + int ref_count; + pixman_format_code_t format; + int width; + int height; + int stride; + uint32_t *data; + uint32_t *free_me; + pixman_image_destroy_func_t destroy_func; + void *destroy_data; +}; + +typedef struct pixman_color { + uint16_t red; + uint16_t green; + uint16_t blue; + uint16_t alpha; +} pixman_color_t; + +static inline pixman_image_t *pixman_image_create_bits(pixman_format_code_t format, + int width, + int height, + uint32_t *bits, + int rowstride_bytes) +{ + pixman_image_t *i = g_new0(pixman_image_t, 1); + + i->width = width; + i->height = height; + i->stride = rowstride_bytes ?: width * DIV_ROUND_UP(PIXMAN_FORMAT_BPP(format), 8); + i->format = format; + if (bits) { + i->data = bits; + } else { + i->free_me = i->data = g_malloc0(rowstride_bytes * height); + } + i->ref_count = 1; + + return i; +} + +static inline pixman_image_t *pixman_image_ref(pixman_image_t *i) +{ + i->ref_count++; + return i; +} + +static inline bool pixman_image_unref(pixman_image_t *i) +{ + i->ref_count--; + + if (i->ref_count == 0) { + if (i->destroy_func) { + i->destroy_func(i, i->destroy_data); + } + g_free(i->free_me); + g_free(i); + + return true; + } + + return false; +} + +static inline void pixman_image_set_destroy_function(pixman_image_t *i, + pixman_image_destroy_func_t func, + void *data) + +{ + i->destroy_func = func; + i->destroy_data = data; +} + +static inline uint32_t *pixman_image_get_data(pixman_image_t *i) +{ + return i->data; +} + +static inline int pixman_image_get_height(pixman_image_t *i) +{ + return i->height; +} + +static inline int pixman_image_get_width(pixman_image_t *i) +{ + return i->width; +} + +static inline int pixman_image_get_stride(pixman_image_t *i) +{ + return i->stride; +} + +static inline pixman_format_code_t pixman_image_get_format(pixman_image_t *i) +{ + return i->format; +} + +#endif /* PIXMAN_MINIMAL_H */ diff --git a/include/ui/qemu-pixman.h b/include/ui/qemu-pixman.h index c140cd84b6..ef13a8210c 100644 --- a/include/ui/qemu-pixman.h +++ b/include/ui/qemu-pixman.h @@ -8,6 +8,8 @@ #ifdef CONFIG_PIXMAN #include +#else +#include "pixman-minimal.h" #endif /* From 6d9ed4eb9470db8fb7294baa83a73b854419bcc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 25 Oct 2023 16:52:56 +0400 Subject: [PATCH 744/974] vl: drop needless -spice checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 5324e3e958e ("qemu-options: define -spice only #ifdef CONFIG_SPICE"), it is unnecessary to check at runtime for "-spice" option. Signed-off-by: Marc-André Lureau Reviewed-by: Thomas Huth --- system/vl.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/system/vl.c b/system/vl.c index 3fb569254a..fb0389e4d0 100644 --- a/system/vl.c +++ b/system/vl.c @@ -3475,12 +3475,7 @@ void qemu_init(int argc, char **argv) break; #ifdef CONFIG_SPICE case QEMU_OPTION_spice: - olist = qemu_find_opts_err("spice", NULL); - if (!olist) { - error_report("spice support is disabled"); - exit(1); - } - opts = qemu_opts_parse_noisily(olist, optarg, false); + opts = qemu_opts_parse_noisily(qemu_find_opts("spice"), optarg, false); if (!opts) { exit(1); } From 6261164bd600422f1c557b5a1dea124a83181881 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 25 Oct 2023 17:05:08 +0400 Subject: [PATCH 745/974] qemu-options: define -vnc only #ifdef CONFIG_VNC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Thomas Huth --- qemu-options.hx | 2 ++ system/vl.c | 13 +++++++++---- ui/vnc-stubs.c | 12 ------------ 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/qemu-options.hx b/qemu-options.hx index 7809036d8c..5b6d16ed58 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -2428,8 +2428,10 @@ SRST OBP. ERST +#ifdef CONFIG_VNC DEF("vnc", HAS_ARG, QEMU_OPTION_vnc , "-vnc shorthand for -display vnc=\n", QEMU_ARCH_ALL) +#endif SRST ``-vnc display[,option[,option[,...]]]`` Normally, if QEMU is compiled with graphical window support, it diff --git a/system/vl.c b/system/vl.c index fb0389e4d0..19aef762e4 100644 --- a/system/vl.c +++ b/system/vl.c @@ -1095,13 +1095,14 @@ DisplayOptions *qmp_query_display_options(Error **errp) static void parse_display(const char *p) { - const char *opts; - if (is_help_option(p)) { qemu_display_help(); exit(0); } +#ifdef CONFIG_VNC + const char *opts; + if (strstart(p, "vnc", &opts)) { /* * vnc isn't a (local) DisplayType but a protocol for remote @@ -1113,9 +1114,11 @@ static void parse_display(const char *p) error_report("VNC requires a display argument vnc="); exit(1); } - } else { - parse_display_qapi(p); + return; } +#endif + + parse_display_qapi(p); } static inline bool nonempty_str(const char *str) @@ -3344,9 +3347,11 @@ void qemu_init(int argc, char **argv) machine_parse_property_opt(qemu_find_opts("smp-opts"), "smp", optarg); break; +#ifdef CONFIG_VNC case QEMU_OPTION_vnc: vnc_parse(optarg); break; +#endif case QEMU_OPTION_no_acpi: warn_report("-no-acpi is deprecated, use '-machine acpi=off' instead"); qdict_put_str(machine_opts_dict, "acpi", "off"); diff --git a/ui/vnc-stubs.c b/ui/vnc-stubs.c index b4eb3ce718..a96bc86236 100644 --- a/ui/vnc-stubs.c +++ b/ui/vnc-stubs.c @@ -10,15 +10,3 @@ int vnc_display_pw_expire(const char *id, time_t expires) { return -ENODEV; }; -void vnc_parse(const char *str) -{ - if (strcmp(str, "none") == 0) { - return; - } - error_setg(&error_fatal, "VNC support is disabled"); -} -int vnc_init_func(void *opaque, QemuOpts *opts, Error **errp) -{ - error_setg(errp, "VNC support is disabled"); - return -1; -} From 484629fc8141eaa257f961b5e5e310a1bbd0f1a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 25 Oct 2023 17:21:17 +0400 Subject: [PATCH 746/974] vl: simplify display_remote logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bump the display_remote variable when the -vnc option is parsed, just like -spice. Signed-off-by: Marc-André Lureau Reviewed-by: Thomas Huth --- system/vl.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/system/vl.c b/system/vl.c index 19aef762e4..e9819408df 100644 --- a/system/vl.c +++ b/system/vl.c @@ -1401,11 +1401,6 @@ static void qemu_create_default_devices(void) #endif } -#if defined(CONFIG_VNC) - if (!QTAILQ_EMPTY(&(qemu_find_opts("vnc")->head))) { - display_remote++; - } -#endif if (dpy.type == DISPLAY_TYPE_DEFAULT && !display_remote) { if (!qemu_display_find_default(&dpy)) { dpy.type = DISPLAY_TYPE_NONE; @@ -3350,6 +3345,7 @@ void qemu_init(int argc, char **argv) #ifdef CONFIG_VNC case QEMU_OPTION_vnc: vnc_parse(optarg); + display_remote++; break; #endif case QEMU_OPTION_no_acpi: From 2aeaa4b2000d95139d5dfae8ca5061c1b19e749e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 18 Oct 2023 17:16:32 +0400 Subject: [PATCH 747/974] vl: move display early init before default devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The next commit needs to have the display registered itself before creating the default VCs. Signed-off-by: Marc-André Lureau Reviewed-by: Thomas Huth --- system/vl.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/system/vl.c b/system/vl.c index e9819408df..cf46e438cc 100644 --- a/system/vl.c +++ b/system/vl.c @@ -1352,6 +1352,23 @@ static void qemu_disable_default_devices(void) } } +static void qemu_setup_display(void) +{ + if (dpy.type == DISPLAY_TYPE_DEFAULT && !display_remote) { + if (!qemu_display_find_default(&dpy)) { + dpy.type = DISPLAY_TYPE_NONE; +#if defined(CONFIG_VNC) + vnc_parse("localhost:0,to=99,id=default"); +#endif + } + } + if (dpy.type == DISPLAY_TYPE_DEFAULT) { + dpy.type = DISPLAY_TYPE_NONE; + } + + qemu_display_early_init(&dpy); +} + static void qemu_create_default_devices(void) { MachineClass *machine_class = MACHINE_GET_CLASS(current_machine); @@ -1401,18 +1418,6 @@ static void qemu_create_default_devices(void) #endif } - if (dpy.type == DISPLAY_TYPE_DEFAULT && !display_remote) { - if (!qemu_display_find_default(&dpy)) { - dpy.type = DISPLAY_TYPE_NONE; -#if defined(CONFIG_VNC) - vnc_parse("localhost:0,to=99,id=default"); -#endif - } - } - if (dpy.type == DISPLAY_TYPE_DEFAULT) { - dpy.type = DISPLAY_TYPE_NONE; - } - /* If no default VGA is requested, the default is "none". */ if (default_vga) { vga_model = get_default_vga_model(machine_class); @@ -1937,7 +1942,6 @@ static void qemu_create_early_backends(void) "ignoring option"); } - qemu_display_early_init(&dpy); qemu_console_early_init(); if (dpy.has_gl && dpy.gl != DISPLAYGL_MODE_OFF && display_opengl == 0) { @@ -3666,6 +3670,7 @@ void qemu_init(int argc, char **argv) suspend_mux_open(); qemu_disable_default_devices(); + qemu_setup_display(); qemu_create_default_devices(); qemu_create_early_backends(); From 1bec1cc0da497e55c16e2a7b50f94cdb2a02197f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 5 Sep 2023 23:18:08 +0400 Subject: [PATCH 748/974] ui/console: allow to override the default VC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a display is backed by a specialized VC, allow to override the default "vc:80Cx24C". As suggested by Paolo, if the display doesn't implement a VC (get_vc() returns NULL), use a fallback that will use a muxed console on stdio. This changes the behaviour of "qemu -display none", to create a muxed serial/monitor by default (on TTY & not daemonized). Signed-off-by: Marc-André Lureau Reviewed-by: Thomas Huth --- include/ui/console.h | 2 ++ system/vl.c | 27 +++++++++++++++++---------- ui/console.c | 14 ++++++++++++++ 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/include/ui/console.h b/include/ui/console.h index acb61a7f15..a4a49ffc64 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -462,12 +462,14 @@ struct QemuDisplay { DisplayType type; void (*early_init)(DisplayOptions *opts); void (*init)(DisplayState *ds, DisplayOptions *opts); + const char *vc; }; void qemu_display_register(QemuDisplay *ui); bool qemu_display_find_default(DisplayOptions *opts); void qemu_display_early_init(DisplayOptions *opts); void qemu_display_init(DisplayState *ds, DisplayOptions *opts); +const char *qemu_display_get_vc(DisplayOptions *opts); void qemu_display_help(void); /* vnc.c */ diff --git a/system/vl.c b/system/vl.c index cf46e438cc..bd7fad770b 100644 --- a/system/vl.c +++ b/system/vl.c @@ -1372,6 +1372,7 @@ static void qemu_setup_display(void) static void qemu_create_default_devices(void) { MachineClass *machine_class = MACHINE_GET_CLASS(current_machine); + const char *vc = qemu_display_get_vc(&dpy); if (is_daemonized()) { /* According to documentation and historically, -nographic redirects @@ -1390,24 +1391,30 @@ static void qemu_create_default_devices(void) } } - if (nographic) { - if (default_parallel) + if (nographic || (!vc && !is_daemonized() && isatty(STDOUT_FILENO))) { + if (default_parallel) { add_device_config(DEV_PARALLEL, "null"); + } if (default_serial && default_monitor) { add_device_config(DEV_SERIAL, "mon:stdio"); } else { - if (default_serial) + if (default_serial) { add_device_config(DEV_SERIAL, "stdio"); - if (default_monitor) + } + if (default_monitor) { monitor_parse("stdio", "readline", false); + } } } else { - if (default_serial) - add_device_config(DEV_SERIAL, "vc:80Cx24C"); - if (default_parallel) - add_device_config(DEV_PARALLEL, "vc:80Cx24C"); - if (default_monitor) - monitor_parse("vc:80Cx24C", "readline", false); + if (default_serial) { + add_device_config(DEV_SERIAL, vc ?: "null"); + } + if (default_parallel) { + add_device_config(DEV_PARALLEL, vc ?: "null"); + } + if (default_monitor && vc) { + monitor_parse(vc, "readline", false); + } } if (default_net) { diff --git a/ui/console.c b/ui/console.c index 8ee66d10c5..a758ed62ad 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1675,6 +1675,20 @@ void qemu_display_init(DisplayState *ds, DisplayOptions *opts) dpys[opts->type]->init(ds, opts); } +const char *qemu_display_get_vc(DisplayOptions *opts) +{ + assert(opts->type < DISPLAY_TYPE__MAX); + if (opts->type == DISPLAY_TYPE_NONE) { + return NULL; + } + assert(dpys[opts->type] != NULL); + if (dpys[opts->type]->vc) { + return dpys[opts->type]->vc; + } else { + return "vc:80Cx24C"; + } +} + void qemu_display_help(void) { int idx; From 600179c39efb30cf072e18c7604ccf52360684c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:38:28 +0400 Subject: [PATCH 749/974] ui/vc: console-vc requires PIXMAN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add stubs for the fallback paths. get_vc() now returns NULL by default if !PIXMAN. Signed-off-by: Marc-André Lureau Reviewed-by: Thomas Huth --- ui/console-vc-stubs.c | 33 +++++++++++++++++++++++++++++++++ ui/console.c | 3 +++ ui/meson.build | 2 +- 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 ui/console-vc-stubs.c diff --git a/ui/console-vc-stubs.c b/ui/console-vc-stubs.c new file mode 100644 index 0000000000..2afc52329f --- /dev/null +++ b/ui/console-vc-stubs.c @@ -0,0 +1,33 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * QEMU VC stubs + */ +#include "qemu/osdep.h" + +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/option.h" +#include "chardev/char.h" +#include "ui/console-priv.h" + +void qemu_text_console_select(QemuTextConsole *c) +{ +} + +const char * +qemu_text_console_get_label(QemuTextConsole *c) +{ + return NULL; +} + +void qemu_text_console_update_cursor(void) +{ +} + +void qemu_text_console_handle_keysym(QemuTextConsole *s, int keysym) +{ +} + +void qemu_console_early_init(void) +{ +} diff --git a/ui/console.c b/ui/console.c index a758ed62ad..a72c495b5a 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1685,8 +1685,11 @@ const char *qemu_display_get_vc(DisplayOptions *opts) if (dpys[opts->type]->vc) { return dpys[opts->type]->vc; } else { +#ifdef CONFIG_PIXMAN return "vc:80Cx24C"; +#endif } + return NULL; } void qemu_display_help(void) diff --git a/ui/meson.build b/ui/meson.build index 0a1e8272a3..3085e10a72 100644 --- a/ui/meson.build +++ b/ui/meson.build @@ -6,7 +6,6 @@ system_ss.add(png) system_ss.add(files( 'clipboard.c', 'console.c', - 'console-vc.c', 'cursor.c', 'input-keymap.c', 'input-legacy.c', @@ -19,6 +18,7 @@ system_ss.add(files( 'ui-qmp-cmds.c', 'util.c', )) +system_ss.add(when: pixman, if_true: files('console-vc.c'), if_false: files('console-vc-stubs.c')) if dbus_display system_ss.add(files('dbus-module.c')) endif From f38aa2c7c0c85d8f978a3c23ab0a32578fc73134 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:38:29 +0400 Subject: [PATCH 750/974] qmp/hmp: disable screendump if PIXMAN is missing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The command requires color conversion and line-by-line feeding. We could have a simple fallback for simple formats though. Signed-off-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth --- hmp-commands.hx | 2 ++ qapi/ui.json | 3 ++- ui/ui-hmp-cmds.c | 2 ++ ui/ui-qmp-cmds.c | 2 ++ 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index c0a27688b6..765349ed14 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -252,6 +252,7 @@ SRST ERST +#ifdef CONFIG_PIXMAN { .name = "screendump", .args_type = "filename:F,format:-fs,device:s?,head:i?", @@ -267,6 +268,7 @@ SRST ``screendump`` *filename* Save screen into PPM image *filename*. ERST +#endif { .name = "logfile", diff --git a/qapi/ui.json b/qapi/ui.json index 006616aa77..e74cc3efb6 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -200,7 +200,8 @@ { 'command': 'screendump', 'data': {'filename': 'str', '*device': 'str', '*head': 'int', '*format': 'ImageFormat'}, - 'coroutine': true } + 'coroutine': true, + 'if': 'CONFIG_PIXMAN' } ## # == Spice diff --git a/ui/ui-hmp-cmds.c b/ui/ui-hmp-cmds.c index c671389473..26c8ced1f2 100644 --- a/ui/ui-hmp-cmds.c +++ b/ui/ui-hmp-cmds.c @@ -437,6 +437,7 @@ void sendkey_completion(ReadLineState *rs, int nb_args, const char *str) } } +#ifdef CONFIG_PIXMAN void coroutine_fn hmp_screendump(Monitor *mon, const QDict *qdict) { @@ -458,6 +459,7 @@ hmp_screendump(Monitor *mon, const QDict *qdict) end: hmp_handle_error(mon, err); } +#endif void hmp_client_migrate_info(Monitor *mon, const QDict *qdict) { diff --git a/ui/ui-qmp-cmds.c b/ui/ui-qmp-cmds.c index debc07d678..d772e1cb7f 100644 --- a/ui/ui-qmp-cmds.c +++ b/ui/ui-qmp-cmds.c @@ -212,6 +212,7 @@ void qmp_client_migrate_info(const char *protocol, const char *hostname, error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "protocol", "'spice'"); } +#ifdef CONFIG_PIXMAN #ifdef CONFIG_PNG /** * png_save: Take a screenshot as PNG @@ -391,3 +392,4 @@ qmp_screendump(const char *filename, const char *device, } } } +#endif /* CONFIG_PIXMAN */ From a200d53b1fde2101ec624853139a52d2e4666208 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:38:30 +0400 Subject: [PATCH 751/974] virtio-gpu: replace PIXMAN for region/rect test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use a simpler implementation for rectangle geometry & intersect, drop the need for (more complex) PIXMAN functions. Signed-off-by: Marc-André Lureau Acked-by: Michael S. Tsirkin --- hw/display/virtio-gpu.c | 30 ++++++++------------- include/ui/rect.h | 59 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 19 deletions(-) create mode 100644 include/ui/rect.h diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 2707bceea8..b016d3bac8 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -16,6 +16,7 @@ #include "qemu/iov.h" #include "sysemu/cpus.h" #include "ui/console.h" +#include "ui/rect.h" #include "trace.h" #include "sysemu/dma.h" #include "sysemu/sysemu.h" @@ -503,7 +504,7 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g, struct virtio_gpu_simple_resource *res; struct virtio_gpu_resource_flush rf; struct virtio_gpu_scanout *scanout; - pixman_region16_t flush_region; + QemuRect flush_rect; bool within_bounds = false; bool update_submitted = false; int i; @@ -565,34 +566,25 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g, return; } - pixman_region_init_rect(&flush_region, - rf.r.x, rf.r.y, rf.r.width, rf.r.height); + qemu_rect_init(&flush_rect, rf.r.x, rf.r.y, rf.r.width, rf.r.height); for (i = 0; i < g->parent_obj.conf.max_outputs; i++) { - pixman_region16_t region, finalregion; - pixman_box16_t *extents; + QemuRect rect; if (!(res->scanout_bitmask & (1 << i))) { continue; } scanout = &g->parent_obj.scanout[i]; - pixman_region_init(&finalregion); - pixman_region_init_rect(®ion, scanout->x, scanout->y, - scanout->width, scanout->height); + qemu_rect_init(&rect, scanout->x, scanout->y, + scanout->width, scanout->height); - pixman_region_intersect(&finalregion, &flush_region, ®ion); - pixman_region_translate(&finalregion, -scanout->x, -scanout->y); - extents = pixman_region_extents(&finalregion); /* work out the area we need to update for each console */ - dpy_gfx_update(g->parent_obj.scanout[i].con, - extents->x1, extents->y1, - extents->x2 - extents->x1, - extents->y2 - extents->y1); - - pixman_region_fini(®ion); - pixman_region_fini(&finalregion); + if (qemu_rect_intersect(&flush_rect, &rect, &rect)) { + qemu_rect_translate(&rect, -scanout->x, -scanout->y); + dpy_gfx_update(g->parent_obj.scanout[i].con, + rect.x, rect.y, rect.width, rect.height); + } } - pixman_region_fini(&flush_region); } static void virtio_unref_resource(pixman_image_t *image, void *data) diff --git a/include/ui/rect.h b/include/ui/rect.h new file mode 100644 index 0000000000..94898f92d0 --- /dev/null +++ b/include/ui/rect.h @@ -0,0 +1,59 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef QEMU_RECT_H +#define QEMU_RECT_H + +#include +#include + +typedef struct QemuRect { + int16_t x; + int16_t y; + uint16_t width; + uint16_t height; +} QemuRect; + +static inline void qemu_rect_init(QemuRect *rect, + int16_t x, int16_t y, + uint16_t width, uint16_t height) +{ + rect->x = x; + rect->y = x; + rect->width = width; + rect->height = height; +} + +static inline void qemu_rect_translate(QemuRect *rect, + int16_t dx, int16_t dy) +{ + rect->x += dx; + rect->y += dy; +} + +static inline bool qemu_rect_intersect(const QemuRect *a, const QemuRect *b, + QemuRect *res) +{ + int16_t x1, x2, y1, y2; + + x1 = MAX(a->x, b->x); + y1 = MAX(a->y, b->y); + x2 = MIN(a->x + a->width, b->x + b->width); + y2 = MIN(a->y + a->height, b->y + b->height); + + if (x1 >= x2 || y1 >= y2) { + if (res) { + qemu_rect_init(res, 0, 0, 0, 0); + } + + return false; + } + + if (res) { + qemu_rect_init(res, x1, y1, x2 - x1, y2 - y1); + } + + return true; +} + +#endif From d7e947965a064d762ece7873c1e695f399f76bd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:38:31 +0400 Subject: [PATCH 752/974] ui/console: when PIXMAN is unavailable, don't draw placeholder msg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we can't draw text, simply show a blank display. Signed-off-by: Marc-André Lureau Reviewed-by: Thomas Huth --- ui/console.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/console.c b/ui/console.c index a72c495b5a..8e688d3569 100644 --- a/ui/console.c +++ b/ui/console.c @@ -584,6 +584,7 @@ DisplaySurface *qemu_create_placeholder_surface(int w, int h, const char *msg) { DisplaySurface *surface = qemu_create_displaysurface(w, h); +#ifdef CONFIG_PIXMAN pixman_color_t bg = QEMU_PIXMAN_COLOR_BLACK; pixman_color_t fg = QEMU_PIXMAN_COLOR_GRAY; pixman_image_t *glyph; @@ -598,6 +599,7 @@ DisplaySurface *qemu_create_placeholder_surface(int w, int h, x+i, y, FONT_WIDTH, FONT_HEIGHT); qemu_pixman_image_unref(glyph); } +#endif surface->flags |= QEMU_PLACEHOLDER_FLAG; return surface; } From 68fd167060e2b07741a03761264aa9495076a971 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:38:32 +0400 Subject: [PATCH 753/974] vhost-user-gpu: skip VHOST_USER_GPU_UPDATE when !PIXMAN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This simply means that 2d drawing updates won't be handled, but 3d should work. Signed-off-by: Marc-André Lureau Acked-by: Michael S. Tsirkin --- hw/display/vhost-user-gpu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c index 1150521d9d..709c8a02a1 100644 --- a/hw/display/vhost-user-gpu.c +++ b/hw/display/vhost-user-gpu.c @@ -307,6 +307,7 @@ vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg) dpy_gl_update(con, m->x, m->y, m->width, m->height); break; } +#ifdef CONFIG_PIXMAN case VHOST_USER_GPU_UPDATE: { VhostUserGpuUpdate *m = &msg->payload.update; @@ -334,6 +335,7 @@ vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg) } break; } +#endif default: g_warning("unhandled message %d %d", msg->request, msg->size); } From 41e0bc3d5cde54571945e9515a469b65d0f39be9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:38:33 +0400 Subject: [PATCH 754/974] ui/gl: opengl doesn't require PIXMAN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The QEMU fallback covers the requirements. We still need the flags of header inclusion with CONFIG_PIXMAN. Signed-off-by: Marc-André Lureau --- ui/meson.build | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/meson.build b/ui/meson.build index 3085e10a72..7c99613950 100644 --- a/ui/meson.build +++ b/ui/meson.build @@ -60,8 +60,8 @@ endif system_ss.add(opengl) if opengl.found() opengl_ss = ss.source_set() - opengl_ss.add(gbm) - opengl_ss.add(when: [opengl, pixman], + opengl_ss.add(gbm, pixman) + opengl_ss.add(when: [opengl], if_true: files('shader.c', 'console-gl.c', 'egl-helpers.c', 'egl-context.c')) ui_modules += {'opengl' : opengl_ss} endif From 89fd3eab52f4dcdd7fcbb6e30a78ad0c7f8b6e80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:38:34 +0400 Subject: [PATCH 755/974] ui/vnc: VNC requires PIXMAN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Thomas Huth --- meson.build | 6 +++++- ui/meson.build | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index 505cc591b9..3e60f42f94 100644 --- a/meson.build +++ b/meson.build @@ -1560,7 +1560,11 @@ endif vnc = not_found jpeg = not_found sasl = not_found -if get_option('vnc').allowed() and have_system +if get_option('vnc') \ + .disable_auto_if(not have_system) \ + .require(pixman.found(), + error_message: 'cannot enable VNC if pixman is not available') \ + .allowed() vnc = declare_dependency() # dummy dependency jpeg = dependency('libjpeg', required: get_option('vnc_jpeg'), method: 'pkg-config') diff --git a/ui/meson.build b/ui/meson.build index 7c99613950..19723188b5 100644 --- a/ui/meson.build +++ b/ui/meson.build @@ -46,7 +46,7 @@ vnc_ss.add(files( )) vnc_ss.add(zlib, jpeg, gnutls) vnc_ss.add(when: sasl, if_true: files('vnc-auth-sasl.c')) -system_ss.add_all(when: vnc, if_true: vnc_ss) +system_ss.add_all(when: [vnc, pixman], if_true: vnc_ss) system_ss.add(when: vnc, if_false: files('vnc-stubs.c')) ui_modules = {} From c98791eb63517299d0c87fdd2823c3738d8c73cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:38:35 +0400 Subject: [PATCH 756/974] ui/spice: SPICE/QXL requires PIXMAN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Thomas Huth --- hw/display/Kconfig | 2 +- meson.build | 6 +++++- ui/meson.build | 10 +++++----- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/hw/display/Kconfig b/hw/display/Kconfig index 7b3da68d1c..4d8b0cec40 100644 --- a/hw/display/Kconfig +++ b/hw/display/Kconfig @@ -93,7 +93,7 @@ config VGA config QXL bool - depends on SPICE && PCI + depends on SPICE && PCI && PIXMAN select VGA config VIRTIO_GPU diff --git a/meson.build b/meson.build index 3e60f42f94..0f578ddbf4 100644 --- a/meson.build +++ b/meson.build @@ -1015,7 +1015,11 @@ if not get_option('spice_protocol').auto() or have_system method: 'pkg-config') endif spice = not_found -if not get_option('spice').auto() or have_system +if get_option('spice') \ + .disable_auto_if(not have_system) \ + .require(pixman.found(), + error_message: 'cannot enable SPICE if pixman is not available') \ + .allowed() spice = dependency('spice-server', version: '>=0.14.0', required: get_option('spice'), method: 'pkg-config') diff --git a/ui/meson.build b/ui/meson.build index 19723188b5..024f494faf 100644 --- a/ui/meson.build +++ b/ui/meson.build @@ -141,12 +141,12 @@ if spice.found() 'spice-display.c' )) ui_modules += {'spice-core' : spice_core_ss} -endif -if spice.found() and gio.found() - spice_ss = ss.source_set() - spice_ss.add(spice, gio, pixman, files('spice-app.c')) - ui_modules += {'spice-app': spice_ss} + if gio.found() + spice_ss = ss.source_set() + spice_ss.add(spice, gio, pixman, files('spice-app.c')) + ui_modules += {'spice-app': spice_ss} + endif endif keymaps = [ From da554e1616be52d3e3bbbda6044aff806c355fc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:38:36 +0400 Subject: [PATCH 757/974] ui/gtk: -display gtk requires PIXMAN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Thomas Huth --- meson.build | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 0f578ddbf4..8422e7da0c 100644 --- a/meson.build +++ b/meson.build @@ -1531,7 +1531,11 @@ gtkx11 = not_found vte = not_found have_gtk_clipboard = get_option('gtk_clipboard').enabled() -if not get_option('gtk').auto() or have_system +if get_option('gtk') \ + .disable_auto_if(not have_system) \ + .require(pixman.found(), + error_message: 'cannot enable GTK if pixman is not available') \ + .allowed() gtk = dependency('gtk+-3.0', version: '>=3.22.0', method: 'pkg-config', required: get_option('gtk')) From 949c084ad62f99448ca4866e41f38f283e990298 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:38:37 +0400 Subject: [PATCH 758/974] ui/dbus: do not require PIXMAN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement a fallback path for region 2D update. Signed-off-by: Marc-André Lureau --- ui/dbus-listener.c | 90 ++++++++++++++++++++++++++++++++-------------- ui/meson.build | 4 +-- 2 files changed, 65 insertions(+), 29 deletions(-) diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c index 36548a7f52..18f556aa73 100644 --- a/ui/dbus-listener.c +++ b/ui/dbus-listener.c @@ -26,9 +26,6 @@ #include "qapi/error.h" #include "sysemu/sysemu.h" #include "dbus.h" -#ifdef CONFIG_OPENGL -#include -#endif #ifdef G_OS_UNIX #include #endif @@ -41,6 +38,7 @@ #include "ui/shader.h" #include "ui/egl-helpers.h" #include "ui/egl-context.h" +#include "ui/qemu-pixman.h" #endif #include "trace.h" @@ -62,9 +60,11 @@ struct _DBusDisplayListener { QemuDBusDisplay1Listener *proxy; -#ifdef CONFIG_OPENGL +#ifdef CONFIG_PIXMAN /* Keep track of the damage region */ pixman_region32_t gl_damage; +#else + int gl_damage; #endif DisplayChangeListener dcl; @@ -545,6 +545,7 @@ static void dbus_gl_refresh(DisplayChangeListener *dcl) return; } +#ifdef CONFIG_PIXMAN int n_rects = pixman_region32_n_rects(&ddl->gl_damage); for (int i = 0; i < n_rects; i++) { @@ -555,6 +556,13 @@ static void dbus_gl_refresh(DisplayChangeListener *dcl) box->x2 - box->x1, box->y2 - box->y1); } pixman_region32_clear(&ddl->gl_damage); +#else + if (ddl->gl_damage) { + dbus_call_update_gl(dcl, 0, 0, + surface_width(ddl->ds), surface_height(ddl->ds)); + ddl->gl_damage = 0; + } +#endif } #endif /* OPENGL */ @@ -569,20 +577,64 @@ static void dbus_gl_gfx_update(DisplayChangeListener *dcl, { DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); +#ifdef CONFIG_PIXMAN pixman_region32_t rect_region; pixman_region32_init_rect(&rect_region, x, y, w, h); pixman_region32_union(&ddl->gl_damage, &ddl->gl_damage, &rect_region); pixman_region32_fini(&rect_region); +#else + ddl->gl_damage++; +#endif } #endif +static void dbus_gfx_update_sub(DBusDisplayListener *ddl, + int x, int y, int w, int h) +{ + pixman_image_t *img; + size_t stride; + GVariant *v_data; + + /* make a copy, since gvariant only handles linear data */ + stride = w * DIV_ROUND_UP(PIXMAN_FORMAT_BPP(surface_format(ddl->ds)), 8); + img = pixman_image_create_bits(surface_format(ddl->ds), + w, h, NULL, stride); +#ifdef CONFIG_PIXMAN + pixman_image_composite(PIXMAN_OP_SRC, ddl->ds->image, NULL, img, + x, y, 0, 0, 0, 0, w, h); +#else + { + uint8_t *src = (uint8_t *)pixman_image_get_data(ddl->ds->image); + uint8_t *dst = (uint8_t *)pixman_image_get_data(img); + int bp = PIXMAN_FORMAT_BPP(surface_format(ddl->ds)) / 8; + int hh; + + for (hh = 0; hh < h; hh++) { + memcpy(&dst[stride * hh], + &src[surface_stride(ddl->ds) * (hh + y) + x * bp], + stride); + } + } +#endif + v_data = g_variant_new_from_data( + G_VARIANT_TYPE("ay"), + pixman_image_get_data(img), + pixman_image_get_stride(img) * h, + TRUE, + (GDestroyNotify)pixman_image_unref, + img); + qemu_dbus_display1_listener_call_update(ddl->proxy, + x, y, w, h, pixman_image_get_stride(img), pixman_image_get_format(img), + v_data, + G_DBUS_CALL_FLAGS_NONE, + DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL); +} + static void dbus_gfx_update(DisplayChangeListener *dcl, int x, int y, int w, int h) { DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); - pixman_image_t *img; GVariant *v_data; - size_t stride; assert(ddl->ds); @@ -619,25 +671,7 @@ static void dbus_gfx_update(DisplayChangeListener *dcl, return; } - /* make a copy, since gvariant only handles linear data */ - stride = w * DIV_ROUND_UP(PIXMAN_FORMAT_BPP(surface_format(ddl->ds)), 8); - img = pixman_image_create_bits(surface_format(ddl->ds), - w, h, NULL, stride); - pixman_image_composite(PIXMAN_OP_SRC, ddl->ds->image, NULL, img, - x, y, 0, 0, 0, 0, w, h); - - v_data = g_variant_new_from_data( - G_VARIANT_TYPE("ay"), - pixman_image_get_data(img), - pixman_image_get_stride(img) * h, - TRUE, - (GDestroyNotify)pixman_image_unref, - img); - qemu_dbus_display1_listener_call_update(ddl->proxy, - x, y, w, h, pixman_image_get_stride(img), pixman_image_get_format(img), - v_data, - G_DBUS_CALL_FLAGS_NONE, - DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL); + dbus_gfx_update_sub(ddl, x, y, w, h); } #ifdef CONFIG_OPENGL @@ -751,8 +785,10 @@ dbus_display_listener_dispose(GObject *object) g_clear_object(&ddl->map_proxy); g_clear_object(&ddl->d3d11_proxy); g_clear_pointer(&ddl->peer_process, CloseHandle); -#ifdef CONFIG_OPENGL +#ifdef CONFIG_PIXMAN pixman_region32_fini(&ddl->gl_damage); +#endif +#ifdef CONFIG_OPENGL egl_fb_destroy(&ddl->fb); #endif #endif @@ -787,7 +823,7 @@ dbus_display_listener_class_init(DBusDisplayListenerClass *klass) static void dbus_display_listener_init(DBusDisplayListener *ddl) { -#ifdef CONFIG_OPENGL +#ifdef CONFIG_PIXMAN pixman_region32_init(&ddl->gl_damage); #endif } diff --git a/ui/meson.build b/ui/meson.build index 024f494faf..0ccb3387ee 100644 --- a/ui/meson.build +++ b/ui/meson.build @@ -93,7 +93,7 @@ if dbus_display '--generate-c-code', '@BASENAME@']) dbus_display1_lib = static_library('dbus-display1', dbus_display1, dependencies: gio) dbus_display1_dep = declare_dependency(link_with: dbus_display1_lib, include_directories: include_directories('.')) - dbus_ss.add(when: [gio, pixman, dbus_display1_dep], + dbus_ss.add(when: [gio, dbus_display1_dep], if_true: [files( 'dbus-chardev.c', 'dbus-clipboard.c', @@ -101,7 +101,7 @@ if dbus_display 'dbus-error.c', 'dbus-listener.c', 'dbus.c', - ), opengl, gbm]) + ), opengl, gbm, pixman]) ui_modules += {'dbus' : dbus_ss} endif From 04c4cc10d9f127eef61ef9b3e4acb6333bc0f670 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:38:38 +0400 Subject: [PATCH 759/974] arm/kconfig: XLNX_ZYNQMP_ARM depends on PIXMAN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Display Port has some strong PIXMAN dependency. Signed-off-by: Marc-André Lureau Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé --- hw/arm/Kconfig | 3 ++- hw/display/Kconfig | 5 +++++ hw/display/meson.build | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index e35007ed41..d215a2c729 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -450,7 +450,7 @@ config STM32F405_SOC config XLNX_ZYNQMP_ARM bool - default y + default y if PIXMAN depends on TCG && AARCH64 select AHCI select ARM_GIC @@ -463,6 +463,7 @@ config XLNX_ZYNQMP_ARM select XILINX_AXI select XILINX_SPIPS select XLNX_CSU_DMA + select XLNX_DISPLAYPORT select XLNX_ZYNQMP select XLNX_ZDMA select USB_DWC3 diff --git a/hw/display/Kconfig b/hw/display/Kconfig index 4d8b0cec40..1aafe1923d 100644 --- a/hw/display/Kconfig +++ b/hw/display/Kconfig @@ -134,3 +134,8 @@ config MACFB bool select FRAMEBUFFER depends on NUBUS + +config XLNX_DISPLAYPORT + bool + # defaults to "N", enabled by specific boards + depends on PIXMAN diff --git a/hw/display/meson.build b/hw/display/meson.build index 2b64fd9f9d..9c06aaee20 100644 --- a/hw/display/meson.build +++ b/hw/display/meson.build @@ -58,7 +58,7 @@ if config_all_devices.has_key('CONFIG_QXL') endif system_ss.add(when: 'CONFIG_DPCD', if_true: files('dpcd.c')) -system_ss.add(when: 'CONFIG_XLNX_ZYNQMP_ARM', if_true: files('xlnx_dp.c')) +system_ss.add(when: 'CONFIG_XLNX_DISPLAYPORT', if_true: files('xlnx_dp.c')) system_ss.add(when: 'CONFIG_ARTIST', if_true: files('artist.c')) From b271b6a392ba74858b04538cad14fa385c4894dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Fri, 3 Nov 2023 19:09:03 +0400 Subject: [PATCH 760/974] hw/arm: XLNX_VERSAL depends on XLNX_CSU_DMA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: 868d968004 ("hw/arm/xlnx-versal: Connect OSPI flash controller") Signed-off-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé --- hw/arm/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index d215a2c729..8b3dc160cd 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -484,6 +484,7 @@ config XLNX_VERSAL select XLNX_EFUSE_VERSAL select XLNX_USB_SUBSYS select XLNX_VERSAL_TRNG + select XLNX_CSU_DMA config NPCM7XX bool From fa140b9562f0dc4665da0620b10d7ef1e3009078 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 5 Sep 2023 16:51:53 +0400 Subject: [PATCH 761/974] hw/sm501: allow compiling without PIXMAN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the "x-pixman" property default value and use the fallback path when PIXMAN support is disabled. Signed-off-by: Marc-André Lureau Reviewed-by: BALATON Zoltan --- hw/display/sm501.c | 45 ++++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 0eecd00701..5b4e4509e1 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -438,6 +438,12 @@ #define SM501_HWC_WIDTH 64 #define SM501_HWC_HEIGHT 64 +#ifdef CONFIG_PIXMAN +#define DEFAULT_X_PIXMAN 7 +#else +#define DEFAULT_X_PIXMAN 0 +#endif + /* SM501 local memory size taken from "linux/drivers/mfd/sm501.c" */ static const uint32_t sm501_mem_local_size[] = { [0] = 4 * MiB, @@ -730,7 +736,6 @@ static void sm501_2d_operation(SM501State *s) switch (cmd) { case 0: /* BitBlt */ { - static uint32_t tmp_buf[16384]; unsigned int src_x = (s->twoD_source >> 16) & 0x01FFF; unsigned int src_y = s->twoD_source & 0xFFFF; uint32_t src_base = s->twoD_source_base & 0x03FFFFFF; @@ -828,9 +833,11 @@ static void sm501_2d_operation(SM501State *s) de = db + (width + (height - 1) * dst_pitch) * bypp; overlap = (db < se && sb < de); } +#ifdef CONFIG_PIXMAN if (overlap && (s->use_pixman & BIT(2))) { /* pixman can't do reverse blit: copy via temporary */ int tmp_stride = DIV_ROUND_UP(width * bypp, sizeof(uint32_t)); + static uint32_t tmp_buf[16384]; uint32_t *tmp = tmp_buf; if (tmp_stride * sizeof(uint32_t) * height > sizeof(tmp_buf)) { @@ -860,7 +867,9 @@ static void sm501_2d_operation(SM501State *s) dst_pitch * bypp / sizeof(uint32_t), 8 * bypp, 8 * bypp, src_x, src_y, dst_x, dst_y, width, height); - } else { + } else +#endif + { fallback = true; } if (fallback) { @@ -894,20 +903,23 @@ static void sm501_2d_operation(SM501State *s) color = cpu_to_le16(color); } +#ifdef CONFIG_PIXMAN if (!(s->use_pixman & BIT(0)) || (width == 1 && height == 1) || !pixman_fill((uint32_t *)&s->local_mem[dst_base], dst_pitch * bypp / sizeof(uint32_t), 8 * bypp, - dst_x, dst_y, width, height, color)) { - /* fallback when pixman failed or we don't want to call it */ - uint8_t *d = s->local_mem + dst_base; - unsigned int x, y, i; - for (y = 0; y < height; y++) { - i = (dst_x + (dst_y + y) * dst_pitch) * bypp; - for (x = 0; x < width; x++, i += bypp) { - stn_he_p(&d[i], bypp, color); + dst_x, dst_y, width, height, color)) +#endif + { + /* fallback when pixman failed or we don't want to call it */ + uint8_t *d = s->local_mem + dst_base; + unsigned int x, y, i; + for (y = 0; y < height; y++) { + i = (dst_x + (dst_y + y) * dst_pitch) * bypp; + for (x = 0; x < width; x++, i += bypp) { + stn_he_p(&d[i], bypp, color); + } } } - } break; } default: @@ -1878,6 +1890,12 @@ static void sm501_reset(SM501State *s) static void sm501_init(SM501State *s, DeviceState *dev, uint32_t local_mem_bytes) { +#ifndef CONFIG_PIXMAN + if (s->use_pixman != 0) { + warn_report("x-pixman != 0, not effective without PIXMAN"); + } +#endif + s->local_mem_size_index = get_local_mem_size_index(local_mem_bytes); /* local memory */ @@ -2038,7 +2056,8 @@ static void sm501_realize_sysbus(DeviceState *dev, Error **errp) static Property sm501_sysbus_properties[] = { DEFINE_PROP_UINT32("vram-size", SM501SysBusState, vram_size, 0), - DEFINE_PROP_UINT8("x-pixman", SM501SysBusState, state.use_pixman, 7), + /* this a debug option, prefer PROP_UINT over PROP_BIT for simplicity */ + DEFINE_PROP_UINT8("x-pixman", SM501SysBusState, state.use_pixman, DEFAULT_X_PIXMAN), DEFINE_PROP_END_OF_LIST(), }; @@ -2126,7 +2145,7 @@ static void sm501_realize_pci(PCIDevice *dev, Error **errp) static Property sm501_pci_properties[] = { DEFINE_PROP_UINT32("vram-size", SM501PCIState, vram_size, 64 * MiB), - DEFINE_PROP_UINT8("x-pixman", SM501PCIState, state.use_pixman, 7), + DEFINE_PROP_UINT8("x-pixman", SM501PCIState, state.use_pixman, DEFAULT_X_PIXMAN), DEFINE_PROP_END_OF_LIST(), }; From 376a0531d4f5ebe3e895986e0b2e1c15a9e4721c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Sat, 4 Nov 2023 15:37:19 +0400 Subject: [PATCH 762/974] hw/mips: FULOONG depends on VT82C686 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé --- hw/mips/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/mips/Kconfig b/hw/mips/Kconfig index ac1eb06a51..66ec536e06 100644 --- a/hw/mips/Kconfig +++ b/hw/mips/Kconfig @@ -33,6 +33,7 @@ config JAZZ config FULOONG bool select PCI_BONITO + select VT82C686 config LOONGSON3V bool From 699f15fd0c062c5f337584c585223bf7c3a9cd8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 5 Sep 2023 16:51:53 +0400 Subject: [PATCH 763/974] hw/display/ati: allow compiling without PIXMAN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the "x-pixman" property default value and use the fallback path when PIXMAN support is disabled. Signed-off-by: Marc-André Lureau Acked-by: BALATON Zoltan --- hw/display/ati.c | 15 ++++++++++++++- hw/display/ati_2d.c | 10 ++++++++-- hw/display/meson.build | 2 +- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/hw/display/ati.c b/hw/display/ati.c index 9a87a5504a..569b8f6165 100644 --- a/hw/display/ati.c +++ b/hw/display/ati.c @@ -32,6 +32,12 @@ #define ATI_DEBUG_HW_CURSOR 0 +#ifdef CONFIG_PIXMAN +#define DEFAULT_X_PIXMAN 3 +#else +#define DEFAULT_X_PIXMAN 0 +#endif + static const struct { const char *name; uint16_t dev_id; @@ -946,6 +952,12 @@ static void ati_vga_realize(PCIDevice *dev, Error **errp) ATIVGAState *s = ATI_VGA(dev); VGACommonState *vga = &s->vga; +#ifndef CONFIG_PIXMAN + if (s->use_pixman != 0) { + warn_report("x-pixman != 0, not effective without PIXMAN"); + } +#endif + if (s->model) { int i; for (i = 0; i < ARRAY_SIZE(ati_model_aliases); i++) { @@ -1033,7 +1045,8 @@ static Property ati_vga_properties[] = { DEFINE_PROP_UINT16("x-device-id", ATIVGAState, dev_id, PCI_DEVICE_ID_ATI_RAGE128_PF), DEFINE_PROP_BOOL("guest_hwcursor", ATIVGAState, cursor_guest_mode, false), - DEFINE_PROP_UINT8("x-pixman", ATIVGAState, use_pixman, 3), + /* this is a debug option, prefer PROP_UINT over PROP_BIT for simplicity */ + DEFINE_PROP_UINT8("x-pixman", ATIVGAState, use_pixman, DEFAULT_X_PIXMAN), DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/display/ati_2d.c b/hw/display/ati_2d.c index 0e6b8e4367..309bb5ccb6 100644 --- a/hw/display/ati_2d.c +++ b/hw/display/ati_2d.c @@ -123,6 +123,7 @@ void ati_2d_blt(ATIVGAState *s) src_bits, dst_bits, src_stride, dst_stride, bpp, bpp, src_x, src_y, dst_x, dst_y, s->regs.dst_width, s->regs.dst_height); +#ifdef CONFIG_PIXMAN if ((s->use_pixman & BIT(1)) && s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT && s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM) { @@ -147,7 +148,9 @@ void ati_2d_blt(ATIVGAState *s) s->regs.dst_width, s->regs.dst_height); } g_free(tmp); - } else { + } else +#endif + { fallback = true; } if (fallback) { @@ -206,9 +209,12 @@ void ati_2d_blt(ATIVGAState *s) DPRINTF("pixman_fill(%p, %d, %d, %d, %d, %d, %d, %x)\n", dst_bits, dst_stride, bpp, dst_x, dst_y, s->regs.dst_width, s->regs.dst_height, filler); +#ifdef CONFIG_PIXMAN if (!(s->use_pixman & BIT(0)) || !pixman_fill((uint32_t *)dst_bits, dst_stride, bpp, dst_x, dst_y, - s->regs.dst_width, s->regs.dst_height, filler)) { + s->regs.dst_width, s->regs.dst_height, filler)) +#endif + { /* fallback when pixman failed or we don't want to call it */ unsigned int x, y, i, bypp = bpp / 8; unsigned int dst_pitch = dst_stride * sizeof(uint32_t); diff --git a/hw/display/meson.build b/hw/display/meson.build index 9c06aaee20..344dfe3d8c 100644 --- a/hw/display/meson.build +++ b/hw/display/meson.build @@ -62,7 +62,7 @@ system_ss.add(when: 'CONFIG_XLNX_DISPLAYPORT', if_true: files('xlnx_dp.c')) system_ss.add(when: 'CONFIG_ARTIST', if_true: files('artist.c')) -system_ss.add(when: [pixman, 'CONFIG_ATI_VGA'], if_true: files('ati.c', 'ati_2d.c', 'ati_dbg.c')) +system_ss.add(when: 'CONFIG_ATI_VGA', if_true: [files('ati.c', 'ati_2d.c', 'ati_dbg.c'), pixman]) if config_all_devices.has_key('CONFIG_VIRTIO_GPU') From d017f28a2ee082f472ed69fedf0435b468000e92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 4 Sep 2023 17:29:41 +0400 Subject: [PATCH 764/974] build-sys: make pixman actually optional MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Thomas Huth --- meson.build | 3 --- 1 file changed, 3 deletions(-) diff --git a/meson.build b/meson.build index 8422e7da0c..4848930680 100644 --- a/meson.build +++ b/meson.build @@ -817,9 +817,6 @@ if not get_option('pixman').auto() or have_system or have_tools pixman = dependency('pixman-1', required: get_option('pixman'), version:'>=0.21.8', method: 'pkg-config') endif -if not pixman.found() and (have_system or have_tools) - error('FIXME: pixman is currently required') -endif zlib = dependency('zlib', required: true) From 853c014bf9565998a245e2b3e70998eef745ac24 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 27 Oct 2023 08:08:08 +0200 Subject: [PATCH 765/974] MAINTAINERS: Add the virtio-gpu documentation to the corresponding section Add virtio-gpu.rst to the corresponding section in MAINTAINERS, so that the maintainers gets CC:-ed on corresponding patches. Message-ID: <20231027060808.242442-1-thuth@redhat.com> Reviewed-by: Manos Pitsidianakis Signed-off-by: Thomas Huth --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 59b92ee640..2058296ede 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2483,6 +2483,7 @@ S: Odd Fixes F: hw/display/virtio-gpu* F: hw/display/virtio-vga.* F: include/hw/virtio/virtio-gpu.h +F: docs/system/devices/virtio-gpu.rst vhost-user-blk M: Raphael Norwitz From e93d1e99830481e9a5c758672d2b8ea318f07080 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sat, 22 Jul 2023 15:26:40 +0900 Subject: [PATCH 766/974] vl: Free machine list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Free machine list and make LeakSanitizer happy. Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20230722062641.18505-1-akihiko.odaki@daynix.com> Signed-off-by: Philippe Mathieu-Daudé --- system/vl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/system/vl.c b/system/vl.c index 3fb569254a..ff76eb0d07 100644 --- a/system/vl.c +++ b/system/vl.c @@ -1529,7 +1529,8 @@ static gint machine_class_cmp(gconstpointer a, gconstpointer b) static void machine_help_func(const QDict *qdict) { - GSList *machines, *el; + g_autoptr(GSList) machines = NULL; + GSList *el; const char *type = qdict_get_try_str(qdict, "type"); machines = object_class_get_list(TYPE_MACHINE, false); From 1494a6528044dedc067ab772ec53e51b10b8b00a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 30 Oct 2023 14:15:29 +0400 Subject: [PATCH 767/974] vl: constify default_list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's not modified, let's make it const. Signed-off-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20231030101529.105266-1-marcandre.lureau@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- system/vl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/vl.c b/system/vl.c index ff76eb0d07..8c803228f4 100644 --- a/system/vl.c +++ b/system/vl.c @@ -194,7 +194,7 @@ static int default_sdcard = 1; static int default_vga = 1; static int default_net = 1; -static struct { +static const struct { const char *driver; int *flag; } default_list[] = { From 57c1a9a7065f52550abbe513b17ec1355d2af576 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 20 Sep 2023 11:10:22 +0200 Subject: [PATCH 768/974] tests/vm/ubuntu.aarch64: Correct comment about TCG specific delay MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wether we use a software MMU or not to set the SSH timeout isn't really relevant. What we want to know is if we use a hardware or software accelerator (TCG). Replace the 'softmmu' mention by 'TCG'. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-Id: <20231002145104.52193-2-philmd@linaro.org> --- tests/vm/ubuntu.aarch64 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/vm/ubuntu.aarch64 b/tests/vm/ubuntu.aarch64 index 666947393b..eeda281f87 100755 --- a/tests/vm/ubuntu.aarch64 +++ b/tests/vm/ubuntu.aarch64 @@ -25,7 +25,7 @@ DEFAULT_CONFIG = { "apt-get install -y libfdt-dev pkg-config language-pack-en ninja-build", # We increase beyond the default time since during boot # it can take some time (many seconds) to log into the VM - # especially using softmmu. + # especially using TCG. 'ssh_timeout' : 60, } From 648625e628dde0b57628a2e9992dd4d77744355f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 20 Sep 2023 11:10:12 +0200 Subject: [PATCH 769/974] tests/unit/test-seccomp: Remove mentions of softmmu in test names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wether we are using a software MMU or not is irrelevant for the seccomp facility. The facility is restricted to system emulation, but such detail isn't really helpful, so directly drop the 'softmmu' mention from the test names. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20231002145104.52193-3-philmd@linaro.org> --- tests/unit/test-seccomp.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/unit/test-seccomp.c b/tests/unit/test-seccomp.c index f02c79cafd..bab93fd6da 100644 --- a/tests/unit/test-seccomp.c +++ b/tests/unit/test-seccomp.c @@ -229,26 +229,26 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); if (can_play_with_seccomp()) { #ifdef SYS_fork - g_test_add_func("/softmmu/seccomp/sys-fork/on", + g_test_add_func("/seccomp/sys-fork/on", test_seccomp_sys_fork_on); - g_test_add_func("/softmmu/seccomp/sys-fork/on-nospawn", + g_test_add_func("/seccomp/sys-fork/on-nospawn", test_seccomp_sys_fork_on_nospawn); - g_test_add_func("/softmmu/seccomp/sys-fork/off", + g_test_add_func("/seccomp/sys-fork/off", test_seccomp_sys_fork_off); #endif - g_test_add_func("/softmmu/seccomp/fork/on", + g_test_add_func("/seccomp/fork/on", test_seccomp_fork_on); - g_test_add_func("/softmmu/seccomp/fork/on-nospawn", + g_test_add_func("/seccomp/fork/on-nospawn", test_seccomp_fork_on_nospawn); - g_test_add_func("/softmmu/seccomp/fork/off", + g_test_add_func("/seccomp/fork/off", test_seccomp_fork_off); - g_test_add_func("/softmmu/seccomp/thread/on", + g_test_add_func("/seccomp/thread/on", test_seccomp_thread_on); - g_test_add_func("/softmmu/seccomp/thread/on-nospawn", + g_test_add_func("/seccomp/thread/on-nospawn", test_seccomp_thread_on_nospawn); - g_test_add_func("/softmmu/seccomp/thread/off", + g_test_add_func("/seccomp/thread/off", test_seccomp_thread_off); if (doit_sched() == 0) { @@ -256,11 +256,11 @@ int main(int argc, char **argv) * musl doesn't impl sched_setscheduler, hence * we check above if it works first */ - g_test_add_func("/softmmu/seccomp/sched/on", + g_test_add_func("/seccomp/sched/on", test_seccomp_sched_on); - g_test_add_func("/softmmu/seccomp/sched/on-nores", + g_test_add_func("/seccomp/sched/on-nores", test_seccomp_sched_on_nores); - g_test_add_func("/softmmu/seccomp/sched/off", + g_test_add_func("/seccomp/sched/off", test_seccomp_sched_off); } } From f4f826c0e0c189869ef55e540a5dcbd90fe392bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 18 Sep 2023 09:56:14 +0200 Subject: [PATCH 770/974] accel/tcg: Declare tcg_flush_jmp_cache() in 'exec/tb-flush.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "exec/cpu-common.h" is meant to contain the declarations related to CPU usable with any accelerator / target combination. tcg_flush_jmp_cache() is specific to TCG, so restrict its declaration by moving it to "exec/tb-flush.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Reviewed-by: Richard Henderson Message-Id: <20230918104153.24433-2-philmd@linaro.org> --- accel/tcg/cputlb.c | 1 + accel/tcg/tcg-accel-ops.c | 1 + hw/core/cpu-common.c | 1 + include/exec/cpu-common.h | 1 - include/exec/tb-flush.h | 2 ++ plugins/core.c | 1 - 6 files changed, 5 insertions(+), 2 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index f35c5f359b..765805e70b 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -24,6 +24,7 @@ #include "exec/memory.h" #include "exec/cpu_ldst.h" #include "exec/cputlb.h" +#include "exec/tb-flush.h" #include "exec/memory-internal.h" #include "exec/ram_addr.h" #include "tcg/tcg.h" diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c index d885cc1d3c..7ddb05c332 100644 --- a/accel/tcg/tcg-accel-ops.c +++ b/accel/tcg/tcg-accel-ops.c @@ -34,6 +34,7 @@ #include "qemu/timer.h" #include "exec/exec-all.h" #include "exec/hwaddr.h" +#include "exec/tb-flush.h" #include "exec/gdbstub.h" #include "tcg-accel-ops.h" diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index bab8942c30..29c917c5dc 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -27,6 +27,7 @@ #include "qemu/main-loop.h" #include "exec/log.h" #include "exec/cpu-common.h" +#include "exec/tb-flush.h" #include "qemu/error-report.h" #include "qemu/qemu-print.h" #include "sysemu/tcg.h" diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 30c376a4de..f700071d12 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -46,7 +46,6 @@ void cpu_list_unlock(void); unsigned int cpu_list_generation_id_get(void); void tcg_flush_softmmu_tlb(CPUState *cs); -void tcg_flush_jmp_cache(CPUState *cs); void tcg_iommu_init_notifier_list(CPUState *cpu); void tcg_iommu_free_notifier_list(CPUState *cpu); diff --git a/include/exec/tb-flush.h b/include/exec/tb-flush.h index d92d06565b..142c240d94 100644 --- a/include/exec/tb-flush.h +++ b/include/exec/tb-flush.h @@ -23,4 +23,6 @@ */ void tb_flush(CPUState *cs); +void tcg_flush_jmp_cache(CPUState *cs); + #endif /* _TB_FLUSH_H_ */ diff --git a/plugins/core.c b/plugins/core.c index fcd33a2bff..49588285dd 100644 --- a/plugins/core.c +++ b/plugins/core.c @@ -21,7 +21,6 @@ #include "qemu/xxhash.h" #include "qemu/rcu.h" #include "hw/core/cpu.h" -#include "exec/cpu-common.h" #include "exec/exec-all.h" #include "exec/tb-flush.h" From 1b5120d74b1e19c12f8f476f8015a0ac87e11878 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 15 Sep 2023 22:55:33 +0200 Subject: [PATCH 771/974] accel: Introduce cpu_exec_reset_hold() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce cpu_exec_reset_hold() which call an accelerator specific AccelOpsClass::cpu_reset_hold() handler. Define a stub on TCG user emulation, because CPU reset is irrelevant there. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Reviewed-by: Richard Henderson Message-Id: <20230918104153.24433-3-philmd@linaro.org> --- accel/tcg/user-exec-stub.c | 4 ++++ hw/core/cpu-common.c | 1 + include/hw/core/cpu.h | 1 + include/sysemu/accel-ops.h | 1 + system/cpus.c | 7 +++++++ 5 files changed, 14 insertions(+) diff --git a/accel/tcg/user-exec-stub.c b/accel/tcg/user-exec-stub.c index 2dc6fd9c4e..4fbe2dbdc8 100644 --- a/accel/tcg/user-exec-stub.c +++ b/accel/tcg/user-exec-stub.c @@ -14,6 +14,10 @@ void qemu_init_vcpu(CPUState *cpu) { } +void cpu_exec_reset_hold(CPUState *cpu) +{ +} + /* User mode emulation does not support record/replay yet. */ bool replay_exception(void) diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 29c917c5dc..7d266c36ac 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -137,6 +137,7 @@ static void cpu_common_reset_hold(Object *obj) cpu->crash_occurred = false; cpu->cflags_next_tb = -1; + cpu_exec_reset_hold(cpu); if (tcg_enabled()) { tcg_flush_jmp_cache(cpu); tcg_flush_softmmu_tlb(cpu); diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 18593db5b2..6373aa4501 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -1153,6 +1153,7 @@ void cpu_class_init_props(DeviceClass *dc); void cpu_exec_initfn(CPUState *cpu); void cpu_exec_realizefn(CPUState *cpu, Error **errp); void cpu_exec_unrealizefn(CPUState *cpu); +void cpu_exec_reset_hold(CPUState *cpu); /** * target_words_bigendian: diff --git a/include/sysemu/accel-ops.h b/include/sysemu/accel-ops.h index 3c1fab4b1e..ef91fc28bb 100644 --- a/include/sysemu/accel-ops.h +++ b/include/sysemu/accel-ops.h @@ -30,6 +30,7 @@ struct AccelOpsClass { void (*ops_init)(AccelOpsClass *ops); bool (*cpus_are_resettable)(void); + void (*cpu_reset_hold)(CPUState *cpu); void (*create_vcpu_thread)(CPUState *cpu); /* MANDATORY NON-NULL */ void (*kick_vcpu_thread)(CPUState *cpu); diff --git a/system/cpus.c b/system/cpus.c index 0848e0dbdb..952f15868c 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -201,6 +201,13 @@ bool cpus_are_resettable(void) return true; } +void cpu_exec_reset_hold(CPUState *cpu) +{ + if (cpus_accel->cpu_reset_hold) { + cpus_accel->cpu_reset_hold(cpu); + } +} + int64_t cpus_get_virtual_clock(void) { /* From bb6cf6f0168efadb95cf3e41963ec295ad28a941 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 15 Sep 2023 22:55:45 +0200 Subject: [PATCH 772/974] accel/tcg: Factor tcg_cpu_reset_hold() out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Factor the TCG specific code from cpu_common_reset_hold() to tcg_cpu_reset_hold() within tcg-accel-ops.c. Since this file is sysemu specific, we can inline tcg_flush_softmmu_tlb(), removing its declaration in "exec/cpu-common.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Reviewed-by: Richard Henderson Message-Id: <20230918104153.24433-4-philmd@linaro.org> --- accel/stubs/tcg-stub.c | 4 ---- accel/tcg/tcg-accel-ops.c | 8 ++++++++ accel/tcg/translate-all.c | 8 -------- hw/core/cpu-common.c | 5 ----- include/exec/cpu-common.h | 2 -- 5 files changed, 8 insertions(+), 19 deletions(-) diff --git a/accel/stubs/tcg-stub.c b/accel/stubs/tcg-stub.c index a9e7a2d5b4..8a496a2a6f 100644 --- a/accel/stubs/tcg-stub.c +++ b/accel/stubs/tcg-stub.c @@ -22,10 +22,6 @@ void tlb_set_dirty(CPUState *cpu, vaddr vaddr) { } -void tcg_flush_jmp_cache(CPUState *cpu) -{ -} - int probe_access_flags(CPUArchState *env, vaddr addr, int size, MMUAccessType access_type, int mmu_idx, bool nonfault, void **phost, uintptr_t retaddr) diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c index 7ddb05c332..1b57290682 100644 --- a/accel/tcg/tcg-accel-ops.c +++ b/accel/tcg/tcg-accel-ops.c @@ -78,6 +78,13 @@ int tcg_cpus_exec(CPUState *cpu) return ret; } +static void tcg_cpu_reset_hold(CPUState *cpu) +{ + tcg_flush_jmp_cache(cpu); + + tlb_flush(cpu); +} + /* mask must never be zero, except for A20 change call */ void tcg_handle_interrupt(CPUState *cpu, int mask) { @@ -206,6 +213,7 @@ static void tcg_accel_ops_init(AccelOpsClass *ops) } } + ops->cpu_reset_hold = tcg_cpu_reset_hold; ops->supports_guest_debug = tcg_supports_guest_debug; ops->insert_breakpoint = tcg_insert_breakpoint; ops->remove_breakpoint = tcg_remove_breakpoint; diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index e579b0891d..b263857ecc 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -673,11 +673,3 @@ void tcg_flush_jmp_cache(CPUState *cpu) qatomic_set(&jc->array[i].tb, NULL); } } - -/* This is a wrapper for common code that can not use CONFIG_SOFTMMU */ -void tcg_flush_softmmu_tlb(CPUState *cs) -{ -#ifdef CONFIG_SOFTMMU - tlb_flush(cs); -#endif -} diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 7d266c36ac..baa6d28b64 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -27,7 +27,6 @@ #include "qemu/main-loop.h" #include "exec/log.h" #include "exec/cpu-common.h" -#include "exec/tb-flush.h" #include "qemu/error-report.h" #include "qemu/qemu-print.h" #include "sysemu/tcg.h" @@ -138,10 +137,6 @@ static void cpu_common_reset_hold(Object *obj) cpu->cflags_next_tb = -1; cpu_exec_reset_hold(cpu); - if (tcg_enabled()) { - tcg_flush_jmp_cache(cpu); - tcg_flush_softmmu_tlb(cpu); - } } static bool cpu_common_has_work(CPUState *cs) diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index f700071d12..41115d8919 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -45,8 +45,6 @@ void cpu_list_lock(void); void cpu_list_unlock(void); unsigned int cpu_list_generation_id_get(void); -void tcg_flush_softmmu_tlb(CPUState *cs); - void tcg_iommu_init_notifier_list(CPUState *cpu); void tcg_iommu_free_notifier_list(CPUState *cpu); From 6ee45fac56a2e3943214dd0f1568388ee89f16c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 13 Oct 2023 11:21:51 +0200 Subject: [PATCH 773/974] target: Unify QOM style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enforce the style described by commit 067109a11c ("docs/devel: mention the spacing requirement for QOM"): The first declaration of a storage or class structure should always be the parent and leave a visual space between that declaration and the new code. It is also useful to separate backing for properties (options driven by the user) and internal state to make navigation easier. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Zhao Liu Message-Id: <20231013140116.255-2-philmd@linaro.org> --- target/alpha/cpu-qom.h | 2 -- target/alpha/cpu.h | 2 -- target/arm/cpu-qom.h | 4 ---- target/arm/cpu.h | 2 -- target/avr/cpu-qom.h | 3 +-- target/avr/cpu.h | 2 -- target/cris/cpu-qom.h | 2 -- target/cris/cpu.h | 2 -- target/hexagon/cpu.h | 5 +---- target/hppa/cpu-qom.h | 2 -- target/hppa/cpu.h | 2 -- target/i386/cpu-qom.h | 2 -- target/i386/cpu.h | 2 -- target/loongarch/cpu.h | 4 ---- target/m68k/cpu-qom.h | 2 -- target/m68k/cpu.h | 2 -- target/microblaze/cpu-qom.h | 2 -- target/microblaze/cpu.h | 2 -- target/mips/cpu-qom.h | 2 -- target/mips/cpu.h | 2 -- target/nios2/cpu.h | 4 ---- target/openrisc/cpu.h | 4 ---- target/ppc/cpu.h | 2 -- target/riscv/cpu-qom.h | 3 +-- target/riscv/cpu.h | 2 -- target/rx/cpu-qom.h | 2 -- target/rx/cpu.h | 2 -- target/s390x/cpu-qom.h | 3 +-- target/s390x/cpu.h | 2 -- target/sh4/cpu-qom.h | 2 -- target/sh4/cpu.h | 2 -- target/sparc/cpu-qom.h | 2 -- target/sparc/cpu.h | 2 -- target/tricore/cpu-qom.h | 2 -- target/tricore/cpu.h | 2 -- target/xtensa/cpu-qom.h | 2 -- target/xtensa/cpu.h | 2 -- 37 files changed, 4 insertions(+), 84 deletions(-) diff --git a/target/alpha/cpu-qom.h b/target/alpha/cpu-qom.h index 1f200724b6..c5fbd8f11a 100644 --- a/target/alpha/cpu-qom.h +++ b/target/alpha/cpu-qom.h @@ -35,9 +35,7 @@ OBJECT_DECLARE_CPU_TYPE(AlphaCPU, AlphaCPUClass, ALPHA_CPU) * An Alpha CPU model. */ struct AlphaCPUClass { - /*< private >*/ CPUClass parent_class; - /*< public >*/ DeviceRealize parent_realize; DeviceReset parent_reset; diff --git a/target/alpha/cpu.h b/target/alpha/cpu.h index e2a467ec17..c8d97ac27a 100644 --- a/target/alpha/cpu.h +++ b/target/alpha/cpu.h @@ -259,9 +259,7 @@ typedef struct CPUArchState { * An Alpha CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ CPUAlphaState env; diff --git a/target/arm/cpu-qom.h b/target/arm/cpu-qom.h index d06c08a734..153865d1bb 100644 --- a/target/arm/cpu-qom.h +++ b/target/arm/cpu-qom.h @@ -46,9 +46,7 @@ void aarch64_cpu_register(const ARMCPUInfo *info); * An ARM CPU model. */ struct ARMCPUClass { - /*< private >*/ CPUClass parent_class; - /*< public >*/ const ARMCPUInfo *info; DeviceRealize parent_realize; @@ -62,9 +60,7 @@ DECLARE_CLASS_CHECKERS(AArch64CPUClass, AARCH64_CPU, TYPE_AARCH64_CPU) struct AArch64CPUClass { - /*< private >*/ ARMCPUClass parent_class; - /*< public >*/ }; void register_cp_regs_for_features(ARMCPU *cpu); diff --git a/target/arm/cpu.h b/target/arm/cpu.h index d51dfe48db..2f7ab22169 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -852,9 +852,7 @@ typedef struct { * An ARM CPU core. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ CPUARMState env; diff --git a/target/avr/cpu-qom.h b/target/avr/cpu-qom.h index 01ea5f160b..d89be01e0f 100644 --- a/target/avr/cpu-qom.h +++ b/target/avr/cpu-qom.h @@ -36,9 +36,8 @@ OBJECT_DECLARE_CPU_TYPE(AVRCPU, AVRCPUClass, AVR_CPU) * A AVR CPU model. */ struct AVRCPUClass { - /*< private >*/ CPUClass parent_class; - /*< public >*/ + DeviceRealize parent_realize; ResettablePhases parent_phases; }; diff --git a/target/avr/cpu.h b/target/avr/cpu.h index 4ce22d8e4f..f8b065ed79 100644 --- a/target/avr/cpu.h +++ b/target/avr/cpu.h @@ -144,9 +144,7 @@ typedef struct CPUArchState { * A AVR CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ CPUAVRState env; }; diff --git a/target/cris/cpu-qom.h b/target/cris/cpu-qom.h index 431a1d536a..c2fee242f4 100644 --- a/target/cris/cpu-qom.h +++ b/target/cris/cpu-qom.h @@ -36,9 +36,7 @@ OBJECT_DECLARE_CPU_TYPE(CRISCPU, CRISCPUClass, CRIS_CPU) * A CRIS CPU model. */ struct CRISCPUClass { - /*< private >*/ CPUClass parent_class; - /*< public >*/ DeviceRealize parent_realize; ResettablePhases parent_phases; diff --git a/target/cris/cpu.h b/target/cris/cpu.h index 676b8e93ca..6aa445348f 100644 --- a/target/cris/cpu.h +++ b/target/cris/cpu.h @@ -174,9 +174,7 @@ typedef struct CPUArchState { * A CRIS CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ CPUCRISState env; }; diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h index 10cd1efd57..035ac4fb6d 100644 --- a/target/hexagon/cpu.h +++ b/target/hexagon/cpu.h @@ -130,17 +130,14 @@ typedef struct CPUArchState { OBJECT_DECLARE_CPU_TYPE(HexagonCPU, HexagonCPUClass, HEXAGON_CPU) typedef struct HexagonCPUClass { - /*< private >*/ CPUClass parent_class; - /*< public >*/ + DeviceRealize parent_realize; ResettablePhases parent_phases; } HexagonCPUClass; struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ CPUHexagonState env; diff --git a/target/hppa/cpu-qom.h b/target/hppa/cpu-qom.h index 4a85ebf5e0..cabd3b681e 100644 --- a/target/hppa/cpu-qom.h +++ b/target/hppa/cpu-qom.h @@ -36,9 +36,7 @@ OBJECT_DECLARE_CPU_TYPE(HPPACPU, HPPACPUClass, HPPA_CPU) * An HPPA CPU model. */ struct HPPACPUClass { - /*< private >*/ CPUClass parent_class; - /*< public >*/ DeviceRealize parent_realize; DeviceReset parent_reset; diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 144794d089..b39bae00d3 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -247,9 +247,7 @@ typedef struct CPUArchState { * An HPPA CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ CPUHPPAState env; QEMUTimer *alarm_timer; diff --git a/target/i386/cpu-qom.h b/target/i386/cpu-qom.h index 2350f4ae60..58145717ef 100644 --- a/target/i386/cpu-qom.h +++ b/target/i386/cpu-qom.h @@ -47,9 +47,7 @@ typedef struct X86CPUModel X86CPUModel; * An x86 CPU model or family. */ struct X86CPUClass { - /*< private >*/ CPUClass parent_class; - /*< public >*/ /* CPU definition, automatically loaded by instance_init if not NULL. * Should be eventually replaced by subclass-specific property defaults. diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 471e71dbc5..096f85483e 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1897,9 +1897,7 @@ struct kvm_msrs; * An x86 CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ CPUX86State env; VMChangeStateEntry *vmsentry; diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 9d0f79f814..555ea1f8d9 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -371,9 +371,7 @@ typedef struct CPUArchState { * A LoongArch CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ CPULoongArchState env; QEMUTimer timer; @@ -398,9 +396,7 @@ OBJECT_DECLARE_CPU_TYPE(LoongArchCPU, LoongArchCPUClass, * A LoongArch CPU model. */ struct LoongArchCPUClass { - /*< private >*/ CPUClass parent_class; - /*< public >*/ DeviceRealize parent_realize; ResettablePhases parent_phases; diff --git a/target/m68k/cpu-qom.h b/target/m68k/cpu-qom.h index 0ec7750a92..13d94c9fe3 100644 --- a/target/m68k/cpu-qom.h +++ b/target/m68k/cpu-qom.h @@ -35,9 +35,7 @@ OBJECT_DECLARE_CPU_TYPE(M68kCPU, M68kCPUClass, M68K_CPU) * A Motorola 68k CPU model. */ struct M68kCPUClass { - /*< private >*/ CPUClass parent_class; - /*< public >*/ DeviceRealize parent_realize; ResettablePhases parent_phases; diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h index 20afb0c94d..9ea18028ad 100644 --- a/target/m68k/cpu.h +++ b/target/m68k/cpu.h @@ -164,9 +164,7 @@ typedef struct CPUArchState { * A Motorola 68k CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ CPUM68KState env; }; diff --git a/target/microblaze/cpu-qom.h b/target/microblaze/cpu-qom.h index cda9220fa9..2a734e644d 100644 --- a/target/microblaze/cpu-qom.h +++ b/target/microblaze/cpu-qom.h @@ -35,9 +35,7 @@ OBJECT_DECLARE_CPU_TYPE(MicroBlazeCPU, MicroBlazeCPUClass, MICROBLAZE_CPU) * A MicroBlaze CPU model. */ struct MicroBlazeCPUClass { - /*< private >*/ CPUClass parent_class; - /*< public >*/ DeviceRealize parent_realize; ResettablePhases parent_phases; diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h index e43c49d4af..e8000237d8 100644 --- a/target/microblaze/cpu.h +++ b/target/microblaze/cpu.h @@ -343,9 +343,7 @@ typedef struct { * A MicroBlaze CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ CPUMBState env; diff --git a/target/mips/cpu-qom.h b/target/mips/cpu-qom.h index 0dffab453b..c70b4a34be 100644 --- a/target/mips/cpu-qom.h +++ b/target/mips/cpu-qom.h @@ -39,9 +39,7 @@ OBJECT_DECLARE_CPU_TYPE(MIPSCPU, MIPSCPUClass, MIPS_CPU) * A MIPS CPU model. */ struct MIPSCPUClass { - /*< private >*/ CPUClass parent_class; - /*< public >*/ DeviceRealize parent_realize; ResettablePhases parent_phases; diff --git a/target/mips/cpu.h b/target/mips/cpu.h index 5fddceff3a..617c373797 100644 --- a/target/mips/cpu.h +++ b/target/mips/cpu.h @@ -1209,9 +1209,7 @@ typedef struct CPUArchState { * A MIPS CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ CPUMIPSState env; diff --git a/target/nios2/cpu.h b/target/nios2/cpu.h index 70b6377a4f..ede1ba2140 100644 --- a/target/nios2/cpu.h +++ b/target/nios2/cpu.h @@ -42,9 +42,7 @@ OBJECT_DECLARE_CPU_TYPE(Nios2CPU, Nios2CPUClass, NIOS2_CPU) * A Nios2 CPU model. */ struct Nios2CPUClass { - /*< private >*/ CPUClass parent_class; - /*< public >*/ DeviceRealize parent_realize; ResettablePhases parent_phases; @@ -214,9 +212,7 @@ typedef struct { * A Nios2 CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ CPUNios2State env; diff --git a/target/openrisc/cpu.h b/target/openrisc/cpu.h index 334997e9a1..29cda7279c 100644 --- a/target/openrisc/cpu.h +++ b/target/openrisc/cpu.h @@ -39,9 +39,7 @@ OBJECT_DECLARE_CPU_TYPE(OpenRISCCPU, OpenRISCCPUClass, OPENRISC_CPU) * A OpenRISC CPU model. */ struct OpenRISCCPUClass { - /*< private >*/ CPUClass parent_class; - /*< public >*/ DeviceRealize parent_realize; ResettablePhases parent_phases; @@ -301,9 +299,7 @@ typedef struct CPUArchState { * A OpenRISC CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ CPUOpenRISCState env; }; diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 30392ebeee..24dd6b1b0a 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1313,9 +1313,7 @@ typedef struct PPCVirtualHypervisorClass PPCVirtualHypervisorClass; * A PowerPC CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ CPUPPCState env; diff --git a/target/riscv/cpu-qom.h b/target/riscv/cpu-qom.h index f3fbe37a2c..b9164a8e5b 100644 --- a/target/riscv/cpu-qom.h +++ b/target/riscv/cpu-qom.h @@ -63,9 +63,8 @@ OBJECT_DECLARE_CPU_TYPE(RISCVCPU, RISCVCPUClass, RISCV_CPU) * A RISCV CPU model. */ struct RISCVCPUClass { - /*< private >*/ CPUClass parent_class; - /*< public >*/ + DeviceRealize parent_realize; ResettablePhases parent_phases; }; diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 8efc4d83ec..149364745a 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -411,9 +411,7 @@ struct CPUArchState { * A RISCV CPU. */ struct ArchCPU { - /* < private > */ CPUState parent_obj; - /* < public > */ CPURISCVState env; diff --git a/target/rx/cpu-qom.h b/target/rx/cpu-qom.h index 1c8466a187..f4cd5664e5 100644 --- a/target/rx/cpu-qom.h +++ b/target/rx/cpu-qom.h @@ -36,9 +36,7 @@ OBJECT_DECLARE_CPU_TYPE(RXCPU, RXCPUClass, RX_CPU) * A RX CPU model. */ struct RXCPUClass { - /*< private >*/ CPUClass parent_class; - /*< public >*/ DeviceRealize parent_realize; ResettablePhases parent_phases; diff --git a/target/rx/cpu.h b/target/rx/cpu.h index f66754eb8a..8379f4a150 100644 --- a/target/rx/cpu.h +++ b/target/rx/cpu.h @@ -107,9 +107,7 @@ typedef struct CPUArchState { * A RX CPU */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ CPURXState env; }; diff --git a/target/s390x/cpu-qom.h b/target/s390x/cpu-qom.h index 00cae2b131..1088965fd5 100644 --- a/target/s390x/cpu-qom.h +++ b/target/s390x/cpu-qom.h @@ -49,9 +49,8 @@ typedef enum cpu_reset_type { * An S/390 CPU model. */ struct S390CPUClass { - /*< private >*/ CPUClass parent_class; - /*< public >*/ + const S390CPUDef *cpu_def; bool kvm_required; bool is_static; diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index 40c5cedd0e..4f366f9e4e 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -172,9 +172,7 @@ static inline uint64_t *get_freg(CPUS390XState *cs, int nr) * An S/390 CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ CPUS390XState env; S390CPUModel *model; diff --git a/target/sh4/cpu-qom.h b/target/sh4/cpu-qom.h index 89785a90f0..08fbebc996 100644 --- a/target/sh4/cpu-qom.h +++ b/target/sh4/cpu-qom.h @@ -42,9 +42,7 @@ OBJECT_DECLARE_CPU_TYPE(SuperHCPU, SuperHCPUClass, SUPERH_CPU) * A SuperH CPU model. */ struct SuperHCPUClass { - /*< private >*/ CPUClass parent_class; - /*< public >*/ DeviceRealize parent_realize; ResettablePhases parent_phases; diff --git a/target/sh4/cpu.h b/target/sh4/cpu.h index f75a235973..dc0561b73b 100644 --- a/target/sh4/cpu.h +++ b/target/sh4/cpu.h @@ -204,9 +204,7 @@ typedef struct CPUArchState { * A SuperH CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ CPUSH4State env; }; diff --git a/target/sparc/cpu-qom.h b/target/sparc/cpu-qom.h index 78bf00b9a2..b4a0db84ce 100644 --- a/target/sparc/cpu-qom.h +++ b/target/sparc/cpu-qom.h @@ -40,9 +40,7 @@ typedef struct sparc_def_t sparc_def_t; * A SPARC CPU model. */ struct SPARCCPUClass { - /*< private >*/ CPUClass parent_class; - /*< public >*/ DeviceRealize parent_realize; ResettablePhases parent_phases; diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index 3e361a5b75..b48004f8c3 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -562,9 +562,7 @@ struct CPUArchState { * A SPARC CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ CPUSPARCState env; }; diff --git a/target/tricore/cpu-qom.h b/target/tricore/cpu-qom.h index 612731daa0..b3b6c75a3a 100644 --- a/target/tricore/cpu-qom.h +++ b/target/tricore/cpu-qom.h @@ -27,9 +27,7 @@ OBJECT_DECLARE_CPU_TYPE(TriCoreCPU, TriCoreCPUClass, TRICORE_CPU) struct TriCoreCPUClass { - /*< private >*/ CPUClass parent_class; - /*< public >*/ DeviceRealize parent_realize; ResettablePhases parent_phases; diff --git a/target/tricore/cpu.h b/target/tricore/cpu.h index a357b573f2..b4a6ab141d 100644 --- a/target/tricore/cpu.h +++ b/target/tricore/cpu.h @@ -63,9 +63,7 @@ typedef struct CPUArchState { * A TriCore CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ CPUTriCoreState env; }; diff --git a/target/xtensa/cpu-qom.h b/target/xtensa/cpu-qom.h index 419c7d8e4a..424bcbd8dd 100644 --- a/target/xtensa/cpu-qom.h +++ b/target/xtensa/cpu-qom.h @@ -47,9 +47,7 @@ typedef struct XtensaConfig XtensaConfig; * An Xtensa CPU model. */ struct XtensaCPUClass { - /*< private >*/ CPUClass parent_class; - /*< public >*/ DeviceRealize parent_realize; ResettablePhases parent_phases; diff --git a/target/xtensa/cpu.h b/target/xtensa/cpu.h index c6bbef1e5d..85aab1bdf8 100644 --- a/target/xtensa/cpu.h +++ b/target/xtensa/cpu.h @@ -556,9 +556,7 @@ struct CPUArchState { * An Xtensa CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ CPUXtensaState env; Clock *clock; From 336588a29d27d7099155b0e9fa67560f1c454f3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 6 Oct 2023 10:31:27 +0200 Subject: [PATCH 774/974] target: Mention 'cpu-qom.h' is target agnostic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "target/foo/cpu-qom.h" is supposed to be target agnostic (include-able by any target). Add such mention in the header. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20231013140116.255-3-philmd@linaro.org> --- target/arm/cpu-qom.h | 2 +- target/hppa/cpu-qom.h | 2 +- target/microblaze/cpu-qom.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/target/arm/cpu-qom.h b/target/arm/cpu-qom.h index 153865d1bb..dfb9d5b827 100644 --- a/target/arm/cpu-qom.h +++ b/target/arm/cpu-qom.h @@ -1,5 +1,5 @@ /* - * QEMU ARM CPU + * QEMU ARM CPU QOM header (target agnostic) * * Copyright (c) 2012 SUSE LINUX Products GmbH * diff --git a/target/hppa/cpu-qom.h b/target/hppa/cpu-qom.h index cabd3b681e..b7d9cdfe11 100644 --- a/target/hppa/cpu-qom.h +++ b/target/hppa/cpu-qom.h @@ -1,5 +1,5 @@ /* - * QEMU HPPA CPU + * QEMU HPPA CPU QOM header (target agnostic) * * Copyright (c) 2016 Richard Henderson * diff --git a/target/microblaze/cpu-qom.h b/target/microblaze/cpu-qom.h index 2a734e644d..78f549b57d 100644 --- a/target/microblaze/cpu-qom.h +++ b/target/microblaze/cpu-qom.h @@ -1,5 +1,5 @@ /* - * QEMU MicroBlaze CPU + * QEMU MicroBlaze CPU QOM header (target agnostic) * * Copyright (c) 2012 SUSE LINUX Products GmbH * From f6524ddf86bc4af87ddc21b71151399cb47d484a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 13 Oct 2023 15:12:35 +0200 Subject: [PATCH 775/974] target/arm: Move internal declarations from 'cpu-qom.h' to 'cpu.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These definitions and declarations are only used by target/arm/, no need to expose them to generic hw/. Reviewed-by: Richard Henderson Message-Id: <20231013140116.255-4-philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé Message-Id: --- target/arm/cpu-qom.h | 28 ---------------------------- target/arm/cpu.h | 22 ++++++++++++++++++++++ target/arm/internals.h | 6 ++++++ 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/target/arm/cpu-qom.h b/target/arm/cpu-qom.h index dfb9d5b827..35c3b0924e 100644 --- a/target/arm/cpu-qom.h +++ b/target/arm/cpu-qom.h @@ -35,9 +35,6 @@ typedef struct ARMCPUInfo { void (*class_init)(ObjectClass *oc, void *data); } ARMCPUInfo; -void arm_cpu_register(const ARMCPUInfo *info); -void aarch64_cpu_register(const ARMCPUInfo *info); - /** * ARMCPUClass: * @parent_realize: The parent class' realize handler. @@ -63,29 +60,4 @@ struct AArch64CPUClass { ARMCPUClass parent_class; }; -void register_cp_regs_for_features(ARMCPU *cpu); -void init_cpreg_list(ARMCPU *cpu); - -/* Callback functions for the generic timer's timers. */ -void arm_gt_ptimer_cb(void *opaque); -void arm_gt_vtimer_cb(void *opaque); -void arm_gt_htimer_cb(void *opaque); -void arm_gt_stimer_cb(void *opaque); -void arm_gt_hvtimer_cb(void *opaque); - -#define ARM_AFF0_SHIFT 0 -#define ARM_AFF0_MASK (0xFFULL << ARM_AFF0_SHIFT) -#define ARM_AFF1_SHIFT 8 -#define ARM_AFF1_MASK (0xFFULL << ARM_AFF1_SHIFT) -#define ARM_AFF2_SHIFT 16 -#define ARM_AFF2_MASK (0xFFULL << ARM_AFF2_SHIFT) -#define ARM_AFF3_SHIFT 32 -#define ARM_AFF3_MASK (0xFFULL << ARM_AFF3_SHIFT) -#define ARM_DEFAULT_CPUS_PER_CLUSTER 8 - -#define ARM32_AFFINITY_MASK (ARM_AFF0_MASK|ARM_AFF1_MASK|ARM_AFF2_MASK) -#define ARM64_AFFINITY_MASK \ - (ARM_AFF0_MASK|ARM_AFF1_MASK|ARM_AFF2_MASK|ARM_AFF3_MASK) -#define ARM64_AFFINITY_INVALID (~ARM64_AFFINITY_MASK) - #endif diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 2f7ab22169..4a86c8f831 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1116,11 +1116,33 @@ struct ArchCPU { uint64_t gt_cntfrq_hz; }; +/* Callback functions for the generic timer's timers. */ +void arm_gt_ptimer_cb(void *opaque); +void arm_gt_vtimer_cb(void *opaque); +void arm_gt_htimer_cb(void *opaque); +void arm_gt_stimer_cb(void *opaque); +void arm_gt_hvtimer_cb(void *opaque); + unsigned int gt_cntfrq_period_ns(ARMCPU *cpu); void gt_rme_post_el_change(ARMCPU *cpu, void *opaque); void arm_cpu_post_init(Object *obj); +#define ARM_AFF0_SHIFT 0 +#define ARM_AFF0_MASK (0xFFULL << ARM_AFF0_SHIFT) +#define ARM_AFF1_SHIFT 8 +#define ARM_AFF1_MASK (0xFFULL << ARM_AFF1_SHIFT) +#define ARM_AFF2_SHIFT 16 +#define ARM_AFF2_MASK (0xFFULL << ARM_AFF2_SHIFT) +#define ARM_AFF3_SHIFT 32 +#define ARM_AFF3_MASK (0xFFULL << ARM_AFF3_SHIFT) +#define ARM_DEFAULT_CPUS_PER_CLUSTER 8 + +#define ARM32_AFFINITY_MASK (ARM_AFF0_MASK | ARM_AFF1_MASK | ARM_AFF2_MASK) +#define ARM64_AFFINITY_MASK \ + (ARM_AFF0_MASK | ARM_AFF1_MASK | ARM_AFF2_MASK | ARM_AFF3_MASK) +#define ARM64_AFFINITY_INVALID (~ARM64_AFFINITY_MASK) + uint64_t arm_cpu_mp_affinity(int idx, uint8_t clustersz); #ifndef CONFIG_USER_ONLY diff --git a/target/arm/internals.h b/target/arm/internals.h index c837506e44..143d57c0fe 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -183,6 +183,12 @@ static inline int r14_bank_number(int mode) return (mode == ARM_CPU_MODE_HYP) ? BANK_USRSYS : bank_number(mode); } +void arm_cpu_register(const ARMCPUInfo *info); +void aarch64_cpu_register(const ARMCPUInfo *info); + +void register_cp_regs_for_features(ARMCPU *cpu); +void init_cpreg_list(ARMCPU *cpu); + void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu); void arm_translate_init(void); From 37b9414b32521dc26e020f16191513c6423ada67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 6 Oct 2023 09:55:22 +0200 Subject: [PATCH 776/974] target/ppc: Remove CPU_RESOLVING_TYPE from 'cpu-qom.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CPU_RESOLVING_TYPE is a per-target definition, and is irrelevant for other targets. Move it to "cpu.h". "target/ppc/cpu-qom.h" is supposed to be target agnostic (include-able by any target). Add such mention in the header. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20231013140116.255-5-philmd@linaro.org> --- target/ppc/cpu-qom.h | 3 +-- target/ppc/cpu.h | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h index be33786bd8..41df51269b 100644 --- a/target/ppc/cpu-qom.h +++ b/target/ppc/cpu-qom.h @@ -1,5 +1,5 @@ /* - * QEMU PowerPC CPU + * QEMU PowerPC CPU QOM header (target agnostic) * * Copyright (c) 2012 SUSE LINUX Products GmbH * @@ -33,7 +33,6 @@ OBJECT_DECLARE_CPU_TYPE(PowerPCCPU, PowerPCCPUClass, POWERPC_CPU) #define POWERPC_CPU_TYPE_SUFFIX "-" TYPE_POWERPC_CPU #define POWERPC_CPU_TYPE_NAME(model) model POWERPC_CPU_TYPE_SUFFIX -#define CPU_RESOLVING_TYPE TYPE_POWERPC_CPU #define TYPE_HOST_POWERPC_CPU POWERPC_CPU_TYPE_NAME("host") diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 24dd6b1b0a..02619e5d54 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -27,6 +27,8 @@ #include "qom/object.h" #include "hw/registerfields.h" +#define CPU_RESOLVING_TYPE TYPE_POWERPC_CPU + #define TCG_GUEST_DEFAULT_MO 0 #define TARGET_PAGE_BITS_64K 16 From 66125f9360f2d698d772d045d1665ff6d380c6c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 6 Oct 2023 09:55:13 +0200 Subject: [PATCH 777/974] target/riscv: Remove CPU_RESOLVING_TYPE from 'cpu-qom.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CPU_RESOLVING_TYPE is a per-target definition, and is irrelevant for other targets. Move it to "cpu.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: LIU Zhiwei Reviewed-by: Richard Henderson Message-Id: <20231013140116.255-6-philmd@linaro.org> --- target/riscv/cpu-qom.h | 1 - target/riscv/cpu.h | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/target/riscv/cpu-qom.h b/target/riscv/cpu-qom.h index b9164a8e5b..b78169093f 100644 --- a/target/riscv/cpu-qom.h +++ b/target/riscv/cpu-qom.h @@ -27,7 +27,6 @@ #define RISCV_CPU_TYPE_SUFFIX "-" TYPE_RISCV_CPU #define RISCV_CPU_TYPE_NAME(name) (name RISCV_CPU_TYPE_SUFFIX) -#define CPU_RESOLVING_TYPE TYPE_RISCV_CPU #define TYPE_RISCV_CPU_ANY RISCV_CPU_TYPE_NAME("any") #define TYPE_RISCV_CPU_MAX RISCV_CPU_TYPE_NAME("max") diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 149364745a..5ad5d6d2bd 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -32,6 +32,8 @@ #include "qapi/qapi-types-common.h" #include "cpu-qom.h" +#define CPU_RESOLVING_TYPE TYPE_RISCV_CPU + #define TCG_GUEST_DEFAULT_MO 0 /* From 2d56be5a29eb05e33d9fb74bdf55013c5016d5ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 6 Oct 2023 10:58:06 +0200 Subject: [PATCH 778/974] target: Declare FOO_CPU_TYPE_NAME/SUFFIX in 'cpu-qom.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hegerogeneous code needs access to the FOO_CPU_TYPE_NAME() macro to resolve target CPU types. Move the declaration (along with the required FOO_CPU_TYPE_SUFFIX) to "cpu-qom.h". "target/foo/cpu-qom.h" is supposed to be target agnostic (include-able by any target). Add such mention in the header. Signed-off-by: Philippe Mathieu-Daudé Acked-by: LIU Zhiwei Reviewed-by: Richard Henderson Message-Id: <20231013140116.255-7-philmd@linaro.org> --- target/alpha/cpu-qom.h | 5 ++++- target/alpha/cpu.h | 2 -- target/avr/cpu-qom.h | 5 ++++- target/avr/cpu.h | 2 -- target/cris/cpu-qom.h | 5 ++++- target/cris/cpu.h | 2 -- target/i386/cpu-qom.h | 3 +++ target/i386/cpu.h | 2 -- target/m68k/cpu-qom.h | 5 ++++- target/m68k/cpu.h | 2 -- target/mips/cpu-qom.h | 3 +++ target/mips/cpu.h | 2 -- target/rx/cpu-qom.h | 5 ++++- target/rx/cpu.h | 2 -- target/s390x/cpu-qom.h | 5 ++++- target/s390x/cpu.h | 2 -- target/sh4/cpu-qom.h | 5 ++++- target/sh4/cpu.h | 2 -- target/sparc/cpu-qom.h | 5 ++++- target/sparc/cpu.h | 2 -- target/tricore/cpu-qom.h | 5 +++++ target/tricore/cpu.h | 2 -- target/xtensa/cpu-qom.h | 5 ++++- target/xtensa/cpu.h | 2 -- 24 files changed, 47 insertions(+), 33 deletions(-) diff --git a/target/alpha/cpu-qom.h b/target/alpha/cpu-qom.h index c5fbd8f11a..c4a4523993 100644 --- a/target/alpha/cpu-qom.h +++ b/target/alpha/cpu-qom.h @@ -1,5 +1,5 @@ /* - * QEMU Alpha CPU + * QEMU Alpha CPU QOM header (target agnostic) * * Copyright (c) 2012 SUSE LINUX Products GmbH * @@ -27,6 +27,9 @@ OBJECT_DECLARE_CPU_TYPE(AlphaCPU, AlphaCPUClass, ALPHA_CPU) +#define ALPHA_CPU_TYPE_SUFFIX "-" TYPE_ALPHA_CPU +#define ALPHA_CPU_TYPE_NAME(model) model ALPHA_CPU_TYPE_SUFFIX + /** * AlphaCPUClass: * @parent_realize: The parent class' realize handler. diff --git a/target/alpha/cpu.h b/target/alpha/cpu.h index c8d97ac27a..3bff56c565 100644 --- a/target/alpha/cpu.h +++ b/target/alpha/cpu.h @@ -426,8 +426,6 @@ enum { void alpha_translate_init(void); -#define ALPHA_CPU_TYPE_SUFFIX "-" TYPE_ALPHA_CPU -#define ALPHA_CPU_TYPE_NAME(model) model ALPHA_CPU_TYPE_SUFFIX #define CPU_RESOLVING_TYPE TYPE_ALPHA_CPU void alpha_cpu_list(void); diff --git a/target/avr/cpu-qom.h b/target/avr/cpu-qom.h index d89be01e0f..75590cdd97 100644 --- a/target/avr/cpu-qom.h +++ b/target/avr/cpu-qom.h @@ -1,5 +1,5 @@ /* - * QEMU AVR CPU + * QEMU AVR CPU QOM header (target agnostic) * * Copyright (c) 2016-2020 Michael Rolnik * @@ -28,6 +28,9 @@ OBJECT_DECLARE_CPU_TYPE(AVRCPU, AVRCPUClass, AVR_CPU) +#define AVR_CPU_TYPE_SUFFIX "-" TYPE_AVR_CPU +#define AVR_CPU_TYPE_NAME(name) (name AVR_CPU_TYPE_SUFFIX) + /** * AVRCPUClass: * @parent_realize: The parent class' realize handler. diff --git a/target/avr/cpu.h b/target/avr/cpu.h index f8b065ed79..0487399cb2 100644 --- a/target/avr/cpu.h +++ b/target/avr/cpu.h @@ -28,8 +28,6 @@ #error "AVR 8-bit does not support user mode" #endif -#define AVR_CPU_TYPE_SUFFIX "-" TYPE_AVR_CPU -#define AVR_CPU_TYPE_NAME(name) (name AVR_CPU_TYPE_SUFFIX) #define CPU_RESOLVING_TYPE TYPE_AVR_CPU #define TCG_GUEST_DEFAULT_MO 0 diff --git a/target/cris/cpu-qom.h b/target/cris/cpu-qom.h index c2fee242f4..d7e5f33e62 100644 --- a/target/cris/cpu-qom.h +++ b/target/cris/cpu-qom.h @@ -1,5 +1,5 @@ /* - * QEMU CRIS CPU + * QEMU CRIS CPU QOM header (target agnostic) * * Copyright (c) 2012 SUSE LINUX Products GmbH * @@ -27,6 +27,9 @@ OBJECT_DECLARE_CPU_TYPE(CRISCPU, CRISCPUClass, CRIS_CPU) +#define CRIS_CPU_TYPE_SUFFIX "-" TYPE_CRIS_CPU +#define CRIS_CPU_TYPE_NAME(name) (name CRIS_CPU_TYPE_SUFFIX) + /** * CRISCPUClass: * @parent_realize: The parent class' realize handler. diff --git a/target/cris/cpu.h b/target/cris/cpu.h index 6aa445348f..b821bb7983 100644 --- a/target/cris/cpu.h +++ b/target/cris/cpu.h @@ -240,8 +240,6 @@ enum { /* CRIS uses 8k pages. */ #define MMAP_SHIFT TARGET_PAGE_BITS -#define CRIS_CPU_TYPE_SUFFIX "-" TYPE_CRIS_CPU -#define CRIS_CPU_TYPE_NAME(name) (name CRIS_CPU_TYPE_SUFFIX) #define CPU_RESOLVING_TYPE TYPE_CRIS_CPU /* MMU modes definitions */ diff --git a/target/i386/cpu-qom.h b/target/i386/cpu-qom.h index 58145717ef..dffc74c1ce 100644 --- a/target/i386/cpu-qom.h +++ b/target/i386/cpu-qom.h @@ -32,6 +32,9 @@ OBJECT_DECLARE_CPU_TYPE(X86CPU, X86CPUClass, X86_CPU) +#define X86_CPU_TYPE_SUFFIX "-" TYPE_X86_CPU +#define X86_CPU_TYPE_NAME(name) (name X86_CPU_TYPE_SUFFIX) + typedef struct X86CPUModel X86CPUModel; /** diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 096f85483e..6c6b066986 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2239,8 +2239,6 @@ void cpu_x86_update_dr7(CPUX86State *env, uint32_t new_dr7); /* hw/pc.c */ uint64_t cpu_get_tsc(CPUX86State *env); -#define X86_CPU_TYPE_SUFFIX "-" TYPE_X86_CPU -#define X86_CPU_TYPE_NAME(name) (name X86_CPU_TYPE_SUFFIX) #define CPU_RESOLVING_TYPE TYPE_X86_CPU #ifdef TARGET_X86_64 diff --git a/target/m68k/cpu-qom.h b/target/m68k/cpu-qom.h index 13d94c9fe3..df0cc8b7a3 100644 --- a/target/m68k/cpu-qom.h +++ b/target/m68k/cpu-qom.h @@ -1,5 +1,5 @@ /* - * QEMU Motorola 68k CPU + * QEMU Motorola 68k CPU QOM header (target agnostic) * * Copyright (c) 2012 SUSE LINUX Products GmbH * @@ -27,6 +27,9 @@ OBJECT_DECLARE_CPU_TYPE(M68kCPU, M68kCPUClass, M68K_CPU) +#define M68K_CPU_TYPE_SUFFIX "-" TYPE_M68K_CPU +#define M68K_CPU_TYPE_NAME(model) model M68K_CPU_TYPE_SUFFIX + /* * M68kCPUClass: * @parent_realize: The parent class' realize handler. diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h index 9ea18028ad..7f34686a6f 100644 --- a/target/m68k/cpu.h +++ b/target/m68k/cpu.h @@ -561,8 +561,6 @@ enum { ACCESS_DATA = 0x20, /* Data load/store access */ }; -#define M68K_CPU_TYPE_SUFFIX "-" TYPE_M68K_CPU -#define M68K_CPU_TYPE_NAME(model) model M68K_CPU_TYPE_SUFFIX #define CPU_RESOLVING_TYPE TYPE_M68K_CPU #define cpu_list m68k_cpu_list diff --git a/target/mips/cpu-qom.h b/target/mips/cpu-qom.h index c70b4a34be..5822dfb1d2 100644 --- a/target/mips/cpu-qom.h +++ b/target/mips/cpu-qom.h @@ -31,6 +31,9 @@ OBJECT_DECLARE_CPU_TYPE(MIPSCPU, MIPSCPUClass, MIPS_CPU) +#define MIPS_CPU_TYPE_SUFFIX "-" TYPE_MIPS_CPU +#define MIPS_CPU_TYPE_NAME(model) model MIPS_CPU_TYPE_SUFFIX + /** * MIPSCPUClass: * @parent_realize: The parent class' realize handler. diff --git a/target/mips/cpu.h b/target/mips/cpu.h index 617c373797..12cc1bfafd 100644 --- a/target/mips/cpu.h +++ b/target/mips/cpu.h @@ -1301,8 +1301,6 @@ enum { */ #define CPU_INTERRUPT_WAKE CPU_INTERRUPT_TGT_INT_0 -#define MIPS_CPU_TYPE_SUFFIX "-" TYPE_MIPS_CPU -#define MIPS_CPU_TYPE_NAME(model) model MIPS_CPU_TYPE_SUFFIX #define CPU_RESOLVING_TYPE TYPE_MIPS_CPU bool cpu_type_supports_cps_smp(const char *cpu_type); diff --git a/target/rx/cpu-qom.h b/target/rx/cpu-qom.h index f4cd5664e5..6213d877f7 100644 --- a/target/rx/cpu-qom.h +++ b/target/rx/cpu-qom.h @@ -1,5 +1,5 @@ /* - * RX CPU + * QEMU RX CPU QOM header (target agnostic) * * Copyright (c) 2019 Yoshinori Sato * @@ -28,6 +28,9 @@ OBJECT_DECLARE_CPU_TYPE(RXCPU, RXCPUClass, RX_CPU) +#define RX_CPU_TYPE_SUFFIX "-" TYPE_RX_CPU +#define RX_CPU_TYPE_NAME(model) model RX_CPU_TYPE_SUFFIX + /* * RXCPUClass: * @parent_realize: The parent class' realize handler. diff --git a/target/rx/cpu.h b/target/rx/cpu.h index 8379f4a150..c81613770c 100644 --- a/target/rx/cpu.h +++ b/target/rx/cpu.h @@ -112,8 +112,6 @@ struct ArchCPU { CPURXState env; }; -#define RX_CPU_TYPE_SUFFIX "-" TYPE_RX_CPU -#define RX_CPU_TYPE_NAME(model) model RX_CPU_TYPE_SUFFIX #define CPU_RESOLVING_TYPE TYPE_RX_CPU const char *rx_crname(uint8_t cr); diff --git a/target/s390x/cpu-qom.h b/target/s390x/cpu-qom.h index 1088965fd5..fcd70daddf 100644 --- a/target/s390x/cpu-qom.h +++ b/target/s390x/cpu-qom.h @@ -1,5 +1,5 @@ /* - * QEMU S/390 CPU + * QEMU S/390 CPU QOM header (target agnostic) * * Copyright (c) 2012 SUSE LINUX Products GmbH * @@ -27,6 +27,9 @@ OBJECT_DECLARE_CPU_TYPE(S390CPU, S390CPUClass, S390_CPU) +#define S390_CPU_TYPE_SUFFIX "-" TYPE_S390_CPU +#define S390_CPU_TYPE_NAME(name) (name S390_CPU_TYPE_SUFFIX) + typedef struct S390CPUModel S390CPUModel; typedef struct S390CPUDef S390CPUDef; diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index 4f366f9e4e..38d7197f4c 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -890,8 +890,6 @@ void s390_set_qemu_cpu_model(uint16_t type, uint8_t gen, uint8_t ec_ga, /* helper.c */ -#define S390_CPU_TYPE_SUFFIX "-" TYPE_S390_CPU -#define S390_CPU_TYPE_NAME(name) (name S390_CPU_TYPE_SUFFIX) #define CPU_RESOLVING_TYPE TYPE_S390_CPU /* interrupt.c */ diff --git a/target/sh4/cpu-qom.h b/target/sh4/cpu-qom.h index 08fbebc996..bd0ef49fa1 100644 --- a/target/sh4/cpu-qom.h +++ b/target/sh4/cpu-qom.h @@ -1,5 +1,5 @@ /* - * QEMU SuperH CPU + * QEMU SuperH CPU QOM header (target agnostic) * * Copyright (c) 2012 SUSE LINUX Products GmbH * @@ -31,6 +31,9 @@ OBJECT_DECLARE_CPU_TYPE(SuperHCPU, SuperHCPUClass, SUPERH_CPU) +#define SUPERH_CPU_TYPE_SUFFIX "-" TYPE_SUPERH_CPU +#define SUPERH_CPU_TYPE_NAME(model) model SUPERH_CPU_TYPE_SUFFIX + /** * SuperHCPUClass: * @parent_realize: The parent class' realize handler. diff --git a/target/sh4/cpu.h b/target/sh4/cpu.h index dc0561b73b..dbe00e29c2 100644 --- a/target/sh4/cpu.h +++ b/target/sh4/cpu.h @@ -250,8 +250,6 @@ int cpu_sh4_is_cached(CPUSH4State * env, target_ulong addr); void cpu_load_tlb(CPUSH4State * env); -#define SUPERH_CPU_TYPE_SUFFIX "-" TYPE_SUPERH_CPU -#define SUPERH_CPU_TYPE_NAME(model) model SUPERH_CPU_TYPE_SUFFIX #define CPU_RESOLVING_TYPE TYPE_SUPERH_CPU #define cpu_list sh4_cpu_list diff --git a/target/sparc/cpu-qom.h b/target/sparc/cpu-qom.h index b4a0db84ce..aca29415b4 100644 --- a/target/sparc/cpu-qom.h +++ b/target/sparc/cpu-qom.h @@ -1,5 +1,5 @@ /* - * QEMU SPARC CPU + * QEMU SPARC CPU QOM header (target agnostic) * * Copyright (c) 2012 SUSE LINUX Products GmbH * @@ -31,6 +31,9 @@ OBJECT_DECLARE_CPU_TYPE(SPARCCPU, SPARCCPUClass, SPARC_CPU) +#define SPARC_CPU_TYPE_SUFFIX "-" TYPE_SPARC_CPU +#define SPARC_CPU_TYPE_NAME(model) model SPARC_CPU_TYPE_SUFFIX + typedef struct sparc_def_t sparc_def_t; /** * SPARCCPUClass: diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index b48004f8c3..a808f2aff6 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -654,8 +654,6 @@ hwaddr cpu_get_phys_page_nofault(CPUSPARCState *env, target_ulong addr, #endif #endif -#define SPARC_CPU_TYPE_SUFFIX "-" TYPE_SPARC_CPU -#define SPARC_CPU_TYPE_NAME(model) model SPARC_CPU_TYPE_SUFFIX #define CPU_RESOLVING_TYPE TYPE_SPARC_CPU #define cpu_list sparc_cpu_list diff --git a/target/tricore/cpu-qom.h b/target/tricore/cpu-qom.h index b3b6c75a3a..2598651008 100644 --- a/target/tricore/cpu-qom.h +++ b/target/tricore/cpu-qom.h @@ -1,4 +1,6 @@ /* + * QEMU TriCore CPU QOM header (target agnostic) + * * Copyright (c) 2012-2014 Bastian Koppelmann C-Lab/University Paderborn * * This library is free software; you can redistribute it and/or @@ -26,6 +28,9 @@ OBJECT_DECLARE_CPU_TYPE(TriCoreCPU, TriCoreCPUClass, TRICORE_CPU) +#define TRICORE_CPU_TYPE_SUFFIX "-" TYPE_TRICORE_CPU +#define TRICORE_CPU_TYPE_NAME(model) model TRICORE_CPU_TYPE_SUFFIX + struct TriCoreCPUClass { CPUClass parent_class; diff --git a/target/tricore/cpu.h b/target/tricore/cpu.h index b4a6ab141d..c537a33ee8 100644 --- a/target/tricore/cpu.h +++ b/target/tricore/cpu.h @@ -268,8 +268,6 @@ static inline void cpu_get_tb_cpu_state(CPUTriCoreState *env, vaddr *pc, *flags = new_flags; } -#define TRICORE_CPU_TYPE_SUFFIX "-" TYPE_TRICORE_CPU -#define TRICORE_CPU_TYPE_NAME(model) model TRICORE_CPU_TYPE_SUFFIX #define CPU_RESOLVING_TYPE TYPE_TRICORE_CPU /* helpers.c */ diff --git a/target/xtensa/cpu-qom.h b/target/xtensa/cpu-qom.h index 424bcbd8dd..03873ea50b 100644 --- a/target/xtensa/cpu-qom.h +++ b/target/xtensa/cpu-qom.h @@ -1,5 +1,5 @@ /* - * QEMU Xtensa CPU + * QEMU Xtensa CPU QOM header (target agnostic) * * Copyright (c) 2012 SUSE LINUX Products GmbH * All rights reserved. @@ -36,6 +36,9 @@ OBJECT_DECLARE_CPU_TYPE(XtensaCPU, XtensaCPUClass, XTENSA_CPU) +#define XTENSA_CPU_TYPE_SUFFIX "-" TYPE_XTENSA_CPU +#define XTENSA_CPU_TYPE_NAME(model) model XTENSA_CPU_TYPE_SUFFIX + typedef struct XtensaConfig XtensaConfig; /** diff --git a/target/xtensa/cpu.h b/target/xtensa/cpu.h index 85aab1bdf8..d6d2fb1f4e 100644 --- a/target/xtensa/cpu.h +++ b/target/xtensa/cpu.h @@ -586,8 +586,6 @@ G_NORETURN void xtensa_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, #define cpu_list xtensa_cpu_list -#define XTENSA_CPU_TYPE_SUFFIX "-" TYPE_XTENSA_CPU -#define XTENSA_CPU_TYPE_NAME(model) model XTENSA_CPU_TYPE_SUFFIX #define CPU_RESOLVING_TYPE TYPE_XTENSA_CPU #if TARGET_BIG_ENDIAN From 7b6917b9b98287125f139872a0351eb1e7e8350b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 6 Oct 2023 10:29:33 +0200 Subject: [PATCH 779/974] target/hexagon: Declare QOM definitions in 'cpu-qom.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "target/foo/cpu.h" contains the target specific declarations. A heterogeneous setup need to access target agnostic declarations (at least the QOM ones, to instantiate the objects). Our convention is to add such target agnostic QOM declarations in the "target/foo/cpu-qom.h" header. Add a comment clarifying that in the header. Extract QOM definitions from "cpu.h" to "cpu-qom.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Brian Cain Message-Id: <20231013140116.255-8-philmd@linaro.org> --- target/hexagon/cpu-qom.h | 28 ++++++++++++++++++++++++++++ target/hexagon/cpu.h | 15 +-------------- 2 files changed, 29 insertions(+), 14 deletions(-) create mode 100644 target/hexagon/cpu-qom.h diff --git a/target/hexagon/cpu-qom.h b/target/hexagon/cpu-qom.h new file mode 100644 index 0000000000..f02df7ee6f --- /dev/null +++ b/target/hexagon/cpu-qom.h @@ -0,0 +1,28 @@ +/* + * QEMU Hexagon CPU QOM header (target agnostic) + * + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef QEMU_HEXAGON_CPU_QOM_H +#define QEMU_HEXAGON_CPU_QOM_H + +#include "hw/core/cpu.h" +#include "qom/object.h" + +#define TYPE_HEXAGON_CPU "hexagon-cpu" + +#define HEXAGON_CPU_TYPE_SUFFIX "-" TYPE_HEXAGON_CPU +#define HEXAGON_CPU_TYPE_NAME(name) (name HEXAGON_CPU_TYPE_SUFFIX) + +#define TYPE_HEXAGON_CPU_V67 HEXAGON_CPU_TYPE_NAME("v67") +#define TYPE_HEXAGON_CPU_V68 HEXAGON_CPU_TYPE_NAME("v68") +#define TYPE_HEXAGON_CPU_V69 HEXAGON_CPU_TYPE_NAME("v69") +#define TYPE_HEXAGON_CPU_V71 HEXAGON_CPU_TYPE_NAME("v71") +#define TYPE_HEXAGON_CPU_V73 HEXAGON_CPU_TYPE_NAME("v73") + +OBJECT_DECLARE_CPU_TYPE(HexagonCPU, HexagonCPUClass, HEXAGON_CPU) + +#endif diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h index 035ac4fb6d..7d16083c6a 100644 --- a/target/hexagon/cpu.h +++ b/target/hexagon/cpu.h @@ -20,11 +20,10 @@ #include "fpu/softfloat-types.h" +#include "cpu-qom.h" #include "exec/cpu-defs.h" #include "hex_regs.h" #include "mmvec/mmvec.h" -#include "qom/object.h" -#include "hw/core/cpu.h" #include "hw/registerfields.h" #define NUM_PREGS 4 @@ -36,18 +35,8 @@ #define PRED_WRITES_MAX 5 /* 4 insns + endloop */ #define VSTORES_MAX 2 -#define TYPE_HEXAGON_CPU "hexagon-cpu" - -#define HEXAGON_CPU_TYPE_SUFFIX "-" TYPE_HEXAGON_CPU -#define HEXAGON_CPU_TYPE_NAME(name) (name HEXAGON_CPU_TYPE_SUFFIX) #define CPU_RESOLVING_TYPE TYPE_HEXAGON_CPU -#define TYPE_HEXAGON_CPU_V67 HEXAGON_CPU_TYPE_NAME("v67") -#define TYPE_HEXAGON_CPU_V68 HEXAGON_CPU_TYPE_NAME("v68") -#define TYPE_HEXAGON_CPU_V69 HEXAGON_CPU_TYPE_NAME("v69") -#define TYPE_HEXAGON_CPU_V71 HEXAGON_CPU_TYPE_NAME("v71") -#define TYPE_HEXAGON_CPU_V73 HEXAGON_CPU_TYPE_NAME("v73") - void hexagon_cpu_list(void); #define cpu_list hexagon_cpu_list @@ -127,8 +116,6 @@ typedef struct CPUArchState { VTCMStoreLog vtcm_log; } CPUHexagonState; -OBJECT_DECLARE_CPU_TYPE(HexagonCPU, HexagonCPUClass, HEXAGON_CPU) - typedef struct HexagonCPUClass { CPUClass parent_class; From edcea147e342a2ae1cb45a3beb40b428f93b1d1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 6 Oct 2023 10:54:26 +0200 Subject: [PATCH 780/974] target/loongarch: Declare QOM definitions in 'cpu-qom.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "target/foo/cpu.h" contains the target specific declarations. A heterogeneous setup need to access target agnostic declarations (at least the QOM ones, to instantiate the objects). Our convention is to add such target agnostic QOM declarations in the "target/foo/cpu-qom.h" header. Add a comment clarifying that in the header. Extract QOM definitions from "cpu.h" to "cpu-qom.h". Reviewed-by: Song Gao Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20231013140116.255-9-philmd@linaro.org> --- target/loongarch/cpu-qom.h | 24 ++++++++++++++++++++++++ target/loongarch/cpu.h | 10 +--------- 2 files changed, 25 insertions(+), 9 deletions(-) create mode 100644 target/loongarch/cpu-qom.h diff --git a/target/loongarch/cpu-qom.h b/target/loongarch/cpu-qom.h new file mode 100644 index 0000000000..82c86d146d --- /dev/null +++ b/target/loongarch/cpu-qom.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * QEMU LoongArch CPU QOM header (target agnostic) + * + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#ifndef LOONGARCH_CPU_QOM_H +#define LOONGARCH_CPU_QOM_H + +#include "hw/core/cpu.h" +#include "qom/object.h" + +#define TYPE_LOONGARCH_CPU "loongarch-cpu" +#define TYPE_LOONGARCH32_CPU "loongarch32-cpu" +#define TYPE_LOONGARCH64_CPU "loongarch64-cpu" + +OBJECT_DECLARE_CPU_TYPE(LoongArchCPU, LoongArchCPUClass, + LOONGARCH_CPU) + +#define LOONGARCH_CPU_TYPE_SUFFIX "-" TYPE_LOONGARCH_CPU +#define LOONGARCH_CPU_TYPE_NAME(model) model LOONGARCH_CPU_TYPE_SUFFIX + +#endif diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 555ea1f8d9..00d1fba597 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -17,6 +17,7 @@ #include "exec/memory.h" #endif #include "cpu-csr.h" +#include "cpu-qom.h" #define IOCSRF_TEMP 0 #define IOCSRF_NODECNT 1 @@ -381,13 +382,6 @@ struct ArchCPU { const char *dtb_compatible; }; -#define TYPE_LOONGARCH_CPU "loongarch-cpu" -#define TYPE_LOONGARCH32_CPU "loongarch32-cpu" -#define TYPE_LOONGARCH64_CPU "loongarch64-cpu" - -OBJECT_DECLARE_CPU_TYPE(LoongArchCPU, LoongArchCPUClass, - LOONGARCH_CPU) - /** * LoongArchCPUClass: * @parent_realize: The parent class' realize handler. @@ -478,8 +472,6 @@ void loongarch_cpu_list(void); #include "exec/cpu-all.h" -#define LOONGARCH_CPU_TYPE_SUFFIX "-" TYPE_LOONGARCH_CPU -#define LOONGARCH_CPU_TYPE_NAME(model) model LOONGARCH_CPU_TYPE_SUFFIX #define CPU_RESOLVING_TYPE TYPE_LOONGARCH_CPU void loongarch_cpu_post_init(Object *obj); From d3680640f18e3f950425e9614c36c154ff43ddb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 6 Oct 2023 10:30:15 +0200 Subject: [PATCH 781/974] target/nios2: Declare QOM definitions in 'cpu-qom.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "target/foo/cpu.h" contains the target specific declarations. A heterogeneous setup need to access target agnostic declarations (at least the QOM ones, to instantiate the objects). Our convention is to add such target agnostic QOM declarations in the "target/foo/cpu-qom.h" header. Add a comment clarifying that in the header. Extract QOM definitions from "cpu.h" to "cpu-qom.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20231013140116.255-10-philmd@linaro.org> --- target/nios2/cpu-qom.h | 19 +++++++++++++++++++ target/nios2/cpu.h | 7 +------ 2 files changed, 20 insertions(+), 6 deletions(-) create mode 100644 target/nios2/cpu-qom.h diff --git a/target/nios2/cpu-qom.h b/target/nios2/cpu-qom.h new file mode 100644 index 0000000000..931bc69b10 --- /dev/null +++ b/target/nios2/cpu-qom.h @@ -0,0 +1,19 @@ +/* + * QEMU Nios II CPU QOM header (target agnostic) + * + * Copyright (c) 2012 Chris Wulff + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef QEMU_NIOS2_CPU_QOM_H +#define QEMU_NIOS2_CPU_QOM_H + +#include "hw/core/cpu.h" +#include "qom/object.h" + +#define TYPE_NIOS2_CPU "nios2-cpu" + +OBJECT_DECLARE_CPU_TYPE(Nios2CPU, Nios2CPUClass, NIOS2_CPU) + +#endif diff --git a/target/nios2/cpu.h b/target/nios2/cpu.h index ede1ba2140..2d79b5b298 100644 --- a/target/nios2/cpu.h +++ b/target/nios2/cpu.h @@ -21,20 +21,15 @@ #ifndef NIOS2_CPU_H #define NIOS2_CPU_H +#include "cpu-qom.h" #include "exec/cpu-defs.h" -#include "hw/core/cpu.h" #include "hw/registerfields.h" -#include "qom/object.h" typedef struct CPUArchState CPUNios2State; #if !defined(CONFIG_USER_ONLY) #include "mmu.h" #endif -#define TYPE_NIOS2_CPU "nios2-cpu" - -OBJECT_DECLARE_CPU_TYPE(Nios2CPU, Nios2CPUClass, NIOS2_CPU) - /** * Nios2CPUClass: * @parent_phases: The parent class' reset phase handlers. From 2d8efe9666dc4297838d9e592d3524285cfb49d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 6 Oct 2023 10:29:20 +0200 Subject: [PATCH 782/974] target/openrisc: Declare QOM definitions in 'cpu-qom.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "target/foo/cpu.h" contains the target specific declarations. A heterogeneous setup need to access target agnostic declarations (at least the QOM ones, to instantiate the objects). Our convention is to add such target agnostic QOM declarations in the "target/foo/cpu-qom.h" header. Add a comment clarifying that in the header. Extract QOM definitions from "cpu.h" to "cpu-qom.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20231013140116.255-11-philmd@linaro.org> --- target/openrisc/cpu-qom.h | 22 ++++++++++++++++++++++ target/openrisc/cpu.h | 10 +--------- 2 files changed, 23 insertions(+), 9 deletions(-) create mode 100644 target/openrisc/cpu-qom.h diff --git a/target/openrisc/cpu-qom.h b/target/openrisc/cpu-qom.h new file mode 100644 index 0000000000..1ba9fb0a4c --- /dev/null +++ b/target/openrisc/cpu-qom.h @@ -0,0 +1,22 @@ +/* + * QEMU OpenRISC CPU QOM header (target agnostic) + * + * Copyright (c) 2011-2012 Jia Liu + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef QEMU_OPENRISC_CPU_QOM_H +#define QEMU_OPENRISC_CPU_QOM_H + +#include "hw/core/cpu.h" +#include "qom/object.h" + +#define TYPE_OPENRISC_CPU "or1k-cpu" + +OBJECT_DECLARE_CPU_TYPE(OpenRISCCPU, OpenRISCCPUClass, OPENRISC_CPU) + +#define OPENRISC_CPU_TYPE_SUFFIX "-" TYPE_OPENRISC_CPU +#define OPENRISC_CPU_TYPE_NAME(model) model OPENRISC_CPU_TYPE_SUFFIX + +#endif diff --git a/target/openrisc/cpu.h b/target/openrisc/cpu.h index 29cda7279c..dedeb89f8e 100644 --- a/target/openrisc/cpu.h +++ b/target/openrisc/cpu.h @@ -20,17 +20,12 @@ #ifndef OPENRISC_CPU_H #define OPENRISC_CPU_H +#include "cpu-qom.h" #include "exec/cpu-defs.h" #include "fpu/softfloat-types.h" -#include "hw/core/cpu.h" -#include "qom/object.h" #define TCG_GUEST_DEFAULT_MO (0) -#define TYPE_OPENRISC_CPU "or1k-cpu" - -OBJECT_DECLARE_CPU_TYPE(OpenRISCCPU, OpenRISCCPUClass, OPENRISC_CPU) - /** * OpenRISCCPUClass: * @parent_realize: The parent class' realize handler. @@ -304,7 +299,6 @@ struct ArchCPU { CPUOpenRISCState env; }; - void cpu_openrisc_list(void); void openrisc_cpu_dump_state(CPUState *cpu, FILE *f, int flags); int openrisc_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); @@ -339,8 +333,6 @@ void cpu_openrisc_count_start(OpenRISCCPU *cpu); void cpu_openrisc_count_stop(OpenRISCCPU *cpu); #endif -#define OPENRISC_CPU_TYPE_SUFFIX "-" TYPE_OPENRISC_CPU -#define OPENRISC_CPU_TYPE_NAME(model) model OPENRISC_CPU_TYPE_SUFFIX #define CPU_RESOLVING_TYPE TYPE_OPENRISC_CPU #include "exec/cpu-all.h" From 27a6e78ef00c778d0d6d563f90c07deb5176e296 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 6 Oct 2023 09:45:40 +0200 Subject: [PATCH 783/974] target/riscv: Move TYPE_RISCV_CPU_BASE definition to 'cpu.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TYPE_RISCV_CPU_BASE depends on the TARGET_RISCV32/TARGET_RISCV64 definitions which are target specific. Such target specific definition taints "cpu-qom.h". Since "cpu-qom.h" must be target agnostic, remove its target specific definition uses by moving TYPE_RISCV_CPU_BASE to "target/riscv/cpu.h". "target/riscv/cpu-qom.h" is now fully target agnostic. Add a comment clarifying that in the header. Reviewed-by: LIU Zhiwei Reviewed-by: Alistair Francis Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20231013140116.255-12-philmd@linaro.org> --- target/riscv/cpu-qom.h | 8 +------- target/riscv/cpu.h | 6 ++++++ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/target/riscv/cpu-qom.h b/target/riscv/cpu-qom.h index b78169093f..76efb614a6 100644 --- a/target/riscv/cpu-qom.h +++ b/target/riscv/cpu-qom.h @@ -1,5 +1,5 @@ /* - * QEMU RISC-V CPU QOM header + * QEMU RISC-V CPU QOM header (target agnostic) * * Copyright (c) 2023 Ventana Micro Systems Inc. * @@ -44,12 +44,6 @@ #define TYPE_RISCV_CPU_VEYRON_V1 RISCV_CPU_TYPE_NAME("veyron-v1") #define TYPE_RISCV_CPU_HOST RISCV_CPU_TYPE_NAME("host") -#if defined(TARGET_RISCV32) -# define TYPE_RISCV_CPU_BASE TYPE_RISCV_CPU_BASE32 -#elif defined(TARGET_RISCV64) -# define TYPE_RISCV_CPU_BASE TYPE_RISCV_CPU_BASE64 -#endif - typedef struct CPUArchState CPURISCVState; OBJECT_DECLARE_CPU_TYPE(RISCVCPU, RISCVCPUClass, RISCV_CPU) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 5ad5d6d2bd..b49fa17e68 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -34,6 +34,12 @@ #define CPU_RESOLVING_TYPE TYPE_RISCV_CPU +#if defined(TARGET_RISCV32) +# define TYPE_RISCV_CPU_BASE TYPE_RISCV_CPU_BASE32 +#elif defined(TARGET_RISCV64) +# define TYPE_RISCV_CPU_BASE TYPE_RISCV_CPU_BASE64 +#endif + #define TCG_GUEST_DEFAULT_MO 0 /* From b0a1333331abcf38b6d18a6b97f8f561afc4f48f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 9 Oct 2023 12:42:36 +0200 Subject: [PATCH 784/974] target/ppc: Use env_archcpu() in helper_book3s_msgsndp() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When CPUArchState* is available (here CPUPPCState*), we can use the fast env_archcpu() macro to get ArchCPU* (here PowerPCCPU*). The QOM cast POWERPC_CPU() macro will be slower when building with --enable-qom-cast-debug. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel Henrique Barboza Reviewed-by: Richard Henderson Acked-by: Alistair Francis Message-Id: <20231009110239.66778-2-philmd@linaro.org> --- target/ppc/excp_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 7926114d5c..a42743a3e0 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -3136,7 +3136,7 @@ void helper_book3s_msgclrp(CPUPPCState *env, target_ulong rb) void helper_book3s_msgsndp(CPUPPCState *env, target_ulong rb) { CPUState *cs = env_cpu(env); - PowerPCCPU *cpu = POWERPC_CPU(cs); + PowerPCCPU *cpu = env_archcpu(env); CPUState *ccs; uint32_t nr_threads = cs->nr_threads; int ttir = rb & PPC_BITMASK(57, 63); From edf67fb4c2844a5dad7beb8e1b9634d01f141e72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 9 Oct 2023 12:44:16 +0200 Subject: [PATCH 785/974] target/riscv: Use env_archcpu() in [check_]nanbox() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When CPUArchState* is available (here CPURISCVState*), we can use the fast env_archcpu() macro to get ArchCPU* (here RISCVCPU*). The QOM cast RISCV_CPU() macro will be slower when building with --enable-qom-cast-debug. Inspired-by: Richard W.M. Jones Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel Henrique Barboza Reviewed-by: LIU Zhiwei Reviewed-by: Richard W.M. Jones Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Message-Id: <20231009110239.66778-3-philmd@linaro.org> --- target/riscv/internals.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/target/riscv/internals.h b/target/riscv/internals.h index b5f823c7ec..8239ae83cc 100644 --- a/target/riscv/internals.h +++ b/target/riscv/internals.h @@ -87,7 +87,7 @@ enum { static inline uint64_t nanbox_s(CPURISCVState *env, float32 f) { /* the value is sign-extended instead of NaN-boxing for zfinx */ - if (RISCV_CPU(env_cpu(env))->cfg.ext_zfinx) { + if (env_archcpu(env)->cfg.ext_zfinx) { return (int32_t)f; } else { return f | MAKE_64BIT_MASK(32, 32); @@ -97,7 +97,7 @@ static inline uint64_t nanbox_s(CPURISCVState *env, float32 f) static inline float32 check_nanbox_s(CPURISCVState *env, uint64_t f) { /* Disable NaN-boxing check when enable zfinx */ - if (RISCV_CPU(env_cpu(env))->cfg.ext_zfinx) { + if (env_archcpu(env)->cfg.ext_zfinx) { return (uint32_t)f; } @@ -113,7 +113,7 @@ static inline float32 check_nanbox_s(CPURISCVState *env, uint64_t f) static inline uint64_t nanbox_h(CPURISCVState *env, float16 f) { /* the value is sign-extended instead of NaN-boxing for zfinx */ - if (RISCV_CPU(env_cpu(env))->cfg.ext_zfinx) { + if (env_archcpu(env)->cfg.ext_zfinx) { return (int16_t)f; } else { return f | MAKE_64BIT_MASK(16, 48); @@ -123,7 +123,7 @@ static inline uint64_t nanbox_h(CPURISCVState *env, float16 f) static inline float16 check_nanbox_h(CPURISCVState *env, uint64_t f) { /* Disable nanbox check when enable zfinx */ - if (RISCV_CPU(env_cpu(env))->cfg.ext_zfinx) { + if (env_archcpu(env)->cfg.ext_zfinx) { return (uint16_t)f; } From 2c6822cd59d5c016bb4e9e36143ad60215f9ff09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 9 Oct 2023 12:42:42 +0200 Subject: [PATCH 786/974] target/s390x: Use env_archcpu() in handle_diag_308() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When CPUArchState* is available (here CPUS390XState*), we can use the fast env_archcpu() macro to get ArchCPU* (here S390CPU*). The QOM cast S390_CPU() macro will be slower when building with --enable-qom-cast-debug. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Acked-by: Alistair Francis Message-Id: <20231009110239.66778-4-philmd@linaro.org> --- target/s390x/diag.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/s390x/diag.c b/target/s390x/diag.c index 8ce18e08f3..27ffd48576 100644 --- a/target/s390x/diag.c +++ b/target/s390x/diag.c @@ -77,7 +77,7 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra) { bool valid; CPUState *cs = env_cpu(env); - S390CPU *cpu = S390_CPU(cs); + S390CPU *cpu = env_archcpu(env); uint64_t addr = env->regs[r1]; uint64_t subcode = env->regs[r3]; IplParameterBlock *iplb; From bcb9d2ea77dc83ba2591559fa2052a8ca63e591f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 9 Oct 2023 12:36:48 +0200 Subject: [PATCH 787/974] target/xtensa: Use env_archcpu() in update_c[compare|count]() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When CPUArchState* is available (here CPUXtensaState*), we can use the fast env_archcpu() macro to get ArchCPU* (here XtensaCPU*). The QOM cast XTENSA_CPU() macro will be slower when building with --enable-qom-cast-debug. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Acked-by: Alistair Francis Message-Id: <20231009110239.66778-5-philmd@linaro.org> --- target/xtensa/op_helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/xtensa/op_helper.c b/target/xtensa/op_helper.c index 7bb8cd6726..496754ba57 100644 --- a/target/xtensa/op_helper.c +++ b/target/xtensa/op_helper.c @@ -37,7 +37,7 @@ void HELPER(update_ccount)(CPUXtensaState *env) { - XtensaCPU *cpu = XTENSA_CPU(env_cpu(env)); + XtensaCPU *cpu = env_archcpu(env); uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); env->ccount_time = now; @@ -58,7 +58,7 @@ void HELPER(wsr_ccount)(CPUXtensaState *env, uint32_t v) void HELPER(update_ccompare)(CPUXtensaState *env, uint32_t i) { - XtensaCPU *cpu = XTENSA_CPU(env_cpu(env)); + XtensaCPU *cpu = env_archcpu(env); uint64_t dcc; qatomic_and(&env->sregs[INTSET], From 82b641d6261a58d9fba5a6ce786e7d58a8876e25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 9 Oct 2023 12:34:50 +0200 Subject: [PATCH 788/974] target/i386/hvf: Use x86_cpu in simulate_[rdmsr|wrmsr]() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We already have 'x86_cpu = X86_CPU(cpu)'. Use the variable instead of doing another QOM cast with X86_CPU(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Roman Bolshakov Tested-by: Roman Bolshakov Reviewed-by: Zhao Liu Message-Id: <20231009110239.66778-6-philmd@linaro.org> --- target/i386/hvf/x86_emu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/i386/hvf/x86_emu.c b/target/i386/hvf/x86_emu.c index ccda568478..af1f205ecf 100644 --- a/target/i386/hvf/x86_emu.c +++ b/target/i386/hvf/x86_emu.c @@ -676,7 +676,7 @@ void simulate_rdmsr(struct CPUState *cpu) val = rdtscp() + rvmcs(cpu->accel->fd, VMCS_TSC_OFFSET); break; case MSR_IA32_APICBASE: - val = cpu_get_apic_base(X86_CPU(cpu)->apic_state); + val = cpu_get_apic_base(x86_cpu->apic_state); break; case MSR_IA32_UCODE_REV: val = x86_cpu->ucode_rev; @@ -776,7 +776,7 @@ void simulate_wrmsr(struct CPUState *cpu) case MSR_IA32_TSC: break; case MSR_IA32_APICBASE: - cpu_set_apic_base(X86_CPU(cpu)->apic_state, data); + cpu_set_apic_base(x86_cpu->apic_state, data); break; case MSR_FSBASE: wvmcs(cpu->accel->fd, VMCS_GUEST_FS_BASE, data); From a9e445df545158e3d6e3239f10c2e88d8119fdd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 9 Oct 2023 12:35:04 +0200 Subject: [PATCH 789/974] target/i386/hvf: Use env_archcpu() in simulate_[rdmsr/wrmsr]() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When CPUArchState* is available (here CPUX86State*), we can use the fast env_archcpu() macro to get ArchCPU* (here X86CPU*). The QOM cast X86_CPU() macro will be slower when building with --enable-qom-cast-debug. Pass CPUX86State* as argument to simulate_rdmsr / simulate_wrmsr instead of a CPUState* to avoid an extra cast. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Roman Bolshakov Tested-by: Roman Bolshakov Reviewed-by: Zhao Liu Message-Id: <20231009110239.66778-7-philmd@linaro.org> --- target/i386/hvf/hvf.c | 4 ++-- target/i386/hvf/x86_emu.c | 21 ++++++++++----------- target/i386/hvf/x86_emu.h | 4 ++-- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index cb2cd0b02f..20b9ca3ef5 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -591,9 +591,9 @@ int hvf_vcpu_exec(CPUState *cpu) { load_regs(cpu); if (exit_reason == EXIT_REASON_RDMSR) { - simulate_rdmsr(cpu); + simulate_rdmsr(env); } else { - simulate_wrmsr(cpu); + simulate_wrmsr(env); } env->eip += ins_len; store_regs(cpu); diff --git a/target/i386/hvf/x86_emu.c b/target/i386/hvf/x86_emu.c index af1f205ecf..b1f8a685d1 100644 --- a/target/i386/hvf/x86_emu.c +++ b/target/i386/hvf/x86_emu.c @@ -663,11 +663,10 @@ static void exec_lods(CPUX86State *env, struct x86_decode *decode) env->eip += decode->len; } -void simulate_rdmsr(struct CPUState *cpu) +void simulate_rdmsr(CPUX86State *env) { - X86CPU *x86_cpu = X86_CPU(cpu); - CPUX86State *env = &x86_cpu->env; - CPUState *cs = env_cpu(env); + X86CPU *x86_cpu = env_archcpu(env); + CPUState *cpu = env_cpu(env); uint32_t msr = ECX(env); uint64_t val = 0; @@ -746,8 +745,8 @@ void simulate_rdmsr(struct CPUState *cpu) 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->nr_threads * cpu->nr_cores; /* thread count, bits 15..0 */ + val |= ((uint32_t)cpu->nr_cores << 16); /* core count, bits 31..16 */ break; default: /* fprintf(stderr, "%s: unknown msr 0x%x\n", __func__, msr); */ @@ -761,14 +760,14 @@ void simulate_rdmsr(struct CPUState *cpu) static void exec_rdmsr(CPUX86State *env, struct x86_decode *decode) { - simulate_rdmsr(env_cpu(env)); + simulate_rdmsr(env); env->eip += decode->len; } -void simulate_wrmsr(struct CPUState *cpu) +void simulate_wrmsr(CPUX86State *env) { - X86CPU *x86_cpu = X86_CPU(cpu); - CPUX86State *env = &x86_cpu->env; + X86CPU *x86_cpu = env_archcpu(env); + CPUState *cpu = env_cpu(env); uint32_t msr = ECX(env); uint64_t data = ((uint64_t)EDX(env) << 32) | EAX(env); @@ -856,7 +855,7 @@ void simulate_wrmsr(struct CPUState *cpu) static void exec_wrmsr(CPUX86State *env, struct x86_decode *decode) { - simulate_wrmsr(env_cpu(env)); + simulate_wrmsr(env); env->eip += decode->len; } diff --git a/target/i386/hvf/x86_emu.h b/target/i386/hvf/x86_emu.h index 640da90b30..4b846ba80e 100644 --- a/target/i386/hvf/x86_emu.h +++ b/target/i386/hvf/x86_emu.h @@ -29,8 +29,8 @@ bool exec_instruction(CPUX86State *env, struct x86_decode *ins); void load_regs(struct CPUState *cpu); void store_regs(struct CPUState *cpu); -void simulate_rdmsr(struct CPUState *cpu); -void simulate_wrmsr(struct CPUState *cpu); +void simulate_rdmsr(CPUX86State *env); +void simulate_wrmsr(CPUX86State *env); target_ulong read_reg(CPUX86State *env, int reg, int size); void write_reg(CPUX86State *env, int reg, target_ulong val, int size); From 89c02195c96641fa2b7a5e767e03eec945c45943 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 20 Oct 2023 13:05:17 +0200 Subject: [PATCH 790/974] target/i386/hvf: Use CPUState typedef MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow C style guidelines and use CPUState forward declaration from "qemu/typedefs.h". No functional changes. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Zhao Liu Message-Id: <20231020111136.44401-2-philmd@linaro.org> --- target/i386/hvf/x86_emu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/i386/hvf/x86_emu.c b/target/i386/hvf/x86_emu.c index b1f8a685d1..cd7ef30126 100644 --- a/target/i386/hvf/x86_emu.c +++ b/target/i386/hvf/x86_emu.c @@ -45,7 +45,7 @@ #include "vmcs.h" #include "vmx.h" -void hvf_handle_io(struct CPUState *cpu, uint16_t port, void *data, +void hvf_handle_io(CPUState *cpu, uint16_t port, void *data, int direction, int size, uint32_t count); #define EXEC_2OP_FLAGS_CMD(env, decode, cmd, FLAGS_FUNC, save_res) \ @@ -1417,7 +1417,7 @@ static void init_cmd_handler() } } -void load_regs(struct CPUState *cpu) +void load_regs(CPUState *cpu) { X86CPU *x86_cpu = X86_CPU(cpu); CPUX86State *env = &x86_cpu->env; @@ -1440,7 +1440,7 @@ void load_regs(struct CPUState *cpu) env->eip = rreg(cpu->accel->fd, HV_X86_RIP); } -void store_regs(struct CPUState *cpu) +void store_regs(CPUState *cpu) { X86CPU *x86_cpu = X86_CPU(cpu); CPUX86State *env = &x86_cpu->env; From 5366a0644f7fc68334a7ac6e0f131c90cdb5bcc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 20 Oct 2023 13:02:16 +0200 Subject: [PATCH 791/974] target/i386/hvf: Rename 'CPUState *cpu' variable as 'cs' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow the naming used by other files in target/i386/. No functional changes. Suggested-by: Zhao Liu Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Zhao Liu Message-Id: <20231020111136.44401-3-philmd@linaro.org> --- target/i386/hvf/x86_emu.c | 92 +++++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/target/i386/hvf/x86_emu.c b/target/i386/hvf/x86_emu.c index cd7ef30126..5b82e84778 100644 --- a/target/i386/hvf/x86_emu.c +++ b/target/i386/hvf/x86_emu.c @@ -45,7 +45,7 @@ #include "vmcs.h" #include "vmx.h" -void hvf_handle_io(CPUState *cpu, uint16_t port, void *data, +void hvf_handle_io(CPUState *cs, uint16_t port, void *data, int direction, int size, uint32_t count); #define EXEC_2OP_FLAGS_CMD(env, decode, cmd, FLAGS_FUNC, save_res) \ @@ -666,13 +666,13 @@ static void exec_lods(CPUX86State *env, struct x86_decode *decode) void simulate_rdmsr(CPUX86State *env) { X86CPU *x86_cpu = env_archcpu(env); - CPUState *cpu = env_cpu(env); + CPUState *cs = env_cpu(env); uint32_t msr = ECX(env); uint64_t val = 0; switch (msr) { case MSR_IA32_TSC: - val = rdtscp() + rvmcs(cpu->accel->fd, VMCS_TSC_OFFSET); + val = rdtscp() + rvmcs(cs->accel->fd, VMCS_TSC_OFFSET); break; case MSR_IA32_APICBASE: val = cpu_get_apic_base(x86_cpu->apic_state); @@ -681,16 +681,16 @@ void simulate_rdmsr(CPUX86State *env) val = x86_cpu->ucode_rev; break; case MSR_EFER: - val = rvmcs(cpu->accel->fd, VMCS_GUEST_IA32_EFER); + val = rvmcs(cs->accel->fd, VMCS_GUEST_IA32_EFER); break; case MSR_FSBASE: - val = rvmcs(cpu->accel->fd, VMCS_GUEST_FS_BASE); + val = rvmcs(cs->accel->fd, VMCS_GUEST_FS_BASE); break; case MSR_GSBASE: - val = rvmcs(cpu->accel->fd, VMCS_GUEST_GS_BASE); + val = rvmcs(cs->accel->fd, VMCS_GUEST_GS_BASE); break; case MSR_KERNELGSBASE: - val = rvmcs(cpu->accel->fd, VMCS_HOST_FS_BASE); + val = rvmcs(cs->accel->fd, VMCS_HOST_FS_BASE); break; case MSR_STAR: abort(); @@ -745,8 +745,8 @@ void simulate_rdmsr(CPUX86State *env) val = env->mtrr_deftype; break; case MSR_CORE_THREAD_COUNT: - val = cpu->nr_threads * cpu->nr_cores; /* thread count, bits 15..0 */ - val |= ((uint32_t)cpu->nr_cores << 16); /* core count, bits 31..16 */ + val = cs->nr_threads * cs->nr_cores; /* thread count, bits 15..0 */ + val |= ((uint32_t)cs->nr_cores << 16); /* core count, bits 31..16 */ break; default: /* fprintf(stderr, "%s: unknown msr 0x%x\n", __func__, msr); */ @@ -767,7 +767,7 @@ static void exec_rdmsr(CPUX86State *env, struct x86_decode *decode) void simulate_wrmsr(CPUX86State *env) { X86CPU *x86_cpu = env_archcpu(env); - CPUState *cpu = env_cpu(env); + CPUState *cs = env_cpu(env); uint32_t msr = ECX(env); uint64_t data = ((uint64_t)EDX(env) << 32) | EAX(env); @@ -778,13 +778,13 @@ void simulate_wrmsr(CPUX86State *env) cpu_set_apic_base(x86_cpu->apic_state, data); break; case MSR_FSBASE: - wvmcs(cpu->accel->fd, VMCS_GUEST_FS_BASE, data); + wvmcs(cs->accel->fd, VMCS_GUEST_FS_BASE, data); break; case MSR_GSBASE: - wvmcs(cpu->accel->fd, VMCS_GUEST_GS_BASE, data); + wvmcs(cs->accel->fd, VMCS_GUEST_GS_BASE, data); break; case MSR_KERNELGSBASE: - wvmcs(cpu->accel->fd, VMCS_HOST_FS_BASE, data); + wvmcs(cs->accel->fd, VMCS_HOST_FS_BASE, data); break; case MSR_STAR: abort(); @@ -796,10 +796,10 @@ void simulate_wrmsr(CPUX86State *env) abort(); break; case MSR_EFER: - /*printf("new efer %llx\n", EFER(cpu));*/ - wvmcs(cpu->accel->fd, VMCS_GUEST_IA32_EFER, data); + /*printf("new efer %llx\n", EFER(cs));*/ + wvmcs(cs->accel->fd, VMCS_GUEST_IA32_EFER, data); if (data & MSR_EFER_NXE) { - hv_vcpu_invalidate_tlb(cpu->accel->fd); + hv_vcpu_invalidate_tlb(cs->accel->fd); } break; case MSR_MTRRphysBase(0): @@ -848,9 +848,9 @@ void simulate_wrmsr(CPUX86State *env) /* Related to support known hypervisor interface */ /* if (g_hypervisor_iface) - g_hypervisor_iface->wrmsr_handler(cpu, msr, data); + g_hypervisor_iface->wrmsr_handler(cs, msr, data); - printf("write msr %llx\n", RCX(cpu));*/ + printf("write msr %llx\n", RCX(cs));*/ } static void exec_wrmsr(CPUX86State *env, struct x86_decode *decode) @@ -1417,56 +1417,56 @@ static void init_cmd_handler() } } -void load_regs(CPUState *cpu) +void load_regs(CPUState *cs) { - X86CPU *x86_cpu = X86_CPU(cpu); + X86CPU *x86_cpu = X86_CPU(cs); CPUX86State *env = &x86_cpu->env; int i = 0; - RRX(env, R_EAX) = rreg(cpu->accel->fd, HV_X86_RAX); - RRX(env, R_EBX) = rreg(cpu->accel->fd, HV_X86_RBX); - RRX(env, R_ECX) = rreg(cpu->accel->fd, HV_X86_RCX); - RRX(env, R_EDX) = rreg(cpu->accel->fd, HV_X86_RDX); - RRX(env, R_ESI) = rreg(cpu->accel->fd, HV_X86_RSI); - RRX(env, R_EDI) = rreg(cpu->accel->fd, HV_X86_RDI); - RRX(env, R_ESP) = rreg(cpu->accel->fd, HV_X86_RSP); - RRX(env, R_EBP) = rreg(cpu->accel->fd, HV_X86_RBP); + RRX(env, R_EAX) = rreg(cs->accel->fd, HV_X86_RAX); + RRX(env, R_EBX) = rreg(cs->accel->fd, HV_X86_RBX); + RRX(env, R_ECX) = rreg(cs->accel->fd, HV_X86_RCX); + RRX(env, R_EDX) = rreg(cs->accel->fd, HV_X86_RDX); + RRX(env, R_ESI) = rreg(cs->accel->fd, HV_X86_RSI); + RRX(env, R_EDI) = rreg(cs->accel->fd, HV_X86_RDI); + RRX(env, R_ESP) = rreg(cs->accel->fd, HV_X86_RSP); + RRX(env, R_EBP) = rreg(cs->accel->fd, HV_X86_RBP); for (i = 8; i < 16; i++) { - RRX(env, i) = rreg(cpu->accel->fd, HV_X86_RAX + i); + RRX(env, i) = rreg(cs->accel->fd, HV_X86_RAX + i); } - env->eflags = rreg(cpu->accel->fd, HV_X86_RFLAGS); + env->eflags = rreg(cs->accel->fd, HV_X86_RFLAGS); rflags_to_lflags(env); - env->eip = rreg(cpu->accel->fd, HV_X86_RIP); + env->eip = rreg(cs->accel->fd, HV_X86_RIP); } -void store_regs(CPUState *cpu) +void store_regs(CPUState *cs) { - X86CPU *x86_cpu = X86_CPU(cpu); + X86CPU *x86_cpu = X86_CPU(cs); CPUX86State *env = &x86_cpu->env; int i = 0; - wreg(cpu->accel->fd, HV_X86_RAX, RAX(env)); - wreg(cpu->accel->fd, HV_X86_RBX, RBX(env)); - wreg(cpu->accel->fd, HV_X86_RCX, RCX(env)); - wreg(cpu->accel->fd, HV_X86_RDX, RDX(env)); - wreg(cpu->accel->fd, HV_X86_RSI, RSI(env)); - wreg(cpu->accel->fd, HV_X86_RDI, RDI(env)); - wreg(cpu->accel->fd, HV_X86_RBP, RBP(env)); - wreg(cpu->accel->fd, HV_X86_RSP, RSP(env)); + wreg(cs->accel->fd, HV_X86_RAX, RAX(env)); + wreg(cs->accel->fd, HV_X86_RBX, RBX(env)); + wreg(cs->accel->fd, HV_X86_RCX, RCX(env)); + wreg(cs->accel->fd, HV_X86_RDX, RDX(env)); + wreg(cs->accel->fd, HV_X86_RSI, RSI(env)); + wreg(cs->accel->fd, HV_X86_RDI, RDI(env)); + wreg(cs->accel->fd, HV_X86_RBP, RBP(env)); + wreg(cs->accel->fd, HV_X86_RSP, RSP(env)); for (i = 8; i < 16; i++) { - wreg(cpu->accel->fd, HV_X86_RAX + i, RRX(env, i)); + wreg(cs->accel->fd, HV_X86_RAX + i, RRX(env, i)); } lflags_to_rflags(env); - wreg(cpu->accel->fd, HV_X86_RFLAGS, env->eflags); - macvm_set_rip(cpu, env->eip); + wreg(cs->accel->fd, HV_X86_RFLAGS, env->eflags); + macvm_set_rip(cs, env->eip); } bool exec_instruction(CPUX86State *env, struct x86_decode *ins) { - /*if (hvf_vcpu_id(cpu)) - printf("%d, %llx: exec_instruction %s\n", hvf_vcpu_id(cpu), env->eip, + /*if (hvf_vcpu_id(cs)) + printf("%d, %llx: exec_instruction %s\n", hvf_vcpu_id(cs), env->eip, decode_cmd_to_string(ins->cmd));*/ if (!_cmd_handler[ins->cmd].handler) { From 3152e954135a40b07fefd11738616c66a2d30e1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 20 Oct 2023 13:03:07 +0200 Subject: [PATCH 792/974] target/i386/hvf: Rename 'X86CPU *x86_cpu' variable as 'cpu' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow the naming used by other files in target/i386/. No functional changes. Suggested-by: Zhao Liu Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Zhao Liu Message-Id: <20231020111136.44401-4-philmd@linaro.org> --- target/i386/hvf/x86_emu.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/target/i386/hvf/x86_emu.c b/target/i386/hvf/x86_emu.c index 5b82e84778..3a3f0a50d0 100644 --- a/target/i386/hvf/x86_emu.c +++ b/target/i386/hvf/x86_emu.c @@ -665,7 +665,7 @@ static void exec_lods(CPUX86State *env, struct x86_decode *decode) void simulate_rdmsr(CPUX86State *env) { - X86CPU *x86_cpu = env_archcpu(env); + X86CPU *cpu = env_archcpu(env); CPUState *cs = env_cpu(env); uint32_t msr = ECX(env); uint64_t val = 0; @@ -675,10 +675,10 @@ void simulate_rdmsr(CPUX86State *env) val = rdtscp() + rvmcs(cs->accel->fd, VMCS_TSC_OFFSET); break; case MSR_IA32_APICBASE: - val = cpu_get_apic_base(x86_cpu->apic_state); + val = cpu_get_apic_base(cpu->apic_state); break; case MSR_IA32_UCODE_REV: - val = x86_cpu->ucode_rev; + val = cpu->ucode_rev; break; case MSR_EFER: val = rvmcs(cs->accel->fd, VMCS_GUEST_IA32_EFER); @@ -766,7 +766,7 @@ static void exec_rdmsr(CPUX86State *env, struct x86_decode *decode) void simulate_wrmsr(CPUX86State *env) { - X86CPU *x86_cpu = env_archcpu(env); + X86CPU *cpu = env_archcpu(env); CPUState *cs = env_cpu(env); uint32_t msr = ECX(env); uint64_t data = ((uint64_t)EDX(env) << 32) | EAX(env); @@ -775,7 +775,7 @@ void simulate_wrmsr(CPUX86State *env) case MSR_IA32_TSC: break; case MSR_IA32_APICBASE: - cpu_set_apic_base(x86_cpu->apic_state, data); + cpu_set_apic_base(cpu->apic_state, data); break; case MSR_FSBASE: wvmcs(cs->accel->fd, VMCS_GUEST_FS_BASE, data); @@ -1419,8 +1419,8 @@ static void init_cmd_handler() void load_regs(CPUState *cs) { - X86CPU *x86_cpu = X86_CPU(cs); - CPUX86State *env = &x86_cpu->env; + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; int i = 0; RRX(env, R_EAX) = rreg(cs->accel->fd, HV_X86_RAX); @@ -1442,8 +1442,8 @@ void load_regs(CPUState *cs) void store_regs(CPUState *cs) { - X86CPU *x86_cpu = X86_CPU(cs); - CPUX86State *env = &x86_cpu->env; + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; int i = 0; wreg(cs->accel->fd, HV_X86_RAX, RAX(env)); From de910c496a62db24388b9ef7da49f06b12745274 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 15 Sep 2023 17:08:01 +0200 Subject: [PATCH 793/974] target/i386/kvm: Correct comment in kvm_cpu_realize() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230918160257.30127-4-philmd@linaro.org> --- target/i386/kvm/kvm-cpu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/i386/kvm/kvm-cpu.c b/target/i386/kvm/kvm-cpu.c index 56c72f3c45..9c791b7b05 100644 --- a/target/i386/kvm/kvm-cpu.c +++ b/target/i386/kvm/kvm-cpu.c @@ -37,6 +37,7 @@ static bool kvm_cpu_realizefn(CPUState *cs, Error **errp) * -> cpu_exec_realizefn(): * -> accel_cpu_common_realize() * kvm_cpu_realizefn() -> host_cpu_realizefn() + * -> cpu_common_realizefn() * -> check/update ucode_rev, phys_bits, mwait */ if (cpu->max_features) { From 2b34d7382b669b154f44d616f7e5b78f459ffd61 Mon Sep 17 00:00:00 2001 From: Dongli Zhang Date: Thu, 26 Oct 2023 14:19:38 -0700 Subject: [PATCH 794/974] target/i386/monitor: synchronize cpu state for lapic info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While the default "info lapic" always synchronizes cpu state ... mon_get_cpu() -> mon_get_cpu_sync(mon, true) -> cpu_synchronize_state(cpu) -> ioctl KVM_GET_LAPIC (taking KVM as example) ... the cpu state is not synchronized when the apic-id is available as argument. The cpu state should be synchronized when apic-id is available. Otherwise the "info lapic " always returns stale data. Reference: https://lore.kernel.org/all/20211028155457.967291-19-berrange@redhat.com/ Cc: Joe Jin Signed-off-by: Dongli Zhang Reviewed-by: Daniel P. Berrangé Reviewed-by: David Woodhouse Message-ID: <20231030085336.2681386-1-armbru@redhat.com> Reviewed-by: Juan Quintela Message-ID: <20231026211938.162815-1-dongli.zhang@oracle.com> Signed-off-by: Philippe Mathieu-Daudé --- target/i386/monitor.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/target/i386/monitor.c b/target/i386/monitor.c index 6512846327..950ff9ccbc 100644 --- a/target/i386/monitor.c +++ b/target/i386/monitor.c @@ -28,6 +28,7 @@ #include "monitor/hmp-target.h" #include "monitor/hmp.h" #include "qapi/qmp/qdict.h" +#include "sysemu/hw_accel.h" #include "sysemu/kvm.h" #include "qapi/error.h" #include "qapi/qapi-commands-misc-target.h" @@ -654,7 +655,11 @@ void hmp_info_local_apic(Monitor *mon, const QDict *qdict) if (qdict_haskey(qdict, "apic-id")) { int id = qdict_get_try_int(qdict, "apic-id", 0); + cs = cpu_by_arch_id(id); + if (cs) { + cpu_synchronize_state(cs); + } } else { cs = mon_get_cpu(mon); } From 04591b3ddd9a96b9298a1dd437a6464ab55e62ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 14 Sep 2023 10:39:37 +0200 Subject: [PATCH 795/974] target/mips: Fix MSA BZ/BNZ opcodes displacement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PC offset is *signed*. Cc: qemu-stable@nongnu.org Reported-by: Sergey Evlashev Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1624 Fixes: c7a9ef7517 ("target/mips: Introduce decode tree bindings for MSA ASE") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230914085807.12241-1-philmd@linaro.org> --- target/mips/tcg/msa.decode | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/mips/tcg/msa.decode b/target/mips/tcg/msa.decode index 9575289195..4410e2a02e 100644 --- a/target/mips/tcg/msa.decode +++ b/target/mips/tcg/msa.decode @@ -31,8 +31,8 @@ @lsa ...... rs:5 rt:5 rd:5 ... sa:2 ...... &r @ldst ...... sa:s10 ws:5 wd:5 .... df:2 &msa_i -@bz_v ...... ... .. wt:5 sa:16 &msa_bz df=3 -@bz ...... ... df:2 wt:5 sa:16 &msa_bz +@bz_v ...... ... .. wt:5 sa:s16 &msa_bz df=3 +@bz ...... ... df:2 wt:5 sa:s16 &msa_bz @elm_df ...... .... ...... ws:5 wd:5 ...... &msa_elm_df df=%elm_df n=%elm_n @elm ...... .......... ws:5 wd:5 ...... &msa_elm @vec ...... ..... wt:5 ws:5 wd:5 ...... &msa_r df=0 From 18f86aecd6a1bea0f78af14587a684ad966d8d3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 14 Sep 2023 11:02:41 +0200 Subject: [PATCH 796/974] target/mips: Fix TX79 LQ/SQ opcodes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The base register address offset is *signed*. Cc: qemu-stable@nongnu.org Fixes: aaaa82a9f9 ("target/mips/tx79: Introduce LQ opcode (Load Quadword)") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230914090447.12557-1-philmd@linaro.org> --- target/mips/tcg/tx79.decode | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/mips/tcg/tx79.decode b/target/mips/tcg/tx79.decode index 57d87a2076..578b8c54c0 100644 --- a/target/mips/tcg/tx79.decode +++ b/target/mips/tcg/tx79.decode @@ -24,7 +24,7 @@ @rs ...... rs:5 ..... .......... ...... &r sa=0 rt=0 rd=0 @rd ...... .......... rd:5 ..... ...... &r sa=0 rs=0 rt=0 -@ldst ...... base:5 rt:5 offset:16 &i +@ldst ...... base:5 rt:5 offset:s16 &i ########################################################################### From aa6edf97ce9783bf831ffaca5eda243210290292 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 8 Sep 2023 18:42:04 +0200 Subject: [PATCH 797/974] sysemu/kvm: Restrict kvmppc_get_radix_page_info() to ppc targets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit kvm_get_radix_page_info() is only defined for ppc targets (in target/ppc/kvm.c). The declaration is not useful in other targets, reduce its scope. Rename using the 'kvmppc_' prefix following other declarations from target/ppc/kvm_ppc.h. Suggested-by: Michael Tokarev Reviewed-by: Daniel Henrique Barboza Message-Id: <20231003070427.69621-2-philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- include/sysemu/kvm.h | 1 - target/ppc/kvm.c | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 80b69d88f6..d614878164 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -521,7 +521,6 @@ int kvm_set_one_reg(CPUState *cs, uint64_t id, void *source); * Returns: 0 on success, or a negative errno on failure. */ int kvm_get_one_reg(CPUState *cs, uint64_t id, void *target); -struct ppc_radix_page_info *kvm_get_radix_page_info(void); /* Notify resamplefd for EOI of specific interrupts. */ void kvm_resample_fd_notify(int gsi); diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index d0e2dcdc77..9b1abe2fc4 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -268,7 +268,7 @@ static void kvm_get_smmu_info(struct kvm_ppc_smmu_info *info, Error **errp) "KVM failed to provide the MMU features it supports"); } -struct ppc_radix_page_info *kvm_get_radix_page_info(void) +static struct ppc_radix_page_info *kvmppc_get_radix_page_info(void) { KVMState *s = KVM_STATE(current_accel()); struct ppc_radix_page_info *radix_page_info; @@ -2368,7 +2368,7 @@ static void kvmppc_host_cpu_class_init(ObjectClass *oc, void *data) } #if defined(TARGET_PPC64) - pcc->radix_page_info = kvm_get_radix_page_info(); + pcc->radix_page_info = kvmppc_get_radix_page_info(); if ((pcc->pvr & 0xffffff00) == CPU_POWERPC_POWER9_DD1) { /* From 86d9ff288a9f978840982c6d663b78d64021a6da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 3 Oct 2023 09:01:01 +0200 Subject: [PATCH 798/974] hw/ppc/e500: Restrict ppce500_init_mpic_kvm() to KVM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Inline and guard the single call to kvm_openpic_connect_vcpu() allows to remove kvm-stub.c. Reviewed-by: Michael Tokarev Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20231003070427.69621-3-philmd@linaro.org> --- hw/ppc/e500.c | 4 ++++ target/ppc/kvm-stub.c | 19 ------------------- target/ppc/meson.build | 2 +- 3 files changed, 5 insertions(+), 20 deletions(-) delete mode 100644 target/ppc/kvm-stub.c diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index e04114fb3c..384226296b 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -834,6 +834,7 @@ static DeviceState *ppce500_init_mpic_qemu(PPCE500MachineState *pms, static DeviceState *ppce500_init_mpic_kvm(const PPCE500MachineClass *pmc, IrqLines *irqs, Error **errp) { +#ifdef CONFIG_KVM DeviceState *dev; CPUState *cs; @@ -854,6 +855,9 @@ static DeviceState *ppce500_init_mpic_kvm(const PPCE500MachineClass *pmc, } return dev; +#else + g_assert_not_reached(); +#endif } static DeviceState *ppce500_init_mpic(PPCE500MachineState *pms, diff --git a/target/ppc/kvm-stub.c b/target/ppc/kvm-stub.c deleted file mode 100644 index b98e1d404f..0000000000 --- a/target/ppc/kvm-stub.c +++ /dev/null @@ -1,19 +0,0 @@ -/* - * QEMU KVM PPC specific function stubs - * - * Copyright Freescale Inc. 2013 - * - * Author: Alexander Graf - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ -#include "qemu/osdep.h" -#include "cpu.h" -#include "hw/ppc/openpic_kvm.h" - -int kvm_openpic_connect_vcpu(DeviceState *d, CPUState *cs) -{ - return -EINVAL; -} diff --git a/target/ppc/meson.build b/target/ppc/meson.build index 97ceb6e7c0..eab4e3e1b3 100644 --- a/target/ppc/meson.build +++ b/target/ppc/meson.build @@ -30,7 +30,7 @@ gen = [ ] ppc_ss.add(when: 'CONFIG_TCG', if_true: gen) -ppc_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c'), if_false: files('kvm-stub.c')) +ppc_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c')) ppc_ss.add(when: 'CONFIG_USER_ONLY', if_true: files('user_only_helper.c')) ppc_system_ss = ss.source_set() From a523b6761c32d62304037366717e0c2ede8bbf3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 12 Sep 2023 09:21:27 +0300 Subject: [PATCH 799/974] target/ppc: Restrict KVM objects to system emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CONFIG_KVM is always FALSE on user emulation, so 'kvm.c' won't be added to ppc_ss[] source set; direcly use the system specific ppc_system_ss[] source set. Reviewed-by: Michael Tokarev Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20231003070427.69621-4-philmd@linaro.org> --- target/ppc/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/ppc/meson.build b/target/ppc/meson.build index eab4e3e1b3..0b89f9b89f 100644 --- a/target/ppc/meson.build +++ b/target/ppc/meson.build @@ -30,7 +30,6 @@ gen = [ ] ppc_ss.add(when: 'CONFIG_TCG', if_true: gen) -ppc_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c')) ppc_ss.add(when: 'CONFIG_USER_ONLY', if_true: files('user_only_helper.c')) ppc_system_ss = ss.source_set() @@ -46,6 +45,7 @@ ppc_system_ss.add(when: 'CONFIG_TCG', if_true: files( ), if_false: files( 'tcg-stub.c', )) +ppc_system_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c')) ppc_system_ss.add(when: 'TARGET_PPC64', if_true: files( 'compat.c', From c6b8252c6d55a4db19a67c295f88b66158c6d94e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 12 Sep 2023 00:16:40 +0300 Subject: [PATCH 800/974] target/ppc: Prohibit target specific KVM prototypes on user emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit None of these target-specific prototypes should be used by user emulation. Remove their declaration there, so we get a compile failure if ever used (instead of having to deal with linker and its possible optimizations, such dead code removal). Suggested-by: Kevin Wolf Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel Henrique Barboza Message-Id: <20231003070427.69621-5-philmd@linaro.org> --- target/ppc/kvm_ppc.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target/ppc/kvm_ppc.h b/target/ppc/kvm_ppc.h index 6a4dd9c560..1975fb5ee6 100644 --- a/target/ppc/kvm_ppc.h +++ b/target/ppc/kvm_ppc.h @@ -13,6 +13,10 @@ #include "exec/hwaddr.h" #include "cpu.h" +#ifdef CONFIG_USER_ONLY +#error Cannot include kvm_ppc.h from user emulation +#endif + #ifdef CONFIG_KVM uint32_t kvmppc_get_tbfreq(void); From 1978a41bcf4112ca95dac96ecf5b79613ae9a4ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 13 Oct 2023 12:40:41 +0200 Subject: [PATCH 801/974] target/ppc: Define powerpc_pm_insn_t in 'internal.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PM instructions are only used by TCG helpers. No need to expose to other hardware. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Cédric Le Goater Message-Id: <20231013125630.95116-3-philmd@linaro.org> --- target/ppc/cpu-qom.h | 10 ---------- target/ppc/internal.h | 9 +++++++++ 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h index 41df51269b..f681bfb4a6 100644 --- a/target/ppc/cpu-qom.h +++ b/target/ppc/cpu-qom.h @@ -114,16 +114,6 @@ enum powerpc_excp_t { POWERPC_EXCP_POWER10, }; -/*****************************************************************************/ -/* PM instructions */ -typedef enum { - PPC_PM_DOZE, - PPC_PM_NAP, - PPC_PM_SLEEP, - PPC_PM_RVWINKLE, - PPC_PM_STOP, -} powerpc_pm_insn_t; - /*****************************************************************************/ /* Input pins model */ typedef enum powerpc_input_t powerpc_input_t; diff --git a/target/ppc/internal.h b/target/ppc/internal.h index c881c67a8b..5b20ecbd33 100644 --- a/target/ppc/internal.h +++ b/target/ppc/internal.h @@ -20,6 +20,15 @@ #include "hw/registerfields.h" +/* PM instructions */ +typedef enum { + PPC_PM_DOZE, + PPC_PM_NAP, + PPC_PM_SLEEP, + PPC_PM_RVWINKLE, + PPC_PM_STOP, +} powerpc_pm_insn_t; + #define FUNC_MASK(name, ret_type, size, max_val) \ static inline ret_type name(uint##size##_t start, \ uint##size##_t end) \ From 866c8cf91879ac9a079fb3d676af3b4f47fc57cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 13 Oct 2023 12:08:51 +0200 Subject: [PATCH 802/974] target/ppc: Move ppc_cpu_class_by_name() declaration to 'cpu.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ppc_cpu_class_by_name() is only called in target/ppc/, no need to expose outside (in particular to hw/). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Cédric Le Goater Message-Id: <20231013125630.95116-4-philmd@linaro.org> --- target/ppc/cpu-qom.h | 2 -- target/ppc/cpu.h | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h index f681bfb4a6..0b8dfa5fee 100644 --- a/target/ppc/cpu-qom.h +++ b/target/ppc/cpu-qom.h @@ -36,8 +36,6 @@ OBJECT_DECLARE_CPU_TYPE(PowerPCCPU, PowerPCCPUClass, POWERPC_CPU) #define TYPE_HOST_POWERPC_CPU POWERPC_CPU_TYPE_NAME("host") -ObjectClass *ppc_cpu_class_by_name(const char *name); - typedef struct CPUArchState CPUPPCState; typedef struct ppc_tb_t ppc_tb_t; typedef struct ppc_dcr_t ppc_dcr_t; diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 02619e5d54..f3ddfd7a26 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1342,6 +1342,7 @@ struct ArchCPU { }; +ObjectClass *ppc_cpu_class_by_name(const char *name); PowerPCCPUClass *ppc_cpu_class_by_pvr(uint32_t pvr); PowerPCCPUClass *ppc_cpu_class_by_pvr_mask(uint32_t pvr); PowerPCCPUClass *ppc_cpu_get_family_class(PowerPCCPUClass *pcc); From f3cb33255c6baa0362012349b58bf49bc8f9f704 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 13 Oct 2023 12:11:18 +0200 Subject: [PATCH 803/974] target/ppc: Move PowerPCCPUClass definition to 'cpu.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The OBJECT_DECLARE_CPU_TYPE() macro forward-declares the PowerPCCPUClass type. This forward declaration is sufficient for code in hw/ to use the QOM definitions. No need to expose the structure definition. Keep it local to target/ppc/ by moving it to target/ppc/cpu.h. Suggested-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20231013125630.95116-5-philmd@linaro.org> --- include/hw/ppc/ppc.h | 2 +- target/ppc/cpu-qom.h | 56 -------------------------------------------- target/ppc/cpu.h | 51 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 57 deletions(-) diff --git a/include/hw/ppc/ppc.h b/include/hw/ppc/ppc.h index 17a8dfc107..d5d119ea7f 100644 --- a/include/hw/ppc/ppc.h +++ b/include/hw/ppc/ppc.h @@ -1,7 +1,7 @@ #ifndef HW_PPC_H #define HW_PPC_H -#include "target/ppc/cpu-qom.h" +#include "target/ppc/cpu.h" void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level); PowerPCCPU *ppc_get_vcpu_by_pir(int pir); diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h index 0b8dfa5fee..65a640470f 100644 --- a/target/ppc/cpu-qom.h +++ b/target/ppc/cpu-qom.h @@ -21,7 +21,6 @@ #define QEMU_PPC_CPU_QOM_H #include "hw/core/cpu.h" -#include "qom/object.h" #ifdef TARGET_PPC64 #define TYPE_POWERPC_CPU "powerpc64-cpu" @@ -36,10 +35,6 @@ OBJECT_DECLARE_CPU_TYPE(PowerPCCPU, PowerPCCPUClass, POWERPC_CPU) #define TYPE_HOST_POWERPC_CPU POWERPC_CPU_TYPE_NAME("host") -typedef struct CPUArchState CPUPPCState; -typedef struct ppc_tb_t ppc_tb_t; -typedef struct ppc_dcr_t ppc_dcr_t; - /*****************************************************************************/ /* MMU model */ typedef enum powerpc_mmu_t powerpc_mmu_t; @@ -133,57 +128,6 @@ enum powerpc_input_t { PPC_FLAGS_INPUT_RCPU, }; -typedef struct PPCHash64Options PPCHash64Options; - -/** - * PowerPCCPUClass: - * @parent_realize: The parent class' realize handler. - * @parent_phases: The parent class' reset phase handlers. - * - * A PowerPC CPU model. - */ -struct PowerPCCPUClass { - /*< private >*/ - CPUClass parent_class; - /*< public >*/ - - DeviceRealize parent_realize; - DeviceUnrealize parent_unrealize; - ResettablePhases parent_phases; - void (*parent_parse_features)(const char *type, char *str, Error **errp); - - uint32_t pvr; - /* - * If @best is false, match if pcc is in the family of pvr - * Else match only if pcc is the best match for pvr in this family. - */ - bool (*pvr_match)(struct PowerPCCPUClass *pcc, uint32_t pvr, bool best); - uint64_t pcr_mask; /* Available bits in PCR register */ - uint64_t pcr_supported; /* Bits for supported PowerISA versions */ - uint32_t svr; - uint64_t insns_flags; - uint64_t insns_flags2; - uint64_t msr_mask; - uint64_t lpcr_mask; /* Available bits in the LPCR */ - uint64_t lpcr_pm; /* Power-saving mode Exit Cause Enable bits */ - powerpc_mmu_t mmu_model; - powerpc_excp_t excp_model; - powerpc_input_t bus_model; - uint32_t flags; - int bfd_mach; - uint32_t l1_dcache_size, l1_icache_size; -#ifndef CONFIG_USER_ONLY - unsigned int gdb_num_sprs; - const char *gdb_spr_xml; -#endif - const PPCHash64Options *hash64_opts; - struct ppc_radix_page_info *radix_page_info; - uint32_t lrg_decr_bits; - int n_host_threads; - void (*init_proc)(CPUPPCState *env); - int (*check_pow)(CPUPPCState *env); -}; - #ifndef CONFIG_USER_ONLY typedef struct PPCTimebase { uint64_t guest_timebase; diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index f3ddfd7a26..55330d9319 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -200,9 +200,14 @@ typedef struct opc_handler_t opc_handler_t; /*****************************************************************************/ /* Types used to describe some PowerPC registers etc. */ typedef struct DisasContext DisasContext; +typedef struct ppc_dcr_t ppc_dcr_t; typedef struct ppc_spr_t ppc_spr_t; +typedef struct ppc_tb_t ppc_tb_t; typedef union ppc_tlb_t ppc_tlb_t; typedef struct ppc_hash_pte64 ppc_hash_pte64_t; +typedef struct PPCHash64Options PPCHash64Options; + +typedef struct CPUArchState CPUPPCState; /* SPR access micro-ops generations callbacks */ struct ppc_spr_t { @@ -1341,6 +1346,52 @@ struct ArchCPU { int32_t mig_slb_nr; }; +/** + * PowerPCCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_phases: The parent class' reset phase handlers. + * + * A PowerPC CPU model. + */ +struct PowerPCCPUClass { + CPUClass parent_class; + + DeviceRealize parent_realize; + DeviceUnrealize parent_unrealize; + ResettablePhases parent_phases; + void (*parent_parse_features)(const char *type, char *str, Error **errp); + + uint32_t pvr; + /* + * If @best is false, match if pcc is in the family of pvr + * Else match only if pcc is the best match for pvr in this family. + */ + bool (*pvr_match)(struct PowerPCCPUClass *pcc, uint32_t pvr, bool best); + uint64_t pcr_mask; /* Available bits in PCR register */ + uint64_t pcr_supported; /* Bits for supported PowerISA versions */ + uint32_t svr; + uint64_t insns_flags; + uint64_t insns_flags2; + uint64_t msr_mask; + uint64_t lpcr_mask; /* Available bits in the LPCR */ + uint64_t lpcr_pm; /* Power-saving mode Exit Cause Enable bits */ + powerpc_mmu_t mmu_model; + powerpc_excp_t excp_model; + powerpc_input_t bus_model; + uint32_t flags; + int bfd_mach; + uint32_t l1_dcache_size, l1_icache_size; +#ifndef CONFIG_USER_ONLY + unsigned int gdb_num_sprs; + const char *gdb_spr_xml; +#endif + const PPCHash64Options *hash64_opts; + struct ppc_radix_page_info *radix_page_info; + uint32_t lrg_decr_bits; + int n_host_threads; + void (*init_proc)(CPUPPCState *env); + int (*check_pow)(CPUPPCState *env); +}; ObjectClass *ppc_cpu_class_by_name(const char *name); PowerPCCPUClass *ppc_cpu_class_by_pvr(uint32_t pvr); From d66d3d4ab9a3774406343ad67ae4b4b77bedec7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 13 Oct 2023 12:12:48 +0200 Subject: [PATCH 804/974] target/ppc: Move powerpc_excp_t definition to 'cpu.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The powerpc_excp_t definition is only used by target/ppc/, no need to expose it. Restrict it by moving it to "target/ppc/cpu.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Cédric Le Goater Message-Id: <20231013125630.95116-6-philmd@linaro.org> --- target/ppc/cpu-qom.h | 29 ----------------------------- target/ppc/cpu.h | 27 +++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 29 deletions(-) diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h index 65a640470f..acc5f1a1dc 100644 --- a/target/ppc/cpu-qom.h +++ b/target/ppc/cpu-qom.h @@ -78,35 +78,6 @@ static inline bool mmu_is_64bit(powerpc_mmu_t mmu_model) return mmu_model & POWERPC_MMU_64; } -/*****************************************************************************/ -/* Exception model */ -typedef enum powerpc_excp_t powerpc_excp_t; -enum powerpc_excp_t { - POWERPC_EXCP_UNKNOWN = 0, - /* Standard PowerPC exception model */ - POWERPC_EXCP_STD, - /* PowerPC 40x exception model */ - POWERPC_EXCP_40x, - /* PowerPC 603/604/G2 exception model */ - POWERPC_EXCP_6xx, - /* PowerPC 7xx exception model */ - POWERPC_EXCP_7xx, - /* PowerPC 74xx exception model */ - POWERPC_EXCP_74xx, - /* BookE exception model */ - POWERPC_EXCP_BOOKE, - /* PowerPC 970 exception model */ - POWERPC_EXCP_970, - /* POWER7 exception model */ - POWERPC_EXCP_POWER7, - /* POWER8 exception model */ - POWERPC_EXCP_POWER8, - /* POWER9 exception model */ - POWERPC_EXCP_POWER9, - /* POWER10 exception model */ - POWERPC_EXCP_POWER10, -}; - /*****************************************************************************/ /* Input pins model */ typedef enum powerpc_input_t powerpc_input_t; diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 55330d9319..94a804a605 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -192,6 +192,33 @@ enum { POWERPC_EXCP_TRAP = 0x40, }; +/* Exception model */ +typedef enum powerpc_excp_t { + POWERPC_EXCP_UNKNOWN = 0, + /* Standard PowerPC exception model */ + POWERPC_EXCP_STD, + /* PowerPC 40x exception model */ + POWERPC_EXCP_40x, + /* PowerPC 603/604/G2 exception model */ + POWERPC_EXCP_6xx, + /* PowerPC 7xx exception model */ + POWERPC_EXCP_7xx, + /* PowerPC 74xx exception model */ + POWERPC_EXCP_74xx, + /* BookE exception model */ + POWERPC_EXCP_BOOKE, + /* PowerPC 970 exception model */ + POWERPC_EXCP_970, + /* POWER7 exception model */ + POWERPC_EXCP_POWER7, + /* POWER8 exception model */ + POWERPC_EXCP_POWER8, + /* POWER9 exception model */ + POWERPC_EXCP_POWER9, + /* POWER10 exception model */ + POWERPC_EXCP_POWER10, +} powerpc_excp_t; + #define PPC_INPUT(env) ((env)->bus_model) /*****************************************************************************/ From 6fb8b16a711596fdaa40b4c2fec90a997e4e8d94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 13 Oct 2023 12:13:40 +0200 Subject: [PATCH 805/974] target/ppc: Move powerpc_mmu_t definition to 'cpu.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The powerpc_mmu_t definition is only used by target/ppc/, no need to expose it. Restrict it by moving it to "target/ppc/cpu.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Cédric Le Goater Message-Id: <20231013125630.95116-7-philmd@linaro.org> --- target/ppc/cpu-qom.h | 43 ------------------------------------------- target/ppc/cpu.h | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 43 deletions(-) diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h index acc5f1a1dc..c35374e15f 100644 --- a/target/ppc/cpu-qom.h +++ b/target/ppc/cpu-qom.h @@ -35,49 +35,6 @@ OBJECT_DECLARE_CPU_TYPE(PowerPCCPU, PowerPCCPUClass, POWERPC_CPU) #define TYPE_HOST_POWERPC_CPU POWERPC_CPU_TYPE_NAME("host") -/*****************************************************************************/ -/* MMU model */ -typedef enum powerpc_mmu_t powerpc_mmu_t; -enum powerpc_mmu_t { - POWERPC_MMU_UNKNOWN = 0x00000000, - /* Standard 32 bits PowerPC MMU */ - POWERPC_MMU_32B = 0x00000001, - /* PowerPC 6xx MMU with software TLB */ - POWERPC_MMU_SOFT_6xx = 0x00000002, - /* - * PowerPC 74xx MMU with software TLB (this has been - * disabled, see git history for more information. - * keywords: tlbld tlbli TLBMISS PTEHI PTELO) - */ - POWERPC_MMU_SOFT_74xx = 0x00000003, - /* PowerPC 4xx MMU with software TLB */ - POWERPC_MMU_SOFT_4xx = 0x00000004, - /* PowerPC MMU in real mode only */ - POWERPC_MMU_REAL = 0x00000006, - /* Freescale MPC8xx MMU model */ - POWERPC_MMU_MPC8xx = 0x00000007, - /* BookE MMU model */ - POWERPC_MMU_BOOKE = 0x00000008, - /* BookE 2.06 MMU model */ - POWERPC_MMU_BOOKE206 = 0x00000009, -#define POWERPC_MMU_64 0x00010000 - /* 64 bits PowerPC MMU */ - POWERPC_MMU_64B = POWERPC_MMU_64 | 0x00000001, - /* Architecture 2.03 and later (has LPCR) */ - POWERPC_MMU_2_03 = POWERPC_MMU_64 | 0x00000002, - /* Architecture 2.06 variant */ - POWERPC_MMU_2_06 = POWERPC_MMU_64 | 0x00000003, - /* Architecture 2.07 variant */ - POWERPC_MMU_2_07 = POWERPC_MMU_64 | 0x00000004, - /* Architecture 3.00 variant */ - POWERPC_MMU_3_00 = POWERPC_MMU_64 | 0x00000005, -}; - -static inline bool mmu_is_64bit(powerpc_mmu_t mmu_model) -{ - return mmu_model & POWERPC_MMU_64; -} - /*****************************************************************************/ /* Input pins model */ typedef enum powerpc_input_t powerpc_input_t; diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 94a804a605..d859c45a2e 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -219,6 +219,48 @@ typedef enum powerpc_excp_t { POWERPC_EXCP_POWER10, } powerpc_excp_t; +/*****************************************************************************/ +/* MMU model */ +typedef enum powerpc_mmu_t { + POWERPC_MMU_UNKNOWN = 0x00000000, + /* Standard 32 bits PowerPC MMU */ + POWERPC_MMU_32B = 0x00000001, + /* PowerPC 6xx MMU with software TLB */ + POWERPC_MMU_SOFT_6xx = 0x00000002, + /* + * PowerPC 74xx MMU with software TLB (this has been + * disabled, see git history for more information. + * keywords: tlbld tlbli TLBMISS PTEHI PTELO) + */ + POWERPC_MMU_SOFT_74xx = 0x00000003, + /* PowerPC 4xx MMU with software TLB */ + POWERPC_MMU_SOFT_4xx = 0x00000004, + /* PowerPC MMU in real mode only */ + POWERPC_MMU_REAL = 0x00000006, + /* Freescale MPC8xx MMU model */ + POWERPC_MMU_MPC8xx = 0x00000007, + /* BookE MMU model */ + POWERPC_MMU_BOOKE = 0x00000008, + /* BookE 2.06 MMU model */ + POWERPC_MMU_BOOKE206 = 0x00000009, +#define POWERPC_MMU_64 0x00010000 + /* 64 bits PowerPC MMU */ + POWERPC_MMU_64B = POWERPC_MMU_64 | 0x00000001, + /* Architecture 2.03 and later (has LPCR) */ + POWERPC_MMU_2_03 = POWERPC_MMU_64 | 0x00000002, + /* Architecture 2.06 variant */ + POWERPC_MMU_2_06 = POWERPC_MMU_64 | 0x00000003, + /* Architecture 2.07 variant */ + POWERPC_MMU_2_07 = POWERPC_MMU_64 | 0x00000004, + /* Architecture 3.00 variant */ + POWERPC_MMU_3_00 = POWERPC_MMU_64 | 0x00000005, +} powerpc_mmu_t; + +static inline bool mmu_is_64bit(powerpc_mmu_t mmu_model) +{ + return mmu_model & POWERPC_MMU_64; +} + #define PPC_INPUT(env) ((env)->bus_model) /*****************************************************************************/ From 2bb53fa2f36c03ed73b643592d9f6d2c3898d9d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 13 Oct 2023 12:14:19 +0200 Subject: [PATCH 806/974] target/ppc: Move powerpc_input_t definition to 'cpu.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The powerpc_input_t definition is only used by target/ppc/, no need to expose it. Restrict it by moving it to "target/ppc/cpu.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Cédric Le Goater Message-Id: <20231013125630.95116-8-philmd@linaro.org> --- target/ppc/cpu-qom.h | 21 --------------------- target/ppc/cpu.h | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h index c35374e15f..0241609efe 100644 --- a/target/ppc/cpu-qom.h +++ b/target/ppc/cpu-qom.h @@ -35,27 +35,6 @@ OBJECT_DECLARE_CPU_TYPE(PowerPCCPU, PowerPCCPUClass, POWERPC_CPU) #define TYPE_HOST_POWERPC_CPU POWERPC_CPU_TYPE_NAME("host") -/*****************************************************************************/ -/* Input pins model */ -typedef enum powerpc_input_t powerpc_input_t; -enum powerpc_input_t { - PPC_FLAGS_INPUT_UNKNOWN = 0, - /* PowerPC 6xx bus */ - PPC_FLAGS_INPUT_6xx, - /* BookE bus */ - PPC_FLAGS_INPUT_BookE, - /* PowerPC 405 bus */ - PPC_FLAGS_INPUT_405, - /* PowerPC 970 bus */ - PPC_FLAGS_INPUT_970, - /* PowerPC POWER7 bus */ - PPC_FLAGS_INPUT_POWER7, - /* PowerPC POWER9 bus */ - PPC_FLAGS_INPUT_POWER9, - /* Freescale RCPU bus */ - PPC_FLAGS_INPUT_RCPU, -}; - #ifndef CONFIG_USER_ONLY typedef struct PPCTimebase { uint64_t guest_timebase; diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index d859c45a2e..f8101ffa29 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -261,6 +261,26 @@ static inline bool mmu_is_64bit(powerpc_mmu_t mmu_model) return mmu_model & POWERPC_MMU_64; } +/*****************************************************************************/ +/* Input pins model */ +typedef enum powerpc_input_t { + PPC_FLAGS_INPUT_UNKNOWN = 0, + /* PowerPC 6xx bus */ + PPC_FLAGS_INPUT_6xx, + /* BookE bus */ + PPC_FLAGS_INPUT_BookE, + /* PowerPC 405 bus */ + PPC_FLAGS_INPUT_405, + /* PowerPC 970 bus */ + PPC_FLAGS_INPUT_970, + /* PowerPC POWER7 bus */ + PPC_FLAGS_INPUT_POWER7, + /* PowerPC POWER9 bus */ + PPC_FLAGS_INPUT_POWER9, + /* Freescale RCPU bus */ + PPC_FLAGS_INPUT_RCPU, +} powerpc_input_t; + #define PPC_INPUT(env) ((env)->bus_model) /*****************************************************************************/ From 6233759ae15302e0ea60b8cda849fcd2c01d8098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 5 Nov 2023 19:22:57 +0100 Subject: [PATCH 807/974] hw/s390x/css: Have css_do_sic() take S390CPU instead of CPUS390XState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "hw/s390x/css.h" is a header used by target-agnostic objects (such hw/s390x/virtio-ccw-gpu.c), thus can not use target-specific types, such CPUS390XState. Have css_do_sic() take S390CPU a pointer, which is target-agnostic. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20231106114500.5269-2-philmd@linaro.org> --- hw/s390x/css.c | 3 ++- include/hw/s390x/css.h | 2 +- target/s390x/kvm/kvm.c | 2 +- target/s390x/tcg/misc_helper.c | 3 ++- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/hw/s390x/css.c b/hw/s390x/css.c index 95d1b3a3ce..bcedec2fc8 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -644,8 +644,9 @@ void css_conditional_io_interrupt(SubchDev *sch) } } -int css_do_sic(CPUS390XState *env, uint8_t isc, uint16_t mode) +int css_do_sic(S390CPU *cpu, uint8_t isc, uint16_t mode) { + CPUS390XState *env = &cpu->env; S390FLICState *fs = s390_get_flic(); S390FLICStateClass *fsc = s390_get_flic_class(fs); int r; diff --git a/include/hw/s390x/css.h b/include/hw/s390x/css.h index 75e5381613..ba72ee3dd2 100644 --- a/include/hw/s390x/css.h +++ b/include/hw/s390x/css.h @@ -233,7 +233,7 @@ typedef enum { } CssIoAdapterType; void css_adapter_interrupt(CssIoAdapterType type, uint8_t isc); -int css_do_sic(CPUS390XState *env, uint8_t isc, uint16_t mode); +int css_do_sic(S390CPU *cpu, uint8_t isc, uint16_t mode); uint32_t css_get_adapter_id(CssIoAdapterType type, uint8_t isc); void css_register_io_adapters(CssIoAdapterType type, bool swap, bool maskable, uint8_t flags, Error **errp); diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c index 0f0e784b2a..1ddad0bec1 100644 --- a/target/s390x/kvm/kvm.c +++ b/target/s390x/kvm/kvm.c @@ -1358,7 +1358,7 @@ static int kvm_sic_service_call(S390CPU *cpu, struct kvm_run *run) mode = env->regs[r1] & 0xffff; isc = (env->regs[r3] >> 27) & 0x7; - r = css_do_sic(env, isc, mode); + r = css_do_sic(cpu, isc, mode); if (r) { kvm_s390_program_interrupt(cpu, -r); } diff --git a/target/s390x/tcg/misc_helper.c b/target/s390x/tcg/misc_helper.c index e85658ce22..56c7f00cf9 100644 --- a/target/s390x/tcg/misc_helper.c +++ b/target/s390x/tcg/misc_helper.c @@ -761,10 +761,11 @@ void HELPER(stpcifc)(CPUS390XState *env, uint32_t r1, uint64_t fiba, void HELPER(sic)(CPUS390XState *env, uint64_t r1, uint64_t r3) { + S390CPU *cpu = env_archcpu(env); int r; qemu_mutex_lock_iothread(); - r = css_do_sic(env, (r3 >> 27) & 0x7, r1 & 0xffff); + r = css_do_sic(cpu, (r3 >> 27) & 0x7, r1 & 0xffff); qemu_mutex_unlock_iothread(); /* css_do_sic() may actually return a PGM_xxx value to inject */ if (r) { From 6d3910c9dbad5ff47c43214397f7afef9645ceb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 6 Nov 2023 07:55:22 +0100 Subject: [PATCH 808/974] hw/s390x/sclp: Have sclp_service_call[_protected]() take S390CPU* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "hw/s390x/sclp.h" is a header used by target-agnostic objects (such hw/char/sclpconsole[-lm].c), thus can not use target-specific types, such CPUS390XState. Have sclp_service_call[_protected]() take a S390CPU pointer, which is target-agnostic. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20231106114500.5269-3-philmd@linaro.org> --- hw/s390x/sclp.c | 7 ++++--- include/hw/s390x/sclp.h | 5 ++--- target/s390x/kvm/kvm.c | 4 ++-- target/s390x/tcg/misc_helper.c | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c index d339cbb7e4..893e71a41b 100644 --- a/hw/s390x/sclp.c +++ b/hw/s390x/sclp.c @@ -269,9 +269,9 @@ static void sclp_execute(SCLPDevice *sclp, SCCB *sccb, uint32_t code) * service_interrupt call. */ #define SCLP_PV_DUMMY_ADDR 0x4000 -int sclp_service_call_protected(CPUS390XState *env, uint64_t sccb, - uint32_t code) +int sclp_service_call_protected(S390CPU *cpu, uint64_t sccb, uint32_t code) { + CPUS390XState *env = &cpu->env; SCLPDevice *sclp = get_sclp_device(); SCLPDeviceClass *sclp_c = SCLP_GET_CLASS(sclp); SCCBHeader header; @@ -296,8 +296,9 @@ out_write: return 0; } -int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code) +int sclp_service_call(S390CPU *cpu, uint64_t sccb, uint32_t code) { + CPUS390XState *env = &cpu->env; SCLPDevice *sclp = get_sclp_device(); SCLPDeviceClass *sclp_c = SCLP_GET_CLASS(sclp); SCCBHeader header; diff --git a/include/hw/s390x/sclp.h b/include/hw/s390x/sclp.h index 9aef6d9370..e229b81a67 100644 --- a/include/hw/s390x/sclp.h +++ b/include/hw/s390x/sclp.h @@ -227,8 +227,7 @@ static inline int sccb_data_len(SCCB *sccb) void s390_sclp_init(void); void sclp_service_interrupt(uint32_t sccb); void raise_irq_cpu_hotplug(void); -int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code); -int sclp_service_call_protected(CPUS390XState *env, uint64_t sccb, - uint32_t code); +int sclp_service_call(S390CPU *cpu, uint64_t sccb, uint32_t code); +int sclp_service_call_protected(S390CPU *cpu, uint64_t sccb, uint32_t code); #endif diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c index 1ddad0bec1..33ab3551f4 100644 --- a/target/s390x/kvm/kvm.c +++ b/target/s390x/kvm/kvm.c @@ -1174,12 +1174,12 @@ static void kvm_sclp_service_call(S390CPU *cpu, struct kvm_run *run, break; case ICPT_PV_INSTR: g_assert(s390_is_pv()); - sclp_service_call_protected(env, sccb, code); + sclp_service_call_protected(cpu, sccb, code); /* Setting the CC is done by the Ultravisor. */ break; case ICPT_INSTRUCTION: g_assert(!s390_is_pv()); - r = sclp_service_call(env, sccb, code); + r = sclp_service_call(cpu, sccb, code); if (r < 0) { kvm_s390_program_interrupt(cpu, -r); return; diff --git a/target/s390x/tcg/misc_helper.c b/target/s390x/tcg/misc_helper.c index 56c7f00cf9..6aa7907438 100644 --- a/target/s390x/tcg/misc_helper.c +++ b/target/s390x/tcg/misc_helper.c @@ -102,7 +102,7 @@ uint64_t HELPER(stck)(CPUS390XState *env) uint32_t HELPER(servc)(CPUS390XState *env, uint64_t r1, uint64_t r2) { qemu_mutex_lock_iothread(); - int r = sclp_service_call(env, r1, r2); + int r = sclp_service_call(env_archcpu(env), r1, r2); qemu_mutex_unlock_iothread(); if (r < 0) { tcg_s390_program_interrupt(env, -r, GETPC()); From 1663e886cb3b609aed05b92c97d24bc6e66110f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 6 Nov 2023 12:37:45 +0100 Subject: [PATCH 809/974] target/s390x/cpu: Restrict cpu_get_tb_cpu_state() definition to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cpu_get_tb_cpu_state() is TCG specific. Another accelerator calling it would be a bug, so restrict the definition to TCG, along with "tcg_s390x.h" header inclusion. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20231106114500.5269-4-philmd@linaro.org> --- target/s390x/cpu.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index 38d7197f4c..110902fa3c 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -29,7 +29,6 @@ #include "cpu_models.h" #include "exec/cpu-defs.h" #include "qemu/cpu-float.h" -#include "tcg/tcg_s390x.h" #include "qapi/qapi-types-machine-common.h" #define ELF_MACHINE_UNAME "S390X" @@ -383,6 +382,10 @@ static inline int cpu_mmu_index(CPUS390XState *env, bool ifetch) #endif } +#ifdef CONFIG_TCG + +#include "tcg/tcg_s390x.h" + static inline void cpu_get_tb_cpu_state(CPUS390XState *env, vaddr *pc, uint64_t *cs_base, uint32_t *flags) { @@ -405,6 +408,8 @@ static inline void cpu_get_tb_cpu_state(CPUS390XState *env, vaddr *pc, } } +#endif /* CONFIG_TCG */ + /* PER bits from control register 9 */ #define PER_CR9_EVENT_BRANCH 0x80000000 #define PER_CR9_EVENT_IFETCH 0x40000000 From 571568a173dfae14c27ef0db9fd2f7b77f0e4bad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 5 Nov 2023 19:15:49 +0100 Subject: [PATCH 810/974] target/s390x/cpu: Restrict CPUS390XState declaration to 'cpu.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "target/s390x/cpu-qom.h" has to be target-agnostic. However, it currently declares CPUS390XState, which is target-specific. Move that declaration to "cpu.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20231106114500.5269-5-philmd@linaro.org> --- target/s390x/cpu-qom.h | 2 -- target/s390x/cpu.h | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/target/s390x/cpu-qom.h b/target/s390x/cpu-qom.h index fcd70daddf..ccf126b7a9 100644 --- a/target/s390x/cpu-qom.h +++ b/target/s390x/cpu-qom.h @@ -33,8 +33,6 @@ OBJECT_DECLARE_CPU_TYPE(S390CPU, S390CPUClass, S390_CPU) typedef struct S390CPUModel S390CPUModel; typedef struct S390CPUDef S390CPUDef; -typedef struct CPUArchState CPUS390XState; - typedef enum cpu_reset_type { S390_CPU_RESET_NORMAL, S390_CPU_RESET_INITIAL, diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index 110902fa3c..942589c597 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -55,7 +55,7 @@ typedef struct PSW { uint64_t addr; } PSW; -struct CPUArchState { +typedef struct CPUArchState { uint64_t regs[16]; /* GP registers */ /* * The floating point registers are part of the vector registers. @@ -157,7 +157,7 @@ struct CPUArchState { /* currently processed sigp order */ uint8_t sigp_order; -}; +} CPUS390XState; static inline uint64_t *get_freg(CPUS390XState *cs, int nr) { From c61b18a5d0562851b933cd2fdc61cf21ea1be270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 15 Sep 2023 17:23:47 +0200 Subject: [PATCH 811/974] target/nios2: Create IRQs *after* accelerator vCPU is realized MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Architecture specific hardware doesn't have a particular dependency on the accelerator vCPU (created with cpu_exec_realizefn), and can be initialized *after* the vCPU is realized. Doing so allows further generic API simplification (in few commits). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230918160257.30127-12-philmd@linaro.org> --- target/nios2/cpu.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/target/nios2/cpu.c b/target/nios2/cpu.c index 15e499f828..a27732bf2b 100644 --- a/target/nios2/cpu.c +++ b/target/nios2/cpu.c @@ -199,14 +199,6 @@ static void nios2_cpu_realizefn(DeviceState *dev, Error **errp) Nios2CPUClass *ncc = NIOS2_CPU_GET_CLASS(dev); Error *local_err = NULL; -#ifndef CONFIG_USER_ONLY - if (cpu->eic_present) { - qdev_init_gpio_in_named(DEVICE(cpu), eic_set_irq, "EIC", 1); - } else { - qdev_init_gpio_in_named(DEVICE(cpu), iic_set_irq, "IRQ", 32); - } -#endif - cpu_exec_realizefn(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); @@ -220,6 +212,14 @@ static void nios2_cpu_realizefn(DeviceState *dev, Error **errp) /* We have reserved storage for cpuid; might as well use it. */ cpu->env.ctrl[CR_CPUID] = cs->cpu_index; +#ifndef CONFIG_USER_ONLY + if (cpu->eic_present) { + qdev_init_gpio_in_named(DEVICE(cpu), eic_set_irq, "EIC", 1); + } else { + qdev_init_gpio_in_named(DEVICE(cpu), iic_set_irq, "IRQ", 32); + } +#endif + ncc->parent_realize(dev, errp); } From 9348028e7ed089a1e9ed45091668c4c199e76fcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 13 Oct 2023 11:35:04 +0200 Subject: [PATCH 812/974] target: Move ArchCPUClass definition to 'cpu.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The OBJECT_DECLARE_CPU_TYPE() macro forward-declares each ArchCPUClass type. These forward declarations are sufficient for code in hw/ to use the QOM definitions. No need to expose these structure definitions. Keep each local to their target/ by moving them to the corresponding "cpu.h" header. Suggested-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20231013140116.255-13-philmd@linaro.org> --- target/alpha/cpu-qom.h | 16 --------------- target/alpha/cpu.h | 13 +++++++++++++ target/arm/cpu-qom.h | 27 ------------------------- target/arm/cpu.h | 25 ++++++++++++++++++++++++ target/avr/cpu-qom.h | 16 --------------- target/avr/cpu.h | 14 +++++++++++++ target/cris/cpu-qom.h | 19 ------------------ target/cris/cpu.h | 16 +++++++++++++++ target/hexagon/cpu-qom.h | 1 - target/hppa/cpu-qom.h | 16 --------------- target/hppa/cpu.h | 14 +++++++++++++ target/i386/cpu-qom.h | 39 ------------------------------------- target/i386/cpu.h | 38 ++++++++++++++++++++++++++++++++++++ target/loongarch/cpu-qom.h | 1 - target/m68k/cpu-qom.h | 16 --------------- target/m68k/cpu.h | 13 +++++++++++++ target/microblaze/cpu-qom.h | 16 --------------- target/microblaze/cpu.h | 13 +++++++++++++ target/mips/cpu-qom.h | 20 ------------------- target/mips/cpu.h | 17 ++++++++++++++++ target/nios2/cpu-qom.h | 1 - target/openrisc/cpu-qom.h | 1 - target/riscv/cpu-qom.h | 16 --------------- target/riscv/cpu.h | 16 +++++++++++++++ target/rx/cpu-qom.h | 15 -------------- target/rx/cpu.h | 14 +++++++++++++ target/s390x/cpu-qom.h | 35 --------------------------------- target/s390x/cpu.h | 30 ++++++++++++++++++++++++++++ target/s390x/cpu_models.h | 8 ++++---- target/sh4/cpu-qom.h | 23 ---------------------- target/sh4/cpu.h | 20 +++++++++++++++++++ target/sparc/cpu-qom.h | 18 ----------------- target/sparc/cpu.h | 18 +++++++++++++++-- target/tricore/cpu-qom.h | 10 ---------- target/tricore/cpu.h | 6 ++++++ target/xtensa/cpu-qom.h | 21 -------------------- target/xtensa/cpu.h | 20 +++++++++++++++++-- 37 files changed, 287 insertions(+), 335 deletions(-) diff --git a/target/alpha/cpu-qom.h b/target/alpha/cpu-qom.h index c4a4523993..1b32b18d34 100644 --- a/target/alpha/cpu-qom.h +++ b/target/alpha/cpu-qom.h @@ -21,7 +21,6 @@ #define QEMU_ALPHA_CPU_QOM_H #include "hw/core/cpu.h" -#include "qom/object.h" #define TYPE_ALPHA_CPU "alpha-cpu" @@ -30,19 +29,4 @@ OBJECT_DECLARE_CPU_TYPE(AlphaCPU, AlphaCPUClass, ALPHA_CPU) #define ALPHA_CPU_TYPE_SUFFIX "-" TYPE_ALPHA_CPU #define ALPHA_CPU_TYPE_NAME(model) model ALPHA_CPU_TYPE_SUFFIX -/** - * AlphaCPUClass: - * @parent_realize: The parent class' realize handler. - * @parent_reset: The parent class' reset handler. - * - * An Alpha CPU model. - */ -struct AlphaCPUClass { - CPUClass parent_class; - - DeviceRealize parent_realize; - DeviceReset parent_reset; -}; - - #endif diff --git a/target/alpha/cpu.h b/target/alpha/cpu.h index 3bff56c565..d672e911dd 100644 --- a/target/alpha/cpu.h +++ b/target/alpha/cpu.h @@ -267,6 +267,19 @@ struct ArchCPU { QEMUTimer *alarm_timer; }; +/** + * AlphaCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_reset: The parent class' reset handler. + * + * An Alpha CPU model. + */ +struct AlphaCPUClass { + CPUClass parent_class; + + DeviceRealize parent_realize; + DeviceReset parent_reset; +}; #ifndef CONFIG_USER_ONLY extern const VMStateDescription vmstate_alpha_cpu; diff --git a/target/arm/cpu-qom.h b/target/arm/cpu-qom.h index 35c3b0924e..02b914c876 100644 --- a/target/arm/cpu-qom.h +++ b/target/arm/cpu-qom.h @@ -21,7 +21,6 @@ #define QEMU_ARM_CPU_QOM_H #include "hw/core/cpu.h" -#include "qom/object.h" #define TYPE_ARM_CPU "arm-cpu" @@ -29,35 +28,9 @@ OBJECT_DECLARE_CPU_TYPE(ARMCPU, ARMCPUClass, ARM_CPU) #define TYPE_ARM_MAX_CPU "max-" TYPE_ARM_CPU -typedef struct ARMCPUInfo { - const char *name; - void (*initfn)(Object *obj); - void (*class_init)(ObjectClass *oc, void *data); -} ARMCPUInfo; - -/** - * ARMCPUClass: - * @parent_realize: The parent class' realize handler. - * @parent_phases: The parent class' reset phase handlers. - * - * An ARM CPU model. - */ -struct ARMCPUClass { - CPUClass parent_class; - - const ARMCPUInfo *info; - DeviceRealize parent_realize; - ResettablePhases parent_phases; -}; - - #define TYPE_AARCH64_CPU "aarch64-cpu" typedef struct AArch64CPUClass AArch64CPUClass; DECLARE_CLASS_CHECKERS(AArch64CPUClass, AARCH64_CPU, TYPE_AARCH64_CPU) -struct AArch64CPUClass { - ARMCPUClass parent_class; -}; - #endif diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 4a86c8f831..a0282e0d28 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1116,6 +1116,31 @@ struct ArchCPU { uint64_t gt_cntfrq_hz; }; +typedef struct ARMCPUInfo { + const char *name; + void (*initfn)(Object *obj); + void (*class_init)(ObjectClass *oc, void *data); +} ARMCPUInfo; + +/** + * ARMCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_phases: The parent class' reset phase handlers. + * + * An ARM CPU model. + */ +struct ARMCPUClass { + CPUClass parent_class; + + const ARMCPUInfo *info; + DeviceRealize parent_realize; + ResettablePhases parent_phases; +}; + +struct AArch64CPUClass { + ARMCPUClass parent_class; +}; + /* Callback functions for the generic timer's timers. */ void arm_gt_ptimer_cb(void *opaque); void arm_gt_vtimer_cb(void *opaque); diff --git a/target/avr/cpu-qom.h b/target/avr/cpu-qom.h index 75590cdd97..38dbcc0535 100644 --- a/target/avr/cpu-qom.h +++ b/target/avr/cpu-qom.h @@ -22,7 +22,6 @@ #define TARGET_AVR_CPU_QOM_H #include "hw/core/cpu.h" -#include "qom/object.h" #define TYPE_AVR_CPU "avr-cpu" @@ -31,19 +30,4 @@ OBJECT_DECLARE_CPU_TYPE(AVRCPU, AVRCPUClass, AVR_CPU) #define AVR_CPU_TYPE_SUFFIX "-" TYPE_AVR_CPU #define AVR_CPU_TYPE_NAME(name) (name AVR_CPU_TYPE_SUFFIX) -/** - * AVRCPUClass: - * @parent_realize: The parent class' realize handler. - * @parent_phases: The parent class' reset phase handlers. - * - * A AVR CPU model. - */ -struct AVRCPUClass { - CPUClass parent_class; - - DeviceRealize parent_realize; - ResettablePhases parent_phases; -}; - - #endif /* TARGET_AVR_CPU_QOM_H */ diff --git a/target/avr/cpu.h b/target/avr/cpu.h index 0487399cb2..8a17862737 100644 --- a/target/avr/cpu.h +++ b/target/avr/cpu.h @@ -147,6 +147,20 @@ struct ArchCPU { CPUAVRState env; }; +/** + * AVRCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_phases: The parent class' reset phase handlers. + * + * A AVR CPU model. + */ +struct AVRCPUClass { + CPUClass parent_class; + + DeviceRealize parent_realize; + ResettablePhases parent_phases; +}; + extern const struct VMStateDescription vms_avr_cpu; void avr_cpu_do_interrupt(CPUState *cpu); diff --git a/target/cris/cpu-qom.h b/target/cris/cpu-qom.h index d7e5f33e62..741ca97a1b 100644 --- a/target/cris/cpu-qom.h +++ b/target/cris/cpu-qom.h @@ -21,7 +21,6 @@ #define QEMU_CRIS_CPU_QOM_H #include "hw/core/cpu.h" -#include "qom/object.h" #define TYPE_CRIS_CPU "cris-cpu" @@ -30,22 +29,4 @@ OBJECT_DECLARE_CPU_TYPE(CRISCPU, CRISCPUClass, CRIS_CPU) #define CRIS_CPU_TYPE_SUFFIX "-" TYPE_CRIS_CPU #define CRIS_CPU_TYPE_NAME(name) (name CRIS_CPU_TYPE_SUFFIX) -/** - * CRISCPUClass: - * @parent_realize: The parent class' realize handler. - * @parent_phases: The parent class' reset phase handlers. - * @vr: Version Register value. - * - * A CRIS CPU model. - */ -struct CRISCPUClass { - CPUClass parent_class; - - DeviceRealize parent_realize; - ResettablePhases parent_phases; - - uint32_t vr; -}; - - #endif diff --git a/target/cris/cpu.h b/target/cris/cpu.h index b821bb7983..1be7f90319 100644 --- a/target/cris/cpu.h +++ b/target/cris/cpu.h @@ -179,6 +179,22 @@ struct ArchCPU { CPUCRISState env; }; +/** + * CRISCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_phases: The parent class' reset phase handlers. + * @vr: Version Register value. + * + * A CRIS CPU model. + */ +struct CRISCPUClass { + CPUClass parent_class; + + DeviceRealize parent_realize; + ResettablePhases parent_phases; + + uint32_t vr; +}; #ifndef CONFIG_USER_ONLY extern const VMStateDescription vmstate_cris_cpu; diff --git a/target/hexagon/cpu-qom.h b/target/hexagon/cpu-qom.h index f02df7ee6f..da92fe7468 100644 --- a/target/hexagon/cpu-qom.h +++ b/target/hexagon/cpu-qom.h @@ -10,7 +10,6 @@ #define QEMU_HEXAGON_CPU_QOM_H #include "hw/core/cpu.h" -#include "qom/object.h" #define TYPE_HEXAGON_CPU "hexagon-cpu" diff --git a/target/hppa/cpu-qom.h b/target/hppa/cpu-qom.h index b7d9cdfe11..5c454bf543 100644 --- a/target/hppa/cpu-qom.h +++ b/target/hppa/cpu-qom.h @@ -21,26 +21,10 @@ #define QEMU_HPPA_CPU_QOM_H #include "hw/core/cpu.h" -#include "qom/object.h" #define TYPE_HPPA_CPU "hppa-cpu" #define TYPE_HPPA64_CPU "hppa64-cpu" OBJECT_DECLARE_CPU_TYPE(HPPACPU, HPPACPUClass, HPPA_CPU) -/** - * HPPACPUClass: - * @parent_realize: The parent class' realize handler. - * @parent_reset: The parent class' reset handler. - * - * An HPPA CPU model. - */ -struct HPPACPUClass { - CPUClass parent_class; - - DeviceRealize parent_realize; - DeviceReset parent_reset; -}; - - #endif diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index b39bae00d3..cecec59700 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -253,6 +253,20 @@ struct ArchCPU { QEMUTimer *alarm_timer; }; +/** + * HPPACPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_reset: The parent class' reset handler. + * + * An HPPA CPU model. + */ +struct HPPACPUClass { + CPUClass parent_class; + + DeviceRealize parent_realize; + DeviceReset parent_reset; +}; + #include "exec/cpu-all.h" static inline bool hppa_is_pa20(CPUHPPAState *env) diff --git a/target/i386/cpu-qom.h b/target/i386/cpu-qom.h index dffc74c1ce..d4e216d000 100644 --- a/target/i386/cpu-qom.h +++ b/target/i386/cpu-qom.h @@ -21,8 +21,6 @@ #define QEMU_I386_CPU_QOM_H #include "hw/core/cpu.h" -#include "qemu/notify.h" -#include "qom/object.h" #ifdef TARGET_X86_64 #define TYPE_X86_CPU "x86_64-cpu" @@ -35,41 +33,4 @@ OBJECT_DECLARE_CPU_TYPE(X86CPU, X86CPUClass, X86_CPU) #define X86_CPU_TYPE_SUFFIX "-" TYPE_X86_CPU #define X86_CPU_TYPE_NAME(name) (name X86_CPU_TYPE_SUFFIX) -typedef struct X86CPUModel X86CPUModel; - -/** - * X86CPUClass: - * @cpu_def: CPU model definition - * @host_cpuid_required: Whether CPU model requires cpuid from host. - * @ordering: Ordering on the "-cpu help" CPU model list. - * @migration_safe: See CpuDefinitionInfo::migration_safe - * @static_model: See CpuDefinitionInfo::static - * @parent_realize: The parent class' realize handler. - * @parent_phases: The parent class' reset phase handlers. - * - * An x86 CPU model or family. - */ -struct X86CPUClass { - CPUClass parent_class; - - /* CPU definition, automatically loaded by instance_init if not NULL. - * Should be eventually replaced by subclass-specific property defaults. - */ - X86CPUModel *model; - - bool host_cpuid_required; - int ordering; - bool migration_safe; - bool static_model; - - /* Optional description of CPU model. - * If unavailable, cpu_def->model_id is used */ - const char *model_description; - - DeviceRealize parent_realize; - DeviceUnrealize parent_unrealize; - ResettablePhases parent_phases; -}; - - #endif diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 6c6b066986..0028aeb0ef 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2037,6 +2037,44 @@ struct ArchCPU { bool xen_vapic; }; +typedef struct X86CPUModel X86CPUModel; + +/** + * X86CPUClass: + * @cpu_def: CPU model definition + * @host_cpuid_required: Whether CPU model requires cpuid from host. + * @ordering: Ordering on the "-cpu help" CPU model list. + * @migration_safe: See CpuDefinitionInfo::migration_safe + * @static_model: See CpuDefinitionInfo::static + * @parent_realize: The parent class' realize handler. + * @parent_phases: The parent class' reset phase handlers. + * + * An x86 CPU model or family. + */ +struct X86CPUClass { + CPUClass parent_class; + + /* + * CPU definition, automatically loaded by instance_init if not NULL. + * Should be eventually replaced by subclass-specific property defaults. + */ + X86CPUModel *model; + + bool host_cpuid_required; + int ordering; + bool migration_safe; + bool static_model; + + /* + * Optional description of CPU model. + * If unavailable, cpu_def->model_id is used. + */ + const char *model_description; + + DeviceRealize parent_realize; + DeviceUnrealize parent_unrealize; + ResettablePhases parent_phases; +}; #ifndef CONFIG_USER_ONLY extern const VMStateDescription vmstate_x86_cpu; diff --git a/target/loongarch/cpu-qom.h b/target/loongarch/cpu-qom.h index 82c86d146d..fa3fcf7186 100644 --- a/target/loongarch/cpu-qom.h +++ b/target/loongarch/cpu-qom.h @@ -9,7 +9,6 @@ #define LOONGARCH_CPU_QOM_H #include "hw/core/cpu.h" -#include "qom/object.h" #define TYPE_LOONGARCH_CPU "loongarch-cpu" #define TYPE_LOONGARCH32_CPU "loongarch32-cpu" diff --git a/target/m68k/cpu-qom.h b/target/m68k/cpu-qom.h index df0cc8b7a3..273e8eae41 100644 --- a/target/m68k/cpu-qom.h +++ b/target/m68k/cpu-qom.h @@ -21,7 +21,6 @@ #define QEMU_M68K_CPU_QOM_H #include "hw/core/cpu.h" -#include "qom/object.h" #define TYPE_M68K_CPU "m68k-cpu" @@ -30,19 +29,4 @@ OBJECT_DECLARE_CPU_TYPE(M68kCPU, M68kCPUClass, M68K_CPU) #define M68K_CPU_TYPE_SUFFIX "-" TYPE_M68K_CPU #define M68K_CPU_TYPE_NAME(model) model M68K_CPU_TYPE_SUFFIX -/* - * M68kCPUClass: - * @parent_realize: The parent class' realize handler. - * @parent_phases: The parent class' reset phase handlers. - * - * A Motorola 68k CPU model. - */ -struct M68kCPUClass { - CPUClass parent_class; - - DeviceRealize parent_realize; - ResettablePhases parent_phases; -}; - - #endif diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h index 7f34686a6f..6cfc696d2b 100644 --- a/target/m68k/cpu.h +++ b/target/m68k/cpu.h @@ -169,6 +169,19 @@ struct ArchCPU { CPUM68KState env; }; +/* + * M68kCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_phases: The parent class' reset phase handlers. + * + * A Motorola 68k CPU model. + */ +struct M68kCPUClass { + CPUClass parent_class; + + DeviceRealize parent_realize; + ResettablePhases parent_phases; +}; #ifndef CONFIG_USER_ONLY void m68k_cpu_do_interrupt(CPUState *cpu); diff --git a/target/microblaze/cpu-qom.h b/target/microblaze/cpu-qom.h index 78f549b57d..92e539fb2f 100644 --- a/target/microblaze/cpu-qom.h +++ b/target/microblaze/cpu-qom.h @@ -21,25 +21,9 @@ #define QEMU_MICROBLAZE_CPU_QOM_H #include "hw/core/cpu.h" -#include "qom/object.h" #define TYPE_MICROBLAZE_CPU "microblaze-cpu" OBJECT_DECLARE_CPU_TYPE(MicroBlazeCPU, MicroBlazeCPUClass, MICROBLAZE_CPU) -/** - * MicroBlazeCPUClass: - * @parent_realize: The parent class' realize handler. - * @parent_phases: The parent class' reset phase handlers. - * - * A MicroBlaze CPU model. - */ -struct MicroBlazeCPUClass { - CPUClass parent_class; - - DeviceRealize parent_realize; - ResettablePhases parent_phases; -}; - - #endif diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h index e8000237d8..b5374365f5 100644 --- a/target/microblaze/cpu.h +++ b/target/microblaze/cpu.h @@ -355,6 +355,19 @@ struct ArchCPU { MicroBlazeCPUConfig cfg; }; +/** + * MicroBlazeCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_phases: The parent class' reset phase handlers. + * + * A MicroBlaze CPU model. + */ +struct MicroBlazeCPUClass { + CPUClass parent_class; + + DeviceRealize parent_realize; + ResettablePhases parent_phases; +}; #ifndef CONFIG_USER_ONLY void mb_cpu_do_interrupt(CPUState *cs); diff --git a/target/mips/cpu-qom.h b/target/mips/cpu-qom.h index 5822dfb1d2..0eea2a2598 100644 --- a/target/mips/cpu-qom.h +++ b/target/mips/cpu-qom.h @@ -21,7 +21,6 @@ #define QEMU_MIPS_CPU_QOM_H #include "hw/core/cpu.h" -#include "qom/object.h" #ifdef TARGET_MIPS64 #define TYPE_MIPS_CPU "mips64-cpu" @@ -34,23 +33,4 @@ OBJECT_DECLARE_CPU_TYPE(MIPSCPU, MIPSCPUClass, MIPS_CPU) #define MIPS_CPU_TYPE_SUFFIX "-" TYPE_MIPS_CPU #define MIPS_CPU_TYPE_NAME(model) model MIPS_CPU_TYPE_SUFFIX -/** - * MIPSCPUClass: - * @parent_realize: The parent class' realize handler. - * @parent_phases: The parent class' reset phase handlers. - * - * A MIPS CPU model. - */ -struct MIPSCPUClass { - CPUClass parent_class; - - DeviceRealize parent_realize; - ResettablePhases parent_phases; - const struct mips_def_t *cpu_def; - - /* Used for the jazz board to modify mips_cpu_do_transaction_failed. */ - bool no_data_aborts; -}; - - #endif diff --git a/target/mips/cpu.h b/target/mips/cpu.h index 12cc1bfafd..52f13f0363 100644 --- a/target/mips/cpu.h +++ b/target/mips/cpu.h @@ -1217,6 +1217,23 @@ struct ArchCPU { Clock *count_div; /* Divider for CP0_Count clock */ }; +/** + * MIPSCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_phases: The parent class' reset phase handlers. + * + * A MIPS CPU model. + */ +struct MIPSCPUClass { + CPUClass parent_class; + + DeviceRealize parent_realize; + ResettablePhases parent_phases; + const struct mips_def_t *cpu_def; + + /* Used for the jazz board to modify mips_cpu_do_transaction_failed. */ + bool no_data_aborts; +}; void mips_cpu_list(void); diff --git a/target/nios2/cpu-qom.h b/target/nios2/cpu-qom.h index 931bc69b10..2fd9121540 100644 --- a/target/nios2/cpu-qom.h +++ b/target/nios2/cpu-qom.h @@ -10,7 +10,6 @@ #define QEMU_NIOS2_CPU_QOM_H #include "hw/core/cpu.h" -#include "qom/object.h" #define TYPE_NIOS2_CPU "nios2-cpu" diff --git a/target/openrisc/cpu-qom.h b/target/openrisc/cpu-qom.h index 1ba9fb0a4c..14bac33312 100644 --- a/target/openrisc/cpu-qom.h +++ b/target/openrisc/cpu-qom.h @@ -10,7 +10,6 @@ #define QEMU_OPENRISC_CPU_QOM_H #include "hw/core/cpu.h" -#include "qom/object.h" #define TYPE_OPENRISC_CPU "or1k-cpu" diff --git a/target/riscv/cpu-qom.h b/target/riscv/cpu-qom.h index 76efb614a6..91b3361dec 100644 --- a/target/riscv/cpu-qom.h +++ b/target/riscv/cpu-qom.h @@ -20,7 +20,6 @@ #define RISCV_CPU_QOM_H #include "hw/core/cpu.h" -#include "qom/object.h" #define TYPE_RISCV_CPU "riscv-cpu" #define TYPE_RISCV_DYNAMIC_CPU "riscv-dynamic-cpu" @@ -44,21 +43,6 @@ #define TYPE_RISCV_CPU_VEYRON_V1 RISCV_CPU_TYPE_NAME("veyron-v1") #define TYPE_RISCV_CPU_HOST RISCV_CPU_TYPE_NAME("host") -typedef struct CPUArchState CPURISCVState; - OBJECT_DECLARE_CPU_TYPE(RISCVCPU, RISCVCPUClass, RISCV_CPU) -/** - * RISCVCPUClass: - * @parent_realize: The parent class' realize handler. - * @parent_phases: The parent class' reset phase handlers. - * - * A RISCV CPU model. - */ -struct RISCVCPUClass { - CPUClass parent_class; - - DeviceRealize parent_realize; - ResettablePhases parent_phases; -}; #endif /* RISCV_CPU_QOM_H */ diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index b49fa17e68..bf58b0f0b5 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -32,6 +32,8 @@ #include "qapi/qapi-types-common.h" #include "cpu-qom.h" +typedef struct CPUArchState CPURISCVState; + #define CPU_RESOLVING_TYPE TYPE_RISCV_CPU #if defined(TARGET_RISCV32) @@ -436,6 +438,20 @@ struct ArchCPU { GHashTable *pmu_event_ctr_map; }; +/** + * RISCVCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_phases: The parent class' reset phase handlers. + * + * A RISCV CPU model. + */ +struct RISCVCPUClass { + CPUClass parent_class; + + DeviceRealize parent_realize; + ResettablePhases parent_phases; +}; + static inline int riscv_has_ext(CPURISCVState *env, target_ulong ext) { return (env->misa_ext & ext) != 0; diff --git a/target/rx/cpu-qom.h b/target/rx/cpu-qom.h index 6213d877f7..ac2e5785ef 100644 --- a/target/rx/cpu-qom.h +++ b/target/rx/cpu-qom.h @@ -20,7 +20,6 @@ #define RX_CPU_QOM_H #include "hw/core/cpu.h" -#include "qom/object.h" #define TYPE_RX_CPU "rx-cpu" @@ -31,18 +30,4 @@ OBJECT_DECLARE_CPU_TYPE(RXCPU, RXCPUClass, RX_CPU) #define RX_CPU_TYPE_SUFFIX "-" TYPE_RX_CPU #define RX_CPU_TYPE_NAME(model) model RX_CPU_TYPE_SUFFIX -/* - * RXCPUClass: - * @parent_realize: The parent class' realize handler. - * @parent_phases: The parent class' reset phase handlers. - * - * A RX CPU model. - */ -struct RXCPUClass { - CPUClass parent_class; - - DeviceRealize parent_realize; - ResettablePhases parent_phases; -}; - #endif diff --git a/target/rx/cpu.h b/target/rx/cpu.h index c81613770c..e931e77e85 100644 --- a/target/rx/cpu.h +++ b/target/rx/cpu.h @@ -112,6 +112,20 @@ struct ArchCPU { CPURXState env; }; +/* + * RXCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_phases: The parent class' reset phase handlers. + * + * A RX CPU model. + */ +struct RXCPUClass { + CPUClass parent_class; + + DeviceRealize parent_realize; + ResettablePhases parent_phases; +}; + #define CPU_RESOLVING_TYPE TYPE_RX_CPU const char *rx_crname(uint8_t cr); diff --git a/target/s390x/cpu-qom.h b/target/s390x/cpu-qom.h index ccf126b7a9..c59bb1eab1 100644 --- a/target/s390x/cpu-qom.h +++ b/target/s390x/cpu-qom.h @@ -21,7 +21,6 @@ #define QEMU_S390_CPU_QOM_H #include "hw/core/cpu.h" -#include "qom/object.h" #define TYPE_S390_CPU "s390x-cpu" @@ -30,38 +29,4 @@ OBJECT_DECLARE_CPU_TYPE(S390CPU, S390CPUClass, S390_CPU) #define S390_CPU_TYPE_SUFFIX "-" TYPE_S390_CPU #define S390_CPU_TYPE_NAME(name) (name S390_CPU_TYPE_SUFFIX) -typedef struct S390CPUModel S390CPUModel; -typedef struct S390CPUDef S390CPUDef; - -typedef enum cpu_reset_type { - S390_CPU_RESET_NORMAL, - S390_CPU_RESET_INITIAL, - S390_CPU_RESET_CLEAR, -} cpu_reset_type; - -/** - * S390CPUClass: - * @parent_realize: The parent class' realize handler. - * @parent_reset: The parent class' reset handler. - * @load_normal: Performs a load normal. - * @cpu_reset: Performs a CPU reset. - * @initial_cpu_reset: Performs an initial CPU reset. - * - * An S/390 CPU model. - */ -struct S390CPUClass { - CPUClass parent_class; - - const S390CPUDef *cpu_def; - bool kvm_required; - bool is_static; - bool is_migration_safe; - const char *desc; - - DeviceRealize parent_realize; - DeviceReset parent_reset; - void (*load_normal)(CPUState *cpu); - void (*reset)(CPUState *cpu, cpu_reset_type type); -}; - #endif diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index 942589c597..fa3aac4f97 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -180,6 +180,36 @@ struct ArchCPU { uint32_t irqstate_saved_size; }; +typedef enum cpu_reset_type { + S390_CPU_RESET_NORMAL, + S390_CPU_RESET_INITIAL, + S390_CPU_RESET_CLEAR, +} cpu_reset_type; + +/** + * S390CPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_reset: The parent class' reset handler. + * @load_normal: Performs a load normal. + * @cpu_reset: Performs a CPU reset. + * @initial_cpu_reset: Performs an initial CPU reset. + * + * An S/390 CPU model. + */ +struct S390CPUClass { + CPUClass parent_class; + + const S390CPUDef *cpu_def; + bool kvm_required; + bool is_static; + bool is_migration_safe; + const char *desc; + + DeviceRealize parent_realize; + DeviceReset parent_reset; + void (*load_normal)(CPUState *cpu); + void (*reset)(CPUState *cpu, cpu_reset_type type); +}; #ifndef CONFIG_USER_ONLY extern const VMStateDescription vmstate_s390_cpu; diff --git a/target/s390x/cpu_models.h b/target/s390x/cpu_models.h index cc7305ec21..d7b8912989 100644 --- a/target/s390x/cpu_models.h +++ b/target/s390x/cpu_models.h @@ -18,7 +18,7 @@ #include "hw/core/cpu.h" /* static CPU definition */ -struct S390CPUDef { +typedef struct S390CPUDef { const char *name; /* name exposed to the user */ const char *desc; /* description exposed to the user */ uint8_t gen; /* hw generation identification */ @@ -38,10 +38,10 @@ struct S390CPUDef { S390FeatBitmap full_feat; /* used to init full_feat from generated data */ S390FeatInit full_init; -}; +} S390CPUDef; /* CPU model based on a CPU definition */ -struct S390CPUModel { +typedef struct S390CPUModel { const S390CPUDef *def; S390FeatBitmap features; /* values copied from the "host" model, can change during migration */ @@ -49,7 +49,7 @@ struct S390CPUModel { uint32_t cpu_id; /* CPU id */ uint8_t cpu_id_format; /* CPU id format bit */ uint8_t cpu_ver; /* CPU version, usually "ff" for kvm */ -}; +} S390CPUModel; /* * CPU ID diff --git a/target/sh4/cpu-qom.h b/target/sh4/cpu-qom.h index bd0ef49fa1..6cf5fbb074 100644 --- a/target/sh4/cpu-qom.h +++ b/target/sh4/cpu-qom.h @@ -21,7 +21,6 @@ #define QEMU_SUPERH_CPU_QOM_H #include "hw/core/cpu.h" -#include "qom/object.h" #define TYPE_SUPERH_CPU "superh-cpu" @@ -34,26 +33,4 @@ OBJECT_DECLARE_CPU_TYPE(SuperHCPU, SuperHCPUClass, SUPERH_CPU) #define SUPERH_CPU_TYPE_SUFFIX "-" TYPE_SUPERH_CPU #define SUPERH_CPU_TYPE_NAME(model) model SUPERH_CPU_TYPE_SUFFIX -/** - * SuperHCPUClass: - * @parent_realize: The parent class' realize handler. - * @parent_phases: The parent class' reset phase handlers. - * @pvr: Processor Version Register - * @prr: Processor Revision Register - * @cvr: Cache Version Register - * - * A SuperH CPU model. - */ -struct SuperHCPUClass { - CPUClass parent_class; - - DeviceRealize parent_realize; - ResettablePhases parent_phases; - - uint32_t pvr; - uint32_t prr; - uint32_t cvr; -}; - - #endif diff --git a/target/sh4/cpu.h b/target/sh4/cpu.h index dbe00e29c2..360eac1fbe 100644 --- a/target/sh4/cpu.h +++ b/target/sh4/cpu.h @@ -209,6 +209,26 @@ struct ArchCPU { CPUSH4State env; }; +/** + * SuperHCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_phases: The parent class' reset phase handlers. + * @pvr: Processor Version Register + * @prr: Processor Revision Register + * @cvr: Cache Version Register + * + * A SuperH CPU model. + */ +struct SuperHCPUClass { + CPUClass parent_class; + + DeviceRealize parent_realize; + ResettablePhases parent_phases; + + uint32_t pvr; + uint32_t prr; + uint32_t cvr; +}; void superh_cpu_dump_state(CPUState *cpu, FILE *f, int flags); int superh_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); diff --git a/target/sparc/cpu-qom.h b/target/sparc/cpu-qom.h index aca29415b4..a86331bd58 100644 --- a/target/sparc/cpu-qom.h +++ b/target/sparc/cpu-qom.h @@ -21,7 +21,6 @@ #define QEMU_SPARC_CPU_QOM_H #include "hw/core/cpu.h" -#include "qom/object.h" #ifdef TARGET_SPARC64 #define TYPE_SPARC_CPU "sparc64-cpu" @@ -34,21 +33,4 @@ OBJECT_DECLARE_CPU_TYPE(SPARCCPU, SPARCCPUClass, SPARC_CPU) #define SPARC_CPU_TYPE_SUFFIX "-" TYPE_SPARC_CPU #define SPARC_CPU_TYPE_NAME(model) model SPARC_CPU_TYPE_SUFFIX -typedef struct sparc_def_t sparc_def_t; -/** - * SPARCCPUClass: - * @parent_realize: The parent class' realize handler. - * @parent_phases: The parent class' reset phase handlers. - * - * A SPARC CPU model. - */ -struct SPARCCPUClass { - CPUClass parent_class; - - DeviceRealize parent_realize; - ResettablePhases parent_phases; - sparc_def_t *cpu_def; -}; - - #endif diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index a808f2aff6..6999a10a40 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -249,7 +249,7 @@ typedef struct trap_state { #endif #define TARGET_INSN_START_EXTRA_WORDS 1 -struct sparc_def_t { +typedef struct sparc_def_t { const char *name; target_ulong iu_version; uint32_t fpu_version; @@ -263,7 +263,7 @@ struct sparc_def_t { uint32_t features; uint32_t nwindows; uint32_t maxtl; -}; +} sparc_def_t; #define FEATURE(X) CPU_FEATURE_BIT_##X, enum { @@ -567,6 +567,20 @@ struct ArchCPU { CPUSPARCState env; }; +/** + * SPARCCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_phases: The parent class' reset phase handlers. + * + * A SPARC CPU model. + */ +struct SPARCCPUClass { + CPUClass parent_class; + + DeviceRealize parent_realize; + ResettablePhases parent_phases; + sparc_def_t *cpu_def; +}; #ifndef CONFIG_USER_ONLY extern const VMStateDescription vmstate_sparc_cpu; diff --git a/target/tricore/cpu-qom.h b/target/tricore/cpu-qom.h index 2598651008..e35dc1ad2d 100644 --- a/target/tricore/cpu-qom.h +++ b/target/tricore/cpu-qom.h @@ -21,8 +21,6 @@ #define QEMU_TRICORE_CPU_QOM_H #include "hw/core/cpu.h" -#include "qom/object.h" - #define TYPE_TRICORE_CPU "tricore-cpu" @@ -31,12 +29,4 @@ OBJECT_DECLARE_CPU_TYPE(TriCoreCPU, TriCoreCPUClass, TRICORE_CPU) #define TRICORE_CPU_TYPE_SUFFIX "-" TYPE_TRICORE_CPU #define TRICORE_CPU_TYPE_NAME(model) model TRICORE_CPU_TYPE_SUFFIX -struct TriCoreCPUClass { - CPUClass parent_class; - - DeviceRealize parent_realize; - ResettablePhases parent_phases; -}; - - #endif /* QEMU_TRICORE_CPU_QOM_H */ diff --git a/target/tricore/cpu.h b/target/tricore/cpu.h index c537a33ee8..de3ab53a83 100644 --- a/target/tricore/cpu.h +++ b/target/tricore/cpu.h @@ -68,6 +68,12 @@ struct ArchCPU { CPUTriCoreState env; }; +struct TriCoreCPUClass { + CPUClass parent_class; + + DeviceRealize parent_realize; + ResettablePhases parent_phases; +}; hwaddr tricore_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); void tricore_cpu_dump_state(CPUState *cpu, FILE *f, int flags); diff --git a/target/xtensa/cpu-qom.h b/target/xtensa/cpu-qom.h index 03873ea50b..d932346b5f 100644 --- a/target/xtensa/cpu-qom.h +++ b/target/xtensa/cpu-qom.h @@ -30,7 +30,6 @@ #define QEMU_XTENSA_CPU_QOM_H #include "hw/core/cpu.h" -#include "qom/object.h" #define TYPE_XTENSA_CPU "xtensa-cpu" @@ -39,24 +38,4 @@ OBJECT_DECLARE_CPU_TYPE(XtensaCPU, XtensaCPUClass, XTENSA_CPU) #define XTENSA_CPU_TYPE_SUFFIX "-" TYPE_XTENSA_CPU #define XTENSA_CPU_TYPE_NAME(model) model XTENSA_CPU_TYPE_SUFFIX -typedef struct XtensaConfig XtensaConfig; - -/** - * XtensaCPUClass: - * @parent_realize: The parent class' realize handler. - * @parent_phases: The parent class' reset phase handlers. - * @config: The CPU core configuration. - * - * An Xtensa CPU model. - */ -struct XtensaCPUClass { - CPUClass parent_class; - - DeviceRealize parent_realize; - ResettablePhases parent_phases; - - const XtensaConfig *config; -}; - - #endif diff --git a/target/xtensa/cpu.h b/target/xtensa/cpu.h index d6d2fb1f4e..dd81729306 100644 --- a/target/xtensa/cpu.h +++ b/target/xtensa/cpu.h @@ -426,7 +426,7 @@ extern const XtensaOpcodeTranslators xtensa_core_opcodes; extern const XtensaOpcodeTranslators xtensa_fpu2000_opcodes; extern const XtensaOpcodeTranslators xtensa_fpu_opcodes; -struct XtensaConfig { +typedef struct XtensaConfig { const char *name; uint64_t options; XtensaGdbRegmap gdb_regmap; @@ -489,7 +489,7 @@ struct XtensaConfig { const xtensa_mpu_entry *mpu_bg; bool use_first_nan; -}; +} XtensaConfig; typedef struct XtensaConfigList { const XtensaConfig *config; @@ -562,6 +562,22 @@ struct ArchCPU { Clock *clock; }; +/** + * XtensaCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_phases: The parent class' reset phase handlers. + * @config: The CPU core configuration. + * + * An Xtensa CPU model. + */ +struct XtensaCPUClass { + CPUClass parent_class; + + DeviceRealize parent_realize; + ResettablePhases parent_phases; + + const XtensaConfig *config; +}; #ifndef CONFIG_USER_ONLY bool xtensa_cpu_tlb_fill(CPUState *cs, vaddr address, int size, From 55f2cd77376c6f2187ff386ab3b330ef260eedb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 8 Sep 2023 11:29:17 +0200 Subject: [PATCH 813/974] target/alpha: Tidy up alpha_cpu_class_by_name() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Gavin Shan Message-Id: <20230908112235.75914-2-philmd@linaro.org> --- target/alpha/cpu.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index 51b7d8d1bf..fae2cb6ec7 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -142,13 +142,10 @@ static ObjectClass *alpha_cpu_class_by_name(const char *cpu_model) typename = g_strdup_printf(ALPHA_CPU_TYPE_NAME("%s"), cpu_model); oc = object_class_by_name(typename); g_free(typename); - if (oc != NULL && object_class_is_abstract(oc)) { - oc = NULL; - } /* TODO: remove match everything nonsense */ - /* Default to ev67; no reason not to emulate insns by default. */ - if (!oc) { + if (!oc || object_class_is_abstract(oc)) { + /* Default to ev67; no reason not to emulate insns by default. */ oc = object_class_by_name(ALPHA_CPU_TYPE_NAME("ev67")); } From 3a9d0d7b64b72144369f48ef12ef0ed69d633fd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 8 Sep 2023 10:09:23 +0200 Subject: [PATCH 814/974] hw/cpu: Call object_class_is_abstract() once in cpu_class_by_name() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let CPUClass::class_by_name() handlers to return abstract classes, and filter them once in the public cpu_class_by_name() method. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230908112235.75914-3-philmd@linaro.org> --- hw/core/cpu-common.c | 14 +++++++++++--- include/hw/core/cpu.h | 7 ++++--- target/alpha/cpu.c | 3 +-- target/arm/cpu.c | 3 +-- target/avr/cpu.c | 3 +-- target/cris/cpu.c | 3 +-- target/hexagon/cpu.c | 3 +-- target/loongarch/cpu.c | 3 +-- target/m68k/cpu.c | 3 +-- target/openrisc/cpu.c | 3 +-- target/riscv/cpu.c | 3 +-- target/rx/cpu.c | 6 +----- target/sh4/cpu.c | 3 --- target/tricore/cpu.c | 3 +-- target/xtensa/cpu.c | 3 +-- 15 files changed, 27 insertions(+), 36 deletions(-) diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index baa6d28b64..d4112b8919 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -146,10 +146,18 @@ static bool cpu_common_has_work(CPUState *cs) ObjectClass *cpu_class_by_name(const char *typename, const char *cpu_model) { - CPUClass *cc = CPU_CLASS(object_class_by_name(typename)); + ObjectClass *oc; + CPUClass *cc; - assert(cpu_model && cc->class_by_name); - return cc->class_by_name(cpu_model); + oc = object_class_by_name(typename); + cc = CPU_CLASS(oc); + assert(cc->class_by_name); + assert(cpu_model); + oc = cc->class_by_name(cpu_model); + if (oc == NULL || object_class_is_abstract(oc)) { + return NULL; + } + return oc; } static void cpu_common_parse_features(const char *typename, char *features, diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 6373aa4501..5d6f8dca43 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -102,7 +102,7 @@ struct SysemuCPUOps; /** * CPUClass: * @class_by_name: Callback to map -cpu command line model name to an - * instantiatable CPU type. + * instantiatable CPU type. * @parse_features: Callback to parse command line arguments. * @reset_dump_flags: #CPUDumpFlags to use for reset logging. * @has_work: Callback for checking if there is work to do. @@ -772,9 +772,10 @@ void cpu_reset(CPUState *cpu); * @typename: The CPU base type. * @cpu_model: The model string without any parameters. * - * Looks up a CPU #ObjectClass matching name @cpu_model. + * Looks up a concrete CPU #ObjectClass matching name @cpu_model. * - * Returns: A #CPUClass or %NULL if not matching class is found. + * Returns: A concrete #CPUClass or %NULL if no matching class is found + * or if the matching class is abstract. */ ObjectClass *cpu_class_by_name(const char *typename, const char *cpu_model); diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index fae2cb6ec7..39cf841b3e 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -126,8 +126,7 @@ static ObjectClass *alpha_cpu_class_by_name(const char *cpu_model) int i; oc = object_class_by_name(cpu_model); - if (oc != NULL && object_class_dynamic_cast(oc, TYPE_ALPHA_CPU) != NULL && - !object_class_is_abstract(oc)) { + if (oc != NULL && object_class_dynamic_cast(oc, TYPE_ALPHA_CPU) != NULL) { return oc; } diff --git a/target/arm/cpu.c b/target/arm/cpu.c index df6496b019..25e9d2ae7b 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2401,8 +2401,7 @@ static ObjectClass *arm_cpu_class_by_name(const char *cpu_model) oc = object_class_by_name(typename); g_strfreev(cpuname); g_free(typename); - if (!oc || !object_class_dynamic_cast(oc, TYPE_ARM_CPU) || - object_class_is_abstract(oc)) { + if (!oc || !object_class_dynamic_cast(oc, TYPE_ARM_CPU)) { return NULL; } return oc; diff --git a/target/avr/cpu.c b/target/avr/cpu.c index 14d8b9d1f0..44de1e18d1 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -157,8 +157,7 @@ static ObjectClass *avr_cpu_class_by_name(const char *cpu_model) ObjectClass *oc; oc = object_class_by_name(cpu_model); - if (object_class_dynamic_cast(oc, TYPE_AVR_CPU) == NULL || - object_class_is_abstract(oc)) { + if (object_class_dynamic_cast(oc, TYPE_AVR_CPU) == NULL) { oc = NULL; } return oc; diff --git a/target/cris/cpu.c b/target/cris/cpu.c index be4a44c218..675b73ac04 100644 --- a/target/cris/cpu.c +++ b/target/cris/cpu.c @@ -95,8 +95,7 @@ static ObjectClass *cris_cpu_class_by_name(const char *cpu_model) typename = g_strdup_printf(CRIS_CPU_TYPE_NAME("%s"), cpu_model); oc = object_class_by_name(typename); g_free(typename); - if (oc != NULL && (!object_class_dynamic_cast(oc, TYPE_CRIS_CPU) || - object_class_is_abstract(oc))) { + if (oc != NULL && !object_class_dynamic_cast(oc, TYPE_CRIS_CPU)) { oc = NULL; } return oc; diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index 1adc11b713..9d1ffc3b4b 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -63,8 +63,7 @@ static ObjectClass *hexagon_cpu_class_by_name(const char *cpu_model) oc = object_class_by_name(typename); g_strfreev(cpuname); g_free(typename); - if (!oc || !object_class_dynamic_cast(oc, TYPE_HEXAGON_CPU) || - object_class_is_abstract(oc)) { + if (!oc || !object_class_dynamic_cast(oc, TYPE_HEXAGON_CPU)) { return NULL; } return oc; diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index a60d07acd5..fc075952e6 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -721,8 +721,7 @@ static ObjectClass *loongarch_cpu_class_by_name(const char *cpu_model) } } - if (object_class_dynamic_cast(oc, TYPE_LOONGARCH_CPU) - && !object_class_is_abstract(oc)) { + if (object_class_dynamic_cast(oc, TYPE_LOONGARCH_CPU)) { return oc; } return NULL; diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index 538d9473c2..11c7e0a790 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -111,8 +111,7 @@ static ObjectClass *m68k_cpu_class_by_name(const char *cpu_model) typename = g_strdup_printf(M68K_CPU_TYPE_NAME("%s"), cpu_model); oc = object_class_by_name(typename); g_free(typename); - if (oc != NULL && (object_class_dynamic_cast(oc, TYPE_M68K_CPU) == NULL || - object_class_is_abstract(oc))) { + if (oc != NULL && object_class_dynamic_cast(oc, TYPE_M68K_CPU) == NULL) { return NULL; } return oc; diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index f5a3d5273b..1173260017 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -164,8 +164,7 @@ static ObjectClass *openrisc_cpu_class_by_name(const char *cpu_model) typename = g_strdup_printf(OPENRISC_CPU_TYPE_NAME("%s"), cpu_model); oc = object_class_by_name(typename); g_free(typename); - if (oc != NULL && (!object_class_dynamic_cast(oc, TYPE_OPENRISC_CPU) || - object_class_is_abstract(oc))) { + if (oc != NULL && !object_class_dynamic_cast(oc, TYPE_OPENRISC_CPU)) { return NULL; } return oc; diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 02db2760d1..83c7c0cf07 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -646,8 +646,7 @@ static ObjectClass *riscv_cpu_class_by_name(const char *cpu_model) oc = object_class_by_name(typename); g_strfreev(cpuname); g_free(typename); - if (!oc || !object_class_dynamic_cast(oc, TYPE_RISCV_CPU) || - object_class_is_abstract(oc)) { + if (!oc || !object_class_dynamic_cast(oc, TYPE_RISCV_CPU)) { return NULL; } return oc; diff --git a/target/rx/cpu.c b/target/rx/cpu.c index 4d0d3a0c8c..9cc9d9d15e 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -111,16 +111,12 @@ static ObjectClass *rx_cpu_class_by_name(const char *cpu_model) char *typename; oc = object_class_by_name(cpu_model); - if (oc != NULL && object_class_dynamic_cast(oc, TYPE_RX_CPU) != NULL && - !object_class_is_abstract(oc)) { + if (oc != NULL && object_class_dynamic_cast(oc, TYPE_RX_CPU) != NULL) { return oc; } typename = g_strdup_printf(RX_CPU_TYPE_NAME("%s"), cpu_model); oc = object_class_by_name(typename); g_free(typename); - if (oc != NULL && object_class_is_abstract(oc)) { - oc = NULL; - } return oc; } diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index 788e41fea6..a8ec98b134 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -152,9 +152,6 @@ static ObjectClass *superh_cpu_class_by_name(const char *cpu_model) typename = g_strdup_printf(SUPERH_CPU_TYPE_NAME("%s"), s); oc = object_class_by_name(typename); - if (oc != NULL && object_class_is_abstract(oc)) { - oc = NULL; - } out: g_free(s); diff --git a/target/tricore/cpu.c b/target/tricore/cpu.c index 5ca666ee12..034e01c189 100644 --- a/target/tricore/cpu.c +++ b/target/tricore/cpu.c @@ -132,8 +132,7 @@ static ObjectClass *tricore_cpu_class_by_name(const char *cpu_model) typename = g_strdup_printf(TRICORE_CPU_TYPE_NAME("%s"), cpu_model); oc = object_class_by_name(typename); g_free(typename); - if (!oc || !object_class_dynamic_cast(oc, TYPE_TRICORE_CPU) || - object_class_is_abstract(oc)) { + if (!oc || !object_class_dynamic_cast(oc, TYPE_TRICORE_CPU)) { return NULL; } return oc; diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index ea1dae7390..e20fe87bf2 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -141,8 +141,7 @@ static ObjectClass *xtensa_cpu_class_by_name(const char *cpu_model) typename = g_strdup_printf(XTENSA_CPU_TYPE_NAME("%s"), cpu_model); oc = object_class_by_name(typename); g_free(typename); - if (oc == NULL || !object_class_dynamic_cast(oc, TYPE_XTENSA_CPU) || - object_class_is_abstract(oc)) { + if (oc == NULL || !object_class_dynamic_cast(oc, TYPE_XTENSA_CPU)) { return NULL; } return oc; From 79a99091c1af7783a13c21ab5e4049265fcbb1d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 15 Sep 2023 16:26:52 +0200 Subject: [PATCH 815/974] exec/cpu: Have cpu_exec_realize() return a boolean MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Following the example documented since commit e3fe3988d7 ("error: Document Error API usage rules"), have cpu_exec_realizefn() return a boolean indicating whether an error is set or not. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230918160257.30127-22-philmd@linaro.org> --- cpu-target.c | 6 ++++-- include/hw/core/cpu.h | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/cpu-target.c b/cpu-target.c index 79363ae370..f3e1ad8bcd 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -131,13 +131,13 @@ const VMStateDescription vmstate_cpu_common = { }; #endif -void cpu_exec_realizefn(CPUState *cpu, Error **errp) +bool cpu_exec_realizefn(CPUState *cpu, Error **errp) { /* cache the cpu class for the hotpath */ cpu->cc = CPU_GET_CLASS(cpu); if (!accel_cpu_common_realize(cpu, errp)) { - return; + return false; } /* Wait until cpu initialization complete before exposing cpu. */ @@ -159,6 +159,8 @@ void cpu_exec_realizefn(CPUState *cpu, Error **errp) vmstate_register(NULL, cpu->cpu_index, cpu->cc->sysemu_ops->legacy_vmsd, cpu); } #endif /* CONFIG_USER_ONLY */ + + return true; } void cpu_exec_unrealizefn(CPUState *cpu) diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 5d6f8dca43..eb943efb8f 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -1152,7 +1152,7 @@ G_NORETURN void cpu_abort(CPUState *cpu, const char *fmt, ...) /* $(top_srcdir)/cpu.c */ void cpu_class_init_props(DeviceClass *dc); void cpu_exec_initfn(CPUState *cpu); -void cpu_exec_realizefn(CPUState *cpu, Error **errp); +bool cpu_exec_realizefn(CPUState *cpu, Error **errp); void cpu_exec_unrealizefn(CPUState *cpu); void cpu_exec_reset_hold(CPUState *cpu); From 3c55dd5896d687b5d5e61e8eec07b8bb2931713a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 9 Oct 2023 09:02:04 +0200 Subject: [PATCH 816/974] hw/cpu: Clean up global variable shadowing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix: hw/core/machine.c:1302:22: error: declaration shadows a variable in the global scope [-Werror,-Wshadow] const CPUArchId *cpus = possible_cpus->cpus; ^ hw/core/numa.c:69:17: error: declaration shadows a variable in the global scope [-Werror,-Wshadow] uint16List *cpus = NULL; ^ hw/acpi/aml-build.c:2005:20: error: declaration shadows a variable in the global scope [-Werror,-Wshadow] CPUArchIdList *cpus = ms->possible_cpus; ^ hw/core/machine-smp.c:77:14: error: declaration shadows a variable in the global scope [-Werror,-Wshadow] unsigned cpus = config->has_cpus ? config->cpus : 0; ^ include/hw/core/cpu.h:589:17: note: previous declaration is here extern CPUTailQ cpus; ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Ani Sinha Message-Id: <20231010115048.11856-2-philmd@linaro.org> --- bsd-user/main.c | 2 +- cpu-common.c | 6 +++--- include/hw/core/cpu.h | 8 ++++---- linux-user/main.c | 2 +- target/s390x/cpu_models.c | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/bsd-user/main.c b/bsd-user/main.c index c402fadf46..e6014f517e 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -118,7 +118,7 @@ void fork_end(int child) */ CPU_FOREACH_SAFE(cpu, next_cpu) { if (cpu != thread_cpu) { - QTAILQ_REMOVE_RCU(&cpus, cpu, node); + QTAILQ_REMOVE_RCU(&cpus_queue, cpu, node); } } mmap_fork_end(child); diff --git a/cpu-common.c b/cpu-common.c index 45c745ecf6..c81fd72d16 100644 --- a/cpu-common.c +++ b/cpu-common.c @@ -73,7 +73,7 @@ static int cpu_get_free_index(void) return max_cpu_index; } -CPUTailQ cpus = QTAILQ_HEAD_INITIALIZER(cpus); +CPUTailQ cpus_queue = QTAILQ_HEAD_INITIALIZER(cpus_queue); static unsigned int cpu_list_generation_id; unsigned int cpu_list_generation_id_get(void) @@ -90,7 +90,7 @@ void cpu_list_add(CPUState *cpu) } else { assert(!cpu_index_auto_assigned); } - QTAILQ_INSERT_TAIL_RCU(&cpus, cpu, node); + QTAILQ_INSERT_TAIL_RCU(&cpus_queue, cpu, node); cpu_list_generation_id++; } @@ -102,7 +102,7 @@ void cpu_list_remove(CPUState *cpu) return; } - QTAILQ_REMOVE_RCU(&cpus, cpu, node); + QTAILQ_REMOVE_RCU(&cpus_queue, cpu, node); cpu->cpu_index = UNASSIGNED_CPU_INDEX; cpu_list_generation_id++; } diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index eb943efb8f..77893d7b81 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -586,13 +586,13 @@ static inline CPUArchState *cpu_env(CPUState *cpu) } typedef QTAILQ_HEAD(CPUTailQ, CPUState) CPUTailQ; -extern CPUTailQ cpus; +extern CPUTailQ cpus_queue; -#define first_cpu QTAILQ_FIRST_RCU(&cpus) +#define first_cpu QTAILQ_FIRST_RCU(&cpus_queue) #define CPU_NEXT(cpu) QTAILQ_NEXT_RCU(cpu, node) -#define CPU_FOREACH(cpu) QTAILQ_FOREACH_RCU(cpu, &cpus, node) +#define CPU_FOREACH(cpu) QTAILQ_FOREACH_RCU(cpu, &cpus_queue, node) #define CPU_FOREACH_SAFE(cpu, next_cpu) \ - QTAILQ_FOREACH_SAFE_RCU(cpu, &cpus, node, next_cpu) + QTAILQ_FOREACH_SAFE_RCU(cpu, &cpus_queue, node, next_cpu) extern __thread CPUState *current_cpu; diff --git a/linux-user/main.c b/linux-user/main.c index 0c23584a96..0cdaf30d34 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -156,7 +156,7 @@ void fork_end(int child) Discard information about the parent threads. */ CPU_FOREACH_SAFE(cpu, next_cpu) { if (cpu != thread_cpu) { - QTAILQ_REMOVE_RCU(&cpus, cpu, node); + QTAILQ_REMOVE_RCU(&cpus_queue, cpu, node); } } qemu_init_cpu_list(); diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index 4dead48650..5c455d00c0 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -757,7 +757,7 @@ void s390_set_qemu_cpu_model(uint16_t type, uint8_t gen, uint8_t ec_ga, const S390CPUDef *def = s390_find_cpu_def(type, gen, ec_ga, NULL); g_assert(def); - g_assert(QTAILQ_EMPTY_RCU(&cpus)); + g_assert(QTAILQ_EMPTY_RCU(&cpus_queue)); /* build the CPU model */ s390_qemu_cpu_model.def = def; From e265ee4379a62949133ce3b5d1b4b4b12a884944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 9 Oct 2023 09:50:21 +0200 Subject: [PATCH 817/974] hw/loader: Clean up global variable shadowing in rom_add_file() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix: hw/core/loader.c:1073:27: error: declaration shadows a variable in the global scope [-Werror,-Wshadow] bool option_rom, MemoryRegion *mr, ^ include/sysemu/sysemu.h:57:22: note: previous declaration is here extern QEMUOptionRom option_rom[MAX_OPTION_ROMS]; ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Ani Sinha Message-Id: <20231010115048.11856-3-philmd@linaro.org> --- hw/core/loader.c | 4 ++-- include/hw/loader.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/core/loader.c b/hw/core/loader.c index b7bb44b7f7..3c79283777 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -1070,7 +1070,7 @@ static void *rom_set_mr(Rom *rom, Object *owner, const char *name, bool ro) ssize_t rom_add_file(const char *file, const char *fw_dir, hwaddr addr, int32_t bootindex, - bool option_rom, MemoryRegion *mr, + bool has_option_rom, MemoryRegion *mr, AddressSpace *as) { MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); @@ -1139,7 +1139,7 @@ ssize_t rom_add_file(const char *file, const char *fw_dir, basename); snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name); - if ((!option_rom || mc->option_rom_has_mr) && mc->rom_file_has_mr) { + if ((!has_option_rom || mc->option_rom_has_mr) && mc->rom_file_has_mr) { data = rom_set_mr(rom, OBJECT(fw_cfg), devpath, true); } else { data = rom->data; diff --git a/include/hw/loader.h b/include/hw/loader.h index c4c14170ea..8685e27334 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -272,7 +272,7 @@ void pstrcpy_targphys(const char *name, ssize_t rom_add_file(const char *file, const char *fw_dir, hwaddr addr, int32_t bootindex, - bool option_rom, MemoryRegion *mr, AddressSpace *as); + bool has_option_rom, MemoryRegion *mr, AddressSpace *as); MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len, size_t max_len, hwaddr addr, const char *fw_file_name, From 2798ee63b03a8a67017c4fa5ef21fc85bfef67d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 20 Oct 2023 12:08:21 +0200 Subject: [PATCH 818/974] hw/isa/i82378: Propagate error if PC_SPEAKER device creation failed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit 40f8214fcd ("hw/audio/pcspk: Inline pcspk_init()") we neglected to give a change to the caller to handle failed device creation cleanly. Respect the caller API contract and propagate the error if creating the PC_SPEAKER device ever failed. This avoid yet another bad API use to be taken as example and copy / pasted all over the code base. Reported-by: Bernhard Beschow Suggested-by: Markus Armbruster Reviewed-by: Richard Henderson Reviewed-by: Bernhard Beschow Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20231020171509.87839-5-philmd@linaro.org> --- hw/isa/i82378.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/isa/i82378.c b/hw/isa/i82378.c index 79ffbb52a0..203b92c264 100644 --- a/hw/isa/i82378.c +++ b/hw/isa/i82378.c @@ -105,7 +105,9 @@ static void i82378_realize(PCIDevice *pci, Error **errp) /* speaker */ pcspk = isa_new(TYPE_PC_SPEAKER); object_property_set_link(OBJECT(pcspk), "pit", OBJECT(pit), &error_fatal); - isa_realize_and_unref(pcspk, isabus, &error_fatal); + if (!isa_realize_and_unref(pcspk, isabus, errp)) { + return; + } /* 2 82C37 (dma) */ isa_create_simple(isabus, "i82374"); From 5f0d69b5a69f56d63a1afd5f927919b1584e5e9b Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 24 Oct 2023 17:03:04 +0800 Subject: [PATCH 819/974] hw/i386: Fix comment style in topology.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For function comments in this file, keep the comment style consistent with other files in the directory. Signed-off-by: Zhao Liu Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Yanan Wang Reviewed-by: Xiaoyao Li Reviewed-by: Babu Moger Tested-by: Babu Moger Tested-by: Yongwei Ma Acked-by: Michael S. Tsirkin Message-ID: <20231024090323.1859210-2-zhao1.liu@linux.intel.com> Signed-off-by: Philippe Mathieu-Daudé --- include/hw/i386/topology.h | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/include/hw/i386/topology.h b/include/hw/i386/topology.h index 380cb27ded..d4eeb7ab82 100644 --- a/include/hw/i386/topology.h +++ b/include/hw/i386/topology.h @@ -24,7 +24,8 @@ #ifndef HW_I386_TOPOLOGY_H #define HW_I386_TOPOLOGY_H -/* This file implements the APIC-ID-based CPU topology enumeration logic, +/* + * This file implements the APIC-ID-based CPU topology enumeration logic, * documented at the following document: * Intel® 64 Architecture Processor Topology Enumeration * http://software.intel.com/en-us/articles/intel-64-architecture-processor-topology-enumeration/ @@ -41,7 +42,8 @@ #include "qemu/bitops.h" -/* APIC IDs can be 32-bit, but beware: APIC IDs > 255 require x2APIC support +/* + * APIC IDs can be 32-bit, but beware: APIC IDs > 255 require x2APIC support */ typedef uint32_t apic_id_t; @@ -58,8 +60,7 @@ typedef struct X86CPUTopoInfo { unsigned threads_per_core; } X86CPUTopoInfo; -/* Return the bit width needed for 'count' IDs - */ +/* Return the bit width needed for 'count' IDs */ static unsigned apicid_bitwidth_for_count(unsigned count) { g_assert(count >= 1); @@ -67,15 +68,13 @@ static unsigned apicid_bitwidth_for_count(unsigned count) return count ? 32 - clz32(count) : 0; } -/* Bit width of the SMT_ID (thread ID) field on the APIC ID - */ +/* Bit width of the SMT_ID (thread ID) field on the APIC ID */ static inline unsigned apicid_smt_width(X86CPUTopoInfo *topo_info) { return apicid_bitwidth_for_count(topo_info->threads_per_core); } -/* Bit width of the Core_ID field - */ +/* Bit width of the Core_ID field */ static inline unsigned apicid_core_width(X86CPUTopoInfo *topo_info) { return apicid_bitwidth_for_count(topo_info->cores_per_die); @@ -87,8 +86,7 @@ static inline unsigned apicid_die_width(X86CPUTopoInfo *topo_info) return apicid_bitwidth_for_count(topo_info->dies_per_pkg); } -/* Bit offset of the Core_ID field - */ +/* Bit offset of the Core_ID field */ static inline unsigned apicid_core_offset(X86CPUTopoInfo *topo_info) { return apicid_smt_width(topo_info); @@ -100,14 +98,14 @@ static inline unsigned apicid_die_offset(X86CPUTopoInfo *topo_info) return apicid_core_offset(topo_info) + apicid_core_width(topo_info); } -/* Bit offset of the Pkg_ID (socket ID) field - */ +/* Bit offset of the Pkg_ID (socket ID) field */ static inline unsigned apicid_pkg_offset(X86CPUTopoInfo *topo_info) { return apicid_die_offset(topo_info) + apicid_die_width(topo_info); } -/* Make APIC ID for the CPU based on Pkg_ID, Core_ID, SMT_ID +/* + * Make APIC ID for the CPU based on Pkg_ID, Core_ID, SMT_ID * * The caller must make sure core_id < nr_cores and smt_id < nr_threads. */ @@ -120,7 +118,8 @@ static inline apic_id_t x86_apicid_from_topo_ids(X86CPUTopoInfo *topo_info, topo_ids->smt_id; } -/* Calculate thread/core/package IDs for a specific topology, +/* + * Calculate thread/core/package IDs for a specific topology, * based on (contiguous) CPU index */ static inline void x86_topo_ids_from_idx(X86CPUTopoInfo *topo_info, @@ -137,7 +136,8 @@ static inline void x86_topo_ids_from_idx(X86CPUTopoInfo *topo_info, topo_ids->smt_id = cpu_index % nr_threads; } -/* Calculate thread/core/package IDs for a specific topology, +/* + * Calculate thread/core/package IDs for a specific topology, * based on APIC ID */ static inline void x86_topo_ids_from_apicid(apic_id_t apicid, @@ -155,7 +155,8 @@ static inline void x86_topo_ids_from_apicid(apic_id_t apicid, topo_ids->pkg_id = apicid >> apicid_pkg_offset(topo_info); } -/* Make APIC ID for the CPU 'cpu_index' +/* + * Make APIC ID for the CPU 'cpu_index' * * 'cpu_index' is a sequential, contiguous ID for the CPU. */ From af4c26e618368cbb474d3fb142b2a18b7a54c2e0 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 24 Oct 2023 17:03:05 +0800 Subject: [PATCH 820/974] tests/unit: Rename test-x86-cpuid.c to test-x86-topo.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tests in this file actually test the APIC ID combinations. Rename to test-x86-topo.c to make its name more in line with its actual content. Signed-off-by: Zhao Liu Reviewed-by: Philippe Mathieu-Daudé Tested-by: Babu Moger Tested-by: Yongwei Ma Acked-by: Michael S. Tsirkin Reviewed-by: Thomas Huth Message-ID: <20231024090323.1859210-3-zhao1.liu@linux.intel.com> Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 2 +- tests/unit/meson.build | 4 ++-- tests/unit/{test-x86-cpuid.c => test-x86-topo.c} | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) rename tests/unit/{test-x86-cpuid.c => test-x86-topo.c} (99%) diff --git a/MAINTAINERS b/MAINTAINERS index 59b92ee640..0e1d063d77 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1772,7 +1772,7 @@ F: include/hw/southbridge/ich9.h F: include/hw/southbridge/piix.h F: hw/isa/apm.c F: include/hw/isa/apm.h -F: tests/unit/test-x86-cpuid.c +F: tests/unit/test-x86-topo.c F: tests/qtest/test-x86-cpuid-compat.c PC Chipset diff --git a/tests/unit/meson.build b/tests/unit/meson.build index e6c51e7a86..a05d471090 100644 --- a/tests/unit/meson.build +++ b/tests/unit/meson.build @@ -22,8 +22,8 @@ tests = { 'test-visitor-serialization': [testqapi], 'test-bitmap': [], 'test-resv-mem': [], - # all code tested by test-x86-cpuid is inside topology.h - 'test-x86-cpuid': [], + # all code tested by test-x86-topo is inside topology.h + 'test-x86-topo': [], 'test-cutils': [], 'test-div128': [], 'test-shift128': [], diff --git a/tests/unit/test-x86-cpuid.c b/tests/unit/test-x86-topo.c similarity index 99% rename from tests/unit/test-x86-cpuid.c rename to tests/unit/test-x86-topo.c index bfabc0403a..2b104f86d7 100644 --- a/tests/unit/test-x86-cpuid.c +++ b/tests/unit/test-x86-topo.c @@ -1,5 +1,5 @@ /* - * Test code for x86 CPUID and Topology functions + * Test code for x86 APIC ID and Topology functions * * Copyright (c) 2012 Red Hat Inc. * From 958ac3c42b683ceee9660fecb707b8d4468348ab Mon Sep 17 00:00:00 2001 From: Zhuocheng Ding Date: Tue, 24 Oct 2023 17:03:06 +0800 Subject: [PATCH 821/974] system/cpus: Fix CPUState.nr_cores' calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From CPUState.nr_cores' comment, it represents "number of cores within this CPU package". After 003f230e37d7 ("machine: Tweak the order of topology members in struct CpuTopology"), the meaning of smp.cores changed to "the number of cores in one die", but this commit missed to change CPUState.nr_cores' calculation, so that CPUState.nr_cores became wrong and now it misses to consider numbers of clusters and dies. At present, only i386 is using CPUState.nr_cores. But as for i386, which supports die level, the uses of CPUState.nr_cores are very confusing: Early uses are based on the meaning of "cores per package" (before die is introduced into i386), and later uses are based on "cores per die" (after die's introduction). This difference is due to that commit a94e1428991f ("target/i386: Add CPUID.1F generation support for multi-dies PCMachine") misunderstood that CPUState.nr_cores means "cores per die" when calculated CPUID.1FH.01H:EBX. After that, the changes in i386 all followed this wrong understanding. With the influence of 003f230e37d7 and a94e1428991f, for i386 currently the result of CPUState.nr_cores is "cores per die", thus the original uses of CPUState.cores based on the meaning of "cores per package" are wrong when multiple dies exist: 1. In cpu_x86_cpuid() of target/i386/cpu.c, CPUID.01H:EBX[bits 23:16] is incorrect because it expects "cpus per package" but now the result is "cpus per die". 2. In cpu_x86_cpuid() of target/i386/cpu.c, for all leaves of CPUID.04H: EAX[bits 31:26] is incorrect because they expect "cpus per package" but now the result is "cpus per die". The error not only impacts the EAX calculation in cache_info_passthrough case, but also impacts other cases of setting cache topology for Intel CPU according to cpu topology (specifically, the incoming parameter "num_cores" expects "cores per package" in encode_cache_cpuid4()). 3. In cpu_x86_cpuid() of target/i386/cpu.c, CPUID.0BH.01H:EBX[bits 15:00] is incorrect because the EBX of 0BH.01H (core level) expects "cpus per package", which may be different with 1FH.01H (The reason is 1FH can support more levels. For QEMU, 1FH also supports die, 1FH.01H:EBX[bits 15:00] expects "cpus per die"). 4. In cpu_x86_cpuid() of target/i386/cpu.c, when CPUID.80000001H is calculated, here "cpus per package" is expected to be checked, but in fact, now it checks "cpus per die". Though "cpus per die" also works for this code logic, this isn't consistent with AMD's APM. 5. In cpu_x86_cpuid() of target/i386/cpu.c, CPUID.80000008H:ECX expects "cpus per package" but it obtains "cpus per die". 6. In simulate_rdmsr() of target/i386/hvf/x86_emu.c, in kvm_rdmsr_core_thread_count() of target/i386/kvm/kvm.c, and in helper_rdmsr() of target/i386/tcg/sysemu/misc_helper.c, MSR_CORE_THREAD_COUNT expects "cpus per package" and "cores per package", but in these functions, it obtains "cpus per die" and "cores per die". On the other hand, these uses are correct now (they are added in/after a94e1428991f): 1. In cpu_x86_cpuid() of target/i386/cpu.c, topo_info.cores_per_die meets the actual meaning of CPUState.nr_cores ("cores per die"). 2. In cpu_x86_cpuid() of target/i386/cpu.c, vcpus_per_socket (in CPUID. 04H's calculation) considers number of dies, so it's correct. 3. In cpu_x86_cpuid() of target/i386/cpu.c, CPUID.1FH.01H:EBX[bits 15:00] needs "cpus per die" and it gets the correct result, and CPUID.1FH.02H:EBX[bits 15:00] gets correct "cpus per package". When CPUState.nr_cores is correctly changed to "cores per package" again , the above errors will be fixed without extra work, but the "currently" correct cases will go wrong and need special handling to pass correct "cpus/cores per die" they want. Fix CPUState.nr_cores' calculation to fit the original meaning "cores per package", as well as changing calculation of topo_info.cores_per_die, vcpus_per_socket and CPUID.1FH. Fixes: a94e1428991f ("target/i386: Add CPUID.1F generation support for multi-dies PCMachine") Fixes: 003f230e37d7 ("machine: Tweak the order of topology members in struct CpuTopology") Signed-off-by: Zhuocheng Ding Co-developed-by: Zhao Liu Signed-off-by: Zhao Liu Tested-by: Babu Moger Tested-by: Yongwei Ma Acked-by: Michael S. Tsirkin Message-ID: <20231024090323.1859210-4-zhao1.liu@linux.intel.com> Signed-off-by: Philippe Mathieu-Daudé --- system/cpus.c | 2 +- target/i386/cpu.c | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/system/cpus.c b/system/cpus.c index 952f15868c..a444a747f0 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -631,7 +631,7 @@ void qemu_init_vcpu(CPUState *cpu) { MachineState *ms = MACHINE(qdev_get_machine()); - cpu->nr_cores = ms->smp.cores; + 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/target/i386/cpu.c b/target/i386/cpu.c index fc8484cb5e..358d9c0a65 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6019,7 +6019,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, X86CPUTopoInfo topo_info; topo_info.dies_per_pkg = env->nr_dies; - topo_info.cores_per_die = cs->nr_cores; + topo_info.cores_per_die = cs->nr_cores / env->nr_dies; topo_info.threads_per_core = cs->nr_threads; /* Calculate & apply limits for different index ranges */ @@ -6095,8 +6095,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, */ if (*eax & 31) { int host_vcpus_per_cache = 1 + ((*eax & 0x3FFC000) >> 14); - int vcpus_per_socket = env->nr_dies * cs->nr_cores * - cs->nr_threads; + int vcpus_per_socket = cs->nr_cores * cs->nr_threads; if (cs->nr_cores > 1) { *eax &= ~0xFC000000; *eax |= (pow2ceil(cs->nr_cores) - 1) << 26; @@ -6273,12 +6272,12 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; case 1: *eax = apicid_die_offset(&topo_info); - *ebx = cs->nr_cores * cs->nr_threads; + *ebx = topo_info.cores_per_die * topo_info.threads_per_core; *ecx |= CPUID_TOPOLOGY_LEVEL_CORE; break; case 2: *eax = apicid_pkg_offset(&topo_info); - *ebx = env->nr_dies * cs->nr_cores * cs->nr_threads; + *ebx = cs->nr_cores * cs->nr_threads; *ecx |= CPUID_TOPOLOGY_LEVEL_DIE; break; default: From aa1878fbc96aea70ba2a3a471111bec5352946fe Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 24 Oct 2023 17:03:07 +0800 Subject: [PATCH 822/974] hw/cpu: Update the comments of nr_cores and nr_dies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the nr_threads' comment, specify it represents the number of threads in the "core" to avoid confusion. Also add comment for nr_dies in CPUX86State. Signed-off-by: Zhao Liu Reviewed-by: Philippe Mathieu-Daudé Tested-by: Babu Moger Tested-by: Yongwei Ma Acked-by: Michael S. Tsirkin Message-ID: <20231024090323.1859210-5-zhao1.liu@linux.intel.com> Signed-off-by: Philippe Mathieu-Daudé --- include/hw/core/cpu.h | 2 +- target/i386/cpu.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 77893d7b81..c0c8320413 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -408,7 +408,7 @@ struct qemu_work_item; * 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. + * @nr_threads: Number of threads within this CPU core. * @running: #true if CPU is currently running (lockless). * @has_waiter: #true if a CPU is currently waiting for the cpu_exec_end; * valid under cpu_list_lock. diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 0028aeb0ef..cd2e295bd6 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1882,6 +1882,7 @@ typedef struct CPUArchState { TPRAccess tpr_access_type; + /* Number of dies within this CPU package. */ unsigned nr_dies; } CPUX86State; From 7d7512019fc40c577e2bdd61f114f31a9eb84a8e Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Wed, 6 Sep 2023 15:09:21 +0200 Subject: [PATCH 823/974] hw/ide: reset: cancel async DMA operation before resetting state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If there is a pending DMA operation during ide_bus_reset(), the fact that the IDEState is already reset before the operation is canceled can be problematic. In particular, ide_dma_cb() might be called and then use the reset IDEState which contains the signature after the reset. When used to construct the IO operation this leads to ide_get_sector() returning 0 and nsector being 1. This is particularly bad, because a write command will thus destroy the first sector which often contains a partition table or similar. Traces showing the unsolicited write happening with IDEState 0x5595af6949d0 being used after reset: > ahci_port_write ahci(0x5595af6923f0)[0]: port write [reg:PxSCTL] @ 0x2c: 0x00000300 > ahci_reset_port ahci(0x5595af6923f0)[0]: reset port > ide_reset IDEstate 0x5595af6949d0 > ide_reset IDEstate 0x5595af694da8 > ide_bus_reset_aio aio_cancel > dma_aio_cancel dbs=0x7f64600089a0 > dma_blk_cb dbs=0x7f64600089a0 ret=0 > dma_complete dbs=0x7f64600089a0 ret=0 cb=0x5595acd40b30 > ahci_populate_sglist ahci(0x5595af6923f0)[0] > ahci_dma_prepare_buf ahci(0x5595af6923f0)[0]: prepare buf limit=512 prepared=512 > ide_dma_cb IDEState 0x5595af6949d0; sector_num=0 n=1 cmd=DMA WRITE > dma_blk_io dbs=0x7f6420802010 bs=0x5595ae2c6c30 offset=0 to_dev=1 > dma_blk_cb dbs=0x7f6420802010 ret=0 > (gdb) p *qiov > $11 = {iov = 0x7f647c76d840, niov = 1, {{nalloc = 1, local_iov = {iov_base = 0x0, > iov_len = 512}}, {__pad = "\001\000\000\000\000\000\000\000\000\000\000", > size = 512}}} > (gdb) bt > #0 blk_aio_pwritev (blk=0x5595ae2c6c30, offset=0, qiov=0x7f6420802070, flags=0, > cb=0x5595ace6f0b0 , opaque=0x7f6420802010) > at ../block/block-backend.c:1682 > #1 0x00005595ace6f185 in dma_blk_cb (opaque=0x7f6420802010, ret=) > at ../softmmu/dma-helpers.c:179 > #2 0x00005595ace6f778 in dma_blk_io (ctx=0x5595ae0609f0, > sg=sg@entry=0x5595af694d00, offset=offset@entry=0, align=align@entry=512, > io_func=io_func@entry=0x5595ace6ee30 , > io_func_opaque=io_func_opaque@entry=0x5595ae2c6c30, > cb=0x5595acd40b30 , opaque=0x5595af6949d0, > dir=DMA_DIRECTION_TO_DEVICE) at ../softmmu/dma-helpers.c:244 > #3 0x00005595ace6f90a in dma_blk_write (blk=0x5595ae2c6c30, > sg=sg@entry=0x5595af694d00, offset=offset@entry=0, align=align@entry=512, > cb=cb@entry=0x5595acd40b30 , opaque=opaque@entry=0x5595af6949d0) > at ../softmmu/dma-helpers.c:280 > #4 0x00005595acd40e18 in ide_dma_cb (opaque=0x5595af6949d0, ret=) > at ../hw/ide/core.c:953 > #5 0x00005595ace6f319 in dma_complete (ret=0, dbs=0x7f64600089a0) > at ../softmmu/dma-helpers.c:107 > #6 dma_blk_cb (opaque=0x7f64600089a0, ret=0) at ../softmmu/dma-helpers.c:127 > #7 0x00005595ad12227d in blk_aio_complete (acb=0x7f6460005b10) > at ../block/block-backend.c:1527 > #8 blk_aio_complete (acb=0x7f6460005b10) at ../block/block-backend.c:1524 > #9 blk_aio_write_entry (opaque=0x7f6460005b10) at ../block/block-backend.c:1594 > #10 0x00005595ad258cfb in coroutine_trampoline (i0=, > i1=) at ../util/coroutine-ucontext.c:177 Signed-off-by: Fiona Ebner Reviewed-by: Philippe Mathieu-Daudé Tested-by: simon.rowe@nutanix.com Message-ID: <20230906130922.142845-1-f.ebner@proxmox.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/ide/core.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/hw/ide/core.c b/hw/ide/core.c index b5e0dcd29b..63ba665f3d 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -2515,19 +2515,19 @@ static void ide_dummy_transfer_stop(IDEState *s) void ide_bus_reset(IDEBus *bus) { - bus->unit = 0; - bus->cmd = 0; - ide_reset(&bus->ifs[0]); - ide_reset(&bus->ifs[1]); - ide_clear_hob(bus); - - /* pending async DMA */ + /* pending async DMA - needs the IDEState before it is reset */ if (bus->dma->aiocb) { trace_ide_bus_reset_aio(); blk_aio_cancel(bus->dma->aiocb); bus->dma->aiocb = NULL; } + bus->unit = 0; + bus->cmd = 0; + ide_reset(&bus->ifs[0]); + ide_reset(&bus->ifs[1]); + ide_clear_hob(bus); + /* reset dma provider too */ if (bus->dma->ops->reset) { bus->dma->ops->reset(bus->dma); From cc610857bbd3551f4b86ae2299336b5d9aa0db2b Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Wed, 6 Sep 2023 15:09:22 +0200 Subject: [PATCH 824/974] tests/qtest: ahci-test: add test exposing reset issue with pending callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before commit "hw/ide: reset: cancel async DMA operation before resetting state", this test would fail, because a reset with a pending write operation would lead to an unsolicited write to the first sector of the disk. The test writes a pattern to the beginning of the disk and verifies that it is still intact after a reset with a pending operation. It also checks that the pending operation actually completes correctly. Signed-off-by: Fiona Ebner Message-ID: <20230906130922.142845-2-f.ebner@proxmox.com> Signed-off-by: Philippe Mathieu-Daudé --- tests/qtest/ahci-test.c | 86 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/tests/qtest/ahci-test.c b/tests/qtest/ahci-test.c index eea8b5f77b..5a1923f721 100644 --- a/tests/qtest/ahci-test.c +++ b/tests/qtest/ahci-test.c @@ -1424,6 +1424,89 @@ static void test_reset(void) ahci_shutdown(ahci); } +static void test_reset_pending_callback(void) +{ + AHCIQState *ahci; + AHCICommand *cmd; + uint8_t port; + uint64_t ptr1; + uint64_t ptr2; + + int bufsize = 4 * 1024; + int speed = bufsize + (bufsize / 2); + int offset1 = 0; + int offset2 = bufsize / AHCI_SECTOR_SIZE; + + g_autofree unsigned char *tx1 = g_malloc(bufsize); + g_autofree unsigned char *tx2 = g_malloc(bufsize); + g_autofree unsigned char *rx1 = g_malloc0(bufsize); + g_autofree unsigned char *rx2 = g_malloc0(bufsize); + + /* Uses throttling to make test independent of specific environment. */ + ahci = ahci_boot_and_enable("-drive if=none,id=drive0,file=%s," + "cache=writeback,format=%s," + "throttling.bps-write=%d " + "-M q35 " + "-device ide-hd,drive=drive0 ", + tmp_path, imgfmt, speed); + + port = ahci_port_select(ahci); + ahci_port_clear(ahci, port); + + ptr1 = ahci_alloc(ahci, bufsize); + ptr2 = ahci_alloc(ahci, bufsize); + + g_assert(ptr1 && ptr2); + + /* Need two different patterns. */ + do { + generate_pattern(tx1, bufsize, AHCI_SECTOR_SIZE); + generate_pattern(tx2, bufsize, AHCI_SECTOR_SIZE); + } while (memcmp(tx1, tx2, bufsize) == 0); + + qtest_bufwrite(ahci->parent->qts, ptr1, tx1, bufsize); + qtest_bufwrite(ahci->parent->qts, ptr2, tx2, bufsize); + + /* Write to beginning of disk to check it wasn't overwritten later. */ + ahci_guest_io(ahci, port, CMD_WRITE_DMA_EXT, ptr1, bufsize, offset1); + + /* Issue asynchronously to get a pending callback during reset. */ + cmd = ahci_command_create(CMD_WRITE_DMA_EXT); + ahci_command_adjust(cmd, offset2, ptr2, bufsize, 0); + ahci_command_commit(ahci, cmd, port); + ahci_command_issue_async(ahci, cmd); + + ahci_set(ahci, AHCI_GHC, AHCI_GHC_HR); + + ahci_command_free(cmd); + + /* Wait for throttled write to finish. */ + sleep(1); + + /* Start again. */ + ahci_clean_mem(ahci); + ahci_pci_enable(ahci); + ahci_hba_enable(ahci); + port = ahci_port_select(ahci); + ahci_port_clear(ahci, port); + + /* Read and verify. */ + ahci_guest_io(ahci, port, CMD_READ_DMA_EXT, ptr1, bufsize, offset1); + qtest_bufread(ahci->parent->qts, ptr1, rx1, bufsize); + g_assert_cmphex(memcmp(tx1, rx1, bufsize), ==, 0); + + ahci_guest_io(ahci, port, CMD_READ_DMA_EXT, ptr2, bufsize, offset2); + qtest_bufread(ahci->parent->qts, ptr2, rx2, bufsize); + g_assert_cmphex(memcmp(tx2, rx2, bufsize), ==, 0); + + ahci_free(ahci, ptr1); + ahci_free(ahci, ptr2); + + ahci_clean_mem(ahci); + + ahci_shutdown(ahci); +} + static void test_ncq_simple(void) { AHCIQState *ahci; @@ -1945,7 +2028,8 @@ int main(int argc, char **argv) qtest_add_func("/ahci/migrate/dma/halted", test_migrate_halted_dma); qtest_add_func("/ahci/max", test_max); - qtest_add_func("/ahci/reset", test_reset); + qtest_add_func("/ahci/reset/simple", test_reset); + qtest_add_func("/ahci/reset/pending_callback", test_reset_pending_callback); qtest_add_func("/ahci/io/ncq/simple", test_ncq_simple); qtest_add_func("/ahci/migrate/ncq/simple", test_migrate_ncq); From cfb0884c6f941977db1e0ce53ac659a8428e4e48 Mon Sep 17 00:00:00 2001 From: Titus Rwantare Date: Mon, 23 Oct 2023 23:46:40 +0000 Subject: [PATCH 825/974] hw/i2c: pmbus add support for block receive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PMBus devices can send and receive variable length data using the block read and write format, with the first byte in the payload denoting the length. This is mostly used for strings and on-device logs. Devices can respond to a block read with an empty string. Reviewed-by: Hao Wu Acked-by: Corey Minyard Signed-off-by: Titus Rwantare Message-ID: <20231023-staging-pmbus-v3-v4-1-07a8cb7cd20a@google.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/i2c/pmbus_device.c | 30 +++++++++++++++++++++++++++++- include/hw/i2c/pmbus_device.h | 7 +++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c index cef51663d0..ea15490720 100644 --- a/hw/i2c/pmbus_device.c +++ b/hw/i2c/pmbus_device.c @@ -102,7 +102,6 @@ void pmbus_send_string(PMBusDevice *pmdev, const char *data) } size_t len = strlen(data); - g_assert(len > 0); g_assert(len + pmdev->out_buf_len < SMBUS_DATA_MAX_LEN); pmdev->out_buf[len + pmdev->out_buf_len] = len; @@ -112,6 +111,35 @@ void pmbus_send_string(PMBusDevice *pmdev, const char *data) pmdev->out_buf_len += len + 1; } +uint8_t pmbus_receive_block(PMBusDevice *pmdev, uint8_t *dest, size_t len) +{ + /* dest may contain data from previous writes */ + memset(dest, 0, len); + + /* Exclude command code from return value */ + pmdev->in_buf++; + pmdev->in_buf_len--; + + /* The byte after the command code denotes the length */ + uint8_t sent_len = pmdev->in_buf[0]; + + if (sent_len != pmdev->in_buf_len - 1) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: length mismatch. Expected %d bytes, got %d bytes\n", + __func__, sent_len, pmdev->in_buf_len - 1); + } + + /* exclude length byte */ + pmdev->in_buf++; + pmdev->in_buf_len--; + + if (pmdev->in_buf_len < len) { + len = pmdev->in_buf_len; + } + memcpy(dest, pmdev->in_buf, len); + return len; +} + static uint64_t pmbus_receive_uint(PMBusDevice *pmdev) { diff --git a/include/hw/i2c/pmbus_device.h b/include/hw/i2c/pmbus_device.h index 93f5d57c9d..7dc00cc4d9 100644 --- a/include/hw/i2c/pmbus_device.h +++ b/include/hw/i2c/pmbus_device.h @@ -501,6 +501,13 @@ void pmbus_send64(PMBusDevice *state, uint64_t data); */ void pmbus_send_string(PMBusDevice *state, const char *data); +/** + * @brief Receive data sent with Block Write. + * @param dest - memory with enough capacity to receive the write + * @param len - the capacity of dest + */ +uint8_t pmbus_receive_block(PMBusDevice *pmdev, uint8_t *dest, size_t len); + /** * @brief Receive data over PMBus * These methods help track how much data is being received over PMBus From 144729b9f1156174d41df54f05f4b57ce80e30de Mon Sep 17 00:00:00 2001 From: Titus Rwantare Date: Mon, 23 Oct 2023 23:46:41 +0000 Subject: [PATCH 826/974] hw/i2c: pmbus: add vout mode bitfields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The VOUT_MODE command is described in the PMBus Specification, Part II, Ver 1.3 Section 8.3 VOUT_MODE has a three bit mode and 4 bit parameter, the three bit mode determines whether voltages are formatted as uint16, uint16, VID, and Direct modes. VID and Direct modes use the remaining 5 bits to scale the voltage readings. Reviewed-by: Hao Wu Acked-by: Corey Minyard Signed-off-by: Titus Rwantare Message-ID: <20231023-staging-pmbus-v3-v4-2-07a8cb7cd20a@google.com> Signed-off-by: Philippe Mathieu-Daudé --- include/hw/i2c/pmbus_device.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/hw/i2c/pmbus_device.h b/include/hw/i2c/pmbus_device.h index 7dc00cc4d9..2e95164aa1 100644 --- a/include/hw/i2c/pmbus_device.h +++ b/include/hw/i2c/pmbus_device.h @@ -444,6 +444,14 @@ typedef struct PMBusCoefficients { int32_t R; /* exponent */ } PMBusCoefficients; +/** + * VOUT_Mode bit fields + */ +typedef struct PMBusVoutMode { + uint8_t mode:3; + int8_t exp:5; +} PMBusVoutMode; + /** * Convert sensor values to direct mode format * From b7fba25ef1f32bfe92d9fcc73f297b2ce58fee9e Mon Sep 17 00:00:00 2001 From: Titus Rwantare Date: Mon, 23 Oct 2023 23:46:42 +0000 Subject: [PATCH 827/974] hw/i2c: pmbus: add fan support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PMBus devices may integrate fans whose operation is configurable over PMBus. This commit allows the driver to read and write the fan control registers but does not model the operation of fans. Reviewed-by: Stephen Longfield Acked-by: Corey Minyard Signed-off-by: Titus Rwantare Message-ID: <20231023-staging-pmbus-v3-v4-3-07a8cb7cd20a@google.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/i2c/pmbus_device.c | 176 ++++++++++++++++++++++++++++++++++ include/hw/i2c/pmbus_device.h | 1 + 2 files changed, 177 insertions(+) diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c index ea15490720..c1d8c93056 100644 --- a/hw/i2c/pmbus_device.c +++ b/hw/i2c/pmbus_device.c @@ -500,6 +500,54 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd) } break; + case PMBUS_FAN_CONFIG_1_2: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmbus_send8(pmdev, pmdev->pages[index].fan_config_1_2); + } else { + goto passthough; + } + break; + + case PMBUS_FAN_COMMAND_1: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmbus_send16(pmdev, pmdev->pages[index].fan_command_1); + } else { + goto passthough; + } + break; + + case PMBUS_FAN_COMMAND_2: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmbus_send16(pmdev, pmdev->pages[index].fan_command_2); + } else { + goto passthough; + } + break; + + case PMBUS_FAN_CONFIG_3_4: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmbus_send8(pmdev, pmdev->pages[index].fan_config_3_4); + } else { + goto passthough; + } + break; + + case PMBUS_FAN_COMMAND_3: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmbus_send16(pmdev, pmdev->pages[index].fan_command_3); + } else { + goto passthough; + } + break; + + case PMBUS_FAN_COMMAND_4: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmbus_send16(pmdev, pmdev->pages[index].fan_command_4); + } else { + goto passthough; + } + break; + case PMBUS_VOUT_OV_FAULT_LIMIT: /* R/W word */ if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { pmbus_send16(pmdev, pmdev->pages[index].vout_ov_fault_limit); @@ -810,6 +858,22 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd) pmbus_send8(pmdev, pmdev->pages[index].status_mfr_specific); break; + case PMBUS_STATUS_FANS_1_2: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmbus_send8(pmdev, pmdev->pages[index].status_fans_1_2); + } else { + goto passthough; + } + break; + + case PMBUS_STATUS_FANS_3_4: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmbus_send8(pmdev, pmdev->pages[index].status_fans_3_4); + } else { + goto passthough; + } + break; + case PMBUS_READ_EIN: /* Read-Only block 5 bytes */ if (pmdev->pages[index].page_flags & PB_HAS_EIN) { pmbus_send(pmdev, pmdev->pages[index].read_ein, 5); @@ -882,6 +946,54 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd) } break; + case PMBUS_READ_FAN_SPEED_1: /* Read-Only word */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmbus_send16(pmdev, pmdev->pages[index].read_fan_speed_1); + } else { + goto passthough; + } + break; + + case PMBUS_READ_FAN_SPEED_2: /* Read-Only word */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmbus_send16(pmdev, pmdev->pages[index].read_fan_speed_2); + } else { + goto passthough; + } + break; + + case PMBUS_READ_FAN_SPEED_3: /* Read-Only word */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmbus_send16(pmdev, pmdev->pages[index].read_fan_speed_3); + } else { + goto passthough; + } + break; + + case PMBUS_READ_FAN_SPEED_4: /* Read-Only word */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmbus_send16(pmdev, pmdev->pages[index].read_fan_speed_4); + } else { + goto passthough; + } + break; + + case PMBUS_READ_DUTY_CYCLE: /* Read-Only word */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmbus_send16(pmdev, pmdev->pages[index].read_duty_cycle); + } else { + goto passthough; + } + break; + + case PMBUS_READ_FREQUENCY: /* Read-Only word */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmbus_send16(pmdev, pmdev->pages[index].read_frequency); + } else { + goto passthough; + } + break; + case PMBUS_READ_POUT: /* Read-Only word */ if (pmdev->pages[index].page_flags & PB_HAS_POUT) { pmbus_send16(pmdev, pmdev->pages[index].read_pout); @@ -1305,6 +1417,54 @@ static int pmbus_write_data(SMBusDevice *smd, uint8_t *buf, uint8_t len) } break; + case PMBUS_FAN_CONFIG_1_2: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmdev->pages[index].fan_config_1_2 = pmbus_receive8(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_FAN_COMMAND_1: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmdev->pages[index].fan_command_1 = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_FAN_COMMAND_2: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmdev->pages[index].fan_command_2 = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_FAN_CONFIG_3_4: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmdev->pages[index].fan_config_3_4 = pmbus_receive8(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_FAN_COMMAND_3: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmdev->pages[index].fan_command_3 = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_FAN_COMMAND_4: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmdev->pages[index].fan_command_4 = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + case PMBUS_VOUT_OV_FAULT_LIMIT: /* R/W word */ if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { pmdev->pages[index].vout_ov_fault_limit = pmbus_receive16(pmdev); @@ -1610,6 +1770,22 @@ static int pmbus_write_data(SMBusDevice *smd, uint8_t *buf, uint8_t len) pmdev->pages[index].status_mfr_specific = pmbus_receive8(pmdev); break; + case PMBUS_STATUS_FANS_1_2: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmbus_send8(pmdev, pmdev->pages[index].status_fans_1_2); + } else { + goto passthrough; + } + break; + + case PMBUS_STATUS_FANS_3_4: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmbus_send8(pmdev, pmdev->pages[index].status_fans_3_4); + } else { + goto passthrough; + } + break; + case PMBUS_PAGE_PLUS_READ: /* Block Read-only */ case PMBUS_CAPABILITY: /* Read-Only byte */ case PMBUS_COEFFICIENTS: /* Read-only block 5 bytes */ diff --git a/include/hw/i2c/pmbus_device.h b/include/hw/i2c/pmbus_device.h index 2e95164aa1..ad431bdc7c 100644 --- a/include/hw/i2c/pmbus_device.h +++ b/include/hw/i2c/pmbus_device.h @@ -258,6 +258,7 @@ OBJECT_DECLARE_TYPE(PMBusDevice, PMBusDeviceClass, #define PB_HAS_TEMP2 BIT_ULL(41) #define PB_HAS_TEMP3 BIT_ULL(42) #define PB_HAS_TEMP_RATING BIT_ULL(43) +#define PB_HAS_FAN BIT_ULL(44) #define PB_HAS_MFR_INFO BIT_ULL(50) #define PB_HAS_STATUS_MFR_SPECIFIC BIT_ULL(51) From 3401b1dd1a947256dcfa5494642dfb46c16c47c2 Mon Sep 17 00:00:00 2001 From: Titus Rwantare Date: Mon, 23 Oct 2023 23:46:43 +0000 Subject: [PATCH 828/974] hw/i2c: pmbus: add VCAP register MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VCAP is a register for devices with energy storage capacitors. Reviewed-by: Benjamin Streb Acked-by: Corey Minyard Signed-off-by: Titus Rwantare Message-ID: <20231023-staging-pmbus-v3-v4-4-07a8cb7cd20a@google.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/i2c/pmbus_device.c | 8 ++++++++ include/hw/i2c/pmbus_device.h | 1 + 2 files changed, 9 insertions(+) diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c index c1d8c93056..3bce39e84e 100644 --- a/hw/i2c/pmbus_device.c +++ b/hw/i2c/pmbus_device.c @@ -906,6 +906,14 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd) } break; + case PMBUS_READ_VCAP: /* Read-Only word */ + if (pmdev->pages[index].page_flags & PB_HAS_VCAP) { + pmbus_send16(pmdev, pmdev->pages[index].read_vcap); + } else { + goto passthough; + } + break; + case PMBUS_READ_VOUT: /* Read-Only word */ if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { pmbus_send16(pmdev, pmdev->pages[index].read_vout); diff --git a/include/hw/i2c/pmbus_device.h b/include/hw/i2c/pmbus_device.h index ad431bdc7c..f195c11384 100644 --- a/include/hw/i2c/pmbus_device.h +++ b/include/hw/i2c/pmbus_device.h @@ -243,6 +243,7 @@ OBJECT_DECLARE_TYPE(PMBusDevice, PMBusDeviceClass, #define PB_HAS_VIN_RATING BIT_ULL(13) #define PB_HAS_VOUT_RATING BIT_ULL(14) #define PB_HAS_VOUT_MODE BIT_ULL(15) +#define PB_HAS_VCAP BIT_ULL(16) #define PB_HAS_IOUT BIT_ULL(21) #define PB_HAS_IIN BIT_ULL(22) #define PB_HAS_IOUT_RATING BIT_ULL(23) From 6f351a7a707f5d4257b6c55b28e9d99012730acc Mon Sep 17 00:00:00 2001 From: Titus Rwantare Date: Mon, 23 Oct 2023 23:46:44 +0000 Subject: [PATCH 829/974] hw/sensor: add ADM1266 device model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ADM1266 is a cascadable super sequencer with margin control and fault recording. This commit adds basic support for its PMBus commands and models the identification registers that can be modified in a firmware update. Reviewed-by: Hao Wu Acked-by: Corey Minyard Signed-off-by: Titus Rwantare [PMD: Cover file in MAINTAINERS] Message-ID: <20231023-staging-pmbus-v3-v4-5-07a8cb7cd20a@google.com> Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 1 + hw/arm/Kconfig | 1 + hw/sensor/Kconfig | 5 + hw/sensor/adm1266.c | 254 ++++++++++++++++++++++++++++++++++++++++++ hw/sensor/meson.build | 1 + 5 files changed, 262 insertions(+) create mode 100644 hw/sensor/adm1266.c diff --git a/MAINTAINERS b/MAINTAINERS index 0e1d063d77..178b763fea 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -859,6 +859,7 @@ M: Hao Wu L: qemu-arm@nongnu.org S: Supported F: hw/*/npcm* +F: hw/sensor/adm1266.c F: include/hw/*/npcm* F: tests/qtest/npcm* F: pc-bios/npcm7xx_bootrom.bin diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index e35007ed41..0f22aee24b 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -489,6 +489,7 @@ config NPCM7XX default y depends on TCG && ARM select A9MPCORE + select ADM1266 select ADM1272 select ARM_GIC select SMBUS diff --git a/hw/sensor/Kconfig b/hw/sensor/Kconfig index e03bd09b50..bc6331b4ab 100644 --- a/hw/sensor/Kconfig +++ b/hw/sensor/Kconfig @@ -22,6 +22,11 @@ config ADM1272 bool depends on I2C +config ADM1266 + bool + depends on PMBUS + default y if PMBUS + config MAX34451 bool depends on I2C diff --git a/hw/sensor/adm1266.c b/hw/sensor/adm1266.c new file mode 100644 index 0000000000..5ae4f82ba1 --- /dev/null +++ b/hw/sensor/adm1266.c @@ -0,0 +1,254 @@ +/* + * Analog Devices ADM1266 Cascadable Super Sequencer with Margin Control and + * Fault Recording with PMBus + * + * https://www.analog.com/media/en/technical-documentation/data-sheets/adm1266.pdf + * + * Copyright 2023 Google LLC + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/i2c/pmbus_device.h" +#include "hw/irq.h" +#include "migration/vmstate.h" +#include "qapi/error.h" +#include "qapi/visitor.h" +#include "qemu/log.h" +#include "qemu/module.h" + +#define TYPE_ADM1266 "adm1266" +OBJECT_DECLARE_SIMPLE_TYPE(ADM1266State, ADM1266) + +#define ADM1266_BLACKBOX_CONFIG 0xD3 +#define ADM1266_PDIO_CONFIG 0xD4 +#define ADM1266_READ_STATE 0xD9 +#define ADM1266_READ_BLACKBOX 0xDE +#define ADM1266_SET_RTC 0xDF +#define ADM1266_GPIO_SYNC_CONFIGURATION 0xE1 +#define ADM1266_BLACKBOX_INFORMATION 0xE6 +#define ADM1266_PDIO_STATUS 0xE9 +#define ADM1266_GPIO_STATUS 0xEA + +/* Defaults */ +#define ADM1266_OPERATION_DEFAULT 0x80 +#define ADM1266_CAPABILITY_DEFAULT 0xA0 +#define ADM1266_CAPABILITY_NO_PEC 0x20 +#define ADM1266_PMBUS_REVISION_DEFAULT 0x22 +#define ADM1266_MFR_ID_DEFAULT "ADI" +#define ADM1266_MFR_ID_DEFAULT_LEN 32 +#define ADM1266_MFR_MODEL_DEFAULT "ADM1266-A1" +#define ADM1266_MFR_MODEL_DEFAULT_LEN 32 +#define ADM1266_MFR_REVISION_DEFAULT "25" +#define ADM1266_MFR_REVISION_DEFAULT_LEN 8 + +#define ADM1266_NUM_PAGES 17 +/** + * PAGE Index + * Page 0 VH1. + * Page 1 VH2. + * Page 2 VH3. + * Page 3 VH4. + * Page 4 VP1. + * Page 5 VP2. + * Page 6 VP3. + * Page 7 VP4. + * Page 8 VP5. + * Page 9 VP6. + * Page 10 VP7. + * Page 11 VP8. + * Page 12 VP9. + * Page 13 VP10. + * Page 14 VP11. + * Page 15 VP12. + * Page 16 VP13. + */ +typedef struct ADM1266State { + PMBusDevice parent; + + char mfr_id[32]; + char mfr_model[32]; + char mfr_rev[8]; +} ADM1266State; + +static const uint8_t adm1266_ic_device_id[] = {0x03, 0x41, 0x12, 0x66}; +static const uint8_t adm1266_ic_device_rev[] = {0x08, 0x01, 0x08, 0x07, 0x0, + 0x0, 0x07, 0x41, 0x30}; + +static void adm1266_exit_reset(Object *obj) +{ + ADM1266State *s = ADM1266(obj); + PMBusDevice *pmdev = PMBUS_DEVICE(obj); + + pmdev->page = 0; + pmdev->capability = ADM1266_CAPABILITY_NO_PEC; + + for (int i = 0; i < ADM1266_NUM_PAGES; i++) { + pmdev->pages[i].operation = ADM1266_OPERATION_DEFAULT; + pmdev->pages[i].revision = ADM1266_PMBUS_REVISION_DEFAULT; + pmdev->pages[i].vout_mode = 0; + pmdev->pages[i].read_vout = pmbus_data2linear_mode(12, 0); + pmdev->pages[i].vout_margin_high = pmbus_data2linear_mode(15, 0); + pmdev->pages[i].vout_margin_low = pmbus_data2linear_mode(3, 0); + pmdev->pages[i].vout_ov_fault_limit = pmbus_data2linear_mode(16, 0); + pmdev->pages[i].revision = ADM1266_PMBUS_REVISION_DEFAULT; + } + + strncpy(s->mfr_id, ADM1266_MFR_ID_DEFAULT, 4); + strncpy(s->mfr_model, ADM1266_MFR_MODEL_DEFAULT, 11); + strncpy(s->mfr_rev, ADM1266_MFR_REVISION_DEFAULT, 3); +} + +static uint8_t adm1266_read_byte(PMBusDevice *pmdev) +{ + ADM1266State *s = ADM1266(pmdev); + + switch (pmdev->code) { + case PMBUS_MFR_ID: /* R/W block */ + pmbus_send_string(pmdev, s->mfr_id); + break; + + case PMBUS_MFR_MODEL: /* R/W block */ + pmbus_send_string(pmdev, s->mfr_model); + break; + + case PMBUS_MFR_REVISION: /* R/W block */ + pmbus_send_string(pmdev, s->mfr_rev); + break; + + case PMBUS_IC_DEVICE_ID: + pmbus_send(pmdev, adm1266_ic_device_id, sizeof(adm1266_ic_device_id)); + break; + + case PMBUS_IC_DEVICE_REV: + pmbus_send(pmdev, adm1266_ic_device_rev, sizeof(adm1266_ic_device_rev)); + break; + + default: + qemu_log_mask(LOG_UNIMP, + "%s: reading from unimplemented register: 0x%02x\n", + __func__, pmdev->code); + return 0xFF; + } + + return 0; +} + +static int adm1266_write_data(PMBusDevice *pmdev, const uint8_t *buf, + uint8_t len) +{ + ADM1266State *s = ADM1266(pmdev); + + switch (pmdev->code) { + case PMBUS_MFR_ID: /* R/W block */ + pmbus_receive_block(pmdev, (uint8_t *)s->mfr_id, sizeof(s->mfr_id)); + break; + + case PMBUS_MFR_MODEL: /* R/W block */ + pmbus_receive_block(pmdev, (uint8_t *)s->mfr_model, + sizeof(s->mfr_model)); + break; + + case PMBUS_MFR_REVISION: /* R/W block*/ + pmbus_receive_block(pmdev, (uint8_t *)s->mfr_rev, sizeof(s->mfr_rev)); + break; + + case ADM1266_SET_RTC: /* do nothing */ + break; + + default: + qemu_log_mask(LOG_UNIMP, + "%s: writing to unimplemented register: 0x%02x\n", + __func__, pmdev->code); + break; + } + return 0; +} + +static void adm1266_get(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) +{ + uint16_t value; + PMBusDevice *pmdev = PMBUS_DEVICE(obj); + PMBusVoutMode *mode = (PMBusVoutMode *)&pmdev->pages[0].vout_mode; + + if (strcmp(name, "vout") == 0) { + value = pmbus_linear_mode2data(*(uint16_t *)opaque, mode->exp); + } else { + value = *(uint16_t *)opaque; + } + + visit_type_uint16(v, name, &value, errp); +} + +static void adm1266_set(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) +{ + uint16_t *internal = opaque; + uint16_t value; + PMBusDevice *pmdev = PMBUS_DEVICE(obj); + PMBusVoutMode *mode = (PMBusVoutMode *)&pmdev->pages[0].vout_mode; + + if (!visit_type_uint16(v, name, &value, errp)) { + return; + } + + *internal = pmbus_data2linear_mode(value, mode->exp); + pmbus_check_limits(pmdev); +} + +static const VMStateDescription vmstate_adm1266 = { + .name = "ADM1266", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]){ + VMSTATE_PMBUS_DEVICE(parent, ADM1266State), + VMSTATE_END_OF_LIST() + } +}; + +static void adm1266_init(Object *obj) +{ + PMBusDevice *pmdev = PMBUS_DEVICE(obj); + uint64_t flags = PB_HAS_VOUT_MODE | PB_HAS_VOUT | PB_HAS_VOUT_MARGIN | + PB_HAS_VOUT_RATING | PB_HAS_STATUS_MFR_SPECIFIC; + + for (int i = 0; i < ADM1266_NUM_PAGES; i++) { + pmbus_page_config(pmdev, i, flags); + + object_property_add(obj, "vout[*]", "uint16", + adm1266_get, + adm1266_set, NULL, &pmdev->pages[i].read_vout); + } +} + +static void adm1266_class_init(ObjectClass *klass, void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + PMBusDeviceClass *k = PMBUS_DEVICE_CLASS(klass); + + dc->desc = "Analog Devices ADM1266 Hot Swap controller"; + dc->vmsd = &vmstate_adm1266; + k->write_data = adm1266_write_data; + k->receive_byte = adm1266_read_byte; + k->device_num_pages = 17; + + rc->phases.exit = adm1266_exit_reset; +} + +static const TypeInfo adm1266_info = { + .name = TYPE_ADM1266, + .parent = TYPE_PMBUS_DEVICE, + .instance_size = sizeof(ADM1266State), + .instance_init = adm1266_init, + .class_init = adm1266_class_init, +}; + +static void adm1266_register_types(void) +{ + type_register_static(&adm1266_info); +} + +type_init(adm1266_register_types) diff --git a/hw/sensor/meson.build b/hw/sensor/meson.build index 30e20e27b8..420fdc3359 100644 --- a/hw/sensor/meson.build +++ b/hw/sensor/meson.build @@ -2,6 +2,7 @@ system_ss.add(when: 'CONFIG_TMP105', if_true: files('tmp105.c')) system_ss.add(when: 'CONFIG_TMP421', if_true: files('tmp421.c')) system_ss.add(when: 'CONFIG_DPS310', if_true: files('dps310.c')) system_ss.add(when: 'CONFIG_EMC141X', if_true: files('emc141x.c')) +system_ss.add(when: 'CONFIG_ADM1266', if_true: files('adm1266.c')) system_ss.add(when: 'CONFIG_ADM1272', if_true: files('adm1272.c')) system_ss.add(when: 'CONFIG_MAX34451', if_true: files('max34451.c')) system_ss.add(when: 'CONFIG_LSM303DLHC_MAG', if_true: files('lsm303dlhc_mag.c')) From 5861f5abf408a4d9c03a827869bc42d2aae8976b Mon Sep 17 00:00:00 2001 From: Titus Rwantare Date: Mon, 23 Oct 2023 23:46:45 +0000 Subject: [PATCH 830/974] tests/qtest: add tests for ADM1266 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ADM1266 can have string fields written by the driver, so it's worth specifically testing. Reviewed-by: Hao Wu Acked-by: Corey Minyard Signed-off-by: Titus Rwantare [PMD: Cover file in MAINTAINERS] Message-ID: <20231023-staging-pmbus-v3-v4-6-07a8cb7cd20a@google.com> Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 1 + tests/qtest/adm1266-test.c | 122 +++++++++++++++++++++++++++++++++++++ tests/qtest/meson.build | 1 + 3 files changed, 124 insertions(+) create mode 100644 tests/qtest/adm1266-test.c diff --git a/MAINTAINERS b/MAINTAINERS index 178b763fea..67086ac6e3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -862,6 +862,7 @@ F: hw/*/npcm* F: hw/sensor/adm1266.c F: include/hw/*/npcm* F: tests/qtest/npcm* +F: tests/qtest/adm1266-test.c F: pc-bios/npcm7xx_bootrom.bin F: roms/vbootrom F: docs/system/arm/nuvoton.rst diff --git a/tests/qtest/adm1266-test.c b/tests/qtest/adm1266-test.c new file mode 100644 index 0000000000..6c312c499f --- /dev/null +++ b/tests/qtest/adm1266-test.c @@ -0,0 +1,122 @@ +/* + * Analog Devices ADM1266 Cascadable Super Sequencer with Margin Control and + * Fault Recording with PMBus + * + * Copyright 2022 Google LLC + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include +#include "hw/i2c/pmbus_device.h" +#include "libqtest-single.h" +#include "libqos/qgraph.h" +#include "libqos/i2c.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qnum.h" +#include "qemu/bitops.h" + +#define TEST_ID "adm1266-test" +#define TEST_ADDR (0x12) + +#define ADM1266_BLACKBOX_CONFIG 0xD3 +#define ADM1266_PDIO_CONFIG 0xD4 +#define ADM1266_READ_STATE 0xD9 +#define ADM1266_READ_BLACKBOX 0xDE +#define ADM1266_SET_RTC 0xDF +#define ADM1266_GPIO_SYNC_CONFIGURATION 0xE1 +#define ADM1266_BLACKBOX_INFORMATION 0xE6 +#define ADM1266_PDIO_STATUS 0xE9 +#define ADM1266_GPIO_STATUS 0xEA + +/* Defaults */ +#define ADM1266_OPERATION_DEFAULT 0x80 +#define ADM1266_CAPABILITY_DEFAULT 0xA0 +#define ADM1266_CAPABILITY_NO_PEC 0x20 +#define ADM1266_PMBUS_REVISION_DEFAULT 0x22 +#define ADM1266_MFR_ID_DEFAULT "ADI" +#define ADM1266_MFR_ID_DEFAULT_LEN 32 +#define ADM1266_MFR_MODEL_DEFAULT "ADM1266-A1" +#define ADM1266_MFR_MODEL_DEFAULT_LEN 32 +#define ADM1266_MFR_REVISION_DEFAULT "25" +#define ADM1266_MFR_REVISION_DEFAULT_LEN 8 +#define TEST_STRING_A "a sample" +#define TEST_STRING_B "b sample" +#define TEST_STRING_C "rev c" + +static void compare_string(QI2CDevice *i2cdev, uint8_t reg, + const char *test_str) +{ + uint8_t len = i2c_get8(i2cdev, reg); + char i2c_str[SMBUS_DATA_MAX_LEN] = {0}; + + i2c_read_block(i2cdev, reg, (uint8_t *)i2c_str, len); + g_assert_cmpstr(i2c_str, ==, test_str); +} + +static void write_and_compare_string(QI2CDevice *i2cdev, uint8_t reg, + const char *test_str, uint8_t len) +{ + char buf[SMBUS_DATA_MAX_LEN] = {0}; + buf[0] = len; + strncpy(buf + 1, test_str, len); + i2c_write_block(i2cdev, reg, (uint8_t *)buf, len + 1); + compare_string(i2cdev, reg, test_str); +} + +static void test_defaults(void *obj, void *data, QGuestAllocator *alloc) +{ + uint16_t i2c_value; + QI2CDevice *i2cdev = (QI2CDevice *)obj; + + i2c_value = i2c_get8(i2cdev, PMBUS_OPERATION); + g_assert_cmphex(i2c_value, ==, ADM1266_OPERATION_DEFAULT); + + i2c_value = i2c_get8(i2cdev, PMBUS_REVISION); + g_assert_cmphex(i2c_value, ==, ADM1266_PMBUS_REVISION_DEFAULT); + + compare_string(i2cdev, PMBUS_MFR_ID, ADM1266_MFR_ID_DEFAULT); + compare_string(i2cdev, PMBUS_MFR_MODEL, ADM1266_MFR_MODEL_DEFAULT); + compare_string(i2cdev, PMBUS_MFR_REVISION, ADM1266_MFR_REVISION_DEFAULT); +} + +/* test r/w registers */ +static void test_rw_regs(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *i2cdev = (QI2CDevice *)obj; + + /* empty strings */ + i2c_set8(i2cdev, PMBUS_MFR_ID, 0); + compare_string(i2cdev, PMBUS_MFR_ID, ""); + + i2c_set8(i2cdev, PMBUS_MFR_MODEL, 0); + compare_string(i2cdev, PMBUS_MFR_MODEL, ""); + + i2c_set8(i2cdev, PMBUS_MFR_REVISION, 0); + compare_string(i2cdev, PMBUS_MFR_REVISION, ""); + + /* test strings */ + write_and_compare_string(i2cdev, PMBUS_MFR_ID, TEST_STRING_A, + sizeof(TEST_STRING_A)); + write_and_compare_string(i2cdev, PMBUS_MFR_ID, TEST_STRING_B, + sizeof(TEST_STRING_B)); + write_and_compare_string(i2cdev, PMBUS_MFR_ID, TEST_STRING_C, + sizeof(TEST_STRING_C)); +} + +static void adm1266_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "id=" TEST_ID ",address=0x12" + }; + add_qi2c_address(&opts, &(QI2CAddress) { TEST_ADDR }); + + qos_node_create_driver("adm1266", i2c_device_create); + qos_node_consumes("adm1266", "i2c-bus", &opts); + + qos_add_test("test_defaults", "adm1266", test_defaults, NULL); + qos_add_test("test_rw_regs", "adm1266", test_rw_regs, NULL); +} + +libqos_init(adm1266_register_nodes); diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index c9945e69b1..47dabf91d0 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -241,6 +241,7 @@ qos_test_ss = ss.source_set() qos_test_ss.add( 'ac97-test.c', 'adm1272-test.c', + 'adm1266-test.c', 'ds1338-test.c', 'e1000-test.c', 'eepro100-test.c', From 84db503e7c260ace0cf7267379955bbae77e85a4 Mon Sep 17 00:00:00 2001 From: Titus Rwantare Date: Mon, 23 Oct 2023 23:46:46 +0000 Subject: [PATCH 831/974] hw/i2c: pmbus: immediately clear faults on request MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The probing process of the generic pmbus driver generates faults to determine if functions are available. These faults were not always cleared resulting in probe failures. Reviewed-by: Patrick Venture Signed-off-by: Titus Rwantare Message-ID: <20231023-staging-pmbus-v3-v4-7-07a8cb7cd20a@google.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/i2c/pmbus_device.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c index 3bce39e84e..481e158380 100644 --- a/hw/i2c/pmbus_device.c +++ b/hw/i2c/pmbus_device.c @@ -1244,6 +1244,11 @@ static int pmbus_write_data(SMBusDevice *smd, uint8_t *buf, uint8_t len) pmdev->in_buf = buf; pmdev->code = buf[0]; /* PMBus command code */ + + if (pmdev->code == PMBUS_CLEAR_FAULTS) { + pmbus_clear_faults(pmdev); + } + if (len == 1) { /* Single length writes are command codes only */ return 0; } From ff0511282d406150984de1aaaaad451da8ad3a1c Mon Sep 17 00:00:00 2001 From: Titus Rwantare Date: Mon, 23 Oct 2023 23:46:47 +0000 Subject: [PATCH 832/974] hw/i2c: pmbus: reset page register for out of range reads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The linux pmbus driver scans all possible pages and does not reset the current page after the scan, making all future page reads fail as out of range on devices with a single page. This change resets out of range pages immediately on write. Also added a qtest for simultaneous writes to all pages. Reviewed-by: Hao Wu Signed-off-by: Titus Rwantare Message-ID: <20231023-staging-pmbus-v3-v4-8-07a8cb7cd20a@google.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/i2c/pmbus_device.c | 18 +++++++++--------- tests/qtest/max34451-test.c | 24 ++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c index 481e158380..1b978e588f 100644 --- a/hw/i2c/pmbus_device.c +++ b/hw/i2c/pmbus_device.c @@ -1255,6 +1255,15 @@ static int pmbus_write_data(SMBusDevice *smd, uint8_t *buf, uint8_t len) if (pmdev->code == PMBUS_PAGE) { pmdev->page = pmbus_receive8(pmdev); + + if (pmdev->page > pmdev->num_pages - 1 && pmdev->page != PB_ALL_PAGES) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: page %u is out of range\n", + __func__, pmdev->page); + pmdev->page = 0; /* undefined behaviour - reset to page 0 */ + pmbus_cml_error(pmdev); + return PMBUS_ERR_BYTE; + } return 0; } @@ -1268,15 +1277,6 @@ static int pmbus_write_data(SMBusDevice *smd, uint8_t *buf, uint8_t len) return 0; } - if (pmdev->page > pmdev->num_pages - 1) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: page %u is out of range\n", - __func__, pmdev->page); - pmdev->page = 0; /* undefined behaviour - reset to page 0 */ - pmbus_cml_error(pmdev); - return PMBUS_ERR_BYTE; - } - index = pmdev->page; switch (pmdev->code) { diff --git a/tests/qtest/max34451-test.c b/tests/qtest/max34451-test.c index 0c98d0764c..dbf6ddc829 100644 --- a/tests/qtest/max34451-test.c +++ b/tests/qtest/max34451-test.c @@ -18,6 +18,7 @@ #define TEST_ID "max34451-test" #define TEST_ADDR (0x4e) +#define MAX34451_MFR_MODE 0xD1 #define MAX34451_MFR_VOUT_PEAK 0xD4 #define MAX34451_MFR_IOUT_PEAK 0xD5 #define MAX34451_MFR_TEMPERATURE_PEAK 0xD6 @@ -315,6 +316,28 @@ static void test_ot_faults(void *obj, void *data, QGuestAllocator *alloc) } } +#define RAND_ON_OFF_CONFIG 0x12 +#define RAND_MFR_MODE 0x3456 + +/* test writes to all pages */ +static void test_all_pages(void *obj, void *data, QGuestAllocator *alloc) +{ + uint16_t i2c_value; + QI2CDevice *i2cdev = (QI2CDevice *)obj; + + i2c_set8(i2cdev, PMBUS_PAGE, PB_ALL_PAGES); + i2c_set8(i2cdev, PMBUS_ON_OFF_CONFIG, RAND_ON_OFF_CONFIG); + max34451_i2c_set16(i2cdev, MAX34451_MFR_MODE, RAND_MFR_MODE); + + for (int i = 0; i < MAX34451_NUM_TEMP_DEVICES + MAX34451_NUM_PWR_DEVICES; + i++) { + i2c_value = i2c_get8(i2cdev, PMBUS_ON_OFF_CONFIG); + g_assert_cmphex(i2c_value, ==, RAND_ON_OFF_CONFIG); + i2c_value = max34451_i2c_get16(i2cdev, MAX34451_MFR_MODE); + g_assert_cmphex(i2c_value, ==, RAND_MFR_MODE); + } +} + static void max34451_register_nodes(void) { QOSGraphEdgeOptions opts = { @@ -332,5 +355,6 @@ static void max34451_register_nodes(void) qos_add_test("test_ro_regs", "max34451", test_ro_regs, NULL); qos_add_test("test_ov_faults", "max34451", test_ov_faults, NULL); qos_add_test("test_ot_faults", "max34451", test_ot_faults, NULL); + qos_add_test("test_all_pages", "max34451", test_all_pages, NULL); } libqos_init(max34451_register_nodes); From 88d2198c0836871e5902da1e1340e3ba3cb5a71d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 31 Oct 2023 06:55:39 +0100 Subject: [PATCH 833/974] hw/sd: Declare QOM types using DEFINE_TYPES() macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When multiple QOM types are registered in the same file, it is simpler to use the the DEFINE_TYPES() macro. In particular because type array declared with such macro are easier to review. Mechanical transformation using the following comby script: [pattern-x1] match=''' static const TypeInfo :[i1~.*_info] = { :[body] }; static void :[rt1~.*_register_type.](void) { type_register_static(&:[i2~.*_info]); } type_init(:[rt2~.*_register_type.]) ''' rewrite=''' static const TypeInfo :[i1][] = { { :[body] }, }; DEFINE_TYPES(:[i1]) ''' rule='where :[i1] == :[i2], :[rt1] == :[rt2]' [pattern-x2] match=''' static const TypeInfo :[i1a~.*_info] = { :[body1] }; ... static const TypeInfo :[i2a~.*_info] = { :[body2] }; static void :[rt1~.*_register_type.](void) { type_register_static(&:[i1b~.*_info]); type_register_static(&:[i2b~.*_info]); } type_init(:[rt2~.*_register_type.]) ''' rewrite=''' static const TypeInfo :[i1a][] = { { :[body1] }, { :[body2] }, }; DEFINE_TYPES(:[i1a]) ''' rule=''' where :[i1a] == :[i1b], :[i2a] == :[i2b], :[rt1] == :[rt2] ''' and re-indented manually. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Message-Id: <20231031080603.86889-2-philmd@linaro.org> --- hw/sd/aspeed_sdhci.c | 19 ++++++++----------- hw/sd/bcm2835_sdhost.c | 33 ++++++++++++++------------------- hw/sd/cadence_sdhci.c | 21 +++++++++------------ hw/sd/core.c | 19 ++++++++----------- hw/sd/npcm7xx_sdhci.c | 21 +++++++++------------ hw/sd/pl181.c | 35 +++++++++++++++-------------------- hw/sd/pxa2xx_mmci.c | 35 +++++++++++++++-------------------- hw/sd/sd.c | 37 ++++++++++++++++--------------------- hw/sd/sdhci-pci.c | 25 +++++++++++-------------- hw/sd/ssi-sd.c | 19 ++++++++----------- 10 files changed, 113 insertions(+), 151 deletions(-) diff --git a/hw/sd/aspeed_sdhci.c b/hw/sd/aspeed_sdhci.c index be8cafd65f..e53206d959 100644 --- a/hw/sd/aspeed_sdhci.c +++ b/hw/sd/aspeed_sdhci.c @@ -198,16 +198,13 @@ static void aspeed_sdhci_class_init(ObjectClass *classp, void *data) device_class_set_props(dc, aspeed_sdhci_properties); } -static const TypeInfo aspeed_sdhci_info = { - .name = TYPE_ASPEED_SDHCI, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(AspeedSDHCIState), - .class_init = aspeed_sdhci_class_init, +static const TypeInfo aspeed_sdhci_types[] = { + { + .name = TYPE_ASPEED_SDHCI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AspeedSDHCIState), + .class_init = aspeed_sdhci_class_init, + }, }; -static void aspeed_sdhci_register_types(void) -{ - type_register_static(&aspeed_sdhci_info); -} - -type_init(aspeed_sdhci_register_types) +DEFINE_TYPES(aspeed_sdhci_types) diff --git a/hw/sd/bcm2835_sdhost.c b/hw/sd/bcm2835_sdhost.c index 9431c35914..a600cf39e2 100644 --- a/hw/sd/bcm2835_sdhost.c +++ b/hw/sd/bcm2835_sdhost.c @@ -436,24 +436,19 @@ static void bcm2835_sdhost_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_bcm2835_sdhost; } -static const TypeInfo bcm2835_sdhost_info = { - .name = TYPE_BCM2835_SDHOST, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(BCM2835SDHostState), - .class_init = bcm2835_sdhost_class_init, - .instance_init = bcm2835_sdhost_init, +static const TypeInfo bcm2835_sdhost_types[] = { + { + .name = TYPE_BCM2835_SDHOST, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(BCM2835SDHostState), + .class_init = bcm2835_sdhost_class_init, + .instance_init = bcm2835_sdhost_init, + }, + { + .name = TYPE_BCM2835_SDHOST_BUS, + .parent = TYPE_SD_BUS, + .instance_size = sizeof(SDBus), + }, }; -static const TypeInfo bcm2835_sdhost_bus_info = { - .name = TYPE_BCM2835_SDHOST_BUS, - .parent = TYPE_SD_BUS, - .instance_size = sizeof(SDBus), -}; - -static void bcm2835_sdhost_register_types(void) -{ - type_register_static(&bcm2835_sdhost_info); - type_register_static(&bcm2835_sdhost_bus_info); -} - -type_init(bcm2835_sdhost_register_types) +DEFINE_TYPES(bcm2835_sdhost_types) diff --git a/hw/sd/cadence_sdhci.c b/hw/sd/cadence_sdhci.c index 75db34befe..ef4e0d74e3 100644 --- a/hw/sd/cadence_sdhci.c +++ b/hw/sd/cadence_sdhci.c @@ -175,17 +175,14 @@ static void cadence_sdhci_class_init(ObjectClass *classp, void *data) dc->vmsd = &vmstate_cadence_sdhci; } -static const TypeInfo cadence_sdhci_info = { - .name = TYPE_CADENCE_SDHCI, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(CadenceSDHCIState), - .instance_init = cadence_sdhci_instance_init, - .class_init = cadence_sdhci_class_init, +static const TypeInfo cadence_sdhci_types[] = { + { + .name = TYPE_CADENCE_SDHCI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(CadenceSDHCIState), + .instance_init = cadence_sdhci_instance_init, + .class_init = cadence_sdhci_class_init, + }, }; -static void cadence_sdhci_register_types(void) -{ - type_register_static(&cadence_sdhci_info); -} - -type_init(cadence_sdhci_register_types) +DEFINE_TYPES(cadence_sdhci_types) diff --git a/hw/sd/core.c b/hw/sd/core.c index 30ee62c510..52d5d90045 100644 --- a/hw/sd/core.c +++ b/hw/sd/core.c @@ -259,16 +259,13 @@ void sdbus_reparent_card(SDBus *from, SDBus *to) sdbus_set_readonly(to, readonly); } -static const TypeInfo sd_bus_info = { - .name = TYPE_SD_BUS, - .parent = TYPE_BUS, - .instance_size = sizeof(SDBus), - .class_size = sizeof(SDBusClass), +static const TypeInfo sd_bus_types[] = { + { + .name = TYPE_SD_BUS, + .parent = TYPE_BUS, + .instance_size = sizeof(SDBus), + .class_size = sizeof(SDBusClass), + }, }; -static void sd_bus_register_types(void) -{ - type_register_static(&sd_bus_info); -} - -type_init(sd_bus_register_types) +DEFINE_TYPES(sd_bus_types) diff --git a/hw/sd/npcm7xx_sdhci.c b/hw/sd/npcm7xx_sdhci.c index b2f5b4a542..9958680090 100644 --- a/hw/sd/npcm7xx_sdhci.c +++ b/hw/sd/npcm7xx_sdhci.c @@ -166,17 +166,14 @@ static void npcm7xx_sdhci_instance_init(Object *obj) TYPE_SYSBUS_SDHCI); } -static const TypeInfo npcm7xx_sdhci_info = { - .name = TYPE_NPCM7XX_SDHCI, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(NPCM7xxSDHCIState), - .instance_init = npcm7xx_sdhci_instance_init, - .class_init = npcm7xx_sdhci_class_init, +static const TypeInfo npcm7xx_sdhci_types[] = { + { + .name = TYPE_NPCM7XX_SDHCI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(NPCM7xxSDHCIState), + .instance_init = npcm7xx_sdhci_instance_init, + .class_init = npcm7xx_sdhci_class_init, + }, }; -static void npcm7xx_sdhci_register_types(void) -{ - type_register_static(&npcm7xx_sdhci_info); -} - -type_init(npcm7xx_sdhci_register_types) +DEFINE_TYPES(npcm7xx_sdhci_types) diff --git a/hw/sd/pl181.c b/hw/sd/pl181.c index 5e554bd467..2b33814d83 100644 --- a/hw/sd/pl181.c +++ b/hw/sd/pl181.c @@ -519,14 +519,6 @@ static void pl181_class_init(ObjectClass *klass, void *data) k->user_creatable = false; } -static const TypeInfo pl181_info = { - .name = TYPE_PL181, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PL181State), - .instance_init = pl181_init, - .class_init = pl181_class_init, -}; - static void pl181_bus_class_init(ObjectClass *klass, void *data) { SDBusClass *sbc = SD_BUS_CLASS(klass); @@ -535,17 +527,20 @@ static void pl181_bus_class_init(ObjectClass *klass, void *data) sbc->set_readonly = pl181_set_readonly; } -static const TypeInfo pl181_bus_info = { - .name = TYPE_PL181_BUS, - .parent = TYPE_SD_BUS, - .instance_size = sizeof(SDBus), - .class_init = pl181_bus_class_init, +static const TypeInfo pl181_info[] = { + { + .name = TYPE_PL181, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PL181State), + .instance_init = pl181_init, + .class_init = pl181_class_init, + }, + { + .name = TYPE_PL181_BUS, + .parent = TYPE_SD_BUS, + .instance_size = sizeof(SDBus), + .class_init = pl181_bus_class_init, + }, }; -static void pl181_register_types(void) -{ - type_register_static(&pl181_info); - type_register_static(&pl181_bus_info); -} - -type_init(pl181_register_types) +DEFINE_TYPES(pl181_info) diff --git a/hw/sd/pxa2xx_mmci.c b/hw/sd/pxa2xx_mmci.c index 4749e935d8..5e8ea69188 100644 --- a/hw/sd/pxa2xx_mmci.c +++ b/hw/sd/pxa2xx_mmci.c @@ -575,25 +575,20 @@ static void pxa2xx_mmci_bus_class_init(ObjectClass *klass, void *data) sbc->set_readonly = pxa2xx_mmci_set_readonly; } -static const TypeInfo pxa2xx_mmci_info = { - .name = TYPE_PXA2XX_MMCI, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PXA2xxMMCIState), - .instance_init = pxa2xx_mmci_instance_init, - .class_init = pxa2xx_mmci_class_init, +static const TypeInfo pxa2xx_mmci_types[] = { + { + .name = TYPE_PXA2XX_MMCI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PXA2xxMMCIState), + .instance_init = pxa2xx_mmci_instance_init, + .class_init = pxa2xx_mmci_class_init, + }, + { + .name = TYPE_PXA2XX_MMCI_BUS, + .parent = TYPE_SD_BUS, + .instance_size = sizeof(SDBus), + .class_init = pxa2xx_mmci_bus_class_init, + }, }; -static const TypeInfo pxa2xx_mmci_bus_info = { - .name = TYPE_PXA2XX_MMCI_BUS, - .parent = TYPE_SD_BUS, - .instance_size = sizeof(SDBus), - .class_init = pxa2xx_mmci_bus_class_init, -}; - -static void pxa2xx_mmci_register_types(void) -{ - type_register_static(&pxa2xx_mmci_info); - type_register_static(&pxa2xx_mmci_bus_info); -} - -type_init(pxa2xx_mmci_register_types) +DEFINE_TYPES(pxa2xx_mmci_types) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 4823befdef..1106ff7d78 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -2278,16 +2278,6 @@ static void sd_class_init(ObjectClass *klass, void *data) sc->proto = &sd_proto_sd; } -static const TypeInfo sd_info = { - .name = TYPE_SD_CARD, - .parent = TYPE_DEVICE, - .instance_size = sizeof(SDState), - .class_size = sizeof(SDCardClass), - .class_init = sd_class_init, - .instance_init = sd_instance_init, - .instance_finalize = sd_instance_finalize, -}; - /* * We do not model the chip select pin, so allow the board to select * whether card should be in SSI or MMC/SD mode. It is also up to the @@ -2303,16 +2293,21 @@ static void sd_spi_class_init(ObjectClass *klass, void *data) sc->proto = &sd_proto_spi; } -static const TypeInfo sd_spi_info = { - .name = TYPE_SD_CARD_SPI, - .parent = TYPE_SD_CARD, - .class_init = sd_spi_class_init, +static const TypeInfo sd_types[] = { + { + .name = TYPE_SD_CARD, + .parent = TYPE_DEVICE, + .instance_size = sizeof(SDState), + .class_size = sizeof(SDCardClass), + .class_init = sd_class_init, + .instance_init = sd_instance_init, + .instance_finalize = sd_instance_finalize, + }, + { + .name = TYPE_SD_CARD_SPI, + .parent = TYPE_SD_CARD, + .class_init = sd_spi_class_init, + }, }; -static void sd_register_types(void) -{ - type_register_static(&sd_info); - type_register_static(&sd_spi_info); -} - -type_init(sd_register_types) +DEFINE_TYPES(sd_types) diff --git a/hw/sd/sdhci-pci.c b/hw/sd/sdhci-pci.c index c737c8b930..9b7bee8b3f 100644 --- a/hw/sd/sdhci-pci.c +++ b/hw/sd/sdhci-pci.c @@ -68,20 +68,17 @@ static void sdhci_pci_class_init(ObjectClass *klass, void *data) sdhci_common_class_init(klass, data); } -static const TypeInfo sdhci_pci_info = { - .name = TYPE_PCI_SDHCI, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(SDHCIState), - .class_init = sdhci_pci_class_init, - .interfaces = (InterfaceInfo[]) { - { INTERFACE_CONVENTIONAL_PCI_DEVICE }, - { }, +static const TypeInfo sdhci_pci_types[] = { + { + .name = TYPE_PCI_SDHCI, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(SDHCIState), + .class_init = sdhci_pci_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }, }; -static void sdhci_pci_register_type(void) -{ - type_register_static(&sdhci_pci_info); -} - -type_init(sdhci_pci_register_type) +DEFINE_TYPES(sdhci_pci_types) diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c index 167c03b780..a6cc1ad6c8 100644 --- a/hw/sd/ssi-sd.c +++ b/hw/sd/ssi-sd.c @@ -403,16 +403,13 @@ static void ssi_sd_class_init(ObjectClass *klass, void *data) dc->user_creatable = false; } -static const TypeInfo ssi_sd_info = { - .name = TYPE_SSI_SD, - .parent = TYPE_SSI_PERIPHERAL, - .instance_size = sizeof(ssi_sd_state), - .class_init = ssi_sd_class_init, +static const TypeInfo ssi_sd_types[] = { + { + .name = TYPE_SSI_SD, + .parent = TYPE_SSI_PERIPHERAL, + .instance_size = sizeof(ssi_sd_state), + .class_init = ssi_sd_class_init, + }, }; -static void ssi_sd_register_types(void) -{ - type_register_static(&ssi_sd_info); -} - -type_init(ssi_sd_register_types) +DEFINE_TYPES(ssi_sd_types) From 670185cad5f1eaaef8c1949d55ccc10e9c4810dc Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 26 Oct 2023 10:00:11 +0200 Subject: [PATCH 834/974] MAINTAINERS: Add include/hw/timer/tmu012.h to the SH4 R2D section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tmu012.h is the header that belongs to hw/timer/sh_timer.c, so we should list it in the same section as sh_timer.c. Signed-off-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Yoshinori Sato Message-ID: <20231026080011.156325-1-thuth@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 67086ac6e3..da39660e24 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1617,6 +1617,7 @@ F: hw/intc/sh_intc.c F: hw/pci-host/sh_pci.c F: hw/timer/sh_timer.c F: include/hw/sh4/sh_intc.h +F: include/hw/timer/tmu012.h Shix R: Yoshinori Sato From 8995f1feeb6aaa001bcfbc61e868d8cb16e6a179 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 27 Oct 2023 08:09:31 +0200 Subject: [PATCH 835/974] MAINTAINERS: Add the CAN documentation file to the CAN section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add can.rst to the corresponding section in MAINTAINERS, so that the maintainers get CC:-ed on corresponding patches. Signed-off-by: Thomas Huth Reviewed-by: Vikram Garhwal Reviewed-by: Philippe Mathieu-Daudé Acked-by: Pavel Pisa Message-ID: <20231027060931.242491-1-thuth@redhat.com> [PMD: Fixed typo in subject] Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index da39660e24..522cd3a671 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2588,6 +2588,7 @@ W: https://canbus.pages.fel.cvut.cz/ F: net/can/* F: hw/net/can/* F: include/net/can_*.h +F: docs/system/devices/can.rst OpenPIC interrupt controller M: Mark Cave-Ayland From f5c5e7d9dba372df3ef02ee828c5041c95ddff29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 27 Oct 2023 10:56:43 +0100 Subject: [PATCH 836/974] MAINTAINERS: update libvirt devel mailing list address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Effective immediately, the libvirt project has moved its list off libvir-list@redhat.com, to devel@lists.libvirt.org Signed-off-by: Daniel P. Berrangé Message-ID: <20231027095643.2842382-1-berrange@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 522cd3a671..2a17decd31 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4057,7 +4057,7 @@ F: gitdm.config F: contrib/gitdm/* Incompatible changes -R: libvir-list@redhat.com +R: devel@lists.libvirt.org F: docs/about/deprecated.rst Build System From 2b531600381595214519e0598aa9ebac80e6a7e5 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 7 Nov 2023 11:21:04 +0100 Subject: [PATCH 837/974] MAINTAINERS: Add include/hw/xtensa/mx_pic.h to the XTFPGA machine section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These machines are the only user of the mx_pic code, so the header (which is currently "unmaintained" according to the MAINTAINERS file) should be added to this section. Signed-off-by: Thomas Huth Acked-by: Max Filippov Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20231107102104.14342-1-thuth@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 2a17decd31..2a9354e695 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1861,6 +1861,7 @@ M: Max Filippov S: Maintained F: hw/xtensa/xtfpga.c F: hw/net/opencores_eth.c +F: include/hw/xtensa/mx_pic.h Devices ------- From 51145a0d872f58a7fed32f999af39f9b10ebd38c Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 7 Nov 2023 11:18:11 +0100 Subject: [PATCH 838/974] MAINTAINERS: Add more guest-agent related files to the corresponding section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit contrib/systemd/qemu-guest-agent.service, tests/data/test-qga-config and tests/data/test-qga-os-release belong to the guest agent, so make sure that these files are covered here, too. Signed-off-by: Thomas Huth Reviewed-by: Konstantin Kostiuk Message-ID: <20231107101811.14189-1-thuth@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 2a9354e695..bc69253a25 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3145,10 +3145,11 @@ M: Michael Roth M: Konstantin Kostiuk S: Maintained F: qga/ +F: contrib/systemd/qemu-guest-agent.service F: docs/interop/qemu-ga.rst F: docs/interop/qemu-ga-ref.rst F: scripts/qemu-guest-agent/ -F: tests/unit/test-qga.c +F: tests/*/test-qga* T: git https://github.com/mdroth/qemu.git qga QEMU Guest Agent Win32 From 547ec5a0a4f697323e313062fabdebefc964eb97 Mon Sep 17 00:00:00 2001 From: Adrian Wowk Date: Sun, 29 Oct 2023 22:41:19 -0400 Subject: [PATCH 839/974] ui/sdl2: use correct key names in win title on mac MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, when using the SDL2 UI on MacOS, the title bar uses incorrect key names (such as Ctrl and Alt instead of the standard MacOS key symbols like ⌃ and ⌥). This commit changes sdl_update_caption in ui/sdl2.c to use the correct symbols when compiling for MacOS (CONFIG_DARWIN is defined). Unfortunately, standard Mac keyboards do not include a "Right-Ctrl" key, so in the case that the SDL grab mode is set to HOT_KEY_MOD_RCTRL, the default text is still used. Signed-off-by: Adrian Wowk Acked-by: Marc-André Lureau Tested-by: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20231030024119.28342-1-dev@adrianwowk.com> Signed-off-by: Philippe Mathieu-Daudé --- ui/sdl2.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ui/sdl2.c b/ui/sdl2.c index fbfdb64e90..4971963f00 100644 --- a/ui/sdl2.c +++ b/ui/sdl2.c @@ -172,11 +172,19 @@ static void sdl_update_caption(struct sdl2_console *scon) status = " [Stopped]"; } else if (gui_grab) { if (alt_grab) { +#ifdef CONFIG_DARWIN + status = " - Press ⌃⌥⇧G to exit grab"; +#else status = " - Press Ctrl-Alt-Shift-G to exit grab"; +#endif } else if (ctrl_grab) { status = " - Press Right-Ctrl-G to exit grab"; } else { +#ifdef CONFIG_DARWIN + status = " - Press ⌃⌥G to exit grab"; +#else status = " - Press Ctrl-Alt-G to exit grab"; +#endif } } From 95a40c44501b5e3b8d1922ea37f30142981b2b34 Mon Sep 17 00:00:00 2001 From: Zongmin Zhou Date: Tue, 7 Nov 2023 10:44:17 +0800 Subject: [PATCH 840/974] dump: Add close fd on error return to avoid resource leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported-by: Coverity CID 1523842 (RESOURCE_LEAK) Fixes: e6549197f7 ("dump: Add command interface for kdump-raw formats") Signed-off-by: Zongmin Zhou Reviewed-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20231107024417.585475-1-min_halo@163.com> Signed-off-by: Philippe Mathieu-Daudé --- dump/dump.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dump/dump.c b/dump/dump.c index 1c304cadfd..ad5294e853 100644 --- a/dump/dump.c +++ b/dump/dump.c @@ -2160,6 +2160,7 @@ void qmp_dump_guest_memory(bool paging, const char *protocol, return; } if (kdump_raw && lseek(fd, 0, SEEK_CUR) == (off_t) -1) { + close(fd); error_setg(errp, "kdump-raw formats require a seekable file"); return; } From e158f8e92df5782f41b61adb6fa43bed70879f7f Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 1 Sep 2023 14:32:26 +0200 Subject: [PATCH 841/974] crypto/rsakey-builtin.c.inc: Clean up two error paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When qcrypto_builtin_rsa_public_key_parse() is about to fail, but no error has been set, it makes one up. Actually, there's just one way to fail without setting an error. Set it there instead. Same for qcrypto_builtin_rsa_private_key_parse(). Signed-off-by: Markus Armbruster Signed-off-by: Daniel P. Berrangé --- crypto/rsakey-builtin.c.inc | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/crypto/rsakey-builtin.c.inc b/crypto/rsakey-builtin.c.inc index aeeacc8f9b..46cc7afe87 100644 --- a/crypto/rsakey-builtin.c.inc +++ b/crypto/rsakey-builtin.c.inc @@ -88,15 +88,13 @@ static QCryptoAkCipherRSAKey *qcrypto_builtin_rsa_public_key_parse( goto error; } if (seq_length != 0) { + error_setg(errp, "Invalid RSA public key"); goto error; } return rsa; error: - if (errp && !*errp) { - error_setg(errp, "Invalid RSA public key"); - } qcrypto_akcipher_rsakey_free(rsa); return NULL; } @@ -169,15 +167,13 @@ static QCryptoAkCipherRSAKey *qcrypto_builtin_rsa_private_key_parse( return rsa; } if (seq_length != 0) { + error_setg(errp, "Invalid RSA private key"); goto error; } return rsa; error: - if (errp && !*errp) { - error_setg(errp, "Invalid RSA private key"); - } qcrypto_akcipher_rsakey_free(rsa); return NULL; } From 9c636e0f9644d0778c16f460e0645e2352f42a7a Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Mon, 4 Sep 2023 11:41:08 -0300 Subject: [PATCH 842/974] io: Stop appending -listen to net listeners MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All callers of qio_net_listener_set_name() already add some sort of "listen" or "listener" suffix. For intance, we currently have "migration-socket-listener-listen" and "vnc-listen-listen" as ioc names. Signed-off-by: Fabiano Rosas Signed-off-by: Daniel P. Berrangé --- io/net-listener.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/io/net-listener.c b/io/net-listener.c index 1c984d69c6..47405965a6 100644 --- a/io/net-listener.c +++ b/io/net-listener.c @@ -109,9 +109,7 @@ void qio_net_listener_add(QIONetListener *listener, QIOChannelSocket *sioc) { if (listener->name) { - char *name = g_strdup_printf("%s-listen", listener->name); - qio_channel_set_name(QIO_CHANNEL(sioc), name); - g_free(name); + qio_channel_set_name(QIO_CHANNEL(sioc), listener->name); } listener->sioc = g_renew(QIOChannelSocket *, listener->sioc, From 5b4edd7230cd3010a05c6bc34c5465215275b12a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 3 Mar 2021 17:43:13 +0000 Subject: [PATCH 843/974] audio: don't abort on f32 audio format in wav backend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Print a debug message as is done for other unsupported audio formats to give the user the chance to understand their mistake. Reviewed-by: Marc-André Lureau Signed-off-by: Daniel P. Berrangé --- audio/wavaudio.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/audio/wavaudio.c b/audio/wavaudio.c index ea20fed0cc..a8798a1c42 100644 --- a/audio/wavaudio.c +++ b/audio/wavaudio.c @@ -97,6 +97,10 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as, dolog ("WAVE files can not handle 32bit formats\n"); return -1; + case AUDIO_FORMAT_F32: + dolog("WAVE files can not handle float formats\n"); + return -1; + default: abort(); } From cc9118f240e125d33d9d0d31dcd0fa017683b042 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 27 Oct 2023 10:55:22 +0100 Subject: [PATCH 844/974] MAINTAINERS: update libvirt devel mailing list address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Effective immediately, the libvirt project has moved its list off libvir-list@redhat.com, to devel@lists.libvirt.org Signed-off-by: Daniel P. Berrangé --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index b86ea7f75a..751860e064 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4062,7 +4062,7 @@ F: gitdm.config F: contrib/gitdm/* Incompatible changes -R: libvir-list@redhat.com +R: devel@lists.libvirt.org F: docs/about/deprecated.rst Build System From 97d3b2cd36d46b033a5f4219084ab929a5bac9d3 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 27 Oct 2023 13:54:49 +0200 Subject: [PATCH 845/974] hw/pci-host: Add emulation of Mai Logic Articia S The Articia S is a generic chipset supporting several different CPUs that were among others used on some PPC boards. This is a minimal emulation of the parts needed for emulating the AmigaOne board. Signed-off-by: BALATON Zoltan Tested-by: Rene Engel Acked-by: Daniel Henrique Barboza Message-ID: <83822787431701cf4d460298d3e3845f362e5da1.1698406922.git.balaton@eik.bme.hu> Signed-off-by: Daniel Henrique Barboza --- hw/pci-host/Kconfig | 5 + hw/pci-host/articia.c | 293 ++++++++++++++++++++++++++++++++++ hw/pci-host/meson.build | 2 + include/hw/pci-host/articia.h | 17 ++ 4 files changed, 317 insertions(+) create mode 100644 hw/pci-host/articia.c create mode 100644 include/hw/pci-host/articia.h diff --git a/hw/pci-host/Kconfig b/hw/pci-host/Kconfig index 54a609d2ca..f046d76a68 100644 --- a/hw/pci-host/Kconfig +++ b/hw/pci-host/Kconfig @@ -73,6 +73,11 @@ config SH_PCI bool select PCI +config ARTICIA + bool + select PCI + select I8259 + config MV64361 bool select PCI diff --git a/hw/pci-host/articia.c b/hw/pci-host/articia.c new file mode 100644 index 0000000000..f3fcc49f81 --- /dev/null +++ b/hw/pci-host/articia.c @@ -0,0 +1,293 @@ +/* + * Mai Logic Articia S emulation + * + * Copyright (c) 2023 BALATON Zoltan + * + * This work is licensed under the GNU GPL license version 2 or later. + * + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "hw/pci/pci_device.h" +#include "hw/pci/pci_host.h" +#include "hw/irq.h" +#include "hw/i2c/bitbang_i2c.h" +#include "hw/intc/i8259.h" +#include "hw/pci-host/articia.h" + +/* + * This is a minimal emulation of this chip as used in AmigaOne board. + * Most features are missing but those are not needed by firmware and guests. + */ + +OBJECT_DECLARE_SIMPLE_TYPE(ArticiaState, ARTICIA) + +OBJECT_DECLARE_SIMPLE_TYPE(ArticiaHostState, ARTICIA_PCI_HOST) +struct ArticiaHostState { + PCIDevice parent_obj; + + ArticiaState *as; +}; + +/* TYPE_ARTICIA */ + +struct ArticiaState { + PCIHostState parent_obj; + + qemu_irq irq[PCI_NUM_PINS]; + MemoryRegion io; + MemoryRegion mem; + MemoryRegion reg; + + bitbang_i2c_interface smbus; + uint32_t gpio; /* bits 0-7 in, 8-15 out, 16-23 direction (0 in, 1 out) */ + hwaddr gpio_base; + MemoryRegion gpio_reg; +}; + +static uint64_t articia_gpio_read(void *opaque, hwaddr addr, unsigned int size) +{ + ArticiaState *s = opaque; + + return (s->gpio >> (addr * 8)) & 0xff; +} + +static void articia_gpio_write(void *opaque, hwaddr addr, uint64_t val, + unsigned int size) +{ + ArticiaState *s = opaque; + uint32_t sh = addr * 8; + + if (addr == 0) { + /* in bits read only? */ + return; + } + + if ((s->gpio & (0xff << sh)) != (val & 0xff) << sh) { + s->gpio &= ~(0xff << sh | 0xff); + s->gpio |= (val & 0xff) << sh; + s->gpio |= bitbang_i2c_set(&s->smbus, BITBANG_I2C_SDA, + s->gpio & BIT(16) ? + !!(s->gpio & BIT(8)) : 1); + if ((s->gpio & BIT(17))) { + s->gpio &= ~BIT(0); + s->gpio |= bitbang_i2c_set(&s->smbus, BITBANG_I2C_SCL, + !!(s->gpio & BIT(9))); + } + } +} + +static const MemoryRegionOps articia_gpio_ops = { + .read = articia_gpio_read, + .write = articia_gpio_write, + .valid.min_access_size = 1, + .valid.max_access_size = 1, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static uint64_t articia_reg_read(void *opaque, hwaddr addr, unsigned int size) +{ + ArticiaState *s = opaque; + uint64_t ret = UINT_MAX; + + switch (addr) { + case 0xc00cf8: + ret = pci_host_conf_le_ops.read(PCI_HOST_BRIDGE(s), 0, size); + break; + case 0xe00cfc ... 0xe00cff: + ret = pci_host_data_le_ops.read(PCI_HOST_BRIDGE(s), addr - 0xe00cfc, size); + break; + case 0xf00000: + ret = pic_read_irq(isa_pic); + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: Unimplemented register read 0x%" + HWADDR_PRIx " %d\n", __func__, addr, size); + break; + } + return ret; +} + +static void articia_reg_write(void *opaque, hwaddr addr, uint64_t val, + unsigned int size) +{ + ArticiaState *s = opaque; + + switch (addr) { + case 0xc00cf8: + pci_host_conf_le_ops.write(PCI_HOST_BRIDGE(s), 0, val, size); + break; + case 0xe00cfc ... 0xe00cff: + pci_host_data_le_ops.write(PCI_HOST_BRIDGE(s), addr, val, size); + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: Unimplemented register write 0x%" + HWADDR_PRIx " %d <- %"PRIx64"\n", __func__, addr, size, val); + break; + } +} + +static const MemoryRegionOps articia_reg_ops = { + .read = articia_reg_read, + .write = articia_reg_write, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void articia_pcihost_set_irq(void *opaque, int n, int level) +{ + ArticiaState *s = opaque; + qemu_set_irq(s->irq[n], level); +} + +/* + * AmigaOne SE PCI slot to IRQ routing + * + * repository: https://source.denx.de/u-boot/custodians/u-boot-avr32.git + * refspec: v2010.06 + * file: board/MAI/AmigaOneG3SE/articiaS_pci.c + */ +static int amigaone_pcihost_bus0_map_irq(PCIDevice *pdev, int pin) +{ + int devfn_slot = PCI_SLOT(pdev->devfn); + + switch (devfn_slot) { + case 6: /* On board ethernet */ + return 3; + case 7: /* South bridge */ + return pin; + default: /* PCI Slot 1 Devfn slot 8, Slot 2 Devfn 9, Slot 3 Devfn 10 */ + return pci_swizzle(devfn_slot, pin); + } + +} + +static void articia_realize(DeviceState *dev, Error **errp) +{ + ArticiaState *s = ARTICIA(dev); + PCIHostState *h = PCI_HOST_BRIDGE(dev); + PCIDevice *pdev; + + bitbang_i2c_init(&s->smbus, i2c_init_bus(dev, "smbus")); + memory_region_init_io(&s->gpio_reg, OBJECT(s), &articia_gpio_ops, s, + TYPE_ARTICIA, 4); + + memory_region_init(&s->mem, OBJECT(dev), "pci-mem", UINT64_MAX); + memory_region_init(&s->io, OBJECT(dev), "pci-io", 0xc00000); + memory_region_init_io(&s->reg, OBJECT(s), &articia_reg_ops, s, + TYPE_ARTICIA, 0x1000000); + memory_region_add_subregion_overlap(&s->reg, 0, &s->io, 1); + + /* devfn_min is 8 that matches first PCI slot in AmigaOne */ + h->bus = pci_register_root_bus(dev, NULL, articia_pcihost_set_irq, + amigaone_pcihost_bus0_map_irq, dev, &s->mem, + &s->io, PCI_DEVFN(8, 0), 4, TYPE_PCI_BUS); + pdev = pci_create_simple_multifunction(h->bus, PCI_DEVFN(0, 0), + TYPE_ARTICIA_PCI_HOST); + ARTICIA_PCI_HOST(pdev)->as = s; + pci_create_simple(h->bus, PCI_DEVFN(0, 1), TYPE_ARTICIA_PCI_BRIDGE); + + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->reg); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mem); + qdev_init_gpio_out(dev, s->irq, ARRAY_SIZE(s->irq)); +} + +static void articia_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = articia_realize; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); +} + +/* TYPE_ARTICIA_PCI_HOST */ + +static void articia_pci_host_cfg_write(PCIDevice *d, uint32_t addr, + uint32_t val, int len) +{ + ArticiaState *s = ARTICIA_PCI_HOST(d)->as; + + pci_default_write_config(d, addr, val, len); + switch (addr) { + case 0x40: + s->gpio_base = val; + break; + case 0x44: + if (val != 0x11) { + /* FIXME what do the bits actually mean? */ + break; + } + if (memory_region_is_mapped(&s->gpio_reg)) { + memory_region_del_subregion(&s->io, &s->gpio_reg); + } + memory_region_add_subregion(&s->io, s->gpio_base + 0x38, &s->gpio_reg); + break; + } +} + +static void articia_pci_host_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->config_write = articia_pci_host_cfg_write; + k->vendor_id = 0x10cc; + k->device_id = 0x0660; + k->class_id = PCI_CLASS_BRIDGE_HOST; + /* + * PCI-facing part of the host bridge, + * not usable without the host-facing part + */ + dc->user_creatable = false; +} + +/* TYPE_ARTICIA_PCI_BRIDGE */ + +static void articia_pci_bridge_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->vendor_id = 0x10cc; + k->device_id = 0x0661; + k->class_id = PCI_CLASS_BRIDGE_HOST; + /* + * PCI-facing part of the host bridge, + * not usable without the host-facing part + */ + dc->user_creatable = false; +} + +static const TypeInfo articia_types[] = { + { + .name = TYPE_ARTICIA, + .parent = TYPE_PCI_HOST_BRIDGE, + .instance_size = sizeof(ArticiaState), + .class_init = articia_class_init, + }, + { + .name = TYPE_ARTICIA_PCI_HOST, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(ArticiaHostState), + .class_init = articia_pci_host_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, + }, + { + .name = TYPE_ARTICIA_PCI_BRIDGE, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCIDevice), + .class_init = articia_pci_bridge_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, + }, +}; + +DEFINE_TYPES(articia_types) diff --git a/hw/pci-host/meson.build b/hw/pci-host/meson.build index f891f026cb..de7bfb5a62 100644 --- a/hw/pci-host/meson.build +++ b/hw/pci-host/meson.build @@ -20,6 +20,8 @@ pci_ss.add(when: 'CONFIG_GRACKLE_PCI', if_true: files('grackle.c')) pci_ss.add(when: 'CONFIG_UNIN_PCI', if_true: files('uninorth.c')) # PowerPC E500 boards pci_ss.add(when: 'CONFIG_PPCE500_PCI', if_true: files('ppce500.c')) +# AmigaOne +pci_ss.add(when: 'CONFIG_ARTICIA', if_true: files('articia.c')) # Pegasos2 pci_ss.add(when: 'CONFIG_MV64361', if_true: files('mv64361.c')) diff --git a/include/hw/pci-host/articia.h b/include/hw/pci-host/articia.h new file mode 100644 index 0000000000..529c240274 --- /dev/null +++ b/include/hw/pci-host/articia.h @@ -0,0 +1,17 @@ +/* + * Mai Logic Articia S emulation + * + * Copyright (c) 2023 BALATON Zoltan + * + * This work is licensed under the GNU GPL license version 2 or later. + * + */ + +#ifndef ARTICIA_H +#define ARTICIA_H + +#define TYPE_ARTICIA "articia" +#define TYPE_ARTICIA_PCI_HOST "articia-pci-host" +#define TYPE_ARTICIA_PCI_BRIDGE "articia-pci-bridge" + +#endif From 3e7ebf58e808afb422e5000bbf77dc4aa88dd6e6 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 18 Oct 2023 18:00:11 +0800 Subject: [PATCH 846/974] scripts/cpu-x86-uarch-abi.py: Fix parameter error of cmd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When run this script, there's the error: python3 scripts/cpu-x86-uarch-abi.py /tmp/qmp Traceback (most recent call last): File "/path-to-qemu/qemu/scripts/cpu-x86-uarch-abi.py", line 96, in cpu = shell.cmd("query-cpu-model-expansion", TypeError: QEMUMonitorProtocol.cmd() takes 2 positional arguments but 3 were given Commit 7f521b023bc28 ("scripts/cpu-x86-uarch-abi.py: use .command() instead of .cmd()") converts the the original .cmd() to .command() (which was later renamed to "cmd" to replace the original one). But the new .cmd() only accepts typing.Mapping as the parameter instead of typing.Dict (see _qmp.execute()). Change the paremeters of "query-cpu-model-expansion" to typing.Mapping format to fix this error. Fixes: 7f521b023bc28 ("scripts/cpu-x86-uarch-abi.py: use .command() instead of .cmd()") Reviewed-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Zhao Liu Signed-off-by: Daniel P. Berrangé --- scripts/cpu-x86-uarch-abi.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/cpu-x86-uarch-abi.py b/scripts/cpu-x86-uarch-abi.py index f6baeeff24..052ddd7514 100644 --- a/scripts/cpu-x86-uarch-abi.py +++ b/scripts/cpu-x86-uarch-abi.py @@ -94,8 +94,8 @@ models = {} for name in sorted(names): cpu = shell.cmd("query-cpu-model-expansion", - { "type": "static", - "model": { "name": name }}) + type="static", + model={ "name": name }) got = {} for (feature, present) in cpu["model"]["props"].items(): From e416fd79d5d12889266259e6df6ff0d22f6f6d6b Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 7 Nov 2023 11:30:44 +0100 Subject: [PATCH 847/974] MAINTAINERS: Add artist.c to the hppa machine section The artist graphics adapter is only used by the hppa machine, so let's add this file to the corresponding section. Message-ID: <20231107103044.15089-1-thuth@redhat.com> Acked-by: Helge Deller Signed-off-by: Thomas Huth --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 2058296ede..a74ad6545b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1192,6 +1192,7 @@ M: Richard Henderson R: Helge Deller S: Odd Fixes F: configs/devices/hppa-softmmu/default.mak +F: hw/display/artist.c F: hw/hppa/ F: hw/input/lasips2.c F: hw/net/*i82596* From 221caadcc5129d3ec5ad9ecfd7374de0f7050316 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 27 Oct 2023 17:53:10 +0200 Subject: [PATCH 848/974] block: Mark bdrv_probe_blocksizes() and callers GRAPH_RDLOCK This adds GRAPH_RDLOCK annotations to declare that callers of bdrv_probe_blocksizes() need to hold a reader lock for the graph because it calls bdrv_filter_bs(), which accesses bs->file/backing. Signed-off-by: Kevin Wolf Message-ID: <20231027155333.420094-2-kwolf@redhat.com> Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- block/block-backend.c | 2 ++ block/raw-format.c | 3 ++- include/block/block-global-state.h | 2 +- include/block/block_int-common.h | 3 ++- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/block/block-backend.c b/block/block-backend.c index 39aac1bbce..53cf3bb8b8 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -2666,6 +2666,8 @@ int blk_load_vmstate(BlockBackend *blk, uint8_t *buf, int64_t pos, int size) int blk_probe_blocksizes(BlockBackend *blk, BlockSizes *bsz) { GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + if (!blk_is_available(blk)) { return -ENOMEDIUM; } diff --git a/block/raw-format.c b/block/raw-format.c index 8ff03adfa4..3fb77b0097 100644 --- a/block/raw-format.c +++ b/block/raw-format.c @@ -543,7 +543,8 @@ static int raw_probe(const uint8_t *buf, int buf_size, const char *filename) return 1; } -static int raw_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz) +static int GRAPH_RDLOCK +raw_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz) { BDRVRawState *s = bs->opaque; int ret; diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h index 6bfafe781d..fca0a40dbd 100644 --- a/include/block/block-global-state.h +++ b/include/block/block-global-state.h @@ -281,7 +281,7 @@ bool bdrv_child_change_aio_context(BdrvChild *c, AioContext *ctx, int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, BdrvChild *ignore_child, Error **errp); -int bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz); +int GRAPH_RDLOCK bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz); int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo); void GRAPH_WRLOCK diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h index b8d9d24f39..8abdd2724b 100644 --- a/include/block/block_int-common.h +++ b/include/block/block_int-common.h @@ -386,7 +386,8 @@ struct BlockDriver { * On success, store them in @bsz and return zero. * On failure, return negative errno. */ - int (*bdrv_probe_blocksizes)(BlockDriverState *bs, BlockSizes *bsz); + int GRAPH_RDLOCK_PTR (*bdrv_probe_blocksizes)( + BlockDriverState *bs, BlockSizes *bsz); /** * Try to get @bs's geometry (cyls, heads, sectors) * On success, store them in @geo and return 0. From 067179868ec8cd467d9810143339e882cb60e388 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 27 Oct 2023 17:53:11 +0200 Subject: [PATCH 849/974] block: Mark bdrv_has_zero_init() and callers GRAPH_RDLOCK This adds GRAPH_RDLOCK annotations to declare that callers of bdrv_has_zero_init() need to hold a reader lock for the graph because it calls bdrv_filter_bs(), which accesses bs->file/backing. Signed-off-by: Kevin Wolf Message-ID: <20231027155333.420094-3-kwolf@redhat.com> Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- block.c | 2 +- block/qcow2.c | 3 ++- block/raw-format.c | 2 +- block/vdi.c | 2 +- block/vhdx.c | 13 +++++++++---- block/vmdk.c | 2 +- block/vpc.c | 2 +- blockdev.c | 2 ++ include/block/block-global-state.h | 2 +- include/block/block_int-common.h | 2 +- qemu-img.c | 2 ++ 11 files changed, 22 insertions(+), 12 deletions(-) diff --git a/block.c b/block.c index a527aa1a4c..1171f47570 100644 --- a/block.c +++ b/block.c @@ -6587,7 +6587,7 @@ int bdrv_has_zero_init_1(BlockDriverState *bs) return 1; } -int bdrv_has_zero_init(BlockDriverState *bs) +int coroutine_mixed_fn bdrv_has_zero_init(BlockDriverState *bs) { BlockDriverState *filtered; GLOBAL_STATE_CODE(); diff --git a/block/qcow2.c b/block/qcow2.c index aa01d9e7b5..a1443a31aa 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -5302,7 +5302,8 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs, return spec_info; } -static int coroutine_mixed_fn qcow2_has_zero_init(BlockDriverState *bs) +static int coroutine_mixed_fn GRAPH_RDLOCK +qcow2_has_zero_init(BlockDriverState *bs) { BDRVQcow2State *s = bs->opaque; bool preallocated; diff --git a/block/raw-format.c b/block/raw-format.c index 3fb77b0097..8ca74c1cf3 100644 --- a/block/raw-format.c +++ b/block/raw-format.c @@ -452,7 +452,7 @@ raw_co_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) return bdrv_co_ioctl(bs->file->bs, req, buf); } -static int raw_has_zero_init(BlockDriverState *bs) +static int GRAPH_RDLOCK raw_has_zero_init(BlockDriverState *bs) { return bdrv_has_zero_init(bs->file->bs); } diff --git a/block/vdi.c b/block/vdi.c index 7cfd12b50d..8e144ce523 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -990,7 +990,7 @@ static void vdi_close(BlockDriverState *bs) migrate_del_blocker(&s->migration_blocker); } -static int vdi_has_zero_init(BlockDriverState *bs) +static int GRAPH_RDLOCK vdi_has_zero_init(BlockDriverState *bs) { BDRVVdiState *s = bs->opaque; diff --git a/block/vhdx.c b/block/vhdx.c index a9d08742f9..e136ba1ae1 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -1695,7 +1695,7 @@ exit: * Fixed images: default state of the BAT is fully populated, with * file offsets and state PAYLOAD_BLOCK_FULLY_PRESENT. */ -static int coroutine_fn +static int coroutine_fn GRAPH_UNLOCKED vhdx_create_bat(BlockBackend *blk, BDRVVHDXState *s, uint64_t image_size, VHDXImageType type, bool use_zero_blocks, uint64_t file_offset, @@ -1708,6 +1708,7 @@ vhdx_create_bat(BlockBackend *blk, BDRVVHDXState *s, uint64_t unused; int block_state; VHDXSectorInfo sinfo; + bool has_zero_init; assert(s->bat == NULL); @@ -1737,9 +1738,13 @@ vhdx_create_bat(BlockBackend *blk, BDRVVHDXState *s, goto exit; } + bdrv_graph_co_rdlock(); + has_zero_init = bdrv_has_zero_init(blk_bs(blk)); + bdrv_graph_co_rdunlock(); + if (type == VHDX_TYPE_FIXED || use_zero_blocks || - bdrv_has_zero_init(blk_bs(blk)) == 0) { + has_zero_init == 0) { /* for a fixed file, the default BAT entry is not zero */ s->bat = g_try_malloc0(length); if (length && s->bat == NULL) { @@ -1782,7 +1787,7 @@ exit: * to create the BAT itself, we will also cause the BAT to be * created. */ -static int coroutine_fn +static int coroutine_fn GRAPH_UNLOCKED vhdx_create_new_region_table(BlockBackend *blk, uint64_t image_size, uint32_t block_size, uint32_t sector_size, uint32_t log_size, bool use_zero_blocks, @@ -2173,7 +2178,7 @@ static int coroutine_fn vhdx_co_check(BlockDriverState *bs, return 0; } -static int vhdx_has_zero_init(BlockDriverState *bs) +static int GRAPH_RDLOCK vhdx_has_zero_init(BlockDriverState *bs) { BDRVVHDXState *s = bs->opaque; int state; diff --git a/block/vmdk.c b/block/vmdk.c index 85864b8045..91ed7a8d93 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -2894,7 +2894,7 @@ vmdk_co_get_allocated_file_size(BlockDriverState *bs) return ret; } -static int vmdk_has_zero_init(BlockDriverState *bs) +static int GRAPH_RDLOCK vmdk_has_zero_init(BlockDriverState *bs) { int i; BDRVVmdkState *s = bs->opaque; diff --git a/block/vpc.c b/block/vpc.c index aa1a48ae0e..483775103c 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -1170,7 +1170,7 @@ fail: } -static int vpc_has_zero_init(BlockDriverState *bs) +static int GRAPH_RDLOCK vpc_has_zero_init(BlockDriverState *bs) { BDRVVPCState *s = bs->opaque; diff --git a/blockdev.c b/blockdev.c index e9b7e38dc4..148df99e00 100644 --- a/blockdev.c +++ b/blockdev.c @@ -3156,9 +3156,11 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) return; } + bdrv_graph_rdlock_main_loop(); zero_target = (arg->sync == MIRROR_SYNC_MODE_FULL && (arg->mode == NEW_IMAGE_MODE_EXISTING || !bdrv_has_zero_init(target_bs))); + bdrv_graph_rdunlock_main_loop(); /* Honor bdrv_try_change_aio_context() context acquisition requirements. */ diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h index fca0a40dbd..3ae468ea15 100644 --- a/include/block/block-global-state.h +++ b/include/block/block-global-state.h @@ -189,7 +189,7 @@ void bdrv_drain_all(void); void bdrv_aio_cancel(BlockAIOCB *acb); int bdrv_has_zero_init_1(BlockDriverState *bs); -int bdrv_has_zero_init(BlockDriverState *bs); +int coroutine_mixed_fn GRAPH_RDLOCK bdrv_has_zero_init(BlockDriverState *bs); BlockDriverState *bdrv_find_node(const char *node_name); BlockDeviceInfoList *bdrv_named_nodes_list(bool flat, Error **errp); XDbgBlockGraph * GRAPH_RDLOCK bdrv_get_xdbg_block_graph(Error **errp); diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h index 8abdd2724b..c0db862de7 100644 --- a/include/block/block_int-common.h +++ b/include/block/block_int-common.h @@ -349,7 +349,7 @@ struct BlockDriver { * Returns 1 if newly created images are guaranteed to contain only * zeros, 0 otherwise. */ - int (*bdrv_has_zero_init)(BlockDriverState *bs); + int GRAPH_RDLOCK_PTR (*bdrv_has_zero_init)(BlockDriverState *bs); /* * Remove fd handlers, timers, and other event loop callbacks so the event diff --git a/qemu-img.c b/qemu-img.c index 369c2e8ddf..c061fd0634 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -2099,7 +2099,9 @@ static int convert_do_copy(ImgConvertState *s) /* Check whether we have zero initialisation or can get it efficiently */ if (!s->has_zero_init && s->target_is_new && s->min_sparse && !s->target_has_backing) { + bdrv_graph_rdlock_main_loop(); s->has_zero_init = bdrv_has_zero_init(blk_bs(s->target)); + bdrv_graph_rdunlock_main_loop(); } /* Allocate buffer for copied data. For compressed images, only one cluster From f5a3a270fe2b89d1be39f50ebdd15db80d59014b Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 27 Oct 2023 17:53:12 +0200 Subject: [PATCH 850/974] block: Mark bdrv_filter_bs() and callers GRAPH_RDLOCK This adds GRAPH_RDLOCK annotations to declare that callers of bdrv_filter_bs() need to hold a reader lock for the graph because it calls bdrv_filter_child(), which accesses bs->file/backing. Signed-off-by: Kevin Wolf Message-ID: <20231027155333.420094-4-kwolf@redhat.com> Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- block.c | 9 +++++++-- block/stream.c | 2 ++ include/block/block-io.h | 2 +- include/block/block_int-io.h | 3 ++- migration/block-dirty-bitmap.c | 4 ++++ 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/block.c b/block.c index 1171f47570..d85738b7dc 100644 --- a/block.c +++ b/block.c @@ -820,12 +820,17 @@ int bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz) int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo) { BlockDriver *drv = bs->drv; - BlockDriverState *filtered = bdrv_filter_bs(bs); + BlockDriverState *filtered; + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); if (drv && drv->bdrv_probe_geometry) { return drv->bdrv_probe_geometry(bs, geo); - } else if (filtered) { + } + + filtered = bdrv_filter_bs(bs); + if (filtered) { return bdrv_probe_geometry(filtered, geo); } diff --git a/block/stream.c b/block/stream.c index ddaab7dbbd..b22d9c236b 100644 --- a/block/stream.c +++ b/block/stream.c @@ -268,6 +268,8 @@ void stream_start(const char *job_id, BlockDriverState *bs, assert(!bottom->drv->is_filter); base_overlay = above_base = bottom; } else { + GRAPH_RDLOCK_GUARD_MAINLOOP(); + base_overlay = bdrv_find_overlay(bs, base); if (!base_overlay) { error_setg(errp, "'%s' is not in the backing chain of '%s'", diff --git a/include/block/block-io.h b/include/block/block-io.h index ad270b6ad2..58c4cf50a0 100644 --- a/include/block/block-io.h +++ b/include/block/block-io.h @@ -183,7 +183,7 @@ bdrv_co_eject(BlockDriverState *bs, bool eject_flag); const char *bdrv_get_format_name(BlockDriverState *bs); -bool bdrv_supports_compressed_writes(BlockDriverState *bs); +bool GRAPH_RDLOCK bdrv_supports_compressed_writes(BlockDriverState *bs); const char *bdrv_get_node_name(const BlockDriverState *bs); const char * GRAPH_RDLOCK diff --git a/include/block/block_int-io.h b/include/block/block_int-io.h index 34eac72d7a..26bff94e4e 100644 --- a/include/block/block_int-io.h +++ b/include/block/block_int-io.h @@ -143,7 +143,8 @@ static inline BlockDriverState *bdrv_cow_bs(BlockDriverState *bs) return child_bs(bdrv_cow_child(bs)); } -static inline BlockDriverState *bdrv_filter_bs(BlockDriverState *bs) +static inline BlockDriverState * GRAPH_RDLOCK +bdrv_filter_bs(BlockDriverState *bs) { IO_CODE(); return child_bs(bdrv_filter_child(bs)); diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c index 03cb2e72ee..24347ab0f7 100644 --- a/migration/block-dirty-bitmap.c +++ b/migration/block-dirty-bitmap.c @@ -607,6 +607,10 @@ static int init_dirty_bitmap_migration(DBMSaveState *s) BlockBackend *blk; GHashTable *alias_map = NULL; + /* Runs in the migration thread, but holds the iothread lock */ + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + if (migrate_has_block_bitmap_mapping()) { alias_map = construct_alias_map(migrate_block_bitmap_mapping(), true, &error_abort); From 03b9eaca540322dd6dd4810aa57f3196ce971542 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 27 Oct 2023 17:53:13 +0200 Subject: [PATCH 851/974] block: Mark bdrv_root_attach_child() GRAPH_WRLOCK Instead of taking the writer lock internally, require callers to already hold it when calling bdrv_root_attach_child(). These callers will typically already hold the graph lock once the locking work is completed, which means that they can't call functions that take it internally. Signed-off-by: Kevin Wolf Message-ID: <20231027155333.420094-5-kwolf@redhat.com> Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- block.c | 5 +---- block/block-backend.c | 2 ++ blockjob.c | 2 ++ include/block/block_int-global-state.h | 13 +++++++------ 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/block.c b/block.c index d85738b7dc..5f92eb4950 100644 --- a/block.c +++ b/block.c @@ -3214,8 +3214,6 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, GLOBAL_STATE_CODE(); - bdrv_graph_wrlock(child_bs); - child = bdrv_attach_child_common(child_bs, child_name, child_class, child_role, perm, shared_perm, opaque, tran, errp); @@ -3228,9 +3226,8 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, out: tran_finalize(tran, ret); - bdrv_graph_wrunlock(); - bdrv_unref(child_bs); + bdrv_schedule_unref(child_bs); return ret < 0 ? NULL : child; } diff --git a/block/block-backend.c b/block/block-backend.c index 53cf3bb8b8..075a0dfa95 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -931,10 +931,12 @@ int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp) ThrottleGroupMember *tgm = &blk->public.throttle_group_member; GLOBAL_STATE_CODE(); bdrv_ref(bs); + bdrv_graph_wrlock(bs); blk->root = bdrv_root_attach_child(bs, "root", &child_root, BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, blk->perm, blk->shared_perm, blk, errp); + bdrv_graph_wrunlock(); if (blk->root == NULL) { return -EPERM; } diff --git a/blockjob.c b/blockjob.c index 5b24de356d..7920f6e500 100644 --- a/blockjob.c +++ b/blockjob.c @@ -248,8 +248,10 @@ int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, } aio_context_acquire(ctx); } + bdrv_graph_wrlock(bs); c = bdrv_root_attach_child(bs, name, &child_job, 0, perm, shared_perm, job, errp); + bdrv_graph_wrunlock(); if (need_context_ops) { aio_context_release(ctx); if (job->job.aio_context != qemu_get_aio_context()) { diff --git a/include/block/block_int-global-state.h b/include/block/block_int-global-state.h index 074b677838..afce6c4416 100644 --- a/include/block/block_int-global-state.h +++ b/include/block/block_int-global-state.h @@ -196,12 +196,13 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, BlockCompletionFunc *cb, void *opaque, JobTxn *txn, Error **errp); -BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, - const char *child_name, - const BdrvChildClass *child_class, - BdrvChildRole child_role, - uint64_t perm, uint64_t shared_perm, - void *opaque, Error **errp); +BdrvChild * GRAPH_WRLOCK +bdrv_root_attach_child(BlockDriverState *child_bs, const char *child_name, + const BdrvChildClass *child_class, + BdrvChildRole child_role, + uint64_t perm, uint64_t shared_perm, + void *opaque, Error **errp); + void GRAPH_WRLOCK bdrv_root_unref_child(BdrvChild *child); void GRAPH_RDLOCK bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm, From f3bbc53dc56c5d410f76442da6ad15ec8f9439fc Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 27 Oct 2023 17:53:14 +0200 Subject: [PATCH 852/974] block: Mark block_job_add_bdrv() GRAPH_WRLOCK Instead of taking the writer lock internally, require callers to already hold it when calling block_job_add_bdrv(). These callers will typically already hold the graph lock once the locking work is completed, which means that they can't call functions that take it internally. Signed-off-by: Kevin Wolf Message-ID: <20231027155333.420094-6-kwolf@redhat.com> Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- block/backup.c | 21 +++++++++++++++------ block/commit.c | 5 +++++ block/mirror.c | 5 +++++ block/stream.c | 4 ++++ blockjob.c | 8 +++++--- include/block/blockjob.h | 5 +++-- include/block/blockjob_int.h | 9 +++++---- tests/unit/test-bdrv-drain.c | 3 +++ 8 files changed, 45 insertions(+), 15 deletions(-) diff --git a/block/backup.c b/block/backup.c index 9a3c4bdc82..5bad7d116f 100644 --- a/block/backup.c +++ b/block/backup.c @@ -374,7 +374,6 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, assert(bs); assert(target); GLOBAL_STATE_CODE(); - GRAPH_RDLOCK_GUARD_MAINLOOP(); /* QMP interface protects us from these cases */ assert(sync_mode != MIRROR_SYNC_MODE_INCREMENTAL); @@ -385,31 +384,33 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, return NULL; } + bdrv_graph_rdlock_main_loop(); if (!bdrv_is_inserted(bs)) { error_setg(errp, "Device is not inserted: %s", bdrv_get_device_name(bs)); - return NULL; + goto error_rdlock; } if (!bdrv_is_inserted(target)) { error_setg(errp, "Device is not inserted: %s", bdrv_get_device_name(target)); - return NULL; + goto error_rdlock; } if (compress && !bdrv_supports_compressed_writes(target)) { error_setg(errp, "Compression is not supported for this drive %s", bdrv_get_device_name(target)); - return NULL; + goto error_rdlock; } if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) { - return NULL; + goto error_rdlock; } if (bdrv_op_is_blocked(target, BLOCK_OP_TYPE_BACKUP_TARGET, errp)) { - return NULL; + goto error_rdlock; } + bdrv_graph_rdunlock_main_loop(); if (perf->max_workers < 1 || perf->max_workers > INT_MAX) { error_setg(errp, "max-workers must be between 1 and %d", INT_MAX); @@ -437,6 +438,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, len = bdrv_getlength(bs); if (len < 0) { + GRAPH_RDLOCK_GUARD_MAINLOOP(); error_setg_errno(errp, -len, "Unable to get length for '%s'", bdrv_get_device_or_node_name(bs)); goto error; @@ -444,6 +446,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, target_len = bdrv_getlength(target); if (target_len < 0) { + GRAPH_RDLOCK_GUARD_MAINLOOP(); error_setg_errno(errp, -target_len, "Unable to get length for '%s'", bdrv_get_device_or_node_name(bs)); goto error; @@ -493,8 +496,10 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, block_copy_set_speed(bcs, speed); /* Required permissions are taken by copy-before-write filter target */ + bdrv_graph_wrlock(target); block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL, &error_abort); + bdrv_graph_wrunlock(); return &job->common; @@ -507,4 +512,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, } return NULL; + +error_rdlock: + bdrv_graph_rdunlock_main_loop(); + return NULL; } diff --git a/block/commit.c b/block/commit.c index 43d1de7577..fc3ad79749 100644 --- a/block/commit.c +++ b/block/commit.c @@ -342,6 +342,7 @@ void commit_start(const char *job_id, BlockDriverState *bs, */ iter_shared_perms = BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE; + bdrv_graph_wrlock(top); for (iter = top; iter != base; iter = bdrv_filter_or_cow_bs(iter)) { if (iter == filtered_base) { /* @@ -354,16 +355,20 @@ void commit_start(const char *job_id, BlockDriverState *bs, ret = block_job_add_bdrv(&s->common, "intermediate node", iter, 0, iter_shared_perms, errp); if (ret < 0) { + bdrv_graph_wrunlock(); goto fail; } } if (bdrv_freeze_backing_chain(commit_top_bs, base, errp) < 0) { + bdrv_graph_wrunlock(); goto fail; } s->chain_frozen = true; ret = block_job_add_bdrv(&s->common, "base", base, 0, BLK_PERM_ALL, errp); + bdrv_graph_wrunlock(); + if (ret < 0) { goto fail; } diff --git a/block/mirror.c b/block/mirror.c index c839542774..a03247a31b 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -1888,11 +1888,13 @@ static BlockJob *mirror_start_job( */ bdrv_disable_dirty_bitmap(s->dirty_bitmap); + bdrv_graph_wrlock(bs); ret = block_job_add_bdrv(&s->common, "source", bs, 0, BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE | BLK_PERM_CONSISTENT_READ, errp); if (ret < 0) { + bdrv_graph_wrunlock(); goto fail; } @@ -1937,14 +1939,17 @@ static BlockJob *mirror_start_job( ret = block_job_add_bdrv(&s->common, "intermediate node", iter, 0, iter_shared_perms, errp); if (ret < 0) { + bdrv_graph_wrunlock(); goto fail; } } if (bdrv_freeze_backing_chain(mirror_top_bs, target, errp) < 0) { + bdrv_graph_wrunlock(); goto fail; } } + bdrv_graph_wrunlock(); QTAILQ_INIT(&s->ops_in_flight); diff --git a/block/stream.c b/block/stream.c index b22d9c236b..51333e460b 100644 --- a/block/stream.c +++ b/block/stream.c @@ -352,8 +352,10 @@ void stream_start(const char *job_id, BlockDriverState *bs, * already have our own plans. Also don't allow resize as the image size is * queried only at the job start and then cached. */ + bdrv_graph_wrlock(bs); if (block_job_add_bdrv(&s->common, "active node", bs, 0, basic_flags | BLK_PERM_WRITE, errp)) { + bdrv_graph_wrunlock(); goto fail; } @@ -373,9 +375,11 @@ void stream_start(const char *job_id, BlockDriverState *bs, ret = block_job_add_bdrv(&s->common, "intermediate node", iter, 0, basic_flags, errp); if (ret < 0) { + bdrv_graph_wrunlock(); goto fail; } } + bdrv_graph_wrunlock(); s->base_overlay = base_overlay; s->above_base = above_base; diff --git a/blockjob.c b/blockjob.c index 7920f6e500..af44322cbe 100644 --- a/blockjob.c +++ b/blockjob.c @@ -248,10 +248,8 @@ int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, } aio_context_acquire(ctx); } - bdrv_graph_wrlock(bs); c = bdrv_root_attach_child(bs, name, &child_job, 0, perm, shared_perm, job, errp); - bdrv_graph_wrunlock(); if (need_context_ops) { aio_context_release(ctx); if (job->job.aio_context != qemu_get_aio_context()) { @@ -515,7 +513,8 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, BlockJob *job; int ret; GLOBAL_STATE_CODE(); - GRAPH_RDLOCK_GUARD_MAINLOOP(); + + bdrv_graph_wrlock(bs); if (job_id == NULL && !(flags & JOB_INTERNAL)) { job_id = bdrv_get_device_name(bs); @@ -524,6 +523,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, job = job_create(job_id, &driver->job_driver, txn, bdrv_get_aio_context(bs), flags, cb, opaque, errp); if (job == NULL) { + bdrv_graph_wrunlock(); return NULL; } @@ -563,9 +563,11 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, goto fail; } + bdrv_graph_wrunlock(); return job; fail: + bdrv_graph_wrunlock(); job_early_fail(&job->job); return NULL; } diff --git a/include/block/blockjob.h b/include/block/blockjob.h index 95854f1477..e594c10d23 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -138,8 +138,9 @@ BlockJob *block_job_get_locked(const char *id); * @job. This means that all operations will be blocked on @bs while * @job exists. */ -int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, - uint64_t perm, uint64_t shared_perm, Error **errp); +int GRAPH_WRLOCK +block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, + uint64_t perm, uint64_t shared_perm, Error **errp); /** * block_job_remove_all_bdrv: diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h index 18ee6f7bf0..4c3d2e25a2 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -111,10 +111,11 @@ struct BlockJobDriver { * This function is not part of the public job interface; it should be * called from a wrapper that is specific to the job type. */ -void *block_job_create(const char *job_id, const BlockJobDriver *driver, - JobTxn *txn, BlockDriverState *bs, uint64_t perm, - uint64_t shared_perm, int64_t speed, int flags, - BlockCompletionFunc *cb, void *opaque, Error **errp); +void * GRAPH_UNLOCKED +block_job_create(const char *job_id, const BlockJobDriver *driver, + JobTxn *txn, BlockDriverState *bs, uint64_t perm, + uint64_t shared_perm, int64_t speed, int flags, + BlockCompletionFunc *cb, void *opaque, Error **errp); /** * block_job_free: diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c index f67e9df01c..40d17b4c5a 100644 --- a/tests/unit/test-bdrv-drain.c +++ b/tests/unit/test-bdrv-drain.c @@ -794,7 +794,10 @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, 0, 0, NULL, NULL, &error_abort); tjob->bs = src; job = &tjob->common; + + bdrv_graph_wrlock(target); block_job_add_bdrv(job, "target", target, 0, BLK_PERM_ALL, &error_abort); + bdrv_graph_wrunlock(); switch (result) { case TEST_JOB_SUCCESS: From 372b69f503d47eb6619a98cac2ab5a6a569e3483 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 27 Oct 2023 17:53:15 +0200 Subject: [PATCH 853/974] block: Mark bdrv_filter_or_cow_bs() and callers GRAPH_RDLOCK This adds GRAPH_RDLOCK annotations to declare that callers of bdrv_filter_or_cow_bs() need to hold a reader lock for the graph because it calls bdrv_filter_or_cow_child(), which accesses bs->file/backing. Signed-off-by: Kevin Wolf Message-ID: <20231027155333.420094-7-kwolf@redhat.com> Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- block.c | 33 +++++++++++++++++++-------------- block/stream.c | 4 ++++ blockdev.c | 2 +- include/block/block_int-io.h | 3 ++- nbd/server.c | 6 ++++++ 5 files changed, 32 insertions(+), 16 deletions(-) diff --git a/block.c b/block.c index 5f92eb4950..a6060eddbc 100644 --- a/block.c +++ b/block.c @@ -5435,17 +5435,6 @@ static int bdrv_replace_node_common(BlockDriverState *from, GLOBAL_STATE_CODE(); - if (detach_subchain) { - assert(bdrv_chain_contains(from, to)); - assert(from != to); - for (to_cow_parent = from; - bdrv_filter_or_cow_bs(to_cow_parent) != to; - to_cow_parent = bdrv_filter_or_cow_bs(to_cow_parent)) - { - ; - } - } - /* Make sure that @from doesn't go away until we have successfully attached * all of its parents to @to. */ bdrv_ref(from); @@ -5457,6 +5446,17 @@ static int bdrv_replace_node_common(BlockDriverState *from, bdrv_graph_wrlock(to); + if (detach_subchain) { + assert(bdrv_chain_contains(from, to)); + assert(from != to); + for (to_cow_parent = from; + bdrv_filter_or_cow_bs(to_cow_parent) != to; + to_cow_parent = bdrv_filter_or_cow_bs(to_cow_parent)) + { + ; + } + } + /* * Do the replacement without permission update. * Replacement may influence the permissions, we should calculate new @@ -5504,10 +5504,14 @@ int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, int bdrv_drop_filter(BlockDriverState *bs, Error **errp) { - GLOBAL_STATE_CODE(); + BlockDriverState *child_bs; - return bdrv_replace_node_common(bs, bdrv_filter_or_cow_bs(bs), true, true, - errp); + GLOBAL_STATE_CODE(); + bdrv_graph_rdlock_main_loop(); + child_bs = bdrv_filter_or_cow_bs(bs); + bdrv_graph_rdunlock_main_loop(); + + return bdrv_replace_node_common(bs, child_bs, true, true, errp); } /* @@ -6509,6 +6513,7 @@ bool bdrv_chain_contains(BlockDriverState *top, BlockDriverState *base) { GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); while (top && top != base) { top = bdrv_filter_or_cow_bs(top); diff --git a/block/stream.c b/block/stream.c index 51333e460b..2781441191 100644 --- a/block/stream.c +++ b/block/stream.c @@ -60,6 +60,8 @@ static int stream_prepare(Job *job) Error *local_err = NULL; int ret = 0; + GLOBAL_STATE_CODE(); + /* We should drop filter at this point, as filter hold the backing chain */ bdrv_cor_filter_drop(s->cor_filter_bs); s->cor_filter_bs = NULL; @@ -78,8 +80,10 @@ static int stream_prepare(Job *job) bdrv_drained_begin(unfiltered_bs_cow); } + bdrv_graph_rdlock_main_loop(); base = bdrv_filter_or_cow_bs(s->above_base); unfiltered_base = bdrv_skip_filters(base); + bdrv_graph_rdunlock_main_loop(); if (bdrv_cow_child(unfiltered_bs)) { const char *base_id = NULL, *base_fmt = NULL; diff --git a/blockdev.c b/blockdev.c index 148df99e00..4cb8e1d91a 100644 --- a/blockdev.c +++ b/blockdev.c @@ -2501,8 +2501,8 @@ void qmp_block_stream(const char *job_id, const char *device, /* * Check for op blockers in the whole chain between bs and base (or bottom) */ - iter_end = bottom ? bdrv_filter_or_cow_bs(bottom_bs) : base_bs; bdrv_graph_rdlock_main_loop(); + iter_end = bottom ? bdrv_filter_or_cow_bs(bottom_bs) : base_bs; for (iter = bs; iter && iter != iter_end; iter = bdrv_filter_or_cow_bs(iter)) { diff --git a/include/block/block_int-io.h b/include/block/block_int-io.h index 26bff94e4e..6800af7590 100644 --- a/include/block/block_int-io.h +++ b/include/block/block_int-io.h @@ -150,7 +150,8 @@ bdrv_filter_bs(BlockDriverState *bs) return child_bs(bdrv_filter_child(bs)); } -static inline BlockDriverState *bdrv_filter_or_cow_bs(BlockDriverState *bs) +static inline BlockDriverState * GRAPH_RDLOCK +bdrv_filter_or_cow_bs(BlockDriverState *bs) { IO_CODE(); return child_bs(bdrv_filter_or_cow_child(bs)); diff --git a/nbd/server.c b/nbd/server.c index 859c163d19..895cf0a752 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -1689,6 +1689,7 @@ static int nbd_export_create(BlockExport *blk_exp, BlockExportOptions *exp_args, size_t i; int ret; + GLOBAL_STATE_CODE(); assert(exp_args->type == BLOCK_EXPORT_TYPE_NBD); if (!nbd_server_is_running()) { @@ -1743,6 +1744,8 @@ static int nbd_export_create(BlockExport *blk_exp, BlockExportOptions *exp_args, } exp->size = QEMU_ALIGN_DOWN(size, BDRV_SECTOR_SIZE); + bdrv_graph_rdlock_main_loop(); + for (bitmaps = arg->bitmaps; bitmaps; bitmaps = bitmaps->next) { exp->nr_export_bitmaps++; } @@ -1825,9 +1828,12 @@ static int nbd_export_create(BlockExport *blk_exp, BlockExportOptions *exp_args, QTAILQ_INSERT_TAIL(&exports, exp, next); + bdrv_graph_rdunlock_main_loop(); + return 0; fail: + bdrv_graph_rdunlock_main_loop(); g_free(exp->export_bitmaps); g_free(exp->name); g_free(exp->description); From 430da832afb6a6eebb7c1726991c60fb06322d3e Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 27 Oct 2023 17:53:16 +0200 Subject: [PATCH 854/974] block: Mark bdrv_skip_implicit_filters() and callers GRAPH_RDLOCK This adds GRAPH_RDLOCK annotations to declare that callers of bdrv_skip_implicit_filters() need to hold a reader lock for the graph because it calls bdrv_filter_child(), which accesses bs->file/backing. Signed-off-by: Kevin Wolf Message-ID: <20231027155333.420094-8-kwolf@redhat.com> Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- block.c | 28 +++++++++++++++++--------- block/monitor/block-hmp-cmds.c | 3 +++ blockdev.c | 14 +++++++------ include/block/block_int-global-state.h | 3 ++- 4 files changed, 32 insertions(+), 16 deletions(-) diff --git a/block.c b/block.c index a6060eddbc..7e8b39711b 100644 --- a/block.c +++ b/block.c @@ -4778,6 +4778,8 @@ bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, return 0; } + bdrv_graph_rdlock_main_loop(); + switch (qobject_type(value)) { case QTYPE_QNULL: assert(is_backing); /* The 'file' option does not allow a null value */ @@ -4787,17 +4789,16 @@ bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, str = qstring_get_str(qobject_to(QString, value)); new_child_bs = bdrv_lookup_bs(NULL, str, errp); if (new_child_bs == NULL) { - return -EINVAL; + ret = -EINVAL; + goto out_rdlock; } - bdrv_graph_rdlock_main_loop(); has_child = bdrv_recurse_has_child(new_child_bs, bs); - bdrv_graph_rdunlock_main_loop(); - if (has_child) { error_setg(errp, "Making '%s' a %s child of '%s' would create a " "cycle", str, child_name, bs->node_name); - return -EINVAL; + ret = -EINVAL; + goto out_rdlock; } break; default: @@ -4809,18 +4810,21 @@ bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, } if (old_child_bs == new_child_bs) { - return 0; + ret = 0; + goto out_rdlock; } if (old_child_bs) { if (bdrv_skip_implicit_filters(old_child_bs) == new_child_bs) { - return 0; + ret = 0; + goto out_rdlock; } if (old_child_bs->implicit) { error_setg(errp, "Cannot replace implicit %s child of %s", child_name, bs->node_name); - return -EPERM; + ret = -EPERM; + goto out_rdlock; } } @@ -4831,7 +4835,8 @@ bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, */ error_setg(errp, "'%s' is a %s filter node that does not support a " "%s child", bs->node_name, bs->drv->format_name, child_name); - return -EINVAL; + ret = -EINVAL; + goto out_rdlock; } if (is_backing) { @@ -4852,6 +4857,7 @@ bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, aio_context_acquire(ctx); } + bdrv_graph_rdunlock_main_loop(); bdrv_graph_wrlock(new_child_bs); ret = bdrv_set_file_or_backing_noperm(bs, new_child_bs, is_backing, @@ -4870,6 +4876,10 @@ bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, } return ret; + +out_rdlock: + bdrv_graph_rdunlock_main_loop(); + return ret; } /* diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c index 5b2c597e7a..c729cbf1eb 100644 --- a/block/monitor/block-hmp-cmds.c +++ b/block/monitor/block-hmp-cmds.c @@ -206,6 +206,9 @@ void hmp_commit(Monitor *mon, const QDict *qdict) BlockBackend *blk; int ret; + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + if (!strcmp(device, "all")) { ret = blk_commit_all(); } else { diff --git a/blockdev.c b/blockdev.c index 4cb8e1d91a..6cdf48beb1 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1746,10 +1746,10 @@ static void drive_backup_action(DriveBackup *backup, assert(format); if (source) { /* Implicit filters should not appear in the filename */ - BlockDriverState *explicit_backing = - bdrv_skip_implicit_filters(source); + BlockDriverState *explicit_backing; bdrv_graph_rdlock_main_loop(); + explicit_backing = bdrv_skip_implicit_filters(source); bdrv_refresh_filename(explicit_backing); bdrv_graph_rdunlock_main_loop(); @@ -3108,16 +3108,18 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) bdrv_img_create(arg->target, format, NULL, NULL, NULL, size, flags, false, &local_err); } else { - /* Implicit filters should not appear in the filename */ - BlockDriverState *explicit_backing = - bdrv_skip_implicit_filters(target_backing_bs); + BlockDriverState *explicit_backing; switch (arg->mode) { case NEW_IMAGE_MODE_EXISTING: break; case NEW_IMAGE_MODE_ABSOLUTE_PATHS: - /* create new image with backing file */ + /* + * Create new image with backing file. + * Implicit filters should not appear in the filename. + */ bdrv_graph_rdlock_main_loop(); + explicit_backing = bdrv_skip_implicit_filters(target_backing_bs); bdrv_refresh_filename(explicit_backing); bdrv_graph_rdunlock_main_loop(); diff --git a/include/block/block_int-global-state.h b/include/block/block_int-global-state.h index afce6c4416..ef31c58bb3 100644 --- a/include/block/block_int-global-state.h +++ b/include/block/block_int-global-state.h @@ -277,7 +277,8 @@ BdrvDirtyBitmap *block_dirty_bitmap_remove(const char *node, const char *name, Error **errp); -BlockDriverState *bdrv_skip_implicit_filters(BlockDriverState *bs); +BlockDriverState * GRAPH_RDLOCK +bdrv_skip_implicit_filters(BlockDriverState *bs); /** * bdrv_add_aio_context_notifier: From ad74751fc0ffdad7678224df0e752689ebb3f4b7 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 27 Oct 2023 17:53:17 +0200 Subject: [PATCH 855/974] block: Mark bdrv_skip_filters() and callers GRAPH_RDLOCK This adds GRAPH_RDLOCK annotations to declare that callers of bdrv_skip_filters() need to hold a reader lock for the graph because it calls bdrv_filter_child(), which accesses bs->file/backing. Signed-off-by: Kevin Wolf Message-ID: <20231027155333.420094-9-kwolf@redhat.com> Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- block/block-backend.c | 1 + block/block-copy.c | 9 +++++++- block/commit.c | 5 ++++- block/mirror.c | 34 +++++++++++++++++++++--------- block/stream.c | 22 ++++++++++++------- blockdev.c | 7 +++--- include/block/block-global-state.h | 8 ++++--- include/block/block_int-io.h | 4 ++-- qemu-img.c | 18 +++++++++++++--- 9 files changed, 77 insertions(+), 31 deletions(-) diff --git a/block/block-backend.c b/block/block-backend.c index 075a0dfa95..4053134781 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -2730,6 +2730,7 @@ int blk_commit_all(void) { BlockBackend *blk = NULL; GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); while ((blk = blk_all_next(blk)) != NULL) { AioContext *aio_context = blk_get_aio_context(blk); diff --git a/block/block-copy.c b/block/block-copy.c index 1c60368d72..6b2be3d204 100644 --- a/block/block-copy.c +++ b/block/block-copy.c @@ -313,7 +313,12 @@ static int64_t block_copy_calculate_cluster_size(BlockDriverState *target, { int ret; BlockDriverInfo bdi; - bool target_does_cow = bdrv_backing_chain_next(target); + bool target_does_cow; + + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + + target_does_cow = bdrv_backing_chain_next(target); /* * If there is no backing file on the target, we cannot rely on COW if our @@ -355,6 +360,8 @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, BdrvDirtyBitmap *copy_bitmap; bool is_fleecing; + GLOBAL_STATE_CODE(); + cluster_size = block_copy_calculate_cluster_size(target->bs, errp); if (cluster_size < 0) { return NULL; diff --git a/block/commit.c b/block/commit.c index fc3ad79749..05eb57d9ea 100644 --- a/block/commit.c +++ b/block/commit.c @@ -255,10 +255,13 @@ void commit_start(const char *job_id, BlockDriverState *bs, GLOBAL_STATE_CODE(); assert(top != bs); + bdrv_graph_rdlock_main_loop(); if (bdrv_skip_filters(top) == bdrv_skip_filters(base)) { error_setg(errp, "Invalid files for merge: top and base are the same"); + bdrv_graph_rdunlock_main_loop(); return; } + bdrv_graph_rdunlock_main_loop(); base_size = bdrv_getlength(base); if (base_size < 0) { @@ -324,6 +327,7 @@ void commit_start(const char *job_id, BlockDriverState *bs, * this is the responsibility of the interface (i.e. whoever calls * commit_start()). */ + bdrv_graph_wrlock(top); s->base_overlay = bdrv_find_overlay(top, base); assert(s->base_overlay); @@ -342,7 +346,6 @@ void commit_start(const char *job_id, BlockDriverState *bs, */ iter_shared_perms = BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE; - bdrv_graph_wrlock(top); for (iter = top; iter != base; iter = bdrv_filter_or_cow_bs(iter)) { if (iter == filtered_base) { /* diff --git a/block/mirror.c b/block/mirror.c index a03247a31b..75e826dac8 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -714,7 +714,6 @@ static int mirror_exit_common(Job *job) bdrv_graph_rdlock_main_loop(); bdrv_child_refresh_perms(mirror_top_bs, mirror_top_bs->backing, &error_abort); - bdrv_graph_rdunlock_main_loop(); if (!abort && s->backing_mode == MIRROR_SOURCE_BACKING_CHAIN) { BlockDriverState *backing = s->is_none_mode ? src : s->base; @@ -737,6 +736,7 @@ static int mirror_exit_common(Job *job) local_err = NULL; } } + bdrv_graph_rdunlock_main_loop(); if (s->to_replace) { replace_aio_context = bdrv_get_aio_context(s->to_replace); @@ -992,13 +992,13 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) } else { s->target_cluster_size = BDRV_SECTOR_SIZE; } - bdrv_graph_co_rdunlock(); if (backing_filename[0] && !bdrv_backing_chain_next(target_bs) && s->granularity < s->target_cluster_size) { s->buf_size = MAX(s->buf_size, s->target_cluster_size); s->cow_bitmap = bitmap_new(length); } s->max_iov = MIN(bs->bl.max_iov, target_bs->bl.max_iov); + bdrv_graph_co_rdunlock(); s->buf = qemu_try_blockalign(bs, s->buf_size); if (s->buf == NULL) { @@ -1744,12 +1744,15 @@ static BlockJob *mirror_start_job( buf_size = DEFAULT_MIRROR_BUF_SIZE; } + bdrv_graph_rdlock_main_loop(); if (bdrv_skip_filters(bs) == bdrv_skip_filters(target)) { error_setg(errp, "Can't mirror node into itself"); + bdrv_graph_rdunlock_main_loop(); return NULL; } target_is_backing = bdrv_chain_contains(bs, target); + bdrv_graph_rdunlock_main_loop(); /* In the case of active commit, add dummy driver to provide consistent * reads on the top, while disabling it in the intermediate nodes, and make @@ -1832,14 +1835,19 @@ static BlockJob *mirror_start_job( } target_shared_perms |= BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE; - } else if (bdrv_chain_contains(bs, bdrv_skip_filters(target))) { - /* - * We may want to allow this in the future, but it would - * require taking some extra care. - */ - error_setg(errp, "Cannot mirror to a filter on top of a node in the " - "source's backing chain"); - goto fail; + } else { + bdrv_graph_rdlock_main_loop(); + if (bdrv_chain_contains(bs, bdrv_skip_filters(target))) { + /* + * We may want to allow this in the future, but it would + * require taking some extra care. + */ + error_setg(errp, "Cannot mirror to a filter on top of a node in " + "the source's backing chain"); + bdrv_graph_rdunlock_main_loop(); + goto fail; + } + bdrv_graph_rdunlock_main_loop(); } s->target = blk_new(s->common.job.aio_context, @@ -1860,6 +1868,7 @@ static BlockJob *mirror_start_job( blk_set_allow_aio_context_change(s->target, true); blk_set_disable_request_queuing(s->target, true); + bdrv_graph_rdlock_main_loop(); s->replaces = g_strdup(replaces); s->on_source_error = on_source_error; s->on_target_error = on_target_error; @@ -1875,6 +1884,7 @@ static BlockJob *mirror_start_job( if (auto_complete) { s->should_complete = true; } + bdrv_graph_rdunlock_main_loop(); s->dirty_bitmap = bdrv_create_dirty_bitmap(s->mirror_top_bs, granularity, NULL, errp); @@ -2007,8 +2017,12 @@ void mirror_start(const char *job_id, BlockDriverState *bs, MirrorSyncMode_str(mode)); return; } + + bdrv_graph_rdlock_main_loop(); is_none_mode = mode == MIRROR_SYNC_MODE_NONE; base = mode == MIRROR_SYNC_MODE_TOP ? bdrv_backing_chain_next(bs) : NULL; + bdrv_graph_rdunlock_main_loop(); + mirror_start_job(job_id, bs, creation_flags, target, replaces, speed, granularity, buf_size, backing_mode, zero_target, on_source_error, on_target_error, unmap, NULL, NULL, diff --git a/block/stream.c b/block/stream.c index 2781441191..5323a9976d 100644 --- a/block/stream.c +++ b/block/stream.c @@ -53,8 +53,8 @@ static int coroutine_fn stream_populate(BlockBackend *blk, static int stream_prepare(Job *job) { StreamBlockJob *s = container_of(job, StreamBlockJob, common.job); - BlockDriverState *unfiltered_bs = bdrv_skip_filters(s->target_bs); - BlockDriverState *unfiltered_bs_cow = bdrv_cow_bs(unfiltered_bs); + BlockDriverState *unfiltered_bs; + BlockDriverState *unfiltered_bs_cow; BlockDriverState *base; BlockDriverState *unfiltered_base; Error *local_err = NULL; @@ -62,6 +62,11 @@ static int stream_prepare(Job *job) GLOBAL_STATE_CODE(); + bdrv_graph_rdlock_main_loop(); + unfiltered_bs = bdrv_skip_filters(s->target_bs); + unfiltered_bs_cow = bdrv_cow_bs(unfiltered_bs); + bdrv_graph_rdunlock_main_loop(); + /* We should drop filter at this point, as filter hold the backing chain */ bdrv_cor_filter_drop(s->cor_filter_bs); s->cor_filter_bs = NULL; @@ -142,18 +147,19 @@ static void stream_clean(Job *job) static int coroutine_fn stream_run(Job *job, Error **errp) { StreamBlockJob *s = container_of(job, StreamBlockJob, common.job); - BlockDriverState *unfiltered_bs = bdrv_skip_filters(s->target_bs); + BlockDriverState *unfiltered_bs; int64_t len; int64_t offset = 0; int error = 0; int64_t n = 0; /* bytes */ - if (unfiltered_bs == s->base_overlay) { - /* Nothing to stream */ - return 0; - } - WITH_GRAPH_RDLOCK_GUARD() { + unfiltered_bs = bdrv_skip_filters(s->target_bs); + if (unfiltered_bs == s->base_overlay) { + /* Nothing to stream */ + return 0; + } + len = bdrv_co_getlength(s->target_bs); if (len < 0) { return len; diff --git a/blockdev.c b/blockdev.c index 6cdf48beb1..5f15ea3b1d 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1710,7 +1710,6 @@ static void drive_backup_action(DriveBackup *backup, bdrv_graph_rdunlock_main_loop(); goto out; } - bdrv_graph_rdunlock_main_loop(); flags = bs->open_flags | BDRV_O_RDWR; @@ -1735,6 +1734,7 @@ static void drive_backup_action(DriveBackup *backup, flags |= BDRV_O_NO_BACKING; set_backing_hd = true; } + bdrv_graph_rdunlock_main_loop(); size = bdrv_getlength(bs); if (size < 0) { @@ -3054,7 +3054,6 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) bdrv_graph_rdunlock_main_loop(); return; } - bdrv_graph_rdunlock_main_loop(); aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); @@ -3076,6 +3075,7 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) if (arg->sync == MIRROR_SYNC_MODE_NONE) { target_backing_bs = bs; } + bdrv_graph_rdunlock_main_loop(); size = bdrv_getlength(bs); if (size < 0) { @@ -3450,15 +3450,16 @@ void qmp_change_backing_file(const char *device, goto out; } + bdrv_graph_rdlock_main_loop(); if (bdrv_find_base(image_bs) == image_bs) { error_setg(errp, "not allowing backing file change on an image " "without a backing file"); + bdrv_graph_rdunlock_main_loop(); goto out; } /* even though we are not necessarily operating on bs, we need it to * determine if block ops are currently prohibited on the chain */ - bdrv_graph_rdlock_main_loop(); if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_CHANGE, errp)) { bdrv_graph_rdunlock_main_loop(); goto out; diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h index 3ae468ea15..b6860ae43b 100644 --- a/include/block/block-global-state.h +++ b/include/block/block-global-state.h @@ -144,9 +144,11 @@ int bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file, void bdrv_register(BlockDriver *bdrv); int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, const char *backing_file_str); -BlockDriverState *bdrv_find_overlay(BlockDriverState *active, - BlockDriverState *bs); -BlockDriverState *bdrv_find_base(BlockDriverState *bs); + +BlockDriverState * GRAPH_RDLOCK +bdrv_find_overlay(BlockDriverState *active, BlockDriverState *bs); + +BlockDriverState * GRAPH_RDLOCK bdrv_find_base(BlockDriverState *bs); bool bdrv_is_backing_chain_frozen(BlockDriverState *bs, BlockDriverState *base, Error **errp); int bdrv_freeze_backing_chain(BlockDriverState *bs, BlockDriverState *base, diff --git a/include/block/block_int-io.h b/include/block/block_int-io.h index 6800af7590..4e7bf57a5e 100644 --- a/include/block/block_int-io.h +++ b/include/block/block_int-io.h @@ -134,8 +134,8 @@ BdrvChild *bdrv_cow_child(BlockDriverState *bs); BdrvChild *bdrv_filter_child(BlockDriverState *bs); BdrvChild *bdrv_filter_or_cow_child(BlockDriverState *bs); BdrvChild * GRAPH_RDLOCK bdrv_primary_child(BlockDriverState *bs); -BlockDriverState *bdrv_skip_filters(BlockDriverState *bs); -BlockDriverState *bdrv_backing_chain_next(BlockDriverState *bs); +BlockDriverState * GRAPH_RDLOCK bdrv_skip_filters(BlockDriverState *bs); +BlockDriverState * GRAPH_RDLOCK bdrv_backing_chain_next(BlockDriverState *bs); static inline BlockDriverState *bdrv_cow_bs(BlockDriverState *bs) { diff --git a/qemu-img.c b/qemu-img.c index c061fd0634..33f3ab5fba 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -1050,12 +1050,14 @@ static int img_commit(int argc, char **argv) qemu_progress_init(progress, 1.f); qemu_progress_print(0.f, 100); + bdrv_graph_rdlock_main_loop(); if (base) { base_bs = bdrv_find_backing_image(bs, base); if (!base_bs) { error_setg(&local_err, "Did not find '%s' in the backing chain of '%s'", base, filename); + bdrv_graph_rdunlock_main_loop(); goto done; } } else { @@ -1065,9 +1067,11 @@ static int img_commit(int argc, char **argv) base_bs = bdrv_backing_chain_next(bs); if (!base_bs) { error_setg(&local_err, "Image does not have a backing file"); + bdrv_graph_rdunlock_main_loop(); goto done; } } + bdrv_graph_rdunlock_main_loop(); cbi = (CommonBlockJobCBInfo){ .errp = &local_err, @@ -1713,7 +1717,8 @@ static void convert_select_part(ImgConvertState *s, int64_t sector_num, } } -static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num) +static int coroutine_mixed_fn GRAPH_RDLOCK +convert_iteration_sectors(ImgConvertState *s, int64_t sector_num) { int64_t src_cur_offset; int ret, n, src_cur; @@ -2115,7 +2120,9 @@ static int convert_do_copy(ImgConvertState *s) } while (sector_num < s->total_sectors) { + bdrv_graph_rdlock_main_loop(); n = convert_iteration_sectors(s, sector_num); + bdrv_graph_rdunlock_main_loop(); if (n < 0) { return n; } @@ -2757,8 +2764,10 @@ static int img_convert(int argc, char **argv) * s.target_backing_sectors has to be negative, which it will * be automatically). The backing file length is used only * for optimizations, so such a case is not fatal. */ + bdrv_graph_rdlock_main_loop(); s.target_backing_sectors = bdrv_nb_sectors(bdrv_backing_chain_next(out_bs)); + bdrv_graph_rdunlock_main_loop(); } else { s.target_backing_sectors = -1; } @@ -3145,6 +3154,9 @@ static int get_block_status(BlockDriverState *bs, int64_t offset, int64_t map; char *filename = NULL; + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + /* As an optimization, we could cache the current range of unallocated * clusters in each file of the chain, and avoid querying the same * range repeatedly. @@ -3173,9 +3185,7 @@ static int get_block_status(BlockDriverState *bs, int64_t offset, has_offset = !!(ret & BDRV_BLOCK_OFFSET_VALID); if (file && has_offset) { - bdrv_graph_rdlock_main_loop(); bdrv_refresh_filename(file); - bdrv_graph_rdunlock_main_loop(); filename = file->filename; } @@ -3663,7 +3673,9 @@ static int img_rebase(int argc, char **argv) } bs = blk_bs(blk); + bdrv_graph_rdlock_main_loop(); unfiltered_bs = bdrv_skip_filters(bs); + bdrv_graph_rdunlock_main_loop(); if (compress && !block_driver_can_compress(unfiltered_bs->drv)) { error_report("Compression not supported for this file format"); From 9275fc72bd0a1c35e915e4991c7d27209ecab923 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 27 Oct 2023 17:53:18 +0200 Subject: [PATCH 856/974] block: Mark bdrv_(un)freeze_backing_chain() and callers GRAPH_RDLOCK This adds GRAPH_RDLOCK annotations to declare that callers of bdrv_(un)freeze_backing_chain() need to hold a reader lock for the graph because it calls bdrv_filter_or_cow_child(), which accesses bs->file/backing. Use the opportunity to make bdrv_is_backing_chain_frozen() static, it has no external callers. Signed-off-by: Kevin Wolf Message-ID: <20231027155333.420094-10-kwolf@redhat.com> Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- block.c | 5 +++-- block/commit.c | 6 ++++++ block/copy-on-read.c | 19 +++++++++++++++---- block/copy-on-read.h | 3 ++- block/mirror.c | 3 +++ block/stream.c | 16 +++++++++++----- include/block/block-global-state.h | 11 ++++++----- 7 files changed, 46 insertions(+), 17 deletions(-) diff --git a/block.c b/block.c index 7e8b39711b..dc1980ee42 100644 --- a/block.c +++ b/block.c @@ -5843,8 +5843,9 @@ BlockDriverState *bdrv_find_base(BlockDriverState *bs) * between @bs and @base is frozen. @errp is set if that's the case. * @base must be reachable from @bs, or NULL. */ -bool bdrv_is_backing_chain_frozen(BlockDriverState *bs, BlockDriverState *base, - Error **errp) +static bool GRAPH_RDLOCK +bdrv_is_backing_chain_frozen(BlockDriverState *bs, BlockDriverState *base, + Error **errp) { BlockDriverState *i; BdrvChild *child; diff --git a/block/commit.c b/block/commit.c index 05eb57d9ea..d92af02ead 100644 --- a/block/commit.c +++ b/block/commit.c @@ -48,8 +48,10 @@ static int commit_prepare(Job *job) { CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); + bdrv_graph_rdlock_main_loop(); bdrv_unfreeze_backing_chain(s->commit_top_bs, s->base_bs); s->chain_frozen = false; + bdrv_graph_rdunlock_main_loop(); /* Remove base node parent that still uses BLK_PERM_WRITE/RESIZE before * the normal backing chain can be restored. */ @@ -68,7 +70,9 @@ static void commit_abort(Job *job) BlockDriverState *top_bs = blk_bs(s->top); if (s->chain_frozen) { + bdrv_graph_rdlock_main_loop(); bdrv_unfreeze_backing_chain(s->commit_top_bs, s->base_bs); + bdrv_graph_rdunlock_main_loop(); } /* Make sure commit_top_bs and top stay around until bdrv_replace_node() */ @@ -404,7 +408,9 @@ void commit_start(const char *job_id, BlockDriverState *bs, fail: if (s->chain_frozen) { + bdrv_graph_rdlock_main_loop(); bdrv_unfreeze_backing_chain(commit_top_bs, base); + bdrv_graph_rdunlock_main_loop(); } if (s->base) { blk_unref(s->base); diff --git a/block/copy-on-read.c b/block/copy-on-read.c index 5149fcf63a..6f245b629a 100644 --- a/block/copy-on-read.c +++ b/block/copy-on-read.c @@ -35,8 +35,8 @@ typedef struct BDRVStateCOR { } BDRVStateCOR; -static int cor_open(BlockDriverState *bs, QDict *options, int flags, - Error **errp) +static int GRAPH_UNLOCKED +cor_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { BlockDriverState *bottom_bs = NULL; BDRVStateCOR *state = bs->opaque; @@ -44,6 +44,8 @@ static int cor_open(BlockDriverState *bs, QDict *options, int flags, const char *bottom_node = qdict_get_try_str(options, "bottom"); int ret; + GLOBAL_STATE_CODE(); + ret = bdrv_open_file_child(NULL, options, "file", bs, errp); if (ret < 0) { return ret; @@ -59,6 +61,8 @@ static int cor_open(BlockDriverState *bs, QDict *options, int flags, bs->file->bs->supported_zero_flags); if (bottom_node) { + GRAPH_RDLOCK_GUARD_MAINLOOP(); + bottom_bs = bdrv_find_node(bottom_node); if (!bottom_bs) { error_setg(errp, "Bottom node '%s' not found", bottom_node); @@ -227,13 +231,17 @@ cor_co_lock_medium(BlockDriverState *bs, bool locked) } -static void cor_close(BlockDriverState *bs) +static void GRAPH_UNLOCKED cor_close(BlockDriverState *bs) { BDRVStateCOR *s = bs->opaque; + GLOBAL_STATE_CODE(); + if (s->chain_frozen) { + bdrv_graph_rdlock_main_loop(); s->chain_frozen = false; bdrv_unfreeze_backing_chain(bs, s->bottom_bs); + bdrv_graph_rdunlock_main_loop(); } bdrv_unref(s->bottom_bs); @@ -263,12 +271,15 @@ static BlockDriver bdrv_copy_on_read = { }; -void bdrv_cor_filter_drop(BlockDriverState *cor_filter_bs) +void no_coroutine_fn bdrv_cor_filter_drop(BlockDriverState *cor_filter_bs) { BDRVStateCOR *s = cor_filter_bs->opaque; + GLOBAL_STATE_CODE(); + /* unfreeze, as otherwise bdrv_replace_node() will fail */ if (s->chain_frozen) { + GRAPH_RDLOCK_GUARD_MAINLOOP(); s->chain_frozen = false; bdrv_unfreeze_backing_chain(cor_filter_bs, s->bottom_bs); } diff --git a/block/copy-on-read.h b/block/copy-on-read.h index 1d8ad38c74..72f9b378ea 100644 --- a/block/copy-on-read.h +++ b/block/copy-on-read.h @@ -27,6 +27,7 @@ #include "block/block_int.h" -void bdrv_cor_filter_drop(BlockDriverState *cor_filter_bs); +void no_coroutine_fn GRAPH_UNLOCKED +bdrv_cor_filter_drop(BlockDriverState *cor_filter_bs); #endif /* BLOCK_COPY_ON_READ_H */ diff --git a/block/mirror.c b/block/mirror.c index 75e826dac8..f8e439371c 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -678,6 +678,7 @@ static int mirror_exit_common(Job *job) s->prepared = true; aio_context_acquire(qemu_get_aio_context()); + bdrv_graph_rdlock_main_loop(); mirror_top_bs = s->mirror_top_bs; bs_opaque = mirror_top_bs->opaque; @@ -696,6 +697,8 @@ static int mirror_exit_common(Job *job) bdrv_ref(mirror_top_bs); bdrv_ref(target_bs); + bdrv_graph_rdunlock_main_loop(); + /* * Remove target parent that still uses BLK_PERM_WRITE/RESIZE before * inserting target_bs at s->to_replace, where we might not be able to get diff --git a/block/stream.c b/block/stream.c index 5323a9976d..c32c98339a 100644 --- a/block/stream.c +++ b/block/stream.c @@ -266,6 +266,8 @@ void stream_start(const char *job_id, BlockDriverState *bs, assert(!(base && bottom)); assert(!(backing_file_str && bottom)); + bdrv_graph_rdlock_main_loop(); + if (bottom) { /* * New simple interface. The code is written in terms of old interface @@ -278,13 +280,11 @@ void stream_start(const char *job_id, BlockDriverState *bs, assert(!bottom->drv->is_filter); base_overlay = above_base = bottom; } else { - GRAPH_RDLOCK_GUARD_MAINLOOP(); - base_overlay = bdrv_find_overlay(bs, base); if (!base_overlay) { error_setg(errp, "'%s' is not in the backing chain of '%s'", base->node_name, bs->node_name); - return; + goto out_rdlock; } /* @@ -306,7 +306,7 @@ void stream_start(const char *job_id, BlockDriverState *bs, if (bs_read_only) { /* Hold the chain during reopen */ if (bdrv_freeze_backing_chain(bs, above_base, errp) < 0) { - return; + goto out_rdlock; } ret = bdrv_reopen_set_read_only(bs, false, errp); @@ -315,10 +315,12 @@ void stream_start(const char *job_id, BlockDriverState *bs, bdrv_unfreeze_backing_chain(bs, above_base); if (ret < 0) { - return; + goto out_rdlock; } } + bdrv_graph_rdunlock_main_loop(); + opts = qdict_new(); qdict_put_str(opts, "driver", "copy-on-read"); @@ -413,4 +415,8 @@ fail: if (bs_read_only) { bdrv_reopen_set_read_only(bs, true, NULL); } + return; + +out_rdlock: + bdrv_graph_rdunlock_main_loop(); } diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h index b6860ae43b..545708c35a 100644 --- a/include/block/block-global-state.h +++ b/include/block/block-global-state.h @@ -149,11 +149,12 @@ BlockDriverState * GRAPH_RDLOCK bdrv_find_overlay(BlockDriverState *active, BlockDriverState *bs); BlockDriverState * GRAPH_RDLOCK bdrv_find_base(BlockDriverState *bs); -bool bdrv_is_backing_chain_frozen(BlockDriverState *bs, BlockDriverState *base, - Error **errp); -int bdrv_freeze_backing_chain(BlockDriverState *bs, BlockDriverState *base, - Error **errp); -void bdrv_unfreeze_backing_chain(BlockDriverState *bs, BlockDriverState *base); + +int GRAPH_RDLOCK +bdrv_freeze_backing_chain(BlockDriverState *bs, BlockDriverState *base, + Error **errp); +void GRAPH_RDLOCK +bdrv_unfreeze_backing_chain(BlockDriverState *bs, BlockDriverState *base); /* * The units of offset and total_work_size may be chosen arbitrarily by the From 79bb76272763c090f2b1dae9519c516257241cac Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 27 Oct 2023 17:53:19 +0200 Subject: [PATCH 857/974] block: Mark bdrv_chain_contains() and callers GRAPH_RDLOCK This adds GRAPH_RDLOCK annotations to declare that callers of bdrv_chain_contains() need to hold a reader lock for the graph because it calls bdrv_filter_or_cow_bs(), which accesses bs->file/backing. Signed-off-by: Kevin Wolf Message-ID: <20231027155333.420094-11-kwolf@redhat.com> Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- block.c | 1 - block/block-copy.c | 2 ++ blockdev.c | 48 +++++++++++++++++------------- include/block/block-global-state.h | 4 ++- 4 files changed, 32 insertions(+), 23 deletions(-) diff --git a/block.c b/block.c index dc1980ee42..bb322df7d8 100644 --- a/block.c +++ b/block.c @@ -6524,7 +6524,6 @@ bool bdrv_chain_contains(BlockDriverState *top, BlockDriverState *base) { GLOBAL_STATE_CODE(); - GRAPH_RDLOCK_GUARD_MAINLOOP(); while (top && top != base) { top = bdrv_filter_or_cow_bs(top); diff --git a/block/block-copy.c b/block/block-copy.c index 6b2be3d204..9ee3dd7ef5 100644 --- a/block/block-copy.c +++ b/block/block-copy.c @@ -399,7 +399,9 @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, * For more information see commit f8d59dfb40bb and test * tests/qemu-iotests/222 */ + bdrv_graph_rdlock_main_loop(); is_fleecing = bdrv_chain_contains(target->bs, source->bs); + bdrv_graph_rdunlock_main_loop(); s = g_new(BlockCopyState, 1); *s = (BlockCopyState) { diff --git a/blockdev.c b/blockdev.c index 5f15ea3b1d..f04faf6373 100644 --- a/blockdev.c +++ b/blockdev.c @@ -2450,11 +2450,12 @@ void qmp_block_stream(const char *job_id, const char *device, aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); + bdrv_graph_rdlock_main_loop(); if (base) { base_bs = bdrv_find_backing_image(bs, base); if (base_bs == NULL) { error_setg(errp, "Can't find '%s' in the backing chain", base); - goto out; + goto out_rdlock; } assert(bdrv_get_aio_context(base_bs) == aio_context); } @@ -2462,38 +2463,36 @@ void qmp_block_stream(const char *job_id, const char *device, if (base_node) { base_bs = bdrv_lookup_bs(NULL, base_node, errp); if (!base_bs) { - goto out; + goto out_rdlock; } if (bs == base_bs || !bdrv_chain_contains(bs, base_bs)) { error_setg(errp, "Node '%s' is not a backing image of '%s'", base_node, device); - goto out; + goto out_rdlock; } assert(bdrv_get_aio_context(base_bs) == aio_context); - bdrv_graph_rdlock_main_loop(); bdrv_refresh_filename(base_bs); - bdrv_graph_rdunlock_main_loop(); } if (bottom) { bottom_bs = bdrv_lookup_bs(NULL, bottom, errp); if (!bottom_bs) { - goto out; + goto out_rdlock; } if (!bottom_bs->drv) { error_setg(errp, "Node '%s' is not open", bottom); - goto out; + goto out_rdlock; } if (bottom_bs->drv->is_filter) { error_setg(errp, "Node '%s' is a filter, use a non-filter node " "as 'bottom'", bottom); - goto out; + goto out_rdlock; } if (!bdrv_chain_contains(bs, bottom_bs)) { error_setg(errp, "Node '%s' is not in a chain starting from '%s'", bottom, device); - goto out; + goto out_rdlock; } assert(bdrv_get_aio_context(bottom_bs) == aio_context); } @@ -2501,14 +2500,12 @@ void qmp_block_stream(const char *job_id, const char *device, /* * Check for op blockers in the whole chain between bs and base (or bottom) */ - bdrv_graph_rdlock_main_loop(); iter_end = bottom ? bdrv_filter_or_cow_bs(bottom_bs) : base_bs; for (iter = bs; iter && iter != iter_end; iter = bdrv_filter_or_cow_bs(iter)) { if (bdrv_op_is_blocked(iter, BLOCK_OP_TYPE_STREAM, errp)) { - bdrv_graph_rdunlock_main_loop(); - goto out; + goto out_rdlock; } } bdrv_graph_rdunlock_main_loop(); @@ -2540,6 +2537,11 @@ void qmp_block_stream(const char *job_id, const char *device, out: aio_context_release(aio_context); + return; + +out_rdlock: + bdrv_graph_rdunlock_main_loop(); + aio_context_release(aio_context); } void qmp_block_commit(const char *job_id, const char *device, @@ -3439,39 +3441,38 @@ void qmp_change_backing_file(const char *device, aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); + bdrv_graph_rdlock_main_loop(); + image_bs = bdrv_lookup_bs(NULL, image_node_name, &local_err); if (local_err) { error_propagate(errp, local_err); - goto out; + goto out_rdlock; } if (!image_bs) { error_setg(errp, "image file not found"); - goto out; + goto out_rdlock; } - bdrv_graph_rdlock_main_loop(); if (bdrv_find_base(image_bs) == image_bs) { error_setg(errp, "not allowing backing file change on an image " "without a backing file"); - bdrv_graph_rdunlock_main_loop(); - goto out; + goto out_rdlock; } /* even though we are not necessarily operating on bs, we need it to * determine if block ops are currently prohibited on the chain */ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_CHANGE, errp)) { - bdrv_graph_rdunlock_main_loop(); - goto out; + goto out_rdlock; } - bdrv_graph_rdunlock_main_loop(); /* final sanity check */ if (!bdrv_chain_contains(bs, image_bs)) { error_setg(errp, "'%s' and image file are not in the same chain", device); - goto out; + goto out_rdlock; } + bdrv_graph_rdunlock_main_loop(); /* if not r/w, reopen to make r/w */ ro = bdrv_is_read_only(image_bs); @@ -3499,6 +3500,11 @@ void qmp_change_backing_file(const char *device, out: aio_context_release(aio_context); + return; + +out_rdlock: + bdrv_graph_rdunlock_main_loop(); + aio_context_release(aio_context); } void qmp_blockdev_add(BlockdevOptions *options, Error **errp) diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h index 545708c35a..9a33bd7ef9 100644 --- a/include/block/block-global-state.h +++ b/include/block/block-global-state.h @@ -199,7 +199,9 @@ XDbgBlockGraph * GRAPH_RDLOCK bdrv_get_xdbg_block_graph(Error **errp); BlockDriverState *bdrv_lookup_bs(const char *device, const char *node_name, Error **errp); -bool bdrv_chain_contains(BlockDriverState *top, BlockDriverState *base); +bool GRAPH_RDLOCK +bdrv_chain_contains(BlockDriverState *top, BlockDriverState *base); + BlockDriverState *bdrv_next_node(BlockDriverState *bs); BlockDriverState *bdrv_next_all_states(BlockDriverState *bs); From ec82cc41a79ffa8b1a2dee76a420a34a59f117c6 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 27 Oct 2023 17:53:20 +0200 Subject: [PATCH 858/974] block: Mark bdrv_filter_child() and callers GRAPH_RDLOCK This adds GRAPH_RDLOCK annotations to declare that callers of bdrv_filter_child() need to hold a reader lock for the graph because it accesses bs->file/backing. Signed-off-by: Kevin Wolf Message-ID: <20231027155333.420094-12-kwolf@redhat.com> Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- block.c | 4 ++-- include/block/block_int-io.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/block.c b/block.c index bb322df7d8..499b147315 100644 --- a/block.c +++ b/block.c @@ -8491,8 +8491,8 @@ BdrvChild *bdrv_primary_child(BlockDriverState *bs) return found; } -static BlockDriverState *bdrv_do_skip_filters(BlockDriverState *bs, - bool stop_on_explicit_filter) +static BlockDriverState * GRAPH_RDLOCK +bdrv_do_skip_filters(BlockDriverState *bs, bool stop_on_explicit_filter) { BdrvChild *c; diff --git a/include/block/block_int-io.h b/include/block/block_int-io.h index 4e7bf57a5e..17547a2dab 100644 --- a/include/block/block_int-io.h +++ b/include/block/block_int-io.h @@ -131,8 +131,8 @@ int co_wrapper_mixed_bdrv_rdlock bdrv_refresh_total_sectors(BlockDriverState *bs, int64_t hint); BdrvChild *bdrv_cow_child(BlockDriverState *bs); -BdrvChild *bdrv_filter_child(BlockDriverState *bs); -BdrvChild *bdrv_filter_or_cow_child(BlockDriverState *bs); +BdrvChild * GRAPH_RDLOCK bdrv_filter_child(BlockDriverState *bs); +BdrvChild * GRAPH_RDLOCK bdrv_filter_or_cow_child(BlockDriverState *bs); BdrvChild * GRAPH_RDLOCK bdrv_primary_child(BlockDriverState *bs); BlockDriverState * GRAPH_RDLOCK bdrv_skip_filters(BlockDriverState *bs); BlockDriverState * GRAPH_RDLOCK bdrv_backing_chain_next(BlockDriverState *bs); From 78a9c76eefae877e63591728234604310c51d88f Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 27 Oct 2023 17:53:21 +0200 Subject: [PATCH 859/974] block: Mark bdrv_cow_child() and callers GRAPH_RDLOCK This adds GRAPH_RDLOCK annotations to declare that callers of bdrv_cow_child() need to hold a reader lock for the graph because it accesses bs->backing. Signed-off-by: Kevin Wolf Message-ID: <20231027155333.420094-13-kwolf@redhat.com> Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- block/stream.c | 2 +- include/block/block_int-io.h | 5 +++-- qemu-img.c | 11 ++++++++--- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/block/stream.c b/block/stream.c index c32c98339a..3f5d773535 100644 --- a/block/stream.c +++ b/block/stream.c @@ -90,7 +90,7 @@ static int stream_prepare(Job *job) unfiltered_base = bdrv_skip_filters(base); bdrv_graph_rdunlock_main_loop(); - if (bdrv_cow_child(unfiltered_bs)) { + if (unfiltered_bs_cow) { const char *base_id = NULL, *base_fmt = NULL; if (unfiltered_base) { base_id = s->backing_file_str ?: unfiltered_base->filename; diff --git a/include/block/block_int-io.h b/include/block/block_int-io.h index 17547a2dab..4a7cf2b4fd 100644 --- a/include/block/block_int-io.h +++ b/include/block/block_int-io.h @@ -130,14 +130,15 @@ bdrv_co_refresh_total_sectors(BlockDriverState *bs, int64_t hint); int co_wrapper_mixed_bdrv_rdlock bdrv_refresh_total_sectors(BlockDriverState *bs, int64_t hint); -BdrvChild *bdrv_cow_child(BlockDriverState *bs); +BdrvChild * GRAPH_RDLOCK bdrv_cow_child(BlockDriverState *bs); BdrvChild * GRAPH_RDLOCK bdrv_filter_child(BlockDriverState *bs); BdrvChild * GRAPH_RDLOCK bdrv_filter_or_cow_child(BlockDriverState *bs); BdrvChild * GRAPH_RDLOCK bdrv_primary_child(BlockDriverState *bs); BlockDriverState * GRAPH_RDLOCK bdrv_skip_filters(BlockDriverState *bs); BlockDriverState * GRAPH_RDLOCK bdrv_backing_chain_next(BlockDriverState *bs); -static inline BlockDriverState *bdrv_cow_bs(BlockDriverState *bs) +static inline BlockDriverState * GRAPH_RDLOCK +bdrv_cow_bs(BlockDriverState *bs) { IO_CODE(); return child_bs(bdrv_cow_child(bs)); diff --git a/qemu-img.c b/qemu-img.c index 33f3ab5fba..5a77f67719 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -3541,7 +3541,7 @@ static int img_rebase(int argc, char **argv) uint8_t *buf_old = NULL; uint8_t *buf_new = NULL; BlockDriverState *bs = NULL, *prefix_chain_bs = NULL; - BlockDriverState *unfiltered_bs; + BlockDriverState *unfiltered_bs, *unfiltered_bs_cow; BlockDriverInfo bdi = {0}; char *filename; const char *fmt, *cache, *src_cache, *out_basefmt, *out_baseimg; @@ -3675,6 +3675,7 @@ static int img_rebase(int argc, char **argv) bdrv_graph_rdlock_main_loop(); unfiltered_bs = bdrv_skip_filters(bs); + unfiltered_bs_cow = bdrv_cow_bs(unfiltered_bs); bdrv_graph_rdunlock_main_loop(); if (compress && !block_driver_can_compress(unfiltered_bs->drv)) { @@ -3710,7 +3711,11 @@ static int img_rebase(int argc, char **argv) /* For safe rebasing we need to compare old and new backing file */ if (!unsafe) { QDict *options = NULL; - BlockDriverState *base_bs = bdrv_cow_bs(unfiltered_bs); + BlockDriverState *base_bs; + + bdrv_graph_rdlock_main_loop(); + base_bs = bdrv_cow_bs(unfiltered_bs); + bdrv_graph_rdunlock_main_loop(); if (base_bs) { blk_old_backing = blk_new(qemu_get_aio_context(), @@ -3876,7 +3881,7 @@ static int img_rebase(int argc, char **argv) * If cluster wasn't changed since prefix_chain, we don't need * to take action */ - ret = bdrv_is_allocated_above(bdrv_cow_bs(unfiltered_bs), + ret = bdrv_is_allocated_above(unfiltered_bs_cow, prefix_chain_bs, false, offset, n, &n); if (ret < 0) { From d0f9fd94d92c15c6ab7f6b8855acd812b80dbbaa Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 27 Oct 2023 17:53:22 +0200 Subject: [PATCH 860/974] block: Mark bdrv_set_backing_hd_drained() GRAPH_WRLOCK Instead of taking the writer lock internally, require callers to already hold it when calling bdrv_set_backing_hd_drained(). Basically everthing in the function needs the lock and its callers may already want to hold the graph lock and so wouldn't be able to call functions that take it internally. Signed-off-by: Kevin Wolf Message-ID: <20231027155333.420094-14-kwolf@redhat.com> Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- block.c | 4 ++-- block/stream.c | 2 ++ include/block/block-global-state.h | 7 ++++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/block.c b/block.c index 499b147315..d79a6f41f9 100644 --- a/block.c +++ b/block.c @@ -3557,7 +3557,6 @@ int bdrv_set_backing_hd_drained(BlockDriverState *bs, if (bs->backing) { assert(bs->backing->bs->quiesce_counter > 0); } - bdrv_graph_wrlock(backing_hd); ret = bdrv_set_backing_noperm(bs, backing_hd, tran, errp); if (ret < 0) { @@ -3567,7 +3566,6 @@ int bdrv_set_backing_hd_drained(BlockDriverState *bs, ret = bdrv_refresh_perms(bs, tran, errp); out: tran_finalize(tran, ret); - bdrv_graph_wrunlock(); return ret; } @@ -3580,7 +3578,9 @@ int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, bdrv_ref(drain_bs); bdrv_drained_begin(drain_bs); + bdrv_graph_wrlock(backing_hd); ret = bdrv_set_backing_hd_drained(bs, backing_hd, errp); + bdrv_graph_wrunlock(); bdrv_drained_end(drain_bs); bdrv_unref(drain_bs); diff --git a/block/stream.c b/block/stream.c index 3f5d773535..0b92410c00 100644 --- a/block/stream.c +++ b/block/stream.c @@ -99,7 +99,9 @@ static int stream_prepare(Job *job) } } + bdrv_graph_wrlock(base); bdrv_set_backing_hd_drained(unfiltered_bs, base, &local_err); + bdrv_graph_wrunlock(); /* * This call will do I/O, so the graph can change again from here on. diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h index 9a33bd7ef9..a1fd70ec97 100644 --- a/include/block/block-global-state.h +++ b/include/block/block-global-state.h @@ -101,9 +101,10 @@ bdrv_co_open_blockdev_ref(BlockdevRef *ref, Error **errp); int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, Error **errp); -int bdrv_set_backing_hd_drained(BlockDriverState *bs, - BlockDriverState *backing_hd, - Error **errp); +int GRAPH_WRLOCK +bdrv_set_backing_hd_drained(BlockDriverState *bs, BlockDriverState *backing_hd, + Error **errp); + int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, const char *bdref_key, Error **errp); From 3204c2e33b6fb77897756bdd8639913f02647300 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 27 Oct 2023 17:53:23 +0200 Subject: [PATCH 861/974] block: Inline bdrv_set_backing_noperm() It's only a single line and has a single caller. Inlining makes things a bit easier to follow. Signed-off-by: Kevin Wolf Message-ID: <20231027155333.420094-15-kwolf@redhat.com> Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- block.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/block.c b/block.c index d79a6f41f9..dc43e36f69 100644 --- a/block.c +++ b/block.c @@ -3532,19 +3532,7 @@ out: * * If a backing child is already present (i.e. we're detaching a node), that * child node must be drained. - * - * After calling this function, the transaction @tran may only be completed - * while holding a writer lock for the graph. */ -static int GRAPH_WRLOCK -bdrv_set_backing_noperm(BlockDriverState *bs, - BlockDriverState *backing_hd, - Transaction *tran, Error **errp) -{ - GLOBAL_STATE_CODE(); - return bdrv_set_file_or_backing_noperm(bs, backing_hd, true, tran, errp); -} - int bdrv_set_backing_hd_drained(BlockDriverState *bs, BlockDriverState *backing_hd, Error **errp) @@ -3558,7 +3546,7 @@ int bdrv_set_backing_hd_drained(BlockDriverState *bs, assert(bs->backing->bs->quiesce_counter > 0); } - ret = bdrv_set_backing_noperm(bs, backing_hd, tran, errp); + ret = bdrv_set_file_or_backing_noperm(bs, backing_hd, true, tran, errp); if (ret < 0) { goto out; } From 5c0ef4954f4cd8131df27f5f8cebe3ec215d64b3 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 27 Oct 2023 17:53:24 +0200 Subject: [PATCH 862/974] block: Mark bdrv_replace_node_common() GRAPH_WRLOCK Instead of taking the writer lock internally, require callers to already hold it when calling bdrv_replace_node_common(). Basically everthing in the function needs the lock and its callers may already want to hold the graph lock and so wouldn't be able to call functions that take it internally. Signed-off-by: Kevin Wolf Message-ID: <20231027155333.420094-16-kwolf@redhat.com> Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- block.c | 72 +++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 45 insertions(+), 27 deletions(-) diff --git a/block.c b/block.c index dc43e36f69..c7409cf658 100644 --- a/block.c +++ b/block.c @@ -5412,6 +5412,9 @@ bdrv_replace_node_noperm(BlockDriverState *from, } /* + * Switch all parents of @from to point to @to instead. @from and @to must be in + * the same AioContext and both must be drained. + * * With auto_skip=true bdrv_replace_node_common skips updating from parents * if it creates a parent-child relation loop or if parent is block-job. * @@ -5421,10 +5424,9 @@ bdrv_replace_node_noperm(BlockDriverState *from, * With @detach_subchain=true @to must be in a backing chain of @from. In this * case backing link of the cow-parent of @to is removed. */ -static int bdrv_replace_node_common(BlockDriverState *from, - BlockDriverState *to, - bool auto_skip, bool detach_subchain, - Error **errp) +static int GRAPH_WRLOCK +bdrv_replace_node_common(BlockDriverState *from, BlockDriverState *to, + bool auto_skip, bool detach_subchain, Error **errp) { Transaction *tran = tran_new(); g_autoptr(GSList) refresh_list = NULL; @@ -5433,16 +5435,9 @@ static int bdrv_replace_node_common(BlockDriverState *from, GLOBAL_STATE_CODE(); - /* Make sure that @from doesn't go away until we have successfully attached - * all of its parents to @to. */ - bdrv_ref(from); - - assert(qemu_get_current_aio_context() == qemu_get_aio_context()); + assert(from->quiesce_counter); + assert(to->quiesce_counter); assert(bdrv_get_aio_context(from) == bdrv_get_aio_context(to)); - bdrv_drained_begin(from); - bdrv_drained_begin(to); - - bdrv_graph_wrlock(to); if (detach_subchain) { assert(bdrv_chain_contains(from, to)); @@ -5483,8 +5478,26 @@ static int bdrv_replace_node_common(BlockDriverState *from, out: tran_finalize(tran, ret); - bdrv_graph_wrunlock(); + return ret; +} +int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, + Error **errp) +{ + int ret; + + GLOBAL_STATE_CODE(); + + /* Make sure that @from doesn't go away until we have successfully attached + * all of its parents to @to. */ + bdrv_ref(from); + bdrv_drained_begin(from); + bdrv_drained_begin(to); + bdrv_graph_wrlock(to); + + ret = bdrv_replace_node_common(from, to, true, false, errp); + + bdrv_graph_wrunlock(); bdrv_drained_end(to); bdrv_drained_end(from); bdrv_unref(from); @@ -5492,24 +5505,24 @@ out: return ret; } -int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, - Error **errp) -{ - GLOBAL_STATE_CODE(); - - return bdrv_replace_node_common(from, to, true, false, errp); -} - int bdrv_drop_filter(BlockDriverState *bs, Error **errp) { BlockDriverState *child_bs; + int ret; GLOBAL_STATE_CODE(); + bdrv_graph_rdlock_main_loop(); child_bs = bdrv_filter_or_cow_bs(bs); bdrv_graph_rdunlock_main_loop(); - return bdrv_replace_node_common(bs, child_bs, true, true, errp); + bdrv_drained_begin(child_bs); + bdrv_graph_wrlock(bs); + ret = bdrv_replace_node_common(bs, child_bs, true, true, errp); + bdrv_graph_wrunlock(); + bdrv_drained_end(child_bs); + + return ret; } /* @@ -5957,15 +5970,15 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, bdrv_ref(top); bdrv_drained_begin(base); - bdrv_graph_rdlock_main_loop(); + bdrv_graph_wrlock(base); if (!top->drv || !base->drv) { - goto exit; + goto exit_wrlock; } /* Make sure that base is in the backing chain of top */ if (!bdrv_chain_contains(top, base)) { - goto exit; + goto exit_wrlock; } /* If 'base' recursively inherits from 'top' then we should set @@ -5997,6 +6010,8 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, * That's a FIXME. */ bdrv_replace_node_common(top, base, false, false, &local_err); + bdrv_graph_wrunlock(); + if (local_err) { error_report_err(local_err); goto exit; @@ -6029,8 +6044,11 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, } ret = 0; + goto exit; + +exit_wrlock: + bdrv_graph_wrunlock(); exit: - bdrv_graph_rdunlock_main_loop(); bdrv_drained_end(base); bdrv_unref(top); return ret; From ccd6a37947574707613e826e2bf04d55f1d5f238 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 27 Oct 2023 17:53:25 +0200 Subject: [PATCH 863/974] block: Mark bdrv_replace_node() GRAPH_WRLOCK Instead of taking the writer lock internally, require callers to already hold it when calling bdrv_replace_node(). Its callers may already want to hold the graph lock and so wouldn't be able to call functions that take it internally. Signed-off-by: Kevin Wolf Message-ID: <20231027155333.420094-17-kwolf@redhat.com> Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- block.c | 30 +++++++++++------------------- block/commit.c | 13 +++++++++++-- block/mirror.c | 26 ++++++++++++++++---------- blockdev.c | 5 +++++ include/block/block-global-state.h | 6 ++++-- tests/unit/test-bdrv-drain.c | 6 ++++++ tests/unit/test-bdrv-graph-mod.c | 13 +++++++++++-- 7 files changed, 64 insertions(+), 35 deletions(-) diff --git a/block.c b/block.c index c7409cf658..cac517ab8b 100644 --- a/block.c +++ b/block.c @@ -5484,25 +5484,7 @@ out: int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, Error **errp) { - int ret; - - GLOBAL_STATE_CODE(); - - /* Make sure that @from doesn't go away until we have successfully attached - * all of its parents to @to. */ - bdrv_ref(from); - bdrv_drained_begin(from); - bdrv_drained_begin(to); - bdrv_graph_wrlock(to); - - ret = bdrv_replace_node_common(from, to, true, false, errp); - - bdrv_graph_wrunlock(); - bdrv_drained_end(to); - bdrv_drained_end(from); - bdrv_unref(from); - - return ret; + return bdrv_replace_node_common(from, to, true, false, errp); } int bdrv_drop_filter(BlockDriverState *bs, Error **errp) @@ -5717,9 +5699,19 @@ BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *options, goto fail; } + /* + * Make sure that @bs doesn't go away until we have successfully attached + * all of its parents to @new_node_bs and undrained it again. + */ + bdrv_ref(bs); bdrv_drained_begin(bs); + bdrv_drained_begin(new_node_bs); + bdrv_graph_wrlock(new_node_bs); ret = bdrv_replace_node(bs, new_node_bs, errp); + bdrv_graph_wrunlock(); + bdrv_drained_end(new_node_bs); bdrv_drained_end(bs); + bdrv_unref(bs); if (ret < 0) { error_prepend(errp, "Could not replace node: "); diff --git a/block/commit.c b/block/commit.c index d92af02ead..30bc082edc 100644 --- a/block/commit.c +++ b/block/commit.c @@ -68,6 +68,7 @@ static void commit_abort(Job *job) { CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); BlockDriverState *top_bs = blk_bs(s->top); + BlockDriverState *commit_top_backing_bs; if (s->chain_frozen) { bdrv_graph_rdlock_main_loop(); @@ -94,8 +95,12 @@ static void commit_abort(Job *job) * XXX Can (or should) we somehow keep 'consistent read' blocked even * after the failed/cancelled commit job is gone? If we already wrote * something to base, the intermediate images aren't valid any more. */ - bdrv_replace_node(s->commit_top_bs, s->commit_top_bs->backing->bs, - &error_abort); + commit_top_backing_bs = s->commit_top_bs->backing->bs; + bdrv_drained_begin(commit_top_backing_bs); + bdrv_graph_wrlock(commit_top_backing_bs); + bdrv_replace_node(s->commit_top_bs, commit_top_backing_bs, &error_abort); + bdrv_graph_wrunlock(); + bdrv_drained_end(commit_top_backing_bs); bdrv_unref(s->commit_top_bs); bdrv_unref(top_bs); @@ -425,7 +430,11 @@ fail: /* commit_top_bs has to be replaced after deleting the block job, * otherwise this would fail because of lack of permissions. */ if (commit_top_bs) { + bdrv_drained_begin(top); + bdrv_graph_wrlock(top); bdrv_replace_node(commit_top_bs, top, &error_abort); + bdrv_graph_wrunlock(); + bdrv_drained_end(top); } } diff --git a/block/mirror.c b/block/mirror.c index f8e439371c..dfc1c416e8 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -712,6 +712,7 @@ static int mirror_exit_common(Job *job) * these permissions any more means that we can't allow any new requests on * mirror_top_bs from now on, so keep it drained. */ bdrv_drained_begin(mirror_top_bs); + bdrv_drained_begin(target_bs); bs_opaque->stop = true; bdrv_graph_rdlock_main_loop(); @@ -757,15 +758,13 @@ static int mirror_exit_common(Job *job) /* The mirror job has no requests in flight any more, but we need to * drain potential other users of the BDS before changing the graph. */ assert(s->in_drain); - bdrv_drained_begin(target_bs); + bdrv_drained_begin(to_replace); /* * Cannot use check_to_replace_node() here, because that would * check for an op blocker on @to_replace, and we have our own * there. - * - * TODO Pull out the writer lock from bdrv_replace_node() to here */ - bdrv_graph_rdlock_main_loop(); + bdrv_graph_wrlock(target_bs); if (bdrv_recurse_can_replace(src, to_replace)) { bdrv_replace_node(to_replace, target_bs, &local_err); } else { @@ -774,8 +773,8 @@ static int mirror_exit_common(Job *job) "would not lead to an abrupt change of visible data", to_replace->node_name, target_bs->node_name); } - bdrv_graph_rdunlock_main_loop(); - bdrv_drained_end(target_bs); + bdrv_graph_wrunlock(); + bdrv_drained_end(to_replace); if (local_err) { error_report_err(local_err); ret = -EPERM; @@ -790,7 +789,6 @@ static int mirror_exit_common(Job *job) aio_context_release(replace_aio_context); } g_free(s->replaces); - bdrv_unref(target_bs); /* * Remove the mirror filter driver from the graph. Before this, get rid of @@ -798,7 +796,12 @@ static int mirror_exit_common(Job *job) * valid. */ block_job_remove_all_bdrv(bjob); + bdrv_graph_wrlock(mirror_top_bs); bdrv_replace_node(mirror_top_bs, mirror_top_bs->backing->bs, &error_abort); + bdrv_graph_wrunlock(); + + bdrv_drained_end(target_bs); + bdrv_unref(target_bs); bs_opaque->job = NULL; @@ -1987,11 +1990,14 @@ fail: } bs_opaque->stop = true; - bdrv_graph_rdlock_main_loop(); + bdrv_drained_begin(bs); + bdrv_graph_wrlock(bs); + assert(mirror_top_bs->backing->bs == bs); bdrv_child_refresh_perms(mirror_top_bs, mirror_top_bs->backing, &error_abort); - bdrv_graph_rdunlock_main_loop(); - bdrv_replace_node(mirror_top_bs, mirror_top_bs->backing->bs, &error_abort); + bdrv_replace_node(mirror_top_bs, bs, &error_abort); + bdrv_graph_wrunlock(); + bdrv_drained_end(bs); bdrv_unref(mirror_top_bs); diff --git a/blockdev.c b/blockdev.c index f04faf6373..5bc921236c 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1610,7 +1610,12 @@ static void external_snapshot_abort(void *opaque) aio_context_acquire(aio_context); } + bdrv_drained_begin(state->new_bs); + bdrv_graph_wrlock(state->old_bs); bdrv_replace_node(state->new_bs, state->old_bs, &error_abort); + bdrv_graph_wrunlock(); + bdrv_drained_end(state->new_bs); + bdrv_unref(state->old_bs); /* bdrv_replace_node() ref'ed old_bs */ aio_context_release(aio_context); diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h index a1fd70ec97..9e0ccc1c32 100644 --- a/include/block/block-global-state.h +++ b/include/block/block-global-state.h @@ -71,8 +71,10 @@ bdrv_co_create_file(const char *filename, QemuOpts *opts, Error **errp); BlockDriverState *bdrv_new(void); int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, Error **errp); -int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, - Error **errp); + +int GRAPH_WRLOCK +bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, Error **errp); + int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs, Error **errp); BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *node_options, diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c index 40d17b4c5a..b16f831c23 100644 --- a/tests/unit/test-bdrv-drain.c +++ b/tests/unit/test-bdrv-drain.c @@ -2000,7 +2000,13 @@ static void do_test_replace_child_mid_drain(int old_drain_count, parent_s->was_undrained = false; g_assert(parent_bs->quiesce_counter == old_drain_count); + bdrv_drained_begin(old_child_bs); + bdrv_drained_begin(new_child_bs); + bdrv_graph_wrlock(NULL); bdrv_replace_node(old_child_bs, new_child_bs, &error_abort); + bdrv_graph_wrunlock(); + bdrv_drained_end(new_child_bs); + bdrv_drained_end(old_child_bs); g_assert(parent_bs->quiesce_counter == new_drain_count); if (!old_drain_count && !new_drain_count) { diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c index 8609f7f42b..22d4cd83f6 100644 --- a/tests/unit/test-bdrv-graph-mod.c +++ b/tests/unit/test-bdrv-graph-mod.c @@ -234,11 +234,16 @@ static void test_parallel_exclusive_write(void) BlockDriverState *fl1 = pass_through_node("fl1"); BlockDriverState *fl2 = pass_through_node("fl2"); + bdrv_drained_begin(fl1); + bdrv_drained_begin(fl2); + /* * bdrv_attach_child() eats child bs reference, so we need two @base - * references for two filters: + * references for two filters. We also need an additional @fl1 reference so + * that it still exists when we want to undrain it. */ bdrv_ref(base); + bdrv_ref(fl1); bdrv_graph_wrlock(NULL); bdrv_attach_child(top, fl1, "backing", &child_of_bds, @@ -250,10 +255,14 @@ static void test_parallel_exclusive_write(void) bdrv_attach_child(fl2, base, "backing", &child_of_bds, BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, &error_abort); - bdrv_graph_wrunlock(); bdrv_replace_node(fl1, fl2, &error_abort); + bdrv_graph_wrunlock(); + bdrv_drained_end(fl2); + bdrv_drained_end(fl1); + + bdrv_unref(fl1); bdrv_unref(fl2); bdrv_unref(top); } From 645198d58b6e1236373f5375fdaa8e3b15519108 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 1 Nov 2023 21:43:22 +0100 Subject: [PATCH 864/974] tests/avocado: Allow newer versions of tesseract in the nextcube test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Current Linux distros ship version 5 of the tesseract OCR software, so the nextcube screen test is ignored there. Let's make the check more flexible to allow newer versions, too, and remove the old v3 test since most Linux distros don't ship this version anymore. Message-ID: <20231101204323.35533-1-huth@tuxfamily.org> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth --- tests/avocado/machine_m68k_nextcube.py | 15 +++------------ tests/avocado/tesseract_utils.py | 4 ++-- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/tests/avocado/machine_m68k_nextcube.py b/tests/avocado/machine_m68k_nextcube.py index f1205d7fc0..1f3c883910 100644 --- a/tests/avocado/machine_m68k_nextcube.py +++ b/tests/avocado/machine_m68k_nextcube.py @@ -55,25 +55,16 @@ class NextCubeMachine(QemuSystemTest): self.assertEqual(width, 1120) self.assertEqual(height, 832) - @skipUnless(tesseract_available(3), 'tesseract v3 OCR tool not available') - def test_bootrom_framebuffer_ocr_with_tesseract_v3(self): - screenshot_path = os.path.join(self.workdir, "dump.ppm") - self.check_bootrom_framebuffer(screenshot_path) - lines = tesseract_ocr(screenshot_path, tesseract_version=3) - text = '\n'.join(lines) - self.assertIn('Backplane', text) - self.assertIn('Ethernet address', text) - # Tesseract 4 adds a new OCR engine based on LSTM neural networks. The # new version is faster and more accurate than version 3. The drawback is # that it is still alpha-level software. - @skipUnless(tesseract_available(4), 'tesseract v4 OCR tool not available') - def test_bootrom_framebuffer_ocr_with_tesseract_v4(self): + @skipUnless(tesseract_available(4), 'tesseract OCR tool not available') + def test_bootrom_framebuffer_ocr_with_tesseract(self): screenshot_path = os.path.join(self.workdir, "dump.ppm") self.check_bootrom_framebuffer(screenshot_path) lines = tesseract_ocr(screenshot_path, tesseract_version=4) text = '\n'.join(lines) - self.assertIn('Testing the FPU, SCC', text) + self.assertIn('Testing the FPU', text) self.assertIn('System test failed. Error code', text) self.assertIn('Boot command', text) self.assertIn('Next>', text) diff --git a/tests/avocado/tesseract_utils.py b/tests/avocado/tesseract_utils.py index 72cd9ab798..476f528147 100644 --- a/tests/avocado/tesseract_utils.py +++ b/tests/avocado/tesseract_utils.py @@ -21,13 +21,13 @@ def tesseract_available(expected_version): version = res.stdout_text.split()[1] except IndexError: version = res.stderr_text.split()[1] - return int(version.split('.')[0]) == expected_version + return int(version.split('.')[0]) >= expected_version match = re.match(r'tesseract\s(\d)', res) if match is None: return False # now this is guaranteed to be a digit - return int(match.groups()[0]) == expected_version + return int(match.groups()[0]) >= expected_version def tesseract_ocr(image_path, tesseract_args='', tesseract_version=3): From 81f993828bce9a9afd72da17b7672cb8bd121e63 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 24 Oct 2023 12:07:03 +0200 Subject: [PATCH 865/974] s390/sclp: fix SCLP facility map Qemu's SCLP implementation incorrectly reports that it supports CPU reconfiguration. If a guest issues a CPU reconfiguration request it is rejected as invalid command. Fix the SCLP_HAS_CPU_INFO mask, and remove the unused SCLP_CMDW_CONFIGURE_CPU and SCLP_CMDW_DECONFIGURE_CPU defines. Reviewed-by: Eric Farman Reviewed-by: Halil Pasic Signed-off-by: Heiko Carstens Message-ID: <20231024100703.929679-1-hca@linux.ibm.com> Signed-off-by: Thomas Huth --- include/hw/s390x/sclp.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/include/hw/s390x/sclp.h b/include/hw/s390x/sclp.h index 9aef6d9370..b4ecd04e23 100644 --- a/include/hw/s390x/sclp.h +++ b/include/hw/s390x/sclp.h @@ -38,10 +38,8 @@ #define MAX_STORAGE_INCREMENTS 1020 /* CPU hotplug SCLP codes */ -#define SCLP_HAS_CPU_INFO 0x0C00000000000000ULL +#define SCLP_HAS_CPU_INFO 0x0800000000000000ULL #define SCLP_CMDW_READ_CPU_INFO 0x00010001 -#define SCLP_CMDW_CONFIGURE_CPU 0x00110001 -#define SCLP_CMDW_DECONFIGURE_CPU 0x00100001 /* SCLP PCI codes */ #define SCLP_HAS_IOA_RECONFIG 0x0000000040000000ULL From ad63e6d69326a2db0ed5ab8c9277b5b504a919a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 30 Oct 2023 10:31:50 +0100 Subject: [PATCH 866/974] target/s390x/cpu_models: Use 'first_cpu' in s390_get_feat_block() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We already have a global 'first_cpu' variable storing a pointer to the first CPU, no need to use a static one. Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20231030093150.65297-1-philmd@linaro.org> Reviewed-by: David Hildenbrand Signed-off-by: Thomas Huth --- target/s390x/cpu_models.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index 4dead48650..540d445023 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -196,11 +196,7 @@ uint32_t s390_get_ibc_val(void) void s390_get_feat_block(S390FeatType type, uint8_t *data) { - static S390CPU *cpu; - - if (!cpu) { - cpu = S390_CPU(qemu_get_cpu(0)); - } + S390CPU *cpu = S390_CPU(first_cpu); if (!cpu || !cpu->model) { return; From aba2ec341c6d20c8dc3e6ecf87fa7c1a71e30c1e Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Mon, 6 Nov 2023 10:31:22 +0100 Subject: [PATCH 867/974] target/s390x: Fix CLC corrupting cc_src CLC updates cc_src before accessing the second operand; if the latter is inaccessible, the former ends up containing a bogus value. Fix by reading cc_src into a temporary first. Fixes: 4f7403d52b1c ("target-s390: Convert CLC") Closes: https://gitlab.com/qemu-project/qemu/-/issues/1865 Cc: qemu-stable@nongnu.org Reviewed-by: Richard Henderson Signed-off-by: Ilya Leoshkevich Reviewed-by: David Hildenbrand Message-ID: <20231106093605.1349201-2-iii@linux.ibm.com> Signed-off-by: Thomas Huth --- target/s390x/tcg/translate.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index 4bae1509f5..a0d6a2a35d 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -2007,6 +2007,7 @@ static DisasJumpType op_cksm(DisasContext *s, DisasOps *o) static DisasJumpType op_clc(DisasContext *s, DisasOps *o) { int l = get_field(s, l1); + TCGv_i64 src; TCGv_i32 vl; MemOp mop; @@ -2016,9 +2017,11 @@ static DisasJumpType op_clc(DisasContext *s, DisasOps *o) case 4: case 8: mop = ctz32(l + 1) | MO_TE; - tcg_gen_qemu_ld_tl(cc_src, o->addr1, get_mem_index(s), mop); + /* Do not update cc_src yet: loading cc_dst may cause an exception. */ + src = tcg_temp_new_i64(); + tcg_gen_qemu_ld_tl(src, o->addr1, get_mem_index(s), mop); tcg_gen_qemu_ld_tl(cc_dst, o->in2, get_mem_index(s), mop); - gen_op_update2_cc_i64(s, CC_OP_LTUGTU_64, cc_src, cc_dst); + gen_op_update2_cc_i64(s, CC_OP_LTUGTU_64, src, cc_dst); return DISAS_NEXT; default: vl = tcg_constant_i32(l); From 43fecbe7a53fe8e5a6aff0d6471b1cc624e26b51 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Mon, 6 Nov 2023 10:31:23 +0100 Subject: [PATCH 868/974] tests/tcg/s390x: Test CLC with inaccessible second operand Add a small test to prevent regressions. Signed-off-by: Ilya Leoshkevich Reviewed-by: Richard Henderson Message-ID: <20231106093605.1349201-3-iii@linux.ibm.com> Signed-off-by: Thomas Huth --- tests/tcg/s390x/Makefile.target | 1 + tests/tcg/s390x/clc.c | 48 +++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 tests/tcg/s390x/clc.c diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target index 826f0a18e4..ccd4f4e68d 100644 --- a/tests/tcg/s390x/Makefile.target +++ b/tests/tcg/s390x/Makefile.target @@ -41,6 +41,7 @@ TESTS+=larl TESTS+=mdeb TESTS+=cgebra TESTS+=clgebr +TESTS+=clc cdsg: CFLAGS+=-pthread cdsg: LDFLAGS+=-pthread diff --git a/tests/tcg/s390x/clc.c b/tests/tcg/s390x/clc.c new file mode 100644 index 0000000000..e14189bd75 --- /dev/null +++ b/tests/tcg/s390x/clc.c @@ -0,0 +1,48 @@ +/* + * Test the CLC instruction. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include +#include +#include +#include + +static void handle_sigsegv(int sig, siginfo_t *info, void *ucontext) +{ + mcontext_t *mcontext = &((ucontext_t *)ucontext)->uc_mcontext; + if (mcontext->gregs[0] != 600) { + write(STDERR_FILENO, "bad r0\n", 7); + _exit(EXIT_FAILURE); + } + if (((mcontext->psw.mask >> 44) & 3) != 1) { + write(STDERR_FILENO, "bad cc\n", 7); + _exit(EXIT_FAILURE); + } + _exit(EXIT_SUCCESS); +} + +int main(void) +{ + register unsigned long r0 asm("r0"); + unsigned long mem = 42, rhs = 500; + struct sigaction act; + int err; + + memset(&act, 0, sizeof(act)); + act.sa_sigaction = handle_sigsegv; + act.sa_flags = SA_SIGINFO; + err = sigaction(SIGSEGV, &act, NULL); + assert(err == 0); + + r0 = 100; + asm("algr %[r0],%[rhs]\n" + "clc 0(8,%[mem]),0(0)\n" /* The 2nd operand will cause a SEGV. */ + : [r0] "+r" (r0) + : [mem] "r" (&mem) + , [rhs] "r" (rhs) + : "cc", "memory"); + + return EXIT_FAILURE; +} From bea402482a8c94389638cbd3d7fe3963fb317f4c Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Mon, 6 Nov 2023 10:31:24 +0100 Subject: [PATCH 869/974] target/s390x: Fix LAALG not updating cc_src LAALG uses op_laa() and wout_addu64(). The latter expects cc_src to be set, but the former does not do it. This can lead to assertion failures if something sets cc_src to neither 0 nor 1 before. Fix by introducing op_laa_addu64(), which sets cc_src, and using it for LAALG. Fixes: 4dba4d6fef61 ("target/s390x: Use atomic operations for LOAD AND OP") Cc: qemu-stable@nongnu.org Signed-off-by: Ilya Leoshkevich Reviewed-by: David Hildenbrand Reviewed-by: Richard Henderson Message-ID: <20231106093605.1349201-4-iii@linux.ibm.com> Signed-off-by: Thomas Huth --- target/s390x/tcg/insn-data.h.inc | 2 +- target/s390x/tcg/translate.c | 19 +++++++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/target/s390x/tcg/insn-data.h.inc b/target/s390x/tcg/insn-data.h.inc index 0bfd88d3c3..2f07f39d9c 100644 --- a/target/s390x/tcg/insn-data.h.inc +++ b/target/s390x/tcg/insn-data.h.inc @@ -442,7 +442,7 @@ D(0xebe8, LAAG, RSY_a, ILA, r3, a2, new, in2_r1, laa, adds64, MO_TEUQ) /* LOAD AND ADD LOGICAL */ D(0xebfa, LAAL, RSY_a, ILA, r3_32u, a2, new, in2_r1_32, laa, addu32, MO_TEUL) - D(0xebea, LAALG, RSY_a, ILA, r3, a2, new, in2_r1, laa, addu64, MO_TEUQ) + D(0xebea, LAALG, RSY_a, ILA, r3, a2, new, in2_r1, laa_addu64, addu64, MO_TEUQ) /* LOAD AND AND */ D(0xebf4, LAN, RSY_a, ILA, r3_32s, a2, new, in2_r1_32, lan, nz32, MO_TESL) D(0xebe4, LANG, RSY_a, ILA, r3, a2, new, in2_r1, lan, nz64, MO_TEUQ) diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index a0d6a2a35d..62ab2be8b1 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -2677,17 +2677,32 @@ static DisasJumpType op_kxb(DisasContext *s, DisasOps *o) return DISAS_NEXT; } -static DisasJumpType op_laa(DisasContext *s, DisasOps *o) +static DisasJumpType help_laa(DisasContext *s, DisasOps *o, bool addu64) { /* The real output is indeed the original value in memory; recompute the addition for the computation of CC. */ tcg_gen_atomic_fetch_add_i64(o->in2, o->in2, o->in1, get_mem_index(s), s->insn->data | MO_ALIGN); /* However, we need to recompute the addition for setting CC. */ - tcg_gen_add_i64(o->out, o->in1, o->in2); + if (addu64) { + tcg_gen_movi_i64(cc_src, 0); + tcg_gen_add2_i64(o->out, cc_src, o->in1, cc_src, o->in2, cc_src); + } else { + tcg_gen_add_i64(o->out, o->in1, o->in2); + } return DISAS_NEXT; } +static DisasJumpType op_laa(DisasContext *s, DisasOps *o) +{ + return help_laa(s, o, false); +} + +static DisasJumpType op_laa_addu64(DisasContext *s, DisasOps *o) +{ + return help_laa(s, o, true); +} + static DisasJumpType op_lan(DisasContext *s, DisasOps *o) { /* The real output is indeed the original value in memory; From ebc14107f1f3ac1db13132cd28cf94adcd38e5d7 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Mon, 6 Nov 2023 10:31:25 +0100 Subject: [PATCH 870/974] tests/tcg/s390x: Test LAALG with negative cc_src Add a small test to prevent regressions. Signed-off-by: Ilya Leoshkevich Reviewed-by: Richard Henderson Message-ID: <20231106093605.1349201-5-iii@linux.ibm.com> Signed-off-by: Thomas Huth --- tests/tcg/s390x/Makefile.target | 1 + tests/tcg/s390x/laalg.c | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 tests/tcg/s390x/laalg.c diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target index ccd4f4e68d..a476547b65 100644 --- a/tests/tcg/s390x/Makefile.target +++ b/tests/tcg/s390x/Makefile.target @@ -42,6 +42,7 @@ TESTS+=mdeb TESTS+=cgebra TESTS+=clgebr TESTS+=clc +TESTS+=laalg cdsg: CFLAGS+=-pthread cdsg: LDFLAGS+=-pthread diff --git a/tests/tcg/s390x/laalg.c b/tests/tcg/s390x/laalg.c new file mode 100644 index 0000000000..797d168bb1 --- /dev/null +++ b/tests/tcg/s390x/laalg.c @@ -0,0 +1,27 @@ +/* + * Test the LAALG instruction. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include + +int main(void) +{ + unsigned long cc = 0, op1, op2 = 40, op3 = 2; + + asm("slgfi %[cc],1\n" /* Set cc_src = -1. */ + "laalg %[op1],%[op3],%[op2]\n" + "ipm %[cc]" + : [cc] "+r" (cc) + , [op1] "=r" (op1) + , [op2] "+T" (op2) + : [op3] "r" (op3) + : "cc"); + + assert(cc == 0xffffffff10ffffff); + assert(op1 == 40); + assert(op2 == 42); + + return EXIT_SUCCESS; +} From d7e61d6b39cd3d644bc33bcb517d24fca479704c Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Mon, 6 Nov 2023 10:31:26 +0100 Subject: [PATCH 871/974] tests/tcg/s390x: Test ADD LOGICAL WITH CARRY Add a test that tries different combinations of ADD LOGICAL WITH CARRY instructions. Signed-off-by: Ilya Leoshkevich Message-ID: <20231106093605.1349201-6-iii@linux.ibm.com> Signed-off-by: Thomas Huth --- tests/tcg/s390x/Makefile.target | 1 + tests/tcg/s390x/add-logical-with-carry.c | 156 +++++++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 tests/tcg/s390x/add-logical-with-carry.c diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target index a476547b65..0e670f3f8b 100644 --- a/tests/tcg/s390x/Makefile.target +++ b/tests/tcg/s390x/Makefile.target @@ -43,6 +43,7 @@ TESTS+=cgebra TESTS+=clgebr TESTS+=clc TESTS+=laalg +TESTS+=add-logical-with-carry cdsg: CFLAGS+=-pthread cdsg: LDFLAGS+=-pthread diff --git a/tests/tcg/s390x/add-logical-with-carry.c b/tests/tcg/s390x/add-logical-with-carry.c new file mode 100644 index 0000000000..d982f8a651 --- /dev/null +++ b/tests/tcg/s390x/add-logical-with-carry.c @@ -0,0 +1,156 @@ +/* + * Test ADD LOGICAL WITH CARRY instructions. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include + +static const struct test { + const char *name; + unsigned long values[3]; + unsigned long exp_sum; + int exp_cc; +} tests[] = { + /* + * Each test starts with CC 0 and executes two chained ADD LOGICAL WITH + * CARRY instructions on three input values. The values must be compatible + * with both 32- and 64-bit test functions. + */ + + /* NAME VALUES EXP_SUM EXP_CC */ + { "cc0->cc0", {0, 0, 0}, 0, 0, }, + { "cc0->cc1", {0, 0, 42}, 42, 1, }, + /* cc0->cc2 is not possible */ + /* cc0->cc3 is not possible */ + /* cc1->cc0 is not possible */ + { "cc1->cc1", {-3, 1, 1}, -1, 1, }, + { "cc1->cc2", {-3, 1, 2}, 0, 2, }, + { "cc1->cc3", {-3, 1, -1}, -3, 3, }, + /* cc2->cc0 is not possible */ + { "cc2->cc1", {-1, 1, 1}, 2, 1, }, + { "cc2->cc2", {-1, 1, -1}, 0, 2, }, + /* cc2->cc3 is not possible */ + /* cc3->cc0 is not possible */ + { "cc3->cc1", {-1, 2, 1}, 3, 1, }, + { "cc3->cc2", {-1, 2, -2}, 0, 2, }, + { "cc3->cc3", {-1, 2, -1}, 1, 3, }, +}; + +/* Test ALCR (register variant) followed by ALC (memory variant). */ +static unsigned long test32rm(unsigned long a, unsigned long b, + unsigned long c, int *cc) +{ + unsigned int a32 = a, b32 = b, c32 = c; + + asm("xr %[cc],%[cc]\n" + "alcr %[a],%[b]\n" + "alc %[a],%[c]\n" + "ipm %[cc]" + : [a] "+&r" (a32), [cc] "+&r" (*cc) + : [b] "r" (b32), [c] "T" (c32) + : "cc"); + *cc >>= 28; + + return (int)a32; +} + +/* Test ALC (memory variant) followed by ALCR (register variant). */ +static unsigned long test32mr(unsigned long a, unsigned long b, + unsigned long c, int *cc) +{ + unsigned int a32 = a, b32 = b, c32 = c; + + asm("xr %[cc],%[cc]\n" + "alc %[a],%[b]\n" + "alcr %[c],%[a]\n" + "ipm %[cc]" + : [a] "+&r" (a32), [c] "+&r" (c32), [cc] "+&r" (*cc) + : [b] "T" (b32) + : "cc"); + *cc >>= 28; + + return (int)c32; +} + +/* Test ALCGR (register variant) followed by ALCG (memory variant). */ +static unsigned long test64rm(unsigned long a, unsigned long b, + unsigned long c, int *cc) +{ + asm("xr %[cc],%[cc]\n" + "alcgr %[a],%[b]\n" + "alcg %[a],%[c]\n" + "ipm %[cc]" + : [a] "+&r" (a), [cc] "+&r" (*cc) + : [b] "r" (b), [c] "T" (c) + : "cc"); + *cc >>= 28; + return a; +} + +/* Test ALCG (memory variant) followed by ALCGR (register variant). */ +static unsigned long test64mr(unsigned long a, unsigned long b, + unsigned long c, int *cc) +{ + asm("xr %[cc],%[cc]\n" + "alcg %[a],%[b]\n" + "alcgr %[c],%[a]\n" + "ipm %[cc]" + : [a] "+&r" (a), [c] "+&r" (c), [cc] "+&r" (*cc) + : [b] "T" (b) + : "cc"); + *cc >>= 28; + return c; +} + +static const struct test_func { + const char *name; + unsigned long (*ptr)(unsigned long, unsigned long, unsigned long, int *); +} test_funcs[] = { + { "test32rm", test32rm }, + { "test32mr", test32mr }, + { "test64rm", test64rm }, + { "test64mr", test64mr }, +}; + +static const struct test_perm { + const char *name; + size_t a_idx, b_idx, c_idx; +} test_perms[] = { + { "a, b, c", 0, 1, 2 }, + { "b, a, c", 1, 0, 2 }, +}; + +int main(void) +{ + unsigned long a, b, c, sum; + int result = EXIT_SUCCESS; + const struct test_func *f; + const struct test_perm *p; + size_t i, j, k; + const struct test *t; + int cc; + + for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { + t = &tests[i]; + for (j = 0; j < sizeof(test_funcs) / sizeof(test_funcs[0]); j++) { + f = &test_funcs[j]; + for (k = 0; k < sizeof(test_perms) / sizeof(test_perms[0]); k++) { + p = &test_perms[k]; + a = t->values[p->a_idx]; + b = t->values[p->b_idx]; + c = t->values[p->c_idx]; + sum = f->ptr(a, b, c, &cc); + if (sum != t->exp_sum || cc != t->exp_cc) { + fprintf(stderr, + "[ FAILED ] %s %s(0x%lx, 0x%lx, 0x%lx) returned 0x%lx cc %d, expected 0x%lx cc %d\n", + t->name, f->name, a, b, c, sum, cc, + t->exp_sum, t->exp_cc); + result = EXIT_FAILURE; + } + } + } + } + + return result; +} From 3e19fbc0c51a62d0c021e1ae768da0df64855927 Mon Sep 17 00:00:00 2001 From: Nina Schoetterl-Glausch Date: Fri, 27 Oct 2023 18:36:37 +0200 Subject: [PATCH 872/974] target/s390x/cpu topology: Fix ordering and creation of TLEs In case of horizontal polarization entitlement has no effect on ordering. Moreover, since the comparison is used to insert CPUs at the correct position in the TLE list, this affects the creation of TLEs and now correctly collapses horizontally polarized CPUs into one TLE. Fixes: f4f54b582f ("target/s390x/cpu topology: handle STSI(15) and build the SYSIB") Signed-off-by: Nina Schoetterl-Glausch Message-ID: <20231027163637.3060537-1-nsg@linux.ibm.com> Signed-off-by: Thomas Huth --- target/s390x/kvm/stsi-topology.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/target/s390x/kvm/stsi-topology.c b/target/s390x/kvm/stsi-topology.c index efd2aa71f1..c8d6389cd8 100644 --- a/target/s390x/kvm/stsi-topology.c +++ b/target/s390x/kvm/stsi-topology.c @@ -210,6 +210,9 @@ static S390TopologyId s390_topology_from_cpu(S390CPU *cpu) static int s390_topology_id_cmp(const S390TopologyId *l, const S390TopologyId *r) { + int l_polarization = l->vertical ? l->entitlement : 0; + int r_polarization = r->vertical ? r->entitlement : 0; + /* * lexical order, compare less significant values only if more significant * ones are equal @@ -219,9 +222,8 @@ static int s390_topology_id_cmp(const S390TopologyId *l, l->book - r->book ?: l->socket - r->socket ?: l->type - r->type ?: - /* logic is inverted for the next three */ - r->vertical - l->vertical ?: - r->entitlement - l->entitlement ?: + /* logic is inverted for the next two */ + r_polarization - l_polarization ?: r->dedicated - l->dedicated ?: l->origin - r->origin; } From d9656f860a38f83efc9710c515eab6a5b015134c Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Tue, 7 Nov 2023 19:40:17 +0100 Subject: [PATCH 873/974] hw/ppc: Add emulation of AmigaOne XE board The AmigaOne is a rebranded MAI Teron board that uses U-Boot firmware with patches to support AmigaOS and is very similar to pegasos2 so can be easily emulated sharing most code with pegasos2. The reason to emulate it is that AmigaOS comes in different versions for AmigaOne and PegasosII which only have drivers for one machine and firmware so these only run on the specific machine. Adding this board allows another AmigaOS version to be used reusing already existing peagasos2 emulation. (The AmigaOne was the first of these boards so likely most widespread which then inspired Pegasos that was later replaced with PegasosII due to problems with Articia S, so these have a lot of similarity. Pegasos mainly ran MorphOS while the PegasosII version of AmigaOS was added later and therefore less common than the AmigaOne version.) Signed-off-by: BALATON Zoltan Tested-by: Rene Engel Acked-by: Daniel Henrique Barboza Message-ID: <804935e7a5921548d630576159ae2c758fe6e275.1699382232.git.balaton@eik.bme.hu> Signed-off-by: Daniel Henrique Barboza --- MAINTAINERS | 8 ++ configs/devices/ppc-softmmu/default.mak | 1 + hw/ppc/Kconfig | 7 + hw/ppc/amigaone.c | 166 ++++++++++++++++++++++++ hw/ppc/meson.build | 2 + 5 files changed, 184 insertions(+) create mode 100644 hw/ppc/amigaone.c diff --git a/MAINTAINERS b/MAINTAINERS index b86ea7f75a..3d0aec7ffa 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1536,6 +1536,14 @@ F: hw/pci-host/mv64361.c F: hw/pci-host/mv643xx.h F: include/hw/pci-host/mv64361.h +amigaone +M: BALATON Zoltan +L: qemu-ppc@nongnu.org +S: Maintained +F: hw/ppc/amigaone.c +F: hw/pci-host/articia.c +F: include/hw/pci-host/articia.h + Virtual Open Firmware (VOF) M: Alexey Kardashevskiy R: David Gibson diff --git a/configs/devices/ppc-softmmu/default.mak b/configs/devices/ppc-softmmu/default.mak index a887f5438b..b85fd2bcd7 100644 --- a/configs/devices/ppc-softmmu/default.mak +++ b/configs/devices/ppc-softmmu/default.mak @@ -14,6 +14,7 @@ CONFIG_SAM460EX=y CONFIG_MAC_OLDWORLD=y CONFIG_MAC_NEWWORLD=y +CONFIG_AMIGAONE=y CONFIG_PEGASOS2=y # For PReP diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig index 5dfbf47ef5..56f0475a8e 100644 --- a/hw/ppc/Kconfig +++ b/hw/ppc/Kconfig @@ -69,6 +69,13 @@ config SAM460EX select USB_OHCI select FDT_PPC +config AMIGAONE + bool + imply ATI_VGA + select ARTICIA + select VT82C686 + select SMBUS_EEPROM + config PEGASOS2 bool imply ATI_VGA diff --git a/hw/ppc/amigaone.c b/hw/ppc/amigaone.c new file mode 100644 index 0000000000..992a55e632 --- /dev/null +++ b/hw/ppc/amigaone.c @@ -0,0 +1,166 @@ +/* + * QEMU Eyetech AmigaOne/Mai Logic Teron emulation + * + * Copyright (c) 2023 BALATON Zoltan + * + * This work is licensed under the GNU GPL license version 2 or later. + * + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qemu/datadir.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "hw/ppc/ppc.h" +#include "hw/boards.h" +#include "hw/loader.h" +#include "hw/pci-host/articia.h" +#include "hw/isa/vt82c686.h" +#include "hw/ide/pci.h" +#include "hw/i2c/smbus_eeprom.h" +#include "hw/ppc/ppc.h" +#include "sysemu/qtest.h" +#include "sysemu/reset.h" +#include "kvm_ppc.h" + +#define BUS_FREQ_HZ 100000000 + +/* + * Firmware binary available at + * https://www.hyperion-entertainment.com/index.php/downloads?view=files&parent=28 + * then "tail -c 524288 updater.image >u-boot-amigaone.bin" + * + * BIOS emulator in firmware cannot run QEMU vgabios and hangs on it, use + * -device VGA,romfile=VGABIOS-lgpl-latest.bin + * from http://www.nongnu.org/vgabios/ instead. + */ +#define PROM_FILENAME "u-boot-amigaone.bin" +#define PROM_ADDR 0xfff00000 +#define PROM_SIZE (512 * KiB) + +static void amigaone_cpu_reset(void *opaque) +{ + PowerPCCPU *cpu = opaque; + + cpu_reset(CPU(cpu)); + cpu_ppc_tb_reset(&cpu->env); +} + +static void fix_spd_data(uint8_t *spd) +{ + uint32_t bank_size = 4 * MiB * spd[31]; + uint32_t rows = bank_size / spd[13] / spd[17]; + spd[3] = ctz32(rows) - spd[4]; +} + +static void amigaone_init(MachineState *machine) +{ + PowerPCCPU *cpu; + CPUPPCState *env; + MemoryRegion *rom, *pci_mem, *mr; + const char *fwname = machine->firmware ?: PROM_FILENAME; + char *filename; + ssize_t sz; + PCIBus *pci_bus; + Object *via; + DeviceState *dev; + I2CBus *i2c_bus; + uint8_t *spd_data; + int i; + + /* init CPU */ + cpu = POWERPC_CPU(cpu_create(machine->cpu_type)); + env = &cpu->env; + if (PPC_INPUT(env) != PPC_FLAGS_INPUT_6xx) { + error_report("Incompatible CPU, only 6xx bus supported"); + exit(1); + } + cpu_ppc_tb_init(env, BUS_FREQ_HZ / 4); + qemu_register_reset(amigaone_cpu_reset, cpu); + + /* RAM */ + if (machine->ram_size > 2 * GiB) { + error_report("RAM size more than 2 GiB is not supported"); + exit(1); + } + memory_region_add_subregion(get_system_memory(), 0, machine->ram); + if (machine->ram_size < 1 * GiB + 32 * KiB) { + /* Firmware uses this area for startup */ + mr = g_new(MemoryRegion, 1); + memory_region_init_ram(mr, NULL, "init-cache", 32 * KiB, &error_fatal); + memory_region_add_subregion(get_system_memory(), 0x40000000, mr); + } + + /* allocate and load firmware */ + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, fwname); + if (filename) { + rom = g_new(MemoryRegion, 1); + memory_region_init_rom(rom, NULL, "rom", PROM_SIZE, &error_fatal); + memory_region_add_subregion(get_system_memory(), PROM_ADDR, rom); + sz = load_image_targphys(filename, PROM_ADDR, PROM_SIZE); + if (sz <= 0 || sz > PROM_SIZE) { + error_report("Could not load firmware '%s'", filename); + exit(1); + } + g_free(filename); + } else if (!qtest_enabled()) { + error_report("Could not find firmware '%s'", fwname); + exit(1); + } + + /* Articia S */ + dev = sysbus_create_simple(TYPE_ARTICIA, 0xfe000000, NULL); + + i2c_bus = I2C_BUS(qdev_get_child_bus(dev, "smbus")); + if (machine->ram_size > 512 * MiB) { + spd_data = spd_data_generate(SDR, machine->ram_size / 2); + } else { + spd_data = spd_data_generate(SDR, machine->ram_size); + } + fix_spd_data(spd_data); + smbus_eeprom_init_one(i2c_bus, 0x51, spd_data); + if (machine->ram_size > 512 * MiB) { + smbus_eeprom_init_one(i2c_bus, 0x52, spd_data); + } + + pci_mem = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1); + mr = g_new(MemoryRegion, 1); + memory_region_init_alias(mr, OBJECT(dev), "pci-mem-low", pci_mem, + 0, 0x1000000); + memory_region_add_subregion(get_system_memory(), 0xfd000000, mr); + mr = g_new(MemoryRegion, 1); + memory_region_init_alias(mr, OBJECT(dev), "pci-mem-high", pci_mem, + 0x80000000, 0x7d000000); + memory_region_add_subregion(get_system_memory(), 0x80000000, mr); + pci_bus = PCI_BUS(qdev_get_child_bus(dev, "pci.0")); + + /* VIA VT82c686B South Bridge (multifunction PCI device) */ + via = OBJECT(pci_create_simple_multifunction(pci_bus, PCI_DEVFN(7, 0), + TYPE_VT82C686B_ISA)); + object_property_add_alias(OBJECT(machine), "rtc-time", + object_resolve_path_component(via, "rtc"), + "date"); + qdev_connect_gpio_out(DEVICE(via), 0, + qdev_get_gpio_in(DEVICE(cpu), PPC6xx_INPUT_INT)); + for (i = 0; i < PCI_NUM_PINS; i++) { + qdev_connect_gpio_out(dev, i, qdev_get_gpio_in_named(DEVICE(via), + "pirq", i)); + } + pci_ide_create_devs(PCI_DEVICE(object_resolve_path_component(via, "ide"))); + pci_vga_init(pci_bus); +} + +static void amigaone_machine_init(MachineClass *mc) +{ + mc->desc = "Eyetech AmigaOne/Mai Logic Teron"; + mc->init = amigaone_init; + mc->block_default_type = IF_IDE; + mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("7457_v1.2"); + mc->default_display = "std"; + mc->default_ram_id = "ram"; + mc->default_ram_size = 512 * MiB; +} + +DEFINE_MACHINE("amigaone", amigaone_machine_init) diff --git a/hw/ppc/meson.build b/hw/ppc/meson.build index 7c2c52434a..7338f9432a 100644 --- a/hw/ppc/meson.build +++ b/hw/ppc/meson.build @@ -81,6 +81,8 @@ ppc_ss.add(when: 'CONFIG_E500', if_true: files( )) # PowerPC 440 Xilinx ML507 reference board. ppc_ss.add(when: 'CONFIG_VIRTEX', if_true: files('virtex_ml507.c')) +# AmigaOne +ppc_ss.add(when: 'CONFIG_AMIGAONE', if_true: files('amigaone.c')) # Pegasos2 ppc_ss.add(when: 'CONFIG_PEGASOS2', if_true: files('pegasos2.c')) From f46f8e0f37e0ecf68c7ef516bcd74640223a192e Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 27 Oct 2023 13:54:51 +0200 Subject: [PATCH 874/974] tests/avocado: Add test for amigaone board Add an avocado test for the amigaone board that tests it with the firmware. Signed-off-by: BALATON Zoltan Reviewed-by: Daniel Henrique Barboza Message-ID: Signed-off-by: Daniel Henrique Barboza --- tests/avocado/ppc_amiga.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 tests/avocado/ppc_amiga.py diff --git a/tests/avocado/ppc_amiga.py b/tests/avocado/ppc_amiga.py new file mode 100644 index 0000000000..b6f866f91d --- /dev/null +++ b/tests/avocado/ppc_amiga.py @@ -0,0 +1,38 @@ +# Test AmigaNG boards +# +# Copyright (c) 2023 BALATON Zoltan +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +from avocado.utils import archive +from avocado.utils import process +from avocado_qemu import QemuSystemTest +from avocado_qemu import wait_for_console_pattern + +class AmigaOneMachine(QemuSystemTest): + + timeout = 90 + + def test_ppc_amigaone(self): + """ + :avocado: tags=arch:ppc + :avocado: tags=machine:amigaone + :avocado: tags=device:articia + :avocado: tags=accel:tcg + """ + self.require_accelerator("tcg") + tar_name = 'A1Firmware_Floppy_05-Mar-2005.zip' + tar_url = ('https://www.hyperion-entertainment.com/index.php/' + 'downloads?view=download&format=raw&file=25') + tar_hash = 'c52e59bc73e31d8bcc3cc2106778f7ac84f6c755' + zip_file = self.fetch_asset(tar_name, locations=tar_url, + asset_hash=tar_hash) + archive.extract(zip_file, self.workdir) + cmd = f"tail -c 524288 {self.workdir}/floppy_edition/updater.image >{self.workdir}/u-boot-amigaone.bin" + process.run(cmd, shell=True) + + self.vm.set_console() + self.vm.add_args('-bios', self.workdir + '/u-boot-amigaone.bin') + self.vm.launch() + wait_for_console_pattern(self, 'FLASH:') From 263b81ee15af05cbb2ad284aadabb0981a19c941 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 16 Oct 2023 17:20:12 -0500 Subject: [PATCH 875/974] ppc/pnv: Add an I2C controller model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The more recent IBM power processors have an embedded I2C controller that is accessible by software via the XSCOM address space. Each instance of the I2C controller is capable of controlling multiple I2C buses (one at a time). Prior to beginning a transaction on an I2C bus, the bus must be selected by writing the port number associated with the bus into the PORT_NUM field of the MODE register. Once an I2C bus is selected, the status of the bus can be determined by reading the Status and Extended Status registers. I2C bus transactions can be started by writing a command to the Command register and reading/writing data from/to the FIFO register. Not supported : . 10 bit I2C addresses . Multimaster . Slave Signed-off-by: Cédric Le Goater [milesg: Split wiring to powernv9 into its own commit] [milesg: Added more detail to commit message] [milesg: Added SPDX Licensed Identifier to new files] [milesg: updated copyright dates] [milesg: Added use of g_autofree] [milesg: Added NULL check after pnv_i2c_get_bus] Signed-off-by: Glenn Miles Acked-by: Daniel Henrique Barboza Message-ID: <20231016222013.3739530-2-milesg@linux.vnet.ibm.com> Signed-off-by: Daniel Henrique Barboza --- hw/ppc/meson.build | 1 + hw/ppc/pnv_i2c.c | 697 +++++++++++++++++++++++++++++++++++++ include/hw/ppc/pnv_i2c.h | 38 ++ include/hw/ppc/pnv_xscom.h | 3 + 4 files changed, 739 insertions(+) create mode 100644 hw/ppc/pnv_i2c.c create mode 100644 include/hw/ppc/pnv_i2c.h diff --git a/hw/ppc/meson.build b/hw/ppc/meson.build index 7338f9432a..ea44856d43 100644 --- a/hw/ppc/meson.build +++ b/hw/ppc/meson.build @@ -43,6 +43,7 @@ ppc_ss.add(when: 'CONFIG_POWERNV', if_true: files( 'pnv.c', 'pnv_xscom.c', 'pnv_core.c', + 'pnv_i2c.c', 'pnv_lpc.c', 'pnv_psi.c', 'pnv_occ.c', diff --git a/hw/ppc/pnv_i2c.c b/hw/ppc/pnv_i2c.c new file mode 100644 index 0000000000..f75e59e709 --- /dev/null +++ b/hw/ppc/pnv_i2c.c @@ -0,0 +1,697 @@ +/* + * QEMU PowerPC PowerNV Processor I2C model + * + * Copyright (c) 2019-2023, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "qemu/log.h" +#include "sysemu/reset.h" + +#include "hw/irq.h" +#include "hw/qdev-properties.h" + +#include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_chip.h" +#include "hw/ppc/pnv_i2c.h" +#include "hw/ppc/pnv_xscom.h" +#include "hw/ppc/fdt.h" + +#include + +/* I2C FIFO register */ +#define I2C_FIFO_REG 0x4 +#define I2C_FIFO PPC_BITMASK(0, 7) + +/* I2C command register */ +#define I2C_CMD_REG 0x5 +#define I2C_CMD_WITH_START PPC_BIT(0) +#define I2C_CMD_WITH_ADDR PPC_BIT(1) +#define I2C_CMD_READ_CONT PPC_BIT(2) +#define I2C_CMD_WITH_STOP PPC_BIT(3) +#define I2C_CMD_INTR_STEERING PPC_BITMASK(6, 7) /* P9 */ +#define I2C_CMD_INTR_STEER_HOST 1 +#define I2C_CMD_INTR_STEER_OCC 2 +#define I2C_CMD_DEV_ADDR PPC_BITMASK(8, 14) +#define I2C_CMD_READ_NOT_WRITE PPC_BIT(15) +#define I2C_CMD_LEN_BYTES PPC_BITMASK(16, 31) +#define I2C_MAX_TFR_LEN 0xfff0ull + +/* I2C mode register */ +#define I2C_MODE_REG 0x6 +#define I2C_MODE_BIT_RATE_DIV PPC_BITMASK(0, 15) +#define I2C_MODE_PORT_NUM PPC_BITMASK(16, 21) +#define I2C_MODE_ENHANCED PPC_BIT(28) +#define I2C_MODE_DIAGNOSTIC PPC_BIT(29) +#define I2C_MODE_PACING_ALLOW PPC_BIT(30) +#define I2C_MODE_WRAP PPC_BIT(31) + +/* I2C watermark register */ +#define I2C_WATERMARK_REG 0x7 +#define I2C_WATERMARK_HIGH PPC_BITMASK(16, 19) +#define I2C_WATERMARK_LOW PPC_BITMASK(24, 27) + +/* + * I2C interrupt mask and condition registers + * + * NB: The function of 0x9 and 0xa changes depending on whether you're reading + * or writing to them. When read they return the interrupt condition bits + * and on writes they update the interrupt mask register. + * + * The bit definitions are the same for all the interrupt registers. + */ +#define I2C_INTR_MASK_REG 0x8 + +#define I2C_INTR_RAW_COND_REG 0x9 /* read */ +#define I2C_INTR_MASK_OR_REG 0x9 /* write*/ + +#define I2C_INTR_COND_REG 0xa /* read */ +#define I2C_INTR_MASK_AND_REG 0xa /* write */ + +#define I2C_INTR_ALL PPC_BITMASK(16, 31) +#define I2C_INTR_INVALID_CMD PPC_BIT(16) +#define I2C_INTR_LBUS_PARITY_ERR PPC_BIT(17) +#define I2C_INTR_BKEND_OVERRUN_ERR PPC_BIT(18) +#define I2C_INTR_BKEND_ACCESS_ERR PPC_BIT(19) +#define I2C_INTR_ARBT_LOST_ERR PPC_BIT(20) +#define I2C_INTR_NACK_RCVD_ERR PPC_BIT(21) +#define I2C_INTR_DATA_REQ PPC_BIT(22) +#define I2C_INTR_CMD_COMP PPC_BIT(23) +#define I2C_INTR_STOP_ERR PPC_BIT(24) +#define I2C_INTR_I2C_BUSY PPC_BIT(25) +#define I2C_INTR_NOT_I2C_BUSY PPC_BIT(26) +#define I2C_INTR_SCL_EQ_1 PPC_BIT(28) +#define I2C_INTR_SCL_EQ_0 PPC_BIT(29) +#define I2C_INTR_SDA_EQ_1 PPC_BIT(30) +#define I2C_INTR_SDA_EQ_0 PPC_BIT(31) + +/* I2C status register */ +#define I2C_RESET_I2C_REG 0xb /* write */ +#define I2C_RESET_ERRORS 0xc +#define I2C_STAT_REG 0xb /* read */ +#define I2C_STAT_INVALID_CMD PPC_BIT(0) +#define I2C_STAT_LBUS_PARITY_ERR PPC_BIT(1) +#define I2C_STAT_BKEND_OVERRUN_ERR PPC_BIT(2) +#define I2C_STAT_BKEND_ACCESS_ERR PPC_BIT(3) +#define I2C_STAT_ARBT_LOST_ERR PPC_BIT(4) +#define I2C_STAT_NACK_RCVD_ERR PPC_BIT(5) +#define I2C_STAT_DATA_REQ PPC_BIT(6) +#define I2C_STAT_CMD_COMP PPC_BIT(7) +#define I2C_STAT_STOP_ERR PPC_BIT(8) +#define I2C_STAT_UPPER_THRS PPC_BITMASK(9, 15) +#define I2C_STAT_ANY_I2C_INTR PPC_BIT(16) +#define I2C_STAT_PORT_HISTORY_BUSY PPC_BIT(19) +#define I2C_STAT_SCL_INPUT_LEVEL PPC_BIT(20) +#define I2C_STAT_SDA_INPUT_LEVEL PPC_BIT(21) +#define I2C_STAT_PORT_BUSY PPC_BIT(22) +#define I2C_STAT_INTERFACE_BUSY PPC_BIT(23) +#define I2C_STAT_FIFO_ENTRY_COUNT PPC_BITMASK(24, 31) + +#define I2C_STAT_ANY_ERR (I2C_STAT_INVALID_CMD | I2C_STAT_LBUS_PARITY_ERR | \ + I2C_STAT_BKEND_OVERRUN_ERR | \ + I2C_STAT_BKEND_ACCESS_ERR | I2C_STAT_ARBT_LOST_ERR | \ + I2C_STAT_NACK_RCVD_ERR | I2C_STAT_STOP_ERR) + + +#define I2C_INTR_ACTIVE \ + ((I2C_STAT_ANY_ERR >> 16) | I2C_INTR_CMD_COMP | I2C_INTR_DATA_REQ) + +/* Pseudo-status used for timeouts */ +#define I2C_STAT_PSEUDO_TIMEOUT PPC_BIT(63) + +/* I2C extended status register */ +#define I2C_EXTD_STAT_REG 0xc +#define I2C_EXTD_STAT_FIFO_SIZE PPC_BITMASK(0, 7) +#define I2C_EXTD_STAT_MSM_CURSTATE PPC_BITMASK(11, 15) +#define I2C_EXTD_STAT_SCL_IN_SYNC PPC_BIT(16) +#define I2C_EXTD_STAT_SDA_IN_SYNC PPC_BIT(17) +#define I2C_EXTD_STAT_S_SCL PPC_BIT(18) +#define I2C_EXTD_STAT_S_SDA PPC_BIT(19) +#define I2C_EXTD_STAT_M_SCL PPC_BIT(20) +#define I2C_EXTD_STAT_M_SDA PPC_BIT(21) +#define I2C_EXTD_STAT_HIGH_WATER PPC_BIT(22) +#define I2C_EXTD_STAT_LOW_WATER PPC_BIT(23) +#define I2C_EXTD_STAT_I2C_BUSY PPC_BIT(24) +#define I2C_EXTD_STAT_SELF_BUSY PPC_BIT(25) +#define I2C_EXTD_STAT_I2C_VERSION PPC_BITMASK(27, 31) + +/* I2C residual front end/back end length */ +#define I2C_RESIDUAL_LEN_REG 0xd +#define I2C_RESIDUAL_FRONT_END PPC_BITMASK(0, 15) +#define I2C_RESIDUAL_BACK_END PPC_BITMASK(16, 31) + +/* Port busy register */ +#define I2C_PORT_BUSY_REG 0xe +#define I2C_SET_S_SCL_REG 0xd +#define I2C_RESET_S_SCL_REG 0xf +#define I2C_SET_S_SDA_REG 0x10 +#define I2C_RESET_S_SDA_REG 0x11 + +#define PNV_I2C_FIFO_SIZE 8 + +static I2CBus *pnv_i2c_get_bus(PnvI2C *i2c) +{ + uint8_t port = GETFIELD(I2C_MODE_PORT_NUM, i2c->regs[I2C_MODE_REG]); + + if (port >= i2c->num_busses) { + qemu_log_mask(LOG_GUEST_ERROR, "I2C: invalid bus number %d/%d\n", port, + i2c->num_busses); + return NULL; + } + return i2c->busses[port]; +} + +static void pnv_i2c_update_irq(PnvI2C *i2c) +{ + I2CBus *bus = pnv_i2c_get_bus(i2c); + bool recv = !!(i2c->regs[I2C_CMD_REG] & I2C_CMD_READ_NOT_WRITE); + uint16_t front_end = GETFIELD(I2C_RESIDUAL_FRONT_END, + i2c->regs[I2C_RESIDUAL_LEN_REG]); + uint16_t back_end = GETFIELD(I2C_RESIDUAL_BACK_END, + i2c->regs[I2C_RESIDUAL_LEN_REG]); + uint8_t fifo_count = GETFIELD(I2C_STAT_FIFO_ENTRY_COUNT, + i2c->regs[I2C_STAT_REG]); + uint8_t fifo_free = PNV_I2C_FIFO_SIZE - fifo_count; + + if (!bus) { + qemu_log_mask(LOG_GUEST_ERROR, "I2C: invalid port\n"); + return; + } + + if (i2c_bus_busy(bus)) { + i2c->regs[I2C_STAT_REG] &= ~I2C_STAT_DATA_REQ; + + if (recv) { + if (fifo_count >= + GETFIELD(I2C_WATERMARK_HIGH, i2c->regs[I2C_WATERMARK_REG])) { + i2c->regs[I2C_EXTD_STAT_REG] |= I2C_EXTD_STAT_HIGH_WATER; + } else { + i2c->regs[I2C_EXTD_STAT_REG] &= ~I2C_EXTD_STAT_HIGH_WATER; + } + + if (((i2c->regs[I2C_EXTD_STAT_REG] & I2C_EXTD_STAT_HIGH_WATER) && + fifo_count != 0) || front_end == 0) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_DATA_REQ; + } + } else { + if (fifo_count <= + GETFIELD(I2C_WATERMARK_LOW, i2c->regs[I2C_WATERMARK_REG])) { + i2c->regs[I2C_EXTD_STAT_REG] |= I2C_EXTD_STAT_LOW_WATER; + } else { + i2c->regs[I2C_EXTD_STAT_REG] &= ~I2C_EXTD_STAT_LOW_WATER; + } + + if (back_end > 0 && + (fifo_free >= back_end || + (i2c->regs[I2C_EXTD_STAT_REG] & I2C_EXTD_STAT_LOW_WATER))) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_DATA_REQ; + } + } + + if (back_end == 0 && front_end == 0) { + i2c->regs[I2C_STAT_REG] &= ~I2C_STAT_DATA_REQ; + i2c->regs[I2C_STAT_REG] |= I2C_STAT_CMD_COMP; + + if (i2c->regs[I2C_CMD_REG] & I2C_CMD_WITH_STOP) { + i2c_end_transfer(bus); + i2c->regs[I2C_EXTD_STAT_REG] &= + ~(I2C_EXTD_STAT_I2C_BUSY | I2C_EXTD_STAT_SELF_BUSY); + } + } else { + i2c->regs[I2C_STAT_REG] &= ~I2C_STAT_CMD_COMP; + } + } + + /* + * Status and interrupt registers have nearly the same layout. + */ + i2c->regs[I2C_INTR_RAW_COND_REG] = i2c->regs[I2C_STAT_REG] >> 16; + i2c->regs[I2C_INTR_COND_REG] = + i2c->regs[I2C_INTR_RAW_COND_REG] & i2c->regs[I2C_INTR_MASK_REG]; + + qemu_set_irq(i2c->psi_irq, i2c->regs[I2C_INTR_COND_REG] != 0); +} + +static void pnv_i2c_fifo_update_count(PnvI2C *i2c) +{ + uint64_t stat = i2c->regs[I2C_STAT_REG]; + + i2c->regs[I2C_STAT_REG] = SETFIELD(I2C_STAT_FIFO_ENTRY_COUNT, stat, + fifo8_num_used(&i2c->fifo)); +} + +static void pnv_i2c_frontend_update(PnvI2C *i2c) +{ + uint64_t residual_end = i2c->regs[I2C_RESIDUAL_LEN_REG]; + uint16_t front_end = GETFIELD(I2C_RESIDUAL_FRONT_END, residual_end); + + i2c->regs[I2C_RESIDUAL_LEN_REG] = + SETFIELD(I2C_RESIDUAL_FRONT_END, residual_end, front_end - 1); +} + +static void pnv_i2c_fifo_flush(PnvI2C *i2c) +{ + I2CBus *bus = pnv_i2c_get_bus(i2c); + uint8_t data; + int ret; + + if (!bus) { + qemu_log_mask(LOG_GUEST_ERROR, "I2C: invalid port\n"); + return; + } + if (!i2c_bus_busy(bus)) { + return; + } + + if (i2c->regs[I2C_CMD_REG] & I2C_CMD_READ_NOT_WRITE) { + if (fifo8_is_full(&i2c->fifo)) { + return; + } + + data = i2c_recv(bus); + fifo8_push(&i2c->fifo, data); + } else { + if (fifo8_is_empty(&i2c->fifo)) { + return; + } + + data = fifo8_pop(&i2c->fifo); + ret = i2c_send(bus, data); + if (ret) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_NACK_RCVD_ERR; + i2c_end_transfer(bus); + } + } + + pnv_i2c_fifo_update_count(i2c); + pnv_i2c_frontend_update(i2c); +} + +static void pnv_i2c_handle_cmd(PnvI2C *i2c, uint64_t val) +{ + I2CBus *bus = pnv_i2c_get_bus(i2c); + uint8_t addr = GETFIELD(I2C_CMD_DEV_ADDR, val); + int recv = !!(val & I2C_CMD_READ_NOT_WRITE); + uint32_t len_bytes = GETFIELD(I2C_CMD_LEN_BYTES, val); + + if (!(val & I2C_CMD_WITH_START) && !(val & I2C_CMD_WITH_ADDR) && + !(val & I2C_CMD_WITH_STOP) && !len_bytes) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_INVALID_CMD; + qemu_log_mask(LOG_GUEST_ERROR, "I2C: invalid command 0x%"PRIx64"\n", + val); + return; + } + + if (!(i2c->regs[I2C_STAT_REG] & I2C_STAT_CMD_COMP)) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_INVALID_CMD; + qemu_log_mask(LOG_GUEST_ERROR, "I2C: command in progress\n"); + return; + } + + if (!bus) { + qemu_log_mask(LOG_GUEST_ERROR, "I2C: invalid port\n"); + return; + } + + i2c->regs[I2C_RESIDUAL_LEN_REG] = + SETFIELD(I2C_RESIDUAL_FRONT_END, 0ull, len_bytes) | + SETFIELD(I2C_RESIDUAL_BACK_END, 0ull, len_bytes); + + if (val & I2C_CMD_WITH_START) { + if (i2c_start_transfer(bus, addr, recv)) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_NACK_RCVD_ERR; + } else { + i2c->regs[I2C_EXTD_STAT_REG] |= + (I2C_EXTD_STAT_I2C_BUSY | I2C_EXTD_STAT_SELF_BUSY); + pnv_i2c_fifo_flush(i2c); + } + } +} + +static void pnv_i2c_backend_update(PnvI2C *i2c) +{ + uint64_t residual_end = i2c->regs[I2C_RESIDUAL_LEN_REG]; + uint16_t back_end = GETFIELD(I2C_RESIDUAL_BACK_END, residual_end); + + if (!back_end) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_BKEND_ACCESS_ERR; + return; + } + + i2c->regs[I2C_RESIDUAL_LEN_REG] = + SETFIELD(I2C_RESIDUAL_BACK_END, residual_end, back_end - 1); +} + +static void pnv_i2c_fifo_in(PnvI2C *i2c) +{ + uint8_t data = GETFIELD(I2C_FIFO, i2c->regs[I2C_FIFO_REG]); + I2CBus *bus = pnv_i2c_get_bus(i2c); + + if (!bus) { + qemu_log_mask(LOG_GUEST_ERROR, "I2C: invalid port\n"); + return; + } + + if (!i2c_bus_busy(bus)) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_INVALID_CMD; + qemu_log_mask(LOG_GUEST_ERROR, "I2C: no command in progress\n"); + return; + } + + if (i2c->regs[I2C_CMD_REG] & I2C_CMD_READ_NOT_WRITE) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_INVALID_CMD; + qemu_log_mask(LOG_GUEST_ERROR, "I2C: read command in progress\n"); + return; + } + + if (fifo8_is_full(&i2c->fifo)) { + if (!(i2c->regs[I2C_MODE_REG] & I2C_MODE_PACING_ALLOW)) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_BKEND_OVERRUN_ERR; + } + return; + } + + fifo8_push(&i2c->fifo, data); + pnv_i2c_fifo_update_count(i2c); + pnv_i2c_backend_update(i2c); + pnv_i2c_fifo_flush(i2c); +} + +static void pnv_i2c_fifo_out(PnvI2C *i2c) +{ + uint8_t data; + I2CBus *bus = pnv_i2c_get_bus(i2c); + + if (!bus) { + qemu_log_mask(LOG_GUEST_ERROR, "I2C: invalid port\n"); + return; + } + + if (!i2c_bus_busy(bus)) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_INVALID_CMD; + qemu_log_mask(LOG_GUEST_ERROR, "I2C: no command in progress\n"); + return; + } + + if (!(i2c->regs[I2C_CMD_REG] & I2C_CMD_READ_NOT_WRITE)) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_INVALID_CMD; + qemu_log_mask(LOG_GUEST_ERROR, "I2C: write command in progress\n"); + return; + } + + if (fifo8_is_empty(&i2c->fifo)) { + if (!(i2c->regs[I2C_MODE_REG] & I2C_MODE_PACING_ALLOW)) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_BKEND_OVERRUN_ERR; + } + return; + } + + data = fifo8_pop(&i2c->fifo); + + i2c->regs[I2C_FIFO_REG] = SETFIELD(I2C_FIFO, 0ull, data); + pnv_i2c_fifo_update_count(i2c); + pnv_i2c_backend_update(i2c); +} + +static uint64_t pnv_i2c_xscom_read(void *opaque, hwaddr addr, + unsigned size) +{ + PnvI2C *i2c = PNV_I2C(opaque); + uint32_t offset = addr >> 3; + uint64_t val = -1; + int i; + + switch (offset) { + case I2C_STAT_REG: + val = i2c->regs[offset]; + break; + + case I2C_FIFO_REG: + pnv_i2c_fifo_out(i2c); + val = i2c->regs[offset]; + break; + + case I2C_PORT_BUSY_REG: /* compute busy bit for each port */ + val = 0; + for (i = 0; i < i2c->num_busses; i++) { + val |= i2c_bus_busy(i2c->busses[i]) << i; + } + break; + + case I2C_CMD_REG: + case I2C_MODE_REG: + case I2C_WATERMARK_REG: + case I2C_INTR_MASK_REG: + case I2C_INTR_RAW_COND_REG: + case I2C_INTR_COND_REG: + case I2C_EXTD_STAT_REG: + case I2C_RESIDUAL_LEN_REG: + val = i2c->regs[offset]; + break; + default: + i2c->regs[I2C_STAT_REG] |= I2C_STAT_INVALID_CMD; + qemu_log_mask(LOG_GUEST_ERROR, "I2C: read at register: 0x%" + HWADDR_PRIx "\n", addr >> 3); + } + + pnv_i2c_update_irq(i2c); + + return val; +} + +static void pnv_i2c_xscom_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvI2C *i2c = PNV_I2C(opaque); + uint32_t offset = addr >> 3; + + switch (offset) { + case I2C_MODE_REG: + { + i2c->regs[offset] = val; + I2CBus *bus = pnv_i2c_get_bus(i2c); + if (!bus) { + qemu_log_mask(LOG_GUEST_ERROR, "I2C: invalid port\n"); + return; + } + if (i2c_bus_busy(bus)) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_INVALID_CMD; + qemu_log_mask(LOG_GUEST_ERROR, "I2C: command in progress\n"); + } + } + break; + + case I2C_CMD_REG: + i2c->regs[offset] = val; + pnv_i2c_handle_cmd(i2c, val); + break; + + case I2C_FIFO_REG: + i2c->regs[offset] = val; + pnv_i2c_fifo_in(i2c); + break; + + case I2C_WATERMARK_REG: + i2c->regs[offset] = val; + break; + + case I2C_RESET_I2C_REG: + i2c->regs[I2C_MODE_REG] = 0; + i2c->regs[I2C_CMD_REG] = 0; + i2c->regs[I2C_WATERMARK_REG] = 0; + i2c->regs[I2C_INTR_MASK_REG] = 0; + i2c->regs[I2C_INTR_COND_REG] = 0; + i2c->regs[I2C_INTR_RAW_COND_REG] = 0; + i2c->regs[I2C_STAT_REG] = 0; + i2c->regs[I2C_RESIDUAL_LEN_REG] = 0; + i2c->regs[I2C_EXTD_STAT_REG] &= + (I2C_EXTD_STAT_FIFO_SIZE | I2C_EXTD_STAT_I2C_VERSION); + break; + + case I2C_RESET_ERRORS: + i2c->regs[I2C_STAT_REG] &= ~I2C_STAT_ANY_ERR; + i2c->regs[I2C_RESIDUAL_LEN_REG] = 0; + i2c->regs[I2C_EXTD_STAT_REG] &= + (I2C_EXTD_STAT_FIFO_SIZE | I2C_EXTD_STAT_I2C_VERSION); + fifo8_reset(&i2c->fifo); + break; + + case I2C_INTR_MASK_REG: + i2c->regs[offset] = val; + break; + + case I2C_INTR_MASK_OR_REG: + i2c->regs[I2C_INTR_MASK_REG] |= val; + break; + + case I2C_INTR_MASK_AND_REG: + i2c->regs[I2C_INTR_MASK_REG] &= val; + break; + + case I2C_PORT_BUSY_REG: + case I2C_SET_S_SCL_REG: + case I2C_RESET_S_SCL_REG: + case I2C_SET_S_SDA_REG: + case I2C_RESET_S_SDA_REG: + i2c->regs[offset] = val; + break; + default: + i2c->regs[I2C_STAT_REG] |= I2C_STAT_INVALID_CMD; + qemu_log_mask(LOG_GUEST_ERROR, "I2C: write at register: 0x%" + HWADDR_PRIx " val=0x%"PRIx64"\n", addr >> 3, val); + } + + pnv_i2c_update_irq(i2c); +} + +static const MemoryRegionOps pnv_i2c_xscom_ops = { + .read = pnv_i2c_xscom_read, + .write = pnv_i2c_xscom_write, + .valid.min_access_size = 8, + .valid.max_access_size = 8, + .impl.min_access_size = 8, + .impl.max_access_size = 8, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static int pnv_i2c_bus_dt_xscom(PnvI2C *i2c, void *fdt, + int offset, int index) +{ + int i2c_bus_offset; + const char i2c_compat[] = + "ibm,opal-i2c\0ibm,power8-i2c-port\0ibm,power9-i2c-port"; + g_autofree char *i2c_port_name = NULL; + g_autofree char *name = g_strdup_printf("i2c-bus@%x", index); + + i2c_bus_offset = fdt_add_subnode(fdt, offset, name); + _FDT(i2c_bus_offset); + + _FDT((fdt_setprop_cell(fdt, i2c_bus_offset, "reg", index))); + _FDT((fdt_setprop_cell(fdt, i2c_bus_offset, "#address-cells", 1))); + _FDT((fdt_setprop_cell(fdt, i2c_bus_offset, "#size-cells", 0))); + _FDT(fdt_setprop(fdt, i2c_bus_offset, "compatible", i2c_compat, + sizeof(i2c_compat))); + _FDT((fdt_setprop_cell(fdt, i2c_bus_offset, "bus-frequency", 400000))); + + i2c_port_name = g_strdup_printf("p8_%08x_e%dp%d", i2c->chip->chip_id, + i2c->engine, index); + _FDT(fdt_setprop_string(fdt, i2c_bus_offset, "ibm,port-name", + i2c_port_name)); + return 0; +} + +#define XSCOM_BUS_FREQUENCY 466500000 +#define I2C_CLOCK_FREQUENCY (XSCOM_BUS_FREQUENCY / 4) + +static int pnv_i2c_dt_xscom(PnvXScomInterface *dev, void *fdt, + int offset) +{ + PnvI2C *i2c = PNV_I2C(dev); + int i2c_offset; + const char i2c_compat[] = "ibm,power8-i2cm\0ibm,power9-i2cm"; + uint32_t i2c_pcba = PNV9_XSCOM_I2CM_BASE + + i2c->engine * PNV9_XSCOM_I2CM_SIZE; + uint32_t reg[2] = { + cpu_to_be32(i2c_pcba), + cpu_to_be32(PNV9_XSCOM_I2CM_SIZE) + }; + int i; + g_autofree char *name = g_strdup_printf("i2cm@%x", i2c_pcba); + + i2c_offset = fdt_add_subnode(fdt, offset, name); + _FDT(i2c_offset); + + _FDT(fdt_setprop(fdt, i2c_offset, "reg", reg, sizeof(reg))); + + _FDT((fdt_setprop_cell(fdt, i2c_offset, "#address-cells", 1))); + _FDT((fdt_setprop_cell(fdt, i2c_offset, "#size-cells", 0))); + _FDT(fdt_setprop(fdt, i2c_offset, "compatible", i2c_compat, + sizeof(i2c_compat))); + _FDT((fdt_setprop_cell(fdt, i2c_offset, "chip-engine#", i2c->engine))); + _FDT((fdt_setprop_cell(fdt, i2c_offset, "clock-frequency", + I2C_CLOCK_FREQUENCY))); + + for (i = 0; i < i2c->num_busses; i++) { + pnv_i2c_bus_dt_xscom(i2c, fdt, i2c_offset, i); + } + return 0; +} + +static void pnv_i2c_reset(void *dev) +{ + PnvI2C *i2c = PNV_I2C(dev); + + memset(i2c->regs, 0, sizeof(i2c->regs)); + + i2c->regs[I2C_STAT_REG] = I2C_STAT_CMD_COMP; + i2c->regs[I2C_EXTD_STAT_REG] = + SETFIELD(I2C_EXTD_STAT_FIFO_SIZE, 0ull, PNV_I2C_FIFO_SIZE) | + SETFIELD(I2C_EXTD_STAT_I2C_VERSION, 0ull, 23); /* last version */ + + fifo8_reset(&i2c->fifo); +} + +static void pnv_i2c_realize(DeviceState *dev, Error **errp) +{ + PnvI2C *i2c = PNV_I2C(dev); + int i; + + assert(i2c->chip); + + pnv_xscom_region_init(&i2c->xscom_regs, OBJECT(i2c), &pnv_i2c_xscom_ops, + i2c, "xscom-i2c", PNV9_XSCOM_I2CM_SIZE); + + i2c->busses = g_new(I2CBus *, i2c->num_busses); + for (i = 0; i < i2c->num_busses; i++) { + char name[32]; + + snprintf(name, sizeof(name), TYPE_PNV_I2C ".%d", i); + i2c->busses[i] = i2c_init_bus(dev, name); + } + + fifo8_create(&i2c->fifo, PNV_I2C_FIFO_SIZE); + + qemu_register_reset(pnv_i2c_reset, dev); + + qdev_init_gpio_out(DEVICE(dev), &i2c->psi_irq, 1); +} + +static Property pnv_i2c_properties[] = { + DEFINE_PROP_LINK("chip", PnvI2C, chip, TYPE_PNV_CHIP, PnvChip *), + DEFINE_PROP_UINT32("engine", PnvI2C, engine, 1), + DEFINE_PROP_UINT32("num-busses", PnvI2C, num_busses, 1), + DEFINE_PROP_END_OF_LIST(), +}; + +static void pnv_i2c_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PnvXScomInterfaceClass *xscomc = PNV_XSCOM_INTERFACE_CLASS(klass); + + xscomc->dt_xscom = pnv_i2c_dt_xscom; + + dc->desc = "PowerNV I2C"; + dc->realize = pnv_i2c_realize; + device_class_set_props(dc, pnv_i2c_properties); +} + +static const TypeInfo pnv_i2c_info = { + .name = TYPE_PNV_I2C, + .parent = TYPE_DEVICE, + .instance_size = sizeof(PnvI2C), + .class_init = pnv_i2c_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_PNV_XSCOM_INTERFACE }, + { } + } +}; + +static void pnv_i2c_register_types(void) +{ + type_register_static(&pnv_i2c_info); +} + +type_init(pnv_i2c_register_types); diff --git a/include/hw/ppc/pnv_i2c.h b/include/hw/ppc/pnv_i2c.h new file mode 100644 index 0000000000..1a37730f1e --- /dev/null +++ b/include/hw/ppc/pnv_i2c.h @@ -0,0 +1,38 @@ +/* + * QEMU PowerPC PowerNV Processor I2C model + * + * Copyright (c) 2019-2023, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PPC_PNV_I2C_H +#define PPC_PNV_I2C_H + +#include "hw/ppc/pnv.h" +#include "hw/i2c/i2c.h" +#include "qemu/fifo8.h" + +#define TYPE_PNV_I2C "pnv-i2c" +#define PNV_I2C(obj) OBJECT_CHECK(PnvI2C, (obj), TYPE_PNV_I2C) + +#define PNV_I2C_REGS 0x20 + +typedef struct PnvI2C { + DeviceState parent; + + struct PnvChip *chip; + + qemu_irq psi_irq; + + uint64_t regs[PNV_I2C_REGS]; + uint32_t engine; + uint32_t num_busses; + I2CBus **busses; + + MemoryRegion xscom_regs; + + Fifo8 fifo; +} PnvI2C; + +#endif /* PPC_PNV_I2C_H */ diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h index 35b19610f7..056392ab60 100644 --- a/include/hw/ppc/pnv_xscom.h +++ b/include/hw/ppc/pnv_xscom.h @@ -90,6 +90,9 @@ struct PnvXScomInterfaceClass { ((uint64_t)(((core) & 0x1C) + 0x40) << 22) #define PNV9_XSCOM_EQ_SIZE 0x100000 +#define PNV9_XSCOM_I2CM_BASE 0xa0000 +#define PNV9_XSCOM_I2CM_SIZE 0x1000 + #define PNV9_XSCOM_OCC_BASE PNV_XSCOM_OCC_BASE #define PNV9_XSCOM_OCC_SIZE 0x8000 From 5f0661215454959e98f69e7d3933e793d884282d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 16 Oct 2023 17:20:13 -0500 Subject: [PATCH 876/974] ppc/pnv: Connect I2C controller model to powernv9 chip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wires up three I2C controller instances to the powernv9 chip XSCOM address space. Each controller instance is wired up to a single I2C bus of its own. No other I2C devices are connected to the buses at this time. Signed-off-by: Cédric Le Goater [milesg: Split wiring from addition of model itself] [milesg: Added new commit message] [milesg: Moved hardcoded attributes into PnvChipClass] [milesg: Removed TODO comment for I2C] Signed-off-by: Glenn Miles Acked-by: Daniel Henrique Barboza Message-ID: <20231016222013.3739530-3-milesg@linux.vnet.ibm.com> Signed-off-by: Daniel Henrique Barboza --- hw/ppc/pnv.c | 28 ++++++++++++++++++++++++++++ include/hw/ppc/pnv_chip.h | 8 ++++++++ 2 files changed, 36 insertions(+) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index c0e34fffbc..bb4d00e266 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -1432,6 +1432,10 @@ static void pnv_chip_power9_instance_init(Object *obj) object_initialize_child(obj, "pec[*]", &chip9->pecs[i], TYPE_PNV_PHB4_PEC); } + + for (i = 0; i < pcc->i2c_num_engines; i++) { + object_initialize_child(obj, "i2c[*]", &chip9->i2c[i], TYPE_PNV_I2C); + } } static void pnv_chip_quad_realize_one(PnvChip *chip, PnvQuad *eq, @@ -1504,6 +1508,7 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) PnvChip *chip = PNV_CHIP(dev); Pnv9Psi *psi9 = &chip9->psi; Error *local_err = NULL; + int i; /* XSCOM bridge is first */ pnv_xscom_init(chip, PNV9_XSCOM_SIZE, PNV9_XSCOM_BASE(chip)); @@ -1602,6 +1607,27 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) error_propagate(errp, local_err); return; } + + /* + * I2C + */ + for (i = 0; i < pcc->i2c_num_engines; i++) { + Object *obj = OBJECT(&chip9->i2c[i]); + + object_property_set_int(obj, "engine", i + 1, &error_fatal); + object_property_set_int(obj, "num-busses", pcc->i2c_num_ports, + &error_fatal); + object_property_set_link(obj, "chip", OBJECT(chip), &error_abort); + if (!qdev_realize(DEVICE(obj), NULL, errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV9_XSCOM_I2CM_BASE + + chip9->i2c[i].engine * PNV9_XSCOM_I2CM_SIZE, + &chip9->i2c[i].xscom_regs); + qdev_connect_gpio_out(DEVICE(&chip9->i2c[i]), 0, + qdev_get_gpio_in(DEVICE(&chip9->psi), + PSIHB9_IRQ_SBE_I2C)); + } } static uint32_t pnv_chip_power9_xscom_pcba(PnvChip *chip, uint64_t addr) @@ -1629,6 +1655,8 @@ static void pnv_chip_power9_class_init(ObjectClass *klass, void *data) k->xscom_pcba = pnv_chip_power9_xscom_pcba; dc->desc = "PowerNV Chip POWER9"; k->num_pecs = PNV9_CHIP_MAX_PEC; + k->i2c_num_engines = PNV9_CHIP_MAX_I2C; + k->i2c_num_ports = PNV9_CHIP_MAX_I2C_PORTS; device_class_set_parent_realize(dc, pnv_chip_power9_realize, &k->parent_realize); diff --git a/include/hw/ppc/pnv_chip.h b/include/hw/ppc/pnv_chip.h index 53e1d921d7..90cfbad1a5 100644 --- a/include/hw/ppc/pnv_chip.h +++ b/include/hw/ppc/pnv_chip.h @@ -9,6 +9,7 @@ #include "hw/ppc/pnv_psi.h" #include "hw/ppc/pnv_sbe.h" #include "hw/ppc/pnv_xive.h" +#include "hw/ppc/pnv_i2c.h" #include "hw/sysbus.h" OBJECT_DECLARE_TYPE(PnvChip, PnvChipClass, @@ -86,6 +87,10 @@ struct Pnv9Chip { #define PNV9_CHIP_MAX_PEC 3 PnvPhb4PecState pecs[PNV9_CHIP_MAX_PEC]; + +#define PNV9_CHIP_MAX_I2C 3 +#define PNV9_CHIP_MAX_I2C_PORTS 1 + PnvI2C i2c[PNV9_CHIP_MAX_I2C]; }; /* @@ -130,6 +135,9 @@ struct PnvChipClass { uint32_t num_pecs; uint32_t num_phbs; + uint32_t i2c_num_engines; + uint32_t i2c_num_ports; + DeviceRealize parent_realize; uint32_t (*core_pir)(PnvChip *chip, uint32_t core_id); From 1ceda19c28a11cf51ca5f670c50934c66b7785bd Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Tue, 17 Oct 2023 17:14:34 -0500 Subject: [PATCH 877/974] ppc/pnv: Connect PNV I2C controller to powernv10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wires up four I2C controller instances to the powernv10 chip XSCOM address space. Each controller instance is wired up to two I2C buses of its own. No other I2C devices are connected to the buses at this time. Signed-off-by: Glenn Miles Reviewed-by: Cédric Le Goater Message-ID: <20231017221434.810363-1-milesg@linux.vnet.ibm.com> Signed-off-by: Daniel Henrique Barboza --- hw/ppc/pnv.c | 29 +++++++++++++++++++++++++++++ include/hw/ppc/pnv_chip.h | 4 ++++ include/hw/ppc/pnv_xscom.h | 3 +++ 3 files changed, 36 insertions(+) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index bb4d00e266..ae8e0b45cd 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -1684,6 +1684,10 @@ static void pnv_chip_power10_instance_init(Object *obj) object_initialize_child(obj, "pec[*]", &chip10->pecs[i], TYPE_PNV_PHB5_PEC); } + + for (i = 0; i < pcc->i2c_num_engines; i++) { + object_initialize_child(obj, "i2c[*]", &chip10->i2c[i], TYPE_PNV_I2C); + } } static void pnv_chip_power10_quad_realize(Pnv10Chip *chip10, Error **errp) @@ -1742,6 +1746,7 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp) PnvChip *chip = PNV_CHIP(dev); Pnv10Chip *chip10 = PNV10_CHIP(dev); Error *local_err = NULL; + int i; /* XSCOM bridge is first */ pnv_xscom_init(chip, PNV10_XSCOM_SIZE, PNV10_XSCOM_BASE(chip)); @@ -1847,6 +1852,28 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp) error_propagate(errp, local_err); return; } + + + /* + * I2C + */ + for (i = 0; i < pcc->i2c_num_engines; i++) { + Object *obj = OBJECT(&chip10->i2c[i]); + + object_property_set_int(obj, "engine", i + 1, &error_fatal); + object_property_set_int(obj, "num-busses", pcc->i2c_num_ports, + &error_fatal); + object_property_set_link(obj, "chip", OBJECT(chip), &error_abort); + if (!qdev_realize(DEVICE(obj), NULL, errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV10_XSCOM_I2CM_BASE + + chip10->i2c[i].engine * PNV10_XSCOM_I2CM_SIZE, + &chip10->i2c[i].xscom_regs); + qdev_connect_gpio_out(DEVICE(&chip10->i2c[i]), 0, + qdev_get_gpio_in(DEVICE(&chip10->psi), + PSIHB9_IRQ_SBE_I2C)); + } } static uint32_t pnv_chip_power10_xscom_pcba(PnvChip *chip, uint64_t addr) @@ -1874,6 +1901,8 @@ static void pnv_chip_power10_class_init(ObjectClass *klass, void *data) k->xscom_pcba = pnv_chip_power10_xscom_pcba; dc->desc = "PowerNV Chip POWER10"; k->num_pecs = PNV10_CHIP_MAX_PEC; + k->i2c_num_engines = PNV10_CHIP_MAX_I2C; + k->i2c_num_ports = PNV10_CHIP_MAX_I2C_PORTS; device_class_set_parent_realize(dc, pnv_chip_power10_realize, &k->parent_realize); diff --git a/include/hw/ppc/pnv_chip.h b/include/hw/ppc/pnv_chip.h index 90cfbad1a5..5815d96ecf 100644 --- a/include/hw/ppc/pnv_chip.h +++ b/include/hw/ppc/pnv_chip.h @@ -120,6 +120,10 @@ struct Pnv10Chip { #define PNV10_CHIP_MAX_PEC 2 PnvPhb4PecState pecs[PNV10_CHIP_MAX_PEC]; + +#define PNV10_CHIP_MAX_I2C 4 +#define PNV10_CHIP_MAX_I2C_PORTS 2 + PnvI2C i2c[PNV10_CHIP_MAX_I2C]; }; #define PNV10_PIR2FUSEDCORE(pir) (((pir) >> 3) & 0xf) diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h index 056392ab60..f5becbab41 100644 --- a/include/hw/ppc/pnv_xscom.h +++ b/include/hw/ppc/pnv_xscom.h @@ -152,6 +152,9 @@ struct PnvXScomInterfaceClass { #define PNV10_XSCOM_PSIHB_BASE 0x3011D00 #define PNV10_XSCOM_PSIHB_SIZE 0x100 +#define PNV10_XSCOM_I2CM_BASE PNV9_XSCOM_I2CM_BASE +#define PNV10_XSCOM_I2CM_SIZE PNV9_XSCOM_I2CM_SIZE + #define PNV10_XSCOM_OCC_BASE PNV9_XSCOM_OCC_BASE #define PNV10_XSCOM_OCC_SIZE PNV9_XSCOM_OCC_SIZE From 0d1dcb0bb168ee876445a7c94d753aee8d8a2e15 Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Wed, 25 Oct 2023 10:27:14 -0500 Subject: [PATCH 878/974] ppc/pnv: Fix number of I2C engines and ports for power9/10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Power9 is supposed to have 4 PIB-connected I2C engines with the following number of ports on each engine: 0: 2 1: 13 2: 2 3: 2 Power10 also has 4 engines but has the following number of ports on each engine: 0: 14 1: 14 2: 2 3: 16 Current code assumes that they all have the same (maximum) number. This can be a problem if software expects to see a certain number of ports present (Power Hypervisor seems to care). Fixed this by adding separate tables for power9 and power10 that map the I2C controller number to the number of I2C buses that should be attached for that engine. Reviewed-by: Cédric Le Goater Signed-off-by: Glenn Miles Message-ID: <20231025152714.956664-1-milesg@linux.vnet.ibm.com> Signed-off-by: Daniel Henrique Barboza --- hw/ppc/pnv.c | 12 ++++++++---- include/hw/ppc/pnv_chip.h | 6 ++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index ae8e0b45cd..9c29727337 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -1615,7 +1615,8 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) Object *obj = OBJECT(&chip9->i2c[i]); object_property_set_int(obj, "engine", i + 1, &error_fatal); - object_property_set_int(obj, "num-busses", pcc->i2c_num_ports, + object_property_set_int(obj, "num-busses", + pcc->i2c_ports_per_engine[i], &error_fatal); object_property_set_link(obj, "chip", OBJECT(chip), &error_abort); if (!qdev_realize(DEVICE(obj), NULL, errp)) { @@ -1640,6 +1641,7 @@ static void pnv_chip_power9_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvChipClass *k = PNV_CHIP_CLASS(klass); + static const int i2c_ports_per_engine[PNV9_CHIP_MAX_I2C] = {2, 13, 2, 2}; k->chip_cfam_id = 0x220d104900008000ull; /* P9 Nimbus DD2.0 */ k->cores_mask = POWER9_CORE_MASK; @@ -1656,7 +1658,7 @@ static void pnv_chip_power9_class_init(ObjectClass *klass, void *data) dc->desc = "PowerNV Chip POWER9"; k->num_pecs = PNV9_CHIP_MAX_PEC; k->i2c_num_engines = PNV9_CHIP_MAX_I2C; - k->i2c_num_ports = PNV9_CHIP_MAX_I2C_PORTS; + k->i2c_ports_per_engine = i2c_ports_per_engine; device_class_set_parent_realize(dc, pnv_chip_power9_realize, &k->parent_realize); @@ -1861,7 +1863,8 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp) Object *obj = OBJECT(&chip10->i2c[i]); object_property_set_int(obj, "engine", i + 1, &error_fatal); - object_property_set_int(obj, "num-busses", pcc->i2c_num_ports, + object_property_set_int(obj, "num-busses", + pcc->i2c_ports_per_engine[i], &error_fatal); object_property_set_link(obj, "chip", OBJECT(chip), &error_abort); if (!qdev_realize(DEVICE(obj), NULL, errp)) { @@ -1886,6 +1889,7 @@ static void pnv_chip_power10_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvChipClass *k = PNV_CHIP_CLASS(klass); + static const int i2c_ports_per_engine[PNV10_CHIP_MAX_I2C] = {14, 14, 2, 16}; k->chip_cfam_id = 0x120da04900008000ull; /* P10 DD1.0 (with NX) */ k->cores_mask = POWER10_CORE_MASK; @@ -1902,7 +1906,7 @@ static void pnv_chip_power10_class_init(ObjectClass *klass, void *data) dc->desc = "PowerNV Chip POWER10"; k->num_pecs = PNV10_CHIP_MAX_PEC; k->i2c_num_engines = PNV10_CHIP_MAX_I2C; - k->i2c_num_ports = PNV10_CHIP_MAX_I2C_PORTS; + k->i2c_ports_per_engine = i2c_ports_per_engine; device_class_set_parent_realize(dc, pnv_chip_power10_realize, &k->parent_realize); diff --git a/include/hw/ppc/pnv_chip.h b/include/hw/ppc/pnv_chip.h index 5815d96ecf..0ab5c42308 100644 --- a/include/hw/ppc/pnv_chip.h +++ b/include/hw/ppc/pnv_chip.h @@ -88,8 +88,7 @@ struct Pnv9Chip { #define PNV9_CHIP_MAX_PEC 3 PnvPhb4PecState pecs[PNV9_CHIP_MAX_PEC]; -#define PNV9_CHIP_MAX_I2C 3 -#define PNV9_CHIP_MAX_I2C_PORTS 1 +#define PNV9_CHIP_MAX_I2C 4 PnvI2C i2c[PNV9_CHIP_MAX_I2C]; }; @@ -122,7 +121,6 @@ struct Pnv10Chip { PnvPhb4PecState pecs[PNV10_CHIP_MAX_PEC]; #define PNV10_CHIP_MAX_I2C 4 -#define PNV10_CHIP_MAX_I2C_PORTS 2 PnvI2C i2c[PNV10_CHIP_MAX_I2C]; }; @@ -140,7 +138,7 @@ struct PnvChipClass { uint32_t num_phbs; uint32_t i2c_num_engines; - uint32_t i2c_num_ports; + const int *i2c_ports_per_engine; DeviceRealize parent_realize; From fcc63904b5e3622e6495b923a9e0e7d10cc78c5c Mon Sep 17 00:00:00 2001 From: Saif Abrar Date: Mon, 16 Oct 2023 12:59:48 -0500 Subject: [PATCH 879/974] hw/pci-host: Update PHB5 XSCOM registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add new XSCOM registers introduced in PHB5. Apply bit-masks within xscom-write methods. Bit-masks specified using PPC_BITMASK macro. Signed-off-by: Saif Abrar Reviewed-by: Cédric Le Goater Reviewed-by: Harsh Prateek Bora Message-ID: <20231016175948.10869-1-saif.abrar@linux.vnet.ibm.com> Signed-off-by: Daniel Henrique Barboza --- hw/pci-host/pnv_phb4.c | 46 ++++++++++++++++++----------- hw/pci-host/pnv_phb4_pec.c | 33 +++++++++++++++++---- include/hw/pci-host/pnv_phb4.h | 2 +- include/hw/pci-host/pnv_phb4_regs.h | 4 ++- 4 files changed, 60 insertions(+), 25 deletions(-) diff --git a/hw/pci-host/pnv_phb4.c b/hw/pci-host/pnv_phb4.c index 37c7afc18c..075499d36d 100644 --- a/hw/pci-host/pnv_phb4.c +++ b/hw/pci-host/pnv_phb4.c @@ -855,7 +855,7 @@ static uint64_t pnv_pec_stk_nest_xscom_read(void *opaque, hwaddr addr, PnvPHB4 *phb = PNV_PHB4(opaque); uint32_t reg = addr >> 3; - /* TODO: add list of allowed registers and error out if not */ + /* All registers are read-able */ return phb->nest_regs[reg]; } @@ -1000,7 +1000,7 @@ static void pnv_pec_stk_nest_xscom_write(void *opaque, hwaddr addr, switch (reg) { case PEC_NEST_STK_PCI_NEST_FIR: - phb->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] = val; + phb->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] = val & PPC_BITMASK(0, 27); break; case PEC_NEST_STK_PCI_NEST_FIR_CLR: phb->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] &= val; @@ -1009,7 +1009,8 @@ static void pnv_pec_stk_nest_xscom_write(void *opaque, hwaddr addr, phb->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] |= val; break; case PEC_NEST_STK_PCI_NEST_FIR_MSK: - phb->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] = val; + phb->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] = val & + PPC_BITMASK(0, 27); break; case PEC_NEST_STK_PCI_NEST_FIR_MSKC: phb->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] &= val; @@ -1019,7 +1020,7 @@ static void pnv_pec_stk_nest_xscom_write(void *opaque, hwaddr addr, break; case PEC_NEST_STK_PCI_NEST_FIR_ACT0: case PEC_NEST_STK_PCI_NEST_FIR_ACT1: - phb->nest_regs[reg] = val; + phb->nest_regs[reg] = val & PPC_BITMASK(0, 27); break; case PEC_NEST_STK_PCI_NEST_FIR_WOF: phb->nest_regs[reg] = 0; @@ -1030,7 +1031,7 @@ static void pnv_pec_stk_nest_xscom_write(void *opaque, hwaddr addr, /* Flag error ? */ break; case PEC_NEST_STK_PBCQ_MODE: - phb->nest_regs[reg] = val & 0xff00000000000000ull; + phb->nest_regs[reg] = val & PPC_BITMASK(0, 7); break; case PEC_NEST_STK_MMIO_BAR0: case PEC_NEST_STK_MMIO_BAR0_MASK: @@ -1041,28 +1042,33 @@ static void pnv_pec_stk_nest_xscom_write(void *opaque, hwaddr addr, PEC_NEST_STK_BAR_EN_MMIO1)) { phb_pec_error(pec, "Changing enabled BAR unsupported"); } - phb->nest_regs[reg] = val & 0xffffffffff000000ull; + phb->nest_regs[reg] = val & PPC_BITMASK(0, 39); break; case PEC_NEST_STK_PHB_REGS_BAR: if (phb->nest_regs[PEC_NEST_STK_BAR_EN] & PEC_NEST_STK_BAR_EN_PHB) { phb_pec_error(pec, "Changing enabled BAR unsupported"); } - phb->nest_regs[reg] = val & 0xffffffffffc00000ull; + phb->nest_regs[reg] = val & PPC_BITMASK(0, 41); break; case PEC_NEST_STK_INT_BAR: if (phb->nest_regs[PEC_NEST_STK_BAR_EN] & PEC_NEST_STK_BAR_EN_INT) { phb_pec_error(pec, "Changing enabled BAR unsupported"); } - phb->nest_regs[reg] = val & 0xfffffff000000000ull; + phb->nest_regs[reg] = val & PPC_BITMASK(0, 27); break; case PEC_NEST_STK_BAR_EN: - phb->nest_regs[reg] = val & 0xf000000000000000ull; + phb->nest_regs[reg] = val & PPC_BITMASK(0, 3); pnv_pec_phb_update_map(phb); break; case PEC_NEST_STK_DATA_FRZ_TYPE: - case PEC_NEST_STK_PBCQ_TUN_BAR: /* Not used for now */ - phb->nest_regs[reg] = val; + phb->nest_regs[reg] = val & PPC_BITMASK(0, 27); + break; + case PEC_NEST_STK_PBCQ_SPARSE_PAGE: + phb->nest_regs[reg] = val & PPC_BITMASK(3, 5); + break; + case PEC_NEST_STK_PBCQ_CACHE_INJ: + phb->nest_regs[reg] = val & PPC_BITMASK(0, 7); break; default: qemu_log_mask(LOG_UNIMP, "phb4_pec: nest_xscom_write 0x%"HWADDR_PRIx @@ -1086,7 +1092,7 @@ static uint64_t pnv_pec_stk_pci_xscom_read(void *opaque, hwaddr addr, PnvPHB4 *phb = PNV_PHB4(opaque); uint32_t reg = addr >> 3; - /* TODO: add list of allowed registers and error out if not */ + /* All registers are read-able */ return phb->pci_regs[reg]; } @@ -1095,10 +1101,9 @@ static void pnv_pec_stk_pci_xscom_write(void *opaque, hwaddr addr, { PnvPHB4 *phb = PNV_PHB4(opaque); uint32_t reg = addr >> 3; - switch (reg) { case PEC_PCI_STK_PCI_FIR: - phb->pci_regs[reg] = val; + phb->pci_regs[reg] = val & PPC_BITMASK(0, 5); break; case PEC_PCI_STK_PCI_FIR_CLR: phb->pci_regs[PEC_PCI_STK_PCI_FIR] &= val; @@ -1107,7 +1112,7 @@ static void pnv_pec_stk_pci_xscom_write(void *opaque, hwaddr addr, phb->pci_regs[PEC_PCI_STK_PCI_FIR] |= val; break; case PEC_PCI_STK_PCI_FIR_MSK: - phb->pci_regs[reg] = val; + phb->pci_regs[reg] = val & PPC_BITMASK(0, 5); break; case PEC_PCI_STK_PCI_FIR_MSKC: phb->pci_regs[PEC_PCI_STK_PCI_FIR_MSK] &= val; @@ -1117,20 +1122,25 @@ static void pnv_pec_stk_pci_xscom_write(void *opaque, hwaddr addr, break; case PEC_PCI_STK_PCI_FIR_ACT0: case PEC_PCI_STK_PCI_FIR_ACT1: - phb->pci_regs[reg] = val; + phb->pci_regs[reg] = val & PPC_BITMASK(0, 5); break; case PEC_PCI_STK_PCI_FIR_WOF: phb->pci_regs[reg] = 0; break; case PEC_PCI_STK_ETU_RESET: - phb->pci_regs[reg] = val & 0x8000000000000000ull; + phb->pci_regs[reg] = val & PPC_BIT(0); /* TODO: Implement reset */ break; case PEC_PCI_STK_PBAIB_ERR_REPORT: break; case PEC_PCI_STK_PBAIB_TX_CMD_CRED: + phb->pci_regs[reg] = val & + ((PPC_BITMASK(0, 2) | PPC_BITMASK(10, 18) + | PPC_BITMASK(26, 34) | PPC_BITMASK(41, 50) + | PPC_BITMASK(58, 63))); + break; case PEC_PCI_STK_PBAIB_TX_DAT_CRED: - phb->pci_regs[reg] = val; + phb->pci_regs[reg] = val & (PPC_BITMASK(33, 34) | PPC_BITMASK(44, 47)); break; default: qemu_log_mask(LOG_UNIMP, "phb4_pec_stk: pci_xscom_write 0x%"HWADDR_PRIx diff --git a/hw/pci-host/pnv_phb4_pec.c b/hw/pci-host/pnv_phb4_pec.c index 3b2850f7a3..ce8e228f98 100644 --- a/hw/pci-host/pnv_phb4_pec.c +++ b/hw/pci-host/pnv_phb4_pec.c @@ -34,7 +34,7 @@ static uint64_t pnv_pec_nest_xscom_read(void *opaque, hwaddr addr, PnvPhb4PecState *pec = PNV_PHB4_PEC(opaque); uint32_t reg = addr >> 3; - /* TODO: add list of allowed registers and error out if not */ + /* All registers are readable */ return pec->nest_regs[reg]; } @@ -45,18 +45,36 @@ static void pnv_pec_nest_xscom_write(void *opaque, hwaddr addr, uint32_t reg = addr >> 3; switch (reg) { - case PEC_NEST_PBCQ_HW_CONFIG: case PEC_NEST_DROP_PRIO_CTRL: + pec->nest_regs[reg] = val & PPC_BITMASK(0, 25); + break; case PEC_NEST_PBCQ_ERR_INJECT: + pec->nest_regs[reg] = val & PPC_BITMASK(0, 11); + break; case PEC_NEST_PCI_NEST_CLK_TRACE_CTL: + pec->nest_regs[reg] = val & PPC_BITMASK(0, 16); + break; case PEC_NEST_PBCQ_PMON_CTRL: + pec->nest_regs[reg] = val & PPC_BITMASK(0, 37); + break; case PEC_NEST_PBCQ_PBUS_ADDR_EXT: + pec->nest_regs[reg] = val & PPC_BITMASK(0, 6); + break; case PEC_NEST_PBCQ_PRED_VEC_TIMEOUT: - case PEC_NEST_CAPP_CTRL: + pec->nest_regs[reg] = val & PPC_BITMASK(0, 15); + break; case PEC_NEST_PBCQ_READ_STK_OVR: + pec->nest_regs[reg] = val & PPC_BITMASK(0, 48); + break; case PEC_NEST_PBCQ_WRITE_STK_OVR: case PEC_NEST_PBCQ_STORE_STK_OVR: + pec->nest_regs[reg] = val & PPC_BITMASK(0, 24); + break; case PEC_NEST_PBCQ_RETRY_BKOFF_CTRL: + pec->nest_regs[reg] = val & PPC_BITMASK(0, 41); + break; + case PEC_NEST_PBCQ_HW_CONFIG: + case PEC_NEST_CAPP_CTRL: pec->nest_regs[reg] = val; break; default: @@ -81,7 +99,7 @@ static uint64_t pnv_pec_pci_xscom_read(void *opaque, hwaddr addr, PnvPhb4PecState *pec = PNV_PHB4_PEC(opaque); uint32_t reg = addr >> 3; - /* TODO: add list of allowed registers and error out if not */ + /* All registers are readable */ return pec->pci_regs[reg]; } @@ -93,8 +111,13 @@ static void pnv_pec_pci_xscom_write(void *opaque, hwaddr addr, switch (reg) { case PEC_PCI_PBAIB_HW_CONFIG: + pec->pci_regs[reg] = val & PPC_BITMASK(0, 42); + break; + case PEC_PCI_PBAIB_HW_OVR: + pec->pci_regs[reg] = val & PPC_BITMASK(0, 15); + break; case PEC_PCI_PBAIB_READ_STK_OVR: - pec->pci_regs[reg] = val; + pec->pci_regs[reg] = val & PPC_BITMASK(0, 48); break; default: phb_pec_error(pec, "%s @0x%"HWADDR_PRIx"=%"PRIx64"\n", __func__, diff --git a/include/hw/pci-host/pnv_phb4.h b/include/hw/pci-host/pnv_phb4.h index 2d026db9a3..3212e68160 100644 --- a/include/hw/pci-host/pnv_phb4.h +++ b/include/hw/pci-host/pnv_phb4.h @@ -117,7 +117,7 @@ struct PnvPHB4 { MemoryRegion pci_regs_mr; /* Nest registers */ -#define PHB4_PEC_NEST_STK_REGS_COUNT 0x17 +#define PHB4_PEC_NEST_STK_REGS_COUNT 0x18 uint64_t nest_regs[PHB4_PEC_NEST_STK_REGS_COUNT]; MemoryRegion nest_regs_mr; diff --git a/include/hw/pci-host/pnv_phb4_regs.h b/include/hw/pci-host/pnv_phb4_regs.h index 4a0d3b28ef..bea96f4d91 100644 --- a/include/hw/pci-host/pnv_phb4_regs.h +++ b/include/hw/pci-host/pnv_phb4_regs.h @@ -77,10 +77,12 @@ #define PEC_NEST_STK_BAR_EN_PHB PPC_BIT(2) #define PEC_NEST_STK_BAR_EN_INT PPC_BIT(3) #define PEC_NEST_STK_DATA_FRZ_TYPE 0x15 -#define PEC_NEST_STK_PBCQ_TUN_BAR 0x16 +#define PEC_NEST_STK_PBCQ_SPARSE_PAGE 0x16 /* P10 */ +#define PEC_NEST_STK_PBCQ_CACHE_INJ 0x17 /* P10 */ /* XSCOM PCI global registers */ #define PEC_PCI_PBAIB_HW_CONFIG 0x00 +#define PEC_PCI_PBAIB_HW_OVR 0x01 #define PEC_PCI_PBAIB_READ_STK_OVR 0x02 /* XSCOM PCI per-stack registers */ From 5bf4ceec109289356f50f69bf277c99b045182e7 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Mon, 30 Oct 2023 17:38:34 +0100 Subject: [PATCH 880/974] ppc: qtest already exports qtest_rtas_call() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Having two functions with the same name is a bad idea. As spapr only uses the function locally, made it static. When you compile with clang, you get this compilation error: /usr/bin/ld: tests/qtest/libqos/libqos.fa.p/.._libqtest.c.o: in function `qtest_rtas_call': /scratch/qemu/clang/full/all/../../../../../mnt/code/qemu/full/tests/qtest/libqtest.c:1195: multiple definition of `qtest_rtas_call'; libqemu-ppc64-softmmu.fa.p/hw_ppc_spapr_rtas.c.o:/scratch/qemu/clang/full/all/../../../../../mnt/code/qemu/full/hw/ppc/spapr_rtas.c:536: first defined here clang-16: error: linker command failed with exit code 1 (use -v to see invocation) ninja: build stopped: subcommand failed. make: *** [Makefile:162: run-ninja] Error 1 Signed-off-by: Juan Quintela Reviewed-by: Cédric Le Goater Message-ID: <20231030163834.4638-1-quintela@redhat.com> [dhb: remove 'spapr_rtas.h' include from spapr_rtas.c] Signed-off-by: Daniel Henrique Barboza --- hw/ppc/spapr_rtas.c | 5 ++--- include/hw/ppc/spapr_rtas.h | 10 ---------- 2 files changed, 2 insertions(+), 13 deletions(-) delete mode 100644 include/hw/ppc/spapr_rtas.h diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index 26c384b261..f329693c55 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -38,7 +38,6 @@ #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_vio.h" -#include "hw/ppc/spapr_rtas.h" #include "hw/ppc/spapr_cpu_core.h" #include "hw/ppc/ppc.h" @@ -531,8 +530,8 @@ target_ulong spapr_rtas_call(PowerPCCPU *cpu, SpaprMachineState *spapr, return H_PARAMETER; } -uint64_t qtest_rtas_call(char *cmd, uint32_t nargs, uint64_t args, - uint32_t nret, uint64_t rets) +static uint64_t qtest_rtas_call(char *cmd, uint32_t nargs, uint64_t args, + uint32_t nret, uint64_t rets) { int token; diff --git a/include/hw/ppc/spapr_rtas.h b/include/hw/ppc/spapr_rtas.h deleted file mode 100644 index 383611f10f..0000000000 --- a/include/hw/ppc/spapr_rtas.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef HW_SPAPR_RTAS_H -#define HW_SPAPR_RTAS_H -/* - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -uint64_t qtest_rtas_call(char *cmd, uint32_t nargs, uint64_t args, - uint32_t nret, uint64_t rets); -#endif /* HW_SPAPR_RTAS_H */ From 01bb72afbb95003fb5562e341a592f583e27e280 Mon Sep 17 00:00:00 2001 From: Vikram Garhwal Date: Wed, 1 Nov 2023 14:07:23 -0700 Subject: [PATCH 881/974] Xen: Fix xen_set_irq() and xendevicemodel_set_irq_level() Remove '=' from 'if CONFIG_XEN_CTRL_INTERFACE_VERSION <= 41500'. Because xendevicemodel_set_irq_level() was introduced in 4.15 version. Also, update xendevicemodel_set_irq_level() to return -1 for older versions. Signed-off-by: Vikram Garhwal Reviewed-by: Stefano Stabellini --- hw/arm/xen_arm.c | 4 +++- include/hw/xen/xen_native.h | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/hw/arm/xen_arm.c b/hw/arm/xen_arm.c index f83b983ec5..a5631529d0 100644 --- a/hw/arm/xen_arm.c +++ b/hw/arm/xen_arm.c @@ -75,7 +75,9 @@ static MemoryRegion ram_lo, ram_hi; static void xen_set_irq(void *opaque, int irq, int level) { - xendevicemodel_set_irq_level(xen_dmod, xen_domid, irq, level); + if (xendevicemodel_set_irq_level(xen_dmod, xen_domid, irq, level)) { + error_report("xendevicemodel_set_irq_level failed"); + } } static void xen_create_virtio_mmio_devices(XenArmState *xam) diff --git a/include/hw/xen/xen_native.h b/include/hw/xen/xen_native.h index 5d2718261f..6f09c48823 100644 --- a/include/hw/xen/xen_native.h +++ b/include/hw/xen/xen_native.h @@ -523,12 +523,12 @@ static inline int xen_set_ioreq_server_state(domid_t dom, enable); } -#if CONFIG_XEN_CTRL_INTERFACE_VERSION <= 41500 +#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 41500 static inline int xendevicemodel_set_irq_level(xendevicemodel_handle *dmod, domid_t domid, uint32_t irq, unsigned int level) { - return 0; + return -1; } #endif From e5e653b0f11df03e18e4a433f2051f36d96bbeb5 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Mon, 6 Nov 2023 18:50:51 +0000 Subject: [PATCH 882/974] default-configs: Add TARGET_XML_FILES definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit loongarch64-linux-user has references to XML files so include them. Fixes: d32688ecdb ("default-configs: Add loongarch linux-user support") Signed-off-by: Akihiko Odaki Message-Id: <20231030054834.39145-6-akihiko.odaki@daynix.com> Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson [AJB: remove base32 from list] Signed-off-by: Alex Bennée Message-Id: <20231106185112.2755262-2-alex.bennee@linaro.org> --- configs/targets/loongarch64-linux-user.mak | 1 + 1 file changed, 1 insertion(+) diff --git a/configs/targets/loongarch64-linux-user.mak b/configs/targets/loongarch64-linux-user.mak index 7d1b964020..d878e5a113 100644 --- a/configs/targets/loongarch64-linux-user.mak +++ b/configs/targets/loongarch64-linux-user.mak @@ -1,3 +1,4 @@ # Default configuration for loongarch64-linux-user TARGET_ARCH=loongarch64 TARGET_BASE_ARCH=loongarch +TARGET_XML_FILES=gdb-xml/loongarch-base64.xml gdb-xml/loongarch-fpu.xml From 940bb5fa9ca9f71fcc0d06e9de9ac3ab7415d0f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 6 Nov 2023 18:50:52 +0000 Subject: [PATCH 883/974] gdb-xml: fix duplicate register in arm-neon.xml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Fixes: 56aebc8916 ("Add GDB XML register description support") Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Alex Bennée Message-Id: <20231106185112.2755262-3-alex.bennee@linaro.org> --- gdb-xml/arm-neon.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gdb-xml/arm-neon.xml b/gdb-xml/arm-neon.xml index 9dce0a996f..d61f6b8549 100644 --- a/gdb-xml/arm-neon.xml +++ b/gdb-xml/arm-neon.xml @@ -76,7 +76,7 @@ - + From 8ce4d441cb1e9a1c30afb762f0f3b1faac53bb6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 6 Nov 2023 18:50:53 +0000 Subject: [PATCH 884/974] target/arm: mark the 32bit alias of PAR when LPAE enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We also mark it ARM_CP_NO_GDB so we avoid duplicate PAR's in the system register XML we send to gdb. Suggested-by: Richard Henderson Signed-off-by: Alex Bennée Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson Message-Id: <20231107105145.2916124-1-alex.bennee@linaro.org> --- target/arm/helper.c | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 5dc0d20a84..6acd87f5b9 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -3722,20 +3722,6 @@ static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri, } #endif -static const ARMCPRegInfo vapa_cp_reginfo[] = { - { .name = "PAR", .cp = 15, .crn = 7, .crm = 4, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .resetvalue = 0, - .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.par_s), - offsetoflow32(CPUARMState, cp15.par_ns) }, - .writefn = par_write }, -#ifndef CONFIG_USER_ONLY - /* This underdecoding is safe because the reginfo is NO_RAW. */ - { .name = "ATS", .cp = 15, .crn = 7, .crm = 8, .opc1 = 0, .opc2 = CP_ANY, - .access = PL1_W, .accessfn = ats_access, - .writefn = ats_write, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC }, -#endif -}; - /* Return basic MPU access permission bits. */ static uint32_t simple_mpu_ap_bits(uint32_t val) { @@ -8904,6 +8890,27 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_arm_cp_regs(cpu, generic_timer_cp_reginfo); } if (arm_feature(env, ARM_FEATURE_VAPA)) { + ARMCPRegInfo vapa_cp_reginfo[] = { + { .name = "PAR", .cp = 15, .crn = 7, .crm = 4, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, .resetvalue = 0, + .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.par_s), + offsetoflow32(CPUARMState, cp15.par_ns) }, + .writefn = par_write}, +#ifndef CONFIG_USER_ONLY + /* This underdecoding is safe because the reginfo is NO_RAW. */ + { .name = "ATS", .cp = 15, .crn = 7, .crm = 8, .opc1 = 0, .opc2 = CP_ANY, + .access = PL1_W, .accessfn = ats_access, + .writefn = ats_write, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC }, +#endif + }; + + /* + * When LPAE exists this 32-bit PAR register is an alias of the + * 64-bit AArch32 PAR register defined in lpae_cp_reginfo[] + */ + if (arm_feature(env, ARM_FEATURE_LPAE)) { + vapa_cp_reginfo[0].type = ARM_CP_ALIAS | ARM_CP_NO_GDB; + } define_arm_cp_regs(cpu, vapa_cp_reginfo); } if (arm_feature(env, ARM_FEATURE_CACHE_TEST_CLEAN)) { From f654387b8138ccbc5d9e3eeb6fedd2b73fbfe4b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 6 Nov 2023 18:50:54 +0000 Subject: [PATCH 885/974] target/arm: hide all versions of DBGD[RS]AR from gdbstub MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This avoids two duplicates being presented to gdbstub. As the registers are RAZ anyway it is unlikely their value would be of use to someone using gdbstub anyway. Acked-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20231106185112.2755262-5-alex.bennee@linaro.org> --- target/arm/debug_helper.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/target/arm/debug_helper.c b/target/arm/debug_helper.c index 79a3659c0c..cbfba532f5 100644 --- a/target/arm/debug_helper.c +++ b/target/arm/debug_helper.c @@ -937,14 +937,14 @@ static const ARMCPRegInfo debug_cp_reginfo[] = { */ { .name = "DBGDRAR", .cp = 14, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL0_R, .accessfn = access_tdra, - .type = ARM_CP_CONST, .resetvalue = 0 }, + .type = ARM_CP_CONST | ARM_CP_NO_GDB, .resetvalue = 0 }, { .name = "MDRAR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 0, .access = PL1_R, .accessfn = access_tdra, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "DBGDSAR", .cp = 14, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL0_R, .accessfn = access_tdra, - .type = ARM_CP_CONST, .resetvalue = 0 }, + .type = ARM_CP_CONST | ARM_CP_NO_GDB, .resetvalue = 0 }, /* Monitor debug system control register; the 32-bit alias is DBGDSCRext. */ { .name = "MDSCR_EL1", .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 2, @@ -1065,9 +1065,11 @@ static const ARMCPRegInfo debug_cp_reginfo[] = { static const ARMCPRegInfo debug_lpae_cp_reginfo[] = { /* 64 bit access versions of the (dummy) debug registers */ { .name = "DBGDRAR", .cp = 14, .crm = 1, .opc1 = 0, - .access = PL0_R, .type = ARM_CP_CONST | ARM_CP_64BIT, .resetvalue = 0 }, + .access = PL0_R, .type = ARM_CP_CONST | ARM_CP_64BIT | ARM_CP_NO_GDB, + .resetvalue = 0 }, { .name = "DBGDSAR", .cp = 14, .crm = 2, .opc1 = 0, - .access = PL0_R, .type = ARM_CP_CONST | ARM_CP_64BIT, .resetvalue = 0 }, + .access = PL0_R, .type = ARM_CP_CONST | ARM_CP_64BIT | ARM_CP_NO_GDB, + .resetvalue = 0 }, }; static void dbgwvr_write(CPUARMState *env, const ARMCPRegInfo *ri, From acd8e83a2f81a6ac98f0ddffd2b476d6c9d8a48a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 6 Nov 2023 18:50:55 +0000 Subject: [PATCH 886/974] target/arm: hide aliased MIDR from gdbstub MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is just a constant alias register with the same value as the "other" MIDR so it serves no purpose being presented to gdbstub. Signed-off-by: Alex Bennée Reviewed-by: Peter Maydell Message-Id: <20231106185112.2755262-6-alex.bennee@linaro.org> --- target/arm/helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 6acd87f5b9..ff1970981e 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -9000,7 +9000,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) .type = ARM_CP_CONST, .resetvalue = cpu->revidr }, }; ARMCPRegInfo id_v8_midr_alias_cp_reginfo = { - .name = "MIDR", .type = ARM_CP_ALIAS | ARM_CP_CONST, + .name = "MIDR", .type = ARM_CP_ALIAS | ARM_CP_CONST | ARM_CP_NO_GDB, .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 4, .access = PL1_R, .resetvalue = cpu->midr }; From 21750c3c89a60d070aea9ccafa447d3ae9084821 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 6 Nov 2023 18:50:56 +0000 Subject: [PATCH 887/974] tests/tcg: add an explicit gdbstub register tester MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We already do a couple of "info registers" for specific tests but this is a more comprehensive multiarch test. It also has some output helpful for debugging the gdbstub by showing which XML features are advertised and what the underlying register numbers are. My initial motivation was to see if there are any duplicate register names exposed via the gdbstub while I was reviewing the proposed register interface for TCG plugins. Mismatches between the xml and remote-desc are reported for debugging but do not fail the test. We also skip the tests for the following arches for now until we can investigate and fix any issues: - s390x (fails to read v0l->v15l, not seen in remote-registers) - ppc64 (fails to read vs0h->vs31h, not seen in remote-registers) Cc: Akihiko Odaki Cc: Luis Machado Cc: Ilya Leoshkevich Cc: qemu-s390x@nongnu.org Cc: Nicholas Piggin Cc: Daniel Henrique Barboza Cc: qemu-ppc@nongnu.org Signed-off-by: Alex Bennée Message-Id: <20231106185112.2755262-7-alex.bennee@linaro.org> --- MAINTAINERS | 2 +- tests/tcg/multiarch/Makefile.target | 11 +- tests/tcg/multiarch/gdbstub/registers.py | 197 ++++++++++++++++++ .../multiarch/system/Makefile.softmmu-target | 13 +- tests/tcg/ppc64/Makefile.target | 7 + tests/tcg/s390x/Makefile.target | 4 + 6 files changed, 231 insertions(+), 3 deletions(-) create mode 100644 tests/tcg/multiarch/gdbstub/registers.py diff --git a/MAINTAINERS b/MAINTAINERS index 33bb1ba0ea..e73a3ff544 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2942,7 +2942,7 @@ F: gdbstub/* F: include/exec/gdbstub.h F: include/gdbstub/* F: gdb-xml/ -F: tests/tcg/multiarch/gdbstub/ +F: tests/tcg/multiarch/gdbstub/* F: scripts/feature_to_c.py F: scripts/probe-gdb-support.py diff --git a/tests/tcg/multiarch/Makefile.target b/tests/tcg/multiarch/Makefile.target index f3bfaf1a22..d31ba8d6ae 100644 --- a/tests/tcg/multiarch/Makefile.target +++ b/tests/tcg/multiarch/Makefile.target @@ -93,12 +93,21 @@ run-gdbstub-thread-breakpoint: testthread --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ --bin $< --test $(MULTIARCH_SRC)/gdbstub/test-thread-breakpoint.py, \ hitting a breakpoint on non-main thread) + +run-gdbstub-registers: sha512 + $(call run-test, $@, $(GDB_SCRIPT) \ + --gdb $(GDB) \ + --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ + --bin $< --test $(MULTIARCH_SRC)/gdbstub/registers.py, \ + checking register enumeration) + else run-gdbstub-%: $(call skip-test, "gdbstub test $*", "need working gdb with $(patsubst -%,,$(TARGET_NAME)) support") endif EXTRA_RUNS += run-gdbstub-sha1 run-gdbstub-qxfer-auxv-read \ - run-gdbstub-proc-mappings run-gdbstub-thread-breakpoint + run-gdbstub-proc-mappings run-gdbstub-thread-breakpoint \ + run-gdbstub-registers # ARM Compatible Semi Hosting Tests # diff --git a/tests/tcg/multiarch/gdbstub/registers.py b/tests/tcg/multiarch/gdbstub/registers.py new file mode 100644 index 0000000000..ff6076b09e --- /dev/null +++ b/tests/tcg/multiarch/gdbstub/registers.py @@ -0,0 +1,197 @@ +# Exercise the register functionality by exhaustively iterating +# through all supported registers on the system. +# +# This is launched via tests/guest-debug/run-test.py but you can also +# call it directly if using it for debugging/introspection: +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import gdb +import sys +import xml.etree.ElementTree as ET + +initial_vlen = 0 +failcount = 0 + +def report(cond, msg): + "Report success/fail of test." + if cond: + print("PASS: %s" % (msg)) + else: + print("FAIL: %s" % (msg)) + global failcount + failcount += 1 + + +def fetch_xml_regmap(): + """ + Iterate through the XML descriptions and validate. + + We check for any duplicate registers and report them. Return a + reg_map hash containing the names, regnums and initial values of + all registers. + """ + + # First check the XML descriptions we have sent. Most arches + # support XML but a few of the ancient ones don't in which case we + # need to gracefully fail. + + try: + xml = gdb.execute("maint print xml-tdesc", False, True) + except (gdb.error): + print("SKIP: target does not support XML") + return None + + total_regs = 0 + reg_map = {} + frame = gdb.selected_frame() + + tree = ET.fromstring(xml) + for f in tree.findall("feature"): + name = f.attrib["name"] + regs = f.findall("reg") + + total = len(regs) + total_regs += total + base = int(regs[0].attrib["regnum"]) + top = int(regs[-1].attrib["regnum"]) + + print(f"feature: {name} has {total} registers from {base} to {top}") + + for r in regs: + name = r.attrib["name"] + regnum = int(r.attrib["regnum"]) + try: + value = frame.read_register(name) + except ValueError: + report(False, f"failed to read reg: {name}") + + entry = { "name": name, "initial": value, "regnum": regnum } + + if name in reg_map: + report(False, f"duplicate register {entry} vs {reg_map[name]}") + continue + + reg_map[name] = entry + + # Validate we match + report(total_regs == len(reg_map.keys()), + f"counted all {total_regs} registers in XML") + + return reg_map + +def crosscheck_remote_xml(reg_map): + """ + Cross-check the list of remote-registers with the XML info. + """ + + remote = gdb.execute("maint print remote-registers", False, True) + r_regs = remote.split("\n") + + total_regs = len(reg_map.keys()) + total_r_regs = 0 + + for r in r_regs: + fields = r.split() + # Some of the registers reported here are "pseudo" registers that + # gdb invents based on actual registers so we need to filter them + # out. + if len(fields) == 8: + r_name = fields[0] + r_regnum = int(fields[6]) + + # check in the XML + try: + x_reg = reg_map[r_name] + except KeyError: + report(False, f"{r_name} not in XML description") + continue + + x_reg["seen"] = True + x_regnum = x_reg["regnum"] + if r_regnum != x_regnum: + report(False, f"{r_name} {r_regnum} == {x_regnum} (xml)") + else: + total_r_regs += 1 + + # Just print a mismatch in totals as gdb will filter out 64 bit + # registers on a 32 bit machine. Also print what is missing to + # help with debug. + if total_regs != total_r_regs: + print(f"xml-tdesc has ({total_regs}) registers") + print(f"remote-registers has ({total_r_regs}) registers") + + for x_key in reg_map.keys(): + x_reg = reg_map[x_key] + if "seen" not in x_reg: + print(f"{x_reg} wasn't seen in remote-registers") + +def complete_and_diff(reg_map): + """ + Let the program run to (almost) completion and then iterate + through all the registers we know about and report which ones have + changed. + """ + # Let the program get to the end and we can check what changed + b = gdb.Breakpoint("_exit") + if b.pending: # workaround Microblaze weirdness + b.delete() + gdb.Breakpoint("_Exit") + + gdb.execute("continue") + + frame = gdb.selected_frame() + changed = 0 + + for e in reg_map.values(): + name = e["name"] + old_val = e["initial"] + + try: + new_val = frame.read_register(name) + except: + report(False, f"failed to read {name} at end of run") + continue + + if new_val != old_val: + print(f"{name} changes from {old_val} to {new_val}") + changed += 1 + + # as long as something changed we can be confident its working + report(changed > 0, f"{changed} registers were changed") + + +def run_test(): + "Run through the tests" + + reg_map = fetch_xml_regmap() + + if reg_map is not None: + crosscheck_remote_xml(reg_map) + complete_and_diff(reg_map) + + +# +# This runs as the script it sourced (via -x, via run-test.py) +# +try: + inferior = gdb.selected_inferior() + arch = inferior.architecture() + print("ATTACHED: %s" % arch.name()) +except (gdb.error, AttributeError): + print("SKIPPING (not connected)", file=sys.stderr) + exit(0) + +if gdb.parse_and_eval('$pc') == 0: + print("SKIP: PC not set") + exit(0) + +try: + run_test() +except (gdb.error): + print ("GDB Exception: %s" % (sys.exc_info()[0])) + failcount += 1 + pass + +print("All tests complete: %d failures" % failcount) +exit(failcount) diff --git a/tests/tcg/multiarch/system/Makefile.softmmu-target b/tests/tcg/multiarch/system/Makefile.softmmu-target index dee4f58dea..32dc0f9830 100644 --- a/tests/tcg/multiarch/system/Makefile.softmmu-target +++ b/tests/tcg/multiarch/system/Makefile.softmmu-target @@ -48,9 +48,20 @@ run-gdbstub-untimely-packet: hello $(call quiet-command, \ (! grep -Fq 'Packet instead of Ack, ignoring it' untimely-packet.gdb.err), \ "GREP", file untimely-packet.gdb.err) + +run-gdbstub-registers: memory + $(call run-test, $@, $(GDB_SCRIPT) \ + --gdb $(GDB) \ + --qemu $(QEMU) \ + --output $<.registers.gdb.out \ + --qargs \ + "-monitor none -display none -chardev file$(COMMA)path=$<.out$(COMMA)id=output $(QEMU_OPTS)" \ + --bin $< --test $(MULTIARCH_SRC)/gdbstub/registers.py, \ + softmmu gdbstub support) else run-gdbstub-%: $(call skip-test, "gdbstub test $*", "need working gdb with $(patsubst -%,,$(TARGET_NAME)) support") endif -MULTIARCH_RUNS += run-gdbstub-memory run-gdbstub-interrupt run-gdbstub-untimely-packet +MULTIARCH_RUNS += run-gdbstub-memory run-gdbstub-interrupt \ + run-gdbstub-untimely-packet run-gdbstub-registers diff --git a/tests/tcg/ppc64/Makefile.target b/tests/tcg/ppc64/Makefile.target index 5721c159f2..1d08076756 100644 --- a/tests/tcg/ppc64/Makefile.target +++ b/tests/tcg/ppc64/Makefile.target @@ -38,4 +38,11 @@ PPC64_TESTS += signal_save_restore_xer PPC64_TESTS += xxspltw PPC64_TESTS += test-aes +ifneq ($(GDB),) +# Skip for now until vsx registers sorted out +run-gdbstub-registers: + $(call skip-test, $<, "BROKEN reading VSX registers") +endif + + TESTS += $(PPC64_TESTS) diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target index 0e670f3f8b..46544fecd4 100644 --- a/tests/tcg/s390x/Makefile.target +++ b/tests/tcg/s390x/Makefile.target @@ -103,6 +103,10 @@ run-gdbstub-svc: hello-s390x-asm --bin $< --test $(S390X_SRC)/gdbstub/test-svc.py, \ single-stepping svc) +# Skip for now until vx registers sorted out +run-gdbstub-registers: + $(call skip-test, $<, "BROKEN reading VX registers") + EXTRA_RUNS += run-gdbstub-signals-s390x run-gdbstub-svc endif From 119599965ecfddc9e4ec7ed21be86d80630d636c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 6 Nov 2023 18:50:57 +0000 Subject: [PATCH 888/974] tests/avocado: update the tcg_plugins test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are a number of things that are broken on the test currently so lets fix that up: - replace retired Debian kernel for tuxrun_baseline one - remove "detected repeat instructions test" since ea185a55 - log total counted instructions/memory accesses Acked-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20231106185112.2755262-8-alex.bennee@linaro.org> --- tests/avocado/tcg_plugins.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/tests/avocado/tcg_plugins.py b/tests/avocado/tcg_plugins.py index 642d2e49e3..15fd87b2c1 100644 --- a/tests/avocado/tcg_plugins.py +++ b/tests/avocado/tcg_plugins.py @@ -54,13 +54,11 @@ class PluginKernelBase(LinuxKernelTest): class PluginKernelNormal(PluginKernelBase): def _grab_aarch64_kernel(self): - kernel_url = ('http://security.debian.org/' - 'debian-security/pool/updates/main/l/linux-signed-arm64/' - 'linux-image-4.19.0-12-arm64_4.19.152-1_arm64.deb') - kernel_sha1 = '2036c2792f80ac9c4ccaae742b2e0a28385b6010' - kernel_deb = self.fetch_asset(kernel_url, asset_hash=kernel_sha1) - kernel_path = self.extract_from_deb(kernel_deb, - "/boot/vmlinuz-4.19.0-12-arm64") + kernel_url = ('https://storage.tuxboot.com/20230331/arm64/Image') + kernel_sha256 = 'ce95a7101a5fecebe0fe630deee6bd97b32ba41bc8754090e9ad8961ea8674c7' + kernel_path = self.fetch_asset(kernel_url, + asset_hash=kernel_sha256, + algorithm = "sha256") return kernel_path def test_aarch64_virt_insn(self): @@ -88,6 +86,10 @@ class PluginKernelNormal(PluginKernelBase): m = re.search(br"insns: (?P\d+)", s) if "count" not in m.groupdict(): self.fail("Failed to find instruction count") + else: + count = int(m.group("count")) + self.log.info(f"Counted: {count} instructions") + def test_aarch64_virt_insn_icount(self): """ @@ -111,9 +113,13 @@ class PluginKernelNormal(PluginKernelBase): with plugin_log as lf, \ mmap.mmap(lf.fileno(), 0, access=mmap.ACCESS_READ) as s: - m = re.search(br"detected repeat execution @ (?P0x[0-9A-Fa-f]+)", s) - if m is not None and "addr" in m.groupdict(): - self.fail("detected repeated instructions") + + m = re.search(br"insns: (?P\d+)", s) + if "count" not in m.groupdict(): + self.fail("Failed to find instruction count") + else: + count = int(m.group("count")) + self.log.info(f"Counted: {count} instructions") def test_aarch64_virt_mem_icount(self): """ @@ -145,3 +151,5 @@ class PluginKernelNormal(PluginKernelBase): callback = int(m[1]) if inline != callback: self.fail("mismatched access counts") + else: + self.log.info(f"Counted {inline} memory accesses") From 6c2313e83d4ecdc036f86f20758d00ac3b898f4d Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Mon, 6 Nov 2023 18:50:58 +0000 Subject: [PATCH 889/974] gdbstub: Add num_regs member to GDBFeature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the number of registers exposed to GDB is written as magic numbers in code. Derive the number of registers GDB actually see from XML files to replace the magic numbers in code later. Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alex Bennée Message-Id: <20231025093128.33116-2-akihiko.odaki@daynix.com> Signed-off-by: Alex Bennée Message-Id: <20231106185112.2755262-9-alex.bennee@linaro.org> --- include/exec/gdbstub.h | 1 + scripts/feature_to_c.py | 46 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h index 1a01c35f8e..a43aa34dad 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -13,6 +13,7 @@ typedef struct GDBFeature { const char *xmlname; const char *xml; + int num_regs; } GDBFeature; diff --git a/scripts/feature_to_c.py b/scripts/feature_to_c.py index bcbcb83beb..e04d6b2df7 100644 --- a/scripts/feature_to_c.py +++ b/scripts/feature_to_c.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: GPL-2.0-or-later -import os, sys +import os, sys, xml.etree.ElementTree def writeliteral(indent, bytes): sys.stdout.write(' ' * indent) @@ -39,10 +39,52 @@ for input in sys.argv[1:]: with open(input, 'rb') as file: read = file.read() + parser = xml.etree.ElementTree.XMLPullParser(['start', 'end']) + parser.feed(read) + events = parser.read_events() + event, element = next(events) + if event != 'start': + sys.stderr.write(f'unexpected event: {event}\n') + exit(1) + if element.tag != 'feature': + sys.stderr.write(f'unexpected start tag: {element.tag}\n') + exit(1) + + regnum = 0 + regnums = [] + tags = ['feature'] + for event, element in events: + if event == 'end': + if element.tag != tags[len(tags) - 1]: + sys.stderr.write(f'unexpected end tag: {element.tag}\n') + exit(1) + + tags.pop() + if element.tag == 'feature': + break + elif event == 'start': + if len(tags) < 2 and element.tag == 'reg': + if 'regnum' in element.attrib: + regnum = int(element.attrib['regnum']) + + regnums.append(regnum) + regnum += 1 + + tags.append(element.tag) + else: + raise Exception(f'unexpected event: {event}\n') + + if len(tags): + sys.stderr.write('unterminated feature tag\n') + exit(1) + + base_reg = min(regnums) + num_regs = max(regnums) - base_reg + 1 if len(regnums) else 0 + sys.stdout.write(' {\n') writeliteral(8, bytes(os.path.basename(input), 'utf-8')) sys.stdout.write(',\n') writeliteral(8, read) - sys.stdout.write('\n },\n') + sys.stdout.write(f',\n {num_regs},\n }},\n') sys.stdout.write(' { NULL }\n};\n') From 1218b68ea673076632fff99e4e46a32a6cc190a1 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Mon, 6 Nov 2023 18:50:59 +0000 Subject: [PATCH 890/974] gdbstub: Introduce gdb_find_static_feature() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function is useful to determine the number of registers exposed to GDB from the XML name. Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-Id: <20231025093128.33116-3-akihiko.odaki@daynix.com> Signed-off-by: Alex Bennée Message-Id: <20231106185112.2755262-10-alex.bennee@linaro.org> --- gdbstub/gdbstub.c | 13 +++++++++++++ include/exec/gdbstub.h | 8 ++++++++ 2 files changed, 21 insertions(+) diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c index 29540a0284..ae24c4848f 100644 --- a/gdbstub/gdbstub.c +++ b/gdbstub/gdbstub.c @@ -422,6 +422,19 @@ static const char *get_feature_xml(const char *p, const char **newp, return NULL; } +const GDBFeature *gdb_find_static_feature(const char *xmlname) +{ + const GDBFeature *feature; + + for (feature = gdb_static_features; feature->xmlname; feature++) { + if (!strcmp(feature->xmlname, xmlname)) { + return feature; + } + } + + g_assert_not_reached(); +} + static int gdb_read_register(CPUState *cpu, GByteArray *buf, int reg) { CPUClass *cc = CPU_GET_CLASS(cpu); diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h index a43aa34dad..7fe00506c7 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -44,6 +44,14 @@ void gdb_register_coprocessor(CPUState *cpu, */ int gdbserver_start(const char *port_or_device); +/** + * gdb_find_static_feature() - Find a static feature. + * @xmlname: The name of the XML. + * + * Return: The static feature. + */ +const GDBFeature *gdb_find_static_feature(const char *xmlname); + void gdb_set_stop_cpu(CPUState *cpu); /* in gdbstub-xml.c, generated by scripts/feature_to_c.py */ From e84f45243ffd9dfebd94ad025ed3bd0405a54cd3 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Mon, 6 Nov 2023 18:51:00 +0000 Subject: [PATCH 891/974] gdbstub: Introduce GDBFeatureBuilder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GDBFeatureBuilder unifies the logic to generate dynamic GDBFeature. Signed-off-by: Akihiko Odaki Reviewed-by: Richard Henderson Message-Id: <20231025093128.33116-4-akihiko.odaki@daynix.com> Signed-off-by: Alex Bennée Message-Id: <20231106185112.2755262-11-alex.bennee@linaro.org> --- gdbstub/gdbstub.c | 65 ++++++++++++++++++++++++++++++++++++++++++ include/exec/gdbstub.h | 50 ++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+) diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c index ae24c4848f..ebb912da1b 100644 --- a/gdbstub/gdbstub.c +++ b/gdbstub/gdbstub.c @@ -422,6 +422,71 @@ static const char *get_feature_xml(const char *p, const char **newp, return NULL; } +void gdb_feature_builder_init(GDBFeatureBuilder *builder, GDBFeature *feature, + const char *name, const char *xmlname, + int base_reg) +{ + char *header = g_markup_printf_escaped( + "" + "" + "", + name); + + builder->feature = feature; + builder->xml = g_ptr_array_new(); + g_ptr_array_add(builder->xml, header); + builder->base_reg = base_reg; + feature->xmlname = xmlname; + feature->num_regs = 0; +} + +void gdb_feature_builder_append_tag(const GDBFeatureBuilder *builder, + const char *format, ...) +{ + va_list ap; + va_start(ap, format); + g_ptr_array_add(builder->xml, g_markup_vprintf_escaped(format, ap)); + va_end(ap); +} + +void gdb_feature_builder_append_reg(const GDBFeatureBuilder *builder, + const char *name, + int bitsize, + int regnum, + const char *type, + const char *group) +{ + if (builder->feature->num_regs < regnum) { + builder->feature->num_regs = regnum; + } + + if (group) { + gdb_feature_builder_append_tag( + builder, + "", + name, bitsize, builder->base_reg + regnum, type, group); + } else { + gdb_feature_builder_append_tag( + builder, + "", + name, bitsize, builder->base_reg + regnum, type); + } +} + +void gdb_feature_builder_end(const GDBFeatureBuilder *builder) +{ + g_ptr_array_add(builder->xml, (void *)""); + g_ptr_array_add(builder->xml, NULL); + + builder->feature->xml = g_strjoinv(NULL, (void *)builder->xml->pdata); + + for (guint i = 0; i < builder->xml->len - 2; i++) { + g_free(g_ptr_array_index(builder->xml, i)); + } + + g_ptr_array_free(builder->xml, TRUE); +} + const GDBFeature *gdb_find_static_feature(const char *xmlname) { const GDBFeature *feature; diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h index 7fe00506c7..d8a3c56fa2 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -16,6 +16,12 @@ typedef struct GDBFeature { int num_regs; } GDBFeature; +typedef struct GDBFeatureBuilder { + GDBFeature *feature; + GPtrArray *xml; + int base_reg; +} GDBFeatureBuilder; + /* Get or set a register. Returns the size of the register. */ typedef int (*gdb_get_reg_cb)(CPUArchState *env, GByteArray *buf, int reg); @@ -44,6 +50,50 @@ void gdb_register_coprocessor(CPUState *cpu, */ int gdbserver_start(const char *port_or_device); +/** + * gdb_feature_builder_init() - Initialize GDBFeatureBuilder. + * @builder: The builder to be initialized. + * @feature: The feature to be filled. + * @name: The name of the feature. + * @xmlname: The name of the XML. + * @base_reg: The base number of the register ID. + */ +void gdb_feature_builder_init(GDBFeatureBuilder *builder, GDBFeature *feature, + const char *name, const char *xmlname, + int base_reg); + +/** + * gdb_feature_builder_append_tag() - Append a tag. + * @builder: The builder. + * @format: The format of the tag. + * @...: The values to be formatted. + */ +void G_GNUC_PRINTF(2, 3) +gdb_feature_builder_append_tag(const GDBFeatureBuilder *builder, + const char *format, ...); + +/** + * gdb_feature_builder_append_reg() - Append a register. + * @builder: The builder. + * @name: The register's name; it must be unique within a CPU. + * @bitsize: The register's size, in bits. + * @regnum: The offset of the register's number in the feature. + * @type: The type of the register. + * @group: The register group to which this register belongs; it can be NULL. + */ +void gdb_feature_builder_append_reg(const GDBFeatureBuilder *builder, + const char *name, + int bitsize, + int regnum, + const char *type, + const char *group); + +/** + * gdb_feature_builder_end() - End building GDBFeature. + * @builder: The builder. + */ +void gdb_feature_builder_end(const GDBFeatureBuilder *builder); + /** * gdb_find_static_feature() - Find a static feature. * @xmlname: The name of the XML. From b4ff21284be784b5dc0f5dc8f3be07e57aa3faf8 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Mon, 6 Nov 2023 18:51:01 +0000 Subject: [PATCH 892/974] cpu: Call plugin hooks only when ready MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The initialization and exit hooks will not affect the state of vCPU outside TCG context, but they may depend on the state of vCPU. Therefore, it's better to call plugin hooks after the vCPU state is fully initialized and before it gets uninitialized. Signed-off-by: Akihiko Odaki Reviewed-by: Alex Bennée Message-Id: <20231025093128.33116-16-akihiko.odaki@daynix.com> Signed-off-by: Alex Bennée Message-Id: <20231106185112.2755262-12-alex.bennee@linaro.org> --- cpu-target.c | 11 ----------- hw/core/cpu-common.c | 10 ++++++++++ 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/cpu-target.c b/cpu-target.c index f3e1ad8bcd..508013e23d 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -42,7 +42,6 @@ #include "hw/core/accel-cpu.h" #include "trace/trace-root.h" #include "qemu/accel.h" -#include "qemu/plugin.h" uintptr_t qemu_host_page_size; intptr_t qemu_host_page_mask; @@ -143,11 +142,6 @@ bool cpu_exec_realizefn(CPUState *cpu, Error **errp) /* Wait until cpu initialization complete before exposing cpu. */ cpu_list_add(cpu); - /* Plugin initialization must wait until cpu_index assigned. */ - if (tcg_enabled()) { - qemu_plugin_vcpu_init_hook(cpu); - } - #ifdef CONFIG_USER_ONLY assert(qdev_get_vmsd(DEVICE(cpu)) == NULL || qdev_get_vmsd(DEVICE(cpu))->unmigratable); @@ -176,11 +170,6 @@ void cpu_exec_unrealizefn(CPUState *cpu) } #endif - /* Call the plugin hook before clearing cpu->cpu_index in cpu_list_remove */ - if (tcg_enabled()) { - qemu_plugin_vcpu_exit_hook(cpu); - } - cpu_list_remove(cpu); /* * Now that the vCPU has been removed from the RCU list, we can call diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index d4112b8919..82dae51a55 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -214,6 +214,11 @@ static void cpu_common_realizefn(DeviceState *dev, Error **errp) cpu_resume(cpu); } + /* Plugin initialization must wait until the cpu is fully realized. */ + if (tcg_enabled()) { + qemu_plugin_vcpu_init_hook(cpu); + } + /* NOTE: latest generic point where the cpu is fully realized */ } @@ -221,6 +226,11 @@ static void cpu_common_unrealizefn(DeviceState *dev) { CPUState *cpu = CPU(dev); + /* Call the plugin hook before clearing the cpu is fully unrealized */ + if (tcg_enabled()) { + qemu_plugin_vcpu_exit_hook(cpu); + } + /* NOTE: latest generic point before the cpu is fully unrealized */ cpu_exec_unrealizefn(cpu); } From 09f17983b7c0f2b90bdd3aaea4d78286a832cfbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 6 Nov 2023 18:51:02 +0000 Subject: [PATCH 893/974] configure: tell meson and contrib_plugins about DLLTOOL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To cleanly handle cross-building we need to export the details of dlltool into meson's list of cross binaries and into the contrib/plugins/ make configuration. Cc: Greg Manning Reviewed-by: Paolo Bonzini Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Alex Bennée Message-Id: <20231106185112.2755262-13-alex.bennee@linaro.org> --- configure | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/configure b/configure index f1456f6123..cd6c521bd8 100755 --- a/configure +++ b/configure @@ -309,6 +309,7 @@ fi ar="${AR-${cross_prefix}ar}" as="${AS-${cross_prefix}as}" ccas="${CCAS-$cc}" +dlltool="${DLLTOOL-${cross_prefix}dlltool}" objcopy="${OBJCOPY-${cross_prefix}objcopy}" ld="${LD-${cross_prefix}ld}" ranlib="${RANLIB-${cross_prefix}ranlib}" @@ -1659,6 +1660,9 @@ echo "SRC_PATH=$source_path/contrib/plugins" >> contrib/plugins/$config_host_mak echo "PKG_CONFIG=${pkg_config}" >> contrib/plugins/$config_host_mak echo "CC=$cc $CPU_CFLAGS" >> contrib/plugins/$config_host_mak echo "CFLAGS=${CFLAGS-$default_cflags} $EXTRA_CFLAGS" >> contrib/plugins/$config_host_mak +if test "$targetos" = windows; then + echo "DLLTOOL=$dlltool" >> contrib/plugins/$config_host_mak +fi if test "$targetos" = darwin; then echo "CONFIG_DARWIN=y" >> contrib/plugins/$config_host_mak fi @@ -1764,6 +1768,7 @@ if test "$skip_meson" = no; then test -n "$cxx" && echo "cpp = [$(meson_quote $cxx $CPU_CFLAGS)]" >> $cross test -n "$objcc" && echo "objc = [$(meson_quote $objcc $CPU_CFLAGS)]" >> $cross echo "ar = [$(meson_quote $ar)]" >> $cross + echo "dlltool = [$(meson_quote $dlltool)]" >> $cross echo "nm = [$(meson_quote $nm)]" >> $cross echo "pkgconfig = [$(meson_quote $pkg_config)]" >> $cross echo "pkg-config = [$(meson_quote $pkg_config)]" >> $cross @@ -1869,6 +1874,7 @@ preserve_env CC preserve_env CFLAGS preserve_env CXX preserve_env CXXFLAGS +preserve_env DLLTOOL preserve_env LD preserve_env LDFLAGS preserve_env LD_LIBRARY_PATH From fb691b8cbabf5bde7d25a7f720d5ec7d5b1341e1 Mon Sep 17 00:00:00 2001 From: Greg Manning Date: Mon, 6 Nov 2023 18:51:03 +0000 Subject: [PATCH 894/974] plugins: add dllexport and dllimport to api funcs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In qemu-plugin.h, mark all API functions as __declspec(dllexport) when compiling the executables, and as __declspec(dllimport) when being used to compile plugins against. Signed-off-by: Greg Manning Reviewed-by: Alex Bennée Message-Id: <20231102172053.17692-2-gmanning@rapitasystems.com> Reviewed-by: Paolo Bonzini Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20231106185112.2755262-14-alex.bennee@linaro.org> --- include/qemu/qemu-plugin.h | 50 +++++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h index 50a9957279..4daab6efd2 100644 --- a/include/qemu/qemu-plugin.h +++ b/include/qemu/qemu-plugin.h @@ -22,15 +22,18 @@ * https://gcc.gnu.org/wiki/Visibility */ #if defined _WIN32 || defined __CYGWIN__ - #ifdef BUILDING_DLL - #define QEMU_PLUGIN_EXPORT __declspec(dllexport) - #else + #ifdef CONFIG_PLUGIN #define QEMU_PLUGIN_EXPORT __declspec(dllimport) + #define QEMU_PLUGIN_API __declspec(dllexport) + #else + #define QEMU_PLUGIN_EXPORT __declspec(dllexport) + #define QEMU_PLUGIN_API __declspec(dllimport) #endif #define QEMU_PLUGIN_LOCAL #else #define QEMU_PLUGIN_EXPORT __attribute__((visibility("default"))) #define QEMU_PLUGIN_LOCAL __attribute__((visibility("hidden"))) + #define QEMU_PLUGIN_API #endif /** @@ -147,6 +150,7 @@ typedef void (*qemu_plugin_vcpu_udata_cb_t)(unsigned int vcpu_index, * * Note: Calling this function from qemu_plugin_install() is a bug. */ +QEMU_PLUGIN_API void qemu_plugin_uninstall(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb); /** @@ -160,6 +164,7 @@ void qemu_plugin_uninstall(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb); * Plugins are reset asynchronously, and therefore the given plugin receives * callbacks until @cb is called. */ +QEMU_PLUGIN_API void qemu_plugin_reset(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb); /** @@ -171,6 +176,7 @@ void qemu_plugin_reset(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb); * * See also: qemu_plugin_register_vcpu_exit_cb() */ +QEMU_PLUGIN_API void qemu_plugin_register_vcpu_init_cb(qemu_plugin_id_t id, qemu_plugin_vcpu_simple_cb_t cb); @@ -183,6 +189,7 @@ void qemu_plugin_register_vcpu_init_cb(qemu_plugin_id_t id, * * See also: qemu_plugin_register_vcpu_init_cb() */ +QEMU_PLUGIN_API void qemu_plugin_register_vcpu_exit_cb(qemu_plugin_id_t id, qemu_plugin_vcpu_simple_cb_t cb); @@ -193,6 +200,7 @@ void qemu_plugin_register_vcpu_exit_cb(qemu_plugin_id_t id, * * The @cb function is called every time a vCPU idles. */ +QEMU_PLUGIN_API void qemu_plugin_register_vcpu_idle_cb(qemu_plugin_id_t id, qemu_plugin_vcpu_simple_cb_t cb); @@ -203,6 +211,7 @@ void qemu_plugin_register_vcpu_idle_cb(qemu_plugin_id_t id, * * The @cb function is called every time a vCPU resumes execution. */ +QEMU_PLUGIN_API void qemu_plugin_register_vcpu_resume_cb(qemu_plugin_id_t id, qemu_plugin_vcpu_simple_cb_t cb); @@ -253,6 +262,7 @@ typedef void (*qemu_plugin_vcpu_tb_trans_cb_t)(qemu_plugin_id_t id, * callbacks to be triggered when the block or individual instruction * executes. */ +QEMU_PLUGIN_API void qemu_plugin_register_vcpu_tb_trans_cb(qemu_plugin_id_t id, qemu_plugin_vcpu_tb_trans_cb_t cb); @@ -265,6 +275,7 @@ void qemu_plugin_register_vcpu_tb_trans_cb(qemu_plugin_id_t id, * * The @cb function is called every time a translated unit executes. */ +QEMU_PLUGIN_API void qemu_plugin_register_vcpu_tb_exec_cb(struct qemu_plugin_tb *tb, qemu_plugin_vcpu_udata_cb_t cb, enum qemu_plugin_cb_flags flags, @@ -296,6 +307,7 @@ enum qemu_plugin_op { * Note: ops are not atomic so in multi-threaded/multi-smp situations * you will get inexact results. */ +QEMU_PLUGIN_API void qemu_plugin_register_vcpu_tb_exec_inline(struct qemu_plugin_tb *tb, enum qemu_plugin_op op, void *ptr, uint64_t imm); @@ -309,6 +321,7 @@ void qemu_plugin_register_vcpu_tb_exec_inline(struct qemu_plugin_tb *tb, * * The @cb function is called every time an instruction is executed */ +QEMU_PLUGIN_API void qemu_plugin_register_vcpu_insn_exec_cb(struct qemu_plugin_insn *insn, qemu_plugin_vcpu_udata_cb_t cb, enum qemu_plugin_cb_flags flags, @@ -324,6 +337,7 @@ void qemu_plugin_register_vcpu_insn_exec_cb(struct qemu_plugin_insn *insn, * Insert an inline op to every time an instruction executes. Useful * if you just want to increment a single counter somewhere in memory. */ +QEMU_PLUGIN_API void qemu_plugin_register_vcpu_insn_exec_inline(struct qemu_plugin_insn *insn, enum qemu_plugin_op op, void *ptr, uint64_t imm); @@ -334,6 +348,7 @@ void qemu_plugin_register_vcpu_insn_exec_inline(struct qemu_plugin_insn *insn, * * Returns: number of instructions in this block */ +QEMU_PLUGIN_API size_t qemu_plugin_tb_n_insns(const struct qemu_plugin_tb *tb); /** @@ -342,6 +357,7 @@ size_t qemu_plugin_tb_n_insns(const struct qemu_plugin_tb *tb); * * Returns: virtual address of block start */ +QEMU_PLUGIN_API uint64_t qemu_plugin_tb_vaddr(const struct qemu_plugin_tb *tb); /** @@ -355,6 +371,7 @@ uint64_t qemu_plugin_tb_vaddr(const struct qemu_plugin_tb *tb); * * Returns: opaque handle to instruction */ +QEMU_PLUGIN_API struct qemu_plugin_insn * qemu_plugin_tb_get_insn(const struct qemu_plugin_tb *tb, size_t idx); @@ -368,6 +385,7 @@ qemu_plugin_tb_get_insn(const struct qemu_plugin_tb *tb, size_t idx); * Returns: pointer to a stream of bytes containing the value of this * instructions opcode. */ +QEMU_PLUGIN_API const void *qemu_plugin_insn_data(const struct qemu_plugin_insn *insn); /** @@ -376,6 +394,7 @@ const void *qemu_plugin_insn_data(const struct qemu_plugin_insn *insn); * * Returns: size of instruction in bytes */ +QEMU_PLUGIN_API size_t qemu_plugin_insn_size(const struct qemu_plugin_insn *insn); /** @@ -384,6 +403,7 @@ size_t qemu_plugin_insn_size(const struct qemu_plugin_insn *insn); * * Returns: virtual address of instruction */ +QEMU_PLUGIN_API uint64_t qemu_plugin_insn_vaddr(const struct qemu_plugin_insn *insn); /** @@ -392,6 +412,7 @@ uint64_t qemu_plugin_insn_vaddr(const struct qemu_plugin_insn *insn); * * Returns: hardware (physical) target address of instruction */ +QEMU_PLUGIN_API void *qemu_plugin_insn_haddr(const struct qemu_plugin_insn *insn); /** @@ -410,6 +431,7 @@ struct qemu_plugin_hwaddr; * * Returns: size of access in ^2 (0=byte, 1=16bit, 2=32bit etc...) */ +QEMU_PLUGIN_API unsigned int qemu_plugin_mem_size_shift(qemu_plugin_meminfo_t info); /** * qemu_plugin_mem_is_sign_extended() - was the access sign extended @@ -417,6 +439,7 @@ unsigned int qemu_plugin_mem_size_shift(qemu_plugin_meminfo_t info); * * Returns: true if it was, otherwise false */ +QEMU_PLUGIN_API bool qemu_plugin_mem_is_sign_extended(qemu_plugin_meminfo_t info); /** * qemu_plugin_mem_is_big_endian() - was the access big endian @@ -424,6 +447,7 @@ bool qemu_plugin_mem_is_sign_extended(qemu_plugin_meminfo_t info); * * Returns: true if it was, otherwise false */ +QEMU_PLUGIN_API bool qemu_plugin_mem_is_big_endian(qemu_plugin_meminfo_t info); /** * qemu_plugin_mem_is_store() - was the access a store @@ -431,6 +455,7 @@ bool qemu_plugin_mem_is_big_endian(qemu_plugin_meminfo_t info); * * Returns: true if it was, otherwise false */ +QEMU_PLUGIN_API bool qemu_plugin_mem_is_store(qemu_plugin_meminfo_t info); /** @@ -446,6 +471,7 @@ bool qemu_plugin_mem_is_store(qemu_plugin_meminfo_t info); * information about the handle should be recovered before the * callback returns. */ +QEMU_PLUGIN_API struct qemu_plugin_hwaddr *qemu_plugin_get_hwaddr(qemu_plugin_meminfo_t info, uint64_t vaddr); @@ -462,6 +488,7 @@ struct qemu_plugin_hwaddr *qemu_plugin_get_hwaddr(qemu_plugin_meminfo_t info, * Returns true if the handle's memory operation is to memory-mapped IO, or * false if it is to RAM */ +QEMU_PLUGIN_API bool qemu_plugin_hwaddr_is_io(const struct qemu_plugin_hwaddr *haddr); /** @@ -473,12 +500,14 @@ bool qemu_plugin_hwaddr_is_io(const struct qemu_plugin_hwaddr *haddr); * Note that the returned physical address may not be unique if you are dealing * with multiple address spaces. */ +QEMU_PLUGIN_API uint64_t qemu_plugin_hwaddr_phys_addr(const struct qemu_plugin_hwaddr *haddr); /* * Returns a string representing the device. The string is valid for * the lifetime of the plugin. */ +QEMU_PLUGIN_API const char *qemu_plugin_hwaddr_device_name(const struct qemu_plugin_hwaddr *h); /** @@ -513,6 +542,7 @@ typedef void (*qemu_plugin_vcpu_mem_cb_t) (unsigned int vcpu_index, * callback so the plugin is responsible for ensuring it doesn't get * confused by making appropriate use of locking if required. */ +QEMU_PLUGIN_API void qemu_plugin_register_vcpu_mem_cb(struct qemu_plugin_insn *insn, qemu_plugin_vcpu_mem_cb_t cb, enum qemu_plugin_cb_flags flags, @@ -531,6 +561,7 @@ void qemu_plugin_register_vcpu_mem_cb(struct qemu_plugin_insn *insn, * instruction. This provides for a lightweight but not thread-safe * way of counting the number of operations done. */ +QEMU_PLUGIN_API void qemu_plugin_register_vcpu_mem_inline(struct qemu_plugin_insn *insn, enum qemu_plugin_mem_rw rw, enum qemu_plugin_op op, void *ptr, @@ -544,6 +575,7 @@ typedef void uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6, uint64_t a7, uint64_t a8); +QEMU_PLUGIN_API void qemu_plugin_register_vcpu_syscall_cb(qemu_plugin_id_t id, qemu_plugin_vcpu_syscall_cb_t cb); @@ -551,6 +583,7 @@ typedef void (*qemu_plugin_vcpu_syscall_ret_cb_t)(qemu_plugin_id_t id, unsigned int vcpu_idx, int64_t num, int64_t ret); +QEMU_PLUGIN_API void qemu_plugin_register_vcpu_syscall_ret_cb(qemu_plugin_id_t id, qemu_plugin_vcpu_syscall_ret_cb_t cb); @@ -563,6 +596,7 @@ qemu_plugin_register_vcpu_syscall_ret_cb(qemu_plugin_id_t id, * Returns an allocated string containing the disassembly */ +QEMU_PLUGIN_API char *qemu_plugin_insn_disas(const struct qemu_plugin_insn *insn); /** @@ -572,6 +606,7 @@ char *qemu_plugin_insn_disas(const struct qemu_plugin_insn *insn); * Return a static string referring to the symbol. This is dependent * on the binary QEMU is running having provided a symbol table. */ +QEMU_PLUGIN_API const char *qemu_plugin_insn_symbol(const struct qemu_plugin_insn *insn); /** @@ -583,9 +618,11 @@ const char *qemu_plugin_insn_symbol(const struct qemu_plugin_insn *insn); * * See also: qemu_plugin_register_vcpu_init_cb() */ +QEMU_PLUGIN_API void qemu_plugin_vcpu_for_each(qemu_plugin_id_t id, qemu_plugin_vcpu_simple_cb_t cb); +QEMU_PLUGIN_API void qemu_plugin_register_flush_cb(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb); @@ -602,6 +639,7 @@ void qemu_plugin_register_flush_cb(qemu_plugin_id_t id, * In user-mode it is possible a few un-instrumented instructions from * child threads may run before the host kernel reaps the threads. */ +QEMU_PLUGIN_API void qemu_plugin_register_atexit_cb(qemu_plugin_id_t id, qemu_plugin_udata_cb_t cb, void *userdata); @@ -615,6 +653,7 @@ int qemu_plugin_n_max_vcpus(void); * qemu_plugin_outs() - output string via QEMU's logging system * @string: a string */ +QEMU_PLUGIN_API void qemu_plugin_outs(const char *string); /** @@ -628,6 +667,7 @@ void qemu_plugin_outs(const char *string); * returns true if the combination @name=@val parses correctly to a boolean * argument, and false otherwise */ +QEMU_PLUGIN_API bool qemu_plugin_bool_parse(const char *name, const char *val, bool *ret); /** @@ -638,6 +678,7 @@ bool qemu_plugin_bool_parse(const char *name, const char *val, bool *ret); * return NULL. The user should g_free() the string once no longer * needed. */ +QEMU_PLUGIN_API const char *qemu_plugin_path_to_binary(void); /** @@ -646,6 +687,7 @@ const char *qemu_plugin_path_to_binary(void); * Returns the nominal start address of the main text segment in * user-mode. Currently returns 0 for system emulation. */ +QEMU_PLUGIN_API uint64_t qemu_plugin_start_code(void); /** @@ -654,6 +696,7 @@ uint64_t qemu_plugin_start_code(void); * Returns the nominal end address of the main text segment in * user-mode. Currently returns 0 for system emulation. */ +QEMU_PLUGIN_API uint64_t qemu_plugin_end_code(void); /** @@ -662,6 +705,7 @@ uint64_t qemu_plugin_end_code(void); * Returns the nominal entry address of the main text segment in * user-mode. Currently returns 0 for system emulation. */ +QEMU_PLUGIN_API uint64_t qemu_plugin_entry_code(void); #endif /* QEMU_QEMU_PLUGIN_H */ From 330fe3b03f6451889bec6f5c9f7e8bb5009c80c3 Mon Sep 17 00:00:00 2001 From: Greg Manning Date: Mon, 6 Nov 2023 18:51:04 +0000 Subject: [PATCH 895/974] plugins: make test/example plugins work on windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Generate a qemu_plugin_api.lib delay import lib on windows, for windows qemu plugins to link against. Implement an example dll load fail hook to link up the API functions correctly when a plugin is loaded on windows. Update the build scripts for the test and example plugins to use these things. Signed-off-by: Greg Manning Acked-by: Alex Bennée Message-Id: <20231102172053.17692-3-gmanning@rapitasystems.com> [AJB: use find_program for dlltool, s/Windows/windows/] Cc: Paolo Bonzini Signed-off-by: Alex Bennée Message-Id: <20231106185112.2755262-15-alex.bennee@linaro.org> --- configure | 3 +++ contrib/plugins/Makefile | 20 ++++++++++++++++---- contrib/plugins/win32_linker.c | 34 ++++++++++++++++++++++++++++++++++ plugins/meson.build | 19 +++++++++++++++++++ tests/plugin/meson.build | 14 +++++++++++--- 5 files changed, 83 insertions(+), 7 deletions(-) create mode 100644 contrib/plugins/win32_linker.c diff --git a/configure b/configure index cd6c521bd8..e50ec99fe2 100755 --- a/configure +++ b/configure @@ -1666,6 +1666,9 @@ fi if test "$targetos" = darwin; then echo "CONFIG_DARWIN=y" >> contrib/plugins/$config_host_mak fi +if test "$targetos" = windows; then + echo "CONFIG_WIN32=y" >> contrib/plugins/$config_host_mak +fi # tests/tcg configuration (config_host_mak=tests/tcg/config-host.mak diff --git a/contrib/plugins/Makefile b/contrib/plugins/Makefile index 8ba78c7a32..751fa38619 100644 --- a/contrib/plugins/Makefile +++ b/contrib/plugins/Makefile @@ -22,7 +22,14 @@ NAMES += hwprofile NAMES += cache NAMES += drcov -SONAMES := $(addsuffix .so,$(addprefix lib,$(NAMES))) +ifeq ($(CONFIG_WIN32),y) +SO_SUFFIX := .dll +LDLIBS += $(shell $(PKG_CONFIG) --libs glib-2.0) +else +SO_SUFFIX := .so +endif + +SONAMES := $(addsuffix $(SO_SUFFIX),$(addprefix lib,$(NAMES))) # The main QEMU uses Glib extensively so it's perfectly fine to use it # in plugins (which many example do). @@ -35,15 +42,20 @@ all: $(SONAMES) %.o: %.c $(CC) $(CFLAGS) $(PLUGIN_CFLAGS) -c -o $@ $< -lib%.so: %.o -ifeq ($(CONFIG_DARWIN),y) +ifeq ($(CONFIG_WIN32),y) +lib%$(SO_SUFFIX): %.o win32_linker.o ../../plugins/qemu_plugin_api.lib + $(CC) -shared -o $@ $^ $(LDLIBS) +else ifeq ($(CONFIG_DARWIN),y) +lib%$(SO_SUFFIX): %.o $(CC) -bundle -Wl,-undefined,dynamic_lookup -o $@ $^ $(LDLIBS) else +lib%$(SO_SUFFIX): %.o $(CC) -shared -o $@ $^ $(LDLIBS) endif + clean: - rm -f *.o *.so *.d + rm -f *.o *$(SO_SUFFIX) *.d rm -Rf .libs .PHONY: all clean diff --git a/contrib/plugins/win32_linker.c b/contrib/plugins/win32_linker.c new file mode 100644 index 0000000000..7534b2b8bf --- /dev/null +++ b/contrib/plugins/win32_linker.c @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023, Greg Manning + * + * This hook, __pfnDliFailureHook2, is documented in the microsoft documentation here: + * https://learn.microsoft.com/en-us/cpp/build/reference/error-handling-and-notification + * It gets called when a delay-loaded DLL encounters various errors. + * We handle the specific case of a DLL looking for a "qemu.exe", + * and give it the running executable (regardless of what it is named). + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include +#include + +FARPROC WINAPI dll_failure_hook(unsigned dliNotify, PDelayLoadInfo pdli); + + +PfnDliHook __pfnDliFailureHook2 = dll_failure_hook; + +FARPROC WINAPI dll_failure_hook(unsigned dliNotify, PDelayLoadInfo pdli) { + if (dliNotify == dliFailLoadLib) { + /* If the failing request was for qemu.exe, ... */ + if (strcmp(pdli->szDll, "qemu.exe") == 0) { + /* Then pass back a pointer to the top level module. */ + HMODULE top = GetModuleHandle(NULL); + return (FARPROC) top; + } + } + /* Otherwise we can't do anything special. */ + return 0; +} + diff --git a/plugins/meson.build b/plugins/meson.build index 71ed996ed3..40d24529c0 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -14,6 +14,25 @@ if not enable_modules endif if get_option('plugins') + if targetos == '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 + win32_plugin_def = configure_file( + input: files('qemu-plugins.symbols'), + output: 'qemu_plugin_api.def', + capture: true, + command: ['sed', '-e', '0,/^/s//EXPORTS/; s/[{};]//g', '@INPUT@']) + # then use dlltool to assemble a delaylib. + win32_qemu_plugin_api_lib = configure_file( + input: win32_plugin_def, + output: 'qemu_plugin_api.lib', + command: [dlltool, '--input-def', '@INPUT@', + '--output-delaylib', '@OUTPUT@', '--dllname', 'qemu.exe'] + ) + endif specific_ss.add(files( 'loader.c', 'core.c', diff --git a/tests/plugin/meson.build b/tests/plugin/meson.build index 322cafcdf6..528bb9d86c 100644 --- a/tests/plugin/meson.build +++ b/tests/plugin/meson.build @@ -1,9 +1,17 @@ t = [] if get_option('plugins') foreach i : ['bb', 'empty', 'insn', 'mem', 'syscall'] - t += shared_module(i, files(i + '.c'), - include_directories: '../../include/qemu', - dependencies: glib) + if targetos == 'windows' + t += shared_module(i, files(i + '.c') + '../../contrib/plugins/win32_linker.c', + include_directories: '../../include/qemu', + objects: [win32_qemu_plugin_api_lib], + dependencies: glib) + + else + t += shared_module(i, files(i + '.c'), + include_directories: '../../include/qemu', + dependencies: glib) + endif endforeach endif if t.length() > 0 From d5f207ea47c19e8bb15df9e7a7c8543a1ed02095 Mon Sep 17 00:00:00 2001 From: Greg Manning Date: Mon, 6 Nov 2023 18:51:05 +0000 Subject: [PATCH 896/974] plugins: disable lockstep plugin on windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The lockstep plugin uses unix sockets and would require a different communication mechanism to work on Windows. Signed-off-by: Greg Manning Reviewed-by: Alex Bennée Message-Id: <20231102172053.17692-4-gmanning@rapitasystems.com> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Alex Bennée Message-Id: <20231106185112.2755262-16-alex.bennee@linaro.org> --- contrib/plugins/Makefile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/contrib/plugins/Makefile b/contrib/plugins/Makefile index 751fa38619..1783750cf6 100644 --- a/contrib/plugins/Makefile +++ b/contrib/plugins/Makefile @@ -17,7 +17,13 @@ NAMES += execlog NAMES += hotblocks NAMES += hotpages NAMES += howvec + +# The lockstep example communicates using unix sockets, +# and can't be easily made to work on windows. +ifneq ($(CONFIG_WIN32),y) NAMES += lockstep +endif + NAMES += hwprofile NAMES += cache NAMES += drcov From f8347d05df3709fd4b62629b2c92b9ad130831f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 6 Nov 2023 19:29:22 +0000 Subject: [PATCH 897/974] gitlab: add dlltool to Windows CI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We also --disable-plugins for the two mingw based cross builds as although they have dlltool they seem to be unhappy linking. Signed-off-by: Alex Bennée --- .gitlab-ci.d/crossbuilds.yml | 4 ++-- .gitlab-ci.d/windows.yml | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.d/crossbuilds.yml b/.gitlab-ci.d/crossbuilds.yml index 84ff2f6d2b..ac71a2abd3 100644 --- a/.gitlab-ci.d/crossbuilds.yml +++ b/.gitlab-ci.d/crossbuilds.yml @@ -165,7 +165,7 @@ cross-win32-system: job: win32-fedora-cross-container variables: IMAGE: fedora-win32-cross - EXTRA_CONFIGURE_OPTS: --enable-fdt=internal + EXTRA_CONFIGURE_OPTS: --enable-fdt=internal --disable-plugins CROSS_SKIP_TARGETS: alpha-softmmu avr-softmmu hppa-softmmu m68k-softmmu microblazeel-softmmu mips64el-softmmu nios2-softmmu artifacts: @@ -179,7 +179,7 @@ cross-win64-system: job: win64-fedora-cross-container variables: IMAGE: fedora-win64-cross - EXTRA_CONFIGURE_OPTS: --enable-fdt=internal + EXTRA_CONFIGURE_OPTS: --enable-fdt=internal --disable-plugins CROSS_SKIP_TARGETS: alpha-softmmu avr-softmmu hppa-softmmu m68k-softmmu microblazeel-softmmu nios2-softmmu or1k-softmmu rx-softmmu sh4eb-softmmu sparc64-softmmu diff --git a/.gitlab-ci.d/windows.yml b/.gitlab-ci.d/windows.yml index 12a987cd71..f7645f72b7 100644 --- a/.gitlab-ci.d/windows.yml +++ b/.gitlab-ci.d/windows.yml @@ -72,6 +72,7 @@ - .\msys64\usr\bin\bash -lc "pacman -Sy --noconfirm --needed bison diffutils flex git grep make sed + $MINGW_TARGET-binutils $MINGW_TARGET-capstone $MINGW_TARGET-ccache $MINGW_TARGET-curl From 36fa07739450a02a92244a4f0c6973c1df7a9a0c Mon Sep 17 00:00:00 2001 From: Greg Manning Date: Mon, 6 Nov 2023 18:51:06 +0000 Subject: [PATCH 898/974] plugins: allow plugins to be enabled on windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit allow plugins to be enabled in the configure script on windows. Also, add the qemu_plugin_api.lib to the installer. Signed-off-by: Greg Manning Reviewed-by: Alex Bennée Message-Id: <20231102172053.17692-5-gmanning@rapitasystems.com> Reviewed-by: Philippe Mathieu-Daudé [AJB: add check for dlltool to configure] Signed-off-by: Alex Bennée Message-Id: <20231106185112.2755262-17-alex.bennee@linaro.org> --- configure | 4 ++-- meson.build | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/configure b/configure index e50ec99fe2..abcb199aa8 100755 --- a/configure +++ b/configure @@ -1011,9 +1011,9 @@ if test "$targetos" = "bogus"; then fi # test for any invalid configuration combinations -if test "$targetos" = "windows"; then +if test "$targetos" = "windows" && ! has "$dlltool"; then if test "$plugins" = "yes"; then - error_exit "TCG plugins not currently supported on Windows platforms" + error_exit "TCG plugins requires dlltool to build on Windows platforms" fi plugins="no" fi diff --git a/meson.build b/meson.build index 4848930680..d7d841e71e 100644 --- a/meson.build +++ b/meson.build @@ -3944,6 +3944,11 @@ endforeach if get_option('plugins') install_headers('include/qemu/qemu-plugin.h') + if targetos == 'windows' + # On windows, we want to deliver the qemu_plugin_api.lib file in the qemu installer, + # so that plugin authors can compile against it. + install_data(win32_qemu_plugin_api_lib, install_dir: 'lib') + endif endif subdir('qga') From 56953a596cdc42713857ea78b51f8034598e47ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 6 Nov 2023 18:51:07 +0000 Subject: [PATCH 899/974] contrib/gitdm: Add Rivos Inc to the domain map MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Whatever they are up to a number of people for the company are contributing to QEMU so lets group them together. Reviewed-by: Palmer Dabbelt Acked-by: Palmer Dabbelt Signed-off-by: Alex Bennée Message-Id: <20231106185112.2755262-18-alex.bennee@linaro.org> --- contrib/gitdm/domain-map | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/gitdm/domain-map b/contrib/gitdm/domain-map index 3e31a06245..e676da8d47 100644 --- a/contrib/gitdm/domain-map +++ b/contrib/gitdm/domain-map @@ -38,6 +38,7 @@ proxmox.com Proxmox quicinc.com Qualcomm Innovation Center redhat.com Red Hat rev.ng rev.ng Labs +rivosinc.com Rivos Inc rt-rk.com RT-RK samsung.com Samsung siemens.com Siemens From 567fa02988c424e2cadcaef77f1ed0f2c02d8a52 Mon Sep 17 00:00:00 2001 From: luzhipeng Date: Mon, 6 Nov 2023 18:51:08 +0000 Subject: [PATCH 900/974] contrib/gitdm: add domain-map for Cestc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: luzhipeng Message-Id: <20230628072236.1925-1-luzhipeng@cestc.cn> Signed-off-by: Alex Bennée Message-Id: <20231106185112.2755262-19-alex.bennee@linaro.org> --- contrib/gitdm/domain-map | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/gitdm/domain-map b/contrib/gitdm/domain-map index e676da8d47..38945cddf0 100644 --- a/contrib/gitdm/domain-map +++ b/contrib/gitdm/domain-map @@ -12,6 +12,7 @@ amd.com AMD aspeedtech.com ASPEED Technology Inc. baidu.com Baidu bytedance.com ByteDance +cestc.cn Cestc cmss.chinamobile.com China Mobile citrix.com Citrix crudebyte.com Crudebyte From e14e0f293d07f50dd5bd732f1b1e95fbf644dce8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 6 Nov 2023 18:51:09 +0000 Subject: [PATCH 901/974] contrib/gitdm: map HiSilicon to Huawei MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HiSilicon is a wholly owned subsidiary of Huawei so map the domain to the same company to avoid splitting the contributions. Reviewed-by: Yicong Yang Signed-off-by: Alex Bennée Message-Id: <20231106185112.2755262-20-alex.bennee@linaro.org> --- contrib/gitdm/domain-map | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/gitdm/domain-map b/contrib/gitdm/domain-map index 38945cddf0..42571fc1c4 100644 --- a/contrib/gitdm/domain-map +++ b/contrib/gitdm/domain-map @@ -22,6 +22,7 @@ fb.com Facebook fujitsu.com Fujitsu google.com Google greensocs.com GreenSocs +hisilicon.com Huawei huawei.com Huawei ibm.com IBM igalia.com Igalia From 8df113d979f83ef8a705266574ebc30d2654a268 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 6 Nov 2023 18:51:10 +0000 Subject: [PATCH 902/974] contrib/gitdm: add Daynix to domain-map MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Daynix describes itself as a cloud technology company so I assume employee contributions should count as such. Reviewed-by: Akihiko Odaki Signed-off-by: Alex Bennée Message-Id: <20231106185112.2755262-21-alex.bennee@linaro.org> --- contrib/gitdm/domain-map | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/gitdm/domain-map b/contrib/gitdm/domain-map index 42571fc1c4..bf1dce03fd 100644 --- a/contrib/gitdm/domain-map +++ b/contrib/gitdm/domain-map @@ -17,6 +17,7 @@ cmss.chinamobile.com China Mobile citrix.com Citrix crudebyte.com Crudebyte chinatelecom.cn China Telecom +daynix.com Daynix eldorado.org.br Instituto de Pesquisas Eldorado fb.com Facebook fujitsu.com Fujitsu From 9163d74fc0917ee20f6134407c644479488ec0f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 6 Nov 2023 18:51:11 +0000 Subject: [PATCH 903/974] mailmap: fixup some more corrupted author fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Cc: Timothée Cocault Cc: fanwenjie Signed-off-by: Alex Bennée Message-Id: <20231106185112.2755262-22-alex.bennee@linaro.org> --- .mailmap | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.mailmap b/.mailmap index 94f19a0ac9..e12e19f691 100644 --- a/.mailmap +++ b/.mailmap @@ -30,10 +30,12 @@ malc malc # Corrupted Author fields Aaron Larson alarson@ddci.com Andreas Färber Andreas Färber +fanwenjie fanwj@mail.ustc.edu.cn Jason Wang Jason Wang Marek Dolata mkdolata@us.ibm.com Michael Ellerman michael@ozlabs.org Nick Hudson hnick@vmware.com +Timothée Cocault timothee.cocault@gmail.com # There is also a: # (no author) <(no author)@c046a42c-6fe2-441c-8c8c-71466251a162> From a475f32b075d566f3f92f94387d50e132b73bcb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 6 Nov 2023 18:51:12 +0000 Subject: [PATCH 904/974] Revert "tests/tcg/nios2: Re-enable linux-user tests" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit nios2 signal tests are broken again: retry.py -n 10 -c -- ./qemu-nios2 ./tests/tcg/nios2-linux-user/signals Results summary: 0: 8 times (80.00%), avg time 2.254 (0.00 varience/0.00 deviation) -11: 2 times (20.00%), avg time 0.253 (0.00 varience/0.00 deviation) Ran command 10 times, 8 passes This wasn't picked up by CI as we don't have a docker container that can build QEMU with the nios2 compiler. I don't have time to bisect the breakage and the target is orphaned anyway so take the easy route and revert it. This reverts commit 20e7524ff9f0cab4c9a0306014d6f3d7b467ae1e. Cc: Chris Wulff Cc: Marek Vasut Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20231106185112.2755262-23-alex.bennee@linaro.org> --- tests/tcg/nios2/Makefile.target | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 tests/tcg/nios2/Makefile.target diff --git a/tests/tcg/nios2/Makefile.target b/tests/tcg/nios2/Makefile.target new file mode 100644 index 0000000000..b38e2352b7 --- /dev/null +++ b/tests/tcg/nios2/Makefile.target @@ -0,0 +1,11 @@ +# nios2 specific test tweaks + +# Currently nios2 signal handling is broken +run-signals: signals + $(call skip-test, $<, "BROKEN") +run-plugin-signals-with-%: + $(call skip-test, $<, "BROKEN") +run-linux-test: linux-test + $(call skip-test, $<, "BROKEN") +run-plugin-linux-test-with-%: + $(call skip-test, $<, "BROKEN") From 004915a96a7a40e942ac85e6d22518cbcd283506 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 27 Oct 2023 17:53:26 +0200 Subject: [PATCH 905/974] block: Protect bs->backing with graph_lock Almost all functions that access bs->backing already take the graph lock now. Add locking to the remaining users and finally annotate the struct field itself as protected by the graph lock. Signed-off-by: Kevin Wolf Message-ID: <20231027155333.420094-18-kwolf@redhat.com> Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- block.c | 27 +++++++++++++++++---------- block/commit.c | 5 ++++- block/mirror.c | 20 +++++++++++++++----- block/qed.c | 2 +- block/replication.c | 7 ++++++- block/vmdk.c | 7 ++++--- include/block/block_int-common.h | 2 +- tests/unit/test-bdrv-drain.c | 22 ++++++++++++++++++---- tests/unit/test-bdrv-graph-mod.c | 5 ++++- 9 files changed, 70 insertions(+), 27 deletions(-) diff --git a/block.c b/block.c index cac517ab8b..ed0afdffbd 100644 --- a/block.c +++ b/block.c @@ -3560,10 +3560,14 @@ out: int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, Error **errp) { - BlockDriverState *drain_bs = bs->backing ? bs->backing->bs : bs; + BlockDriverState *drain_bs; int ret; GLOBAL_STATE_CODE(); + bdrv_graph_rdlock_main_loop(); + drain_bs = bs->backing ? bs->backing->bs : bs; + bdrv_graph_rdunlock_main_loop(); + bdrv_ref(drain_bs); bdrv_drained_begin(drain_bs); bdrv_graph_wrlock(backing_hd); @@ -3602,6 +3606,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, Error *local_err = NULL; GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); if (bs->backing != NULL) { goto free_exit; @@ -3643,10 +3648,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, implicit_backing = !strcmp(bs->auto_backing_file, bs->backing_file); } - bdrv_graph_rdlock_main_loop(); backing_filename = bdrv_get_full_backing_filename(bs, &local_err); - bdrv_graph_rdunlock_main_loop(); - if (local_err) { ret = -EINVAL; error_propagate(errp, local_err); @@ -3677,9 +3679,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, } if (implicit_backing) { - bdrv_graph_rdlock_main_loop(); bdrv_refresh_filename(backing_hd); - bdrv_graph_rdunlock_main_loop(); pstrcpy(bs->auto_backing_file, sizeof(bs->auto_backing_file), backing_hd->filename); } @@ -4750,8 +4750,8 @@ bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, { BlockDriverState *bs = reopen_state->bs; BlockDriverState *new_child_bs; - BlockDriverState *old_child_bs = is_backing ? child_bs(bs->backing) : - child_bs(bs->file); + BlockDriverState *old_child_bs; + const char *child_name = is_backing ? "backing" : "file"; QObject *value; const char *str; @@ -4797,6 +4797,7 @@ bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, g_assert_not_reached(); } + old_child_bs = is_backing ? child_bs(bs->backing) : child_bs(bs->file); if (old_child_bs == new_child_bs) { ret = 0; goto out_rdlock; @@ -5008,13 +5009,16 @@ bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, * file or if the image file has a backing file name as part of * its metadata. Otherwise the 'backing' option can be omitted. */ + bdrv_graph_rdlock_main_loop(); if (drv->supports_backing && reopen_state->backing_missing && (reopen_state->bs->backing || reopen_state->bs->backing_file[0])) { error_setg(errp, "backing is missing for '%s'", reopen_state->bs->node_name); + bdrv_graph_rdunlock_main_loop(); ret = -EINVAL; goto error; } + bdrv_graph_rdunlock_main_loop(); /* * Allow changing the 'backing' option. The new value can be @@ -5204,10 +5208,11 @@ static void bdrv_close(BlockDriverState *bs) QLIST_FOREACH_SAFE(child, &bs->children, next, next) { bdrv_unref_child(bs, child); } - bdrv_graph_wrunlock(); assert(!bs->backing); assert(!bs->file); + bdrv_graph_wrunlock(); + g_free(bs->opaque); bs->opaque = NULL; qatomic_set(&bs->copy_on_read, 0); @@ -5531,7 +5536,9 @@ int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, GLOBAL_STATE_CODE(); + bdrv_graph_rdlock_main_loop(); assert(!bs_new->backing); + bdrv_graph_rdunlock_main_loop(); old_context = bdrv_get_aio_context(bs_top); bdrv_drained_begin(bs_top); @@ -8115,7 +8122,7 @@ static bool append_strong_runtime_options(QDict *d, BlockDriverState *bs) /* Note: This function may return false positives; it may return true * even if opening the backing file specified by bs's image header * would result in exactly bs->backing. */ -static bool bdrv_backing_overridden(BlockDriverState *bs) +static bool GRAPH_RDLOCK bdrv_backing_overridden(BlockDriverState *bs) { GLOBAL_STATE_CODE(); if (bs->backing) { diff --git a/block/commit.c b/block/commit.c index 30bc082edc..eb3dc01f45 100644 --- a/block/commit.c +++ b/block/commit.c @@ -95,7 +95,10 @@ static void commit_abort(Job *job) * XXX Can (or should) we somehow keep 'consistent read' blocked even * after the failed/cancelled commit job is gone? If we already wrote * something to base, the intermediate images aren't valid any more. */ + bdrv_graph_rdlock_main_loop(); commit_top_backing_bs = s->commit_top_bs->backing->bs; + bdrv_graph_rdunlock_main_loop(); + bdrv_drained_begin(commit_top_backing_bs); bdrv_graph_wrlock(commit_top_backing_bs); bdrv_replace_node(s->commit_top_bs, commit_top_backing_bs, &error_abort); @@ -219,7 +222,7 @@ bdrv_commit_top_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags); } -static void bdrv_commit_top_refresh_filename(BlockDriverState *bs) +static GRAPH_RDLOCK void bdrv_commit_top_refresh_filename(BlockDriverState *bs) { pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), bs->backing->bs->filename); diff --git a/block/mirror.c b/block/mirror.c index dfc1c416e8..2096fade90 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -479,7 +479,7 @@ static unsigned mirror_perform(MirrorBlockJob *s, int64_t offset, return bytes_handled; } -static void coroutine_fn mirror_iteration(MirrorBlockJob *s) +static void coroutine_fn GRAPH_RDLOCK mirror_iteration(MirrorBlockJob *s) { BlockDriverState *source = s->mirror_top_bs->backing->bs; MirrorOp *pseudo_op; @@ -839,14 +839,18 @@ static void coroutine_fn mirror_throttle(MirrorBlockJob *s) } } -static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s) +static int coroutine_fn GRAPH_UNLOCKED mirror_dirty_init(MirrorBlockJob *s) { int64_t offset; - BlockDriverState *bs = s->mirror_top_bs->backing->bs; + BlockDriverState *bs; BlockDriverState *target_bs = blk_bs(s->target); int ret; int64_t count; + bdrv_graph_co_rdlock(); + bs = s->mirror_top_bs->backing->bs; + bdrv_graph_co_rdunlock(); + if (s->zero_target) { if (!bdrv_can_write_zeroes_with_unmap(target_bs)) { bdrv_set_dirty_bitmap(s->dirty_bitmap, 0, s->bdev_length); @@ -926,7 +930,7 @@ static int coroutine_fn mirror_flush(MirrorBlockJob *s) static int coroutine_fn mirror_run(Job *job, Error **errp) { MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); - BlockDriverState *bs = s->mirror_top_bs->backing->bs; + BlockDriverState *bs; MirrorBDSOpaque *mirror_top_opaque = s->mirror_top_bs->opaque; BlockDriverState *target_bs = blk_bs(s->target); bool need_drain = true; @@ -938,6 +942,10 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) checking for a NULL string */ int ret = 0; + bdrv_graph_co_rdlock(); + bs = bdrv_filter_bs(s->mirror_top_bs); + bdrv_graph_co_rdunlock(); + if (job_is_cancelled(&s->common.job)) { goto immediate_exit; } @@ -1070,7 +1078,9 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) mirror_wait_for_free_in_flight_slot(s); continue; } else if (cnt != 0) { + bdrv_graph_co_rdlock(); mirror_iteration(s); + bdrv_graph_co_rdunlock(); } } @@ -1640,7 +1650,7 @@ bdrv_mirror_top_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) offset, bytes, NULL, 0); } -static void bdrv_mirror_top_refresh_filename(BlockDriverState *bs) +static void GRAPH_RDLOCK bdrv_mirror_top_refresh_filename(BlockDriverState *bs) { if (bs->backing == NULL) { /* we can be here after failed bdrv_attach_child in diff --git a/block/qed.c b/block/qed.c index 45ae320290..686ad711f7 100644 --- a/block/qed.c +++ b/block/qed.c @@ -1138,7 +1138,7 @@ out: /** * Check if the QED_F_NEED_CHECK bit should be set during allocating write */ -static bool qed_should_set_need_check(BDRVQEDState *s) +static bool GRAPH_RDLOCK qed_should_set_need_check(BDRVQEDState *s) { /* The flush before L2 update path ensures consistency */ if (s->bs->backing) { diff --git a/block/replication.c b/block/replication.c index d522c7396f..49ecc608b2 100644 --- a/block/replication.c +++ b/block/replication.c @@ -363,6 +363,9 @@ static void reopen_backing_file(BlockDriverState *bs, bool writable, BdrvChild *hidden_disk, *secondary_disk; BlockReopenQueue *reopen_queue = NULL; + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + /* * s->hidden_disk and s->secondary_disk may not be set yet, as they will * only be set after the children are writable. @@ -496,9 +499,11 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, case REPLICATION_MODE_PRIMARY: break; case REPLICATION_MODE_SECONDARY: + bdrv_graph_rdlock_main_loop(); active_disk = bs->file; if (!active_disk || !active_disk->bs || !active_disk->bs->backing) { error_setg(errp, "Active disk doesn't have backing file"); + bdrv_graph_rdunlock_main_loop(); aio_context_release(aio_context); return; } @@ -506,11 +511,11 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, hidden_disk = active_disk->bs->backing; if (!hidden_disk->bs || !hidden_disk->bs->backing) { error_setg(errp, "Hidden disk doesn't have backing file"); + bdrv_graph_rdunlock_main_loop(); aio_context_release(aio_context); return; } - bdrv_graph_rdlock_main_loop(); secondary_disk = hidden_disk->bs->backing; if (!secondary_disk->bs || !bdrv_has_blk(secondary_disk->bs)) { error_setg(errp, "The secondary disk doesn't have block backend"); diff --git a/block/vmdk.c b/block/vmdk.c index 91ed7a8d93..5c789bb65b 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -380,7 +380,7 @@ out: return ret; } -static int coroutine_fn vmdk_is_cid_valid(BlockDriverState *bs) +static int coroutine_fn GRAPH_RDLOCK vmdk_is_cid_valid(BlockDriverState *bs) { BDRVVmdkState *s = bs->opaque; uint32_t cur_pcid; @@ -3044,8 +3044,9 @@ vmdk_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) return 0; } -static void vmdk_gather_child_options(BlockDriverState *bs, QDict *target, - bool backing_overridden) +static void GRAPH_RDLOCK +vmdk_gather_child_options(BlockDriverState *bs, QDict *target, + bool backing_overridden) { /* No children but file and backing can be explicitly specified (TODO) */ qdict_put(target, "file", diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h index c0db862de7..ed6066929a 100644 --- a/include/block/block_int-common.h +++ b/include/block/block_int-common.h @@ -1178,7 +1178,7 @@ struct BlockDriverState { * are connected with BdrvChildRole. */ QLIST_HEAD(, BdrvChild GRAPH_RDLOCK_PTR) children; - BdrvChild *backing; + BdrvChild * GRAPH_RDLOCK_PTR backing; BdrvChild *file; QLIST_HEAD(, BdrvChild GRAPH_RDLOCK_PTR) parents; diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c index b16f831c23..ba4e42b197 100644 --- a/tests/unit/test-bdrv-drain.c +++ b/tests/unit/test-bdrv-drain.c @@ -218,8 +218,14 @@ static void do_drain_end_unlocked(enum drain_type drain_type, BlockDriverState * } } -static void test_drv_cb_common(BlockBackend *blk, enum drain_type drain_type, - bool recursive) +/* + * Locking the block graph would be a bit cumbersome here because this function + * is called both in coroutine and non-coroutine context. We know this is a test + * and nothing else is running, so don't bother with TSA. + */ +static void coroutine_mixed_fn TSA_NO_TSA +test_drv_cb_common(BlockBackend *blk, enum drain_type drain_type, + bool recursive) { BlockDriverState *bs = blk_bs(blk); BlockDriverState *backing = bs->backing->bs; @@ -307,8 +313,14 @@ static void test_drv_cb_co_drain(void) blk_unref(blk); } -static void test_quiesce_common(BlockBackend *blk, enum drain_type drain_type, - bool recursive) +/* + * Locking the block graph would be a bit cumbersome here because this function + * is called both in coroutine and non-coroutine context. We know this is a test + * and nothing else is running, so don't bother with TSA. + */ +static void coroutine_mixed_fn TSA_NO_TSA +test_quiesce_common(BlockBackend *blk, enum drain_type drain_type, + bool recursive) { BlockDriverState *bs = blk_bs(blk); BlockDriverState *backing = bs->backing->bs; @@ -1868,6 +1880,8 @@ static void bdrv_replace_test_drain_end(BlockDriverState *bs) { BDRVReplaceTestState *s = bs->opaque; + GRAPH_RDLOCK_GUARD_MAINLOOP(); + if (!s->setup_completed) { return; } diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c index 22d4cd83f6..878544dbd5 100644 --- a/tests/unit/test-bdrv-graph-mod.c +++ b/tests/unit/test-bdrv-graph-mod.c @@ -206,15 +206,18 @@ static void test_should_update_child(void) bdrv_set_backing_hd(target, bs, &error_abort); - g_assert(target->backing->bs == bs); bdrv_graph_wrlock(NULL); + g_assert(target->backing->bs == bs); bdrv_attach_child(filter, target, "target", &child_of_bds, BDRV_CHILD_DATA, &error_abort); bdrv_graph_wrunlock(); aio_context_acquire(qemu_get_aio_context()); bdrv_append(filter, bs, &error_abort); aio_context_release(qemu_get_aio_context()); + + bdrv_graph_rdlock_main_loop(); g_assert(target->backing->bs == bs); + bdrv_graph_rdunlock_main_loop(); bdrv_unref(filter); bdrv_unref(bs); From 244b26d259bc56097b2d82690847cb7657e22830 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 27 Oct 2023 17:53:27 +0200 Subject: [PATCH 906/974] blkverify: Add locking for request_fn This is either bdrv_co_preadv() or bdrv_co_pwritev() which both need to have the graph locked. Annotate the function pointer accordingly and add locking to its callers. This shouldn't actually have resulted in a bug because the graph lock is already held by blkverify_co_prwv(), which waits for the coroutines to terminate. Annotate with GRAPH_RDLOCK as well to make this clearer. Signed-off-by: Kevin Wolf Message-ID: <20231027155333.420094-19-kwolf@redhat.com> Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- block/blkverify.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/block/blkverify.c b/block/blkverify.c index dae9716a26..9ea2cb4cc5 100644 --- a/block/blkverify.c +++ b/block/blkverify.c @@ -33,8 +33,8 @@ typedef struct BlkverifyRequest { uint64_t bytes; int flags; - int (*request_fn)(BdrvChild *, int64_t, int64_t, QEMUIOVector *, - BdrvRequestFlags); + int GRAPH_RDLOCK_PTR (*request_fn)( + BdrvChild *, int64_t, int64_t, QEMUIOVector *, BdrvRequestFlags); int ret; /* test image result */ int raw_ret; /* raw image result */ @@ -170,8 +170,11 @@ static void coroutine_fn blkverify_do_test_req(void *opaque) BlkverifyRequest *r = opaque; BDRVBlkverifyState *s = r->bs->opaque; + bdrv_graph_co_rdlock(); r->ret = r->request_fn(s->test_file, r->offset, r->bytes, r->qiov, r->flags); + bdrv_graph_co_rdunlock(); + r->done++; qemu_coroutine_enter_if_inactive(r->co); } @@ -180,13 +183,16 @@ static void coroutine_fn blkverify_do_raw_req(void *opaque) { BlkverifyRequest *r = opaque; + bdrv_graph_co_rdlock(); r->raw_ret = r->request_fn(r->bs->file, r->offset, r->bytes, r->raw_qiov, r->flags); + bdrv_graph_co_rdunlock(); + r->done++; qemu_coroutine_enter_if_inactive(r->co); } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK blkverify_co_prwv(BlockDriverState *bs, BlkverifyRequest *r, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, QEMUIOVector *raw_qiov, int flags, bool is_write) @@ -222,7 +228,7 @@ blkverify_co_prwv(BlockDriverState *bs, BlkverifyRequest *r, uint64_t offset, return r->ret; } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK blkverify_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { @@ -251,7 +257,7 @@ blkverify_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, return ret; } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK blkverify_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { From e2dd273754eb9a47c33660b4e14074e8e96ada4d Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 27 Oct 2023 17:53:28 +0200 Subject: [PATCH 907/974] block: Introduce bdrv_co_change_backing_file() bdrv_change_backing_file() is called both inside and outside coroutine context. This makes it difficult for it to take the graph lock internally. It also means that driver implementations need to be able to run outside of coroutines, too. Switch it to the usual model with a coroutine based implementation and a co_wrapper instead. The new function is marked GRAPH_RDLOCK. As the co_wrapper now runs the function in the AioContext of the node (as it should always have done), this is not GLOBAL_STATE_CODE() any more. Signed-off-by: Kevin Wolf Message-ID: <20231027155333.420094-20-kwolf@redhat.com> Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- block.c | 11 +-- block/qcow2.c | 112 +++++++++++++++-------------- block/qed.c | 62 ++++++++-------- include/block/block-global-state.h | 3 +- include/block/block-io.h | 8 +++ include/block/block_int-common.h | 5 +- tests/unit/test-bdrv-drain.c | 8 +-- 7 files changed, 111 insertions(+), 98 deletions(-) diff --git a/block.c b/block.c index ed0afdffbd..4910b95d7d 100644 --- a/block.c +++ b/block.c @@ -5764,13 +5764,14 @@ int coroutine_fn bdrv_co_check(BlockDriverState *bs, * image file header * -ENOTSUP - format driver doesn't support changing the backing file */ -int bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file, - const char *backing_fmt, bool require) +int coroutine_fn +bdrv_co_change_backing_file(BlockDriverState *bs, const char *backing_file, + const char *backing_fmt, bool require) { BlockDriver *drv = bs->drv; int ret; - GLOBAL_STATE_CODE(); + IO_CODE(); if (!drv) { return -ENOMEDIUM; @@ -5785,8 +5786,8 @@ int bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file, return -EINVAL; } - if (drv->bdrv_change_backing_file != NULL) { - ret = drv->bdrv_change_backing_file(bs, backing_file, backing_fmt); + if (drv->bdrv_co_change_backing_file != NULL) { + ret = drv->bdrv_co_change_backing_file(bs, backing_file, backing_fmt); } else { ret = -ENOTSUP; } diff --git a/block/qcow2.c b/block/qcow2.c index a1443a31aa..875e613ea9 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -3155,8 +3155,9 @@ fail: return ret; } -static int qcow2_change_backing_file(BlockDriverState *bs, - const char *backing_file, const char *backing_fmt) +static int coroutine_fn GRAPH_RDLOCK +qcow2_co_change_backing_file(BlockDriverState *bs, const char *backing_file, + const char *backing_fmt) { BDRVQcow2State *s = bs->opaque; @@ -3816,8 +3817,11 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) backing_format = BlockdevDriver_str(qcow2_opts->backing_fmt); } - ret = bdrv_change_backing_file(blk_bs(blk), qcow2_opts->backing_file, - backing_format, false); + bdrv_graph_co_rdlock(); + ret = bdrv_co_change_backing_file(blk_bs(blk), qcow2_opts->backing_file, + backing_format, false); + bdrv_graph_co_rdunlock(); + if (ret < 0) { error_setg_errno(errp, -ret, "Could not assign backing file '%s' " "with format '%s'", qcow2_opts->backing_file, @@ -6115,64 +6119,64 @@ static const char *const qcow2_strong_runtime_opts[] = { }; BlockDriver bdrv_qcow2 = { - .format_name = "qcow2", - .instance_size = sizeof(BDRVQcow2State), - .bdrv_probe = qcow2_probe, - .bdrv_open = qcow2_open, - .bdrv_close = qcow2_close, - .bdrv_reopen_prepare = qcow2_reopen_prepare, - .bdrv_reopen_commit = qcow2_reopen_commit, - .bdrv_reopen_commit_post = qcow2_reopen_commit_post, - .bdrv_reopen_abort = qcow2_reopen_abort, - .bdrv_join_options = qcow2_join_options, - .bdrv_child_perm = bdrv_default_perms, - .bdrv_co_create_opts = qcow2_co_create_opts, - .bdrv_co_create = qcow2_co_create, - .bdrv_has_zero_init = qcow2_has_zero_init, - .bdrv_co_block_status = qcow2_co_block_status, + .format_name = "qcow2", + .instance_size = sizeof(BDRVQcow2State), + .bdrv_probe = qcow2_probe, + .bdrv_open = qcow2_open, + .bdrv_close = qcow2_close, + .bdrv_reopen_prepare = qcow2_reopen_prepare, + .bdrv_reopen_commit = qcow2_reopen_commit, + .bdrv_reopen_commit_post = qcow2_reopen_commit_post, + .bdrv_reopen_abort = qcow2_reopen_abort, + .bdrv_join_options = qcow2_join_options, + .bdrv_child_perm = bdrv_default_perms, + .bdrv_co_create_opts = qcow2_co_create_opts, + .bdrv_co_create = qcow2_co_create, + .bdrv_has_zero_init = qcow2_has_zero_init, + .bdrv_co_block_status = qcow2_co_block_status, - .bdrv_co_preadv_part = qcow2_co_preadv_part, - .bdrv_co_pwritev_part = qcow2_co_pwritev_part, - .bdrv_co_flush_to_os = qcow2_co_flush_to_os, + .bdrv_co_preadv_part = qcow2_co_preadv_part, + .bdrv_co_pwritev_part = qcow2_co_pwritev_part, + .bdrv_co_flush_to_os = qcow2_co_flush_to_os, - .bdrv_co_pwrite_zeroes = qcow2_co_pwrite_zeroes, - .bdrv_co_pdiscard = qcow2_co_pdiscard, - .bdrv_co_copy_range_from = qcow2_co_copy_range_from, - .bdrv_co_copy_range_to = qcow2_co_copy_range_to, - .bdrv_co_truncate = qcow2_co_truncate, - .bdrv_co_pwritev_compressed_part = qcow2_co_pwritev_compressed_part, - .bdrv_make_empty = qcow2_make_empty, + .bdrv_co_pwrite_zeroes = qcow2_co_pwrite_zeroes, + .bdrv_co_pdiscard = qcow2_co_pdiscard, + .bdrv_co_copy_range_from = qcow2_co_copy_range_from, + .bdrv_co_copy_range_to = qcow2_co_copy_range_to, + .bdrv_co_truncate = qcow2_co_truncate, + .bdrv_co_pwritev_compressed_part = qcow2_co_pwritev_compressed_part, + .bdrv_make_empty = qcow2_make_empty, - .bdrv_snapshot_create = qcow2_snapshot_create, - .bdrv_snapshot_goto = qcow2_snapshot_goto, - .bdrv_snapshot_delete = qcow2_snapshot_delete, - .bdrv_snapshot_list = qcow2_snapshot_list, - .bdrv_snapshot_load_tmp = qcow2_snapshot_load_tmp, - .bdrv_measure = qcow2_measure, - .bdrv_co_get_info = qcow2_co_get_info, - .bdrv_get_specific_info = qcow2_get_specific_info, + .bdrv_snapshot_create = qcow2_snapshot_create, + .bdrv_snapshot_goto = qcow2_snapshot_goto, + .bdrv_snapshot_delete = qcow2_snapshot_delete, + .bdrv_snapshot_list = qcow2_snapshot_list, + .bdrv_snapshot_load_tmp = qcow2_snapshot_load_tmp, + .bdrv_measure = qcow2_measure, + .bdrv_co_get_info = qcow2_co_get_info, + .bdrv_get_specific_info = qcow2_get_specific_info, - .bdrv_co_save_vmstate = qcow2_co_save_vmstate, - .bdrv_co_load_vmstate = qcow2_co_load_vmstate, + .bdrv_co_save_vmstate = qcow2_co_save_vmstate, + .bdrv_co_load_vmstate = qcow2_co_load_vmstate, - .is_format = true, - .supports_backing = true, - .bdrv_change_backing_file = qcow2_change_backing_file, + .is_format = true, + .supports_backing = true, + .bdrv_co_change_backing_file = qcow2_co_change_backing_file, - .bdrv_refresh_limits = qcow2_refresh_limits, - .bdrv_co_invalidate_cache = qcow2_co_invalidate_cache, - .bdrv_inactivate = qcow2_inactivate, + .bdrv_refresh_limits = qcow2_refresh_limits, + .bdrv_co_invalidate_cache = qcow2_co_invalidate_cache, + .bdrv_inactivate = qcow2_inactivate, - .create_opts = &qcow2_create_opts, - .amend_opts = &qcow2_amend_opts, - .strong_runtime_opts = qcow2_strong_runtime_opts, - .mutable_opts = mutable_opts, - .bdrv_co_check = qcow2_co_check, - .bdrv_amend_options = qcow2_amend_options, - .bdrv_co_amend = qcow2_co_amend, + .create_opts = &qcow2_create_opts, + .amend_opts = &qcow2_amend_opts, + .strong_runtime_opts = qcow2_strong_runtime_opts, + .mutable_opts = mutable_opts, + .bdrv_co_check = qcow2_co_check, + .bdrv_amend_options = qcow2_amend_options, + .bdrv_co_amend = qcow2_co_amend, - .bdrv_detach_aio_context = qcow2_detach_aio_context, - .bdrv_attach_aio_context = qcow2_attach_aio_context, + .bdrv_detach_aio_context = qcow2_detach_aio_context, + .bdrv_attach_aio_context = qcow2_attach_aio_context, .bdrv_supports_persistent_dirty_bitmap = qcow2_supports_persistent_dirty_bitmap, diff --git a/block/qed.c b/block/qed.c index 686ad711f7..996aa384fe 100644 --- a/block/qed.c +++ b/block/qed.c @@ -1498,9 +1498,9 @@ bdrv_qed_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) return 0; } -static int bdrv_qed_change_backing_file(BlockDriverState *bs, - const char *backing_file, - const char *backing_fmt) +static int coroutine_fn GRAPH_RDLOCK +bdrv_qed_co_change_backing_file(BlockDriverState *bs, const char *backing_file, + const char *backing_fmt) { BDRVQEDState *s = bs->opaque; QEDHeader new_header, le_header; @@ -1562,7 +1562,7 @@ static int bdrv_qed_change_backing_file(BlockDriverState *bs, } /* Write new header */ - ret = bdrv_pwrite_sync(bs->file, 0, buffer_len, buffer, 0); + ret = bdrv_co_pwrite_sync(bs->file, 0, buffer_len, buffer, 0); g_free(buffer); if (ret == 0) { memcpy(&s->header, &new_header, sizeof(new_header)); @@ -1636,34 +1636,34 @@ static QemuOptsList qed_create_opts = { }; static BlockDriver bdrv_qed = { - .format_name = "qed", - .instance_size = sizeof(BDRVQEDState), - .create_opts = &qed_create_opts, - .is_format = true, - .supports_backing = true, + .format_name = "qed", + .instance_size = sizeof(BDRVQEDState), + .create_opts = &qed_create_opts, + .is_format = true, + .supports_backing = true, - .bdrv_probe = bdrv_qed_probe, - .bdrv_open = bdrv_qed_open, - .bdrv_close = bdrv_qed_close, - .bdrv_reopen_prepare = bdrv_qed_reopen_prepare, - .bdrv_child_perm = bdrv_default_perms, - .bdrv_co_create = bdrv_qed_co_create, - .bdrv_co_create_opts = bdrv_qed_co_create_opts, - .bdrv_has_zero_init = bdrv_has_zero_init_1, - .bdrv_co_block_status = bdrv_qed_co_block_status, - .bdrv_co_readv = bdrv_qed_co_readv, - .bdrv_co_writev = bdrv_qed_co_writev, - .bdrv_co_pwrite_zeroes = bdrv_qed_co_pwrite_zeroes, - .bdrv_co_truncate = bdrv_qed_co_truncate, - .bdrv_co_getlength = bdrv_qed_co_getlength, - .bdrv_co_get_info = bdrv_qed_co_get_info, - .bdrv_refresh_limits = bdrv_qed_refresh_limits, - .bdrv_change_backing_file = bdrv_qed_change_backing_file, - .bdrv_co_invalidate_cache = bdrv_qed_co_invalidate_cache, - .bdrv_co_check = bdrv_qed_co_check, - .bdrv_detach_aio_context = bdrv_qed_detach_aio_context, - .bdrv_attach_aio_context = bdrv_qed_attach_aio_context, - .bdrv_drain_begin = bdrv_qed_drain_begin, + .bdrv_probe = bdrv_qed_probe, + .bdrv_open = bdrv_qed_open, + .bdrv_close = bdrv_qed_close, + .bdrv_reopen_prepare = bdrv_qed_reopen_prepare, + .bdrv_child_perm = bdrv_default_perms, + .bdrv_co_create = bdrv_qed_co_create, + .bdrv_co_create_opts = bdrv_qed_co_create_opts, + .bdrv_has_zero_init = bdrv_has_zero_init_1, + .bdrv_co_block_status = bdrv_qed_co_block_status, + .bdrv_co_readv = bdrv_qed_co_readv, + .bdrv_co_writev = bdrv_qed_co_writev, + .bdrv_co_pwrite_zeroes = bdrv_qed_co_pwrite_zeroes, + .bdrv_co_truncate = bdrv_qed_co_truncate, + .bdrv_co_getlength = bdrv_qed_co_getlength, + .bdrv_co_get_info = bdrv_qed_co_get_info, + .bdrv_refresh_limits = bdrv_qed_refresh_limits, + .bdrv_co_change_backing_file = bdrv_qed_co_change_backing_file, + .bdrv_co_invalidate_cache = bdrv_qed_co_invalidate_cache, + .bdrv_co_check = bdrv_qed_co_check, + .bdrv_detach_aio_context = bdrv_qed_detach_aio_context, + .bdrv_attach_aio_context = bdrv_qed_attach_aio_context, + .bdrv_drain_begin = bdrv_qed_drain_begin, }; static void bdrv_qed_init(void) diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h index 9e0ccc1c32..6b21fbc73f 100644 --- a/include/block/block-global-state.h +++ b/include/block/block-global-state.h @@ -142,8 +142,7 @@ bdrv_refresh_limits(BlockDriverState *bs, Transaction *tran, Error **errp); int bdrv_commit(BlockDriverState *bs); int GRAPH_RDLOCK bdrv_make_empty(BdrvChild *c, Error **errp); -int bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file, - const char *backing_fmt, bool warn); + void bdrv_register(BlockDriver *bdrv); int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, const char *backing_file_str); diff --git a/include/block/block-io.h b/include/block/block-io.h index 58c4cf50a0..f8729ccc55 100644 --- a/include/block/block-io.h +++ b/include/block/block-io.h @@ -210,6 +210,14 @@ void bdrv_round_to_subclusters(BlockDriverState *bs, void bdrv_get_backing_filename(BlockDriverState *bs, char *filename, int filename_size); +int coroutine_fn GRAPH_RDLOCK +bdrv_co_change_backing_file(BlockDriverState *bs, const char *backing_file, + const char *backing_fmt, bool warn); + +int co_wrapper_bdrv_rdlock +bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file, + const char *backing_fmt, bool warn); + int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf, int64_t pos, int size); diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h index ed6066929a..59f6d7f195 100644 --- a/include/block/block_int-common.h +++ b/include/block/block_int-common.h @@ -331,8 +331,9 @@ struct BlockDriver { const char *name, Error **errp); - int (*bdrv_change_backing_file)(BlockDriverState *bs, - const char *backing_file, const char *backing_fmt); + int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_change_backing_file)( + BlockDriverState *bs, const char *backing_file, + const char *backing_fmt); /* TODO Better pass a option string/QDict/QemuOpts to add any rule? */ int (*bdrv_debug_breakpoint)(BlockDriverState *bs, const char *event, diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c index ba4e42b197..8d05538bf6 100644 --- a/tests/unit/test-bdrv-drain.c +++ b/tests/unit/test-bdrv-drain.c @@ -96,9 +96,9 @@ static int coroutine_fn bdrv_test_co_preadv(BlockDriverState *bs, return 0; } -static int bdrv_test_change_backing_file(BlockDriverState *bs, - const char *backing_file, - const char *backing_fmt) +static int bdrv_test_co_change_backing_file(BlockDriverState *bs, + const char *backing_file, + const char *backing_fmt) { return 0; } @@ -116,7 +116,7 @@ static BlockDriver bdrv_test = { .bdrv_child_perm = bdrv_default_perms, - .bdrv_change_backing_file = bdrv_test_change_backing_file, + .bdrv_co_change_backing_file = bdrv_test_co_change_backing_file, }; static void aio_ret_cb(void *opaque, int ret) From 79a558664840adf502fe94907b0a680836e3e98e Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 27 Oct 2023 17:53:29 +0200 Subject: [PATCH 908/974] block: Add missing GRAPH_RDLOCK annotations This adds GRAPH_RDLOCK to some driver callbacks that are already called with the graph lock held, and which will need the annotation because they access bs->file, but don't have it yet. This also covers a few callbacks that were not marked GRAPH_RDLOCK before, but where updating BlockDriver is trivially possible. Signed-off-by: Kevin Wolf Message-ID: <20231027155333.420094-21-kwolf@redhat.com> Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- block/blkdebug.c | 13 +++++-------- block/blkverify.c | 2 +- block/copy-before-write.c | 2 +- block/filter-compress.c | 3 ++- block/io.c | 2 ++ block/parallels.c | 12 ++++-------- block/preallocate.c | 4 ++-- block/qcow.c | 2 +- block/qcow2.c | 4 ++-- block/qcow2.h | 11 ++++++----- block/qed.c | 10 ++++------ block/raw-format.c | 16 ++++++++-------- block/snapshot-access.c | 2 +- block/vdi.c | 9 ++++----- block/vhdx.c | 6 +++--- include/block/block_int-common.h | 17 +++++++++-------- 16 files changed, 55 insertions(+), 60 deletions(-) diff --git a/block/blkdebug.c b/block/blkdebug.c index addad914b3..230efa9e8d 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -746,13 +746,10 @@ blkdebug_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) return bdrv_co_pdiscard(bs->file, offset, bytes); } -static int coroutine_fn blkdebug_co_block_status(BlockDriverState *bs, - bool want_zero, - int64_t offset, - int64_t bytes, - int64_t *pnum, - int64_t *map, - BlockDriverState **file) +static int coroutine_fn GRAPH_RDLOCK +blkdebug_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset, + int64_t bytes, int64_t *pnum, int64_t *map, + BlockDriverState **file) { int err; @@ -973,7 +970,7 @@ blkdebug_co_getlength(BlockDriverState *bs) return bdrv_co_getlength(bs->file->bs); } -static void blkdebug_refresh_filename(BlockDriverState *bs) +static void GRAPH_RDLOCK blkdebug_refresh_filename(BlockDriverState *bs) { BDRVBlkdebugState *s = bs->opaque; const QDictEntry *e; diff --git a/block/blkverify.c b/block/blkverify.c index 9ea2cb4cc5..a96905db35 100644 --- a/block/blkverify.c +++ b/block/blkverify.c @@ -288,7 +288,7 @@ blkverify_recurse_can_replace(BlockDriverState *bs, bdrv_recurse_can_replace(s->test_file->bs, to_replace); } -static void blkverify_refresh_filename(BlockDriverState *bs) +static void GRAPH_RDLOCK blkverify_refresh_filename(BlockDriverState *bs) { BDRVBlkverifyState *s = bs->opaque; diff --git a/block/copy-before-write.c b/block/copy-before-write.c index 4ffabc5ca2..8193d3a4cd 100644 --- a/block/copy-before-write.c +++ b/block/copy-before-write.c @@ -335,7 +335,7 @@ cbw_co_pdiscard_snapshot(BlockDriverState *bs, int64_t offset, int64_t bytes) return bdrv_co_pdiscard(s->target, offset, bytes); } -static void cbw_refresh_filename(BlockDriverState *bs) +static void GRAPH_RDLOCK cbw_refresh_filename(BlockDriverState *bs) { pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), bs->file->bs->filename); diff --git a/block/filter-compress.c b/block/filter-compress.c index 320d9576fa..e3fc82f322 100644 --- a/block/filter-compress.c +++ b/block/filter-compress.c @@ -97,7 +97,8 @@ compress_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) } -static void compress_refresh_limits(BlockDriverState *bs, Error **errp) +static void GRAPH_RDLOCK +compress_refresh_limits(BlockDriverState *bs, Error **errp) { BlockDriverInfo bdi; int ret; diff --git a/block/io.c b/block/io.c index 527a1de04e..7e62fabbf5 100644 --- a/block/io.c +++ b/block/io.c @@ -3685,6 +3685,8 @@ out: void bdrv_cancel_in_flight(BlockDriverState *bs) { GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + if (!bs || !bs->drv) { return; } diff --git a/block/parallels.c b/block/parallels.c index 6318dd02e7..9b9bd1f993 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -415,14 +415,10 @@ parallels_co_flush_to_os(BlockDriverState *bs) return 0; } - -static int coroutine_fn parallels_co_block_status(BlockDriverState *bs, - bool want_zero, - int64_t offset, - int64_t bytes, - int64_t *pnum, - int64_t *map, - BlockDriverState **file) +static int coroutine_fn GRAPH_RDLOCK +parallels_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset, + int64_t bytes, int64_t *pnum, int64_t *map, + BlockDriverState **file) { BDRVParallelsState *s = bs->opaque; int count; diff --git a/block/preallocate.c b/block/preallocate.c index bfb638d8b1..4e0c891ab2 100644 --- a/block/preallocate.c +++ b/block/preallocate.c @@ -541,8 +541,8 @@ static void preallocate_drop_resize_bh(void *opaque) preallocate_drop_resize(opaque, NULL); } -static void preallocate_set_perm(BlockDriverState *bs, - uint64_t perm, uint64_t shared) +static void GRAPH_RDLOCK +preallocate_set_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared) { BDRVPreallocateState *s = bs->opaque; diff --git a/block/qcow.c b/block/qcow.c index eab68e387c..1e2835f1f4 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -1024,7 +1024,7 @@ fail: return ret; } -static int qcow_make_empty(BlockDriverState *bs) +static int GRAPH_RDLOCK qcow_make_empty(BlockDriverState *bs) { BDRVQcowState *s = bs->opaque; uint32_t l1_length = s->l1_size * sizeof(uint64_t); diff --git a/block/qcow2.c b/block/qcow2.c index 875e613ea9..7ab399ffc5 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -5226,8 +5226,8 @@ qcow2_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) return 0; } -static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs, - Error **errp) +static ImageInfoSpecific * GRAPH_RDLOCK +qcow2_get_specific_info(BlockDriverState *bs, Error **errp) { BDRVQcow2State *s = bs->opaque; ImageInfoSpecific *spec_info; diff --git a/block/qcow2.h b/block/qcow2.h index 29958c512b..948335979f 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -972,11 +972,12 @@ int GRAPH_RDLOCK qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id, const char *name, Error **errp); -int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab); -int qcow2_snapshot_load_tmp(BlockDriverState *bs, - const char *snapshot_id, - const char *name, - Error **errp); +int GRAPH_RDLOCK +qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab); + +int GRAPH_RDLOCK +qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_id, + const char *name, Error **errp); void qcow2_free_snapshots(BlockDriverState *bs); int coroutine_fn GRAPH_RDLOCK diff --git a/block/qed.c b/block/qed.c index 996aa384fe..f4c1628a81 100644 --- a/block/qed.c +++ b/block/qed.c @@ -1443,12 +1443,10 @@ bdrv_qed_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, QED_AIOCB_WRITE | QED_AIOCB_ZERO); } -static int coroutine_fn bdrv_qed_co_truncate(BlockDriverState *bs, - int64_t offset, - bool exact, - PreallocMode prealloc, - BdrvRequestFlags flags, - Error **errp) +static int coroutine_fn GRAPH_RDLOCK +bdrv_qed_co_truncate(BlockDriverState *bs, int64_t offset, bool exact, + PreallocMode prealloc, BdrvRequestFlags flags, + Error **errp) { BDRVQEDState *s = bs->opaque; uint64_t old_image_size; diff --git a/block/raw-format.c b/block/raw-format.c index 8ca74c1cf3..2640d54801 100644 --- a/block/raw-format.c +++ b/block/raw-format.c @@ -279,11 +279,10 @@ fail: return ret; } -static int coroutine_fn raw_co_block_status(BlockDriverState *bs, - bool want_zero, int64_t offset, - int64_t bytes, int64_t *pnum, - int64_t *map, - BlockDriverState **file) +static int coroutine_fn GRAPH_RDLOCK +raw_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset, + int64_t bytes, int64_t *pnum, int64_t *map, + BlockDriverState **file) { BDRVRawState *s = bs->opaque; *pnum = bytes; @@ -397,7 +396,7 @@ raw_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) return bdrv_co_get_info(bs->file->bs, bdi); } -static void raw_refresh_limits(BlockDriverState *bs, Error **errp) +static void GRAPH_RDLOCK raw_refresh_limits(BlockDriverState *bs, Error **errp) { bs->bl.has_variable_length = bs->file->bs->bl.has_variable_length; @@ -561,7 +560,8 @@ raw_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz) return 0; } -static int raw_probe_geometry(BlockDriverState *bs, HDGeometry *geo) +static int GRAPH_RDLOCK +raw_probe_geometry(BlockDriverState *bs, HDGeometry *geo) { BDRVRawState *s = bs->opaque; if (s->offset || s->has_size) { @@ -611,7 +611,7 @@ static const char *const raw_strong_runtime_opts[] = { NULL }; -static void raw_cancel_in_flight(BlockDriverState *bs) +static void GRAPH_RDLOCK raw_cancel_in_flight(BlockDriverState *bs) { bdrv_cancel_in_flight(bs->file->bs); } diff --git a/block/snapshot-access.c b/block/snapshot-access.c index 8d4e8932b8..7c45739eb1 100644 --- a/block/snapshot-access.c +++ b/block/snapshot-access.c @@ -73,7 +73,7 @@ snapshot_access_co_pwritev_part(BlockDriverState *bs, } -static void snapshot_access_refresh_filename(BlockDriverState *bs) +static void GRAPH_RDLOCK snapshot_access_refresh_filename(BlockDriverState *bs) { pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), bs->file->bs->filename); diff --git a/block/vdi.c b/block/vdi.c index 8e144ce523..84150180ec 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -520,11 +520,10 @@ static int vdi_reopen_prepare(BDRVReopenState *state, return 0; } -static int coroutine_fn vdi_co_block_status(BlockDriverState *bs, - bool want_zero, - int64_t offset, int64_t bytes, - int64_t *pnum, int64_t *map, - BlockDriverState **file) +static int coroutine_fn GRAPH_RDLOCK +vdi_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset, + int64_t bytes, int64_t *pnum, int64_t *map, + BlockDriverState **file) { BDRVVdiState *s = (BDRVVdiState *)bs->opaque; size_t bmap_index = offset / s->block_size; diff --git a/block/vhdx.c b/block/vhdx.c index e136ba1ae1..ac4d52e8c6 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -2163,9 +2163,9 @@ fail: * r/w and any log has already been replayed, so there is nothing (currently) * for us to do here */ -static int coroutine_fn vhdx_co_check(BlockDriverState *bs, - BdrvCheckResult *result, - BdrvCheckMode fix) +static int coroutine_fn GRAPH_RDLOCK +vhdx_co_check(BlockDriverState *bs, BdrvCheckResult *result, + BdrvCheckMode fix) { BDRVVHDXState *s = bs->opaque; diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h index 59f6d7f195..63bc523d7c 100644 --- a/include/block/block_int-common.h +++ b/include/block/block_int-common.h @@ -310,7 +310,7 @@ struct BlockDriver { * One example usage is to avoid waiting for an nbd target node reconnect * timeout during job-cancel with force=true. */ - void (*bdrv_cancel_in_flight)(BlockDriverState *bs); + void GRAPH_RDLOCK_PTR (*bdrv_cancel_in_flight)(BlockDriverState *bs); int GRAPH_RDLOCK_PTR (*bdrv_inactivate)(BlockDriverState *bs); @@ -324,12 +324,12 @@ struct BlockDriver { BlockDriverState *bs, const char *snapshot_id, const char *name, Error **errp); - int (*bdrv_snapshot_list)(BlockDriverState *bs, - QEMUSnapshotInfo **psn_info); - int (*bdrv_snapshot_load_tmp)(BlockDriverState *bs, - const char *snapshot_id, - const char *name, - Error **errp); + int GRAPH_RDLOCK_PTR (*bdrv_snapshot_list)( + BlockDriverState *bs, QEMUSnapshotInfo **psn_info); + + int GRAPH_RDLOCK_PTR (*bdrv_snapshot_load_tmp)( + BlockDriverState *bs, const char *snapshot_id, const char *name, + Error **errp); int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_change_backing_file)( BlockDriverState *bs, const char *backing_file, @@ -396,7 +396,8 @@ struct BlockDriver { * Only drivers that want to override guest geometry implement this * callback; see hd_geometry_guess(). */ - int (*bdrv_probe_geometry)(BlockDriverState *bs, HDGeometry *geo); + int GRAPH_RDLOCK_PTR (*bdrv_probe_geometry)( + BlockDriverState *bs, HDGeometry *geo); void GRAPH_WRLOCK_PTR (*bdrv_add_child)( BlockDriverState *parent, BlockDriverState *child, Error **errp); From 8f8973416e231bc1e45b2c4a3d419fbb8ef91c94 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 27 Oct 2023 17:53:30 +0200 Subject: [PATCH 909/974] qcow2: Take locks for accessing bs->file This updates the qcow2 code to add GRAPH_RDLOCK annotations for all places that read bs->file. Signed-off-by: Kevin Wolf Message-ID: <20231027155333.420094-22-kwolf@redhat.com> Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- block/qcow2-bitmap.c | 14 +++++++------ block/qcow2-cluster.c | 25 +++++++++++----------- block/qcow2.c | 13 ++++++++---- block/qcow2.h | 48 ++++++++++++++++++++++++++----------------- 4 files changed, 59 insertions(+), 41 deletions(-) diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c index 3058309c47..0e567ed588 100644 --- a/block/qcow2-bitmap.c +++ b/block/qcow2-bitmap.c @@ -105,7 +105,7 @@ static inline bool can_write(BlockDriverState *bs) return !bdrv_is_read_only(bs) && !(bdrv_get_flags(bs) & BDRV_O_INACTIVE); } -static int update_header_sync(BlockDriverState *bs) +static int GRAPH_RDLOCK update_header_sync(BlockDriverState *bs) { int ret; @@ -221,8 +221,9 @@ clear_bitmap_table(BlockDriverState *bs, uint64_t *bitmap_table, } } -static int bitmap_table_load(BlockDriverState *bs, Qcow2BitmapTable *tb, - uint64_t **bitmap_table) +static int GRAPH_RDLOCK +bitmap_table_load(BlockDriverState *bs, Qcow2BitmapTable *tb, + uint64_t **bitmap_table) { int ret; BDRVQcow2State *s = bs->opaque; @@ -551,8 +552,9 @@ static uint32_t bitmap_list_count(Qcow2BitmapList *bm_list) * Get bitmap list from qcow2 image. Actually reads bitmap directory, * checks it and convert to bitmap list. */ -static Qcow2BitmapList *bitmap_list_load(BlockDriverState *bs, uint64_t offset, - uint64_t size, Error **errp) +static Qcow2BitmapList * GRAPH_RDLOCK +bitmap_list_load(BlockDriverState *bs, uint64_t offset, uint64_t size, + Error **errp) { int ret; BDRVQcow2State *s = bs->opaque; @@ -961,7 +963,7 @@ static void set_readonly_helper(gpointer bitmap, gpointer value) * If header_updated is not NULL then it is set appropriately regardless of * the return value. */ -bool coroutine_fn GRAPH_RDLOCK +bool coroutine_fn qcow2_load_dirty_bitmaps(BlockDriverState *bs, bool *header_updated, Error **errp) { diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 5af439bd11..ce8c0076b3 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -391,11 +391,10 @@ fail: * If the L2 entry is invalid return -errno and set @type to * QCOW2_SUBCLUSTER_INVALID. */ -static int qcow2_get_subcluster_range_type(BlockDriverState *bs, - uint64_t l2_entry, - uint64_t l2_bitmap, - unsigned sc_from, - QCow2SubclusterType *type) +static int GRAPH_RDLOCK +qcow2_get_subcluster_range_type(BlockDriverState *bs, uint64_t l2_entry, + uint64_t l2_bitmap, unsigned sc_from, + QCow2SubclusterType *type) { BDRVQcow2State *s = bs->opaque; uint32_t val; @@ -442,9 +441,10 @@ static int qcow2_get_subcluster_range_type(BlockDriverState *bs, * On failure return -errno and update @l2_index to point to the * invalid entry. */ -static int count_contiguous_subclusters(BlockDriverState *bs, int nb_clusters, - unsigned sc_index, uint64_t *l2_slice, - unsigned *l2_index) +static int GRAPH_RDLOCK +count_contiguous_subclusters(BlockDriverState *bs, int nb_clusters, + unsigned sc_index, uint64_t *l2_slice, + unsigned *l2_index) { BDRVQcow2State *s = bs->opaque; int i, count = 0; @@ -1329,7 +1329,8 @@ calculate_l2_meta(BlockDriverState *bs, uint64_t host_cluster_offset, * requires a new allocation (that is, if the cluster is unallocated * or has refcount > 1 and therefore cannot be written in-place). */ -static bool cluster_needs_new_alloc(BlockDriverState *bs, uint64_t l2_entry) +static bool GRAPH_RDLOCK +cluster_needs_new_alloc(BlockDriverState *bs, uint64_t l2_entry) { switch (qcow2_get_cluster_type(bs, l2_entry)) { case QCOW2_CLUSTER_NORMAL: @@ -1360,9 +1361,9 @@ static bool cluster_needs_new_alloc(BlockDriverState *bs, uint64_t l2_entry) * allocated and can be overwritten in-place (this includes clusters * of type QCOW2_CLUSTER_ZERO_ALLOC). */ -static int count_single_write_clusters(BlockDriverState *bs, int nb_clusters, - uint64_t *l2_slice, int l2_index, - bool new_alloc) +static int GRAPH_RDLOCK +count_single_write_clusters(BlockDriverState *bs, int nb_clusters, + uint64_t *l2_slice, int l2_index, bool new_alloc) { BDRVQcow2State *s = bs->opaque; uint64_t l2_entry = get_l2_entry(s, l2_slice, l2_index); diff --git a/block/qcow2.c b/block/qcow2.c index 7ab399ffc5..cf2468858f 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -95,9 +95,10 @@ static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename) } -static int qcow2_crypto_hdr_read_func(QCryptoBlock *block, size_t offset, - uint8_t *buf, size_t buflen, - void *opaque, Error **errp) +static int GRAPH_RDLOCK +qcow2_crypto_hdr_read_func(QCryptoBlock *block, size_t offset, + uint8_t *buf, size_t buflen, + void *opaque, Error **errp) { BlockDriverState *bs = opaque; BDRVQcow2State *s = bs->opaque; @@ -156,7 +157,7 @@ qcow2_crypto_hdr_init_func(QCryptoBlock *block, size_t headerlen, void *opaque, /* The graph lock must be held when called in coroutine context */ -static int coroutine_mixed_fn +static int coroutine_mixed_fn GRAPH_RDLOCK qcow2_crypto_hdr_write_func(QCryptoBlock *block, size_t offset, const uint8_t *buf, size_t buflen, void *opaque, Error **errp) @@ -2029,6 +2030,8 @@ static void qcow2_reopen_commit(BDRVReopenState *state) { BDRVQcow2State *s = state->bs->opaque; + GRAPH_RDLOCK_GUARD_MAINLOOP(); + qcow2_update_options_commit(state->bs, state->opaque); if (!s->data_file) { /* @@ -2064,6 +2067,8 @@ static void qcow2_reopen_abort(BDRVReopenState *state) { BDRVQcow2State *s = state->bs->opaque; + GRAPH_RDLOCK_GUARD_MAINLOOP(); + if (!s->data_file) { /* * If we don't have an external data file, s->data_file was cleared by diff --git a/block/qcow2.h b/block/qcow2.h index 948335979f..a9e3481c6e 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -641,7 +641,7 @@ static inline void set_l2_bitmap(BDRVQcow2State *s, uint64_t *l2_slice, l2_slice[idx + 1] = cpu_to_be64(bitmap); } -static inline bool has_data_file(BlockDriverState *bs) +static inline bool GRAPH_RDLOCK has_data_file(BlockDriverState *bs) { BDRVQcow2State *s = bs->opaque; return (s->data_file != bs->file); @@ -709,8 +709,8 @@ static inline int64_t qcow2_vm_state_offset(BDRVQcow2State *s) return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits); } -static inline QCow2ClusterType qcow2_get_cluster_type(BlockDriverState *bs, - uint64_t l2_entry) +static inline QCow2ClusterType GRAPH_RDLOCK +qcow2_get_cluster_type(BlockDriverState *bs, uint64_t l2_entry) { BDRVQcow2State *s = bs->opaque; @@ -743,7 +743,7 @@ static inline QCow2ClusterType qcow2_get_cluster_type(BlockDriverState *bs, * (this checks the whole entry and bitmap, not only the bits related * to subcluster @sc_index). */ -static inline +static inline GRAPH_RDLOCK QCow2SubclusterType qcow2_get_subcluster_type(BlockDriverState *bs, uint64_t l2_entry, uint64_t l2_bitmap, @@ -834,9 +834,9 @@ int64_t qcow2_refcount_metadata_size(int64_t clusters, size_t cluster_size, int refcount_order, bool generous_increase, uint64_t *refblock_count); -int qcow2_mark_dirty(BlockDriverState *bs); -int qcow2_mark_corrupt(BlockDriverState *bs); -int qcow2_update_header(BlockDriverState *bs); +int GRAPH_RDLOCK qcow2_mark_dirty(BlockDriverState *bs); +int GRAPH_RDLOCK qcow2_mark_corrupt(BlockDriverState *bs); +int GRAPH_RDLOCK qcow2_update_header(BlockDriverState *bs); void GRAPH_RDLOCK qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset, @@ -890,10 +890,11 @@ int GRAPH_RDLOCK qcow2_write_caches(BlockDriverState *bs); int coroutine_fn qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix); -void qcow2_process_discards(BlockDriverState *bs, int ret); +void GRAPH_RDLOCK qcow2_process_discards(BlockDriverState *bs, int ret); -int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, - int64_t size); +int GRAPH_RDLOCK +qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, + int64_t size); int GRAPH_RDLOCK qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset, int64_t size, bool data_file); @@ -939,8 +940,9 @@ qcow2_alloc_host_offset(BlockDriverState *bs, uint64_t offset, int coroutine_fn GRAPH_RDLOCK qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, uint64_t offset, int compressed_size, uint64_t *host_offset); -void qcow2_parse_compressed_l2_entry(BlockDriverState *bs, uint64_t l2_entry, - uint64_t *coffset, int *csize); +void GRAPH_RDLOCK +qcow2_parse_compressed_l2_entry(BlockDriverState *bs, uint64_t l2_entry, + uint64_t *coffset, int *csize); int coroutine_fn GRAPH_RDLOCK qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m); @@ -993,8 +995,9 @@ qcow2_check_fix_snapshot_table(BlockDriverState *bs, BdrvCheckResult *result, BdrvCheckMode fix); /* qcow2-cache.c functions */ -Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables, - unsigned table_size); +Qcow2Cache * GRAPH_RDLOCK +qcow2_cache_create(BlockDriverState *bs, int num_tables, unsigned table_size); + int qcow2_cache_destroy(Qcow2Cache *c); void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table); @@ -1020,17 +1023,24 @@ void *qcow2_cache_is_table_offset(Qcow2Cache *c, uint64_t offset); void qcow2_cache_discard(Qcow2Cache *c, void *table); /* qcow2-bitmap.c functions */ -int coroutine_fn +int coroutine_fn GRAPH_RDLOCK qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res, void **refcount_table, int64_t *refcount_table_size); + bool coroutine_fn GRAPH_RDLOCK -qcow2_load_dirty_bitmaps(BlockDriverState *bs, bool *header_updated, Error **errp); -bool qcow2_get_bitmap_info_list(BlockDriverState *bs, - Qcow2BitmapInfoList **info_list, Error **errp); +qcow2_load_dirty_bitmaps(BlockDriverState *bs, bool *header_updated, + Error **errp); + +bool GRAPH_RDLOCK +qcow2_get_bitmap_info_list(BlockDriverState *bs, + Qcow2BitmapInfoList **info_list, Error **errp); + int GRAPH_RDLOCK qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp); int GRAPH_RDLOCK qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp); -int coroutine_fn qcow2_truncate_bitmaps_check(BlockDriverState *bs, Error **errp); + +int coroutine_fn GRAPH_RDLOCK +qcow2_truncate_bitmaps_check(BlockDriverState *bs, Error **errp); bool GRAPH_RDLOCK qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, bool release_stored, From 65ff757df04a541ae6a34c51267e54244627efef Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 27 Oct 2023 17:53:31 +0200 Subject: [PATCH 910/974] vhdx: Take locks for accessing bs->file This updates the vhdx code to add GRAPH_RDLOCK annotations for all places that read bs->file. Signed-off-by: Kevin Wolf Message-ID: <20231027155333.420094-23-kwolf@redhat.com> Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- block/vhdx-log.c | 40 ++++++++++++++++++++++------------------ block/vhdx.c | 18 +++++++++++------- block/vhdx.h | 9 ++++++--- 3 files changed, 39 insertions(+), 28 deletions(-) diff --git a/block/vhdx-log.c b/block/vhdx-log.c index d8ed651b70..4385a2d4f6 100644 --- a/block/vhdx-log.c +++ b/block/vhdx-log.c @@ -55,8 +55,9 @@ static const MSGUID zero_guid = { 0 }; /* Allow peeking at the hdr entry at the beginning of the current * read index, without advancing the read index */ -static int vhdx_log_peek_hdr(BlockDriverState *bs, VHDXLogEntries *log, - VHDXLogEntryHeader *hdr) +static int GRAPH_RDLOCK +vhdx_log_peek_hdr(BlockDriverState *bs, VHDXLogEntries *log, + VHDXLogEntryHeader *hdr) { int ret = 0; uint64_t offset; @@ -107,7 +108,7 @@ static int vhdx_log_inc_idx(uint32_t idx, uint64_t length) /* Reset the log to empty */ -static void vhdx_log_reset(BlockDriverState *bs, BDRVVHDXState *s) +static void GRAPH_RDLOCK vhdx_log_reset(BlockDriverState *bs, BDRVVHDXState *s) { MSGUID guid = { 0 }; s->log.read = s->log.write = 0; @@ -127,9 +128,10 @@ static void vhdx_log_reset(BlockDriverState *bs, BDRVVHDXState *s) * not modified. * * 0 is returned on success, -errno otherwise. */ -static int vhdx_log_read_sectors(BlockDriverState *bs, VHDXLogEntries *log, - uint32_t *sectors_read, void *buffer, - uint32_t num_sectors, bool peek) +static int GRAPH_RDLOCK +vhdx_log_read_sectors(BlockDriverState *bs, VHDXLogEntries *log, + uint32_t *sectors_read, void *buffer, + uint32_t num_sectors, bool peek) { int ret = 0; uint64_t offset; @@ -333,9 +335,9 @@ static int vhdx_compute_desc_sectors(uint32_t desc_cnt) * will allocate all the space for buffer, which must be NULL when * passed into this function. Each descriptor will also be validated, * and error returned if any are invalid. */ -static int vhdx_log_read_desc(BlockDriverState *bs, BDRVVHDXState *s, - VHDXLogEntries *log, VHDXLogDescEntries **buffer, - bool convert_endian) +static int GRAPH_RDLOCK +vhdx_log_read_desc(BlockDriverState *bs, BDRVVHDXState *s, VHDXLogEntries *log, + VHDXLogDescEntries **buffer, bool convert_endian) { int ret = 0; uint32_t desc_sectors; @@ -412,8 +414,9 @@ exit: * For a zero descriptor, it may describe multiple sectors to fill with zeroes. * In this case, it should be noted that zeroes are written to disk, and the * image file is not extended as a sparse file. */ -static int vhdx_log_flush_desc(BlockDriverState *bs, VHDXLogDescriptor *desc, - VHDXLogDataSector *data) +static int GRAPH_RDLOCK +vhdx_log_flush_desc(BlockDriverState *bs, VHDXLogDescriptor *desc, + VHDXLogDataSector *data) { int ret = 0; uint64_t seq, file_offset; @@ -484,8 +487,8 @@ exit: * file, and then set the log to 'empty' status once complete. * * The log entries should be validate prior to flushing */ -static int vhdx_log_flush(BlockDriverState *bs, BDRVVHDXState *s, - VHDXLogSequence *logs) +static int GRAPH_RDLOCK +vhdx_log_flush(BlockDriverState *bs, BDRVVHDXState *s, VHDXLogSequence *logs) { int ret = 0; int i; @@ -584,9 +587,10 @@ exit: return ret; } -static int vhdx_validate_log_entry(BlockDriverState *bs, BDRVVHDXState *s, - VHDXLogEntries *log, uint64_t seq, - bool *valid, VHDXLogEntryHeader *entry) +static int GRAPH_RDLOCK +vhdx_validate_log_entry(BlockDriverState *bs, BDRVVHDXState *s, + VHDXLogEntries *log, uint64_t seq, + bool *valid, VHDXLogEntryHeader *entry) { int ret = 0; VHDXLogEntryHeader hdr; @@ -663,8 +667,8 @@ free_and_exit: /* Search through the log circular buffer, and find the valid, active * log sequence, if any exists * */ -static int vhdx_log_search(BlockDriverState *bs, BDRVVHDXState *s, - VHDXLogSequence *logs) +static int GRAPH_RDLOCK +vhdx_log_search(BlockDriverState *bs, BDRVVHDXState *s, VHDXLogSequence *logs) { int ret = 0; uint32_t tail; diff --git a/block/vhdx.c b/block/vhdx.c index ac4d52e8c6..5aa1a13506 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -353,8 +353,9 @@ exit: * * - non-current header is updated with largest sequence number */ -static int vhdx_update_header(BlockDriverState *bs, BDRVVHDXState *s, - bool generate_data_write_guid, MSGUID *log_guid) +static int GRAPH_RDLOCK +vhdx_update_header(BlockDriverState *bs, BDRVVHDXState *s, + bool generate_data_write_guid, MSGUID *log_guid) { int ret = 0; int hdr_idx = 0; @@ -416,8 +417,8 @@ int vhdx_update_headers(BlockDriverState *bs, BDRVVHDXState *s, } /* opens the specified header block from the VHDX file header section */ -static void vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s, - Error **errp) +static void GRAPH_RDLOCK +vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s, Error **errp) { int ret; VHDXHeader *header1; @@ -517,7 +518,8 @@ exit: } -static int vhdx_open_region_tables(BlockDriverState *bs, BDRVVHDXState *s) +static int GRAPH_RDLOCK +vhdx_open_region_tables(BlockDriverState *bs, BDRVVHDXState *s) { int ret = 0; uint8_t *buffer; @@ -634,7 +636,8 @@ fail: * Also, if the File Parameters indicate this is a differencing file, * we must also look for the Parent Locator metadata item. */ -static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s) +static int GRAPH_RDLOCK +vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s) { int ret = 0; uint8_t *buffer; @@ -885,7 +888,8 @@ static void vhdx_calc_bat_entries(BDRVVHDXState *s) } -static int vhdx_check_bat_entries(BlockDriverState *bs, int *errcnt) +static int coroutine_mixed_fn GRAPH_RDLOCK +vhdx_check_bat_entries(BlockDriverState *bs, int *errcnt) { BDRVVHDXState *s = bs->opaque; int64_t image_file_size = bdrv_getlength(bs->file->bs); diff --git a/block/vhdx.h b/block/vhdx.h index 85594a5380..c6dd4d6040 100644 --- a/block/vhdx.h +++ b/block/vhdx.h @@ -401,8 +401,9 @@ typedef struct BDRVVHDXState { void vhdx_guid_generate(MSGUID *guid); -int vhdx_update_headers(BlockDriverState *bs, BDRVVHDXState *s, bool rw, - MSGUID *log_guid); +int GRAPH_RDLOCK +vhdx_update_headers(BlockDriverState *bs, BDRVVHDXState *s, bool rw, + MSGUID *log_guid); uint32_t vhdx_update_checksum(uint8_t *buf, size_t size, int crc_offset); uint32_t vhdx_checksum_calc(uint32_t crc, uint8_t *buf, size_t size, @@ -448,6 +449,8 @@ void vhdx_metadata_header_le_import(VHDXMetadataTableHeader *hdr); void vhdx_metadata_header_le_export(VHDXMetadataTableHeader *hdr); void vhdx_metadata_entry_le_import(VHDXMetadataTableEntry *e); void vhdx_metadata_entry_le_export(VHDXMetadataTableEntry *e); -int vhdx_user_visible_write(BlockDriverState *bs, BDRVVHDXState *s); + +int GRAPH_RDLOCK +vhdx_user_visible_write(BlockDriverState *bs, BDRVVHDXState *s); #endif From a4b740db5ee3db0d5b76a6ea9895875763453187 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 27 Oct 2023 17:53:32 +0200 Subject: [PATCH 911/974] block: Take graph lock for most of .bdrv_open Most implementations of .bdrv_open first open their file child (which is an operation that internally takes the write lock and therefore we shouldn't hold the graph lock while calling it), and afterwards many operations that require holding the graph lock, e.g. for accessing bs->file. This changes block drivers that follow this pattern to take the graph lock after opening the child node. Signed-off-by: Kevin Wolf Message-ID: <20231027155333.420094-24-kwolf@redhat.com> Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- block/blkdebug.c | 16 ++++++++++------ block/bochs.c | 4 ++++ block/cloop.c | 4 ++++ block/copy-before-write.c | 2 ++ block/copy-on-read.c | 4 ++-- block/crypto.c | 4 ++++ block/dmg.c | 5 +++++ block/filter-compress.c | 2 ++ block/parallels.c | 4 ++-- block/preallocate.c | 4 ++++ block/qcow.c | 11 +++++++---- block/raw-format.c | 6 ++++-- block/snapshot-access.c | 3 +++ block/throttle.c | 3 +++ block/vdi.c | 4 ++-- block/vpc.c | 4 ++-- 16 files changed, 60 insertions(+), 20 deletions(-) diff --git a/block/blkdebug.c b/block/blkdebug.c index 230efa9e8d..9da8c9eddc 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -508,6 +508,8 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, goto out; } + bdrv_graph_rdlock_main_loop(); + bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | (BDRV_REQ_FUA & bs->file->bs->supported_write_flags); bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | @@ -520,7 +522,7 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, if (s->align && (s->align >= INT_MAX || !is_power_of_2(s->align))) { error_setg(errp, "Cannot meet constraints with align %" PRIu64, s->align); - goto out; + goto out_rdlock; } align = MAX(s->align, bs->file->bs->bl.request_alignment); @@ -530,7 +532,7 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, !QEMU_IS_ALIGNED(s->max_transfer, align))) { error_setg(errp, "Cannot meet constraints with max-transfer %" PRIu64, s->max_transfer); - goto out; + goto out_rdlock; } s->opt_write_zero = qemu_opt_get_size(opts, "opt-write-zero", 0); @@ -539,7 +541,7 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, !QEMU_IS_ALIGNED(s->opt_write_zero, align))) { error_setg(errp, "Cannot meet constraints with opt-write-zero %" PRIu64, s->opt_write_zero); - goto out; + goto out_rdlock; } s->max_write_zero = qemu_opt_get_size(opts, "max-write-zero", 0); @@ -549,7 +551,7 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, MAX(s->opt_write_zero, align)))) { error_setg(errp, "Cannot meet constraints with max-write-zero %" PRIu64, s->max_write_zero); - goto out; + goto out_rdlock; } s->opt_discard = qemu_opt_get_size(opts, "opt-discard", 0); @@ -558,7 +560,7 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, !QEMU_IS_ALIGNED(s->opt_discard, align))) { error_setg(errp, "Cannot meet constraints with opt-discard %" PRIu64, s->opt_discard); - goto out; + goto out_rdlock; } s->max_discard = qemu_opt_get_size(opts, "max-discard", 0); @@ -568,12 +570,14 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, MAX(s->opt_discard, align)))) { error_setg(errp, "Cannot meet constraints with max-discard %" PRIu64, s->max_discard); - goto out; + goto out_rdlock; } bdrv_debug_event(bs, BLKDBG_NONE); ret = 0; +out_rdlock: + bdrv_graph_rdunlock_main_loop(); out: if (ret < 0) { qemu_mutex_destroy(&s->lock); diff --git a/block/bochs.c b/block/bochs.c index 8c659fa9b9..b099fb52fe 100644 --- a/block/bochs.c +++ b/block/bochs.c @@ -105,6 +105,8 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, struct bochs_header bochs; int ret; + GLOBAL_STATE_CODE(); + /* No write support yet */ bdrv_graph_rdlock_main_loop(); ret = bdrv_apply_auto_read_only(bs, NULL, errp); @@ -118,6 +120,8 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, return ret; } + GRAPH_RDLOCK_GUARD_MAINLOOP(); + ret = bdrv_pread(bs->file, 0, sizeof(bochs), &bochs, 0); if (ret < 0) { return ret; diff --git a/block/cloop.c b/block/cloop.c index 773d7918be..443af1444e 100644 --- a/block/cloop.c +++ b/block/cloop.c @@ -67,6 +67,8 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags, uint32_t offsets_size, max_compressed_block_size = 1, i; int ret; + GLOBAL_STATE_CODE(); + bdrv_graph_rdlock_main_loop(); ret = bdrv_apply_auto_read_only(bs, NULL, errp); bdrv_graph_rdunlock_main_loop(); @@ -79,6 +81,8 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags, return ret; } + GRAPH_RDLOCK_GUARD_MAINLOOP(); + /* read header */ ret = bdrv_pread(bs->file, 128, 4, &s->block_size, 0); if (ret < 0) { diff --git a/block/copy-before-write.c b/block/copy-before-write.c index 8193d3a4cd..ad3b73cc4a 100644 --- a/block/copy-before-write.c +++ b/block/copy-before-write.c @@ -433,6 +433,8 @@ static int cbw_open(BlockDriverState *bs, QDict *options, int flags, return -EINVAL; } + GRAPH_RDLOCK_GUARD_MAINLOOP(); + ctx = bdrv_get_aio_context(bs); aio_context_acquire(ctx); diff --git a/block/copy-on-read.c b/block/copy-on-read.c index 6f245b629a..c36f253d16 100644 --- a/block/copy-on-read.c +++ b/block/copy-on-read.c @@ -51,6 +51,8 @@ cor_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) return ret; } + GRAPH_RDLOCK_GUARD_MAINLOOP(); + bs->supported_read_flags = BDRV_REQ_PREFETCH; bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | @@ -61,8 +63,6 @@ cor_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) bs->file->bs->supported_zero_flags); if (bottom_node) { - GRAPH_RDLOCK_GUARD_MAINLOOP(); - bottom_bs = bdrv_find_node(bottom_node); if (!bottom_bs) { error_setg(errp, "Bottom node '%s' not found", bottom_node); diff --git a/block/crypto.c b/block/crypto.c index b3f0233d53..6ee0cac4b6 100644 --- a/block/crypto.c +++ b/block/crypto.c @@ -263,11 +263,15 @@ static int block_crypto_open_generic(QCryptoBlockFormat format, unsigned int cflags = 0; QDict *cryptoopts = NULL; + GLOBAL_STATE_CODE(); + ret = bdrv_open_file_child(NULL, options, "file", bs, errp); if (ret < 0) { return ret; } + GRAPH_RDLOCK_GUARD_MAINLOOP(); + bs->supported_write_flags = BDRV_REQ_FUA & bs->file->bs->supported_write_flags; diff --git a/block/dmg.c b/block/dmg.c index 38ee72bbe5..853ad36a00 100644 --- a/block/dmg.c +++ b/block/dmg.c @@ -452,6 +452,8 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags, int64_t offset; int ret; + GLOBAL_STATE_CODE(); + bdrv_graph_rdlock_main_loop(); ret = bdrv_apply_auto_read_only(bs, NULL, errp); bdrv_graph_rdunlock_main_loop(); @@ -463,6 +465,9 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags, if (ret < 0) { return ret; } + + GRAPH_RDLOCK_GUARD_MAINLOOP(); + /* * NB: if uncompress submodules are absent, * ie block_module_load return value == 0, the function pointers diff --git a/block/filter-compress.c b/block/filter-compress.c index e3fc82f322..9b68a2be64 100644 --- a/block/filter-compress.c +++ b/block/filter-compress.c @@ -36,6 +36,8 @@ static int compress_open(BlockDriverState *bs, QDict *options, int flags, return ret; } + GRAPH_RDLOCK_GUARD_MAINLOOP(); + if (!bs->file->bs->drv || !block_driver_can_compress(bs->file->bs->drv)) { error_setg(errp, "Compression is not supported for underlying format: %s", diff --git a/block/parallels.c b/block/parallels.c index 9b9bd1f993..a04f836cc2 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -1255,6 +1255,8 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, return ret; } + GRAPH_RDLOCK_GUARD_MAINLOOP(); + file_nb_sectors = bdrv_nb_sectors(bs->file->bs); if (file_nb_sectors < 0) { return -EINVAL; @@ -1359,11 +1361,9 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, bitmap_new(DIV_ROUND_UP(s->header_size, s->bat_dirty_block)); /* Disable migration until bdrv_activate method is added */ - bdrv_graph_rdlock_main_loop(); error_setg(&s->migration_blocker, "The Parallels format used by node '%s' " "does not support live migration", bdrv_get_device_or_node_name(bs)); - bdrv_graph_rdunlock_main_loop(); ret = migrate_add_blocker_normal(&s->migration_blocker, errp); if (ret < 0) { diff --git a/block/preallocate.c b/block/preallocate.c index 4e0c891ab2..f302d17d3f 100644 --- a/block/preallocate.c +++ b/block/preallocate.c @@ -143,6 +143,8 @@ static int preallocate_open(BlockDriverState *bs, QDict *options, int flags, BDRVPreallocateState *s = bs->opaque; int ret; + GLOBAL_STATE_CODE(); + /* * s->data_end and friends should be initialized on permission update. * For this to work, mark them invalid. @@ -155,6 +157,8 @@ static int preallocate_open(BlockDriverState *bs, QDict *options, int flags, return ret; } + GRAPH_RDLOCK_GUARD_MAINLOOP(); + if (!preallocate_absorb_opts(&s->opts, options, bs->file->bs, errp)) { return -EINVAL; } diff --git a/block/qcow.c b/block/qcow.c index 1e2835f1f4..c6d0e15f1e 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -124,9 +124,11 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, ret = bdrv_open_file_child(NULL, options, "file", bs, errp); if (ret < 0) { - goto fail; + goto fail_unlocked; } + bdrv_graph_rdlock_main_loop(); + ret = bdrv_pread(bs->file, 0, sizeof(header), &header, 0); if (ret < 0) { goto fail; @@ -301,11 +303,9 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, } /* Disable migration when qcow images are used */ - bdrv_graph_rdlock_main_loop(); error_setg(&s->migration_blocker, "The qcow format used by node '%s' " "does not support live migration", bdrv_get_device_or_node_name(bs)); - bdrv_graph_rdunlock_main_loop(); ret = migrate_add_blocker_normal(&s->migration_blocker, errp); if (ret < 0) { @@ -315,9 +315,12 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, qobject_unref(encryptopts); qapi_free_QCryptoBlockOpenOptions(crypto_opts); qemu_co_mutex_init(&s->lock); + bdrv_graph_rdunlock_main_loop(); return 0; - fail: +fail: + bdrv_graph_rdunlock_main_loop(); +fail_unlocked: g_free(s->l1_table); qemu_vfree(s->l2_cache); g_free(s->cluster_cache); diff --git a/block/raw-format.c b/block/raw-format.c index 2640d54801..66741be954 100644 --- a/block/raw-format.c +++ b/block/raw-format.c @@ -473,6 +473,8 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, BdrvChildRole file_role; int ret; + GLOBAL_STATE_CODE(); + ret = raw_read_options(options, &offset, &has_size, &size, errp); if (ret < 0) { return ret; @@ -490,6 +492,8 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, bdrv_open_child(NULL, options, "file", bs, &child_of_bds, file_role, false, errp); + + GRAPH_RDLOCK_GUARD_MAINLOOP(); if (!bs->file) { return -EINVAL; } @@ -504,9 +508,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, BDRV_REQ_ZERO_WRITE; if (bs->probed && !bdrv_is_read_only(bs)) { - bdrv_graph_rdlock_main_loop(); bdrv_refresh_filename(bs->file->bs); - bdrv_graph_rdunlock_main_loop(); fprintf(stderr, "WARNING: Image format was not specified for '%s' and probing " "guessed raw.\n" diff --git a/block/snapshot-access.c b/block/snapshot-access.c index 7c45739eb1..84d0d13f86 100644 --- a/block/snapshot-access.c +++ b/block/snapshot-access.c @@ -85,6 +85,9 @@ static int snapshot_access_open(BlockDriverState *bs, QDict *options, int flags, bdrv_open_child(NULL, options, "file", bs, &child_of_bds, BDRV_CHILD_DATA | BDRV_CHILD_PRIMARY, false, errp); + + GRAPH_RDLOCK_GUARD_MAINLOOP(); + if (!bs->file) { return -EINVAL; } diff --git a/block/throttle.c b/block/throttle.c index 1098a4ae9a..97972d1f15 100644 --- a/block/throttle.c +++ b/block/throttle.c @@ -84,6 +84,9 @@ static int throttle_open(BlockDriverState *bs, QDict *options, if (ret < 0) { return ret; } + + GRAPH_RDLOCK_GUARD_MAINLOOP(); + bs->supported_write_flags = bs->file->bs->supported_write_flags | BDRV_REQ_WRITE_UNCHANGED; bs->supported_zero_flags = bs->file->bs->supported_zero_flags | diff --git a/block/vdi.c b/block/vdi.c index 84150180ec..3b57becb9f 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -383,6 +383,8 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, return ret; } + GRAPH_RDLOCK_GUARD_MAINLOOP(); + logout("\n"); ret = bdrv_pread(bs->file, 0, sizeof(header), &header, 0); @@ -492,11 +494,9 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, } /* Disable migration when vdi images are used */ - bdrv_graph_rdlock_main_loop(); error_setg(&s->migration_blocker, "The vdi format used by node '%s' " "does not support live migration", bdrv_get_device_or_node_name(bs)); - bdrv_graph_rdunlock_main_loop(); ret = migrate_add_blocker_normal(&s->migration_blocker, errp); if (ret < 0) { diff --git a/block/vpc.c b/block/vpc.c index 483775103c..d95a204612 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -238,6 +238,8 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, return ret; } + GRAPH_RDLOCK_GUARD_MAINLOOP(); + opts = qemu_opts_create(&vpc_runtime_opts, NULL, 0, &error_abort); if (!qemu_opts_absorb_qdict(opts, options, errp)) { ret = -EINVAL; @@ -446,11 +448,9 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, } /* Disable migration when VHD images are used */ - bdrv_graph_rdlock_main_loop(); error_setg(&s->migration_blocker, "The vpc format used by node '%s' " "does not support live migration", bdrv_get_device_or_node_name(bs)); - bdrv_graph_rdunlock_main_loop(); ret = migrate_add_blocker_normal(&s->migration_blocker, errp); if (ret < 0) { From 1f051dcbdf2e4b6f518db731c84e304b2b9d15ce Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 27 Oct 2023 17:53:33 +0200 Subject: [PATCH 912/974] block: Protect bs->file with graph_lock Almost all functions that access bs->file already take the graph lock now. Add locking to the remaining users and finally annotate the struct field itself as protected by the graph lock. Signed-off-by: Kevin Wolf Message-ID: <20231027155333.420094-25-kwolf@redhat.com> Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- block.c | 11 ++++++++--- block/blkreplay.c | 8 +++++++- block/copy-before-write.c | 2 +- block/crypto.c | 6 ++++++ block/dmg.c | 16 ++++++++++------ block/parallels-ext.c | 21 ++++++++++----------- block/parallels.c | 6 ++++-- block/parallels.h | 5 +++-- block/preallocate.c | 19 ++++++++++++++----- block/qed.c | 12 ++++++++++-- block/qed.h | 2 +- block/raw-format.c | 9 ++++++--- block/replication.c | 5 ++++- block/vmdk.c | 14 ++++++++++++-- include/block/block_int-common.h | 2 +- 15 files changed, 97 insertions(+), 41 deletions(-) diff --git a/block.c b/block.c index 4910b95d7d..eac105a504 100644 --- a/block.c +++ b/block.c @@ -1707,12 +1707,14 @@ bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, const char *node_name, return 0; open_failed: bs->drv = NULL; + + bdrv_graph_wrlock(NULL); if (bs->file != NULL) { - bdrv_graph_wrlock(NULL); bdrv_unref_child(bs, bs->file); - bdrv_graph_wrunlock(); assert(!bs->file); } + bdrv_graph_wrunlock(); + g_free(bs->opaque); bs->opaque = NULL; return ret; @@ -1854,9 +1856,12 @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file, Error *local_err = NULL; bool ro; + GLOBAL_STATE_CODE(); + + bdrv_graph_rdlock_main_loop(); assert(bs->file == NULL); assert(options != NULL && bs->options != options); - GLOBAL_STATE_CODE(); + bdrv_graph_rdunlock_main_loop(); opts = qemu_opts_create(&bdrv_runtime_opts, NULL, 0, &error_abort); if (!qemu_opts_absorb_qdict(opts, options, errp)) { diff --git a/block/blkreplay.c b/block/blkreplay.c index 04f53eea41..792d980aa9 100644 --- a/block/blkreplay.c +++ b/block/blkreplay.c @@ -130,7 +130,13 @@ static int coroutine_fn GRAPH_RDLOCK blkreplay_co_flush(BlockDriverState *bs) static int blkreplay_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) { - return bdrv_snapshot_goto(bs->file->bs, snapshot_id, NULL); + BlockDriverState *file_bs; + + bdrv_graph_rdlock_main_loop(); + file_bs = bs->file->bs; + bdrv_graph_rdunlock_main_loop(); + + return bdrv_snapshot_goto(file_bs, snapshot_id, NULL); } static BlockDriver bdrv_blkreplay = { diff --git a/block/copy-before-write.c b/block/copy-before-write.c index ad3b73cc4a..13972879b1 100644 --- a/block/copy-before-write.c +++ b/block/copy-before-write.c @@ -203,7 +203,7 @@ static int coroutine_fn GRAPH_RDLOCK cbw_co_flush(BlockDriverState *bs) * It's guaranteed that guest writes will not interact in the region until * cbw_snapshot_read_unlock() called. */ -static coroutine_fn BlockReq * +static BlockReq * coroutine_fn GRAPH_RDLOCK cbw_snapshot_read_lock(BlockDriverState *bs, int64_t offset, int64_t bytes, int64_t *pnum, BdrvChild **file) { diff --git a/block/crypto.c b/block/crypto.c index 6ee0cac4b6..921933a5e5 100644 --- a/block/crypto.c +++ b/block/crypto.c @@ -65,6 +65,9 @@ static int block_crypto_read_func(QCryptoBlock *block, BlockDriverState *bs = opaque; ssize_t ret; + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + ret = bdrv_pread(bs->file, offset, buflen, buf, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read encryption header"); @@ -83,6 +86,9 @@ static int block_crypto_write_func(QCryptoBlock *block, BlockDriverState *bs = opaque; ssize_t ret; + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + ret = bdrv_pwrite(bs->file, offset, buflen, buf, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Could not write encryption header"); diff --git a/block/dmg.c b/block/dmg.c index 853ad36a00..33dcb3a349 100644 --- a/block/dmg.c +++ b/block/dmg.c @@ -70,7 +70,8 @@ static int dmg_probe(const uint8_t *buf, int buf_size, const char *filename) return 0; } -static int read_uint64(BlockDriverState *bs, int64_t offset, uint64_t *result) +static int GRAPH_RDLOCK +read_uint64(BlockDriverState *bs, int64_t offset, uint64_t *result) { uint64_t buffer; int ret; @@ -84,7 +85,8 @@ static int read_uint64(BlockDriverState *bs, int64_t offset, uint64_t *result) return 0; } -static int read_uint32(BlockDriverState *bs, int64_t offset, uint32_t *result) +static int GRAPH_RDLOCK +read_uint32(BlockDriverState *bs, int64_t offset, uint32_t *result) { uint32_t buffer; int ret; @@ -321,8 +323,9 @@ fail: return ret; } -static int dmg_read_resource_fork(BlockDriverState *bs, DmgHeaderState *ds, - uint64_t info_begin, uint64_t info_length) +static int GRAPH_RDLOCK +dmg_read_resource_fork(BlockDriverState *bs, DmgHeaderState *ds, + uint64_t info_begin, uint64_t info_length) { BDRVDMGState *s = bs->opaque; int ret; @@ -388,8 +391,9 @@ fail: return ret; } -static int dmg_read_plist_xml(BlockDriverState *bs, DmgHeaderState *ds, - uint64_t info_begin, uint64_t info_length) +static int GRAPH_RDLOCK +dmg_read_plist_xml(BlockDriverState *bs, DmgHeaderState *ds, + uint64_t info_begin, uint64_t info_length) { BDRVDMGState *s = bs->opaque; int ret; diff --git a/block/parallels-ext.c b/block/parallels-ext.c index 4d8ecf5047..b4e14c88f2 100644 --- a/block/parallels-ext.c +++ b/block/parallels-ext.c @@ -59,11 +59,10 @@ typedef struct ParallelsDirtyBitmapFeature { } QEMU_PACKED ParallelsDirtyBitmapFeature; /* Given L1 table read bitmap data from the image and populate @bitmap */ -static int parallels_load_bitmap_data(BlockDriverState *bs, - const uint64_t *l1_table, - uint32_t l1_size, - BdrvDirtyBitmap *bitmap, - Error **errp) +static int GRAPH_RDLOCK +parallels_load_bitmap_data(BlockDriverState *bs, const uint64_t *l1_table, + uint32_t l1_size, BdrvDirtyBitmap *bitmap, + Error **errp) { BDRVParallelsState *s = bs->opaque; int ret = 0; @@ -120,10 +119,9 @@ finish: * @data buffer (of @data_size size) is the Dirty bitmaps feature which * consists of ParallelsDirtyBitmapFeature followed by L1 table. */ -static BdrvDirtyBitmap *parallels_load_bitmap(BlockDriverState *bs, - uint8_t *data, - size_t data_size, - Error **errp) +static BdrvDirtyBitmap * GRAPH_RDLOCK +parallels_load_bitmap(BlockDriverState *bs, uint8_t *data, size_t data_size, + Error **errp) { int ret; ParallelsDirtyBitmapFeature bf; @@ -183,8 +181,9 @@ static BdrvDirtyBitmap *parallels_load_bitmap(BlockDriverState *bs, return bitmap; } -static int parallels_parse_format_extension(BlockDriverState *bs, - uint8_t *ext_cluster, Error **errp) +static int GRAPH_RDLOCK +parallels_parse_format_extension(BlockDriverState *bs, uint8_t *ext_cluster, + Error **errp) { BDRVParallelsState *s = bs->opaque; int ret; diff --git a/block/parallels.c b/block/parallels.c index a04f836cc2..9205a0864f 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -200,7 +200,7 @@ static int mark_used(BlockDriverState *bs, unsigned long *bitmap, * bitmap anyway, as much as we can. This information will be used for * error resolution. */ -static int parallels_fill_used_bitmap(BlockDriverState *bs) +static int GRAPH_RDLOCK parallels_fill_used_bitmap(BlockDriverState *bs) { BDRVParallelsState *s = bs->opaque; int64_t payload_bytes; @@ -1185,7 +1185,7 @@ static int parallels_probe(const uint8_t *buf, int buf_size, return 0; } -static int parallels_update_header(BlockDriverState *bs) +static int GRAPH_RDLOCK parallels_update_header(BlockDriverState *bs) { BDRVParallelsState *s = bs->opaque; unsigned size = MAX(bdrv_opt_mem_align(bs->file->bs), @@ -1428,6 +1428,8 @@ static void parallels_close(BlockDriverState *bs) { BDRVParallelsState *s = bs->opaque; + GRAPH_RDLOCK_GUARD_MAINLOOP(); + if ((bs->open_flags & BDRV_O_RDWR) && !(bs->open_flags & BDRV_O_INACTIVE)) { s->header->inuse = 0; parallels_update_header(bs); diff --git a/block/parallels.h b/block/parallels.h index 6b199443cf..423b2ad727 100644 --- a/block/parallels.h +++ b/block/parallels.h @@ -90,7 +90,8 @@ typedef struct BDRVParallelsState { Error *migration_blocker; } BDRVParallelsState; -int parallels_read_format_extension(BlockDriverState *bs, - int64_t ext_off, Error **errp); +int GRAPH_RDLOCK +parallels_read_format_extension(BlockDriverState *bs, int64_t ext_off, + Error **errp); #endif diff --git a/block/preallocate.c b/block/preallocate.c index f302d17d3f..d215bc5d6d 100644 --- a/block/preallocate.c +++ b/block/preallocate.c @@ -173,7 +173,8 @@ static int preallocate_open(BlockDriverState *bs, QDict *options, int flags, return 0; } -static int preallocate_truncate_to_real_size(BlockDriverState *bs, Error **errp) +static int GRAPH_RDLOCK +preallocate_truncate_to_real_size(BlockDriverState *bs, Error **errp) { BDRVPreallocateState *s = bs->opaque; int ret; @@ -204,6 +205,9 @@ static void preallocate_close(BlockDriverState *bs) { BDRVPreallocateState *s = bs->opaque; + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + qemu_bh_cancel(s->drop_resize_bh); qemu_bh_delete(s->drop_resize_bh); @@ -227,6 +231,9 @@ static int preallocate_reopen_prepare(BDRVReopenState *reopen_state, PreallocateOpts *opts = g_new0(PreallocateOpts, 1); int ret; + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + if (!preallocate_absorb_opts(opts, reopen_state->options, reopen_state->bs->file->bs, errp)) { g_free(opts); @@ -287,7 +294,7 @@ static bool can_write_resize(uint64_t perm) return (perm & BLK_PERM_WRITE) && (perm & BLK_PERM_RESIZE); } -static bool has_prealloc_perms(BlockDriverState *bs) +static bool GRAPH_RDLOCK has_prealloc_perms(BlockDriverState *bs) { BDRVPreallocateState *s = bs->opaque; @@ -503,7 +510,8 @@ preallocate_co_getlength(BlockDriverState *bs) return ret; } -static int preallocate_drop_resize(BlockDriverState *bs, Error **errp) +static int GRAPH_RDLOCK +preallocate_drop_resize(BlockDriverState *bs, Error **errp) { BDRVPreallocateState *s = bs->opaque; int ret; @@ -529,15 +537,16 @@ static int preallocate_drop_resize(BlockDriverState *bs, Error **errp) */ s->data_end = s->file_end = s->zero_start = -EINVAL; - bdrv_graph_rdlock_main_loop(); bdrv_child_refresh_perms(bs, bs->file, NULL); - bdrv_graph_rdunlock_main_loop(); return 0; } static void preallocate_drop_resize_bh(void *opaque) { + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + /* * In case of errors, we'll simply keep the exclusive lock on the image * indefinitely. diff --git a/block/qed.c b/block/qed.c index f4c1628a81..bc2f0a61c0 100644 --- a/block/qed.c +++ b/block/qed.c @@ -612,7 +612,7 @@ static int bdrv_qed_reopen_prepare(BDRVReopenState *state, return 0; } -static void bdrv_qed_close(BlockDriverState *bs) +static void GRAPH_RDLOCK bdrv_qed_do_close(BlockDriverState *bs) { BDRVQEDState *s = bs->opaque; @@ -631,6 +631,14 @@ static void bdrv_qed_close(BlockDriverState *bs) qemu_vfree(s->l1_table); } +static void GRAPH_UNLOCKED bdrv_qed_close(BlockDriverState *bs) +{ + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + + bdrv_qed_do_close(bs); +} + static int coroutine_fn GRAPH_UNLOCKED bdrv_qed_co_create(BlockdevCreateOptions *opts, Error **errp) { @@ -1574,7 +1582,7 @@ bdrv_qed_co_invalidate_cache(BlockDriverState *bs, Error **errp) BDRVQEDState *s = bs->opaque; int ret; - bdrv_qed_close(bs); + bdrv_qed_do_close(bs); bdrv_qed_init_state(bs); qemu_co_mutex_lock(&s->table_lock); diff --git a/block/qed.h b/block/qed.h index 988654cb86..26d4bf038c 100644 --- a/block/qed.h +++ b/block/qed.h @@ -185,7 +185,7 @@ enum { /** * Header functions */ -int qed_write_header_sync(BDRVQEDState *s); +int GRAPH_RDLOCK qed_write_header_sync(BDRVQEDState *s); /** * L2 cache functions diff --git a/block/raw-format.c b/block/raw-format.c index 66741be954..1111dffd54 100644 --- a/block/raw-format.c +++ b/block/raw-format.c @@ -95,9 +95,9 @@ end: return ret; } -static int raw_apply_options(BlockDriverState *bs, BDRVRawState *s, - uint64_t offset, bool has_size, uint64_t size, - Error **errp) +static int GRAPH_RDLOCK +raw_apply_options(BlockDriverState *bs, BDRVRawState *s, uint64_t offset, + bool has_size, uint64_t size, Error **errp) { int64_t real_size = 0; @@ -145,6 +145,9 @@ static int raw_reopen_prepare(BDRVReopenState *reopen_state, uint64_t offset, size; int ret; + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + assert(reopen_state != NULL); assert(reopen_state->bs != NULL); diff --git a/block/replication.c b/block/replication.c index 49ecc608b2..43e259444b 100644 --- a/block/replication.c +++ b/block/replication.c @@ -311,7 +311,7 @@ static void GRAPH_UNLOCKED secondary_do_checkpoint(BlockDriverState *bs, Error **errp) { BDRVReplicationState *s = bs->opaque; - BdrvChild *active_disk = bs->file; + BdrvChild *active_disk; Error *local_err = NULL; int ret; @@ -328,6 +328,7 @@ secondary_do_checkpoint(BlockDriverState *bs, Error **errp) return; } + active_disk = bs->file; if (!active_disk->bs->drv) { error_setg(errp, "Active disk %s is ejected", active_disk->bs->node_name); @@ -755,11 +756,13 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp) return; } + bdrv_graph_rdlock_main_loop(); s->stage = BLOCK_REPLICATION_FAILOVER; s->commit_job = commit_active_start( NULL, bs->file->bs, s->secondary_disk->bs, JOB_INTERNAL, 0, BLOCKDEV_ON_ERROR_REPORT, NULL, replication_done, bs, true, errp); + bdrv_graph_rdunlock_main_loop(); break; default: aio_context_release(aio_context); diff --git a/block/vmdk.c b/block/vmdk.c index 5c789bb65b..dda783f06b 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -300,7 +300,8 @@ static void vmdk_free_last_extent(BlockDriverState *bs) } /* Return -ve errno, or 0 on success and write CID into *pcid. */ -static int vmdk_read_cid(BlockDriverState *bs, int parent, uint32_t *pcid) +static int GRAPH_RDLOCK +vmdk_read_cid(BlockDriverState *bs, int parent, uint32_t *pcid) { char *desc; uint32_t cid; @@ -415,6 +416,9 @@ static int vmdk_reopen_prepare(BDRVReopenState *state, BDRVVmdkReopenState *rs; int i; + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + assert(state != NULL); assert(state->bs != NULL); assert(state->opaque == NULL); @@ -451,6 +455,9 @@ static void vmdk_reopen_commit(BDRVReopenState *state) BDRVVmdkReopenState *rs = state->opaque; int i; + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + for (i = 0; i < s->num_extents; i++) { if (rs->extents_using_bs_file[i]) { s->extents[i].file = state->bs->file; @@ -465,7 +472,7 @@ static void vmdk_reopen_abort(BDRVReopenState *state) vmdk_reopen_clean(state); } -static int vmdk_parent_open(BlockDriverState *bs) +static int GRAPH_RDLOCK vmdk_parent_open(BlockDriverState *bs) { char *p_name; char *desc; @@ -2547,7 +2554,10 @@ vmdk_co_do_create(int64_t size, ret = -EINVAL; goto exit; } + + bdrv_graph_co_rdlock(); ret = vmdk_read_cid(blk_bs(backing), 0, &parent_cid); + bdrv_graph_co_rdunlock(); blk_co_unref(backing); if (ret) { error_setg(errp, "Failed to read parent CID"); diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h index 63bc523d7c..4e31d161c5 100644 --- a/include/block/block_int-common.h +++ b/include/block/block_int-common.h @@ -1181,7 +1181,7 @@ struct BlockDriverState { */ QLIST_HEAD(, BdrvChild GRAPH_RDLOCK_PTR) children; BdrvChild * GRAPH_RDLOCK_PTR backing; - BdrvChild *file; + BdrvChild * GRAPH_RDLOCK_PTR file; QLIST_HEAD(, BdrvChild GRAPH_RDLOCK_PTR) parents; From b523a3d54f3d031a54cd0931cc5d855608e63140 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 11 Oct 2023 15:12:20 +0200 Subject: [PATCH 913/974] hw/ide/ahci: trigger either error IRQ or regular IRQ, not both MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to AHCI 1.3.1, 5.3.8.1 RegFIS:Entry, if ERR_STAT is set, we jump to state ERR:FatalTaskfile, which will raise a TFES IRQ unconditionally, regardless if the I bit is set in the FIS or not. Thus, we should never raise a normal IRQ after having sent an error IRQ. NOTE: for QEMU platforms that use SeaBIOS, this patch depends on QEMU commit 784155cdcb02 ("seabios: update submodule to git snapshot"), and QEMU commit 14f5a7bae4cb ("seabios: update binaries to git snapshot"), which update SeaBIOS to a version that contains SeaBIOS commit 1281e340 ("ahci: handle TFES irq correctly"). Signed-off-by: Niklas Cassel Message-ID: <20231011131220.1992064-1-nks@flawful.org> Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- hw/ide/ahci.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index fcc5476e9e..7676e2d871 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -897,11 +897,10 @@ static bool ahci_write_fis_d2h(AHCIDevice *ad, bool d2h_fis_i) pr->tfdata = (ad->port.ifs[0].error << 8) | ad->port.ifs[0].status; + /* TFES IRQ is always raised if ERR_STAT is set, regardless of I bit. */ if (d2h_fis[2] & ERR_STAT) { ahci_trigger_irq(ad->hba, ad, AHCI_PORT_IRQ_BIT_TFES); - } - - if (d2h_fis_i) { + } else if (d2h_fis_i) { ahci_trigger_irq(ad->hba, ad, AHCI_PORT_IRQ_BIT_DHRS); } From 4d96307c5b4fac40c6ca25f38318b4b65d315de0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 8 Nov 2023 14:56:49 +0400 Subject: [PATCH 914/974] tracetool: avoid invalid escape in Python string MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is an error in Python 3.12; fix it by using a raw string literal. Cc: Signed-off-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Stefan Hajnoczi Message-ID: <20231108105649.60453-1-marcandre.lureau@redhat.com> --- scripts/tracetool/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py index b29594d75e..b887540a55 100644 --- a/scripts/tracetool/__init__.py +++ b/scripts/tracetool/__init__.py @@ -91,7 +91,7 @@ ALLOWED_TYPES = [ def validate_type(name): bits = name.split(" ") for bit in bits: - bit = re.sub("\*", "", bit) + bit = re.sub(r"\*", "", bit) if bit == "": continue if bit == "const": From 33f0c061287fcb8c4ae97802374a6208778684a4 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 9 Nov 2023 18:42:30 +0100 Subject: [PATCH 915/974] hw/i386/pc: Use qdev_prop_set_array() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of manually setting "foo-len" and "foo[i]" properties, build a QList and use the new qdev_prop_set_array() helper to set the whole array property with a single call. Signed-off-by: Kevin Wolf Reviewed-by: Peter Maydell Message-ID: <20231109174240.72376-2-kwolf@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Kevin Wolf --- hw/i386/pc.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 188bc9d0f8..29b9964733 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -44,6 +44,7 @@ #include "sysemu/reset.h" #include "kvm/kvm_i386.h" #include "hw/xen/xen.h" +#include "qapi/qmp/qlist.h" #include "qemu/error-report.h" #include "hw/acpi/cpu_hotplug.h" #include "acpi-build.h" @@ -1457,10 +1458,11 @@ static void pc_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, /* Declare the APIC range as the reserved MSI region */ char *resv_prop_str = g_strdup_printf("0xfee00000:0xfeefffff:%d", VIRTIO_IOMMU_RESV_MEM_T_MSI); + QList *reserved_regions = qlist_new(); + + qlist_append_str(reserved_regions, resv_prop_str); + qdev_prop_set_array(dev, "reserved-regions", reserved_regions); - object_property_set_uint(OBJECT(dev), "len-reserved-regions", 1, errp); - object_property_set_str(OBJECT(dev), "reserved-regions[0]", - resv_prop_str, errp); g_free(resv_prop_str); } From 31805a0aa46d8806fcdac7e1473251f40ca5b993 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 9 Nov 2023 18:42:31 +0100 Subject: [PATCH 916/974] hw/arm/mps2-tz: Use qdev_prop_set_array() Instead of manually setting "foo-len" and "foo[i]" properties, build a QList and use the new qdev_prop_set_array() helper to set the whole array property with a single call. Signed-off-by: Kevin Wolf Reviewed-by: Peter Maydell Message-ID: <20231109174240.72376-3-kwolf@redhat.com> Signed-off-by: Kevin Wolf --- hw/arm/mps2-tz.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index eae3639da2..668db5ed61 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -48,6 +48,7 @@ #include "qemu/units.h" #include "qemu/cutils.h" #include "qapi/error.h" +#include "qapi/qmp/qlist.h" #include "qemu/error-report.h" #include "hw/arm/boot.h" #include "hw/arm/armv7m.h" @@ -461,6 +462,7 @@ static MemoryRegion *make_scc(MPS2TZMachineState *mms, void *opaque, MPS2SCC *scc = opaque; DeviceState *sccdev; MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_GET_CLASS(mms); + QList *oscclk; uint32_t i; object_initialize_child(OBJECT(mms), "scc", scc, TYPE_MPS2_SCC); @@ -469,11 +471,13 @@ static MemoryRegion *make_scc(MPS2TZMachineState *mms, void *opaque, qdev_prop_set_uint32(sccdev, "scc-cfg4", 0x2); qdev_prop_set_uint32(sccdev, "scc-aid", 0x00200008); qdev_prop_set_uint32(sccdev, "scc-id", mmc->scc_id); - qdev_prop_set_uint32(sccdev, "len-oscclk", mmc->len_oscclk); + + oscclk = qlist_new(); for (i = 0; i < mmc->len_oscclk; i++) { - g_autofree char *propname = g_strdup_printf("oscclk[%u]", i); - qdev_prop_set_uint32(sccdev, propname, mmc->oscclk[i]); + qlist_append_int(oscclk, mmc->oscclk[i]); } + qdev_prop_set_array(sccdev, "oscclk", oscclk); + sysbus_realize(SYS_BUS_DEVICE(scc), &error_fatal); return sysbus_mmio_get_region(SYS_BUS_DEVICE(sccdev), 0); } From 80e09151c202d5a5b740cf1b25ea59fac342f879 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 9 Nov 2023 18:42:32 +0100 Subject: [PATCH 917/974] hw/arm/mps2: Use qdev_prop_set_array() Instead of manually setting "foo-len" and "foo[i]" properties, build a QList and use the new qdev_prop_set_array() helper to set the whole array property with a single call. Signed-off-by: Kevin Wolf Reviewed-by: Peter Maydell Message-ID: <20231109174240.72376-4-kwolf@redhat.com> Signed-off-by: Kevin Wolf --- hw/arm/mps2.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/hw/arm/mps2.c b/hw/arm/mps2.c index d92fd60684..292a180ad2 100644 --- a/hw/arm/mps2.c +++ b/hw/arm/mps2.c @@ -48,6 +48,7 @@ #include "net/net.h" #include "hw/watchdog/cmsdk-apb-watchdog.h" #include "hw/qdev-clock.h" +#include "qapi/qmp/qlist.h" #include "qom/object.h" typedef enum MPS2FPGAType { @@ -138,6 +139,7 @@ static void mps2_common_init(MachineState *machine) MemoryRegion *system_memory = get_system_memory(); MachineClass *mc = MACHINE_GET_CLASS(machine); DeviceState *armv7m, *sccdev; + QList *oscclk; int i; if (strcmp(machine->cpu_type, mc->default_cpu_type) != 0) { @@ -402,10 +404,12 @@ static void mps2_common_init(MachineState *machine) qdev_prop_set_uint32(sccdev, "scc-aid", 0x00200008); qdev_prop_set_uint32(sccdev, "scc-id", mmc->scc_id); /* All these FPGA images have the same OSCCLK configuration */ - qdev_prop_set_uint32(sccdev, "len-oscclk", 3); - qdev_prop_set_uint32(sccdev, "oscclk[0]", 50000000); - qdev_prop_set_uint32(sccdev, "oscclk[1]", 24576000); - qdev_prop_set_uint32(sccdev, "oscclk[2]", 25000000); + oscclk = qlist_new(); + qlist_append_int(oscclk, 50000000); + qlist_append_int(oscclk, 24576000); + qlist_append_int(oscclk, 25000000); + qdev_prop_set_array(sccdev, "oscclk", oscclk); + sysbus_realize(SYS_BUS_DEVICE(&mms->scc), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(sccdev), 0, 0x4002f000); object_initialize_child(OBJECT(mms), "fpgaio", From d210fa2f055bc1528cfb32c1b644cebbe36ff220 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 9 Nov 2023 18:42:33 +0100 Subject: [PATCH 918/974] hw/arm/sbsa-ref: Use qdev_prop_set_array() Instead of manually setting "foo-len" and "foo[i]" properties, build a QList and use the new qdev_prop_set_array() helper to set the whole array property with a single call. Signed-off-by: Kevin Wolf Reviewed-by: Peter Maydell Message-ID: <20231109174240.72376-5-kwolf@redhat.com> Signed-off-by: Kevin Wolf --- hw/arm/sbsa-ref.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c index bce44690e5..f3c9704693 100644 --- a/hw/arm/sbsa-ref.c +++ b/hw/arm/sbsa-ref.c @@ -48,6 +48,7 @@ #include "hw/char/pl011.h" #include "hw/watchdog/sbsa_gwdt.h" #include "net/net.h" +#include "qapi/qmp/qlist.h" #include "qom/object.h" #define RAMLIMIT_GB 8192 @@ -437,6 +438,7 @@ static void create_gic(SBSAMachineState *sms, MemoryRegion *mem) SysBusDevice *gicbusdev; const char *gictype; uint32_t redist0_capacity, redist0_count; + QList *redist_region_count; int i; gictype = gicv3_class_name(); @@ -455,8 +457,9 @@ static void create_gic(SBSAMachineState *sms, MemoryRegion *mem) sbsa_ref_memmap[SBSA_GIC_REDIST].size / GICV3_REDIST_SIZE; redist0_count = MIN(smp_cpus, redist0_capacity); - qdev_prop_set_uint32(sms->gic, "len-redist-region-count", 1); - qdev_prop_set_uint32(sms->gic, "redist-region-count[0]", redist0_count); + redist_region_count = qlist_new(); + qlist_append_int(redist_region_count, redist0_count); + qdev_prop_set_array(sms->gic, "redist-region-count", redist_region_count); object_property_set_link(OBJECT(sms->gic), "sysmem", OBJECT(mem), &error_fatal); From 50ab8648c0ae513ca882b07571837b41668655fd Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 9 Nov 2023 18:42:34 +0100 Subject: [PATCH 919/974] hw/arm/vexpress: Use qdev_prop_set_array() Instead of manually setting "foo-len" and "foo[i]" properties, build a QList and use the new qdev_prop_set_array() helper to set the whole array property with a single call. Signed-off-by: Kevin Wolf Reviewed-by: Peter Maydell Message-ID: <20231109174240.72376-6-kwolf@redhat.com> Signed-off-by: Kevin Wolf --- hw/arm/vexpress.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index c08ea34e92..fd981f4c33 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -43,6 +43,7 @@ #include "hw/cpu/a15mpcore.h" #include "hw/i2c/arm_sbcon_i2c.h" #include "hw/sd/sd.h" +#include "qapi/qmp/qlist.h" #include "qom/object.h" #include "audio/audio.h" @@ -544,6 +545,7 @@ static void vexpress_common_init(MachineState *machine) ram_addr_t vram_size, sram_size; MemoryRegion *sysmem = get_system_memory(); const hwaddr *map = daughterboard->motherboard_map; + QList *db_voltage, *db_clock; int i; daughterboard->init(vms, machine->ram_size, machine->cpu_type, pic); @@ -584,20 +586,19 @@ static void vexpress_common_init(MachineState *machine) sysctl = qdev_new("realview_sysctl"); qdev_prop_set_uint32(sysctl, "sys_id", sys_id); qdev_prop_set_uint32(sysctl, "proc_id", daughterboard->proc_id); - qdev_prop_set_uint32(sysctl, "len-db-voltage", - daughterboard->num_voltage_sensors); + + db_voltage = qlist_new(); for (i = 0; i < daughterboard->num_voltage_sensors; i++) { - char *propname = g_strdup_printf("db-voltage[%d]", i); - qdev_prop_set_uint32(sysctl, propname, daughterboard->voltages[i]); - g_free(propname); + qlist_append_int(db_voltage, daughterboard->voltages[i]); } - qdev_prop_set_uint32(sysctl, "len-db-clock", - daughterboard->num_clocks); + qdev_prop_set_array(sysctl, "db-voltage", db_voltage); + + db_clock = qlist_new(); for (i = 0; i < daughterboard->num_clocks; i++) { - char *propname = g_strdup_printf("db-clock[%d]", i); - qdev_prop_set_uint32(sysctl, propname, daughterboard->clocks[i]); - g_free(propname); + qlist_append_int(db_clock, daughterboard->clocks[i]); } + qdev_prop_set_array(sysctl, "db-clock", db_clock); + sysbus_realize_and_unref(SYS_BUS_DEVICE(sysctl), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(sysctl), 0, map[VE_SYSREGS]); From 3c86b9dadc378db207d36f6f9c988d6886c8a2e3 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 9 Nov 2023 18:42:35 +0100 Subject: [PATCH 920/974] hw/arm/virt: Use qdev_prop_set_array() Instead of manually setting "foo-len" and "foo[i]" properties, build a QList and use the new qdev_prop_set_array() helper to set the whole array property with a single call. Signed-off-by: Kevin Wolf Reviewed-by: Peter Maydell Message-ID: <20231109174240.72376-7-kwolf@redhat.com> Signed-off-by: Kevin Wolf --- hw/arm/virt.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 0a16ab3095..85e3c5ba9d 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -69,6 +69,7 @@ #include "hw/firmware/smbios.h" #include "qapi/visitor.h" #include "qapi/qapi-visit-common.h" +#include "qapi/qmp/qlist.h" #include "standard-headers/linux/input.h" #include "hw/arm/smmuv3.h" #include "hw/acpi/acpi.h" @@ -752,14 +753,23 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) } if (vms->gic_version != VIRT_GIC_VERSION_2) { + QList *redist_region_count; uint32_t redist0_capacity = virt_redist_capacity(vms, VIRT_GIC_REDIST); uint32_t redist0_count = MIN(smp_cpus, redist0_capacity); nb_redist_regions = virt_gicv3_redist_region_count(vms); - qdev_prop_set_uint32(vms->gic, "len-redist-region-count", - nb_redist_regions); - qdev_prop_set_uint32(vms->gic, "redist-region-count[0]", redist0_count); + redist_region_count = qlist_new(); + qlist_append_int(redist_region_count, redist0_count); + if (nb_redist_regions == 2) { + uint32_t redist1_capacity = + virt_redist_capacity(vms, VIRT_HIGH_GIC_REDIST2); + + qlist_append_int(redist_region_count, + MIN(smp_cpus - redist0_count, redist1_capacity)); + } + qdev_prop_set_array(vms->gic, "redist-region-count", + redist_region_count); if (!kvm_irqchip_in_kernel()) { if (vms->tcg_its) { @@ -768,14 +778,6 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) qdev_prop_set_bit(vms->gic, "has-lpi", true); } } - - if (nb_redist_regions == 2) { - uint32_t redist1_capacity = - virt_redist_capacity(vms, VIRT_HIGH_GIC_REDIST2); - - qdev_prop_set_uint32(vms->gic, "redist-region-count[1]", - MIN(smp_cpus - redist0_count, redist1_capacity)); - } } else { if (!kvm_irqchip_in_kernel()) { qdev_prop_set_bit(vms->gic, "has-virtualization-extensions", @@ -2748,6 +2750,7 @@ static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, virtio_md_pci_pre_plug(VIRTIO_MD_PCI(dev), MACHINE(hotplug_dev), errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) { hwaddr db_start = 0, db_end = 0; + QList *reserved_regions; char *resv_prop_str; if (vms->iommu != VIRT_IOMMU_NONE) { @@ -2774,9 +2777,9 @@ static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, db_start, db_end, VIRTIO_IOMMU_RESV_MEM_T_MSI); - object_property_set_uint(OBJECT(dev), "len-reserved-regions", 1, errp); - object_property_set_str(OBJECT(dev), "reserved-regions[0]", - resv_prop_str, errp); + reserved_regions = qlist_new(); + qlist_append_str(reserved_regions, resv_prop_str); + qdev_prop_set_array(dev, "reserved-regions", reserved_regions); g_free(resv_prop_str); } } From 2394c782a9040891429d0f160cf695bf00379a0d Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 9 Nov 2023 18:42:36 +0100 Subject: [PATCH 921/974] hw/arm/xlnx-versal: Use qdev_prop_set_array() Instead of manually setting "foo-len" and "foo[i]" properties, build a QList and use the new qdev_prop_set_array() helper to set the whole array property with a single call. Signed-off-by: Kevin Wolf Reviewed-by: Peter Maydell Message-ID: <20231109174240.72376-8-kwolf@redhat.com> Signed-off-by: Kevin Wolf --- hw/arm/xlnx-versal.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index 4f74a64a0d..9600551c44 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -12,6 +12,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qapi/error.h" +#include "qapi/qmp/qlist.h" #include "qemu/module.h" #include "hw/sysbus.h" #include "net/net.h" @@ -69,6 +70,7 @@ static void versal_create_apu_gic(Versal *s, qemu_irq *pic) }; SysBusDevice *gicbusdev; DeviceState *gicdev; + QList *redist_region_count; int nr_apu_cpus = ARRAY_SIZE(s->fpd.apu.cpu); int i; @@ -79,8 +81,11 @@ static void versal_create_apu_gic(Versal *s, qemu_irq *pic) qdev_prop_set_uint32(gicdev, "revision", 3); qdev_prop_set_uint32(gicdev, "num-cpu", nr_apu_cpus); qdev_prop_set_uint32(gicdev, "num-irq", XLNX_VERSAL_NR_IRQS + 32); - qdev_prop_set_uint32(gicdev, "len-redist-region-count", 1); - qdev_prop_set_uint32(gicdev, "redist-region-count[0]", nr_apu_cpus); + + redist_region_count = qlist_new(); + qlist_append_int(redist_region_count, nr_apu_cpus); + qdev_prop_set_array(gicdev, "redist-region-count", redist_region_count); + qdev_prop_set_bit(gicdev, "has-security-extensions", true); sysbus_realize(SYS_BUS_DEVICE(&s->fpd.apu.gic), &error_fatal); From 670581f932d9415e843de8a5e31e041938248237 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 9 Nov 2023 18:42:37 +0100 Subject: [PATCH 922/974] hw/rx/rx62n: Use qdev_prop_set_array() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of manually setting "foo-len" and "foo[i]" properties, build a QList and use the new qdev_prop_set_array() helper to set the whole array property with a single call. Signed-off-by: Kevin Wolf Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20231109174240.72376-9-kwolf@redhat.com> Signed-off-by: Kevin Wolf --- hw/rx/rx62n.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/hw/rx/rx62n.c b/hw/rx/rx62n.c index d00fcb0ef0..4dc44afd9d 100644 --- a/hw/rx/rx62n.c +++ b/hw/rx/rx62n.c @@ -28,6 +28,7 @@ #include "hw/sysbus.h" #include "hw/qdev-properties.h" #include "sysemu/sysemu.h" +#include "qapi/qmp/qlist.h" #include "qom/object.h" /* @@ -130,22 +131,22 @@ static void register_icu(RX62NState *s) { int i; SysBusDevice *icu; + QList *ipr_map, *trigger_level; object_initialize_child(OBJECT(s), "icu", &s->icu, TYPE_RX_ICU); icu = SYS_BUS_DEVICE(&s->icu); - qdev_prop_set_uint32(DEVICE(icu), "len-ipr-map", NR_IRQS); + + ipr_map = qlist_new(); for (i = 0; i < NR_IRQS; i++) { - char propname[32]; - snprintf(propname, sizeof(propname), "ipr-map[%d]", i); - qdev_prop_set_uint32(DEVICE(icu), propname, ipr_table[i]); + qlist_append_int(ipr_map, ipr_table[i]); } - qdev_prop_set_uint32(DEVICE(icu), "len-trigger-level", - ARRAY_SIZE(levelirq)); + qdev_prop_set_array(DEVICE(icu), "ipr-map", ipr_map); + + trigger_level = qlist_new(); for (i = 0; i < ARRAY_SIZE(levelirq); i++) { - char propname[32]; - snprintf(propname, sizeof(propname), "trigger-level[%d]", i); - qdev_prop_set_uint32(DEVICE(icu), propname, levelirq[i]); + qlist_append_int(trigger_level, levelirq[i]); } + qdev_prop_set_array(DEVICE(icu), "trigger-level", trigger_level); for (i = 0; i < NR_IRQS; i++) { s->irq[i] = qdev_get_gpio_in(DEVICE(icu), i); From 125062e791258c68109f3a59cb7aca3dadbdb5a3 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 9 Nov 2023 18:42:38 +0100 Subject: [PATCH 923/974] qom: Add object_property_set_default_list() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function provides a default for properties that are accessed using the list visitor interface. The default is always an empty list. Signed-off-by: Kevin Wolf Reviewed-by: Peter Maydell Message-ID: <20231109174240.72376-10-kwolf@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Kevin Wolf --- include/qom/object.h | 8 ++++++++ qom/object.c | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/include/qom/object.h b/include/qom/object.h index ef7258a5e1..afccd24ca7 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -1093,6 +1093,14 @@ void object_property_set_default_bool(ObjectProperty *prop, bool value); */ void object_property_set_default_str(ObjectProperty *prop, const char *value); +/** + * object_property_set_default_list: + * @prop: the property to set + * + * Set the property default value to be an empty list. + */ +void object_property_set_default_list(ObjectProperty *prop); + /** * object_property_set_default_int: * @prop: the property to set diff --git a/qom/object.c b/qom/object.c index 8557fe8e4e..95c0dc8285 100644 --- a/qom/object.c +++ b/qom/object.c @@ -31,6 +31,7 @@ * of the QOM core on QObject? */ #include "qom/qom-qobject.h" #include "qapi/qmp/qbool.h" +#include "qapi/qmp/qlist.h" #include "qapi/qmp/qnum.h" #include "qapi/qmp/qstring.h" #include "qemu/error-report.h" @@ -1588,6 +1589,11 @@ void object_property_set_default_str(ObjectProperty *prop, const char *value) object_property_set_default(prop, QOBJECT(qstring_from_str(value))); } +void object_property_set_default_list(ObjectProperty *prop) +{ + object_property_set_default(prop, QOBJECT(qlist_new())); +} + void object_property_set_default_int(ObjectProperty *prop, int64_t value) { object_property_set_default(prop, QOBJECT(qnum_from_int(value))); From 3257b854d81ca3ebe6f14375e83a0ed2db3c7562 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 9 Nov 2023 18:42:39 +0100 Subject: [PATCH 924/974] qdev: Make netdev properties work as list elements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'name' parameter of QOM setters is primarily used to specify the name of the currently parsed input element in the visitor interface. For top-level qdev properties, this is always set and matches 'prop->name'. However, for list elements it is NULL, because each element of a list doesn't have a separate name. Passing a non-NULL value runs into assertion failures in the visitor code. Therefore, using 'name' in error messages is not right for property types that are used in lists, because "(null)" (or even a segfault) isn't very helpful to identify what QEMU is complaining about. Change netdev properties to use 'prop->name' instead, which will contain the name of the array property after switching array properties to lists in the external interface. (This is still not perfect, as it doesn't identify which element in the list caused the error, but strictly better than before.) Signed-off-by: Kevin Wolf Message-ID: <20231109174240.72376-11-kwolf@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Kevin Wolf --- hw/core/qdev-properties-system.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index b46d16cd2c..1473ab3d5e 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -450,7 +450,7 @@ static void set_netdev(Object *obj, Visitor *v, const char *name, peers_ptr->queues = queues; out: - error_set_from_qdev_prop_error(errp, err, obj, name, str); + error_set_from_qdev_prop_error(errp, err, obj, prop->name, str); g_free(str); } From b06f8b500da2a5a73dfb15de17cd96ad2385fdc7 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 9 Nov 2023 18:42:40 +0100 Subject: [PATCH 925/974] qdev: Rework array properties based on list visitor Until now, array properties are actually implemented with a hack that uses multiple properties on the QOM level: a static "foo-len" property and after it is set, dynamically created "foo[i]" properties. In external interfaces (-device on the command line and device_add in QMP), this interface was broken by commit f3558b1b ('qdev: Base object creation on QDict rather than QemuOpts') because QDicts are unordered and therefore it could happen that QEMU tried to set the indexed properties before setting the length, which fails and effectively makes array properties inaccessible. In particular, this affects the 'ports' property of the 'rocker' device, which used to be configured like this: -device rocker,len-ports=2,ports[0]=dev0,ports[1]=dev1 This patch reworks the external interface so that instead of using a separate top-level property for the length and for each element, we use a single true array property that accepts a list value. In the external interfaces, this is naturally expressed as a JSON list and makes array properties accessible again. The new syntax looks like this: -device '{"driver":"rocker","ports":["dev0","dev1"]}' Creating an array property on the command line without using JSON format is currently not possible. This could be fixed by switching from QemuOpts to a keyval parser, which however requires consideration of the compatibility implications. All internal users of devices with array properties go through qdev_prop_set_array() at this point, so updating it takes care of all of them. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1090 Fixes: f3558b1b763683bb877f7dd5b282469cdadc65c3 Signed-off-by: Kevin Wolf Message-ID: <20231109174240.72376-12-kwolf@redhat.com> Signed-off-by: Kevin Wolf --- hw/core/qdev-properties.c | 241 +++++++++++++++++++++++------------ include/hw/qdev-properties.h | 35 +++-- 2 files changed, 174 insertions(+), 102 deletions(-) diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 950ef48e01..91632f7be9 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -546,98 +546,187 @@ const PropertyInfo qdev_prop_size32 = { /* --- support for array properties --- */ -/* Used as an opaque for the object properties we add for each - * array element. Note that the struct Property must be first - * in the struct so that a pointer to this works as the opaque - * for the underlying element's property hooks as well as for - * our own release callback. - */ -typedef struct { - struct Property prop; - char *propname; - ObjectPropertyRelease *release; -} ArrayElementProperty; +typedef struct ArrayElementList ArrayElementList; -/* object property release callback for array element properties: - * we call the underlying element's property release hook, and - * then free the memory we allocated when we added the property. +struct ArrayElementList { + ArrayElementList *next; + void *value; +}; + +/* + * Given an array property @parent_prop in @obj, return a Property for a + * specific element of the array. Arrays are backed by an uint32_t length field + * and an element array. @elem points at an element in this element array. */ -static void array_element_release(Object *obj, const char *name, void *opaque) +static Property array_elem_prop(Object *obj, Property *parent_prop, + const char *name, char *elem) { - ArrayElementProperty *p = opaque; - if (p->release) { - p->release(obj, name, opaque); - } - g_free(p->propname); - g_free(p); + return (Property) { + .info = parent_prop->arrayinfo, + .name = name, + /* + * This ugly piece of pointer arithmetic sets up the offset so + * that when the underlying release hook calls qdev_get_prop_ptr + * they get the right answer despite the array element not actually + * being inside the device struct. + */ + .offset = (uintptr_t)elem - (uintptr_t)obj, + }; } -static void set_prop_arraylen(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) +/* + * Object property release callback for array properties: We call the + * underlying element's property release hook for each element. + * + * Note that it is the responsibility of the individual device's deinit + * to free the array proper. + */ +static void release_prop_array(Object *obj, const char *name, void *opaque) { - /* Setter for the property which defines the length of a - * variable-sized property array. As well as actually setting the - * array-length field in the device struct, we have to create the - * array itself and dynamically add the corresponding properties. - */ Property *prop = opaque; uint32_t *alenptr = object_field_prop_ptr(obj, prop); void **arrayptr = (void *)obj + prop->arrayoffset; - void *eltptr; - const char *arrayname; + char *elem = *arrayptr; int i; + if (!prop->arrayinfo->release) { + return; + } + + for (i = 0; i < *alenptr; i++) { + Property elem_prop = array_elem_prop(obj, prop, name, elem); + prop->arrayinfo->release(obj, NULL, &elem_prop); + elem += prop->arrayfieldsize; + } +} + +/* + * Setter for an array property. This sets both the array length (which + * is technically the property field in the object) and the array itself + * (a pointer to which is stored in the additional field described by + * prop->arrayoffset). + */ +static void set_prop_array(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + ERRP_GUARD(); + Property *prop = opaque; + uint32_t *alenptr = object_field_prop_ptr(obj, prop); + void **arrayptr = (void *)obj + prop->arrayoffset; + ArrayElementList *list, *elem, *next; + const size_t size = sizeof(*list); + char *elemptr; + bool ok = true; + if (*alenptr) { error_setg(errp, "array size property %s may not be set more than once", name); return; } - if (!visit_type_uint32(v, name, alenptr, errp)) { - return; - } - if (!*alenptr) { + + if (!visit_start_list(v, name, (GenericList **) &list, size, errp)) { return; } - /* DEFINE_PROP_ARRAY guarantees that name should start with this prefix; - * strip it off so we can get the name of the array itself. - */ - assert(strncmp(name, PROP_ARRAY_LEN_PREFIX, - strlen(PROP_ARRAY_LEN_PREFIX)) == 0); - arrayname = name + strlen(PROP_ARRAY_LEN_PREFIX); + /* Read the whole input into a temporary list */ + elem = list; + while (elem) { + Property elem_prop; - /* Note that it is the responsibility of the individual device's deinit - * to free the array proper. + elem->value = g_malloc0(prop->arrayfieldsize); + elem_prop = array_elem_prop(obj, prop, name, elem->value); + prop->arrayinfo->set(obj, v, NULL, &elem_prop, errp); + if (*errp) { + ok = false; + goto out_obj; + } + if (*alenptr == INT_MAX) { + error_setg(errp, "array is too big"); + return; + } + (*alenptr)++; + elem = (ArrayElementList *) visit_next_list(v, (GenericList*) elem, + size); + } + + ok = visit_check_list(v, errp); +out_obj: + visit_end_list(v, (void**) &list); + + if (!ok) { + for (elem = list; elem; elem = next) { + Property elem_prop = array_elem_prop(obj, prop, name, + elem->value); + if (prop->arrayinfo->release) { + prop->arrayinfo->release(obj, NULL, &elem_prop); + } + next = elem->next; + g_free(elem->value); + g_free(elem); + } + return; + } + + /* + * Now that we know how big the array has to be, move the data over to a + * linear array and free the temporary list. */ - *arrayptr = eltptr = g_malloc0(*alenptr * prop->arrayfieldsize); - for (i = 0; i < *alenptr; i++, eltptr += prop->arrayfieldsize) { - char *propname = g_strdup_printf("%s[%d]", arrayname, i); - ArrayElementProperty *arrayprop = g_new0(ArrayElementProperty, 1); - arrayprop->release = prop->arrayinfo->release; - arrayprop->propname = propname; - arrayprop->prop.info = prop->arrayinfo; - arrayprop->prop.name = propname; - /* This ugly piece of pointer arithmetic sets up the offset so - * that when the underlying get/set hooks call qdev_get_prop_ptr - * they get the right answer despite the array element not actually - * being inside the device struct. - */ - arrayprop->prop.offset = eltptr - (void *)obj; - assert(object_field_prop_ptr(obj, &arrayprop->prop) == eltptr); - object_property_add(obj, propname, - arrayprop->prop.info->name, - field_prop_getter(arrayprop->prop.info), - field_prop_setter(arrayprop->prop.info), - array_element_release, - arrayprop); + *arrayptr = g_malloc_n(*alenptr, prop->arrayfieldsize); + elemptr = *arrayptr; + for (elem = list; elem; elem = next) { + memcpy(elemptr, elem->value, prop->arrayfieldsize); + elemptr += prop->arrayfieldsize; + next = elem->next; + g_free(elem->value); + g_free(elem); } } -const PropertyInfo qdev_prop_arraylen = { - .name = "uint32", - .get = get_uint32, - .set = set_prop_arraylen, - .set_default_value = qdev_propinfo_set_default_value_uint, +static void get_prop_array(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + ERRP_GUARD(); + Property *prop = opaque; + uint32_t *alenptr = object_field_prop_ptr(obj, prop); + void **arrayptr = (void *)obj + prop->arrayoffset; + char *elem = *arrayptr; + GenericList *list; + const size_t list_elem_size = sizeof(*list) + prop->arrayfieldsize; + int i; + bool ok; + + if (!visit_start_list(v, name, &list, list_elem_size, errp)) { + return; + } + + for (i = 0; i < *alenptr; i++) { + Property elem_prop = array_elem_prop(obj, prop, name, elem); + prop->arrayinfo->get(obj, v, NULL, &elem_prop, errp); + if (*errp) { + goto out_obj; + } + elem += prop->arrayfieldsize; + } + + /* visit_check_list() can only fail for input visitors */ + ok = visit_check_list(v, errp); + assert(ok); + +out_obj: + visit_end_list(v, (void**) &list); +} + +static void default_prop_array(ObjectProperty *op, const Property *prop) +{ + object_property_set_default_list(op); +} + +const PropertyInfo qdev_prop_array = { + .name = "list", + .get = get_prop_array, + .set = set_prop_array, + .release = release_prop_array, + .set_default_value = default_prop_array, }; /* --- public helpers --- */ @@ -743,20 +832,8 @@ void qdev_prop_set_enum(DeviceState *dev, const char *name, int value) void qdev_prop_set_array(DeviceState *dev, const char *name, QList *values) { - const QListEntry *entry; - g_autofree char *prop_len = g_strdup_printf("len-%s", name); - uint32_t i = 0; - - object_property_set_int(OBJECT(dev), prop_len, qlist_size(values), - &error_abort); - - QLIST_FOREACH_ENTRY(values, entry) { - g_autofree char *prop_idx = g_strdup_printf("%s[%u]", name, i); - object_property_set_qobject(OBJECT(dev), prop_idx, entry->value, - &error_abort); - i++; - } - + object_property_set_qobject(OBJECT(dev), name, QOBJECT(values), + &error_abort); qobject_unref(values); } diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h index 7fa2fdb7c9..25743a29a0 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -61,7 +61,7 @@ extern const PropertyInfo qdev_prop_size; extern const PropertyInfo qdev_prop_string; extern const PropertyInfo qdev_prop_on_off_auto; extern const PropertyInfo qdev_prop_size32; -extern const PropertyInfo qdev_prop_arraylen; +extern const PropertyInfo qdev_prop_array; extern const PropertyInfo qdev_prop_link; #define DEFINE_PROP(_name, _state, _field, _prop, _type, ...) { \ @@ -115,8 +115,6 @@ extern const PropertyInfo qdev_prop_link; .bitmask = (_bitmask), \ .set_default = false) -#define PROP_ARRAY_LEN_PREFIX "len-" - /** * DEFINE_PROP_ARRAY: * @_name: name of the array @@ -127,28 +125,25 @@ extern const PropertyInfo qdev_prop_link; * @_arrayprop: PropertyInfo defining what property the array elements have * @_arraytype: C type of the array elements * - * Define device properties for a variable-length array _name. A - * static property "len-arrayname" is defined. When the device creator - * sets this property to the desired length of array, further dynamic - * properties "arrayname[0]", "arrayname[1]", ... are defined so the - * device creator can set the array element values. Setting the - * "len-arrayname" property more than once is an error. + * Define device properties for a variable-length array _name. The array is + * represented as a list in the visitor interface. * - * When the array length is set, the @_field member of the device + * @_arraytype is required to be movable with memcpy(). + * + * When the array property is set, the @_field member of the device * struct is set to the array length, and @_arrayfield is set to point - * to (zero-initialised) memory allocated for the array. For a zero - * length array, @_field will be set to 0 and @_arrayfield to NULL. + * to the memory allocated for the array. + * * It is the responsibility of the device deinit code to free the * @_arrayfield memory. */ -#define DEFINE_PROP_ARRAY(_name, _state, _field, \ - _arrayfield, _arrayprop, _arraytype) \ - DEFINE_PROP((PROP_ARRAY_LEN_PREFIX _name), \ - _state, _field, qdev_prop_arraylen, uint32_t, \ - .set_default = true, \ - .defval.u = 0, \ - .arrayinfo = &(_arrayprop), \ - .arrayfieldsize = sizeof(_arraytype), \ +#define DEFINE_PROP_ARRAY(_name, _state, _field, \ + _arrayfield, _arrayprop, _arraytype) \ + DEFINE_PROP(_name, _state, _field, qdev_prop_array, uint32_t, \ + .set_default = true, \ + .defval.u = 0, \ + .arrayinfo = &(_arrayprop), \ + .arrayfieldsize = sizeof(_arraytype), \ .arrayoffset = offsetof(_state, _arrayfield)) #define DEFINE_PROP_LINK(_name, _state, _field, _type, _ptr_type) \ From 881d1073d0f83d9a07e5ea3ff444e1bef9679a7c Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Tue, 7 Nov 2023 22:32:36 +0100 Subject: [PATCH 926/974] target/hppa: Mask reserved PSW bits in expand_sm_imm The system mask is a restricted subset of the psw, with only a couple of reserved bits. It is better to handle this up front in the translator than require helper_swap_system_mask to use cpu_hppa_get_psw and cpu_hppa_put_psw. Signed-off-by: Helge Deller [rth: Handle this in expand_sm_imm not helper_swap_system_mask.] Signed-off-by: Richard Henderson --- target/hppa/translate.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index bcce65d587..f3b17ba16d 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -77,11 +77,14 @@ typedef struct DisasContext { /* Note that ssm/rsm instructions number PSW_W and PSW_E differently. */ static int expand_sm_imm(DisasContext *ctx, int val) { - if (val & PSW_SM_E) { - val = (val & ~PSW_SM_E) | PSW_E; - } - if (val & PSW_SM_W) { - val = (val & ~PSW_SM_W) | PSW_W; + /* Keep unimplemented bits disabled -- see cpu_hppa_put_psw. */ + if (ctx->is_pa20) { + if (val & PSW_SM_W) { + val |= PSW_W; + } + val &= ~(PSW_SM_W | PSW_SM_E | PSW_G); + } else { + val &= ~(PSW_SM_W | PSW_SM_E | PSW_O); } return val; } From 6ab4f1c9e2def22f766ed36d903484b67a2fd95b Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 23 Oct 2023 19:50:38 +0200 Subject: [PATCH 927/974] block/snapshot: Fix compiler warning with -Wshadow=local No need to declare a new variable in the the inner code block here, we can re-use the "ret" variable that has been declared at the beginning of the function. With this change, the code can now be successfully compiled with -Wshadow=local again. Signed-off-by: Thomas Huth Message-ID: <20231023175038.111607-1-thuth@redhat.com> Reviewed-by: Markus Armbruster [Commit message tweaked] Signed-off-by: Markus Armbruster --- block/snapshot.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/block/snapshot.c b/block/snapshot.c index 6e16eb803a..55974273ae 100644 --- a/block/snapshot.c +++ b/block/snapshot.c @@ -629,7 +629,6 @@ int bdrv_all_goto_snapshot(const char *name, while (iterbdrvs) { BlockDriverState *bs = iterbdrvs->data; AioContext *ctx = bdrv_get_aio_context(bs); - int ret = 0; bool all_snapshots_includes_bs; aio_context_acquire(ctx); @@ -637,9 +636,8 @@ int bdrv_all_goto_snapshot(const char *name, all_snapshots_includes_bs = bdrv_all_snapshots_includes_bs(bs); bdrv_graph_rdunlock_main_loop(); - if (devices || all_snapshots_includes_bs) { - ret = bdrv_snapshot_goto(bs, name, errp); - } + ret = (devices || all_snapshots_includes_bs) ? + bdrv_snapshot_goto(bs, name, errp) : 0; aio_context_release(ctx); if (ret < 0) { bdrv_graph_rdlock_main_loop(); From fe73674af1e80ad7375e627563042395a1a746b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 23 Oct 2023 17:45:06 +0200 Subject: [PATCH 928/974] igb: Add a VF reset handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Export the igb_vf_reset() helper routine from the PF model to let the IGBVF model implement its own device reset. Cc: Akihiko Odaki Suggested-by: Sriram Yagnaraman Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Cédric Le Goater Signed-off-by: Jason Wang --- hw/net/igb.c | 6 ++++++ hw/net/igb_common.h | 1 + hw/net/igb_core.c | 6 ++++-- hw/net/igb_core.h | 3 +++ hw/net/igbvf.c | 10 ++++++++++ hw/net/trace-events | 1 + 6 files changed, 25 insertions(+), 2 deletions(-) diff --git a/hw/net/igb.c b/hw/net/igb.c index 8ff832acfc..e70a66ee03 100644 --- a/hw/net/igb.c +++ b/hw/net/igb.c @@ -122,6 +122,12 @@ igb_mmio_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) igb_core_write(&s->core, addr, val, size); } +void igb_vf_reset(void *opaque, uint16_t vfn) +{ + IGBState *s = opaque; + igb_core_vf_reset(&s->core, vfn); +} + static bool igb_io_get_reg_index(IGBState *s, uint32_t *idx) { diff --git a/hw/net/igb_common.h b/hw/net/igb_common.h index 5c261ba9d3..b316a5bcfa 100644 --- a/hw/net/igb_common.h +++ b/hw/net/igb_common.h @@ -152,5 +152,6 @@ enum { uint64_t igb_mmio_read(void *opaque, hwaddr addr, unsigned size); void igb_mmio_write(void *opaque, hwaddr addr, uint64_t val, unsigned size); +void igb_vf_reset(void *opaque, uint16_t vfn); #endif diff --git a/hw/net/igb_core.c b/hw/net/igb_core.c index f6a5e2327b..2a7a11aa9e 100644 --- a/hw/net/igb_core.c +++ b/hw/net/igb_core.c @@ -2477,11 +2477,13 @@ static void igb_set_vfmailbox(IGBCore *core, int index, uint32_t val) } } -static void igb_vf_reset(IGBCore *core, uint16_t vfn) +void igb_core_vf_reset(IGBCore *core, uint16_t vfn) { uint16_t qn0 = vfn; uint16_t qn1 = vfn + IGB_NUM_VM_POOLS; + trace_igb_core_vf_reset(vfn); + /* disable Rx and Tx for the VF*/ core->mac[RXDCTL0 + (qn0 * 16)] &= ~E1000_RXDCTL_QUEUE_ENABLE; core->mac[RXDCTL0 + (qn1 * 16)] &= ~E1000_RXDCTL_QUEUE_ENABLE; @@ -2560,7 +2562,7 @@ static void igb_set_vtctrl(IGBCore *core, int index, uint32_t val) if (val & E1000_CTRL_RST) { vfn = (index - PVTCTRL0) / 0x40; - igb_vf_reset(core, vfn); + igb_core_vf_reset(core, vfn); } } diff --git a/hw/net/igb_core.h b/hw/net/igb_core.h index 9cbbfd516b..bf8c46f26b 100644 --- a/hw/net/igb_core.h +++ b/hw/net/igb_core.h @@ -130,6 +130,9 @@ igb_core_set_link_status(IGBCore *core); void igb_core_pci_uninit(IGBCore *core); +void +igb_core_vf_reset(IGBCore *core, uint16_t vfn); + bool igb_can_receive(IGBCore *core); diff --git a/hw/net/igbvf.c b/hw/net/igbvf.c index d55e1e8a6a..07343fa14a 100644 --- a/hw/net/igbvf.c +++ b/hw/net/igbvf.c @@ -273,6 +273,13 @@ static void igbvf_pci_realize(PCIDevice *dev, Error **errp) pcie_ari_init(dev, 0x150); } +static void igbvf_qdev_reset_hold(Object *obj) +{ + PCIDevice *vf = PCI_DEVICE(obj); + + igb_vf_reset(pcie_sriov_get_pf(vf), pcie_sriov_vf_number(vf)); +} + static void igbvf_pci_uninit(PCIDevice *dev) { IgbVfState *s = IGBVF(dev); @@ -287,6 +294,7 @@ static void igbvf_class_init(ObjectClass *class, void *data) { DeviceClass *dc = DEVICE_CLASS(class); PCIDeviceClass *c = PCI_DEVICE_CLASS(class); + ResettableClass *rc = RESETTABLE_CLASS(class); c->realize = igbvf_pci_realize; c->exit = igbvf_pci_uninit; @@ -295,6 +303,8 @@ static void igbvf_class_init(ObjectClass *class, void *data) c->revision = 1; c->class_id = PCI_CLASS_NETWORK_ETHERNET; + rc->phases.hold = igbvf_qdev_reset_hold; + dc->desc = "Intel 82576 Virtual Function"; dc->user_creatable = false; diff --git a/hw/net/trace-events b/hw/net/trace-events index 3097742cc0..387e32e153 100644 --- a/hw/net/trace-events +++ b/hw/net/trace-events @@ -274,6 +274,7 @@ igb_core_mdic_read(uint32_t addr, uint32_t data) "MDIC READ: PHY[%u] = 0x%x" igb_core_mdic_read_unhandled(uint32_t addr) "MDIC READ: PHY[%u] UNHANDLED" igb_core_mdic_write(uint32_t addr, uint32_t data) "MDIC WRITE: PHY[%u] = 0x%x" igb_core_mdic_write_unhandled(uint32_t addr) "MDIC WRITE: PHY[%u] UNHANDLED" +igb_core_vf_reset(uint16_t vfn) "VF%d" igb_link_set_ext_params(bool asd_check, bool speed_select_bypass, bool pfrstd) "Set extended link params: ASD check: %d, Speed select bypass: %d, PF reset done: %d" From d90014fc337ab77f37285b1a30fd4f545056be0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 23 Oct 2023 17:45:07 +0200 Subject: [PATCH 929/974] igb: Add Function Level Reset to PF and VF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Intel 82576EB GbE Controller say that the Physical and Virtual Functions support Function Level Reset. Add the capability to the PF device model using device property "x-pcie-flr-init" which is "on" by default and "off" for machines <= 8.1 to preserve compatibility. The FLR capability of the VF model is defined according to the FLR property of the PF, this to avoid adding an extra compatibility property. Cc: Sriram Yagnaraman Fixes: 3a977deebe6b ("Intrdocue igb device emulation") Reviewed-by: Akihiko Odaki Tested-by: Akihiko Odaki Signed-off-by: Cédric Le Goater Signed-off-by: Jason Wang --- hw/core/machine.c | 3 ++- hw/net/igb.c | 9 +++++++++ hw/net/igbvf.c | 9 +++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index 50edaab737..0c17398141 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -35,7 +35,8 @@ GlobalProperty hw_compat_8_1[] = { { TYPE_PCI_BRIDGE, "x-pci-express-writeable-slt-bug", "true" }, { "ramfb", "x-migrate", "off" }, - { "vfio-pci-nohotplug", "x-ramfb-migrate", "off" } + { "vfio-pci-nohotplug", "x-ramfb-migrate", "off" }, + { "igb", "x-pcie-flr-init", "off" }, }; const size_t hw_compat_8_1_len = G_N_ELEMENTS(hw_compat_8_1); diff --git a/hw/net/igb.c b/hw/net/igb.c index e70a66ee03..dfb722b695 100644 --- a/hw/net/igb.c +++ b/hw/net/igb.c @@ -78,6 +78,7 @@ struct IGBState { uint32_t ioaddr; IGBCore core; + bool has_flr; }; #define IGB_CAP_SRIOV_OFFSET (0x160) @@ -101,6 +102,9 @@ static void igb_write_config(PCIDevice *dev, uint32_t addr, trace_igb_write_config(addr, val, len); pci_default_write_config(dev, addr, val, len); + if (s->has_flr) { + pcie_cap_flr_write_config(dev, addr, val, len); + } if (range_covers_byte(addr, len, PCI_COMMAND) && (dev->config[PCI_COMMAND] & PCI_COMMAND_MASTER)) { @@ -433,6 +437,10 @@ static void igb_pci_realize(PCIDevice *pci_dev, Error **errp) } /* PCIe extended capabilities (in order) */ + if (s->has_flr) { + pcie_cap_flr_init(pci_dev); + } + if (pcie_aer_init(pci_dev, 1, 0x100, 0x40, errp) < 0) { hw_error("Failed to initialize AER capability"); } @@ -588,6 +596,7 @@ static const VMStateDescription igb_vmstate = { static Property igb_properties[] = { DEFINE_NIC_PROPERTIES(IGBState, conf), + DEFINE_PROP_BOOL("x-pcie-flr-init", IGBState, has_flr, true), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/net/igbvf.c b/hw/net/igbvf.c index 07343fa14a..94a4e885f2 100644 --- a/hw/net/igbvf.c +++ b/hw/net/igbvf.c @@ -204,6 +204,10 @@ static void igbvf_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, { trace_igbvf_write_config(addr, val, len); pci_default_write_config(dev, addr, val, len); + if (object_property_get_bool(OBJECT(pcie_sriov_get_pf(dev)), + "x-pcie-flr-init", &error_abort)) { + pcie_cap_flr_write_config(dev, addr, val, len); + } } static uint64_t igbvf_mmio_read(void *opaque, hwaddr addr, unsigned size) @@ -266,6 +270,11 @@ static void igbvf_pci_realize(PCIDevice *dev, Error **errp) hw_error("Failed to initialize PCIe capability"); } + if (object_property_get_bool(OBJECT(pcie_sriov_get_pf(dev)), + "x-pcie-flr-init", &error_abort)) { + pcie_cap_flr_init(dev); + } + if (pcie_aer_init(dev, 1, 0x100, 0x40, errp) < 0) { hw_error("Failed to initialize AER capability"); } From 364eff6885a79869a074852d628dfa7a137ba492 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 23 Oct 2023 13:13:41 +0200 Subject: [PATCH 930/974] virtio-mem: fix division by zero in virtio_mem_activate_memslots_to_plug() When running with "dynamic-memslots=off", we enter virtio_mem_activate_memslots_to_plug() to return immediately again because "vmem->dynamic_memslots == false". However, the compiler might not optimize out calculating start_idx+end_idx, where we divide by vmem->memslot_size. In such a configuration, the memslot size is 0 and we'll get a division by zero: (qemu) qom-set vmem0 requested-size 3G (qemu) q35.sh: line 38: 622940 Floating point exception(core dumped) The same is true for virtio_mem_deactivate_unplugged_memslots(), however we never really reach that code without a prior virtio_mem_activate_memslots_to_plug() call. Let's fix it by simply calling these functions only with "dynamic-memslots=on". This was found when using a debug build of QEMU. Message-ID: <20231023111341.219317-1-david@redhat.com> Reprted-by: Mario Casquero Fixes: 177f9b1ee464 ("virtio-mem: Expose device memory dynamically via multiple memslots if enabled") Reviewed-by: Michael S. Tsirkin Reviewed-by: Maciej S. Szmigiero Tested-by: Mario Casquero Signed-off-by: David Hildenbrand --- hw/virtio/virtio-mem.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index a5ea3be414..75ee38aa46 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -525,9 +525,7 @@ static void virtio_mem_activate_memslots_to_plug(VirtIOMEM *vmem, vmem->memslot_size; unsigned int idx; - if (!vmem->dynamic_memslots) { - return; - } + assert(vmem->dynamic_memslots); /* Activate all involved memslots in a single transaction. */ memory_region_transaction_begin(); @@ -547,9 +545,7 @@ static void virtio_mem_deactivate_unplugged_memslots(VirtIOMEM *vmem, vmem->memslot_size; unsigned int idx; - if (!vmem->dynamic_memslots) { - return; - } + assert(vmem->dynamic_memslots); /* Deactivate all memslots with unplugged blocks in a single transaction. */ memory_region_transaction_begin(); @@ -598,7 +594,9 @@ static int virtio_mem_set_block_state(VirtIOMEM *vmem, uint64_t start_gpa, virtio_mem_notify_unplug(vmem, offset, size); virtio_mem_set_range_unplugged(vmem, start_gpa, size); /* Deactivate completely unplugged memslots after updating the state. */ - virtio_mem_deactivate_unplugged_memslots(vmem, offset, size); + if (vmem->dynamic_memslots) { + virtio_mem_deactivate_unplugged_memslots(vmem, offset, size); + } return 0; } @@ -635,9 +633,11 @@ static int virtio_mem_set_block_state(VirtIOMEM *vmem, uint64_t start_gpa, * blocks we are plugging here. The following notification will inform * registered listeners about the blocks we're plugging. */ - virtio_mem_activate_memslots_to_plug(vmem, offset, size); + if (vmem->dynamic_memslots) { + virtio_mem_activate_memslots_to_plug(vmem, offset, size); + } ret = virtio_mem_notify_plug(vmem, offset, size); - if (ret) { + if (ret && vmem->dynamic_memslots) { virtio_mem_deactivate_unplugged_memslots(vmem, offset, size); } } @@ -749,7 +749,9 @@ static int virtio_mem_unplug_all(VirtIOMEM *vmem) notifier_list_notify(&vmem->size_change_notifiers, &vmem->size); /* Deactivate all memslots after updating the state. */ - virtio_mem_deactivate_unplugged_memslots(vmem, 0, region_size); + if (vmem->dynamic_memslots) { + virtio_mem_deactivate_unplugged_memslots(vmem, 0, region_size); + } } trace_virtio_mem_unplugged_all(); From 569205e4e92f802cf409fe03dd2ac41dd0b54aae Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 26 Oct 2023 07:31:15 +0200 Subject: [PATCH 931/974] meson: Enable -Wshadow=local MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Local variables shadowing other local variables or parameters make the code needlessly hard to understand. Bugs love to hide in such code. Evidence: commit bbde656263d (migration/rdma: Fix save_page method to fail on polling error). Enable -Wshadow=local to prevent such issues. Possible thanks to recent cleanups. Enabling -Wshadow would prevent more issues, but we're not yet ready for that. As usual, the warning is only enabled when the compiler recognizes it. GCC does, Clang doesn't. Some shadowed locals remain in bsd-user. Since BSD prefers Clang, let's not wait for its cleanup. Signed-off-by: Markus Armbruster Message-ID: <20231026053115.2066744-2-armbru@redhat.com> Reviewed-by: Thomas Huth Tested-by: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé --- meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/meson.build b/meson.build index d7d841e71e..ec01f8b138 100644 --- a/meson.build +++ b/meson.build @@ -462,6 +462,7 @@ warn_flags = [ '-Wno-tautological-type-limit-compare', '-Wno-psabi', '-Wno-gnu-variable-sized-type-not-at-end', + '-Wshadow=local', ] if targetos != 'darwin' From 6d133eef98bed96ea11c73ad3cd15f21815da993 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 24 Oct 2023 12:48:41 +0200 Subject: [PATCH 932/974] qapi: Fix QAPISchemaEntity.__repr__() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I messed it up on merge. It's a debugging aid, so no impact on build. Fixes: e307a8174bb8 (qapi: provide a friendly string representation of QAPI classes) Signed-off-by: Markus Armbruster Message-ID: <20231024104841.1569250-1-armbru@redhat.com> Reviewed-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé --- scripts/qapi/schema.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index d739e558e9..6a836950a9 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -76,7 +76,8 @@ class QAPISchemaEntity: def __repr__(self): if self.name is None: return "<%s at 0x%x>" % (type(self).__name__, id(self)) - return "<%s:%s at 0x%x>" % type(self).__name__, self.name, id(self) + return "<%s:%s at 0x%x>" % (type(self).__name__, self.name, + id(self)) def c_name(self): return c_name(self.name) From c375f05ef5a358f1dd19b45bb348100de0f97c9d Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 25 Oct 2023 11:21:59 +0200 Subject: [PATCH 933/974] sphinx/qapidoc: Tidy up pylint warning raise-missing-from Pylint advises: docs/sphinx/qapidoc.py:518:12: W0707: Consider explicitly re-raising using 'raise ExtensionError(str(err)) from err' (raise-missing-from) >From its manual: Python's exception chaining shows the traceback of the current exception, but also of the original exception. When you raise a new exception after another exception was caught it's likely that the second exception is a friendly re-wrapping of the first exception. In such cases `raise from` provides a better link between the two tracebacks in the final error. Makes sense, so do it. Signed-off-by: Markus Armbruster Message-ID: <20231025092159.1782638-2-armbru@redhat.com> Reviewed-by: John Snow --- docs/sphinx/qapidoc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index 8f3b9997a1..658c288f8f 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -515,7 +515,7 @@ class QAPIDocDirective(Directive): except QAPIError as err: # Launder QAPI parse errors into Sphinx extension errors # so they are displayed nicely to the user - raise ExtensionError(str(err)) + raise ExtensionError(str(err)) from err def do_parse(self, rstlist, node): """Parse rST source lines and add them to the specified node From 5c24c3e2f3b22f1b77d556a14dd3bb8deed1f976 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 25 Oct 2023 11:29:25 +0200 Subject: [PATCH 934/974] tests/qapi-schema: Tidy up pylint warnings and advice Pylint warns: tests/qapi-schema/test-qapi.py:139:13: W1514: Using open without explicitly specifying an encoding (unspecified-encoding) tests/qapi-schema/test-qapi.py:143:13: W1514: Using open without explicitly specifying an encoding (unspecified-encoding) Add encoding='utf-8'. Pylint advises: tests/qapi-schema/test-qapi.py:143:13: R1732: Consider using 'with' for resource-allocating operations (consider-using-with) Silence this by returning the value directly. Pylint advises: tests/qapi-schema/test-qapi.py:221:4: R1722: Consider using sys.exit() (consider-using-sys-exit) tests/qapi-schema/test-qapi.py:226:4: R1722: Consider using sys.exit() (consider-using-sys-exit) Sure, why not. Signed-off-by: Markus Armbruster Message-ID: <20231025092925.1785934-1-armbru@redhat.com> Reviewed-by: John Snow --- tests/qapi-schema/test-qapi.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index d58c31f539..14f7b62a44 100755 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -136,12 +136,11 @@ def test_frontend(fname): def open_test_result(dir_name, file_name, update): mode = 'r+' if update else 'r' try: - fp = open(os.path.join(dir_name, file_name), mode) + return open(os.path.join(dir_name, file_name), mode, encoding='utf-8') except FileNotFoundError: if not update: raise - fp = open(os.path.join(dir_name, file_name), 'w+') - return fp + return open(os.path.join(dir_name, file_name), 'w+', encoding='utf-8') def test_and_diff(test_name, dir_name, update): @@ -218,9 +217,9 @@ def main(argv): test_name = os.path.splitext(base_name)[0] status |= test_and_diff(test_name, dir_name, args.update) - exit(status) + sys.exit(status) if __name__ == '__main__': main(sys.argv) - exit(0) + sys.exit(0) From 34aee9c94691f529cd952f9483a6b357ca098042 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 8 Nov 2023 09:59:54 +0100 Subject: [PATCH 935/974] host/include/generic/host/atomic128: Fix compilation problem with Clang 17 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When compiling QEMU with Clang 17 on a s390x, the compilation fails: In file included from ../accel/tcg/cputlb.c:32: In file included from /root/qemu/include/exec/helper-proto-common.h:10: In file included from /root/qemu/include/qemu/atomic128.h:62: /root/qemu/host/include/generic/host/atomic128-ldst.h:68:15: error: __sync builtin operation MUST have natural alignment (consider using __ atomic). [-Werror,-Wsync-alignment] 68 | } while (!__sync_bool_compare_and_swap_16(ptr_align, old, new.i)); | ^ In file included from ../accel/tcg/cputlb.c:32: In file included from /root/qemu/include/exec/helper-proto-common.h:10: In file included from /root/qemu/include/qemu/atomic128.h:61: /root/qemu/host/include/generic/host/atomic128-cas.h:36:11: error: __sync builtin operation MUST have natural alignment (consider using __a tomic). [-Werror,-Wsync-alignment] 36 | r.i = __sync_val_compare_and_swap_16(ptr_align, c.i, n.i); | ^ 2 errors generated. It's arguably a bug in Clang since we already use __builtin_assume_aligned() to tell the compiler that the pointer is properly aligned. But according to https://github.com/llvm/llvm-project/issues/69146 it seems like the Clang folks don't see an easy fix on their side and recommend to use a type declared with __attribute__((aligned(16))) to work around this problem. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1934 Message-ID: <20231108085954.313071-1-thuth@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Thomas Huth --- host/include/generic/host/atomic128-cas.h | 2 +- host/include/generic/host/atomic128-ldst.h | 2 +- include/qemu/int128.h | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/host/include/generic/host/atomic128-cas.h b/host/include/generic/host/atomic128-cas.h index 991d3da082..6b40cc2271 100644 --- a/host/include/generic/host/atomic128-cas.h +++ b/host/include/generic/host/atomic128-cas.h @@ -28,7 +28,7 @@ atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new) static inline Int128 ATTRIBUTE_ATOMIC128_OPT atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new) { - __int128_t *ptr_align = __builtin_assume_aligned(ptr, 16); + Int128Aligned *ptr_align = __builtin_assume_aligned(ptr, 16); Int128Alias r, c, n; c.s = cmp; diff --git a/host/include/generic/host/atomic128-ldst.h b/host/include/generic/host/atomic128-ldst.h index 80fff0643a..691e6a8531 100644 --- a/host/include/generic/host/atomic128-ldst.h +++ b/host/include/generic/host/atomic128-ldst.h @@ -58,7 +58,7 @@ atomic16_read_rw(Int128 *ptr) static inline void ATTRIBUTE_ATOMIC128_OPT atomic16_set(Int128 *ptr, Int128 val) { - __int128_t *ptr_align = __builtin_assume_aligned(ptr, 16); + Int128Aligned *ptr_align = __builtin_assume_aligned(ptr, 16); __int128_t old; Int128Alias new; diff --git a/include/qemu/int128.h b/include/qemu/int128.h index 73624e8be7..174bd7dafb 100644 --- a/include/qemu/int128.h +++ b/include/qemu/int128.h @@ -10,6 +10,7 @@ */ #if defined(CONFIG_INT128) && !defined(CONFIG_TCG_INTERPRETER) typedef __int128_t Int128; +typedef __int128_t __attribute__((aligned(16))) Int128Aligned; static inline Int128 int128_make64(uint64_t a) { @@ -224,6 +225,7 @@ static inline Int128 int128_rems(Int128 a, Int128 b) #else /* !CONFIG_INT128 */ typedef struct Int128 Int128; +typedef struct Int128 __attribute__((aligned(16))) Int128Aligned; /* * We guarantee that the in-memory byte representation of an From 0ab35658401a2e0a411ce0d1836a4f509bde717a Mon Sep 17 00:00:00 2001 From: Matthew Rosato Date: Fri, 10 Nov 2023 12:51:07 -0500 Subject: [PATCH 936/974] s390x/pci: bypass vfio DMA counting when using cdev The current code assumes that there is always a vfio group, but that's no longer guaranteed with the iommufd backend when using cdev. In this case, we don't need to track the vfio dma limit anyway. Signed-off-by: Matthew Rosato Message-ID: <20231110175108.465851-2-mjrosato@linux.ibm.com> Signed-off-by: Thomas Huth --- hw/s390x/s390-pci-vfio.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/s390x/s390-pci-vfio.c b/hw/s390x/s390-pci-vfio.c index 59a2e03873..e28573b593 100644 --- a/hw/s390x/s390-pci-vfio.c +++ b/hw/s390x/s390-pci-vfio.c @@ -66,6 +66,10 @@ S390PCIDMACount *s390_pci_start_dma_count(S390pciState *s, assert(vpdev); + if (!vpdev->vbasedev.group) { + return NULL; + } + id = vpdev->vbasedev.group->container->fd; if (!s390_pci_update_dma_avail(id, &avail)) { From 8011b508cf0ddbdbda03820f4fa6cd484a6d9aed Mon Sep 17 00:00:00 2001 From: Matthew Rosato Date: Fri, 10 Nov 2023 12:51:08 -0500 Subject: [PATCH 937/974] s390x/pci: only limit DMA aperture if vfio DMA limit reported If the host kernel lacks vfio DMA limit reporting, do not attempt to shrink the guest DMA aperture. Fixes: df202e3ff3 ("s390x/pci: shrink DMA aperture to be bound by vfio DMA limit") Signed-off-by: Matthew Rosato Message-ID: <20231110175108.465851-3-mjrosato@linux.ibm.com> Signed-off-by: Thomas Huth --- hw/s390x/s390-pci-vfio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/s390x/s390-pci-vfio.c b/hw/s390x/s390-pci-vfio.c index e28573b593..7dbbc76823 100644 --- a/hw/s390x/s390-pci-vfio.c +++ b/hw/s390x/s390-pci-vfio.c @@ -136,7 +136,7 @@ static void s390_pci_read_base(S390PCIBusDevice *pbdev, * to the guest based upon the vfio DMA limit. */ vfio_size = pbdev->iommu->max_dma_limit << TARGET_PAGE_BITS; - if (vfio_size < (cap->end_dma - cap->start_dma + 1)) { + if (vfio_size > 0 && vfio_size < cap->end_dma - cap->start_dma + 1) { pbdev->zpci_fn.edma = cap->start_dma + vfio_size - 1; } } From 4940da2096f969cb21fc23e0fd6f52e766bf4fdf Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 20 Oct 2023 08:09:33 +0200 Subject: [PATCH 938/974] MAINTAINERS: Add include/hw/input/pl050.h to the PrimeCell/CMSDK section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The corresponding pl050.c file is already listed here, so we should mention the header here, too. Message-ID: <20231020060936.524988-2-thuth@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index e73a3ff544..ccc7aa3e3e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -657,6 +657,7 @@ F: include/hw/dma/pl080.h F: hw/dma/pl330.c F: hw/gpio/pl061.c F: hw/input/pl050.c +F: include/hw/input/pl050.h F: hw/intc/pl190.c F: hw/sd/pl181.c F: hw/ssi/pl022.c From 261c1281e87e030ba8c1ceb45b34ae03d4c7972f Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 20 Oct 2023 08:09:34 +0200 Subject: [PATCH 939/974] MAINTAINERS: Add hw/input/ads7846.c to the PXA2XX section The code from hw/input/ads7846.c is only used by hw/arm/spitz.c, so add this file to the same section where hw/arm/spitz.c is listed. Message-ID: <20231020060936.524988-3-thuth@redhat.com> Reviewed-by: Peter Maydell Signed-off-by: Thomas Huth --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index ccc7aa3e3e..e82b64d1ca 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -928,6 +928,7 @@ F: hw/*/pxa2xx* F: hw/display/tc6393xb.c F: hw/gpio/max7310.c F: hw/gpio/zaurus.c +F: hw/input/ads7846.c F: hw/misc/mst_fpga.c F: hw/adc/max111x.c F: include/hw/adc/max111x.h From 42c31682bae341bc16a92e7eb94587a6b4d84dfa Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 20 Oct 2023 08:09:35 +0200 Subject: [PATCH 940/974] MAINTAINERS: Add hw/display/sii9022.c to the Versatile Express section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This graphics adapter is only used by the Versatile Express machine, so add it to the corresponding section in MAINTAINERS. Message-ID: <20231020060936.524988-4-thuth@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index e82b64d1ca..cc13f15996 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -996,6 +996,7 @@ M: Peter Maydell L: qemu-arm@nongnu.org S: Maintained F: hw/arm/vexpress.c +F: hw/display/sii9022.c F: docs/system/arm/vexpress.rst Versatile PB From 7c7e1f6017ece833499ff459fea1e6d32f543940 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 20 Oct 2023 08:09:36 +0200 Subject: [PATCH 941/974] MAINTAINERS: Extend the Stellaris section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This header include/hw/timer/stellaris-gptm.h obviously belongs to the Stellaris machines, so let's add it to the corresponding section. And hw/display/ssd0303.c and hw/display/ssd0323.c are only used by hw/arm/stellaris.c, so add them to the corresponding section in the MAINTAINERS file, too. Message-ID: <20231020060936.524988-5-thuth@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index cc13f15996..c127a373ab 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -981,7 +981,9 @@ M: Peter Maydell L: qemu-arm@nongnu.org S: Maintained F: hw/*/stellaris* +F: hw/display/ssd03* F: include/hw/input/gamepad.h +F: include/hw/timer/stellaris-gptm.h F: docs/system/arm/stellaris.rst STM32VLDISCOVERY From d229996b402e6d08764f3da5a49aa3a25193db47 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 29 Sep 2023 15:45:51 +0200 Subject: [PATCH 942/974] MAINTAINERS: Add a general architecture section for x86 It's a little bit weird that the files in target/i386/ which are not in a subfolder there do not have any associated maintainer (and thus nobody might be CC:-ed on changes to these files). We should have a general x86 section for these files, similar to what we already have for s390x and mips. Since Paolo is already listed as maintainer for both, the x86 KVM and TCG CPUs, I'd like to suggest him as maintainer for the general files, too. Message-ID: <20230929134551.395438-1-thuth@redhat.com> Signed-off-by: Thomas Huth --- MAINTAINERS | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index c127a373ab..6999b26a0f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -131,6 +131,17 @@ K: ^Subject:.*(?i)mips F: docs/system/target-mips.rst F: configs/targets/mips* +X86 general architecture support +M: Paolo Bonzini +S: Maintained +F: configs/devices/i386-softmmu/default.mak +F: configs/targets/i386-softmmu.mak +F: configs/targets/x86_64-softmmu.mak +F: docs/system/target-i386* +F: target/i386/*.[ch] +F: target/i386/Kconfig +F: target/i386/meson.build + Guest CPU cores (TCG) --------------------- Overall TCG CPUs From 00ac955b06b28803319159551bfed6a130f8ec2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Nov 2023 16:09:00 +0100 Subject: [PATCH 943/974] tests/vm/netbsd: Use Python v3.11 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We requiere the 'ninja-build', which depends on 'python311': $ pkgin show-deps ninja-build direct dependencies for ninja-build-1.11.1nb1 python311>=3.11.0 So we end up installing both Python v3.10 and v3.11: [31/76] installing python311-3.11.5... [54/76] installing python310-3.10.13... [74/76] installing py310-expat-3.10.13nb1... Then the build system picks Python v3.11, and doesn't find py-expat because we only installed the 3.10 version: python determined to be '/usr/pkg/bin/python3.11' python version: Python 3.11.5 *** Ouch! *** Python's pyexpat module is not found. It's normally part of the Python standard library, maybe your distribution packages it separately? Either install pyexpat, or alleviate the need for it in the first place by installing pip and setuptools for '/usr/pkg/bin/python3.11'. (Hint: NetBSD's pkgsrc debundles this to e.g. 'py310-expat'.) ERROR: python venv creation failed Fix by installing py-expat for v3.11. Remove the v3.10 packages since we aren't using them anymore. Signed-off-by: Philippe Mathieu-Daudé Tested-by: Thomas Huth Message-ID: <20231109150900.91186-1-philmd@linaro.org> Signed-off-by: Thomas Huth --- tests/vm/netbsd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/vm/netbsd b/tests/vm/netbsd index 40b27a3469..649fcad353 100755 --- a/tests/vm/netbsd +++ b/tests/vm/netbsd @@ -30,8 +30,8 @@ class NetBSDVM(basevm.BaseVM): "git-base", "pkgconf", "xz", - "python310", - "py310-expat", + "python311", + "py311-expat", "ninja-build", # gnu tools From 2e990d81d9813628148f64b944691b5cbc251fab Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 10 Nov 2023 09:36:54 +0100 Subject: [PATCH 944/974] test-resv-mem: Fix CID 1523911 Coverity complains about passing "&expected" to "run_range_inverse_array", which dereferences null "expected". I guess the problem is that the compare_ranges() loop dereferences 'e' without testing it. However the loop condition is based on 'ranges' which is garanteed to have the same length as 'expected' given the g_assert_cmpint() just before the loop. So the code looks safe to me. Nevertheless adding a test on expected before the loop to get rid of the warning. Fixes: CID 1523901 Signed-off-by: Eric Auger Reported-by: Coverity (CID 1523901) Message-ID: <20231110083654.277345-1-eric.auger@redhat.com> Reviewed-by: Thomas Huth Signed-off-by: Thomas Huth --- tests/unit/test-resv-mem.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/unit/test-resv-mem.c b/tests/unit/test-resv-mem.c index 5963274e2c..cd8f7318cc 100644 --- a/tests/unit/test-resv-mem.c +++ b/tests/unit/test-resv-mem.c @@ -44,6 +44,10 @@ static void compare_ranges(const char *prefix, GList *ranges, print_ranges("out", ranges); print_ranges("expected", expected); #endif + if (!expected) { + g_assert_true(!ranges); + return; + } g_assert_cmpint(g_list_length(ranges), ==, g_list_length(expected)); for (l = ranges, e = expected; l ; l = l->next, e = e->next) { Range *r = (Range *)l->data; From f9a19bd8d23f0048315a60d695857c705988dc05 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 9 Nov 2023 18:47:20 +0100 Subject: [PATCH 945/974] tests/tsan: Rename the file with the entries that should be ignored MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let's use a better file name here. Message-ID: <20231109174720.375873-1-thuth@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth --- docs/devel/testing.rst | 4 ++-- tests/tsan/{blacklist.tsan => ignore.tsan} | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) rename tests/tsan/{blacklist.tsan => ignore.tsan} (57%) diff --git a/docs/devel/testing.rst b/docs/devel/testing.rst index b0680cbb22..fef64accc1 100644 --- a/docs/devel/testing.rst +++ b/docs/devel/testing.rst @@ -668,11 +668,11 @@ suppressing it. More information on the file format can be found here: https://github.com/google/sanitizers/wiki/ThreadSanitizerSuppressions -tests/tsan/blacklist.tsan - Has TSan warnings we wish to disable +tests/tsan/ignore.tsan - Has TSan warnings we wish to disable at compile time for test or debug. Add flags to configure to enable: -"--extra-cflags=-fsanitize-blacklist=/tests/tsan/blacklist.tsan" +"--extra-cflags=-fsanitize-blacklist=/tests/tsan/ignore.tsan" More information on the file format can be found here under "Blacklist Format": diff --git a/tests/tsan/blacklist.tsan b/tests/tsan/ignore.tsan similarity index 57% rename from tests/tsan/blacklist.tsan rename to tests/tsan/ignore.tsan index 75e444f5dc..423e482d2f 100644 --- a/tests/tsan/blacklist.tsan +++ b/tests/tsan/ignore.tsan @@ -1,6 +1,6 @@ -# This is an example blacklist. -# To enable use of the blacklist add this to configure: -# "--extra-cflags=-fsanitize-blacklist=/tests/tsan/blacklist.tsan" +# This is an example ignore list. +# To enable use of the ignore list add this to configure: +# "--extra-cflags=-fsanitize-blacklist=/tests/tsan/ignore.tsan" # The eventual goal would be to fix these warnings. # TSan is not happy about setting/getting of dirty bits, From 4409a6d85522925df580554d476161a570bb1ed9 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 10 Nov 2023 16:43:18 +0000 Subject: [PATCH 946/974] hw/audio/es1370: Clean up comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace a sweary comment with one that's a bit more helpful to future readers of the code. Signed-off-by: Peter Maydell Reviewed-by: Volker Rümelin Message-ID: <20231110164318.2197569-1-peter.maydell@linaro.org> Signed-off-by: Thomas Huth --- hw/audio/es1370.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/hw/audio/es1370.c b/hw/audio/es1370.c index 91c47330ad..fad5541211 100644 --- a/hw/audio/es1370.c +++ b/hw/audio/es1370.c @@ -670,8 +670,13 @@ static void es1370_transfer_audio (ES1370State *s, struct chan *d, int loop_sel, cnt += (transferred + d->leftover) >> 2; if (s->sctl & loop_sel) { - /* Bah, how stupid is that having a 0 represent true value? - i just spent few hours on this shit */ + /* + * loop_sel tells us which bit in the SCTL register to look at + * (either P1_LOOP_SEL, P2_LOOP_SEL or R1_LOOP_SEL). The sense + * of these bits is 0 for loop mode (set interrupt and keep recording + * when the sample count reaches zero) or 1 for stop mode (set + * interrupt and stop recording). + */ AUD_log ("es1370: warning", "non looping mode\n"); } else { d->frame_cnt = size; From c96c116e10938d3a01a55273d10e104ba7534030 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Sat, 11 Nov 2023 08:49:20 +0800 Subject: [PATCH 947/974] MAINTAINERS: update virtio-fs mailing list address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The old virtio-fs mailing list address is no longer in use. Switch to the new mailing list address. Cc: Philippe Mathieu-Daudé Cc: Vivek Goyal Cc: German Maglione Cc: Hanna Czenczek Reviewed-by: German Maglione Signed-off-by: Stefan Hajnoczi Message-ID: <20231111004920.148348-1-stefanha@redhat.com> --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index e73a3ff544..c52df9f76c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2241,7 +2241,7 @@ M: Stefan Hajnoczi S: Supported F: hw/virtio/vhost-user-fs* F: include/hw/virtio/vhost-user-fs.h -L: virtio-fs@redhat.com +L: virtio-fs@lists.linux.dev virtio-input M: Gerd Hoffmann From 1d675e59ea194fc918fe0f553eb79209b3fb3a8f Mon Sep 17 00:00:00 2001 From: Jean-Philippe Brucker Date: Fri, 10 Nov 2023 09:05:58 +0000 Subject: [PATCH 948/974] hw/arm/virt: fix GIC maintenance IRQ registration Since commit 9036e917f8 ("{include/}hw/arm: refactor virt PPI logic"), GIC maintenance IRQ registration fails on arm64: [ 0.979743] kvm [1]: Cannot register interrupt 9 That commit re-defined VIRTUAL_PMU_IRQ to be a INTID but missed a case where the maintenance IRQ is actually referred by its PPI index. Just like commit fa68ecb330db ("hw/arm/virt: fix PMU IRQ registration"), use INITID_TO_PPI(). A search of "GIC_FDT_IRQ_TYPE_PPI" indicates that there shouldn't be more similar issues. Fixes: 9036e917f8 ("{include/}hw/arm: refactor virt PPI logic") Signed-off-by: Jean-Philippe Brucker Message-id: 20231110090557.3219206-2-jean-philippe@linaro.org Signed-off-by: Peter Maydell --- hw/arm/virt.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 85e3c5ba9d..be2856c018 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -576,7 +576,8 @@ static void fdt_add_gic_node(VirtMachineState *vms) if (vms->virt) { qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", - GIC_FDT_IRQ_TYPE_PPI, ARCH_GIC_MAINT_IRQ, + GIC_FDT_IRQ_TYPE_PPI, + INTID_TO_PPI(ARCH_GIC_MAINT_IRQ), GIC_FDT_IRQ_FLAGS_LEVEL_HI); } } else { @@ -600,7 +601,8 @@ static void fdt_add_gic_node(VirtMachineState *vms) 2, vms->memmap[VIRT_GIC_VCPU].base, 2, vms->memmap[VIRT_GIC_VCPU].size); qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", - GIC_FDT_IRQ_TYPE_PPI, ARCH_GIC_MAINT_IRQ, + GIC_FDT_IRQ_TYPE_PPI, + INTID_TO_PPI(ARCH_GIC_MAINT_IRQ), GIC_FDT_IRQ_FLAGS_LEVEL_HI); } } From fc58891d0422607d172a3d6b3158798f2556aef1 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 9 Nov 2023 15:19:17 +0000 Subject: [PATCH 949/974] target/arm: HVC at EL3 should go to EL3, not EL2 AArch64 permits code at EL3 to use the HVC instruction; however the exception we take should go to EL3, not down to EL2 (see the pseudocode AArch64.CallHypervisor()). Fix the target EL. Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell Reviewed-by: Edgar E. Iglesias Message-id: 20231109151917.1925107-1-peter.maydell@linaro.org --- target/arm/tcg/translate-a64.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 41484d8ae5..a2e49c39f9 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -2351,6 +2351,8 @@ static bool trans_SVC(DisasContext *s, arg_i *a) static bool trans_HVC(DisasContext *s, arg_i *a) { + int target_el = s->current_el == 3 ? 3 : 2; + if (s->current_el == 0) { unallocated_encoding(s); return true; @@ -2363,7 +2365,7 @@ static bool trans_HVC(DisasContext *s, arg_i *a) gen_helper_pre_hvc(tcg_env); /* Architecture requires ss advance before we do the actual work */ gen_ss_advance(s); - gen_exception_insn_el(s, 4, EXCP_HVC, syn_aa64_hvc(a->imm), 2); + gen_exception_insn_el(s, 4, EXCP_HVC, syn_aa64_hvc(a->imm), target_el); return true; } From 4d044472ab7666adf99d4daa0cc90b7502b90109 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 10 Nov 2023 16:25:46 +0000 Subject: [PATCH 950/974] target/arm: Correct MTE tag checking for reverse-copy MOPS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we are doing a FEAT_MOPS copy that must be performed backwards, we call mte_mops_probe_rev(), passing it the address of the last byte in the region we are probing. However, allocation_tag_mem_probe() wants the address of the first byte to get the tag memory for. Because we passed it (ptr, size) we could incorrectly trip the allocation_tag_mem_probe() check for "does this access run across to the following page", and if that following page happened not to be valid then we would assert. We know we will always be only dealing with a single page because the code that calls mte_mops_probe_rev() ensures that. We could make mte_mops_probe_rev() pass 'ptr - (size - 1)' to allocation_tag_mem_probe(), but then we would have to adjust the returned 'mem' pointer to get back to the tag RAM for the last byte of the region. It's simpler to just pass in a size of 1 byte, because we know that allocation_tag_mem_probe() in pure-probe single-page mode doesn't care about the size. Fixes: 69c51dc3723b ("target/arm: Implement MTE tag-checking functions for FEAT_MOPS copies") Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Acked-by: Richard Henderson Message-id: 20231110162546.2192512-1-peter.maydell@linaro.org --- target/arm/tcg/mte_helper.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/mte_helper.c b/target/arm/tcg/mte_helper.c index 70ac876105..ffb8ea1c34 100644 --- a/target/arm/tcg/mte_helper.c +++ b/target/arm/tcg/mte_helper.c @@ -1101,10 +1101,18 @@ uint64_t mte_mops_probe_rev(CPUARMState *env, uint64_t ptr, uint64_t size, uint32_t n; mmu_idx = FIELD_EX32(desc, MTEDESC, MIDX); - /* True probe; this will never fault */ + /* + * True probe; this will never fault. Note that our caller passes + * us a pointer to the end of the region, but allocation_tag_mem_probe() + * wants a pointer to the start. Because we know we don't span a page + * boundary and that allocation_tag_mem_probe() doesn't otherwise care + * about the size, pass in a size of 1 byte. This is simpler than + * adjusting the ptr to point to the start of the region and then having + * to adjust the returned 'mem' to get the end of the tag memory. + */ mem = allocation_tag_mem_probe(env, mmu_idx, ptr, w ? MMU_DATA_STORE : MMU_DATA_LOAD, - size, MMU_DATA_LOAD, true, 0); + 1, MMU_DATA_LOAD, true, 0); if (!mem) { return size; } From 52c773ce893f6321f20c80101aa4ea9489a6f701 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 7 Nov 2023 18:06:15 +0400 Subject: [PATCH 951/974] hw/mips: LOONGSON3V depends on UNIMP device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Cc: qemu-stable@nongnu.org Fixes: c76b409fef ("hw/mips: Add Loongson-3 machine support") Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20231107140615.3034763-1-marcandre.lureau@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/mips/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/mips/Kconfig b/hw/mips/Kconfig index 66ec536e06..505381a0bb 100644 --- a/hw/mips/Kconfig +++ b/hw/mips/Kconfig @@ -46,6 +46,7 @@ config LOONGSON3V select PCI_EXPRESS_GENERIC_BRIDGE select MSI_NONBROKEN select FW_CFG_MIPS + select UNIMP config MIPS_CPS bool From 4c7ae73caf7e794a7bf909906e21381f5f01b369 Mon Sep 17 00:00:00 2001 From: Alexandra Diupina Date: Fri, 10 Nov 2023 20:41:04 +0300 Subject: [PATCH 952/974] hw/display/vmware_vga: fix probably typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When calling trace_vmware_verify_rect_greater_than_bound() replace "y" with "h" and y with h Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: 02218aedb1 ("hw/display/vmware_vga: replace fprintf calls with trace events") Signed-off-by: Alexandra Diupina Reviewed-by: Thomas Huth Message-ID: <20231110174104.13280-1-adiupina@astralinux.ru> Signed-off-by: Philippe Mathieu-Daudé --- hw/display/vmware_vga.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c index 7490d43881..3f26bea190 100644 --- a/hw/display/vmware_vga.c +++ b/hw/display/vmware_vga.c @@ -336,8 +336,8 @@ static inline bool vmsvga_verify_rect(DisplaySurface *surface, return false; } if (h > SVGA_MAX_HEIGHT) { - trace_vmware_verify_rect_greater_than_bound(name, "y", SVGA_MAX_HEIGHT, - y); + trace_vmware_verify_rect_greater_than_bound(name, "h", SVGA_MAX_HEIGHT, + h); return false; } if (y + h > surface_height(surface)) { From 0034d0395e4e92c722a7044d291b2bab42f705bd Mon Sep 17 00:00:00 2001 From: Ani Sinha Date: Thu, 9 Nov 2023 10:26:01 +0530 Subject: [PATCH 953/974] tests/avocado: add test to exercise processor address space memory bound checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QEMU has validations to make sure that a VM is not started with more memory (static and hotpluggable memory) than what the guest processor can address directly with its addressing bits. This change adds a test to make sure QEMU fails to start with a specific error message when an attempt is made to start a VM with more memory than what the processor can directly address. The test also checks for passing cases when the address space of the processor is capable of addressing all memory. Boundary cases are tested. CC: imammedo@redhat.com CC: David Hildenbrand Acked-by: David Hildenbrand Acked-by: Philippe Mathieu-Daudé Signed-off-by: Ani Sinha Message-ID: <20231109045601.33349-1-anisinha@redhat.com> Message-ID: [PMD: Use SPDX tag] Signed-off-by: Philippe Mathieu-Daudé --- tests/avocado/mem-addr-space-check.py | 356 ++++++++++++++++++++++++++ 1 file changed, 356 insertions(+) create mode 100644 tests/avocado/mem-addr-space-check.py diff --git a/tests/avocado/mem-addr-space-check.py b/tests/avocado/mem-addr-space-check.py new file mode 100644 index 0000000000..be949222a4 --- /dev/null +++ b/tests/avocado/mem-addr-space-check.py @@ -0,0 +1,356 @@ +# Check for crash when using memory beyond the available guest processor +# address space. +# +# Copyright (c) 2023 Red Hat, Inc. +# +# Author: +# Ani Sinha +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from avocado_qemu import QemuSystemTest +import signal +import time + +class MemAddrCheck(QemuSystemTest): + # after launch, in order to generate the logs from QEMU we need to + # wait for some time. Launching and then immediately shutting down + # the VM generates empty logs. A delay of 1 second is added for + # this reason. + DELAY_Q35_BOOT_SEQUENCE = 1 + + # first, lets test some 32-bit processors. + # for all 32-bit cases, pci64_hole_size is 0. + def test_phybits_low_pse36(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=arch:x86_64 + + With pse36 feature ON, a processor has 36 bits of addressing. So it can + access up to a maximum of 64GiB of memory. Memory hotplug region begins + at 4 GiB boundary when "above_4g_mem_size" is 0 (this would be true when + we have 0.5 GiB of VM memory, see pc_q35_init()). This means total + hotpluggable memory size is 60 GiB. Per slot, we reserve 1 GiB of memory + for dimm alignment for all newer machines (see enforce_aligned_dimm + property for pc machines and pc_get_device_memory_range()). That leaves + total hotpluggable actual memory size of 59 GiB. If the VM is started + with 0.5 GiB of memory, maxmem should be set to a maximum value of + 59.5 GiB to ensure that the processor can address all memory directly. + Note that 64-bit pci hole size is 0 in this case. If maxmem is set to + 59.6G, QEMU should fail to start with a message "phy-bits are too low". + If maxmem is set to 59.5G with all other QEMU parameters identical, QEMU + should start fine. + """ + self.vm.add_args('-S', '-machine', 'q35', '-m', + '512,slots=1,maxmem=59.6G', + '-cpu', 'pentium,pse36=on', '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + self.vm.wait() + self.assertEquals(self.vm.exitcode(), 1, "QEMU exit code should be 1") + self.assertRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_low_pae(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=arch:x86_64 + + With pae feature ON, a processor has 36 bits of addressing. So it can + access up to a maximum of 64GiB of memory. Rest is the same as the case + with pse36 above. + """ + self.vm.add_args('-S', '-machine', 'q35', '-m', + '512,slots=1,maxmem=59.6G', + '-cpu', 'pentium,pae=on', '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + self.vm.wait() + self.assertEquals(self.vm.exitcode(), 1, "QEMU exit code should be 1") + self.assertRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_ok_pentium_pse36(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=arch:x86_64 + + Setting maxmem to 59.5G and making sure that QEMU can start with the + same options as the failing case above with pse36 cpu feature. + """ + self.vm.add_args('-machine', 'q35', '-m', + '512,slots=1,maxmem=59.5G', + '-cpu', 'pentium,pse36=on', '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + time.sleep(self.DELAY_Q35_BOOT_SEQUENCE) + self.vm.shutdown() + self.assertNotRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_ok_pentium_pae(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=arch:x86_64 + + Test is same as above but now with pae cpu feature turned on. + Setting maxmem to 59.5G and making sure that QEMU can start fine + with the same options as the case above. + """ + self.vm.add_args('-machine', 'q35', '-m', + '512,slots=1,maxmem=59.5G', + '-cpu', 'pentium,pae=on', '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + time.sleep(self.DELAY_Q35_BOOT_SEQUENCE) + self.vm.shutdown() + self.assertNotRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_ok_pentium2(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=arch:x86_64 + + Pentium2 has 36 bits of addressing, so its same as pentium + with pse36 ON. + """ + self.vm.add_args('-machine', 'q35', '-m', + '512,slots=1,maxmem=59.5G', + '-cpu', 'pentium2', '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + time.sleep(self.DELAY_Q35_BOOT_SEQUENCE) + self.vm.shutdown() + self.assertNotRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_low_nonpse36(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=arch:x86_64 + + Pentium processor has 32 bits of addressing without pse36 or pae + so it can access physical address up to 4 GiB. Setting maxmem to + 4 GiB should make QEMU fail to start with "phys-bits too low" + message because the region for memory hotplug is always placed + above 4 GiB due to the PCI hole and simplicity. + """ + self.vm.add_args('-S', '-machine', 'q35', '-m', + '512,slots=1,maxmem=4G', + '-cpu', 'pentium', '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + self.vm.wait() + self.assertEquals(self.vm.exitcode(), 1, "QEMU exit code should be 1") + self.assertRegex(self.vm.get_log(), r'phys-bits too low') + + # now lets test some 64-bit CPU cases. + def test_phybits_low_tcg_q35_70_amd(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=arch:x86_64 + + For q35 7.1 machines and above, there is a HT window that starts at + 1024 GiB and ends at 1 TiB - 1. If the max GPA falls in this range, + "above_4G" memory is adjusted to start at 1 TiB boundary for AMD cpus + in the default case. Lets test without that case for machines 7.0. + For q35-7.0 machines, "above 4G" memory starts are 4G. + pci64_hole size is 32 GiB. Since TCG_PHYS_ADDR_BITS is defined to + be 40, TCG emulated CPUs have maximum of 1 TiB (1024 GiB) of + directly addressible memory. + Hence, maxmem value at most can be + 1024 GiB - 4 GiB - 1 GiB per slot for alignment - 32 GiB + 0.5 GiB + which is equal to 987.5 GiB. Setting the value to 988 GiB should + make QEMU fail with the error message. + """ + self.vm.add_args('-S', '-machine', 'pc-q35-7.0', '-m', + '512,slots=1,maxmem=988G', + '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + self.vm.wait() + self.assertEquals(self.vm.exitcode(), 1, "QEMU exit code should be 1") + self.assertRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_low_tcg_q35_71_amd(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=arch:x86_64 + + AMD_HT_START is defined to be at 1012 GiB. So for q35 machines + version > 7.0 and AMD cpus, instead of 1024 GiB limit for 40 bit + processor address space, it has to be 1012 GiB , that is 12 GiB + less than the case above in order to accomodate HT hole. + Make sure QEMU fails when maxmem size is 976 GiB (12 GiB less + than 988 GiB). + """ + self.vm.add_args('-S', '-machine', 'pc-q35-7.1', '-m', + '512,slots=1,maxmem=976G', + '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + self.vm.wait() + self.assertEquals(self.vm.exitcode(), 1, "QEMU exit code should be 1") + self.assertRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_ok_tcg_q35_70_amd(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=arch:x86_64 + + Same as q35-7.0 AMD case except that here we check that QEMU can + successfully start when maxmem is < 988G. + """ + self.vm.add_args('-S', '-machine', 'pc-q35-7.0', '-m', + '512,slots=1,maxmem=987.5G', + '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + time.sleep(self.DELAY_Q35_BOOT_SEQUENCE) + self.vm.shutdown() + self.assertNotRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_ok_tcg_q35_71_amd(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=arch:x86_64 + + Same as q35-7.1 AMD case except that here we check that QEMU can + successfully start when maxmem is < 976G. + """ + self.vm.add_args('-S', '-machine', 'pc-q35-7.1', '-m', + '512,slots=1,maxmem=975.5G', + '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + time.sleep(self.DELAY_Q35_BOOT_SEQUENCE) + self.vm.shutdown() + self.assertNotRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_ok_tcg_q35_71_intel(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=arch:x86_64 + + Same parameters as test_phybits_low_tcg_q35_71_amd() but use + Intel cpu instead. QEMU should start fine in this case as + "above_4G" memory starts at 4G. + """ + self.vm.add_args('-S', '-cpu', 'Skylake-Server', + '-machine', 'pc-q35-7.1', '-m', + '512,slots=1,maxmem=976G', + '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + time.sleep(self.DELAY_Q35_BOOT_SEQUENCE) + self.vm.shutdown() + self.assertNotRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_low_tcg_q35_71_amd_41bits(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=arch:x86_64 + + AMD processor with 41 bits. Max cpu hw address = 2 TiB. + By setting maxram above 1012 GiB - 32 GiB - 4 GiB = 976 GiB, we can + force "above_4G" memory to start at 1 TiB for q35-7.1 machines + (max GPA will be above AMD_HT_START which is defined as 1012 GiB). + + With pci_64_hole size at 32 GiB, in this case, maxmem should be 991.5 + GiB with 1 GiB per slot for alignment and 0.5 GiB as non-hotplug + memory for the VM (1024 - 32 - 1 + 0.5). With 992 GiB, QEMU should + fail to start. + """ + self.vm.add_args('-S', '-cpu', 'EPYC-v4,phys-bits=41', + '-machine', 'pc-q35-7.1', '-m', + '512,slots=1,maxmem=992G', + '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + self.vm.wait() + self.assertEquals(self.vm.exitcode(), 1, "QEMU exit code should be 1") + self.assertRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_ok_tcg_q35_71_amd_41bits(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=arch:x86_64 + + AMD processor with 41 bits. Max cpu hw address = 2 TiB. + Same as above but by setting maxram beween 976 GiB and 992 Gib, + QEMU should start fine. + """ + self.vm.add_args('-S', '-cpu', 'EPYC-v4,phys-bits=41', + '-machine', 'pc-q35-7.1', '-m', + '512,slots=1,maxmem=990G', + '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + time.sleep(self.DELAY_Q35_BOOT_SEQUENCE) + self.vm.shutdown() + self.assertNotRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_low_tcg_q35_intel_cxl(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=arch:x86_64 + + cxl memory window starts after memory device range. Here, we use 1 GiB + of cxl window memory. 4G_mem end aligns at 4G. pci64_hole is 32 GiB and + starts after the cxl memory window. + So maxmem here should be at most 986 GiB considering all memory boundary + alignment constraints with 40 bits (1 TiB) of processor physical bits. + """ + self.vm.add_args('-S', '-cpu', 'Skylake-Server,phys-bits=40', + '-machine', 'q35,cxl=on', '-m', + '512,slots=1,maxmem=987G', + '-display', 'none', + '-device', 'pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1', + '-M', 'cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=1G') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + self.vm.wait() + self.assertEquals(self.vm.exitcode(), 1, "QEMU exit code should be 1") + self.assertRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_ok_tcg_q35_intel_cxl(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=arch:x86_64 + + Same as above but here we do not reserve any cxl memory window. Hence, + with the exact same parameters as above, QEMU should start fine even + with cxl enabled. + """ + self.vm.add_args('-S', '-cpu', 'Skylake-Server,phys-bits=40', + '-machine', 'q35,cxl=on', '-m', + '512,slots=1,maxmem=987G', + '-display', 'none', + '-device', 'pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + time.sleep(self.DELAY_Q35_BOOT_SEQUENCE) + self.vm.shutdown() + self.assertNotRegex(self.vm.get_log(), r'phys-bits too low') From f6e8d1ef05a126de796ae03dd81e048e3ff48ff1 Mon Sep 17 00:00:00 2001 From: Nikita Ostrenkov Date: Sun, 12 Nov 2023 16:56:58 +0000 Subject: [PATCH 954/974] target/arm/tcg: enable PMU feature for Cortex-A8 and A9 According to the technical reference manual, the Cortex-A9 has a Perfomance Unit Monitor (PMU): https://developer.arm.com/documentation/100511/0401/performance-monitoring-unit/about-the-performance-monitoring-unit The Cortex-A8 does also. We already already define the PMU registers when emulating the Cortex-A8 and Cortex-A9, because we put them in v7_cp_reginfo[] rather than guarding them behind ARM_FEATURE_PMU. So the only thing that setting the feature bit changes is that the registers actually do something. Enable ARM_FEATURE_PMU for Cortex-A8 and Cortex-A9, to avoid this anomaly. (The A8 and A9 PMU predates the standardisation of ID_DFR0.PerfMon, so the field there is 0, but the PMU is still present.) Signed-off-by: Nikita Ostrenkov Message-id: 20231112165658.2335-1-n.ostrenkov@gmail.com Reviewed-by: Peter Maydell [PMM: tweaked commit message; also enable PMU for A8] Signed-off-by: Peter Maydell --- target/arm/tcg/cpu32.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/arm/tcg/cpu32.c b/target/arm/tcg/cpu32.c index 0d5d8e307d..d9e0e2a4dd 100644 --- a/target/arm/tcg/cpu32.c +++ b/target/arm/tcg/cpu32.c @@ -351,6 +351,7 @@ static void cortex_a8_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); set_feature(&cpu->env, ARM_FEATURE_EL3); + set_feature(&cpu->env, ARM_FEATURE_PMU); cpu->midr = 0x410fc080; cpu->reset_fpsid = 0x410330c0; cpu->isar.mvfr0 = 0x11110222; @@ -418,6 +419,7 @@ static void cortex_a9_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); set_feature(&cpu->env, ARM_FEATURE_EL3); + set_feature(&cpu->env, ARM_FEATURE_PMU); /* * Note that A9 supports the MP extensions even for * A9UP and single-core A9MP (which are both different From e5d487c9724e0fc62b1afca9f8ea11fd072643a8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 7 Nov 2023 13:31:20 -0800 Subject: [PATCH 955/974] target/hppa: Use only low 2 immediate bits for PROBEI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During the conversion to decodetree, the 2-bit mask was lost. Fixes: deee69a19fd ("target/hppa: Convert memory management insns") Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hppa/translate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index f3b17ba16d..bb1b65fef0 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -2297,7 +2297,7 @@ static bool trans_probe(DisasContext *ctx, arg_probe *a) form_gva(ctx, &addr, &ofs, a->b, 0, 0, 0, a->sp, 0, false); if (a->imm) { - level = tcg_constant_i32(a->ri); + level = tcg_constant_i32(a->ri & 3); } else { level = tcg_temp_new_i32(); tcg_gen_extrl_i64_i32(level, load_gpr(ctx, a->ri)); From 576fc9376d7f220a21807bd7759d297acb4b6072 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 7 Nov 2023 13:30:27 -0800 Subject: [PATCH 956/974] target/hppa: Use PRIV_P_TO_MMU_IDX in helper_probe Direct privilege level to mmu_idx mapping has been false for some time. Provide the correct value to hppa_get_physical_address. Fixes: fa824d99f9b ("target/hppa: Switch to use MMU indices 11-15") Signed-off-by: Richard Henderson --- target/hppa/op_helper.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c index a0e31c0c25..7f607c3afd 100644 --- a/target/hppa/op_helper.c +++ b/target/hppa/op_helper.c @@ -338,7 +338,7 @@ target_ulong HELPER(probe)(CPUHPPAState *env, target_ulong addr, #ifdef CONFIG_USER_ONLY return page_check_range(addr, 1, want); #else - int prot, excp; + int prot, excp, mmu_idx; hwaddr phys; trace_hppa_tlb_probe(addr, level, want); @@ -347,7 +347,8 @@ target_ulong HELPER(probe)(CPUHPPAState *env, target_ulong addr, return 0; } - excp = hppa_get_physical_address(env, addr, level, 0, &phys, + mmu_idx = PRIV_P_TO_MMU_IDX(level, env->psw & PSW_P); + excp = hppa_get_physical_address(env, addr, mmu_idx, 0, &phys, &prot, NULL); if (excp >= 0) { if (env->psw & PSW_Q) { From e722e5a11212f3080456dea1c3cc5b231d1d3a2d Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Fri, 10 Nov 2023 01:11:24 +0100 Subject: [PATCH 957/974] target/hppa: Fix calculation of CR_IIASQ back register Need to use iasq_b and iaoq_b to determine back register of CR_IIASQ. This fixes random faults when booting up Linux user space. Signed-off-by: Helge Deller Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson --- target/hppa/int_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/hppa/int_helper.c b/target/hppa/int_helper.c index 467ee7daf5..98e9d688f6 100644 --- a/target/hppa/int_helper.c +++ b/target/hppa/int_helper.c @@ -126,7 +126,7 @@ void hppa_cpu_do_interrupt(CPUState *cs) env->cr[CR_IIASQ] = hppa_form_gva_psw(old_psw, env->iasq_f, env->iaoq_f) >> 32; env->cr_back[0] = - hppa_form_gva_psw(old_psw, env->iasq_f, env->iaoq_f) >> 32; + hppa_form_gva_psw(old_psw, env->iasq_b, env->iaoq_b) >> 32; } else { env->cr[CR_IIASQ] = 0; env->cr_back[0] = 0; From 2a23f0f1189666fe453027eca60daf03c561bfd6 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Fri, 10 Nov 2023 20:27:43 +0100 Subject: [PATCH 958/974] target/hppa: Fix possible overflow in TLB size calculation Coverty found that the shift of TARGET_PAGE_SIZE (32-bit type) might overflow. Fix it by casting TARGET_PAGE_SIZE to a 64-bit type before doing the shift (CID 1523902 and CID 1523908). Reported-by: Peter Maydell Signed-off-by: Helge Deller Message-Id: Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson --- target/hppa/mem_helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c index 858ce6ec7f..a13f200359 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -450,7 +450,7 @@ static void itlbt_pa20(CPUHPPAState *env, target_ulong r1, int mask_shift; mask_shift = 2 * (r1 & 0xf); - va_size = TARGET_PAGE_SIZE << mask_shift; + va_size = (uint64_t)TARGET_PAGE_SIZE << mask_shift; va_b &= -va_size; va_e = va_b + va_size - 1; @@ -505,7 +505,7 @@ static void ptlb_work(CPUState *cpu, run_on_cpu_data data) */ end = start & 0xf; start &= TARGET_PAGE_MASK; - end = TARGET_PAGE_SIZE << (2 * end); + end = (vaddr)TARGET_PAGE_SIZE << (2 * end); end = start + end - 1; hppa_flush_tlb_range(env, start, end); From 17fe594c59c7a7304c663534ffb0853eb7e903d5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 7 Nov 2023 11:33:45 -0800 Subject: [PATCH 959/974] target/hppa: Introduce MMU_IDX_MMU_DISABLED MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reduce the number of direct checks against MMU_PHYS_IDX. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hppa/cpu.h | 1 + target/hppa/mem_helper.c | 4 ++-- target/hppa/translate.c | 20 +++++++++++--------- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index cecec59700..6c0f104661 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -41,6 +41,7 @@ #define MMU_USER_P_IDX 14 #define MMU_PHYS_IDX 15 +#define MMU_IDX_MMU_DISABLED(MIDX) ((MIDX) == MMU_PHYS_IDX) #define MMU_IDX_TO_PRIV(MIDX) (((MIDX) - MMU_KERNEL_IDX) / 2) #define MMU_IDX_TO_P(MIDX) (((MIDX) - MMU_KERNEL_IDX) & 1) #define PRIV_P_TO_MMU_IDX(PRIV, P) ((PRIV) * 2 + !!(P) + MMU_KERNEL_IDX) diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c index a13f200359..af8e86699d 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -367,8 +367,8 @@ bool hppa_cpu_tlb_fill(CPUState *cs, vaddr addr, int size, trace_hppa_tlb_fill_excp(env, addr, size, type, mmu_idx); /* Failure. Raise the indicated exception. */ - raise_exception_with_ior(env, excp, retaddr, - addr, mmu_idx == MMU_PHYS_IDX); + raise_exception_with_ior(env, excp, retaddr, addr, + MMU_IDX_MMU_DISABLED(mmu_idx)); } trace_hppa_tlb_fill_success(env, addr & TARGET_PAGE_MASK, diff --git a/target/hppa/translate.c b/target/hppa/translate.c index bb1b65fef0..727dd8a829 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -69,9 +69,11 @@ typedef struct DisasContext { } DisasContext; #ifdef CONFIG_USER_ONLY -#define UNALIGN(C) (C)->unalign +#define UNALIGN(C) (C)->unalign +#define MMU_DISABLED(C) false #else -#define UNALIGN(C) MO_ALIGN +#define UNALIGN(C) MO_ALIGN +#define MMU_DISABLED(C) MMU_IDX_MMU_DISABLED((C)->mmu_idx) #endif /* Note that ssm/rsm instructions number PSW_W and PSW_E differently. */ @@ -1375,7 +1377,7 @@ static void do_load_32(DisasContext *ctx, TCGv_i32 dest, unsigned rb, assert(ctx->null_cond.c == TCG_COND_NEVER); form_gva(ctx, &addr, &ofs, rb, rx, scale, disp, sp, modify, - ctx->mmu_idx == MMU_PHYS_IDX); + MMU_DISABLED(ctx)); tcg_gen_qemu_ld_i32(dest, addr, ctx->mmu_idx, mop | UNALIGN(ctx)); if (modify) { save_gpr(ctx, rb, ofs); @@ -1393,7 +1395,7 @@ static void do_load_64(DisasContext *ctx, TCGv_i64 dest, unsigned rb, assert(ctx->null_cond.c == TCG_COND_NEVER); form_gva(ctx, &addr, &ofs, rb, rx, scale, disp, sp, modify, - ctx->mmu_idx == MMU_PHYS_IDX); + MMU_DISABLED(ctx)); tcg_gen_qemu_ld_i64(dest, addr, ctx->mmu_idx, mop | UNALIGN(ctx)); if (modify) { save_gpr(ctx, rb, ofs); @@ -1411,7 +1413,7 @@ static void do_store_32(DisasContext *ctx, TCGv_i32 src, unsigned rb, assert(ctx->null_cond.c == TCG_COND_NEVER); form_gva(ctx, &addr, &ofs, rb, rx, scale, disp, sp, modify, - ctx->mmu_idx == MMU_PHYS_IDX); + MMU_DISABLED(ctx)); tcg_gen_qemu_st_i32(src, addr, ctx->mmu_idx, mop | UNALIGN(ctx)); if (modify) { save_gpr(ctx, rb, ofs); @@ -1429,7 +1431,7 @@ static void do_store_64(DisasContext *ctx, TCGv_i64 src, unsigned rb, assert(ctx->null_cond.c == TCG_COND_NEVER); form_gva(ctx, &addr, &ofs, rb, rx, scale, disp, sp, modify, - ctx->mmu_idx == MMU_PHYS_IDX); + MMU_DISABLED(ctx)); tcg_gen_qemu_st_i64(src, addr, ctx->mmu_idx, mop | UNALIGN(ctx)); if (modify) { save_gpr(ctx, rb, ofs); @@ -3078,7 +3080,7 @@ static bool trans_ldc(DisasContext *ctx, arg_ldst *a) } form_gva(ctx, &addr, &ofs, a->b, a->x, a->scale ? a->size : 0, - a->disp, a->sp, a->m, ctx->mmu_idx == MMU_PHYS_IDX); + a->disp, a->sp, a->m, MMU_DISABLED(ctx)); /* * For hppa1.1, LDCW is undefined unless aligned mod 16. @@ -3108,7 +3110,7 @@ static bool trans_stby(DisasContext *ctx, arg_stby *a) nullify_over(ctx); form_gva(ctx, &addr, &ofs, a->b, 0, 0, a->disp, a->sp, a->m, - ctx->mmu_idx == MMU_PHYS_IDX); + MMU_DISABLED(ctx)); val = load_gpr(ctx, a->r); if (a->a) { if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { @@ -3142,7 +3144,7 @@ static bool trans_stdby(DisasContext *ctx, arg_stby *a) nullify_over(ctx); form_gva(ctx, &addr, &ofs, a->b, 0, 0, a->disp, a->sp, a->m, - ctx->mmu_idx == MMU_PHYS_IDX); + MMU_DISABLED(ctx)); val = load_gpr(ctx, a->r); if (a->a) { if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { From 451d993d58fb577425f8a79cdaf4ee213a72f702 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 7 Nov 2023 12:13:17 -0800 Subject: [PATCH 960/974] target/hppa: Replace MMU_PHYS_IDX with MMU_ABS_IDX, MMU_ABS_W_IDX Align the language with pa2.0, separating absolute and physical. The translation from absolute to physical depends on PSW.W, and we prefer not to flush between changes, therefore use 2 mmu_idx. Signed-off-by: Richard Henderson --- target/hppa/cpu.h | 26 +++++++++++++------------ target/hppa/mem_helper.c | 41 ++++++++++++++++++++-------------------- target/hppa/translate.c | 6 +++--- 3 files changed, 38 insertions(+), 35 deletions(-) diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 6c0f104661..bcfed04f7c 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -31,24 +31,25 @@ basis. It's probably easier to fall back to a strong memory model. */ #define TCG_GUEST_DEFAULT_MO TCG_MO_ALL -#define MMU_KERNEL_IDX 7 -#define MMU_KERNEL_P_IDX 8 -#define MMU_PL1_IDX 9 -#define MMU_PL1_P_IDX 10 -#define MMU_PL2_IDX 11 -#define MMU_PL2_P_IDX 12 -#define MMU_USER_IDX 13 -#define MMU_USER_P_IDX 14 -#define MMU_PHYS_IDX 15 +#define MMU_ABS_W_IDX 6 +#define MMU_ABS_IDX 7 +#define MMU_KERNEL_IDX 8 +#define MMU_KERNEL_P_IDX 9 +#define MMU_PL1_IDX 10 +#define MMU_PL1_P_IDX 11 +#define MMU_PL2_IDX 12 +#define MMU_PL2_P_IDX 13 +#define MMU_USER_IDX 14 +#define MMU_USER_P_IDX 15 -#define MMU_IDX_MMU_DISABLED(MIDX) ((MIDX) == MMU_PHYS_IDX) +#define MMU_IDX_MMU_DISABLED(MIDX) ((MIDX) < MMU_KERNEL_IDX) #define MMU_IDX_TO_PRIV(MIDX) (((MIDX) - MMU_KERNEL_IDX) / 2) #define MMU_IDX_TO_P(MIDX) (((MIDX) - MMU_KERNEL_IDX) & 1) #define PRIV_P_TO_MMU_IDX(PRIV, P) ((PRIV) * 2 + !!(P) + MMU_KERNEL_IDX) #define TARGET_INSN_START_EXTRA_WORDS 2 -/* No need to flush MMU_PHYS_IDX */ +/* No need to flush MMU_ABS*_IDX */ #define HPPA_MMU_FLUSH_MASK \ (1 << MMU_KERNEL_IDX | 1 << MMU_KERNEL_P_IDX | \ 1 << MMU_PL1_IDX | 1 << MMU_PL1_P_IDX | \ @@ -288,7 +289,8 @@ static inline int cpu_mmu_index(CPUHPPAState *env, bool ifetch) if (env->psw & (ifetch ? PSW_C : PSW_D)) { return PRIV_P_TO_MMU_IDX(env->iaoq_f & 3, env->psw & PSW_P); } - return MMU_PHYS_IDX; /* mmu disabled */ + /* mmu disabled */ + return env->psw & PSW_W ? MMU_ABS_W_IDX : MMU_ABS_IDX; #endif } diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c index af8e86699d..7bc456d4ee 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -53,17 +53,6 @@ hwaddr hppa_abs_to_phys_pa2_w0(vaddr addr) return (addr & MAKE_64BIT_MASK(0, 24)) | MAKE_64BIT_MASK(60, 4); } -static hwaddr hppa_abs_to_phys(CPUHPPAState *env, vaddr addr) -{ - if (!hppa_is_pa20(env)) { - return addr; - } else if (env->psw & PSW_W) { - return hppa_abs_to_phys_pa2_w1(addr); - } else { - return hppa_abs_to_phys_pa2_w0(addr); - } -} - static HPPATLBEntry *hppa_find_tlb(CPUHPPAState *env, vaddr addr) { IntervalTreeNode *i = interval_tree_iter_first(&env->tlb_root, addr, addr); @@ -161,9 +150,22 @@ int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx, *tlb_entry = NULL; } - /* Virtual translation disabled. Direct map virtual to physical. */ - if (mmu_idx == MMU_PHYS_IDX) { - phys = addr; + /* Virtual translation disabled. Map absolute to physical. */ + if (MMU_IDX_MMU_DISABLED(mmu_idx)) { + switch (mmu_idx) { + case MMU_ABS_W_IDX: + phys = hppa_abs_to_phys_pa2_w1(addr); + break; + case MMU_ABS_IDX: + if (hppa_is_pa20(env)) { + phys = hppa_abs_to_phys_pa2_w0(addr); + } else { + phys = (uint32_t)addr; + } + break; + default: + g_assert_not_reached(); + } prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; goto egress; } @@ -261,7 +263,7 @@ int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx, } egress: - *pphys = phys = hppa_abs_to_phys(env, phys); + *pphys = phys; *pprot = prot; trace_hppa_tlb_get_physical_address(env, ret, prot, addr, phys); return ret; @@ -271,16 +273,15 @@ hwaddr hppa_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { HPPACPU *cpu = HPPA_CPU(cs); hwaddr phys; - int prot, excp; + int prot, excp, mmu_idx; /* If the (data) mmu is disabled, bypass translation. */ /* ??? We really ought to know if the code mmu is disabled too, in order to get the correct debugging dumps. */ - if (!(cpu->env.psw & PSW_D)) { - return hppa_abs_to_phys(&cpu->env, addr); - } + mmu_idx = (cpu->env.psw & PSW_D ? MMU_KERNEL_IDX : + cpu->env.psw & PSW_W ? MMU_ABS_W_IDX : MMU_ABS_IDX); - excp = hppa_get_physical_address(&cpu->env, addr, MMU_KERNEL_IDX, 0, + excp = hppa_get_physical_address(&cpu->env, addr, mmu_idx, 0, &phys, &prot, NULL); /* Since we're translating for debugging, the only error that is a diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 727dd8a829..4a4830c3e3 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -3172,7 +3172,7 @@ static bool trans_lda(DisasContext *ctx, arg_ldst *a) int hold_mmu_idx = ctx->mmu_idx; CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); - ctx->mmu_idx = MMU_PHYS_IDX; + ctx->mmu_idx = ctx->tb_flags & PSW_W ? MMU_ABS_W_IDX : MMU_ABS_IDX; trans_ld(ctx, a); ctx->mmu_idx = hold_mmu_idx; return true; @@ -3183,7 +3183,7 @@ static bool trans_sta(DisasContext *ctx, arg_ldst *a) int hold_mmu_idx = ctx->mmu_idx; CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); - ctx->mmu_idx = MMU_PHYS_IDX; + ctx->mmu_idx = ctx->tb_flags & PSW_W ? MMU_ABS_W_IDX : MMU_ABS_IDX; trans_st(ctx, a); ctx->mmu_idx = hold_mmu_idx; return true; @@ -4435,7 +4435,7 @@ static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->privilege = (ctx->tb_flags >> TB_FLAG_PRIV_SHIFT) & 3; ctx->mmu_idx = (ctx->tb_flags & PSW_D ? PRIV_P_TO_MMU_IDX(ctx->privilege, ctx->tb_flags & PSW_P) - : MMU_PHYS_IDX); + : ctx->tb_flags & PSW_W ? MMU_ABS_W_IDX : MMU_ABS_IDX); /* Recover the IAOQ values from the GVA + PRIV. */ uint64_t cs_base = ctx->base.tb->cs_base; From fa71b4f84fb88c034ece3f3928e4d3a72e49b82f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 7 Nov 2023 09:28:56 -0800 Subject: [PATCH 961/974] target/hppa: Reduce TARGET_PHYS_ADDR_SPACE_BITS to 40 This is the value that is supported by both PA-8500 and Astro. If we support a larger address space than expected, we trip up software that did not fill in all of the page table bits, expecting them to be ignored. Signed-off-by: Richard Henderson --- target/hppa/cpu-param.h | 3 ++- target/hppa/mem_helper.c | 50 ++++++++++++++++++++++++++-------------- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/target/hppa/cpu-param.h b/target/hppa/cpu-param.h index 6746869a3b..bb3d7ef6f7 100644 --- a/target/hppa/cpu-param.h +++ b/target/hppa/cpu-param.h @@ -14,7 +14,8 @@ # define TARGET_PHYS_ADDR_SPACE_BITS 32 # define TARGET_VIRT_ADDR_SPACE_BITS 32 #else -# define TARGET_PHYS_ADDR_SPACE_BITS 64 +/* ??? PA-8000 through 8600 have 40 bits; PA-8700 and 8900 have 44 bits. */ +# define TARGET_PHYS_ADDR_SPACE_BITS 40 # define TARGET_VIRT_ADDR_SPACE_BITS 64 #endif diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c index 7bc456d4ee..08abd1a9f9 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -27,30 +27,39 @@ hwaddr hppa_abs_to_phys_pa2_w1(vaddr addr) { - if (likely(extract64(addr, 58, 4) != 0xf)) { - /* Memory address space */ - return addr & MAKE_64BIT_MASK(0, 62); - } - if (extract64(addr, 54, 4) != 0) { - /* I/O address space */ - return addr | MAKE_64BIT_MASK(62, 2); - } - /* PDC address space */ - return (addr & MAKE_64BIT_MASK(0, 54)) | MAKE_64BIT_MASK(60, 4); + /* + * Figure H-8 "62-bit Absolute Accesses when PSW W-bit is 1" describes + * an algorithm in which a 62-bit absolute address is transformed to + * a 64-bit physical address. This must then be combined with that + * pictured in Figure H-11 "Physical Address Space Mapping", in which + * the full physical address is truncated to the N-bit physical address + * supported by the implementation. + * + * Since the supported physical address space is below 54 bits, the + * H-8 algorithm is moot and all that is left is to truncate. + */ + QEMU_BUILD_BUG_ON(TARGET_PHYS_ADDR_SPACE_BITS > 54); + return sextract64(addr, 0, TARGET_PHYS_ADDR_SPACE_BITS); } hwaddr hppa_abs_to_phys_pa2_w0(vaddr addr) { + /* + * See Figure H-10, "Absolute Accesses when PSW W-bit is 0", + * combined with Figure H-11, as above. + */ if (likely(extract32(addr, 28, 4) != 0xf)) { /* Memory address space */ - return addr & MAKE_64BIT_MASK(0, 32); - } - if (extract32(addr, 24, 4) != 0) { + addr = (uint32_t)addr; + } else if (extract32(addr, 24, 4) != 0) { /* I/O address space */ - return addr | MAKE_64BIT_MASK(32, 32); + addr = (int32_t)addr; + } else { + /* PDC address space */ + addr &= MAKE_64BIT_MASK(0, 24); + addr |= -1ull << (TARGET_PHYS_ADDR_SPACE_BITS - 4); } - /* PDC address space */ - return (addr & MAKE_64BIT_MASK(0, 24)) | MAKE_64BIT_MASK(60, 4); + return addr; } static HPPATLBEntry *hppa_find_tlb(CPUHPPAState *env, vaddr addr) @@ -460,7 +469,14 @@ static void itlbt_pa20(CPUHPPAState *env, target_ulong r1, ent->itree.start = va_b; ent->itree.last = va_e; - ent->pa = (r1 << 7) & (TARGET_PAGE_MASK << mask_shift); + + /* Extract all 52 bits present in the page table entry. */ + ent->pa = r1 << (TARGET_PAGE_BITS - 5); + /* Align per the page size. */ + ent->pa &= TARGET_PAGE_MASK << mask_shift; + /* Ignore the bits beyond physical address space. */ + ent->pa = sextract64(ent->pa, 0, TARGET_PHYS_ADDR_SPACE_BITS); + ent->t = extract64(r2, 61, 1); ent->d = extract64(r2, 60, 1); ent->b = extract64(r2, 59, 1); From 8066102df12fecfac7b076bd6aa208e63f8f0b04 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Thu, 9 Nov 2023 18:12:20 +0100 Subject: [PATCH 962/974] hw/pci-host/astro: Fix boot for C3700 machine Apply the "32-bit PCI addressing on 40-bit Runway" as the default iommu transformation. This allows PCI devices to dma PDC memory. Signed-off-by: Helge Deller Acked-by: Richard Henderson Signed-off-by: Richard Henderson --- hw/pci-host/astro.c | 73 ++++++++++++++++++----------------------- hw/pci-host/meson.build | 2 +- 2 files changed, 33 insertions(+), 42 deletions(-) diff --git a/hw/pci-host/astro.c b/hw/pci-host/astro.c index bd226581af..7d68ccee7e 100644 --- a/hw/pci-host/astro.c +++ b/hw/pci-host/astro.c @@ -32,6 +32,7 @@ #include "hw/pci-host/astro.h" #include "hw/hppa/hppa_hardware.h" #include "migration/vmstate.h" +#include "target/hppa/cpu.h" #include "trace.h" #include "qom/object.h" @@ -268,22 +269,6 @@ static const MemoryRegionOps elroy_config_addr_ops = { }; -/* - * A subroutine of astro_translate_iommu that builds an IOMMUTLBEntry using the - * given translated address and mask. - */ -static bool make_iommu_tlbe(hwaddr addr, hwaddr taddr, hwaddr mask, - IOMMUTLBEntry *ret) -{ - hwaddr tce_mask = ~((1ull << 12) - 1); - ret->target_as = &address_space_memory; - ret->iova = addr & tce_mask; - ret->translated_addr = taddr & tce_mask; - ret->addr_mask = ~tce_mask; - ret->perm = IOMMU_RW; - return true; -} - /* Handle PCI-to-system address translation. */ static IOMMUTLBEntry astro_translate_iommu(IOMMUMemoryRegion *iommu, hwaddr addr, @@ -291,53 +276,59 @@ static IOMMUTLBEntry astro_translate_iommu(IOMMUMemoryRegion *iommu, int iommu_idx) { AstroState *s = container_of(iommu, AstroState, iommu); - IOMMUTLBEntry ret = { - .target_as = &address_space_memory, - .iova = addr, - .translated_addr = 0, - .addr_mask = ~(hwaddr)0, - .perm = IOMMU_NONE, - }; - hwaddr pdir_ptr, index, a, ibase; + hwaddr pdir_ptr, index, ibase; hwaddr addr_mask = 0xfff; /* 4k translation */ uint64_t entry; #define IOVP_SHIFT 12 /* equals PAGE_SHIFT */ #define PDIR_INDEX(iovp) ((iovp) >> IOVP_SHIFT) -#define IOVP_MASK PAGE_MASK #define SBA_PDIR_VALID_BIT 0x8000000000000000ULL + addr &= ~addr_mask; + + /* + * Default translation: "32-bit PCI Addressing on 40-bit Runway". + * For addresses in the 32-bit memory address range ... and then + * language which not-coincidentally matches the PSW.W=0 mapping. + */ + if (addr <= UINT32_MAX) { + entry = hppa_abs_to_phys_pa2_w0(addr); + } else { + entry = addr; + } + /* "range enable" flag cleared? */ if ((s->tlb_ibase & 1) == 0) { - make_iommu_tlbe(addr, addr, addr_mask, &ret); - return ret; + goto skip; } - a = addr; ibase = s->tlb_ibase & ~1ULL; - if ((a & s->tlb_imask) != ibase) { + if ((addr & s->tlb_imask) != ibase) { /* do not translate this one! */ - make_iommu_tlbe(addr, addr, addr_mask, &ret); - return ret; + goto skip; } - index = PDIR_INDEX(a); + + index = PDIR_INDEX(addr); pdir_ptr = s->tlb_pdir_base + index * sizeof(entry); entry = ldq_le_phys(&address_space_memory, pdir_ptr); + if (!(entry & SBA_PDIR_VALID_BIT)) { /* I/O PDIR entry valid ? */ - g_assert_not_reached(); - goto failure; + /* failure */ + return (IOMMUTLBEntry) { .perm = IOMMU_NONE }; } + entry &= ~SBA_PDIR_VALID_BIT; entry >>= IOVP_SHIFT; entry <<= 12; - entry |= addr & 0xfff; - make_iommu_tlbe(addr, entry, addr_mask, &ret); - goto success; - failure: - ret = (IOMMUTLBEntry) { .perm = IOMMU_NONE }; - success: - return ret; + skip: + return (IOMMUTLBEntry) { + .target_as = &address_space_memory, + .iova = addr, + .translated_addr = entry, + .addr_mask = addr_mask, + .perm = IOMMU_RW, + }; } static AddressSpace *elroy_pcihost_set_iommu(PCIBus *bus, void *opaque, diff --git a/hw/pci-host/meson.build b/hw/pci-host/meson.build index de7bfb5a62..36d5ab756f 100644 --- a/hw/pci-host/meson.build +++ b/hw/pci-host/meson.build @@ -29,7 +29,7 @@ pci_ss.add(when: 'CONFIG_MV64361', if_true: files('mv64361.c')) pci_ss.add(when: 'CONFIG_VERSATILE_PCI', if_true: files('versatile.c')) # HPPA devices -pci_ss.add(when: 'CONFIG_ASTRO', if_true: files('astro.c')) +specific_ss.add(when: 'CONFIG_ASTRO', if_true: files('astro.c')) pci_ss.add(when: 'CONFIG_DINO', if_true: files('dino.c')) system_ss.add_all(when: 'CONFIG_PCI', if_true: pci_ss) From e274d2a777de168082b36a31b6b5ea2b9185e960 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Sat, 11 Nov 2023 20:13:53 +0100 Subject: [PATCH 963/974] hw/hppa: Move software power button address to page zero Something appears to be off between the 64-bit CPU, the 32-bit PDC (SeaBIOS-hppa firmware), and the 64-bit kernel in addressing the power button address in high-mapped firmware memory. Use a 32-bit value at PAGE0->pad0[4] instead. Signed-off-by: Helge Deller Signed-off-by: Richard Henderson --- hw/hppa/machine.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index a3222d3a96..f7d9ce9b46 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -36,7 +36,8 @@ #define MIN_SEABIOS_HPPA_VERSION 10 /* require at least this fw version */ -#define HPA_POWER_BUTTON (FIRMWARE_END - 0x10) +/* Power button address at &PAGE0->pad[4] */ +#define HPA_POWER_BUTTON (0x40 + 4 * sizeof(uint32_t)) #define enable_lasi_lan() 0 From ec6f3fc3ef0bc0bb8cbec0a62767a32e3f00aa74 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Sat, 11 Nov 2023 20:22:12 +0100 Subject: [PATCH 964/974] target/hppa: Update to SeaBIOS-hppa from version 10 to 12 SEABIOS_HPPA_VERSION 12 contains those fixes and enhancements: - Reduce debug level - Update README file for PA-RISC - Fix debug name of CPU_HPA_xx if xx >= 10 - Disable device indexing SEABIOS_HPPA_VERSION 11 contains those fixes and enhancements (mostly to enable support for 64-bit Linux kernel): - Fixed 64-bit CPU detection via "mfctl,w" instruction - Implement PDC_PSW for 64-bit CPUs - Added PAT PDC functions: - PDC_PAT_CELL - PDC_PAT_CHASSIS_LOG - PDC_PAT_PD_GET_ADDR_MAP - PDC_PAT_CPU - Fix return value of PDC_CACHE_RET_SPID space-id bits - Introduce new default software IDs for the machines - Fix CPU and FPU model numbers - Fix 64-bit SMP rendezvous - Fix Linux 64-bit kernel crash in STI due to usage of unsigned 32-bit "next_font" pointer in sti header files - Fix graphics output to LASI artist card on PA2.0 machines - More USB OHCI endianess fixes - Fixes which make ODE run on B160L - Fixes which make ODE detect Astro Runway port and CPUs - Implement "firmware unlocking" via PDC_MODEL/PDC_MODEL_CAPABILITIES call - Add subfunction 2 for PDC_MODEL_VERSIONS Signed-off-by: Helge Deller Acked-by: Richard Henderson Signed-off-by: Richard Henderson --- pc-bios/hppa-firmware.img | Bin 755480 -> 681332 bytes roms/seabios-hppa | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/pc-bios/hppa-firmware.img b/pc-bios/hppa-firmware.img index e976c0cc933214ef3d6f420c5936f6972ae8c343..9a2d54f26bf8e9ec29475adc84e8aad247fa1a08 100644 GIT binary patch literal 681332 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%0DNBINk6{HRMc+7IIS8PIa~H0(_Q&D}%U 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
-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

(ZS7?l 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%0AyB# zzOWV6emXsHF(b$QhpRBM+OKZj_0{@ach2rAsxA5C{G_&u^_4;j_Msh^L#~^QqQydUG`TKlUz?P;Jt@_N^q+RY^k0d z`R5bYbBKTWF(Bv9HXs#xC3ksaYJSSn|5qEMxB+hZ|Xr$!TLcH&GKb_Q~87wqaHp|4`E%bk?@ct%aAd`)R#q zOF|i=yE7Tr(#HB6vM}G!G95tMaWR*FIq@4tnQbxg?pR*ZIhC>YJ5D#`j|WNr&QWiT z3&@?l)wISS6b!_sATof;xO0FOm4f}geZS;JUn8dZMXx!o5#)2&Zqz@g@NHnIZ4p713mqOll90h8o2EZn`@SV9lu(!TurT$Y`EY}0r209N z!90<9GS|p4>ovtW%qrtB@gy_9-`tX>YZ>W zQv%xdem+VXc0<`H<(+7N&$1bc`|4_eilbglr5lbVt#91;BQ|HQ zSc}GCG9Nz81cH(p1hw&4a`zh_@|QJ>wbcTP-(LQ0s7~_7!AzybJA_3DJFZoiL+72v z8gxXJ^>7e&WYQFRl84cgl%rk_c0oqr1l>mYYGA3+01Yxf*k-t5 zX9~PxR>;D_;7ZZ8x8tKlKY-sC``U4n&M48uFiVWQ!zTFqYKp>gD5Y~$%mz{IM!}Oh zlv)i=s>>x#D~Gg9Ieqo2@F1sWH))L$tr$y;tqBzLV{^VV|6__(;;R<>V{^WDCd`D^ zo3vJmR;(qKTFV%aWPSEq&pnrd7P5@Yj4OjXpNa<3r^a%$Z4JZyNG(i2+eFsFhh0aP zT_nV;K{J}l9;sWA9?kggBTMz#n#FL+X4@&iV3+Dc+&}%tMY6|*;X(Scn_=0Fo3!XcI^A%feyIcJ~h))(_Pb-2QS2y>O8ik)mqw`HC0I-MDV|x&Bxu{w;V|h>pqK# zklZZmzxA-JO&=`W>0@Y~|55P$jp29*&WcHW?oYHA_!BPOg>ebIN$>%>1#)bG7r9Ok z6kBUW`e}4ORC_m96Yg#+o={VEVi?`lvn4$wy8Cq}d}L2wHu4J=DXoi%SE`SKoCe5Y zzKoi_Sk2_Du2ks>B~k3g!}nm z1FZZvBD~HJZgb+@J^hu3y&BLsXt(Q?+4>#uPzhw9quYTFjaf}EOq8E**@1I`k*w=< zW}QQ!>u0{@7TBBV<(t=}7hDTqnST08JQ!!LmxZ&-0W5pSm?@3J*Hx_?s{yS1v@tor zzRzUOMXlvFn-5M^iwPJRT0tLJ*Wv6zcNDSjW+(sXBuxy6ge!Q=_FX&hJuX@bM1q=R znG$ZUFMmKcJA{@fDBGzVmz;yJ6#M65H4oKDIe5_R?Wa{kXQM1)P2Mpdmg2keCu-eY zH!M;qklg?Ws5$^;k|OYx z3+DUDqq|L;R{@uObfC#_r{EzIf25{(^t7pIHi@@BI#KUVoP)wjUzf_hFs@J2Drb`1 zzyH6I^xL)n5)5I3Av>rN9%UvOh4wF)(sv>Eygh zAS$ys^A@*4$KK9J`yCAXK)g<1M6;Zm)vnK$?!6amHHqQ4nn)zQF78LyglYR}ED+;* zA*w!DM}@n_eLpqiKoCt; z5B=<68?hIYG^*EC%wz|oV8@V+z-c?q$nEIfojahv3<{f}%4m?AoSM`8IOy6nao8-Z zX1M_qb!>y5Pb&D-8aETfG-h+>xJDltzoJ3IBR-m~hM!GgPG#oSDdu+qGfAo|jwJ1O za2~r2_1m`E@KxJL;N3)XD!<>|)gQhl(b$Kel=cMg2WBk)#XL}7%uj|GdQbmm+NGKE z!6|r22%f)3K~ojbkKbl+BwSuD9FvM-7H2C42{1nZW?IDIdu()j(6deF*(hDB7^#%v z-}+^P{M!p4x=?(-Y?%wi2e?vvMLVjqh2?_DwRGLY2CEr6dP@FRl+3Bpj}Qj-N>RSI zUyAbnK`F`ykBZ#Kb;+CyI9L}O+$9}x!dc~O&xkzKZw)OKcXTu-^}AVQQm&PW+-kMXVdi1>oo0u0Y53l+RZ15 z+BlV>YIkgY+4fRt6!|sWU3P$N!R~}9;A1bPrzqZ#98Wgb{h;3^ZL5Z zM5%mijvS@=QcG~x+1Q~b_|Y1dFbF!myv!d+(}NPdte#qQdT@XAsH}Uisg(Jx(ZkWB zY*+IvcJV6`3JIocG2%Yj!<^HkJ?s*?y_X&QsJKF`a?B+c)q4qhYK`Xf zGo8>!3UOV=fVQW}$gbs4pAo|=>jr36LIN2mRi=Fuo8mfTN^8@S8?xZ+?BSEH2Q`AS zgs^YU)7PYvGwY3~$Nb!jADVkHtkxs(P~PFwwq_lKBhF+^=wg?YKB~zx8q(8Q;@hM2 z0c55DpZhwgOQ|PwtV1V5?yN*>Rm6IR*1Yzkxdl>`hRh9WrcLCgBtV!86B$$ z!#6pN#O@7$WN%LOtOY~+5KE#&@9zElvtPG{&gz;qd+vgrEvqd_@#&pmC#xoHc={DC zgoQ>xDTgnalGv!`@XCivRC9RMCD%j%Q+TK`sulFwqmeNmzMNX_B}2p99fMHCSDxmS zEf{n?T^rBJG^8*WQ?QsJRcfCV#*0Lo2W3`P9!(){H3+EQ<;F%{IOTR3*!81I8M_!% zpBRUpc+xjY`eV8%AYk(@z?ecQmA< zqF`oJ_>7}MFgq%IW*cFnqOOLX7PE;QgZ%Sb7!d#JxDB;FiP&w5&70V=%^)BM%tQiO zC|In{+>V>rvH_beiTmqi%f^X-HwJ(h_m2e)yBF;CPlZV;tN6Zwn+&@kwo}!1ODqk$ z$~uFVNX_C$qXv?oJJKTD6s>imSS0h4H^5>f(IeFj0!$HaxX@xZ@e!FhZxqNiI2bpy z4G?C^FRBJ-$Jx9~dGb;Z5;+Wc58@XxKOQx>pprk5a6@H`<1^(^QL}TYY3pUtq+4qU&Kub#0-q;RDDlpDb#mL?_StUg!EE_;uT z!+o!*zekpViR?A-uzMtDNm?1xX)7QabCZE zMl$p#T-y**)DUg&r|k&Nwpj_AAm0|qy9F0WEdx-&fI+hjkm)>hu@AanYL?N#>(|BI zbYXZw;+stD`6KO#5qs=nFHkkdc2^#2sN8j|$p1;@F4VD&HOE>ikJa<-_2q%=@%ax<4NGK*f1;e?)V2;mzlO)9FYLfY2r8uXS% z$r2Rsy6Y2aYa6%k*xB$#tUXeF(d*Tx>x+2kjr6K^3%&YWl6FdwwT)Y^>ngFKmFr3V z7fN9WBsOt9srWQPthH)AxrK1HAz6RH!cZf?4^kBlS3aG*l|VaY7N56})N&zG*C~8# zQrJLu-%y+6Kfqo+gt6JIEEdBDa38SR0fK#3*>Y0rLUxUf3KcBx?wzIpKU))MoDG`9|6i<&~>;uI-bDzQB zS4>^OVNSszcr_>YnMro9$n5B~khA?#9qLq4J(S#vnHBV$U&(Jh3|%3-$NM{s4lY)1tu$74mA4zDB{(A& z$(nQYa=39^^C}NIbHiP%mT@l9>ESL--G0|Hllz%t-X5~O@tD6^fFaj0Z!U>dRYe-I$aXe`8Xt&kx%3=hH!Ze#L?wyU#v$-`CHu_8+=g((s7*+Xxm+H%W#57?>BPCI13piM81 z{J;4)YhLQJMiq#D>5%N6ZCbr_=+AN+4x??fGY6}h;$Avr);YR#@@M$9zG0^LZhp42 zR+CRu`Pp7J>S=HTIYNPkkI#-CjT82>*V+q*Q}<7~&t5|`uN>o{6zH;UmmSJjhm=sA zKheZSHUF#XO&Z%!`Ev3(fj8*Jh;{Q=qr_gRS98bOYHS?{VXBqcXN;C)tgyAl3Kbi> zhh@@?VSbFMhzE|)^$=nGK6u!^rL39>1W0@cMfS3|_wEBS07daeMFo(3EaV|I?y^Gx ztv$N3(SdXMi5!2(0xNBuOC`=$e9}Q}Vz8~II*UR1{tEF7cu-ch_SIOtsXkmmI~!7-lVkIlL&CxUSG)_V+Oio z$f=oVt0n%EmdJ^^l^C#WN45v(f-f<~T8RhJ6625rx_K+CunU^&I#Y&FrTVs$-a~_W z_8CLn=fO&2&$!+_Bo(9o(YCjYIcdKSx{=&hJQw~7;^56Y6Tpws0M26=uTkR zSs415F1+}xvDL=Gt*VfX7PUdym*?m3yaacF2J%^B9XUEcbJReqoJB}B^67XXRRqt;o_2p+nsV{R}PVtob z@^i7&mp}89OBE`A?)tXAdMiv%FLVi87cc}cPk%@4Cdo0xV*8ZoS>X%ep_JsUy=9~M zGfaFl@z5R4-RV`As7p}60@=$5?)9m0sBDyC`ZEmbGmF|vEpky+#E|7~);<-}$3*Nq zD?&IMz#^M7e)cn_%=QJtvs8F}qi%b!43FT5_IYi+X0-Co@W;MqkYizdmc71ZN`6UF z8VV)shQe`Ng;`{N{6)7H+T9QB!bXXkkT0qI{F&5eD5f2LmG1QG_ln;zU2g*d) z`r+o>K4wP!Qo_uz`gE3A&t}h?s5G%><67;L65^~E6w(+A@e-lniPZ_2!+|`@@MZ1$qSUx~KzFxtQemS}qzajI%6^V0tXv(_5myrs@*? zS*z5nU+B{)<44bUe`ZWqOfQWGSM&{T?_jAoB=BAnMP`$r+{5h)&~2XsSjn zQn@Y4Z>!O9X&Ma5eZ@u#Uoi5vCXpSNiDzttVs)JJ8LLeT^usD>=EYu}1L^=PkX?v= z)JOZ{E+YdaupY`@XeQ^eYB^c%5H?Lc__lWK%Jwle&X`$tp8a^L@O#^|w1ywzEu(B? zr!5Rx>Dg5UwU4PDe4X7gRFcrVsu{eBmap&=`HlxD}{ zYAxw+TbEM$!ypeL7DOiAGl@Dz!%}thr9CdWKHDp%iidaTk3W<6?epHYEpb+(^rY)& zlk}t^G~{>&r4=6D@^HD78L1?H$rguEK2>J_;pb<13C39RVE#k{knFt%(U&otGK8Fz z;>_=RlFu&G6i+l%zO`{@c;}9o?qGO{=~Eb{&q7Q#On5%fIBEBk9P4bV{B>{5XN{1- z^f3smSTS0Bs|+gHUvT-!e_10109iM{X63~@v^~U@*eq%LLZ?V;ITNl5?1y*mzYtk$ zy8PMD2*_-k7Ptz`wTlH-R?X_nO6c+9OB--~%i=@QOWkxO6xe>|_I|(~PQl`owxz7) z>JT$1AgJXk{l<_U&&lI*)X$qK{iK1Vw-mR9-q4q~G=!!6uEOzQPqXSMDYr8b%sY_X z*owBM#$Eg6a7TGn#{5^YgCF+PSigf8^T4e3}wBEZGHdI;5wr*tlgflv`c&cnBTkE0ChnT_kt{}SIPj@{;sS2aF#0R z;@)4KcqU`x1up(@Oyi-whO-!1EdG;aXvO_+9 zNqVE&KRj!mnoDC-tKiE1bV+Y znQ4uT34B&-7~nBDg++6EAIgt%gTcqsj?fx2+~e%db*$UCluh!lkFfcavuyp8fk{zg z$8Oxyt7+A-0gL(vOPuWMXQA7|u|W<$5Qp`5gzMskPYtFk)Yq4R$dA|QIvMYhiGIGm zoL(pU`UTz&Q)NW|`ni47E!mZte|;6nwNPY3<{mJHB7n>Hc_}y-pLz)9reA0eHd4Lx zYevg9zJfW@kc+x=`BiC%4EI(Bd?R-TX$dOXmVIQZHfyyAspG> z7nwXY`FEUh&29W!7E77UXd%=UU7qwmL=C#~g}KnWK7jfT<%fpdp#>9lWFqu1PV|;( z+Ay|z>-sd)sBF{bW7F=9O&iTmrA_mdQOy?}S2GGj!#uUrTNq*_R&q0JX-j-JPSGzS zGvL0^!Kt}LzLS5Gb39R@QzXw$Rd7AMwj2C9TeLg;>RwEtBs+7I>f6T_ z1D0f>mBo|d>7ZF|dFC0)`K{hCOU!mUdlKyi4h4-{2GoqO4Vl?Rw$A_x zzbY*XlV9~s;DJPC*^gSLFaOki7wAvfcY*%oz8A~-Ud;BLvARj$iT#Ov7tE{ly`j{1 zdj7hRprkVUgTd@F6K7C_(#*sz9tBuC|Fn1ON>9Y02=U8e+IgIrk@U79`>h0mxuj{ z=;|n;tGWzb)g^QhETOCV$D@nnS3_6hb?AB>ByECY7lYqrZnWYG-iw%1S`7_QwA#`w zmUA@Hke9_@dror-zKb;l-aBk>QjZO{@f#Z#gHV25y>_IrF9>|aZ) zUH)sK#NCG|%Ku1e(NaZaxPaz?-?r@JDueMCS^&JuU{c}7oFj&EjFB|ncFaHUHg??u zZ(IeC^$+CQf}M!xz-0Ugl7ZcJnY=;#i>cwlwYz;XS7|B<=CtqbM=lCg+$}`}jmyeM zR>ab9-FA23_^6f|YW!&Mo26`q!H=+#FnVr#8$;FBk?FSU=nK(O%|O!G7| zvGwGu&&tMviPKv-VOpG*;SqtRG=vr3*WKJAV}OIMdqlF{2AcW*y~FpY(=V@?o1{Aow-af3l*!l3(;ve?bzK_RzEzIbF*is zq z^&>#F>!;^a2Ku+?^WxJoG~jRBF^|okc!S$^Rc;qXY9?zi%Zdw$iUMdICW&4Z6Ail$ zg!PziF!4De9l;(qxn+8bKBu?LZOM-M`qgkT8eb24-MN}e(wBuY|8@yYmjt(Y*|bLq zL@gQ%1lx%EVqx>546tlF__r5k^vYlU=j{C&x)medH!qB4B7SZp;OcKLOsH%fhyrb4 zt3Td({o6~@1L6JoJ_i0i4lwJmvOo=j+eTzXa{8ORY88;};xX+V>S@<7VTz*M|Auf8 zEWZ9*YbiL=pf`_fj)Wfnu#M%zmU!=+^N@vc>wUmIGi;(xId~VOVIZ}}5c(|KXZnpn z1Glbde0~VZ(vrE|-V%~&-4ADUSIDsQ!;9Tpk0iU_$Z^Iy-f$$jPq*}W!O5qswro8d z(+*#kwqCvUSWG*XrkO?h57(>Te?wZa?F~1SbtjQGRDP$PO?|`0!rB|=-t>;SH{Fhw z*yHqn^kWbS_UOR>IBGg#3l2+5Z#%{xUQ9%2+C2V{oh;;I!F2(cZ-F1KC++`R4O@GI z+M@Xdg8yf&4w46Xruw{Iu^Znou|dX=20-!^<_ESgZe_!N*%($6#X{lY#*3Rz%76d` zUF4Ymv)(10BBpaZ9pnFwREn%ddE1V^+DUrbPJ{WYj#%jAuO6F3e;B~E%(Jl+rld7T#cO+J;s48 z>#ccGvgk$~<~+i|62@s>=Oy4liSBz`--tB}=9|mAv9yUfT@DPcDss7wCTT0oS}4~U z=a#jQr;v>=V!ZyS>ttQ^M`XF&c)Vhlkr;NLMoJVp) zK|uNO*=j)<6UpU)O2By1TJ>zQ>F;!HNJD4CkzZn~LUiXFu>*CES1H}5>i8|Na8=mg z!#{LA4_29iP-%y~lMxO+KTP%yPy2KD>D=$gY~puvTx1pCrTl-9Dae1xk2?Dvb=*Qf z$Umny>D$B)%00b_L9(6}iZh$i1EpM{F;L*>CTKwCY&4_LVZ?;VbADA%U%Hz|bCV|9 ze$UZjemqTrL~ByDN%z^laLS{$Pv%dl?WfS%+4q_J*@6Ym7QR;?;d_PEjrvY1%Np>g zZEM8#AEf3;OekA^LCvG^R@1Y}FwkAKY7{94Xz>en!Rw*W7sXAdkb z(_Y$*LB}m8zQ$>?EEereaI4z4P%W!=$*;=P-R=tXC%0I`Nf5${UZN@PVCZ%|Gz$Iqq zss>qsvf*@MRV{^SeNV36_p$0|l7GSZE{3qLa;8x;-Pbb1f**nxnhHfM?R^t3;F1~n zR#jfZ+gT|XmcL^2pxPk-1;e~4j%XWb7%sM!Y7b$nz1Y`~)?Vx@=8w2g3g>gNK!R~1 zUeffmCONjHF?O{yJy!*5mf6L5EGM+)s7+_V6YHdfFCP`7aZU?q@rs|A#Y1GvIeQwl z$XWtn$#{~YL|w&eNnyN(O;`A!SPO2zVR0O9i9^D zVR+ei;f(C}&lFf)^ccd~!a049rJQR?oa@TAV=M@vER{+18yjR%^su{Hc+a(vQ2ef*>+a({-;6?`K6|Imf5nD8 zkVjx{W6|HR8?yY$KH=Z$TjabDIX(V5yIniseALgrtqnMC_ooiA2V3UZ z5vd1<&?)gpIwe&(jX%fG0bZF=<5|-6O5ngC|D+f6Oo-mvg_Kr##5=_5nJ zQG8YhF#Qp9OP^$hemAN&R!XrW;zXO*dq$zeC~rroziv=i9kkp!EhS zTjRu)KMR~StDB%VXa8AyZ-L%b0-hgO-1eBy0Qr}Z_ZTToS8=+LZ{^-Z{<1a|=mPxK zVAG|*LK~V*4qdWaJ(Np3ZXGg03uCStnnZx>xyw%47sdM@_^KY2Lk$y&&*{>fp5_?y zBJx;)*WLQvG*7;ph#bmO4gm2Ql)u15qQpttsGpaUG!1Mj*uTE9%7V=ly&TJdMcOix z!*LXM;TH68C*55#`*!}E_pcbta=S|K7lj7kRyhS1+`8^C?Qx?#2yU+(Osb72YzuP* z6+2g0C|I$DCA)31MBCM6PX@MW+XPE9L%creIPGo6J?^ye?>I|t9oH6?;`U-JK<+?V z7ENWuX1GekrT&6XpO=Dh5X8UzLtu1>#}*0Wlw0_n$ep%NJzZ);x12j`eLX7*kV1x9 zp*qk_#T2=@DKM=T)6I68s`~n5$|sqBm7HOd%HN`CqK5_er}r8Dkq8P1x_qF|^;LF6mR z6}8RLcqIKMgU&p&N%5PcijYKErV9&mAo+X7$%_$g*G!`Q&?b@S z+w}CV1(UyDHajFfkahkZbq(^7yV!R6Jw`TKkyqmi97s!t-t~xd95R6=Dc8Vmd1QdR zZ31>GNUBRAua8u7ChE25e|}v4*Gk`?TMh$x+d{J^ZKW+V7mDu+L~z?eLlCcQ@V8et zmeTkrOKV@Of$XrlcY8@{FdEyt3%H*LJeKhn+PFAG;)*z6;G3Gg&E=kJ3RCU+n z>>OP$fvveKrKBsMBj&kAIL&j7F)HO5r7IC^43t&TGOJS~SOS}*r)ddO3~HJrauq(c z%w;gDUW)H7=a!AFFXztXD`%3zxBo>#cP32cm~czZ;=Yp5@+%g1h1OZzA0&hzBFYdE zg}f}Q>BQN~ycuJWVBUl^c>8d8oM7Nf45J?ap?poR|3Za#&F8TE*65UC@3h!H&zZNc z0oUlcn1yRRyCl5*rG#4)yGHEaH$2~ne+xc2F7ordw}EExlgzn&I_x{@;;u@ub&HO} zWIRE1-|~EI<=mT!_l5b@-Qy!P>+CShM6T`Fa6A!e@2C?GITf~oIg*TtRf{)SUtY^v z$ZqvlFyOlwoncUAh{kh=qQzcdYp-oNOtWkudTmQ@c_gwY&s( zb9J*3`|L`l3bCL|;<51+4|r@Gkz59O6hr0@opg^;+bpLl0f=S)N*cZbYYs3pLQL2X z8_XY{caNWRy=0!u^T1muXqm-2hA*fU9)@f%g6&6#GhOmUZC^nL^R}?qTuG-oi zJKGz#zwr(}KcG)}^JstL9lQ8`U-tV%!`F(tFa%maclFQByT;DyZC}lm*PFYp-rP;= zO%k~|d(B7^aBR9hedq7@D!^=-3$TSENBZ$2X7mM3C6TJ^b%fVUdDke&?Fr$S=JS%q zIe84nwA?0OZ@Z8-NNQ?*=ilj%GO}qEa;u`WceC9O;n<#HD5p^TS#|)|6}suxP>CPw zB>fKaKmGQUmsq2=A5Fp>G{rJBkmS2UqcUmM5WWjV*1-4?Gg?C|aaU+nCZ~CJ-hQ+t zq9XkcvpxOxtOY{!uSqfcnlFptajTIFZjZQWSXpK<@&-)s-vDof?{jdG5jBb~F!&si zW6#UoERHd-e9AELw1l)qE;eF?#6`$ZW8~mwGR0Y{YP7jVj-T&l=Ce>H7<0N7Z#@|6 zTeb@m=r^Xy>LWk7)4X`Iq_sFc0ZgZ4r8J^-+(z^swtM{iqM<5fnZBG$*O|g$__s7! zQh)l@;^#+%ydKWw)?^)Ap_ggWO10#$A?L7OjpMLhYXLvxN7}WmgLP6X!V(%qfnJi3 zx8F?v3%|;`w4CQ!!hU7orwr)*x+sk6uxvff>cXk1lZ~Az{USCJI*Ug9`-GcE5(VsB zo{ff?IT*JW*s8XqkGQtk9GB2$a|_ymEU+I6H;1i>p~f{WX?v!v?*O{Fv&4(ErJtA;;sp8#0Sxi%xOO~2QAo* z5WhCjS_CWXFMrw9kI2Imu1rXW*HX8a(lV{BEwUyc3f1-Gdbuqs_z4tFe9UOmhqo%- zJM5)v%;4I+b}(zfQ82O3v$Jg@3A-m_Km_w_i({B*<2Q{hKV*Fpy+!8Kk(4T3KadmzzKY`u^t7V^jt~7 z50-)`V8qL6u_gIfsAO*<%S&IOMf(bghE2yX(7r;V-y175XkQ`0r$Q|RKT!&@5w@)o z!?=~vZh^_PbTH%{Wk#rsAp3@78>3gmCgQlO)a5}Ll}RZ>ylIv_L2d=ee4b=)@Oikc z)fu=gOa+50yNf{?nnzn^LSk!%(J9RmN2M;qAiF-BXvTV5#$43#Y#FC?U+Zerp!T`YsPi#$={2KLX9X?LK}*s&=2`JBXD9h-sbKOdH@ybq3X8XSVNs1JQ# z|GaLu3Ce;c2&_g(LDXO|Xu`Ti^v~;br)bbsNgWq|#Q*7lrZoPyg7Wrs_Nh0Nu_#)~ zP$BqQI#}@`%f2xJ;X>n%c8mk3+VN=znk+W^;{*S} z>$63~m-rFwo`Cz)i(INANjq45XrmCr0f{tyYfG3+-iHPE4$5DR@aeoVVW0k>^hVAH|FL)>Kgoq4kL3@GM(Y|d@?*9Kb+ zo%d%TP2#mShqf--91^W;T{44LgSDym?L*?wcnF`yi*ZKJ^*Wi2s%s7G__|O4$U_7BZzc!A`x}1o>08>|uiZX+t_e{9N>v&!;x)3PN2PWd;5IHsrP`!r?LW?WPnt-|k4c$JvFHdng#Vhk}87D424OvkO`7QQE5ho2#NJ-kTkQdzC%d z?S`*-R%R#@oZVKtk{xVjv8PUh`BoibW&ZeW;sl3pb5+=Hxn5?&Be2B_{7Io18iS=? zNZ?@u67K{kj*~!ODXols_ZiI;`HnGa(LgG-L*$JxHK{S2mShsyoKm|aZem}1KmQE; znoek9zF|NA@IJ2dS^p!)zpf5fI(Weoy-CGd)=#=yt5_@sx>63ox1p(`18k{*pRHIZ zgI}na5PS}MkIc0B*Gw`ypG?$Klrud1&B@s5^pdKM6xrZjs*(mC6V}o=m9k&w%bs5xYsq1n;!jOrcZJw2%ROY!$7Ie{!JqS$E0yJ9^EYeEgtZsS zSdh$mxiC|~pVO5bqy(Aun|7c^;dz)Te<=Q&z2WA@X>1%Bp_#%((X(|Q4tiIXrm6!?+$0Nez zn|QWN#gUYJ$U!63Mno!aJBsb$0X;e`+sv!^tKKN{YW_#KMRY&P&v~FZ_br{Ye9N8n z-)G$`BHLHoe1&CP;97y5NC!Gqg_(-5j&so>w$^{Bsu-fKgVZ%&;UiR-g=npBEJOjv z;^CGb+V;xMQ0T-TIUiqyBK<{jX1+ZtBT?s%#+ch>Ps(sK2pwph`=cxVLK(q-Q{BX% z%g`(NqxvL&Vxt$mL)gbR6Mt)n_hBF3Mtp6E_hFg+C4bb319vo2v=W&3e3sum|bWQ0df|Sp$l{ z!_iOn!y}TmY=I3o(TApi!qBtyYbYbvOBvuv)1b7;Z{k3}wf9pjY2KSQIrgO7CIjn{6l8RqZ+#_A&@@BYAWVacwp1 z3ErNUxU9wfa)6Ip2!l^$u;B&S#6;cld9)o-?{0+ge7^}a);D_gnTw$%Gir%cB$<|! z5FoI2W>7l_cmxD^up1B{um_N}AAmqyuLp|Vw|&5{)BPK3$T0T?!oF;{ZlH8SWVlY^ z3?B2V`U^wBE0k{t%^hwI_e7-Hs*(NKt{y@>k}g_lTcTXP zmRq*DcRl<8H;9!Gb`RlrCZgKl8Jg?i2i)MY6lKc+J}LtZUWzlCMoaR?AyD0k`SgS- z2xv2%`ZO{rXmP_d5yfHDFe%zU9k93X94@>_)?ZD^m}`K+P_9S9ah?^`%Pv!qZ258t zhE4n*axf!gKX&LS{~SG9iPibb7=lllIT?RubBg*pQL|Y2fKjtp4b8S`_UW@Vp$Y24 z*%}*5l}|4Y*CPRLIGVim_7{={)cQ5GMgM_1q_&}yK%cyn6!Ij+7m~L$K8pn{`8S5} z&b$iP_`-{pqOaP96pr2)2A*SRjVG611=phWT!&Y{Sj(P63bfg_mYrr+iZ6%sdm!$6 zR47-5vIFFYNW(6`z}mUO=kijp5y{xII6# z+imZKxE49#_rs;ZdZEwhF?HVCkvh0+tEr@Int$8Qa3Ku3)1$D3mk9_~F(u5sFy)Vl zxjZMc#4n0yY@WZ^v;#*q{6NhM3vD864a*Cw<|EL8YuY{=@C##h!@_55_4eLrU!GkH z9NN5OJitozK$-eYrcBARYpq^ZNV5$vM5aNz9d?wyPU#&(mFVw}5nB5wvJX`b-X9CI zWvZOn!dhjzKe90qy!q`ymBVva0X{MZgBN#8TOoKoeYu+r0MBU!_{ba#KIPkjHGgGm zl_EdfYlN-0Xv%)}m%;nv#c()}pxxtNE$H#5z$`lX-;ouN{JZn#id&U-_j}|rC?Iy^*8+rLI7hVR}Be5}MDt9V|Lo29WdGD=0fdg;$LO*XMC{KBea+bx= z0|x5sUR@Qi;J+R4LEksZ{gWk44_;F-r*nr09U7`XagSYBX>C05HVd>j-f@B*D7D@Z zG*Exi-wgq$Eagx7QDE3}p{~*2ko9kedH_F)!)W+T?!(fKD^BD^Z9OC{olImk|C0Y z(97v^Uzt+{W*LVAWt_mnA8hmTZe@VtIz6@W%T<4{qLJ)UJXvcQ_k#5+(H zW=LD#p()PS0$Huyn!WS-SO+pUjU$Ub!!@RY+TE?yX zyBpRZNO2g0%-S2^GFTqZWsakab@Aiy#!0(W)ZRFE|J*J{HU22%cagq_!PIaWXHFMy z4$EnvSygHmBWR3OY0G#H^2by{8}B_VWBS=r5J2dFo!EQ?kg)>3CMOCM^$~+pLFetQ zd0Bt3!4{V_760jKFMGDOgCScMPS?V_OF`XD1%?%7zLjOFbsi?lwJHxE$-v+v^9iLl!+M%w^IY8x55+}c z%-Rqe@eHSS&u}QG)xf_u2lFvjsj&>L9c?05IDUw+TPYL{^LM4NR?u@UemL4$=GO{~ zPDvuPD0C>FEUmflyHcn;v3NG6KP8o zS^{R7BC$=w7U!QO2l=}*i z+UT{~{E4nK90-QOKF4a(0CQ{DRa^YcjoU6Ct~90@6+_fW?$6b54^-o&?*0^+STmFf z_O_6Z^OS7&ubS#dj~&9%^i1ZByNCeBtAz*L$(w8+O6db>dL&kJDAh46C5_Fx`&UPC zZ=MMsD1{5hYr>I3M^iJ;XF-azIfTaWzG)woY>MjJ3oldj?FLm=G!QD`wb@6VSN-11k(n~YqOUK3?`D0)%|&%X72aU29EGr4LIUB&p|h++7h^L z6A-w54T+`t7#5YKD~cWtI^#yA+j+xrZKy2*exg@*)egGeK5Pp5PG^Y*>fqq;3GAcC zO#X|D+*sulCg*Oda<-U3jGHZ9*pT6Y*?JTP8@?{y3is87)>942;saXuteO~Y)f`Bx ziP5vgC5`P;@q*PJqc0SHpy(eI&u&noV)WS!uWTUtl?@lN(bu;v+dSUt=!4G(R0Qe&V0?a5@J)@W)>IsW28_gL!w4q;sxAn7VBwQRO!s9Nw4DD>To7~rDSyG1 z*_i7R3UX2}yQ_wd7=5*J&W;n(qDq|zg|c2AV7>IEMFd!B2h!4F^wr8KuCBVNs=2Bx zy5_20QS>WS6J^MqD1J%E{Zer*8?BC&cZ=od36>+@f=Djbf+x}z#Axe}*{@hf)CU-b z?JO7EIW4LeDlb&==W^9G{;(iG(bK1x4Du4zI2EtxSnRTldLHJ&5NnU& zo(cpFSEw$GHy{@_Qf$j^3-9JFNMH6R@fhcC;3uD~@e*f5u^PAA{7*gNX0)9daKD3c zJu^QMek3#S&vJIuvh}M3L^>u87ihB+$Fe?=W(^23Au?1vhkp=iSgs6xSG$zNByT_f zXh52Ioa2qL3jT~$T&=j}NV4@8ZmSLV9IO`tKZnIup=#I^xg0qa7AoS0wKD}lU{PZE zY04+pTrz=&ax8%UWiEfX(f!NZm3-kD-5)7TRVa|wbJ->9(p!ubCcVJO$URkD@@IDn z*RoAS8S8uT6jI8H)~u`e|B#ewwJ50`0lB&X44=s@YLBwW!CB#HY`RYC zlIbxH7HSySTu1Hva1eh7!tgEC(b&`STA=VFBdF&HD_6gao8SMmkHgvAL zr}(s&S$LcPk&h_JeQ9Lc|4x{GylmRmo=Mk&`bn%?{OxWA#EgY((f#CHL;mR?8-%c} z5QiE>p!(60Pu1zLw8wP}RK4Y)&+tYDwnM&8pBm9rfT{&u7WW~=eJH{AoBHF0PC<*L zoLtMd?B<`{Sd`$v&~_2Ns!>)qy(87!q8E})@Aj#wI+M+GnUl$9m&1L54-6r%v5F+k zw|MazMZt!Vuydl}zj!IAOc0ae4kC^AN^F@udMBcRN6}iUdFO7KH<&%uI~(M=%N+Q; zvqM2UKKR8;R4n}PQ;{%{23pe44K}({sju9eLqN84cR~gj_XQRhwHu|9%%^oLs6lh= z8}4@lzc?qGggvgY*{-LvX*Yq%8o+|8layu@P~gOfC^oK*v_=}&*iDf<4H>wAy|G~) z{tr4mVnO0OMnsuZE5QFlA61OGuO6h2gT9!^*vHVB5<466m*KK)TzUyZVB4gqFa~uA z?Zrz;LC0T8VP77Cm504AexuPg)T>@Gr$(G*R#p!scfU@VFIKXpsIJ+TF1@8_h)%^W zyUL;Dh`bW`@AHx;Z3Np8Es>*j7Nj8bkz$Q{?j6qJY$}TtkcD)A5b1uUL{`F5$YwZ# z;!s8LxI!3Y?IOu|PSNMcn29DQZMgSzPejeVXHqN?%wkC?0ZRnSuypUGTEmixx>O5> z-kM^_Mo^7g2X~d@h7Pc{Sumg`A~Lk&O1$@(+-pHw0e#RTtG0wUf|^%CVVA^N+m-lW z37dwP_UeF7JFXhe5Sbeu7dFRpXGDQ#Q2%BUt?Ai+=!S|i92-xvq9Pd%j|sn0!XYkYe2TGX+ ze5Q+@rL@D0$)w|B1+%1Q$a-K#mbV98vmNQAK2~kQ>U4snS*=HU{%G}8&;_DY%-tXY zC;Uvw=gC{`qgkB}vMgr<#hbDL4o@i3NVKy~ z>XkGY=6>hyl$;g3VJzM=n;nbP-?WW|8B!kviA-x*5nX%ru~0EK7MvU2u;#cK8E@** zXJ5h%qv6@VFN3i_S6TKRKxhn40}b#r(7ICBLs1NZQhm4IbFnvK{yi({AP~$Bf>OdD z5G)&n_ZG2xiUQPoi>zj4dwx0nM)}bv4wH89Be2gf%6tgVAGYc!Z?^)35{X*K)qHxZN68lOLhUo(slk zb1U+QU0D!E3(=t)l>IIwG|7s_vvlQ+O&PjtaiMoLqytSbJJ3oA15Gd;Xv@@D%G6oL zy55Duv%tC(6!*6YS!1#tMrn=Z+?y8Ej3OC%O$SifWUr5={ogo>y?|tGF3z*DYGRQx zPeMj2-7iI^m%_wTnM>6{E>L z=#J|eHU@CMs7$D?m$MyasKPbXl`~1a(X;V-3RNPk5Rlhy%DM=BJGb7LI9A-?XE1rXf>i$54 zR)vxq`#tk##Li$Zx^p}n+Vv)lQfS@&~9zlCD#64 zJzgi%pt6~K_WMnpZex+zV+HahmaBSxpGUBusGmU(Zr^`!7ME!63Tjq9GPBI8{9RT6 z^VsNjaT(h2?fcz^Mv;S^`P4R54f#&0>v4lc*S4uAHbMb)xD^dOqb#Ra03qL}N8m zBy}dJqGG1G;*>mTO>LMlJztET*>JI%=!?}?Zq2sq%B>4H+;R(B&u`5}pWiz38kPN; z)33=!pMK35q74fWyZ)wBrVrs|)+L8&?BT4b8nbX-tKKx03;Y>JkD4uY-go58#+6N^ zu57w?OE&e|EsI--UfgnVOE&uAmN^bU-CXUN+Dvq84=0YfPo9=X-h=MrgBbvKu?kmq z;Iuf!A0t%tB7h@`xZaOr2;{+d5J_N~99;VU^aT4{TQdl5&L3%kx-EKh;Yf=pULOpJ zrnLW2>Ql=&!d2f_xiXh+$K@>}Lj1_q@hsvkpR&Q1up4o=cS+z-$xp2wqIS=~`M~fu zU-=ntB5+~Ll`SEh=3m2vBwzWQ2ROfFeM<<3mf2W6#olX4lEM-v(C+YAR7mHxT-qW= zwPkIKMVJao?E%#84z$u9@wgDj{pL`|IkLn$$^k+v4Zw%3gtQFyM<0T*(S#HK4f$Pg z779=)+@jF92wWD8)?l9aAyX#$`m8^xm%<7?Is|^X1Nf+0fY{6)2gD!3j@&OGxkIbE zgzPbQDH%pVmf~5v^VWXtJ3K)@otpV1xO?-=Hg~u^wZ8jku)b@@w_JC3VZ%S2a@~W3 zCnSS@RGn3C53{OJsoRYUI^W{M12tB*HJ=Z)BEViWlO+7~<<4dKn#n%7!xt~69XWhy zu<~iIDG`^=9bT!cd^UWCvMz_CR8|vzCa%V?Lh;Sb2K*AZp>NCa9U^ybXg8eW6R{aX z(EVPhVDDKo9Yz$#zQClToRYBK+shF+FD@oWlKP!}EM76(Pq;3PPq3O03M>=lCE7z+ zbB}atlG~6R>49Mql#(WQq!(L%{T)fi_sCE&I@dLP9+_b}$y8~na4Cx;mi;xELdecA zcVvdmR@9K?j=(J&E1&VRwDGRW<7J3CGKc!Ua@ePKJ$!vC0)w##bCu>>yohf|2vqWo zEn0N(=^F52Mj=Hgpjcym25VOHd|bwK-G-h%0`9q%M9tjOZE@jlh9fqc$NkQ|GAh|S zC=-rByglO!&I%TYj@8#Y4{VXFbz>pOk*G{=sEztd5a z81_<6YGd`CLo%~G?3^65IWNH@@_px=*xVjYFj!{yBsGSL|l3mm`D;eQ9G zXo8H|(0O51Rat4LGMR*?CZWTU-H7h%d<0DStP6f4()4DnOzbL@YeieEhh04@qXAmv zT4c-?X?!*L?u1sKK~gHllXuy!kG}r9Me#;M`*(}gZljB$`*-W={FPI~&Uaf#%aInE z@}0}thae19Nk&b!2!r2QZ3skRxcKfGs*ON(AsjM)H4OdEQOK6d9w*l?GE6K*E|TOE0=`2c^M`7aOn+ zwqA>~3aVd&@CZgNFoDf1vj|%?9O6Y{`z`|CO{UW;by873jLX`qLYVw=C$Ci7uIHsd zNZ*@Q>ur|$U_jURpco^g!GNuB;`Ke|qF)f2Kv7eh9ow{ZWv~Z8DajA=_m+h@{L8NGI@-FgtOUT=lc<@7k zyCAE{-xG=eBR?VH+=%&y-osw-;*HT9<14yHdyjBa!O*8%U7EvP(04Ct>@&;*&K^*k zabZtcdfmkWe>^kudCt1z5KxC$7O*7F$x_fzbF4j6VYm=jWQUEU;Q72|`#uDv70^}pFqnO#lF*E|{pDOW za4{%ak6~LpeW#WM6NB=x5yOSzyS2db(@6Z~MV1pSqNCH2MK>HtEX-kHT|@Yo;z&>; zZGcWFj67b*)VN&K_9I~L``w<$^&F+KWpO=@xa;7!g@fiSk-O96``yOlVwmh{5GOMM zLckt-YP%}C$yB|L$9;besGIS)Fgo975XyL*PD4MAEsv3v)ka;LAsu6q%?xjIH+0=S zKx_0CxBAW1?k-=Js9(^xt}6b3t8T7*IXNa<&*qlmvE=3CPF|pJ zEv=On_Qi-%rE`#fDSe66M)x^QI zEMagh3*nSTZe5Z%KEPe2+c>5*3?uTyY%V;j9NmEL|;BSv?M6M@KlDC|o$T6%a;a~P=Mn>{0Ms1TT) z(zMd8*2nWF_PWPCa9Xc5FqGH@(cvWEKm;nCl+l7O9pcc(uPV3JVGvX5*(4MLUL9il z0r%#%3}Yz4OtfhIKgfFkY_{BFvCIrLw!s4(aEE+qc^gAnHf}c!mzOf&4qcO2a*dt8 zwtZ|h&>>*3hC?>{zLv-83^g70`1WAgQejjo-%-vg?Awkk&`$Fjj=@jsISSND?u`FF zOW0M*Z+W=U_I1PTw^EyboNkl&mf_}LxlV_Pt~ul(H|aI3h3@Pujvt-36bDtZ*78sTwWuzKD&dW46A3s1u`dS&)EJc8Dc4?Pmg zC)Pd^1K0A`go0~@ac3KnafcHOflcD_gMMSJGm`%+xq@EHBdG3k=Qs}G&m^9o`Lm=E*eA>3N;ri5JdaSP z+H!qXQXeJJ^gfE91${uNQtCX>z}QNJYiN~bYFTdY%;CI10tp02WQAh%#JPl8DuyqR zr{b$syLQN1-f9}Xn)~Zq<$mwBbP6YLF}z;5YTiIUa?%<3%E2RHZ@o3Koa9HUBa}@3lQS8X_$tG@aBy}n77=c2S?Mm8YDLLib75{*XQ=@F9NoA z<_9WHA|f?|A{+tq`OXUghJ=tRTSuJ89!ox}1){zl8n16I48fh-+evEgu`u_xy;?~n z#@?ysv<`eD$#_H3CqJ7U)E!~b_9$0B_^Gasq4xF=@`75U;YD*(l%|d{ah&Qtr;6a)h0X-iruWH>vnz`Z6?wqb~V}(kuWWhg~ZH6P4z$$7d2xk z{|_sqNjq72Hk-&uxNT8RwNkVZrwZk~F_APsiG6QBeoo_qGcS}2Zuh;Eb#HckbZza# zw|CySWLgtcjW6MVO2F9HUj@ww4k|$?%tXc3e|*hr5W@pG*65F4k`#qJD5aQYLs|oE zY%r2lcEzr1L&7+5%iL|Vlgqdsn=_aajNL>BNoA*-6q3cmf$6YSGF6^ka>)*xYn1A! z@n#f$;Iz<;#Krogrm5ZohI*QHvq5`1UA)~?aUz#Px{)ZCOlJ~R(!_3`CHM=`9MYs-j=n+y3$Acbljcvul$y*wqmi*q)SqgV{ z7Z2m7K=I1vu}2`yIsle1$2}}g*w|WiIB6JC8Le%%5=`xytkg8IvnS-RmQd8DQEkQ# zy7t-Zq>rf^SAJ|NqN%wwznO$<4c%}iI2-5(qy1&4*@F{Omg+|<1ZTUwiE<&>r*=$2 zER_dNx`XhU&9%A#`C6R)iqSBL8zyRV2lW?)dk~H{%*){>KSLVj-@>@rbKh_{4E}T2 z_8r8wZ|R0UTWbVuH)4Utz@XglRmJiPh1yoJ)ppyhzOzZngb*^-xl-ct-y9h!HWZbHCR zB$(2Sj5FYRgRl3(iw7uVfb(zJ2Y*S}{8HG!V6~Ak!8b$+zMJNCo5JrIcWk8LlzZm7 z{f;%1COps<`HY9#)_OfFvoZz7`sf<(rtZ59CR;NFb#>~V1y2cN$u8E?H5{h;?#vYR z0#Mhm?ji)HZg4evvix{bda&Hp?B1?82+_yZ1u|`)@WN9~carwf<&!vF?0eEKK5Z5y1Av`>1cm#ZPt{ub zT$AOGm~DTMs0S@;39)fN)EjEG3-9-l|31qabxUOB0Se1nKI@f@Wy+WXx_5WGbt(%J zARKi&OU%|C5^twZK4-9misGuqR^@Z%o3uB+;|>(nwYR3a10RFV9|tA%*G0#Esf2Fp z7-sQ<42*Y+73y(A>A|Bi1X#1^9TJKo>V}5t+i;mBe3T(#;edD{PKB@=vK2k%@4Vr@ zZG@slsrVP-IGguzFm-Jn)vOP;B2AfYrp`77O&0F#)*mBB!7vF;+d*t+(1L$(LOOHg zcxFuqhbi;HtAhTZ!=cxwfDYaNAQP#G*YN5toIb}NW&bku+u}JR4;p&DEkl7U9N5!E z*9q2M50ASmHp=YZ1AIh>!I!qCY5BZj!Zv#^+iYKcBtL`aqCZU+?2KoKsGR*Np~q7! zMH|d^HtipxSuAV*PvKc6rWNy{(x%25nFXg8sUzWs+jTPkL4Pt2CL*hOxF`(aJFAC( zdU^|oKf?B%MPJAgjExy>F}T9v9dME!`@>cZwH6tO(jQia-hO?>ocgQ&Jq&{wibBBo z++se47x8l!AI7+lKUIkFr?^ZQLxB>u6IpcwP%C8DYHb8U)5XYJXak_9 z4asQJ;iU5oGC$y$g$sV04!>CXY&%+(I?ATIEMgMl_iP61x(a4p=nFeGH#nVc-ZWYy z@2O?YE-R>SrmWzALV z!w`5#AsR>@7a*;{kFo05EuS_J*;X!jU&@G@9*!4%&Lg~}1t>sQd?MaWEPtG69go9H zAMe-Pj#2$j5%tt1oA1~0^~XjuMR-j7P$q*_qzXtJWJw;h#1hko5j{2n2{4%nKHk>B zaa-X%v&;esg^!=5XQ-}y(2^!SbFx&98Nn%DE{_H478lK(=@Wldeu`U+(?o92 zPgDd(phKRAvgL(r_MdU5Z!;e^t(G z5L;VFZYd{nW2EJxl@Mjwz=h5bI=aYa-Yq5WQQk+I>71NdvW#x{VptJB(yX`JLd5`m zl#bhw50N`{EnOz$hk8D`93(%Bb6$G7(tUjC(1?!GcfCl~-dIV9x6`%&k` ztTtRvo(|hRQ{|NTrn`PCCeWSC#GR&3dVr93W0#q?ejV|E18%3Ri`u)?ANZCpE~?_5 zJRhb(eWk9oB3IV$yk$H|K{269(KpZ%6j^3?9?33_n6FbXcD1x0X~*yWX;*}yC{XQT z2HagN`uK_UP$iDaI1lv6cEE;wU_eSAbNM$gB-0qKVcX9tn`GW!$+?YHi@bB5yJGL+jO8cu z7DEqc<8MqQZsTw?W}94X)f~3f&)u zdKHw7cN_DGPq9!G`7KY$bAUPUQ}*&GgH9EOztPMzk!8tFd}LKivsTw`pHoPygBF@T zWk?G^rIGxNg|O&Jr3@bZ9F1#HihhCFF=WtHTsM_NcQ0wl#s{}QH}EJR&JNe%tD#nl z|IMzH1tE#&DF26#Pdk?j3v|c8QWI5G(J8CZWLSA6>{EILHI5kGCDx}&NL$`w6^v!Qi5Y5 z<3vf-42T&75ntiM!j59&Fu+JcGFC{!y&(xrNUJuydudBj+8=F6Li*rT)8-fOSD_S$Q&z4pi1C!a4%IpO%-eaBe> z@R4rfe*UksAR*O2ng11n<0zO{Bo!|zMYY8R`b>2LwP7#RG7fL=nfw}GEr4br>t|qn zrI**BDjKJuIyp~6wH%$L%kr6qYTfxIYnvH>F;tqYlUXhe5OI8FgD<#Z-Kh$qs~YiO zWd`?{j(Eo1qYmbGJ?zA~Lh*dE{f_w@zA0w#olY^`*>NKeKdQrvT7)~!q$S^I5%fK)3vZDe-fOPxRQ`-X zD-QXycrk=7K7oYH5aDQXwYe)r+tbk^%iD#Zq+x#rVfP={{3^PUY=od+!nMO4uih(p z>J8jA;Ok+P;E0(P z4AG;8LSke}5Q+0}UfxEo#)!b$Sn=AJC0=kd#s(NOw49Y+O^H-`Xj+<7?k_;M@V=KiLwwb2B{RAckBL0oQxRW9|F%uFe}N};n|Y{(>hLG9no4BPD&v6+qI2F}e-%oJvC{Qy3# zu$qC#L}pJeRt@Bh;_sZyil3cF(cfM0+-#FuKs@rGh5%QMK}rGlLBBNWh?`q z%H|))e-EQ5URA;Xnt}_;;ztC(f80!;C)6BH>C51v@#pYb#NJo;n#)~ZEtJ5?!iQen zC!wPV?U&F)Dy@fa&@QO4^u%W3_ZBhW@ypDqn~ysrkdcKq3~0`FkJq4opRoK_~^%sLYBQ6BC1`qpAGcw4iKUalOIbgp?@Cz0hw(mU27&KlG4QNr zjX`}qDe`?6BX!tWAmc6i&I*~x{JWF%yLlLjMZg5$+rnd(@4v&g&UvSNCVUr!PrDF4 z&E1Wy6h7uc0FOxsFg~P?Y9vSFRy;<#jNZ~1`*O9a!ApIemvDB+%kx?6Ev7)E=RgK3 zXNC*Jl8gmmlQ=0aCVmj_4GzQ7#0{cm6^VtEXW;~werMnX5?bY%HC_cfEFVio(}9-$ zql6?A&JZ!vS!7z2&k`*%(^+JS`FOwx7rNm+l&*jYU)#LS@BhIkLHUh@VP2HWQvD%4V z#gF^R>Vx@+SsGv9|4w%U#_nY-`&h5oieN$f&jxvGp0g}Ggl_?* zd?_7=t>Wq9@@WkHrCYhUMjyt~279R>D|O8B{qFaG&Y8CM@_$(4c=>s36dYKEOI=ig zR}>wpE3;g&6X>eIBvB- z*OPCLvRZD(k$F)CtYoQTtX5gj1jJM+@8tlfFg(TChs=(C62>lfB4`edF*a$Y@YloU zI4#~2#qBy=KH&N#7EEOVLhhVRGom(u?WR@slvT5O;Y>nq(pcanNPMWAY&+3CTzl1{ zn|J}>NSflOs0R;<7SzCHo#X_g2@{NU557aqt02h*zi~@&iU&evHI;6J);#JZ`Jpqa zD#OsYu&}nm&VJLo!~?nXN$?n!bGK&I5ZQi;2?XcW`V1=0WkYU1Ex}O`ZC-wgFOJcF zX6&5nivUtKvj6R>nSUGu?PKmWb5$*Cl<~W%^gpJAAk87bbPR3$QPya48LQrl_WpSAh1nzJZs{*aY|LDu|Xi_zIEIeu0)OZE{d z?f2bCPGRtpJr4GiC#E;R_R;8%DJOmm@8lUe2#u#Z@#E>StOR}db5^ml%~h;ZcC%z3 z1k4|1i?$3(yoQDCH|bkU<#RoCHj~fg%$Tf$`{@JVPi5hV;Ca2x#yn1V(l@D`@P@x9 z-Gp`ZQC5726B@dHr{QA?4V=+rc!cz@rNl7k`BF{%q;Ev(EjT#r>cmf|A@K+qzn(gk zM(>beM;;81r_ZIEc-|zcXUdu@`B!D3<2MK!cue|m9J~QV;2QmAbt`ov9nePlXeRcJ z*E4NC?95GW3~griWC7~Qis71QD?LHklmNQ?;I-Uta~^aDQvj`}4yNHj`QR~_KAQ#|*gcJz>@YuoE!0nwxjsrd5XX|THRyRns>v?H>C@&alLyGJa9Ok$8|RTs1qB_oX+IOz%yqueb=x? zHp7CNVRGz*NE4&k09|4_>()KDur&^(Eo?6Ae!9+KNxULzI8A$_bNwBn5k+=(2~#IFmI)dr6#zYAaRrZ!^Zd2ec-&-sq?zap65G2ES>#>&B1QXCp$Eo#UHPzAgxn< z*VB5aw>|z`5Y=$ymuiRaY|X3<%B6h_VN-Q@84M-sX74 zA()MHJDq6h!^||CF~yRmGEgmSThm;N(xJ9$f|1Q?3ETrkxCmRB6rdxE5oQ|6n9oS4 zmS8t%Y-9h~CTT~LhHz;G{SV*5c82Adq+uNEGv?&Pu$qbw<5T{1zZ1XipUZTH_qoia zOecN`l}{etOPo=+M5oB`&RvgGWuxmlt0y*&A~@lR%!y1BW{qL%AQSijsbiM!n+V2; z>x8ii=!EB3xQ>sj0&|YJJObo&yaU{4=Y*&HvwkNm)zLUw+(zmy$(f+?Psar)enQ0Y z6E=LQ^=7Omuf}JHDfmYQXH2ar2WJf{LdFMB)A;7B6P`(piqF7TM5fSN9sGI!qJ$a0 zXc~p#b#yuAWq2-gA=ANMFbxEM1DiexdBRPgDEWXC@t^>cy#+{~62w%Wz5;OZz?v5x z)2Yj;cu;b9Ecij-GzcuwSD+SnPK%U8VU2es23076W*!#Zc_%I$h0}V32%`jC0C*ZD zOs3*7nL16AO^E=S;X7U!x6@#?PQz*qvug}PQ!oayvWR3oVcezkuw&{>TR9J(2~Z6D zQ-Lu$4rAP?Fb+!ujvn}^2Onb!k!&hJYZz8(R@EqnmC=-OTK8!*5h5qj)`U6#6wTrk z+6NifG$74Z0m@YRS~`d=W`N@+EvR}ujOZ~#EvIf$pqr$~DW>3XwC5rU2E`4CpH6@c z`O|%fqV9mD5~#l?S7sFynGcws8 zya_#E-OqcGTmwHi^Q0t3d^!xQ5*e|vVU4V#TgH2!&@X}*rc<>igHb}Dw3u^WA^9A~ z(k`;k!4F9Ac{^p4gY_t1m=xyqpf$|qY*sTIv%~u8Nj;#!alkc{FxR4t>G zO3XF33knByr~@P$A&(4Ea2&1yOs65&blO5%f}zbcT%p&DQ4?1uks)rWpoEDuytoO+ zJZ%x$pcd0Mp=O8&m34-jarGW`&5SQV2ObpP#l|IW6ZZ>_HW9f4GG>63{ zx?EIsJeuN;6USa{4|+1N2h*&^I37kY9!md#qf?z($30dcxMl+Ej|c{R|y@E`6Z0CK1^8(RG=gQ1_~2l zoh2_oG!n^Yk_I&elZ2I0Cq7pg)A5BuHt&pN&&3_jf+nEJu9I|j32UBpmR#^G%-n-2 z8)G;Biqf1~p z5lg)LvEm4kXGs_Rq=V50=<_?uoP-WZci z)HBbGjYJ_gHWP)zDTy=n2`~i)1^*MJ4C8Bg%>3q2U403=YVl^JWDsuOoM5=L95~iv zmc(a4*HqOSS{3)*fU9+KWgRKPlDSvDCyGrCTgO3~es!!;b>m>u7?v zq*__elryZHT0IjcSSSSfT33N|xl&?23q~E9I_##&&lWf}JXxAV^JG-OES1+uI;S8w zCU|%B&EiDiOAtp-V7pUp3vL^^2Hglii>|(;(H;0d{uCk;nSN82g0hU ziJ-h0ppJE`DAsHwVu~!(OnR`2(<7ba{UUS_E5rqq$nyRn5{DS{AohL;AXkNvVHz34 zVn7CqK3uLfq6B{pOY`vcY>$fJGf9j(D>9FpGal8R_h9)4T{Gqz^r>Mi#Huk|P=T*c zP5Q9^J>$czqVqnr=v(q(Eq~RAZ(MHp@Co5fpXyOjg*CS!tlwgFpXBw?Zy5alUx`jq zFvr4DfYWTcgZ<*XX~m`(ER=6d+P|hi+5p-jEmYenIC*&YoD}4e8PiukT*Ra>?iN8O zxx`eyCXL3UHME+b6D2qYjb22s1+8&A1#40@ zdn}|10mMNS?-q^MO4Y1Lumys|IH$)g(bSLhP;W|(k;F%$?67*&ZAT+34rYad)&VrY z&OoI>v81FHC}Q@P(A@l*l!U989eP|0OEK%gLK)PAam|-JdhV$2t6U=I3(XXT3 z0XXgWanxO@{33K1@W=CSMCuI)xgxxFBp~{P)C$)6OpUsU%S_Y^?pR@3^tPZ%+xA(4 zH!c0j03zZ3Aweo&hJImNNo7q~T73XKW2nf5b>R4=jqkvQWXxj50ca8GC?lD+ATcSs z6y{jiybKT+Xe?L+X__mbsbgdmxmr%JkRBF}d*jeQ$zDYfG)X9w;9xjW;$Xwp5(kG# zp+uy54ebtogtu$b?%FfF)=8W^?XF!f5hUE^hcOyQ0B(Dwm$Rfd(7L%$6o#)uL1#SJ z798hjKIxUUWVP&F_2O!jTV5PR?U7!19aV&os$pDUf7o1D*M?^>iknP?rnEpov3#jq zBGA8bu@b%}Z8CIBvutd#aZ*cFc+P`O+1nl!#X>lWUH9R%*06&88BWFA^JXd*H%8Lb z7-nrGIy6r*O1jpKHtK7Y47?1(H4^FEfTWZv7K43bT8foszM*T7tKh=6X^;7n!ZggM zHZ5{yTH!>2u>5J1wdTWW!aSTEEGA&>X$14{lv~0HQ%B(#;ntr73R#r1zSzjsVi}nv z19{MLEzy_=Qw|kF(%Sp_FIo6rs9N!^N!n*b4V3sLpMe%Z{Y86!sR6de2m3sTy)cE+ zQPvmH#?VShH(}`x?=W;M!o9_SMtPCuotkDf{mW#G^~dY`8jvb zz{GVdPp;cXDRQ@d0Nc!RPd@DM2%Ab`(KL4^@D}`8FJuMv9QUDaJuGEo`{s7J0H1k@ zf-d#%VXccgL+RJ>8BMi_>+01SZnr0eHjz5!qj^}bqUD)SLpd<^{Q{kh!zK;2Cx1ER%6rG-8gGYe&_? z%fErWF4(+28(nf1?`RSABANtTO=eXH zurzTP=!HWMV3FX19XpQlRb>^JQr++#mS8VH$og_k0w2imbf-*@{_-WJXctR&=dkws zCsq?#I6VtsL!YGL@yCu4{O>c$Dl&t}ZxpjG7}v^e`h7zw7-wbl6?L%U$!)r4{$@Fw zxM5B>{LQnMB8%`TNRe$N z<9~mQYSB3@d=m&@@SD&Ol*o^%_P7TEo{`eNmgP_>=G!$w9e!9AjWiac`;5pYFbB)O4J|bi(L{5UkCjn`4X*)`fvHw zZG3J-*t4nRn`!HQW%~zp6zY@(9+-o zQO<tY<@(Qt*x15dzGTR*+K zoTVnSX0z0M)^e8G%!+PPgWD#ysi|%A+tlK=@ocii`-4|Gz?vB}3@j!$w{T_<91}dPFb< znDAjKVMLN%$z$HUe1)(3oMcz>eDqQrSwIMnjH|qIRf$PWj~*aBAA>TX8}JMiCQdP` zs^_)hq^z%%I9Z|XL+U0Q!}EF@3*@KV9cY5nvN-`ieljiw&&N?K6wDFWLywm3IM(kl zN0%4~39f$_VQax+s;8WBD3WJoekt(3D-i$^C7x4V7K@ zhylHorfy>|3A;4tf2;mAKhL0SQ`6bwIcg#&nyUtLqdV2$&Ys;WwtI57n%;dgUrpvO z=d0EHo;|Dr=^azJaF60M??7OVzv;pCk1|GDe=@5qXAwnScz{NW_D56T?HkjsY-~EH z+bOp*@D{{WmYT^L-lj&k4a3XavATn#zay=&2~SNRC{z{(g&}&9s~On28_7~*S({m^ zXWM3un%yxcnfMk#4JK@hlIeSEw9%y;n&mYDm=AP5+b7e!DW&GHp@j{TQ9m{c)}?{I z-%Yvmz&Z2C>2MXpX?<+n&xzRo6zqs{mWv|%0BUkZ)y#`9me5c&s$rZHbeV$36qImS zSm7yd%qQR%uX)8#%=*;4Z$XOD$FI@6Lq2T6jrnlh!9+YES5Ewj2*4XuYa?f3hnn0m zvjb;!hr~nt4ic(Vb%P+;T*tbVJw@-w;!#+P+C>bnOi{g7JmP)`#Ck}|wA85(vr9b; zuRv~{us#d)9d5~(z^FL>x`gvE5rutPG;542w>ZSj;fynB{>4c`{s1ng5paYZ#$Uql zk8>GT#^14iJWtHrawe^vA?5hY*zM7rt-;)4Aq6K}*0D#Xm1i&&GmDW_HI_P&ii0ep zY4|GJN*X>Suq^+2hf6cO7!>R^8(GSwDs8$i z<;uc19Nn?9N3HGYd4M$SPT1m9wX}Uz^voW@d?poNNSYJy@4+~g1vd}EVIuxNGzmY# z=|4i<)^yQ0@3NH1bjIZ+ATViRbBR7|^%+Tr`eDv3v)q5Q8OB#cm`}pxVl!_^JNWU6 zPo}g7r!M%E#!;|fqyB$Bm@I*N{_4q5yV=uI|e0Sc%R0mB+312 ziHsL8_EkcCL0K5~HuG*vPyC-}l{F5BVCPz%+Q_@5Y4qy`@vDpWTMC-^G$H*!>0MB zU-hJqW~lLu!E800J+fU*ZlB%(gL|6>*8_uRt$Fd)Aej+PrK_3r@oj2i8$6Bq+{Nu` zdHb-&yRED-E@H`q)Z>9paUG2$@clPEH^=M`$0S)^G)s+Ujncr6(?WT?b8K>@!>}_@ zE-ZmWePeKHF!sTB9Hkn0d}zKUPASFwqI=nCx7g?&AIh7c8@9+L*x58SpSBQBz;sRg z5FaFKI*kWMPRZ|r9@9K{&x-v+81StM18g}DR=~iROhr5xcHjgzEh)T&wMu32q*O9j zE)~s_3e^Aw_5e?68^d`*A+4JfWF^Xz25mysN{9oe5dA&>l7x>YaQxCteyphunYqP3 zEsJnK+?g2ua~4P7^}?CAsJXXHKdNRQ9ezxWJ~sN88h>p3F*Whn%wuZqvH8c;;$wpc z)bN3u2h`+&xdUq9!2H|P^4o5`P2GOmaETf%nJZBXB{vVM$%C^8)%?NLx2yHH4<1p& zN2ZUc*(38u)Z&rbM^yCj>35LL48O3?l`{dsg;)VBB=Oy6iG#Ypm%+m5lozhBthdaj z;`HHss#?S;L7WF!#}TR2TdC@H>U5f#OG!^y3hZu)vrk4DZ>DZ^A zPgjfS>)VvQZ8%$vX3u7;`Rv7PwVWNzQG+>~vNVxdN_Sxeixzl4a(bJZ-L|$(ZEPFL zRwLPC+3?nvveioVyvBtlDflZ33P)t&DUEC!+oo=ATiB+Swry_1XE9>gYAAaqTg_!p zZp|={gM2t@_aDr4i@Db5mHitfY#X8y&)}S}TEHv}rw2Erd+Y$KQ2Jx4Iwr!6!wc9< z!Kfpnmq*90=mFKU1D&H7hj3>(7YeB1-ENI!%VKZgm8pXLE zESO?uXLHqb)+fH&JU3b}9a-1ZCl$s7G{=%c9_r8r`)du}Sm!bmW3mV7e4Xj!J#JVH zz|>^{M}VB2S5Hn6GnByi=<4KUvDR|v!Gnn`n%ghK??0o`@bK537<`8se#h`rYV@g_ zPpQeLMxR#WPcJ{MR-c{>s_EcDg<7guu28ELn-!|(nbLT~o>Fs9Z9b)Xo?bkmmQQSyt6Sxp<*KJ*q(Y5V+^kTO z71I@Jwqm71@~=rl%Lw+*wlJKfhAPG?)Of{&#`q7!Zdx2zhn^gLQjI;i{-laNHT@Jz zNs6B!sqsi&eR)ZmG+a&@zOqFhatFO;jL@|AM6R=y!l zCeg4;sL@7)YO(ANT!$LbZa%G+pN^hzN7>}elRu)YOK^DM9dLUKUi&nC`b9RrwVBI8 zrk+|5)IDfxtOv|vU7R`N;JjXr6g_K_KC*_3Zwlyon2wNzEorSj}UFUwcs_zy?_ zN78A7(s77sfu5^pAGqlTVZBzle=06y&wf+|^QG*e95s?Nk)x(^R&vx@&U%irb2f9B zcmHi=VdgH5sHf->r;8`Cjb~uAol@nj8iv=KW)}VyU}0$j2O3G>QQ`6!M%PWxg7E!T zC9@~-TEQ7`-ikpFi%Mx@es$A7Cp;ejXPB?kFY_Fz6gO9<@^7nQrH@IaOHjO}&*XTH zhLwfo;_1|$G!;vWi2-=xCfVc8o6P(Nar^;WzH?l4z{-rQ``q%0O_>um-dU`y2F!ey zy;usu?A)yG405-2SggP?v>F^4FHFh?wqGz3u5vbj5zQ_fqvPdy`Z&ux0|#)3bhur^ z=wqll7AiK?kDXUlNv2p5+$LuwEvUiLd8fCF1uE!|sE=OpC>Q z-{Ml`Fve#!fj$I|G2xSj9!Il8NKv?H06ISAk(MI>Z?Nd>VCPb-RV{a_v zim(GZ(ti(H4kIA;1Rw3L{JarEq-K(Ye_vO6f{I`0VIbO+Pj#u(#ci_=mO`!KIv-H-; zzhRQsU78EPS8F~f~Gld+J2bI@xUWH9UJ`13$Fy;%*K#)(blxxwD;#A98Ne;e}`pU}ArOdOAJ&uN-{Rxs)b_Oh07*<|M< zOg@dQvX3FmI0t6_qb_)G3p@un;7VUJ>84IOnj`S_vi9VK|omx~OdR#~ry(MCQyHW99L$)}i! zH~?I3S)MhmTi2|24SKG6ulO#jSjs?Ze_9`Qe0rq^SXQaVzX#>1qB7Bu_uj~VbI;Jd zZ`^mi;M)Cz4_tllO5pNCv4;m9=`ZZt8{OBtpIHpAd#VubcER%qAIC3%ANaVJb?J8c z3c3jT3OWHg4dMg%5vK=_CP%g>`aI<>*_wLZy8)czS_W=CxR3R1z|ZLGeZ!=s2j}FW&NrTpAQQ5rj=jDAUlw(&xI$Eh2@#D3abza<|5eh<;< z2m8UhHB&A+dZKq10Q?!=iYgH<9%JPzN32%mAuCKoX3{MI!*DucT3kRIZ2 zQcl{0V`1_bW*WW7;BBA|mx4RTUISv;<;q~}x!}Nig8k12`>M*K?+y0yjjdvhd!PZh zCBdDuuLAc(>x|D7dqS^{I%S?*-3JnTMcj z9={%M%cDDcrMMzZ`|z}`+Ys}eh*bv%UJUlXFWC3~VDwC|SKmVJ&?#L<9%DO$(X?Q% zAMjC4=j}D z<740fwny{OTT{{Zp=gNdS^USgj7h2(uDK51yd@h%46p1fe+Y zI|2w7CHoZbIElDPtzTlJ%Z!mWwBE6a^s}yhW8&XgOI$+otN-f)n)ftClWr2 zNk4WL>5F$s45n~a#^oG*k|mXp?#d~uzVN(opiR9trb5h%xe9v|wy;!#`Lh&|#b_`+oSj?9scT>lc zHQNhb8!B}e9Kw7Zh zAB<*H^k(+)#rA;C_wF$~Z<;ph_XYb@Sv18NKutfcWWQ^|uMj_!RXK&noY9Newt?(m ze@?J3w=B9n*t>%$hS%r15iZp2!DvV4pBqKE$7!dsJ}la6kJ!}zJ(REU#p3mM(qc$! z(0Sv2w3B8dZAHs_t1dpjfV3ISGqezn%YctA3}WN_R+09w&M)n3>i=Hy(}|x04{#u~2S&zUai4ApS1) z_v|3zt6e&4%00UZSztb_`+*on96`aKDL2%H@C1HF1~ts*^j*{DHOCOwpk-g?c-KxE z`!)1P>Lr69V3Sr$nw490#qd>o7HP)+N}3Bed1sJ5?8wVZx{VCZRUz$?mW|t1oLfNr zsE)te*j3knbTjWlC@$-8CBkhk+Y?}-ZH37L`k;)(W&Ixt_JxAcnqY4&lMSDd9)wTo zGPlnEBJ)Tyx{gr1yn5EX%2i$g6K$)W?NI6B-6=I&yGy#^<@^ZJO}~xzO(Qc_ z-72}W%!U%gqy3DXl=+<)3ZoAVqeyr3A&W%#4hkMc_&M+z!o~QRJdJ!hK8WGHk@%f= z;ob1p$a0LnOPdA&qpU{Ca8v8S0OmySGr3kHOEr$I`|vDqj6NjklA(R!7}AYgg|-tP zL%e(5YUFrFB}3StlwqaTBlsW7-~Gv7XhlNj<+#ry;5{TY2+Sv0h|ZzH_n$VsoC2`#gH=GeKH*MaAr!yVxnM+)+Djz(3rTX%V!PxuC2Hs!Rf2OSO17*<<277;@ z;@W$!;)VyOP5K)Ut~!+%djfz#uYVekL6@!2?kp4I`ZrLH@O<+8BS!tkcuX53{m;}X zR*m>enqJ&q1rUd_Vytg*T&DfriasCPPE^sf%Vp{q)@Vt$&nl04ob@Clmn&n5bSc-! z_bPcjlkD?e9YtKHrWNPK&>3VtW4jQF%V{wBZ^UmR-qcIVHT+)dM0=nO%~+i9$&Ldb zbsj1~_lGYhdiF+Y@OoPCnm;(09=w_nypkEboCOCkC=TGWH+*km%Yc4iKMe@}46no0 z2)lK`$a;7P^28dV?z7vZ+zVQX0LZB>V2cnd%fR#ZNNJi^IpB|y>i8S`Kq_?4R3VF z+xwJMt31G@ZQrJmT1F6uRN zc!x}k_vK%sj7rU#QXUg*>({#RFzdmh+msFC*YIGwmkNxXILpeIg1zEe%6oec-za=@ z@6f(C_Fpf$_Ljj%ufFw4@#V*22L|5OU($CldI))pu6@~$u+iUmKRhlptuKQg&xl^T zIq_yzaA;fbjqKp{oZz+Gvcc_FcLcBO3|`I)#_kCY>)9m0X?AeOrq~tnsD_$5zS{}()EeP8sP z?ERAG+Gjk2qn@ju^<4R!Cpxax#o^(rl^`>r+Zg*=*wGSK2rKr8H+uZlO@wdhek^m| zUd%N~CV(G!_^l1d&)ApXKvuASTd*&?ESl5HK5XRk+X(wGzOv5-uNDQbyd^kLT-N_s zS>J(RZ^<*)_6_d8{Ald0(YKLgfdAOm40R&zqn+sURz!XM9_^wzIKSJr1&8}`4iegx zbbhY^dW?R)$Ubr6@}Re@Z^-+eH>2L6KJOd--s=P2YccQOW$)E1-YZwVv1{Ie>)w9! zh&R2xH>5`dG@aRHJpV0zw0>G&TePh7&2p({^*|u{NN?edd*0kNwEKwB)|3iHbldRT%Y%L>fd?ZFY@h>y|xb9C9U&w#C{fvj%uh46-vWZyMo6@g4 zG4z6XukcCT*KLWNYVZeow8`bY+2#G2eR$sn=?1@-bcyf4XA!1GI&h7F$roh%NAq|b z)pIe%SVk`w5KJ-t)QKBDj5%f3{PFQ7wkxfo{W*QP(d~Nq!SQv!jW)|N zay-%dVAYM>H@6S%cw^`FyleLi?z+1BO8(_Nv3m#Z>o4fLKgtQQ;pay+2pb<8p+x*m zgkd*0M!Mtwob{W&Gk%ry-1u64)++0}7@zk>y?q{S-Jsu;$6t)#aWdJq{lzNcN^}`w z+YIiH`E=$px?Qaw7ojg@>AoiI3J)`?;NfoZm48x-yi1zq9qVOA7HfXsn0Ycn@pa_2 zYP1b(Mislm_3YraoZw(?@ap#9l^wy$JA<*j;J`h>{$0Vo-ND{H!DznZWqGLIUk{@% z6=?lA(U%>J<^+3lD~Eh6dGA-nc*k};v65=w8`JDBJQTc69%M=Al`1npW*pu$WsiSWt^_+ zfBMGZH%o`!{>G8(k6$}F_{7zBTzTi^W3eX(p6WXuJ<%IPz5p}Zo_oh(vvSS2(3@4^ zYy&dY=}-4hf&XK=Uj0^i!uQG}lI?lG#>2foZ{i>1(;ctt;(72W;*Iawk9mksme>#{ ztzdf|TtT>ei$57a9LfsdnTv6ZeH$AG&%}HKsz0R<_q!pvSmTsTqy3=Q#KGJzn(^*y z9xh~Y#urQFoUbpvm*ZNg#y{L-S@*&A9|!(>Zv@_aXz1ZL9=Tq4ZSUZ|tNX7MU4BdK z(Sf)27xz6DJynEa2T+(nW4g9MbnVVLk!hy9K^)3oJV6!~y?)gryA`r>vj zV@S_8a4}%wA2sk{z=JOM9f0Qm2Tb}2z-zAbQ?`{u`8SyKUjR%Q7aO<*@Fd{l2EGUw zO&l+;$F}sj9Fu+xa1YW84g6ni@2M)lxu^_O&NGShall0;{dWOVe;oSXw#_*mlm1t> z?-|NpY59Bp9_c-Vb-FhdF!?Po@D9LZfQ>x7djKy3F45^3Z^c<0>W?Yk8*t9%nEVHM z2_Moc4g8dI4#&Xnw!J4&AKt0S`gmW&-E~D^$Dw!5_Esz+zQ?3D+14J`zuV;R20Rbg z)YtoQ=X{Mxe-rf`M!Ko5_lvgo^_YQSue^|-K5Jv}e+T$OF8pr-pYmXS;(rJD8-Shi z{)g>77jUKjqiyN)H-_H7;)}j(CLQ|jO9i~{f^z_`y5M^Olm3`)A0GxteSSyN^Suoh z=ONj`acvRVN#HNJ@}C9F_A&MKbz!{l1Fq5X^YtM=9aqkym_?8G}4X!`ToSV^f@1s{uhu}waE{8`8I4zpZhWC=+6&kA$?rK{1}Tq_haA( zU>_WLLtd%`>9>IIH|fs;z706bz`tl)0oKp-U)-o_1p|@y1mt(1E#(>FzTbv0BQUb*dKic$iUkGcM{*gu7@q# zZ;Jg-$1#Ij=>N{=kHRy|cH-Pls<(wlj`9Fv=HpRfN zF>nJGVl;jG6SalBL3%jq}i4Kf5S;;w#S4?{~exk z2F&&&{x^Bv88GcH;XmccXIJ`P@eDLz+E1qc9rzstd+)1Mjjeeb&dokf%P&W8nQbi$i^HGw=bx-GGl781_}4)iH3TZRvA42L6C;9YTA1 zjDDttZA+iOG3l_c`izZ%KVn<@T#bQYU-elT1K)7Y#u)hX7@sJgTvQ(RJZ%c_BH%0o z!(Y&6XAJ)Ll41W(05iYo&uKpf%>G|&@c)4~J_0V%u-^-q{bSO=w5Kt^M!)^}NN0PW zHRNon7vL!1fPrCe zE5-reHt?r$^WzxcqXzzEz?A2xfxnFV9y_D!DxO#C&I{)e`u&x{!OyRerdNcU=c zk`Dbil?%Asq+|Tm=S4J}0sExSff)EfjE4&NY+vYSMiI_@P=A_CI^?6zfEf6cZRxWf z27W)_QQ-F&I0Ah`e{%FK1M<*kJWP5oVCrLofgul=c=*W%{x#du=Qj)t|D$5cMGy7Y zXE#hb`inlZVc?$v9s>SN4QHZ1=rbDzzR$MwxeNotU(@F;3|tBr`tC9E&qRIoxeAm1 zKJYUQ`W*jYpC1|lJe%Mz6ZS)&u`u}UfHy%e%fPPzpZdW5kNlZ$0H%J?J`nz#jZ4V@ zJLUfh;1a;5zh+JXpZ3)B=geuqtgi$A0q~h_^fB{K0IxzlhD`Z?2LCJ%>G&8X$J@;R zVO#p_g-*}PvaOl|q#OOsg1@EDU6}L=z~tZPM;7{jZ7$$SlYSBW^Z+*eWTCzF84Hs> z1b@ikALVC_B7FwD3o!7Dwx!Pi7&u~EdavKWuh^E}<2P^&a2x0u`;_yhZRtIHll}$U z(tGs=#(YWd%NzLX&OW?>7i{abD)3ulVA$)+)MwMaIhY?@cC~lTx>^5m(*F+qH4OY? zy1(Y!Mmp_99$?suTzq!qTq$7VkLMO3ebB&6e+=ntFWMLMx7=e$zv)V^L^|Xb&yV@} z>yv;3n%~?;z>AQ_jDgYqdY|9GBhLQ5fzdzozP*8Go&9!v3#SS+cRuS@3-sx+tJ^zi~)b#z_3?(U){hZfT`a`-?zU5H-6^2 z;Bvr}zmxtx!0hj){Ot|4rT5DXy>{5I9;CBZkY04GjOVf5?^p_mK|RQ)JS=X@gb{`tZxpUtF?ACafLHwx#!g4UF}J*ErY0TZgWe-GFvfqq=V{;w`cKU8hyJy* z1?kk6TPFQAXMfYcH*D*-(H_1A1Ao!kr!?>s?k#L{@$(1H{-a6%fo)@XTO#L_ZC$ARs8|agV<+1!WeA=`I=_gHjef-oZ;3)%t z2J0tdfOi>u^bftqX3}SoPI=#B(id%OH4m_pANo>3ew_RmKlGlP&VLW|LGQ5{H~@GR z_$3C0fAHg6!1)G#C*UIRpJiaww}Se&U6+3k#>bzO0$wudEw;Ce?Z3mMziL~0zs#gx z)9d@*d8DJ9dp-*o`Y-are!x9n2F&)M{IDNz&u=?>W+wk4=&vI^$IxH3t-qNBykOFy z4;!#Ip0I)cfnW7R`b`b*+K&0pZvi&*pll1=+%J!vA zJ_G*Q{|x`Tp&zd;x$s{B{RzNqZ_>Mh^hHjCW0s|P&hf-$~+%B6q# zA4UFKNO$tbz;7F1(|-9QW`B_Khsx)F$@Y9Ifb@WYzXE;=UHN|x=>X&D3xJP9UieS) ze-HATM7o)u<-@+5s|IZBTR!A->uRU1rD?f&GQ=dIp|9=zod;#D?{(C-Pdxu7l-fiFuw)c%Q;P<$JKL+_9MY@sy zp6lS}rVAd0{?-^6`nCuCsk{g<{YS#+Pvu#F3k>{!K>nS8P5bQm3E(ytjQW3SC;{K= zwY^zmXdbo?alrvUx0smg_q z{`Q)iKGri|8*|~Kf4$b@!v7=N`|2cM(_RHXMtT6SLm%tquK{%Uxj!5DF8cSQzq~q@ zz`q~;460P1L*G`1L)9y;9lUn=s$1}@R$o9bIs6kvdH`V+u+)BgtGF&F+10Qb1?G2iWW(GR2|J&?c;6aWV3&<|kzx#*%F zI0bmjg%5ke^?*2jm+gIN7O>IJz#!~d7%=q{_9pNJ;ODyVrvbb9`9Hy5z~IB)1pX2* zz&QPXCBQ#@-r*r1t`FJJF+5N35bqPQUGP0y(jU-i|DW^k`%mUS@Sn{8&i`covMu@l zJ@`52Od$@(pa~zsdX}|H8fZkq5zHsg0Q5X0(~p&paFaXKjx@lV#vv zMEM-wO?eNW2RuYL7d*o)Zvj8cu5|1t_DXps|Ho}l70TOQWXgj-9~}X{sejHNV*W$< zRT_MZcds*lo|ezUpS7`9fTBuFd0%qQY#aOuUYi8`QUiY#^&JCj=s!Gfdp|c0I0fy6 z{~!J{oX?p8+-B&(U+cR9*lWuB2ec>nwd!>HKH_uE!fN`D4XvI%j50{*#d3aaa0jl&=7r_I%_QY)b{;dfA%aN9t@( zLpiFmzC-luk!IU-rP7rj^5^2d~QP?`HB367!Ud` zC-N8W1UwJe)UWU%z%_(*eF_gb=LbO#{%PR}=p*#i$I76;7rq<#J;3)H80~R20NC(b z*o65|4^>vzzwl-Fr+^*#6uye~hrXmA*YYUD_}6y{$)P@KC0_eI1Ty500#{I z*KJQfK(EsMsqmZ7mr+DG^?^UqHw`$~)Ccm^=S)q0_)~qPXUf}4`EUSs;9Zbk7GP7K zy;%Ryf101Y`}BN74i`cmd*Lr0Uj@c#)EDis_X+Ud1UT2=V|?vvAguY>TLt@g3$Q~U z^SkKp0Q{hq=U&*~z6fB4pB7hpqdoS16!sDFvkG;6_QGH5doS@#{x3qGP;{I=#^au0 zfJQ!hzXf?xpPc+l8sFEg<+=BVSf7|hIX$3<|M&hB?H>Ssk)i*Oz-Rk7{NG0TF8S}v zL^{iJ($^e$fhovqUmo!3keK@I3qT$-3H*JJ0`|M$!@xgHd|m#&a=?{8E^*O=eYnE*F#PR4wFT_9befnWrB-WKXOq=X+Yzem=xlGb z8e7iccM-oPtF5cEs}Z<(b~Lt}Z;CWTx(S#^T?jcW{F)*yR&%7e4l`jud;o%+2zA!A zy==8~H8oYY*ETkR$_s6^brCBZsjuy7>I}8Dx7I~a8&vYbxp1hlr4hLx;igEqLt+S^ zD(9_8Q+w;nq1O6(64pRNTdj%cIB@XbA*-XVqcH?Ywxijs+RoZgq`R}dwyrZ|gk!a} zUW~M(rD`wMLY6J9EunKAFIz9PHMVrMRkVby`qr+NFl5x+R@>Qlt|?O6P}gX6Ha17v zLmf?#NSoCajx^Q2Y&DxlrJ6I74qgcxX%X5$k+Y>p4gxQr`#>*S2>=LS2UsS=5?PQ(Xhg0Dr;8){bW*t<4dr zVi|btOp39H0~(F3RuK7G+ryD|gXMr0q(hoSV(T^5p>-K#j;ZD`xN_H(r@7dlJ= zK;Sn;>N=sPcZ&;kG`8d@)gnja0b} zTOG~kAnZ^JRLJ^Bq`50pSARYfZmtbMqgp!Z(eJCCKV2PqZ^d&jhRU9KHdIz!X@$F* zn_ot?8=);|H%g+h{UcVmv8DCSSm|zeMl*MNgK-y$rVh>osiH`v^GzP+|H z5;{_P4%$rn)QF0ZfUwTK6lzBc8hS815y_7Su*1Y;P|TNwwi1Tfs!n%d9J){AODIJU z*loh-STBJ!XnR|8!$mERW^`LL#mV=F!tIU79_nljb#+9f@pOzdB=nv_sjF?PJqKgZ z`7#j@$qp;c*m|)Ak|hrvjm>Q^f9zGz>PR;_dZ?`G#n8#B6S^CBTx{%w5v8iNw$tL$ zoKg@M8^fIq4o;{ICQAARjYU`*+p6noZx_jH7&5DEZ|tZu$sMrwz@;3WbQDUv0&%xB zG}d)EblM}$tuIAj_gkAo7hoPjO^qF$U>8h4sL&!jTH9;SM=WStV_OH!Z*y}kMc;9V zT(kdl(P%Tb4lFfa{s&||GuP7M2zbXHfc za}puy>gprRFLsClNd!BsUF~%d6oux23c9cpRf0;lwzRNX@i^kc;wSTpfj$v&vAwY~ z(ggRQx%NUt8xlr|Hp3S{Qo_A(Oh??`5VMlVNXfOfG`$SHWJcj49OolN^<(A?0H^<4UB3G7cwFJ1#V~B_QIO&Kn<&Bu@HTJj&=Xi8=%mhA-5D zK^5%>_XZA2b8EN@CJL@jgJU7%CNgyH|taU_wICU^9va0pGVIreOCKys6&%6^{}0$FN-yBb>$?uvsHSb5aciw4{*R>1fJTT zAOMdbEd7nL5Fv0VsXkME;P8>V)=`F>17gwOp|+udwQU@ZnrrLmIEmh&_d7#ZTcjOk z9cE5k)sCc$8cjC_Ek;rz`%aG+I)f-}SI0TpNmeo>R@itbE_=$hX@i!>9o@vBXa)y~ za9kMy!i^ml#Qq9~_FQ4h?vj=?#DXUa&K;&y@hMrSOv%WD%+aLRvipj8a9CsKmJ~+N z3QDnOjgi2mXMP9KpX(=b?10nRmDXnqM& zrXny=!<>zFxL}Mw4ube<348&!ChP2Njdh{MbIzEg`=ORyMSbH7ZIQZD;R^aqFLbtd zIRiKa(%Fc~qzt4|S!qJHsX&ggH2v5T31aSWRBUD3lc)P4ZiK{ei2>u5IO9m5G{xH? z7O9Gy3BVAZ)6;-Nh$Rr4xn7QYOK|HAB^@Fb$(U&a(vtJZELSXe8K=(AG_|(1z5GJs zd8=Hn5}Yir;-uCI0BDrn^}tdXoDm|$}x+=xCG7dc7P=>-_$j))aBTG@Yed_u5ghB^OOX66%+3tpP_z4H8CDM7Zwf%ylg-N6iA9W_fMgw5JoEVzVhT3)x zJCXL5T1*@Cm}%lo*V9(kBpD)&EtpWa7(QHzRi_AWIB-Gf#HCEaIQaJXoLR36f;!u; zm990tCJRr_A+gkg1*13wwZ>R4|6*F)o$LrDmr$w|o{o(bZq*8{O@m`8?{L@9wb3bF zs%>Wj!f3R2TJNhk{Q_FSdf_=P&YcK{+b!B-`mpdn&)0Thu@VZ^!P?ehHQ>PGN362) z#NwS;JUSp9ZIMV==0lxOYAl3_l|R{a?s+b!wscYS9b3fsc4OnX=CUQ}um!(L1RKKY z71n}5TsqEH8i%%IJh%leNrn$SVU@k`{_3)m2M!%P^2C<3qgDszJ+<&ku)=~ypugPE z*b+fsk!L7)@JLA|Jnr`9i)?&Zs^R!^0nS2a)46z$NouJ>kIVFqgKW!7t*8neVrONg z4L!w-ds}LD=m;ierYpw@$77_ga*C5&)Z4efrC7Ue4q+0DT5!bx13%XxMQdd-(y3Y5 z;m1o#=zDP$KrgZiMC~QEcK955IDNbY>)vfx!DpUUt~$cYpx2HriPb$RZw_MB;8e709J`6C|A2J4hTw_3jX7eInLPLdV`gf)wr~k%)DZ(6QKndFd36KC~Y` zD4Us9M|+75G`C}dh3!p?C@%s9-bb@M0gEGpV+R%%HJg~fsqx(Ty1GL+g3!Tg5os?;cLCX8Jg??Ya&?Y31OBHpU8-OD&A9l`U9cr%AiB$2^}~UI?UO` zxmLIcdWp>V2(DbG-h)dO-uLW@ldY{6PGUa~bK2^cv85plwVuM{#yQmxlA#|H1+L6; zkJ0gcol*~$S|Kdm!X1#zr$R@t=|zg6x{hYVQH~auINB>&158jj@xXqpOh2%B2)Bqw zECgibtz>sUbiUqt={$BWxSK7$h3wT!WzZbl3)Dj5vay9S(S$tyVBAK-OB>gsO%3R; z>YQ9#iF9K15h=CJI>#YX52qGw&8!8rDFpAS6HY7LeQd8o2VUYalrC8RaN}W(gvO-f z)&+dVvC1#L5JYoWr^=tJZNn1KxvuliW5KPfvx@v0%B7f6RWx@&k~kSdEo;Zz5F4YB z+HQxBj^;)j-f6>TRCC99r?CIKk+TPlDhT5E-b*Agg;>}r7~xR-T1`xHSP1G}3elbxIlnhQqZFk4m zgx5nIj<72dUd96xalKY#ID%XW9txs4Xu_o((!mEB0v~i@5V3N>8i+atRx&e!RyW3S z^`P-auIz_g-1F*S!x!5k`q2vI!Ct#4LRSmhW#t`qWGd^9 z4i%3ViYJei)T9Zc5F9DB1@%muOuJoh@fF2oB-a+%!EVS-JtJYZr23UFojg|Tb4A%o zEL0hdk;NimRw6&y3uMDe%mIyUDq|y_ovlr?iGuZVt~6sxfMF8*(8-=28jCvWXp7$0 z=EjM%5`IH00b}D5^buT5OrhAU+oU1ZxK-jhj?H>jbx)Us{=vMMUTCH4n2n)6^@)^v zxB;RX;w|c3%ta{$UqQamj~ya&2T4t6r^jb?TC(=Zf++3-H!xk5H6Y5zErjiCHrG%j z;wpGtQ#>|!=ciOmeMOaiT6LF#iRVbADo%3^x(Z{JjcD{V1zc`Th^D`GE zG|Ep*5{M!Dn_h+E#k^w)_jqeMdc5B^R~y@hG499wfm|1$e_ZbP*X7RKLFZ+D`Wd{a zAZ1A+@&hkNs?0)AGg=MYcx_~Kv_^Q9QN$7v=U}9^k$n|p?EJZzd9VIvFH^mWJFtO7 z4`f-Xx2zGJm!^Xxh+|x9+6<8RO4sr&v{-WSyMV}GDdvRzMqFl@(lYhrjnp2C+sTC= z-=|TPv@bcg`yI3;jfsVj?@a`ERO2v{YL+{!tsCCWcU{K8#yGUafc$! zefjTeGJFqO<8cK@5`L1>{64y*A~C3c;+MvK2Y2EBIQ}1-R4%Onw+YdECz&^A_rC1zXG6lcTT0k;MEPq=F- z++A>4SiDdU;bi>o$M*E^%92a2e0A>0wiNDgEI%M`sn;vcjo0bM`078u>h;OhvdbJP z*XhuAA)j-|bL^^28z?vA*Lt0K zlu6^7Lz@ialqEq2@rmRNi^{eCAQLP&ugVctoFhYe_&&+H75KJi;6u}r}gn3dRyMcz9{GOS$j_i z7JGZ%d!PHqecpMV{p`KhUO&I<`+I%YnuliGFw>$~Qbh8n2p1kLXN8F29uW3*cvS3o z(uE>QL?Q0@jw^-_f|pP}iiwo(kHJ$quGq5GAI)d-Wq$nb9+{pHDRUA+{Bhp2aW~Ii zQ2EWsym`~~c~zDA%scMA>pM&CU9OKW)k_vEUow66+y(mWci-DQRy3_%y>iu*Z+>&d z9jlwx+;-j4yYKpD)3W8?`quJ$zqxAplH2aMd)2t6l`EHskuT%!8(%uEbli&DmrcBF z($d=}5BI_1Y!vFKMj^bq5Y@Q4alM0UGOqh^J&$WhR|N8%Q6F#9^%7MmVLb2CC-1xv zD8zur6mkqj;v;YPVw&D>=6gY)4a*sP3td2d*O$tN-mr2;?NykjJ1v1iRajo)v%oWe zi|@iRsxeVeaLzVInUgGE9CliQwXQRf6rsl&D>mtP4@|d+MB@tAq{Ir&kHz=~E)A zTdBS9#UBS;(>(3X5fN^lEh5eHM67wfXm73-N1AIyUlPBHPXTY=S41MJH3r`oF+Fk} z@YdD;X^eO)@iCInC60x~SAPHb{XgLYe)XUD@&6P0Xnr|#s`l%QseGy zGVlBKt0|AvWcrYP4c|pLng5^ZhaQIfn{KHlZ@OH6zQU^hL$8Ciw(}?BPVeAj+Ifsq zQFD|!)u+@C#%+H-vC8QkXwl9O9yXsJ)V^RI_Wjx=y14t77IEx?*WLVD$Obt(v-#DK zPmh6Lv=v-vekD|hx++muZS&DkM)*MR#WB}=_Qy1ztJo8ZzFcuC ziaMgm7eyUWlqJmP{xW3Q+7|Qby*n?sDm^jC^QW0N(U5;G@Pg>W`;CELhmv z6Z$fXeF|af3CWT9w-1VbPdOcRc73#{ z$GdxzBc_8UD~mgz6UBww)vd>=2jP2lqM=~iV41fCphS6Q5bWr@$PFupFSJ$ zj_>(zUVP8t_oqj^^ZWLT@45Vb*NFG?=ywgqy_snk`|dTcEi_SWZPD0eexypHLF z1N*$OQTGzATUIxmWFPs=y63(UT9?eTes#lk)`NELa>YCf;GBs%^!2M_r5s~%J8ZyJ zUbJPoZ7?SM_Lwa`7|%_@NWXSTDQrF4%Z=rh0AYr4l{c1)0ItbMQ*k+v-qJWhOu{v# zae_sT59v->c7pBY=`Esk`fAH0)cfE-SMC?BU3$s2ON59_OmEuJtGiFc{CQejsk^Pk zGKnxvwi%uvt_zoo(i5v}>R?&!S*=B>8}#N5g(p}7n_&G8{5AKiyj(q%BcPpE;`U5s0mgpVUar&(*5(Xtni(tM92a*vEk#XlT?oc)Zr5Xa9P{L7 z+h}J9JSPtMmE-DkXKm@x-GF6>YXM{uzJu;-Ss!u1r2}tw;i~h*D&s=OU3FRAevI=x zp0&zyaXs!1+)HtHx>R>2$Kul5evSoc^IT~%|Cj7D-m@wfh|-PA^lsNT-2RSODe>h2 zz+4%Oy>Rwqe2e?JRYD((JwJ3ZzFEby8_z8#~}{j ztYV&2^!+{H!{^ZjLXR#KC6ii&N9)kCw!~xiy4JfhA8ssF(Pr(AJM}59HuSgA?;BW^ zJGA9~z+)rvF851q?PTI(4si_iZ6NH38*GRCNb(}D$%}jjFCtDuUUmg*MQ?bG_{oVi zwry1{V*kmw<|Zt{tHi&W_a75Re0H>W+KU=h;aKI_<-i;`o7^41`I~X?bnXBzSf+p9 zu?y+j+*y+_))SqY`@|~kuUdz);KV}(AINe)H2S=wQLin!O^Ankb!*XW7Llw!$(JNg zBJF@DaZGbMLig;rJ#>`c9V^}OK;~qHW$>*F{n|x3!aPYDB28{tt$PWBS#?71i0cB+ zl0VE^XbBH=|9X>29DSO8}-9FfMnnD|7+4GekI|ou}1oWA0$b zAAE2I2O<#@vo`OuOEiNKobQXU4S(eJYOhcW^BOLK)4!3i+B}4;41|g~mcB8V`M_4EowM$gA0qRrBYp7AprjZF_N@#{6~-bmsin z4*GRAy7z)N#PL01|BIG2aM1Rm7T5d(2km&yZMFYJ>C~RZg|s_cL=1SKZn-bCg7twu z+g&B@O@$Mk7RdH`SE+lWYqEO-DtG*q$5FhAN2YDYA)H`ekv`o^0_L9;8{L9n^ zamTw&_Eoz9{XJmoP&&~6Z`i+3M)4W<8?=8_9JYT|ZkY5}q1ZaKOk15cN*#@Kl-=^F zo`Bvn$KI}~+K;q!R|)iIz|;OiEzSOsmg>6N(;fwEZBJ-c4f?0+D$lMc;H(yDiffFg zTYE?IMhSC{lW@maql7)jN%&)|Qg6dNKsL_?4b0~}&V3U)lLauy1DvvyX~4sJb(FFY zddE5NzeVO8H&FiyPPdBY0nFpun8*Im{N}epHO;>XEopu;bO+}C9?bn(%>6pd{btPl zZp?iT=KgWa{Zp9xpJMJ`h@KZ`qF;!_;30M9;2w3x;2~S<;2v9>>#X}x*O0qgeN|~w zf1tDu9+0tG}-J1MqDY@GgR~D^d1-;Ob7`>Mro>C}4g=^KsD33F!Le zA{?D6BC1PS1V7j<&8I__=J#L=ybhi6R~3I~ex>41&96eIJPDiN9nA4y#oNutU}L=$ z8V{N-8$6_qAKar|519S{FnvP3M`=|bR@#t$4C#Yf8(^?h=|GvE+W)4dIGWsT&_P>K z&kO2{TBhW+jqVPuEiFr1nw}YbrQ)a2SMjYgElXXMo(X-S)!u7s!+361&nqdm>y-?) zVXIQC=-cDAJS9b4rnKVuDU^L2WuHpcrENo<&FPuihe{hRyY_@)wJlS&0!A;{6{QuI zReMEA891)qqqZt-1J9}VVyv$Z9Jl}Lz;pKhYI|7OX1hn(igzpCQ`Bw1lV-H_YQ=7} z*AsoCVt4e{6+O1jG^=e@dJ5+iX)ht|b!`XQzAHV`)`o9O(^H@m-w2w&Nwop~nsS5v z;}Xa?MbWHjZrguPcY)3dL3?9CBjZ6UWw!Ncifu`{MSUb~w7Moe8}TRiI5qSJl#>uYZUXUy_4qEex80s^91-a10oU~#C#sZe3pxO%~M6S)()Iq zk?xNkuXsCpqT-b6o1U1f6!cD7!@3A)xFkxNgN=avO|&z#U!lxXz~i7Om9$NIEEg;B z{r)KEG77qkf-W(RdoZ50;1%n@A2x$G>;_-x0Z(`w_T^LP|4%WF7sv~O?^mn?@0<-@ zI0ro8R`AAZ@PJw14H3}6G|<6xsTXH~N2CBpt;#Klg4}H49c+)fK(<&I7%dhFE`eMo=$}_Eta2L}zHijOBuAr>H4|>ORcNX>YEghk|Orkd$%SkD`e^_EapvHz|Ko*2Jr2ed_#4ul>@AF>9z3Wg?ih8`d2%pKbC z@ZJ}-Ra(c6-q2pNj-A)85Q038K99)Nwg`1q@T&~SHQHCcfEDlXTSSV5^oPWLAC6$` zGXrTtK(-k6`q%h4KwCYuf#^Gc&8P2-g}MT5atwD5xBD7=+ebs2BV%k~+4ifU-4U!= zpnckPJprts#eNWW1J<>GRY@Qvbiwh8-h(=&9l0qMIxOWg>;>?E8qDct%;|2-X^-UZ zuNn69wT;lh%Tu4E-LZ3zu48^Hix)U8ut7coTq8{_sZU05v5t=T%A`&s?F+&O{i!o; z^v9qF_h61YoGaarY&-&8rd%kOv)gx8>D3%(q&z(l><~)Rg!Dv5OeY=`F&*=E6~3L_ z+7X+~@5G6n(?dtiGG}BObTE`VgX^p;QF<+Yjv)z5a z(7S<;RUO5!XHtZ+N!RbkJM3WVBFtUQwZO$dx$F6+D_zgKHh9``bvL&{r*HM_!gm9X zs0%RMEn#TriEq-DaD_cW_5;t63F&=odt+l^wM#eBp<}C&Kze_`3uK3`eeFXZT!3zU z#iVm0O*^DzZ|Um&8)-w@2J77gfk`Q0?IFnC(Mmn>*m=Y~y}d!-MICq2D%hv1v<3D9 z+8);v?o7aZG<1zyfV*j~TRZ}^Cbs-r>>B53QLyPu=#xn&U_Npn-}kE!1oqNT@d{yyg8O|dHOrBKNQSM&ZaiZ8R28nX!1Ec!S%ECEurJ)+#DI! zdkYGlLA~#V%UwS+@nk`8p(5^n3-a|h&Z!cGL~|>|ATy2ji-L%j(e6(ETVw*FlDMgXai+b%h18 z`%%d5J>cyN!LJrXLvW^eq1O~BRlxP>px3M5H@XRaqx%9jkrvQ|6?&cm_}qXs%IZL> z=w1KAb&c9;C9XP@o3zsk?Y6jUk-oEky1L!=nKsJ4UGqD>t#6-n5c=C8=%pv@Ppi=R zs6%eqr}u$3R;ksR*YokkDN6T{+LZ1UXw1>0;R}i`z;NqGJ89~^f zuTtg!KH$;g!K2H-qo;vK&jvlu2R+vS7nev}d<*(oma+hRc{=#Bl2%S}6Jz#9}5#Zh_!1)DR;?86mY`*V1?_j#!>uh#s(SEgeDhr)=8F`&|yQN>m zxx}4i-=ox@=tRDy{05sjuGBe~vy4rZWmdqafHaO<(rH1#M}a)R>N0(yO+#MfLZ1So zLocyk#UOOfNwRO?GcRfPNm=Xx{nUYO>RGq*es`Ah0e3e0a^fKR(WNYMwix|@9k$T< zU3WIp>LCZRocDeu-|7+h?vwfIOx~Ykf3cf#U$f^yFFK!o#q<^tp1wv*moP*-(^rcs zJKExSr>_z-*%omJ_zVA8=p~FZ6EOD^|B|?ed30lpnH(c=Fi_x4lXG#4;eP>Mzts9j z;k}ieBFynrc8KYmFV>5+Dv7II;(E}_LXH*RXPVzinV(}vz5w&FKb2j;>n>&C#+d#K z>KVYvOW*{aFXxMS;Y%T$7S8MhE$!87qg^86Jm84|Csg2U*trHWNOyNN_lU^GUR`bO zLfW%P3wUCn^%{(O!=#qbOPmua>wX2iehoN%6uA9rXne2=I^ZjzvS6j~g1_3pUo*g8 zeTjyg!;ok6^VWi2%)t8|@Wz?;4HEv>+c(HL*X%!(?;grN>IF z4a8Ab+@s3=OmvEYIO>dhw7?`jqu#j3G0-COh=CS->-0F0rhu0D&AR>oeT2|259l6e zALPul5*CuK)~~>~Z0M6K3^-}+<~PV3$R(-!4aN&DfM?slvopZ6ec;)J;Mrrrv&VyH zmyu7*{Z+`qJKC{=&khyG=Kl6%t!*3lh?~A1whw=x)L|t(8+j!UGkNn+xO|lR?mo;r z*O7`|K5 zApCRJCwUpt0^$3_q$EGHKcd`{)b*gxjtMLmr4ING9UCvDM@PG%>7+x{6K36x zEpA_u_aUtcbd1l{k9?`djEQ>mBAwRpcZ&S~i<*Lnc zwIj-5?6TZqmW#ey(estE51M5oX4yF-%HCj>y@_SH_6D1&c4Ya!mP22#yyJ*FzVqSu zC-V78gZDdrOt=_w!tt*L?|1wJdfiw0pD6IJAw32%E$frAY{g(G13Kb2sEgy-hv#1f za)iKh;b4dF?26OyKlp`;`}o0f-&x?sY~Tp(i21;oO5_`};uQGADe#Arp=m)}E8Y!d znF;zDEZ`)3z+r=QzHFdH(iS=6R(t&zxh&y#}w9xHrPb zL|Mu)Ko;dwCI@^<5&ASf2ziXLz*n3<4&%_?i1(IM4u;0TmusvYBM<3@TZ@8}&>jHkudh~Z9`jJr_e~are@WTtqQ%?NcyDQr1n7p-9cf%gYWPRl8kXhSW z7hKe{nE#DheHZnWYiYYpP-Bzsv-dh1A!~2DRMt3`U6eJ>+YMQ>{xN+XZFSh+5$Mv5 ztcUhRWD@O6JQt?M)(?hDVP7mXd5!{K>MYie{_bO*6P;?Cwodaq*NJ~hzO8>ObeHrG z7Zl8({bJ0KT<2QO^f%)A1JECV|8}oy3;PWCLEgHy893zH0sH|T5vJ5LPdwxshiCtZ z@A#(JJ0{1;C!^-N*Tr>V)}Ob}tp8rf?Rk*X;1SSS%=(eGkUD`W&u>AS)W@!$*&<2? z<2i|rcxl>K>;8Y*Prz_F`^vc^d;^8)XHK-J)16C1r}o%nssBL7Jixnm~pL;b8`n_1I zA9A#MRDGH2-{A|%a@_BhYlh}})g1T`{j47{@9+E$i3aCxu8U(Fx8iv%c{`rJBm01H z$U1Y<`w-M_pgQzNc>pbw?%(-CnE7wO`24rA>H8-KXr;R&TIJlrkSQ#_GTn z$WyPn)ILS?QO-k;JIa1s>|=_!_QAITC9^G#~P$1mB&S0UlSKsLJ2g-tVVy4%Hc*F0baspH zZW7Zb+fuYXv|j^$X<-@g(EEvhi3SVetfpq5?=}1mduu*ys`=6PVAr3Xx~}=XshblG z_8!>|zEfT%s`8#i`7m^+CD5Jjh`wBY3i{JM(4W=^I=gJ{tIuCh(jQP^C`ggKywV z>ePTMVLcXfApqy`fIa1B8Re(?1Hg0Bx`_Hiz;jh@dVh-R_ zNWgV*{yDxk|MFif&3*r3Gx9jzW*T5-=$kK>F~;iebk5-WQ!I>oNmS+hp7~jpcg)@V zz&^xw7vwMJc!HmV+PRFe!SgQ6m5@5x*Z8ds`ml;3=q`YL*`!Wk1uUvDo@wxdNFN6I z4t({)x~!|p&}F6Up6Sk9ajZOpI^kfPdTIF>wCM?CrC3)CmXF2zc&^WrhvB&l-;`jD zaO2y=;Jf9s;3J=e@`Wp2DbK?-4%gJBr^}};eWkoIQI%bV`;300-fZY=<550q#jE8$ z=#y7KcN-r(RZe~ezkBK$iKEbEw=C6H61LD~$M0CJS3}P%t~?zYGt!5Ew5zEnu;2JL zmSeHEmO$^IJ@qfAfIDSTz`)Q+%Pj_+EU}~y6|kZmMqM*Z_-tw`>;{}`jPlSC@g3z0 zjq-+`xZ-rhZmxMySHU;{j|%$MC5-Kz%EA?|RCEo)HV(aLCj3K4mvB8+@f2{nM8fW! z3h>~Hr;&dT_5I)rLw=AtVYQiOB%d(26 zq1%24-S(rTZkrrG_{D=9%kUTqa-Wv#MZ@wog|e#Y~w?nG6_x5z_-gXO!EJX6Y}=cPRILWV6ReJ2_+A&<&; z0~UrnTAa0k?-BKX)0t6nz-xrI`k`cA9q9Kx!1X7| zGqG;aM>;TZ{S?+KZBf*NwMZZQ-i7dYkA<&$Jp9~cSc{wnxj!3lpAVSV0Om^o^E=Sz zdjRvbg!$ZokR@0RKB!9B(Is^T*cG6WlE_u*eQifVW1s`iG1nvd1A{^(j@dVAXzxx(|mP={q&k?KV|h6(XN`bS)|z} zXg2lr=c%*b+&)+_vHt-4Nvk|==o;?+1Mn#=_oU%n=|2FU(kk#^#!j?Y{TpM2*T5dj z!aav}6@7=W-CPrwVa;c!GMDxk=5C=g=3cbr89gy_%|}IK3CcPr{zh1Br#0ugU+ZQZF~5B|=zxR{y&ya3DyucV&_{M6h2aOfH41->e( zQ>mNehW=cTcN}`vf*mU>&Vk2ceaOFo^9Ns!7wZZ&iN&MNp+4&D53&w}-+|7lXJgKR zGmHVJo>~K3Sc~;e$+sN0U6c!sWf$dw<94?dW4;dY996N7burB4^_#-F8$KlH`aZ^- zRtLP8E2;Z9zL0eKYWZ&37>+wH^|8Yjfws)pv2RNsdtLMC@*d(o>ZwUoseeKF$D^mq zeXwKRL7gQdz8&H>_>?A_{+%gFpA6E7wfaxY?n3##v7o&Ej(eAhjJ%SKFY(j4A`3R9V4FYAgl)3 z!;Hll%GiNfsk?Fy>egDHOW1LU zL5na?M_8`GUypP@WX?oflMt6$^voXE3y*k;nCIcuD4zv+9JhCCEoL2YB^jSMr9+o> zT!T8Yux=B;HPN}+tYeL*2;-OWyCe4*Fpanax%OBTcfjJ(nC3EZ4Sf1>+HBFs2SEo5I1}deICWO2^;F3dgI*R z(w7lzd&NK?MBKk`sdqT)VsO2 zLi&&WmZ8WL$Mc|TzkLSuRQL>zErlM{O5UA+?qsOo0`o;CIQFwo%!jozzvGbLn%dCG zj!|BW_bmGZI=W=tJz2lKNAaJmw5gFYwDTBv(&C(+lMMwISf8wWieo?OwI&w(U$R$e zIfETJLnm5Ban1Y2ftFn9FZ>7N>odlezJ0{_?uQ@m%VhgsKfb()!{aM=?1%mQrM*qF zI-U@QU*w-056dMMd;i2SfsgEFyMzbY-Ouq5E{S~K+sQb3Ij^ucAd78-SLF_YKRLjI zeS=lGXQ>Zh-ncFRd(S^(FeP_rF!sSiz@_QbOAUPg`n@^WR*leuULM$`k98jzC^S$ z{{y<)Z_u6C!+JLDE!4?dKzBnXO_QfsNAeWJ1_Iwk-$OO{W#|~Ss(g5o3J$r_Aw9{^!P*wcQ+_^o_8eTxTLLU|$Fi=*ycE1zA()BEE~?IaZF5VuUSaE!<&>jS^(N#wgeXI*lvm+|FtyfU7`Tz3Zj&N$ZV&&Qa@ zkKj3FNQYnAKZ-O;n=9}`#Ii{qlKTI`6^CIq*K2YF*{~YVZyN~ta{W#yjyCSrCuH{~qDfZL6 z7wfJIfywrF>4!~JWgVfPmU&=zT*hx^tX19pD(2LXf1BkTHe_-=PTE<&YzfcfGxu6W zAF*IP*L4B?ADz^<0ng2Zqr{b0O_(AL`u1IO@jlE)7@uOo5Byrf4*XoM>tjDX{Ub9t zCc+u|@$#?2)EKvKBKOk~u1T1}cbg3zuE^enIGeaoBurO(1iu@8UHF9n!z4Vh?p`lp z$#rY?gKL^5Y=_~5ekKZXMsY1J*?*~HU2gBkVqXt@P`cwuLyyIpr`!V*?zk8$gf-@C zPOMTtp^lC8(uY?;@65!$j#aj&kp`J#zw2Z}-UrlEV7JJJuHeG@mc3Gc)v-SnI??7pkLV*l;Qr2Vxq|#JM$R?m9_Ew25cYwwrMtm1hGRV2 zvA=|KBFkTf{t)vCnB*@qW!v6nj1@X?q12npggy(f0pA7R)_kY~dn%!?(GDE}3*t0k zz%s|ys)fui%Pqxv2G;)x56E)rK(PICefaK3ECxJ@_kN{n(2x_5IZ~E_=JF;YkJ=y4 zfqx7` ziTTA?`rn1#JQI2|bS2Q`81n8U&KDzQ8M;(V!awW=KZc!;F`DUZtdH-&k9{L`lVO;d zv9#dpL%3q?dT?_5gHsBIxCRGX ztsn7*;Ps#fismBbP1^%2+wv9sO;q||O7lv&hUXZqdi5sR)Ff7B9cmXXf zq^a0hGLj##t!B(a)nwULn7A3FEf4%;U8r*rafEHMFRa7BQU5TGN|}!Rso@FYOt@V9 zZlv!XX}?eU?(ywo_?4QZ{{}ubxsUA+KB-?IuB`UcPwnT4E5&+D!9KQ}V40=y(?5)^ zlKdmjh^u5@rM`o;N%%+afj@g8Wb<0a{ey?E(|eC#|Bv_yaAva}iz%cN;N`(Un+35J z(AWG*Bv5R7nX;7erqGM&ldFc@JHvh>F6wuHTMhj_;RnRW^zq=E9D|I3cmO;XYsR)5 zYtE;C$ab)At^4*aXMMvyR<4_V?V72f$B<^wuX>h$BOX=;Lm_Q4;1%&7 zv5iNPU7YpQ-=2o#9$N3m=*FJ_;9NtkD-3-#ZT1dzRC5iZH=K1n8vY; z#543!1z#|D66H~T`bQ(>FyVnZ>o`v7)8TsGgmlJYPNpA|@ua9D%dEG@4xbI`s3)uj zAESKqe1VuDxwa5Jg|$ng{T}erG}yt|7LTM}#08<}3b{+s{a-lA8_XS@gFicT0NWjpLf#7ZfQ z<9-u&?qN8L`#jo{xL3Pwg1_`S?DNGQA?_=}zMv;DZ-|o&> zBGwb*o{hB=#14QLAm$Tmx;*n^ICcR2nF0JNixjJ|z@7G9(A9YQ#_1R5JSO4(72i1H z#Otm!{_bzt~-bGM1Dek0NyL-3*%c!Cn(QJ<2e?NClNnv zHR4pU-tvDNqY8R$Y`?SO8TM7uJA6Emu@~dT))iwP650e@Zsfiw$7HjQQ%3Z$-0Wk8 z*+(MSIV;uQL*2iA$kw%&@PYLk`%nS9Xk1G$%HD)|NROY&W;Ux7}kA5R0n9S@z= zziA^jwgcxjt&iP1%meW)dda$M$bvZzZ|w-ZDD%|7pIqrZZQ^hj_Mz+Yx!yk&az-$w z2J}vQs202{n`zhsj<`Ir5PR0>U!AuUc5I{tv0_Gyr4MnG*^brjI`|k9_*S@dFjRni ze#iIS6L19(mn{9kh&3+4+C>~=+y~vaoHCGcLxG8-JdC}CeddSnKMTJ7(K5#V1mfWk z`wIWQTr1L!!SBBVv7wJB3lYcHe&PYOu=pHk=HKky(F^u&@~@^)dK2Q7_XYyE_q!i~ zAAi@tBZw1t#G?*8;u~`AMI8Af9&O+e#0enw8P5*;0uAd6i0xa0*uFJ57Xxb$lu5~W zxy!6SF!r&X?G>$oURRiom_qx0*E+-)$+2_D?`R zV6*p6K)gZ+*2gBG4;@$^Gxaa>V#Y#~U*o(AtN|HkosjmDJYiTyf#(?han};~!k?F8 zFHfJGXmCx#`+oC1jQ2vk_nYsfc(>ra4?OJ)v?F6MlXVaKz3JP>dMfs!NxfuiPwzbX z?^y@%4SpB2Q<=3;>IblmxRyZwCwOWZWKlQhRjwZ-c^GjRw&dqhu0qbx=A&$Ftt(z?8L`r?i9eM)h+yiX7Kah6hD5zbP&sSx*@QP;oYUQ6DI`)$Ro^8L=@ zR{4H!@jChbK=FEcUr&CEd|Svraeo-)S>I!TtF*Bhf66%2^&PQ!*emHLUc$$!N3L`| z3H_!t&=&f9TMu+y*rSrSRr^|cBeKk?(6~gUtDHQ{z&rS{M(hs*y)ah# zDE-}RNAgneP~sid)l#X8LMFE(z9rlABVP}nSt^C~_GDY67wSBbE7NuG?akX_dKmnn5_O+J%n5u2mV7+_4$pb)Kk2-w zC0)w@k-7@|om_)LJ-ZY2uH)$c4BB)!LraMi3orYOI;xYj2N+zkZiICR=%Oyh{Xyo0 z4l*D63fa~=3>WO3z*s0_5Ce|2IK;8NQK$bnT8DE1IzksVemk}cG8yL(>4>?7-az`v z11um97)KJl#o}g+c+&5O^@>vIFGE`NU3^cT-BpnD0_O$&twTTRWuD|WLl3veF@wj! z-W*EQr=?=ft$>vkF#B(b265P4r{v%rJV)$9+QLMA>S6Lt>v0Ls zHl2HB3?2I`a7Ui);I~$+Q~YgbTj=M^$GQOD@$9ecANzKtR!4sm_S`D{s}R?~cm}K~ ztJp7z*k$T{Q`yg=mbWf|&&YL5;xc3}_dtYk$NmRpI{L@G5QRwd7Oj_iA;#j_!?ut< z9`R3ifR^UtxeVzF=L_@0YJ*`NZPU`&bTmbE`??j71qE+rW7}EH7~uyaxTQLjSnlxj9jv zzJYzka}V0D23_2WdD#s(X=q!OnmnxcheMp;`PE zeMIORj~M3%CHV$$P0q2253)~8!@AwKh!4P9sW-UF^@Rgn@Gm|)%8S_Mh4k%1&wUf? zCE>~_)){Y0d|xt6Dj9NGfDq^5?476%)!G-idR#AgjPDhTeEg z50eI9iT}IV<^afn!W_$kgNyK$^M+| zQ9qIXEX1bZj5t5&h`#(&GVVX$|JlesUc1DNGZDn|cfSF9@|W1VHLGBVHja#u{1xKw zUWa_?L;T%qa(`zT`Zq1|ghOrJpzD!#p|kw9zk$*rTbsskofk_Lm}W;HtFJ**Np2 zX*tdh!<^lLXUDH$`@{>*VqR-UY3?lUQT_CfIV~LHmIuH45&Ofs9PhXn1d2t(vDy=1 zorsNFsKu@R$RnZ~F>~CPKPzxeYCoQDfIfID;9i60dDyF7tG%j?#@Y{b^3?v58(c3o z;coUX8o*gbN!npO=tCy^q#@=`*3su)i1c>W!+KHAJhnOUelc0ryV|o5eW{L|N8RfS zUTngBxV~SzIVRs=ZNb@3|L8r&F$E73MtS4zn2NFY;k!Egy_4T*8!}Jpp|Cfymhnq| z?q|#5Gsn&IF+dk3feEQ+Fy@63#ATyDyWkV1{g-oRJoU_{e{l6ryfv-V`JiXsY^>+N zCWPP5^(6btabwIY@ogdNbFT1kt-dBOB~2vzjdNJe5$?q6ViEp|?y2;Q#J}MCX)yQ+!6T+(oYvZe%;34VeG3oUG_NAp91Y$m!2;BKO|jGOz+3@;Q(|K?FU*m zo?lGH3o>34z7g2G#-8|LzepVC!rkt^BH&B$${0L@mn>ZWsLt5Tg5ba5OpQV8-+4~P zX8vr;nu;~Yu*VARzZ4uS+YP=Ehs^k|OJia(nuX{vvGPsVO{n zt=iS=uHD+8^K7z<=XYdk-IF>5&Z|?WuV1@o9L@pE!`UVdwo4vv&qS=}L!Joc_Xfma z!tVq>-e|^UhU#%vDde_ao;||3mHPZ!;1w>MCG;EUG)^%GF}l+NqeLCzbY}+~I2-qk z(0rV2@ebm3YjDm*BlyV@>@AXhYw$SZ+)y*Ni#jjP^ztF@w-9l^W5IW%zni{p*lK>B zL%K=7>UrYU*6Rwpss9DgN1RbLkFleOUG?KEyT@@p+t9|J#TuC(XZ-Mt7tManWX8-E zVUJ-4*2n6gQ$G$~)5S4@HtExQk9BOlR@cEpW;)j!?X_O3PoK0Avh5q-aW?q&mWDn* z*67a%zTgB9C6W-9`v*7q3!H66e#}{4Yk6TQ%E)*)`!}lD?#uRZY~MPibzC?C9|dF2 z=Ge<+pYFtb&*4}S^`1Y0zt05B^8&95r8Q9WJLG$9YkBA}^NeV_hv$_dKlKu>ogHTW zU?5b;vxPS9+PjF)xX)#|q61FLq=VLW=d*g(AA64|sOL`@LmtMk2|l6rwlj@fbE&ln ztE!$!O;6Nke8_s?Uz6w6q8*#jj{h^Z13j#0L>-R;b_Y>U_lW*=BW?TO6nrB2pfO_rhUc?*9xI=Xwt1FO4azXqA8^ba$1)wk(*4vW zF;2vM4Z|vqG44hT^1^^eCHEmN{(|1aWO8*Erjho2hp z&jRC4p7-m)S_kOVcaZtzIO?5CkcU3wgUmOg4}`lcw@EL%bwqyF$^1w89cO*{5Hnwh znEA1YnIDgs`7*@JPeaW7Y{bmZhkUPrd|v|j{!gv>V7!Ig+l+Hh#$!Ks8El|3+RxN+ z0#mdm_+aX}jzJxVx=hl~Ry+~@Mfl}{W%g?0e5is&!CGrS)A`Q)!7^=sur`%8S{}<$ zwvacWZt9Pxm=E8Vsvr zCPN-wTpMU=QTx#*_JMvM^q7N(KZ|Soad}#FJVc7xJmi` z0Q>^jTZs2{;4?Liw_BY3r@cdsEf$q?(|_8>XRZHq4)2csL-L!q|By`2=|3dX{rz#7 zu44}t^A#cfgZC@x1B=E}W3;UsS6SS&t&u(&&mP*=&^f)0%Pskh%PdzREz5{Ot~k{M ze-8B3%bpWY_^@x7^9t(AID2n1aRtZuSmT?FzTXJd`y*|tbpn(gZJRKRCG&T8xdp4HH=4Sm9yJ$^ftOfmam*o@2qQ#={mV!`w~-Zq5d7&IfK9w)byB7KiTcz<#DqN2xoG zwWK{7&Qa8Oj^g=lww@_CF&wWvO=4=B(Jf z<3CUfnP%(Kj?Z}xHu|er7sWSiqgB(Er`n)1&w`#2VLs>(5!(*!H(I+jOIwke={(@s zg+0^Vts7!G>)*N|^fb$E{o&pp$$HTfAds;W_d5}CPdCpGj5BJpbeN?}@W7IqD&g5C7=6O7GF6N6(R`0?&Gw)p*w&AGvmFxDRtH&xXY z$GOWbUijmLJTv)t5$vcOQS#{@Er%@M!a3+Kgf7_1G~b{ueDJm4+yR00qyo}N^mrp; zVEoD)y!%c@)i&rXwK!j`=F>!08`5urJop1+N$`B*?jyR`U57OkKVmK6H=xYe{S4k+ zLfPJ{_d$+KHspxX6fC2EE@KbjOjNjd-cQzLKhy6=>qS&5EC=N^XCR0@|_*H%{w%BA$MrtzTBb0-k8|DR(~G*%od?P zb&==9szMoZTc~JKwcN&=6cpsV6Q2OVICH_4tmlDp22$KGad`)a^M-PIX>e_f&HWpJd-m7 zd|$Nmnb1+5D>kqnzTf?!x7BmX+=2b@|9(I8`oO`Qp``~y$8FCm-2+|telT=upbOt# z484Kp-_q}2smCP<{~godiD`gYWNkZ4+VQ)QQ{N0ygPh*YB`Lt3#d6 zJ%-Ll1Z-R53^CX5&9&WUv9|lMvDc7e3}mN&Iru1GxW_j%xHo5rJ~7nO>3q~9u>UaA zxyPemE!VnbO~aecxF-#?$~etB%(c|L`&#sp3+zXhxb2g=8gP_x z=v4+zrXbcL=Q!{ZYo)$v29GKDIdPUc1M<}->heAcv|hxgM7`w{>nFZrKY4rAAyKj{ zE=oB^pkK@@&UfLOGVJ-U+0V8BYvi$h=n3@u_d`#>UFnC8fV&m6hBMv$q3xpVFy1j% z-_uP`h|X|HQjfC>zcMKLWg~K`#ca*5#&7d^+@Daz5o; zE>6u3WWW!%68^MCcgKmw{13>J;KzzJ-7Uf>J1^h~eVAD6{=8|Km`5E9-`ut*xEBh0 zR9AUC6Ys)a8Ne8N*J$K>3TatpK2_#v!P#i5Jeth2%H!DFw?q$Yg0Jcq@M$e}-vb?| zbbAbI*ocArNArw5i#ep>#A4Tn8sgYok0Hh(?#?7kT;D?+Ot%~H6P_H$8jlz4`O%+z z8an}ILTI#ikzjP)RJAaBahuMysIAXRX^;CQVe4KZn%`Dt&aPDUg z(*8leaQ;v_|InBi3lKNF_d^ z&dH$n60FCD5tkpv9CjxbJLFmdbc}s~R}9}^hZP*f_j#A6-SJ;sO1oEbznMY1b-u$U z?P_R03jbA>tz6RX>|xs7qRibwQCT8cdhKG13j>R7P7QST=&1-(icB+scd z_q`|2s06+Gc}^wI6h)jyF7kE)KV#s%u|$FMAn>gB&SjwjIZ4m!xf|LYtE9lQuR_z39W z0O?@V8ul+ymsL+c4Cnx{3>GnruXUrJ1}!W>zA;UW(!S@uhvy*!bUL;w)^e4@;Y) z?~s0T8eAF8xx;7_G1PmfE4S6pn<6j2D6bhHIL%Re=+ZM4eUIWr3Wl-HE!js@Lu2F<~K<%(1&ZFU|fe+$Kro&#{485otHY@idd?xn-B2B)tZ{|Kg z#(8lME^MC7gEMl6PW~644}Fla)UcZ>6Lr~)jdvPtu={?wM*JRY_@+3 z*QfjC}@i z>y7wy4d*`i5o`KA?t?*H(*i#dj$N>~@VtaFJyBQi6m31|enWuoOvy*7`z_9y&;S4U z<+5pwILGWgo)-~#3OI-T^MNO^PVgg~0gkdBCxnKz29)!Ch%+v8n$Yx-$dSFjNLPq^C9ieI{!MGO|!u- za*%0n#EX?-(3`StoLAO% zM4w{AnH8IMRY=~*SY7qBmHHO`*QQ_;aOd2%ULA99xfz#-II%NU@d48RO!|@jF5+w- za2Ef=2-wjd>jSJ9*FBar4H($~&qBax6XZ!&m4ZeEd7vCAfRwK`IlyTfR^A&gxJgzkA$a)=hyD{hT{~!&%2w%Zv{uh!o2ASZM z>x1xrvd`oT=r?_ToO|g5jaI_vSOtB)PMOMBK*zIg{2zej%k?d>c|6}7vb~=8XMY0! zlhc~cGaz-QpWN%UZauV)abID+GwvBWtDr9!&&AlA&$O%i4MdMtGC|FhBZzBgQpN*4dEzAnU_CQHCs${YIPS zI*Uc}L*f?W58;oNxCA^SjuF2kFPvrYLd-v71X>|KVBff(lK51Y{t?nO`MlW8J(5|8 zI+Za8R-8M-b94~D(%8@M@S&;Ta}LTRqbz62sNZ8>`BBKI$t;7m5d#d|yxf`7`L43L`0rAKcOpJJ#}dYx z7uVdvTUtV!WO?f1Jnya{V-eZnkfmQrI3JL3evx^Q{}J{Rd*jZ*KAR_bLVeCYjHecI!p%PK zxIOd{&anO9Yhh141+X{h0rI=v&HsA=o?C%0SG(D!-OznLGGJnnvD|16ezsAmBiHwm z@$7PpW=uQBl&DMTf^RX5_$As?Rgni!f4w^!?WpSM;j@Eo;~c1n{{j48WFE7c<3LK+k{CK@<1M{NZ8skjb&|N1({tt{cJX6tT*m!nLl=DrNPwGp&?e{?4P=W7A} zr>P2Ogmhvb@Ue;>{0Gs6=0`#EofdCVJ7POwel*7{DqJ*Bv2T!hGA)e3#hCPVd^6j(%l75F zVILU%y?{Nbi`~)=hyU{);5pa%ZyheT*gcAMvVPtZ_1a;4yL8O54%cD%U5J&W*ZzGt zcdA$S0H)ackhh3%m3VrdbW!&v;cM7pmg{l8b1K%_j~KiOy!D^h!$mqrKVnjM0iObI znnjr;^%x_@SMn_KD(EilNqt~;s<XJp0qyx|jY(>`A5#3LO19m>4#pg+72lx)pXR&SiqpN!Sxt18V3! z%2li>m2&L*YsmMSC>kkalkyd|{7(`4K)jqG`!0D|ilj5h8_Ls0$ki)`zb#e|k_JI% z2UJ78@;phC&XjGiX~%=k=9#fkC6Z?>R+D_e;Is9LAIKz+{_ zP44T+Pk)ngm*4;H+X$IVnL6;X={rcp9eNFM5OdC$_8#&E(w^MI{z>jO+Udnb;GbA~ zl>hnq2lYy(VIO-6);o;Un?9cJV?V%r_%-_QUG4)xzyI#+ zcFxPSjj-thqdfTU5f5VXF4?=v*t_IDWGDVBnCB~S|0@621a?nP5zeyH9?-HG>y388 z@Lgtc-4J@q^rH21e~UFNQMB%E^KQP!jCa^`q+zS`5caM-AaD-32%CHxu;<>oxxa<~ zsFbnQ$QQ%B#awkpY!Bics=-%hNPmX8hK)Re{h<#8ye53&kZl|GJ*H@_N(y3Muc0q$ z=a$fMro;D;D*3wU`x@zk8W}GRoS8vBz-=A)00eZ?3r+0)Ls2wY(i#pN*}W0o++ zU|I1J;EMdeJJ9X&;_%$x!pApR2W!EeXv5-$2~mt1vM;(a}y9lR4`4}QBW|B`Ps+fD?zj9r-ZZD zq6f>aSppxxIQ9)P*P9$$5~fVYe*TxBgEbm$>+`tBbK`B;GhQmhYvmv8VVOjI{vC*2 zGTY6c&u6(8v_5w>o{vCpn9F>TwW1$7-8+cGdXMvO_@rcvT{dCLK1!S}BuN|Z-J(jBz_~`w~5>M@9-Y>8U019rT0O$!E0o_#1Y6_+KHP{Pb2GR zUs1U}a}@QkFOnw}T+#K_vkU-V8M7hBg?+_5 zTh#e|`1(ie=RH|x>vrP*14%bLBZIUf*WG`Jv!p>oSfgFwY;yCwnUS8a zv^$dX=O{tU|22Oc$MvQZ*&T)nTU!6Q7(UA2S`|#DalYZx~`hX~-KzD_d zMTnu~83^<#F94r?j(sDq=-sJp5N^QOJ0sgpK|NMK*+0&Ip-_0L*5{$DZ$bagZ1NF5unw)kh9@fA1DLp1kx zzTCbsbX3Nf3W0b^(o)uk&WcW&1}!K!{NtB`Icehy2rxiJe#z`lk;D+ zGVH|uDaiB?{Bofh8I!tL8;AJPKIjlQQ#bSlafJKXntxOAA?6bQ)m-s8+K)=T7yrEl zpCu;X>Mw~(6aLsY2yXxF&$39U%Av_cRC3lZ~lQPS(4gOre0;QQ5sgzjEHOPw@YASU-Zls{#L&)(yWT_Q4CrsMey6I=t^Hj-~M2OJz4? zCZ2a8tzAD7Lrjt7!o!Z!Ws&RFsPWbNJKqpa?I|7Rw*naL!X+%O3fGD*M& ztu^RC0*1*zxC}%sij-tXBorv2Y<98AE_8?8i6ju@mKfOuesLSx(t|zQ9z9C8^aVdd zD{@iWL{z|oEw;5C+wD1c_V(9sD*AhW)-wULy07Ptc|G&2XI;N*t?&A-?{yxrhYvwR ze#!GPGS&>|m)sc*(z)|&3I__{}FtH3wx?f<-sjUA^$kThS(C8N#i{bgW zo6NdS@HUca(CRD6utj&7X6>+2ALx=Se zI+Bim?LH@a$dU2EW1or6&*fKN@Z4|rA$c5qXi*;+3;eP%@2el$3-SrZ8u(R$UQs+p zW6Q}{YelmVd#alA_9A|dy5ftx8Qg8H;Ce@lGe&%+$eG3dF}=~s8+7O0$^0YZg)VWG z!K-^tc?S26-bkL3_@eY(+V|xV@XDf+?N!mdqxe5A(A_aT*XV8gev#JV` z2NGj>i?JAA6?$)->PG)C89kfSVdU5efBMLqYusV+JXve^=zdLKzXUC5WTv*QjpGLo z_Xn)j!|rh8$L=t;vQMFhJl%}726hi;$j(@47j?|G?*LBpZLD3$c>}HAbB9e|7ALh= zMbpC2kaoMdw0o;NLYWz?{RNEM;{M*0*>A_^wrP3C8xIfAZm&CnFQy3kA4h4oBhju; zFs0pRueR&>Lj2y6eb^QZw{CPJ`!x5(e^I8je>fjFXn%dd?Na-*(YqHZ=&iLzgJTmot#sAg3lIKm$#WnqD!T&o8+EF_4hSY5RR~#;6li$`IUUop>sXh2?yhMF_w)NK*t4;bhRWRN}yp!NF27Z^p z7e+xqJ88G3OVj>&FQwT+T;3e_pNY< zzQuLFVU0Aj{xr>&$u+0FY5E9n=J@%6vP9nXRcPs`Z>{BNF;m9e16ik^5!+LI9>GNA z4Q-4ZmpZ`94x>XbLdPgiLN-@e1+RdymIe;8j`wGZCXw7gIC_HaT1$yr$S>Es+$uQj zAAR}T=rJQNiJs0a*(iNx&sRKoXV9<7{ECHbG_>rZ?mnncBjhLA*%`& zy8F3TOcpHbp0Ca2{X4uD3Xj8=FmhzUh40uaGfw)VvDDKS#xW2dbKPAUX)FMY;8N78!L7tEdX7tBF~Jot)8x=U%BcN0UBF>QV@Fwej!>ULq@|G4T?I_-X{ zjtR7n;?Xv+C%Jc~n*M?lV~jNw9}|tLd*_3-;`ua?Cf^hjl*Zl=FDkUL<}vYan(TSa zKUo!Z$JoQgYSW~l&q12;3vcFy`K_{b5=sNFF)uGLFRV@FA6b{|l^Iv&<||eT{6I4| zgHI>sWkq6MR(yP3L_2WL%{DONj;eS4k2e>+lbEj+Uh2C#Uj^srd$jJ@$b1zX5^gf( zm?wKod8JiR4|dg@Q`9BDitHKNmtwQ$5OZM8bKBmhsF3$A(|)1jLV0Eu_ioR9#L|>p zHQAg?PG|t;w){@M-8;He_+(_w&uGgGs2y-HvJ&idYVhs&i5Ryd`=II<$ua2dt9Chs z;AQcjvCUz>kFT0#V#i1?VJ5c4TKmMlsmXdZ<}U)?wx60sDJkkJzuD*+RD&6~cv0ml}4_dy5*7iSH-ft;h+`{@_-d5O?A29JS(%s44-u-5Gzv5dse!q3gzJ9f7%cznM@D1Jl zl#7|@#7)9iF=rBIL`gAv>Us22zGWo0Zr-}3KlaK$jsE9Exek>e+v9u^A+<0pT5258ug)z z*IT>C^KDPwL;u|G0dFq^mw)juzhSv1{V^bZ>&wVs@}$#O7PA!Bud5Rg(xl$e)}-D- zi}!BTr7=@qyY4X3M`>MF+kWLPvnc(TIfwF)A#vx07KwQ;`Z_)E8=E4SlTQ<)Z&+w*eb0+gBBM3)%<#>oGoVK3N{V{QD}q<#VBLtKYoi ztKc@n7y8;D{QsBnDU)FZv8yV@ro=)gW;}M(x1bj@A?QK&d=h-%_PQh+;0%m_vl|)5 z9C+4>rFliq;MKiu7y3xTjVonmN-QNew%XEnx=XtBW^F?ob+oh$4}JwcJUC8irP%cG zeSazApxr!RJUi(1x+acyS5BOpUQORB(HFmkJ72Kl!V=%$Zxu5GoK5}Mi)LdlT8Ir+ zDK=Q+xnE3RolX9i`0-yH1g}e$&~)8p(GmC_l&oAjJPopS!6x}}rMtYmD;`jD_7?L_ z9&~UPsNbxy)%3H@^1+A{jH@Y*PoVfhM#v+zqYoKb)tCGBqXm(5c>b{+0Z)Nz4)Nz`$R z&Ial!RaxGHjPqUdzre(?m}kbL0?cq%Wkc261|hdJaG z9vAMO4KF3kdY%lwMY>Vbs*b@+>4%r1Skuyh_&NNOpM@^5ryT5~Z1zwg`$qKLocJQ_ zOgPV^n*wgX9bEGpt(SOHRwq1!7{4y`@z8D0T25c0;wuFI4?1#9Su@!e$-M7|U(!;4 zs;02iidpVi9#2moV3i{72>uXRNS8BG`j%zAR#MXEjwO3trwnetyW>Kc6+G+=clGV- zw()b)hv4-Hx&X3)pF!Qi>37hVrmh|RPY#|R!Q)Bil?Qvy@mKM9O(XaE_qf+zV&6H~ zcW(AwHv6uSeOJo98($Bsps^-%R!)Pino|#KYDyal>D!zc;6cWp z(J7jrbFE$T%KLKn_dTh+Z{hzP1{UHqG-XT-z9IYdF*ErFPdam{#$O)Zu_WH)y&T*` z9Wm@oIUjj{Q+4saoA+-1qyOqX=)Di#qkNaQIg5DT<@zys+Ell9gK4KNXNz}mL-`crz>}3uXKy&+aHhhfFwWwqZ>n=*Y#{S29zx2&*`ycOGn-}c;hHV!@zM1i+ z^e1E+;$4}@zv`O&s}uRZb|wD|<)zF_=!7w@KItVnZ-9?DBF}pbKH`I#Q~WGH%3PhL z@3#NcrK&3tZ+*GVgY~YkeUrwUJp1L7bfgdTYle4~^4Pw*y!69MA4dF2lYge^Gh@@3 zG!BiCG5eKAd|!=Gyj^4e(&UD(P5pmO{tYJoER%ozHTl2wvHYFNFWQTFGCU{dBC%&J z?SJ@U#AjQA{m-|?{Bm<_%Dc#n+U1Ma%uPv=@e{%tJ~(pMg8!Pk=_|-AsdH0nprxLB z$fjk10z=>NADf!>TGL8LrjeolSHF1|jI4Lot&!DYqnW3B4me<87QA+R@tU#g9-@1p z%1ZwpIojoP2rKF}_P&xK(5G28UUA@mg}Vu7Ja*v{kIOboWv_w3ko2@*5nPjlSbZd-%hF|Cd>YUSCpb@aGg??~>L#2~AQ)324-e>NEpNzdIyIRFw#HRKNT#nb~+kN#P5sXf> z?UV1jiuj^}l|fJKZ2DZ!I-LbQ6CeiS06Yl~emDD;o>ELu;3j`~FWTc>@zdJ;6(?&8 zrCYG#cx^Gd4(sq=SL*IHI#oBQ9I@3acWeqg>3-VKd^bJL5=>+)IFcNcTK*h9%+`0?U=a{XXEmo_XH{FBCs^Mh~bw32=o)MP>aNEqK!|SwjW9-^5-scUoj%3$(vp^!K;u%tnWK z4Q1dpSVfd?q0AS}{-Zpysmt1j&@HjL)3qzi8Q*-}5$51o?l(Twtv2h<)sFAw>|l*3 zmg64E$w%h5f&W6)%X;3fvglMiDfn+%R=Zm79r*oL9d+kx7YpyvhVgL-?^LmKmrA}_ z_eSk@a!MrsV`|xrH`F{*w)8Z9cEsci;uxHv7fYefyaz%=1zMB7M%3jF_PF>9D1iqEWo72Z@E>0XC2-AM}nt5%c4@;QW zmCWmX@zL)4G_Qh_=0vd7ybzCTrsm{CAG{6vCU^i>{M_VShR!Qs?%`=bm($0O)t49( z^YUSQQQG80e`ip?^!5zywCm67)Z9^j0rQh><_>?DRne>f@fW@9o9R3=dH!JY^`*P* z_OlM)Ki0G_9-jK3waB>5p8LaZuhTuXfpxe?^K0Yi+=H5T@LTuDI7H9W&b&lfbFcct zZ%6fATbBRq;atf`R93o(1_scR?^AZ2J-(Fn*N#u)y1F)Vj;Vcg?q_Ozo3BUS4-9F0 zE#>2^A8^mquL+jA=V>fj2P3jr=KYEeo7O`4{4oyjsGIRa!(z8=`x(-jre9+m%u6=u zExc>*K}Tdgs&&Zw%#K)THEFXVT@F{LRa)UX20yKk`*QP`A>pJGA7CGKMJLbMf=u*+ zncGj3hZbkQYxJDCQ$_yP;iKQz*hektQaR}KL!xyVzwA{S;!SDGnU82>x5K5rvKAMf zE-3l|d7$ro&GfrVecBu=HM|${qOXr{i?+Z;=u3Bb0KRT>@p{pCC;IvWd`nL7_%{L1 z{pv3`SAH->Qy5zzcu^*HR=lZTpZW++eAezu>K*C}=gS`TW$+&g9~eC5UT5aJIcsKB z)M0(**jd{eyLkVaD|}`TvS(vDtHhVmc?bP}m(DwLh9&$%#G?f_85$$k@k)AcMXa?Y zZBRHGyVfDj!|3aOT_8Eaj1jr^#@=>~QFuI=JX<*H^uIw*-dB=Wdi_m2p)rf*nD4n0 z<6Tb&4g_<{Eb)lN%Zk)w79f8uftOVf^pE{Z;uA#59|Z6A<+kIe`dQ{7aWB*Q$+&~D zpJl&QsBOW9cjQJH#is8{yOlKv-#kuVsZEIf7&YId-Fq%7}^()j(L0O`OxE> ztCjY;^gdb@bs8RCqCbn(A7t;3t6uijMfR49@!Y~*n#_3C2A&>c?vltnW6%vb!TrwG z`t!AuV}1QG=9jyC!2-3-`muSZ(cRG z*)xsc}{hjnMl7jFL}%wp|7RrV~&@V0s-k#DQ4?uFh*bGpE-vYmCB`(zPxM3MDn{ui5h?(^JD4A;4`qhO9%H~xvl?kX^N z_!#v6C;etM`Af6HK4ZHi`j<8SEWGEY_Hv!(dI%lg@>`4pLeFNhh?Y z>ZR-S3EN8LD{HY_v|l0J1K|Mdlw_kzjC03d3D2n>bT==^XEt@UvcBO9vOWrFvpL71 zHhFip_Tk&*8eO%I>GCxdJ$`N9=_7jFjv(!i!R~275P!4}wZ){D&V9uZy4r3!ZKUL@ z-790bMwV^&!FTBseRzU6r3twMyxV`-Pg@JJ8uzF9X`?Uq7`X*LXqgMypGaPzScSu! zkL%;nQAU?H=uftwf0hLOSuxQ;^fMXztjW+p^@0n}InZ+Jcuvz8X7?t20PpHz`6l7Q zy#GpEHiMr8XW@Q)!F98D)-zY_&`88I9HY254&hY!k_L8X;R{&c&@$mf#$Q z1bV?;dy~(`qj?j9DM@SSXHRf<@>$l~LU5&g3l<@lIHdf_8;|C`P5uky+XJtn+vGcK z%3L6?pS(-R>p4?fxQM%t;>k7Ocl|zm-mNs})W`an+OB@8PxKjo2g&oamNn+D9W%P$ z(v_DCF1xbm0TiyO7g<8-CYr?mw5gukSH^d>+0LZR-ArJ+VW5O3dpVa|aa9XiHv?*jJeS5KW~Iwl62)d*IT9omz(IG4v@mKws%j3(X~1l%_$baLnXmcFp;CnuIllsXTsTS<)IZe4>$}TSAoJd^VD|}{fmcfg_ z^aT2~M`b6&H^U!LQ2KxvI0* zXQYU17dIHM#lEPDNquBCRHY2cwT`;djIpYk25 zm}WP{o7@Y;*QSoy(sRJJ(3;JCwX1JQx9HcvGShA{@>!?yt1s9fz8m-_`jkRll;xcF zQuc((>MRQWF0DUNN547OuhKuIlr_G7C>Nv6nXHA8ndR$u=y~E~CekN*VURINt~jc>M5B`dNP;mx10(T+e1lF>7#7+;VazraVC%rP?2& zp}7xm|0%i&x+T)?1>kg4eF83!fP!%c<{A*zH|bdi|M@?0y<%p9>Z8&R#1zY3Ks!kEq_J zB*ss^^Pvgkg$4?ZS3m2|k@iNY5Lv(c8#u|EjXlVt!iV5Y(dIwm_bmVKV?WjL|Hu57 zKI9D1bJ}CARcYP9nzU}wm9KWB7r3A+vv$M(=WI&Qk@CyN`gU$to0`|iH`2Nz>(a1? z8&$}fy=c$A{{ZoSPH3prkR@yqJ z{jGe^o)e%wCqs8mgYKLI&cN3qxI=ir;HnTfxVV!wjlHKMAb(z>&rY;)mn(1t?0T&a znX@~nJ8bx8(bQwC1?=M3Gv{30@o4JX=q}E!TY- z9$Q;Z%*Kz%ac%r4{aD%5W`my!vkH8>ar-o^^T(W*i%a0Cy91g-*lt{UG1N=9|H#@Jfzx4$?kwDKux{ z3%W}Npefw_%uQDrI77cle{@|?9mK{#k52k>xh9s3_j|tUHeCoEN#N(G@_ZZX8RU0w zsx8LdsrG>5hDm>PC-jhv{y{cl*STN#Vo|`vx~O_%YPLP@;wKoly^r8^Fb30q@l2_+ zg}TS3b3VkQ{$`ay@AKTXW2pMYRGqKa`0oDK+VgXMer9_y?u9e$_GG8^Z)^`;BQwYF zs5yfS-vnCM#8bxBfO#&J9J}u7m`7vN?LNRiI z&Na~tqWSH!((rb(E(4nr@o3RR)rs$i2Em0gtIZnt^ZbvJe>fg3WG^Ph^hsMPvrncv zh^6uEO&4mBZPb2b`kPn>*l_1l$CFj3r)D$H+wrwqWYZwPTYhxywOt4tG|#0k7WG#n zx5PJr=qTpwTEDr8wywgX5FQG10u3sd=nT->y1E83i|skNZn-^P@lByixNjkwnE{M@ z>31LhzW^O`3_9j{=$M~E z$9#gGt9l{yl<)+6hs(fsCU}cDkI;$8;*iOG9bW5E`q$0ekMKk%0Po4{lWFYrIV1F1 zJu)}#L1f^`;FWB1&a$3px73{9&5ld-<^ESFR(Uk3+2h!P4yu${Si!aLArF`JZ%bPbDo8uNR<%qALgUV;G!6}XC zguhcy-|Fcb=f!8*4OabPO1oWm_M56ZVGG`3(wauStu$lz9Zg=1JUfiuiuh-9kl`l! zl)YMc+)d78-TU}9OMP*^;c>lqD1t|1xwVdO;vYxZ+e<_fkiQOoMRL`dDLuiIWTVGm_Lb;`dmTnTvNJZ; zrL!OZT3xfxPw8o0VO655Uea>ERn7gXYe&nt>c}#9U--3WKUuR+--eH&WNTTQRYU$= zJZJEHlxLXd<2)niu3N|i}QTgaM2$I0q9G|H{I z_sYk5;p$*=?MJjD{<>x6XmRe_=zi=aZTIZ+w=F+@c%YRryvLpU(kl;?Tu(nD&;nk5 z9(*rXKJp0u?H(uyzw$tyg?`CLn_^`R!Clyqca#i^hL%qRZ1lsy)Y^}zXFIa9%cmbH z`407ys{jbB(_7e*>n7S*vI~GiBD5S%!J5F=1TlJ<_o@o8@d~wN$oE) zYOu}DwNfK();jdVQac_ijcFZd%vuYqg8}HE>{s#IV)At46zHy@yS!|ws8hV@wZi+G zV=WcL+c0++)d8Ke4;>Vr(gYj91i8w~21X@?uL(y+zLHo&tPlL&b-_0nVlAp~=g{9a z>nO2)46hbm$)k2#f4x&{2fv{+wMLB$As-xtuU5*>?063Ur_j6h7}9;v`deOo&i|ao zK%TW(ojC>Do<}!_b)U!sP3M^Q>M#A@O60jIYg0~vy&oLN7Sf-!(~B>Ii?DyNz5~9; z#@uHuyWgt8f73_7Jk|mHgVqP2^KwIGJqB|<)qBfwnBaDgDi> z>u&S}v+hK9vF0=nT5~q7k|%xs1FauhYgrivuEee(eYeiUV1WxcwcGGWS-*`{C#&Sk zLUPdc!~x01zjGmRKuXbB8;{P~1oU?%qXRSz9iBPp@Z1I7T*6wH%(NZ6x+NZkSFQDu zxMP|zG&v?RhGEg+$W~@YYON?v9`jeYb4Khw}4u=1#XuI!v-}dHqh;^!TEzJGcXV1Waah$JU5n`m6S# zKp=8BtAFE;#gD4Ymb7ne7cH)~sL#N-{wI^*V`T)0xsZ+DAmDP>;NS7T;p6$~tp4=ghFB}}XUa(TZxOybJkD9-KWm=Z zpBq|#nANW*{wwp%J?16kQZI$>V~wJNg{%eL251q*2T)u9$(_*0LJo7BIX+JO0B9QG z0<^Q{CqujF-h+NtXg%wB4)VCw?5TOsF|rd##92uF1+tfk&@tk5*mR8Aq8-*pA?u@* z^)a6H@n^Kn7hh5g25VkCn%)SUtd?a~Xs8MQ7@S>0P57oIHser}@yVM!RAbUzLp3Jd zGgM>Jy+ds#y~h_{LqClNltwk%yS?-c>ncu-_Zivcmd8%U7)HTmHhDgw3Swd-U#vZdD())b}XO z?UJrk)|c=vRwP+lqCE z!B)%?-GSLzYLn zlX&yS_k(x>#B4NoK*^!l3%ZwXiOo-xT+YC!FvgE8N;tQs3O`ARfAqb;U>oxO|= zIYA7Y<+nH^A3)#s9rVJ-qI3J6e2Qbkdwm^3eX+D<@gnGb3 zKH+Dna zmwn9&ppVjAhkSl(cR;@MqoS9QhZ;S1;?5x}z+VqBXYNn)<$;Hr?aB zj2=)!`3z|NY-c1MbuX!k;sc|k_z9g|nltG~RQ)1b>*_4?a2IrL0GO|2J`|g27IQU? zdD59B+kz#`gV9d|{_WoJf!^5ZK+(kU&^0x+ztelrZQsE(#(3kHUAilG){Yyd&ccp? zalbcr=f$HRWiFj7j@6E%J!0~4r~c{9(l3)w!!qiYZjSM3Xy-3F#{DA|yKq^#Q=_gks>`uYxO-pH#~z zoxU!f+1taV6>588P3=+g%cm%G#~04AXQn}S%z^H>3%W!43`?LpK1Fx#W(Rk?u;A=E zwodo(S%WW*zKDKb#)=mudngOAry}*ViQd6vbV;lc-2wOV8DAhSxguk)9NO?3@)F5RPQb$-By89aziKX`FYyzx3@BhdK#@Fnsx-wUBBj=G8b@pb-%xmmBV*x%l+|}khY#jA6Px%qh9on3=+Go;B5}&~M9kOZV zEu6(VuX-4#@r4H7eL^@8UQ`8T8&o%K^t0AhR!zmGc{)hQupP+*di%+?;I*-d*4~v7F%VZW50&Tewp)D)40?u(zl$ zLLbL-4xYo8$6KpLcz^H{4)l}8>%Orw_T|c8x+5HX2p`k^0gL}F z@|%Gy{D{tyIsR+>XT$R{zO#Rq9Z+B6js8xfTi85GcX{esvO7_Brpn6xDc_V0d=PYw{yQZr z8XEG{HfXw~$3pATf1D;5AQzerPazxqr+4(uJoTIRM&z}7txVo!lPfvD;pLz^Bi(cP zjIE&T*+kj=e3KSpgQO#?b`Q-h`V;)M?0940a>hJk z``pra8FZ?ClkU%&$9~(Rbp`wuf-kp-SE;qeTJJ#?)5BU@qjOkg`9a4ObI;FucKh5w z7re1fwH026U){aFLiplC^?R)f^35Gd7ca{G<~wA*$qr7u$;E}zZ-}Z6@bf&u0)6!1 zGrbuFWq+gi^r4>!&y{_dc$mK9LgH__#779r8yj#BYS?_ zjh{Vt8Ph&Kcf^Ee?zr1^YX0JjoQD{v;43?~`J&0BR}6n(5A>FFfD|83G9%mP_jQIp zVEbv7okvsZCHh{{!0PpaW?qA5WH3jej6Y67ao#stan9D z5pvi{YzUpqqhIuK>tw4EKD-NBv;aLZ#fS_D7s8`i2+tFJo-e>>wF->yh*s`gEjiuv z51mr(3rz)u;$3*Iq!blaTc$&N4T?_dgFV8qtHB zf$f`YYvm`^PniHV%-hul|Fvgk@^0jL9sPjJpFbsqozfcWFmT_JXf&(P@n*SSPHKAlI=j{e#^R5x}BQ`OEj^*E`=^o6rOMR|gL zZ1qa8fl6^+(3qu9Rdv2rKHHZh=69u`wV|y=7Xu6Ne%hfwis0Xt{cywLcl9)KC3KOP zr)eYlah$IfZ5!X6<*{Vh#5qOskovg2H>6w9dHg+9;E=4dy z&*WHad2O@ml&u9iXHnU-f!~Rh%2|Y-F!!4;{dOQ=xk{YV<(KOjTQ_(+JVT!iGpAK| ze(GcF(aDk{v3I9&cQkfO>tTyNIYOTc7A*Bg7Lr3*!JIeZ{HJ z{#7SJhtQ$gu;P5^sS|zsp42$0C#ULMNV3ADcw_Q0d^wC$y?WF5Z{xzNd0}*1ZtQu6rqzJNUxrKMidgoo4piOT+>>6FQ2`R1)LT8itqr1$e*N z@c*rK+fJ3b;B{4xoO{~C+TYindyM0#@Ezr~e`QOORDLUUEKX7U!Hee3s&i|x*X#>> zO`t;pETQ`+L-$XE?zeTBKUFPW<^Q&H}%}>n3T<#A(&4pmDdZ43vY}k)!UxD2B#lG))GPd~`XR@`?AE;cm4!RPU z2sZEl-lB~b@T`%6p`VnmHJH)iTc}v>rrqnD%_F{LRYtf^^ueS*T233bnT^!J`G720 zbA{iocw-4PO>GkV?S8?t>ZQ=H`33^j{8_uGw{htUp|@Ep&(QZ@tKa;0@&9)`CoO$G z^dA5JNn=c`C5?;qrq3H^w4s!Haq9E#~hJZ~Lmv8Jf>~u{lRc%ZMp$XMf-ZozwV1j>6w+#6IkM%iu#U z+khQ1?P}dzyFTpmQES8+!e7_ft81iiNH_(bUI&j8xL;`f;Syt4jwc#gLd-){;!FO6wO#4|#l zIXEpYNhgqzJcTU^cgT|Ml1FI2!$+8) zF?g4ukKT&!IC!6|MdJ$rKIq?$;HS;8u+h1*>0ZYx;yDpxIUqk1xTuD&==edrG4&=x zFPD_8RXwII=y3EfuGDkl@U&>la$+!pFM|&219USsh0j}M8{1>U^f?cFv7bBp@T!5( zzOuE%Z@)jQKXZ8id(hAm>L=^tacmWifHU@Ses}471m@B&_mD?=W!SQYa#g3!1@dLX z8ytSUEu#Rvv%(dpL;1w!TFL+76CVV=j6N`YTx@IwgKNjW`)Z#BU&&ISxjTV*B{-^? zz9x^f0p1^Op*_{Xz4w-?v)Gw5<+ZDByN|Uq6Mb|B9h@QOL;0L@Ug4>wM?=Kn2<57b z`nuvcx_tM##*?R*v8|c?YG}XR$Mg)eujg&DIY$>gt3qQ z+qdywB)>)SStR;+=Z?qjP`$MKYt9n+7WZT(&RsqES>mYz%Xigoyea1``uPWYJU5?U zJd*1=_TG32X%l8>C(#N1I! zcj~V6Egt{G>#dxO3SS<6)B545KfeikHkIMNnT!7>d_NrdqIv&`FMr2@vR`>m`SKs` zugrl*e#4F@%f93N88VI^$V3Xd8?S5jdjnMO2e`XHpgVtL7 z-sC-IogF^>Ez=HuCgG6==(F+5N}sV2aW>Icx9eoGyLF{iK_C1(A1*Hwy`wgO=?H9d z@p@f9naoJq2crkPmy_J(Iex^2` zvu(j%u$&r8*ZFUC2G8^VjnFUQ4ZL?I>MDglbVTq#Z}u+xe6#kTuWS*g{I2f(^fg8O z8M6~QD;1i{2Or;}AI;*;?d-n(71o6KW&Kx}S%Rk$xw?dSap)pQp%) zUj{G6PWk)$>1Qmlr?G<{a|{~$z{FMX%)hqS@Ts;Buz!JzJ?7$FjJd$>yJPA|-{EZ- z`v>suTEP*1eq+2bYe;b-Ga~G3zjXorL*rLHR?EZ0u;%?&Uv*o?Mm!U|gD=GQznX5X z5q-zmx`*-$89#ozodfa3$jYf_b1d|8SB~>eyB~h(t`9Q*PpGe~U*>+yLEtzyy_?vH zPM<%0hwy>p#~XK+ntLJW)r0KE0wx~ntKVVO{+CS4=kE-cx-_1ifRVZV@9qKf{1tb# zSL9DdYcSrJIT<*0drz1+JilRI8h+2iEoLmg)!r}frSGRg>+AQV96)ET9e&xCRmVc` zT2k&n?zCrfZ2oiFU#9;o4QzudfaPF(QPI1G{t@r2X-v;mn2uR*{u?{RF-y#ULo1Fk zYl(QJW zv$wU@csINZ-jDG?7*4*3-@hON~4tQk~Yi)0)tUzRN7^N{ruU?+<*1_&{!C z&%S5QKo~EqnNg~XdUiuMb=rN)E;jYCFIWT9!DspW41VQV3=V>?S0Fpm+!y^%9-)pr z@(WkzYTxKwSN?1AZr7e-Y|O>zX&S@kSmhn9=hIpfZC{Kpx^BDOwy#+8xwE4(L$U|P z(X!LJ`Tfrr$DhnN9*PMkO*b$xw&N)kiT;@LNMocg`Rbqg1+R6r`nEH6vv{!dsmSPt zZ;stOjy|;{>Uj-&DRi+iFU#Ju9-0*y;wAKS9RE>YSI7_@hSzfv-Us|~_yE%TkY4Bv z=;N#L2z6gylr#}s(hUu}MtdZIOSa*s;wEqjHmeEytA8aPU*!8%OXR=Ux^tOrijz|=K*PPJ zJ2U*-uQTQu;q|n0ueBO~|1IJB?J|Oe+JZN{2YW5EU$241hChRay#@-+8n^b88_p%$L^eH#(w2}%w3-i3;$i3k57XIx`6m=486tt&td-7us0l9Bis@9 zf#V-Xuiz0 z+4hm6iSPA`N$px=Rc}n{;jVwE>iJ1Mtk*S4<9(0D2wcAe&hZG=_$N%&eDPm;tkAW{ z4c7u|;w6)o$-9N0Y}bq*TVuI91t@P~S@OSv-wouwWpJ0#jSBKUiC+W1=pg!7e4978 z%jjMC2GPrsyplG2q~ASA{BG*cAtvH*%LCTu`Su0U@8N$F|L^5nUcA}0&$~2d)QR)` z@JuH)mU0e2^T~$)_FzVBN-)zY-|lNB&P@$)#`o8spVZTJz8@bFHZ1?ca5VM}liKka zj^1$KZg00Q&HJ#=1>avf;)+QbHgXlCzwbD}obk;&Rp*1^cXHOgjlG%pEsgPp;)8;@ zcXf^>yCCgn>1RquamHa_y25$@UdNi2R_m`g-zEr;aW4pvuO+cI2B2@JstvQg@v{M~ zjV`!giGsNS*+Mwj$Avep%=#!;npUB6!Nf6s1spI04j2Rn{2Uzc zGjPDm;DDFF0WTs~Jp~>(2_85B9uN-Yd|1`)xFEc@qSq0Zoge1oVOGa*Roh>(Dult4H;g3r1 zp7SL&9=r}7Kj#A9BtJkF;Jr)VZj|npzqUQ{cGiKG8qbf=<+&8}IlbIZNsM;E5BJ2$EW$Ylf~GV4)(^KrT_E=$F}tq zdzH8JzdMryr`fx%M42b7X|xf*rnLe3Ert)SB-U$vX|KwRe>UCDHui+f2e|Yp_WvR9=uz9ODvZ;aY!^Y8NXuU;F?q8|4oyelB$)yj39OT;zPu+!{*%rp$uXoD93$|Qm zYD+#s|9K9068)<5Ir>IjyY&ou(QQXo7IZV8(e&(zt2}GqMFoh7QN}$n{K$DLB);JY zFG@07-6!Lg^FegU*K^kp{;S|={GW1`{deSrGY#IeoYTddj5iQp6`aR89TN@=7P;_u zo+bLN1-#ZuKkv}pm$V5w3rSm}cl{^L4S!=FY3u4whxW;Lf6vpbQ+)2iLoU!g2mDwd z`KbPbyDn4i1-spxH^twiF5CXNA?FDDUUiSWKg#>M$T#SN@T2Zni!zUDFOaqdygZU- zZ1%waedy#z$Sa$FS0cacbcA1(R**=WZ0dl{B4+5L5!r6so1dWBTc()zres5h2~J%@ zQzr_}@T1>0>tT_1kKlUpFuXS4YOk3j)=UZVdXI~JzCm@E{eN%9c8zh)A=+q4IyUFW z(7`%q7Ns8&oL3%ZY*%2JjtnKVkF+(wyiV!lTPZk`whlYxeGgv+o&0t3A(}h#FqsV&Z2o z-`fUjjek+*@llmAW7y6ZpabkNjLNXbkZ*X%A0I|nCtz1d^fATv!GL{!Lj$~pqh1PF-vbIy@*+=iHFEhXe>FqX8HN9K9GmcZF zjb;B0<5%iL(BUfExT8IWAMJYlde_Pi3i{4Ia9L&gPLuzu;GVnSflCgKGG=$GzM*%s zKzjtfV}+iQqX*rtGVol&hZORgMFmH(RqvkBWsU8z0yntb)my;rPWWK1;Ex?G!5>pk zL$c!NTEV9sEn3s7PKWjcpGMAlI@E(Nnq**qdue~F1I;~0S=eiJ{RQ#|W7A^lhmYX$ zfu}3*_0uuJb06^ByGwmk8vK=sI(HNYW@~%xFYUB33-mp<8h^~-W!LK6;yM2y_Vqx# zJmz%Z7b`x|Ut3=ufyQpgJ*F5h^sQbp7&r14WHVv<(4=wT3t$hwo_Ir23Td~~PI9cR zcE8PAK(CB3#qJ9Xl*ck1U2V0CRj0jIMr{E$XPKvi>Ju>VsZH@0n43!CkXB-Q^;lVp zRiSaAU*i!DAIJ^6)MtESgyRkHmROtA@r3G~m}>mnR3gK@7&{*uERR_gkDSH_WFKYk zr>uYAfGJyqPGG|r$EySWTH?n0N?Q%zt(ZP!@~fD*D(`5#!LgjYvg1T2V(Rbo&9v2! zJ#*r!+K;xk)E>0QSp0-3XZlkN&Nupool0YG89R0G&AS@o4EFlguCi~=xZfI!ACpF4 zwOHeYCie-Kd5mtCa|%8v9|e}|eP-S4d+WX6X7HkXQE*?GvECYM!!S?tFC7fdDF2dj zG~n4st^DUsMl1!<)eU~mB=)eKr?7lpBy99s;e!RQPpyXTsq}u|+G~FweU5$Q>iVy& z6VkL#1rNSO;|;|A+ixXzeZSWD@wDq6y2%dvcINQ z@bcsG*JL>|TadpSnSj=YUEUEB%}czoJ=`_!Hg|09!liL|IoyX!2ePK=o(-LORQK%Q zjY$={YeNI!Plh{s5w=Xalj|OS?fqMRgA#Y}tV;14v5UFos^8dWAL1bC{SvbA9>psjcss5xain%OoC} ziDRfdcaleGVV@t_Hs#X`GN;=%JSKg{HF;+x@>ZL?btdnWYx34ylQ(4Y&dOw5nYZ}R zAxJM6>1XE1xYYh6lW!*adA>3$>wma@-X%O1mF#4aHx>GR)cdTn#2y~kp#Q`TAoy$T zkx4#q?_&I>Gf%GPQ;S{SP4k~VSTLNqAjft3V4~V+ZCDb6bOC8KufL8>Q;l_t=SR*j@jaw; z&q2?E^gd4?HhSmNcX|4-Bgzy{Bx_fWOZ*R>9`QSPdI#5oGgk+r41(C;Sbr*(t7i-tL` zs<~6#2i`HU3V3gcN4;*n51uvOr|^BkHQ$$UA2_LZwV`&eT}PkV)`NlS!QICGF-%$U z8mQa6bjkG@i^pLME272&~H&pV-xFz_lCITT&H*F zo1RyHmp<8j+>^$8J={SZn%|>zFm4 zV%Gd7=Pd1s>7TPEjqH&tc@z7?m67JfT+;q2|X;JUfDS`l!*ix|T(#X!OT`MVRdYmcH& zQOOzC!Wn!Ca>L30HQ;^u{E|*%BQl6R;$i;h_-@G1IDa$;#&+wc*!a8x4`m1*${;+H zpTk4>8Mypq_$V*IM|lw*{V8;FPNJK00^OYB=;pkDF3&OabDo!9IDC^uJ=Nl^l!q-x zq}=;#yumBJ7iWFeC7w}eCdby^(!J8D9kJ2ISH=6X&z2n(`bqdcM@LFNmYVY4MrU!j zqeXt?;Atc|K16=qv$d{tKzYo$xF~4?Z6b$AY7|UwToS+*$K8)U)&V2W=WH$S&zy!I zQ>%4qrYq30Q+f-eWxZ#0#2fZURH*YaF?=Uy-!4tWJ9k&BJZZ~>!+4aB> zy}45K=Em0pLwF*S;fYLxw>+mF7$V1LjE{C~k&PL=1E=CxV?*@XQqi%<6*Ydl|C#M0 zV@kaDs2?L^ABH5uC&NlY~j(t#J08r zyV|APGo-&MeuVTJ6Zch5^2m6x+i3aEjoIqan87iY)=9h}r%bC1f% ze|zNCj0=%qMm(609?PhJ!}GVPd7iat#4j`VjUrE*?pMCAdmh!f?fWuxe*k+J*;#qf z50iZmy!w(4`R^I=cdEYfo@uY^fW?3PocxmcrGm3;AFI(~=sNFN(|^`pL~D<-Bi~JW z6-p4*ned#7*J8WqiZ>J*`Txd_TjsI$AIHB|0rck~#&((KKHj;nje1-ABHmD7ctXf9 za}4~f%!{%oohcsMwm@X)nS$YIhpk(5j-s1;e(56?X@$c}w_A7ef9}vTMZ-LA&wz&x zFAyE6B6Kn;9V7X{{YMjZ1txw_i!Yg2&cy#EUVO=GO7Hx~vKX*xnfM#IkbjPymNQOiomN>4zR671 zsLSNB>!0dg=}Zq6pdW!e-OitFbZG7WS*Cqxe$_WC@x4>!ksA#`LHbe~CbDmgo zs;7mxU--JVd2-edpyetx=OZ@U4_ii$!DHV=;9;M@=O=s_?wm)_XIwvWUh(V!Pwv4d z;t}05HpgxaYu|A1wgfl*w*O=EV!Iz3-?x`OGoZ}qq;UBC|?jYfxCIwEHLl-2%ilzs;JE-_aJkzGW9mm9ot zlRJ={8t$_q;a+R5Ip-2FN=UoioO5l?CcfQ?UYY0~jv|Shb9liy7GRZj$IrM7rjP#2)SyP##`tUAei#}KV zdgb8gVf82ch;^rI?G@7qSPUzkis|Fi#5q#TBl_CeQreaN2r;eUjllbFp12+!p zbZ^Nr*7HZ$f#yDZKd^$=$(a0_L*O%ywLQf8_+l`Va|Cz@7HV5zKN;&Y&qPfkS11A!7O!-kWbbEG+ z|B9Wcm7M3gkfIor*`7z8Ie#{PJ3eLpzE?JwzQcJZYva%6TRM~@a~bX!B0&O{%Y9c*S#-Z%!_?Dew{s$oAn_6Anu25OvE0-N63{p^jVwi zIP}xkTesBpTGh%|_Xzi(L|)Qr>fkR)-&=OeYCq#}pilB8qrU}A41SMh-^6)R&0J2^ z92?zZd!1Eko$)UFbFI5--Xr$9OObD`yLrE>`-9JkCUUy7*%K{Ve^wDRsdzMZ!#96d zYvJMh$-`c;*Qe##(Q?}nttn#u7=GHOmMOdJzWDN)BY2!e-#D1Z{G>4-kERxT+S1U) z_5TCw7~RX{cQXcj@=vp7=zkH1m^8hYyRtr_ugj|nYTruadsON0{-x*lMg8ZyY%?M% zxBXW&yUe`xT4CmCy5KhwD~WUcDEoHCh^{=}X9<4jaUI>bt7aVKoWQpdc;9mE{E!wL zfj8eK$xeJz%WcQh4&TCwIhuaW9L->kW{s3l8{pQ7YDe(k+zq44zeli&H>BWGFl1~X z7|%@J53;9ZC;B+=v*EXH&>rKxhxdBkn|Z%|gia=H4)1eqIA_75%#iM}9YdlqJzs4k z{Fu&_#UUN2G=S06_2O+&scuvKOCVRe~KS} z%TJ|0iy!!Jrx_hbcR1PO4y3rWmdNtAXiNIl4G!b_Eb42GE8fbBm(dFlThH#`!Yi0F!9S#rc9MpT@ zY2>Tr$XEAgRvOu_dDU07S z!|O}4+9>DiZuy+#xIci0i|i*A-IMQ_GG@+7(vIMdVGVI}%w5RH6&4q~r9Sh&(mb_) z7Z)6Yu9+wNu6}YJMQ3~i8}g{TdB!?oskQiKus6Z)*R5nM(HCn1VUv$~B9k)Cj@T*a z3^=`;`$J=v>}qtUZv=-<5Pkv&G?;sOG|$Mm!{|zgt}y&Z`R0?HE1I`YgZGJ)$TrU-t-m?`0mq7w!9jUha+s=-%LCSA7n45F00&^A77me&^bl z^Hk=}g%9+N$mNXef22SEslJ2<;Ok<08cliEz+ZHydBNR4}47Pc4(ny;cBTXnORnz=7Uv%0aRzt7+n)7FR|7Ay8*v_BlYF6r*_ zyb!)=t-PuN*SbVG_#|0z$~jc7uF0BLH{ZJZTAGivdq!wJzI{nFAM4_V*?m?;Z{IEG zF<4IZL%v_|Vc$-?BY)OhU+%SaWR37*!H4MT%O0VL^N!f(qd!3B+W1riPp(xx8n5`@ zi9Ub%nm$KNpPR<8Z`9{(gGc`zeZEC~4(4WJTU_1uf1%&fv(`Q5)B3&Xntm79{Vu-4 z?zeCJe+<^a{~NIWvp(xQkuQ39Hu9^p1s>X-xo@Y%y8a;$IX$Dq;K%a$A@(HxJopYD zq7vSO?Z2tWS|EQ)wm-$RF!#1Mg`ZshvL^PvC%!oIE;H6%t08e#Ms-%!ZM5#?EWL*_ zwCT!Oxzt(L4SoxL*Jo{eggeDoRDR4sct1t@&;A^UFLaxDr}8VgwG-Jc_+oJH=;4vR zUFlb0(k|04)--cu^oitW>2zJma2#%2{ob^g!ENA3m5mo=I=+aFt@&S4{IdS5->X=Q zBjvU|5GbI%C~}hp>XZK4`Aa5VoB#Bb67@&+W*bz8epmW{EjKixoBN;b_ZS;&>333J z1+Xs!_T%FVize1@bQ#_iXX$M5uk1UgSuXB;*l> zUl}DWU-TsUES7jdlCiozP+Yym+f@b}c@F-S8~)YD?aRRnvhfBU=nMGlxlrtbPw^k7 zwTFCmX8jZ^3~z}s<~3a%V@kC>#!-p=@3!fv#iMT0T)^WqJT7pikwtjFc~!>bZS(oP zf8$FU!DEUW0}hsLTO)Hb4_%O_`8|{Qbk2eQ*0cD{%x7{A2uH`8-TOH!maHT zX(#kf`dZf;o+9lBv*xrn)OU@Q{lHkK2FtxIT0ffmKbPl)u+7bfPR9n3y~Tg%WbX8f z^NdVRWkg@E>P@Ra#y3QLM#gs|dSiw+Yy4o~reAI9 z8@&2=(l@noA4@a;&6)Tg^p}NhxqRGPqC4UXJ2!x z{=$u)kF68$bYn&7JYaiIdQCOsuzn>!wcBEUQ;gk%?lV7B`&)ZA7D!HP<|R6cbJdE& zFF8Y=T^qMQ_L$0)-5he< zBlg$i=l=VE!xL_E5I5HNLA3oNJz(^VgOl2-^B5jHfsvHj1KgNt7-Ed&1L5L zoG|#}Am4t^opCYaem__ku#TU7v&4qa=&CR@CNMAHj^y9kTl22cx5fTCg+58&`5$?< z;@e~`|Nkd8ipjxJPj~vTvt;vyK#Oa#=V)(DWi@%6@Sg%at9k#b-dP*weQQnWI?bbz z`ImZPVf38Urp_DgZ$914@avejDONhg`+48|%@;~9GROC84WK(y0Nwry-=5`uQ9w-8 ze!&4>&liD##ytV=y>@Wbpo{kNlu zvoPifZ-sCE@uq=*H{hFJHL>=7@xdPMkI`T2hT}B@Ve~a>(ILM@<4E6S-|cn`gr?f- zZ_G{JvA+4dm*`1-S^O5f{w@EJO)b?Ki9Ay^#`waN?RsZ?L0~sYM1J@d8+&Wt59T{AK*!F1<$=+|5AEWt&v$(I z(AwB3!CHDv;k6DIx-iLVQ|nM`P4El z&l5ipKBjvnc$hUAkiLpyh|DRSk@Y9;9P(Ea28VwLKEDCo+t4&yZe`6Wa)r0f4O}#L zg~hq=%EyN20epUB6Q?En^xl%;-f*CZ^&0K^?vgNVrXc%B!WWC$w9jS6fo)PLvZwLL zo+coBnq2>H_SMGO-H=zPxqY!Iz?qze?uhFNy&D=zcbLVQ6V)%)(s6yi=FCpezG({# zU2<>HM6*^(#vJqZ=UBbrO6i1o(r*{P&x&@mgt7#8?n2O6#0p;uPkFKTU3i|lA1UoM zyL|C6!Ds6ofmZmJKKR&S?rJ&c@q+vCsir#Qi+Hh@7;>hL<>FoPzQClHlswk;-R4?T z$DPeL>j}(qHE*nqn>1fKyTw1juRyhEz@3W&aptSeKCe>_;`?DRvXmGfjd>2yALg8t zKhB_a0bg^!lAl%aiN&ic55%0L{{|lkEoR?9`;zCk`j&pHhx<@+(I0;8S*Nj;$GAuJ zSwDn#Al}Nv3XN? zT`<=ndrl|)dT4V;U{LJ|=j*(HZXFWtzhWCB+2@FTjJX?z+Bf!=;eXt5VQX*gE|p_! z*V%neg8p$Dd@WgTWHR+NB_+;GXes4wa=uKS13DKehaU%r#a(lwU0!)s3gIBA_|)nIP2c#-n=h403sb@Z(W+tsxyr}FIC2I!HzGhh zuqyaJk7q+`z+o*5I9xn^JPUXRc=Fv*$+Mbgm}i71`5nsN!n2j<8lLNT#&~w|?B=kJ>-{Ei46}e_rs@*#JI|>TLMK zul|4X-aS0(>fZPN&M-+PlSwiR2_zwxNdh*AmsBR)%w@tw2HCchYZ5~nMQW7VqJmW) zY$qW=xCp@n1a>IVT`casX`xC*f6ykoqEU}~2g21OKcTJLvHU#6vulg3%J21F^PP~g z+jD;BdG>$5d7gRJciq?etk3$a+vk$MTwx7QSpVLC*vR&sr+xSIA2zzrf7O3Dhx^+h zLmA`Wm@~@luZ0`R-N-9AS4^l2>=oU9t8@P)@M=c!6!-O#4}s5rZ|pv1_)mogq9@|F zftQQBKX+UIo{^VYx(cP&Ek-MAb6Hms&xFr7tnj-rTV0eFUV5ft=(Mwp(-?OMT^E6+ z?^a~DUmvhIXF4$x0enY36&v^58{wDs&3dffJ+u^=vv>UyW))@MlC|iPj59J%#edulN$R{bU&(LkN&ZIe@M|G0XGq%Mi@xD~A zbDFC!{O?IxAB-G8bYc4Y;MYwWWBT_m$on7zf2-~{YQ4|;ayP8~``MHk;0z);OA9{s zp-WFxX5$v(j^W*4Xmp`>CcN)H^vO;^=6;y>r@joe$@`)&Yt20cVxYyy<;*P?-3%3aOK3w2i z^`Q5#FBkVQ@6V0f3HuyT!ZTxI56PL;u~zz6pKmwc&ObVco)s!iH}6ost{M zmL`U@#)YppvTifWZs&~y88Y^q;U{Y+&7O&Ei$eAv!Pk&4+X&rCWnXo+@DB=SoCAdc zJ8leepYUV1b$^NXjieuNFSSm1i$1)^j9WX>@N5b8jNfDZ+|j8`vi}EP-d^U*C5;1b z7AH^aN&Z*P3`Lw50<6XG+Cb+I$8?v2=S4abVaIj{v@S4+PmFiX>}SB!-nWhfTGhw) zu7E3QrTyR0g-K7U4Vxo3y=wYeo*rErx#`EcHz$mDWZ@LNF?Gpi|APJ=@u`)nj8J*f z8MD7}3^`-UjFh^f4j1&)xv@ByCiyZq@}L3mvn+6ey$^3m;Fvr3TFRr$Ew^YAcPxg# zLu)_hZ~B=3HJw3__c)?7ffv%)LpaL7tMFUpp%S$*_>vXYyo@e#OoGmyH|_pXdbF@3 z&7pP#$0j$LIr9PcNhQf=;K4S!y301YLcuYfnzA%x|2~2?Iucuj|Ivlc3e6w;do+R-$NbLKq-%gyOpQ9y!wQSZ!E%PL&0GO(HG8V!d1(&zXGnSQ z=3kq^zAC|-v9&h}7F(r(-_z#rUAZFusNf#gnc#IEU+{y3(wVvB>1?Ep>2A{}m*r|4 zl@us0QCqC{i)*?mhyB;Ml=JL|hc)0DI25P(+8UNwYLj#ps{Uy0uqD#LVCf;GE%9|{X>Ucf-lf1P^a56(-x&D^-dNfHW-<0o_=OWTcCm*7-&oi7&Umw; z!r<4!A?UD6Xf2)(tzun?E*!$Wc}k6$11%mGaoY*Ii@U9fr>nAj*agf@u61SeK6hzD zb|{jv9(%I}PlvLvP1r9yoAFLX1-k6^r_{IVtn|3(cMQ)1kE#xP4SWrqAD?I)ytlui zVn%;Oq36-P*jBE<)@Q{`orBp^F2Rp(==^8QTrDX0ZcA5D2zp*Eostt)b1#$2KZ(6V zr{7ulF!XM%-&OW_LMwRtDc^|`*1@Ax#Qrh~{c*bQ=N_-@G2-hBrYDc~BtTQ0ydTf- zw)@?M=qe-My0XVpYV3Tvw=wUSv3#Q6`6tEiuDpGpK|eeF9`=*RxVuyvyoK$Rj|KBSCwejoaTGniLCZ^ZAX4O=uH8X`uA zw~uo57UITV($L91iv26!*YkY?|C@q8WX_(KU32*MeuR$iA6hQMduGn@*o|V}L3z-E zk@pEVroLJ+a%z7?ws1GNwhEnK71;)NYDbPC{>#MOsyM=L75<0IlhBQQ(>~_W^y2kz zMQ33ja6;#GKVQG{rq!`>b6$?&+)Yc0$C+~$ItCL%(FFygRUUcEV&&(ke)%)ac-H28 zEF9)M5Wuy0Go9Z;cQv|(lKZr;is2SAYjugUr=F=8sW9}fysdnsTiy6W*3ld*pZ6B& zt2TDy;K3X%cxykkHu8WuoACw^w-;yT0C^{%dumwv`fK=HHV)!**%ae*Va?sPe)*=C zv`>MzBEnlx^WZZxVJ$iLfQJLhVR)frIhP6~H+@wu-tS{oVdW_+YW z*h*gN37>SkTaptz?&@5tL~kn9!>sFd##cFn-B3N;y6LZ$1&wbQo9ZJCYn;i4y^eu= zqvCnUe;xJB9Ndn(>XXi_joh%BIUY8BVGp(1IjW%nd*)BNZkS)((arr9HbcNP?wyb7EQ`H{%QKEvWX=K({i~DTkL+zZBYq&!M7=*FSI(Z*_0x?%Z-M8d z>ep>usJyXUPqC&wC_LBrHCQ3x;Q zUWR zv|?!Zv!uP0`H$ci?-#Q7WZj^4VlnFYU_~uABA6oQXY6tTB6|1w(!mTO%y?A4!q>Jnm4m!1ZM6_5QFq-|UZ_Z~8}E zJ>MLMw+MXvKRe&V&NYwO=bR?{T(izT=WKz_z$chkq49_=%&2Bh&H4l%9OsJZ^;Svs zTPB;z^PmSmvdPi!Y^+8@k@KGmi;bg#o4wP-i;SDp*t8+&}?hez6@JC0Vtqpdq&J(bH@0{ml68U04G z69Eiv;Y{%_=(GKhezwkhSM`fmJ3sS2zB#*n8vYaQ9ai=Ca-k#H$Zu5vx0lgP;fgNE z2FD%0r;e|Y_8F6=-sxU#VP6A2OziAPb(V9f|@{GIM%Up@!1#c6)-*^uoXZS+aWy0%uU$T_v z^lsYu0_{BPmwqP);ZAJx$d6uI?kYL=HXOFbQ)X!pJu1YzD96J;t>FOeKS28r(EbCo z{{ZbjK>MGg{Re3O0os3n_CIG`$5=cDK2B7fwCQgAwJA3{YfZcLCR39zi8>;Jzs?UD zbB%ZS->p#Vb9c=?y&4;p3o=AQ)i)d8QzTQy8MdNO=Re*6mMwj)V&>8w_J@nSk11}} z(TcByKY@<$cFFk96`v?J&@$5QX0I^l%>&+MV?$4SH+a2<>8*qJy^+l?1m3)Txz*LY zwraDTxBWPFim-8K$xe}A&$m;yiol;TzFo|hR@z;fvc{7LJ=-BVK;5;;@DcL%zfkAv z(^crJs2TFL7>&rkQDM=#^d2*R&r6?3z^^+1$~@=&%pcjP;RaFIY0VRbytBN{<`E6 z){RN3kGUU_++eYb{52zLCuHSY(_Xep(0}kX>Swb$4K*`o`QxC!Awt`+De&?*Af& z&g9trUI}eUrSGz_jDb0L74&wuAaN^ibxvq0Gzz_EpJ5}7cRjv)`1X@l=fW@e&QKn{ zkCRsEUEbgM6Um=R+;F8!z1QEWG^yY5N3=Fa7bKiZUE<$<5*mTOJ~iFns(F~2;*T6F z$hw@G;NO01c=qLEnK?#AM6@IMO;3B;_nG_XI7)lO?@U|4+@DwVHgA84jX$;~HtuC! zp=&P&x8WZH!>nnH6S5A}mvc7wDf^^PSwm}lcahE>1K&*G(o7re`Q+MX=F`!NLi*#< zoMKI%$-LShn^OtL%$$0iIrXA75qS>w+sLLjYp=~2hcii5OZ-0!{-2-xo^UeOzqGSvj0$oN zny_neejPXuyyJFMSzBW`Uz4AwF$CugUZCIRdi3D$MPJp>t2jU6c^l_Nr@(pVYT>-_ zTQC`n^NqkahV$v%?Iky&6T;YW4bz8UHfz?xk?cpTl0p6z__H`ufakgUY#a{oZR`@Q zW^XC{jlVTqgQvgJdm!y?<^3dP|9XMr4$0mNr-ZxF1rDwMwW&$MSu298{b5~duHVT! z;1c|a#E1KLXI<{!mVH^W_MB@CA5-$$7=GzaB933JzmMUU+E0Go`>aNNYraJn4*v*0 zGDt2ZB}ky7r=w&Nc#VFkIF!l77LRvBR-)nWTp?`*-C{oMPwGvf6BTHO?pD@RR1*RHJ76ca(ntd7S8_qlznOFv*w^}NEvKSqA+ZFsmA`j@13`F5hK|M4(;(N(Jh zNKJFc@u#bI9HJTC&jy{_zliZ(Lm!?5{@$-=gQu(!lv$+{|NQh#M?sr zF16>$$izFi4y{hMyWYdnmFr%`$^ z7^hk0&UlY2tUZx=le%*V@JP1v4Qq|%Nj^y)P@QTIP{cWybKiY^#o z?$ESXFm>PQc%SkvZ+RS<`<#?Rg}l{4?+XjL)8ww@!Lro!XxY-V^JQZk4TQfD%t`7~ zUwJPW-+H*{{?JP6yg9Q!3_pl{k3!gQ%$fah_&=cK+=132Bllp+PUPWvdr6+`$?-f6 zKgh2VYsPn2KVJ3X?&*E|^P|xPndfBxA@;uSENVMk8U<%KYZkc!{oD^GF%EZHtAp=& zko_Gxi7_?fAy|cONIJ8+xv)XH=ALXR{KvxOX%4+D6uy||C|l~>UbfMBsBE9LKj=&9 zL%ypV{bwJGuH#-XbM?-GW9Ix+<69>_8sq}s*L+*sQnFX~G;7z4wKz|IC&n8x%r3(k0!zIXsy{~a<>H1!t>2pEe zS7YOle~kO{658fspLP&F6T<_=z6;#}?2+!w=l#oDvc`GC*MNz|yja7Ua~8R;v(V#d zk(1@-esWbu`98HfeSa@l0RZk3i5tP5Y{rQzx!+Jb`hGX^3VLQAqb-fA&lj1uZ-wLEFa5Kg zFFDgzcNdzsFgyG_`zPLS^M)qRaC*>+Bu;FUf8tJY7M&6%x!)l=17=p{S1 zx!Kk=_4ULAgCCKKw*?>ey+?r2_i;Z07QJSFSeFXEjx0o8tKs#v-Ld(`nG}DQXtgY}C)uo@Ot(Eb%t`1@M$8Ik=-5G!QKh9MQReMsN^K{1a*5%LFKFGWo-I+6! zG}>pEQwFlG1uG0J=r8$)5~n-HQ5t9BxO1o&x@P4M3_S=0dHX9&==F|^>2_yM+%|!` zQqDac*v@!ceO=#Kbq?KM?%=!VhHu+x@T_RL?6f3?2Ka>A12weo>+nE6K|P!?@)lCp z67>mpJ}x#t3#Q}3t5i^it9FjY^|0PUd-BXX>)Obq-;rOpjzh*AIR57 zY*m{G|B+eyk;PX$?}FH%GBy`O;4t^DMmFBaA;n;n1+J>R>>E|S?&cH@uiK;OVSmHa zS&;Qd_E*xU<*1M>VJi|7wKiIBmHQ#*VILSDff`_fCtsF z_U3~t1?SXO#$qDxRN1j_=U4QG6~Mj+;TiDv`R{$FA~SIK->%^1an^I)`)b8#Ha%I; zL2L>0mV)h-dwVN*$FCSlnj7!^WyP0CQ-(VUf5qb0E2XOL(t(oq&1k%x@Bl_hJ!W2 ziQR{((6$HItIS1KLhoc&+CA7qe=!ght<+tD-Vfk|G4@a32RTEYTI|Xdj-P|R&^c94 zNLLHCZ0_RRfPFmfqo^N$HT+{y&gq|u4-@-H+_^h;u5AwOpW0vf0PBkB-vAxC(|^IN zX9fP#o7*ZsVSZ(SA7!cso#)Ss7P*(i^`s&%bf>>b`*Yj}%{i?g|D4jQy}Gx28W{uW z<~zPJ13ea!Z>)S@c7IYB88%DkV<`|3SE ze$3eKyGL)W_8SN`fuLKt&IQ7wqPt0(E88)`-O8nW`}zJd<9Z+NB>Yas$jAHcX#T?I zJpMOE7YzA)F>kn2PgaTtQgp_NeV;_n3uaGwJ7YP|jAM(hV8ee@-mG!7-+-p1w^;Kw zMJh+9WO;Rj(4O8*K2pZ(hvXmkm4KQs7S;I6l-H=#SPRb&&EN1D-`vG($R zB|1MxdW&V3P;U(L_?{b=iD%6jgUv#{hh;21$C$IX5?vx)4dYkKeRQh!yYMs2d@@+n z_SfjshUrBO;L;zkA2*XXpDfmufpV~@746>Zu*}c!)xap!HYeC)>^-Cn6hex$Kcc~&dM(GR4hJPG5y}xfgkYX zpLg$D(9fgLaOMI3NsHfvb|0;n!M<3rZ- zJD8rv#yjf{jV1bW3Jp%>XPNQ$p6AYF%^0^g>RS!(Gxy^T=i26g?tp97wiLcX_zvc~ zn{+$jXZ}EbY)4#zE^{7A8?AjaxD(@zUcj9tXG0TbVRMUnI_>+>gSAI=Diu9@o0|hW z;e(3VD2MM!XM?zna@Ne4{LM<@zn@zG?9$+e!Unk`#{a1`bU}`hwG4m2K01HIxgfkk zo11sPt~&o#xr|>k^~_aS#{N0wLtka%{(tDMDs;c~KZBF?Mvyi?_?Y!Sy+8EvUgh|r zd1d5!??0mtXqdIqrmdGYv`3bqSHR6((=hUf)~^iwl=ERJa#zo?w|XeLaOfV%V1-}M z8!kxo)dDHxg1gPnAak^hto>I5M`xF1)Y2P^Q1v5sWdn4}< zPtx`V(H+j@(2OJ_n~NON=b|~#n1;q;?%G9n=8rJCzUXH}b{*n&xHVpbZM{bGeGh1U zqi5i0bTDkk25%>Fv%6CA(Bbh}Qm*WVj$cuAWzF7*cpB$xb^2xj(D= z$jGFU2R^?y;PX2MpWj*d{4T)f_hC#w0s0pfA^XJ|YV&_gjxO--Q5m*BC%Pz4^2~J` zndf@5XTE0vPfPlE`y=1@1vKAd+JC2o&CsN1OG|N|@NVOpz&pUQMR|eiD&RE(yKsxV zzw}Gid=BzOuE4|G(*UQ)Z?J80-j^MGADw=axDOFL>hiR<>-_Tz`gKHY;tpb~@$b}Snd4P3v5AgnC{mO`M>AA|6`99A+Vh=V1o@Af8WyTrupwn|>q|lo6Zsm>{JFW8m z?2(r!bDGNZp7TeL^vD8(tSkT z9mhM3|7Q7bdl()O*{J<>dd)yPW#cv$R;qRaw|%Cq+(W{DeM#+zPH}BF{Xy?~|6$Lr zmeyN03$_|-dmIupuH0`fqMpxN)|4;QxNdB@^{8MP(y4q$&UKGci6n>}Cfid6!|U;9&S2bjvIej%A%x8oswflbpD1 znk#&#q5qIIlDT2)hCtpcrWYa`2wHaB&i1NxG&r934DnGn4p5iv=s0p1#--Kpu z2e;?rw&6}!K3w=-t9~~CJ9L9Y>#~mkL-x5#{zH(HN)Ud+a?Nc$BgjgUmaw(hsZ1*gs&-+b}od1nrn3nuK5i-R?QH^*OARmbQl@~p2T&-xnjtVfY&eHA%S(KXJ(!ae3-bk=R) z0Ow`)yJ74OoDaNc^K%!vqS!WE9d5KrHntUZhwo*txNz7avmU%#wT``p>}DHXx8i{} zdkJ{JVtU$=CW_veFzz_uDe7kaJPnMt6Q9T0vx9avM>?ArQ^)l!%}rUf;dAV(O;kH3 zuzsy--+jpNmB-q#L$(~W{-{ju3HRfdOa=A%pTMSwv7>yozO=)1O9=;KFt@tE0{A;@b`PG+JEq#+EhgN_>Q#wX`O+N-!hLPoslr@ z|Dv_?2U~HkM+c%K()ok6ChhWk?f|X*Y4^l*1<=3ST01MhsyJ+mlx_}G^r`KJwv0>e z(!KPihQc$*aHm1LGvG@x=U3b|&avB9!cmf+OCJ^P&ZQ8Rz7+aLCZUT^Q7J@)&YSqszKj(*m?%sVl#qv-ea>5qw* zzy-r&AA@B&?;q|g_@{Xv8FEOt#9Ge#A8U~{WIH%`Mft$nC&?QnUV4+Z!&}|TKN6jv zxX-MIUH)mr>0b2#_+Ub6H)p8;Z7$(XoqH4Jk>$KBoaXG>K)O8AMXd9jmlx#ph`zto z8PeUJ75)$JM9v?+vB(OLPV3M|lGi%5(%r(IFgG|V5z?(8uqx*n*19Z*E(y)Hc$^^6CUMmT|2&IqS{&9kq_;3 zG^Fo{(N2vQI1ynzcvd{oz{Q8&uE5v^pB0>xYIthYcgkm1=M1rLm?-#ZJh*q7!1ryy zKz#1V&^Vmvq-#V+-Titm8h9(}w7wPRZzJy8?30e_9xyuJY3RKAG-11;tF_Vju1xy3 z6IeE?9?GjRZ%^%3-JtU7RbF_x?2{WEo?q7+VRL)AXzW>|!=)ElacBg3T*l0TMnKai z&4PZ|G}b}6%V^6(*R*r_pmsi@HmYs^8Ai80VBk}j>JA}?85(y@Ipx+sIgCXf@sCi~ zqvDC6-pAuS5rnPOeqU`@9mIW)xe_sb8khX6=Fs}(0Y5w-`S8B3R$JhiC}2&WF1`?x z2bpi^-Le34S^r=QcC^H=qP^>U#}gX6LHC@V6`j|F)7ZAX;zJnl0q3 zVH`S{H*K0j(4qk6XNSRog1j=|{Q1~8erfIo0<**A;Mu6an}1Obv@}*uvfIo>=8nqJ z+h%y2cQ5llO8YJ?ZFiIw-PdhRz@`OZm$b$b2Hj+T-tH*jzbrTmzSVZC6cIn;g-)B;H@qKUANyME%wJ#Q9~l=s}FuO}tU%s{ZKw{0G%m=%MAP&kCt-GcM3T zrKe5KM)DXGQEzR5|Ft05a`}wt?(DA?_PL>kYOE`L|>C4&IotDf##LF&uMHD8=$Y2%VOM91wX-K zR=ZcSGOft%0F#VN#$mgGby1)O*p+~zjam~o2Lk4eJzVbXf)A-PJ=?!MJ(qPVTlt3V z!EH5oN<9A6>GW?xXPVNYE#23VK8&;({?_7cYag1>nPHK)O}5)7bOz7J&NBY(>g(0@ z+Y^Y*qofC8^Juu}{^w>LX>DK4d9|bn`aHhF(H&ot$-h0A;j&VOc`}<{+%}!`AC0|B zv^lBQ^Oz@_{vFj=|7E(ZpTyJA??7;JQZMONcpmrUfE$Iex-<6)Ua`8LdQx>4zsTBf z#1TzD9N2ZStJ`@^ndbbWx}g^d!AV1UuZai#v9_sB(qslF59!s}9a*ka_|cdbv>QB8 zcyxZ2boddMvCFjMVXJggLvXbXj}q2*--h=6yGqh-;=atXHZ*kwiW<^}mJHMVnfx7` zpRhafFmE{Rt>zwvvQkC+)u+wP<35U)wK+2GG~ZX^dqll|N8u-qt;;D!_CaH$b7Qow zK=<$aqw}r#!5rRr=8gO)2CrfC-?B8fp-G|mdby!}9QT-2R~Q4c4-$T9jDgKIaCfWb z1AE4HwI#!HSk!?YA^$;lfwLZ%m#(>I^H$nhf}UZ&XdU*L59+OV)7=4|=qqqM86KAa z%}ZO|Sf0=t{-#yh@-Ib=TFYBr9d#Mr6z9h7TX>sbAI<(#d6u578p@g<33jA(F?Vvk z&DZeKv<%{s=lu>7(sw^* z!D}2Le<$l80e80$8qz$8tZLjLI*HHFEo4NhV_qBeP3nRxB=5-a;SOv zw>dvo{ZV)7q%m()ikGZXa8yEsy`8HiQgMf1Dfp`W8mIUk$7_#~ca1Me`vGH%!`zJx z)!JOgSa*!y3jgT}|8~K^-XjgjV-a>O4CXe!uveWC9`y)C`fc>{WvtNxhYhWy-9*T~Ox zsDH?)F}CU3V)Aq1J@CHhkEAu4acxC5gtr5uGrDMyOvtFI$Tr6F3LYBES&SiaLG$_E zZs=Gf&?q{#xq0j#X@lr$AO`c~0>3%sKIoOh4uO)XlGV(5?3v3~ABuHjK& ztMzt^xo_7#n{uww@DzC2Mt;H^FCp(}>bau1$Qm99b0@a3;rh>2w|A2-3)p7&XBAx5 z-I(%|k9|eTUiKji1vlOpc19OMcc|m7&g;3qiNVdR)q-7IzqSoGV3sS`sr}3&FM7Cx z{L6q}(T|__#yRgne-YzC{V}?JI+9nlxw$wAnlzE{w@q7hW{Cuf^xm<#dEJkcmN?FP zhJHM)y@lEee+&K08O_+z&*b?^2VAs{nt1py#RKyw_mD4(J_`PuBcnerT6OcE8H2$3 zm(FW^TLhdU@_Vva0r4`j#6DpXr&w|mh* zyE|7`Xs=Xc&Z16VoFC=tJ}{+Ldojj^x-IyWWA|LhzP%k=w^ELT%ZGFi3g4x0`n5AW zQQ>^E#aI#eUR!(Gp=V=zTGtVcm*k-dJ5ctx zr<6L3P3Yoz`S8=R2dHzl68BhPFkg3x(|B{W;H|q{cGHj&=~9qgQ!_ur6VLk~wA<`Y z;X(JYW`x2|SG^&7wBhaW#P9HR>TQtpJG@)CD_N(z5?_zE$+u(O9aY~^S!x4eI|`{^ z^7im@%q|;4U0%vs>Bus5F-OFMfgheQ-sM}UYccWPwR*{02;95Do6CZi;t9V*SQOcb z55d{)j_+_URKJ3^}I_`5!zP|8bXBhvE@Jq~*Lyk6V&b$$R z+0lx8>JFvB|2*$h?y(J}+E+NeK3^wxY^<@1nZx4}FKK_%h-INH|aHW${j_g+^bc0ac6fQ`|&L7QaIJWS*_kc z*exxcK_0*By%UFyzKwN$g8yye;fC8_o=Vwxtb(b zJif(Qo3FqlQ)*zd(ElKCiRyhxahy@nJ(ARC$4x??L16QXT>79NUST>r5>|uVvSd@P zmFPEVYld-8h`yu-3%%)G4b(k-tnOC0J7|2y-Kudfj%>aXSPs6lTfL3ryoWg7^eMct z+`B)x*tM)UXy#!Z=lLRjCpL_|E1)tK1}7#*V&Un>2yauk{OEC!ZYyI4pxAZupV4!(h4|ih z-IUj5^40jq5Pqp~r8hK?w>Wt}d1u0_e1SVyA9~X6AAr%k{P%TV&RXMU%$@g@C+{B= zw~x4qcAQIbI)lXO8CpwOv%~+un9xS>WG_6m`9lb{nL!VxBw<@l`9o4SmlTI8UFj&(kOUMLI7h zHaH`(bE+>(XKJ0F&HU)_`j&PicJbfKe}B;`_Q~x@zM_XhuJI42R4?sFvWm8EzCu`* z=AdZP($2&~o4ywb6X%ZIrDQ(KI*I%X=Qid?zw zWEK7RCdchR^M+2H@cQq^4gWrQ?|An0o7QkI+rWIHosEPkJUBjKf4C|Ep1P_lX(s}^ zJSRLIjTH&|({^*$6inF9I#MwHpf}X`eJhaG>)YkodGX(Ev?VD+q2s>BJGTC zTl!1>HqSO!M*8plZK7Mn@LV-6w@QmTXbZC4l#_wK)%N>r|9;z_W&0zxKVbW}+x`;U z-)8$uZGTtMrkl7|@LIM%%-H?e4vRFR`_&Gg&3(OP=Z%N$wf!MG%^}+#w*B3VQy z#+6povzWCs6P_^1Rt|y3>WbUn{Kzw1Ms|Ire~N+agv8SDBUWVWUC_dbxPtejL<4uv zFFnJ9lN0y;dp9(sz&XXF(LGu@cYe_ErK`Md&Cx+R#dk*MWmgEl!Y_!X>R!O{DC39C zUG|oh)JAu6#`}h1zrC}eEn|A*-_FKPowLfX!HuhVM&4QHOhCk(}DZ97xCSu)> zuHYQp$+*LDJ)1XGyri?O`-G=ca3jo@zSJqUx+eMA@N%#86`412*4u^0JF=HwJn#5t_&5yT26$GhbC#jO3oX{4r1Rm& zyuQ=Bv)miXH=6Ys-t7dlF4qoy!r;lu*j|CPunV0rTNF0-F7VCaoGrW(ePy52jsG^` zUt{qQea|qsm^DP{rUfT+PMC*{H`dDVyXkw2x*dyw$ETdn=MgqP*kpBs2c?W}esBbH zJ*(iqLlcp|_!4g=DYv&Zu+IEGb{+|44e1U}=3W3@iqUyP-`2XM_NY&df#eY3cffN3 z{ZB3lznZ?+v;#Zi!JE@&o9`P^`Zn$u+c@*NX%2G)bJDEdcj0%0k17VE z{0AvVYrC-@g8q!hB#TR(foN^kYSPsyEijiZK^uPg$=r)&6-Uy~1s_g`NY~Sp<<@NU z^3NH3Z$j8S&2#CdAZ~%~O{YZGx-;s6s?&Vg^SmsfGNA~5rS6W}kUx)tf%klY$ zzn(d1&c4i}d76*RP40y#vy(Z=UC!YB3$|T9G2?n?CfrQy=jIPBM1E8KZN1vxT=o{8 zNUVRukN)-k<<2C{8PmVJV*TUYW_Bn@|G1BcpT!BA7r%!fZUOh`b6fkXe#DyFVAlL` zR*e^%!vkx6x%KsiarBWjpEIzbIr!^Yo3GyWjPm6QCh>iR*89duNys@!t|VpRO!g^j zn{R0R&uiZe+7mm?8z|>pv!9uS>`WGTSudLtNg)%L

2QrS%1K;LEDdUmZBX9V&FR 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
;1(DP8jAiwd5M~{5pr~%@{nda2l$nbu2kEJ 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%0DB+@E|dN!@G$4xhwe2r0ns?=+E6<5BuPDh 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�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%0DK>Sx!=8g_AgzkF7%Vky@s!Bijs+K2J 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
RFn|3)E84bTL{z1Oe)Oe^92i2qv^83fl|r7 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 Date: Sat, 11 Nov 2023 20:31:37 +0100 Subject: [PATCH 965/974] hw/hppa: Require at least SeaBIOS-hppa version 12 The new SeaBIOS-hppa version 12 includes the necessary fixes to support emulated PA2.0 CPUs and which allows starting 64-bit Linux kernels in the guest. To boot a 64-bit machine use the "-machine C3700" qemu option. Signed-off-by: Helge Deller Acked-by: Richard Henderson Signed-off-by: Richard Henderson --- hw/hppa/machine.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index f7d9ce9b46..9d08f39490 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -34,7 +34,7 @@ #include "net/net.h" #include "qemu/log.h" -#define MIN_SEABIOS_HPPA_VERSION 10 /* require at least this fw version */ +#define MIN_SEABIOS_HPPA_VERSION 12 /* require at least this fw version */ /* Power button address at &PAGE0->pad[4] */ #define HPA_POWER_BUTTON (0x40 + 4 * sizeof(uint32_t)) From 0b2af475e9f325e1db98148d5423de61da6bf110 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 13 Nov 2023 15:07:21 +0100 Subject: [PATCH 966/974] tests/avocado/machine_s390_ccw_virtio.py: Fix SyntaxWarnings from python 3.12 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Python 3.12 now warns about backslashes in strings that aren't used for escaping a special character from Python. Silence the warning by using raw strings here instead. Signed-off-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20231113140721.46903-1-thuth@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- tests/avocado/machine_s390_ccw_virtio.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/avocado/machine_s390_ccw_virtio.py b/tests/avocado/machine_s390_ccw_virtio.py index e1f493bc44..ffd914ded9 100644 --- a/tests/avocado/machine_s390_ccw_virtio.py +++ b/tests/avocado/machine_s390_ccw_virtio.py @@ -36,8 +36,8 @@ class S390CCWVirtioMachine(QemuSystemTest): dmesg_clear_count = 1 def clear_guest_dmesg(self): exec_command_and_wait_for_pattern(self, 'dmesg -c > /dev/null; ' - 'echo dm_clear\ ' + str(self.dmesg_clear_count), - 'dm_clear ' + str(self.dmesg_clear_count)) + r'echo dm_clear\ ' + str(self.dmesg_clear_count), + r'dm_clear ' + str(self.dmesg_clear_count)) self.dmesg_clear_count += 1 def test_s390x_devices(self): @@ -121,15 +121,15 @@ class S390CCWVirtioMachine(QemuSystemTest): 'cat /sys/bus/ccw/devices/0.1.1111/cutype', '3832/01') exec_command_and_wait_for_pattern(self, - 'cat /sys/bus/pci/devices/0005\:00\:00.0/subsystem_vendor', - '0x1af4') + r'cat /sys/bus/pci/devices/0005\:00\:00.0/subsystem_vendor', + r'0x1af4') exec_command_and_wait_for_pattern(self, - 'cat /sys/bus/pci/devices/0005\:00\:00.0/subsystem_device', - '0x0001') + r'cat /sys/bus/pci/devices/0005\:00\:00.0/subsystem_device', + r'0x0001') # check fid propagation exec_command_and_wait_for_pattern(self, - 'cat /sys/bus/pci/devices/000a\:00\:00.0/function_id', - '0x0000000c') + r'cat /sys/bus/pci/devices/000a\:00\:00.0/function_id', + r'0x0000000c') # add another device self.clear_guest_dmesg() self.vm.cmd('device_add', driver='virtio-net-ccw', @@ -235,7 +235,7 @@ class S390CCWVirtioMachine(QemuSystemTest): 'while ! (dmesg | grep gpudrmfb) ; do sleep 1 ; done', 'virtio_gpudrmfb frame buffer device') exec_command_and_wait_for_pattern(self, - 'echo -e "\e[?25l" > /dev/tty0', ':/#') + r'echo -e "\e[?25l" > /dev/tty0', ':/#') exec_command_and_wait_for_pattern(self, 'for ((i=0;i<250;i++)); do ' 'echo " The qu ick fo x j ump s o ver a laz y d og" >> fox.txt;' 'done', From 04c0a003dd33c7f1dea989319085984822c768e6 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Sat, 11 Nov 2023 08:49:20 +0800 Subject: [PATCH 967/974] MAINTAINERS: update virtio-fs mailing list address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The old virtio-fs mailing list address is no longer in use. Switch to the new mailing list address. Cc: Philippe Mathieu-Daudé Cc: Vivek Goyal Cc: German Maglione Cc: Hanna Czenczek Signed-off-by: Stefan Hajnoczi Reviewed-by: German Maglione Message-ID: <20231111004920.148348-1-stefanha@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index e73a3ff544..c52df9f76c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2241,7 +2241,7 @@ M: Stefan Hajnoczi S: Supported F: hw/virtio/vhost-user-fs* F: include/hw/virtio/vhost-user-fs.h -L: virtio-fs@redhat.com +L: virtio-fs@lists.linux.dev virtio-input M: Gerd Hoffmann From 816644b1219900875f47d7adf9bfb283f1b29aa0 Mon Sep 17 00:00:00 2001 From: Janosch Frank Date: Thu, 9 Nov 2023 12:04:41 +0000 Subject: [PATCH 968/974] target/s390x/dump: Remove unneeded dump info function pointer init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dump_state_prepare() now sets the function pointers to NULL so we only need to touch them if we're going to use them. Signed-off-by: Janosch Frank Reviewed-by: Marc-André Lureau Reviewed-by: Thomas Huth Message-ID: <20231109120443.185979-2-frankja@linux.ibm.com> Signed-off-by: Thomas Huth --- target/s390x/arch_dump.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/target/s390x/arch_dump.c b/target/s390x/arch_dump.c index 51a2116515..bdb0bfa0e7 100644 --- a/target/s390x/arch_dump.c +++ b/target/s390x/arch_dump.c @@ -448,10 +448,6 @@ int cpu_get_dump_info(ArchDumpInfo *info, info->arch_sections_add_fn = *arch_sections_add; info->arch_sections_write_hdr_fn = *arch_sections_write_hdr; info->arch_sections_write_fn = *arch_sections_write; - } else { - info->arch_sections_add_fn = NULL; - info->arch_sections_write_hdr_fn = NULL; - info->arch_sections_write_fn = NULL; } return 0; } From e72629e5149aba6f44122ea6d2a803ef136a0c6b Mon Sep 17 00:00:00 2001 From: Janosch Frank Date: Thu, 9 Nov 2023 12:04:42 +0000 Subject: [PATCH 969/974] dump: Add arch cleanup function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some architectures (s390x) need to cleanup after a failed dump to be able to continue to run the vm. Add a cleanup function pointer and call it if it's set. Signed-off-by: Janosch Frank Reviewed-by: Thomas Huth Reviewed-by: Marc-André Lureau Message-ID: <20231109120443.185979-3-frankja@linux.ibm.com> Signed-off-by: Thomas Huth --- dump/dump.c | 4 ++++ include/sysemu/dump-arch.h | 1 + 2 files changed, 5 insertions(+) diff --git a/dump/dump.c b/dump/dump.c index ad5294e853..4819050764 100644 --- a/dump/dump.c +++ b/dump/dump.c @@ -96,6 +96,10 @@ uint64_t cpu_to_dump64(DumpState *s, uint64_t val) static int dump_cleanup(DumpState *s) { + if (s->dump_info.arch_cleanup_fn) { + s->dump_info.arch_cleanup_fn(s); + } + guest_phys_blocks_free(&s->guest_phys_blocks); memory_mapping_list_free(&s->list); close(s->fd); diff --git a/include/sysemu/dump-arch.h b/include/sysemu/dump-arch.h index 59bbc9be38..743916e46c 100644 --- a/include/sysemu/dump-arch.h +++ b/include/sysemu/dump-arch.h @@ -24,6 +24,7 @@ typedef struct ArchDumpInfo { void (*arch_sections_add_fn)(DumpState *s); uint64_t (*arch_sections_write_hdr_fn)(DumpState *s, uint8_t *buff); int (*arch_sections_write_fn)(DumpState *s, uint8_t *buff); + void (*arch_cleanup_fn)(DumpState *s); } ArchDumpInfo; struct GuestPhysBlockList; /* memory_mapping.h */ From d12a91e0baafce7b1cbacff7cf9339eeb0011732 Mon Sep 17 00:00:00 2001 From: Janosch Frank Date: Thu, 9 Nov 2023 12:04:43 +0000 Subject: [PATCH 970/974] target/s390x/arch_dump: Add arch cleanup function for PV dumps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PV dumps block vcpu runs until dump end is reached. If there's an error between PV dump init and PV dump end the vm will never be able to run again. One example of such an error is insufficient disk space for the dump file. Let's add a cleanup function that tries to do a dump end. The dump completion data is discarded but there's no point in writing it to a file anyway if there's a possibility that other PV dump data is missing. Signed-off-by: Janosch Frank Reviewed-by: Thomas Huth Reviewed-by: Claudio Imbrenda Reviewed-by: Marc-André Lureau Message-ID: <20231109120443.185979-4-frankja@linux.ibm.com> Signed-off-by: Thomas Huth --- target/s390x/arch_dump.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/target/s390x/arch_dump.c b/target/s390x/arch_dump.c index bdb0bfa0e7..7e8a1b4fc0 100644 --- a/target/s390x/arch_dump.c +++ b/target/s390x/arch_dump.c @@ -433,6 +433,22 @@ static int arch_sections_write(DumpState *s, uint8_t *buff) return 0; } +static void arch_cleanup(DumpState *s) +{ + g_autofree uint8_t *buff = NULL; + int rc; + + if (!pv_dump_initialized) { + return; + } + + buff = g_malloc(kvm_s390_pv_dmp_get_size_completion_data()); + rc = kvm_s390_dump_completion_data(buff); + if (!rc) { + pv_dump_initialized = false; + } +} + int cpu_get_dump_info(ArchDumpInfo *info, const struct GuestPhysBlockList *guest_phys_blocks) { @@ -448,6 +464,7 @@ int cpu_get_dump_info(ArchDumpInfo *info, info->arch_sections_add_fn = *arch_sections_add; info->arch_sections_write_hdr_fn = *arch_sections_write_hdr; info->arch_sections_write_fn = *arch_sections_write; + info->arch_cleanup_fn = *arch_cleanup; } return 0; } From 9c673a41eefc50f1cb2fe3c083e7de842c7d276a Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 14 Nov 2023 12:35:47 -0500 Subject: [PATCH 971/974] Update version for v8.2.0-rc0 release Signed-off-by: Stefan Hajnoczi --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index ae3313c92b..eea587b882 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.1.50 +8.1.90 From cf9b5790db77e5efdae589acff8a98165d2543e0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 10 Nov 2023 08:21:23 -0800 Subject: [PATCH 972/974] accel/tcg: Remove CF_LAST_IO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In cpu_exec_step_atomic, we did not set CF_LAST_IO, which lead to a loop with cpu_io_recompile. But since 18a536f1f8 ("Always require can_do_io") we no longer need a flag to indicate when the last insn should have can_do_io set, so remove the flag entirely. Reported-by: Clément Chigot Tested-by: Clément Chigot Reviewed-by: Claudio Fontana Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1961 Signed-off-by: Richard Henderson --- accel/tcg/cpu-exec.c | 2 +- accel/tcg/tb-maint.c | 6 ++---- accel/tcg/translate-all.c | 4 ++-- accel/tcg/translator.c | 22 +++++++++------------- docs/devel/tcg-icount.rst | 6 ------ include/exec/translation-block.h | 13 ++++++------- system/watchpoint.c | 6 ++---- 7 files changed, 22 insertions(+), 37 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 1a5bc90220..c938eb96f8 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -721,7 +721,7 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret) && cpu->neg.icount_decr.u16.low + cpu->icount_extra == 0) { /* Execute just one insn to trigger exception pending in the log */ cpu->cflags_next_tb = (curr_cflags(cpu) & ~CF_USE_ICOUNT) - | CF_LAST_IO | CF_NOIRQ | 1; + | CF_NOIRQ | 1; } #endif return false; diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c index e678d20dc2..3d2a896220 100644 --- a/accel/tcg/tb-maint.c +++ b/accel/tcg/tb-maint.c @@ -1083,8 +1083,7 @@ bool tb_invalidate_phys_page_unwind(tb_page_addr_t addr, uintptr_t pc) if (current_tb_modified) { /* Force execution of one insn next time. */ CPUState *cpu = current_cpu; - cpu->cflags_next_tb = - 1 | CF_LAST_IO | CF_NOIRQ | curr_cflags(current_cpu); + cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(current_cpu); return true; } return false; @@ -1154,8 +1153,7 @@ tb_invalidate_phys_page_range__locked(struct page_collection *pages, if (current_tb_modified) { page_collection_unlock(pages); /* Force execution of one insn next time. */ - current_cpu->cflags_next_tb = - 1 | CF_LAST_IO | CF_NOIRQ | curr_cflags(current_cpu); + current_cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(current_cpu); mmap_unlock(); cpu_loop_exit_noexc(current_cpu); } diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index b263857ecc..79a88f5fb7 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -304,7 +304,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, if (phys_pc == -1) { /* Generate a one-shot TB with 1 insn in it */ - cflags = (cflags & ~CF_COUNT_MASK) | CF_LAST_IO | 1; + cflags = (cflags & ~CF_COUNT_MASK) | 1; } max_insns = cflags & CF_COUNT_MASK; @@ -632,7 +632,7 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr) * operations only (which execute after completion) so we don't * double instrument the instruction. */ - cpu->cflags_next_tb = curr_cflags(cpu) | CF_MEMI_ONLY | CF_LAST_IO | n; + cpu->cflags_next_tb = curr_cflags(cpu) | CF_MEMI_ONLY | n; if (qemu_loglevel_mask(CPU_LOG_EXEC)) { vaddr pc = log_pc(cpu, tb); diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index 575b9812ad..38c34009a5 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -89,7 +89,7 @@ static TCGOp *gen_tb_start(DisasContextBase *db, uint32_t cflags) * each translation block. The cost is minimal, plus it would be * very easy to forget doing it in the translator. */ - set_can_do_io(db, db->max_insns == 1 && (cflags & CF_LAST_IO)); + set_can_do_io(db, db->max_insns == 1); return icount_start_insn; } @@ -151,13 +151,7 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, ops->tb_start(db, cpu); tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ - if (cflags & CF_MEMI_ONLY) { - /* We should only see CF_MEMI_ONLY for io_recompile. */ - assert(cflags & CF_LAST_IO); - plugin_enabled = plugin_gen_tb_start(cpu, db, true); - } else { - plugin_enabled = plugin_gen_tb_start(cpu, db, false); - } + plugin_enabled = plugin_gen_tb_start(cpu, db, cflags & CF_MEMI_ONLY); db->plugin_enabled = plugin_enabled; while (true) { @@ -169,11 +163,13 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, plugin_gen_insn_start(cpu, db); } - /* Disassemble one instruction. The translate_insn hook should - update db->pc_next and db->is_jmp to indicate what should be - done next -- either exiting this loop or locate the start of - the next instruction. */ - if (db->num_insns == db->max_insns && (cflags & CF_LAST_IO)) { + /* + * Disassemble one instruction. The translate_insn hook should + * update db->pc_next and db->is_jmp to indicate what should be + * done next -- either exiting this loop or locate the start of + * the next instruction. + */ + if (db->num_insns == db->max_insns) { /* Accept I/O on the last instruction. */ set_can_do_io(db, true); } diff --git a/docs/devel/tcg-icount.rst b/docs/devel/tcg-icount.rst index 50c8e8dabc..7df883446a 100644 --- a/docs/devel/tcg-icount.rst +++ b/docs/devel/tcg-icount.rst @@ -62,12 +62,6 @@ To deal with this case, when an I/O access is made we: - re-compile a single [1]_ instruction block for the current PC - exit the cpu loop and execute the re-compiled block -The new block is created with the CF_LAST_IO compile flag which -ensures the final instruction translation starts with a call to -gen_io_start() so we don't enter a perpetual loop constantly -recompiling a single instruction block. For translators using the -common translator_loop this is done automatically. - .. [1] sometimes two instructions if dealing with delay slots Other I/O operations diff --git a/include/exec/translation-block.h b/include/exec/translation-block.h index b785751774..e2b26e16da 100644 --- a/include/exec/translation-block.h +++ b/include/exec/translation-block.h @@ -71,13 +71,12 @@ struct TranslationBlock { #define CF_NO_GOTO_TB 0x00000200 /* Do not chain with goto_tb */ #define CF_NO_GOTO_PTR 0x00000400 /* Do not chain with goto_ptr */ #define CF_SINGLE_STEP 0x00000800 /* gdbstub single-step in effect */ -#define CF_LAST_IO 0x00008000 /* Last insn may be an IO access. */ -#define CF_MEMI_ONLY 0x00010000 /* Only instrument memory ops */ -#define CF_USE_ICOUNT 0x00020000 -#define CF_INVALID 0x00040000 /* TB is stale. Set with @jmp_lock held */ -#define CF_PARALLEL 0x00080000 /* Generate code for a parallel context */ -#define CF_NOIRQ 0x00100000 /* Generate an uninterruptible TB */ -#define CF_PCREL 0x00200000 /* Opcodes in TB are PC-relative */ +#define CF_MEMI_ONLY 0x00001000 /* Only instrument memory ops */ +#define CF_USE_ICOUNT 0x00002000 +#define CF_INVALID 0x00004000 /* TB is stale. Set with @jmp_lock held */ +#define CF_PARALLEL 0x00008000 /* Generate code for a parallel context */ +#define CF_NOIRQ 0x00010000 /* Generate an uninterruptible TB */ +#define CF_PCREL 0x00020000 /* Opcodes in TB are PC-relative */ #define CF_CLUSTER_MASK 0xff000000 /* Top 8 bits are cluster ID */ #define CF_CLUSTER_SHIFT 24 diff --git a/system/watchpoint.c b/system/watchpoint.c index 45d1f12faf..ba5ad13352 100644 --- a/system/watchpoint.c +++ b/system/watchpoint.c @@ -179,8 +179,7 @@ void cpu_check_watchpoint(CPUState *cpu, vaddr addr, vaddr len, */ if (!cpu->neg.can_do_io) { /* Force execution of one insn next time. */ - cpu->cflags_next_tb = 1 | CF_LAST_IO | CF_NOIRQ - | curr_cflags(cpu); + cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(cpu); cpu_loop_exit_restore(cpu, ra); } /* @@ -212,8 +211,7 @@ void cpu_check_watchpoint(CPUState *cpu, vaddr addr, vaddr len, cpu_loop_exit(cpu); } else { /* Force execution of one insn next time. */ - cpu->cflags_next_tb = 1 | CF_LAST_IO | CF_NOIRQ - | curr_cflags(cpu); + cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(cpu); mmap_unlock(); cpu_loop_exit_noexc(cpu); } From e2faabee78ff127848f59892747d4c07c56de033 Mon Sep 17 00:00:00 2001 From: Jessica Clarke Date: Fri, 10 Nov 2023 21:43:03 -0800 Subject: [PATCH 973/974] accel/tcg: Forward probe size on to notdirty_write Without this, we just dirty a single byte, and so if the caller writes more than one byte to the host memory then we won't have invalidated any translation blocks that start after the first byte and overlap those writes. In particular, AArch64's DC ZVA implementation uses probe_access (via probe_write), and so we don't invalidate the entire block, only the TB overlapping the first byte (and, in the unusual case an unaligned VA is given to the instruction, we also probe that specific address in order to get the right VA reported on an exception, so will invalidate a TB overlapping that address too). Since our IC IVAU implementation is a no-op for system emulation that relies on the softmmu already having detected self-modifying code via this mechanism, this means we have observably wrong behaviour when jumping to code that has been DC ZVA'ed. In practice this is an unusual thing for software to do, as in reality the OS will DC ZVA the page and the application will go and write actual instructions to it that aren't UDF #0, but you can write a test that clearly shows the faulty behaviour. For functions other than probe_access it's not clear what size to use when 0 is passed in. Arguably a size of 0 shouldn't dirty at all, since if you want to actually write then you should pass in a real size, but I have conservatively kept the implementation as dirtying the first byte in that case so as to avoid breaking any assumptions about that behaviour. Signed-off-by: Jessica Clarke Message-Id: <20231104031232.3246614-1-jrtc27@jrtc27.com> [rth: Move the dirtysize computation next to notdirty_write.] Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 765805e70b..db3f93fda9 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -1479,7 +1479,8 @@ int probe_access_full(CPUArchState *env, vaddr addr, int size, /* Handle clean RAM pages. */ if (unlikely(flags & TLB_NOTDIRTY)) { - notdirty_write(env_cpu(env), addr, 1, *pfull, retaddr); + int dirtysize = size == 0 ? 1 : size; + notdirty_write(env_cpu(env), addr, dirtysize, *pfull, retaddr); flags &= ~TLB_NOTDIRTY; } @@ -1502,7 +1503,8 @@ int probe_access_full_mmu(CPUArchState *env, vaddr addr, int size, /* Handle clean RAM pages. */ if (unlikely(flags & TLB_NOTDIRTY)) { - notdirty_write(env_cpu(env), addr, 1, *pfull, 0); + int dirtysize = size == 0 ? 1 : size; + notdirty_write(env_cpu(env), addr, dirtysize, *pfull, 0); flags &= ~TLB_NOTDIRTY; } @@ -1524,7 +1526,8 @@ int probe_access_flags(CPUArchState *env, vaddr addr, int size, /* Handle clean RAM pages. */ if (unlikely(flags & TLB_NOTDIRTY)) { - notdirty_write(env_cpu(env), addr, 1, full, retaddr); + int dirtysize = size == 0 ? 1 : size; + notdirty_write(env_cpu(env), addr, dirtysize, full, retaddr); flags &= ~TLB_NOTDIRTY; } @@ -1560,7 +1563,7 @@ void *probe_access(CPUArchState *env, vaddr addr, int size, /* Handle clean RAM pages. */ if (flags & TLB_NOTDIRTY) { - notdirty_write(env_cpu(env), addr, 1, full, retaddr); + notdirty_write(env_cpu(env), addr, size, full, retaddr); } } From 0dfae4f94813995c365bfabac22cba1cea8758bd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Nov 2023 12:47:00 -0800 Subject: [PATCH 974/974] target/sparc: Fix RETURN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Perform window restore before pc update. Required in order to recognize any window underflow trap with the current pc. Fixes: 86b82fe021f4 ("target/sparc: Move JMPL, RETT, RETURN to decodetree") Reported-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Tested-by: Mark Cave-Ayland Acked-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/translate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 6fc333a6b8..9387299559 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -4096,12 +4096,12 @@ TRANS(RETT, 32, do_add_special, a, do_rett) static bool do_return(DisasContext *dc, int rd, TCGv src) { gen_check_align(dc, src, 3); + gen_helper_restore(tcg_env); gen_mov_pc_npc(dc); tcg_gen_mov_tl(cpu_npc, src); gen_address_mask(dc, cpu_npc); - gen_helper_restore(tcg_env); dc->npc = DYNAMIC_PC_LOOKUP; return true; }