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