lguest: the host code

This is the code for the "lg.ko" module, which allows lguest guests to
be launched.

[akpm@linux-foundation.org: update for futex-new-private-futexes]
[akpm@linux-foundation.org: build fix]
[jmorris@namei.org: lguest: use hrtimers]
[akpm@linux-foundation.org: x86_64 build fix]
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Cc: Andi Kleen <ak@suse.de>
Cc: Eric Dumazet <dada1@cosmosbay.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Rusty Russell 2007-07-19 01:49:23 -07:00 committed by Linus Torvalds
parent 07ad157f6e
commit d7e28ffe6c
17 changed files with 2703 additions and 35 deletions

View file

@ -27,6 +27,7 @@ static int tsc_enabled;
* an extra value to store the TSC freq
*/
unsigned int tsc_khz;
EXPORT_SYMBOL_GPL(tsc_khz);
int tsc_disable;
@ -58,10 +59,11 @@ __setup("notsc", tsc_setup);
*/
static int tsc_unstable;
static inline int check_tsc_unstable(void)
int check_tsc_unstable(void)
{
return tsc_unstable;
}
EXPORT_SYMBOL_GPL(check_tsc_unstable);
/* Accellerators for sched_clock()
* convert from cycles(64bits) => nanoseconds (64bits)

View file

@ -44,7 +44,7 @@ unsigned long long sched_clock(void)
static int tsc_unstable;
static inline int check_tsc_unstable(void)
inline int check_tsc_unstable(void)
{
return tsc_unstable;
}

462
drivers/lguest/core.c Normal file
View file

@ -0,0 +1,462 @@
/* World's simplest hypervisor, to test paravirt_ops and show
* unbelievers that virtualization is the future. Plus, it's fun! */
#include <linux/module.h>
#include <linux/stringify.h>
#include <linux/stddef.h>
#include <linux/io.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
#include <linux/cpu.h>
#include <linux/freezer.h>
#include <asm/paravirt.h>
#include <asm/desc.h>
#include <asm/pgtable.h>
#include <asm/uaccess.h>
#include <asm/poll.h>
#include <asm/highmem.h>
#include <asm/asm-offsets.h>
#include <asm/i387.h>
#include "lg.h"
/* Found in switcher.S */
extern char start_switcher_text[], end_switcher_text[], switch_to_guest[];
extern unsigned long default_idt_entries[];
/* Every guest maps the core switcher code. */
#define SHARED_SWITCHER_PAGES \
DIV_ROUND_UP(end_switcher_text - start_switcher_text, PAGE_SIZE)
/* Pages for switcher itself, then two pages per cpu */
#define TOTAL_SWITCHER_PAGES (SHARED_SWITCHER_PAGES + 2 * NR_CPUS)
/* We map at -4M for ease of mapping into the guest (one PTE page). */
#define SWITCHER_ADDR 0xFFC00000
static struct vm_struct *switcher_vma;
static struct page **switcher_page;
static int cpu_had_pge;
static struct {
unsigned long offset;
unsigned short segment;
} lguest_entry;
/* This One Big lock protects all inter-guest data structures. */
DEFINE_MUTEX(lguest_lock);
static DEFINE_PER_CPU(struct lguest *, last_guest);
/* FIXME: Make dynamic. */
#define MAX_LGUEST_GUESTS 16
struct lguest lguests[MAX_LGUEST_GUESTS];
/* Offset from where switcher.S was compiled to where we've copied it */
static unsigned long switcher_offset(void)
{
return SWITCHER_ADDR - (unsigned long)start_switcher_text;
}
/* This cpu's struct lguest_pages. */
static struct lguest_pages *lguest_pages(unsigned int cpu)
{
return &(((struct lguest_pages *)
(SWITCHER_ADDR + SHARED_SWITCHER_PAGES*PAGE_SIZE))[cpu]);
}
static __init int map_switcher(void)
{
int i, err;
struct page **pagep;
switcher_page = kmalloc(sizeof(switcher_page[0])*TOTAL_SWITCHER_PAGES,
GFP_KERNEL);
if (!switcher_page) {
err = -ENOMEM;
goto out;
}
for (i = 0; i < TOTAL_SWITCHER_PAGES; i++) {
unsigned long addr = get_zeroed_page(GFP_KERNEL);
if (!addr) {
err = -ENOMEM;
goto free_some_pages;
}
switcher_page[i] = virt_to_page(addr);
}
switcher_vma = __get_vm_area(TOTAL_SWITCHER_PAGES * PAGE_SIZE,
VM_ALLOC, SWITCHER_ADDR, VMALLOC_END);
if (!switcher_vma) {
err = -ENOMEM;
printk("lguest: could not map switcher pages high\n");
goto free_pages;
}
pagep = switcher_page;
err = map_vm_area(switcher_vma, PAGE_KERNEL, &pagep);
if (err) {
printk("lguest: map_vm_area failed: %i\n", err);
goto free_vma;
}
memcpy(switcher_vma->addr, start_switcher_text,
end_switcher_text - start_switcher_text);
/* Fix up IDT entries to point into copied text. */
for (i = 0; i < IDT_ENTRIES; i++)
default_idt_entries[i] += switcher_offset();
for_each_possible_cpu(i) {
struct lguest_pages *pages = lguest_pages(i);
struct lguest_ro_state *state = &pages->state;
/* These fields are static: rest done in copy_in_guest_info */
state->host_gdt_desc.size = GDT_SIZE-1;
state->host_gdt_desc.address = (long)get_cpu_gdt_table(i);
store_idt(&state->host_idt_desc);
state->guest_idt_desc.size = sizeof(state->guest_idt)-1;
state->guest_idt_desc.address = (long)&state->guest_idt;
state->guest_gdt_desc.size = sizeof(state->guest_gdt)-1;
state->guest_gdt_desc.address = (long)&state->guest_gdt;
state->guest_tss.esp0 = (long)(&pages->regs + 1);
state->guest_tss.ss0 = LGUEST_DS;
/* No I/O for you! */
state->guest_tss.io_bitmap_base = sizeof(state->guest_tss);
setup_default_gdt_entries(state);
setup_default_idt_entries(state, default_idt_entries);
/* Setup LGUEST segments on all cpus */
get_cpu_gdt_table(i)[GDT_ENTRY_LGUEST_CS] = FULL_EXEC_SEGMENT;
get_cpu_gdt_table(i)[GDT_ENTRY_LGUEST_DS] = FULL_SEGMENT;
}
/* Initialize entry point into switcher. */
lguest_entry.offset = (long)switch_to_guest + switcher_offset();
lguest_entry.segment = LGUEST_CS;
printk(KERN_INFO "lguest: mapped switcher at %p\n",
switcher_vma->addr);
return 0;
free_vma:
vunmap(switcher_vma->addr);
free_pages:
i = TOTAL_SWITCHER_PAGES;
free_some_pages:
for (--i; i >= 0; i--)
__free_pages(switcher_page[i], 0);
kfree(switcher_page);
out:
return err;
}
static void unmap_switcher(void)
{
unsigned int i;
vunmap(switcher_vma->addr);
for (i = 0; i < TOTAL_SWITCHER_PAGES; i++)
__free_pages(switcher_page[i], 0);
}
/* IN/OUT insns: enough to get us past boot-time probing. */
static int emulate_insn(struct lguest *lg)
{
u8 insn;
unsigned int insnlen = 0, in = 0, shift = 0;
unsigned long physaddr = guest_pa(lg, lg->regs->eip);
/* This only works for addresses in linear mapping... */
if (lg->regs->eip < lg->page_offset)
return 0;
lgread(lg, &insn, physaddr, 1);
/* Operand size prefix means it's actually for ax. */
if (insn == 0x66) {
shift = 16;
insnlen = 1;
lgread(lg, &insn, physaddr + insnlen, 1);
}
switch (insn & 0xFE) {
case 0xE4: /* in <next byte>,%al */
insnlen += 2;
in = 1;
break;
case 0xEC: /* in (%dx),%al */
insnlen += 1;
in = 1;
break;
case 0xE6: /* out %al,<next byte> */
insnlen += 2;
break;
case 0xEE: /* out %al,(%dx) */
insnlen += 1;
break;
default:
return 0;
}
if (in) {
/* Lower bit tells is whether it's a 16 or 32 bit access */
if (insn & 0x1)
lg->regs->eax = 0xFFFFFFFF;
else
lg->regs->eax |= (0xFFFF << shift);
}
lg->regs->eip += insnlen;
return 1;
}
int lguest_address_ok(const struct lguest *lg,
unsigned long addr, unsigned long len)
{
return (addr+len) / PAGE_SIZE < lg->pfn_limit && (addr+len >= addr);
}
/* Just like get_user, but don't let guest access lguest binary. */
u32 lgread_u32(struct lguest *lg, unsigned long addr)
{
u32 val = 0;
/* 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);
return val;
}
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)
kill_guest(lg, "bad write address %#lx", addr);
}
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 should do this, but as we rely on it... */
memset(b, 0, bytes);
kill_guest(lg, "bad read address %#lx len %u", addr, bytes);
}
}
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)
kill_guest(lg, "bad write address %#lx len %u", addr, bytes);
}
static void set_ts(void)
{
u32 cr0;
cr0 = read_cr0();
if (!(cr0 & 8))
write_cr0(cr0|8);
}
static void copy_in_guest_info(struct lguest *lg, struct lguest_pages *pages)
{
if (__get_cpu_var(last_guest) != lg || lg->last_pages != pages) {
__get_cpu_var(last_guest) = lg;
lg->last_pages = pages;
lg->changed = CHANGED_ALL;
}
/* These are pretty cheap, so we do them unconditionally. */
pages->state.host_cr3 = __pa(current->mm->pgd);
map_switcher_in_guest(lg, pages);
pages->state.guest_tss.esp1 = lg->esp1;
pages->state.guest_tss.ss1 = lg->ss1;
/* Copy direct trap entries. */
if (lg->changed & CHANGED_IDT)
copy_traps(lg, pages->state.guest_idt, default_idt_entries);
/* Copy all GDT entries but the TSS. */
if (lg->changed & CHANGED_GDT)
copy_gdt(lg, pages->state.guest_gdt);
/* If only the TLS entries have changed, copy them. */
else if (lg->changed & CHANGED_GDT_TLS)
copy_gdt_tls(lg, pages->state.guest_gdt);
lg->changed = 0;
}
static void run_guest_once(struct lguest *lg, struct lguest_pages *pages)
{
unsigned int clobber;
copy_in_guest_info(lg, pages);
/* Put eflags on stack, lcall does rest: suitable for iret return. */
asm volatile("pushf; lcall *lguest_entry"
: "=a"(clobber), "=b"(clobber)
: "0"(pages), "1"(__pa(lg->pgdirs[lg->pgdidx].pgdir))
: "memory", "%edx", "%ecx", "%edi", "%esi");
}
int run_guest(struct lguest *lg, unsigned long __user *user)
{
while (!lg->dead) {
unsigned int cr2 = 0; /* Damn gcc */
/* Hypercalls first: we might have been out to userspace */
do_hypercalls(lg);
if (lg->dma_is_pending) {
if (put_user(lg->pending_dma, user) ||
put_user(lg->pending_key, user+1))
return -EFAULT;
return sizeof(unsigned long)*2;
}
if (signal_pending(current))
return -ERESTARTSYS;
/* If Waker set break_out, return to Launcher. */
if (lg->break_out)
return -EAGAIN;
maybe_do_interrupt(lg);
try_to_freeze();
if (lg->dead)
break;
if (lg->halted) {
set_current_state(TASK_INTERRUPTIBLE);
schedule();
continue;
}
local_irq_disable();
/* Even if *we* don't want FPU trap, guest might... */
if (lg->ts)
set_ts();
/* Don't let Guest do SYSENTER: we can't handle it. */
if (boot_cpu_has(X86_FEATURE_SEP))
wrmsr(MSR_IA32_SYSENTER_CS, 0, 0);
run_guest_once(lg, lguest_pages(raw_smp_processor_id()));
/* Save cr2 now if we page-faulted. */
if (lg->regs->trapnum == 14)
cr2 = read_cr2();
else if (lg->regs->trapnum == 7)
math_state_restore();
if (boot_cpu_has(X86_FEATURE_SEP))
wrmsr(MSR_IA32_SYSENTER_CS, __KERNEL_CS, 0);
local_irq_enable();
switch (lg->regs->trapnum) {
case 13: /* We've intercepted a GPF. */
if (lg->regs->errcode == 0) {
if (emulate_insn(lg))
continue;
}
break;
case 14: /* We've intercepted a page fault. */
if (demand_page(lg, cr2, lg->regs->errcode))
continue;
/* If lguest_data is NULL, this won't hurt. */
if (put_user(cr2, &lg->lguest_data->cr2))
kill_guest(lg, "Writing cr2");
break;
case 7: /* We've intercepted a Device Not Available fault. */
/* If they don't want to know, just absorb it. */
if (!lg->ts)
continue;
break;
case 32 ... 255: /* Real interrupt, fall thru */
cond_resched();
case LGUEST_TRAP_ENTRY: /* Handled at top of loop */
continue;
}
if (deliver_trap(lg, lg->regs->trapnum))
continue;
kill_guest(lg, "unhandled trap %li at %#lx (%#lx)",
lg->regs->trapnum, lg->regs->eip,
lg->regs->trapnum == 14 ? cr2 : lg->regs->errcode);
}
return -ENOENT;
}
int find_free_guest(void)
{
unsigned int i;
for (i = 0; i < MAX_LGUEST_GUESTS; i++)
if (!lguests[i].tsk)
return i;
return -1;
}
static void adjust_pge(void *on)
{
if (on)
write_cr4(read_cr4() | X86_CR4_PGE);
else
write_cr4(read_cr4() & ~X86_CR4_PGE);
}
static int __init init(void)
{
int err;
if (paravirt_enabled()) {
printk("lguest is afraid of %s\n", paravirt_ops.name);
return -EPERM;
}
err = map_switcher();
if (err)
return err;
err = init_pagetables(switcher_page, SHARED_SWITCHER_PAGES);
if (err) {
unmap_switcher();
return err;
}
lguest_io_init();
err = lguest_device_init();
if (err) {
free_pagetables();
unmap_switcher();
return err;
}
lock_cpu_hotplug();
if (cpu_has_pge) { /* We have a broader idea of "global". */
cpu_had_pge = 1;
on_each_cpu(adjust_pge, (void *)0, 0, 1);
clear_bit(X86_FEATURE_PGE, boot_cpu_data.x86_capability);
}
unlock_cpu_hotplug();
return 0;
}
static void __exit fini(void)
{
lguest_device_remove();
free_pagetables();
unmap_switcher();
lock_cpu_hotplug();
if (cpu_had_pge) {
set_bit(X86_FEATURE_PGE, boot_cpu_data.x86_capability);
on_each_cpu(adjust_pge, (void *)1, 0, 1);
}
unlock_cpu_hotplug();
}
module_init(init);
module_exit(fini);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Rusty Russell <rusty@rustcorp.com.au>");

192
drivers/lguest/hypercalls.c Normal file
View file

@ -0,0 +1,192 @@
/* Actual hypercalls, which allow guests to actually do something.
Copyright (C) 2006 Rusty Russell IBM Corporation
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <linux/uaccess.h>
#include <linux/syscalls.h>
#include <linux/mm.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <irq_vectors.h>
#include "lg.h"
static void do_hcall(struct lguest *lg, struct lguest_regs *regs)
{
switch (regs->eax) {
case LHCALL_FLUSH_ASYNC:
break;
case LHCALL_LGUEST_INIT:
kill_guest(lg, "already have lguest_data");
break;
case LHCALL_CRASH: {
char msg[128];
lgread(lg, msg, regs->edx, sizeof(msg));
msg[sizeof(msg)-1] = '\0';
kill_guest(lg, "CRASH: %s", msg);
break;
}
case LHCALL_FLUSH_TLB:
if (regs->edx)
guest_pagetable_clear_all(lg);
else
guest_pagetable_flush_user(lg);
break;
case LHCALL_GET_WALLCLOCK: {
struct timespec ts;
ktime_get_real_ts(&ts);
regs->eax = ts.tv_sec;
break;
}
case LHCALL_BIND_DMA:
regs->eax = bind_dma(lg, regs->edx, regs->ebx,
regs->ecx >> 8, regs->ecx & 0xFF);
break;
case LHCALL_SEND_DMA:
send_dma(lg, regs->edx, regs->ebx);
break;
case LHCALL_LOAD_GDT:
load_guest_gdt(lg, regs->edx, regs->ebx);
break;
case LHCALL_LOAD_IDT_ENTRY:
load_guest_idt_entry(lg, regs->edx, regs->ebx, regs->ecx);
break;
case LHCALL_NEW_PGTABLE:
guest_new_pagetable(lg, regs->edx);
break;
case LHCALL_SET_STACK:
guest_set_stack(lg, regs->edx, regs->ebx, regs->ecx);
break;
case LHCALL_SET_PTE:
guest_set_pte(lg, regs->edx, regs->ebx, mkgpte(regs->ecx));
break;
case LHCALL_SET_PMD:
guest_set_pmd(lg, regs->edx, regs->ebx);
break;
case LHCALL_LOAD_TLS:
guest_load_tls(lg, regs->edx);
break;
case LHCALL_SET_CLOCKEVENT:
guest_set_clockevent(lg, regs->edx);
break;
case LHCALL_TS:
lg->ts = regs->edx;
break;
case LHCALL_HALT:
lg->halted = 1;
break;
default:
kill_guest(lg, "Bad hypercall %li\n", regs->eax);
}
}
/* We always do queued calls before actual hypercall. */
static void do_async_hcalls(struct lguest *lg)
{
unsigned int i;
u8 st[LHCALL_RING_SIZE];
if (copy_from_user(&st, &lg->lguest_data->hcall_status, sizeof(st)))
return;
for (i = 0; i < ARRAY_SIZE(st); i++) {
struct lguest_regs regs;
unsigned int n = lg->next_hcall;
if (st[n] == 0xFF)
break;
if (++lg->next_hcall == LHCALL_RING_SIZE)
lg->next_hcall = 0;
if (get_user(regs.eax, &lg->lguest_data->hcalls[n].eax)
|| get_user(regs.edx, &lg->lguest_data->hcalls[n].edx)
|| get_user(regs.ecx, &lg->lguest_data->hcalls[n].ecx)
|| get_user(regs.ebx, &lg->lguest_data->hcalls[n].ebx)) {
kill_guest(lg, "Fetching async hypercalls");
break;
}
do_hcall(lg, &regs);
if (put_user(0xFF, &lg->lguest_data->hcall_status[n])) {
kill_guest(lg, "Writing result for async hypercall");
break;
}
if (lg->dma_is_pending)
break;
}
}
static void initialize(struct lguest *lg)
{
u32 tsc_speed;
if (lg->regs->eax != LHCALL_LGUEST_INIT) {
kill_guest(lg, "hypercall %li before LGUEST_INIT",
lg->regs->eax);
return;
}
/* We only tell the guest to use the TSC if it's reliable. */
if (boot_cpu_has(X86_FEATURE_CONSTANT_TSC) && !check_tsc_unstable())
tsc_speed = tsc_khz;
else
tsc_speed = 0;
lg->lguest_data = (struct lguest_data __user *)lg->regs->edx;
/* We check here so we can simply copy_to_user/from_user */
if (!lguest_address_ok(lg, lg->regs->edx, sizeof(*lg->lguest_data))) {
kill_guest(lg, "bad guest page %p", lg->lguest_data);
return;
}
if (get_user(lg->noirq_start, &lg->lguest_data->noirq_start)
|| get_user(lg->noirq_end, &lg->lguest_data->noirq_end)
/* We reserve the top pgd entry. */
|| put_user(4U*1024*1024, &lg->lguest_data->reserve_mem)
|| put_user(tsc_speed, &lg->lguest_data->tsc_khz)
|| put_user(lg->guestid, &lg->lguest_data->guestid))
kill_guest(lg, "bad guest page %p", lg->lguest_data);
/* This is the one case where the above accesses might have
* been the first write to a Guest page. This may have caused
* a copy-on-write fault, but the Guest might be referring to
* the old (read-only) page. */
guest_pagetable_clear_all(lg);
}
/* Even if we go out to userspace and come back, we don't want to do
* the hypercall again. */
static void clear_hcall(struct lguest *lg)
{
lg->regs->trapnum = 255;
}
void do_hypercalls(struct lguest *lg)
{
if (unlikely(!lg->lguest_data)) {
if (lg->regs->trapnum == LGUEST_TRAP_ENTRY) {
initialize(lg);
clear_hcall(lg);
}
return;
}
do_async_hcalls(lg);
if (!lg->dma_is_pending && lg->regs->trapnum == LGUEST_TRAP_ENTRY) {
do_hcall(lg, lg->regs);
clear_hcall(lg);
}
}

View file

@ -0,0 +1,268 @@
#include <linux/uaccess.h>
#include "lg.h"
static unsigned long idt_address(u32 lo, u32 hi)
{
return (lo & 0x0000FFFF) | (hi & 0xFFFF0000);
}
static int idt_type(u32 lo, u32 hi)
{
return (hi >> 8) & 0xF;
}
static int idt_present(u32 lo, u32 hi)
{
return (hi & 0x8000);
}
static void push_guest_stack(struct lguest *lg, unsigned long *gstack, u32 val)
{
*gstack -= 4;
lgwrite_u32(lg, *gstack, val);
}
static void set_guest_interrupt(struct lguest *lg, u32 lo, u32 hi, int has_err)
{
unsigned long gstack;
u32 eflags, ss, irq_enable;
/* If they want a ring change, we use new stack and push old ss/esp */
if ((lg->regs->ss&0x3) != GUEST_PL) {
gstack = guest_pa(lg, lg->esp1);
ss = lg->ss1;
push_guest_stack(lg, &gstack, lg->regs->ss);
push_guest_stack(lg, &gstack, lg->regs->esp);
} else {
gstack = guest_pa(lg, lg->regs->esp);
ss = lg->regs->ss;
}
/* We use IF bit in eflags to indicate whether irqs were disabled
(it's always 0, since irqs are enabled when guest is running). */
eflags = lg->regs->eflags;
if (get_user(irq_enable, &lg->lguest_data->irq_enabled))
irq_enable = 0;
eflags |= (irq_enable & X86_EFLAGS_IF);
push_guest_stack(lg, &gstack, eflags);
push_guest_stack(lg, &gstack, lg->regs->cs);
push_guest_stack(lg, &gstack, lg->regs->eip);
if (has_err)
push_guest_stack(lg, &gstack, lg->regs->errcode);
/* Change the real stack so switcher returns to trap handler */
lg->regs->ss = ss;
lg->regs->esp = gstack + lg->page_offset;
lg->regs->cs = (__KERNEL_CS|GUEST_PL);
lg->regs->eip = idt_address(lo, hi);
/* Disable interrupts for an interrupt gate. */
if (idt_type(lo, hi) == 0xE)
if (put_user(0, &lg->lguest_data->irq_enabled))
kill_guest(lg, "Disabling interrupts");
}
void maybe_do_interrupt(struct lguest *lg)
{
unsigned int irq;
DECLARE_BITMAP(blk, LGUEST_IRQS);
struct desc_struct *idt;
if (!lg->lguest_data)
return;
/* Mask out any interrupts they have blocked. */
if (copy_from_user(&blk, lg->lguest_data->blocked_interrupts,
sizeof(blk)))
return;
bitmap_andnot(blk, lg->irqs_pending, blk, LGUEST_IRQS);
irq = find_first_bit(blk, LGUEST_IRQS);
if (irq >= LGUEST_IRQS)
return;
if (lg->regs->eip >= lg->noirq_start && lg->regs->eip < lg->noirq_end)
return;
/* If they're halted, we re-enable interrupts. */
if (lg->halted) {
/* Re-enable interrupts. */
if (put_user(X86_EFLAGS_IF, &lg->lguest_data->irq_enabled))
kill_guest(lg, "Re-enabling interrupts");
lg->halted = 0;
} else {
/* Maybe they have interrupts disabled? */
u32 irq_enabled;
if (get_user(irq_enabled, &lg->lguest_data->irq_enabled))
irq_enabled = 0;
if (!irq_enabled)
return;
}
idt = &lg->idt[FIRST_EXTERNAL_VECTOR+irq];
if (idt_present(idt->a, idt->b)) {
clear_bit(irq, lg->irqs_pending);
set_guest_interrupt(lg, idt->a, idt->b, 0);
}
}
static int has_err(unsigned int trap)
{
return (trap == 8 || (trap >= 10 && trap <= 14) || trap == 17);
}
int deliver_trap(struct lguest *lg, unsigned int num)
{
u32 lo = lg->idt[num].a, hi = lg->idt[num].b;
if (!idt_present(lo, hi))
return 0;
set_guest_interrupt(lg, lo, hi, has_err(num));
return 1;
}
static int direct_trap(const struct lguest *lg,
const struct desc_struct *trap,
unsigned int num)
{
/* Hardware interrupts don't go to guest (except syscall). */
if (num >= FIRST_EXTERNAL_VECTOR && num != SYSCALL_VECTOR)
return 0;
/* We intercept page fault (demand shadow paging & cr2 saving)
protection fault (in/out emulation) and device not
available (TS handling), and hypercall */
if (num == 14 || num == 13 || num == 7 || num == LGUEST_TRAP_ENTRY)
return 0;
/* Interrupt gates (0xE) or not present (0x0) can't go direct. */
return idt_type(trap->a, trap->b) == 0xF;
}
void pin_stack_pages(struct lguest *lg)
{
unsigned int i;
for (i = 0; i < lg->stack_pages; i++)
pin_page(lg, lg->esp1 - i * PAGE_SIZE);
}
void guest_set_stack(struct lguest *lg, u32 seg, u32 esp, unsigned int pages)
{
/* You cannot have a stack segment with priv level 0. */
if ((seg & 0x3) != GUEST_PL)
kill_guest(lg, "bad stack segment %i", seg);
if (pages > 2)
kill_guest(lg, "bad stack pages %u", pages);
lg->ss1 = seg;
lg->esp1 = esp;
lg->stack_pages = pages;
pin_stack_pages(lg);
}
/* Set up trap in IDT. */
static void set_trap(struct lguest *lg, struct desc_struct *trap,
unsigned int num, u32 lo, u32 hi)
{
u8 type = idt_type(lo, hi);
if (!idt_present(lo, hi)) {
trap->a = trap->b = 0;
return;
}
if (type != 0xE && type != 0xF)
kill_guest(lg, "bad IDT type %i", type);
trap->a = ((__KERNEL_CS|GUEST_PL)<<16) | (lo&0x0000FFFF);
trap->b = (hi&0xFFFFEF00);
}
void load_guest_idt_entry(struct lguest *lg, unsigned int num, u32 lo, u32 hi)
{
/* Guest never handles: NMI, doublefault, hypercall, spurious irq. */
if (num == 2 || num == 8 || num == 15 || num == LGUEST_TRAP_ENTRY)
return;
lg->changed |= CHANGED_IDT;
if (num < ARRAY_SIZE(lg->idt))
set_trap(lg, &lg->idt[num], num, lo, hi);
else if (num == SYSCALL_VECTOR)
set_trap(lg, &lg->syscall_idt, num, lo, hi);
}
static void default_idt_entry(struct desc_struct *idt,
int trap,
const unsigned long handler)
{
u32 flags = 0x8e00;
/* They can't "int" into any of them except hypercall. */
if (trap == LGUEST_TRAP_ENTRY)
flags |= (GUEST_PL << 13);
idt->a = (LGUEST_CS<<16) | (handler&0x0000FFFF);
idt->b = (handler&0xFFFF0000) | flags;
}
void setup_default_idt_entries(struct lguest_ro_state *state,
const unsigned long *def)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(state->guest_idt); i++)
default_idt_entry(&state->guest_idt[i], i, def[i]);
}
void copy_traps(const struct lguest *lg, struct desc_struct *idt,
const unsigned long *def)
{
unsigned int i;
/* All hardware interrupts are same whatever the guest: only the
* traps might be different. */
for (i = 0; i < FIRST_EXTERNAL_VECTOR; i++) {
if (direct_trap(lg, &lg->idt[i], i))
idt[i] = lg->idt[i];
else
default_idt_entry(&idt[i], i, def[i]);
}
i = SYSCALL_VECTOR;
if (direct_trap(lg, &lg->syscall_idt, i))
idt[i] = lg->syscall_idt;
else
default_idt_entry(&idt[i], i, def[i]);
}
void guest_set_clockevent(struct lguest *lg, unsigned long delta)
{
ktime_t expires;
if (unlikely(delta == 0)) {
/* Clock event device is shutting down. */
hrtimer_cancel(&lg->hrt);
return;
}
expires = ktime_add_ns(ktime_get_real(), delta);
hrtimer_start(&lg->hrt, expires, HRTIMER_MODE_ABS);
}
static enum hrtimer_restart clockdev_fn(struct hrtimer *timer)
{
struct lguest *lg = container_of(timer, struct lguest, hrt);
set_bit(0, lg->irqs_pending);
if (lg->halted)
wake_up_process(lg->tsk);
return HRTIMER_NORESTART;
}
void init_clockdev(struct lguest *lg)
{
hrtimer_init(&lg->hrt, CLOCK_REALTIME, HRTIMER_MODE_ABS);
lg->hrt.function = clockdev_fn;
}

399
drivers/lguest/io.c Normal file
View file

@ -0,0 +1,399 @@
/* Simple I/O model for guests, based on shared memory.
* Copyright (C) 2006 Rusty Russell IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <linux/types.h>
#include <linux/futex.h>
#include <linux/jhash.h>
#include <linux/mm.h>
#include <linux/highmem.h>
#include <linux/uaccess.h>
#include "lg.h"
static struct list_head dma_hash[61];
void lguest_io_init(void)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(dma_hash); i++)
INIT_LIST_HEAD(&dma_hash[i]);
}
/* FIXME: allow multi-page lengths. */
static int check_dma_list(struct lguest *lg, const struct lguest_dma *dma)
{
unsigned int i;
for (i = 0; i < LGUEST_MAX_DMA_SECTIONS; i++) {
if (!dma->len[i])
return 1;
if (!lguest_address_ok(lg, dma->addr[i], dma->len[i]))
goto kill;
if (dma->len[i] > PAGE_SIZE)
goto kill;
/* We could do over a page, but is it worth it? */
if ((dma->addr[i] % PAGE_SIZE) + dma->len[i] > PAGE_SIZE)
goto kill;
}
return 1;
kill:
kill_guest(lg, "bad DMA entry: %u@%#lx", dma->len[i], dma->addr[i]);
return 0;
}
static unsigned int hash(const union futex_key *key)
{
return jhash2((u32*)&key->both.word,
(sizeof(key->both.word)+sizeof(key->both.ptr))/4,
key->both.offset)
% ARRAY_SIZE(dma_hash);
}
static inline int key_eq(const union futex_key *a, const union futex_key *b)
{
return (a->both.word == b->both.word
&& a->both.ptr == b->both.ptr
&& a->both.offset == b->both.offset);
}
/* Must hold read lock on dmainfo owner's current->mm->mmap_sem */
static void unlink_dma(struct lguest_dma_info *dmainfo)
{
BUG_ON(!mutex_is_locked(&lguest_lock));
dmainfo->interrupt = 0;
list_del(&dmainfo->list);
drop_futex_key_refs(&dmainfo->key);
}
static int unbind_dma(struct lguest *lg,
const union futex_key *key,
unsigned long dmas)
{
int i, ret = 0;
for (i = 0; i < LGUEST_MAX_DMA; i++) {
if (key_eq(key, &lg->dma[i].key) && dmas == lg->dma[i].dmas) {
unlink_dma(&lg->dma[i]);
ret = 1;
break;
}
}
return ret;
}
int bind_dma(struct lguest *lg,
unsigned long ukey, unsigned long dmas, u16 numdmas, u8 interrupt)
{
unsigned int i;
int ret = 0;
union futex_key key;
struct rw_semaphore *fshared = &current->mm->mmap_sem;
if (interrupt >= LGUEST_IRQS)
return 0;
mutex_lock(&lguest_lock);
down_read(fshared);
if (get_futex_key((u32 __user *)ukey, fshared, &key) != 0) {
kill_guest(lg, "bad dma key %#lx", ukey);
goto unlock;
}
get_futex_key_refs(&key);
if (interrupt == 0)
ret = unbind_dma(lg, &key, dmas);
else {
for (i = 0; i < LGUEST_MAX_DMA; i++) {
if (lg->dma[i].interrupt)
continue;
lg->dma[i].dmas = dmas;
lg->dma[i].num_dmas = numdmas;
lg->dma[i].next_dma = 0;
lg->dma[i].key = key;
lg->dma[i].guestid = lg->guestid;
lg->dma[i].interrupt = interrupt;
list_add(&lg->dma[i].list, &dma_hash[hash(&key)]);
ret = 1;
goto unlock;
}
}
drop_futex_key_refs(&key);
unlock:
up_read(fshared);
mutex_unlock(&lguest_lock);
return ret;
}
/* lgread from another guest */
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) {
memset(buf, 0, bytes);
kill_guest(lg, "bad address in registered DMA struct");
return 0;
}
return 1;
}
/* lgwrite to another guest */
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)) {
kill_guest(lg, "bad address writing to registered DMA");
return 0;
}
return 1;
}
static u32 copy_data(struct lguest *srclg,
const struct lguest_dma *src,
const struct lguest_dma *dst,
struct page *pages[])
{
unsigned int totlen, si, di, srcoff, dstoff;
void *maddr = NULL;
totlen = 0;
si = di = 0;
srcoff = dstoff = 0;
while (si < LGUEST_MAX_DMA_SECTIONS && src->len[si]
&& di < LGUEST_MAX_DMA_SECTIONS && dst->len[di]) {
u32 len = min(src->len[si] - srcoff, dst->len[di] - dstoff);
if (!maddr)
maddr = kmap(pages[di]);
/* FIXME: This is not completely portable, since
archs do different things for copy_to_user_page. */
if (copy_from_user(maddr + (dst->addr[di] + dstoff)%PAGE_SIZE,
(void *__user)src->addr[si], len) != 0) {
kill_guest(srclg, "bad address in sending DMA");
totlen = 0;
break;
}
totlen += len;
srcoff += len;
dstoff += len;
if (srcoff == src->len[si]) {
si++;
srcoff = 0;
}
if (dstoff == dst->len[di]) {
kunmap(pages[di]);
maddr = NULL;
di++;
dstoff = 0;
}
}
if (maddr)
kunmap(pages[di]);
return totlen;
}
/* Src is us, ie. current. */
static u32 do_dma(struct lguest *srclg, const struct lguest_dma *src,
struct lguest *dstlg, const struct lguest_dma *dst)
{
int i;
u32 ret;
struct page *pages[LGUEST_MAX_DMA_SECTIONS];
if (!check_dma_list(dstlg, dst) || !check_dma_list(srclg, src))
return 0;
/* First get the destination pages */
for (i = 0; i < LGUEST_MAX_DMA_SECTIONS; i++) {
if (dst->len[i] == 0)
break;
if (get_user_pages(dstlg->tsk, dstlg->mm,
dst->addr[i], 1, 1, 1, pages+i, NULL)
!= 1) {
kill_guest(dstlg, "Error mapping DMA pages");
ret = 0;
goto drop_pages;
}
}
/* Now copy until we run out of src or dst. */
ret = copy_data(srclg, src, dst, pages);
drop_pages:
while (--i >= 0)
put_page(pages[i]);
return ret;
}
static int dma_transfer(struct lguest *srclg,
unsigned long udma,
struct lguest_dma_info *dst)
{
struct lguest_dma dst_dma, src_dma;
struct lguest *dstlg;
u32 i, dma = 0;
dstlg = &lguests[dst->guestid];
/* Get our dma list. */
lgread(srclg, &src_dma, udma, sizeof(src_dma));
/* We can't deadlock against them dmaing to us, because this
* is all under the lguest_lock. */
down_read(&dstlg->mm->mmap_sem);
for (i = 0; i < dst->num_dmas; i++) {
dma = (dst->next_dma + i) % dst->num_dmas;
if (!lgread_other(dstlg, &dst_dma,
dst->dmas + dma * sizeof(struct lguest_dma),
sizeof(dst_dma))) {
goto fail;
}
if (!dst_dma.used_len)
break;
}
if (i != dst->num_dmas) {
unsigned long used_lenp;
unsigned int ret;
ret = do_dma(srclg, &src_dma, dstlg, &dst_dma);
/* Put used length in src. */
lgwrite_u32(srclg,
udma+offsetof(struct lguest_dma, used_len), ret);
if (ret == 0 && src_dma.len[0] != 0)
goto fail;
/* Make sure destination sees contents before length. */
wmb();
used_lenp = dst->dmas
+ dma * sizeof(struct lguest_dma)
+ offsetof(struct lguest_dma, used_len);
lgwrite_other(dstlg, used_lenp, &ret, sizeof(ret));
dst->next_dma++;
}
up_read(&dstlg->mm->mmap_sem);
/* Do this last so dst doesn't simply sleep on lock. */
set_bit(dst->interrupt, dstlg->irqs_pending);
wake_up_process(dstlg->tsk);
return i == dst->num_dmas;
fail:
up_read(&dstlg->mm->mmap_sem);
return 0;
}
void send_dma(struct lguest *lg, unsigned long ukey, unsigned long udma)
{
union futex_key key;
int empty = 0;
struct rw_semaphore *fshared = &current->mm->mmap_sem;
again:
mutex_lock(&lguest_lock);
down_read(fshared);
if (get_futex_key((u32 __user *)ukey, fshared, &key) != 0) {
kill_guest(lg, "bad sending DMA key");
goto unlock;
}
/* Shared mapping? Look for other guests... */
if (key.shared.offset & 1) {
struct lguest_dma_info *i;
list_for_each_entry(i, &dma_hash[hash(&key)], list) {
if (i->guestid == lg->guestid)
continue;
if (!key_eq(&key, &i->key))
continue;
empty += dma_transfer(lg, udma, i);
break;
}
if (empty == 1) {
/* Give any recipients one chance to restock. */
up_read(&current->mm->mmap_sem);
mutex_unlock(&lguest_lock);
empty++;
goto again;
}
} else {
/* Private mapping: tell our userspace. */
lg->dma_is_pending = 1;
lg->pending_dma = udma;
lg->pending_key = ukey;
}
unlock:
up_read(fshared);
mutex_unlock(&lguest_lock);
}
void release_all_dma(struct lguest *lg)
{
unsigned int i;
BUG_ON(!mutex_is_locked(&lguest_lock));
down_read(&lg->mm->mmap_sem);
for (i = 0; i < LGUEST_MAX_DMA; i++) {
if (lg->dma[i].interrupt)
unlink_dma(&lg->dma[i]);
}
up_read(&lg->mm->mmap_sem);
}
/* Userspace wants a dma buffer from this guest. */
unsigned long get_dma_buffer(struct lguest *lg,
unsigned long ukey, unsigned long *interrupt)
{
unsigned long ret = 0;
union futex_key key;
struct lguest_dma_info *i;
struct rw_semaphore *fshared = &current->mm->mmap_sem;
mutex_lock(&lguest_lock);
down_read(fshared);
if (get_futex_key((u32 __user *)ukey, fshared, &key) != 0) {
kill_guest(lg, "bad registered DMA buffer");
goto unlock;
}
list_for_each_entry(i, &dma_hash[hash(&key)], list) {
if (key_eq(&key, &i->key) && i->guestid == lg->guestid) {
unsigned int j;
for (j = 0; j < i->num_dmas; j++) {
struct lguest_dma dma;
ret = i->dmas + j * sizeof(struct lguest_dma);
lgread(lg, &dma, ret, sizeof(dma));
if (dma.used_len == 0)
break;
}
*interrupt = i->interrupt;
break;
}
}
unlock:
up_read(fshared);
mutex_unlock(&lguest_lock);
return ret;
}

261
drivers/lguest/lg.h Normal file
View file

@ -0,0 +1,261 @@
#ifndef _LGUEST_H
#define _LGUEST_H
#include <asm/desc.h>
#define GDT_ENTRY_LGUEST_CS 10
#define GDT_ENTRY_LGUEST_DS 11
#define LGUEST_CS (GDT_ENTRY_LGUEST_CS * 8)
#define LGUEST_DS (GDT_ENTRY_LGUEST_DS * 8)
#ifndef __ASSEMBLY__
#include <linux/types.h>
#include <linux/init.h>
#include <linux/stringify.h>
#include <linux/binfmts.h>
#include <linux/futex.h>
#include <linux/lguest.h>
#include <linux/lguest_launcher.h>
#include <linux/wait.h>
#include <linux/err.h>
#include <asm/semaphore.h>
#include "irq_vectors.h"
#define GUEST_PL 1
struct lguest_regs
{
/* Manually saved part. */
unsigned long ebx, ecx, edx;
unsigned long esi, edi, ebp;
unsigned long gs;
unsigned long eax;
unsigned long fs, ds, es;
unsigned long trapnum, errcode;
/* Trap pushed part */
unsigned long eip;
unsigned long cs;
unsigned long eflags;
unsigned long esp;
unsigned long ss;
};
void free_pagetables(void);
int init_pagetables(struct page **switcher_page, unsigned int pages);
/* Full 4G segment descriptors, suitable for CS and DS. */
#define FULL_EXEC_SEGMENT ((struct desc_struct){0x0000ffff, 0x00cf9b00})
#define FULL_SEGMENT ((struct desc_struct){0x0000ffff, 0x00cf9300})
struct lguest_dma_info
{
struct list_head list;
union futex_key key;
unsigned long dmas;
u16 next_dma;
u16 num_dmas;
u16 guestid;
u8 interrupt; /* 0 when not registered */
};
/* We have separate types for the guest's ptes & pgds and the shadow ptes &
* pgds. Since this host might use three-level pagetables and the guest and
* shadow pagetables don't, we can't use the normal pte_t/pgd_t. */
typedef union {
struct { unsigned flags:12, pfn:20; };
struct { unsigned long val; } raw;
} spgd_t;
typedef union {
struct { unsigned flags:12, pfn:20; };
struct { unsigned long val; } raw;
} spte_t;
typedef union {
struct { unsigned flags:12, pfn:20; };
struct { unsigned long val; } raw;
} gpgd_t;
typedef union {
struct { unsigned flags:12, pfn:20; };
struct { unsigned long val; } raw;
} gpte_t;
#define mkgpte(_val) ((gpte_t){.raw.val = _val})
#define mkgpgd(_val) ((gpgd_t){.raw.val = _val})
struct pgdir
{
unsigned long cr3;
spgd_t *pgdir;
};
/* This is a guest-specific page (mapped ro) into the guest. */
struct lguest_ro_state
{
/* Host information we need to restore when we switch back. */
u32 host_cr3;
struct Xgt_desc_struct host_idt_desc;
struct Xgt_desc_struct host_gdt_desc;
u32 host_sp;
/* Fields which are used when guest is running. */
struct Xgt_desc_struct guest_idt_desc;
struct Xgt_desc_struct guest_gdt_desc;
struct i386_hw_tss guest_tss;
struct desc_struct guest_idt[IDT_ENTRIES];
struct desc_struct guest_gdt[GDT_ENTRIES];
};
/* We have two pages shared with guests, per cpu. */
struct lguest_pages
{
/* This is the stack page mapped rw in guest */
char spare[PAGE_SIZE - sizeof(struct lguest_regs)];
struct lguest_regs regs;
/* This is the host state & guest descriptor page, ro in guest */
struct lguest_ro_state state;
} __attribute__((aligned(PAGE_SIZE)));
#define CHANGED_IDT 1
#define CHANGED_GDT 2
#define CHANGED_GDT_TLS 4 /* Actually a subset of CHANGED_GDT */
#define CHANGED_ALL 3
/* The private info the thread maintains about the guest. */
struct lguest
{
/* At end of a page shared mapped over lguest_pages in guest. */
unsigned long regs_page;
struct lguest_regs *regs;
struct lguest_data __user *lguest_data;
struct task_struct *tsk;
struct mm_struct *mm; /* == tsk->mm, but that becomes NULL on exit */
u16 guestid;
u32 pfn_limit;
u32 page_offset;
u32 cr2;
int halted;
int ts;
u32 next_hcall;
u32 esp1;
u8 ss1;
/* Do we need to stop what we're doing and return to userspace? */
int break_out;
wait_queue_head_t break_wq;
/* Bitmap of what has changed: see CHANGED_* above. */
int changed;
struct lguest_pages *last_pages;
/* We keep a small number of these. */
u32 pgdidx;
struct pgdir pgdirs[4];
/* Cached wakeup: we hold a reference to this task. */
struct task_struct *wake;
unsigned long noirq_start, noirq_end;
int dma_is_pending;
unsigned long pending_dma; /* struct lguest_dma */
unsigned long pending_key; /* address they're sending to */
unsigned int stack_pages;
u32 tsc_khz;
struct lguest_dma_info dma[LGUEST_MAX_DMA];
/* Dead? */
const char *dead;
/* The GDT entries copied into lguest_ro_state when running. */
struct desc_struct gdt[GDT_ENTRIES];
/* The IDT entries: some copied into lguest_ro_state when running. */
struct desc_struct idt[FIRST_EXTERNAL_VECTOR+LGUEST_IRQS];
struct desc_struct syscall_idt;
/* Virtual clock device */
struct hrtimer hrt;
/* Pending virtual interrupts */
DECLARE_BITMAP(irqs_pending, LGUEST_IRQS);
};
extern struct lguest lguests[];
extern struct mutex lguest_lock;
/* core.c: */
u32 lgread_u32(struct lguest *lg, unsigned long addr);
void lgwrite_u32(struct lguest *lg, unsigned long addr, u32 val);
void lgread(struct lguest *lg, void *buf, unsigned long addr, unsigned len);
void lgwrite(struct lguest *lg, unsigned long, const void *buf, unsigned len);
int find_free_guest(void);
int lguest_address_ok(const struct lguest *lg,
unsigned long addr, unsigned long len);
int run_guest(struct lguest *lg, unsigned long __user *user);
/* interrupts_and_traps.c: */
void maybe_do_interrupt(struct lguest *lg);
int deliver_trap(struct lguest *lg, unsigned int num);
void load_guest_idt_entry(struct lguest *lg, unsigned int i, u32 low, u32 hi);
void guest_set_stack(struct lguest *lg, u32 seg, u32 esp, unsigned int pages);
void pin_stack_pages(struct lguest *lg);
void setup_default_idt_entries(struct lguest_ro_state *state,
const unsigned long *def);
void copy_traps(const struct lguest *lg, struct desc_struct *idt,
const unsigned long *def);
void guest_set_clockevent(struct lguest *lg, unsigned long delta);
void init_clockdev(struct lguest *lg);
/* segments.c: */
void setup_default_gdt_entries(struct lguest_ro_state *state);
void setup_guest_gdt(struct lguest *lg);
void load_guest_gdt(struct lguest *lg, unsigned long table, u32 num);
void guest_load_tls(struct lguest *lg, unsigned long tls_array);
void copy_gdt(const struct lguest *lg, struct desc_struct *gdt);
void copy_gdt_tls(const struct lguest *lg, struct desc_struct *gdt);
/* page_tables.c: */
int init_guest_pagetable(struct lguest *lg, unsigned long pgtable);
void free_guest_pagetable(struct lguest *lg);
void guest_new_pagetable(struct lguest *lg, unsigned long pgtable);
void guest_set_pmd(struct lguest *lg, unsigned long cr3, u32 i);
void guest_pagetable_clear_all(struct lguest *lg);
void guest_pagetable_flush_user(struct lguest *lg);
void guest_set_pte(struct lguest *lg, unsigned long cr3,
unsigned long vaddr, gpte_t val);
void map_switcher_in_guest(struct lguest *lg, struct lguest_pages *pages);
int demand_page(struct lguest *info, unsigned long cr2, int errcode);
void pin_page(struct lguest *lg, unsigned long vaddr);
/* lguest_user.c: */
int lguest_device_init(void);
void lguest_device_remove(void);
/* io.c: */
void lguest_io_init(void);
int bind_dma(struct lguest *lg,
unsigned long key, unsigned long udma, u16 numdmas, u8 interrupt);
void send_dma(struct lguest *info, unsigned long key, unsigned long udma);
void release_all_dma(struct lguest *lg);
unsigned long get_dma_buffer(struct lguest *lg, unsigned long key,
unsigned long *interrupt);
/* hypercalls.c: */
void do_hypercalls(struct lguest *lg);
#define kill_guest(lg, fmt...) \
do { \
if (!(lg)->dead) { \
(lg)->dead = kasprintf(GFP_ATOMIC, fmt); \
if (!(lg)->dead) \
(lg)->dead = ERR_PTR(-ENOMEM); \
} \
} while(0)
static inline unsigned long guest_pa(struct lguest *lg, unsigned long vaddr)
{
return vaddr - lg->page_offset;
}
#endif /* __ASSEMBLY__ */
#endif /* _LGUEST_H */

View file

@ -25,6 +25,8 @@
#include <linux/screen_info.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/lguest.h>
#include <linux/lguest_launcher.h>
#include <linux/lguest_bus.h>
@ -37,6 +39,7 @@
#include <asm/e820.h>
#include <asm/mce.h>
#include <asm/io.h>
//#include <asm/sched-clock.h>
/* Declarations for definitions in lguest_guest.S */
extern char lguest_noirq_start[], lguest_noirq_end[];
@ -54,7 +57,6 @@ struct lguest_data lguest_data = {
.blocked_interrupts = { 1 }, /* Block timer interrupts */
};
struct lguest_device_desc *lguest_devices;
static __initdata const struct lguest_boot_info *boot = __va(0);
static enum paravirt_lazy_mode lazy_mode;
static void lguest_lazy_mode(enum paravirt_lazy_mode mode)
@ -210,7 +212,7 @@ static void lguest_cpuid(unsigned int *eax, unsigned int *ebx,
case 1: /* Basic feature request. */
/* We only allow kernel to see SSE3, CMPXCHG16B and SSSE3 */
*ecx &= 0x00002201;
/* Similarly: SSE, SSE2, FXSR, MMX, CMOV, CMPXCHG8B, FPU. */
/* SSE, SSE2, FXSR, MMX, CMOV, CMPXCHG8B, FPU. */
*edx &= 0x07808101;
/* Host wants to know when we flush kernel pages: set PGE. */
*edx |= 0x00002000;
@ -346,24 +348,104 @@ static unsigned long lguest_get_wallclock(void)
return hcall(LHCALL_GET_WALLCLOCK, 0, 0, 0);
}
static void lguest_time_irq(unsigned int irq, struct irq_desc *desc)
static cycle_t lguest_clock_read(void)
{
do_timer(hcall(LHCALL_TIMER_READ, 0, 0, 0));
update_process_times(user_mode_vm(get_irq_regs()));
if (lguest_data.tsc_khz)
return native_read_tsc();
else
return jiffies;
}
/* This is what we tell the kernel is our clocksource. */
static struct clocksource lguest_clock = {
.name = "lguest",
.rating = 400,
.read = lguest_clock_read,
};
/* We also need a "struct clock_event_device": Linux asks us to set it to go
* off some time in the future. Actually, James Morris figured all this out, I
* just applied the patch. */
static int lguest_clockevent_set_next_event(unsigned long delta,
struct clock_event_device *evt)
{
if (delta < LG_CLOCK_MIN_DELTA) {
if (printk_ratelimit())
printk(KERN_DEBUG "%s: small delta %lu ns\n",
__FUNCTION__, delta);
return -ETIME;
}
hcall(LHCALL_SET_CLOCKEVENT, delta, 0, 0);
return 0;
}
static void lguest_clockevent_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt)
{
switch (mode) {
case CLOCK_EVT_MODE_UNUSED:
case CLOCK_EVT_MODE_SHUTDOWN:
/* A 0 argument shuts the clock down. */
hcall(LHCALL_SET_CLOCKEVENT, 0, 0, 0);
break;
case CLOCK_EVT_MODE_ONESHOT:
/* This is what we expect. */
break;
case CLOCK_EVT_MODE_PERIODIC:
BUG();
}
}
/* This describes our primitive timer chip. */
static struct clock_event_device lguest_clockevent = {
.name = "lguest",
.features = CLOCK_EVT_FEAT_ONESHOT,
.set_next_event = lguest_clockevent_set_next_event,
.set_mode = lguest_clockevent_set_mode,
.rating = INT_MAX,
.mult = 1,
.shift = 0,
.min_delta_ns = LG_CLOCK_MIN_DELTA,
.max_delta_ns = LG_CLOCK_MAX_DELTA,
};
/* This is the Guest timer interrupt handler (hardware interrupt 0). We just
* call the clockevent infrastructure and it does whatever needs doing. */
static void lguest_time_irq(unsigned int irq, struct irq_desc *desc)
{
unsigned long flags;
/* Don't interrupt us while this is running. */
local_irq_save(flags);
lguest_clockevent.event_handler(&lguest_clockevent);
local_irq_restore(flags);
}
static u64 sched_clock_base;
static void lguest_time_init(void)
{
set_irq_handler(0, lguest_time_irq);
hcall(LHCALL_TIMER_READ, 0, 0, 0);
sched_clock_base = jiffies_64;
enable_lguest_irq(0);
}
static unsigned long long lguest_sched_clock(void)
{
return (jiffies_64 - sched_clock_base) * (1000000000 / HZ);
/* We use the TSC if the Host tells us we can, otherwise a dumb
* jiffies-based clock. */
if (lguest_data.tsc_khz) {
lguest_clock.shift = 22;
lguest_clock.mult = clocksource_khz2mult(lguest_data.tsc_khz,
lguest_clock.shift);
lguest_clock.mask = CLOCKSOURCE_MASK(64);
lguest_clock.flags = CLOCK_SOURCE_IS_CONTINUOUS;
} else {
/* To understand this, start at kernel/time/jiffies.c... */
lguest_clock.shift = 8;
lguest_clock.mult = (((u64)NSEC_PER_SEC<<8)/ACTHZ) << 8;
lguest_clock.mask = CLOCKSOURCE_MASK(32);
}
clocksource_register(&lguest_clock);
/* We can't set cpumask in the initializer: damn C limitations! */
lguest_clockevent.cpumask = cpumask_of_cpu(0);
clockevents_register_device(&lguest_clockevent);
enable_lguest_irq(0);
}
static void lguest_load_esp0(struct tss_struct *tss,
@ -418,8 +500,7 @@ static __init char *lguest_memory_setup(void)
/* We do this here because lockcheck barfs if before start_kernel */
atomic_notifier_chain_register(&panic_notifier_list, &paniced);
e820.nr_map = 0;
add_memory_region(0, PFN_PHYS(boot->max_pfn), E820_RAM);
add_memory_region(E820_MAP->addr, E820_MAP->size, E820_MAP->type);
return "LGUEST";
}
@ -450,8 +531,13 @@ static unsigned lguest_patch(u8 type, u16 clobber, void *insns, unsigned len)
return insn_len;
}
__init void lguest_init(void)
__init void lguest_init(void *boot)
{
/* Copy boot parameters first. */
memcpy(&boot_params, boot, PARAM_SIZE);
memcpy(boot_command_line, __va(boot_params.hdr.cmd_line_ptr),
COMMAND_LINE_SIZE);
paravirt_ops.name = "lguest";
paravirt_ops.paravirt_enabled = 1;
paravirt_ops.kernel_rpl = 1;
@ -498,10 +584,8 @@ __init void lguest_init(void)
paravirt_ops.time_init = lguest_time_init;
paravirt_ops.set_lazy_mode = lguest_lazy_mode;
paravirt_ops.wbinvd = lguest_wbinvd;
paravirt_ops.sched_clock = lguest_sched_clock;
hcall(LHCALL_LGUEST_INIT, __pa(&lguest_data), 0, 0);
strncpy(boot_command_line, boot->cmdline, COMMAND_LINE_SIZE);
/* We use top of mem for initial pagetables. */
init_pg_tables_end = __pa(pg0);
@ -532,13 +616,6 @@ __init void lguest_init(void)
add_preferred_console("hvc", 0, NULL);
if (boot->initrd_size) {
/* We stash this at top of memory. */
INITRD_START = boot->max_pfn*PAGE_SIZE - boot->initrd_size;
INITRD_SIZE = boot->initrd_size;
LOADER_TYPE = 0xFF;
}
pm_power_off = lguest_power_off;
start_kernel();
}

View file

@ -10,7 +10,8 @@
* This is where we begin: we have a magic signature which the launcher looks
* for. The plan is that the Linux boot protocol will be extended with a
* "platform type" field which will guide us here from the normal entry point,
* but for the moment this suffices.
* but for the moment this suffices. We pass the virtual address of the boot
* info to lguest_init().
*
* We put it in .init.text will be discarded after boot.
*/
@ -18,6 +19,8 @@
.ascii "GenuineLguest"
/* Set up initial stack. */
movl $(init_thread_union+THREAD_SIZE),%esp
movl %esi, %eax
addl $__PAGE_OFFSET, %eax
jmp lguest_init
/* The templates for inline patching. */

View file

@ -0,0 +1,236 @@
/* Userspace control of the guest, via /dev/lguest. */
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include "lg.h"
static void setup_regs(struct lguest_regs *regs, unsigned long start)
{
/* Write out stack in format lguest expects, so we can switch to it. */
regs->ds = regs->es = regs->ss = __KERNEL_DS|GUEST_PL;
regs->cs = __KERNEL_CS|GUEST_PL;
regs->eflags = 0x202; /* Interrupts enabled. */
regs->eip = start;
/* esi points to our boot information (physical address 0) */
}
/* + addr */
static long user_get_dma(struct lguest *lg, const u32 __user *input)
{
unsigned long key, udma, irq;
if (get_user(key, input) != 0)
return -EFAULT;
udma = get_dma_buffer(lg, key, &irq);
if (!udma)
return -ENOENT;
/* We put irq number in udma->used_len. */
lgwrite_u32(lg, udma + offsetof(struct lguest_dma, used_len), irq);
return udma;
}
/* To force the Guest to stop running and return to the Launcher, the
* Waker sets writes LHREQ_BREAK and the value "1" to /dev/lguest. The
* Launcher then writes LHREQ_BREAK and "0" to release the Waker. */
static int break_guest_out(struct lguest *lg, const u32 __user *input)
{
unsigned long on;
/* Fetch whether they're turning break on or off.. */
if (get_user(on, input) != 0)
return -EFAULT;
if (on) {
lg->break_out = 1;
/* Pop it out (may be running on different CPU) */
wake_up_process(lg->tsk);
/* Wait for them to reset it */
return wait_event_interruptible(lg->break_wq, !lg->break_out);
} else {
lg->break_out = 0;
wake_up(&lg->break_wq);
return 0;
}
}
/* + irq */
static int user_send_irq(struct lguest *lg, const u32 __user *input)
{
u32 irq;
if (get_user(irq, input) != 0)
return -EFAULT;
if (irq >= LGUEST_IRQS)
return -EINVAL;
set_bit(irq, lg->irqs_pending);
return 0;
}
static ssize_t read(struct file *file, char __user *user, size_t size,loff_t*o)
{
struct lguest *lg = file->private_data;
if (!lg)
return -EINVAL;
/* If you're not the task which owns the guest, go away. */
if (current != lg->tsk)
return -EPERM;
if (lg->dead) {
size_t len;
if (IS_ERR(lg->dead))
return PTR_ERR(lg->dead);
len = min(size, strlen(lg->dead)+1);
if (copy_to_user(user, lg->dead, len) != 0)
return -EFAULT;
return len;
}
if (lg->dma_is_pending)
lg->dma_is_pending = 0;
return run_guest(lg, (unsigned long __user *)user);
}
/* Take: pfnlimit, pgdir, start, pageoffset. */
static int initialize(struct file *file, const u32 __user *input)
{
struct lguest *lg;
int err, i;
u32 args[4];
/* We grab the Big Lguest lock, which protects the global array
* "lguests" and multiple simultaneous initializations. */
mutex_lock(&lguest_lock);
if (file->private_data) {
err = -EBUSY;
goto unlock;
}
if (copy_from_user(args, input, sizeof(args)) != 0) {
err = -EFAULT;
goto unlock;
}
i = find_free_guest();
if (i < 0) {
err = -ENOSPC;
goto unlock;
}
lg = &lguests[i];
lg->guestid = i;
lg->pfn_limit = args[0];
lg->page_offset = args[3];
lg->regs_page = get_zeroed_page(GFP_KERNEL);
if (!lg->regs_page) {
err = -ENOMEM;
goto release_guest;
}
lg->regs = (void *)lg->regs_page + PAGE_SIZE - sizeof(*lg->regs);
err = init_guest_pagetable(lg, args[1]);
if (err)
goto free_regs;
setup_regs(lg->regs, args[2]);
setup_guest_gdt(lg);
init_clockdev(lg);
lg->tsk = current;
lg->mm = get_task_mm(lg->tsk);
init_waitqueue_head(&lg->break_wq);
lg->last_pages = NULL;
file->private_data = lg;
mutex_unlock(&lguest_lock);
return sizeof(args);
free_regs:
free_page(lg->regs_page);
release_guest:
memset(lg, 0, sizeof(*lg));
unlock:
mutex_unlock(&lguest_lock);
return err;
}
static ssize_t write(struct file *file, const char __user *input,
size_t size, loff_t *off)
{
struct lguest *lg = file->private_data;
u32 req;
if (get_user(req, input) != 0)
return -EFAULT;
input += sizeof(req);
if (req != LHREQ_INITIALIZE && !lg)
return -EINVAL;
if (lg && lg->dead)
return -ENOENT;
/* If you're not the task which owns the Guest, you can only break */
if (lg && current != lg->tsk && req != LHREQ_BREAK)
return -EPERM;
switch (req) {
case LHREQ_INITIALIZE:
return initialize(file, (const u32 __user *)input);
case LHREQ_GETDMA:
return user_get_dma(lg, (const u32 __user *)input);
case LHREQ_IRQ:
return user_send_irq(lg, (const u32 __user *)input);
case LHREQ_BREAK:
return break_guest_out(lg, (const u32 __user *)input);
default:
return -EINVAL;
}
}
static int close(struct inode *inode, struct file *file)
{
struct lguest *lg = file->private_data;
if (!lg)
return 0;
mutex_lock(&lguest_lock);
/* Cancels the hrtimer set via LHCALL_SET_CLOCKEVENT. */
hrtimer_cancel(&lg->hrt);
release_all_dma(lg);
free_guest_pagetable(lg);
mmput(lg->mm);
if (!IS_ERR(lg->dead))
kfree(lg->dead);
free_page(lg->regs_page);
memset(lg, 0, sizeof(*lg));
mutex_unlock(&lguest_lock);
return 0;
}
static struct file_operations lguest_fops = {
.owner = THIS_MODULE,
.release = close,
.write = write,
.read = read,
};
static struct miscdevice lguest_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "lguest",
.fops = &lguest_fops,
};
int __init lguest_device_init(void)
{
return misc_register(&lguest_dev);
}
void __exit lguest_device_remove(void)
{
misc_deregister(&lguest_dev);
}

View file

@ -0,0 +1,411 @@
/* Shadow page table operations.
* Copyright (C) Rusty Russell IBM Corporation 2006.
* GPL v2 and any later version */
#include <linux/mm.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/random.h>
#include <linux/percpu.h>
#include <asm/tlbflush.h>
#include "lg.h"
#define PTES_PER_PAGE_SHIFT 10
#define PTES_PER_PAGE (1 << PTES_PER_PAGE_SHIFT)
#define SWITCHER_PGD_INDEX (PTES_PER_PAGE - 1)
static DEFINE_PER_CPU(spte_t *, switcher_pte_pages);
#define switcher_pte_page(cpu) per_cpu(switcher_pte_pages, cpu)
static unsigned vaddr_to_pgd_index(unsigned long vaddr)
{
return vaddr >> (PAGE_SHIFT + PTES_PER_PAGE_SHIFT);
}
/* These access the shadow versions (ie. the ones used by the CPU). */
static spgd_t *spgd_addr(struct lguest *lg, u32 i, unsigned long vaddr)
{
unsigned int index = vaddr_to_pgd_index(vaddr);
if (index >= SWITCHER_PGD_INDEX) {
kill_guest(lg, "attempt to access switcher pages");
index = 0;
}
return &lg->pgdirs[i].pgdir[index];
}
static spte_t *spte_addr(struct lguest *lg, spgd_t spgd, unsigned long vaddr)
{
spte_t *page = __va(spgd.pfn << PAGE_SHIFT);
BUG_ON(!(spgd.flags & _PAGE_PRESENT));
return &page[(vaddr >> PAGE_SHIFT) % PTES_PER_PAGE];
}
/* These access the guest versions. */
static unsigned long gpgd_addr(struct lguest *lg, unsigned long vaddr)
{
unsigned int index = vaddr >> (PAGE_SHIFT + PTES_PER_PAGE_SHIFT);
return lg->pgdirs[lg->pgdidx].cr3 + index * sizeof(gpgd_t);
}
static unsigned long gpte_addr(struct lguest *lg,
gpgd_t gpgd, unsigned long vaddr)
{
unsigned long gpage = gpgd.pfn << PAGE_SHIFT;
BUG_ON(!(gpgd.flags & _PAGE_PRESENT));
return gpage + ((vaddr>>PAGE_SHIFT) % PTES_PER_PAGE) * sizeof(gpte_t);
}
/* Do a virtual -> physical mapping on a user page. */
static unsigned long get_pfn(unsigned long virtpfn, int write)
{
struct page *page;
unsigned long ret = -1UL;
down_read(&current->mm->mmap_sem);
if (get_user_pages(current, current->mm, virtpfn << PAGE_SHIFT,
1, write, 1, &page, NULL) == 1)
ret = page_to_pfn(page);
up_read(&current->mm->mmap_sem);
return ret;
}
static spte_t gpte_to_spte(struct lguest *lg, gpte_t gpte, int write)
{
spte_t spte;
unsigned long pfn;
/* We ignore the global flag. */
spte.flags = (gpte.flags & ~_PAGE_GLOBAL);
pfn = get_pfn(gpte.pfn, write);
if (pfn == -1UL) {
kill_guest(lg, "failed to get page %u", gpte.pfn);
/* Must not put_page() bogus page on cleanup. */
spte.flags = 0;
}
spte.pfn = pfn;
return spte;
}
static void release_pte(spte_t pte)
{
if (pte.flags & _PAGE_PRESENT)
put_page(pfn_to_page(pte.pfn));
}
static void check_gpte(struct lguest *lg, gpte_t gpte)
{
if ((gpte.flags & (_PAGE_PWT|_PAGE_PSE)) || gpte.pfn >= lg->pfn_limit)
kill_guest(lg, "bad page table entry");
}
static void check_gpgd(struct lguest *lg, gpgd_t gpgd)
{
if ((gpgd.flags & ~_PAGE_TABLE) || gpgd.pfn >= lg->pfn_limit)
kill_guest(lg, "bad page directory entry");
}
/* FIXME: We hold reference to pages, which prevents them from being
swapped. It'd be nice to have a callback when Linux wants to swap out. */
/* We fault pages in, which allows us to update accessed/dirty bits.
* Return true if we got page. */
int demand_page(struct lguest *lg, unsigned long vaddr, int errcode)
{
gpgd_t gpgd;
spgd_t *spgd;
unsigned long gpte_ptr;
gpte_t gpte;
spte_t *spte;
gpgd = mkgpgd(lgread_u32(lg, gpgd_addr(lg, vaddr)));
if (!(gpgd.flags & _PAGE_PRESENT))
return 0;
spgd = spgd_addr(lg, lg->pgdidx, vaddr);
if (!(spgd->flags & _PAGE_PRESENT)) {
/* Get a page of PTEs for them. */
unsigned long ptepage = get_zeroed_page(GFP_KERNEL);
/* FIXME: Steal from self in this case? */
if (!ptepage) {
kill_guest(lg, "out of memory allocating pte page");
return 0;
}
check_gpgd(lg, gpgd);
spgd->raw.val = (__pa(ptepage) | gpgd.flags);
}
gpte_ptr = gpte_addr(lg, gpgd, vaddr);
gpte = mkgpte(lgread_u32(lg, gpte_ptr));
/* No page? */
if (!(gpte.flags & _PAGE_PRESENT))
return 0;
/* Write to read-only page? */
if ((errcode & 2) && !(gpte.flags & _PAGE_RW))
return 0;
/* User access to a non-user page? */
if ((errcode & 4) && !(gpte.flags & _PAGE_USER))
return 0;
check_gpte(lg, gpte);
gpte.flags |= _PAGE_ACCESSED;
if (errcode & 2)
gpte.flags |= _PAGE_DIRTY;
/* We're done with the old pte. */
spte = spte_addr(lg, *spgd, vaddr);
release_pte(*spte);
/* We don't make it writable if this isn't a write: later
* write will fault so we can set dirty bit in guest. */
if (gpte.flags & _PAGE_DIRTY)
*spte = gpte_to_spte(lg, gpte, 1);
else {
gpte_t ro_gpte = gpte;
ro_gpte.flags &= ~_PAGE_RW;
*spte = gpte_to_spte(lg, ro_gpte, 0);
}
/* Now we update dirty/accessed on guest. */
lgwrite_u32(lg, gpte_ptr, gpte.raw.val);
return 1;
}
/* This is much faster than the full demand_page logic. */
static int page_writable(struct lguest *lg, unsigned long vaddr)
{
spgd_t *spgd;
unsigned long flags;
spgd = spgd_addr(lg, lg->pgdidx, vaddr);
if (!(spgd->flags & _PAGE_PRESENT))
return 0;
flags = spte_addr(lg, *spgd, vaddr)->flags;
return (flags & (_PAGE_PRESENT|_PAGE_RW)) == (_PAGE_PRESENT|_PAGE_RW);
}
void pin_page(struct lguest *lg, unsigned long vaddr)
{
if (!page_writable(lg, vaddr) && !demand_page(lg, vaddr, 2))
kill_guest(lg, "bad stack page %#lx", vaddr);
}
static void release_pgd(struct lguest *lg, spgd_t *spgd)
{
if (spgd->flags & _PAGE_PRESENT) {
unsigned int i;
spte_t *ptepage = __va(spgd->pfn << PAGE_SHIFT);
for (i = 0; i < PTES_PER_PAGE; i++)
release_pte(ptepage[i]);
free_page((long)ptepage);
spgd->raw.val = 0;
}
}
static void flush_user_mappings(struct lguest *lg, int idx)
{
unsigned int i;
for (i = 0; i < vaddr_to_pgd_index(lg->page_offset); i++)
release_pgd(lg, lg->pgdirs[idx].pgdir + i);
}
void guest_pagetable_flush_user(struct lguest *lg)
{
flush_user_mappings(lg, lg->pgdidx);
}
static unsigned int find_pgdir(struct lguest *lg, unsigned long pgtable)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(lg->pgdirs); i++)
if (lg->pgdirs[i].cr3 == pgtable)
break;
return i;
}
static unsigned int new_pgdir(struct lguest *lg,
unsigned long cr3,
int *blank_pgdir)
{
unsigned int next;
next = random32() % ARRAY_SIZE(lg->pgdirs);
if (!lg->pgdirs[next].pgdir) {
lg->pgdirs[next].pgdir = (spgd_t *)get_zeroed_page(GFP_KERNEL);
if (!lg->pgdirs[next].pgdir)
next = lg->pgdidx;
else
/* There are no mappings: you'll need to re-pin */
*blank_pgdir = 1;
}
lg->pgdirs[next].cr3 = cr3;
/* Release all the non-kernel mappings. */
flush_user_mappings(lg, next);
return next;
}
void guest_new_pagetable(struct lguest *lg, unsigned long pgtable)
{
int newpgdir, repin = 0;
newpgdir = find_pgdir(lg, pgtable);
if (newpgdir == ARRAY_SIZE(lg->pgdirs))
newpgdir = new_pgdir(lg, pgtable, &repin);
lg->pgdidx = newpgdir;
if (repin)
pin_stack_pages(lg);
}
static void release_all_pagetables(struct lguest *lg)
{
unsigned int i, j;
for (i = 0; i < ARRAY_SIZE(lg->pgdirs); i++)
if (lg->pgdirs[i].pgdir)
for (j = 0; j < SWITCHER_PGD_INDEX; j++)
release_pgd(lg, lg->pgdirs[i].pgdir + j);
}
void guest_pagetable_clear_all(struct lguest *lg)
{
release_all_pagetables(lg);
pin_stack_pages(lg);
}
static void do_set_pte(struct lguest *lg, int idx,
unsigned long vaddr, gpte_t gpte)
{
spgd_t *spgd = spgd_addr(lg, idx, vaddr);
if (spgd->flags & _PAGE_PRESENT) {
spte_t *spte = spte_addr(lg, *spgd, vaddr);
release_pte(*spte);
if (gpte.flags & (_PAGE_DIRTY | _PAGE_ACCESSED)) {
check_gpte(lg, gpte);
*spte = gpte_to_spte(lg, gpte, gpte.flags&_PAGE_DIRTY);
} else
spte->raw.val = 0;
}
}
void guest_set_pte(struct lguest *lg,
unsigned long cr3, unsigned long vaddr, gpte_t gpte)
{
/* Kernel mappings must be changed on all top levels. */
if (vaddr >= lg->page_offset) {
unsigned int i;
for (i = 0; i < ARRAY_SIZE(lg->pgdirs); i++)
if (lg->pgdirs[i].pgdir)
do_set_pte(lg, i, vaddr, gpte);
} else {
int pgdir = find_pgdir(lg, cr3);
if (pgdir != ARRAY_SIZE(lg->pgdirs))
do_set_pte(lg, pgdir, vaddr, gpte);
}
}
void guest_set_pmd(struct lguest *lg, unsigned long cr3, u32 idx)
{
int pgdir;
if (idx >= SWITCHER_PGD_INDEX)
return;
pgdir = find_pgdir(lg, cr3);
if (pgdir < ARRAY_SIZE(lg->pgdirs))
release_pgd(lg, lg->pgdirs[pgdir].pgdir + idx);
}
int init_guest_pagetable(struct lguest *lg, unsigned long pgtable)
{
/* We assume this in flush_user_mappings, so check now */
if (vaddr_to_pgd_index(lg->page_offset) >= SWITCHER_PGD_INDEX)
return -EINVAL;
lg->pgdidx = 0;
lg->pgdirs[lg->pgdidx].cr3 = pgtable;
lg->pgdirs[lg->pgdidx].pgdir = (spgd_t*)get_zeroed_page(GFP_KERNEL);
if (!lg->pgdirs[lg->pgdidx].pgdir)
return -ENOMEM;
return 0;
}
void free_guest_pagetable(struct lguest *lg)
{
unsigned int i;
release_all_pagetables(lg);
for (i = 0; i < ARRAY_SIZE(lg->pgdirs); i++)
free_page((long)lg->pgdirs[i].pgdir);
}
/* Caller must be preempt-safe */
void map_switcher_in_guest(struct lguest *lg, struct lguest_pages *pages)
{
spte_t *switcher_pte_page = __get_cpu_var(switcher_pte_pages);
spgd_t switcher_pgd;
spte_t regs_pte;
/* Since switcher less that 4MB, we simply mug top pte page. */
switcher_pgd.pfn = __pa(switcher_pte_page) >> PAGE_SHIFT;
switcher_pgd.flags = _PAGE_KERNEL;
lg->pgdirs[lg->pgdidx].pgdir[SWITCHER_PGD_INDEX] = switcher_pgd;
/* Map our regs page over stack page. */
regs_pte.pfn = __pa(lg->regs_page) >> PAGE_SHIFT;
regs_pte.flags = _PAGE_KERNEL;
switcher_pte_page[(unsigned long)pages/PAGE_SIZE%PTES_PER_PAGE]
= regs_pte;
}
static void free_switcher_pte_pages(void)
{
unsigned int i;
for_each_possible_cpu(i)
free_page((long)switcher_pte_page(i));
}
static __init void populate_switcher_pte_page(unsigned int cpu,
struct page *switcher_page[],
unsigned int pages)
{
unsigned int i;
spte_t *pte = switcher_pte_page(cpu);
for (i = 0; i < pages; i++) {
pte[i].pfn = page_to_pfn(switcher_page[i]);
pte[i].flags = _PAGE_PRESENT|_PAGE_ACCESSED;
}
/* We only map this CPU's pages, so guest can't see others. */
i = pages + cpu*2;
/* First page (regs) is rw, second (state) is ro. */
pte[i].pfn = page_to_pfn(switcher_page[i]);
pte[i].flags = _PAGE_PRESENT|_PAGE_ACCESSED|_PAGE_RW;
pte[i+1].pfn = page_to_pfn(switcher_page[i+1]);
pte[i+1].flags = _PAGE_PRESENT|_PAGE_ACCESSED;
}
__init int init_pagetables(struct page **switcher_page, unsigned int pages)
{
unsigned int i;
for_each_possible_cpu(i) {
switcher_pte_page(i) = (spte_t *)get_zeroed_page(GFP_KERNEL);
if (!switcher_pte_page(i)) {
free_switcher_pte_pages();
return -ENOMEM;
}
populate_switcher_pte_page(i, switcher_page, pages);
}
return 0;
}
void free_pagetables(void)
{
free_switcher_pte_pages();
}

125
drivers/lguest/segments.c Normal file
View file

@ -0,0 +1,125 @@
#include "lg.h"
static int desc_ok(const struct desc_struct *gdt)
{
/* MBZ=0, P=1, DT=1 */
return ((gdt->b & 0x00209000) == 0x00009000);
}
static int segment_present(const struct desc_struct *gdt)
{
return gdt->b & 0x8000;
}
static int ignored_gdt(unsigned int num)
{
return (num == GDT_ENTRY_TSS
|| num == GDT_ENTRY_LGUEST_CS
|| num == GDT_ENTRY_LGUEST_DS
|| num == GDT_ENTRY_DOUBLEFAULT_TSS);
}
/* We don't allow removal of CS, DS or SS; it doesn't make sense. */
static void check_segment_use(struct lguest *lg, unsigned int desc)
{
if (lg->regs->gs / 8 == desc)
lg->regs->gs = 0;
if (lg->regs->fs / 8 == desc)
lg->regs->fs = 0;
if (lg->regs->es / 8 == desc)
lg->regs->es = 0;
if (lg->regs->ds / 8 == desc
|| lg->regs->cs / 8 == desc
|| lg->regs->ss / 8 == desc)
kill_guest(lg, "Removed live GDT entry %u", desc);
}
static void fixup_gdt_table(struct lguest *lg, unsigned start, unsigned end)
{
unsigned int i;
for (i = start; i < end; i++) {
/* We never copy these ones to real gdt */
if (ignored_gdt(i))
continue;
/* We could fault in switch_to_guest if they are using
* a removed segment. */
if (!segment_present(&lg->gdt[i])) {
check_segment_use(lg, i);
continue;
}
if (!desc_ok(&lg->gdt[i]))
kill_guest(lg, "Bad GDT descriptor %i", i);
/* DPL 0 presumably means "for use by guest". */
if ((lg->gdt[i].b & 0x00006000) == 0)
lg->gdt[i].b |= (GUEST_PL << 13);
/* Set accessed bit, since gdt isn't writable. */
lg->gdt[i].b |= 0x00000100;
}
}
void setup_default_gdt_entries(struct lguest_ro_state *state)
{
struct desc_struct *gdt = state->guest_gdt;
unsigned long tss = (unsigned long)&state->guest_tss;
/* Hypervisor segments. */
gdt[GDT_ENTRY_LGUEST_CS] = FULL_EXEC_SEGMENT;
gdt[GDT_ENTRY_LGUEST_DS] = FULL_SEGMENT;
/* This is the one which we *cannot* copy from guest, since tss
is depended on this lguest_ro_state, ie. this cpu. */
gdt[GDT_ENTRY_TSS].a = 0x00000067 | (tss << 16);
gdt[GDT_ENTRY_TSS].b = 0x00008900 | (tss & 0xFF000000)
| ((tss >> 16) & 0x000000FF);
}
void setup_guest_gdt(struct lguest *lg)
{
lg->gdt[GDT_ENTRY_KERNEL_CS] = FULL_EXEC_SEGMENT;
lg->gdt[GDT_ENTRY_KERNEL_DS] = FULL_SEGMENT;
lg->gdt[GDT_ENTRY_KERNEL_CS].b |= (GUEST_PL << 13);
lg->gdt[GDT_ENTRY_KERNEL_DS].b |= (GUEST_PL << 13);
}
/* This is a fast version for the common case where only the three TLS entries
* have changed. */
void copy_gdt_tls(const struct lguest *lg, struct desc_struct *gdt)
{
unsigned int i;
for (i = GDT_ENTRY_TLS_MIN; i <= GDT_ENTRY_TLS_MAX; i++)
gdt[i] = lg->gdt[i];
}
void copy_gdt(const struct lguest *lg, struct desc_struct *gdt)
{
unsigned int i;
for (i = 0; i < GDT_ENTRIES; i++)
if (!ignored_gdt(i))
gdt[i] = lg->gdt[i];
}
void load_guest_gdt(struct lguest *lg, unsigned long table, u32 num)
{
if (num > ARRAY_SIZE(lg->gdt))
kill_guest(lg, "too many gdt entries %i", num);
lgread(lg, lg->gdt, table, num * sizeof(lg->gdt[0]));
fixup_gdt_table(lg, 0, ARRAY_SIZE(lg->gdt));
lg->changed |= CHANGED_GDT;
}
void guest_load_tls(struct lguest *lg, unsigned long gtls)
{
struct desc_struct *tls = &lg->gdt[GDT_ENTRY_TLS_MIN];
lgread(lg, tls, gtls, sizeof(*tls)*GDT_ENTRY_TLS_ENTRIES);
fixup_gdt_table(lg, GDT_ENTRY_TLS_MIN, GDT_ENTRY_TLS_MAX+1);
lg->changed |= CHANGED_GDT_TLS;
}

159
drivers/lguest/switcher.S Normal file
View file

@ -0,0 +1,159 @@
/* This code sits at 0xFFC00000 to do the low-level guest<->host switch.
There is are two pages above us for this CPU (struct lguest_pages).
The second page (struct lguest_ro_state) becomes read-only after the
context switch. The first page (the stack for traps) remains writable,
but while we're in here, the guest cannot be running.
*/
#include <linux/linkage.h>
#include <asm/asm-offsets.h>
#include "lg.h"
.text
ENTRY(start_switcher_text)
/* %eax points to lguest pages for this CPU. %ebx contains cr3 value.
All normal registers can be clobbered! */
ENTRY(switch_to_guest)
/* Save host segments on host stack. */
pushl %es
pushl %ds
pushl %gs
pushl %fs
/* With CONFIG_FRAME_POINTER, gcc doesn't let us clobber this! */
pushl %ebp
/* Save host stack. */
movl %esp, LGUEST_PAGES_host_sp(%eax)
/* Switch to guest stack: if we get NMI we expect to be there. */
movl %eax, %edx
addl $LGUEST_PAGES_regs, %edx
movl %edx, %esp
/* Switch to guest's GDT, IDT. */
lgdt LGUEST_PAGES_guest_gdt_desc(%eax)
lidt LGUEST_PAGES_guest_idt_desc(%eax)
/* Switch to guest's TSS while GDT still writable. */
movl $(GDT_ENTRY_TSS*8), %edx
ltr %dx
/* Set host's TSS GDT entry to available (clear byte 5 bit 2). */
movl (LGUEST_PAGES_host_gdt_desc+2)(%eax), %edx
andb $0xFD, (GDT_ENTRY_TSS*8 + 5)(%edx)
/* Switch to guest page tables: lguest_pages->state now read-only. */
movl %ebx, %cr3
/* Restore guest regs */
popl %ebx
popl %ecx
popl %edx
popl %esi
popl %edi
popl %ebp
popl %gs
popl %eax
popl %fs
popl %ds
popl %es
/* Skip error code and trap number */
addl $8, %esp
iret
#define SWITCH_TO_HOST \
/* Save guest state */ \
pushl %es; \
pushl %ds; \
pushl %fs; \
pushl %eax; \
pushl %gs; \
pushl %ebp; \
pushl %edi; \
pushl %esi; \
pushl %edx; \
pushl %ecx; \
pushl %ebx; \
/* Load lguest ds segment for convenience. */ \
movl $(LGUEST_DS), %eax; \
movl %eax, %ds; \
/* Figure out where we are, based on stack (at top of regs). */ \
movl %esp, %eax; \
subl $LGUEST_PAGES_regs, %eax; \
/* Put trap number in %ebx before we switch cr3 and lose it. */ \
movl LGUEST_PAGES_regs_trapnum(%eax), %ebx; \
/* Switch to host page tables (host GDT, IDT and stack are in host \
mem, so need this first) */ \
movl LGUEST_PAGES_host_cr3(%eax), %edx; \
movl %edx, %cr3; \
/* Set guest's TSS to available (clear byte 5 bit 2). */ \
andb $0xFD, (LGUEST_PAGES_guest_gdt+GDT_ENTRY_TSS*8+5)(%eax); \
/* Switch to host's GDT & IDT. */ \
lgdt LGUEST_PAGES_host_gdt_desc(%eax); \
lidt LGUEST_PAGES_host_idt_desc(%eax); \
/* Switch to host's stack. */ \
movl LGUEST_PAGES_host_sp(%eax), %esp; \
/* Switch to host's TSS */ \
movl $(GDT_ENTRY_TSS*8), %edx; \
ltr %dx; \
popl %ebp; \
popl %fs; \
popl %gs; \
popl %ds; \
popl %es
/* Return to run_guest_once. */
return_to_host:
SWITCH_TO_HOST
iret
deliver_to_host:
SWITCH_TO_HOST
/* Decode IDT and jump to hosts' irq handler. When that does iret, it
* will return to run_guest_once. This is a feature. */
movl (LGUEST_PAGES_host_idt_desc+2)(%eax), %edx
leal (%edx,%ebx,8), %eax
movzwl (%eax),%edx
movl 4(%eax), %eax
xorw %ax, %ax
orl %eax, %edx
jmp *%edx
/* Real hardware interrupts are delivered straight to the host. Others
cause us to return to run_guest_once so it can decide what to do. Note
that some of these are overridden by the guest to deliver directly, and
never enter here (see load_guest_idt_entry). */
.macro IRQ_STUB N TARGET
.data; .long 1f; .text; 1:
/* Make an error number for most traps, which don't have one. */
.if (\N <> 8) && (\N < 10 || \N > 14) && (\N <> 17)
pushl $0
.endif
pushl $\N
jmp \TARGET
ALIGN
.endm
.macro IRQ_STUBS FIRST LAST TARGET
irq=\FIRST
.rept \LAST-\FIRST+1
IRQ_STUB irq \TARGET
irq=irq+1
.endr
.endm
/* We intercept every interrupt, because we may need to switch back to
* host. Unfortunately we can't tell them apart except by entry
* point, so we need 256 entry points.
*/
.data
.global default_idt_entries
default_idt_entries:
.text
IRQ_STUBS 0 1 return_to_host /* First two traps */
IRQ_STUB 2 handle_nmi /* NMI */
IRQ_STUBS 3 31 return_to_host /* Rest of traps */
IRQ_STUBS 32 127 deliver_to_host /* Real interrupts */
IRQ_STUB 128 return_to_host /* System call (overridden) */
IRQ_STUBS 129 255 deliver_to_host /* Other real interrupts */
/* We ignore NMI and return. */
handle_nmi:
addl $8, %esp
iret
ENTRY(end_switcher_text)

View file

@ -63,6 +63,7 @@ extern void tsc_init(void);
extern void mark_tsc_unstable(char *reason);
extern int unsynchronized_tsc(void);
extern void init_tsc_clocksource(void);
int check_tsc_unstable(void);
/*
* Boot-time check whether the TSCs are synchronized across

View file

@ -3,11 +3,6 @@
#ifndef _ASM_LGUEST_H
#define _ASM_LGUEST_H
/* These are randomly chosen numbers which indicate we're an lguest at boot */
#define LGUEST_MAGIC_EBP 0x4C687970
#define LGUEST_MAGIC_EDI 0x652D4D65
#define LGUEST_MAGIC_ESI 0xFFFFFFFF
#ifndef __ASSEMBLY__
#include <asm/irq.h>
@ -20,7 +15,7 @@
#define LHCALL_LOAD_IDT_ENTRY 6
#define LHCALL_SET_STACK 7
#define LHCALL_TS 8
#define LHCALL_TIMER_READ 9
#define LHCALL_SET_CLOCKEVENT 9
#define LHCALL_HALT 10
#define LHCALL_GET_WALLCLOCK 11
#define LHCALL_BIND_DMA 12
@ -29,6 +24,9 @@
#define LHCALL_SET_PMD 15
#define LHCALL_LOAD_TLS 16
#define LG_CLOCK_MIN_DELTA 100UL
#define LG_CLOCK_MAX_DELTA ULONG_MAX
#define LGUEST_TRAP_ENTRY 0x1F
static inline unsigned long
@ -75,6 +73,8 @@ struct lguest_data
unsigned long reserve_mem;
/* ID of this guest (used by network driver to set ethernet address) */
u16 guestid;
/* KHz for the TSC clock. */
u32 tsc_khz;
/* Fields initialized by the guest at boot: */
/* Instruction range to suppress interrupts even if enabled */

View file

@ -0,0 +1,73 @@
#ifndef _ASM_LGUEST_USER
#define _ASM_LGUEST_USER
/* Everything the "lguest" userspace program needs to know. */
/* They can register up to 32 arrays of lguest_dma. */
#define LGUEST_MAX_DMA 32
/* At most we can dma 16 lguest_dma in one op. */
#define LGUEST_MAX_DMA_SECTIONS 16
/* How many devices? Assume each one wants up to two dma arrays per device. */
#define LGUEST_MAX_DEVICES (LGUEST_MAX_DMA/2)
struct lguest_dma
{
/* 0 if free to be used, filled by hypervisor. */
u32 used_len;
unsigned long addr[LGUEST_MAX_DMA_SECTIONS];
u16 len[LGUEST_MAX_DMA_SECTIONS];
};
struct lguest_block_page
{
/* 0 is a read, 1 is a write. */
int type;
u32 sector; /* Offset in device = sector * 512. */
u32 bytes; /* Length expected to be read/written in bytes */
/* 0 = pending, 1 = done, 2 = done, error */
int result;
u32 num_sectors; /* Disk length = num_sectors * 512 */
};
/* There is a shared page of these. */
struct lguest_net
{
/* Simply the mac address (with multicast bit meaning promisc). */
unsigned char mac[6];
};
/* Where the Host expects the Guest to SEND_DMA console output to. */
#define LGUEST_CONSOLE_DMA_KEY 0
/* We have a page of these descriptors in the lguest_device page. */
struct lguest_device_desc {
u16 type;
#define LGUEST_DEVICE_T_CONSOLE 1
#define LGUEST_DEVICE_T_NET 2
#define LGUEST_DEVICE_T_BLOCK 3
u16 features;
#define LGUEST_NET_F_NOCSUM 0x4000 /* Don't bother checksumming */
#define LGUEST_DEVICE_F_RANDOMNESS 0x8000 /* IRQ is fairly random */
u16 status;
/* 256 and above are device specific. */
#define LGUEST_DEVICE_S_ACKNOWLEDGE 1 /* We have seen device. */
#define LGUEST_DEVICE_S_DRIVER 2 /* We have found a driver */
#define LGUEST_DEVICE_S_DRIVER_OK 4 /* Driver says OK! */
#define LGUEST_DEVICE_S_REMOVED 8 /* Device has gone away. */
#define LGUEST_DEVICE_S_REMOVED_ACK 16 /* Driver has been told. */
#define LGUEST_DEVICE_S_FAILED 128 /* Something actually failed */
u16 num_pages;
u32 pfn;
};
/* Write command first word is a request. */
enum lguest_req
{
LHREQ_INITIALIZE, /* + pfnlimit, pgdir, start, pageoffset */
LHREQ_GETDMA, /* + addr (returns &lguest_dma, irq in ->used_len) */
LHREQ_IRQ, /* + irq */
LHREQ_BREAK, /* + on/off flag (on blocks until someone does off) */
};
#endif /* _ASM_LGUEST_USER */

View file

@ -127,7 +127,6 @@ void __put_task_struct(struct task_struct *tsk)
if (!profile_handoff_task(tsk))
free_task(tsk);
}
EXPORT_SYMBOL_GPL(__put_task_struct);
void __init fork_init(unsigned long mempages)
{