Question about User Mode

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
kemosparc
Member
Member
Posts: 207
Joined: Tue Oct 29, 2013 1:13 pm

Question about User Mode

Post by kemosparc »

Hi,

I am trying to switch to user mode but I have some questions as it seems that I have some blanks that need to be filled.

I have a 64-bit hobby OS and I was able to create different processes (Different stack and different C Functions) that run all in kernel mode. I use the PIT to do the switching between them.

SO far it runs good and it does what I want.

Every process runs a new function that has an infinite loop and the loop body just displays a string on the screen at a specific location together with a loop counter.

I followed James Molley tutorial and I read this http://www.jamesmolloy.co.uk/tutorial_h ... 0Mode.html and did some parts of it, and read the OSDev wiki articles http://wiki.osdev.org/Getting_to_Ring_3 and http://wiki.osdev.org/System_Calls.

James Molley's tutorial switchs to user mode in the main function and print a string on the screen, which does not run forever. Also most of the documentation I read are in 32-bit and I don't know if it is the same as 64 or there are other considerations I need to take care of.

In my case I think (correct me if I am wrong) this will be the scenario:
1. create process and at creation switch to usermode through the IRET as per the documentation and off course switch the stack.
2. When PIT occurs, the processor will switch to the RSP0 and SS0 from the GDT TSS selector. (Kernel mode)
3. The processor will go to the interrupt handler of the PIT where the scheduler is.
4. The scheduler will fetch the next task to switch to which is a usermode task (But I am still in kernle mode)

My question is that how can I switch here to the usermode using IRET since the infinite loop is already running and I don't know which instruction I will return to.
I mean in the entry point of the new process I can insert the switch_to_usermode but when I come back from the PIT interrupt where can I do that.

Here is the code of my current scheduler

Code: Select all


    uint64_t rsp,rbp,rip;
    asm volatile("mov %%rsp, %0" : "=r"(rsp));
    asm volatile("mov %%rbp, %0" : "=r"(rbp));
    rip = read_rip();
    if (rip == 0x12345) return;
    tasks[current_task]->setRBP(rbp);
    tasks[current_task]->setRSP(rsp);
    tasks[current_task]->setRIP(rip);

    current_task ++;
    if ( current_task >= task_count ) current_task = 0;

    rbp = tasks[current_task]->getRBP();
    rsp = tasks[current_task]->getRSP();
    rip = tasks[current_task]->getRIP();
    currentkernelPageManager = tasks[current_task]->getPageManager();

    asm volatile("         \
      cli;                 \
      mov %0, %%rcx;       \
      mov %1, %%rsp;       \
      mov %2, %%rbp;       \
      mov %3, %%cr3;       \
      mov $0x12345, %%rax; \
      sti;                 \
      jmp *%%rcx           "
                 : : "r"(rip), "r"(rsp), "r"(rbp), "r"(currentkernelPageManager));
Notice that the "jmp *%%rcx" will take me to where I left the process earlier, but this still is in the kernel mode.

How can I switch back the code selector and the data selector to the GDT user mode entries which are 0x1b and 0x23

Thanks
Karim.
User avatar
JAAman
Member
Member
Posts: 879
Joined: Wed Oct 27, 2004 11:00 pm
Location: WA

Re: Question about User Mode

Post by JAAman »

My question is that how can I switch here to the usermode using IRET since the infinite loop is already running and I don't know which instruction I will return to.
I mean in the entry point of the new process I can insert the switch_to_usermode but when I come back from the PIT interrupt where can I do that.
start by setting the tutorial aside, and pull out your copy of the Intel manuals (you do have them, right? if not check the link in my signature) look in Intel volume 3, and read through chapter 5. read the entire chapter, since you are lacking the most basic knowledge here, although section 5.1 (basic overview of how interrupts are handled) will answer your question in general, the specifics are found later (section 5.6 gives more details, and again repeats the answer to your question, and 5.12 gives much more information that is more specific to your question, while section 5.14 details the differences between 32-bit and 64-bit mode here)

the answer to this question is extremely simple, which is why I know you are lacking some very basic knowledge, and would be better benefited by pointing you to the relevant sections in the manual rather than answering your question myself

if you have further questions after reading those sections, feel free to post followup questions here, and we will try to help you
kemosparc
Member
Member
Posts: 207
Joined: Tue Oct 29, 2013 1:13 pm

Re: Question about User Mode

Post by kemosparc »

Hi,

I have spent the past couple of days reading the manuals chapters you recommended.

I have also tried to implement the user mode switch but I keep on getting a page fault after the "iretq" instruction.

I switch to user mode like that:

Code: Select all

void switch_to_user_mode ()
{
    asm volatile("  \
         cli; \
         mov $0x23, %ax; \
         mov %ax, %ds; \
         mov %ax, %es; \
         mov %ax, %fs; \
         mov %ax, %gs; \
                       \
         mov %rsp, %rax; \
         pushq $0x23; \
         pushq %rax; \
         pushfq; \
         pushq $0x1B; \
         pushq $1f; \
         iretq; \
       1: \
         ");
}

and I call it at the end of the kernel main function:

Code: Select all



extern "C" void kernel_main()
{
.
.
.
.
.
.
.
.
.

    uint64_t rsp;
    asm volatile("mov %%rsp, %0" : "=r"(rsp));
    globalDescriptorTable.setKernelRSP0(rsp);
    switch_to_user_mode();
}
When I run it I get a page fault at address 0x277b1, which is the statement just right after the iretq

Here is the objdump of the code of the switch_to_user_mode () which include this address

Code: Select all

void switch_to_user_mode ()
{
   27790:       55                      push   %rbp
   27791:       48 89 e5                mov    %rsp,%rbp
         pushfq; \
         pushq $0x1B; \
         pushq $1f; \
         iretq; \
       1: \
         ");
   27794:       fa                      cli    
   27795:       66 b8 23 00             mov    $0x23,%ax
   27799:       8e d8                   mov    %eax,%ds
   2779b:       8e c0                   mov    %eax,%es
   2779d:       8e e0                   mov    %eax,%fs
   2779f:       8e e8                   mov    %eax,%gs
   277a1:       48 89 e0                mov    %rsp,%rax
   277a4:       6a 23                   pushq  $0x23
   277a6:       50                      push   %rax
   277a7:       9c                      pushfq 
   277a8:       6a 1b                   pushq  $0x1b
   277aa:       68 b1 77 02 00          pushq  $0x277b1
   277af:       48 cf                   iretq  
}
   277b1:       5d                      pop    %rbp
   277b2:       c3                      retq   
   277b3:       66 66 66 66 2e 0f 1f    data32 data32 data32 nopw %cs:0x0(%rax,%rax,1)
   277ba:       84 00 00 00 00 00 

00000000000277c0 <kernel_main>:

So I run it with bochs and I put a break point at the address 0x277b1 where the page fault happens, and here are some of the debug data prior to the page fault.

This is my gdt including the TSS descriptor

Code: Select all

<bochs:3> info gdt
Global Descriptor Table (base=0x0000000000034020, limit=55):
GDT[0x00]=??? descriptor hi=0x00000000, lo=0x00000000
GDT[0x01]=Code segment, base=0x00000000, limit=0x00000000, Execute-Only, Non-Conforming, 64-bit
GDT[0x02]=Data segment, base=0x00000000, limit=0x00000000, Read/Write, Accessed
GDT[0x03]=Code segment, base=0x00000000, limit=0x00000000, Execute-Only, Non-Conforming, Accessed, 64-bit
GDT[0x04]=Data segment, base=0x00000000, limit=0x00000000, Read/Write, Accessed
GDT[0x05]=32-Bit TSS (Busy) at 0x000360e0, length 0x00067
GDT[0x06]=??? descriptor hi=0x00000000, lo=0x00000000
You can list individual entries with 'info gdt [NUM]' or groups with 'info gdt [NUM] [NUM]'
00558546357i[XGUI  ] Mouse capture off
This is my TSS:

Code: Select all

<bochs:4> info tss
tr:s=0x2b, base=0x00000000000360e0, valid=1
ss:esp(0): 0x0000:0x00709fa0
ss:esp(1): 0x0000:0x00000000
ss:esp(2): 0x0000:0x00000000
cr3: 0x00000000
eip: 0x00000000
eflags: 0x00000000
cs: 0x0000 ds: 0x0000 ss: 0x0000
es: 0x0000 fs: 0x0000 gs: 0x0000
eax: 0x00000000  ebx: 0x00000000  ecx: 0x00000000  edx: 0x00000000
esi: 0x00000000  edi: 0x00000000  ebp: 0x00000000  esp: 0x00000000
ldt: 0x0000
i/o map: 0x0000

And this is the stack dump

Code: Select all

<bochs:5> print-stack
Stack address size 8
 | STACK 0x0000000000709ff0 [0x00000000:0x00000006]
 | STACK 0x0000000000709ff8 [0x00000000:0x0001001f]
 | STACK 0x000000000070a000 [0x00000000:0x00c8aa1c]
 | STACK 0x000000000070a008 [0x00000000:0x00c8aa23]
 | STACK 0x000000000070a010 [0x00000000:0x00000008]
 | STACK 0x000000000070a018 [0x00000000:0x00000000]
 | STACK 0x000000000070a020 [0x00000000:0x00c8aa44]
 | STACK 0x000000000070a028 [0x00000000:0x00c8aa4b]
 | STACK 0x000000000070a030 [0x00000000:0x00000008]
 | STACK 0x000000000070a038 [0x00000000:0x00000000]
 | STACK 0x000000000070a040 [0x00000000:0x00c8c25c]
 | STACK 0x000000000070a048 [0x00000000:0x00c8c265]
 | STACK 0x000000000070a050 [0x00000000:0x0000000a]
 | STACK 0x000000000070a058 [0x00000000:0x00000000]
 | STACK 0x000000000070a060 [0x00000000:0x00c0a030]
 | STACK 0x000000000070a068 [0x00000000:0x00c0a03f]
I think that I am trying to pop from a wrong location in memory, although when I printed the stack the addresses are correct as shown above

I also noticed that the stack selector SS in my TSS is 0x0; should it be 0x10? But I am under the impression that this should have an impact only when an interrupt occurs and the hardware tries to pull out the stack pointer from the TSS to be able to switch to kernel mode? So it should create a problem but not here as I have made sure to disable interrupts to eliminate as much as possible hazard factors.

Also here are my TSS entry data structure:

Code: Select all

struct tss_entry_struct
{
        uint32_t reserve1;
        uint64_t rsp0;
        uint64_t rsp1;
        uint64_t rsp2;
        uint64_t reserve2;
        uint64_t ist1;
        uint64_t ist2;
        uint64_t ist3;
        uint64_t ist4;
        uint64_t ist5;
        uint64_t ist6;
        uint64_t ist7;
        uint64_t reserve3;
        uint16_t reserve4;
        uint16_t iomap_base;
        uint8_t iomap[TSS_IOMAP_SIZE];
} __attribute__((packed));
typedef struct tss_entry_struct tss_entry_t;
And here is the TSS descriptor data structure as well:

Code: Select all

typedef struct {
        unsigned limit_0_15: 16;
        unsigned base_0_15: 16;
        unsigned base_16_23: 8;
        unsigned type: 4;
        unsigned : 1;
        unsigned dpl : 2;
        unsigned present : 1;
        unsigned limit_16_19: 4;
        unsigned available: 1;
        unsigned : 2;
        unsigned granularity : 1;
        unsigned base_24_31: 8;
        unsigned base_32_63 : 32;
        unsigned  : 32;
} __attribute__ ((packed)) tss_descriptor_t;
Kindly if you notice anything in the above that might cause this problem, please let me know.


Thanks.
Karim.
User avatar
JAAman
Member
Member
Posts: 879
Joined: Wed Oct 27, 2004 11:00 pm
Location: WA

Re: Question about User Mode

Post by JAAman »

kemosparc wrote:Hi,

I have spent the past couple of days reading the manuals chapters you recommended.

I have also tried to implement the user mode switch but I keep on getting a page fault after the "iretq" instruction.
#PF means you are using paging, and the address is not appropriately mapped...

this is the address following the iretq, but it is also the target address (which the iretq will land on):

1) when the CPU attempts to load the instruction at CS:IP it finds that the page for that instruction is supervisor (and it is currently running in ring3)
2) when the CPU attempts to access the stack (because the instruction at that address is a POP), it finds that the stack is in a supervisor page (and current code is running in ring3, and cannot access it)

usually you will use separate user and kernel pages, and then on the transition you will switch to an unrelated area, not continue the same code in the same place as you are already running, and not continue the stack in the same place either...

so first you need to create a user stack, and map it into the page tables, then create page table mapping for the user code you wish to run (note this should NOT be the same code you are already running, this should be a different function) -- it is perfectly acceptable (at this point, for testing this, eventually you will want to do it differently) to map the same physical addresses to both locations for the code (basically all code is mapped to both the supervisor and user areas), alternatively you could copy the code from its current place in supervisor pages into user-mapped pages

although you need to be careful here, since the code is now running at a different address, any absolute addresses will break, and any data references to any fixed-place data (such as globals and static variables) will break, so it would be easiest to write this in ASM, and load it as data, rather than linking it with the kernel code you are starting with (though at this point, if you are careful about what your usercode does, it probably won't matter much)

once you have done this, then you can transition to the user code -- remember, the kernel code and user code are actually supposed to be separate programs, not the same continuous program
kemosparc
Member
Member
Posts: 207
Joined: Tue Oct 29, 2013 1:13 pm

Re: Question about User Mode

Post by kemosparc »

First of all thank you very much for your reply.

It was so stupid of me not to think about it that way, of course the page map was the problem.

I have adjusted things and now it passes through and continue execution without the #PF.

My problem now is that I transfer execution to a function that has an infinite loop and I want to make sure that it is really executing.

So I inserted in the body of the loop a system call that basically prints a string on the screen.

Here is the loop

Code: Select all

void new_thread1 ()
{
    uint64_t nnn = 0;
    char * buf = (char *) kmalloc_ptr->kmalloc(100);
    Utils::memset ( buf,0,100);
    Utils::strcpy(buf,"Hello from user mode");
    for ( ; ;)
    {
	syscall_monitor_write(buf);
	nnn ++;
    }
}

The problem is that its gives #GPF at address 0x15f1a which is basically the call to interrupt 0x80. here is the objdump

Code: Select all

DEFN_SYSCALL1(monitor_write, 0,char*);
   15f10:       55                      push   %rbp
   15f11:       31 c0                   xor    %eax,%eax
   15f13:       48 89 e5                mov    %rsp,%rbp
   15f16:       53                      push   %rbx
   15f17:       48 89 fb                mov    %rdi,%rbx
   15f1a:       cd 80                   int    $0x80
   15f1c:       5b                      pop    %rbx
   15f1d:       5d                      pop    %rbp
   15f1e:       c3                      retq   
   15f1f:       90                      nop
My question is that how can I initiate a system call if I cannot execute "int 0x80" user mode ?

Thanks
Karim.
Techel
Member
Member
Posts: 215
Joined: Fri Jan 30, 2015 4:57 pm
Location: Germany
Contact:

Re: Question about User Mode

Post by Techel »

Make sure your DPL of IDT-entry 0x80 is 3.
User avatar
JAAman
Member
Member
Posts: 879
Joined: Wed Oct 27, 2004 11:00 pm
Location: WA

Re: Question about User Mode

Post by JAAman »

kemosparc wrote: My question is that how can I initiate a system call if I cannot execute "int 0x80" user mode ?
you can... but you need to specify that in your IDT -- each entry in the IDT has a field that controls the least-privilege that is allowed to call it (see 3A:5.12.1.1), and you will get a #GP if it is called from less-privileged code than permitted, so your IDT must specify that it allows calls from ring3

so, you need to specify the permissions as well as the destination in the IDT
kemosparc
Member
Member
Posts: 207
Joined: Tue Oct 29, 2013 1:13 pm

Re: Question about User Mode

Post by kemosparc »

Hi,

Thanks a lot for the reply. This fixed the problem and now at the simplest case the above code works and the system call is invoked.

Now back to my very first question in this post.

I have moved what I have done so far and inserted it into my scheduler, and I have a problem after I get the first PIT when I am in user mode.


Here is my code:

Code: Select all


void new_thread1 ()
{
    uint64_t nnn = 0;
    char * buf = (char *) kmalloc_ptr->kmalloc(100);
    Utils::memset ( buf,0,100);
    Utils::strcpy(buf,"Hello from usermode: ");
    for ( uint64_t i = 0 ; ; i++)
    {
           syscall_monitor_write(buf,nnn);
           nnn ++;
    }
}

void switch_to_user_mode ()
{
    asm volatile("  \
         cli; \
         mov $0x23, %ax; \
         mov %ax, %ds; \
         mov %ax, %es; \
         mov %ax, %fs; \
         mov %ax, %gs; \
                       \
         mov %rsp, %rax; \
         pushq $0x23; \
         pushq %rax; \
         pushfq; \
         popq  %rax; \
         or $0x200,%rax; \
         pushq %rax;\
         pushq $0x1B; \
         pushq $1f; \
         iretq; \
       1: \
         ");
}

int create_process (routine _startRoutine)
{
    asm volatile("cli");
    uint64_t _rip = (uint64_t)&&CreateProcessPtr;

CreateProcessPtr:

    uint8_t core_id = Utils::getAPICId();
    if ( coreManager->getCurrentKernelPageManager(core_id)->getParentKernelPageManager() == NULL)
    {
        Utils::spin_lock(&sl);
        KernelPageManager * processPageManager = kernelPageManager.createProcessPageManager();
        processPageManager->setStartRoutine(_startRoutine);
        processPageManager->cloneStack();
        coreManager->getTaskManager(core_id)->addTask(processPageManager,_rip,core_id,true);
        Utils::spin_unlock(&sl);
        asm volatile("sti");
        return 0 ;
    }
    else
    {
        routine _threadRoutine = coreManager->getCurrentKernelPageManager(core_id)->getStartRoutine();
        asm volatile ("sti;");
        switch_to_user_mode();
        _threadRoutine();
        return 100;

    }
}

In my main I just create a new process like this

Code: Select all

    create_process(new_thread1);
I have the PIT enabled so it fires up and calls my task scheduler.

Code: Select all

    uint8_t core_id = Utils::getAPICId();
    if ( task_count  < 2 )
    {
        if ( coreManager->getAPPITScheduler(core_id) != NULL) coreManager->getAPPITScheduler(core_id)->APICEOI();
        return;
    }
    uint64_t rsp,rbp,rip;
    asm volatile("mov %%rsp, %0" : "=r"(rsp));
    asm volatile("mov %%rbp, %0" : "=r"(rbp));
    uint64_t rip = (uint64_t)&&return_address;
return_address:
    if (rip == 0x12345) return;
    tasks[current_task]->setRBP(rbp);
    tasks[current_task]->setRSP(rsp);
    tasks[current_task]->setRIP(rip);

    current_task ++;
    if ( current_task >= task_count ) current_task = 0;

    rbp = tasks[current_task]->getRBP();
    rsp = tasks[current_task]->getRSP();
    rip = tasks[current_task]->getRIP();
    currentkernelPageManager = tasks[current_task]->getPageManager();
    coreManager->setCurrentKernelPageManager(core_id,tasks[current_task]->getPageManager());
    if ( coreManager->getAPPITScheduler(core_id) != NULL) coreManager->getAPPITScheduler(core_id)->APICEOI();

    asm volatile("         \
          cli;                 \
          mov %0, %%rcx;       \
          mov %1, %%rsp;       \
          mov %2, %%rbp;       \
          mov %3, %%cr3;       \
          mov $0x12345, %%rax; \
          sti;                 \
          jmp *%%rcx           " : : "r"(rip), "r"(rsp), "r"(rbp), "r"(page_directory_address));

The first time the scheduler switch execution to the child process it executes well as it comes from the kernel mode and stay in kernel mode until the switch_to_user_mode function is called, in the else section (child section) of the create process, and the loop is invoked and starts printing through the system call.

When the PIT fires again it goes to the handler in kernel mode fetching the stack from the TSS and runs normally. Now it is in kernel mode.

I then get a #GPF at code line # 16725.

When I checked that in the objdump I found that it is in the very last return on the isr handler, here:

Code: Select all

idt_common_stub:
    pushaq
   166e3:       50                      push   %rax
   166e4:       53                      push   %rbx
   166e5:       51                      push   %rcx
   166e6:       52                      push   %rdx
   166e7:       55                      push   %rbp
   166e8:       56                      push   %rsi
   166e9:       57                      push   %rdi
   166ea:       41 50                   push   %r8
   166ec:       41 51                   push   %r9
   166ee:       41 52                   push   %r10
   166f0:       41 53                   push   %r11
   166f2:       41 54                   push   %r12
   166f4:       41 55                   push   %r13
   166f6:       41 56                   push   %r14
   166f8:       41 57                   push   %r15

    mov rdi,rsp
   166fa:       48 89 e7                mov    %rsp,%rdi

    mov rax,idt_handler
   166fd:       48 b8 b0 6d 02 00 00    movabs $0x26db0,%rax
   16704:       00 00 00 
    call rax
   16707:       ff d0                   callq  *%rax

    popaq
   16709:       41 5f                   pop    %r15
   1670b:       41 5e                   pop    %r14
   1670d:       41 5d                   pop    %r13
   1670f:       41 5c                   pop    %r12
   16711:       41 5b                   pop    %r11
   16713:       41 5a                   pop    %r10
   16715:       41 59                   pop    %r9
   16717:       41 58                   pop    %r8
   16719:       5f                      pop    %rdi
   1671a:       5e                      pop    %rsi
   1671b:       5d                      pop    %rbp
   1671c:       5a                      pop    %rdx
   1671d:       59                      pop    %rcx
   1671e:       5b                      pop    %rbx
   1671f:       58                      pop    %rax
    add rsp,16
   16720:       48 83 c4 10             add    $0x10,%rsp
    sti
   16724:       fb                      sti    
    iretq           ; pops 5 things at once: CS, RIP, EFLAGS, SS, and RSP
   16725:       48 cf                   iretq  

If I remove the "switch_to_user_mode" function call in the child section (which was my original code before introducing the user mode) it works well as expected as everything is in kernel mode.

Also when I remove the following three lines from the "switch_to_user_mode" it works good (but actually stays in user mode and PIT is disabled) as it does not enable interrupts and hence the scheduler is not called again, and the loop keeps on printing as expected, so it must be the switch from user mode to kernel mode within the interrupt handler/scheduler.

Code: Select all

         popq  %rax; \
         or $0x200,%rax; \
         pushq %rax;\
I also have another doubt about the stack the isr expects upon the return. Because inside my scheduler I switch the stack to the new kernel stack (from child stack which is set in the previous PIT fire) while the stack I use in the TSS is a special kernel stack I dedicated for this task (coming back from user mode); because when I use the same kernel stack for the TSS it overwrites some of the kernel stack and I get an Opcode exception.


I have been hitting my head into the wall for the past 3 days on this :) so if you can see anything that is the cause of the #GPF please let me know.

Thanks,
Karim.
kemosparc
Member
Member
Posts: 207
Joined: Tue Oct 29, 2013 1:13 pm

Re: Question about User Mode

Post by kemosparc »

Hi,

I have another question.

When a PIT interrupt first while in user mode, the stack is set to the tss->RSP0 so where the rip,rsp,rbp of the user mode just right when the PIT occurs are saved ?

Thanks
Techel
Member
Member
Posts: 215
Joined: Fri Jan 30, 2015 4:57 pm
Location: Germany
Contact:

Re: Question about User Mode

Post by Techel »

When an interrupt or exception ocurres ss0:sp0, which represent the kernel stack, are taken from the tss. If the interrupt happens while not in ring0, the old user's ss and sp are pushed to the kernel stack. Then flags, cs, ip and an additional error code (see http://wiki.osdev.org/Exceptions) are pushed.
kemosparc
Member
Member
Posts: 207
Joined: Tue Oct 29, 2013 1:13 pm

Re: Question about User Mode

Post by kemosparc »

Hi,

Thanks, I finally got it working, and I call the system call successfully from the within my int 0x80 handler invoked from user mode through the system call :)

My problem now is that whenever I try to change the system call to use 5 parameters as I need to pass more parameters to the system call I get either op code errors or the whole thing just hangs.

I read the ABI and followed the sequence of parameter passing rdi, rsi, rdx,rcx, and r8, and I also push the values on the stack.

I also checked the values in the ic structure and they are all correct as passed from the user mode.

Here is my inline asm:

Code: Select all

 asm volatile (" \
      push %1; \
      push %2; \
      push %3; \
      push %4; \
      push %5; \
      mov %1,%%rdi;\
      mov %2,%%rsi;\
      mov %3,%%rdx;\    Problem occurs when I introduce this line
      mov %4,%%rcx;\
      mov %5,%%r8;\
      call *%6; \
      pop %%rbx; \
      pop %%rbx; \
      pop %%rbx; \
      pop %%rbx; \
      pop %%rbx; \
    " : "=a" (ret) : "r" (ic->rdi), "r" (ic->rsi), "r" (ic->rdx), "r" (ic->rcx), "r" (ic->rbx), "r" (location));
The problem occurs when try to write something into rdx.

Here also my system call declarations and the interrupt context structure (variable ic above):

Code: Select all

// I fill this in the idt_common_stub
struct interrupted_context{
    uint64_t r15,r14,r13,r12,r11,r10,r9,r8,rdi, rsi, rbp , rdx, rcx, rbx, rax;
    uint64_t intNo, errCode;
    uint64_t return_address, cs, eflags, orsp, ss;
} ;


#define DECL_SYSCALL5(fn,p1,p2,p3,p4,p5) int syscall_##fn(p1,p2,p3,p4,p5);


#define DEFN_SYSCALL5(fn, num,P1,P2,P3,P4,P5) \
int syscall_##fn(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) \
{ \
  int a; \
  asm volatile("int $0x80" : "=a" (a) : "0" (num), "D" (p1), "S" (p2), "d" (p3), "c" (p4), "b" (p5)); \
  return a; \
}


DEFN_SYSCALL5(monitor_write, 0,char*,uint64_t,uint64_t,uint64_t,uint64_t);

static void write_to_monitor (char * str,uint64_t xloc,uint64_t yloc,uint64_t t_index,uint64_t index);


Thanks,
Karim.

kemosparc
Member
Member
Posts: 207
Joined: Tue Oct 29, 2013 1:13 pm

Re: Question about User Mode

Post by kemosparc »

Hi,

I am stuck in something trivial but I cannot find the documentation for it.

in the inline asm I can use contraints for rax, rbx, rcx, and rdx registers like "a", "b", "c", and "d"

How can I do the same for the r8-15 ?

Thanks
Karim.
Techel
Member
Member
Posts: 215
Joined: Fri Jan 30, 2015 4:57 pm
Location: Germany
Contact:

Re: Question about User Mode

Post by Techel »

Maybe there is a problem with the stack (not large enough, or overwriten something). Also, to make your syscall stuff easier, you should push all your 5 registers that may be used to the stack and call the appropriate function (should be declared with cdecl convention). Then add the amount of bytes the registers need on the stack (5*8=40) to rsp instead of popping all into an unused register. You don't need the ugly DECL_SYSCALL and defines anymore.
kemosparc
Member
Member
Posts: 207
Joined: Tue Oct 29, 2013 1:13 pm

Re: Question about User Mode

Post by kemosparc »

Hi, Thanks for the reply.

Okay, I am past this.

But I have another problem that is transient.

The problem is that when I try to switch from the scheduler to the user mode function I try to restore all the registers that I saved previously before switching from the process. I am trying to do that because some of the registers does not have the same values as before the switch. So I save all the registers prior switching away from the process. When I return to it back trying to resume executing it I do that

Code: Select all


            interrupted_context * _ic  = &(tasks[current_task]->ic);
        asm volatile("  \
             cli; \
             mov %0,%%rax; \
             pushq $0x23; \
             pushq %%rax; \
             pushfq; \
             popq  %%rax; \
             or $0x200,%%rax; \
             pushq %%rax;\
             pushq $0x1B; \
             pushq %1; \
             mov %2, %%cr3;\
             pushq %3; \
             pushq %4; \
             pushq %5; \
             pushq %6; \
             pushq %7; \
             pushq %8; \
             pushq %9; \
             pushq %10; \
             pushq %11; \
             pushq %12; \
             pushq %13; \
             popq %%rdx; \
             popq %%rcx; \
             popq %%rbx; \
             popq %%r8; \
             popq %%r9; \
             popq %%r10; \
             popq %%r11; \
             popq %%r12; \
             popq %%r13; \
             popq %%r14; \
             popq %%r15; \
             iretq; \
             "
             : : "r"(rsp),"r"(rip),"r"(page_directory_address),"r" (_ic->r15),"r" (_ic->r14),"r" (_ic->r13),"r" (_ic->r12),"r" (_ic->r11),"r" (_ic->r10),"r" (_ic->r9),"r" (_ic->r8),"r"(_ic->rbx),"r"(_ic->rcx),"r"(_ic->rdx));

My problem now is that the rsi and the rdi sometimes arrive at the process target function with invalid values after the switch affecting the parameters that I use inside the function. If I try to add all the registers in the inline asm (rsi and rdi) the compiler gives me an error saying "asm operand has impossible constraints".

The invalid values appear after the switch as after the first iteration of the loop the subsequent iteration has the correct values until the next switch.

Thanks
Karim.
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: Question about User Mode

Post by Combuster »

Yet another reason not to do this in C.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
Post Reply