diff --git a/tools/kvm/Makefile b/tools/kvm/Makefile
index 29f18220c749..b87265189d1f 100644
--- a/tools/kvm/Makefile
+++ b/tools/kvm/Makefile
@@ -158,7 +158,7 @@ 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/timer.o
+			   arm/kvm.o arm/kvm-cpu.o arm/pci.o arm/timer.o
 HDRS_ARM_COMMON		:= arm/include
 ifeq ($(ARCH), arm)
 	DEFINES		+= -DCONFIG_ARM
diff --git a/tools/kvm/arm/fdt.c b/tools/kvm/arm/fdt.c
index 9a34d9804930..d90fc9e1734f 100644
--- a/tools/kvm/arm/fdt.c
+++ b/tools/kvm/arm/fdt.c
@@ -5,6 +5,7 @@
 #include "kvm/virtio-mmio.h"
 
 #include "arm-common/gic.h"
+#include "arm-common/pci.h"
 
 #include <stdbool.h>
 
@@ -155,6 +156,9 @@ static int setup_fdt(struct kvm *kvm)
 		dev_hdr = device__next_dev(dev_hdr);
 	}
 
+	/* PCI host controller */
+	pci__generate_fdt_nodes(fdt, gic_phandle);
+
 	/* PSCI firmware */
 	_FDT(fdt_begin_node(fdt, "psci"));
 	_FDT(fdt_property_string(fdt, "compatible", "arm,psci"));
diff --git a/tools/kvm/arm/include/arm-common/pci.h b/tools/kvm/arm/include/arm-common/pci.h
new file mode 100644
index 000000000000..ee87725ee82f
--- /dev/null
+++ b/tools/kvm/arm/include/arm-common/pci.h
@@ -0,0 +1,6 @@
+#ifndef ARM_COMMON__PCI_H
+#define ARM_COMMON__PCI_H
+
+void pci__generate_fdt_nodes(void *fdt, u32 gic_phandle);
+
+#endif /* ARM_COMMON__PCI_H */
diff --git a/tools/kvm/arm/pci.c b/tools/kvm/arm/pci.c
new file mode 100644
index 000000000000..ce1932ecc3ee
--- /dev/null
+++ b/tools/kvm/arm/pci.c
@@ -0,0 +1,118 @@
+#include "kvm/devices.h"
+#include "kvm/fdt.h"
+#include "kvm/of_pci.h"
+#include "kvm/pci.h"
+#include "kvm/util.h"
+
+#include "arm-common/pci.h"
+
+/*
+ * An entry in the interrupt-map table looks like:
+ * <pci unit address> <pci interrupt pin> <gic phandle> <gic interrupt>
+ */
+
+struct of_gic_irq {
+	u32 type, num, flags;
+} __attribute__((packed));
+
+struct of_interrupt_map_entry {
+	struct of_pci_irq_mask		pci_irq_mask;
+	u32				gic_phandle;
+	struct of_gic_irq		gic_irq;
+} __attribute__((packed));
+
+void pci__generate_fdt_nodes(void *fdt, u32 gic_phandle)
+{
+	struct device_header *dev_hdr;
+	struct of_interrupt_map_entry irq_map[OF_PCI_IRQ_MAP_MAX];
+	unsigned nentries = 0;
+	/* Describe the memory ranges (config and memory) */
+	struct of_pci_ranges_entry ranges[] = {
+		{
+			.pci_addr = {
+				.hi	= cpu_to_fdt32(of_pci_b_ss(OF_PCI_SS_CONFIG)),
+				.mid	= 0,
+				.lo	= 0,
+			},
+			.cpu_addr	= cpu_to_fdt64(KVM_PCI_CFG_AREA),
+			.length		= cpu_to_fdt64(ARM_PCI_CFG_SIZE),
+		},
+		{
+			.pci_addr = {
+				.hi	= cpu_to_fdt32(of_pci_b_ss(OF_PCI_SS_IO)),
+				.mid	= 0,
+				.lo	= 0,
+			},
+			.cpu_addr	= cpu_to_fdt64(KVM_IOPORT_AREA),
+			.length		= cpu_to_fdt64(ARM_IOPORT_SIZE),
+		},
+		{
+			.pci_addr = {
+				.hi	= cpu_to_fdt32(of_pci_b_ss(OF_PCI_SS_M32)),
+				.mid	= 0,
+				.lo	= 0,
+			},
+			.cpu_addr	= cpu_to_fdt64(KVM_PCI_MMIO_AREA),
+			.length		= cpu_to_fdt64(ARM_PCI_MMIO_SIZE),
+		},
+	};
+
+	/* Boilerplate PCI properties */
+	_FDT(fdt_begin_node(fdt, "pci"));
+	_FDT(fdt_property_cell(fdt, "#address-cells", 0x3));
+	_FDT(fdt_property_cell(fdt, "#size-cells", 0x2));
+	_FDT(fdt_property_cell(fdt, "#interrupt-cells", 0x1));
+	_FDT(fdt_property_string(fdt, "compatible", "linux,pci-virt"));
+
+	_FDT(fdt_property(fdt, "ranges", ranges, sizeof(ranges)));
+
+	/* Generate the interrupt map ... */
+	dev_hdr = device__first_dev(DEVICE_BUS_PCI);
+	while (dev_hdr && nentries < ARRAY_SIZE(irq_map)) {
+		struct of_interrupt_map_entry *entry = &irq_map[nentries];
+		struct pci_device_header *pci_hdr = dev_hdr->data;
+		u8 dev_num = dev_hdr->dev_num;
+		u8 pin = pci_hdr->irq_pin;
+		u8 irq = pci_hdr->irq_line;
+
+		*entry = (struct of_interrupt_map_entry) {
+			.pci_irq_mask = {
+				.pci_addr = {
+					.hi	= cpu_to_fdt32(of_pci_b_ddddd(dev_num)),
+					.mid	= 0,
+					.lo	= 0,
+				},
+				.pci_pin	= cpu_to_fdt32(pin),
+			},
+			.gic_phandle	= cpu_to_fdt32(gic_phandle),
+			.gic_irq = {
+				.type	= cpu_to_fdt32(GIC_FDT_IRQ_TYPE_SPI),
+				.num	= cpu_to_fdt32(irq - GIC_SPI_IRQ_BASE),
+				.flags	= cpu_to_fdt32(GIC_FDT_IRQ_FLAGS_EDGE_LO_HI),
+			},
+		};
+
+		nentries++;
+		dev_hdr = device__next_dev(dev_hdr);
+	}
+
+	_FDT(fdt_property(fdt, "interrupt-map", irq_map,
+			  sizeof(struct of_interrupt_map_entry) * nentries));
+
+	/* ... and the corresponding mask. */
+	if (nentries) {
+		struct of_pci_irq_mask irq_mask = {
+			.pci_addr = {
+				.hi	= cpu_to_fdt32(of_pci_b_ddddd(-1)),
+				.mid	= 0,
+				.lo	= 0,
+			},
+			.pci_pin	= cpu_to_fdt32(7),
+		};
+
+		_FDT(fdt_property(fdt, "interrupt-map-mask", &irq_mask,
+				  sizeof(irq_mask)));
+	}
+
+	_FDT(fdt_end_node(fdt));
+}
diff --git a/tools/kvm/include/kvm/of_pci.h b/tools/kvm/include/kvm/of_pci.h
new file mode 100644
index 000000000000..c8187ab378a1
--- /dev/null
+++ b/tools/kvm/include/kvm/of_pci.h
@@ -0,0 +1,44 @@
+#ifndef KVM__OF_PCI_H
+#define KVM__OF_PCI_H
+
+#include <linux/types.h>
+
+/*
+ * Definitions for implementing parts of the OpenFirmware PCI Bus Binding
+ * Specification (IEEE Std 1275-1994).
+ */
+
+struct of_pci_unit_address {
+	u32 hi, mid, lo;
+} __attribute__((packed));
+
+struct of_pci_irq_mask {
+	struct of_pci_unit_address	pci_addr;
+	u32				pci_pin;
+} __attribute__((packed));
+
+struct of_pci_ranges_entry {
+	struct of_pci_unit_address	pci_addr;
+	u64				cpu_addr;
+	u64				length;
+} __attribute__((packed));
+
+/* Macros to operate with address in OF binding to PCI */
+#define __b_x(x, p, l)		(((x) & ((1<<(l))-1)) << (p))
+#define of_pci_b_n(x)		__b_x((x), 31, 1)	/* 0 if relocatable */
+#define of_pci_b_p(x)		__b_x((x), 30, 1)	/* 1 if prefetchable */
+#define of_pci_b_t(x)		__b_x((x), 29, 1)	/* 1 if the address is aliased */
+#define of_pci_b_ss(x)		__b_x((x), 24, 2)	/* the space code */
+#define of_pci_b_bbbbbbbb(x)	__b_x((x), 16, 8)	/* bus number */
+#define of_pci_b_ddddd(x)	__b_x((x), 11, 5)	/* device number */
+#define of_pci_b_fff(x)		__b_x((x), 8, 3)	/* function number */
+#define of_pci_b_rrrrrrrr(x)	__b_x((x), 0, 8)	/* register number */
+
+#define OF_PCI_SS_CONFIG	0
+#define OF_PCI_SS_IO		1
+#define OF_PCI_SS_M32		2
+#define OF_PCI_SS_M64		3
+
+#define OF_PCI_IRQ_MAP_MAX	256	/* 5 bit device + 3 bit pin */
+
+#endif /* KVM__OF_PCI_H */
