xref: /kvmtool/arm/fdt.c (revision 95f47968a1d34ea27d4f3ad767f0c2c49f2ffc5b)
1 #include "kvm/devices.h"
2 #include "kvm/fdt.h"
3 #include "kvm/kvm.h"
4 #include "kvm/kvm-cpu.h"
5 #include "kvm/virtio-mmio.h"
6 
7 #include "arm-common/gic.h"
8 #include "arm-common/pci.h"
9 
10 #include <stdbool.h>
11 
12 #include <linux/byteorder.h>
13 #include <linux/kernel.h>
14 #include <linux/sizes.h>
15 #include <linux/psci.h>
16 
dump_fdt(const char * dtb_file,void * fdt)17 static void dump_fdt(const char *dtb_file, void *fdt)
18 {
19 	int count, fd;
20 
21 	fd = open(dtb_file, O_CREAT | O_TRUNC | O_RDWR, 0666);
22 	if (fd < 0)
23 		die("Failed to write dtb to %s", dtb_file);
24 
25 	count = write(fd, fdt, FDT_MAX_SIZE);
26 	if (count < 0)
27 		die_perror("Failed to dump dtb");
28 
29 	pr_debug("Wrote %d bytes to dtb %s", count, dtb_file);
30 	close(fd);
31 }
32 
33 #define CPU_NAME_MAX_LEN 15
generate_cpu_nodes(void * fdt,struct kvm * kvm)34 static void generate_cpu_nodes(void *fdt, struct kvm *kvm)
35 {
36 	int cpu;
37 
38 	_FDT(fdt_begin_node(fdt, "cpus"));
39 	_FDT(fdt_property_cell(fdt, "#address-cells", 0x1));
40 	_FDT(fdt_property_cell(fdt, "#size-cells", 0x0));
41 
42 	for (cpu = 0; cpu < kvm->nrcpus; ++cpu) {
43 		char cpu_name[CPU_NAME_MAX_LEN];
44 		struct kvm_cpu *vcpu = kvm->cpus[cpu];
45 		unsigned long mpidr = kvm_cpu__get_vcpu_mpidr(vcpu);
46 
47 		mpidr &= ARM_MPIDR_HWID_BITMASK;
48 		snprintf(cpu_name, CPU_NAME_MAX_LEN, "cpu@%lx", mpidr);
49 
50 		_FDT(fdt_begin_node(fdt, cpu_name));
51 		_FDT(fdt_property_string(fdt, "device_type", "cpu"));
52 		_FDT(fdt_property_string(fdt, "compatible", vcpu->cpu_compatible));
53 
54 		if (kvm->nrcpus > 1)
55 			_FDT(fdt_property_string(fdt, "enable-method", "psci"));
56 
57 		_FDT(fdt_property_cell(fdt, "reg", mpidr));
58 		_FDT(fdt_end_node(fdt));
59 	}
60 
61 	_FDT(fdt_end_node(fdt));
62 }
63 
generate_irq_prop(void * fdt,u8 irq,enum irq_type irq_type)64 static void generate_irq_prop(void *fdt, u8 irq, enum irq_type irq_type)
65 {
66 	u32 irq_prop[] = {
67 		cpu_to_fdt32(GIC_FDT_IRQ_TYPE_SPI),
68 		cpu_to_fdt32(irq - GIC_SPI_IRQ_BASE),
69 		cpu_to_fdt32(irq_type)
70 	};
71 
72 	_FDT(fdt_property(fdt, "interrupts", irq_prop, sizeof(irq_prop)));
73 }
74 
75 struct psci_fns {
76 	u32 cpu_suspend;
77 	u32 cpu_off;
78 	u32 cpu_on;
79 	u32 migrate;
80 };
81 
82 static struct psci_fns psci_0_1_fns = {
83 	.cpu_suspend = KVM_PSCI_FN_CPU_SUSPEND,
84 	.cpu_off = KVM_PSCI_FN_CPU_OFF,
85 	.cpu_on = KVM_PSCI_FN_CPU_ON,
86 	.migrate = KVM_PSCI_FN_MIGRATE,
87 };
88 
89 static struct psci_fns psci_0_2_aarch32_fns = {
90 	.cpu_suspend = PSCI_0_2_FN_CPU_SUSPEND,
91 	.cpu_off = PSCI_0_2_FN_CPU_OFF,
92 	.cpu_on = PSCI_0_2_FN_CPU_ON,
93 	.migrate = PSCI_0_2_FN_MIGRATE,
94 };
95 
96 static struct psci_fns psci_0_2_aarch64_fns = {
97 	.cpu_suspend = PSCI_0_2_FN64_CPU_SUSPEND,
98 	.cpu_off = PSCI_0_2_FN_CPU_OFF,
99 	.cpu_on = PSCI_0_2_FN64_CPU_ON,
100 	.migrate = PSCI_0_2_FN64_MIGRATE,
101 };
102 
setup_fdt(struct kvm * kvm)103 static int setup_fdt(struct kvm *kvm)
104 {
105 	struct device_header *dev_hdr;
106 	u8 staging_fdt[FDT_MAX_SIZE];
107 	u64 mem_reg_prop[]	= {
108 		cpu_to_fdt64(kvm->arch.memory_guest_start),
109 		cpu_to_fdt64(kvm->ram_size),
110 	};
111 	struct psci_fns *fns;
112 	void *fdt		= staging_fdt;
113 	void *fdt_dest		= guest_flat_to_host(kvm,
114 						     kvm->arch.dtb_guest_start);
115 	void (*generate_mmio_fdt_nodes)(void *, struct device_header *,
116 					void (*)(void *, u8, enum irq_type));
117 	void (*generate_cpu_peripheral_fdt_nodes)(void *, struct kvm *)
118 					= kvm->cpus[0]->generate_fdt_nodes;
119 
120 	/* Create new tree without a reserve map */
121 	_FDT(fdt_create(fdt, FDT_MAX_SIZE));
122 	_FDT(fdt_finish_reservemap(fdt));
123 
124 	/* Header */
125 	_FDT(fdt_begin_node(fdt, ""));
126 	_FDT(fdt_property_cell(fdt, "interrupt-parent", PHANDLE_GIC));
127 	_FDT(fdt_property_string(fdt, "compatible", "linux,dummy-virt"));
128 	_FDT(fdt_property_cell(fdt, "#address-cells", 0x2));
129 	_FDT(fdt_property_cell(fdt, "#size-cells", 0x2));
130 
131 	/* /chosen */
132 	_FDT(fdt_begin_node(fdt, "chosen"));
133 
134 	/* Pass on our amended command line to a Linux kernel only. */
135 	if (kvm->cfg.firmware_filename) {
136 		if (kvm->cfg.kernel_cmdline)
137 			_FDT(fdt_property_string(fdt, "bootargs",
138 						 kvm->cfg.kernel_cmdline));
139 	} else if (kvm->cfg.real_cmdline) {
140 		_FDT(fdt_property_string(fdt, "bootargs",
141 					 kvm->cfg.real_cmdline));
142 	}
143 
144 	_FDT(fdt_property_u64(fdt, "kaslr-seed", kvm->cfg.arch.kaslr_seed));
145 	_FDT(fdt_property_string(fdt, "stdout-path", "serial0"));
146 
147 	/* Initrd */
148 	if (kvm->arch.initrd_size != 0) {
149 		u64 ird_st_prop = cpu_to_fdt64(kvm->arch.initrd_guest_start);
150 		u64 ird_end_prop = cpu_to_fdt64(kvm->arch.initrd_guest_start +
151 					       kvm->arch.initrd_size);
152 
153 		_FDT(fdt_property(fdt, "linux,initrd-start",
154 				   &ird_st_prop, sizeof(ird_st_prop)));
155 		_FDT(fdt_property(fdt, "linux,initrd-end",
156 				   &ird_end_prop, sizeof(ird_end_prop)));
157 	}
158 	_FDT(fdt_end_node(fdt));
159 
160 	/* Memory */
161 	_FDT(fdt_begin_node(fdt, "memory"));
162 	_FDT(fdt_property_string(fdt, "device_type", "memory"));
163 	_FDT(fdt_property(fdt, "reg", mem_reg_prop, sizeof(mem_reg_prop)));
164 	_FDT(fdt_end_node(fdt));
165 
166 	/* CPU and peripherals (interrupt controller, timers, etc) */
167 	generate_cpu_nodes(fdt, kvm);
168 	if (generate_cpu_peripheral_fdt_nodes)
169 		generate_cpu_peripheral_fdt_nodes(fdt, kvm);
170 
171 	/* Virtio MMIO devices */
172 	dev_hdr = device__first_dev(DEVICE_BUS_MMIO);
173 	while (dev_hdr) {
174 		generate_mmio_fdt_nodes = dev_hdr->data;
175 		if (generate_mmio_fdt_nodes) {
176 			generate_mmio_fdt_nodes(fdt, dev_hdr, generate_irq_prop);
177 		} else {
178 			pr_debug("Missing FDT node generator for MMIO device %d",
179 				 dev_hdr->dev_num);
180 		}
181 		dev_hdr = device__next_dev(dev_hdr);
182 	}
183 
184 	/* IOPORT devices (!) */
185 	dev_hdr = device__first_dev(DEVICE_BUS_IOPORT);
186 	while (dev_hdr) {
187 		generate_mmio_fdt_nodes = dev_hdr->data;
188 		generate_mmio_fdt_nodes(fdt, dev_hdr, generate_irq_prop);
189 		dev_hdr = device__next_dev(dev_hdr);
190 	}
191 
192 	/* PCI host controller */
193 	pci__generate_fdt_nodes(fdt, kvm);
194 
195 	/* PSCI firmware */
196 	_FDT(fdt_begin_node(fdt, "psci"));
197 	if (kvm__supports_extension(kvm, KVM_CAP_ARM_PSCI_0_2)) {
198 		const char compatible[] = "arm,psci-0.2\0arm,psci";
199 		_FDT(fdt_property(fdt, "compatible",
200 				  compatible, sizeof(compatible)));
201 		if (kvm->cfg.arch.aarch32_guest)
202 			fns = &psci_0_2_aarch32_fns;
203 		else
204 			fns = &psci_0_2_aarch64_fns;
205 	} else {
206 		_FDT(fdt_property_string(fdt, "compatible", "arm,psci"));
207 		fns = &psci_0_1_fns;
208 	}
209 	_FDT(fdt_property_string(fdt, "method", "hvc"));
210 	_FDT(fdt_property_cell(fdt, "cpu_suspend", fns->cpu_suspend));
211 	_FDT(fdt_property_cell(fdt, "cpu_off", fns->cpu_off));
212 	_FDT(fdt_property_cell(fdt, "cpu_on", fns->cpu_on));
213 	_FDT(fdt_property_cell(fdt, "migrate", fns->migrate));
214 	_FDT(fdt_end_node(fdt));
215 
216 	if (fdt_stdout_path) {
217 		_FDT(fdt_begin_node(fdt, "aliases"));
218 		_FDT(fdt_property_string(fdt, "serial0", fdt_stdout_path));
219 		_FDT(fdt_end_node(fdt));
220 
221 		free(fdt_stdout_path);
222 		fdt_stdout_path = NULL;
223 	}
224 
225 	/* Finalise. */
226 	_FDT(fdt_end_node(fdt));
227 	_FDT(fdt_finish(fdt));
228 
229 	_FDT(fdt_open_into(fdt, fdt_dest, FDT_MAX_SIZE));
230 	_FDT(fdt_pack(fdt_dest));
231 
232 	if (kvm->cfg.arch.dump_dtb_filename)
233 		dump_fdt(kvm->cfg.arch.dump_dtb_filename, fdt_dest);
234 	return 0;
235 }
236 late_init(setup_fdt);
237