Commit 3c6b5bfa authored by Rusty Russell's avatar Rusty Russell

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: default avatarRusty Russell <rusty@rustcorp.com.au>
parent 6649bb7a
# This creates the demonstration utility "lguest" which runs a Linux guest. # This creates the demonstration utility "lguest" which runs a Linux guest.
CFLAGS:=-Wall -Wmissing-declarations -Wmissing-prototypes -O3 -I../../include
# For those people that have a separate object dir, look there for .config
KBUILD_OUTPUT := ../..
ifdef O
ifeq ("$(origin O)", "command line")
KBUILD_OUTPUT := $(O)
endif
endif
# We rely on CONFIG_PAGE_OFFSET to know where to put lguest binary.
include $(KBUILD_OUTPUT)/.config
LGUEST_GUEST_TOP := ($(CONFIG_PAGE_OFFSET) - 0x08000000)
CFLAGS:=-Wall -Wmissing-declarations -Wmissing-prototypes -O3 -I../../include -Wl,-T,lguest.lds
LDLIBS:=-lz LDLIBS:=-lz
# Removing this works for some versions of ld.so (eg. Ubuntu Feisty) and
# not others (eg. FC7).
LDFLAGS+=-static
all: lguest.lds lguest
# The linker script on x86 is so complex the only way of creating one all: lguest
# which will link our binary in the right place is to mangle the
# default one.
lguest.lds:
$(LD) --verbose | awk '/^==========/ { PRINT=1; next; } /SIZEOF_HEADERS/ { gsub(/0x[0-9A-F]*/, "$(LGUEST_GUEST_TOP)") } { if (PRINT) print $$0; }' > $@
clean: clean:
rm -f lguest.lds lguest rm -f lguest
This diff is collapsed.
...@@ -325,8 +325,8 @@ static int emulate_insn(struct lguest *lg) ...@@ -325,8 +325,8 @@ static int emulate_insn(struct lguest *lg)
* Dealing With Guest Memory. * Dealing With Guest Memory.
* *
* When the Guest gives us (what it thinks is) a physical address, we can use * 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, * the normal copy_from_user() & copy_to_user() on the corresponding place in
* Guest physical == Launcher virtual. * the memory region allocated by the Launcher.
* *
* But we can't trust the Guest: it might be trying to access 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 * 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) ...@@ -348,8 +348,8 @@ u32 lgread_u32(struct lguest *lg, unsigned long addr)
/* Don't let them access lguest binary. */ /* Don't let them access lguest binary. */
if (!lguest_address_ok(lg, addr, sizeof(val)) if (!lguest_address_ok(lg, addr, sizeof(val))
|| get_user(val, (u32 __user *)addr) != 0) || get_user(val, (u32 *)(lg->mem_base + addr)) != 0)
kill_guest(lg, "bad read address %#lx", addr); kill_guest(lg, "bad read address %#lx: pfn_limit=%u membase=%p", addr, lg->pfn_limit, lg->mem_base);
return val; return val;
} }
...@@ -357,7 +357,7 @@ u32 lgread_u32(struct lguest *lg, unsigned long addr) ...@@ -357,7 +357,7 @@ u32 lgread_u32(struct lguest *lg, unsigned long addr)
void lgwrite_u32(struct lguest *lg, unsigned long addr, u32 val) void lgwrite_u32(struct lguest *lg, unsigned long addr, u32 val)
{ {
if (!lguest_address_ok(lg, addr, sizeof(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); kill_guest(lg, "bad write address %#lx", addr);
} }
...@@ -367,7 +367,7 @@ void lgwrite_u32(struct lguest *lg, unsigned long addr, u32 val) ...@@ -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) void lgread(struct lguest *lg, void *b, unsigned long addr, unsigned bytes)
{ {
if (!lguest_address_ok(lg, addr, 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... */ /* copy_from_user should do this, but as we rely on it... */
memset(b, 0, bytes); memset(b, 0, bytes);
kill_guest(lg, "bad read address %#lx len %u", addr, 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, ...@@ -379,7 +379,7 @@ void lgwrite(struct lguest *lg, unsigned long addr, const void *b,
unsigned bytes) unsigned bytes)
{ {
if (!lguest_address_ok(lg, addr, 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); kill_guest(lg, "bad write address %#lx len %u", addr, bytes);
} }
/* (end of memory access helper routines) :*/ /* (end of memory access helper routines) :*/
...@@ -616,11 +616,9 @@ int run_guest(struct lguest *lg, unsigned long __user *user) ...@@ -616,11 +616,9 @@ int run_guest(struct lguest *lg, unsigned long __user *user)
* *
* Note that if the Guest were really messed up, this * Note that if the Guest were really messed up, this
* could happen before it's done the INITIALIZE * could happen before it's done the INITIALIZE
* hypercall, so lg->lguest_data will be NULL, so * hypercall, so lg->lguest_data will be NULL */
* &lg->lguest_data->cr2 will be address 8. Writing if (lg->lguest_data
* into that address won't hurt the Host at all, && put_user(cr2, &lg->lguest_data->cr2))
* though. */
if (put_user(cr2, &lg->lguest_data->cr2))
kill_guest(lg, "Writing cr2"); kill_guest(lg, "Writing cr2");
break; break;
case 7: /* We've intercepted a Device Not Available fault. */ case 7: /* We've intercepted a Device Not Available fault. */
......
...@@ -205,16 +205,19 @@ static void initialize(struct lguest *lg) ...@@ -205,16 +205,19 @@ static void initialize(struct lguest *lg)
tsc_speed = 0; tsc_speed = 0;
/* The pointer to the Guest's "struct lguest_data" is the only /* The pointer to the Guest's "struct lguest_data" is the only
* argument. */ * argument. We check that address now. */
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. */
if (!lguest_address_ok(lg, lg->regs->edx, sizeof(*lg->lguest_data))) { if (!lguest_address_ok(lg, lg->regs->edx, sizeof(*lg->lguest_data))) {
kill_guest(lg, "bad guest page %p", lg->lguest_data); kill_guest(lg, "bad guest page %p", lg->lguest_data);
return; 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 Guest tells us where we're not to deliver interrupts by putting
* the range of addresses into "struct lguest_data". */ * the range of addresses into "struct lguest_data". */
if (get_user(lg->noirq_start, &lg->lguest_data->noirq_start) if (get_user(lg->noirq_start, &lg->lguest_data->noirq_start)
......
...@@ -186,7 +186,7 @@ int bind_dma(struct lguest *lg, ...@@ -186,7 +186,7 @@ int bind_dma(struct lguest *lg,
* we're doing this. */ * we're doing this. */
mutex_lock(&lguest_lock); mutex_lock(&lguest_lock);
down_read(fshared); 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); kill_guest(lg, "bad dma key %#lx", ukey);
goto unlock; goto unlock;
} }
...@@ -247,7 +247,8 @@ static int lgread_other(struct lguest *lg, ...@@ -247,7 +247,8 @@ static int lgread_other(struct lguest *lg,
void *buf, u32 addr, unsigned bytes) void *buf, u32 addr, unsigned bytes)
{ {
if (!lguest_address_ok(lg, addr, 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); memset(buf, 0, bytes);
kill_guest(lg, "bad address in registered DMA struct"); kill_guest(lg, "bad address in registered DMA struct");
return 0; return 0;
...@@ -261,8 +262,8 @@ static int lgwrite_other(struct lguest *lg, u32 addr, ...@@ -261,8 +262,8 @@ static int lgwrite_other(struct lguest *lg, u32 addr,
const void *buf, unsigned bytes) const void *buf, unsigned bytes)
{ {
if (!lguest_address_ok(lg, addr, bytes) if (!lguest_address_ok(lg, addr, bytes)
|| (access_process_vm(lg->tsk, addr, (void *)buf, bytes, 1) || access_process_vm(lg->tsk, (unsigned long)lg->mem_base + addr,
!= bytes)) { (void *)buf, bytes, 1) != bytes) {
kill_guest(lg, "bad address writing to registered DMA"); kill_guest(lg, "bad address writing to registered DMA");
return 0; return 0;
} }
...@@ -318,7 +319,7 @@ static u32 copy_data(struct lguest *srclg, ...@@ -318,7 +319,7 @@ static u32 copy_data(struct lguest *srclg,
* copy_to_user_page(), and some arch's seem to need special * copy_to_user_page(), and some arch's seem to need special
* flushes. x86 is fine. */ * flushes. x86 is fine. */
if (copy_from_user(maddr + (dst->addr[di] + dstoff)%PAGE_SIZE, 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. */ /* If a copy failed, it's the source's fault. */
kill_guest(srclg, "bad address in sending DMA"); kill_guest(srclg, "bad address in sending DMA");
totlen = 0; totlen = 0;
...@@ -377,7 +378,8 @@ static u32 do_dma(struct lguest *srclg, const struct lguest_dma *src, ...@@ -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 * number of pages. Note that we're holding the destination's
* mmap_sem, as get_user_pages() requires. */ * mmap_sem, as get_user_pages() requires. */
if (get_user_pages(dstlg->tsk, dstlg->mm, 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) { != 1) {
/* This means the destination gave us a bogus buffer */ /* This means the destination gave us a bogus buffer */
kill_guest(dstlg, "Error mapping DMA pages"); kill_guest(dstlg, "Error mapping DMA pages");
...@@ -493,7 +495,7 @@ void send_dma(struct lguest *lg, unsigned long ukey, unsigned long udma) ...@@ -493,7 +495,7 @@ void send_dma(struct lguest *lg, unsigned long ukey, unsigned long udma)
mutex_lock(&lguest_lock); mutex_lock(&lguest_lock);
down_read(fshared); down_read(fshared);
/* Get the futex key for the key the Guest gave us */ /* 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"); kill_guest(lg, "bad sending DMA key");
goto unlock; goto unlock;
} }
...@@ -584,7 +586,7 @@ unsigned long get_dma_buffer(struct lguest *lg, ...@@ -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 /* 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). */ * 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"); kill_guest(lg, "bad registered DMA buffer");
goto unlock; goto unlock;
} }
......
...@@ -142,6 +142,9 @@ struct lguest ...@@ -142,6 +142,9 @@ struct lguest
struct mm_struct *mm; /* == tsk->mm, but that becomes NULL on exit */ struct mm_struct *mm; /* == tsk->mm, but that becomes NULL on exit */
u16 guestid; u16 guestid;
u32 pfn_limit; 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 page_offset;
u32 cr2; u32 cr2;
int halted; int halted;
......
/*P:200 This contains all the /dev/lguest code, whereby the userspace launcher /*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 * controls and communicates with the Guest. For example, the first write will
* tell us the memory size, pagetable, entry point and kernel address offset. * tell us the Guest's memory layout, pagetable, entry point and kernel address
* A read will run the Guest until a signal is pending (-EINTR), or the Guest * offset. A read will run the Guest until something happens, such as a signal
* does a DMA out to the Launcher. Writes are also used to get a DMA buffer * or the Guest doing a DMA out to the Launcher. Writes are also used to get a
* registered by the Guest and to send the Guest an interrupt. :*/ * DMA buffer registered by the Guest and to send the Guest an interrupt. :*/
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/miscdevice.h> #include <linux/miscdevice.h>
#include <linux/fs.h> #include <linux/fs.h>
...@@ -142,9 +142,11 @@ static ssize_t read(struct file *file, char __user *user, size_t size,loff_t*o) ...@@ -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); 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: * 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 * 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 * allowed to access. The Launcher has to live in Guest memory, so it sets
* this to ensure the Guest can't reach it. * this to ensure the Guest can't reach it.
...@@ -166,7 +168,7 @@ static int initialize(struct file *file, const u32 __user *input) ...@@ -166,7 +168,7 @@ static int initialize(struct file *file, const u32 __user *input)
* Guest. */ * Guest. */
struct lguest *lg; struct lguest *lg;
int err, i; int err, i;
u32 args[4]; u32 args[5];
/* We grab the Big Lguest lock, which protects the global array /* We grab the Big Lguest lock, which protects the global array
* "lguests" and multiple simultaneous initializations. */ * "lguests" and multiple simultaneous initializations. */
...@@ -194,8 +196,9 @@ static int initialize(struct file *file, const u32 __user *input) ...@@ -194,8 +196,9 @@ static int initialize(struct file *file, const u32 __user *input)
/* Populate the easy fields of our "struct lguest" */ /* Populate the easy fields of our "struct lguest" */
lg->guestid = i; lg->guestid = i;
lg->pfn_limit = args[0]; lg->mem_base = (void __user *)(long)args[0];
lg->page_offset = args[3]; lg->pfn_limit = args[1];
lg->page_offset = args[4];
/* We need a complete page for the Guest registers: they are accessible /* 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. */ * 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) ...@@ -210,13 +213,13 @@ static int initialize(struct file *file, const u32 __user *input)
/* Initialize the Guest's shadow page tables, using the toplevel /* Initialize the Guest's shadow page tables, using the toplevel
* address the Launcher gave us. This allocates memory, so can * address the Launcher gave us. This allocates memory, so can
* fail. */ * fail. */
err = init_guest_pagetable(lg, args[1]); err = init_guest_pagetable(lg, args[2]);
if (err) if (err)
goto free_regs; goto free_regs;
/* Now we initialize the Guest's registers, handing it the start /* Now we initialize the Guest's registers, handing it the start
* address. */ * 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 /* There are a couple of GDT entries the Guest expects when first
* booting. */ * booting. */
......
...@@ -152,7 +152,7 @@ static unsigned long get_pfn(unsigned long virtpfn, int write) ...@@ -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) static spte_t gpte_to_spte(struct lguest *lg, gpte_t gpte, int write)
{ {
spte_t spte; spte_t spte;
unsigned long pfn; unsigned long pfn, base;
/* The Guest sets the global flag, because it thinks that it is using /* 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 * 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) ...@@ -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. */ * use the global bit, so throw it away. */
spte.flags = (gpte.flags & ~_PAGE_GLOBAL); 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 /* We need a temporary "unsigned long" variable to hold the answer from
* get_pfn(), because it returns 0xFFFFFFFF on failure, which wouldn't * get_pfn(), because it returns 0xFFFFFFFF on failure, which wouldn't
* fit in spte.pfn. get_pfn() finds the real physical number of the * fit in spte.pfn. get_pfn() finds the real physical number of the
* page, given the virtual number. */ * page, given the virtual number. */
pfn = get_pfn(gpte.pfn, write); pfn = get_pfn(base + gpte.pfn, write);
if (pfn == -1UL) { if (pfn == -1UL) {
kill_guest(lg, "failed to get page %u", gpte.pfn); kill_guest(lg, "failed to get page %u", gpte.pfn);
/* When we destroy the Guest, we'll go through the shadow page /* When we destroy the Guest, we'll go through the shadow page
......
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