]> Pileus Git - ~andy/linux/blob - drivers/lguest/segments.c
lguest: documentation I: Preparation
[~andy/linux] / drivers / lguest / segments.c
1 /*P:600 The x86 architecture has segments, which involve a table of descriptors
2  * which can be used to do funky things with virtual address interpretation.
3  * We originally used to use segments so the Guest couldn't alter the
4  * Guest<->Host Switcher, and then we had to trim Guest segments, and restore
5  * for userspace per-thread segments, but trim again for on userspace->kernel
6  * transitions...  This nightmarish creation was contained within this file,
7  * where we knew not to tread without heavy armament and a change of underwear.
8  *
9  * In these modern times, the segment handling code consists of simple sanity
10  * checks, and the worst you'll experience reading this code is butterfly-rash
11  * from frolicking through its parklike serenity. :*/
12 #include "lg.h"
13
14 static int desc_ok(const struct desc_struct *gdt)
15 {
16         /* MBZ=0, P=1, DT=1  */
17         return ((gdt->b & 0x00209000) == 0x00009000);
18 }
19
20 static int segment_present(const struct desc_struct *gdt)
21 {
22         return gdt->b & 0x8000;
23 }
24
25 static int ignored_gdt(unsigned int num)
26 {
27         return (num == GDT_ENTRY_TSS
28                 || num == GDT_ENTRY_LGUEST_CS
29                 || num == GDT_ENTRY_LGUEST_DS
30                 || num == GDT_ENTRY_DOUBLEFAULT_TSS);
31 }
32
33 /* We don't allow removal of CS, DS or SS; it doesn't make sense. */
34 static void check_segment_use(struct lguest *lg, unsigned int desc)
35 {
36         if (lg->regs->gs / 8 == desc)
37                 lg->regs->gs = 0;
38         if (lg->regs->fs / 8 == desc)
39                 lg->regs->fs = 0;
40         if (lg->regs->es / 8 == desc)
41                 lg->regs->es = 0;
42         if (lg->regs->ds / 8 == desc
43             || lg->regs->cs / 8 == desc
44             || lg->regs->ss / 8 == desc)
45                 kill_guest(lg, "Removed live GDT entry %u", desc);
46 }
47
48 static void fixup_gdt_table(struct lguest *lg, unsigned start, unsigned end)
49 {
50         unsigned int i;
51
52         for (i = start; i < end; i++) {
53                 /* We never copy these ones to real gdt */
54                 if (ignored_gdt(i))
55                         continue;
56
57                 /* We could fault in switch_to_guest if they are using
58                  * a removed segment. */
59                 if (!segment_present(&lg->gdt[i])) {
60                         check_segment_use(lg, i);
61                         continue;
62                 }
63
64                 if (!desc_ok(&lg->gdt[i]))
65                         kill_guest(lg, "Bad GDT descriptor %i", i);
66
67                 /* DPL 0 presumably means "for use by guest". */
68                 if ((lg->gdt[i].b & 0x00006000) == 0)
69                         lg->gdt[i].b |= (GUEST_PL << 13);
70
71                 /* Set accessed bit, since gdt isn't writable. */
72                 lg->gdt[i].b |= 0x00000100;
73         }
74 }
75
76 void setup_default_gdt_entries(struct lguest_ro_state *state)
77 {
78         struct desc_struct *gdt = state->guest_gdt;
79         unsigned long tss = (unsigned long)&state->guest_tss;
80
81         /* Hypervisor segments. */
82         gdt[GDT_ENTRY_LGUEST_CS] = FULL_EXEC_SEGMENT;
83         gdt[GDT_ENTRY_LGUEST_DS] = FULL_SEGMENT;
84
85         /* This is the one which we *cannot* copy from guest, since tss
86            is depended on this lguest_ro_state, ie. this cpu. */
87         gdt[GDT_ENTRY_TSS].a = 0x00000067 | (tss << 16);
88         gdt[GDT_ENTRY_TSS].b = 0x00008900 | (tss & 0xFF000000)
89                 | ((tss >> 16) & 0x000000FF);
90 }
91
92 void setup_guest_gdt(struct lguest *lg)
93 {
94         lg->gdt[GDT_ENTRY_KERNEL_CS] = FULL_EXEC_SEGMENT;
95         lg->gdt[GDT_ENTRY_KERNEL_DS] = FULL_SEGMENT;
96         lg->gdt[GDT_ENTRY_KERNEL_CS].b |= (GUEST_PL << 13);
97         lg->gdt[GDT_ENTRY_KERNEL_DS].b |= (GUEST_PL << 13);
98 }
99
100 /* This is a fast version for the common case where only the three TLS entries
101  * have changed. */
102 void copy_gdt_tls(const struct lguest *lg, struct desc_struct *gdt)
103 {
104         unsigned int i;
105
106         for (i = GDT_ENTRY_TLS_MIN; i <= GDT_ENTRY_TLS_MAX; i++)
107                 gdt[i] = lg->gdt[i];
108 }
109
110 void copy_gdt(const struct lguest *lg, struct desc_struct *gdt)
111 {
112         unsigned int i;
113
114         for (i = 0; i < GDT_ENTRIES; i++)
115                 if (!ignored_gdt(i))
116                         gdt[i] = lg->gdt[i];
117 }
118
119 void load_guest_gdt(struct lguest *lg, unsigned long table, u32 num)
120 {
121         if (num > ARRAY_SIZE(lg->gdt))
122                 kill_guest(lg, "too many gdt entries %i", num);
123
124         lgread(lg, lg->gdt, table, num * sizeof(lg->gdt[0]));
125         fixup_gdt_table(lg, 0, ARRAY_SIZE(lg->gdt));
126         lg->changed |= CHANGED_GDT;
127 }
128
129 void guest_load_tls(struct lguest *lg, unsigned long gtls)
130 {
131         struct desc_struct *tls = &lg->gdt[GDT_ENTRY_TLS_MIN];
132
133         lgread(lg, tls, gtls, sizeof(*tls)*GDT_ENTRY_TLS_ENTRIES);
134         fixup_gdt_table(lg, GDT_ENTRY_TLS_MIN, GDT_ENTRY_TLS_MAX+1);
135         lg->changed |= CHANGED_GDT_TLS;
136 }