Commit 1e0c135a authored by Will Deacon's avatar Will Deacon
Browse files

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: default avatarMarc Zyngier <marc.zyngier@arm.com>
Signed-off-by: default avatarWill Deacon <will.deacon@arm.com>
Signed-off-by: default avatarPekka Enberg <penberg@kernel.org>
parent 61076240
......@@ -103,7 +103,7 @@ OBJS += virtio/mmio.o
# Translate uname -m into ARCH string
ARCH ?= $(shell uname -m | sed -e s/i.86/i386/ -e s/ppc.*/powerpc/ \
-e s/armv7.*/arm/)
-e s/armv7.*/arm/ -e s/aarch64.*/arm64/)
ifeq ($(ARCH),i386)
ARCH := x86
......@@ -174,6 +174,18 @@ ifeq ($(ARCH), arm)
OTHEROBJS += $(LIBFDT_OBJS)
endif
# ARM64
ifeq ($(ARCH), arm64)
DEFINES += -DCONFIG_ARM64
OBJS += $(OBJS_ARM_COMMON)
OBJS += arm/aarch64/cortex-a57.o
OBJS += arm/aarch64/kvm-cpu.o
ARCH_INCLUDE := $(HDRS_ARM_COMMON)
ARCH_INCLUDE += -Iarm/aarch64/include
CFLAGS += -I../../scripts/dtc/libfdt
OTHEROBJS += $(LIBFDT_OBJS)
endif
###
ifeq (,$(ARCH_INCLUDE))
......
#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"
......
#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 */
#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);
#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 */
#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 */
#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 */
#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 */
#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, &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, &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, &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, &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, &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, &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, &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, &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, &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, &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, &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, &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, &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, &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, &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, &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, &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.");
......
#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;
......
#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)",
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment