123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421 |
- #include "libcflat.h"
- #include "processor.h"
- #include "vm.h"
- #include "desc.h"
- #include "isr.h"
- #include "apic.h"
- #include "apic-defs.h"
- #ifdef __x86_64__
- # define R "r"
- #else
- # define R "e"
- #endif
- static inline void io_delay(void)
- {
- }
- void apic_self_ipi(u8 v)
- {
- apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED |
- APIC_INT_ASSERT | v, 0);
- }
- void apic_self_nmi(void)
- {
- apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
- }
- #define flush_phys_addr(__s) outl(__s, 0xe4)
- #define flush_stack() do { \
- int __l; \
- flush_phys_addr(virt_to_phys(&__l)); \
- } while (0)
- extern char isr_iret_ip[];
- static void flush_idt_page()
- {
- struct descriptor_table_ptr ptr;
- sidt(&ptr);
- flush_phys_addr(virt_to_phys((void*)ptr.base));
- }
- static volatile unsigned int test_divider;
- static volatile int test_count;
- ulong stack_phys;
- void *stack_va;
- void do_pf_tss(void)
- {
- printf("PF running\n");
- install_pte(phys_to_virt(read_cr3()), 1, stack_va,
- stack_phys | PT_PRESENT_MASK | PT_WRITABLE_MASK, 0);
- invlpg(stack_va);
- }
- extern void pf_tss(void);
- asm ("pf_tss: \n\t"
- #ifdef __x86_64__
- // no task on x86_64, save/restore caller-save regs
- "push %rax; push %rcx; push %rdx; push %rsi; push %rdi\n"
- "push %r8; push %r9; push %r10; push %r11\n"
- #endif
- "call do_pf_tss \n\t"
- #ifdef __x86_64__
- "pop %r11; pop %r10; pop %r9; pop %r8\n"
- "pop %rdi; pop %rsi; pop %rdx; pop %rcx; pop %rax\n"
- #endif
- "add $"S", %"R "sp\n\t" // discard error code
- "iret"W" \n\t"
- "jmp pf_tss\n\t"
- );
- #ifndef __x86_64__
- static void of_isr(struct ex_regs *r)
- {
- printf("OF isr running\n");
- test_count++;
- }
- #endif
- static void np_isr(struct ex_regs *r)
- {
- printf("NP isr running %lx err=%lx\n", r->rip, r->error_code);
- set_idt_sel(33, read_cs());
- test_count++;
- }
- static void de_isr(struct ex_regs *r)
- {
- printf("DE isr running divider is %d\n", test_divider);
- test_divider = 10;
- }
- static void bp_isr(struct ex_regs *r)
- {
- printf("BP isr running\n");
- test_count++;
- }
- static void nested_nmi_isr(struct ex_regs *r)
- {
- printf("Nested NMI isr running rip=%lx\n", r->rip);
- if (r->rip != (ulong)&isr_iret_ip)
- test_count++;
- }
- static void nmi_isr(struct ex_regs *r)
- {
- printf("NMI isr running %p\n", &isr_iret_ip);
- test_count++;
- handle_exception(2, nested_nmi_isr);
- printf("Sending nested NMI to self\n");
- apic_self_nmi();
- io_delay();
- printf("After nested NMI to self\n");
- }
- unsigned long *iret_stack;
- static void nested_nmi_iret_isr(struct ex_regs *r)
- {
- printf("Nested NMI isr running rip=%lx\n", r->rip);
- if (r->rip == iret_stack[-3])
- test_count++;
- }
- extern void do_iret(ulong phys_stack, void *virt_stack);
- // Return to same privilege level won't pop SS or SP, so
- // save it in RDX while we run on the nested stack
- asm("do_iret:"
- #ifdef __x86_64__
- "mov %rdi, %rax \n\t" // phys_stack
- "mov %rsi, %rdx \n\t" // virt_stack
- #else
- "mov 4(%esp), %eax \n\t" // phys_stack
- "mov 8(%esp), %edx \n\t" // virt_stack
- #endif
- "xchg %"R "dx, %"R "sp \n\t" // point to new stack
- "pushf"W" \n\t"
- "mov %cs, %ecx \n\t"
- "push"W" %"R "cx \n\t"
- "push"W" $1f \n\t"
- "outl %eax, $0xe4 \n\t" // flush page
- "iret"W" \n\t"
- "1: xchg %"R "dx, %"R "sp \n\t" // point to old stack
- "ret\n\t"
- );
- static void nmi_iret_isr(struct ex_regs *r)
- {
- unsigned long *s = alloc_page();
- test_count++;
- printf("NMI isr running stack %p\n", s);
- handle_exception(2, nested_nmi_iret_isr);
- printf("Sending nested NMI to self\n");
- apic_self_nmi();
- printf("After nested NMI to self\n");
- iret_stack = &s[128];
- do_iret(virt_to_phys(s), iret_stack);
- printf("After iret\n");
- }
- static void tirq0(isr_regs_t *r)
- {
- printf("irq0 running\n");
- if (test_count == 1)
- test_count++;
- eoi();
- }
- static void tirq1(isr_regs_t *r)
- {
- printf("irq1 running\n");
- test_count++;
- eoi();
- }
- ulong saved_stack;
- #define switch_stack(S) do { \
- asm volatile ("mov %%" R "sp, %0":"=r"(saved_stack)); \
- asm volatile ("mov %0, %%" R "sp"::"r"(S)); \
- } while(0)
- #define restore_stack() do { \
- asm volatile ("mov %0, %%" R "sp"::"r"(saved_stack)); \
- } while(0)
- int main()
- {
- unsigned int res;
- ulong *pt, *cr3, i;
- setup_vm();
- setup_idt();
- setup_alt_stack();
- handle_irq(32, tirq0);
- handle_irq(33, tirq1);
- /* generate HW exception that will fault on IDT and stack */
- handle_exception(0, de_isr);
- printf("Try to divide by 0\n");
- flush_idt_page();
- flush_stack();
- asm volatile ("divl %3": "=a"(res)
- : "d"(0), "a"(1500), "m"(test_divider));
- printf("Result is %d\n", res);
- report("DE exception", res == 150);
- /* generate soft exception (BP) that will fault on IDT and stack */
- test_count = 0;
- handle_exception(3, bp_isr);
- printf("Try int 3\n");
- flush_idt_page();
- flush_stack();
- asm volatile ("int $3");
- printf("After int 3\n");
- report("BP exception", test_count == 1);
- #ifndef __x86_64__
- /* generate soft exception (OF) that will fault on IDT */
- test_count = 0;
- handle_exception(4, of_isr);
- flush_idt_page();
- printf("Try into\n");
- asm volatile ("addb $127, %b0\ninto"::"a"(127));
- printf("After into\n");
- report("OF exception", test_count == 1);
- /* generate soft exception (OF) using two bit instruction that will
- fault on IDT */
- test_count = 0;
- handle_exception(4, of_isr);
- flush_idt_page();
- printf("Try into\n");
- asm volatile ("addb $127, %b0\naddr16 into"::"a"(127));
- printf("After into\n");
- report("2 byte OF exception", test_count == 1);
- #endif
- /* generate HW interrupt that will fault on IDT */
- test_count = 0;
- flush_idt_page();
- printf("Sending vec 33 to self\n");
- irq_enable();
- apic_self_ipi(33);
- io_delay();
- irq_disable();
- printf("After vec 33 to self\n");
- report("vec 33", test_count == 1);
- /* generate soft interrupt that will fault on IDT and stack */
- test_count = 0;
- flush_idt_page();
- printf("Try int $33\n");
- flush_stack();
- asm volatile ("int $33");
- printf("After int $33\n");
- report("int $33", test_count == 1);
- /* Inject two HW interrupt than open iterrupt windows. Both interrupt
- will fault on IDT access */
- test_count = 0;
- flush_idt_page();
- printf("Sending vec 32 and 33 to self\n");
- apic_self_ipi(32);
- apic_self_ipi(33);
- io_delay();
- irq_enable();
- asm volatile("nop");
- irq_disable();
- printf("After vec 32 and 33 to self\n");
- report("vec 32/33", test_count == 2);
- /* Inject HW interrupt, do sti and than (while in irq shadow) inject
- soft interrupt. Fault during soft interrupt. Soft interrup shoud be
- handled before HW interrupt */
- test_count = 0;
- flush_idt_page();
- printf("Sending vec 32 and int $33\n");
- apic_self_ipi(32);
- flush_stack();
- io_delay();
- asm volatile ("sti; int $33");
- irq_disable();
- printf("After vec 32 and int $33\n");
- report("vec 32/int $33", test_count == 2);
- /* test that TPR is honored */
- test_count = 0;
- handle_irq(62, tirq1);
- flush_idt_page();
- printf("Sending vec 33 and 62 and mask one with TPR\n");
- apic_write(APIC_TASKPRI, 0xf << 4);
- irq_enable();
- apic_self_ipi(32);
- apic_self_ipi(62);
- io_delay();
- apic_write(APIC_TASKPRI, 0x2 << 4);
- printf("After 33/62 TPR test\n");
- report("TPR", test_count == 1);
- apic_write(APIC_TASKPRI, 0x0);
- while(test_count != 2); /* wait for second irq */
- irq_disable();
- /* test fault durint NP delivery */
- printf("Before NP test\n");
- test_count = 0;
- handle_exception(11, np_isr);
- set_idt_sel(33, NP_SEL);
- flush_idt_page();
- flush_stack();
- asm volatile ("int $33");
- printf("After int33\n");
- report("NP exception", test_count == 2);
- /* generate NMI that will fault on IDT */
- test_count = 0;
- handle_exception(2, nmi_isr);
- flush_idt_page();
- printf("Sending NMI to self\n");
- apic_self_nmi();
- printf("After NMI to self\n");
- /* this is needed on VMX without NMI window notification.
- Interrupt windows is used instead, so let pending NMI
- to be injected */
- irq_enable();
- asm volatile ("nop");
- irq_disable();
- report("NMI", test_count == 2);
- /* generate NMI that will fault on IRET */
- printf("Before NMI IRET test\n");
- test_count = 0;
- handle_exception(2, nmi_iret_isr);
- printf("Sending NMI to self\n");
- apic_self_nmi();
- /* this is needed on VMX without NMI window notification.
- Interrupt windows is used instead, so let pending NMI
- to be injected */
- irq_enable();
- asm volatile ("nop");
- irq_disable();
- printf("After NMI to self\n");
- report("NMI", test_count == 2);
- stack_phys = (ulong)virt_to_phys(alloc_page());
- stack_va = alloc_vpage();
- /* Generate DE and PF exceptions serially */
- test_divider = 0;
- set_intr_alt_stack(14, pf_tss);
- handle_exception(0, de_isr);
- printf("Try to divide by 0\n");
- /* install read only pte */
- install_pte(phys_to_virt(read_cr3()), 1, stack_va,
- stack_phys | PT_PRESENT_MASK, 0);
- invlpg(stack_va);
- flush_phys_addr(stack_phys);
- switch_stack(stack_va + 4095);
- flush_idt_page();
- asm volatile ("divl %3": "=a"(res)
- : "d"(0), "a"(1500), "m"(test_divider));
- restore_stack();
- printf("Result is %d\n", res);
- report("DE PF exceptions", res == 150);
- /* Generate NP and PF exceptions serially */
- printf("Before NP test\n");
- test_count = 0;
- set_intr_alt_stack(14, pf_tss);
- handle_exception(11, np_isr);
- set_idt_sel(33, NP_SEL);
- /* install read only pte */
- install_pte(phys_to_virt(read_cr3()), 1, stack_va,
- stack_phys | PT_PRESENT_MASK, 0);
- invlpg(stack_va);
- flush_idt_page();
- flush_phys_addr(stack_phys);
- switch_stack(stack_va + 4095);
- asm volatile ("int $33");
- restore_stack();
- printf("After int33\n");
- report("NP PF exceptions", test_count == 2);
- pt = alloc_page();
- cr3 = (void*)read_cr3();
- memset(pt, 0, 4096);
- /* use shadowed stack during interrupt delivery */
- for (i = 0; i < 4096/sizeof(ulong); i++) {
- if (!cr3[i]) {
- cr3[i] = virt_to_phys(pt) | PT_PRESENT_MASK | PT_WRITABLE_MASK;
- pt[0] = virt_to_phys(pt) | PT_PRESENT_MASK | PT_WRITABLE_MASK;
- #ifndef __x86_64__
- ((ulong*)(i<<22))[1] = 0;
- #else
- ((ulong*)(i<<39))[1] = 0;
- #endif
- write_cr3(virt_to_phys(cr3));
- break;
- }
- }
- test_count = 0;
- printf("Try int 33 with shadowed stack\n");
- switch_stack(((char*)pt) + 4095);
- asm volatile("int $33");
- restore_stack();
- printf("After int 33 with shadowed stack\n");
- report("int 33 with shadowed stack", test_count == 1);
- return report_summary();
- }
|