Intel x86 Assembly Language & Microarchitecture Real vs Protected modes Unreal mode


Example

The unreal mode exploits two facts on how both Intel and AMD processors load and save the information to describe a segment.

  1. The processor caches the descriptor information fetched during a move in a selector register in protected mode.
    These information are stored in an architectural invisible part of the selector register themselves.

  2. In real mode the selector registers are called segment registers but, other than that, they designate the same set of registers and as such they also have an invisible part. These parts are filled with fixed values, but for the base which is derived from the value just loaded.

In such view, real mode is just a special case of protected mode: where the information of a segment, suchlike the base and limit, are fetched without a GDT/LDT but still read from the segment register hidden part.


By switching in protected mode and crafting a GDT is possible to create a segment with the desired attributes, for example a base of 0 and a limit of 4GiB.
Through a successive loading of a selector register such attributes are cached, it is then possible to switch back in real mode and have a segment register through which access the whole 32 bit address space.

BITS 16

jmp 7c0h:__START__

__START__:
 push cs
 pop ds
 push ds
 pop ss
 xor sp, sp

 
 lgdt [GDT]            ;Set the GDTR register
 

 cli                ;We don't have an IDT set, we can't handle interrupts


 ;Entering protected mode

 mov eax, cr0
 or ax, 01h            ;Set bit PE (bit 0) of CR0
 mov cr0, eax            ;Apply

 ;We are now in Protected mode

 mov bx, 08h           ;Selector to use, RPL = 0, Table = 0 (GDT), Index = 1

 mov fs, bx            ;Load FS with descriptor 1 info
 mov gs, bx            ;Load GS with descriptor 1 info

 ;Exit protected mode

 and ax, 0fffeh            ;Clear bit PE (bit0) of CR0
 mov cr0, eax                   ;Apply

 sti                

 ;Back to real mode

 ;Do nothing
 cli
 hlt 



 GDT:
    ;First entry, number 0
    ;Null descriptor
    ;Used to store a m16&32 object that tells the GDT start and size

    dw 0fh                 ;Size in byte -1 of the GDT (2 descriptors = 16 bytes)
    dd GDT + 7c00h         ;Linear address of GDT start (24 bits)
    dw 00h                 ;Pad 

    dd 0000ffffh           ;Base[15:00] = 0, Limit[15:00] = 0ffffh
    dd 00cf9200h           ;Base[31:24] = 0, G = 1, B = 1, Limit[19:16] = 0fh, 
               ;P = 1, DPL = 0, E = 0, W = 1, A = 0, Base[23:16] = 00h


 TIMES 510-($-$$) db 00h
 dw 0aa55h 

Considerations

  • As soon as a segment register is reloaded, even with the same value, the processor reload the hidden attributes according to the current mode. This is why the code above use fs and gs to hold the "extended" segments: such registers are less likely to be used/saved/restored by the various 16 bit services.
  • The lgdt instruction doesn't load a far pointer to the GDT, instead it loads a 24 bit (can be overridden to 32 bit) linear address. This is not a near address, it is the physical address (since paging must be disabled). That's the reason of GDT+7c00h.
  • The program above is a bootloader (for MBR, it has no BPB) that set cs/ds/ss tp 7c00h and start the location counter from 0. So a byte at offset X in the file is at offset X in the segment 7c00h and at the linear address 7c00h + X.
  • Interrupts must be disabled as an IDT is not set for the short round trip in protected mode.
  • The code use an hack to save 6 bytes of code. The structure loaded by lgdt is saved in the... GDT itself, in the null descriptor (the first descriptor).

For a description of the GDT descriptors see Chapter 3.4.3 of Intel Manual Volume 3A.