* [Qemu-devel] [PATCH v2 0/7] target-arm: Support AArch64 KVM
@ 2013-12-17 12:15 Peter Maydell
2013-12-17 12:15 ` [Qemu-devel] [PATCH v2 1/7] target-arm/kvm: Split 32 bit only code into its own file Peter Maydell
` (7 more replies)
0 siblings, 8 replies; 13+ messages in thread
From: Peter Maydell @ 2013-12-17 12:15 UTC (permalink / raw)
To: qemu-devel; +Cc: kvmarm, Christoffer Dall, patches
This patchset adds support for basic AArch64 KVM VM control;
it's based on current master. This is a quick resend with the very
minor nits Christoffer pointed out fixed; I'm planning to put it into
a target-arm pullreq in the next day or two.
This patch series supports:
* 64 bit KVM VM control
* SMP and UP
* PSCI boot of secondary CPUs
It doesn't support:
* migration
* reset (partly because there's no way to reset a mach-virt system yet)
* anything except "-cpu host"
* debugging the VM via qemu gdbstub
* running 32 bit VMs on a 64 bit system
[Mian's patchset includes support for that but I have left it out
for the moment because it needs more thought about UI and so on]
Changes v1->v2:
* improved a couple of comments
* compat string for 64 bit is "arm,arm-v8", not -v7
* removed superfluous include of usb.mak and pci.mak from config
Mian M. Hamayun (2):
target-arm: Add minimal KVM AArch64 support
hw/arm/boot: Add boot support for AArch64 processor
Peter Maydell (5):
target-arm/kvm: Split 32 bit only code into its own file
target-arm: Clean up handling of AArch64 PSTATE
configure: Enable KVM for aarch64 host/target combination
hw/arm/boot: Allow easier swapping in of different loader code
default-configs: Add config for aarch64-softmmu
configure | 2 +-
default-configs/aarch64-softmmu.mak | 6 +
hw/arm/boot.c | 193 ++++++++++----
linux-user/signal.c | 6 +-
target-arm/Makefile.objs | 2 +
target-arm/cpu.c | 6 +
target-arm/cpu.h | 70 ++++-
target-arm/gdbstub64.c | 4 +-
target-arm/kvm.c | 495 +---------------------------------
target-arm/kvm32.c | 515 ++++++++++++++++++++++++++++++++++++
target-arm/kvm64.c | 204 ++++++++++++++
target-arm/translate-a64.c | 12 +-
12 files changed, 954 insertions(+), 561 deletions(-)
create mode 100644 default-configs/aarch64-softmmu.mak
create mode 100644 target-arm/kvm32.c
create mode 100644 target-arm/kvm64.c
--
1.8.5
^ permalink raw reply [flat|nested] 13+ messages in thread
* [Qemu-devel] [PATCH v2 1/7] target-arm/kvm: Split 32 bit only code into its own file
2013-12-17 12:15 [Qemu-devel] [PATCH v2 0/7] target-arm: Support AArch64 KVM Peter Maydell
@ 2013-12-17 12:15 ` Peter Maydell
2013-12-17 12:15 ` [Qemu-devel] [PATCH v2 2/7] target-arm: Clean up handling of AArch64 PSTATE Peter Maydell
` (6 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Peter Maydell @ 2013-12-17 12:15 UTC (permalink / raw)
To: qemu-devel; +Cc: kvmarm, Christoffer Dall, patches
Split ARM KVM support code which is 32 bit specific out into its
own file, which we only compile on 32 bit hosts. This will give
us a place to add the 64 bit support code without adding lots of
ifdefs to kvm.c.
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 1385645602-18662-2-git-send-email-peter.maydell@linaro.org
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
---
target-arm/Makefile.objs | 1 +
target-arm/kvm.c | 491 --------------------------------------------
target-arm/kvm32.c | 515 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 516 insertions(+), 491 deletions(-)
create mode 100644 target-arm/kvm32.c
diff --git a/target-arm/Makefile.objs b/target-arm/Makefile.objs
index 356fbfc..d1db77c 100644
--- a/target-arm/Makefile.objs
+++ b/target-arm/Makefile.objs
@@ -6,3 +6,4 @@ obj-y += translate.o op_helper.o helper.o cpu.o
obj-y += neon_helper.o iwmmxt_helper.o
obj-y += gdbstub.o
obj-$(TARGET_AARCH64) += cpu64.o translate-a64.o gdbstub64.o
+obj-$(call land,$(CONFIG_KVM),$(call lnot,$(TARGET_AARCH64))) += kvm32.o
diff --git a/target-arm/kvm.c b/target-arm/kvm.c
index f865dac..5cdb3b9 100644
--- a/target-arm/kvm.c
+++ b/target-arm/kvm.c
@@ -100,120 +100,6 @@ void kvm_arm_destroy_scratch_host_vcpu(int *fdarray)
}
}
-static inline void set_feature(uint64_t *features, int feature)
-{
- *features |= 1ULL << feature;
-}
-
-bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc)
-{
- /* Identify the feature bits corresponding to the host CPU, and
- * fill out the ARMHostCPUClass fields accordingly. To do this
- * we have to create a scratch VM, create a single CPU inside it,
- * and then query that CPU for the relevant ID registers.
- */
- int i, ret, fdarray[3];
- uint32_t midr, id_pfr0, id_isar0, mvfr1;
- uint64_t features = 0;
- /* Old kernels may not know about the PREFERRED_TARGET ioctl: however
- * we know these will only support creating one kind of guest CPU,
- * which is its preferred CPU type.
- */
- static const uint32_t cpus_to_try[] = {
- QEMU_KVM_ARM_TARGET_CORTEX_A15,
- QEMU_KVM_ARM_TARGET_NONE
- };
- struct kvm_vcpu_init init;
- struct kvm_one_reg idregs[] = {
- {
- .id = KVM_REG_ARM | KVM_REG_SIZE_U32
- | ENCODE_CP_REG(15, 0, 0, 0, 0, 0),
- .addr = (uintptr_t)&midr,
- },
- {
- .id = KVM_REG_ARM | KVM_REG_SIZE_U32
- | ENCODE_CP_REG(15, 0, 0, 1, 0, 0),
- .addr = (uintptr_t)&id_pfr0,
- },
- {
- .id = KVM_REG_ARM | KVM_REG_SIZE_U32
- | ENCODE_CP_REG(15, 0, 0, 2, 0, 0),
- .addr = (uintptr_t)&id_isar0,
- },
- {
- .id = KVM_REG_ARM | KVM_REG_SIZE_U32
- | KVM_REG_ARM_VFP | KVM_REG_ARM_VFP_MVFR1,
- .addr = (uintptr_t)&mvfr1,
- },
- };
-
- if (!kvm_arm_create_scratch_host_vcpu(cpus_to_try, fdarray, &init)) {
- return false;
- }
-
- ahcc->target = init.target;
-
- /* This is not strictly blessed by the device tree binding docs yet,
- * but in practice the kernel does not care about this string so
- * there is no point maintaining an KVM_ARM_TARGET_* -> string table.
- */
- ahcc->dtb_compatible = "arm,arm-v7";
-
- for (i = 0; i < ARRAY_SIZE(idregs); i++) {
- ret = ioctl(fdarray[2], KVM_GET_ONE_REG, &idregs[i]);
- if (ret) {
- break;
- }
- }
-
- kvm_arm_destroy_scratch_host_vcpu(fdarray);
-
- if (ret) {
- return false;
- }
-
- /* Now we've retrieved all the register information we can
- * set the feature bits based on the ID register fields.
- * We can assume any KVM supporting CPU is at least a v7
- * with VFPv3, LPAE and the generic timers; this in turn implies
- * most of the other feature bits, but a few must be tested.
- */
- set_feature(&features, ARM_FEATURE_V7);
- set_feature(&features, ARM_FEATURE_VFP3);
- set_feature(&features, ARM_FEATURE_LPAE);
- set_feature(&features, ARM_FEATURE_GENERIC_TIMER);
-
- switch (extract32(id_isar0, 24, 4)) {
- case 1:
- set_feature(&features, ARM_FEATURE_THUMB_DIV);
- break;
- case 2:
- set_feature(&features, ARM_FEATURE_ARM_DIV);
- set_feature(&features, ARM_FEATURE_THUMB_DIV);
- break;
- default:
- break;
- }
-
- if (extract32(id_pfr0, 12, 4) == 1) {
- set_feature(&features, ARM_FEATURE_THUMB2EE);
- }
- if (extract32(mvfr1, 20, 4) == 1) {
- set_feature(&features, ARM_FEATURE_VFP_FP16);
- }
- if (extract32(mvfr1, 12, 4) == 1) {
- set_feature(&features, ARM_FEATURE_NEON);
- }
- if (extract32(mvfr1, 28, 4) == 1) {
- /* FMAC support implies VFPv4 */
- set_feature(&features, ARM_FEATURE_VFP4);
- }
-
- ahcc->features = features;
-
- return true;
-}
-
static void kvm_arm_host_cpu_class_init(ObjectClass *oc, void *data)
{
ARMHostCPUClass *ahcc = ARM_HOST_CPU_CLASS(oc);
@@ -265,144 +151,6 @@ unsigned long kvm_arch_vcpu_id(CPUState *cpu)
return cpu->cpu_index;
}
-static bool reg_syncs_via_tuple_list(uint64_t regidx)
-{
- /* Return true if the regidx is a register we should synchronize
- * via the cpreg_tuples array (ie is not a core reg we sync by
- * hand in kvm_arch_get/put_registers())
- */
- switch (regidx & KVM_REG_ARM_COPROC_MASK) {
- case KVM_REG_ARM_CORE:
- case KVM_REG_ARM_VFP:
- return false;
- default:
- return true;
- }
-}
-
-static int compare_u64(const void *a, const void *b)
-{
- if (*(uint64_t *)a > *(uint64_t *)b) {
- return 1;
- }
- if (*(uint64_t *)a < *(uint64_t *)b) {
- return -1;
- }
- return 0;
-}
-
-int kvm_arch_init_vcpu(CPUState *cs)
-{
- struct kvm_vcpu_init init;
- int i, ret, arraylen;
- uint64_t v;
- struct kvm_one_reg r;
- struct kvm_reg_list rl;
- struct kvm_reg_list *rlp;
- ARMCPU *cpu = ARM_CPU(cs);
-
- if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE) {
- fprintf(stderr, "KVM is not supported for this guest CPU type\n");
- return -EINVAL;
- }
-
- init.target = cpu->kvm_target;
- memset(init.features, 0, sizeof(init.features));
- if (cpu->start_powered_off) {
- init.features[0] = 1 << KVM_ARM_VCPU_POWER_OFF;
- }
- ret = kvm_vcpu_ioctl(cs, KVM_ARM_VCPU_INIT, &init);
- if (ret) {
- return ret;
- }
- /* Query the kernel to make sure it supports 32 VFP
- * registers: QEMU's "cortex-a15" CPU is always a
- * VFP-D32 core. The simplest way to do this is just
- * to attempt to read register d31.
- */
- r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP | 31;
- r.addr = (uintptr_t)(&v);
- ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
- if (ret == -ENOENT) {
- return -EINVAL;
- }
-
- /* Populate the cpreg list based on the kernel's idea
- * of what registers exist (and throw away the TCG-created list).
- */
- rl.n = 0;
- ret = kvm_vcpu_ioctl(cs, KVM_GET_REG_LIST, &rl);
- if (ret != -E2BIG) {
- return ret;
- }
- rlp = g_malloc(sizeof(struct kvm_reg_list) + rl.n * sizeof(uint64_t));
- rlp->n = rl.n;
- ret = kvm_vcpu_ioctl(cs, KVM_GET_REG_LIST, rlp);
- if (ret) {
- goto out;
- }
- /* Sort the list we get back from the kernel, since cpreg_tuples
- * must be in strictly ascending order.
- */
- qsort(&rlp->reg, rlp->n, sizeof(rlp->reg[0]), compare_u64);
-
- for (i = 0, arraylen = 0; i < rlp->n; i++) {
- if (!reg_syncs_via_tuple_list(rlp->reg[i])) {
- continue;
- }
- switch (rlp->reg[i] & KVM_REG_SIZE_MASK) {
- case KVM_REG_SIZE_U32:
- case KVM_REG_SIZE_U64:
- break;
- default:
- fprintf(stderr, "Can't handle size of register in kernel list\n");
- ret = -EINVAL;
- goto out;
- }
-
- arraylen++;
- }
-
- cpu->cpreg_indexes = g_renew(uint64_t, cpu->cpreg_indexes, arraylen);
- cpu->cpreg_values = g_renew(uint64_t, cpu->cpreg_values, arraylen);
- cpu->cpreg_vmstate_indexes = g_renew(uint64_t, cpu->cpreg_vmstate_indexes,
- arraylen);
- cpu->cpreg_vmstate_values = g_renew(uint64_t, cpu->cpreg_vmstate_values,
- arraylen);
- cpu->cpreg_array_len = arraylen;
- cpu->cpreg_vmstate_array_len = arraylen;
-
- for (i = 0, arraylen = 0; i < rlp->n; i++) {
- uint64_t regidx = rlp->reg[i];
- if (!reg_syncs_via_tuple_list(regidx)) {
- continue;
- }
- cpu->cpreg_indexes[arraylen] = regidx;
- arraylen++;
- }
- assert(cpu->cpreg_array_len == arraylen);
-
- if (!write_kvmstate_to_list(cpu)) {
- /* Shouldn't happen unless kernel is inconsistent about
- * what registers exist.
- */
- fprintf(stderr, "Initial read of kernel register state failed\n");
- ret = -EINVAL;
- goto out;
- }
-
- /* Save a copy of the initial register values so that we can
- * feed it back to the kernel on VCPU reset.
- */
- cpu->cpreg_reset_values = g_memdup(cpu->cpreg_values,
- cpu->cpreg_array_len *
- sizeof(cpu->cpreg_values[0]));
-
-out:
- g_free(rlp);
- return ret;
-}
-
/* We track all the KVM devices which need their memory addresses
* passing to the kernel in a list of these structures.
* When board init is complete we run through the list and
@@ -563,232 +311,6 @@ bool write_list_to_kvmstate(ARMCPU *cpu)
return ok;
}
-typedef struct Reg {
- uint64_t id;
- int offset;
-} Reg;
-
-#define COREREG(KERNELNAME, QEMUFIELD) \
- { \
- KVM_REG_ARM | KVM_REG_SIZE_U32 | \
- KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(KERNELNAME), \
- offsetof(CPUARMState, QEMUFIELD) \
- }
-
-#define VFPSYSREG(R) \
- { \
- KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP | \
- KVM_REG_ARM_VFP_##R, \
- offsetof(CPUARMState, vfp.xregs[ARM_VFP_##R]) \
- }
-
-static const Reg regs[] = {
- /* R0_usr .. R14_usr */
- COREREG(usr_regs.uregs[0], regs[0]),
- COREREG(usr_regs.uregs[1], regs[1]),
- COREREG(usr_regs.uregs[2], regs[2]),
- COREREG(usr_regs.uregs[3], regs[3]),
- COREREG(usr_regs.uregs[4], regs[4]),
- COREREG(usr_regs.uregs[5], regs[5]),
- COREREG(usr_regs.uregs[6], regs[6]),
- COREREG(usr_regs.uregs[7], regs[7]),
- COREREG(usr_regs.uregs[8], usr_regs[0]),
- COREREG(usr_regs.uregs[9], usr_regs[1]),
- COREREG(usr_regs.uregs[10], usr_regs[2]),
- COREREG(usr_regs.uregs[11], usr_regs[3]),
- COREREG(usr_regs.uregs[12], usr_regs[4]),
- COREREG(usr_regs.uregs[13], banked_r13[0]),
- COREREG(usr_regs.uregs[14], banked_r14[0]),
- /* R13, R14, SPSR for SVC, ABT, UND, IRQ banks */
- COREREG(svc_regs[0], banked_r13[1]),
- COREREG(svc_regs[1], banked_r14[1]),
- COREREG(svc_regs[2], banked_spsr[1]),
- COREREG(abt_regs[0], banked_r13[2]),
- COREREG(abt_regs[1], banked_r14[2]),
- COREREG(abt_regs[2], banked_spsr[2]),
- COREREG(und_regs[0], banked_r13[3]),
- COREREG(und_regs[1], banked_r14[3]),
- COREREG(und_regs[2], banked_spsr[3]),
- COREREG(irq_regs[0], banked_r13[4]),
- COREREG(irq_regs[1], banked_r14[4]),
- COREREG(irq_regs[2], banked_spsr[4]),
- /* R8_fiq .. R14_fiq and SPSR_fiq */
- COREREG(fiq_regs[0], fiq_regs[0]),
- COREREG(fiq_regs[1], fiq_regs[1]),
- COREREG(fiq_regs[2], fiq_regs[2]),
- COREREG(fiq_regs[3], fiq_regs[3]),
- COREREG(fiq_regs[4], fiq_regs[4]),
- COREREG(fiq_regs[5], banked_r13[5]),
- COREREG(fiq_regs[6], banked_r14[5]),
- COREREG(fiq_regs[7], banked_spsr[5]),
- /* R15 */
- COREREG(usr_regs.uregs[15], regs[15]),
- /* VFP system registers */
- VFPSYSREG(FPSID),
- VFPSYSREG(MVFR1),
- VFPSYSREG(MVFR0),
- VFPSYSREG(FPEXC),
- VFPSYSREG(FPINST),
- VFPSYSREG(FPINST2),
-};
-
-int kvm_arch_put_registers(CPUState *cs, int level)
-{
- ARMCPU *cpu = ARM_CPU(cs);
- CPUARMState *env = &cpu->env;
- struct kvm_one_reg r;
- int mode, bn;
- int ret, i;
- uint32_t cpsr, fpscr;
-
- /* Make sure the banked regs are properly set */
- mode = env->uncached_cpsr & CPSR_M;
- bn = bank_number(mode);
- if (mode == ARM_CPU_MODE_FIQ) {
- memcpy(env->fiq_regs, env->regs + 8, 5 * sizeof(uint32_t));
- } else {
- memcpy(env->usr_regs, env->regs + 8, 5 * sizeof(uint32_t));
- }
- env->banked_r13[bn] = env->regs[13];
- env->banked_r14[bn] = env->regs[14];
- env->banked_spsr[bn] = env->spsr;
-
- /* Now we can safely copy stuff down to the kernel */
- for (i = 0; i < ARRAY_SIZE(regs); i++) {
- r.id = regs[i].id;
- r.addr = (uintptr_t)(env) + regs[i].offset;
- ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
- if (ret) {
- return ret;
- }
- }
-
- /* Special cases which aren't a single CPUARMState field */
- cpsr = cpsr_read(env);
- r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 |
- KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(usr_regs.ARM_cpsr);
- r.addr = (uintptr_t)(&cpsr);
- ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
- if (ret) {
- return ret;
- }
-
- /* VFP registers */
- r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP;
- for (i = 0; i < 32; i++) {
- r.addr = (uintptr_t)(&env->vfp.regs[i]);
- ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
- if (ret) {
- return ret;
- }
- r.id++;
- }
-
- r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP |
- KVM_REG_ARM_VFP_FPSCR;
- fpscr = vfp_get_fpscr(env);
- r.addr = (uintptr_t)&fpscr;
- ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
- if (ret) {
- return ret;
- }
-
- /* Note that we do not call write_cpustate_to_list()
- * here, so we are only writing the tuple list back to
- * KVM. This is safe because nothing can change the
- * CPUARMState cp15 fields (in particular gdb accesses cannot)
- * and so there are no changes to sync. In fact syncing would
- * be wrong at this point: for a constant register where TCG and
- * KVM disagree about its value, the preceding write_list_to_cpustate()
- * would not have had any effect on the CPUARMState value (since the
- * register is read-only), and a write_cpustate_to_list() here would
- * then try to write the TCG value back into KVM -- this would either
- * fail or incorrectly change the value the guest sees.
- *
- * If we ever want to allow the user to modify cp15 registers via
- * the gdb stub, we would need to be more clever here (for instance
- * tracking the set of registers kvm_arch_get_registers() successfully
- * managed to update the CPUARMState with, and only allowing those
- * to be written back up into the kernel).
- */
- if (!write_list_to_kvmstate(cpu)) {
- return EINVAL;
- }
-
- return ret;
-}
-
-int kvm_arch_get_registers(CPUState *cs)
-{
- ARMCPU *cpu = ARM_CPU(cs);
- CPUARMState *env = &cpu->env;
- struct kvm_one_reg r;
- int mode, bn;
- int ret, i;
- uint32_t cpsr, fpscr;
-
- for (i = 0; i < ARRAY_SIZE(regs); i++) {
- r.id = regs[i].id;
- r.addr = (uintptr_t)(env) + regs[i].offset;
- ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
- if (ret) {
- return ret;
- }
- }
-
- /* Special cases which aren't a single CPUARMState field */
- r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 |
- KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(usr_regs.ARM_cpsr);
- r.addr = (uintptr_t)(&cpsr);
- ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
- if (ret) {
- return ret;
- }
- cpsr_write(env, cpsr, 0xffffffff);
-
- /* Make sure the current mode regs are properly set */
- mode = env->uncached_cpsr & CPSR_M;
- bn = bank_number(mode);
- if (mode == ARM_CPU_MODE_FIQ) {
- memcpy(env->regs + 8, env->fiq_regs, 5 * sizeof(uint32_t));
- } else {
- memcpy(env->regs + 8, env->usr_regs, 5 * sizeof(uint32_t));
- }
- env->regs[13] = env->banked_r13[bn];
- env->regs[14] = env->banked_r14[bn];
- env->spsr = env->banked_spsr[bn];
-
- /* VFP registers */
- r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP;
- for (i = 0; i < 32; i++) {
- r.addr = (uintptr_t)(&env->vfp.regs[i]);
- ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
- if (ret) {
- return ret;
- }
- r.id++;
- }
-
- r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP |
- KVM_REG_ARM_VFP_FPSCR;
- r.addr = (uintptr_t)&fpscr;
- ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
- if (ret) {
- return ret;
- }
- vfp_set_fpscr(env, fpscr);
-
- if (!write_kvmstate_to_list(cpu)) {
- return EINVAL;
- }
- /* Note that it's OK to have registers which aren't in CPUState,
- * so we can ignore a failure return here.
- */
- write_list_to_cpustate(cpu);
-
- return 0;
-}
-
void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run)
{
}
@@ -802,19 +324,6 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
return 0;
}
-void kvm_arch_reset_vcpu(CPUState *cs)
-{
- /* Feed the kernel back its initial register state */
- ARMCPU *cpu = ARM_CPU(cs);
-
- memmove(cpu->cpreg_values, cpu->cpreg_reset_values,
- cpu->cpreg_array_len * sizeof(cpu->cpreg_values[0]));
-
- if (!write_list_to_kvmstate(cpu)) {
- abort();
- }
-}
-
bool kvm_arch_stop_on_emulation_error(CPUState *cs)
{
return true;
diff --git a/target-arm/kvm32.c b/target-arm/kvm32.c
new file mode 100644
index 0000000..a4fde07
--- /dev/null
+++ b/target-arm/kvm32.c
@@ -0,0 +1,515 @@
+/*
+ * ARM implementation of KVM hooks, 32 bit specific code.
+ *
+ * Copyright Christoffer Dall 2009-2010
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+
+#include <linux/kvm.h>
+
+#include "qemu-common.h"
+#include "qemu/timer.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/kvm.h"
+#include "kvm_arm.h"
+#include "cpu.h"
+#include "hw/arm/arm.h"
+
+static inline void set_feature(uint64_t *features, int feature)
+{
+ *features |= 1ULL << feature;
+}
+
+bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc)
+{
+ /* Identify the feature bits corresponding to the host CPU, and
+ * fill out the ARMHostCPUClass fields accordingly. To do this
+ * we have to create a scratch VM, create a single CPU inside it,
+ * and then query that CPU for the relevant ID registers.
+ */
+ int i, ret, fdarray[3];
+ uint32_t midr, id_pfr0, id_isar0, mvfr1;
+ uint64_t features = 0;
+ /* Old kernels may not know about the PREFERRED_TARGET ioctl: however
+ * we know these will only support creating one kind of guest CPU,
+ * which is its preferred CPU type.
+ */
+ static const uint32_t cpus_to_try[] = {
+ QEMU_KVM_ARM_TARGET_CORTEX_A15,
+ QEMU_KVM_ARM_TARGET_NONE
+ };
+ struct kvm_vcpu_init init;
+ struct kvm_one_reg idregs[] = {
+ {
+ .id = KVM_REG_ARM | KVM_REG_SIZE_U32
+ | ENCODE_CP_REG(15, 0, 0, 0, 0, 0),
+ .addr = (uintptr_t)&midr,
+ },
+ {
+ .id = KVM_REG_ARM | KVM_REG_SIZE_U32
+ | ENCODE_CP_REG(15, 0, 0, 1, 0, 0),
+ .addr = (uintptr_t)&id_pfr0,
+ },
+ {
+ .id = KVM_REG_ARM | KVM_REG_SIZE_U32
+ | ENCODE_CP_REG(15, 0, 0, 2, 0, 0),
+ .addr = (uintptr_t)&id_isar0,
+ },
+ {
+ .id = KVM_REG_ARM | KVM_REG_SIZE_U32
+ | KVM_REG_ARM_VFP | KVM_REG_ARM_VFP_MVFR1,
+ .addr = (uintptr_t)&mvfr1,
+ },
+ };
+
+ if (!kvm_arm_create_scratch_host_vcpu(cpus_to_try, fdarray, &init)) {
+ return false;
+ }
+
+ ahcc->target = init.target;
+
+ /* This is not strictly blessed by the device tree binding docs yet,
+ * but in practice the kernel does not care about this string so
+ * there is no point maintaining an KVM_ARM_TARGET_* -> string table.
+ */
+ ahcc->dtb_compatible = "arm,arm-v7";
+
+ for (i = 0; i < ARRAY_SIZE(idregs); i++) {
+ ret = ioctl(fdarray[2], KVM_GET_ONE_REG, &idregs[i]);
+ if (ret) {
+ break;
+ }
+ }
+
+ kvm_arm_destroy_scratch_host_vcpu(fdarray);
+
+ if (ret) {
+ return false;
+ }
+
+ /* Now we've retrieved all the register information we can
+ * set the feature bits based on the ID register fields.
+ * We can assume any KVM supporting CPU is at least a v7
+ * with VFPv3, LPAE and the generic timers; this in turn implies
+ * most of the other feature bits, but a few must be tested.
+ */
+ set_feature(&features, ARM_FEATURE_V7);
+ set_feature(&features, ARM_FEATURE_VFP3);
+ set_feature(&features, ARM_FEATURE_LPAE);
+ set_feature(&features, ARM_FEATURE_GENERIC_TIMER);
+
+ switch (extract32(id_isar0, 24, 4)) {
+ case 1:
+ set_feature(&features, ARM_FEATURE_THUMB_DIV);
+ break;
+ case 2:
+ set_feature(&features, ARM_FEATURE_ARM_DIV);
+ set_feature(&features, ARM_FEATURE_THUMB_DIV);
+ break;
+ default:
+ break;
+ }
+
+ if (extract32(id_pfr0, 12, 4) == 1) {
+ set_feature(&features, ARM_FEATURE_THUMB2EE);
+ }
+ if (extract32(mvfr1, 20, 4) == 1) {
+ set_feature(&features, ARM_FEATURE_VFP_FP16);
+ }
+ if (extract32(mvfr1, 12, 4) == 1) {
+ set_feature(&features, ARM_FEATURE_NEON);
+ }
+ if (extract32(mvfr1, 28, 4) == 1) {
+ /* FMAC support implies VFPv4 */
+ set_feature(&features, ARM_FEATURE_VFP4);
+ }
+
+ ahcc->features = features;
+
+ return true;
+}
+
+static bool reg_syncs_via_tuple_list(uint64_t regidx)
+{
+ /* Return true if the regidx is a register we should synchronize
+ * via the cpreg_tuples array (ie is not a core reg we sync by
+ * hand in kvm_arch_get/put_registers())
+ */
+ switch (regidx & KVM_REG_ARM_COPROC_MASK) {
+ case KVM_REG_ARM_CORE:
+ case KVM_REG_ARM_VFP:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static int compare_u64(const void *a, const void *b)
+{
+ if (*(uint64_t *)a > *(uint64_t *)b) {
+ return 1;
+ }
+ if (*(uint64_t *)a < *(uint64_t *)b) {
+ return -1;
+ }
+ return 0;
+}
+
+int kvm_arch_init_vcpu(CPUState *cs)
+{
+ struct kvm_vcpu_init init;
+ int i, ret, arraylen;
+ uint64_t v;
+ struct kvm_one_reg r;
+ struct kvm_reg_list rl;
+ struct kvm_reg_list *rlp;
+ ARMCPU *cpu = ARM_CPU(cs);
+
+ if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE) {
+ fprintf(stderr, "KVM is not supported for this guest CPU type\n");
+ return -EINVAL;
+ }
+
+ init.target = cpu->kvm_target;
+ memset(init.features, 0, sizeof(init.features));
+ if (cpu->start_powered_off) {
+ init.features[0] = 1 << KVM_ARM_VCPU_POWER_OFF;
+ }
+ ret = kvm_vcpu_ioctl(cs, KVM_ARM_VCPU_INIT, &init);
+ if (ret) {
+ return ret;
+ }
+ /* Query the kernel to make sure it supports 32 VFP
+ * registers: QEMU's "cortex-a15" CPU is always a
+ * VFP-D32 core. The simplest way to do this is just
+ * to attempt to read register d31.
+ */
+ r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP | 31;
+ r.addr = (uintptr_t)(&v);
+ ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
+ if (ret == -ENOENT) {
+ return -EINVAL;
+ }
+
+ /* Populate the cpreg list based on the kernel's idea
+ * of what registers exist (and throw away the TCG-created list).
+ */
+ rl.n = 0;
+ ret = kvm_vcpu_ioctl(cs, KVM_GET_REG_LIST, &rl);
+ if (ret != -E2BIG) {
+ return ret;
+ }
+ rlp = g_malloc(sizeof(struct kvm_reg_list) + rl.n * sizeof(uint64_t));
+ rlp->n = rl.n;
+ ret = kvm_vcpu_ioctl(cs, KVM_GET_REG_LIST, rlp);
+ if (ret) {
+ goto out;
+ }
+ /* Sort the list we get back from the kernel, since cpreg_tuples
+ * must be in strictly ascending order.
+ */
+ qsort(&rlp->reg, rlp->n, sizeof(rlp->reg[0]), compare_u64);
+
+ for (i = 0, arraylen = 0; i < rlp->n; i++) {
+ if (!reg_syncs_via_tuple_list(rlp->reg[i])) {
+ continue;
+ }
+ switch (rlp->reg[i] & KVM_REG_SIZE_MASK) {
+ case KVM_REG_SIZE_U32:
+ case KVM_REG_SIZE_U64:
+ break;
+ default:
+ fprintf(stderr, "Can't handle size of register in kernel list\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ arraylen++;
+ }
+
+ cpu->cpreg_indexes = g_renew(uint64_t, cpu->cpreg_indexes, arraylen);
+ cpu->cpreg_values = g_renew(uint64_t, cpu->cpreg_values, arraylen);
+ cpu->cpreg_vmstate_indexes = g_renew(uint64_t, cpu->cpreg_vmstate_indexes,
+ arraylen);
+ cpu->cpreg_vmstate_values = g_renew(uint64_t, cpu->cpreg_vmstate_values,
+ arraylen);
+ cpu->cpreg_array_len = arraylen;
+ cpu->cpreg_vmstate_array_len = arraylen;
+
+ for (i = 0, arraylen = 0; i < rlp->n; i++) {
+ uint64_t regidx = rlp->reg[i];
+ if (!reg_syncs_via_tuple_list(regidx)) {
+ continue;
+ }
+ cpu->cpreg_indexes[arraylen] = regidx;
+ arraylen++;
+ }
+ assert(cpu->cpreg_array_len == arraylen);
+
+ if (!write_kvmstate_to_list(cpu)) {
+ /* Shouldn't happen unless kernel is inconsistent about
+ * what registers exist.
+ */
+ fprintf(stderr, "Initial read of kernel register state failed\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Save a copy of the initial register values so that we can
+ * feed it back to the kernel on VCPU reset.
+ */
+ cpu->cpreg_reset_values = g_memdup(cpu->cpreg_values,
+ cpu->cpreg_array_len *
+ sizeof(cpu->cpreg_values[0]));
+
+out:
+ g_free(rlp);
+ return ret;
+}
+
+typedef struct Reg {
+ uint64_t id;
+ int offset;
+} Reg;
+
+#define COREREG(KERNELNAME, QEMUFIELD) \
+ { \
+ KVM_REG_ARM | KVM_REG_SIZE_U32 | \
+ KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(KERNELNAME), \
+ offsetof(CPUARMState, QEMUFIELD) \
+ }
+
+#define VFPSYSREG(R) \
+ { \
+ KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP | \
+ KVM_REG_ARM_VFP_##R, \
+ offsetof(CPUARMState, vfp.xregs[ARM_VFP_##R]) \
+ }
+
+static const Reg regs[] = {
+ /* R0_usr .. R14_usr */
+ COREREG(usr_regs.uregs[0], regs[0]),
+ COREREG(usr_regs.uregs[1], regs[1]),
+ COREREG(usr_regs.uregs[2], regs[2]),
+ COREREG(usr_regs.uregs[3], regs[3]),
+ COREREG(usr_regs.uregs[4], regs[4]),
+ COREREG(usr_regs.uregs[5], regs[5]),
+ COREREG(usr_regs.uregs[6], regs[6]),
+ COREREG(usr_regs.uregs[7], regs[7]),
+ COREREG(usr_regs.uregs[8], usr_regs[0]),
+ COREREG(usr_regs.uregs[9], usr_regs[1]),
+ COREREG(usr_regs.uregs[10], usr_regs[2]),
+ COREREG(usr_regs.uregs[11], usr_regs[3]),
+ COREREG(usr_regs.uregs[12], usr_regs[4]),
+ COREREG(usr_regs.uregs[13], banked_r13[0]),
+ COREREG(usr_regs.uregs[14], banked_r14[0]),
+ /* R13, R14, SPSR for SVC, ABT, UND, IRQ banks */
+ COREREG(svc_regs[0], banked_r13[1]),
+ COREREG(svc_regs[1], banked_r14[1]),
+ COREREG(svc_regs[2], banked_spsr[1]),
+ COREREG(abt_regs[0], banked_r13[2]),
+ COREREG(abt_regs[1], banked_r14[2]),
+ COREREG(abt_regs[2], banked_spsr[2]),
+ COREREG(und_regs[0], banked_r13[3]),
+ COREREG(und_regs[1], banked_r14[3]),
+ COREREG(und_regs[2], banked_spsr[3]),
+ COREREG(irq_regs[0], banked_r13[4]),
+ COREREG(irq_regs[1], banked_r14[4]),
+ COREREG(irq_regs[2], banked_spsr[4]),
+ /* R8_fiq .. R14_fiq and SPSR_fiq */
+ COREREG(fiq_regs[0], fiq_regs[0]),
+ COREREG(fiq_regs[1], fiq_regs[1]),
+ COREREG(fiq_regs[2], fiq_regs[2]),
+ COREREG(fiq_regs[3], fiq_regs[3]),
+ COREREG(fiq_regs[4], fiq_regs[4]),
+ COREREG(fiq_regs[5], banked_r13[5]),
+ COREREG(fiq_regs[6], banked_r14[5]),
+ COREREG(fiq_regs[7], banked_spsr[5]),
+ /* R15 */
+ COREREG(usr_regs.uregs[15], regs[15]),
+ /* VFP system registers */
+ VFPSYSREG(FPSID),
+ VFPSYSREG(MVFR1),
+ VFPSYSREG(MVFR0),
+ VFPSYSREG(FPEXC),
+ VFPSYSREG(FPINST),
+ VFPSYSREG(FPINST2),
+};
+
+int kvm_arch_put_registers(CPUState *cs, int level)
+{
+ ARMCPU *cpu = ARM_CPU(cs);
+ CPUARMState *env = &cpu->env;
+ struct kvm_one_reg r;
+ int mode, bn;
+ int ret, i;
+ uint32_t cpsr, fpscr;
+
+ /* Make sure the banked regs are properly set */
+ mode = env->uncached_cpsr & CPSR_M;
+ bn = bank_number(mode);
+ if (mode == ARM_CPU_MODE_FIQ) {
+ memcpy(env->fiq_regs, env->regs + 8, 5 * sizeof(uint32_t));
+ } else {
+ memcpy(env->usr_regs, env->regs + 8, 5 * sizeof(uint32_t));
+ }
+ env->banked_r13[bn] = env->regs[13];
+ env->banked_r14[bn] = env->regs[14];
+ env->banked_spsr[bn] = env->spsr;
+
+ /* Now we can safely copy stuff down to the kernel */
+ for (i = 0; i < ARRAY_SIZE(regs); i++) {
+ r.id = regs[i].id;
+ r.addr = (uintptr_t)(env) + regs[i].offset;
+ ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ /* Special cases which aren't a single CPUARMState field */
+ cpsr = cpsr_read(env);
+ r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 |
+ KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(usr_regs.ARM_cpsr);
+ r.addr = (uintptr_t)(&cpsr);
+ ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
+ if (ret) {
+ return ret;
+ }
+
+ /* VFP registers */
+ r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP;
+ for (i = 0; i < 32; i++) {
+ r.addr = (uintptr_t)(&env->vfp.regs[i]);
+ ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
+ if (ret) {
+ return ret;
+ }
+ r.id++;
+ }
+
+ r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP |
+ KVM_REG_ARM_VFP_FPSCR;
+ fpscr = vfp_get_fpscr(env);
+ r.addr = (uintptr_t)&fpscr;
+ ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
+ if (ret) {
+ return ret;
+ }
+
+ /* Note that we do not call write_cpustate_to_list()
+ * here, so we are only writing the tuple list back to
+ * KVM. This is safe because nothing can change the
+ * CPUARMState cp15 fields (in particular gdb accesses cannot)
+ * and so there are no changes to sync. In fact syncing would
+ * be wrong at this point: for a constant register where TCG and
+ * KVM disagree about its value, the preceding write_list_to_cpustate()
+ * would not have had any effect on the CPUARMState value (since the
+ * register is read-only), and a write_cpustate_to_list() here would
+ * then try to write the TCG value back into KVM -- this would either
+ * fail or incorrectly change the value the guest sees.
+ *
+ * If we ever want to allow the user to modify cp15 registers via
+ * the gdb stub, we would need to be more clever here (for instance
+ * tracking the set of registers kvm_arch_get_registers() successfully
+ * managed to update the CPUARMState with, and only allowing those
+ * to be written back up into the kernel).
+ */
+ if (!write_list_to_kvmstate(cpu)) {
+ return EINVAL;
+ }
+
+ return ret;
+}
+
+int kvm_arch_get_registers(CPUState *cs)
+{
+ ARMCPU *cpu = ARM_CPU(cs);
+ CPUARMState *env = &cpu->env;
+ struct kvm_one_reg r;
+ int mode, bn;
+ int ret, i;
+ uint32_t cpsr, fpscr;
+
+ for (i = 0; i < ARRAY_SIZE(regs); i++) {
+ r.id = regs[i].id;
+ r.addr = (uintptr_t)(env) + regs[i].offset;
+ ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ /* Special cases which aren't a single CPUARMState field */
+ r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 |
+ KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(usr_regs.ARM_cpsr);
+ r.addr = (uintptr_t)(&cpsr);
+ ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
+ if (ret) {
+ return ret;
+ }
+ cpsr_write(env, cpsr, 0xffffffff);
+
+ /* Make sure the current mode regs are properly set */
+ mode = env->uncached_cpsr & CPSR_M;
+ bn = bank_number(mode);
+ if (mode == ARM_CPU_MODE_FIQ) {
+ memcpy(env->regs + 8, env->fiq_regs, 5 * sizeof(uint32_t));
+ } else {
+ memcpy(env->regs + 8, env->usr_regs, 5 * sizeof(uint32_t));
+ }
+ env->regs[13] = env->banked_r13[bn];
+ env->regs[14] = env->banked_r14[bn];
+ env->spsr = env->banked_spsr[bn];
+
+ /* VFP registers */
+ r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP;
+ for (i = 0; i < 32; i++) {
+ r.addr = (uintptr_t)(&env->vfp.regs[i]);
+ ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
+ if (ret) {
+ return ret;
+ }
+ r.id++;
+ }
+
+ r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP |
+ KVM_REG_ARM_VFP_FPSCR;
+ r.addr = (uintptr_t)&fpscr;
+ ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
+ if (ret) {
+ return ret;
+ }
+ vfp_set_fpscr(env, fpscr);
+
+ if (!write_kvmstate_to_list(cpu)) {
+ return EINVAL;
+ }
+ /* Note that it's OK to have registers which aren't in CPUState,
+ * so we can ignore a failure return here.
+ */
+ write_list_to_cpustate(cpu);
+
+ return 0;
+}
+
+void kvm_arch_reset_vcpu(CPUState *cs)
+{
+ /* Feed the kernel back its initial register state */
+ ARMCPU *cpu = ARM_CPU(cs);
+
+ memmove(cpu->cpreg_values, cpu->cpreg_reset_values,
+ cpu->cpreg_array_len * sizeof(cpu->cpreg_values[0]));
+
+ if (!write_list_to_kvmstate(cpu)) {
+ abort();
+ }
+}
--
1.8.5
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [Qemu-devel] [PATCH v2 2/7] target-arm: Clean up handling of AArch64 PSTATE
2013-12-17 12:15 [Qemu-devel] [PATCH v2 0/7] target-arm: Support AArch64 KVM Peter Maydell
2013-12-17 12:15 ` [Qemu-devel] [PATCH v2 1/7] target-arm/kvm: Split 32 bit only code into its own file Peter Maydell
@ 2013-12-17 12:15 ` Peter Maydell
2013-12-17 12:15 ` [Qemu-devel] [PATCH v2 3/7] target-arm: Add minimal KVM AArch64 support Peter Maydell
` (5 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Peter Maydell @ 2013-12-17 12:15 UTC (permalink / raw)
To: qemu-devel; +Cc: kvmarm, Christoffer Dall, patches
The env->pstate field is a little odd since it doesn't strictly
speaking represent an architectural register. However it's convenient
for QEMU to use it to hold the various PSTATE architectural bits
in the same format the architecture specifies for SPSR registers
(since this is the same format the kernel uses for signal handlers
and the KVM register). Add some structure to how we deal with it:
* document what env->pstate is
* add some #defines for various bits in it
* add helpers for reading/writing it taking account of caching
of NZCV, and use them where appropriate
* reset it on startup
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 1385645602-18662-3-git-send-email-peter.maydell@linaro.org
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
---
linux-user/signal.c | 6 ++--
target-arm/cpu.c | 6 ++++
target-arm/cpu.h | 70 ++++++++++++++++++++++++++++++++++++++--------
target-arm/gdbstub64.c | 4 +--
target-arm/translate-a64.c | 12 ++++----
5 files changed, 78 insertions(+), 20 deletions(-)
diff --git a/linux-user/signal.c b/linux-user/signal.c
index 7751c47..4e7148a 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -1171,7 +1171,7 @@ static int target_setup_sigframe(struct target_rt_sigframe *sf,
}
__put_user(env->xregs[31], &sf->uc.tuc_mcontext.sp);
__put_user(env->pc, &sf->uc.tuc_mcontext.pc);
- __put_user(env->pstate, &sf->uc.tuc_mcontext.pstate);
+ __put_user(pstate_read(env), &sf->uc.tuc_mcontext.pstate);
__put_user(/*current->thread.fault_address*/ 0,
&sf->uc.tuc_mcontext.fault_address);
@@ -1210,6 +1210,7 @@ static int target_restore_sigframe(CPUARMState *env,
struct target_aux_context *aux =
(struct target_aux_context *)sf->uc.tuc_mcontext.__reserved;
uint32_t magic, size;
+ uint64_t pstate;
target_to_host_sigset(&set, &sf->uc.tuc_sigmask);
sigprocmask(SIG_SETMASK, &set, NULL);
@@ -1220,7 +1221,8 @@ static int target_restore_sigframe(CPUARMState *env,
__get_user(env->xregs[31], &sf->uc.tuc_mcontext.sp);
__get_user(env->pc, &sf->uc.tuc_mcontext.pc);
- __get_user(env->pstate, &sf->uc.tuc_mcontext.pstate);
+ __get_user(pstate, &sf->uc.tuc_mcontext.pstate);
+ pstate_write(env, pstate);
__get_user(magic, &aux->fpsimd.head.magic);
__get_user(size, &aux->fpsimd.head.size);
diff --git a/target-arm/cpu.c b/target-arm/cpu.c
index 0635e78..42057ad 100644
--- a/target-arm/cpu.c
+++ b/target-arm/cpu.c
@@ -88,6 +88,12 @@ static void arm_cpu_reset(CPUState *s)
if (arm_feature(env, ARM_FEATURE_AARCH64)) {
/* 64 bit CPUs always start in 64 bit mode */
env->aarch64 = 1;
+#if defined(CONFIG_USER_ONLY)
+ env->pstate = PSTATE_MODE_EL0t;
+#else
+ env->pstate = PSTATE_D | PSTATE_A | PSTATE_I | PSTATE_F
+ | PSTATE_MODE_EL1h;
+#endif
}
#if defined(CONFIG_USER_ONLY)
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index c3f007f..d15fdcd 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -113,8 +113,15 @@ typedef struct CPUARMState {
/* Regs for A64 mode. */
uint64_t xregs[32];
uint64_t pc;
- /* TODO: pstate doesn't correspond to an architectural register;
- * it would be better modelled as the underlying fields.
+ /* PSTATE isn't an architectural register for ARMv8. However, it is
+ * convenient for us to assemble the underlying state into a 32 bit format
+ * identical to the architectural format used for the SPSR. (This is also
+ * what the Linux kernel's 'pstate' field in signal handlers and KVM's
+ * 'pstate' register are.) Of the PSTATE bits:
+ * NZCV are kept in the split out env->CF/VF/NF/ZF, (which have the same
+ * semantics as for AArch32, as described in the comments on each field)
+ * nRW (also known as M[4]) is kept, inverted, in env->aarch64
+ * all other bits are stored in their correct places in env->pstate
*/
uint32_t pstate;
uint32_t aarch64; /* 1 if CPU is in aarch64 state; inverse of PSTATE.nRW */
@@ -309,15 +316,6 @@ static inline bool is_a64(CPUARMState *env)
return env->aarch64;
}
-#define PSTATE_N_SHIFT 3
-#define PSTATE_N (1 << PSTATE_N_SHIFT)
-#define PSTATE_Z_SHIFT 2
-#define PSTATE_Z (1 << PSTATE_Z_SHIFT)
-#define PSTATE_C_SHIFT 1
-#define PSTATE_C (1 << PSTATE_C_SHIFT)
-#define PSTATE_V_SHIFT 0
-#define PSTATE_V (1 << PSTATE_V_SHIFT)
-
/* you can call this signal handler from your SIGBUS and SIGSEGV
signal handlers to inform the virtual CPU of exceptions. non zero
is returned if the signal was handled by the virtual CPU. */
@@ -352,6 +350,56 @@ int cpu_arm_handle_mmu_fault (CPUARMState *env, target_ulong address, int rw,
/* Execution state bits. MRS read as zero, MSR writes ignored. */
#define CPSR_EXEC (CPSR_T | CPSR_IT | CPSR_J)
+/* Bit definitions for ARMv8 SPSR (PSTATE) format.
+ * Only these are valid when in AArch64 mode; in
+ * AArch32 mode SPSRs are basically CPSR-format.
+ */
+#define PSTATE_M (0xFU)
+#define PSTATE_nRW (1U << 4)
+#define PSTATE_F (1U << 6)
+#define PSTATE_I (1U << 7)
+#define PSTATE_A (1U << 8)
+#define PSTATE_D (1U << 9)
+#define PSTATE_IL (1U << 20)
+#define PSTATE_SS (1U << 21)
+#define PSTATE_V (1U << 28)
+#define PSTATE_C (1U << 29)
+#define PSTATE_Z (1U << 30)
+#define PSTATE_N (1U << 31)
+#define PSTATE_NZCV (PSTATE_N | PSTATE_Z | PSTATE_C | PSTATE_V)
+#define CACHED_PSTATE_BITS (PSTATE_NZCV)
+/* Mode values for AArch64 */
+#define PSTATE_MODE_EL3h 13
+#define PSTATE_MODE_EL3t 12
+#define PSTATE_MODE_EL2h 9
+#define PSTATE_MODE_EL2t 8
+#define PSTATE_MODE_EL1h 5
+#define PSTATE_MODE_EL1t 4
+#define PSTATE_MODE_EL0t 0
+
+/* Return the current PSTATE value. For the moment we don't support 32<->64 bit
+ * interprocessing, so we don't attempt to sync with the cpsr state used by
+ * the 32 bit decoder.
+ */
+static inline uint32_t pstate_read(CPUARMState *env)
+{
+ int ZF;
+
+ ZF = (env->ZF == 0);
+ return (env->NF & 0x80000000) | (ZF << 30)
+ | (env->CF << 29) | ((env->VF & 0x80000000) >> 3)
+ | env->pstate;
+}
+
+static inline void pstate_write(CPUARMState *env, uint32_t val)
+{
+ env->ZF = (~val) & PSTATE_Z;
+ env->NF = val;
+ env->CF = (val >> 29) & 1;
+ env->VF = (val << 3) & 0x80000000;
+ env->pstate = val & ~CACHED_PSTATE_BITS;
+}
+
/* Return the current CPSR value. */
uint32_t cpsr_read(CPUARMState *env);
/* Set the CPSR. Note that some bits of mask must be all-set or all-clear. */
diff --git a/target-arm/gdbstub64.c b/target-arm/gdbstub64.c
index 7cb6a7c..e8a8295 100644
--- a/target-arm/gdbstub64.c
+++ b/target-arm/gdbstub64.c
@@ -37,7 +37,7 @@ int aarch64_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n)
return gdb_get_reg64(mem_buf, env->pc);
break;
case 33:
- return gdb_get_reg32(mem_buf, env->pstate);
+ return gdb_get_reg32(mem_buf, pstate_read(env));
}
/* Unknown register. */
return 0;
@@ -65,7 +65,7 @@ int aarch64_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
return 8;
case 33:
/* CPSR */
- env->pstate = tmp;
+ pstate_write(env, tmp);
return 4;
}
/* Unknown register. */
diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c
index f120088..932b601 100644
--- a/target-arm/translate-a64.c
+++ b/target-arm/translate-a64.c
@@ -67,6 +67,7 @@ void aarch64_cpu_dump_state(CPUState *cs, FILE *f,
{
ARMCPU *cpu = ARM_CPU(cs);
CPUARMState *env = &cpu->env;
+ uint32_t psr = pstate_read(env);
int i;
cpu_fprintf(f, "PC=%016"PRIx64" SP=%016"PRIx64"\n",
@@ -79,11 +80,12 @@ void aarch64_cpu_dump_state(CPUState *cs, FILE *f,
cpu_fprintf(f, " ");
}
}
- cpu_fprintf(f, "PSTATE=%c%c%c%c\n",
- env->pstate & PSTATE_N ? 'n' : '.',
- env->pstate & PSTATE_Z ? 'z' : '.',
- env->pstate & PSTATE_C ? 'c' : '.',
- env->pstate & PSTATE_V ? 'v' : '.');
+ cpu_fprintf(f, "PSTATE=%08x (flags %c%c%c%c)\n",
+ psr,
+ psr & PSTATE_N ? 'N' : '-',
+ psr & PSTATE_Z ? 'Z' : '-',
+ psr & PSTATE_C ? 'C' : '-',
+ psr & PSTATE_V ? 'V' : '-');
cpu_fprintf(f, "\n");
}
--
1.8.5
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [Qemu-devel] [PATCH v2 3/7] target-arm: Add minimal KVM AArch64 support
2013-12-17 12:15 [Qemu-devel] [PATCH v2 0/7] target-arm: Support AArch64 KVM Peter Maydell
2013-12-17 12:15 ` [Qemu-devel] [PATCH v2 1/7] target-arm/kvm: Split 32 bit only code into its own file Peter Maydell
2013-12-17 12:15 ` [Qemu-devel] [PATCH v2 2/7] target-arm: Clean up handling of AArch64 PSTATE Peter Maydell
@ 2013-12-17 12:15 ` Peter Maydell
2013-12-17 12:15 ` [Qemu-devel] [PATCH v2 4/7] configure: Enable KVM for aarch64 host/target combination Peter Maydell
` (4 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Peter Maydell @ 2013-12-17 12:15 UTC (permalink / raw)
To: qemu-devel; +Cc: kvmarm, Christoffer Dall, patches
From: "Mian M. Hamayun" <m.hamayun@virtualopensystems.com>
Add the bare minimum set of functions needed for control of an
AArch64 KVM vcpu:
* CPU initialization
* minimal get/put register functions which only handle the
basic state of the CPU
Signed-off-by: Mian M. Hamayun <m.hamayun@virtualopensystems.com>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 1385645602-18662-4-git-send-email-peter.maydell@linaro.org
[PMM: significantly overhauled; most notably:
* code lives in kvm64.c rather than using #ifdefs
* support '-cpu host' rather than implicitly using whatever the
host's CPU is regardless of what the user requests
* fix bug attempting to get/set nonexistent X[31]
* fix bug writing 64 bit kernel pstate into uint32_t env field
]
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
---
target-arm/Makefile.objs | 1 +
target-arm/kvm.c | 4 +
target-arm/kvm64.c | 204 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 209 insertions(+)
create mode 100644 target-arm/kvm64.c
diff --git a/target-arm/Makefile.objs b/target-arm/Makefile.objs
index d1db77c..5493a4c 100644
--- a/target-arm/Makefile.objs
+++ b/target-arm/Makefile.objs
@@ -7,3 +7,4 @@ obj-y += neon_helper.o iwmmxt_helper.o
obj-y += gdbstub.o
obj-$(TARGET_AARCH64) += cpu64.o translate-a64.o gdbstub64.o
obj-$(call land,$(CONFIG_KVM),$(call lnot,$(TARGET_AARCH64))) += kvm32.o
+obj-$(call land,$(CONFIG_KVM),$(TARGET_AARCH64)) += kvm64.o
diff --git a/target-arm/kvm.c b/target-arm/kvm.c
index 5cdb3b9..1d2688d 100644
--- a/target-arm/kvm.c
+++ b/target-arm/kvm.c
@@ -128,7 +128,11 @@ static void kvm_arm_host_cpu_initfn(Object *obj)
static const TypeInfo host_arm_cpu_type_info = {
.name = TYPE_ARM_HOST_CPU,
+#ifdef TARGET_AARCH64
+ .parent = TYPE_AARCH64_CPU,
+#else
.parent = TYPE_ARM_CPU,
+#endif
.instance_init = kvm_arm_host_cpu_initfn,
.class_init = kvm_arm_host_cpu_class_init,
.class_size = sizeof(ARMHostCPUClass),
diff --git a/target-arm/kvm64.c b/target-arm/kvm64.c
new file mode 100644
index 0000000..1b7ca90
--- /dev/null
+++ b/target-arm/kvm64.c
@@ -0,0 +1,204 @@
+/*
+ * ARM implementation of KVM hooks, 64 bit specific code
+ *
+ * Copyright Mian-M. Hamayun 2013, Virtual Open Systems
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+
+#include <linux/kvm.h>
+
+#include "qemu-common.h"
+#include "qemu/timer.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/kvm.h"
+#include "kvm_arm.h"
+#include "cpu.h"
+#include "hw/arm/arm.h"
+
+static inline void set_feature(uint64_t *features, int feature)
+{
+ *features |= 1ULL << feature;
+}
+
+bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc)
+{
+ /* Identify the feature bits corresponding to the host CPU, and
+ * fill out the ARMHostCPUClass fields accordingly. To do this
+ * we have to create a scratch VM, create a single CPU inside it,
+ * and then query that CPU for the relevant ID registers.
+ * For AArch64 we currently don't care about ID registers at
+ * all; we just want to know the CPU type.
+ */
+ int fdarray[3];
+ uint64_t features = 0;
+ /* Old kernels may not know about the PREFERRED_TARGET ioctl: however
+ * we know these will only support creating one kind of guest CPU,
+ * which is its preferred CPU type. Fortunately these old kernels
+ * support only a very limited number of CPUs.
+ */
+ static const uint32_t cpus_to_try[] = {
+ KVM_ARM_TARGET_AEM_V8,
+ KVM_ARM_TARGET_FOUNDATION_V8,
+ KVM_ARM_TARGET_CORTEX_A57,
+ QEMU_KVM_ARM_TARGET_NONE
+ };
+ struct kvm_vcpu_init init;
+
+ if (!kvm_arm_create_scratch_host_vcpu(cpus_to_try, fdarray, &init)) {
+ return false;
+ }
+
+ ahcc->target = init.target;
+ ahcc->dtb_compatible = "arm,arm-v8";
+
+ kvm_arm_destroy_scratch_host_vcpu(fdarray);
+
+ /* We can assume any KVM supporting CPU is at least a v8
+ * with VFPv4+Neon; this in turn implies most of the other
+ * feature bits.
+ */
+ set_feature(&features, ARM_FEATURE_V8);
+ set_feature(&features, ARM_FEATURE_VFP4);
+ set_feature(&features, ARM_FEATURE_NEON);
+ set_feature(&features, ARM_FEATURE_AARCH64);
+
+ ahcc->features = features;
+
+ return true;
+}
+
+int kvm_arch_init_vcpu(CPUState *cs)
+{
+ ARMCPU *cpu = ARM_CPU(cs);
+ struct kvm_vcpu_init init;
+ int ret;
+
+ if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE ||
+ !arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
+ fprintf(stderr, "KVM is not supported for this guest CPU type\n");
+ return -EINVAL;
+ }
+
+ init.target = cpu->kvm_target;
+ memset(init.features, 0, sizeof(init.features));
+ if (cpu->start_powered_off) {
+ init.features[0] = 1 << KVM_ARM_VCPU_POWER_OFF;
+ }
+ ret = kvm_vcpu_ioctl(cs, KVM_ARM_VCPU_INIT, &init);
+
+ /* TODO : support for save/restore/reset of system regs via tuple list */
+
+ return ret;
+}
+
+#define AARCH64_CORE_REG(x) (KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \
+ KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(x))
+
+int kvm_arch_put_registers(CPUState *cs, int level)
+{
+ struct kvm_one_reg reg;
+ uint64_t val;
+ int i;
+ int ret;
+
+ ARMCPU *cpu = ARM_CPU(cs);
+ CPUARMState *env = &cpu->env;
+
+ for (i = 0; i < 31; i++) {
+ reg.id = AARCH64_CORE_REG(regs.regs[i]);
+ reg.addr = (uintptr_t) &env->xregs[i];
+ ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ reg.id = AARCH64_CORE_REG(regs.sp);
+ reg.addr = (uintptr_t) &env->xregs[31];
+ ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®);
+ if (ret) {
+ return ret;
+ }
+
+ /* Note that KVM thinks pstate is 64 bit but we use a uint32_t */
+ val = pstate_read(env);
+ reg.id = AARCH64_CORE_REG(regs.pstate);
+ reg.addr = (uintptr_t) &val;
+ ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®);
+ if (ret) {
+ return ret;
+ }
+
+ reg.id = AARCH64_CORE_REG(regs.pc);
+ reg.addr = (uintptr_t) &env->pc;
+ ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®);
+ if (ret) {
+ return ret;
+ }
+
+ /* TODO:
+ * SP_EL1
+ * ELR_EL1
+ * SPSR[]
+ * FP state
+ * system registers
+ */
+ return ret;
+}
+
+int kvm_arch_get_registers(CPUState *cs)
+{
+ struct kvm_one_reg reg;
+ uint64_t val;
+ int i;
+ int ret;
+
+ ARMCPU *cpu = ARM_CPU(cs);
+ CPUARMState *env = &cpu->env;
+
+ for (i = 0; i < 31; i++) {
+ reg.id = AARCH64_CORE_REG(regs.regs[i]);
+ reg.addr = (uintptr_t) &env->xregs[i];
+ ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ reg.id = AARCH64_CORE_REG(regs.sp);
+ reg.addr = (uintptr_t) &env->xregs[31];
+ ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®);
+ if (ret) {
+ return ret;
+ }
+
+ reg.id = AARCH64_CORE_REG(regs.pstate);
+ reg.addr = (uintptr_t) &val;
+ ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®);
+ if (ret) {
+ return ret;
+ }
+ pstate_write(env, val);
+
+ reg.id = AARCH64_CORE_REG(regs.pc);
+ reg.addr = (uintptr_t) &env->pc;
+ ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®);
+ if (ret) {
+ return ret;
+ }
+
+ /* TODO: other registers */
+ return ret;
+}
+
+void kvm_arch_reset_vcpu(CPUState *cs)
+{
+}
--
1.8.5
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [Qemu-devel] [PATCH v2 4/7] configure: Enable KVM for aarch64 host/target combination
2013-12-17 12:15 [Qemu-devel] [PATCH v2 0/7] target-arm: Support AArch64 KVM Peter Maydell
` (2 preceding siblings ...)
2013-12-17 12:15 ` [Qemu-devel] [PATCH v2 3/7] target-arm: Add minimal KVM AArch64 support Peter Maydell
@ 2013-12-17 12:15 ` Peter Maydell
2013-12-17 12:15 ` [Qemu-devel] [PATCH v2 5/7] hw/arm/boot: Allow easier swapping in of different loader code Peter Maydell
` (3 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Peter Maydell @ 2013-12-17 12:15 UTC (permalink / raw)
To: qemu-devel; +Cc: kvmarm, Christoffer Dall, patches
Enable KVM if the host and target CPU are both aarch64. Note
that host aarch64 + target arm is not valid for KVM acceleration:
the 64 bit kernel does not support the ioctl interface for
32 bit CPUs. 32 bit VMs on 64 bit hosts need to be created
using the 64 bit ioctl interface; when QEMU supports this it
will be on the arch64-softmmu target with a -cpu parameter for
a 32 bit CPU, which is still an aarch64/aarch64 combination
as far as configure is concerned.
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 1385645602-18662-5-git-send-email-peter.maydell@linaro.org
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
---
configure | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/configure b/configure
index edfea95..02c94e2 100755
--- a/configure
+++ b/configure
@@ -4550,7 +4550,7 @@ case "$target_name" in
*)
esac
case "$target_name" in
- arm|i386|x86_64|ppcemb|ppc|ppc64|s390x)
+ aarch64|arm|i386|x86_64|ppcemb|ppc|ppc64|s390x)
# Make sure the target and host cpus are compatible
if test "$kvm" = "yes" -a "$target_softmmu" = "yes" -a \
\( "$target_name" = "$cpu" -o \
--
1.8.5
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [Qemu-devel] [PATCH v2 5/7] hw/arm/boot: Allow easier swapping in of different loader code
2013-12-17 12:15 [Qemu-devel] [PATCH v2 0/7] target-arm: Support AArch64 KVM Peter Maydell
` (3 preceding siblings ...)
2013-12-17 12:15 ` [Qemu-devel] [PATCH v2 4/7] configure: Enable KVM for aarch64 host/target combination Peter Maydell
@ 2013-12-17 12:15 ` Peter Maydell
2013-12-17 12:15 ` [Qemu-devel] [PATCH v2 6/7] hw/arm/boot: Add boot support for AArch64 processor Peter Maydell
` (2 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Peter Maydell @ 2013-12-17 12:15 UTC (permalink / raw)
To: qemu-devel; +Cc: kvmarm, Christoffer Dall, patches
For AArch64 we will obviously require a different set of
primary and secondary boot loader code fragments. However currently
we hardcode the offsets into the loader code where we must write
the entrypoint and other data into arm_load_kernel(). This makes it
hard to substitute a different loader fragment, so switch to a more
flexible scheme where instead of a raw array of instructions we use
an array of (instruction, fixup-type) pairs that indicate which
words need special action or data written into them.
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 1385645602-18662-6-git-send-email-peter.maydell@linaro.org
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
Reviewed-by: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
---
hw/arm/boot.c | 152 +++++++++++++++++++++++++++++++++++++++++-----------------
1 file changed, 107 insertions(+), 45 deletions(-)
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index 55d552f..0c05a64 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -20,15 +20,33 @@
#define KERNEL_ARGS_ADDR 0x100
#define KERNEL_LOAD_ADDR 0x00010000
+typedef enum {
+ FIXUP_NONE = 0, /* do nothing */
+ FIXUP_TERMINATOR, /* end of insns */
+ FIXUP_BOARDID, /* overwrite with board ID number */
+ FIXUP_ARGPTR, /* overwrite with pointer to kernel args */
+ FIXUP_ENTRYPOINT, /* overwrite with kernel entry point */
+ FIXUP_GIC_CPU_IF, /* overwrite with GIC CPU interface address */
+ FIXUP_BOOTREG, /* overwrite with boot register address */
+ FIXUP_DSB, /* overwrite with correct DSB insn for cpu */
+ FIXUP_MAX,
+} FixupType;
+
+typedef struct ARMInsnFixup {
+ uint32_t insn;
+ FixupType fixup;
+} ARMInsnFixup;
+
/* The worlds second smallest bootloader. Set r0-r2, then jump to kernel. */
-static uint32_t bootloader[] = {
- 0xe3a00000, /* mov r0, #0 */
- 0xe59f1004, /* ldr r1, [pc, #4] */
- 0xe59f2004, /* ldr r2, [pc, #4] */
- 0xe59ff004, /* ldr pc, [pc, #4] */
- 0, /* Board ID */
- 0, /* Address of kernel args. Set by integratorcp_init. */
- 0 /* Kernel entry point. Set by integratorcp_init. */
+static const ARMInsnFixup bootloader[] = {
+ { 0xe3a00000 }, /* mov r0, #0 */
+ { 0xe59f1004 }, /* ldr r1, [pc, #4] */
+ { 0xe59f2004 }, /* ldr r2, [pc, #4] */
+ { 0xe59ff004 }, /* ldr pc, [pc, #4] */
+ { 0, FIXUP_BOARDID },
+ { 0, FIXUP_ARGPTR },
+ { 0, FIXUP_ENTRYPOINT },
+ { 0, FIXUP_TERMINATOR }
};
/* Handling for secondary CPU boot in a multicore system.
@@ -48,39 +66,83 @@ static uint32_t bootloader[] = {
#define DSB_INSN 0xf57ff04f
#define CP15_DSB_INSN 0xee070f9a /* mcr cp15, 0, r0, c7, c10, 4 */
-static uint32_t smpboot[] = {
- 0xe59f2028, /* ldr r2, gic_cpu_if */
- 0xe59f0028, /* ldr r0, startaddr */
- 0xe3a01001, /* mov r1, #1 */
- 0xe5821000, /* str r1, [r2] - set GICC_CTLR.Enable */
- 0xe3a010ff, /* mov r1, #0xff */
- 0xe5821004, /* str r1, [r2, 4] - set GIC_PMR.Priority to 0xff */
- DSB_INSN, /* dsb */
- 0xe320f003, /* wfi */
- 0xe5901000, /* ldr r1, [r0] */
- 0xe1110001, /* tst r1, r1 */
- 0x0afffffb, /* beq <wfi> */
- 0xe12fff11, /* bx r1 */
- 0, /* gic_cpu_if: base address of GIC CPU interface */
- 0 /* bootreg: Boot register address is held here */
+static const ARMInsnFixup smpboot[] = {
+ { 0xe59f2028 }, /* ldr r2, gic_cpu_if */
+ { 0xe59f0028 }, /* ldr r0, bootreg_addr */
+ { 0xe3a01001 }, /* mov r1, #1 */
+ { 0xe5821000 }, /* str r1, [r2] - set GICC_CTLR.Enable */
+ { 0xe3a010ff }, /* mov r1, #0xff */
+ { 0xe5821004 }, /* str r1, [r2, 4] - set GIC_PMR.Priority to 0xff */
+ { 0, FIXUP_DSB }, /* dsb */
+ { 0xe320f003 }, /* wfi */
+ { 0xe5901000 }, /* ldr r1, [r0] */
+ { 0xe1110001 }, /* tst r1, r1 */
+ { 0x0afffffb }, /* beq <wfi> */
+ { 0xe12fff11 }, /* bx r1 */
+ { 0, FIXUP_GIC_CPU_IF }, /* gic_cpu_if: .word 0x.... */
+ { 0, FIXUP_BOOTREG }, /* bootreg_addr: .word 0x.... */
+ { 0, FIXUP_TERMINATOR }
};
+static void write_bootloader(const char *name, hwaddr addr,
+ const ARMInsnFixup *insns, uint32_t *fixupcontext)
+{
+ /* Fix up the specified bootloader fragment and write it into
+ * guest memory using rom_add_blob_fixed(). fixupcontext is
+ * an array giving the values to write in for the fixup types
+ * which write a value into the code array.
+ */
+ int i, len;
+ uint32_t *code;
+
+ len = 0;
+ while (insns[len].fixup != FIXUP_TERMINATOR) {
+ len++;
+ }
+
+ code = g_new0(uint32_t, len);
+
+ for (i = 0; i < len; i++) {
+ uint32_t insn = insns[i].insn;
+ FixupType fixup = insns[i].fixup;
+
+ switch (fixup) {
+ case FIXUP_NONE:
+ break;
+ case FIXUP_BOARDID:
+ case FIXUP_ARGPTR:
+ case FIXUP_ENTRYPOINT:
+ case FIXUP_GIC_CPU_IF:
+ case FIXUP_BOOTREG:
+ case FIXUP_DSB:
+ insn = fixupcontext[fixup];
+ break;
+ default:
+ abort();
+ }
+ code[i] = tswap32(insn);
+ }
+
+ rom_add_blob_fixed(name, code, len * sizeof(uint32_t), addr);
+
+ g_free(code);
+}
+
static void default_write_secondary(ARMCPU *cpu,
const struct arm_boot_info *info)
{
- int n;
- smpboot[ARRAY_SIZE(smpboot) - 1] = info->smp_bootreg_addr;
- smpboot[ARRAY_SIZE(smpboot) - 2] = info->gic_cpu_if_addr;
- for (n = 0; n < ARRAY_SIZE(smpboot); n++) {
- /* Replace DSB with the pre-v7 DSB if necessary. */
- if (!arm_feature(&cpu->env, ARM_FEATURE_V7) &&
- smpboot[n] == DSB_INSN) {
- smpboot[n] = CP15_DSB_INSN;
- }
- smpboot[n] = tswap32(smpboot[n]);
+ uint32_t fixupcontext[FIXUP_MAX];
+
+ fixupcontext[FIXUP_GIC_CPU_IF] = info->gic_cpu_if_addr;
+ fixupcontext[FIXUP_BOOTREG] = info->smp_bootreg_addr;
+ if (arm_feature(&cpu->env, ARM_FEATURE_V7)) {
+ fixupcontext[FIXUP_DSB] = DSB_INSN;
+ } else {
+ fixupcontext[FIXUP_DSB] = CP15_DSB_INSN;
}
- rom_add_blob_fixed("smpboot", smpboot, sizeof(smpboot),
- info->smp_loader_start);
+
+ write_bootloader("smpboot", info->smp_loader_start,
+ smpboot, fixupcontext);
}
static void default_reset_secondary(ARMCPU *cpu,
@@ -354,7 +416,6 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
CPUState *cs = CPU(cpu);
int kernel_size;
int initrd_size;
- int n;
int is_linux = 0;
uint64_t elf_entry;
hwaddr entry;
@@ -420,6 +481,8 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
}
info->entry = entry;
if (is_linux) {
+ uint32_t fixupcontext[FIXUP_MAX];
+
if (info->initrd_filename) {
initrd_size = load_ramdisk(info->initrd_filename,
info->initrd_start,
@@ -441,7 +504,7 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
}
info->initrd_size = initrd_size;
- bootloader[4] = info->board_id;
+ fixupcontext[FIXUP_BOARDID] = info->board_id;
/* for device tree boot, we pass the DTB directly in r2. Otherwise
* we point to the kernel args.
@@ -456,9 +519,9 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
if (load_dtb(dtb_start, info)) {
exit(1);
}
- bootloader[5] = dtb_start;
+ fixupcontext[FIXUP_ARGPTR] = dtb_start;
} else {
- bootloader[5] = info->loader_start + KERNEL_ARGS_ADDR;
+ fixupcontext[FIXUP_ARGPTR] = info->loader_start + KERNEL_ARGS_ADDR;
if (info->ram_size >= (1ULL << 32)) {
fprintf(stderr, "qemu: RAM size must be less than 4GB to boot"
" Linux kernel using ATAGS (try passing a device tree"
@@ -466,12 +529,11 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
exit(1);
}
}
- bootloader[6] = entry;
- for (n = 0; n < sizeof(bootloader) / 4; n++) {
- bootloader[n] = tswap32(bootloader[n]);
- }
- rom_add_blob_fixed("bootloader", bootloader, sizeof(bootloader),
- info->loader_start);
+ fixupcontext[FIXUP_ENTRYPOINT] = entry;
+
+ write_bootloader("bootloader", info->loader_start,
+ bootloader, fixupcontext);
+
if (info->nb_cpus > 1) {
info->write_secondary_boot(cpu, info);
}
--
1.8.5
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [Qemu-devel] [PATCH v2 6/7] hw/arm/boot: Add boot support for AArch64 processor
2013-12-17 12:15 [Qemu-devel] [PATCH v2 0/7] target-arm: Support AArch64 KVM Peter Maydell
` (4 preceding siblings ...)
2013-12-17 12:15 ` [Qemu-devel] [PATCH v2 5/7] hw/arm/boot: Allow easier swapping in of different loader code Peter Maydell
@ 2013-12-17 12:15 ` Peter Maydell
2013-12-17 13:04 ` Peter Crosthwaite
2013-12-17 12:15 ` [Qemu-devel] [PATCH v2 7/7] default-configs: Add config for aarch64-softmmu Peter Maydell
2013-12-18 6:05 ` [Qemu-devel] [PATCH v2 0/7] target-arm: Support AArch64 KVM Christoffer Dall
7 siblings, 1 reply; 13+ messages in thread
From: Peter Maydell @ 2013-12-17 12:15 UTC (permalink / raw)
To: qemu-devel; +Cc: kvmarm, Christoffer Dall, patches
From: "Mian M. Hamayun" <m.hamayun@virtualopensystems.com>
This commit adds support for booting a single AArch64 CPU by setting
appropriate registers. The bootloader includes placehoders for Board-ID
that are used to implement uniform indexing across different bootloaders.
Signed-off-by: Mian M. Hamayun <m.hamayun@virtualopensystems.com>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 1385645602-18662-7-git-send-email-peter.maydell@linaro.org
[PMM:
* updated to use ARMInsnFixup style bootloader fragments
* dropped virt.c additions
* use runtime checks for "is this an AArch64 core" rather than ifdefs
* drop some unnecessary setting of registers in reset hook
]
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
---
hw/arm/boot.c | 43 ++++++++++++++++++++++++++++++++++++++-----
1 file changed, 38 insertions(+), 5 deletions(-)
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index 0c05a64..90e9534 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -17,8 +17,13 @@
#include "sysemu/device_tree.h"
#include "qemu/config-file.h"
+/* Kernel boot protocol is specified in the kernel docs
+ * Documentation/arm/Booting and Documentation/arm64/booting.txt
+ * They have different preferred image load offsets from system RAM base.
+ */
#define KERNEL_ARGS_ADDR 0x100
#define KERNEL_LOAD_ADDR 0x00010000
+#define KERNEL64_LOAD_ADDR 0x00080000
typedef enum {
FIXUP_NONE = 0, /* do nothing */
@@ -37,6 +42,20 @@ typedef struct ARMInsnFixup {
FixupType fixup;
} ARMInsnFixup;
+static const ARMInsnFixup bootloader_aarch64[] = {
+ { 0x580000c0 }, /* ldr x0, arg ; Load the lower 32-bits of DTB */
+ { 0xaa1f03e1 }, /* mov x1, xzr */
+ { 0xaa1f03e2 }, /* mov x2, xzr */
+ { 0xaa1f03e3 }, /* mov x3, xzr */
+ { 0x58000084 }, /* ldr x4, entry ; Load the lower 32-bits of kernel entry */
+ { 0xd61f0080 }, /* br x4 ; Jump to the kernel entry point */
+ { 0, FIXUP_ARGPTR }, /* arg: .word @DTB Lower 32-bits */
+ { 0 }, /* .word @DTB Higher 32-bits */
+ { 0, FIXUP_ENTRYPOINT }, /* entry: .word @Kernel Entry Lower 32-bits */
+ { 0 }, /* .word @Kernel Entry Higher 32-bits */
+ { 0, FIXUP_TERMINATOR }
+};
+
/* The worlds second smallest bootloader. Set r0-r2, then jump to kernel. */
static const ARMInsnFixup bootloader[] = {
{ 0xe3a00000 }, /* mov r0, #0 */
@@ -396,7 +415,12 @@ static void do_cpu_reset(void *opaque)
env->thumb = info->entry & 1;
} else {
if (CPU(cpu) == first_cpu) {
- env->regs[15] = info->loader_start;
+ if (env->aarch64) {
+ env->pc = info->loader_start;
+ } else {
+ env->regs[15] = info->loader_start;
+ }
+
if (!info->dtb_filename) {
if (old_param) {
set_kernel_args_old(info);
@@ -418,8 +442,9 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
int initrd_size;
int is_linux = 0;
uint64_t elf_entry;
- hwaddr entry;
+ hwaddr entry, kernel_load_offset;
int big_endian;
+ static const ARMInsnFixup *primary_loader;
/* Load the kernel. */
if (!info->kernel_filename) {
@@ -429,6 +454,14 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
return;
}
+ if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
+ primary_loader = bootloader_aarch64;
+ kernel_load_offset = KERNEL64_LOAD_ADDR;
+ } else {
+ primary_loader = bootloader;
+ kernel_load_offset = KERNEL_LOAD_ADDR;
+ }
+
info->dtb_filename = qemu_opt_get(qemu_get_machine_opts(), "dtb");
if (!info->secondary_cpu_reset_hook) {
@@ -469,9 +502,9 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
&is_linux);
}
if (kernel_size < 0) {
- entry = info->loader_start + KERNEL_LOAD_ADDR;
+ entry = info->loader_start + kernel_load_offset;
kernel_size = load_image_targphys(info->kernel_filename, entry,
- info->ram_size - KERNEL_LOAD_ADDR);
+ info->ram_size - kernel_load_offset);
is_linux = 1;
}
if (kernel_size < 0) {
@@ -532,7 +565,7 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
fixupcontext[FIXUP_ENTRYPOINT] = entry;
write_bootloader("bootloader", info->loader_start,
- bootloader, fixupcontext);
+ primary_loader, fixupcontext);
if (info->nb_cpus > 1) {
info->write_secondary_boot(cpu, info);
--
1.8.5
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [Qemu-devel] [PATCH v2 7/7] default-configs: Add config for aarch64-softmmu
2013-12-17 12:15 [Qemu-devel] [PATCH v2 0/7] target-arm: Support AArch64 KVM Peter Maydell
` (5 preceding siblings ...)
2013-12-17 12:15 ` [Qemu-devel] [PATCH v2 6/7] hw/arm/boot: Add boot support for AArch64 processor Peter Maydell
@ 2013-12-17 12:15 ` Peter Maydell
2013-12-17 12:51 ` Peter Crosthwaite
2013-12-18 6:05 ` [Qemu-devel] [PATCH v2 0/7] target-arm: Support AArch64 KVM Christoffer Dall
7 siblings, 1 reply; 13+ messages in thread
From: Peter Maydell @ 2013-12-17 12:15 UTC (permalink / raw)
To: qemu-devel; +Cc: kvmarm, Christoffer Dall, patches
Add a config for aarch64-softmmu; this enables building of this target.
The resulting executable doesn't know about any 64 bit CPUs, but all
the 32 bit CPUs and board models work.
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 1385645602-18662-8-git-send-email-peter.maydell@linaro.org
---
default-configs/aarch64-softmmu.mak | 6 ++++++
1 file changed, 6 insertions(+)
create mode 100644 default-configs/aarch64-softmmu.mak
diff --git a/default-configs/aarch64-softmmu.mak b/default-configs/aarch64-softmmu.mak
new file mode 100644
index 0000000..6d3b5c7
--- /dev/null
+++ b/default-configs/aarch64-softmmu.mak
@@ -0,0 +1,6 @@
+# Default configuration for aarch64-softmmu
+
+# We support all the 32 bit boards so need all their config
+include arm-softmmu.mak
+
+# Currently no 64-bit specific config requirements
--
1.8.5
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [PATCH v2 7/7] default-configs: Add config for aarch64-softmmu
2013-12-17 12:15 ` [Qemu-devel] [PATCH v2 7/7] default-configs: Add config for aarch64-softmmu Peter Maydell
@ 2013-12-17 12:51 ` Peter Crosthwaite
0 siblings, 0 replies; 13+ messages in thread
From: Peter Crosthwaite @ 2013-12-17 12:51 UTC (permalink / raw)
To: Peter Maydell
Cc: Patch Tracking, qemu-devel@nongnu.org Developers,
Christoffer Dall, kvmarm@lists.cs.columbia.edu
On Tue, Dec 17, 2013 at 10:15 PM, Peter Maydell
<peter.maydell@linaro.org> wrote:
> Add a config for aarch64-softmmu; this enables building of this target.
> The resulting executable doesn't know about any 64 bit CPUs, but all
> the 32 bit CPUs and board models work.
>
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
> Message-id: 1385645602-18662-8-git-send-email-peter.maydell@linaro.org
> ---
> default-configs/aarch64-softmmu.mak | 6 ++++++
> 1 file changed, 6 insertions(+)
> create mode 100644 default-configs/aarch64-softmmu.mak
>
> diff --git a/default-configs/aarch64-softmmu.mak b/default-configs/aarch64-softmmu.mak
> new file mode 100644
> index 0000000..6d3b5c7
> --- /dev/null
> +++ b/default-configs/aarch64-softmmu.mak
> @@ -0,0 +1,6 @@
> +# Default configuration for aarch64-softmmu
> +
> +# We support all the 32 bit boards so need all their config
> +include arm-softmmu.mak
> +
> +# Currently no 64-bit specific config requirements
>
> 1.8.5
>
>
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [PATCH v2 6/7] hw/arm/boot: Add boot support for AArch64 processor
2013-12-17 12:15 ` [Qemu-devel] [PATCH v2 6/7] hw/arm/boot: Add boot support for AArch64 processor Peter Maydell
@ 2013-12-17 13:04 ` Peter Crosthwaite
2013-12-17 13:14 ` Peter Maydell
0 siblings, 1 reply; 13+ messages in thread
From: Peter Crosthwaite @ 2013-12-17 13:04 UTC (permalink / raw)
To: Peter Maydell
Cc: Patch Tracking, qemu-devel@nongnu.org Developers,
Christoffer Dall, kvmarm@lists.cs.columbia.edu
On Tue, Dec 17, 2013 at 10:15 PM, Peter Maydell
<peter.maydell@linaro.org> wrote:
> From: "Mian M. Hamayun" <m.hamayun@virtualopensystems.com>
>
> This commit adds support for booting a single AArch64 CPU by setting
> appropriate registers. The bootloader includes placehoders for Board-ID
"placeholders"
> that are used to implement uniform indexing across different bootloaders.
>
> Signed-off-by: Mian M. Hamayun <m.hamayun@virtualopensystems.com>
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
> Message-id: 1385645602-18662-7-git-send-email-peter.maydell@linaro.org
> [PMM:
> * updated to use ARMInsnFixup style bootloader fragments
> * dropped virt.c additions
> * use runtime checks for "is this an AArch64 core" rather than ifdefs
> * drop some unnecessary setting of registers in reset hook
> ]
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
> Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
> ---
> hw/arm/boot.c | 43 ++++++++++++++++++++++++++++++++++++++-----
> 1 file changed, 38 insertions(+), 5 deletions(-)
>
> diff --git a/hw/arm/boot.c b/hw/arm/boot.c
> index 0c05a64..90e9534 100644
> --- a/hw/arm/boot.c
> +++ b/hw/arm/boot.c
> @@ -17,8 +17,13 @@
> #include "sysemu/device_tree.h"
> #include "qemu/config-file.h"
>
> +/* Kernel boot protocol is specified in the kernel docs
> + * Documentation/arm/Booting and Documentation/arm64/booting.txt
> + * They have different preferred image load offsets from system RAM base.
> + */
> #define KERNEL_ARGS_ADDR 0x100
> #define KERNEL_LOAD_ADDR 0x00010000
So out of context, but I have been booting an ARMv7 multi defconfig on
a few qemu platforms recently (spec. allwinner, highbank and zynq) and
I found I had to patch this to 0x8000 due to some image alignment
expectations of head.S. Could we patch this to 0x8000 - is there this
same sense of preferred image offset in KERNEL32_LOAD_ADDR?
> +#define KERNEL64_LOAD_ADDR 0x00080000
>
> typedef enum {
> FIXUP_NONE = 0, /* do nothing */
> @@ -37,6 +42,20 @@ typedef struct ARMInsnFixup {
> FixupType fixup;
> } ARMInsnFixup;
>
> +static const ARMInsnFixup bootloader_aarch64[] = {
> + { 0x580000c0 }, /* ldr x0, arg ; Load the lower 32-bits of DTB */
> + { 0xaa1f03e1 }, /* mov x1, xzr */
> + { 0xaa1f03e2 }, /* mov x2, xzr */
> + { 0xaa1f03e3 }, /* mov x3, xzr */
> + { 0x58000084 }, /* ldr x4, entry ; Load the lower 32-bits of kernel entry */
> + { 0xd61f0080 }, /* br x4 ; Jump to the kernel entry point */
> + { 0, FIXUP_ARGPTR }, /* arg: .word @DTB Lower 32-bits */
> + { 0 }, /* .word @DTB Higher 32-bits */
> + { 0, FIXUP_ENTRYPOINT }, /* entry: .word @Kernel Entry Lower 32-bits */
> + { 0 }, /* .word @Kernel Entry Higher 32-bits */
> + { 0, FIXUP_TERMINATOR }
> +};
> +
> /* The worlds second smallest bootloader. Set r0-r2, then jump to kernel. */
> static const ARMInsnFixup bootloader[] = {
> { 0xe3a00000 }, /* mov r0, #0 */
> @@ -396,7 +415,12 @@ static void do_cpu_reset(void *opaque)
> env->thumb = info->entry & 1;
> } else {
> if (CPU(cpu) == first_cpu) {
> - env->regs[15] = info->loader_start;
> + if (env->aarch64) {
Curious, why does this 'if' directly deref env, while the one below
(for primary_loader selection) uses ARM_FEATURE?
Regards,
Peter
> + env->pc = info->loader_start;
> + } else {
> + env->regs[15] = info->loader_start;
> + }
> +
> if (!info->dtb_filename) {
> if (old_param) {
> set_kernel_args_old(info);
> @@ -418,8 +442,9 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
> int initrd_size;
> int is_linux = 0;
> uint64_t elf_entry;
> - hwaddr entry;
> + hwaddr entry, kernel_load_offset;
> int big_endian;
> + static const ARMInsnFixup *primary_loader;
>
> /* Load the kernel. */
> if (!info->kernel_filename) {
> @@ -429,6 +454,14 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
> return;
> }
>
> + if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
> + primary_loader = bootloader_aarch64;
> + kernel_load_offset = KERNEL64_LOAD_ADDR;
> + } else {
> + primary_loader = bootloader;
> + kernel_load_offset = KERNEL_LOAD_ADDR;
> + }
> +
> info->dtb_filename = qemu_opt_get(qemu_get_machine_opts(), "dtb");
>
> if (!info->secondary_cpu_reset_hook) {
> @@ -469,9 +502,9 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
> &is_linux);
> }
> if (kernel_size < 0) {
> - entry = info->loader_start + KERNEL_LOAD_ADDR;
> + entry = info->loader_start + kernel_load_offset;
> kernel_size = load_image_targphys(info->kernel_filename, entry,
> - info->ram_size - KERNEL_LOAD_ADDR);
> + info->ram_size - kernel_load_offset);
> is_linux = 1;
> }
> if (kernel_size < 0) {
> @@ -532,7 +565,7 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
> fixupcontext[FIXUP_ENTRYPOINT] = entry;
>
> write_bootloader("bootloader", info->loader_start,
> - bootloader, fixupcontext);
> + primary_loader, fixupcontext);
>
> if (info->nb_cpus > 1) {
> info->write_secondary_boot(cpu, info);
> --
> 1.8.5
>
>
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [PATCH v2 6/7] hw/arm/boot: Add boot support for AArch64 processor
2013-12-17 13:04 ` Peter Crosthwaite
@ 2013-12-17 13:14 ` Peter Maydell
2013-12-17 13:44 ` Peter Crosthwaite
0 siblings, 1 reply; 13+ messages in thread
From: Peter Maydell @ 2013-12-17 13:14 UTC (permalink / raw)
To: Peter Crosthwaite
Cc: Patch Tracking, qemu-devel@nongnu.org Developers,
Christoffer Dall, kvmarm@lists.cs.columbia.edu
On 17 December 2013 13:04, Peter Crosthwaite
<peter.crosthwaite@xilinx.com> wrote:
> On Tue, Dec 17, 2013 at 10:15 PM, Peter Maydell
> <peter.maydell@linaro.org> wrote:
>> From: "Mian M. Hamayun" <m.hamayun@virtualopensystems.com>
>>
>> This commit adds support for booting a single AArch64 CPU by setting
>> appropriate registers. The bootloader includes placehoders for Board-ID
>
> "placeholders"
Fixed (not going to respin just for that though :-))
>> that are used to implement uniform indexing across different bootloaders.
>>
>> Signed-off-by: Mian M. Hamayun <m.hamayun@virtualopensystems.com>
>> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
>> Message-id: 1385645602-18662-7-git-send-email-peter.maydell@linaro.org
>> [PMM:
>> * updated to use ARMInsnFixup style bootloader fragments
>> * dropped virt.c additions
>> * use runtime checks for "is this an AArch64 core" rather than ifdefs
>> * drop some unnecessary setting of registers in reset hook
>> ]
>> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
>> Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
>> ---
>> hw/arm/boot.c | 43 ++++++++++++++++++++++++++++++++++++++-----
>> 1 file changed, 38 insertions(+), 5 deletions(-)
>>
>> diff --git a/hw/arm/boot.c b/hw/arm/boot.c
>> index 0c05a64..90e9534 100644
>> --- a/hw/arm/boot.c
>> +++ b/hw/arm/boot.c
>> @@ -17,8 +17,13 @@
>> #include "sysemu/device_tree.h"
>> #include "qemu/config-file.h"
>>
>> +/* Kernel boot protocol is specified in the kernel docs
>> + * Documentation/arm/Booting and Documentation/arm64/booting.txt
>> + * They have different preferred image load offsets from system RAM base.
>> + */
>> #define KERNEL_ARGS_ADDR 0x100
>> #define KERNEL_LOAD_ADDR 0x00010000
>
> So out of context, but I have been booting an ARMv7 multi defconfig on
> a few qemu platforms recently (spec. allwinner, highbank and zynq) and
> I found I had to patch this to 0x8000 due to some image alignment
> expectations of head.S. Could we patch this to 0x8000 - is there this
> same sense of preferred image offset in KERNEL32_LOAD_ADDR?
Hmm. Are you booting non-zImage kernels? The booting doc says
# When booting a raw (non-zImage) kernel the constraints are tighter.
# In this case the kernel must be loaded at an offset into system equal
# to TEXT_OFFSET - PAGE_OFFSET.
so maybe that's what we're not getting right there? I practically
always use a zImage so I wouldn't ever see that.
In any case if you can nail down whether that's the problem and
submit a patch that would be good, though we'll have to test
on a bunch of boards that we haven't broken something else.
(I don't particularly want to change to 0x8000 because it "happens
to work", but the same change with a rationale attached is fine :-))
>> +#define KERNEL64_LOAD_ADDR 0x00080000
>>
>> typedef enum {
>> FIXUP_NONE = 0, /* do nothing */
>> @@ -37,6 +42,20 @@ typedef struct ARMInsnFixup {
>> FixupType fixup;
>> } ARMInsnFixup;
>>
>> +static const ARMInsnFixup bootloader_aarch64[] = {
>> + { 0x580000c0 }, /* ldr x0, arg ; Load the lower 32-bits of DTB */
>> + { 0xaa1f03e1 }, /* mov x1, xzr */
>> + { 0xaa1f03e2 }, /* mov x2, xzr */
>> + { 0xaa1f03e3 }, /* mov x3, xzr */
>> + { 0x58000084 }, /* ldr x4, entry ; Load the lower 32-bits of kernel entry */
>> + { 0xd61f0080 }, /* br x4 ; Jump to the kernel entry point */
>> + { 0, FIXUP_ARGPTR }, /* arg: .word @DTB Lower 32-bits */
>> + { 0 }, /* .word @DTB Higher 32-bits */
>> + { 0, FIXUP_ENTRYPOINT }, /* entry: .word @Kernel Entry Lower 32-bits */
>> + { 0 }, /* .word @Kernel Entry Higher 32-bits */
>> + { 0, FIXUP_TERMINATOR }
>> +};
>> +
>> /* The worlds second smallest bootloader. Set r0-r2, then jump to kernel. */
>> static const ARMInsnFixup bootloader[] = {
>> { 0xe3a00000 }, /* mov r0, #0 */
>> @@ -396,7 +415,12 @@ static void do_cpu_reset(void *opaque)
>> env->thumb = info->entry & 1;
>> } else {
>> if (CPU(cpu) == first_cpu) {
>> - env->regs[15] = info->loader_start;
>> + if (env->aarch64) {
>
> Curious, why does this 'if' directly deref env, while the one below
> (for primary_loader selection) uses ARM_FEATURE?
When we're picking the bootloader the CPU isn't started, so
its internal state (including the aarch64 flag, which is a reflection
of PSTATE.nRW) isn't valid. We want to pick a bootloader on
the basis of "if this CPU can do AArch64 then use the AArch64
loader". This code however runs after reset and so we can say
"if you're currently in AArch64 mode then this is how the PC is
set, otherwise like this".
I'm mulling a cleanup at some point that makes the AArch32
also use env->pc, at which point this if() will just evaporate anyway.
thanks
-- PMM
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [PATCH v2 6/7] hw/arm/boot: Add boot support for AArch64 processor
2013-12-17 13:14 ` Peter Maydell
@ 2013-12-17 13:44 ` Peter Crosthwaite
0 siblings, 0 replies; 13+ messages in thread
From: Peter Crosthwaite @ 2013-12-17 13:44 UTC (permalink / raw)
To: Peter Maydell
Cc: kvmarm@lists.cs.columbia.edu, qemu-devel@nongnu.org Developers,
Christoffer Dall, Patch Tracking
On Tue, Dec 17, 2013 at 11:14 PM, Peter Maydell
<peter.maydell@linaro.org> wrote:
> On 17 December 2013 13:04, Peter Crosthwaite
> <peter.crosthwaite@xilinx.com> wrote:
>> On Tue, Dec 17, 2013 at 10:15 PM, Peter Maydell
>> <peter.maydell@linaro.org> wrote:
>>> From: "Mian M. Hamayun" <m.hamayun@virtualopensystems.com>
>>>
>>> This commit adds support for booting a single AArch64 CPU by setting
>>> appropriate registers. The bootloader includes placehoders for Board-ID
>>
>> "placeholders"
>
> Fixed (not going to respin just for that though :-))
>
>>> that are used to implement uniform indexing across different bootloaders.
>>>
>>> Signed-off-by: Mian M. Hamayun <m.hamayun@virtualopensystems.com>
>>> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
>>> Message-id: 1385645602-18662-7-git-send-email-peter.maydell@linaro.org
>>> [PMM:
>>> * updated to use ARMInsnFixup style bootloader fragments
>>> * dropped virt.c additions
>>> * use runtime checks for "is this an AArch64 core" rather than ifdefs
>>> * drop some unnecessary setting of registers in reset hook
>>> ]
>>> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
>>> Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
>>> ---
>>> hw/arm/boot.c | 43 ++++++++++++++++++++++++++++++++++++++-----
>>> 1 file changed, 38 insertions(+), 5 deletions(-)
>>>
>>> diff --git a/hw/arm/boot.c b/hw/arm/boot.c
>>> index 0c05a64..90e9534 100644
>>> --- a/hw/arm/boot.c
>>> +++ b/hw/arm/boot.c
>>> @@ -17,8 +17,13 @@
>>> #include "sysemu/device_tree.h"
>>> #include "qemu/config-file.h"
>>>
>>> +/* Kernel boot protocol is specified in the kernel docs
>>> + * Documentation/arm/Booting and Documentation/arm64/booting.txt
>>> + * They have different preferred image load offsets from system RAM base.
>>> + */
>>> #define KERNEL_ARGS_ADDR 0x100
>>> #define KERNEL_LOAD_ADDR 0x00010000
>>
>> So out of context, but I have been booting an ARMv7 multi defconfig on
>> a few qemu platforms recently (spec. allwinner, highbank and zynq) and
>> I found I had to patch this to 0x8000 due to some image alignment
>> expectations of head.S. Could we patch this to 0x8000 - is there this
>> same sense of preferred image offset in KERNEL32_LOAD_ADDR?
>
> Hmm. Are you booting non-zImage kernels? The booting doc says
>
Yep.
> # When booting a raw (non-zImage) kernel the constraints are tighter.
> # In this case the kernel must be loaded at an offset into system equal
> # to TEXT_OFFSET - PAGE_OFFSET.
>
Yes it'll probably that, although my trace through head.S had
significant variation with different Kconfigs and normal Image has
booted in the past. Would have to get back with specifics.
> so maybe that's what we're not getting right there? I practically
> always use a zImage so I wouldn't ever see that.
>
> In any case if you can nail down whether that's the problem and
> submit a patch that would be good, though we'll have to test
> on a bunch of boards that we haven't broken something else.
> (I don't particularly want to change to 0x8000 because it "happens
> to work", but the same change with a rationale attached is fine :-))
>
>>> +#define KERNEL64_LOAD_ADDR 0x00080000
>>>
>>> typedef enum {
>>> FIXUP_NONE = 0, /* do nothing */
>>> @@ -37,6 +42,20 @@ typedef struct ARMInsnFixup {
>>> FixupType fixup;
>>> } ARMInsnFixup;
>>>
>>> +static const ARMInsnFixup bootloader_aarch64[] = {
>>> + { 0x580000c0 }, /* ldr x0, arg ; Load the lower 32-bits of DTB */
>>> + { 0xaa1f03e1 }, /* mov x1, xzr */
>>> + { 0xaa1f03e2 }, /* mov x2, xzr */
>>> + { 0xaa1f03e3 }, /* mov x3, xzr */
>>> + { 0x58000084 }, /* ldr x4, entry ; Load the lower 32-bits of kernel entry */
>>> + { 0xd61f0080 }, /* br x4 ; Jump to the kernel entry point */
>>> + { 0, FIXUP_ARGPTR }, /* arg: .word @DTB Lower 32-bits */
>>> + { 0 }, /* .word @DTB Higher 32-bits */
>>> + { 0, FIXUP_ENTRYPOINT }, /* entry: .word @Kernel Entry Lower 32-bits */
>>> + { 0 }, /* .word @Kernel Entry Higher 32-bits */
>>> + { 0, FIXUP_TERMINATOR }
>>> +};
>>> +
>>> /* The worlds second smallest bootloader. Set r0-r2, then jump to kernel. */
>>> static const ARMInsnFixup bootloader[] = {
>>> { 0xe3a00000 }, /* mov r0, #0 */
>>> @@ -396,7 +415,12 @@ static void do_cpu_reset(void *opaque)
>>> env->thumb = info->entry & 1;
>>> } else {
>>> if (CPU(cpu) == first_cpu) {
>>> - env->regs[15] = info->loader_start;
>>> + if (env->aarch64) {
>>
>> Curious, why does this 'if' directly deref env, while the one below
>> (for primary_loader selection) uses ARM_FEATURE?
>
> When we're picking the bootloader the CPU isn't started, so
> its internal state (including the aarch64 flag, which is a reflection
> of PSTATE.nRW) isn't valid. We want to pick a bootloader on
> the basis of "if this CPU can do AArch64 then use the AArch64
> loader". This code however runs after reset and so we can say
> "if you're currently in AArch64 mode then this is how the PC is
> set, otherwise like this".
>
But isn't the selection of which primary bootloader pre-decided by
arm_feature? This would lead to an error if (somehow) the bootloader
blobbed in the 64 bit version and the CPU reset into 32 (via this
aarch64 flag). Perhaps this check should also be arm_feature, and then
the code can addtionally assert correctness of this ->aarch64 CPU
state rather than trusting in coherency between the two stages.
Regards,
Peter
> I'm mulling a cleanup at some point that makes the AArch32
> also use env->pc, at which point this if() will just evaporate anyway.
>
> thanks
> -- PMM
>64
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [PATCH v2 0/7] target-arm: Support AArch64 KVM
2013-12-17 12:15 [Qemu-devel] [PATCH v2 0/7] target-arm: Support AArch64 KVM Peter Maydell
` (6 preceding siblings ...)
2013-12-17 12:15 ` [Qemu-devel] [PATCH v2 7/7] default-configs: Add config for aarch64-softmmu Peter Maydell
@ 2013-12-18 6:05 ` Christoffer Dall
7 siblings, 0 replies; 13+ messages in thread
From: Christoffer Dall @ 2013-12-18 6:05 UTC (permalink / raw)
To: Peter Maydell; +Cc: kvmarm, qemu-devel, patches
On Tue, Dec 17, 2013 at 12:15:15PM +0000, Peter Maydell wrote:
> This patchset adds support for basic AArch64 KVM VM control;
> it's based on current master. This is a quick resend with the very
> minor nits Christoffer pointed out fixed; I'm planning to put it into
> a target-arm pullreq in the next day or two.
>
> This patch series supports:
> * 64 bit KVM VM control
> * SMP and UP
> * PSCI boot of secondary CPUs
> It doesn't support:
> * migration
> * reset (partly because there's no way to reset a mach-virt system yet)
> * anything except "-cpu host"
> * debugging the VM via qemu gdbstub
> * running 32 bit VMs on a 64 bit system
> [Mian's patchset includes support for that but I have left it out
> for the moment because it needs more thought about UI and so on]
I finally managed to test this on the foundation model. I verified the
following works:
- virtio-block
- virtio-net
- ssh/shell environemnt in guest
- apache in guest (tested with apachebench - the model is slow)
- gcc in guest
- hackbench in guest (the model is slow)
- php in guest
- python in guest
-Christoffer
>
> Changes v1->v2:
> * improved a couple of comments
> * compat string for 64 bit is "arm,arm-v8", not -v7
> * removed superfluous include of usb.mak and pci.mak from config
>
> Mian M. Hamayun (2):
> target-arm: Add minimal KVM AArch64 support
> hw/arm/boot: Add boot support for AArch64 processor
>
> Peter Maydell (5):
> target-arm/kvm: Split 32 bit only code into its own file
> target-arm: Clean up handling of AArch64 PSTATE
> configure: Enable KVM for aarch64 host/target combination
> hw/arm/boot: Allow easier swapping in of different loader code
> default-configs: Add config for aarch64-softmmu
>
> configure | 2 +-
> default-configs/aarch64-softmmu.mak | 6 +
> hw/arm/boot.c | 193 ++++++++++----
> linux-user/signal.c | 6 +-
> target-arm/Makefile.objs | 2 +
> target-arm/cpu.c | 6 +
> target-arm/cpu.h | 70 ++++-
> target-arm/gdbstub64.c | 4 +-
> target-arm/kvm.c | 495 +---------------------------------
> target-arm/kvm32.c | 515 ++++++++++++++++++++++++++++++++++++
> target-arm/kvm64.c | 204 ++++++++++++++
> target-arm/translate-a64.c | 12 +-
> 12 files changed, 954 insertions(+), 561 deletions(-)
> create mode 100644 default-configs/aarch64-softmmu.mak
> create mode 100644 target-arm/kvm32.c
> create mode 100644 target-arm/kvm64.c
>
> --
> 1.8.5
>
--
Christoffer
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2013-12-18 6:05 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-12-17 12:15 [Qemu-devel] [PATCH v2 0/7] target-arm: Support AArch64 KVM Peter Maydell
2013-12-17 12:15 ` [Qemu-devel] [PATCH v2 1/7] target-arm/kvm: Split 32 bit only code into its own file Peter Maydell
2013-12-17 12:15 ` [Qemu-devel] [PATCH v2 2/7] target-arm: Clean up handling of AArch64 PSTATE Peter Maydell
2013-12-17 12:15 ` [Qemu-devel] [PATCH v2 3/7] target-arm: Add minimal KVM AArch64 support Peter Maydell
2013-12-17 12:15 ` [Qemu-devel] [PATCH v2 4/7] configure: Enable KVM for aarch64 host/target combination Peter Maydell
2013-12-17 12:15 ` [Qemu-devel] [PATCH v2 5/7] hw/arm/boot: Allow easier swapping in of different loader code Peter Maydell
2013-12-17 12:15 ` [Qemu-devel] [PATCH v2 6/7] hw/arm/boot: Add boot support for AArch64 processor Peter Maydell
2013-12-17 13:04 ` Peter Crosthwaite
2013-12-17 13:14 ` Peter Maydell
2013-12-17 13:44 ` Peter Crosthwaite
2013-12-17 12:15 ` [Qemu-devel] [PATCH v2 7/7] default-configs: Add config for aarch64-softmmu Peter Maydell
2013-12-17 12:51 ` Peter Crosthwaite
2013-12-18 6:05 ` [Qemu-devel] [PATCH v2 0/7] target-arm: Support AArch64 KVM Christoffer Dall
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).