1
- //! This module defines and manages the descriptor tables (GDT and IDT) for both the host and guest.
2
- //! It provides utilities to capture, initialize, and manage these tables.
3
-
4
1
use {
5
- crate :: {
6
- error:: HypervisorError ,
2
+ x86:: {
3
+ dtables:: DescriptorTablePointer ,
4
+ segmentation:: {
5
+ cs, BuildDescriptor , CodeSegmentType , Descriptor , DescriptorBuilder , GateDescriptorBuilder ,
6
+ SegmentDescriptorBuilder , SegmentSelector ,
7
+ } ,
7
8
} ,
8
9
alloc:: { boxed:: Box , vec:: Vec } ,
9
- x86 :: dtables :: DescriptorTablePointer ,
10
+ crate :: intel :: support :: sgdt ,
10
11
} ;
11
- use crate :: intel:: support:: { sgdt, sidt} ;
12
-
13
- /// Represents the descriptor tables (GDT and IDT) for the host.
14
- /// Contains the GDT and IDT along with their respective register pointers.
15
- #[ repr( C , align( 4096 ) ) ]
16
- pub struct DescriptorTables {
17
- /// Global Descriptor Table (GDT) for the host.
18
- /// Reference: Intel® 64 and IA-32 Architectures Software Developer's Manual: 3.5.1 Segment Descriptor Tables
19
- pub global_descriptor_table : Vec < u64 > ,
20
-
21
- /// GDTR holds the address and size of the GDT.
22
- /// Reference: Intel® 64 and IA-32 Architectures Software Developer's Manual: 2.4.1 Global Descriptor Table Register (GDTR)
23
- pub gdtr : DescriptorTablePointer < u64 > ,
24
12
25
- /// Interrupt Descriptor Table (IDT) for the host.
26
- /// Reference: Intel® 64 and IA-32 Architectures Software Developer's Manual: 6.10 INTERRUPT DESCRIPTOR TABLE (IDT)
27
- pub interrupt_descriptor_table : Vec < u64 > ,
28
-
29
- /// IDTR holds the address and size of the IDT.
30
- /// Reference: Intel® 64 and IA-32 Architectures Software Developer's Manual: 2.4.3 IDTR Interrupt Descriptor Table Register
31
- pub idtr : DescriptorTablePointer < u64 > ,
13
+ // UEFI does not set TSS in the GDT. This is incompatible to be both as VM and
14
+ // hypervisor states. This struct supports creating a new GDT that does contain
15
+ // the TSS.
16
+ //
17
+ // See: 27.2.3 Checks on Host Segment and Descriptor-Table Registers
18
+ // See: 27.3.1.2 Checks on Guest Segment Registers
19
+ pub struct Descriptors {
20
+ gdt : Vec < u64 > ,
21
+ pub gdtr : DescriptorTablePointer < u64 > ,
22
+ pub cs : SegmentSelector ,
23
+ pub tr : SegmentSelector ,
24
+ pub tss : TaskStateSegment ,
32
25
}
26
+ impl Default for Descriptors {
27
+ fn default ( ) -> Self {
28
+ Self {
29
+ gdt : Vec :: new ( ) ,
30
+ gdtr : DescriptorTablePointer :: < u64 > :: default ( ) ,
31
+ cs : SegmentSelector :: from_raw ( 0 ) ,
32
+ tr : SegmentSelector :: from_raw ( 0 ) ,
33
+ tss : TaskStateSegment :: default ( ) ,
34
+ }
35
+ }
36
+ }
37
+ impl Descriptors {
38
+ /// Creates a new GDT with TSS based on the current GDT.
39
+ pub fn new_from_current ( ) -> Self {
40
+ log:: debug!( "Creating a new GDT with TSS for guest" ) ;
33
41
34
- impl DescriptorTables {
35
- /// Captures the currently loaded GDT and IDT for the guest.
36
- pub fn initialize_for_guest (
37
- descriptor_tables : & mut Box < DescriptorTables > ,
38
- ) -> Result < ( ) , HypervisorError > {
39
- log:: trace!( "Capturing current Global Descriptor Table (GDT) and Interrupt Descriptor Table (IDT) for guest" ) ;
40
-
41
- // Capture the current GDT and IDT.
42
- descriptor_tables. gdtr = sgdt ( ) ;
43
- descriptor_tables. idtr = sidt ( ) ;
44
-
45
- // Note: We don't need to create new tables for the guest;
46
- // we just capture the current ones.
42
+ // Get the current GDT.
43
+ let current_gdtr = sgdt ( ) ;
44
+ let current_gdt = unsafe {
45
+ core:: slice:: from_raw_parts (
46
+ current_gdtr. base . cast :: < u64 > ( ) ,
47
+ usize:: from ( current_gdtr. limit + 1 ) / 8 ,
48
+ )
49
+ } ;
47
50
48
- log:: trace!( "Captured GDT and IDT for guest successfully!" ) ;
51
+ // Copy the current GDT.
52
+ let mut descriptors = Self {
53
+ gdt : current_gdt. to_vec ( ) ,
54
+ ..Default :: default ( )
55
+ } ;
49
56
50
- Ok ( ( ) )
51
- }
57
+ // Append the TSS descriptor. Push extra 0 as it is 16 bytes.
58
+ // See: 3.5.2 Segment Descriptor Tables in IA-32e Mode
59
+ let tr_index = descriptors. gdt . len ( ) as u16 ;
60
+ descriptors
61
+ . gdt
62
+ . push ( Self :: task_segment_descriptor ( & descriptors. tss ) . as_u64 ( ) ) ;
63
+ descriptors. gdt . push ( 0 ) ;
52
64
53
- /// Initializes and returns the descriptor tables (GDT and IDT) for the host.
54
- pub fn initialize_for_host (
55
- descriptor_tables : & mut Box < DescriptorTables > ,
56
- ) -> Result < ( ) , HypervisorError > {
57
- log:: trace!( "Initializing descriptor tables for host" ) ;
65
+ descriptors. gdtr = DescriptorTablePointer :: new_from_slice ( & descriptors. gdt ) ;
66
+ descriptors. cs = cs ( ) ;
67
+ descriptors. tr = SegmentSelector :: new ( tr_index, x86:: Ring :: Ring0 ) ;
58
68
59
- descriptor_tables. copy_current_gdt ( ) ;
60
- descriptor_tables. copy_current_idt ( ) ;
69
+ log:: debug!( "New GDT with TSS created for guest successfully!" ) ;
61
70
62
- log:: trace!( "Initialized descriptor tables for host" ) ;
63
- Ok ( ( ) )
71
+ descriptors
64
72
}
65
73
66
- /// Copies the current GDT.
67
- fn copy_current_gdt ( & mut self ) {
68
- log:: trace !( "Copying current GDT" ) ;
74
+ /// Creates a new GDT with TSS from scratch for the host .
75
+ pub fn new_for_host ( ) -> Self {
76
+ log:: debug !( "Creating a new GDT with TSS for host " ) ;
69
77
70
- // Get the current GDTR
71
- let current_gdtr = sgdt ( ) ;
78
+ let mut descriptors = Self :: default ( ) ;
72
79
73
- // Create a slice from the current GDT entries.
74
- let current_gdt = Self :: from_pointer ( & current_gdtr) ;
80
+ descriptors. gdt . push ( 0 ) ;
81
+ descriptors
82
+ . gdt
83
+ . push ( Self :: code_segment_descriptor ( ) . as_u64 ( ) ) ;
84
+ descriptors
85
+ . gdt
86
+ . push ( Self :: task_segment_descriptor ( & descriptors. tss ) . as_u64 ( ) ) ;
87
+ descriptors. gdt . push ( 0 ) ;
75
88
76
- // Create a new GDT from the slice.
77
- let new_gdt = current_gdt. to_vec ( ) ;
89
+ descriptors. gdtr = DescriptorTablePointer :: new_from_slice ( & descriptors. gdt ) ;
90
+ descriptors. cs = SegmentSelector :: new ( 1 , x86:: Ring :: Ring0 ) ;
91
+ descriptors. tr = SegmentSelector :: new ( 2 , x86:: Ring :: Ring0 ) ;
78
92
79
- // Create a new GDTR from the new GDT.
80
- let new_gdtr = DescriptorTablePointer :: new_from_slice ( new_gdt. as_slice ( ) ) ;
93
+ log:: debug!( "New GDT with TSS created for host successfully!" ) ;
81
94
82
- // Store the new GDT in the DescriptorTables structure
83
- self . global_descriptor_table = new_gdt;
84
- self . gdtr = new_gdtr;
85
- log:: trace!( "Copied current GDT" ) ;
95
+ descriptors
86
96
}
87
97
88
- /// Copies the current IDT.
89
- fn copy_current_idt ( & mut self ) {
90
- log:: trace!( "Copying current IDT" ) ;
91
-
92
- // Get the current IDTR
93
- let current_idtr = sidt ( ) ;
94
-
95
- // Create a slice from the current IDT entries.
96
- let current_idt = Self :: from_pointer ( & current_idtr) ;
97
-
98
- // Create a new IDT from the slice.
99
- let new_idt = current_idt. to_vec ( ) ;
100
-
101
- // Create a new IDTR from the new IDT.
102
- let new_idtr = DescriptorTablePointer :: new_from_slice ( new_idt. as_slice ( ) ) ;
98
+ /// Builds a segment descriptor from the task state segment.
99
+ fn task_segment_descriptor ( tss : & TaskStateSegment ) -> Descriptor {
100
+ <DescriptorBuilder as GateDescriptorBuilder < u32 > >:: tss_descriptor ( tss. base , tss. limit , true )
101
+ . present ( )
102
+ . dpl ( x86:: Ring :: Ring0 )
103
+ . finish ( )
104
+ }
103
105
104
- // Store the new IDT in the DescriptorTables structure
105
- self . interrupt_descriptor_table = new_idt;
106
- self . idtr = new_idtr; // Use the same IDTR as it points to the correct base and limit
107
- log:: trace!( "Copied current IDT" ) ;
106
+ fn code_segment_descriptor ( ) -> Descriptor {
107
+ DescriptorBuilder :: code_descriptor ( 0 , u32:: MAX , CodeSegmentType :: ExecuteAccessed )
108
+ . present ( )
109
+ . dpl ( x86:: Ring :: Ring0 )
110
+ . limit_granularity_4kb ( )
111
+ . l ( )
112
+ . finish ( )
108
113
}
109
114
110
115
/// Gets the table as a slice from the pointer.
@@ -116,4 +121,30 @@ impl DescriptorTables {
116
121
)
117
122
}
118
123
}
119
- }
124
+ }
125
+
126
+ #[ derive( derivative:: Derivative ) ]
127
+ #[ derivative( Debug ) ]
128
+ pub struct TaskStateSegment {
129
+ pub base : u64 ,
130
+ pub limit : u64 ,
131
+ pub ar : u32 ,
132
+ #[ allow( dead_code) ]
133
+ #[ derivative( Debug = "ignore" ) ]
134
+ segment : Box < TaskStateSegmentRaw > ,
135
+ }
136
+ impl Default for TaskStateSegment {
137
+ fn default ( ) -> Self {
138
+ let segment = Box :: new ( TaskStateSegmentRaw ( [ 0 ; 104 ] ) ) ;
139
+ Self {
140
+ base : segment. as_ref ( ) as * const _ as u64 ,
141
+ limit : core:: mem:: size_of_val ( segment. as_ref ( ) ) as u64 - 1 ,
142
+ ar : 0x8b00 ,
143
+ segment,
144
+ }
145
+ }
146
+ }
147
+
148
+ /// See: Figure 8-11. 64-Bit TSS Format
149
+
150
+ struct TaskStateSegmentRaw ( [ u8 ; 104 ] ) ;
0 commit comments