diff options
Diffstat (limited to 'target')
| -rw-r--r-- | target/arm/arch_dump.c | 124 | ||||
| -rw-r--r-- | target/arm/cpu.c | 1 | ||||
| -rw-r--r-- | target/arm/cpu.h | 25 | ||||
| -rw-r--r-- | target/arm/kvm64.c | 24 | ||||
| -rw-r--r-- | target/arm/pauth_helper.c | 4 | ||||
| -rw-r--r-- | target/m68k/translate.c | 42 |
6 files changed, 177 insertions, 43 deletions
diff --git a/target/arm/arch_dump.c b/target/arm/arch_dump.c index 26a2c09868..2345dec3c2 100644 --- a/target/arm/arch_dump.c +++ b/target/arm/arch_dump.c @@ -62,12 +62,23 @@ struct aarch64_user_vfp_state { QEMU_BUILD_BUG_ON(sizeof(struct aarch64_user_vfp_state) != 528); +/* struct user_sve_header from arch/arm64/include/uapi/asm/ptrace.h */ +struct aarch64_user_sve_header { + uint32_t size; + uint32_t max_size; + uint16_t vl; + uint16_t max_vl; + uint16_t flags; + uint16_t reserved; +} QEMU_PACKED; + struct aarch64_note { Elf64_Nhdr hdr; char name[8]; /* align_up(sizeof("CORE"), 4) */ union { struct aarch64_elf_prstatus prstatus; struct aarch64_user_vfp_state vfp; + struct aarch64_user_sve_header sve; }; } QEMU_PACKED; @@ -76,6 +87,8 @@ struct aarch64_note { (AARCH64_NOTE_HEADER_SIZE + sizeof(struct aarch64_elf_prstatus)) #define AARCH64_PRFPREG_NOTE_SIZE \ (AARCH64_NOTE_HEADER_SIZE + sizeof(struct aarch64_user_vfp_state)) +#define AARCH64_SVE_NOTE_SIZE(env) \ + (AARCH64_NOTE_HEADER_SIZE + sve_size(env)) static void aarch64_note_init(struct aarch64_note *note, DumpState *s, const char *name, Elf64_Word namesz, @@ -128,11 +141,102 @@ static int aarch64_write_elf64_prfpreg(WriteCoreDumpFunction f, return 0; } +#ifdef TARGET_AARCH64 +static off_t sve_zreg_offset(uint32_t vq, int n) +{ + off_t off = sizeof(struct aarch64_user_sve_header); + return ROUND_UP(off, 16) + vq * 16 * n; +} + +static off_t sve_preg_offset(uint32_t vq, int n) +{ + return sve_zreg_offset(vq, 32) + vq * 16 / 8 * n; +} + +static off_t sve_fpsr_offset(uint32_t vq) +{ + off_t off = sve_preg_offset(vq, 17); + return ROUND_UP(off, 16); +} + +static off_t sve_fpcr_offset(uint32_t vq) +{ + return sve_fpsr_offset(vq) + sizeof(uint32_t); +} + +static uint32_t sve_current_vq(CPUARMState *env) +{ + return sve_zcr_len_for_el(env, arm_current_el(env)) + 1; +} + +static size_t sve_size_vq(uint32_t vq) +{ + off_t off = sve_fpcr_offset(vq) + sizeof(uint32_t); + return ROUND_UP(off, 16); +} + +static size_t sve_size(CPUARMState *env) +{ + return sve_size_vq(sve_current_vq(env)); +} + +static int aarch64_write_elf64_sve(WriteCoreDumpFunction f, + CPUARMState *env, int cpuid, + DumpState *s) +{ + struct aarch64_note *note; + ARMCPU *cpu = env_archcpu(env); + uint32_t vq = sve_current_vq(env); + uint64_t tmp[ARM_MAX_VQ * 2], *r; + uint32_t fpr; + uint8_t *buf; + int ret, i; + + note = g_malloc0(AARCH64_SVE_NOTE_SIZE(env)); + buf = (uint8_t *)¬e->sve; + + aarch64_note_init(note, s, "LINUX", 6, NT_ARM_SVE, sve_size_vq(vq)); + + note->sve.size = cpu_to_dump32(s, sve_size_vq(vq)); + note->sve.max_size = cpu_to_dump32(s, sve_size_vq(cpu->sve_max_vq)); + note->sve.vl = cpu_to_dump16(s, vq * 16); + note->sve.max_vl = cpu_to_dump16(s, cpu->sve_max_vq * 16); + note->sve.flags = cpu_to_dump16(s, 1); + + for (i = 0; i < 32; ++i) { + r = sve_bswap64(tmp, &env->vfp.zregs[i].d[0], vq * 2); + memcpy(&buf[sve_zreg_offset(vq, i)], r, vq * 16); + } + + for (i = 0; i < 17; ++i) { + r = sve_bswap64(tmp, r = &env->vfp.pregs[i].p[0], + DIV_ROUND_UP(vq * 2, 8)); + memcpy(&buf[sve_preg_offset(vq, i)], r, vq * 16 / 8); + } + + fpr = cpu_to_dump32(s, vfp_get_fpsr(env)); + memcpy(&buf[sve_fpsr_offset(vq)], &fpr, sizeof(uint32_t)); + + fpr = cpu_to_dump32(s, vfp_get_fpcr(env)); + memcpy(&buf[sve_fpcr_offset(vq)], &fpr, sizeof(uint32_t)); + + ret = f(note, AARCH64_SVE_NOTE_SIZE(env), s); + g_free(note); + + if (ret < 0) { + return -1; + } + + return 0; +} +#endif + int arm_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, int cpuid, void *opaque) { struct aarch64_note note; - CPUARMState *env = &ARM_CPU(cs)->env; + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; DumpState *s = opaque; uint64_t pstate, sp; int ret, i; @@ -163,7 +267,18 @@ int arm_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, return -1; } - return aarch64_write_elf64_prfpreg(f, env, cpuid, s); + ret = aarch64_write_elf64_prfpreg(f, env, cpuid, s); + if (ret) { + return ret; + } + +#ifdef TARGET_AARCH64 + if (cpu_isar_feature(aa64_sve, cpu)) { + ret = aarch64_write_elf64_sve(f, env, cpuid, s); + } +#endif + + return ret; } /* struct pt_regs from arch/arm/include/asm/ptrace.h */ @@ -335,6 +450,11 @@ ssize_t cpu_get_note_size(int class, int machine, int nr_cpus) if (class == ELFCLASS64) { note_size = AARCH64_PRSTATUS_NOTE_SIZE; note_size += AARCH64_PRFPREG_NOTE_SIZE; +#ifdef TARGET_AARCH64 + if (cpu_isar_feature(aa64_sve, cpu)) { + note_size += AARCH64_SVE_NOTE_SIZE(env); + } +#endif } else { note_size = ARM_PRSTATUS_NOTE_SIZE; if (arm_feature(env, ARM_FEATURE_VFP)) { diff --git a/target/arm/cpu.c b/target/arm/cpu.c index d62fd5fdc6..64cd0a7d73 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2121,6 +2121,7 @@ static void cortex_r5_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V7); set_feature(&cpu->env, ARM_FEATURE_V7MP); set_feature(&cpu->env, ARM_FEATURE_PMSA); + set_feature(&cpu->env, ARM_FEATURE_PMU); cpu->midr = 0x411fc153; /* r1p3 */ cpu->id_pfr0 = 0x0131; cpu->id_pfr1 = 0x001; diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 40f2c45e17..c1aedbeac0 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -980,6 +980,31 @@ void aarch64_sve_narrow_vq(CPUARMState *env, unsigned vq); void aarch64_sve_change_el(CPUARMState *env, int old_el, int new_el, bool el0_a64); void aarch64_add_sve_properties(Object *obj); + +/* + * SVE registers are encoded in KVM's memory in an endianness-invariant format. + * The byte at offset i from the start of the in-memory representation contains + * the bits [(7 + 8 * i) : (8 * i)] of the register value. As this means the + * lowest offsets are stored in the lowest memory addresses, then that nearly + * matches QEMU's representation, which is to use an array of host-endian + * uint64_t's, where the lower offsets are at the lower indices. To complete + * the translation we just need to byte swap the uint64_t's on big-endian hosts. + */ +static inline uint64_t *sve_bswap64(uint64_t *dst, uint64_t *src, int nr) +{ +#ifdef HOST_WORDS_BIGENDIAN + int i; + + for (i = 0; i < nr; ++i) { + dst[i] = bswap64(src[i]); + } + + return dst; +#else + return src; +#endif +} + #else static inline void aarch64_sve_narrow_vq(CPUARMState *env, unsigned vq) { } static inline void aarch64_sve_change_el(CPUARMState *env, int o, diff --git a/target/arm/kvm64.c b/target/arm/kvm64.c index 876184b8fe..e2da756e65 100644 --- a/target/arm/kvm64.c +++ b/target/arm/kvm64.c @@ -877,30 +877,6 @@ static int kvm_arch_put_fpsimd(CPUState *cs) } /* - * SVE registers are encoded in KVM's memory in an endianness-invariant format. - * The byte at offset i from the start of the in-memory representation contains - * the bits [(7 + 8 * i) : (8 * i)] of the register value. As this means the - * lowest offsets are stored in the lowest memory addresses, then that nearly - * matches QEMU's representation, which is to use an array of host-endian - * uint64_t's, where the lower offsets are at the lower indices. To complete - * the translation we just need to byte swap the uint64_t's on big-endian hosts. - */ -static uint64_t *sve_bswap64(uint64_t *dst, uint64_t *src, int nr) -{ -#ifdef HOST_WORDS_BIGENDIAN - int i; - - for (i = 0; i < nr; ++i) { - dst[i] = bswap64(src[i]); - } - - return dst; -#else - return src; -#endif -} - -/* * KVM SVE registers come in slices where ZREGs have a slice size of 2048 bits * and PREGS and the FFR have a slice size of 256 bits. However we simply hard * code the slice index to zero for now as it's unlikely we'll need more than diff --git a/target/arm/pauth_helper.c b/target/arm/pauth_helper.c index d3194f2043..0a5f41e10c 100644 --- a/target/arm/pauth_helper.c +++ b/target/arm/pauth_helper.c @@ -89,7 +89,7 @@ static uint64_t pac_sub(uint64_t i) uint64_t o = 0; int b; - for (b = 0; b < 64; b += 16) { + for (b = 0; b < 64; b += 4) { o |= (uint64_t)sub[(i >> b) & 0xf] << b; } return o; @@ -104,7 +104,7 @@ static uint64_t pac_inv_sub(uint64_t i) uint64_t o = 0; int b; - for (b = 0; b < 64; b += 16) { + for (b = 0; b < 64; b += 4) { o |= (uint64_t)inv_sub[(i >> b) & 0xf] << b; } return o; diff --git a/target/m68k/translate.c b/target/m68k/translate.c index 31b743717e..0f80888203 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -289,16 +289,21 @@ static void gen_jmp(DisasContext *s, TCGv dest) s->base.is_jmp = DISAS_JUMP; } -static void gen_exception(DisasContext *s, uint32_t dest, int nr) +static void gen_raise_exception(int nr) { TCGv_i32 tmp; - update_cc_op(s); - tcg_gen_movi_i32(QREG_PC, dest); - tmp = tcg_const_i32(nr); gen_helper_raise_exception(cpu_env, tmp); tcg_temp_free_i32(tmp); +} + +static void gen_exception(DisasContext *s, uint32_t dest, int nr) +{ + update_cc_op(s); + tcg_gen_movi_i32(QREG_PC, dest); + + gen_raise_exception(nr); s->base.is_jmp = DISAS_NORETURN; } @@ -6198,29 +6203,36 @@ static void m68k_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *dc = container_of(dcbase, DisasContext, base); - if (dc->base.is_jmp == DISAS_NORETURN) { - return; - } - if (dc->base.singlestep_enabled) { - gen_helper_raise_exception(cpu_env, tcg_const_i32(EXCP_DEBUG)); - return; - } - switch (dc->base.is_jmp) { + case DISAS_NORETURN: + break; case DISAS_TOO_MANY: update_cc_op(dc); - gen_jmp_tb(dc, 0, dc->pc); + if (dc->base.singlestep_enabled) { + tcg_gen_movi_i32(QREG_PC, dc->pc); + gen_raise_exception(EXCP_DEBUG); + } else { + gen_jmp_tb(dc, 0, dc->pc); + } break; case DISAS_JUMP: /* We updated CC_OP and PC in gen_jmp/gen_jmp_im. */ - tcg_gen_lookup_and_goto_ptr(); + if (dc->base.singlestep_enabled) { + gen_raise_exception(EXCP_DEBUG); + } else { + tcg_gen_lookup_and_goto_ptr(); + } break; case DISAS_EXIT: /* * We updated CC_OP and PC in gen_exit_tb, but also modified * other state that may require returning to the main loop. */ - tcg_gen_exit_tb(NULL, 0); + if (dc->base.singlestep_enabled) { + gen_raise_exception(EXCP_DEBUG); + } else { + tcg_gen_exit_tb(NULL, 0); + } break; default: g_assert_not_reached(); |