xref: /kvmtool/riscv/fdt.c (revision 7c9aac003925e52121b34c48cf81564c4077b26c)
1*7c9aac00SAnup Patel #include "kvm/devices.h"
2*7c9aac00SAnup Patel #include "kvm/fdt.h"
3*7c9aac00SAnup Patel #include "kvm/kvm.h"
4*7c9aac00SAnup Patel #include "kvm/kvm-cpu.h"
5*7c9aac00SAnup Patel 
6*7c9aac00SAnup Patel #include <stdbool.h>
7*7c9aac00SAnup Patel 
8*7c9aac00SAnup Patel #include <linux/byteorder.h>
9*7c9aac00SAnup Patel #include <linux/kernel.h>
10*7c9aac00SAnup Patel #include <linux/sizes.h>
11*7c9aac00SAnup Patel 
12*7c9aac00SAnup Patel static void dump_fdt(const char *dtb_file, void *fdt)
13*7c9aac00SAnup Patel {
14*7c9aac00SAnup Patel 	int count, fd;
15*7c9aac00SAnup Patel 
16*7c9aac00SAnup Patel 	fd = open(dtb_file, O_CREAT | O_TRUNC | O_RDWR, 0666);
17*7c9aac00SAnup Patel 	if (fd < 0)
18*7c9aac00SAnup Patel 		die("Failed to write dtb to %s", dtb_file);
19*7c9aac00SAnup Patel 
20*7c9aac00SAnup Patel 	count = write(fd, fdt, FDT_MAX_SIZE);
21*7c9aac00SAnup Patel 	if (count < 0)
22*7c9aac00SAnup Patel 		die_perror("Failed to dump dtb");
23*7c9aac00SAnup Patel 
24*7c9aac00SAnup Patel 	pr_debug("Wrote %d bytes to dtb %s", count, dtb_file);
25*7c9aac00SAnup Patel 	close(fd);
26*7c9aac00SAnup Patel }
27*7c9aac00SAnup Patel 
28*7c9aac00SAnup Patel #define CPU_NAME_MAX_LEN 15
29*7c9aac00SAnup Patel #define CPU_ISA_MAX_LEN 128
30*7c9aac00SAnup Patel static void generate_cpu_nodes(void *fdt, struct kvm *kvm)
31*7c9aac00SAnup Patel {
32*7c9aac00SAnup Patel 	int cpu, pos, i, index, valid_isa_len;
33*7c9aac00SAnup Patel 	const char *valid_isa_order = "IEMAFDQCLBJTPVNSUHKORWXYZG";
34*7c9aac00SAnup Patel 
35*7c9aac00SAnup Patel 	_FDT(fdt_begin_node(fdt, "cpus"));
36*7c9aac00SAnup Patel 	_FDT(fdt_property_cell(fdt, "#address-cells", 0x1));
37*7c9aac00SAnup Patel 	_FDT(fdt_property_cell(fdt, "#size-cells", 0x0));
38*7c9aac00SAnup Patel 	_FDT(fdt_property_cell(fdt, "timebase-frequency",
39*7c9aac00SAnup Patel 				kvm->cpus[0]->riscv_timebase));
40*7c9aac00SAnup Patel 
41*7c9aac00SAnup Patel 	for (cpu = 0; cpu < kvm->nrcpus; ++cpu) {
42*7c9aac00SAnup Patel 		char cpu_name[CPU_NAME_MAX_LEN];
43*7c9aac00SAnup Patel 		char cpu_isa[CPU_ISA_MAX_LEN];
44*7c9aac00SAnup Patel 		struct kvm_cpu *vcpu = kvm->cpus[cpu];
45*7c9aac00SAnup Patel 
46*7c9aac00SAnup Patel 		snprintf(cpu_name, CPU_NAME_MAX_LEN, "cpu@%x", cpu);
47*7c9aac00SAnup Patel 
48*7c9aac00SAnup Patel 		snprintf(cpu_isa, CPU_ISA_MAX_LEN, "rv%ld", vcpu->riscv_xlen);
49*7c9aac00SAnup Patel 		pos = strlen(cpu_isa);
50*7c9aac00SAnup Patel 		valid_isa_len = strlen(valid_isa_order);
51*7c9aac00SAnup Patel 		for (i = 0; i < valid_isa_len; i++) {
52*7c9aac00SAnup Patel 			index = valid_isa_order[i] - 'A';
53*7c9aac00SAnup Patel 			if (vcpu->riscv_isa & (1 << (index)))
54*7c9aac00SAnup Patel 				cpu_isa[pos++] = 'a' + index;
55*7c9aac00SAnup Patel 		}
56*7c9aac00SAnup Patel 		cpu_isa[pos] = '\0';
57*7c9aac00SAnup Patel 
58*7c9aac00SAnup Patel 		_FDT(fdt_begin_node(fdt, cpu_name));
59*7c9aac00SAnup Patel 		_FDT(fdt_property_string(fdt, "device_type", "cpu"));
60*7c9aac00SAnup Patel 		_FDT(fdt_property_string(fdt, "compatible", "riscv"));
61*7c9aac00SAnup Patel 		if (vcpu->riscv_xlen == 64)
62*7c9aac00SAnup Patel 			_FDT(fdt_property_string(fdt, "mmu-type",
63*7c9aac00SAnup Patel 						 "riscv,sv48"));
64*7c9aac00SAnup Patel 		else
65*7c9aac00SAnup Patel 			_FDT(fdt_property_string(fdt, "mmu-type",
66*7c9aac00SAnup Patel 						 "riscv,sv32"));
67*7c9aac00SAnup Patel 		_FDT(fdt_property_string(fdt, "riscv,isa", cpu_isa));
68*7c9aac00SAnup Patel 		_FDT(fdt_property_cell(fdt, "reg", cpu));
69*7c9aac00SAnup Patel 		_FDT(fdt_property_string(fdt, "status", "okay"));
70*7c9aac00SAnup Patel 
71*7c9aac00SAnup Patel 		_FDT(fdt_begin_node(fdt, "interrupt-controller"));
72*7c9aac00SAnup Patel 		_FDT(fdt_property_string(fdt, "compatible", "riscv,cpu-intc"));
73*7c9aac00SAnup Patel 		_FDT(fdt_property_cell(fdt, "#interrupt-cells", 1));
74*7c9aac00SAnup Patel 		_FDT(fdt_property(fdt, "interrupt-controller", NULL, 0));
75*7c9aac00SAnup Patel 		_FDT(fdt_property_cell(fdt, "phandle",
76*7c9aac00SAnup Patel 					PHANDLE_CPU_INTC_BASE + cpu));
77*7c9aac00SAnup Patel 		_FDT(fdt_end_node(fdt));
78*7c9aac00SAnup Patel 
79*7c9aac00SAnup Patel 		_FDT(fdt_end_node(fdt));
80*7c9aac00SAnup Patel 	}
81*7c9aac00SAnup Patel 
82*7c9aac00SAnup Patel 	_FDT(fdt_end_node(fdt));
83*7c9aac00SAnup Patel }
84*7c9aac00SAnup Patel 
85*7c9aac00SAnup Patel static int setup_fdt(struct kvm *kvm)
86*7c9aac00SAnup Patel {
87*7c9aac00SAnup Patel 	struct device_header *dev_hdr;
88*7c9aac00SAnup Patel 	u8 staging_fdt[FDT_MAX_SIZE];
89*7c9aac00SAnup Patel 	u64 mem_reg_prop[]	= {
90*7c9aac00SAnup Patel 		cpu_to_fdt64(kvm->arch.memory_guest_start),
91*7c9aac00SAnup Patel 		cpu_to_fdt64(kvm->ram_size),
92*7c9aac00SAnup Patel 	};
93*7c9aac00SAnup Patel 	void *fdt		= staging_fdt;
94*7c9aac00SAnup Patel 	void *fdt_dest		= guest_flat_to_host(kvm,
95*7c9aac00SAnup Patel 						     kvm->arch.dtb_guest_start);
96*7c9aac00SAnup Patel 	void (*generate_mmio_fdt_nodes)(void *, struct device_header *,
97*7c9aac00SAnup Patel 					void (*)(void *, u8, enum irq_type));
98*7c9aac00SAnup Patel 
99*7c9aac00SAnup Patel 	/* Create new tree without a reserve map */
100*7c9aac00SAnup Patel 	_FDT(fdt_create(fdt, FDT_MAX_SIZE));
101*7c9aac00SAnup Patel 	_FDT(fdt_finish_reservemap(fdt));
102*7c9aac00SAnup Patel 
103*7c9aac00SAnup Patel 	/* Header */
104*7c9aac00SAnup Patel 	_FDT(fdt_begin_node(fdt, ""));
105*7c9aac00SAnup Patel 	_FDT(fdt_property_string(fdt, "compatible", "linux,dummy-virt"));
106*7c9aac00SAnup Patel 	_FDT(fdt_property_cell(fdt, "#address-cells", 0x2));
107*7c9aac00SAnup Patel 	_FDT(fdt_property_cell(fdt, "#size-cells", 0x2));
108*7c9aac00SAnup Patel 
109*7c9aac00SAnup Patel 	/* /chosen */
110*7c9aac00SAnup Patel 	_FDT(fdt_begin_node(fdt, "chosen"));
111*7c9aac00SAnup Patel 
112*7c9aac00SAnup Patel 	/* Pass on our amended command line to a Linux kernel only. */
113*7c9aac00SAnup Patel 	if (kvm->cfg.firmware_filename) {
114*7c9aac00SAnup Patel 		if (kvm->cfg.kernel_cmdline)
115*7c9aac00SAnup Patel 			_FDT(fdt_property_string(fdt, "bootargs",
116*7c9aac00SAnup Patel 						 kvm->cfg.kernel_cmdline));
117*7c9aac00SAnup Patel 	} else
118*7c9aac00SAnup Patel 		_FDT(fdt_property_string(fdt, "bootargs",
119*7c9aac00SAnup Patel 					 kvm->cfg.real_cmdline));
120*7c9aac00SAnup Patel 
121*7c9aac00SAnup Patel 	_FDT(fdt_property_string(fdt, "stdout-path", "serial0"));
122*7c9aac00SAnup Patel 
123*7c9aac00SAnup Patel 	/* Initrd */
124*7c9aac00SAnup Patel 	if (kvm->arch.initrd_size != 0) {
125*7c9aac00SAnup Patel 		u64 ird_st_prop = cpu_to_fdt64(kvm->arch.initrd_guest_start);
126*7c9aac00SAnup Patel 		u64 ird_end_prop = cpu_to_fdt64(kvm->arch.initrd_guest_start +
127*7c9aac00SAnup Patel 					       kvm->arch.initrd_size);
128*7c9aac00SAnup Patel 
129*7c9aac00SAnup Patel 		_FDT(fdt_property(fdt, "linux,initrd-start",
130*7c9aac00SAnup Patel 				   &ird_st_prop, sizeof(ird_st_prop)));
131*7c9aac00SAnup Patel 		_FDT(fdt_property(fdt, "linux,initrd-end",
132*7c9aac00SAnup Patel 				   &ird_end_prop, sizeof(ird_end_prop)));
133*7c9aac00SAnup Patel 	}
134*7c9aac00SAnup Patel 
135*7c9aac00SAnup Patel 	_FDT(fdt_end_node(fdt));
136*7c9aac00SAnup Patel 
137*7c9aac00SAnup Patel 	/* Memory */
138*7c9aac00SAnup Patel 	_FDT(fdt_begin_node(fdt, "memory"));
139*7c9aac00SAnup Patel 	_FDT(fdt_property_string(fdt, "device_type", "memory"));
140*7c9aac00SAnup Patel 	_FDT(fdt_property(fdt, "reg", mem_reg_prop, sizeof(mem_reg_prop)));
141*7c9aac00SAnup Patel 	_FDT(fdt_end_node(fdt));
142*7c9aac00SAnup Patel 
143*7c9aac00SAnup Patel 	/* CPUs */
144*7c9aac00SAnup Patel 	generate_cpu_nodes(fdt, kvm);
145*7c9aac00SAnup Patel 
146*7c9aac00SAnup Patel 	/* Simple Bus */
147*7c9aac00SAnup Patel 	_FDT(fdt_begin_node(fdt, "smb"));
148*7c9aac00SAnup Patel 	_FDT(fdt_property_string(fdt, "compatible", "simple-bus"));
149*7c9aac00SAnup Patel 	_FDT(fdt_property_cell(fdt, "#address-cells", 0x2));
150*7c9aac00SAnup Patel 	_FDT(fdt_property_cell(fdt, "#size-cells", 0x2));
151*7c9aac00SAnup Patel 	_FDT(fdt_property_cell(fdt, "interrupt-parent", PHANDLE_PLIC));
152*7c9aac00SAnup Patel 	_FDT(fdt_property(fdt, "ranges", NULL, 0));
153*7c9aac00SAnup Patel 
154*7c9aac00SAnup Patel 	/* Virtio MMIO devices */
155*7c9aac00SAnup Patel 	dev_hdr = device__first_dev(DEVICE_BUS_MMIO);
156*7c9aac00SAnup Patel 	while (dev_hdr) {
157*7c9aac00SAnup Patel 		generate_mmio_fdt_nodes = dev_hdr->data;
158*7c9aac00SAnup Patel 		generate_mmio_fdt_nodes(fdt, dev_hdr, plic__generate_irq_prop);
159*7c9aac00SAnup Patel 		dev_hdr = device__next_dev(dev_hdr);
160*7c9aac00SAnup Patel 	}
161*7c9aac00SAnup Patel 
162*7c9aac00SAnup Patel 	/* IOPORT devices */
163*7c9aac00SAnup Patel 	dev_hdr = device__first_dev(DEVICE_BUS_IOPORT);
164*7c9aac00SAnup Patel 	while (dev_hdr) {
165*7c9aac00SAnup Patel 		generate_mmio_fdt_nodes = dev_hdr->data;
166*7c9aac00SAnup Patel 		generate_mmio_fdt_nodes(fdt, dev_hdr, plic__generate_irq_prop);
167*7c9aac00SAnup Patel 		dev_hdr = device__next_dev(dev_hdr);
168*7c9aac00SAnup Patel 	}
169*7c9aac00SAnup Patel 
170*7c9aac00SAnup Patel 	_FDT(fdt_end_node(fdt));
171*7c9aac00SAnup Patel 
172*7c9aac00SAnup Patel 	if (fdt_stdout_path) {
173*7c9aac00SAnup Patel 		_FDT(fdt_begin_node(fdt, "aliases"));
174*7c9aac00SAnup Patel 		_FDT(fdt_property_string(fdt, "serial0", fdt_stdout_path));
175*7c9aac00SAnup Patel 		_FDT(fdt_end_node(fdt));
176*7c9aac00SAnup Patel 
177*7c9aac00SAnup Patel 		free(fdt_stdout_path);
178*7c9aac00SAnup Patel 		fdt_stdout_path = NULL;
179*7c9aac00SAnup Patel 	}
180*7c9aac00SAnup Patel 
181*7c9aac00SAnup Patel 	/* Finalise. */
182*7c9aac00SAnup Patel 	_FDT(fdt_end_node(fdt));
183*7c9aac00SAnup Patel 	_FDT(fdt_finish(fdt));
184*7c9aac00SAnup Patel 
185*7c9aac00SAnup Patel 	_FDT(fdt_open_into(fdt, fdt_dest, FDT_MAX_SIZE));
186*7c9aac00SAnup Patel 	_FDT(fdt_pack(fdt_dest));
187*7c9aac00SAnup Patel 
188*7c9aac00SAnup Patel 	if (kvm->cfg.arch.dump_dtb_filename)
189*7c9aac00SAnup Patel 		dump_fdt(kvm->cfg.arch.dump_dtb_filename, fdt_dest);
190*7c9aac00SAnup Patel 	return 0;
191*7c9aac00SAnup Patel }
192*7c9aac00SAnup Patel late_init(setup_fdt);
193