Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | /* * linux/kernel/ldt.c * * Copyright (C) 1992 Krishna Balasubramanian and Linus Torvalds */ #include <linux/config.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/string.h> #include <asm/segment.h> #include <asm/system.h> #include <linux/ldt.h> static int read_ldt(void * ptr, unsigned long bytecount) { int error; void * address = current->ldt; unsigned long size; if (!ptr) return -EINVAL; size = LDT_ENTRIES*LDT_ENTRY_SIZE; if (!address) { address = &default_ldt; size = sizeof(default_ldt); } if (size > bytecount) size = bytecount; error = verify_area(VERIFY_WRITE, ptr, size); if (error) return error; memcpy_tofs(ptr, address, size); return size; } static int write_ldt(void * ptr, unsigned long bytecount) { struct modify_ldt_ldt_s ldt_info; unsigned long *lp; unsigned long base, limit; int error, i; if (bytecount != sizeof(ldt_info)) return -EINVAL; error = verify_area(VERIFY_READ, ptr, sizeof(ldt_info)); if (error) return error; memcpy_fromfs(&ldt_info, ptr, sizeof(ldt_info)); if (ldt_info.contents == 3 || ldt_info.entry_number >= LDT_ENTRIES) return -EINVAL; limit = ldt_info.limit; base = ldt_info.base_addr; if (ldt_info.limit_in_pages) limit *= PAGE_SIZE; limit += base; if (limit < base || limit >= 0xC0000000) return -EINVAL; if (!current->ldt) { for (i=1 ; i<NR_TASKS ; i++) { if (task[i] == current) { if (!(current->ldt = (struct desc_struct*) vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE))) return -ENOMEM; set_ldt_desc(gdt+(i<<1)+FIRST_LDT_ENTRY, current->ldt, LDT_ENTRIES); load_ldt(i); } } } lp = (unsigned long *) ¤t->ldt[ldt_info.entry_number]; /* Allow LDTs to be cleared by the user. */ if (ldt_info.base_addr == 0 && ldt_info.limit == 0) { *lp = 0; *(lp+1) = 0; return 0; } *lp = ((ldt_info.base_addr & 0x0000ffff) << 16) | (ldt_info.limit & 0x0ffff); *(lp+1) = (ldt_info.base_addr & 0xff000000) | ((ldt_info.base_addr & 0x00ff0000)>>16) | (ldt_info.limit & 0xf0000) | (ldt_info.contents << 10) | ((ldt_info.read_exec_only ^ 1) << 9) | (ldt_info.seg_32bit << 22) | (ldt_info.limit_in_pages << 23) | ((ldt_info.seg_not_present ^1) << 15) | 0x7000; return 0; } asmlinkage int sys_modify_ldt(int func, void *ptr, unsigned long bytecount) { if (func == 0) return read_ldt(ptr, bytecount); if (func == 1) return write_ldt(ptr, bytecount); return -ENOSYS; } |