assemblyx86-64nasmosdevgdt

Why does this assembly code does more than just lgdt?


So, I am beginning the development of a x86_64 hobby kernel and I found this code to load the GDT (Global Descriptor Table), but I don't understand what it does.

load_gdt:
  lgdt [rdi]
  mov ax, 0x10
  mov ss, ax
  mov ds, ax
  mov es, ax
  mov rax, qword .trampoline
  push qword 0x8
  push rax
  o64 retf

.trampoline:
    ret

I know it loads my gdt descriptor from the rdi register (register of the first parameter of a function call in sysv abi) but I don't know why I need to set all segment registers to 0x10 and what black magic is doing the rest ?


Solution

  • Once a (new) GDT exists, setting segment regs to selectors that index the GDT is what you do next; that's why you wanted a GDT in the first place. Or wanted your own GDT if you're replacing an old one, e.g. from UEFI. (Since you're already in 64-bit mode, there must already be a GDT; mov rax, .trampoline is a strange choice vs. a RIP-relative LEA, but involves a REX prefix so would decode wrong in 32-bit mode.)

    The last few are setting CS with a far jump, done by pushing a new CS:RIP and doing a far-ret to pop it into CS:RIP. It needs to be 64-bit operand-size to make sure you pop a 64-bit RIP, not a 32-bit EIP from the stack.

    (You can't of course mov to CS; that would be a jump because it would change where code-fetch is coming from. x86 only allows writing E/RIP or CS:E/RIP via jump instructions like call / jmp or retf.)

    See also https://wiki.osdev.org/Global_Descriptor_Table