aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/lguest/segments.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/lguest/segments.c')
-rw-r--r--drivers/lguest/segments.c125
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
3static int desc_ok(const struct desc_struct *gdt)
4{
5 /* MBZ=0, P=1, DT=1 */
6 return ((gdt->b & 0x00209000) == 0x00009000);
7}
8
9static int segment_present(const struct desc_struct *gdt)
10{
11 return gdt->b & 0x8000;
12}
13
14static 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. */
23static 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
37static 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
65void 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
81void 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. */
91void 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
99void 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
108void 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
118void 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}