LCOV - code coverage report
Current view: top level - arch/x86/um - ldt.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 170 0.0 %
Date: 2023-04-06 08:38:28 Functions: 0 8 0.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
       3             :  * Licensed under the GPL
       4             :  */
       5             : 
       6             : #include <linux/mm.h>
       7             : #include <linux/sched.h>
       8             : #include <linux/slab.h>
       9             : #include <linux/syscalls.h>
      10             : #include <linux/uaccess.h>
      11             : #include <asm/unistd.h>
      12             : #include <os.h>
      13             : #include <skas.h>
      14             : #include <sysdep/tls.h>
      15             : 
      16             : static inline int modify_ldt (int func, void *ptr, unsigned long bytecount)
      17             : {
      18           0 :         return syscall(__NR_modify_ldt, func, ptr, bytecount);
      19             : }
      20             : 
      21           0 : static long write_ldt_entry(struct mm_id *mm_idp, int func,
      22             :                      struct user_desc *desc, void **addr, int done)
      23             : {
      24             :         long res;
      25             :         void *stub_addr;
      26             : 
      27             :         BUILD_BUG_ON(sizeof(*desc) % sizeof(long));
      28             : 
      29           0 :         res = syscall_stub_data(mm_idp, (unsigned long *)desc,
      30             :                                 sizeof(*desc) / sizeof(long),
      31             :                                 addr, &stub_addr);
      32           0 :         if (!res) {
      33           0 :                 unsigned long args[] = { func,
      34           0 :                                          (unsigned long)stub_addr,
      35             :                                          sizeof(*desc),
      36             :                                          0, 0, 0 };
      37           0 :                 res = run_syscall_stub(mm_idp, __NR_modify_ldt, args,
      38             :                                        0, addr, done);
      39             :         }
      40             : 
      41           0 :         return res;
      42             : }
      43             : 
      44             : /*
      45             :  * In skas mode, we hold our own ldt data in UML.
      46             :  * Thus, the code implementing sys_modify_ldt_skas
      47             :  * is very similar to (and mostly stolen from) sys_modify_ldt
      48             :  * for arch/i386/kernel/ldt.c
      49             :  * The routines copied and modified in part are:
      50             :  * - read_ldt
      51             :  * - read_default_ldt
      52             :  * - write_ldt
      53             :  * - sys_modify_ldt_skas
      54             :  */
      55             : 
      56           0 : static int read_ldt(void __user * ptr, unsigned long bytecount)
      57             : {
      58           0 :         int i, err = 0;
      59             :         unsigned long size;
      60           0 :         uml_ldt_t *ldt = &current->mm->context.arch.ldt;
      61             : 
      62           0 :         if (!ldt->entry_count)
      63             :                 goto out;
      64           0 :         if (bytecount > LDT_ENTRY_SIZE*LDT_ENTRIES)
      65           0 :                 bytecount = LDT_ENTRY_SIZE*LDT_ENTRIES;
      66           0 :         err = bytecount;
      67             : 
      68           0 :         mutex_lock(&ldt->lock);
      69           0 :         if (ldt->entry_count <= LDT_DIRECT_ENTRIES) {
      70           0 :                 size = LDT_ENTRY_SIZE*LDT_DIRECT_ENTRIES;
      71           0 :                 if (size > bytecount)
      72           0 :                         size = bytecount;
      73           0 :                 if (copy_to_user(ptr, ldt->u.entries, size))
      74           0 :                         err = -EFAULT;
      75           0 :                 bytecount -= size;
      76           0 :                 ptr += size;
      77             :         }
      78             :         else {
      79           0 :                 for (i=0; i<ldt->entry_count/LDT_ENTRIES_PER_PAGE && bytecount;
      80           0 :                      i++) {
      81           0 :                         size = PAGE_SIZE;
      82           0 :                         if (size > bytecount)
      83           0 :                                 size = bytecount;
      84           0 :                         if (copy_to_user(ptr, ldt->u.pages[i], size)) {
      85             :                                 err = -EFAULT;
      86             :                                 break;
      87             :                         }
      88           0 :                         bytecount -= size;
      89           0 :                         ptr += size;
      90             :                 }
      91             :         }
      92           0 :         mutex_unlock(&ldt->lock);
      93             : 
      94           0 :         if (bytecount == 0 || err == -EFAULT)
      95             :                 goto out;
      96             : 
      97           0 :         if (clear_user(ptr, bytecount))
      98           0 :                 err = -EFAULT;
      99             : 
     100             : out:
     101           0 :         return err;
     102             : }
     103             : 
     104             : static int read_default_ldt(void __user * ptr, unsigned long bytecount)
     105             : {
     106             :         int err;
     107             : 
     108           0 :         if (bytecount > 5*LDT_ENTRY_SIZE)
     109           0 :                 bytecount = 5*LDT_ENTRY_SIZE;
     110             : 
     111           0 :         err = bytecount;
     112             :         /*
     113             :          * UML doesn't support lcall7 and lcall27.
     114             :          * So, we don't really have a default ldt, but emulate
     115             :          * an empty ldt of common host default ldt size.
     116             :          */
     117           0 :         if (clear_user(ptr, bytecount))
     118           0 :                 err = -EFAULT;
     119             : 
     120             :         return err;
     121             : }
     122             : 
     123           0 : static int write_ldt(void __user * ptr, unsigned long bytecount, int func)
     124             : {
     125           0 :         uml_ldt_t *ldt = &current->mm->context.arch.ldt;
     126           0 :         struct mm_id * mm_idp = &current->mm->context.id;
     127             :         int i, err;
     128             :         struct user_desc ldt_info;
     129             :         struct ldt_entry entry0, *ldt_p;
     130           0 :         void *addr = NULL;
     131             : 
     132           0 :         err = -EINVAL;
     133           0 :         if (bytecount != sizeof(ldt_info))
     134             :                 goto out;
     135           0 :         err = -EFAULT;
     136           0 :         if (copy_from_user(&ldt_info, ptr, sizeof(ldt_info)))
     137             :                 goto out;
     138             : 
     139           0 :         err = -EINVAL;
     140           0 :         if (ldt_info.entry_number >= LDT_ENTRIES)
     141             :                 goto out;
     142           0 :         if (ldt_info.contents == 3) {
     143           0 :                 if (func == 1)
     144             :                         goto out;
     145           0 :                 if (ldt_info.seg_not_present == 0)
     146             :                         goto out;
     147             :         }
     148             : 
     149           0 :         mutex_lock(&ldt->lock);
     150             : 
     151           0 :         err = write_ldt_entry(mm_idp, func, &ldt_info, &addr, 1);
     152           0 :         if (err)
     153             :                 goto out_unlock;
     154             : 
     155           0 :         if (ldt_info.entry_number >= ldt->entry_count &&
     156             :             ldt_info.entry_number >= LDT_DIRECT_ENTRIES) {
     157           0 :                 for (i=ldt->entry_count/LDT_ENTRIES_PER_PAGE;
     158           0 :                      i*LDT_ENTRIES_PER_PAGE <= ldt_info.entry_number;
     159           0 :                      i++) {
     160           0 :                         if (i == 0)
     161           0 :                                 memcpy(&entry0, ldt->u.entries,
     162             :                                        sizeof(entry0));
     163           0 :                         ldt->u.pages[i] = (struct ldt_entry *)
     164           0 :                                 __get_free_page(GFP_KERNEL|__GFP_ZERO);
     165           0 :                         if (!ldt->u.pages[i]) {
     166           0 :                                 err = -ENOMEM;
     167             :                                 /* Undo the change in host */
     168           0 :                                 memset(&ldt_info, 0, sizeof(ldt_info));
     169           0 :                                 write_ldt_entry(mm_idp, 1, &ldt_info, &addr, 1);
     170           0 :                                 goto out_unlock;
     171             :                         }
     172           0 :                         if (i == 0) {
     173           0 :                                 memcpy(ldt->u.pages[0], &entry0,
     174             :                                        sizeof(entry0));
     175           0 :                                 memcpy(ldt->u.pages[0]+1, ldt->u.entries+1,
     176             :                                        sizeof(entry0)*(LDT_DIRECT_ENTRIES-1));
     177             :                         }
     178           0 :                         ldt->entry_count = (i + 1) * LDT_ENTRIES_PER_PAGE;
     179             :                 }
     180             :         }
     181           0 :         if (ldt->entry_count <= ldt_info.entry_number)
     182           0 :                 ldt->entry_count = ldt_info.entry_number + 1;
     183             : 
     184           0 :         if (ldt->entry_count <= LDT_DIRECT_ENTRIES)
     185           0 :                 ldt_p = ldt->u.entries + ldt_info.entry_number;
     186             :         else
     187           0 :                 ldt_p = ldt->u.pages[ldt_info.entry_number/LDT_ENTRIES_PER_PAGE] +
     188           0 :                         ldt_info.entry_number%LDT_ENTRIES_PER_PAGE;
     189             : 
     190           0 :         if (ldt_info.base_addr == 0 && ldt_info.limit == 0 &&
     191           0 :            (func == 1 || LDT_empty(&ldt_info))) {
     192           0 :                 ldt_p->a = 0;
     193           0 :                 ldt_p->b = 0;
     194             :         }
     195             :         else{
     196           0 :                 if (func == 1)
     197           0 :                         ldt_info.useable = 0;
     198           0 :                 ldt_p->a = LDT_entry_a(&ldt_info);
     199           0 :                 ldt_p->b = LDT_entry_b(&ldt_info);
     200             :         }
     201             :         err = 0;
     202             : 
     203             : out_unlock:
     204           0 :         mutex_unlock(&ldt->lock);
     205             : out:
     206           0 :         return err;
     207             : }
     208             : 
     209           0 : static long do_modify_ldt_skas(int func, void __user *ptr,
     210             :                                unsigned long bytecount)
     211             : {
     212           0 :         int ret = -ENOSYS;
     213             : 
     214           0 :         switch (func) {
     215             :                 case 0:
     216           0 :                         ret = read_ldt(ptr, bytecount);
     217           0 :                         break;
     218             :                 case 1:
     219             :                 case 0x11:
     220           0 :                         ret = write_ldt(ptr, bytecount, func);
     221           0 :                         break;
     222             :                 case 2:
     223             :                         ret = read_default_ldt(ptr, bytecount);
     224             :                         break;
     225             :         }
     226           0 :         return ret;
     227             : }
     228             : 
     229             : static DEFINE_SPINLOCK(host_ldt_lock);
     230             : static short dummy_list[9] = {0, -1};
     231             : static short * host_ldt_entries = NULL;
     232             : 
     233           0 : static void ldt_get_host_info(void)
     234             : {
     235             :         long ret;
     236             :         struct ldt_entry * ldt;
     237             :         short *tmp;
     238             :         int i, size, k, order;
     239             : 
     240           0 :         spin_lock(&host_ldt_lock);
     241             : 
     242           0 :         if (host_ldt_entries != NULL) {
     243             :                 spin_unlock(&host_ldt_lock);
     244             :                 return;
     245             :         }
     246           0 :         host_ldt_entries = dummy_list+1;
     247             : 
     248           0 :         spin_unlock(&host_ldt_lock);
     249             : 
     250           0 :         for (i = LDT_PAGES_MAX-1, order=0; i; i>>=1, order++)
     251             :                 ;
     252             : 
     253           0 :         ldt = (struct ldt_entry *)
     254           0 :               __get_free_pages(GFP_KERNEL|__GFP_ZERO, order);
     255           0 :         if (ldt == NULL) {
     256           0 :                 printk(KERN_ERR "ldt_get_host_info: couldn't allocate buffer "
     257             :                        "for host ldt\n");
     258           0 :                 return;
     259             :         }
     260             : 
     261           0 :         ret = modify_ldt(0, ldt, (1<<order)*PAGE_SIZE);
     262           0 :         if (ret < 0) {
     263           0 :                 printk(KERN_ERR "ldt_get_host_info: couldn't read host ldt\n");
     264           0 :                 goto out_free;
     265             :         }
     266           0 :         if (ret == 0) {
     267             :                 /* default_ldt is active, simply write an empty entry 0 */
     268           0 :                 host_ldt_entries = dummy_list;
     269           0 :                 goto out_free;
     270             :         }
     271             : 
     272           0 :         for (i=0, size=0; i<ret/LDT_ENTRY_SIZE; i++) {
     273           0 :                 if (ldt[i].a != 0 || ldt[i].b != 0)
     274           0 :                         size++;
     275             :         }
     276             : 
     277           0 :         if (size < ARRAY_SIZE(dummy_list))
     278           0 :                 host_ldt_entries = dummy_list;
     279             :         else {
     280           0 :                 size = (size + 1) * sizeof(dummy_list[0]);
     281           0 :                 tmp = kmalloc(size, GFP_KERNEL);
     282           0 :                 if (tmp == NULL) {
     283           0 :                         printk(KERN_ERR "ldt_get_host_info: couldn't allocate "
     284             :                                "host ldt list\n");
     285           0 :                         goto out_free;
     286             :                 }
     287           0 :                 host_ldt_entries = tmp;
     288             :         }
     289             : 
     290           0 :         for (i=0, k=0; i<ret/LDT_ENTRY_SIZE; i++) {
     291           0 :                 if (ldt[i].a != 0 || ldt[i].b != 0)
     292           0 :                         host_ldt_entries[k++] = i;
     293             :         }
     294           0 :         host_ldt_entries[k] = -1;
     295             : 
     296             : out_free:
     297           0 :         free_pages((unsigned long)ldt, order);
     298             : }
     299             : 
     300           0 : long init_new_ldt(struct mm_context *new_mm, struct mm_context *from_mm)
     301             : {
     302             :         struct user_desc desc;
     303             :         short * num_p;
     304             :         int i;
     305           0 :         long page, err=0;
     306           0 :         void *addr = NULL;
     307             : 
     308             : 
     309           0 :         mutex_init(&new_mm->arch.ldt.lock);
     310             : 
     311           0 :         if (!from_mm) {
     312           0 :                 memset(&desc, 0, sizeof(desc));
     313             :                 /*
     314             :                  * Now we try to retrieve info about the ldt, we
     315             :                  * inherited from the host. All ldt-entries found
     316             :                  * will be reset in the following loop
     317             :                  */
     318           0 :                 ldt_get_host_info();
     319           0 :                 for (num_p=host_ldt_entries; *num_p != -1; num_p++) {
     320           0 :                         desc.entry_number = *num_p;
     321           0 :                         err = write_ldt_entry(&new_mm->id, 1, &desc,
     322           0 :                                               &addr, *(num_p + 1) == -1);
     323           0 :                         if (err)
     324             :                                 break;
     325             :                 }
     326           0 :                 new_mm->arch.ldt.entry_count = 0;
     327             : 
     328           0 :                 goto out;
     329             :         }
     330             : 
     331             :         /*
     332             :          * Our local LDT is used to supply the data for
     333             :          * modify_ldt(READLDT), if PTRACE_LDT isn't available,
     334             :          * i.e., we have to use the stub for modify_ldt, which
     335             :          * can't handle the big read buffer of up to 64kB.
     336             :          */
     337           0 :         mutex_lock(&from_mm->arch.ldt.lock);
     338           0 :         if (from_mm->arch.ldt.entry_count <= LDT_DIRECT_ENTRIES)
     339           0 :                 memcpy(new_mm->arch.ldt.u.entries, from_mm->arch.ldt.u.entries,
     340             :                        sizeof(new_mm->arch.ldt.u.entries));
     341             :         else {
     342           0 :                 i = from_mm->arch.ldt.entry_count / LDT_ENTRIES_PER_PAGE;
     343           0 :                 while (i-->0) {
     344           0 :                         page = __get_free_page(GFP_KERNEL|__GFP_ZERO);
     345           0 :                         if (!page) {
     346             :                                 err = -ENOMEM;
     347             :                                 break;
     348             :                         }
     349           0 :                         new_mm->arch.ldt.u.pages[i] =
     350           0 :                                 (struct ldt_entry *) page;
     351           0 :                         memcpy(new_mm->arch.ldt.u.pages[i],
     352           0 :                                from_mm->arch.ldt.u.pages[i], PAGE_SIZE);
     353             :                 }
     354             :         }
     355           0 :         new_mm->arch.ldt.entry_count = from_mm->arch.ldt.entry_count;
     356           0 :         mutex_unlock(&from_mm->arch.ldt.lock);
     357             : 
     358             :     out:
     359           0 :         return err;
     360             : }
     361             : 
     362             : 
     363           0 : void free_ldt(struct mm_context *mm)
     364             : {
     365             :         int i;
     366             : 
     367           0 :         if (mm->arch.ldt.entry_count > LDT_DIRECT_ENTRIES) {
     368           0 :                 i = mm->arch.ldt.entry_count / LDT_ENTRIES_PER_PAGE;
     369           0 :                 while (i-- > 0)
     370           0 :                         free_page((long) mm->arch.ldt.u.pages[i]);
     371             :         }
     372           0 :         mm->arch.ldt.entry_count = 0;
     373           0 : }
     374             : 
     375           0 : SYSCALL_DEFINE3(modify_ldt, int , func , void __user * , ptr ,
     376             :                 unsigned long , bytecount)
     377             : {
     378             :         /* See non-um modify_ldt() for why we do this cast */
     379           0 :         return (unsigned int)do_modify_ldt_skas(func, ptr, bytecount);
     380             : }

Generated by: LCOV version 1.14