diff options
Diffstat (limited to 'drivers/lguest/segments.c')
-rw-r--r-- | drivers/lguest/segments.c | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/drivers/lguest/segments.c b/drivers/lguest/segments.c new file mode 100644 index 000000000000..1b2cfe89dcd5 --- /dev/null +++ b/drivers/lguest/segments.c | |||
@@ -0,0 +1,125 @@ | |||
1 | #include "lg.h" | ||
2 | |||
3 | static int desc_ok(const struct desc_struct *gdt) | ||
4 | { | ||
5 | /* MBZ=0, P=1, DT=1 */ | ||
6 | return ((gdt->b & 0x00209000) == 0x00009000); | ||
7 | } | ||
8 | |||
9 | static int segment_present(const struct desc_struct *gdt) | ||
10 | { | ||
11 | return gdt->b & 0x8000; | ||
12 | } | ||
13 | |||
14 | static int ignored_gdt(unsigned int num) | ||
15 | { | ||
16 | return (num == GDT_ENTRY_TSS | ||
17 | || num == GDT_ENTRY_LGUEST_CS | ||
18 | || num == GDT_ENTRY_LGUEST_DS | ||
19 | || num == GDT_ENTRY_DOUBLEFAULT_TSS); | ||
20 | } | ||
21 | |||
22 | /* We don't allow removal of CS, DS or SS; it doesn't make sense. */ | ||
23 | static void check_segment_use(struct lguest *lg, unsigned int desc) | ||
24 | { | ||
25 | if (lg->regs->gs / 8 == desc) | ||
26 | lg->regs->gs = 0; | ||
27 | if (lg->regs->fs / 8 == desc) | ||
28 | lg->regs->fs = 0; | ||
29 | if (lg->regs->es / 8 == desc) | ||
30 | lg->regs->es = 0; | ||
31 | if (lg->regs->ds / 8 == desc | ||
32 | || lg->regs->cs / 8 == desc | ||
33 | || lg->regs->ss / 8 == desc) | ||
34 | kill_guest(lg, "Removed live GDT entry %u", desc); | ||
35 | } | ||
36 | |||
37 | static void fixup_gdt_table(struct lguest *lg, unsigned start, unsigned end) | ||
38 | { | ||
39 | unsigned int i; | ||
40 | |||
41 | for (i = start; i < end; i++) { | ||
42 | /* We never copy these ones to real gdt */ | ||
43 | if (ignored_gdt(i)) | ||
44 | continue; | ||
45 | |||
46 | /* We could fault in switch_to_guest if they are using | ||
47 | * a removed segment. */ | ||
48 | if (!segment_present(&lg->gdt[i])) { | ||
49 | check_segment_use(lg, i); | ||
50 | continue; | ||
51 | } | ||
52 | |||
53 | if (!desc_ok(&lg->gdt[i])) | ||
54 | kill_guest(lg, "Bad GDT descriptor %i", i); | ||
55 | |||
56 | /* DPL 0 presumably means "for use by guest". */ | ||
57 | if ((lg->gdt[i].b & 0x00006000) == 0) | ||
58 | lg->gdt[i].b |= (GUEST_PL << 13); | ||
59 | |||
60 | /* Set accessed bit, since gdt isn't writable. */ | ||
61 | lg->gdt[i].b |= 0x00000100; | ||
62 | } | ||
63 | } | ||
64 | |||
65 | void setup_default_gdt_entries(struct lguest_ro_state *state) | ||
66 | { | ||
67 | struct desc_struct *gdt = state->guest_gdt; | ||
68 | unsigned long tss = (unsigned long)&state->guest_tss; | ||
69 | |||
70 | /* Hypervisor segments. */ | ||
71 | gdt[GDT_ENTRY_LGUEST_CS] = FULL_EXEC_SEGMENT; | ||
72 | gdt[GDT_ENTRY_LGUEST_DS] = FULL_SEGMENT; | ||
73 | |||
74 | /* This is the one which we *cannot* copy from guest, since tss | ||
75 | is depended on this lguest_ro_state, ie. this cpu. */ | ||
76 | gdt[GDT_ENTRY_TSS].a = 0x00000067 | (tss << 16); | ||
77 | gdt[GDT_ENTRY_TSS].b = 0x00008900 | (tss & 0xFF000000) | ||
78 | | ((tss >> 16) & 0x000000FF); | ||
79 | } | ||
80 | |||
81 | void setup_guest_gdt(struct lguest *lg) | ||
82 | { | ||
83 | lg->gdt[GDT_ENTRY_KERNEL_CS] = FULL_EXEC_SEGMENT; | ||
84 | lg->gdt[GDT_ENTRY_KERNEL_DS] = FULL_SEGMENT; | ||
85 | lg->gdt[GDT_ENTRY_KERNEL_CS].b |= (GUEST_PL << 13); | ||
86 | lg->gdt[GDT_ENTRY_KERNEL_DS].b |= (GUEST_PL << 13); | ||
87 | } | ||
88 | |||
89 | /* This is a fast version for the common case where only the three TLS entries | ||
90 | * have changed. */ | ||
91 | void copy_gdt_tls(const struct lguest *lg, struct desc_struct *gdt) | ||
92 | { | ||
93 | unsigned int i; | ||
94 | |||
95 | for (i = GDT_ENTRY_TLS_MIN; i <= GDT_ENTRY_TLS_MAX; i++) | ||
96 | gdt[i] = lg->gdt[i]; | ||
97 | } | ||
98 | |||
99 | void copy_gdt(const struct lguest *lg, struct desc_struct *gdt) | ||
100 | { | ||
101 | unsigned int i; | ||
102 | |||
103 | for (i = 0; i < GDT_ENTRIES; i++) | ||
104 | if (!ignored_gdt(i)) | ||
105 | gdt[i] = lg->gdt[i]; | ||
106 | } | ||
107 | |||
108 | void load_guest_gdt(struct lguest *lg, unsigned long table, u32 num) | ||
109 | { | ||
110 | if (num > ARRAY_SIZE(lg->gdt)) | ||
111 | kill_guest(lg, "too many gdt entries %i", num); | ||
112 | |||
113 | lgread(lg, lg->gdt, table, num * sizeof(lg->gdt[0])); | ||
114 | fixup_gdt_table(lg, 0, ARRAY_SIZE(lg->gdt)); | ||
115 | lg->changed |= CHANGED_GDT; | ||
116 | } | ||
117 | |||
118 | void guest_load_tls(struct lguest *lg, unsigned long gtls) | ||
119 | { | ||
120 | struct desc_struct *tls = &lg->gdt[GDT_ENTRY_TLS_MIN]; | ||
121 | |||
122 | lgread(lg, tls, gtls, sizeof(*tls)*GDT_ENTRY_TLS_ENTRIES); | ||
123 | fixup_gdt_table(lg, GDT_ENTRY_TLS_MIN, GDT_ENTRY_TLS_MAX+1); | ||
124 | lg->changed |= CHANGED_GDT_TLS; | ||
125 | } | ||