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 | 561 | endif |
| 562 | 562 | |
| 563 | 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 | 565 | XEN_OBJS += xen_console.o xenfb.o xen_disk.o xen_nic.o |
| 566 | 566 | ifeq ($(CONFIG_XEN), yes) |
| 567 | 567 | OBJS += $(XEN_OBJS) | ... | ... |
configure
| ... | ... | @@ -1634,7 +1634,7 @@ if test "$bluez" = "yes" ; then |
| 1634 | 1634 | echo "#define CONFIG_BLUEZ 1" >> $config_h |
| 1635 | 1635 | fi |
| 1636 | 1636 | if test "$xen" = "yes" ; then |
| 1637 | - echo "XEN_LIBS=-lxenstore -lxenctrl" >> $config_mak | |
| 1637 | + echo "XEN_LIBS=-lxenstore -lxenctrl -lxenguest" >> $config_mak | |
| 1638 | 1638 | fi |
| 1639 | 1639 | if test "$aio" = "yes" ; then |
| 1640 | 1640 | echo "#define CONFIG_AIO 1" >> $config_h | ... | ... |
hw/xen_backend.h
| ... | ... | @@ -100,5 +100,8 @@ void xen_init_display(int domid); |
| 100 | 100 | void xen_config_cleanup(void); |
| 101 | 101 | int xen_config_dev_blk(DriveInfo *disk); |
| 102 | 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 | 107 | #endif /* QEMU_HW_XEN_BACKEND_H */ | ... | ... |
hw/xen_devconfig.c
| ... | ... | @@ -140,3 +140,32 @@ int xen_config_dev_nic(NICInfo *nic) |
| 140 | 140 | /* common stuff */ |
| 141 | 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 | 27 | #include "sysemu.h" |
| 28 | 28 | #include "boards.h" |
| 29 | 29 | #include "xen_backend.h" |
| 30 | +#include "xen_domainbuild.h" | |
| 30 | 31 | |
| 31 | 32 | uint32_t xen_domid; |
| 32 | 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 | 58 | fprintf(stderr, "%s: xen backend core setup failed\n", __FUNCTION__); |
| 58 | 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 | 79 | xen_be_register("console", &xen_console_ops); |
| 61 | 80 | xen_be_register("vkbd", &xen_kbdmouse_ops); |
| 62 | 81 | xen_be_register("vfb", &xen_framebuffer_ops); | ... | ... |