/*H:100 * Hypercalls * * Remember from the Guest, hypercalls come in two flavors: normal and * asynchronous. This file handles both of types. */ void do_hypercalls(struct lg_cpu *cpu) { /* Not initialized yet? This hypercall must do it. */ if (unlikely(!cpu->lg->lguest_data)) { /* Set up the "struct lguest_data" */ initialize(cpu); /* Hcall is done. */ cpu->hcall = NULL; return; } /* The Guest has initialized. * * Look in the hypercall ring for the async hypercalls: */ do_async_hcalls(cpu); /* If we stopped reading the hypercall ring because the Guest did a * NOTIFY to the Launcher, we want to return now. Otherwise we do * the hypercall. */ if (!cpu->pending_notify) { do_hcall(cpu, cpu->hcall); /* Tricky point: we reset the hcall pointer to mark the * hypercall as "done". We use the hcall pointer rather than * the trap number to indicate a hypercall is pending. * Normally it doesn't matter: the Guest will run again and * update the trap number before we come back here. * * However, if we are signalled or the Guest sends I/O to the * Launcher, the run_guest() loop will exit without running the * Guest. When it comes back it would try to re-run the * hypercall. Finding that bug sucked. */ cpu->hcall = NULL; } }
/*H:124 * Asynchronous hypercalls are easy: we just look in the array in the * Guest's "struct lguest_data" to see if any new ones are marked "ready". * * We are careful to do these in order: obviously we respect the order the * Guest put them in the ring, but we also promise the Guest that they will * happen before any normal hypercall (which is why we check this before * checking for a normal hcall). */ static void do_async_hcalls(struct lg_cpu *cpu) { unsigned int i; u8 st[LHCALL_RING_SIZE]; /* For simplicity, we copy the entire call status array in at once. */ if (copy_from_user(&st, &cpu->lg->lguest_data->hcall_status, sizeof(st))) return; /* We process "struct lguest_data"s hcalls[] ring once. */ for (i = 0; i < ARRAY_SIZE(st); i++) { struct hcall_args args; /* * We remember where we were up to from last time. This makes * sure that the hypercalls are done in the order the Guest * places them in the ring. */ unsigned int n = cpu->next_hcall; /* 0xFF means there's no call here (yet). */ if (st[n] == 0xFF) break; /* * OK, we have hypercall. Increment the "next_hcall" cursor, * and wrap back to 0 if we reach the end. */ if (++cpu->next_hcall == LHCALL_RING_SIZE) cpu->next_hcall = 0; /* * Copy the hypercall arguments into a local copy of the * hcall_args struct. */ if (copy_from_user(&args, &cpu->lg->lguest_data->hcalls[n], sizeof(struct hcall_args))) { kill_guest(cpu, "Fetching async hypercalls"); break; } /* Do the hypercall, same as a normal one. */ do_hcall(cpu, &args); /* Mark the hypercall done. */ if (put_user(0xFF, &cpu->lg->lguest_data->hcall_status[n])) { kill_guest(cpu, "Writing result for async hypercall"); break; } /* * Stop doing hypercalls if they want to notify the Launcher: * it needs to service this first. */ if (cpu->pending_notify) break; } }
void do_hypercalls(struct lg_cpu *cpu) { if (unlikely(!cpu->lg->lguest_data)) { initialize(cpu); cpu->hcall = NULL; return; } do_async_hcalls(cpu); if (!cpu->pending_notify) { do_hcall(cpu, cpu->hcall); cpu->hcall = NULL; } }
static void do_async_hcalls(struct lg_cpu *cpu) { unsigned int i; u8 st[LHCALL_RING_SIZE]; if (copy_from_user(&st, &cpu->lg->lguest_data->hcall_status, sizeof(st))) return; for (i = 0; i < ARRAY_SIZE(st); i++) { struct hcall_args args; unsigned int n = cpu->next_hcall; if (st[n] == 0xFF) break; if (++cpu->next_hcall == LHCALL_RING_SIZE) cpu->next_hcall = 0; if (copy_from_user(&args, &cpu->lg->lguest_data->hcalls[n], sizeof(struct hcall_args))) { kill_guest(cpu, "Fetching async hypercalls"); break; } do_hcall(cpu, &args); if (put_user(0xFF, &cpu->lg->lguest_data->hcall_status[n])) { kill_guest(cpu, "Writing result for async hypercall"); break; } if (cpu->pending_notify) break; } }