From 9f07e47a5e96c88c1d2892fbdcbc8ff0437b7ac3 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 May 2024 17:44:12 +0200 Subject: [PATCH 01/27] target/i386: remove PCOMMIT from TCG, deprecate property The PCOMMIT instruction was never included in any physical processor. TCG implements it as a no-op instruction, but its utility is debatable to say the least. Drop it from the decoder since it is only available with "-cpu max", which does not guarantee migration compatibility across versions, and deprecate the property just in case someone is using it as "pcommit=off". Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- docs/about/deprecated.rst | 8 ++++++++ target/i386/cpu.c | 2 +- target/i386/cpu.h | 2 -- target/i386/tcg/translate.c | 12 +----------- 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 5c3ca47801..40585ca7d5 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -432,6 +432,14 @@ Backend ``memory`` (since 9.0) CPU device properties ''''''''''''''''''''' +``pcommit`` on x86 (since 9.1) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The PCOMMIT instruction was never included in any physical processor. +It was implemented as a no-op instruction in TCG up to QEMU 9.0, but +only with ``-cpu max`` (which does not guarantee migration compatibility +across versions). + ``pmu-num=n`` on RISC-V CPUs (since 8.2) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 1058b6803f..79372de8c5 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -713,7 +713,7 @@ void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, #endif #define TCG_7_0_EBX_FEATURES (CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_SMAP | \ CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ADX | \ - CPUID_7_0_EBX_PCOMMIT | CPUID_7_0_EBX_CLFLUSHOPT | \ + 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_SHA_NI | CPUID_7_0_EBX_KERNEL_FEATURES) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 1e0d2c915f..ccccb62fc3 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -816,8 +816,6 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, #define CPUID_7_0_EBX_SMAP (1U << 20) /* AVX-512 Integer Fused Multiply Add */ #define CPUID_7_0_EBX_AVX512IFMA (1U << 21) -/* Persistent Commit */ -#define CPUID_7_0_EBX_PCOMMIT (1U << 22) /* Flush a Cache Line Optimized */ #define CPUID_7_0_EBX_CLFLUSHOPT (1U << 23) /* Cache Line Write Back */ diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 3842b29484..7d9f6b5c55 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -4487,17 +4487,7 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) } goto unknown_op; - case 0xf8: /* sfence / pcommit */ - if (prefixes & PREFIX_DATA) { - /* pcommit */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_PCOMMIT) - || (prefixes & PREFIX_LOCK)) { - goto illegal_op; - } - break; - } - /* fallthru */ - case 0xf9 ... 0xff: /* sfence */ + case 0xf8 ... 0xff: /* sfence */ if (!(s->cpuid_features & CPUID_SSE) || (prefixes & PREFIX_LOCK)) { goto illegal_op; From 41c685dc59bb611096f3bb6a663cfa82e4cba97b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 12:38:10 +0200 Subject: [PATCH 02/27] target/i386: fix operand size for DATA16 REX.W POPCNT According to the manual, 32-bit vs 64-bit is governed by REX.W and REX ignores the 0x66 prefix. This can be confirmed with this program: #include int main() { int x = 0x12340000; int y; asm("popcntl %1, %0" : "=r" (y) : "r" (x)); printf("%x\n", y); asm("mov $-1, %0; .byte 0x66; popcntl %1, %0" : "+r" (y) : "r" (x)); printf("%x\n", y); asm("mov $-1, %0; .byte 0x66; popcntq %q1, %q0" : "+r" (y) : "r" (x)); printf("%x\n", y); } which prints 5/ffff0000/5 on real hardware and 5/ffff0000/ffff0000 on QEMU. Cc: qemu-stable@nongnu.org Reviewed-by: Zhao Liu Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 7d9f6b5c55..5366dc32dd 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -411,16 +411,6 @@ static inline MemOp mo_stacksize(DisasContext *s) return CODE64(s) ? MO_64 : SS32(s) ? MO_32 : MO_16; } -/* Select only size 64 else 32. Used for SSE operand sizes. */ -static inline MemOp mo_64_32(MemOp ot) -{ -#ifdef TARGET_X86_64 - return ot == MO_64 ? MO_64 : MO_32; -#else - return MO_32; -#endif -} - /* Select size 8 if lsb of B is clear, else OT. Used for decoding byte vs word opcodes. */ static inline MemOp mo_b_d(int b, MemOp ot) @@ -4545,12 +4535,7 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) modrm = x86_ldub_code(env, s); reg = ((modrm >> 3) & 7) | REX_R(s); - if (s->prefix & PREFIX_DATA) { - ot = MO_16; - } else { - ot = mo_64_32(dflag); - } - + ot = dflag; gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); gen_extu(ot, s->T0); tcg_gen_mov_tl(cpu_cc_src, s->T0); From 40a3ec7b5ffde500789d016660a171057d6b467c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 15:55:47 +0200 Subject: [PATCH 03/27] target/i386: rdpkru/wrpkru are no-prefix instructions Reject 0x66/0xf3/0xf2 in front of them. Cc: qemu-stable@nongnu.org Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 5366dc32dd..3da4fdf64c 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -3907,7 +3907,8 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1); break; case 0xee: /* rdpkru */ - if (prefixes & PREFIX_LOCK) { + if (s->prefix & (PREFIX_LOCK | PREFIX_DATA + | PREFIX_REPZ | PREFIX_REPNZ)) { goto illegal_op; } tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_ECX]); @@ -3915,7 +3916,8 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) tcg_gen_extr_i64_tl(cpu_regs[R_EAX], cpu_regs[R_EDX], s->tmp1_i64); break; case 0xef: /* wrpkru */ - if (prefixes & PREFIX_LOCK) { + if (s->prefix & (PREFIX_LOCK | PREFIX_DATA + | PREFIX_REPZ | PREFIX_REPNZ)) { goto illegal_op; } tcg_gen_concat_tl_i64(s->tmp1_i64, cpu_regs[R_EAX], From 3fabbe0b7d458d6380f4b3246b8b32400f6bd1d9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 6 May 2024 17:34:55 +0200 Subject: [PATCH 04/27] target/i386: move prefetch and multi-byte UD/NOP to new decoder These are trivial to add, and moving them to the new decoder fixes some corner cases: raising #UD instead of an instruction fetch page fault for the undefined opcodes, and incorrectly rejecting 0F 18 prefetches with register operands (which are treated as reserved NOPs). Reviewed-by: Richard Henderson Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 24 +++++++++++++++++++++--- target/i386/tcg/decode-new.h | 1 + target/i386/tcg/emit.c.inc | 5 +++++ target/i386/tcg/translate.c | 30 ------------------------------ 4 files changed, 27 insertions(+), 33 deletions(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index 0e1811399f..141ab2bc56 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -55,6 +55,10 @@ * mask could be applied (and the original sign-extended value would be * optimized away by TCG) in the emitter function. * + * Finally, a "nop" operand type is used for multi-byte NOPs. It accepts + * any value of mod including 11b (unlike M) but it does not try to + * interpret the operand (like M). + * * Vector operands * --------------- * @@ -1056,6 +1060,16 @@ static const X86OpEntry opcodes_0F[256] = { [0xa0] = X86_OP_ENTRYr(PUSH, FS, w), [0xa1] = X86_OP_ENTRYw(POP, FS, w), + [0x0b] = X86_OP_ENTRY0(UD), /* UD2 */ + [0x0d] = X86_OP_ENTRY1(NOP, M,v), /* 3DNow! prefetch */ + + [0x18] = X86_OP_ENTRY1(NOP, nop,v), /* prefetch/reserved NOP */ + [0x19] = X86_OP_ENTRY1(NOP, nop,v), /* reserved NOP */ + [0x1c] = X86_OP_ENTRY1(NOP, nop,v), /* reserved NOP */ + [0x1d] = X86_OP_ENTRY1(NOP, nop,v), /* reserved NOP */ + [0x1e] = X86_OP_ENTRY1(NOP, nop,v), /* reserved NOP */ + [0x1f] = X86_OP_ENTRY1(NOP, nop,v), /* NOP/reserved NOP */ + [0x28] = X86_OP_ENTRY3(MOVDQ, V,x, None,None, W,x, vex1 p_00_66), /* MOVAPS */ [0x29] = X86_OP_ENTRY3(MOVDQ, W,x, None,None, V,x, vex1 p_00_66), /* MOVAPS */ [0x2A] = X86_OP_GROUP0(0F2A), @@ -1135,6 +1149,8 @@ static const X86OpEntry opcodes_0F[256] = { [0xb6] = X86_OP_ENTRY3(MOV, G,v, E,b, None, None, zextT0), /* MOVZX */ [0xb7] = X86_OP_ENTRY3(MOV, G,v, E,w, None, None, zextT0), /* MOVZX */ + /* decoded as modrm, which is visible as a difference between page fault and #UD */ + [0xb9] = X86_OP_ENTRYr(UD, nop,v), /* UD1 */ [0xbe] = X86_OP_ENTRY3(MOV, G,v, E,b, None, None, sextT0), /* MOVSX */ [0xbf] = X86_OP_ENTRY3(MOV, G,v, E,w, None, None, sextT0), /* MOVSX */ @@ -1206,7 +1222,7 @@ static const X86OpEntry opcodes_0F[256] = { [0xfc] = X86_OP_ENTRY3(PADDB, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), [0xfd] = X86_OP_ENTRY3(PADDW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), [0xfe] = X86_OP_ENTRY3(PADDD, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), - /* 0xff = UD0 */ + [0xff] = X86_OP_ENTRYr(UD, nop,v), /* UD0 */ }; static void do_decode_0F(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) @@ -1852,6 +1868,8 @@ static bool decode_op(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, if ((modrm >> 6) == 3) { return false; } + /* fall through */ + case X86_TYPE_nop: /* modrm operand decoded but not fetched */ get_modrm: decode_modrm(s, env, decode, op, type); break; @@ -2397,8 +2415,8 @@ static void disas_insn(DisasContext *s, CPUState *cpu) switch (b) { case 0x00 ... 0x03: /* mostly privileged instructions */ case 0x05 ... 0x09: - case 0x0d: /* 3DNow! prefetch */ - case 0x18 ... 0x23: /* prefetch, MPX, mov from/to CR and DR */ + case 0x1a ... 0x1b: /* MPX */ + case 0x20 ... 0x23: /* mov from/to CR and DR */ case 0x30 ... 0x35: /* more privileged instructions */ case 0xa2 ... 0xa5: /* CPUID, BT, SHLD */ case 0xaa ... 0xae: /* RSM, SHRD, grp15 */ diff --git a/target/i386/tcg/decode-new.h b/target/i386/tcg/decode-new.h index 2ea06b4478..51ef0e621b 100644 --- a/target/i386/tcg/decode-new.h +++ b/target/i386/tcg/decode-new.h @@ -50,6 +50,7 @@ typedef enum X86OpType { X86_TYPE_EM, /* modrm byte selects an ALU memory operand */ X86_TYPE_WM, /* modrm byte selects an XMM/YMM memory operand */ X86_TYPE_I_unsigned, /* Immediate, zero-extended */ + X86_TYPE_nop, /* modrm operand decoded but not loaded into s->T{0,1} */ X86_TYPE_2op, /* 2-operand RMW instruction */ X86_TYPE_LoBits, /* encoded in bits 0-2 of the operand + REX.B */ X86_TYPE_0, /* Hard-coded GPRs (RAX..RDI) */ diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 58f255873f..2dee33dd48 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -3517,6 +3517,11 @@ static void gen_SUB(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) prepare_update2_cc(decode, s, CC_OP_SUBB + ot); } +static void gen_UD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_illegal_opcode(s); +} + static void gen_VAESIMC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { assert(!s->vex_l); diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 3da4fdf64c..de87775016 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -4019,25 +4019,6 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) set_cc_op(s, CC_OP_EFLAGS); } break; - case 0x118: - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - op = (modrm >> 3) & 7; - switch(op) { - case 0: /* prefetchnta */ - case 1: /* prefetchnt0 */ - case 2: /* prefetchnt0 */ - case 3: /* prefetchnt0 */ - if (mod == 3) - goto illegal_op; - gen_nop_modrm(env, s, modrm); - /* nothing more to do */ - break; - default: /* nop (multi byte) */ - gen_nop_modrm(env, s, modrm); - break; - } - break; case 0x11a: modrm = x86_ldub_code(env, s); if (s->flags & HF_MPX_EN_MASK) { @@ -4229,10 +4210,6 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) } gen_nop_modrm(env, s, modrm); break; - case 0x119: case 0x11c ... 0x11f: /* nop (multi byte) */ - modrm = x86_ldub_code(env, s); - gen_nop_modrm(env, s, modrm); - break; case 0x120: /* mov reg, crN */ case 0x122: /* mov crN, reg */ @@ -4506,13 +4483,6 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) } break; - case 0x10d: /* 3DNow! prefetch(w) */ - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - if (mod == 3) - goto illegal_op; - gen_nop_modrm(env, s, modrm); - break; case 0x1aa: /* rsm */ gen_svm_check_intercept(s, SVM_EXIT_RSM); if (!(s->flags & HF_SMM_MASK)) From fe01af5d47d4cf7fdf90c54d43f784e5068c8d72 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 May 2024 11:10:54 +0200 Subject: [PATCH 05/27] target/i386: fix feature dependency for WAITPKG The VMX feature bit depends on general availability of WAITPKG, not the other way round. Fixes: 33cc88261c3 ("target/i386: add support for VMX_SECONDARY_EXEC_ENABLE_USER_WAIT_PAUSE", 2023-08-28) Cc: qemu-stable@nongnu.org Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 79372de8c5..cfe7c92d6b 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -1551,8 +1551,8 @@ static FeatureDep feature_dependencies[] = { .to = { FEAT_SVM, ~0ull }, }, { - .from = { FEAT_VMX_SECONDARY_CTLS, VMX_SECONDARY_EXEC_ENABLE_USER_WAIT_PAUSE }, - .to = { FEAT_7_0_ECX, CPUID_7_0_ECX_WAITPKG }, + .from = { FEAT_7_0_ECX, CPUID_7_0_ECX_WAITPKG }, + .to = { FEAT_VMX_SECONDARY_CTLS, VMX_SECONDARY_EXEC_ENABLE_USER_WAIT_PAUSE }, }, }; From ff5b5739f97d08d9ca984ec8016b54487a76401b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 12:41:38 +0200 Subject: [PATCH 06/27] tests/tcg: cover lzcnt/tzcnt/popcnt Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- tests/tcg/i386/test-i386.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/tcg/i386/test-i386.c b/tests/tcg/i386/test-i386.c index 864c4e620d..ce3bf74b5a 100644 --- a/tests/tcg/i386/test-i386.c +++ b/tests/tcg/i386/test-i386.c @@ -715,6 +715,30 @@ void test_mul(void) printf("%-10s A=" FMTLX " R=" FMTLX " %ld\n", #op, val, res, resz);\ } +void test_xcnt(void) +{ + TEST_BSX(tzcntw, "w", 0); + TEST_BSX(tzcntw, "w", 0x12340128); + TEST_BSX(lzcntw, "w", 0); + TEST_BSX(lzcntw, "w", 0x12340128); + TEST_BSX(popcntw, "w", 0); + TEST_BSX(popcntw, "w", 0x12340128); + TEST_BSX(tzcntl, "k", 0); + TEST_BSX(tzcntl, "k", 0x00340128); + TEST_BSX(lzcntl, "k", 0); + TEST_BSX(lzcntl, "k", 0x00340128); + TEST_BSX(popcntl, "k", 0); + TEST_BSX(popcntl, "k", 0x00340128); +#if defined(__x86_64__) + TEST_BSX(tzcntq, "", 0); + TEST_BSX(tzcntq, "", 0x003401281234); + TEST_BSX(lzcntq, "", 0); + TEST_BSX(lzcntq, "", 0x003401281234); + TEST_BSX(popcntq, "", 0); + TEST_BSX(popcntq, "", 0x003401281234); +#endif +} + void test_bsx(void) { TEST_BSX(bsrw, "w", 0); @@ -2162,6 +2186,7 @@ int main(int argc, char **argv) func(); } test_bsx(); + test_xcnt(); test_mul(); test_jcc(); test_loop(); From 23b1f53c2c8990ed745acede171e49645af3d6d0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 7 May 2024 12:48:22 +0200 Subject: [PATCH 07/27] configure: quote -D options that are passed through to meson Ensure that they go through unmodified, instead of removing one layer of quoting. -D is a pretty specialized option and most options that can have spaces do not need it (for example, c_args is covered by --extra-cflags). Therefore it's unlikely that this causes actual trouble. However, a somewhat realistic failure case would be with -Dpkg_config_path and a pkg-config directory that contains spaces. Cc: qemu-stable@nongnu.org Reviewed-by: Thomas Huth Signed-off-by: Paolo Bonzini --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 1dca3d94c0..330664786d 100755 --- a/configure +++ b/configure @@ -762,7 +762,7 @@ for opt do --*) meson_option_parse "$opt" "$optarg" ;; # Pass through -Dxxxx options to meson - -D*) meson_options="$meson_options $opt" + -D*) meson_option_add "$opt" ;; esac done From d1b223dd07637621f0439701d7d248f87995a275 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 7 May 2024 11:29:31 +0200 Subject: [PATCH 08/27] sh4: select correct components for no-board build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Acked-by: Alex Bennée Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest.yml | 5 +++-- hw/sh4/meson.build | 2 +- target/sh4/Kconfig | 2 ++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 9836d81c24..786c8f9209 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -650,7 +650,7 @@ build-tci: # Check our reduced build configurations # requires libfdt: aarch64, arm, i386, loongarch64, microblaze, microblazeel, # mips64el, or1k, ppc, ppc64, riscv32, riscv64, rx, x86_64 -# does not build without boards: i386, s390x, sh4, sh4eb, x86_64 +# does not build without boards: i386, s390x, x86_64 build-without-defaults: extends: .native_build_job_template needs: @@ -665,7 +665,8 @@ build-without-defaults: --disable-qom-cast-debug --disable-strip TARGETS: alpha-softmmu avr-softmmu cris-softmmu hppa-softmmu m68k-softmmu - mips-softmmu mips64-softmmu mipsel-softmmu sparc-softmmu + mips-softmmu mips64-softmmu mipsel-softmmu + sh4-softmmu sh4eb-softmmu sparc-softmmu sparc64-softmmu tricore-softmmu xtensa-softmmu xtensaeb-softmmu hexagon-linux-user i386-linux-user s390x-linux-user MAKE_CHECK_ARGS: check diff --git a/hw/sh4/meson.build b/hw/sh4/meson.build index 424d5674de..70e814c3a2 100644 --- a/hw/sh4/meson.build +++ b/hw/sh4/meson.build @@ -1,5 +1,5 @@ sh4_ss = ss.source_set() -sh4_ss.add(files( +sh4_ss.add(when: 'CONFIG_SH7750', if_true: files( 'sh7750.c', 'sh7750_regnames.c', )) diff --git a/target/sh4/Kconfig b/target/sh4/Kconfig index 2397c86028..93b92f1e48 100644 --- a/target/sh4/Kconfig +++ b/target/sh4/Kconfig @@ -1,2 +1,4 @@ config SH4 bool + # needed for sh_intc_get_pending_vector + select SH_INTC From ef7c4a97bfdfdb913b03720d3be3c49579cf1835 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 19:00:32 +0200 Subject: [PATCH 09/27] s390x: move s390_cpu_addr2state to target/s390x/sigp.c This function has no dependency on the virtio-ccw machine type, though it assumes that the CPU address corresponds to the core_id and the index. If there is any need of something different or more fancy (unlikely) S390 can include a MachineClass subclass and implement it there. For now, move it to sigp.c for simplicity. Signed-off-by: Paolo Bonzini Reviewed-by: Thomas Huth Message-ID: <20240509170044.190795-2-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- hw/s390x/s390-virtio-ccw.c | 16 ---------------- target/s390x/sigp.c | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 2afaf45ce6..42628fc45d 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -50,22 +50,6 @@ static Error *pv_mig_blocker; -S390CPU *s390_cpu_addr2state(uint16_t cpu_addr) -{ - static MachineState *ms; - - if (!ms) { - ms = MACHINE(qdev_get_machine()); - g_assert(ms->possible_cpus); - } - - /* CPU address corresponds to the core_id and the index */ - if (cpu_addr >= ms->possible_cpus->len) { - return NULL; - } - return S390_CPU(ms->possible_cpus->cpus[cpu_addr].cpu); -} - static S390CPU *s390x_new_cpu(const char *typename, uint32_t core_id, Error **errp) { diff --git a/target/s390x/sigp.c b/target/s390x/sigp.c index 9dd977349a..ad0ad61177 100644 --- a/target/s390x/sigp.c +++ b/target/s390x/sigp.c @@ -11,6 +11,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "s390x-internal.h" +#include "hw/boards.h" #include "sysemu/hw_accel.h" #include "sysemu/runstate.h" #include "exec/address-spaces.h" @@ -435,6 +436,22 @@ static int sigp_set_architecture(S390CPU *cpu, uint32_t param, return SIGP_CC_STATUS_STORED; } +S390CPU *s390_cpu_addr2state(uint16_t cpu_addr) +{ + static MachineState *ms; + + if (!ms) { + ms = MACHINE(qdev_get_machine()); + g_assert(ms->possible_cpus); + } + + /* CPU address corresponds to the core_id and the index */ + if (cpu_addr >= ms->possible_cpus->len) { + return NULL; + } + return S390_CPU(ms->possible_cpus->cpus[cpu_addr].cpu); +} + int handle_sigp(CPUS390XState *env, uint8_t order, uint64_t r1, uint64_t r3) { uint64_t *status_reg = &env->regs[r1]; From 9d1b0f5bf515a0dd8e4174d9f3aca2c549727ef6 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 19:00:33 +0200 Subject: [PATCH 10/27] s390_flic: add migration-enabled property Instead of mucking with css_migration_enabled(), add a property specific to the FLIC device, similar to what is done for TYPE_S390_STATTRIB. Signed-off-by: Paolo Bonzini Reviewed-by: Thomas Huth Message-ID: <20240509170044.190795-3-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- hw/intc/s390_flic.c | 6 +++++- hw/s390x/s390-virtio-ccw.c | 1 + include/hw/s390x/s390_flic.h | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/hw/intc/s390_flic.c b/hw/intc/s390_flic.c index f4a848460b..7f93080087 100644 --- a/hw/intc/s390_flic.c +++ b/hw/intc/s390_flic.c @@ -405,6 +405,8 @@ static void qemu_s390_flic_class_init(ObjectClass *oc, void *data) static Property s390_flic_common_properties[] = { DEFINE_PROP_UINT32("adapter_routes_max_batch", S390FLICState, adapter_routes_max_batch, ADAPTER_ROUTES_MAX_GSI), + DEFINE_PROP_BOOL("migration-enabled", S390FLICState, + migration_enabled, true), DEFINE_PROP_END_OF_LIST(), }; @@ -457,7 +459,9 @@ type_init(qemu_s390_flic_register_types) static bool adapter_info_so_needed(void *opaque) { - return css_migration_enabled(); + S390FLICState *fs = S390_FLIC_COMMON(opaque); + + return fs->migration_enabled; } const VMStateDescription vmstate_adapter_info_so = { diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 42628fc45d..756b4f40ae 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -1182,6 +1182,7 @@ static void ccw_machine_2_9_class_options(MachineClass *mc) S390CcwMachineClass *s390mc = S390_CCW_MACHINE_CLASS(mc); static GlobalProperty compat[] = { { TYPE_S390_STATTRIB, "migration-enabled", "off", }, + { TYPE_S390_FLIC_COMMON, "migration-enabled", "off", }, }; ccw_machine_2_10_class_options(mc); diff --git a/include/hw/s390x/s390_flic.h b/include/hw/s390x/s390_flic.h index 3907a13d07..bcb081def5 100644 --- a/include/hw/s390x/s390_flic.h +++ b/include/hw/s390x/s390_flic.h @@ -47,6 +47,7 @@ struct S390FLICState { /* to limit AdapterRoutes.num_routes for compat */ uint32_t adapter_routes_max_batch; bool ais_supported; + bool migration_enabled; }; From a55ae46683f289c46a0c0a807dbb89229a1e00cd Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 19:00:34 +0200 Subject: [PATCH 11/27] s390: move css_migration_enabled from machine to css.c The CSS subsystem uses global variables, just face the truth and use a variable also for whether the CSS vmstate is in use; remove the indirection of fetching it from the machine type, which makes the TCG code depend unnecessarily on the virtio-ccw machine. Signed-off-by: Paolo Bonzini Message-ID: <20240509170044.190795-4-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- hw/s390x/css.c | 10 +++++++--- hw/s390x/s390-virtio-ccw.c | 15 +++------------ include/hw/s390x/css.h | 6 ++++++ include/hw/s390x/s390-virtio-ccw.h | 7 ------- 4 files changed, 16 insertions(+), 22 deletions(-) diff --git a/hw/s390x/css.c b/hw/s390x/css.c index 295530963a..b2d5327dbf 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -23,6 +23,8 @@ #include "hw/s390x/s390-virtio-ccw.h" #include "hw/s390x/s390-ccw.h" +bool css_migration_enabled = true; + typedef struct CrwContainer { CRW crw; QTAILQ_ENTRY(CrwContainer) sibling; @@ -180,7 +182,7 @@ static const VMStateDescription vmstate_orb = { static bool vmstate_schdev_orb_needed(void *opaque) { - return css_migration_enabled(); + return css_migration_enabled; } static const VMStateDescription vmstate_schdev_orb = { @@ -388,7 +390,7 @@ static int subch_dev_post_load(void *opaque, int version_id) css_subch_assign(s->cssid, s->ssid, s->schid, s->devno, s); } - if (css_migration_enabled()) { + if (css_migration_enabled) { /* No compat voodoo to do ;) */ return 0; } @@ -412,7 +414,9 @@ static int subch_dev_post_load(void *opaque, int version_id) void css_register_vmstate(void) { - vmstate_register(NULL, 0, &vmstate_css, &channel_subsys); + if (css_migration_enabled) { + vmstate_register(NULL, 0, &vmstate_css, &channel_subsys); + } } IndAddr *get_indicator(hwaddr ind_addr, int len) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 756b4f40ae..3d0bc3e7f2 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -283,11 +283,9 @@ static void ccw_init(MachineState *machine) s390_enable_css_support(s390_cpu_addr2state(0)); ret = css_create_css_image(VIRTUAL_CSSID, true); - assert(ret == 0); - if (css_migration_enabled()) { - css_register_vmstate(); - } + + css_register_vmstate(); /* Create VirtIO network adapters */ s390_create_virtio_net(BUS(css_bus), mc->default_nic); @@ -749,7 +747,6 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data) s390mc->ri_allowed = true; s390mc->cpu_model_allowed = true; - s390mc->css_migration_enabled = true; s390mc->hpage_1m_allowed = true; s390mc->max_threads = 1; mc->init = ccw_init; @@ -819,11 +816,6 @@ static const TypeInfo ccw_machine_info = { }, }; -bool css_migration_enabled(void) -{ - return get_machine_class()->css_migration_enabled; -} - #define DEFINE_CCW_MACHINE(suffix, verstr, latest) \ static void ccw_machine_##suffix##_class_init(ObjectClass *oc, \ void *data) \ @@ -1179,7 +1171,6 @@ static void ccw_machine_2_9_instance_options(MachineState *machine) static void ccw_machine_2_9_class_options(MachineClass *mc) { - S390CcwMachineClass *s390mc = S390_CCW_MACHINE_CLASS(mc); static GlobalProperty compat[] = { { TYPE_S390_STATTRIB, "migration-enabled", "off", }, { TYPE_S390_FLIC_COMMON, "migration-enabled", "off", }, @@ -1188,7 +1179,7 @@ static void ccw_machine_2_9_class_options(MachineClass *mc) ccw_machine_2_10_class_options(mc); compat_props_add(mc->compat_props, hw_compat_2_9, hw_compat_2_9_len); compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); - s390mc->css_migration_enabled = false; + css_migration_enabled = false; } DEFINE_CCW_MACHINE(2_9, "2.9", false); diff --git a/include/hw/s390x/css.h b/include/hw/s390x/css.h index ba72ee3dd2..8289e45837 100644 --- a/include/hw/s390x/css.h +++ b/include/hw/s390x/css.h @@ -333,4 +333,10 @@ static inline int ccw_dstream_read_buf(CcwDataStream *cds, void *buff, int len) #define ccw_dstream_read(cds, v) ccw_dstream_read_buf((cds), &(v), sizeof(v)) #define ccw_dstream_write(cds, v) ccw_dstream_write_buf((cds), &(v), sizeof(v)) +/** + * true if (vmstate based) migration of the channel subsystem + * is enabled, false if it is disabled. + */ +extern bool css_migration_enabled; + #endif diff --git a/include/hw/s390x/s390-virtio-ccw.h b/include/hw/s390x/s390-virtio-ccw.h index 7605d06bff..996864a34e 100644 --- a/include/hw/s390x/s390-virtio-ccw.h +++ b/include/hw/s390x/s390-virtio-ccw.h @@ -46,7 +46,6 @@ struct S390CcwMachineClass { /*< public >*/ bool ri_allowed; bool cpu_model_allowed; - bool css_migration_enabled; bool hpage_1m_allowed; int max_threads; }; @@ -58,10 +57,4 @@ bool cpu_model_allowed(void); /* 1M huge page mappings allowed by the machine */ bool hpage_1m_allowed(void); -/** - * Returns true if (vmstate based) migration of the channel subsystem - * is enabled, false if it is disabled. - */ -bool css_migration_enabled(void); - #endif From e799b65faef129f2905bd9bf66c30aaaa7115dac Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 19:00:35 +0200 Subject: [PATCH 12/27] s390x: select correct components for no-board build Signed-off-by: Paolo Bonzini Reviewed-by: Thomas Huth Message-ID: <20240509170044.190795-5-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest.yml | 4 ++-- target/s390x/Kconfig | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 786c8f9209..8ca3e0586c 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -650,7 +650,7 @@ build-tci: # Check our reduced build configurations # requires libfdt: aarch64, arm, i386, loongarch64, microblaze, microblazeel, # mips64el, or1k, ppc, ppc64, riscv32, riscv64, rx, x86_64 -# does not build without boards: i386, s390x, x86_64 +# does not build without boards: i386, x86_64 build-without-defaults: extends: .native_build_job_template needs: @@ -666,7 +666,7 @@ build-without-defaults: --disable-strip TARGETS: alpha-softmmu avr-softmmu cris-softmmu hppa-softmmu m68k-softmmu mips-softmmu mips64-softmmu mipsel-softmmu - sh4-softmmu sh4eb-softmmu sparc-softmmu + s390x-softmmu sh4-softmmu sh4eb-softmmu sparc-softmmu sparc64-softmmu tricore-softmmu xtensa-softmmu xtensaeb-softmmu hexagon-linux-user i386-linux-user s390x-linux-user MAKE_CHECK_ARGS: check diff --git a/target/s390x/Kconfig b/target/s390x/Kconfig index 72da48136c..d886be48b4 100644 --- a/target/s390x/Kconfig +++ b/target/s390x/Kconfig @@ -1,2 +1,4 @@ config S390X bool + select PCI + select S390_FLIC From fa5365e8b6eff888e99f31cd65afefd3643e5d0e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 19:00:36 +0200 Subject: [PATCH 13/27] tests/qtest: s390x: fix operation in a build without any boards or devices Do the bare minimum to ensure that at least a vanilla --without-default-devices build works for all targets except i386, x86_64 and ppc64. In particular this fixes s390x-softmmu; i386 and x86_64 have about a dozen failing tests that do not pass -M and therefore require a default machine type; ppc64 has the same issue, though only with numa-test. If we can for now ignore the cases where boards and devices are picked by hand, drive_del-test however can be fixed easily; almost all tests check for the virtio-blk or virtio-scsi device that they use, and are already skipped. Only one didn't get the memo; plus another one does not need a machine at all and can be run with -M none. Signed-off-by: Paolo Bonzini Reviewed-by: Thomas Huth Message-ID: <20240509170044.190795-6-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- tests/qtest/drive_del-test.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/qtest/drive_del-test.c b/tests/qtest/drive_del-test.c index 8a6f3ac963..7b67a4bbee 100644 --- a/tests/qtest/drive_del-test.c +++ b/tests/qtest/drive_del-test.c @@ -173,7 +173,7 @@ static void test_drive_without_dev(void) QTestState *qts; /* Start with an empty drive */ - qts = qtest_init("-drive if=none,id=drive0"); + qts = qtest_init("-drive if=none,id=drive0 -M none"); /* Delete the drive */ drive_del(qts); @@ -192,6 +192,11 @@ static void test_after_failed_device_add(void) QDict *response; QTestState *qts; + if (!has_device_builtin("virtio-blk")) { + g_test_skip("Device virtio-blk is not available"); + return; + } + snprintf(driver, sizeof(driver), "virtio-blk-%s", qvirtio_get_dev_type()); From 0973996fe47bfca8f0bf552728e36682d642c9cc Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 19:00:37 +0200 Subject: [PATCH 14/27] xen: initialize legacy backends from xen_bus_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prepare for moving the calls to xen_be_register() under the control of xen_bus_init(), using the normal xen_backend_init() method that is used by the "modern" backends. This requires the xenstore global variable to be initialized, which is done by xen_be_init(). To ensure that everything is ready at the time the xen_backend_init() functions are called, remove the xen_be_init() function from all the boards and place it directly in xen_bus_init(). Signed-off-by: Paolo Bonzini Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240509170044.190795-7-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- hw/i386/pc.c | 1 - hw/xen/xen-bus.c | 4 ++++ hw/xen/xen-hvm-common.c | 2 -- hw/xenpv/xen_machine_pv.c | 5 +---- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 505ea750f4..19f21953b4 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1250,7 +1250,6 @@ void pc_basic_device_init(struct PCMachineState *pcms, pci_create_simple(pcms->pcibus, -1, "xen-platform"); } xen_bus_init(); - xen_be_init(); } #endif diff --git a/hw/xen/xen-bus.c b/hw/xen/xen-bus.c index fb82cc33e4..95b207ac8b 100644 --- a/hw/xen/xen-bus.c +++ b/hw/xen/xen-bus.c @@ -13,6 +13,7 @@ #include "hw/sysbus.h" #include "hw/xen/xen.h" #include "hw/xen/xen-backend.h" +#include "hw/xen/xen-legacy-backend.h" /* xen_be_init() */ #include "hw/xen/xen-bus.h" #include "hw/xen/xen-bus-helper.h" #include "monitor/monitor.h" @@ -329,6 +330,9 @@ static void xen_bus_realize(BusState *bus, Error **errp) goto fail; } + /* Initialize legacy backend core & drivers */ + xen_be_init(); + if (xs_node_scanf(xenbus->xsh, XBT_NULL, "", /* domain root node */ "domid", NULL, "%u", &domid) == 1) { xenbus->backend_id = domid; diff --git a/hw/xen/xen-hvm-common.c b/hw/xen/xen-hvm-common.c index 1627da7398..2d1b032121 100644 --- a/hw/xen/xen-hvm-common.c +++ b/hw/xen/xen-hvm-common.c @@ -872,8 +872,6 @@ void xen_register_ioreq(XenIOState *state, unsigned int max_cpus, xen_bus_init(); - xen_be_init(); - return; err: diff --git a/hw/xenpv/xen_machine_pv.c b/hw/xenpv/xen_machine_pv.c index 1130d1a147..b500ce0989 100644 --- a/hw/xenpv/xen_machine_pv.c +++ b/hw/xenpv/xen_machine_pv.c @@ -34,8 +34,7 @@ static void xen_init_pv(MachineState *machine) { setup_xen_backend_ops(); - /* Initialize backend core & drivers */ - xen_be_init(); + xen_bus_init(); switch (xen_mode) { case XEN_ATTACH: @@ -60,8 +59,6 @@ static void xen_init_pv(MachineState *machine) vga_interface_created = true; } - xen_bus_init(); - /* config cleanup hook */ atexit(xen_config_cleanup); } From 88f5ed7017897bc3108463e75e3b4828032a3992 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 19:00:38 +0200 Subject: [PATCH 15/27] xen: register legacy backends via xen_backend_init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is okay to register legacy backends in the middle of xen_bus_init(). All that the registration does is record the existence of the backend in xenstore. This makes it possible to remove them from the build without introducing undefined symbols in xen_be_init(). It also removes the need for the backend_register callback, whose only purpose is to avoid registering nonfunctional backends. Signed-off-by: Paolo Bonzini Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240509170044.190795-8-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- hw/9pfs/xen-9p-backend.c | 8 +++++++- hw/display/xenfb.c | 8 +++++++- hw/usb/xen-usb.c | 14 ++++---------- hw/xen/xen-legacy-backend.c | 16 ---------------- include/hw/xen/xen-legacy-backend.h | 14 ++------------ include/hw/xen/xen_pvdev.h | 1 - 6 files changed, 20 insertions(+), 41 deletions(-) diff --git a/hw/9pfs/xen-9p-backend.c b/hw/9pfs/xen-9p-backend.c index 4aa9c8c736..a3ac53f989 100644 --- a/hw/9pfs/xen-9p-backend.c +++ b/hw/9pfs/xen-9p-backend.c @@ -513,7 +513,7 @@ static void xen_9pfs_alloc(struct XenLegacyDevice *xendev) xenstore_write_be_int(xendev, "max-ring-page-order", MAX_RING_ORDER); } -struct XenDevOps xen_9pfs_ops = { +static struct XenDevOps xen_9pfs_ops = { .size = sizeof(Xen9pfsDev), .flags = DEVOPS_FLAG_NEED_GNTDEV, .alloc = xen_9pfs_alloc, @@ -522,3 +522,9 @@ struct XenDevOps xen_9pfs_ops = { .disconnect = xen_9pfs_disconnect, .free = xen_9pfs_free, }; + +static void xen_9pfs_register_backend(void) +{ + xen_be_register("9pfs", &xen_9pfs_ops); +} +xen_backend_init(xen_9pfs_register_backend); diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c index b2130a0d70..27536bfce0 100644 --- a/hw/display/xenfb.c +++ b/hw/display/xenfb.c @@ -972,7 +972,7 @@ static void fb_event(struct XenLegacyDevice *xendev) /* -------------------------------------------------------------------- */ -struct XenDevOps xen_kbdmouse_ops = { +static struct XenDevOps xen_kbdmouse_ops = { .size = sizeof(struct XenInput), .init = input_init, .initialise = input_initialise, @@ -995,3 +995,9 @@ static const GraphicHwOps xenfb_ops = { .gfx_update = xenfb_update, .ui_info = xenfb_ui_info, }; + +static void xen_vkbd_register_backend(void) +{ + xen_be_register("vkbd", &xen_kbdmouse_ops); +} +xen_backend_init(xen_vkbd_register_backend); diff --git a/hw/usb/xen-usb.c b/hw/usb/xen-usb.c index 09ec326aea..416623f956 100644 --- a/hw/usb/xen-usb.c +++ b/hw/usb/xen-usb.c @@ -1083,7 +1083,7 @@ static void usbback_event(struct XenLegacyDevice *xendev) qemu_bh_schedule(usbif->bh); } -struct XenDevOps xen_usb_ops = { +static struct XenDevOps xen_usb_ops = { .size = sizeof(struct usbback_info), .flags = DEVOPS_FLAG_NEED_GNTDEV, .init = usbback_init, @@ -1095,15 +1095,9 @@ struct XenDevOps xen_usb_ops = { .event = usbback_event, }; -#else /* USBIF_SHORT_NOT_OK */ - -static int usbback_not_supported(void) +static void xen_usb_register_backend(void) { - return -EINVAL; + xen_be_register("qusb", &xen_usb_ops); } - -struct XenDevOps xen_usb_ops = { - .backend_register = usbback_not_supported, -}; - +xen_backend_init(xen_usb_register_backend); #endif diff --git a/hw/xen/xen-legacy-backend.c b/hw/xen/xen-legacy-backend.c index 124dd5f3d6..6f0b300a42 100644 --- a/hw/xen/xen-legacy-backend.c +++ b/hw/xen/xen-legacy-backend.c @@ -622,27 +622,11 @@ void xen_be_init(void) qbus_set_bus_hotplug_handler(xen_sysbus); xen_set_dynamic_sysbus(); - - xen_be_register("vkbd", &xen_kbdmouse_ops); -#ifdef CONFIG_VIRTFS - xen_be_register("9pfs", &xen_9pfs_ops); -#endif -#ifdef CONFIG_USB_LIBUSB - xen_be_register("qusb", &xen_usb_ops); -#endif } int xen_be_register(const char *type, struct XenDevOps *ops) { char path[50]; - int rc; - - if (ops->backend_register) { - rc = ops->backend_register(); - if (rc) { - return rc; - } - } snprintf(path, sizeof(path), "device-model/%u/backends/%s", xen_domid, type); diff --git a/include/hw/xen/xen-legacy-backend.h b/include/hw/xen/xen-legacy-backend.h index 2cca174778..979c4ea04c 100644 --- a/include/hw/xen/xen-legacy-backend.h +++ b/include/hw/xen/xen-legacy-backend.h @@ -66,18 +66,8 @@ static inline void xen_be_unmap_grant_ref(struct XenLegacyDevice *xendev, return xen_be_unmap_grant_refs(xendev, ptr, &ref, 1); } -/* actual backend drivers */ -extern struct XenDevOps xen_console_ops; /* xen_console.c */ -extern struct XenDevOps xen_kbdmouse_ops; /* xen_framebuffer.c */ -extern struct XenDevOps xen_framebuffer_ops; /* xen_framebuffer.c */ -extern struct XenDevOps xen_blkdev_ops; /* xen_disk.c */ -#ifdef CONFIG_VIRTFS -extern struct XenDevOps xen_9pfs_ops; /* xen-9p-backend.c */ -#endif -extern struct XenDevOps xen_netdev_ops; /* xen_nic.c */ -#ifdef CONFIG_USB_LIBUSB -extern struct XenDevOps xen_usb_ops; /* xen-usb.c */ -#endif +/* backend drivers not included in all machines */ +extern struct XenDevOps xen_framebuffer_ops; /* xenfb.c */ /* configuration (aka xenbus setup) */ void xen_config_cleanup(void); diff --git a/include/hw/xen/xen_pvdev.h b/include/hw/xen/xen_pvdev.h index ddad4b9f36..fdf84f47af 100644 --- a/include/hw/xen/xen_pvdev.h +++ b/include/hw/xen/xen_pvdev.h @@ -29,7 +29,6 @@ struct XenDevOps { const char *node); void (*frontend_changed)(struct XenLegacyDevice *xendev, const char *node); - int (*backend_register)(void); }; struct XenLegacyDevice { From 7974e51342775c87f6e759a8c525db1045ddfa24 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 19:00:39 +0200 Subject: [PATCH 16/27] i386: correctly select code in hw/i386 that depends on other components fw_cfg.c and vapic.c are currently included unconditionally but depend on other components. vapic.c depends on the local APIC, while fw_cfg.c includes a piece of AML builder code that depends on CONFIG_ACPI. Signed-off-by: Paolo Bonzini Reviewed-by: Zhao Liu Message-ID: <20240509170044.190795-9-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- hw/i386/fw_cfg.c | 2 ++ hw/i386/meson.build | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c index d802d2787f..6e0d9945d0 100644 --- a/hw/i386/fw_cfg.c +++ b/hw/i386/fw_cfg.c @@ -203,6 +203,7 @@ void fw_cfg_build_feature_control(MachineState *ms, FWCfgState *fw_cfg) fw_cfg_add_file(fw_cfg, "etc/msr_feature_control", val, sizeof(*val)); } +#ifdef CONFIG_ACPI void fw_cfg_add_acpi_dsdt(Aml *scope, FWCfgState *fw_cfg) { /* @@ -229,3 +230,4 @@ void fw_cfg_add_acpi_dsdt(Aml *scope, FWCfgState *fw_cfg) aml_append(dev, aml_name_decl("_CRS", crs)); aml_append(scope, dev); } +#endif diff --git a/hw/i386/meson.build b/hw/i386/meson.build index d8b70ef3e9..d9da676038 100644 --- a/hw/i386/meson.build +++ b/hw/i386/meson.build @@ -1,12 +1,12 @@ i386_ss = ss.source_set() i386_ss.add(files( 'fw_cfg.c', - 'vapic.c', 'e820_memory_layout.c', 'multiboot.c', 'x86.c', )) +i386_ss.add(when: 'CONFIG_APIC', if_true: files('vapic.c')) i386_ss.add(when: 'CONFIG_X86_IOMMU', if_true: files('x86-iommu.c'), if_false: files('x86-iommu-stub.c')) i386_ss.add(when: 'CONFIG_AMD_IOMMU', if_true: files('amd_iommu.c'), From b348fdcdac9f9fc70be9ae56c54e41765e9aae24 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 19:00:40 +0200 Subject: [PATCH 17/27] i386: pc: remove unnecessary MachineClass overrides There is no need to override these fields of MachineClass because they are already set to the right value in the superclass. Signed-off-by: Paolo Bonzini Reviewed-by: Zhao Liu Message-ID: <20240509170044.190795-10-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- hw/i386/pc.c | 3 --- hw/i386/x86.c | 6 +++--- include/hw/i386/x86.h | 4 ---- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 19f21953b4..bfb46e9b54 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1826,9 +1826,6 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) assert(!mc->get_hotplug_handler); mc->get_hotplug_handler = pc_get_hotplug_handler; mc->hotplug_allowed = pc_hotplug_allowed; - mc->cpu_index_to_instance_props = x86_cpu_index_to_props; - mc->get_default_cpu_node_id = x86_get_default_cpu_node_id; - mc->possible_cpu_arch_ids = x86_possible_cpu_arch_ids; mc->auto_enable_numa_with_memhp = true; mc->auto_enable_numa_with_memdev = true; mc->has_hotpluggable_cpus = true; diff --git a/hw/i386/x86.c b/hw/i386/x86.c index c61f4ebfa6..fcef652c1e 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -443,7 +443,7 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, numa_cpu_pre_plug(cpu_slot, dev, errp); } -CpuInstanceProperties +static CpuInstanceProperties x86_cpu_index_to_props(MachineState *ms, unsigned cpu_index) { MachineClass *mc = MACHINE_GET_CLASS(ms); @@ -453,7 +453,7 @@ x86_cpu_index_to_props(MachineState *ms, unsigned cpu_index) return possible_cpus->cpus[cpu_index].props; } -int64_t x86_get_default_cpu_node_id(const MachineState *ms, int idx) +static int64_t x86_get_default_cpu_node_id(const MachineState *ms, int idx) { X86CPUTopoIDs topo_ids; X86MachineState *x86ms = X86_MACHINE(ms); @@ -467,7 +467,7 @@ int64_t x86_get_default_cpu_node_id(const MachineState *ms, int idx) return topo_ids.pkg_id % ms->numa_state->num_nodes; } -const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms) +static const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms) { X86MachineState *x86ms = X86_MACHINE(ms); unsigned int max_cpus = ms->smp.max_cpus; diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h index d7b7d3f3ce..c2062db13f 100644 --- a/include/hw/i386/x86.h +++ b/include/hw/i386/x86.h @@ -114,10 +114,6 @@ uint32_t x86_cpu_apic_id_from_index(X86MachineState *pcms, void x86_cpu_new(X86MachineState *pcms, int64_t apic_id, Error **errp); void x86_cpus_init(X86MachineState *pcms, int default_cpu_version); -CpuInstanceProperties x86_cpu_index_to_props(MachineState *ms, - unsigned cpu_index); -int64_t x86_get_default_cpu_node_id(const MachineState *ms, int idx); -const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms); CPUArchId *x86_find_cpu_slot(MachineState *ms, uint32_t id, int *idx); void x86_rtc_set_cpus_count(ISADevice *rtc, uint16_t cpus_count); void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, From b061f0598b9231f7992aff4fcdf3f336f9747d11 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 19:00:41 +0200 Subject: [PATCH 18/27] hw/i386: split x86.c in multiple parts Keep the basic X86MachineState definition in x86.c. Move out functions that are only needed by other files: x86-common.c for the pc and microvm machines, x86-cpu.c for those used by accelerator code. Signed-off-by: Paolo Bonzini Reviewed-by: Zhao Liu Message-ID: <20240509170044.190795-11-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- hw/i386/meson.build | 4 +- hw/i386/x86-common.c | 1007 +++++++++++++++++++++++++++++++++++++++ hw/i386/x86-cpu.c | 97 ++++ hw/i386/x86.c | 1052 +---------------------------------------- include/hw/i386/x86.h | 6 +- 5 files changed, 1113 insertions(+), 1053 deletions(-) create mode 100644 hw/i386/x86-common.c create mode 100644 hw/i386/x86-cpu.c diff --git a/hw/i386/meson.build b/hw/i386/meson.build index d9da676038..3437da0aad 100644 --- a/hw/i386/meson.build +++ b/hw/i386/meson.build @@ -4,6 +4,7 @@ i386_ss.add(files( 'e820_memory_layout.c', 'multiboot.c', 'x86.c', + 'x86-cpu.c', )) i386_ss.add(when: 'CONFIG_APIC', if_true: files('vapic.c')) @@ -12,7 +13,7 @@ i386_ss.add(when: 'CONFIG_X86_IOMMU', if_true: files('x86-iommu.c'), i386_ss.add(when: 'CONFIG_AMD_IOMMU', if_true: files('amd_iommu.c'), if_false: files('amd_iommu-stub.c')) i386_ss.add(when: 'CONFIG_I440FX', if_true: files('pc_piix.c')) -i386_ss.add(when: 'CONFIG_MICROVM', if_true: files('microvm.c', 'acpi-microvm.c', 'microvm-dt.c')) +i386_ss.add(when: 'CONFIG_MICROVM', if_true: files('x86-common.c', 'microvm.c', 'acpi-microvm.c', 'microvm-dt.c')) i386_ss.add(when: 'CONFIG_Q35', if_true: files('pc_q35.c')) i386_ss.add(when: 'CONFIG_VMMOUSE', if_true: files('vmmouse.c')) i386_ss.add(when: 'CONFIG_VMPORT', if_true: files('vmport.c')) @@ -22,6 +23,7 @@ i386_ss.add(when: 'CONFIG_SGX', if_true: files('sgx-epc.c','sgx.c'), i386_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-common.c')) i386_ss.add(when: 'CONFIG_PC', if_true: files( + 'x86-common.c', 'pc.c', 'pc_sysfw.c', 'acpi-build.c', diff --git a/hw/i386/x86-common.c b/hw/i386/x86-common.c new file mode 100644 index 0000000000..67b03c913a --- /dev/null +++ b/hw/i386/x86-common.c @@ -0,0 +1,1007 @@ +/* + * Copyright (c) 2003-2004 Fabrice Bellard + * Copyright (c) 2019, 2024 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qemu/cutils.h" +#include "qemu/units.h" +#include "qemu/datadir.h" +#include "qapi/error.h" +#include "sysemu/numa.h" +#include "sysemu/sysemu.h" +#include "sysemu/xen.h" +#include "trace.h" + +#include "hw/i386/x86.h" +#include "target/i386/cpu.h" +#include "hw/rtc/mc146818rtc.h" +#include "target/i386/sev.h" + +#include "hw/acpi/cpu_hotplug.h" +#include "hw/irq.h" +#include "hw/loader.h" +#include "multiboot.h" +#include "elf.h" +#include "standard-headers/asm-x86/bootparam.h" +#include CONFIG_DEVICES +#include "kvm/kvm_i386.h" + +#ifdef CONFIG_XEN_EMU +#include "hw/xen/xen.h" +#include "hw/i386/kvm/xen_evtchn.h" +#endif + +/* Physical Address of PVH entry point read from kernel ELF NOTE */ +static size_t pvh_start_addr; + +static void x86_cpu_new(X86MachineState *x86ms, int64_t apic_id, Error **errp) +{ + Object *cpu = object_new(MACHINE(x86ms)->cpu_type); + + if (!object_property_set_uint(cpu, "apic-id", apic_id, errp)) { + goto out; + } + qdev_realize(DEVICE(cpu), NULL, errp); + +out: + object_unref(cpu); +} + +void x86_cpus_init(X86MachineState *x86ms, int default_cpu_version) +{ + int i; + const CPUArchIdList *possible_cpus; + MachineState *ms = MACHINE(x86ms); + MachineClass *mc = MACHINE_GET_CLASS(x86ms); + + x86_cpu_set_default_version(default_cpu_version); + + /* + * Calculates the limit to CPU APIC ID values + * + * Limit for the APIC ID value, so that all + * CPU APIC IDs are < x86ms->apic_id_limit. + * + * This is used for FW_CFG_MAX_CPUS. See comments on fw_cfg_arch_create(). + */ + x86ms->apic_id_limit = x86_cpu_apic_id_from_index(x86ms, + ms->smp.max_cpus - 1) + 1; + + /* + * Can we support APIC ID 255 or higher? With KVM, that requires + * both in-kernel lapic and X2APIC userspace API. + * + * kvm_enabled() must go first to ensure that kvm_* references are + * not emitted for the linker to consume (kvm_enabled() is + * a literal `0` in configurations where kvm_* aren't defined) + */ + if (kvm_enabled() && x86ms->apic_id_limit > 255 && + kvm_irqchip_in_kernel() && !kvm_enable_x2apic()) { + error_report("current -smp configuration requires kernel " + "irqchip and X2APIC API support."); + exit(EXIT_FAILURE); + } + + if (kvm_enabled()) { + kvm_set_max_apic_id(x86ms->apic_id_limit); + } + + if (!kvm_irqchip_in_kernel()) { + apic_set_max_apic_id(x86ms->apic_id_limit); + } + + possible_cpus = mc->possible_cpu_arch_ids(ms); + for (i = 0; i < ms->smp.cpus; i++) { + x86_cpu_new(x86ms, possible_cpus->cpus[i].arch_id, &error_fatal); + } +} + +void x86_rtc_set_cpus_count(ISADevice *s, uint16_t cpus_count) +{ + MC146818RtcState *rtc = MC146818_RTC(s); + + if (cpus_count > 0xff) { + /* + * If the number of CPUs can't be represented in 8 bits, the + * BIOS must use "FW_CFG_NB_CPUS". Set RTC field to 0 just + * to make old BIOSes fail more predictably. + */ + mc146818rtc_set_cmos_data(rtc, 0x5f, 0); + } else { + mc146818rtc_set_cmos_data(rtc, 0x5f, cpus_count - 1); + } +} + +static int x86_apic_cmp(const void *a, const void *b) +{ + CPUArchId *apic_a = (CPUArchId *)a; + CPUArchId *apic_b = (CPUArchId *)b; + + return apic_a->arch_id - apic_b->arch_id; +} + +/* + * returns pointer to CPUArchId descriptor that matches CPU's apic_id + * in ms->possible_cpus->cpus, if ms->possible_cpus->cpus has no + * entry corresponding to CPU's apic_id returns NULL. + */ +static CPUArchId *x86_find_cpu_slot(MachineState *ms, uint32_t id, int *idx) +{ + CPUArchId apic_id, *found_cpu; + + apic_id.arch_id = id; + found_cpu = bsearch(&apic_id, ms->possible_cpus->cpus, + ms->possible_cpus->len, sizeof(*ms->possible_cpus->cpus), + x86_apic_cmp); + if (found_cpu && idx) { + *idx = found_cpu - ms->possible_cpus->cpus; + } + return found_cpu; +} + +void x86_cpu_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + CPUArchId *found_cpu; + Error *local_err = NULL; + X86CPU *cpu = X86_CPU(dev); + X86MachineState *x86ms = X86_MACHINE(hotplug_dev); + + if (x86ms->acpi_dev) { + hotplug_handler_plug(x86ms->acpi_dev, dev, &local_err); + if (local_err) { + goto out; + } + } + + /* increment the number of CPUs */ + x86ms->boot_cpus++; + if (x86ms->rtc) { + x86_rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus); + } + if (x86ms->fw_cfg) { + fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus); + } + + found_cpu = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, NULL); + found_cpu->cpu = CPU(dev); +out: + error_propagate(errp, local_err); +} + +void x86_cpu_unplug_request_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + int idx = -1; + X86CPU *cpu = X86_CPU(dev); + X86MachineState *x86ms = X86_MACHINE(hotplug_dev); + + if (!x86ms->acpi_dev) { + error_setg(errp, "CPU hot unplug not supported without ACPI"); + return; + } + + x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, &idx); + assert(idx != -1); + if (idx == 0) { + error_setg(errp, "Boot CPU is unpluggable"); + return; + } + + hotplug_handler_unplug_request(x86ms->acpi_dev, dev, + errp); +} + +void x86_cpu_unplug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + CPUArchId *found_cpu; + Error *local_err = NULL; + X86CPU *cpu = X86_CPU(dev); + X86MachineState *x86ms = X86_MACHINE(hotplug_dev); + + hotplug_handler_unplug(x86ms->acpi_dev, dev, &local_err); + if (local_err) { + goto out; + } + + found_cpu = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, NULL); + found_cpu->cpu = NULL; + qdev_unrealize(dev); + + /* decrement the number of CPUs */ + x86ms->boot_cpus--; + /* Update the number of CPUs in CMOS */ + x86_rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus); + fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus); + out: + error_propagate(errp, local_err); +} + +void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + int idx; + CPUState *cs; + CPUArchId *cpu_slot; + X86CPUTopoIDs topo_ids; + X86CPU *cpu = X86_CPU(dev); + CPUX86State *env = &cpu->env; + MachineState *ms = MACHINE(hotplug_dev); + X86MachineState *x86ms = X86_MACHINE(hotplug_dev); + unsigned int smp_cores = ms->smp.cores; + unsigned int smp_threads = ms->smp.threads; + X86CPUTopoInfo topo_info; + + if (!object_dynamic_cast(OBJECT(cpu), ms->cpu_type)) { + error_setg(errp, "Invalid CPU type, expected cpu type: '%s'", + ms->cpu_type); + return; + } + + if (x86ms->acpi_dev) { + Error *local_err = NULL; + + hotplug_handler_pre_plug(HOTPLUG_HANDLER(x86ms->acpi_dev), dev, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + } + + init_topo_info(&topo_info, x86ms); + + env->nr_dies = ms->smp.dies; + + /* + * If APIC ID is not set, + * set it based on socket/die/core/thread properties. + */ + if (cpu->apic_id == UNASSIGNED_APIC_ID) { + int max_socket = (ms->smp.max_cpus - 1) / + smp_threads / smp_cores / ms->smp.dies; + + /* + * die-id was optional in QEMU 4.0 and older, so keep it optional + * if there's only one die per socket. + */ + if (cpu->die_id < 0 && ms->smp.dies == 1) { + cpu->die_id = 0; + } + + if (cpu->socket_id < 0) { + error_setg(errp, "CPU socket-id is not set"); + return; + } else if (cpu->socket_id > max_socket) { + error_setg(errp, "Invalid CPU socket-id: %u must be in range 0:%u", + cpu->socket_id, max_socket); + return; + } + if (cpu->die_id < 0) { + error_setg(errp, "CPU die-id is not set"); + return; + } else if (cpu->die_id > ms->smp.dies - 1) { + error_setg(errp, "Invalid CPU die-id: %u must be in range 0:%u", + cpu->die_id, ms->smp.dies - 1); + return; + } + if (cpu->core_id < 0) { + error_setg(errp, "CPU core-id is not set"); + return; + } else if (cpu->core_id > (smp_cores - 1)) { + error_setg(errp, "Invalid CPU core-id: %u must be in range 0:%u", + cpu->core_id, smp_cores - 1); + return; + } + if (cpu->thread_id < 0) { + error_setg(errp, "CPU thread-id is not set"); + return; + } else if (cpu->thread_id > (smp_threads - 1)) { + error_setg(errp, "Invalid CPU thread-id: %u must be in range 0:%u", + cpu->thread_id, smp_threads - 1); + return; + } + + topo_ids.pkg_id = cpu->socket_id; + topo_ids.die_id = cpu->die_id; + topo_ids.core_id = cpu->core_id; + topo_ids.smt_id = cpu->thread_id; + cpu->apic_id = x86_apicid_from_topo_ids(&topo_info, &topo_ids); + } + + cpu_slot = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, &idx); + if (!cpu_slot) { + x86_topo_ids_from_apicid(cpu->apic_id, &topo_info, &topo_ids); + error_setg(errp, + "Invalid CPU [socket: %u, die: %u, core: %u, thread: %u] with" + " APIC ID %" PRIu32 ", valid index range 0:%d", + topo_ids.pkg_id, topo_ids.die_id, topo_ids.core_id, topo_ids.smt_id, + cpu->apic_id, ms->possible_cpus->len - 1); + return; + } + + if (cpu_slot->cpu) { + error_setg(errp, "CPU[%d] with APIC ID %" PRIu32 " exists", + idx, cpu->apic_id); + return; + } + + /* if 'address' properties socket-id/core-id/thread-id are not set, set them + * so that machine_query_hotpluggable_cpus would show correct values + */ + /* TODO: move socket_id/core_id/thread_id checks into x86_cpu_realizefn() + * once -smp refactoring is complete and there will be CPU private + * CPUState::nr_cores and CPUState::nr_threads fields instead of globals */ + x86_topo_ids_from_apicid(cpu->apic_id, &topo_info, &topo_ids); + if (cpu->socket_id != -1 && cpu->socket_id != topo_ids.pkg_id) { + error_setg(errp, "property socket-id: %u doesn't match set apic-id:" + " 0x%x (socket-id: %u)", cpu->socket_id, cpu->apic_id, + topo_ids.pkg_id); + return; + } + cpu->socket_id = topo_ids.pkg_id; + + if (cpu->die_id != -1 && cpu->die_id != topo_ids.die_id) { + error_setg(errp, "property die-id: %u doesn't match set apic-id:" + " 0x%x (die-id: %u)", cpu->die_id, cpu->apic_id, topo_ids.die_id); + return; + } + cpu->die_id = topo_ids.die_id; + + if (cpu->core_id != -1 && cpu->core_id != topo_ids.core_id) { + error_setg(errp, "property core-id: %u doesn't match set apic-id:" + " 0x%x (core-id: %u)", cpu->core_id, cpu->apic_id, + topo_ids.core_id); + return; + } + cpu->core_id = topo_ids.core_id; + + if (cpu->thread_id != -1 && cpu->thread_id != topo_ids.smt_id) { + error_setg(errp, "property thread-id: %u doesn't match set apic-id:" + " 0x%x (thread-id: %u)", cpu->thread_id, cpu->apic_id, + topo_ids.smt_id); + return; + } + cpu->thread_id = topo_ids.smt_id; + + /* + * kvm_enabled() must go first to ensure that kvm_* references are + * not emitted for the linker to consume (kvm_enabled() is + * a literal `0` in configurations where kvm_* aren't defined) + */ + if (kvm_enabled() && hyperv_feat_enabled(cpu, HYPERV_FEAT_VPINDEX) && + !kvm_hv_vpindex_settable()) { + error_setg(errp, "kernel doesn't allow setting HyperV VP_INDEX"); + return; + } + + cs = CPU(cpu); + cs->cpu_index = idx; + + numa_cpu_pre_plug(cpu_slot, dev, errp); +} + +static long get_file_size(FILE *f) +{ + long where, size; + + /* XXX: on Unix systems, using fstat() probably makes more sense */ + + where = ftell(f); + fseek(f, 0, SEEK_END); + size = ftell(f); + fseek(f, where, SEEK_SET); + + return size; +} + +void gsi_handler(void *opaque, int n, int level) +{ + GSIState *s = opaque; + + trace_x86_gsi_interrupt(n, level); + switch (n) { + case 0 ... ISA_NUM_IRQS - 1: + if (s->i8259_irq[n]) { + /* Under KVM, Kernel will forward to both PIC and IOAPIC */ + qemu_set_irq(s->i8259_irq[n], level); + } + /* fall through */ + case ISA_NUM_IRQS ... IOAPIC_NUM_PINS - 1: +#ifdef CONFIG_XEN_EMU + /* + * Xen delivers the GSI to the Legacy PIC (not that Legacy PIC + * routing actually works properly under Xen). And then to + * *either* the PIRQ handling or the I/OAPIC depending on + * whether the former wants it. + */ + if (xen_mode == XEN_EMULATE && xen_evtchn_set_gsi(n, level)) { + break; + } +#endif + qemu_set_irq(s->ioapic_irq[n], level); + break; + case IO_APIC_SECONDARY_IRQBASE + ... IO_APIC_SECONDARY_IRQBASE + IOAPIC_NUM_PINS - 1: + qemu_set_irq(s->ioapic2_irq[n - IO_APIC_SECONDARY_IRQBASE], level); + break; + } +} + +void ioapic_init_gsi(GSIState *gsi_state, Object *parent) +{ + DeviceState *dev; + SysBusDevice *d; + unsigned int i; + + assert(parent); + if (kvm_ioapic_in_kernel()) { + dev = qdev_new(TYPE_KVM_IOAPIC); + } else { + dev = qdev_new(TYPE_IOAPIC); + } + object_property_add_child(parent, "ioapic", OBJECT(dev)); + d = SYS_BUS_DEVICE(dev); + sysbus_realize_and_unref(d, &error_fatal); + sysbus_mmio_map(d, 0, IO_APIC_DEFAULT_ADDRESS); + + for (i = 0; i < IOAPIC_NUM_PINS; i++) { + gsi_state->ioapic_irq[i] = qdev_get_gpio_in(dev, i); + } +} + +DeviceState *ioapic_init_secondary(GSIState *gsi_state) +{ + DeviceState *dev; + SysBusDevice *d; + unsigned int i; + + dev = qdev_new(TYPE_IOAPIC); + d = SYS_BUS_DEVICE(dev); + sysbus_realize_and_unref(d, &error_fatal); + sysbus_mmio_map(d, 0, IO_APIC_SECONDARY_ADDRESS); + + for (i = 0; i < IOAPIC_NUM_PINS; i++) { + gsi_state->ioapic2_irq[i] = qdev_get_gpio_in(dev, i); + } + return dev; +} + +/* + * The entry point into the kernel for PVH boot is different from + * the native entry point. The PVH entry is defined by the x86/HVM + * direct boot ABI and is available in an ELFNOTE in the kernel binary. + * + * This function is passed to load_elf() when it is called from + * load_elfboot() which then additionally checks for an ELF Note of + * type XEN_ELFNOTE_PHYS32_ENTRY and passes it to this function to + * parse the PVH entry address from the ELF Note. + * + * Due to trickery in elf_opts.h, load_elf() is actually available as + * load_elf32() or load_elf64() and this routine needs to be able + * to deal with being called as 32 or 64 bit. + * + * The address of the PVH entry point is saved to the 'pvh_start_addr' + * global variable. (although the entry point is 32-bit, the kernel + * binary can be either 32-bit or 64-bit). + */ +static uint64_t read_pvh_start_addr(void *arg1, void *arg2, bool is64) +{ + size_t *elf_note_data_addr; + + /* Check if ELF Note header passed in is valid */ + if (arg1 == NULL) { + return 0; + } + + if (is64) { + struct elf64_note *nhdr64 = (struct elf64_note *)arg1; + uint64_t nhdr_size64 = sizeof(struct elf64_note); + uint64_t phdr_align = *(uint64_t *)arg2; + uint64_t nhdr_namesz = nhdr64->n_namesz; + + elf_note_data_addr = + ((void *)nhdr64) + nhdr_size64 + + QEMU_ALIGN_UP(nhdr_namesz, phdr_align); + + pvh_start_addr = *elf_note_data_addr; + } else { + struct elf32_note *nhdr32 = (struct elf32_note *)arg1; + uint32_t nhdr_size32 = sizeof(struct elf32_note); + uint32_t phdr_align = *(uint32_t *)arg2; + uint32_t nhdr_namesz = nhdr32->n_namesz; + + elf_note_data_addr = + ((void *)nhdr32) + nhdr_size32 + + QEMU_ALIGN_UP(nhdr_namesz, phdr_align); + + pvh_start_addr = *(uint32_t *)elf_note_data_addr; + } + + return pvh_start_addr; +} + +static bool load_elfboot(const char *kernel_filename, + int kernel_file_size, + uint8_t *header, + size_t pvh_xen_start_addr, + FWCfgState *fw_cfg) +{ + uint32_t flags = 0; + uint32_t mh_load_addr = 0; + uint32_t elf_kernel_size = 0; + uint64_t elf_entry; + uint64_t elf_low, elf_high; + int kernel_size; + + if (ldl_p(header) != 0x464c457f) { + return false; /* no elfboot */ + } + + bool elf_is64 = header[EI_CLASS] == ELFCLASS64; + flags = elf_is64 ? + ((Elf64_Ehdr *)header)->e_flags : ((Elf32_Ehdr *)header)->e_flags; + + if (flags & 0x00010004) { /* LOAD_ELF_HEADER_HAS_ADDR */ + error_report("elfboot unsupported flags = %x", flags); + exit(1); + } + + uint64_t elf_note_type = XEN_ELFNOTE_PHYS32_ENTRY; + kernel_size = load_elf(kernel_filename, read_pvh_start_addr, + NULL, &elf_note_type, &elf_entry, + &elf_low, &elf_high, NULL, 0, I386_ELF_MACHINE, + 0, 0); + + if (kernel_size < 0) { + error_report("Error while loading elf kernel"); + exit(1); + } + mh_load_addr = elf_low; + elf_kernel_size = elf_high - elf_low; + + if (pvh_start_addr == 0) { + error_report("Error loading uncompressed kernel without PVH ELF Note"); + exit(1); + } + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ENTRY, pvh_start_addr); + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, mh_load_addr); + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, elf_kernel_size); + + return true; +} + +void x86_load_linux(X86MachineState *x86ms, + FWCfgState *fw_cfg, + int acpi_data_size, + bool pvh_enabled) +{ + bool linuxboot_dma_enabled = X86_MACHINE_GET_CLASS(x86ms)->fwcfg_dma_enabled; + uint16_t protocol; + int setup_size, kernel_size, cmdline_size; + int dtb_size, setup_data_offset; + uint32_t initrd_max; + uint8_t header[8192], *setup, *kernel; + hwaddr real_addr, prot_addr, cmdline_addr, initrd_addr = 0; + FILE *f; + char *vmode; + MachineState *machine = MACHINE(x86ms); + struct setup_data *setup_data; + const char *kernel_filename = machine->kernel_filename; + const char *initrd_filename = machine->initrd_filename; + const char *dtb_filename = machine->dtb; + const char *kernel_cmdline = machine->kernel_cmdline; + SevKernelLoaderContext sev_load_ctx = {}; + + /* Align to 16 bytes as a paranoia measure */ + cmdline_size = (strlen(kernel_cmdline) + 16) & ~15; + + /* load the kernel header */ + f = fopen(kernel_filename, "rb"); + if (!f) { + fprintf(stderr, "qemu: could not open kernel file '%s': %s\n", + kernel_filename, strerror(errno)); + exit(1); + } + + kernel_size = get_file_size(f); + if (!kernel_size || + fread(header, 1, MIN(ARRAY_SIZE(header), kernel_size), f) != + MIN(ARRAY_SIZE(header), kernel_size)) { + fprintf(stderr, "qemu: could not load kernel '%s': %s\n", + kernel_filename, strerror(errno)); + exit(1); + } + + /* kernel protocol version */ + if (ldl_p(header + 0x202) == 0x53726448) { + protocol = lduw_p(header + 0x206); + } else { + /* + * This could be a multiboot kernel. If it is, let's stop treating it + * like a Linux kernel. + * Note: some multiboot images could be in the ELF format (the same of + * PVH), so we try multiboot first since we check the multiboot magic + * header before to load it. + */ + if (load_multiboot(x86ms, fw_cfg, f, kernel_filename, initrd_filename, + kernel_cmdline, kernel_size, header)) { + return; + } + /* + * Check if the file is an uncompressed kernel file (ELF) and load it, + * saving the PVH entry point used by the x86/HVM direct boot ABI. + * If load_elfboot() is successful, populate the fw_cfg info. + */ + if (pvh_enabled && + load_elfboot(kernel_filename, kernel_size, + header, pvh_start_addr, fw_cfg)) { + fclose(f); + + fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, + strlen(kernel_cmdline) + 1); + fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline); + + fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, sizeof(header)); + fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA, + header, sizeof(header)); + + /* load initrd */ + if (initrd_filename) { + GMappedFile *mapped_file; + gsize initrd_size; + gchar *initrd_data; + GError *gerr = NULL; + + mapped_file = g_mapped_file_new(initrd_filename, false, &gerr); + if (!mapped_file) { + fprintf(stderr, "qemu: error reading initrd %s: %s\n", + initrd_filename, gerr->message); + exit(1); + } + x86ms->initrd_mapped_file = mapped_file; + + initrd_data = g_mapped_file_get_contents(mapped_file); + initrd_size = g_mapped_file_get_length(mapped_file); + initrd_max = x86ms->below_4g_mem_size - acpi_data_size - 1; + if (initrd_size >= initrd_max) { + fprintf(stderr, "qemu: initrd is too large, cannot support." + "(max: %"PRIu32", need %"PRId64")\n", + initrd_max, (uint64_t)initrd_size); + exit(1); + } + + initrd_addr = (initrd_max - initrd_size) & ~4095; + + fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_addr); + fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size); + fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, initrd_data, + initrd_size); + } + + option_rom[nb_option_roms].bootindex = 0; + option_rom[nb_option_roms].name = "pvh.bin"; + nb_option_roms++; + + return; + } + protocol = 0; + } + + if (protocol < 0x200 || !(header[0x211] & 0x01)) { + /* Low kernel */ + real_addr = 0x90000; + cmdline_addr = 0x9a000 - cmdline_size; + prot_addr = 0x10000; + } else if (protocol < 0x202) { + /* High but ancient kernel */ + real_addr = 0x90000; + cmdline_addr = 0x9a000 - cmdline_size; + prot_addr = 0x100000; + } else { + /* High and recent kernel */ + real_addr = 0x10000; + cmdline_addr = 0x20000; + prot_addr = 0x100000; + } + + /* highest address for loading the initrd */ + if (protocol >= 0x20c && + lduw_p(header + 0x236) & XLF_CAN_BE_LOADED_ABOVE_4G) { + /* + * Linux has supported initrd up to 4 GB for a very long time (2007, + * long before XLF_CAN_BE_LOADED_ABOVE_4G which was added in 2013), + * though it only sets initrd_max to 2 GB to "work around bootloader + * bugs". Luckily, QEMU firmware(which does something like bootloader) + * has supported this. + * + * It's believed that if XLF_CAN_BE_LOADED_ABOVE_4G is set, initrd can + * be loaded into any address. + * + * In addition, initrd_max is uint32_t simply because QEMU doesn't + * support the 64-bit boot protocol (specifically the ext_ramdisk_image + * field). + * + * Therefore here just limit initrd_max to UINT32_MAX simply as well. + */ + initrd_max = UINT32_MAX; + } else if (protocol >= 0x203) { + initrd_max = ldl_p(header + 0x22c); + } else { + initrd_max = 0x37ffffff; + } + + if (initrd_max >= x86ms->below_4g_mem_size - acpi_data_size) { + initrd_max = x86ms->below_4g_mem_size - acpi_data_size - 1; + } + + fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_ADDR, cmdline_addr); + fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, strlen(kernel_cmdline) + 1); + fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline); + sev_load_ctx.cmdline_data = (char *)kernel_cmdline; + sev_load_ctx.cmdline_size = strlen(kernel_cmdline) + 1; + + if (protocol >= 0x202) { + stl_p(header + 0x228, cmdline_addr); + } else { + stw_p(header + 0x20, 0xA33F); + stw_p(header + 0x22, cmdline_addr - real_addr); + } + + /* handle vga= parameter */ + vmode = strstr(kernel_cmdline, "vga="); + if (vmode) { + unsigned int video_mode; + const char *end; + int ret; + /* skip "vga=" */ + vmode += 4; + if (!strncmp(vmode, "normal", 6)) { + video_mode = 0xffff; + } else if (!strncmp(vmode, "ext", 3)) { + video_mode = 0xfffe; + } else if (!strncmp(vmode, "ask", 3)) { + video_mode = 0xfffd; + } else { + ret = qemu_strtoui(vmode, &end, 0, &video_mode); + if (ret != 0 || (*end && *end != ' ')) { + fprintf(stderr, "qemu: invalid 'vga=' kernel parameter.\n"); + exit(1); + } + } + stw_p(header + 0x1fa, video_mode); + } + + /* loader type */ + /* + * High nybble = B reserved for QEMU; low nybble is revision number. + * If this code is substantially changed, you may want to consider + * incrementing the revision. + */ + if (protocol >= 0x200) { + header[0x210] = 0xB0; + } + /* heap */ + if (protocol >= 0x201) { + header[0x211] |= 0x80; /* CAN_USE_HEAP */ + stw_p(header + 0x224, cmdline_addr - real_addr - 0x200); + } + + /* load initrd */ + if (initrd_filename) { + GMappedFile *mapped_file; + gsize initrd_size; + gchar *initrd_data; + GError *gerr = NULL; + + if (protocol < 0x200) { + fprintf(stderr, "qemu: linux kernel too old to load a ram disk\n"); + exit(1); + } + + mapped_file = g_mapped_file_new(initrd_filename, false, &gerr); + if (!mapped_file) { + fprintf(stderr, "qemu: error reading initrd %s: %s\n", + initrd_filename, gerr->message); + exit(1); + } + x86ms->initrd_mapped_file = mapped_file; + + initrd_data = g_mapped_file_get_contents(mapped_file); + initrd_size = g_mapped_file_get_length(mapped_file); + if (initrd_size >= initrd_max) { + fprintf(stderr, "qemu: initrd is too large, cannot support." + "(max: %"PRIu32", need %"PRId64")\n", + initrd_max, (uint64_t)initrd_size); + exit(1); + } + + initrd_addr = (initrd_max - initrd_size) & ~4095; + + fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_addr); + fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size); + fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, initrd_data, initrd_size); + sev_load_ctx.initrd_data = initrd_data; + sev_load_ctx.initrd_size = initrd_size; + + stl_p(header + 0x218, initrd_addr); + stl_p(header + 0x21c, initrd_size); + } + + /* load kernel and setup */ + setup_size = header[0x1f1]; + if (setup_size == 0) { + setup_size = 4; + } + setup_size = (setup_size + 1) * 512; + if (setup_size > kernel_size) { + fprintf(stderr, "qemu: invalid kernel header\n"); + exit(1); + } + kernel_size -= setup_size; + + setup = g_malloc(setup_size); + kernel = g_malloc(kernel_size); + fseek(f, 0, SEEK_SET); + if (fread(setup, 1, setup_size, f) != setup_size) { + fprintf(stderr, "fread() failed\n"); + exit(1); + } + if (fread(kernel, 1, kernel_size, f) != kernel_size) { + fprintf(stderr, "fread() failed\n"); + exit(1); + } + fclose(f); + + /* append dtb to kernel */ + if (dtb_filename) { + if (protocol < 0x209) { + fprintf(stderr, "qemu: Linux kernel too old to load a dtb\n"); + exit(1); + } + + dtb_size = get_image_size(dtb_filename); + if (dtb_size <= 0) { + fprintf(stderr, "qemu: error reading dtb %s: %s\n", + dtb_filename, strerror(errno)); + exit(1); + } + + setup_data_offset = QEMU_ALIGN_UP(kernel_size, 16); + kernel_size = setup_data_offset + sizeof(struct setup_data) + dtb_size; + kernel = g_realloc(kernel, kernel_size); + + stq_p(header + 0x250, prot_addr + setup_data_offset); + + setup_data = (struct setup_data *)(kernel + setup_data_offset); + setup_data->next = 0; + setup_data->type = cpu_to_le32(SETUP_DTB); + setup_data->len = cpu_to_le32(dtb_size); + + load_image_size(dtb_filename, setup_data->data, dtb_size); + } + + /* + * If we're starting an encrypted VM, it will be OVMF based, which uses the + * efi stub for booting and doesn't require any values to be placed in the + * kernel header. We therefore don't update the header so the hash of the + * kernel on the other side of the fw_cfg interface matches the hash of the + * file the user passed in. + */ + if (!sev_enabled()) { + memcpy(setup, header, MIN(sizeof(header), setup_size)); + } + + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, prot_addr); + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size); + fw_cfg_add_bytes(fw_cfg, FW_CFG_KERNEL_DATA, kernel, kernel_size); + sev_load_ctx.kernel_data = (char *)kernel; + sev_load_ctx.kernel_size = kernel_size; + + fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_ADDR, real_addr); + fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, setup_size); + fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA, setup, setup_size); + sev_load_ctx.setup_data = (char *)setup; + sev_load_ctx.setup_size = setup_size; + + if (sev_enabled()) { + sev_add_kernel_loader_hashes(&sev_load_ctx, &error_fatal); + } + + option_rom[nb_option_roms].bootindex = 0; + option_rom[nb_option_roms].name = "linuxboot.bin"; + if (linuxboot_dma_enabled && fw_cfg_dma_enabled(fw_cfg)) { + option_rom[nb_option_roms].name = "linuxboot_dma.bin"; + } + nb_option_roms++; +} + +void x86_isa_bios_init(MemoryRegion *isa_bios, MemoryRegion *isa_memory, + MemoryRegion *bios, bool read_only) +{ + uint64_t bios_size = memory_region_size(bios); + uint64_t isa_bios_size = MIN(bios_size, 128 * KiB); + + memory_region_init_alias(isa_bios, NULL, "isa-bios", bios, + bios_size - isa_bios_size, isa_bios_size); + memory_region_add_subregion_overlap(isa_memory, 1 * MiB - isa_bios_size, + isa_bios, 1); + memory_region_set_readonly(isa_bios, read_only); +} + +void x86_bios_rom_init(X86MachineState *x86ms, const char *default_firmware, + MemoryRegion *rom_memory, bool isapc_ram_fw) +{ + const char *bios_name; + char *filename; + int bios_size; + ssize_t ret; + + /* BIOS load */ + bios_name = MACHINE(x86ms)->firmware ?: default_firmware; + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + if (filename) { + bios_size = get_image_size(filename); + } else { + bios_size = -1; + } + if (bios_size <= 0 || + (bios_size % 65536) != 0) { + goto bios_error; + } + memory_region_init_ram(&x86ms->bios, NULL, "pc.bios", bios_size, + &error_fatal); + if (sev_enabled()) { + /* + * The concept of a "reset" simply doesn't exist for + * confidential computing guests, we have to destroy and + * re-launch them instead. So there is no need to register + * the firmware as rom to properly re-initialize on reset. + * Just go for a straight file load instead. + */ + void *ptr = memory_region_get_ram_ptr(&x86ms->bios); + load_image_size(filename, ptr, bios_size); + x86_firmware_configure(ptr, bios_size); + } else { + memory_region_set_readonly(&x86ms->bios, !isapc_ram_fw); + ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size), -1); + if (ret != 0) { + goto bios_error; + } + } + g_free(filename); + + /* map the last 128KB of the BIOS in ISA space */ + x86_isa_bios_init(&x86ms->isa_bios, rom_memory, &x86ms->bios, + !isapc_ram_fw); + + /* map all the bios at the top of memory */ + memory_region_add_subregion(rom_memory, + (uint32_t)(-bios_size), + &x86ms->bios); + return; + +bios_error: + fprintf(stderr, "qemu: could not load PC BIOS '%s'\n", bios_name); + exit(1); +} diff --git a/hw/i386/x86-cpu.c b/hw/i386/x86-cpu.c new file mode 100644 index 0000000000..ab2920522d --- /dev/null +++ b/hw/i386/x86-cpu.c @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2003-2004 Fabrice Bellard + * Copyright (c) 2019, 2024 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu/osdep.h" +#include "sysemu/whpx.h" +#include "sysemu/cpu-timers.h" +#include "trace.h" + +#include "hw/i386/x86.h" +#include "target/i386/cpu.h" +#include "hw/intc/i8259.h" +#include "hw/irq.h" +#include "sysemu/kvm.h" + +/* TSC handling */ +uint64_t cpu_get_tsc(CPUX86State *env) +{ + return cpus_get_elapsed_ticks(); +} + +/* IRQ handling */ +static void pic_irq_request(void *opaque, int irq, int level) +{ + CPUState *cs = first_cpu; + X86CPU *cpu = X86_CPU(cs); + + trace_x86_pic_interrupt(irq, level); + if (cpu_is_apic_enabled(cpu->apic_state) && !kvm_irqchip_in_kernel() && + !whpx_apic_in_platform()) { + CPU_FOREACH(cs) { + cpu = X86_CPU(cs); + if (apic_accept_pic_intr(cpu->apic_state)) { + apic_deliver_pic_intr(cpu->apic_state, level); + } + } + } else { + if (level) { + cpu_interrupt(cs, CPU_INTERRUPT_HARD); + } else { + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); + } + } +} + +qemu_irq x86_allocate_cpu_irq(void) +{ + return qemu_allocate_irq(pic_irq_request, NULL, 0); +} + +int cpu_get_pic_interrupt(CPUX86State *env) +{ + X86CPU *cpu = env_archcpu(env); + int intno; + + if (!kvm_irqchip_in_kernel() && !whpx_apic_in_platform()) { + intno = apic_get_interrupt(cpu->apic_state); + if (intno >= 0) { + return intno; + } + /* read the irq from the PIC */ + if (!apic_accept_pic_intr(cpu->apic_state)) { + return -1; + } + } + + intno = pic_read_irq(isa_pic); + return intno; +} + +DeviceState *cpu_get_current_apic(void) +{ + if (current_cpu) { + X86CPU *cpu = X86_CPU(current_cpu); + return cpu->apic_state; + } else { + return NULL; + } +} diff --git a/hw/i386/x86.c b/hw/i386/x86.c index fcef652c1e..0b5cc59956 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -22,52 +22,25 @@ */ #include "qemu/osdep.h" #include "qemu/error-report.h" -#include "qemu/option.h" -#include "qemu/cutils.h" #include "qemu/units.h" -#include "qemu/datadir.h" #include "qapi/error.h" #include "qapi/qapi-visit-common.h" -#include "qapi/clone-visitor.h" #include "qapi/qapi-visit-machine.h" #include "qapi/visitor.h" #include "sysemu/qtest.h" -#include "sysemu/whpx.h" #include "sysemu/numa.h" -#include "sysemu/replay.h" -#include "sysemu/sysemu.h" -#include "sysemu/cpu-timers.h" -#include "sysemu/xen.h" #include "trace.h" +#include "hw/acpi/aml-build.h" #include "hw/i386/x86.h" -#include "target/i386/cpu.h" #include "hw/i386/topology.h" -#include "hw/i386/fw_cfg.h" -#include "hw/intc/i8259.h" -#include "hw/rtc/mc146818rtc.h" -#include "target/i386/sev.h" -#include "hw/acpi/cpu_hotplug.h" -#include "hw/irq.h" #include "hw/nmi.h" -#include "hw/loader.h" -#include "multiboot.h" -#include "elf.h" -#include "standard-headers/asm-x86/bootparam.h" -#include CONFIG_DEVICES #include "kvm/kvm_i386.h" -#ifdef CONFIG_XEN_EMU -#include "hw/xen/xen.h" -#include "hw/i386/kvm/xen_evtchn.h" -#endif -/* Physical Address of PVH entry point read from kernel ELF NOTE */ -static size_t pvh_start_addr; - -static void init_topo_info(X86CPUTopoInfo *topo_info, - const X86MachineState *x86ms) +void init_topo_info(X86CPUTopoInfo *topo_info, + const X86MachineState *x86ms) { MachineState *ms = MACHINE(x86ms); @@ -94,355 +67,6 @@ uint32_t x86_cpu_apic_id_from_index(X86MachineState *x86ms, return x86_apicid_from_cpu_idx(&topo_info, cpu_index); } - -void x86_cpu_new(X86MachineState *x86ms, int64_t apic_id, Error **errp) -{ - Object *cpu = object_new(MACHINE(x86ms)->cpu_type); - - if (!object_property_set_uint(cpu, "apic-id", apic_id, errp)) { - goto out; - } - qdev_realize(DEVICE(cpu), NULL, errp); - -out: - object_unref(cpu); -} - -void x86_cpus_init(X86MachineState *x86ms, int default_cpu_version) -{ - int i; - const CPUArchIdList *possible_cpus; - MachineState *ms = MACHINE(x86ms); - MachineClass *mc = MACHINE_GET_CLASS(x86ms); - - x86_cpu_set_default_version(default_cpu_version); - - /* - * Calculates the limit to CPU APIC ID values - * - * Limit for the APIC ID value, so that all - * CPU APIC IDs are < x86ms->apic_id_limit. - * - * This is used for FW_CFG_MAX_CPUS. See comments on fw_cfg_arch_create(). - */ - x86ms->apic_id_limit = x86_cpu_apic_id_from_index(x86ms, - ms->smp.max_cpus - 1) + 1; - - /* - * Can we support APIC ID 255 or higher? With KVM, that requires - * both in-kernel lapic and X2APIC userspace API. - * - * kvm_enabled() must go first to ensure that kvm_* references are - * not emitted for the linker to consume (kvm_enabled() is - * a literal `0` in configurations where kvm_* aren't defined) - */ - if (kvm_enabled() && x86ms->apic_id_limit > 255 && - kvm_irqchip_in_kernel() && !kvm_enable_x2apic()) { - error_report("current -smp configuration requires kernel " - "irqchip and X2APIC API support."); - exit(EXIT_FAILURE); - } - - if (kvm_enabled()) { - kvm_set_max_apic_id(x86ms->apic_id_limit); - } - - if (!kvm_irqchip_in_kernel()) { - apic_set_max_apic_id(x86ms->apic_id_limit); - } - - possible_cpus = mc->possible_cpu_arch_ids(ms); - for (i = 0; i < ms->smp.cpus; i++) { - x86_cpu_new(x86ms, possible_cpus->cpus[i].arch_id, &error_fatal); - } -} - -void x86_rtc_set_cpus_count(ISADevice *s, uint16_t cpus_count) -{ - MC146818RtcState *rtc = MC146818_RTC(s); - - if (cpus_count > 0xff) { - /* - * If the number of CPUs can't be represented in 8 bits, the - * BIOS must use "FW_CFG_NB_CPUS". Set RTC field to 0 just - * to make old BIOSes fail more predictably. - */ - mc146818rtc_set_cmos_data(rtc, 0x5f, 0); - } else { - mc146818rtc_set_cmos_data(rtc, 0x5f, cpus_count - 1); - } -} - -static int x86_apic_cmp(const void *a, const void *b) -{ - CPUArchId *apic_a = (CPUArchId *)a; - CPUArchId *apic_b = (CPUArchId *)b; - - return apic_a->arch_id - apic_b->arch_id; -} - -/* - * returns pointer to CPUArchId descriptor that matches CPU's apic_id - * in ms->possible_cpus->cpus, if ms->possible_cpus->cpus has no - * entry corresponding to CPU's apic_id returns NULL. - */ -CPUArchId *x86_find_cpu_slot(MachineState *ms, uint32_t id, int *idx) -{ - CPUArchId apic_id, *found_cpu; - - apic_id.arch_id = id; - found_cpu = bsearch(&apic_id, ms->possible_cpus->cpus, - ms->possible_cpus->len, sizeof(*ms->possible_cpus->cpus), - x86_apic_cmp); - if (found_cpu && idx) { - *idx = found_cpu - ms->possible_cpus->cpus; - } - return found_cpu; -} - -void x86_cpu_plug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - CPUArchId *found_cpu; - Error *local_err = NULL; - X86CPU *cpu = X86_CPU(dev); - X86MachineState *x86ms = X86_MACHINE(hotplug_dev); - - if (x86ms->acpi_dev) { - hotplug_handler_plug(x86ms->acpi_dev, dev, &local_err); - if (local_err) { - goto out; - } - } - - /* increment the number of CPUs */ - x86ms->boot_cpus++; - if (x86ms->rtc) { - x86_rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus); - } - if (x86ms->fw_cfg) { - fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus); - } - - found_cpu = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, NULL); - found_cpu->cpu = CPU(dev); -out: - error_propagate(errp, local_err); -} - -void x86_cpu_unplug_request_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - int idx = -1; - X86CPU *cpu = X86_CPU(dev); - X86MachineState *x86ms = X86_MACHINE(hotplug_dev); - - if (!x86ms->acpi_dev) { - error_setg(errp, "CPU hot unplug not supported without ACPI"); - return; - } - - x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, &idx); - assert(idx != -1); - if (idx == 0) { - error_setg(errp, "Boot CPU is unpluggable"); - return; - } - - hotplug_handler_unplug_request(x86ms->acpi_dev, dev, - errp); -} - -void x86_cpu_unplug_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - CPUArchId *found_cpu; - Error *local_err = NULL; - X86CPU *cpu = X86_CPU(dev); - X86MachineState *x86ms = X86_MACHINE(hotplug_dev); - - hotplug_handler_unplug(x86ms->acpi_dev, dev, &local_err); - if (local_err) { - goto out; - } - - found_cpu = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, NULL); - found_cpu->cpu = NULL; - qdev_unrealize(dev); - - /* decrement the number of CPUs */ - x86ms->boot_cpus--; - /* Update the number of CPUs in CMOS */ - x86_rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus); - fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus); - out: - error_propagate(errp, local_err); -} - -void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - int idx; - CPUState *cs; - CPUArchId *cpu_slot; - X86CPUTopoIDs topo_ids; - X86CPU *cpu = X86_CPU(dev); - CPUX86State *env = &cpu->env; - MachineState *ms = MACHINE(hotplug_dev); - X86MachineState *x86ms = X86_MACHINE(hotplug_dev); - unsigned int smp_cores = ms->smp.cores; - unsigned int smp_threads = ms->smp.threads; - X86CPUTopoInfo topo_info; - - if (!object_dynamic_cast(OBJECT(cpu), ms->cpu_type)) { - error_setg(errp, "Invalid CPU type, expected cpu type: '%s'", - ms->cpu_type); - return; - } - - if (x86ms->acpi_dev) { - Error *local_err = NULL; - - hotplug_handler_pre_plug(HOTPLUG_HANDLER(x86ms->acpi_dev), dev, - &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - } - - init_topo_info(&topo_info, x86ms); - - env->nr_dies = ms->smp.dies; - - /* - * If APIC ID is not set, - * set it based on socket/die/core/thread properties. - */ - if (cpu->apic_id == UNASSIGNED_APIC_ID) { - int max_socket = (ms->smp.max_cpus - 1) / - smp_threads / smp_cores / ms->smp.dies; - - /* - * die-id was optional in QEMU 4.0 and older, so keep it optional - * if there's only one die per socket. - */ - if (cpu->die_id < 0 && ms->smp.dies == 1) { - cpu->die_id = 0; - } - - if (cpu->socket_id < 0) { - error_setg(errp, "CPU socket-id is not set"); - return; - } else if (cpu->socket_id > max_socket) { - error_setg(errp, "Invalid CPU socket-id: %u must be in range 0:%u", - cpu->socket_id, max_socket); - return; - } - if (cpu->die_id < 0) { - error_setg(errp, "CPU die-id is not set"); - return; - } else if (cpu->die_id > ms->smp.dies - 1) { - error_setg(errp, "Invalid CPU die-id: %u must be in range 0:%u", - cpu->die_id, ms->smp.dies - 1); - return; - } - if (cpu->core_id < 0) { - error_setg(errp, "CPU core-id is not set"); - return; - } else if (cpu->core_id > (smp_cores - 1)) { - error_setg(errp, "Invalid CPU core-id: %u must be in range 0:%u", - cpu->core_id, smp_cores - 1); - return; - } - if (cpu->thread_id < 0) { - error_setg(errp, "CPU thread-id is not set"); - return; - } else if (cpu->thread_id > (smp_threads - 1)) { - error_setg(errp, "Invalid CPU thread-id: %u must be in range 0:%u", - cpu->thread_id, smp_threads - 1); - return; - } - - topo_ids.pkg_id = cpu->socket_id; - topo_ids.die_id = cpu->die_id; - topo_ids.core_id = cpu->core_id; - topo_ids.smt_id = cpu->thread_id; - cpu->apic_id = x86_apicid_from_topo_ids(&topo_info, &topo_ids); - } - - cpu_slot = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, &idx); - if (!cpu_slot) { - x86_topo_ids_from_apicid(cpu->apic_id, &topo_info, &topo_ids); - error_setg(errp, - "Invalid CPU [socket: %u, die: %u, core: %u, thread: %u] with" - " APIC ID %" PRIu32 ", valid index range 0:%d", - topo_ids.pkg_id, topo_ids.die_id, topo_ids.core_id, topo_ids.smt_id, - cpu->apic_id, ms->possible_cpus->len - 1); - return; - } - - if (cpu_slot->cpu) { - error_setg(errp, "CPU[%d] with APIC ID %" PRIu32 " exists", - idx, cpu->apic_id); - return; - } - - /* if 'address' properties socket-id/core-id/thread-id are not set, set them - * so that machine_query_hotpluggable_cpus would show correct values - */ - /* TODO: move socket_id/core_id/thread_id checks into x86_cpu_realizefn() - * once -smp refactoring is complete and there will be CPU private - * CPUState::nr_cores and CPUState::nr_threads fields instead of globals */ - x86_topo_ids_from_apicid(cpu->apic_id, &topo_info, &topo_ids); - if (cpu->socket_id != -1 && cpu->socket_id != topo_ids.pkg_id) { - error_setg(errp, "property socket-id: %u doesn't match set apic-id:" - " 0x%x (socket-id: %u)", cpu->socket_id, cpu->apic_id, - topo_ids.pkg_id); - return; - } - cpu->socket_id = topo_ids.pkg_id; - - if (cpu->die_id != -1 && cpu->die_id != topo_ids.die_id) { - error_setg(errp, "property die-id: %u doesn't match set apic-id:" - " 0x%x (die-id: %u)", cpu->die_id, cpu->apic_id, topo_ids.die_id); - return; - } - cpu->die_id = topo_ids.die_id; - - if (cpu->core_id != -1 && cpu->core_id != topo_ids.core_id) { - error_setg(errp, "property core-id: %u doesn't match set apic-id:" - " 0x%x (core-id: %u)", cpu->core_id, cpu->apic_id, - topo_ids.core_id); - return; - } - cpu->core_id = topo_ids.core_id; - - if (cpu->thread_id != -1 && cpu->thread_id != topo_ids.smt_id) { - error_setg(errp, "property thread-id: %u doesn't match set apic-id:" - " 0x%x (thread-id: %u)", cpu->thread_id, cpu->apic_id, - topo_ids.smt_id); - return; - } - cpu->thread_id = topo_ids.smt_id; - - /* - * kvm_enabled() must go first to ensure that kvm_* references are - * not emitted for the linker to consume (kvm_enabled() is - * a literal `0` in configurations where kvm_* aren't defined) - */ - if (kvm_enabled() && hyperv_feat_enabled(cpu, HYPERV_FEAT_VPINDEX) && - !kvm_hv_vpindex_settable()) { - error_setg(errp, "kernel doesn't allow setting HyperV VP_INDEX"); - return; - } - - cs = CPU(cpu); - cs->cpu_index = idx; - - numa_cpu_pre_plug(cpu_slot, dev, errp); -} - static CpuInstanceProperties x86_cpu_index_to_props(MachineState *ms, unsigned cpu_index) { @@ -528,676 +152,6 @@ static void x86_nmi(NMIState *n, int cpu_index, Error **errp) } } -static long get_file_size(FILE *f) -{ - long where, size; - - /* XXX: on Unix systems, using fstat() probably makes more sense */ - - where = ftell(f); - fseek(f, 0, SEEK_END); - size = ftell(f); - fseek(f, where, SEEK_SET); - - return size; -} - -/* TSC handling */ -uint64_t cpu_get_tsc(CPUX86State *env) -{ - return cpus_get_elapsed_ticks(); -} - -/* IRQ handling */ -static void pic_irq_request(void *opaque, int irq, int level) -{ - CPUState *cs = first_cpu; - X86CPU *cpu = X86_CPU(cs); - - trace_x86_pic_interrupt(irq, level); - if (cpu_is_apic_enabled(cpu->apic_state) && !kvm_irqchip_in_kernel() && - !whpx_apic_in_platform()) { - CPU_FOREACH(cs) { - cpu = X86_CPU(cs); - if (apic_accept_pic_intr(cpu->apic_state)) { - apic_deliver_pic_intr(cpu->apic_state, level); - } - } - } else { - if (level) { - cpu_interrupt(cs, CPU_INTERRUPT_HARD); - } else { - cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); - } - } -} - -qemu_irq x86_allocate_cpu_irq(void) -{ - return qemu_allocate_irq(pic_irq_request, NULL, 0); -} - -int cpu_get_pic_interrupt(CPUX86State *env) -{ - X86CPU *cpu = env_archcpu(env); - int intno; - - if (!kvm_irqchip_in_kernel() && !whpx_apic_in_platform()) { - intno = apic_get_interrupt(cpu->apic_state); - if (intno >= 0) { - return intno; - } - /* read the irq from the PIC */ - if (!apic_accept_pic_intr(cpu->apic_state)) { - return -1; - } - } - - intno = pic_read_irq(isa_pic); - return intno; -} - -DeviceState *cpu_get_current_apic(void) -{ - if (current_cpu) { - X86CPU *cpu = X86_CPU(current_cpu); - return cpu->apic_state; - } else { - return NULL; - } -} - -void gsi_handler(void *opaque, int n, int level) -{ - GSIState *s = opaque; - - trace_x86_gsi_interrupt(n, level); - switch (n) { - case 0 ... ISA_NUM_IRQS - 1: - if (s->i8259_irq[n]) { - /* Under KVM, Kernel will forward to both PIC and IOAPIC */ - qemu_set_irq(s->i8259_irq[n], level); - } - /* fall through */ - case ISA_NUM_IRQS ... IOAPIC_NUM_PINS - 1: -#ifdef CONFIG_XEN_EMU - /* - * Xen delivers the GSI to the Legacy PIC (not that Legacy PIC - * routing actually works properly under Xen). And then to - * *either* the PIRQ handling or the I/OAPIC depending on - * whether the former wants it. - */ - if (xen_mode == XEN_EMULATE && xen_evtchn_set_gsi(n, level)) { - break; - } -#endif - qemu_set_irq(s->ioapic_irq[n], level); - break; - case IO_APIC_SECONDARY_IRQBASE - ... IO_APIC_SECONDARY_IRQBASE + IOAPIC_NUM_PINS - 1: - qemu_set_irq(s->ioapic2_irq[n - IO_APIC_SECONDARY_IRQBASE], level); - break; - } -} - -void ioapic_init_gsi(GSIState *gsi_state, Object *parent) -{ - DeviceState *dev; - SysBusDevice *d; - unsigned int i; - - assert(parent); - if (kvm_ioapic_in_kernel()) { - dev = qdev_new(TYPE_KVM_IOAPIC); - } else { - dev = qdev_new(TYPE_IOAPIC); - } - object_property_add_child(parent, "ioapic", OBJECT(dev)); - d = SYS_BUS_DEVICE(dev); - sysbus_realize_and_unref(d, &error_fatal); - sysbus_mmio_map(d, 0, IO_APIC_DEFAULT_ADDRESS); - - for (i = 0; i < IOAPIC_NUM_PINS; i++) { - gsi_state->ioapic_irq[i] = qdev_get_gpio_in(dev, i); - } -} - -DeviceState *ioapic_init_secondary(GSIState *gsi_state) -{ - DeviceState *dev; - SysBusDevice *d; - unsigned int i; - - dev = qdev_new(TYPE_IOAPIC); - d = SYS_BUS_DEVICE(dev); - sysbus_realize_and_unref(d, &error_fatal); - sysbus_mmio_map(d, 0, IO_APIC_SECONDARY_ADDRESS); - - for (i = 0; i < IOAPIC_NUM_PINS; i++) { - gsi_state->ioapic2_irq[i] = qdev_get_gpio_in(dev, i); - } - return dev; -} - -/* - * The entry point into the kernel for PVH boot is different from - * the native entry point. The PVH entry is defined by the x86/HVM - * direct boot ABI and is available in an ELFNOTE in the kernel binary. - * - * This function is passed to load_elf() when it is called from - * load_elfboot() which then additionally checks for an ELF Note of - * type XEN_ELFNOTE_PHYS32_ENTRY and passes it to this function to - * parse the PVH entry address from the ELF Note. - * - * Due to trickery in elf_opts.h, load_elf() is actually available as - * load_elf32() or load_elf64() and this routine needs to be able - * to deal with being called as 32 or 64 bit. - * - * The address of the PVH entry point is saved to the 'pvh_start_addr' - * global variable. (although the entry point is 32-bit, the kernel - * binary can be either 32-bit or 64-bit). - */ -static uint64_t read_pvh_start_addr(void *arg1, void *arg2, bool is64) -{ - size_t *elf_note_data_addr; - - /* Check if ELF Note header passed in is valid */ - if (arg1 == NULL) { - return 0; - } - - if (is64) { - struct elf64_note *nhdr64 = (struct elf64_note *)arg1; - uint64_t nhdr_size64 = sizeof(struct elf64_note); - uint64_t phdr_align = *(uint64_t *)arg2; - uint64_t nhdr_namesz = nhdr64->n_namesz; - - elf_note_data_addr = - ((void *)nhdr64) + nhdr_size64 + - QEMU_ALIGN_UP(nhdr_namesz, phdr_align); - - pvh_start_addr = *elf_note_data_addr; - } else { - struct elf32_note *nhdr32 = (struct elf32_note *)arg1; - uint32_t nhdr_size32 = sizeof(struct elf32_note); - uint32_t phdr_align = *(uint32_t *)arg2; - uint32_t nhdr_namesz = nhdr32->n_namesz; - - elf_note_data_addr = - ((void *)nhdr32) + nhdr_size32 + - QEMU_ALIGN_UP(nhdr_namesz, phdr_align); - - pvh_start_addr = *(uint32_t *)elf_note_data_addr; - } - - return pvh_start_addr; -} - -static bool load_elfboot(const char *kernel_filename, - int kernel_file_size, - uint8_t *header, - size_t pvh_xen_start_addr, - FWCfgState *fw_cfg) -{ - uint32_t flags = 0; - uint32_t mh_load_addr = 0; - uint32_t elf_kernel_size = 0; - uint64_t elf_entry; - uint64_t elf_low, elf_high; - int kernel_size; - - if (ldl_p(header) != 0x464c457f) { - return false; /* no elfboot */ - } - - bool elf_is64 = header[EI_CLASS] == ELFCLASS64; - flags = elf_is64 ? - ((Elf64_Ehdr *)header)->e_flags : ((Elf32_Ehdr *)header)->e_flags; - - if (flags & 0x00010004) { /* LOAD_ELF_HEADER_HAS_ADDR */ - error_report("elfboot unsupported flags = %x", flags); - exit(1); - } - - uint64_t elf_note_type = XEN_ELFNOTE_PHYS32_ENTRY; - kernel_size = load_elf(kernel_filename, read_pvh_start_addr, - NULL, &elf_note_type, &elf_entry, - &elf_low, &elf_high, NULL, 0, I386_ELF_MACHINE, - 0, 0); - - if (kernel_size < 0) { - error_report("Error while loading elf kernel"); - exit(1); - } - mh_load_addr = elf_low; - elf_kernel_size = elf_high - elf_low; - - if (pvh_start_addr == 0) { - error_report("Error loading uncompressed kernel without PVH ELF Note"); - exit(1); - } - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ENTRY, pvh_start_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, mh_load_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, elf_kernel_size); - - return true; -} - -void x86_load_linux(X86MachineState *x86ms, - FWCfgState *fw_cfg, - int acpi_data_size, - bool pvh_enabled) -{ - bool linuxboot_dma_enabled = X86_MACHINE_GET_CLASS(x86ms)->fwcfg_dma_enabled; - uint16_t protocol; - int setup_size, kernel_size, cmdline_size; - int dtb_size, setup_data_offset; - uint32_t initrd_max; - uint8_t header[8192], *setup, *kernel; - hwaddr real_addr, prot_addr, cmdline_addr, initrd_addr = 0; - FILE *f; - char *vmode; - MachineState *machine = MACHINE(x86ms); - struct setup_data *setup_data; - const char *kernel_filename = machine->kernel_filename; - const char *initrd_filename = machine->initrd_filename; - const char *dtb_filename = machine->dtb; - const char *kernel_cmdline = machine->kernel_cmdline; - SevKernelLoaderContext sev_load_ctx = {}; - - /* Align to 16 bytes as a paranoia measure */ - cmdline_size = (strlen(kernel_cmdline) + 16) & ~15; - - /* load the kernel header */ - f = fopen(kernel_filename, "rb"); - if (!f) { - fprintf(stderr, "qemu: could not open kernel file '%s': %s\n", - kernel_filename, strerror(errno)); - exit(1); - } - - kernel_size = get_file_size(f); - if (!kernel_size || - fread(header, 1, MIN(ARRAY_SIZE(header), kernel_size), f) != - MIN(ARRAY_SIZE(header), kernel_size)) { - fprintf(stderr, "qemu: could not load kernel '%s': %s\n", - kernel_filename, strerror(errno)); - exit(1); - } - - /* kernel protocol version */ - if (ldl_p(header + 0x202) == 0x53726448) { - protocol = lduw_p(header + 0x206); - } else { - /* - * This could be a multiboot kernel. If it is, let's stop treating it - * like a Linux kernel. - * Note: some multiboot images could be in the ELF format (the same of - * PVH), so we try multiboot first since we check the multiboot magic - * header before to load it. - */ - if (load_multiboot(x86ms, fw_cfg, f, kernel_filename, initrd_filename, - kernel_cmdline, kernel_size, header)) { - return; - } - /* - * Check if the file is an uncompressed kernel file (ELF) and load it, - * saving the PVH entry point used by the x86/HVM direct boot ABI. - * If load_elfboot() is successful, populate the fw_cfg info. - */ - if (pvh_enabled && - load_elfboot(kernel_filename, kernel_size, - header, pvh_start_addr, fw_cfg)) { - fclose(f); - - fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, - strlen(kernel_cmdline) + 1); - fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline); - - fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, sizeof(header)); - fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA, - header, sizeof(header)); - - /* load initrd */ - if (initrd_filename) { - GMappedFile *mapped_file; - gsize initrd_size; - gchar *initrd_data; - GError *gerr = NULL; - - mapped_file = g_mapped_file_new(initrd_filename, false, &gerr); - if (!mapped_file) { - fprintf(stderr, "qemu: error reading initrd %s: %s\n", - initrd_filename, gerr->message); - exit(1); - } - x86ms->initrd_mapped_file = mapped_file; - - initrd_data = g_mapped_file_get_contents(mapped_file); - initrd_size = g_mapped_file_get_length(mapped_file); - initrd_max = x86ms->below_4g_mem_size - acpi_data_size - 1; - if (initrd_size >= initrd_max) { - fprintf(stderr, "qemu: initrd is too large, cannot support." - "(max: %"PRIu32", need %"PRId64")\n", - initrd_max, (uint64_t)initrd_size); - exit(1); - } - - initrd_addr = (initrd_max - initrd_size) & ~4095; - - fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size); - fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, initrd_data, - initrd_size); - } - - option_rom[nb_option_roms].bootindex = 0; - option_rom[nb_option_roms].name = "pvh.bin"; - nb_option_roms++; - - return; - } - protocol = 0; - } - - if (protocol < 0x200 || !(header[0x211] & 0x01)) { - /* Low kernel */ - real_addr = 0x90000; - cmdline_addr = 0x9a000 - cmdline_size; - prot_addr = 0x10000; - } else if (protocol < 0x202) { - /* High but ancient kernel */ - real_addr = 0x90000; - cmdline_addr = 0x9a000 - cmdline_size; - prot_addr = 0x100000; - } else { - /* High and recent kernel */ - real_addr = 0x10000; - cmdline_addr = 0x20000; - prot_addr = 0x100000; - } - - /* highest address for loading the initrd */ - if (protocol >= 0x20c && - lduw_p(header + 0x236) & XLF_CAN_BE_LOADED_ABOVE_4G) { - /* - * Linux has supported initrd up to 4 GB for a very long time (2007, - * long before XLF_CAN_BE_LOADED_ABOVE_4G which was added in 2013), - * though it only sets initrd_max to 2 GB to "work around bootloader - * bugs". Luckily, QEMU firmware(which does something like bootloader) - * has supported this. - * - * It's believed that if XLF_CAN_BE_LOADED_ABOVE_4G is set, initrd can - * be loaded into any address. - * - * In addition, initrd_max is uint32_t simply because QEMU doesn't - * support the 64-bit boot protocol (specifically the ext_ramdisk_image - * field). - * - * Therefore here just limit initrd_max to UINT32_MAX simply as well. - */ - initrd_max = UINT32_MAX; - } else if (protocol >= 0x203) { - initrd_max = ldl_p(header + 0x22c); - } else { - initrd_max = 0x37ffffff; - } - - if (initrd_max >= x86ms->below_4g_mem_size - acpi_data_size) { - initrd_max = x86ms->below_4g_mem_size - acpi_data_size - 1; - } - - fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_ADDR, cmdline_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, strlen(kernel_cmdline) + 1); - fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline); - sev_load_ctx.cmdline_data = (char *)kernel_cmdline; - sev_load_ctx.cmdline_size = strlen(kernel_cmdline) + 1; - - if (protocol >= 0x202) { - stl_p(header + 0x228, cmdline_addr); - } else { - stw_p(header + 0x20, 0xA33F); - stw_p(header + 0x22, cmdline_addr - real_addr); - } - - /* handle vga= parameter */ - vmode = strstr(kernel_cmdline, "vga="); - if (vmode) { - unsigned int video_mode; - const char *end; - int ret; - /* skip "vga=" */ - vmode += 4; - if (!strncmp(vmode, "normal", 6)) { - video_mode = 0xffff; - } else if (!strncmp(vmode, "ext", 3)) { - video_mode = 0xfffe; - } else if (!strncmp(vmode, "ask", 3)) { - video_mode = 0xfffd; - } else { - ret = qemu_strtoui(vmode, &end, 0, &video_mode); - if (ret != 0 || (*end && *end != ' ')) { - fprintf(stderr, "qemu: invalid 'vga=' kernel parameter.\n"); - exit(1); - } - } - stw_p(header + 0x1fa, video_mode); - } - - /* loader type */ - /* - * High nybble = B reserved for QEMU; low nybble is revision number. - * If this code is substantially changed, you may want to consider - * incrementing the revision. - */ - if (protocol >= 0x200) { - header[0x210] = 0xB0; - } - /* heap */ - if (protocol >= 0x201) { - header[0x211] |= 0x80; /* CAN_USE_HEAP */ - stw_p(header + 0x224, cmdline_addr - real_addr - 0x200); - } - - /* load initrd */ - if (initrd_filename) { - GMappedFile *mapped_file; - gsize initrd_size; - gchar *initrd_data; - GError *gerr = NULL; - - if (protocol < 0x200) { - fprintf(stderr, "qemu: linux kernel too old to load a ram disk\n"); - exit(1); - } - - mapped_file = g_mapped_file_new(initrd_filename, false, &gerr); - if (!mapped_file) { - fprintf(stderr, "qemu: error reading initrd %s: %s\n", - initrd_filename, gerr->message); - exit(1); - } - x86ms->initrd_mapped_file = mapped_file; - - initrd_data = g_mapped_file_get_contents(mapped_file); - initrd_size = g_mapped_file_get_length(mapped_file); - if (initrd_size >= initrd_max) { - fprintf(stderr, "qemu: initrd is too large, cannot support." - "(max: %"PRIu32", need %"PRId64")\n", - initrd_max, (uint64_t)initrd_size); - exit(1); - } - - initrd_addr = (initrd_max - initrd_size) & ~4095; - - fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size); - fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, initrd_data, initrd_size); - sev_load_ctx.initrd_data = initrd_data; - sev_load_ctx.initrd_size = initrd_size; - - stl_p(header + 0x218, initrd_addr); - stl_p(header + 0x21c, initrd_size); - } - - /* load kernel and setup */ - setup_size = header[0x1f1]; - if (setup_size == 0) { - setup_size = 4; - } - setup_size = (setup_size + 1) * 512; - if (setup_size > kernel_size) { - fprintf(stderr, "qemu: invalid kernel header\n"); - exit(1); - } - kernel_size -= setup_size; - - setup = g_malloc(setup_size); - kernel = g_malloc(kernel_size); - fseek(f, 0, SEEK_SET); - if (fread(setup, 1, setup_size, f) != setup_size) { - fprintf(stderr, "fread() failed\n"); - exit(1); - } - if (fread(kernel, 1, kernel_size, f) != kernel_size) { - fprintf(stderr, "fread() failed\n"); - exit(1); - } - fclose(f); - - /* append dtb to kernel */ - if (dtb_filename) { - if (protocol < 0x209) { - fprintf(stderr, "qemu: Linux kernel too old to load a dtb\n"); - exit(1); - } - - dtb_size = get_image_size(dtb_filename); - if (dtb_size <= 0) { - fprintf(stderr, "qemu: error reading dtb %s: %s\n", - dtb_filename, strerror(errno)); - exit(1); - } - - setup_data_offset = QEMU_ALIGN_UP(kernel_size, 16); - kernel_size = setup_data_offset + sizeof(struct setup_data) + dtb_size; - kernel = g_realloc(kernel, kernel_size); - - stq_p(header + 0x250, prot_addr + setup_data_offset); - - setup_data = (struct setup_data *)(kernel + setup_data_offset); - setup_data->next = 0; - setup_data->type = cpu_to_le32(SETUP_DTB); - setup_data->len = cpu_to_le32(dtb_size); - - load_image_size(dtb_filename, setup_data->data, dtb_size); - } - - /* - * If we're starting an encrypted VM, it will be OVMF based, which uses the - * efi stub for booting and doesn't require any values to be placed in the - * kernel header. We therefore don't update the header so the hash of the - * kernel on the other side of the fw_cfg interface matches the hash of the - * file the user passed in. - */ - if (!sev_enabled()) { - memcpy(setup, header, MIN(sizeof(header), setup_size)); - } - - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, prot_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size); - fw_cfg_add_bytes(fw_cfg, FW_CFG_KERNEL_DATA, kernel, kernel_size); - sev_load_ctx.kernel_data = (char *)kernel; - sev_load_ctx.kernel_size = kernel_size; - - fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_ADDR, real_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, setup_size); - fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA, setup, setup_size); - sev_load_ctx.setup_data = (char *)setup; - sev_load_ctx.setup_size = setup_size; - - if (sev_enabled()) { - sev_add_kernel_loader_hashes(&sev_load_ctx, &error_fatal); - } - - option_rom[nb_option_roms].bootindex = 0; - option_rom[nb_option_roms].name = "linuxboot.bin"; - if (linuxboot_dma_enabled && fw_cfg_dma_enabled(fw_cfg)) { - option_rom[nb_option_roms].name = "linuxboot_dma.bin"; - } - nb_option_roms++; -} - -void x86_isa_bios_init(MemoryRegion *isa_bios, MemoryRegion *isa_memory, - MemoryRegion *bios, bool read_only) -{ - uint64_t bios_size = memory_region_size(bios); - uint64_t isa_bios_size = MIN(bios_size, 128 * KiB); - - memory_region_init_alias(isa_bios, NULL, "isa-bios", bios, - bios_size - isa_bios_size, isa_bios_size); - memory_region_add_subregion_overlap(isa_memory, 1 * MiB - isa_bios_size, - isa_bios, 1); - memory_region_set_readonly(isa_bios, read_only); -} - -void x86_bios_rom_init(X86MachineState *x86ms, const char *default_firmware, - MemoryRegion *rom_memory, bool isapc_ram_fw) -{ - const char *bios_name; - char *filename; - int bios_size; - ssize_t ret; - - /* BIOS load */ - bios_name = MACHINE(x86ms)->firmware ?: default_firmware; - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - if (filename) { - bios_size = get_image_size(filename); - } else { - bios_size = -1; - } - if (bios_size <= 0 || - (bios_size % 65536) != 0) { - goto bios_error; - } - memory_region_init_ram(&x86ms->bios, NULL, "pc.bios", bios_size, - &error_fatal); - if (sev_enabled()) { - /* - * The concept of a "reset" simply doesn't exist for - * confidential computing guests, we have to destroy and - * re-launch them instead. So there is no need to register - * the firmware as rom to properly re-initialize on reset. - * Just go for a straight file load instead. - */ - void *ptr = memory_region_get_ram_ptr(&x86ms->bios); - load_image_size(filename, ptr, bios_size); - x86_firmware_configure(ptr, bios_size); - } else { - memory_region_set_readonly(&x86ms->bios, !isapc_ram_fw); - ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size), -1); - if (ret != 0) { - goto bios_error; - } - } - g_free(filename); - - /* map the last 128KB of the BIOS in ISA space */ - x86_isa_bios_init(&x86ms->isa_bios, rom_memory, &x86ms->bios, - !isapc_ram_fw); - - /* map all the bios at the top of memory */ - memory_region_add_subregion(rom_memory, - (uint32_t)(-bios_size), - &x86ms->bios); - return; - -bios_error: - fprintf(stderr, "qemu: could not load PC BIOS '%s'\n", bios_name); - exit(1); -} - bool x86_machine_is_smm_enabled(const X86MachineState *x86ms) { bool smm_available = false; diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h index c2062db13f..b006f16b8d 100644 --- a/include/hw/i386/x86.h +++ b/include/hw/i386/x86.h @@ -21,6 +21,7 @@ #include "exec/memory.h" #include "hw/boards.h" +#include "hw/i386/topology.h" #include "hw/intc/ioapic.h" #include "hw/isa/isa.h" #include "qom/object.h" @@ -109,12 +110,11 @@ struct X86MachineState { #define TYPE_X86_MACHINE MACHINE_TYPE_NAME("x86") OBJECT_DECLARE_TYPE(X86MachineState, X86MachineClass, X86_MACHINE) -uint32_t x86_cpu_apic_id_from_index(X86MachineState *pcms, +void init_topo_info(X86CPUTopoInfo *topo_info, const X86MachineState *x86ms); +uint32_t x86_cpu_apic_id_from_index(X86MachineState *x86ms, unsigned int cpu_index); -void x86_cpu_new(X86MachineState *pcms, int64_t apic_id, Error **errp); void x86_cpus_init(X86MachineState *pcms, int default_cpu_version); -CPUArchId *x86_find_cpu_slot(MachineState *ms, uint32_t id, int *idx); void x86_rtc_set_cpus_count(ISADevice *rtc, uint16_t cpus_count); void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); From d0be0ac2c37eb978ed51c822b4a8a7dc9015e1e0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 19:00:42 +0200 Subject: [PATCH 19/27] hw/i386: move rtc-reset-reinjection command out of hw/rtc The rtc-reset-reinjection QMP command is specific to x86, other boards do not have the ACK tracking functionality that is needed for RTC interrupt reinjection. Therefore the QMP command is only included in x86, but qmp_rtc_reset_reinjection() is implemented by hw/rtc/mc146818rtc.c and requires tracking of all created RTC devices. Move the implementation to hw/i386, so that 1) it is available even if no RTC device exist 2) the only RTC that exists is easily found in x86ms->rtc. Signed-off-by: Paolo Bonzini Reviewed-by: Zhao Liu Message-ID: <20240509170044.190795-12-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- hw/i386/meson.build | 1 + hw/i386/monitor.c | 46 ++++++++++++++++++++++++++++++++++++ hw/rtc/mc146818rtc.c | 12 ++-------- include/hw/rtc/mc146818rtc.h | 2 +- 4 files changed, 50 insertions(+), 11 deletions(-) create mode 100644 hw/i386/monitor.c diff --git a/hw/i386/meson.build b/hw/i386/meson.build index 3437da0aad..03aad10df7 100644 --- a/hw/i386/meson.build +++ b/hw/i386/meson.build @@ -2,6 +2,7 @@ i386_ss = ss.source_set() i386_ss.add(files( 'fw_cfg.c', 'e820_memory_layout.c', + 'monitor.c', 'multiboot.c', 'x86.c', 'x86-cpu.c', diff --git a/hw/i386/monitor.c b/hw/i386/monitor.c new file mode 100644 index 0000000000..1ebd3564bf --- /dev/null +++ b/hw/i386/monitor.c @@ -0,0 +1,46 @@ +/* + * QEMU monitor + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "monitor/monitor.h" +#include "qapi/qmp/qdict.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-misc-target.h" +#include "hw/i386/x86.h" +#include "hw/rtc/mc146818rtc.h" + +#include CONFIG_DEVICES + +void qmp_rtc_reset_reinjection(Error **errp) +{ + X86MachineState *x86ms = X86_MACHINE(qdev_get_machine()); + +#ifdef CONFIG_MC146818RTC + if (x86ms->rtc) { + rtc_reset_reinjection(MC146818_RTC(x86ms->rtc)); + } +#else + assert(!x86ms->rtc); +#endif +} diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c index 3379f92748..8ccee9a385 100644 --- a/hw/rtc/mc146818rtc.c +++ b/hw/rtc/mc146818rtc.c @@ -104,16 +104,9 @@ static void rtc_coalesced_timer_update(MC146818RtcState *s) } } -static QLIST_HEAD(, MC146818RtcState) rtc_devices = - QLIST_HEAD_INITIALIZER(rtc_devices); - -void qmp_rtc_reset_reinjection(Error **errp) +void rtc_reset_reinjection(MC146818RtcState *rtc) { - MC146818RtcState *s; - - QLIST_FOREACH(s, &rtc_devices, link) { - s->irq_coalesced = 0; - } + rtc->irq_coalesced = 0; } static bool rtc_policy_slew_deliver_irq(MC146818RtcState *s) @@ -941,7 +934,6 @@ static void rtc_realizefn(DeviceState *dev, Error **errp) object_property_add_tm(OBJECT(s), "date", rtc_get_date); qdev_init_gpio_out(dev, &s->irq, 1); - QLIST_INSERT_HEAD(&rtc_devices, s, link); } MC146818RtcState *mc146818_rtc_init(ISABus *bus, int base_year, diff --git a/include/hw/rtc/mc146818rtc.h b/include/hw/rtc/mc146818rtc.h index 97cec0b3e8..64893be151 100644 --- a/include/hw/rtc/mc146818rtc.h +++ b/include/hw/rtc/mc146818rtc.h @@ -55,6 +55,6 @@ MC146818RtcState *mc146818_rtc_init(ISABus *bus, int base_year, qemu_irq intercept_irq); void mc146818rtc_set_cmos_data(MC146818RtcState *s, int addr, int val); int mc146818rtc_get_cmos_data(MC146818RtcState *s, int addr); -void qmp_rtc_reset_reinjection(Error **errp); +void rtc_reset_reinjection(MC146818RtcState *rtc); #endif /* HW_RTC_MC146818RTC_H */ From 1b1badf3c523105d4cf5e6ccf8413d1be1dbadc4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 19:00:43 +0200 Subject: [PATCH 20/27] i386: select correct components for no-board build The local APIC is a part of the CPU and has callbacks that are invoked from multiple accelerators. The IOAPIC on the other hand is optional, but ioapic_eoi_broadcast is used by common x86 code to implement the IOAPIC's implicit EOI mode. Add a stub in case the IOAPIC device is not included but the APIC is. Signed-off-by: Paolo Bonzini Reviewed-by: Zhao Liu Message-ID: <20240509170044.190795-13-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest.yml | 2 +- hw/intc/ioapic-stub.c | 29 +++++++++++++++++++++++++++++ hw/intc/meson.build | 2 +- target/i386/Kconfig | 1 + 4 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 hw/intc/ioapic-stub.c diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 8ca3e0586c..7af5c1ce8a 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -650,7 +650,7 @@ build-tci: # Check our reduced build configurations # requires libfdt: aarch64, arm, i386, loongarch64, microblaze, microblazeel, # mips64el, or1k, ppc, ppc64, riscv32, riscv64, rx, x86_64 -# does not build without boards: i386, x86_64 +# fails qtest without boards: i386, x86_64 build-without-defaults: extends: .native_build_job_template needs: diff --git a/hw/intc/ioapic-stub.c b/hw/intc/ioapic-stub.c new file mode 100644 index 0000000000..4dcd86248d --- /dev/null +++ b/hw/intc/ioapic-stub.c @@ -0,0 +1,29 @@ +/* + * ioapic.c IOAPIC emulation logic + * + * Copyright (c) 2004-2005 Fabrice Bellard + * + * Split the ioapic logic from apic.c + * Xiantao Zhang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.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 "qapi/error.h" +#include "hw/intc/ioapic.h" + +void ioapic_eoi_broadcast(int vector) +{ +} diff --git a/hw/intc/meson.build b/hw/intc/meson.build index f4b540e6a8..0d1b7d0a43 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -20,7 +20,7 @@ system_ss.add(when: 'CONFIG_GOLDFISH_PIC', if_true: files('goldfish_pic.c')) system_ss.add(when: 'CONFIG_HEATHROW_PIC', if_true: files('heathrow_pic.c')) system_ss.add(when: 'CONFIG_I8259', if_true: files('i8259_common.c', 'i8259.c')) system_ss.add(when: 'CONFIG_IMX', if_true: files('imx_avic.c', 'imx_gpcv2.c')) -system_ss.add(when: 'CONFIG_IOAPIC', if_true: files('ioapic_common.c')) +system_ss.add(when: 'CONFIG_IOAPIC', if_true: files('ioapic_common.c'), if_false: files('ioapic-stub.c')) system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_intc.c')) system_ss.add(when: 'CONFIG_OPENPIC', if_true: files('openpic.c')) system_ss.add(when: 'CONFIG_PL190', if_true: files('pl190.c')) diff --git a/target/i386/Kconfig b/target/i386/Kconfig index ad9291d3b8..6b0feef029 100644 --- a/target/i386/Kconfig +++ b/target/i386/Kconfig @@ -1,5 +1,6 @@ config I386 bool + select APIC # kvm_arch_fixup_msi_route() needs to access PCIDevice select PCI if KVM From 6bd92a7c62648eb37053ccc809c3cab7d7629584 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 19:00:44 +0200 Subject: [PATCH 21/27] tests/qtest: arm: fix operation in a build without any boards or devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ARM/aarch64 are easy to fix because they already have to pass a machine type by hand. Just guard the tests with a check that the machine actually exists. Signed-off-by: Paolo Bonzini Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-ID: <20240509170044.190795-14-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- tests/qtest/arm-cpu-features.c | 4 ++++ tests/qtest/migration-test.c | 6 ++++++ tests/qtest/numa-test.c | 4 ++++ 3 files changed, 14 insertions(+) diff --git a/tests/qtest/arm-cpu-features.c b/tests/qtest/arm-cpu-features.c index 9d6e6190d5..966c65d5c3 100644 --- a/tests/qtest/arm-cpu-features.c +++ b/tests/qtest/arm-cpu-features.c @@ -632,6 +632,10 @@ int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); + if (!qtest_has_machine("virt")) { + goto out; + } + if (qtest_has_accel("tcg")) { qtest_add_data_func("/arm/query-cpu-model-expansion", NULL, test_query_cpu_model_expansion); diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 7a1345f80f..e8d3555f56 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -781,6 +781,12 @@ static int test_migrate_start(QTestState **from, QTestState **to, kvm_opts = ",dirty-ring-size=4096"; } + if (!qtest_has_machine(machine_alias)) { + g_autofree char *msg = g_strdup_printf("machine %s not supported", machine_alias); + g_test_skip(msg); + return -1; + } + machine = resolve_machine_version(machine_alias, QEMU_ENV_SRC, QEMU_ENV_DST); diff --git a/tests/qtest/numa-test.c b/tests/qtest/numa-test.c index 4f4404a4b1..7aa262dbb9 100644 --- a/tests/qtest/numa-test.c +++ b/tests/qtest/numa-test.c @@ -558,6 +558,9 @@ int main(int argc, char **argv) } if (g_str_equal(arch, "aarch64")) { + if (!qtest_has_machine("virt")) { + goto out; + } g_string_append(args, " -machine virt"); } @@ -590,5 +593,6 @@ int main(int argc, char **argv) aarch64_numa_cpu); } +out: return g_test_run(); } From 727bb5b477e66b6cd8a558bf2cdf9a666b914f27 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 7 May 2024 12:22:31 +0200 Subject: [PATCH 22/27] meson: pick libfdt from common_ss when building target-specific files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid having to list dependencies such as libfdt twice, both on common_ss and specific_ss. Instead, just take all the dependencies in common_ss and allow the target-specific libqemu-*.fa library to use them. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- hw/arm/meson.build | 2 +- hw/loongarch/meson.build | 2 +- hw/mips/meson.build | 2 +- hw/openrisc/meson.build | 4 ++-- hw/ppc/meson.build | 4 +--- hw/riscv/meson.build | 2 +- meson.build | 14 +++++++++++--- 7 files changed, 18 insertions(+), 12 deletions(-) diff --git a/hw/arm/meson.build b/hw/arm/meson.build index 6808135c1f..aefde0c69a 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -1,5 +1,5 @@ arm_ss = ss.source_set() -arm_ss.add(files('boot.c'), fdt) +arm_ss.add(files('boot.c')) arm_ss.add(when: 'CONFIG_ARM_VIRT', if_true: files('virt.c')) arm_ss.add(when: 'CONFIG_ACPI', if_true: files('virt-acpi-build.c')) arm_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic_boards.c')) diff --git a/hw/loongarch/meson.build b/hw/loongarch/meson.build index d306d82c2e..bce7ebac97 100644 --- a/hw/loongarch/meson.build +++ b/hw/loongarch/meson.build @@ -3,7 +3,7 @@ loongarch_ss.add(files( 'fw_cfg.c', 'boot.c', )) -loongarch_ss.add(when: 'CONFIG_LOONGARCH_VIRT', if_true: [files('virt.c'), fdt]) +loongarch_ss.add(when: 'CONFIG_LOONGARCH_VIRT', if_true: files('virt.c')) loongarch_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-build.c')) hw_arch += {'loongarch': loongarch_ss} diff --git a/hw/mips/meson.build b/hw/mips/meson.build index f06d88f343..ca37c42d90 100644 --- a/hw/mips/meson.build +++ b/hw/mips/meson.build @@ -9,7 +9,7 @@ if 'CONFIG_TCG' in config_all_accel mips_ss.add(when: 'CONFIG_JAZZ', if_true: files('jazz.c')) mips_ss.add(when: 'CONFIG_MIPSSIM', if_true: files('mipssim.c')) mips_ss.add(when: 'CONFIG_FULOONG', if_true: files('fuloong2e.c')) -mips_ss.add(when: 'CONFIG_MIPS_BOSTON', if_true: [files('boston.c'), fdt]) +mips_ss.add(when: 'CONFIG_MIPS_BOSTON', if_true: files('boston.c')) endif hw_arch += {'mips': mips_ss} diff --git a/hw/openrisc/meson.build b/hw/openrisc/meson.build index 2dbc6365bb..82f1f0ef1c 100644 --- a/hw/openrisc/meson.build +++ b/hw/openrisc/meson.build @@ -1,7 +1,7 @@ openrisc_ss = ss.source_set() openrisc_ss.add(files('cputimer.c')) openrisc_ss.add(files('boot.c')) -openrisc_ss.add(when: 'CONFIG_OR1K_SIM', if_true: [files('openrisc_sim.c'), fdt]) -openrisc_ss.add(when: 'CONFIG_OR1K_VIRT', if_true: [files('virt.c'), fdt]) +openrisc_ss.add(when: 'CONFIG_OR1K_SIM', if_true: files('openrisc_sim.c')) +openrisc_ss.add(when: 'CONFIG_OR1K_VIRT', if_true: files('virt.c')) hw_arch += {'openrisc': openrisc_ss} diff --git a/hw/ppc/meson.build b/hw/ppc/meson.build index d096636ee7..3ebbf329bc 100644 --- a/hw/ppc/meson.build +++ b/hw/ppc/meson.build @@ -3,9 +3,7 @@ ppc_ss.add(files( 'ppc.c', 'ppc_booke.c', )) -ppc_ss.add(when: 'CONFIG_FDT_PPC', if_true: [files( - 'fdt.c', -), fdt]) +ppc_ss.add(when: 'CONFIG_FDT_PPC', if_true: files('fdt.c')) ppc_ss.add(when: 'CONFIG_FW_CFG_PPC', if_true: files('fw_cfg.c')) # IBM pSeries (sPAPR) diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build index 2f7ee81be3..f872674093 100644 --- a/hw/riscv/meson.build +++ b/hw/riscv/meson.build @@ -1,5 +1,5 @@ riscv_ss = ss.source_set() -riscv_ss.add(files('boot.c'), fdt) +riscv_ss.add(files('boot.c')) riscv_ss.add(when: 'CONFIG_RISCV_NUMA', if_true: files('numa.c')) riscv_ss.add(files('riscv_hart.c')) riscv_ss.add(when: 'CONFIG_OPENTITAN', if_true: files('opentitan.c')) diff --git a/meson.build b/meson.build index 83ae4347c7..ab1f44b25c 100644 --- a/meson.build +++ b/meson.build @@ -3867,15 +3867,23 @@ foreach target : target_dirs target_common = common_ss.apply(config_target, strict: false) objects = common_all.extract_objects(target_common.sources()) - deps = target_common.dependencies() + arch_deps += target_common.dependencies() target_specific = specific_ss.apply(config_target, strict: false) arch_srcs += target_specific.sources() arch_deps += target_specific.dependencies() + # allow using headers from the dependencies but do not include the sources, + # because this emulator only needs those in "objects". For external + # dependencies, the full dependency is included below in the executable. + lib_deps = [] + foreach dep : arch_deps + lib_deps += dep.partial_dependency(compile_args: true, includes: true) + endforeach + lib = static_library('qemu-' + target, sources: arch_srcs + genh, - dependencies: arch_deps, + dependencies: lib_deps, objects: objects, include_directories: target_inc, c_args: c_args, @@ -3923,7 +3931,7 @@ foreach target : target_dirs emulator = executable(exe_name, exe['sources'], install: true, c_args: c_args, - dependencies: arch_deps + deps + exe['dependencies'], + dependencies: arch_deps + exe['dependencies'], objects: lib.extract_all_objects(recursive: true), link_depends: [block_syms, qemu_syms], link_args: link_args, From 7a6f3343b6f140c945b62a972b36d22efa14bdba Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 25 Jan 2024 12:22:57 +0100 Subject: [PATCH 23/27] meson: move libfdt together with other dependencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the libfdt detection code together with other dependencies instead of keeping it with subprojects. This has the disadvantage of performing the detection even if no target requires libfdt; but it has the advantage that Kconfig will be able to observe the availability of the library. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- meson.build | 79 ++++++++++++++++++++++++++--------------------------- 1 file changed, 38 insertions(+), 41 deletions(-) diff --git a/meson.build b/meson.build index ab1f44b25c..dd4a28f8f8 100644 --- a/meson.build +++ b/meson.build @@ -1858,6 +1858,34 @@ if numa.found() and not cc.links(''' endif endif +fdt = not_found +fdt_opt = get_option('fdt') +if fdt_opt == 'enabled' and get_option('wrap_mode') == 'nodownload' + fdt_opt = 'system' +endif +if fdt_opt in ['enabled', 'system'] or (fdt_opt == 'auto' and have_system) + fdt = cc.find_library('fdt', required: fdt_opt == 'system') + if fdt.found() and cc.links(''' + #include + #include + int main(void) { fdt_find_max_phandle(NULL, NULL); return 0; }''', + dependencies: fdt) + fdt_opt = 'system' + elif fdt_opt != 'system' + fdt_opt = get_option('wrap_mode') == 'nodownload' ? 'disabled' : 'internal' + fdt = not_found + else + error('system libfdt is too old (1.5.1 or newer required)') + endif +endif +if fdt_opt == 'internal' + assert(not fdt.found()) + libfdt_proj = subproject('dtc', required: true, + default_options: ['tools=false', 'yaml=disabled', + 'python=disabled', 'default_library=static']) + fdt = libfdt_proj.get_variable('libfdt_dep') +endif + rdma = not_found if not get_option('rdma').auto() or have_system libumad = cc.find_library('ibumad', required: get_option('rdma')) @@ -2199,6 +2227,7 @@ config_host_data.set('CONFIG_BSD', host_os in bsd_oses) config_host_data.set('CONFIG_CAPSTONE', capstone.found()) config_host_data.set('CONFIG_COCOA', cocoa.found()) config_host_data.set('CONFIG_DARWIN', host_os == 'darwin') +config_host_data.set('CONFIG_FDT', fdt.found()) config_host_data.set('CONFIG_FUZZ', get_option('fuzzing')) config_host_data.set('CONFIG_GCOV', get_option('b_coverage')) config_host_data.set('CONFIG_LIBUDEV', libudev.found()) @@ -3024,14 +3053,16 @@ foreach target : target_dirs error('No accelerator available for target @0@'.format(target)) endif - actual_target_dirs += target config_target += keyval.load('configs/targets' / target + '.mak') config_target += { 'TARGET_' + config_target['TARGET_ARCH'].to_upper(): 'y' } - if 'TARGET_NEED_FDT' in config_target + if 'TARGET_NEED_FDT' in config_target and not fdt.found() fdt_required += target + continue endif + actual_target_dirs += target + # Add default keys if 'TARGET_BASE_ARCH' not in config_target config_target += {'TARGET_BASE_ARCH': config_target['TARGET_ARCH']} @@ -3119,6 +3150,10 @@ genh += custom_target('config-poison.h', command: [find_program('scripts/make-config-poison.sh'), target_configs_h]) +if fdt_required.length() > 0 + error('fdt disabled but required by targets ' + ', '.join(fdt_required)) +endif + ############### # Subprojects # ############### @@ -3129,44 +3164,6 @@ if have_system and vfio_user_server_allowed libvfio_user_dep = libvfio_user_proj.get_variable('libvfio_user_dep') endif -fdt = not_found -fdt_opt = get_option('fdt') -if fdt_required.length() > 0 or fdt_opt == 'enabled' - if fdt_opt == 'disabled' - error('fdt disabled but required by targets ' + ', '.join(fdt_required)) - endif - - if fdt_opt in ['enabled', 'auto', 'system'] - if get_option('wrap_mode') == 'nodownload' - fdt_opt = 'system' - endif - fdt = cc.find_library('fdt', required: fdt_opt == 'system') - if fdt.found() and cc.links(''' - #include - #include - int main(void) { fdt_find_max_phandle(NULL, NULL); return 0; }''', - dependencies: fdt) - fdt_opt = 'system' - elif fdt_opt == 'system' - error('system libfdt requested, but it is too old (1.5.1 or newer required)') - else - fdt_opt = 'internal' - fdt = not_found - endif - endif - if not fdt.found() - assert(fdt_opt == 'internal') - libfdt_proj = subproject('dtc', required: true, - default_options: ['tools=false', 'yaml=disabled', - 'python=disabled', 'default_library=static']) - fdt = libfdt_proj.get_variable('libfdt_dep') - endif -else - fdt_opt = 'disabled' -endif - -config_host_data.set('CONFIG_FDT', fdt.found()) - vhost_user = not_found if host_os == 'linux' and have_vhost_user libvhost_user = subproject('libvhost-user') @@ -4417,7 +4414,7 @@ summary_info += {'Linux AIO support': libaio} summary_info += {'Linux io_uring support': linux_io_uring} summary_info += {'ATTR/XATTR support': libattr} summary_info += {'RDMA support': rdma} -summary_info += {'fdt support': fdt_opt == 'disabled' ? false : fdt_opt} +summary_info += {'fdt support': fdt_opt == 'internal' ? 'internal' : fdt} summary_info += {'libcap-ng support': libcap_ng} summary_info += {'bpf support': libbpf} summary_info += {'rbd support': rbd} From 1935b7ead18dbcbf459dbe7a8fd4253fbe1ed4d0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 7 May 2024 14:13:46 +0200 Subject: [PATCH 24/27] kconfig: allow compiling out QEMU device tree code per target Introduce a new Kconfig symbol, CONFIG_DEVICE_TREE, that specifies whether to include the common device tree code in system/device_tree.c and to link to libfdt. For now, include it unconditionally if libfdt is available. Signed-off-by: Paolo Bonzini --- Kconfig.host | 4 ++++ hw/core/Kconfig | 9 ++++++++- hw/core/meson.build | 2 +- include/monitor/hmp.h | 1 + include/sysemu/device_tree.h | 1 - meson.build | 1 + monitor/hmp-cmds.c | 17 +++++++++++++++++ system/device_tree-stub.c | 10 ++++++++++ system/device_tree.c | 14 -------------- system/meson.build | 4 +++- 10 files changed, 45 insertions(+), 18 deletions(-) create mode 100644 system/device_tree-stub.c diff --git a/Kconfig.host b/Kconfig.host index f6a2a131e6..a0d4a52131 100644 --- a/Kconfig.host +++ b/Kconfig.host @@ -23,6 +23,10 @@ config IVSHMEM config TPM bool +config FDT + bool + select DEVICE_TREE + config VHOST_USER bool diff --git a/hw/core/Kconfig b/hw/core/Kconfig index 9397503656..24411f5930 100644 --- a/hw/core/Kconfig +++ b/hw/core/Kconfig @@ -4,8 +4,14 @@ config EMPTY_SLOT config PTIMER bool +config DEVICE_TREE + bool + # fail the build if libfdt not found + depends on FDT + config FITLOADER bool + depends on DEVICE_TREE config GENERIC_LOADER bool @@ -14,13 +20,14 @@ config GENERIC_LOADER config GUEST_LOADER bool default y - depends on TCG + depends on TCG && DEVICE_TREE config OR_IRQ bool config PLATFORM_BUS bool + depends on DEVICE_TREE config REGISTER bool diff --git a/hw/core/meson.build b/hw/core/meson.build index f20d4143f7..a3d9bab9f4 100644 --- a/hw/core/meson.build +++ b/hw/core/meson.build @@ -16,7 +16,7 @@ common_ss.add(files('cpu-common.c')) common_ss.add(files('machine-smp.c')) system_ss.add(when: 'CONFIG_FITLOADER', if_true: files('loader-fit.c')) system_ss.add(when: 'CONFIG_GENERIC_LOADER', if_true: files('generic-loader.c')) -system_ss.add(when: ['CONFIG_GUEST_LOADER', fdt], if_true: files('guest-loader.c')) +system_ss.add(when: 'CONFIG_GUEST_LOADER', if_true: files('guest-loader.c')) system_ss.add(when: 'CONFIG_OR_IRQ', if_true: files('or-irq.c')) system_ss.add(when: 'CONFIG_PLATFORM_BUS', if_true: files('platform-bus.c')) system_ss.add(when: 'CONFIG_PTIMER', if_true: files('ptimer.c')) diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h index f4cf8f6717..954f3c83ad 100644 --- a/include/monitor/hmp.h +++ b/include/monitor/hmp.h @@ -180,5 +180,6 @@ void hmp_ioport_write(Monitor *mon, const QDict *qdict); void hmp_boot_set(Monitor *mon, const QDict *qdict); void hmp_info_mtree(Monitor *mon, const QDict *qdict); void hmp_info_cryptodev(Monitor *mon, const QDict *qdict); +void hmp_dumpdtb(Monitor *mon, const QDict *qdict); #endif diff --git a/include/sysemu/device_tree.h b/include/sysemu/device_tree.h index 8eab395934..eb601522f8 100644 --- a/include/sysemu/device_tree.h +++ b/include/sysemu/device_tree.h @@ -134,7 +134,6 @@ int qemu_fdt_add_path(void *fdt, const char *path); } while (0) void qemu_fdt_dumpdtb(void *fdt, int size); -void hmp_dumpdtb(Monitor *mon, const QDict *qdict); /** * qemu_fdt_setprop_sized_cells_from_array: diff --git a/meson.build b/meson.build index dd4a28f8f8..8b90ea6cf7 100644 --- a/meson.build +++ b/meson.build @@ -2989,6 +2989,7 @@ host_kconfig = \ (have_ivshmem ? ['CONFIG_IVSHMEM=y'] : []) + \ (opengl.found() ? ['CONFIG_OPENGL=y'] : []) + \ (x11.found() ? ['CONFIG_X11=y'] : []) + \ + (fdt.found() ? ['CONFIG_FDT=y'] : []) + \ (have_vhost_user ? ['CONFIG_VHOST_USER=y'] : []) + \ (have_vhost_vdpa ? ['CONFIG_VHOST_VDPA=y'] : []) + \ (have_vhost_kernel ? ['CONFIG_VHOST_KERNEL=y'] : []) + \ diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index 871898ac46..ea79148ee8 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -22,6 +22,7 @@ #include "monitor/monitor-internal.h" #include "qapi/error.h" #include "qapi/qapi-commands-control.h" +#include "qapi/qapi-commands-machine.h" #include "qapi/qapi-commands-misc.h" #include "qapi/qmp/qdict.h" #include "qemu/cutils.h" @@ -443,3 +444,19 @@ void hmp_info_mtree(Monitor *mon, const QDict *qdict) mtree_info(flatview, dispatch_tree, owner, disabled); } + +#if defined(CONFIG_FDT) +void hmp_dumpdtb(Monitor *mon, const QDict *qdict) +{ + const char *filename = qdict_get_str(qdict, "filename"); + Error *local_err = NULL; + + qmp_dumpdtb(filename, &local_err); + + if (hmp_handle_error(mon, local_err)) { + return; + } + + monitor_printf(mon, "dtb dumped to %s", filename); +} +#endif diff --git a/system/device_tree-stub.c b/system/device_tree-stub.c new file mode 100644 index 0000000000..bddda6fa37 --- /dev/null +++ b/system/device_tree-stub.c @@ -0,0 +1,10 @@ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-machine.h" + +#ifdef CONFIG_FDT +void qmp_dumpdtb(const char *filename, Error **errp) +{ + error_setg(errp, "This machine doesn't have a FDT"); +} +#endif diff --git a/system/device_tree.c b/system/device_tree.c index eb5166ca36..2e38259d34 100644 --- a/system/device_tree.c +++ b/system/device_tree.c @@ -668,20 +668,6 @@ void qmp_dumpdtb(const char *filename, Error **errp) } } -void hmp_dumpdtb(Monitor *mon, const QDict *qdict) -{ - const char *filename = qdict_get_str(qdict, "filename"); - Error *local_err = NULL; - - qmp_dumpdtb(filename, &local_err); - - if (hmp_handle_error(mon, local_err)) { - return; - } - - info_report("dtb dumped to %s", filename); -} - void qemu_fdt_randomize_seeds(void *fdt) { int noffset, poffset, len; diff --git a/system/meson.build b/system/meson.build index 25e2117250..a296270cb0 100644 --- a/system/meson.build +++ b/system/meson.build @@ -32,7 +32,9 @@ if have_tpm endif system_ss.add(when: seccomp, if_true: files('qemu-seccomp.c')) -system_ss.add(when: fdt, if_true: files('device_tree.c')) +system_ss.add(when: 'CONFIG_DEVICE_TREE', + if_true: [fdt, files('device_tree.c')], + if_false: files('device_tree-stub.c')) if host_os == 'linux' system_ss.add(files('async-teardown.c')) endif From d641ec30be4963f43bffe033f5265a57cb8f4c90 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 30 Apr 2024 21:08:15 +0200 Subject: [PATCH 25/27] kconfig: express dependency of individual boards on libfdt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that boards are enabled by default and the "CONFIG_FOO=y" entries are gone from configs/devices/, there cannot be any more a conflicts between the default contents of configs/devices/ and a failed "depends on" clause. With this change, each individual board or target can express whether it needs FDT. It can then include the common code in the build via "select DEVICE_TREE", which will also as tell meson to link with libfdt. This allows building non-microvm x86 emulators without having libfdt available. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- Kconfig.host | 1 - hw/arm/Kconfig | 5 +++++ hw/i386/Kconfig | 3 ++- hw/loongarch/Kconfig | 3 ++- hw/mips/Kconfig | 3 ++- hw/openrisc/Kconfig | 2 ++ hw/ppc/Kconfig | 15 ++++++++------- hw/riscv/Kconfig | 4 ++++ hw/rx/Kconfig | 3 ++- hw/xtensa/Kconfig | 1 + target/arm/Kconfig | 2 ++ target/microblaze/Kconfig | 1 + target/openrisc/Kconfig | 1 + target/riscv/Kconfig | 2 ++ 14 files changed, 34 insertions(+), 12 deletions(-) diff --git a/Kconfig.host b/Kconfig.host index a0d4a52131..17f405004b 100644 --- a/Kconfig.host +++ b/Kconfig.host @@ -25,7 +25,6 @@ config TPM config FDT bool - select DEVICE_TREE config VHOST_USER bool diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 98c264ed21..8b97683a45 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -15,6 +15,7 @@ config ARM_VIRT select ACPI select ARM_SMMUV3 select GPIO_KEY + select DEVICE_TREE select FW_CFG_DMA select PCI_EXPRESS select PCI_EXPRESS_GENERIC_BRIDGE @@ -265,6 +266,7 @@ config SBSA_REF default y depends on TCG && AARCH64 imply PCI_DEVICES + select DEVICE_TREE select AHCI select ARM_SMMUV3 select GPIO_KEY @@ -347,6 +349,7 @@ config VEXPRESS bool default y depends on TCG && ARM + select DEVICE_TREE select A9MPCORE select A15MPCORE select ARM_MPTIMER @@ -492,6 +495,7 @@ config XLNX_ZYNQMP_ARM select CPU_CLUSTER select DDC select DPCD + select DEVICE_TREE select SDHCI select SSI select SSI_M25P80 @@ -509,6 +513,7 @@ config XLNX_VERSAL depends on TCG && AARCH64 select ARM_GIC select CPU_CLUSTER + select DEVICE_TREE select PL011 select CADENCE select VIRTIO_MMIO diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index 40b1e44580..f4a33b6c08 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -115,7 +115,8 @@ config Q35 config MICROVM bool default y - depends on I386 + depends on I386 && FDT + select DEVICE_TREE select SERIAL_ISA # for serial_hds_isa_init() select ISA_BUS select APIC diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig index ad77502445..90a0dba9d5 100644 --- a/hw/loongarch/Kconfig +++ b/hw/loongarch/Kconfig @@ -1,7 +1,8 @@ config LOONGARCH_VIRT bool default y - depends on LOONGARCH64 + depends on LOONGARCH64 && FDT + select DEVICE_TREE select PCI select PCI_EXPRESS_GENERIC_BRIDGE imply VIRTIO_VGA diff --git a/hw/mips/Kconfig b/hw/mips/Kconfig index 9bccb363eb..a7f26edebe 100644 --- a/hw/mips/Kconfig +++ b/hw/mips/Kconfig @@ -80,9 +80,10 @@ config MIPS_CPS config MIPS_BOSTON bool default y - depends on MIPS64 && !TARGET_BIG_ENDIAN + depends on MIPS64 && !TARGET_BIG_ENDIAN && FDT imply PCI_DEVICES imply TEST_DEVICES + select DEVICE_TREE select FITLOADER select MIPS_CPS select PCI_EXPRESS_XILINX diff --git a/hw/openrisc/Kconfig b/hw/openrisc/Kconfig index 9c9015e0a5..76b953c62c 100644 --- a/hw/openrisc/Kconfig +++ b/hw/openrisc/Kconfig @@ -2,6 +2,7 @@ config OR1K_SIM bool default y depends on OPENRISC + select DEVICE_TREE select SERIAL select OPENCORES_ETH select OMPIC @@ -14,6 +15,7 @@ config OR1K_VIRT imply PCI_DEVICES imply VIRTIO_VGA imply TEST_DEVICES + select DEVICE_TREE select PCI select PCI_EXPRESS_GENERIC_BRIDGE select GOLDFISH_RTC diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig index 78f83e78ce..347212f4db 100644 --- a/hw/ppc/Kconfig +++ b/hw/ppc/Kconfig @@ -1,7 +1,7 @@ config PSERIES bool default y - depends on PPC64 + depends on PPC64 && FDT imply USB_OHCI_PCI imply PCI_DEVICES imply TEST_DEVICES @@ -26,7 +26,7 @@ config SPAPR_RNG config POWERNV bool default y - depends on PPC64 + depends on PPC64 && FDT imply PCI_DEVICES imply TEST_DEVICES select ISA_IPMI_BT @@ -52,7 +52,7 @@ config PPC405 config PPC440 bool default y - depends on PPC + depends on PPC && FDT imply PCI_DEVICES imply TEST_DEVICES imply E1000_PCI @@ -71,7 +71,7 @@ config PPC4XX config SAM460EX bool default y - depends on PPC + depends on PPC && FDT select PFLASH_CFI01 select IDE_SII3112 select M41T80 @@ -168,19 +168,19 @@ config E500 config E500PLAT bool default y - depends on PPC + depends on PPC && FDT select E500 config MPC8544DS bool default y - depends on PPC + depends on PPC && FDT select E500 config VIRTEX bool default y - depends on PPC + depends on PPC && FDT select PPC4XX select PFLASH_CFI01 select SERIAL @@ -193,6 +193,7 @@ config FW_CFG_PPC bool config FDT_PPC + select DEVICE_TREE bool config VOF diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index 5f5f9e31bb..a2030e3a6f 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -12,6 +12,7 @@ config MICROCHIP_PFSOC depends on RISCV64 select CADENCE_SDHCI select CPU_CLUSTER + select DEVICE_TREE select MCHP_PFSOC_DMC select MCHP_PFSOC_IOSCB select MCHP_PFSOC_MMUART @@ -37,6 +38,7 @@ config RISCV_VIRT imply VIRTIO_VGA imply TEST_DEVICES imply TPM_TIS_SYSBUS + select DEVICE_TREE select RISCV_NUMA select GOLDFISH_RTC select PCI @@ -82,6 +84,7 @@ config SIFIVE_U depends on RISCV32 || RISCV64 select CADENCE select CPU_CLUSTER + select DEVICE_TREE select RISCV_ACLINT select SIFIVE_GPIO select SIFIVE_PDMA @@ -99,6 +102,7 @@ config SPIKE bool default y depends on RISCV32 || RISCV64 + select DEVICE_TREE select RISCV_NUMA select HTIF select RISCV_ACLINT diff --git a/hw/rx/Kconfig b/hw/rx/Kconfig index b2fa2b7eec..aa9242d1ef 100644 --- a/hw/rx/Kconfig +++ b/hw/rx/Kconfig @@ -8,5 +8,6 @@ config RX62N_MCU config RX_GDBSIM bool default y - depends on RX + depends on RX && FDT + select DEVICE_TREE select RX62N_MCU diff --git a/hw/xtensa/Kconfig b/hw/xtensa/Kconfig index 443b415c2b..8ea283a7a3 100644 --- a/hw/xtensa/Kconfig +++ b/hw/xtensa/Kconfig @@ -15,6 +15,7 @@ config XTENSA_XTFPGA bool default y depends on XTENSA + imply DEVICE_TREE select OPENCORES_ETH select PFLASH_CFI01 select SERIAL diff --git a/target/arm/Kconfig b/target/arm/Kconfig index 5847c5a74a..7f8a2217ae 100644 --- a/target/arm/Kconfig +++ b/target/arm/Kconfig @@ -6,6 +6,8 @@ config ARM # translate.c v7m helpers under ARM_V7M. select ARM_V7M if TCG + select DEVICE_TREE # needed by boot.c + config AARCH64 bool select ARM diff --git a/target/microblaze/Kconfig b/target/microblaze/Kconfig index a5410d9218..e91d58d88f 100644 --- a/target/microblaze/Kconfig +++ b/target/microblaze/Kconfig @@ -1,2 +1,3 @@ config MICROBLAZE bool + select DEVICE_TREE # needed by boot.c diff --git a/target/openrisc/Kconfig b/target/openrisc/Kconfig index e0da4ac1df..cd66c2e3b6 100644 --- a/target/openrisc/Kconfig +++ b/target/openrisc/Kconfig @@ -1,2 +1,3 @@ config OPENRISC bool + select DEVICE_TREE # needed by boot.c diff --git a/target/riscv/Kconfig b/target/riscv/Kconfig index adb7de3f37..5f30df22f2 100644 --- a/target/riscv/Kconfig +++ b/target/riscv/Kconfig @@ -1,7 +1,9 @@ config RISCV32 bool select ARM_COMPATIBLE_SEMIHOSTING # for do_common_semihosting() + select DEVICE_TREE # needed by boot.c config RISCV64 bool select ARM_COMPATIBLE_SEMIHOSTING # for do_common_semihosting() + select DEVICE_TREE # needed by boot.c From 47771d67563dc48753d275249d0764144d56357d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 22 Jan 2024 10:19:17 +0100 Subject: [PATCH 26/27] hw/xtensa: require libfdt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All other boards require libfdt if it can be used (including for example i386/x86_64), so change the "imply" to "select" and always allow -dtb in qemu-system-xtensa. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- hw/xtensa/Kconfig | 4 ++-- hw/xtensa/xtfpga.c | 9 --------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/hw/xtensa/Kconfig b/hw/xtensa/Kconfig index 8ea283a7a3..fc5c785cfa 100644 --- a/hw/xtensa/Kconfig +++ b/hw/xtensa/Kconfig @@ -14,8 +14,8 @@ config XTENSA_VIRT config XTENSA_XTFPGA bool default y - depends on XTENSA - imply DEVICE_TREE + depends on XTENSA && FDT + select DEVICE_TREE select OPENCORES_ETH select PFLASH_CFI01 select SERIAL diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c index f49e6591dc..955e8867a3 100644 --- a/hw/xtensa/xtfpga.c +++ b/hw/xtensa/xtfpga.c @@ -356,7 +356,6 @@ static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine) cur_tagptr = put_tag(cur_tagptr, BP_TAG_COMMAND_LINE, strlen(kernel_cmdline) + 1, kernel_cmdline); } -#ifdef CONFIG_FDT if (dtb_filename) { int fdt_size; void *fdt = load_device_tree(dtb_filename, &fdt_size); @@ -373,14 +372,6 @@ static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine) cur_lowmem = QEMU_ALIGN_UP(cur_lowmem + fdt_size, 4 * KiB); g_free(fdt); } -#else - if (dtb_filename) { - error_report("could not load DTB '%s': " - "FDT support is not configured in QEMU", - dtb_filename); - exit(EXIT_FAILURE); - } -#endif if (initrd_filename) { BpMemInfo initrd_location = { 0 }; int initrd_size = load_ramdisk(initrd_filename, cur_lowmem, From 9b089d254a5623baef01f7d0ec37331c1155f1ce Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 May 2024 09:25:48 +0200 Subject: [PATCH 27/27] configs: disable emulators that require it if libfdt is not found MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since boards can express their dependency on libfdt and system/device_tree.c, only leave TARGET_NEED_FDT if the target has a hard dependency. Those emulators will be skipped if libfdt is disabled, or if it is "auto" and not found and --disable-download is passed; unless the target is mentioned explicitly in --target-list, in which case the build will fail. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest.yml | 8 ++++---- configs/targets/aarch64-softmmu.mak | 1 + configs/targets/arm-softmmu.mak | 1 + configs/targets/i386-softmmu.mak | 1 - configs/targets/loongarch64-softmmu.mak | 1 + configs/targets/microblaze-softmmu.mak | 1 + configs/targets/microblazeel-softmmu.mak | 1 + configs/targets/mips64el-softmmu.mak | 1 - configs/targets/or1k-softmmu.mak | 1 + configs/targets/ppc-softmmu.mak | 1 - configs/targets/ppc64-softmmu.mak | 1 + configs/targets/riscv32-softmmu.mak | 1 + configs/targets/riscv64-softmmu.mak | 1 + configs/targets/rx-softmmu.mak | 1 + configs/targets/x86_64-softmmu.mak | 1 - meson.build | 6 +++++- 16 files changed, 19 insertions(+), 9 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 7af5c1ce8a..372404fc85 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -648,8 +648,8 @@ build-tci: - make check-tcg # Check our reduced build configurations -# requires libfdt: aarch64, arm, i386, loongarch64, microblaze, microblazeel, -# mips64el, or1k, ppc, ppc64, riscv32, riscv64, rx, x86_64 +# requires libfdt: aarch64, arm, loongarch64, microblaze, microblazeel, +# or1k, ppc64, riscv32, riscv64, rx # fails qtest without boards: i386, x86_64 build-without-defaults: extends: .native_build_job_template @@ -665,8 +665,8 @@ build-without-defaults: --disable-qom-cast-debug --disable-strip TARGETS: alpha-softmmu avr-softmmu cris-softmmu hppa-softmmu m68k-softmmu - mips-softmmu mips64-softmmu mipsel-softmmu - s390x-softmmu sh4-softmmu sh4eb-softmmu sparc-softmmu + mips-softmmu mips64-softmmu mipsel-softmmu mips64el-softmmu + ppc-softmmu s390x-softmmu sh4-softmmu sh4eb-softmmu sparc-softmmu sparc64-softmmu tricore-softmmu xtensa-softmmu xtensaeb-softmmu hexagon-linux-user i386-linux-user s390x-linux-user MAKE_CHECK_ARGS: check diff --git a/configs/targets/aarch64-softmmu.mak b/configs/targets/aarch64-softmmu.mak index 83c22391a6..84cb32dc2f 100644 --- a/configs/targets/aarch64-softmmu.mak +++ b/configs/targets/aarch64-softmmu.mak @@ -3,4 +3,5 @@ TARGET_BASE_ARCH=arm TARGET_SUPPORTS_MTTCG=y TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml gdb-xml/arm-m-profile-mve.xml gdb-xml/aarch64-pauth.xml +# needed by boot.c TARGET_NEED_FDT=y diff --git a/configs/targets/arm-softmmu.mak b/configs/targets/arm-softmmu.mak index 92c8349b96..bf390b7a8d 100644 --- a/configs/targets/arm-softmmu.mak +++ b/configs/targets/arm-softmmu.mak @@ -1,4 +1,5 @@ TARGET_ARCH=arm TARGET_SUPPORTS_MTTCG=y TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml gdb-xml/arm-m-profile-mve.xml +# needed by boot.c TARGET_NEED_FDT=y diff --git a/configs/targets/i386-softmmu.mak b/configs/targets/i386-softmmu.mak index d61b507613..2ac69d5ba3 100644 --- a/configs/targets/i386-softmmu.mak +++ b/configs/targets/i386-softmmu.mak @@ -1,5 +1,4 @@ TARGET_ARCH=i386 TARGET_SUPPORTS_MTTCG=y -TARGET_NEED_FDT=y TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_XML_FILES= gdb-xml/i386-32bit.xml diff --git a/configs/targets/loongarch64-softmmu.mak b/configs/targets/loongarch64-softmmu.mak index f23780fdd8..84beb19b90 100644 --- a/configs/targets/loongarch64-softmmu.mak +++ b/configs/targets/loongarch64-softmmu.mak @@ -2,4 +2,5 @@ TARGET_ARCH=loongarch64 TARGET_BASE_ARCH=loongarch TARGET_SUPPORTS_MTTCG=y TARGET_XML_FILES= gdb-xml/loongarch-base32.xml gdb-xml/loongarch-base64.xml gdb-xml/loongarch-fpu.xml +# all boards require libfdt TARGET_NEED_FDT=y diff --git a/configs/targets/microblaze-softmmu.mak b/configs/targets/microblaze-softmmu.mak index e84c0cc728..eea266d4f3 100644 --- a/configs/targets/microblaze-softmmu.mak +++ b/configs/targets/microblaze-softmmu.mak @@ -1,5 +1,6 @@ TARGET_ARCH=microblaze TARGET_BIG_ENDIAN=y TARGET_SUPPORTS_MTTCG=y +# needed by boot.c TARGET_NEED_FDT=y TARGET_XML_FILES=gdb-xml/microblaze-core.xml gdb-xml/microblaze-stack-protect.xml diff --git a/configs/targets/microblazeel-softmmu.mak b/configs/targets/microblazeel-softmmu.mak index 9b688036bd..77b968acad 100644 --- a/configs/targets/microblazeel-softmmu.mak +++ b/configs/targets/microblazeel-softmmu.mak @@ -1,4 +1,5 @@ TARGET_ARCH=microblaze TARGET_SUPPORTS_MTTCG=y +# needed by boot.c TARGET_NEED_FDT=y TARGET_XML_FILES=gdb-xml/microblaze-core.xml gdb-xml/microblaze-stack-protect.xml diff --git a/configs/targets/mips64el-softmmu.mak b/configs/targets/mips64el-softmmu.mak index 8d9ab3ddc4..3864daa736 100644 --- a/configs/targets/mips64el-softmmu.mak +++ b/configs/targets/mips64el-softmmu.mak @@ -1,3 +1,2 @@ TARGET_ARCH=mips64 TARGET_BASE_ARCH=mips -TARGET_NEED_FDT=y diff --git a/configs/targets/or1k-softmmu.mak b/configs/targets/or1k-softmmu.mak index 432f855a30..0341cb2a6b 100644 --- a/configs/targets/or1k-softmmu.mak +++ b/configs/targets/or1k-softmmu.mak @@ -1,4 +1,5 @@ TARGET_ARCH=openrisc TARGET_SUPPORTS_MTTCG=y TARGET_BIG_ENDIAN=y +# needed by boot.c and all boards TARGET_NEED_FDT=y diff --git a/configs/targets/ppc-softmmu.mak b/configs/targets/ppc-softmmu.mak index f3ea9c98f7..53120dab41 100644 --- a/configs/targets/ppc-softmmu.mak +++ b/configs/targets/ppc-softmmu.mak @@ -2,4 +2,3 @@ TARGET_ARCH=ppc TARGET_BIG_ENDIAN=y TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_XML_FILES= gdb-xml/power-core.xml gdb-xml/power-fpu.xml gdb-xml/power-altivec.xml gdb-xml/power-spe.xml -TARGET_NEED_FDT=y diff --git a/configs/targets/ppc64-softmmu.mak b/configs/targets/ppc64-softmmu.mak index 1db8d8381d..40881d9396 100644 --- a/configs/targets/ppc64-softmmu.mak +++ b/configs/targets/ppc64-softmmu.mak @@ -4,4 +4,5 @@ TARGET_BIG_ENDIAN=y TARGET_SUPPORTS_MTTCG=y TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_XML_FILES= gdb-xml/power64-core.xml gdb-xml/power-fpu.xml gdb-xml/power-altivec.xml gdb-xml/power-spe.xml gdb-xml/power-vsx.xml +# all boards require libfdt TARGET_NEED_FDT=y diff --git a/configs/targets/riscv32-softmmu.mak b/configs/targets/riscv32-softmmu.mak index d8b71cddcd..338182d5b8 100644 --- a/configs/targets/riscv32-softmmu.mak +++ b/configs/targets/riscv32-softmmu.mak @@ -2,4 +2,5 @@ TARGET_ARCH=riscv32 TARGET_BASE_ARCH=riscv TARGET_SUPPORTS_MTTCG=y TARGET_XML_FILES= gdb-xml/riscv-32bit-cpu.xml gdb-xml/riscv-32bit-fpu.xml gdb-xml/riscv-64bit-fpu.xml gdb-xml/riscv-32bit-virtual.xml +# needed by boot.c TARGET_NEED_FDT=y diff --git a/configs/targets/riscv64-softmmu.mak b/configs/targets/riscv64-softmmu.mak index 7c0e7eeb42..f688ffa7bc 100644 --- a/configs/targets/riscv64-softmmu.mak +++ b/configs/targets/riscv64-softmmu.mak @@ -2,4 +2,5 @@ TARGET_ARCH=riscv64 TARGET_BASE_ARCH=riscv TARGET_SUPPORTS_MTTCG=y TARGET_XML_FILES= gdb-xml/riscv-64bit-cpu.xml gdb-xml/riscv-32bit-fpu.xml gdb-xml/riscv-64bit-fpu.xml gdb-xml/riscv-64bit-virtual.xml +# needed by boot.c TARGET_NEED_FDT=y diff --git a/configs/targets/rx-softmmu.mak b/configs/targets/rx-softmmu.mak index 0c458b2d07..706bbe6062 100644 --- a/configs/targets/rx-softmmu.mak +++ b/configs/targets/rx-softmmu.mak @@ -1,3 +1,4 @@ TARGET_ARCH=rx TARGET_XML_FILES= gdb-xml/rx-core.xml +# all boards require libfdt TARGET_NEED_FDT=y diff --git a/configs/targets/x86_64-softmmu.mak b/configs/targets/x86_64-softmmu.mak index c5f882e5ba..e12ac3dc59 100644 --- a/configs/targets/x86_64-softmmu.mak +++ b/configs/targets/x86_64-softmmu.mak @@ -1,6 +1,5 @@ TARGET_ARCH=x86_64 TARGET_BASE_ARCH=i386 TARGET_SUPPORTS_MTTCG=y -TARGET_NEED_FDT=y TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_XML_FILES= gdb-xml/i386-64bit.xml diff --git a/meson.build b/meson.build index 8b90ea6cf7..9c14192514 100644 --- a/meson.build +++ b/meson.build @@ -3058,7 +3058,11 @@ foreach target : target_dirs config_target += { 'TARGET_' + config_target['TARGET_ARCH'].to_upper(): 'y' } if 'TARGET_NEED_FDT' in config_target and not fdt.found() - fdt_required += target + if default_targets + warning('Disabling ' + target + ' due to missing libfdt') + else + fdt_required += target + endif continue endif