xref: /kvmtool/arm/kvm.c (revision 9afefd6c79015480366e42eb7faa60f72d5984ae)
17c0e8b0cSWill Deacon #include "kvm/kvm.h"
27c0e8b0cSWill Deacon #include "kvm/term.h"
37c0e8b0cSWill Deacon #include "kvm/util.h"
497e65b5fSWill Deacon #include "kvm/8250-serial.h"
57c0e8b0cSWill Deacon #include "kvm/virtio-console.h"
63c8aec9eSAndre Przywara #include "kvm/fdt.h"
77c0e8b0cSWill Deacon 
87c0e8b0cSWill Deacon #include "arm-common/gic.h"
97c0e8b0cSWill Deacon 
107c0e8b0cSWill Deacon #include <linux/kernel.h>
117c0e8b0cSWill Deacon #include <linux/kvm.h>
129eb3dbcbSWill Deacon #include <linux/sizes.h>
137c0e8b0cSWill Deacon 
147c0e8b0cSWill Deacon struct kvm_ext kvm_req_ext[] = {
157c0e8b0cSWill Deacon 	{ DEFINE_KVM_EXT(KVM_CAP_IRQCHIP) },
167c0e8b0cSWill Deacon 	{ DEFINE_KVM_EXT(KVM_CAP_ONE_REG) },
1761076240SWill Deacon 	{ DEFINE_KVM_EXT(KVM_CAP_ARM_PSCI) },
187c0e8b0cSWill Deacon 	{ 0, 0 },
197c0e8b0cSWill Deacon };
207c0e8b0cSWill Deacon 
217c0e8b0cSWill Deacon bool kvm__arch_cpu_supports_vm(void)
227c0e8b0cSWill Deacon {
237c0e8b0cSWill Deacon 	/* The KVM capability check is enough. */
247c0e8b0cSWill Deacon 	return true;
257c0e8b0cSWill Deacon }
267c0e8b0cSWill Deacon 
277c0e8b0cSWill Deacon void kvm__init_ram(struct kvm *kvm)
287c0e8b0cSWill Deacon {
297c0e8b0cSWill Deacon 	int err;
307c0e8b0cSWill Deacon 	u64 phys_start, phys_size;
317c0e8b0cSWill Deacon 	void *host_mem;
327c0e8b0cSWill Deacon 
337c0e8b0cSWill Deacon 	phys_start	= ARM_MEMORY_AREA;
347c0e8b0cSWill Deacon 	phys_size	= kvm->ram_size;
357c0e8b0cSWill Deacon 	host_mem	= kvm->ram_start;
367c0e8b0cSWill Deacon 
378f46c736SJean-Philippe Brucker 	err = kvm__register_ram(kvm, phys_start, phys_size, host_mem);
387c0e8b0cSWill Deacon 	if (err)
397c0e8b0cSWill Deacon 		die("Failed to register %lld bytes of memory at physical "
407c0e8b0cSWill Deacon 		    "address 0x%llx [err %d]", phys_size, phys_start, err);
417c0e8b0cSWill Deacon 
427c0e8b0cSWill Deacon 	kvm->arch.memory_guest_start = phys_start;
437c0e8b0cSWill Deacon }
447c0e8b0cSWill Deacon 
457c0e8b0cSWill Deacon void kvm__arch_delete_ram(struct kvm *kvm)
467c0e8b0cSWill Deacon {
479eb3dbcbSWill Deacon 	munmap(kvm->arch.ram_alloc_start, kvm->arch.ram_alloc_size);
487c0e8b0cSWill Deacon }
497c0e8b0cSWill Deacon 
5012c406a8SJonathan Austin void kvm__arch_read_term(struct kvm *kvm)
517c0e8b0cSWill Deacon {
5297e65b5fSWill Deacon 	serial8250__update_consoles(kvm);
537c0e8b0cSWill Deacon 	virtio_console__inject_interrupt(kvm);
547c0e8b0cSWill Deacon }
557c0e8b0cSWill Deacon 
567c0e8b0cSWill Deacon void kvm__arch_set_cmdline(char *cmdline, bool video)
577c0e8b0cSWill Deacon {
587c0e8b0cSWill Deacon }
597c0e8b0cSWill Deacon 
607c0e8b0cSWill Deacon void kvm__arch_init(struct kvm *kvm, const char *hugetlbfs_path, u64 ram_size)
617c0e8b0cSWill Deacon {
629eb3dbcbSWill Deacon 	/*
63b4d9ac64SMarc Zyngier 	 * Allocate guest memory. We must align our buffer to 64K to
649eb3dbcbSWill Deacon 	 * correlate with the maximum guest page size for virtio-mmio.
65b4d9ac64SMarc Zyngier 	 * If using THP, then our minimal alignment becomes 2M.
66b4d9ac64SMarc Zyngier 	 * 2M trumps 64K, so let's go with that.
679eb3dbcbSWill Deacon 	 */
681e0c135aSWill Deacon 	kvm->ram_size = min(ram_size, (u64)ARM_MAX_MEMORY(kvm));
69b4d9ac64SMarc Zyngier 	kvm->arch.ram_alloc_size = kvm->ram_size + SZ_2M;
709eb3dbcbSWill Deacon 	kvm->arch.ram_alloc_start = mmap_anon_or_hugetlbfs(kvm, hugetlbfs_path,
719eb3dbcbSWill Deacon 						kvm->arch.ram_alloc_size);
729eb3dbcbSWill Deacon 
739eb3dbcbSWill Deacon 	if (kvm->arch.ram_alloc_start == MAP_FAILED)
747c0e8b0cSWill Deacon 		die("Failed to map %lld bytes for guest memory (%d)",
759eb3dbcbSWill Deacon 		    kvm->arch.ram_alloc_size, errno);
769eb3dbcbSWill Deacon 
779eb3dbcbSWill Deacon 	kvm->ram_start = (void *)ALIGN((unsigned long)kvm->arch.ram_alloc_start,
78b4d9ac64SMarc Zyngier 					SZ_2M);
799eb3dbcbSWill Deacon 
809eb3dbcbSWill Deacon 	madvise(kvm->arch.ram_alloc_start, kvm->arch.ram_alloc_size,
810093df80SStefan Agner 		MADV_MERGEABLE);
820093df80SStefan Agner 
830093df80SStefan Agner 	madvise(kvm->arch.ram_alloc_start, kvm->arch.ram_alloc_size,
840093df80SStefan Agner 		MADV_HUGEPAGE);
857c0e8b0cSWill Deacon 
8669b9a17aSMarc Zyngier 	/* Create the virtual GIC. */
8743d2781cSAndre Przywara 	if (gic__create(kvm, kvm->cfg.arch.irqchip))
8869b9a17aSMarc Zyngier 		die("Failed to create virtual GIC");
897c0e8b0cSWill Deacon }
903c8aec9eSAndre Przywara 
913c8aec9eSAndre Przywara #define FDT_ALIGN	SZ_2M
923c8aec9eSAndre Przywara #define INITRD_ALIGN	4
933c8aec9eSAndre Przywara bool kvm__arch_load_kernel_image(struct kvm *kvm, int fd_kernel, int fd_initrd,
943c8aec9eSAndre Przywara 				 const char *kernel_cmdline)
953c8aec9eSAndre Przywara {
963c8aec9eSAndre Przywara 	void *pos, *kernel_end, *limit;
973c8aec9eSAndre Przywara 	unsigned long guest_addr;
983c8aec9eSAndre Przywara 	ssize_t file_size;
993c8aec9eSAndre Przywara 
1003c8aec9eSAndre Przywara 	/*
1013c8aec9eSAndre Przywara 	 * Linux requires the initrd and dtb to be mapped inside lowmem,
1023c8aec9eSAndre Przywara 	 * so we can't just place them at the top of memory.
1033c8aec9eSAndre Przywara 	 */
1043c8aec9eSAndre Przywara 	limit = kvm->ram_start + min(kvm->ram_size, (u64)SZ_256M) - 1;
1053c8aec9eSAndre Przywara 
1063c8aec9eSAndre Przywara 	pos = kvm->ram_start + ARM_KERN_OFFSET(kvm);
1073c8aec9eSAndre Przywara 	kvm->arch.kern_guest_start = host_to_guest_flat(kvm, pos);
1083c8aec9eSAndre Przywara 	file_size = read_file(fd_kernel, pos, limit - pos);
1093c8aec9eSAndre Przywara 	if (file_size < 0) {
1103c8aec9eSAndre Przywara 		if (errno == ENOMEM)
1113c8aec9eSAndre Przywara 			die("kernel image too big to contain in guest memory.");
1123c8aec9eSAndre Przywara 
1133c8aec9eSAndre Przywara 		die_perror("kernel read");
1143c8aec9eSAndre Przywara 	}
1153c8aec9eSAndre Przywara 	kernel_end = pos + file_size;
1163c8aec9eSAndre Przywara 	pr_info("Loaded kernel to 0x%llx (%zd bytes)",
1173c8aec9eSAndre Przywara 		kvm->arch.kern_guest_start, file_size);
1183c8aec9eSAndre Przywara 
1193c8aec9eSAndre Przywara 	/*
1203c8aec9eSAndre Przywara 	 * Now load backwards from the end of memory so the kernel
1213c8aec9eSAndre Przywara 	 * decompressor has plenty of space to work with. First up is
1223c8aec9eSAndre Przywara 	 * the device tree blob...
1233c8aec9eSAndre Przywara 	 */
1243c8aec9eSAndre Przywara 	pos = limit;
1253c8aec9eSAndre Przywara 	pos -= (FDT_MAX_SIZE + FDT_ALIGN);
1263c8aec9eSAndre Przywara 	guest_addr = ALIGN(host_to_guest_flat(kvm, pos), FDT_ALIGN);
1273c8aec9eSAndre Przywara 	pos = guest_flat_to_host(kvm, guest_addr);
1283c8aec9eSAndre Przywara 	if (pos < kernel_end)
1293c8aec9eSAndre Przywara 		die("fdt overlaps with kernel image.");
1303c8aec9eSAndre Przywara 
1313c8aec9eSAndre Przywara 	kvm->arch.dtb_guest_start = guest_addr;
1323c8aec9eSAndre Przywara 	pr_info("Placing fdt at 0x%llx - 0x%llx",
1333c8aec9eSAndre Przywara 		kvm->arch.dtb_guest_start,
1343c8aec9eSAndre Przywara 		host_to_guest_flat(kvm, limit));
1353c8aec9eSAndre Przywara 	limit = pos;
1363c8aec9eSAndre Przywara 
1373c8aec9eSAndre Przywara 	/* ... and finally the initrd, if we have one. */
1383c8aec9eSAndre Przywara 	if (fd_initrd != -1) {
1393c8aec9eSAndre Przywara 		struct stat sb;
1403c8aec9eSAndre Przywara 		unsigned long initrd_start;
1413c8aec9eSAndre Przywara 
1423c8aec9eSAndre Przywara 		if (fstat(fd_initrd, &sb))
1433c8aec9eSAndre Przywara 			die_perror("fstat");
1443c8aec9eSAndre Przywara 
1453c8aec9eSAndre Przywara 		pos -= (sb.st_size + INITRD_ALIGN);
1463c8aec9eSAndre Przywara 		guest_addr = ALIGN(host_to_guest_flat(kvm, pos), INITRD_ALIGN);
1473c8aec9eSAndre Przywara 		pos = guest_flat_to_host(kvm, guest_addr);
1483c8aec9eSAndre Przywara 		if (pos < kernel_end)
1493c8aec9eSAndre Przywara 			die("initrd overlaps with kernel image.");
1503c8aec9eSAndre Przywara 
1513c8aec9eSAndre Przywara 		initrd_start = guest_addr;
1523c8aec9eSAndre Przywara 		file_size = read_file(fd_initrd, pos, limit - pos);
1533c8aec9eSAndre Przywara 		if (file_size == -1) {
1543c8aec9eSAndre Przywara 			if (errno == ENOMEM)
1553c8aec9eSAndre Przywara 				die("initrd too big to contain in guest memory.");
1563c8aec9eSAndre Przywara 
1573c8aec9eSAndre Przywara 			die_perror("initrd read");
1583c8aec9eSAndre Przywara 		}
1593c8aec9eSAndre Przywara 
1603c8aec9eSAndre Przywara 		kvm->arch.initrd_guest_start = initrd_start;
1613c8aec9eSAndre Przywara 		kvm->arch.initrd_size = file_size;
1623c8aec9eSAndre Przywara 		pr_info("Loaded initrd to 0x%llx (%llu bytes)",
1633c8aec9eSAndre Przywara 			kvm->arch.initrd_guest_start,
1643c8aec9eSAndre Przywara 			kvm->arch.initrd_size);
1653c8aec9eSAndre Przywara 	} else {
1663c8aec9eSAndre Przywara 		kvm->arch.initrd_size = 0;
1673c8aec9eSAndre Przywara 	}
1683c8aec9eSAndre Przywara 
1693c8aec9eSAndre Przywara 	return true;
1703c8aec9eSAndre Przywara }
171f269c81dSJulien Thierry 
172*9afefd6cSJulien Thierry static bool validate_fw_addr(struct kvm *kvm, u64 fw_addr)
173*9afefd6cSJulien Thierry {
174*9afefd6cSJulien Thierry 	u64 ram_phys;
175*9afefd6cSJulien Thierry 
176*9afefd6cSJulien Thierry 	ram_phys = host_to_guest_flat(kvm, kvm->ram_start);
177*9afefd6cSJulien Thierry 
178*9afefd6cSJulien Thierry 	if (fw_addr < ram_phys || fw_addr >= ram_phys + kvm->ram_size) {
179*9afefd6cSJulien Thierry 		pr_err("Provide --firmware-address an address in RAM: "
180*9afefd6cSJulien Thierry 		       "0x%016llx - 0x%016llx",
181*9afefd6cSJulien Thierry 		       ram_phys, ram_phys + kvm->ram_size);
182*9afefd6cSJulien Thierry 
183*9afefd6cSJulien Thierry 		return false;
184*9afefd6cSJulien Thierry 	}
185*9afefd6cSJulien Thierry 
186*9afefd6cSJulien Thierry 	return true;
187*9afefd6cSJulien Thierry }
188*9afefd6cSJulien Thierry 
189f269c81dSJulien Thierry bool kvm__load_firmware(struct kvm *kvm, const char *firmware_filename)
190f269c81dSJulien Thierry {
191*9afefd6cSJulien Thierry 	u64 fw_addr = kvm->cfg.arch.fw_addr;
192*9afefd6cSJulien Thierry 	void *host_pos;
193*9afefd6cSJulien Thierry 	void *limit;
194*9afefd6cSJulien Thierry 	ssize_t fw_sz;
195*9afefd6cSJulien Thierry 	int fd;
196*9afefd6cSJulien Thierry 
197*9afefd6cSJulien Thierry 	limit = kvm->ram_start + kvm->ram_size;
198*9afefd6cSJulien Thierry 
199*9afefd6cSJulien Thierry 	/* For default firmware address, lets load it at the begining of RAM */
200*9afefd6cSJulien Thierry 	if (fw_addr == 0)
201*9afefd6cSJulien Thierry 		fw_addr = ARM_MEMORY_AREA;
202*9afefd6cSJulien Thierry 
203*9afefd6cSJulien Thierry 	if (!validate_fw_addr(kvm, fw_addr))
204*9afefd6cSJulien Thierry 		die("Bad firmware destination: 0x%016llx", fw_addr);
205*9afefd6cSJulien Thierry 
206*9afefd6cSJulien Thierry 	fd = open(firmware_filename, O_RDONLY);
207*9afefd6cSJulien Thierry 	if (fd < 0)
208f269c81dSJulien Thierry 		return false;
209*9afefd6cSJulien Thierry 
210*9afefd6cSJulien Thierry 	host_pos = guest_flat_to_host(kvm, fw_addr);
211*9afefd6cSJulien Thierry 	if (!host_pos || host_pos < kvm->ram_start)
212*9afefd6cSJulien Thierry 		return false;
213*9afefd6cSJulien Thierry 
214*9afefd6cSJulien Thierry 	fw_sz = read_file(fd, host_pos, limit - host_pos);
215*9afefd6cSJulien Thierry 	if (fw_sz < 0)
216*9afefd6cSJulien Thierry 		die("failed to load firmware");
217*9afefd6cSJulien Thierry 	close(fd);
218*9afefd6cSJulien Thierry 
219*9afefd6cSJulien Thierry 	/* Kernel isn't loaded by kvm, point start address to firmware */
220*9afefd6cSJulien Thierry 	kvm->arch.kern_guest_start = fw_addr;
221*9afefd6cSJulien Thierry 
222*9afefd6cSJulien Thierry 	/* Load dtb just after the firmware image*/
223*9afefd6cSJulien Thierry 	host_pos += fw_sz;
224*9afefd6cSJulien Thierry 	if (host_pos + FDT_MAX_SIZE > limit)
225*9afefd6cSJulien Thierry 		die("not enough space to load fdt");
226*9afefd6cSJulien Thierry 
227*9afefd6cSJulien Thierry 	kvm->arch.dtb_guest_start = ALIGN(host_to_guest_flat(kvm, host_pos),
228*9afefd6cSJulien Thierry 					  FDT_ALIGN);
229*9afefd6cSJulien Thierry 	pr_info("Placing fdt at 0x%llx - 0x%llx",
230*9afefd6cSJulien Thierry 		kvm->arch.dtb_guest_start,
231*9afefd6cSJulien Thierry 		kvm->arch.dtb_guest_start + FDT_MAX_SIZE);
232*9afefd6cSJulien Thierry 
233*9afefd6cSJulien Thierry 	return true;
234f269c81dSJulien Thierry }
235f269c81dSJulien Thierry 
236f269c81dSJulien Thierry int kvm__arch_setup_firmware(struct kvm *kvm)
237f269c81dSJulien Thierry {
238f269c81dSJulien Thierry 	return 0;
239f269c81dSJulien Thierry }
240