Commit 9306acb50991b7c78510ff4ffe19a190f2fc89c0
1 parent
2c8b24a3
xen: pv domain builder. (Gerd Hoffmann)
This adds domain building support for paravirtual domains to qemu. This allows booting xen guests directly with qemu, without Xend and the management stack. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com> git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@7226 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
7 changed files
with
360 additions
and
2 deletions
Makefile.target
| @@ -561,7 +561,7 @@ LIBS += $(CONFIG_BLUEZ_LIBS) | @@ -561,7 +561,7 @@ LIBS += $(CONFIG_BLUEZ_LIBS) | ||
| 561 | endif | 561 | endif |
| 562 | 562 | ||
| 563 | # xen backend driver support | 563 | # xen backend driver support |
| 564 | -XEN_OBJS := xen_machine_pv.o xen_backend.o xen_devconfig.o | 564 | +XEN_OBJS := xen_machine_pv.o xen_backend.o xen_devconfig.o xen_domainbuild.o |
| 565 | XEN_OBJS += xen_console.o xenfb.o xen_disk.o xen_nic.o | 565 | XEN_OBJS += xen_console.o xenfb.o xen_disk.o xen_nic.o |
| 566 | ifeq ($(CONFIG_XEN), yes) | 566 | ifeq ($(CONFIG_XEN), yes) |
| 567 | OBJS += $(XEN_OBJS) | 567 | OBJS += $(XEN_OBJS) |
configure
| @@ -1634,7 +1634,7 @@ if test "$bluez" = "yes" ; then | @@ -1634,7 +1634,7 @@ if test "$bluez" = "yes" ; then | ||
| 1634 | echo "#define CONFIG_BLUEZ 1" >> $config_h | 1634 | echo "#define CONFIG_BLUEZ 1" >> $config_h |
| 1635 | fi | 1635 | fi |
| 1636 | if test "$xen" = "yes" ; then | 1636 | if test "$xen" = "yes" ; then |
| 1637 | - echo "XEN_LIBS=-lxenstore -lxenctrl" >> $config_mak | 1637 | + echo "XEN_LIBS=-lxenstore -lxenctrl -lxenguest" >> $config_mak |
| 1638 | fi | 1638 | fi |
| 1639 | if test "$aio" = "yes" ; then | 1639 | if test "$aio" = "yes" ; then |
| 1640 | echo "#define CONFIG_AIO 1" >> $config_h | 1640 | echo "#define CONFIG_AIO 1" >> $config_h |
hw/xen_backend.h
| @@ -100,5 +100,8 @@ void xen_init_display(int domid); | @@ -100,5 +100,8 @@ void xen_init_display(int domid); | ||
| 100 | void xen_config_cleanup(void); | 100 | void xen_config_cleanup(void); |
| 101 | int xen_config_dev_blk(DriveInfo *disk); | 101 | int xen_config_dev_blk(DriveInfo *disk); |
| 102 | int xen_config_dev_nic(NICInfo *nic); | 102 | int xen_config_dev_nic(NICInfo *nic); |
| 103 | +int xen_config_dev_vfb(int vdev, const char *type); | ||
| 104 | +int xen_config_dev_vkbd(int vdev); | ||
| 105 | +int xen_config_dev_console(int vdev); | ||
| 103 | 106 | ||
| 104 | #endif /* QEMU_HW_XEN_BACKEND_H */ | 107 | #endif /* QEMU_HW_XEN_BACKEND_H */ |
hw/xen_devconfig.c
| @@ -140,3 +140,32 @@ int xen_config_dev_nic(NICInfo *nic) | @@ -140,3 +140,32 @@ int xen_config_dev_nic(NICInfo *nic) | ||
| 140 | /* common stuff */ | 140 | /* common stuff */ |
| 141 | return xen_config_dev_all(fe, be); | 141 | return xen_config_dev_all(fe, be); |
| 142 | } | 142 | } |
| 143 | + | ||
| 144 | +int xen_config_dev_vfb(int vdev, const char *type) | ||
| 145 | +{ | ||
| 146 | + char fe[256], be[256]; | ||
| 147 | + | ||
| 148 | + xen_config_dev_dirs("vfb", "vfb", vdev, fe, be, sizeof(fe)); | ||
| 149 | + | ||
| 150 | + /* backend */ | ||
| 151 | + xenstore_write_str(be, "type", type); | ||
| 152 | + | ||
| 153 | + /* common stuff */ | ||
| 154 | + return xen_config_dev_all(fe, be); | ||
| 155 | +} | ||
| 156 | + | ||
| 157 | +int xen_config_dev_vkbd(int vdev) | ||
| 158 | +{ | ||
| 159 | + char fe[256], be[256]; | ||
| 160 | + | ||
| 161 | + xen_config_dev_dirs("vkbd", "vkbd", vdev, fe, be, sizeof(fe)); | ||
| 162 | + return xen_config_dev_all(fe, be); | ||
| 163 | +} | ||
| 164 | + | ||
| 165 | +int xen_config_dev_console(int vdev) | ||
| 166 | +{ | ||
| 167 | + char fe[256], be[256]; | ||
| 168 | + | ||
| 169 | + xen_config_dev_dirs("console", "console", vdev, fe, be, sizeof(fe)); | ||
| 170 | + return xen_config_dev_all(fe, be); | ||
| 171 | +} |
hw/xen_domainbuild.c
0 → 100644
| 1 | +#include <signal.h> | ||
| 2 | +#include "xen_backend.h" | ||
| 3 | +#include "xen_domainbuild.h" | ||
| 4 | +#include "sysemu.h" | ||
| 5 | +#include "qemu-timer.h" | ||
| 6 | + | ||
| 7 | +#include <xenguest.h> | ||
| 8 | + | ||
| 9 | +static int xenstore_domain_mkdir(char *path) | ||
| 10 | +{ | ||
| 11 | + struct xs_permissions perms_ro[] = {{ | ||
| 12 | + .id = 0, /* set owner: dom0 */ | ||
| 13 | + },{ | ||
| 14 | + .id = xen_domid, | ||
| 15 | + .perms = XS_PERM_READ, | ||
| 16 | + }}; | ||
| 17 | + struct xs_permissions perms_rw[] = {{ | ||
| 18 | + .id = 0, /* set owner: dom0 */ | ||
| 19 | + },{ | ||
| 20 | + .id = xen_domid, | ||
| 21 | + .perms = XS_PERM_READ | XS_PERM_WRITE, | ||
| 22 | + }}; | ||
| 23 | + const char *writable[] = { "device", "control", "error", NULL }; | ||
| 24 | + char subpath[256]; | ||
| 25 | + int i; | ||
| 26 | + | ||
| 27 | + if (!xs_mkdir(xenstore, 0, path)) { | ||
| 28 | + fprintf(stderr, "%s: xs_mkdir %s: failed\n", __FUNCTION__, path); | ||
| 29 | + return -1; | ||
| 30 | + } | ||
| 31 | + if (!xs_set_permissions(xenstore, 0, path, perms_ro, 2)) { | ||
| 32 | + fprintf(stderr, "%s: xs_set_permissions failed\n", __FUNCTION__); | ||
| 33 | + return -1; | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + for (i = 0; writable[i]; i++) { | ||
| 37 | + snprintf(subpath, sizeof(subpath), "%s/%s", path, writable[i]); | ||
| 38 | + if (!xs_mkdir(xenstore, 0, subpath)) { | ||
| 39 | + fprintf(stderr, "%s: xs_mkdir %s: failed\n", __FUNCTION__, subpath); | ||
| 40 | + return -1; | ||
| 41 | + } | ||
| 42 | + if (!xs_set_permissions(xenstore, 0, subpath, perms_rw, 2)) { | ||
| 43 | + fprintf(stderr, "%s: xs_set_permissions failed\n", __FUNCTION__); | ||
| 44 | + return -1; | ||
| 45 | + } | ||
| 46 | + } | ||
| 47 | + return 0; | ||
| 48 | +} | ||
| 49 | + | ||
| 50 | +int xenstore_domain_init1(const char *kernel, const char *ramdisk, | ||
| 51 | + const char *cmdline) | ||
| 52 | +{ | ||
| 53 | + char *dom, uuid_string[42], vm[256], path[256]; | ||
| 54 | + int i; | ||
| 55 | + | ||
| 56 | + snprintf(uuid_string, sizeof(uuid_string), UUID_FMT, | ||
| 57 | + qemu_uuid[0], qemu_uuid[1], qemu_uuid[2], qemu_uuid[3], | ||
| 58 | + qemu_uuid[4], qemu_uuid[5], qemu_uuid[6], qemu_uuid[7], | ||
| 59 | + qemu_uuid[8], qemu_uuid[9], qemu_uuid[10], qemu_uuid[11], | ||
| 60 | + qemu_uuid[12], qemu_uuid[13], qemu_uuid[14], qemu_uuid[15]); | ||
| 61 | + dom = xs_get_domain_path(xenstore, xen_domid); | ||
| 62 | + snprintf(vm, sizeof(vm), "/vm/%s", uuid_string); | ||
| 63 | + | ||
| 64 | + xenstore_domain_mkdir(dom); | ||
| 65 | + | ||
| 66 | + xenstore_write_str(vm, "image/ostype", "linux"); | ||
| 67 | + if (kernel) | ||
| 68 | + xenstore_write_str(vm, "image/kernel", kernel); | ||
| 69 | + if (ramdisk) | ||
| 70 | + xenstore_write_str(vm, "image/ramdisk", ramdisk); | ||
| 71 | + if (cmdline) | ||
| 72 | + xenstore_write_str(vm, "image/cmdline", cmdline); | ||
| 73 | + | ||
| 74 | + /* name + id */ | ||
| 75 | + xenstore_write_str(vm, "name", qemu_name ? qemu_name : "no-name"); | ||
| 76 | + xenstore_write_str(vm, "uuid", uuid_string); | ||
| 77 | + xenstore_write_str(dom, "name", qemu_name ? qemu_name : "no-name"); | ||
| 78 | + xenstore_write_int(dom, "domid", xen_domid); | ||
| 79 | + xenstore_write_str(dom, "vm", vm); | ||
| 80 | + | ||
| 81 | + /* memory */ | ||
| 82 | + xenstore_write_int(dom, "memory/target", ram_size >> 10); // kB | ||
| 83 | + xenstore_write_int(vm, "memory", ram_size >> 20); // MB | ||
| 84 | + xenstore_write_int(vm, "maxmem", ram_size >> 20); // MB | ||
| 85 | + | ||
| 86 | + /* cpus */ | ||
| 87 | + for (i = 0; i < smp_cpus; i++) { | ||
| 88 | + snprintf(path, sizeof(path), "cpu/%d/availability",i); | ||
| 89 | + xenstore_write_str(dom, path, "online"); | ||
| 90 | + } | ||
| 91 | + xenstore_write_int(vm, "vcpu_avail", smp_cpus); | ||
| 92 | + xenstore_write_int(vm, "vcpus", smp_cpus); | ||
| 93 | + | ||
| 94 | + /* vnc password */ | ||
| 95 | + xenstore_write_str(vm, "vncpassword", "" /* FIXME */); | ||
| 96 | + | ||
| 97 | + free(dom); | ||
| 98 | + return 0; | ||
| 99 | +} | ||
| 100 | + | ||
| 101 | +int xenstore_domain_init2(int xenstore_port, int xenstore_mfn, | ||
| 102 | + int console_port, int console_mfn) | ||
| 103 | +{ | ||
| 104 | + char *dom; | ||
| 105 | + | ||
| 106 | + dom = xs_get_domain_path(xenstore, xen_domid); | ||
| 107 | + | ||
| 108 | + /* signal new domain */ | ||
| 109 | + xs_introduce_domain(xenstore, | ||
| 110 | + xen_domid, | ||
| 111 | + xenstore_mfn, | ||
| 112 | + xenstore_port); | ||
| 113 | + | ||
| 114 | + /* xenstore */ | ||
| 115 | + xenstore_write_int(dom, "store/ring-ref", xenstore_mfn); | ||
| 116 | + xenstore_write_int(dom, "store/port", xenstore_port); | ||
| 117 | + | ||
| 118 | + /* console */ | ||
| 119 | + xenstore_write_str(dom, "console/type", "ioemu"); | ||
| 120 | + xenstore_write_int(dom, "console/limit", 128 * 1024); | ||
| 121 | + xenstore_write_int(dom, "console/ring-ref", console_mfn); | ||
| 122 | + xenstore_write_int(dom, "console/port", console_port); | ||
| 123 | + xen_config_dev_console(0); | ||
| 124 | + | ||
| 125 | + free(dom); | ||
| 126 | + return 0; | ||
| 127 | +} | ||
| 128 | + | ||
| 129 | +/* ------------------------------------------------------------- */ | ||
| 130 | + | ||
| 131 | +static QEMUTimer *xen_poll; | ||
| 132 | + | ||
| 133 | +/* check domain state once per second */ | ||
| 134 | +static void xen_domain_poll(void *opaque) | ||
| 135 | +{ | ||
| 136 | + struct xc_dominfo info; | ||
| 137 | + int rc; | ||
| 138 | + | ||
| 139 | + rc = xc_domain_getinfo(xen_xc, xen_domid, 1, &info); | ||
| 140 | + if ((1 != rc) || (info.domid != xen_domid)) { | ||
| 141 | + qemu_log("xen: domain %d is gone\n", xen_domid); | ||
| 142 | + goto quit; | ||
| 143 | + } | ||
| 144 | + if (info.dying) { | ||
| 145 | + qemu_log("xen: domain %d is dying (%s%s)\n", xen_domid, | ||
| 146 | + info.crashed ? "crashed" : "", | ||
| 147 | + info.shutdown ? "shutdown" : ""); | ||
| 148 | + goto quit; | ||
| 149 | + } | ||
| 150 | + | ||
| 151 | + qemu_mod_timer(xen_poll, qemu_get_clock(rt_clock) + 1000); | ||
| 152 | + return; | ||
| 153 | + | ||
| 154 | +quit: | ||
| 155 | + qemu_system_shutdown_request(); | ||
| 156 | + return; | ||
| 157 | +} | ||
| 158 | + | ||
| 159 | +static void xen_domain_watcher(void) | ||
| 160 | +{ | ||
| 161 | + int qemu_running = 1; | ||
| 162 | + int fd[2], i, n, rc; | ||
| 163 | + char byte; | ||
| 164 | + | ||
| 165 | + pipe(fd); | ||
| 166 | + if (fork() != 0) | ||
| 167 | + return; /* not child */ | ||
| 168 | + | ||
| 169 | + /* close all file handles, except stdio/out/err, | ||
| 170 | + * our watch pipe and the xen interface handle */ | ||
| 171 | + n = getdtablesize(); | ||
| 172 | + for (i = 3; i < n; i++) { | ||
| 173 | + if (i == fd[0]) | ||
| 174 | + continue; | ||
| 175 | + if (i == xen_xc) | ||
| 176 | + continue; | ||
| 177 | + close(i); | ||
| 178 | + } | ||
| 179 | + | ||
| 180 | + /* ignore term signals */ | ||
| 181 | + signal(SIGINT, SIG_IGN); | ||
| 182 | + signal(SIGTERM, SIG_IGN); | ||
| 183 | + | ||
| 184 | + /* wait for qemu exiting */ | ||
| 185 | + while (qemu_running) { | ||
| 186 | + rc = read(fd[0], &byte, 1); | ||
| 187 | + switch (rc) { | ||
| 188 | + case -1: | ||
| 189 | + if (EINTR == errno) | ||
| 190 | + continue; | ||
| 191 | + qemu_log("%s: Huh? read error: %s\n", __FUNCTION__, strerror(errno)); | ||
| 192 | + qemu_running = 0; | ||
| 193 | + break; | ||
| 194 | + case 0: | ||
| 195 | + /* EOF -> qemu exited */ | ||
| 196 | + qemu_running = 0; | ||
| 197 | + break; | ||
| 198 | + default: | ||
| 199 | + qemu_log("%s: Huh? data on the watch pipe?\n", __FUNCTION__); | ||
| 200 | + break; | ||
| 201 | + } | ||
| 202 | + } | ||
| 203 | + | ||
| 204 | + /* cleanup */ | ||
| 205 | + qemu_log("%s: destroy domain %d\n", __FUNCTION__, xen_domid); | ||
| 206 | + xc_domain_destroy(xen_xc, xen_domid); | ||
| 207 | + _exit(0); | ||
| 208 | +} | ||
| 209 | + | ||
| 210 | +/* normal cleanup */ | ||
| 211 | +static void xen_domain_cleanup(void) | ||
| 212 | +{ | ||
| 213 | + char *dom; | ||
| 214 | + | ||
| 215 | + dom = xs_get_domain_path(xenstore, xen_domid); | ||
| 216 | + if (dom) { | ||
| 217 | + xs_rm(xenstore, 0, dom); | ||
| 218 | + free(dom); | ||
| 219 | + } | ||
| 220 | + xs_release_domain(xenstore, xen_domid); | ||
| 221 | +} | ||
| 222 | + | ||
| 223 | +int xen_domain_build_pv(const char *kernel, const char *ramdisk, | ||
| 224 | + const char *cmdline) | ||
| 225 | +{ | ||
| 226 | + uint32_t ssidref = 0; | ||
| 227 | + uint32_t flags = 0; | ||
| 228 | + xen_domain_handle_t uuid; | ||
| 229 | + unsigned int xenstore_port = 0, console_port = 0; | ||
| 230 | + unsigned long xenstore_mfn = 0, console_mfn = 0; | ||
| 231 | + int rc; | ||
| 232 | + | ||
| 233 | + memcpy(uuid, qemu_uuid, sizeof(uuid)); | ||
| 234 | + rc = xc_domain_create(xen_xc, ssidref, uuid, flags, &xen_domid); | ||
| 235 | + if (rc < 0) { | ||
| 236 | + fprintf(stderr, "xen: xc_domain_create() failed\n"); | ||
| 237 | + goto err; | ||
| 238 | + } | ||
| 239 | + qemu_log("xen: created domain %d\n", xen_domid); | ||
| 240 | + atexit(xen_domain_cleanup); | ||
| 241 | + xen_domain_watcher(); | ||
| 242 | + | ||
| 243 | + xenstore_domain_init1(kernel, ramdisk, cmdline); | ||
| 244 | + | ||
| 245 | + rc = xc_domain_max_vcpus(xen_xc, xen_domid, smp_cpus); | ||
| 246 | + if (rc < 0) { | ||
| 247 | + fprintf(stderr, "xen: xc_domain_max_vcpus() failed\n"); | ||
| 248 | + goto err; | ||
| 249 | + } | ||
| 250 | + | ||
| 251 | +#if 0 | ||
| 252 | + rc = xc_domain_setcpuweight(xen_xc, xen_domid, 256); | ||
| 253 | + if (rc < 0) { | ||
| 254 | + fprintf(stderr, "xen: xc_domain_setcpuweight() failed\n"); | ||
| 255 | + goto err; | ||
| 256 | + } | ||
| 257 | +#endif | ||
| 258 | + | ||
| 259 | + rc = xc_domain_setmaxmem(xen_xc, xen_domid, ram_size >> 10); | ||
| 260 | + if (rc < 0) { | ||
| 261 | + fprintf(stderr, "xen: xc_domain_setmaxmem() failed\n"); | ||
| 262 | + goto err; | ||
| 263 | + } | ||
| 264 | + | ||
| 265 | + xenstore_port = xc_evtchn_alloc_unbound(xen_xc, xen_domid, 0); | ||
| 266 | + console_port = xc_evtchn_alloc_unbound(xen_xc, xen_domid, 0); | ||
| 267 | + | ||
| 268 | + rc = xc_linux_build(xen_xc, xen_domid, ram_size >> 20, | ||
| 269 | + kernel, ramdisk, cmdline, | ||
| 270 | + 0, flags, | ||
| 271 | + xenstore_port, &xenstore_mfn, | ||
| 272 | + console_port, &console_mfn); | ||
| 273 | + if (rc < 0) { | ||
| 274 | + fprintf(stderr, "xen: xc_linux_build() failed\n"); | ||
| 275 | + goto err; | ||
| 276 | + } | ||
| 277 | + | ||
| 278 | + xenstore_domain_init2(xenstore_port, xenstore_mfn, | ||
| 279 | + console_port, console_mfn); | ||
| 280 | + | ||
| 281 | + qemu_log("xen: unpausing domain %d\n", xen_domid); | ||
| 282 | + rc = xc_domain_unpause(xen_xc, xen_domid); | ||
| 283 | + if (rc < 0) { | ||
| 284 | + fprintf(stderr, "xen: xc_domain_unpause() failed\n"); | ||
| 285 | + goto err; | ||
| 286 | + } | ||
| 287 | + | ||
| 288 | + xen_poll = qemu_new_timer(rt_clock, xen_domain_poll, NULL); | ||
| 289 | + qemu_mod_timer(xen_poll, qemu_get_clock(rt_clock) + 1000); | ||
| 290 | + return 0; | ||
| 291 | + | ||
| 292 | +err: | ||
| 293 | + return -1; | ||
| 294 | +} |
hw/xen_domainbuild.h
0 → 100644
| 1 | +#ifndef QEMU_HW_XEN_DOMAINBUILD_H | ||
| 2 | +#define QEMU_HW_XEN_DOMAINBUILD_H 1 | ||
| 3 | + | ||
| 4 | +#include "xen_common.h" | ||
| 5 | + | ||
| 6 | +int xenstore_domain_init1(const char *kernel, const char *ramdisk, | ||
| 7 | + const char *cmdline); | ||
| 8 | +int xenstore_domain_init2(int xenstore_port, int xenstore_mfn, | ||
| 9 | + int console_port, int console_mfn); | ||
| 10 | +int xen_domain_build_pv(const char *kernel, const char *ramdisk, | ||
| 11 | + const char *cmdline); | ||
| 12 | + | ||
| 13 | +#endif /* QEMU_HW_XEN_DOMAINBUILD_H */ |
hw/xen_machine_pv.c
| @@ -27,6 +27,7 @@ | @@ -27,6 +27,7 @@ | ||
| 27 | #include "sysemu.h" | 27 | #include "sysemu.h" |
| 28 | #include "boards.h" | 28 | #include "boards.h" |
| 29 | #include "xen_backend.h" | 29 | #include "xen_backend.h" |
| 30 | +#include "xen_domainbuild.h" | ||
| 30 | 31 | ||
| 31 | uint32_t xen_domid; | 32 | uint32_t xen_domid; |
| 32 | enum xen_mode xen_mode = XEN_EMULATE; | 33 | enum xen_mode xen_mode = XEN_EMULATE; |
| @@ -57,6 +58,24 @@ static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size, | @@ -57,6 +58,24 @@ static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size, | ||
| 57 | fprintf(stderr, "%s: xen backend core setup failed\n", __FUNCTION__); | 58 | fprintf(stderr, "%s: xen backend core setup failed\n", __FUNCTION__); |
| 58 | exit(1); | 59 | exit(1); |
| 59 | } | 60 | } |
| 61 | + | ||
| 62 | + switch (xen_mode) { | ||
| 63 | + case XEN_ATTACH: | ||
| 64 | + /* nothing to do, xend handles everything */ | ||
| 65 | + break; | ||
| 66 | + case XEN_CREATE: | ||
| 67 | + if (xen_domain_build_pv(kernel_filename, initrd_filename, | ||
| 68 | + kernel_cmdline) < 0) { | ||
| 69 | + fprintf(stderr, "xen pv domain creation failed\n"); | ||
| 70 | + exit(1); | ||
| 71 | + } | ||
| 72 | + break; | ||
| 73 | + case XEN_EMULATE: | ||
| 74 | + fprintf(stderr, "xen emulation not implemented (yet)\n"); | ||
| 75 | + exit(1); | ||
| 76 | + break; | ||
| 77 | + } | ||
| 78 | + | ||
| 60 | xen_be_register("console", &xen_console_ops); | 79 | xen_be_register("console", &xen_console_ops); |
| 61 | xen_be_register("vkbd", &xen_kbdmouse_ops); | 80 | xen_be_register("vkbd", &xen_kbdmouse_ops); |
| 62 | xen_be_register("vfb", &xen_framebuffer_ops); | 81 | xen_be_register("vfb", &xen_framebuffer_ops); |