1
2
3
4
5
6#include <linux/module.h>
7#include <linux/stringify.h>
8#include <linux/stddef.h>
9#include <linux/io.h>
10#include <linux/mm.h>
11#include <linux/vmalloc.h>
12#include <linux/cpu.h>
13#include <linux/freezer.h>
14#include <linux/highmem.h>
15#include <linux/slab.h>
16#include <asm/paravirt.h>
17#include <asm/pgtable.h>
18#include <asm/uaccess.h>
19#include <asm/poll.h>
20#include <asm/asm-offsets.h>
21#include "lg.h"
22
23unsigned long switcher_addr;
24struct page **lg_switcher_pages;
25static struct vm_struct *switcher_text_vma;
26static struct vm_struct *switcher_stacks_vma;
27
28
29DEFINE_MUTEX(lguest_lock);
30
31
32
33
34
35
36
37
38
39
40
41
42
43static __init int map_switcher(void)
44{
45 int i, err;
46
47
48
49
50
51
52
53
54
55
56 if (end_switcher_text - start_switcher_text > PAGE_SIZE) {
57 printk(KERN_ERR "lguest: switcher text too large (%zu)\n",
58 end_switcher_text - start_switcher_text);
59 return -EINVAL;
60 }
61
62
63
64
65
66 lg_switcher_pages = kmalloc(sizeof(lg_switcher_pages[0])
67 * TOTAL_SWITCHER_PAGES,
68 GFP_KERNEL);
69 if (!lg_switcher_pages) {
70 err = -ENOMEM;
71 goto out;
72 }
73
74
75
76
77
78 for (i = 0; i < TOTAL_SWITCHER_PAGES; i++) {
79 lg_switcher_pages[i] = alloc_page(GFP_KERNEL|__GFP_ZERO);
80 if (!lg_switcher_pages[i]) {
81 err = -ENOMEM;
82 goto free_some_pages;
83 }
84 }
85
86
87
88
89
90 memcpy(kmap(lg_switcher_pages[0]), start_switcher_text,
91 end_switcher_text - start_switcher_text);
92 kunmap(lg_switcher_pages[0]);
93
94
95
96
97
98
99
100 switcher_addr = FIXADDR_START - TOTAL_SWITCHER_PAGES*PAGE_SIZE;
101
102
103
104
105
106
107
108
109 switcher_text_vma = __get_vm_area(PAGE_SIZE, VM_ALLOC|VM_NO_GUARD,
110 switcher_addr,
111 switcher_addr + PAGE_SIZE);
112
113 if (!switcher_text_vma) {
114 err = -ENOMEM;
115 printk("lguest: could not map switcher pages high\n");
116 goto free_pages;
117 }
118
119 switcher_stacks_vma = __get_vm_area(SWITCHER_STACK_PAGES * PAGE_SIZE,
120 VM_ALLOC|VM_NO_GUARD,
121 switcher_addr + PAGE_SIZE,
122 switcher_addr + TOTAL_SWITCHER_PAGES * PAGE_SIZE);
123 if (!switcher_stacks_vma) {
124 err = -ENOMEM;
125 printk("lguest: could not map switcher pages high\n");
126 goto free_text_vma;
127 }
128
129
130
131
132
133
134
135 err = map_vm_area(switcher_text_vma, PAGE_KERNEL_RX, lg_switcher_pages);
136 if (err) {
137 printk("lguest: text map_vm_area failed: %i\n", err);
138 goto free_vmas;
139 }
140
141 err = map_vm_area(switcher_stacks_vma, PAGE_KERNEL,
142 lg_switcher_pages + SWITCHER_TEXT_PAGES);
143 if (err) {
144 printk("lguest: stacks map_vm_area failed: %i\n", err);
145 goto free_vmas;
146 }
147
148
149
150
151 printk(KERN_INFO "lguest: mapped switcher at %p\n",
152 switcher_text_vma->addr);
153
154 return 0;
155
156free_vmas:
157
158 vunmap(switcher_stacks_vma->addr);
159free_text_vma:
160 vunmap(switcher_text_vma->addr);
161free_pages:
162 i = TOTAL_SWITCHER_PAGES;
163free_some_pages:
164 for (--i; i >= 0; i--)
165 __free_pages(lg_switcher_pages[i], 0);
166 kfree(lg_switcher_pages);
167out:
168 return err;
169}
170
171
172
173static void unmap_switcher(void)
174{
175 unsigned int i;
176
177
178 vunmap(switcher_text_vma->addr);
179 vunmap(switcher_stacks_vma->addr);
180
181 for (i = 0; i < TOTAL_SWITCHER_PAGES; i++)
182 __free_pages(lg_switcher_pages[i], 0);
183 kfree(lg_switcher_pages);
184}
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201bool lguest_address_ok(const struct lguest *lg,
202 unsigned long addr, unsigned long len)
203{
204 return addr+len <= lg->pfn_limit * PAGE_SIZE && (addr+len >= addr);
205}
206
207
208
209
210
211
212void __lgread(struct lg_cpu *cpu, void *b, unsigned long addr, unsigned bytes)
213{
214 if (!lguest_address_ok(cpu->lg, addr, bytes)
215 || copy_from_user(b, cpu->lg->mem_base + addr, bytes) != 0) {
216
217 memset(b, 0, bytes);
218 kill_guest(cpu, "bad read address %#lx len %u", addr, bytes);
219 }
220}
221
222
223void __lgwrite(struct lg_cpu *cpu, unsigned long addr, const void *b,
224 unsigned bytes)
225{
226 if (!lguest_address_ok(cpu->lg, addr, bytes)
227 || copy_to_user(cpu->lg->mem_base + addr, b, bytes) != 0)
228 kill_guest(cpu, "bad write address %#lx len %u", addr, bytes);
229}
230
231
232
233
234
235
236
237int run_guest(struct lg_cpu *cpu, unsigned long __user *user)
238{
239
240 if (cpu->reg_read) {
241 if (put_user(*cpu->reg_read, user))
242 return -EFAULT;
243 cpu->reg_read = NULL;
244 return sizeof(*cpu->reg_read);
245 }
246
247
248 while (!cpu->lg->dead) {
249 unsigned int irq;
250 bool more;
251
252
253 if (cpu->hcall)
254 do_hypercalls(cpu);
255
256
257 if (cpu->pending.trap) {
258 if (copy_to_user(user, &cpu->pending,
259 sizeof(cpu->pending)))
260 return -EFAULT;
261 return sizeof(cpu->pending);
262 }
263
264
265
266
267
268
269 try_to_freeze();
270
271
272 if (signal_pending(current))
273 return -ERESTARTSYS;
274
275
276
277
278
279
280 irq = interrupt_pending(cpu, &more);
281 if (irq < LGUEST_IRQS)
282 try_deliver_interrupt(cpu, irq, more);
283
284
285
286
287
288 if (cpu->lg->dead)
289 break;
290
291
292
293
294
295 if (cpu->halted) {
296 set_current_state(TASK_INTERRUPTIBLE);
297
298
299
300
301 if (interrupt_pending(cpu, &more) < LGUEST_IRQS)
302 set_current_state(TASK_RUNNING);
303 else
304 schedule();
305 continue;
306 }
307
308
309
310
311
312 local_irq_disable();
313
314
315 lguest_arch_run_guest(cpu);
316
317
318 local_irq_enable();
319
320
321 lguest_arch_handle_trap(cpu);
322 }
323
324
325 if (cpu->lg->dead == ERR_PTR(-ERESTART))
326 return -ERESTART;
327
328
329 return -ENOENT;
330}
331
332
333
334
335
336
337
338
339
340static int __init init(void)
341{
342 int err;
343
344
345 if (get_kernel_rpl() != 0) {
346 printk("lguest is afraid of being a guest\n");
347 return -EPERM;
348 }
349
350
351 err = map_switcher();
352 if (err)
353 goto out;
354
355
356 err = init_interrupts();
357 if (err)
358 goto unmap;
359
360
361 err = lguest_device_init();
362 if (err)
363 goto free_interrupts;
364
365
366 lguest_arch_host_init();
367
368
369 return 0;
370
371free_interrupts:
372 free_interrupts();
373unmap:
374 unmap_switcher();
375out:
376 return err;
377}
378
379
380static void __exit fini(void)
381{
382 lguest_device_remove();
383 free_interrupts();
384 unmap_switcher();
385
386 lguest_arch_host_fini();
387}
388
389
390
391
392
393
394module_init(init);
395module_exit(fini);
396MODULE_LICENSE("GPL");
397MODULE_AUTHOR("Rusty Russell <rusty@rustcorp.com.au>");
398