xref: /kvmtool/arm/fdt.c (revision ff7ba6faee97ec6459d259747c75d7f16a8780f3)
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"
87c0e8b0cSWill Deacon 
97c0e8b0cSWill Deacon #include <stdbool.h>
107c0e8b0cSWill Deacon 
117c0e8b0cSWill Deacon #include <asm/setup.h>
127c0e8b0cSWill Deacon #include <linux/byteorder.h>
137c0e8b0cSWill Deacon #include <linux/kernel.h>
147c0e8b0cSWill Deacon #include <linux/sizes.h>
157c0e8b0cSWill Deacon 
167c0e8b0cSWill Deacon #define DEBUG			0
177c0e8b0cSWill Deacon #define DEBUG_FDT_DUMP_FILE	"/tmp/kvmtool.dtb"
187c0e8b0cSWill Deacon 
197c0e8b0cSWill Deacon static char kern_cmdline[COMMAND_LINE_SIZE];
207c0e8b0cSWill Deacon 
217c0e8b0cSWill Deacon bool kvm__load_firmware(struct kvm *kvm, const char *firmware_filename)
227c0e8b0cSWill Deacon {
237c0e8b0cSWill Deacon 	return false;
247c0e8b0cSWill Deacon }
257c0e8b0cSWill Deacon 
267c0e8b0cSWill Deacon int kvm__arch_setup_firmware(struct kvm *kvm)
277c0e8b0cSWill Deacon {
287c0e8b0cSWill Deacon 	return 0;
297c0e8b0cSWill Deacon }
307c0e8b0cSWill Deacon 
317c0e8b0cSWill Deacon #if DEBUG
327c0e8b0cSWill Deacon static void dump_fdt(void *fdt)
337c0e8b0cSWill Deacon {
347c0e8b0cSWill Deacon 	int count, fd;
357c0e8b0cSWill Deacon 
367c0e8b0cSWill Deacon 	fd = open(DEBUG_FDT_DUMP_FILE, O_CREAT | O_TRUNC | O_RDWR, 0666);
377c0e8b0cSWill Deacon 	if (fd < 0)
387c0e8b0cSWill Deacon 		die("Failed to write dtb to %s", DEBUG_FDT_DUMP_FILE);
397c0e8b0cSWill Deacon 
407c0e8b0cSWill Deacon 	count = write(fd, fdt, FDT_MAX_SIZE);
417c0e8b0cSWill Deacon 	if (count < 0)
427c0e8b0cSWill Deacon 		die_perror("Failed to dump dtb");
437c0e8b0cSWill Deacon 
447c0e8b0cSWill Deacon 	pr_info("Wrote %d bytes to dtb %s\n", count, DEBUG_FDT_DUMP_FILE);
457c0e8b0cSWill Deacon 	close(fd);
467c0e8b0cSWill Deacon }
477c0e8b0cSWill Deacon #else
487c0e8b0cSWill Deacon static void dump_fdt(void *fdt) { }
497c0e8b0cSWill Deacon #endif
507c0e8b0cSWill Deacon 
517c0e8b0cSWill Deacon #define DEVICE_NAME_MAX_LEN 32
527c0e8b0cSWill Deacon static void generate_virtio_mmio_node(void *fdt, struct virtio_mmio *vmmio)
537c0e8b0cSWill Deacon {
547c0e8b0cSWill Deacon 	char dev_name[DEVICE_NAME_MAX_LEN];
557c0e8b0cSWill Deacon 	u64 addr = vmmio->addr;
567c0e8b0cSWill Deacon 	u64 reg_prop[] = {
577c0e8b0cSWill Deacon 		cpu_to_fdt64(addr),
587c0e8b0cSWill Deacon 		cpu_to_fdt64(VIRTIO_MMIO_IO_SIZE)
597c0e8b0cSWill Deacon 	};
607c0e8b0cSWill Deacon 	u32 irq_prop[] = {
617c0e8b0cSWill Deacon 		cpu_to_fdt32(GIC_FDT_IRQ_TYPE_SPI),
627c0e8b0cSWill Deacon 		cpu_to_fdt32(vmmio->irq - GIC_SPI_IRQ_BASE),
637c0e8b0cSWill Deacon 		cpu_to_fdt32(GIC_FDT_IRQ_FLAGS_EDGE_LO_HI),
647c0e8b0cSWill Deacon 	};
657c0e8b0cSWill Deacon 
667c0e8b0cSWill Deacon 	snprintf(dev_name, DEVICE_NAME_MAX_LEN, "virtio@%llx", addr);
677c0e8b0cSWill Deacon 
687c0e8b0cSWill Deacon 	_FDT(fdt_begin_node(fdt, dev_name));
697c0e8b0cSWill Deacon 	_FDT(fdt_property_string(fdt, "compatible", "virtio,mmio"));
707c0e8b0cSWill Deacon 	_FDT(fdt_property(fdt, "reg", reg_prop, sizeof(reg_prop)));
717c0e8b0cSWill Deacon 	_FDT(fdt_property(fdt, "interrupts", irq_prop, sizeof(irq_prop)));
727c0e8b0cSWill Deacon 	_FDT(fdt_end_node(fdt));
737c0e8b0cSWill Deacon }
747c0e8b0cSWill Deacon 
757c0e8b0cSWill Deacon static int setup_fdt(struct kvm *kvm)
767c0e8b0cSWill Deacon {
777c0e8b0cSWill Deacon 	struct device_header *dev_hdr;
787c0e8b0cSWill Deacon 	u8 staging_fdt[FDT_MAX_SIZE];
797c0e8b0cSWill Deacon 	u32 gic_phandle		= fdt__alloc_phandle();
807c0e8b0cSWill Deacon 	u64 mem_reg_prop[]	= {
817c0e8b0cSWill Deacon 		cpu_to_fdt64(kvm->arch.memory_guest_start),
827c0e8b0cSWill Deacon 		cpu_to_fdt64(kvm->ram_size),
837c0e8b0cSWill Deacon 	};
847c0e8b0cSWill Deacon 	void *fdt		= staging_fdt;
857c0e8b0cSWill Deacon 	void *fdt_dest		= guest_flat_to_host(kvm,
867c0e8b0cSWill Deacon 						     kvm->arch.dtb_guest_start);
877c0e8b0cSWill Deacon 	void (*generate_cpu_nodes)(void *, struct kvm *, u32)
887c0e8b0cSWill Deacon 				= kvm->cpus[0]->generate_fdt_nodes;
897c0e8b0cSWill Deacon 
907c0e8b0cSWill Deacon 	/* Create new tree without a reserve map */
917c0e8b0cSWill Deacon 	_FDT(fdt_create(fdt, FDT_MAX_SIZE));
927c0e8b0cSWill Deacon 	if (kvm->nrcpus > 1)
937c0e8b0cSWill Deacon 		_FDT(fdt_add_reservemap_entry(fdt,
947c0e8b0cSWill Deacon 					      kvm->arch.smp_pen_guest_start,
957c0e8b0cSWill Deacon 					      ARM_SMP_PEN_SIZE));
967c0e8b0cSWill Deacon 	_FDT(fdt_finish_reservemap(fdt));
977c0e8b0cSWill Deacon 
987c0e8b0cSWill Deacon 	/* Header */
997c0e8b0cSWill Deacon 	_FDT(fdt_begin_node(fdt, ""));
1007c0e8b0cSWill Deacon 	_FDT(fdt_property_cell(fdt, "interrupt-parent", gic_phandle));
1017c0e8b0cSWill Deacon 	_FDT(fdt_property_string(fdt, "compatible", "linux,dummy-virt"));
1027c0e8b0cSWill Deacon 	_FDT(fdt_property_cell(fdt, "#address-cells", 0x2));
1037c0e8b0cSWill Deacon 	_FDT(fdt_property_cell(fdt, "#size-cells", 0x2));
1047c0e8b0cSWill Deacon 
1057c0e8b0cSWill Deacon 	/* /chosen */
1067c0e8b0cSWill Deacon 	_FDT(fdt_begin_node(fdt, "chosen"));
1077c0e8b0cSWill Deacon 	_FDT(fdt_property_string(fdt, "bootargs", kern_cmdline));
1087c0e8b0cSWill Deacon 
1097c0e8b0cSWill Deacon 	/* Initrd */
1107c0e8b0cSWill Deacon 	if (kvm->arch.initrd_size != 0) {
1117c0e8b0cSWill Deacon 		u32 ird_st_prop = cpu_to_fdt64(kvm->arch.initrd_guest_start);
1127c0e8b0cSWill Deacon 		u32 ird_end_prop = cpu_to_fdt64(kvm->arch.initrd_guest_start +
1137c0e8b0cSWill Deacon 					       kvm->arch.initrd_size);
1147c0e8b0cSWill Deacon 
1157c0e8b0cSWill Deacon 		_FDT(fdt_property(fdt, "linux,initrd-start",
1167c0e8b0cSWill Deacon 				   &ird_st_prop, sizeof(ird_st_prop)));
1177c0e8b0cSWill Deacon 		_FDT(fdt_property(fdt, "linux,initrd-end",
1187c0e8b0cSWill Deacon 				   &ird_end_prop, sizeof(ird_end_prop)));
1197c0e8b0cSWill Deacon 	}
1207c0e8b0cSWill Deacon 	_FDT(fdt_end_node(fdt));
1217c0e8b0cSWill Deacon 
1227c0e8b0cSWill Deacon 	/* Memory */
1237c0e8b0cSWill Deacon 	_FDT(fdt_begin_node(fdt, "memory"));
1247c0e8b0cSWill Deacon 	_FDT(fdt_property_string(fdt, "device_type", "memory"));
1257c0e8b0cSWill Deacon 	_FDT(fdt_property(fdt, "reg", mem_reg_prop, sizeof(mem_reg_prop)));
1267c0e8b0cSWill Deacon 	_FDT(fdt_end_node(fdt));
1277c0e8b0cSWill Deacon 
1287c0e8b0cSWill Deacon 	/* CPU and peripherals (interrupt controller, timers, etc) */
1297c0e8b0cSWill Deacon 	if (generate_cpu_nodes)
1307c0e8b0cSWill Deacon 		generate_cpu_nodes(fdt, kvm, gic_phandle);
1317c0e8b0cSWill Deacon 
1327c0e8b0cSWill Deacon 	/* Virtio MMIO devices */
1337c0e8b0cSWill Deacon 	dev_hdr = device__first_dev(DEVICE_BUS_MMIO);
1347c0e8b0cSWill Deacon 	while (dev_hdr) {
1357c0e8b0cSWill Deacon 		generate_virtio_mmio_node(fdt, dev_hdr->data);
1367c0e8b0cSWill Deacon 		dev_hdr = device__next_dev(dev_hdr);
1377c0e8b0cSWill Deacon 	}
1387c0e8b0cSWill Deacon 
1397c0e8b0cSWill Deacon 	/* Finalise. */
1407c0e8b0cSWill Deacon 	_FDT(fdt_end_node(fdt));
1417c0e8b0cSWill Deacon 	_FDT(fdt_finish(fdt));
1427c0e8b0cSWill Deacon 
1437c0e8b0cSWill Deacon 	_FDT(fdt_open_into(fdt, fdt_dest, FDT_MAX_SIZE));
1447c0e8b0cSWill Deacon 	_FDT(fdt_pack(fdt_dest));
1457c0e8b0cSWill Deacon 
1467c0e8b0cSWill Deacon 	dump_fdt(fdt_dest);
1477c0e8b0cSWill Deacon 	return 0;
1487c0e8b0cSWill Deacon }
1497c0e8b0cSWill Deacon late_init(setup_fdt);
1507c0e8b0cSWill Deacon 
1517c0e8b0cSWill Deacon static int read_image(int fd, void **pos, void *limit)
1527c0e8b0cSWill Deacon {
1537c0e8b0cSWill Deacon 	int count;
1547c0e8b0cSWill Deacon 
1557c0e8b0cSWill Deacon 	while (((count = xread(fd, *pos, SZ_64K)) > 0) && *pos <= limit)
1567c0e8b0cSWill Deacon 		*pos += count;
1577c0e8b0cSWill Deacon 
1587c0e8b0cSWill Deacon 	if (pos < 0)
1597c0e8b0cSWill Deacon 		die_perror("xread");
1607c0e8b0cSWill Deacon 
1617c0e8b0cSWill Deacon 	return *pos < limit ? 0 : -ENOMEM;
1627c0e8b0cSWill Deacon }
1637c0e8b0cSWill Deacon 
1647c0e8b0cSWill Deacon #define FDT_ALIGN	SZ_2M
1657c0e8b0cSWill Deacon #define INITRD_ALIGN	4
1667c0e8b0cSWill Deacon #define SMP_PEN_ALIGN	PAGE_SIZE
1677c0e8b0cSWill Deacon int load_flat_binary(struct kvm *kvm, int fd_kernel, int fd_initrd,
1687c0e8b0cSWill Deacon 		     const char *kernel_cmdline)
1697c0e8b0cSWill Deacon {
1707c0e8b0cSWill Deacon 	void *pos, *kernel_end, *limit;
1717c0e8b0cSWill Deacon 	unsigned long guest_addr;
1727c0e8b0cSWill Deacon 
1737c0e8b0cSWill Deacon 	if (lseek(fd_kernel, 0, SEEK_SET) < 0)
1747c0e8b0cSWill Deacon 		die_perror("lseek");
1757c0e8b0cSWill Deacon 
1767c0e8b0cSWill Deacon 	/*
1777c0e8b0cSWill Deacon 	 * Linux requires the initrd, pen and dtb to be mapped inside
1787c0e8b0cSWill Deacon 	 * lowmem, so we can't just place them at the top of memory.
1797c0e8b0cSWill Deacon 	 */
1807c0e8b0cSWill Deacon 	limit = kvm->ram_start + min(kvm->ram_size, (u64)SZ_256M) - 1;
1817c0e8b0cSWill Deacon 
1827c0e8b0cSWill Deacon 	pos = kvm->ram_start + ARM_KERN_OFFSET;
1837c0e8b0cSWill Deacon 	kvm->arch.kern_guest_start = host_to_guest_flat(kvm, pos);
1847c0e8b0cSWill Deacon 	if (read_image(fd_kernel, &pos, limit) == -ENOMEM)
1857c0e8b0cSWill Deacon 		die("kernel image too big to contain in guest memory.");
1867c0e8b0cSWill Deacon 
1877c0e8b0cSWill Deacon 	kernel_end = pos;
1887c0e8b0cSWill Deacon 	pr_info("Loaded kernel to 0x%llx (%llu bytes)",
1897c0e8b0cSWill Deacon 		kvm->arch.kern_guest_start,
1907c0e8b0cSWill Deacon 		host_to_guest_flat(kvm, pos) - kvm->arch.kern_guest_start);
1917c0e8b0cSWill Deacon 
1927c0e8b0cSWill Deacon 	/*
1937c0e8b0cSWill Deacon 	 * Now load backwards from the end of memory so the kernel
1947c0e8b0cSWill Deacon 	 * decompressor has plenty of space to work with. First up is
1957c0e8b0cSWill Deacon 	 * the SMP pen if we have more than one virtual CPU...
1967c0e8b0cSWill Deacon 	 */
1977c0e8b0cSWill Deacon 	pos = limit;
1987c0e8b0cSWill Deacon 	if (kvm->cfg.nrcpus > 1) {
1997c0e8b0cSWill Deacon 		pos -= (ARM_SMP_PEN_SIZE + SMP_PEN_ALIGN);
2007c0e8b0cSWill Deacon 		guest_addr = ALIGN(host_to_guest_flat(kvm, pos), SMP_PEN_ALIGN);
2017c0e8b0cSWill Deacon 		pos = guest_flat_to_host(kvm, guest_addr);
2027c0e8b0cSWill Deacon 		if (pos < kernel_end)
2037c0e8b0cSWill Deacon 			die("SMP pen overlaps with kernel image.");
2047c0e8b0cSWill Deacon 
2057c0e8b0cSWill Deacon 		kvm->arch.smp_pen_guest_start = guest_addr;
2067c0e8b0cSWill Deacon 		pr_info("Placing SMP pen at 0x%llx - 0x%llx",
2077c0e8b0cSWill Deacon 			kvm->arch.smp_pen_guest_start,
2087c0e8b0cSWill Deacon 			host_to_guest_flat(kvm, limit));
2097c0e8b0cSWill Deacon 		limit = pos;
2107c0e8b0cSWill Deacon 	}
2117c0e8b0cSWill Deacon 
2127c0e8b0cSWill Deacon 	/* ...now the device tree blob... */
2137c0e8b0cSWill Deacon 	pos -= (FDT_MAX_SIZE + FDT_ALIGN);
2147c0e8b0cSWill Deacon 	guest_addr = ALIGN(host_to_guest_flat(kvm, pos), FDT_ALIGN);
2157c0e8b0cSWill Deacon 	pos = guest_flat_to_host(kvm, guest_addr);
2167c0e8b0cSWill Deacon 	if (pos < kernel_end)
2177c0e8b0cSWill Deacon 		die("fdt overlaps with kernel image.");
2187c0e8b0cSWill Deacon 
2197c0e8b0cSWill Deacon 	kvm->arch.dtb_guest_start = guest_addr;
2207c0e8b0cSWill Deacon 	pr_info("Placing fdt at 0x%llx - 0x%llx",
2217c0e8b0cSWill Deacon 		kvm->arch.dtb_guest_start,
2227c0e8b0cSWill Deacon 		host_to_guest_flat(kvm, limit));
2237c0e8b0cSWill Deacon 	limit = pos;
2247c0e8b0cSWill Deacon 
2257c0e8b0cSWill Deacon 	/* ... and finally the initrd, if we have one. */
2267c0e8b0cSWill Deacon 	if (fd_initrd != -1) {
2277c0e8b0cSWill Deacon 		struct stat sb;
2287c0e8b0cSWill Deacon 		unsigned long initrd_start;
2297c0e8b0cSWill Deacon 
2307c0e8b0cSWill Deacon 		if (lseek(fd_initrd, 0, SEEK_SET) < 0)
2317c0e8b0cSWill Deacon 			die_perror("lseek");
2327c0e8b0cSWill Deacon 
2337c0e8b0cSWill Deacon 		if (fstat(fd_initrd, &sb))
2347c0e8b0cSWill Deacon 			die_perror("fstat");
2357c0e8b0cSWill Deacon 
2367c0e8b0cSWill Deacon 		pos -= (sb.st_size + INITRD_ALIGN);
2377c0e8b0cSWill Deacon 		guest_addr = ALIGN(host_to_guest_flat(kvm, pos), INITRD_ALIGN);
2387c0e8b0cSWill Deacon 		pos = guest_flat_to_host(kvm, guest_addr);
2397c0e8b0cSWill Deacon 		if (pos < kernel_end)
2407c0e8b0cSWill Deacon 			die("initrd overlaps with kernel image.");
2417c0e8b0cSWill Deacon 
2427c0e8b0cSWill Deacon 		initrd_start = guest_addr;
2437c0e8b0cSWill Deacon 		if (read_image(fd_initrd, &pos, limit) == -ENOMEM)
2447c0e8b0cSWill Deacon 			die("initrd too big to contain in guest memory.");
2457c0e8b0cSWill Deacon 
2467c0e8b0cSWill Deacon 		kvm->arch.initrd_guest_start = initrd_start;
2477c0e8b0cSWill Deacon 		kvm->arch.initrd_size = host_to_guest_flat(kvm, pos) - initrd_start;
2487c0e8b0cSWill Deacon 		pr_info("Loaded initrd to 0x%llx (%llu bytes)",
2497c0e8b0cSWill Deacon 			kvm->arch.initrd_guest_start,
2507c0e8b0cSWill Deacon 			kvm->arch.initrd_size);
2517c0e8b0cSWill Deacon 	} else {
2527c0e8b0cSWill Deacon 		kvm->arch.initrd_size = 0;
2537c0e8b0cSWill Deacon 	}
2547c0e8b0cSWill Deacon 
2557c0e8b0cSWill Deacon 	strncpy(kern_cmdline, kernel_cmdline, COMMAND_LINE_SIZE);
2567c0e8b0cSWill Deacon 	kern_cmdline[COMMAND_LINE_SIZE - 1] = '\0';
2577c0e8b0cSWill Deacon 
2587c0e8b0cSWill Deacon 	return true;
2597c0e8b0cSWill Deacon }
2607c0e8b0cSWill Deacon 
261*ff7ba6faSWill Deacon bool load_bzimage(struct kvm *kvm, int fd_kernel, int fd_initrd,
262*ff7ba6faSWill Deacon 		  const char *kernel_cmdline)
2637c0e8b0cSWill Deacon {
2647c0e8b0cSWill Deacon 	/* To b or not to b? That is the zImage. */
2657c0e8b0cSWill Deacon 	return false;
2667c0e8b0cSWill Deacon }
267