xref: /kvmtool/riscv/fdt.c (revision 8aff29e1dafe888d00786574925765ea667fbb43)
17c9aac00SAnup Patel #include "kvm/devices.h"
27c9aac00SAnup Patel #include "kvm/fdt.h"
37c9aac00SAnup Patel #include "kvm/kvm.h"
47c9aac00SAnup Patel #include "kvm/kvm-cpu.h"
57c9aac00SAnup Patel 
67c9aac00SAnup Patel #include <stdbool.h>
77c9aac00SAnup Patel 
87c9aac00SAnup Patel #include <linux/byteorder.h>
97c9aac00SAnup Patel #include <linux/kernel.h>
107c9aac00SAnup Patel #include <linux/sizes.h>
117c9aac00SAnup Patel 
12*8aff29e1SAtish Patra #define RISCV_ISA_EXT_REG(id)	__kvm_reg_id(KVM_REG_RISCV_ISA_EXT, \
13*8aff29e1SAtish Patra 					     id, KVM_REG_SIZE_ULONG)
14*8aff29e1SAtish Patra struct isa_ext_info {
15*8aff29e1SAtish Patra 	const char *name;
16*8aff29e1SAtish Patra 	unsigned long ext_id;
17*8aff29e1SAtish Patra };
18*8aff29e1SAtish Patra 
19*8aff29e1SAtish Patra struct isa_ext_info isa_info_arr[] = {
20*8aff29e1SAtish Patra };
21*8aff29e1SAtish Patra 
227c9aac00SAnup Patel static void dump_fdt(const char *dtb_file, void *fdt)
237c9aac00SAnup Patel {
247c9aac00SAnup Patel 	int count, fd;
257c9aac00SAnup Patel 
267c9aac00SAnup Patel 	fd = open(dtb_file, O_CREAT | O_TRUNC | O_RDWR, 0666);
277c9aac00SAnup Patel 	if (fd < 0)
287c9aac00SAnup Patel 		die("Failed to write dtb to %s", dtb_file);
297c9aac00SAnup Patel 
307c9aac00SAnup Patel 	count = write(fd, fdt, FDT_MAX_SIZE);
317c9aac00SAnup Patel 	if (count < 0)
327c9aac00SAnup Patel 		die_perror("Failed to dump dtb");
337c9aac00SAnup Patel 
347c9aac00SAnup Patel 	pr_debug("Wrote %d bytes to dtb %s", count, dtb_file);
357c9aac00SAnup Patel 	close(fd);
367c9aac00SAnup Patel }
377c9aac00SAnup Patel 
387c9aac00SAnup Patel #define CPU_NAME_MAX_LEN 15
397c9aac00SAnup Patel #define CPU_ISA_MAX_LEN 128
407c9aac00SAnup Patel static void generate_cpu_nodes(void *fdt, struct kvm *kvm)
417c9aac00SAnup Patel {
427c9aac00SAnup Patel 	int cpu, pos, i, index, valid_isa_len;
437c9aac00SAnup Patel 	const char *valid_isa_order = "IEMAFDQCLBJTPVNSUHKORWXYZG";
44*8aff29e1SAtish Patra 	int arr_sz = ARRAY_SIZE(isa_info_arr);
457c9aac00SAnup Patel 
467c9aac00SAnup Patel 	_FDT(fdt_begin_node(fdt, "cpus"));
477c9aac00SAnup Patel 	_FDT(fdt_property_cell(fdt, "#address-cells", 0x1));
487c9aac00SAnup Patel 	_FDT(fdt_property_cell(fdt, "#size-cells", 0x0));
497c9aac00SAnup Patel 	_FDT(fdt_property_cell(fdt, "timebase-frequency",
507c9aac00SAnup Patel 				kvm->cpus[0]->riscv_timebase));
517c9aac00SAnup Patel 
527c9aac00SAnup Patel 	for (cpu = 0; cpu < kvm->nrcpus; ++cpu) {
537c9aac00SAnup Patel 		char cpu_name[CPU_NAME_MAX_LEN];
547c9aac00SAnup Patel 		char cpu_isa[CPU_ISA_MAX_LEN];
557c9aac00SAnup Patel 		struct kvm_cpu *vcpu = kvm->cpus[cpu];
56*8aff29e1SAtish Patra 		struct kvm_one_reg reg;
57*8aff29e1SAtish Patra 		unsigned long isa_ext_out = 0;
587c9aac00SAnup Patel 
597c9aac00SAnup Patel 		snprintf(cpu_name, CPU_NAME_MAX_LEN, "cpu@%x", cpu);
607c9aac00SAnup Patel 
617c9aac00SAnup Patel 		snprintf(cpu_isa, CPU_ISA_MAX_LEN, "rv%ld", vcpu->riscv_xlen);
627c9aac00SAnup Patel 		pos = strlen(cpu_isa);
637c9aac00SAnup Patel 		valid_isa_len = strlen(valid_isa_order);
647c9aac00SAnup Patel 		for (i = 0; i < valid_isa_len; i++) {
657c9aac00SAnup Patel 			index = valid_isa_order[i] - 'A';
667c9aac00SAnup Patel 			if (vcpu->riscv_isa & (1 << (index)))
677c9aac00SAnup Patel 				cpu_isa[pos++] = 'a' + index;
687c9aac00SAnup Patel 		}
69*8aff29e1SAtish Patra 
70*8aff29e1SAtish Patra 		for (i = 0; i < arr_sz; i++) {
71*8aff29e1SAtish Patra 			reg.id = RISCV_ISA_EXT_REG(isa_info_arr[i].ext_id);
72*8aff29e1SAtish Patra 			reg.addr = (unsigned long)&isa_ext_out;
73*8aff29e1SAtish Patra 			if (ioctl(vcpu->vcpu_fd, KVM_GET_ONE_REG, &reg) < 0)
74*8aff29e1SAtish Patra 				continue;
75*8aff29e1SAtish Patra 			if (!isa_ext_out)
76*8aff29e1SAtish Patra 				/* This extension is not available in hardware */
77*8aff29e1SAtish Patra 				continue;
78*8aff29e1SAtish Patra 
79*8aff29e1SAtish Patra 			if ((strlen(isa_info_arr[i].name) + pos + 1) >= CPU_ISA_MAX_LEN) {
80*8aff29e1SAtish Patra 				pr_warning("Insufficient space to append ISA exension\n");
81*8aff29e1SAtish Patra 				break;
82*8aff29e1SAtish Patra 			}
83*8aff29e1SAtish Patra 			pos += snprintf(cpu_isa + pos, CPU_ISA_MAX_LEN, "_%s",
84*8aff29e1SAtish Patra 					isa_info_arr[i].name);
85*8aff29e1SAtish Patra 		}
867c9aac00SAnup Patel 		cpu_isa[pos] = '\0';
877c9aac00SAnup Patel 
887c9aac00SAnup Patel 		_FDT(fdt_begin_node(fdt, cpu_name));
897c9aac00SAnup Patel 		_FDT(fdt_property_string(fdt, "device_type", "cpu"));
907c9aac00SAnup Patel 		_FDT(fdt_property_string(fdt, "compatible", "riscv"));
917c9aac00SAnup Patel 		if (vcpu->riscv_xlen == 64)
927c9aac00SAnup Patel 			_FDT(fdt_property_string(fdt, "mmu-type",
937c9aac00SAnup Patel 						 "riscv,sv48"));
947c9aac00SAnup Patel 		else
957c9aac00SAnup Patel 			_FDT(fdt_property_string(fdt, "mmu-type",
967c9aac00SAnup Patel 						 "riscv,sv32"));
977c9aac00SAnup Patel 		_FDT(fdt_property_string(fdt, "riscv,isa", cpu_isa));
987c9aac00SAnup Patel 		_FDT(fdt_property_cell(fdt, "reg", cpu));
997c9aac00SAnup Patel 		_FDT(fdt_property_string(fdt, "status", "okay"));
1007c9aac00SAnup Patel 
1017c9aac00SAnup Patel 		_FDT(fdt_begin_node(fdt, "interrupt-controller"));
1027c9aac00SAnup Patel 		_FDT(fdt_property_string(fdt, "compatible", "riscv,cpu-intc"));
1037c9aac00SAnup Patel 		_FDT(fdt_property_cell(fdt, "#interrupt-cells", 1));
1047c9aac00SAnup Patel 		_FDT(fdt_property(fdt, "interrupt-controller", NULL, 0));
1057c9aac00SAnup Patel 		_FDT(fdt_property_cell(fdt, "phandle",
1067c9aac00SAnup Patel 					PHANDLE_CPU_INTC_BASE + cpu));
1077c9aac00SAnup Patel 		_FDT(fdt_end_node(fdt));
1087c9aac00SAnup Patel 
1097c9aac00SAnup Patel 		_FDT(fdt_end_node(fdt));
1107c9aac00SAnup Patel 	}
1117c9aac00SAnup Patel 
1127c9aac00SAnup Patel 	_FDT(fdt_end_node(fdt));
1137c9aac00SAnup Patel }
1147c9aac00SAnup Patel 
1157c9aac00SAnup Patel static int setup_fdt(struct kvm *kvm)
1167c9aac00SAnup Patel {
1177c9aac00SAnup Patel 	struct device_header *dev_hdr;
1187c9aac00SAnup Patel 	u8 staging_fdt[FDT_MAX_SIZE];
1197c9aac00SAnup Patel 	u64 mem_reg_prop[]	= {
1207c9aac00SAnup Patel 		cpu_to_fdt64(kvm->arch.memory_guest_start),
1217c9aac00SAnup Patel 		cpu_to_fdt64(kvm->ram_size),
1227c9aac00SAnup Patel 	};
1237c9aac00SAnup Patel 	void *fdt		= staging_fdt;
1247c9aac00SAnup Patel 	void *fdt_dest		= guest_flat_to_host(kvm,
1257c9aac00SAnup Patel 						     kvm->arch.dtb_guest_start);
1267c9aac00SAnup Patel 	void (*generate_mmio_fdt_nodes)(void *, struct device_header *,
1277c9aac00SAnup Patel 					void (*)(void *, u8, enum irq_type));
1287c9aac00SAnup Patel 
1297c9aac00SAnup Patel 	/* Create new tree without a reserve map */
1307c9aac00SAnup Patel 	_FDT(fdt_create(fdt, FDT_MAX_SIZE));
1317c9aac00SAnup Patel 	_FDT(fdt_finish_reservemap(fdt));
1327c9aac00SAnup Patel 
1337c9aac00SAnup Patel 	/* Header */
1347c9aac00SAnup Patel 	_FDT(fdt_begin_node(fdt, ""));
1357c9aac00SAnup Patel 	_FDT(fdt_property_string(fdt, "compatible", "linux,dummy-virt"));
1367c9aac00SAnup Patel 	_FDT(fdt_property_cell(fdt, "#address-cells", 0x2));
1377c9aac00SAnup Patel 	_FDT(fdt_property_cell(fdt, "#size-cells", 0x2));
1387c9aac00SAnup Patel 
1397c9aac00SAnup Patel 	/* /chosen */
1407c9aac00SAnup Patel 	_FDT(fdt_begin_node(fdt, "chosen"));
1417c9aac00SAnup Patel 
1427c9aac00SAnup Patel 	/* Pass on our amended command line to a Linux kernel only. */
1437c9aac00SAnup Patel 	if (kvm->cfg.firmware_filename) {
1447c9aac00SAnup Patel 		if (kvm->cfg.kernel_cmdline)
1457c9aac00SAnup Patel 			_FDT(fdt_property_string(fdt, "bootargs",
1467c9aac00SAnup Patel 						 kvm->cfg.kernel_cmdline));
1477c9aac00SAnup Patel 	} else
1487c9aac00SAnup Patel 		_FDT(fdt_property_string(fdt, "bootargs",
1497c9aac00SAnup Patel 					 kvm->cfg.real_cmdline));
1507c9aac00SAnup Patel 
1517c9aac00SAnup Patel 	_FDT(fdt_property_string(fdt, "stdout-path", "serial0"));
1527c9aac00SAnup Patel 
1537c9aac00SAnup Patel 	/* Initrd */
1547c9aac00SAnup Patel 	if (kvm->arch.initrd_size != 0) {
1557c9aac00SAnup Patel 		u64 ird_st_prop = cpu_to_fdt64(kvm->arch.initrd_guest_start);
1567c9aac00SAnup Patel 		u64 ird_end_prop = cpu_to_fdt64(kvm->arch.initrd_guest_start +
1577c9aac00SAnup Patel 					       kvm->arch.initrd_size);
1587c9aac00SAnup Patel 
1597c9aac00SAnup Patel 		_FDT(fdt_property(fdt, "linux,initrd-start",
1607c9aac00SAnup Patel 				   &ird_st_prop, sizeof(ird_st_prop)));
1617c9aac00SAnup Patel 		_FDT(fdt_property(fdt, "linux,initrd-end",
1627c9aac00SAnup Patel 				   &ird_end_prop, sizeof(ird_end_prop)));
1637c9aac00SAnup Patel 	}
1647c9aac00SAnup Patel 
1657c9aac00SAnup Patel 	_FDT(fdt_end_node(fdt));
1667c9aac00SAnup Patel 
1677c9aac00SAnup Patel 	/* Memory */
1687c9aac00SAnup Patel 	_FDT(fdt_begin_node(fdt, "memory"));
1697c9aac00SAnup Patel 	_FDT(fdt_property_string(fdt, "device_type", "memory"));
1707c9aac00SAnup Patel 	_FDT(fdt_property(fdt, "reg", mem_reg_prop, sizeof(mem_reg_prop)));
1717c9aac00SAnup Patel 	_FDT(fdt_end_node(fdt));
1727c9aac00SAnup Patel 
1737c9aac00SAnup Patel 	/* CPUs */
1747c9aac00SAnup Patel 	generate_cpu_nodes(fdt, kvm);
1757c9aac00SAnup Patel 
1767c9aac00SAnup Patel 	/* Simple Bus */
1777c9aac00SAnup Patel 	_FDT(fdt_begin_node(fdt, "smb"));
1787c9aac00SAnup Patel 	_FDT(fdt_property_string(fdt, "compatible", "simple-bus"));
1797c9aac00SAnup Patel 	_FDT(fdt_property_cell(fdt, "#address-cells", 0x2));
1807c9aac00SAnup Patel 	_FDT(fdt_property_cell(fdt, "#size-cells", 0x2));
1817c9aac00SAnup Patel 	_FDT(fdt_property_cell(fdt, "interrupt-parent", PHANDLE_PLIC));
1827c9aac00SAnup Patel 	_FDT(fdt_property(fdt, "ranges", NULL, 0));
1837c9aac00SAnup Patel 
1847c9aac00SAnup Patel 	/* Virtio MMIO devices */
1857c9aac00SAnup Patel 	dev_hdr = device__first_dev(DEVICE_BUS_MMIO);
1867c9aac00SAnup Patel 	while (dev_hdr) {
1877c9aac00SAnup Patel 		generate_mmio_fdt_nodes = dev_hdr->data;
1887c9aac00SAnup Patel 		generate_mmio_fdt_nodes(fdt, dev_hdr, plic__generate_irq_prop);
1897c9aac00SAnup Patel 		dev_hdr = device__next_dev(dev_hdr);
1907c9aac00SAnup Patel 	}
1917c9aac00SAnup Patel 
1927c9aac00SAnup Patel 	/* IOPORT devices */
1937c9aac00SAnup Patel 	dev_hdr = device__first_dev(DEVICE_BUS_IOPORT);
1947c9aac00SAnup Patel 	while (dev_hdr) {
1957c9aac00SAnup Patel 		generate_mmio_fdt_nodes = dev_hdr->data;
1967c9aac00SAnup Patel 		generate_mmio_fdt_nodes(fdt, dev_hdr, plic__generate_irq_prop);
1977c9aac00SAnup Patel 		dev_hdr = device__next_dev(dev_hdr);
1987c9aac00SAnup Patel 	}
1997c9aac00SAnup Patel 
200cdd7d8ccSAnup Patel 	/* PCI host controller */
201cdd7d8ccSAnup Patel 	pci__generate_fdt_nodes(fdt);
202cdd7d8ccSAnup Patel 
2037c9aac00SAnup Patel 	_FDT(fdt_end_node(fdt));
2047c9aac00SAnup Patel 
2057c9aac00SAnup Patel 	if (fdt_stdout_path) {
2067c9aac00SAnup Patel 		_FDT(fdt_begin_node(fdt, "aliases"));
2077c9aac00SAnup Patel 		_FDT(fdt_property_string(fdt, "serial0", fdt_stdout_path));
2087c9aac00SAnup Patel 		_FDT(fdt_end_node(fdt));
2097c9aac00SAnup Patel 
2107c9aac00SAnup Patel 		free(fdt_stdout_path);
2117c9aac00SAnup Patel 		fdt_stdout_path = NULL;
2127c9aac00SAnup Patel 	}
2137c9aac00SAnup Patel 
2147c9aac00SAnup Patel 	/* Finalise. */
2157c9aac00SAnup Patel 	_FDT(fdt_end_node(fdt));
2167c9aac00SAnup Patel 	_FDT(fdt_finish(fdt));
2177c9aac00SAnup Patel 
2187c9aac00SAnup Patel 	_FDT(fdt_open_into(fdt, fdt_dest, FDT_MAX_SIZE));
2197c9aac00SAnup Patel 	_FDT(fdt_pack(fdt_dest));
2207c9aac00SAnup Patel 
2217c9aac00SAnup Patel 	if (kvm->cfg.arch.dump_dtb_filename)
2227c9aac00SAnup Patel 		dump_fdt(kvm->cfg.arch.dump_dtb_filename, fdt_dest);
2237c9aac00SAnup Patel 	return 0;
2247c9aac00SAnup Patel }
2257c9aac00SAnup Patel late_init(setup_fdt);
226