diff options
161 files changed, 3776 insertions, 2712 deletions
diff --git a/.travis.yml b/.travis.yml index b92798ac3b..5672d129ec 100644 --- a/.travis.yml +++ b/.travis.yml @@ -79,6 +79,7 @@ env: - MAIN_SOFTMMU_TARGETS="aarch64-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu" - CCACHE_SLOPPINESS="include_file_ctime,include_file_mtime" - CCACHE_MAXSIZE=1G + - G_MESSAGES_DEBUG=error git: @@ -315,7 +316,7 @@ jobs: - name: "GCC check-acceptance" dist: bionic env: - - CONFIG="--target-list=aarch64-softmmu,alpha-softmmu,arm-softmmu,m68k-softmmu,microblaze-softmmu,mips-softmmu,mips64el-softmmu,nios2-softmmu,or1k-softmmu,ppc-softmmu,ppc64-softmmu,s390x-softmmu,sparc-softmmu,x86_64-softmmu,xtensa-softmmu" + - CONFIG="--enable-tools --target-list=aarch64-softmmu,alpha-softmmu,arm-softmmu,m68k-softmmu,microblaze-softmmu,mips-softmmu,mips64el-softmmu,nios2-softmmu,or1k-softmmu,ppc-softmmu,ppc64-softmmu,s390x-softmmu,sparc-softmmu,x86_64-softmmu,xtensa-softmmu" - TEST_CMD="make check-acceptance" after_script: - python3 -c 'import json; r = json.load(open("tests/results/latest/results.json")); [print(t["logfile"]) for t in r["tests"] if t["status"] not in ("PASS", "SKIP")]' | xargs cat diff --git a/accel/tcg/tcg-runtime-gvec.c b/accel/tcg/tcg-runtime-gvec.c index 5b1902d591..ca449702e6 100644 --- a/accel/tcg/tcg-runtime-gvec.c +++ b/accel/tcg/tcg-runtime-gvec.c @@ -24,48 +24,6 @@ #include "tcg/tcg-gvec-desc.h" -/* Virtually all hosts support 16-byte vectors. Those that don't can emulate - * them via GCC's generic vector extension. This turns out to be simpler and - * more reliable than getting the compiler to autovectorize. - * - * In tcg-op-gvec.c, we asserted that both the size and alignment of the data - * are multiples of 16. - * - * When the compiler does not support all of the operations we require, the - * loops are written so that we can always fall back on the base types. - */ -#ifdef CONFIG_VECTOR16 -typedef uint8_t vec8 __attribute__((vector_size(16))); -typedef uint16_t vec16 __attribute__((vector_size(16))); -typedef uint32_t vec32 __attribute__((vector_size(16))); -typedef uint64_t vec64 __attribute__((vector_size(16))); - -typedef int8_t svec8 __attribute__((vector_size(16))); -typedef int16_t svec16 __attribute__((vector_size(16))); -typedef int32_t svec32 __attribute__((vector_size(16))); -typedef int64_t svec64 __attribute__((vector_size(16))); - -#define DUP16(X) { X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X } -#define DUP8(X) { X, X, X, X, X, X, X, X } -#define DUP4(X) { X, X, X, X } -#define DUP2(X) { X, X } -#else -typedef uint8_t vec8; -typedef uint16_t vec16; -typedef uint32_t vec32; -typedef uint64_t vec64; - -typedef int8_t svec8; -typedef int16_t svec16; -typedef int32_t svec32; -typedef int64_t svec64; - -#define DUP16(X) X -#define DUP8(X) X -#define DUP4(X) X -#define DUP2(X) X -#endif /* CONFIG_VECTOR16 */ - static inline void clear_high(void *d, intptr_t oprsz, uint32_t desc) { intptr_t maxsz = simd_maxsz(desc); @@ -83,8 +41,8 @@ void HELPER(gvec_add8)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec8)) { - *(vec8 *)(d + i) = *(vec8 *)(a + i) + *(vec8 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint8_t)) { + *(uint8_t *)(d + i) = *(uint8_t *)(a + i) + *(uint8_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -94,8 +52,8 @@ void HELPER(gvec_add16)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec16)) { - *(vec16 *)(d + i) = *(vec16 *)(a + i) + *(vec16 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint16_t)) { + *(uint16_t *)(d + i) = *(uint16_t *)(a + i) + *(uint16_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -105,8 +63,8 @@ void HELPER(gvec_add32)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec32)) { - *(vec32 *)(d + i) = *(vec32 *)(a + i) + *(vec32 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint32_t)) { + *(uint32_t *)(d + i) = *(uint32_t *)(a + i) + *(uint32_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -116,8 +74,8 @@ void HELPER(gvec_add64)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) + *(vec64 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) + *(uint64_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -125,11 +83,10 @@ void HELPER(gvec_add64)(void *d, void *a, void *b, uint32_t desc) void HELPER(gvec_adds8)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec8 vecb = (vec8)DUP16(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec8)) { - *(vec8 *)(d + i) = *(vec8 *)(a + i) + vecb; + for (i = 0; i < oprsz; i += sizeof(uint8_t)) { + *(uint8_t *)(d + i) = *(uint8_t *)(a + i) + (uint8_t)b; } clear_high(d, oprsz, desc); } @@ -137,11 +94,10 @@ void HELPER(gvec_adds8)(void *d, void *a, uint64_t b, uint32_t desc) void HELPER(gvec_adds16)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec16 vecb = (vec16)DUP8(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec16)) { - *(vec16 *)(d + i) = *(vec16 *)(a + i) + vecb; + for (i = 0; i < oprsz; i += sizeof(uint16_t)) { + *(uint16_t *)(d + i) = *(uint16_t *)(a + i) + (uint16_t)b; } clear_high(d, oprsz, desc); } @@ -149,11 +105,10 @@ void HELPER(gvec_adds16)(void *d, void *a, uint64_t b, uint32_t desc) void HELPER(gvec_adds32)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec32 vecb = (vec32)DUP4(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec32)) { - *(vec32 *)(d + i) = *(vec32 *)(a + i) + vecb; + for (i = 0; i < oprsz; i += sizeof(uint32_t)) { + *(uint32_t *)(d + i) = *(uint32_t *)(a + i) + (uint32_t)b; } clear_high(d, oprsz, desc); } @@ -161,11 +116,10 @@ void HELPER(gvec_adds32)(void *d, void *a, uint64_t b, uint32_t desc) void HELPER(gvec_adds64)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec64 vecb = (vec64)DUP2(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) + vecb; + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) + b; } clear_high(d, oprsz, desc); } @@ -175,8 +129,8 @@ void HELPER(gvec_sub8)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec8)) { - *(vec8 *)(d + i) = *(vec8 *)(a + i) - *(vec8 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint8_t)) { + *(uint8_t *)(d + i) = *(uint8_t *)(a + i) - *(uint8_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -186,8 +140,8 @@ void HELPER(gvec_sub16)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec16)) { - *(vec16 *)(d + i) = *(vec16 *)(a + i) - *(vec16 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint16_t)) { + *(uint16_t *)(d + i) = *(uint16_t *)(a + i) - *(uint16_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -197,8 +151,8 @@ void HELPER(gvec_sub32)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec32)) { - *(vec32 *)(d + i) = *(vec32 *)(a + i) - *(vec32 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint32_t)) { + *(uint32_t *)(d + i) = *(uint32_t *)(a + i) - *(uint32_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -208,8 +162,8 @@ void HELPER(gvec_sub64)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) - *(vec64 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) - *(uint64_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -217,11 +171,10 @@ void HELPER(gvec_sub64)(void *d, void *a, void *b, uint32_t desc) void HELPER(gvec_subs8)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec8 vecb = (vec8)DUP16(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec8)) { - *(vec8 *)(d + i) = *(vec8 *)(a + i) - vecb; + for (i = 0; i < oprsz; i += sizeof(uint8_t)) { + *(uint8_t *)(d + i) = *(uint8_t *)(a + i) - (uint8_t)b; } clear_high(d, oprsz, desc); } @@ -229,11 +182,10 @@ void HELPER(gvec_subs8)(void *d, void *a, uint64_t b, uint32_t desc) void HELPER(gvec_subs16)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec16 vecb = (vec16)DUP8(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec16)) { - *(vec16 *)(d + i) = *(vec16 *)(a + i) - vecb; + for (i = 0; i < oprsz; i += sizeof(uint16_t)) { + *(uint16_t *)(d + i) = *(uint16_t *)(a + i) - (uint16_t)b; } clear_high(d, oprsz, desc); } @@ -241,11 +193,10 @@ void HELPER(gvec_subs16)(void *d, void *a, uint64_t b, uint32_t desc) void HELPER(gvec_subs32)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec32 vecb = (vec32)DUP4(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec32)) { - *(vec32 *)(d + i) = *(vec32 *)(a + i) - vecb; + for (i = 0; i < oprsz; i += sizeof(uint32_t)) { + *(uint32_t *)(d + i) = *(uint32_t *)(a + i) - (uint32_t)b; } clear_high(d, oprsz, desc); } @@ -253,11 +204,10 @@ void HELPER(gvec_subs32)(void *d, void *a, uint64_t b, uint32_t desc) void HELPER(gvec_subs64)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec64 vecb = (vec64)DUP2(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) - vecb; + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) - b; } clear_high(d, oprsz, desc); } @@ -267,8 +217,8 @@ void HELPER(gvec_mul8)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec8)) { - *(vec8 *)(d + i) = *(vec8 *)(a + i) * *(vec8 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint8_t)) { + *(uint8_t *)(d + i) = *(uint8_t *)(a + i) * *(uint8_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -278,8 +228,8 @@ void HELPER(gvec_mul16)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec16)) { - *(vec16 *)(d + i) = *(vec16 *)(a + i) * *(vec16 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint16_t)) { + *(uint16_t *)(d + i) = *(uint16_t *)(a + i) * *(uint16_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -289,8 +239,8 @@ void HELPER(gvec_mul32)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec32)) { - *(vec32 *)(d + i) = *(vec32 *)(a + i) * *(vec32 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint32_t)) { + *(uint32_t *)(d + i) = *(uint32_t *)(a + i) * *(uint32_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -300,8 +250,8 @@ void HELPER(gvec_mul64)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) * *(vec64 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) * *(uint64_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -309,11 +259,10 @@ void HELPER(gvec_mul64)(void *d, void *a, void *b, uint32_t desc) void HELPER(gvec_muls8)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec8 vecb = (vec8)DUP16(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec8)) { - *(vec8 *)(d + i) = *(vec8 *)(a + i) * vecb; + for (i = 0; i < oprsz; i += sizeof(uint8_t)) { + *(uint8_t *)(d + i) = *(uint8_t *)(a + i) * (uint8_t)b; } clear_high(d, oprsz, desc); } @@ -321,11 +270,10 @@ void HELPER(gvec_muls8)(void *d, void *a, uint64_t b, uint32_t desc) void HELPER(gvec_muls16)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec16 vecb = (vec16)DUP8(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec16)) { - *(vec16 *)(d + i) = *(vec16 *)(a + i) * vecb; + for (i = 0; i < oprsz; i += sizeof(uint16_t)) { + *(uint16_t *)(d + i) = *(uint16_t *)(a + i) * (uint16_t)b; } clear_high(d, oprsz, desc); } @@ -333,11 +281,10 @@ void HELPER(gvec_muls16)(void *d, void *a, uint64_t b, uint32_t desc) void HELPER(gvec_muls32)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec32 vecb = (vec32)DUP4(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec32)) { - *(vec32 *)(d + i) = *(vec32 *)(a + i) * vecb; + for (i = 0; i < oprsz; i += sizeof(uint32_t)) { + *(uint32_t *)(d + i) = *(uint32_t *)(a + i) * (uint32_t)b; } clear_high(d, oprsz, desc); } @@ -345,11 +292,10 @@ void HELPER(gvec_muls32)(void *d, void *a, uint64_t b, uint32_t desc) void HELPER(gvec_muls64)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec64 vecb = (vec64)DUP2(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) * vecb; + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) * b; } clear_high(d, oprsz, desc); } @@ -359,8 +305,8 @@ void HELPER(gvec_neg8)(void *d, void *a, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec8)) { - *(vec8 *)(d + i) = -*(vec8 *)(a + i); + for (i = 0; i < oprsz; i += sizeof(uint8_t)) { + *(uint8_t *)(d + i) = -*(uint8_t *)(a + i); } clear_high(d, oprsz, desc); } @@ -370,8 +316,8 @@ void HELPER(gvec_neg16)(void *d, void *a, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec16)) { - *(vec16 *)(d + i) = -*(vec16 *)(a + i); + for (i = 0; i < oprsz; i += sizeof(uint16_t)) { + *(uint16_t *)(d + i) = -*(uint16_t *)(a + i); } clear_high(d, oprsz, desc); } @@ -381,8 +327,8 @@ void HELPER(gvec_neg32)(void *d, void *a, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec32)) { - *(vec32 *)(d + i) = -*(vec32 *)(a + i); + for (i = 0; i < oprsz; i += sizeof(uint32_t)) { + *(uint32_t *)(d + i) = -*(uint32_t *)(a + i); } clear_high(d, oprsz, desc); } @@ -392,8 +338,8 @@ void HELPER(gvec_neg64)(void *d, void *a, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = -*(vec64 *)(a + i); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = -*(uint64_t *)(a + i); } clear_high(d, oprsz, desc); } @@ -499,8 +445,8 @@ void HELPER(gvec_not)(void *d, void *a, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = ~*(vec64 *)(a + i); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = ~*(uint64_t *)(a + i); } clear_high(d, oprsz, desc); } @@ -510,8 +456,8 @@ void HELPER(gvec_and)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) & *(vec64 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) & *(uint64_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -521,8 +467,8 @@ void HELPER(gvec_or)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) | *(vec64 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) | *(uint64_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -532,8 +478,8 @@ void HELPER(gvec_xor)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) ^ *(vec64 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) ^ *(uint64_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -543,8 +489,8 @@ void HELPER(gvec_andc)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) &~ *(vec64 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) &~ *(uint64_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -554,8 +500,8 @@ void HELPER(gvec_orc)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) |~ *(vec64 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) |~ *(uint64_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -565,8 +511,8 @@ void HELPER(gvec_nand)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = ~(*(vec64 *)(a + i) & *(vec64 *)(b + i)); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = ~(*(uint64_t *)(a + i) & *(uint64_t *)(b + i)); } clear_high(d, oprsz, desc); } @@ -576,8 +522,8 @@ void HELPER(gvec_nor)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = ~(*(vec64 *)(a + i) | *(vec64 *)(b + i)); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = ~(*(uint64_t *)(a + i) | *(uint64_t *)(b + i)); } clear_high(d, oprsz, desc); } @@ -587,8 +533,8 @@ void HELPER(gvec_eqv)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = ~(*(vec64 *)(a + i) ^ *(vec64 *)(b + i)); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = ~(*(uint64_t *)(a + i) ^ *(uint64_t *)(b + i)); } clear_high(d, oprsz, desc); } @@ -596,11 +542,10 @@ void HELPER(gvec_eqv)(void *d, void *a, void *b, uint32_t desc) void HELPER(gvec_ands)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec64 vecb = (vec64)DUP2(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) & vecb; + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) & b; } clear_high(d, oprsz, desc); } @@ -608,11 +553,10 @@ void HELPER(gvec_ands)(void *d, void *a, uint64_t b, uint32_t desc) void HELPER(gvec_xors)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec64 vecb = (vec64)DUP2(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) ^ vecb; + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) ^ b; } clear_high(d, oprsz, desc); } @@ -620,11 +564,10 @@ void HELPER(gvec_xors)(void *d, void *a, uint64_t b, uint32_t desc) void HELPER(gvec_ors)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec64 vecb = (vec64)DUP2(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) | vecb; + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) | b; } clear_high(d, oprsz, desc); } @@ -635,8 +578,8 @@ void HELPER(gvec_shl8i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec8)) { - *(vec8 *)(d + i) = *(vec8 *)(a + i) << shift; + for (i = 0; i < oprsz; i += sizeof(uint8_t)) { + *(uint8_t *)(d + i) = *(uint8_t *)(a + i) << shift; } clear_high(d, oprsz, desc); } @@ -647,8 +590,8 @@ void HELPER(gvec_shl16i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec16)) { - *(vec16 *)(d + i) = *(vec16 *)(a + i) << shift; + for (i = 0; i < oprsz; i += sizeof(uint16_t)) { + *(uint16_t *)(d + i) = *(uint16_t *)(a + i) << shift; } clear_high(d, oprsz, desc); } @@ -659,8 +602,8 @@ void HELPER(gvec_shl32i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec32)) { - *(vec32 *)(d + i) = *(vec32 *)(a + i) << shift; + for (i = 0; i < oprsz; i += sizeof(uint32_t)) { + *(uint32_t *)(d + i) = *(uint32_t *)(a + i) << shift; } clear_high(d, oprsz, desc); } @@ -671,8 +614,8 @@ void HELPER(gvec_shl64i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) << shift; + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) << shift; } clear_high(d, oprsz, desc); } @@ -683,8 +626,8 @@ void HELPER(gvec_shr8i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec8)) { - *(vec8 *)(d + i) = *(vec8 *)(a + i) >> shift; + for (i = 0; i < oprsz; i += sizeof(uint8_t)) { + *(uint8_t *)(d + i) = *(uint8_t *)(a + i) >> shift; } clear_high(d, oprsz, desc); } @@ -695,8 +638,8 @@ void HELPER(gvec_shr16i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec16)) { - *(vec16 *)(d + i) = *(vec16 *)(a + i) >> shift; + for (i = 0; i < oprsz; i += sizeof(uint16_t)) { + *(uint16_t *)(d + i) = *(uint16_t *)(a + i) >> shift; } clear_high(d, oprsz, desc); } @@ -707,8 +650,8 @@ void HELPER(gvec_shr32i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec32)) { - *(vec32 *)(d + i) = *(vec32 *)(a + i) >> shift; + for (i = 0; i < oprsz; i += sizeof(uint32_t)) { + *(uint32_t *)(d + i) = *(uint32_t *)(a + i) >> shift; } clear_high(d, oprsz, desc); } @@ -719,8 +662,8 @@ void HELPER(gvec_shr64i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) >> shift; + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) >> shift; } clear_high(d, oprsz, desc); } @@ -731,8 +674,8 @@ void HELPER(gvec_sar8i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec8)) { - *(svec8 *)(d + i) = *(svec8 *)(a + i) >> shift; + for (i = 0; i < oprsz; i += sizeof(uint8_t)) { + *(int8_t *)(d + i) = *(int8_t *)(a + i) >> shift; } clear_high(d, oprsz, desc); } @@ -743,8 +686,8 @@ void HELPER(gvec_sar16i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec16)) { - *(svec16 *)(d + i) = *(svec16 *)(a + i) >> shift; + for (i = 0; i < oprsz; i += sizeof(uint16_t)) { + *(int16_t *)(d + i) = *(int16_t *)(a + i) >> shift; } clear_high(d, oprsz, desc); } @@ -755,8 +698,8 @@ void HELPER(gvec_sar32i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec32)) { - *(svec32 *)(d + i) = *(svec32 *)(a + i) >> shift; + for (i = 0; i < oprsz; i += sizeof(uint32_t)) { + *(int32_t *)(d + i) = *(int32_t *)(a + i) >> shift; } clear_high(d, oprsz, desc); } @@ -767,8 +710,8 @@ void HELPER(gvec_sar64i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(svec64 *)(d + i) = *(svec64 *)(a + i) >> shift; + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(int64_t *)(d + i) = *(int64_t *)(a + i) >> shift; } clear_high(d, oprsz, desc); } @@ -917,39 +860,30 @@ void HELPER(gvec_sar64v)(void *d, void *a, void *b, uint32_t desc) clear_high(d, oprsz, desc); } -/* If vectors are enabled, the compiler fills in -1 for true. - Otherwise, we must take care of this by hand. */ -#ifdef CONFIG_VECTOR16 -# define DO_CMP0(X) X -#else -# define DO_CMP0(X) -(X) -#endif - #define DO_CMP1(NAME, TYPE, OP) \ void HELPER(NAME)(void *d, void *a, void *b, uint32_t desc) \ { \ intptr_t oprsz = simd_oprsz(desc); \ intptr_t i; \ for (i = 0; i < oprsz; i += sizeof(TYPE)) { \ - *(TYPE *)(d + i) = DO_CMP0(*(TYPE *)(a + i) OP *(TYPE *)(b + i)); \ + *(TYPE *)(d + i) = -(*(TYPE *)(a + i) OP *(TYPE *)(b + i)); \ } \ clear_high(d, oprsz, desc); \ } #define DO_CMP2(SZ) \ - DO_CMP1(gvec_eq##SZ, vec##SZ, ==) \ - DO_CMP1(gvec_ne##SZ, vec##SZ, !=) \ - DO_CMP1(gvec_lt##SZ, svec##SZ, <) \ - DO_CMP1(gvec_le##SZ, svec##SZ, <=) \ - DO_CMP1(gvec_ltu##SZ, vec##SZ, <) \ - DO_CMP1(gvec_leu##SZ, vec##SZ, <=) + DO_CMP1(gvec_eq##SZ, uint##SZ##_t, ==) \ + DO_CMP1(gvec_ne##SZ, uint##SZ##_t, !=) \ + DO_CMP1(gvec_lt##SZ, int##SZ##_t, <) \ + DO_CMP1(gvec_le##SZ, int##SZ##_t, <=) \ + DO_CMP1(gvec_ltu##SZ, uint##SZ##_t, <) \ + DO_CMP1(gvec_leu##SZ, uint##SZ##_t, <=) DO_CMP2(8) DO_CMP2(16) DO_CMP2(32) DO_CMP2(64) -#undef DO_CMP0 #undef DO_CMP1 #undef DO_CMP2 @@ -1450,11 +1384,11 @@ void HELPER(gvec_bitsel)(void *d, void *a, void *b, void *c, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - vec64 aa = *(vec64 *)(a + i); - vec64 bb = *(vec64 *)(b + i); - vec64 cc = *(vec64 *)(c + i); - *(vec64 *)(d + i) = (bb & aa) | (cc & ~aa); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + uint64_t aa = *(uint64_t *)(a + i); + uint64_t bb = *(uint64_t *)(b + i); + uint64_t cc = *(uint64_t *)(c + i); + *(uint64_t *)(d + i) = (bb & aa) | (cc & ~aa); } clear_high(d, oprsz, desc); } diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c index c3a6368dfc..4c8c375172 100644 --- a/block/monitor/block-hmp-cmds.c +++ b/block/monitor/block-hmp-cmds.c @@ -838,10 +838,8 @@ void hmp_info_blockstats(Monitor *mon, const QDict *qdict) void hmp_info_block_jobs(Monitor *mon, const QDict *qdict) { BlockJobInfoList *list; - Error *err = NULL; - list = qmp_query_block_jobs(&err); - assert(!err); + list = qmp_query_block_jobs(&error_abort); if (!list) { monitor_printf(mon, "No active jobs\n"); @@ -303,6 +303,7 @@ libs_qga="" debug_info="yes" stack_protector="" use_containers="yes" +gdb_bin=$(command -v "gdb") if test -e "$source_path/.git" then @@ -1598,6 +1599,8 @@ for opt do ;; --disable-fuzzing) fuzzing=no ;; + --gdb=*) gdb_bin="$optarg" + ;; *) echo "ERROR: unknown option $opt" echo "Try '$0 --help' for more information" @@ -1783,6 +1786,7 @@ Advanced options (experts only): --enable-plugins enable plugins via shared library loading --disable-containers don't use containers for cross-building + --gdb=GDB-path gdb to use for gdbstub tests [$gdb_bin] Optional features, enabled with --enable-FEATURE and disabled with --disable-FEATURE, default is enabled if available: @@ -5761,58 +5765,6 @@ if test "$plugins" = "yes" && fi ######################################## -# See if 16-byte vector operations are supported. -# Even without a vector unit the compiler may expand these. -# There is a bug in old GCC for PPC that crashes here. -# Unfortunately it's the system compiler for Centos 7. - -cat > $TMPC << EOF -typedef unsigned char U1 __attribute__((vector_size(16))); -typedef unsigned short U2 __attribute__((vector_size(16))); -typedef unsigned int U4 __attribute__((vector_size(16))); -typedef unsigned long long U8 __attribute__((vector_size(16))); -typedef signed char S1 __attribute__((vector_size(16))); -typedef signed short S2 __attribute__((vector_size(16))); -typedef signed int S4 __attribute__((vector_size(16))); -typedef signed long long S8 __attribute__((vector_size(16))); -static U1 a1, b1; -static U2 a2, b2; -static U4 a4, b4; -static U8 a8, b8; -static S1 c1; -static S2 c2; -static S4 c4; -static S8 c8; -static int i; -void helper(void *d, void *a, int shift, int i); -void helper(void *d, void *a, int shift, int i) -{ - *(U1 *)(d + i) = *(U1 *)(a + i) << shift; - *(U2 *)(d + i) = *(U2 *)(a + i) << shift; - *(U4 *)(d + i) = *(U4 *)(a + i) << shift; - *(U8 *)(d + i) = *(U8 *)(a + i) << shift; -} -int main(void) -{ - a1 += b1; a2 += b2; a4 += b4; a8 += b8; - a1 -= b1; a2 -= b2; a4 -= b4; a8 -= b8; - a1 *= b1; a2 *= b2; a4 *= b4; a8 *= b8; - a1 &= b1; a2 &= b2; a4 &= b4; a8 &= b8; - a1 |= b1; a2 |= b2; a4 |= b4; a8 |= b8; - a1 ^= b1; a2 ^= b2; a4 ^= b4; a8 ^= b8; - a1 <<= i; a2 <<= i; a4 <<= i; a8 <<= i; - a1 >>= i; a2 >>= i; a4 >>= i; a8 >>= i; - c1 >>= i; c2 >>= i; c4 >>= i; c8 >>= i; - return 0; -} -EOF - -vector16=no -if compile_prog "" "" ; then - vector16=yes -fi - -######################################## # See if __attribute__((alias)) is supported. # This false for Xcode 9, but has been remedied for Xcode 10. # Unfortunately, travis uses Xcode 9 by default. @@ -6785,6 +6737,7 @@ echo "libudev $libudev" echo "default devices $default_devices" echo "plugin support $plugins" echo "fuzzing support $fuzzing" +echo "gdb $gdb_bin" if test "$supported_cpu" = "no"; then echo @@ -7441,10 +7394,6 @@ if test "$atomic64" = "yes" ; then echo "CONFIG_ATOMIC64=y" >> $config_host_mak fi -if test "$vector16" = "yes" ; then - echo "CONFIG_VECTOR16=y" >> $config_host_mak -fi - if test "$attralias" = "yes" ; then echo "CONFIG_ATTRIBUTE_ALIAS=y" >> $config_host_mak fi @@ -7666,6 +7615,10 @@ if test "$plugins" = "yes" ; then fi fi +if test -n "$gdb_bin" ; then + echo "HAVE_GDB_BIN=$gdb_bin" >> $config_host_mak +fi + if test "$tcg_interpreter" = "yes"; then QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/tci $QEMU_INCLUDES" elif test "$ARCH" = "sparc64" ; then diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt index 59d6973e1e..1967adfa92 100644 --- a/docs/devel/qapi-code-gen.txt +++ b/docs/devel/qapi-code-gen.txt @@ -172,7 +172,8 @@ Syntax: ENUM = { 'enum': STRING, 'data': [ ENUM-VALUE, ... ], '*prefix': STRING, - '*if': COND } + '*if': COND, + '*features': FEATURES } ENUM-VALUE = STRING | { 'name': STRING, '*if': COND } @@ -207,6 +208,9 @@ the job satisfactorily. The optional 'if' member specifies a conditional. See "Configuring the schema" below for more on this. +The optional 'features' member specifies features. See "Features" +below for more on this. + === Type references and array types === @@ -230,7 +234,9 @@ Syntax: '*features': FEATURES } MEMBERS = { MEMBER, ... } MEMBER = STRING : TYPE-REF - | STRING : { 'type': TYPE-REF, '*if': COND } + | STRING : { 'type': TYPE-REF, + '*if': COND, + '*features': FEATURES } Member 'struct' names the struct type. @@ -279,12 +285,14 @@ below for more on this. Syntax: UNION = { 'union': STRING, 'data': BRANCHES, - '*if': COND } + '*if': COND, + '*features': FEATURES } | { 'union': STRING, 'data': BRANCHES, 'base': ( MEMBERS | STRING ), 'discriminator': STRING, - '*if': COND } + '*if': COND, + '*features': FEATURES } BRANCHES = { BRANCH, ... } BRANCH = STRING : TYPE-REF | STRING : { 'type': TYPE-REF, '*if': COND } @@ -391,15 +399,19 @@ is identical on the wire to: The optional 'if' member specifies a conditional. See "Configuring the schema" below for more on this. +The optional 'features' member specifies features. See "Features" +below for more on this. + === Alternate types === Syntax: ALTERNATE = { 'alternate': STRING, 'data': ALTERNATIVES, - '*if': COND } + '*if': COND, + '*features': FEATURES } ALTERNATIVES = { ALTERNATIVE, ... } - ALTERNATIVE = STRING : TYPE-REF + ALTERNATIVE = STRING : STRING | STRING : { 'type': STRING, '*if': COND } Member 'alternate' names the alternate type. @@ -441,6 +453,9 @@ following example objects: The optional 'if' member specifies a conditional. See "Configuring the schema" below for more on this. +The optional 'features' member specifies features. See "Features" +below for more on this. + === Commands === @@ -584,6 +599,9 @@ started with --preconfig. The optional 'if' member specifies a conditional. See "Configuring the schema" below for more on this. +The optional 'features' member specifies features. See "Features" +below for more on this. + === Events === @@ -595,7 +613,8 @@ Syntax: 'data': STRING, 'boxed': true, ) - '*if': COND } + '*if': COND, + '*features': FEATURES } Member 'event' names the event. This is the event name used in the Client JSON Protocol. @@ -628,6 +647,9 @@ complex type. See section "Code generated for events" for examples. The optional 'if' member specifies a conditional. See "Configuring the schema" below for more on this. +The optional 'features' member specifies features. See "Features" +below for more on this. + === Features === @@ -642,13 +664,8 @@ that previously resulted in an error). QMP clients may still need to know whether the extension is available. For this purpose, a list of features can be specified for a command or -struct type. This is exposed to the client as a list of strings, -where each string signals that this build of QEMU shows a certain -behaviour. - -Each member of the 'features' array defines a feature. It can either -be { 'name': STRING, '*if': COND }, or STRING, which is shorthand for -{ 'name': STRING }. +struct type. Each list member can either be { 'name': STRING, '*if': +COND }, or STRING, which is shorthand for { 'name': STRING }. The optional 'if' member specifies a conditional. See "Configuring the schema" below for more on this. @@ -659,6 +676,18 @@ Example: 'data': { 'number': 'int' }, 'features': [ 'allow-negative-numbers' ] } +The feature strings are exposed to clients in introspection, as +explained in section "Client JSON Protocol introspection". + +Intended use is to have each feature string signal that this build of +QEMU shows a certain behaviour. + + +==== Special features ==== + +Feature "deprecated" marks a command, event, or struct member as +deprecated. It is not supported elsewhere so far. + === Naming rules and reserved names === @@ -965,8 +994,9 @@ schema, along with the SchemaInfo type. This text attempts to give an overview how things work. For details you need to consult the QAPI schema. -SchemaInfo objects have common members "name" and "meta-type", and -additional variant members depending on the value of meta-type. +SchemaInfo objects have common members "name", "meta-type", +"features", and additional variant members depending on the value of +meta-type. Each SchemaInfo object describes a wire ABI entity of a certain meta-type: a command, event or one of several kinds of type. @@ -979,6 +1009,9 @@ not. Therefore, the SchemaInfo for types have auto-generated meaningless names. For readability, the examples in this section use meaningful type names instead. +Optional member "features" exposes the entity's feature strings as a +JSON array of strings. + To examine a type, start with a command or event using it, then follow references by name. @@ -988,9 +1021,9 @@ The SchemaInfo for a command has meta-type "command", and variant members "arg-type", "ret-type" and "allow-oob". On the wire, the "arguments" member of a client's "execute" command must conform to the object type named by "arg-type". The "return" member that the server -passes in a success response conforms to the type named by -"ret-type". When "allow-oob" is set, it means the command supports -out-of-band execution. +passes in a success response conforms to the type named by "ret-type". +When "allow-oob" is true, it means the command supports out-of-band +execution. It defaults to false. If the command takes no arguments, "arg-type" names an object type without members. Likewise, if the command returns nothing, "ret-type" @@ -1047,6 +1080,16 @@ Example: the SchemaInfo for MyType from section Struct types { "name": "member2", "type": "int" }, { "name": "member3", "type": "str", "default": null } ] } +"features" exposes the command's feature strings as a JSON array of +strings. + +Example: the SchemaInfo for TestType from section Features: + + { "name": "TestType", "meta-type": "object", + "members": [ + { "name": "number", "type": "int" } ], + "features": ["allow-negative-numbers"] } + "tag" is the name of the common member serving as type tag. "variants" is a JSON array describing the object's variant members. Each element is a JSON object with members "case" (the value of type diff --git a/docs/system/deprecated.rst b/docs/system/deprecated.rst index 0838338d8f..c633fe2bef 100644 --- a/docs/system/deprecated.rst +++ b/docs/system/deprecated.rst @@ -180,27 +180,67 @@ QEMU Machine Protocol (QMP) commands Use ``blockdev-change-medium`` or ``change-vnc-password`` instead. +``blockdev-open-tray``, ``blockdev-close-tray`` argument ``device`` (since 2.8.0) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use argument ``id`` instead. + +``eject`` argument ``device`` (since 2.8.0) +''''''''''''''''''''''''''''''''''''''''''' + +Use argument ``id`` instead. + +``blockdev-change-medium`` argument ``device`` (since 2.8.0) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use argument ``id`` instead. + +``block_set_io_throttle`` argument ``device`` (since 2.8.0) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use argument ``id`` instead. + ``migrate_set_downtime`` and ``migrate_set_speed`` (since 2.8.0) '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Use ``migrate-set-parameters`` instead. +``query-named-block-nodes`` result ``encryption_key_missing`` (since 2.10.0) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Always false. + +``query-block`` result ``inserted.encryption_key_missing`` (since 2.10.0) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Always false. + +``blockdev-add`` empty string argument ``backing`` (since 2.10.0) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use argument value ``null`` instead. + ``migrate-set-cache-size`` and ``query-migrate-cache-size`` (since 2.11.0) '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Use ``migrate-set-parameters`` and ``query-migrate-parameters`` instead. +``block-commit`` arguments ``base`` and ``top`` (since 3.1.0) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use arguments ``base-node`` and ``top-node`` instead. + ``object-add`` option ``props`` (since 5.0) ''''''''''''''''''''''''''''''''''''''''''' Specify the properties for the object as top-level arguments instead. -``query-block`` result field ``dirty-bitmaps[i].status`` (since 4.0) -'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' +``query-named-block-nodes`` and ``query-block`` result dirty-bitmaps[i].status (since 4.0) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' The ``status`` field of the ``BlockDirtyInfo`` structure, returned by -the query-block command is deprecated. Two new boolean fields, -``recording`` and ``busy`` effectively replace it. +these commands is deprecated. Two new boolean fields, ``recording`` and +``busy`` effectively replace it. ``query-block`` result field ``dirty-bitmaps`` (Since 4.2) '''''''''''''''''''''''''''''''''''''''''''''''''''''''''' @@ -289,6 +329,13 @@ The RISC-V no MMU cpus have been depcreated. The two CPUs: ``rv32imacu-nommu`` a ``rv64imacu-nommu`` should no longer be used. Instead the MMU status can be specified via the CPU ``mmu`` option when using the ``rv32`` or ``rv64`` CPUs. +``compat`` property of server class POWER CPUs (since 5.0) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The ``compat`` property used to set backwards compatibility modes for +the processor has been deprecated. The ``max-cpu-compat`` property of +the ``pseries`` machine type should be used instead. + System emulator devices ----------------------- @@ -319,8 +319,8 @@ static int gdb_signal_to_target (int sig) typedef struct GDBRegisterState { int base_reg; int num_regs; - gdb_reg_cb get_reg; - gdb_reg_cb set_reg; + gdb_get_reg_cb get_reg; + gdb_set_reg_cb set_reg; const char *xml; struct GDBRegisterState *next; } GDBRegisterState; @@ -342,6 +342,7 @@ enum RSState { RS_CHKSUM2, }; typedef struct GDBState { + bool init; /* have we been initialised? */ CPUState *c_cpu; /* current CPU for step/continue ops */ CPUState *g_cpu; /* current CPU for other ops */ CPUState *query_cpu; /* for q{f|s}ThreadInfo */ @@ -350,8 +351,7 @@ typedef struct GDBState { int line_buf_index; int line_sum; /* running checksum */ int line_csum; /* checksum at the end of the packet */ - uint8_t last_packet[MAX_PACKET_LENGTH + 4]; - int last_packet_len; + GByteArray *last_packet; int signal; #ifdef CONFIG_USER_ONLY int fd; @@ -365,6 +365,8 @@ typedef struct GDBState { int process_num; char syscall_buf[256]; gdb_syscall_complete_cb current_syscall_cb; + GString *str_buf; + GByteArray *mem_buf; } GDBState; /* By default use no IRQs and no timers while single stepping so as to @@ -372,7 +374,26 @@ typedef struct GDBState { */ static int sstep_flags = SSTEP_ENABLE|SSTEP_NOIRQ|SSTEP_NOTIMER; -static GDBState *gdbserver_state; +static GDBState gdbserver_state; + +static void init_gdbserver_state(void) +{ + g_assert(!gdbserver_state.init); + memset(&gdbserver_state, 0, sizeof(GDBState)); + gdbserver_state.init = true; + gdbserver_state.str_buf = g_string_new(NULL); + gdbserver_state.mem_buf = g_byte_array_sized_new(MAX_PACKET_LENGTH); + gdbserver_state.last_packet = g_byte_array_sized_new(MAX_PACKET_LENGTH + 4); +} + +#ifndef CONFIG_USER_ONLY +static void reset_gdbserver_state(void) +{ + g_free(gdbserver_state.processes); + gdbserver_state.processes = NULL; + gdbserver_state.process_num = 0; +} +#endif bool gdb_has_xml; @@ -380,21 +401,21 @@ bool gdb_has_xml; /* XXX: This is not thread safe. Do we care? */ static int gdbserver_fd = -1; -static int get_char(GDBState *s) +static int get_char(void) { uint8_t ch; int ret; for(;;) { - ret = qemu_recv(s->fd, &ch, 1, 0); + ret = qemu_recv(gdbserver_state.fd, &ch, 1, 0); if (ret < 0) { if (errno == ECONNRESET) - s->fd = -1; + gdbserver_state.fd = -1; if (errno != EINTR) return -1; } else if (ret == 0) { - close(s->fd); - s->fd = -1; + close(gdbserver_state.fd); + gdbserver_state.fd = -1; return -1; } else { break; @@ -425,18 +446,18 @@ int use_gdb_syscalls(void) /* -semihosting-config target=auto */ /* On the first call check if gdb is connected and remember. */ if (gdb_syscall_mode == GDB_SYS_UNKNOWN) { - gdb_syscall_mode = (gdbserver_state ? GDB_SYS_ENABLED - : GDB_SYS_DISABLED); + gdb_syscall_mode = gdbserver_state.init ? + GDB_SYS_ENABLED : GDB_SYS_DISABLED; } return gdb_syscall_mode == GDB_SYS_ENABLED; } /* Resume execution. */ -static inline void gdb_continue(GDBState *s) +static inline void gdb_continue(void) { #ifdef CONFIG_USER_ONLY - s->running_state = 1; + gdbserver_state.running_state = 1; trace_gdbstub_op_continue(); #else if (!runstate_needs_reset()) { @@ -450,7 +471,7 @@ static inline void gdb_continue(GDBState *s) * Resume execution, per CPU actions. For user-mode emulation it's * equivalent to gdb_continue. */ -static int gdb_continue_partial(GDBState *s, char *newstates) +static int gdb_continue_partial(char *newstates) { CPUState *cpu; int res = 0; @@ -465,7 +486,7 @@ static int gdb_continue_partial(GDBState *s, char *newstates) cpu_single_step(cpu, sstep_flags); } } - s->running_state = 1; + gdbserver_state.running_state = 1; #else int flag = 0; @@ -503,13 +524,13 @@ static int gdb_continue_partial(GDBState *s, char *newstates) return res; } -static void put_buffer(GDBState *s, const uint8_t *buf, int len) +static void put_buffer(const uint8_t *buf, int len) { #ifdef CONFIG_USER_ONLY int ret; while (len > 0) { - ret = send(s->fd, buf, len, 0); + ret = send(gdbserver_state.fd, buf, len, 0); if (ret < 0) { if (errno != EINTR) return; @@ -521,7 +542,7 @@ static void put_buffer(GDBState *s, const uint8_t *buf, int len) #else /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(&s->chr, buf, len); + qemu_chr_fe_write_all(&gdbserver_state.chr, buf, len); #endif } @@ -546,25 +567,24 @@ static inline int tohex(int v) } /* writes 2*len+1 bytes in buf */ -static void memtohex(char *buf, const uint8_t *mem, int len) +static void memtohex(GString *buf, const uint8_t *mem, int len) { int i, c; - char *q; - q = buf; for(i = 0; i < len; i++) { c = mem[i]; - *q++ = tohex(c >> 4); - *q++ = tohex(c & 0xf); + g_string_append_c(buf, tohex(c >> 4)); + g_string_append_c(buf, tohex(c & 0xf)); } - *q = '\0'; + g_string_append_c(buf, '\0'); } -static void hextomem(uint8_t *mem, const char *buf, int len) +static void hextomem(GByteArray *mem, const char *buf, int len) { int i; for(i = 0; i < len; i++) { - mem[i] = (fromhex(buf[0]) << 4) | fromhex(buf[1]); + guint8 byte = fromhex(buf[0]) << 4 | fromhex(buf[1]); + g_byte_array_append(mem, &byte, 1); buf += 2; } } @@ -603,33 +623,35 @@ static void hexdump(const char *buf, int len, } /* return -1 if error, 0 if OK */ -static int put_packet_binary(GDBState *s, const char *buf, int len, bool dump) +static int put_packet_binary(const char *buf, int len, bool dump) { int csum, i; - uint8_t *p; + uint8_t footer[3]; if (dump && trace_event_get_state_backends(TRACE_GDBSTUB_IO_BINARYREPLY)) { hexdump(buf, len, trace_gdbstub_io_binaryreply); } for(;;) { - p = s->last_packet; - *(p++) = '$'; - memcpy(p, buf, len); - p += len; + g_byte_array_set_size(gdbserver_state.last_packet, 0); + g_byte_array_append(gdbserver_state.last_packet, + (const uint8_t *) "$", 1); + g_byte_array_append(gdbserver_state.last_packet, + (const uint8_t *) buf, len); csum = 0; for(i = 0; i < len; i++) { csum += buf[i]; } - *(p++) = '#'; - *(p++) = tohex((csum >> 4) & 0xf); - *(p++) = tohex((csum) & 0xf); + footer[0] = '#'; + footer[1] = tohex((csum >> 4) & 0xf); + footer[2] = tohex((csum) & 0xf); + g_byte_array_append(gdbserver_state.last_packet, footer, 3); - s->last_packet_len = p - s->last_packet; - put_buffer(s, (uint8_t *)s->last_packet, s->last_packet_len); + put_buffer(gdbserver_state.last_packet->data, + gdbserver_state.last_packet->len); #ifdef CONFIG_USER_ONLY - i = get_char(s); + i = get_char(); if (i < 0) return -1; if (i == '+') @@ -642,65 +664,69 @@ static int put_packet_binary(GDBState *s, const char *buf, int len, bool dump) } /* return -1 if error, 0 if OK */ -static int put_packet(GDBState *s, const char *buf) +static int put_packet(const char *buf) { trace_gdbstub_io_reply(buf); - return put_packet_binary(s, buf, strlen(buf), false); + return put_packet_binary(buf, strlen(buf), false); +} + +static void put_strbuf(void) +{ + put_packet(gdbserver_state.str_buf->str); } /* Encode data using the encoding for 'x' packets. */ -static int memtox(char *buf, const char *mem, int len) +static void memtox(GString *buf, const char *mem, int len) { - char *p = buf; char c; while (len--) { c = *(mem++); switch (c) { case '#': case '$': case '*': case '}': - *(p++) = '}'; - *(p++) = c ^ 0x20; + g_string_append_c(buf, '}'); + g_string_append_c(buf, c ^ 0x20); break; default: - *(p++) = c; + g_string_append_c(buf, c); break; } } - return p - buf; } -static uint32_t gdb_get_cpu_pid(const GDBState *s, CPUState *cpu) +static uint32_t gdb_get_cpu_pid(CPUState *cpu) { /* TODO: In user mode, we should use the task state PID */ if (cpu->cluster_index == UNASSIGNED_CLUSTER_INDEX) { /* Return the default process' PID */ - return s->processes[s->process_num - 1].pid; + int index = gdbserver_state.process_num - 1; + return gdbserver_state.processes[index].pid; } return cpu->cluster_index + 1; } -static GDBProcess *gdb_get_process(const GDBState *s, uint32_t pid) +static GDBProcess *gdb_get_process(uint32_t pid) { int i; if (!pid) { /* 0 means any process, we take the first one */ - return &s->processes[0]; + return &gdbserver_state.processes[0]; } - for (i = 0; i < s->process_num; i++) { - if (s->processes[i].pid == pid) { - return &s->processes[i]; + for (i = 0; i < gdbserver_state.process_num; i++) { + if (gdbserver_state.processes[i].pid == pid) { + return &gdbserver_state.processes[i]; } } return NULL; } -static GDBProcess *gdb_get_cpu_process(const GDBState *s, CPUState *cpu) +static GDBProcess *gdb_get_cpu_process(CPUState *cpu) { - return gdb_get_process(s, gdb_get_cpu_pid(s, cpu)); + return gdb_get_process(gdb_get_cpu_pid(cpu)); } static CPUState *find_cpu(uint32_t thread_id) @@ -716,13 +742,12 @@ static CPUState *find_cpu(uint32_t thread_id) return NULL; } -static CPUState *get_first_cpu_in_process(const GDBState *s, - GDBProcess *process) +static CPUState *get_first_cpu_in_process(GDBProcess *process) { CPUState *cpu; CPU_FOREACH(cpu) { - if (gdb_get_cpu_pid(s, cpu) == process->pid) { + if (gdb_get_cpu_pid(cpu) == process->pid) { return cpu; } } @@ -730,13 +755,13 @@ static CPUState *get_first_cpu_in_process(const GDBState *s, return NULL; } -static CPUState *gdb_next_cpu_in_process(const GDBState *s, CPUState *cpu) +static CPUState *gdb_next_cpu_in_process(CPUState *cpu) { - uint32_t pid = gdb_get_cpu_pid(s, cpu); + uint32_t pid = gdb_get_cpu_pid(cpu); cpu = CPU_NEXT(cpu); while (cpu) { - if (gdb_get_cpu_pid(s, cpu) == pid) { + if (gdb_get_cpu_pid(cpu) == pid) { break; } @@ -747,12 +772,12 @@ static CPUState *gdb_next_cpu_in_process(const GDBState *s, CPUState *cpu) } /* Return the cpu following @cpu, while ignoring unattached processes. */ -static CPUState *gdb_next_attached_cpu(const GDBState *s, CPUState *cpu) +static CPUState *gdb_next_attached_cpu(CPUState *cpu) { cpu = CPU_NEXT(cpu); while (cpu) { - if (gdb_get_cpu_process(s, cpu)->attached) { + if (gdb_get_cpu_process(cpu)->attached) { break; } @@ -763,29 +788,29 @@ static CPUState *gdb_next_attached_cpu(const GDBState *s, CPUState *cpu) } /* Return the first attached cpu */ -static CPUState *gdb_first_attached_cpu(const GDBState *s) +static CPUState *gdb_first_attached_cpu(void) { CPUState *cpu = first_cpu; - GDBProcess *process = gdb_get_cpu_process(s, cpu); + GDBProcess *process = gdb_get_cpu_process(cpu); if (!process->attached) { - return gdb_next_attached_cpu(s, cpu); + return gdb_next_attached_cpu(cpu); } return cpu; } -static CPUState *gdb_get_cpu(const GDBState *s, uint32_t pid, uint32_t tid) +static CPUState *gdb_get_cpu(uint32_t pid, uint32_t tid) { GDBProcess *process; CPUState *cpu; if (!pid && !tid) { /* 0 means any process/thread, we take the first attached one */ - return gdb_first_attached_cpu(s); + return gdb_first_attached_cpu(); } else if (pid && !tid) { /* any thread in a specific process */ - process = gdb_get_process(s, pid); + process = gdb_get_process(pid); if (process == NULL) { return NULL; @@ -795,7 +820,7 @@ static CPUState *gdb_get_cpu(const GDBState *s, uint32_t pid, uint32_t tid) return NULL; } - return get_first_cpu_in_process(s, process); + return get_first_cpu_in_process(process); } else { /* a specific thread */ cpu = find_cpu(tid); @@ -804,7 +829,7 @@ static CPUState *gdb_get_cpu(const GDBState *s, uint32_t pid, uint32_t tid) return NULL; } - process = gdb_get_cpu_process(s, cpu); + process = gdb_get_cpu_process(cpu); if (pid && process->pid != pid) { return NULL; @@ -818,13 +843,13 @@ static CPUState *gdb_get_cpu(const GDBState *s, uint32_t pid, uint32_t tid) } } -static const char *get_feature_xml(const GDBState *s, const char *p, - const char **newp, GDBProcess *process) +static const char *get_feature_xml(const char *p, const char **newp, + GDBProcess *process) { size_t len; int i; const char *name; - CPUState *cpu = get_first_cpu_in_process(s, process); + CPUState *cpu = get_first_cpu_in_process(process); CPUClass *cc = CPU_GET_CLASS(cpu); len = 0; @@ -881,19 +906,19 @@ static const char *get_feature_xml(const GDBState *s, const char *p, return name ? xml_builtin[i][1] : NULL; } -static int gdb_read_register(CPUState *cpu, uint8_t *mem_buf, int reg) +static int gdb_read_register(CPUState *cpu, GByteArray *buf, int reg) { CPUClass *cc = CPU_GET_CLASS(cpu); CPUArchState *env = cpu->env_ptr; GDBRegisterState *r; if (reg < cc->gdb_num_core_regs) { - return cc->gdb_read_register(cpu, mem_buf, reg); + return cc->gdb_read_register(cpu, buf, reg); } for (r = cpu->gdb_regs; r; r = r->next) { if (r->base_reg <= reg && reg < r->base_reg + r->num_regs) { - return r->get_reg(env, mem_buf, reg - r->base_reg); + return r->get_reg(env, buf, reg - r->base_reg); } } return 0; @@ -924,7 +949,7 @@ static int gdb_write_register(CPUState *cpu, uint8_t *mem_buf, int reg) */ void gdb_register_coprocessor(CPUState *cpu, - gdb_reg_cb get_reg, gdb_reg_cb set_reg, + gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg, int num_regs, const char *xml, int g_pos) { GDBRegisterState *s; @@ -984,7 +1009,7 @@ static int gdb_breakpoint_insert(int type, target_ulong addr, target_ulong len) int err = 0; if (kvm_enabled()) { - return kvm_insert_breakpoint(gdbserver_state->c_cpu, addr, len, type); + return kvm_insert_breakpoint(gdbserver_state.c_cpu, addr, len, type); } switch (type) { @@ -1021,7 +1046,7 @@ static int gdb_breakpoint_remove(int type, target_ulong addr, target_ulong len) int err = 0; if (kvm_enabled()) { - return kvm_remove_breakpoint(gdbserver_state->c_cpu, addr, len, type); + return kvm_remove_breakpoint(gdbserver_state.c_cpu, addr, len, type); } switch (type) { @@ -1059,13 +1084,13 @@ static inline void gdb_cpu_breakpoint_remove_all(CPUState *cpu) #endif } -static void gdb_process_breakpoint_remove_all(const GDBState *s, GDBProcess *p) +static void gdb_process_breakpoint_remove_all(GDBProcess *p) { - CPUState *cpu = get_first_cpu_in_process(s, p); + CPUState *cpu = get_first_cpu_in_process(p); while (cpu) { gdb_cpu_breakpoint_remove_all(cpu); - cpu = gdb_next_cpu_in_process(s, cpu); + cpu = gdb_next_cpu_in_process(cpu); } } @@ -1074,7 +1099,7 @@ static void gdb_breakpoint_remove_all(void) CPUState *cpu; if (kvm_enabled()) { - kvm_remove_all_breakpoints(gdbserver_state->c_cpu); + kvm_remove_all_breakpoints(gdbserver_state.c_cpu); return; } @@ -1083,25 +1108,22 @@ static void gdb_breakpoint_remove_all(void) } } -static void gdb_set_cpu_pc(GDBState *s, target_ulong pc) +static void gdb_set_cpu_pc(target_ulong pc) { - CPUState *cpu = s->c_cpu; + CPUState *cpu = gdbserver_state.c_cpu; cpu_synchronize_state(cpu); cpu_set_pc(cpu, pc); } -static char *gdb_fmt_thread_id(const GDBState *s, CPUState *cpu, - char *buf, size_t buf_size) +static void gdb_append_thread_id(CPUState *cpu, GString *buf) { - if (s->multiprocess) { - snprintf(buf, buf_size, "p%02x.%02x", - gdb_get_cpu_pid(s, cpu), cpu_gdb_index(cpu)); + if (gdbserver_state.multiprocess) { + g_string_append_printf(buf, "p%02x.%02x", + gdb_get_cpu_pid(cpu), cpu_gdb_index(cpu)); } else { - snprintf(buf, buf_size, "%02x", cpu_gdb_index(cpu)); + g_string_append_printf(buf, "%02x", cpu_gdb_index(cpu)); } - - return buf; } typedef enum GDBThreadIdKind { @@ -1163,7 +1185,7 @@ static GDBThreadIdKind read_thread_id(const char *buf, const char **end_buf, * returns -ENOTSUP if a command is unsupported, -EINVAL or -ERANGE if there is * a format error, 0 on success. */ -static int gdb_handle_vcont(GDBState *s, const char *p) +static int gdb_handle_vcont(const char *p) { int res, signal = 0; char cur_action; @@ -1238,36 +1260,36 @@ static int gdb_handle_vcont(GDBState *s, const char *p) goto out; case GDB_ALL_PROCESSES: - cpu = gdb_first_attached_cpu(s); + cpu = gdb_first_attached_cpu(); while (cpu) { if (newstates[cpu->cpu_index] == 1) { newstates[cpu->cpu_index] = cur_action; } - cpu = gdb_next_attached_cpu(s, cpu); + cpu = gdb_next_attached_cpu(cpu); } break; case GDB_ALL_THREADS: - process = gdb_get_process(s, pid); + process = gdb_get_process(pid); if (!process->attached) { res = -EINVAL; goto out; } - cpu = get_first_cpu_in_process(s, process); + cpu = get_first_cpu_in_process(process); while (cpu) { if (newstates[cpu->cpu_index] == 1) { newstates[cpu->cpu_index] = cur_action; } - cpu = gdb_next_cpu_in_process(s, cpu); + cpu = gdb_next_cpu_in_process(cpu); } break; case GDB_ONE_THREAD: - cpu = gdb_get_cpu(s, pid, tid); + cpu = gdb_get_cpu(pid, tid); /* invalid CPU/thread specified */ if (!cpu) { @@ -1282,8 +1304,8 @@ static int gdb_handle_vcont(GDBState *s, const char *p) break; } } - s->signal = signal; - gdb_continue_partial(s, newstates); + gdbserver_state.signal = signal; + gdb_continue_partial(newstates); out: g_free(newstates); @@ -1392,11 +1414,8 @@ static int cmd_parse_params(const char *data, const char *schema, } typedef struct GdbCmdContext { - GDBState *s; GdbCmdVariant *params; int num_params; - uint8_t mem_buf[MAX_PACKET_LENGTH]; - char str_buf[MAX_PACKET_LENGTH + 1]; } GdbCmdContext; typedef void (*GdbCmdHandler)(GdbCmdContext *gdb_ctx, void *user_ctx); @@ -1436,7 +1455,7 @@ static inline int startswith(const char *string, const char *pattern) return !strncmp(string, pattern, strlen(pattern)); } -static int process_string_cmd(GDBState *s, void *user_ctx, const char *data, +static int process_string_cmd(void *user_ctx, const char *data, const GdbCmdParseEntry *cmds, int num_cmds) { int i, schema_len, max_num_params = 0; @@ -1473,7 +1492,6 @@ static int process_string_cmd(GDBState *s, void *user_ctx, const char *data, return -1; } - gdb_ctx.s = s; cmd->handler(&gdb_ctx, user_ctx); return 0; } @@ -1481,53 +1499,54 @@ static int process_string_cmd(GDBState *s, void *user_ctx, const char *data, return -1; } -static void run_cmd_parser(GDBState *s, const char *data, - const GdbCmdParseEntry *cmd) +static void run_cmd_parser(const char *data, const GdbCmdParseEntry *cmd) { if (!data) { return; } + g_string_set_size(gdbserver_state.str_buf, 0); + g_byte_array_set_size(gdbserver_state.mem_buf, 0); + /* In case there was an error during the command parsing we must * send a NULL packet to indicate the command is not supported */ - if (process_string_cmd(s, NULL, data, cmd, 1)) { - put_packet(s, ""); + if (process_string_cmd(NULL, data, cmd, 1)) { + put_packet(""); } } static void handle_detach(GdbCmdContext *gdb_ctx, void *user_ctx) { GDBProcess *process; - GDBState *s = gdb_ctx->s; uint32_t pid = 1; - if (s->multiprocess) { + if (gdbserver_state.multiprocess) { if (!gdb_ctx->num_params) { - put_packet(s, "E22"); + put_packet("E22"); return; } pid = gdb_ctx->params[0].val_ul; } - process = gdb_get_process(s, pid); - gdb_process_breakpoint_remove_all(s, process); + process = gdb_get_process(pid); + gdb_process_breakpoint_remove_all(process); process->attached = false; - if (pid == gdb_get_cpu_pid(s, s->c_cpu)) { - s->c_cpu = gdb_first_attached_cpu(s); + if (pid == gdb_get_cpu_pid(gdbserver_state.c_cpu)) { + gdbserver_state.c_cpu = gdb_first_attached_cpu(); } - if (pid == gdb_get_cpu_pid(s, s->g_cpu)) { - s->g_cpu = gdb_first_attached_cpu(s); + if (pid == gdb_get_cpu_pid(gdbserver_state.g_cpu)) { + gdbserver_state.g_cpu = gdb_first_attached_cpu(); } - if (!s->c_cpu) { + if (!gdbserver_state.c_cpu) { /* No more process attached */ gdb_syscall_mode = GDB_SYS_DISABLED; - gdb_continue(s); + gdb_continue(); } - put_packet(s, "OK"); + put_packet("OK"); } static void handle_thread_alive(GdbCmdContext *gdb_ctx, void *user_ctx) @@ -1535,33 +1554,33 @@ static void handle_thread_alive(GdbCmdContext *gdb_ctx, void *user_ctx) CPUState *cpu; if (!gdb_ctx->num_params) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } if (gdb_ctx->params[0].thread_id.kind == GDB_READ_THREAD_ERR) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } - cpu = gdb_get_cpu(gdb_ctx->s, gdb_ctx->params[0].thread_id.pid, + cpu = gdb_get_cpu(gdb_ctx->params[0].thread_id.pid, gdb_ctx->params[0].thread_id.tid); if (!cpu) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } - put_packet(gdb_ctx->s, "OK"); + put_packet("OK"); } static void handle_continue(GdbCmdContext *gdb_ctx, void *user_ctx) { if (gdb_ctx->num_params) { - gdb_set_cpu_pc(gdb_ctx->s, gdb_ctx->params[0].val_ull); + gdb_set_cpu_pc(gdb_ctx->params[0].val_ull); } - gdb_ctx->s->signal = 0; - gdb_continue(gdb_ctx->s); + gdbserver_state.signal = 0; + gdb_continue(); } static void handle_cont_with_sig(GdbCmdContext *gdb_ctx, void *user_ctx) @@ -1576,11 +1595,11 @@ static void handle_cont_with_sig(GdbCmdContext *gdb_ctx, void *user_ctx) signal = gdb_ctx->params[0].val_ul; } - gdb_ctx->s->signal = gdb_signal_to_target(signal); - if (gdb_ctx->s->signal == -1) { - gdb_ctx->s->signal = 0; + gdbserver_state.signal = gdb_signal_to_target(signal); + if (gdbserver_state.signal == -1) { + gdbserver_state.signal = 0; } - gdb_continue(gdb_ctx->s); + gdb_continue(); } static void handle_set_thread(GdbCmdContext *gdb_ctx, void *user_ctx) @@ -1588,24 +1607,24 @@ static void handle_set_thread(GdbCmdContext *gdb_ctx, void *user_ctx) CPUState *cpu; if (gdb_ctx->num_params != 2) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } if (gdb_ctx->params[1].thread_id.kind == GDB_READ_THREAD_ERR) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } if (gdb_ctx->params[1].thread_id.kind != GDB_ONE_THREAD) { - put_packet(gdb_ctx->s, "OK"); + put_packet("OK"); return; } - cpu = gdb_get_cpu(gdb_ctx->s, gdb_ctx->params[1].thread_id.pid, + cpu = gdb_get_cpu(gdb_ctx->params[1].thread_id.pid, gdb_ctx->params[1].thread_id.tid); if (!cpu) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } @@ -1615,15 +1634,15 @@ static void handle_set_thread(GdbCmdContext *gdb_ctx, void *user_ctx) */ switch (gdb_ctx->params[0].opcode) { case 'c': - gdb_ctx->s->c_cpu = cpu; - put_packet(gdb_ctx->s, "OK"); + gdbserver_state.c_cpu = cpu; + put_packet("OK"); break; case 'g': - gdb_ctx->s->g_cpu = cpu; - put_packet(gdb_ctx->s, "OK"); + gdbserver_state.g_cpu = cpu; + put_packet("OK"); break; default: - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); break; } } @@ -1633,7 +1652,7 @@ static void handle_insert_bp(GdbCmdContext *gdb_ctx, void *user_ctx) int res; if (gdb_ctx->num_params != 3) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } @@ -1641,14 +1660,14 @@ static void handle_insert_bp(GdbCmdContext *gdb_ctx, void *user_ctx) gdb_ctx->params[1].val_ull, gdb_ctx->params[2].val_ull); if (res >= 0) { - put_packet(gdb_ctx->s, "OK"); + put_packet("OK"); return; } else if (res == -ENOSYS) { - put_packet(gdb_ctx->s, ""); + put_packet(""); return; } - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); } static void handle_remove_bp(GdbCmdContext *gdb_ctx, void *user_ctx) @@ -1656,7 +1675,7 @@ static void handle_remove_bp(GdbCmdContext *gdb_ctx, void *user_ctx) int res; if (gdb_ctx->num_params != 3) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } @@ -1664,14 +1683,14 @@ static void handle_remove_bp(GdbCmdContext *gdb_ctx, void *user_ctx) gdb_ctx->params[1].val_ull, gdb_ctx->params[2].val_ull); if (res >= 0) { - put_packet(gdb_ctx->s, "OK"); + put_packet("OK"); return; } else if (res == -ENOSYS) { - put_packet(gdb_ctx->s, ""); + put_packet(""); return; } - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); } /* @@ -1690,20 +1709,20 @@ static void handle_set_reg(GdbCmdContext *gdb_ctx, void *user_ctx) int reg_size; if (!gdb_has_xml) { - put_packet(gdb_ctx->s, ""); + put_packet(""); return; } if (gdb_ctx->num_params != 2) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } reg_size = strlen(gdb_ctx->params[1].data) / 2; - hextomem(gdb_ctx->mem_buf, gdb_ctx->params[1].data, reg_size); - gdb_write_register(gdb_ctx->s->g_cpu, gdb_ctx->mem_buf, + hextomem(gdbserver_state.mem_buf, gdb_ctx->params[1].data, reg_size); + gdb_write_register(gdbserver_state.g_cpu, gdbserver_state.mem_buf->data, gdb_ctx->params[0].val_ull); - put_packet(gdb_ctx->s, "OK"); + put_packet("OK"); } static void handle_get_reg(GdbCmdContext *gdb_ctx, void *user_ctx) @@ -1711,73 +1730,79 @@ static void handle_get_reg(GdbCmdContext *gdb_ctx, void *user_ctx) int reg_size; if (!gdb_has_xml) { - put_packet(gdb_ctx->s, ""); + put_packet(""); return; } if (!gdb_ctx->num_params) { - put_packet(gdb_ctx->s, "E14"); + put_packet("E14"); return; } - reg_size = gdb_read_register(gdb_ctx->s->g_cpu, gdb_ctx->mem_buf, + reg_size = gdb_read_register(gdbserver_state.g_cpu, + gdbserver_state.mem_buf, gdb_ctx->params[0].val_ull); if (!reg_size) { - put_packet(gdb_ctx->s, "E14"); + put_packet("E14"); return; + } else { + g_byte_array_set_size(gdbserver_state.mem_buf, reg_size); } - memtohex(gdb_ctx->str_buf, gdb_ctx->mem_buf, reg_size); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, reg_size); + put_strbuf(); } static void handle_write_mem(GdbCmdContext *gdb_ctx, void *user_ctx) { if (gdb_ctx->num_params != 3) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } /* hextomem() reads 2*len bytes */ if (gdb_ctx->params[1].val_ull > strlen(gdb_ctx->params[2].data) / 2) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } - hextomem(gdb_ctx->mem_buf, gdb_ctx->params[2].data, + hextomem(gdbserver_state.mem_buf, gdb_ctx->params[2].data, gdb_ctx->params[1].val_ull); - if (target_memory_rw_debug(gdb_ctx->s->g_cpu, gdb_ctx->params[0].val_ull, - gdb_ctx->mem_buf, - gdb_ctx->params[1].val_ull, true)) { - put_packet(gdb_ctx->s, "E14"); + if (target_memory_rw_debug(gdbserver_state.g_cpu, gdb_ctx->params[0].val_ull, + gdbserver_state.mem_buf->data, + gdbserver_state.mem_buf->len, true)) { + put_packet("E14"); return; } - put_packet(gdb_ctx->s, "OK"); + put_packet("OK"); } static void handle_read_mem(GdbCmdContext *gdb_ctx, void *user_ctx) { if (gdb_ctx->num_params != 2) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } /* memtohex() doubles the required space */ if (gdb_ctx->params[1].val_ull > MAX_PACKET_LENGTH / 2) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } - if (target_memory_rw_debug(gdb_ctx->s->g_cpu, gdb_ctx->params[0].val_ull, - gdb_ctx->mem_buf, - gdb_ctx->params[1].val_ull, false)) { - put_packet(gdb_ctx->s, "E14"); + g_byte_array_set_size(gdbserver_state.mem_buf, gdb_ctx->params[1].val_ull); + + if (target_memory_rw_debug(gdbserver_state.g_cpu, gdb_ctx->params[0].val_ull, + gdbserver_state.mem_buf->data, + gdbserver_state.mem_buf->len, false)) { + put_packet("E14"); return; } - memtohex(gdb_ctx->str_buf, gdb_ctx->mem_buf, gdb_ctx->params[1].val_ull); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, + gdbserver_state.mem_buf->len); + put_strbuf(); } static void handle_write_all_regs(GdbCmdContext *gdb_ctx, void *user_ctx) @@ -1790,37 +1815,40 @@ static void handle_write_all_regs(GdbCmdContext *gdb_ctx, void *user_ctx) return; } - cpu_synchronize_state(gdb_ctx->s->g_cpu); - registers = gdb_ctx->mem_buf; + cpu_synchronize_state(gdbserver_state.g_cpu); len = strlen(gdb_ctx->params[0].data) / 2; - hextomem(registers, gdb_ctx->params[0].data, len); - for (addr = 0; addr < gdb_ctx->s->g_cpu->gdb_num_g_regs && len > 0; + hextomem(gdbserver_state.mem_buf, gdb_ctx->params[0].data, len); + registers = gdbserver_state.mem_buf->data; + for (addr = 0; addr < gdbserver_state.g_cpu->gdb_num_g_regs && len > 0; addr++) { - reg_size = gdb_write_register(gdb_ctx->s->g_cpu, registers, addr); + reg_size = gdb_write_register(gdbserver_state.g_cpu, registers, addr); len -= reg_size; registers += reg_size; } - put_packet(gdb_ctx->s, "OK"); + put_packet("OK"); } static void handle_read_all_regs(GdbCmdContext *gdb_ctx, void *user_ctx) { target_ulong addr, len; - cpu_synchronize_state(gdb_ctx->s->g_cpu); + cpu_synchronize_state(gdbserver_state.g_cpu); + g_byte_array_set_size(gdbserver_state.mem_buf, 0); len = 0; - for (addr = 0; addr < gdb_ctx->s->g_cpu->gdb_num_g_regs; addr++) { - len += gdb_read_register(gdb_ctx->s->g_cpu, gdb_ctx->mem_buf + len, + for (addr = 0; addr < gdbserver_state.g_cpu->gdb_num_g_regs; addr++) { + len += gdb_read_register(gdbserver_state.g_cpu, + gdbserver_state.mem_buf, addr); } + g_assert(len == gdbserver_state.mem_buf->len); - memtohex(gdb_ctx->str_buf, gdb_ctx->mem_buf, len); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, len); + put_strbuf(); } static void handle_file_io(GdbCmdContext *gdb_ctx, void *user_ctx) { - if (gdb_ctx->num_params >= 1 && gdb_ctx->s->current_syscall_cb) { + if (gdb_ctx->num_params >= 1 && gdbserver_state.current_syscall_cb) { target_ulong ret, err; ret = (target_ulong)gdb_ctx->params[0].val_ull; @@ -1829,31 +1857,31 @@ static void handle_file_io(GdbCmdContext *gdb_ctx, void *user_ctx) } else { err = 0; } - gdb_ctx->s->current_syscall_cb(gdb_ctx->s->c_cpu, ret, err); - gdb_ctx->s->current_syscall_cb = NULL; + gdbserver_state.current_syscall_cb(gdbserver_state.c_cpu, ret, err); + gdbserver_state.current_syscall_cb = NULL; } if (gdb_ctx->num_params >= 3 && gdb_ctx->params[2].opcode == (uint8_t)'C') { - put_packet(gdb_ctx->s, "T02"); + put_packet("T02"); return; } - gdb_continue(gdb_ctx->s); + gdb_continue(); } static void handle_step(GdbCmdContext *gdb_ctx, void *user_ctx) { if (gdb_ctx->num_params) { - gdb_set_cpu_pc(gdb_ctx->s, (target_ulong)gdb_ctx->params[0].val_ull); + gdb_set_cpu_pc((target_ulong)gdb_ctx->params[0].val_ull); } - cpu_single_step(gdb_ctx->s->c_cpu, sstep_flags); - gdb_continue(gdb_ctx->s); + cpu_single_step(gdbserver_state.c_cpu, sstep_flags); + gdb_continue(); } static void handle_v_cont_query(GdbCmdContext *gdb_ctx, void *user_ctx) { - put_packet(gdb_ctx->s, "vCont;c;C;s;S"); + put_packet("vCont;c;C;s;S"); } static void handle_v_cont(GdbCmdContext *gdb_ctx, void *user_ctx) @@ -1864,11 +1892,11 @@ static void handle_v_cont(GdbCmdContext *gdb_ctx, void *user_ctx) return; } - res = gdb_handle_vcont(gdb_ctx->s, gdb_ctx->params[0].data); + res = gdb_handle_vcont(gdb_ctx->params[0].data); if ((res == -EINVAL) || (res == -ERANGE)) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); } else if (res) { - put_packet(gdb_ctx->s, ""); + put_packet(""); } } @@ -1876,38 +1904,37 @@ static void handle_v_attach(GdbCmdContext *gdb_ctx, void *user_ctx) { GDBProcess *process; CPUState *cpu; - char thread_id[16]; - pstrcpy(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "E22"); + g_string_assign(gdbserver_state.str_buf, "E22"); if (!gdb_ctx->num_params) { goto cleanup; } - process = gdb_get_process(gdb_ctx->s, gdb_ctx->params[0].val_ul); + process = gdb_get_process(gdb_ctx->params[0].val_ul); if (!process) { goto cleanup; } - cpu = get_first_cpu_in_process(gdb_ctx->s, process); + cpu = get_first_cpu_in_process(process); if (!cpu) { goto cleanup; } process->attached = true; - gdb_ctx->s->g_cpu = cpu; - gdb_ctx->s->c_cpu = cpu; + gdbserver_state.g_cpu = cpu; + gdbserver_state.c_cpu = cpu; - gdb_fmt_thread_id(gdb_ctx->s, cpu, thread_id, sizeof(thread_id)); - snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "T%02xthread:%s;", - GDB_SIGNAL_TRAP, thread_id); + g_string_printf(gdbserver_state.str_buf, "T%02xthread:", GDB_SIGNAL_TRAP); + gdb_append_thread_id(cpu, gdbserver_state.str_buf); + g_string_append_c(gdbserver_state.str_buf, ';'); cleanup: - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + put_strbuf(); } static void handle_v_kill(GdbCmdContext *gdb_ctx, void *user_ctx) { /* Kill the target */ - put_packet(gdb_ctx->s, "OK"); + put_packet("OK"); error_report("QEMU: Terminated via GDBstub"); exit(0); } @@ -1944,19 +1971,18 @@ static void handle_v_commands(GdbCmdContext *gdb_ctx, void *user_ctx) return; } - if (process_string_cmd(gdb_ctx->s, NULL, gdb_ctx->params[0].data, + if (process_string_cmd(NULL, gdb_ctx->params[0].data, gdb_v_commands_table, ARRAY_SIZE(gdb_v_commands_table))) { - put_packet(gdb_ctx->s, ""); + put_packet(""); } } static void handle_query_qemu_sstepbits(GdbCmdContext *gdb_ctx, void *user_ctx) { - snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), - "ENABLE=%x,NOIRQ=%x,NOTIMER=%x", SSTEP_ENABLE, - SSTEP_NOIRQ, SSTEP_NOTIMER); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + g_string_printf(gdbserver_state.str_buf, "ENABLE=%x,NOIRQ=%x,NOTIMER=%x", + SSTEP_ENABLE, SSTEP_NOIRQ, SSTEP_NOTIMER); + put_strbuf(); } static void handle_set_qemu_sstep(GdbCmdContext *gdb_ctx, void *user_ctx) @@ -1966,68 +1992,63 @@ static void handle_set_qemu_sstep(GdbCmdContext *gdb_ctx, void *user_ctx) } sstep_flags = gdb_ctx->params[0].val_ul; - put_packet(gdb_ctx->s, "OK"); + put_packet("OK"); } static void handle_query_qemu_sstep(GdbCmdContext *gdb_ctx, void *user_ctx) { - snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "0x%x", sstep_flags); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + g_string_printf(gdbserver_state.str_buf, "0x%x", sstep_flags); + put_strbuf(); } static void handle_query_curr_tid(GdbCmdContext *gdb_ctx, void *user_ctx) { CPUState *cpu; GDBProcess *process; - char thread_id[16]; /* * "Current thread" remains vague in the spec, so always return * the first thread of the current process (gdb returns the * first thread). */ - process = gdb_get_cpu_process(gdb_ctx->s, gdb_ctx->s->g_cpu); - cpu = get_first_cpu_in_process(gdb_ctx->s, process); - gdb_fmt_thread_id(gdb_ctx->s, cpu, thread_id, sizeof(thread_id)); - snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "QC%s", thread_id); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + process = gdb_get_cpu_process(gdbserver_state.g_cpu); + cpu = get_first_cpu_in_process(process); + g_string_assign(gdbserver_state.str_buf, "QC"); + gdb_append_thread_id(cpu, gdbserver_state.str_buf); + put_strbuf(); } static void handle_query_threads(GdbCmdContext *gdb_ctx, void *user_ctx) { - char thread_id[16]; - - if (!gdb_ctx->s->query_cpu) { - put_packet(gdb_ctx->s, "l"); + if (!gdbserver_state.query_cpu) { + put_packet("l"); return; } - gdb_fmt_thread_id(gdb_ctx->s, gdb_ctx->s->query_cpu, thread_id, - sizeof(thread_id)); - snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "m%s", thread_id); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); - gdb_ctx->s->query_cpu = - gdb_next_attached_cpu(gdb_ctx->s, gdb_ctx->s->query_cpu); + g_string_assign(gdbserver_state.str_buf, "m"); + gdb_append_thread_id(gdbserver_state.query_cpu, gdbserver_state.str_buf); + put_strbuf(); + gdbserver_state.query_cpu = gdb_next_attached_cpu(gdbserver_state.query_cpu); } static void handle_query_first_threads(GdbCmdContext *gdb_ctx, void *user_ctx) { - gdb_ctx->s->query_cpu = gdb_first_attached_cpu(gdb_ctx->s); + gdbserver_state.query_cpu = gdb_first_attached_cpu(); handle_query_threads(gdb_ctx, user_ctx); } static void handle_query_thread_extra(GdbCmdContext *gdb_ctx, void *user_ctx) { + g_autoptr(GString) rs = g_string_new(NULL); CPUState *cpu; - int len; if (!gdb_ctx->num_params || gdb_ctx->params[0].thread_id.kind == GDB_READ_THREAD_ERR) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } - cpu = gdb_get_cpu(gdb_ctx->s, gdb_ctx->params[0].thread_id.pid, + cpu = gdb_get_cpu(gdb_ctx->params[0].thread_id.pid, gdb_ctx->params[0].thread_id.tid); if (!cpu) { return; @@ -2035,24 +2056,21 @@ static void handle_query_thread_extra(GdbCmdContext *gdb_ctx, void *user_ctx) cpu_synchronize_state(cpu); - if (gdb_ctx->s->multiprocess && (gdb_ctx->s->process_num > 1)) { + if (gdbserver_state.multiprocess && (gdbserver_state.process_num > 1)) { /* Print the CPU model and name in multiprocess mode */ ObjectClass *oc = object_get_class(OBJECT(cpu)); const char *cpu_model = object_class_get_name(oc); - char *cpu_name = object_get_canonical_path_component(OBJECT(cpu)); - len = snprintf((char *)gdb_ctx->mem_buf, sizeof(gdb_ctx->str_buf) / 2, - "%s %s [%s]", cpu_model, cpu_name, - cpu->halted ? "halted " : "running"); - g_free(cpu_name); + g_autofree char *cpu_name; + cpu_name = object_get_canonical_path_component(OBJECT(cpu)); + g_string_printf(rs, "%s %s [%s]", cpu_model, cpu_name, + cpu->halted ? "halted " : "running"); } else { - /* memtohex() doubles the required space */ - len = snprintf((char *)gdb_ctx->mem_buf, sizeof(gdb_ctx->str_buf) / 2, - "CPU#%d [%s]", cpu->cpu_index, + g_string_printf(rs, "CPU#%d [%s]", cpu->cpu_index, cpu->halted ? "halted " : "running"); } - trace_gdbstub_op_extra_info((char *)gdb_ctx->mem_buf); - memtohex(gdb_ctx->str_buf, gdb_ctx->mem_buf, len); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + trace_gdbstub_op_extra_info(rs->str); + memtohex(gdbserver_state.str_buf, (uint8_t *)rs->str, rs->len); + put_strbuf(); } #ifdef CONFIG_USER_ONLY @@ -2060,37 +2078,40 @@ static void handle_query_offsets(GdbCmdContext *gdb_ctx, void *user_ctx) { TaskState *ts; - ts = gdb_ctx->s->c_cpu->opaque; - snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), - "Text=" TARGET_ABI_FMT_lx ";Data=" TARGET_ABI_FMT_lx - ";Bss=" TARGET_ABI_FMT_lx, - ts->info->code_offset, - ts->info->data_offset, - ts->info->data_offset); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + ts = gdbserver_state.c_cpu->opaque; + g_string_printf(gdbserver_state.str_buf, + "Text=" TARGET_ABI_FMT_lx + ";Data=" TARGET_ABI_FMT_lx + ";Bss=" TARGET_ABI_FMT_lx, + ts->info->code_offset, + ts->info->data_offset, + ts->info->data_offset); + put_strbuf(); } #else static void handle_query_rcmd(GdbCmdContext *gdb_ctx, void *user_ctx) { + const guint8 zero = 0; int len; if (!gdb_ctx->num_params) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } len = strlen(gdb_ctx->params[0].data); if (len % 2) { - put_packet(gdb_ctx->s, "E01"); + put_packet("E01"); return; } + g_assert(gdbserver_state.mem_buf->len == 0); len = len / 2; - hextomem(gdb_ctx->mem_buf, gdb_ctx->params[0].data, len); - gdb_ctx->mem_buf[len++] = 0; - qemu_chr_be_write(gdb_ctx->s->mon_chr, gdb_ctx->mem_buf, len); - put_packet(gdb_ctx->s, "OK"); - + hextomem(gdbserver_state.mem_buf, gdb_ctx->params[0].data, len); + g_byte_array_append(gdbserver_state.mem_buf, &zero, 1); + qemu_chr_be_write(gdbserver_state.mon_chr, gdbserver_state.mem_buf->data, + gdbserver_state.mem_buf->len); + put_packet("OK"); } #endif @@ -2098,21 +2119,19 @@ static void handle_query_supported(GdbCmdContext *gdb_ctx, void *user_ctx) { CPUClass *cc; - snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "PacketSize=%x", - MAX_PACKET_LENGTH); + g_string_printf(gdbserver_state.str_buf, "PacketSize=%x", MAX_PACKET_LENGTH); cc = CPU_GET_CLASS(first_cpu); if (cc->gdb_core_xml_file) { - pstrcat(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), - ";qXfer:features:read+"); + g_string_append(gdbserver_state.str_buf, ";qXfer:features:read+"); } if (gdb_ctx->num_params && strstr(gdb_ctx->params[0].data, "multiprocess+")) { - gdb_ctx->s->multiprocess = true; + gdbserver_state.multiprocess = true; } - pstrcat(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), ";multiprocess+"); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + g_string_append(gdbserver_state.str_buf, ";vContSupported+;multiprocess+"); + put_strbuf(); } static void handle_query_xfer_features(GdbCmdContext *gdb_ctx, void *user_ctx) @@ -2124,22 +2143,22 @@ static void handle_query_xfer_features(GdbCmdContext *gdb_ctx, void *user_ctx) const char *p; if (gdb_ctx->num_params < 3) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } - process = gdb_get_cpu_process(gdb_ctx->s, gdb_ctx->s->g_cpu); - cc = CPU_GET_CLASS(gdb_ctx->s->g_cpu); + process = gdb_get_cpu_process(gdbserver_state.g_cpu); + cc = CPU_GET_CLASS(gdbserver_state.g_cpu); if (!cc->gdb_core_xml_file) { - put_packet(gdb_ctx->s, ""); + put_packet(""); return; } gdb_has_xml = true; p = gdb_ctx->params[0].data; - xml = get_feature_xml(gdb_ctx->s, p, &p, process); + xml = get_feature_xml(p, &p, process); if (!xml) { - put_packet(gdb_ctx->s, "E00"); + put_packet("E00"); return; } @@ -2147,7 +2166,7 @@ static void handle_query_xfer_features(GdbCmdContext *gdb_ctx, void *user_ctx) len = gdb_ctx->params[2].val_ul; total_len = strlen(xml); if (addr > total_len) { - put_packet(gdb_ctx->s, "E00"); + put_packet("E00"); return; } @@ -2156,42 +2175,43 @@ static void handle_query_xfer_features(GdbCmdContext *gdb_ctx, void *user_ctx) } if (len < total_len - addr) { - gdb_ctx->str_buf[0] = 'm'; - len = memtox(gdb_ctx->str_buf + 1, xml + addr, len); + g_string_assign(gdbserver_state.str_buf, "m"); + memtox(gdbserver_state.str_buf, xml + addr, len); } else { - gdb_ctx->str_buf[0] = 'l'; - len = memtox(gdb_ctx->str_buf + 1, xml + addr, total_len - addr); + g_string_assign(gdbserver_state.str_buf, "l"); + memtox(gdbserver_state.str_buf, xml + addr, total_len - addr); } - put_packet_binary(gdb_ctx->s, gdb_ctx->str_buf, len + 1, true); + put_packet_binary(gdbserver_state.str_buf->str, + gdbserver_state.str_buf->len, true); } static void handle_query_attached(GdbCmdContext *gdb_ctx, void *user_ctx) { - put_packet(gdb_ctx->s, GDB_ATTACHED); + put_packet(GDB_ATTACHED); } static void handle_query_qemu_supported(GdbCmdContext *gdb_ctx, void *user_ctx) { - snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "sstepbits;sstep"); + g_string_printf(gdbserver_state.str_buf, "sstepbits;sstep"); #ifndef CONFIG_USER_ONLY - pstrcat(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), ";PhyMemMode"); + g_string_append(gdbserver_state.str_buf, ";PhyMemMode"); #endif - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + put_strbuf(); } #ifndef CONFIG_USER_ONLY static void handle_query_qemu_phy_mem_mode(GdbCmdContext *gdb_ctx, void *user_ctx) { - snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "%d", phy_memory_mode); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + g_string_printf(gdbserver_state.str_buf, "%d", phy_memory_mode); + put_strbuf(); } static void handle_set_qemu_phy_mem_mode(GdbCmdContext *gdb_ctx, void *user_ctx) { if (!gdb_ctx->num_params) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } @@ -2200,7 +2220,7 @@ static void handle_set_qemu_phy_mem_mode(GdbCmdContext *gdb_ctx, void *user_ctx) } else { phy_memory_mode = 1; } - put_packet(gdb_ctx->s, "OK"); + put_packet("OK"); } #endif @@ -2316,16 +2336,16 @@ static void handle_gen_query(GdbCmdContext *gdb_ctx, void *user_ctx) return; } - if (!process_string_cmd(gdb_ctx->s, NULL, gdb_ctx->params[0].data, + if (!process_string_cmd(NULL, gdb_ctx->params[0].data, gdb_gen_query_set_common_table, ARRAY_SIZE(gdb_gen_query_set_common_table))) { return; } - if (process_string_cmd(gdb_ctx->s, NULL, gdb_ctx->params[0].data, + if (process_string_cmd(NULL, gdb_ctx->params[0].data, gdb_gen_query_table, ARRAY_SIZE(gdb_gen_query_table))) { - put_packet(gdb_ctx->s, ""); + put_packet(""); } } @@ -2335,28 +2355,25 @@ static void handle_gen_set(GdbCmdContext *gdb_ctx, void *user_ctx) return; } - if (!process_string_cmd(gdb_ctx->s, NULL, gdb_ctx->params[0].data, + if (!process_string_cmd(NULL, gdb_ctx->params[0].data, gdb_gen_query_set_common_table, ARRAY_SIZE(gdb_gen_query_set_common_table))) { return; } - if (process_string_cmd(gdb_ctx->s, NULL, gdb_ctx->params[0].data, + if (process_string_cmd(NULL, gdb_ctx->params[0].data, gdb_gen_set_table, ARRAY_SIZE(gdb_gen_set_table))) { - put_packet(gdb_ctx->s, ""); + put_packet(""); } } static void handle_target_halt(GdbCmdContext *gdb_ctx, void *user_ctx) { - char thread_id[16]; - - gdb_fmt_thread_id(gdb_ctx->s, gdb_ctx->s->c_cpu, thread_id, - sizeof(thread_id)); - snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "T%02xthread:%s;", - GDB_SIGNAL_TRAP, thread_id); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + g_string_printf(gdbserver_state.str_buf, "T%02xthread:", GDB_SIGNAL_TRAP); + gdb_append_thread_id(gdbserver_state.c_cpu, gdbserver_state.str_buf); + g_string_append_c(gdbserver_state.str_buf, ';'); + put_strbuf(); /* * Remove all the breakpoints when this query is issued, * because gdb is doing an initial connect and the state @@ -2365,7 +2382,7 @@ static void handle_target_halt(GdbCmdContext *gdb_ctx, void *user_ctx) gdb_breakpoint_remove_all(); } -static int gdb_handle_packet(GDBState *s, const char *line_buf) +static int gdb_handle_packet(const char *line_buf) { const GdbCmdParseEntry *cmd_parser = NULL; @@ -2373,7 +2390,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) switch (line_buf[0]) { case '!': - put_packet(s, "OK"); + put_packet("OK"); break; case '?': { @@ -2588,12 +2605,12 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) break; default: /* put empty packet */ - put_packet(s, ""); + put_packet(""); break; } if (cmd_parser) { - run_cmd_parser(s, line_buf, cmd_parser); + run_cmd_parser(line_buf, cmd_parser); } return RS_IDLE; @@ -2601,7 +2618,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) void gdb_set_stop_cpu(CPUState *cpu) { - GDBProcess *p = gdb_get_cpu_process(gdbserver_state, cpu); + GDBProcess *p = gdb_get_cpu_process(cpu); if (!p->attached) { /* @@ -2611,26 +2628,25 @@ void gdb_set_stop_cpu(CPUState *cpu) return; } - gdbserver_state->c_cpu = cpu; - gdbserver_state->g_cpu = cpu; + gdbserver_state.c_cpu = cpu; + gdbserver_state.g_cpu = cpu; } #ifndef CONFIG_USER_ONLY static void gdb_vm_state_change(void *opaque, int running, RunState state) { - GDBState *s = gdbserver_state; - CPUState *cpu = s->c_cpu; - char buf[256]; - char thread_id[16]; + CPUState *cpu = gdbserver_state.c_cpu; + g_autoptr(GString) buf = g_string_new(NULL); + g_autoptr(GString) tid = g_string_new(NULL); const char *type; int ret; - if (running || s->state == RS_INACTIVE) { + if (running || gdbserver_state.state == RS_INACTIVE) { return; } /* Is there a GDB syscall waiting to be sent? */ - if (s->current_syscall_cb) { - put_packet(s, s->syscall_buf); + if (gdbserver_state.current_syscall_cb) { + put_packet(gdbserver_state.syscall_buf); return; } @@ -2639,7 +2655,7 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) return; } - gdb_fmt_thread_id(s, cpu, thread_id, sizeof(thread_id)); + gdb_append_thread_id(cpu, tid); switch (state) { case RUN_STATE_DEBUG: @@ -2657,10 +2673,9 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) } trace_gdbstub_hit_watchpoint(type, cpu_gdb_index(cpu), (target_ulong)cpu->watchpoint_hit->vaddr); - snprintf(buf, sizeof(buf), - "T%02xthread:%s;%swatch:" TARGET_FMT_lx ";", - GDB_SIGNAL_TRAP, thread_id, type, - (target_ulong)cpu->watchpoint_hit->vaddr); + g_string_printf(buf, "T%02xthread:%s;%swatch:" TARGET_FMT_lx ";", + GDB_SIGNAL_TRAP, tid->str, type, + (target_ulong)cpu->watchpoint_hit->vaddr); cpu->watchpoint_hit = NULL; goto send_packet; } else { @@ -2701,10 +2716,10 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) break; } gdb_set_stop_cpu(cpu); - snprintf(buf, sizeof(buf), "T%02xthread:%s;", ret, thread_id); + g_string_printf(buf, "T%02xthread:%s;", ret, tid->str); send_packet: - put_packet(s, buf); + put_packet(buf->str); /* disable single step if it was enabled */ cpu_single_step(cpu, 0); @@ -2722,17 +2737,17 @@ void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va) char *p_end; target_ulong addr; uint64_t i64; - GDBState *s; - s = gdbserver_state; - if (!s) + if (!gdbserver_state.init) { return; - s->current_syscall_cb = cb; + } + + gdbserver_state.current_syscall_cb = cb; #ifndef CONFIG_USER_ONLY vm_stop(RUN_STATE_DEBUG); #endif - p = s->syscall_buf; - p_end = &s->syscall_buf[sizeof(s->syscall_buf)]; + p = &gdbserver_state.syscall_buf[0]; + p_end = &gdbserver_state.syscall_buf[sizeof(gdbserver_state.syscall_buf)]; *(p++) = 'F'; while (*fmt) { if (*fmt == '%') { @@ -2765,14 +2780,14 @@ void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va) } *p = 0; #ifdef CONFIG_USER_ONLY - put_packet(s, s->syscall_buf); + put_packet(gdbserver_state.syscall_buf); /* Return control to gdb for it to process the syscall request. * Since the protocol requires that gdb hands control back to us * using a "here are the results" F packet, we don't need to check * gdb_handlesig's return value (which is the signal to deliver if * execution was resumed via a continue packet). */ - gdb_handlesig(s->c_cpu, 0); + gdb_handlesig(gdbserver_state.c_cpu, 0); #else /* In this case wait to send the syscall packet until notification that the CPU has stopped. This must be done because if the packet is sent @@ -2780,7 +2795,7 @@ void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va) is still in the running state, which can cause packets to be dropped and state transition 'T' packets to be sent while the syscall is still being processed. */ - qemu_cpu_kick(s->c_cpu); + qemu_cpu_kick(gdbserver_state.c_cpu); #endif } @@ -2793,25 +2808,27 @@ void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...) va_end(va); } -static void gdb_read_byte(GDBState *s, uint8_t ch) +static void gdb_read_byte(uint8_t ch) { uint8_t reply; #ifndef CONFIG_USER_ONLY - if (s->last_packet_len) { + if (gdbserver_state.last_packet->len) { /* Waiting for a response to the last packet. If we see the start of a new command then abandon the previous response. */ if (ch == '-') { trace_gdbstub_err_got_nack(); - put_buffer(s, (uint8_t *)s->last_packet, s->last_packet_len); + put_buffer(gdbserver_state.last_packet->data, + gdbserver_state.last_packet->len); } else if (ch == '+') { trace_gdbstub_io_got_ack(); } else { trace_gdbstub_io_got_unexpected(ch); } - if (ch == '+' || ch == '$') - s->last_packet_len = 0; + if (ch == '+' || ch == '$') { + g_byte_array_set_size(gdbserver_state.last_packet, 0); + } if (ch != '$') return; } @@ -2822,13 +2839,13 @@ static void gdb_read_byte(GDBState *s, uint8_t ch) } else #endif { - switch(s->state) { + switch(gdbserver_state.state) { case RS_IDLE: if (ch == '$') { /* start of command packet */ - s->line_buf_index = 0; - s->line_sum = 0; - s->state = RS_GETLINE; + gdbserver_state.line_buf_index = 0; + gdbserver_state.line_sum = 0; + gdbserver_state.state = RS_GETLINE; } else { trace_gdbstub_err_garbage(ch); } @@ -2836,37 +2853,37 @@ static void gdb_read_byte(GDBState *s, uint8_t ch) case RS_GETLINE: if (ch == '}') { /* start escape sequence */ - s->state = RS_GETLINE_ESC; - s->line_sum += ch; + gdbserver_state.state = RS_GETLINE_ESC; + gdbserver_state.line_sum += ch; } else if (ch == '*') { /* start run length encoding sequence */ - s->state = RS_GETLINE_RLE; - s->line_sum += ch; + gdbserver_state.state = RS_GETLINE_RLE; + gdbserver_state.line_sum += ch; } else if (ch == '#') { /* end of command, start of checksum*/ - s->state = RS_CHKSUM1; - } else if (s->line_buf_index >= sizeof(s->line_buf) - 1) { + gdbserver_state.state = RS_CHKSUM1; + } else if (gdbserver_state.line_buf_index >= sizeof(gdbserver_state.line_buf) - 1) { trace_gdbstub_err_overrun(); - s->state = RS_IDLE; + gdbserver_state.state = RS_IDLE; } else { /* unescaped command character */ - s->line_buf[s->line_buf_index++] = ch; - s->line_sum += ch; + gdbserver_state.line_buf[gdbserver_state.line_buf_index++] = ch; + gdbserver_state.line_sum += ch; } break; case RS_GETLINE_ESC: if (ch == '#') { /* unexpected end of command in escape sequence */ - s->state = RS_CHKSUM1; - } else if (s->line_buf_index >= sizeof(s->line_buf) - 1) { + gdbserver_state.state = RS_CHKSUM1; + } else if (gdbserver_state.line_buf_index >= sizeof(gdbserver_state.line_buf) - 1) { /* command buffer overrun */ trace_gdbstub_err_overrun(); - s->state = RS_IDLE; + gdbserver_state.state = RS_IDLE; } else { /* parse escaped character and leave escape state */ - s->line_buf[s->line_buf_index++] = ch ^ 0x20; - s->line_sum += ch; - s->state = RS_GETLINE; + gdbserver_state.line_buf[gdbserver_state.line_buf_index++] = ch ^ 0x20; + gdbserver_state.line_sum += ch; + gdbserver_state.state = RS_GETLINE; } break; case RS_GETLINE_RLE: @@ -2877,25 +2894,25 @@ static void gdb_read_byte(GDBState *s, uint8_t ch) if (ch < ' ' || ch == '#' || ch == '$' || ch > 126) { /* invalid RLE count encoding */ trace_gdbstub_err_invalid_repeat(ch); - s->state = RS_GETLINE; + gdbserver_state.state = RS_GETLINE; } else { /* decode repeat length */ int repeat = ch - ' ' + 3; - if (s->line_buf_index + repeat >= sizeof(s->line_buf) - 1) { + if (gdbserver_state.line_buf_index + repeat >= sizeof(gdbserver_state.line_buf) - 1) { /* that many repeats would overrun the command buffer */ trace_gdbstub_err_overrun(); - s->state = RS_IDLE; - } else if (s->line_buf_index < 1) { + gdbserver_state.state = RS_IDLE; + } else if (gdbserver_state.line_buf_index < 1) { /* got a repeat but we have nothing to repeat */ trace_gdbstub_err_invalid_rle(); - s->state = RS_GETLINE; + gdbserver_state.state = RS_GETLINE; } else { /* repeat the last character */ - memset(s->line_buf + s->line_buf_index, - s->line_buf[s->line_buf_index - 1], repeat); - s->line_buf_index += repeat; - s->line_sum += ch; - s->state = RS_GETLINE; + memset(gdbserver_state.line_buf + gdbserver_state.line_buf_index, + gdbserver_state.line_buf[gdbserver_state.line_buf_index - 1], repeat); + gdbserver_state.line_buf_index += repeat; + gdbserver_state.line_sum += ch; + gdbserver_state.state = RS_GETLINE; } } break; @@ -2903,33 +2920,33 @@ static void gdb_read_byte(GDBState *s, uint8_t ch) /* get high hex digit of checksum */ if (!isxdigit(ch)) { trace_gdbstub_err_checksum_invalid(ch); - s->state = RS_GETLINE; + gdbserver_state.state = RS_GETLINE; break; } - s->line_buf[s->line_buf_index] = '\0'; - s->line_csum = fromhex(ch) << 4; - s->state = RS_CHKSUM2; + gdbserver_state.line_buf[gdbserver_state.line_buf_index] = '\0'; + gdbserver_state.line_csum = fromhex(ch) << 4; + gdbserver_state.state = RS_CHKSUM2; break; case RS_CHKSUM2: /* get low hex digit of checksum */ if (!isxdigit(ch)) { trace_gdbstub_err_checksum_invalid(ch); - s->state = RS_GETLINE; + gdbserver_state.state = RS_GETLINE; break; } - s->line_csum |= fromhex(ch); + gdbserver_state.line_csum |= fromhex(ch); - if (s->line_csum != (s->line_sum & 0xff)) { - trace_gdbstub_err_checksum_incorrect(s->line_sum, s->line_csum); + if (gdbserver_state.line_csum != (gdbserver_state.line_sum & 0xff)) { + trace_gdbstub_err_checksum_incorrect(gdbserver_state.line_sum, gdbserver_state.line_csum); /* send NAK reply */ reply = '-'; - put_buffer(s, &reply, 1); - s->state = RS_IDLE; + put_buffer(&reply, 1); + gdbserver_state.state = RS_IDLE; } else { /* send ACK reply */ reply = '+'; - put_buffer(s, &reply, 1); - s->state = gdb_handle_packet(s, s->line_buf); + put_buffer(&reply, 1); + gdbserver_state.state = gdb_handle_packet(gdbserver_state.line_buf); } break; default: @@ -2941,15 +2958,13 @@ static void gdb_read_byte(GDBState *s, uint8_t ch) /* Tell the remote gdb that the process has exited. */ void gdb_exit(CPUArchState *env, int code) { - GDBState *s; char buf[4]; - s = gdbserver_state; - if (!s) { + if (!gdbserver_state.init) { return; } #ifdef CONFIG_USER_ONLY - if (gdbserver_fd < 0 || s->fd < 0) { + if (gdbserver_fd < 0 || gdbserver_state.fd < 0) { return; } #endif @@ -2957,10 +2972,10 @@ void gdb_exit(CPUArchState *env, int code) trace_gdbstub_op_exiting((uint8_t)code); snprintf(buf, sizeof(buf), "W%02x", (uint8_t)code); - put_packet(s, buf); + put_packet(buf); #ifndef CONFIG_USER_ONLY - qemu_chr_fe_deinit(&s->chr, true); + qemu_chr_fe_deinit(&gdbserver_state.chr, true); #endif } @@ -2974,7 +2989,7 @@ static void create_default_process(GDBState *s) GDBProcess *process; int max_pid = 0; - if (s->process_num) { + if (gdbserver_state.process_num) { max_pid = s->processes[s->process_num - 1].pid; } @@ -2993,12 +3008,10 @@ static void create_default_process(GDBState *s) int gdb_handlesig(CPUState *cpu, int sig) { - GDBState *s; char buf[256]; int n; - s = gdbserver_state; - if (gdbserver_fd < 0 || s->fd < 0) { + if (gdbserver_fd < 0 || gdbserver_state.fd < 0) { return sig; } @@ -3008,58 +3021,55 @@ gdb_handlesig(CPUState *cpu, int sig) if (sig != 0) { snprintf(buf, sizeof(buf), "S%02x", target_signal_to_gdb(sig)); - put_packet(s, buf); + put_packet(buf); } /* put_packet() might have detected that the peer terminated the connection. */ - if (s->fd < 0) { + if (gdbserver_state.fd < 0) { return sig; } sig = 0; - s->state = RS_IDLE; - s->running_state = 0; - while (s->running_state == 0) { - n = read(s->fd, buf, 256); + gdbserver_state.state = RS_IDLE; + gdbserver_state.running_state = 0; + while (gdbserver_state.running_state == 0) { + n = read(gdbserver_state.fd, buf, 256); if (n > 0) { int i; for (i = 0; i < n; i++) { - gdb_read_byte(s, buf[i]); + gdb_read_byte(buf[i]); } } else { /* XXX: Connection closed. Should probably wait for another connection before continuing. */ if (n == 0) { - close(s->fd); + close(gdbserver_state.fd); } - s->fd = -1; + gdbserver_state.fd = -1; return sig; } } - sig = s->signal; - s->signal = 0; + sig = gdbserver_state.signal; + gdbserver_state.signal = 0; return sig; } /* Tell the remote gdb that the process has exited due to SIG. */ void gdb_signalled(CPUArchState *env, int sig) { - GDBState *s; char buf[4]; - s = gdbserver_state; - if (gdbserver_fd < 0 || s->fd < 0) { + if (gdbserver_fd < 0 || gdbserver_state.fd < 0) { return; } snprintf(buf, sizeof(buf), "X%02x", target_signal_to_gdb(sig)); - put_packet(s, buf); + put_packet(buf); } static bool gdb_accept(void) { - GDBState *s; struct sockaddr_in sockaddr; socklen_t len; int fd; @@ -3083,15 +3093,13 @@ static bool gdb_accept(void) return false; } - s = g_malloc0(sizeof(GDBState)); - create_default_process(s); - s->processes[0].attached = true; - s->c_cpu = gdb_first_attached_cpu(s); - s->g_cpu = s->c_cpu; - s->fd = fd; + init_gdbserver_state(); + create_default_process(&gdbserver_state); + gdbserver_state.processes[0].attached = true; + gdbserver_state.c_cpu = gdb_first_attached_cpu(); + gdbserver_state.g_cpu = gdbserver_state.c_cpu; + gdbserver_state.fd = fd; gdb_has_xml = false; - - gdbserver_state = s; return true; } @@ -3144,13 +3152,11 @@ int gdbserver_start(int port) /* Disable gdb stub for child processes. */ void gdbserver_fork(CPUState *cpu) { - GDBState *s = gdbserver_state; - - if (gdbserver_fd < 0 || s->fd < 0) { + if (gdbserver_fd < 0 || gdbserver_state.fd < 0) { return; } - close(s->fd); - s->fd = -1; + close(gdbserver_state.fd); + gdbserver_state.fd = -1; cpu_breakpoint_remove_all(cpu, BP_GDB); cpu_watchpoint_remove_all(cpu, BP_GDB); } @@ -3167,7 +3173,7 @@ static void gdb_chr_receive(void *opaque, const uint8_t *buf, int size) int i; for (i = 0; i < size; i++) { - gdb_read_byte(gdbserver_state, buf[i]); + gdb_read_byte(buf[i]); } } @@ -3183,7 +3189,7 @@ static void gdb_chr_event(void *opaque, QEMUChrEvent event) s->processes[i].attached = !i; } - s->c_cpu = gdb_first_attached_cpu(s); + s->c_cpu = gdb_first_attached_cpu(); s->g_cpu = s->c_cpu; vm_stop(RUN_STATE_PAUSED); @@ -3194,32 +3200,11 @@ static void gdb_chr_event(void *opaque, QEMUChrEvent event) } } -static void gdb_monitor_output(GDBState *s, const char *msg, int len) -{ - char buf[MAX_PACKET_LENGTH]; - - buf[0] = 'O'; - if (len > (MAX_PACKET_LENGTH/2) - 1) - len = (MAX_PACKET_LENGTH/2) - 1; - memtohex(buf + 1, (uint8_t *)msg, len); - put_packet(s, buf); -} - static int gdb_monitor_write(Chardev *chr, const uint8_t *buf, int len) { - const char *p = (const char *)buf; - int max_sz; - - max_sz = (sizeof(gdbserver_state->last_packet) - 2) / 2; - for (;;) { - if (len <= max_sz) { - gdb_monitor_output(gdbserver_state, p, len); - break; - } - gdb_monitor_output(gdbserver_state, p, max_sz); - p += max_sz; - len -= max_sz; - } + g_autoptr(GString) hex_buf = g_string_new("O"); + memtohex(hex_buf, buf, len); + put_packet(hex_buf->str); return len; } @@ -3300,26 +3285,18 @@ static void create_processes(GDBState *s) { object_child_foreach(object_get_root(), find_cpu_clusters, s); - if (s->processes) { + if (gdbserver_state.processes) { /* Sort by PID */ - qsort(s->processes, s->process_num, sizeof(s->processes[0]), pid_order); + qsort(gdbserver_state.processes, gdbserver_state.process_num, sizeof(gdbserver_state.processes[0]), pid_order); } create_default_process(s); } -static void cleanup_processes(GDBState *s) -{ - g_free(s->processes); - s->process_num = 0; - s->processes = NULL; -} - int gdbserver_start(const char *device) { trace_gdbstub_op_start(device); - GDBState *s; char gdbstub_device_name[128]; Chardev *chr = NULL; Chardev *mon_chr; @@ -3357,10 +3334,8 @@ int gdbserver_start(const char *device) return -1; } - s = gdbserver_state; - if (!s) { - s = g_malloc0(sizeof(GDBState)); - gdbserver_state = s; + if (!gdbserver_state.init) { + init_gdbserver_state(); qemu_add_vm_change_state_handler(gdb_vm_state_change, NULL); @@ -3369,31 +3344,30 @@ int gdbserver_start(const char *device) NULL, NULL, &error_abort); monitor_init_hmp(mon_chr, false, &error_abort); } else { - qemu_chr_fe_deinit(&s->chr, true); - mon_chr = s->mon_chr; - cleanup_processes(s); - memset(s, 0, sizeof(GDBState)); - s->mon_chr = mon_chr; + qemu_chr_fe_deinit(&gdbserver_state.chr, true); + mon_chr = gdbserver_state.mon_chr; + reset_gdbserver_state(); } - create_processes(s); + create_processes(&gdbserver_state); if (chr) { - qemu_chr_fe_init(&s->chr, chr, &error_abort); - qemu_chr_fe_set_handlers(&s->chr, gdb_chr_can_receive, gdb_chr_receive, - gdb_chr_event, NULL, s, NULL, true); + qemu_chr_fe_init(&gdbserver_state.chr, chr, &error_abort); + qemu_chr_fe_set_handlers(&gdbserver_state.chr, gdb_chr_can_receive, + gdb_chr_receive, gdb_chr_event, + NULL, &gdbserver_state, NULL, true); } - s->state = chr ? RS_IDLE : RS_INACTIVE; - s->mon_chr = mon_chr; - s->current_syscall_cb = NULL; + gdbserver_state.state = chr ? RS_IDLE : RS_INACTIVE; + gdbserver_state.mon_chr = mon_chr; + gdbserver_state.current_syscall_cb = NULL; return 0; } void gdbserver_cleanup(void) { - if (gdbserver_state) { - put_packet(gdbserver_state, "W00"); + if (gdbserver_state.init) { + put_packet("W00"); } } diff --git a/hw/alpha/dp264.c b/hw/alpha/dp264.c index e5350a287f..f7751b18f6 100644 --- a/hw/alpha/dp264.c +++ b/hw/alpha/dp264.c @@ -15,7 +15,7 @@ #include "qemu/error-report.h" #include "sysemu/sysemu.h" #include "hw/rtc/mc146818rtc.h" -#include "hw/ide.h" +#include "hw/ide/pci.h" #include "hw/timer/i8254.h" #include "hw/isa/superio.h" #include "hw/dma/i8257.h" @@ -57,6 +57,7 @@ static void clipper_init(MachineState *machine) const char *initrd_filename = machine->initrd_filename; AlphaCPU *cpus[4]; PCIBus *pci_bus; + PCIDevice *pci_dev; ISABus *isa_bus; qemu_irq rtc_irq; long size, i; @@ -99,12 +100,8 @@ static void clipper_init(MachineState *machine) isa_create_simple(isa_bus, TYPE_SMC37C669_SUPERIO); /* IDE disk setup. */ - { - DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; - ide_drive_get(hd, ARRAY_SIZE(hd)); - - pci_cmd646_ide_init(pci_bus, hd, 0); - } + pci_dev = pci_create_simple(pci_bus, -1, "cmd646-ide"); + pci_ide_create_devs(pci_dev); /* Load PALcode. Given that this is not "real" cpu palcode, but one explicitly written for the emulation, we might as diff --git a/hw/block/xen-block.c b/hw/block/xen-block.c index 3885464513..07bb32e22b 100644 --- a/hw/block/xen-block.c +++ b/hw/block/xen-block.c @@ -998,29 +998,27 @@ static void xen_block_device_destroy(XenBackendInstance *backend, XenBlockVdev *vdev = &blockdev->props.vdev; XenBlockDrive *drive = blockdev->drive; XenBlockIOThread *iothread = blockdev->iothread; + Error *local_err = NULL; trace_xen_block_device_destroy(vdev->number); object_unparent(OBJECT(xendev)); if (iothread) { - Error *local_err = NULL; - xen_block_iothread_destroy(iothread, &local_err); if (local_err) { error_propagate_prepend(errp, local_err, - "failed to destroy iothread: "); + "failed to destroy iothread: "); return; } } if (drive) { - Error *local_err = NULL; - xen_block_drive_destroy(drive, &local_err); if (local_err) { error_propagate_prepend(errp, local_err, - "failed to destroy drive: "); + "failed to destroy drive: "); + return; } } } diff --git a/hw/core/cpu.c b/hw/core/cpu.c index b889878f3c..786a1bec8a 100644 --- a/hw/core/cpu.c +++ b/hw/core/cpu.c @@ -177,7 +177,7 @@ static int cpu_common_write_elf64_note(WriteCoreDumpFunction f, } -static int cpu_common_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg) +static int cpu_common_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg) { return 0; } diff --git a/hw/hppa/hppa_sys.h b/hw/hppa/hppa_sys.h index 4d08501464..0b18271cc9 100644 --- a/hw/hppa/hppa_sys.h +++ b/hw/hppa/hppa_sys.h @@ -5,7 +5,6 @@ #include "hw/pci/pci.h" #include "hw/pci/pci_host.h" -#include "hw/ide.h" #include "hw/boards.h" #include "hw/intc/i8259.h" diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index 9175f4b790..00dd9f58d6 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -13,7 +13,6 @@ #include "sysemu/reset.h" #include "sysemu/sysemu.h" #include "hw/rtc/mc146818rtc.h" -#include "hw/ide.h" #include "hw/timer/i8254.h" #include "hw/char/serial.h" #include "hw/net/lasi_82596.h" diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index e2d98243bc..e6756216f9 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -38,7 +38,7 @@ #include "hw/pci/pci_ids.h" #include "hw/usb.h" #include "net/net.h" -#include "hw/ide.h" +#include "hw/ide/pci.h" #include "hw/irq.h" #include "sysemu/kvm.h" #include "hw/kvm/clock.h" @@ -85,7 +85,6 @@ static void pc_init1(MachineState *machine, int piix3_devfn = -1; qemu_irq smi_irq; GSIState *gsi_state; - DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; BusState *idebus[MAX_IDE_BUS]; ISADevice *rtc_state; MemoryRegion *ram_memory; @@ -239,21 +238,22 @@ static void pc_init1(MachineState *machine, pc_nic_init(pcmc, isa_bus, pci_bus); - ide_drive_get(hd, ARRAY_SIZE(hd)); if (pcmc->pci_enabled) { PCIDevice *dev; - if (xen_enabled()) { - dev = pci_piix3_xen_ide_init(pci_bus, hd, piix3_devfn + 1); - } else { - dev = pci_piix3_ide_init(pci_bus, hd, piix3_devfn + 1); - } + + dev = pci_create_simple(pci_bus, piix3_devfn + 1, + xen_enabled() ? "piix3-ide-xen" : "piix3-ide"); + pci_ide_create_devs(dev); idebus[0] = qdev_get_child_bus(&dev->qdev, "ide.0"); idebus[1] = qdev_get_child_bus(&dev->qdev, "ide.1"); pc_cmos_init(pcms, idebus[0], idebus[1], rtc_state); } #ifdef CONFIG_IDE_ISA -else { + else { + DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; int i; + + ide_drive_get(hd, ARRAY_SIZE(hd)); for (i = 0; i < MAX_IDE_BUS; i++) { ISADevice *dev; char busname[] = "ide.0"; diff --git a/hw/ide/ahci_internal.h b/hw/ide/ahci_internal.h index 73424516da..bab0459774 100644 --- a/hw/ide/ahci_internal.h +++ b/hw/ide/ahci_internal.h @@ -27,6 +27,7 @@ #include "hw/ide/ahci.h" #include "hw/ide/internal.h" #include "hw/sysbus.h" +#include "hw/pci/pci.h" #define AHCI_MEM_BAR_SIZE 0x1000 #define AHCI_MAX_PORTS 32 diff --git a/hw/ide/cmd646.c b/hw/ide/cmd646.c index 335c060673..699f25824d 100644 --- a/hw/ide/cmd646.c +++ b/hw/ide/cmd646.c @@ -207,9 +207,9 @@ static void cmd646_set_irq(void *opaque, int channel, int level) cmd646_update_irq(pd); } -static void cmd646_reset(void *opaque) +static void cmd646_reset(DeviceState *dev) { - PCIIDEState *d = opaque; + PCIIDEState *d = PCI_IDE(dev); unsigned int i; for (i = 0; i < 2; i++) { @@ -301,9 +301,6 @@ static void pci_cmd646_ide_realize(PCIDevice *dev, Error **errp) ide_register_restart_cb(&d->bus[i]); } g_free(irq); - - vmstate_register(VMSTATE_IF(dev), 0, &vmstate_ide_pci, d); - qemu_register_reset(cmd646_reset, d); } static void pci_cmd646_ide_exitfn(PCIDevice *dev) @@ -317,18 +314,6 @@ static void pci_cmd646_ide_exitfn(PCIDevice *dev) } } -void pci_cmd646_ide_init(PCIBus *bus, DriveInfo **hd_table, - int secondary_ide_enabled) -{ - PCIDevice *dev; - - dev = pci_create(bus, -1, "cmd646-ide"); - qdev_prop_set_uint32(&dev->qdev, "secondary", secondary_ide_enabled); - qdev_init_nofail(&dev->qdev); - - pci_ide_create_devs(dev, hd_table); -} - static Property cmd646_ide_properties[] = { DEFINE_PROP_UINT32("secondary", PCIIDEState, secondary, 0), DEFINE_PROP_END_OF_LIST(), @@ -339,6 +324,8 @@ static void cmd646_ide_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + dc->reset = cmd646_reset; + dc->vmsd = &vmstate_ide_pci; k->realize = pci_cmd646_ide_realize; k->exit = pci_cmd646_ide_exitfn; k->vendor_id = PCI_VENDOR_ID_CMD; diff --git a/hw/ide/pci.c b/hw/ide/pci.c index 1a6a287e76..97347f07f1 100644 --- a/hw/ide/pci.c +++ b/hw/ide/pci.c @@ -476,17 +476,20 @@ const VMStateDescription vmstate_ide_pci = { } }; -void pci_ide_create_devs(PCIDevice *dev, DriveInfo **hd_table) +/* hd_table must contain 4 block drivers */ +void pci_ide_create_devs(PCIDevice *dev) { PCIIDEState *d = PCI_IDE(dev); + DriveInfo *hd_table[2 * MAX_IDE_DEVS]; static const int bus[4] = { 0, 0, 1, 1 }; static const int unit[4] = { 0, 1, 0, 1 }; int i; + ide_drive_get(hd_table, ARRAY_SIZE(hd_table)); for (i = 0; i < 4; i++) { - if (hd_table[i] == NULL) - continue; - ide_create_drive(d->bus+bus[i], unit[i], hd_table[i]); + if (hd_table[i]) { + ide_create_drive(d->bus + bus[i], unit[i], hd_table[i]); + } } } diff --git a/hw/ide/piix.c b/hw/ide/piix.c index bc575b4d70..3b2de4c312 100644 --- a/hw/ide/piix.c +++ b/hw/ide/piix.c @@ -197,15 +197,6 @@ int pci_piix3_xen_ide_unplug(DeviceState *dev, bool aux) return 0; } -PCIDevice *pci_piix3_xen_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn) -{ - PCIDevice *dev; - - dev = pci_create_simple(bus, devfn, "piix3-ide-xen"); - pci_ide_create_devs(dev, hd_table); - return dev; -} - static void pci_piix_ide_exitfn(PCIDevice *dev) { PCIIDEState *d = PCI_IDE(dev); @@ -217,28 +208,7 @@ static void pci_piix_ide_exitfn(PCIDevice *dev) } } -/* hd_table must contain 4 block drivers */ /* NOTE: for the PIIX3, the IRQs and IOports are hardcoded */ -PCIDevice *pci_piix3_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn) -{ - PCIDevice *dev; - - dev = pci_create_simple(bus, devfn, "piix3-ide"); - pci_ide_create_devs(dev, hd_table); - return dev; -} - -/* hd_table must contain 4 block drivers */ -/* NOTE: for the PIIX4, the IRQs and IOports are hardcoded */ -PCIDevice *pci_piix4_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn) -{ - PCIDevice *dev; - - dev = pci_create_simple(bus, devfn, "piix4-ide"); - pci_ide_create_devs(dev, hd_table); - return dev; -} - static void piix3_ide_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -266,6 +236,7 @@ static const TypeInfo piix3_ide_xen_info = { .class_init = piix3_ide_class_init, }; +/* NOTE: for the PIIX4, the IRQs and IOports are hardcoded */ static void piix4_ide_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ide/via.c b/hw/ide/via.c index 096de8dba0..8de4945cc1 100644 --- a/hw/ide/via.c +++ b/hw/ide/via.c @@ -113,10 +113,7 @@ static void via_ide_set_irq(void *opaque, int n, int level) } level = (d->config[0x70] & 0x80) || (d->config[0x78] & 0x80); - n = pci_get_byte(d->config + PCI_INTERRUPT_LINE); - if (n) { - qemu_set_irq(isa_get_irq(NULL, n), level); - } + qemu_set_irq(isa_get_irq(NULL, 14 + n), level); } static void via_ide_reset(DeviceState *dev) @@ -167,9 +164,10 @@ static void via_ide_realize(PCIDevice *dev, Error **errp) uint8_t *pci_conf = dev->config; int i; - pci_config_set_prog_interface(pci_conf, 0x8f); /* native PCI ATA mode */ + pci_config_set_prog_interface(pci_conf, 0x8a); /* legacy mode */ pci_set_long(pci_conf + PCI_CAPABILITY_LIST, 0x000000c0); - dev->wmask[PCI_INTERRUPT_LINE] = 0xf; + dev->wmask[PCI_INTERRUPT_LINE] = 0; + dev->wmask[PCI_CLASS_PROG] = 5; memory_region_init_io(&d->data_bar[0], OBJECT(d), &pci_ide_data_le_ops, &d->bus[0], "via-ide0-data", 8); @@ -190,8 +188,6 @@ static void via_ide_realize(PCIDevice *dev, Error **errp) bmdma_setup_bar(d); pci_register_bar(dev, 4, PCI_BASE_ADDRESS_SPACE_IO, &d->bmdma_bar); - vmstate_register(VMSTATE_IF(dev), 0, &vmstate_ide_pci, d); - for (i = 0; i < 2; i++) { ide_bus_new(&d->bus[i], sizeof(d->bus[i]), DEVICE(d), i, 2); ide_init2(&d->bus[i], qemu_allocate_irq(via_ide_set_irq, d, i)); @@ -213,20 +209,13 @@ static void via_ide_exitfn(PCIDevice *dev) } } -void via_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn) -{ - PCIDevice *dev; - - dev = pci_create_simple(bus, devfn, "via-ide"); - pci_ide_create_devs(dev, hd_table); -} - static void via_ide_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); dc->reset = via_ide_reset; + dc->vmsd = &vmstate_ide_pci; k->realize = via_ide_realize; k->exit = via_ide_exitfn; k->vendor_id = PCI_VENDOR_ID_VIA; diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c index 20c8155557..6608d7220a 100644 --- a/hw/intc/spapr_xive.c +++ b/hw/intc/spapr_xive.c @@ -677,8 +677,8 @@ static void spapr_xive_dt(SpaprInterruptController *intc, uint32_t nr_servers, uint64_t timas[2 * 2]; /* Interrupt number ranges for the IPIs */ uint32_t lisn_ranges[] = { - cpu_to_be32(0), - cpu_to_be32(nr_servers), + cpu_to_be32(SPAPR_IRQ_IPI), + cpu_to_be32(SPAPR_IRQ_IPI + nr_servers), }; /* * EQ size - the sizes of pages supported by the system 4K, 64K, diff --git a/hw/isa/piix4.c b/hw/isa/piix4.c index 7edec5e149..9a10fb9b3c 100644 --- a/hw/isa/piix4.c +++ b/hw/isa/piix4.c @@ -34,7 +34,7 @@ #include "hw/dma/i8257.h" #include "hw/timer/i8254.h" #include "hw/rtc/mc146818rtc.h" -#include "hw/ide.h" +#include "hw/ide/pci.h" #include "migration/vmstate.h" #include "sysemu/reset.h" #include "sysemu/runstate.h" @@ -240,28 +240,25 @@ static void piix4_register_types(void) type_init(piix4_register_types) -DeviceState *piix4_create(PCIBus *pci_bus, ISABus **isa_bus, - I2CBus **smbus, size_t ide_buses) +DeviceState *piix4_create(PCIBus *pci_bus, ISABus **isa_bus, I2CBus **smbus) { - size_t ide_drives = ide_buses * MAX_IDE_DEVS; - DriveInfo **hd; PCIDevice *pci; DeviceState *dev; + int devfn = PCI_DEVFN(10, 0); - pci = pci_create_simple_multifunction(pci_bus, PCI_DEVFN(10, 0), - true, TYPE_PIIX4_PCI_DEVICE); + pci = pci_create_simple_multifunction(pci_bus, devfn, true, + TYPE_PIIX4_PCI_DEVICE); dev = DEVICE(pci); if (isa_bus) { *isa_bus = ISA_BUS(qdev_get_child_bus(dev, "isa.0")); } - hd = g_new(DriveInfo *, ide_drives); - ide_drive_get(hd, ide_drives); - pci_piix4_ide_init(pci_bus, hd, pci->devfn + 1); - g_free(hd); - pci_create_simple(pci_bus, pci->devfn + 2, "piix4-usb-uhci"); + pci = pci_create_simple(pci_bus, devfn + 1, "piix4-ide"); + pci_ide_create_devs(pci); + + pci_create_simple(pci_bus, devfn + 2, "piix4-usb-uhci"); if (smbus) { - *smbus = piix4_pm_init(pci_bus, pci->devfn + 3, 0x1100, + *smbus = piix4_pm_init(pci_bus, devfn + 3, 0x1100, isa_get_irq(NULL, 9), NULL, 0, NULL); } diff --git a/hw/mips/mips_fulong2e.c b/hw/mips/mips_fulong2e.c index 4727b1d3a4..5040afd581 100644 --- a/hw/mips/mips_fulong2e.c +++ b/hw/mips/mips_fulong2e.c @@ -36,7 +36,7 @@ #include "audio/audio.h" #include "qemu/log.h" #include "hw/loader.h" -#include "hw/ide.h" +#include "hw/ide/pci.h" #include "elf.h" #include "hw/isa/vt82c686.h" #include "hw/rtc/mc146818rtc.h" @@ -238,7 +238,7 @@ static void vt82c686b_southbridge_init(PCIBus *pci_bus, int slot, qemu_irq intc, { qemu_irq *i8259; ISABus *isa_bus; - DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; + PCIDevice *dev; isa_bus = vt82c686b_isa_init(pci_bus, PCI_DEVFN(slot, 0)); if (!isa_bus) { @@ -256,8 +256,8 @@ static void vt82c686b_southbridge_init(PCIBus *pci_bus, int slot, qemu_irq intc, /* Super I/O */ isa_create_simple(isa_bus, TYPE_VT82C686B_SUPERIO); - ide_drive_get(hd, ARRAY_SIZE(hd)); - via_ide_init(pci_bus, hd, PCI_DEVFN(slot, 1)); + dev = pci_create_simple(pci_bus, PCI_DEVFN(slot, 1), "via-ide"); + pci_ide_create_devs(dev); pci_create_simple(pci_bus, PCI_DEVFN(slot, 2), "vt82c686b-usb-uhci"); pci_create_simple(pci_bus, PCI_DEVFN(slot, 3), "vt82c686b-usb-uhci"); diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index d380f73d7b..e4c4de1b4e 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -1403,7 +1403,7 @@ void mips_malta_init(MachineState *machine) pci_bus = gt64120_register(s->i8259); /* Southbridge */ - dev = piix4_create(pci_bus, &isa_bus, &smbus, MAX_IDE_BUS); + dev = piix4_create(pci_bus, &isa_bus, &smbus); /* Interrupt controller */ qdev_connect_gpio_out_named(dev, "intr", 0, i8259_irq); diff --git a/hw/mips/mips_r4k.c b/hw/mips/mips_r4k.c index ad8b75e286..3487013a4a 100644 --- a/hw/mips/mips_r4k.c +++ b/hw/mips/mips_r4k.c @@ -26,6 +26,7 @@ #include "qemu/log.h" #include "hw/mips/bios.h" #include "hw/ide.h" +#include "hw/ide/internal.h" #include "hw/loader.h" #include "elf.h" #include "hw/rtc/mc146818rtc.h" diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c index 1a0fad74e1..a8dc9b377d 100644 --- a/hw/misc/ivshmem.c +++ b/hw/misc/ivshmem.c @@ -832,7 +832,6 @@ static void ivshmem_common_realize(PCIDevice *dev, Error **errp) IVShmemState *s = IVSHMEM_COMMON(dev); Error *err = NULL; uint8_t *pci_conf; - Error *local_err = NULL; /* IRQFD requires MSI */ if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD) && @@ -899,9 +898,9 @@ static void ivshmem_common_realize(PCIDevice *dev, Error **errp) if (!ivshmem_is_master(s)) { error_setg(&s->migration_blocker, "Migration is disabled when using feature 'peer mode' in device 'ivshmem'"); - migrate_add_blocker(s->migration_blocker, &local_err); - if (local_err) { - error_propagate(errp, local_err); + migrate_add_blocker(s->migration_blocker, &err); + if (err) { + error_propagate(errp, err); error_free(s->migration_blocker); return; } diff --git a/hw/pci/pci.c b/hw/pci/pci.c index e1ed6677e1..b5bc842fac 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -302,8 +302,11 @@ static void pci_do_device_reset(PCIDevice *dev) pci_word_test_and_clear_mask(dev->config + PCI_STATUS, pci_get_word(dev->wmask + PCI_STATUS) | pci_get_word(dev->w1cmask + PCI_STATUS)); + /* Some devices make bits of PCI_INTERRUPT_LINE read only */ + pci_byte_test_and_clear_mask(dev->config + PCI_INTERRUPT_LINE, + pci_get_word(dev->wmask + PCI_INTERRUPT_LINE) | + pci_get_word(dev->w1cmask + PCI_INTERRUPT_LINE)); dev->config[PCI_CACHE_LINE_SIZE] = 0x0; - dev->config[PCI_INTERRUPT_LINE] = 0x0; for (r = 0; r < PCI_NUM_REGIONS; ++r) { PCIIORegion *region = &dev->io_regions[r]; if (!region->size) { diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index b2ec372958..428cf63578 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -62,7 +62,6 @@ #include "hw/char/escc.h" #include "hw/misc/macio/macio.h" #include "hw/ppc/openpic.h" -#include "hw/ide.h" #include "hw/loader.h" #include "hw/fw-path-provider.h" #include "elf.h" diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index faaa165f3f..101bdc5c4d 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -41,7 +41,6 @@ #include "hw/nvram/fw_cfg.h" #include "hw/char/escc.h" #include "hw/misc/macio/macio.h" -#include "hw/ide.h" #include "hw/loader.h" #include "hw/fw-path-provider.h" #include "elf.h" diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c index f150deca34..b5ffa48dac 100644 --- a/hw/ppc/pnv_lpc.c +++ b/hw/ppc/pnv_lpc.c @@ -829,7 +829,7 @@ ISABus *pnv_lpc_isa_create(PnvLpcController *lpc, bool use_cpld, Error **errp) bool hostboot_mode = !!pnv->fw_load_addr; /* let isa_bus_new() create its own bridge on SysBus otherwise - * devices speficied on the command line won't find the bus and + * devices specified on the command line won't find the bus and * will fail to create. */ isa_bus = isa_bus_new(NULL, &lpc->isa_mem, &lpc->isa_io, &local_err); diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index 111cc80867..44be9d25a2 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -37,7 +37,6 @@ #include "hw/boards.h" #include "qemu/error-report.h" #include "qemu/log.h" -#include "hw/ide.h" #include "hw/irq.h" #include "hw/loader.h" #include "hw/rtc/mc146818rtc.h" diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 41c0f2401f..9a2bd501aa 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -103,7 +103,7 @@ #define FW_OVERHEAD 0x2800000 #define KERNEL_LOAD_ADDR FW_MAX_SIZE -#define MIN_RMA_SLOF 128UL +#define MIN_RMA_SLOF (128 * MiB) #define PHANDLE_INTC 0x00001111 @@ -217,10 +217,9 @@ static int spapr_fixup_cpu_numa_dt(void *fdt, int offset, PowerPCCPU *cpu) sizeof(associativity)); } -/* Populate the "ibm,pa-features" property */ -static void spapr_populate_pa_features(SpaprMachineState *spapr, - PowerPCCPU *cpu, - void *fdt, int offset) +static void spapr_dt_pa_features(SpaprMachineState *spapr, + PowerPCCPU *cpu, + void *fdt, int offset) { uint8_t pa_features_206[] = { 6, 0, 0xf6, 0x1f, 0xc7, 0x00, 0x80, 0xc0 }; @@ -315,8 +314,8 @@ static void add_str(GString *s, const gchar *s1) g_string_append_len(s, s1, strlen(s1) + 1); } -static int spapr_populate_memory_node(void *fdt, int nodeid, hwaddr start, - hwaddr size) +static int spapr_dt_memory_node(void *fdt, int nodeid, hwaddr start, + hwaddr size) { uint32_t associativity[] = { cpu_to_be32(0x4), /* length */ @@ -341,257 +340,6 @@ static int spapr_populate_memory_node(void *fdt, int nodeid, hwaddr start, return off; } -static int spapr_populate_memory(SpaprMachineState *spapr, void *fdt) -{ - MachineState *machine = MACHINE(spapr); - hwaddr mem_start, node_size; - int i, nb_nodes = machine->numa_state->num_nodes; - NodeInfo *nodes = machine->numa_state->nodes; - - for (i = 0, mem_start = 0; i < nb_nodes; ++i) { - if (!nodes[i].node_mem) { - continue; - } - if (mem_start >= machine->ram_size) { - node_size = 0; - } else { - node_size = nodes[i].node_mem; - if (node_size > machine->ram_size - mem_start) { - node_size = machine->ram_size - mem_start; - } - } - if (!mem_start) { - /* spapr_machine_init() checks for rma_size <= node0_size - * already */ - spapr_populate_memory_node(fdt, i, 0, spapr->rma_size); - mem_start += spapr->rma_size; - node_size -= spapr->rma_size; - } - for ( ; node_size; ) { - hwaddr sizetmp = pow2floor(node_size); - - /* mem_start != 0 here */ - if (ctzl(mem_start) < ctzl(sizetmp)) { - sizetmp = 1ULL << ctzl(mem_start); - } - - spapr_populate_memory_node(fdt, i, mem_start, sizetmp); - node_size -= sizetmp; - mem_start += sizetmp; - } - } - - return 0; -} - -static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, - SpaprMachineState *spapr) -{ - MachineState *ms = MACHINE(spapr); - PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; - PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs); - int index = spapr_get_vcpu_id(cpu); - uint32_t segs[] = {cpu_to_be32(28), cpu_to_be32(40), - 0xffffffff, 0xffffffff}; - uint32_t tbfreq = kvm_enabled() ? kvmppc_get_tbfreq() - : SPAPR_TIMEBASE_FREQ; - uint32_t cpufreq = kvm_enabled() ? kvmppc_get_clockfreq() : 1000000000; - uint32_t page_sizes_prop[64]; - size_t page_sizes_prop_size; - unsigned int smp_threads = ms->smp.threads; - uint32_t vcpus_per_socket = smp_threads * ms->smp.cores; - uint32_t pft_size_prop[] = {0, cpu_to_be32(spapr->htab_shift)}; - int compat_smt = MIN(smp_threads, ppc_compat_max_vthreads(cpu)); - SpaprDrc *drc; - int drc_index; - uint32_t radix_AP_encodings[PPC_PAGE_SIZES_MAX_SZ]; - int i; - - drc = spapr_drc_by_id(TYPE_SPAPR_DRC_CPU, index); - if (drc) { - drc_index = spapr_drc_index(drc); - _FDT((fdt_setprop_cell(fdt, offset, "ibm,my-drc-index", drc_index))); - } - - _FDT((fdt_setprop_cell(fdt, offset, "reg", index))); - _FDT((fdt_setprop_string(fdt, offset, "device_type", "cpu"))); - - _FDT((fdt_setprop_cell(fdt, offset, "cpu-version", env->spr[SPR_PVR]))); - _FDT((fdt_setprop_cell(fdt, offset, "d-cache-block-size", - env->dcache_line_size))); - _FDT((fdt_setprop_cell(fdt, offset, "d-cache-line-size", - env->dcache_line_size))); - _FDT((fdt_setprop_cell(fdt, offset, "i-cache-block-size", - env->icache_line_size))); - _FDT((fdt_setprop_cell(fdt, offset, "i-cache-line-size", - env->icache_line_size))); - - if (pcc->l1_dcache_size) { - _FDT((fdt_setprop_cell(fdt, offset, "d-cache-size", - pcc->l1_dcache_size))); - } else { - warn_report("Unknown L1 dcache size for cpu"); - } - if (pcc->l1_icache_size) { - _FDT((fdt_setprop_cell(fdt, offset, "i-cache-size", - pcc->l1_icache_size))); - } else { - warn_report("Unknown L1 icache size for cpu"); - } - - _FDT((fdt_setprop_cell(fdt, offset, "timebase-frequency", tbfreq))); - _FDT((fdt_setprop_cell(fdt, offset, "clock-frequency", cpufreq))); - _FDT((fdt_setprop_cell(fdt, offset, "slb-size", cpu->hash64_opts->slb_size))); - _FDT((fdt_setprop_cell(fdt, offset, "ibm,slb-size", cpu->hash64_opts->slb_size))); - _FDT((fdt_setprop_string(fdt, offset, "status", "okay"))); - _FDT((fdt_setprop(fdt, offset, "64-bit", NULL, 0))); - - if (env->spr_cb[SPR_PURR].oea_read) { - _FDT((fdt_setprop_cell(fdt, offset, "ibm,purr", 1))); - } - if (env->spr_cb[SPR_SPURR].oea_read) { - _FDT((fdt_setprop_cell(fdt, offset, "ibm,spurr", 1))); - } - - if (ppc_hash64_has(cpu, PPC_HASH64_1TSEG)) { - _FDT((fdt_setprop(fdt, offset, "ibm,processor-segment-sizes", - segs, sizeof(segs)))); - } - - /* Advertise VSX (vector extensions) if available - * 1 == VMX / Altivec available - * 2 == VSX available - * - * Only CPUs for which we create core types in spapr_cpu_core.c - * are possible, and all of those have VMX */ - if (spapr_get_cap(spapr, SPAPR_CAP_VSX) != 0) { - _FDT((fdt_setprop_cell(fdt, offset, "ibm,vmx", 2))); - } else { - _FDT((fdt_setprop_cell(fdt, offset, "ibm,vmx", 1))); - } - - /* Advertise DFP (Decimal Floating Point) if available - * 0 / no property == no DFP - * 1 == DFP available */ - if (spapr_get_cap(spapr, SPAPR_CAP_DFP) != 0) { - _FDT((fdt_setprop_cell(fdt, offset, "ibm,dfp", 1))); - } - - page_sizes_prop_size = ppc_create_page_sizes_prop(cpu, page_sizes_prop, - sizeof(page_sizes_prop)); - if (page_sizes_prop_size) { - _FDT((fdt_setprop(fdt, offset, "ibm,segment-page-sizes", - page_sizes_prop, page_sizes_prop_size))); - } - - spapr_populate_pa_features(spapr, cpu, fdt, offset); - - _FDT((fdt_setprop_cell(fdt, offset, "ibm,chip-id", - cs->cpu_index / vcpus_per_socket))); - - _FDT((fdt_setprop(fdt, offset, "ibm,pft-size", - pft_size_prop, sizeof(pft_size_prop)))); - - if (ms->numa_state->num_nodes > 1) { - _FDT(spapr_fixup_cpu_numa_dt(fdt, offset, cpu)); - } - - _FDT(spapr_fixup_cpu_smt_dt(fdt, offset, cpu, compat_smt)); - - if (pcc->radix_page_info) { - for (i = 0; i < pcc->radix_page_info->count; i++) { - radix_AP_encodings[i] = - cpu_to_be32(pcc->radix_page_info->entries[i]); - } - _FDT((fdt_setprop(fdt, offset, "ibm,processor-radix-AP-encodings", - radix_AP_encodings, - pcc->radix_page_info->count * - sizeof(radix_AP_encodings[0])))); - } - - /* - * We set this property to let the guest know that it can use the large - * decrementer and its width in bits. - */ - if (spapr_get_cap(spapr, SPAPR_CAP_LARGE_DECREMENTER) != SPAPR_CAP_OFF) - _FDT((fdt_setprop_u32(fdt, offset, "ibm,dec-bits", - pcc->lrg_decr_bits))); -} - -static void spapr_populate_cpus_dt_node(void *fdt, SpaprMachineState *spapr) -{ - CPUState **rev; - CPUState *cs; - int n_cpus; - int cpus_offset; - char *nodename; - int i; - - cpus_offset = fdt_add_subnode(fdt, 0, "cpus"); - _FDT(cpus_offset); - _FDT((fdt_setprop_cell(fdt, cpus_offset, "#address-cells", 0x1))); - _FDT((fdt_setprop_cell(fdt, cpus_offset, "#size-cells", 0x0))); - - /* - * We walk the CPUs in reverse order to ensure that CPU DT nodes - * created by fdt_add_subnode() end up in the right order in FDT - * for the guest kernel the enumerate the CPUs correctly. - * - * The CPU list cannot be traversed in reverse order, so we need - * to do extra work. - */ - n_cpus = 0; - rev = NULL; - CPU_FOREACH(cs) { - rev = g_renew(CPUState *, rev, n_cpus + 1); - rev[n_cpus++] = cs; - } - - for (i = n_cpus - 1; i >= 0; i--) { - CPUState *cs = rev[i]; - PowerPCCPU *cpu = POWERPC_CPU(cs); - int index = spapr_get_vcpu_id(cpu); - DeviceClass *dc = DEVICE_GET_CLASS(cs); - int offset; - - if (!spapr_is_thread0_in_vcore(spapr, cpu)) { - continue; - } - - nodename = g_strdup_printf("%s@%x", dc->fw_name, index); - offset = fdt_add_subnode(fdt, cpus_offset, nodename); - g_free(nodename); - _FDT(offset); - spapr_populate_cpu_dt(cs, fdt, offset, spapr); - } - - g_free(rev); -} - -static int spapr_rng_populate_dt(void *fdt) -{ - int node; - int ret; - - node = qemu_fdt_add_subnode(fdt, "/ibm,platform-facilities"); - if (node <= 0) { - return -1; - } - ret = fdt_setprop_string(fdt, node, "device_type", - "ibm,platform-facilities"); - ret |= fdt_setprop_cell(fdt, node, "#address-cells", 0x1); - ret |= fdt_setprop_cell(fdt, node, "#size-cells", 0x0); - - node = fdt_add_subnode(fdt, node, "ibm,random-v1"); - if (node <= 0) { - return -1; - } - ret |= fdt_setprop_string(fdt, node, "compatible", "ibm,random"); - - return ret ? -1 : 0; -} - static uint32_t spapr_pc_dimm_node(MemoryDeviceInfoList *list, ram_addr_t addr) { MemoryDeviceInfoList *info; @@ -642,9 +390,8 @@ spapr_get_drconf_cell(uint32_t seq_lmbs, uint64_t base_addr, return elem; } -/* ibm,dynamic-memory-v2 */ -static int spapr_populate_drmem_v2(SpaprMachineState *spapr, void *fdt, - int offset, MemoryDeviceInfoList *dimms) +static int spapr_dt_dynamic_memory_v2(SpaprMachineState *spapr, void *fdt, + int offset, MemoryDeviceInfoList *dimms) { MachineState *machine = MACHINE(spapr); uint8_t *int_buf, *cur_index; @@ -735,8 +482,7 @@ static int spapr_populate_drmem_v2(SpaprMachineState *spapr, void *fdt, return 0; } -/* ibm,dynamic-memory */ -static int spapr_populate_drmem_v1(SpaprMachineState *spapr, void *fdt, +static int spapr_dt_dynamic_memory(SpaprMachineState *spapr, void *fdt, int offset, MemoryDeviceInfoList *dimms) { MachineState *machine = MACHINE(spapr); @@ -805,7 +551,8 @@ static int spapr_populate_drmem_v1(SpaprMachineState *spapr, void *fdt, * Refer to docs/specs/ppc-spapr-hotplug.txt for the documentation * of this device tree node. */ -static int spapr_populate_drconf_memory(SpaprMachineState *spapr, void *fdt) +static int spapr_dt_dynamic_reconfiguration_memory(SpaprMachineState *spapr, + void *fdt) { MachineState *machine = MACHINE(spapr); int nb_numa_nodes = machine->numa_state->num_nodes; @@ -844,9 +591,9 @@ static int spapr_populate_drconf_memory(SpaprMachineState *spapr, void *fdt) /* ibm,dynamic-memory or ibm,dynamic-memory-v2 */ dimms = qmp_memory_device_list(); if (spapr_ovec_test(spapr->ov5_cas, OV5_DRMEM_V2)) { - ret = spapr_populate_drmem_v2(spapr, fdt, offset, dimms); + ret = spapr_dt_dynamic_memory_v2(spapr, fdt, offset, dimms); } else { - ret = spapr_populate_drmem_v1(spapr, fdt, offset, dimms); + ret = spapr_dt_dynamic_memory(spapr, fdt, offset, dimms); } qapi_free_MemoryDeviceInfoList(dimms); @@ -877,30 +624,267 @@ static int spapr_populate_drconf_memory(SpaprMachineState *spapr, void *fdt) return ret; } -static int spapr_dt_cas_updates(SpaprMachineState *spapr, void *fdt, - SpaprOptionVector *ov5_updates) +static int spapr_dt_memory(SpaprMachineState *spapr, void *fdt) { + MachineState *machine = MACHINE(spapr); SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); - int ret = 0, offset; + hwaddr mem_start, node_size; + int i, nb_nodes = machine->numa_state->num_nodes; + NodeInfo *nodes = machine->numa_state->nodes; + + for (i = 0, mem_start = 0; i < nb_nodes; ++i) { + if (!nodes[i].node_mem) { + continue; + } + if (mem_start >= machine->ram_size) { + node_size = 0; + } else { + node_size = nodes[i].node_mem; + if (node_size > machine->ram_size - mem_start) { + node_size = machine->ram_size - mem_start; + } + } + if (!mem_start) { + /* spapr_machine_init() checks for rma_size <= node0_size + * already */ + spapr_dt_memory_node(fdt, i, 0, spapr->rma_size); + mem_start += spapr->rma_size; + node_size -= spapr->rma_size; + } + for ( ; node_size; ) { + hwaddr sizetmp = pow2floor(node_size); + + /* mem_start != 0 here */ + if (ctzl(mem_start) < ctzl(sizetmp)) { + sizetmp = 1ULL << ctzl(mem_start); + } + + spapr_dt_memory_node(fdt, i, mem_start, sizetmp); + node_size -= sizetmp; + mem_start += sizetmp; + } + } /* Generate ibm,dynamic-reconfiguration-memory node if required */ - if (spapr_ovec_test(ov5_updates, OV5_DRCONF_MEMORY)) { + if (spapr_ovec_test(spapr->ov5_cas, OV5_DRCONF_MEMORY)) { + int ret; + g_assert(smc->dr_lmb_enabled); - ret = spapr_populate_drconf_memory(spapr, fdt); + ret = spapr_dt_dynamic_reconfiguration_memory(spapr, fdt); if (ret) { return ret; } } - offset = fdt_path_offset(fdt, "/chosen"); - if (offset < 0) { - offset = fdt_add_subnode(fdt, 0, "chosen"); - if (offset < 0) { - return offset; + return 0; +} + +static void spapr_dt_cpu(CPUState *cs, void *fdt, int offset, + SpaprMachineState *spapr) +{ + MachineState *ms = MACHINE(spapr); + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs); + int index = spapr_get_vcpu_id(cpu); + uint32_t segs[] = {cpu_to_be32(28), cpu_to_be32(40), + 0xffffffff, 0xffffffff}; + uint32_t tbfreq = kvm_enabled() ? kvmppc_get_tbfreq() + : SPAPR_TIMEBASE_FREQ; + uint32_t cpufreq = kvm_enabled() ? kvmppc_get_clockfreq() : 1000000000; + uint32_t page_sizes_prop[64]; + size_t page_sizes_prop_size; + unsigned int smp_threads = ms->smp.threads; + uint32_t vcpus_per_socket = smp_threads * ms->smp.cores; + uint32_t pft_size_prop[] = {0, cpu_to_be32(spapr->htab_shift)}; + int compat_smt = MIN(smp_threads, ppc_compat_max_vthreads(cpu)); + SpaprDrc *drc; + int drc_index; + uint32_t radix_AP_encodings[PPC_PAGE_SIZES_MAX_SZ]; + int i; + + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_CPU, index); + if (drc) { + drc_index = spapr_drc_index(drc); + _FDT((fdt_setprop_cell(fdt, offset, "ibm,my-drc-index", drc_index))); + } + + _FDT((fdt_setprop_cell(fdt, offset, "reg", index))); + _FDT((fdt_setprop_string(fdt, offset, "device_type", "cpu"))); + + _FDT((fdt_setprop_cell(fdt, offset, "cpu-version", env->spr[SPR_PVR]))); + _FDT((fdt_setprop_cell(fdt, offset, "d-cache-block-size", + env->dcache_line_size))); + _FDT((fdt_setprop_cell(fdt, offset, "d-cache-line-size", + env->dcache_line_size))); + _FDT((fdt_setprop_cell(fdt, offset, "i-cache-block-size", + env->icache_line_size))); + _FDT((fdt_setprop_cell(fdt, offset, "i-cache-line-size", + env->icache_line_size))); + + if (pcc->l1_dcache_size) { + _FDT((fdt_setprop_cell(fdt, offset, "d-cache-size", + pcc->l1_dcache_size))); + } else { + warn_report("Unknown L1 dcache size for cpu"); + } + if (pcc->l1_icache_size) { + _FDT((fdt_setprop_cell(fdt, offset, "i-cache-size", + pcc->l1_icache_size))); + } else { + warn_report("Unknown L1 icache size for cpu"); + } + + _FDT((fdt_setprop_cell(fdt, offset, "timebase-frequency", tbfreq))); + _FDT((fdt_setprop_cell(fdt, offset, "clock-frequency", cpufreq))); + _FDT((fdt_setprop_cell(fdt, offset, "slb-size", cpu->hash64_opts->slb_size))); + _FDT((fdt_setprop_cell(fdt, offset, "ibm,slb-size", cpu->hash64_opts->slb_size))); + _FDT((fdt_setprop_string(fdt, offset, "status", "okay"))); + _FDT((fdt_setprop(fdt, offset, "64-bit", NULL, 0))); + + if (env->spr_cb[SPR_PURR].oea_read) { + _FDT((fdt_setprop_cell(fdt, offset, "ibm,purr", 1))); + } + if (env->spr_cb[SPR_SPURR].oea_read) { + _FDT((fdt_setprop_cell(fdt, offset, "ibm,spurr", 1))); + } + + if (ppc_hash64_has(cpu, PPC_HASH64_1TSEG)) { + _FDT((fdt_setprop(fdt, offset, "ibm,processor-segment-sizes", + segs, sizeof(segs)))); + } + + /* Advertise VSX (vector extensions) if available + * 1 == VMX / Altivec available + * 2 == VSX available + * + * Only CPUs for which we create core types in spapr_cpu_core.c + * are possible, and all of those have VMX */ + if (spapr_get_cap(spapr, SPAPR_CAP_VSX) != 0) { + _FDT((fdt_setprop_cell(fdt, offset, "ibm,vmx", 2))); + } else { + _FDT((fdt_setprop_cell(fdt, offset, "ibm,vmx", 1))); + } + + /* Advertise DFP (Decimal Floating Point) if available + * 0 / no property == no DFP + * 1 == DFP available */ + if (spapr_get_cap(spapr, SPAPR_CAP_DFP) != 0) { + _FDT((fdt_setprop_cell(fdt, offset, "ibm,dfp", 1))); + } + + page_sizes_prop_size = ppc_create_page_sizes_prop(cpu, page_sizes_prop, + sizeof(page_sizes_prop)); + if (page_sizes_prop_size) { + _FDT((fdt_setprop(fdt, offset, "ibm,segment-page-sizes", + page_sizes_prop, page_sizes_prop_size))); + } + + spapr_dt_pa_features(spapr, cpu, fdt, offset); + + _FDT((fdt_setprop_cell(fdt, offset, "ibm,chip-id", + cs->cpu_index / vcpus_per_socket))); + + _FDT((fdt_setprop(fdt, offset, "ibm,pft-size", + pft_size_prop, sizeof(pft_size_prop)))); + + if (ms->numa_state->num_nodes > 1) { + _FDT(spapr_fixup_cpu_numa_dt(fdt, offset, cpu)); + } + + _FDT(spapr_fixup_cpu_smt_dt(fdt, offset, cpu, compat_smt)); + + if (pcc->radix_page_info) { + for (i = 0; i < pcc->radix_page_info->count; i++) { + radix_AP_encodings[i] = + cpu_to_be32(pcc->radix_page_info->entries[i]); } + _FDT((fdt_setprop(fdt, offset, "ibm,processor-radix-AP-encodings", + radix_AP_encodings, + pcc->radix_page_info->count * + sizeof(radix_AP_encodings[0])))); } - return spapr_ovec_populate_dt(fdt, offset, spapr->ov5_cas, - "ibm,architecture-vec-5"); + + /* + * We set this property to let the guest know that it can use the large + * decrementer and its width in bits. + */ + if (spapr_get_cap(spapr, SPAPR_CAP_LARGE_DECREMENTER) != SPAPR_CAP_OFF) + _FDT((fdt_setprop_u32(fdt, offset, "ibm,dec-bits", + pcc->lrg_decr_bits))); +} + +static void spapr_dt_cpus(void *fdt, SpaprMachineState *spapr) +{ + CPUState **rev; + CPUState *cs; + int n_cpus; + int cpus_offset; + char *nodename; + int i; + + cpus_offset = fdt_add_subnode(fdt, 0, "cpus"); + _FDT(cpus_offset); + _FDT((fdt_setprop_cell(fdt, cpus_offset, "#address-cells", 0x1))); + _FDT((fdt_setprop_cell(fdt, cpus_offset, "#size-cells", 0x0))); + + /* + * We walk the CPUs in reverse order to ensure that CPU DT nodes + * created by fdt_add_subnode() end up in the right order in FDT + * for the guest kernel the enumerate the CPUs correctly. + * + * The CPU list cannot be traversed in reverse order, so we need + * to do extra work. + */ + n_cpus = 0; + rev = NULL; + CPU_FOREACH(cs) { + rev = g_renew(CPUState *, rev, n_cpus + 1); + rev[n_cpus++] = cs; + } + + for (i = n_cpus - 1; i >= 0; i--) { + CPUState *cs = rev[i]; + PowerPCCPU *cpu = POWERPC_CPU(cs); + int index = spapr_get_vcpu_id(cpu); + DeviceClass *dc = DEVICE_GET_CLASS(cs); + int offset; + + if (!spapr_is_thread0_in_vcore(spapr, cpu)) { + continue; + } + + nodename = g_strdup_printf("%s@%x", dc->fw_name, index); + offset = fdt_add_subnode(fdt, cpus_offset, nodename); + g_free(nodename); + _FDT(offset); + spapr_dt_cpu(cs, fdt, offset, spapr); + } + + g_free(rev); +} + +static int spapr_dt_rng(void *fdt) +{ + int node; + int ret; + + node = qemu_fdt_add_subnode(fdt, "/ibm,platform-facilities"); + if (node <= 0) { + return -1; + } + ret = fdt_setprop_string(fdt, node, "device_type", + "ibm,platform-facilities"); + ret |= fdt_setprop_cell(fdt, node, "#address-cells", 0x1); + ret |= fdt_setprop_cell(fdt, node, "#size-cells", 0x0); + + node = fdt_add_subnode(fdt, node, "ibm,random-v1"); + if (node <= 0) { + return -1; + } + ret |= fdt_setprop_string(fdt, node, "compatible", "ibm,random"); + + return ret ? -1 : 0; } static void spapr_dt_rtas(SpaprMachineState *spapr, void *fdt) @@ -967,6 +951,29 @@ static void spapr_dt_rtas(SpaprMachineState *spapr, void *fdt) _FDT(fdt_setprop(fdt, rtas, "ibm,max-associativity-domains", maxdomains, sizeof(maxdomains))); + /* + * FWNMI reserves RTAS_ERROR_LOG_MAX for the machine check error log, + * and 16 bytes per CPU for system reset error log plus an extra 8 bytes. + * + * The system reset requirements are driven by existing Linux and PowerVM + * implementation which (contrary to PAPR) saves r3 in the error log + * structure like machine check, so Linux expects to find the saved r3 + * value at the address in r3 upon FWNMI-enabled sreset interrupt (and + * does not look at the error value). + * + * System reset interrupts are not subject to interlock like machine + * check, so this memory area could be corrupted if the sreset is + * interrupted by a machine check (or vice versa) if it was shared. To + * prevent this, system reset uses per-CPU areas for the sreset save + * area. A system reset that interrupts a system reset handler could + * still overwrite this area, but Linux doesn't try to recover in that + * case anyway. + * + * The extra 8 bytes is required because Linux's FWNMI error log check + * is off-by-one. + */ + _FDT(fdt_setprop_cell(fdt, rtas, "rtas-size", RTAS_ERROR_LOG_MAX + + ms->smp.max_cpus * sizeof(uint64_t)*2 + sizeof(uint64_t))); _FDT(fdt_setprop_cell(fdt, rtas, "rtas-error-log-max", RTAS_ERROR_LOG_MAX)); _FDT(fdt_setprop_cell(fdt, rtas, "rtas-event-scan-rate", @@ -1040,81 +1047,91 @@ static void spapr_dt_ov5_platform_support(SpaprMachineState *spapr, void *fdt, val, sizeof(val))); } -static void spapr_dt_chosen(SpaprMachineState *spapr, void *fdt) +static void spapr_dt_chosen(SpaprMachineState *spapr, void *fdt, bool reset) { MachineState *machine = MACHINE(spapr); SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); int chosen; - const char *boot_device = machine->boot_order; - char *stdout_path = spapr_vio_stdout_path(spapr->vio_bus); - size_t cb = 0; - char *bootlist = get_boot_devices_list(&cb); _FDT(chosen = fdt_add_subnode(fdt, 0, "chosen")); - if (machine->kernel_cmdline && machine->kernel_cmdline[0]) { - _FDT(fdt_setprop_string(fdt, chosen, "bootargs", - machine->kernel_cmdline)); - } - if (spapr->initrd_size) { - _FDT(fdt_setprop_cell(fdt, chosen, "linux,initrd-start", - spapr->initrd_base)); - _FDT(fdt_setprop_cell(fdt, chosen, "linux,initrd-end", - spapr->initrd_base + spapr->initrd_size)); - } + if (reset) { + const char *boot_device = machine->boot_order; + char *stdout_path = spapr_vio_stdout_path(spapr->vio_bus); + size_t cb = 0; + char *bootlist = get_boot_devices_list(&cb); + + if (machine->kernel_cmdline && machine->kernel_cmdline[0]) { + _FDT(fdt_setprop_string(fdt, chosen, "bootargs", + machine->kernel_cmdline)); + } - if (spapr->kernel_size) { - uint64_t kprop[2] = { cpu_to_be64(spapr->kernel_addr), - cpu_to_be64(spapr->kernel_size) }; + if (spapr->initrd_size) { + _FDT(fdt_setprop_cell(fdt, chosen, "linux,initrd-start", + spapr->initrd_base)); + _FDT(fdt_setprop_cell(fdt, chosen, "linux,initrd-end", + spapr->initrd_base + spapr->initrd_size)); + } + + if (spapr->kernel_size) { + uint64_t kprop[2] = { cpu_to_be64(spapr->kernel_addr), + cpu_to_be64(spapr->kernel_size) }; - _FDT(fdt_setprop(fdt, chosen, "qemu,boot-kernel", + _FDT(fdt_setprop(fdt, chosen, "qemu,boot-kernel", &kprop, sizeof(kprop))); - if (spapr->kernel_le) { - _FDT(fdt_setprop(fdt, chosen, "qemu,boot-kernel-le", NULL, 0)); + if (spapr->kernel_le) { + _FDT(fdt_setprop(fdt, chosen, "qemu,boot-kernel-le", NULL, 0)); + } } - } - if (boot_menu) { - _FDT((fdt_setprop_cell(fdt, chosen, "qemu,boot-menu", boot_menu))); - } - _FDT(fdt_setprop_cell(fdt, chosen, "qemu,graphic-width", graphic_width)); - _FDT(fdt_setprop_cell(fdt, chosen, "qemu,graphic-height", graphic_height)); - _FDT(fdt_setprop_cell(fdt, chosen, "qemu,graphic-depth", graphic_depth)); + if (boot_menu) { + _FDT((fdt_setprop_cell(fdt, chosen, "qemu,boot-menu", boot_menu))); + } + _FDT(fdt_setprop_cell(fdt, chosen, "qemu,graphic-width", graphic_width)); + _FDT(fdt_setprop_cell(fdt, chosen, "qemu,graphic-height", graphic_height)); + _FDT(fdt_setprop_cell(fdt, chosen, "qemu,graphic-depth", graphic_depth)); - if (cb && bootlist) { - int i; + if (cb && bootlist) { + int i; - for (i = 0; i < cb; i++) { - if (bootlist[i] == '\n') { - bootlist[i] = ' '; + for (i = 0; i < cb; i++) { + if (bootlist[i] == '\n') { + bootlist[i] = ' '; + } } + _FDT(fdt_setprop_string(fdt, chosen, "qemu,boot-list", bootlist)); } - _FDT(fdt_setprop_string(fdt, chosen, "qemu,boot-list", bootlist)); - } - if (boot_device && strlen(boot_device)) { - _FDT(fdt_setprop_string(fdt, chosen, "qemu,boot-device", boot_device)); - } + if (boot_device && strlen(boot_device)) { + _FDT(fdt_setprop_string(fdt, chosen, "qemu,boot-device", boot_device)); + } + + if (!spapr->has_graphics && stdout_path) { + /* + * "linux,stdout-path" and "stdout" properties are + * deprecated by linux kernel. New platforms should only + * use the "stdout-path" property. Set the new property + * and continue using older property to remain compatible + * with the existing firmware. + */ + _FDT(fdt_setprop_string(fdt, chosen, "linux,stdout-path", stdout_path)); + _FDT(fdt_setprop_string(fdt, chosen, "stdout-path", stdout_path)); + } - if (!spapr->has_graphics && stdout_path) { /* - * "linux,stdout-path" and "stdout" properties are deprecated by linux - * kernel. New platforms should only use the "stdout-path" property. Set - * the new property and continue using older property to remain - * compatible with the existing firmware. + * We can deal with BAR reallocation just fine, advertise it + * to the guest */ - _FDT(fdt_setprop_string(fdt, chosen, "linux,stdout-path", stdout_path)); - _FDT(fdt_setprop_string(fdt, chosen, "stdout-path", stdout_path)); - } + if (smc->linux_pci_probe) { + _FDT(fdt_setprop_cell(fdt, chosen, "linux,pci-probe-only", 0)); + } - /* We can deal with BAR reallocation just fine, advertise it to the guest */ - if (smc->linux_pci_probe) { - _FDT(fdt_setprop_cell(fdt, chosen, "linux,pci-probe-only", 0)); - } + spapr_dt_ov5_platform_support(spapr, fdt, chosen); - spapr_dt_ov5_platform_support(spapr, fdt, chosen); + g_free(stdout_path); + g_free(bootlist); + } - g_free(stdout_path); - g_free(bootlist); + _FDT(spapr_dt_ovec(fdt, chosen, spapr->ov5_cas, "ibm,architecture-vec-5")); } static void spapr_dt_hypervisor(SpaprMachineState *spapr, void *fdt) @@ -1192,7 +1209,7 @@ void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space) /* /interrupt controller */ spapr_irq_dt(spapr, spapr_max_server_number(spapr), fdt, PHANDLE_INTC); - ret = spapr_populate_memory(spapr, fdt); + ret = spapr_dt_memory(spapr, fdt); if (ret < 0) { error_report("couldn't setup memory nodes in fdt"); exit(1); @@ -1202,7 +1219,7 @@ void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space) spapr_dt_vdevice(spapr->vio_bus, fdt); if (object_resolve_path_type("", TYPE_SPAPR_RNG, NULL)) { - ret = spapr_rng_populate_dt(fdt); + ret = spapr_dt_rng(fdt); if (ret < 0) { error_report("could not set up rng device in the fdt"); exit(1); @@ -1217,8 +1234,7 @@ void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space) } } - /* cpus */ - spapr_populate_cpus_dt_node(fdt, spapr); + spapr_dt_cpus(fdt, spapr); if (smc->dr_lmb_enabled) { _FDT(spapr_dt_drc(fdt, 0, NULL, SPAPR_DR_CONNECTOR_TYPE_LMB)); @@ -1240,9 +1256,7 @@ void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space) spapr_dt_rtas(spapr, fdt); /* /chosen */ - if (reset) { - spapr_dt_chosen(spapr, fdt); - } + spapr_dt_chosen(spapr, fdt, reset); /* /hypervisor */ if (kvm_enabled()) { @@ -1261,13 +1275,6 @@ void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space) } } - /* ibm,client-architecture-support updates */ - ret = spapr_dt_cas_updates(spapr, fdt, spapr->ov5_cas); - if (ret < 0) { - error_report("couldn't setup CAS properties fdt"); - exit(1); - } - if (smc->dr_phb_enabled) { ret = spapr_dt_drc(fdt, 0, NULL, SPAPR_DR_CONNECTOR_TYPE_PHB); if (ret < 0) { @@ -1569,7 +1576,7 @@ void spapr_reallocate_hpt(SpaprMachineState *spapr, int shift, spapr_set_all_lpcrs(0, LPCR_HR | LPCR_UPRT); } -void spapr_setup_hpt_and_vrma(SpaprMachineState *spapr) +void spapr_setup_hpt(SpaprMachineState *spapr) { int hpt_shift; @@ -1585,9 +1592,16 @@ void spapr_setup_hpt_and_vrma(SpaprMachineState *spapr) } spapr_reallocate_hpt(spapr, hpt_shift, &error_fatal); - if (spapr->vrma_adjust) { - spapr->rma_size = kvmppc_rma_size(spapr_node0_size(MACHINE(spapr)), - spapr->htab_shift); + if (kvm_enabled()) { + hwaddr vrma_limit = kvmppc_vrma_limit(spapr->htab_shift); + + /* Check our RMA fits in the possible VRMA */ + if (vrma_limit < spapr->rma_size) { + error_report("Unable to create %" HWADDR_PRIu + "MiB RMA (VRMA only allows %" HWADDR_PRIu "MiB", + spapr->rma_size / MiB, vrma_limit / MiB); + exit(EXIT_FAILURE); + } } } @@ -1627,7 +1641,7 @@ static void spapr_machine_reset(MachineState *machine) spapr->patb_entry = PATE1_GR; spapr_set_all_lpcrs(LPCR_HR | LPCR_UPRT, LPCR_HR | LPCR_UPRT); } else { - spapr_setup_hpt_and_vrma(spapr); + spapr_setup_hpt(spapr); } qemu_devices_reset(); @@ -1691,16 +1705,17 @@ static void spapr_machine_reset(MachineState *machine) spapr->fdt_blob = fdt; /* Set up the entry state */ - spapr_cpu_set_entry_state(first_ppc_cpu, SPAPR_ENTRY_POINT, fdt_addr); + spapr_cpu_set_entry_state(first_ppc_cpu, SPAPR_ENTRY_POINT, 0, fdt_addr, 0); first_ppc_cpu->env.gpr[5] = 0; spapr->cas_reboot = false; - spapr->mc_status = -1; - spapr->guest_machine_check_addr = -1; + spapr->fwnmi_system_reset_addr = -1; + spapr->fwnmi_machine_check_addr = -1; + spapr->fwnmi_machine_check_interlock = -1; /* Signal all vCPUs waiting on this condition */ - qemu_cond_broadcast(&spapr->mc_delivery_cond); + qemu_cond_broadcast(&spapr->fwnmi_machine_check_interlock_cond); migrate_del_blocker(spapr->fwnmi_migration_blocker); } @@ -1989,7 +2004,7 @@ static bool spapr_fwnmi_needed(void *opaque) { SpaprMachineState *spapr = (SpaprMachineState *)opaque; - return spapr->guest_machine_check_addr != -1; + return spapr->fwnmi_machine_check_addr != -1; } static int spapr_fwnmi_pre_save(void *opaque) @@ -2000,7 +2015,7 @@ static int spapr_fwnmi_pre_save(void *opaque) * Check if machine check handling is in progress and print a * warning message. */ - if (spapr->mc_status != -1) { + if (spapr->fwnmi_machine_check_interlock != -1) { warn_report("A machine check is being handled during migration. The" "handler may run and log hardware error on the destination"); } @@ -2008,15 +2023,16 @@ static int spapr_fwnmi_pre_save(void *opaque) return 0; } -static const VMStateDescription vmstate_spapr_machine_check = { - .name = "spapr_machine_check", +static const VMStateDescription vmstate_spapr_fwnmi = { + .name = "spapr_fwnmi", .version_id = 1, .minimum_version_id = 1, .needed = spapr_fwnmi_needed, .pre_save = spapr_fwnmi_pre_save, .fields = (VMStateField[]) { - VMSTATE_UINT64(guest_machine_check_addr, SpaprMachineState), - VMSTATE_INT32(mc_status, SpaprMachineState), + VMSTATE_UINT64(fwnmi_system_reset_addr, SpaprMachineState), + VMSTATE_UINT64(fwnmi_machine_check_addr, SpaprMachineState), + VMSTATE_INT32(fwnmi_machine_check_interlock, SpaprMachineState), VMSTATE_END_OF_LIST() }, }; @@ -2055,7 +2071,7 @@ static const VMStateDescription vmstate_spapr = { &vmstate_spapr_cap_large_decr, &vmstate_spapr_cap_ccf_assist, &vmstate_spapr_cap_fwnmi, - &vmstate_spapr_machine_check, + &vmstate_spapr_fwnmi, NULL } }; @@ -2641,6 +2657,42 @@ static PCIHostState *spapr_create_default_phb(void) return PCI_HOST_BRIDGE(dev); } +static hwaddr spapr_rma_size(SpaprMachineState *spapr, Error **errp) +{ + MachineState *machine = MACHINE(spapr); + SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); + hwaddr rma_size = machine->ram_size; + hwaddr node0_size = spapr_node0_size(machine); + + /* RMA has to fit in the first NUMA node */ + rma_size = MIN(rma_size, node0_size); + + /* + * VRMA access is via a special 1TiB SLB mapping, so the RMA can + * never exceed that + */ + rma_size = MIN(rma_size, 1 * TiB); + + /* + * Clamp the RMA size based on machine type. This is for + * migration compatibility with older qemu versions, which limited + * the RMA size for complicated and mostly bad reasons. + */ + if (smc->rma_limit) { + rma_size = MIN(rma_size, smc->rma_limit); + } + + if (rma_size < MIN_RMA_SLOF) { + error_setg(errp, + "pSeries SLOF firmware requires >= %" HWADDR_PRIx + "ldMiB guest RMA (Real Mode Area memory)", + MIN_RMA_SLOF / MiB); + return 0; + } + + return rma_size; +} + /* pSeries LPAR / sPAPR hardware init */ static void spapr_machine_init(MachineState *machine) { @@ -2652,7 +2704,6 @@ static void spapr_machine_init(MachineState *machine) PCIHostState *phb; int i; MemoryRegion *sysmem = get_system_memory(); - hwaddr node0_size = spapr_node0_size(machine); long load_limit, fw_size; char *filename; Error *resize_hpt_err = NULL; @@ -2692,34 +2743,7 @@ static void spapr_machine_init(MachineState *machine) exit(1); } - spapr->rma_size = node0_size; - - /* With KVM, we don't actually know whether KVM supports an - * unbounded RMA (PR KVM) or is limited by the hash table size - * (HV KVM using VRMA), so we always assume the latter - * - * In that case, we also limit the initial allocations for RTAS - * etc... to 256M since we have no way to know what the VRMA size - * is going to be as it depends on the size of the hash table - * which isn't determined yet. - */ - if (kvm_enabled()) { - spapr->vrma_adjust = 1; - spapr->rma_size = MIN(spapr->rma_size, 0x10000000); - } - - /* Actually we don't support unbounded RMA anymore since we added - * proper emulation of HV mode. The max we can get is 16G which - * also happens to be what we configure for PAPR mode so make sure - * we don't do anything bigger than that - */ - spapr->rma_size = MIN(spapr->rma_size, 0x400000000ull); - - if (spapr->rma_size > node0_size) { - error_report("Numa node 0 has to span the RMA (%#08"HWADDR_PRIx")", - spapr->rma_size); - exit(1); - } + spapr->rma_size = spapr_rma_size(spapr, &error_fatal); /* Setup a load limit for the ramdisk leaving room for SLOF and FDT */ load_limit = MIN(spapr->rma_size, RTAS_MAX_ADDR) - FW_OVERHEAD; @@ -2869,7 +2893,7 @@ static void spapr_machine_init(MachineState *machine) spapr_create_lmb_dr_connectors(spapr); } - if (spapr_get_cap(spapr, SPAPR_CAP_FWNMI_MCE) == SPAPR_CAP_ON) { + if (spapr_get_cap(spapr, SPAPR_CAP_FWNMI) == SPAPR_CAP_ON) { /* Create the error string for live migration blocker */ error_setg(&spapr->fwnmi_migration_blocker, "A machine check is being handled during migration. The handler" @@ -2956,13 +2980,6 @@ static void spapr_machine_init(MachineState *machine) } } - if (spapr->rma_size < (MIN_RMA_SLOF * MiB)) { - error_report( - "pSeries SLOF firmware requires >= %ldM guest RMA (Real Mode Area memory)", - MIN_RMA_SLOF); - exit(1); - } - if (kernel_filename) { uint64_t lowaddr = 0; @@ -3045,7 +3062,7 @@ static void spapr_machine_init(MachineState *machine) kvmppc_spapr_enable_inkernel_multitce(); } - qemu_cond_init(&spapr->mc_delivery_cond); + qemu_cond_init(&spapr->fwnmi_machine_check_interlock_cond); } static int spapr_kvm_type(MachineState *machine, const char *vm_type) @@ -3367,8 +3384,28 @@ static void spapr_machine_finalizefn(Object *obj) void spapr_do_system_reset_on_cpu(CPUState *cs, run_on_cpu_data arg) { + SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); + cpu_synchronize_state(cs); - ppc_cpu_do_system_reset(cs); + /* If FWNMI is inactive, addr will be -1, which will deliver to 0x100 */ + if (spapr->fwnmi_system_reset_addr != -1) { + uint64_t rtas_addr, addr; + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + + /* get rtas addr from fdt */ + rtas_addr = spapr_get_rtas_addr(); + if (!rtas_addr) { + qemu_system_guest_panicked(NULL); + return; + } + + addr = rtas_addr + RTAS_ERROR_LOG_MAX + cs->cpu_index * sizeof(uint64_t)*2; + stq_be_phys(&address_space_memory, addr, env->gpr[3]); + stq_be_phys(&address_space_memory, addr + sizeof(uint64_t), 0); + env->gpr[3] = addr; + } + ppc_cpu_do_system_reset(cs, spapr->fwnmi_system_reset_addr); } static void spapr_nmi(NMIState *n, int cpu_index, Error **errp) @@ -3389,8 +3426,8 @@ int spapr_lmb_dt_populate(SpaprDrc *drc, SpaprMachineState *spapr, addr = spapr_drc_index(drc) * SPAPR_MEMORY_BLOCK_SIZE; node = object_property_get_uint(OBJECT(drc->dev), PC_DIMM_NODE_PROP, &error_abort); - *fdt_start_offset = spapr_populate_memory_node(fdt, node, addr, - SPAPR_MEMORY_BLOCK_SIZE); + *fdt_start_offset = spapr_dt_memory_node(fdt, node, addr, + SPAPR_MEMORY_BLOCK_SIZE); return 0; } @@ -3791,7 +3828,7 @@ int spapr_core_dt_populate(SpaprDrc *drc, SpaprMachineState *spapr, offset = fdt_add_subnode(fdt, 0, nodename); g_free(nodename); - spapr_populate_cpu_dt(cs, fdt, offset, spapr); + spapr_dt_cpu(cs, fdt, offset, spapr); *fdt_start_offset = offset; return 0; @@ -4504,7 +4541,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) smc->default_caps.caps[SPAPR_CAP_NESTED_KVM_HV] = SPAPR_CAP_OFF; smc->default_caps.caps[SPAPR_CAP_LARGE_DECREMENTER] = SPAPR_CAP_ON; smc->default_caps.caps[SPAPR_CAP_CCF_ASSIST] = SPAPR_CAP_ON; - smc->default_caps.caps[SPAPR_CAP_FWNMI_MCE] = SPAPR_CAP_ON; + smc->default_caps.caps[SPAPR_CAP_FWNMI] = SPAPR_CAP_ON; spapr_caps_add_properties(smc, &error_abort); smc->irq = &spapr_irq_dual; smc->dr_phb_enabled = true; @@ -4582,7 +4619,8 @@ static void spapr_machine_4_2_class_options(MachineClass *mc) spapr_machine_5_0_class_options(mc); compat_props_add(mc->compat_props, hw_compat_4_2, hw_compat_4_2_len); smc->default_caps.caps[SPAPR_CAP_CCF_ASSIST] = SPAPR_CAP_OFF; - smc->default_caps.caps[SPAPR_CAP_FWNMI_MCE] = SPAPR_CAP_OFF; + smc->default_caps.caps[SPAPR_CAP_FWNMI] = SPAPR_CAP_OFF; + smc->rma_limit = 16 * GiB; mc->nvdimm_supported = false; } diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index 8b27d3ac09..679ae7959f 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -509,17 +509,14 @@ static void cap_ccf_assist_apply(SpaprMachineState *spapr, uint8_t val, } } -static void cap_fwnmi_mce_apply(SpaprMachineState *spapr, uint8_t val, +static void cap_fwnmi_apply(SpaprMachineState *spapr, uint8_t val, Error **errp) { if (!val) { return; /* Disabled by default */ } - if (tcg_enabled()) { - warn_report("Firmware Assisted Non-Maskable Interrupts(FWNMI) not " - "supported in TCG"); - } else if (kvm_enabled()) { + if (kvm_enabled()) { if (kvmppc_set_fwnmi() < 0) { error_setg(errp, "Firmware Assisted Non-Maskable Interrupts(FWNMI) " "not supported by KVM"); @@ -626,14 +623,14 @@ SpaprCapabilityInfo capability_table[SPAPR_CAP_NUM] = { .type = "bool", .apply = cap_ccf_assist_apply, }, - [SPAPR_CAP_FWNMI_MCE] = { - .name = "fwnmi-mce", - .description = "Handle fwnmi machine check exceptions", - .index = SPAPR_CAP_FWNMI_MCE, + [SPAPR_CAP_FWNMI] = { + .name = "fwnmi", + .description = "Implements PAPR FWNMI option", + .index = SPAPR_CAP_FWNMI, .get = spapr_cap_get_bool, .set = spapr_cap_set_bool, .type = "bool", - .apply = cap_fwnmi_mce_apply, + .apply = cap_fwnmi_apply, }, }; @@ -774,7 +771,7 @@ SPAPR_CAP_MIG_STATE(hpt_maxpagesize, SPAPR_CAP_HPT_MAXPAGESIZE); SPAPR_CAP_MIG_STATE(nested_kvm_hv, SPAPR_CAP_NESTED_KVM_HV); SPAPR_CAP_MIG_STATE(large_decr, SPAPR_CAP_LARGE_DECREMENTER); SPAPR_CAP_MIG_STATE(ccf_assist, SPAPR_CAP_CCF_ASSIST); -SPAPR_CAP_MIG_STATE(fwnmi, SPAPR_CAP_FWNMI_MCE); +SPAPR_CAP_MIG_STATE(fwnmi, SPAPR_CAP_FWNMI); void spapr_caps_init(SpaprMachineState *spapr) { diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index d09125d9af..ac1c109427 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -50,22 +50,14 @@ static void spapr_reset_vcpu(PowerPCCPU *cpu) * the settings below ensure proper operations with TCG in absence of * a real hypervisor. * - * Clearing VPM0 will also cause us to use RMOR in mmu-hash64.c for - * real mode accesses, which thankfully defaults to 0 and isn't - * accessible in guest mode. - * * Disable Power-saving mode Exit Cause exceptions for the CPU, so * we don't get spurious wakups before an RTAS start-cpu call. * For the same reason, set PSSCR_EC. */ - lpcr &= ~(LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV | pcc->lpcr_pm); + lpcr &= ~(LPCR_VPM1 | LPCR_ISL | LPCR_KBV | pcc->lpcr_pm); lpcr |= LPCR_LPES0 | LPCR_LPES1; env->spr[SPR_PSSCR] |= PSSCR_EC; - /* Set RMLS to the max (ie, 16G) */ - lpcr &= ~LPCR_RMLS; - lpcr |= 1ull << LPCR_RMLS_SHIFT; - ppc_store_lpcr(cpu, lpcr); /* Set a full AMOR so guest can use the AMR as it sees fit */ @@ -84,13 +76,17 @@ static void spapr_reset_vcpu(PowerPCCPU *cpu) spapr_irq_cpu_intc_reset(spapr, cpu); } -void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, target_ulong r3) +void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, + target_ulong r1, target_ulong r3, + target_ulong r4) { PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); CPUPPCState *env = &cpu->env; env->nip = nip; + env->gpr[1] = r1; env->gpr[3] = r3; + env->gpr[4] = r4; kvmppc_set_reg_ppc_online(cpu, 1); CPU(cpu)->halted = 0; /* Enable Power-saving mode Exit Cause exceptions */ diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c index 8b32b7eea5..323fcef4aa 100644 --- a/hw/ppc/spapr_events.c +++ b/hw/ppc/spapr_events.c @@ -786,28 +786,12 @@ static void spapr_mce_dispatch_elog(PowerPCCPU *cpu, bool recovered) { SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); CPUState *cs = CPU(cpu); - uint64_t rtas_addr; CPUPPCState *env = &cpu->env; - PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); - target_ulong msr = 0; + uint64_t rtas_addr; struct rtas_error_log log; struct mc_extended_log *ext_elog; uint32_t summary; - /* - * Properly set bits in MSR before we invoke the handler. - * SRR0/1, DAR and DSISR are properly set by KVM - */ - if (!(*pcc->interrupts_big_endian)(cpu)) { - msr |= (1ULL << MSR_LE); - } - - if (env->msr & (1ULL << MSR_SF)) { - msr |= (1ULL << MSR_SF); - } - - msr |= (1ULL << MSR_ME); - ext_elog = g_malloc0(sizeof(*ext_elog)); summary = spapr_mce_get_elog_type(cpu, recovered, ext_elog); @@ -823,8 +807,7 @@ static void spapr_mce_dispatch_elog(PowerPCCPU *cpu, bool recovered) /* get rtas addr from fdt */ rtas_addr = spapr_get_rtas_addr(); if (!rtas_addr) { - /* Unable to fetch rtas_addr. Hence reset the guest */ - ppc_cpu_do_system_reset(cs); + qemu_system_guest_panicked(NULL); g_free(ext_elog); return; } @@ -836,12 +819,11 @@ static void spapr_mce_dispatch_elog(PowerPCCPU *cpu, bool recovered) cpu_physical_memory_write(rtas_addr + RTAS_ERROR_LOG_OFFSET + sizeof(env->gpr[3]) + sizeof(log), ext_elog, sizeof(*ext_elog)); + g_free(ext_elog); env->gpr[3] = rtas_addr + RTAS_ERROR_LOG_OFFSET; - env->msr = msr; - env->nip = spapr->guest_machine_check_addr; - g_free(ext_elog); + ppc_cpu_do_fwnmi_machine_check(cs, spapr->fwnmi_machine_check_addr); } void spapr_mce_req_event(PowerPCCPU *cpu, bool recovered) @@ -851,7 +833,7 @@ void spapr_mce_req_event(PowerPCCPU *cpu, bool recovered) int ret; Error *local_err = NULL; - if (spapr->guest_machine_check_addr == -1) { + if (spapr->fwnmi_machine_check_addr == -1) { /* * This implies that we have hit a machine check either when the * guest has not registered FWNMI (i.e., "ibm,nmi-register" not @@ -863,19 +845,19 @@ void spapr_mce_req_event(PowerPCCPU *cpu, bool recovered) return; } - while (spapr->mc_status != -1) { + while (spapr->fwnmi_machine_check_interlock != -1) { /* * Check whether the same CPU got machine check error * while still handling the mc error (i.e., before * that CPU called "ibm,nmi-interlock") */ - if (spapr->mc_status == cpu->vcpu_id) { + if (spapr->fwnmi_machine_check_interlock == cpu->vcpu_id) { qemu_system_guest_panicked(NULL); return; } - qemu_cond_wait_iothread(&spapr->mc_delivery_cond); + qemu_cond_wait_iothread(&spapr->fwnmi_machine_check_interlock_cond); /* Meanwhile if the system is reset, then just return */ - if (spapr->guest_machine_check_addr == -1) { + if (spapr->fwnmi_machine_check_addr == -1) { return; } } @@ -891,7 +873,7 @@ void spapr_mce_req_event(PowerPCCPU *cpu, bool recovered) warn_report("Received a fwnmi while migration was in progress"); } - spapr->mc_status = cpu->vcpu_id; + spapr->fwnmi_machine_check_interlock = cpu->vcpu_id; spapr_mce_dispatch_elog(cpu, recovered); } @@ -983,6 +965,19 @@ void spapr_clear_pending_events(SpaprMachineState *spapr) } } +void spapr_clear_pending_hotplug_events(SpaprMachineState *spapr) +{ + SpaprEventLogEntry *entry = NULL, *next_entry; + + QTAILQ_FOREACH_SAFE(entry, &spapr->pending_events, next, next_entry) { + if (spapr_event_log_entry_type(entry) == RTAS_LOG_TYPE_HOTPLUG) { + QTAILQ_REMOVE(&spapr->pending_events, entry, next); + g_free(entry->extended_log); + g_free(entry); + } + } +} + void spapr_events_init(SpaprMachineState *spapr) { int epow_irq = SPAPR_IRQ_EPOW; diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 934eb12d27..40c86e91eb 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -1458,7 +1458,7 @@ static void spapr_check_setup_free_hpt(SpaprMachineState *spapr, spapr_free_hpt(spapr); } else if (!(patbe_new & PATE1_GR)) { /* RADIX->HASH || NOTHING->HASH : Allocate HPT */ - spapr_setup_hpt_and_vrma(spapr); + spapr_setup_hpt(spapr); } return; } @@ -1640,7 +1640,7 @@ static uint32_t cas_check_pvr(SpaprMachineState *spapr, PowerPCCPU *cpu, return best_compat; } -static bool spapr_transient_dev_before_cas(void) +static void spapr_handle_transient_dev_before_cas(SpaprMachineState *spapr) { Object *drc_container; ObjectProperty *prop; @@ -1658,10 +1658,11 @@ static bool spapr_transient_dev_before_cas(void) prop->name, NULL)); if (spapr_drc_transient(drc)) { - return true; + spapr_drc_reset(drc); } } - return false; + + spapr_clear_pending_hotplug_events(spapr); } static target_ulong h_client_architecture_support(PowerPCCPU *cpu, @@ -1834,9 +1835,7 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, spapr_irq_update_active_intc(spapr); - if (spapr_transient_dev_before_cas()) { - spapr->cas_reboot = true; - } + spapr_handle_transient_dev_before_cas(spapr); if (!spapr->cas_reboot) { void *fdt; @@ -1846,7 +1845,7 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, * (because the guest isn't going to use radix) then set it up here. */ if ((spapr->patb_entry & PATE1_GR) && !guest_radix) { /* legacy hash or new hash: */ - spapr_setup_hpt_and_vrma(spapr); + spapr_setup_hpt(spapr); } if (fdt_bufsize < sizeof(hdr)) { diff --git a/hw/ppc/spapr_nvdimm.c b/hw/ppc/spapr_nvdimm.c index 74eeb8bb74..25be8082d7 100644 --- a/hw/ppc/spapr_nvdimm.c +++ b/hw/ppc/spapr_nvdimm.c @@ -35,6 +35,7 @@ void spapr_nvdimm_validate_opts(NVDIMMDevice *nvdimm, uint64_t size, { char *uuidstr = NULL; QemuUUID uuid; + int ret; if (size % SPAPR_MINIMUM_SCM_BLOCK_SIZE) { error_setg(errp, "NVDIMM memory size excluding the label area" @@ -43,8 +44,10 @@ void spapr_nvdimm_validate_opts(NVDIMMDevice *nvdimm, uint64_t size, return; } - uuidstr = object_property_get_str(OBJECT(nvdimm), NVDIMM_UUID_PROP, NULL); - qemu_uuid_parse(uuidstr, &uuid); + uuidstr = object_property_get_str(OBJECT(nvdimm), NVDIMM_UUID_PROP, + &error_abort); + ret = qemu_uuid_parse(uuidstr, &uuid); + g_assert(!ret); g_free(uuidstr); if (qemu_uuid_is_null(&uuid)) { diff --git a/hw/ppc/spapr_ovec.c b/hw/ppc/spapr_ovec.c index 0ff6d1aeae..dd003f1763 100644 --- a/hw/ppc/spapr_ovec.c +++ b/hw/ppc/spapr_ovec.c @@ -200,8 +200,8 @@ SpaprOptionVector *spapr_ovec_parse_vector(target_ulong table_addr, int vector) return ov; } -int spapr_ovec_populate_dt(void *fdt, int fdt_offset, - SpaprOptionVector *ov, const char *name) +int spapr_dt_ovec(void *fdt, int fdt_offset, + SpaprOptionVector *ov, const char *name) { uint8_t vec[OV_MAXBYTES + 1]; uint16_t vec_len; diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index 656fdd2216..9fb8c8632a 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -190,7 +190,7 @@ static void rtas_start_cpu(PowerPCCPU *callcpu, SpaprMachineState *spapr, */ newcpu->env.tb_env->tb_offset = callcpu->env.tb_env->tb_offset; - spapr_cpu_set_entry_state(newcpu, start, r3); + spapr_cpu_set_entry_state(newcpu, start, 0, r3, 0); qemu_cpu_kick(CPU(newcpu)); @@ -414,8 +414,9 @@ static void rtas_ibm_nmi_register(PowerPCCPU *cpu, uint32_t nret, target_ulong rets) { hwaddr rtas_addr; + target_ulong sreset_addr, mce_addr; - if (spapr_get_cap(spapr, SPAPR_CAP_FWNMI_MCE) == SPAPR_CAP_OFF) { + if (spapr_get_cap(spapr, SPAPR_CAP_FWNMI) == SPAPR_CAP_OFF) { rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED); return; } @@ -426,7 +427,19 @@ static void rtas_ibm_nmi_register(PowerPCCPU *cpu, return; } - spapr->guest_machine_check_addr = rtas_ld(args, 1); + sreset_addr = rtas_ld(args, 0); + mce_addr = rtas_ld(args, 1); + + /* PAPR requires these are in the first 32M of memory and within RMA */ + if (sreset_addr >= 32 * MiB || sreset_addr >= spapr->rma_size || + mce_addr >= 32 * MiB || mce_addr >= spapr->rma_size) { + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); + return; + } + + spapr->fwnmi_system_reset_addr = sreset_addr; + spapr->fwnmi_machine_check_addr = mce_addr; + rtas_st(rets, 0, RTAS_OUT_SUCCESS); } @@ -436,29 +449,39 @@ static void rtas_ibm_nmi_interlock(PowerPCCPU *cpu, target_ulong args, uint32_t nret, target_ulong rets) { - if (spapr_get_cap(spapr, SPAPR_CAP_FWNMI_MCE) == SPAPR_CAP_OFF) { + if (spapr_get_cap(spapr, SPAPR_CAP_FWNMI) == SPAPR_CAP_OFF) { rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED); return; } - if (spapr->guest_machine_check_addr == -1) { + if (spapr->fwnmi_machine_check_addr == -1) { /* NMI register not called */ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } - if (spapr->mc_status != cpu->vcpu_id) { - /* The vCPU that hit the NMI should invoke "ibm,nmi-interlock" */ - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); + if (spapr->fwnmi_machine_check_interlock != cpu->vcpu_id) { + /* + * The vCPU that hit the NMI should invoke "ibm,nmi-interlock" + * This should be PARAM_ERROR, but Linux calls "ibm,nmi-interlock" + * for system reset interrupts, despite them not being interlocked. + * PowerVM silently ignores this and returns success here. Returning + * failure causes Linux to print the error "FWNMI: nmi-interlock + * failed: -3", although no other apparent ill effects, this is a + * regression for the user when enabling FWNMI. So for now, match + * PowerVM. When most Linux clients are fixed, this could be + * changed. + */ + rtas_st(rets, 0, RTAS_OUT_SUCCESS); return; } /* * vCPU issuing "ibm,nmi-interlock" is done with NMI handling, - * hence unset mc_status. + * hence unset fwnmi_machine_check_interlock. */ - spapr->mc_status = -1; - qemu_cond_signal(&spapr->mc_delivery_cond); + spapr->fwnmi_machine_check_interlock = -1; + qemu_cond_signal(&spapr->fwnmi_machine_check_interlock_cond); rtas_st(rets, 0, RTAS_OUT_SUCCESS); migrate_del_blocker(spapr->fwnmi_migration_blocker); } diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c index 7d584e7732..923488beb2 100644 --- a/hw/scsi/spapr_vscsi.c +++ b/hw/scsi/spapr_vscsi.c @@ -55,6 +55,8 @@ #define VSCSI_MAX_SECTORS 4096 #define VSCSI_REQ_LIMIT 24 +/* Maximum size of a IU payload */ +#define SRP_MAX_IU_DATA_LEN (SRP_MAX_IU_LEN - sizeof(union srp_iu)) #define SRP_RSP_SENSE_DATA_LEN 18 #define SRP_REPORT_LUNS_WLUN 0xc10100000000000ULL @@ -66,7 +68,7 @@ typedef union vscsi_crq { typedef struct vscsi_req { vscsi_crq crq; - union viosrp_iu iu; + uint8_t viosrp_iu_buf[SRP_MAX_IU_LEN]; /* SCSI request tracking */ SCSIRequest *sreq; @@ -97,6 +99,11 @@ typedef struct { vscsi_req reqs[VSCSI_REQ_LIMIT]; } VSCSIState; +static union viosrp_iu *req_iu(vscsi_req *req) +{ + return (union viosrp_iu *)req->viosrp_iu_buf; +} + static struct vscsi_req *vscsi_get_req(VSCSIState *s) { vscsi_req *req; @@ -121,7 +128,7 @@ static struct vscsi_req *vscsi_find_req(VSCSIState *s, uint64_t srp_tag) for (i = 0; i < VSCSI_REQ_LIMIT; i++) { req = &s->reqs[i]; - if (req->iu.srp.cmd.tag == srp_tag) { + if (req_iu(req)->srp.cmd.tag == srp_tag) { return req; } } @@ -176,9 +183,11 @@ static int vscsi_send_iu(VSCSIState *s, vscsi_req *req, { long rc, rc1; + assert(length <= SRP_MAX_IU_LEN); + /* First copy the SRP */ rc = spapr_vio_dma_write(&s->vdev, req->crq.s.IU_data_ptr, - &req->iu, length); + &req->viosrp_iu_buf, length); if (rc) { fprintf(stderr, "vscsi_send_iu: DMA write failure !\n"); } @@ -188,7 +197,7 @@ static int vscsi_send_iu(VSCSIState *s, vscsi_req *req, req->crq.s.reserved = 0x00; req->crq.s.timeout = cpu_to_be16(0x0000); req->crq.s.IU_length = cpu_to_be16(length); - req->crq.s.IU_data_ptr = req->iu.srp.rsp.tag; /* right byte order */ + req->crq.s.IU_data_ptr = req_iu(req)->srp.rsp.tag; /* right byte order */ if (rc == 0) { req->crq.s.status = VIOSRP_OK; @@ -224,7 +233,7 @@ static void vscsi_makeup_sense(VSCSIState *s, vscsi_req *req, static int vscsi_send_rsp(VSCSIState *s, vscsi_req *req, uint8_t status, int32_t res_in, int32_t res_out) { - union viosrp_iu *iu = &req->iu; + union viosrp_iu *iu = req_iu(req); uint64_t tag = iu->srp.rsp.tag; int total_len = sizeof(iu->srp.rsp); uint8_t sol_not = iu->srp.cmd.sol_not; @@ -261,10 +270,12 @@ static int vscsi_send_rsp(VSCSIState *s, vscsi_req *req, if (status) { iu->srp.rsp.sol_not = (sol_not & 0x04) >> 2; if (req->senselen) { - req->iu.srp.rsp.flags |= SRP_RSP_FLAG_SNSVALID; - req->iu.srp.rsp.sense_data_len = cpu_to_be32(req->senselen); - memcpy(req->iu.srp.rsp.data, req->sense, req->senselen); - total_len += req->senselen; + int sense_data_len = MIN(req->senselen, SRP_MAX_IU_DATA_LEN); + + iu->srp.rsp.flags |= SRP_RSP_FLAG_SNSVALID; + iu->srp.rsp.sense_data_len = cpu_to_be32(sense_data_len); + memcpy(iu->srp.rsp.data, req->sense, sense_data_len); + total_len += sense_data_len; } } else { iu->srp.rsp.sol_not = (sol_not & 0x02) >> 1; @@ -285,7 +296,7 @@ static int vscsi_fetch_desc(VSCSIState *s, struct vscsi_req *req, unsigned n, unsigned buf_offset, struct srp_direct_buf *ret) { - struct srp_cmd *cmd = &req->iu.srp.cmd; + struct srp_cmd *cmd = &req_iu(req)->srp.cmd; switch (req->dma_fmt) { case SRP_NO_DATA_DESC: { @@ -473,7 +484,7 @@ static int data_out_desc_size(struct srp_cmd *cmd) static int vscsi_preprocess_desc(vscsi_req *req) { - struct srp_cmd *cmd = &req->iu.srp.cmd; + struct srp_cmd *cmd = &req_iu(req)->srp.cmd; req->cdb_offset = cmd->add_cdb_len & ~3; @@ -597,7 +608,7 @@ static const VMStateDescription vmstate_spapr_vscsi_req = { .minimum_version_id = 1, .fields = (VMStateField[]) { VMSTATE_BUFFER(crq.raw, vscsi_req), - VMSTATE_BUFFER(iu.srp.reserved, vscsi_req), + VMSTATE_BUFFER(viosrp_iu_buf, vscsi_req), VMSTATE_UINT32(qtag, vscsi_req), VMSTATE_BOOL(active, vscsi_req), VMSTATE_UINT32(data_len, vscsi_req), @@ -655,7 +666,7 @@ static void *vscsi_load_request(QEMUFile *f, SCSIRequest *sreq) static void vscsi_process_login(VSCSIState *s, vscsi_req *req) { - union viosrp_iu *iu = &req->iu; + union viosrp_iu *iu = req_iu(req); struct srp_login_rsp *rsp = &iu->srp.login_rsp; uint64_t tag = iu->srp.rsp.tag; @@ -671,8 +682,8 @@ static void vscsi_process_login(VSCSIState *s, vscsi_req *req) */ rsp->req_lim_delta = cpu_to_be32(VSCSI_REQ_LIMIT-2); rsp->tag = tag; - rsp->max_it_iu_len = cpu_to_be32(sizeof(union srp_iu)); - rsp->max_ti_iu_len = cpu_to_be32(sizeof(union srp_iu)); + rsp->max_it_iu_len = cpu_to_be32(SRP_MAX_IU_LEN); + rsp->max_ti_iu_len = cpu_to_be32(SRP_MAX_IU_LEN); /* direct and indirect */ rsp->buf_fmt = cpu_to_be16(SRP_BUF_FORMAT_DIRECT | SRP_BUF_FORMAT_INDIRECT); @@ -681,7 +692,7 @@ static void vscsi_process_login(VSCSIState *s, vscsi_req *req) static void vscsi_inquiry_no_target(VSCSIState *s, vscsi_req *req) { - uint8_t *cdb = req->iu.srp.cmd.cdb; + uint8_t *cdb = req_iu(req)->srp.cmd.cdb; uint8_t resp_data[36]; int rc, len, alen; @@ -770,7 +781,7 @@ static void vscsi_report_luns(VSCSIState *s, vscsi_req *req) static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req) { - union srp_iu *srp = &req->iu.srp; + union srp_iu *srp = &req_iu(req)->srp; SCSIDevice *sdev; int n, lun; @@ -821,17 +832,16 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req) static int vscsi_process_tsk_mgmt(VSCSIState *s, vscsi_req *req) { - union viosrp_iu *iu = &req->iu; + union viosrp_iu *iu = req_iu(req); vscsi_req *tmpreq; int i, lun = 0, resp = SRP_TSK_MGMT_COMPLETE; SCSIDevice *d; uint64_t tag = iu->srp.rsp.tag; uint8_t sol_not = iu->srp.cmd.sol_not; - fprintf(stderr, "vscsi_process_tsk_mgmt %02x\n", - iu->srp.tsk_mgmt.tsk_mgmt_func); - - d = vscsi_device_find(&s->bus, be64_to_cpu(req->iu.srp.tsk_mgmt.lun), &lun); + trace_spapr_vscsi_process_tsk_mgmt(iu->srp.tsk_mgmt.tsk_mgmt_func); + d = vscsi_device_find(&s->bus, + be64_to_cpu(req_iu(req)->srp.tsk_mgmt.lun), &lun); if (!d) { resp = SRP_TSK_MGMT_FIELDS_INVALID; } else { @@ -842,7 +852,7 @@ static int vscsi_process_tsk_mgmt(VSCSIState *s, vscsi_req *req) break; } - tmpreq = vscsi_find_req(s, req->iu.srp.tsk_mgmt.task_tag); + tmpreq = vscsi_find_req(s, req_iu(req)->srp.tsk_mgmt.task_tag); if (tmpreq && tmpreq->sreq) { assert(tmpreq->sreq->hba_private); scsi_req_cancel(tmpreq->sreq); @@ -867,7 +877,8 @@ static int vscsi_process_tsk_mgmt(VSCSIState *s, vscsi_req *req) for (i = 0; i < VSCSI_REQ_LIMIT; i++) { tmpreq = &s->reqs[i]; - if (tmpreq->iu.srp.cmd.lun != req->iu.srp.tsk_mgmt.lun) { + if (req_iu(tmpreq)->srp.cmd.lun + != req_iu(req)->srp.tsk_mgmt.lun) { continue; } if (!tmpreq->active || !tmpreq->sreq) { @@ -889,6 +900,7 @@ static int vscsi_process_tsk_mgmt(VSCSIState *s, vscsi_req *req) } /* Compose the response here as */ + QEMU_BUILD_BUG_ON(SRP_MAX_IU_DATA_LEN < 4); memset(iu, 0, sizeof(struct srp_rsp) + 4); iu->srp.rsp.opcode = SRP_RSP; iu->srp.rsp.req_lim_delta = cpu_to_be32(1); @@ -911,7 +923,7 @@ static int vscsi_process_tsk_mgmt(VSCSIState *s, vscsi_req *req) static int vscsi_handle_srp_req(VSCSIState *s, vscsi_req *req) { - union srp_iu *srp = &req->iu.srp; + union srp_iu *srp = &req_iu(req)->srp; int done = 1; uint8_t opcode = srp->rsp.opcode; @@ -948,7 +960,7 @@ static int vscsi_send_adapter_info(VSCSIState *s, vscsi_req *req) struct mad_adapter_info_data info; int rc; - sinfo = &req->iu.mad.adapter_info; + sinfo = &req_iu(req)->mad.adapter_info; #if 0 /* What for ? */ rc = spapr_vio_dma_read(&s->vdev, be64_to_cpu(sinfo->buffer), @@ -984,7 +996,7 @@ static int vscsi_send_capabilities(VSCSIState *s, vscsi_req *req) uint64_t buffer; int rc; - vcap = &req->iu.mad.capabilities; + vcap = &req_iu(req)->mad.capabilities; req_len = len = be16_to_cpu(vcap->common.length); buffer = be64_to_cpu(vcap->buffer); if (len > sizeof(cap)) { @@ -1029,7 +1041,7 @@ static int vscsi_send_capabilities(VSCSIState *s, vscsi_req *req) static int vscsi_handle_mad_req(VSCSIState *s, vscsi_req *req) { - union mad_iu *mad = &req->iu.mad; + union mad_iu *mad = &req_iu(req)->mad; bool request_handled = false; uint64_t retlen = 0; @@ -1088,7 +1100,7 @@ static void vscsi_got_payload(VSCSIState *s, vscsi_crq *crq) * in our 256 bytes IUs. If not we'll have to increase the size * of the structure. */ - if (crq->s.IU_length > sizeof(union viosrp_iu)) { + if (crq->s.IU_length > SRP_MAX_IU_LEN) { fprintf(stderr, "VSCSI: SRP IU too long (%d bytes) !\n", crq->s.IU_length); vscsi_put_req(req); @@ -1096,7 +1108,7 @@ static void vscsi_got_payload(VSCSIState *s, vscsi_crq *crq) } /* XXX Handle failure differently ? */ - if (spapr_vio_dma_read(&s->vdev, crq->s.IU_data_ptr, &req->iu, + if (spapr_vio_dma_read(&s->vdev, crq->s.IU_data_ptr, &req->viosrp_iu_buf, crq->s.IU_length)) { fprintf(stderr, "vscsi_got_payload: DMA read failure !\n"); vscsi_put_req(req); diff --git a/hw/scsi/trace-events b/hw/scsi/trace-events index b0820052f8..9a4a60ca63 100644 --- a/hw/scsi/trace-events +++ b/hw/scsi/trace-events @@ -227,6 +227,7 @@ spapr_vscsi_command_complete_status(uint32_t status) "Command complete err=%"PRI spapr_vscsi_save_request(uint32_t qtag, unsigned desc, unsigned offset) "saving tag=%"PRIu32", current desc#%u, offset=0x%x" spapr_vscsi_load_request(uint32_t qtag, unsigned desc, unsigned offset) "restoring tag=%"PRIu32", current desc#%u, offset=0x%x" spapr_vscsi_process_login(void) "Got login, sending response !" +spapr_vscsi_process_tsk_mgmt(uint8_t func) "tsk_mgmt_func 0x%02x" spapr_vscsi_queue_cmd_no_drive(uint64_t lun) "Command for lun 0x%08" PRIx64 " with no drive" spapr_vscsi_queue_cmd(uint32_t qtag, unsigned cdb, const char *cmd, int lun, int ret) "Queued command tag 0x%"PRIx32" CMD 0x%x=%s LUN %d ret: %d" spapr_vscsi_do_crq(unsigned c0, unsigned c1) "crq: %02x %02x ..." diff --git a/hw/scsi/viosrp.h b/hw/scsi/viosrp.h index d8e365db1e..e5f9768e8f 100644 --- a/hw/scsi/viosrp.h +++ b/hw/scsi/viosrp.h @@ -34,6 +34,8 @@ #ifndef PPC_VIOSRP_H #define PPC_VIOSRP_H +#include "hw/scsi/srp.h" + #define SRP_VERSION "16.a" #define SRP_MAX_IU_LEN 256 #define SRP_MAX_LOC_LEN 32 @@ -47,7 +49,6 @@ union srp_iu { struct srp_tsk_mgmt tsk_mgmt; struct srp_cmd cmd; struct srp_rsp rsp; - uint8_t reserved[SRP_MAX_IU_LEN]; }; enum viosrp_crq_formats { diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c index 91db069212..829797b597 100644 --- a/hw/sd/ssi-sd.c +++ b/hw/sd/ssi-sd.c @@ -255,13 +255,25 @@ static void ssi_sd_realize(SSISlave *d, Error **errp) carddev = qdev_create(BUS(&s->sdbus), TYPE_SD_CARD); if (dinfo) { qdev_prop_set_drive(carddev, "drive", blk_by_legacy_dinfo(dinfo), &err); + if (err) { + goto fail; + } } + object_property_set_bool(OBJECT(carddev), true, "spi", &err); + if (err) { + goto fail; + } + object_property_set_bool(OBJECT(carddev), true, "realized", &err); if (err) { - error_setg(errp, "failed to init SD card: %s", error_get_pretty(err)); - return; + goto fail; } + + return; + +fail: + error_propagate_prepend(errp, err, "failed to init SD card: "); } static void ssi_sd_reset(DeviceState *dev) diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index d33e84f831..6abfcb30f8 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -50,7 +50,6 @@ #include "hw/sparc/sparc64.h" #include "hw/nvram/fw_cfg.h" #include "hw/sysbus.h" -#include "hw/ide.h" #include "hw/ide/pci.h" #include "hw/loader.h" #include "hw/fw-path-provider.h" @@ -563,7 +562,6 @@ static void sun4uv_init(MemoryRegion *address_space_mem, PCIBus *pci_bus, *pci_busA, *pci_busB; PCIDevice *ebus, *pci_dev; SysBusDevice *s; - DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; DeviceState *iommu, *dev; FWCfgState *fw_cfg; NICInfo *nd; @@ -663,12 +661,10 @@ static void sun4uv_init(MemoryRegion *address_space_mem, qemu_macaddr_default_if_unset(&macaddr); } - ide_drive_get(hd, ARRAY_SIZE(hd)); - pci_dev = pci_create(pci_busA, PCI_DEVFN(3, 0), "cmd646-ide"); qdev_prop_set_uint32(&pci_dev->qdev, "secondary", 1); qdev_init_nofail(&pci_dev->qdev); - pci_ide_create_devs(pci_dev, hd); + pci_ide_create_devs(pci_dev); /* Map NVRAM into I/O (ebus) space */ nvram = m48t59_init(NULL, 0, 0, NVRAM_SIZE, 1968, 59); diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h index 08363969c1..30b909ebd2 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -68,40 +68,76 @@ void gdb_signalled(CPUArchState *, int); void gdbserver_fork(CPUState *); #endif /* Get or set a register. Returns the size of the register. */ -typedef int (*gdb_reg_cb)(CPUArchState *env, uint8_t *buf, int reg); +typedef int (*gdb_get_reg_cb)(CPUArchState *env, GByteArray *buf, int reg); +typedef int (*gdb_set_reg_cb)(CPUArchState *env, uint8_t *buf, int reg); void gdb_register_coprocessor(CPUState *cpu, - gdb_reg_cb get_reg, gdb_reg_cb set_reg, + gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg, int num_regs, const char *xml, int g_pos); -/* The GDB remote protocol transfers values in target byte order. This means - * we can use the raw memory access routines to access the value buffer. - * Conveniently, these also handle the case where the buffer is mis-aligned. +/* + * The GDB remote protocol transfers values in target byte order. As + * the gdbstub may be batching up several register values we always + * append to the array. */ -static inline int gdb_get_reg8(uint8_t *mem_buf, uint8_t val) +static inline int gdb_get_reg8(GByteArray *buf, uint8_t val) { - stb_p(mem_buf, val); + g_byte_array_append(buf, &val, 1); return 1; } -static inline int gdb_get_reg16(uint8_t *mem_buf, uint16_t val) +static inline int gdb_get_reg16(GByteArray *buf, uint16_t val) { - stw_p(mem_buf, val); + uint16_t to_word = tswap16(val); + g_byte_array_append(buf, (uint8_t *) &to_word, 2); return 2; } -static inline int gdb_get_reg32(uint8_t *mem_buf, uint32_t val) +static inline int gdb_get_reg32(GByteArray *buf, uint32_t val) { - stl_p(mem_buf, val); + uint32_t to_long = tswap32(val); + g_byte_array_append(buf, (uint8_t *) &to_long, 4); return 4; } -static inline int gdb_get_reg64(uint8_t *mem_buf, uint64_t val) +static inline int gdb_get_reg64(GByteArray *buf, uint64_t val) { - stq_p(mem_buf, val); + uint64_t to_quad = tswap64(val); + g_byte_array_append(buf, (uint8_t *) &to_quad, 8); return 8; } +static inline int gdb_get_reg128(GByteArray *buf, uint64_t val_hi, + uint64_t val_lo) +{ + uint64_t to_quad; +#ifdef TARGET_WORDS_BIGENDIAN + to_quad = tswap64(val_hi); + g_byte_array_append(buf, (uint8_t *) &to_quad, 8); + to_quad = tswap64(val_lo); + g_byte_array_append(buf, (uint8_t *) &to_quad, 8); +#else + to_quad = tswap64(val_lo); + g_byte_array_append(buf, (uint8_t *) &to_quad, 8); + to_quad = tswap64(val_hi); + g_byte_array_append(buf, (uint8_t *) &to_quad, 8); +#endif + return 16; +} + +/** + * gdb_get_reg_ptr: get pointer to start of last element + * @len: length of element + * + * This is a helper function to extract the pointer to the last + * element for additional processing. Some front-ends do additional + * dynamic swapping of the elements based on CPU state. + */ +static inline uint8_t * gdb_get_reg_ptr(GByteArray *buf, int len) +{ + return buf->data + buf->len - len; +} + #if TARGET_LONG_BITS == 64 #define gdb_get_regl(buf, val) gdb_get_reg64(buf, val) #define ldtul_p(addr) ldq_p(addr) diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 88ee543722..5bf94d28cf 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -193,7 +193,7 @@ typedef struct CPUClass { hwaddr (*get_phys_page_attrs_debug)(CPUState *cpu, vaddr addr, MemTxAttrs *attrs); int (*asidx_from_attrs)(CPUState *cpu, MemTxAttrs attrs); - int (*gdb_read_register)(CPUState *cpu, uint8_t *buf, int reg); + int (*gdb_read_register)(CPUState *cpu, GByteArray *buf, int reg); int (*gdb_write_register)(CPUState *cpu, uint8_t *buf, int reg); bool (*debug_check_watchpoint)(CPUState *cpu, CPUWatchpoint *wp); void (*debug_excp_handler)(CPUState *cpu); diff --git a/include/hw/ide.h b/include/hw/ide.h index 28d8a06439..c5ce5da4f4 100644 --- a/include/hw/ide.h +++ b/include/hw/ide.h @@ -2,23 +2,14 @@ #define HW_IDE_H #include "hw/isa/isa.h" -#include "hw/pci/pci.h" #include "exec/memory.h" -#define MAX_IDE_DEVS 2 - /* ide-isa.c */ ISADevice *isa_ide_init(ISABus *bus, int iobase, int iobase2, int isairq, DriveInfo *hd0, DriveInfo *hd1); /* ide-pci.c */ -void pci_cmd646_ide_init(PCIBus *bus, DriveInfo **hd_table, - int secondary_ide_enabled); -PCIDevice *pci_piix3_xen_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); -PCIDevice *pci_piix3_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); -PCIDevice *pci_piix4_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); int pci_piix3_xen_ide_unplug(DeviceState *dev, bool aux); -void via_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); /* ide-mmio.c */ void mmio_ide_init_drives(DeviceState *dev, DriveInfo *hd0, DriveInfo *hd1); diff --git a/include/hw/ide/internal.h b/include/hw/ide/internal.h index 1bc1fc73e5..55da35d768 100644 --- a/include/hw/ide/internal.h +++ b/include/hw/ide/internal.h @@ -27,6 +27,8 @@ typedef struct IDEDMAOps IDEDMAOps; #define TYPE_IDE_BUS "IDE" #define IDE_BUS(obj) OBJECT_CHECK(IDEBus, (obj), TYPE_IDE_BUS) +#define MAX_IDE_DEVS 2 + /* Bits of HD_STATUS */ #define ERR_STAT 0x01 #define INDEX_STAT 0x02 diff --git a/include/hw/ide/pci.h b/include/hw/ide/pci.h index a9f2c33e68..dd504e5a0b 100644 --- a/include/hw/ide/pci.h +++ b/include/hw/ide/pci.h @@ -2,6 +2,7 @@ #define HW_IDE_PCI_H #include "hw/ide/internal.h" +#include "hw/pci/pci.h" #define BM_STATUS_DMAING 0x01 #define BM_STATUS_ERROR 0x02 @@ -62,7 +63,7 @@ static inline IDEState *bmdma_active_if(BMDMAState *bmdma) void bmdma_init(IDEBus *bus, BMDMAState *bm, PCIIDEState *d); void bmdma_cmd_writeb(BMDMAState *bm, uint32_t val); extern MemoryRegionOps bmdma_addr_ioport_ops; -void pci_ide_create_devs(PCIDevice *dev, DriveInfo **hd_table); +void pci_ide_create_devs(PCIDevice *dev); extern const VMStateDescription vmstate_ide_pci; extern const MemoryRegionOps pci_ide_cmd_le_ops; diff --git a/include/hw/misc/macio/macio.h b/include/hw/misc/macio/macio.h index 070a694eb5..87335a991c 100644 --- a/include/hw/misc/macio/macio.h +++ b/include/hw/misc/macio/macio.h @@ -27,6 +27,7 @@ #define MACIO_H #include "hw/char/escc.h" +#include "hw/pci/pci.h" #include "hw/ide/internal.h" #include "hw/intc/heathrow_pic.h" #include "hw/misc/macio/cuda.h" diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 09110961a5..42d64a0368 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -79,10 +79,10 @@ typedef enum { #define SPAPR_CAP_LARGE_DECREMENTER 0x08 /* Count Cache Flush Assist HW Instruction */ #define SPAPR_CAP_CCF_ASSIST 0x09 -/* FWNMI machine check handling */ -#define SPAPR_CAP_FWNMI_MCE 0x0A +/* Implements PAPR FWNMI option */ +#define SPAPR_CAP_FWNMI 0x0A /* Num Caps */ -#define SPAPR_CAP_NUM (SPAPR_CAP_FWNMI_MCE + 1) +#define SPAPR_CAP_NUM (SPAPR_CAP_FWNMI + 1) /* * Capability Values @@ -126,6 +126,7 @@ struct SpaprMachineClass { bool pre_4_1_migration; /* don't migrate hpt-max-page-size */ bool linux_pci_probe; bool smp_threads_vsmt; /* set VSMT to smp_threads by default */ + hwaddr rma_limit; /* clamp the RMA to this size */ void (*phb_placement)(SpaprMachineState *spapr, uint32_t index, uint64_t *buid, hwaddr *pio, @@ -156,7 +157,6 @@ struct SpaprMachineState { SpaprPendingHpt *pending_hpt; /* in-progress resize */ hwaddr rma_size; - int vrma_adjust; uint32_t fdt_size; uint32_t fdt_initial_size; void *fdt_blob; @@ -192,14 +192,22 @@ struct SpaprMachineState { * occurs during the unplug process. */ QTAILQ_HEAD(, SpaprDimmState) pending_dimm_unplugs; - /* State related to "ibm,nmi-register" and "ibm,nmi-interlock" calls */ - target_ulong guest_machine_check_addr; - /* - * mc_status is set to -1 if mc is not in progress, else is set to the CPU - * handling the mc. + /* State related to FWNMI option */ + + /* System Reset and Machine Check Notification Routine addresses + * registered by "ibm,nmi-register" RTAS call. + */ + target_ulong fwnmi_system_reset_addr; + target_ulong fwnmi_machine_check_addr; + + /* Machine Check FWNMI synchronization, fwnmi_machine_check_interlock is + * set to -1 if a FWNMI machine check is not in progress, else is set to + * the CPU that was delivered the machine check, and is set back to -1 + * when that CPU makes an "ibm,nmi-interlock" RTAS call. The cond is used + * to synchronize other CPUs. */ - int mc_status; - QemuCond mc_delivery_cond; + int fwnmi_machine_check_interlock; + QemuCond fwnmi_machine_check_interlock_cond; /*< public >*/ char *kvm_type; @@ -736,6 +744,7 @@ void spapr_load_rtas(SpaprMachineState *spapr, void *fdt, hwaddr addr); #define SPAPR_IS_PCI_LIOBN(liobn) (!!((liobn) & 0x80000000)) #define SPAPR_PCI_DMA_WINDOW_NUM(liobn) ((liobn) & 0xff) +#define RTAS_SIZE 2048 #define RTAS_ERROR_LOG_MAX 2048 /* Offset from rtas-base where error log is placed */ @@ -795,7 +804,7 @@ void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space); void spapr_events_init(SpaprMachineState *sm); void spapr_dt_events(SpaprMachineState *sm, void *fdt); void close_htab_fd(SpaprMachineState *spapr); -void spapr_setup_hpt_and_vrma(SpaprMachineState *spapr); +void spapr_setup_hpt(SpaprMachineState *spapr); void spapr_free_hpt(SpaprMachineState *spapr); SpaprTceTable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn); void spapr_tce_table_enable(SpaprTceTable *tcet, @@ -824,6 +833,7 @@ int spapr_hpt_shift_for_ramsize(uint64_t ramsize); void spapr_reallocate_hpt(SpaprMachineState *spapr, int shift, Error **errp); void spapr_clear_pending_events(SpaprMachineState *spapr); +void spapr_clear_pending_hotplug_events(SpaprMachineState *spapr); int spapr_max_server_number(SpaprMachineState *spapr); void spapr_store_hpte(PowerPCCPU *cpu, hwaddr ptex, uint64_t pte0, uint64_t pte1); diff --git a/include/hw/ppc/spapr_cpu_core.h b/include/hw/ppc/spapr_cpu_core.h index 1c4cc6559c..7aed8f555b 100644 --- a/include/hw/ppc/spapr_cpu_core.h +++ b/include/hw/ppc/spapr_cpu_core.h @@ -40,7 +40,9 @@ typedef struct SpaprCpuCoreClass { } SpaprCpuCoreClass; const char *spapr_get_cpu_core_type(const char *cpu_type); -void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, target_ulong r3); +void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, + target_ulong r1, target_ulong r3, + target_ulong r4); typedef struct SpaprCpuState { uint64_t vpa_addr; diff --git a/include/hw/ppc/spapr_ovec.h b/include/hw/ppc/spapr_ovec.h index 2bed517a2b..d4dee9e06a 100644 --- a/include/hw/ppc/spapr_ovec.h +++ b/include/hw/ppc/spapr_ovec.h @@ -72,8 +72,8 @@ void spapr_ovec_set(SpaprOptionVector *ov, long bitnr); void spapr_ovec_clear(SpaprOptionVector *ov, long bitnr); bool spapr_ovec_test(SpaprOptionVector *ov, long bitnr); SpaprOptionVector *spapr_ovec_parse_vector(target_ulong table_addr, int vector); -int spapr_ovec_populate_dt(void *fdt, int fdt_offset, - SpaprOptionVector *ov, const char *name); +int spapr_dt_ovec(void *fdt, int fdt_offset, + SpaprOptionVector *ov, const char *name); /* migration */ extern const VMStateDescription vmstate_spapr_ovec; diff --git a/include/hw/southbridge/piix.h b/include/hw/southbridge/piix.h index 152628c6d9..02bd741209 100644 --- a/include/hw/southbridge/piix.h +++ b/include/hw/southbridge/piix.h @@ -68,7 +68,6 @@ extern PCIDevice *piix4_dev; PIIX3State *piix3_create(PCIBus *pci_bus, ISABus **isa_bus); -DeviceState *piix4_create(PCIBus *pci_bus, ISABus **isa_bus, - I2CBus **smbus, size_t ide_buses); +DeviceState *piix4_create(PCIBus *pci_bus, ISABus **isa_bus, I2CBus **smbus); #endif diff --git a/include/net/net.h b/include/net/net.h index e175ba9677..094e966af9 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -98,6 +98,7 @@ struct NetClientState { unsigned rxfilter_notify_enabled:1; int vring_enable; int vnet_hdr_len; + bool is_netdev; QTAILQ_HEAD(, NetFilterState) filters; }; @@ -203,7 +204,6 @@ void net_cleanup(void); void hmp_host_net_add(Monitor *mon, const QDict *qdict); void hmp_host_net_remove(Monitor *mon, const QDict *qdict); void netdev_add(QemuOpts *opts, Error **errp); -void qmp_netdev_add(QDict *qdict, QObject **ret, Error **errp); int net_hub_id_for_client(NetClientState *nc, int *id); NetClientState *net_hub_port_find(int hub_id); diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h index 9aa426a398..5a9cf82472 100644 --- a/include/qapi/qmp/dispatch.h +++ b/include/qapi/qmp/dispatch.h @@ -39,7 +39,8 @@ typedef QTAILQ_HEAD(QmpCommandList, QmpCommand) QmpCommandList; void qmp_register_command(QmpCommandList *cmds, const char *name, QmpCommandFunc *fn, QmpCommandOptions options); -QmpCommand *qmp_find_command(QmpCommandList *cmds, const char *name); +const QmpCommand *qmp_find_command(const QmpCommandList *cmds, + const char *name); void qmp_disable_command(QmpCommandList *cmds, const char *name); void qmp_enable_command(QmpCommandList *cmds, const char *name); @@ -47,13 +48,13 @@ bool qmp_command_is_enabled(const QmpCommand *cmd); const char *qmp_command_name(const QmpCommand *cmd); bool qmp_has_success_response(const QmpCommand *cmd); QDict *qmp_error_response(Error *err); -QDict *qmp_dispatch(QmpCommandList *cmds, QObject *request, +QDict *qmp_dispatch(const QmpCommandList *cmds, QObject *request, bool allow_oob); bool qmp_is_oob(const QDict *dict); -typedef void (*qmp_cmd_callback_fn)(QmpCommand *cmd, void *opaque); +typedef void (*qmp_cmd_callback_fn)(const QmpCommand *cmd, void *opaque); -void qmp_for_each_command(QmpCommandList *cmds, qmp_cmd_callback_fn fn, +void qmp_for_each_command(const QmpCommandList *cmds, qmp_cmd_callback_fn fn, void *opaque); #endif diff --git a/monitor/misc.c b/monitor/misc.c index c3bc34c099..6c45fa490f 100644 --- a/monitor/misc.c +++ b/monitor/misc.c @@ -247,8 +247,6 @@ static void monitor_init_qmp_commands(void) qmp_query_qmp_schema, QCO_ALLOW_PRECONFIG); qmp_register_command(&qmp_commands, "device_add", qmp_device_add, QCO_NO_OPTIONS); - qmp_register_command(&qmp_commands, "netdev_add", qmp_netdev_add, - QCO_NO_OPTIONS); qmp_register_command(&qmp_commands, "object-add", qmp_object_add, QCO_NO_OPTIONS); @@ -2037,13 +2035,11 @@ void netdev_del_completion(ReadLineState *rs, int nb_args, const char *str) count = qemu_find_net_clients_except(NULL, ncs, NET_CLIENT_DRIVER_NIC, MAX_QUEUE_NUM); for (i = 0; i < MIN(count, MAX_QUEUE_NUM); i++) { - QemuOpts *opts; const char *name = ncs[i]->name; if (strncmp(str, name, len)) { continue; } - opts = qemu_opts_find(qemu_find_opts_err("netdev", NULL), name); - if (opts) { + if (ncs[i]->is_netdev) { readline_add_completion(rs, name); } } diff --git a/monitor/monitor-internal.h b/monitor/monitor-internal.h index 3e6baba88f..8f60ccc70a 100644 --- a/monitor/monitor-internal.h +++ b/monitor/monitor-internal.h @@ -133,7 +133,7 @@ typedef struct { * qmp_capabilities succeeds, we go into command mode, and * @command becomes &qmp_commands. */ - QmpCommandList *commands; + const QmpCommandList *commands; bool capab_offered[QMP_CAPABILITY__MAX]; /* capabilities offered */ bool capab[QMP_CAPABILITY__MAX]; /* offered and accepted */ /* diff --git a/monitor/qmp-cmds-control.c b/monitor/qmp-cmds-control.c index 5cd9bb817c..8f04cfa6e6 100644 --- a/monitor/qmp-cmds-control.c +++ b/monitor/qmp-cmds-control.c @@ -101,7 +101,7 @@ VersionInfo *qmp_query_version(Error **errp) return info; } -static void query_commands_cb(QmpCommand *cmd, void *opaque) +static void query_commands_cb(const QmpCommand *cmd, void *opaque) { CommandInfoList *info, **list = opaque; @@ -1060,6 +1060,15 @@ static int net_client_init1(const void *object, bool is_netdev, Error **errp) } return -1; } + + if (is_netdev) { + NetClientState *nc; + + nc = qemu_find_netdev(netdev->id); + assert(nc); + nc->is_netdev = true; + } + return 0; } @@ -1170,36 +1179,14 @@ void netdev_add(QemuOpts *opts, Error **errp) net_client_init(opts, true, errp); } -void qmp_netdev_add(QDict *qdict, QObject **ret, Error **errp) +void qmp_netdev_add(Netdev *netdev, Error **errp) { - Error *local_err = NULL; - QemuOptsList *opts_list; - QemuOpts *opts; - - opts_list = qemu_find_opts_err("netdev", &local_err); - if (local_err) { - goto out; - } - - opts = qemu_opts_from_qdict(opts_list, qdict, &local_err); - if (local_err) { - goto out; - } - - netdev_add(opts, &local_err); - if (local_err) { - qemu_opts_del(opts); - goto out; - } - -out: - error_propagate(errp, local_err); + net_client_init1(netdev, true, errp); } void qmp_netdev_del(const char *id, Error **errp) { NetClientState *nc; - QemuOpts *opts; nc = qemu_find_netdev(id); if (!nc) { @@ -1208,14 +1195,12 @@ void qmp_netdev_del(const char *id, Error **errp) return; } - opts = qemu_opts_find(qemu_find_opts_err("netdev", NULL), id); - if (!opts) { + if (!nc->is_netdev) { error_setg(errp, "Device '%s' is not a netdev", id); return; } qemu_del_net_client(nc); - qemu_opts_del(opts); } static void netfilter_print_info(Monitor *mon, NetFilterState *nf) diff --git a/pc-bios/README b/pc-bios/README index d6d33d237f..f54c2743d0 100644 --- a/pc-bios/README +++ b/pc-bios/README @@ -14,7 +14,7 @@ - SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware implementation for certain IBM POWER hardware. The sources are at https://github.com/aik/SLOF, and the image currently in qemu is - built from git tag qemu-slof-20191217. + built from git tag qemu-slof-20200317. - sgabios (the Serial Graphics Adapter option ROM) provides a means for legacy x86 software to communicate with an attached serial console as diff --git a/pc-bios/slof.bin b/pc-bios/slof.bin Binary files differindex 78d8b26cbb..40499a1451 100644 --- a/pc-bios/slof.bin +++ b/pc-bios/slof.bin diff --git a/python/qemu/machine.py b/python/qemu/machine.py index 183d8f3d38..f53abfa492 100644 --- a/python/qemu/machine.py +++ b/python/qemu/machine.py @@ -270,7 +270,8 @@ class QEMUMachine(object): self._vm_monitor = os.path.join(self._sock_dir, self._name + "-monitor.sock") self._remove_files.append(self._vm_monitor) - self._qmp = qmp.QEMUMonitorProtocol(self._vm_monitor, server=True) + self._qmp = qmp.QEMUMonitorProtocol(self._vm_monitor, server=True, + nickname=self._name) def _post_launch(self): if self._qmp: diff --git a/python/qemu/qmp.py b/python/qemu/qmp.py index f40586eedd..d6c9b2f4b1 100644 --- a/python/qemu/qmp.py +++ b/python/qemu/qmp.py @@ -46,7 +46,7 @@ class QEMUMonitorProtocol: #: Logger object for debugging messages logger = logging.getLogger('QMP') - def __init__(self, address, server=False): + def __init__(self, address, server=False, nickname=None): """ Create a QEMUMonitorProtocol class. @@ -62,6 +62,9 @@ class QEMUMonitorProtocol: self.__address = address self.__sock = self.__get_sock() self.__sockfile = None + self._nickname = nickname + if self._nickname: + self.logger = logging.getLogger('QMP').getChild(self._nickname) if server: self.__sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.__sock.bind(self.__address) diff --git a/qapi/block-core.json b/qapi/block-core.json index 91586fb1fb..943df1926a 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -297,7 +297,7 @@ # # @encrypted: true if the backing device is encrypted # -# @encryption_key_missing: Deprecated; always false +# @encryption_key_missing: always false # # @detect_zeroes: detect and optimize zero writes (Since 2.1) # @@ -363,13 +363,19 @@ # @dirty-bitmaps: dirty bitmaps information (only present if node # has one or more dirty bitmaps) (Since 4.2) # +# Features: +# @deprecated: Member @encryption_key_missing is deprecated. It is +# always false. +# # Since: 0.14.0 # ## { 'struct': 'BlockDeviceInfo', 'data': { 'file': 'str', '*node-name': 'str', 'ro': 'bool', 'drv': 'str', '*backing_file': 'str', 'backing_file_depth': 'int', - 'encrypted': 'bool', 'encryption_key_missing': 'bool', + 'encrypted': 'bool', + 'encryption_key_missing': { 'type': 'bool', + 'features': [ 'deprecated' ] }, 'detect_zeroes': 'BlockdevDetectZeroesOptions', 'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int', 'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int', @@ -475,7 +481,7 @@ # # @granularity: granularity of the dirty bitmap in bytes (since 1.4) # -# @status: Deprecated in favor of @recording and @locked. (since 2.4) +# @status: current status of the dirty bitmap (since 2.4) # # @recording: true if the bitmap is recording new writes from the guest. # Replaces `active` and `disabled` statuses. (since 4.0) @@ -492,11 +498,17 @@ # @busy to be false. This bitmap cannot be used. To remove # it, use @block-dirty-bitmap-remove. (Since 4.0) # +# Features: +# @deprecated: Member @status is deprecated. Use @recording and +# @locked instead. +# # Since: 1.3 ## { 'struct': 'BlockDirtyInfo', 'data': {'*name': 'str', 'count': 'int', 'granularity': 'uint32', - 'recording': 'bool', 'busy': 'bool', 'status': 'DirtyBitmapStatus', + 'recording': 'bool', 'busy': 'bool', + 'status': { 'type': 'DirtyBitmapStatus', + 'features': [ 'deprecated' ] }, 'persistent': 'bool', '*inconsistent': 'bool' } } ## @@ -587,7 +599,6 @@ # # @dirty-bitmaps: dirty bitmaps information (only present if the # driver has one or more dirty bitmaps) (Since 2.0) -# Deprecated in 4.2; see BlockDeviceInfo instead. # # @io-status: @BlockDeviceIoStatus. Only present if the device # supports it and the VM is configured to stop on errors @@ -597,13 +608,18 @@ # @inserted: @BlockDeviceInfo describing the device if media is # present # +# Features: +# @deprecated: Member @dirty-bitmaps is deprecated. Use @inserted +# member @dirty-bitmaps instead. +# # Since: 0.14.0 ## { 'struct': 'BlockInfo', 'data': {'device': 'str', '*qdev': 'str', 'type': 'str', 'removable': 'bool', 'locked': 'bool', '*inserted': 'BlockDeviceInfo', '*tray_open': 'bool', '*io-status': 'BlockDeviceIoStatus', - '*dirty-bitmaps': ['BlockDirtyInfo'] } } + '*dirty-bitmaps': { 'type': ['BlockDirtyInfo'], + 'features': [ 'deprecated' ] } } } ## # @BlockMeasureInfo: @@ -1551,7 +1567,7 @@ # @base: Same as @base-node, except that it is a file name rather than a node # name. This must be the exact filename string that was used to open the # node; other strings, even if addressing the same file, are not -# accepted (deprecated, use @base-node instead) +# accepted # # @top-node: The node name of the backing image within the image chain # which contains the topmost data to be committed down. If @@ -1560,7 +1576,7 @@ # @top: Same as @top-node, except that it is a file name rather than a node # name. This must be the exact filename string that was used to open the # node; other strings, even if addressing the same file, are not -# accepted (deprecated, use @base-node instead) +# accepted # # @backing-file: The backing file string to write into the overlay # image of 'top'. If 'top' is the active layer, @@ -1614,6 +1630,10 @@ # list without user intervention. # Defaults to true. (Since 3.1) # +# Features: +# @deprecated: Members @base and @top are deprecated. Use @base-node +# and @top-node instead. +# # Returns: - Nothing on success # - If @device does not exist, DeviceNotFound # - Any other error returns a GenericError. @@ -1630,7 +1650,9 @@ ## { 'command': 'block-commit', 'data': { '*job-id': 'str', 'device': 'str', '*base-node': 'str', - '*base': 'str', '*top-node': 'str', '*top': 'str', + '*base': { 'type': 'str', 'features': [ 'deprecated' ] }, + '*top-node': 'str', + '*top': { 'type': 'str', 'features': [ 'deprecated' ] }, '*backing-file': 'str', '*speed': 'int', '*on-error': 'BlockdevOnError', '*filter-node-name': 'str', @@ -2296,7 +2318,7 @@ # # A set of parameters describing block throttling. # -# @device: Block device name (deprecated, use @id instead) +# @device: Block device name # # @id: The name or QOM path of the guest device (since: 2.8) # @@ -2364,10 +2386,14 @@ # # @group: throttle group name (Since 2.4) # +# Features: +# @deprecated: Member @device is deprecated. Use @id instead. +# # Since: 1.1 ## { 'struct': 'BlockIOThrottle', - 'data': { '*device': 'str', '*id': 'str', 'bps': 'int', 'bps_rd': 'int', + 'data': { '*device': { 'type': 'str', 'features': [ 'deprecated' ] }, + '*id': 'str', 'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int', 'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int', '*bps_max': 'int', '*bps_rd_max': 'int', '*bps_wr_max': 'int', '*iops_max': 'int', diff --git a/qapi/block.json b/qapi/block.json index 97bf52b7c7..2ddbfa8306 100644 --- a/qapi/block.json +++ b/qapi/block.json @@ -90,15 +90,18 @@ ## # @eject: # -# Ejects a device from a removable drive. +# Ejects the medium from a removable drive. # -# @device: Block device name (deprecated, use @id instead) +# @device: Block device name # # @id: The name or QOM path of the guest device (since: 2.8) # # @force: If true, eject regardless of whether the drive is locked. # If not specified, the default value is false. # +# Features: +# @deprecated: Member @device is deprecated. Use @id instead. +# # Returns: - Nothing on success # - If @device is not a valid block device, DeviceNotFound # Notes: Ejecting a device with no media results in success @@ -111,7 +114,7 @@ # <- { "return": {} } ## { 'command': 'eject', - 'data': { '*device': 'str', + 'data': { '*device': { 'type': 'str', 'features': [ 'deprecated' ] }, '*id': 'str', '*force': 'bool' } } @@ -134,7 +137,7 @@ # to it # - if the guest device does not have an actual tray # -# @device: Block device name (deprecated, use @id instead) +# @device: Block device name # # @id: The name or QOM path of the guest device (since: 2.8) # @@ -143,6 +146,9 @@ # immediately); if true, the tray will be opened regardless of whether # it is locked # +# Features: +# @deprecated: Member @device is deprecated. Use @id instead. +# # Since: 2.5 # # Example: @@ -161,7 +167,7 @@ # ## { 'command': 'blockdev-open-tray', - 'data': { '*device': 'str', + 'data': { '*device': { 'type': 'str', 'features': [ 'deprecated' ] }, '*id': 'str', '*force': 'bool' } } @@ -174,10 +180,13 @@ # # If the tray was already closed before, this will be a no-op. # -# @device: Block device name (deprecated, use @id instead) +# @device: Block device name # # @id: The name or QOM path of the guest device (since: 2.8) # +# Features: +# @deprecated: Member @device is deprecated. Use @id instead. +# # Since: 2.5 # # Example: @@ -196,7 +205,7 @@ # ## { 'command': 'blockdev-close-tray', - 'data': { '*device': 'str', + 'data': { '*device': { 'type': 'str', 'features': [ 'deprecated' ] }, '*id': 'str' } } ## @@ -303,7 +312,7 @@ # combines blockdev-open-tray, blockdev-remove-medium, blockdev-insert-medium # and blockdev-close-tray). # -# @device: Block device name (deprecated, use @id instead) +# @device: Block device name # # @id: The name or QOM path of the guest device # (since: 2.8) @@ -316,6 +325,9 @@ # @read-only-mode: change the read-only mode of the device; defaults # to 'retain' # +# Features: +# @deprecated: Member @device is deprecated. Use @id instead. +# # Since: 2.5 # # Examples: @@ -350,7 +362,7 @@ # ## { 'command': 'blockdev-change-medium', - 'data': { '*device': 'str', + 'data': { '*device': { 'type': 'str', 'features': [ 'deprecated' ] }, '*id': 'str', 'filename': 'str', '*format': 'str', diff --git a/qapi/char.json b/qapi/char.json index 6907b2bfdb..daceb20f84 100644 --- a/qapi/char.json +++ b/qapi/char.json @@ -258,6 +258,7 @@ # @server: create server socket (default: true) # @wait: wait for incoming connection on server # sockets (default: false). +# Silently ignored with server: false. This use is deprecated. # @nodelay: set TCP_NODELAY socket option (default: false) # @telnet: enable telnet protocol on server # sockets (default: false) diff --git a/qapi/control.json b/qapi/control.json index 85b12fe0fb..6b816bb61f 100644 --- a/qapi/control.json +++ b/qapi/control.json @@ -174,13 +174,15 @@ # # Return information on QMP events. # +# Features: +# @deprecated: This command is deprecated, because its output doesn't +# reflect compile-time configuration. Use 'query-qmp-schema' +# instead. +# # Returns: A list of @EventInfo. # # Since: 1.2.0 # -# Note: This command is deprecated, because its output doesn't reflect -# compile-time configuration. Use query-qmp-schema instead. -# # Example: # # -> { "execute": "query-events" } @@ -198,7 +200,8 @@ # Note: This example has been shortened as the real response is too long. # ## -{ 'command': 'query-events', 'returns': ['EventInfo'] } +{ 'command': 'query-events', 'returns': ['EventInfo'], + 'features': [ 'deprecated' ] } ## # @quit: diff --git a/qapi/introspect.json b/qapi/introspect.json index 8756e7920e..b1aabd4cfd 100644 --- a/qapi/introspect.json +++ b/qapi/introspect.json @@ -89,12 +89,18 @@ # # @meta-type: the entity's meta type, inherited from @base. # +# @features: names of features associated with the entity, in no +# particular order. +# (since 4.1 for object types, 4.2 for commands, 5.0 for +# the rest) +# # Additional members depend on the value of @meta-type. # # Since: 2.5 ## { 'union': 'SchemaInfo', - 'base': { 'name': 'str', 'meta-type': 'SchemaMetaType' }, + 'base': { 'name': 'str', 'meta-type': 'SchemaMetaType', + '*features': [ 'str' ] }, 'discriminator': 'meta-type', 'data': { 'builtin': 'SchemaInfoBuiltin', @@ -174,9 +180,6 @@ # and may even differ from the order of the values of the # enum type of the @tag. # -# @features: names of features associated with the type, in no particular -# order. (since: 4.1) -# # Values of this type are JSON object on the wire. # # Since: 2.5 @@ -184,8 +187,7 @@ { 'struct': 'SchemaInfoObject', 'data': { 'members': [ 'SchemaInfoObjectMember' ], '*tag': 'str', - '*variants': [ 'SchemaInfoObjectVariant' ], - '*features': [ 'str' ] } } + '*variants': [ 'SchemaInfoObjectVariant' ] } } ## # @SchemaInfoObjectMember: @@ -204,11 +206,15 @@ # Future extension: if present and non-null, the parameter # is optional, and defaults to this value. # +# @features: names of features associated with the member, in no +# particular order. (since 5.0) +# # Since: 2.5 ## { 'struct': 'SchemaInfoObjectMember', - 'data': { 'name': 'str', 'type': 'str', '*default': 'any' } } + 'data': { 'name': 'str', 'type': 'str', '*default': 'any', # @default's type must be null or match @type + '*features': [ 'str' ] } } ## # @SchemaInfoObjectVariant: @@ -266,17 +272,13 @@ # @allow-oob: whether the command allows out-of-band execution, # defaults to false (Since: 2.12) # -# @features: names of features associated with the command, in no particular -# order. (since 4.2) -# # TODO: @success-response (currently irrelevant, because it's QGA, not QMP) # # Since: 2.5 ## { 'struct': 'SchemaInfoCommand', 'data': { 'arg-type': 'str', 'ret-type': 'str', - '*allow-oob': 'bool', - '*features': [ 'str' ] } } + '*allow-oob': 'bool' } } ## # @SchemaInfoEvent: diff --git a/qapi/machine.json b/qapi/machine.json index 6c11e3cf3a..0da3f3adc4 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -184,8 +184,11 @@ # This command causes vCPU threads to exit to userspace, which causes # a small interruption to guest CPU execution. This will have a negative # impact on realtime guests and other latency sensitive guest workloads. -# It is recommended to use @query-cpus-fast instead of this command to -# avoid the vCPU interruption. +# +# Features: +# @deprecated: This command is deprecated, because it interferes with +# the guest. Use 'query-cpus-fast' instead to avoid the vCPU +# interruption. # # Returns: a list of @CpuInfo for each virtual CPU # @@ -216,12 +219,9 @@ # ] # } # -# Notes: This interface is deprecated (since 2.12.0), and it is strongly -# recommended that you avoid using it. Use @query-cpus-fast to -# obtain information about virtual CPUs. -# ## -{ 'command': 'query-cpus', 'returns': ['CpuInfo'] } +{ 'command': 'query-cpus', 'returns': ['CpuInfo'], + 'features': [ 'deprecated' ] } ## # @CpuInfoFast: @@ -237,12 +237,14 @@ # @props: properties describing to which node/socket/core/thread # virtual CPU belongs to, provided if supported by board # -# @arch: base architecture of the cpu; deprecated since 3.0.0 in favor -# of @target +# @arch: base architecture of the cpu # # @target: the QEMU system emulation target, which determines which # additional fields will be listed (since 3.0) # +# Features: +# @deprecated: Member @arch is deprecated. Use @target instead. +# # Since: 2.12 # ## @@ -251,7 +253,8 @@ 'qom-path' : 'str', 'thread-id' : 'int', '*props' : 'CpuInstanceProperties', - 'arch' : 'CpuInfoArch', + 'arch' : { 'type': 'CpuInfoArch', + 'features': [ 'deprecated' ] }, 'target' : 'SysEmuTarget' }, 'discriminator' : 'target', 'data' : { 's390x' : 'CpuInfoS390' } } @@ -307,21 +310,22 @@ # # @id: ID of CPU to be created, valid values [0..max_cpus) # +# Features: +# @deprecated: This command is deprecated. Use `device_add` instead. +# See the `query-hotpluggable-cpus` command for details. +# # Returns: Nothing on success # # Since: 1.5 # -# Note: This command is deprecated. The `device_add` command should be -# used instead. See the `query-hotpluggable-cpus` command for -# details. -# # Example: # # -> { "execute": "cpu-add", "arguments": { "id": 2 } } # <- { "return": {} } # ## -{ 'command': 'cpu-add', 'data': {'id': 'int'} } +{ 'command': 'cpu-add', 'data': {'id': 'int'}, + 'features': [ 'deprecated' ] } ## # @MachineInfo: diff --git a/qapi/migration.json b/qapi/migration.json index 0d1c0712ca..eca2981d0a 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -1210,9 +1210,11 @@ # # @value: maximum downtime in seconds # -# Returns: nothing on success +# Features: +# @deprecated: This command is deprecated. Use +# 'migrate-set-parameters' instead. # -# Notes: This command is deprecated in favor of 'migrate-set-parameters' +# Returns: nothing on success # # Since: 0.14.0 # @@ -1222,7 +1224,8 @@ # <- { "return": {} } # ## -{ 'command': 'migrate_set_downtime', 'data': {'value': 'number'} } +{ 'command': 'migrate_set_downtime', 'data': {'value': 'number'}, + 'features': [ 'deprecated' ] } ## # @migrate_set_speed: @@ -1231,9 +1234,11 @@ # # @value: maximum speed in bytes per second. # -# Returns: nothing on success +# Features: +# @deprecated: This command is deprecated. Use +# 'migrate-set-parameters' instead. # -# Notes: This command is deprecated in favor of 'migrate-set-parameters' +# Returns: nothing on success # # Since: 0.14.0 # @@ -1243,7 +1248,8 @@ # <- { "return": {} } # ## -{ 'command': 'migrate_set_speed', 'data': {'value': 'int'} } +{ 'command': 'migrate_set_speed', 'data': {'value': 'int'}, + 'features': [ 'deprecated' ] } ## # @migrate-set-cache-size: @@ -1252,13 +1258,15 @@ # # @value: cache size in bytes # +# Features: +# @deprecated: This command is deprecated. Use +# 'migrate-set-parameters' instead. +# # The size will be rounded down to the nearest power of 2. # The cache size can be modified before and during ongoing migration # # Returns: nothing on success # -# Notes: This command is deprecated in favor of 'migrate-set-parameters' -# # Since: 1.2 # # Example: @@ -1268,16 +1276,19 @@ # <- { "return": {} } # ## -{ 'command': 'migrate-set-cache-size', 'data': {'value': 'int'} } +{ 'command': 'migrate-set-cache-size', 'data': {'value': 'int'}, + 'features': [ 'deprecated' ] } ## # @query-migrate-cache-size: # # Query migration XBZRLE cache size # -# Returns: XBZRLE cache size in bytes +# Features: +# @deprecated: This command is deprecated. Use +# 'query-migrate-parameters' instead. # -# Notes: This command is deprecated in favor of 'query-migrate-parameters' +# Returns: XBZRLE cache size in bytes # # Since: 1.2 # @@ -1287,7 +1298,8 @@ # <- { "return": 67108864 } # ## -{ 'command': 'query-migrate-cache-size', 'returns': 'int' } +{ 'command': 'query-migrate-cache-size', 'returns': 'int', + 'features': [ 'deprecated' ] } ## # @migrate: diff --git a/qapi/misc.json b/qapi/misc.json index c18fe681fb..99b90ac80b 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -872,14 +872,14 @@ # If @device is 'vnc' and @target is 'password', this is the new VNC # password to set. See change-vnc-password for additional notes. # +# Features: +# @deprecated: This command is deprecated. For changing block +# devices, use 'blockdev-change-medium' instead; for changing VNC +# parameters, use 'change-vnc-password' instead. +# # Returns: - Nothing on success. # - If @device is not a valid block device, DeviceNotFound # -# Notes: This interface is deprecated, and it is strongly recommended that you -# avoid using it. For changing block devices, use -# blockdev-change-medium; for changing VNC parameters, use -# change-vnc-password. -# # Since: 0.14.0 # # Example: @@ -900,7 +900,8 @@ # ## { 'command': 'change', - 'data': {'device': 'str', 'target': 'str', '*arg': 'str'} } + 'data': {'device': 'str', 'target': 'str', '*arg': 'str'}, + 'features': [ 'deprecated' ] } ## # @xen-set-global-dirty-log: diff --git a/qapi/net.json b/qapi/net.json index 1cb9a7d782..cebb1b52e3 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -39,18 +39,8 @@ # # Add a network backend. # -# @type: the type of network backend. Possible values are listed in -# NetClientDriver (excluding 'none' and 'nic') -# -# @id: the name of the new network backend -# # Additional arguments depend on the type. # -# TODO: This command effectively bypasses QAPI completely due to its -# "additional arguments" business. It shouldn't have been added to -# the schema in this form. It should be qapified properly, or -# replaced by a properly qapified command. -# # Since: 0.14.0 # # Returns: Nothing on success @@ -64,9 +54,7 @@ # <- { "return": {} } # ## -{ 'command': 'netdev_add', - 'data': {'type': 'str', 'id': 'str'}, - 'gen': false } # so we can get the additional arguments +{ 'command': 'netdev_add', 'data': 'Netdev', 'boxed': true } ## # @netdev_del: diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index bc264b3c9b..c30c7ff9e1 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -19,20 +19,13 @@ #include "sysemu/runstate.h" #include "qapi/qmp/qbool.h" -static QDict *qmp_dispatch_check_obj(const QObject *request, bool allow_oob, +static QDict *qmp_dispatch_check_obj(QDict *dict, bool allow_oob, Error **errp) { const char *exec_key = NULL; const QDictEntry *ent; const char *arg_name; const QObject *arg_obj; - QDict *dict; - - dict = qobject_to(QDict, request); - if (!dict) { - error_setg(errp, "QMP input must be a JSON object"); - return NULL; - } for (ent = qdict_first(dict); ent; ent = qdict_next(dict, ent)) { @@ -75,19 +68,50 @@ static QDict *qmp_dispatch_check_obj(const QObject *request, bool allow_oob, return dict; } -static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request, - bool allow_oob, Error **errp) +QDict *qmp_error_response(Error *err) +{ + QDict *rsp; + + rsp = qdict_from_jsonf_nofail("{ 'error': { 'class': %s, 'desc': %s } }", + QapiErrorClass_str(error_get_class(err)), + error_get_pretty(err)); + error_free(err); + return rsp; +} + +/* + * Does @qdict look like a command to be run out-of-band? + */ +bool qmp_is_oob(const QDict *dict) +{ + return qdict_haskey(dict, "exec-oob") + && !qdict_haskey(dict, "execute"); +} + +QDict *qmp_dispatch(const QmpCommandList *cmds, QObject *request, + bool allow_oob) { - Error *local_err = NULL; + Error *err = NULL; bool oob; const char *command; - QDict *args, *dict; - QmpCommand *cmd; + QDict *args; + const QmpCommand *cmd; + QDict *dict; + QObject *id; QObject *ret = NULL; + QDict *rsp = NULL; - dict = qmp_dispatch_check_obj(request, allow_oob, errp); + dict = qobject_to(QDict, request); if (!dict) { - return NULL; + id = NULL; + error_setg(&err, "QMP input must be a JSON object"); + goto out; + } + + id = qdict_get(dict, "id"); + + if (!qmp_dispatch_check_obj(dict, allow_oob, &err)) { + goto out; } command = qdict_get_try_str(dict, "execute"); @@ -99,27 +123,27 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request, } cmd = qmp_find_command(cmds, command); if (cmd == NULL) { - error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, + error_set(&err, ERROR_CLASS_COMMAND_NOT_FOUND, "The command %s has not been found", command); - return NULL; + goto out; } if (!cmd->enabled) { - error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, + error_set(&err, ERROR_CLASS_COMMAND_NOT_FOUND, "The command %s has been disabled for this instance", command); - return NULL; + goto out; } if (oob && !(cmd->options & QCO_ALLOW_OOB)) { - error_setg(errp, "The command %s does not support OOB", + error_setg(&err, "The command %s does not support OOB", command); - return NULL; + goto out; } if (runstate_check(RUN_STATE_PRECONFIG) && !(cmd->options & QCO_ALLOW_PRECONFIG)) { - error_setg(errp, "The command '%s' isn't permitted in '%s' state", + error_setg(&err, "The command '%s' isn't permitted in '%s' state", cmd->name, RunState_str(RUN_STATE_PRECONFIG)); - return NULL; + goto out; } if (!qdict_haskey(dict, "arguments")) { @@ -128,62 +152,36 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request, args = qdict_get_qdict(dict, "arguments"); qobject_ref(args); } + cmd->fn(args, &ret, &err); + qobject_unref(args); + if (err) { + goto out; + } - cmd->fn(args, &ret, &local_err); - if (local_err) { - error_propagate(errp, local_err); - } else if (cmd->options & QCO_NO_SUCCESS_RESP) { + if (cmd->options & QCO_NO_SUCCESS_RESP) { g_assert(!ret); + return NULL; } else if (!ret) { - /* TODO turn into assertion */ + /* + * When the command's schema has no 'returns', cmd->fn() + * leaves @ret null. The QMP spec calls for an empty object + * then; supply it. + */ ret = QOBJECT(qdict_new()); } - qobject_unref(args); - - return ret; -} - -QDict *qmp_error_response(Error *err) -{ - QDict *rsp; - - rsp = qdict_from_jsonf_nofail("{ 'error': { 'class': %s, 'desc': %s } }", - QapiErrorClass_str(error_get_class(err)), - error_get_pretty(err)); - error_free(err); - return rsp; -} - -/* - * Does @qdict look like a command to be run out-of-band? - */ -bool qmp_is_oob(const QDict *dict) -{ - return qdict_haskey(dict, "exec-oob") - && !qdict_haskey(dict, "execute"); -} - -QDict *qmp_dispatch(QmpCommandList *cmds, QObject *request, - bool allow_oob) -{ - Error *err = NULL; - QDict *dict = qobject_to(QDict, request); - QObject *ret, *id = dict ? qdict_get(dict, "id") : NULL; - QDict *rsp; + rsp = qdict_new(); + qdict_put_obj(rsp, "return", ret); - ret = do_qmp_dispatch(cmds, request, allow_oob, &err); +out: if (err) { + assert(!rsp); rsp = qmp_error_response(err); - } else if (ret) { - rsp = qdict_new(); - qdict_put_obj(rsp, "return", ret); - } else { - /* Can only happen for commands with QCO_NO_SUCCESS_RESP */ - rsp = NULL; } - if (rsp && id) { + assert(rsp); + + if (id) { qdict_put_obj(rsp, "id", qobject_ref(id)); } diff --git a/qapi/qmp-registry.c b/qapi/qmp-registry.c index ca00f74795..d0f9a1d3e3 100644 --- a/qapi/qmp-registry.c +++ b/qapi/qmp-registry.c @@ -27,7 +27,7 @@ void qmp_register_command(QmpCommandList *cmds, const char *name, QTAILQ_INSERT_TAIL(cmds, cmd, node); } -QmpCommand *qmp_find_command(QmpCommandList *cmds, const char *name) +const QmpCommand *qmp_find_command(const QmpCommandList *cmds, const char *name) { QmpCommand *cmd; @@ -77,10 +77,10 @@ bool qmp_has_success_response(const QmpCommand *cmd) return !(cmd->options & QCO_NO_SUCCESS_RESP); } -void qmp_for_each_command(QmpCommandList *cmds, qmp_cmd_callback_fn fn, +void qmp_for_each_command(const QmpCommandList *cmds, qmp_cmd_callback_fn fn, void *opaque) { - QmpCommand *cmd; + const QmpCommand *cmd; QTAILQ_FOREACH(cmd, cmds, node) { fn(cmd, opaque); diff --git a/qga/commands.c b/qga/commands.c index 43c323cead..f8852beb9c 100644 --- a/qga/commands.c +++ b/qga/commands.c @@ -54,7 +54,7 @@ void qmp_guest_ping(Error **errp) slog("guest-ping called"); } -static void qmp_command_info(QmpCommand *cmd, void *opaque) +static void qmp_command_info(const QmpCommand *cmd, void *opaque) { GuestAgentInfo *info = opaque; GuestAgentCommandInfo *cmd_info; diff --git a/qga/main.c b/qga/main.c index e5c39c189a..8ee2736f8e 100644 --- a/qga/main.c +++ b/qga/main.c @@ -359,7 +359,7 @@ static gint ga_strcmp(gconstpointer str1, gconstpointer str2) } /* disable commands that aren't safe for fsfreeze */ -static void ga_disable_non_whitelisted(QmpCommand *cmd, void *opaque) +static void ga_disable_non_whitelisted(const QmpCommand *cmd, void *opaque) { bool whitelisted = false; int i = 0; @@ -378,7 +378,7 @@ static void ga_disable_non_whitelisted(QmpCommand *cmd, void *opaque) } /* [re-]enable all commands, except those explicitly blacklisted by user */ -static void ga_enable_non_blacklisted(QmpCommand *cmd, void *opaque) +static void ga_enable_non_blacklisted(const QmpCommand *cmd, void *opaque) { GList *blacklist = opaque; const char *name = qmp_command_name(cmd); @@ -918,7 +918,7 @@ int64_t ga_get_fd_handle(GAState *s, Error **errp) return handle; } -static void ga_print_cmd(QmpCommand *cmd, void *opaque) +static void ga_print_cmd(const QmpCommand *cmd, void *opaque) { printf("%s\n", qmp_command_name(cmd)); } diff --git a/roms/SLOF b/roms/SLOF -Subproject 9546892a80d5a4c73deea6719de46372f007f4a +Subproject ab6984f5a6d054e1f634dda855b32e535711197 diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index 0e13e82989..bc30876c88 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -283,9 +283,9 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds); prefix=self._prefix)) self._genc.add(gen_registry(self._regy.get_content(), self._prefix)) - def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, - success_response, boxed, allow_oob, allow_preconfig, - features): + def visit_command(self, name, info, ifcond, features, + arg_type, ret_type, gen, success_response, boxed, + allow_oob, allow_preconfig): if not gen: return # FIXME: If T is a user-defined type, the user is responsible diff --git a/scripts/qapi/doc.py b/scripts/qapi/doc.py index 1787a53d91..92f584edcf 100644 --- a/scripts/qapi/doc.py +++ b/scripts/qapi/doc.py @@ -243,34 +243,34 @@ class QAPISchemaGenDocVisitor(QAPISchemaVisitor): def write(self, output_dir): self._gen.write(output_dir) - def visit_enum_type(self, name, info, ifcond, members, prefix): + def visit_enum_type(self, name, info, ifcond, features, members, prefix): doc = self.cur_doc self._gen.add(texi_type('Enum', doc, ifcond, texi_members(doc, 'Values', member_func=texi_enum_value))) - def visit_object_type(self, name, info, ifcond, base, members, variants, - features): + def visit_object_type(self, name, info, ifcond, features, + base, members, variants): doc = self.cur_doc if base and base.is_implicit(): base = None self._gen.add(texi_type('Object', doc, ifcond, texi_members(doc, 'Members', base, variants))) - def visit_alternate_type(self, name, info, ifcond, variants): + def visit_alternate_type(self, name, info, ifcond, features, variants): doc = self.cur_doc self._gen.add(texi_type('Alternate', doc, ifcond, texi_members(doc, 'Members'))) - def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, - success_response, boxed, allow_oob, allow_preconfig, - features): + def visit_command(self, name, info, ifcond, features, + arg_type, ret_type, gen, success_response, boxed, + allow_oob, allow_preconfig): doc = self.cur_doc self._gen.add(texi_msg('Command', doc, ifcond, texi_arguments(doc, arg_type if boxed else None))) - def visit_event(self, name, info, ifcond, arg_type, boxed): + def visit_event(self, name, info, ifcond, features, arg_type, boxed): doc = self.cur_doc self._gen.add(texi_msg('Event', doc, ifcond, texi_arguments(doc, diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py index a98b9f5099..b544af5a1c 100644 --- a/scripts/qapi/events.py +++ b/scripts/qapi/events.py @@ -189,7 +189,7 @@ void %(event_emit)s(%(event_enum)s event, QDict *qdict); event_emit=self._event_emit_name, event_enum=self._event_enum_name)) - def visit_event(self, name, info, ifcond, arg_type, boxed): + def visit_event(self, name, info, ifcond, features, arg_type, boxed): with ifcontext(ifcond, self._genh, self._genc): self._genh.add(gen_event_send_decl(name, arg_type, boxed)) self._genc.add(gen_event_send(name, arg_type, boxed, diff --git a/scripts/qapi/expr.py b/scripts/qapi/expr.py index fecf466fa7..2942520399 100644 --- a/scripts/qapi/expr.py +++ b/scripts/qapi/expr.py @@ -167,8 +167,9 @@ def check_type(value, info, source, allow_optional=True, permit_upper=permit_upper) if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'): raise QAPISemError(info, "%s uses reserved name" % key_source) - check_keys(arg, info, key_source, ['type'], ['if']) + check_keys(arg, info, key_source, ['type'], ['if', 'features']) check_if(arg, info, key_source) + check_features(arg.get('features'), info) check_type(arg['type'], info, key_source, allow_array=True) @@ -219,7 +220,6 @@ def check_struct(expr, info): check_type(members, info, "'data'", allow_dict=name) check_type(expr.get('base'), info, "'base'") - check_features(expr.get('features'), info) def check_union(expr, info): @@ -267,7 +267,6 @@ def check_command(expr, info): raise QAPISemError(info, "'boxed': true requires 'data'") check_type(args, info, "'data'", allow_dict=not boxed) check_type(rets, info, "'returns'", allow_array=True) - check_features(expr.get('features'), info) def check_event(expr, info): @@ -319,18 +318,18 @@ def check_exprs(exprs): if meta == 'enum': check_keys(expr, info, meta, - ['enum', 'data'], ['if', 'prefix']) + ['enum', 'data'], ['if', 'features', 'prefix']) check_enum(expr, info) elif meta == 'union': check_keys(expr, info, meta, ['union', 'data'], - ['base', 'discriminator', 'if']) + ['base', 'discriminator', 'if', 'features']) normalize_members(expr.get('base')) normalize_members(expr['data']) check_union(expr, info) elif meta == 'alternate': check_keys(expr, info, meta, - ['alternate', 'data'], ['if']) + ['alternate', 'data'], ['if', 'features']) normalize_members(expr['data']) check_alternate(expr, info) elif meta == 'struct': @@ -348,13 +347,14 @@ def check_exprs(exprs): check_command(expr, info) elif meta == 'event': check_keys(expr, info, meta, - ['event'], ['data', 'boxed', 'if']) + ['event'], ['data', 'boxed', 'if', 'features']) normalize_members(expr.get('data')) check_event(expr, info) else: assert False, 'unexpected meta type' check_if(expr, info, meta) + check_features(expr.get('features'), info) check_flags(expr, info) return exprs diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index b5537eddc0..23652be810 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -16,7 +16,19 @@ from qapi.schema import (QAPISchemaArrayType, QAPISchemaBuiltinType, QAPISchemaType) -def to_qlit(obj, level=0, suppress_first_indent=False): +def _make_tree(obj, ifcond, features, extra=None): + if extra is None: + extra = {} + if ifcond: + extra['if'] = ifcond + if features: + obj['features'] = [(f.name, {'if': f.ifcond}) for f in features] + if extra: + return (obj, extra) + return obj + + +def _tree_to_qlit(obj, level=0, suppress_first_indent=False): def indent(level): return level * 4 * ' ' @@ -30,7 +42,7 @@ def to_qlit(obj, level=0, suppress_first_indent=False): ret += indent(level) + '/* %s */\n' % comment if ifcond: ret += gen_if(ifcond) - ret += to_qlit(ifobj, level) + ret += _tree_to_qlit(ifobj, level) if ifcond: ret += '\n' + gen_endif(ifcond) return ret @@ -43,7 +55,7 @@ def to_qlit(obj, level=0, suppress_first_indent=False): elif isinstance(obj, str): ret += 'QLIT_QSTR(' + to_c_string(obj) + ')' elif isinstance(obj, list): - elts = [to_qlit(elt, level + 1).strip('\n') + elts = [_tree_to_qlit(elt, level + 1).strip('\n') for elt in obj] elts.append(indent(level + 1) + "{}") ret += 'QLIT_QLIST(((QLitObject[]) {\n' @@ -53,7 +65,8 @@ def to_qlit(obj, level=0, suppress_first_indent=False): elts = [] for key, value in sorted(obj.items()): elts.append(indent(level + 1) + '{ %s, %s }' % - (to_c_string(key), to_qlit(value, level + 1, True))) + (to_c_string(key), + _tree_to_qlit(value, level + 1, True))) elts.append(indent(level + 1) + '{}') ret += 'QLIT_QDICT(((QLitDictEntry[]) {\n' ret += ',\n'.join(elts) + '\n' @@ -79,7 +92,7 @@ class QAPISchemaGenIntrospectVisitor(QAPISchemaMonolithicCVisitor): ' * QAPI/QMP schema introspection', __doc__) self._unmask = unmask self._schema = None - self._qlits = [] + self._trees = [] self._used_types = [] self._name_map = {} self._genc.add(mcgen(''' @@ -108,9 +121,9 @@ extern const QLitObject %(c_name)s; const QLitObject %(c_name)s = %(c_string)s; ''', c_name=c_name(name), - c_string=to_qlit(self._qlits))) + c_string=_tree_to_qlit(self._trees))) self._schema = None - self._qlits = [] + self._trees = [] self._used_types = [] self._name_map = {} @@ -144,89 +157,78 @@ const QLitObject %(c_name)s = %(c_string)s; return '[' + self._use_type(typ.element_type) + ']' return self._name(typ.name) - def _gen_qlit(self, name, mtype, obj, ifcond): - extra = {} + def _gen_tree(self, name, mtype, obj, ifcond, features): + extra = None if mtype not in ('command', 'event', 'builtin', 'array'): if not self._unmask: # Output a comment to make it easy to map masked names # back to the source when reading the generated output. - extra['comment'] = '"%s" = %s' % (self._name(name), name) + extra = {'comment': '"%s" = %s' % (self._name(name), name)} name = self._name(name) obj['name'] = name obj['meta-type'] = mtype - if ifcond: - extra['if'] = ifcond - if extra: - self._qlits.append((obj, extra)) - else: - self._qlits.append(obj) + self._trees.append(_make_tree(obj, ifcond, features, extra)) def _gen_member(self, member): - ret = {'name': member.name, 'type': self._use_type(member.type)} + obj = {'name': member.name, 'type': self._use_type(member.type)} if member.optional: - ret['default'] = None - if member.ifcond: - ret = (ret, {'if': member.ifcond}) - return ret + obj['default'] = None + return _make_tree(obj, member.ifcond, member.features) def _gen_variants(self, tag_name, variants): return {'tag': tag_name, 'variants': [self._gen_variant(v) for v in variants]} def _gen_variant(self, variant): - return ({'case': variant.name, 'type': self._use_type(variant.type)}, - {'if': variant.ifcond}) + obj = {'case': variant.name, 'type': self._use_type(variant.type)} + return _make_tree(obj, variant.ifcond, None) def visit_builtin_type(self, name, info, json_type): - self._gen_qlit(name, 'builtin', {'json-type': json_type}, []) + self._gen_tree(name, 'builtin', {'json-type': json_type}, [], None) - def visit_enum_type(self, name, info, ifcond, members, prefix): - self._gen_qlit(name, 'enum', - {'values': - [(m.name, {'if': m.ifcond}) for m in members]}, - ifcond) + def visit_enum_type(self, name, info, ifcond, features, members, prefix): + self._gen_tree(name, 'enum', + {'values': [_make_tree(m.name, m.ifcond, None) + for m in members]}, + ifcond, features) def visit_array_type(self, name, info, ifcond, element_type): element = self._use_type(element_type) - self._gen_qlit('[' + element + ']', 'array', {'element-type': element}, - ifcond) + self._gen_tree('[' + element + ']', 'array', {'element-type': element}, + ifcond, None) - def visit_object_type_flat(self, name, info, ifcond, members, variants, - features): + def visit_object_type_flat(self, name, info, ifcond, features, + members, variants): obj = {'members': [self._gen_member(m) for m in members]} if variants: obj.update(self._gen_variants(variants.tag_member.name, variants.variants)) - if features: - obj['features'] = [(f.name, {'if': f.ifcond}) for f in features] - self._gen_qlit(name, 'object', obj, ifcond) + self._gen_tree(name, 'object', obj, ifcond, features) - def visit_alternate_type(self, name, info, ifcond, variants): - self._gen_qlit(name, 'alternate', + def visit_alternate_type(self, name, info, ifcond, features, variants): + self._gen_tree(name, 'alternate', {'members': [ - ({'type': self._use_type(m.type)}, {'if': m.ifcond}) - for m in variants.variants]}, ifcond) - - def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, - success_response, boxed, allow_oob, allow_preconfig, - features): + _make_tree({'type': self._use_type(m.type)}, + m.ifcond, None) + for m in variants.variants]}, + ifcond, features) + + def visit_command(self, name, info, ifcond, features, + arg_type, ret_type, gen, success_response, boxed, + allow_oob, allow_preconfig): arg_type = arg_type or self._schema.the_empty_object_type ret_type = ret_type or self._schema.the_empty_object_type obj = {'arg-type': self._use_type(arg_type), 'ret-type': self._use_type(ret_type)} if allow_oob: obj['allow-oob'] = allow_oob + self._gen_tree(name, 'command', obj, ifcond, features) - if features: - obj['features'] = [(f.name, {'if': f.ifcond}) for f in features] - - self._gen_qlit(name, 'command', obj, ifcond) - - def visit_event(self, name, info, ifcond, arg_type, boxed): + def visit_event(self, name, info, ifcond, features, arg_type, boxed): arg_type = arg_type or self._schema.the_empty_object_type - self._gen_qlit(name, 'event', {'arg-type': self._use_type(arg_type)}, - ifcond) + self._gen_tree(name, 'event', {'arg-type': self._use_type(arg_type)}, + ifcond, features) def gen_introspect(schema, output_dir, prefix, opt_unmask): diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index d759308b4e..78309a00f0 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -53,13 +53,13 @@ class QAPISchemaEntity: seen = {} for f in self.features: f.check_clash(self.info, seen) - if self.doc: - self.doc.connect_feature(f) - self._checked = True def connect_doc(self, doc=None): - pass + doc = doc or self.doc + if doc: + for f in self.features: + doc.connect_feature(f) def check_doc(self): if self.doc: @@ -109,29 +109,29 @@ class QAPISchemaVisitor: def visit_builtin_type(self, name, info, json_type): pass - def visit_enum_type(self, name, info, ifcond, members, prefix): + def visit_enum_type(self, name, info, ifcond, features, members, prefix): pass def visit_array_type(self, name, info, ifcond, element_type): pass - def visit_object_type(self, name, info, ifcond, base, members, variants, - features): + def visit_object_type(self, name, info, ifcond, features, + base, members, variants): pass - def visit_object_type_flat(self, name, info, ifcond, members, variants, - features): + def visit_object_type_flat(self, name, info, ifcond, features, + members, variants): pass - def visit_alternate_type(self, name, info, ifcond, variants): + def visit_alternate_type(self, name, info, ifcond, features, variants): pass - def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, - success_response, boxed, allow_oob, allow_preconfig, - features): + def visit_command(self, name, info, ifcond, features, + arg_type, ret_type, gen, success_response, boxed, + allow_oob, allow_preconfig): pass - def visit_event(self, name, info, ifcond, arg_type, boxed): + def visit_event(self, name, info, ifcond, features, arg_type, boxed): pass @@ -193,6 +193,12 @@ class QAPISchemaType(QAPISchemaEntity): return None return self.name + def check(self, schema): + QAPISchemaEntity.check(self, schema) + if 'deprecated' in [f.name for f in self.features]: + raise QAPISemError( + self.info, "feature 'deprecated' is not supported for types") + def describe(self): assert self.meta return "%s type '%s'" % (self.meta, self.name) @@ -234,8 +240,8 @@ class QAPISchemaBuiltinType(QAPISchemaType): class QAPISchemaEnumType(QAPISchemaType): meta = 'enum' - def __init__(self, name, info, doc, ifcond, members, prefix): - super().__init__(name, info, doc, ifcond) + def __init__(self, name, info, doc, ifcond, features, members, prefix): + super().__init__(name, info, doc, ifcond, features) for m in members: assert isinstance(m, QAPISchemaEnumMember) m.set_defined_in(name) @@ -250,10 +256,10 @@ class QAPISchemaEnumType(QAPISchemaType): m.check_clash(self.info, seen) def connect_doc(self, doc=None): + super().connect_doc(doc) doc = doc or self.doc - if doc: - for m in self.members: - doc.connect_member(m) + for m in self.members: + m.connect_doc(doc) def is_implicit(self): # See QAPISchema._make_implicit_enum_type() and ._def_predefineds() @@ -270,15 +276,16 @@ class QAPISchemaEnumType(QAPISchemaType): def visit(self, visitor): super().visit(visitor) - visitor.visit_enum_type(self.name, self.info, self.ifcond, - self.members, self.prefix) + visitor.visit_enum_type( + self.name, self.info, self.ifcond, self.features, + self.members, self.prefix) class QAPISchemaArrayType(QAPISchemaType): meta = 'array' def __init__(self, name, info, element_type): - super().__init__(name, info, None, None) + super().__init__(name, info, None) assert isinstance(element_type, str) self._element_type_name = element_type self.element_type = None @@ -324,8 +331,8 @@ class QAPISchemaArrayType(QAPISchemaType): class QAPISchemaObjectType(QAPISchemaType): - def __init__(self, name, info, doc, ifcond, - base, local_members, variants, features): + def __init__(self, name, info, doc, ifcond, features, + base, local_members, variants): # struct has local_members, optional base, and no variants # flat union has base, variants, and no local_members # simple union has local_members, variants, and no base @@ -336,7 +343,7 @@ class QAPISchemaObjectType(QAPISchemaType): assert isinstance(m, QAPISchemaObjectTypeMember) m.set_defined_in(name) if variants is not None: - assert isinstance(variants, QAPISchemaObjectTypeVariants) + assert isinstance(variants, QAPISchemaVariants) variants.set_defined_in(name) self._base_name = base self.base = None @@ -392,12 +399,12 @@ class QAPISchemaObjectType(QAPISchemaType): m.check_clash(info, seen) def connect_doc(self, doc=None): + super().connect_doc(doc) doc = doc or self.doc - if doc: - if self.base and self.base.is_implicit(): - self.base.connect_doc(doc) - for m in self.local_members: - doc.connect_member(m) + if self.base and self.base.is_implicit(): + self.base.connect_doc(doc) + for m in self.local_members: + m.connect_doc(doc) @property def ifcond(self): @@ -433,93 +440,82 @@ class QAPISchemaObjectType(QAPISchemaType): def visit(self, visitor): super().visit(visitor) - visitor.visit_object_type(self.name, self.info, self.ifcond, - self.base, self.local_members, self.variants, - self.features) - visitor.visit_object_type_flat(self.name, self.info, self.ifcond, - self.members, self.variants, - self.features) - - -class QAPISchemaMember: - """ Represents object members, enum members and features """ - role = 'member' - - def __init__(self, name, info, ifcond=None): - assert isinstance(name, str) - self.name = name - self.info = info - self.ifcond = ifcond or [] - self.defined_in = None - - def set_defined_in(self, name): - assert not self.defined_in - self.defined_in = name - - def check_clash(self, info, seen): - cname = c_name(self.name) - if cname in seen: - raise QAPISemError( - info, - "%s collides with %s" - % (self.describe(info), seen[cname].describe(info))) - seen[cname] = self + visitor.visit_object_type( + self.name, self.info, self.ifcond, self.features, + self.base, self.local_members, self.variants) + visitor.visit_object_type_flat( + self.name, self.info, self.ifcond, self.features, + self.members, self.variants) - def describe(self, info): - role = self.role - defined_in = self.defined_in - assert defined_in - - if defined_in.startswith('q_obj_'): - # See QAPISchema._make_implicit_object_type() - reverse the - # mapping there to create a nice human-readable description - defined_in = defined_in[6:] - if defined_in.endswith('-arg'): - # Implicit type created for a command's dict 'data' - assert role == 'member' - role = 'parameter' - elif defined_in.endswith('-base'): - # Implicit type created for a flat union's dict 'base' - role = 'base ' + role - else: - # Implicit type created for a simple union's branch - assert defined_in.endswith('-wrapper') - # Unreachable and not implemented - assert False - elif defined_in.endswith('Kind'): - # See QAPISchema._make_implicit_enum_type() - # Implicit enum created for simple union's branches - assert role == 'value' - role = 'branch' - elif defined_in != info.defn_name: - return "%s '%s' of type '%s'" % (role, self.name, defined_in) - return "%s '%s'" % (role, self.name) +class QAPISchemaAlternateType(QAPISchemaType): + meta = 'alternate' -class QAPISchemaEnumMember(QAPISchemaMember): - role = 'value' + def __init__(self, name, info, doc, ifcond, features, variants): + super().__init__(name, info, doc, ifcond, features) + assert isinstance(variants, QAPISchemaVariants) + assert variants.tag_member + variants.set_defined_in(name) + variants.tag_member.set_defined_in(self.name) + self.variants = variants + def check(self, schema): + super().check(schema) + self.variants.tag_member.check(schema) + # Not calling self.variants.check_clash(), because there's nothing + # to clash with + self.variants.check(schema, {}) + # Alternate branch names have no relation to the tag enum values; + # so we have to check for potential name collisions ourselves. + seen = {} + types_seen = {} + for v in self.variants.variants: + v.check_clash(self.info, seen) + qtype = v.type.alternate_qtype() + if not qtype: + raise QAPISemError( + self.info, + "%s cannot use %s" + % (v.describe(self.info), v.type.describe())) + conflicting = set([qtype]) + if qtype == 'QTYPE_QSTRING': + if isinstance(v.type, QAPISchemaEnumType): + for m in v.type.members: + if m.name in ['on', 'off']: + conflicting.add('QTYPE_QBOOL') + if re.match(r'[-+0-9.]', m.name): + # lazy, could be tightened + conflicting.add('QTYPE_QNUM') + else: + conflicting.add('QTYPE_QNUM') + conflicting.add('QTYPE_QBOOL') + for qt in conflicting: + if qt in types_seen: + raise QAPISemError( + self.info, + "%s can't be distinguished from '%s'" + % (v.describe(self.info), types_seen[qt])) + types_seen[qt] = v.name -class QAPISchemaFeature(QAPISchemaMember): - role = 'feature' + def connect_doc(self, doc=None): + super().connect_doc(doc) + doc = doc or self.doc + for v in self.variants.variants: + v.connect_doc(doc) + def c_type(self): + return c_name(self.name) + pointer_suffix -class QAPISchemaObjectTypeMember(QAPISchemaMember): - def __init__(self, name, info, typ, optional, ifcond=None): - super().__init__(name, info, ifcond) - assert isinstance(typ, str) - assert isinstance(optional, bool) - self._type_name = typ - self.type = None - self.optional = optional + def json_type(self): + return 'value' - def check(self, schema): - assert self.defined_in - self.type = schema.resolve_type(self._type_name, self.info, - self.describe) + def visit(self, visitor): + super().visit(visitor) + visitor.visit_alternate_type( + self.name, self.info, self.ifcond, self.features, self.variants) -class QAPISchemaObjectTypeVariants: +class QAPISchemaVariants: def __init__(self, tag_name, info, tag_member, variants): # Flat unions pass tag_name but not tag_member. # Simple unions and alternates pass tag_member but not tag_name. @@ -529,7 +525,7 @@ class QAPISchemaObjectTypeVariants: assert (isinstance(tag_name, str) or isinstance(tag_member, QAPISchemaObjectTypeMember)) for v in variants: - assert isinstance(v, QAPISchemaObjectTypeVariant) + assert isinstance(v, QAPISchemaVariant) self._tag_name = tag_name self.info = info self.tag_member = tag_member @@ -579,8 +575,8 @@ class QAPISchemaObjectTypeVariants: cases = {v.name for v in self.variants} for m in self.tag_member.type.members: if m.name not in cases: - v = QAPISchemaObjectTypeVariant(m.name, self.info, - 'q_empty', m.ifcond) + v = QAPISchemaVariant(m.name, self.info, + 'q_empty', m.ifcond) v.set_defined_in(self.tag_member.defined_in) self.variants.append(v) if not self.variants: @@ -610,86 +606,114 @@ class QAPISchemaObjectTypeVariants: v.type.check_clash(info, dict(seen)) -class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember): - role = 'branch' +class QAPISchemaMember: + """ Represents object members, enum members and features """ + role = 'member' - def __init__(self, name, info, typ, ifcond=None): - super().__init__(name, info, typ, False, ifcond) + def __init__(self, name, info, ifcond=None): + assert isinstance(name, str) + self.name = name + self.info = info + self.ifcond = ifcond or [] + self.defined_in = None + def set_defined_in(self, name): + assert not self.defined_in + self.defined_in = name -class QAPISchemaAlternateType(QAPISchemaType): - meta = 'alternate' + def check_clash(self, info, seen): + cname = c_name(self.name) + if cname in seen: + raise QAPISemError( + info, + "%s collides with %s" + % (self.describe(info), seen[cname].describe(info))) + seen[cname] = self + + def connect_doc(self, doc): + if doc: + doc.connect_member(self) + + def describe(self, info): + role = self.role + defined_in = self.defined_in + assert defined_in + + if defined_in.startswith('q_obj_'): + # See QAPISchema._make_implicit_object_type() - reverse the + # mapping there to create a nice human-readable description + defined_in = defined_in[6:] + if defined_in.endswith('-arg'): + # Implicit type created for a command's dict 'data' + assert role == 'member' + role = 'parameter' + elif defined_in.endswith('-base'): + # Implicit type created for a flat union's dict 'base' + role = 'base ' + role + else: + # Implicit type created for a simple union's branch + assert defined_in.endswith('-wrapper') + # Unreachable and not implemented + assert False + elif defined_in.endswith('Kind'): + # See QAPISchema._make_implicit_enum_type() + # Implicit enum created for simple union's branches + assert role == 'value' + role = 'branch' + elif defined_in != info.defn_name: + return "%s '%s' of type '%s'" % (role, self.name, defined_in) + return "%s '%s'" % (role, self.name) + + +class QAPISchemaEnumMember(QAPISchemaMember): + role = 'value' + + +class QAPISchemaFeature(QAPISchemaMember): + role = 'feature' - def __init__(self, name, info, doc, ifcond, variants): - super().__init__(name, info, doc, ifcond) - assert isinstance(variants, QAPISchemaObjectTypeVariants) - assert variants.tag_member - variants.set_defined_in(name) - variants.tag_member.set_defined_in(self.name) - self.variants = variants + +class QAPISchemaObjectTypeMember(QAPISchemaMember): + def __init__(self, name, info, typ, optional, ifcond=None, features=None): + super().__init__(name, info, ifcond) + assert isinstance(typ, str) + assert isinstance(optional, bool) + for f in features or []: + assert isinstance(f, QAPISchemaFeature) + f.set_defined_in(name) + self._type_name = typ + self.type = None + self.optional = optional + self.features = features or [] def check(self, schema): - super().check(schema) - self.variants.tag_member.check(schema) - # Not calling self.variants.check_clash(), because there's nothing - # to clash with - self.variants.check(schema, {}) - # Alternate branch names have no relation to the tag enum values; - # so we have to check for potential name collisions ourselves. + assert self.defined_in + self.type = schema.resolve_type(self._type_name, self.info, + self.describe) seen = {} - types_seen = {} - for v in self.variants.variants: - v.check_clash(self.info, seen) - qtype = v.type.alternate_qtype() - if not qtype: - raise QAPISemError( - self.info, - "%s cannot use %s" - % (v.describe(self.info), v.type.describe())) - conflicting = set([qtype]) - if qtype == 'QTYPE_QSTRING': - if isinstance(v.type, QAPISchemaEnumType): - for m in v.type.members: - if m.name in ['on', 'off']: - conflicting.add('QTYPE_QBOOL') - if re.match(r'[-+0-9.]', m.name): - # lazy, could be tightened - conflicting.add('QTYPE_QNUM') - else: - conflicting.add('QTYPE_QNUM') - conflicting.add('QTYPE_QBOOL') - for qt in conflicting: - if qt in types_seen: - raise QAPISemError( - self.info, - "%s can't be distinguished from '%s'" - % (v.describe(self.info), types_seen[qt])) - types_seen[qt] = v.name + for f in self.features: + f.check_clash(self.info, seen) - def connect_doc(self, doc=None): - doc = doc or self.doc + def connect_doc(self, doc): + super().connect_doc(doc) if doc: - for v in self.variants.variants: - doc.connect_member(v) + for f in self.features: + doc.connect_feature(f) - def c_type(self): - return c_name(self.name) + pointer_suffix - def json_type(self): - return 'value' +class QAPISchemaVariant(QAPISchemaObjectTypeMember): + role = 'branch' - def visit(self, visitor): - super().visit(visitor) - visitor.visit_alternate_type(self.name, self.info, self.ifcond, - self.variants) + def __init__(self, name, info, typ, ifcond=None): + super().__init__(name, info, typ, False, ifcond) class QAPISchemaCommand(QAPISchemaEntity): meta = 'command' - def __init__(self, name, info, doc, ifcond, arg_type, ret_type, - gen, success_response, boxed, allow_oob, allow_preconfig, - features): + def __init__(self, name, info, doc, ifcond, features, + arg_type, ret_type, + gen, success_response, boxed, allow_oob, allow_preconfig): super().__init__(name, info, doc, ifcond, features) assert not arg_type or isinstance(arg_type, str) assert not ret_type or isinstance(ret_type, str) @@ -733,6 +757,7 @@ class QAPISchemaCommand(QAPISchemaEntity): % self.ret_type.describe()) def connect_doc(self, doc=None): + super().connect_doc(doc) doc = doc or self.doc if doc: if self.arg_type and self.arg_type.is_implicit(): @@ -740,19 +765,17 @@ class QAPISchemaCommand(QAPISchemaEntity): def visit(self, visitor): super().visit(visitor) - visitor.visit_command(self.name, self.info, self.ifcond, - self.arg_type, self.ret_type, - self.gen, self.success_response, - self.boxed, self.allow_oob, - self.allow_preconfig, - self.features) + visitor.visit_command( + self.name, self.info, self.ifcond, self.features, + self.arg_type, self.ret_type, self.gen, self.success_response, + self.boxed, self.allow_oob, self.allow_preconfig) class QAPISchemaEvent(QAPISchemaEntity): meta = 'event' - def __init__(self, name, info, doc, ifcond, arg_type, boxed): - super().__init__(name, info, doc, ifcond) + def __init__(self, name, info, doc, ifcond, features, arg_type, boxed): + super().__init__(name, info, doc, ifcond, features) assert not arg_type or isinstance(arg_type, str) self._arg_type_name = arg_type self.arg_type = None @@ -775,6 +798,7 @@ class QAPISchemaEvent(QAPISchemaEntity): % self.arg_type.describe()) def connect_doc(self, doc=None): + super().connect_doc(doc) doc = doc or self.doc if doc: if self.arg_type and self.arg_type.is_implicit(): @@ -782,8 +806,9 @@ class QAPISchemaEvent(QAPISchemaEntity): def visit(self, visitor): super().visit(visitor) - visitor.visit_event(self.name, self.info, self.ifcond, - self.arg_type, self.boxed) + visitor.visit_event( + self.name, self.info, self.ifcond, self.features, + self.arg_type, self.boxed) class QAPISchema: @@ -888,7 +913,7 @@ class QAPISchema: ('null', 'null', 'QNull' + pointer_suffix)]: self._def_builtin_type(*t) self.the_empty_object_type = QAPISchemaObjectType( - 'q_empty', None, None, None, None, [], None, []) + 'q_empty', None, None, None, None, None, [], None) self._def_entity(self.the_empty_object_type) qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', @@ -896,10 +921,12 @@ class QAPISchema: qtype_values = self._make_enum_members( [{'name': n} for n in qtypes], None) - self._def_entity(QAPISchemaEnumType('QType', None, None, None, + self._def_entity(QAPISchemaEnumType('QType', None, None, None, None, qtype_values, 'QTYPE')) def _make_features(self, features, info): + if features is None: + return [] return [QAPISchemaFeature(f['name'], info, f.get('if')) for f in features] @@ -911,7 +938,8 @@ class QAPISchema: # See also QAPISchemaObjectTypeMember.describe() name = name + 'Kind' # reserved by check_defn_name_str() self._def_entity(QAPISchemaEnumType( - name, info, None, ifcond, self._make_enum_members(values, info), + name, info, None, ifcond, None, + self._make_enum_members(values, info), None)) return name @@ -939,8 +967,8 @@ class QAPISchema: # TODO kill simple unions or implement the disjunction assert (ifcond or []) == typ._ifcond # pylint: disable=protected-access else: - self._def_entity(QAPISchemaObjectType(name, info, None, ifcond, - None, members, None, [])) + self._def_entity(QAPISchemaObjectType( + name, info, None, ifcond, None, None, members, None)) return name def _def_enum_type(self, expr, info, doc): @@ -948,11 +976,12 @@ class QAPISchema: data = expr['data'] prefix = expr.get('prefix') ifcond = expr.get('if') + features = self._make_features(expr.get('features'), info) self._def_entity(QAPISchemaEnumType( - name, info, doc, ifcond, + name, info, doc, ifcond, features, self._make_enum_members(data, info), prefix)) - def _make_member(self, name, typ, ifcond, info): + def _make_member(self, name, typ, ifcond, features, info): optional = False if name.startswith('*'): name = name[1:] @@ -960,10 +989,12 @@ class QAPISchema: if isinstance(typ, list): assert len(typ) == 1 typ = self._make_array_type(typ[0], info) - return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond) + return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond, + self._make_features(features, info)) def _make_members(self, data, info): - return [self._make_member(key, value['type'], value.get('if'), info) + return [self._make_member(key, value['type'], value.get('if'), + value.get('features'), info) for (key, value) in data.items()] def _def_struct_type(self, expr, info, doc): @@ -971,15 +1002,14 @@ class QAPISchema: base = expr.get('base') data = expr['data'] ifcond = expr.get('if') - features = expr.get('features', []) + features = self._make_features(expr.get('features'), info) self._def_entity(QAPISchemaObjectType( - name, info, doc, ifcond, base, + name, info, doc, ifcond, features, base, self._make_members(data, info), - None, - self._make_features(features, info))) + None)) def _make_variant(self, case, typ, ifcond, info): - return QAPISchemaObjectTypeVariant(case, info, typ, ifcond) + return QAPISchemaVariant(case, info, typ, ifcond) def _make_simple_variant(self, case, typ, ifcond, info): if isinstance(typ, list): @@ -987,14 +1017,15 @@ class QAPISchema: typ = self._make_array_type(typ[0], info) typ = self._make_implicit_object_type( typ, info, self.lookup_type(typ), - 'wrapper', [self._make_member('data', typ, None, info)]) - return QAPISchemaObjectTypeVariant(case, info, typ, ifcond) + 'wrapper', [self._make_member('data', typ, None, None, info)]) + return QAPISchemaVariant(case, info, typ, ifcond) def _def_union_type(self, expr, info, doc): name = expr['union'] data = expr['data'] base = expr.get('base') ifcond = expr.get('if') + features = self._make_features(expr.get('features'), info) tag_name = expr.get('discriminator') tag_member = None if isinstance(base, dict): @@ -1015,22 +1046,23 @@ class QAPISchema: tag_member = QAPISchemaObjectTypeMember('type', info, typ, False) members = [tag_member] self._def_entity( - QAPISchemaObjectType(name, info, doc, ifcond, base, members, - QAPISchemaObjectTypeVariants( - tag_name, info, tag_member, variants), - [])) + QAPISchemaObjectType(name, info, doc, ifcond, features, + base, members, + QAPISchemaVariants( + tag_name, info, tag_member, variants))) def _def_alternate_type(self, expr, info, doc): name = expr['alternate'] data = expr['data'] ifcond = expr.get('if') + features = self._make_features(expr.get('features'), info) variants = [self._make_variant(key, value['type'], value.get('if'), info) for (key, value) in data.items()] tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False) self._def_entity( - QAPISchemaAlternateType(name, info, doc, ifcond, - QAPISchemaObjectTypeVariants( + QAPISchemaAlternateType(name, info, doc, ifcond, features, + QAPISchemaVariants( None, info, tag_member, variants))) def _def_command(self, expr, info, doc): @@ -1043,27 +1075,31 @@ class QAPISchema: allow_oob = expr.get('allow-oob', False) allow_preconfig = expr.get('allow-preconfig', False) ifcond = expr.get('if') - features = expr.get('features', []) + features = self._make_features(expr.get('features'), info) if isinstance(data, OrderedDict): data = self._make_implicit_object_type( - name, info, ifcond, 'arg', self._make_members(data, info)) + name, info, ifcond, + 'arg', self._make_members(data, info)) if isinstance(rets, list): assert len(rets) == 1 rets = self._make_array_type(rets[0], info) - self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets, + self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, features, + data, rets, gen, success_response, - boxed, allow_oob, allow_preconfig, - self._make_features(features, info))) + boxed, allow_oob, allow_preconfig)) def _def_event(self, expr, info, doc): name = expr['event'] data = expr.get('data') boxed = expr.get('boxed', False) ifcond = expr.get('if') + features = self._make_features(expr.get('features'), info) if isinstance(data, OrderedDict): data = self._make_implicit_object_type( - name, info, ifcond, 'arg', self._make_members(data, info)) - self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed)) + name, info, ifcond, + 'arg', self._make_members(data, info)) + self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, features, + data, boxed)) def _def_exprs(self, exprs): for expr_elem in exprs: diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py index 3c83b6e4be..3ad33af4ee 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -278,7 +278,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): self._genh.add(gen_type_cleanup_decl(name)) self._genc.add(gen_type_cleanup(name)) - def visit_enum_type(self, name, info, ifcond, members, prefix): + def visit_enum_type(self, name, info, ifcond, features, members, prefix): with ifcontext(ifcond, self._genh, self._genc): self._genh.preamble_add(gen_enum(name, members, prefix)) self._genc.add(gen_enum_lookup(name, members, prefix)) @@ -289,8 +289,8 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): self._genh.add(gen_array(name, element_type)) self._gen_type_cleanup(name) - def visit_object_type(self, name, info, ifcond, base, members, variants, - features): + def visit_object_type(self, name, info, ifcond, features, + base, members, variants): # Nothing to do for the special empty builtin if name == 'q_empty': return @@ -306,7 +306,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): # implicit types won't be directly allocated/freed self._gen_type_cleanup(name) - def visit_alternate_type(self, name, info, ifcond, variants): + def visit_alternate_type(self, name, info, ifcond, features, variants): with ifcontext(ifcond, self._genh): self._genh.preamble_add(gen_fwd_object_or_array(name)) self._genh.add(gen_object(name, ifcond, None, diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py index 421e5bd8cd..23d9194aa4 100644 --- a/scripts/qapi/visit.py +++ b/scripts/qapi/visit.py @@ -316,7 +316,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): ''', types=types)) - def visit_enum_type(self, name, info, ifcond, members, prefix): + def visit_enum_type(self, name, info, ifcond, features, members, prefix): with ifcontext(ifcond, self._genh, self._genc): self._genh.add(gen_visit_decl(name, scalar=True)) self._genc.add(gen_visit_enum(name)) @@ -326,8 +326,8 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): self._genh.add(gen_visit_decl(name)) self._genc.add(gen_visit_list(name, element_type)) - def visit_object_type(self, name, info, ifcond, base, members, variants, - features): + def visit_object_type(self, name, info, ifcond, features, + base, members, variants): # Nothing to do for the special empty builtin if name == 'q_empty': return @@ -342,7 +342,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): self._genh.add(gen_visit_decl(name)) self._genc.add(gen_visit_object(name, base, members, variants)) - def visit_alternate_type(self, name, info, ifcond, variants): + def visit_alternate_type(self, name, info, ifcond, features, variants): with ifcontext(ifcond, self._genh, self._genc): self._genh.add(gen_visit_decl(name)) self._genc.add(gen_visit_alternate(name, variants)) diff --git a/target/alpha/cpu.h b/target/alpha/cpu.h index 3f782c0efe..be29bdd530 100644 --- a/target/alpha/cpu.h +++ b/target/alpha/cpu.h @@ -280,7 +280,7 @@ void alpha_cpu_do_interrupt(CPUState *cpu); bool alpha_cpu_exec_interrupt(CPUState *cpu, int int_req); void alpha_cpu_dump_state(CPUState *cs, FILE *f, int flags); hwaddr alpha_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -int alpha_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int alpha_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int alpha_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void alpha_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, MMUAccessType access_type, diff --git a/target/alpha/gdbstub.c b/target/alpha/gdbstub.c index 7f9cc092a9..0cd76ddaa9 100644 --- a/target/alpha/gdbstub.c +++ b/target/alpha/gdbstub.c @@ -21,7 +21,7 @@ #include "cpu.h" #include "exec/gdbstub.h" -int alpha_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int alpha_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { AlphaCPU *cpu = ALPHA_CPU(cs); CPUAlphaState *env = &cpu->env; diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 8e5ba619a0..a79f233b17 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -195,9 +195,10 @@ static void arm_cpu_reset(DeviceState *dev) env->cp15.cpacr_el1 = deposit64(env->cp15.cpacr_el1, 20, 2, 3); /* and to the SVE instructions */ env->cp15.cpacr_el1 = deposit64(env->cp15.cpacr_el1, 16, 2, 3); - /* with maximum vector length */ - env->vfp.zcr_el[1] = cpu_isar_feature(aa64_sve, cpu) ? - cpu->sve_max_vq - 1 : 0; + /* with reasonable vector length */ + if (cpu_isar_feature(aa64_sve, cpu)) { + env->vfp.zcr_el[1] = MIN(cpu->sve_max_vq - 1, 3); + } /* * Enable TBI0 and TBI1. While the real kernel only enables TBI0, * turning on both here will produce smaller code and otherwise diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 4ffd991b6f..8b9f2961ba 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -128,14 +128,20 @@ enum { /** * DynamicGDBXMLInfo: * @desc: Contains the XML descriptions. - * @num_cpregs: Number of the Coprocessor registers seen by GDB. - * @cpregs_keys: Array that contains the corresponding Key of - * a given cpreg with the same order of the cpreg in the XML description. + * @num: Number of the registers in this XML seen by GDB. + * @data: A union with data specific to the set of registers + * @cpregs_keys: Array that contains the corresponding Key of + * a given cpreg with the same order of the cpreg + * in the XML description. */ typedef struct DynamicGDBXMLInfo { char *desc; - int num_cpregs; - uint32_t *cpregs_keys; + int num; + union { + struct { + uint32_t *keys; + } cpregs; + } data; } DynamicGDBXMLInfo; /* CPU state for each instance of a generic timer (in cp15 c14) */ @@ -749,7 +755,8 @@ struct ARMCPU { uint64_t *cpreg_vmstate_values; int32_t cpreg_vmstate_array_len; - DynamicGDBXMLInfo dyn_xml; + DynamicGDBXMLInfo dyn_sysreg_xml; + DynamicGDBXMLInfo dyn_svereg_xml; /* Timers used by the generic (architected) timer */ QEMUTimer *gt_timer[NUM_GTIMERS]; @@ -968,13 +975,15 @@ bool arm_cpu_exec_interrupt(CPUState *cpu, int int_req); hwaddr arm_cpu_get_phys_page_attrs_debug(CPUState *cpu, vaddr addr, MemTxAttrs *attrs); -int arm_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int arm_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int arm_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); -/* Dynamically generates for gdb stub an XML description of the sysregs from - * the cp_regs hashtable. Returns the registered sysregs number. +/* + * Helpers to dynamically generates XML descriptions of the sysregs + * and SVE registers. Returns the number of registers in each set. */ -int arm_gen_dynamic_xml(CPUState *cpu); +int arm_gen_dynamic_sysreg_xml(CPUState *cpu, int base_reg); +int arm_gen_dynamic_svereg_xml(CPUState *cpu, int base_reg); /* Returns the dynamically generated XML for the gdb stub. * Returns a pointer to the XML contents for the specified XML file or NULL @@ -988,7 +997,7 @@ int arm_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, int cpuid, void *opaque); #ifdef TARGET_AARCH64 -int aarch64_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int aarch64_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int aarch64_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void aarch64_sve_narrow_vq(CPUARMState *env, unsigned vq); void aarch64_sve_change_el(CPUARMState *env, int old_el, diff --git a/target/arm/gdbstub.c b/target/arm/gdbstub.c index 1239abd984..d9ef7d2187 100644 --- a/target/arm/gdbstub.c +++ b/target/arm/gdbstub.c @@ -24,6 +24,7 @@ typedef struct RegisterSysregXmlParam { CPUState *cs; GString *s; + int n; } RegisterSysregXmlParam; /* Old gdb always expect FPA registers. Newer (xml-aware) gdb only expect @@ -32,7 +33,7 @@ typedef struct RegisterSysregXmlParam { We hack round this by giving the FPA regs zero size when talking to a newer gdb. */ -int arm_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int arm_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; @@ -106,15 +107,16 @@ int arm_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) return 0; } -static void arm_gen_one_xml_reg_tag(GString *s, DynamicGDBXMLInfo *dyn_xml, - ARMCPRegInfo *ri, uint32_t ri_key, - int bitsize) +static void arm_gen_one_xml_sysreg_tag(GString *s, DynamicGDBXMLInfo *dyn_xml, + ARMCPRegInfo *ri, uint32_t ri_key, + int bitsize, int regnum) { g_string_append_printf(s, "<reg name=\"%s\"", ri->name); g_string_append_printf(s, " bitsize=\"%d\"", bitsize); + g_string_append_printf(s, " regnum=\"%d\"", regnum); g_string_append_printf(s, " group=\"cp_regs\"/>"); - dyn_xml->num_cpregs++; - dyn_xml->cpregs_keys[dyn_xml->num_cpregs - 1] = ri_key; + dyn_xml->data.cpregs.keys[dyn_xml->num] = ri_key; + dyn_xml->num++; } static void arm_register_sysreg_for_xml(gpointer key, gpointer value, @@ -126,12 +128,13 @@ static void arm_register_sysreg_for_xml(gpointer key, gpointer value, GString *s = param->s; ARMCPU *cpu = ARM_CPU(param->cs); CPUARMState *env = &cpu->env; - DynamicGDBXMLInfo *dyn_xml = &cpu->dyn_xml; + DynamicGDBXMLInfo *dyn_xml = &cpu->dyn_sysreg_xml; if (!(ri->type & (ARM_CP_NO_RAW | ARM_CP_NO_GDB))) { if (arm_feature(env, ARM_FEATURE_AARCH64)) { if (ri->state == ARM_CP_STATE_AA64) { - arm_gen_one_xml_reg_tag(s , dyn_xml, ri, ri_key, 64); + arm_gen_one_xml_sysreg_tag(s , dyn_xml, ri, ri_key, 64, + param->n++); } } else { if (ri->state == ARM_CP_STATE_AA32) { @@ -140,38 +143,174 @@ static void arm_register_sysreg_for_xml(gpointer key, gpointer value, return; } if (ri->type & ARM_CP_64BIT) { - arm_gen_one_xml_reg_tag(s , dyn_xml, ri, ri_key, 64); + arm_gen_one_xml_sysreg_tag(s , dyn_xml, ri, ri_key, 64, + param->n++); } else { - arm_gen_one_xml_reg_tag(s , dyn_xml, ri, ri_key, 32); + arm_gen_one_xml_sysreg_tag(s , dyn_xml, ri, ri_key, 32, + param->n++); } } } } } -int arm_gen_dynamic_xml(CPUState *cs) +int arm_gen_dynamic_sysreg_xml(CPUState *cs, int base_reg) { ARMCPU *cpu = ARM_CPU(cs); GString *s = g_string_new(NULL); - RegisterSysregXmlParam param = {cs, s}; + RegisterSysregXmlParam param = {cs, s, base_reg}; - cpu->dyn_xml.num_cpregs = 0; - cpu->dyn_xml.cpregs_keys = g_new(uint32_t, g_hash_table_size(cpu->cp_regs)); + cpu->dyn_sysreg_xml.num = 0; + cpu->dyn_sysreg_xml.data.cpregs.keys = g_new(uint32_t, g_hash_table_size(cpu->cp_regs)); g_string_printf(s, "<?xml version=\"1.0\"?>"); g_string_append_printf(s, "<!DOCTYPE target SYSTEM \"gdb-target.dtd\">"); g_string_append_printf(s, "<feature name=\"org.qemu.gdb.arm.sys.regs\">"); g_hash_table_foreach(cpu->cp_regs, arm_register_sysreg_for_xml, ¶m); g_string_append_printf(s, "</feature>"); - cpu->dyn_xml.desc = g_string_free(s, false); - return cpu->dyn_xml.num_cpregs; + cpu->dyn_sysreg_xml.desc = g_string_free(s, false); + return cpu->dyn_sysreg_xml.num; } +struct TypeSize { + const char *gdb_type; + int size; + const char sz, suffix; +}; + +static const struct TypeSize vec_lanes[] = { + /* quads */ + { "uint128", 128, 'q', 'u' }, + { "int128", 128, 'q', 's' }, + /* 64 bit */ + { "uint64", 64, 'd', 'u' }, + { "int64", 64, 'd', 's' }, + { "ieee_double", 64, 'd', 'f' }, + /* 32 bit */ + { "uint32", 32, 's', 'u' }, + { "int32", 32, 's', 's' }, + { "ieee_single", 32, 's', 'f' }, + /* 16 bit */ + { "uint16", 16, 'h', 'u' }, + { "int16", 16, 'h', 's' }, + { "ieee_half", 16, 'h', 'f' }, + /* bytes */ + { "uint8", 8, 'b', 'u' }, + { "int8", 8, 'b', 's' }, +}; + + +int arm_gen_dynamic_svereg_xml(CPUState *cs, int base_reg) +{ + ARMCPU *cpu = ARM_CPU(cs); + GString *s = g_string_new(NULL); + DynamicGDBXMLInfo *info = &cpu->dyn_svereg_xml; + g_autoptr(GString) ts = g_string_new(""); + int i, bits, reg_width = (cpu->sve_max_vq * 128); + info->num = 0; + g_string_printf(s, "<?xml version=\"1.0\"?>"); + g_string_append_printf(s, "<!DOCTYPE target SYSTEM \"gdb-target.dtd\">"); + g_string_append_printf(s, "<feature name=\"org.qemu.gdb.aarch64.sve\">"); + + /* First define types and totals in a whole VL */ + for (i = 0; i < ARRAY_SIZE(vec_lanes); i++) { + int count = reg_width / vec_lanes[i].size; + g_string_printf(ts, "vq%d%c%c", count, + vec_lanes[i].sz, vec_lanes[i].suffix); + g_string_append_printf(s, + "<vector id=\"%s\" type=\"%s\" count=\"%d\"/>", + ts->str, vec_lanes[i].gdb_type, count); + } + /* + * Now define a union for each size group containing unsigned and + * signed and potentially float versions of each size from 128 to + * 8 bits. + */ + for (bits = 128; bits >= 8; bits /= 2) { + int count = reg_width / bits; + g_string_append_printf(s, "<union id=\"vq%dn\">", count); + for (i = 0; i < ARRAY_SIZE(vec_lanes); i++) { + if (vec_lanes[i].size == bits) { + g_string_append_printf(s, "<field name=\"%c\" type=\"vq%d%c%c\"/>", + vec_lanes[i].suffix, + count, + vec_lanes[i].sz, vec_lanes[i].suffix); + } + } + g_string_append(s, "</union>"); + } + /* And now the final union of unions */ + g_string_append(s, "<union id=\"vq\">"); + for (bits = 128; bits >= 8; bits /= 2) { + int count = reg_width / bits; + for (i = 0; i < ARRAY_SIZE(vec_lanes); i++) { + if (vec_lanes[i].size == bits) { + g_string_append_printf(s, "<field name=\"%c\" type=\"vq%dn\"/>", + vec_lanes[i].sz, count); + break; + } + } + } + g_string_append(s, "</union>"); + + /* Then define each register in parts for each vq */ + for (i = 0; i < 32; i++) { + g_string_append_printf(s, + "<reg name=\"z%d\" bitsize=\"%d\"" + " regnum=\"%d\" group=\"vector\"" + " type=\"vq\"/>", + i, reg_width, base_reg++); + info->num++; + } + /* fpscr & status registers */ + g_string_append_printf(s, "<reg name=\"fpsr\" bitsize=\"32\"" + " regnum=\"%d\" group=\"float\"" + " type=\"int\"/>", base_reg++); + g_string_append_printf(s, "<reg name=\"fpcr\" bitsize=\"32\"" + " regnum=\"%d\" group=\"float\"" + " type=\"int\"/>", base_reg++); + info->num += 2; + /* + * Predicate registers aren't so big they are worth splitting up + * but we do need to define a type to hold the array of quad + * references. + */ + g_string_append_printf(s, + "<vector id=\"vqp\" type=\"uint16\" count=\"%d\"/>", + cpu->sve_max_vq); + for (i = 0; i < 16; i++) { + g_string_append_printf(s, + "<reg name=\"p%d\" bitsize=\"%d\"" + " regnum=\"%d\" group=\"vector\"" + " type=\"vqp\"/>", + i, cpu->sve_max_vq * 16, base_reg++); + info->num++; + } + g_string_append_printf(s, + "<reg name=\"ffr\" bitsize=\"%d\"" + " regnum=\"%d\" group=\"vector\"" + " type=\"vqp\"/>", + cpu->sve_max_vq * 16, base_reg++); + g_string_append_printf(s, + "<reg name=\"vg\" bitsize=\"64\"" + " regnum=\"%d\" group=\"vector\"" + " type=\"uint32\"/>", + base_reg++); + info->num += 2; + g_string_append_printf(s, "</feature>"); + cpu->dyn_svereg_xml.desc = g_string_free(s, false); + + return cpu->dyn_svereg_xml.num; +} + + const char *arm_gdb_get_dynamic_xml(CPUState *cs, const char *xmlname) { ARMCPU *cpu = ARM_CPU(cs); if (strcmp(xmlname, "system-registers.xml") == 0) { - return cpu->dyn_xml.desc; + return cpu->dyn_sysreg_xml.desc; + } else if (strcmp(xmlname, "sve-registers.xml") == 0) { + return cpu->dyn_svereg_xml.desc; } return NULL; } diff --git a/target/arm/gdbstub64.c b/target/arm/gdbstub64.c index 665ebb3ef6..35d0b80c2d 100644 --- a/target/arm/gdbstub64.c +++ b/target/arm/gdbstub64.c @@ -20,7 +20,7 @@ #include "cpu.h" #include "exec/gdbstub.h" -int aarch64_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int aarch64_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; diff --git a/target/arm/helper.c b/target/arm/helper.c index b61ee73d18..d2ec2c5351 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -48,30 +48,27 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, static void switch_mode(CPUARMState *env, int mode); -static int vfp_gdb_get_reg(CPUARMState *env, uint8_t *buf, int reg) +static int vfp_gdb_get_reg(CPUARMState *env, GByteArray *buf, int reg) { ARMCPU *cpu = env_archcpu(env); int nregs = cpu_isar_feature(aa32_simd_r32, cpu) ? 32 : 16; /* VFP data registers are always little-endian. */ if (reg < nregs) { - stq_le_p(buf, *aa32_vfp_dreg(env, reg)); - return 8; + return gdb_get_reg64(buf, *aa32_vfp_dreg(env, reg)); } if (arm_feature(env, ARM_FEATURE_NEON)) { /* Aliases for Q regs. */ nregs += 16; if (reg < nregs) { uint64_t *q = aa32_vfp_qreg(env, reg - 32); - stq_le_p(buf, q[0]); - stq_le_p(buf + 8, q[1]); - return 16; + return gdb_get_reg128(buf, q[0], q[1]); } } switch (reg - nregs) { - case 0: stl_p(buf, env->vfp.xregs[ARM_VFP_FPSID]); return 4; - case 1: stl_p(buf, vfp_get_fpscr(env)); return 4; - case 2: stl_p(buf, env->vfp.xregs[ARM_VFP_FPEXC]); return 4; + case 0: return gdb_get_reg32(buf, env->vfp.xregs[ARM_VFP_FPSID]); break; + case 1: return gdb_get_reg32(buf, vfp_get_fpscr(env)); break; + case 2: return gdb_get_reg32(buf, env->vfp.xregs[ARM_VFP_FPEXC]); break; } return 0; } @@ -102,25 +99,21 @@ static int vfp_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg) return 0; } -static int aarch64_fpu_gdb_get_reg(CPUARMState *env, uint8_t *buf, int reg) +static int aarch64_fpu_gdb_get_reg(CPUARMState *env, GByteArray *buf, int reg) { switch (reg) { case 0 ... 31: - /* 128 bit FP register */ - { - uint64_t *q = aa64_vfp_qreg(env, reg); - stq_le_p(buf, q[0]); - stq_le_p(buf + 8, q[1]); - return 16; - } + { + /* 128 bit FP register - quads are in LE order */ + uint64_t *q = aa64_vfp_qreg(env, reg); + return gdb_get_reg128(buf, q[1], q[0]); + } case 32: /* FPSR */ - stl_p(buf, vfp_get_fpsr(env)); - return 4; + return gdb_get_reg32(buf, vfp_get_fpsr(env)); case 33: /* FPCR */ - stl_p(buf, vfp_get_fpcr(env)); - return 4; + return gdb_get_reg32(buf,vfp_get_fpcr(env)); default: return 0; } @@ -209,13 +202,22 @@ static void write_raw_cp_reg(CPUARMState *env, const ARMCPRegInfo *ri, } } -static int arm_gdb_get_sysreg(CPUARMState *env, uint8_t *buf, int reg) +/** + * arm_get/set_gdb_*: get/set a gdb register + * @env: the CPU state + * @buf: a buffer to copy to/from + * @reg: register number (offset from start of group) + * + * We return the number of bytes copied + */ + +static int arm_gdb_get_sysreg(CPUARMState *env, GByteArray *buf, int reg) { ARMCPU *cpu = env_archcpu(env); const ARMCPRegInfo *ri; uint32_t key; - key = cpu->dyn_xml.cpregs_keys[reg]; + key = cpu->dyn_sysreg_xml.data.cpregs.keys[reg]; ri = get_arm_cp_reginfo(cpu->cp_regs, key); if (ri) { if (cpreg_field_is_64bit(ri)) { @@ -232,6 +234,102 @@ static int arm_gdb_set_sysreg(CPUARMState *env, uint8_t *buf, int reg) return 0; } +#ifdef TARGET_AARCH64 +static int arm_gdb_get_svereg(CPUARMState *env, GByteArray *buf, int reg) +{ + ARMCPU *cpu = env_archcpu(env); + + switch (reg) { + /* The first 32 registers are the zregs */ + case 0 ... 31: + { + int vq, len = 0; + for (vq = 0; vq < cpu->sve_max_vq; vq++) { + len += gdb_get_reg128(buf, + env->vfp.zregs[reg].d[vq * 2 + 1], + env->vfp.zregs[reg].d[vq * 2]); + } + return len; + } + case 32: + return gdb_get_reg32(buf, vfp_get_fpsr(env)); + case 33: + return gdb_get_reg32(buf, vfp_get_fpcr(env)); + /* then 16 predicates and the ffr */ + case 34 ... 50: + { + int preg = reg - 34; + int vq, len = 0; + for (vq = 0; vq < cpu->sve_max_vq; vq = vq + 4) { + len += gdb_get_reg64(buf, env->vfp.pregs[preg].p[vq / 4]); + } + return len; + } + case 51: + { + /* + * We report in Vector Granules (VG) which is 64bit in a Z reg + * while the ZCR works in Vector Quads (VQ) which is 128bit chunks. + */ + int vq = sve_zcr_len_for_el(env, arm_current_el(env)) + 1; + return gdb_get_reg32(buf, vq * 2); + } + default: + /* gdbstub asked for something out our range */ + qemu_log_mask(LOG_UNIMP, "%s: out of range register %d", __func__, reg); + break; + } + + return 0; +} + +static int arm_gdb_set_svereg(CPUARMState *env, uint8_t *buf, int reg) +{ + ARMCPU *cpu = env_archcpu(env); + + /* The first 32 registers are the zregs */ + switch (reg) { + /* The first 32 registers are the zregs */ + case 0 ... 31: + { + int vq, len = 0; + uint64_t *p = (uint64_t *) buf; + for (vq = 0; vq < cpu->sve_max_vq; vq++) { + env->vfp.zregs[reg].d[vq * 2 + 1] = *p++; + env->vfp.zregs[reg].d[vq * 2] = *p++; + len += 16; + } + return len; + } + case 32: + vfp_set_fpsr(env, *(uint32_t *)buf); + return 4; + case 33: + vfp_set_fpcr(env, *(uint32_t *)buf); + return 4; + case 34 ... 50: + { + int preg = reg - 34; + int vq, len = 0; + uint64_t *p = (uint64_t *) buf; + for (vq = 0; vq < cpu->sve_max_vq; vq = vq + 4) { + env->vfp.pregs[preg].p[vq / 4] = *p++; + len += 8; + } + return len; + } + case 51: + /* cannot set vg via gdbstub */ + return 0; + default: + /* gdbstub asked for something out our range */ + break; + } + + return 0; +} +#endif /* TARGET_AARCH64 */ + static bool raw_accessors_invalid(const ARMCPRegInfo *ri) { /* Return true if the regdef would cause an assertion if you called @@ -6599,6 +6697,7 @@ static uint64_t id_pfr1_read(CPUARMState *env, const ARMCPRegInfo *ri) return pfr1; } +#ifndef CONFIG_USER_ONLY static uint64_t id_aa64pfr0_read(CPUARMState *env, const ARMCPRegInfo *ri) { ARMCPU *cpu = env_archcpu(env); @@ -6609,6 +6708,7 @@ static uint64_t id_aa64pfr0_read(CPUARMState *env, const ARMCPRegInfo *ri) } return pfr0; } +#endif /* Shared logic between LORID and the rest of the LOR* registers. * Secure state has already been delt with. @@ -7182,16 +7282,24 @@ void register_cp_regs_for_features(ARMCPU *cpu) * define new registers here. */ ARMCPRegInfo v8_idregs[] = { - /* ID_AA64PFR0_EL1 is not a plain ARM_CP_CONST because we don't - * know the right value for the GIC field until after we - * define these regs. + /* + * ID_AA64PFR0_EL1 is not a plain ARM_CP_CONST in system + * emulation because we don't know the right value for the + * GIC field until after we define these regs. */ { .name = "ID_AA64PFR0_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 0, - .access = PL1_R, .type = ARM_CP_NO_RAW, + .access = PL1_R, +#ifdef CONFIG_USER_ONLY + .type = ARM_CP_CONST, + .resetvalue = cpu->isar.id_aa64pfr0 +#else + .type = ARM_CP_NO_RAW, .accessfn = access_aa64_tid3, .readfn = id_aa64pfr0_read, - .writefn = arm_cp_write_ignore }, + .writefn = arm_cp_write_ignore +#endif + }, { .name = "ID_AA64PFR1_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, @@ -7966,9 +8074,22 @@ void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu) CPUARMState *env = &cpu->env; if (arm_feature(env, ARM_FEATURE_AARCH64)) { - gdb_register_coprocessor(cs, aarch64_fpu_gdb_get_reg, - aarch64_fpu_gdb_set_reg, - 34, "aarch64-fpu.xml", 0); + /* + * The lower part of each SVE register aliases to the FPU + * registers so we don't need to include both. + */ +#ifdef TARGET_AARCH64 + if (isar_feature_aa64_sve(&cpu->isar)) { + gdb_register_coprocessor(cs, arm_gdb_get_svereg, arm_gdb_set_svereg, + arm_gen_dynamic_svereg_xml(cs, cs->gdb_num_regs), + "sve-registers.xml", 0); + } else +#endif + { + gdb_register_coprocessor(cs, aarch64_fpu_gdb_get_reg, + aarch64_fpu_gdb_set_reg, + 34, "aarch64-fpu.xml", 0); + } } else if (arm_feature(env, ARM_FEATURE_NEON)) { gdb_register_coprocessor(cs, vfp_gdb_get_reg, vfp_gdb_set_reg, 51, "arm-neon.xml", 0); @@ -7980,8 +8101,9 @@ void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu) 19, "arm-vfp.xml", 0); } gdb_register_coprocessor(cs, arm_gdb_get_sysreg, arm_gdb_set_sysreg, - arm_gen_dynamic_xml(cs), + arm_gen_dynamic_sysreg_xml(cs, cs->gdb_num_regs), "system-registers.xml", 0); + } /* Sort alphabetically by type name, except for "any". */ diff --git a/target/arm/monitor.c b/target/arm/monitor.c index c2dc7908de..ea6598c412 100644 --- a/target/arm/monitor.c +++ b/target/arm/monitor.c @@ -206,9 +206,7 @@ CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, return NULL; } } else { - Error *err = NULL; - arm_cpu_finalize_features(ARM_CPU(obj), &err); - assert(err == NULL); + arm_cpu_finalize_features(ARM_CPU(obj), &error_abort); } expansion_info = g_new0(CpuModelExpansionInfo, 1); @@ -221,12 +219,10 @@ CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, while ((name = cpu_model_advertised_features[i++]) != NULL) { ObjectProperty *prop = object_property_find(obj, name, NULL); if (prop) { - Error *err = NULL; QObject *value; assert(prop->get); - value = object_property_get_qobject(obj, name, &err); - assert(!err); + value = object_property_get_qobject(obj, name, &error_abort); qdict_put_obj(qdict_out, name, value); } diff --git a/target/cris/cpu.h b/target/cris/cpu.h index ca240bc788..8f08d7628b 100644 --- a/target/cris/cpu.h +++ b/target/cris/cpu.h @@ -195,8 +195,8 @@ void cris_cpu_dump_state(CPUState *cs, FILE *f, int flags); hwaddr cris_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -int crisv10_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); -int cris_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int crisv10_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); +int cris_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int cris_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); /* you can call this signal handler from your SIGBUS and SIGSEGV diff --git a/target/cris/gdbstub.c b/target/cris/gdbstub.c index a3d76d2e8c..b01b2aa081 100644 --- a/target/cris/gdbstub.c +++ b/target/cris/gdbstub.c @@ -21,7 +21,7 @@ #include "cpu.h" #include "exec/gdbstub.h" -int crisv10_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int crisv10_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { CRISCPU *cpu = CRIS_CPU(cs); CPUCRISState *env = &cpu->env; @@ -53,7 +53,7 @@ int crisv10_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) return 0; } -int cris_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int cris_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { CRISCPU *cpu = CRIS_CPU(cs); CPUCRISState *env = &cpu->env; diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 6713d04f11..801a4fb1ba 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -321,7 +321,7 @@ void cpu_hppa_change_prot_id(CPUHPPAState *env); int cpu_hppa_signal_handler(int host_signum, void *pinfo, void *puc); hwaddr hppa_cpu_get_phys_page_debug(CPUState *cs, vaddr addr); -int hppa_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int hppa_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int hppa_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void hppa_cpu_do_interrupt(CPUState *cpu); bool hppa_cpu_exec_interrupt(CPUState *cpu, int int_req); diff --git a/target/hppa/gdbstub.c b/target/hppa/gdbstub.c index 341888a9da..a6428a2893 100644 --- a/target/hppa/gdbstub.c +++ b/target/hppa/gdbstub.c @@ -21,7 +21,7 @@ #include "cpu.h" #include "exec/gdbstub.h" -int hppa_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int hppa_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { HPPACPU *cpu = HPPA_CPU(cs); CPUHPPAState *env = &cpu->env; diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 7e9e963d78..60d797d594 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1769,7 +1769,7 @@ void x86_cpu_dump_state(CPUState *cs, FILE *f, int flags); hwaddr x86_cpu_get_phys_page_attrs_debug(CPUState *cpu, vaddr addr, MemTxAttrs *attrs); -int x86_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int x86_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int x86_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void x86_cpu_exec_enter(CPUState *cpu); diff --git a/target/i386/gdbstub.c b/target/i386/gdbstub.c index 572ead641c..f3d23b614e 100644 --- a/target/i386/gdbstub.c +++ b/target/i386/gdbstub.c @@ -79,7 +79,7 @@ static const int gpr_map32[8] = { 0, 1, 2, 3, 4, 5, 6, 7 }; #endif -int x86_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int x86_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; @@ -98,26 +98,22 @@ int x86_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) return gdb_get_reg64(mem_buf, env->regs[gpr_map[n]] & 0xffffffffUL); } else { - memset(mem_buf, 0, sizeof(target_ulong)); - return sizeof(target_ulong); + return gdb_get_regl(mem_buf, 0); } } else { return gdb_get_reg32(mem_buf, env->regs[gpr_map32[n]]); } } else if (n >= IDX_FP_REGS && n < IDX_FP_REGS + 8) { -#ifdef USE_X86LDOUBLE - /* FIXME: byteswap float values - after fixing fpregs layout. */ - memcpy(mem_buf, &env->fpregs[n - IDX_FP_REGS], 10); -#else - memset(mem_buf, 0, 10); -#endif - return 10; + floatx80 *fp = (floatx80 *) &env->fpregs[n - IDX_FP_REGS]; + int len = gdb_get_reg64(mem_buf, cpu_to_le64(fp->low)); + len += gdb_get_reg16(mem_buf + len, cpu_to_le16(fp->high)); + return len; } else if (n >= IDX_XMM_REGS && n < IDX_XMM_REGS + CPU_NB_REGS) { n -= IDX_XMM_REGS; if (n < CPU_NB_REGS32 || TARGET_LONG_BITS == 64) { - stq_p(mem_buf, env->xmm_regs[n].ZMM_Q(0)); - stq_p(mem_buf + 8, env->xmm_regs[n].ZMM_Q(1)); - return 16; + return gdb_get_reg128(mem_buf, + env->xmm_regs[n].ZMM_Q(0), + env->xmm_regs[n].ZMM_Q(1)); } } else { switch (n) { @@ -290,10 +286,9 @@ int x86_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) return 4; } } else if (n >= IDX_FP_REGS && n < IDX_FP_REGS + 8) { -#ifdef USE_X86LDOUBLE - /* FIXME: byteswap float values - after fixing fpregs layout. */ - memcpy(&env->fpregs[n - IDX_FP_REGS], mem_buf, 10); -#endif + floatx80 *fp = (floatx80 *) &env->fpregs[n - IDX_FP_REGS]; + fp->low = le64_to_cpu(* (uint64_t *) mem_buf); + fp->high = le16_to_cpu(* (uint16_t *) (mem_buf + 8)); return 10; } else if (n >= IDX_XMM_REGS && n < IDX_XMM_REGS + CPU_NB_REGS) { n -= IDX_XMM_REGS; diff --git a/target/lm32/cpu.h b/target/lm32/cpu.h index 064c6b1267..01d408eb55 100644 --- a/target/lm32/cpu.h +++ b/target/lm32/cpu.h @@ -202,7 +202,7 @@ void lm32_cpu_do_interrupt(CPUState *cpu); bool lm32_cpu_exec_interrupt(CPUState *cs, int int_req); void lm32_cpu_dump_state(CPUState *cpu, FILE *f, int flags); hwaddr lm32_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -int lm32_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int lm32_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int lm32_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); typedef enum { diff --git a/target/lm32/gdbstub.c b/target/lm32/gdbstub.c index 82ede436e1..b6fe12e1d6 100644 --- a/target/lm32/gdbstub.c +++ b/target/lm32/gdbstub.c @@ -22,7 +22,7 @@ #include "exec/gdbstub.h" #include "hw/lm32/lm32_pic.h" -int lm32_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int lm32_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { LM32CPU *cpu = LM32_CPU(cs); CPULM32State *env = &cpu->env; diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h index 3de8e06dfe..521ac67cdd 100644 --- a/target/m68k/cpu.h +++ b/target/m68k/cpu.h @@ -168,7 +168,7 @@ void m68k_cpu_do_interrupt(CPUState *cpu); bool m68k_cpu_exec_interrupt(CPUState *cpu, int int_req); void m68k_cpu_dump_state(CPUState *cpu, FILE *f, int flags); hwaddr m68k_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -int m68k_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int m68k_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int m68k_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void m68k_tcg_init(void); diff --git a/target/m68k/gdbstub.c b/target/m68k/gdbstub.c index fdc96f57ff..eb2d030e14 100644 --- a/target/m68k/gdbstub.c +++ b/target/m68k/gdbstub.c @@ -21,7 +21,7 @@ #include "cpu.h" #include "exec/gdbstub.h" -int m68k_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int m68k_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { M68kCPU *cpu = M68K_CPU(cs); CPUM68KState *env = &cpu->env; diff --git a/target/m68k/helper.c b/target/m68k/helper.c index baf7729af0..014657c637 100644 --- a/target/m68k/helper.c +++ b/target/m68k/helper.c @@ -68,23 +68,19 @@ void m68k_cpu_list(void) g_slist_free(list); } -static int cf_fpu_gdb_get_reg(CPUM68KState *env, uint8_t *mem_buf, int n) +static int cf_fpu_gdb_get_reg(CPUM68KState *env, GByteArray *mem_buf, int n) { if (n < 8) { float_status s; - stfq_p(mem_buf, floatx80_to_float64(env->fregs[n].d, &s)); - return 8; + return gdb_get_reg64(mem_buf, floatx80_to_float64(env->fregs[n].d, &s)); } switch (n) { case 8: /* fpcontrol */ - stl_be_p(mem_buf, env->fpcr); - return 4; + return gdb_get_reg32(mem_buf, env->fpcr); case 9: /* fpstatus */ - stl_be_p(mem_buf, env->fpsr); - return 4; + return gdb_get_reg32(mem_buf, env->fpsr); case 10: /* fpiar, not implemented */ - memset(mem_buf, 0, 4); - return 4; + return gdb_get_reg32(mem_buf, 0); } return 0; } @@ -109,24 +105,21 @@ static int cf_fpu_gdb_set_reg(CPUM68KState *env, uint8_t *mem_buf, int n) return 0; } -static int m68k_fpu_gdb_get_reg(CPUM68KState *env, uint8_t *mem_buf, int n) +static int m68k_fpu_gdb_get_reg(CPUM68KState *env, GByteArray *mem_buf, int n) { if (n < 8) { - stw_be_p(mem_buf, env->fregs[n].l.upper); - memset(mem_buf + 2, 0, 2); - stq_be_p(mem_buf + 4, env->fregs[n].l.lower); - return 12; + int len = gdb_get_reg16(mem_buf, env->fregs[n].l.upper); + len += gdb_get_reg16(mem_buf + len, 0); + len += gdb_get_reg64(mem_buf + len, env->fregs[n].l.lower); + return len; } switch (n) { case 8: /* fpcontrol */ - stl_be_p(mem_buf, env->fpcr); - return 4; + return gdb_get_reg32(mem_buf, env->fpcr); case 9: /* fpstatus */ - stl_be_p(mem_buf, env->fpsr); - return 4; + return gdb_get_reg32(mem_buf, env->fpsr); case 10: /* fpiar, not implemented */ - memset(mem_buf, 0, 4); - return 4; + return gdb_get_reg32(mem_buf, 0); } return 0; } diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h index 32522f606b..1a700a880c 100644 --- a/target/microblaze/cpu.h +++ b/target/microblaze/cpu.h @@ -313,7 +313,7 @@ void mb_cpu_do_interrupt(CPUState *cs); bool mb_cpu_exec_interrupt(CPUState *cs, int int_req); void mb_cpu_dump_state(CPUState *cpu, FILE *f, int flags); hwaddr mb_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -int mb_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int mb_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int mb_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void mb_tcg_init(void); diff --git a/target/microblaze/gdbstub.c b/target/microblaze/gdbstub.c index 30677b6d1f..f41ebf1f33 100644 --- a/target/microblaze/gdbstub.c +++ b/target/microblaze/gdbstub.c @@ -21,7 +21,7 @@ #include "cpu.h" #include "exec/gdbstub.h" -int mb_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int mb_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); CPUMBState *env = &cpu->env; diff --git a/target/mips/gdbstub.c b/target/mips/gdbstub.c index bbb2544939..98f56e660d 100644 --- a/target/mips/gdbstub.c +++ b/target/mips/gdbstub.c @@ -22,7 +22,7 @@ #include "internal.h" #include "exec/gdbstub.h" -int mips_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int mips_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { MIPSCPU *cpu = MIPS_CPU(cs); CPUMIPSState *env = &cpu->env; diff --git a/target/mips/internal.h b/target/mips/internal.h index df55f845ba..1bf274b3ef 100644 --- a/target/mips/internal.h +++ b/target/mips/internal.h @@ -82,7 +82,7 @@ void mips_cpu_do_interrupt(CPUState *cpu); bool mips_cpu_exec_interrupt(CPUState *cpu, int int_req); void mips_cpu_dump_state(CPUState *cpu, FILE *f, int flags); hwaddr mips_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -int mips_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int mips_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int mips_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void mips_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, MMUAccessType access_type, diff --git a/target/nios2/cpu.c b/target/nios2/cpu.c index 0a4075949e..8f7011fcb9 100644 --- a/target/nios2/cpu.c +++ b/target/nios2/cpu.c @@ -124,7 +124,7 @@ static void nios2_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) #endif } -static int nios2_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +static int nios2_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { Nios2CPU *cpu = NIOS2_CPU(cs); CPUClass *cc = CPU_GET_CLASS(cs); diff --git a/target/openrisc/cpu.h b/target/openrisc/cpu.h index e7fb06445e..f37a52e153 100644 --- a/target/openrisc/cpu.h +++ b/target/openrisc/cpu.h @@ -320,7 +320,7 @@ void openrisc_cpu_do_interrupt(CPUState *cpu); bool openrisc_cpu_exec_interrupt(CPUState *cpu, int int_req); void openrisc_cpu_dump_state(CPUState *cpu, FILE *f, int flags); hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -int openrisc_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int openrisc_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int openrisc_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void openrisc_translate_init(void); bool openrisc_cpu_tlb_fill(CPUState *cs, vaddr address, int size, diff --git a/target/openrisc/gdbstub.c b/target/openrisc/gdbstub.c index 0fcdb79668..095bf76c12 100644 --- a/target/openrisc/gdbstub.c +++ b/target/openrisc/gdbstub.c @@ -21,7 +21,7 @@ #include "cpu.h" #include "exec/gdbstub.h" -int openrisc_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int openrisc_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { OpenRISCCPU *cpu = OPENRISC_CPU(cs); CPUOpenRISCState *env = &cpu->env; diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h index b528131761..000c7d405b 100644 --- a/target/ppc/cpu-qom.h +++ b/target/ppc/cpu-qom.h @@ -177,6 +177,7 @@ typedef struct PowerPCCPUClass { uint64_t insns_flags; uint64_t insns_flags2; uint64_t msr_mask; + uint64_t lpcr_mask; /* Available bits in the LPCR */ uint64_t lpcr_pm; /* Power-saving mode Exit Cause Enable bits */ powerpc_mmu_t mmu_model; powerpc_excp_t excp_model; diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index b283042515..88d9449555 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -24,8 +24,6 @@ #include "exec/cpu-defs.h" #include "cpu-qom.h" -/* #define PPC_EMULATE_32BITS_HYPV */ - #define TCG_GUEST_DEFAULT_MO 0 #define TARGET_PAGE_BITS_64K 16 @@ -300,13 +298,12 @@ typedef struct ppc_v3_pate_t { #define MSR_SF 63 /* Sixty-four-bit mode hflags */ #define MSR_TAG 62 /* Tag-active mode (POWERx ?) */ #define MSR_ISF 61 /* Sixty-four-bit interrupt mode on 630 */ -#define MSR_SHV 60 /* hypervisor state hflags */ +#define MSR_HV 60 /* hypervisor state hflags */ #define MSR_TS0 34 /* Transactional state, 2 bits (Book3s) */ #define MSR_TS1 33 #define MSR_TM 32 /* Transactional Memory Available (Book3s) */ #define MSR_CM 31 /* Computation mode for BookE hflags */ #define MSR_ICM 30 /* Interrupt computation mode for BookE */ -#define MSR_THV 29 /* hypervisor state for 32 bits PowerPC hflags */ #define MSR_GS 28 /* guest state for BookE */ #define MSR_UCLE 26 /* User-mode cache lock enable for BookE */ #define MSR_VR 25 /* altivec available x hflags */ @@ -401,10 +398,13 @@ typedef struct ppc_v3_pate_t { #define msr_sf ((env->msr >> MSR_SF) & 1) #define msr_isf ((env->msr >> MSR_ISF) & 1) -#define msr_shv ((env->msr >> MSR_SHV) & 1) +#if defined(TARGET_PPC64) +#define msr_hv ((env->msr >> MSR_HV) & 1) +#else +#define msr_hv (0) +#endif #define msr_cm ((env->msr >> MSR_CM) & 1) #define msr_icm ((env->msr >> MSR_ICM) & 1) -#define msr_thv ((env->msr >> MSR_THV) & 1) #define msr_gs ((env->msr >> MSR_GS) & 1) #define msr_ucle ((env->msr >> MSR_UCLE) & 1) #define msr_vr ((env->msr >> MSR_VR) & 1) @@ -449,16 +449,9 @@ typedef struct ppc_v3_pate_t { /* Hypervisor bit is more specific */ #if defined(TARGET_PPC64) -#define MSR_HVB (1ULL << MSR_SHV) -#define msr_hv msr_shv -#else -#if defined(PPC_EMULATE_32BITS_HYPV) -#define MSR_HVB (1ULL << MSR_THV) -#define msr_hv msr_thv +#define MSR_HVB (1ULL << MSR_HV) #else #define MSR_HVB (0ULL) -#define msr_hv (0) -#endif #endif /* DSISR */ @@ -1051,10 +1044,6 @@ struct CPUPPCState { uint32_t flags; uint64_t insns_flags; uint64_t insns_flags2; -#if defined(TARGET_PPC64) - ppc_slb_t vrma_slb; - target_ulong rmls; -#endif int error_code; uint32_t pending_interrupts; @@ -1218,8 +1207,8 @@ bool ppc_cpu_exec_interrupt(CPUState *cpu, int int_req); void ppc_cpu_dump_state(CPUState *cpu, FILE *f, int flags); void ppc_cpu_dump_statistics(CPUState *cpu, int flags); hwaddr ppc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -int ppc_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); -int ppc_cpu_gdb_read_register_apple(CPUState *cpu, uint8_t *buf, int reg); +int ppc_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); +int ppc_cpu_gdb_read_register_apple(CPUState *cpu, GByteArray *buf, int reg); int ppc_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); int ppc_cpu_gdb_write_register_apple(CPUState *cpu, uint8_t *buf, int reg); #ifndef CONFIG_USER_ONLY @@ -1231,7 +1220,8 @@ int ppc64_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, int ppc32_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, int cpuid, void *opaque); #ifndef CONFIG_USER_ONLY -void ppc_cpu_do_system_reset(CPUState *cs); +void ppc_cpu_do_system_reset(CPUState *cs, target_ulong vector); +void ppc_cpu_do_fwnmi_machine_check(CPUState *cs, target_ulong vector); extern const VMStateDescription vmstate_ppc_cpu; #endif diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 027f54c0ed..08bc885ca6 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -128,6 +128,37 @@ static uint64_t ppc_excp_vector_offset(CPUState *cs, int ail) return offset; } +static inline void powerpc_set_excp_state(PowerPCCPU *cpu, + target_ulong vector, target_ulong msr) +{ + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + + /* + * We don't use hreg_store_msr here as already have treated any + * special case that could occur. Just store MSR and update hflags + * + * Note: We *MUST* not use hreg_store_msr() as-is anyway because it + * will prevent setting of the HV bit which some exceptions might need + * to do. + */ + env->msr = msr & env->msr_mask; + hreg_compute_hflags(env); + env->nip = vector; + /* Reset exception state */ + cs->exception_index = POWERPC_EXCP_NONE; + env->error_code = 0; + + /* Reset the reservation */ + env->reserve_addr = -1; + + /* + * Any interrupt is context synchronizing, check if TCG TLB needs + * a delayed flush on ppc64 + */ + check_tlb_flush(env, false); +} + /* * Note that this function should be greatly optimized when called * with a constant excp, from ppc_hw_interrupt @@ -768,29 +799,8 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) } } #endif - /* - * We don't use hreg_store_msr here as already have treated any - * special case that could occur. Just store MSR and update hflags - * - * Note: We *MUST* not use hreg_store_msr() as-is anyway because it - * will prevent setting of the HV bit which some exceptions might need - * to do. - */ - env->msr = new_msr & env->msr_mask; - hreg_compute_hflags(env); - env->nip = vector; - /* Reset exception state */ - cs->exception_index = POWERPC_EXCP_NONE; - env->error_code = 0; - /* Reset the reservation */ - env->reserve_addr = -1; - - /* - * Any interrupt is context synchronizing, check if TCG TLB needs - * a delayed flush on ppc64 - */ - check_tlb_flush(env, false); + powerpc_set_excp_state(cpu, vector, new_msr); } void ppc_cpu_do_interrupt(CPUState *cs) @@ -951,12 +961,35 @@ static void ppc_hw_interrupt(CPUPPCState *env) } } -void ppc_cpu_do_system_reset(CPUState *cs) +void ppc_cpu_do_system_reset(CPUState *cs, target_ulong vector) { PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *env = &cpu->env; powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_RESET); + if (vector != -1) { + env->nip = vector; + } +} + +void ppc_cpu_do_fwnmi_machine_check(CPUState *cs, target_ulong vector) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + target_ulong msr = 0; + + /* + * Set MSR and NIP for the handler, SRR0/1, DAR and DSISR have already + * been set by KVM. + */ + msr = (1ULL << MSR_ME); + msr |= env->msr & (1ULL << MSR_SF); + if (!(*pcc->interrupts_big_endian)(cpu)) { + msr |= (1ULL << MSR_LE); + } + + powerpc_set_excp_state(cpu, vector, msr); } #endif /* !CONFIG_USER_ONLY */ diff --git a/target/ppc/gdbstub.c b/target/ppc/gdbstub.c index 823759c92e..eb362dd9ae 100644 --- a/target/ppc/gdbstub.c +++ b/target/ppc/gdbstub.c @@ -114,10 +114,11 @@ void ppc_maybe_bswap_register(CPUPPCState *env, uint8_t *mem_buf, int len) * the FP regs zero size when talking to a newer gdb. */ -int ppc_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int ppc_cpu_gdb_read_register(CPUState *cs, GByteArray *buf, int n) { PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *env = &cpu->env; + uint8_t *mem_buf; int r = ppc_gdb_register_len(n); if (!r) { @@ -126,17 +127,17 @@ int ppc_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) if (n < 32) { /* gprs */ - gdb_get_regl(mem_buf, env->gpr[n]); + gdb_get_regl(buf, env->gpr[n]); } else if (n < 64) { /* fprs */ - stfq_p(mem_buf, *cpu_fpr_ptr(env, n - 32)); + gdb_get_reg64(buf, *cpu_fpr_ptr(env, n - 32)); } else { switch (n) { case 64: - gdb_get_regl(mem_buf, env->nip); + gdb_get_regl(buf, env->nip); break; case 65: - gdb_get_regl(mem_buf, env->msr); + gdb_get_regl(buf, env->msr); break; case 66: { @@ -145,31 +146,33 @@ int ppc_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) for (i = 0; i < 8; i++) { cr |= env->crf[i] << (32 - ((i + 1) * 4)); } - gdb_get_reg32(mem_buf, cr); + gdb_get_reg32(buf, cr); break; } case 67: - gdb_get_regl(mem_buf, env->lr); + gdb_get_regl(buf, env->lr); break; case 68: - gdb_get_regl(mem_buf, env->ctr); + gdb_get_regl(buf, env->ctr); break; case 69: - gdb_get_reg32(mem_buf, env->xer); + gdb_get_reg32(buf, env->xer); break; case 70: - gdb_get_reg32(mem_buf, env->fpscr); + gdb_get_reg32(buf, env->fpscr); break; } } + mem_buf = buf->data + buf->len - r; ppc_maybe_bswap_register(env, mem_buf, r); return r; } -int ppc_cpu_gdb_read_register_apple(CPUState *cs, uint8_t *mem_buf, int n) +int ppc_cpu_gdb_read_register_apple(CPUState *cs, GByteArray *buf, int n) { PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *env = &cpu->env; + uint8_t *mem_buf; int r = ppc_gdb_register_len_apple(n); if (!r) { @@ -178,21 +181,21 @@ int ppc_cpu_gdb_read_register_apple(CPUState *cs, uint8_t *mem_buf, int n) if (n < 32) { /* gprs */ - gdb_get_reg64(mem_buf, env->gpr[n]); + gdb_get_reg64(buf, env->gpr[n]); } else if (n < 64) { /* fprs */ - stfq_p(mem_buf, *cpu_fpr_ptr(env, n - 32)); + gdb_get_reg64(buf, *cpu_fpr_ptr(env, n - 32)); } else if (n < 96) { /* Altivec */ - stq_p(mem_buf, n - 64); - stq_p(mem_buf + 8, 0); + gdb_get_reg64(buf, n - 64); + gdb_get_reg64(buf, 0); } else { switch (n) { case 64 + 32: - gdb_get_reg64(mem_buf, env->nip); + gdb_get_reg64(buf, env->nip); break; case 65 + 32: - gdb_get_reg64(mem_buf, env->msr); + gdb_get_reg64(buf, env->msr); break; case 66 + 32: { @@ -201,23 +204,24 @@ int ppc_cpu_gdb_read_register_apple(CPUState *cs, uint8_t *mem_buf, int n) for (i = 0; i < 8; i++) { cr |= env->crf[i] << (32 - ((i + 1) * 4)); } - gdb_get_reg32(mem_buf, cr); + gdb_get_reg32(buf, cr); break; } case 67 + 32: - gdb_get_reg64(mem_buf, env->lr); + gdb_get_reg64(buf, env->lr); break; case 68 + 32: - gdb_get_reg64(mem_buf, env->ctr); + gdb_get_reg64(buf, env->ctr); break; case 69 + 32: - gdb_get_reg32(mem_buf, env->xer); + gdb_get_reg32(buf, env->xer); break; case 70 + 32: - gdb_get_reg64(mem_buf, env->fpscr); + gdb_get_reg64(buf, env->fpscr); break; } } + mem_buf = buf->data + buf->len - r; ppc_maybe_bswap_register(env, mem_buf, r); return r; } diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 7f44b1aa1a..597f72be1b 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -2113,7 +2113,7 @@ void kvmppc_error_append_smt_possible_hint(Error *const *errp) #ifdef TARGET_PPC64 -uint64_t kvmppc_rma_size(uint64_t current_size, unsigned int hash_shift) +uint64_t kvmppc_vrma_limit(unsigned int hash_shift) { struct kvm_ppc_smmu_info info; long rampagesize, best_page_shift; @@ -2140,8 +2140,7 @@ uint64_t kvmppc_rma_size(uint64_t current_size, unsigned int hash_shift) } } - return MIN(current_size, - 1ULL << (best_page_shift + hash_shift - 7)); + return 1ULL << (best_page_shift + hash_shift - 7); } #endif diff --git a/target/ppc/kvm_ppc.h b/target/ppc/kvm_ppc.h index 9e4f2357cc..332fa0aa1c 100644 --- a/target/ppc/kvm_ppc.h +++ b/target/ppc/kvm_ppc.h @@ -47,7 +47,7 @@ void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t page_shift, int *pfd, bool need_vfio); int kvmppc_remove_spapr_tce(void *table, int pfd, uint32_t window_size); int kvmppc_reset_htab(int shift_hint); -uint64_t kvmppc_rma_size(uint64_t current_size, unsigned int hash_shift); +uint64_t kvmppc_vrma_limit(unsigned int hash_shift); bool kvmppc_has_cap_spapr_vfio(void); #endif /* !CONFIG_USER_ONLY */ bool kvmppc_has_cap_epr(void); @@ -255,10 +255,9 @@ static inline int kvmppc_reset_htab(int shift_hint) return 0; } -static inline uint64_t kvmppc_rma_size(uint64_t current_size, - unsigned int hash_shift) +static inline uint64_t kvmppc_vrma_limit(unsigned int hash_shift) { - return ram_size; + g_assert_not_reached(); } static inline bool kvmppc_hpt_needs_host_contiguous_pages(void) diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index da8966ccf5..34f6009b1e 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -18,6 +18,7 @@ * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "cpu.h" #include "exec/exec-all.h" #include "exec/helper-proto.h" @@ -668,6 +669,21 @@ unsigned ppc_hash64_hpte_page_shift_noslb(PowerPCCPU *cpu, return 0; } +static bool ppc_hash64_use_vrma(CPUPPCState *env) +{ + switch (env->mmu_model) { + case POWERPC_MMU_3_00: + /* + * ISAv3.0 (POWER9) always uses VRMA, the VPM0 field and RMOR + * register no longer exist + */ + return true; + + default: + return !!(env->spr[SPR_LPCR] & LPCR_VPM0); + } +} + static void ppc_hash64_set_isi(CPUState *cs, uint64_t error_code) { CPUPPCState *env = &POWERPC_CPU(cs)->env; @@ -676,15 +692,7 @@ static void ppc_hash64_set_isi(CPUState *cs, uint64_t error_code) if (msr_ir) { vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM1); } else { - switch (env->mmu_model) { - case POWERPC_MMU_3_00: - /* Field deprecated in ISAv3.00 - interrupts always go to hyperv */ - vpm = true; - break; - default: - vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM0); - break; - } + vpm = ppc_hash64_use_vrma(env); } if (vpm && !msr_hv) { cs->exception_index = POWERPC_EXCP_HISI; @@ -702,15 +710,7 @@ static void ppc_hash64_set_dsi(CPUState *cs, uint64_t dar, uint64_t dsisr) if (msr_dr) { vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM1); } else { - switch (env->mmu_model) { - case POWERPC_MMU_3_00: - /* Field deprecated in ISAv3.00 - interrupts always go to hyperv */ - vpm = true; - break; - default: - vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM0); - break; - } + vpm = ppc_hash64_use_vrma(env); } if (vpm && !msr_hv) { cs->exception_index = POWERPC_EXCP_HDSI; @@ -758,11 +758,67 @@ static void ppc_hash64_set_c(PowerPCCPU *cpu, hwaddr ptex, uint64_t pte1) stb_phys(CPU(cpu)->as, base + offset, (pte1 & 0xff) | 0x80); } +static target_ulong rmls_limit(PowerPCCPU *cpu) +{ + CPUPPCState *env = &cpu->env; + /* + * In theory the meanings of RMLS values are implementation + * dependent. In practice, this seems to have been the set from + * POWER4+..POWER8, and RMLS is no longer supported in POWER9. + * + * Unsupported values mean the OS has shot itself in the + * foot. Return a 0-sized RMA in this case, which we expect + * to trigger an immediate DSI or ISI + */ + static const target_ulong rma_sizes[16] = { + [0] = 256 * GiB, + [1] = 16 * GiB, + [2] = 1 * GiB, + [3] = 64 * MiB, + [4] = 256 * MiB, + [7] = 128 * MiB, + [8] = 32 * MiB, + }; + target_ulong rmls = (env->spr[SPR_LPCR] & LPCR_RMLS) >> LPCR_RMLS_SHIFT; + + return rma_sizes[rmls]; +} + +static int build_vrma_slbe(PowerPCCPU *cpu, ppc_slb_t *slb) +{ + CPUPPCState *env = &cpu->env; + target_ulong lpcr = env->spr[SPR_LPCR]; + uint32_t vrmasd = (lpcr & LPCR_VRMASD) >> LPCR_VRMASD_SHIFT; + target_ulong vsid = SLB_VSID_VRMA | ((vrmasd << 4) & SLB_VSID_LLP_MASK); + int i; + + for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { + const PPCHash64SegmentPageSizes *sps = &cpu->hash64_opts->sps[i]; + + if (!sps->page_shift) { + break; + } + + if ((vsid & SLB_VSID_LLP_MASK) == sps->slb_enc) { + slb->esid = SLB_ESID_V; + slb->vsid = vsid; + slb->sps = sps; + return 0; + } + } + + error_report("Bad page size encoding in LPCR[VRMASD]; LPCR=0x" + TARGET_FMT_lx"\n", lpcr); + + return -1; +} + int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx, int mmu_idx) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; + ppc_slb_t vrma_slbe; ppc_slb_t *slb; unsigned apshift; hwaddr ptex; @@ -789,27 +845,32 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, */ raddr = eaddr & 0x0FFFFFFFFFFFFFFFULL; - /* In HV mode, add HRMOR if top EA bit is clear */ - if (msr_hv || !env->has_hv_mode) { + if (cpu->vhyp) { + /* + * In virtual hypervisor mode, there's nothing to do: + * EA == GPA == qemu guest address + */ + } else if (msr_hv || !env->has_hv_mode) { + /* In HV mode, add HRMOR if top EA bit is clear */ if (!(eaddr >> 63)) { raddr |= env->spr[SPR_HRMOR]; } - } else { - /* Otherwise, check VPM for RMA vs VRMA */ - if (env->spr[SPR_LPCR] & LPCR_VPM0) { - slb = &env->vrma_slb; - if (slb->sps) { - goto skip_slb_search; - } - /* Not much else to do here */ + } else if (ppc_hash64_use_vrma(env)) { + /* Emulated VRMA mode */ + slb = &vrma_slbe; + if (build_vrma_slbe(cpu, slb) != 0) { + /* Invalid VRMA setup, machine check */ cs->exception_index = POWERPC_EXCP_MCHECK; env->error_code = 0; return 1; - } else if (raddr < env->rmls) { - /* RMA. Check bounds in RMLS */ - raddr |= env->spr[SPR_RMOR]; - } else { - /* The access failed, generate the approriate interrupt */ + } + + goto skip_slb_search; + } else { + target_ulong limit = rmls_limit(cpu); + + /* Emulated old-style RMO mode, bounds check against RMLS */ + if (raddr >= limit) { if (rwx == 2) { ppc_hash64_set_isi(cs, SRR1_PROTFAULT); } else { @@ -821,6 +882,8 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, } return 1; } + + raddr |= env->spr[SPR_RMOR]; } tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, PAGE_READ | PAGE_WRITE | PAGE_EXEC, mmu_idx, @@ -943,6 +1006,7 @@ skip_slb_search: hwaddr ppc_hash64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong addr) { CPUPPCState *env = &cpu->env; + ppc_slb_t vrma_slbe; ppc_slb_t *slb; hwaddr ptex, raddr; ppc_hash_pte64_t pte; @@ -953,22 +1017,29 @@ hwaddr ppc_hash64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong addr) /* In real mode the top 4 effective address bits are ignored */ raddr = addr & 0x0FFFFFFFFFFFFFFFULL; - /* In HV mode, add HRMOR if top EA bit is clear */ - if ((msr_hv || !env->has_hv_mode) && !(addr >> 63)) { + if (cpu->vhyp) { + /* + * In virtual hypervisor mode, there's nothing to do: + * EA == GPA == qemu guest address + */ + return raddr; + } else if ((msr_hv || !env->has_hv_mode) && !(addr >> 63)) { + /* In HV mode, add HRMOR if top EA bit is clear */ return raddr | env->spr[SPR_HRMOR]; - } + } else if (ppc_hash64_use_vrma(env)) { + /* Emulated VRMA mode */ + slb = &vrma_slbe; + if (build_vrma_slbe(cpu, slb) != 0) { + return -1; + } + } else { + target_ulong limit = rmls_limit(cpu); - /* Otherwise, check VPM for RMA vs VRMA */ - if (env->spr[SPR_LPCR] & LPCR_VPM0) { - slb = &env->vrma_slb; - if (!slb->sps) { + /* Emulated old-style RMO mode, bounds check against RMLS */ + if (raddr >= limit) { return -1; } - } else if (raddr < env->rmls) { - /* RMA. Check bounds in RMLS */ return raddr | env->spr[SPR_RMOR]; - } else { - return -1; } } else { slb = slb_lookup(cpu, addr); @@ -997,168 +1068,12 @@ void ppc_hash64_tlb_flush_hpte(PowerPCCPU *cpu, target_ulong ptex, cpu->env.tlb_need_flush = TLB_NEED_GLOBAL_FLUSH | TLB_NEED_LOCAL_FLUSH; } -static void ppc_hash64_update_rmls(PowerPCCPU *cpu) -{ - CPUPPCState *env = &cpu->env; - uint64_t lpcr = env->spr[SPR_LPCR]; - - /* - * This is the full 4 bits encoding of POWER8. Previous - * CPUs only support a subset of these but the filtering - * is done when writing LPCR - */ - switch ((lpcr & LPCR_RMLS) >> LPCR_RMLS_SHIFT) { - case 0x8: /* 32MB */ - env->rmls = 0x2000000ull; - break; - case 0x3: /* 64MB */ - env->rmls = 0x4000000ull; - break; - case 0x7: /* 128MB */ - env->rmls = 0x8000000ull; - break; - case 0x4: /* 256MB */ - env->rmls = 0x10000000ull; - break; - case 0x2: /* 1GB */ - env->rmls = 0x40000000ull; - break; - case 0x1: /* 16GB */ - env->rmls = 0x400000000ull; - break; - default: - /* What to do here ??? */ - env->rmls = 0; - } -} - -static void ppc_hash64_update_vrma(PowerPCCPU *cpu) -{ - CPUPPCState *env = &cpu->env; - const PPCHash64SegmentPageSizes *sps = NULL; - target_ulong esid, vsid, lpcr; - ppc_slb_t *slb = &env->vrma_slb; - uint32_t vrmasd; - int i; - - /* First clear it */ - slb->esid = slb->vsid = 0; - slb->sps = NULL; - - /* Is VRMA enabled ? */ - lpcr = env->spr[SPR_LPCR]; - if (!(lpcr & LPCR_VPM0)) { - return; - } - - /* - * Make one up. Mostly ignore the ESID which will not be needed - * for translation - */ - vsid = SLB_VSID_VRMA; - vrmasd = (lpcr & LPCR_VRMASD) >> LPCR_VRMASD_SHIFT; - vsid |= (vrmasd << 4) & (SLB_VSID_L | SLB_VSID_LP); - esid = SLB_ESID_V; - - for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { - const PPCHash64SegmentPageSizes *sps1 = &cpu->hash64_opts->sps[i]; - - if (!sps1->page_shift) { - break; - } - - if ((vsid & SLB_VSID_LLP_MASK) == sps1->slb_enc) { - sps = sps1; - break; - } - } - - if (!sps) { - error_report("Bad page size encoding esid 0x"TARGET_FMT_lx - " vsid 0x"TARGET_FMT_lx, esid, vsid); - return; - } - - slb->vsid = vsid; - slb->esid = esid; - slb->sps = sps; -} - void ppc_store_lpcr(PowerPCCPU *cpu, target_ulong val) { + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); CPUPPCState *env = &cpu->env; - uint64_t lpcr = 0; - - /* Filter out bits */ - switch (env->mmu_model) { - case POWERPC_MMU_64B: /* 970 */ - if (val & 0x40) { - lpcr |= LPCR_LPES0; - } - if (val & 0x8000000000000000ull) { - lpcr |= LPCR_LPES1; - } - if (val & 0x20) { - lpcr |= (0x4ull << LPCR_RMLS_SHIFT); - } - if (val & 0x4000000000000000ull) { - lpcr |= (0x2ull << LPCR_RMLS_SHIFT); - } - if (val & 0x2000000000000000ull) { - lpcr |= (0x1ull << LPCR_RMLS_SHIFT); - } - env->spr[SPR_RMOR] = ((lpcr >> 41) & 0xffffull) << 26; - /* - * XXX We could also write LPID from HID4 here - * but since we don't tag any translation on it - * it doesn't actually matter - * - * XXX For proper emulation of 970 we also need - * to dig HRMOR out of HID5 - */ - break; - case POWERPC_MMU_2_03: /* P5p */ - lpcr = val & (LPCR_RMLS | LPCR_ILE | - LPCR_LPES0 | LPCR_LPES1 | - LPCR_RMI | LPCR_HDICE); - break; - case POWERPC_MMU_2_06: /* P7 */ - lpcr = val & (LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_DPFD | - LPCR_VRMASD | LPCR_RMLS | LPCR_ILE | - LPCR_P7_PECE0 | LPCR_P7_PECE1 | LPCR_P7_PECE2 | - LPCR_MER | LPCR_TC | - LPCR_LPES0 | LPCR_LPES1 | LPCR_HDICE); - break; - case POWERPC_MMU_2_07: /* P8 */ - lpcr = val & (LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV | - LPCR_DPFD | LPCR_VRMASD | LPCR_RMLS | LPCR_ILE | - LPCR_AIL | LPCR_ONL | LPCR_P8_PECE0 | LPCR_P8_PECE1 | - LPCR_P8_PECE2 | LPCR_P8_PECE3 | LPCR_P8_PECE4 | - LPCR_MER | LPCR_TC | LPCR_LPES0 | LPCR_HDICE); - break; - case POWERPC_MMU_3_00: /* P9 */ - lpcr = val & (LPCR_VPM1 | LPCR_ISL | LPCR_KBV | LPCR_DPFD | - (LPCR_PECE_U_MASK & LPCR_HVEE) | LPCR_ILE | LPCR_AIL | - LPCR_UPRT | LPCR_EVIRT | LPCR_ONL | LPCR_HR | LPCR_LD | - (LPCR_PECE_L_MASK & (LPCR_PDEE | LPCR_HDEE | LPCR_EEE | - LPCR_DEE | LPCR_OEE)) | LPCR_MER | LPCR_GTSE | LPCR_TC | - LPCR_HEIC | LPCR_LPES0 | LPCR_HVICE | LPCR_HDICE); - /* - * If we have a virtual hypervisor, we need to bring back RMLS. It - * doesn't exist on an actual P9 but that's all we know how to - * configure with softmmu at the moment - */ - if (cpu->vhyp) { - lpcr |= (val & LPCR_RMLS); - } - break; - default: - ; - } - env->spr[SPR_LPCR] = lpcr; - ppc_hash64_update_rmls(cpu); - ppc_hash64_update_vrma(cpu); + env->spr[SPR_LPCR] = val & pcc->lpcr_mask; } void helper_store_lpcr(CPUPPCState *env, target_ulong val) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 36fa27367c..127c82a24e 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -1938,15 +1938,17 @@ static void gen_rlwinm(DisasContext *ctx) me += 32; #endif mask = MASK(mb, me); - if (sh == 0) { - tcg_gen_andi_tl(t_ra, t_rs, mask); - } else if (mask <= 0xffffffffu) { - TCGv_i32 t0 = tcg_temp_new_i32(); - tcg_gen_trunc_tl_i32(t0, t_rs); - tcg_gen_rotli_i32(t0, t0, sh); - tcg_gen_andi_i32(t0, t0, mask); - tcg_gen_extu_i32_tl(t_ra, t0); - tcg_temp_free_i32(t0); + if (mask <= 0xffffffffu) { + if (sh == 0) { + tcg_gen_andi_tl(t_ra, t_rs, mask); + } else { + TCGv_i32 t0 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(t0, t_rs); + tcg_gen_rotli_i32(t0, t0, sh); + tcg_gen_andi_i32(t0, t0, mask); + tcg_gen_extu_i32_tl(t_ra, t0); + tcg_temp_free_i32(t0); + } } else { #if defined(TARGET_PPC64) tcg_gen_deposit_i64(t_ra, t_rs, t_rs, 32, 32); diff --git a/target/ppc/translate_init.inc.c b/target/ppc/translate_init.inc.c index 7e9780e875..e853164a86 100644 --- a/target/ppc/translate_init.inc.c +++ b/target/ppc/translate_init.inc.c @@ -7895,25 +7895,21 @@ static void spr_write_lpcr(DisasContext *ctx, int sprn, int gprn) { gen_helper_store_lpcr(cpu_env, cpu_gpr[gprn]); } - -static void spr_write_970_hid4(DisasContext *ctx, int sprn, int gprn) -{ -#if defined(TARGET_PPC64) - spr_write_generic(ctx, sprn, gprn); - gen_helper_store_lpcr(cpu_env, cpu_gpr[gprn]); -#endif -} - #endif /* !defined(CONFIG_USER_ONLY) */ static void gen_spr_970_lpar(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) - /* Logical partitionning */ - /* PPC970: HID4 is effectively the LPCR */ + /* + * PPC970: HID4 covers things later controlled by the LPCR and + * RMOR in later CPUs, but with a different encoding. We only + * support the 970 in "Apple mode" which has all hypervisor + * facilities disabled by strapping, so we can basically just + * ignore it + */ spr_register(env, SPR_970_HID4, "HID4", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_970_hid4, + &spr_read_generic, &spr_write_generic, 0x00000000); #endif } @@ -8019,12 +8015,16 @@ static void gen_spr_book3s_ids(CPUPPCState *env) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, 0x00000000); - spr_register_hv(env, SPR_RMOR, "RMOR", + spr_register_hv(env, SPR_HRMOR, "HRMOR", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, 0x00000000); - spr_register_hv(env, SPR_HRMOR, "HRMOR", +} + +static void gen_spr_rmor(CPUPPCState *env) +{ + spr_register_hv(env, SPR_RMOR, "RMOR", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, @@ -8476,6 +8476,8 @@ POWERPC_FAMILY(POWER5P)(ObjectClass *oc, void *data) (1ull << MSR_DR) | (1ull << MSR_PMM) | (1ull << MSR_RI); + pcc->lpcr_mask = LPCR_RMLS | LPCR_ILE | LPCR_LPES0 | LPCR_LPES1 | + LPCR_RMI | LPCR_HDICE; pcc->mmu_model = POWERPC_MMU_2_03; #if defined(CONFIG_SOFTMMU) pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; @@ -8492,44 +8494,6 @@ POWERPC_FAMILY(POWER5P)(ObjectClass *oc, void *data) pcc->l1_icache_size = 0x10000; } -/* - * The CPU used to have a "compat" property which set the - * compatibility mode PVR. However, this was conceptually broken - it - * only makes sense on the pseries machine type (otherwise the guest - * owns the PCR and can control the compatibility mode itself). It's - * been replaced with the 'max-cpu-compat' property on the pseries - * machine type. For backwards compatibility, pseries specially - * parses the -cpu parameter and converts old compat= parameters into - * the appropriate machine parameters. This stub implementation of - * the parameter catches any uses on explicitly created CPUs. - */ -static void getset_compat_deprecated(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - QNull *null = NULL; - - if (!qtest_enabled()) { - warn_report("CPU 'compat' property is deprecated and has no effect; " - "use max-cpu-compat machine property instead"); - } - visit_type_null(v, name, &null, NULL); - qobject_unref(null); -} - -static const PropertyInfo ppc_compat_deprecated_propinfo = { - .name = "str", - .description = "compatibility mode (deprecated)", - .get = getset_compat_deprecated, - .set = getset_compat_deprecated, -}; -static Property powerpc_servercpu_properties[] = { - { - .name = "compat", - .info = &ppc_compat_deprecated_propinfo, - }, - DEFINE_PROP_END_OF_LIST(), -}; - static void init_proc_POWER7(CPUPPCState *env) { /* Common Registers */ @@ -8539,6 +8503,7 @@ static void init_proc_POWER7(CPUPPCState *env) /* POWER7 Specific Registers */ gen_spr_book3s_ids(env); + gen_spr_rmor(env); gen_spr_amr(env); gen_spr_book3s_purr(env); gen_spr_power5p_common(env); @@ -8611,7 +8576,6 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) dc->fw_name = "PowerPC,POWER7"; dc->desc = "POWER7"; - device_class_set_props(dc, powerpc_servercpu_properties); pcc->pvr_match = ppc_pvr_match_power7; pcc->pcr_mask = PCR_VEC_DIS | PCR_VSX_DIS | PCR_COMPAT_2_05; pcc->pcr_supported = PCR_COMPAT_2_06 | PCR_COMPAT_2_05; @@ -8652,6 +8616,12 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) (1ull << MSR_PMM) | (1ull << MSR_RI) | (1ull << MSR_LE); + pcc->lpcr_mask = LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_DPFD | + LPCR_VRMASD | LPCR_RMLS | LPCR_ILE | + LPCR_P7_PECE0 | LPCR_P7_PECE1 | LPCR_P7_PECE2 | + LPCR_MER | LPCR_TC | + LPCR_LPES0 | LPCR_LPES1 | LPCR_HDICE; + pcc->lpcr_pm = LPCR_P7_PECE0 | LPCR_P7_PECE1 | LPCR_P7_PECE2; pcc->mmu_model = POWERPC_MMU_2_06; #if defined(CONFIG_SOFTMMU) pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; @@ -8668,7 +8638,6 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) pcc->l1_dcache_size = 0x8000; pcc->l1_icache_size = 0x8000; pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr; - pcc->lpcr_pm = LPCR_P7_PECE0 | LPCR_P7_PECE1 | LPCR_P7_PECE2; } static void init_proc_POWER8(CPUPPCState *env) @@ -8680,6 +8649,7 @@ static void init_proc_POWER8(CPUPPCState *env) /* POWER8 Specific Registers */ gen_spr_book3s_ids(env); + gen_spr_rmor(env); gen_spr_amr(env); gen_spr_iamr(env); gen_spr_book3s_purr(env); @@ -8776,7 +8746,6 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) dc->fw_name = "PowerPC,POWER8"; dc->desc = "POWER8"; - device_class_set_props(dc, powerpc_servercpu_properties); pcc->pvr_match = ppc_pvr_match_power8; pcc->pcr_mask = PCR_TM_DIS | PCR_COMPAT_2_06 | PCR_COMPAT_2_05; pcc->pcr_supported = PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_COMPAT_2_05; @@ -8804,7 +8773,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) PPC2_ISA205 | PPC2_ISA207S | PPC2_FP_CVT_S64 | PPC2_TM | PPC2_PM_ISA206; pcc->msr_mask = (1ull << MSR_SF) | - (1ull << MSR_SHV) | + (1ull << MSR_HV) | (1ull << MSR_TM) | (1ull << MSR_VR) | (1ull << MSR_VSX) | @@ -8823,6 +8792,13 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) (1ull << MSR_TS0) | (1ull << MSR_TS1) | (1ull << MSR_LE); + pcc->lpcr_mask = LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV | + LPCR_DPFD | LPCR_VRMASD | LPCR_RMLS | LPCR_ILE | + LPCR_AIL | LPCR_ONL | LPCR_P8_PECE0 | LPCR_P8_PECE1 | + LPCR_P8_PECE2 | LPCR_P8_PECE3 | LPCR_P8_PECE4 | + LPCR_MER | LPCR_TC | LPCR_LPES0 | LPCR_HDICE; + pcc->lpcr_pm = LPCR_P8_PECE0 | LPCR_P8_PECE1 | LPCR_P8_PECE2 | + LPCR_P8_PECE3 | LPCR_P8_PECE4; pcc->mmu_model = POWERPC_MMU_2_07; #if defined(CONFIG_SOFTMMU) pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; @@ -8840,8 +8816,6 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) pcc->l1_dcache_size = 0x8000; pcc->l1_icache_size = 0x8000; pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr; - pcc->lpcr_pm = LPCR_P8_PECE0 | LPCR_P8_PECE1 | LPCR_P8_PECE2 | - LPCR_P8_PECE3 | LPCR_P8_PECE4; } #ifdef CONFIG_SOFTMMU @@ -8988,7 +8962,6 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) dc->fw_name = "PowerPC,POWER9"; dc->desc = "POWER9"; - device_class_set_props(dc, powerpc_servercpu_properties); pcc->pvr_match = ppc_pvr_match_power9; pcc->pcr_mask = PCR_COMPAT_2_05 | PCR_COMPAT_2_06 | PCR_COMPAT_2_07; pcc->pcr_supported = PCR_COMPAT_3_00 | PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | @@ -9017,7 +8990,7 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) PPC2_ISA205 | PPC2_ISA207S | PPC2_FP_CVT_S64 | PPC2_TM | PPC2_ISA300 | PPC2_PRCNTL; pcc->msr_mask = (1ull << MSR_SF) | - (1ull << MSR_SHV) | + (1ull << MSR_HV) | (1ull << MSR_TM) | (1ull << MSR_VR) | (1ull << MSR_VSX) | @@ -9034,6 +9007,14 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) (1ull << MSR_PMM) | (1ull << MSR_RI) | (1ull << MSR_LE); + pcc->lpcr_mask = LPCR_VPM1 | LPCR_ISL | LPCR_KBV | LPCR_DPFD | + (LPCR_PECE_U_MASK & LPCR_HVEE) | LPCR_ILE | LPCR_AIL | + LPCR_UPRT | LPCR_EVIRT | LPCR_ONL | LPCR_HR | LPCR_LD | + (LPCR_PECE_L_MASK & (LPCR_PDEE | LPCR_HDEE | LPCR_EEE | + LPCR_DEE | LPCR_OEE)) + | LPCR_MER | LPCR_GTSE | LPCR_TC | + LPCR_HEIC | LPCR_LPES0 | LPCR_HVICE | LPCR_HDICE; + pcc->lpcr_pm = LPCR_PDEE | LPCR_HDEE | LPCR_EEE | LPCR_DEE | LPCR_OEE; pcc->mmu_model = POWERPC_MMU_3_00; #if defined(CONFIG_SOFTMMU) pcc->handle_mmu_fault = ppc64_v3_handle_mmu_fault; @@ -9053,7 +9034,6 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) pcc->l1_dcache_size = 0x8000; pcc->l1_icache_size = 0x8000; pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr; - pcc->lpcr_pm = LPCR_PDEE | LPCR_HDEE | LPCR_EEE | LPCR_DEE | LPCR_OEE; } #ifdef CONFIG_SOFTMMU @@ -9198,7 +9178,6 @@ POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data) dc->fw_name = "PowerPC,POWER10"; dc->desc = "POWER10"; - device_class_set_props(dc, powerpc_servercpu_properties); pcc->pvr_match = ppc_pvr_match_power10; pcc->pcr_mask = PCR_COMPAT_2_05 | PCR_COMPAT_2_06 | PCR_COMPAT_2_07 | PCR_COMPAT_3_00; @@ -9228,7 +9207,7 @@ POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data) PPC2_ISA205 | PPC2_ISA207S | PPC2_FP_CVT_S64 | PPC2_TM | PPC2_ISA300 | PPC2_PRCNTL; pcc->msr_mask = (1ull << MSR_SF) | - (1ull << MSR_SHV) | + (1ull << MSR_HV) | (1ull << MSR_TM) | (1ull << MSR_VR) | (1ull << MSR_VSX) | @@ -9245,6 +9224,14 @@ POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data) (1ull << MSR_PMM) | (1ull << MSR_RI) | (1ull << MSR_LE); + pcc->lpcr_mask = LPCR_VPM1 | LPCR_ISL | LPCR_KBV | LPCR_DPFD | + (LPCR_PECE_U_MASK & LPCR_HVEE) | LPCR_ILE | LPCR_AIL | + LPCR_UPRT | LPCR_EVIRT | LPCR_ONL | LPCR_HR | LPCR_LD | + (LPCR_PECE_L_MASK & (LPCR_PDEE | LPCR_HDEE | LPCR_EEE | + LPCR_DEE | LPCR_OEE)) + | LPCR_MER | LPCR_GTSE | LPCR_TC | + LPCR_HEIC | LPCR_LPES0 | LPCR_HVICE | LPCR_HDICE; + pcc->lpcr_pm = LPCR_PDEE | LPCR_HDEE | LPCR_EEE | LPCR_DEE | LPCR_OEE; pcc->mmu_model = POWERPC_MMU_3_00; #if defined(CONFIG_SOFTMMU) pcc->handle_mmu_fault = ppc64_v3_handle_mmu_fault; @@ -9263,7 +9250,6 @@ POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data) pcc->l1_dcache_size = 0x8000; pcc->l1_icache_size = 0x8000; pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr; - pcc->lpcr_pm = LPCR_PDEE | LPCR_HDEE | LPCR_EEE | LPCR_DEE | LPCR_OEE; } #if !defined(CONFIG_USER_ONLY) @@ -9857,7 +9843,7 @@ static int gdb_find_spr_idx(CPUPPCState *env, int n) return -1; } -static int gdb_get_spr_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +static int gdb_get_spr_reg(CPUPPCState *env, GByteArray *buf, int n) { int reg; int len; @@ -9868,8 +9854,8 @@ static int gdb_get_spr_reg(CPUPPCState *env, uint8_t *mem_buf, int n) } len = TARGET_LONG_SIZE; - stn_p(mem_buf, len, env->spr[reg]); - ppc_maybe_bswap_register(env, mem_buf, len); + gdb_get_regl(buf, env->spr[reg]); + ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, len), len); return len; } @@ -9891,15 +9877,18 @@ static int gdb_set_spr_reg(CPUPPCState *env, uint8_t *mem_buf, int n) } #endif -static int gdb_get_float_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +static int gdb_get_float_reg(CPUPPCState *env, GByteArray *buf, int n) { + uint8_t *mem_buf; if (n < 32) { - stfq_p(mem_buf, *cpu_fpr_ptr(env, n)); + gdb_get_reg64(buf, *cpu_fpr_ptr(env, n)); + mem_buf = gdb_get_reg_ptr(buf, 8); ppc_maybe_bswap_register(env, mem_buf, 8); return 8; } if (n == 32) { - stl_p(mem_buf, env->fpscr); + gdb_get_reg32(buf, env->fpscr); + mem_buf = gdb_get_reg_ptr(buf, 4); ppc_maybe_bswap_register(env, mem_buf, 4); return 4; } @@ -9921,28 +9910,31 @@ static int gdb_set_float_reg(CPUPPCState *env, uint8_t *mem_buf, int n) return 0; } -static int gdb_get_avr_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +static int gdb_get_avr_reg(CPUPPCState *env, GByteArray *buf, int n) { + uint8_t *mem_buf; + if (n < 32) { ppc_avr_t *avr = cpu_avr_ptr(env, n); if (!avr_need_swap(env)) { - stq_p(mem_buf, avr->u64[0]); - stq_p(mem_buf + 8, avr->u64[1]); + gdb_get_reg128(buf, avr->u64[0] , avr->u64[1]); } else { - stq_p(mem_buf, avr->u64[1]); - stq_p(mem_buf + 8, avr->u64[0]); + gdb_get_reg128(buf, avr->u64[1] , avr->u64[0]); } + mem_buf = gdb_get_reg_ptr(buf, 16); ppc_maybe_bswap_register(env, mem_buf, 8); ppc_maybe_bswap_register(env, mem_buf + 8, 8); return 16; } if (n == 32) { - stl_p(mem_buf, helper_mfvscr(env)); + gdb_get_reg32(buf, helper_mfvscr(env)); + mem_buf = gdb_get_reg_ptr(buf, 4); ppc_maybe_bswap_register(env, mem_buf, 4); return 4; } if (n == 33) { - stl_p(mem_buf, (uint32_t)env->spr[SPR_VRSAVE]); + gdb_get_reg32(buf, (uint32_t)env->spr[SPR_VRSAVE]); + mem_buf = gdb_get_reg_ptr(buf, 4); ppc_maybe_bswap_register(env, mem_buf, 4); return 4; } @@ -9977,25 +9969,25 @@ static int gdb_set_avr_reg(CPUPPCState *env, uint8_t *mem_buf, int n) return 0; } -static int gdb_get_spe_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +static int gdb_get_spe_reg(CPUPPCState *env, GByteArray *buf, int n) { if (n < 32) { #if defined(TARGET_PPC64) - stl_p(mem_buf, env->gpr[n] >> 32); - ppc_maybe_bswap_register(env, mem_buf, 4); + gdb_get_reg32(buf, env->gpr[n] >> 32); + ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, 4), 4); #else - stl_p(mem_buf, env->gprh[n]); + gdb_get_reg32(buf, env->gprh[n]); #endif return 4; } if (n == 32) { - stq_p(mem_buf, env->spe_acc); - ppc_maybe_bswap_register(env, mem_buf, 8); + gdb_get_reg64(buf, env->spe_acc); + ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, 8), 8); return 8; } if (n == 33) { - stl_p(mem_buf, env->spe_fscr); - ppc_maybe_bswap_register(env, mem_buf, 4); + gdb_get_reg32(buf, env->spe_fscr); + ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, 4), 4); return 4; } return 0; @@ -10030,11 +10022,11 @@ static int gdb_set_spe_reg(CPUPPCState *env, uint8_t *mem_buf, int n) return 0; } -static int gdb_get_vsx_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +static int gdb_get_vsx_reg(CPUPPCState *env, GByteArray *buf, int n) { if (n < 32) { - stq_p(mem_buf, *cpu_vsrl_ptr(env, n)); - ppc_maybe_bswap_register(env, mem_buf, 8); + gdb_get_reg64(buf, *cpu_vsrl_ptr(env, n)); + ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, 8), 8); return 8; } return 0; @@ -10486,6 +10478,8 @@ static void ppc_cpu_parse_featurestr(const char *type, char *features, *s = '\0'; for (i = 0; inpieces[i]; i++) { if (g_str_has_prefix(inpieces[i], "compat=")) { + warn_report_once("CPU 'compat' property is deprecated; " + "use max-cpu-compat machine property instead"); compat_str = inpieces[i]; continue; } diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 078fc83959..7d21addbab 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -293,7 +293,7 @@ extern const char * const riscv_excp_names[]; extern const char * const riscv_intr_names[]; void riscv_cpu_do_interrupt(CPUState *cpu); -int riscv_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int riscv_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int riscv_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request); bool riscv_cpu_fp_enabled(CPURISCVState *env); diff --git a/target/riscv/gdbstub.c b/target/riscv/gdbstub.c index 2f32750f2f..eba12a86f2 100644 --- a/target/riscv/gdbstub.c +++ b/target/riscv/gdbstub.c @@ -270,7 +270,7 @@ static int csr_register_map[] = { CSR_MHCOUNTEREN, }; -int riscv_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int riscv_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; @@ -301,14 +301,14 @@ int riscv_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) return 0; } -static int riscv_gdb_get_fpu(CPURISCVState *env, uint8_t *mem_buf, int n) +static int riscv_gdb_get_fpu(CPURISCVState *env, GByteArray *buf, int n) { if (n < 32) { if (env->misa & RVD) { - return gdb_get_reg64(mem_buf, env->fpr[n]); + return gdb_get_reg64(buf, env->fpr[n]); } if (env->misa & RVF) { - return gdb_get_reg32(mem_buf, env->fpr[n]); + return gdb_get_reg32(buf, env->fpr[n]); } /* there is hole between ft11 and fflags in fpu.xml */ } else if (n < 36 && n > 32) { @@ -322,7 +322,7 @@ static int riscv_gdb_get_fpu(CPURISCVState *env, uint8_t *mem_buf, int n) result = riscv_csrrw_debug(env, n - 33 + csr_register_map[8], &val, 0, 0); if (result == 0) { - return gdb_get_regl(mem_buf, val); + return gdb_get_regl(buf, val); } } return 0; @@ -351,7 +351,7 @@ static int riscv_gdb_set_fpu(CPURISCVState *env, uint8_t *mem_buf, int n) return 0; } -static int riscv_gdb_get_csr(CPURISCVState *env, uint8_t *mem_buf, int n) +static int riscv_gdb_get_csr(CPURISCVState *env, GByteArray *buf, int n) { if (n < ARRAY_SIZE(csr_register_map)) { target_ulong val = 0; @@ -359,7 +359,7 @@ static int riscv_gdb_get_csr(CPURISCVState *env, uint8_t *mem_buf, int n) result = riscv_csrrw_debug(env, csr_register_map[n], &val, 0, 0); if (result == 0) { - return gdb_get_regl(mem_buf, val); + return gdb_get_regl(buf, val); } } return 0; @@ -379,13 +379,13 @@ static int riscv_gdb_set_csr(CPURISCVState *env, uint8_t *mem_buf, int n) return 0; } -static int riscv_gdb_get_virtual(CPURISCVState *cs, uint8_t *mem_buf, int n) +static int riscv_gdb_get_virtual(CPURISCVState *cs, GByteArray *buf, int n) { if (n == 0) { #ifdef CONFIG_USER_ONLY - return gdb_get_regl(mem_buf, 0); + return gdb_get_regl(buf, 0); #else - return gdb_get_regl(mem_buf, cs->priv); + return gdb_get_regl(buf, cs->priv); #endif } return 0; diff --git a/target/s390x/gdbstub.c b/target/s390x/gdbstub.c index e24a49f4a9..d6fce5ff1e 100644 --- a/target/s390x/gdbstub.c +++ b/target/s390x/gdbstub.c @@ -27,7 +27,7 @@ #include "sysemu/hw_accel.h" #include "sysemu/tcg.h" -int s390_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int s390_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { S390CPU *cpu = S390_CPU(cs); CPUS390XState *env = &cpu->env; @@ -82,11 +82,11 @@ int s390_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) /* total number of registers in s390-acr.xml */ #define S390_NUM_AC_REGS 16 -static int cpu_read_ac_reg(CPUS390XState *env, uint8_t *mem_buf, int n) +static int cpu_read_ac_reg(CPUS390XState *env, GByteArray *buf, int n) { switch (n) { case S390_A0_REGNUM ... S390_A15_REGNUM: - return gdb_get_reg32(mem_buf, env->aregs[n]); + return gdb_get_reg32(buf, env->aregs[n]); default: return 0; } @@ -111,13 +111,13 @@ static int cpu_write_ac_reg(CPUS390XState *env, uint8_t *mem_buf, int n) /* total number of registers in s390-fpr.xml */ #define S390_NUM_FP_REGS 17 -static int cpu_read_fp_reg(CPUS390XState *env, uint8_t *mem_buf, int n) +static int cpu_read_fp_reg(CPUS390XState *env, GByteArray *buf, int n) { switch (n) { case S390_FPC_REGNUM: - return gdb_get_reg32(mem_buf, env->fpc); + return gdb_get_reg32(buf, env->fpc); case S390_F0_REGNUM ... S390_F15_REGNUM: - return gdb_get_reg64(mem_buf, *get_freg(env, n - S390_F0_REGNUM)); + return gdb_get_reg64(buf, *get_freg(env, n - S390_F0_REGNUM)); default: return 0; } @@ -145,17 +145,17 @@ static int cpu_write_fp_reg(CPUS390XState *env, uint8_t *mem_buf, int n) /* total number of registers in s390-vx.xml */ #define S390_NUM_VREGS 32 -static int cpu_read_vreg(CPUS390XState *env, uint8_t *mem_buf, int n) +static int cpu_read_vreg(CPUS390XState *env, GByteArray *buf, int n) { int ret; switch (n) { case S390_V0L_REGNUM ... S390_V15L_REGNUM: - ret = gdb_get_reg64(mem_buf, env->vregs[n][1]); + ret = gdb_get_reg64(buf, env->vregs[n][1]); break; case S390_V16_REGNUM ... S390_V31_REGNUM: - ret = gdb_get_reg64(mem_buf, env->vregs[n][0]); - ret += gdb_get_reg64(mem_buf + 8, env->vregs[n][1]); + ret = gdb_get_reg64(buf, env->vregs[n][0]); + ret += gdb_get_reg64(buf, env->vregs[n][1]); break; default: ret = 0; @@ -186,11 +186,11 @@ static int cpu_write_vreg(CPUS390XState *env, uint8_t *mem_buf, int n) #define S390_NUM_C_REGS 16 #ifndef CONFIG_USER_ONLY -static int cpu_read_c_reg(CPUS390XState *env, uint8_t *mem_buf, int n) +static int cpu_read_c_reg(CPUS390XState *env, GByteArray *buf, int n) { switch (n) { case S390_C0_REGNUM ... S390_C15_REGNUM: - return gdb_get_regl(mem_buf, env->cregs[n]); + return gdb_get_regl(buf, env->cregs[n]); default: return 0; } @@ -223,7 +223,7 @@ static int cpu_write_c_reg(CPUS390XState *env, uint8_t *mem_buf, int n) /* total number of registers in s390-virt.xml */ #define S390_NUM_VIRT_REGS 8 -static int cpu_read_virt_reg(CPUS390XState *env, uint8_t *mem_buf, int n) +static int cpu_read_virt_reg(CPUS390XState *env, GByteArray *mem_buf, int n) { switch (n) { case S390_VIRT_CKC_REGNUM: @@ -296,9 +296,9 @@ static int cpu_write_virt_reg(CPUS390XState *env, uint8_t *mem_buf, int n) /* total number of registers in s390-gs.xml */ #define S390_NUM_GS_REGS 4 -static int cpu_read_gs_reg(CPUS390XState *env, uint8_t *mem_buf, int n) +static int cpu_read_gs_reg(CPUS390XState *env, GByteArray *buf, int n) { - return gdb_get_regl(mem_buf, env->gscb[n]); + return gdb_get_regl(buf, env->gscb[n]); } static int cpu_write_gs_reg(CPUS390XState *env, uint8_t *mem_buf, int n) diff --git a/target/s390x/internal.h b/target/s390x/internal.h index d37816104d..8c95c734db 100644 --- a/target/s390x/internal.h +++ b/target/s390x/internal.h @@ -292,7 +292,7 @@ uint16_t float128_dcmask(CPUS390XState *env, float128 f1); /* gdbstub.c */ -int s390_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int s390_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int s390_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void s390_cpu_gdb_init(CPUState *cs); diff --git a/target/sh4/cpu.h b/target/sh4/cpu.h index 452a596e67..dbe58c7888 100644 --- a/target/sh4/cpu.h +++ b/target/sh4/cpu.h @@ -208,7 +208,7 @@ void superh_cpu_do_interrupt(CPUState *cpu); bool superh_cpu_exec_interrupt(CPUState *cpu, int int_req); void superh_cpu_dump_state(CPUState *cpu, FILE *f, int flags); hwaddr superh_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -int superh_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int superh_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int superh_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void superh_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, MMUAccessType access_type, diff --git a/target/sh4/gdbstub.c b/target/sh4/gdbstub.c index 44c1679e9d..49fc4a0cc6 100644 --- a/target/sh4/gdbstub.c +++ b/target/sh4/gdbstub.c @@ -24,7 +24,7 @@ /* Hint: Use "set architecture sh4" in GDB to see fpu registers */ /* FIXME: We should use XML for this. */ -int superh_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int superh_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { SuperHCPU *cpu = SUPERH_CPU(cs); CPUSH4State *env = &cpu->env; diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index ae97c7d9f7..b9369398f2 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -571,7 +571,7 @@ extern const VMStateDescription vmstate_sparc_cpu; void sparc_cpu_do_interrupt(CPUState *cpu); void sparc_cpu_dump_state(CPUState *cpu, FILE *f, int flags); hwaddr sparc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -int sparc_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int sparc_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int sparc_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void QEMU_NORETURN sparc_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, MMUAccessType access_type, diff --git a/target/sparc/gdbstub.c b/target/sparc/gdbstub.c index 8be742b5a3..78dc8dcc98 100644 --- a/target/sparc/gdbstub.c +++ b/target/sparc/gdbstub.c @@ -27,7 +27,7 @@ #define gdb_get_rega(buf, val) gdb_get_regl(buf, val) #endif -int sparc_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int sparc_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { SPARCCPU *cpu = SPARC_CPU(cs); CPUSPARCState *env = &cpu->env; diff --git a/target/xtensa/cpu.h b/target/xtensa/cpu.h index 493f4fc80c..c0d69fad96 100644 --- a/target/xtensa/cpu.h +++ b/target/xtensa/cpu.h @@ -569,7 +569,7 @@ void xtensa_cpu_dump_state(CPUState *cpu, FILE *f, int flags); hwaddr xtensa_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); void xtensa_count_regs(const XtensaConfig *config, unsigned *n_regs, unsigned *n_core_regs); -int xtensa_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int xtensa_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int xtensa_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void xtensa_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, MMUAccessType access_type, diff --git a/target/xtensa/gdbstub.c b/target/xtensa/gdbstub.c index 54727881f3..0ee3feabe5 100644 --- a/target/xtensa/gdbstub.c +++ b/target/xtensa/gdbstub.c @@ -63,7 +63,7 @@ void xtensa_count_regs(const XtensaConfig *config, } } -int xtensa_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int xtensa_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { XtensaCPU *cpu = XTENSA_CPU(cs); CPUXtensaState *env = &cpu->env; diff --git a/tcg/i386/tcg-target.inc.c b/tcg/i386/tcg-target.inc.c index cdedcb2b25..223dba9c8c 100644 --- a/tcg/i386/tcg-target.inc.c +++ b/tcg/i386/tcg-target.inc.c @@ -3391,12 +3391,15 @@ static void expand_vec_sari(TCGType type, unsigned vece, case MO_64: if (imm <= 32) { - /* We can emulate a small sign extend by performing an arithmetic + /* + * We can emulate a small sign extend by performing an arithmetic * 32-bit shift and overwriting the high half of a 64-bit logical - * shift (note that the ISA says shift of 32 is valid). + * shift. Note that the ISA says shift of 32 is valid, but TCG + * does not, so we have to bound the smaller shift -- we get the + * same result in the high half either way. */ t1 = tcg_temp_new_vec(type); - tcg_gen_sari_vec(MO_32, t1, v1, imm); + tcg_gen_sari_vec(MO_32, t1, v1, MIN(imm, 31)); tcg_gen_shri_vec(MO_64, v0, v1, imm); vec_gen_4(INDEX_op_x86_blend_vec, type, MO_32, tcgv_vec_arg(v0), tcgv_vec_arg(v0), diff --git a/tests/.gitignore b/tests/.gitignore index 7306866f21..d03c037d77 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -10,6 +10,7 @@ qht-bench rcutorture test-* !test-*.c +!test-*.py !docker/test-* test-qapi-commands.[ch] test-qapi-init-commands.[ch] diff --git a/tests/Makefile.include b/tests/Makefile.include index 67e8fcddda..51de676298 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -20,6 +20,8 @@ check-help: @echo " $(MAKE) check-venv Creates a Python venv for tests" @echo " $(MAKE) check-clean Clean the tests and related data" @echo + @echo " $(MAKE) get-vm-images Downloads all images used by acceptance tests, according to configured targets (~350 MB each, 1.5 GB max)" + @echo @echo @echo "The variable SPEED can be set to control the gtester speed setting." @echo "Default options are -k and (for $(MAKE) V=1) --verbose; they can be" @@ -242,6 +244,7 @@ qapi-schema += event-case.json qapi-schema += event-member-invalid-dict.json qapi-schema += event-nest-struct.json qapi-schema += features-bad-type.json +qapi-schema += features-deprecated-type.json qapi-schema += features-duplicate-name.json qapi-schema += features-if-invalid.json qapi-schema += features-missing-name.json @@ -889,7 +892,21 @@ $(TESTS_RESULTS_DIR): check-venv: $(TESTS_VENV_DIR) -check-acceptance: check-venv $(TESTS_RESULTS_DIR) +FEDORA_31_ARCHES_CANDIDATES=$(patsubst ppc64,ppc64le,$(TARGETS)) +FEDORA_31_ARCHES := x86_64 aarch64 ppc64le s390x +FEDORA_31_DOWNLOAD=$(filter $(FEDORA_31_ARCHES),$(FEDORA_31_ARCHES_CANDIDATES)) + +# download one specific Fedora 31 image +get-vm-image-fedora-31-%: check-venv + $(call quiet-command, \ + $(TESTS_VENV_DIR)/bin/python -m avocado vmimage get \ + --distro=fedora --distro-version=31 --arch=$*, \ + "AVOCADO", "Downloading acceptance tests VM image for $*") + +# download all vm images, according to defined targets +get-vm-images: check-venv $(patsubst %,get-vm-image-fedora-31-%, $(FEDORA_31_DOWNLOAD)) + +check-acceptance: check-venv $(TESTS_RESULTS_DIR) get-vm-images $(call quiet-command, \ $(TESTS_VENV_DIR)/bin/python -m avocado \ --show=$(AVOCADO_SHOW) run --job-results-dir=$(TESTS_RESULTS_DIR) \ @@ -900,7 +917,7 @@ check-acceptance: check-venv $(TESTS_RESULTS_DIR) # Consolidated targets -.PHONY: check-block check-qapi-schema check-qtest check-unit check check-clean +.PHONY: check-block check-qapi-schema check-qtest check-unit check check-clean get-vm-images check-qapi-schema: check-tests/qapi-schema/frontend check-tests/qapi-schema/doc-good.texi check-qtest: $(patsubst %,check-qtest-%, $(QTEST_TARGETS)) ifeq ($(CONFIG_TOOLS),y) diff --git a/tests/acceptance/avocado_qemu/__init__.py b/tests/acceptance/avocado_qemu/__init__.py index d4358eb431..59e7b4f763 100644 --- a/tests/acceptance/avocado_qemu/__init__.py +++ b/tests/acceptance/avocado_qemu/__init__.py @@ -16,8 +16,21 @@ import tempfile import avocado -SRC_ROOT_DIR = os.path.join(os.path.dirname(__file__), '..', '..', '..') -sys.path.append(os.path.join(SRC_ROOT_DIR, 'python')) +#: The QEMU build root directory. It may also be the source directory +#: if building from the source dir, but it's safer to use BUILD_DIR for +#: that purpose. Be aware that if this code is moved outside of a source +#: and build tree, it will not be accurate. +BUILD_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) + +if os.path.islink(os.path.dirname(os.path.dirname(__file__))): + # The link to the acceptance tests dir in the source code directory + lnk = os.path.dirname(os.path.dirname(__file__)) + #: The QEMU root source directory + SOURCE_DIR = os.path.dirname(os.path.dirname(os.readlink(lnk))) +else: + SOURCE_DIR = BUILD_DIR + +sys.path.append(os.path.join(SOURCE_DIR, 'python')) from qemu.machine import QEMUMachine @@ -49,10 +62,10 @@ def pick_default_qemu_bin(arch=None): if is_readable_executable_file(qemu_bin_relative_path): return qemu_bin_relative_path - qemu_bin_from_src_dir_path = os.path.join(SRC_ROOT_DIR, + qemu_bin_from_bld_dir_path = os.path.join(BUILD_DIR, qemu_bin_relative_path) - if is_readable_executable_file(qemu_bin_from_src_dir_path): - return qemu_bin_from_src_dir_path + if is_readable_executable_file(qemu_bin_from_bld_dir_path): + return qemu_bin_from_bld_dir_path def _console_interaction(test, success_message, failure_message, @@ -153,7 +166,7 @@ class Test(avocado.Test): self.qemu_bin = self.params.get('qemu_bin', default=default_qemu_bin) if self.qemu_bin is None: - self.cancel("No QEMU binary defined or found in the source tree") + self.cancel("No QEMU binary defined or found in the build tree") def _new_vm(self, *args): vm = QEMUMachine(self.qemu_bin, sock_dir=tempfile.mkdtemp()) diff --git a/tests/acceptance/boot_linux.py b/tests/acceptance/boot_linux.py new file mode 100644 index 0000000000..075a386300 --- /dev/null +++ b/tests/acceptance/boot_linux.py @@ -0,0 +1,222 @@ +# Functional test that boots a complete Linux system via a cloud image +# +# Copyright (c) 2018-2020 Red Hat, Inc. +# +# Author: +# Cleber Rosa <crosa@redhat.com> +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import os + +from avocado_qemu import Test, BUILD_DIR + +from qemu.accel import kvm_available +from qemu.accel import tcg_available + +from avocado.utils import cloudinit +from avocado.utils import network +from avocado.utils import vmimage +from avocado.utils import datadrainer +from avocado.utils.path import find_command + +ACCEL_NOT_AVAILABLE_FMT = "%s accelerator does not seem to be available" +KVM_NOT_AVAILABLE = ACCEL_NOT_AVAILABLE_FMT % "KVM" +TCG_NOT_AVAILABLE = ACCEL_NOT_AVAILABLE_FMT % "TCG" + + +class BootLinux(Test): + """ + Boots a Linux system, checking for a successful initialization + """ + + timeout = 900 + chksum = None + + def setUp(self): + super(BootLinux, self).setUp() + self.vm.add_args('-smp', '2') + self.vm.add_args('-m', '1024') + self.prepare_boot() + self.prepare_cloudinit() + + def prepare_boot(self): + self.log.debug('Looking for and selecting a qemu-img binary to be ' + 'used to create the bootable snapshot image') + # If qemu-img has been built, use it, otherwise the system wide one + # will be used. If none is available, the test will cancel. + qemu_img = os.path.join(BUILD_DIR, 'qemu-img') + if not os.path.exists(qemu_img): + qemu_img = find_command('qemu-img', False) + if qemu_img is False: + self.cancel('Could not find "qemu-img", which is required to ' + 'create the bootable image') + vmimage.QEMU_IMG = qemu_img + + self.log.info('Downloading/preparing boot image') + # Fedora 31 only provides ppc64le images + image_arch = self.arch + if image_arch == 'ppc64': + image_arch = 'ppc64le' + try: + self.boot = vmimage.get( + 'fedora', arch=image_arch, version='31', + checksum=self.chksum, + algorithm='sha256', + cache_dir=self.cache_dirs[0], + snapshot_dir=self.workdir) + self.vm.add_args('-drive', 'file=%s' % self.boot.path) + except: + self.cancel('Failed to download/prepare boot image') + + def prepare_cloudinit(self): + self.log.info('Preparing cloudinit image') + try: + cloudinit_iso = os.path.join(self.workdir, 'cloudinit.iso') + self.phone_home_port = network.find_free_port() + cloudinit.iso(cloudinit_iso, self.name, + username='root', + password='password', + # QEMU's hard coded usermode router address + phone_home_host='10.0.2.2', + phone_home_port=self.phone_home_port) + self.vm.add_args('-drive', 'file=%s,format=raw' % cloudinit_iso) + except Exception: + self.cancel('Failed to prepared cloudinit image') + + def launch_and_wait(self): + self.vm.set_console() + self.vm.launch() + console_drainer = datadrainer.LineLogger(self.vm.console_socket.fileno(), + logger=self.log.getChild('console')) + console_drainer.start() + self.log.info('VM launched, waiting for boot confirmation from guest') + cloudinit.wait_for_phone_home(('0.0.0.0', self.phone_home_port), self.name) + + +class BootLinuxX8664(BootLinux): + """ + :avocado: tags=arch:x86_64 + """ + + chksum = 'e3c1b309d9203604922d6e255c2c5d098a309c2d46215d8fc026954f3c5c27a0' + + def test_pc_i440fx_tcg(self): + """ + :avocado: tags=machine:pc + :avocado: tags=accel:tcg + """ + if not tcg_available(self.qemu_bin): + self.cancel(TCG_NOT_AVAILABLE) + self.vm.add_args("-accel", "tcg") + self.launch_and_wait() + + def test_pc_i440fx_kvm(self): + """ + :avocado: tags=machine:pc + :avocado: tags=accel:kvm + """ + if not kvm_available(self.arch, self.qemu_bin): + self.cancel(KVM_NOT_AVAILABLE) + self.vm.add_args("-accel", "kvm") + self.launch_and_wait() + + def test_pc_q35_tcg(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=accel:tcg + """ + if not tcg_available(self.qemu_bin): + self.cancel(TCG_NOT_AVAILABLE) + self.vm.add_args("-accel", "tcg") + self.launch_and_wait() + + def test_pc_q35_kvm(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=accel:kvm + """ + if not kvm_available(self.arch, self.qemu_bin): + self.cancel(KVM_NOT_AVAILABLE) + self.vm.add_args("-accel", "kvm") + self.launch_and_wait() + + +class BootLinuxAarch64(BootLinux): + """ + :avocado: tags=arch:aarch64 + :avocado: tags=machine:virt + :avocado: tags=machine:gic-version=2 + """ + + chksum = '1e18d9c0cf734940c4b5d5ec592facaed2af0ad0329383d5639c997fdf16fe49' + + def add_common_args(self): + self.vm.add_args('-bios', + os.path.join(BUILD_DIR, 'pc-bios', + 'edk2-aarch64-code.fd')) + self.vm.add_args('-device', 'virtio-rng-pci,rng=rng0') + self.vm.add_args('-object', 'rng-random,id=rng0,filename=/dev/urandom') + + def test_virt_tcg(self): + """ + :avocado: tags=accel:tcg + :avocado: tags=cpu:max + """ + if not tcg_available(self.qemu_bin): + self.cancel(TCG_NOT_AVAILABLE) + self.vm.add_args("-accel", "tcg") + self.vm.add_args("-cpu", "max") + self.vm.add_args("-machine", "virt,gic-version=2") + self.add_common_args() + self.launch_and_wait() + + def test_virt_kvm(self): + """ + :avocado: tags=accel:kvm + :avocado: tags=cpu:host + """ + if not kvm_available(self.arch, self.qemu_bin): + self.cancel(KVM_NOT_AVAILABLE) + self.vm.add_args("-accel", "kvm") + self.vm.add_args("-cpu", "host") + self.vm.add_args("-machine", "virt,gic-version=2") + self.add_common_args() + self.launch_and_wait() + + +class BootLinuxPPC64(BootLinux): + """ + :avocado: tags=arch:ppc64 + """ + + chksum = '7c3528b85a3df4b2306e892199a9e1e43f991c506f2cc390dc4efa2026ad2f58' + + def test_pseries_tcg(self): + """ + :avocado: tags=machine:pseries + :avocado: tags=accel:tcg + """ + if not tcg_available(self.qemu_bin): + self.cancel(TCG_NOT_AVAILABLE) + self.vm.add_args("-accel", "tcg") + self.launch_and_wait() + + +class BootLinuxS390X(BootLinux): + """ + :avocado: tags=arch:s390x + """ + + chksum = '4caaab5a434fd4d1079149a072fdc7891e354f834d355069ca982fdcaf5a122d' + + def test_s390_ccw_virtio_tcg(self): + """ + :avocado: tags=machine:s390-ccw-virtio + :avocado: tags=accel:tcg + """ + if not tcg_available(self.qemu_bin): + self.cancel(TCG_NOT_AVAILABLE) + self.vm.add_args("-accel", "tcg") + self.launch_and_wait() diff --git a/tests/docker/dockerfiles/centos8.docker b/tests/docker/dockerfiles/centos8.docker new file mode 100644 index 0000000000..bfa0d33c9c --- /dev/null +++ b/tests/docker/dockerfiles/centos8.docker @@ -0,0 +1,32 @@ +FROM centos:8.1.1911 + +RUN dnf -y update +ENV PACKAGES \ + SDL-devel \ + bison \ + bzip2 \ + bzip2-devel \ + dbus-daemon \ + flex \ + gcc \ + gcc-c++ \ + gettext \ + git \ + glib2-devel \ + libaio-devel \ + libepoxy-devel \ + lzo-devel \ + make \ + mesa-libEGL-devel \ + nettle-devel \ + perl-Test-Harness \ + pixman-devel \ + python36 \ + rdma-core-devel \ + spice-glib-devel \ + spice-server \ + tar \ + zlib-devel + +RUN dnf install -y $PACKAGES +RUN rpm -q $PACKAGES | sort > /packages.txt diff --git a/tests/docker/dockerfiles/debian-amd64.docker b/tests/docker/dockerfiles/debian-amd64.docker index 0456fc7a0c..d4849f509f 100644 --- a/tests/docker/dockerfiles/debian-amd64.docker +++ b/tests/docker/dockerfiles/debian-amd64.docker @@ -28,9 +28,9 @@ RUN apt update && \ libegl1-mesa-dev \ libepoxy-dev \ libgbm-dev -RUN git clone https://anongit.freedesktop.org/git/virglrenderer.git /usr/src/virglrenderer && \ - cd /usr/src/virglrenderer && git checkout virglrenderer-0.7.0 -RUN cd /usr/src/virglrenderer && ./autogen.sh && ./configure --with-glx --disable-tests && make install +RUN git clone https://gitlab.freedesktop.org/virgl/virglrenderer.git /usr/src/virglrenderer && \ + cd /usr/src/virglrenderer && git checkout virglrenderer-0.8.0 +RUN cd /usr/src/virglrenderer && ./autogen.sh && ./configure --disable-tests && make install # netmap RUN apt update && \ diff --git a/tests/docker/dockerfiles/debian-win32-cross.docker b/tests/docker/dockerfiles/debian-win32-cross.docker index 9d7053e59d..d16d6431bc 100644 --- a/tests/docker/dockerfiles/debian-win32-cross.docker +++ b/tests/docker/dockerfiles/debian-win32-cross.docker @@ -9,7 +9,7 @@ MAINTAINER Philippe Mathieu-Daudé <f4bug@amsat.org> ENV TARGET i686 -ENV PATH $PATH:/usr/lib/mxe/usr/$TARGET-w64-mingw32.shared/bin +ENV PATH $PATH:/usr/lib/mxe/usr/bin:/usr/lib/mxe/usr/$TARGET-w64-mingw32.shared/bin ENV PKG_CONFIG_PATH \ $PKG_CONFIG_PATH:/usr/lib/mxe/usr/$TARGET-w64-mingw32.shared/lib/pkgconfig diff --git a/tests/docker/dockerfiles/debian10.docker b/tests/docker/dockerfiles/debian10.docker index 5de79ae552..2fcdc406e8 100644 --- a/tests/docker/dockerfiles/debian10.docker +++ b/tests/docker/dockerfiles/debian10.docker @@ -17,14 +17,17 @@ RUN apt update && \ DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata && \ DEBIAN_FRONTEND=noninteractive eatmydata \ apt install -y --no-install-recommends \ + bc \ bison \ build-essential \ ca-certificates \ clang \ dbus \ flex \ + gdb-multiarch \ gettext \ git \ + libncurses5-dev \ pkg-config \ psmisc \ python3 \ diff --git a/tests/docker/dockerfiles/debian9.docker b/tests/docker/dockerfiles/debian9.docker index 8cbd742bb5..92edbbf0f4 100644 --- a/tests/docker/dockerfiles/debian9.docker +++ b/tests/docker/dockerfiles/debian9.docker @@ -17,13 +17,16 @@ RUN apt update && \ DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata && \ DEBIAN_FRONTEND=noninteractive eatmydata \ apt install -y --no-install-recommends \ + bc \ bison \ build-essential \ ca-certificates \ clang \ flex \ + gdb-multiarch \ gettext \ git \ + libncurses5-dev \ pkg-config \ psmisc \ python3 \ diff --git a/tests/guest-debug/run-test.py b/tests/guest-debug/run-test.py new file mode 100755 index 0000000000..8c49ee2f22 --- /dev/null +++ b/tests/guest-debug/run-test.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +# +# Run a gdbstub test case +# +# Copyright (c) 2019 Linaro +# +# Author: Alex Bennée <alex.bennee@linaro.org> +# +# This work is licensed under the terms of the GNU GPL, version 2 or later. +# See the COPYING file in the top-level directory. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import argparse +import subprocess +import shutil +import shlex + +def get_args(): + parser = argparse.ArgumentParser(description="A gdbstub test runner") + parser.add_argument("--qemu", help="Qemu binary for test", + required=True) + parser.add_argument("--qargs", help="Qemu arguments for test") + parser.add_argument("--binary", help="Binary to debug", + required=True) + parser.add_argument("--test", help="GDB test script", + required=True) + parser.add_argument("--gdb", help="The gdb binary to use", default=None) + + return parser.parse_args() + +if __name__ == '__main__': + args = get_args() + + # Search for a gdb we can use + if not args.gdb: + args.gdb = shutil.which("gdb-multiarch") + if not args.gdb: + args.gdb = shutil.which("gdb") + if not args.gdb: + print("We need gdb to run the test") + exit(-1) + + # Launch QEMU with binary + if "system" in args.qemu: + cmd = "%s %s %s -s -S" % (args.qemu, args.qargs, args.binary) + else: + cmd = "%s %s -g 1234 %s" % (args.qemu, args.qargs, args.binary) + + inferior = subprocess.Popen(shlex.split(cmd)) + + # Now launch gdb with our test and collect the result + gdb_cmd = "%s %s -ex 'target remote localhost:1234' -x %s" % (args.gdb, args.binary, args.test) + + result = subprocess.call(gdb_cmd, shell=True); + + exit(result) diff --git a/tests/qapi-schema/alternate-base.err b/tests/qapi-schema/alternate-base.err index 31ebe56bbf..970a08ab26 100644 --- a/tests/qapi-schema/alternate-base.err +++ b/tests/qapi-schema/alternate-base.err @@ -1,3 +1,3 @@ alternate-base.json: In alternate 'Alt': alternate-base.json:4: alternate has unknown key 'base' -Valid keys are 'alternate', 'data', 'if'. +Valid keys are 'alternate', 'data', 'features', 'if'. diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json index d992e713d9..ddd89d1233 100644 --- a/tests/qapi-schema/doc-good.json +++ b/tests/qapi-schema/doc-good.json @@ -53,10 +53,14 @@ # @Enum: # @one: The _one_ {and only} # +# Features: +# @enum-feat: Also _one_ {and only} +# # @two is undocumented ## { 'enum': 'Enum', 'data': [ { 'name': 'one', 'if': 'defined(IFONE)' }, 'two' ], + 'features': [ 'enum-feat' ], 'if': 'defined(IFCOND)' } ## @@ -74,10 +78,13 @@ # # Features: # @variant1-feat: a feature +# @member-feat: a member feature ## { 'struct': 'Variant1', 'features': [ 'variant1-feat' ], - 'data': { 'var1': { 'type': 'str', 'if': 'defined(IFSTR)' } } } + 'data': { 'var1': { 'type': 'str', + 'features': [ 'member-feat' ], + 'if': 'defined(IFSTR)' } } } ## # @Variant2: @@ -86,24 +93,34 @@ ## # @Object: +# Features: +# @union-feat1: a feature ## { 'union': 'Object', + 'features': [ 'union-feat1' ], 'base': 'Base', 'discriminator': 'base1', 'data': { 'one': 'Variant1', 'two': { 'type': 'Variant2', 'if': 'IFTWO' } } } ## # @SugaredUnion: +# Features: +# @union-feat2: a feature ## { 'union': 'SugaredUnion', + 'features': [ 'union-feat2' ], 'data': { 'one': 'Variant1', 'two': { 'type': 'Variant2', 'if': 'IFTWO' } } } ## # @Alternate: # @i: an integer # @b is undocumented +# +# Features: +# @alt-feat: a feature ## { 'alternate': 'Alternate', + 'features': [ 'alt-feat' ], 'data': { 'i': 'int', 'b': 'bool' } } ## @@ -160,6 +177,9 @@ ## # @EVT-BOXED: +# Features: +# @feat3: a feature ## { 'event': 'EVT-BOXED', 'boxed': true, + 'features': [ 'feat3' ], 'data': 'Object' } diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out index 4c9406a464..6757dd26a2 100644 --- a/tests/qapi-schema/doc-good.out +++ b/tests/qapi-schema/doc-good.out @@ -15,11 +15,13 @@ enum Enum if ['defined(IFONE)'] member two if ['defined(IFCOND)'] + feature enum-feat object Base member base1: Enum optional=False object Variant1 member var1: str optional=False if ['defined(IFSTR)'] + feature member-feat feature variant1-feat object Variant2 object Object @@ -28,6 +30,7 @@ object Object case one: Variant1 case two: Variant2 if ['IFTWO'] + feature union-feat1 object q_obj_Variant1-wrapper member data: Variant1 optional=False object q_obj_Variant2-wrapper @@ -42,10 +45,12 @@ object SugaredUnion case one: q_obj_Variant1-wrapper case two: q_obj_Variant2-wrapper if ['IFTWO'] + feature union-feat2 alternate Alternate tag type case i: int case b: bool + feature alt-feat object q_obj_cmd-arg member arg1: int optional=False member arg2: str optional=True @@ -60,6 +65,7 @@ command cmd-boxed Object -> None feature cmd-feat2 event EVT-BOXED Object boxed=True + feature feat3 doc freeform body= = Section @@ -112,6 +118,8 @@ doc symbol=Enum The _one_ {and only} arg=two + feature=enum-feat +Also _one_ {and only} section=None @two is undocumented doc symbol=Base @@ -128,17 +136,23 @@ Another paragraph (but no @var: line) feature=variant1-feat a feature + feature=member-feat +a member feature doc symbol=Variant2 body= doc symbol=Object body= + feature=union-feat1 +a feature doc symbol=SugaredUnion body= arg=type + feature=union-feat2 +a feature doc symbol=Alternate body= @@ -147,6 +161,8 @@ an integer @b is undocumented arg=b + feature=alt-feat +a feature doc freeform body= == Another subsection @@ -197,3 +213,5 @@ another feature doc symbol=EVT-BOXED body= + feature=feat3 +a feature diff --git a/tests/qapi-schema/doc-good.texi b/tests/qapi-schema/doc-good.texi index d4b15dabf0..7f28fb7a0f 100644 --- a/tests/qapi-schema/doc-good.texi +++ b/tests/qapi-schema/doc-good.texi @@ -88,6 +88,12 @@ The @emph{one} @{and only@} @item @code{two} Not documented @end table + +@b{Features:} +@table @asis +@item @code{enum-feat} +Also @emph{one} @{and only@} +@end table @code{two} is undocumented @b{If:} @code{defined(IFCOND)} @@ -126,6 +132,8 @@ Not documented @table @asis @item @code{variant1-feat} a feature +@item @code{member-feat} +a member feature @end table @end deftp @@ -151,6 +159,12 @@ a feature @item The members of @code{Variant2} when @code{base1} is @t{"two"} (@b{If:} @code{IFTWO}) @end table +@b{Features:} +@table @asis +@item @code{union-feat1} +a feature +@end table + @end deftp @@ -167,6 +181,12 @@ One of @t{"one"}, @t{"two"} @item @code{data: Variant2} when @code{type} is @t{"two"} (@b{If:} @code{IFTWO}) @end table +@b{Features:} +@table @asis +@item @code{union-feat2} +a feature +@end table + @end deftp @@ -184,6 +204,12 @@ an integer Not documented @end table +@b{Features:} +@table @asis +@item @code{alt-feat} +a feature +@end table + @end deftp @@ -283,5 +309,11 @@ another feature @b{Arguments:} the members of @code{Object} +@b{Features:} +@table @asis +@item @code{feat3} +a feature +@end table + @end deftypefn diff --git a/tests/qapi-schema/features-deprecated-type.err b/tests/qapi-schema/features-deprecated-type.err new file mode 100644 index 0000000000..af4ffe20aa --- /dev/null +++ b/tests/qapi-schema/features-deprecated-type.err @@ -0,0 +1,2 @@ +features-deprecated-type.json: In struct 'S': +features-deprecated-type.json:2: feature 'deprecated' is not supported for types diff --git a/tests/qapi-schema/features-deprecated-type.json b/tests/qapi-schema/features-deprecated-type.json new file mode 100644 index 0000000000..4b5bf5b86e --- /dev/null +++ b/tests/qapi-schema/features-deprecated-type.json @@ -0,0 +1,3 @@ +# Feature 'deprecated' is not supported for types +{ 'struct': 'S', 'data': {}, + 'features': [ 'deprecated' ] } diff --git a/tests/qapi-schema/features-deprecated-type.out b/tests/qapi-schema/features-deprecated-type.out new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/qapi-schema/features-deprecated-type.out diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index 9abf175fe0..6b1f05afa7 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -252,13 +252,13 @@ 'bar': { 'type': ['TestIfEnum'], 'if': 'defined(TEST_IF_EVT_BAR)' } }, 'if': 'defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)' } -# test 'features' for structs +# test 'features' { 'struct': 'FeatureStruct0', 'data': { 'foo': 'int' }, 'features': [] } { 'struct': 'FeatureStruct1', - 'data': { 'foo': 'int' }, + 'data': { 'foo': { 'type': 'int', 'features': [ 'deprecated' ] } }, 'features': [ 'feature1' ] } { 'struct': 'FeatureStruct2', 'data': { 'foo': 'int' }, @@ -281,7 +281,22 @@ 'data': { 'foo': 'int' }, 'features': [ { 'name': 'feature1', 'if': [ 'defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)'] } ] } -{ 'command': 'test-features', + +{ 'enum': 'FeatureEnum1', + 'data': [ 'eins', 'zwei', 'drei' ], + 'features': [ 'feature1' ] } + +{ 'union': 'FeatureUnion1', + 'base': { 'tag': 'FeatureEnum1' }, + 'discriminator': 'tag', + 'data': { 'eins': 'FeatureStruct1' }, + 'features': [ 'feature1' ] } + +{ 'alternate': 'FeatureAlternate1', + 'data': { 'eins': 'FeatureStruct1' }, + 'features': [ 'feature1' ] } + +{ 'command': 'test-features0', 'data': { 'fs0': 'FeatureStruct0', 'fs1': 'FeatureStruct1', 'fs2': 'FeatureStruct2', @@ -289,14 +304,11 @@ 'fs4': 'FeatureStruct4', 'cfs1': 'CondFeatureStruct1', 'cfs2': 'CondFeatureStruct2', - 'cfs3': 'CondFeatureStruct3' } } - -# test 'features' for command - -{ 'command': 'test-command-features0', + 'cfs3': 'CondFeatureStruct3' }, 'features': [] } + { 'command': 'test-command-features1', - 'features': [ 'feature1' ] } + 'features': [ 'deprecated' ] } { 'command': 'test-command-features3', 'features': [ 'feature1', 'feature2' ] } @@ -308,3 +320,6 @@ { 'command': 'test-command-cond-features3', 'features': [ { 'name': 'feature1', 'if': [ 'defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)'] } ] } + +{ 'event': 'TEST-EVENT-FEATURES1', + 'features': [ 'deprecated' ] } diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index 9bd3c4a490..891b4101e0 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -359,6 +359,7 @@ object FeatureStruct0 member foo: int optional=False object FeatureStruct1 member foo: int optional=False + feature deprecated feature feature1 object FeatureStruct2 member foo: int optional=False @@ -387,7 +388,25 @@ object CondFeatureStruct3 member foo: int optional=False feature feature1 if ['defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)'] -object q_obj_test-features-arg +enum FeatureEnum1 + member eins + member zwei + member drei + feature feature1 +object q_obj_FeatureUnion1-base + member tag: FeatureEnum1 optional=False +object FeatureUnion1 + base q_obj_FeatureUnion1-base + tag tag + case eins: FeatureStruct1 + case zwei: q_empty + case drei: q_empty + feature feature1 +alternate FeatureAlternate1 + tag type + case eins: FeatureStruct1 + feature feature1 +object q_obj_test-features0-arg member fs0: FeatureStruct0 optional=False member fs1: FeatureStruct1 optional=False member fs2: FeatureStruct2 optional=False @@ -396,13 +415,11 @@ object q_obj_test-features-arg member cfs1: CondFeatureStruct1 optional=False member cfs2: CondFeatureStruct2 optional=False member cfs3: CondFeatureStruct3 optional=False -command test-features q_obj_test-features-arg -> None - gen=True success_response=True boxed=False oob=False preconfig=False -command test-command-features0 None -> None +command test-features0 q_obj_test-features0-arg -> None gen=True success_response=True boxed=False oob=False preconfig=False command test-command-features1 None -> None gen=True success_response=True boxed=False oob=False preconfig=False - feature feature1 + feature deprecated command test-command-features3 None -> None gen=True success_response=True boxed=False oob=False preconfig=False feature feature1 @@ -421,6 +438,9 @@ command test-command-cond-features3 None -> None gen=True success_response=True boxed=False oob=False preconfig=False feature feature1 if ['defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)'] +event TEST-EVENT-FEATURES1 None + boxed=False + feature deprecated module include/sub-module.json include sub-sub-module.json object SecondArrayRef diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index bee18ee344..f396b471eb 100755 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -30,7 +30,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): def visit_include(self, name, info): print('include %s' % name) - def visit_enum_type(self, name, info, ifcond, members, prefix): + def visit_enum_type(self, name, info, ifcond, features, members, prefix): print('enum %s' % name) if prefix: print(' prefix %s' % prefix) @@ -38,6 +38,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): print(' member %s' % m.name) self._print_if(m.ifcond, indent=8) self._print_if(ifcond) + self._print_features(features) def visit_array_type(self, name, info, ifcond, element_type): if not info: @@ -45,8 +46,8 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): print('array %s %s' % (name, element_type.name)) self._print_if(ifcond) - def visit_object_type(self, name, info, ifcond, base, members, variants, - features): + def visit_object_type(self, name, info, ifcond, features, + base, members, variants): print('object %s' % name) if base: print(' base %s' % base.name) @@ -54,18 +55,20 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): print(' member %s: %s optional=%s' % (m.name, m.type.name, m.optional)) self._print_if(m.ifcond, 8) + self._print_features(m.features, indent=8) self._print_variants(variants) self._print_if(ifcond) self._print_features(features) - def visit_alternate_type(self, name, info, ifcond, variants): + def visit_alternate_type(self, name, info, ifcond, features, variants): print('alternate %s' % name) self._print_variants(variants) self._print_if(ifcond) + self._print_features(features) - def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, - success_response, boxed, allow_oob, allow_preconfig, - features): + def visit_command(self, name, info, ifcond, features, + arg_type, ret_type, gen, success_response, boxed, + allow_oob, allow_preconfig): print('command %s %s -> %s' % (name, arg_type and arg_type.name, ret_type and ret_type.name)) @@ -74,10 +77,11 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): self._print_if(ifcond) self._print_features(features) - def visit_event(self, name, info, ifcond, arg_type, boxed): + def visit_event(self, name, info, ifcond, features, arg_type, boxed): print('event %s %s' % (name, arg_type and arg_type.name)) print(' boxed=%s' % boxed) self._print_if(ifcond) + self._print_features(features) @staticmethod def _print_variants(variants): @@ -93,11 +97,11 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): print('%sif %s' % (' ' * indent, ifcond)) @classmethod - def _print_features(cls, features): + def _print_features(cls, features, indent=4): if features: for f in features: - print(' feature %s' % f.name) - cls._print_if(f.ifcond, 8) + print('%sfeature %s' % (' ' * indent, f.name)) + cls._print_if(f.ifcond, indent + 4) def test_frontend(fname): diff --git a/tests/qtest/fuzz/qos_fuzz.c b/tests/qtest/fuzz/qos_fuzz.c index 1a99277d60..af28c92866 100644 --- a/tests/qtest/fuzz/qos_fuzz.c +++ b/tests/qtest/fuzz/qos_fuzz.c @@ -55,10 +55,8 @@ static void qos_set_machines_devices_available(void) QObject *response; QDict *args = qdict_new(); QList *lst; - Error *err = NULL; - qmp_marshal_query_machines(NULL, &response, &err); - assert(!err); + qmp_marshal_query_machines(NULL, &response, &error_abort); lst = qobject_to(QList, response); apply_to_qlist(lst, true); @@ -70,8 +68,7 @@ static void qos_set_machines_devices_available(void) qdict_put_bool(args, "abstract", true); qdict_put_obj(req, "arguments", (QObject *) args); - qmp_marshal_qom_list_types(args, &response, &err); - assert(!err); + qmp_marshal_qom_list_types(args, &response, &error_abort); lst = qobject_to(QList, response); apply_to_qlist(lst, false); qobject_unref(response); diff --git a/tests/qtest/hd-geo-test.c b/tests/qtest/hd-geo-test.c index a249800544..48e8e02d6e 100644 --- a/tests/qtest/hd-geo-test.c +++ b/tests/qtest/hd-geo-test.c @@ -421,7 +421,7 @@ static char *create_qcow2_with_mbr(MBRpartitions mbr, uint64_t sectors) char *raw_path = strdup(template); char *qcow2_path = strdup(template); char cmd[100 + 2 * PATH_MAX]; - uint8_t buf[512]; + uint8_t buf[512] = {}; int i, ret, fd, offset; uint64_t qcow2_size = sectors * 512; uint8_t status, parttype, head, sector, cyl; @@ -457,8 +457,8 @@ static char *create_qcow2_with_mbr(MBRpartitions mbr, uint64_t sectors) buf[offset + 0x6] = sector; buf[offset + 0x7] = cyl; - (*(uint32_t *)&buf[offset + 0x8]) = cpu_to_le32(mbr[i].start_sect); - (*(uint32_t *)&buf[offset + 0xc]) = cpu_to_le32(mbr[i].nr_sects); + stl_le_p(&buf[offset + 0x8], mbr[i].start_sect); + stl_le_p(&buf[offset + 0xc], mbr[i].nr_sects); offset += 0x10; } diff --git a/tests/qtest/libqos/libqos-spapr.h b/tests/qtest/libqos/libqos-spapr.h index d9c4c22343..49bd72d20b 100644 --- a/tests/qtest/libqos/libqos-spapr.h +++ b/tests/qtest/libqos/libqos-spapr.h @@ -12,7 +12,6 @@ void qtest_spapr_shutdown(QOSState *qs); "cap-cfpc=broken," \ "cap-sbbc=broken," \ "cap-ibs=broken," \ - "cap-ccf-assist=off," \ - "cap-fwnmi-mce=off" + "cap-ccf-assist=off," #endif diff --git a/tests/requirements.txt b/tests/requirements.txt index f4f1736a08..f9c84b4ba1 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -2,3 +2,4 @@ # in the tests/venv Python virtual environment. For more info, # refer to: https://pip.pypa.io/en/stable/user_guide/#id1 avocado-framework==76.0 +pycdlib==1.9.0 diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target index 8ed477d0d5..d99b2a9ece 100644 --- a/tests/tcg/aarch64/Makefile.target +++ b/tests/tcg/aarch64/Makefile.target @@ -42,4 +42,36 @@ run-semiconsole: semiconsole run-plugin-semiconsole-with-%: $(call skip-test, $<, "MANUAL ONLY") +ifneq ($(DOCKER_IMAGE)$(CROSS_CC_HAS_SVE),) +# System Registers Tests +AARCH64_TESTS += sysregs +sysregs: CFLAGS+=-march=armv8.1-a+sve + +# SVE ioctl test +AARCH64_TESTS += sve-ioctls +sve-ioctls: CFLAGS+=-march=armv8.1-a+sve + +ifneq ($(HAVE_GDB_BIN),) +GDB_SCRIPT=$(SRC_PATH)/tests/guest-debug/run-test.py + +AARCH64_TESTS += gdbstub-sysregs gdbstub-sve-ioctls + +.PHONY: gdbstub-sysregs gdbstub-sve-ioctls +run-gdbstub-sysregs: sysregs + $(call run-test, $@, $(GDB_SCRIPT) \ + --gdb $(HAVE_GDB_BIN) \ + --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ + --bin $< --test $(AARCH64_SRC)/gdbstub/test-sve.py, \ + "basic gdbstub SVE support") + +run-gdbstub-sve-ioctls: sve-ioctls + $(call run-test, $@, $(GDB_SCRIPT) \ + --gdb $(HAVE_GDB_BIN) \ + --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ + --bin $< --test $(AARCH64_SRC)/gdbstub/test-sve-ioctl.py, \ + "basic gdbstub SVE ZLEN support") +endif + +endif + TESTS += $(AARCH64_TESTS) diff --git a/tests/tcg/aarch64/gdbstub/test-sve-ioctl.py b/tests/tcg/aarch64/gdbstub/test-sve-ioctl.py new file mode 100644 index 0000000000..984fbeb277 --- /dev/null +++ b/tests/tcg/aarch64/gdbstub/test-sve-ioctl.py @@ -0,0 +1,85 @@ +from __future__ import print_function +# +# Test the SVE ZReg reports the right amount of data. It uses the +# sve-ioctl test and examines the register data each time the +# __sve_ld_done breakpoint is hit. +# +# This is launched via tests/guest-debug/run-test.py +# + +import gdb +import sys + +initial_vlen = 0 +failcount = 0 + +def report(cond, msg): + "Report success/fail of test" + if cond: + print ("PASS: %s" % (msg)) + else: + print ("FAIL: %s" % (msg)) + global failcount + failcount += 1 + +class TestBreakpoint(gdb.Breakpoint): + def __init__(self, sym_name="__sve_ld_done"): + super(TestBreakpoint, self).__init__(sym_name) + # self.sym, ok = gdb.lookup_symbol(sym_name) + + def stop(self): + val_i = gdb.parse_and_eval('i') + global initial_vlen + try: + for i in range(0, int(val_i)): + val_z = gdb.parse_and_eval("$z0.b.u[%d]" % i) + report(int(val_z) == i, "z0.b.u[%d] == %d" % (i, i)) + for i in range(i + 1, initial_vlen): + val_z = gdb.parse_and_eval("$z0.b.u[%d]" % i) + report(int(val_z) == 0, "z0.b.u[%d] == 0" % (i)) + except gdb.error: + report(False, "checking zregs (out of range)") + + +def run_test(): + "Run through the tests one by one" + + print ("Setup breakpoint") + bp = TestBreakpoint() + + global initial_vlen + vg = gdb.parse_and_eval("$vg") + initial_vlen = int(vg) * 8 + + gdb.execute("c") + +# +# This runs as the script it sourced (via -x, via run-test.py) +# +try: + inferior = gdb.selected_inferior() + if inferior.was_attached == False: + print("SKIPPING (failed to attach)", file=sys.stderr) + exit(0) + arch = inferior.architecture() + report(arch.name() == "aarch64", "connected to aarch64") +except (gdb.error, AttributeError): + print("SKIPPING (not connected)", file=sys.stderr) + exit(0) + +try: + # These are not very useful in scripts + gdb.execute("set pagination off") + gdb.execute("set confirm off") + + # Run the actual tests + run_test() +except: + print ("GDB Exception: %s" % (sys.exc_info()[0])) + failcount += 1 + import code + code.InteractiveConsole(locals=globals()).interact() + raise + +print("All tests complete: %d failures" % failcount) +exit(failcount) diff --git a/tests/tcg/aarch64/gdbstub/test-sve.py b/tests/tcg/aarch64/gdbstub/test-sve.py new file mode 100644 index 0000000000..dbe7f2aa93 --- /dev/null +++ b/tests/tcg/aarch64/gdbstub/test-sve.py @@ -0,0 +1,84 @@ +from __future__ import print_function +# +# Test the SVE registers are visable and changeable via gdbstub +# +# This is launched via tests/guest-debug/run-test.py +# + +import gdb +import sys + +MAGIC = 0xDEADBEEF + +failcount = 0 + +def report(cond, msg): + "Report success/fail of test" + if cond: + print ("PASS: %s" % (msg)) + else: + print ("FAIL: %s" % (msg)) + global failcount + failcount += 1 + +def run_test(): + "Run through the tests one by one" + + gdb.execute("info registers") + report(True, "info registers") + + gdb.execute("info registers vector") + report(True, "info registers vector") + + # Now all the zregs + frame = gdb.selected_frame() + for i in range(0, 32): + rname = "z%d" % (i) + zreg = frame.read_register(rname) + report(True, "Reading %s" % rname) + for j in range(0, 4): + cmd = "set $%s.q.u[%d] = 0x%x" % (rname, j, MAGIC) + gdb.execute(cmd) + report(True, "%s" % cmd) + for j in range(0, 4): + reg = "$%s.q.u[%d]" % (rname, j) + v = gdb.parse_and_eval(reg) + report(str(v.type) == "uint128_t", "size of %s" % (reg)) + for j in range(0, 8): + cmd = "set $%s.d.u[%d] = 0x%x" % (rname, j, MAGIC) + gdb.execute(cmd) + report(True, "%s" % cmd) + for j in range(0, 8): + reg = "$%s.d.u[%d]" % (rname, j) + v = gdb.parse_and_eval(reg) + report(str(v.type) == "uint64_t", "size of %s" % (reg)) + report(int(v) == MAGIC, "%s is 0x%x" % (reg, MAGIC)) + +# +# This runs as the script it sourced (via -x, via run-test.py) +# +try: + inferior = gdb.selected_inferior() + if inferior.was_attached == False: + print("SKIPPING (failed to attach)", file=sys.stderr) + exit(0) + arch = inferior.architecture() + report(arch.name() == "aarch64", "connected to aarch64") +except (gdb.error, AttributeError): + print("SKIPPING (not connected)", file=sys.stderr) + exit(0) + +try: + # These are not very useful in scripts + gdb.execute("set pagination off") + gdb.execute("set confirm off") + + # Run the actual tests + run_test() +except: + print ("GDB Exception: %s" % (sys.exc_info()[0])) + failcount += 1 + +print("All tests complete: %d failures" % failcount) + +exit(failcount) diff --git a/tests/tcg/aarch64/sve-ioctls.c b/tests/tcg/aarch64/sve-ioctls.c new file mode 100644 index 0000000000..9544dffa0e --- /dev/null +++ b/tests/tcg/aarch64/sve-ioctls.c @@ -0,0 +1,70 @@ +/* + * SVE ioctls tests + * + * Test the SVE width setting ioctls work and provide a base for + * testing the gdbstub. + * + * Copyright (c) 2019 Linaro Ltd + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include <sys/prctl.h> +#include <asm/hwcap.h> +#include <stdio.h> +#include <sys/auxv.h> +#include <stdint.h> +#include <stdlib.h> + +#ifndef HWCAP_CPUID +#define HWCAP_CPUID (1 << 11) +#endif + +#define SVE_MAX_QUADS (2048 / 128) +#define BYTES_PER_QUAD (128 / 8) + +#define get_cpu_reg(id) ({ \ + unsigned long __val; \ + asm("mrs %0, "#id : "=r" (__val)); \ + __val; \ + }) + +static int do_sve_ioctl_test(void) +{ + int i, res, init_vq; + + res = prctl(PR_SVE_GET_VL, 0, 0, 0, 0); + if (res < 0) { + printf("FAILED to PR_SVE_GET_VL (%d)", res); + return -1; + } + init_vq = res & PR_SVE_VL_LEN_MASK; + + for (i = init_vq; i > 15; i /= 2) { + printf("Checking PR_SVE_SET_VL=%d\n", i); + res = prctl(PR_SVE_SET_VL, i, 0, 0, 0, 0); + if (res < 0) { + printf("FAILED to PR_SVE_SET_VL (%d)", res); + return -1; + } + asm("index z0.b, #0, #1\n" + ".global __sve_ld_done\n" + "__sve_ld_done:\n" + "mov z0.b, #0\n" + : /* no outputs kept */ + : /* no inputs */ + : "memory", "z0"); + } + printf("PASS\n"); + return 0; +} + +int main(int argc, char **argv) +{ + /* we also need to probe for the ioctl support */ + if (getauxval(AT_HWCAP) & HWCAP_SVE) { + return do_sve_ioctl_test(); + } else { + printf("SKIP: no HWCAP_SVE on this system\n"); + return 0; + } +} diff --git a/tests/tcg/aarch64/sysregs.c b/tests/tcg/aarch64/sysregs.c new file mode 100644 index 0000000000..40cf8d2877 --- /dev/null +++ b/tests/tcg/aarch64/sysregs.c @@ -0,0 +1,172 @@ +/* + * Check emulated system register access for linux-user mode. + * + * See: https://www.kernel.org/doc/Documentation/arm64/cpu-feature-registers.txt + * + * Copyright (c) 2019 Linaro + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <asm/hwcap.h> +#include <stdio.h> +#include <sys/auxv.h> +#include <signal.h> +#include <string.h> +#include <stdbool.h> + +#ifndef HWCAP_CPUID +#define HWCAP_CPUID (1 << 11) +#endif + +int failed_bit_count; + +/* Read and print system register `id' value */ +#define get_cpu_reg(id) ({ \ + unsigned long __val = 0xdeadbeef; \ + asm("mrs %0, "#id : "=r" (__val)); \ + printf("%-20s: 0x%016lx\n", #id, __val); \ + __val; \ + }) + +/* As above but also check no bits outside of `mask' are set*/ +#define get_cpu_reg_check_mask(id, mask) ({ \ + unsigned long __cval = get_cpu_reg(id); \ + unsigned long __extra = __cval & ~mask; \ + if (__extra) { \ + printf("%-20s: 0x%016lx\n", " !!extra bits!!", __extra); \ + failed_bit_count++; \ + } \ +}) + +/* As above but check RAZ */ +#define get_cpu_reg_check_zero(id) ({ \ + unsigned long __val = 0xdeadbeef; \ + asm("mrs %0, "#id : "=r" (__val)); \ + if (__val) { \ + printf("%-20s: 0x%016lx (not RAZ!)\n", #id, __val); \ + failed_bit_count++; \ + } \ +}) + +/* Chunk up mask into 63:48, 47:32, 31:16, 15:0 to ease counting */ +#define _m(a, b, c, d) (0x ## a ## b ## c ## d ##ULL) + +bool should_fail; +int should_fail_count; +int should_not_fail_count; +uintptr_t failed_pc[10]; + +void sigill_handler(int signo, siginfo_t *si, void *data) +{ + ucontext_t *uc = (ucontext_t *)data; + + if (should_fail) { + should_fail_count++; + } else { + uintptr_t pc = (uintptr_t) uc->uc_mcontext.pc; + failed_pc[should_not_fail_count++] = pc; + } + uc->uc_mcontext.pc += 4; +} + +int main(void) +{ + struct sigaction sa; + + /* Hook in a SIGILL handler */ + memset(&sa, 0, sizeof(struct sigaction)); + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = &sigill_handler; + sigemptyset(&sa.sa_mask); + + if (sigaction(SIGILL, &sa, 0) != 0) { + perror("sigaction"); + return 1; + } + + /* Counter values have been exposed since Linux 4.12 */ + printf("Checking Counter registers\n"); + + get_cpu_reg(ctr_el0); + get_cpu_reg(cntvct_el0); + get_cpu_reg(cntfrq_el0); + + /* HWCAP_CPUID indicates we can read feature registers, since Linux 4.11 */ + if (!(getauxval(AT_HWCAP) & HWCAP_CPUID)) { + printf("CPUID registers unavailable\n"); + return 1; + } else { + printf("Checking CPUID registers\n"); + } + + /* + * Some registers only expose some bits to user-space. Anything + * that is IMPDEF is exported as 0 to user-space. The _mask checks + * assert no extra bits are set. + * + * This check is *not* comprehensive as some fields are set to + * minimum valid fields - for the purposes of this check allowed + * to have non-zero values. + */ + get_cpu_reg_check_mask(id_aa64isar0_el1, _m(00ff,ffff,f0ff,fff0)); + get_cpu_reg_check_mask(id_aa64isar1_el1, _m(0000,00f0,ffff,ffff)); + /* TGran4 & TGran64 as pegged to -1 */ + get_cpu_reg_check_mask(id_aa64mmfr0_el1, _m(0000,0000,ff00,0000)); + get_cpu_reg_check_zero(id_aa64mmfr1_el1); + /* EL1/EL0 reported as AA64 only */ + get_cpu_reg_check_mask(id_aa64pfr0_el1, _m(000f,000f,00ff,0011)); + get_cpu_reg_check_mask(id_aa64pfr1_el1, _m(0000,0000,0000,00f0)); + /* all hidden, DebugVer fixed to 0x6 (ARMv8 debug architecture) */ + get_cpu_reg_check_mask(id_aa64dfr0_el1, _m(0000,0000,0000,0006)); + get_cpu_reg_check_zero(id_aa64dfr1_el1); + get_cpu_reg_check_zero(id_aa64zfr0_el1); + + get_cpu_reg_check_zero(id_aa64afr0_el1); + get_cpu_reg_check_zero(id_aa64afr1_el1); + + get_cpu_reg_check_mask(midr_el1, _m(0000,0000,ffff,ffff)); + /* mpidr sets bit 31, everything else hidden */ + get_cpu_reg_check_mask(mpidr_el1, _m(0000,0000,8000,0000)); + /* REVIDR is all IMPDEF so should be all zeros to user-space */ + get_cpu_reg_check_zero(revidr_el1); + + /* + * There are a block of more registers that are RAZ in the rest of + * the Op0=3, Op1=0, CRn=0, CRm=0,4,5,6,7 space. However for + * brevity we don't check stuff that is currently un-allocated + * here. Feel free to add them ;-) + */ + + printf("Remaining registers should fail\n"); + should_fail = true; + + /* Unexposed register access causes SIGILL */ + get_cpu_reg(id_mmfr0_el1); + get_cpu_reg(id_mmfr1_el1); + get_cpu_reg(id_mmfr2_el1); + get_cpu_reg(id_mmfr3_el1); + + get_cpu_reg(mvfr0_el1); + get_cpu_reg(mvfr1_el1); + + if (should_not_fail_count > 0) { + int i; + for (i = 0; i < should_not_fail_count; i++) { + uintptr_t pc = failed_pc[i]; + uint32_t insn = *(uint32_t *) pc; + printf("insn %#x @ %#lx unexpected FAIL\n", insn, pc); + } + return 1; + } + + if (failed_bit_count > 0) { + printf("Extra information leaked to user-space!\n"); + return 1; + } + + return should_fail_count == 6 ? 0 : 1; +} diff --git a/tests/test-qmp-cmds.c b/tests/test-qmp-cmds.c index 79507d9e54..d12ff47e26 100644 --- a/tests/test-qmp-cmds.c +++ b/tests/test-qmp-cmds.c @@ -1,5 +1,6 @@ #include "qemu/osdep.h" #include "qapi/qmp/qdict.h" +#include "qapi/qmp/qjson.h" #include "qapi/qmp/qnum.h" #include "qapi/qmp/qstring.h" #include "qapi/error.h" @@ -44,7 +45,7 @@ void qmp_user_def_cmd1(UserDefOne * ud1, Error **errp) { } -void qmp_test_features(FeatureStruct0 *fs0, FeatureStruct1 *fs1, +void qmp_test_features0(FeatureStruct0 *fs0, FeatureStruct1 *fs1, FeatureStruct2 *fs2, FeatureStruct3 *fs3, FeatureStruct4 *fs4, CondFeatureStruct1 *cfs1, CondFeatureStruct2 *cfs2, CondFeatureStruct3 *cfs3, @@ -52,10 +53,6 @@ void qmp_test_features(FeatureStruct0 *fs0, FeatureStruct1 *fs1, { } -void qmp_test_command_features0(Error **errp) -{ -} - void qmp_test_command_features1(Error **errp) { } @@ -145,66 +142,87 @@ __org_qemu_x_Union1 *qmp___org_qemu_x_command(__org_qemu_x_EnumList *a, } -/* test commands with no input and no return value */ -static void test_dispatch_cmd(void) +static QObject *do_qmp_dispatch(bool allow_oob, const char *template, ...) { - QDict *req = qdict_new(); - QDict *resp; + va_list ap; + QDict *req, *resp; + QObject *ret; - qdict_put_str(req, "execute", "user_def_cmd"); + va_start(ap, template); + req = qdict_from_vjsonf_nofail(template, ap); + va_end(ap); - resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false); - assert(resp != NULL); - assert(!qdict_haskey(resp, "error")); + resp = qmp_dispatch(&qmp_commands, QOBJECT(req), allow_oob); + g_assert(resp); + ret = qdict_get(resp, "return"); + g_assert(ret); + g_assert(qdict_size(resp) == 1); + qobject_ref(ret); qobject_unref(resp); qobject_unref(req); + return ret; } -static void test_dispatch_cmd_oob(void) +static void do_qmp_dispatch_error(bool allow_oob, ErrorClass cls, + const char *template, ...) { - QDict *req = qdict_new(); - QDict *resp; - - qdict_put_str(req, "exec-oob", "test-flags-command"); - - resp = qmp_dispatch(&qmp_commands, QOBJECT(req), true); - assert(resp != NULL); - assert(!qdict_haskey(resp, "error")); + va_list ap; + QDict *req, *resp; + QDict *error; + + va_start(ap, template); + req = qdict_from_vjsonf_nofail(template, ap); + va_end(ap); + + resp = qmp_dispatch(&qmp_commands, QOBJECT(req), allow_oob); + g_assert(resp); + error = qdict_get_qdict(resp, "error"); + g_assert(error); + g_assert_cmpstr(qdict_get_try_str(error, "class"), + ==, QapiErrorClass_str(cls)); + g_assert(qdict_get_try_str(error, "desc")); + g_assert(qdict_size(error) == 2); + g_assert(qdict_size(resp) == 1); qobject_unref(resp); qobject_unref(req); } -/* test commands that return an error due to invalid parameters */ -static void test_dispatch_cmd_failure(void) +/* test commands with no input and no return value */ +static void test_dispatch_cmd(void) { - QDict *req = qdict_new(); - QDict *args = qdict_new(); - QDict *resp; - - qdict_put_str(req, "execute", "user_def_cmd2"); - - resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false); - assert(resp != NULL); - assert(qdict_haskey(resp, "error")); - - qobject_unref(resp); - qobject_unref(req); + QDict *ret; - /* check that with extra arguments it throws an error */ - req = qdict_new(); - qdict_put_int(args, "a", 66); - qdict_put(req, "arguments", args); + ret = qobject_to(QDict, + do_qmp_dispatch(false, + "{ 'execute': 'user_def_cmd' }")); + assert(ret && qdict_size(ret) == 0); + qobject_unref(ret); +} - qdict_put_str(req, "execute", "user_def_cmd"); +static void test_dispatch_cmd_oob(void) +{ + QDict *ret; - resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false); - assert(resp != NULL); - assert(qdict_haskey(resp, "error")); + ret = qobject_to(QDict, + do_qmp_dispatch(true, + "{ 'exec-oob': 'test-flags-command' }")); + assert(ret && qdict_size(ret) == 0); + qobject_unref(ret); +} - qobject_unref(resp); - qobject_unref(req); +/* test commands that return an error due to invalid parameters */ +static void test_dispatch_cmd_failure(void) +{ + /* missing arguments */ + do_qmp_dispatch_error(false, ERROR_CLASS_GENERIC_ERROR, + "{ 'execute': 'user_def_cmd2' }"); + + /* extra arguments */ + do_qmp_dispatch_error(false, ERROR_CLASS_GENERIC_ERROR, + "{ 'execute': 'user_def_cmd'," + " 'arguments': { 'a': 66 } }"); } static void test_dispatch_cmd_success_response(void) @@ -218,43 +236,18 @@ static void test_dispatch_cmd_success_response(void) qobject_unref(req); } -static QObject *test_qmp_dispatch(QDict *req) -{ - QDict *resp; - QObject *ret; - - resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false); - assert(resp && !qdict_haskey(resp, "error")); - ret = qdict_get(resp, "return"); - assert(ret); - qobject_ref(ret); - qobject_unref(resp); - return ret; -} - /* test commands that involve both input parameters and return values */ static void test_dispatch_cmd_io(void) { - QDict *req = qdict_new(); - QDict *args = qdict_new(); - QDict *args3 = qdict_new(); - QDict *ud1a = qdict_new(); - QDict *ud1b = qdict_new(); QDict *ret, *ret_dict, *ret_dict_dict, *ret_dict_dict_userdef; QDict *ret_dict_dict2, *ret_dict_dict2_userdef; QNum *ret3; int64_t val; - qdict_put_int(ud1a, "integer", 42); - qdict_put_str(ud1a, "string", "hello"); - qdict_put_int(ud1b, "integer", 422); - qdict_put_str(ud1b, "string", "hello2"); - qdict_put(args, "ud1a", ud1a); - qdict_put(args, "ud1b", ud1b); - qdict_put(req, "arguments", args); - qdict_put_str(req, "execute", "user_def_cmd2"); - - ret = qobject_to(QDict, test_qmp_dispatch(req)); + ret = qobject_to(QDict, do_qmp_dispatch(false, + "{ 'execute': 'user_def_cmd2', 'arguments': {" + " 'ud1a': { 'integer': 42, 'string': 'hello' }," + " 'ud1b': { 'integer': 422, 'string': 'hello2' } } }")); assert(!strcmp(qdict_get_str(ret, "string0"), "blah1")); ret_dict = qdict_get_qdict(ret, "dict1"); @@ -271,16 +264,11 @@ static void test_dispatch_cmd_io(void) assert(!strcmp(qdict_get_str(ret_dict_dict2, "string"), "blah4")); qobject_unref(ret); - qdict_put_int(args3, "a", 66); - qdict_put(req, "arguments", args3); - qdict_put_str(req, "execute", "guest-get-time"); - - ret3 = qobject_to(QNum, test_qmp_dispatch(req)); + ret3 = qobject_to(QNum, do_qmp_dispatch(false, + "{ 'execute': 'guest-get-time', 'arguments': { 'a': 66 } }")); g_assert(qnum_get_try_int(ret3, &val)); g_assert_cmpint(val, ==, 66); qobject_unref(ret3); - - qobject_unref(req); } /* test generated dealloc functions for generated types */ diff --git a/tests/test-qmp-event.c b/tests/test-qmp-event.c index eee7e08ab6..7dd0053190 100644 --- a/tests/test-qmp-event.c +++ b/tests/test-qmp-event.c @@ -17,6 +17,7 @@ #include "qapi/error.h" #include "qapi/qmp/qbool.h" #include "qapi/qmp/qdict.h" +#include "qapi/qmp/qjson.h" #include "qapi/qmp/qnum.h" #include "qapi/qmp/qstring.h" #include "qapi/qmp-event.h" @@ -25,75 +26,12 @@ typedef struct TestEventData { QDict *expect; + bool emitted; } TestEventData; -typedef struct QDictCmpData { - QDict *expect; - bool result; -} QDictCmpData; - TestEventData *test_event_data; static GMutex test_event_lock; -/* Only compares bool, int, string */ -static -void qdict_cmp_do_simple(const char *key, QObject *obj1, void *opaque) - -{ - QObject *obj2; - QDictCmpData d_new, *d = opaque; - int64_t val1, val2; - - if (!d->result) { - return; - } - - obj2 = qdict_get(d->expect, key); - if (!obj2) { - d->result = false; - return; - } - - if (qobject_type(obj1) != qobject_type(obj2)) { - d->result = false; - return; - } - - switch (qobject_type(obj1)) { - case QTYPE_QBOOL: - d->result = (qbool_get_bool(qobject_to(QBool, obj1)) == - qbool_get_bool(qobject_to(QBool, obj2))); - return; - case QTYPE_QNUM: - g_assert(qnum_get_try_int(qobject_to(QNum, obj1), &val1)); - g_assert(qnum_get_try_int(qobject_to(QNum, obj2), &val2)); - d->result = val1 == val2; - return; - case QTYPE_QSTRING: - d->result = g_strcmp0(qstring_get_str(qobject_to(QString, obj1)), - qstring_get_str(qobject_to(QString, obj2))) == 0; - return; - case QTYPE_QDICT: - d_new.expect = qobject_to(QDict, obj2); - d_new.result = true; - qdict_iter(qobject_to(QDict, obj1), qdict_cmp_do_simple, &d_new); - d->result = d_new.result; - return; - default: - abort(); - } -} - -static bool qdict_cmp_simple(QDict *a, QDict *b) -{ - QDictCmpData d; - - d.expect = b; - d.result = true; - qdict_iter(a, qdict_cmp_do_simple, &d); - return d.result; -} - void test_qapi_event_emit(test_QAPIEvent event, QDict *d) { QDict *t; @@ -114,8 +52,8 @@ void test_qapi_event_emit(test_QAPIEvent event, QDict *d) qdict_del(d, "timestamp"); - g_assert(qdict_cmp_simple(d, test_event_data->expect)); - + g_assert(qobject_is_equal(QOBJECT(d), QOBJECT(test_event_data->expect))); + test_event_data->emitted = true; } static void event_prepare(TestEventData *data, @@ -124,17 +62,13 @@ static void event_prepare(TestEventData *data, /* Global variable test_event_data was used to pass the expectation, so test cases can't be executed at same time. */ g_mutex_lock(&test_event_lock); - - data->expect = qdict_new(); test_event_data = data; } static void event_teardown(TestEventData *data, const void *unused) { - qobject_unref(data->expect); test_event_data = NULL; - g_mutex_unlock(&test_event_lock); } @@ -152,90 +86,58 @@ static void event_test_add(const char *testpath, static void test_event_a(TestEventData *data, const void *unused) { - QDict *d; - d = data->expect; - qdict_put_str(d, "event", "EVENT_A"); + data->expect = qdict_from_jsonf_nofail("{ 'event': 'EVENT_A' }"); qapi_event_send_event_a(); + g_assert(data->emitted); + qobject_unref(data->expect); } static void test_event_b(TestEventData *data, const void *unused) { - QDict *d; - d = data->expect; - qdict_put_str(d, "event", "EVENT_B"); + data->expect = qdict_from_jsonf_nofail("{ 'event': 'EVENT_B' }"); qapi_event_send_event_b(); + g_assert(data->emitted); + qobject_unref(data->expect); } static void test_event_c(TestEventData *data, const void *unused) { - QDict *d, *d_data, *d_b; - - UserDefOne b; - b.integer = 2; - b.string = g_strdup("test1"); - b.has_enum1 = false; - - d_b = qdict_new(); - qdict_put_int(d_b, "integer", 2); - qdict_put_str(d_b, "string", "test1"); - - d_data = qdict_new(); - qdict_put_int(d_data, "a", 1); - qdict_put(d_data, "b", d_b); - qdict_put_str(d_data, "c", "test2"); - - d = data->expect; - qdict_put_str(d, "event", "EVENT_C"); - qdict_put(d, "data", d_data); + UserDefOne b = { .integer = 2, .string = (char *)"test1" }; + data->expect = qdict_from_jsonf_nofail( + "{ 'event': 'EVENT_C', 'data': {" + " 'a': 1, 'b': { 'integer': 2, 'string': 'test1' }, 'c': 'test2' } }"); qapi_event_send_event_c(true, 1, true, &b, "test2"); - - g_free(b.string); + g_assert(data->emitted); + qobject_unref(data->expect); } /* Complex type */ static void test_event_d(TestEventData *data, const void *unused) { - UserDefOne struct1; - EventStructOne a; - QDict *d, *d_data, *d_a, *d_struct1; - - struct1.integer = 2; - struct1.string = g_strdup("test1"); - struct1.has_enum1 = true; - struct1.enum1 = ENUM_ONE_VALUE1; - - a.struct1 = &struct1; - a.string = g_strdup("test2"); - a.has_enum2 = true; - a.enum2 = ENUM_ONE_VALUE2; - - d_struct1 = qdict_new(); - qdict_put_int(d_struct1, "integer", 2); - qdict_put_str(d_struct1, "string", "test1"); - qdict_put_str(d_struct1, "enum1", "value1"); - - d_a = qdict_new(); - qdict_put(d_a, "struct1", d_struct1); - qdict_put_str(d_a, "string", "test2"); - qdict_put_str(d_a, "enum2", "value2"); - - d_data = qdict_new(); - qdict_put(d_data, "a", d_a); - qdict_put_str(d_data, "b", "test3"); - qdict_put_str(d_data, "enum3", "value3"); - - d = data->expect; - qdict_put_str(d, "event", "EVENT_D"); - qdict_put(d, "data", d_data); - + UserDefOne struct1 = { + .integer = 2, .string = (char *)"test1", + .has_enum1 = true, .enum1 = ENUM_ONE_VALUE1, + }; + EventStructOne a = { + .struct1 = &struct1, + .string = (char *)"test2", + .has_enum2 = true, + .enum2 = ENUM_ONE_VALUE2, + }; + + data->expect = qdict_from_jsonf_nofail( + "{ 'event': 'EVENT_D', 'data': {" + " 'a': {" + " 'struct1': { 'integer': 2, 'string': 'test1', 'enum1': 'value1' }," + " 'string': 'test2', 'enum2': 'value2' }," + " 'b': 'test3', 'enum3': 'value3' } }"); qapi_event_send_event_d(&a, "test3", false, NULL, true, ENUM_ONE_VALUE3); - - g_free(struct1.string); - g_free(a.string); + g_assert(data->emitted); + qobject_unref(data->expect); } int main(int argc, char **argv) |