Ejemplo n.º 1
0
/* Last of all, we look at what happens first of all.  The very first time the
 * Guest makes a hypercall, we end up here to set things up: */
static void initialize(struct lg_cpu *cpu)
{
    /* You can't do anything until you're initialized.  The Guest knows the
     * rules, so we're unforgiving here. */
    if (cpu->hcall->arg0 != LHCALL_LGUEST_INIT) {
        kill_guest(cpu, "hypercall %li before INIT", cpu->hcall->arg0);
        return;
    }

    if (lguest_arch_init_hypercalls(cpu))
        kill_guest(cpu, "bad guest page %p", cpu->lg->lguest_data);

    /* The Guest tells us where we're not to deliver interrupts by putting
     * the range of addresses into "struct lguest_data". */
    if (get_user(cpu->lg->noirq_start, &cpu->lg->lguest_data->noirq_start)
        || get_user(cpu->lg->noirq_end, &cpu->lg->lguest_data->noirq_end))
        kill_guest(cpu, "bad guest page %p", cpu->lg->lguest_data);

    /* We write the current time into the Guest's data page once so it can
     * set its clock. */
    write_timestamp(cpu);

    /* page_tables.c will also do some setup. */
    page_table_guest_data_init(cpu);

    /* This is the one case where the above accesses might have been the
     * first write to a Guest page.  This may have caused a copy-on-write
     * fault, but the old page might be (read-only) in the Guest
     * pagetable. */
    guest_pagetable_clear_all(cpu);
}
Ejemplo n.º 2
0
/*
 * Our hypercalls mechanism used to be based on direct software interrupts.
 * After Anthony's "Refactor hypercall infrastructure" kvm patch, we decided to
 * change over to using kvm hypercalls.
 *
 * KVM_HYPERCALL is actually a "vmcall" instruction, which generates an invalid
 * opcode fault (fault 6) on non-VT cpus, so the easiest solution seemed to be
 * an *emulation approach*: if the fault was really produced by an hypercall
 * (is_hypercall() does exactly this check), we can just call the corresponding
 * hypercall host implementation function.
 *
 * But these invalid opcode faults are notably slower than software interrupts.
 * So we implemented the *patching (or rewriting) approach*: every time we hit
 * the KVM_HYPERCALL opcode in Guest code, we patch it to the old "int 0x1f"
 * opcode, so next time the Guest calls this hypercall it will use the
 * faster trap mechanism.
 *
 * Matias even benchmarked it to convince you: this shows the average cycle
 * cost of a hypercall.  For each alternative solution mentioned above we've
 * made 5 runs of the benchmark:
 *
 * 1) direct software interrupt: 2915, 2789, 2764, 2721, 2898
 * 2) emulation technique: 3410, 3681, 3466, 3392, 3780
 * 3) patching (rewrite) technique: 2977, 2975, 2891, 2637, 2884
 *
 * One two-line function is worth a 20% hypercall speed boost!
 */
static void rewrite_hypercall(struct lg_cpu *cpu)
{
	/*
	 * This are the opcodes we use to patch the Guest.  The opcode for "int
	 * $0x1f" is "0xcd 0x1f" but vmcall instruction is 3 bytes long, so we
	 * complete the sequence with a NOP (0x90).
	 */
	u8 insn[3] = {0xcd, 0x1f, 0x90};

	__lgwrite(cpu, guest_pa(cpu, cpu->regs->eip), insn, sizeof(insn));
	/*
	 * The above write might have caused a copy of that page to be made
	 * (if it was read-only).  We need to make sure the Guest has
	 * up-to-date pagetables.  As this doesn't happen often, we can just
	 * drop them all.
	 */
	guest_pagetable_clear_all(cpu);
}
static void initialize(struct lg_cpu *cpu)
{
	if (cpu->hcall->arg0 != LHCALL_LGUEST_INIT) {
		kill_guest(cpu, "hypercall %li before INIT", cpu->hcall->arg0);
		return;
	}

	if (lguest_arch_init_hypercalls(cpu))
		kill_guest(cpu, "bad guest page %p", cpu->lg->lguest_data);

	if (get_user(cpu->lg->noirq_start, &cpu->lg->lguest_data->noirq_start)
	    || get_user(cpu->lg->noirq_end, &cpu->lg->lguest_data->noirq_end))
		kill_guest(cpu, "bad guest page %p", cpu->lg->lguest_data);

	write_timestamp(cpu);

	
	page_table_guest_data_init(cpu);

	guest_pagetable_clear_all(cpu);
}
Ejemplo n.º 4
0
/* For setting a mid-level, we just throw everything away.  It's easy. */
void guest_set_pmd(struct lguest *lg, unsigned long pmdp, u32 idx)
{
	guest_pagetable_clear_all(&lg->cpus[0]);
}
Ejemplo n.º 5
0
/*H:120 This is the core hypercall routine: where the Guest gets what it wants.
 * Or gets killed.  Or, in the case of LHCALL_SHUTDOWN, both. */
static void do_hcall(struct lg_cpu *cpu, struct hcall_args *args)
{
    switch (args->arg0) {
    case LHCALL_FLUSH_ASYNC:
        /* This call does nothing, except by breaking out of the Guest
         * it makes us process all the asynchronous hypercalls. */
        break;
    case LHCALL_LGUEST_INIT:
        /* You can't get here unless you're already initialized.  Don't
         * do that. */
        kill_guest(cpu, "already have lguest_data");
        break;
    case LHCALL_SHUTDOWN: {
        /* Shutdown is such a trivial hypercall that we do it in four
         * lines right here. */
        char msg[128];
        /* If the lgread fails, it will call kill_guest() itself; the
         * kill_guest() with the message will be ignored. */
        __lgread(cpu, msg, args->arg1, sizeof(msg));
        msg[sizeof(msg)-1] = '\0';
        kill_guest(cpu, "CRASH: %s", msg);
        if (args->arg2 == LGUEST_SHUTDOWN_RESTART)
            cpu->lg->dead = ERR_PTR(-ERESTART);
        break;
    }
    case LHCALL_FLUSH_TLB:
        /* FLUSH_TLB comes in two flavors, depending on the
         * argument: */
        if (args->arg1)
            guest_pagetable_clear_all(cpu);
        else
            guest_pagetable_flush_user(cpu);
        break;

    /* All these calls simply pass the arguments through to the right
     * routines. */
    case LHCALL_NEW_PGTABLE:
        guest_new_pagetable(cpu, args->arg1);
        break;
    case LHCALL_SET_STACK:
        guest_set_stack(cpu, args->arg1, args->arg2, args->arg3);
        break;
    case LHCALL_SET_PTE:
        guest_set_pte(cpu, args->arg1, args->arg2, __pte(args->arg3));
        break;
    case LHCALL_SET_PMD:
        guest_set_pmd(cpu->lg, args->arg1, args->arg2);
        break;
    case LHCALL_SET_CLOCKEVENT:
        guest_set_clockevent(cpu, args->arg1);
        break;
    case LHCALL_TS:
        /* This sets the TS flag, as we saw used in run_guest(). */
        cpu->ts = args->arg1;
        break;
    case LHCALL_HALT:
        /* Similarly, this sets the halted flag for run_guest(). */
        cpu->halted = 1;
        break;
    case LHCALL_NOTIFY:
        cpu->pending_notify = args->arg1;
        break;
    default:
        /* It should be an architecture-specific hypercall. */
        if (lguest_arch_do_hcall(cpu, args))
            kill_guest(cpu, "Bad hypercall %li\n", args->arg0);
    }
}
static void do_hcall(struct lg_cpu *cpu, struct hcall_args *args)
{
	switch (args->arg0) {
	case LHCALL_FLUSH_ASYNC:
		break;
	case LHCALL_SEND_INTERRUPTS:
		break;
	case LHCALL_LGUEST_INIT:
		kill_guest(cpu, "already have lguest_data");
		break;
	case LHCALL_SHUTDOWN: {
		char msg[128];
		__lgread(cpu, msg, args->arg1, sizeof(msg));
		msg[sizeof(msg)-1] = '\0';
		kill_guest(cpu, "CRASH: %s", msg);
		if (args->arg2 == LGUEST_SHUTDOWN_RESTART)
			cpu->lg->dead = ERR_PTR(-ERESTART);
		break;
	}
	case LHCALL_FLUSH_TLB:
		
		if (args->arg1)
			guest_pagetable_clear_all(cpu);
		else
			guest_pagetable_flush_user(cpu);
		break;

	case LHCALL_NEW_PGTABLE:
		guest_new_pagetable(cpu, args->arg1);
		break;
	case LHCALL_SET_STACK:
		guest_set_stack(cpu, args->arg1, args->arg2, args->arg3);
		break;
	case LHCALL_SET_PTE:
#ifdef CONFIG_X86_PAE
		guest_set_pte(cpu, args->arg1, args->arg2,
				__pte(args->arg3 | (u64)args->arg4 << 32));
#else
		guest_set_pte(cpu, args->arg1, args->arg2, __pte(args->arg3));
#endif
		break;
	case LHCALL_SET_PGD:
		guest_set_pgd(cpu->lg, args->arg1, args->arg2);
		break;
#ifdef CONFIG_X86_PAE
	case LHCALL_SET_PMD:
		guest_set_pmd(cpu->lg, args->arg1, args->arg2);
		break;
#endif
	case LHCALL_SET_CLOCKEVENT:
		guest_set_clockevent(cpu, args->arg1);
		break;
	case LHCALL_TS:
		
		cpu->ts = args->arg1;
		break;
	case LHCALL_HALT:
		
		cpu->halted = 1;
		break;
	case LHCALL_NOTIFY:
		cpu->pending_notify = args->arg1;
		break;
	default:
		
		if (lguest_arch_do_hcall(cpu, args))
			kill_guest(cpu, "Bad hypercall %li\n", args->arg0);
	}
}