source: trunk/packages/xen-common/xen-common/xen/arch/x86/x86_32/seg_fixup.c @ 34

Last change on this file since 34 was 34, checked in by hartmans, 17 years ago

Add xen and xen-common

File size: 13.1 KB
Line 
1/******************************************************************************
2 * arch/x86/x86_32/seg_fixup.c
3 *
4 * Support for -ve accesses to pseudo-4GB segments.
5 *
6 * Copyright (c) 2004, K A Fraser
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21 */
22
23#include <xen/config.h>
24#include <xen/init.h>
25#include <xen/sched.h>
26#include <xen/lib.h>
27#include <xen/errno.h>
28#include <xen/mm.h>
29#include <xen/perfc.h>
30#include <asm/current.h>
31#include <asm/processor.h>
32#include <asm/regs.h>
33#include <asm/x86_emulate.h>
34
35/* General instruction properties. */
36#define INSN_SUFFIX_BYTES (7)
37#define OPCODE_BYTE       (1<<4) 
38#define HAS_MODRM         (1<<5)
39
40/* Short forms for the table. */
41#define X  0 /* invalid for some random reason */
42#define O  OPCODE_BYTE
43#define M  HAS_MODRM
44
45static unsigned char insn_decode[256] = {
46    /* 0x00 - 0x0F */
47    O|M, O|M, O|M, O|M, X, X, X, X,
48    O|M, O|M, O|M, O|M, X, X, X, X,
49    /* 0x10 - 0x1F */
50    O|M, O|M, O|M, O|M, X, X, X, X,
51    O|M, O|M, O|M, O|M, X, X, X, X,
52    /* 0x20 - 0x2F */
53    O|M, O|M, O|M, O|M, X, X, X, X,
54    O|M, O|M, O|M, O|M, X, X, X, X,
55    /* 0x30 - 0x3F */
56    O|M, O|M, O|M, O|M, X, X, X, X,
57    O|M, O|M, O|M, O|M, X, X, X, X,
58    /* 0x40 - 0x4F */
59    X, X, X, X, X, X, X, X,
60    X, X, X, X, X, X, X, X,
61    /* 0x50 - 0x5F */
62    X, X, X, X, X, X, X, X,
63    X, X, X, X, X, X, X, X,
64    /* 0x60 - 0x6F */
65    X, X, X, X, X, X, X, X,
66    X, O|M|4, X, O|M|1, X, X, X, X,
67    /* 0x70 - 0x7F */
68    X, X, X, X, X, X, X, X,
69    X, X, X, X, X, X, X, X,
70    /* 0x80 - 0x8F */
71    O|M|1, O|M|4, O|M|1, O|M|1, O|M, O|M, O|M, O|M,
72    O|M, O|M, O|M, O|M, O|M, O|M, O|M, X,
73    /* 0x90 - 0x9F */
74    X, X, X, X, X, X, X, X,
75    X, X, X, X, X, X, X, X,
76    /* 0xA0 - 0xAF */
77    O|4, O|4, O|4, O|4, X, X, X, X,
78    X, X, X, X, X, X, X, X,
79    /* 0xB0 - 0xBF */
80    X, X, X, X, X, X, X, X,
81    X, X, X, X, X, X, X, X,
82    /* 0xC0 - 0xCF */
83    O|M|1, O|M|1, X, X, X, X, O|M|1, O|M|4,
84    X, X, X, X, X, X, X, X,
85    /* 0xD0 - 0xDF */
86    O|M, O|M, O|M, O|M, X, X, X, X,
87    X, X, X, X, X, X, X, X,
88    /* 0xE0 - 0xEF */
89    X, X, X, X, X, X, X, X,
90    X, X, X, X, X, X, X, X,
91    /* 0xF0 - 0xFF */
92    X, X, X, X, X, X, X, X,
93    X, X, X, X, X, X, O|M, O|M
94};
95
96/*
97 * Obtain the base and limit associated with the given segment selector.
98 * The selector must identify a 32-bit code or data segment. Any segment that
99 * appears to be truncated to not overlap with Xen is assumed to be a truncated
100 * 4GB segment, and the returned limit reflects this.
101 *  @seg   (IN) : Segment selector to decode.
102 *  @base  (OUT): Decoded linear base address.
103 *  @limit (OUT): Decoded segment limit, in bytes. 0 == unlimited (4GB).
104 */
105int get_baselimit(u16 seg, unsigned long *base, unsigned long *limit)
106{
107    struct vcpu *d = current;
108    unsigned long *table, a, b;
109    int            ldt = !!(seg & 4);
110    int            idx = (seg >> 3) & 8191;
111
112    /* Get base and check limit. */
113    if ( ldt )
114    {
115        table = (unsigned long *)LDT_VIRT_START(d);
116        if ( idx >= d->arch.guest_context.ldt_ents )
117            goto fail;
118    }
119    else /* gdt */
120    {
121        table = (unsigned long *)GDT_VIRT_START(d);
122        if ( idx >= d->arch.guest_context.gdt_ents )
123            goto fail;
124    }
125
126    /* Grab the segment descriptor. */
127    if ( __get_user(a, &table[2*idx+0]) ||
128         __get_user(b, &table[2*idx+1]) )
129        goto fail; /* Barking up the wrong tree. Decode needs a page fault.*/
130
131    /* We only parse 32-bit code and data segments. */
132    if ( (b & (_SEGMENT_P|_SEGMENT_S|_SEGMENT_DB)) != 
133         (_SEGMENT_P|_SEGMENT_S|_SEGMENT_DB) )
134        goto fail;
135
136    /* Decode base and limit. */
137    *base  = (b&(0xff<<24)) | ((b&0xff)<<16) | (a>>16);
138    *limit = ((b & 0xf0000) | (a & 0x0ffff)) + 1;
139    if ( (b & _SEGMENT_G) )
140        *limit <<= 12;
141
142    /*
143     * Anything that looks like a truncated segment we assume ought really
144     * to be a 4GB segment. DANGER!
145     */
146    if ( (GUEST_SEGMENT_MAX_ADDR - (*base + *limit)) < PAGE_SIZE )
147        *limit = 0;
148
149    return 1;
150
151 fail:
152    return 0;
153}
154
155/* Turn a segment+offset into a linear address. */
156int linearise_address(u16 seg, unsigned long off, unsigned long *linear)
157{
158    unsigned long base, limit;
159
160    if ( !get_baselimit(seg, &base, &limit) )
161        return 0;
162
163    if ( off > (limit-1) )
164        return 0;
165
166    *linear = base + off;
167
168    return 1;
169}
170
171int fixup_seg(u16 seg, unsigned long offset)
172{
173    struct vcpu *d = current;
174    unsigned long *table, a, b, base, limit;
175    int            ldt = !!(seg & 4);
176    int            idx = (seg >> 3) & 8191;
177
178    /* Get base and check limit. */
179    if ( ldt )
180    {
181        table = (unsigned long *)LDT_VIRT_START(d);
182        if ( idx >= d->arch.guest_context.ldt_ents )
183        {
184            dprintk(XENLOG_DEBUG, "Segment %04x out of LDT range (%ld)\n",
185                    seg, d->arch.guest_context.ldt_ents);
186            goto fail;
187        }
188    }
189    else /* gdt */
190    {
191        table = (unsigned long *)GDT_VIRT_START(d);
192        if ( idx >= d->arch.guest_context.gdt_ents )
193        {
194            dprintk(XENLOG_DEBUG, "Segment %04x out of GDT range (%ld)\n",
195                    seg, d->arch.guest_context.gdt_ents);
196            goto fail;
197        }
198    }
199
200    /* Grab the segment descriptor. */
201    if ( __get_user(a, &table[2*idx+0]) ||
202         __get_user(b, &table[2*idx+1]) )
203    {
204        dprintk(XENLOG_DEBUG, "Fault while reading segment %04x\n", seg);
205        goto fail; /* Barking up the wrong tree. Decode needs a page fault.*/
206    }
207
208    /* We only parse 32-bit page-granularity non-privileged data segments. */
209    if ( (b & (_SEGMENT_P|_SEGMENT_S|_SEGMENT_DB|
210               _SEGMENT_G|_SEGMENT_CODE|_SEGMENT_DPL)) != 
211         (_SEGMENT_P|_SEGMENT_S|_SEGMENT_DB|_SEGMENT_G|_SEGMENT_DPL) )
212    {
213        dprintk(XENLOG_DEBUG, "Bad segment %08lx:%08lx\n", a, b);
214        goto fail;
215    }
216
217    /* Decode base and limit. */
218    base  = (b&(0xff<<24)) | ((b&0xff)<<16) | (a>>16);
219    limit = (((b & 0xf0000) | (a & 0x0ffff)) + 1) << 12;
220
221    if ( b & _SEGMENT_EC )
222    {
223        /* Expands-down: All the way to zero? Assume 4GB if so. */
224        if ( ((base + limit) < PAGE_SIZE) && (offset <= limit)  )
225        {
226            /* Flip to expands-up. */
227            limit = GUEST_SEGMENT_MAX_ADDR - base;
228            goto flip;
229        }
230    }
231    else
232    {
233        /* Expands-up: All the way to Xen space? Assume 4GB if so. */
234        if ( ((GUEST_SEGMENT_MAX_ADDR - (base + limit)) < PAGE_SIZE) &&
235             (offset > limit) )
236        {
237            /* Flip to expands-down. */
238            limit = -(base & PAGE_MASK);
239            goto flip;
240        }
241    }
242
243    dprintk(XENLOG_DEBUG, "None of the above! "
244            "(%08lx:%08lx, %08lx, %08lx, %08lx)\n",
245            a, b, base, limit, base+limit);
246
247 fail:
248    return 0;
249
250 flip:
251    limit = (limit >> 12) - 1;
252    a &= ~0x0ffff; a |= limit & 0x0ffff;
253    b &= ~0xf0000; b |= limit & 0xf0000;
254    b ^= _SEGMENT_EC; /* grows-up <-> grows-down */
255    /* NB. These can't fault. Checked readable above; must also be writable. */
256    table[2*idx+0] = a;
257    table[2*idx+1] = b;
258    return 1;
259}
260
261/*
262 * Called from the general-protection fault handler to attempt to decode
263 * and emulate an instruction that depends on 4GB segments.
264 */
265int gpf_emulate_4gb(struct cpu_user_regs *regs)
266{
267    struct vcpu *d = current;
268    struct trap_info   *ti;
269    struct trap_bounce *tb;
270    u8            modrm, mod, reg, rm, decode;
271    void         *memreg;
272    unsigned long offset;
273    u8            disp8;
274    u32           disp32 = 0;
275    u8            *eip;         /* ptr to instruction start */
276    u8            *pb, b;       /* ptr into instr. / current instr. byte */
277    int            gs_override = 0;
278
279    /* WARNING: We only work for ring-3 segments. */
280    if ( unlikely(vm86_mode(regs)) || unlikely(!ring_3(regs)) )
281        goto fail;
282
283    if ( !linearise_address((u16)regs->cs, regs->eip, (unsigned long *)&eip) )
284    {
285        dprintk(XENLOG_DEBUG, "Cannot linearise %04x:%08x\n",
286                regs->cs, regs->eip);
287        goto fail;
288    }
289
290    /* Parse prefix bytes. We're basically looking for segment override. */
291    for ( pb = eip; ; pb++ )
292    {
293        if ( get_user(b, pb) )
294        {
295            dprintk(XENLOG_DEBUG,
296                    "Fault while accessing byte %ld of instruction\n",
297                    (long)(pb-eip));
298            goto page_fault;
299        }
300
301        if ( (pb - eip) >= 15 )
302        {
303            dprintk(XENLOG_DEBUG, "Too many instruction prefixes for a "
304                    "legal instruction\n");
305            goto fail;
306        }
307
308        switch ( b )
309        {
310        case 0x67: /* Address-size override */
311        case 0x2e: /* CS override */
312        case 0x3e: /* DS override */
313        case 0x26: /* ES override */
314        case 0x64: /* FS override */
315        case 0x36: /* SS override */
316            dprintk(XENLOG_DEBUG, "Unhandled prefix %02x\n", b);
317            goto fail;
318        case 0x66: /* Operand-size override */
319        case 0xf0: /* LOCK */
320        case 0xf2: /* REPNE/REPNZ */
321        case 0xf3: /* REP/REPE/REPZ */
322            break;
323        case 0x65: /* GS override */
324            gs_override = 1;
325            break;
326        default: /* Not a prefix byte */
327            goto done_prefix;
328        }
329    }
330 done_prefix:
331
332    if ( !gs_override )
333    {
334        dprintk(XENLOG_DEBUG, "Only instructions with GS override\n");
335        goto fail;
336    }
337
338    decode = insn_decode[b]; /* opcode byte */
339    pb++;
340    if ( decode == 0 )
341    {
342        dprintk(XENLOG_DEBUG, "Unsupported opcode %02x\n", b);
343        goto fail;
344    }
345   
346    if ( !(decode & HAS_MODRM) )
347    {
348        /* Must be a <disp32>, or bail. */
349        if ( (decode & 7) != 4 )
350            goto fail;
351
352        if ( get_user(offset, (u32 *)pb) )
353        {
354            dprintk(XENLOG_DEBUG, "Fault while extracting <disp32>.\n");
355            goto page_fault;
356        }
357        pb += 4;
358
359        goto skip_modrm;
360    }
361
362    /*
363     * Mod/RM processing.
364     */
365
366    if ( get_user(modrm, pb) )
367    {
368        dprintk(XENLOG_DEBUG, "Fault while extracting modrm byte\n");
369        goto page_fault;
370    }
371
372    pb++;
373
374    mod = (modrm >> 6) & 3;
375    reg = (modrm >> 3) & 7;
376    rm  = (modrm >> 0) & 7;
377
378    if ( rm == 4 )
379    {
380        dprintk(XENLOG_DEBUG, "FIXME: Add decoding for the SIB byte.\n");
381        goto fixme;
382    }
383
384    /* Decode R/M field. */
385    memreg = decode_register(rm,  regs, 0);
386
387    /* Decode Mod field. */
388    switch ( modrm >> 6 )
389    {
390    case 0:
391        disp32 = 0;
392        if ( rm == 5 ) /* disp32 rather than (EBP) */
393        {
394            memreg = NULL;
395            if ( get_user(disp32, (u32 *)pb) )
396            {
397                dprintk(XENLOG_DEBUG, "Fault while extracting <disp8>.\n");
398                goto page_fault;
399            }
400            pb += 4;
401        }
402        break;
403
404    case 1:
405        if ( get_user(disp8, pb) )
406        {
407            dprintk(XENLOG_DEBUG, "Fault while extracting <disp8>.\n");
408            goto page_fault;
409        }
410        pb++;
411        disp32 = (disp8 & 0x80) ? (disp8 | ~0xff) : disp8;;
412        break;
413
414    case 2:
415        if ( get_user(disp32, (u32 *)pb) )
416        {
417            dprintk(XENLOG_DEBUG, "Fault while extracting <disp8>.\n");
418            goto page_fault;
419        }
420        pb += 4;
421        break;
422
423    case 3:
424        dprintk(XENLOG_DEBUG, "Not a memory operand!\n");
425        goto fail;
426    }
427
428    offset = disp32;
429    if ( memreg != NULL )
430        offset += *(u32 *)memreg;
431
432 skip_modrm:
433    if ( !fixup_seg((u16)regs->gs, offset) )
434        goto fail;
435
436    /* Success! */
437    perfc_incr(seg_fixups);
438
439    /* If requested, give a callback on otherwise unused vector 15. */
440    if ( VM_ASSIST(d->domain, VMASST_TYPE_4gb_segments_notify) )
441    {
442        ti  = &d->arch.guest_context.trap_ctxt[15];
443        tb  = &d->arch.trap_bounce;
444        tb->flags      = TBF_EXCEPTION | TBF_EXCEPTION_ERRCODE;
445        tb->error_code = pb - eip;
446        tb->cs         = ti->cs;
447        tb->eip        = ti->address;
448        if ( TI_GET_IF(ti) )
449            tb->flags |= TBF_INTERRUPT;
450    }
451
452    return EXCRET_fault_fixed;
453
454 fixme:
455    dprintk(XENLOG_DEBUG, "Undecodable instruction "
456            "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x "
457            "caused GPF(0) at %04x:%08x\n",
458            eip[0], eip[1], eip[2], eip[3],
459            eip[4], eip[5], eip[6], eip[7],
460            regs->cs, regs->eip);
461 fail:
462    return 0;
463
464 page_fault:
465    propagate_page_fault((unsigned long)pb, 0); /* read fault */
466    return EXCRET_fault_fixed;
467}
468
469/*
470 * Local variables:
471 * mode: C
472 * c-set-style: "BSD"
473 * c-basic-offset: 4
474 * tab-width: 4
475 * indent-tabs-mode: nil
476 * End:
477 */
Note: See TracBrowser for help on using the repository browser.