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.
# 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
CFLAGS:=-Wall -Wmissing-declarations -Wmissing-prototypes -O3 -I../../include
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
# 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; }' > $@
all: lguest
clean:
rm -f lguest.lds lguest
rm -f lguest
This diff is collapsed.
......@@ -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. */
......
......@@ -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)
......
......@@ -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 @@ void send_dma(struct lguest *lg, unsigned long ukey, unsigned long udma)
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;
}
......
......@@ -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;
......
/*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 <linux/uaccess.h>
#include <linux/miscdevice.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)
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. */
......
......@@ -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
......
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