* install syscall handlers in compatibility mode. * copy the syscall entry in the compatibility commpage, ATM only for Intel SYSENTER. * copy the thread exit handler in the compatibility commpage. Change-Id: Ic350799938815194377d8a4560cb106fe7366cc6
152 lines
4.0 KiB
C++
152 lines
4.0 KiB
C++
/*
|
|
* Copyright 2018, Jérôme Duval, [email protected].
|
|
* Copyright 2012, Alex Smith, [email protected].
|
|
* Distributed under the terms of the MIT License.
|
|
*/
|
|
|
|
|
|
#include "x86_syscalls.h"
|
|
|
|
#include <KernelExport.h>
|
|
|
|
#ifdef _COMPAT_MODE
|
|
# include <commpage_compat.h>
|
|
#endif
|
|
#include <cpu.h>
|
|
#ifdef _COMPAT_MODE
|
|
# include <elf.h>
|
|
#endif
|
|
#include <smp.h>
|
|
|
|
|
|
// SYSCALL handler (in interrupts.S).
|
|
extern "C" void x86_64_syscall_entry(void);
|
|
|
|
|
|
#ifdef _COMPAT_MODE
|
|
|
|
// SYSCALL/SYSENTER handlers (in entry_compat.S).
|
|
extern "C" {
|
|
void x86_64_syscall32_entry(void);
|
|
void x86_64_sysenter32_entry(void);
|
|
}
|
|
|
|
|
|
void (*gX86SetSyscallStack)(addr_t stackTop) = NULL;
|
|
|
|
|
|
// user syscall assembly stubs
|
|
extern "C" void x86_user_syscall_sysenter(void);
|
|
extern unsigned int x86_user_syscall_sysenter_end;
|
|
extern "C" void x86_user_syscall_syscall(void);
|
|
extern unsigned int x86_user_syscall_syscall_end;
|
|
|
|
extern "C" void x86_sysenter32_userspace_thread_exit(void);
|
|
extern unsigned int x86_sysenter32_userspace_thread_exit_end;
|
|
|
|
#endif // _COMPAT_MODE
|
|
|
|
|
|
static void
|
|
init_syscall_registers(void* dummy, int cpuNum)
|
|
{
|
|
// Enable SYSCALL (EFER.SCE = 1).
|
|
x86_write_msr(IA32_MSR_EFER, x86_read_msr(IA32_MSR_EFER)
|
|
| IA32_MSR_EFER_SYSCALL);
|
|
|
|
// Flags to clear upon entry. Want interrupts disabled and the direction
|
|
// flag cleared.
|
|
x86_write_msr(IA32_MSR_FMASK, X86_EFLAGS_INTERRUPT | X86_EFLAGS_DIRECTION
|
|
| X86_EFLAGS_ALIGNMENT_CHECK);
|
|
|
|
// Entry point address.
|
|
x86_write_msr(IA32_MSR_LSTAR, (addr_t)x86_64_syscall_entry);
|
|
|
|
#ifdef _COMPAT_MODE
|
|
// Syscall compat entry point address.
|
|
x86_write_msr(IA32_MSR_CSTAR, (addr_t)x86_64_syscall32_entry);
|
|
#endif
|
|
|
|
// Segments that will be set upon entry and return. This is very strange
|
|
// and requires a specific ordering of segments in the GDT. Upon entry:
|
|
// - CS is set to IA32_STAR[47:32]
|
|
// - SS is set to IA32_STAR[47:32] + 8
|
|
// Upon return:
|
|
// - CS is set to IA32_STAR[63:48] + 16
|
|
// - SS is set to IA32_STAR[63:48] + 8
|
|
// From this we get:
|
|
// - Entry CS = KERNEL_CODE_SELECTOR
|
|
// - Entry SS = KERNEL_CODE_SELECTOR + 8 = KERNEL_DATA_SELECTOR
|
|
// - Return CS = USER32_CODE_SELECTOR + 16 = USER_CODE_SELECTOR
|
|
// - Return SS = USER32_CODE_SELECTOR + 8 = USER_DATA_SELECTOR
|
|
x86_write_msr(IA32_MSR_STAR, ((uint64)(USER32_CODE_SELECTOR) << 48)
|
|
| ((uint64)(KERNEL_CODE_SELECTOR) << 32));
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
void
|
|
x86_initialize_syscall(void)
|
|
{
|
|
// SYSCALL/SYSRET are always available on x86_64 so we just use them, no
|
|
// need to use the commpage. Tell all CPUs to initialize the SYSCALL MSRs.
|
|
call_all_cpus_sync(&init_syscall_registers, NULL);
|
|
}
|
|
|
|
|
|
#ifdef _COMPAT_MODE
|
|
|
|
static void
|
|
set_intel_syscall_stack(addr_t stackTop)
|
|
{
|
|
x86_write_msr(IA32_MSR_SYSENTER_ESP, stackTop);
|
|
}
|
|
|
|
|
|
static void
|
|
init_intel_syscall_registers(void* dummy, int cpuNum)
|
|
{
|
|
x86_write_msr(IA32_MSR_SYSENTER_CS, KERNEL_CODE_SELECTOR);
|
|
x86_write_msr(IA32_MSR_SYSENTER_ESP, 0);
|
|
x86_write_msr(IA32_MSR_SYSENTER_EIP, (addr_t)x86_64_sysenter32_entry);
|
|
|
|
gX86SetSyscallStack = &set_intel_syscall_stack;
|
|
}
|
|
|
|
|
|
void
|
|
x86_compat_initialize_syscall(void)
|
|
{
|
|
// for 32-bit syscalls, fill the commpage with the right mechanism
|
|
call_all_cpus_sync(&init_intel_syscall_registers, NULL);
|
|
|
|
void* syscallCode = (void *)&x86_user_syscall_sysenter;
|
|
void* syscallCodeEnd = &x86_user_syscall_sysenter_end;
|
|
|
|
// TODO check AMD for sysenter
|
|
|
|
// fill in the table entry
|
|
size_t len = (size_t)((addr_t)syscallCodeEnd - (addr_t)syscallCode);
|
|
addr_t position = fill_commpage_compat_entry(COMMPAGE_ENTRY_X86_SYSCALL,
|
|
syscallCode, len);
|
|
|
|
image_id image = get_commpage_compat_image();
|
|
elf_add_memory_image_symbol(image, "commpage_compat_syscall", position,
|
|
len, B_SYMBOL_TYPE_TEXT);
|
|
|
|
void* threadExitCode = (void *)&x86_sysenter32_userspace_thread_exit;
|
|
void* threadExitCodeEnd = &x86_sysenter32_userspace_thread_exit_end;
|
|
|
|
len = (size_t)((addr_t)threadExitCodeEnd - (addr_t)threadExitCode);
|
|
position = fill_commpage_compat_entry(COMMPAGE_ENTRY_X86_THREAD_EXIT,
|
|
threadExitCode, len);
|
|
|
|
elf_add_memory_image_symbol(image, "commpage_compat_thread_exit",
|
|
position, len, B_SYMBOL_TYPE_TEXT);
|
|
}
|
|
|
|
#endif // _COMPAT_MODE
|
|
|