Sambuc, There are two methods for reaching exceptions. One is the threaded irq method which you have discovered. The other one which is relevant for operating systems, you need to install exception handlers for each thread using the l4_exchange_registers syscall. This installation must be done per-thread, during thread initialization. It is an asynchronous mechanism, e.g. when a thread is executing and hits an exception, the exception handler gets called within that thread's context, using a different exception stack. I was going to offer l4_exchange_registers manpage but it seems this feature is not there - we will update that.
Below is example OS virtualization code which we provide for understanding the concept better. Note the usage of a separate exception stack, and the exregs_set_handler helper function. There are other handlers specified at different indexes, please check conts/userlibs/libl4/include/l4/arch/arm/exception.h for more. Below code does a few things. It creates threads, it uses voluntary scheduling so that you can implement your own kernel scheduling policy, it also installs timer interrupts for asynchronous exception handling and preemptive multitasking. The below code should work for QEMU/versatile express model. Hope it helps, Bahadir /* * Main function for this container */ #include <string.h> #include <l4lib/kip.h> #include <l4lib/exregs.h> #include <l4/api/irq.h> #include <l4/arch/arm/exception.h> #include <dev/platform.h> #include <dev/timer.h> #include <dev/io.h> #include <l4/glue/arm/guest.h> #include <l4lib/lib/cap.h> #include <l4lib/mutex.h> #include L4LIB_INC_ARCH(syslib.h) #include L4LIB_INC_ARCH(syscalls.h) extern int print_hello_world(void); #define TICKS_PER_SWITCH 5 #define TASKS_TOTAL 4 #define TIMER_IRQ 35 #define TIMER_BASE 0x10012000 #define TIMER_VALUE 1000000 static int guest_domain = -1; #define irq_global_state (guest_get_user_data(guest_domain)->irq_state) #define PARAVIRT_IRQS_ENABLED 1 #define PARAVIRT_IRQS_DISABLED 0 void dmb() { __asm__ __volatile__ ( "dmb" :: ); } #define THREAD_STACK_SIZE PAGE_SIZE #define IRQ_STACK_SIZE PAGE_SIZE static char __attribute__((aligned(0x1000))) timer_page[0x1000]; static u8 __attribute__((aligned(8))) irq_stack[TASKS_TOTAL][IRQ_STACK_SIZE]; static u8 __attribute__((aligned(8))) thread_stack[TASKS_TOTAL][THREAD_STACK_SIZE]; static u8 __attribute__((aligned(8))) init_irq_stack[IRQ_STACK_SIZE]; static struct task_ids threads[TASKS_TOTAL]; #define timer_base ((unsigned long)timer_page) void timer_irq_handler(void); /* This is needed for atomic clearing of pending guest irq bits */ int paravirt_ack_mask_irq(unsigned int irq) { int err; if ((err = l4_irq_control(IRQ_CONTROL_ACK_MASK, 0, irq)) < 0) { printf("Irq ack/mask failed. err=%d, irq=%u\n", err, irq); return err; } return 0; } static unsigned int timer_ticks = 0; static int current_thread = TASKS_TOTAL; void schedule() { int from; int to; from = current_thread; current_thread++; if (current_thread >= TASKS_TOTAL) current_thread = 0; to = current_thread; printf("Switching from %d to %d\n", threads[from].tid, threads[to].tid); if ((l4_context_switch(threads[to].tid, CSWITCH_STOP, 0) < 0)) bug(); } void timer_irq_handler(void) { int err; irq_global_state = PARAVIRT_IRQS_DISABLED; unsigned int sp; __asm__ __volatile__( "mov %0, sp\n" : "=r"(sp) : ); printf("IRQ stack=0x%x\n", sp); timer_irq_clear(timer_base); paravirt_ack_mask_irq(TIMER_IRQ); if ((err = l4_irq_control(IRQ_CONTROL_ENABLE, 0, TIMER_IRQ)) < 0) { printf("Irq enable failed. err=%d, irq=%u\n", err, TIMER_IRQ); bug(); } irq_global_state = PARAVIRT_IRQS_ENABLED; if (timer_ticks++ % TICKS_PER_SWITCH == 0) schedule(); printf("Returning from timer irq tid=%d\n", self_tid()); l4_context_switch(0, CSWITCH_PREV_CONTEXT, 0); } #define DLY 150 void guest_thread(void) { struct task_ids ids; int c, d, e; l4_getid(&ids); while (1) { c++; if ((c %= DLY) == 0) { d++; if ((d %= DLY) == 0) { e++; if ((e %= DLY) == 0) { printf("MSG from tid: %d\n", ids.tid); } } } } } void init_thread_setup(void) { struct exregs_data exregs; struct task_ids ids; int err; l4_getid(&ids); memset(&exregs, 0, sizeof(exregs)); exregs_set_handler_pc(&exregs, l4_irq_handler, (unsigned long)timer_irq_handler); exregs_set_handler_sp(&exregs, l4_irq_handler, (unsigned long)init_irq_stack); if ((err = l4_exchange_registers(&exregs, ids.tid)) < 0) { printf("l4_exchange_registers error\n"); bug(); } } void thread_create(struct task_ids *ids, void (*func)(void), void *stack, void *irq_stack) { struct exregs_data exregs; int err; l4_getid(ids); /* Create thread in kernel */ if ((err = l4_thread_control(THREAD_CREATE | TC_SHARE_SPACE, ids)) < 0) { printf("l4_thread_control error\n"); bug(); } /* Setup new thread pc, sp, utcb */ memset(&exregs, 0, sizeof(exregs)); exregs_set_stack(&exregs, (unsigned long)stack); exregs_set_pc(&exregs, (unsigned long)func); exregs_set_handler_pc(&exregs, l4_irq_handler, (unsigned long)timer_irq_handler); exregs_set_handler_sp(&exregs, l4_irq_handler, ((unsigned long)irq_stack)); if ((err = l4_exchange_registers(&exregs, ids->tid)) < 0) { printf("l4_exchange_registers error\n"); bug(); } } void create_threads(void) { for (int i = 0; i < TASKS_TOTAL; i++) { thread_create(&threads[i], guest_thread, &thread_stack[i][THREAD_STACK_SIZE], &irq_stack[i][IRQ_STACK_SIZE]); } } int main(void) { int err; print_hello_world(); create_threads(); guest_domain = cap_read_domain(); irq_global_state = PARAVIRT_IRQS_DISABLED; if ((err = l4_map((void *)TIMER_BASE, (void *)timer_page, 1, MAP_USR_IO, self_tid())) < 0) { printf("FATAL: Failed to map TIMER device from 0x%x" " to 0x%lx (err: %d)\n", TIMER_BASE, timer_base, err); bug(); } timer_init_periodic(timer_base, TIMER_VALUE); timer_start(timer_base); irq_global_state = PARAVIRT_IRQS_ENABLED; printf("Registering IRQ%d handler for the main thread\n", TIMER_IRQ); init_thread_setup(); /* Register and enable irqs */ if ((err = l4_irq_control(IRQ_CONTROL_REGISTER, IRQ_USER_HANDLER, TIMER_IRQ)) < 0) { printf("%s: FATAL: Timer irq could not be registered. " "err=%d\n", __FUNCTION__, err); bug(); } schedule(); bug(); return 0; } On Tue, 2011-03-15 at 07:29 +0000, Sambuc Lionel wrote: > Hello, > > For a master course > (http://www.reds.ch/Formations/Master/SEEEDoc.aspx),<http://www.reds.ch/Formations/Master/SEEEDoc.aspx%29,> > which is about embedded runtimes and Operating Systems, last year we have > written a simple monolitic kernel for our student to learn some about OS > kernels and virtualization. This Kernel runs on top of OKL4, as it enables us > to also present virtualization and microkernel concepts. > > This year we decided to replace the OKL4 kernel with CodeZero's in order to > reduce the code base and allow our students to focus on the notions they have > to acquire. > > During the port of that kernel, I am hitting the following problem: > * How can I retrieve exceptions in my container. I have found a way for > IRQs, but nothing for exceptions, which is a problem as it is a central > notion. > > Regards, > > Lionel Sambuc > > --- > Lionel Sambuc, HES-SO BSc in Computer Science > Tel. : +41 24 55 762 84 > lionel.sam...@heig-vd.ch > > REDS Institute, Reconfigurable & Embedded Digital Systems > http://www.reds.ch > > HEIG-VD, School of Business and Engineering Vaud Haute > Ecole d'Ingénierie et de Gestion du Canton de Vaud > Route de Cheseaux 1 > CH-1401 Yverdon-les-Bains > http://www.heig-vd.ch > > _______________________________________________ > codezero-devel mailing list > codezero-devel@lists.l4dev.org > http://lists.l4dev.org/mailman/listinfo/codezero-devel_lists.l4dev.org _______________________________________________ codezero-devel mailing list codezero-devel@lists.l4dev.org http://lists.l4dev.org/mailman/listinfo/codezero-devel_lists.l4dev.org