1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// Copyright 2017 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// For GDT details see arch/x86/include/asm/segment.h

use hypervisor::Segment;

/// Constructor for a conventional segment GDT (or LDT) entry. Derived from the kernel's segment.h.
pub fn gdt_entry(flags: u16, base: u32, limit: u32) -> u64 {
    (((base as u64) & 0xff000000u64) << (56 - 24))
        | (((flags as u64) & 0x0000f0ffu64) << 40)
        | (((limit as u64) & 0x000f0000u64) << (48 - 16))
        | (((base as u64) & 0x00ffffffu64) << 16)
        | ((limit as u64) & 0x0000ffffu64)
}

fn get_base(entry: u64) -> u64 {
    (((entry) & 0xFF00000000000000) >> 32)
        | (((entry) & 0x000000FF00000000) >> 16)
        | (((entry) & 0x00000000FFFF0000) >> 16)
}

fn get_limit(entry: u64) -> u32 {
    ((((entry) & 0x000F000000000000) >> 32) | ((entry) & 0x000000000000FFFF)) as u32
}

fn get_g(entry: u64) -> u8 {
    ((entry & 0x0080000000000000) >> 55) as u8
}

fn get_db(entry: u64) -> u8 {
    ((entry & 0x0040000000000000) >> 54) as u8
}

fn get_l(entry: u64) -> u8 {
    ((entry & 0x0020000000000000) >> 53) as u8
}

fn get_avl(entry: u64) -> u8 {
    ((entry & 0x0010000000000000) >> 52) as u8
}

fn get_p(entry: u64) -> u8 {
    ((entry & 0x0000800000000000) >> 47) as u8
}

fn get_dpl(entry: u64) -> u8 {
    ((entry & 0x0000600000000000) >> 45) as u8
}

fn get_s(entry: u64) -> u8 {
    ((entry & 0x0000100000000000) >> 44) as u8
}

fn get_type(entry: u64) -> u8 {
    ((entry & 0x00000F0000000000) >> 40) as u8
}

/// Automatically build the hypervisor Segment struct for set_sregs from the kernel bit fields.
///
/// # Arguments
///
/// * `entry` - The gdt entry.
/// * `table_index` - Index of the entry in the gdt table.
pub fn segment_from_gdt(entry: u64, table_index: u8) -> Segment {
    let g = get_g(entry);
    let limit = get_limit(entry);
    let limit_bytes = if g == 0 {
        // 1-byte granularity
        limit
    } else {
        // 4096-byte granularity
        (limit * 4096) + 4095
    };

    Segment {
        base: get_base(entry),
        limit_bytes,
        selector: (table_index * 8) as u16,
        type_: get_type(entry),
        present: get_p(entry),
        dpl: get_dpl(entry),
        db: get_db(entry),
        s: get_s(entry),
        l: get_l(entry),
        g,
        avl: get_avl(entry),
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn field_parse() {
        let gdt = gdt_entry(0xA09B, 0x100000, 0xfffff);
        let seg = segment_from_gdt(gdt, 0);
        // 0xA09B
        // 'A'
        assert_eq!(0x1, seg.g);
        assert_eq!(0x0, seg.db);
        assert_eq!(0x1, seg.l);
        assert_eq!(0x0, seg.avl);
        // '9'
        assert_eq!(0x1, seg.present);
        assert_eq!(0x0, seg.dpl);
        assert_eq!(0x1, seg.s);
        // 'B'
        assert_eq!(0xB, seg.type_);
        // base and limit
        assert_eq!(0x100000, seg.base);
        assert_eq!(0xffffffff, seg.limit_bytes);
    }
}