diff options
author | Will Deacon <will.deacon@arm.com> | 2013-01-07 18:43:44 +0000 |
---|---|---|
committer | Will Deacon <will.deacon@arm.com> | 2015-06-01 16:39:54 +0100 |
commit | 1e0c135a3b4592d73020c8e46a21d39c8a076f43 (patch) | |
tree | a6d3990c0769a1adfc1449eac5d4d1c8671b27c4 /arm | |
parent | 61076240634dd372f691d577f6990b8121891ac9 (diff) | |
download | kvmtool-1e0c135a3b4592d73020c8e46a21d39c8a076f43.tar.gz |
kvm tools: add support for ARMv8 processors
This patch adds support for ARMv8 processors (more specifically,
Cortex-A57) to kvmtool. Both AArch64 and AArch32 guests are supported,
so the existing AArch32 code is slightly restructured to allow for
re-use of much of the current code.
The implementation closely follows the ARMv7 code and reuses much of the
work written there.
Tested-by: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Pekka Enberg <penberg@kernel.org>
Diffstat (limited to 'arm')
-rw-r--r-- | arm/aarch32/include/kvm/kvm-arch.h | 20 | ||||
-rw-r--r-- | arm/aarch32/include/kvm/kvm-config-arch.h | 8 | ||||
-rw-r--r-- | arm/aarch64/cortex-a57.c | 95 | ||||
-rw-r--r-- | arm/aarch64/include/kvm/barrier.h | 8 | ||||
-rw-r--r-- | arm/aarch64/include/kvm/kvm-arch.h | 17 | ||||
-rw-r--r-- | arm/aarch64/include/kvm/kvm-config-arch.h | 10 | ||||
-rw-r--r-- | arm/aarch64/include/kvm/kvm-cpu-arch.h | 13 | ||||
-rw-r--r-- | arm/aarch64/kvm-cpu.c | 160 | ||||
-rw-r--r-- | arm/fdt.c | 2 | ||||
-rw-r--r-- | arm/include/arm-common/kvm-arch.h | 22 | ||||
-rw-r--r-- | arm/include/arm-common/kvm-config-arch.h (renamed from arm/include/kvm/kvm-config-arch.h) | 8 | ||||
-rw-r--r-- | arm/kvm.c | 2 |
12 files changed, 340 insertions, 25 deletions
diff --git a/arm/aarch32/include/kvm/kvm-arch.h b/arm/aarch32/include/kvm/kvm-arch.h index ca79b247..1632e3c5 100644 --- a/arm/aarch32/include/kvm/kvm-arch.h +++ b/arm/aarch32/include/kvm/kvm-arch.h @@ -1,28 +1,12 @@ #ifndef KVM__KVM_ARCH_H #define KVM__KVM_ARCH_H -#include <linux/const.h> - -#define ARM_LOMAP_MMIO_AREA _AC(0x00000000, UL) -#define ARM_LOMAP_AXI_AREA _AC(0x40000000, UL) -#define ARM_LOMAP_MEMORY_AREA _AC(0x80000000, UL) -#define ARM_LOMAP_MAX_MEMORY _AC(0x7fffffff, UL) - #define ARM_GIC_DIST_SIZE 0x1000 -#define ARM_GIC_DIST_BASE (ARM_LOMAP_AXI_AREA - ARM_GIC_DIST_SIZE) #define ARM_GIC_CPUI_SIZE 0x2000 -#define ARM_GIC_CPUI_BASE (ARM_GIC_DIST_BASE - ARM_GIC_CPUI_SIZE) - -#define ARM_KERN_OFFSET 0x8000 - -#define ARM_VIRTIO_MMIO_SIZE (ARM_GIC_DIST_BASE - ARM_LOMAP_MMIO_AREA) -#define ARM_PCI_MMIO_SIZE (ARM_LOMAP_MEMORY_AREA - ARM_LOMAP_AXI_AREA) -#define ARM_MEMORY_AREA ARM_LOMAP_MEMORY_AREA -#define ARM_MAX_MEMORY ARM_LOMAP_MAX_MEMORY +#define ARM_KERN_OFFSET(...) 0x8000 -#define KVM_PCI_MMIO_AREA ARM_LOMAP_AXI_AREA -#define KVM_VIRTIO_MMIO_AREA ARM_LOMAP_MMIO_AREA +#define ARM_MAX_MEMORY(...) ARM_LOMAP_MAX_MEMORY #include "arm-common/kvm-arch.h" diff --git a/arm/aarch32/include/kvm/kvm-config-arch.h b/arm/aarch32/include/kvm/kvm-config-arch.h new file mode 100644 index 00000000..acf0d238 --- /dev/null +++ b/arm/aarch32/include/kvm/kvm-config-arch.h @@ -0,0 +1,8 @@ +#ifndef KVM__KVM_CONFIG_ARCH_H +#define KVM__KVM_CONFIG_ARCH_H + +#define ARM_OPT_ARCH_RUN(...) + +#include "arm-common/kvm-config-arch.h" + +#endif /* KVM__KVM_CONFIG_ARCH_H */ diff --git a/arm/aarch64/cortex-a57.c b/arm/aarch64/cortex-a57.c new file mode 100644 index 00000000..4fd11ba1 --- /dev/null +++ b/arm/aarch64/cortex-a57.c @@ -0,0 +1,95 @@ +#include "kvm/fdt.h" +#include "kvm/kvm.h" +#include "kvm/kvm-cpu.h" +#include "kvm/util.h" + +#include "arm-common/gic.h" + +#include <linux/byteorder.h> +#include <linux/types.h> + +#define CPU_NAME_MAX_LEN 8 +static void generate_cpu_nodes(void *fdt, struct kvm *kvm) +{ + int cpu; + + _FDT(fdt_begin_node(fdt, "cpus")); + _FDT(fdt_property_cell(fdt, "#address-cells", 0x1)); + _FDT(fdt_property_cell(fdt, "#size-cells", 0x0)); + + for (cpu = 0; cpu < kvm->nrcpus; ++cpu) { + char cpu_name[CPU_NAME_MAX_LEN]; + + if (kvm->cpus[cpu]->cpu_type != KVM_ARM_TARGET_CORTEX_A57) { + pr_warning("Ignoring unknown type for CPU %d\n", cpu); + continue; + } + + snprintf(cpu_name, CPU_NAME_MAX_LEN, "cpu@%d", cpu); + + _FDT(fdt_begin_node(fdt, cpu_name)); + _FDT(fdt_property_string(fdt, "device_type", "cpu")); + _FDT(fdt_property_string(fdt, "compatible", "arm,cortex-a57")); + + if (kvm->nrcpus > 1) + _FDT(fdt_property_string(fdt, "enable-method", "psci")); + + _FDT(fdt_property_cell(fdt, "reg", cpu)); + _FDT(fdt_end_node(fdt)); + } + + _FDT(fdt_end_node(fdt)); +} + +static void generate_timer_nodes(void *fdt, struct kvm *kvm) +{ + u32 cpu_mask = (((1 << kvm->nrcpus) - 1) << GIC_FDT_IRQ_PPI_CPU_SHIFT) \ + & GIC_FDT_IRQ_PPI_CPU_MASK; + u32 irq_prop[] = { + cpu_to_fdt32(GIC_FDT_IRQ_TYPE_PPI), + cpu_to_fdt32(13), + cpu_to_fdt32(cpu_mask | GIC_FDT_IRQ_FLAGS_EDGE_LO_HI), + + cpu_to_fdt32(GIC_FDT_IRQ_TYPE_PPI), + cpu_to_fdt32(14), + cpu_to_fdt32(cpu_mask | GIC_FDT_IRQ_FLAGS_EDGE_LO_HI), + + cpu_to_fdt32(GIC_FDT_IRQ_TYPE_PPI), + cpu_to_fdt32(11), + cpu_to_fdt32(cpu_mask | GIC_FDT_IRQ_FLAGS_EDGE_LO_HI), + + cpu_to_fdt32(GIC_FDT_IRQ_TYPE_PPI), + cpu_to_fdt32(10), + cpu_to_fdt32(cpu_mask | GIC_FDT_IRQ_FLAGS_EDGE_LO_HI), + }; + + _FDT(fdt_begin_node(fdt, "timer")); + _FDT(fdt_property_string(fdt, "compatible", "arm,armv8-timer")); + _FDT(fdt_property(fdt, "interrupts", irq_prop, sizeof(irq_prop))); + _FDT(fdt_end_node(fdt)); +} + +static void generate_fdt_nodes(void *fdt, struct kvm *kvm, u32 gic_phandle) +{ + generate_cpu_nodes(fdt, kvm); + gic__generate_fdt_nodes(fdt, gic_phandle); + generate_timer_nodes(fdt, kvm); +} + + +static int cortex_a57__vcpu_init(struct kvm_cpu *vcpu) +{ + vcpu->generate_fdt_nodes = generate_fdt_nodes; + return 0; +} + +static struct kvm_arm_target target_cortex_a57 = { + .id = KVM_ARM_TARGET_CORTEX_A57, + .init = cortex_a57__vcpu_init, +}; + +static int cortex_a57__core_init(struct kvm *kvm) +{ + return kvm_cpu__register_kvm_arm_target(&target_cortex_a57); +} +core_init(cortex_a57__core_init); diff --git a/arm/aarch64/include/kvm/barrier.h b/arm/aarch64/include/kvm/barrier.h new file mode 100644 index 00000000..97ab2521 --- /dev/null +++ b/arm/aarch64/include/kvm/barrier.h @@ -0,0 +1,8 @@ +#ifndef KVM__KVM_BARRIER_H +#define KVM__KVM_BARRIER_H + +#define mb() asm volatile ("dmb ish" : : : "memory") +#define rmb() asm volatile ("dmb ishld" : : : "memory") +#define wmb() asm volatile ("dmb ishst" : : : "memory") + +#endif /* KVM__KVM_BARRIER_H */ diff --git a/arm/aarch64/include/kvm/kvm-arch.h b/arm/aarch64/include/kvm/kvm-arch.h new file mode 100644 index 00000000..2f08a263 --- /dev/null +++ b/arm/aarch64/include/kvm/kvm-arch.h @@ -0,0 +1,17 @@ +#ifndef KVM__KVM_ARCH_H +#define KVM__KVM_ARCH_H + +#define ARM_GIC_DIST_SIZE 0x10000 +#define ARM_GIC_CPUI_SIZE 0x10000 + +#define ARM_KERN_OFFSET(kvm) ((kvm)->cfg.arch.aarch32_guest ? \ + 0x8000 : \ + 0x80000) + +#define ARM_MAX_MEMORY(kvm) ((kvm)->cfg.arch.aarch32_guest ? \ + ARM_LOMAP_MAX_MEMORY : \ + ARM_HIMAP_MAX_MEMORY) + +#include "arm-common/kvm-arch.h" + +#endif /* KVM__KVM_ARCH_H */ diff --git a/arm/aarch64/include/kvm/kvm-config-arch.h b/arm/aarch64/include/kvm/kvm-config-arch.h new file mode 100644 index 00000000..89860ae3 --- /dev/null +++ b/arm/aarch64/include/kvm/kvm-config-arch.h @@ -0,0 +1,10 @@ +#ifndef KVM__KVM_CONFIG_ARCH_H +#define KVM__KVM_CONFIG_ARCH_H + +#define ARM_OPT_ARCH_RUN(cfg) \ + OPT_BOOLEAN('\0', "aarch32", &(cfg)->aarch32_guest, \ + "Run AArch32 guest"), + +#include "arm-common/kvm-config-arch.h" + +#endif /* KVM__KVM_CONFIG_ARCH_H */ diff --git a/arm/aarch64/include/kvm/kvm-cpu-arch.h b/arm/aarch64/include/kvm/kvm-cpu-arch.h new file mode 100644 index 00000000..d85c5834 --- /dev/null +++ b/arm/aarch64/include/kvm/kvm-cpu-arch.h @@ -0,0 +1,13 @@ +#ifndef KVM__KVM_CPU_ARCH_H +#define KVM__KVM_CPU_ARCH_H + +#include "kvm/kvm.h" + +#include "arm-common/kvm-cpu-arch.h" + +#define ARM_VCPU_FEATURE_FLAGS(kvm, cpuid) { \ + [0] = ((!!(cpuid) << KVM_ARM_VCPU_POWER_OFF) | \ + (!!(kvm)->cfg.arch.aarch32_guest << KVM_ARM_VCPU_EL1_32BIT)) \ +} + +#endif /* KVM__KVM_CPU_ARCH_H */ diff --git a/arm/aarch64/kvm-cpu.c b/arm/aarch64/kvm-cpu.c new file mode 100644 index 00000000..2eb06eab --- /dev/null +++ b/arm/aarch64/kvm-cpu.c @@ -0,0 +1,160 @@ +#include "kvm/kvm-cpu.h" +#include "kvm/kvm.h" + +#include <asm/ptrace.h> + +#define COMPAT_PSR_F_BIT 0x00000040 +#define COMPAT_PSR_I_BIT 0x00000080 +#define COMPAT_PSR_MODE_SVC 0x00000013 + +#define ARM64_CORE_REG(x) (KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \ + KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(x)) + +static void reset_vcpu_aarch32(struct kvm_cpu *vcpu) +{ + struct kvm *kvm = vcpu->kvm; + struct kvm_one_reg reg; + u64 data; + + reg.addr = (u64)&data; + + /* pstate = all interrupts masked */ + data = COMPAT_PSR_I_BIT | COMPAT_PSR_F_BIT | COMPAT_PSR_MODE_SVC; + reg.id = ARM64_CORE_REG(regs.pstate); + if (ioctl(vcpu->vcpu_fd, KVM_SET_ONE_REG, ®) < 0) + die_perror("KVM_SET_ONE_REG failed (spsr[EL1])"); + + /* Secondary cores are stopped awaiting PSCI wakeup */ + if (vcpu->cpu_id != 0) + return; + + /* r0 = 0 */ + data = 0; + reg.id = ARM64_CORE_REG(regs.regs[0]); + if (ioctl(vcpu->vcpu_fd, KVM_SET_ONE_REG, ®) < 0) + die_perror("KVM_SET_ONE_REG failed (r0)"); + + /* r1 = machine type (-1) */ + data = -1; + reg.id = ARM64_CORE_REG(regs.regs[1]); + if (ioctl(vcpu->vcpu_fd, KVM_SET_ONE_REG, ®) < 0) + die_perror("KVM_SET_ONE_REG failed (r1)"); + + /* r2 = physical address of the device tree blob */ + data = kvm->arch.dtb_guest_start; + reg.id = ARM64_CORE_REG(regs.regs[2]); + if (ioctl(vcpu->vcpu_fd, KVM_SET_ONE_REG, ®) < 0) + die_perror("KVM_SET_ONE_REG failed (r2)"); + + /* pc = start of kernel image */ + data = kvm->arch.kern_guest_start; + reg.id = ARM64_CORE_REG(regs.pc); + if (ioctl(vcpu->vcpu_fd, KVM_SET_ONE_REG, ®) < 0) + die_perror("KVM_SET_ONE_REG failed (pc)"); +} + +static void reset_vcpu_aarch64(struct kvm_cpu *vcpu) +{ + struct kvm *kvm = vcpu->kvm; + struct kvm_one_reg reg; + u64 data; + + reg.addr = (u64)&data; + + /* pstate = all interrupts masked */ + data = PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT | PSR_MODE_EL1h; + reg.id = ARM64_CORE_REG(regs.pstate); + if (ioctl(vcpu->vcpu_fd, KVM_SET_ONE_REG, ®) < 0) + die_perror("KVM_SET_ONE_REG failed (spsr[EL1])"); + + /* x1...x3 = 0 */ + data = 0; + reg.id = ARM64_CORE_REG(regs.regs[1]); + if (ioctl(vcpu->vcpu_fd, KVM_SET_ONE_REG, ®) < 0) + die_perror("KVM_SET_ONE_REG failed (x1)"); + + reg.id = ARM64_CORE_REG(regs.regs[2]); + if (ioctl(vcpu->vcpu_fd, KVM_SET_ONE_REG, ®) < 0) + die_perror("KVM_SET_ONE_REG failed (x2)"); + + reg.id = ARM64_CORE_REG(regs.regs[3]); + if (ioctl(vcpu->vcpu_fd, KVM_SET_ONE_REG, ®) < 0) + die_perror("KVM_SET_ONE_REG failed (x3)"); + + /* Secondary cores are stopped awaiting PSCI wakeup */ + if (vcpu->cpu_id == 0) { + /* x0 = physical address of the device tree blob */ + data = kvm->arch.dtb_guest_start; + reg.id = ARM64_CORE_REG(regs.regs[0]); + if (ioctl(vcpu->vcpu_fd, KVM_SET_ONE_REG, ®) < 0) + die_perror("KVM_SET_ONE_REG failed (x0)"); + + /* pc = start of kernel image */ + data = kvm->arch.kern_guest_start; + reg.id = ARM64_CORE_REG(regs.pc); + if (ioctl(vcpu->vcpu_fd, KVM_SET_ONE_REG, ®) < 0) + die_perror("KVM_SET_ONE_REG failed (pc)"); + } +} + +void kvm_cpu__reset_vcpu(struct kvm_cpu *vcpu) +{ + if (vcpu->kvm->cfg.arch.aarch32_guest) + return reset_vcpu_aarch32(vcpu); + else + return reset_vcpu_aarch64(vcpu); +} + +void kvm_cpu__show_code(struct kvm_cpu *vcpu) +{ + struct kvm_one_reg reg; + unsigned long data; + + reg.addr = (u64)&data; + + printf("*pc:\n"); + reg.id = ARM64_CORE_REG(regs.pc); + if (ioctl(vcpu->vcpu_fd, KVM_GET_ONE_REG, ®) < 0) + die("KVM_GET_ONE_REG failed (show_code @ PC)"); + + kvm__dump_mem(vcpu->kvm, data, 32); + printf("\n"); + + printf("*lr:\n"); + reg.id = ARM64_CORE_REG(regs.regs[30]); + if (ioctl(vcpu->vcpu_fd, KVM_GET_ONE_REG, ®) < 0) + die("KVM_GET_ONE_REG failed (show_code @ LR)"); + + kvm__dump_mem(vcpu->kvm, data, 32); + printf("\n"); +} + +void kvm_cpu__show_registers(struct kvm_cpu *vcpu) +{ + struct kvm_one_reg reg; + unsigned long data; + int debug_fd = kvm_cpu__get_debug_fd(); + + reg.addr = (u64)&data; + dprintf(debug_fd, "\n Registers:\n"); + + reg.id = ARM64_CORE_REG(regs.pc); + if (ioctl(vcpu->vcpu_fd, KVM_GET_ONE_REG, ®) < 0) + die("KVM_GET_ONE_REG failed (pc)"); + dprintf(debug_fd, " PC: 0x%lx\n", data); + + reg.id = ARM64_CORE_REG(regs.pstate); + if (ioctl(vcpu->vcpu_fd, KVM_GET_ONE_REG, ®) < 0) + die("KVM_GET_ONE_REG failed (pstate)"); + dprintf(debug_fd, " PSTATE: 0x%lx\n", data); + + reg.id = ARM64_CORE_REG(sp_el1); + if (ioctl(vcpu->vcpu_fd, KVM_GET_ONE_REG, ®) < 0) + die("KVM_GET_ONE_REG failed (sp_el1)"); + dprintf(debug_fd, " SP_EL1: 0x%lx\n", data); + + reg.id = ARM64_CORE_REG(regs.regs[30]); + if (ioctl(vcpu->vcpu_fd, KVM_GET_ONE_REG, ®) < 0) + die("KVM_GET_ONE_REG failed (lr)"); + dprintf(debug_fd, " LR: 0x%lx\n", data); +} @@ -178,7 +178,7 @@ int load_flat_binary(struct kvm *kvm, int fd_kernel, int fd_initrd, */ limit = kvm->ram_start + min(kvm->ram_size, (u64)SZ_256M) - 1; - pos = kvm->ram_start + ARM_KERN_OFFSET; + pos = kvm->ram_start + ARM_KERN_OFFSET(kvm); kvm->arch.kern_guest_start = host_to_guest_flat(kvm, pos); if (read_image(fd_kernel, &pos, limit) == -ENOMEM) die("kernel image too big to contain in guest memory."); diff --git a/arm/include/arm-common/kvm-arch.h b/arm/include/arm-common/kvm-arch.h index 798af590..46ee7e24 100644 --- a/arm/include/arm-common/kvm-arch.h +++ b/arm/include/arm-common/kvm-arch.h @@ -1,11 +1,29 @@ #ifndef ARM_COMMON__KVM_ARCH_H #define ARM_COMMON__KVM_ARCH_H -#define VIRTIO_DEFAULT_TRANS VIRTIO_MMIO - #include <stdbool.h> +#include <linux/const.h> #include <linux/types.h> +#define ARM_MMIO_AREA _AC(0x0000000000000000, UL) +#define ARM_AXI_AREA _AC(0x0000000040000000, UL) +#define ARM_MEMORY_AREA _AC(0x0000000080000000, UL) + +#define ARM_LOMAP_MAX_MEMORY ((1ULL << 32) - ARM_MEMORY_AREA) +#define ARM_HIMAP_MAX_MEMORY ((1ULL << 40) - ARM_MEMORY_AREA) + +#define ARM_GIC_DIST_BASE (ARM_AXI_AREA - ARM_GIC_DIST_SIZE) +#define ARM_GIC_CPUI_BASE (ARM_GIC_DIST_BASE - ARM_GIC_CPUI_SIZE) +#define ARM_GIC_SIZE (ARM_GIC_DIST_SIZE + ARM_GIC_CPUI_SIZE) + +#define ARM_VIRTIO_MMIO_SIZE (ARM_AXI_AREA - ARM_GIC_SIZE) +#define ARM_PCI_MMIO_SIZE (ARM_MEMORY_AREA - ARM_AXI_AREA) + +#define KVM_PCI_MMIO_AREA ARM_AXI_AREA +#define KVM_VIRTIO_MMIO_AREA ARM_MMIO_AREA + +#define VIRTIO_DEFAULT_TRANS VIRTIO_MMIO + static inline bool arm_addr_in_virtio_mmio_region(u64 phys_addr) { u64 limit = KVM_VIRTIO_MMIO_AREA + ARM_VIRTIO_MMIO_SIZE; diff --git a/arm/include/kvm/kvm-config-arch.h b/arm/include/arm-common/kvm-config-arch.h index f63f3020..7ac6f6e8 100644 --- a/arm/include/kvm/kvm-config-arch.h +++ b/arm/include/arm-common/kvm-config-arch.h @@ -1,15 +1,17 @@ -#ifndef KVM__KVM_CONFIG_ARCH_H -#define KVM__KVM_CONFIG_ARCH_H +#ifndef ARM_COMMON__KVM_CONFIG_ARCH_H +#define ARM_COMMON__KVM_CONFIG_ARCH_H #include "kvm/parse-options.h" struct kvm_config_arch { const char *dump_dtb_filename; + bool aarch32_guest; }; #define OPT_ARCH_RUN(pfx, cfg) \ pfx, \ + ARM_OPT_ARCH_RUN(cfg) \ OPT_STRING('\0', "dump-dtb", &(cfg)->dump_dtb_filename, \ ".dtb file", "Dump generated .dtb to specified file"), -#endif /* KVM__KVM_CONFIG_ARCH_H */ +#endif /* ARM_COMMON__KVM_CONFIG_ARCH_H */ @@ -57,7 +57,7 @@ void kvm__arch_set_cmdline(char *cmdline, bool video) void kvm__arch_init(struct kvm *kvm, const char *hugetlbfs_path, u64 ram_size) { /* Allocate guest memory. */ - kvm->ram_size = min(ram_size, (u64)ARM_MAX_MEMORY); + kvm->ram_size = min(ram_size, (u64)ARM_MAX_MEMORY(kvm)); kvm->ram_start = mmap_anon_or_hugetlbfs(kvm, hugetlbfs_path, kvm->ram_size); if (kvm->ram_start == MAP_FAILED) die("Failed to map %lld bytes for guest memory (%d)", |