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