/* The syscall vector it wants must be unused by Host. */ bool check_syscall_vector(struct lguest *lg) { u32 vector; if (get_user(vector, &lg->lguest_data->syscall_vec)) return false; return could_be_syscall(vector); }
/*H:250 Here's the hard part: returning to the Host every time a trap happens * and then calling deliver_trap() and re-entering the Guest is slow. * Particularly because Guest userspace system calls are traps (usually trap * 128). * * So we'd like to set up the IDT to tell the CPU to deliver traps directly * into the Guest. This is possible, but the complexities cause the size of * this file to double! However, 150 lines of code is worth writing for taking * system calls down from 1750ns to 270ns. Plus, if lguest didn't do it, all * the other hypervisors would beat it up at lunchtime. * * This routine indicates if a particular trap number could be delivered * directly. */ static int direct_trap(unsigned int num) { /* Hardware interrupts don't go to the Guest at all (except system * call). */ if (num >= FIRST_EXTERNAL_VECTOR && !could_be_syscall(num)) return 0; /* The Host needs to see page faults (for shadow paging and to save the * fault address), general protection faults (in/out emulation) and * device not available (TS handling), and of course, the hypercall * trap. */ return num != 14 && num != 13 && num != 7 && num != LGUEST_TRAP_ENTRY; }
/*H:050 Once we've re-enabled interrupts, we look at why the Guest exited. */ void lguest_arch_handle_trap(struct lg_cpu *cpu) { unsigned long iomem_addr; switch (cpu->regs->trapnum) { case 13: /* We've intercepted a General Protection Fault. */ /* Hand to Launcher to emulate those pesky IN and OUT insns */ if (cpu->regs->errcode == 0) { setup_emulate_insn(cpu); return; } break; case 14: /* We've intercepted a Page Fault. */ /* * The Guest accessed a virtual address that wasn't mapped. * This happens a lot: we don't actually set up most of the page * tables for the Guest at all when we start: as it runs it asks * for more and more, and we set them up as required. In this * case, we don't even tell the Guest that the fault happened. * * The errcode tells whether this was a read or a write, and * whether kernel or userspace code. */ if (demand_page(cpu, cpu->arch.last_pagefault, cpu->regs->errcode, &iomem_addr)) return; /* Was this an access to memory mapped IO? */ if (iomem_addr) { /* Tell Launcher, let it handle it. */ setup_iomem_insn(cpu, iomem_addr); return; } /* * OK, it's really not there (or not OK): the Guest needs to * know. We write out the cr2 value so it knows where the * fault occurred. * * Note that if the Guest were really messed up, this could * happen before it's done the LHCALL_LGUEST_INIT hypercall, so * lg->lguest_data could be NULL */ if (cpu->lg->lguest_data && put_user(cpu->arch.last_pagefault, &cpu->lg->lguest_data->cr2)) kill_guest(cpu, "Writing cr2"); break; case 7: /* We've intercepted a Device Not Available fault. */ /* No special handling is needed here. */ break; case 32 ... 255: /* This might be a syscall. */ if (could_be_syscall(cpu->regs->trapnum)) break; /* * Other values mean a real interrupt occurred, in which case * the Host handler has already been run. We just do a * friendly check if another process should now be run, then * return to run the Guest again. */ cond_resched(); return; case LGUEST_TRAP_ENTRY: /* * Our 'struct hcall_args' maps directly over our regs: we set * up the pointer now to indicate a hypercall is pending. */ cpu->hcall = (struct hcall_args *)cpu->regs; return; } /* We didn't handle the trap, so it needs to go to the Guest. */ if (!deliver_trap(cpu, cpu->regs->trapnum)) /* * If the Guest doesn't have a handler (either it hasn't * registered any yet, or it's one of the faults we don't let * it handle), it dies with this cryptic error message. */ kill_guest(cpu, "unhandled trap %li at %#lx (%#lx)", cpu->regs->trapnum, cpu->regs->eip, cpu->regs->trapnum == 14 ? cpu->arch.last_pagefault : cpu->regs->errcode); }