]> git.saurik.com Git - apple/xnu.git/blobdiff - osfmk/i386/user_ldt.c
xnu-792.12.6.tar.gz
[apple/xnu.git] / osfmk / i386 / user_ldt.c
index 1c735f0d02e8ee93d5b48e828805e0d34ad3412d..ea01e5265fc4807a64b6c4379e033787085ee976 100644 (file)
@@ -1,23 +1,31 @@
 /*
  * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
  *
- * @APPLE_LICENSE_HEADER_START@
+ * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
  * 
- * The contents of this file constitute Original Code as defined in and
- * are subject to the Apple Public Source License Version 1.1 (the
- * "License").  You may not use this file except in compliance with the
- * License.  Please obtain a copy of the License at
- * http://www.apple.com/publicsource and read it before using this file.
- * 
- * This Original Code and all software distributed under the License are
- * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
- * License for the specific language governing rights and limitations
- * under the License.
- * 
- * @APPLE_LICENSE_HEADER_END@
+ * This file contains Original Code and/or Modifications of Original Code 
+ * as defined in and that are subject to the Apple Public Source License 
+ * Version 2.0 (the 'License'). You may not use this file except in 
+ * compliance with the License.  The rights granted to you under the 
+ * License may not be used to create, or enable the creation or 
+ * redistribution of, unlawful or unlicensed copies of an Apple operating 
+ * system, or to circumvent, violate, or enable the circumvention or 
+ * violation of, any terms of an Apple operating system software license 
+ * agreement.
+ *
+ * Please obtain a copy of the License at 
+ * http://www.opensource.apple.com/apsl/ and read it before using this 
+ * file.
+ *
+ * The Original Code and all software distributed under the License are 
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 
+ * Please see the License for the specific language governing rights and 
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
  */
 /*
  * @OSF_COPYRIGHT@
@@ -53,7 +61,7 @@
 
 /*
  * User LDT management.
- * Each task may have its own LDT.
+ * Each thread in a task may have its own LDT.
  */
 
 #include <kern/kalloc.h>
 #include <i386/seg.h>
 #include <i386/thread.h>
 #include <i386/user_ldt.h>
-#include <i386/mp_desc.h>
-#include <i386/proc_reg.h>
-#include <i386/machdep_call.h>
-#include <i386/mp.h>
-#include <i386/machine_routines.h>
 
-#include <sys/errno.h>
+char   acc_type[8][3] = {
+    /* code    stack   data */
+    {  0,      0,      1       },      /* data */
+    {  0,      1,      1       },      /* data, writable */
+    {  0,      0,      1       },      /* data, expand-down */
+    {  0,      1,      1       },      /* data, writable, expand-down */
+    {  1,      0,      0       },      /* code */
+    {  1,      0,      1       },      /* code, readable */
+    {  1,      0,      0       },      /* code, conforming */
+    {  1,      0,      1       },      /* code, readable, conforming */
+};
+
+#if 0
+/* Forward */
+
+extern boolean_t       selector_check(
+                               thread_t                thread,
+                               int                     sel,
+                               int                     type);
+
+boolean_t
+selector_check(
+       thread_t                thread,
+       int                     sel,
+       int                     type)
+{
+       struct user_ldt *ldt;
+       int     access;
+
+       ldt = thread->machine.pcb->ims.ldt;
+       if (ldt == 0) {
+           switch (type) {
+               case S_CODE:
+                   return sel == USER_CS;
+               case S_STACK:
+                   return sel == USER_DS;
+               case S_DATA:
+                   return sel == 0 ||
+                          sel == USER_CS ||
+                          sel == USER_DS;
+           }
+       }
+
+       if (type != S_DATA && sel == 0)
+           return FALSE;
+       if ((sel & (SEL_LDTS|SEL_PL)) != (SEL_LDTS|SEL_PL_U)
+         || sel > ldt->desc.limit_low)
+               return FALSE;
 
-static void user_ldt_set_action(void *);
+       access = ldt->ldt[sel_idx(sel)].access;
+       
+       if ((access & (ACC_P|ACC_PL|ACC_TYPE_USER))
+               != (ACC_P|ACC_PL_U|ACC_TYPE_USER))
+           return FALSE;
+               /* present, pl == pl.user, not system */
+
+       return acc_type[(access & 0xe)>>1][type];
+}
 
 /*
  * Add the descriptors to the LDT, starting with
  * the descriptor for 'first_selector'.
  */
 
-int
+kern_return_t
 i386_set_ldt(
-       int                     *retval,
-       uint32_t                start_sel,
-       uint32_t                descs,  /* out */
-       uint32_t                num_sels)
+       thread_t                thr_act,
+       int                     first_selector,
+       descriptor_list_t       desc_list,
+       mach_msg_type_number_t  count)
 {
-       user_ldt_t      new_ldt, old_ldt;
+       user_ldt_t      new_ldt, old_ldt, temp;
        struct real_descriptor *dp;
-       unsigned int    i;
-       unsigned int    min_selector = LDTSZ_MIN;       /* do not allow the system selectors to be changed */
-       task_t          task = current_task();
-       unsigned int    ldt_count;
-       kern_return_t err;
-
-       if (start_sel != LDT_AUTO_ALLOC
-           && (start_sel != 0 || num_sels != 0)
-           && (start_sel < min_selector || start_sel >= LDTSZ))
-           return EINVAL;
-       if (start_sel != LDT_AUTO_ALLOC
-           && start_sel + num_sels > LDTSZ)
-           return EINVAL;
-
-       task_lock(task);
-       
-       old_ldt = task->i386_ldt;
+       int             i;
+       int             min_selector = 0;
+       pcb_t           pcb;
+       vm_size_t       ldt_size_needed;
+       int             first_desc = sel_idx(first_selector);
+       vm_map_copy_t   old_copy_object;
+       thread_t                thread;
+
+       if (first_desc < min_selector || first_desc > 8191)
+           return KERN_INVALID_ARGUMENT;
+       if (first_desc + count >= 8192)
+           return KERN_INVALID_ARGUMENT;
+       if (thr_act == THREAD_NULL)
+           return KERN_INVALID_ARGUMENT;
+       if ((thread = act_lock_thread(thr_act)) == THREAD_NULL) {
+               act_unlock_thread(thr_act);
+           return KERN_INVALID_ARGUMENT;
+       }
+       if (thread == current_thread())
+               min_selector = LDTSZ;
+       act_unlock_thread(thr_act);
 
-       if (start_sel == LDT_AUTO_ALLOC) {
-           if (old_ldt) {
-               unsigned int null_count;
-               struct real_descriptor null_ldt;
-               
-               bzero(&null_ldt, sizeof(null_ldt));
-
-               /*
-                * Look for null selectors among the already-allocated
-                * entries.
-                */
-               null_count = 0;
-               i = 0;
-               while (i < old_ldt->count)
-               {
-                   if (!memcmp(&old_ldt->ldt[i++], &null_ldt, sizeof(null_ldt))) {
-                       null_count++;
-                       if (null_count == num_sels)
-                           break;  /* break out of while loop */
-                   } else {
-                       null_count = 0;
-                   }
-               }
+       /*
+        * We must copy out desc_list to the kernel map, and wire
+        * it down (we touch it while the PCB is locked).
+        *
+        * We make a copy of the copyin object, and clear
+        * out the old one, so that the MIG stub will have a
+        * a empty (but valid) copyin object to discard.
+        */
+       {
+           kern_return_t       kr;
+           vm_map_offset_t     dst_addr;
+
+           old_copy_object = (vm_map_copy_t) desc_list;
+
+           kr = vm_map_copyout(ipc_kernel_map, &dst_addr,
+                               vm_map_copy_copy(old_copy_object));
+           if (kr != KERN_SUCCESS)
+               return kr;
+
+           (void) vm_map_wire(ipc_kernel_map,
+                       vm_map_trunc_page(dst_addr),
+                       vm_map_round_page(dst_addr + 
+                               count * sizeof(struct real_descriptor)),
+                       VM_PROT_READ|VM_PROT_WRITE, FALSE);
+           desc_list = CAST_DOWN(descriptor_list_t, dst_addr);
+       }
 
-               /*
-                * If we broke out of the while loop, i points to the selector
-                * after num_sels null selectors.  Otherwise it points to the end
-                * of the old LDTs, and null_count is the number of null selectors
-                * at the end. 
-                *
-                * Either way, there are null_count null selectors just prior to
-                * the i-indexed selector, and either null_count >= num_sels,
-                * or we're at the end, so we can extend.
-                */
-               start_sel = old_ldt->start + i - null_count;
-           } else {
-               start_sel = LDTSZ_MIN;
-           }
-               
-           if (start_sel + num_sels > LDTSZ) {
-               task_unlock(task);
-               return ENOMEM;
+       for (i = 0, dp = (struct real_descriptor *) desc_list;
+            i < count;
+            i++, dp++)
+       {
+           switch (dp->access & ~ACC_A) {
+               case 0:
+               case ACC_P:
+                   /* valid empty descriptor */
+                   break;
+               case ACC_P | ACC_CALL_GATE:
+                   /* Mach kernel call */
+                   *dp = *(struct real_descriptor *)
+                               &ldt[sel_idx(USER_SCALL)];
+                   break;
+               case ACC_P | ACC_PL_U | ACC_DATA:
+               case ACC_P | ACC_PL_U | ACC_DATA_W:
+               case ACC_P | ACC_PL_U | ACC_DATA_E:
+               case ACC_P | ACC_PL_U | ACC_DATA_EW:
+               case ACC_P | ACC_PL_U | ACC_CODE:
+               case ACC_P | ACC_PL_U | ACC_CODE_R:
+               case ACC_P | ACC_PL_U | ACC_CODE_C:
+               case ACC_P | ACC_PL_U | ACC_CODE_CR:
+               case ACC_P | ACC_PL_U | ACC_CALL_GATE_16:
+               case ACC_P | ACC_PL_U | ACC_CALL_GATE:
+                   break;
+               default:
+                   (void) vm_map_remove(ipc_kernel_map, 
+                                        vm_map_trunc_page(desc_list),
+                                        vm_map_round_page(&desc_list[count]),
+                                        VM_MAP_REMOVE_KUNWIRE);
+                   return KERN_INVALID_ARGUMENT;
            }
        }
-
-       if (start_sel == 0 && num_sels == 0) {
-           new_ldt = NULL;
-       } else {
+       ldt_size_needed = sizeof(struct real_descriptor)
+                       * (first_desc + count);
+
+       pcb = thr_act->machine.pcb;
+       new_ldt = 0;
+    Retry:
+       simple_lock(&pcb->lock);
+       old_ldt = pcb->ims.ldt;
+       if (old_ldt == 0 ||
+           old_ldt->desc.limit_low + 1 < ldt_size_needed)
+       {
            /*
-            * Allocate new LDT
+            * No old LDT, or not big enough
             */
-
-           unsigned int    begin_sel = start_sel;
-           unsigned int    end_sel = begin_sel + num_sels;
-           
-           if (old_ldt != NULL) {
-               if (old_ldt->start < begin_sel)
-                   begin_sel = old_ldt->start;
-               if (old_ldt->start + old_ldt->count > end_sel)
-                   end_sel = old_ldt->start + old_ldt->count;
-           }
-
-           ldt_count = end_sel - begin_sel;
-
-           new_ldt = (user_ldt_t)kalloc(sizeof(struct user_ldt) + (ldt_count * sizeof(struct real_descriptor)));
-           if (new_ldt == NULL) {
-               task_unlock(task);
-               return ENOMEM;
+           if (new_ldt == 0) {
+               simple_unlock(&pcb->lock);
+
+               new_ldt = (user_ldt_t) kalloc(ldt_size_needed
+                                             + sizeof(struct real_descriptor));
+               new_ldt->desc.limit_low   = ldt_size_needed - 1;
+               new_ldt->desc.limit_high  = 0;
+               new_ldt->desc.base_low    = 
+                               ((vm_offset_t)&new_ldt->ldt[0]) & 0xffff;
+               new_ldt->desc.base_med    = 
+                               (((vm_offset_t)&new_ldt->ldt[0]) >> 16)
+                                                & 0xff;
+               new_ldt->desc.base_high   = 
+                               ((vm_offset_t)&new_ldt->ldt[0]) >> 24;
+               new_ldt->desc.access      = ACC_P | ACC_LDT;
+               new_ldt->desc.granularity = 0;
+
+               goto Retry;
            }
 
-           new_ldt->start = begin_sel;
-           new_ldt->count = ldt_count;
-
            /*
             * Have new LDT.  If there was a an old ldt, copy descriptors
-            * from old to new.
+            * from old to new.  Otherwise copy the default ldt.
             */
            if (old_ldt) {
-               bcopy(&old_ldt->ldt[0],
-                     &new_ldt->ldt[old_ldt->start - begin_sel],
-                     old_ldt->count * sizeof(struct real_descriptor));
-
-               /*
-                * If the old and new LDTs are non-overlapping, fill the 
-                * center in with null selectors.
-                */
-                                
-               if (old_ldt->start + old_ldt->count < start_sel)
-                   bzero(&new_ldt->ldt[old_ldt->count],
-                         (start_sel - (old_ldt->start + old_ldt->count)) * sizeof(struct real_descriptor));
-               else if (old_ldt->start > start_sel + num_sels)
-                   bzero(&new_ldt->ldt[num_sels],
-                         (old_ldt->start - (start_sel + num_sels)) * sizeof(struct real_descriptor));
+               bcopy((char *)&old_ldt->ldt[0],
+                     (char *)&new_ldt->ldt[0],
+                     old_ldt->desc.limit_low + 1);
            }
-
-           /*
-            * Install new descriptors.
-            */
-           if (descs != 0) {
-               err = copyin(descs, (char *)&new_ldt->ldt[start_sel - begin_sel],
-                            num_sels * sizeof(struct real_descriptor));
-               if (err != 0)
-               {
-                   task_unlock(task);
-                   user_ldt_free(new_ldt);
-                   return err;
+           else if (thr_act == current_thread()) {
+               struct real_descriptor template = {0, 0, 0, ACC_P, 0, 0 ,0};
+
+               for (dp = &new_ldt->ldt[0], i = 0; i < first_desc; i++, dp++) {
+                   if (i < LDTSZ)
+                       *dp = *(struct real_descriptor *) &ldt[i];
+                   else
+                       *dp = template;
                }
-           } else {
-               bzero(&new_ldt->ldt[start_sel - begin_sel], num_sels * sizeof(struct real_descriptor));
            }
 
-           /*
-            * Validate descriptors.
-            * Only allow descriptors with user priviledges.
-            */
-           for (i = 0, dp = (struct real_descriptor *) &new_ldt->ldt[start_sel - begin_sel];
-                i < num_sels;
-                i++, dp++)
-           {
-               switch (dp->access & ~ACC_A) {
-                   case 0:
-                   case ACC_P:
-                       /* valid empty descriptor */
-                       break;
-                   case ACC_P | ACC_PL_U | ACC_DATA:
-                   case ACC_P | ACC_PL_U | ACC_DATA_W:
-                   case ACC_P | ACC_PL_U | ACC_DATA_E:
-                   case ACC_P | ACC_PL_U | ACC_DATA_EW:
-                   case ACC_P | ACC_PL_U | ACC_CODE:
-                   case ACC_P | ACC_PL_U | ACC_CODE_R:
-                   case ACC_P | ACC_PL_U | ACC_CODE_C:
-                   case ACC_P | ACC_PL_U | ACC_CODE_CR:
-                   case ACC_P | ACC_PL_U | ACC_CALL_GATE_16:
-                   case ACC_P | ACC_PL_U | ACC_CALL_GATE:
-                       break;
-                   default:
-                       task_unlock(task);
-                       user_ldt_free(new_ldt);
-                       return EACCES;
-               }
-           }
-       }
+           temp = old_ldt;
+           old_ldt = new_ldt;  /* use new LDT from now on */
+           new_ldt = temp;     /* discard old LDT */
 
-       task->i386_ldt = new_ldt; /* new LDT for task */
+           pcb->ims.ldt = old_ldt;     /* new LDT for thread */
+       }
 
        /*
-        * Switch to new LDT.  We need to do this on all CPUs, since
-        * another thread in this same task may be currently running,
-        * and we need to make sure the new LDT is in place
-        * throughout the task before returning to the user.
+        * Install new descriptors.
         */
-       mp_rendezvous_no_intrs(user_ldt_set_action, task);
+       bcopy((char *)desc_list,
+             (char *)&old_ldt->ldt[first_desc],
+             count * sizeof(struct real_descriptor));
 
-       task_unlock(task);
+       simple_unlock(&pcb->lock);
 
-       /* free old LDT.  We can't do this until after we've
-        * rendezvoused with all CPUs, in case another thread
-        * in this task was in the process of context switching.
-        */
-       if (old_ldt)
-           user_ldt_free(old_ldt);
-
-       *retval = start_sel;
+       if (new_ldt)
+           kfree((vm_offset_t)new_ldt,
+                 new_ldt->desc.limit_low+1+sizeof(struct real_descriptor));
 
-       return 0;
+       /*
+        * Free the descriptor list.
+        */
+       (void) vm_map_remove(ipc_kernel_map, vm_map_trunc_page(desc_list),
+                            vm_map_round_page(&desc_list[count]),
+                            VM_MAP_REMOVE_KUNWIRE);
+       return KERN_SUCCESS;
 }
 
-int
+kern_return_t
 i386_get_ldt(
-       int                     *retval,
-       uint32_t                start_sel,
-       uint32_t                descs,  /* out */
-       uint32_t                num_sels)
+       thread_t                thr_act,
+       int                     first_selector,
+       int                     selector_count, /* number wanted */
+       descriptor_list_t       *desc_list,     /* in/out */
+       mach_msg_type_number_t  *count)         /* in/out */
 {
-       user_ldt_t      user_ldt;
-       task_t          task = current_task();
+       struct user_ldt *user_ldt;
+       pcb_t           pcb = thr_act->machine.pcb;
+       int             first_desc = sel_idx(first_selector);
        unsigned int    ldt_count;
-       kern_return_t   err;
-
-       if (start_sel >= 8192)
-           return EINVAL;
-       if (start_sel + num_sels > 8192)
-           return EINVAL;
-       if (descs == 0)
-           return EINVAL;
+       vm_size_t       ldt_size;
+       vm_size_t       size, size_needed;
+       vm_offset_t     addr;
+       thread_t                thread;
+
+       if (thr_act == THREAD_NULL)
+           return KERN_INVALID_ARGUMENT;
+
+       if (first_desc < 0 || first_desc > 8191)
+           return KERN_INVALID_ARGUMENT;
+       if (first_desc + selector_count >= 8192)
+           return KERN_INVALID_ARGUMENT;
+
+       addr = 0;
+       size = 0;
+
+       for (;;) {
+           simple_lock(&pcb->lock);
+           user_ldt = pcb->ims.ldt;
+           if (user_ldt == 0) {
+               simple_unlock(&pcb->lock);
+               if (addr)
+                   kmem_free(ipc_kernel_map, addr, size);
+               *count = 0;
+               return KERN_SUCCESS;
+           }
 
-       task_lock(task);
+           /*
+            * Find how many descriptors we should return.
+            */
+           ldt_count = (user_ldt->desc.limit_low + 1) /
+                       sizeof (struct real_descriptor);
+           ldt_count -= first_desc;
+           if (ldt_count > selector_count)
+               ldt_count = selector_count;
 
-       user_ldt = task->i386_ldt;
-       err = 0;
+           ldt_size = ldt_count * sizeof(struct real_descriptor);
 
-       /*
-        * copy out the descriptors
-        */
+           /*
+            * Do we have the memory we need?
+            */
+           if (ldt_count <= *count)
+               break;          /* fits in-line */
 
-       if (user_ldt != 0)
-           ldt_count = user_ldt->start + user_ldt->count;
-       else
-           ldt_count = LDTSZ_MIN;
+           size_needed = round_page(ldt_size);
+           if (size_needed <= size)
+               break;
 
+           /*
+            * Unlock the pcb and allocate more memory
+            */
+           simple_unlock(&pcb->lock);
 
-       if (start_sel < ldt_count)
-       {
-           unsigned int copy_sels = num_sels;
+           if (size != 0)
+               kmem_free(ipc_kernel_map, addr, size);
 
-           if (start_sel + num_sels > ldt_count)
-               copy_sels = ldt_count - start_sel;
+           size = size_needed;
 
-           err = copyout((char *)(current_ldt() + start_sel),
-                         descs, copy_sels * sizeof(struct real_descriptor));
+           if (kmem_alloc(ipc_kernel_map, &addr, size)
+                       != KERN_SUCCESS)
+               return KERN_RESOURCE_SHORTAGE;
        }
 
-       task_unlock(task);
+       /*
+        * copy out the descriptors
+        */
+       bcopy((char *)&user_ldt->ldt[first_desc],
+             (char *)addr,
+             ldt_size);
+       *count = ldt_count;
+       simple_unlock(&pcb->lock);
 
-       *retval = ldt_count;
+       if (addr) {
+           vm_size_t           size_used, size_left;
+           vm_map_copy_t       memory;
 
-       return err;
-}
+           /*
+            * Free any unused memory beyond the end of the last page used
+            */
+           size_used = round_page(ldt_size);
+           if (size_used != size)
+               kmem_free(ipc_kernel_map,
+                       addr + size_used, size - size_used);
 
-void
-user_ldt_free(
-       user_ldt_t      user_ldt)
-{
-       kfree(user_ldt, sizeof(struct user_ldt) + (user_ldt->count * sizeof(struct real_descriptor)));
-}
+           /*
+            * Zero the remainder of the page being returned.
+            */
+           size_left = size_used - ldt_size;
+           if (size_left > 0)
+               bzero((char *)addr + ldt_size, size_left);
 
-user_ldt_t
-user_ldt_copy(
-       user_ldt_t      user_ldt)
-{
-       if (user_ldt != NULL) {
-           size_t      size = sizeof(struct user_ldt) + (user_ldt->count * sizeof(struct real_descriptor));
-           user_ldt_t  new_ldt = (user_ldt_t)kalloc(size);
-           if (new_ldt != NULL)
-               bcopy(user_ldt, new_ldt, size);
-           return new_ldt;
+           /*
+            * Unwire the memory and make it into copyin form.
+            */
+           (void) vm_map_unwire(ipc_kernel_map, vm_map_trunc_page(addr),
+                                vm_map_round_page(addr + size_used), FALSE);
+           (void) vm_map_copyin(ipc_kernel_map, (vm_map_address_t)addr, 
+                                (vm_map_size_t)size_used, TRUE, &memory);
+           *desc_list = (descriptor_list_t) memory;
        }
-       
-       return 0;
-}
 
-void
-user_ldt_set_action(
-       void *arg)
-{
-       task_t          arg_task = (task_t)arg;
-
-       if (arg_task == current_task()) {
-           user_ldt_set(current_thread());
-       }
+       return KERN_SUCCESS;
 }
 
-/*
- * Set the LDT for the given thread on the current CPU.  Should be invoked
- * with interrupts disabled.
- */
+#endif 
 void
-user_ldt_set(
-       thread_t thread)
+user_ldt_free(
+       user_ldt_t      user_ldt)
 {
-        task_t         task = thread->task;
-       user_ldt_t      user_ldt;
-
-       user_ldt = task->i386_ldt;
-
-       if (user_ldt != 0) {
-           struct real_descriptor *ldtp = (struct real_descriptor *)current_ldt();
-
-           if (user_ldt->start > LDTSZ_MIN) {
-               bzero(&ldtp[LDTSZ_MIN],
-                     sizeof(struct real_descriptor) * (user_ldt->start - LDTSZ_MIN));
-           }
-           
-           bcopy(user_ldt->ldt, &ldtp[user_ldt->start],
-                 sizeof(struct real_descriptor) * (user_ldt->count));
-
-           gdt_desc_p(USER_LDT)->limit_low = (sizeof(struct real_descriptor) * (user_ldt->start + user_ldt->count)) - 1;
-
-           ml_cpu_set_ldt(USER_LDT);
-       } else {
-           ml_cpu_set_ldt(KERNEL_LDT);
-       }
+       kfree(user_ldt,
+               user_ldt->desc.limit_low+1+sizeof(struct real_descriptor));
 }