Page 1 of 2
User mode syscalls problem
Posted: Mon Oct 03, 2022 9:20 am
by WinExperements
Hello! I have some problems with syscalls and user mode, when user space program calls syscall, then the arguments that passed to the handlers seems a little incorrect and when the function returns i got #PF with random address. Yeah i know that it's because the address is the last that pushed into it, but why the arguments are changes?
Here some code of multitasking,IRQ and syscalls:
process scheduling:
Code: Select all
if (disableScheduler) return;
struct process *kill = process_findByStatus(PROCESS_KILLING);
if (kill != NULL) {
__process_destroy(kill);
if (runningTask == kill) {
runningTask = NULL;
}
}
if (runningTask != NULL) {
// Simply select new task
arch_saveRegs(stack,runningTask->esp);
runningTask = process_findNextByStatus(PROCESS_RUNNING,runningTask->lAddr);
runningTask->quota = 0;
} else {
// first switch
runningTask = process_findByStatus(PROCESS_RUNNING);
}
if (runningTask == NULL) {
// okay switch to idle
runningTask = idle;
}
// change the TSS and VMM
tss_set_stack(0x10,runningTask->kernelESP);
if (!pmml_isPageAllocated((void *)runningTask->dir)) {
printf("proc: AAA\n");
return;
}
vmm_switch((int *)runningTask->dir);
arch_switchContext(runningTask->esp);
}
IRQ handler:
Code: Select all
push eax
push ecx
push edx
push ebx
push ebp
push esi
push edi
mov ax,ds
push eax
mov ax, 0x10 ; load the kernel data segment descriptor
mov ds, eax
mov es, eax
mov fs, eax
mov gs, eax
push esp
call irq_handler
mov esp,eax
jmp irq_handler_exit ; Exit from this code using jmp
[GLOBAL irq_handler_exit]
irq_handler_exit:
pop ebx
mov ds, ebx
mov es, ebx
mov fs, ebx
mov gs, ebx
pop edi
pop esi
pop ebp
pop ebx
pop edx
pop ecx
pop eax
add esp, 8 ; Cleans up the pushed error code and pushed ISR number
iret ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP
And syscall handler:
Code: Select all
registers_t *syscall_handler(registers_t *regs) {
if (regs->eax > 23) {
printf("No such syscall number: %d\n",regs->eax);
process_kill(process_getCurrentPID());
process_yield();
} else {
int (*handler)(int,int,int,int,int) = (int (*)(int,int,int,int,int))syscalls[regs->eax];
regs->eax = handler(regs->edx,regs->ecx,regs->ebx,regs->edi,regs->esi);
}
return regs;
}
Re: User mode syscalls problem
Posted: Mon Oct 03, 2022 1:11 pm
by Octocontrabass
WinExperements wrote:when user space program calls syscall, then the arguments that passed to the handlers seems a little incorrect
Incorrect how? I don't see anything that would obviously corrupt your syscall arguments, although your design decisions are very strange. (Why have separate "ISR" and "IRQ" code? Why return anything from the C portion of your ISR when you never use it to switch stacks? Why are syscalls in the "IRQ" category?)
WinExperements wrote:and when the function returns i got #PF with random address.
Probably caused by the same thing that's corrupting your syscall arguments.
I'd start by running QEMU with "-d int" and see if the register dumps from the syscall and the subsequent #PF reveal anything interesting.
Re: User mode syscalls problem
Posted: Mon Oct 03, 2022 2:28 pm
by WinExperements
Octocontrabass wrote:I'd start by running QEMU with "-d int" and see if the register dumps from the syscall and the subsequent #PF reveal anything interesting.
Yeah, i found that it's begin rewriten after the scheduler changes task, this is my functions for saving and restoring task stack
Code: Select all
void arch_saveRegs(void *from,void *to) {
registers_t *f = (registers_t *)from;
registers_t *tt = (registers_t *)to;
tt->eax = f->eax;
tt->ebx = f->ebx;
tt->ecx = f->ecx;
tt->edx = f->edx;
tt->ebp = f->ebp;
tt->esi = f->esi;
tt->edi = f->edi;
tt->eip = f->eip;
}
Code: Select all
registers_t *regs = (registers_t *)stack;
PUSH_ASM(regs->ss);
PUSH_ASM(regs->useresp);
PUSH_ASM(regs->eflags);
PUSH_ASM(regs->cs);
PUSH_ASM(regs->eip);
PUSH_ASM(0);
PUSH_ASM(0);
PUSH_ASM(regs->eax);
PUSH_ASM(regs->ecx);
PUSH_ASM(regs->edx);
PUSH_ASM(regs->ebx);
PUSH_ASM(regs->ebp);
PUSH_ASM(regs->esi);
PUSH_ASM(regs->edi);
PUSH_ASM(regs->ds);
asm volatile("jmp irq_handler_exit");
It's pushed all the registers only for test, i delete this soon.
Any reasons?
Re: User mode syscalls problem
Posted: Mon Oct 03, 2022 3:07 pm
by AndrewAPrice
WinExperements wrote:
Code: Select all
registers_t *regs = (registers_t *)stack;
PUSH_ASM(regs->ss);
PUSH_ASM(regs->useresp);
PUSH_ASM(regs->eflags);
PUSH_ASM(regs->cs);
PUSH_ASM(regs->eip);
PUSH_ASM(0);
PUSH_ASM(0);
PUSH_ASM(regs->eax);
PUSH_ASM(regs->ecx);
PUSH_ASM(regs->edx);
PUSH_ASM(regs->ebx);
PUSH_ASM(regs->ebp);
PUSH_ASM(regs->esi);
PUSH_ASM(regs->edi);
PUSH_ASM(regs->ds);
asm volatile("jmp irq_handler_exit");
I don't think this will work as you intend. How is PUSH_ASM defined?
Re: User mode syscalls problem
Posted: Mon Oct 03, 2022 3:12 pm
by WinExperements
AndrewAPrice wrote:WinExperements wrote:
Code: Select all
registers_t *regs = (registers_t *)stack;
PUSH_ASM(regs->ss);
PUSH_ASM(regs->useresp);
PUSH_ASM(regs->eflags);
PUSH_ASM(regs->cs);
PUSH_ASM(regs->eip);
PUSH_ASM(0);
PUSH_ASM(0);
PUSH_ASM(regs->eax);
PUSH_ASM(regs->ecx);
PUSH_ASM(regs->edx);
PUSH_ASM(regs->ebx);
PUSH_ASM(regs->ebp);
PUSH_ASM(regs->esi);
PUSH_ASM(regs->edi);
PUSH_ASM(regs->ds);
asm volatile("jmp irq_handler_exit");
I don't think this will work as you intend. How is PUSH_ASM defined?
Code: Select all
#define PUSH_ASM(vol) __asm__ __volatile__("pushl %%eax" : : "a" (vol))
Re: User mode syscalls problem
Posted: Mon Oct 03, 2022 3:40 pm
by Octocontrabass
Inline assembly isn't allowed to modify the stack pointer.
Re: User mode syscalls problem
Posted: Mon Oct 03, 2022 4:14 pm
by devc1
Try disassembling the C functions, it is probably that GCC is pushing some registers into the stack and you are not popping the correct amount of values before returning from the handler.
Also Try to return in C code instead of jump and see if it proves what I said before.
Can't you just push the values in assembly ?
It is never recommended to jump manually within a C function.
Re: User mode syscalls problem
Posted: Tue Oct 04, 2022 3:41 am
by WinExperements
devc1 wrote:It is never recommended to jump manually within a C function.
Yeah i rewriten it in assembly, but now i have #UD in bochs, and #GP in QEMU.
I writen it like this:
Code: Select all
[extern irq_handler_exit]
[global arch_switchContext]
arch_switchContext:
mov esi,[esp+4]
push dword [esi+56]
push dword [esi+52]
push dword [esi+48]
push dword [esi+44]
push dword [esi+40]
push dword 0
push dword 0
push dword [esi+28]
push dword [esi+24]
push dword [esi+20]
push dword [esi+16]
push dword [esi+12]
push dword [esi+8]
push dword [esi+4]
push dword [esi+0]
jmp irq_handler_exit
Maybe the problem is context saving?
Re: User mode syscalls problem
Posted: Tue Oct 04, 2022 4:18 am
by devc1
Are you calling arch_switchContext from C ?
it is never recommended to jump manually within C function and within a function called from C. Basicly the Interrupt stack frame isn't well setup, if you call exit_handler from C it will push the return address.
Here is my recommended handler :
- Just create a normal C function without any special attributes.
- Create the handler (or wrapper) in assembly, it will just push registers, call the C function, pop them then iret.
Example :
Code: Select all
PageFaultISR:
pusha
call PageFaultHandler
popa
iretd
Re: User mode syscalls problem
Posted: Tue Oct 04, 2022 4:30 am
by devc1
Here is an explanation of function calls and interrupts.
- The call instruction is actually a task switch and all the Flags are affected, it sets the TS Flag (Task switched) and pushes the return address (EIP) in the stack.
- the ret instruction pops the return address from the stack pointed to by EIP and jumps to it, it has an optional 16 bit argument which contains the number of additional bytes to pop of the stack.
Code: Select all
ret ; Pops EIP and jumps
ret 32 ; Pops EIP along with 32 bytes from the stack and jumps
There is no pushed ISR number, and there is no meaning of pushed the Stack Pointer itself.
- When an interrupt occurs, a stack frame is pushed to the stack, the iret instruction pops this stack frame to return to the previous task. An invalid stack frame will cause these #UD #PF #GPF....
The thing is that the CPU is jumping to an invalid address because you're not preserving the value of EIP.
Format of a stack frame :
On a task scheduler, we create our own stack frame in order to switch to that task.
Code: Select all
struct {
(Optionnal) UINT64 StatusCode; // Some interrupts do not push a status code
UINT64 InstructionPointer;
UINT64 CodeSegment;
UINT64 CpuRflags;
UINT64 StackPointer;
UINT64 StackSegment;
} IntStack;
Re: User mode syscalls problem
Posted: Tue Oct 04, 2022 6:08 am
by WinExperements
My irq handler_exit called from assembly code, but i rewriten the timer interrupt handler, and i now i have only #GF with invalid segment.
My question is how i can pass arguments to arch_restoreContext correctly? Because my code seems strange
This is my code:
Code: Select all
scheduler_irq:
push 0
push 0
push eax
push ecx
push edx
push ebx
push ebp
push esi
push edi
mov ax,ds
push eax
mov ax, 0x10 ; load the kernel data segment descriptor
mov ds, eax
mov es, eax
mov fs, eax
mov gs, eax
push esp
call process_schedule
; Push only for test, our ESP from runningTask
mov esi,[runningTask+0]
push dword [esi+56]
push dword [esi+52]
push dword [esi+48]
push dword [esi+44]
push dword [esi+40]
push esi
jmp arch_switchContext
EDIT: How i can use trap frame to switch thread context?
Re: User mode syscalls problem
Posted: Tue Oct 04, 2022 12:45 pm
by Octocontrabass
You're making this a lot more complicated than it needs to be.
You can save and restore a thread's context by switching stacks. The function you call to do that might look like this:
Code: Select all
; void switchStack( unsigned * oldesp, unsigned newesp )
switchStack:
push ebx
push ebp
push esi
push edi
mov eax, [esp+20]
mov [eax], esp
mov esp, [esp+24]
pop edi
pop esi
pop ebp
pop ebx
ret
This code doesn't include updating the TSS and CR3 - you can add that code to this function, or do it separately before calling this function.
If a thread has ring 3 context, the ring 3 context will be at the bottom of the stack (at the highest address). Threads don't need to have a ring 3 context - you can have threads that run entirely in ring 0, and this function will still save and restore their context.
Re: User mode syscalls problem
Posted: Tue Oct 04, 2022 12:56 pm
by WinExperements
Octocontrabass wrote:If a thread has ring 3 context, the ring 3 context will be at the bottom of the stack (at the highest address). Threads don't need to have a ring 3 context - you can have threads that run entirely in ring 0, and this function will still save and restore their context.
Yeah i see'd the Brendan's multitasking tutorial and even used that before the re-design.
Your example can save and restore tasks in ring 3? This must called by the interrupt handler or the scheduler method?
I ask this questions to understand more about the context switch with this design.
Re: User mode syscalls problem
Posted: Tue Oct 04, 2022 12:56 pm
by devc1
WinExperiments wrote:How i can use trap frame to switch thread context?
You see the structure I mentionned before ? An IRQ does not push a status code.
You will just do this :
Context switching to the new thread
Code: Select all
push (Thread_EIP)
push (Thread_CS)
push (Thread_Eflags)
push (Thread_ESP)
push (Thread_SS)
iret
Just for your information : you must save thread's EIP,CS,EFLAGS,ESP,SS from the stack frame, they're pushed by the CPU because their values are changed when passing control to the handler.
I was also lost when trying to create a scheduler, reading the intel manual is what helped me.
Re: User mode syscalls problem
Posted: Tue Oct 04, 2022 1:01 pm
by devc1
I am talking to you as you're creating a scheduler.
You're trying to create an interrupt for system calls right ? So you don't need to make a stack frame manually.