Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1292994
  • 博文数量: 196
  • 博客积分: 4141
  • 博客等级: 中将
  • 技术积分: 2253
  • 用 户 组: 普通用户
  • 注册时间: 2009-03-21 20:04
文章存档

2019年(31)

2016年(1)

2014年(16)

2011年(8)

2010年(25)

2009年(115)

分类: 虚拟化

2009-03-22 15:42:36

/* Userspace control of the guest, via /dev/lguest. */

#include 

#include 

#include 

#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);

}
阅读(1831) | 评论(0) | 转发(2) |
给主人留下些什么吧!~~