diff --git a/doc/configuration.rst b/doc/configuration.rst index 4a3ffea66..ed606860c 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -2693,6 +2693,15 @@ Binds to: dtb: 'dtb' nic: 'user' + QEMUDriver: + qemu_bin: qemu-i386 + machine: pc + cpu: core2duo + memory: 2G + extra_args: "-accel kvm" + nic: user,model=virtio-net-pci + disk: disk-image + .. code-block:: yaml tools: @@ -2709,8 +2718,8 @@ Implements: Arguments: - qemu_bin (str): reference to the tools key for the QEMU binary - - machine (str): QEMU machine type - - cpu (str): QEMU cpu type + - machine (str): optional, QEMU machine type + - cpu (str): optional, QEMU cpu type - memory (str): QEMU memory size (ends with M or G) - extra_args (str): optional, extra QEMU arguments, they are passed directly to the QEMU binary - boot_args (str): optional, additional kernel boot argument diff --git a/labgrid/driver/qemudriver.py b/labgrid/driver/qemudriver.py index a9dc0829d..652833f66 100644 --- a/labgrid/driver/qemudriver.py +++ b/labgrid/driver/qemudriver.py @@ -1,5 +1,6 @@ """The QEMUDriver implements a driver to use a QEMU target""" import atexit +import os import select import shlex import shutil @@ -32,10 +33,11 @@ class QEMUDriver(ConsoleExpectMixin, Driver, PowerProtocol, ConsoleProtocol): Args: qemu_bin (str): reference to the tools key for the QEMU binary - machine (str): QEMU machine type - cpu (str): QEMU cpu type memory (str): QEMU memory size (ends with M or G) - extra_args (str): optional, extra QEMU arguments passed directly to the QEMU binary + extra_args (str): optional, extra QEMU arguments passed directly to the + QEMU binary + cpu (str): optional, QEMU cpu type + machine (str): optional, QEMU machine type boot_args (str): optional, additional kernel boot argument kernel (str): optional, reference to the images key for the kernel disk (str): optional, reference to the images key for the disk image @@ -52,11 +54,15 @@ class QEMUDriver(ConsoleExpectMixin, Driver, PowerProtocol, ConsoleProtocol): nic (str): optional, configuration string to pass to QEMU to create a network interface """ qemu_bin = attr.ib(validator=attr.validators.instance_of(str)) - machine = attr.ib(validator=attr.validators.instance_of(str)) - cpu = attr.ib(validator=attr.validators.instance_of(str)) memory = attr.ib(validator=attr.validators.instance_of(str)) extra_args = attr.ib( - default='', + default=None, + validator=attr.validators.optional(attr.validators.instance_of(str))) + cpu = attr.ib( + default=None, + validator=attr.validators.optional(attr.validators.instance_of(str))) + machine = attr.ib( + default=None, validator=attr.validators.optional(attr.validators.instance_of(str))) boot_args = attr.ib( default=None, @@ -103,6 +109,7 @@ def __attrs_post_init__(self): self._tempdir = None self._socket = None self._clientsocket = None + self._sockpath = None self._forwarded_ports = {} atexit.register(self._atexit) @@ -127,6 +134,17 @@ def get_qemu_version(self, qemu_bin): return (int(m.group('major')), int(m.group('minor')), int(m.group('micro'))) + def set_bios(self, bios): + """Set the filename of the bios + + This can be used by strategies to set the bios filename, overriding the + value provided in the environment. + + Args: + bios (str): New bios filename + """ + self.bios = bios + def get_qemu_base_args(self): """Returns the base command line used for Qemu without the options related to QMP. These options can be used to start an interactive @@ -161,7 +179,7 @@ def get_qemu_base_args(self): cmd.append( f"if=sd,format={disk_format},file={disk_path},id=mmc0{disk_opts}") boot_args.append("root=/dev/mmcblk0p1 rootfstype=ext4 rootwait") - elif self.machine in ["pc", "q35", "virt"]: + elif self.machine in ["pc", "q35", "virt", None]: cmd.append("-drive") cmd.append( f"if=virtio,format={disk_format},file={disk_path}{disk_opts}") @@ -187,17 +205,21 @@ def get_qemu_base_args(self): f"if=pflash,format=raw,file={self.target.env.config.get_image_path(self.flash)},id=nor0") # pylint: disable=line-too-long if self.bios is not None: cmd.append("-bios") - cmd.append( - self.target.env.config.get_image_path(self.bios)) - - if "-append" in shlex.split(self.extra_args): - raise ExecutionError("-append in extra_args not allowed, use boot_args instead") - - cmd.extend(shlex.split(self.extra_args)) - cmd.append("-machine") - cmd.append(self.machine) - cmd.append("-cpu") - cmd.append(self.cpu) + if os.path.exists(self.bios): + cmd.append(self.bios) + else: + cmd.append(self.target.env.config.get_image_path(self.bios)) + if self.extra_args: + if "-append" in shlex.split(self.extra_args): + raise ExecutionError("-append in extra_args not allowed, use boot_args instead") + + cmd.extend(shlex.split(self.extra_args)) + if self.machine: + cmd.append("-machine") + cmd.append(self.machine) + if self.cpu: + cmd.append("-cpu") + cmd.append(self.cpu) cmd.append("-m") cmd.append(self.memory) if self.display == "none": @@ -231,22 +253,11 @@ def get_qemu_base_args(self): def on_activate(self): self._tempdir = tempfile.mkdtemp(prefix="labgrid-qemu-tmp-") - sockpath = f"{self._tempdir}/serialrw" + self._sockpath = f"{self._tempdir}/serialrw" self._socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - self._socket.bind(sockpath) + self._socket.bind(self._sockpath) self._socket.listen(0) - self._cmd = self.get_qemu_base_args() - - self._cmd.append("-S") - self._cmd.append("-qmp") - self._cmd.append("stdio") - - self._cmd.append("-chardev") - self._cmd.append(f"socket,id=serialsocket,path={sockpath}") - self._cmd.append("-serial") - self._cmd.append("chardev:serialsocket") - def on_deactivate(self): if self.status: self.off() @@ -255,6 +266,7 @@ def on_deactivate(self): self._clientsocket = None self._socket.close() self._socket = None + self._sockpath = None shutil.rmtree(self._tempdir) @step() @@ -263,6 +275,17 @@ def on(self): afterwards start the emulator using a QMP Command""" if self.status: return + self._cmd = self.get_qemu_base_args() + + self._cmd.append("-S") + self._cmd.append("-qmp") + self._cmd.append("stdio") + + self._cmd.append("-chardev") + self._cmd.append(f"socket,id=serialsocket,path={self._sockpath}") + self._cmd.append("-serial") + self._cmd.append("chardev:serialsocket") + self.logger.debug("Starting with: %s", self._cmd) self._child = subprocess.Popen( self._cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) @@ -333,6 +356,8 @@ def remove_port_forward(self, proto, local_address, local_port): ) def _read(self, size=1, timeout=10, max_size=None): + if not self._clientsocket: + raise ExecutionError('QEMU has not been started') ready, _, _ = select.select([self._clientsocket], [], [], timeout) if ready: # Collect some more data