Descriptors: Difference between revisions
[unchecked revision] | [unchecked revision] |
No edit summary |
m Bot: Replace deprecated source tag with syntaxhighlight |
||
(13 intermediate revisions by 6 users not shown) | |||
Line 1: | Line 1: | ||
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]]. |
|||
{{Convert}} |
|||
== Common properties == |
|||
All descriptors are 8 bytes in size. Different types may have different format. |
|||
_There is also [descriptor coverage in BabySteps tutorial|BabyStep6]. This is intended as a reference, with exhaustive coverage of various descriptor types, their structs and options, which can be used both for analysis of existing descriptors and for synthesis of your own._ |
|||
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]. |
|||
All descriptors are 8 bytes in size, but 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) |
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) |
||
<syntaxhighlight lang="c"> |
|||
<verbatim> |
|||
// a "general" structure of a descriptor whose type we don't know, |
// 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 |
// 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 |
// according to the type you're dealing with, or define a union for all types |
||
struct Descr{ |
struct Descr{ |
||
uint8_t bytes[8]; |
|||
// --- accessor methods |
|||
// --- accessor methods |
|||
uint8 type_attr(){ return bytes[5]; }; |
|||
uint8_t type_attr(){ return bytes[5]; }; |
|||
}; |
}; |
||
</syntaxhighlight> |
|||
</verbatim> |
|||
"type_attr" has the following bit fields: |
|||
* bit 7: "P" (Present) flag |
* 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 <del>General Protection Fault</del> 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) |
* 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). |
* 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) |
* bits 0..3: type (4 bits, 0..0xF) |
||
Here are the formulas: |
Here are the formulas: |
||
{| {{wikitable}} |
|||
type | |
|||
|- |
|||
(type_attr & 0xF) |
|||
! type |
|||
S | |
|||
| (type_attr & 0xF) |
|||
|- |
|||
DPL | |
|||
! S |
|||
((type_attr>>5) & 3) |
|||
| ((type_attr>>4) & 1) |
|||
P | |
|||
|- |
|||
((type_attr>>7) & 1) |
|||
! DPL |
|||
| ((type_attr>>5) & 3) |
|||
|- |
|||
! P |
|||
| ((type_attr>>7) & 1) |
|||
|} |
|||
== Code/Data Segment Descriptors == |
|||
These have S=1. Bit 3 of "type" indicates whether it's (0) Data or (1) Code. |
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: |
|||
<verbatim> |
|||
<syntaxhighlight lang="c"> |
|||
struct SegDescr{ |
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 0..3: limit, bits 16..19 |
||
//^ bits 4..7: additional data/code attributes |
//^ bits 4..7: additional data/code attributes |
||
uint8_t base_3; // base, bits 24..31 |
|||
}; |
}; |
||
</syntaxhighlight> |
|||
</verbatim> |
|||
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 |
|||
So we have (putting all chunks of each field together): |
|||
* 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) |
|||
* Base - 32 bits total |
|||
* when using G=1 (granularity: pages), the amount of accessible bytes can be from 4Kb to 4Gb (= 0x1000 * limit + 0xfff) |
|||
* Limit - 20 bits total |
|||
** when using G=0 (granularity: bytes), limit can be from 1b to 1Mb |
|||
** when using G=1 (granularity: pages), limit can be from 4Kb to 4Gb |
|||
Type bits interpretation (general, for details specific to data or code see below): |
|||
bit 3 | |
|||
Data/Code | |
|||
0 (data)<br> |
|||
1 (code) |
|||
bit 2 | |
|||
E/C | |
|||
Expand-down (data)<br> |
|||
Conforming (code) |
|||
bit 1 | |
|||
W/R | |
|||
Writeable (data)<br> |
|||
Readable (code) |
|||
bit 0 | |
|||
A | |
|||
Accessed (code, data) |
|||
Additional attributes from lim_attr (bits 4..7): |
Additional attributes from lim_attr (bits 4..7): |
||
{| {{wikitable}} |
|||
bit 7 | |
|||
|- |
|||
G | |
|||
! bit 7 |
|||
Granularity | |
|||
! G |
|||
0 (limit is in bytes)<br> |
|||
! Granularity |
|||
1 (limit is in pages of 4096 bytes) |
|||
| 0 (limit is in bytes)<br>1 (limit is in pages of 4096 bytes) |
|||
bit 6 | |
|||
|- |
|||
D/B | |
|||
! bit 6 |
|||
Default operand size/Big | |
|||
! D/B |
|||
0 for 16-bit segments<br> |
|||
! Default operand size/Big |
|||
1 for 32-bit segments |
|||
| 0 for 16-bit segments<br>1 for 32-bit segments |
|||
bit 5 | |
|||
|- |
|||
L | |
|||
! bit 5 |
|||
! L |
|||
0 normally<br> |
|||
! 64-bit code segment |
|||
| 0 normally<br>1 if this is a 64-bit code segment in IA-32e mode |
|||
bit 4 | |
|||
|- |
|||
AVL | |
|||
! bit 4 |
|||
Available | |
|||
! AVL |
|||
For use by system software<br>(your OS can use this as you choose) |
|||
! Available |
|||
| For use by system software<br>(your OS can use this as you choose) |
|||
|} |
|||
The following snippet calculates the base and 20-bit limit from a descriptor: |
|||
<syntaxhighlight lang="c"> |
|||
<verbatim> |
|||
// computing base and limit values - putting the bits together |
// computing base and limit values - putting the bits together |
||
uint32_t SegDescr::base(){ |
|||
return |
return |
||
base_1 | |
base_1 | |
||
Line 115: | Line 105: | ||
}; |
}; |
||
uint32_t SegDescr::limit(){ |
|||
return |
return |
||
limit_1 | |
limit_1 | |
||
((lim_attr&0xf)<<16); |
((lim_attr&0xf)<<16); |
||
}; |
}; |
||
</syntaxhighlight> |
|||
</verbatim> |
|||
=== Type bits for Data segments === |
|||
{| {{wikitable}} |
|||
!! Type bits for Data segments |
|||
|- |
|||
! bit 3 |
|||
! Data/Code |
|||
| 0 (data) |
|||
|- |
|||
! bit 2 |
|||
! Expand-down |
|||
| 0 (normal) <br> 1 ([[Expand_Down|expand-down]]) |
|||
|- |
|||
! bit 1 |
|||
! Writable |
|||
| 0 (read-only) <br> 1 (read-write) |
|||
|- |
|||
! bit 0 |
|||
! Accessed |
|||
| 0 (hasn't been accessed) <br> 1 (has been accessed) |
|||
|} |
|||
=== Type bits for Code segments === |
|||
bit 3 | |
|||
Data/Code | |
|||
0 (data) |
|||
bit 2 | |
|||
Expand-down | |
|||
0 (normal)<br> |
|||
1 (expand-down FIXME how this works?) |
|||
bit 1 | |
|||
Writeable | |
|||
0 (read-only)<br> |
|||
1 (read-write) |
|||
bit 0 | |
|||
Accessed | |
|||
0 (hasn't been accessed)<br> |
|||
1 (has been accessed) |
|||
{| {{wikitable}} |
|||
!! Type bits for Code segments |
|||
|- |
|||
! bit 3 |
|||
! Data/Code |
|||
| 1 (code) |
|||
|- |
|||
! bit 2 |
|||
! Conforming |
|||
| 0 (non-conforming) <br> 1 (conforming) |
|||
|- |
|||
! bit 1 |
|||
! Readable |
|||
| 0 (execute-only) <br> 1 (executable and readable) |
|||
|- |
|||
! bit 0 |
|||
! Accessed |
|||
| 0 (hasn't been accessed) <br> 1 (has been accessed) |
|||
|} |
|||
== See Also == |
|||
bit 3 | |
|||
* [[Protected Mode]] |
|||
Data/Code | |
|||
* [[Babystep6]] |
|||
1 (code) |
|||
bit 2 | |
|||
Conforming | |
|||
0 (non-conforming)<br> |
|||
1 (conforming FIXME how this works?) |
|||
bit 1 | |
|||
Readable | |
|||
0 (execute-only)<br> |
|||
1 (executable and readable) |
|||
bit 0 | |
|||
Accessed | |
|||
0 (hasn't been accessed)<br> |
|||
1 (has been accessed) |
|||
[[Category:X86 CPU]] |
|||
_to be continued_ |
Latest revision as of 05:13, 9 June 2024
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 FaultSegment 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(){
return
base_1 |
(base_2<<16) |
(base_3<<24);
};
uint32_t SegDescr::limit(){
return
limit_1 |
((lim_attr&0xf)<<16);
};
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) |