aboutsummaryrefslogtreecommitdiffstats
path: root/xen/arch/x86/x86_32/traps.c
blob: 3a489f244c6405ada1337e85bc6438405596ccbd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
#include <xen/config.h>
#include <xen/init.h>
#include <xen/sched.h>
#include <xen/lib.h>
#include <xen/console.h>
#include <xen/mm.h>
#include <xen/irq.h>
#include <asm/current.h>
#include <asm/flushtlb.h>
#include <asm/vmx.h>

/* All CPUs have their own IDT to allow int80 direct trap. */
idt_entry_t *idt_tables[NR_CPUS] = { 0 };

void show_registers(struct cpu_user_regs *regs)
{
    unsigned long ss, ds, es, fs, gs, cs;
    unsigned long eip, esp, eflags, cr0, cr3;
    const char *context;

    if ( VMX_DOMAIN(current) && (regs->eflags == 0) )
    {
        __vmread(GUEST_EIP, &eip);
        __vmread(GUEST_ESP, &esp);
        __vmread(GUEST_EFLAGS, &eflags);
        __vmread(GUEST_SS_SELECTOR, &ss);
        __vmread(GUEST_DS_SELECTOR, &ds);
        __vmread(GUEST_ES_SELECTOR, &es);
        __vmread(GUEST_FS_SELECTOR, &fs);
        __vmread(GUEST_GS_SELECTOR, &gs);
        __vmread(GUEST_CS_SELECTOR, &cs);
        __vmread(CR0_READ_SHADOW, &cr0);
        __vmread(GUEST_CR3, &cr3);
        context = "vmx guest";
    }
    else
    {
        eip    = regs->eip;
        eflags = regs->eflags;
        cr0    = read_cr0();
        cr3    = read_cr3();

        __asm__ ( "movl %%fs,%0 ; movl %%gs,%1" : "=r" (fs), "=r" (gs) );

        if ( GUEST_MODE(regs) )
        {
            esp = regs->esp;
            ss  = regs->ss & 0xffff;
            ds  = regs->ds & 0xffff;
            es  = regs->es & 0xffff;
            cs  = regs->cs & 0xffff;
            context = "guest";
        }
        else
        {
            esp = (unsigned long)&regs->esp;
            ss  = __HYPERVISOR_DS;
            ds  = __HYPERVISOR_DS;
            es  = __HYPERVISOR_DS;
            cs  = __HYPERVISOR_CS;
            context = "hypervisor";
        }
    }

    printk("CPU:    %d\nEIP:    %04lx:[<%08lx>]      \nEFLAGS: %08lx   "
           "CONTEXT: %s\n",
           smp_processor_id(), (unsigned long)0xffff & regs->cs,
           eip, eflags, context);
    printk("eax: %08x   ebx: %08x   ecx: %08x   edx: %08x\n",
           regs->eax, regs->ebx, regs->ecx, regs->edx);
    printk("esi: %08x   edi: %08x   ebp: %08x   esp: %08lx\n",
           regs->esi, regs->edi, regs->ebp, esp);
    printk("cr0: %08lx   cr3: %08lx\n", cr0, cr3);
    printk("ds: %04lx   es: %04lx   fs: %04lx   gs: %04lx   "
           "ss: %04lx   cs: %04lx\n",
           ds, es, fs, gs, ss, cs);

    if ( GUEST_MODE(regs) )
        show_guest_stack();
    else
        show_stack((unsigned long *)&regs->esp);
} 

void show_page_walk(unsigned long addr)
{
    l2_pgentry_t pmd;
    l1_pgentry_t *pte;

    if ( addr < PAGE_OFFSET )
        return;

    printk("Pagetable walk from %08lx:\n", addr);
    
    pmd = idle_pg_table_l2[l2_linear_offset(addr)];
    printk(" L2 = %"PRIpte" %s\n", l2e_get_intpte(pmd),
           (l2e_get_flags(pmd) & _PAGE_PSE) ? "(2/4MB)" : "");
    if ( !(l2e_get_flags(pmd) & _PAGE_PRESENT) ||
         (l2e_get_flags(pmd) & _PAGE_PSE) )
        return;

    pte  = __va(l2e_get_paddr(pmd));
    pte += l1_table_offset(addr);
    printk("  L1 = %"PRIpte"\n", l1e_get_intpte(*pte));
}

#define DOUBLEFAULT_STACK_SIZE 1024
static struct tss_struct doublefault_tss;
static unsigned char doublefault_stack[DOUBLEFAULT_STACK_SIZE];

asmlinkage void do_double_fault(void)
{
    struct tss_struct *tss = &doublefault_tss;
    unsigned int cpu = ((tss->back_link>>3)-__FIRST_TSS_ENTRY)>>1;

    watchdog_disable();

    console_force_unlock();

    /* Find information saved during fault and dump it to the console. */
    tss = &init_tss[cpu];
    printk("CPU:    %d\nEIP:    %04x:[<%08x>]      \nEFLAGS: %08x\n",
           cpu, tss->cs, tss->eip, tss->eflags);
    printk("CR3:    %08x\n", tss->__cr3);
    printk("eax: %08x   ebx: %08x   ecx: %08x   edx: %08x\n",
           tss->eax, tss->ebx, tss->ecx, tss->edx);
    printk("esi: %08x   edi: %08x   ebp: %08x   esp: %08x\n",
           tss->esi, tss->edi, tss->ebp, tss->esp);
    printk("ds: %04x   es: %04x   fs: %04x   gs: %04x   ss: %04x\n",
           tss->ds, tss->es, tss->fs, tss->gs, tss->ss);
    printk("************************************\n");
    printk("CPU%d DOUBLE FAULT -- system shutdown\n", cpu);
    printk("System needs manual reset.\n");
    printk("************************************\n");

    /* Lock up the console to prevent spurious output from other CPUs. */
    console_force_lock();

    /* Wait for manual reset. */
    for ( ; ; )
        __asm__ __volatile__ ( "hlt" );
}

BUILD_SMP_INTERRUPT(deferred_nmi, TRAP_deferred_nmi)
asmlinkage void smp_deferred_nmi(struct cpu_user_regs regs)
{
    asmlinkage void do_nmi(struct cpu_user_regs *, unsigned long);
    ack_APIC_irq();
    do_nmi(&regs, 0);
}

void __init percpu_traps_init(void)
{
    asmlinkage int hypercall(void);

    if ( smp_processor_id() != 0 )
        return;

    /* CPU0 uses the master IDT. */
    idt_tables[0] = idt_table;

    /* The hypercall entry vector is only accessible from ring 1. */
    _set_gate(idt_table+HYPERCALL_VECTOR, 14, 1, &hypercall);

    set_intr_gate(TRAP_deferred_nmi, &deferred_nmi);

    /*
     * Make a separate task for double faults. This will get us debug output if
     * we blow the kernel stack.
     */
    struct tss_struct *tss = &doublefault_tss;
    memset(tss, 0, sizeof(*tss));
    tss->ds     = __HYPERVISOR_DS;
    tss->es     = __HYPERVISOR_DS;
    tss->ss     = __HYPERVISOR_DS;
    tss->esp    = (unsigned long)
        &doublefault_stack[DOUBLEFAULT_STACK_SIZE];
    tss->__cr3  = __pa(idle_pg_table);
    tss->cs     = __HYPERVISOR_CS;
    tss->eip    = (unsigned long)do_double_fault;
    tss->eflags = 2;
    tss->bitmap = IOBMP_INVALID_OFFSET;
    _set_tssldt_desc(
        gdt_table + __DOUBLEFAULT_TSS_ENTRY - FIRST_RESERVED_GDT_ENTRY,
        (unsigned long)tss, 235, 9);

    set_task_gate(TRAP_double_fault, __DOUBLEFAULT_TSS_ENTRY<<3);
}

void init_int80_direct_trap(struct vcpu *v)
{
    trap_info_t *ti = &v->arch.guest_context.trap_ctxt[0x80];

    /*
     * We can't virtualise interrupt gates, as there's no way to get
     * the CPU to automatically clear the events_mask variable.
     */
    if ( TI_GET_IF(ti) )
        return;

    v->arch.int80_desc.a = (ti->cs << 16) | (ti->address & 0xffff);
    v->arch.int80_desc.b =
        (ti->address & 0xffff0000) | 0x8f00 | ((TI_GET_DPL(ti) & 3) << 13);

    if ( v == current )
        set_int80_direct_trap(v);
}

long do_set_callbacks(unsigned long event_selector,
                      unsigned long event_address,
                      unsigned long failsafe_selector,
                      unsigned long failsafe_address)
{
    struct vcpu *d = current;

    if ( !VALID_CODESEL(event_selector) || !VALID_CODESEL(failsafe_selector) )
        return -EPERM;

    d->arch.guest_context.event_callback_cs     = event_selector;
    d->arch.guest_context.event_callback_eip    = event_address;
    d->arch.guest_context.failsafe_callback_cs  = failsafe_selector;
    d->arch.guest_context.failsafe_callback_eip = failsafe_address;

    return 0;
}

/*
 * Local variables:
 * mode: C
 * c-set-style: "BSD"
 * c-basic-offset: 4
 * tab-width: 4
 * indent-tabs-mode: nil
 * End:
 */