xref: /kvmtool/arm/fdt.c (revision 95f47968a1d34ea27d4f3ad767f0c2c49f2ffc5b)
17c0e8b0cSWill Deacon #include "kvm/devices.h"
27c0e8b0cSWill Deacon #include "kvm/fdt.h"
37c0e8b0cSWill Deacon #include "kvm/kvm.h"
47c0e8b0cSWill Deacon #include "kvm/kvm-cpu.h"
57c0e8b0cSWill Deacon #include "kvm/virtio-mmio.h"
67c0e8b0cSWill Deacon 
77c0e8b0cSWill Deacon #include "arm-common/gic.h"
816242d27SWill Deacon #include "arm-common/pci.h"
97c0e8b0cSWill Deacon 
107c0e8b0cSWill Deacon #include <stdbool.h>
117c0e8b0cSWill Deacon 
127c0e8b0cSWill Deacon #include <linux/byteorder.h>
137c0e8b0cSWill Deacon #include <linux/kernel.h>
147c0e8b0cSWill Deacon #include <linux/sizes.h>
15c2dad402SAnup Patel #include <linux/psci.h>
167c0e8b0cSWill Deacon 
dump_fdt(const char * dtb_file,void * fdt)17ba27ff46SWill Deacon static void dump_fdt(const char *dtb_file, void *fdt)
187c0e8b0cSWill Deacon {
197c0e8b0cSWill Deacon 	int count, fd;
207c0e8b0cSWill Deacon 
21ba27ff46SWill Deacon 	fd = open(dtb_file, O_CREAT | O_TRUNC | O_RDWR, 0666);
227c0e8b0cSWill Deacon 	if (fd < 0)
23ba27ff46SWill Deacon 		die("Failed to write dtb to %s", dtb_file);
247c0e8b0cSWill Deacon 
257c0e8b0cSWill Deacon 	count = write(fd, fdt, FDT_MAX_SIZE);
267c0e8b0cSWill Deacon 	if (count < 0)
277c0e8b0cSWill Deacon 		die_perror("Failed to dump dtb");
287c0e8b0cSWill Deacon 
29e1c7c62aSAndre Przywara 	pr_debug("Wrote %d bytes to dtb %s", count, dtb_file);
307c0e8b0cSWill Deacon 	close(fd);
317c0e8b0cSWill Deacon }
327c0e8b0cSWill Deacon 
33cc874a22SWill Deacon #define CPU_NAME_MAX_LEN 15
generate_cpu_nodes(void * fdt,struct kvm * kvm)34beff7ae0SMarc Zyngier static void generate_cpu_nodes(void *fdt, struct kvm *kvm)
35beff7ae0SMarc Zyngier {
36beff7ae0SMarc Zyngier 	int cpu;
37beff7ae0SMarc Zyngier 
38beff7ae0SMarc Zyngier 	_FDT(fdt_begin_node(fdt, "cpus"));
39beff7ae0SMarc Zyngier 	_FDT(fdt_property_cell(fdt, "#address-cells", 0x1));
40beff7ae0SMarc Zyngier 	_FDT(fdt_property_cell(fdt, "#size-cells", 0x0));
41beff7ae0SMarc Zyngier 
42beff7ae0SMarc Zyngier 	for (cpu = 0; cpu < kvm->nrcpus; ++cpu) {
43beff7ae0SMarc Zyngier 		char cpu_name[CPU_NAME_MAX_LEN];
44d06bc640SMarc Zyngier 		struct kvm_cpu *vcpu = kvm->cpus[cpu];
45d06bc640SMarc Zyngier 		unsigned long mpidr = kvm_cpu__get_vcpu_mpidr(vcpu);
46beff7ae0SMarc Zyngier 
47d06bc640SMarc Zyngier 		mpidr &= ARM_MPIDR_HWID_BITMASK;
48d06bc640SMarc Zyngier 		snprintf(cpu_name, CPU_NAME_MAX_LEN, "cpu@%lx", mpidr);
49beff7ae0SMarc Zyngier 
50beff7ae0SMarc Zyngier 		_FDT(fdt_begin_node(fdt, cpu_name));
51beff7ae0SMarc Zyngier 		_FDT(fdt_property_string(fdt, "device_type", "cpu"));
52d06bc640SMarc Zyngier 		_FDT(fdt_property_string(fdt, "compatible", vcpu->cpu_compatible));
53beff7ae0SMarc Zyngier 
54beff7ae0SMarc Zyngier 		if (kvm->nrcpus > 1)
55beff7ae0SMarc Zyngier 			_FDT(fdt_property_string(fdt, "enable-method", "psci"));
56beff7ae0SMarc Zyngier 
57d06bc640SMarc Zyngier 		_FDT(fdt_property_cell(fdt, "reg", mpidr));
58beff7ae0SMarc Zyngier 		_FDT(fdt_end_node(fdt));
59beff7ae0SMarc Zyngier 	}
60beff7ae0SMarc Zyngier 
61beff7ae0SMarc Zyngier 	_FDT(fdt_end_node(fdt));
62beff7ae0SMarc Zyngier }
63beff7ae0SMarc Zyngier 
generate_irq_prop(void * fdt,u8 irq,enum irq_type irq_type)642bfd9ac3SAndre Przywara static void generate_irq_prop(void *fdt, u8 irq, enum irq_type irq_type)
657c0e8b0cSWill Deacon {
667c0e8b0cSWill Deacon 	u32 irq_prop[] = {
677c0e8b0cSWill Deacon 		cpu_to_fdt32(GIC_FDT_IRQ_TYPE_SPI),
682454c7dcSWill Deacon 		cpu_to_fdt32(irq - GIC_SPI_IRQ_BASE),
692bfd9ac3SAndre Przywara 		cpu_to_fdt32(irq_type)
707c0e8b0cSWill Deacon 	};
717c0e8b0cSWill Deacon 
727c0e8b0cSWill Deacon 	_FDT(fdt_property(fdt, "interrupts", irq_prop, sizeof(irq_prop)));
737c0e8b0cSWill Deacon }
747c0e8b0cSWill Deacon 
75c2dad402SAnup Patel struct psci_fns {
76c2dad402SAnup Patel 	u32 cpu_suspend;
77c2dad402SAnup Patel 	u32 cpu_off;
78c2dad402SAnup Patel 	u32 cpu_on;
79c2dad402SAnup Patel 	u32 migrate;
80c2dad402SAnup Patel };
81c2dad402SAnup Patel 
82c2dad402SAnup Patel static struct psci_fns psci_0_1_fns = {
83c2dad402SAnup Patel 	.cpu_suspend = KVM_PSCI_FN_CPU_SUSPEND,
84c2dad402SAnup Patel 	.cpu_off = KVM_PSCI_FN_CPU_OFF,
85c2dad402SAnup Patel 	.cpu_on = KVM_PSCI_FN_CPU_ON,
86c2dad402SAnup Patel 	.migrate = KVM_PSCI_FN_MIGRATE,
87c2dad402SAnup Patel };
88c2dad402SAnup Patel 
89c2dad402SAnup Patel static struct psci_fns psci_0_2_aarch32_fns = {
90c2dad402SAnup Patel 	.cpu_suspend = PSCI_0_2_FN_CPU_SUSPEND,
91c2dad402SAnup Patel 	.cpu_off = PSCI_0_2_FN_CPU_OFF,
92c2dad402SAnup Patel 	.cpu_on = PSCI_0_2_FN_CPU_ON,
93c2dad402SAnup Patel 	.migrate = PSCI_0_2_FN_MIGRATE,
94c2dad402SAnup Patel };
95c2dad402SAnup Patel 
96c2dad402SAnup Patel static struct psci_fns psci_0_2_aarch64_fns = {
97c2dad402SAnup Patel 	.cpu_suspend = PSCI_0_2_FN64_CPU_SUSPEND,
98c2dad402SAnup Patel 	.cpu_off = PSCI_0_2_FN_CPU_OFF,
99c2dad402SAnup Patel 	.cpu_on = PSCI_0_2_FN64_CPU_ON,
100c2dad402SAnup Patel 	.migrate = PSCI_0_2_FN64_MIGRATE,
101c2dad402SAnup Patel };
102c2dad402SAnup Patel 
setup_fdt(struct kvm * kvm)1037c0e8b0cSWill Deacon static int setup_fdt(struct kvm *kvm)
1047c0e8b0cSWill Deacon {
1057c0e8b0cSWill Deacon 	struct device_header *dev_hdr;
1067c0e8b0cSWill Deacon 	u8 staging_fdt[FDT_MAX_SIZE];
1077c0e8b0cSWill Deacon 	u64 mem_reg_prop[]	= {
1087c0e8b0cSWill Deacon 		cpu_to_fdt64(kvm->arch.memory_guest_start),
1097c0e8b0cSWill Deacon 		cpu_to_fdt64(kvm->ram_size),
1107c0e8b0cSWill Deacon 	};
111c2dad402SAnup Patel 	struct psci_fns *fns;
1127c0e8b0cSWill Deacon 	void *fdt		= staging_fdt;
1137c0e8b0cSWill Deacon 	void *fdt_dest		= guest_flat_to_host(kvm,
1147c0e8b0cSWill Deacon 						     kvm->arch.dtb_guest_start);
1152454c7dcSWill Deacon 	void (*generate_mmio_fdt_nodes)(void *, struct device_header *,
1162bfd9ac3SAndre Przywara 					void (*)(void *, u8, enum irq_type));
1170063d50cSAndre Przywara 	void (*generate_cpu_peripheral_fdt_nodes)(void *, struct kvm *)
1187c0e8b0cSWill Deacon 					= kvm->cpus[0]->generate_fdt_nodes;
1197c0e8b0cSWill Deacon 
1207c0e8b0cSWill Deacon 	/* Create new tree without a reserve map */
1217c0e8b0cSWill Deacon 	_FDT(fdt_create(fdt, FDT_MAX_SIZE));
1227c0e8b0cSWill Deacon 	_FDT(fdt_finish_reservemap(fdt));
1237c0e8b0cSWill Deacon 
1247c0e8b0cSWill Deacon 	/* Header */
1257c0e8b0cSWill Deacon 	_FDT(fdt_begin_node(fdt, ""));
126cd9a8066SAndre Przywara 	_FDT(fdt_property_cell(fdt, "interrupt-parent", PHANDLE_GIC));
1277c0e8b0cSWill Deacon 	_FDT(fdt_property_string(fdt, "compatible", "linux,dummy-virt"));
1287c0e8b0cSWill Deacon 	_FDT(fdt_property_cell(fdt, "#address-cells", 0x2));
1297c0e8b0cSWill Deacon 	_FDT(fdt_property_cell(fdt, "#size-cells", 0x2));
1307c0e8b0cSWill Deacon 
1317c0e8b0cSWill Deacon 	/* /chosen */
1327c0e8b0cSWill Deacon 	_FDT(fdt_begin_node(fdt, "chosen"));
1339afefd6cSJulien Thierry 
1345e4b563dSAndre Przywara 	/* Pass on our amended command line to a Linux kernel only. */
1359afefd6cSJulien Thierry 	if (kvm->cfg.firmware_filename) {
1369afefd6cSJulien Thierry 		if (kvm->cfg.kernel_cmdline)
1375e4b563dSAndre Przywara 			_FDT(fdt_property_string(fdt, "bootargs",
1385e4b563dSAndre Przywara 						 kvm->cfg.kernel_cmdline));
1395613ae26SAlexandru Elisei 	} else if (kvm->cfg.real_cmdline) {
1409afefd6cSJulien Thierry 		_FDT(fdt_property_string(fdt, "bootargs",
1419afefd6cSJulien Thierry 					 kvm->cfg.real_cmdline));
1425613ae26SAlexandru Elisei 	}
1439afefd6cSJulien Thierry 
14484d77496SWill Deacon 	_FDT(fdt_property_u64(fdt, "kaslr-seed", kvm->cfg.arch.kaslr_seed));
14556e45ea4SAndre Przywara 	_FDT(fdt_property_string(fdt, "stdout-path", "serial0"));
1467c0e8b0cSWill Deacon 
1477c0e8b0cSWill Deacon 	/* Initrd */
1487c0e8b0cSWill Deacon 	if (kvm->arch.initrd_size != 0) {
149bd33695dSAndre Przywara 		u64 ird_st_prop = cpu_to_fdt64(kvm->arch.initrd_guest_start);
150bd33695dSAndre Przywara 		u64 ird_end_prop = cpu_to_fdt64(kvm->arch.initrd_guest_start +
1517c0e8b0cSWill Deacon 					       kvm->arch.initrd_size);
1527c0e8b0cSWill Deacon 
1537c0e8b0cSWill Deacon 		_FDT(fdt_property(fdt, "linux,initrd-start",
1547c0e8b0cSWill Deacon 				   &ird_st_prop, sizeof(ird_st_prop)));
1557c0e8b0cSWill Deacon 		_FDT(fdt_property(fdt, "linux,initrd-end",
1567c0e8b0cSWill Deacon 				   &ird_end_prop, sizeof(ird_end_prop)));
1577c0e8b0cSWill Deacon 	}
1587c0e8b0cSWill Deacon 	_FDT(fdt_end_node(fdt));
1597c0e8b0cSWill Deacon 
1607c0e8b0cSWill Deacon 	/* Memory */
1617c0e8b0cSWill Deacon 	_FDT(fdt_begin_node(fdt, "memory"));
1627c0e8b0cSWill Deacon 	_FDT(fdt_property_string(fdt, "device_type", "memory"));
1637c0e8b0cSWill Deacon 	_FDT(fdt_property(fdt, "reg", mem_reg_prop, sizeof(mem_reg_prop)));
1647c0e8b0cSWill Deacon 	_FDT(fdt_end_node(fdt));
1657c0e8b0cSWill Deacon 
1667c0e8b0cSWill Deacon 	/* CPU and peripherals (interrupt controller, timers, etc) */
167beff7ae0SMarc Zyngier 	generate_cpu_nodes(fdt, kvm);
1682454c7dcSWill Deacon 	if (generate_cpu_peripheral_fdt_nodes)
1690063d50cSAndre Przywara 		generate_cpu_peripheral_fdt_nodes(fdt, kvm);
1707c0e8b0cSWill Deacon 
1717c0e8b0cSWill Deacon 	/* Virtio MMIO devices */
1727c0e8b0cSWill Deacon 	dev_hdr = device__first_dev(DEVICE_BUS_MMIO);
1737c0e8b0cSWill Deacon 	while (dev_hdr) {
1742454c7dcSWill Deacon 		generate_mmio_fdt_nodes = dev_hdr->data;
1756b74f68fSAlexandru Elisei 		if (generate_mmio_fdt_nodes) {
1762454c7dcSWill Deacon 			generate_mmio_fdt_nodes(fdt, dev_hdr, generate_irq_prop);
1776b74f68fSAlexandru Elisei 		} else {
1786b74f68fSAlexandru Elisei 			pr_debug("Missing FDT node generator for MMIO device %d",
1796b74f68fSAlexandru Elisei 				 dev_hdr->dev_num);
1806b74f68fSAlexandru Elisei 		}
1817c0e8b0cSWill Deacon 		dev_hdr = device__next_dev(dev_hdr);
1827c0e8b0cSWill Deacon 	}
1837c0e8b0cSWill Deacon 
184ed7b31c9SWill Deacon 	/* IOPORT devices (!) */
185ed7b31c9SWill Deacon 	dev_hdr = device__first_dev(DEVICE_BUS_IOPORT);
186ed7b31c9SWill Deacon 	while (dev_hdr) {
187ed7b31c9SWill Deacon 		generate_mmio_fdt_nodes = dev_hdr->data;
188ed7b31c9SWill Deacon 		generate_mmio_fdt_nodes(fdt, dev_hdr, generate_irq_prop);
189ed7b31c9SWill Deacon 		dev_hdr = device__next_dev(dev_hdr);
190ed7b31c9SWill Deacon 	}
191ed7b31c9SWill Deacon 
19216242d27SWill Deacon 	/* PCI host controller */
193*95f47968SAlexandru Elisei 	pci__generate_fdt_nodes(fdt, kvm);
19416242d27SWill Deacon 
19561076240SWill Deacon 	/* PSCI firmware */
19661076240SWill Deacon 	_FDT(fdt_begin_node(fdt, "psci"));
197c2dad402SAnup Patel 	if (kvm__supports_extension(kvm, KVM_CAP_ARM_PSCI_0_2)) {
198c2dad402SAnup Patel 		const char compatible[] = "arm,psci-0.2\0arm,psci";
199c2dad402SAnup Patel 		_FDT(fdt_property(fdt, "compatible",
200c2dad402SAnup Patel 				  compatible, sizeof(compatible)));
201c2dad402SAnup Patel 		if (kvm->cfg.arch.aarch32_guest)
202c2dad402SAnup Patel 			fns = &psci_0_2_aarch32_fns;
203c2dad402SAnup Patel 		else
204c2dad402SAnup Patel 			fns = &psci_0_2_aarch64_fns;
205c2dad402SAnup Patel 	} else {
20661076240SWill Deacon 		_FDT(fdt_property_string(fdt, "compatible", "arm,psci"));
207c2dad402SAnup Patel 		fns = &psci_0_1_fns;
208c2dad402SAnup Patel 	}
20961076240SWill Deacon 	_FDT(fdt_property_string(fdt, "method", "hvc"));
210c2dad402SAnup Patel 	_FDT(fdt_property_cell(fdt, "cpu_suspend", fns->cpu_suspend));
211c2dad402SAnup Patel 	_FDT(fdt_property_cell(fdt, "cpu_off", fns->cpu_off));
212c2dad402SAnup Patel 	_FDT(fdt_property_cell(fdt, "cpu_on", fns->cpu_on));
213c2dad402SAnup Patel 	_FDT(fdt_property_cell(fdt, "migrate", fns->migrate));
21461076240SWill Deacon 	_FDT(fdt_end_node(fdt));
21561076240SWill Deacon 
21656e45ea4SAndre Przywara 	if (fdt_stdout_path) {
21756e45ea4SAndre Przywara 		_FDT(fdt_begin_node(fdt, "aliases"));
21856e45ea4SAndre Przywara 		_FDT(fdt_property_string(fdt, "serial0", fdt_stdout_path));
21956e45ea4SAndre Przywara 		_FDT(fdt_end_node(fdt));
22056e45ea4SAndre Przywara 
22156e45ea4SAndre Przywara 		free(fdt_stdout_path);
22256e45ea4SAndre Przywara 		fdt_stdout_path = NULL;
22356e45ea4SAndre Przywara 	}
22456e45ea4SAndre Przywara 
2257c0e8b0cSWill Deacon 	/* Finalise. */
2267c0e8b0cSWill Deacon 	_FDT(fdt_end_node(fdt));
2277c0e8b0cSWill Deacon 	_FDT(fdt_finish(fdt));
2287c0e8b0cSWill Deacon 
2297c0e8b0cSWill Deacon 	_FDT(fdt_open_into(fdt, fdt_dest, FDT_MAX_SIZE));
2307c0e8b0cSWill Deacon 	_FDT(fdt_pack(fdt_dest));
2317c0e8b0cSWill Deacon 
232ba27ff46SWill Deacon 	if (kvm->cfg.arch.dump_dtb_filename)
233ba27ff46SWill Deacon 		dump_fdt(kvm->cfg.arch.dump_dtb_filename, fdt_dest);
2347c0e8b0cSWill Deacon 	return 0;
2357c0e8b0cSWill Deacon }
2367c0e8b0cSWill Deacon late_init(setup_fdt);
237