how to set EFLAG for iretq when flushing gdt in long 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.
Post Reply
markq
Posts: 24
Joined: Fri Jan 30, 2015 3:42 pm

how to set EFLAG for iretq when flushing gdt in long mode?

Post by markq »

Hello,

I have read various post regarding how to load a new gdt in long mode when in kernel but I am confused about how the EFLAG should be set. According to this post (the one by StephanvanSchaik) we should restore the old flag, unset a few bits in the old flag and save that as the current flag then push the old flag back with execution of iretq (that's how I understand the code below). I am not quite understanding the reasoning behind that (why not just use old flag values?). On top of that I have found a few other code sample where it just sets EFLAG to value 2 or some other random value and it seems to work at least on my kernel. Could someone enlighten me on how the EFLAG should be set when I am using iretq? Below is the a repost of the code StephanvanSchaik wrote:

Code: Select all

mov rsp, 0x90000
    lgdt [GDT.Pointer]
    push QWORD GDT.KData
    push QWORD 0x90000
    pushfq
    pushfq
    pop rax
    and rax, 1111111111111111111111111111111111111111111111101011111011111111b
    push rax
    popfq
    push QWORD GDT.KCode
    push QWORD .Flush
    iretq
   
.Flush:
    mov ax, GDT.KData
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: how to set EFLAG for iretq when flushing gdt in long mod

Post by Brendan »

Hi,

First, here's the code with comments:

Code: Select all

    mov rsp, 0x90000                 ;Set RSP
    lgdt [GDT.Pointer]               ;Load GDT
    push QWORD GDT.KData             ;Set "Return SS" for the IRETQ
    push QWORD 0x90000               ;Set "Return ESP" for the IRETQ
    pushfq                           ;Set "Return EFLAGS" for the IRETQ
    pushfq                           ;Save another copy of EFLAGS
    pop rax                          ;Load copy of EFLAGS into RAX
    and rax, 0xFFFFFFFFFFFEBEFF      ;Clear the Trap Flag, the reserved flag and the Virtual8086 flag
    push rax                         ;Store the modified EFLAGS on the stack
    popfq                            ;Load the modified EFLAGS
    push QWORD GDT.KCode             ;Set "Return CS" for the IRETQ
    push QWORD .Flush                ;Set "Return RIP" for the IRETQ
    iretq                            ;Load CS, RIP, EFLAGS, ESP and SS
.Flush:
    mov ax, GDT.KData
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
You'll see that the "modified EFLAGS" is only used for 3 instructions, then it's overwritten by the original/unmodified EFLAGS loaded by the IRETQ. Because it's 64-bit code you know the virtual8086 flag must already be clear, so clearing it does nothing. Clearing the "reserved, must be zero" flag also does nothing. This means that it only really disables single step debugging (the Trap Flag) for 3 instructions. It's highly likely that single step debugging wasn't being used either, and therefore highly likely that the entire EFLAGs manipulation is completely pointless.

This means it's basically the same as:

Code: Select all

    mov rsp, 0x90000                 ;Set RSP
    lgdt [GDT.Pointer]               ;Load GDT
    push QWORD GDT.KData             ;Set "Return SS" for the IRETQ
    push QWORD 0x90000               ;Set "Return ESP" for the IRETQ
    pushfq                           ;Set "Return EFLAGS" for the IRETQ
    push QWORD GDT.KCode             ;Set "Return CS" for the IRETQ
    push QWORD .Flush                ;Set "Return RIP" for the IRETQ
    iretq                            ;Load CS, RIP, EFLAGS, ESP and SS
.Flush:
    mov ax, GDT.KData
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
However; I'd expect that the only thing this code is intended to do is load CS (and SS). To load CS, it's better to use "RET FAR" or "JMP FAR absolute indirect", which are both faster and simpler. SS can be loaded with a "mov ss,ax" (after the existing "mov ax, GDT.KData").


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Post Reply