OSDev.org

The Place to Start for Operating System Developers
It is currently Thu Mar 28, 2024 10:33 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 23 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: User mode syscalls problem
PostPosted: Mon Oct 03, 2022 9:20 am 
Offline
Member
Member

Joined: Thu Jul 14, 2022 9:45 am
Posts: 91
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:
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:
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:
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;
}


Top
 Profile  
 
 Post subject: Re: User mode syscalls problem
PostPosted: Mon Oct 03, 2022 1:11 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5099
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.


Top
 Profile  
 
 Post subject: Re: User mode syscalls problem
PostPosted: Mon Oct 03, 2022 2:28 pm 
Offline
Member
Member

Joined: Thu Jul 14, 2022 9:45 am
Posts: 91
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:
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:
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?


Top
 Profile  
 
 Post subject: Re: User mode syscalls problem
PostPosted: Mon Oct 03, 2022 3:07 pm 
Offline
Member
Member
User avatar

Joined: Mon Jun 05, 2006 11:00 pm
Posts: 2293
Location: USA (and Australia)
WinExperements wrote:
Code:
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?

_________________
My OS is Perception.


Top
 Profile  
 
 Post subject: Re: User mode syscalls problem
PostPosted: Mon Oct 03, 2022 3:12 pm 
Offline
Member
Member

Joined: Thu Jul 14, 2022 9:45 am
Posts: 91
AndrewAPrice wrote:
WinExperements wrote:
Code:
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:
#define PUSH_ASM(vol) __asm__ __volatile__("pushl %%eax" : : "a" (vol))


Top
 Profile  
 
 Post subject: Re: User mode syscalls problem
PostPosted: Mon Oct 03, 2022 3:40 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5099
Inline assembly isn't allowed to modify the stack pointer.


Top
 Profile  
 
 Post subject: Re: User mode syscalls problem
PostPosted: Mon Oct 03, 2022 4:14 pm 
Offline
Member
Member

Joined: Fri Feb 11, 2022 4:55 am
Posts: 435
Location: behind the keyboard
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.


Top
 Profile  
 
 Post subject: Re: User mode syscalls problem
PostPosted: Tue Oct 04, 2022 3:41 am 
Offline
Member
Member

Joined: Thu Jul 14, 2022 9:45 am
Posts: 91
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:
[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?


Top
 Profile  
 
 Post subject: Re: User mode syscalls problem
PostPosted: Tue Oct 04, 2022 4:18 am 
Offline
Member
Member

Joined: Fri Feb 11, 2022 4:55 am
Posts: 435
Location: behind the keyboard
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:
PageFaultISR:
       pusha
       call PageFaultHandler
       popa
       iretd


Top
 Profile  
 
 Post subject: Re: User mode syscalls problem
PostPosted: Tue Oct 04, 2022 4:30 am 
Offline
Member
Member

Joined: Fri Feb 11, 2022 4:55 am
Posts: 435
Location: behind the keyboard
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:
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:
    struct {
         (Optionnal) UINT64 StatusCode; // Some interrupts do not push a status code
        UINT64        InstructionPointer;
        UINT64        CodeSegment;
        UINT64        CpuRflags;
        UINT64        StackPointer;
        UINT64        StackSegment;
    } IntStack;


Top
 Profile  
 
 Post subject: Re: User mode syscalls problem
PostPosted: Tue Oct 04, 2022 6:08 am 
Offline
Member
Member

Joined: Thu Jul 14, 2022 9:45 am
Posts: 91
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:
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?


Top
 Profile  
 
 Post subject: Re: User mode syscalls problem
PostPosted: Tue Oct 04, 2022 12:45 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5099
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:
; 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.


Top
 Profile  
 
 Post subject: Re: User mode syscalls problem
PostPosted: Tue Oct 04, 2022 12:56 pm 
Offline
Member
Member

Joined: Thu Jul 14, 2022 9:45 am
Posts: 91
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.


Top
 Profile  
 
 Post subject: Re: User mode syscalls problem
PostPosted: Tue Oct 04, 2022 12:56 pm 
Offline
Member
Member

Joined: Fri Feb 11, 2022 4:55 am
Posts: 435
Location: behind the keyboard
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:
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.


Top
 Profile  
 
 Post subject: Re: User mode syscalls problem
PostPosted: Tue Oct 04, 2022 1:01 pm 
Offline
Member
Member

Joined: Fri Feb 11, 2022 4:55 am
Posts: 435
Location: behind the keyboard
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.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 23 posts ]  Go to page 1, 2  Next

All times are UTC - 6 hours


Who is online

Users browsing this forum: DotBot [Bot], Majestic-12 [Bot], SemrushBot [Bot] and 61 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group