Skip to contentSkip to conversation

x86: vm86 cleanups

PATCH Superseded Brian Gerst 2 participants 16 messages Jul 16, 2015 – Jul 17, 2015

Subsystems (3)

v2 of 4 Superseded Latest is v4 →

lore
Share & tools
lore
#0 lore raw

PATCH v2 1/1 x86: vm86 cleanups

The goal of this set of patches is to change vm86 support to return to userspace with the normal exit paths instead of leaving data on the kernel stack and jumping directly into the exit asm routines. This fixes issues like ptrace and syscall auditing not working with vm86, and makes possible cleanups in the syscall exit work code.

Changes from v1:
- Added first two patches
- Changed userspace access to copy each field explicitly instead of relying on the same order of members in the structure.

In-Reply-To:

#1 lore raw

PATCH 1/7 x86/vm86: Clean up saved_fs/gs

There is no need to save FS and non-lazy GS outside the 32-bit regs. Lazy GS still needs to be saved because it wasn't saved on syscall entry.

---
 arch/x86/include/asm/processor.h | 3 ++-
 arch/x86/kernel/vm86_32.c        | 6 ++----
 2 files changed, 4 insertions(+), 5 deletions(-)
arch/x86/include/asm/processor.h +2 -1
index 43e6519..a4b5c6a 100644
@@ -410,9 +410,10 @@ struct thread_struct {
 	unsigned long		v86flags;
 	unsigned long		v86mask;
 	unsigned long		saved_sp0;
-	unsigned int		saved_fs;
+#ifdef CONFIG_X86_32_LAZY_GS
 	unsigned int		saved_gs;
 #endif
+#endif
 	/* IO permissions: */
 	unsigned long		*io_bitmap_ptr;
 	unsigned long		iopl;
arch/x86/kernel/vm86_32.c +2 -4
index fc9db6e..75eab83 100644
@@ -159,8 +159,7 @@ struct pt_regs *save_v86_state(struct kernel_vm86_regs *regs)
 
 	ret = KVM86->regs32;
 
-	ret->fs = current->thread.saved_fs;
-	set_user_gs(ret, current->thread.saved_gs);
+	lazy_load_gs(current->thread.saved_gs);
 
 	return ret;
 }
@@ -315,8 +314,7 @@ static void do_sys_vm86(struct kernel_vm86_struct *info, struct task_struct *tsk
  */
 	info->regs32->ax = VM86_SIGNAL;
 	tsk->thread.saved_sp0 = tsk->thread.sp0;
-	tsk->thread.saved_fs = info->regs32->fs;
-	tsk->thread.saved_gs = get_user_gs(info->regs32);
+	lazy_save_gs(tsk->thread.saved_gs);
 
 	tss = &per_cpu(cpu_tss, get_cpu());
 	tsk->thread.sp0 = (unsigned long) &info->VM86_TSS_ESP0;
Signature · 1 line
2.4.3
#2 lore raw

Re: [PATCH 1/7] x86/vm86: Clean up saved_fs/gs

Quoted 2 lines · Brian Gerst

There is no need to save FS and non-lazy GS outside the 32-bit regs. Lazy GS still needs to be saved because it wasn't saved on syscall entry.

I think I'm missing something. Why can't you just lazy_save_gs into regs32->gs?

--Andy

#3 lore raw

Re: [PATCH 1/7] x86/vm86: Clean up saved_fs/gs

Quoted 7 lines · Andy Lutomirski
Quoted 2 lines · Brian Gerst

There is no need to save FS and non-lazy GS outside the 32-bit regs. Lazy GS still needs to be saved because it wasn't saved on syscall entry.

I think I'm missing something. Why can't you just lazy_save_gs into regs32->gs?

--Andy

That is probably a better idea. The gs field exists in pt_regs but is unused in lazy mode. Could also change switch_to() to store GS in pt_regs instead of thread.gs, which would simplify ptrace a bit.

--
Brian Gerst

#4 lore raw

PATCH 2/7 x86/vm86: Preserve orig_ax

In reply to Brian Gerst

There is no legitimate reason for usermode to modify the orig_ax field on entry to vm86 mode, so copy it from the 32-bit regs.

---
 arch/x86/kernel/vm86_32.c | 2 ++
 1 file changed, 2 insertions(+)
arch/x86/kernel/vm86_32.c +2 -0
index 75eab83..8424290 100644
@@ -294,6 +294,8 @@ static void do_sys_vm86(struct kernel_vm86_struct *info, struct task_struct *tsk
 	info->regs.pt.flags |= info->regs32->flags & ~SAFE_MASK;
 	info->regs.pt.flags |= X86_VM_MASK;
 
+	info->regs.pt.orig_ax = info->regs32->orig_ax;
+
 	switch (info->cpu_type) {
 	case CPU_286:
 		tsk->thread.v86mask = 0;
Signature · 1 line
2.4.3
#5 lore raw

Re: [PATCH 2/7] x86/vm86: Preserve orig_ax

Quoted 2 lines · Brian Gerst

There is no legitimate reason for usermode to modify the orig_ax field on entry to vm86 mode, so copy it from the 32-bit regs.

Looks good.

--Andy

#6 lore raw

PATCH 3/7 x86/vm86: Move userspace accesses to do_sys_vm86()

In reply to Brian Gerst

Move the userspace accesses down into the common function in preparation for the next set of patches.

---
 arch/x86/include/asm/processor.h |   2 +-
 arch/x86/kernel/vm86_32.c        | 176 +++++++++++++++++++--------------------
 2 files changed, 89 insertions(+), 89 deletions(-)
arch/x86/include/asm/processor.h +1 -1
index a4b5c6a..98190a6 100644
@@ -405,7 +405,7 @@ struct thread_struct {
 	unsigned long		error_code;
 #ifdef CONFIG_X86_32
 	/* Virtual 86 mode info */
-	struct vm86_struct __user *vm86_info;
+	struct vm86plus_struct __user *vm86_info;
 	unsigned long		screen_bitmap;
 	unsigned long		v86flags;
 	unsigned long		v86mask;
arch/x86/kernel/vm86_32.c +88 -88

237 lines not on this page · raw message

Signature · 1 line
2.4.3
#7 lore raw

Re: [PATCH 3/7] x86/vm86: Move userspace accesses to do_sys_vm86()

Quoted 2 lines · Brian Gerst

Move the userspace accesses down into the common function in preparation for the next set of patches.

Nice!

Could you improve the changelog a bit? You're really changing the way it works (in a good way).

arch/x86/kernel/vm86_32.c struct pt_regs *save_v86_state(struct kernel_vm86_regs *regs) · line 114

Quoted 7 lines
arch/x86/kernel/vm86_32.c +3 -4
-       set_flags(regs->pt.flags, VEFLAGS, X86_EFLAGS_VIF | current->thread.v86mask);
-       tmp = copy_vm86_regs_to_user(&current->thread.vm86_info->regs, regs);
-       tmp += put_user(current->thread.screen_bitmap, &current->thread.vm86_info->screen_bitmap);
-       if (tmp) {
+       set_flags(regs->pt.flags, VEFLAGS, X86_EFLAGS_VIF | tsk->thread.v86mask);
+       user = tsk->thread.vm86_info;
+       put_user_try {

I think that put_user_try/put_user_ex requires access_ok.

arch/x86/kernel/vm86_32.c SYSCALL_DEFINE2(vm86, unsigned long, cmd, unsigned long, arg) · line 244

Quoted 2 lines
arch/x86/kernel/vm86_32.c +2 -0
+       get_user_try {
+               unsigned short seg;

This also needs access_ok, I think.

--Andy

#8 lore raw

Re: [PATCH 3/7] x86/vm86: Move userspace accesses to do_sys_vm86()

Quoted 18 lines · Andy Lutomirski
Quoted 2 lines · Brian Gerst

Move the userspace accesses down into the common function in preparation for the next set of patches.

Nice!

Could you improve the changelog a bit? You're really changing the way it works (in a good way).

arch/x86/kernel/vm86_32.c struct pt_regs *save_v86_state(struct kernel_vm86_regs *regs) · line 114

Quoted 7 lines
arch/x86/kernel/vm86_32.c +3 -4
-       set_flags(regs->pt.flags, VEFLAGS, X86_EFLAGS_VIF | current->thread.v86mask);
-       tmp = copy_vm86_regs_to_user(&current->thread.vm86_info->regs, regs);
-       tmp += put_user(current->thread.screen_bitmap, &current->thread.vm86_info->screen_bitmap);
-       if (tmp) {
+       set_flags(regs->pt.flags, VEFLAGS, X86_EFLAGS_VIF | tsk->thread.v86mask);
+       user = tsk->thread.vm86_info;
+       put_user_try {
Quoted 7 lines

I think that put_user_try/put_user_ex requires access_ok.

arch/x86/kernel/vm86_32.c SYSCALL_DEFINE2(vm86, unsigned long, cmd, unsigned long, arg) · line 244

Quoted 2 lines
arch/x86/kernel/vm86_32.c +2 -0
+       get_user_try {
+               unsigned short seg;

This also needs access_ok, I think.

Good catch. I modeled this off restore_sigcontext(), but the access_ok() is actually in the caller.

--
Brian Gerst

#9 lore raw

PATCH 4/7 x86/vm86: Move vm86 fields out of thread_struct

In reply to Brian Gerst

Allocate a separate structure for the vm86 fields.

---
 arch/x86/include/asm/processor.h | 12 ++--------
 arch/x86/include/asm/vm86.h      | 11 +++++++++
 arch/x86/kernel/process.c        |  7 ++++++
 arch/x86/kernel/vm86_32.c        | 51 +++++++++++++++++++++++-----------------
 arch/x86/mm/fault.c              |  4 ++--
 5 files changed, 52 insertions(+), 33 deletions(-)
arch/x86/include/asm/processor.h +2 -10
index 98190a6..d636fb8 100644
@@ -403,16 +403,9 @@ struct thread_struct {
 	unsigned long		cr2;
 	unsigned long		trap_nr;
 	unsigned long		error_code;
-#ifdef CONFIG_X86_32
+#ifdef CONFIG_VM86
 	/* Virtual 86 mode info */
-	struct vm86plus_struct __user *vm86_info;
-	unsigned long		screen_bitmap;
-	unsigned long		v86flags;
-	unsigned long		v86mask;
-	unsigned long		saved_sp0;
-#ifdef CONFIG_X86_32_LAZY_GS
-	unsigned int		saved_gs;
-#endif
+	struct kernel_vm86_info *vm86;
 #endif
 	/* IO permissions: */
 	unsigned long		*io_bitmap_ptr;
@@ -717,7 +710,6 @@ static inline void spin_lock_prefetch(const void *x)
 
 #define INIT_THREAD  {							  \
 	.sp0			= TOP_OF_INIT_STACK,			  \
-	.vm86_info		= NULL,					  \
 	.sysenter_cs		= __KERNEL_CS,				  \
 	.io_bitmap_ptr		= NULL,					  \
 }
arch/x86/include/asm/vm86.h +11 -0
index 1d8de3f..07de263 100644
@@ -58,6 +58,17 @@ struct kernel_vm86_struct {
  */
 };
 
+struct kernel_vm86_info {
+	struct vm86plus_struct __user *vm86_info;
+	unsigned long screen_bitmap;
+	unsigned long v86flags;
+	unsigned long v86mask;
+	unsigned long saved_sp0;
+#ifdef CONFIG_X86_32_LAZY_GS
+	unsigned int saved_gs;
+#endif
+};
+
 #ifdef CONFIG_VM86
 
 void handle_vm86_fault(struct kernel_vm86_regs *, long);
arch/x86/kernel/process.c +7 -0
index 9cad694..5dcd037 100644
@@ -110,6 +110,13 @@ void exit_thread(void)
 		kfree(bp);
 	}
 
+#ifdef CONFIG_VM86
+	if (t->vm86) {
+		kfree(t->vm86);
+		t->vm86 = NULL;
+	}
+#endif
+
 	fpu__drop(fpu);
 }
 
arch/x86/kernel/vm86_32.c +30 -21

152 lines not on this page · raw message

arch/x86/mm/fault.c +2 -2
index 81dcebf..5196ac4 100644
@@ -315,12 +315,12 @@ check_v8086_mode(struct pt_regs *regs, unsigned long address,
 {
 	unsigned long bit;
 
-	if (!v8086_mode(regs))
+	if (!v8086_mode(regs) || !tsk->thread.vm86)
 		return;
 
 	bit = (address - 0xA0000) >> PAGE_SHIFT;
 	if (bit < 32)
-		tsk->thread.screen_bitmap |= 1 << bit;
+		tsk->thread.vm86->screen_bitmap |= 1 << bit;
 }
 
 static bool low_pfn(unsigned long pfn)
Signature · 1 line
2.4.3
#10 lore raw

Re: [PATCH 4/7] x86/vm86: Move vm86 fields out of thread_struct

arch/x86/mm/fault.c check_v8086_mode(struct pt_regs *regs, unsigned long address, · line 320

Quoted 13 lines · Brian Gerst

Allocate a separate structure for the vm86 fields.

arch/x86/mm/fault.c +1 -1
@@ -315,12 +315,12 @@ check_v8086_mode(struct pt_regs *regs, unsigned long address,
 {
        unsigned long bit;
 
-       if (!v8086_mode(regs))
+       if (!v8086_mode(regs) || !tsk->thread.vm86)
                return;
 

What's this for? Shouldn't that "if (!v8086_mode(regs) || WARN_ON(!tsk->thread.vm86))"?

--Andy

#11 lore raw

PATCH 5/7 x86/vm86: Move fields from kernel_vm86_struct

In reply to Brian Gerst

Move the non-regs fields to the off-stack data.

---
 arch/x86/include/asm/vm86.h | 16 ++++++++--------
 arch/x86/kernel/vm86_32.c   | 40 +++++++++++++++++++++-------------------
 2 files changed, 29 insertions(+), 27 deletions(-)
arch/x86/include/asm/vm86.h +8 -8
index 07de263..8e83e57 100644
@@ -38,13 +38,7 @@ struct kernel_vm86_struct {
  * Therefore, pt_regs in fact points to a complete 'kernel_vm86_struct'
  * in kernelspace, hence we need not reget the data from userspace.
  */
-#define VM86_TSS_ESP0 flags
-	unsigned long flags;
-	unsigned long screen_bitmap;
-	unsigned long cpu_type;
-	struct revectored_struct int_revectored;
-	struct revectored_struct int21_revectored;
-	struct vm86plus_info_struct vm86plus;
+#define VM86_TSS_ESP0 regs32
 	struct pt_regs *regs32;   /* here we save the pointer to the old regs */
 /*
  * The below is not part of the structure, but the stack layout continues
@@ -60,13 +54,19 @@ struct kernel_vm86_struct {
 
 struct kernel_vm86_info {
 	struct vm86plus_struct __user *vm86_info;
-	unsigned long screen_bitmap;
 	unsigned long v86flags;
 	unsigned long v86mask;
 	unsigned long saved_sp0;
 #ifdef CONFIG_X86_32_LAZY_GS
 	unsigned int saved_gs;
 #endif
+
+	unsigned long flags;
+	unsigned long screen_bitmap;
+	unsigned long cpu_type;
+	struct revectored_struct int_revectored;
+	struct revectored_struct int21_revectored;
+	struct vm86plus_info_struct vm86plus;
 };
 
 #ifdef CONFIG_VM86
arch/x86/kernel/vm86_32.c +21 -19
index 07dcab1d..29a9d92 100644
@@ -68,7 +68,6 @@
 
 
 #define KVM86	((struct kernel_vm86_struct *)regs)
-#define VMPI	KVM86->vm86plus
 
 
 /*
@@ -270,25 +269,27 @@ static long do_sys_vm86(struct vm86plus_struct __user *v86, bool plus,
 		get_user_ex(info->regs.fs, &v86->regs.fs);
 		get_user_ex(info->regs.gs, &v86->regs.gs);
 
-		get_user_ex(info->flags, &v86->flags);
-		get_user_ex(info->screen_bitmap, &v86->screen_bitmap);
-		get_user_ex(info->cpu_type, &v86->cpu_type);
+		get_user_ex(vm86->flags, &v86->flags);
+		get_user_ex(vm86->screen_bitmap, &v86->screen_bitmap);
+		get_user_ex(vm86->cpu_type, &v86->cpu_type);
 	} get_user_catch(err);
 	if (err)
 		return err;
 
-	if (copy_from_user(&info->int_revectored, &v86->int_revectored,
+	if (copy_from_user(&vm86->int_revectored, &v86->int_revectored,
 			   sizeof(struct revectored_struct)))
 		return -EFAULT;
-	if (copy_from_user(&info->int21_revectored, &v86->int21_revectored,
+	if (copy_from_user(&vm86->int21_revectored, &v86->int21_revectored,
 			   sizeof(struct revectored_struct)))
 		return -EFAULT;
 	if (plus) {
-		if (copy_from_user(&info->vm86plus, &v86->vm86plus,
+		if (copy_from_user(&vm86->vm86plus, &v86->vm86plus,
 				   sizeof(struct vm86plus_info_struct)))
 			return -EFAULT;
-		info->vm86plus.is_vm86pus = 1;
-	}
+		vm86->vm86plus.is_vm86pus = 1;
+	} else
+		memset(&vm86->vm86plus, 0,
+		       sizeof(struct vm86plus_info_struct));
 
 	info->regs32 = current_pt_regs();
 	vm86->vm86_info = v86;
@@ -305,7 +306,7 @@ static long do_sys_vm86(struct vm86plus_struct __user *v86, bool plus,
 
 	info->regs.pt.orig_ax = info->regs32->orig_ax;
 
-	switch (info->cpu_type) {
+	switch (vm86->cpu_type) {
 	case CPU_286:
 		vm86->v86mask = 0;
 		break;
@@ -334,8 +335,7 @@ static long do_sys_vm86(struct vm86plus_struct __user *v86, bool plus,
 	load_sp0(tss, &tsk->thread);
 	put_cpu();
 
-	vm86->screen_bitmap = info->screen_bitmap;
-	if (info->flags & VM86_SCREEN_BITMAP)
+	if (vm86->flags & VM86_SCREEN_BITMAP)
 		mark_screen_rdonly(tsk->mm);
 
 	/*call __audit_syscall_exit since we do not exit via the normal paths */
@@ -527,12 +527,13 @@ static void do_int(struct kernel_vm86_regs *regs, int i,
 {
 	unsigned long __user *intr_ptr;
 	unsigned long segoffs;
+	struct kernel_vm86_info *vm86 = current->thread.vm86;
 
 	if (regs->pt.cs == BIOSSEG)
 		goto cannot_handle;
-	if (is_revectored(i, &KVM86->int_revectored))
+	if (is_revectored(i, &vm86->int_revectored))
 		goto cannot_handle;
-	if (i == 0x21 && is_revectored(AH(regs), &KVM86->int21_revectored))
+	if (i == 0x21 && is_revectored(AH(regs), &vm86->int21_revectored))
 		goto cannot_handle;
 	intr_ptr = (unsigned long __user *) (i << 2);
 	if (get_user(segoffs, intr_ptr))
@@ -556,7 +557,7 @@ cannot_handle:
 
 int handle_vm86_trap(struct kernel_vm86_regs *regs, long error_code, int trapno)
 {
-	if (VMPI.is_vm86pus) {
+	if (current->thread.vm86->vm86plus.is_vm86pus) {
 		if ((trapno == 3) || (trapno == 1)) {
 			KVM86->regs32->ax = VM86_TRAP + (trapno << 8);
 			/* setting this flag forces the code in entry_32.S to
@@ -583,12 +584,13 @@ void handle_vm86_fault(struct kernel_vm86_regs *regs, long error_code)
 	unsigned char __user *ssp;
 	unsigned short ip, sp, orig_flags;
 	int data32, pref_done;
+	struct vm86plus_info_struct *vmpi = &current->thread.vm86->vm86plus;
 
 #define CHECK_IF_IN_TRAP \
-	if (VMPI.vm86dbg_active && VMPI.vm86dbg_TFpendig) \
+	if (vmpi->vm86dbg_active && vmpi->vm86dbg_TFpendig) \
 		newflags |= X86_EFLAGS_TF
 #define VM86_FAULT_RETURN do { \
-	if (VMPI.force_return_for_pic  && (VEFLAGS & (X86_EFLAGS_IF | X86_EFLAGS_VIF))) \
+	if (vmpi->force_return_for_pic  && (VEFLAGS & (X86_EFLAGS_IF | X86_EFLAGS_VIF))) \
 		return_to_32bit(regs, VM86_PICRETURN); \
 	if (orig_flags & X86_EFLAGS_TF) \
 		handle_vm86_trap(regs, 0, 1); \
@@ -658,8 +660,8 @@ void handle_vm86_fault(struct kernel_vm86_regs *regs, long error_code)
 	case 0xcd: {
 		int intno = popb(csp, ip, simulate_sigsegv);
 		IP(regs) = ip;
-		if (VMPI.vm86dbg_active) {
-			if ((1 << (intno & 7)) & VMPI.vm86dbg_intxxtab[intno >> 3])
+		if (vmpi->vm86dbg_active) {
+			if ((1 << (intno & 7)) & vmpi->vm86dbg_intxxtab[intno >> 3])
 				return_to_32bit(regs, VM86_INTx + (intno << 8));
 		}
 		do_int(regs, intno, ssp, sp);
Signature · 1 line
2.4.3
#13 lore raw

PATCH 6/7 x86/vm86: Eliminate kernel_vm86_struct

In reply to Brian Gerst

Now there is no vm86-specific data left on the kernel stack while in userspace, except for the 32-bit regs.

---
 arch/x86/include/asm/vm86.h | 25 +------------
 arch/x86/kernel/vm86_32.c   | 91 +++++++++++++++++++--------------------------
 2 files changed, 40 insertions(+), 76 deletions(-)
arch/x86/include/asm/vm86.h +1 -24
index 8e83e57..6573145 100644
@@ -28,32 +28,9 @@ struct kernel_vm86_regs {
 	unsigned short gs, __gsh;
 };
 
-struct kernel_vm86_struct {
-	struct kernel_vm86_regs regs;
-/*
- * the below part remains on the kernel stack while we are in VM86 mode.
- * 'tss.esp0' then contains the address of VM86_TSS_ESP0 below, and when we
- * get forced back from VM86, the CPU and "SAVE_ALL" will restore the above
- * 'struct kernel_vm86_regs' with the then actual values.
- * Therefore, pt_regs in fact points to a complete 'kernel_vm86_struct'
- * in kernelspace, hence we need not reget the data from userspace.
- */
-#define VM86_TSS_ESP0 regs32
-	struct pt_regs *regs32;   /* here we save the pointer to the old regs */
-/*
- * The below is not part of the structure, but the stack layout continues
- * this way. In front of 'return-eip' may be some data, depending on
- * compilation, so we don't rely on this and save the pointer to 'oldregs'
- * in 'regs32' above.
- * However, with GCC-2.7.2 and the current CFLAGS you see exactly this:
-
-	long return-eip;        from call to vm86()
-	struct pt_regs oldregs;  user space registers as saved by syscall
- */
-};
-
 struct kernel_vm86_info {
 	struct vm86plus_struct __user *vm86_info;
+	struct pt_regs *regs32;
 	unsigned long v86flags;
 	unsigned long v86mask;
 	unsigned long saved_sp0;
arch/x86/kernel/vm86_32.c +39 -52

178 lines not on this page · raw message

Signature · 1 line
2.4.3
#15 lore raw

PATCH 7/7 x86/vm86: Use the normal pt_regs area for vm86

In reply to Brian Gerst

Change to use the normal pt_regs area to enter and exit vm86 mode. This is done by increasing the padding at the top of the stack to make room for the extra vm86 segment slots in the IRET frame. It then saves the 32-bit regs in the off-stack vm86 data, and copies in the vm86 regs. Exiting back to 32-bit mode does the reverse. This allows removing the hacks to jump directly into the exit asm code due to having to change the stack pointer. Returning normally from the vm86 syscall and the exception handlers allows things like ptrace and auditing to work properly.

---
 arch/x86/entry/entry_32.S          |  24 +--------
 arch/x86/include/asm/thread_info.h |  11 ++--
 arch/x86/include/asm/vm86.h        |   6 ++-
 arch/x86/kernel/signal.c           |   3 ++
 arch/x86/kernel/vm86_32.c          | 106 +++++++++++++++----------------------
 5 files changed, 58 insertions(+), 92 deletions(-)
arch/x86/entry/entry_32.S +1 -23
index 21dc60a..f940e24 100644
@@ -525,34 +525,12 @@ work_resched:
 
 work_notifysig:					# deal with pending signals and
 						# notify-resume requests
-#ifdef CONFIG_VM86
-	testl	$X86_EFLAGS_VM, PT_EFLAGS(%esp)
-	movl	%esp, %eax
-	jnz	work_notifysig_v86		# returning to kernel-space or
-						# vm86-space
-1:
-#else
-	movl	%esp, %eax
-#endif
 	TRACE_IRQS_ON
 	ENABLE_INTERRUPTS(CLBR_NONE)
-	movb	PT_CS(%esp), %bl
-	andb	$SEGMENT_RPL_MASK, %bl
-	cmpb	$USER_RPL, %bl
-	jb	resume_kernel
+	movl	%esp, %eax
 	xorl	%edx, %edx
 	call	do_notify_resume
 	jmp	resume_userspace
-
-#ifdef CONFIG_VM86
-	ALIGN
-work_notifysig_v86:
-	pushl	%ecx				# save ti_flags for do_notify_resume
-	call	save_v86_state			# %eax contains pt_regs pointer
-	popl	%ecx
-	movl	%eax, %esp
-	jmp	1b
-#endif
 END(work_pending)
 
 	# perform syscall exit tracing
arch/x86/include/asm/thread_info.h +7 -4
index 225ee54..fdad5c2 100644
@@ -27,14 +27,17 @@
  * Without this offset, that can result in a page fault.  (We are
  * careful that, in this case, the value we read doesn't matter.)
  *
- * In vm86 mode, the hardware frame is much longer still, but we neither
- * access the extra members from NMI context, nor do we write such a
- * frame at sp0 at all.
+ * In vm86 mode, the hardware frame is much longer still, so add 16
+ * bytes to make room for the real-mode segments.
  *
  * x86_64 has a fixed-length stack frame.
  */
 #ifdef CONFIG_X86_32
-# define TOP_OF_KERNEL_STACK_PADDING 8
+# ifdef CONFIG_VM86
+#  define TOP_OF_KERNEL_STACK_PADDING 16
+# else
+#  define TOP_OF_KERNEL_STACK_PADDING 8
+# endif
 #else
 # define TOP_OF_KERNEL_STACK_PADDING 0
 #endif
arch/x86/include/asm/vm86.h +4 -2
index 6573145..736c789a 100644
@@ -30,7 +30,7 @@ struct kernel_vm86_regs {
 
 struct kernel_vm86_info {
 	struct vm86plus_struct __user *vm86_info;
-	struct pt_regs *regs32;
+	struct pt_regs regs32;
 	unsigned long v86flags;
 	unsigned long v86mask;
 	unsigned long saved_sp0;
@@ -50,7 +50,7 @@ struct kernel_vm86_info {
 
 void handle_vm86_fault(struct kernel_vm86_regs *, long);
 int handle_vm86_trap(struct kernel_vm86_regs *, long, int);
-struct pt_regs *save_v86_state(struct kernel_vm86_regs *);
+void save_v86_state(struct kernel_vm86_regs *, int);
 
 struct task_struct;
 void release_vm86_irqs(struct task_struct *);
@@ -65,6 +65,8 @@ static inline int handle_vm86_trap(struct kernel_vm86_regs *a, long b, int c)
 	return 0;
 }
 
+static inline void save_v86_state(struct kernel_vm86_regs *, int) { }
+
 #endif /* CONFIG_VM86 */
 
 #endif /* _ASM_X86_VM86_H */
arch/x86/kernel/signal.c +3 -0
index 7e88cc7..bfd736e 100644
@@ -635,6 +635,9 @@ handle_signal(struct ksignal *ksig, struct pt_regs *regs)
 	bool stepping, failed;
 	struct fpu *fpu = &current->thread.fpu;
 
+	if (v8086_mode(regs))
+		save_v86_state((struct kernel_vm86_regs *) regs, VM86_SIGNAL);
+
 	/* Are we from a system call? */
 	if (syscall_get_nr(current, regs) >= 0) {
 		/* If so, check system call restarting.. */
arch/x86/kernel/vm86_32.c +43 -63

235 lines not on this page · raw message

Signature · 1 line
2.4.3