| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright (C) 2001, 2004 Hewlett-Packard Co | 
|  | 3 | *	David Mosberger-Tang <davidm@hpl.hp.com> | 
|  | 4 | * | 
|  | 5 | * Adapted from arch/i386/kernel/ldt.c | 
|  | 6 | */ | 
|  | 7 |  | 
|  | 8 | #include <linux/errno.h> | 
|  | 9 | #include <linux/sched.h> | 
|  | 10 | #include <linux/string.h> | 
|  | 11 | #include <linux/mm.h> | 
|  | 12 | #include <linux/smp.h> | 
|  | 13 | #include <linux/smp_lock.h> | 
|  | 14 | #include <linux/vmalloc.h> | 
|  | 15 |  | 
|  | 16 | #include <asm/uaccess.h> | 
|  | 17 |  | 
|  | 18 | #include "ia32priv.h" | 
|  | 19 |  | 
|  | 20 | /* | 
|  | 21 | * read_ldt() is not really atomic - this is not a problem since synchronization of reads | 
|  | 22 | * and writes done to the LDT has to be assured by user-space anyway. Writes are atomic, | 
|  | 23 | * to protect the security checks done on new descriptors. | 
|  | 24 | */ | 
|  | 25 | static int | 
|  | 26 | read_ldt (void __user *ptr, unsigned long bytecount) | 
|  | 27 | { | 
|  | 28 | unsigned long bytes_left, n; | 
|  | 29 | char __user *src, *dst; | 
|  | 30 | char buf[256];	/* temporary buffer (don't overflow kernel stack!) */ | 
|  | 31 |  | 
|  | 32 | if (bytecount > IA32_LDT_ENTRIES*IA32_LDT_ENTRY_SIZE) | 
|  | 33 | bytecount = IA32_LDT_ENTRIES*IA32_LDT_ENTRY_SIZE; | 
|  | 34 |  | 
|  | 35 | bytes_left = bytecount; | 
|  | 36 |  | 
|  | 37 | src = (void __user *) IA32_LDT_OFFSET; | 
|  | 38 | dst = ptr; | 
|  | 39 |  | 
|  | 40 | while (bytes_left) { | 
|  | 41 | n = sizeof(buf); | 
|  | 42 | if (n > bytes_left) | 
|  | 43 | n = bytes_left; | 
|  | 44 |  | 
|  | 45 | /* | 
|  | 46 | * We know we're reading valid memory, but we still must guard against | 
|  | 47 | * running out of memory. | 
|  | 48 | */ | 
|  | 49 | if (__copy_from_user(buf, src, n)) | 
|  | 50 | return -EFAULT; | 
|  | 51 |  | 
|  | 52 | if (copy_to_user(dst, buf, n)) | 
|  | 53 | return -EFAULT; | 
|  | 54 |  | 
|  | 55 | src += n; | 
|  | 56 | dst += n; | 
|  | 57 | bytes_left -= n; | 
|  | 58 | } | 
|  | 59 | return bytecount; | 
|  | 60 | } | 
|  | 61 |  | 
|  | 62 | static int | 
|  | 63 | read_default_ldt (void __user * ptr, unsigned long bytecount) | 
|  | 64 | { | 
|  | 65 | unsigned long size; | 
|  | 66 | int err; | 
|  | 67 |  | 
|  | 68 | /* XXX fix me: should return equivalent of default_ldt[0] */ | 
|  | 69 | err = 0; | 
|  | 70 | size = 8; | 
|  | 71 | if (size > bytecount) | 
|  | 72 | size = bytecount; | 
|  | 73 |  | 
|  | 74 | err = size; | 
|  | 75 | if (clear_user(ptr, size)) | 
|  | 76 | err = -EFAULT; | 
|  | 77 |  | 
|  | 78 | return err; | 
|  | 79 | } | 
|  | 80 |  | 
|  | 81 | static int | 
|  | 82 | write_ldt (void __user * ptr, unsigned long bytecount, int oldmode) | 
|  | 83 | { | 
|  | 84 | struct ia32_user_desc ldt_info; | 
|  | 85 | __u64 entry; | 
|  | 86 | int ret; | 
|  | 87 |  | 
|  | 88 | if (bytecount != sizeof(ldt_info)) | 
|  | 89 | return -EINVAL; | 
|  | 90 | if (copy_from_user(&ldt_info, ptr, sizeof(ldt_info))) | 
|  | 91 | return -EFAULT; | 
|  | 92 |  | 
|  | 93 | if (ldt_info.entry_number >= IA32_LDT_ENTRIES) | 
|  | 94 | return -EINVAL; | 
|  | 95 | if (ldt_info.contents == 3) { | 
|  | 96 | if (oldmode) | 
|  | 97 | return -EINVAL; | 
|  | 98 | if (ldt_info.seg_not_present == 0) | 
|  | 99 | return -EINVAL; | 
|  | 100 | } | 
|  | 101 |  | 
|  | 102 | if (ldt_info.base_addr == 0 && ldt_info.limit == 0 | 
|  | 103 | && (oldmode || (ldt_info.contents == 0 && ldt_info.read_exec_only == 1 | 
|  | 104 | && ldt_info.seg_32bit == 0 && ldt_info.limit_in_pages == 0 | 
|  | 105 | && ldt_info.seg_not_present == 1 && ldt_info.useable == 0))) | 
|  | 106 | /* allow LDTs to be cleared by the user */ | 
|  | 107 | entry = 0; | 
|  | 108 | else | 
|  | 109 | /* we must set the "Accessed" bit as IVE doesn't emulate it */ | 
|  | 110 | entry = IA32_SEG_DESCRIPTOR(ldt_info.base_addr, ldt_info.limit, | 
|  | 111 | (((ldt_info.read_exec_only ^ 1) << 1) | 
|  | 112 | | (ldt_info.contents << 2)) | 1, | 
|  | 113 | 1, 3, ldt_info.seg_not_present ^ 1, | 
|  | 114 | (oldmode ? 0 : ldt_info.useable), | 
|  | 115 | ldt_info.seg_32bit, | 
|  | 116 | ldt_info.limit_in_pages); | 
|  | 117 | /* | 
|  | 118 | * Install the new entry.  We know we're accessing valid (mapped) user-level | 
|  | 119 | * memory, but we still need to guard against out-of-memory, hence we must use | 
|  | 120 | * put_user(). | 
|  | 121 | */ | 
|  | 122 | ret = __put_user(entry, (__u64 __user *) IA32_LDT_OFFSET + ldt_info.entry_number); | 
|  | 123 | ia32_load_segment_descriptors(current); | 
|  | 124 | return ret; | 
|  | 125 | } | 
|  | 126 |  | 
|  | 127 | asmlinkage int | 
|  | 128 | sys32_modify_ldt (int func, unsigned int ptr, unsigned int bytecount) | 
|  | 129 | { | 
|  | 130 | int ret = -ENOSYS; | 
|  | 131 |  | 
|  | 132 | switch (func) { | 
|  | 133 | case 0: | 
|  | 134 | ret = read_ldt(compat_ptr(ptr), bytecount); | 
|  | 135 | break; | 
|  | 136 | case 1: | 
|  | 137 | ret = write_ldt(compat_ptr(ptr), bytecount, 1); | 
|  | 138 | break; | 
|  | 139 | case 2: | 
|  | 140 | ret = read_default_ldt(compat_ptr(ptr), bytecount); | 
|  | 141 | break; | 
|  | 142 | case 0x11: | 
|  | 143 | ret = write_ldt(compat_ptr(ptr), bytecount, 0); | 
|  | 144 | break; | 
|  | 145 | } | 
|  | 146 | return ret; | 
|  | 147 | } |