Commit cc6d4fbc authored by Rusty Russell's avatar Rusty Russell

Introduce "hcall" pointer to indicate pending hypercall.

Currently we look at the "trapnum" to see if the Guest wants a
hypercall.  But once the hypercall is done we have to reset trapnum to
a bogus value, otherwise if we exit to userspace and return, we'd run
the same hypercall twice (that was a nasty bug to find!).

This has two main effects:

1) When Jes's patch changes the hypercall args to be a generic "struct
   hcall_args" we simply change the type of "lg->hcall".  It's set by
   arch code, so if it has to copy args or something it can do so, and
   point "hcall" into lg->arch somewhere.

2) Async hypercalls only get run when an actual hypercall is pending.
   This simplfies the code a little and is a more logical semantic.
Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
parent 4614a3a3
...@@ -198,10 +198,10 @@ int run_guest(struct lguest *lg, unsigned long __user *user) ...@@ -198,10 +198,10 @@ int run_guest(struct lguest *lg, unsigned long __user *user)
{ {
/* We stop running once the Guest is dead. */ /* We stop running once the Guest is dead. */
while (!lg->dead) { while (!lg->dead) {
/* First we run any hypercalls the Guest wants done: either in /* First we run any hypercalls the Guest wants done. */
* the hypercall ring in "struct lguest_data", or directly by if (lg->hcall)
* using int 31 (LGUEST_TRAP_ENTRY). */ do_hypercalls(lg);
do_hypercalls(lg);
/* It's possible the Guest did a SEND_DMA hypercall to the /* It's possible the Guest did a SEND_DMA hypercall to the
* Launcher, in which case we return from the read() now. */ * Launcher, in which case we return from the read() now. */
if (lg->dma_is_pending) { if (lg->dma_is_pending) {
......
...@@ -241,19 +241,6 @@ static void initialize(struct lguest *lg) ...@@ -241,19 +241,6 @@ static void initialize(struct lguest *lg)
* is one other way we can do things for the Guest, as we see in * is one other way we can do things for the Guest, as we see in
* emulate_insn(). */ * emulate_insn(). */
/*H:110 Tricky point: we mark the hypercall as "done" once we've done it.
* Normally we don't need to do this: the Guest will run again and update the
* trap number before we come back around the run_guest() loop to
* do_hypercalls().
*
* However, if we are signalled or the Guest sends DMA to the Launcher, that
* loop will exit without running the Guest. When it comes back it would try
* to re-run the hypercall. */
static void clear_hcall(struct lguest *lg)
{
lg->regs->trapnum = 255;
}
/*H:100 /*H:100
* Hypercalls * Hypercalls
* *
...@@ -262,16 +249,12 @@ static void clear_hcall(struct lguest *lg) ...@@ -262,16 +249,12 @@ static void clear_hcall(struct lguest *lg)
*/ */
void do_hypercalls(struct lguest *lg) void do_hypercalls(struct lguest *lg)
{ {
/* Not initialized yet? */ /* Not initialized yet? This hypercall must do it. */
if (unlikely(!lg->lguest_data)) { if (unlikely(!lg->lguest_data)) {
/* Did the Guest make a hypercall? We might have come back for /* Set up the "struct lguest_data" */
* some other reason (an interrupt, a different trap). */ initialize(lg);
if (lg->regs->trapnum == LGUEST_TRAP_ENTRY) { /* Hcall is done. */
/* Set up the "struct lguest_data" */ lg->hcall = NULL;
initialize(lg);
/* The hypercall is done. */
clear_hcall(lg);
}
return; return;
} }
...@@ -281,12 +264,21 @@ void do_hypercalls(struct lguest *lg) ...@@ -281,12 +264,21 @@ void do_hypercalls(struct lguest *lg)
do_async_hcalls(lg); do_async_hcalls(lg);
/* If we stopped reading the hypercall ring because the Guest did a /* If we stopped reading the hypercall ring because the Guest did a
* SEND_DMA to the Launcher, we want to return now. Otherwise if the * SEND_DMA to the Launcher, we want to return now. Otherwise we do
* Guest asked us to do a hypercall, we do it. */ * the hypercall. */
if (!lg->dma_is_pending && lg->regs->trapnum == LGUEST_TRAP_ENTRY) { if (!lg->dma_is_pending) {
do_hcall(lg, lg->regs); do_hcall(lg, lg->hcall);
/* The hypercall is done. */ /* Tricky point: we reset the hcall pointer to mark the
clear_hcall(lg); * 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 DMA 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. */
lg->hcall = NULL;
} }
} }
......
...@@ -106,6 +106,9 @@ struct lguest ...@@ -106,6 +106,9 @@ struct lguest
u32 esp1; u32 esp1;
u8 ss1; u8 ss1;
/* If a hypercall was asked for, this points to the arguments. */
struct lguest_regs *hcall;
/* Do we need to stop what we're doing and return to userspace? */ /* Do we need to stop what we're doing and return to userspace? */
int break_out; int break_out;
wait_queue_head_t break_wq; wait_queue_head_t break_wq;
......
...@@ -316,13 +316,14 @@ void lguest_arch_handle_trap(struct lguest *lg) ...@@ -316,13 +316,14 @@ void lguest_arch_handle_trap(struct lguest *lg)
return; return;
break; break;
case 32 ... 255: case 32 ... 255:
/* These values mean a real interrupt occurred, in /* These values mean a real interrupt occurred, in which case
* which case the Host handler has already been run. * the Host handler has already been run. We just do a
* We just do a friendly check if another process * friendly check if another process should now be run, then
* should now be run, then fall through to loop * return to run the Guest again */
* around: */
cond_resched(); cond_resched();
case LGUEST_TRAP_ENTRY: /* Handled before re-entering Guest */ return;
case LGUEST_TRAP_ENTRY:
lg->hcall = lg->regs;
return; return;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment