diff --git a/tools/ioemu/Makefile.target b/tools/ioemu/Makefile.target index e220aee9e1..e500233eea 100644 --- a/tools/ioemu/Makefile.target +++ b/tools/ioemu/Makefile.target @@ -410,6 +410,8 @@ VL_OBJS+= piix4acpi.o VL_OBJS+= xenstore.o VL_OBJS+= xen_platform.o VL_OBJS+= xen_machine_fv.o +VL_OBJS+= xen_machine_pv.o +VL_OBJS+= ../../xenfb/xenfb.o VL_OBJS+= tpm_tis.o CPPFLAGS += -DHAS_AUDIO endif diff --git a/tools/ioemu/hw/xen_machine_pv.c b/tools/ioemu/hw/xen_machine_pv.c new file mode 100644 index 0000000000..6a2a2ee26d --- /dev/null +++ b/tools/ioemu/hw/xen_machine_pv.c @@ -0,0 +1,315 @@ +/* + * QEMU Xen PV Machine + * + * Copyright (c) 2007 Red Hat + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "vl.h" +#include "../../xenfb/xenfb.h" +#include + +/* + * Tables to map from scancode to Linux input layer keycode. + * Scancodes are hardware-specific. These maps assumes a + * standard AT or PS/2 keyboard which is what QEMU feeds us. + */ +static const unsigned char atkbd_set2_keycode[512] = { + + 0, 67, 65, 63, 61, 59, 60, 88, 0, 68, 66, 64, 62, 15, 41,117, + 0, 56, 42, 93, 29, 16, 2, 0, 0, 0, 44, 31, 30, 17, 3, 0, + 0, 46, 45, 32, 18, 5, 4, 95, 0, 57, 47, 33, 20, 19, 6,183, + 0, 49, 48, 35, 34, 21, 7,184, 0, 0, 50, 36, 22, 8, 9,185, + 0, 51, 37, 23, 24, 11, 10, 0, 0, 52, 53, 38, 39, 25, 12, 0, + 0, 89, 40, 0, 26, 13, 0, 0, 58, 54, 28, 27, 0, 43, 0, 85, + 0, 86, 91, 90, 92, 0, 14, 94, 0, 79,124, 75, 71,121, 0, 0, + 82, 83, 80, 76, 77, 72, 1, 69, 87, 78, 81, 74, 55, 73, 70, 99, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 217,100,255, 0, 97,165, 0, 0,156, 0, 0, 0, 0, 0, 0,125, + 173,114, 0,113, 0, 0, 0,126,128, 0, 0,140, 0, 0, 0,127, + 159, 0,115, 0,164, 0, 0,116,158, 0,150,166, 0, 0, 0,142, + 157, 0, 0, 0, 0, 0, 0, 0,155, 0, 98, 0, 0,163, 0, 0, + 226, 0, 0, 0, 0, 0, 0, 0, 0,255, 96, 0, 0, 0,143, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,107, 0,105,102, 0, 0,112, + 110,111,108,112,106,103, 0,119, 0,118,109, 0, 99,104,119, 0, + +}; + +static const unsigned char atkbd_unxlate_table[128] = { + + 0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13, + 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27, + 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42, + 50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88, 5, 6, 4, 12, 3, + 11, 2, 10, 1, 9,119,126,108,117,125,123,107,115,116,121,105, + 114,122,112,113,127, 96, 97,120, 7, 15, 23, 31, 39, 47, 55, 63, + 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111, + 19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110 + +}; + +static unsigned char scancode2linux[512]; + +/* A convenient function for munging pixels between different depths */ +#define BLT(SRC_T,DST_T,RLS,GLS,BLS,RRS,GRS,BRS,RM,GM,BM) \ + for (line = y ; line < h ; line++) { \ + SRC_T *src = (SRC_T *)(xenfb->pixels \ + + (line * xenfb->row_stride) \ + + (x * xenfb->depth / 8)); \ + DST_T *dst = (DST_T *)(ds->data \ + + (line * ds->linesize) \ + + (x * ds->depth / 8)); \ + int col; \ + for (col = x ; col < w ; col++) { \ + *dst = (((*src >> RRS) & RM) << RLS) | \ + (((*src >> GRS) & GM) << GLS) | \ + (((*src >> GRS) & BM) << BLS); \ + src++; \ + dst++; \ + } \ + } + +/* This copies data from the guest framebuffer region, into QEMU's copy + * NB. QEMU's copy is stored in the pixel format of a) the local X + * server (SDL case) or b) the current VNC client pixel format. + */ +static void xen_pvfb_guest_copy(struct xenfb *xenfb, int x, int y, int w, int h) +{ + DisplayState *ds = (DisplayState *)xenfb->user_data; + int line; + + if (xenfb->depth == ds->depth) { /* Perfect match can use fast path */ + for (line = y ; line < (y+h) ; line++) { + memcpy(ds->data + (line * ds->linesize) + (x * ds->depth / 8), + xenfb->pixels + (line * xenfb->row_stride) + (x * xenfb->depth / 8), + w * xenfb->depth / 8); + } + } else { /* Mismatch requires slow pixel munging */ + if (xenfb->depth == 8) { + /* 8 bit source == r:3 g:3 b:2 */ + if (ds->depth == 16) { + BLT(uint8_t, uint16_t, 5, 2, 0, 11, 5, 0, 7, 7, 3); + } else if (ds->depth == 32) { + BLT(uint8_t, uint32_t, 5, 2, 0, 16, 8, 0, 7, 7, 3); + } + } else if (xenfb->depth == 16) { + /* 16 bit source == r:5 g:6 b:5 */ + if (ds->depth == 8) { + BLT(uint16_t, uint8_t, 11, 5, 0, 5, 2, 0, 31, 63, 31); + } else if (ds->depth == 32) { + BLT(uint16_t, uint32_t, 11, 5, 0, 16, 8, 0, 31, 63, 31); + } + } else if (xenfb->depth == 32) { + /* 32 bit source == r:8 g:8 b:8 (padding:8) */ + if (ds->depth == 8) { + BLT(uint32_t, uint8_t, 16, 8, 0, 5, 2, 0, 255, 255, 255); + } else if (ds->depth == 16) { + BLT(uint32_t, uint16_t, 16, 8, 0, 11, 5, 0, 255, 255, 255); + } + } + } + dpy_update(ds, x, y, w, h); +} + +/* + * Send a key event from the client to the guest OS + * QEMU gives us a raw scancode from an AT / PS/2 style keyboard. + * We have to turn this into a Linux Input layer keycode. + * + * Extra complexity from the fact that with extended scancodes + * (like those produced by arrow keys) this method gets called + * twice, but we only want to send a single event. So we have to + * track the '0xe0' scancode state & collapse the extended keys + * as needed. + * + * Wish we could just send scancodes straight to the guest which + * already has code for dealing with this... + */ +static void xen_pvfb_key_event(void *opaque, int scancode) +{ + static int extended = 0; + int down = 1; + if (scancode == 0xe0) { + extended = 1; + return; + } else if (scancode & 0x80) { + scancode &= 0x7f; + down = 0; + } + if (extended) { + scancode |= 0x80; + extended = 0; + } + xenfb_send_key(opaque, down, scancode2linux[scancode]); +} + +/* + * Send a mouse event from the client to the guest OS + * + * The QEMU mouse can be in either relative, or absolute mode. + * Movement is sent separately from button state, which has to + * be encoded as virtual key events. We also don't actually get + * given any button up/down events, so have to track changes in + * the button state. + */ +static void xen_pvfb_mouse_event(void *opaque, + int dx, int dy, int dz, int button_state) +{ + static int old_state = 0; + int i; + struct xenfb *xenfb = opaque; + DisplayState *ds = (DisplayState *)xenfb->user_data; + if (xenfb->abs_pointer_wanted) + xenfb_send_position(xenfb, + dx * ds->width / 0x7fff, + dy * ds->height / 0x7fff); + else + xenfb_send_motion(xenfb, dx, dy); + + for (i = 0 ; i < 8 ; i++) { + int lastDown = old_state & (1 << i); + int down = button_state & (1 << i); + if (down == lastDown) + continue; + + if (xenfb_send_key(xenfb, down, BTN_LEFT+i) < 0) + return; + } + old_state = button_state; +} + +/* QEMU display state changed, so refresh the framebuffer copy */ +/* XXX - can we optimize this, or the next func at all ? */ +void xen_pvfb_update(void *opaque) +{ + struct xenfb *xenfb = opaque; + xen_pvfb_guest_copy(xenfb, 0, 0, xenfb->width, xenfb->height); +} + +/* QEMU display state changed, so refresh the framebuffer copy */ +void xen_pvfb_invalidate(void *opaque) +{ + xen_pvfb_update(opaque); +} + +/* Screen dump is not used in Xen, so no need to impl this ? */ +void xen_pvfb_screen_dump(void *opaque, const char *name) { } + +void xen_pvfb_dispatch_store(void *opaque) { + int ret; + if ((ret = xenfb_dispatch_store(opaque)) < 0) { + fprintf(stderr, "Failure while dispatching store: %d\n", ret); + exit(1); + } +} + +void xen_pvfb_dispatch_channel(void *opaque) { + int ret; + if ((ret = xenfb_dispatch_channel(opaque)) < 0) { + fprintf(stderr, "Failure while dispatching store: %d\n", ret); + exit(1); + } +} + +/* The Xen PV machine currently provides + * - a virtual framebuffer + * - .... + */ +static void xen_init_pv(uint64_t ram_size, int vga_ram_size, char *boot_device, + DisplayState *ds, const char **fd_filename, + int snapshot, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename) +{ + struct xenfb *xenfb; + extern int domid; + int fd, i; + + /* Prepare scancode mapping table */ + for (i = 0; i < 128; i++) { + scancode2linux[i] = atkbd_set2_keycode[atkbd_unxlate_table[i]]; + scancode2linux[i | 0x80] = + atkbd_set2_keycode[atkbd_unxlate_table[i] | 0x80]; + } + + /* Prepare PVFB state */ + xenfb = xenfb_new(); + if (xenfb == NULL) { + fprintf(stderr, "Could not create framebuffer (%s)\n", + strerror(errno)); + exit(1); + } + + /* Talk to the guest */ + if (xenfb_attach_dom(xenfb, domid) < 0) { + fprintf(stderr, "Could not connect to domain (%s)\n", + strerror(errno)); + exit(1); + } + xenfb->update = xen_pvfb_guest_copy; + xenfb->user_data = ds; + + /* Tell QEMU to allocate a graphical console */ + graphic_console_init(ds, + xen_pvfb_update, + xen_pvfb_invalidate, + xen_pvfb_screen_dump, + xenfb); + + /* Register our keyboard & mouse handlers */ + qemu_add_kbd_event_handler(xen_pvfb_key_event, xenfb); + qemu_add_mouse_event_handler(xen_pvfb_mouse_event, xenfb, + xenfb->abs_pointer_wanted, + "Xen PVFB Mouse"); + + /* Listen for events from xenstore */ + fd = xenfb_get_store_fd(xenfb); + if (qemu_set_fd_handler2(fd, NULL, xen_pvfb_dispatch_store, NULL, xenfb) < 0) { + fprintf(stderr, "Could not register event handler (%s)\n", + strerror(errno)); + } + + /* Listen for events from the event channel */ + fd = xenfb_get_channel_fd(xenfb); + if (qemu_set_fd_handler2(fd, NULL, xen_pvfb_dispatch_channel, NULL, xenfb) < 0) { + fprintf(stderr, "Could not register event handler (%s)\n", + strerror(errno)); + } + + /* Setup QEMU display */ + dpy_resize(ds, xenfb->width, xenfb->height); +} + +QEMUMachine xenpv_machine = { + "xenpv", + "Xen Para-virtualized PC", + xen_init_pv, +}; + +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff --git a/tools/ioemu/target-i386-dm/helper2.c b/tools/ioemu/target-i386-dm/helper2.c index cc37867de6..013efb8e26 100644 --- a/tools/ioemu/target-i386-dm/helper2.c +++ b/tools/ioemu/target-i386-dm/helper2.c @@ -616,7 +616,7 @@ int main_loop(void) extern int shutdown_requested; extern int suspend_requested; CPUState *env = cpu_single_env; - int evtchn_fd = xc_evtchn_fd(xce_handle); + int evtchn_fd = xce_handle == -1 ? -1 : xc_evtchn_fd(xce_handle); char qemu_file[PATH_MAX]; fd_set fds; @@ -624,7 +624,8 @@ int main_loop(void) cpu_single_env); qemu_mod_timer(buffered_io_timer, qemu_get_clock(rt_clock)); - qemu_set_fd_handler(evtchn_fd, cpu_handle_ioreq, NULL, env); + if (evtchn_fd != -1) + qemu_set_fd_handler(evtchn_fd, cpu_handle_ioreq, NULL, env); xenstore_record_dm_state("running"); while (1) { diff --git a/tools/ioemu/vl.c b/tools/ioemu/vl.c index 34a52c5ad0..1187f0cd39 100644 --- a/tools/ioemu/vl.c +++ b/tools/ioemu/vl.c @@ -193,7 +193,7 @@ extern int vcpus; int xc_handle; -char domain_name[64] = "Xen-HVM-no-name"; +char domain_name[64] = "Xen-no-name"; extern int domid; char vncpasswd[64]; @@ -6700,6 +6700,7 @@ void register_machines(void) qemu_register_machine(&isapc_machine); #else qemu_register_machine(&xenfv_machine); + qemu_register_machine(&xenpv_machine); #endif #elif defined(TARGET_PPC) qemu_register_machine(&heathrow_machine); @@ -7446,7 +7447,7 @@ int main(int argc, char **argv) break; case QEMU_OPTION_domainname: snprintf(domain_name, sizeof(domain_name), - "Xen-HVM-%s", optarg); + "Xen-%s", optarg); break; case QEMU_OPTION_d: domid = atoi(optarg); diff --git a/tools/ioemu/vl.h b/tools/ioemu/vl.h index 54e4c1a6fa..620ea36d1e 100644 --- a/tools/ioemu/vl.h +++ b/tools/ioemu/vl.h @@ -1110,6 +1110,7 @@ extern QEMUMachine pc_machine; extern QEMUMachine isapc_machine; #ifdef CONFIG_DM extern QEMUMachine xenfv_machine; +extern QEMUMachine xenpv_machine; #endif extern int fd_bootchk; diff --git a/tools/python/xen/xend/server/vfbif.py b/tools/python/xen/xend/server/vfbif.py index eb1990e64f..58f5f9d5ac 100644 --- a/tools/python/xen/xend/server/vfbif.py +++ b/tools/python/xen/xend/server/vfbif.py @@ -50,8 +50,10 @@ class VfbifController(DevController): # is HVM, so qemu-dm will handle the vfb. return - std_args = [ "--domid", "%d" % self.vm.getDomid(), - "--title", self.vm.getName() ] + args = [ xen.util.auxbin.pathTo("qemu-dm"), + "-M", "xenpv", + "-d", "%d" % self.vm.getDomid(), + "-domain-name", self.vm.getName() ] t = config.get("type", None) if t == "vnc": passwd = None @@ -65,15 +67,14 @@ class VfbifController(DevController): else: log.debug("No VNC passwd configured for vfb access") - # 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 + int(config["vncdisplay"]))] - vnclisten = config.get("vnclisten", + vnclisten = config.get('vnclisten', xen.xend.XendOptions.instance().get_vnclisten_address()) - args += [ "--listen", vnclisten ] + vncdisplay = config.get('vncdisplay', 0) + args += ['-vnc', "%s:%d" % (vnclisten, vncdisplay)] + + if config.get('vncunused', 0): + args += ['-vncunused'] + if config.has_key("keymap"): args += ["-k", "%s" % config["keymap"]] else: @@ -81,15 +82,14 @@ class VfbifController(DevController): if xoptions.get_keymap(): args += ["-k", "%s" % xoptions.get_keymap()] - spawn_detached(args[0], args + std_args, os.environ) + spawn_detached(args[0], 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) + spawn_detached(args[0], args, env) else: raise VmError('Unknown vfb type %s (%s)' % (t, repr(config))) diff --git a/tools/xenfb/xenfb.c b/tools/xenfb/xenfb.c index e94e01eee4..62ee658ff2 100644 --- a/tools/xenfb/xenfb.c +++ b/tools/xenfb/xenfb.c @@ -670,37 +670,58 @@ static int xenfb_on_state_change(struct xenfb_device *dev) 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) +int xenfb_dispatch_channel(struct xenfb *xenfb_pub) { struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; evtchn_port_t port; + 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; + + return 0; +} + +int xenfb_dispatch_store(struct xenfb *xenfb_pub) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; unsigned dummy; char **vec; int r; + 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; +} + + +/* 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; + int ret; + 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 ((ret = xenfb_dispatch_channel(xenfb_pub)) < 0) + return ret; } 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; + if ((ret = xenfb_dispatch_store(xenfb_pub)) < 0) + return ret; } return 0; @@ -717,6 +738,18 @@ int xenfb_select_fds(struct xenfb *xenfb_pub, fd_set *readfds) return fd1 > fd2 ? fd1 + 1 : fd2 + 1; } +int xenfb_get_store_fd(struct xenfb *xenfb_pub) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + return xs_fileno(xenfb->xsh); +} + +int xenfb_get_channel_fd(struct xenfb *xenfb_pub) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + return xc_evtchn_fd(xenfb->evt_xch); +} + static int xenfb_kbd_event(struct xenfb_private *xenfb, union xenkbd_in_event *event) { @@ -777,3 +810,10 @@ int xenfb_send_position(struct xenfb *xenfb_pub, int abs_x, int abs_y) return xenfb_kbd_event(xenfb, &event); } +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ diff --git a/tools/xenfb/xenfb.h b/tools/xenfb/xenfb.h index c08d979b39..86d5ec48df 100644 --- a/tools/xenfb/xenfb.h +++ b/tools/xenfb/xenfb.h @@ -25,8 +25,12 @@ void xenfb_teardown(struct xenfb *xenfb); int xenfb_attach_dom(struct xenfb *xenfb, int domid); +int xenfb_dispatch_store(struct xenfb *xenfb_pub); +int xenfb_dispatch_channel(struct xenfb *xenfb_pub); int xenfb_select_fds(struct xenfb *xenfb, fd_set *readfds); int xenfb_poll(struct xenfb *xenfb, fd_set *readfds); +int xenfb_get_store_fd(struct xenfb *xenfb_pub); +int xenfb_get_channel_fd(struct xenfb *xenfb_pub); int xenfb_send_key(struct xenfb *xenfb, bool down, int keycode); int xenfb_send_motion(struct xenfb *xenfb, int rel_x, int rel_y);