WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-devel

Re: [Xen-devel] [PATCH] Paravirt framebuffer frontend kernel support [1/

To: Steven Smith <sos22@xxxxxxxxx>
Subject: Re: [Xen-devel] [PATCH] Paravirt framebuffer frontend kernel support [1/5]
From: Markus Armbruster <armbru@xxxxxxxxxx>
Date: Sat, 30 Sep 2006 10:52:13 +0200
Cc: aliguori <aliguori@xxxxxxxxxxxxxxx>, Jeremy Katz <katzj@xxxxxxxxxx>, xen-devel <xen-devel@xxxxxxxxxxxxxxxxxxx>, sos22@xxxxxxxxxxxxx
Delivery-date: Sat, 30 Sep 2006 01:53:24 -0700
Envelope-to: www-data@xxxxxxxxxxxxxxxxxx
In-reply-to: <20060910093801.GA1874@xxxxxxxxx> (Steven Smith's message of "Sun, 10 Sep 2006 10:38:01 +0100")
List-help: <mailto:xen-devel-request@lists.xensource.com?subject=help>
List-id: Xen developer discussion <xen-devel.lists.xensource.com>
List-post: <mailto:xen-devel@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=unsubscribe>
References: <1157227080.11059.38.camel@xxxxxxxxxxxxxx> <20060904090045.GA4812@xxxxxxxxx> <87hczgrx07.fsf@xxxxxxxxxxxxxxxxx> <20060910093801.GA1874@xxxxxxxxx>
Sender: xen-devel-bounces@xxxxxxxxxxxxxxxxxxx
User-agent: Gnus/5.1008 (Gnus v5.10.8) Emacs/21.4 (gnu/linux)
Steven Smith <sos22@xxxxxxxxx> writes:

>> > Okay, this fixes a lot of the things I complained about last time, and
>> > most of the new problems should be fairly easy to fix up.
>> >
>> > There are still a couple of things from the last round which are
>> > unfixed.  This is mostly my fault for going on holiday at an
>> > inconvenient time; sorry about that.
>> I'm about to commit the same crime :)  Expect me back on the 19th.
> Have fun.
>
>> > The big problem with this is that you don't support shadow translate
>> > mode guests, because the frontend exposes mfns directly to the
>> > backend.  The correct way of fixing this is to use grant tables, but,
>> > as Anthony pointed out, that's quite hard due to the amount of memory
>> > you need to grant access to.
>> >
>> > The easy way of doing this would be to just increase the size of the
>> > grant tables.  Alternatively, you could add support for granting
>> > access to ranges of memory, but that sounds like a whole lot of work.
>> Yes, it would be great if Xen provided the means to do it right, but
>> as Keir wrote that's post 3.0.3.
>> 
>> > You could also put in a quick hack of just translating the mfns in the
>> > backend if the frontend is in shadow translate mode, but that's not
>> > really very nice.
>> As far as I can see, this is our only option right now.  I'm ignorant
>> of `shadow translate mode'; can somebody please educate me on how to
>> detect and translate shadow translate mode frontend mfns in the
>> backend?
> Shadow translate mode is a mode of the shadow page tables in which Xen
> does machine<->physical address translations on behalf of the guest.
> It's mostly used for HVM guests to give them the impression of a
> mostly-contiguous block of memory starting at machine address 0.

How mostly is `mostly used for HVM guests'?  Is it actually used with
PV guests?  If not, coping with it is unnecessarily general --- I'd
rather just detect it (just in case) and bail out.

> If you just bung the guest-supplied machine frame numbers through
> xc_translate_gpfn_list you should be fine.  That does the translation
> if you're in shadow translate mode and does nothing if you're not.

You mean xc_domain_translate_gpfn_list(), I suppose.

>> > It'd also be nice if the protocol supported putting the backend in an
>> > unprivileged domain, which grant tables would give you for free, but
>> > that's a lower priority.
>> I guess that has to wait until we can do the framebuffer mapping
>> nicely.
> Yep.
>
>> > Aside from that, the lack of support for multiple framebuffers in one
>> > guest is kind of annoying.  It's not necessary to have this working in
>> > a first cut of the patch, but it'd be good if the protocol included
>> > some way of adding it in later, which the current one doesn't.
>> Well, the only place that limits us to a single framebuffer is our use
>> of xenstore, isn't it?.  All we need for additional framebuffers then
>> is new xenstore directories, say vfbN and vkbdN, where N is 0, 1, ...
>> 
>> If you feel it's useful, I can rename the xenstore directories to vfb0
>> and vkbd0 now.
> Yes, please.  It'd be good not to have to break the protocol in order
> to add this in the future.  I'd prefer vfb/0 and vkbd/0, though, for
> consistency with other types of devices.

Done.

>> > Hard coding the resolution is also kind of icky, but is again
>> > acceptable for a first cut.  It'd be good to have at least a plan for
>> > how this is supposed to work before the thing gets merged, including
>> > how to change the resolution of a live framebuffer.
>> It's always good to have a plan for future work.  Note, however, that
>> the protocol is easily extensible, and therefore we don't have to fear
>> coding ourselves into a box now that we have to break later, just
>> for lack of foresight.
> True.
>
>> > Also, the spec you pointed me at last time appears to be out of date:
>> > it still thinks you get the pgdir mfn from start info, which certainly
>> > isn't acceptable for new drivers.
>> Yes, it's out of date.  Let's nail down things here, and if we then
>> feel the spec is useful to have, we update it.
> Good plan.
>
>> >>  /*
>> >>   * Do a quick page-table lookup for a single page.
>> >> diff -r 5fa9b746d24f -r 510c6bceb87f 
>> >> linux-2.6-xen-sparse/drivers/xen/xenfb/Makefile
>> >> --- /dev/null     Thu Jan 01 00:00:00 1970 +0000
>> >> +++ b/linux-2.6-xen-sparse/drivers/xen/xenfb/Makefile     Sat Sep 02 
>> >> 15:11:17 2006 -0400
>> >> @@ -0,0 +1,1 @@
>> >> +obj-$(CONFIG_XEN_FRAMEBUFFER)    := xenfb.o
>> >> diff -r 5fa9b746d24f -r 510c6bceb87f 
>> >> linux-2.6-xen-sparse/drivers/xen/xenfb/xenfb.c
>> >> --- /dev/null     Thu Jan 01 00:00:00 1970 +0000
>> >> +++ b/linux-2.6-xen-sparse/drivers/xen/xenfb/xenfb.c      Sat Sep 02 
>> >> 15:11:17 2006 -0400
>> >> @@ -0,0 +1,571 @@
>> >> +/*
>> >> + * linux/drivers/video/xenfb.c -- Xen para-virtual frame buffer device
>> >> + *
>> >> + * Copyright (C) 2005-2006
>> >> + *
>> >> + *      Anthony Liguori <aliguori@xxxxxxxxxx>
>> >> + *
>> >> + *  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.
>> >> + */
>> >> +
>> >> +#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/xenbus.h>
>> >> +#include <linux/xenfb.h>
>> >> +#include <linux/kthread.h>
>> >> +
>> >> +#define XENFB_WIDTH 800
>> >> +#define XENFB_HEIGHT 600
>> >> +#define XENFB_DEPTH 32
>> >> +
>> >> +static int xenfb_fps = 20;
>> >> +static unsigned long xenfb_mem_len = XENFB_WIDTH * XENFB_HEIGHT * 
>> >> XENFB_DEPTH / 8;
>> >> +
>> >> +struct xenfb_mapping
>> >> +{
>> >> + struct list_head        next;
>> > Perhaps not the best name for a list head?
>> The mappings are kept in a list of struct xenfb_mapping, chained
>> through member next.  I've seen worse names.  Feel free to suggest a
>> better one.
> Well, list_heads are double linked, so next is kind of deceptive.
> Perhaps just ``list''?

Renamed to 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;
>> > Why unsigned char?
>> I think this is a matter of style.  Plain char suggests characters,
>> while unsigned char suggests bytes.  fb points to an array of bytes.
> I'd have gone for void *, but, as you say, matter of style.
>
>> >> + struct fb_fix_screeninfo        *fix;
>> >> + struct fb_var_screeninfo        *var;
>> > Not quite sure what these are for.  Why not just get at them through
>> > the fb_info pointer?
>> Fine with me.
> Thanks.
>
>> >> + struct fb_info                  *fb_info;
>> >> + struct timer_list               refresh;
>> >> + int                             dirty;
>> >
>> >> + int                             y1, y2;
>> >> + int                             x1, x2;
>> > These could perhaps do with slightly more descriptive names?
>> Would you settle for a descriptive comment instead?
> Sure.

Done.

>> >> +
>> >> + struct semaphore                mm_lock;
>> > That's going to confuse people.
>> >
>> > (Me, for a start)
>> What exactly confuses you?  The name?  Could rename to mm_sem.
> Sorry, got my wires crossed a bit here.  Solaris has a global mm_lock,
> and Linux used to, and I thought Linux still did.  Ignore me, I'm
> wibbling.
>
>> >> + int                             nr_pages;
>> >> + struct page                     **pages;
>> >> + struct list_head                mappings;
>> >> +
>> >> + unsigned                        evtchn;
>> >> + int                             irq;
>> >> + struct xenfb_page               *page;
>> >> + unsigned long                   *mfns;
>> >> + u32                             flags;
>> > Maybe a comment to say what sort of flags go in here?
>> Sure.
> Thanks.
>
>> >> +};
>> >> +
>> >> +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;
>> >> + if (prod - info->page->out_cons == XENFB_OUT_RING_LEN)
>> >> +         return;         /* ring buffer full, event lost */
>> > It seems to me like if we lose the event, we probably shouldn't mark
>> > the framebuffer as clean and throw away the update region.  Perhaps
>> > the caller should instead reschedule the timer?
>> Actually, this can't happen.  xenfb_update_screen() ensures there's
>> space in the ring before it proceeds to walk the mappings.  That space
>> can't go away, because no other place puts stuff into the ring, and
>> only one instance of xenfb_do_update() can run (in the kernel thread).
> Okay.
>
>> > Also, is there any reason the test couldn't use xenfb_queue_full?
>> I removed the redundant test instead.
> Thanks.
>
>> >> + 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)
>> >> +{
>> >> + int y1, y2, x1, x2;
>> >> + struct list_head *item;
>> >> + struct xenfb_mapping *map;
>> >> +
>> >> + if (!(info->flags & XENFB_FLAG_UPDATE))
>> >> +         return;
>> >> + if (xenfb_queue_full(info))
>> >> +         return;
>> >> +
>> >> + y1 = info->y1;
>> >> + y2 = info->y2;
>> >> + x1 = info->x1;
>> >> + x2 = info->x2;
>> >> + info->y1 = info->y2 = info->x1 = info->x2 = 0;
>> >> + down(&info->mm_lock);
>> >> + list_for_each(item, &info->mappings) {
>> >> +         map = list_entry(item, struct xenfb_mapping, next);
>> > list_for_each_entry, perhaps?
>> Going to try.
> Thanks.

Done.

>> >> > Could this end up running at the same time as update_screen and race
>> >> > to update dirty?  I suppose it doesn't actually matter too much if it
>> >> > does.
>> >> xenfb_timer() runs from the timer interrupt, sets dirty, then kicks
>> >> the wait queue.
>> >>
>> >> A separate kernel thread running xenfb_thread() sleeps on the wait
>> >> queue.  When it wakes up, and dirty is set, it clears dirty and
>> >> updates the screen.  Then goes back to sleep.
>> >>
>> >> Obviously, dirty is always set when the thread wakes up.  If
>> >> xenfb_timer() runs before the thread goes to sleep again, the wakeup
>> >> is lost, and the value of dirty doesn't matter.  I believe that's
>> >> okay.
>> >
>> > It looks to me like we can lose updates to the screen in that case:
>> >
>> > xenfb_timer runs, sets dirty to 1, kicks the thread
>> >                                         xenfb_thread wakes up
>> >                                         xenfb_thread reads the update 
>> > region
>> > another update happens, extends the update region, reschedules xenfb_timer
>> > xenfb_timer runs again, sets dirty to 2, kicks the thread again
>> >                                         xenfb_thread sets dirty to 0
>> >                                         xenfb_thread sends an update using
>> >                                                 the old region
>> >
>> > I suppose the race is sufficiently unlikely and sufficiently mild that
>> > it can probably be ignored for all realistic purposes.
>> This can't happen, because both use and update of the mappings is
>> protected by mm_lock:
> No it isn't.  Look at xenfb_update_screen: we read and reset the
> update area, and then acquire the lock immediately afterwards.

See below.

>> 
>>   xenfb_timer runs, sets dirty to 1, kicks the thread
>>                                 xenfb_thread wakes up 
>>                                 xenfb_thread sets dirty to 0
>> 
>>                                 xenfb_thread down mm_lock
>>                                 xenfb_thread reads the update region
>>                                 xenfb_thread up mm_lock
>> 
>>                                 xenfb_thread sends an update using
>>                                         the old region
>> 
>> Possibly concurrent:
>> 
>>   another update happens:
>>   down mm_lock
>>   extends the update region, reschedules xenfb_timer
>>   up mm_lock
>> 
>>   xenfb_timer runs again, sets dirty to 1, kicks the thread again
>> 
>> Harmless race: dirty can conceivably become 1 when there's no work to
>> do.  Then xenfb_thread wakes up, finds nothing to do and goes back to
>> sleep.
>> 
>> Looks safe to me.
>>
>> >> +
>> >> +static void xenfb_refresh(struct xenfb_info *info,
>> >> +                   int x1, int y1, int w, int h)
>> > Is there any reason why there can't be multiple calls to this function
>> > live at the same time?  In particular, if vm_nopage and xenfb_copyarea
>> > happen at the same time it looks like we can lose part of the update
>> > area.
>> >
>> > Also, vm_nopage holds the mm_lock when it calls this, whereas the
>> > other callers appear not to.  Was this deliberate?
>> The question is what mm_lock protects.  If it protects info->mappings,
>> then xenfb_refresh() doesn't care about it, as it doesn't access it.
>> 
>> If it should protect x1, x2, y1, y2 as well, then locking looks
>> flawed.
>> 
>> x1, x2, y1, y2 need protection if they can be updated concurrently.
>> Users are xenfb_update_screen() and xenfb_refresh().  The latter runs
>> from fb_ops methods and vm_operations_struct method nopage.  Looks
>> like they need protection, doesn't it?  Anthony?
> Once you've figured this out, could you put a comment somewhere
> sensible, please?

In Anthony's code, mm_lock protects info->mapping.

However, the dirty rectangle x1, y1, x2, y2 needs protection as well,
because it's written from multiple threads.  It could use a separate
lock, but that doesn't seem to be worth the trouble, so I simply use
mm_lock.

>> >> +{
>> >> + int y2, x2;
>> >> +
>> >> + y2 = y1 + h;
>> >> + x2 = x1 + w;
>> >> + if (info->y2 == 0) {
>> >> +         info->y1 = y1;
>> >> +         info->y2 = y2;
>> >> + }
>> >> + if (info->x2 == 0) {
>> >> +         info->x1 = x1;
>> >> +         info->x2 = x2;
>> >> + }
>> > Presumably, these are just looking to see if there's a pre-existing
>> > region, and if not copying the new region across verbatim?  Could you
>> > test dirty instead in that case?
>> What about initializing x1, y1 with an impossibly small value, and x2,
>> y2 with an impossibly large value?
> If you do it the other way around, that would be ideal.

Done.

>> >> +
>> >> + 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);
>> > Would it be sensible to have a fallback so that an update is always
>> > sent to the backend e.g. 100ms after any change to the framebuffer, so
>> > that the user still sees something when the display is being updated
>> > very frequently?
>> Could this be done in a later revision?  Happy to add the idea in a
>> comment now.
>
> xenfb_fps is only 20.  20fps is hardly an excessively fast animation,
> so I'd expect the display to lock up under some reasonably common and
> plausible situations with the current design.  That isn't really good
> enough.
>
> It's not a terribly hard thing to fix:
>
> if (jiffies - info->last_update_sent > HZ/10)
>       xenfb_update_screen(info)
> else
>       mod_timer(&info->refresh, jiffies + HZ/xenfb_fps);
>
> and then add an
>
> info->last_update_sent = jiffies;
>
> to xenfb_update_screen.  You might need to move the locking around a
> little in update_screen, and maybe move the dirty test in there from
> xenfb_thread, but that's about it.

I reread Anthony's code, and I believe it's is just fine.

When the first dirt arrives, the timer is not pending, so it gets
modified to fire in 1/xenfb_fps seconds.

If more dirt arrives before that time, the timer is pending, and
therefore does not get modified.

When the timer fires, xenfb_timer() runs and wakes the refresh thread.
This happens about 1/xenfb_fps seconds after the first dirt arrived.

When the next dirt arrives, the timer is again not pending, and my
argument loops.

If dirt arrives continuously, the timer fires approximately xenfb_fps
times per second.

>> >> + map_pages = (vma->vm_end - vma->vm_start + PAGE_SIZE-1) >> PAGE_SHIFT;
>> >> + if (map_pages > info->nr_pages)
>> >> +         goto out;
>> >> +
>> >> + ret = -ENOMEM;
>> >> + map = kmalloc(sizeof(*map), GFP_KERNEL);
>> >> + if (map == NULL)
>> >> +         goto out;
>> >> + memset(map, 0, sizeof(*map));
>> > kzalloc, maybe?
>> Sure.
> Thanks.
>
>> >> + return IRQ_HANDLED;
>> >> +}
>> >> +
>> >> +static unsigned long vmalloc_to_mfn(void *address)
>> >> +{
>> >> + // FIXME was return pfn_to_mfn(vmalloc_to_pfn(address));
>> > Not quite sure why this comment is here.
>> Accident.  However, arbitrary_virt_to_machine() doesn't appear to
>> exist on ia64, so I went back to the old code.
> Fair enough.
>
>> >> + return arbitrary_virt_to_machine(address) >> PAGE_SHIFT;
>> >> +}
>> >> +
>> >> +static struct xenfb_info *xenfb_info;
>> >> +
>> >> +static int __init xenfb_probe(void)
>> > I'd find it easier to be confident of the error handling here if the
>> > function were split up a little.
>> It's just the way I found it.  My own idea of cleanup would be to make
>> xenfb_cleanup() work for the error case.
> That would also be a good thing.  It would be even better if you could
> do both. :)

All cleanup is now in xenfb_cleanup().

On splitting xenfb_probe(): one step at a time.  If we aim for
perfection in the initial revision, we'll never get it done.

>> >> +
>> >> + info = kmalloc(sizeof(*info), GFP_KERNEL);
>> >> + if (info == NULL)
>> >> +         return -ENOMEM;
>> >> + memset(info, 0, sizeof(*info));
>> >> +
>> >> + INIT_LIST_HEAD(&info->mappings);
>> >> +
>> >> + info->fb = vmalloc(xenfb_mem_len);
>> >> + if (info->fb == NULL)
>> >> +         goto error;
>> >> + 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_vfree;
>> >> + for (i = 0; i < info->nr_pages; i++)
>> >> +         info->pages[i] = vmalloc_to_page(info->fb + i * PAGE_SIZE);
>> >> +
>> >> + fb_info = framebuffer_alloc(sizeof(u32) * 256, NULL);
>> >> + // FIXME sizeof(struct xenfb_info)
>> > Where did sizeof(u32) * 256 come from?  What does the comment mean?
>> I *suspect* that the correct argument is sizeof(struct xenfb_info),
>> but can't yet exclude subtle, evil hackery going on here, so I put in
>> a comment for now.  Anthony, do you remember hackery here?
> I think it's actually the size of the pseudo palette, but I wouldn't
> swear to it.

I think you're right.  See `evil hackery' below.

>> > fb_info isn't released from the error path.
>> Fixing...
> Thanks.
>
>> >> + if (fb_info == NULL)
>> >> +         goto error_kfree;
>> >> +
>> >> + info->mfns = vmalloc(sizeof(unsigned long) * info->nr_pages);
>> > What happens if this fails?  Also, you never seem to vfree this if we
>> > hit an error later on.
>> Fixing...
> Thanks.
>
>> >> + /* set up shared page */
>> >> + info->page = (void *)__get_free_page(GFP_KERNEL);
>> >> + if (!info->page)
>> >> +         goto error_kfree;
>> >> + /* set up event channel */
>> >> + alloc_unbound.dom = DOMID_SELF;
>> >> + alloc_unbound.remote_dom = 0;
>> >> + ret = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound,
>> >> +                                   &alloc_unbound);
>> >> + if (ret)
>> >> +         goto error_freep;
>> >> + info->evtchn = alloc_unbound.port;
>> >> +
>> >> + 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);
>> > What happens if this domain is running in shadow translate mode?  It
>> > looks to me like the backend will end up mapping completely the wrong
>> > frame.
>> Discussion above seems to suggest that our only option is to have the
>> backend detect and translate shadow translate mode frontends, so no
>> action is required here.  Correct?
> Agreed.
>
>> >> + 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;
>> >> +
>> >> + ret = bind_evtchn_to_irqhandler(info->evtchn, xenfb_event_handler,
>> >> +                                 0, "xenfb", info);
>> >> + if (ret < 0) {
>> >> +         close.port = info->evtchn;
>> >> +         HYPERVISOR_event_channel_op(EVTCHNOP_close, &close);
>> >> +         goto error_freep;
>> >> + }
>> >> +
>> >> + info->irq = ret;
>> >> + xenfb_info = info;
>> >> +
>> >> + fb_info->pseudo_palette = fb_info->par;
>> >> + fb_info->par = info;
> Eh?  What was the reasoning behind this?

I have no idea.  Ugly, evil hackery.  The only excuse I can see is
that it works.  Hope to slay it in a future revision.

>> >> + fb_info->screen_base = info->fb;
>> >> +
>> >> + memset(&fb_info->var, 0, sizeof(fb_info->var));
>> >> + memset(&fb_info->fix, 0, sizeof(fb_info->fix));
>> > I think these are already guaranteed to be zero, aren't they?
>> Yes, framebuffer_alloc() clears *fb_info.  Happy to rely on that?
> I can't see any reason not to.

Done.

>> >> + 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;
>> >> +
>> >> + fb_alloc_cmap(&fb_info->cmap, 256, 0);
>> > This can fail, and the error paths don't bother to deallocate it.
>> Fixing...
> Thanks.
>
>> >> +
>> >> + again:
>> >> + ret = xenbus_transaction_start(&xbt);
>> >> + if (ret)
>> >> +         goto error_unreg;
>> >> + ret = xenbus_printf(xbt, "vfb", "page-ref", "%lu",
>> >> +                     virt_to_mfn(info->page));
>> >> + // FIXME grant tables?
>> > Again, what happens in shadow translate mode?
>> >
>> > Also, it's kind of annoying that there's no support for multiple
>> > framebuffers in a domU.  That's very much a wishlist thing, though.
>> See above.
> Ack.
>
>> >> + unregister_framebuffer(fb_info);
>> >> + error_unbind:
>> >> + unbind_from_irqhandler(info->irq, info);
>> >> + // FIXME do we have to stop info->kthread?
>> > If you've started it you certainly need to stop it.
>> Fixing...
> Thanks.
>
>> >> + return -ENODEV;
>> >> +}
>> >> +
>> >> +static int __init xenfb_init(void)
>> >> +{
>> >> + return xenfb_probe();
>> >
>> > From last time:
>> >
>> >> > You might want to consider using xenbus_device and friends here.
>> >> I considered that, but it wasn't obvious to me how to make it fit
>> >> cleanly to a user-space backend.  Care to advise?
>> > I think if the frontend needs to know whether the backend is in kernel
>> > or userspace then we've messed up somewhere.  Why do you expect this
>> > to be difficult?
>> I didn't *expect* it to be difficult, I just couldn't see how to fit
>> Anthony's code to xenbus_device without rewriting more than I like,
>> and I still can't see it.  That's why I asked for advice.
> The probing stuff seems to be fairly self-contained at the moment.
> You'd probably have to rewrite xenfb_probe, but I can't see any need
> to change much beyond that.
>
> Unless I'm just missing something?
>
>> Anyway, I'm reluctant to throw out working code while there's so much
>> code that needs fixing.  I may be less reluctant once we've drained
>> the swamp some more.
> So I think long term we'd probably like to use the standard xenbus
> device setup protocol, going through XenbusStateInitialised,
> XenbusStateConnected, XenbusStateClosing, etc.  That'd be a
> non-backwards-compatible change if we do it later, so it'd be good to
> get it in now.  xenbus_device gives you that for free, although I'll
> admit it is a bit of a pain to use.

We'll have to break backwards compatibility anyway when we switch to
grant tables.  Why dig up the same code twice, and in the meantime
deprive users of the code that works?

>> >> +}
>> >> +
>> >> +static void __exit xenfb_cleanup(void)
>> >> +{
>> >> + struct xenfb_info *info = xenfb_info;
>> >> +
>> >> + unregister_framebuffer(info->fb_info);
>> >> + unbind_from_irqhandler(info->irq, info);
>> >> + free_page((unsigned long)info->page);
>> >> + kfree(info->pages);
>> >> + vfree(info->fb);
>> >> + kfree(info);
>> >> + xenfb_info = NULL;
>> > I don't think this releases all of the memory you allocate in
>> > xenfb_probe.
>> Fixing...
> Thanks.
>
>> >> +   return -ENODEV;
>> >> +#if 0
>> >> +        /* if we're not set up to use graphics mode, then don't 
>> >> initialize */
>> >> + if (xenbus_scanf(XBT_NIL, "console", "use_graphics", "%d", &ret) < 0)
>> >> +   return -ENODEV;
>> >> + if (ret == 0)
>> >> +   return -ENODEV;
>> >> +#endif
>> > Not sure about this.
>> Same as for xenfb.c.  The #if is an accident.
> Ack.
>
>> >> +
>> >> + dev = kmalloc(sizeof(*dev), GFP_KERNEL);
>> >> + input_dev = input_allocate_device();
>> >> + if (!dev || !input_dev)
>> >> +         return -ENOMEM;
>> > You potentially leak memory if one of these fails and the other succeeds.
>> >
>> > Also, the error paths don't release input_dev.  In fact, nothing ever
>> > releases input_dev, unless I'm missing something.
>> Fixing...
> Thanks.
>
>> >> +
>> >> + dev->dev = input_dev;
>> >> + dev->info = (void *)__get_free_page(GFP_KERNEL);
>> >> + if (!dev->info) {
>> >> +         ret = -ENOMEM;
>> >> +         goto error;
>> >> + }
>> >> +
>> >> + alloc_unbound.dom = DOMID_SELF;
>> >> + alloc_unbound.remote_dom = 0;
>> >> + ret = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound,
>> >> +                                   &alloc_unbound);
>> >> + if (ret)
>> >> +         goto error_freep;
>> >> + dev->evtchn = alloc_unbound.port;
>> >> + ret = bind_evtchn_to_irqhandler(dev->evtchn, input_handler, 0,
>> >> +                                 "xenkbd", dev);
>> >> + if (ret < 0)
>> >> +         goto error_freep;
>> > You might want to close the event channel here.
>> Fixing...
> Thanks.
>
>> >> +
>> >> + input_dev->name = "Xen Virtual Keyboard/Mouse";
>> >> +
>> >> + input_register_device(input_dev);
>> > This can fail.
>> Most uses elsewhere in the kernel don't care.  Fixing anyway.
> Yeah, I know, but it's good to get it right anyway.  Thanks.
>
>> >> +
>> >> + again:
>> >> + ret = xenbus_transaction_start(&xbt);
>> >> + if (ret)
>> >> +         goto error_unreg;
>> >> + ret = xenbus_printf(xbt, "vkbd", "page-ref", "%lu",
>> >> +                     virt_to_mfn(dev->info));
>> > There's no reason that I can see not to use grant tables here, and
>> > that'd avoid all of the potential issues with shadow translate mode.
>> Since we have to fool around with shadow translate mode anyway for
>> sharing the framebuffer, I don't quite see why we need to address this
>> right now.  This code works, and there's no shortage of code that
>> needs fixing.
> Alright, leave this for now and fix it when you fix the vfb equivalent
> bits.
>
>> Same for vfb/page-ref.
> Well, that's a reference to a bunch of untranslated frame numbers, so
> it makes more sense for it to be untranslated itself.  The keyboard
> page doesn't require any further translation.
>
>> >> +MODULE_LICENSE("GPL");
>> >> diff -r 5fa9b746d24f -r 510c6bceb87f 
>> >> linux-2.6-xen-sparse/include/linux/xenfb.h
>> >> --- /dev/null     Thu Jan 01 00:00:00 1970 +0000
>> >> +++ b/linux-2.6-xen-sparse/include/linux/xenfb.h  Sat Sep 02 15:11:17 
>> >> 2006 -0400
>> >
>> > This is defining an io protocol, so belongs in xen/include/public/io
>> Okay.
> Thanks.
>
>> >> +#include <asm/types.h>
>> >> +
>> >> +/* out events */
>> >> +
>> >> +#define XENFB_OUT_EVENT_SIZE 40
>> >> +
>> >> +#define XENFB_TYPE_MOTION 1
>> >> +#define XENFB_TYPE_UPDATE 2
>> >> +
>> > The other tagged unions in Xen look more like this:
>> >
>> > struct {
>> >    u8 type;
>> >    union {
>> >            struct {
>> >                    u32 field1;
>> >                    u32 field2;
>> >            } type1;
>> >            struct {
>> >                    u16 field1;
>> >            } type2;
>> >    } u;
>> > };
>> >
>> > I'd find this a lot easier to read than the current approach of
>> > putting the type into the individual event type structures.  Is there
>> > any reason why this couldn't be used here?
>> Matter of taste.  Will change it if you insist.
> It'd be good to be consistent with the other protocols.

How to pad such a struct to a fixed size?

>> >> +struct xenfb_motion          /* currently unused */
>> >> +{
>> >> + __u8 type;          /* XENFB_TYPE_MOTION */
>> >> + __u16 x;            /* The new x coordinate */
>> >> + __u16 y;            /* The new y coordinate */
>> >> +};
>> > I think if the comment said what was moving this'd be a lot clearer.
>> I'm not into documenting unused code I didn't write and don't
>> understand, so I'm going to remove this.
> Even better.
>
>> >> +
>> >> +struct xenfb_update
>> >> +{
>> >> + __u8 type;          /* XENFB_TYPE_UPDATE */
>> >> + __u16 x;            /* source x */
>> >> + __u16 y;            /* source y */
>> >> + __u16 width;        /* rect width */
>> >> + __u16 height;       /* rect height */
>> >> +};
>> >> +
>> >> +union xenfb_out_event
>> >> +{
>> >> + __u8 type;
>> >> + struct xenfb_motion motion;
>> >> + struct xenfb_update update;
>> >> + char _[XENFB_OUT_EVENT_SIZE];
>> > Perhaps ``pad'' would be clearer than ``_''?
>> Works for me.
> Thanks.
>
>> >> +struct xenfb_page
>> >> +{
>> >> + __u16 width;         /* the width of the framebuffer (in pixels) */
>> >> + __u16 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) */
>> >> +
>> >> + unsigned long pd[2];    /* FIXME rename to pgdir? */
>> >> + /* FIXME pd[1] unused at this time, shrink? */
>> > I think it'd be enough to just give some indication of where the magic
>> > number 2 came from.
>> Okay.
> Thanks.
>
>> >> +
>> >> + __u32 in_cons, in_prod;
>> >> + __u32 out_cons, out_prod;
>> >> +};
>> >> +
>> >> +void xenfb_resume(void);
>> > No longer exists.
>> Removed.
> Thanks.
>
>> >> +
>> >> +#endif
>> >> diff -r 5fa9b746d24f -r 510c6bceb87f 
>> >> linux-2.6-xen-sparse/include/linux/xenkbd.h
>> >> --- /dev/null     Thu Jan 01 00:00:00 1970 +0000
>> >> +++ b/linux-2.6-xen-sparse/include/linux/xenkbd.h Sat Sep 02 15:11:17 
>> >> 2006 -0400
>> > Again, this belongs in xen/include/public/io.
>> Okay.
> Thanks.
>
>> >> +union xenkbd_in_event
>> >> +{
>> >> + __u8 type;
>> >> + struct xenkbd_motion motion;
>> >> + struct xenkbd_button button;
>> >> + struct xenkbd_key key;
>> >> + char _[XENKBD_IN_EVENT_SIZE];
>> >> +};
>> >> +
>> >> +/* out events */
>> > Maybe a comment that none are currently defined?
>> Sure.
> Thanks.
>
>> >> +struct xenkbd_info
>> >> +{
>> >> + __u32 in_cons, in_prod;
>> >> + __u32 out_cons, out_prod;
>> >> +};
>> >> +
>> >> +void xenkbd_resume(void);
>> > No longer exists.
>> Removed.
> Thanks.
>
> Steven.

Next try.  The patch contains Amos Waterland's patch for xenconsole to
use /dev/xvc0 instead of taking over ttys, with Jeremy's fixes and
cleanups.  If you need those parts separate, let me know.


diff -r 47c098fdce14 arch/i386/kernel/setup-xen.c
--- a/arch/i386/kernel/setup-xen.c      Wed Sep 20 15:35:23 2006 +0200
+++ b/arch/i386/kernel/setup-xen.c      Sat Sep 30 10:34:01 2006 +0200
@@ -1788,8 +1788,9 @@ 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
        }
 #ifdef CONFIG_X86_TSC
        tsc_init();
diff -r 47c098fdce14 arch/ia64/kernel/setup.c
--- a/arch/ia64/kernel/setup.c  Wed Sep 20 15:35:23 2006 +0200
+++ b/arch/ia64/kernel/setup.c  Sat Sep 30 10:34:01 2006 +0200
@@ -583,9 +583,9 @@ 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
                }
        }
 #endif
diff -r 47c098fdce14 arch/x86_64/kernel/setup-xen.c
--- a/arch/x86_64/kernel/setup-xen.c    Wed Sep 20 15:35:23 2006 +0200
+++ b/arch/x86_64/kernel/setup-xen.c    Sat Sep 30 10:34:01 2006 +0200
@@ -931,9 +931,10 @@ 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
+                }
        }
 #else  /* CONFIG_XEN */
 
diff -r 47c098fdce14 drivers/char/tty_io.c
--- a/drivers/char/tty_io.c     Wed Sep 20 15:35:23 2006 +0200
+++ b/drivers/char/tty_io.c     Sat Sep 30 10:34:01 2006 +0200
@@ -130,8 +130,6 @@ LIST_HEAD(tty_drivers);                     /* linked list
    vt.c for deeply disgusting hack reasons */
 DEFINE_MUTEX(tty_mutex);
 
-int console_use_vt = 1;
-
 #ifdef CONFIG_UNIX98_PTYS
 extern struct tty_driver *ptm_driver;  /* Unix98 pty masters; for /dev/ptmx */
 extern int pty_limit;          /* Config limit on Unix98 ptys */
@@ -2485,7 +2483,7 @@ retry_open:
                goto got_driver;
        }
 #ifdef CONFIG_VT
-       if (console_use_vt && (device == MKDEV(TTY_MAJOR,0))) {
+       if (device == MKDEV(TTY_MAJOR,0)) {
                extern struct tty_driver *console_driver;
                driver = console_driver;
                index = fg_console;
@@ -3911,8 +3909,6 @@ static int __init tty_init(void)
 #endif
 
 #ifdef CONFIG_VT
-       if (!console_use_vt)
-               goto out_vt;
        cdev_init(&vc0_cdev, &console_fops);
        if (cdev_add(&vc0_cdev, MKDEV(TTY_MAJOR, 0), 1) ||
            register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1, "/dev/vc/0") < 0)
diff -r 47c098fdce14 drivers/xen/Kconfig
--- a/drivers/xen/Kconfig       Wed Sep 20 15:35:23 2006 +0200
+++ b/drivers/xen/Kconfig       Sat Sep 30 10:34:01 2006 +0200
@@ -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
+       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
diff -r 47c098fdce14 drivers/xen/Makefile
--- a/drivers/xen/Makefile      Wed Sep 20 15:35:23 2006 +0200
+++ b/drivers/xen/Makefile      Sat Sep 30 10:34:01 2006 +0200
@@ -15,3 +15,5 @@ obj-$(CONFIG_XEN_NETDEV_FRONTEND)     += net
 obj-$(CONFIG_XEN_NETDEV_FRONTEND)      += netfront/
 obj-$(CONFIG_XEN_PCIDEV_BACKEND)       += pciback/
 obj-$(CONFIG_XEN_PCIDEV_FRONTEND)      += pcifront/
+obj-$(CONFIG_XEN_FRAMEBUFFER)          += xenfb/
+obj-$(CONFIG_XEN_KEYBOARD)             += xenkbd/
diff -r 47c098fdce14 drivers/xen/console/console.c
--- a/drivers/xen/console/console.c     Wed Sep 20 15:35:23 2006 +0200
+++ b/drivers/xen/console/console.c     Sat Sep 30 10:34:01 2006 +0200
@@ -58,19 +58,27 @@
 #include <asm/hypervisor.h>
 #include <xen/evtchn.h>
 #include <xen/xencons.h>
+#include <xen/xenbus.h>
 
 /*
  * Modes:
  *  'xencons=off'  [XC_OFF]:     Console is disabled.
  *  '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/xvc[0-9]+'.
  *                 [XC_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_mode = XC_DEFAULT;
+static enum { XC_OFF, XC_DEFAULT, XC_TTY, XC_SERIAL, XC_XVC } 
+    xc_mode = XC_DEFAULT;
 static int xc_num = -1;
+
+/* If we are in XC_XVC mode (a virtual console at /dev/xvcX), we need to
+ * comply with Lanana and use a minor under the low density serial major.
+ */
+#define XEN_XVC_MINOR 187
 
 #ifdef CONFIG_MAGIC_SYSRQ
 static unsigned long sysrq_requested;
@@ -86,6 +94,8 @@ static int __init xencons_setup(char *st
                xc_mode = XC_SERIAL;
        else if (!strncmp(str, "tty", 3))
                xc_mode = XC_TTY;
+       else if (!strncmp(str, "xvc", 3))
+               xc_mode = XC_XVC;
        else if (!strncmp(str, "off", 3))
                xc_mode = XC_OFF;
 
@@ -96,6 +106,11 @@ static int __init xencons_setup(char *st
                        xc_num = n;
                break;
        case XC_TTY:
+               n = simple_strtol(str+3, &q, 10);
+               if (q > (str + 3))
+                       xc_num = n;
+               break;
+       case XC_XVC:
                n = simple_strtol(str+3, &q, 10);
                if (q > (str + 3))
                        xc_num = n;
@@ -196,11 +211,20 @@ static int __init xen_console_init(void)
                if (!xen_start_info->console.domU.evtchn)
                        goto out;
                if (xc_mode == XC_DEFAULT)
-                       xc_mode = XC_TTY;
+#ifdef CONFIG_XEN_FRAMEBUFFER
+                       xc_mode = XC_XVC;
+#else
                kcons_info.write = kcons_write;
+#endif
        }
 
        switch (xc_mode) {
+       case XC_XVC:
+               strcpy(kcons_info.name, "xvc");
+               if (xc_num == -1)
+                       xc_num = 0;
+               break;
+
        case XC_SERIAL:
                strcpy(kcons_info.name, "ttyS");
                if (xc_num == -1)
@@ -305,7 +329,7 @@ void dom0_init_screen_info(const struct 
 /******************** User-space console driver (/dev/console) ************/
 
 #define DRV(_d)         (_d)
-#define DUMMY_TTY(_tty) ((xc_mode != XC_SERIAL) &&             \
+#define DUMMY_TTY(_tty) ((xc_mode != XC_SERIAL) && (xc_mode != XC_XVC) && \
                         ((_tty)->index != (xc_num - 1)))
 
 static struct termios *xencons_termios[MAX_NR_CONSOLES];
@@ -628,7 +652,8 @@ static int __init xencons_init(void)
                        return rc;
        }
 
-       xencons_driver = alloc_tty_driver((xc_mode == XC_SERIAL) ?
+       xencons_driver = alloc_tty_driver(((xc_mode == XC_SERIAL) || 
+                                          (xc_mode == XC_XVC)) ?
                                          1 : MAX_NR_CONSOLES);
        if (xencons_driver == NULL)
                return -ENOMEM;
@@ -648,6 +673,12 @@ static int __init xencons_init(void)
                DRV(xencons_driver)->name        = "ttyS";
                DRV(xencons_driver)->minor_start = 64 + xc_num;
                DRV(xencons_driver)->name_base   = 0 + xc_num;
+       } else if (xc_mode == XC_XVC) {
+               DRV(xencons_driver)->name        = "xvc";
+               DRV(xencons_driver)->major       = 250; /* FIXME: until lanana a
+pproves for 204:187 */
+               DRV(xencons_driver)->minor_start = XEN_XVC_MINOR;
+               DRV(xencons_driver)->name_base   = xc_num;
        } else {
                DRV(xencons_driver)->name        = "tty";
                DRV(xencons_driver)->minor_start = 1;
@@ -680,6 +711,20 @@ static int __init xencons_init(void)
        printk("Xen virtual console successfully installed as %s%d\n",
               DRV(xencons_driver)->name, xc_num);
 
+        /* Don't need to check about graphical fb for domain 0 */
+        if (is_initial_xendomain())
+               return 0;
+
+       rc = 0;
+       if (xenbus_scanf(XBT_NIL, "console", "use_graphics", "%d", &rc) < 0)
+               printk(KERN_ERR "Unable to read console/use_graphics\n");
+       if (rc == 0) {
+               /* FIXME: this is ugly */
+              unregister_console(&kcons_info);
+              kcons_info.flags |= CON_CONSDEV;
+              register_console(&kcons_info);
+       }
+
        return 0;
 }
 
diff -r 47c098fdce14 mm/memory.c
--- a/mm/memory.c       Wed Sep 20 15:35:23 2006 +0200
+++ b/mm/memory.c       Sat Sep 30 10:34:01 2006 +0200
@@ -891,6 +891,7 @@ unsigned long zap_page_range(struct vm_a
                tlb_finish_mmu(tlb, address, end);
        return end;
 }
+EXPORT_SYMBOL(zap_page_range);
 
 /*
  * Do a quick page-table lookup for a single page.
diff -r 47c098fdce14 drivers/xen/xenfb/Makefile
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/xen/xenfb/Makefile        Sat Sep 30 10:34:01 2006 +0200
@@ -0,0 +1,1 @@
+obj-$(CONFIG_XEN_FRAMEBUFFER)  := xenfb.o
diff -r 47c098fdce14 drivers/xen/xenfb/xenfb.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/xen/xenfb/xenfb.c Sat Sep 30 10:34:01 2006 +0200
@@ -0,0 +1,580 @@
+/*
+ * linux/drivers/video/xenfb.c -- Xen para-virtual frame buffer device
+ *
+ * Copyright (C) 2005-2006
+ *
+ *      Anthony Liguori <aliguori@xxxxxxxxxx>
+ *
+ *  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.
+ *
+ * Use xenbus_device API.
+ */
+
+#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/xenfb.h>
+#include <xen/xenbus.h>
+#include <linux/kthread.h>
+
+#define XENFB_WIDTH 800
+#define XENFB_HEIGHT 600
+#define XENFB_DEPTH 32
+
+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 */
+       struct semaphore        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;
+       u32                     flags; /* combination of XENFB_FLAG_* */
+};
+
+static int xenfb_fps = 20;
+static unsigned long xenfb_mem_len = XENFB_WIDTH * XENFB_HEIGHT * XENFB_DEPTH 
/ 8;
+
+static void __exit xenfb_cleanup(void);
+
+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)
+{
+       int y1, y2, x1, x2;
+       struct xenfb_mapping *map;
+
+       if (!(info->flags & XENFB_FLAG_UPDATE))
+               return;
+       if (xenfb_queue_full(info))
+               return;
+
+       down(&info->mm_lock);
+
+       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;
+       }
+
+       up(&info->mm_lock);
+
+       xenfb_do_update(info, x1, y1, x2 - x1, y2 - y1);
+}
+
+static int xenfb_thread(void *data)
+{
+       struct xenfb_info *info = data;
+
+       for (;;) {
+               if (kthread_should_stop())
+                       break;
+               if (info->dirty) {
+                       info->dirty = 0;
+                       xenfb_update_screen(info);
+               }
+               wait_event_interruptible(info->wq,
+                       kthread_should_stop() || info->dirty);
+       }
+       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)
+{
+       down(&info->mm_lock);
+       __xenfb_refresh(info, x1, y1, w, h);
+       up(&info->mm_lock);
+}
+
+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;
+
+       down(&info->mm_lock);
+       if (atomic_dec_and_test(&map->map_refs)) {
+               list_del(&map->link);
+               kfree(map);
+       }
+       up(&info->mm_lock);
+}
+
+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;
+       struct page *page;
+       int y1, y2;
+
+       if (pgnr >= info->nr_pages)
+               return NOPAGE_SIGBUS;
+
+       down(&info->mm_lock);
+       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);
+       up(&info->mm_lock);
+
+       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;
+       struct xenfb_mapping *map;
+       int ret;
+       int map_pages;
+
+       down(&info->mm_lock);
+
+       ret = -EINVAL;
+       if (!(vma->vm_flags & VM_WRITE))
+               goto out;
+       if (!(vma->vm_flags & VM_SHARED))
+               goto out;
+       if (vma->vm_pgoff != 0)
+               goto out;
+
+       map_pages = (vma->vm_end - vma->vm_start + PAGE_SIZE-1) >> PAGE_SHIFT;
+       if (map_pages > info->nr_pages)
+               goto out;
+
+       ret = -ENOMEM;
+       map = kzalloc(sizeof(*map), GFP_KERNEL);
+       if (map == NULL)
+               goto out;
+
+       map->vma = vma;
+       map->faults = 0;
+       map->info = info;
+       atomic_set(&map->map_refs, 1);
+       list_add(&map->link, &info->mappings);
+       vma->vm_ops = &xenfb_vm_ops;
+       vma->vm_flags |= (VM_DONTEXPAND | VM_RESERVED);
+       vma->vm_private_data = map;
+       ret = 0;
+
+ out:
+       up(&info->mm_lock);
+       return ret;
+}
+
+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)
+{
+       struct xenfb_info *info = dev_id;
+       __u32 cons, prod;
+
+       prod = info->page->in_prod;
+       rmb();                  /* ensure we see ring contents up to prod */
+       for (cons = info->page->in_cons; cons != prod; cons++) {
+               union xenfb_in_event *event;
+               event = &XENFB_IN_RING_REF(info->page, cons);
+
+               switch (event->type) {
+               case XENFB_TYPE_SET_EVENTS:
+                       info->flags = event->set_events.flags;
+                       break;
+               }
+       }
+       mb();                   /* ensure we're done with ring contents */
+       info->page->in_cons = cons;
+       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 struct xenfb_info *xenfb_info;
+
+static int __init xenfb_init(void)
+{
+       struct xenfb_info *info;
+       int i, ret;
+       struct fb_info *fb_info;
+       struct evtchn_alloc_unbound alloc_unbound;
+       struct evtchn_close close;
+       struct xenbus_transaction xbt;
+
+        /* Nothing to do if running in dom0. */
+        if (is_initial_xendomain())
+               return -ENODEV;
+        /* if we're not set up to use graphics mode, then don't initialize */
+       if (xenbus_scanf(XBT_NIL, "console", "use_graphics", "%d", &ret) < 0)
+               return -ENODEV;
+       if (ret == 0)
+               return -ENODEV;
+
+       info = kzalloc(sizeof(*info), GFP_KERNEL);
+       if (info == NULL)
+               return -ENOMEM;
+       xenfb_info = info;
+       info->irq = -1;
+       info->x1 = info->y1 = INT_MAX;
+       init_MUTEX(&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;
+       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;
+       for (i = 0; i < info->nr_pages; i++)
+               info->pages[i] = vmalloc_to_page(info->fb + i * PAGE_SIZE);
+
+       info->mfns = vmalloc(sizeof(unsigned long) * info->nr_pages);
+       if (!info->mfns)
+               goto error;
+       for (i = 0; i < info->nr_pages; i++)
+               info->mfns[i] = vmalloc_to_mfn(info->fb + i * PAGE_SIZE);
+
+       /* set up shared page */
+       info->page = (void *)__get_free_page(GFP_KERNEL);
+       if (!info->page)
+               goto error;
+
+       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;
+
+       /* set up event channel */
+       alloc_unbound.dom = DOMID_SELF;
+       alloc_unbound.remote_dom = 0;
+       ret = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound,
+                                         &alloc_unbound);
+       if (ret)
+               goto error;
+       info->evtchn = alloc_unbound.port;
+
+       ret = bind_evtchn_to_irqhandler(info->evtchn, xenfb_event_handler,
+                                       0, "xenfb", info);
+       if (ret < 0) {
+               close.port = info->evtchn;
+               HYPERVISOR_event_channel_op(EVTCHNOP_close, &close);
+               goto error;
+       }
+       info->irq = ret;
+
+       fb_info = framebuffer_alloc(sizeof(u32) * 256, NULL);
+                               /* see fishy hackery below */
+       if (fb_info == NULL)
+               goto error;
+
+       /* 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;
+
+       if (fb_alloc_cmap(&fb_info->cmap, 256, 0) < 0) {
+               framebuffer_release(fb_info);
+               goto error;
+       }
+
+       ret = register_framebuffer(fb_info);
+       if (ret) {
+               fb_dealloc_cmap(&info->fb_info->cmap);
+               framebuffer_release(fb_info);
+               goto error;
+       }
+       info->fb_info = fb_info;
+
+       info->kthread = kthread_run(xenfb_thread, info, "xenfb thread");
+       if (IS_ERR(info->kthread))
+               goto error;
+
+ again:
+       ret = xenbus_transaction_start(&xbt);
+       if (ret)
+               goto error;
+       ret = xenbus_printf(xbt, "vfb/0", "page-ref", "%lu",
+                           virt_to_mfn(info->page));
+       if (ret)
+               goto error_xenbus;
+       ret = xenbus_printf(xbt, "vfb/0", "event-channel", "%u",
+                           info->evtchn);
+       if (ret)
+               goto error_xenbus;
+       ret = xenbus_transaction_end(xbt, 0);
+       if (ret) {
+               if (ret == -EAGAIN)
+                       goto again;
+               goto error;
+       }
+
+       printk(KERN_DEBUG "xenfb initialized\n");
+       return 0;
+
+ error_xenbus:
+       xenbus_transaction_end(xbt, 1);
+ error:
+       xenfb_cleanup();
+
+       return -ENODEV;
+}
+
+static void __exit xenfb_cleanup(void)
+{
+       struct xenfb_info *info = xenfb_info;
+
+       if (!info)
+               return;
+       printk(KERN_DEBUG "xenfb cleaning up\n");
+       del_timer(&info->refresh);
+       if (info->kthread)
+               kthread_stop(info->kthread);
+       if (info->irq >= 0)
+               unbind_from_irqhandler(info->irq, 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);
+       xenfb_info = NULL;
+       printk(KERN_DEBUG "xenfb cleaned up\n");
+}
+
+module_init(xenfb_init);
+module_exit(xenfb_cleanup);
+
+MODULE_LICENSE("GPL");
diff -r 47c098fdce14 drivers/xen/xenkbd/Makefile
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/xen/xenkbd/Makefile       Sat Sep 30 10:34:01 2006 +0200
@@ -0,0 +1,1 @@
+obj-$(CONFIG_XEN_KEYBOARD)     += xenkbd.o
diff -r 47c098fdce14 drivers/xen/xenkbd/xenkbd.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/xen/xenkbd/xenkbd.c       Sat Sep 30 10:34:01 2006 +0200
@@ -0,0 +1,198 @@
+/*
+ * linux/drivers/input/keyboard/xenkbd.c -- Xen para-virtual input device
+ *
+ * Copyright (C) 2005
+ *
+ *      Anthony Liguori <aliguori@xxxxxxxxxx>
+ *
+ *  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.
+ *
+ * Use xenbus_device API.
+ */
+
+#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/xenkbd.h>
+#include <xen/xenbus.h>
+
+struct xenkbd_device
+{
+       struct input_dev *dev;
+       struct xenkbd_info *info;
+       unsigned evtchn;
+       int irq;
+};
+
+static void __exit xenkbd_cleanup(void);
+
+static irqreturn_t input_handler(int rq, void *dev_id, struct pt_regs *regs)
+{
+       struct xenkbd_device *dev = dev_id;
+       struct xenkbd_info *info = dev ? dev->info : 0;
+       static int button_map[3] = { BTN_RIGHT, BTN_MIDDLE, BTN_LEFT };
+       __u32 cons, prod;
+
+       prod = info->in_prod;
+       rmb();                  /* ensure we see ring contents up to prod */
+       for (cons = info->in_cons; cons != prod; cons++) {
+               union xenkbd_in_event *event;
+               event = &XENKBD_IN_RING_REF(info, cons);
+       
+               switch (event->type) {
+               case XENKBD_TYPE_MOTION:
+                       input_report_rel(dev->dev, REL_X, event->motion.rel_x);
+                       input_report_rel(dev->dev, REL_Y, event->motion.rel_y);
+                       break;
+               case XENKBD_TYPE_BUTTON:
+                       if (event->button.button < 3)
+                               input_report_key(dev->dev,
+                                                
button_map[event->button.button],
+                                                event->button.pressed);
+                       break;
+               case XENKBD_TYPE_KEY:
+                       input_report_key(dev->dev, event->key.keycode, 
event->key.pressed);
+                       break;
+               }
+       }
+       input_sync(dev->dev);
+       mb();                   /* ensure we got ring contents */
+       info->in_cons = cons;
+       notify_remote_via_evtchn(dev->evtchn);
+
+       return IRQ_HANDLED;
+}
+
+static struct xenkbd_device *xenkbd_dev;
+
+int __init xenkbd_init(void)
+{
+       int ret = 0;
+       int i;
+       struct xenkbd_device *dev;
+       struct input_dev *input_dev;
+       struct evtchn_alloc_unbound alloc_unbound;
+       struct evtchn_close close;
+       struct xenbus_transaction xbt;
+
+        /* Nothing to do if running in dom0. */
+        if (is_initial_xendomain())
+               return -ENODEV;
+        /* if we're not set up to use graphics mode, then don't initialize */
+       if (xenbus_scanf(XBT_NIL, "console", "use_graphics", "%d", &ret) < 0)
+               return -ENODEV;
+       if (ret == 0)
+               return -ENODEV;
+
+       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+       if (!dev)
+               return -ENODEV;
+       xenkbd_dev = dev;
+
+       dev->info = (void *)__get_free_page(GFP_KERNEL);
+       if (!dev->info) {
+               ret = -ENOMEM;
+               goto error;
+       }
+       dev->info->in_cons = dev->info->in_prod = 0;
+       dev->info->out_cons = dev->info->out_prod = 0;
+
+       input_dev = input_allocate_device();
+       if (!input_dev) {
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
+       input_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT);
+       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";
+
+       ret = input_register_device(input_dev);
+       if (ret) {
+               input_free_device(input_dev);
+               goto error;
+       }
+       dev->dev = input_dev;
+
+       alloc_unbound.dom = DOMID_SELF;
+       alloc_unbound.remote_dom = 0;
+       ret = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound,
+                                         &alloc_unbound);
+       if (ret)
+               goto error;
+       dev->evtchn = alloc_unbound.port;
+       ret = bind_evtchn_to_irqhandler(dev->evtchn, input_handler, 0,
+                                       "xenkbd", dev);
+       if (ret < 0) {
+               close.port = dev->evtchn;
+               HYPERVISOR_event_channel_op(EVTCHNOP_close, &close);
+               goto error;
+       }
+       dev->irq = ret;
+
+ again:
+       ret = xenbus_transaction_start(&xbt);
+       if (ret)
+               goto error;
+       ret = xenbus_printf(xbt, "vkbd/0", "page-ref", "%lu",
+                           virt_to_mfn(dev->info));
+       if (ret)
+               goto error_xenbus;
+       ret = xenbus_printf(xbt, "vkbd/0", "event-channel", "%u",
+                           dev->evtchn);
+       if (ret)
+               goto error_xenbus;
+       ret = xenbus_transaction_end(xbt, 0);
+       if (ret) {
+               if (ret == -EAGAIN)
+                       goto again;
+               goto error;
+       }
+
+       printk(KERN_DEBUG "xenkbd initialized\n");
+       return ret;
+
+ error_xenbus:
+       xenbus_transaction_end(xbt, 1);
+ error:
+       xenkbd_cleanup();
+       return ret;
+}
+
+static void __exit xenkbd_cleanup(void)
+{
+       if (!xenkbd_dev)
+               return;
+       printk(KERN_DEBUG "xenkbd cleaning up\n");
+       if (xenkbd_dev->irq >= 0)
+               unbind_from_irqhandler(xenkbd_dev->irq, xenkbd_dev);
+       input_unregister_device(xenkbd_dev->dev);
+       free_page((unsigned long)xenkbd_dev->info);
+       kfree(xenkbd_dev);
+       xenkbd_dev = NULL;
+       printk(KERN_DEBUG "xenkbd cleaned up\n");
+}
+
+module_init(xenkbd_init);
+module_exit(xenkbd_cleanup);
+
+MODULE_LICENSE("GPL");
diff -r 47c098fdce14 include/xen/interface/io/xenfb.h
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/include/xen/interface/io/xenfb.h  Sat Sep 30 10:34:01 2006 +0200
@@ -0,0 +1,103 @@
+/*
+ * linux/include/linux/xenfb.h -- Xen virtual frame buffer device
+ *
+ * Copyright (C) 2005
+ *
+ *      Anthony Liguori <aliguori@xxxxxxxxxx>
+ *
+ *  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 _LINUX_XENFB_H
+#define _LINUX_XENFB_H
+
+#include <asm/types.h>
+
+/* out events */
+
+#define XENFB_OUT_EVENT_SIZE 40
+
+/* value 1 currently not used */
+#define XENFB_TYPE_UPDATE 2
+
+struct xenfb_update
+{
+       __u8 type;          /* XENFB_TYPE_UPDATE */
+       __u16 x;            /* source x */
+       __u16 y;            /* source y */
+       __u16 width;        /* rect width */
+       __u16 height;       /* rect height */
+};
+
+union xenfb_out_event
+{
+       __u8 type;
+       struct xenfb_update update;
+       char pad[XENFB_OUT_EVENT_SIZE];
+};
+
+/* in events */
+
+#define XENFB_IN_EVENT_SIZE 40
+
+#define XENFB_TYPE_SET_EVENTS 1
+
+#define XENFB_FLAG_UPDATE 2
+
+struct xenfb_set_events
+{
+       __u8 type;          /* XENFB_TYPE_SET_EVENTS */
+       __u32 flags;        /* combination of XENFB_FLAG_* */
+};
+
+union xenfb_in_event
+{
+       __u8 type;
+       struct xenfb_set_events set_events;
+       char pad[XENFB_OUT_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;
+
+       __u16 width;         /* the width of the framebuffer (in pixels) */
+       __u16 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];
+};
+
+#endif
diff -r 47c098fdce14 include/xen/interface/io/xenkbd.h
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/include/xen/interface/io/xenkbd.h Sat Sep 30 10:34:01 2006 +0200
@@ -0,0 +1,92 @@
+/*
+ * linux/include/linux/xenkbd.h -- Xen virtual keyboard/mouse
+ *
+ * Copyright (C) 2005
+ *
+ *      Anthony Liguori <aliguori@xxxxxxxxxx>
+ *
+ *  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 _LINUX_XENKBD_H
+#define _LINUX_XENKBD_H
+
+#include <asm/types.h>
+
+/* in events */
+
+#define XENKBD_IN_EVENT_SIZE 40
+
+#define XENKBD_TYPE_MOTION  1     /* mouse movement event */
+#define XENKBD_TYPE_BUTTON  2     /* mouse button event */
+#define XENKBD_TYPE_KEY     3     /* keyboard event */
+
+struct xenkbd_motion
+{
+       __u8 type;         /* XENKBD_TYPE_MOTION */
+       __s16 rel_x;       /* relative X motion */
+       __s16 rel_y;       /* relative Y motion */
+};
+
+struct xenkbd_button
+{
+       __u8 type;         /* XENKBD_TYPE_BUTTON */
+       __u8 pressed;      /* 1 if pressed; 0 otherwise */
+       __u8 button;       /* the button (0, 1, 2 is right, middle, left) */
+};
+
+struct xenkbd_key
+{
+       __u8 type;         /* XENKBD_TYPE_KEY */
+       __u8 pressed;      /* 1 if pressed; 0 otherwise */
+       __u16 keycode;     /* KEY_* from linux/input.h */
+};
+
+union xenkbd_in_event
+{
+       __u8 type;
+       struct xenkbd_motion motion;
+       struct xenkbd_button button;
+       struct xenkbd_key key;
+       char pad[XENKBD_IN_EVENT_SIZE];
+};
+
+/* out events */
+
+#define XENKBD_OUT_EVENT_SIZE 40
+
+/* no out events currently defined */
+
+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_info
+{
+       __u32 in_cons, in_prod;
+       __u32 out_cons, out_prod;
+};
+
+#endif

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel