|
View:
New views
20 Messages
—
Rating Filter:
Alert me
|
| < Prev | 1 - 2 - 3 - 4 - 5 - 6 | Next > |
|
|
[PATCH 00/13] merge some xen bits into qemu Hi folks,
This is the fourth version of the xen support for qemu patch series, addressing review comments. Also fixed some bugs and made the backend drivers restartable (for boots via pvgrub). New in this submission is the domain building support. The console and framebuffer backend drivers are largely based on the xen code, the other bits are rewritten from scratch. Nevertheless the code should be functionally identical. Overview (individual patches have longer descriptions): #1 -- Handle terminating signals. #2 -- add DisplayState->idle #3 -- add container_of() macro to osdep.h #4 -- move GUI_REFRESH_INTERVAL define from vl.c to console.h #5 -- groundwork: build system, cmd line options, ... #6 -- xen backend driver infrastructrure #7 -- xen console backend driver #8 -- xen framebuffer backend driver #9 -- xen block backend driver #10 -- xen nic backend driver #11 -- allow xen disks and nics being configured via qemu command line options. #12 -- set vnc password from xenstore. #13 -- xen: pv domain builder. The first four patches are preparing patches. They put some stuff in place the other xen patches depend on. With the next four patches in place upstream qemu can replace xen's qemu-dm for paravirtual domains. The last five patches add full userspace implementations of block and nic backend drivers using the grant table device (gntdev), command line support config support for setting up these backends and domain building support. xen support is implemented using another machine type. xen's qemu-dm already uses the machine type to switch between paravirtualized and fully virtualized machines, so this was the natural choice. qemu has gets a new "xenpv" machine type additionally to the "pc" and "isapc" ones. The patches are also available here: http://kraxel.fedorapeople.org/patches/qemu-upstream/ There is also a patch queue for qemu-dm, so the patches can get a test-drive there: http://kraxel.fedorapeople.org/patches/qemu-xen/ There also a '-b' (no whitespace changes) version for easier review: http://kraxel.fedorapeople.org/patches/qemu-xen/no-ws/ The qemu-dm patch series has a slightly different ordering to make the patch queue more bisect-friendly. Also some preparing patches are not needed because they already there. Some extra patches (backports) are there due to the age of the qemu-dm tree. The last two patches are not present (yet). Comments? cheers, Gerd _______________________________________________ Xen-devel mailing list Xen-devel@... http://lists.xensource.com/xen-devel |
|
|
[PATCH 01/13] Handle terminating signals.This patch makes qemu handle signals better. It sets the request_shutdown
flag, making the main_loop exit and qemu taking the usual exit route, with atexit handlers being called and so on, instead of qemu just being killed by the signal. To avoid calling vm_start() from the signal handler main_loop() got an additional check so qemu_system_shutdown_request() works even when the vm is in stopped state. Signed-off-by: Gerd Hoffmann <kraxel@...> --- curses.c | 2 -- sdl.c | 9 +-------- vl.c | 27 +++++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/curses.c b/curses.c index d09eff2..96b6e49 100644 --- a/curses.c +++ b/curses.c @@ -350,8 +350,6 @@ void curses_display_init(DisplayState *ds, int full_screen) atexit(curses_atexit); #ifndef _WIN32 - signal(SIGINT, SIG_DFL); - signal(SIGQUIT, SIG_DFL); #if defined(SIGWINCH) && defined(KEY_RESIZE) /* some curses implementations provide a handler, but we * want to be sure this is handled regardless of the library */ diff --git a/sdl.c b/sdl.c index 0edc4a0..84a9d6d 100644 --- a/sdl.c +++ b/sdl.c @@ -476,10 +476,8 @@ static void sdl_refresh(DisplayState *ds) sdl_process_key(&ev->key); break; case SDL_QUIT: - if (!no_quit) { + if (!no_quit) qemu_system_shutdown_request(); - vm_start(); /* In case we're paused */ - } break; case SDL_MOUSEMOTION: if (gui_grab || kbd_mouse_is_absolute() || @@ -636,11 +634,6 @@ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) fprintf(stderr, "Could not initialize SDL - exiting\n"); exit(1); } -#ifndef _WIN32 - /* NOTE: we still want Ctrl-C to work, so we undo the SDL redirections */ - signal(SIGINT, SIG_DFL); - signal(SIGQUIT, SIG_DFL); -#endif ds->dpy_update = sdl_update; ds->dpy_resize = sdl_resize; diff --git a/vl.c b/vl.c index fc5019f..071ee0c 100644 --- a/vl.c +++ b/vl.c @@ -7619,6 +7619,8 @@ static int main_loop(void) timeout = 0; } } else { + if (shutdown_requested) + break; timeout = 10; } #ifdef CONFIG_PROFILER @@ -8183,6 +8185,26 @@ static BOOL WINAPI qemu_ctrl_handler(DWORD type) #define MAX_NET_CLIENTS 32 +#ifndef _WIN32 + +static void termsig_handler(int signal) +{ + qemu_system_shutdown_request(); +} + +void termsig_setup(void) +{ + struct sigaction act; + + memset(&act, 0, sizeof(act)); + act.sa_handler = termsig_handler; + sigaction(SIGINT, &act, NULL); + sigaction(SIGHUP, &act, NULL); + sigaction(SIGTERM, &act, NULL); +} + +#endif + int main(int argc, char **argv) { #ifdef CONFIG_GDBSTUB @@ -9071,6 +9093,11 @@ int main(int argc, char **argv) #endif } +#ifndef _WIN32 + /* must be after terminal init, SDL library changes signal handlers */ + termsig_setup(); +#endif + /* Maintain compatibility with multiple stdio monitors */ if (!strcmp(monitor_device,"stdio")) { for (i = 0; i < MAX_SERIAL_PORTS; i++) { -- 1.5.5.1 _______________________________________________ Xen-devel mailing list Xen-devel@... http://lists.xensource.com/xen-devel |
|
|
[PATCH 02/13] add DisplayState->idleFrom: Samuel Thibault <samuel.thibault@...>
From: Samuel Thibault <samuel.thibault@...> Add idle field to DisplayState struct, so drivers can figure the display is idle and take advantage of that. The xen framebuffer driver will use this to communicate the idle state to the guest, so it knows it can stop doing updates to a virtual display which is invisible anyway. Signed-off-by: Gerd Hoffmann <kraxel@...> --- console.h | 1 + sdl.c | 2 ++ vl.c | 2 ++ vnc.c | 3 +++ 4 files changed, 8 insertions(+), 0 deletions(-) diff --git a/console.h b/console.h index 561ef51..233e7ef 100644 --- a/console.h +++ b/console.h @@ -80,6 +80,7 @@ struct DisplayState { void *opaque; struct QEMUTimer *gui_timer; uint64_t gui_timer_interval; + int idle; void (*dpy_update)(struct DisplayState *s, int x, int y, int w, int h); void (*dpy_resize)(struct DisplayState *s, int w, int h); diff --git a/sdl.c b/sdl.c index 84a9d6d..15427c5 100644 --- a/sdl.c +++ b/sdl.c @@ -524,9 +524,11 @@ static void sdl_refresh(DisplayState *ds) if (ev->active.gain) { /* Back to default interval */ ds->gui_timer_interval = 0; + ds->idle = 0; } else { /* Sleeping interval */ ds->gui_timer_interval = 500; + ds->idle = 1; } } break; diff --git a/vl.c b/vl.c index 071ee0c..ee89107 100644 --- a/vl.c +++ b/vl.c @@ -5974,6 +5974,8 @@ static void dumb_display_init(DisplayState *ds) ds->dpy_update = dumb_update; ds->dpy_resize = dumb_resize; ds->dpy_refresh = dumb_refresh; + ds->gui_timer_interval = 500; + ds->idle = 1; } /***********************************************************/ diff --git a/vnc.c b/vnc.c index 2d17044..0d55a78 100644 --- a/vnc.c +++ b/vnc.c @@ -660,6 +660,7 @@ static int vnc_client_io_error(VncState *vs, int ret, int last_errno) qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL); closesocket(vs->csock); vs->csock = -1; + vs->ds->idle = 1; buffer_reset(&vs->input); buffer_reset(&vs->output); vs->need_update = 0; @@ -1920,6 +1921,7 @@ static int protocol_version(VncState *vs, uint8_t *version, size_t len) static void vnc_connect(VncState *vs) { VNC_DEBUG("New client on socket %d\n", vs->csock); + vs->ds->idle = 0; socket_set_nonblock(vs->csock); qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); vnc_write(vs, "RFB 003.008\n", 12); @@ -1959,6 +1961,7 @@ void vnc_display_init(DisplayState *ds) exit(1); ds->opaque = vs; + ds->idle = 1; vnc_state = vs; vs->display = NULL; vs->password = NULL; -- 1.5.5.1 _______________________________________________ Xen-devel mailing list Xen-devel@... http://lists.xensource.com/xen-devel |
|
|
[PATCH 03/13] add container_of() macro to osdep.h>From linux kernel sources, xen bits will use it, put it
into a place where others can see and use it too ;) Signed-off-by: Gerd Hoffmann <kraxel@...> --- osdep.h | 4 ++++ 1 files changed, 4 insertions(+), 0 deletions(-) diff --git a/osdep.h b/osdep.h index 6312e7a..09ebace 100644 --- a/osdep.h +++ b/osdep.h @@ -23,6 +23,10 @@ #define unlikely(x) __builtin_expect(!!(x), 0) #endif +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + #ifndef MIN #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #endif -- 1.5.5.1 _______________________________________________ Xen-devel mailing list Xen-devel@... http://lists.xensource.com/xen-devel |
|
|
[PATCH 04/13] move GUI_REFRESH_INTERVAL define from vl.c to console.hSigned-off-by: Gerd Hoffmann <kraxel@...> --- console.h | 3 +++ vl.c | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/console.h b/console.h index 233e7ef..069e73d 100644 --- a/console.h +++ b/console.h @@ -9,6 +9,9 @@ #define MOUSE_EVENT_RBUTTON 0x02 #define MOUSE_EVENT_MBUTTON 0x04 +/* in ms */ +#define GUI_REFRESH_INTERVAL 30 + typedef void QEMUPutKBDEvent(void *opaque, int keycode); typedef void QEMUPutMouseEvent(void *opaque, int dx, int dy, int dz, int buttons_state); diff --git a/vl.c b/vl.c index ee89107..3211507 100644 --- a/vl.c +++ b/vl.c @@ -154,8 +154,6 @@ int inet_aton(const char *cp, struct in_addr *ia); #else #define DEFAULT_RAM_SIZE 128 #endif -/* in ms */ -#define GUI_REFRESH_INTERVAL 30 /* Max number of USB devices that can be specified on the commandline. */ #define MAX_USB_CMDLINE 8 -- 1.5.5.1 _______________________________________________ Xen-devel mailing list Xen-devel@... http://lists.xensource.com/xen-devel |
|
|
[PATCH 05/13] xen: groundwork for xen support- configure script and build system changes.
- wind up new machine type. - add -domid command line option. - allow xenpv machines run without disk and kernel specified by adding a nodisk_ok field to QEMUMachine. - detect xen being present. Signed-off-by: Gerd Hoffmann <kraxel@...> --- Makefile.target | 8 +++ configure | 27 +++++++++ hw/boards.h | 3 + hw/xen.h | 18 ++++++ hw/xen_machine_pv.c | 146 +++++++++++++++++++++++++++++++++++++++++++++++++ target-i386/machine.c | 3 + vl.c | 15 +++++ 7 files changed, 220 insertions(+), 0 deletions(-) create mode 100644 hw/xen.h create mode 100644 hw/xen_machine_pv.c diff --git a/Makefile.target b/Makefile.target index 42162c3..13ee854 100644 --- a/Makefile.target +++ b/Makefile.target @@ -515,6 +515,14 @@ CPPFLAGS += $(CONFIG_VNC_TLS_CFLAGS) LIBS += $(CONFIG_VNC_TLS_LIBS) endif +# xen backend driver support +XEN_OBJS := xen_machine_pv.o +ifeq ($(CONFIG_XEN), yes) + OBJS += $(XEN_OBJS) + LIBS += $(XEN_LIBS) + $(XEN_OBJS) : CFLAGS += -Wall -Wmissing-prototypes -Wstrict-prototypes +endif + # SCSI layer OBJS+= lsi53c895a.o esp.o diff --git a/configure b/configure index 74ce443..14689d2 100755 --- a/configure +++ b/configure @@ -109,6 +109,7 @@ curses="yes" aio="yes" nptl="yes" mixemu="no" +xen="no" # OS specific targetos=`uname -s` @@ -204,6 +205,7 @@ linux="yes" linux_user="yes" if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then kqemu="yes" + xen="yes" audio_possible_drivers="$audio_possible_drivers fmod" fi ;; @@ -287,6 +289,8 @@ for opt do ;; --disable-kqemu) kqemu="no" ;; + --disable-xen) xen="no" + ;; --disable-brlapi) brlapi="no" ;; --enable-profiler) profiler="yes" @@ -425,6 +429,7 @@ echo " Available drivers: $audio_possible_drivers" echo " --audio-card-list=LIST set list of additional emulated audio cards" echo " Available cards: ac97 adlib cs4231a gus" echo " --enable-mixemu enable mixer emulation" +echo " --disable-xen disable xen backend driver support" echo " --disable-brlapi disable BrlAPI" echo " --disable-vnc-tls disable TLS encryption for VNC server" echo " --disable-curses disable curses output" @@ -686,6 +691,22 @@ else fi ########################################## +# xen probe + +if test "$xen" = "yes" ; then +cat > $TMPC <<EOF +#include <xenctrl.h> +#include <xs.h> +int main(void) { xs_daemon_open; xc_interface_open; } +EOF + if $cc $ARCH_CFLAGS -c -o $TMPO $TMPC -lxenstore -lxenctrl 2> /dev/null ; then + : + else + xen="no" + fi +fi + +########################################## # SDL probe sdl_too_old=no @@ -946,6 +967,7 @@ if test -n "$sparc_cpu"; then echo "Target Sparc Arch $sparc_cpu" fi echo "kqemu support $kqemu" +echo "xen support $xen" echo "brlapi support $brlapi" echo "Documentation $build_docs" [ ! -z "$uname_release" ] && \ @@ -1203,6 +1225,11 @@ if test "$brlapi" = "yes" ; then echo "#define CONFIG_BRLAPI 1" >> $config_h echo "BRLAPI_LIBS=-lbrlapi" >> $config_mak fi +if test "$xen" = "yes" ; then + echo "CONFIG_XEN=yes" >> $config_mak + echo "#define CONFIG_XEN 1" >> $config_h + echo "XEN_LIBS=-lxenstore -lxenctrl" >> $config_mak +fi if test "$aio" = "yes" ; then echo "#define CONFIG_AIO 1" >> $config_h fi diff --git a/hw/boards.h b/hw/boards.h index e6dd198..9af9939 100644 --- a/hw/boards.h +++ b/hw/boards.h @@ -30,6 +30,9 @@ extern QEMUMachine bareetraxfs_machine; extern QEMUMachine pc_machine; extern QEMUMachine isapc_machine; +/* xen_machine.c */ +extern QEMUMachine xenpv_machine; + /* ppc.c */ extern QEMUMachine prep_machine; extern QEMUMachine core99_machine; diff --git a/hw/xen.h b/hw/xen.h new file mode 100644 index 0000000..a772aad --- /dev/null +++ b/hw/xen.h @@ -0,0 +1,18 @@ +#ifndef QEMU_XEN_H +#define QEMU_XEN_H 1 +/* + * public xen header + * stuff needed outside xen-*.c, i.e. interfaces to qemu. + * must not depend on any xen headers being present in + * /usr/include/xen, so it can be included unconditionally. + */ + +/* xen-machine.c */ +extern int xen_domid; +extern int xen_present; +extern int xen_emulate; +extern int xen_domainbuild; + +int xen_detect(void); + +#endif /* QEMU_XEN_H */ diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c new file mode 100644 index 0000000..05f0e67 --- /dev/null +++ b/hw/xen_machine_pv.c @@ -0,0 +1,146 @@ +/* + * QEMU Xen PV Machine + * + * Copyright (c) 2007,08 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 "hw.h" +#include "boards.h" + +#include <xenctrl.h> + +/* -------------------------------------------------------------------- */ +/* variables */ + +int xen_domid; +int xen_present = -1; +int xen_emulate; +int xen_domainbuild; + +/* -------------------------------------------------------------------- */ +/* initialization code */ + +/* + * Figure the environment we are running in. + * Returns true when xen is present, false otherwise. + * Also checks whenever the domain specified via -domid + * exists (so we can attach) or whenever it must be created. + */ +int xen_detect(void) +{ + struct xc_dominfo info; + int xc, rc; + + if (xen_present != -1) + goto out; + + /* running on xen? (priviledged domain) */ + xen_present = 0; + xc = xc_interface_open(); + if (-1 == xc) + goto out; + xen_present = 1; + + /* does the domain exist? Or should we create one? */ + rc = xc_domain_getinfo(xc, xen_domid, 1, &info); + if ((1 != rc) || (info.domid != xen_domid)) + xen_domainbuild = 1; + close(xc); + +out: + return xen_present; +} + +static int xen_init(void) +{ + if (!xen_domid) { + fprintf(stderr, "%s: no domid specified\n", __FUNCTION__); + return -1; + } + + if (!xen_detect()) { + fprintf(stderr, "%s: emulating Xen\n", __FUNCTION__); + xen_emulate = 1; + } + + return 0; +} + +static int xen_init_pv(DisplayState *ds) +{ + int rc; + + rc = xen_init(); + if (rc < 0) + return rc; + + return 0; +} + +/* -------------------------------------------------------------------- */ +/* paravirtualized xen machine */ + +static void xenpv_init(ram_addr_t ram_size, int vga_ram_size, + const char *boot_device, DisplayState *ds, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + const char *cpu_model) +{ + CPUState *env; + int rc; + + rc = xen_init_pv(ds); + if (-1 == rc) + goto err; + + if (xen_emulate) { + fprintf(stderr, "xen pv emulation not implemented yet\n"); + goto err; + } + if (xen_domainbuild) { + fprintf(stderr, "xen pv domain creation not implemented yet\n"); + goto err; + } + + /* create dummy cpu, halted */ + if (cpu_model == NULL) { +#ifdef TARGET_X86_64 + cpu_model = "qemu64"; +#else + cpu_model = "qemu32"; +#endif + } + env = cpu_init(cpu_model); + env->halted = 1; + + return; + +err: + exit(1); +} + +QEMUMachine xenpv_machine = { + .name = "xenpv", + .desc = "paravirtualized Xen machine", + .init = xenpv_init, + .nodisk_ok = 1, +}; diff --git a/target-i386/machine.c b/target-i386/machine.c index 91dbd55..98ece17 100644 --- a/target-i386/machine.c +++ b/target-i386/machine.c @@ -9,6 +9,9 @@ void register_machines(void) { qemu_register_machine(&pc_machine); qemu_register_machine(&isapc_machine); +#ifdef CONFIG_XEN + qemu_register_machine(&xenpv_machine); +#endif } static void cpu_put_seg(QEMUFile *f, SegmentCache *dt) diff --git a/vl.c b/vl.c index 3211507..ff94c6f 100644 --- a/vl.c +++ b/vl.c @@ -29,6 +29,7 @@ #include "hw/audiodev.h" #include "hw/isa.h" #include "hw/baum.h" +#include "hw/xen.h" #include "net.h" #include "console.h" #include "sysemu.h" @@ -7780,6 +7781,9 @@ static void help(int exitcode) "-startdate select initial date of the clock\n" "-icount [N|auto]\n" " Enable virtual instruction counter with 2^N clock ticks per instruction\n" +#ifdef CONFIG_XEN + "-domid specify xen guest domain id\n" +#endif "\n" "During emulation, the following keys are useful:\n" "ctrl-alt-f toggle full screen\n" @@ -7885,6 +7889,9 @@ enum { QEMU_OPTION_startdate, QEMU_OPTION_tb_size, QEMU_OPTION_icount, +#ifdef CONFIG_XEN + QEMU_OPTION_domid, +#endif }; typedef struct QEMUOption { @@ -7973,6 +7980,9 @@ const QEMUOption qemu_options[] = { #ifdef CONFIG_CURSES { "curses", 0, QEMU_OPTION_curses }, #endif +#ifdef CONFIG_XEN + { "domid", HAS_ARG, QEMU_OPTION_domid }, +#endif /* temporary options */ { "usb", 0, QEMU_OPTION_usb }, @@ -8847,6 +8857,11 @@ int main(int argc, char **argv) icount_time_shift = strtol(optarg, NULL, 0); } break; +#ifdef CONFIG_XEN + case QEMU_OPTION_domid: + xen_domid = atoi(optarg); + break; +#endif } } } -- 1.5.5.1 _______________________________________________ Xen-devel mailing list Xen-devel@... http://lists.xensource.com/xen-devel |
|
|
[PATCH 06/13] xen: backend driver coreThis patch adds infrastructure for xen backend drivers living in qemu,
so drivers don't need to implement common stuff on their own. It's mostly xenbus management stuff: some functions to access xentore, setting up xenstore watches, callbacks on device discovery and state changes, handle event channel, ... Signed-off-by: Gerd Hoffmann <kraxel@...> --- Makefile.target | 2 +- hw/xen_backend.c | 691 +++++++++++++++++++++++++++++++++++++++++++++++++++ hw/xen_backend.h | 86 +++++++ hw/xen_common.h | 34 +++ hw/xen_machine_pv.c | 7 +- 5 files changed, 818 insertions(+), 2 deletions(-) create mode 100644 hw/xen_backend.c create mode 100644 hw/xen_backend.h create mode 100644 hw/xen_common.h diff --git a/Makefile.target b/Makefile.target index 13ee854..78a6402 100644 --- a/Makefile.target +++ b/Makefile.target @@ -516,7 +516,7 @@ LIBS += $(CONFIG_VNC_TLS_LIBS) endif # xen backend driver support -XEN_OBJS := xen_machine_pv.o +XEN_OBJS := xen_machine_pv.o xen_backend.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) LIBS += $(XEN_LIBS) diff --git a/hw/xen_backend.c b/hw/xen_backend.c new file mode 100644 index 0000000..e926a01 --- /dev/null +++ b/hw/xen_backend.c @@ -0,0 +1,691 @@ +/* + * xen backend driver infrastructure + * (c) 2008 Gerd Hoffmann <kraxel@...> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * TODO: add some xenbus / xenstore concepts overview here. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <inttypes.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/signal.h> + +#include <xs.h> +#include <xenctrl.h> +#include <xen/grant_table.h> + +#include "hw.h" +#include "qemu-char.h" +#include "xen_backend.h" + +/* ------------------------------------------------------------- */ + +/* public */ +int xen_xc; +struct xs_handle *xenstore = NULL; + +/* private */ +static TAILQ_HEAD(XenDeviceHead, XenDevice) xendevs = TAILQ_HEAD_INITIALIZER(xendevs); +static int debug = 0; + +/* ------------------------------------------------------------- */ + +int xenstore_write_str(const char *base, const char *node, const char *val) +{ + char abspath[XEN_BUFSIZE]; + + snprintf(abspath, sizeof(abspath), "%s/%s", base, node); + if (!xs_write(xenstore, 0, abspath, val, strlen(val))) + return -1; + return 0; +} + +char *xenstore_read_str(const char *base, const char *node) +{ + char abspath[XEN_BUFSIZE]; + unsigned int len; + + snprintf(abspath, sizeof(abspath), "%s/%s", base, node); + return xs_read(xenstore, 0, abspath, &len); +} + +int xenstore_write_int(const char *base, const char *node, int ival) +{ + char val[32]; + + snprintf(val, sizeof(val), "%d", ival); + return xenstore_write_str(base, node, val); +} + +int xenstore_read_int(const char *base, const char *node, int *ival) +{ + char *val; + int rc = -1; + + val = xenstore_read_str(base, node); + if (val && 1 == sscanf(val, "%d", ival)) + rc = 0; + qemu_free(val); + return rc; +} + +int xenstore_write_be_str(struct XenDevice *xendev, const char *node, const char *val) +{ + return xenstore_write_str(xendev->be, node, val); +} + +int xenstore_write_be_int(struct XenDevice *xendev, const char *node, int ival) +{ + return xenstore_write_int(xendev->be, node, ival); +} + +char *xenstore_read_be_str(struct XenDevice *xendev, const char *node) +{ + return xenstore_read_str(xendev->be, node); +} + +int xenstore_read_be_int(struct XenDevice *xendev, const char *node, int *ival) +{ + return xenstore_read_int(xendev->be, node, ival); +} + +char *xenstore_read_fe_str(struct XenDevice *xendev, const char *node) +{ + return xenstore_read_str(xendev->fe, node); +} + +int xenstore_read_fe_int(struct XenDevice *xendev, const char *node, int *ival) +{ + return xenstore_read_int(xendev->fe, node, ival); +} + +/* ------------------------------------------------------------- */ + +const char *xenbus_strstate(enum xenbus_state state) +{ + static const char *const name[] = { + [ XenbusStateUnknown ] = "Unknown", + [ XenbusStateInitialising ] = "Initialising", + [ XenbusStateInitWait ] = "InitWait", + [ XenbusStateInitialised ] = "Initialised", + [ XenbusStateConnected ] = "Connected", + [ XenbusStateClosing ] = "Closing", + [ XenbusStateClosed ] = "Closed", + }; + return (state < ARRAY_SIZE(name)) ? name[state] : "INVALID"; +} + +int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state) +{ + int rc; + + rc = xenstore_write_be_int(xendev, "state", state); + if (rc < 0) + return rc; + xen_be_printf(xendev, 1, "backend state: %s -> %s\n", + xenbus_strstate(xendev->be_state), xenbus_strstate(state)); + xendev->be_state = state; + return 0; +} + +/* ------------------------------------------------------------- */ + +struct XenDevice *xen_be_find_xendev(char *type, int dom, int dev) +{ + struct XenDevice *xendev; + + TAILQ_FOREACH(xendev, &xendevs, next) { + if (xendev->dom != dom) + continue; + if (xendev->dev != dev) + continue; + if (0 != strcmp(xendev->type, type)) + continue; + return xendev; + } + return NULL; +} + +/* + * get xen backend device, allocate a new one if it doesn't exist. + */ +static struct XenDevice *xen_be_get_xendev(char *type, int dom, int dev, + struct XenDevOps *ops) +{ + struct XenDevice *xendev; + char *dom0; + + xendev = xen_be_find_xendev(type, dom, dev); + if (xendev) + return xendev; + + /* init new xendev */ + xendev = qemu_mallocz(ops->size); + if (!xendev) + return NULL; + xendev->type = type; + xendev->dom = dom; + xendev->dev = dev; + xendev->ops = ops; + + dom0 = xs_get_domain_path(xenstore, 0); + snprintf(xendev->be, sizeof(xendev->be), "%s/backend/%s/%d/%d", + dom0, xendev->type, xendev->dom, xendev->dev); + snprintf(xendev->name, sizeof(xendev->name), "%s-%d", + xendev->type, xendev->dev); + free(dom0); + + xendev->debug = debug; + xendev->local_port = -1; + + xendev->evtchndev = xc_evtchn_open(); + if (xendev->evtchndev < 0) { + fprintf(stderr, "can't open evtchn device\n"); + qemu_free(xendev); + return NULL; + } + fcntl(xc_evtchn_fd(xendev->evtchndev), F_SETFD, FD_CLOEXEC); + + if (ops->flags & DEVOPS_FLAG_NEED_GNTDEV) { + xendev->gnttabdev = xc_gnttab_open(); + if (xendev->gnttabdev < 0) { + fprintf(stderr, "can't open gnttab device\n"); + xc_evtchn_close(xendev->evtchndev); + qemu_free(xendev); + return NULL; + } + } else { + xendev->gnttabdev = -1; + } + + TAILQ_INSERT_TAIL(&xendevs, xendev, next); + + if (xendev->ops->alloc) + xendev->ops->alloc(xendev); + + return xendev; +} + +/* + * release xen backend device. + */ +static struct XenDevice *xen_be_del_xendev(int dom, int dev) +{ + struct XenDevice *xendev, *xnext; + + /* + * This is pretty much like TAILQ_FOREACH(xendev, &xendevs, next) but + * we save the next pointer in xnext because we might free xendev. + */ + xnext = xendevs.tqh_first; + while (xnext) { + xendev = xnext; + xnext = xendev->next.tqe_next; + + if (xendev->dom != dom) + continue; + if (xendev->dev != dev && dev != -1) + continue; + + if (xendev->ops->free) + xendev->ops->free(xendev); + + if (xendev->fe) { + char token[XEN_BUFSIZE]; + snprintf(token, sizeof(token), "fe:%p", xendev); + xs_unwatch(xenstore, xendev->fe, token); + qemu_free(xendev->fe); + } + + if (xendev->evtchndev >= 0) + xc_evtchn_close(xendev->evtchndev); + if (xendev->gnttabdev >= 0) + xc_gnttab_close(xendev->gnttabdev); + + TAILQ_REMOVE(&xendevs, xendev, next); + qemu_free(xendev); + } + return NULL; +} + +/* + * Sync internal data structures on xenstore updates. + * Node specifies the changed field. node = NULL means + * update all fields (used for initialization). + */ +static void xen_be_backend_changed(struct XenDevice *xendev, const char *node) +{ + if (NULL == node || 0 == strcmp(node, "online")) { + if (-1 == xenstore_read_be_int(xendev, "online", &xendev->online)) + xendev->online = 0; + } + + if (node) { + xen_be_printf(xendev, 2, "backend update: %s\n", node); + if (xendev->ops->backend_changed) + xendev->ops->backend_changed(xendev, node); + } +} + +static void xen_be_frontend_changed(struct XenDevice *xendev, const char *node) +{ + int fe_state; + + if (NULL == node || 0 == strcmp(node, "state")) { + if (-1 == xenstore_read_fe_int(xendev, "state", &fe_state)) + fe_state = XenbusStateUnknown; + if (xendev->fe_state != fe_state) + xen_be_printf(xendev, 1, "frontend state: %s -> %s\n", + xenbus_strstate(xendev->fe_state), + xenbus_strstate(fe_state)); + xendev->fe_state = fe_state; + } + if (NULL == node || 0 == strcmp(node, "protocol")) { + qemu_free(xendev->protocol); + xendev->protocol = xenstore_read_fe_str(xendev, "protocol"); + if (xendev->protocol) + xen_be_printf(xendev, 1, "frontend protocol: %s\n", xendev->protocol); + } + + if (node) { + xen_be_printf(xendev, 2, "frontend update: %s\n", node); + if (xendev->ops->frontend_changed) + xendev->ops->frontend_changed(xendev, node); + } +} + +/* ------------------------------------------------------------- */ +/* Check for possible state transitions and perform them. */ + +/* + * Initial xendev setup. Read frontend path, register watch for it. + * Should succeed once xend finished setting up the backend device. + * + * Also sets initial state (-> Initializing) when done. Which + * only affects the xendev->be_state variable as xenbus should + * already be put into that state by xend. + */ +static int xen_be_try_setup(struct XenDevice *xendev) +{ + char token[XEN_BUFSIZE]; + int be_state; + + if (-1 == xenstore_read_be_int(xendev, "state", &be_state)) { + xen_be_printf(xendev, 0, "reading backend state failed\n"); + return -1; + } + + if (be_state != XenbusStateInitialising) { + xen_be_printf(xendev, 0, "initial backend state is wrong (%s)\n", + xenbus_strstate(be_state)); + return -1; + } + + xendev->fe = xenstore_read_be_str(xendev, "frontend"); + if (NULL == xendev->fe) { + xen_be_printf(xendev, 0, "reading frontend path failed\n"); + return -1; + } + + /* setup frontend watch */ + snprintf(token, sizeof(token), "fe:%p", xendev); + if (!xs_watch(xenstore, xendev->fe, token)) { + xen_be_printf(xendev, 0, "watching frontend path (%s) failed\n", + xendev->fe); + return -1; + } + xen_be_set_state(xendev, XenbusStateInitialising); + + xen_be_backend_changed(xendev, NULL); + xen_be_frontend_changed(xendev, NULL); + return 0; +} + +/* + * Try initialize xendev. Prepare everything the backend can do + * without synchronizing with the frontend. Fakes hotplug-status. No + * hotplug involved here because this is about userspace drivers, thus + * there are kernel backend devices which could invoke hotplug. + * + * Goes to InitWait on success. + */ +static int xen_be_try_init(struct XenDevice *xendev) +{ + int rc = 0; + + if (!xendev->online) { + xen_be_printf(xendev, 1, "not online\n"); + return -1; + } + + if (xendev->ops->init) + rc = xendev->ops->init(xendev); + if (0 != rc) { + xen_be_printf(xendev, 1, "init() failed\n"); + return rc; + } + + xenstore_write_be_str(xendev, "hotplug-status", "connected"); + xen_be_set_state(xendev, XenbusStateInitWait); + return 0; +} + +/* + * Try to connect xendev. Depends on the frontend being ready + * for it (shared ring and evtchn info in xenstore, state being + * Initialised or Connected). + * + * Goes to Connected on success. + */ +static int xen_be_try_connect(struct XenDevice *xendev) +{ + int rc = 0; + + if (xendev->fe_state != XenbusStateInitialised && + xendev->fe_state != XenbusStateConnected) { + if (xendev->ops->flags & DEVOPS_FLAG_IGNORE_STATE) { + xen_be_printf(xendev, 2, "frontend not ready, ignoring\n"); + } else { + xen_be_printf(xendev, 2, "frontend not ready (yet)\n"); + return -1; + } + } + + if (xendev->ops->connect) + rc = xendev->ops->connect(xendev); + if (0 != rc) { + xen_be_printf(xendev, 0, "connect() failed\n"); + return rc; + } + + xen_be_set_state(xendev, XenbusStateConnected); + return 0; +} + +/* + * Teardown connection. + * + * Goes to Closed when done. + */ +static void xen_be_disconnect(struct XenDevice *xendev, enum xenbus_state state) +{ + if (xendev->be_state != XenbusStateClosing && + xendev->be_state != XenbusStateClosed && + xendev->ops->disconnect) + xendev->ops->disconnect(xendev); + if (xendev->be_state != state) + xen_be_set_state(xendev, state); +} + +/* + * Try to reset xendev, for reconnection by another frontend instance. + */ +static int xen_be_try_reset(struct XenDevice *xendev) +{ + if (xendev->fe_state != XenbusStateInitialising) + return -1; + + xen_be_printf(xendev, 1, "device reset (for re-connect)\n"); + xen_be_set_state(xendev, XenbusStateInitialising); + return 0; +} + +/* + * state change dispatcher function + */ +void xen_be_check_state(struct XenDevice *xendev) +{ + int rc = 0; + + /* frontend may request shutdown from almost anywhere */ + if (xendev->fe_state == XenbusStateClosing || + xendev->fe_state == XenbusStateClosed) { + xen_be_disconnect(xendev, xendev->fe_state); + return; + } + + /* check for possible backend state transitions */ + for (;;) { + switch (xendev->be_state) { + case XenbusStateUnknown: + rc = xen_be_try_setup(xendev); + break; + case XenbusStateInitialising: + rc = xen_be_try_init(xendev); + break; + case XenbusStateInitWait: + rc = xen_be_try_connect(xendev); + break; + case XenbusStateClosed: + rc = xen_be_try_reset(xendev); + break; + default: + rc = -1; + } + if (0 != rc) + break; + } +} + +/* ------------------------------------------------------------- */ + +static int xenstore_scan(char *type, int dom, struct XenDevOps *ops) +{ + struct XenDevice *xendev; + char path[XEN_BUFSIZE], token[XEN_BUFSIZE]; + char **dev = NULL, *dom0; + unsigned int cdev, j; + + /* setup watch */ + dom0 = xs_get_domain_path(xenstore, 0); + snprintf(token, sizeof(token), "be:%p:%d:%p", type, dom, ops); + snprintf(path, sizeof(path), "%s/backend/%s/%d", dom0, type, dom); + free(dom0); + if (!xs_watch(xenstore, path, token)) { + fprintf(stderr, "xen be: watching backend path (%s) failed\n", path); + return -1; + } + + /* look for backends */ + dev = xs_directory(xenstore, 0, path, &cdev); + if (!dev) + return 0; + for (j = 0; j < cdev; j++) { + xendev = xen_be_get_xendev(type, dom, atoi(dev[j]), ops); + if (NULL == xendev) + continue; + xen_be_check_state(xendev); + } + qemu_free(dev); + return 0; +} + +static void xenstore_update_be(char *watch, char *type, int dom, + struct XenDevOps *ops) +{ + struct XenDevice *xendev; + char path[XEN_BUFSIZE], *dom0; + unsigned int len, dev; + + dom0 = xs_get_domain_path(xenstore, 0); + len = snprintf(path, sizeof(path), "%s/backend/%s/%d", dom0, type, dom); + free(dom0); + if (0 != strncmp(path, watch, len)) + return; + if (2 != sscanf(watch+len, "/%u/%255s", &dev, path)) { + strcpy(path, ""); + if (1 != sscanf(watch+len, "/%u", &dev)) + dev = -1; + } + if (-1 == dev) + return; + + if (0) { + /* FIXME: detect devices being deleted from xenstore ... */ + xen_be_del_xendev(dom, dev); + } + + xendev = xen_be_get_xendev(type, dom, dev, ops); + if (NULL != xendev) { + xen_be_backend_changed(xendev, path); + xen_be_check_state(xendev); + } +} + +static void xenstore_update_fe(char *watch, struct XenDevice *xendev) +{ + char *node; + unsigned int len; + + len = strlen(xendev->fe); + if (0 != strncmp(xendev->fe, watch, len)) + return; + if (watch[len] != '/') + return; + node = watch + len + 1; + + xen_be_frontend_changed(xendev, node); + xen_be_check_state(xendev); +} + +static void xenstore_update(void *unused) +{ + char **vec = NULL; + intptr_t type, ops, ptr; + unsigned int dom, count; + + vec = xs_read_watch(xenstore, &count); + if (NULL == vec) + goto cleanup; + + if (3 == sscanf(vec[XS_WATCH_TOKEN], "be:%" PRIxPTR ":%d:%" PRIxPTR, + &type, &dom, &ops)) + xenstore_update_be(vec[XS_WATCH_PATH], (void*)type, dom, (void*)ops); + if (1 == sscanf(vec[XS_WATCH_TOKEN], "fe:%" PRIxPTR, &ptr)) + xenstore_update_fe(vec[XS_WATCH_PATH], (void*)ptr); + +cleanup: + qemu_free(vec); +} + +static void xen_be_evtchn_event(void *opaque) +{ + struct XenDevice *xendev = opaque; + evtchn_port_t port; + + port = xc_evtchn_pending(xendev->evtchndev); + if (port != xendev->local_port) { + xen_be_printf(xendev, 0, "xc_evtchn_pending returned %d (expected %d)\n", + port, xendev->local_port); + return; + } + xc_evtchn_unmask(xendev->evtchndev, port); + + if (xendev->ops->event) + xendev->ops->event(xendev); +} + +/* -------------------------------------------------------------------- */ + +int xen_be_init(void) +{ + xenstore = xs_daemon_open(); + if (!xenstore) { + fprintf(stderr, "can't connect to xenstored\n"); + return -1; + } + + if (qemu_set_fd_handler(xs_fileno(xenstore), xenstore_update, NULL, NULL) < 0) + goto err; + + xen_xc = xc_interface_open(); + if (-1 == xen_xc) { + fprintf(stderr, "can't open xen interface\n"); + goto err; + } + return 0; + +err: + qemu_set_fd_handler(xs_fileno(xenstore), NULL, NULL, NULL); + xs_daemon_close(xenstore); + xenstore = NULL; + + return -1; +} + +int xen_be_register(char *type, struct XenDevOps *ops) +{ + return xenstore_scan(type, xen_domid, ops); +} + +int xen_be_bind_evtchn(struct XenDevice *xendev) +{ + if (xendev->local_port != -1) + return 0; + xendev->local_port = xc_evtchn_bind_interdomain + (xendev->evtchndev, xendev->dom, xendev->remote_port); + if (-1 == xendev->local_port) { + xen_be_printf(xendev, 0, "xc_evtchn_bind_interdomain failed\n"); + return -1; + } + xen_be_printf(xendev, 2, "bind evtchn port %d\n", xendev->local_port); + qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev), + xen_be_evtchn_event, NULL, xendev); + return 0; +} + +void xen_be_unbind_evtchn(struct XenDevice *xendev) +{ + if (xendev->local_port == -1) + return; + qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev), NULL, NULL, NULL); + xc_evtchn_unbind(xendev->evtchndev, xendev->local_port); + xen_be_printf(xendev, 2, "unbind evtchn port %d\n", xendev->local_port); + xendev->local_port = -1; +} + +int xen_be_send_notify(struct XenDevice *xendev) +{ + return xc_evtchn_notify(xendev->evtchndev, xendev->local_port); +} + +/* + * msg_level: + * 0 == errors. + * 1 == informative debug messages. + * 2 == noisy debug messages. + * 3 == will flood your log. + */ +void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ...) +{ + va_list args; + + if (msg_level > xendev->debug) + return; + fprintf(stderr, "xen be: %s: ", xendev->name); + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} diff --git a/hw/xen_backend.h b/hw/xen_backend.h new file mode 100644 index 0000000..3a79ad0 --- /dev/null +++ b/hw/xen_backend.h @@ -0,0 +1,86 @@ +#ifndef QEMU_XEN_BACKEND_H +#define QEMU_XEN_BACKEND_H 1 + +#include "xen_common.h" + +/* ------------------------------------------------------------- */ + +#define XEN_BUFSIZE 1024 + +struct XenDevice; + +/* driver uses grant tables -> open gntdev device (xendev->gnttabdev) */ +#define DEVOPS_FLAG_NEED_GNTDEV 1 +/* don't expect frontend doing correct state transitions (aka console quirk) */ +#define DEVOPS_FLAG_IGNORE_STATE 2 + +struct XenDevOps { + size_t size; + uint32_t flags; + void (*alloc)(struct XenDevice *xendev); + int (*init)(struct XenDevice *xendev); + int (*connect)(struct XenDevice *xendev); + void (*event)(struct XenDevice *xendev); + void (*disconnect)(struct XenDevice *xendev); + int (*free)(struct XenDevice *xendev); + void (*backend_changed)(struct XenDevice *xendev, const char *node); + void (*frontend_changed)(struct XenDevice *xendev, const char *node); +}; + +struct XenDevice { + char *type; + int dom; + int dev; + char name[64]; + int debug; + + enum xenbus_state be_state; + enum xenbus_state fe_state; + int online; + char be[XEN_BUFSIZE]; + char *fe; + char *protocol; + int remote_port; + int local_port; + + int evtchndev; + int gnttabdev; + + struct XenDevOps *ops; + TAILQ_ENTRY(XenDevice) next; +}; + +/* ------------------------------------------------------------- */ + +/* variables */ +extern int xen_xc; +extern struct xs_handle *xenstore; + +/* xenstore helper functions */ +int xenstore_write_str(const char *base, const char *node, const char *val); +int xenstore_write_int(const char *base, const char *node, int ival); +char *xenstore_read_str(const char *base, const char *node); +int xenstore_read_int(const char *base, const char *node, int *ival); + +int xenstore_write_be_str(struct XenDevice *xendev, const char *node, const char *val); +int xenstore_write_be_int(struct XenDevice *xendev, const char *node, int ival); +char *xenstore_read_be_str(struct XenDevice *xendev, const char *node); +int xenstore_read_be_int(struct XenDevice *xendev, const char *node, int *ival); +char *xenstore_read_fe_str(struct XenDevice *xendev, const char *node); +int xenstore_read_fe_int(struct XenDevice *xendev, const char *node, int *ival); + +const char *xenbus_strstate(enum xenbus_state state); +struct XenDevice *xen_be_find_xendev(char *type, int dom, int dev); +void xen_be_check_state(struct XenDevice *xendev); + +/* xen backend driver bits */ +int xen_be_init(void); +int xen_be_register(char *type, struct XenDevOps *ops); +int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state); +int xen_be_bind_evtchn(struct XenDevice *xendev); +void xen_be_unbind_evtchn(struct XenDevice *xendev); +int xen_be_send_notify(struct XenDevice *xendev); +void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ...) + __attribute__ ((format(printf, 3, 4))); + +#endif /* QEMU_XEN_BACKEND_H */ diff --git a/hw/xen_common.h b/hw/xen_common.h new file mode 100644 index 0000000..a9e1d26 --- /dev/null +++ b/hw/xen_common.h @@ -0,0 +1,34 @@ +#ifndef QEMU_XEN_COMMON_H +#define QEMU_XEN_COMMON_H 1 + +#include <stddef.h> +#include <inttypes.h> + +#include <xenctrl.h> +#include <xs.h> +#include <xen/io/xenbus.h> + +#include "hw.h" +#include "xen.h" +#include "sys-queue.h" /* BSD list implementation */ + +/* + * tweaks needed to build with different xen versions + * 0x00030205 -> 3.1.0 + * 0x00030207 -> 3.2.0 + * 0x00030208 -> unstable + */ +#include <xen/xen-compat.h> +#if __XEN_LATEST_INTERFACE_VERSION__ < 0x00030205 +# define evtchn_port_or_error_t int +#endif +#if __XEN_LATEST_INTERFACE_VERSION__ < 0x00030207 +# define xc_map_foreign_pages xc_map_foreign_batch +#endif +#if __XEN_LATEST_INTERFACE_VERSION__ < 0x00030208 +# define xen_mb() mb() +# define xen_rmb() rmb() +# define xen_wmb() wmb() +#endif + +#endif /* QEMU_XEN_COMMON_H */ diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c index 05f0e67..2eceb8e 100644 --- a/hw/xen_machine_pv.c +++ b/hw/xen_machine_pv.c @@ -25,7 +25,7 @@ #include "hw.h" #include "boards.h" -#include <xenctrl.h> +#include "xen_backend.h" /* -------------------------------------------------------------------- */ /* variables */ @@ -81,6 +81,11 @@ static int xen_init(void) xen_emulate = 1; } + if (-1 == xen_be_init()) { + fprintf(stderr, "%s: xen backend core setup failed\n", __FUNCTION__); + return -1; + } + return 0; } -- 1.5.5.1 _______________________________________________ Xen-devel mailing list Xen-devel@... http://lists.xensource.com/xen-devel |
|
|
[PATCH 07/13] xen: add console backend driver.This patch adds a xenconsole backend driver. It it based on current
xen-unstable code. It has been changed to make use of the common backend driver code. Signed-off-by: Gerd Hoffmann <kraxel@...> --- Makefile.target | 1 + hw/xen_backend.h | 3 + hw/xen_console.c | 271 +++++++++++++++++++++++++++++++++++++++++++++++++++ hw/xen_machine_pv.c | 3 + 4 files changed, 278 insertions(+), 0 deletions(-) create mode 100644 hw/xen_console.c diff --git a/Makefile.target b/Makefile.target index 78a6402..92ad109 100644 --- a/Makefile.target +++ b/Makefile.target @@ -517,6 +517,7 @@ endif # xen backend driver support XEN_OBJS := xen_machine_pv.o xen_backend.o +XEN_OBJS += xen_console.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) LIBS += $(XEN_LIBS) diff --git a/hw/xen_backend.h b/hw/xen_backend.h index 3a79ad0..10bd1a6 100644 --- a/hw/xen_backend.h +++ b/hw/xen_backend.h @@ -83,4 +83,7 @@ int xen_be_send_notify(struct XenDevice *xendev); void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ...) __attribute__ ((format(printf, 3, 4))); +/* actual backend drivers */ +struct XenDevOps xen_console_ops; /* xen_console.c */ + #endif /* QEMU_XEN_BACKEND_H */ diff --git a/hw/xen_console.c b/hw/xen_console.c new file mode 100644 index 0000000..2ea88f6 --- /dev/null +++ b/hw/xen_console.c @@ -0,0 +1,271 @@ +/* + * Copyright (C) International Business Machines Corp., 2005 + * Author(s): Anthony Liguori <aliguori@...> + * + * Copyright (C) Red Hat 2007 + * + * Xen Console + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <sys/select.h> +#include <fcntl.h> +#include <unistd.h> +#include <termios.h> +#include <stdarg.h> +#include <sys/mman.h> +#include <xs.h> +#include <xen/io/console.h> +#include <xenctrl.h> + +#include "hw.h" +#include "sysemu.h" +#include "qemu-char.h" +#include "xen_backend.h" + +#define dolog(val, fmt, ...) fprintf(stderr, fmt "\n", ## __VA_ARGS__) + +struct buffer { + uint8_t *data; + size_t consumed; + size_t size; + size_t capacity; + size_t max_capacity; +}; + +struct XenConsole { + struct XenDevice xendev; /* must be first */ + struct buffer buffer; + char console[XEN_BUFSIZE]; + int ring_ref; + void *sring; + CharDriverState *chr; + int backlog; +}; + +static void buffer_append(struct XenConsole *con) +{ + struct buffer *buffer = &con->buffer; + XENCONS_RING_IDX cons, prod, size; + struct xencons_interface *intf = con->sring; + + cons = intf->out_cons; + prod = intf->out_prod; + xen_mb(); + + size = prod - cons; + if ((size == 0) || (size > sizeof(intf->out))) + return; + + if ((buffer->capacity - buffer->size) < size) { + buffer->capacity += (size + 1024); + buffer->data = qemu_realloc(buffer->data, buffer->capacity); + if (buffer->data == NULL) { + dolog(LOG_ERR, "Memory allocation failed"); + exit(ENOMEM); + } + } + + while (cons != prod) + buffer->data[buffer->size++] = intf->out[ + MASK_XENCONS_IDX(cons++, intf->out)]; + + xen_mb(); + intf->out_cons = cons; + xen_be_send_notify(&con->xendev); + + if (buffer->max_capacity && + buffer->size > buffer->max_capacity) { + /* Discard the middle of the data. */ + + size_t over = buffer->size - buffer->max_capacity; + uint8_t *maxpos = buffer->data + buffer->max_capacity; + + memmove(maxpos - over, maxpos, over); + buffer->data = qemu_realloc(buffer->data, buffer->max_capacity); + buffer->size = buffer->capacity = buffer->max_capacity; + + if (buffer->consumed > buffer->max_capacity - over) + buffer->consumed = buffer->max_capacity - over; + } +} + +static void buffer_advance(struct buffer *buffer, size_t len) +{ + buffer->consumed += len; + if (buffer->consumed == buffer->size) { + buffer->consumed = 0; + buffer->size = 0; + } +} + +static int ring_free_bytes(struct XenConsole *con) +{ + struct xencons_interface *intf = con->sring; + XENCONS_RING_IDX cons, prod, space; + + cons = intf->in_cons; + prod = intf->in_prod; + xen_mb(); + + space = prod - cons; + if (space > sizeof(intf->in)) + return 0; /* ring is screwed: ignore it */ + + return (sizeof(intf->in) - space); +} + +static int xencons_can_receive(void *opaque) +{ + struct XenConsole *con = opaque; + return ring_free_bytes(con); +} + +static void xencons_receive(void *opaque, const uint8_t *buf, int len) +{ + struct XenConsole *con = opaque; + struct xencons_interface *intf = con->sring; + XENCONS_RING_IDX prod; + int i, max; + + max = ring_free_bytes(con); + /* The can_receive() func limits this, but check again anyway */ + if (max < len) + len = max; + + prod = intf->in_prod; + for (i = 0; i < len; i++) { + intf->in[MASK_XENCONS_IDX(prod++, intf->in)] = + buf[i]; + } + xen_wmb(); + intf->in_prod = prod; + xen_be_send_notify(&con->xendev); +} + +static void xencons_send(struct XenConsole *con) +{ + ssize_t len, size; + + size = con->buffer.size - con->buffer.consumed; + len = qemu_chr_write(con->chr, con->buffer.data + con->buffer.consumed, + size); + if (len < 1) { + if (!con->backlog) { + con->backlog = 1; + xen_be_printf(&con->xendev, 1, "backlog piling up, nobody listening?\n"); + } + } else { + buffer_advance(&con->buffer, len); + if (con->backlog && len == size) { + con->backlog = 0; + xen_be_printf(&con->xendev, 1, "backlog is gone\n"); + } + } +} + +/* -------------------------------------------------------------------- */ + +static int con_init(struct XenDevice *xendev) +{ + struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); + char *type, *dom; + + if (!serial_hds[con->xendev.dev]) { + xen_be_printf(xendev, 1, "serial line %d not configured\n", con->xendev.dev); + return -1; + } + + /* setup */ + dom = xs_get_domain_path(xenstore, con->xendev.dom); + snprintf(con->console, sizeof(con->console), "%s/console", dom); + free( |