From 0c811322c5d8f2c1bcbfd8ca38b88b18fe4532fb Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Wed, 22 Feb 2012 17:54:58 +0100 Subject: [PATCH 01/11] dom0: vchan version for stubdom --- vchan/Makefile.stubdom | 36 ++++++++++++++++ vchan/init.c | 93 ++++++++++++++++++++++++++++++++++++++---- vchan/io.c | 19 +++++++++ vchan/libvchan.h | 7 ++++ 4 files changed, 146 insertions(+), 9 deletions(-) create mode 100644 vchan/Makefile.stubdom diff --git a/vchan/Makefile.stubdom b/vchan/Makefile.stubdom new file mode 100644 index 00000000..af716cef --- /dev/null +++ b/vchan/Makefile.stubdom @@ -0,0 +1,36 @@ +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2012 Marek Marczykowski +# +# 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; either version 2 +# of the License, or (at your option) any later version. +# +# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# + +# Assume it is placed as xen-root/tools/vchan + +XEN_ROOT = ../.. +include $(XEN_ROOT)/tools/Rules.mk + +CFLAGS+=-Wall -I$(XEN_ROOT)/tools/libxc -DCONFIG_STUBDOM +all: libvchan.a + +libvchan.a: init.o io.o + $(AR) rc $@ $^ + +clean: + rm -f *.o *so *~ client server node node-select + + diff --git a/vchan/init.c b/vchan/init.c index 30cc2001..d0127939 100644 --- a/vchan/init.c +++ b/vchan/init.c @@ -19,6 +19,9 @@ * */ +#include +#include +#include #include #include #include @@ -29,25 +32,38 @@ #include #include #include +#include #include "libvchan.h" +#ifndef CONFIG_STUBDOM #include "../u2mfn/u2mfnlib.h" +#endif static int ring_init(struct libvchan *ctrl) { - int u2mfn = open("/proc/u2mfn", O_RDONLY); int mfn; struct vchan_interface *ring; +#ifdef CONFIG_STUBDOM + ring = (struct vchan_interface *) memalign(XC_PAGE_SIZE, sizeof(*ring)); + + if (!ring) + return -1; + + + mfn = virtual_to_mfn(ring); +#else + int u2mfn = open("/proc/u2mfn", O_RDONLY); ring = (struct vchan_interface *) u2mfn_alloc_kpage (); - + if (ring == MAP_FAILED) return -1; - ctrl->ring = ring; if (u2mfn_get_last_mfn (&mfn) < 0) return -1; - - ctrl->ring_ref = mfn; close(u2mfn); +#endif + + ctrl->ring = ring; + ctrl->ring_ref = mfn; ring->cons_in = ring->prod_in = ring->cons_out = ring->prod_out = 0; ring->server_closed = ring->client_closed = 0; @@ -65,13 +81,18 @@ static int server_interface_init(struct libvchan *ctrl, int devno) struct xs_handle *xs; char buf[64]; char ref[16]; + /* XXX temp hack begin */ + char *domid_s; + int domid = 0; + unsigned int len; + /* XXX temp hack end */ #ifdef XENCTRL_HAS_XC_INTERFACE xc_evtchn *evfd; #else int evfd; #endif evtchn_port_or_error_t port; - xs = xs_domain_open(); + xs = xs_daemon_open(); if (!xs) { return ret; } @@ -90,20 +111,41 @@ static int server_interface_init(struct libvchan *ctrl, int devno) if (port < 0) goto fail2; ctrl->evport = port; + ctrl->devno = devno; + + // stubdom debug HACK XXX + domid_s = xs_read(xs, 0, "domid", &len); + if (domid_s) + domid = atoi(domid_s); + snprintf(ref, sizeof ref, "%d", ctrl->ring_ref); snprintf(buf, sizeof buf, "device/vchan/%d/ring-ref", devno); if (!xs_write(xs, 0, buf, ref, strlen(ref))) +#ifdef CONFIG_STUBDOM + // TEMP HACK XXX FIXME goto fail2; + fprintf(stderr, "xenstore-write /local/domain/%d/%s %s\n", domid, buf, ref); +#else goto fail2; +#endif snprintf(ref, sizeof ref, "%d", ctrl->evport); snprintf(buf, sizeof buf, "device/vchan/%d/event-channel", devno); if (!xs_write(xs, 0, buf, ref, strlen(ref))) +#ifdef CONFIG_STUBDOM + // TEMP HACK XXX FIXME goto fail2; + fprintf(stderr, "xenstore-write /local/domain/%d/%s %s\n", domid, buf, ref); +#else goto fail2; +#endif + // do not block in stubdom - libvchan_server_handle_connected will be + // called on first input +#ifndef CONFIG_STUBDOM // wait for the peer to arrive if (xc_evtchn_pending(evfd) == -1) goto fail2; xc_evtchn_unmask(ctrl->evfd, ctrl->evport); snprintf(buf, sizeof buf, "device/vchan/%d", devno); xs_rm(xs, 0, buf); +#endif ret = 0; fail2: @@ -129,8 +171,8 @@ static int server_interface_init(struct libvchan *ctrl, int devno) ctrl->rd_ring_size = sizeof(ctrl->ring->buf_##dir2) /** - Run in AppVM (any domain). - Sleeps until the connection is established. + Run in AppVM (any domain). + Sleeps until the connection is established. (unless in stubdom) \param devno something like a well-known port. \returns NULL on failure, handle on success */ @@ -156,6 +198,39 @@ struct libvchan *libvchan_server_init(int devno) return ctrl; } +int libvchan_server_handle_connected(struct libvchan *ctrl) +{ + struct xs_handle *xs; + char buf[64]; + int ret = -1; + int libvchan_fd; + fd_set rfds; + + xs = xs_daemon_open(); + if (!xs) { + return ret; + } + // clear the pending flag + xc_evtchn_pending(ctrl->evfd); + + snprintf(buf, sizeof buf, "device/vchan/%d", ctrl->devno); + xs_rm(xs, 0, buf); + + ret = 0; + +#if 0 +fail2: + if (ret) +#ifdef XENCTRL_HAS_XC_INTERFACE + xc_evtchn_close(ctrl->evfd); +#else + close(ctrl->evfd); +#endif +#endif + xs_daemon_close(xs); + return ret; +} + /** retrieves ring-ref and event-channel numbers from xenstore (if they don't exist, return error, because nobody seems to listen); @@ -250,7 +325,7 @@ static int client_interface_init(struct libvchan *ctrl, int domain, int devno) /** Run on the client side of connection (currently, must be dom0). \returns NULL on failure (e.g. noone listening), handle on success -*/ +*/ struct libvchan *libvchan_client_init(int domain, int devno) { struct libvchan *ctrl = diff --git a/vchan/io.c b/vchan/io.c index 6b16f406..23135a25 100644 --- a/vchan/io.c +++ b/vchan/io.c @@ -22,6 +22,8 @@ #include "libvchan.h" #include #include +#include +#include /** \return How much data is immediately available for reading */ @@ -67,7 +69,24 @@ int libvchan_is_eof(struct libvchan *ctrl) int libvchan_wait(struct libvchan *ctrl) { int ret; +#ifndef CONFIG_STUBDOM ret = xc_evtchn_pending(ctrl->evfd); +#else + int vchan_fd = libvchan_fd_for_select(ctrl); + fd_set rfds; + + libvchan_prepare_to_select(ctrl); + while ((ret = xc_evtchn_pending(ctrl->evfd)) < 0) { + FD_ZERO(&rfds); + FD_SET(0, &rfds); + FD_SET(vchan_fd, &rfds); + ret = select(vchan_fd + 1, &rfds, NULL, NULL, NULL); + if (ret < 0 && errno != EINTR) { + perror("select"); + return ret; + } + } +#endif if (ret!=-1 && xc_evtchn_unmask(ctrl->evfd, ctrl->evport)) return -1; if (ret!=-1 && libvchan_is_eof(ctrl)) diff --git a/vchan/libvchan.h b/vchan/libvchan.h index 6a6025fb..3f647462 100644 --- a/vchan/libvchan.h +++ b/vchan/libvchan.h @@ -19,6 +19,9 @@ * */ +#ifndef _LIBVCHAN_H +#define _LIBVCHAN_H + #include #include typedef uint32_t VCHAN_RING_IDX; @@ -44,6 +47,7 @@ struct libvchan { int evfd; #endif int evport; + int devno; VCHAN_RING_IDX *wr_cons, *wr_prod, *rd_cons, *rd_prod; char *rd_ring, *wr_ring; int rd_ring_size, wr_ring_size; @@ -51,6 +55,7 @@ struct libvchan { }; struct libvchan *libvchan_server_init(int devno); +int libvchan_server_handle_connected(struct libvchan *ctrl); struct libvchan *libvchan_client_init(int domain, int devno); @@ -63,3 +68,5 @@ int libvchan_fd_for_select(struct libvchan *ctrl); int libvchan_is_eof(struct libvchan *ctrl); int libvchan_data_ready(struct libvchan *ctrl); int libvchan_buffer_space(struct libvchan *ctrl); + +#endif /* _LIBVCHAN_H */ From 104030b15cffd51aef78cee497c2197b3c69046a Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 24 Feb 2012 04:17:45 +0100 Subject: [PATCH 02/11] dom0/core: more generic way to specify xen config template --- dom0/qvm-core/qubes.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 333212e8..cf6e72b3 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -78,6 +78,8 @@ default_servicevm_vcpus = 1 default_kernelopts = "" default_kernelopts_pcidevs = "iommu=soft swiotlb=2048" +config_template_pv = '/usr/share/qubes/vm-template.conf' + qubes_whitelisted_appmenus = 'whitelisted-appmenus.list' dom0_update_check_interval = 6*3600 @@ -238,6 +240,8 @@ class QubesVm(object): self.firewall_conf = self.absolute_path(firewall_conf, default_firewall_conf_file) + self.config_file_template = config_template_pv + self.updateable = updateable self.label = label if label is not None else QubesVmLabels["red"] if self.dir_path is not None: @@ -804,7 +808,7 @@ class QubesVm(object): if source_template is None: source_template = self.template_vm - f_conf_template = open('/usr/share/qubes/vm-template.conf', 'r') + f_conf_template = open(self.config_file_template, 'r') conf_template = f_conf_template.read() f_conf_template.close() From db4a96dfea374c0cf3134caa085ea32aeaafa966 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 24 Feb 2012 04:18:45 +0100 Subject: [PATCH 03/11] dom0/core: move qrexec start to separate function --- dom0/qvm-core/qubes.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index cf6e72b3..f0257f89 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -1153,6 +1153,14 @@ class QubesVm(object): if notify_function is not None: notify_function("error", "ERROR: Cannot start the Qubes Clipboard Notifier!") + def start_qrexec_daemon(self, verbose = False): + if verbose: + print >> sys.stderr, "--> Starting the qrexec daemon..." + retcode = subprocess.call ([qrexec_daemon_path, str(xid)]) + if (retcode != 0) : + self.force_shutdown() + raise OSError ("ERROR: Cannot execute qrexec_daemon!") + def start(self, debug_console = False, verbose = False, preparing_dvm = False): if dry_run: return @@ -1224,12 +1232,7 @@ class QubesVm(object): qmemman_client.close() if not preparing_dvm: - if verbose: - print >> sys.stderr, "--> Starting the qrexec daemon..." - retcode = subprocess.call ([qrexec_daemon_path, str(xid)]) - if (retcode != 0) : - self.force_shutdown() - raise OSError ("ERROR: Cannot execute qrexec_daemon!") + self.start_qrexec_daemon(verbose=verbose) if not preparing_dvm and os.path.exists('/var/run/shm.id'): self.start_guid(verbose=verbose) From 8b3a8953178249a6ef3748c7ed49252652cca9b9 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 24 Feb 2012 04:19:51 +0100 Subject: [PATCH 04/11] dom0/core: more robust XML attrs generation Still not ideal... --- dom0/qvm-core/qubes.py | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index f0257f89..7e150ef3 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -1278,26 +1278,20 @@ class QubesVm(object): attrs["qid"] = str(self.qid) attrs["name"] = self.name attrs["dir_path"] = self.dir_path - attrs["conf_file"] = self.relative_path(self.conf_file) - attrs["root_img"] = self.relative_path(self.root_img) - attrs["volatile_img"] = self.relative_path(self.volatile_img) - attrs["private_img"] = self.relative_path(self.private_img) - attrs["uses_default_netvm"] = str(self.uses_default_netvm) + # Simple paths + for prop in ['conf_file', 'root_img', 'volatile_img', 'private_img']: + if hasattr(self, prop): + attrs[prop] = self.relative_path(self.__getattribute__(prop)) + # Simple string attrs + for prop in ['memory', 'maxmem', 'pcidevs', 'vcpus', 'internal',\ + 'uses_default_kernel', 'kernel', 'uses_default_kernelopts',\ + 'kernelopts', 'services', 'updateable', 'installed_by_rpm',\ + 'uses_default_netvm' ]: + if hasattr(self, prop): + attrs[prop] = str(self.__getattribute__(prop)) attrs["netvm_qid"] = str(self.netvm_vm.qid) if self.netvm_vm is not None else "none" - attrs["installed_by_rpm"] = str(self.installed_by_rpm) attrs["template_qid"] = str(self.template_vm.qid) if self.template_vm and not self.is_updateable() else "none" - attrs["updateable"] = str(self.updateable) attrs["label"] = self.label.name - attrs["memory"] = str(self.memory) - attrs["maxmem"] = str(self.maxmem) - attrs["pcidevs"] = str(self.pcidevs) - attrs["vcpus"] = str(self.vcpus) - attrs["internal"] = str(self.internal) - attrs["uses_default_kernel"] = str(self.uses_default_kernel) - attrs["kernel"] = str(self.kernel) - attrs["uses_default_kernelopts"] = str(self.uses_default_kernelopts) - attrs["kernelopts"] = str(self.kernelopts) - attrs["services"] = str(self.services) return attrs def create_xml_element(self): From 85c5074dbe1103e9abab3a8d8b6ecc30ad092d3f Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 24 Feb 2012 04:22:14 +0100 Subject: [PATCH 05/11] dom0/core: check if object has attr before using it --- dom0/qvm-core/qubes.py | 15 +++++++++------ dom0/qvm-tools/qvm-prefs | 40 ++++++++++++++++++++++++++-------------- 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 7e150ef3..19de047f 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -778,7 +778,8 @@ class QubesVm(object): def get_config_params(self, source_template=None): args = {} args['name'] = self.name - args['kerneldir'] = self.kernels_dir + if hasattr(self, 'kernels_dir'): + args['kerneldir'] = self.kernels_dir args['vmdir'] = self.dir_path args['pcidev'] = str(self.pcidevs).strip('[]') args['mem'] = str(self.memory) @@ -794,11 +795,13 @@ class QubesVm(object): args['rootdev'] = self.get_rootdev(source_template=source_template) args['privatedev'] = "'script:file:{dir}/private.img,xvdb,w',".format(dir=self.dir_path) args['volatiledev'] = "'script:file:{dir}/volatile.img,xvdc,w',".format(dir=self.dir_path) - modulesmode='r' - if self.is_updateable() and self.kernel is None: - modulesmode='w' - args['otherdevs'] = "'script:file:{dir}/modules.img,xvdd,{mode}',".format(dir=self.kernels_dir, mode=modulesmode) - args['kernelopts'] = self.kernelopts + if hasattr(self, 'kernel'): + modulesmode='r' + if self.is_updateable() and self.kernel is None: + modulesmode='w' + args['otherdevs'] = "'script:file:{dir}/modules.img,xvdd,{mode}',".format(dir=self.kernels_dir, mode=modulesmode) + if hasattr(self, 'kernelopts'): + args['kernelopts'] = self.kernelopts return args diff --git a/dom0/qvm-tools/qvm-prefs b/dom0/qvm-tools/qvm-prefs index 3b36dd2b..c7753890 100755 --- a/dom0/qvm-tools/qvm-prefs +++ b/dom0/qvm-tools/qvm-prefs @@ -51,22 +51,29 @@ def do_list(vm): print fmt.format ("root COW img", vm.rootcow_img) if vm.template_vm is not None: print fmt.format ("root img", vm.template_vm.root_img) - print fmt.format ("root volatile img", vm.volatile_img) + if hasattr(vm, 'volatile_img'): + print fmt.format ("root volatile img", vm.volatile_img) - print fmt.format ("private img", vm.private_img) + if hasattr(vm, 'private_img'): + print fmt.format ("private img", vm.private_img) print fmt.format ("vcpus", str(vm.vcpus)) print fmt.format ("memory", vm.memory) - print fmt.format ("maxmem", vm.maxmem) - if vm.template_vm is not None: - print fmt.format ("kernel", "%s (from template)" % vm.kernel) - elif vm.uses_default_kernel: - print fmt.format ("kernel", "%s (default)" % vm.kernel) - else: - print fmt.format ("kernel", vm.kernel) - if vm.uses_default_kernelopts: - print fmt.format ("kernelopts", "%s (default)" % vm.kernelopts) - else: - print fmt.format ("kernelopts", vm.kernelopts) + if hasattr(vm, 'maxmem'): + print fmt.format ("maxmem", vm.maxmem) + + if hasattr(vm, 'kernel'): + if vm.template_vm is not None: + print fmt.format ("kernel", "%s (from template)" % vm.kernel) + elif vm.uses_default_kernel: + print fmt.format ("kernel", "%s (default)" % vm.kernel) + else: + print fmt.format ("kernel", vm.kernel) + + if hasattr(vm, 'kernelopts'): + if vm.uses_default_kernelopts: + print fmt.format ("kernelopts", "%s (default)" % vm.kernelopts) + else: + print fmt.format ("kernelopts", vm.kernelopts) def set_label(vms, vm, args): @@ -309,6 +316,10 @@ def do_set(vms, vm, property, args): print >> sys.stderr, "ERROR: Wrong property name: '{0}'".format(property) return False + if not hasattr(vm, property): + print >> sys.stderr, "ERROR: Property '{0}' not available for this VM".format(property) + return False + return properties[property](vms, vm, args) @@ -353,7 +364,8 @@ def main(): print >> sys.stderr, "You must specify the property you wish to set..." print >> sys.stderr, "Available properties:" for p in properties.keys(): - print >> sys.stderr, "--> '{0}'".format(p) + if hasattr(vm, p): + print >> sys.stderr, "--> '{0}'".format(p) exit (1) property = args[1] From 40d8ac66a3a5f1154f5ec301931f3217975c943e Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 24 Feb 2012 04:23:27 +0100 Subject: [PATCH 06/11] dom0/core: introduce QubesHVm class --- dom0/qvm-core/qubes.py | 153 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 19de047f..f1753bd7 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -21,6 +21,7 @@ # import sys +import stat import os import os.path import subprocess @@ -78,7 +79,10 @@ default_servicevm_vcpus = 1 default_kernelopts = "" default_kernelopts_pcidevs = "iommu=soft swiotlb=2048" +default_hvm_disk_size = 20*1024*1024*1024 + config_template_pv = '/usr/share/qubes/vm-template.conf' +config_template_hvm = '/usr/share/qubes/vm-template-hvm.conf' qubes_whitelisted_appmenus = 'whitelisted-appmenus.list' @@ -1978,6 +1982,127 @@ class QubesAppVm(QubesVm): def post_rename(self, old_name): self.create_appmenus(False) +class QubesHVm(QubesVm): + """ + A class that represents an HVM. A child of QubesVm. + """ + + # FIXME: logically should inherit after QubesAppVm, but none of its methods + # are useful for HVM + def __init__(self, **kwargs): + + if "dir_path" not in kwargs or kwargs["dir_path"] is None: + kwargs["dir_path"] = qubes_appvms_dir + "/" + kwargs["name"] + + super(QubesHVm, self).__init__(**kwargs) + self.updateable = True + self.config_file_template = config_template_hvm + # remove settings not used by HVM (at least for now) + self.__delattr__('kernel') + self.__delattr__('kernelopts') + self.__delattr__('uses_default_kernel') + self.__delattr__('uses_default_kernelopts') + self.__delattr__('private_img') + self.__delattr__('volatile_img') + # HVM doesn't support dynamic memory management + self.maxmem = self.memory + + self.drive = None + if 'drive' in kwargs.keys(): + self.drive = kwargs['drive'] + + @property + def type(self): + return "HVM" + + def is_appvm(self): + return True + + def create_on_disk(self, verbose, source_template = None): + if dry_run: + return + + if verbose: + print >> sys.stderr, "--> Creating directory: {0}".format(self.dir_path) + os.mkdir (self.dir_path) + + self.create_config_file() + + # create empty disk + f_root = open(self.root_img, "w") + f_root.truncate(default_hvm_disk_size) + f_root.close() + + + def get_disk_utilization_private_img(self): + return 0 + + def get_private_img_sz(self): + return 0 + + def resize_private_img(self, size): + raise NotImplementedError("HVM has no private.img") + + def get_config_params(self, source_template=None): + + params = super(QubesHVm, self).get_config_params(source_template=source_template) + + params['volatiledev'] = '' + params['privatedev'] = '' + if self.drive: + stat_res = os.stat(self.drive) + if stat.S_ISBLK(stat_res.st_mode): + params['otherdevs'] = "'phy:%s,hdc:cdrom,r'," % self.drive + else: + params['otherdevs'] = "'script:file:%s,hdc:cdrom,r'," % self.drive + else: + params['otherdevs'] = '' + return params + + def verify_files(self): + if dry_run: + return + + if not os.path.exists (self.dir_path): + raise QubesException ( + "VM directory doesn't exist: {0}".\ + format(self.dir_path)) + + if self.is_updateable() and not os.path.exists (self.root_img): + raise QubesException ( + "VM root image file doesn't exist: {0}".\ + format(self.root_img)) + + return True + + def reset_volatile_storage(self, **kwargs): + pass + + def run(self, command, **kwargs): + raise NotImplementedError("Needs qrexec agent - TODO") + + @property + def stubdom_xid(self): + if not self.is_running(): + return -1 + + return int(xs.read('', '/local/domain/%d/image/device-model-domid' % self.xid)) + + def start_guid(self, verbose = True, notify_function = None): + if verbose: + print >> sys.stderr, "--> Starting Qubes GUId..." + + retcode = subprocess.call ([qubes_guid_path, "-d", str(self.stubdom_xid), "-c", self.label.color, "-i", self.label.icon, "-l", str(self.label.index)]) + if (retcode != 0) : + raise QubesException("Cannot start qubes_guid!") + + def start_qrexec_daemon(self, **kwargs): + pass + + def get_xml_attrs(self): + attrs = super(QubesHVm, self).get_xml_attrs() + attrs["drive"] = str(self.drive) + return attrs class QubesVmCollection(dict): """ @@ -2036,6 +2161,20 @@ class QubesVmCollection(dict): self[vm.qid]=vm return vm + def add_new_hvm(self, name, label = None): + + qid = self.get_new_unused_qid() + vm = QubesHVm (qid=qid, name=name, + netvm_vm = self.get_default_netvm_vm(), + kernel = self.get_default_kernel(), + uses_default_kernel = True, + label=label) + + if not self.verify_new_vm (vm): + assert False, "Wrong VM description!" + self[vm.qid]=vm + return vm + def add_new_disposablevm(self, name, template_vm, dispid, label = None): @@ -2561,6 +2700,20 @@ class QubesVmCollection(dict): os.path.basename(sys.argv[0]), err)) return False + # And HVMs + for element in tree.findall("QubesHVm"): + try: + kwargs = self.parse_xml_element(element) + vm = QubesHVm(**kwargs) + + self[vm.qid] = vm + + self.set_netvm_dependency(element) + except (ValueError, LookupError) as err: + print("{0}: import error (QubesHVm): {1}".format( + os.path.basename(sys.argv[0]), err)) + return False + # Really finally, read in the DisposableVMs for element in tree.findall("QubesDisposableVm"): try: From 9c2161944bc6b60dcb824a744d85d605769de4ef Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 24 Feb 2012 04:24:36 +0100 Subject: [PATCH 07/11] dom0/qvm-create: support for HVM --- dom0/qvm-tools/qvm-create | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/dom0/qvm-tools/qvm-create b/dom0/qvm-tools/qvm-create index 5bd58ea1..143f753f 100755 --- a/dom0/qvm-tools/qvm-create +++ b/dom0/qvm-tools/qvm-create @@ -37,10 +37,14 @@ def main(): help="Specify the label to use for the new VM (e.g. red, yellow, green, ...)") parser.add_option ("-p", "--proxy", action="store_true", dest="proxyvm", default=False, help="Create ProxyVM") + parser.add_option ("-H", "--hvm", action="store_true", dest="hvm", default=False, + help="Create HVM (implies --standalone)") parser.add_option ("-n", "--net", action="store_true", dest="netvm", default=False, help="Create NetVM") parser.add_option ("-s", "--standalone", action="store_true", dest="standalone", default=False, help="Create standalone VM - independent of template ") + parser.add_option ("-r", "--root", dest="root", default=None, + help="Use provided root.img instead of default/empty one (file will be MOVED)") parser.add_option ("-m", "--mem", dest="mem", default=None, help="Initial memory size (in MB)") parser.add_option ("-c", "--vcpus", dest="vcpus", default=None, @@ -82,6 +86,18 @@ def main(): exit (1) label = QubesVmLabels[options.label] + if options.hvm: + # Only standalone HVMs are supported for now + options.standalone = True + + if not options.standalone and options.root is not None: + print >> sys.stderr, "root.img can be specified only for standalone VMs" + exit (1) + + if options.root is not None and not os.path.exists(options.root): + print >> sys.stderr, "File specified as root.img does not exists" + exit (1) + qvm_collection = QubesVmCollection() qvm_collection.lock_db_for_writing() qvm_collection.load() @@ -90,6 +106,7 @@ def main(): print >> sys.stderr, "A VM with the name '{0}' already exists in the system.".format(vmname) exit(1) + template_vm = None if options.template is not None: template_vm = qvm_collection.get_vm_by_name(options.template) if template_vm is None: @@ -101,7 +118,7 @@ def main(): if (options.verbose): print "--> Using TemplateVM: {0}".format(template_vm.name) - else: + elif not options.hvm: if qvm_collection.get_default_template_vm() is None: print >> sys.stderr, "No default TempleteVM defined!" exit (1) @@ -120,6 +137,8 @@ def main(): vm = qvm_collection.add_new_netvm(vmname, new_vm_template, label = label, updateable = options.standalone) elif options.proxyvm: vm = qvm_collection.add_new_proxyvm(vmname, new_vm_template, label = label, updateable = options.standalone) + elif options.hvm: + vm = qvm_collection.add_new_hvm(vmname, label = label) else: vm = qvm_collection.add_new_appvm(vmname, new_vm_template, label = label, updateable = options.standalone) @@ -134,6 +153,9 @@ def main(): try: vm.create_on_disk(verbose=options.verbose, source_template=template_vm) + if options.root: + os.unlink(vm.root_img) + os.rename(options.root, vm.root_img) except (IOError, OSError) as err: print >> sys.stderr, "ERROR: {0}".format(err) From 724c03200577e78f70fcc835d1a944ca0ccc93e7 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 24 Feb 2012 04:25:06 +0100 Subject: [PATCH 08/11] dom0/qvm-prefs: support for 'drive' property --- dom0/qvm-tools/qvm-prefs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/dom0/qvm-tools/qvm-prefs b/dom0/qvm-tools/qvm-prefs index c7753890..19ec8727 100755 --- a/dom0/qvm-tools/qvm-prefs +++ b/dom0/qvm-tools/qvm-prefs @@ -75,6 +75,9 @@ def do_list(vm): else: print fmt.format ("kernelopts", vm.kernelopts) + if hasattr(vm, 'drive'): + print fmt.format("drive", str(vm.drive)) + def set_label(vms, vm, args): if len (args) != 1: @@ -294,6 +297,13 @@ def set_name(vms, vm, args): vm.set_name(args[0]) return True +def set_drive(vms, vm, args): + if len (args) != 1: + print >> sys.stderr, "Missing new drive content (file/device)!" + return False + + vm.drive = args[0] + return True properties = { "updateable": set_updateable, @@ -308,6 +318,7 @@ properties = { "vcpus" : set_vcpus, "kernelopts": set_kernelopts, "name": set_name, + "drive": set_drive, } From 8f27fd49c71de0a84c3b369480776e2bca93af82 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 24 Feb 2012 04:25:27 +0100 Subject: [PATCH 09/11] dom0/qvm-start: starting with drive connected temporarily --- dom0/qvm-tools/qvm-start | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/dom0/qvm-tools/qvm-start b/dom0/qvm-tools/qvm-start index 1211d265..5ec3c1c5 100755 --- a/dom0/qvm-tools/qvm-start +++ b/dom0/qvm-tools/qvm-start @@ -37,6 +37,8 @@ def main(): help="Do not start the GUId (ignored)") parser.add_option ("--console", action="store_true", dest="debug_console", default=False, help="Attach debugging console to the newly started VM") + parser.add_option ("--drive", dest="drive", default=None, + help="Temporarily attach specified drive as CD/DVD") parser.add_option ("--dvm", action="store_true", dest="preparing_dvm", default=False, help="Do actions necessary when preparing DVM image") @@ -55,6 +57,13 @@ def main(): print >> sys.stderr, "A VM with the name '{0}' does not exist in the system.".format(vmname) exit(1) + if options.drive: + if hasattr(vm, 'drive'): + vm.drive = options.drive + else: + print >> sys.stderr, "This VM does not support attaching drives" + exit (1) + try: vm.verify_files() xid = vm.start(debug_console=options.debug_console, verbose=options.verbose, preparing_dvm=options.preparing_dvm) From 1c4854ac71e78b424c0c3f4040b065cabc18dbf6 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 24 Feb 2012 04:26:01 +0100 Subject: [PATCH 10/11] dom0/core: config template for HVM --- dom0/misc/vm-template-hvm.conf | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 dom0/misc/vm-template-hvm.conf diff --git a/dom0/misc/vm-template-hvm.conf b/dom0/misc/vm-template-hvm.conf new file mode 100644 index 00000000..44b31fb5 --- /dev/null +++ b/dom0/misc/vm-template-hvm.conf @@ -0,0 +1,32 @@ +# +# This is a Xen VM config file for Qubes VM +# DO NOT EDIT - autogenerated by qubes tools +# + +name = "{name}" + +builder='hvm' +memory={mem} +viridian=1 +kernel='hvmloader' +#acpi=1 +#apic=1 +boot='dca' +device_model='stubdom-dm' +#pae=1 +usbdevice='tablet' +sdl=0 +vnc=0 +disk = [ {rootdev} + {otherdevs} + ] +vif = [ {netdev} ] +pci = [ {pcidev} ] +vcpus = {vcpus} + +#tsc_mode = 2 +#xen_extended_power_mgmt=0 + +on_poweroff = 'destroy' +on_reboot = 'destroy' +on_crash = 'destroy' From 3ad50b58e70b52b716bd7043f5f424d81b8d40ef Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 24 Feb 2012 04:53:15 +0100 Subject: [PATCH 11/11] dom0/spec: include HVM config template in rpm --- rpm_spec/core-dom0.spec | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rpm_spec/core-dom0.spec b/rpm_spec/core-dom0.spec index 06bbbc91..396db1b6 100644 --- a/rpm_spec/core-dom0.spec +++ b/rpm_spec/core-dom0.spec @@ -151,6 +151,7 @@ cp misc/qubes-vm.directory.template $RPM_BUILD_ROOT/usr/share/qubes/ cp misc/qubes-templatevm.directory.template $RPM_BUILD_ROOT/usr/share/qubes/ cp misc/qubes-appmenu-select.desktop $RPM_BUILD_ROOT/usr/share/qubes/ cp misc/vm-template.conf $RPM_BUILD_ROOT/usr/share/qubes/ +cp misc/vm-template-hvm.conf $RPM_BUILD_ROOT/usr/share/qubes/ mkdir -p $RPM_BUILD_ROOT/usr/bin cp ../network/qubes_setup_dnat_to_ns $RPM_BUILD_ROOT/usr/lib/qubes @@ -344,6 +345,7 @@ fi /usr/share/qubes/qubes-templatevm.directory.template /usr/share/qubes/qubes-appmenu-select.desktop /usr/share/qubes/vm-template.conf +/usr/share/qubes/vm-template-hvm.conf /usr/lib/qubes/qubes_setup_dnat_to_ns /usr/lib/qubes/qubes_fix_nm_conf.sh /etc/dhclient.d/qubes_setup_dnat_to_ns.sh