The usual translation goes as follow:
Logical address --> GDT --> Linear address --> Page tables --> Physical Address
(segment:offset) (segment base + offset)
\______________________________________________________/
Virtual address
(can be either logical or linear)
If running in VMX non-root mode (i.e. in a VM) and EPT is enabled then:
Logical address --> GDT --> Linear address --> Page tables --> Guest Physical Address --> EPT --> (System) Physical Address
(segment:offset) (segment base + offset)
\______________________________________________________/ \__________________________________________________________/
Virtual address Physical address
(can be either logical or linear)
If an IOMMU is present (like the umbrella technology VT-d):
Logical address --> GDT --> Linear address --> Page tables --> Guest Physical Address --> EPT --> (System) Physical Address --> 1 or 2 level translation --> (IO) Physical address
(segment:offset) (segment base + offset)
\______________________________________________________/ \___________________________________________________________________________________________________________________/
Virtual address Physical address
(can be either logical or linear)
The MMIO can even perform the translation of the Guest Virtual Address or the Guest Physical Address (one of it's purposes is to reify the Virtual address of an application to the hardware and simplify the management of the plethora of address spaces encountered during the translation).
Note As Hadi Brais pointed out, the term "Virtual address" only designates a Linear address in the Intel and AMD manuals.
I find it more useful to label both the logical and the linear addresses as virtual because they are before the page translation step.
The segment register holds a segment selector that index a segment descriptor that is used to performs the security checks and get the segment base that is summed with the offset part of the logical address.
After that, it's done.
Every address specified at the instruction level is a logical address - requiring the lookup of the segment descriptor.
To avoid reading it from memory each time the memory is accessed by an instruction, the CPU caches it - otherwise that would be a performance killer.
The OS setup the segment registers based on what it need to do but it rarely need more that four segments anyway.
The primary intent for segmentation (in PM) was to fulfil process isolation by defining non overlapping segments for each program.
A program usually need only a stack segment, a data segment and a code segment - the other three are there to avoid saving/restoring the data segment back then when a segment max size was 64KiB (read: Real mode. fs
and gs
were added later though).
Today OSes use a flat model where there are only two segments (code and data/stack - this is a simplification, other segments are required) encompassing the whole address space, plus OS specifics segments for things like TLS or PEB/TEB.
So six segment registers are even more than it's needed, the 8192 entries of the GDT are there in case they are (if even) needed.
syscall
assumes that the kernel will useswapgs
and a load from[gs: somewhere]
to find the kernel stack. But maybe that design came after they decided to keep segment bases. – Peter Cordessyscall
, see Why does Windows64 use a different calling convention from all other OSes on x86-64? for links to mailing list archives about the design ofsyscall
andswapgs
(between AMD architects and Linux kernel devs, resulting in AMD adding a mask for RFLAGS and using R11 to save the old RFLAGS, so the kernel could make sure interrupts are disabled if it wants.) – Peter Cordes