diff mbox

[RFC,16/16] kvm tools: add support for ARMv7 processors

Message ID 1352721450-11340-17-git-send-email-will.deacon@arm.com (mailing list archive)
State New, archived
Headers show

Commit Message

Will Deacon Nov. 12, 2012, 11:57 a.m. UTC
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.deacon@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

Comments

Pekka Enberg Nov. 13, 2012, 7:39 a.m. UTC | #1
On Mon, 12 Nov 2012, Will Deacon wrote:
> 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.deacon@arm.com>

I'm happy with this but I'm not really an ARM guy. Is there anyone in the 
ARM/KVM community who is interested in reviewing this?

			Pekka
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Matt Evans Nov. 13, 2012, 10:21 a.m. UTC | #2
Hi Pekka,

On 13 November 2012 07:40 Pekka Enberg wrote:

> On Mon, 12 Nov 2012, Will Deacon wrote:
> > 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.deacon@arm.com>
>
> I'm happy with this but I'm not really an ARM guy. Is there anyone in
> the
> ARM/KVM community who is interested in reviewing this?

For the sake of transparency, I sent some minor feedback to Will off-list so that my (Outlook >:( ) broken linewraps & legal autoappend signature (see above and below) wouldn't pollute the thread on-list.  He'll use my IMAP address next time. ;-)

I *think* Will was going to make some small changes, if you've already merged it then a follow-up set perhaps?

(Modulo minor changes,
Acked-By: Matt Evans <matt@ozlabs.org>
)

Cheers,


Matt


-- IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium.  Thank you.

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Pekka Enberg Nov. 13, 2012, 10:28 a.m. UTC | #3
On Tue, Nov 13, 2012 at 12:21 PM, Matt Evans <Matt.Evans@arm.com> wrote:
> I *think* Will was going to make some small changes, if you've already merged it then a follow-up set perhaps?

I only merged the non-ARM specific changes which looked good to me.
But sure, please send an incremental patch if you need to fix them up,
Will.
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Will Deacon Nov. 13, 2012, 6:34 p.m. UTC | #4
On Tue, Nov 13, 2012 at 10:28:20AM +0000, Pekka Enberg wrote:
> On Tue, Nov 13, 2012 at 12:21 PM, Matt Evans <Matt.Evans@arm.com> wrote:
> > I *think* Will was going to make some small changes, if you've already merged it then a follow-up set perhaps?
> 
> I only merged the non-ARM specific changes which looked good to me.
> But sure, please send an incremental patch if you need to fix them up,
> Will.

Thanks Pekka! The patches you merged are all fine, so I'll send a v2 against
kvmtool master when I've updated the remaining patches to incorporate
comments from Sasha and Matt. I also need to investigate the build failure
you saw, because I've not managed to reproduce it myself.

Will

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

Patch

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);
+}