How to nest svc, eg
void svcall_handler(){ if(smth_happened()){ asm volatile("svc #0"); } }
Thanks.
Answer for my question..
It's not simply possible but we can do some workaround.
In the handler we need to make additional stack frame that instructs the processor to jump to another handler in Thread mode. And preserve existent stack frame to be able return original interrupted address.
Then we need to create veneer that will jump to original interrupted address after our handler completed.
SVC handler, also can be PendSV handler. Create stack frame and return from exception:
extern "C" __attribute__((naked)) void eSVCall(){ // naked because we don't want compiler smash the stack asm volatile( // prepare variables // fresh PSR for our handler "mov r2, %0 \n" // PC - address of handler "movw r1, #:lower16:svcall_handler \n" "movt r1, #:upper16:svcall_handler \n" // LR - address of handler return veneer "movw r0, #:lower16:svcall_handler_return \n" "movt r0, #:upper16:svcall_handler_return \n" // create stack frame "push {r0, r1, r2} \n" // push {lr, pc, xPSR} "sub sp, #5*4 \n" // push {r0,r1,r2,r3,r12} - undefined values // return from this exception to Pendsv_handler "bx lr \n" : :"i"(xPSR_RESET_VALUE)); }
Veneer jump to instruction that has been interrupted by PendSV/SVC - simulate processor unstacking behavior:
extern "C" __attribute__((naked)) void svcall_handler_return(){ asm volatile( // load xPSR from stack and restore "ldr r0, [sp, #7*4] \n" "msr APSR, r0 \n" // load PC from stack and place it to xPSR place. see later "ldr r0, [sp, #6*4] \n" // PC "orr r0, #1 \n" // set T-bit (Thumb state) "str r0, [sp, #7*4] \n" // xPSR = PC in stack // pop remaining registers "pop {r0, r1, r2, r3, r12, lr} \n" "add sp, #4 \n" // skip PC (old, dup) // do the jump "pop {pc} \n" ); }
Usage of this mess is quite simple :
extern "C" void svcall_handler(){ if(smth_happened()){ asm volatile("svc #0"); } }
It works for synchronous exceptions(SVC) but wont work properly with asynchronous exceptions like PendSV because we cannot write directly to EPSR by MSR instruction. So if the processor interrupts on continuable instruction(like LDM/STM/PUSH/POP) or If/Then block it will lead to undefined behavior.
But there is another workaround: rather than restoring state manually we can delegate this work to processor using SVC instruction.
Our PendSV handler will be almost the same:
__attribute__((naked)) void ePendSV(){ asm volatile( // "push {r0,r4-r11, lr} \n" // not necessary, callee will save them // create new stack frame on the top of existing "mov r2, %0 \n" // PSR "movw r1, #:lower16:nested_pendsv_hendler \n" // PC "movt r1, #:upper16:nested_pendsv_hendler \n" "movw r0, #:lower16:pendsv_handler_return \n" // LR "movt r0, #:upper16:pendsv_handler_return \n" "push {r0, r1, r2} \n" // push {lr, pc, xPSR} "sub sp, #5*4 \n" // push {r0,r1,r2,r3,r12} - undefined values // return from exception to our handler "bx lr \n" : :"i"(xPSR_RESET_VALUE)); }
nested_pendsv_handler (just regular C/C++ function) will jump here after return:
extern "C" __attribute__((naked)) void pendsv_handler_return(){ asm volatile("svc #0"); }
and then:
extern "C" __attribute__((naked)) void eSVCall(){ asm volatile( // kill current stack frame. "add sp, #8*4 \n" // perform an EXC_RETURN - unstacking original state preserved by ePendSV earlier "bx lr \n" ); }
Priority of PendSV must be lower than SVC that PendSV will never preempt SVC otherwise it will lead to undefined behavior because of tail-chaining and late-arrival.
Thats all. Usage is quite simple too:
extern "C" nested_pendsv_hendler(){ // do some work if(must_i_reenter){ set_PendSV(); // do another work }else return; }
Why I need it? - I'm using it to build super simple stack-allocation-less preemptive asynchronous task scheduler http://www.embedded.com/design/prototyping-and-development/4025691/Build-a-Super-Simple-Tasker using modern c++11 features(templates, lambdas,...)