From 3c6b5bfa3cf3b4057788e08482a468cc3bc00780 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 22 Oct 2007 11:03:26 +1000 Subject: Introduce guest mem offset, static link example launcher In order to avoid problematic special linking of the Launcher, we give the Host an offset: this means we can use any memory region in the Launcher as Guest memory rather than insisting on mmap() at 0. The result is quite pleasing: a number of casts are replaced with simple additions. Signed-off-by: Rusty Russell --- drivers/lguest/core.c | 22 ++++++++++------------ drivers/lguest/hypercalls.c | 15 +++++++++------ drivers/lguest/io.c | 18 ++++++++++-------- drivers/lguest/lg.h | 3 +++ drivers/lguest/lguest_user.c | 23 +++++++++++++---------- drivers/lguest/page_tables.c | 7 +++++-- 6 files changed, 50 insertions(+), 38 deletions(-) (limited to 'drivers/lguest') diff --git a/drivers/lguest/core.c b/drivers/lguest/core.c index a0788c1..eb95860 100644 --- a/drivers/lguest/core.c +++ b/drivers/lguest/core.c @@ -325,8 +325,8 @@ static int emulate_insn(struct lguest *lg) * Dealing With Guest Memory. * * When the Guest gives us (what it thinks is) a physical address, we can use - * the normal copy_from_user() & copy_to_user() on that address: remember, - * Guest physical == Launcher virtual. + * the normal copy_from_user() & copy_to_user() on the corresponding place in + * the memory region allocated by the Launcher. * * But we can't trust the Guest: it might be trying to access the Launcher * code. We have to check that the range is below the pfn_limit the Launcher @@ -348,8 +348,8 @@ u32 lgread_u32(struct lguest *lg, unsigned long addr) /* Don't let them access lguest binary. */ if (!lguest_address_ok(lg, addr, sizeof(val)) - || get_user(val, (u32 __user *)addr) != 0) - kill_guest(lg, "bad read address %#lx", addr); + || get_user(val, (u32 *)(lg->mem_base + addr)) != 0) + kill_guest(lg, "bad read address %#lx: pfn_limit=%u membase=%p", addr, lg->pfn_limit, lg->mem_base); return val; } @@ -357,7 +357,7 @@ u32 lgread_u32(struct lguest *lg, unsigned long addr) void lgwrite_u32(struct lguest *lg, unsigned long addr, u32 val) { if (!lguest_address_ok(lg, addr, sizeof(val)) - || put_user(val, (u32 __user *)addr) != 0) + || put_user(val, (u32 *)(lg->mem_base + addr)) != 0) kill_guest(lg, "bad write address %#lx", addr); } @@ -367,7 +367,7 @@ void lgwrite_u32(struct lguest *lg, unsigned long addr, u32 val) void lgread(struct lguest *lg, void *b, unsigned long addr, unsigned bytes) { if (!lguest_address_ok(lg, addr, bytes) - || copy_from_user(b, (void __user *)addr, bytes) != 0) { + || copy_from_user(b, lg->mem_base + addr, bytes) != 0) { /* copy_from_user should do this, but as we rely on it... */ memset(b, 0, bytes); kill_guest(lg, "bad read address %#lx len %u", addr, bytes); @@ -379,7 +379,7 @@ void lgwrite(struct lguest *lg, unsigned long addr, const void *b, unsigned bytes) { if (!lguest_address_ok(lg, addr, bytes) - || copy_to_user((void __user *)addr, b, bytes) != 0) + || copy_to_user(lg->mem_base + addr, b, bytes) != 0) kill_guest(lg, "bad write address %#lx len %u", addr, bytes); } /* (end of memory access helper routines) :*/ @@ -616,11 +616,9 @@ int run_guest(struct lguest *lg, unsigned long __user *user) * * Note that if the Guest were really messed up, this * could happen before it's done the INITIALIZE - * hypercall, so lg->lguest_data will be NULL, so - * &lg->lguest_data->cr2 will be address 8. Writing - * into that address won't hurt the Host at all, - * though. */ - if (put_user(cr2, &lg->lguest_data->cr2)) + * hypercall, so lg->lguest_data will be NULL */ + if (lg->lguest_data + && put_user(cr2, &lg->lguest_data->cr2)) kill_guest(lg, "Writing cr2"); break; case 7: /* We've intercepted a Device Not Available fault. */ diff --git a/drivers/lguest/hypercalls.c b/drivers/lguest/hypercalls.c index 5ecd60b..02e67b4 100644 --- a/drivers/lguest/hypercalls.c +++ b/drivers/lguest/hypercalls.c @@ -205,16 +205,19 @@ static void initialize(struct lguest *lg) tsc_speed = 0; /* The pointer to the Guest's "struct lguest_data" is the only - * argument. */ - lg->lguest_data = (struct lguest_data __user *)lg->regs->edx; - /* If we check the address they gave is OK now, we can simply - * copy_to_user/from_user from now on rather than using lgread/lgwrite. - * I put this in to show that I'm not immune to writing stupid - * optimizations. */ + * argument. We check that address now. */ if (!lguest_address_ok(lg, lg->regs->edx, sizeof(*lg->lguest_data))) { kill_guest(lg, "bad guest page %p", lg->lguest_data); return; } + + /* Having checked it, we simply set lg->lguest_data to point straight + * into the Launcher's memory at the right place and then use + * copy_to_user/from_user from now on, instead of lgread/write. I put + * this in to show that I'm not immune to writing stupid + * optimizations. */ + lg->lguest_data = lg->mem_base + lg->regs->edx; + /* The Guest tells us where we're not to deliver interrupts by putting * the range of addresses into "struct lguest_data". */ if (get_user(lg->noirq_start, &lg->lguest_data->noirq_start) diff --git a/drivers/lguest/io.c b/drivers/lguest/io.c index ea68613..3a84533 100644 --- a/drivers/lguest/io.c +++ b/drivers/lguest/io.c @@ -186,7 +186,7 @@ int bind_dma(struct lguest *lg, * we're doing this. */ mutex_lock(&lguest_lock); down_read(fshared); - if (get_futex_key((u32 __user *)ukey, fshared, &key) != 0) { + if (get_futex_key(lg->mem_base + ukey, fshared, &key) != 0) { kill_guest(lg, "bad dma key %#lx", ukey); goto unlock; } @@ -247,7 +247,8 @@ static int lgread_other(struct lguest *lg, void *buf, u32 addr, unsigned bytes) { if (!lguest_address_ok(lg, addr, bytes) - || access_process_vm(lg->tsk, addr, buf, bytes, 0) != bytes) { + || access_process_vm(lg->tsk, (unsigned long)lg->mem_base + addr, + buf, bytes, 0) != bytes) { memset(buf, 0, bytes); kill_guest(lg, "bad address in registered DMA struct"); return 0; @@ -261,8 +262,8 @@ static int lgwrite_other(struct lguest *lg, u32 addr, const void *buf, unsigned bytes) { if (!lguest_address_ok(lg, addr, bytes) - || (access_process_vm(lg->tsk, addr, (void *)buf, bytes, 1) - != bytes)) { + || access_process_vm(lg->tsk, (unsigned long)lg->mem_base + addr, + (void *)buf, bytes, 1) != bytes) { kill_guest(lg, "bad address writing to registered DMA"); return 0; } @@ -318,7 +319,7 @@ static u32 copy_data(struct lguest *srclg, * copy_to_user_page(), and some arch's seem to need special * flushes. x86 is fine. */ if (copy_from_user(maddr + (dst->addr[di] + dstoff)%PAGE_SIZE, - (void __user *)src->addr[si], len) != 0) { + srclg->mem_base+src->addr[si], len) != 0) { /* If a copy failed, it's the source's fault. */ kill_guest(srclg, "bad address in sending DMA"); totlen = 0; @@ -377,7 +378,8 @@ static u32 do_dma(struct lguest *srclg, const struct lguest_dma *src, * number of pages. Note that we're holding the destination's * mmap_sem, as get_user_pages() requires. */ if (get_user_pages(dstlg->tsk, dstlg->mm, - dst->addr[i], 1, 1, 1, pages+i, NULL) + (unsigned long)dstlg->mem_base+dst->addr[i], + 1, 1, 1, pages+i, NULL) != 1) { /* This means the destination gave us a bogus buffer */ kill_guest(dstlg, "Error mapping DMA pages"); @@ -493,7 +495,7 @@ again: mutex_lock(&lguest_lock); down_read(fshared); /* Get the futex key for the key the Guest gave us */ - if (get_futex_key((u32 __user *)ukey, fshared, &key) != 0) { + if (get_futex_key(lg->mem_base + ukey, fshared, &key) != 0) { kill_guest(lg, "bad sending DMA key"); goto unlock; } @@ -584,7 +586,7 @@ unsigned long get_dma_buffer(struct lguest *lg, /* This can fail if it's not a valid address, or if the address is not * divisible by 4 (the futex code needs that, we don't really). */ - if (get_futex_key((u32 __user *)ukey, fshared, &key) != 0) { + if (get_futex_key(lg->mem_base + ukey, fshared, &key) != 0) { kill_guest(lg, "bad registered DMA buffer"); goto unlock; } diff --git a/drivers/lguest/lg.h b/drivers/lguest/lg.h index 399eab8..54f2c24 100644 --- a/drivers/lguest/lg.h +++ b/drivers/lguest/lg.h @@ -142,6 +142,9 @@ struct lguest struct mm_struct *mm; /* == tsk->mm, but that becomes NULL on exit */ u16 guestid; u32 pfn_limit; + /* This provides the offset to the base of guest-physical + * memory in the Launcher. */ + void __user *mem_base; u32 page_offset; u32 cr2; int halted; diff --git a/drivers/lguest/lguest_user.c b/drivers/lguest/lguest_user.c index 80d1b58..816d4d1 100644 --- a/drivers/lguest/lguest_user.c +++ b/drivers/lguest/lguest_user.c @@ -1,9 +1,9 @@ /*P:200 This contains all the /dev/lguest code, whereby the userspace launcher * controls and communicates with the Guest. For example, the first write will - * tell us the memory size, pagetable, entry point and kernel address offset. - * A read will run the Guest until a signal is pending (-EINTR), or the Guest - * does a DMA out to the Launcher. Writes are also used to get a DMA buffer - * registered by the Guest and to send the Guest an interrupt. :*/ + * tell us the Guest's memory layout, pagetable, entry point and kernel address + * offset. A read will run the Guest until something happens, such as a signal + * or the Guest doing a DMA out to the Launcher. Writes are also used to get a + * DMA buffer registered by the Guest and to send the Guest an interrupt. :*/ #include #include #include @@ -142,9 +142,11 @@ static ssize_t read(struct file *file, char __user *user, size_t size,loff_t*o) return run_guest(lg, (unsigned long __user *)user); } -/*L:020 The initialization write supplies 4 32-bit values (in addition to the +/*L:020 The initialization write supplies 5 32-bit values (in addition to the * 32-bit LHREQ_INITIALIZE value). These are: * + * base: The start of the Guest-physical memory inside the Launcher memory. + * * pfnlimit: The highest (Guest-physical) page number the Guest should be * allowed to access. The Launcher has to live in Guest memory, so it sets * this to ensure the Guest can't reach it. @@ -166,7 +168,7 @@ static int initialize(struct file *file, const u32 __user *input) * Guest. */ struct lguest *lg; int err, i; - u32 args[4]; + u32 args[5]; /* We grab the Big Lguest lock, which protects the global array * "lguests" and multiple simultaneous initializations. */ @@ -194,8 +196,9 @@ static int initialize(struct file *file, const u32 __user *input) /* Populate the easy fields of our "struct lguest" */ lg->guestid = i; - lg->pfn_limit = args[0]; - lg->page_offset = args[3]; + lg->mem_base = (void __user *)(long)args[0]; + lg->pfn_limit = args[1]; + lg->page_offset = args[4]; /* We need a complete page for the Guest registers: they are accessible * to the Guest and we can only grant it access to whole pages. */ @@ -210,13 +213,13 @@ static int initialize(struct file *file, const u32 __user *input) /* Initialize the Guest's shadow page tables, using the toplevel * address the Launcher gave us. This allocates memory, so can * fail. */ - err = init_guest_pagetable(lg, args[1]); + err = init_guest_pagetable(lg, args[2]); if (err) goto free_regs; /* Now we initialize the Guest's registers, handing it the start * address. */ - setup_regs(lg->regs, args[2]); + setup_regs(lg->regs, args[3]); /* There are a couple of GDT entries the Guest expects when first * booting. */ diff --git a/drivers/lguest/page_tables.c b/drivers/lguest/page_tables.c index b7a924a..9cd2fac 100644 --- a/drivers/lguest/page_tables.c +++ b/drivers/lguest/page_tables.c @@ -152,7 +152,7 @@ static unsigned long get_pfn(unsigned long virtpfn, int write) static spte_t gpte_to_spte(struct lguest *lg, gpte_t gpte, int write) { spte_t spte; - unsigned long pfn; + unsigned long pfn, base; /* The Guest sets the global flag, because it thinks that it is using * PGE. We only told it to use PGE so it would tell us whether it was @@ -160,11 +160,14 @@ static spte_t gpte_to_spte(struct lguest *lg, gpte_t gpte, int write) * use the global bit, so throw it away. */ spte.flags = (gpte.flags & ~_PAGE_GLOBAL); + /* The Guest's pages are offset inside the Launcher. */ + base = (unsigned long)lg->mem_base / PAGE_SIZE; + /* We need a temporary "unsigned long" variable to hold the answer from * get_pfn(), because it returns 0xFFFFFFFF on failure, which wouldn't * fit in spte.pfn. get_pfn() finds the real physical number of the * page, given the virtual number. */ - pfn = get_pfn(gpte.pfn, write); + pfn = get_pfn(base + gpte.pfn, write); if (pfn == -1UL) { kill_guest(lg, "failed to get page %u", gpte.pfn); /* When we destroy the Guest, we'll go through the shadow page -- cgit v1.1