Fix couple of problems in compat code.
[akaros.git] / kern / drivers / net / udrvr / compat.c
1 /*
2  * Copyright (c) 2016 Google Inc
3  * Author: Kanoj Sarcar <kanoj@google.com>
4  * See LICENSE for details.
5  */
6
7 #include <err.h>
8 #include <kmalloc.h>
9 #include <kref.h>
10 #include <string.h>
11 #include <stdio.h>
12 #include <assert.h>
13 #include <error.h>
14 #include <pmap.h>
15 #include <smp.h>
16 #include <devfs.h>
17 #include <linux/rdma/ib_user_verbs.h>
18 #include "uverbs.h"
19
20 /*
21  * Our version knocked off from kern/src/mm.c version + uncaching logic from
22  * vmap_pmem_nocache().
23  */
24 int map_upage_at_addr(struct proc *p, physaddr_t paddr, uintptr_t addr, int pteprot, int dolock)
25 {
26         pte_t   pte;
27         int     rv = -1;
28
29         spin_lock(&p->pte_lock);
30
31         pte = pgdir_walk(p->env_pgdir, (void*)addr, TRUE);
32
33         if (!pte_walk_okay(pte))
34                 goto err1;
35         pte_write(pte, paddr, pteprot);
36         // tlbflush(); tlb_flush_global();
37         rv = 0;
38 err1:
39         spin_unlock(&p->pte_lock);
40
41         /*
42          * TODO: @mm tear down, unmap_and_destroy_vmrs():__vmr_free_pgs()
43          * decrefs page, which is a problem. 1st level workaround is to set
44          * PG_LOCKED/PG_PAGEMAP to avoid that. Not proud of myself.
45          */
46         if ((rv == 0) && (dolock == 1))
47                 atomic_set(&pa2page(paddr)->pg_flags, PG_LOCKED | PG_PAGEMAP);
48
49         return rv;
50 }
51
52 void set_page_dirty_lock(struct page *pagep)
53 {
54         atomic_or(&pagep->pg_flags, PG_DIRTY);
55 }
56
57 /*
58  * get_user_pages() does not grab a page ref count. Thus, put_page()
59  * can not release page ref count.
60  */
61 void put_page(struct page *pagep)
62 {
63         /* page_decref(pagep) / __put_page(pagep) */
64 }
65
66 int get_user_page(struct proc *p, unsigned long uvastart, int write, int force,
67     struct page **plist)
68 {
69         pte_t   pte;
70         int     ret = -1;
71
72         spin_lock(&p->pte_lock);
73
74         pte = pgdir_walk(p->env_pgdir, (void*)uvastart, TRUE);
75
76         if (!pte_walk_okay(pte))
77                 goto err1;
78
79         if (!pte_is_present(pte)) {
80                 struct page *pp;
81                 unsigned long prot = PTE_P | PTE_U | PTE_A | PTE_W | PTE_D;
82 #if 0
83                 printk("[akaros]: get_user_page() uva=0x%llx pte absent\n",
84                     uvastart);
85 #endif
86                 /*
87                  * TODO: ok to allocate with pte_lock? "prot" needs to be
88                  * based on VMR writability, refer to pgprot_noncached().
89                  */
90                 if (upage_alloc(p, &pp, 0))
91                         goto err1;
92                 pte_write(pte, page2pa(pp), prot);
93         }
94
95         if (write && (!pte_has_perm_urw(pte))) {
96                 /* TODO: How is Linux using the "force" parameter */
97                 printk("[akaros]: get_user_page() uva=0x%llx pte ro\n",
98                     uvastart);
99                 goto err1;
100         }
101
102         plist[0] = pa2page(pte_get_paddr(pte));
103         ret = 1;
104 err1:
105         spin_unlock(&p->pte_lock);
106         return ret;
107 }
108
109 int sg_alloc_table(struct sg_table *ptr, unsigned int npages, gfp_t mask)
110 {
111         ptr->sgl = kmalloc((sizeof(struct scatterlist) * npages), mask);
112         ptr->nents = ptr->orig_nents = npages;
113         sg_init_table(ptr->sgl, npages);
114         return 0;
115 }
116
117 void sg_free_table(struct sg_table *ptr)
118 {
119         kfree(ptr->sgl);
120 }
121
122 void idr_remove(struct idr *idp, int id)
123 {
124         BUG_ON((id < 0) || (id >= MAXITEMS));
125         idp->values[id] = NULL;
126 }
127
128 void *idr_find(struct idr *idp, int id)
129 {
130         BUG_ON((id < 0) || (id >= MAXITEMS));
131         BUG_ON(idp->values[id] == NULL);
132         return idp->values[id];
133 }
134
135 int idr_alloc(struct idr *idp, void *ptr, int start, int end, gfp_t gfp_mask)
136 {
137         int     i;
138
139         /* We use values[] == NULL as an indicator that slot is free */
140         BUG_ON(ptr == NULL);
141
142         spin_lock_irqsave(&idp->lock, f);
143
144         for (i = 0; i < MAXITEMS; i++) {
145                 if (idp->values[i] == NULL) {
146                         idp->values[i] = ptr;
147                         goto done;
148                 }
149         }
150
151         i = -1;                 /* error return */
152
153 done:
154         spin_unlock_irqsave(&idp->lock);
155         return i;
156 }
157
158 /* START: Linux /sys support for lib/apps */
159
160 /* Callers must pass in null terminated strings */
161 static ssize_t sysfs_read(char __user *buf, size_t ucount, loff_t *pos,
162     char *src)
163 {
164         int             slen = strlen(src) + 1; /* + 1 for terminating null */
165         unsigned long   off = *pos, nb = slen - off;
166
167         if (off >= slen)
168                 return 0;
169
170         if (copy_to_user(buf, (src + off), nb))
171                 return -EFAULT;
172
173         *pos += nb;
174         return nb;
175 }
176
177 static ssize_t ib_api_ver_read(struct file *filp, char __user *buf,
178     size_t count, loff_t *pos)
179 {
180         char            src[4] = { 0, 0, 0, 0};
181
182         src[0] = '0' + IB_USER_VERBS_ABI_VERSION;
183
184         return sysfs_read(buf, count, pos, src);
185 }
186
187 static const struct file_operations ib_api_ver = {
188         .read   = ib_api_ver_read,
189         .open   = kfs_open,
190         .release= kfs_release,
191 };
192
193 void sysfs_init(void)
194 {
195         do_mkdir("/dev/infiniband", S_IRWXU | S_IRWXG | S_IRWXO);
196         do_mkdir("/sys", S_IRWXU | S_IRWXG | S_IRWXO);
197         do_mkdir("/sys/class", S_IRWXU | S_IRWXG | S_IRWXO);
198         do_mkdir("/sys/class/infiniband_verbs", S_IRWXU | S_IRWXG | S_IRWXO);
199         do_mkdir("/sys/class/infiniband", S_IRWXU | S_IRWXG | S_IRWXO);
200
201         make_device("/sys/class/infiniband_verbs/abi_version",
202                     S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH,
203                     __S_IFCHR, (struct file_operations *)&ib_api_ver);
204 }
205
206 static ssize_t dver_read(struct file *filp, char __user *buf,
207     size_t count, loff_t *pos)
208 {
209         struct ib_uverbs_device *uvp;
210         char            src[4] = { 0, 0, 0, 0};
211
212         uvp = (struct ib_uverbs_device *)get_fs_info(filp);
213         src[0] = '0' + uvp->ib_dev->uverbs_abi_ver;
214
215         return sysfs_read(buf, count, pos, src);
216 }
217
218 static ssize_t dname_read(struct file *filp, char __user *buf,
219     size_t count, loff_t *pos)
220 {
221         struct ib_uverbs_device *uvp;
222
223         uvp = (struct ib_uverbs_device *)get_fs_info(filp);
224         return sysfs_read(buf, count, pos, uvp->ib_dev->name);
225 }
226
227 static ssize_t ntype_read(struct file *filp, char __user *buf,
228     size_t count, loff_t *pos)
229 {
230         char    src[] = "1";
231
232         return sysfs_read(buf, count, pos, src);
233 }
234
235 static ssize_t ddev_read(struct file *filp, char __user *buf,
236     size_t count, loff_t *pos)
237 {
238         char    src[] = "0x1003";
239
240         return sysfs_read(buf, count, pos, src);
241 }
242
243 static ssize_t dven_read(struct file *filp, char __user *buf,
244     size_t count, loff_t *pos)
245 {
246         char    src[] = "0x15b3";
247
248         return sysfs_read(buf, count, pos, src);
249 }
250
251 static ssize_t vsd_read(struct file *filp, char __user *buf,
252     size_t count, loff_t *pos)
253 {
254         char    *src = "puma20_A1-10.2.3.0";
255
256         return sysfs_read(buf, count, pos, src);
257 }
258
259 static const struct file_operations dver_fops = {
260         .read   = dver_read,
261         .open   = kfs_open,
262         .release= kfs_release,
263 };
264
265 static const struct file_operations dname_fops = {
266         .read   = dname_read,
267         .open   = kfs_open,
268         .release= kfs_release,
269 };
270
271 static const struct file_operations ddev_fops = {
272         .read   = ddev_read,
273         .open   = kfs_open,
274         .release= kfs_release,
275 };
276
277 static const struct file_operations dven_fops = {
278         .read   = dven_read,
279         .open   = kfs_open,
280         .release= kfs_release,
281 };
282
283 static const struct file_operations ntype_fops = {
284         .read   = ntype_read,
285         .open   = kfs_open,
286         .release= kfs_release,
287 };
288
289 static const struct file_operations vsd_fops = {
290         .read   = vsd_read,
291         .open   = kfs_open,
292         .release= kfs_release,
293 };
294
295 void sysfs_create(int devnum, const struct file_operations *verb_fops,
296     void *ptr)
297 {
298         char            sysname[256] = "/sys/class/infiniband_verbs/uverbs0";
299         char            devname[] = "/dev/infiniband/uverbs0";
300         char            drvname[64] = "/sys/class/infiniband/";
301         int             sysnameidx = strlen(sysname), drvidx;
302         struct file     *fp;
303         struct ib_uverbs_device *uvp = (struct ib_uverbs_device *)ptr;
304
305         /* Create correct name */
306         if (devnum > 9)
307                 panic("Too many devs");
308         devname[strlen(devname) - 1] = '0' + devnum;
309         sysname[sysnameidx - 1] = '0' + devnum;
310
311         /* Foll fops need to come from caller */
312         fp = make_device(devname,
313             S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH,
314             __S_IFCHR, (struct file_operations *)verb_fops);
315         set_fs_info(fp, ptr);
316
317         /* /sys/class/infiniband/mlx4_0 */
318         strncpy((drvname + strlen(drvname)), uvp->ib_dev->name, 12);
319         do_mkdir(drvname, S_IRWXU | S_IRWXG | S_IRWXO);
320         drvidx = strlen(drvname);
321
322         /* /sys/class/infiniband/mlx4_0/node_type */
323         strncpy(drvname + drvidx, "/node_type", 11);
324         make_device(drvname,
325             S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH,
326             __S_IFCHR, (struct file_operations *)&ntype_fops);
327
328         /* /sys/class/infiniband/mlx4_0/vsd */
329         strncpy(drvname + drvidx, "/vsd", 5);
330         fp = make_device(drvname,
331             S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH,
332             __S_IFCHR, (struct file_operations *)&vsd_fops);
333         set_fs_info(fp, ptr);
334
335         /* /sys/class/infiniband_verbs/uverbs0 */
336         do_mkdir(sysname, S_IRWXU | S_IRWXG | S_IRWXO);
337
338         /* /sys/class/infiniband_verbs/uverbs0/device */
339         strncpy(sysname + sysnameidx, "/device", 16);
340         do_mkdir(sysname, S_IRWXU | S_IRWXG | S_IRWXO);
341
342         /* /sys/class/infiniband_verbs/uverbs0/device/device */
343         strncpy(sysname + sysnameidx, "/device/device", 16);
344         fp = make_device(sysname,
345             S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH,
346             __S_IFCHR, (struct file_operations *)&ddev_fops);
347         set_fs_info(fp, ptr);
348
349         /* /sys/class/infiniband_verbs/uverbs0/device/vendor */
350         strncpy(sysname + sysnameidx, "/device/vendor", 16);
351         fp = make_device(sysname,
352             S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH,
353             __S_IFCHR, (struct file_operations *)&dven_fops);
354         set_fs_info(fp, ptr);
355
356         /* /sys/class/infiniband_verbs/uverbs0/ibdev */
357         strncpy(sysname + sysnameidx, "/ibdev", 16);
358         fp = make_device(sysname,
359             S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH,
360             __S_IFCHR, (struct file_operations *)&dname_fops);
361         set_fs_info(fp, ptr);
362
363         /* /sys/class/infiniband_verbs/uverbs0/abi_version */
364         strncpy(sysname + sysnameidx, "/abi_version", 16);
365         fp = make_device(sysname,
366             S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH,
367             __S_IFCHR, (struct file_operations *)&dver_fops);
368         set_fs_info(fp, ptr);
369 }
370
371 /* END: Linux /sys support for lib/apps */
372
373 /* START: Support older version of libibverbs */
374
375 /* in_words and provider_in_words are in terms of 4-byte words, not 8-byte */
376 struct ib_uverbs_ex_cmd_hdr_compat {
377         __u16 provider_in_words;
378         __u16 provider_out_words;
379         __u32 cmd_hdr_reserved;
380         __u32 comp_mask;
381         /* __u32 dummy; */
382         __u64 response;
383         __u32 qp_handle;
384 };
385
386 static ssize_t compat_ex(struct ib_uverbs_file *file, size_t count,
387     const char __user *buf)
388 {
389         struct ib_uverbs_cmd_hdr hdr;
390         struct ib_uverbs_ex_cmd_hdr_compat ex_hdr;
391         struct ib_udata ucore;
392         struct ib_udata uhw;
393         __u32 command;
394         int err;
395         unsigned long   tmpbuf[16];
396         struct ib_uverbs_create_flow *ptr;
397
398         if (copy_from_user(&hdr, buf, sizeof hdr))
399                 return -EFAULT;
400
401         command = hdr.command & IB_USER_VERBS_CMD_COMMAND_MASK;
402         command -= 2;
403
404         if (command == IB_USER_VERBS_EX_CMD_DESTROY_FLOW) {
405                 INIT_UDATA_BUF_OR_NULL(&ucore, buf + 8, 0, 8, 0);
406                 err = ib_uverbs_ex_destroy_flow(file, &ucore, &uhw);
407                 goto next;
408         }
409
410         /*
411          * "struct ibv_create_flow" is 56 bytes, "struct ibv_kern_spec" is
412          * 48 bytes, so at a minimum we expect 56 + (n x 48), n >= 1.
413          */
414         if (count < 104)
415                 return -EINVAL;
416
417         if (copy_from_user(&ex_hdr, buf + sizeof(hdr), sizeof(ex_hdr)))
418                 return -EFAULT;
419
420         if ((hdr.in_words + ex_hdr.provider_in_words) * 4 != count)
421                 return -EINVAL;
422
423         if (ex_hdr.cmd_hdr_reserved)
424                 return -EINVAL;
425
426         if (ex_hdr.comp_mask)
427                 return -EINVAL;
428
429         if (ex_hdr.response) {
430                 if (!hdr.out_words && !ex_hdr.provider_out_words)
431                         return -EINVAL;
432
433                 if (!access_ok(VERIFY_WRITE,
434                                (void __user *) (unsigned long) ex_hdr.response,
435                                (hdr.out_words + ex_hdr.provider_out_words) * 4))
436                         return -EFAULT;
437         } else {
438                 if (hdr.out_words || ex_hdr.provider_out_words)
439                         return -EINVAL;
440         }
441
442         ptr = (struct ib_uverbs_create_flow *)tmpbuf;
443         ptr->comp_mask = 0;     /* user input already validated above */
444         ptr->qp_handle = ex_hdr.qp_handle;
445
446         if ((count-36) > 120)
447                 BUG();
448
449         /* Copy 16 bytes worth "struct ibv_kern_flow_attr" */
450         copy_from_user(&tmpbuf[1], buf+36, sizeof(struct ib_uverbs_flow_attr));
451
452         ptr->flow_attr.size -= 56;              /* Comes in as 96 = 56 + 40 */
453
454         /* Copy "struct ibv_kern_spec"s */
455         copy_from_user(&tmpbuf[3], buf+56, count-56);
456
457         /*
458          * Copy : count-56 "struct ibv_kern_spec"s,
459          * 16 bytes "struct ibv_kern_flow_attr", 16 bytes comp_mask/qp_handle.
460          */
461         copy_to_user((char __user *)buf, tmpbuf, count-24);
462
463         INIT_UDATA_BUF_OR_NULL(&ucore, buf,
464             (unsigned long) ex_hdr.response, count - 24,
465             hdr.out_words * 4);
466
467         err = ib_uverbs_ex_create_flow(file, &ucore, &uhw);
468
469 next:
470         if (err)
471                 return err;
472
473         return count;
474 }
475
476 static ssize_t compat(struct ib_uverbs_file *file, size_t count,
477     const char __user *buf)
478 {
479         unsigned long                   tmpbuf[17];
480         struct ib_uverbs_cmd_hdr        *p = (struct ib_uverbs_cmd_hdr *)tmpbuf;
481         char __user                     *dst = (char __user *)buf;
482         int                             insz, outsz;
483
484         /*
485          * User "struct ibv_qp_dest" is 40 bytes, passes in 136 bytes.
486          * Kernel "struct ib_uverbs_qp_dest" is 32 bytes, expects 120.
487          * Last 8 bytes of user "struct ibv_qp_dest" not used by kernel.
488          * Kernel expects this layout:
489          *      struct ib_uverbs_cmd_hdr (8)
490          *      struct ib_uverbs_qp_dest (32 <- 40)
491          *      struct ib_uverbs_qp_dest (32 <- 40)
492          *      Rest of qp_mod inputs    (48)
493          */
494
495         if (count > 136)
496                 BUG();
497
498         if (copy_from_user(tmpbuf, buf, count))
499                 return -EFAULT;
500         insz = p->in_words * 4;
501         outsz = p->out_words * 4;
502
503         copy_to_user(dst, &tmpbuf[1], sizeof(struct ib_uverbs_qp_dest));
504         dst += sizeof(struct ib_uverbs_qp_dest);
505         copy_to_user(dst, &tmpbuf[6], sizeof(struct ib_uverbs_qp_dest));
506         dst += sizeof(struct ib_uverbs_qp_dest);
507         copy_to_user(dst, &tmpbuf[11], 48);
508         
509
510         return ib_uverbs_modify_qp(file, buf, insz, outsz);
511 }
512
513 /*
514  * Compat hack for applications/libraries we care about. Retrofit Linux 3.12
515  * style APIs.
516  */
517 ssize_t check_old_abi(struct file *filp, const char __user *buf, size_t count)
518 {
519         struct ib_uverbs_cmd_hdr hdr;
520         int                      tmp;
521         struct ib_uverbs_file *file = filp->private_data;
522
523         if (copy_from_user(&hdr, buf, sizeof hdr))
524                 return -EFAULT;
525
526         tmp = hdr.command & IB_USER_VERBS_CMD_COMMAND_MASK;
527         if ((tmp >= 52) && (tmp <= 53)) {
528                 return compat_ex(file, count, buf);
529         } else if (tmp == IB_USER_VERBS_CMD_MODIFY_QP) {
530                 return compat(file, count, buf);
531         } else if (tmp == IB_USER_VERBS_CMD_QUERY_QP) {
532                 panic("query_qp API difference not handled\n");
533         }
534
535         /* Continue with processing this command */
536         return 0;
537 }
538
539 /* END: Support older version of libibverbs */