Merge to Fedora kernel-2.6.17-1.2187_FC5 patched with stable patch-2.6.17.13-vs2...
[linux-2.6.git] / arch / i386 / kernel / sysenter.c
1 /*
2  * linux/arch/i386/kernel/sysenter.c
3  *
4  * (C) Copyright 2002 Linus Torvalds
5  *
6  * This file contains the needed initializations to support sysenter.
7  */
8
9 #include <linux/init.h>
10 #include <linux/smp.h>
11 #include <linux/thread_info.h>
12 #include <linux/sched.h>
13 #include <linux/gfp.h>
14 #include <linux/string.h>
15 #include <linux/elf.h>
16 #include <linux/mm.h>
17 #include <linux/mman.h>
18
19 #include <asm/a.out.h>
20 #include <asm/cpufeature.h>
21 #include <asm/msr.h>
22 #include <asm/pgtable.h>
23 #include <asm/unistd.h>
24
25 #ifdef CONFIG_XEN
26 #include <xen/interface/callback.h>
27 #endif
28
29 extern asmlinkage void sysenter_entry(void);
30
31 void enable_sep_cpu(void)
32 {
33 #ifndef CONFIG_X86_NO_TSS
34         int cpu = get_cpu();
35         struct tss_struct *tss = &per_cpu(init_tss, cpu);
36
37         if (!boot_cpu_has(X86_FEATURE_SEP)) {
38                 put_cpu();
39                 return;
40         }
41
42         tss->ss1 = __KERNEL_CS;
43         tss->esp1 = sizeof(struct tss_struct) + (unsigned long) tss;
44         wrmsr(MSR_IA32_SYSENTER_CS, __KERNEL_CS, 0);
45         wrmsr(MSR_IA32_SYSENTER_ESP, tss->esp1, 0);
46         wrmsr(MSR_IA32_SYSENTER_EIP, (unsigned long) sysenter_entry, 0);
47         put_cpu();      
48 #endif
49 }
50
51 /*
52  * These symbols are defined by vsyscall.o to mark the bounds
53  * of the ELF DSO images included therein.
54  */
55 extern const char vsyscall_int80_start, vsyscall_int80_end;
56 extern const char vsyscall_sysenter_start, vsyscall_sysenter_end;
57 static struct page *sysenter_pages[2];
58
59 int __init sysenter_setup(void)
60 {
61         void *page = (void *)get_zeroed_page(GFP_ATOMIC);
62
63         sysenter_pages[0] = virt_to_page(page);
64
65 #ifdef CONFIG_XEN
66         if (boot_cpu_has(X86_FEATURE_SEP)) {
67                 struct callback_register sysenter = {
68                         .type = CALLBACKTYPE_sysenter,
69                         .address = { __KERNEL_CS, (unsigned long)sysenter_entry },
70                 };
71
72                 if (HYPERVISOR_callback_op(CALLBACKOP_register, &sysenter) < 0)
73                         clear_bit(X86_FEATURE_SEP, boot_cpu_data.x86_capability);
74         }
75 #endif
76
77         if (boot_cpu_has(X86_FEATURE_SEP)) {
78                 memcpy(page,
79                        &vsyscall_sysenter_start,
80                        &vsyscall_sysenter_end - &vsyscall_sysenter_start);
81                 return 0;
82         }
83
84         memcpy(page,
85                &vsyscall_int80_start,
86                &vsyscall_int80_end - &vsyscall_int80_start);
87
88         return 0;
89 }
90
91 extern void SYSENTER_RETURN_OFFSET;
92
93 unsigned int vdso_enabled = 1;
94
95 /*
96  * This is called from binfmt_elf, we create the special vma for the
97  * vDSO and insert it into the mm struct tree.
98  */
99 int arch_setup_additional_pages(struct linux_binprm *bprm,
100         int executable_stack, unsigned long start_code,
101         unsigned long interp_map_address)
102 {
103         struct thread_info *ti = current_thread_info();
104         unsigned long addr = 0, len;
105         unsigned flags = MAP_PRIVATE;
106         int err;
107
108         current->mm->context.vdso = NULL;
109         if (unlikely(!vdso_enabled) || unlikely(!sysenter_pages[0]))
110                 return 0;
111
112         /*
113          * Map the vDSO (it will be randomized):
114          */
115         down_write(&current->mm->mmap_sem);
116         len = PAGE_SIZE > ELF_EXEC_PAGESIZE ? PAGE_SIZE : ELF_EXEC_PAGESIZE;
117         if (0==exec_shield) { /* off; %cs limit off */
118                 addr = STACK_TOP;  /* minimal interference with anybody */
119                 flags = MAP_PRIVATE | MAP_FIXED;
120         }
121         else if ((3<<2) & exec_shield) { /* vdso just below .text */
122                 addr = (((2<<2) & exec_shield) && interp_map_address) ?
123                         interp_map_address : start_code;
124                 /* 1MB for vm86; 64K for vm86 himem */
125                 if ((0x110000 + len) <= addr) {
126                         addr = (PAGE_MASK & addr) - len;
127                 }
128                 else { /* start_code is too low */
129                         addr = 0;
130                 }
131         }
132         addr = get_unmapped_area_prot(NULL, addr, len, 0,
133                                       flags, PROT_READ | PROT_EXEC);
134         if (unlikely(addr & ~PAGE_MASK)) {
135                 up_write(&current->mm->mmap_sem);
136                 return addr;
137         }
138         err = install_special_mapping(current->mm, addr, len,
139                                       VM_DONTEXPAND | VM_READ | VM_EXEC |
140                                       VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC,
141                                       PAGE_READONLY_EXEC,
142                                       sysenter_pages);
143         if (likely(err == 0)) {
144                 current->mm->context.vdso = (void *)addr;
145                 ti->sysenter_return = &SYSENTER_RETURN_OFFSET + addr;
146         }
147         up_write(&current->mm->mmap_sem);
148         return err;
149 }
150
151 #ifndef CONFIG_XEN
152 int in_gate_area_no_task(unsigned long addr)
153 {
154         return 0;
155 }
156
157 int in_gate_area(struct task_struct *task, unsigned long addr)
158 {
159         return 0;
160 }
161
162 struct vm_area_struct *get_gate_vma(struct task_struct *tsk)
163 {
164         return NULL;
165 }
166 #endif