]> err.no Git - linux-2.6/blob - drivers/lguest/lguest_user.c
lguest: documentation I: Preparation
[linux-2.6] / drivers / lguest / lguest_user.c
1 /*P:200 This contains all the /dev/lguest code, whereby the userspace launcher
2  * controls and communicates with the Guest.  For example, the first write will
3  * tell us the memory size, pagetable, entry point and kernel address offset.
4  * A read will run the Guest until a signal is pending (-EINTR), or the Guest
5  * does a DMA out to the Launcher.  Writes are also used to get a DMA buffer
6  * registered by the Guest and to send the Guest an interrupt. :*/
7 #include <linux/uaccess.h>
8 #include <linux/miscdevice.h>
9 #include <linux/fs.h>
10 #include "lg.h"
11
12 static void setup_regs(struct lguest_regs *regs, unsigned long start)
13 {
14         /* Write out stack in format lguest expects, so we can switch to it. */
15         regs->ds = regs->es = regs->ss = __KERNEL_DS|GUEST_PL;
16         regs->cs = __KERNEL_CS|GUEST_PL;
17         regs->eflags = 0x202;   /* Interrupts enabled. */
18         regs->eip = start;
19         /* esi points to our boot information (physical address 0) */
20 }
21
22 /* + addr */
23 static long user_get_dma(struct lguest *lg, const u32 __user *input)
24 {
25         unsigned long key, udma, irq;
26
27         if (get_user(key, input) != 0)
28                 return -EFAULT;
29         udma = get_dma_buffer(lg, key, &irq);
30         if (!udma)
31                 return -ENOENT;
32
33         /* We put irq number in udma->used_len. */
34         lgwrite_u32(lg, udma + offsetof(struct lguest_dma, used_len), irq);
35         return udma;
36 }
37
38 /* To force the Guest to stop running and return to the Launcher, the
39  * Waker sets writes LHREQ_BREAK and the value "1" to /dev/lguest.  The
40  * Launcher then writes LHREQ_BREAK and "0" to release the Waker. */
41 static int break_guest_out(struct lguest *lg, const u32 __user *input)
42 {
43         unsigned long on;
44
45         /* Fetch whether they're turning break on or off.. */
46         if (get_user(on, input) != 0)
47                 return -EFAULT;
48
49         if (on) {
50                 lg->break_out = 1;
51                 /* Pop it out (may be running on different CPU) */
52                 wake_up_process(lg->tsk);
53                 /* Wait for them to reset it */
54                 return wait_event_interruptible(lg->break_wq, !lg->break_out);
55         } else {
56                 lg->break_out = 0;
57                 wake_up(&lg->break_wq);
58                 return 0;
59         }
60 }
61
62 /* + irq */
63 static int user_send_irq(struct lguest *lg, const u32 __user *input)
64 {
65         u32 irq;
66
67         if (get_user(irq, input) != 0)
68                 return -EFAULT;
69         if (irq >= LGUEST_IRQS)
70                 return -EINVAL;
71         set_bit(irq, lg->irqs_pending);
72         return 0;
73 }
74
75 static ssize_t read(struct file *file, char __user *user, size_t size,loff_t*o)
76 {
77         struct lguest *lg = file->private_data;
78
79         if (!lg)
80                 return -EINVAL;
81
82         /* If you're not the task which owns the guest, go away. */
83         if (current != lg->tsk)
84                 return -EPERM;
85
86         if (lg->dead) {
87                 size_t len;
88
89                 if (IS_ERR(lg->dead))
90                         return PTR_ERR(lg->dead);
91
92                 len = min(size, strlen(lg->dead)+1);
93                 if (copy_to_user(user, lg->dead, len) != 0)
94                         return -EFAULT;
95                 return len;
96         }
97
98         if (lg->dma_is_pending)
99                 lg->dma_is_pending = 0;
100
101         return run_guest(lg, (unsigned long __user *)user);
102 }
103
104 /* Take: pfnlimit, pgdir, start, pageoffset. */
105 static int initialize(struct file *file, const u32 __user *input)
106 {
107         struct lguest *lg;
108         int err, i;
109         u32 args[4];
110
111         /* We grab the Big Lguest lock, which protects the global array
112          * "lguests" and multiple simultaneous initializations. */
113         mutex_lock(&lguest_lock);
114
115         if (file->private_data) {
116                 err = -EBUSY;
117                 goto unlock;
118         }
119
120         if (copy_from_user(args, input, sizeof(args)) != 0) {
121                 err = -EFAULT;
122                 goto unlock;
123         }
124
125         i = find_free_guest();
126         if (i < 0) {
127                 err = -ENOSPC;
128                 goto unlock;
129         }
130         lg = &lguests[i];
131         lg->guestid = i;
132         lg->pfn_limit = args[0];
133         lg->page_offset = args[3];
134         lg->regs_page = get_zeroed_page(GFP_KERNEL);
135         if (!lg->regs_page) {
136                 err = -ENOMEM;
137                 goto release_guest;
138         }
139         lg->regs = (void *)lg->regs_page + PAGE_SIZE - sizeof(*lg->regs);
140
141         err = init_guest_pagetable(lg, args[1]);
142         if (err)
143                 goto free_regs;
144
145         setup_regs(lg->regs, args[2]);
146         setup_guest_gdt(lg);
147         init_clockdev(lg);
148         lg->tsk = current;
149         lg->mm = get_task_mm(lg->tsk);
150         init_waitqueue_head(&lg->break_wq);
151         lg->last_pages = NULL;
152         file->private_data = lg;
153
154         mutex_unlock(&lguest_lock);
155
156         return sizeof(args);
157
158 free_regs:
159         free_page(lg->regs_page);
160 release_guest:
161         memset(lg, 0, sizeof(*lg));
162 unlock:
163         mutex_unlock(&lguest_lock);
164         return err;
165 }
166
167 static ssize_t write(struct file *file, const char __user *input,
168                      size_t size, loff_t *off)
169 {
170         struct lguest *lg = file->private_data;
171         u32 req;
172
173         if (get_user(req, input) != 0)
174                 return -EFAULT;
175         input += sizeof(req);
176
177         if (req != LHREQ_INITIALIZE && !lg)
178                 return -EINVAL;
179         if (lg && lg->dead)
180                 return -ENOENT;
181
182         /* If you're not the task which owns the Guest, you can only break */
183         if (lg && current != lg->tsk && req != LHREQ_BREAK)
184                 return -EPERM;
185
186         switch (req) {
187         case LHREQ_INITIALIZE:
188                 return initialize(file, (const u32 __user *)input);
189         case LHREQ_GETDMA:
190                 return user_get_dma(lg, (const u32 __user *)input);
191         case LHREQ_IRQ:
192                 return user_send_irq(lg, (const u32 __user *)input);
193         case LHREQ_BREAK:
194                 return break_guest_out(lg, (const u32 __user *)input);
195         default:
196                 return -EINVAL;
197         }
198 }
199
200 static int close(struct inode *inode, struct file *file)
201 {
202         struct lguest *lg = file->private_data;
203
204         if (!lg)
205                 return 0;
206
207         mutex_lock(&lguest_lock);
208         /* Cancels the hrtimer set via LHCALL_SET_CLOCKEVENT. */
209         hrtimer_cancel(&lg->hrt);
210         release_all_dma(lg);
211         free_guest_pagetable(lg);
212         mmput(lg->mm);
213         if (!IS_ERR(lg->dead))
214                 kfree(lg->dead);
215         free_page(lg->regs_page);
216         memset(lg, 0, sizeof(*lg));
217         mutex_unlock(&lguest_lock);
218         return 0;
219 }
220
221 static struct file_operations lguest_fops = {
222         .owner   = THIS_MODULE,
223         .release = close,
224         .write   = write,
225         .read    = read,
226 };
227 static struct miscdevice lguest_dev = {
228         .minor  = MISC_DYNAMIC_MINOR,
229         .name   = "lguest",
230         .fops   = &lguest_fops,
231 };
232
233 int __init lguest_device_init(void)
234 {
235         return misc_register(&lguest_dev);
236 }
237
238 void __exit lguest_device_remove(void)
239 {
240         misc_deregister(&lguest_dev);
241 }