This commit is contained in:
Steven Smith committed 2006-12-01 13:12:41 +00:00
commit e812bdf5c4
22 files changed
+2902 -17

No files matched your search

@@ -1867,9 +1867,11 @@ void __init setup_arch(char **cmdline_p)
#endif
#endif
} else {
extern int console_use_vt;
console_use_vt = 0;
#if defined(CONFIG_VT) && defined(CONFIG_DUMMY_CONSOLE)
conswitchp = &dummy_con;
#endif
}
xencons_early_setup();
}
static int
@@ -550,14 +550,16 @@ setup_arch (char **cmdline_p)
xen_start_info->nr_pages, xen_start_info->flags);
if (!is_initial_xendomain()) {
extern int console_use_vt;
#if !defined(CONFIG_VT) || !defined(CONFIG_DUMMY_CONSOLE)
conswitchp = NULL;
console_use_vt = 0;
#endif
}
}
xencons_early_setup();
#endif
#endif
/* enable IA-64 Machine Check Abort Handling unless disabled */
if (!strstr(saved_command_line, "nomca"))
ia64_mca_init();
@@ -983,10 +983,12 @@ void __init setup_arch(char **cmdline_p)
#endif
#endif
} else {
extern int console_use_vt;
console_use_vt = 0;
}
#if defined(CONFIG_VT) && defined(CONFIG_DUMMY_CONSOLE)
conswitchp = &dummy_con;
#endif
}
}
xencons_early_setup();
#else /* CONFIG_XEN */
#ifdef CONFIG_VT
+23
View File
@@ -172,6 +172,29 @@ config XEN_NETDEV_FRONTEND
dedicated device-driver domain, or your master control domain
(domain 0), then you almost certainly want to say Y here.
config XEN_FRAMEBUFFER
tristate "Framebuffer-device frontend driver"
depends on XEN && FB
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
default y
help
The framebuffer-device frontend drivers allows the kernel to create a
virtual framebuffer. This framebuffer can be viewed in another
domain. Unless this domain has access to a real video card, you
probably want to say Y here.
config XEN_KEYBOARD
tristate "Keyboard-device frontend driver"
depends on XEN && XEN_FRAMEBUFFER && INPUT
default y
help
The keyboard-device frontend driver allows the kernel to create a
virtual keyboard. This keyboard can then be driven by another
domain. If you've said Y to CONFIG_XEN_FRAMEBUFFER, you probably
want to say Y here.
config XEN_SCRUB_PAGES
bool "Scrub memory before freeing it to Xen"
default y
@@ -15,3 +15,5 @@ obj-$(CONFIG_XEN_BLKDEV_FRONTEND) += blkfront/
obj-$(CONFIG_XEN_NETDEV_FRONTEND) += netfront/
obj-$(CONFIG_XEN_PCIDEV_BACKEND) += pciback/
obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += pcifront/
obj-$(CONFIG_XEN_FRAMEBUFFER) += fbfront/
obj-$(CONFIG_XEN_KEYBOARD) += fbfront/
@@ -57,6 +57,7 @@
#include <xen/interface/event_channel.h>
#include <asm/hypervisor.h>
#include <xen/evtchn.h>
#include <xen/xenbus.h>
#include <xen/xencons.h>
/*
@@ -65,14 +66,14 @@
* 'xencons=tty' [XC_TTY]: Console attached to '/dev/tty[0-9]+'.
* 'xencons=ttyS' [XC_SERIAL]: Console attached to '/dev/ttyS[0-9]+'.
* 'xencons=xvc' [XC_XVC]: Console attached to '/dev/xvc0'.
* [XC_DEFAULT]: DOM0 -> XC_SERIAL ; all others -> XC_TTY.
* default: DOM0 -> XC_SERIAL ; all others -> XC_TTY.
*
* NB. In mode XC_TTY, we create dummy consoles for tty2-63. This suppresses
* warnings from standard distro startup scripts.
*/
static enum {
XC_OFF, XC_DEFAULT, XC_TTY, XC_SERIAL, XC_XVC
} xc_mode = XC_DEFAULT;
XC_OFF, XC_TTY, XC_SERIAL, XC_XVC
} xc_mode;
static int xc_num = -1;
/* /dev/xvc0 device number allocated by lanana.org. */
@@ -84,17 +85,32 @@ static unsigned long sysrq_requested;
extern int sysrq_enabled;
#endif
void xencons_early_setup(void)
{
extern int console_use_vt;
if (is_initial_xendomain()) {
xc_mode = XC_SERIAL;
} else {
xc_mode = XC_TTY;
console_use_vt = 0;
}
}
static int __init xencons_setup(char *str)
{
char *q;
int n;
extern int console_use_vt;
console_use_vt = 1;
if (!strncmp(str, "ttyS", 4)) {
xc_mode = XC_SERIAL;
str += 4;
} else if (!strncmp(str, "tty", 3)) {
xc_mode = XC_TTY;
str += 3;
console_use_vt = 0;
} else if (!strncmp(str, "xvc", 3)) {
xc_mode = XC_XVC;
str += 3;
@@ -192,14 +208,10 @@ static int __init xen_console_init(void)
goto out;
if (is_initial_xendomain()) {
if (xc_mode == XC_DEFAULT)
xc_mode = XC_SERIAL;
kcons_info.write = kcons_write_dom0;
} else {
if (!xen_start_info->console.domU.evtchn)
goto out;
if (xc_mode == XC_DEFAULT)
xc_mode = XC_TTY;
kcons_info.write = kcons_write;
}
@@ -0,0 +1,2 @@
obj-$(CONFIG_XEN_FRAMEBUFFER) := xenfb.o
obj-$(CONFIG_XEN_KEYBOARD) += xenkbd.o
@@ -0,0 +1,682 @@
/*
* linux/drivers/video/xenfb.c -- Xen para-virtual frame buffer device
*
* Copyright (C) 2005-2006 Anthony Liguori <aliguori@us.ibm.com>
* Copyright (C) 2006 Red Hat, Inc., Markus Armbruster <armbru@redhat.com>
*
* Based on linux/drivers/video/q40fb.c
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive for
* more details.
*/
/*
* TODO:
*
* Switch to grant tables when they become capable of dealing with the
* frame buffer.
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/fb.h>
#include <linux/module.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <asm/hypervisor.h>
#include <xen/evtchn.h>
#include <xen/interface/io/fbif.h>
#include <xen/xenbus.h>
#include <linux/kthread.h>
struct xenfb_mapping
{
struct list_head link;
struct vm_area_struct *vma;
atomic_t map_refs;
int faults;
struct xenfb_info *info;
};
struct xenfb_info
{
struct task_struct *kthread;
wait_queue_head_t wq;
unsigned char *fb;
struct fb_info *fb_info;
struct timer_list refresh;
int dirty;
int x1, y1, x2, y2; /* dirty rectangle,
protected by mm_lock */
spinlock_t mm_lock;
int nr_pages;
struct page **pages;
struct list_head mappings; /* protected by mm_lock */
unsigned evtchn;
int irq;
struct xenfb_page *page;
unsigned long *mfns;
int update_wanted; /* XENFB_TYPE_UPDATE wanted */
struct xenbus_device *xbdev;
};
static int xenfb_fps = 20;
static unsigned long xenfb_mem_len = XENFB_WIDTH * XENFB_HEIGHT * XENFB_DEPTH / 8;
static int xenfb_remove(struct xenbus_device *);
static void xenfb_init_shared_page(struct xenfb_info *);
static int xenfb_connect_backend(struct xenbus_device *, struct xenfb_info *);
static void xenfb_disconnect_backend(struct xenfb_info *);
static void xenfb_do_update(struct xenfb_info *info,
int x, int y, int w, int h)
{
union xenfb_out_event event;
__u32 prod;
event.type = XENFB_TYPE_UPDATE;
event.update.x = x;
event.update.y = y;
event.update.width = w;
event.update.height = h;
prod = info->page->out_prod;
/* caller ensures !xenfb_queue_full() */
mb(); /* ensure ring space available */
XENFB_OUT_RING_REF(info->page, prod) = event;
wmb(); /* ensure ring contents visible */
info->page->out_prod = prod + 1;
notify_remote_via_evtchn(info->evtchn);
}
static int xenfb_queue_full(struct xenfb_info *info)
{
__u32 cons, prod;
prod = info->page->out_prod;
cons = info->page->out_cons;
return prod - cons == XENFB_OUT_RING_LEN;
}
static void xenfb_update_screen(struct xenfb_info *info)
{
unsigned long flags;
int y1, y2, x1, x2;
struct xenfb_mapping *map;
if (!info->update_wanted)
return;
if (xenfb_queue_full(info))
return;
spin_lock_irqsave(&info->mm_lock, flags);
y1 = info->y1;
y2 = info->y2;
x1 = info->x1;
x2 = info->x2;
info->x1 = info->y1 = INT_MAX;
info->x2 = info->y2 = 0;
list_for_each_entry(map, &info->mappings, link) {
if (!map->faults)
continue;
zap_page_range(map->vma, map->vma->vm_start,
map->vma->vm_end - map->vma->vm_start, NULL);
map->faults = 0;
}
spin_unlock_irqrestore(&info->mm_lock, flags);
xenfb_do_update(info, x1, y1, x2 - x1, y2 - y1);
}
static int xenfb_thread(void *data)
{
struct xenfb_info *info = data;
while (!kthread_should_stop()) {
if (info->dirty) {
info->dirty = 0;
xenfb_update_screen(info);
}
wait_event_interruptible(info->wq,
kthread_should_stop() || info->dirty);
try_to_freeze();
}
return 0;
}
static int xenfb_setcolreg(unsigned regno, unsigned red, unsigned green,
unsigned blue, unsigned transp,
struct fb_info *info)
{
u32 v;
if (regno > info->cmap.len)
return 1;
red >>= (16 - info->var.red.length);
green >>= (16 - info->var.green.length);
blue >>= (16 - info->var.blue.length);
v = (red << info->var.red.offset) |
(green << info->var.green.offset) |
(blue << info->var.blue.offset);
/* FIXME is this sane? check against xxxfb_setcolreg()! */
switch (info->var.bits_per_pixel) {
case 16:
case 24:
case 32:
((u32 *)info->pseudo_palette)[regno] = v;
break;
}
return 0;
}
static void xenfb_timer(unsigned long data)
{
struct xenfb_info *info = (struct xenfb_info *)data;
info->dirty = 1;
wake_up(&info->wq);
}
static void __xenfb_refresh(struct xenfb_info *info,
int x1, int y1, int w, int h)
{
int y2, x2;
y2 = y1 + h;
x2 = x1 + w;
if (info->y1 > y1)
info->y1 = y1;
if (info->y2 < y2)
info->y2 = y2;
if (info->x1 > x1)
info->x1 = x1;
if (info->x2 < x2)
info->x2 = x2;
if (timer_pending(&info->refresh))
return;
mod_timer(&info->refresh, jiffies + HZ/xenfb_fps);
}
static void xenfb_refresh(struct xenfb_info *info,
int x1, int y1, int w, int h)
{
unsigned long flags;
spin_lock_irqsave(&info->mm_lock, flags);
__xenfb_refresh(info, x1, y1, w, h);
spin_unlock_irqrestore(&info->mm_lock, flags);
}
static void xenfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
{
struct xenfb_info *info = p->par;
cfb_fillrect(p, rect);
xenfb_refresh(info, rect->dx, rect->dy, rect->width, rect->height);
}
static void xenfb_imageblit(struct fb_info *p, const struct fb_image *image)
{
struct xenfb_info *info = p->par;
cfb_imageblit(p, image);
xenfb_refresh(info, image->dx, image->dy, image->width, image->height);
}
static void xenfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
{
struct xenfb_info *info = p->par;
cfb_copyarea(p, area);
xenfb_refresh(info, area->dx, area->dy, area->width, area->height);
}
static void xenfb_vm_open(struct vm_area_struct *vma)
{
struct xenfb_mapping *map = vma->vm_private_data;
atomic_inc(&map->map_refs);
}
static void xenfb_vm_close(struct vm_area_struct *vma)
{
struct xenfb_mapping *map = vma->vm_private_data;
struct xenfb_info *info = map->info;
unsigned long flags;
spin_lock_irqsave(&info->mm_lock, flags);
if (atomic_dec_and_test(&map->map_refs)) {
list_del(&map->link);
kfree(map);
}
spin_unlock_irqrestore(&info->mm_lock, flags);
}
static struct page *xenfb_vm_nopage(struct vm_area_struct *vma,
unsigned long vaddr, int *type)
{
struct xenfb_mapping *map = vma->vm_private_data;
struct xenfb_info *info = map->info;
int pgnr = (vaddr - vma->vm_start) >> PAGE_SHIFT;
unsigned long flags;
struct page *page;
int y1, y2;
if (pgnr >= info->nr_pages)
return NOPAGE_SIGBUS;
spin_lock_irqsave(&info->mm_lock, flags);
page = info->pages[pgnr];
get_page(page);
map->faults++;
y1 = pgnr * PAGE_SIZE / info->fb_info->fix.line_length;
y2 = (pgnr * PAGE_SIZE + PAGE_SIZE - 1) / info->fb_info->fix.line_length;
if (y2 > info->fb_info->var.yres)
y2 = info->fb_info->var.yres;
__xenfb_refresh(info, 0, y1, info->fb_info->var.xres, y2 - y1);
spin_unlock_irqrestore(&info->mm_lock, flags);
if (type)
*type = VM_FAULT_MINOR;
return page;
}
static struct vm_operations_struct xenfb_vm_ops = {
.open = xenfb_vm_open,
.close = xenfb_vm_close,
.nopage = xenfb_vm_nopage,
};
static int xenfb_mmap(struct fb_info *fb_info, struct vm_area_struct *vma)
{
struct xenfb_info *info = fb_info->par;
unsigned long flags;
struct xenfb_mapping *map;
int map_pages;
if (!(vma->vm_flags & VM_WRITE))
return -EINVAL;
if (!(vma->vm_flags & VM_SHARED))
return -EINVAL;
if (vma->vm_pgoff != 0)
return -EINVAL;
map_pages = (vma->vm_end - vma->vm_start + PAGE_SIZE-1) >> PAGE_SHIFT;
if (map_pages > info->nr_pages)
return -EINVAL;
map = kzalloc(sizeof(*map), GFP_KERNEL);
if (map == NULL)
return -ENOMEM;
map->vma = vma;
map->faults = 0;
map->info = info;
atomic_set(&map->map_refs, 1);
spin_lock_irqsave(&info->mm_lock, flags);
list_add(&map->link, &info->mappings);
spin_unlock_irqrestore(&info->mm_lock, flags);
vma->vm_ops = &xenfb_vm_ops;
vma->vm_flags |= (VM_DONTEXPAND | VM_RESERVED);
vma->vm_private_data = map;
return 0;
}
static struct fb_ops xenfb_fb_ops = {
.owner = THIS_MODULE,
.fb_setcolreg = xenfb_setcolreg,
.fb_fillrect = xenfb_fillrect,
.fb_copyarea = xenfb_copyarea,
.fb_imageblit = xenfb_imageblit,
.fb_mmap = xenfb_mmap,
};
static irqreturn_t xenfb_event_handler(int rq, void *dev_id,
struct pt_regs *regs)
{
/*
* No in events recognized, simply ignore them all.
* If you need to recognize some, see xenbkd's input_handler()
* for how to do that.
*/
struct xenfb_info *info = dev_id;
struct xenfb_page *page = info->page;
if (page->in_cons != page->in_prod) {
info->page->in_cons = info->page->in_prod;
notify_remote_via_evtchn(info->evtchn);
}
return IRQ_HANDLED;
}
static unsigned long vmalloc_to_mfn(void *address)
{
return pfn_to_mfn(vmalloc_to_pfn(address));
}
static int __devinit xenfb_probe(struct xenbus_device *dev,
const struct xenbus_device_id *id)
{
struct xenfb_info *info;
struct fb_info *fb_info;
int ret;
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (info == NULL) {
xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure");
return -ENOMEM;
}
dev->dev.driver_data = info;
info->xbdev = dev;
info->irq = -1;
info->x1 = info->y1 = INT_MAX;
spin_lock_init(&info->mm_lock);
init_waitqueue_head(&info->wq);
init_timer(&info->refresh);
info->refresh.function = xenfb_timer;
info->refresh.data = (unsigned long)info;
INIT_LIST_HEAD(&info->mappings);
info->fb = vmalloc(xenfb_mem_len);
if (info->fb == NULL)
goto error_nomem;
memset(info->fb, 0, xenfb_mem_len);
info->nr_pages = (xenfb_mem_len + PAGE_SIZE - 1) >> PAGE_SHIFT;
info->pages = kmalloc(sizeof(struct page *) * info->nr_pages,
GFP_KERNEL);
if (info->pages == NULL)
goto error_nomem;
info->mfns = vmalloc(sizeof(unsigned long) * info->nr_pages);
if (!info->mfns)
goto error_nomem;
/* set up shared page */
info->page = (void *)__get_free_page(GFP_KERNEL);
if (!info->page)
goto error_nomem;
xenfb_init_shared_page(info);
fb_info = framebuffer_alloc(sizeof(u32) * 256, NULL);
/* see fishy hackery below */
if (fb_info == NULL)
goto error_nomem;
/* FIXME fishy hackery */
fb_info->pseudo_palette = fb_info->par;
fb_info->par = info;
/* /FIXME */
fb_info->screen_base = info->fb;
fb_info->fbops = &xenfb_fb_ops;
fb_info->var.xres_virtual = fb_info->var.xres = info->page->width;
fb_info->var.yres_virtual = fb_info->var.yres = info->page->height;
fb_info->var.bits_per_pixel = info->page->depth;
fb_info->var.red = (struct fb_bitfield){16, 8, 0};
fb_info->var.green = (struct fb_bitfield){8, 8, 0};
fb_info->var.blue = (struct fb_bitfield){0, 8, 0};
fb_info->var.activate = FB_ACTIVATE_NOW;
fb_info->var.height = -1;
fb_info->var.width = -1;
fb_info->var.vmode = FB_VMODE_NONINTERLACED;
fb_info->fix.visual = FB_VISUAL_TRUECOLOR;
fb_info->fix.line_length = info->page->line_length;
fb_info->fix.smem_start = 0;
fb_info->fix.smem_len = xenfb_mem_len;
strcpy(fb_info->fix.id, "xen");
fb_info->fix.type = FB_TYPE_PACKED_PIXELS;
fb_info->fix.accel = FB_ACCEL_NONE;
fb_info->flags = FBINFO_FLAG_DEFAULT;
ret = fb_alloc_cmap(&fb_info->cmap, 256, 0);
if (ret < 0) {
framebuffer_release(fb_info);
xenbus_dev_fatal(dev, ret, "fb_alloc_cmap");
goto error;
}
ret = register_framebuffer(fb_info);
if (ret) {
fb_dealloc_cmap(&info->fb_info->cmap);
framebuffer_release(fb_info);
xenbus_dev_fatal(dev, ret, "register_framebuffer");
goto error;
}
info->fb_info = fb_info;
/* FIXME should this be delayed until backend XenbusStateConnected? */
info->kthread = kthread_run(xenfb_thread, info, "xenfb thread");
if (IS_ERR(info->kthread)) {
ret = PTR_ERR(info->kthread);
info->kthread = NULL;
xenbus_dev_fatal(dev, ret, "register_framebuffer");
goto error;
}
ret = xenfb_connect_backend(dev, info);
if (ret < 0)
goto error;
return 0;
error_nomem:
ret = -ENOMEM;
xenbus_dev_fatal(dev, ret, "allocating device memory");
error:
xenfb_remove(dev);
return ret;
}
static int xenfb_resume(struct xenbus_device *dev)
{
struct xenfb_info *info = dev->dev.driver_data;
xenfb_disconnect_backend(info);
xenfb_init_shared_page(info);
return xenfb_connect_backend(dev, info);
}
static int xenfb_remove(struct xenbus_device *dev)
{
struct xenfb_info *info = dev->dev.driver_data;
del_timer(&info->refresh);
if (info->kthread)
kthread_stop(info->kthread);
xenfb_disconnect_backend(info);
if (info->fb_info) {
unregister_framebuffer(info->fb_info);
fb_dealloc_cmap(&info->fb_info->cmap);
framebuffer_release(info->fb_info);
}
free_page((unsigned long)info->page);
vfree(info->mfns);
kfree(info->pages);
vfree(info->fb);
kfree(info);
return 0;
}
static void xenfb_init_shared_page(struct xenfb_info *info)
{
int i;
for (i = 0; i < info->nr_pages; i++)
info->pages[i] = vmalloc_to_page(info->fb + i * PAGE_SIZE);
for (i = 0; i < info->nr_pages; i++)
info->mfns[i] = vmalloc_to_mfn(info->fb + i * PAGE_SIZE);
info->page->pd[0] = vmalloc_to_mfn(info->mfns);
info->page->pd[1] = 0;
info->page->width = XENFB_WIDTH;
info->page->height = XENFB_HEIGHT;
info->page->depth = XENFB_DEPTH;
info->page->line_length = (info->page->depth / 8) * info->page->width;
info->page->mem_length = xenfb_mem_len;
info->page->in_cons = info->page->in_prod = 0;
info->page->out_cons = info->page->out_prod = 0;
}
static int xenfb_connect_backend(struct xenbus_device *dev,
struct xenfb_info *info)
{
int ret;
struct xenbus_transaction xbt;
ret = xenbus_alloc_evtchn(dev, &info->evtchn);
if (ret)
return ret;
ret = bind_evtchn_to_irqhandler(info->evtchn, xenfb_event_handler,
0, "xenfb", info);
if (ret < 0) {
xenbus_free_evtchn(dev, info->evtchn);
xenbus_dev_fatal(dev, ret, "bind_evtchn_to_irqhandler");
return ret;
}
info->irq = ret;
again:
ret = xenbus_transaction_start(&xbt);
if (ret) {
xenbus_dev_fatal(dev, ret, "starting transaction");
return ret;
}
ret = xenbus_printf(xbt, dev->nodename, "page-ref", "%lu",
virt_to_mfn(info->page));
if (ret)
goto error_xenbus;
ret = xenbus_printf(xbt, dev->nodename, "event-channel", "%u",
info->evtchn);
if (ret)
goto error_xenbus;
ret = xenbus_printf(xbt, dev->nodename, "feature-update", "1");
if (ret)
goto error_xenbus;
ret = xenbus_transaction_end(xbt, 0);
if (ret) {
if (ret == -EAGAIN)
goto again;
xenbus_dev_fatal(dev, ret, "completing transaction");
return ret;
}
xenbus_switch_state(dev, XenbusStateInitialised);
return 0;
error_xenbus:
xenbus_transaction_end(xbt, 1);
xenbus_dev_fatal(dev, ret, "writing xenstore");
return ret;
}
static void xenfb_disconnect_backend(struct xenfb_info *info)
{
if (info->irq >= 0)
unbind_from_irqhandler(info->irq, info);
info->irq = -1;
}
static void xenfb_backend_changed(struct xenbus_device *dev,
enum xenbus_state backend_state)
{
struct xenfb_info *info = dev->dev.driver_data;
int val;
switch (backend_state) {
case XenbusStateInitialising:
case XenbusStateInitialised:
case XenbusStateUnknown:
case XenbusStateClosed:
break;
case XenbusStateInitWait:
InitWait:
xenbus_switch_state(dev, XenbusStateConnected);
break;
case XenbusStateConnected:
/*
* Work around xenbus race condition: If backend goes
* through InitWait to Connected fast enough, we can
* get Connected twice here.
*/
if (dev->state != XenbusStateConnected)
goto InitWait; /* no InitWait seen yet, fudge it */
if (xenbus_scanf(XBT_NIL, info->xbdev->otherend,
"request-update", "%d", &val) < 0)
val = 0;
if (val)
info->update_wanted = 1;
break;
case XenbusStateClosing:
// FIXME is this safe in any dev->state?
xenbus_frontend_closed(dev);
break;
}
}
static struct xenbus_device_id xenfb_ids[] = {
{ "vfb" },
{ "" }
};
static struct xenbus_driver xenfb = {
.name = "vfb",
.owner = THIS_MODULE,
.ids = xenfb_ids,
.probe = xenfb_probe,
.remove = xenfb_remove,
.resume = xenfb_resume,
.otherend_changed = xenfb_backend_changed,
};
static int __init xenfb_init(void)
{
if (!is_running_on_xen())
return -ENODEV;
/* Nothing to do if running in dom0. */
if (is_initial_xendomain())
return -ENODEV;
return xenbus_register_frontend(&xenfb);
}
static void __exit xenfb_cleanup(void)
{
return xenbus_unregister_driver(&xenfb);
}
module_init(xenfb_init);
module_exit(xenfb_cleanup);
MODULE_LICENSE("GPL");
@@ -0,0 +1,300 @@
/*
* linux/drivers/input/keyboard/xenkbd.c -- Xen para-virtual input device
*
* Copyright (C) 2005 Anthony Liguori <aliguori@us.ibm.com>
* Copyright (C) 2006 Red Hat, Inc., Markus Armbruster <armbru@redhat.com>
*
* Based on linux/drivers/input/mouse/sermouse.c
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive for
* more details.
*/
/*
* TODO:
*
* Switch to grant tables together with xenfb.c.
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/input.h>
#include <asm/hypervisor.h>
#include <xen/evtchn.h>
#include <xen/interface/io/fbif.h>
#include <xen/interface/io/kbdif.h>
#include <xen/xenbus.h>
struct xenkbd_info
{
struct input_dev *dev;
struct xenkbd_page *page;
unsigned evtchn;
int irq;
struct xenbus_device *xbdev;
};
static int xenkbd_remove(struct xenbus_device *);
static int xenkbd_connect_backend(struct xenbus_device *, struct xenkbd_info *);
static void xenkbd_disconnect_backend(struct xenkbd_info *);
/*
* Note: if you need to send out events, see xenfb_do_update() for how
* to do that.
*/
static irqreturn_t input_handler(int rq, void *dev_id, struct pt_regs *regs)
{
struct xenkbd_info *info = dev_id;
struct xenkbd_page *page = info->page;
__u32 cons, prod;
prod = page->in_prod;
if (prod == page->out_cons)
return IRQ_HANDLED;
rmb(); /* ensure we see ring contents up to prod */
for (cons = page->in_cons; cons != prod; cons++) {
union xenkbd_in_event *event;
event = &XENKBD_IN_RING_REF(page, cons);
switch (event->type) {
case XENKBD_TYPE_MOTION:
input_report_rel(info->dev, REL_X, event->motion.rel_x);
input_report_rel(info->dev, REL_Y, event->motion.rel_y);
break;
case XENKBD_TYPE_KEY:
input_report_key(info->dev, event->key.keycode, event->key.pressed);
break;
case XENKBD_TYPE_POS:
input_report_abs(info->dev, ABS_X, event->pos.abs_x);
input_report_abs(info->dev, ABS_Y, event->pos.abs_y);
break;
}
}
input_sync(info->dev);
mb(); /* ensure we got ring contents */
page->in_cons = cons;
notify_remote_via_evtchn(info->evtchn);
return IRQ_HANDLED;
}
int __devinit xenkbd_probe(struct xenbus_device *dev,
const struct xenbus_device_id *id)
{
int ret, i;
struct xenkbd_info *info;
struct input_dev *input_dev;
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info) {
xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure");
return -ENOMEM;
}
dev->dev.driver_data = info;
info->xbdev = dev;
info->page = (void *)__get_free_page(GFP_KERNEL);
if (!info->page)
goto error_nomem;
info->page->in_cons = info->page->in_prod = 0;
info->page->out_cons = info->page->out_prod = 0;
input_dev = input_allocate_device();
if (!input_dev)
goto error_nomem;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_ABS);
input_dev->keybit[LONG(BTN_MOUSE)]
= BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
/* TODO additional buttons */
input_dev->relbit[0] = BIT(REL_X) | BIT(REL_Y);
/* FIXME not sure this is quite right */
for (i = 0; i < 256; i++)
set_bit(i, input_dev->keybit);
input_dev->name = "Xen Virtual Keyboard/Mouse";
input_set_abs_params(input_dev, ABS_X, 0, XENFB_WIDTH, 0, 0);
input_set_abs_params(input_dev, ABS_Y, 0, XENFB_HEIGHT, 0, 0);
ret = input_register_device(input_dev);
if (ret) {
input_free_device(input_dev);
xenbus_dev_fatal(dev, ret, "input_register_device");
goto error;
}
info->dev = input_dev;
ret = xenkbd_connect_backend(dev, info);
if (ret < 0)
goto error;
return 0;
error_nomem:
ret = -ENOMEM;
xenbus_dev_fatal(dev, ret, "allocating device memory");
error:
xenkbd_remove(dev);
return ret;
}
static int xenkbd_resume(struct xenbus_device *dev)
{
struct xenkbd_info *info = dev->dev.driver_data;
xenkbd_disconnect_backend(info);
return xenkbd_connect_backend(dev, info);
}
static int xenkbd_remove(struct xenbus_device *dev)
{
struct xenkbd_info *info = dev->dev.driver_data;
xenkbd_disconnect_backend(info);
input_unregister_device(info->dev);
free_page((unsigned long)info->page);
kfree(info);
return 0;
}
static int xenkbd_connect_backend(struct xenbus_device *dev,
struct xenkbd_info *info)
{
int ret;
struct xenbus_transaction xbt;
ret = xenbus_alloc_evtchn(dev, &info->evtchn);
if (ret)
return ret;
ret = bind_evtchn_to_irqhandler(info->evtchn, input_handler, 0,
"xenkbd", info);
if (ret < 0) {
xenbus_free_evtchn(dev, info->evtchn);
xenbus_dev_fatal(dev, ret, "bind_evtchn_to_irqhandler");
return ret;
}
info->irq = ret;
again:
ret = xenbus_transaction_start(&xbt);
if (ret) {
xenbus_dev_fatal(dev, ret, "starting transaction");
return ret;
}
ret = xenbus_printf(xbt, dev->nodename, "page-ref", "%lu",
virt_to_mfn(info->page));
if (ret)
goto error_xenbus;
ret = xenbus_printf(xbt, dev->nodename, "event-channel", "%u",
info->evtchn);
if (ret)
goto error_xenbus;
ret = xenbus_transaction_end(xbt, 0);
if (ret) {
if (ret == -EAGAIN)
goto again;
xenbus_dev_fatal(dev, ret, "completing transaction");
return ret;
}
xenbus_switch_state(dev, XenbusStateInitialised);
return 0;
error_xenbus:
xenbus_transaction_end(xbt, 1);
xenbus_dev_fatal(dev, ret, "writing xenstore");
return ret;
}
static void xenkbd_disconnect_backend(struct xenkbd_info *info)
{
if (info->irq >= 0)
unbind_from_irqhandler(info->irq, info);
info->irq = -1;
}
static void xenkbd_backend_changed(struct xenbus_device *dev,
enum xenbus_state backend_state)
{
struct xenkbd_info *info = dev->dev.driver_data;
int ret, val;
switch (backend_state) {
case XenbusStateInitialising:
case XenbusStateInitialised:
case XenbusStateUnknown:
case XenbusStateClosed:
break;
case XenbusStateInitWait:
InitWait:
ret = xenbus_scanf(XBT_NIL, info->xbdev->otherend,
"feature-abs-pointer", "%d", &val);
if (ret < 0)
val = 0;
if (val) {
ret = xenbus_printf(XBT_NIL, info->xbdev->nodename,
"request-abs-pointer", "1");
if (ret)
; /* FIXME */
}
xenbus_switch_state(dev, XenbusStateConnected);
break;
case XenbusStateConnected:
/*
* Work around xenbus race condition: If backend goes
* through InitWait to Connected fast enough, we can
* get Connected twice here.
*/
if (dev->state != XenbusStateConnected)
goto InitWait; /* no InitWait seen yet, fudge it */
break;
case XenbusStateClosing:
xenbus_frontend_closed(dev);
break;
}
}
static struct xenbus_device_id xenkbd_ids[] = {
{ "vkbd" },
{ "" }
};
static struct xenbus_driver xenkbd = {
.name = "vkbd",
.owner = THIS_MODULE,
.ids = xenkbd_ids,
.probe = xenkbd_probe,
.remove = xenkbd_remove,
.resume = xenkbd_resume,
.otherend_changed = xenkbd_backend_changed,
};
static int __init xenkbd_init(void)
{
if (!is_running_on_xen())
return -ENODEV;
/* Nothing to do if running in dom0. */
if (is_initial_xendomain())
return -ENODEV;
return xenbus_register_frontend(&xenkbd);
}
static void __exit xenkbd_cleanup(void)
{
return xenbus_unregister_driver(&xenkbd);
}
module_init(xenkbd_init);
module_exit(xenkbd_cleanup);
MODULE_LICENSE("GPL");
@@ -14,4 +14,6 @@ void xencons_tx(void);
int xencons_ring_init(void);
int xencons_ring_send(const char *data, unsigned len);
void xencons_early_setup(void);
#endif /* __ASM_XENCONS_H__ */
+1
View File
@@ -882,6 +882,7 @@ unsigned long zap_page_range(struct vm_area_struct *vma, unsigned long address,
tlb_finish_mmu(tlb, address, end);
return end;
}
EXPORT_SYMBOL(zap_page_range);
/*
* Do a quick page-table lookup for a single page.
+1
View File
@@ -19,6 +19,7 @@ SUBDIRS-y += xenstat
SUBDIRS-y += libaio
SUBDIRS-y += blktap
SUBDIRS-y += libfsimage
SUBDIRS-y += xenfb
SUBDIRS-$(LIBXENAPI_BINDINGS) += libxen
# These don't cross-compile
+3 -1
View File
@@ -19,7 +19,7 @@
# A collection of DevControllers
#
from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, usbif
from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, usbif, vfbif
from xen.xend.server.BlktapController import BlktapController
class XendDevices:
@@ -41,6 +41,8 @@ class XendDevices:
'irq': irqif.IRQController,
'usb': usbif.UsbifController,
'tap': BlktapController,
'vfb': vfbif.VfbifController,
'vkbd': vfbif.VkbdifController,
}
#@classmethod
+74
View File
@@ -0,0 +1,74 @@
from xen.xend.server.DevController import DevController
from xen.xend.XendError import VmError
import xen.xend
import os
def spawn_detached(path, args, env):
p = os.fork()
if p == 0:
os.spawnve(os.P_NOWAIT, path, args, env)
os._exit(0)
else:
os.waitpid(p, 0)
class VfbifController(DevController):
"""Virtual frame buffer controller. Handles all vfb devices for a domain.
Note that we only support a single vfb per domain at the moment.
"""
def __init__(self, vm):
DevController.__init__(self, vm)
self.config = {}
def getDeviceDetails(self, config):
"""@see DevController.getDeviceDetails"""
devid = 0
back = {}
front = {}
return (devid, back, front)
def getDeviceConfiguration(self, devid):
r = DevController.getDeviceConfiguration(self, devid)
for (k,v) in self.config.iteritems():
r[k] = v
return r
def createDevice(self, config):
DevController.createDevice(self, config)
self.config = config
std_args = [ "--domid", "%d" % self.vm.getDomid(),
"--title", self.vm.getName() ]
t = config.get("type", None)
if t == "vnc":
# Try to start the vnc backend
args = [xen.util.auxbin.pathTo("xen-vncfb")]
if config.has_key("vncunused"):
args += ["--unused"]
elif config.has_key("vncdisplay"):
args += ["--vncport", "%d" % (5900 + config["vncdisplay"])]
vnclisten = config.get("vnclisten",
xen.xend.XendRoot.instance().get_vnclisten_address())
args += [ "--listen", vnclisten ]
spawn_detached(args[0], args + std_args, os.environ)
elif t == "sdl":
args = [xen.util.auxbin.pathTo("xen-sdlfb")]
env = dict(os.environ)
if config.has_key("display"):
env['DISPLAY'] = config["display"]
if config.has_key("xauthority"):
env['XAUTHORITY'] = config["xauthority"]
spawn_detached(args[0], args + std_args, env)
else:
raise VmError('Unknown vfb type %s (%s)' % (t, repr(config)))
class VkbdifController(DevController):
"""Virtual keyboard controller. Handles all vkbd devices for a domain.
"""
def getDeviceDetails(self, config):
"""@see DevController.getDeviceDetails"""
devid = 0
back = {}
front = {}
return (devid, back, front)
+32 -2
View File
@@ -284,6 +284,18 @@ gopts.var('usbport', val='PATH',
use="""Add a physical USB port to a domain, as specified by the path
to that port. This option may be repeated to add more than one port.""")
gopts.var('vfb', val="type={vnc,sdl},vncunused=1,vncdisplay=N,vnclisten=ADDR,display=DISPLAY,xauthority=XAUTHORITY",
fn=append_value, default=[],
use="""Make the domain a framebuffer backend.
The backend type should be either sdl or vnc.
For type=vnc, connect an external vncviewer. The server will listen
on ADDR (default 127.0.0.1) on port N+5900. N defaults to the
domain id. If vncunused=1, the server will try to find an arbitrary
unused port above 5900.
For type=sdl, a viewer will be started automatically using the
given DISPLAY and XAUTHORITY, which default to the current user's
ones.""")
gopts.var('vif', val="type=TYPE,mac=MAC,bridge=BRIDGE,ip=IPADDR,script=SCRIPT,backend=DOM,vifname=NAME",
fn=append_value, default=[],
use="""Add a network interface with the given MAC address and bridge.
@@ -512,8 +524,8 @@ def configure_image(vals):
config_image.append(['args', vals.extra])
if vals.builder == 'hvm':
configure_hvm(config_image, vals)
configure_hvm(config_image, vals)
return config_image
def configure_disks(config_devs, vals):
@@ -564,6 +576,23 @@ def configure_usb(config_devs, vals):
config_usb = ['usbport', ['path', path]]
config_devs.append(['device', config_usb])
def configure_vfbs(config_devs, vals):
for f in vals.vfb:
d = comma_sep_kv_to_dict(f)
config = ['vfb']
if not d.has_key("type"):
d['type'] = 'sdl'
for (k,v) in d.iteritems():
if not k in [ 'vnclisten', 'vncunused', 'vncdisplay', 'display',
'xauthority', 'type' ]:
err("configuration option %s unknown to vfbs" % k)
config.append([k,v])
if not d.has_key("display") and os.environ.has_key("DISPLAY"):
config.append(["display", os.environ['DISPLAY']])
if not d.has_key("xauthority"):
config.append(["xauthority", get_xauthority()])
config_devs.append(['device', ['vkbd']])
config_devs.append(['device', config])
def configure_security(config, vals):
"""Create the config for ACM security labels.
@@ -742,6 +771,7 @@ def make_config(vals):
configure_vifs(config_devs, vals)
configure_usb(config_devs, vals)
configure_vtpm(config_devs, vals)
configure_vfbs(config_devs, vals)
configure_security(config, vals)
config += config_devs
+35
View File
@@ -0,0 +1,35 @@
XEN_ROOT=../..
include $(XEN_ROOT)/tools/Rules.mk
CFLAGS += -I$(XEN_LIBXC) -I$(XEN_XENSTORE) -I$(XEN_ROOT)/linux-2.6-xen-sparse/include
LDFLAGS += -L$(XEN_LIBXC) -L$(XEN_XENSTORE)
INSTALL = install
INSTALL_PROG = $(INSTALL) -m0755
INSTALL_DIR = $(INSTALL) -d -m0755
.PHONY: all
all: build
.PHONY: build
build: mk-symlinks
$(MAKE) vncfb sdlfb
install: all
$(INSTALL_DIR) -p $(DESTDIR)/usr/$(LIBDIR)/xen/bin
$(INSTALL_PROG) vncfb $(DESTDIR)/usr/$(LIBDIR)/xen/bin/xen-vncfb
$(INSTALL_PROG) sdlfb $(DESTDIR)/usr/$(LIBDIR)/xen/bin/xen-sdlfb
sdlfb: sdlfb.o xenfb.o
sdlfb.o: CFLAGS += $(shell sdl-config --cflags)
sdlfb: LDLIBS += $(shell sdl-config --libs) -lxenctrl -lxenstore
clean:
$(RM) *.o *~ vncfb sdlfb
vncfb: vncfb.o xenfb.o
vncfb.o: CFLAGS += $(shell libvncserver-config --cflags)
vncfb: LDLIBS += $(shell libvncserver-config --libs) -lxenctrl -lxenstore
sdlfb.o xenfb.o vncfb.o: xenfb.h
+342
View File
@@ -0,0 +1,342 @@
#include <SDL.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/select.h>
#include <stdlib.h>
#include <linux/input.h>
#include <getopt.h>
#include <string.h>
#include "xenfb.h"
struct SDLFBData
{
SDL_Surface *dst;
SDL_Surface *src;
};
/*
* Map from scancode to Linux input layer keycode. Scancodes are
* hardware-specific. This map assumes a standard AT or PS/2
* keyboard.
*
* Why use scancodes? We can't use key symbols, because they don't
* identify keys --- they're what keys are mapped to. The standard
* German keymap, for instance, maps both KEY_COMMA and KEY_102ND to
* SDLK_LESS.
*/
static int keymap[256] = {
[9] = KEY_ESC,
[10] = KEY_1,
[11] = KEY_2,
[12] = KEY_3,
[13] = KEY_4,
[14] = KEY_5,
[15] = KEY_6,
[16] = KEY_7,
[17] = KEY_8,
[18] = KEY_9,
[19] = KEY_0,
[20] = KEY_MINUS,
[21] = KEY_EQUAL,
[22] = KEY_BACKSPACE,
[23] = KEY_TAB,
[24] = KEY_Q,
[25] = KEY_W,
[26] = KEY_E,
[27] = KEY_R,
[28] = KEY_T,
[29] = KEY_Y,
[30] = KEY_U,
[31] = KEY_I,
[32] = KEY_O,
[33] = KEY_P,
[34] = KEY_LEFTBRACE,
[35] = KEY_RIGHTBRACE,
[36] = KEY_ENTER,
[37] = KEY_LEFTCTRL,
[38] = KEY_A,
[39] = KEY_S,
[40] = KEY_D,
[41] = KEY_F,
[42] = KEY_G,
[43] = KEY_H,
[44] = KEY_J,
[45] = KEY_K,
[46] = KEY_L,
[47] = KEY_SEMICOLON,
[48] = KEY_APOSTROPHE,
[49] = KEY_GRAVE,
[50] = KEY_LEFTSHIFT,
[51] = KEY_BACKSLASH,
[52] = KEY_Z,
[53] = KEY_X,
[54] = KEY_C,
[55] = KEY_V,
[56] = KEY_B,
[57] = KEY_N,
[58] = KEY_M,
[59] = KEY_COMMA,
[60] = KEY_DOT,
[61] = KEY_SLASH,
[62] = KEY_RIGHTSHIFT,
[63] = KEY_KPASTERISK,
[64] = KEY_LEFTALT,
[65] = KEY_SPACE,
[66] = KEY_CAPSLOCK,
[67] = KEY_F1,
[68] = KEY_F2,
[69] = KEY_F3,
[70] = KEY_F4,
[71] = KEY_F5,
[72] = KEY_F6,
[73] = KEY_F7,
[74] = KEY_F8,
[75] = KEY_F9,
[76] = KEY_F10,
[77] = KEY_NUMLOCK,
[78] = KEY_SCROLLLOCK,
[79] = KEY_KP7,
[80] = KEY_KP8,
[81] = KEY_KP9,
[82] = KEY_KPMINUS,
[83] = KEY_KP4,
[84] = KEY_KP5,
[85] = KEY_KP6,
[86] = KEY_KPPLUS,
[87] = KEY_KP1,
[88] = KEY_KP2,
[89] = KEY_KP3,
[90] = KEY_KP0,
[91] = KEY_KPDOT,
[94] = KEY_102ND, /* FIXME is this correct? */
[95] = KEY_F11,
[96] = KEY_F12,
[108] = KEY_KPENTER,
[109] = KEY_RIGHTCTRL,
[112] = KEY_KPSLASH,
[111] = KEY_SYSRQ,
[113] = KEY_RIGHTALT,
[97] = KEY_HOME,
[98] = KEY_UP,
[99] = KEY_PAGEUP,
[100] = KEY_LEFT,
[102] = KEY_RIGHT,
[103] = KEY_END,
[104] = KEY_DOWN,
[105] = KEY_PAGEDOWN,
[106] = KEY_INSERT,
[107] = KEY_DELETE,
[110] = KEY_PAUSE,
[115] = KEY_LEFTMETA,
[116] = KEY_RIGHTMETA,
[117] = KEY_MENU,
};
static int btnmap[] = {
[SDL_BUTTON_LEFT] = BTN_LEFT,
[SDL_BUTTON_MIDDLE] = BTN_MIDDLE,
[SDL_BUTTON_RIGHT] = BTN_RIGHT,
/* FIXME not 100% sure about these: */
[SDL_BUTTON_WHEELUP] = BTN_FORWARD,
[SDL_BUTTON_WHEELDOWN] BTN_BACK
};
static void sdl_update(struct xenfb *xenfb, int x, int y, int width, int height)
{
struct SDLFBData *data = xenfb->user_data;
SDL_Rect r = { x, y, width, height };
SDL_BlitSurface(data->src, &r, data->dst, &r);
SDL_UpdateRect(data->dst, x, y, width, height);
}
static int sdl_on_event(struct xenfb *xenfb, SDL_Event *event)
{
int x, y, ret;
switch (event->type) {
case SDL_KEYDOWN:
case SDL_KEYUP:
if (keymap[event->key.keysym.scancode] == 0)
break;
ret = xenfb_send_key(xenfb,
event->type == SDL_KEYDOWN,
keymap[event->key.keysym.scancode]);
if (ret < 0)
fprintf(stderr, "Key %d %s lost (%s)\n",
keymap[event->key.keysym.scancode],
event->type == SDL_KEYDOWN ? "down" : "up",
strerror(errno));
break;
case SDL_MOUSEMOTION:
if (xenfb->abs_pointer_wanted) {
SDL_GetMouseState(&x, &y);
ret = xenfb_send_position(xenfb, x, y);
} else {
SDL_GetRelativeMouseState(&x, &y);
ret = xenfb_send_motion(xenfb, x, y);
}
if (ret < 0)
fprintf(stderr, "Pointer to %d,%d lost (%s)\n",
x, y, strerror(errno));
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
if (event->button.button >= sizeof(btnmap) / sizeof(*btnmap))
break;
if (btnmap[event->button.button] == 0)
break;
ret = xenfb_send_key(xenfb,
event->type == SDL_MOUSEBUTTONDOWN,
btnmap[event->button.button]);
if (ret < 0)
fprintf(stderr, "Button %d %s lost (%s)\n",
btnmap[event->button.button] - BTN_MOUSE,
event->type == SDL_MOUSEBUTTONDOWN ? "down" : "up",
strerror(errno));
break;
case SDL_QUIT:
return 0;
}
return 1;
}
static struct option options[] = {
{ "domid", 1, NULL, 'd' },
{ "title", 1, NULL, 't' },
{ NULL }
};
int main(int argc, char **argv)
{
struct xenfb *xenfb;
int domid = -1;
char * title = NULL;
fd_set readfds;
int nfds;
struct SDLFBData data;
SDL_Rect r;
struct timeval tv;
SDL_Event event;
int do_quit = 0;
int opt;
char *endp;
int retval;
while ((opt = getopt_long(argc, argv, "d:t:", options,
NULL)) != -1) {
switch (opt) {
case 'd':
domid = strtol(optarg, &endp, 10);
if (endp == optarg || *endp) {
fprintf(stderr, "Invalid domain id specified\n");
exit(1);
}
break;
case 't':
title = strdup(optarg);
break;
case '?':
exit(1);
}
}
if (optind != argc) {
fprintf(stderr, "Invalid options!\n");
exit(1);
}
if (domid <= 0) {
fprintf(stderr, "Domain ID must be specified!\n");
exit(1);
}
xenfb = xenfb_new();
if (xenfb == NULL) {
fprintf(stderr, "Could not create framebuffer (%s)\n",
strerror(errno));
exit(1);
}
if (xenfb_attach_dom(xenfb, domid) < 0) {
fprintf(stderr, "Could not connect to domain (%s)\n",
strerror(errno));
exit(1);
}
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
fprintf(stderr, "Could not initialize SDL\n");
exit(1);
}
data.dst = SDL_SetVideoMode(xenfb->width, xenfb->height, xenfb->depth,
SDL_SWSURFACE);
if (!data.dst) {
fprintf(stderr, "SDL_SetVideoMode failed\n");
exit(1);
}
data.src = SDL_CreateRGBSurfaceFrom(xenfb->pixels,
xenfb->width, xenfb->height,
xenfb->depth, xenfb->row_stride,
0xFF0000, 0xFF00, 0xFF, 0);
if (!data.src) {
fprintf(stderr, "SDL_CreateRGBSurfaceFrom failed\n");
exit(1);
}
if (title == NULL)
title = strdup("xen-sdlfb");
SDL_WM_SetCaption(title, title);
r.x = r.y = 0;
r.w = xenfb->width;
r.h = xenfb->height;
SDL_BlitSurface(data.src, &r, data.dst, &r);
SDL_UpdateRect(data.dst, 0, 0, xenfb->width, xenfb->height);
xenfb->update = sdl_update;
xenfb->user_data = &data;
SDL_ShowCursor(0);
/*
* We need to wait for fds becoming ready or SDL events to
* arrive. We time out the select after 10ms to poll for SDL
* events. Clunky, but works. Could avoid the clunkiness
* with a separate thread.
*/
for (;;) {
FD_ZERO(&readfds);
nfds = xenfb_select_fds(xenfb, &readfds);
tv = (struct timeval){0, 10000};
if (select(nfds, &readfds, NULL, NULL, &tv) < 0) {
if (errno == EINTR)
continue;
fprintf(stderr,
"Can't select() on event channel (%s)\n",
strerror(errno));
break;
}
while (SDL_PollEvent(&event)) {
if (!sdl_on_event(xenfb, &event))
do_quit = 1;
}
if (do_quit)
break;
retval = xenfb_poll(xenfb, &readfds);
if (retval == -2)
xenfb_teardown(xenfb);
if (retval < 0)
break;
}
xenfb_delete(xenfb);
SDL_Quit();
return 0;
}
+401
View File
@@ -0,0 +1,401 @@
#define _GNU_SOURCE
#include <errno.h>
#include <getopt.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <malloc.h>
#include <rfb/rfb.h>
#include <rfb/keysym.h>
#include <linux/input.h>
#include <xs.h>
#include "xenfb.h"
static int xk2linux[0x10000] = {
[XK_a] = KEY_A,
[XK_b] = KEY_B,
[XK_c] = KEY_C,
[XK_d] = KEY_D,
[XK_e] = KEY_E,
[XK_f] = KEY_F,
[XK_g] = KEY_G,
[XK_h] = KEY_H,
[XK_i] = KEY_I,
[XK_j] = KEY_J,
[XK_k] = KEY_K,
[XK_l] = KEY_L,
[XK_m] = KEY_M,
[XK_n] = KEY_N,
[XK_o] = KEY_O,
[XK_p] = KEY_P,
[XK_q] = KEY_Q,
[XK_r] = KEY_R,
[XK_s] = KEY_S,
[XK_t] = KEY_T,
[XK_u] = KEY_U,
[XK_v] = KEY_V,
[XK_w] = KEY_W,
[XK_x] = KEY_X,
[XK_y] = KEY_Y,
[XK_z] = KEY_Z,
[XK_A] = KEY_A,
[XK_B] = KEY_B,
[XK_C] = KEY_C,
[XK_D] = KEY_D,
[XK_E] = KEY_E,
[XK_F] = KEY_F,
[XK_G] = KEY_G,
[XK_H] = KEY_H,
[XK_I] = KEY_I,
[XK_J] = KEY_J,
[XK_K] = KEY_K,
[XK_L] = KEY_L,
[XK_M] = KEY_M,
[XK_N] = KEY_N,
[XK_O] = KEY_O,
[XK_P] = KEY_P,
[XK_Q] = KEY_Q,
[XK_R] = KEY_R,
[XK_S] = KEY_S,
[XK_T] = KEY_T,
[XK_U] = KEY_U,
[XK_V] = KEY_V,
[XK_W] = KEY_W,
[XK_X] = KEY_X,
[XK_Y] = KEY_Y,
[XK_Z] = KEY_Z,
[XK_0] = KEY_0,
[XK_1] = KEY_1,
[XK_2] = KEY_2,
[XK_3] = KEY_3,
[XK_4] = KEY_4,
[XK_5] = KEY_5,
[XK_6] = KEY_6,
[XK_7] = KEY_7,
[XK_8] = KEY_8,
[XK_9] = KEY_9,
[XK_Return] = KEY_ENTER,
[XK_BackSpace] = KEY_BACKSPACE,
[XK_Tab] = KEY_TAB,
[XK_Pause] = KEY_PAUSE,
[XK_Delete] = KEY_DELETE,
[XK_slash] = KEY_SLASH,
[XK_minus] = KEY_MINUS,
[XK_equal] = KEY_EQUAL,
[XK_Escape] = KEY_ESC,
[XK_braceleft] = KEY_LEFTBRACE,
[XK_braceright] = KEY_RIGHTBRACE,
[XK_bracketleft] = KEY_LEFTMETA,
[XK_bracketright] = KEY_RIGHTMETA,
[XK_Control_L] = KEY_LEFTCTRL,
[XK_Control_R] = KEY_RIGHTCTRL,
[XK_Shift_L] = KEY_LEFTSHIFT,
[XK_Shift_R] = KEY_RIGHTSHIFT,
[XK_Alt_L] = KEY_LEFTALT,
[XK_Alt_R] = KEY_RIGHTALT,
[XK_semicolon] = KEY_SEMICOLON,
[XK_apostrophe] = KEY_APOSTROPHE,
[XK_grave] = KEY_GRAVE,
[XK_backslash] = KEY_BACKSLASH,
[XK_comma] = KEY_COMMA,
[XK_period] = KEY_DOT,
[XK_space] = KEY_SPACE,
[XK_Caps_Lock] = KEY_CAPSLOCK,
[XK_Num_Lock] = KEY_NUMLOCK,
[XK_Scroll_Lock] = KEY_SCROLLLOCK,
[XK_Sys_Req] = KEY_SYSRQ,
[XK_Linefeed] = KEY_LINEFEED,
[XK_Home] = KEY_HOME,
[XK_Pause] = KEY_PAUSE,
[XK_F1] = KEY_F1,
[XK_F2] = KEY_F2,
[XK_F3] = KEY_F3,
[XK_F4] = KEY_F4,
[XK_F5] = KEY_F5,
[XK_F6] = KEY_F6,
[XK_F7] = KEY_F7,
[XK_F8] = KEY_F8,
[XK_F9] = KEY_F9,
[XK_F10] = KEY_F10,
[XK_F11] = KEY_F11,
[XK_F12] = KEY_F12,
[XK_Up] = KEY_UP,
[XK_Page_Up] = KEY_PAGEUP,
[XK_Left] = KEY_LEFT,
[XK_Right] = KEY_RIGHT,
[XK_End] = KEY_END,
[XK_Down] = KEY_DOWN,
[XK_Page_Down] = KEY_PAGEDOWN,
[XK_Insert] = KEY_INSERT,
[XK_colon] = KEY_SEMICOLON,
[XK_quotedbl] = KEY_APOSTROPHE,
[XK_less] = KEY_COMMA,
[XK_greater] = KEY_DOT,
[XK_question] = KEY_SLASH,
[XK_bar] = KEY_BACKSLASH,
[XK_asciitilde] = KEY_GRAVE,
[XK_exclam] = KEY_1,
[XK_at] = KEY_2,
[XK_numbersign] = KEY_3,
[XK_dollar] = KEY_4,
[XK_percent] = KEY_5,
[XK_asciicircum] = KEY_6,
[XK_ampersand] = KEY_7,
[XK_asterisk] = KEY_8,
[XK_parenleft] = KEY_9,
[XK_parenright] = KEY_0,
[XK_underscore] = KEY_MINUS,
[XK_plus] = KEY_EQUAL,
};
static void on_kbd_event(rfbBool down, rfbKeySym keycode, rfbClientPtr cl)
{
/*
* We need to map to the key's Linux input layer keycode.
* Unfortunately, we don't get the key here, only the
* rfbKeySym, which is what the key is mapped to. Mapping
* back to the key is impossible in general, even when you
* know the keymap. For instance, the standard German keymap
* maps both KEY_COMMA and KEY_102ND to XK_less. We simply
* assume standard US layout. This sucks.
*/
rfbScreenInfoPtr server = cl->screen;
struct xenfb *xenfb = server->screenData;
if (keycode >= sizeof(xk2linux) / sizeof(*xk2linux))
return;
if (xk2linux[keycode] == 0)
return;
if (xenfb_send_key(xenfb, down, xk2linux[keycode]) < 0)
fprintf(stderr, "Key %d %s lost (%s)\n",
xk2linux[keycode], down ? "down" : "up",
strerror(errno));
}
static void on_ptr_event(int buttonMask, int x, int y, rfbClientPtr cl)
{
/* initial pointer state: at (0,0), buttons up */
static int last_x, last_y, last_button;
rfbScreenInfoPtr server = cl->screen;
struct xenfb *xenfb = server->screenData;
int i, last_down, down, ret;
for (i = 0; i < 8; i++) {
last_down = last_button & (1 << i);
down = buttonMask & (1 << i);
if (down == last_down)
continue;
/* FIXME this assumes buttons are numbered the same; verify they are */
if (xenfb_send_key(xenfb, down != 0, BTN_MOUSE + i) < 0)
fprintf(stderr, "Button %d %s lost (%s)\n",
i, down ? "down" : "up", strerror(errno));
}
if (x != last_x || y != last_y) {
if (xenfb->abs_pointer_wanted)
ret = xenfb_send_position(xenfb, x, y);
else
ret = xenfb_send_motion(xenfb, x - last_x, y - last_y);
if (ret < 0)
fprintf(stderr, "Pointer to %d,%d lost (%s)\n",
x, y, strerror(errno));
}
last_button = buttonMask;
last_x = x;
last_y = y;
}
static void xenstore_write_vncport(int port, int domid)
{
char *buf = NULL, *path;
char portstr[10];
struct xs_handle *xsh = NULL;
xsh = xs_daemon_open();
if (xsh == NULL)
return;
path = xs_get_domain_path(xsh, domid);
if (path == NULL) {
fprintf(stderr, "Can't get domain path (%s)\n",
strerror(errno));
goto out;
}
if (asprintf(&buf, "%s/console/vnc-port", path) == -1) {
fprintf(stderr, "Can't make vncport path\n");
goto out;
}
if (snprintf(portstr, sizeof(portstr), "%d", port) == -1) {
fprintf(stderr, "Can't make vncport value\n");
goto out;
}
if (!xs_write(xsh, XBT_NULL, buf, portstr, strlen(portstr)))
fprintf(stderr, "Can't set vncport (%s)\n",
strerror(errno));
out:
free(buf);
}
static void vnc_update(struct xenfb *xenfb, int x, int y, int w, int h)
{
rfbScreenInfoPtr server = xenfb->user_data;
rfbMarkRectAsModified(server, x, y, x + w, y + h);
}
static struct option options[] = {
{ "domid", 1, NULL, 'd' },
{ "vncport", 1, NULL, 'p' },
{ "title", 1, NULL, 't' },
{ "unused", 0, NULL, 'u' },
{ "listen", 1, NULL, 'l' },
{ NULL }
};
int main(int argc, char **argv)
{
rfbScreenInfoPtr server;
char *fake_argv[7] = { "vncfb", "-rfbport", "5901",
"-desktop", "xen-vncfb",
"-listen", "127.0.0.1" };
int fake_argc = sizeof(fake_argv) / sizeof(fake_argv[0]);
int domid = -1, port = -1;
char *title = NULL;
char *listen = NULL;
bool unused = false;
int opt;
struct xenfb *xenfb;
fd_set readfds;
int nfds;
char portstr[10];
char *endp;
int r;
while ((opt = getopt_long(argc, argv, "d:p:t:u", options,
NULL)) != -1) {
switch (opt) {
case 'd':
errno = 0;
domid = strtol(optarg, &endp, 10);
if (endp == optarg || *endp || errno) {
fprintf(stderr, "Invalid domain id specified\n");
exit(1);
}
break;
case 'p':
errno = 0;
port = strtol(optarg, &endp, 10);
if (endp == optarg || *endp || errno) {
fprintf(stderr, "Invalid port specified\n");
exit(1);
}
break;
case 't':
title = strdup(optarg);
break;
case 'u':
unused = true;
break;
case 'l':
listen = strdup(optarg);
break;
case '?':
exit(1);
}
}
if (optind != argc) {
fprintf(stderr, "Invalid options!\n");
exit(1);
}
if (domid <= 0) {
fprintf(stderr, "Domain ID must be specified!\n");
exit(1);
}
if (port <= 0)
port = 5900 + domid;
if (snprintf(portstr, sizeof(portstr), "%d", port) == -1) {
fprintf(stderr, "Invalid port specified\n");
exit(1);
}
fake_argv[2] = portstr;
if (title != NULL)
fake_argv[4] = title;
if (listen != NULL)
fake_argv[6] = listen;
signal(SIGPIPE, SIG_IGN);
xenfb = xenfb_new();
if (xenfb == NULL) {
fprintf(stderr, "Could not create framebuffer (%s)\n",
strerror(errno));
exit(1);
}
if (xenfb_attach_dom(xenfb, domid) < 0) {
fprintf(stderr, "Could not connect to domain (%s)\n",
strerror(errno));
exit(1);
}
server = rfbGetScreen(&fake_argc, fake_argv,
xenfb->width, xenfb->height,
8, 3, xenfb->depth / 8);
if (server == NULL) {
fprintf(stderr, "Could not create VNC server\n");
exit(1);
}
xenfb->user_data = server;
xenfb->update = vnc_update;
if (unused)
server->autoPort = true;
server->serverFormat.redShift = 16;
server->serverFormat.greenShift = 8;
server->serverFormat.blueShift = 0;
server->kbdAddEvent = on_kbd_event;
server->ptrAddEvent = on_ptr_event;
server->frameBuffer = xenfb->pixels;
server->screenData = xenfb;
server->cursor = NULL;
rfbInitServer(server);
rfbRunEventLoop(server, -1, true);
xenstore_write_vncport(server->port, domid);
for (;;) {
FD_ZERO(&readfds);
nfds = xenfb_select_fds(xenfb, &readfds);
if (select(nfds, &readfds, NULL, NULL, NULL) < 0) {
if (errno == EINTR)
continue;
fprintf(stderr,
"Can't select() on event channel (%s)\n",
strerror(errno));
break;
}
r = xenfb_poll(xenfb, &readfds);
if (r == -2)
xenfb_teardown(xenfb);
if (r < 0)
break;
}
rfbScreenCleanup(server);
xenfb_delete(xenfb);
return 0;
}
+711
View File
@@ -0,0 +1,711 @@
#include <stdarg.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <xenctrl.h>
#include <xen/io/xenbus.h>
#include <xen/io/fbif.h>
#include <xen/io/kbdif.h>
#include <sys/select.h>
#include <stdbool.h>
#include <xen/linux/evtchn.h>
#include <xen/event_channel.h>
#include <sys/mman.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <xs.h>
#include "xenfb.h"
// FIXME defend against malicious frontend?
struct xenfb_device {
const char *devicetype;
char nodename[64]; /* backend xenstore dir */
char otherend[64]; /* frontend xenstore dir */
int otherend_id; /* frontend domid */
enum xenbus_state state; /* backend state */
void *page; /* shared page */
evtchn_port_t port;
struct xenfb_private *xenfb;
};
struct xenfb_private {
struct xenfb pub;
int evt_xch; /* event channel driver handle */
int xc; /* hypervisor interface handle */
struct xs_handle *xsh; /* xs daemon handle */
struct xenfb_device fb, kbd;
size_t fb_len; /* size of framebuffer */
};
static void xenfb_detach_dom(struct xenfb_private *);
static char *xenfb_path_in_dom(struct xs_handle *xsh,
char *buf, size_t size,
unsigned domid, const char *fmt, ...)
{
va_list ap;
char *domp = xs_get_domain_path(xsh, domid);
int n;
if (domp == NULL)
return NULL;
n = snprintf(buf, size, "%s/", domp);
free(domp);
if (n >= size)
return NULL;
va_start(ap, fmt);
n += vsnprintf(buf + n, size - n, fmt, ap);
va_end(ap);
if (n >= size)
return NULL;
return buf;
}
static int xenfb_xs_scanf1(struct xs_handle *xsh,
const char *dir, const char *node,
const char *fmt, void *dest)
{
char buf[1024];
char *p;
int ret;
if (snprintf(buf, sizeof(buf), "%s/%s", dir, node) >= sizeof(buf)) {
errno = ENOENT;
return -1;
}
p = xs_read(xsh, XBT_NULL, buf, NULL);
if (!p) {
errno = ENOENT;
return -1;
}
ret = sscanf(p, fmt, dest);
free(p);
if (ret != 1) {
errno = EDOM;
return -1;
}
return ret;
}
static int xenfb_xs_printf(struct xs_handle *xsh,
const char *dir, const char *node, char *fmt, ...)
{
va_list ap;
char key[1024];
char val[1024];
int n;
if (snprintf(key, sizeof(key), "%s/%s", dir, node) >= sizeof(key)) {
errno = ENOENT;
return -1;
}
va_start(ap, fmt);
n = vsnprintf(val, sizeof(val), fmt, ap);
va_end(ap);
if (n >= sizeof(val)) {
errno = ENOSPC; /* close enough */
return -1;
}
if (!xs_write(xsh, XBT_NULL, key, val, n))
return -1;
return 0;
}
static void xenfb_device_init(struct xenfb_device *dev,
const char *type,
struct xenfb_private *xenfb)
{
dev->devicetype = type;
dev->otherend_id = -1;
dev->port = -1;
dev->xenfb = xenfb;
}
int xenfb_device_set_domain(struct xenfb_device *dev, int domid)
{
struct xenfb_private *xenfb = dev->xenfb;
dev->otherend_id = domid;
if (!xenfb_path_in_dom(xenfb->xsh,
dev->otherend, sizeof(dev->otherend),
domid, "device/%s/0", dev->devicetype)) {
errno = ENOENT;
return -1;
}
if (!xenfb_path_in_dom(xenfb->xsh,
dev->nodename, sizeof(dev->nodename),
0, "backend/%s/%d/0", dev->devicetype, domid)) {
errno = ENOENT;
return -1;
}
return 0;
}
struct xenfb *xenfb_new(void)
{
struct xenfb_private *xenfb = malloc(sizeof(*xenfb));
int serrno;
if (xenfb == NULL)
return NULL;
memset(xenfb, 0, sizeof(*xenfb));
xenfb->evt_xch = xenfb->xc = -1;
xenfb_device_init(&xenfb->fb, "vfb", xenfb);
xenfb_device_init(&xenfb->kbd, "vkbd", xenfb);
xenfb->evt_xch = xc_evtchn_open();
if (xenfb->evt_xch == -1)
goto fail;
xenfb->xc = xc_interface_open();
if (xenfb->xc == -1)
goto fail;
xenfb->xsh = xs_daemon_open();
if (!xenfb->xsh)
goto fail;
return &xenfb->pub;
fail:
serrno = errno;
xenfb_delete(&xenfb->pub);
errno = serrno;
return NULL;
}
/* Remove the backend area in xenbus since the framebuffer really is
going away. */
void xenfb_teardown(struct xenfb *xenfb_pub)
{
struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub;
xs_rm(xenfb->xsh, XBT_NULL, xenfb->fb.nodename);
xs_rm(xenfb->xsh, XBT_NULL, xenfb->kbd.nodename);
}
void xenfb_delete(struct xenfb *xenfb_pub)
{
struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub;
xenfb_detach_dom(xenfb);
if (xenfb->xc >= 0)
xc_interface_close(xenfb->xc);
if (xenfb->evt_xch >= 0)
xc_evtchn_close(xenfb->evt_xch);
if (xenfb->xsh)
xs_daemon_close(xenfb->xsh);
free(xenfb);
}
static enum xenbus_state xenfb_read_state(struct xs_handle *xsh,
const char *dir)
{
int ret, state;
ret = xenfb_xs_scanf1(xsh, dir, "state", "%d", &state);
if (ret < 0)
return XenbusStateUnknown;
if ((unsigned)state > XenbusStateClosed)
state = XenbusStateUnknown;
return state;
}
static int xenfb_switch_state(struct xenfb_device *dev,
enum xenbus_state state)
{
struct xs_handle *xsh = dev->xenfb->xsh;
if (xenfb_xs_printf(xsh, dev->nodename, "state", "%d", state) < 0)
return -1;
dev->state = state;
return 0;
}
static int xenfb_wait_for_state(struct xs_handle *xsh, const char *dir,
unsigned awaited)
{
unsigned state, dummy;
char **vec;
for (;;) {
state = xenfb_read_state(xsh, dir);
if (state < 0)
return -1;
if ((1 << state) & awaited)
return state;
vec = xs_read_watch(xsh, &dummy);
if (!vec)
return -1;
free(vec);
}
}
static int xenfb_wait_for_backend_creation(struct xenfb_device *dev)
{
struct xs_handle *xsh = dev->xenfb->xsh;
int state;
if (!xs_watch(xsh, dev->nodename, ""))
return -1;
state = xenfb_wait_for_state(xsh, dev->nodename,
(1 << XenbusStateInitialising)
| (1 << XenbusStateClosed)
#if 1 /* TODO fudging state to permit restarting; to be removed */
| (1 << XenbusStateInitWait)
| (1 << XenbusStateConnected)
| (1 << XenbusStateClosing)
#endif
);
xs_unwatch(xsh, dev->nodename, "");
switch (state) {
#if 1
case XenbusStateInitWait:
case XenbusStateConnected:
printf("Fudging state to %d\n", XenbusStateInitialising); /* FIXME */
#endif
case XenbusStateInitialising:
case XenbusStateClosing:
case XenbusStateClosed:
break;
default:
return -1;
}
return 0;
}
static int xenfb_hotplug(struct xenfb_device *dev)
{
if (xenfb_xs_printf(dev->xenfb->xsh, dev->nodename,
"hotplug-status", "connected"))
return -1;
return 0;
}
static int xenfb_wait_for_frontend_initialised(struct xenfb_device *dev)
{
switch (xenfb_wait_for_state(dev->xenfb->xsh, dev->otherend,
#if 1 /* TODO fudging state to permit restarting; to be removed */
(1 << XenbusStateInitialised)
| (1 << XenbusStateConnected)
#else
1 << XenbusStateInitialised,
#endif
)) {
#if 1
case XenbusStateConnected:
printf("Fudging state to %d\n", XenbusStateInitialised); /* FIXME */
#endif
case XenbusStateInitialised:
break;
default:
return -1;
}
return 0;
}
static int xenfb_map_fb(struct xenfb_private *xenfb, int domid)
{
struct xenfb_page *page = xenfb->fb.page;
int n_fbmfns;
int n_fbdirs;
unsigned long *fbmfns;
n_fbmfns = (xenfb->fb_len + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE;
n_fbdirs = n_fbmfns * sizeof(unsigned long);
n_fbdirs = (n_fbdirs + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE;
/*
* Bug alert: xc_map_foreign_batch() can fail partly and
* return a non-null value. This is a design flaw. When it
* happens, we happily continue here, and later crash on
* access.
*/
fbmfns = xc_map_foreign_batch(xenfb->xc, domid,
PROT_READ, page->pd, n_fbdirs);
if (fbmfns == NULL)
return -1;
xenfb->pub.pixels = xc_map_foreign_batch(xenfb->xc, domid,
PROT_READ | PROT_WRITE, fbmfns, n_fbmfns);
if (xenfb->pub.pixels == NULL) {
munmap(fbmfns, n_fbdirs * XC_PAGE_SIZE);
return -1;
}
return munmap(fbmfns, n_fbdirs * XC_PAGE_SIZE);
}
static int xenfb_bind(struct xenfb_device *dev)
{
struct xenfb_private *xenfb = dev->xenfb;
unsigned long mfn;
evtchn_port_t evtchn;
if (xenfb_xs_scanf1(xenfb->xsh, dev->otherend, "page-ref", "%lu",
&mfn) < 0)
return -1;
if (xenfb_xs_scanf1(xenfb->xsh, dev->otherend, "event-channel", "%u",
&evtchn) < 0)
return -1;
dev->port = xc_evtchn_bind_interdomain(xenfb->evt_xch,
dev->otherend_id, evtchn);
if (dev->port == -1)
return -1;
dev->page = xc_map_foreign_range(xenfb->xc, dev->otherend_id,
XC_PAGE_SIZE, PROT_READ | PROT_WRITE, mfn);
if (dev->page == NULL)
return -1;
return 0;
}
static void xenfb_unbind(struct xenfb_device *dev)
{
if (dev->page) {
munmap(dev->page, XC_PAGE_SIZE);
dev->page = NULL;
}
if (dev->port >= 0) {
xc_evtchn_unbind(dev->xenfb->evt_xch, dev->port);
dev->port = -1;
}
}
static int xenfb_wait_for_frontend_connected(struct xenfb_device *dev)
{
switch (xenfb_wait_for_state(dev->xenfb->xsh, dev->otherend,
1 << XenbusStateConnected)) {
case XenbusStateConnected:
break;
default:
return -1;
}
return 0;
}
static void xenfb_dev_fatal(struct xenfb_device *dev, int err,
const char *fmt, ...)
{
struct xs_handle *xsh = dev->xenfb->xsh;
va_list ap;
char errdir[80];
char buf[1024];
int n;
fprintf(stderr, "%s ", dev->nodename); /* somewhat crude */
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
if (err)
fprintf(stderr, " (%s)", strerror(err));
putc('\n', stderr);
if (!xenfb_path_in_dom(xsh, errdir, sizeof(errdir), 0,
"error/%s", dev->nodename))
goto out; /* FIXME complain */
va_start(ap, fmt);
n = snprintf(buf, sizeof(buf), "%d ", err);
snprintf(buf + n, sizeof(buf) - n, fmt, ap);
va_end(ap);
if (xenfb_xs_printf(xsh, buf, "error", "%s", buf) < 0)
goto out; /* FIXME complain */
out:
xenfb_switch_state(dev, XenbusStateClosing);
}
int xenfb_attach_dom(struct xenfb *xenfb_pub, int domid)
{
struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub;
struct xs_handle *xsh = xenfb->xsh;
int val, serrno;
struct xenfb_page *fb_page;
xenfb_detach_dom(xenfb);
xenfb_device_set_domain(&xenfb->fb, domid);
xenfb_device_set_domain(&xenfb->kbd, domid);
if (xenfb_wait_for_backend_creation(&xenfb->fb) < 0)
goto error;
if (xenfb_wait_for_backend_creation(&xenfb->kbd) < 0)
goto error;
if (xenfb_xs_printf(xsh, xenfb->kbd.nodename, "feature-abs-pointer", "1"))
goto error;
if (xenfb_switch_state(&xenfb->fb, XenbusStateInitWait))
goto error;
if (xenfb_switch_state(&xenfb->kbd, XenbusStateInitWait))
goto error;
if (xenfb_hotplug(&xenfb->fb) < 0)
goto error;
if (xenfb_hotplug(&xenfb->kbd) < 0)
goto error;
if (!xs_watch(xsh, xenfb->fb.otherend, ""))
goto error;
if (!xs_watch(xsh, xenfb->kbd.otherend, ""))
goto error;
if (xenfb_wait_for_frontend_initialised(&xenfb->fb) < 0)
goto error;
if (xenfb_wait_for_frontend_initialised(&xenfb->kbd) < 0)
goto error;
if (xenfb_bind(&xenfb->fb) < 0)
goto error;
if (xenfb_bind(&xenfb->kbd) < 0)
goto error;
if (xenfb_xs_scanf1(xsh, xenfb->fb.otherend, "feature-update",
"%d", &val) < 0)
val = 0;
if (!val) {
errno = ENOTSUP;
goto error;
}
xenfb_xs_printf(xsh, xenfb->fb.nodename, "request-update", "1");
/* TODO check for permitted ranges */
fb_page = xenfb->fb.page;
xenfb->pub.depth = fb_page->depth;
xenfb->pub.width = fb_page->width;
xenfb->pub.height = fb_page->height;
/* TODO check for consistency with the above */
xenfb->fb_len = fb_page->mem_length;
xenfb->pub.row_stride = fb_page->line_length;
if (xenfb_map_fb(xenfb, domid) < 0)
goto error;
if (xenfb_switch_state(&xenfb->fb, XenbusStateConnected))
goto error;
if (xenfb_switch_state(&xenfb->kbd, XenbusStateConnected))
goto error;
if (xenfb_wait_for_frontend_connected(&xenfb->kbd) < 0)
goto error;
if (xenfb_xs_scanf1(xsh, xenfb->kbd.otherend, "request-abs-pointer",
"%d", &val) < 0)
val = 0;
xenfb->pub.abs_pointer_wanted = val;
return 0;
error:
serrno = errno;
xenfb_detach_dom(xenfb);
xenfb_dev_fatal(&xenfb->fb, serrno, "on fire");
xenfb_dev_fatal(&xenfb->kbd, serrno, "on fire");
errno = serrno;
return -1;
}
static void xenfb_detach_dom(struct xenfb_private *xenfb)
{
xenfb_unbind(&xenfb->fb);
xenfb_unbind(&xenfb->kbd);
if (xenfb->pub.pixels) {
munmap(xenfb->pub.pixels, xenfb->fb_len);
xenfb->pub.pixels = NULL;
}
}
static void xenfb_on_fb_event(struct xenfb_private *xenfb)
{
uint32_t prod, cons;
struct xenfb_page *page = xenfb->fb.page;
prod = page->out_prod;
if (prod == page->out_cons)
return;
rmb(); /* ensure we see ring contents up to prod */
for (cons = page->out_cons; cons != prod; cons++) {
union xenfb_out_event *event = &XENFB_OUT_RING_REF(page, cons);
switch (event->type) {
case XENFB_TYPE_UPDATE:
if (xenfb->pub.update)
xenfb->pub.update(&xenfb->pub,
event->update.x, event->update.y,
event->update.width, event->update.height);
break;
}
}
mb(); /* ensure we're done with ring contents */
page->out_cons = cons;
xc_evtchn_notify(xenfb->evt_xch, xenfb->fb.port);
}
static void xenfb_on_kbd_event(struct xenfb_private *xenfb)
{
struct xenkbd_page *page = xenfb->kbd.page;
/* We don't understand any keyboard events, so just ignore them. */
if (page->out_prod == page->out_cons)
return;
page->out_cons = page->out_prod;
xc_evtchn_notify(xenfb->evt_xch, xenfb->kbd.port);
}
static int xenfb_on_state_change(struct xenfb_device *dev)
{
enum xenbus_state state;
state = xenfb_read_state(dev->xenfb->xsh, dev->otherend);
switch (state) {
case XenbusStateUnknown:
/* There was an error reading the frontend state. The
domain has probably gone away; in any case, there's
not much point in us continuing. */
return -1;
case XenbusStateInitialising:
case XenbusStateInitWait:
case XenbusStateInitialised:
case XenbusStateConnected:
break;
case XenbusStateClosing:
xenfb_unbind(dev);
xenfb_switch_state(dev, state);
break;
case XenbusStateClosed:
xenfb_switch_state(dev, state);
}
return 0;
}
/* Returns 0 normally, -1 on error, or -2 if the domain went away. */
int xenfb_poll(struct xenfb *xenfb_pub, fd_set *readfds)
{
struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub;
evtchn_port_t port;
unsigned dummy;
char **vec;
int r;
if (FD_ISSET(xc_evtchn_fd(xenfb->evt_xch), readfds)) {
port = xc_evtchn_pending(xenfb->evt_xch);
if (port == -1)
return -1;
if (port == xenfb->fb.port)
xenfb_on_fb_event(xenfb);
else if (port == xenfb->kbd.port)
xenfb_on_kbd_event(xenfb);
if (xc_evtchn_unmask(xenfb->evt_xch, port) == -1)
return -1;
}
if (FD_ISSET(xs_fileno(xenfb->xsh), readfds)) {
vec = xs_read_watch(xenfb->xsh, &dummy);
free(vec);
r = xenfb_on_state_change(&xenfb->fb);
if (r == 0)
r = xenfb_on_state_change(&xenfb->kbd);
if (r == -1)
return -2;
}
return 0;
}
int xenfb_select_fds(struct xenfb *xenfb_pub, fd_set *readfds)
{
struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub;
int fd1 = xc_evtchn_fd(xenfb->evt_xch);
int fd2 = xs_fileno(xenfb->xsh);
FD_SET(fd1, readfds);
FD_SET(fd2, readfds);
return fd1 > fd2 ? fd1 + 1 : fd2 + 1;
}
static int xenfb_kbd_event(struct xenfb_private *xenfb,
union xenkbd_in_event *event)
{
uint32_t prod;
struct xenkbd_page *page = xenfb->kbd.page;
if (xenfb->kbd.state != XenbusStateConnected)
return 0;
prod = page->in_prod;
if (prod - page->in_cons == XENKBD_IN_RING_LEN) {
errno = EAGAIN;
return -1;
}
mb(); /* ensure ring space available */
XENKBD_IN_RING_REF(page, prod) = *event;
wmb(); /* ensure ring contents visible */
page->in_prod = prod + 1;
return xc_evtchn_notify(xenfb->evt_xch, xenfb->kbd.port);
}
int xenfb_send_key(struct xenfb *xenfb_pub, bool down, int keycode)
{
struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub;
union xenkbd_in_event event;
memset(&event, 0, XENKBD_IN_EVENT_SIZE);
event.type = XENKBD_TYPE_KEY;
event.key.pressed = down ? 1 : 0;
event.key.keycode = keycode;
return xenfb_kbd_event(xenfb, &event);
}
int xenfb_send_motion(struct xenfb *xenfb_pub, int rel_x, int rel_y)
{
struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub;
union xenkbd_in_event event;
memset(&event, 0, XENKBD_IN_EVENT_SIZE);
event.type = XENKBD_TYPE_MOTION;
event.motion.rel_x = rel_x;
event.motion.rel_y = rel_y;
return xenfb_kbd_event(xenfb, &event);
}
int xenfb_send_position(struct xenfb *xenfb_pub, int abs_x, int abs_y)
{
struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub;
union xenkbd_in_event event;
memset(&event, 0, XENKBD_IN_EVENT_SIZE);
event.type = XENKBD_TYPE_POS;
event.pos.abs_x = abs_x;
event.pos.abs_y = abs_y;
return xenfb_kbd_event(xenfb, &event);
}
+35
View File
@@ -0,0 +1,35 @@
#ifndef _XENFB_H_
#define _XENFB_H_
#include <stdbool.h>
#include <sys/types.h>
struct xenfb
{
void *pixels;
int row_stride;
int depth;
int width;
int height;
int abs_pointer_wanted;
void *user_data;
void (*update)(struct xenfb *xenfb, int x, int y, int width, int height);
};
struct xenfb *xenfb_new(void);
void xenfb_delete(struct xenfb *xenfb);
void xenfb_teardown(struct xenfb *xenfb);
int xenfb_attach_dom(struct xenfb *xenfb, int domid);
int xenfb_select_fds(struct xenfb *xenfb, fd_set *readfds);
int xenfb_poll(struct xenfb *xenfb, fd_set *readfds);
int xenfb_send_key(struct xenfb *xenfb, bool down, int keycode);
int xenfb_send_motion(struct xenfb *xenfb, int rel_x, int rel_y);
int xenfb_send_position(struct xenfb *xenfb, int abs_x, int abs_y);
#endif
+116
View File
@@ -0,0 +1,116 @@
/*
* fbif.h -- Xen virtual frame buffer device
*
* Copyright (C) 2005 Anthony Liguori <aliguori@us.ibm.com>
* Copyright (C) 2006 Red Hat, Inc., Markus Armbruster <armbru@redhat.com>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive for
* more details.
*/
#ifndef __XEN_PUBLIC_IO_FBIF_H__
#define __XEN_PUBLIC_IO_FBIF_H__
#include <asm/types.h>
/* Out events (frontend -> backend) */
/*
* Out events may be sent only when requested by backend, and receipt
* of an unknown out event is an error.
*/
/* Event type 1 currently not used */
/*
* Framebuffer update notification event
* Capable frontend sets feature-update in xenstore.
* Backend requests it by setting request-update in xenstore.
*/
#define XENFB_TYPE_UPDATE 2
struct xenfb_update
{
__u8 type; /* XENFB_TYPE_UPDATE */
__s32 x; /* source x */
__s32 y; /* source y */
__s32 width; /* rect width */
__s32 height; /* rect height */
};
#define XENFB_OUT_EVENT_SIZE 40
union xenfb_out_event
{
__u8 type;
struct xenfb_update update;
char pad[XENFB_OUT_EVENT_SIZE];
};
/* In events (backend -> frontend) */
/*
* Frontends should ignore unknown in events.
* No in events currently defined.
*/
#define XENFB_IN_EVENT_SIZE 40
union xenfb_in_event
{
__u8 type;
char pad[XENFB_IN_EVENT_SIZE];
};
/* shared page */
#define XENFB_IN_RING_SIZE 1024
#define XENFB_IN_RING_LEN (XENFB_IN_RING_SIZE / XENFB_IN_EVENT_SIZE)
#define XENFB_IN_RING_OFFS 1024
#define XENFB_IN_RING(page) \
((union xenfb_in_event *)((char *)(page) + XENFB_IN_RING_OFFS))
#define XENFB_IN_RING_REF(page, idx) \
(XENFB_IN_RING((page))[(idx) % XENFB_IN_RING_LEN])
#define XENFB_OUT_RING_SIZE 2048
#define XENFB_OUT_RING_LEN (XENFB_OUT_RING_SIZE / XENFB_OUT_EVENT_SIZE)
#define XENFB_OUT_RING_OFFS (XENFB_IN_RING_OFFS + XENFB_IN_RING_SIZE)
#define XENFB_OUT_RING(page) \
((union xenfb_out_event *)((char *)(page) + XENFB_OUT_RING_OFFS))
#define XENFB_OUT_RING_REF(page, idx) \
(XENFB_OUT_RING((page))[(idx) % XENFB_OUT_RING_LEN])
struct xenfb_page
{
__u32 in_cons, in_prod;
__u32 out_cons, out_prod;
__s32 width; /* the width of the framebuffer (in pixels) */
__s32 height; /* the height of the framebuffer (in pixels) */
__u32 line_length; /* the length of a row of pixels (in bytes) */
__u32 mem_length; /* the length of the framebuffer (in bytes) */
__u8 depth; /* the depth of a pixel (in bits) */
/*
* Framebuffer page directory
*
* Each directory page holds PAGE_SIZE / sizeof(*pd)
* framebuffer pages, and can thus map up to PAGE_SIZE *
* PAGE_SIZE / sizeof(*pd) bytes. With PAGE_SIZE == 4096 and
* sizeof(unsigned long) == 4, that's 4 Megs. Two directory
* pages should be enough for a while.
*/
unsigned long pd[2];
};
/*
* Wart: xenkbd needs to know resolution. Put it here until a better
* solution is found, but don't leak it to the backend.
*/
#ifdef __KERNEL__
#define XENFB_WIDTH 800
#define XENFB_HEIGHT 600
#define XENFB_DEPTH 32
#endif
#endif
+108
View File
@@ -0,0 +1,108 @@
/*
* kbdif.h -- Xen virtual keyboard/mouse
*
* Copyright (C) 2005 Anthony Liguori <aliguori@us.ibm.com>
* Copyright (C) 2006 Red Hat, Inc., Markus Armbruster <armbru@redhat.com>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive for
* more details.
*/
#ifndef __XEN_PUBLIC_IO_KBDIF_H__
#define __XEN_PUBLIC_IO_KBDIF_H__
#include <asm/types.h>
/* In events (backend -> frontend) */
/*
* Frontends should ignore unknown in events.
*/
/* Pointer movement event */
#define XENKBD_TYPE_MOTION 1
/* Event type 2 currently not used */
/* Key event (includes pointer buttons) */
#define XENKBD_TYPE_KEY 3
/*
* Pointer position event
* Capable backend sets feature-abs-pointer in xenstore.
* Frontend requests ot instead of XENKBD_TYPE_MOTION by setting
* request-abs-update in xenstore.
*/
#define XENKBD_TYPE_POS 4
struct xenkbd_motion
{
__u8 type; /* XENKBD_TYPE_MOTION */
__s32 rel_x; /* relative X motion */
__s32 rel_y; /* relative Y motion */
};
struct xenkbd_key
{
__u8 type; /* XENKBD_TYPE_KEY */
__u8 pressed; /* 1 if pressed; 0 otherwise */
__u32 keycode; /* KEY_* from linux/input.h */
};
struct xenkbd_position
{
__u8 type; /* XENKBD_TYPE_POS */
__s32 abs_x; /* absolute X position (in FB pixels) */
__s32 abs_y; /* absolute Y position (in FB pixels) */
};
#define XENKBD_IN_EVENT_SIZE 40
union xenkbd_in_event
{
__u8 type;
struct xenkbd_motion motion;
struct xenkbd_key key;
struct xenkbd_position pos;
char pad[XENKBD_IN_EVENT_SIZE];
};
/* Out events (frontend -> backend) */
/*
* Out events may be sent only when requested by backend, and receipt
* of an unknown out event is an error.
* No out events currently defined.
*/
#define XENKBD_OUT_EVENT_SIZE 40
union xenkbd_out_event
{
__u8 type;
char pad[XENKBD_OUT_EVENT_SIZE];
};
/* shared page */
#define XENKBD_IN_RING_SIZE 2048
#define XENKBD_IN_RING_LEN (XENKBD_IN_RING_SIZE / XENKBD_IN_EVENT_SIZE)
#define XENKBD_IN_RING_OFFS 1024
#define XENKBD_IN_RING(page) \
((union xenkbd_in_event *)((char *)(page) + XENKBD_IN_RING_OFFS))
#define XENKBD_IN_RING_REF(page, idx) \
(XENKBD_IN_RING((page))[(idx) % XENKBD_IN_RING_LEN])
#define XENKBD_OUT_RING_SIZE 1024
#define XENKBD_OUT_RING_LEN (XENKBD_OUT_RING_SIZE / XENKBD_OUT_EVENT_SIZE)
#define XENKBD_OUT_RING_OFFS (XENKBD_IN_RING_OFFS + XENKBD_IN_RING_SIZE)
#define XENKBD_OUT_RING(page) \
((union xenkbd_out_event *)((char *)(page) + XENKBD_OUT_RING_OFFS))
#define XENKBD_OUT_RING_REF(page, idx) \
(XENKBD_OUT_RING((page))[(idx) % XENKBD_OUT_RING_LEN])
struct xenkbd_page
{
__u32 in_cons, in_prod;
__u32 out_cons, out_prod;
};
#endif
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.