ia32: Move as much of the interrupt and syscall traps into C as possible
This commit is contained in:
parent
6b96bb995c
commit
7d877465de
6 changed files with 190 additions and 212 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
169
src/arch/ia32/c_traps.c
Normal 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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
"""
|
||||
|
|
|
|||
Loading…
Reference in a new issue