Page 1 of 1

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

Posted: Mon Feb 23, 2015 5:58 pm
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

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

Posted: Tue Feb 24, 2015 6:15 am
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