xref: /kvmtool/mips/kvm.c (revision 1ade2d7d89dc33a183f748ddf871337f6bcd9664)
17281a8dbSDavid Daney #include "kvm/kvm.h"
27281a8dbSDavid Daney #include "kvm/ioport.h"
37281a8dbSDavid Daney #include "kvm/virtio-console.h"
47281a8dbSDavid Daney 
57281a8dbSDavid Daney #include <linux/kvm.h>
67281a8dbSDavid Daney 
77281a8dbSDavid Daney #include <ctype.h>
87281a8dbSDavid Daney #include <unistd.h>
9*1ade2d7dSDavid Daney #include <elf.h>
107281a8dbSDavid Daney 
117281a8dbSDavid Daney struct kvm_ext kvm_req_ext[] = {
127281a8dbSDavid Daney 	{ 0, 0 }
137281a8dbSDavid Daney };
147281a8dbSDavid Daney 
157281a8dbSDavid Daney void kvm__arch_read_term(struct kvm *kvm)
167281a8dbSDavid Daney {
177281a8dbSDavid Daney 	virtio_console__inject_interrupt(kvm);
187281a8dbSDavid Daney }
197281a8dbSDavid Daney 
207281a8dbSDavid Daney void kvm__init_ram(struct kvm *kvm)
217281a8dbSDavid Daney {
227281a8dbSDavid Daney 	u64	phys_start, phys_size;
237281a8dbSDavid Daney 	void	*host_mem;
247281a8dbSDavid Daney 
257281a8dbSDavid Daney 	phys_start = 0;
267281a8dbSDavid Daney 	phys_size  = kvm->ram_size;
277281a8dbSDavid Daney 	host_mem   = kvm->ram_start;
287281a8dbSDavid Daney 
297281a8dbSDavid Daney 	kvm__register_mem(kvm, phys_start, phys_size, host_mem);
307281a8dbSDavid Daney }
317281a8dbSDavid Daney 
327281a8dbSDavid Daney void kvm__arch_delete_ram(struct kvm *kvm)
337281a8dbSDavid Daney {
347281a8dbSDavid Daney 	munmap(kvm->ram_start, kvm->ram_size);
357281a8dbSDavid Daney }
367281a8dbSDavid Daney 
377281a8dbSDavid Daney void kvm__arch_set_cmdline(char *cmdline, bool video)
387281a8dbSDavid Daney {
397281a8dbSDavid Daney 
407281a8dbSDavid Daney }
417281a8dbSDavid Daney 
427281a8dbSDavid Daney /* Architecture-specific KVM init */
437281a8dbSDavid Daney void kvm__arch_init(struct kvm *kvm, const char *hugetlbfs_path, u64 ram_size)
447281a8dbSDavid Daney {
457281a8dbSDavid Daney 	int ret;
467281a8dbSDavid Daney 
477281a8dbSDavid Daney 	kvm->ram_start = mmap_anon_or_hugetlbfs(kvm, hugetlbfs_path, ram_size);
487281a8dbSDavid Daney 	kvm->ram_size = ram_size;
497281a8dbSDavid Daney 
507281a8dbSDavid Daney 	if (kvm->ram_start == MAP_FAILED)
517281a8dbSDavid Daney 		die("out of memory");
527281a8dbSDavid Daney 
537281a8dbSDavid Daney 	madvise(kvm->ram_start, kvm->ram_size, MADV_MERGEABLE);
547281a8dbSDavid Daney 
557281a8dbSDavid Daney 	ret = ioctl(kvm->vm_fd, KVM_CREATE_IRQCHIP);
567281a8dbSDavid Daney 	if (ret < 0)
577281a8dbSDavid Daney 		die_perror("KVM_CREATE_IRQCHIP ioctl");
587281a8dbSDavid Daney }
597281a8dbSDavid Daney 
607281a8dbSDavid Daney void kvm__irq_line(struct kvm *kvm, int irq, int level)
617281a8dbSDavid Daney {
627281a8dbSDavid Daney 	struct kvm_irq_level irq_level;
637281a8dbSDavid Daney 	int ret;
647281a8dbSDavid Daney 
657281a8dbSDavid Daney 	irq_level.irq = irq;
667281a8dbSDavid Daney 	irq_level.level = level ? 1 : 0;
677281a8dbSDavid Daney 
687281a8dbSDavid Daney 	ret = ioctl(kvm->vm_fd, KVM_IRQ_LINE, &irq_level);
697281a8dbSDavid Daney 	if (ret < 0)
707281a8dbSDavid Daney 		die_perror("KVM_IRQ_LINE ioctl");
717281a8dbSDavid Daney }
727281a8dbSDavid Daney 
737281a8dbSDavid Daney void kvm__irq_trigger(struct kvm *kvm, int irq)
747281a8dbSDavid Daney {
757281a8dbSDavid Daney 	struct kvm_irq_level irq_level;
767281a8dbSDavid Daney 	int ret;
777281a8dbSDavid Daney 
787281a8dbSDavid Daney 	irq_level.irq = irq;
797281a8dbSDavid Daney 	irq_level.level = 1;
807281a8dbSDavid Daney 
817281a8dbSDavid Daney 	ret = ioctl(kvm->vm_fd, KVM_IRQ_LINE, &irq_level);
827281a8dbSDavid Daney 	if (ret < 0)
837281a8dbSDavid Daney 		die_perror("KVM_IRQ_LINE ioctl");
847281a8dbSDavid Daney }
857281a8dbSDavid Daney 
867281a8dbSDavid Daney void ioport__setup_arch(struct kvm *kvm)
877281a8dbSDavid Daney {
887281a8dbSDavid Daney }
897281a8dbSDavid Daney 
907281a8dbSDavid Daney bool kvm__arch_cpu_supports_vm(void)
917281a8dbSDavid Daney {
927281a8dbSDavid Daney 	return true;
937281a8dbSDavid Daney }
947281a8dbSDavid Daney bool kvm__load_firmware(struct kvm *kvm, const char *firmware_filename)
957281a8dbSDavid Daney {
967281a8dbSDavid Daney 	return false;
977281a8dbSDavid Daney }
987281a8dbSDavid Daney int kvm__arch_setup_firmware(struct kvm *kvm)
997281a8dbSDavid Daney {
1007281a8dbSDavid Daney 	return 0;
1017281a8dbSDavid Daney }
1027281a8dbSDavid Daney 
103*1ade2d7dSDavid Daney static void kvm__mips_install_cmdline(struct kvm *kvm)
104*1ade2d7dSDavid Daney {
105*1ade2d7dSDavid Daney 	char *p = kvm->ram_start;
106*1ade2d7dSDavid Daney 	u64 cmdline_offset = 0x2000;
107*1ade2d7dSDavid Daney 	u64 argv_start = 0x3000;
108*1ade2d7dSDavid Daney 	u64 argv_offset = argv_start;
109*1ade2d7dSDavid Daney 	u64 argc = 0;
110*1ade2d7dSDavid Daney 
111*1ade2d7dSDavid Daney 	sprintf(p + cmdline_offset, "mem=0x%llx@0 ",
112*1ade2d7dSDavid Daney 		 (unsigned long long)kvm->ram_size);
113*1ade2d7dSDavid Daney 
114*1ade2d7dSDavid Daney 	strcat(p + cmdline_offset, kvm->cfg.real_cmdline); /* maximum size is 2K */
115*1ade2d7dSDavid Daney 
116*1ade2d7dSDavid Daney 	while (p[cmdline_offset]) {
117*1ade2d7dSDavid Daney 		if (!isspace(p[cmdline_offset])) {
118*1ade2d7dSDavid Daney 			if (kvm->arch.is64bit) {
119*1ade2d7dSDavid Daney 				*(u64 *)(p + argv_offset) = 0xffffffff80000000ull + cmdline_offset;
120*1ade2d7dSDavid Daney 				argv_offset += sizeof(u64);
121*1ade2d7dSDavid Daney 			} else {
122*1ade2d7dSDavid Daney 				*(u32 *)(p + argv_offset) = 0x80000000u + cmdline_offset;
123*1ade2d7dSDavid Daney 				argv_offset += sizeof(u32);
124*1ade2d7dSDavid Daney 			}
125*1ade2d7dSDavid Daney 			argc++;
126*1ade2d7dSDavid Daney 			while(p[cmdline_offset] && !isspace(p[cmdline_offset]))
127*1ade2d7dSDavid Daney 				cmdline_offset++;
128*1ade2d7dSDavid Daney 			continue;
129*1ade2d7dSDavid Daney 		}
130*1ade2d7dSDavid Daney 		/* Must be a space character skip over these*/
131*1ade2d7dSDavid Daney 		while(p[cmdline_offset] && isspace(p[cmdline_offset])) {
132*1ade2d7dSDavid Daney 			p[cmdline_offset] = 0;
133*1ade2d7dSDavid Daney 			cmdline_offset++;
134*1ade2d7dSDavid Daney 		}
135*1ade2d7dSDavid Daney 	}
136*1ade2d7dSDavid Daney 	kvm->arch.argc = argc;
137*1ade2d7dSDavid Daney 	kvm->arch.argv = 0xffffffff80000000ull + argv_start;
138*1ade2d7dSDavid Daney }
139*1ade2d7dSDavid Daney 
1407281a8dbSDavid Daney /* Load at the 1M point. */
1417281a8dbSDavid Daney #define KERNEL_LOAD_ADDR 0x1000000
1427281a8dbSDavid Daney int load_flat_binary(struct kvm *kvm, int fd_kernel, int fd_initrd, const char *kernel_cmdline)
1437281a8dbSDavid Daney {
1447281a8dbSDavid Daney 	void *p;
1457281a8dbSDavid Daney 	void *k_start;
1467281a8dbSDavid Daney 	int nr;
1477281a8dbSDavid Daney 
1487281a8dbSDavid Daney 	if (lseek(fd_kernel, 0, SEEK_SET) < 0)
1497281a8dbSDavid Daney 		die_perror("lseek");
1507281a8dbSDavid Daney 
1517281a8dbSDavid Daney 	p = k_start = guest_flat_to_host(kvm, KERNEL_LOAD_ADDR);
1527281a8dbSDavid Daney 
1537281a8dbSDavid Daney 	while ((nr = read(fd_kernel, p, 65536)) > 0)
1547281a8dbSDavid Daney 		p += nr;
1557281a8dbSDavid Daney 
1567281a8dbSDavid Daney 	kvm->arch.is64bit = true;
1577281a8dbSDavid Daney 	kvm->arch.entry_point = 0xffffffff81000000ull;
1587281a8dbSDavid Daney 
1597281a8dbSDavid Daney 	pr_info("Loaded kernel to 0x%x (%ld bytes)", KERNEL_LOAD_ADDR, (long int)(p - k_start));
1607281a8dbSDavid Daney 
1617281a8dbSDavid Daney 	return true;
1627281a8dbSDavid Daney }
1637281a8dbSDavid Daney 
164*1ade2d7dSDavid Daney struct kvm__arch_elf_info {
165*1ade2d7dSDavid Daney 	u64 load_addr;
166*1ade2d7dSDavid Daney 	u64 entry_point;
167*1ade2d7dSDavid Daney 	size_t len;
168*1ade2d7dSDavid Daney 	size_t offset;
169*1ade2d7dSDavid Daney };
170*1ade2d7dSDavid Daney 
171*1ade2d7dSDavid Daney static bool kvm__arch_get_elf_64_info(Elf64_Ehdr *ehdr, int fd_kernel,
172*1ade2d7dSDavid Daney 				      struct kvm__arch_elf_info *ei)
173*1ade2d7dSDavid Daney {
174*1ade2d7dSDavid Daney 	int i;
175*1ade2d7dSDavid Daney 	size_t nr;
176*1ade2d7dSDavid Daney 	Elf64_Phdr phdr;
177*1ade2d7dSDavid Daney 
178*1ade2d7dSDavid Daney 	if (ehdr->e_phentsize != sizeof(phdr)) {
179*1ade2d7dSDavid Daney 		pr_info("Incompatible ELF PHENTSIZE %d", ehdr->e_phentsize);
180*1ade2d7dSDavid Daney 		return false;
181*1ade2d7dSDavid Daney 	}
182*1ade2d7dSDavid Daney 
183*1ade2d7dSDavid Daney 	ei->entry_point = ehdr->e_entry;
184*1ade2d7dSDavid Daney 
185*1ade2d7dSDavid Daney 	if (lseek(fd_kernel, ehdr->e_phoff, SEEK_SET) < 0)
186*1ade2d7dSDavid Daney 		die_perror("lseek");
187*1ade2d7dSDavid Daney 
188*1ade2d7dSDavid Daney 	phdr.p_type = PT_NULL;
189*1ade2d7dSDavid Daney 	for (i = 0; i < ehdr->e_phnum; i++) {
190*1ade2d7dSDavid Daney 		nr = read(fd_kernel, &phdr, sizeof(phdr));
191*1ade2d7dSDavid Daney 		if (nr != sizeof(phdr)) {
192*1ade2d7dSDavid Daney 			pr_info("Couldn't read %d bytes for ELF PHDR.", (int)sizeof(phdr));
193*1ade2d7dSDavid Daney 			return false;
194*1ade2d7dSDavid Daney 		}
195*1ade2d7dSDavid Daney 		if (phdr.p_type == PT_LOAD)
196*1ade2d7dSDavid Daney 			break;
197*1ade2d7dSDavid Daney 	}
198*1ade2d7dSDavid Daney 	if (phdr.p_type != PT_LOAD) {
199*1ade2d7dSDavid Daney 		pr_info("No PT_LOAD Program Header found.");
200*1ade2d7dSDavid Daney 		return false;
201*1ade2d7dSDavid Daney 	}
202*1ade2d7dSDavid Daney 
203*1ade2d7dSDavid Daney 	ei->load_addr = phdr.p_paddr;
204*1ade2d7dSDavid Daney 
205*1ade2d7dSDavid Daney 	if ((ei->load_addr & 0xffffffffc0000000ull) == 0xffffffff80000000ull)
206*1ade2d7dSDavid Daney 		ei->load_addr &= 0x1ffffffful; /* Convert KSEG{0,1} to physical. */
207*1ade2d7dSDavid Daney 	if ((ei->load_addr & 0xc000000000000000ull) == 0x8000000000000000ull)
208*1ade2d7dSDavid Daney 		ei->load_addr &= 0x07ffffffffffffffull; /* Convert XKPHYS to pysical */
209*1ade2d7dSDavid Daney 
210*1ade2d7dSDavid Daney 
211*1ade2d7dSDavid Daney 	ei->len = phdr.p_filesz;
212*1ade2d7dSDavid Daney 	ei->offset = phdr.p_offset;
213*1ade2d7dSDavid Daney 
214*1ade2d7dSDavid Daney 	return true;
215*1ade2d7dSDavid Daney }
216*1ade2d7dSDavid Daney 
217*1ade2d7dSDavid Daney static bool kvm__arch_get_elf_32_info(Elf32_Ehdr *ehdr, int fd_kernel,
218*1ade2d7dSDavid Daney 				      struct kvm__arch_elf_info *ei)
219*1ade2d7dSDavid Daney {
220*1ade2d7dSDavid Daney 	int i;
221*1ade2d7dSDavid Daney 	size_t nr;
222*1ade2d7dSDavid Daney 	Elf32_Phdr phdr;
223*1ade2d7dSDavid Daney 
224*1ade2d7dSDavid Daney 	if (ehdr->e_phentsize != sizeof(phdr)) {
225*1ade2d7dSDavid Daney 		pr_info("Incompatible ELF PHENTSIZE %d", ehdr->e_phentsize);
226*1ade2d7dSDavid Daney 		return false;
227*1ade2d7dSDavid Daney 	}
228*1ade2d7dSDavid Daney 
229*1ade2d7dSDavid Daney 	ei->entry_point = (s64)((s32)ehdr->e_entry);
230*1ade2d7dSDavid Daney 
231*1ade2d7dSDavid Daney 	if (lseek(fd_kernel, ehdr->e_phoff, SEEK_SET) < 0)
232*1ade2d7dSDavid Daney 		die_perror("lseek");
233*1ade2d7dSDavid Daney 
234*1ade2d7dSDavid Daney 	phdr.p_type = PT_NULL;
235*1ade2d7dSDavid Daney 	for (i = 0; i < ehdr->e_phnum; i++) {
236*1ade2d7dSDavid Daney 		nr = read(fd_kernel, &phdr, sizeof(phdr));
237*1ade2d7dSDavid Daney 		if (nr != sizeof(phdr)) {
238*1ade2d7dSDavid Daney 			pr_info("Couldn't read %d bytes for ELF PHDR.", (int)sizeof(phdr));
239*1ade2d7dSDavid Daney 			return false;
240*1ade2d7dSDavid Daney 		}
241*1ade2d7dSDavid Daney 		if (phdr.p_type == PT_LOAD)
242*1ade2d7dSDavid Daney 			break;
243*1ade2d7dSDavid Daney 	}
244*1ade2d7dSDavid Daney 	if (phdr.p_type != PT_LOAD) {
245*1ade2d7dSDavid Daney 		pr_info("No PT_LOAD Program Header found.");
246*1ade2d7dSDavid Daney 		return false;
247*1ade2d7dSDavid Daney 	}
248*1ade2d7dSDavid Daney 
249*1ade2d7dSDavid Daney 	ei->load_addr = (s64)((s32)phdr.p_paddr);
250*1ade2d7dSDavid Daney 
251*1ade2d7dSDavid Daney 	if ((ei->load_addr & 0xffffffffc0000000ull) == 0xffffffff80000000ull)
252*1ade2d7dSDavid Daney 		ei->load_addr &= 0x1fffffffull; /* Convert KSEG{0,1} to physical. */
253*1ade2d7dSDavid Daney 
254*1ade2d7dSDavid Daney 	ei->len = phdr.p_filesz;
255*1ade2d7dSDavid Daney 	ei->offset = phdr.p_offset;
256*1ade2d7dSDavid Daney 
257*1ade2d7dSDavid Daney 	return true;
258*1ade2d7dSDavid Daney }
259*1ade2d7dSDavid Daney 
260*1ade2d7dSDavid Daney int load_elf_binary(struct kvm *kvm, int fd_kernel, int fd_initrd, const char *kernel_cmdline)
261*1ade2d7dSDavid Daney {
262*1ade2d7dSDavid Daney 	union {
263*1ade2d7dSDavid Daney 		Elf64_Ehdr ehdr;
264*1ade2d7dSDavid Daney 		Elf32_Ehdr ehdr32;
265*1ade2d7dSDavid Daney 	} eh;
266*1ade2d7dSDavid Daney 
267*1ade2d7dSDavid Daney 	size_t nr;
268*1ade2d7dSDavid Daney 	char *p;
269*1ade2d7dSDavid Daney 	struct kvm__arch_elf_info ei;
270*1ade2d7dSDavid Daney 
271*1ade2d7dSDavid Daney 	if (lseek(fd_kernel, 0, SEEK_SET) < 0)
272*1ade2d7dSDavid Daney 		die_perror("lseek");
273*1ade2d7dSDavid Daney 
274*1ade2d7dSDavid Daney 	nr = read(fd_kernel, &eh, sizeof(eh));
275*1ade2d7dSDavid Daney 	if (nr != sizeof(eh)) {
276*1ade2d7dSDavid Daney 		pr_info("Couldn't read %d bytes for ELF header.", (int)sizeof(eh));
277*1ade2d7dSDavid Daney 		return false;
278*1ade2d7dSDavid Daney 	}
279*1ade2d7dSDavid Daney 
280*1ade2d7dSDavid Daney 	if (eh.ehdr.e_ident[EI_MAG0] != ELFMAG0 ||
281*1ade2d7dSDavid Daney 	    eh.ehdr.e_ident[EI_MAG1] != ELFMAG1 ||
282*1ade2d7dSDavid Daney 	    eh.ehdr.e_ident[EI_MAG2] != ELFMAG2 ||
283*1ade2d7dSDavid Daney 	    eh.ehdr.e_ident[EI_MAG3] != ELFMAG3 ||
284*1ade2d7dSDavid Daney 	    (eh.ehdr.e_ident[EI_CLASS] != ELFCLASS64 && eh.ehdr.e_ident[EI_CLASS] != ELFCLASS32) ||
285*1ade2d7dSDavid Daney 	    eh.ehdr.e_ident[EI_VERSION] != EV_CURRENT) {
286*1ade2d7dSDavid Daney 		pr_info("Incompatible ELF header.");
287*1ade2d7dSDavid Daney 		return false;
288*1ade2d7dSDavid Daney 	}
289*1ade2d7dSDavid Daney 	if (eh.ehdr.e_type != ET_EXEC || eh.ehdr.e_machine != EM_MIPS) {
290*1ade2d7dSDavid Daney 		pr_info("Incompatible ELF not MIPS EXEC.");
291*1ade2d7dSDavid Daney 		return false;
292*1ade2d7dSDavid Daney 	}
293*1ade2d7dSDavid Daney 
294*1ade2d7dSDavid Daney 	if (eh.ehdr.e_ident[EI_CLASS] == ELFCLASS64) {
295*1ade2d7dSDavid Daney 		if (!kvm__arch_get_elf_64_info(&eh.ehdr, fd_kernel, &ei))
296*1ade2d7dSDavid Daney 			return false;
297*1ade2d7dSDavid Daney 		kvm->arch.is64bit = true;
298*1ade2d7dSDavid Daney 	} else {
299*1ade2d7dSDavid Daney 		if (!kvm__arch_get_elf_32_info(&eh.ehdr32, fd_kernel, &ei))
300*1ade2d7dSDavid Daney 			return false;
301*1ade2d7dSDavid Daney 		kvm->arch.is64bit = false;
302*1ade2d7dSDavid Daney 	}
303*1ade2d7dSDavid Daney 
304*1ade2d7dSDavid Daney 	kvm->arch.entry_point = ei.entry_point;
305*1ade2d7dSDavid Daney 
306*1ade2d7dSDavid Daney 	if (lseek(fd_kernel, ei.offset, SEEK_SET) < 0)
307*1ade2d7dSDavid Daney 		die_perror("lseek");
308*1ade2d7dSDavid Daney 
309*1ade2d7dSDavid Daney 	p = guest_flat_to_host(kvm, ei.load_addr);
310*1ade2d7dSDavid Daney 
311*1ade2d7dSDavid Daney 	pr_info("ELF Loading 0x%lx bytes from 0x%llx to 0x%llx",
312*1ade2d7dSDavid Daney 		(unsigned long)ei.len, (unsigned long long)ei.offset, (unsigned long long)ei.load_addr);
313*1ade2d7dSDavid Daney 	do {
314*1ade2d7dSDavid Daney 		nr = read(fd_kernel, p, ei.len);
315*1ade2d7dSDavid Daney 		if (nr < 0)
316*1ade2d7dSDavid Daney 			die_perror("read");
317*1ade2d7dSDavid Daney 		p += nr;
318*1ade2d7dSDavid Daney 		ei.len -= nr;
319*1ade2d7dSDavid Daney 	} while (ei.len);
320*1ade2d7dSDavid Daney 
321*1ade2d7dSDavid Daney 	kvm__mips_install_cmdline(kvm);
322*1ade2d7dSDavid Daney 
323*1ade2d7dSDavid Daney 	return true;
324*1ade2d7dSDavid Daney }
325*1ade2d7dSDavid Daney 
3267281a8dbSDavid Daney void ioport__map_irq(u8 *irq)
3277281a8dbSDavid Daney {
3287281a8dbSDavid Daney }
329