Last modified: Mon Jun 29 08:12:32 UTC+0200 2026 © A. Tarpai
Descriptor structures
These structures sit in main memory, set up by system software, and the cpu will read them, when p-mode operation is turned on (PE-bit=1).
- Descriptor memory layout
- S=1 Segment Descriptors
- 80286 Code/Data descriptor
- 80386 Code/Data descriptor
- The B-bit meaning for limit check
- The B-bit meaning for Stack Segment Register
- LIMIT granularity and the G-bit
- Possible bit-combinations for loading Segment Descriptors and allowed r/w-operations with override prefix
- S=0 Control Descriptors
- Interrupt gate and Trap gate
- Call gate
- Task Gate
- Task State Descriptor
- LDT Segment Descriptor
- S=0 Control Descriptors summary
- The 3 Descriptor Tables and allowed Descriptors
Descriptor memory layout
The funny layout comes from 286, which used 3-words size descriptors (the last word is zero). The 286 is a 16-bit cpu with 24-bit address lines: all EA calculation and therefore every LIMIT is 16-bit (max 64K) and every BASE is 24-bit (3 bytes, address-space extension to 16MB). Eg. for segment descriptors:
286 16-bit Descriptor 386 32-bit Descriptor 15 0 15 0 +-----------------------------------------------+ +-----------------------------------------------+ | LIMIT 15..0 | 0 | LIMIT 15..0 | 0 +-----------------------------------------------+ +-----------------------------------------------+ | BASE 15..0 | 2 | BASE 15..0 | 2 +-----------------------+-----------------------+ +-----------------------+-----------------------+ | ACCESS RIGHT BYTE | BASE 23..16 | 4 | ACCESS RIGHT BYTE | BASE 23..16 | 4 +-----------------------+-----------------------+ +-----------------------+-----------+-----------+ | RESERVED = 0 | 6 | BASE HI | ATTR BITS | LIMIT HI | 6 +-----------------------------------------------+ +-----------------------+-----------+-----------+
The 286 structure, when layed out in memory in little-endian order, can be directly loaded into 286 CPU 48-bit cache registers. The last word was simply ignored.
286 DESCRIPTOR CACHE REGISTER 6 MEMORY BYTES
47 0
+----+----+----+----+----+----+ Load.. +----+----+----+----+----+----+----+----+
| AR | BASE | LIMIT | <------- | LIMIT | BASE | AR | |
+----+----+----+----+----+----+ +----+----+----+----+----+----+----+----+
0 1 2 3 4 5 6 7
The 386 is a 32-bit cpu with 32-bit address lines. Every EA OFFSET and every BASE values are 32-bit (4 bytes). 4-bits of Extended attributes and high address/limit information is placed in the last word. By having this word zero, descriptors are compatible with the 286 and system code can be still executed on the 386.
386 DESCRIPTOR CACHE REGISTER 8 MEMORY BYTES
+-------+----+----+----+----+----+----+----+----+ Load.. +----+----+----+----+----+----+----+----+
|ATTR/AR| BASE 31..0 | LIMIT 31..0 | <------- | LIMIT | BASE | AR | ATTR/HI |
+-------+----+----+----+----+----+----+----+----+ +----+----+----+----+----+----+----+----+
0 1 2 3 4 5 6 7
Loading this structure is a little more complicated – see LIMIT GRANULARITY below.
S=1 Segment Descriptors
Only S=1 descriptors can be loaded into SR DESCRIPTOR CACHE REGISTERS.
80286 Code/Data descriptor
286 Code/Data descriptor 15 0 +---------------------------------------------------------------+ | LIMIT 15..0 | 0 +---------------------------------------------------------------+ | BASE 15..0 | 2 +---+---+---+---+---+---+---+---+-------------------------------+ | P | DPL |S=1| X |C/E|R/W| A | BASE 23..16 | 4 +---+---+---+---+---+---+---+---+-------------------------------+ | RESERVED = 0 | 6 +---------------------------------------------------------------+ Segment Descriptor ACCESS RIGHTS BYTE: P - SEGMENT PRESENT i.e. addressable in real memory DPL - DESCRIPTOR PRIVILEGE LEVEL S=1 - SEGMENT bit: Data/Code segment. Must be 1 for segments. X - EXECUTABLE A - ACCESSED. CPU sets this bit in memory when it loads a segment selector
When X=1 the segment is Executable (the only type allowed for CS):
EXECUTABLE SEGMENT DESCRIPTOR (X=1)
| |
+---+---+---+---+---+---+---+---+-------------------------------+
| P | DPL |S=1|X=1| C | R | A | BASE 23..16 | 4
+---+---+---+---+---+---+---+---+-------------------------------+
| RESERVED = 0 | 6
+---------------------------------------------------------------+
C - CONFORMING
The code segment may be accessed from any lower privilege level
code without privilege level change - and stack-switch
R - READABLE (dont't care for CS, has meaning through data segregs)
It is not possible to write to a segment described as a code segment
When X=0 the segment is a data- or stack segment:
DATA SEGMENT DESCRIPTOR (X=0)
| |
+---+---+---+---+---+---+---+---+-------------------------------+
| P | DPL |S=1|X=0| E | W | A | BASE 23..16 | 4
+---+---+---+---+---+---+---+---+-------------------------------+
| RESERVED = 0 | 6
+---------------------------------------------------------------+
W - WRITABLE
For SS, only writable segment can be loaded.
E - EXPAND-DOWN
__
FFFF |
.... |
.... | E=1 EXPAND-DOWN
.... | VALID OFFSET > LIMIT
.... __|
LIMIT |
.... |
.... |
.... | E=0
.... | VALID OFFSET <= LIMIT
.... |
.... |
0000 __|
An expand-down segment has maximum size when the limit is zero.
BUT! Accessing Byte 0 will cause exception: must be greater, than limit.
Accessing Byte 1 will not - tested on real hardware.
Present-bit is the virtual memory management support bit and for disk swapping: If this bit is zero, the descriptor is not valid for use in address transformation; the processor will signal an exception when a selector for the descriptor is loaded into a segment register.
80386 Code/Data descriptor
Same ACCESS RIGHTS BYTE meaning. New attribute-bits:
386 Code/Data descriptor (Segment-bit = 1)
15 0
+---------------------------------------------------------------+
| LIMIT 15..0 | 0
+---------------------------------------------------------------+
| BASE 15..0 | 2
+---+---+---+---+---+---+---+---+-------------------------------+
| P | DPL |S=1| X |C/E|R/W| A | BASE 23..16 | 4
+---+---+---+---+---+---+---+---+---+---+---+---+---------------+
| BASE 31..24 | G |D/B| 0 | V | LIMIT 19..16 | 6
+-------------------------------+---+---+---+---+---------------+
G - LIMIT GRANULARITY FLAG = how to load the 20-bit value
31 0
+---+---+---+---+---+---+---+---+
| 0 0 0 | LIMIT 19..0 | <-- G=0: high-order 12 bits filled with zeroes (max 1MB)
+---+---+---+---+---+---+---+---+
| LIMIT 19..0 | F F F | <-- G=1: shift value by 12 and fill low-
+---+---+---+---+---+---+---+---+ order bits with ones*
* This is true for EXPAND-DOWN as well: LIMIT=0 and G=1 will load 00000FFF
V - AVAILABLE. Software can write any value here.
The processor does not set or clear this field.
386 EXECUTABLE SEGMENT DESCRIPTOR (X=1)
15 0
| |
+---+---+---+---+---+---+---+---+-------------------------------+
| P | DPL |S=1|X=1| C | R | A | BASE 23..16 | 4
+---+---+---+---+---+---+---+---+---+---+---+---+---------------+
| BASE 31..24 | G | D | 0 | V | LIMIT 19..16 | 6
+-------------------------------+---+---+---+---+---------------+
D - DEFAULT OPERAND- AND ADDRESS-SIZE
D = 0
W-bit in opcode or implicit data/register size means 16-bit WORD
MODR/M byte interpreted as legacy 8086 and results in 16-bit OFFSET (with OFFSET-HI zeroed)
D = 1
W-bit in opcode or implicit data/register size means 32-bit DWORD
MODR/M byte interpreted as 386-type and results in 32-bit OFFSET
386 DATA SEGMENT DESCRIPTOR (X=0)
15 0
| |
+---+---+---+---+---+---+---+---+-------------------------------+
| P | DPL |S=1|X=0| E | W | A | BASE 23..16 | 4
+---+---+---+---+---+---+---+---+---+---+---+---+---------------+
| BASE 31..24 | G | B | 0 | V | LIMIT 19..16 | 6
+-------------------------------+---+---+---+---+---------------+
B - BIG STACK POINTER
B = 1
Use 32-bit ESP for implicit stack instructions and operation
B = 0
Use 16-bit SP for implicit instructions
16-BIT LIMIT CHECK for expand down data segments including SS
The B-bit meaning for limit check
Offsets must be between 0000_0000 to 0000_FFFF when referencing through B=0 E=1 Expand Down Segments.
The B-bit meaning for Stack Segment Register
For dec-inc SP or ESP and data movement to [SS-BASE + SP] or [SS-BASE + ESP] for instructions that implicitly use the stack:
B = 0 B = 1
31 0 31 0
+---------+---------+ +-------------------+
| . . . . | SP +/- | ESP | ESP +/- | ESP
+---------+---------+ +-------------------+
| |
v v
+-------------------+ +-------------------+
| 0 0 0 0 x x x x | | x x x x x x x x |
+-------------------+ +-------------------+
LIMIT check LIMIT check
+-------------------+ +-------------------+
| SS:BASE | | SS:BASE |
+ +-------------------+ +-------------------+
_______________________________________________________________
+-------------------+ +-------------------+
| EFFECTIVE ADDRESS | | EFFECTIVE ADDRESS |
+-------------------+ +-------------------+
(.) ESP HI unchanged and ignored
These instructions are push, pop, ret, retf, iret and are NOT affected by address-size (D-bit) or the 67h prefix. B-bit in the current SS determines the operation. They implicitly use SS and there is segment override is not possible (tried with push).
LIMIT granularity and the G-bit
Guides how to load the 32-bit LIMIT into DCR from the 20-bit LIMIT-field value of the descriptor in memory.
G=1 usage makes sense to set limit over 1MB size (eg. flat memory model) – or essentially all expand-down segment in 32-bit (we have to specify a quite high limit):
LIMIT G=0 LIMIT G=1
Byte-granularity 4K-granularity
From 0 to 1MB In 4KB increments EXPAND UP EXPAND DOWN
in byte increment From 4KB From 0
to 4GB to 4GB-4KB
____________________________ ________________________________________________
Byte Byte Byte
LIMIT32 <-- LIMIT20 size LIMIT32 <-- LIMIT20 size size
FFFFFFFF FFFFFFFF <-- FFFFF 4GB 0
-- -- -- -- -- --
-- -- -- FFFFEFFF <-- FFFFE 4GB-4KB 4KB
-- -- -- -- -- --
-- -- -- FFFFDFFF <-- FFFFD 4GB-8KB 8KB
-- -- -- -- -- --
-- -- -- ........ .....
-- -- -- ........ .....
-- -- -- ........ .....
-- -- -- ........ .....
-- -- -- ........ .....
-- -- -- -- -- --
000FFFFF <-- FFFFF 1MB 000FFFFF <-- 000FF 1MB
000FFFFE <-- FFFFE -- -- --
........ ..... ........ .....
........ ..... ........ .....
........ ..... --------
........ ..... 00001FFF <-- 00001 8KB 4GB-8KB
........ ..... ........ .....
........ ..... 00000FFF <-- 00000 4KB 4GB-4KB
00000001 <-- 00001 1 byte ........
00000000 <-- 00000 0 byte ........ N/A
31.....0 19..0 31.....0 19..0
000xxxxx <-- xxxxx xxxxxFFF <-- xxxxx
LIMIT20 calculation for G=1 based on N = 4K size:
- EXPAND UP: LIMIT20 = N − 1
- EXPAND DOWN: LIMIT20 = NOT N
LSL – Load Segment Limit instruction operates the same: "If the descriptor has a page granular segment limit (the granularity flag is set to 1), the LSL instruction will translate the page granular limit (page limit) into a byte limit before loading it into the destination operand. The translation is performed by shifting the 20-bit "raw" limit left 12 bits and filling the low-order 12 bits with 1s."
Possible bit-combinations for loading Segment Descriptors and allowed r/w-operations with override prefix
When cannot be loaded/overridden – cannot be used for r/w. Invalid means exception (general protection fault with an error code of 0).
| S=1 Segment Descriptor | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| X=1 | X=0 | |||||||||||
| R=03 | R=1 | W=0 | W=1 | |||||||||
| LOAD | READ | WRITE | LOAD | READ | WRITE | LOAD | READ | WRITE | LOAD | READ | WRITE | |
| CS | valid2 | N/A | valid2 | N/A | invalid | invalid | ||||||
| DS | invalid | valid | valid | invalid | valid | valid | invalid | valid | valid | valid | ||
| ES | ||||||||||||
| SS | invalid1 | invalid1 | invalid1 | valid | valid | valid | ||||||
| (1) For SS, only writable segment can be loaded. Executable segment is not writable (when PE=1). (2) For CS, the R-bit is don't care (3) Execute-only segment: cannot be accessed via DS or ES, nor read via CS with a CS override prefix | ||||||||||||
Note that on the 8086 and in Real Mode Emulation (or any 16/32-code but PE=0) the Executable segment is writable and this is legal:
[BITS 16]
2E 67 FE 05 [00000000] INC BYTE [dword CS:0]
| |
| 32-bit addressing mode prefix
CS-prefix
S=0 Control Descriptors
There are Gates for execution transfer:
- Interrupt gates
- Trap gates
- Call gates
- Task gates
And Segments, a memory area with BASE and LIMIT:
- Local Descriptor Table Segment Descriptor: address of a LD TABLE
- Task State Segment Descriptor: address of a TSS STRUCTURE
These has selector registers to load and from GDT only. BASE/LIMIT is then cached on-chip in hidden descriptor part.
NOTE!! DESTINATION CODE SELECTOR RPL is don't care in GATES. DPL of Gate matters.
Interrupt gate and Trap gate
In IDT only:
Interrupt gate (IF=0 i.e. CLI) and Trap gate (IF=1) 16-bit 286 Type = 6/7h 15 0 +---------------------------------------------------------------+ | INTERRUPT CODE OFFSET | 0 +---------------------------------------------------+---+---+---+ | INTERRUPT CODE SEGMENT SELECTOR | T | x x | 2 +---+---+---+---+---+---+---+---+---+---+---+-------+---+---+---+ | P | DPL |S=0| 0 1 1 IF| 0 0 0 | unused | 4 +---+---+---+---+---+---+---+---+---+---+---+-------------------+ | RESERVED = 0 | 6 +---------------------------------------------------------------+ 32-bit 386 Type = E/Fh 15 0 +---------------------------------------------------------------+ | INTERRUPT CODE OFFSET | 0 +---------------------------------------------------+---+---+---+ | INTERRUPT CODE SEGMENT SELECTOR | T | x x | 2 +---+---+---+---+---+---+---+---+---+---+---+-------+---+---+---+ | P | DPL |S=0| 1 1 1 IF| 0 0 0 | unused | 4 +---+---+---+---+---+---+---+---+---+---+---+-------------------+ | INTERRUPT CODE OFFSET 31..16 | 6 +---------------------------------------------------------------+ x: RPL don't care. Protection on pointed descr DPL.
The difference between a trap and an interrupt gate is whether the interrupt enable flag IF is to be cleared or not.
Call gate
A controlled, indirect function pointer:
Call gate 16-bit 286 Type = 4h 15 0 +---------------------------------------------------------------+ | DESTINATION CODE OFFSET | 0 +---------------------------------------------------+---+---+---+ | DESTINATION CODE SELECTOR | T | x x | 2 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | P | DPL |S=0| 0 1 0 0 | 0 0 0 | WORD COUNT | 4 +---+---+---+---+---+---+---+---+---+---+---+-------------------+ | RESERVED = 0 | 6 +---------------------------------------------------------------+ 32-bit 386: Type = Ch 15 0 +---------------------------------------------------------------+ | DESTINATION CODE OFFSET | 0 +---------------------------------------------------+---+---+---+ | DESTINATION CODE SELECTOR | T | x x | 2 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | P | DPL |S=0| 1 1 0 0 | 0 0 0 | DWORD COUNT | 4 +---+---+---+---+---+---+---+---+---+---+---+-------------------+ | OFFSET 31..16 | 6 +---------------------------------------------------------------+ x: RPL don't care. Protection on the pointed (executable) descriptor's DPL.
WORD/DWORD COUNT. Used only when CALL more-privilege and stack-switch. Copy n (0..31) WORD/DWORD to new stack. RETF n pulls both.
Based on current TSS values. Get new SS selector from TSS.. set new and copy.
Task Gate
Used for another level of indirection to make task-switch.
Task Gate Descriptor (286/386) 15 0 +---------------------------------------------------------------+ | unused | 0 +---------------------------------------------------------------+ | TSS SELECTOR | 2 +---+---+---+---+---+---+---+---+-------------------------------+ | P | DPL |S=0| 0 1 0 1 | unused | 4 +---+---+---+---+---+---+---+---+-------------------------------+ | RESERVED = 0 | 6 +---------------------------------------------------------------+
Task gates can be located in any Descriptor Table (GDT, LDT, IDT).
Invoked by JMP or CALL (or INT).
Task gates may only refer to a task state segment. And the TSS is only in GDT.
Task switch
GDT
+------+
GDT/LDT/IDT | .. |
+------+ +------+
| .. | +-----------> |TSS De| ---> TSS structure
+------+ | +------+
| .. | | |LDT De| ---> LD TABLE
+------+ | +------+
|TASK G| ------------+ | .. |
+------+
| .. | Direct jump/call
refers to a TSS Descriptor
Indirect: jump/call/int in GDT
refers to a TASK GATE
(Task gate stores a selector
referring to the GDT)
Task State Descriptor
Only in GDT. Holds a linear memory address of a Task State Structure, TSS (with limit and access-rights).
Cached into on-chip register TR on explicit load (LTR r/m16) or on task-switch..
_________________________________________
| CPU | MEMORY
| |
| +--------------+ | 15 0
| | TR | --------------->---- |----> +-------------+
| +--------------+ linear address | | LDT | --> My LD TABLE Selector
| of TSS | +-------------+
| | | SEG REGS |
| ... |
+-------------+
| GP REGS |
| ... |
+-------------+
| FLAGS |
+-------------+
| IP | --> entry point
+-------------+
| SS:SP 2/1/0 | for automatic stack-switch
| ... |
+-------------+
| BACKLINK | --> Previous TSS Selector
NB: This is 16-bit TSS +-------------+
The 386 can work with both TSS structures to run 16-bit 286 system compatibility code. And therefore different 16/32-bit types. The 32-bit TSS is very different.. (just not really interested in this).
Task State Descriptor 16-bit 286 Type = 1/3h 15 0 +---------------------------------------------------------------+ | LIMIT 15..0 | 0 +---------------------------------------------------------------+ | BASE 15..0 | 2 +---+---+---+---+---+---+---+---+-------------------------------+ | P | DPL |S=0| 0 0 B 1 | BASE 23..16 | 4 +---+---+---+---+---+---+---+---+-------------------------------+ | RESERVED = 0 | 6 +---------------------------------------------------------------+ B-bit: BUSY CPU sets this bit, when TR reg is loaded by - explicitly by LTR instruction - any direct/indirect call/jmp/int referring to a Task Gate or this TSS Descriptor (dispatch) NB: LTR is not dispatch! But CPU will use eg. to read higher privilege SS:SP on privilege level change CALL/INT. 32-bit 386 Type = 9/Bh 15 0 +---------------------------------------------------------------+ | LIMIT 15..0 | 0 +---------------------------------------------------------------+ | BASE 15..0 | 2 +---+---+---+---+---+---+---+---+-------------------------------+ | P | DPL |S=0| 1 0 B 1 | BASE 23..16 | 4 +---+---+---+---+---+---+---+---+---+---+---+---+---------------+ | BASE 31..24 | G | 0 | 0 | V | LIMIT 19..16 | 6 +-------------------------------+---+---+---+---+---------------+
LDT Segment Descriptor
Only in GDT. Holds a linear memory address of a LOCAL TABLE structure (with limit and access-rights).
Cached into on-chip register LDTR on explicit load (LLDT r/m16) or on task-switch from the TSS.
_________________________________________
| CPU | MEMORY
| |
| +--------------+ | LDT
| | LDTR | --------------->---- |----> +------+
| +--------------+ linear address | | SEG |
| of descriptor table | +------+
| | |CALL G|
+------+
|TASK G|
+------+
| .. |
Same 286/386 type, RESERVED = 0 makes it 16-bit 286-compatible.
LDT Segment Descriptor (in GDT) Type = 2h 16-bit 286 15 0 +---------------------------------------------------------------+ | LIMIT 15..0 | 0 +---------------------------------------------------------------+ | BASE 15..0 | 2 +---+---+---+---+---+---+---+---+-------------------------------+ | P | DPL |S=0| 0 0 1 0 | BASE 23..16 | 4 +---+---+---+---+---+---+---+---+-------------------------------+ | RESERVED = 0 | 6 +---------------------------------------------------------------+ 32-bit 386 15 0 +---------------------------------------------------------------+ | LIMIT 15..0 | 0 +---------------------------------------------------------------+ | BASE 15..0 | 2 +---+---+---+---+---+---+---+---+-------------------------------+ | P | DPL |S=0| 0 0 1 0 | BASE 23..16 | 4 +---+---+---+---+---+---+---+---+---+---+---+---+---------------+ | BASE 31..24 | G | 0 | 0 | V | LIMIT 19..16 | 6 +-------------------------------+---+---+---+---+---------------+
S=0 Control Descriptors summary
| Type | 286 S=0 Types | 386 S=0 Types | |
|---|---|---|---|
| Name | Name | ||
| 0 | 0000 | - | - |
| 1 | 0001 | Available TSS | 80286 Available TSS |
| 2 | 0010 | LDT Segment | LDT Segment |
| 3 | 0011 | Busy TSS | 80286 Busy TSS |
| 4 | 0100 | Call gate | 80286 Call gate |
| 5 | 0101 | Task gate | Task gate |
| 6 | 0110 | Interrupt gate | 80286 Interrupt gate |
| 7 | 0111 | Trap gate | 80286 Trap gate |
| 8 | 1000 | - | - |
| 9 | 1001 | - | 80386 Available TSS |
| A | 1010 | - | - |
| B | 1011 | - | 80386 Busy TSS |
| C | 1100 | - | 80386 Call gate |
| D | 1101 | - | - |
| E | 1110 | - | 80386 Interrupt gate |
| F | 1111 | - | 80386 Trap gate |
The 3 Descriptor Tables and allowed Descriptors
Segment means a memory area with BASE, LIMIT and Access rights.
| GDT | LDT | IDT |
|---|---|---|
| Task State Segment | ||
| LDT Segment | ||
| Code/Data/Stack Segment | Code/Data/Stack Segment | |
| Call gate | Call gate | |
| Task gate | Task gate | Task gate |
| Interrupt gate | ||
| Trap gate |