xref: /cloud-hypervisor/hypervisor/src/arch/x86/gdt.rs (revision b440cb7d2330770cd415b63544a371d4caa2db3a)
1 // Copyright © 2020, Oracle and/or its affiliates.
2 //
3 // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 // SPDX-License-Identifier: Apache-2.0
5 //
6 // Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
7 // Use of this source code is governed by a BSD-style license that can be
8 // found in the LICENSE-BSD-3-Clause file.
9 
10 // For GDT details see arch/x86/include/asm/segment.h
11 use crate::arch::x86::SegmentRegister;
12 
13 /// Constructor for a conventional segment GDT (or LDT) entry. Derived from the kernel's segment.h.
14 pub fn gdt_entry(flags: u16, base: u32, limit: u32) -> u64 {
15     (((base as u64) & 0xff000000u64) << (56 - 24))
16         | (((flags as u64) & 0x0000f0ffu64) << 40)
17         | (((limit as u64) & 0x000f0000u64) << (48 - 16))
18         | (((base as u64) & 0x00ffffffu64) << 16)
19         | ((limit as u64) & 0x0000ffffu64)
20 }
21 
22 fn get_base(entry: u64) -> u64 {
23     (((entry) & 0xFF00000000000000) >> 32)
24         | (((entry) & 0x000000FF00000000) >> 16)
25         | (((entry) & 0x00000000FFFF0000) >> 16)
26 }
27 
28 // Extract the segment limit from the GDT segment descriptor.
29 //
30 // In a segment descriptor, the limit field is 20 bits, so it can directly describe
31 // a range from 0 to 0xFFFFF (1MByte). When G flag is set (4-KByte page granularity) it
32 // scales the value in the limit field by a factor of 2^12 (4Kbytes), making the effective
33 // limit range from 0xFFF (4 KBytes) to 0xFFFF_FFFF (4 GBytes).
34 //
35 // However, the limit field in the VMCS definition is a 32 bit field, and the limit value is not
36 // automatically scaled using the G flag. This means that for a desired range of 4GB for a
37 // given segment, its limit must be specified as 0xFFFF_FFFF. Therefore the method of obtaining
38 // the limit from the GDT entry is not sufficient, since it only provides 20 bits when 32 bits
39 // are necessary. Fortunately, we can check if the G flag is set when extracting the limit since
40 // the full GDT entry is passed as an argument, and perform the scaling of the limit value to
41 // return the full 32 bit value.
42 //
43 // The scaling mentioned above is required when using PVH boot, since the guest boots in protected
44 // (32-bit) mode and must be able to access the entire 32-bit address space. It does not cause issues
45 // for the case of direct boot to 64-bit (long) mode, since in 64-bit mode the processor does not
46 // perform runtime limit checking on code or data segments.
47 fn get_limit(entry: u64) -> u32 {
48     let limit: u32 =
49         ((((entry) & 0x000F000000000000) >> 32) | ((entry) & 0x000000000000FFFF)) as u32;
50 
51     // Perform manual limit scaling if G flag is set
52     match get_g(entry) {
53         0 => limit,
54         _ => ((limit << 12) | 0xFFF), // G flag is either 0 or 1
55     }
56 }
57 
58 fn get_g(entry: u64) -> u8 {
59     ((entry & 0x0080000000000000) >> 55) as u8
60 }
61 
62 fn get_db(entry: u64) -> u8 {
63     ((entry & 0x0040000000000000) >> 54) as u8
64 }
65 
66 fn get_l(entry: u64) -> u8 {
67     ((entry & 0x0020000000000000) >> 53) as u8
68 }
69 
70 fn get_avl(entry: u64) -> u8 {
71     ((entry & 0x0010000000000000) >> 52) as u8
72 }
73 
74 fn get_p(entry: u64) -> u8 {
75     ((entry & 0x0000800000000000) >> 47) as u8
76 }
77 
78 fn get_dpl(entry: u64) -> u8 {
79     ((entry & 0x0000600000000000) >> 45) as u8
80 }
81 
82 fn get_s(entry: u64) -> u8 {
83     ((entry & 0x0000100000000000) >> 44) as u8
84 }
85 
86 fn get_type(entry: u64) -> u8 {
87     ((entry & 0x00000F0000000000) >> 40) as u8
88 }
89 
90 /// Automatically build the struct for SET_SREGS from the kernel bit fields.
91 ///
92 /// # Arguments
93 ///
94 /// * `entry` - The gdt entry.
95 /// * `table_index` - Index of the entry in the gdt table.
96 pub fn segment_from_gdt(entry: u64, table_index: u8) -> SegmentRegister {
97     SegmentRegister {
98         base: get_base(entry),
99         limit: get_limit(entry),
100         selector: (table_index * 8) as u16,
101         type_: get_type(entry),
102         present: get_p(entry),
103         dpl: get_dpl(entry),
104         db: get_db(entry),
105         s: get_s(entry),
106         l: get_l(entry),
107         g: get_g(entry),
108         avl: get_avl(entry),
109         unusable: match get_p(entry) {
110             0 => 1,
111             _ => 0,
112         },
113     }
114 }
115 
116 #[cfg(test)]
117 mod tests {
118     use super::*;
119 
120     #[test]
121     fn field_parse() {
122         let gdt = gdt_entry(0xA09B, 0x100000, 0xfffff);
123         let seg = segment_from_gdt(gdt, 0);
124         // 0xA09B
125         // 'A'
126         assert_eq!(0x1, seg.g);
127         assert_eq!(0x0, seg.db);
128         assert_eq!(0x1, seg.l);
129         assert_eq!(0x0, seg.avl);
130         // '9'
131         assert_eq!(0x1, seg.present);
132         assert_eq!(0x0, seg.dpl);
133         assert_eq!(0x1, seg.s);
134         // 'B'
135         assert_eq!(0xB, seg.type_);
136         // base and limit
137         assert_eq!(0x100000, seg.base);
138         assert_eq!(0xffffffff, seg.limit);
139         assert_eq!(0x0, seg.unusable);
140     }
141 }
142