Hello,
I was posting on the mailing list just before, not sure if this here is necessary or if I should have posted here in the first place. I am working on a Linux-like syscall handler in user-space and want to implement a trap-and-emulate like mechanism.
Firstly, some background information and motivation. I see that the seL4 system call API uses negative syscall numbers. This allows me theoretically to use positive syscall numbers to implement an additional syscall API. The seL4 kernel triggers an UnknownSyscall Exception and leads it to the fault handler
endpoint in user space of the corresponding CAmkES component.
I work with CAmkES and was able to create a fault handler similar to the one implemented in GDBMem with the help of templates. I am generally able to catch the exception in a separate thread in the same component and get the corresponding arguments. What I don’t seem to manage is redirecting the program flow so that we return to right after the faulting syscall instruction in the control thread. It generally just retriggers the same exception or a different one (the VMFault exception).
To better understand what I did, I quickly provide the code:
- My C code: Triggers the system call number 60 with 6 arguments:
int run(void) {
while(1){
register int rax asm (“rax”) = 60;
register int rdi asm(“rdi”) = 1;
register int rsi asm(“rsi”) = 2;
register int rdx asm(“rdx”) = 3;
register int r10 asm(“r10”) = 4;
register int r8 asm(“r8”) = 5;
register int r9 asm(“r9”) = 6;
asm volatile (
“syscall”
: “+r” (rax)
: “r” (rdi), “r” (rsi), “r” (rdx), “r” (r10), “r” (r8), “r” (r9)
: “rcx”, “r11”, “memory”);
printf(“After the syscall instruction!\n”);
}
}
- My CAmkES code: We connect two threads of the component CompA, the faultIn thread is related to the From Template, the faultOut thread is related to the To template.
connector camkesFaultHandlerThread {
from Procedure;
to Procedure;
}procedure CAmkES_FaultHandlerThread {
void bla();
}component CompA {
control;
uses CAmkES_FaultHandlerThread faultIn;
provides CAmkES_FaultHandlerThread faultOut;
}assembly {
composition {
component CompA compA;
connection camkesFaultHandlerThread fault0 (from compA.faultIn, to compA.faultOut);
}
}
- The from Template: Waits on the fault endpoint object of TCBs in the same
component
/- set thread_caps = [] -/
/- set fault_ep = alloc(“fault”, seL4_EndpointObject, read=True, write=True, grantreply=True) -//- for cap in cap_space.cnode: -/
/- if isinstance(cap_space.cnode[cap].referent, capdl.TCB): -/
/- set cap_name = cap_space.cnode[cap].referent.name-/
/- do thread_caps.append((cap, cap_name)) -/
/- endif -/
/- endfor -//- for cap, cap_name in thread_caps: -/
/- do cap_space.cnode[fault_ep].set_badge(cap) -/
/- do cap_space.cnode[cap].referent.set_fault_ep_slot(fault_ep) -/
/- endfor -/
- The to Template: gets triggered with every fault, should handle the system call and then redirect program flow
/- set fault_ep = alloc(“fault”, seL4_EndpointObject, read=True, write=True, grantreply=True) -/
/- set info = c_symbol(‘info’) -/int /? me.interface.name ?/__run(void) {
seL4_Word fault_type;
seL4_Word length;
seL4_Word delegate_tcb;
seL4_UserContext regs;
seL4_MessageInfo_t info;
while (1) {
info = seL4_Recv(/? fault_ep ?/, &delegate_tcb);
seL4_Fault_t fault = seL4_getFault(info);
fault_type = seL4_MessageInfo_get_label(info);if(fault_type == seL4_Fault_UnknownSyscall){ printf("faulting PC: %zx\n",seL4_Fault_UnknownSyscall_get_FaultIP(fault)); //handle system call ... // set the corresponding registers of the faulting control thread, i.e. the one that caused the UnknownSyscall Exception seL4_Fault_UnknownSyscall_set_RAX(fault, 0); seL4_Fault_UnknownSyscall_set_RCX(fault, seL4_Fault_UnknownSyscall_get_FaultIP(fault) + 2); seL4_Fault_UnknownSyscall_set_R11(fault, seL4_Fault_UnknownSyscall_get_FLAGS(fault)); length = seL4_MessageInfo_get_length(info); seL4_TCB_ReadRegisters(delegate_tcb, false, 0, sizeof(seL4_UserContext) / sizeof(seL4_Word), ®s); // Which registers should I set for the faultOut thread?, right now simply jump over the syscall instruction regs.rip += 2; // Write registers back seL4_TCB_WriteRegisters(delegate_tcb, false, 0, sizeof(seL4_UserContext) / sizeof(seL4_Word), ®s); // Resume the caller seL4_MessageInfo_t info = seL4_MessageInfo_new(0, 0, 0, length); seL4_Reply(info); } else if(fault_type = seL4_Fault_VMFault){ // Why do we get in here? } }
}
- the available threads in the system before triggering the fault:
compA:faultOut running 0x401626 254 0 compA:faultIn running 0x401626 254 0 compA:fault_handler blocked on recv 0x401626 255 0 compA:control running 0x4011f7 254 0 idle_thread idle 0 0 0 rootserver inactive 0x4014bb 255 0
- the available threads in the system after triggering the fault:
compA:faultOut running 0x406d69 254 0 compA:faultIn blocked on recv 0x401626 254 0 compA:fault_handler blocked on recv 0x401626 255 0 compA:control blocked on reply 0x40146d 254 0 idle_thread idle 0 0 0 rootserver inactive 0x4014bb 255 0
So, I have a faulting syscall instruction in the control thread, i.e. the one in the run method. This triggers the UnknownSyscall Exception and somehow, the faultIn thread blocks then and we receive on the fault endpoint in the faultOut thread. I can catch this fault and handle it in the faultOut thread. What I want to achieve is that after handling the fault, I want to return to the control thread right after the faulting syscall instruction, i.e. 0x40146d + 2. The registers in the regs struct consists of the values in the faultIn thread. The values we can set/get from seL4_Fault_UnknownSyscall_set/get_X are the ones from the control thread, i.e. the actual faulting syscall instruction.
My problem is that I can’t properly redirect the control flow without triggering the same exception or a different one.
Has anyone ever tried something similar? Help would be very much appreciated.
Thank you very much!
Kind Regards,
Lukas