diff options
-rw-r--r-- | drivers/lguest/core.c | 8 | ||||
-rw-r--r-- | drivers/lguest/lg.h | 3 | ||||
-rw-r--r-- | drivers/lguest/lguest_user.c | 49 | ||||
-rw-r--r-- | drivers/lguest/x86/core.c | 46 | ||||
-rw-r--r-- | include/linux/lguest_launcher.h | 2 |
5 files changed, 108 insertions, 0 deletions
diff --git a/drivers/lguest/core.c b/drivers/lguest/core.c index 6590558d1d31..cdb2f9aa5860 100644 --- a/drivers/lguest/core.c +++ b/drivers/lguest/core.c @@ -208,6 +208,14 @@ void __lgwrite(struct lg_cpu *cpu, unsigned long addr, const void *b, */ int run_guest(struct lg_cpu *cpu, unsigned long __user *user) { + /* If the launcher asked for a register with LHREQ_GETREG */ + if (cpu->reg_read) { + if (put_user(*cpu->reg_read, user)) + return -EFAULT; + cpu->reg_read = NULL; + return sizeof(*cpu->reg_read); + } + /* We stop running once the Guest is dead. */ while (!cpu->lg->dead) { unsigned int irq; diff --git a/drivers/lguest/lg.h b/drivers/lguest/lg.h index 2eef40be4c04..1c98bf74fd68 100644 --- a/drivers/lguest/lg.h +++ b/drivers/lguest/lg.h @@ -52,6 +52,8 @@ struct lg_cpu { unsigned long pending_notify; /* pfn from LHCALL_NOTIFY */ + unsigned long *reg_read; /* register from LHREQ_GETREG */ + /* At end of a page shared mapped over lguest_pages in guest. */ unsigned long regs_page; struct lguest_regs *regs; @@ -210,6 +212,7 @@ void lguest_arch_handle_trap(struct lg_cpu *cpu); int lguest_arch_init_hypercalls(struct lg_cpu *cpu); int lguest_arch_do_hcall(struct lg_cpu *cpu, struct hcall_args *args); void lguest_arch_setup_regs(struct lg_cpu *cpu, unsigned long start); +unsigned long *lguest_arch_regptr(struct lg_cpu *cpu, size_t reg_off, bool any); /* <arch>/switcher.S: */ extern char start_switcher_text[], end_switcher_text[], switch_to_guest[]; diff --git a/drivers/lguest/lguest_user.c b/drivers/lguest/lguest_user.c index 4263f4cc8c55..7f14c152dd23 100644 --- a/drivers/lguest/lguest_user.c +++ b/drivers/lguest/lguest_user.c @@ -173,6 +173,51 @@ static int attach_eventfd(struct lguest *lg, const unsigned long __user *input) return err; } +/* The Launcher can get the registers, and also set some of them. */ +static int getreg_setup(struct lg_cpu *cpu, const unsigned long __user *input) +{ + unsigned long which; + + /* We re-use the ptrace structure to specify which register to read. */ + if (get_user(which, input) != 0) + return -EFAULT; + + /* + * We set up the cpu register pointer, and their next read will + * actually get the value (instead of running the guest). + * + * The last argument 'true' says we can access any register. + */ + cpu->reg_read = lguest_arch_regptr(cpu, which, true); + if (!cpu->reg_read) + return -ENOENT; + + /* And because this is a write() call, we return the length used. */ + return sizeof(unsigned long) * 2; +} + +static int setreg(struct lg_cpu *cpu, const unsigned long __user *input) +{ + unsigned long which, value, *reg; + + /* We re-use the ptrace structure to specify which register to read. */ + if (get_user(which, input) != 0) + return -EFAULT; + input++; + if (get_user(value, input) != 0) + return -EFAULT; + + /* The last argument 'false' means we can't access all registers. */ + reg = lguest_arch_regptr(cpu, which, false); + if (!reg) + return -ENOENT; + + *reg = value; + + /* And because this is a write() call, we return the length used. */ + return sizeof(unsigned long) * 3; +} + /*L:050 * Sending an interrupt is done by writing LHREQ_IRQ and an interrupt * number to /dev/lguest. @@ -434,6 +479,10 @@ static ssize_t write(struct file *file, const char __user *in, return user_send_irq(cpu, input); case LHREQ_EVENTFD: return attach_eventfd(lg, input); + case LHREQ_GETREG: + return getreg_setup(cpu, input); + case LHREQ_SETREG: + return setreg(cpu, input); default: return -EINVAL; } diff --git a/drivers/lguest/x86/core.c b/drivers/lguest/x86/core.c index 922a1acbf652..f7a16b4ea456 100644 --- a/drivers/lguest/x86/core.c +++ b/drivers/lguest/x86/core.c @@ -181,6 +181,52 @@ static void run_guest_once(struct lg_cpu *cpu, struct lguest_pages *pages) } /*:*/ +unsigned long *lguest_arch_regptr(struct lg_cpu *cpu, size_t reg_off, bool any) +{ + switch (reg_off) { + case offsetof(struct pt_regs, bx): + return &cpu->regs->ebx; + case offsetof(struct pt_regs, cx): + return &cpu->regs->ecx; + case offsetof(struct pt_regs, dx): + return &cpu->regs->edx; + case offsetof(struct pt_regs, si): + return &cpu->regs->esi; + case offsetof(struct pt_regs, di): + return &cpu->regs->edi; + case offsetof(struct pt_regs, bp): + return &cpu->regs->ebp; + case offsetof(struct pt_regs, ax): + return &cpu->regs->eax; + case offsetof(struct pt_regs, ip): + return &cpu->regs->eip; + case offsetof(struct pt_regs, sp): + return &cpu->regs->esp; + } + + /* Launcher can read these, but we don't allow any setting. */ + if (any) { + switch (reg_off) { + case offsetof(struct pt_regs, ds): + return &cpu->regs->ds; + case offsetof(struct pt_regs, es): + return &cpu->regs->es; + case offsetof(struct pt_regs, fs): + return &cpu->regs->fs; + case offsetof(struct pt_regs, gs): + return &cpu->regs->gs; + case offsetof(struct pt_regs, cs): + return &cpu->regs->cs; + case offsetof(struct pt_regs, flags): + return &cpu->regs->eflags; + case offsetof(struct pt_regs, ss): + return &cpu->regs->ss; + } + } + + return NULL; +} + /*M:002 * There are hooks in the scheduler which we can register to tell when we * get kicked off the CPU (preempt_notifier_register()). This would allow us diff --git a/include/linux/lguest_launcher.h b/include/linux/lguest_launcher.h index 495203ff221c..f27cae27b0c1 100644 --- a/include/linux/lguest_launcher.h +++ b/include/linux/lguest_launcher.h @@ -63,6 +63,8 @@ enum lguest_req LHREQ_IRQ, /* + irq */ LHREQ_BREAK, /* No longer used */ LHREQ_EVENTFD, /* + address, fd. */ + LHREQ_GETREG, /* + offset within struct pt_regs (then read value). */ + LHREQ_SETREG, /* + offset within struct pt_regs, value. */ }; /* |