xref: /kvmtool/riscv/kvm.c (revision 0dff350174f8da2b65dd43a3e569e5377f4ee906)
1 #include "kvm/kvm.h"
2 #include "kvm/util.h"
3 #include "kvm/8250-serial.h"
4 #include "kvm/virtio-console.h"
5 #include "kvm/fdt.h"
6 
7 #include <linux/kernel.h>
8 #include <linux/kvm.h>
9 #include <linux/sizes.h>
10 
11 struct kvm_ext kvm_req_ext[] = {
12 	{ DEFINE_KVM_EXT(KVM_CAP_ONE_REG) },
13 	{ 0, 0 },
14 };
15 
kvm__arch_default_ram_address(void)16 u64 kvm__arch_default_ram_address(void)
17 {
18 	return RISCV_RAM;
19 }
20 
kvm__arch_validate_cfg(struct kvm * kvm)21 void kvm__arch_validate_cfg(struct kvm *kvm)
22 {
23 }
24 
kvm__arch_cpu_supports_vm(void)25 bool kvm__arch_cpu_supports_vm(void)
26 {
27 	/* The KVM capability check is enough. */
28 	return true;
29 }
30 
kvm__init_ram(struct kvm * kvm)31 void kvm__init_ram(struct kvm *kvm)
32 {
33 	int err;
34 	u64 phys_start, phys_size;
35 	void *host_mem;
36 
37 	phys_start	= RISCV_RAM;
38 	phys_size	= kvm->ram_size;
39 	host_mem	= kvm->ram_start;
40 
41 	err = kvm__register_ram(kvm, phys_start, phys_size, host_mem);
42 	if (err)
43 		die("Failed to register %lld bytes of memory at physical "
44 		    "address 0x%llx [err %d]", phys_size, phys_start, err);
45 
46 	kvm->arch.memory_guest_start = phys_start;
47 }
48 
kvm__arch_delete_ram(struct kvm * kvm)49 void kvm__arch_delete_ram(struct kvm *kvm)
50 {
51 	munmap(kvm->arch.ram_alloc_start, kvm->arch.ram_alloc_size);
52 }
53 
kvm__arch_read_term(struct kvm * kvm)54 void kvm__arch_read_term(struct kvm *kvm)
55 {
56 	serial8250__update_consoles(kvm);
57 	virtio_console__inject_interrupt(kvm);
58 }
59 
kvm__arch_set_cmdline(char * cmdline,bool video)60 void kvm__arch_set_cmdline(char *cmdline, bool video)
61 {
62 }
63 
64 #if __riscv_xlen == 64
65 #define HUGEPAGE_SIZE	SZ_2M
66 #else
67 #define HUGEPAGE_SIZE	SZ_4M
68 #endif
69 
kvm__arch_init(struct kvm * kvm)70 void kvm__arch_init(struct kvm *kvm)
71 {
72 	/*
73 	 * Allocate guest memory. We must align our buffer to 64K to
74 	 * correlate with the maximum guest page size for virtio-mmio.
75 	 * If using THP, then our minimal alignment becomes hugepage
76 	 * size. The hugepage size is always greater than 64K, so
77 	 * let's go with that.
78 	 */
79 	kvm->ram_size = min(kvm->cfg.ram_size, (u64)RISCV_MAX_MEMORY(kvm));
80 	kvm->arch.ram_alloc_size = kvm->ram_size;
81 	if (!kvm->cfg.hugetlbfs_path)
82 		kvm->arch.ram_alloc_size += HUGEPAGE_SIZE;
83 	kvm->arch.ram_alloc_start = mmap_anon_or_hugetlbfs(kvm,
84 						kvm->cfg.hugetlbfs_path,
85 						kvm->arch.ram_alloc_size);
86 
87 	if (kvm->arch.ram_alloc_start == MAP_FAILED)
88 		die("Failed to map %lld bytes for guest memory (%d)",
89 		    kvm->arch.ram_alloc_size, errno);
90 
91 	kvm->ram_start = (void *)ALIGN((unsigned long)kvm->arch.ram_alloc_start,
92 					SZ_2M);
93 
94 	madvise(kvm->arch.ram_alloc_start, kvm->arch.ram_alloc_size,
95 		MADV_MERGEABLE);
96 
97 	madvise(kvm->arch.ram_alloc_start, kvm->arch.ram_alloc_size,
98 		MADV_HUGEPAGE);
99 
100 	riscv__irqchip_create(kvm);
101 }
102 
103 #define FDT_ALIGN	SZ_4M
104 #define INITRD_ALIGN	8
kvm__arch_load_kernel_image(struct kvm * kvm,int fd_kernel,int fd_initrd,const char * kernel_cmdline)105 bool kvm__arch_load_kernel_image(struct kvm *kvm, int fd_kernel, int fd_initrd,
106 				 const char *kernel_cmdline)
107 {
108 	void *pos, *kernel_end, *limit;
109 	unsigned long guest_addr, kernel_offset;
110 	ssize_t file_size;
111 
112 	/*
113 	 * Linux requires the initrd and dtb to be mapped inside lowmem,
114 	 * so we can't just place them at the top of memory.
115 	 */
116 	limit = kvm->ram_start + min(kvm->ram_size, (u64)SZ_256M) - 1;
117 
118 #if __riscv_xlen == 64
119 	/* Linux expects to be booted at 2M boundary for RV64 */
120 	kernel_offset = 0x200000;
121 #else
122 	/* Linux expects to be booted at 4M boundary for RV32 */
123 	kernel_offset = 0x400000;
124 #endif
125 
126 	pos = kvm->ram_start + kernel_offset;
127 	kvm->arch.kern_guest_start = host_to_guest_flat(kvm, pos);
128 	file_size = read_file(fd_kernel, pos, limit - pos);
129 	if (file_size < 0) {
130 		if (errno == ENOMEM)
131 			die("kernel image too big to fit in guest memory.");
132 
133 		die_perror("kernel read");
134 	}
135 	kernel_end = pos + file_size;
136 	pr_debug("Loaded kernel to 0x%llx (%zd bytes)",
137 		 kvm->arch.kern_guest_start, file_size);
138 
139 	/* Place FDT just after kernel at FDT_ALIGN address */
140 	pos = kernel_end + FDT_ALIGN;
141 	guest_addr = ALIGN(host_to_guest_flat(kvm, pos), FDT_ALIGN);
142 	pos = guest_flat_to_host(kvm, guest_addr);
143 	if (pos < kernel_end)
144 		die("fdt overlaps with kernel image.");
145 
146 	kvm->arch.dtb_guest_start = guest_addr;
147 	pr_debug("Placing fdt at 0x%llx - 0x%llx",
148 		 kvm->arch.dtb_guest_start,
149 		 host_to_guest_flat(kvm, limit));
150 
151 	/* ... and finally the initrd, if we have one. */
152 	if (fd_initrd != -1) {
153 		struct stat sb;
154 		unsigned long initrd_start;
155 
156 		if (fstat(fd_initrd, &sb))
157 			die_perror("fstat");
158 
159 		pos = limit - (sb.st_size + INITRD_ALIGN);
160 		guest_addr = ALIGN(host_to_guest_flat(kvm, pos), INITRD_ALIGN);
161 		pos = guest_flat_to_host(kvm, guest_addr);
162 		if (pos < kernel_end)
163 			die("initrd overlaps with kernel image.");
164 
165 		initrd_start = guest_addr;
166 		file_size = read_file(fd_initrd, pos, limit - pos);
167 		if (file_size == -1) {
168 			if (errno == ENOMEM)
169 				die("initrd too big to fit in guest memory.");
170 
171 			die_perror("initrd read");
172 		}
173 
174 		kvm->arch.initrd_guest_start = initrd_start;
175 		kvm->arch.initrd_size = file_size;
176 		pr_debug("Loaded initrd to 0x%llx (%llu bytes)",
177 			 kvm->arch.initrd_guest_start,
178 			 kvm->arch.initrd_size);
179 	} else {
180 		kvm->arch.initrd_size = 0;
181 	}
182 
183 	return true;
184 }
185 
kvm__load_firmware(struct kvm * kvm,const char * firmware_filename)186 bool kvm__load_firmware(struct kvm *kvm, const char *firmware_filename)
187 {
188 	/* TODO: Firmware loading to be supported later. */
189 	return false;
190 }
191 
kvm__arch_setup_firmware(struct kvm * kvm)192 int kvm__arch_setup_firmware(struct kvm *kvm)
193 {
194 	return 0;
195 }
196