ia32: Move as much of the interrupt and syscall traps into C as possible

This commit is contained in:
Adrian Danis 2014-08-11 17:10:24 +10:00
parent 6b96bb995c
commit 7d877465de
6 changed files with 190 additions and 212 deletions

View file

@ -17,7 +17,7 @@
#include <api/syscall.h>
void slowpath(syscall_t syscall)
VISIBLE FASTCALL NORETURN;
NORETURN;
void fastpath_call(word_t cptr, word_t r_msgInfo)
VISIBLE FASTCALL NORETURN;

View file

@ -15,6 +15,8 @@ include ${SOURCE_ROOT}/src/arch/$(ARCH)/model/Makefile
include ${SOURCE_ROOT}/src/arch/$(ARCH)/machine/Makefile
include ${SOURCE_ROOT}/src/arch/$(ARCH)/fastpath/Makefile
ARCH_C_SOURCES += c_traps.c
ARCH_ASM_SOURCES += machine_asm.S \
traps.S \
halt.S \

169
src/arch/ia32/c_traps.c Normal file
View file

@ -0,0 +1,169 @@
/*
* Copyright 2014, General Dynamics C4 Systems
*
* This software may be distributed and modified according to the terms of
* the GNU General Public License version 2. Note that NO WARRANTY is provided.
* See "LICENSE_GPLv2.txt" for details.
*
* @TAG(GD_GPL)
*/
#include <config.h>
#include <model/statedata.h>
#include <arch/kernel/lock.h>
#include <arch/machine/fpu.h>
#include <arch/fastpath/fastpath.h>
#include <api/syscall.h>
void __attribute__((noreturn)) __attribute__((externally_visible)) restore_user_context(void);
void __attribute__((noreturn)) __attribute__((externally_visible)) restore_user_context(void) {
/* set the tss.esp0 */
tss_ptr_set_esp0(&ia32KStss, ((uint32_t)ksCurThread) + 0x4c);
if (unlikely(ksCurThread == ia32KSfpuOwner)) {
/* We are using the FPU, make sure it is enabled */
enableFpu();
} else if (unlikely(ia32KSfpuOwner)) {
/* Someone is using the FPU and it might be enabled */
disableFpu();
} else {
/* No-one (including us) is using the FPU, so we assume it
* is currently disabled */
}
/* see if we entered via syscall */
if (likely(ksCurThread->tcbContext.registers[Error] == -1)) {
ksCurThread->tcbContext.registers[EFLAGS] &= ~0x200;
asm volatile(
// Set our stack pointer to the top of the tcb so we can efficiently pop
"movl %0, %%esp\n"
// restore syscall number
"popl %%eax\n"
// cap/badge reigster
"popl %%ebx\n"
// skip ecx and edx, these will contain esp and nexteip due to sysenter/sysexit convention
"addl $8, %%esp\n"
// message info register
"popl %%esi\n"
// message register
"popl %%edi\n"
// message register
"popl %%ebp\n"
//ds (if changed)
"cmpl $0x23, (%%esp)\n"
"je 1f\n"
"popl %%ds\n"
"jmp 2f\n"
"1: addl $4, %%esp\n"
"2:\n"
//es (if changed)
"cmpl $0x23, (%%esp)\n"
"je 1f\n"
"popl %%es\n"
"jmp 2f\n"
"1: addl $4, %%esp\n"
"2:\n"
//have to reload other selectors
"popl %%fs\n"
"popl %%gs\n"
// skip faulteip, tls_base and error (these are fake registers)
"addl $12, %%esp\n"
// restore nexteip
"popl %%edx\n"
// skip cs
"addl $4, %%esp\n"
"popfl\n"
// reset interrupt bit
"orl $0x200, -4(%%esp)\n"
// restore esp
"pop %%ecx\n"
"sti\n"
"sysexit\n"
:
: "r"(&ksCurThread->tcbContext.registers[EAX])
// Clobber memory so the compiler is forced to complete all stores
// before running this assembler
: "memory"
);
} else {
asm volatile(
// Set our stack pointer to the top of the tcb so we can efficiently pop
"movl %0, %%esp\n"
"popl %%eax\n"
"popl %%ebx\n"
"popl %%ecx\n"
"popl %%edx\n"
"popl %%esi\n"
"popl %%edi\n"
"popl %%ebp\n"
"popl %%ds\n"
"popl %%es\n"
"popl %%fs\n"
"popl %%gs\n"
// skip faulteip, tls_base, error
"addl $12, %%esp\n"
"iret\n"
:
: "r"(&ksCurThread->tcbContext.registers[EAX])
// Clobber memory so the compiler is forced to complete all stores
// before running this assembler
: "memory"
);
}
while(1);
}
void __attribute__((fastcall)) __attribute__((externally_visible)) c_handle_interrupt(int irq, int syscall);
void __attribute__((fastcall)) __attribute__((externally_visible)) c_handle_interrupt(int irq, int syscall) {
if (irq == int_unimpl_dev) {
handleUnimplementedDevice();
} else if (irq == int_page_fault) {
/* Error code is in Error. Pull out bit 5, which is whether it was instruction or data */
handleVMFaultEvent((ksCurThread->tcbContext.registers[Error] >> 4) & 1);
} else if (irq < int_irq_min) {
handleUserLevelFault(irq, ksCurThread->tcbContext.registers[Error]);
} else if (likely(irq < int_trap_min)) {
ia32KScurInterrupt = irq;
handleInterruptEntry();
} else if (irq == int_spurious) {
/* fall through to restore_user_context and do nothing */
} else {
/* Interpret a trap as an unknown syscall */
/* Adjust FaultEIP to point to trapping INT
* instruction by subtracting 2 */
int sys_num;
ksCurThread->tcbContext.registers[FaultEIP] -= 2;
/* trap number is MSBs of the syscall number and the LSBS of EAX */
sys_num = (irq << 24) | (syscall & 0x00ffffff);
handleUnknownSyscall(sys_num);
}
restore_user_context();
}
void __attribute__((noreturn))
slowpath(syscall_t syscall)
{
ia32KScurInterrupt = -1;
/* increment nextEIP to skip sysenter */
ksCurThread->tcbContext.registers[NextEIP] += 2;
/* check for undefined syscall */
if (unlikely(syscall < SYSCALL_MIN || syscall > SYSCALL_MAX)) {
handleUnknownSyscall(syscall);
} else {
handleSyscall(syscall);
}
restore_user_context();
}
void __attribute__((externally_visible)) c_handle_syscall(syscall_t syscall, word_t cptr, word_t msgInfo);
void __attribute__((externally_visible)) c_handle_syscall(syscall_t syscall, word_t cptr, word_t msgInfo)
{
#ifdef FASTPATH
if (syscall == SysCall) {
fastpath_call(cptr, msgInfo);
} else if (syscall == SysReplyWait) {
fastpath_reply_wait(cptr, msgInfo);
}
#endif
slowpath(syscall);
}

View file

@ -91,6 +91,8 @@ Arch_initFpu(void)
/* Enable FPU / SSE / SSE2 / SSE3 / SSSE3 / SSE4 Extensions. */
write_cr4(read_cr4() | CR4_OSFXSR);
/* Enable the FPU in general. */
write_cr0((read_cr0() & ~CR0_EMULATION) | CR0_MONITOR_COPROC | CR0_NUMERIC_ERROR);
/* Enable the FPU in general. Although leave it in a state where it will
* generate a fault if someone tries to use it as we implement lazy
* switching */
write_cr0((read_cr0() & ~CR0_EMULATION) | CR0_MONITOR_COPROC | CR0_NUMERIC_ERROR | CR0_TASK_SWITCH);
}

View file

@ -9,9 +9,6 @@
*/
#include <machine/assembler.h>
#include <arch/api/syscall.h>
# minimum and maximum seL4 syscall number
# On kernel entry, ESP points to the end of the thread's registers array.
# Hardware pushes onto the stack SS, ESP, EFLAGS, CS, NextEIP and Error,
@ -349,70 +346,14 @@ handle_interrupt:
andb %bl, %dl # we have a kernel exception if both BL and DL are 1
jnz kernel_exception
# save ESP (which now points to the current TCB)
movl %esp, %edx
# save ECX (which contains the interrupt vector)
movl %ecx, ia32KScurInterrupt
# switch to kernel stack
leal _kernel_stack_top, %esp
# the code below assumes:
# - EAX contains the syscall number
# - ECX contains the interrupt vector
# - EDX contains the current TCB
# Place the syscall number in the second argument slot
movl %eax, %edx
# depending on the interrupt vector, call appropriate handling function
cmpl $0x07, %ecx # 0x07 = int_unimpl_dev
je handle_unimplemented_device
cmpl $0x0e, %ecx # 0x0e = int_page_fault
je handle_page_fault
cmpl $0x20, %ecx # 0x20 = int_irq_isa_min
jb handle_fault
cmpl $0x50, %ecx # 0x50 = int_trap_min
jb handle_IRQ
cmpl $0xff, %ecx # 0xff = int_spurious
je restore_user_context # ignore and do not ack spurious interrupt
# Here we know we have a trap. It is interpreted as unknown syscall.
# We have to adjust the FaultEIP to point to the trapping INT instruction
# by subtracting 2. EDX contains the current TCB.
subl $2, 0x2c(%edx)
# The trap number is stored in the MSB and the 24-bit syscall number
# in the LSBs of EAX.
shll $24, %ecx
andl $0x00ffffff, %eax
orl %ecx, %eax
jmp undefined_syscall
handle_fault:
# fault code corresponds to error code of the exception
pushl 0x34(%edx) # push Error
# fault number corresponds to exception vector number
pushl %ecx
call handleUserLevelFault
addl $8, %esp
jmp restore_user_context
handle_page_fault:
movl 0x34(%edx), %edx # get Error code from TCB
shrl $4, %edx # get I/D flag
andl $1, %edx # mask rest of error code
pushl %edx # vm_fault_type_t (0 = data, 1 = instruction)
call handleVMFaultEvent
addl $4, %esp
jmp restore_user_context
handle_unimplemented_device:
call handleUnimplementedDevice
jmp restore_user_context
handle_IRQ:
call handleInterruptEntry
jmp restore_user_context
# gtfo to C land, we will not return
call c_handle_interrupt
# Handle a kernel exception
@ -444,27 +385,6 @@ BEGIN_FUNC(kernel_exception)
jmp halt
END_FUNC(kernel_exception)
#ifdef FASTPATH
# Calls the slowpath from the fastpath
# Defined with fastcall calling convention
BEGIN_FUNC(slowpath)
# indicate int_invalid as the interrupt currently being served
movl $-1, ia32KScurInterrupt
# syscall is in ECX
pushl %ecx
# Update NextEIP
movl ksCurThread, %ecx
addl $2, 56(%ecx)
call handleSyscall
jmp restore_user_context
END_FUNC(slowpath)
#endif
#define SET_SEL(sel, offset, val) \
cmpl $val, (offset)(%esp); \
je 1f; \
@ -507,131 +427,15 @@ BEGIN_FUNC(handle_syscall)
SET_SEL(ds, 7 * 4, 0x23)
SET_SEL(es, 8 * 4, 0x23)
# Perform this comparison as soon as we stop doing operations that clobber the FLAGS
#ifdef FASTPATH
cmpl $SYSCALL_REPLY_WAIT, %eax
#endif
# switch to kernel stack
leal _kernel_stack_top, %esp
#ifdef FASTPATH
# Shuffle some register to suit the fastcall calling convention
movl %ebx, %ecx
movl %esi, %edx
je fastpath_reply_wait
cmpl $SYSCALL_CALL, %eax
je fastpath_call
#endif
# Push all the arguments for c_handle_syscall
pushl %esi # msgInfo
pushl %ebx # cptr
pushl %eax # syscall number
# indicate int_invalid as the interrupt currently being served
movl $-1, ia32KScurInterrupt
# gtfo to C land, we will not return
call c_handle_syscall
# increment NextEIP
movl ksCurThread, %ecx
addl $2, 56(%ecx)
# check that syscall number is in range
cmpl $SYSCALL_MAX, %eax
jg undefined_syscall
cmpl $SYSCALL_MIN, %eax
jl undefined_syscall
# we have a seL4 syscall
pushl %eax
call handleSyscall
jmp restore_user_context
undefined_syscall:
pushl %eax
call handleUnknownSyscall
# fall through to restore_user_context
END_FUNC(handle_syscall)
#define POP_SEL(sel, val) \
cmpl $val, (%esp); \
je 1f; \
popl %sel; \
jmp 2f; \
1: \
addl $4, %esp; \
2:
BEGIN_FUNC(restore_user_context)
# save kernel ESP to TSS for next kernel entry
movl ksCurThread, %esp # point ESP to current user_context_t
addl $0x4c, %esp # size of registers array (0x4c bytes)
leal ia32KStss, %ecx
movl %esp, 4(%ecx) # tss.esp0 = pointer to end of thread's registers array
subl $0x4c, %esp # restore ESP to point to current TCB
# disable the FPU, unless owned by the current thread
cmpl %esp, ia32KSfpuOwner
je fpu_enable
movl %cr0, %eax
orl $0x8, %eax
movl %eax, %cr0
fpu_done:
# determine whether we entered via interrupt or syscall
cmpl $-1, 0x34(%esp) # if (Error == -1)
je restore_syscall
# we entered via interrupt, i.e. we return via iret
# precondition: ESP == ksCurThread
popl %eax
popl %ebx
popl %ecx
popl %edx
popl %esi
popl %edi
popl %ebp
popl %ds
popl %es
popl %fs
popl %gs
addl $4, %esp # skip FaultEIP
addl $4, %esp # skip TLS_BASE
addl $4, %esp # skip the error code
iret
fpu_enable:
clts
jmp fpu_done
restore_syscall:
# we entered via sysenter, i.e. we return via sysexit
# precondition: ESP == ksCurThread
popl %eax # restore EAX (syscall number)
popl %ebx # restore EBX (cap/badge register)
addl $8, %esp # skip ECX and EDX (will contain ESP and NextEIP)
popl %esi # restore ESI (msgInfo register)
popl %edi # restore EDI (message register)
popl %ebp # restore EBP (message register)
# Only pop the code and data selectors if their values have changed from the default
POP_SEL(ds, 0x23)
POP_SEL(es, 0x23)
# Have to reload fs and gs for update to the selectors to become visible
popl %fs
popl %gs
addl $4, %esp # skip FaultEIP
addl $4, %esp # skip TLS_BASE
addl $4, %esp # skip Error
popl %edx # restore NextEIP (passed in EDX)
addl $4, %esp # skip CS
andl $~0x200, (%esp) # clear interrupt bit before popping into EFLAGS
popfl # restore EFLAGS with interrupts off
orl $0x200, -4(%esp) # set interrupt bit again
popl %ecx # restore ESP (passed in ECX)
# skip SS
# We have following register contents when returning:
# EAX : syscall number (needed to restart syscall)
# ECX : user ESP
# EDX : user NextEIP
sti # now enable the interrupts, which were disabled
# by sysenter (will be enabled AFTER sysexit)
sysexit # return to user space
END_FUNC(restore_user_context)

View file

@ -41,13 +41,14 @@ kernel_header_template = \
#define SYSCALL_{{upper(syscall)}} ({{syscall_number}})
{{py:syscall_number -= 1}}
{{endfor}}
{{py:syscall_max = -len(list)}}
{{endfor}}
#endif
#define SYSCALL_MAX (-1)
#define SYSCALL_MIN ({{syscall_number + 1}})
#else /* C definitions */
#ifndef __ASSEMBLER__
enum syscall {
{{py:syscall_number = -1}}
@ -66,7 +67,7 @@ enum syscall {
};
typedef uint32_t syscall_t;
#endif /* ASSEMBLER */
#endif
#endif /* __ARCH_API_SYSCALL_H */
"""