I'm studying about protection ring of x86 system.
Examples of accessing data segments
In this picture, there are segment selectors. My questions are...
Thanks.
I will attempt to explain this in terms as simple as possible.
Segmentation is the idea of dividing memory into segments. These segments have a base address and a limit (or size).
Traditionally, in Real Mode (DOS era), segments were defined as follows:
base_address = segment_register * 16
limit = 64 KiB
In Protected Mode, however, segments are no longer defined like this. Instead, there's a table which contains details about every segment. As a bonus, it not only describes the base address and the limit, but also the privilege required to use it (that's why it's called Protected Mode) and if it's a 16-bit or 32-bit segment.
The GDT is a table (i.e. array) set up by an operating system in memory which contains descriptors. These come in 2 types: segment descriptors (which define segments, along with all their attributes) and system descriptors (which define various OS structures in memory, some are important, some not so much).
The operating system tells the CPU where this table is located using the privileged lgdt
instruction. However, if you attempt it in user-mode code, it will fault.
Segment descriptors define segments. See below to see how to actually load and use one. A segment descriptor has this format.
Segment selectors are simply indexes into the GDT. They have a specific format in which the last 3 bits are used for other purposes (bit 2 is the Table Indicator, while bits 1-0 are the Requested Privilege Level). Only bits 15-3 describe the actual index. For example, selector 0x20
indicates index 4 (starting from 0) in the GDT.
The segment registers (CS
, DS
, ES
, FS
, GS
and SS
) are designed to store these selectors while they are in use. However, when they are not in use, they can be stored in memory or in any other place.
The NULL selector acts as a sort of NULL pointer from C. You can freely load it into segment registers, but any attempt to use it will fault.
Example: a memory access such as [ebx]
will be relative to the segment base described by DS
(or you can explicitly specify a segment register, such as [es:ebx]
).
In Real Mode, such an instruction would have accessed the memory address ds * 16 + ebx
. However, in Protected Mode, the base address is taken from the GDT (at the index indicated by DS
), and the offset ebx
is added to it. The good part is that if the descriptor indicated by DS
is privileged, but the code making the memory access is not, it will fault with a General Protection Fault.
The segment descriptors, as mentioned before, are part of the GDT and are created by the operating system. The segment selectors are (usually) given by the OS to the application. The application is free to set its own selectors if it wants, provided that the corresponding descriptors exist and are accessible (which in modern OSes is almost never the case).
One can and always has multiple selectors. This is because CS
must reference a code segment descriptor, while everything else references a data segment descriptor.
It is. If that was the case, the CPU would have to access the correct GDT entry every time someone accessed memory, in order to validate privilege levels and to get the segment base and limit for calculations.
Fortunately, the CPU caches these entries (into the so-called descriptor caches) whenever you load a segment register. This is why loading a segment register is a rather expensive operation. These caches are then guaranteed to remain unchanged until you reload the corresponding segment register again. This includes switching out of Protected Mode, without restoring the descriptor caches (and this is how you enter the Unreal Mode).
Segmentation cannot be disabled on x86, but it can be...well...bypassed. By setting all descriptors as having base 0 and a 4GiB limit, you essentially bypass all advantages and disadvantages of segmentation.
All modern systems use paging to achieve memory protection. However, segmentation is still required because it enforces the privilege level. Each descriptor in the GDT has a DPL (Descriptor Privilege Level) field. This is what enforces the so-called privilege Rings. If a user-mode application had access to Ring 0 selectors, it could execute privileged instructions and bypass every protection mechanism, including paging.
Apart from that, there are a couple of system structures (defined in the GDT using system descriptors) such as the Task State Segment (TSS) which are critical for the OS. The TSS, for example, ensures that when any transition from Ring 3 to Ring 0 happens, the stack (SS:ESP
) is switched to a well-known kernel stack, since the user-mode stack can't be trusted.
Segmentation is still used for the FS
and GS
registers. In Windows, FS
references a descriptor which points to the Thread Information Block, while GS
is NULL.
In 64-bit mode, segmentation is almost disabled. The only selector that truly matters is CS
, which sets the privilege level according to the DPL field. The DS
, ES
and SS
registers are unused. DS
and ES
are set to 0 (in 64-bit mode the NULL selector does nothing, it doesn't fault when used), while SS
still points to a descriptor (loading NULL faults), though I'm not really sure why it can't be 0.
However, the descriptors can't set a base or a limit*. Essentially, the CPU creates the same environment that has to be created manually in 32-bit mode. Memory protection is achieved by paging, which is mandatory in 64-bit mode.
There is a way to set the base for the FS
and GS
selectors (see the WRFSBASE
and WRGSBASE
instructions). This doesn't affect the GDT entries at all (they still only have space for a 32-bit base), but instead directly modifies the descriptor caches (the registers themselves are typically set to 0). Windows 64-bit uses GS
instead of FS
to point to the TIB.
* Some CPUs support setting a segment limit in the last 4GiB of the 256TiB address space. From what I understand, this was done to facilitate software virtualization before hardware virtualization (VT-x and AMD-V) was a thing.