-
Notifications
You must be signed in to change notification settings - Fork 36
Documentation for gdt.rs #7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Intact01
wants to merge
3
commits into
codenet:main
Choose a base branch
from
Intact01:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
# [GDT](https://github.com/codenet/vmm-reference/blob/main/src/vm-vcpu-ref/src/x86_64/gdt.rs) | ||
codenet marked this conversation as resolved.
Show resolved
Hide resolved
codenet marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
codenet marked this conversation as resolved.
Show resolved
Hide resolved
|
||
This is breif description of file [gdt.rs](https://github.com/codenet/vmm-reference/blob/main/src/vm-vcpu-ref/src/x86_64/gdt.rs) | ||
|
||
This module is used for building Global Descriptors Table (GDT) and writing it to Guest Memory. | ||
|
||
The module uses following crates- | ||
- [`kvm-bindings`](https://github.com/rust-vmm/kvm-bindings): To use `kvm_segment` struct which is the datatype of all sregs registers (cs, ds, es, fs etc.) in `KvmVcpu` (see [file](https://github.com/codenet/vmm-reference/blob/main/src/vm-vcpu/src/vcpu/mod.rs)) | ||
- [`vm-memory`](https://github.com/rust-vmm/vm-memory): used when writing GDT to Guest Memory | ||
|
||
<br/><br/> | ||
Let's now start understanding the different parts of the file- | ||
## `SegmentDescriptor` | ||
```rs | ||
pub struct SegmentDescriptor(pub u64); | ||
``` | ||
SegmentDescriptor is the class to create GDT entries and export them as `kvm_segment`. | ||
|
||
The segment descriptor has following structure (from Section "3.4.5 Segment Descriptors" of the [Intel Manual](https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-vol-3a-part-1-manual.pdf)) | ||
 | ||
<br/><br/> | ||
|
||
Now let's look at the methods of the class- | ||
### `from` | ||
```rs | ||
pub fn from(flags: u16, base: u32, limit: u32) -> Self | ||
``` | ||
This method creates segment descriptor from the arguments `base`, `limit` and `flags`. The descriptions of these and strcutrure of the 64 bits are the same as given in the above figure. | ||
<br/><br/> | ||
|
||
### Segment Descriptor Fields Extraction Methods | ||
```rs | ||
fn base(&self) -> u64 | ||
fn limit(&self) -> u32 | ||
fn g(&self) -> u8 | ||
fn db(&self) -> u8 | ||
fn l(&self) -> u8 | ||
fn avl(&self) -> u8 | ||
fn p(&self) -> u8 | ||
fn dpl(&self) -> u8 | ||
fn s(&self) -> u8 | ||
fn segment_type(&self) -> u8 | ||
``` | ||
All the fields are explianed in the figure above. These methods extract the values of these fields from the 64 bits stored in the class. | ||
<br/><br/> | ||
|
||
### `create_kvm_segment` | ||
```rs | ||
fn create_kvm_segment(&self, table_index: usize) -> kvm_segment { | ||
kvm_segment { | ||
base: self.base(), | ||
limit: self.limit(), | ||
// The multiplication is safe because the table_index can be maximum | ||
// `MAX_GDT_SIZE`. The conversion is safe because the result fits in u16. | ||
selector: (table_index * 8) as u16, | ||
type_: self.segment_type(), | ||
present: self.p(), | ||
dpl: self.dpl(), | ||
db: self.db(), | ||
s: self.s(), | ||
l: self.l(), | ||
g: self.g(), | ||
avl: self.avl(), | ||
padding: 0, | ||
unusable: match self.p() { | ||
0 => 1, | ||
_ => 0, | ||
}, | ||
} | ||
} | ||
``` | ||
This method creates a `kvm_segment` from `SegmentDescriptor` object. | ||
|
||
All the fields are the same as the segment descriptor except for `selector` and `unusable`. | ||
|
||
- `selector` stores the 16 bit segment selector value. First 13 bits are index of the entry in GDT table and last 3 bits are flags (TI and RPL) which are set to 0 here. | ||
- TI specifies which descriptor table to use. It's set if LDT (Local Descriptor Table) is used. | ||
- RPL is requested Privilege Level of the selector. | ||
- If the entry is not present then it is `unusable`. | ||
<br/><br/> | ||
|
||
## `Gdt` | ||
```rs | ||
pub struct Gdt(Vec<SegmentDescriptor>); | ||
``` | ||
`Gdt` is a wrapper for creating and managing operations on the Global Descriptor Table (GDT). | ||
|
||
This class has methods `new`, `try_push`, `create_kvm_segment_for` and `write_to_mem`. | ||
<br/><br/> | ||
|
||
### `new` | ||
```rs | ||
pub fn new() -> Gdt | ||
``` | ||
This just creates empty GDT. | ||
<br/><br/> | ||
|
||
### `try_push` | ||
```rs | ||
pub fn try_push(&mut self, entry: SegmentDescriptor) -> Result<()> { | ||
if self.0.len() >= MAX_GDT_SIZE { | ||
return Err(Error::TooManyEntries); | ||
} | ||
self.0.push(entry); | ||
Ok(()) | ||
} | ||
``` | ||
This method tries to push an entry (`SegmentDescriptor` object) to the GDT and is successful if the size of GDT is less than `MAX_GDT_SIZE` (set to 2<sup>13</sup>). | ||
<br/><br/> | ||
|
||
### `create_kvm_segment_for` | ||
```rs | ||
pub fn create_kvm_segment_for(&self, index: usize) -> Option<kvm_segment> | ||
``` | ||
This method creates and returns `kvm_segment` for the GDT entry at given index. | ||
<br/><br/> | ||
|
||
### `write_to_mem` | ||
```rs | ||
pub fn write_to_mem<Memory: GuestMemory>(&self, mem: &Memory) -> Result<()> { | ||
let boot_gdt_addr = GuestAddress(BOOT_GDT_OFFSET); | ||
for (index, entry) in self.0.iter().enumerate() { | ||
// The multiplication below cannot fail because we can have maximum 8192 entries in | ||
// the gdt table, and 8192 * 4 (size_of::<u64>) fits in usize | ||
let addr = mem | ||
.checked_offset(boot_gdt_addr, index * mem::size_of::<SegmentDescriptor>()) | ||
.ok_or(GuestMemoryError::InvalidGuestAddress(boot_gdt_addr))?; | ||
mem.write_obj(*entry, addr)?; | ||
} | ||
Ok(()) | ||
} | ||
``` | ||
This method writes the GDT into the Guest Memory with GDT base at address `BOOT_GDT_OFFSET` (equals `0x500`). | ||
<br/><br/> | ||
|
||
### Default GDT | ||
```rs | ||
fn default() -> Self { | ||
Gdt(vec![ | ||
SegmentDescriptor::from(0, 0, 0), // NULL | ||
SegmentDescriptor::from(0xa09b, 0, 0xfffff), // CODE | ||
SegmentDescriptor::from(0xc093, 0, 0xfffff), // DATA | ||
SegmentDescriptor::from(0x808b, 0, 0xfffff), // TSS | ||
]) | ||
} | ||
``` | ||
- `Gdt` provides a default implementation ("Flat Memory Model") that can be used for setting up a vCPU for booting. | ||
- The default implementation contains all 4 segment descriptors corresponding to null, code, data, and TSS. | ||
- The default can beextended by using `try_push`. | ||
<br/><br/> | ||
|
||
## IDT | ||
This file also provides a method `write_idt_value` to write val in the IDT Offset (equals `0x520`). | ||
```rs | ||
pub fn write_idt_value<Memory: GuestMemory>(val: u64, guest_mem: &Memory) -> Result<()> { | ||
let boot_idt_addr = GuestAddress(BOOT_IDT_OFFSET); | ||
guest_mem | ||
.write_obj(val, boot_idt_addr) | ||
.map_err(Error::GuestMemory) | ||
} | ||
``` | ||
<br/><br/> | ||
|
||
### Uses in the codebase | ||
This file has been used in [this file](https://github.com/codenet/vmm-reference/blob/main/src/vm-vcpu/src/vcpu/mod.rs) in `configure_sregs` method of class `KvmCpu`. The modules are used for the following (in the specified order). | ||
```rs | ||
let gdt_table = Gdt::default(); | ||
``` | ||
- Create Default GDT | ||
```rs | ||
let code_seg = gdt_table.create_kvm_segment_for(1).unwrap(); | ||
let data_seg = gdt_table.create_kvm_segment_for(2).unwrap(); | ||
let tss_seg = gdt_table.create_kvm_segment_for(3).unwrap(); | ||
``` | ||
- Convert default segment descriptors for code, data and tss to `kvm_segment` (to be stored in `sregs`) | ||
```rs | ||
gdt_table.write_to_mem(guest_memory).map_err(Error::Gdt)?; | ||
``` | ||
- Write GDT Table into Guest Memory | ||
```rs | ||
write_idt_value(0, guest_memory).map_err(Error::Gdt)?; | ||
``` | ||
- Write IDT Offset value (default 0) | ||
|
||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.