This patch adds initial support for ARMv7 processors (more specifically,
Cortex-A15) to kvmtool.

Everything is driven by FDT, including dynamic generation of virtio nodes
for MMIO devices (PCI is not used due to lack of a suitable host-bridge).

The virtual timers and virtual interrupt controller (VGIC) are provided
by the kernel and require very little in terms of userspace code.

Signed-off-by: Will Deacon <will.dea...@arm.com>
---
 tools/kvm/Makefile                           |   20 ++-
 tools/kvm/arm/aarch32/cortex-a15.c           |   98 ++++++++++
 tools/kvm/arm/aarch32/include/kvm/barrier.h  |   10 +
 tools/kvm/arm/aarch32/include/kvm/kvm-arch.h |   28 +++
 tools/kvm/arm/aarch32/kvm-cpu.c              |  111 +++++++++++
 tools/kvm/arm/aarch32/smp-pen.S              |   14 ++
 tools/kvm/arm/fdt.c                          |  269 ++++++++++++++++++++++++++
 tools/kvm/arm/gic.c                          |   86 ++++++++
 tools/kvm/arm/include/arm-common/gic.h       |   29 +++
 tools/kvm/arm/include/arm-common/kvm-arch.h  |   31 +++
 tools/kvm/arm/include/arm-common/smp.h       |    7 +
 tools/kvm/arm/include/kvm/kvm-cpu-arch.h     |   47 +++++
 tools/kvm/arm/ioport.c                       |    5 +
 tools/kvm/arm/irq.c                          |   25 +++
 tools/kvm/arm/kvm-cpu.c                      |  112 +++++++++++
 tools/kvm/arm/kvm.c                          |   69 +++++++
 tools/kvm/arm/smp.c                          |   13 ++
 17 files changed, 973 insertions(+), 1 deletions(-)
 create mode 100644 tools/kvm/arm/aarch32/cortex-a15.c
 create mode 100644 tools/kvm/arm/aarch32/include/kvm/barrier.h
 create mode 100644 tools/kvm/arm/aarch32/include/kvm/kvm-arch.h
 create mode 100644 tools/kvm/arm/aarch32/kvm-cpu.c
 create mode 100644 tools/kvm/arm/aarch32/smp-pen.S
 create mode 100644 tools/kvm/arm/fdt.c
 create mode 100644 tools/kvm/arm/gic.c
 create mode 100644 tools/kvm/arm/include/arm-common/gic.h
 create mode 100644 tools/kvm/arm/include/arm-common/kvm-arch.h
 create mode 100644 tools/kvm/arm/include/arm-common/smp.h
 create mode 100644 tools/kvm/arm/include/kvm/kvm-cpu-arch.h
 create mode 100644 tools/kvm/arm/ioport.c
 create mode 100644 tools/kvm/arm/irq.c
 create mode 100644 tools/kvm/arm/kvm-cpu.c
 create mode 100644 tools/kvm/arm/kvm.c
 create mode 100644 tools/kvm/arm/smp.c

diff --git a/tools/kvm/Makefile b/tools/kvm/Makefile
index 5da416f..2c9c19e 100644
--- a/tools/kvm/Makefile
+++ b/tools/kvm/Makefile
@@ -102,7 +102,8 @@ OBJS        += builtin-sandbox.o
 OBJS   += virtio/mmio.o
 
 # Translate uname -m into ARCH string
-ARCH ?= $(shell uname -m | sed -e s/i.86/i386/ -e s/ppc.*/powerpc/)
+ARCH ?= $(shell uname -m | sed -e s/i.86/i386/ -e s/ppc.*/powerpc/ \
+         -e s/armv7.*/arm/)
 
 ifeq ($(ARCH),i386)
        ARCH         := x86
@@ -157,6 +158,23 @@ ifeq ($(ARCH), powerpc)
        CFLAGS  += -m64
 endif
 
+# ARM
+OBJS_ARM_COMMON                := arm/fdt.o arm/gic.o arm/ioport.o arm/irq.o \
+                          arm/kvm.o arm/kvm-cpu.o arm/smp.o
+HDRS_ARM_COMMON                := arm/include
+ifeq ($(ARCH), arm)
+       DEFINES         += -DCONFIG_ARM
+       OBJS            += $(OBJS_ARM_COMMON)
+       OBJS            += arm/aarch32/cortex-a15.o
+       OBJS            += arm/aarch32/kvm-cpu.o
+       OTHEROBJS       += arm/aarch32/smp-pen.o
+       ARCH_INCLUDE    := $(HDRS_ARM_COMMON)
+       ARCH_INCLUDE    += -Iarm/aarch32/include
+       CFLAGS          += -march=armv7-a
+       CFLAGS          += -I../../scripts/dtc/libfdt
+       OTHEROBJS       += $(LIBFDT_OBJS)
+endif
+
 ###
 
 ifeq (,$(ARCH_INCLUDE))
diff --git a/tools/kvm/arm/aarch32/cortex-a15.c 
b/tools/kvm/arm/aarch32/cortex-a15.c
new file mode 100644
index 0000000..eac0bb9
--- /dev/null
+++ b/tools/kvm/arm/aarch32/cortex-a15.c
@@ -0,0 +1,98 @@
+#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_A15) {
+                       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-a15"));
+
+               if (kvm->nrcpus > 1) {
+                       _FDT(fdt_property_string(fdt, "enable-method",
+                                                "spin-table"));
+                       _FDT(fdt_property_cell(fdt, "cpu-release-addr",
+                                              kvm->arch.smp_jump_guest_start));
+               }
+
+               _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,armv7-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_a15__vcpu_init(struct kvm_cpu *vcpu)
+{
+       vcpu->generate_fdt_nodes = generate_fdt_nodes;
+       return 0;
+}
+
+static struct kvm_arm_target target_cortex_a15 = {
+       .id     = KVM_ARM_TARGET_CORTEX_A15,
+       .init   = cortex_a15__vcpu_init,
+};
+
+static int cortex_a15__core_init(struct kvm *kvm)
+{
+       return kvm_cpu__register_kvm_arm_target(&target_cortex_a15);
+}
+core_init(cortex_a15__core_init);
diff --git a/tools/kvm/arm/aarch32/include/kvm/barrier.h 
b/tools/kvm/arm/aarch32/include/kvm/barrier.h
new file mode 100644
index 0000000..ee8e643
--- /dev/null
+++ b/tools/kvm/arm/aarch32/include/kvm/barrier.h
@@ -0,0 +1,10 @@
+#ifndef KVM__KVM_BARRIER_H
+#define KVM__KVM_BARRIER_H
+
+#define dmb()  asm volatile ("dmb" : : : "memory")
+
+#define mb()   dmb()
+#define rmb()  dmb()
+#define wmb()  dmb()
+
+#endif
diff --git a/tools/kvm/arm/aarch32/include/kvm/kvm-arch.h 
b/tools/kvm/arm/aarch32/include/kvm/kvm-arch.h
new file mode 100644
index 0000000..eb51917
--- /dev/null
+++ b/tools/kvm/arm/aarch32/include/kvm/kvm-arch.h
@@ -0,0 +1,28 @@
+#ifndef KVM__KVM_ARCH_H
+#define KVM__KVM_ARCH_H
+
+#define ARM_LOMAP_MMIO_AREA    0x00000000UL
+#define ARM_LOMAP_AXI_AREA     0x40000000UL
+#define ARM_LOMAP_MEMORY_AREA  0x80000000UL
+#define ARM_LOMAP_MAX_MEMORY   0x7ffff000UL
+
+#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_SMP_PEN_SIZE       PAGE_SIZE
+#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 KVM_PCI_MMIO_AREA      ARM_LOMAP_AXI_AREA
+#define KVM_VIRTIO_MMIO_AREA   ARM_LOMAP_MMIO_AREA
+
+#include "arm-common/kvm-arch.h"
+
+#endif
diff --git a/tools/kvm/arm/aarch32/kvm-cpu.c b/tools/kvm/arm/aarch32/kvm-cpu.c
new file mode 100644
index 0000000..f00a2f1
--- /dev/null
+++ b/tools/kvm/arm/aarch32/kvm-cpu.c
@@ -0,0 +1,111 @@
+#include "kvm/kvm-cpu.h"
+#include "kvm/kvm.h"
+
+#include <asm/ptrace.h>
+
+#define ARM_CORE_REG(x)        (KVM_REG_ARM | KVM_REG_SIZE_U32 | 
KVM_REG_ARM_CORE | \
+                        KVM_REG_ARM_CORE_REG(x))
+
+void kvm_cpu__reset_vcpu(struct kvm_cpu *vcpu)
+{
+       struct kvm *kvm = vcpu->kvm;
+       struct kvm_one_reg reg;
+       u32 data;
+
+       /* Who said future-proofing was a good idea? */
+       reg.addr = (u64)(unsigned long)&data;
+
+       /* cpsr = IRQs/FIQs masked */
+       data    = PSR_I_BIT | PSR_F_BIT | SVC_MODE;
+       reg.id  = ARM_CORE_REG(usr_regs.ARM_cpsr);
+       if (ioctl(vcpu->vcpu_fd, KVM_SET_ONE_REG, &reg) < 0)
+               die_perror("KVM_SET_ONE_REG failed (cpsr)");
+
+       if (vcpu->cpu_id == 0) {
+               /* r0 = 0 */
+               data    = 0;
+               reg.id  = ARM_CORE_REG(usr_regs.ARM_r0);
+               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  = ARM_CORE_REG(usr_regs.ARM_r1);
+               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  = ARM_CORE_REG(usr_regs.ARM_r2);
+               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  = ARM_CORE_REG(usr_regs.ARM_pc);
+               if (ioctl(vcpu->vcpu_fd, KVM_SET_ONE_REG, &reg) < 0)
+                       die_perror("KVM_SET_ONE_REG failed (pc)");
+
+       } else {
+               /* Simply enter the pen */
+               data    = kvm->arch.smp_pen_guest_start;
+               reg.id  = ARM_CORE_REG(usr_regs.ARM_pc);
+               if (ioctl(vcpu->vcpu_fd, KVM_SET_ONE_REG, &reg) < 0)
+                       die_perror("KVM_SET_ONE_REG failed (SMP pc)");
+       }
+}
+
+void kvm_cpu__show_code(struct kvm_cpu *vcpu)
+{
+       struct kvm_one_reg reg;
+       u32 data;
+
+       reg.addr = (u64)(unsigned long)&data;
+
+       printf("*pc:\n");
+       reg.id = ARM_CORE_REG(usr_regs.ARM_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 (svc):\n");
+       reg.id = ARM_CORE_REG(svc_regs[1]);
+       if (ioctl(vcpu->vcpu_fd, KVM_GET_ONE_REG, &reg) < 0)
+               die("KVM_GET_ONE_REG failed (show_code @ LR_svc)");
+       data &= ~0x1;
+
+       kvm__dump_mem(vcpu->kvm, data, 32);
+       printf("\n");
+}
+
+void kvm_cpu__show_registers(struct kvm_cpu *vcpu)
+{
+       struct kvm_one_reg reg;
+       u32 data;
+       int debug_fd = kvm_cpu__get_debug_fd();
+
+       reg.addr        = (u64)(unsigned long)&data;
+       dprintf(debug_fd, "\n Registers:\n");
+
+       reg.id          = ARM_CORE_REG(usr_regs.ARM_pc);
+       if (ioctl(vcpu->vcpu_fd, KVM_GET_ONE_REG, &reg) < 0)
+               die("KVM_GET_ONE_REG failed (pc)");
+       dprintf(debug_fd, " PC:    0x%x\n", data);
+
+       reg.id          = ARM_CORE_REG(usr_regs.ARM_cpsr);
+       if (ioctl(vcpu->vcpu_fd, KVM_GET_ONE_REG, &reg) < 0)
+               die("KVM_GET_ONE_REG failed (cpsr)");
+       dprintf(debug_fd, " CPSR:  0x%x\n", data);
+
+       reg.id          = ARM_CORE_REG(svc_regs[0]);
+       if (ioctl(vcpu->vcpu_fd, KVM_GET_ONE_REG, &reg) < 0)
+               die("KVM_GET_ONE_REG failed (SP_svc)");
+       dprintf(debug_fd, " SP_svc:  0x%x\n", data);
+
+       reg.id          = ARM_CORE_REG(svc_regs[1]);
+       if (ioctl(vcpu->vcpu_fd, KVM_GET_ONE_REG, &reg) < 0)
+               die("KVM_GET_ONE_REG failed (LR_svc)");
+       dprintf(debug_fd, " LR_svc:  0x%x\n", data);
+}
diff --git a/tools/kvm/arm/aarch32/smp-pen.S b/tools/kvm/arm/aarch32/smp-pen.S
new file mode 100644
index 0000000..5e25753
--- /dev/null
+++ b/tools/kvm/arm/aarch32/smp-pen.S
@@ -0,0 +1,14 @@
+       .arm
+       .globl  smp_pen_start
+       .globl  smp_pen_end
+       .globl  smp_jump_addr
+       .align
+smp_pen_start:
+       adr     r0, smp_jump_addr
+       wfi
+       ldr     r1, [r0]
+       mov     pc, r1
+       .align
+smp_jump_addr:
+       .long   0xdeadc0de
+smp_pen_end:
diff --git a/tools/kvm/arm/fdt.c b/tools/kvm/arm/fdt.c
new file mode 100644
index 0000000..97b92e0
--- /dev/null
+++ b/tools/kvm/arm/fdt.c
@@ -0,0 +1,269 @@
+#include "kvm/devices.h"
+#include "kvm/fdt.h"
+#include "kvm/kvm.h"
+#include "kvm/kvm-cpu.h"
+#include "kvm/virtio-mmio.h"
+
+#include "arm-common/gic.h"
+
+#include <stdbool.h>
+
+#include <asm/setup.h>
+#include <linux/byteorder.h>
+#include <linux/kernel.h>
+#include <linux/sizes.h>
+
+#define DEBUG                  0
+#define DEBUG_FDT_DUMP_FILE    "/tmp/kvmtool.dtb"
+
+static char kern_cmdline[COMMAND_LINE_SIZE];
+
+bool kvm__load_firmware(struct kvm *kvm, const char *firmware_filename)
+{
+       return false;
+}
+
+int kvm__arch_setup_firmware(struct kvm *kvm)
+{
+       return 0;
+}
+
+#if DEBUG
+static void dump_fdt(void *fdt)
+{
+       int count, fd;
+
+       fd = open(DEBUG_FDT_DUMP_FILE, O_CREAT | O_TRUNC | O_RDWR, 0666);
+       if (fd < 0)
+               die("Failed to write dtb to %s", DEBUG_FDT_DUMP_FILE);
+
+       count = write(fd, fdt, FDT_MAX_SIZE);
+       if (count < 0)
+               die_perror("Failed to dump dtb");
+
+       pr_info("Wrote %d bytes to dtb %s\n", count, DEBUG_FDT_DUMP_FILE);
+       close(fd);
+}
+#else
+static void dump_fdt(void *fdt) { }
+#endif
+
+#define DEVICE_NAME_MAX_LEN 32
+static void generate_virtio_mmio_node(void *fdt, struct virtio_mmio *vmmio)
+{
+       char dev_name[DEVICE_NAME_MAX_LEN];
+       u64 addr = vmmio->addr;
+       u64 reg_prop[] = {
+               cpu_to_fdt64(addr),
+               cpu_to_fdt64(VIRTIO_MMIO_IO_SIZE)
+       };
+       u32 irq_prop[] = {
+               cpu_to_fdt32(GIC_FDT_IRQ_TYPE_SPI),
+               cpu_to_fdt32(vmmio->irq - GIC_SPI_IRQ_BASE),
+               cpu_to_fdt32(GIC_FDT_IRQ_FLAGS_EDGE_LO_HI),
+       };
+
+       snprintf(dev_name, DEVICE_NAME_MAX_LEN, "virtio@%llx", addr);
+
+       _FDT(fdt_begin_node(fdt, dev_name));
+       _FDT(fdt_property_string(fdt, "compatible", "virtio,mmio"));
+       _FDT(fdt_property(fdt, "reg", reg_prop, sizeof(reg_prop)));
+       _FDT(fdt_property(fdt, "interrupts", irq_prop, sizeof(irq_prop)));
+       _FDT(fdt_end_node(fdt));
+}
+
+static int setup_fdt(struct kvm *kvm)
+{
+       int devid;
+       u8 staging_fdt[FDT_MAX_SIZE];
+       u32 gic_phandle         = fdt__alloc_phandle();
+       u64 mem_reg_prop[]      = {
+               cpu_to_fdt64(kvm->arch.memory_guest_start),
+               cpu_to_fdt64(kvm->ram_size),
+       };
+       void *fdt               = staging_fdt;
+       void *fdt_dest          = guest_flat_to_host(kvm,
+                                                    kvm->arch.dtb_guest_start);
+       void (*generate_cpu_nodes)(void *, struct kvm *, u32)
+                               = kvm->cpus[0]->generate_fdt_nodes;
+
+       /* Create new tree without a reserve map */
+       _FDT(fdt_create(fdt, FDT_MAX_SIZE));
+       if (kvm->nrcpus > 1)
+               _FDT(fdt_add_reservemap_entry(fdt,
+                                             kvm->arch.smp_pen_guest_start,
+                                             ARM_SMP_PEN_SIZE));
+       _FDT(fdt_finish_reservemap(fdt));
+
+       /* Header */
+       _FDT(fdt_begin_node(fdt, ""));
+       _FDT(fdt_property_cell(fdt, "interrupt-parent", gic_phandle));
+       _FDT(fdt_property_string(fdt, "compatible", "linux,dummy-virt"));
+       _FDT(fdt_property_cell(fdt, "#address-cells", 0x2));
+       _FDT(fdt_property_cell(fdt, "#size-cells", 0x2));
+
+       /* /chosen */
+       _FDT(fdt_begin_node(fdt, "chosen"));
+       _FDT(fdt_property_string(fdt, "bootargs", kern_cmdline));
+
+       /* Initrd */
+       if (kvm->arch.initrd_size != 0) {
+               u32 ird_st_prop = cpu_to_fdt64(kvm->arch.initrd_guest_start);
+               u32 ird_end_prop = cpu_to_fdt64(kvm->arch.initrd_guest_start +
+                                              kvm->arch.initrd_size);
+
+               _FDT(fdt_property(fdt, "linux,initrd-start",
+                                  &ird_st_prop, sizeof(ird_st_prop)));
+               _FDT(fdt_property(fdt, "linux,initrd-end",
+                                  &ird_end_prop, sizeof(ird_end_prop)));
+       }
+       _FDT(fdt_end_node(fdt));
+
+       /* Memory */
+       _FDT(fdt_begin_node(fdt, "memory"));
+       _FDT(fdt_property_string(fdt, "device_type", "memory"));
+       _FDT(fdt_property(fdt, "reg", mem_reg_prop, sizeof(mem_reg_prop)));
+       _FDT(fdt_end_node(fdt));
+
+       /* CPU and peripherals (interrupt controller, timers, etc) */
+       if (generate_cpu_nodes)
+               generate_cpu_nodes(fdt, kvm, gic_phandle);
+
+       /* Virtio MMIO devices */
+       for (devid = 0; devid < KVM_MAX_DEVICES; ++devid) {
+               struct device_header *dev_hdr = device__find_dev(devid);
+
+               if (!dev_hdr || dev_hdr->bus_type != DEVICE_BUS_MMIO)
+                       continue;
+
+               generate_virtio_mmio_node(fdt, dev_hdr->data);
+       }
+
+       /* Finalise. */
+       _FDT(fdt_end_node(fdt));
+       _FDT(fdt_finish(fdt));
+
+       _FDT(fdt_open_into(fdt, fdt_dest, FDT_MAX_SIZE));
+       _FDT(fdt_pack(fdt_dest));
+
+       dump_fdt(fdt_dest);
+       return 0;
+}
+firmware_init(setup_fdt);
+
+static int read_image(int fd, void **pos, void *limit)
+{
+       int count;
+
+       while (((count = read(fd, *pos, SZ_64K)) > 0) && *pos <= limit)
+               *pos += count;
+
+       if (pos < 0)
+               die_perror("read");
+
+       return *pos < limit ? 0 : -ENOMEM;
+}
+
+#define FDT_ALIGN      SZ_2M
+#define INITRD_ALIGN   4
+#define SMP_PEN_ALIGN  PAGE_SIZE
+int load_flat_binary(struct kvm *kvm, int fd_kernel, int fd_initrd,
+                    const char *kernel_cmdline)
+{
+       void *pos, *kernel_end, *limit;
+       unsigned long guest_addr;
+
+       if (lseek(fd_kernel, 0, SEEK_SET) < 0)
+               die_perror("lseek");
+
+       /*
+        * Linux requires the initrd, pen and dtb to be mapped inside
+        * lowmem, so we can't just place them at the top of memory.
+        */
+       limit = kvm->ram_start + min(kvm->ram_size, (u64)SZ_256M) - 1;
+
+       pos = kvm->ram_start + ARM_KERN_OFFSET;
+       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.");
+
+       kernel_end = pos;
+       pr_info("Loaded kernel to 0x%lx (%ld bytes)",
+               kvm->arch.kern_guest_start,
+               host_to_guest_flat(kvm, pos) - kvm->arch.kern_guest_start);
+
+       /*
+        * Now load backwards from the end of memory so the kernel
+        * decompressor has plenty of space to work with. First up is
+        * the SMP pen if we have more than one virtual CPU...
+        */
+       pos = limit;
+       if (kvm->cfg.nrcpus > 1) {
+               pos -= (ARM_SMP_PEN_SIZE + SMP_PEN_ALIGN);
+               guest_addr = ALIGN(host_to_guest_flat(kvm, pos), SMP_PEN_ALIGN);
+               pos = guest_flat_to_host(kvm, guest_addr);
+               if (pos < kernel_end)
+                       die("SMP pen overlaps with kernel image.");
+
+               kvm->arch.smp_pen_guest_start = guest_addr;
+               pr_info("Placing SMP pen at 0x%lx - 0x%lx",
+                       kvm->arch.smp_pen_guest_start,
+                       host_to_guest_flat(kvm, limit));
+               limit = pos;
+       }
+
+       /* ...now the device tree blob... */
+       pos -= (FDT_MAX_SIZE + FDT_ALIGN);
+       guest_addr = ALIGN(host_to_guest_flat(kvm, pos), FDT_ALIGN);
+       pos = guest_flat_to_host(kvm, guest_addr);
+       if (pos < kernel_end)
+               die("fdt overlaps with kernel image.");
+
+       kvm->arch.dtb_guest_start = guest_addr;
+       pr_info("Placing fdt at 0x%lx - 0x%lx",
+               kvm->arch.dtb_guest_start,
+               host_to_guest_flat(kvm, limit));
+       limit = pos;
+
+       /* ... and finally the initrd, if we have one. */
+       if (fd_initrd != -1) {
+               struct stat sb;
+               unsigned long initrd_start;
+
+               if (lseek(fd_initrd, 0, SEEK_SET) < 0)
+                       die_perror("lseek");
+
+               if (fstat(fd_initrd, &sb))
+                       die_perror("fstat");
+
+               pos -= (sb.st_size + INITRD_ALIGN);
+               guest_addr = ALIGN(host_to_guest_flat(kvm, pos), INITRD_ALIGN);
+               pos = guest_flat_to_host(kvm, guest_addr);
+               if (pos < kernel_end)
+                       die("initrd overlaps with kernel image.");
+
+               initrd_start = guest_addr;
+               if (read_image(fd_initrd, &pos, limit) == -ENOMEM)
+                       die("initrd too big to contain in guest memory.");
+
+               kvm->arch.initrd_guest_start = initrd_start;
+               kvm->arch.initrd_size = host_to_guest_flat(kvm, pos) - 
initrd_start;
+               pr_info("Loaded initrd to 0x%lx (%ld bytes)",
+                       kvm->arch.initrd_guest_start,
+                       kvm->arch.initrd_size);
+       } else {
+               kvm->arch.initrd_size = 0;
+       }
+
+       strncpy(kern_cmdline, kernel_cmdline, COMMAND_LINE_SIZE);
+       kern_cmdline[COMMAND_LINE_SIZE - 1] = '\0';
+
+       return true;
+}
+
+bool load_bzimage(struct kvm *kvm, int fd_kernel,
+                 int fd_initrd, const char *kernel_cmdline, u16 vidmode)
+{
+       /* To b or not to b? That is the zImage. */
+       return false;
+}
diff --git a/tools/kvm/arm/gic.c b/tools/kvm/arm/gic.c
new file mode 100644
index 0000000..f5fa7c7
--- /dev/null
+++ b/tools/kvm/arm/gic.c
@@ -0,0 +1,86 @@
+#include "kvm/fdt.h"
+#include "kvm/kvm.h"
+#include "kvm/virtio.h"
+
+#include "arm-common/gic.h"
+
+#include <linux/byteorder.h>
+#include <linux/kvm.h>
+
+static int irq_ids;
+
+int gic__alloc_irqnum(void)
+{
+       int irq = GIC_SPI_IRQ_BASE + irq_ids++;
+
+       if (irq > KVM_MAX_IRQ)
+               die("IRQ limit %d reached!", KVM_MAX_IRQ);
+
+       return irq;
+}
+
+int gic__init_irqchip(struct kvm *kvm)
+{
+       int err;
+       struct kvm_device_address gic_addr[] = {
+               [0] = {
+                       .id = (KVM_ARM_DEVICE_VGIC_V2 << KVM_DEVICE_ID_SHIFT) |\
+                              KVM_VGIC_V2_ADDR_TYPE_DIST,
+                       .addr = ARM_GIC_DIST_BASE,
+               },
+               [1] = {
+                       .id = (KVM_ARM_DEVICE_VGIC_V2 << KVM_DEVICE_ID_SHIFT) |\
+                              KVM_VGIC_V2_ADDR_TYPE_CPU,
+                       .addr = ARM_GIC_CPUI_BASE,
+               }
+       };
+
+       err = ioctl(kvm->vm_fd, KVM_CREATE_IRQCHIP);
+       if (err)
+               return err;
+
+       err = ioctl(kvm->vm_fd, KVM_SET_DEVICE_ADDRESS, &gic_addr[0]);
+       if (err)
+               return err;
+
+       err = ioctl(kvm->vm_fd, KVM_SET_DEVICE_ADDRESS, &gic_addr[1]);
+       return err;
+}
+
+void gic__generate_fdt_nodes(void *fdt, u32 phandle)
+{
+       u64 reg_prop[] = {
+               cpu_to_fdt64(ARM_GIC_DIST_BASE), 
cpu_to_fdt64(ARM_GIC_DIST_SIZE),
+               cpu_to_fdt64(ARM_GIC_CPUI_BASE), 
cpu_to_fdt64(ARM_GIC_CPUI_SIZE),
+       };
+
+       _FDT(fdt_begin_node(fdt, "intc"));
+       _FDT(fdt_property_string(fdt, "compatible", "arm,cortex-a15-gic"));
+       _FDT(fdt_property_cell(fdt, "#interrupt-cells", GIC_FDT_IRQ_NUM_CELLS));
+       _FDT(fdt_property(fdt, "interrupt-controller", NULL, 0));
+       _FDT(fdt_property(fdt, "reg", reg_prop, sizeof(reg_prop)));
+       _FDT(fdt_property_cell(fdt, "phandle", phandle));
+       _FDT(fdt_end_node(fdt));
+}
+
+#define KVM_IRQCHIP_IRQ(x) (KVM_ARM_IRQ_TYPE_SPI << KVM_ARM_IRQ_TYPE_SHIFT) |\
+                          ((x) & KVM_ARM_IRQ_NUM_MASK)
+
+void kvm__irq_line(struct kvm *kvm, int irq, int level)
+{
+       struct kvm_irq_level irq_level = {
+               .irq    = KVM_IRQCHIP_IRQ(irq),
+               .level  = !!level,
+       };
+
+       if (irq < GIC_SPI_IRQ_BASE || irq > KVM_MAX_IRQ)
+               pr_warning("Ignoring invalid GIC IRQ %d", irq);
+       else if (ioctl(kvm->vm_fd, KVM_IRQ_LINE, &irq_level) < 0)
+               pr_warning("Could not KVM_IRQ_LINE for irq %d", irq);
+}
+
+void kvm__irq_trigger(struct kvm *kvm, int irq)
+{
+       kvm__irq_line(kvm, irq, VIRTIO_IRQ_HIGH);
+       kvm__irq_line(kvm, irq, VIRTIO_IRQ_LOW);
+}
diff --git a/tools/kvm/arm/include/arm-common/gic.h 
b/tools/kvm/arm/include/arm-common/gic.h
new file mode 100644
index 0000000..4d85dd9
--- /dev/null
+++ b/tools/kvm/arm/include/arm-common/gic.h
@@ -0,0 +1,29 @@
+#ifndef ARM_COMMON__GIC_H
+#define ARM_COMMON__GIC_H
+
+#define GIC_SGI_IRQ_BASE               0
+#define GIC_PPI_IRQ_BASE               16
+#define GIC_SPI_IRQ_BASE               32
+
+#define GIC_FDT_IRQ_NUM_CELLS          3
+
+#define GIC_FDT_IRQ_TYPE_SPI           0
+#define GIC_FDT_IRQ_TYPE_PPI           1
+
+#define GIC_FDT_IRQ_FLAGS_EDGE_LO_HI   1
+#define GIC_FDT_IRQ_FLAGS_EDGE_HI_LO   2
+#define GIC_FDT_IRQ_FLAGS_LEVEL_HI     4
+#define GIC_FDT_IRQ_FLAGS_LEVEL_LO     8
+
+#define GIC_FDT_IRQ_PPI_CPU_SHIFT      8
+#define GIC_FDT_IRQ_PPI_CPU_MASK       (0xff << GIC_FDT_IRQ_PPI_CPU_SHIFT)
+
+#define KVM_MAX_IRQ                    255
+
+struct kvm;
+
+int gic__alloc_irqnum(void);
+int gic__init_irqchip(struct kvm *kvm);
+void gic__generate_fdt_nodes(void *fdt, u32 phandle);
+
+#endif /* ARM_COMMON__GIC_H */
diff --git a/tools/kvm/arm/include/arm-common/kvm-arch.h 
b/tools/kvm/arm/include/arm-common/kvm-arch.h
new file mode 100644
index 0000000..0b20437
--- /dev/null
+++ b/tools/kvm/arm/include/arm-common/kvm-arch.h
@@ -0,0 +1,31 @@
+#ifndef ARM_COMMON__KVM_ARCH_H
+#define ARM_COMMON__KVM_ARCH_H
+
+#include <stdbool.h>
+#include <linux/types.h>
+
+#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;
+       return phys_addr >= KVM_VIRTIO_MMIO_AREA && phys_addr < limit;
+}
+
+static inline bool arm_addr_in_pci_mmio_region(u64 phys_addr)
+{
+       u64 limit = KVM_PCI_MMIO_AREA + ARM_PCI_MMIO_SIZE;
+       return phys_addr >= KVM_PCI_MMIO_AREA && phys_addr < limit;
+}
+
+struct kvm_arch {
+       unsigned long   memory_guest_start;
+       unsigned long   kern_guest_start;
+       unsigned long   initrd_guest_start;
+       unsigned long   initrd_size;
+       unsigned long   dtb_guest_start;
+       unsigned long   smp_pen_guest_start;
+       unsigned long   smp_jump_guest_start;
+};
+
+#endif /* ARM_COMMON__KVM_ARCH_H */
diff --git a/tools/kvm/arm/include/arm-common/smp.h 
b/tools/kvm/arm/include/arm-common/smp.h
new file mode 100644
index 0000000..993a3e6
--- /dev/null
+++ b/tools/kvm/arm/include/arm-common/smp.h
@@ -0,0 +1,7 @@
+#ifndef ARM_COMMON__SMP_H
+#define ARM_COMMON__SMP_H
+
+extern u8 smp_pen_start, smp_pen_end, smp_jump_addr;
+void smp_pen_init(struct kvm *kvm);
+
+#endif /* ARM_COMMON__SMP_H */
diff --git a/tools/kvm/arm/include/kvm/kvm-cpu-arch.h 
b/tools/kvm/arm/include/kvm/kvm-cpu-arch.h
new file mode 100644
index 0000000..d4618e9
--- /dev/null
+++ b/tools/kvm/arm/include/kvm/kvm-cpu-arch.h
@@ -0,0 +1,47 @@
+#ifndef ARM_COMMON__KVM_CPU_ARCH_H
+#define ARM_COMMON__KVM_CPU_ARCH_H
+
+#include <linux/kvm.h>
+#include <pthread.h>
+#include <stdbool.h>
+
+struct kvm;
+
+struct kvm_cpu {
+       pthread_t       thread;
+
+       unsigned long   cpu_id;
+       unsigned long   cpu_type;
+
+       struct kvm      *kvm;
+       int             vcpu_fd;
+       struct kvm_run  *kvm_run;
+
+       u8              is_running;
+       u8              paused;
+       u8              needs_nmi;
+
+       struct kvm_coalesced_mmio_ring  *ring;
+
+       void            (*generate_fdt_nodes)(void *fdt, struct kvm* kvm,
+                                             u32 gic_phandle);
+};
+
+struct kvm_arm_target {
+       u32     id;
+       int     (*init)(struct kvm_cpu *vcpu);
+};
+
+int kvm_cpu__register_kvm_arm_target(struct kvm_arm_target *target);
+
+static inline bool kvm_cpu__emulate_io(struct kvm *kvm, u16 port, void *data,
+                                      int direction, int size, u32 count)
+{
+       return false;
+}
+
+bool kvm_cpu__emulate_mmio(struct kvm *kvm, u64 phys_addr, u8 *data, u32 len,
+                          u8 is_write);
+
+#endif /* ARM_COMMON__KVM_CPU_ARCH_H */
+
diff --git a/tools/kvm/arm/ioport.c b/tools/kvm/arm/ioport.c
new file mode 100644
index 0000000..3c03fa0
--- /dev/null
+++ b/tools/kvm/arm/ioport.c
@@ -0,0 +1,5 @@
+#include "kvm/ioport.h"
+
+void ioport__setup_arch(struct kvm *kvm)
+{
+}
diff --git a/tools/kvm/arm/irq.c b/tools/kvm/arm/irq.c
new file mode 100644
index 0000000..a17c722
--- /dev/null
+++ b/tools/kvm/arm/irq.c
@@ -0,0 +1,25 @@
+#include "kvm/devices.h"
+#include "kvm/irq.h"
+#include "kvm/kvm.h"
+#include "kvm/util.h"
+
+#include "arm-common/gic.h"
+
+static int device_ids;
+
+int irq__register_device(u32 dev, u8 *num, u8 *pin, u8 *line)
+{
+       if (device_ids >= KVM_MAX_DEVICES)
+               die("Device limit %d reached!", KVM_MAX_DEVICES);
+
+       *num    = device_ids++;
+       *line   = gic__alloc_irqnum();
+
+       return 0;
+}
+
+int irq__add_msix_route(struct kvm *kvm, struct msi_msg *msg)
+{
+       die(__FUNCTION__);
+       return 0;
+}
diff --git a/tools/kvm/arm/kvm-cpu.c b/tools/kvm/arm/kvm-cpu.c
new file mode 100644
index 0000000..f659118
--- /dev/null
+++ b/tools/kvm/arm/kvm-cpu.c
@@ -0,0 +1,112 @@
+#include "kvm/kvm.h"
+#include "kvm/kvm-cpu.h"
+
+#include "arm-common/smp.h"
+
+static int debug_fd;
+
+void kvm_cpu__set_debug_fd(int fd)
+{
+       debug_fd = fd;
+}
+
+int kvm_cpu__get_debug_fd(void)
+{
+       return debug_fd;
+}
+
+static struct kvm_arm_target *kvm_arm_targets[KVM_ARM_NUM_TARGETS];
+int kvm_cpu__register_kvm_arm_target(struct kvm_arm_target *target)
+{
+       unsigned int i = 0;
+
+       for (i = 0; i < ARRAY_SIZE(kvm_arm_targets); ++i) {
+               if (!kvm_arm_targets[i]) {
+                       kvm_arm_targets[i] = target;
+                       return 0;
+               }
+       }
+
+       return -ENOSPC;
+}
+
+struct kvm_cpu *kvm_cpu__arch_init(struct kvm *kvm, unsigned long cpu_id)
+{
+       struct kvm_cpu *vcpu;
+       int coalesced_offset, mmap_size, err = -1;
+       unsigned int i;
+       struct kvm_vcpu_init vcpu_init = { };
+
+       vcpu = calloc(1, sizeof(struct kvm_cpu));
+       if (!vcpu)
+               return NULL;
+
+       vcpu->vcpu_fd = ioctl(kvm->vm_fd, KVM_CREATE_VCPU, cpu_id);
+       if (vcpu->vcpu_fd < 0)
+               die_perror("KVM_CREATE_VCPU ioctl");
+
+       mmap_size = ioctl(kvm->sys_fd, KVM_GET_VCPU_MMAP_SIZE, 0);
+       if (mmap_size < 0)
+               die_perror("KVM_GET_VCPU_MMAP_SIZE ioctl");
+
+       vcpu->kvm_run = mmap(NULL, mmap_size, PROT_RW, MAP_SHARED,
+                            vcpu->vcpu_fd, 0);
+       if (vcpu->kvm_run == MAP_FAILED)
+               die("unable to mmap vcpu fd");
+
+       /* Find an appropriate target CPU type. */
+       for (i = 0; i < ARRAY_SIZE(kvm_arm_targets); ++i) {
+               vcpu_init.target = kvm_arm_targets[i]->id;
+               err = ioctl(vcpu->vcpu_fd, KVM_ARM_VCPU_INIT, &vcpu_init);
+               if (!err)
+                       break;
+       }
+
+       if (err || kvm_arm_targets[i]->init(vcpu))
+               die("Unable to initialise ARM vcpu");
+
+       coalesced_offset = ioctl(kvm->sys_fd, KVM_CHECK_EXTENSION,
+                                KVM_CAP_COALESCED_MMIO);
+       if (coalesced_offset)
+               vcpu->ring = (void *)vcpu->kvm_run +
+                            (coalesced_offset * PAGE_SIZE);
+
+       if (cpu_id != 0)
+               smp_pen_init(kvm);
+
+       /* Populate the vcpu structure. */
+       vcpu->kvm               = kvm;
+       vcpu->cpu_id            = cpu_id;
+       vcpu->cpu_type          = vcpu_init.target;
+       vcpu->is_running        = true;
+       return vcpu;
+}
+
+void kvm_cpu__arch_nmi(struct kvm_cpu *cpu)
+{
+}
+
+void kvm_cpu__delete(struct kvm_cpu *vcpu)
+{
+       free(vcpu);
+}
+
+bool kvm_cpu__handle_exit(struct kvm_cpu *vcpu)
+{
+       return false;
+}
+
+bool kvm_cpu__emulate_mmio(struct kvm *kvm, u64 phys_addr, u8 *data, u32 len,
+                          u8 is_write)
+{
+       if (arm_addr_in_virtio_mmio_region(phys_addr))
+               return kvm__emulate_mmio(kvm, phys_addr, data, len, is_write);
+       else if (arm_addr_in_pci_mmio_region(phys_addr))
+               die("PCI emulation not supported on ARM!");
+
+       return false;
+}
+
+void kvm_cpu__show_page_tables(struct kvm_cpu *vcpu)
+{
+}
diff --git a/tools/kvm/arm/kvm.c b/tools/kvm/arm/kvm.c
new file mode 100644
index 0000000..aa2673a
--- /dev/null
+++ b/tools/kvm/arm/kvm.c
@@ -0,0 +1,69 @@
+#include "kvm/kvm.h"
+#include "kvm/term.h"
+#include "kvm/util.h"
+#include "kvm/virtio-console.h"
+
+#include "arm-common/gic.h"
+
+#include <linux/kernel.h>
+#include <linux/kvm.h>
+
+struct kvm_ext kvm_req_ext[] = {
+       { DEFINE_KVM_EXT(KVM_CAP_IRQCHIP) },
+       { DEFINE_KVM_EXT(KVM_CAP_ONE_REG) },
+       { 0, 0 },
+};
+
+bool kvm__arch_cpu_supports_vm(void)
+{
+       /* The KVM capability check is enough. */
+       return true;
+}
+
+void kvm__init_ram(struct kvm *kvm)
+{
+       int err;
+       u64 phys_start, phys_size;
+       void *host_mem;
+
+       phys_start      = ARM_MEMORY_AREA;
+       phys_size       = kvm->ram_size;
+       host_mem        = kvm->ram_start;
+
+       err = kvm__register_mem(kvm, phys_start, phys_size, host_mem);
+       if (err)
+               die("Failed to register %lld bytes of memory at physical "
+                   "address 0x%llx [err %d]", phys_size, phys_start, err);
+
+       kvm->arch.memory_guest_start = phys_start;
+}
+
+void kvm__arch_delete_ram(struct kvm *kvm)
+{
+       munmap(kvm->ram_start, kvm->ram_size);
+}
+
+void kvm__arch_periodic_poll(struct kvm *kvm)
+{
+       if (term_readable(0))
+               virtio_console__inject_interrupt(kvm);
+}
+
+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_start = mmap_anon_or_hugetlbfs(kvm, NULL, kvm->ram_size);
+       if (kvm->ram_start == MAP_FAILED)
+               die("Failed to map %lld bytes for guest memory (%d)",
+                   kvm->ram_size, errno);
+       madvise(kvm->ram_start, kvm->ram_size, MADV_MERGEABLE);
+
+       /* Initialise the virtual GIC. */
+       if (gic__init_irqchip(kvm))
+               die("Failed to initialise virtual GIC");
+}
diff --git a/tools/kvm/arm/smp.c b/tools/kvm/arm/smp.c
new file mode 100644
index 0000000..73e58bd
--- /dev/null
+++ b/tools/kvm/arm/smp.c
@@ -0,0 +1,13 @@
+#include "kvm/kvm.h"
+
+#include "arm-common/smp.h"
+
+void smp_pen_init(struct kvm *kvm)
+{
+       unsigned long pen_size = &smp_pen_end - &smp_pen_start;
+       unsigned long pen_start = kvm->arch.smp_pen_guest_start;
+       unsigned long jump_offset = &smp_jump_addr - &smp_pen_start;
+
+       kvm->arch.smp_jump_guest_start = pen_start + jump_offset;
+       memcpy(guest_flat_to_host(kvm, pen_start), &smp_pen_start, pen_size);
+}
-- 
1.7.4.1

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to