/** * Native CPU irq.h implementation * * uses POSIX real-time extension signals to create interrupts * TODO: needs to be rewritten for better portability * * Copyright (C) 2013 Ludwig Ortmann * * This file subject to the terms and conditions of the GNU General Public * License. See the file LICENSE in the top level directory for more details. * * @ingroup native_cpu * @ingroup irq * @{ * @file * @author Ludwig Ortmann */ #include #include #include #include "cpu.h" #include "debug.h" static int native_interrupts_enabled; static int native_in_irs; static int last_sig; static ucontext_t native_context; static sigset_t native_sig_set; static char __isr_stack[SIGSTKSZ]; extern volatile tcb_t *active_thread; struct int_handler_t { void (*func)(void); }; static struct int_handler_t native_irq_handlers[255]; volatile ucontext_t *interrupted_contexts[MAXTHREADS]; void print_thread_sigmask(ucontext_t *cp) { sigset_t *p = &cp->uc_sigmask; if (sigemptyset(p) == -1) { err(1, "print_thread_sigmask: sigemptyset"); } for (int i = 1; i<(SIGRTMAX); i++) { if (native_irq_handlers[i].func != NULL) { printf("%s: %s\n", strsignal(i), (sigismember(&native_sig_set, i) ? "blocked": "unblocked") ); } if (sigismember(p, i)) { printf("%s: pending\n", strsignal(i)); } } } void print_sigmasks(void) { ucontext_t *p; //tcb_t *cb = NULL; for (int i=0; iname); //print_thread_sigmask(sched_threads[i]->sp); p = sched_threads[i]->stack_start; print_thread_sigmask(p); puts(""); } } } void native_print_signals() { sigset_t p, q; puts("native signals:\n"); if (sigemptyset(&p) == -1) { err(1, "native_print_signals: sigemptyset"); } if (sigpending(&p) == -1) { err(1, "native_print_signals: sigpending"); } if (sigprocmask(SIG_SETMASK, NULL, &q) == -1) { err(1, "native_print_signals(): sigprocmask"); } for (int i = 1; i<(SIGRTMAX); i++) { if (native_irq_handlers[i].func != NULL || i == SIGUSR1) { printf("%s: %s\n", strsignal(i), (sigismember(&native_sig_set, i) ? "blocked": "unblocked") ); } if (sigismember(&p, i)) { printf("%s: pending\n", strsignal(i)); } if (sigismember(&q, i)) { printf("%s: currently blocked\n", strsignal(i)); } } } /** * block signals */ unsigned disableIRQ(void) { unsigned int prev_state; sigset_t mask; DEBUG("disableIRQ()\n"); if (sigfillset(&mask) == -1) { err(1, "disableIRQ(): sigfillset"); } if (native_interrupts_enabled == 1) { DEBUG("sigprocmask(..native_sig_set)\n"); if (sigprocmask(SIG_SETMASK, &mask, &native_sig_set) == -1) { err(1, "disableIRQ(): sigprocmask"); } } else { DEBUG("sigprocmask()\n"); if (sigprocmask(SIG_SETMASK, &mask, NULL) == -1) { err(1, "disableIRQ(): sigprocmask()"); } } prev_state = native_interrupts_enabled; native_interrupts_enabled = 0; return prev_state; } /** * unblock signals */ unsigned enableIRQ(void) { unsigned int prev_state; DEBUG("enableIRQ()\n"); if (sigprocmask(SIG_SETMASK, &native_sig_set, NULL) == -1) { err(1, "enableIRQ(): sigprocmask()"); } prev_state = native_interrupts_enabled; native_interrupts_enabled = 1; //print_sigmasks(); //native_print_signals(); return prev_state; } void restoreIRQ(unsigned state) { DEBUG("restoreIRQ()\n"); if (state == 1) { enableIRQ(); } else { disableIRQ(); } return; } int inISR(void) { DEBUG("inISR()\n"); return native_in_irs; } void dINT(void) { disableIRQ(); } void eINT(void) { enableIRQ(); } /** * call signal handler, * restore user context */ void native_irq_handler() { if (last_sig == -1) { errx(1, "XXX: internal error"); } if (native_irq_handlers[last_sig].func != NULL) { DEBUG("calling interrupt handler for %i\n", last_sig); native_irq_handlers[last_sig].func(); } else if (last_sig == SIGUSR1) { DEBUG("ignoring SIGUSR1\n"); } else { printf("XXX: no handler for signal %i\n", last_sig); errx(1, "XXX: this should not have happened!\n"); } last_sig = -1; native_in_irs = 0; cpu_switch_context_exit(); } /** * load isr context, save signal */ void native_isr_entry(int sig, siginfo_t *info, void *context) { /* * This is how it goes: * We create a new context "R" for the RIOT interrupt service * routine. * We save the current (signalhandler) context "S" to the active * threads context. * We then jump into the R context. * Later, when jumping back into "S", we start out in the signal * handler context only to immediately return into the context we * originally left. This step is done by the kernel for us. * * So the thing to wrap your head around is that the active thread * remains within in the signal handler context (which is pushed * onto the active threads own stack by swapcontext) until the * thread is activated again, at which point the kernel handles * the context switch back onto the interrupted context for us. * */ /* save the signal */ last_sig = sig; /* indicate irs status */ native_in_irs = 1; if (getcontext(&native_context) == -1) { err(1, "native_isr_entry(): getcontext()"); } native_context.uc_stack.ss_sp = __isr_stack; native_context.uc_stack.ss_size = SIGSTKSZ; native_context.uc_stack.ss_flags = 0; /* XXX: disable interrupts * -> sigfillset(&(native_context.uc_sigmask)); * else: */ //sigemptyset(&(native_context.uc_sigmask)); if (sigfillset(&(native_context.uc_sigmask)) == -1) { err(1, "native_isr_entry(): sigfillset()"); } makecontext(&native_context, native_irq_handler, 0); #ifdef NATIVEUSESWAPCONTEXT /** * XXX: * This seems to be the cause of undefined behavior. Try saving * the context passed to this function and using setcontext to * leave the handler. * Also it is probably wise to makecontext the isr context * elsewehere. * */ if ((swapcontext((ucontext_t*)active_thread->sp, &native_context)) == -1) { err(1, "swapcontext failed"); } else { DEBUG("returning to interrupted thread\n"); } native_in_irs = 0; enableIRQ(); #else interrupted_contexts[thread_getpid()] = context; /** * signal masks are broken */ //active_thread->sp = context; setcontext(&native_context); DEBUG("\n\n\tnever reached\n\n\n"); #endif } /** * register signal/interrupt handler for signal sig * * TODO: check sa_flags for appropriateness * TODO: use appropriate data structure for signal * handlers. */ int register_interrupt(int sig, void *handler) { struct sigaction sa; DEBUG("XXX: register_interrupt()\n"); if (sigdelset(&native_sig_set, sig)) { err(1, "register_interrupt: sigdelset"); } native_irq_handlers[sig].func = handler; sa.sa_sigaction = (void*) native_isr_entry; /* sa.sa_handler = (void*) native_isr_entry; */ if (sigemptyset(&sa.sa_mask) == -1) { err(1, "register_interrupt: sigemptyset"); } sa.sa_flags = SA_RESTART | SA_SIGINFO; if (sigaction(sig, &sa, NULL)) { err(1, "register_interrupt: sigaction"); } return 0; } /** * empty signal mask * * TODO: see register_interrupt * TODO: ignore signal */ int unregister_interrupt(int sig) { struct sigaction sa; DEBUG("XXX: unregister_interrupt()\n"); if (sigaddset(&native_sig_set, sig) == -1) { err(1, "unregister_interrupt: sigaddset"); } native_irq_handlers[sig].func = NULL; /* sa.sa_sigaction = SIG_IGN; */ sa.sa_handler = SIG_IGN; if (sigemptyset(&sa.sa_mask) == -1) { err(1, "unregister_interrupt: sigemptyset"); } sa.sa_flags = SA_RESTART | SA_SIGINFO; if (sigaction(sig, &sa, NULL)) { err(1, "unregister_interrupt: sigaction"); } return 0; } /** * register internal signal handler, * initalize local variables * * TODO: see register_interrupt */ void native_interrupt_init(void) { struct sigaction sa; DEBUG("XXX: native_interrupt_init()\n"); native_interrupts_enabled = 1; for (int i = 0; i<255; i++) { native_irq_handlers[i].func = NULL; } sa.sa_sigaction = (void*) native_isr_entry; if (sigemptyset(&sa.sa_mask) == -1) { err(1, "native_interrupt_init: sigemptyset"); } sa.sa_flags = SA_RESTART | SA_SIGINFO; /* if (sigemptyset(&native_sig_set) == -1) { err(1, "native_interrupt_init: sigemptyset"); } */ if (sigprocmask(SIG_SETMASK, NULL, &native_sig_set) == -1) { err(1, "native_interrupt_init(): sigprocmask"); } if (sigdelset(&native_sig_set, SIGUSR1) == -1) { err(1, "native_interrupt_init: sigdelset"); } if (sigaction(SIGUSR1, &sa, NULL)) { err(1, "native_interrupt_init: sigaction"); } if (sigdelset(&native_sig_set, SIGALRM) == -1) { err(1, "native_interrupt_init: sigdelset"); } if (sigaction(SIGALRM, &sa, NULL)) { err(1, "native_interrupt_init: sigaction"); } if (sigprocmask(SIG_SETMASK, &native_sig_set, NULL) == -1) { err(1, "native_interrupt_init(): sigprocmask"); } for (int i = 0; i