From OSDev Wiki
Jump to: navigation, search

Descriptors are one of the key structures of Protected Mode. They are used to hold information about various system objects and are used as entries in the three system tables - GDT, LDT and IDT.


Common properties

All descriptors are 8 bytes in size. Different types may have different format.

One thing they all have in common is the "type_attr" field (byte at offset 5) that contains descriptor type and some common attributes. Thus, if you have a descriptor of yet unknown type, you should look at the "type_attr" byte to find out the type. (Maybe you want to analyze and display the contents of some table)

// a "general" structure of a descriptor whose type we don't know,
// only for the sake of discussion - you will want to use a more specific structure
// according to the type you're dealing with, or define a union for all types
struct Descr{
    uint8_t bytes[8];
    // --- accessor methods
    uint8_t type_attr(){ return bytes[5]; };

"type_attr" has the following bit fields:

  • bit 7: "P" (Present) flag
Indicates whether the descriptor is being used. Accessing an object through a descriptor which has P=0 will cause a General Protection Fault Segment Not Present Fault (Int 0Bh) - or, if it's trying to be used as a Stack Segment, a Stack Fault (Int 0Ch).
  • bits 5..6: DPL - Descriptor Privilege Level (2 bits, 0..3)
Used to control access to the object described by the descriptor
  • bit 4: "S" - whether this is a data/code segment (S=1) or a system segment (some other object) (S=0).
One can think of it as "(data/code) Segment" flag. This, combined with the "type" code, can be used to determine the descriptor type and structure.
  • bits 0..3: type (4 bits, 0..0xF)

Here are the formulas:

type (type_attr & 0xF)
S ((type_attr>>4) & 1)
DPL ((type_attr>>5) & 3)
P ((type_attr>>7) & 1)

Code/Data Segment Descriptors

These are descriptors in the GDT that have S=1. Bit 3 of "type" indicates whether it's (0) Data or (1) Code. The interpretation of the other bits are given further down this page.

The structure for a code/data descriptor is as follows:

struct SegDescr{
    uint16_t limit_1;   // limit, bits 0..15
    uint16_t base_1;    // base, bits 0..15
    uint8_t base_2;     // base, bits 16..23
    uint8_t type_attr;  // type_attr
    uint8_t lim_attr;
      //^ bits 0..3: limit, bits 16..19
      //^ bits 4..7: additional data/code attributes
    uint8_t base_3;     // base, bits 24..31

When concatenating all chunks, base has 32 bits and limit has 20 bits. To allow for a wide range of sizes, the G bit influences how the 20 bits of the limit field are converted to a 32-bit limit

  • when using G=0 (granularity: bytes), the amount of accessible bytes can be from 1b to 1Mb (bytes 0x0 to limit. byte 0 will always be writable)
  • when using G=1 (granularity: pages), the amount of accessible bytes can be from 4Kb to 4Gb (= 0x1000 * limit + 0xfff)

Additional attributes from lim_attr (bits 4..7):

bit 7 G Granularity 0 (limit is in bytes)
1 (limit is in pages of 4096 bytes)
bit 6 D/B Default operand size/Big 0 for 16-bit segments
1 for 32-bit segments
bit 5 L 64-bit code segment 0 normally
1 if this is a 64-bit code segment in IA-32e mode
bit 4 AVL Available For use by system software
(your OS can use this as you choose)

The following snippet calculates the base and 20-bit limit from a descriptor:

// computing base and limit values - putting the bits together
uint32_t SegDescr::base(){
   base_1 |
   (base_2<<16) |
uint32_t SegDescr::limit(){
  limit_1 |

Type bits for Data segments

bit 3 Data/Code 0 (data)
bit 2 Expand-down 0 (normal)
1 (expand-down)
bit 1 Writable 0 (read-only)
1 (read-write)
bit 0 Accessed 0 (hasn't been accessed)
1 (has been accessed)

Type bits for Code segments

bit 3 Data/Code 1 (code)
bit 2 Conforming 0 (non-conforming)
1 (conforming)
bit 1 Readable 0 (execute-only)
1 (executable and readable)
bit 0 Accessed 0 (hasn't been accessed)
1 (has been accessed)

See Also

Personal tools