1
2#include <mini-os/os.h>
3#include <mini-os/hypervisor.h>
4#include <mini-os/mm.h>
5#include <mini-os/lib.h>
6
7#include <mini-os/machine/traps.h>
8
9#include <bmk-core/pgalloc.h>
10#include <bmk-core/string.h>
11
12/*
13 * These are assembler stubs in entry.S.
14 * They are the actual entry points for virtual exceptions.
15 */
16void _minios_entry_divide_error(void);
17void _minios_entry_debug(void);
18void _minios_entry_int3(void);
19void _minios_entry_overflow(void);
20void _minios_entry_bounds(void);
21void _minios_entry_invalid_op(void);
22void _minios_entry_device_not_available(void);
23void _minios_entry_coprocessor_segment_overrun(void);
24void _minios_entry_invalid_TSS(void);
25void _minios_entry_segment_not_present(void);
26void _minios_entry_stack_segment(void);
27void _minios_entry_general_protection(void);
28void _minios_entry_page_fault(void);
29void _minios_entry_coprocessor_error(void);
30void _minios_entry_simd_coprocessor_error(void);
31void _minios_entry_alignment_check(void);
32void _minios_entry_spurious_interrupt_bug(void);
33void _minios_entry_machine_check(void);
34
35
36void dump_regs(struct pt_regs *regs)
37{
38    minios_printk("Thread: %s\n", bmk_sched_threadname(bmk_current));
39#ifdef __i386__
40    minios_printk("EIP: %x, EFLAGS %x.\n", regs->eip, regs->eflags);
41    minios_printk("EBX: %08x ECX: %08x EDX: %08x\n",
42	   regs->ebx, regs->ecx, regs->edx);
43    minios_printk("ESI: %08x EDI: %08x EBP: %08x EAX: %08x\n",
44	   regs->esi, regs->edi, regs->ebp, regs->eax);
45    minios_printk("DS: %04x ES: %04x orig_eax: %08x, eip: %08x\n",
46	   regs->xds, regs->xes, regs->orig_eax, regs->eip);
47    minios_printk("CS: %04x EFLAGS: %08x esp: %08x ss: %04x\n",
48	   regs->xcs, regs->eflags, regs->esp, regs->xss);
49#else
50    minios_printk("RIP: %04lx:[<%016lx>] ", regs->cs & 0xffff, regs->rip);
51    minios_printk("\nRSP: %04lx:%016lx  EFLAGS: %08lx\n",
52           regs->ss, regs->rsp, regs->eflags);
53    minios_printk("RAX: %016lx RBX: %016lx RCX: %016lx\n",
54           regs->rax, regs->rbx, regs->rcx);
55    minios_printk("RDX: %016lx RSI: %016lx RDI: %016lx\n",
56           regs->rdx, regs->rsi, regs->rdi);
57    minios_printk("RBP: %016lx R08: %016lx R09: %016lx\n",
58           regs->rbp, regs->r8, regs->r9);
59    minios_printk("R10: %016lx R11: %016lx R12: %016lx\n",
60           regs->r10, regs->r11, regs->r12);
61    minios_printk("R13: %016lx R14: %016lx R15: %016lx\n",
62           regs->r13, regs->r14, regs->r15);
63#endif
64}
65
66static void do_trap(int trapnr, char *str, struct pt_regs * regs, unsigned long error_code)
67{
68    minios_printk("FATAL:  Unhandled Trap %d (%s), error code=0x%lx\n", trapnr, str, error_code);
69    minios_printk("Regs address %p\n", regs);
70    dump_regs(regs);
71    minios_do_exit();
72}
73
74#define DO_ERROR(trapnr, str, name) \
75void do_##name(struct pt_regs * regs, unsigned long error_code) \
76{ \
77	do_trap(trapnr, str, regs, error_code); \
78}
79
80#define DO_ERROR_INFO(trapnr, str, name, sicode, siaddr) \
81void do_##name(struct pt_regs * regs, unsigned long error_code) \
82{ \
83	do_trap(trapnr, str, regs, error_code); \
84}
85
86DO_ERROR_INFO( 0, "divide error", divide_error, FPE_INTDIV, regs->eip)
87DO_ERROR( 3, "int3", int3)
88DO_ERROR( 4, "overflow", overflow)
89DO_ERROR( 5, "bounds", bounds)
90DO_ERROR_INFO( 6, "invalid operand", invalid_op, ILL_ILLOPN, regs->eip)
91DO_ERROR( 7, "device not available", device_not_available)
92DO_ERROR( 9, "coprocessor segment overrun", coprocessor_segment_overrun)
93DO_ERROR(10, "invalid TSS", invalid_TSS)
94DO_ERROR(11, "segment not present", segment_not_present)
95DO_ERROR(12, "stack segment", stack_segment)
96DO_ERROR_INFO(17, "alignment check", alignment_check, BUS_ADRALN, 0)
97DO_ERROR(18, "machine check", machine_check)
98
99void page_walk(unsigned long virt_address)
100{
101        pgentry_t *tab = (pgentry_t *)start_info.pt_base, page;
102        unsigned long addr = virt_address;
103        minios_printk("Pagetable walk from virt %lx, base %lx:\n", virt_address, start_info.pt_base);
104
105#if defined(__x86_64__)
106        page = tab[l4_table_offset(addr)];
107        tab = pte_to_virt(page);
108        minios_printk(" L4 = %"PRIpte" (%p)  [offset = %lx]\n", page, tab, l4_table_offset(addr));
109#endif
110        page = tab[l3_table_offset(addr)];
111        tab = pte_to_virt(page);
112        minios_printk("  L3 = %"PRIpte" (%p)  [offset = %lx]\n", page, tab, l3_table_offset(addr));
113        page = tab[l2_table_offset(addr)];
114        tab = pte_to_virt(page);
115        minios_printk("   L2 = %"PRIpte" (%p)  [offset = %lx]\n", page, tab, l2_table_offset(addr));
116
117        page = tab[l1_table_offset(addr)];
118        minios_printk("    L1 = %"PRIpte" [offset = %lx]\n", page, l1_table_offset(addr));
119
120}
121
122static int handle_cow(unsigned long addr) {
123        pgentry_t *tab = (pgentry_t *)start_info.pt_base, page;
124	void *new_page;
125	int rc;
126
127#if defined(__x86_64__)
128        page = tab[l4_table_offset(addr)];
129	if (!(page & _PAGE_PRESENT))
130	    return 0;
131        tab = pte_to_virt(page);
132#endif
133        page = tab[l3_table_offset(addr)];
134	if (!(page & _PAGE_PRESENT))
135	    return 0;
136        tab = pte_to_virt(page);
137
138        page = tab[l2_table_offset(addr)];
139	if (!(page & _PAGE_PRESENT))
140	    return 0;
141        tab = pte_to_virt(page);
142
143        page = tab[l1_table_offset(addr)];
144	if (!(page & _PAGE_PRESENT))
145	    return 0;
146	/* Only support CoW for the zero page.  */
147	if (PHYS_PFN(page) != _minios_mfn_zero)
148	    return 0;
149
150	new_page = bmk_pgalloc_one();
151	bmk_memset(new_page, 0, PAGE_SIZE);
152
153	rc = HYPERVISOR_update_va_mapping(addr & PAGE_MASK, __pte(virt_to_mach(new_page) | L1_PROT), UVMF_INVLPG);
154	if (!rc)
155		return 1;
156
157	minios_printk("Map zero page to %lx failed: %d.\n", addr, rc);
158	return 0;
159}
160
161#define MAXWALKDEPTH 100
162static int walkdepth;
163
164static void do_stack_walk(unsigned long frame_base)
165{
166    unsigned long *frame = (void*) frame_base;
167    minios_printk("base is %#lx ", frame_base);
168    minios_printk("caller is %#lx\n", frame[1]);
169    if (frame[0] && ++walkdepth < MAXWALKDEPTH && frame[1] < (unsigned long)&_etext)
170	do_stack_walk(frame[0]);
171}
172
173static void
174stack_walk_enter(unsigned long base)
175{
176
177    walkdepth = 0;
178    do_stack_walk(base);
179}
180
181void stack_walk(void)
182{
183    unsigned long bp;
184#ifdef __x86_64__
185    asm("movq %%rbp, %0":"=r"(bp));
186#else
187    asm("movl %%ebp, %0":"=r"(bp));
188#endif
189    stack_walk_enter(bp);
190}
191
192static void dump_mem(unsigned long addr)
193{
194    unsigned long i;
195    if (addr < PAGE_SIZE)
196	return;
197
198    for (i = ((addr)-16 ) & ~15; i < (((addr)+48 ) & ~15); i++)
199    {
200	if (!(i%16))
201	    minios_printk("\n%lx:", i);
202	minios_printk(" %02x", *(unsigned char *)i);
203    }
204    minios_printk("\n");
205}
206#define read_cr2() \
207        (HYPERVISOR_shared_info->vcpu_info[smp_processor_id()].arch.cr2)
208
209static int handling_pg_fault = 0;
210
211void do_page_fault(struct pt_regs *regs, unsigned long error_code)
212{
213    unsigned long addr = read_cr2();
214    struct sched_shutdown sched_shutdown = { .reason = SHUTDOWN_crash };
215
216    if ((error_code & TRAP_PF_WRITE) && handle_cow(addr))
217	return;
218
219    /* If we are already handling a page fault, and got another one
220       that means we faulted in pagetable walk. Continuing here would cause
221       a recursive fault */
222    if(handling_pg_fault == 1)
223    {
224        minios_printk("Page fault in pagetable walk (access to invalid memory?).\n");
225        HYPERVISOR_sched_op(SCHEDOP_shutdown, &sched_shutdown);
226    }
227    handling_pg_fault++;
228    barrier();
229
230#if defined(__x86_64__)
231    minios_printk("Page fault at linear address %p, rip %p, regs %p, sp %p, our_sp %p, code %lx\n",
232           addr, regs->rip, regs, regs->rsp, &addr, error_code);
233#else
234    minios_printk("Page fault at linear address %p, eip %p, regs %p, sp %p, our_sp %p, code %lx\n",
235           addr, regs->eip, regs, regs->esp, &addr, error_code);
236#endif
237
238    dump_regs(regs);
239#if defined(__x86_64__)
240    stack_walk_enter(regs->rbp);
241    dump_mem(regs->rsp);
242    dump_mem(regs->rbp);
243    dump_mem(regs->rip);
244#else
245    stack_walk_enter(regs->ebp);
246    dump_mem(regs->esp);
247    dump_mem(regs->ebp);
248    dump_mem(regs->eip);
249#endif
250    page_walk(addr);
251    HYPERVISOR_sched_op(SCHEDOP_shutdown, &sched_shutdown);
252    /* We should never get here ... but still */
253    handling_pg_fault--;
254}
255
256void do_general_protection(struct pt_regs *regs, long error_code)
257{
258    struct sched_shutdown sched_shutdown = { .reason = SHUTDOWN_crash };
259#ifdef __i386__
260    minios_printk("GPF eip: %p, error_code=%lx\n", regs->eip, error_code);
261#else
262    minios_printk("GPF rip: %p, error_code=%lx\n", regs->rip, error_code);
263#endif
264    dump_regs(regs);
265#if defined(__x86_64__)
266    stack_walk_enter(regs->rbp);
267    dump_mem(regs->rsp);
268    dump_mem(regs->rbp);
269    dump_mem(regs->rip);
270#else
271    stack_walk_enter(regs->ebp);
272    dump_mem(regs->esp);
273    dump_mem(regs->ebp);
274    dump_mem(regs->eip);
275#endif
276    HYPERVISOR_sched_op(SCHEDOP_shutdown, &sched_shutdown);
277}
278
279
280void do_debug(struct pt_regs * regs)
281{
282    minios_printk("Debug exception\n");
283#define TF_MASK 0x100
284    regs->eflags &= ~TF_MASK;
285    dump_regs(regs);
286    minios_do_exit();
287}
288
289void do_coprocessor_error(struct pt_regs * regs)
290{
291    minios_printk("Copro error\n");
292    dump_regs(regs);
293    minios_do_exit();
294}
295
296void simd_math_error(void *eip)
297{
298    minios_printk("SIMD error\n");
299}
300
301void do_simd_coprocessor_error(struct pt_regs * regs)
302{
303    minios_printk("SIMD copro error\n");
304}
305
306void do_spurious_interrupt_bug(struct pt_regs * regs)
307{
308}
309
310/*
311 * Submit a virtual IDT to teh hypervisor. This consists of tuples
312 * (interrupt vector, privilege ring, CS:EIP of handler).
313 * The 'privilege ring' field specifies the least-privileged ring that
314 * can trap to that vector using a software-interrupt instruction (INT).
315 */
316static trap_info_t trap_table[] = {
317    {  0, 0, __KERNEL_CS, (unsigned long)_minios_entry_divide_error                },
318    {  1, 0, __KERNEL_CS, (unsigned long)_minios_entry_debug                       },
319    {  3, 3, __KERNEL_CS, (unsigned long)_minios_entry_int3                        },
320    {  4, 3, __KERNEL_CS, (unsigned long)_minios_entry_overflow                    },
321    {  5, 3, __KERNEL_CS, (unsigned long)_minios_entry_bounds                      },
322    {  6, 0, __KERNEL_CS, (unsigned long)_minios_entry_invalid_op                  },
323    {  7, 0, __KERNEL_CS, (unsigned long)_minios_entry_device_not_available        },
324    {  9, 0, __KERNEL_CS, (unsigned long)_minios_entry_coprocessor_segment_overrun },
325    { 10, 0, __KERNEL_CS, (unsigned long)_minios_entry_invalid_TSS                 },
326    { 11, 0, __KERNEL_CS, (unsigned long)_minios_entry_segment_not_present         },
327    { 12, 0, __KERNEL_CS, (unsigned long)_minios_entry_stack_segment               },
328    { 13, 0, __KERNEL_CS, (unsigned long)_minios_entry_general_protection          },
329    { 14, 0, __KERNEL_CS, (unsigned long)_minios_entry_page_fault                  },
330    { 15, 0, __KERNEL_CS, (unsigned long)_minios_entry_spurious_interrupt_bug      },
331    { 16, 0, __KERNEL_CS, (unsigned long)_minios_entry_coprocessor_error           },
332    { 17, 0, __KERNEL_CS, (unsigned long)_minios_entry_alignment_check             },
333    { 19, 0, __KERNEL_CS, (unsigned long)_minios_entry_simd_coprocessor_error      },
334    {  0, 0,           0, 0                           }
335};
336
337
338
339void trap_init(void)
340{
341    HYPERVISOR_set_trap_table(trap_table);
342}
343
344void trap_fini(void)
345{
346    HYPERVISOR_set_trap_table(NULL);
347}
348