lguest: documentation IV: Launcher
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / lguest / segments.c
CommitLineData
f938d2c8
RR
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. :*/
d7e28ffe
RR
12#include "lg.h"
13
14static int desc_ok(const struct desc_struct *gdt)
15{
16 /* MBZ=0, P=1, DT=1 */
17 return ((gdt->b & 0x00209000) == 0x00009000);
18}
19
20static int segment_present(const struct desc_struct *gdt)
21{
22 return gdt->b & 0x8000;
23}
24
25static 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. */
34static 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
48static 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
76void 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
92void 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. */
102void 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
110void 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
119void 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
129void 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}