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