diff options
author | Jeremy Fitzhardinge <jeremy@goop.org> | 2006-12-06 20:14:03 -0500 |
---|---|---|
committer | Andi Kleen <andi@basil.nowhere.org> | 2006-12-06 20:14:03 -0500 |
commit | 49d26b6eaa8e970c8cf6e299e6ccba2474191bf5 (patch) | |
tree | 174f586b64652e06ce0de8f4db4f94a4eec456cc /arch/i386/kernel/vm86.c | |
parent | 66e10a44d724f1464b5e8b5a3eae1e2cbbc2cca6 (diff) |
[PATCH] i386: Update sys_vm86 to cope with changed pt_regs and %gs usage
sys_vm86 uses a struct kernel_vm86_regs, which is identical to pt_regs, but
adds an extra space for all the segment registers. Previously this structure
was completely independent, so changes in pt_regs had to be reflected in
kernel_vm86_regs. This changes just embeds pt_regs in kernel_vm86_regs, and
makes the appropriate changes to vm86.c to deal with the new naming.
Also, since %gs is dealt with differently in the kernel, this change adjusts
vm86.c to reflect this.
While making these changes, I also cleaned up some frankly bizarre code which
was added when auditing was added to sys_vm86.
Signed-off-by: Jeremy Fitzhardinge <jeremy@xensource.com>
Signed-off-by: Andi Kleen <ak@suse.de>
Cc: Chuck Ebbert <76306.1226@compuserve.com>
Cc: Zachary Amsden <zach@vmware.com>
Cc: Jan Beulich <jbeulich@novell.com>
Cc: Andi Kleen <ak@suse.de>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Jason Baron <jbaron@redhat.com>
Cc: Chris Wright <chrisw@sous-sol.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Diffstat (limited to 'arch/i386/kernel/vm86.c')
-rw-r--r-- | arch/i386/kernel/vm86.c | 121 |
1 files changed, 74 insertions, 47 deletions
diff --git a/arch/i386/kernel/vm86.c b/arch/i386/kernel/vm86.c index cbcd61d6120b..be2f96e67f78 100644 --- a/arch/i386/kernel/vm86.c +++ b/arch/i386/kernel/vm86.c | |||
@@ -43,6 +43,7 @@ | |||
43 | #include <linux/highmem.h> | 43 | #include <linux/highmem.h> |
44 | #include <linux/ptrace.h> | 44 | #include <linux/ptrace.h> |
45 | #include <linux/audit.h> | 45 | #include <linux/audit.h> |
46 | #include <linux/stddef.h> | ||
46 | 47 | ||
47 | #include <asm/uaccess.h> | 48 | #include <asm/uaccess.h> |
48 | #include <asm/io.h> | 49 | #include <asm/io.h> |
@@ -72,10 +73,10 @@ | |||
72 | /* | 73 | /* |
73 | * 8- and 16-bit register defines.. | 74 | * 8- and 16-bit register defines.. |
74 | */ | 75 | */ |
75 | #define AL(regs) (((unsigned char *)&((regs)->eax))[0]) | 76 | #define AL(regs) (((unsigned char *)&((regs)->pt.eax))[0]) |
76 | #define AH(regs) (((unsigned char *)&((regs)->eax))[1]) | 77 | #define AH(regs) (((unsigned char *)&((regs)->pt.eax))[1]) |
77 | #define IP(regs) (*(unsigned short *)&((regs)->eip)) | 78 | #define IP(regs) (*(unsigned short *)&((regs)->pt.eip)) |
78 | #define SP(regs) (*(unsigned short *)&((regs)->esp)) | 79 | #define SP(regs) (*(unsigned short *)&((regs)->pt.esp)) |
79 | 80 | ||
80 | /* | 81 | /* |
81 | * virtual flags (16 and 32-bit versions) | 82 | * virtual flags (16 and 32-bit versions) |
@@ -89,10 +90,37 @@ | |||
89 | #define SAFE_MASK (0xDD5) | 90 | #define SAFE_MASK (0xDD5) |
90 | #define RETURN_MASK (0xDFF) | 91 | #define RETURN_MASK (0xDFF) |
91 | 92 | ||
92 | #define VM86_REGS_PART2 orig_eax | 93 | /* convert kernel_vm86_regs to vm86_regs */ |
93 | #define VM86_REGS_SIZE1 \ | 94 | static int copy_vm86_regs_to_user(struct vm86_regs __user *user, |
94 | ( (unsigned)( & (((struct kernel_vm86_regs *)0)->VM86_REGS_PART2) ) ) | 95 | const struct kernel_vm86_regs *regs) |
95 | #define VM86_REGS_SIZE2 (sizeof(struct kernel_vm86_regs) - VM86_REGS_SIZE1) | 96 | { |
97 | int ret = 0; | ||
98 | |||
99 | /* kernel_vm86_regs is missing xfs, so copy everything up to | ||
100 | (but not including) xgs, and then rest after xgs. */ | ||
101 | ret += copy_to_user(user, regs, offsetof(struct kernel_vm86_regs, pt.xgs)); | ||
102 | ret += copy_to_user(&user->__null_gs, ®s->pt.xgs, | ||
103 | sizeof(struct kernel_vm86_regs) - | ||
104 | offsetof(struct kernel_vm86_regs, pt.xgs)); | ||
105 | |||
106 | return ret; | ||
107 | } | ||
108 | |||
109 | /* convert vm86_regs to kernel_vm86_regs */ | ||
110 | static int copy_vm86_regs_from_user(struct kernel_vm86_regs *regs, | ||
111 | const struct vm86_regs __user *user, | ||
112 | unsigned extra) | ||
113 | { | ||
114 | int ret = 0; | ||
115 | |||
116 | ret += copy_from_user(regs, user, offsetof(struct kernel_vm86_regs, pt.xgs)); | ||
117 | ret += copy_from_user(®s->pt.xgs, &user->__null_gs, | ||
118 | sizeof(struct kernel_vm86_regs) - | ||
119 | offsetof(struct kernel_vm86_regs, pt.xgs) + | ||
120 | extra); | ||
121 | |||
122 | return ret; | ||
123 | } | ||
96 | 124 | ||
97 | struct pt_regs * FASTCALL(save_v86_state(struct kernel_vm86_regs * regs)); | 125 | struct pt_regs * FASTCALL(save_v86_state(struct kernel_vm86_regs * regs)); |
98 | struct pt_regs * fastcall save_v86_state(struct kernel_vm86_regs * regs) | 126 | struct pt_regs * fastcall save_v86_state(struct kernel_vm86_regs * regs) |
@@ -112,10 +140,8 @@ struct pt_regs * fastcall save_v86_state(struct kernel_vm86_regs * regs) | |||
112 | printk("no vm86_info: BAD\n"); | 140 | printk("no vm86_info: BAD\n"); |
113 | do_exit(SIGSEGV); | 141 | do_exit(SIGSEGV); |
114 | } | 142 | } |
115 | set_flags(regs->eflags, VEFLAGS, VIF_MASK | current->thread.v86mask); | 143 | set_flags(regs->pt.eflags, VEFLAGS, VIF_MASK | current->thread.v86mask); |
116 | tmp = copy_to_user(¤t->thread.vm86_info->regs,regs, VM86_REGS_SIZE1); | 144 | tmp = copy_vm86_regs_to_user(¤t->thread.vm86_info->regs,regs); |
117 | tmp += copy_to_user(¤t->thread.vm86_info->regs.VM86_REGS_PART2, | ||
118 | ®s->VM86_REGS_PART2, VM86_REGS_SIZE2); | ||
119 | tmp += put_user(current->thread.screen_bitmap,¤t->thread.vm86_info->screen_bitmap); | 145 | tmp += put_user(current->thread.screen_bitmap,¤t->thread.vm86_info->screen_bitmap); |
120 | if (tmp) { | 146 | if (tmp) { |
121 | printk("vm86: could not access userspace vm86_info\n"); | 147 | printk("vm86: could not access userspace vm86_info\n"); |
@@ -129,9 +155,11 @@ struct pt_regs * fastcall save_v86_state(struct kernel_vm86_regs * regs) | |||
129 | current->thread.saved_esp0 = 0; | 155 | current->thread.saved_esp0 = 0; |
130 | put_cpu(); | 156 | put_cpu(); |
131 | 157 | ||
132 | loadsegment(fs, current->thread.saved_fs); | ||
133 | loadsegment(gs, current->thread.saved_gs); | ||
134 | ret = KVM86->regs32; | 158 | ret = KVM86->regs32; |
159 | |||
160 | loadsegment(fs, current->thread.saved_fs); | ||
161 | ret->xgs = current->thread.saved_gs; | ||
162 | |||
135 | return ret; | 163 | return ret; |
136 | } | 164 | } |
137 | 165 | ||
@@ -183,9 +211,9 @@ asmlinkage int sys_vm86old(struct pt_regs regs) | |||
183 | tsk = current; | 211 | tsk = current; |
184 | if (tsk->thread.saved_esp0) | 212 | if (tsk->thread.saved_esp0) |
185 | goto out; | 213 | goto out; |
186 | tmp = copy_from_user(&info, v86, VM86_REGS_SIZE1); | 214 | tmp = copy_vm86_regs_from_user(&info.regs, &v86->regs, |
187 | tmp += copy_from_user(&info.regs.VM86_REGS_PART2, &v86->regs.VM86_REGS_PART2, | 215 | offsetof(struct kernel_vm86_struct, vm86plus) - |
188 | (long)&info.vm86plus - (long)&info.regs.VM86_REGS_PART2); | 216 | sizeof(info.regs)); |
189 | ret = -EFAULT; | 217 | ret = -EFAULT; |
190 | if (tmp) | 218 | if (tmp) |
191 | goto out; | 219 | goto out; |
@@ -233,9 +261,9 @@ asmlinkage int sys_vm86(struct pt_regs regs) | |||
233 | if (tsk->thread.saved_esp0) | 261 | if (tsk->thread.saved_esp0) |
234 | goto out; | 262 | goto out; |
235 | v86 = (struct vm86plus_struct __user *)regs.ecx; | 263 | v86 = (struct vm86plus_struct __user *)regs.ecx; |
236 | tmp = copy_from_user(&info, v86, VM86_REGS_SIZE1); | 264 | tmp = copy_vm86_regs_from_user(&info.regs, &v86->regs, |
237 | tmp += copy_from_user(&info.regs.VM86_REGS_PART2, &v86->regs.VM86_REGS_PART2, | 265 | offsetof(struct kernel_vm86_struct, regs32) - |
238 | (long)&info.regs32 - (long)&info.regs.VM86_REGS_PART2); | 266 | sizeof(info.regs)); |
239 | ret = -EFAULT; | 267 | ret = -EFAULT; |
240 | if (tmp) | 268 | if (tmp) |
241 | goto out; | 269 | goto out; |
@@ -252,15 +280,15 @@ out: | |||
252 | static void do_sys_vm86(struct kernel_vm86_struct *info, struct task_struct *tsk) | 280 | static void do_sys_vm86(struct kernel_vm86_struct *info, struct task_struct *tsk) |
253 | { | 281 | { |
254 | struct tss_struct *tss; | 282 | struct tss_struct *tss; |
255 | long eax; | ||
256 | /* | 283 | /* |
257 | * make sure the vm86() system call doesn't try to do anything silly | 284 | * make sure the vm86() system call doesn't try to do anything silly |
258 | */ | 285 | */ |
259 | info->regs.__null_ds = 0; | 286 | info->regs.pt.xds = 0; |
260 | info->regs.__null_es = 0; | 287 | info->regs.pt.xes = 0; |
288 | info->regs.pt.xgs = 0; | ||
261 | 289 | ||
262 | /* we are clearing fs,gs later just before "jmp resume_userspace", | 290 | /* we are clearing fs later just before "jmp resume_userspace", |
263 | * because starting with Linux 2.1.x they aren't no longer saved/restored | 291 | * because it is not saved/restored. |
264 | */ | 292 | */ |
265 | 293 | ||
266 | /* | 294 | /* |
@@ -268,10 +296,10 @@ static void do_sys_vm86(struct kernel_vm86_struct *info, struct task_struct *tsk | |||
268 | * has set it up safely, so this makes sure interrupt etc flags are | 296 | * has set it up safely, so this makes sure interrupt etc flags are |
269 | * inherited from protected mode. | 297 | * inherited from protected mode. |
270 | */ | 298 | */ |
271 | VEFLAGS = info->regs.eflags; | 299 | VEFLAGS = info->regs.pt.eflags; |
272 | info->regs.eflags &= SAFE_MASK; | 300 | info->regs.pt.eflags &= SAFE_MASK; |
273 | info->regs.eflags |= info->regs32->eflags & ~SAFE_MASK; | 301 | info->regs.pt.eflags |= info->regs32->eflags & ~SAFE_MASK; |
274 | info->regs.eflags |= VM_MASK; | 302 | info->regs.pt.eflags |= VM_MASK; |
275 | 303 | ||
276 | switch (info->cpu_type) { | 304 | switch (info->cpu_type) { |
277 | case CPU_286: | 305 | case CPU_286: |
@@ -294,7 +322,7 @@ static void do_sys_vm86(struct kernel_vm86_struct *info, struct task_struct *tsk | |||
294 | info->regs32->eax = 0; | 322 | info->regs32->eax = 0; |
295 | tsk->thread.saved_esp0 = tsk->thread.esp0; | 323 | tsk->thread.saved_esp0 = tsk->thread.esp0; |
296 | savesegment(fs, tsk->thread.saved_fs); | 324 | savesegment(fs, tsk->thread.saved_fs); |
297 | savesegment(gs, tsk->thread.saved_gs); | 325 | tsk->thread.saved_gs = info->regs32->xgs; |
298 | 326 | ||
299 | tss = &per_cpu(init_tss, get_cpu()); | 327 | tss = &per_cpu(init_tss, get_cpu()); |
300 | tsk->thread.esp0 = (unsigned long) &info->VM86_TSS_ESP0; | 328 | tsk->thread.esp0 = (unsigned long) &info->VM86_TSS_ESP0; |
@@ -306,19 +334,18 @@ static void do_sys_vm86(struct kernel_vm86_struct *info, struct task_struct *tsk | |||
306 | tsk->thread.screen_bitmap = info->screen_bitmap; | 334 | tsk->thread.screen_bitmap = info->screen_bitmap; |
307 | if (info->flags & VM86_SCREEN_BITMAP) | 335 | if (info->flags & VM86_SCREEN_BITMAP) |
308 | mark_screen_rdonly(tsk->mm); | 336 | mark_screen_rdonly(tsk->mm); |
309 | __asm__ __volatile__("xorl %eax,%eax; movl %eax,%fs; movl %eax,%gs\n\t"); | ||
310 | __asm__ __volatile__("movl %%eax, %0\n" :"=r"(eax)); | ||
311 | 337 | ||
312 | /*call audit_syscall_exit since we do not exit via the normal paths */ | 338 | /*call audit_syscall_exit since we do not exit via the normal paths */ |
313 | if (unlikely(current->audit_context)) | 339 | if (unlikely(current->audit_context)) |
314 | audit_syscall_exit(AUDITSC_RESULT(eax), eax); | 340 | audit_syscall_exit(AUDITSC_RESULT(0), 0); |
315 | 341 | ||
316 | __asm__ __volatile__( | 342 | __asm__ __volatile__( |
317 | "movl %0,%%esp\n\t" | 343 | "movl %0,%%esp\n\t" |
318 | "movl %1,%%ebp\n\t" | 344 | "movl %1,%%ebp\n\t" |
345 | "mov %2, %%fs\n\t" | ||
319 | "jmp resume_userspace" | 346 | "jmp resume_userspace" |
320 | : /* no outputs */ | 347 | : /* no outputs */ |
321 | :"r" (&info->regs), "r" (task_thread_info(tsk))); | 348 | :"r" (&info->regs), "r" (task_thread_info(tsk)), "r" (0)); |
322 | /* we never return here */ | 349 | /* we never return here */ |
323 | } | 350 | } |
324 | 351 | ||
@@ -348,12 +375,12 @@ static inline void clear_IF(struct kernel_vm86_regs * regs) | |||
348 | 375 | ||
349 | static inline void clear_TF(struct kernel_vm86_regs * regs) | 376 | static inline void clear_TF(struct kernel_vm86_regs * regs) |
350 | { | 377 | { |
351 | regs->eflags &= ~TF_MASK; | 378 | regs->pt.eflags &= ~TF_MASK; |
352 | } | 379 | } |
353 | 380 | ||
354 | static inline void clear_AC(struct kernel_vm86_regs * regs) | 381 | static inline void clear_AC(struct kernel_vm86_regs * regs) |
355 | { | 382 | { |
356 | regs->eflags &= ~AC_MASK; | 383 | regs->pt.eflags &= ~AC_MASK; |
357 | } | 384 | } |
358 | 385 | ||
359 | /* It is correct to call set_IF(regs) from the set_vflags_* | 386 | /* It is correct to call set_IF(regs) from the set_vflags_* |
@@ -370,7 +397,7 @@ static inline void clear_AC(struct kernel_vm86_regs * regs) | |||
370 | static inline void set_vflags_long(unsigned long eflags, struct kernel_vm86_regs * regs) | 397 | static inline void set_vflags_long(unsigned long eflags, struct kernel_vm86_regs * regs) |
371 | { | 398 | { |
372 | set_flags(VEFLAGS, eflags, current->thread.v86mask); | 399 | set_flags(VEFLAGS, eflags, current->thread.v86mask); |
373 | set_flags(regs->eflags, eflags, SAFE_MASK); | 400 | set_flags(regs->pt.eflags, eflags, SAFE_MASK); |
374 | if (eflags & IF_MASK) | 401 | if (eflags & IF_MASK) |
375 | set_IF(regs); | 402 | set_IF(regs); |
376 | else | 403 | else |
@@ -380,7 +407,7 @@ static inline void set_vflags_long(unsigned long eflags, struct kernel_vm86_regs | |||
380 | static inline void set_vflags_short(unsigned short flags, struct kernel_vm86_regs * regs) | 407 | static inline void set_vflags_short(unsigned short flags, struct kernel_vm86_regs * regs) |
381 | { | 408 | { |
382 | set_flags(VFLAGS, flags, current->thread.v86mask); | 409 | set_flags(VFLAGS, flags, current->thread.v86mask); |
383 | set_flags(regs->eflags, flags, SAFE_MASK); | 410 | set_flags(regs->pt.eflags, flags, SAFE_MASK); |
384 | if (flags & IF_MASK) | 411 | if (flags & IF_MASK) |
385 | set_IF(regs); | 412 | set_IF(regs); |
386 | else | 413 | else |
@@ -389,7 +416,7 @@ static inline void set_vflags_short(unsigned short flags, struct kernel_vm86_reg | |||
389 | 416 | ||
390 | static inline unsigned long get_vflags(struct kernel_vm86_regs * regs) | 417 | static inline unsigned long get_vflags(struct kernel_vm86_regs * regs) |
391 | { | 418 | { |
392 | unsigned long flags = regs->eflags & RETURN_MASK; | 419 | unsigned long flags = regs->pt.eflags & RETURN_MASK; |
393 | 420 | ||
394 | if (VEFLAGS & VIF_MASK) | 421 | if (VEFLAGS & VIF_MASK) |
395 | flags |= IF_MASK; | 422 | flags |= IF_MASK; |
@@ -493,7 +520,7 @@ static void do_int(struct kernel_vm86_regs *regs, int i, | |||
493 | unsigned long __user *intr_ptr; | 520 | unsigned long __user *intr_ptr; |
494 | unsigned long segoffs; | 521 | unsigned long segoffs; |
495 | 522 | ||
496 | if (regs->cs == BIOSSEG) | 523 | if (regs->pt.xcs == BIOSSEG) |
497 | goto cannot_handle; | 524 | goto cannot_handle; |
498 | if (is_revectored(i, &KVM86->int_revectored)) | 525 | if (is_revectored(i, &KVM86->int_revectored)) |
499 | goto cannot_handle; | 526 | goto cannot_handle; |
@@ -505,9 +532,9 @@ static void do_int(struct kernel_vm86_regs *regs, int i, | |||
505 | if ((segoffs >> 16) == BIOSSEG) | 532 | if ((segoffs >> 16) == BIOSSEG) |
506 | goto cannot_handle; | 533 | goto cannot_handle; |
507 | pushw(ssp, sp, get_vflags(regs), cannot_handle); | 534 | pushw(ssp, sp, get_vflags(regs), cannot_handle); |
508 | pushw(ssp, sp, regs->cs, cannot_handle); | 535 | pushw(ssp, sp, regs->pt.xcs, cannot_handle); |
509 | pushw(ssp, sp, IP(regs), cannot_handle); | 536 | pushw(ssp, sp, IP(regs), cannot_handle); |
510 | regs->cs = segoffs >> 16; | 537 | regs->pt.xcs = segoffs >> 16; |
511 | SP(regs) -= 6; | 538 | SP(regs) -= 6; |
512 | IP(regs) = segoffs & 0xffff; | 539 | IP(regs) = segoffs & 0xffff; |
513 | clear_TF(regs); | 540 | clear_TF(regs); |
@@ -524,7 +551,7 @@ int handle_vm86_trap(struct kernel_vm86_regs * regs, long error_code, int trapno | |||
524 | if (VMPI.is_vm86pus) { | 551 | if (VMPI.is_vm86pus) { |
525 | if ( (trapno==3) || (trapno==1) ) | 552 | if ( (trapno==3) || (trapno==1) ) |
526 | return_to_32bit(regs, VM86_TRAP + (trapno << 8)); | 553 | return_to_32bit(regs, VM86_TRAP + (trapno << 8)); |
527 | do_int(regs, trapno, (unsigned char __user *) (regs->ss << 4), SP(regs)); | 554 | do_int(regs, trapno, (unsigned char __user *) (regs->pt.xss << 4), SP(regs)); |
528 | return 0; | 555 | return 0; |
529 | } | 556 | } |
530 | if (trapno !=1) | 557 | if (trapno !=1) |
@@ -560,10 +587,10 @@ void handle_vm86_fault(struct kernel_vm86_regs * regs, long error_code) | |||
560 | handle_vm86_trap(regs, 0, 1); \ | 587 | handle_vm86_trap(regs, 0, 1); \ |
561 | return; } while (0) | 588 | return; } while (0) |
562 | 589 | ||
563 | orig_flags = *(unsigned short *)®s->eflags; | 590 | orig_flags = *(unsigned short *)®s->pt.eflags; |
564 | 591 | ||
565 | csp = (unsigned char __user *) (regs->cs << 4); | 592 | csp = (unsigned char __user *) (regs->pt.xcs << 4); |
566 | ssp = (unsigned char __user *) (regs->ss << 4); | 593 | ssp = (unsigned char __user *) (regs->pt.xss << 4); |
567 | sp = SP(regs); | 594 | sp = SP(regs); |
568 | ip = IP(regs); | 595 | ip = IP(regs); |
569 | 596 | ||
@@ -650,7 +677,7 @@ void handle_vm86_fault(struct kernel_vm86_regs * regs, long error_code) | |||
650 | SP(regs) += 6; | 677 | SP(regs) += 6; |
651 | } | 678 | } |
652 | IP(regs) = newip; | 679 | IP(regs) = newip; |
653 | regs->cs = newcs; | 680 | regs->pt.xcs = newcs; |
654 | CHECK_IF_IN_TRAP; | 681 | CHECK_IF_IN_TRAP; |
655 | if (data32) { | 682 | if (data32) { |
656 | set_vflags_long(newflags, regs); | 683 | set_vflags_long(newflags, regs); |