2

I'm writing my own kernel and used this code to override global descriptor table set by bootloader. This is done in 32 bit protected mode.

flush_gdt:
    lgdt [gdtr]
    jmp 0x08:complete_flush
 
complete_flush:
    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax
    ret

The code works fine however I'm not able to understand why next instruction after lgdt is executed properly.

As far as I understand values of segment registers (especially CS) remain unchanged. Therefore CPU will use old register and new table to resolve address of instruction jmp 0x08:complete_flush. Chances are that entry in my table pointed by value of old CS is not a valid code segment and this would lead to crash. However nothing like this happens. Why?

EDIT: Asking this because my understanding is that CPU resolve linear addresses using CS:EIP registers pair. Now after lgdt CS would suddenly point to diffrent entry so why next instruction executed is the jmp instruction

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
jason
  • 45
  • 1
  • 7
  • 3
    The segment registers have shadow fields for base, limit, access rights, privilege, etc. All or some of these are written to when you reload the register. In Protected Mode reloading a segment register dereferences the selector into the LDT or GDT, then reads the descriptor, and then updates the shadow fields from the descriptor's contents. In Real and Virtual 86 Mode the shadow segment base is updated from the segment value that is being loaded. If you switch from Real 86 Mode to Protected Mode, the `cs` shadow fields retain the limit and base and so on, until you reload `cs` (by far jump). – ecm Sep 29 '20 at 07:56
  • 2
    The value/meaning of CS isn’t changed when you change the GDT. The base, limit, type, etc, are stored within the processor. They change only when CS is reloaded by the far jmp instruction. – prl Sep 29 '20 at 08:06
  • 2
    @ecm: I wouldn't say "shadowed", that implies they're shadowed *by* something. But the segment base / limit and current mode and so on are simply not directly accessible in any mode (except for FS / GS base [via `wrmsr` or `wrfsbase`](https://stackoverflow.com/questions/55746156/)). I prefer to say "internal" registers. But yes the key point is that they're only updated when modify a segment register. i.e. you can only set them indirectly but they are real registers, not a "cache" or anything. The thing they were loaded from can change without affecting them. – Peter Cordes Sep 29 '20 at 08:06
  • @Peter Cordes: Technically we could call them "caches", just ones with such particular reload semantics that they "cache" the descriptors from the point in time when the segreg was last reloaded. Anyway, this is splitting hairs. – ecm Sep 29 '20 at 08:22
  • @ecm: I don't like the term "cache" for something that can't be evicted and reloaded / recalculated from other data at any time. It leads to much less confusion if we reserve the term for actual caches. (e.g. people have all kinds of wrong ideas about (lack of) CPU cache coherency because of sloppy descriptions of C++ compilers "caching" a non-atomic value in registers.) Useful choices for terminology are a huge part of clearly explaining complicated things. In this case, I'm happy with terminology like "hidden part of the architectural state", or "internal registers", or similar. – Peter Cordes Sep 29 '20 at 08:33
  • 2
    @ecm Thank you for your comment. That makes a lot of sense now. Misconception that I had is that only a segment selector is stored in registers. If base, limit, access rights, etc. are all stored that's a tottaly different story. It seems resonable as CPU won't have to access GDT every time it want to get data about code segment. Big thanks. – jason Sep 29 '20 at 09:24
  • @ecm After a moment of thinking I still do not understand. Segment registers are 16 bit, how can they hold base, limit, access rights and privilage? Also according to highest voted answer here https://stackoverflow.com/questions/9113310/segment-selector-in-ia-32 I understand that only table type, index in table and previlages are stored in registers. Can you explain how it works? – jason Sep 29 '20 at 10:08
  • 3
    Only the visible part is 16 bit, the other fields are stored in the appropriately sized hidden part. – Jester Sep 29 '20 at 10:19
  • @Jester Ahhh I see. Thank you. Could you please share the source to get this kind of information? I've spent some time searching for this and couldn't find anything reliable. – jason Sep 29 '20 at 10:26
  • 3
    _Intel® 64 and IA-32 Architectures Software Developer's Manual Volume 3: System Programming Guide_, section _3.4.3 Segment Registers_. – Jester Sep 29 '20 at 10:37
  • @Jester I was afraid that is the case :). Well looks like I'm not gonna get away with reading that. Thanks. – jason Sep 29 '20 at 10:48

0 Answers0