fdt.c 6.38 KB
Newer Older
1
2
3
4
5
6
7
#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"
8
#include "arm-common/pci.h"
9
10
11
12
13
14

#include <stdbool.h>

#include <linux/byteorder.h>
#include <linux/kernel.h>
#include <linux/sizes.h>
15
#include <linux/psci.h>
16

17
static void dump_fdt(const char *dtb_file, void *fdt)
18
19
20
{
	int count, fd;

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
26
27
28

	count = write(fd, fdt, FDT_MAX_SIZE);
	if (count < 0)
		die_perror("Failed to dump dtb");

29
	pr_debug("Wrote %d bytes to dtb %s", count, dtb_file);
30
31
32
	close(fd);
}

33
#define CPU_NAME_MAX_LEN 15
34
35
36
37
38
39
40
41
42
43
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];
44
45
		struct kvm_cpu *vcpu = kvm->cpus[cpu];
		unsigned long mpidr = kvm_cpu__get_vcpu_mpidr(vcpu);
46

47
48
		mpidr &= ARM_MPIDR_HWID_BITMASK;
		snprintf(cpu_name, CPU_NAME_MAX_LEN, "cpu@%lx", mpidr);
49
50
51

		_FDT(fdt_begin_node(fdt, cpu_name));
		_FDT(fdt_property_string(fdt, "device_type", "cpu"));
52
		_FDT(fdt_property_string(fdt, "compatible", vcpu->cpu_compatible));
53
54
55
56

		if (kvm->nrcpus > 1)
			_FDT(fdt_property_string(fdt, "enable-method", "psci"));

57
		_FDT(fdt_property_cell(fdt, "reg", mpidr));
58
59
60
61
62
63
		_FDT(fdt_end_node(fdt));
	}

	_FDT(fdt_end_node(fdt));
}

64
static void generate_irq_prop(void *fdt, u8 irq, enum irq_type irq_type)
65
66
67
{
	u32 irq_prop[] = {
		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
73
74
	};

	_FDT(fdt_property(fdt, "interrupts", irq_prop, sizeof(irq_prop)));
}

75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
struct psci_fns {
	u32 cpu_suspend;
	u32 cpu_off;
	u32 cpu_on;
	u32 migrate;
};

static struct psci_fns psci_0_1_fns = {
	.cpu_suspend = KVM_PSCI_FN_CPU_SUSPEND,
	.cpu_off = KVM_PSCI_FN_CPU_OFF,
	.cpu_on = KVM_PSCI_FN_CPU_ON,
	.migrate = KVM_PSCI_FN_MIGRATE,
};

static struct psci_fns psci_0_2_aarch32_fns = {
	.cpu_suspend = PSCI_0_2_FN_CPU_SUSPEND,
	.cpu_off = PSCI_0_2_FN_CPU_OFF,
	.cpu_on = PSCI_0_2_FN_CPU_ON,
	.migrate = PSCI_0_2_FN_MIGRATE,
};

static struct psci_fns psci_0_2_aarch64_fns = {
	.cpu_suspend = PSCI_0_2_FN64_CPU_SUSPEND,
	.cpu_off = PSCI_0_2_FN_CPU_OFF,
	.cpu_on = PSCI_0_2_FN64_CPU_ON,
	.migrate = PSCI_0_2_FN64_MIGRATE,
};

103
104
105
106
107
108
109
110
static int setup_fdt(struct kvm *kvm)
{
	struct device_header *dev_hdr;
	u8 staging_fdt[FDT_MAX_SIZE];
	u64 mem_reg_prop[]	= {
		cpu_to_fdt64(kvm->arch.memory_guest_start),
		cpu_to_fdt64(kvm->ram_size),
	};
111
	struct psci_fns *fns;
112
113
114
	void *fdt		= staging_fdt;
	void *fdt_dest		= guest_flat_to_host(kvm,
						     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
121
122
123
124
125

	/* Create new tree without a reserve map */
	_FDT(fdt_create(fdt, FDT_MAX_SIZE));
	_FDT(fdt_finish_reservemap(fdt));

	/* Header */
	_FDT(fdt_begin_node(fdt, ""));
Andre Przywara's avatar
Andre Przywara committed
126
	_FDT(fdt_property_cell(fdt, "interrupt-parent", PHANDLE_GIC));
127
128
129
130
131
132
	_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"));
133
	_FDT(fdt_property_cell(fdt, "linux,pci-probe-only", 1));
Julien Thierry's avatar
Julien Thierry committed
134

135
	/* Pass on our amended command line to a Linux kernel only. */
Julien Thierry's avatar
Julien Thierry committed
136
137
	if (kvm->cfg.firmware_filename) {
		if (kvm->cfg.kernel_cmdline)
138
139
			_FDT(fdt_property_string(fdt, "bootargs",
						 kvm->cfg.kernel_cmdline));
Julien Thierry's avatar
Julien Thierry committed
140
141
142
143
	} else
		_FDT(fdt_property_string(fdt, "bootargs",
					 kvm->cfg.real_cmdline));

144
	_FDT(fdt_property_u64(fdt, "kaslr-seed", kvm->cfg.arch.kaslr_seed));
145
	_FDT(fdt_property_string(fdt, "stdout-path", "serial0"));
146
147
148

	/* Initrd */
	if (kvm->arch.initrd_size != 0) {
149
150
		u64 ird_st_prop = cpu_to_fdt64(kvm->arch.initrd_guest_start);
		u64 ird_end_prop = cpu_to_fdt64(kvm->arch.initrd_guest_start +
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
					       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) */
167
	generate_cpu_nodes(fdt, kvm);
168
	if (generate_cpu_peripheral_fdt_nodes)
169
		generate_cpu_peripheral_fdt_nodes(fdt, kvm);
170
171
172
173

	/* Virtio MMIO devices */
	dev_hdr = device__first_dev(DEVICE_BUS_MMIO);
	while (dev_hdr) {
174
175
		generate_mmio_fdt_nodes = dev_hdr->data;
		generate_mmio_fdt_nodes(fdt, dev_hdr, generate_irq_prop);
176
177
178
		dev_hdr = device__next_dev(dev_hdr);
	}

179
180
181
182
183
184
185
186
	/* IOPORT devices (!) */
	dev_hdr = device__first_dev(DEVICE_BUS_IOPORT);
	while (dev_hdr) {
		generate_mmio_fdt_nodes = dev_hdr->data;
		generate_mmio_fdt_nodes(fdt, dev_hdr, generate_irq_prop);
		dev_hdr = device__next_dev(dev_hdr);
	}

187
	/* PCI host controller */
188
	pci__generate_fdt_nodes(fdt);
189

190
191
	/* PSCI firmware */
	_FDT(fdt_begin_node(fdt, "psci"));
192
193
194
195
196
197
198
199
200
201
202
203
	if (kvm__supports_extension(kvm, KVM_CAP_ARM_PSCI_0_2)) {
		const char compatible[] = "arm,psci-0.2\0arm,psci";
		_FDT(fdt_property(fdt, "compatible",
				  compatible, sizeof(compatible)));
		if (kvm->cfg.arch.aarch32_guest)
			fns = &psci_0_2_aarch32_fns;
		else
			fns = &psci_0_2_aarch64_fns;
	} else {
		_FDT(fdt_property_string(fdt, "compatible", "arm,psci"));
		fns = &psci_0_1_fns;
	}
204
	_FDT(fdt_property_string(fdt, "method", "hvc"));
205
206
207
208
	_FDT(fdt_property_cell(fdt, "cpu_suspend", fns->cpu_suspend));
	_FDT(fdt_property_cell(fdt, "cpu_off", fns->cpu_off));
	_FDT(fdt_property_cell(fdt, "cpu_on", fns->cpu_on));
	_FDT(fdt_property_cell(fdt, "migrate", fns->migrate));
209
210
	_FDT(fdt_end_node(fdt));

211
212
213
214
215
216
217
218
219
	if (fdt_stdout_path) {
		_FDT(fdt_begin_node(fdt, "aliases"));
		_FDT(fdt_property_string(fdt, "serial0", fdt_stdout_path));
		_FDT(fdt_end_node(fdt));

		free(fdt_stdout_path);
		fdt_stdout_path = NULL;
	}

220
221
222
223
224
225
226
	/* 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));

227
228
	if (kvm->cfg.arch.dump_dtb_filename)
		dump_fdt(kvm->cfg.arch.dump_dtb_filename, fdt_dest);
229
230
231
	return 0;
}
late_init(setup_fdt);