On Thu, 12 Jul 2012 11:27:34 +0200, Marc Zyngier <marc.zyngier at arm.com> wrote: > From: Rusty Russell [rusty.russell at linaro.org] Sent: Thursday, July 12, > 2012 6:54 AM > > > Hi all, > > > > This turned out to be a longer patch series than I expected. > > The idea is that qemu tells us exactly what CPU (and cpu features) the > > guest should have, and we tell it exactly what CP15 registers we have. > > > > This allows us to be futureproof: if we want to emulate an A9 in > > future, this interface would let us do that. If a future host CPU can > > create a guest environment which looks like an A15, we can do that, too. > > Brilliant! Is there a corresponding Qemu patch somewhere? I'd love to give > this a spin... I have a terrible hack to check it works, see below; I wanted to discuss the transition with Peter before writing something decent. Or let him do the qemu side :) If you don't apply the final patch, then you just need the kvm_arch_init_vcpu() call to KVM_SET_SREGS. If we ignore the return value, then it works with older kernels too. Cheers, Rusty. diff --git a/linux-headers/asm-arm/kvm.h b/linux-headers/asm-arm/kvm.h index 38ff1d6..988890a 100644 --- a/linux-headers/asm-arm/kvm.h +++ b/linux-headers/asm-arm/kvm.h @@ -66,7 +66,13 @@ struct kvm_regs { }; +/* Supported Processor Types */ +#define KVM_ARM_TARGET_CORTEX_A15 (0xC0F) + struct kvm_sregs { + __u32 target; + __u32 num_features; + __u32 features[14]; }; struct kvm_fpu { diff --git a/target-arm/kvm.c b/target-arm/kvm.c index 29bb51f..650c9b3 100644 --- a/target-arm/kvm.c +++ b/target-arm/kvm.c @@ -32,8 +32,153 @@ int kvm_arch_init(KVMState *s) return 0; } +/* Exactly like x86. */ +struct kvm_msr_entry { + __u32 index; + __u32 reserved; + __u64 data; +}; + +/* for KVM_GET_MSRS and KVM_SET_MSRS */ +struct kvm_msrs { + __u32 nmsrs; /* number of msrs in entries */ + __u32 pad; + + struct kvm_msr_entry entries[0]; +}; + +/* for KVM_GET_MSR_INDEX_LIST */ +struct kvm_msr_list { + __u32 nmsrs; /* number of msrs in entries */ + __u32 indices[0]; +}; + +/* If you need to interpret the index values, here's the key. */ +#define KVM_ARM_MSR_COPROC_MASK 0xFFFF0000 +#define KVM_ARM_MSR_64_BIT_MASK 0x00008000 +#define KVM_ARM_MSR_64_OPC1_MASK 0x000000F0 +#define KVM_ARM_MSR_64_CRM_MASK 0x0000000F +#define KVM_ARM_MSR_32_CRM_MASK 0x0000000F +#define KVM_ARM_MSR_32_OPC2_MASK 0x00000070 +#define KVM_ARM_MSR_32_CRN_MASK 0x00000780 +#define KVM_ARM_MSR_32_OPC1_MASK 0x00003800 + +static void print_index(__u32 idx) +{ + int coproc, opc1, crm, crn, opc2; + coproc = (idx & KVM_ARM_MSR_COPROC_MASK) >> 16; + if (coproc > 15) { + printf("Special (%u) => %u", + coproc, idx & ~KVM_ARM_MSR_COPROC_MASK); + } else if (idx & KVM_ARM_MSR_64_BIT_MASK) { + opc1 = (idx & KVM_ARM_MSR_64_OPC1_MASK) >> 4; + crm = idx & KVM_ARM_MSR_64_CRM_MASK; + printf("cp%u CRm(%u) Op1(%u)", coproc, crm, opc1); + } else { + opc1 = (idx & KVM_ARM_MSR_32_OPC1_MASK) >> 11; + crn = (idx & KVM_ARM_MSR_32_CRN_MASK) >> 7; + opc2 = (idx & KVM_ARM_MSR_32_OPC2_MASK) >> 4; + crm = idx & KVM_ARM_MSR_32_CRM_MASK; + printf("cp%u CRn(%u) CRm(%u) Op1(%u) Op2(%u)", + coproc, crn, crm, opc1, opc2); + } +} + +static void play_with_msrs(CPUARMState *env) +{ + int ret, i; + struct kvm_msr_list *list = malloc(4096); + struct kvm_msrs *msrs; + + list->nmsrs = 1; + + ret = kvm_vcpu_ioctl(env, KVM_GET_MSR_INDEX_LIST, list); + printf("KVM_GET_MSR_INDEX_LIST(nmsrs=1) => %u, %s\n", + list->nmsrs, strerror(-ret)); + + if (ret == -EINVAL) + return; + + assert(ret == -E2BIG); + + list->nmsrs = 1023; + ret = kvm_vcpu_ioctl(env, KVM_GET_MSR_INDEX_LIST, list); + printf("KVM_GET_MSR_INDEX_LIST(nmsrs=1023) => %u, %s\n", + list->nmsrs, strerror(-ret)); + assert(ret == 0); + + for (i = 0; i < list->nmsrs; i++) { + printf(" "); + print_index(list->indices[i]); + printf("\n"); + } + + msrs = malloc(sizeof(*msrs) + sizeof(msrs->entries[0])*list->nmsrs); + + /* Test invalid set/get */ + msrs->nmsrs = 1; + msrs->entries[0].index = 0xDEADBEEF; + + ret = kvm_vcpu_ioctl(env, KVM_GET_MSRS, msrs); + printf("KVM_GET_MSRS(nmsrs=1,entry[0]=0xDEADBEEF) => %s\n", + strerror(-ret)); + assert(ret == -EINVAL); + ret = kvm_vcpu_ioctl(env, KVM_SET_MSRS, msrs); + printf("KVM_SET_MSRS(nmsrs=1,entry[0]=0xDEADBEEF) => %s\n", + strerror(-ret)); + assert(ret == -EINVAL); + + msrs->nmsrs = list->nmsrs; + for (i = 0; i < list->nmsrs; i++) + msrs->entries[i].index = list->indices[i]; + + ret = kvm_vcpu_ioctl(env, KVM_GET_MSRS, msrs); + printf("KVM_GET_MSRS(nmsrs=%u) => %s\n", + list->nmsrs, strerror(-ret)); + assert(ret == 0); + assert(msrs->nmsrs == list->nmsrs); + + for (i = 0; i < msrs->nmsrs; i++) { + print_index(msrs->entries[i].index); + printf(" = 0x%llx\n", (long long)msrs->entries[i].data); + } + + ret = kvm_vcpu_ioctl(env, KVM_SET_MSRS, msrs); + printf("KVM_SET_MSRS(nmsrs=%u) => %s\n", + msrs->nmsrs, strerror(-ret)); + assert(ret == 0); + + /* Should not be able to change a fixed value. */ + msrs->entries[0].data ^= 1; + ret = kvm_vcpu_ioctl(env, KVM_SET_MSRS, msrs); + printf("KVM_SET_MSRS(nmsrs=%u,entries[0]=changed) => %s\n", + msrs->nmsrs, strerror(-ret)); + assert(ret == -EINVAL); + + msrs->entries[0].data ^= 1; + + /* Should be able to change a variable one. */ + msrs->entries[msrs->nmsrs - 1].data ^= 1; + ret = kvm_vcpu_ioctl(env, KVM_SET_MSRS, msrs); + printf("KVM_SET_MSRS(nmsrs=%u,entries[%u]=changed) => %s\n", + msrs->nmsrs, msrs->nmsrs-1, strerror(-ret)); + assert(ret == 0); + + free(list); + free(msrs); +} + int kvm_arch_init_vcpu(CPUARMState *env) { + struct kvm_sregs sregs; + int ret; + + sregs.target = KVM_ARM_TARGET_CORTEX_A15; + sregs.num_features = 0; + + ret = kvm_vcpu_ioctl(env, KVM_SET_SREGS, &sregs); + if (ret == 0) + play_with_msrs(env); return 0; }