root/src/system/kernel/arch/x86/32/signals.cpp
/*
 * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */


#include "x86_signals.h"

#include <string.h>

#include <KernelExport.h>

#include <commpage.h>
#include <cpu.h>
#include <elf.h>
#include <smp.h>

#include "syscall_numbers.h"


// implemented in assembly
extern "C" void x86_signal_frame_function_beos(signal_frame_data* frameData);


extern "C" void
x86_signal_frame_function(signal_frame_data* frameData)
{
        // Note: This function is copied to the commpage. Hence it needs to be
        // position independent. We don't build this source file with the respective
        // flags, but the code the compiler generates for this function is position
        // independent anyway. It simply doesn't contain constructs that could
        // result in position dependent code. The potentially problematic jumps
        // needed due to the "if" statement are all harmless relative jumps.

        if (frameData->siginfo_handler) {
                // SA_SIGINFO style handler function -- we additionally pass the user
                // data pointer
                void (*handler)(int, siginfo_t*, void*, void*)
                        = (void (*)(int, siginfo_t*, void*, void*))frameData->handler;
                handler(frameData->info.si_signo, &frameData->info,
                        &frameData->context, frameData->user_data);
        } else {
                // Simple handler function -- we call it with additional user data
                // pointer and vregs parameters. Note that unlike in BeOS the last
                // parameter is a pointer to a vregs structure, while in BeOS the
                // structure was passed be value. For setting up a BeOS binary
                // compatible signal handler call x86_signal_frame_function_beos() is
                // used instead.
                void (*handler)(int, void*, vregs*)
                        = (void (*)(int, void*, vregs*))frameData->handler;
                handler(frameData->info.si_signo, frameData->user_data,
                        &frameData->context.uc_mcontext);
        }

        #define TO_STRING_LITERAL_HELPER(number)        #number
        #define TO_STRING_LITERAL(number)       TO_STRING_LITERAL_HELPER(number)

        // call the restore_signal_frame() syscall -- does not return (here)
        asm volatile(
                // push frameData -- the parameter to restore_signal_frame()
                "pushl %0;"
                // push a dummy return value
                "pushl $0;"
                // syscall number to eax
                "movl $" TO_STRING_LITERAL(SYSCALL_RESTORE_SIGNAL_FRAME) ", %%eax;"
                // syscall
                "int $99;"
                :: "r"(frameData)
        );

        #undef TO_STRING_LITERAL_HELPER
        #undef TO_STRING_LITERAL
}


static void
register_signal_handler_function(const char* functionName, int32 commpageIndex,
        const char* commpageSymbolName, addr_t expectedAddress)
{
        // look up the x86_signal_frame_function() symbol -- we have its address,
        // but also need its size
        elf_symbol_info symbolInfo;
        if (elf_lookup_kernel_symbol(functionName, &symbolInfo)
                        != B_OK) {
                panic("x86_initialize_commpage_signal_handler(): Failed to find "
                        "signal frame function \"%s\"!", functionName);
        }

        ASSERT(expectedAddress == symbolInfo.address);

        // fill in the commpage table entry
        addr_t position = fill_commpage_entry(commpageIndex,
                (void*)symbolInfo.address, symbolInfo.size);

        // add symbol to the commpage image
        image_id image = get_commpage_image();
        elf_add_memory_image_symbol(image, commpageSymbolName, position,
                symbolInfo.size, B_SYMBOL_TYPE_TEXT);
}


void
x86_initialize_commpage_signal_handler()
{
        // standard handler
        register_signal_handler_function("x86_signal_frame_function",
                COMMPAGE_ENTRY_X86_SIGNAL_HANDLER, "commpage_signal_handler",
                (addr_t)&x86_signal_frame_function);

        // handler for BeOS backwards compatibility
        register_signal_handler_function("x86_signal_frame_function_beos",
                COMMPAGE_ENTRY_X86_SIGNAL_HANDLER_BEOS, "commpage_signal_handler_beos",
                (addr_t)&x86_signal_frame_function_beos);
}


addr_t
x86_get_user_signal_handler_wrapper(bool beosHandler, void* commPageAdddress)
{
        int32 index = beosHandler
                ? COMMPAGE_ENTRY_X86_SIGNAL_HANDLER_BEOS
                : COMMPAGE_ENTRY_X86_SIGNAL_HANDLER;
        return ((addr_t*)commPageAdddress)[index] + (addr_t)commPageAdddress;
}