int ia32_clone_tls (struct task_struct *child, struct pt_regs *childregs) { struct desc_struct *desc; struct ia32_user_desc info; int idx; if (copy_from_user(&info, (void __user *)(childregs->r14 & 0xffffffff), sizeof(info))) return -EFAULT; if (LDT_empty(&info)) return -EINVAL; idx = info.entry_number; if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) return -EINVAL; desc = child->thread.tls_array + idx - GDT_ENTRY_TLS_MIN; desc->a = LDT_entry_a(&info); desc->b = LDT_entry_b(&info); /* XXX: can this be done in a cleaner way ? */ load_TLS(&child->thread, smp_processor_id()); ia32_load_segment_descriptors(child); load_TLS(¤t->thread, smp_processor_id()); return 0; }
int set_tls_array(struct thread_struct *t, int idx, unsigned long addr, unsigned int limit) { struct user_desc info; struct desc_struct *desc; if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) return -EINVAL; desc = t->tls_array + idx - GDT_ENTRY_TLS_MIN; info.entry_number = idx; info.base_addr = addr; info.limit = limit; info.contents = 0; info.read_exec_only = 0; info.seg_not_present = 0; info.seg_32bit = 1; info.limit_in_pages = 1; info.useable = 0; /* FIXME */ fill_ldt(desc, &info); #if 0 desc->a = LDT_entry_a(&info); desc->b = LDT_entry_b(&info); #endif return 0; }
/* * Set a given TLS descriptor: * When you want addresses > 32bit use arch_prctl() */ int do_set_thread_area(struct thread_struct *t, struct user_desc __user *u_info) { struct user_desc info; struct n_desc_struct *desc; int cpu, idx; if (copy_from_user(&info, u_info, sizeof(info))) return -EFAULT; idx = info.entry_number; /* * index -1 means the kernel should try to find and * allocate an empty descriptor: */ if (idx == -1) { idx = get_free_idx(); if (idx < 0) return idx; if (put_user(idx, &u_info->entry_number)) return -EFAULT; } if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) return -EINVAL; desc = ((struct n_desc_struct *)t->tls_array) + idx - GDT_ENTRY_TLS_MIN; /* * We must not get preempted while modifying the TLS. */ cpu = get_cpu(); if (LDT_empty(&info)) { desc->a = 0; desc->b = 0; } else { desc->a = LDT_entry_a(&info); desc->b = LDT_entry_b(&info); } if (t == ¤t->thread) load_TLS(t, cpu); put_cpu(); return 0; }
int ia32_child_tls(struct task_struct *p, struct pt_regs *childregs) { struct n_desc_struct *desc; struct user_desc info, *cp; int idx; cp = (void *)childregs->rsi; if (copy_from_user(&info, cp, sizeof(info))) return -EFAULT; if (LDT_empty(&info)) return -EINVAL; idx = info.entry_number; if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) return -EINVAL; desc = (struct n_desc_struct *)(p->thread.tls_array) + idx - GDT_ENTRY_TLS_MIN; desc->a = LDT_entry_a(&info); desc->b = LDT_entry_b(&info); return 0; }
int copy_thread(int nr, unsigned long clone_flags, unsigned long esp, unsigned long unused, struct task_struct * p, struct pt_regs * regs) { struct pt_regs * childregs; struct task_struct *tsk; int err; childregs = task_pt_regs(p); *childregs = *regs; childregs->eax = 0; childregs->esp = esp; p->thread.esp = (unsigned long) childregs; p->thread.esp0 = (unsigned long) (childregs+1); p->thread.eip = (unsigned long) ret_from_fork; savesegment(fs,p->thread.fs); savesegment(gs,p->thread.gs); tsk = current; if (unlikely(test_tsk_thread_flag(tsk, TIF_IO_BITMAP))) { p->thread.io_bitmap_ptr = kmalloc(IO_BITMAP_BYTES, GFP_KERNEL); if (!p->thread.io_bitmap_ptr) { p->thread.io_bitmap_max = 0; return -ENOMEM; } memcpy(p->thread.io_bitmap_ptr, tsk->thread.io_bitmap_ptr, IO_BITMAP_BYTES); set_tsk_thread_flag(p, TIF_IO_BITMAP); } /* * Set a new TLS for the child thread? */ if (clone_flags & CLONE_SETTLS) { struct desc_struct *desc; struct user_desc info; int idx; err = -EFAULT; if (copy_from_user(&info, (void __user *)childregs->esi, sizeof(info))) goto out; err = -EINVAL; if (LDT_empty(&info)) goto out; idx = info.entry_number; if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) goto out; desc = p->thread.tls_array + idx - GDT_ENTRY_TLS_MIN; desc->a = LDT_entry_a(&info); desc->b = LDT_entry_b(&info); } p->thread.iopl = current->thread.iopl; err = 0; out: if (err && p->thread.io_bitmap_ptr) { kfree(p->thread.io_bitmap_ptr); p->thread.io_bitmap_max = 0; } return err; }
int copy_thread(int nr, unsigned long clone_flags, unsigned long esp, unsigned long unused, struct task_struct * p, struct pt_regs * regs) { struct pt_regs * childregs; struct task_struct *tsk; int err, i; childregs = ((struct pt_regs *) (THREAD_SIZE + (unsigned long) p->thread_info)) - 1; *childregs = *regs; childregs->eax = 0; childregs->esp = esp; p->set_child_tid = p->clear_child_tid = NULL; p->thread.esp = (unsigned long) childregs; p->thread.esp0 = (unsigned long) (childregs+1); /* * get the two stack pages, for the virtual stack. * * IMPORTANT: this code relies on the fact that the task * structure is an THREAD_SIZE aligned piece of physical memory. */ for (i = 0; i < ARRAY_SIZE(p->thread.stack_page); i++) p->thread.stack_page[i] = virt_to_page((unsigned long)p->thread_info + (i*PAGE_SIZE)); p->thread.eip = (unsigned long) ret_from_fork; p->thread_info->real_stack = p->thread_info; savesegment(fs,p->thread.fs); savesegment(gs,p->thread.gs); tsk = current; if (unlikely(NULL != tsk->thread.io_bitmap_ptr)) { p->thread.io_bitmap_ptr = kmalloc(IO_BITMAP_BYTES, GFP_KERNEL); if (!p->thread.io_bitmap_ptr) { p->thread.io_bitmap_max = 0; return -ENOMEM; } memcpy(p->thread.io_bitmap_ptr, tsk->thread.io_bitmap_ptr, IO_BITMAP_BYTES); } /* * Set a new TLS for the child thread? */ if (clone_flags & CLONE_SETTLS) { struct desc_struct *desc; struct user_desc info; int idx; err = -EFAULT; if (copy_from_user(&info, (void __user *)childregs->esi, sizeof(info))) goto out; err = -EINVAL; if (LDT_empty(&info)) goto out; idx = info.entry_number; if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) goto out; desc = p->thread.tls_array + idx - GDT_ENTRY_TLS_MIN; desc->a = LDT_entry_a(&info); desc->b = LDT_entry_b(&info); } p->thread.io_pl = current->thread.io_pl; err = 0; out: if (err && p->thread.io_bitmap_ptr) { kfree(p->thread.io_bitmap_ptr); p->thread.io_bitmap_max = 0; } return err; }