1

I am developing a x86 architecture based 64 bit Operating System. I have implemented GDT(Null, Kernel-code, kernel-data, user-code, user-data, tss) . Now I want to switch into user space(Lower half memory space Ring-3) So I wrote the below function

__attribute__((naked, noreturn))
void switch_to_user_mode(uint64_t stack_addr, uint64_t code_addr)
{
    asm volatile(
        "cli\n"                 // Disable Interrupt
        "mov $0x23, %%ax  \n"   // User-mode data segment (ring 3)
        "mov %%ax, %%ds   \n"
        "mov %%ax, %%es   \n"
        "mov %%ax, %%fs   \n"
        "mov %%ax, %%gs   \n"
        "push $0x23       \n"   // Push USER_SS(DATA SEGMENT) Selector
        "push %[stack]    \n"   // Push User Stack pointer
        "pushf            \n"   // Push RFLAGS
        "pop %%rax        \n"   // Taking RFLAGS into RAX
        "or $0x200, %%rax \n"   // Set IF flag to make automatic enabling Interrupt
        "push %%rax       \n"   // Push Updated RFLAGS into the stack
        "push $0x1B       \n"   // Push USER_CS(CODE SEGMENT)
        "push %[entry]    \n"   // Push entry point address        
        "iret             \n"   // Interrupt Return to User Mode
        :
        : [stack] "r" (stack_addr), [entry] "r" (code_addr)
        : "rax"
    );
}

By using above function I switch userspace by following way

uint64_t create_user_function() {
    void *user_code = (void *) uheap_alloc(0x1000);

    // simple infinite loop machine code
    uint8_t user_program[] = {
        0xeb, 0xfe  // Infinite loop: jmp $
    };

    printf("user_code or user_programe is null\n");
    memcpy(user_code, (void*)&user_program, sizeof(user_program));

    return (uint64_t)user_code; // Return the user-accessible function pointer
}


void init_user_mode(){
    uint64_t code_addr = (uint64_t) create_user_function();
    page_t *code_page = get_page(code_addr, 0, (pml4_t *)get_cr3_addr());
    code_page->rw = 0;      // Making readable only
    code_page->nx = 0;      // Making executable
    code_page->user = 1;    // Making User accessible

    uint64_t stack_base_addr = ((uint64_t) uheap_alloc(STACK_SIZE));
    for(uint64_t addr = stack_base_addr; addr < stack_base_addr + STACK_SIZE; addr += 0x1000){
        page_t *_page = get_page(addr, 0,  (pml4_t *)get_cr3_addr());
        _page->rw = 1;      // Making it read-writable
        _page->nx = 1;      // Making non-executable
        _page->user = 1;    // Making User accessible
    }
    uint64_t stack_top_addr = stack_base_addr + STACK_SIZE;   // Set stack top
    
    flush_tlb_all();
    
    printf("Starting Switching to the user mode: code addr.- %x, stack addr.- %x\n", code_addr, stack_top_addr);
    switch_to_user_mode(stack_top_addr, code_addr);
}

Now I am getting following Output:

[Info] Interrupt Based System Call initialized!
GDT Entry 0x0: Base=0x0 Limit=0x0 Access=0x0 Flags=0x0
GDT Entry 0x8: Base=0x0 Limit=0xFFFF Access=0x9A Flags=0xA
GDT Entry 0x10: Base=0x0 Limit=0xFFFF Access=0x93 Flags=0xA
GDT Entry 0x1B: Base=0x0 Limit=0xFFFF Access=0xFA Flags=0xA
GDT Entry 0x23: Base=0x0 Limit=0xFFFF Access=0xF2 Flags=0xA
GDT Entry 0x28: Base=0x8059A060 Limit=0x67 Access=0x8B Flags=0x0
Current stack address: 0xFFFF80007FF41FC0
Current rip address: 0xFFFFFFFF8000EF0A
Current rflags address: 0x286
Starting Switching to the user mode: code addr.- 0x1000, stack addr.- 0x7000
General protection fault (pushes an error code)
recieved interrupt: 13
Error Code: 0x0
CS: 0x8, RIP : 0xFFFFFFFF8000EA61
Stack (rsp = 0xFFFF80007FF41F70) Contents(First 26) :
  [0xFFFF80007FF41F70] = 0x1000
  [0xFFFF80007FF41F78] = 0x1B
  [0xFFFF80007FF41F80] = 0x292
  [0xFFFF80007FF41F88] = 0x7000
  [0xFFFF80007FF41F90] = 0x23
  [0xFFFF80007FF41F98] = 0xFFFFFFFF8000EBCD
  [0xFFFF80007FF41FA0] = 0x7FF28030
  [0xFFFF80007FF41FA8] = 0x7000
  [0xFFFF80007FF41FB0] = 0x3000
  [0xFFFF80007FF41FB8] = 0x7FF28008
  [0xFFFF80007FF41FC0] = 0x1000
  [0xFFFF80007FF41FC8] = 0x7000
  [0xFFFF80007FF41FD0] = 0xFFFF80007FF41FF0
  [0xFFFF80007FF41FD8] = 0xFFFFFFFF800080A1
  [0xFFFF80007FF41FE0] = 0x0
  [0xFFFF80007FF41FE8] = 0xFEBD5000
  [0xFFFF80007FF41FF0] = 0x0
  [0xFFFF80007FF41FF8] = 0x0
  [0xFFFF80007FF42000] = 0x800000015CD00037
  [0xFFFF80007FF42008] = 0xFFFF
  [0xFFFF80007FF42010] = 0x0
  [0xFFFF80007FF42018] = 0x0
  [0xFFFF80007FF42020] = 0x0
  [0xFFFF80007FF42028] = 0x0
  [0xFFFF80007FF42030] = 0x0
  [0xFFFF80007FF42038] = 0x0
System Halted!

The above stack contents showing

+---------------+
| RIP | 0x1000  |
+------+--------+
| CS | 0x1B     |
+------+--------+
| RFLAGS | 0x292|
+------+--------+
| RSP | 0x7000  |
+------+--------+
| SS | 0x23     |
+------+--------+
| .... | ....   |

Which states that switch_to_user_mode successfully placed all values but iret never switched from kernel space to userspace as the interrupted rip and rsp are belong kernel space.

I have tried to debug by GDB but it is not understandable for me. How can I resolve this issue?

Edit: gdt tss switch_to_user.c

8
  • 1
    Maybe you meant to use iretq instead of iret Commented Apr 15 at 15:57
  • yes pushq instead push as well as iretq instead of iret Commented Apr 15 at 16:25
  • Now made those changes interrupt is not working even enabling interrupt bit in rflags. Is this cause of TSS implementation? Commented Apr 15 at 16:27
  • When you say interrupts don't work what happens? You receive no interrupts or you get an exception? Commented Apr 15 at 16:41
  • Yes! after Starting Switching to the user mode: code addr.- 0x1000, stack addr.- 0x7000 system stalks .Which may be feasible due to loop in user code. Interrupt is not working I mean not receiving interrupt ,although interrupt bit set. I found a bug in set tss 64 bit base address. Commented Apr 15 at 17:00

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.