Index: kernel/arch/ia32/src/asm.S
===================================================================
--- kernel/arch/ia32/src/asm.S	(revision 0737078089c7303e45c2821ff585eb62cd0963b2)
+++ kernel/arch/ia32/src/asm.S	(revision ed7998b74c53c523bc17491e703fc2c4dd6651fd)
@@ -163,45 +163,4 @@
 	popfl
 .endm
-
-/*
- * The SYSENTER syscall mechanism can be used for syscalls with
- * four or fewer arguments. To pass these four arguments, we
- * use four registers: EDX, ECX, EBX, ESI. The syscall number
- * is passed in EAX. We use EDI to remember the return address
- * and EBP to remember the stack. The INT-based syscall mechanism
- * can actually handle six arguments plus the syscall number
- * entirely in registers.
- */
-.global sysenter_handler
-sysenter_handler:
-	sti
-	pushl %ebp  /* remember user stack */
-	pushl %edi  /* remember return user address */
-	
-	xorl %ebp, %ebp  /* stop stack traces here */
-	
-	pushl %gs  /* remember TLS */
-	
-	pushl %eax     /* syscall number */
-	subl $8, %esp  /* unused sixth and fifth argument */
-	pushl %esi     /* fourth argument */
-	pushl %ebx     /* third argument */
-	pushl %ecx     /* second argument */
-	pushl %edx     /* first argument */
-	
-	movw $16, %ax
-	movw %ax, %ds
-	movw %ax, %es
-	
-	cld
-	call syscall_handler
-	addl $28, %esp  /* remove arguments from stack */
-	
-	pop %gs  /* restore TLS */
-	
-	pop %edx  /* prepare return EIP for SYSEXIT */
-	pop %ecx  /* prepare userspace ESP for SYSEXIT */
-	
-	sysexit   /* return to userspace */
 
 #define ISTATE_OFFSET_EDX         0
@@ -231,4 +190,81 @@
 #define ISTATE_SOFT_SIZE  52
 
+/*
+ * Size of the entire istate structure including the error word and the
+ * hardware-saved part.
+ */
+#define ISTATE_REAL_SIZE  (ISTATE_SOFT_SIZE + 24)
+
+/*
+ * The SYSENTER syscall mechanism can be used for syscalls with
+ * four or fewer arguments. To pass these four arguments, we
+ * use four registers: EDX, ECX, EBX, ESI. The syscall number
+ * is passed in EAX. We use EDI to remember the return address
+ * and EBP to remember the stack. The INT-based syscall mechanism
+ * can actually handle six arguments plus the syscall number
+ * entirely in registers.
+ */
+.global sysenter_handler
+sysenter_handler:
+	sti
+	subl $(ISTATE_REAL_SIZE), %esp
+
+	/*
+	 * Save the return address and the userspace stack in the istate
+	 * structure on locations that would normally be taken by them.
+	 */
+	movl %ebp, ISTATE_OFFSET_ESP(%esp)
+	movl %edi, ISTATE_OFFSET_EIP(%esp)
+
+	/*
+	 * Push syscall arguments onto the stack
+	 */
+	movl %eax, ISTATE_OFFSET_EAX(%esp)
+	movl %ebx, ISTATE_OFFSET_EBX(%esp)
+	movl %ecx, ISTATE_OFFSET_ECX(%esp)
+	movl %edx, ISTATE_OFFSET_EDX(%esp)
+	movl %esi, ISTATE_OFFSET_ESI(%esp)
+	movl %edi, ISTATE_OFFSET_EDI(%esp)	/* observability; not needed */
+	movl %ebp, ISTATE_OFFSET_EBP(%esp)	/* observability; not needed */
+	
+	/*
+	 * Fake up the stack trace linkage.
+	 */
+	movl %edi, ISTATE_OFFSET_EIP_FRAME(%esp)
+	movl $0, ISTATE_OFFSET_EBP_FRAME(%esp)
+	leal ISTATE_OFFSET_EBP_FRAME(%esp), %ebp
+
+	/*
+	 * Save TLS.
+	 */
+	movl %gs, %edx
+	movl %edx, ISTATE_OFFSET_GS(%esp)
+
+	/*
+	 * Switch to kernel selectors.
+	 */
+	movw $16, %ax
+	movw %ax, %ds
+	movw %ax, %es
+	
+	cld
+	call syscall_handler
+	
+	/*
+	 * Restore TLS.
+	 */
+	movl ISTATE_OFFSET_GS(%esp), %edx
+	movl %edx, %gs
+	
+	/*
+	 * Prepare return address and userspace stack for SYSEXIT.
+	 */
+	movl ISTATE_OFFSET_EIP(%esp), %edx
+	movl ISTATE_OFFSET_ESP(%esp), %ecx
+
+	addl $(ISTATE_REAL_SIZE), %esp
+	
+	sysexit   /* return to userspace */
+
 /** Declare interrupt handlers
  *
