OSDev.org

The Place to Start for Operating System Developers
It is currently Tue Jun 19, 2018 11:54 pm

All times are UTC - 6 hours




Post new topic Reply to topic  [ 11 posts ] 
Author Message
 Post subject: General Protection Fault when switching to u-mode with iret
PostPosted: Mon Jun 04, 2018 8:27 am 
Offline

Joined: Mon Jun 04, 2018 8:10 am
Posts: 6
Hello,
i am following tutorial on building an OS but I have a problem getting to user mode.

My GDT is set up like this:
Code:

void init_gdt() {

   gdt_ptr.base = (u32)&gdt_entries;
   gdt_ptr.limit = sizeof(gdt_entry_t) * GDT_SIZE;

   gdt_entries[0] = create_gdt_entry(0, 0, 0, 0); // 0x00

   gdt_entries[1] = create_gdt_entry(0, 0xffffffff, 0x98, 0xd0); // code segment 0x08
   gdt_entries[2] = create_gdt_entry(0, 0xffffffff, 0x92, 0xd0); // data segment 0x10
   gdt_entries[3] = create_gdt_entry(0x00, 0x00, 0x96, 0xd0);   // kernel stack 0x18 (not used)

   gdt_entries[4] = create_gdt_entry(0x30000, 0x1000, 0xf8, 0xd0); // user code segment 0x20
   gdt_entries[5] = create_gdt_entry(0x30000, 0x1000, 0xf2, 0xd0); // user data segment 0x28
   gdt_entries[6] = create_gdt_entry(0x00, 0x30000, 0xf6, 0xd0); // user stack 0x30 (not used)

   load_gdt((u32)&gdt_ptr);

   print_ok();
   print("New GDT loaded\n");

}

gdt_entry_t create_gdt_entry(const u32 base, const u32 limit, const u8 access, const u8 gran) {

   return (gdt_entry_t) {
      .base_low       = (base & 0xffff),
      .base_middle    = (base >> 16) & 0xff,
      .base_high       = (base >> 24) & 0xff,

      .limit_low       = limit & 0xffff,
      .granularity    = (gran & 0xf0) | ((limit >> 16) & 0x0f),

      .access       = access
   };

}



The TSS is loaded like that:
Code:
void init_tss() {

    // init the TSS to zeros
    memset(&default_tss, 0, sizeof(tss_t));

    __asm__("  movw %%ss, %0 \n \
       movl %%esp, %1" : "=m" (default_tss.ss0), "=m" (default_tss.esp0) : );

    // entry 7 = 0x38
    gdt_entries[7] = create_gdt_entry((u32)&default_tss, sizeof(tss_t), 0xe9, 0x00);

    // load TSS register
    tss_load();

}


Code:
tss_load:
    mov ax, 0x3b ; tss segment (or it with 0x03 for ring 3 lvl)
    ltr ax
    ret


The kernel then copies executable code (printing a character in the top left corner and halting) to location 0x30000 like that:

Code:

   uchar *ptr = 0x30000;
   ptr[0] = 0xc6;
   ptr[1] = 0x05;
   ptr[2] = 0x00;
   ptr[3] = 0x80;

   ptr[4] = 0x0b;
   ptr[5] = 0x00;
   ptr[6] = 0x72;
   ptr[7] = 0xc6;

   ptr[8] = 0x05;
   ptr[9] = 0x01;
   ptr[10] = 0x80;
   ptr[11] = 0x0b;

   ptr[12] = 0x00;
   ptr[13] = 0x72;
   ptr[14] = 0xfa;
   ptr[15] = 0xf4;



and here is the routine that switches to user mode:
Code:

jmp_umode:
    cli

    push 0x2b       ; push user ss
    push 0x1000       ; push user esp

    pushfl
    pop eax            ;
   or eax, 0x200      ;
    and eax, 0xffffbfff   ;
   push eax         ; re-enable interupt after switch

    push 0x23       ; push user cs (0x23)
    push 0x00       ; push code offset

    mov ax, 0x2b   ; user data segement
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    iretd




But after executing iretd I get a General Protection Fault with error code 0x000010d8.
Any clue on why I am getting a General Protection Fault and why iret is not actually switching to 0x20:0x00?

Regards


Top
 Profile  
 
 Post subject: Re: General Protection Fault when switching to u-mode with i
PostPosted: Mon Jun 04, 2018 4:22 pm 
Offline
Member
Member

Joined: Thu Apr 26, 2018 11:21 pm
Posts: 44
Your user code is actually trying to halt the system? That would at least be part of your issues, HLT is a privileged instruction. In my OS there's an idle thread running in ring 0, which is basically a HLT/JMP loop. To test user code I do a syscall to print registers in a loop


Top
 Profile  
 
 Post subject: Re: General Protection Fault when switching to u-mode with i
PostPosted: Tue Jun 05, 2018 1:04 am 
Offline

Joined: Mon Jun 04, 2018 8:10 am
Posts: 6
Hello and thank you for your reply.
I have changed the executed instructions to
Code:
jmp $
and I still get a General Protection Fault.
When I set up segment 0x20 to be ring 0 code and I execute iret, everything works fine (the OS loops forever)
But when I set up segment 0x20 to be ring 3 code I still get the General Protection Fault with error 0x0000105c this time. I am not sure what this error code corresponds to...


Top
 Profile  
 
 Post subject: Re: General Protection Fault when switching to u-mode with i
PostPosted: Tue Jun 05, 2018 1:40 am 
Offline
Member
Member
User avatar

Joined: Sat Mar 31, 2012 3:07 am
Posts: 3168
Location: Chichester, UK
Without disassembling your code, are you trying to move a value into ds:0xB8000?

As I see it there are two problems with this:

1. Your data segment starts at 0x30000, so you want to write to ds:0x88000.

2. Neither of those addresses is within your user data segments, so you can't write to them in user mode.

It would make your user code easier to understand if you listed it in assembler rather than a series of bytes.


Top
 Profile  
 
 Post subject: Re: General Protection Fault when switching to u-mode with i
PostPosted: Tue Jun 05, 2018 2:33 am 
Offline

Joined: Mon Jun 04, 2018 8:10 am
Posts: 6
Thank you iansjack for your reply but even by changing the executable code to
Code:
jmp $
I still get the general protection fault.


Top
 Profile  
 
 Post subject: Re: General Protection Fault when switching to u-mode with i
PostPosted: Tue Jun 05, 2018 4:33 am 
Offline
Member
Member
User avatar

Joined: Sat Mar 31, 2012 3:07 am
Posts: 3168
Location: Chichester, UK
I'm a bit puzzled by your error codes, as they don't appear to be valid codes for a GPF.

How do you know it's a GPF, and how are you determining the error code?


Top
 Profile  
 
 Post subject: Re: General Protection Fault when switching to u-mode with i
PostPosted: Tue Jun 05, 2018 6:07 am 
Offline

Joined: Mon Jun 04, 2018 8:10 am
Posts: 6
This is the interrupt:

Code:
%macro ISR_ERRCODE 1
     [GLOBAL isr%1]
     isr%1:
       cli
       push byte %1
       jmp isr_common_stub
%endmacro


Here is the code that handles interrupts:

Code:
[EXTERN isr_handler]

;# Common ISR code
isr_common_stub:

    ;# 1. Save CPU state
   pusha

   mov ax, ds       ;# Lower 16-bits of eax = ds.
   push eax       ;# save the data segment descriptor

   mov ax, 0x10     ;# kernel data segment descriptor
   mov ds, ax
   mov es, ax
   mov fs, ax
   mov gs, ax

    ;# 2. Call C handler
   call isr_handler

    ;# 3. Restore state
   pop eax
   mov ds, ax
   mov es, ax
   mov fs, ax
   mov gs, ax
   popa

   add esp, 8    ;# Cleans up the pushed error code and pushed ISR number

   sti         ;# enable interruption again

   iret       ;# pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP


Code:
typedef struct {
   u32 ds;                               /* Data segment selector */
   u32 edi, esi, ebp, esp, ebx, edx, ecx, eax;    /* Pushed by pusha. */
   u32 int_no, err_code;                   /* Interrupt number and error code (if applicable) */
   u32 eip, cs, eflags, useresp, ss;          /* Pushed by the processor automatically */
} registers_t;


Code:
void isr_handler(registers_t r) {

   /* this definition should be elsewhere */
   char *exception_messages[] = {
      "Division By Zero",
      "Debug",
      "Non Maskable Interrupt",
      "Breakpoint",
      "Into Detected Overflow",
      "Out of Bounds",
      "Invalid Opcode",
      "No Coprocessor",

      "Double Fault",
      "Coprocessor Segment Overrun",
      "Bad TSS",
      "Segment Not Present",
      "Stack Fault",
      "General Protection Fault",
      "Page Fault",
      "Unknown Interrupt",

      "Coprocessor Fault",
      "Alignment Check",
      "Machine Check",
      "Reserved",
      "Reserved",
      "Reserved",
      "Reserved",
      "Reserved",

      "Reserved",
      "Reserved",
      "Reserved",
      "Reserved",
      "Reserved",
      "Reserved",
      "Reserved",
      "Reserved"
   };
   /* ========================= */

   set_char_attr(YELLOW_FORGND);
    print("received interrupt ");
    print_int(r.int_no);
    print(": ");
    print(exception_messages[r.int_no]);
    print("\n");

    // if interrupt handler is set, run it
    if(interrupt_handlers[r.int_no]) {
        interrupt_handlers[r.int_no](r);
   }

   // stay there
   while(1);
}


And here is the function that handles GPF:

Code:
static void general_protection_fault(registers_t r) {

    set_char_attr(BLUE_BACKGND | WHITE_FORGND);
    print("/!\\ GENERAL PROTECTION FAULT, SYSTEM HALTED /!\\ err code = ");
   print_hex(r.err_code);

    __asm__("cli\nhlt");
    while(1);

}


And this is what Qemu shows me:
Attachment:
Capture d’écran 2018-06-05 à 2.00.14 PM.png
Capture d’écran 2018-06-05 à 2.00.14 PM.png [ 40.08 KiB | Viewed 265 times ]


As I have read, the error code that GPF shows is the segment that caused the error, however i do not understand why the error is 0x000010d8. Is 0x000010d8 a segment index ?? I am quiet confused...

Thank you for your replies!


Top
 Profile  
 
 Post subject: Re: General Protection Fault when switching to u-mode with i
PostPosted: Tue Jun 05, 2018 7:12 am 
Offline
Member
Member
User avatar

Joined: Sat Mar 31, 2012 3:07 am
Posts: 3168
Location: Chichester, UK
I'm afraid I'm not very good at following mixed C and assembler code. What I would suggest is that you single-step the code in a debugger to see exactly where it is failing and what the status of memory and registers is at that point. Also check that the GDT in memory is actually what you think it is.


Top
 Profile  
 
 Post subject: Re: General Protection Fault when switching to u-mode with i
PostPosted: Tue Jun 05, 2018 11:13 am 
Offline

Joined: Wed Aug 30, 2017 8:24 am
Posts: 12
Hi,

I am not terribly familiar with passing structures by value in 32-bit code. I would probably end the pushing tirade with "push esp" and change the argument of isr_handler() to a pointer. At least I know how that works. In 64-bit, passing a large structure by value is defined to be no different than passing a pointer, and it is the caller's job to pass a copy (since arguments are local variables of the callee, so changes to them should not be reflected in the caller).

You know, you can push segment registers directly ("push ds"). Also, depending on where you're headed with your OS, it might be good to keep in mind that the segment registers could be different from each other. Or if they can't be today, they might tomorrow. Linux uses that to implement thread-pointers, for example.

Also, here's a few things you can try: Actually plot all the information in the exception frame. In particular, what is the saved CS:IP, and did you expect that value? Another thing you can try is setting the trap flag (bit 8 ) in the EFLAGS image in the iret frame you are preparing. That way you should get debug exceptions (remember to set the resume flag (bit 16) before returning from those!) after each instruction, and can thus trace out what is happening (keep plotting those CS:IP values...)

HTH,
Markus


Top
 Profile  
 
 Post subject: Re: General Protection Fault when switching to u-mode with i
PostPosted: Wed Jun 06, 2018 4:17 am 
Offline

Joined: Mon Jun 04, 2018 8:10 am
Posts: 6
Ok, thank you for all your replies


Top
 Profile  
 
 Post subject: [SOLVED]General Protection Fault when switching to u-mode wi
PostPosted: Thu Jun 07, 2018 6:55 am 
Offline

Joined: Mon Jun 04, 2018 8:10 am
Posts: 6
The problem was with the pushfl instruction that was treated as a label. I changed "pushfl" to "pushf" and everything seems to be working fine!

Code:
jmp_umode:
    cli

    push 0x2b       ; push user ss
    push 0x1000       ; push user esp

    pushf
    pop eax            ;
   or eax, 0x200      ;
    and eax, 0xffffbfff   ;
   push eax         ; re-enable interupt after switch

    push 0x23       ; push user cs (0x23)
    push 0x00       ; push code offset

    mov ax, 0x2b   ; user data segement
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    iretd


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 11 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: Google [Bot] and 10 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