diff --git a/qubes/ext/pci.py b/qubes/ext/pci.py index c3938c7e4..d7e810c51 100644 --- a/qubes/ext/pci.py +++ b/qubes/ext/pci.py @@ -100,14 +100,7 @@ def pcidev_class(dev_xmldesc): def pcidev_interface(dev_xmldesc): - sysfs_path = dev_xmldesc.findtext("path") - assert sysfs_path - try: - with open(sysfs_path + "/class", encoding="ascii") as f_class: - class_id = f_class.read().strip() - except OSError: - return "000000" - + class_id = dev_xmldesc.xpath("capability[@type='pci']/class/text()")[0] if class_id.startswith("0x"): class_id = class_id[2:] return class_id diff --git a/qubes/tests/vm/qubesvm.py b/qubes/tests/vm/qubesvm.py index d6a0fbc4d..42b7fec12 100644 --- a/qubes/tests/vm/qubesvm.py +++ b/qubes/tests/vm/qubesvm.py @@ -634,6 +634,87 @@ def test_262_kernelopts(self): ) self.assertPropertyValue(vm, "kernelopts", "", "", "") + @unittest.mock.patch.dict( + qubes.config.system_path, {"qubes_kernels_base_dir": "/tmp"} + ) + def test_263_kernelopts_common(self): + d = tempfile.mkdtemp(prefix="/tmp/") + self.addCleanup(shutil.rmtree, d) + open(d + "/vmlinuz", "w").close() + open(d + "/initramfs", "w").close() + with open(d + "/default-kernelopts-common.txt", "w") as f: + f.write("some default root=/dev/sda nomodeset other") + vm = self.get_vm() + vm.kernel = os.path.basename(d) + uuid_str = str(vm.uuid).replace("-", "") + self.assertPropertyDefaultValue( + vm, + "kernelopts_common", + f"systemd.machine_id={uuid_str} " + "some default root=/dev/sda nomodeset other", + ) + vm.features["no-nomodeset"] = "1" + vm.features["os"] = "non-Linux" + self.assertPropertyDefaultValue( + vm, "kernelopts_common", "some default root=/dev/sda other" + ) + + @unittest.mock.patch.dict( + qubes.config.system_path, {"qubes_kernels_base_dir": "/tmp"} + ) + def test_264_kernelopts_common_gpu(self): + d = tempfile.mkdtemp(prefix="/tmp/") + self.addCleanup(shutil.rmtree, d) + open(d + "/vmlinuz", "w").close() + open(d + "/initramfs", "w").close() + with open(d + "/default-kernelopts-common.txt", "w") as f: + f.write("some default root=/dev/sda nomodeset other") + # required for PCI devices listing + self.app.vmm.offline_mode = False + hostdev_details = unittest.mock.Mock( + **{ + "XMLDesc.return_value": """ + + pci_0000_00_02_0 + /sys/devices/pci0000:00/0000:00:02.0 + computer + + 0x030000 + 0 + 0 + 2 + 0 + Unknown + Intel Corporation + + """, + } + ) + self.app.vmm.libvirt_mock = unittest.mock.Mock( + **{"nodeDeviceLookupByName.return_value": hostdev_details} + ) + vm = self.get_vm() + assignment = qubes.device_protocol.DeviceAssignment( + qubes.device_protocol.VirtualDevice( + qubes.device_protocol.Port( + backend_domain=vm, # this is violation of API, + # but for PCI the argument is unused + port_id="00_02.0", + devclass="pci", + ) + ), + mode="required", + ) + vm.devices["pci"]._set.add(assignment) + vm.kernel = os.path.basename(d) + uuid_str = str(vm.uuid).replace("-", "") + self.assertPropertyDefaultValue( + vm, + "kernelopts_common", + f"systemd.machine_id={uuid_str} " + "some default root=/dev/sda other", + ) + def test_270_qrexec_timeout(self): vm = self.get_vm() self.assertPropertyDefaultValue(vm, "qrexec_timeout", 60) @@ -969,6 +1050,73 @@ def test_600_libvirt_xml_hvm(self): lxml.etree.XML(libvirt_xml), lxml.etree.XML(expected) ) + def test_600_libvirt_xml_hvm_with_guivm(self): + my_uuid = "7db78950-c467-4863-94d1-af59806384ea" + expected = """ + test-inst-test + 7db78950-c467-4863-94d1-af59806384ea + 400 + 400 + 2 + + + + + + + + + hvm + + hvmloader + + + + + + + + + + + destroy + destroy + destroy + + + + + + + + + + + + """ + guivm = self.get_vm(name="guivm") + p = unittest.mock.patch.object(guivm, "is_running", lambda: True) + p.start() + self.addCleanup(p.stop) + vm = self.get_vm(uuid=my_uuid) + vm.netvm = None + vm.virt_mode = "hvm" + vm.guivm = guivm + vm.debug = True + libvirt_xml = vm.create_config_file() + self.assertXMLEqual( + lxml.etree.XML(libvirt_xml), lxml.etree.XML(expected) + ) + def test_600_libvirt_xml_hvm_dom0_kernel(self): my_uuid = "7db78950-c467-4863-94d1-af59806384ea" expected = f""" diff --git a/qubes/vm/qubesvm.py b/qubes/vm/qubesvm.py index 4375d7121..c9c159c78 100644 --- a/qubes/vm/qubesvm.py +++ b/qubes/vm/qubesvm.py @@ -45,7 +45,7 @@ import qubes.vm import qubes.vm.adminvm import qubes.vm.mix.net -from qubes.device_protocol import DeviceInterface +from qubes.device_protocol import DeviceInterface, DeviceCategory qmemman_present = False try: @@ -2593,9 +2593,25 @@ def kernelopts_common(self): ) if os.path.exists(kernelopts_path): with open(kernelopts_path, encoding="ascii") as f_kernelopts: - return base_kernelopts + f_kernelopts.read().rstrip("\n\r") + result = base_kernelopts + f_kernelopts.read().rstrip("\n\r") else: - return base_kernelopts + qubes.config.defaults["kernelopts_common"] + result = ( + base_kernelopts + qubes.config.defaults["kernelopts_common"] + ) + no_nomodeset = self.features.check_with_template("no-nomodeset", None) + if no_nomodeset is None: + # automatically skip nomodeset if a GPU is attached + for dev_ass in self.devices["pci"].get_assigned_devices(): + for intf in dev_ass.device.interfaces: + if intf.category == DeviceCategory.Display: + no_nomodeset = True + break + if no_nomodeset: + result = " ".join( + opt for opt in result.split(" ") if opt != "nomodeset" + ) + + return result # # helper methods diff --git a/templates/libvirt/xen.xml b/templates/libvirt/xen.xml index 7d8974cdd..3d18136e5 100644 --- a/templates/libvirt/xen.xml +++ b/templates/libvirt/xen.xml @@ -224,7 +224,15 @@ {% if vm.features.check_with_template('linux-stubdom', True) %} {# TODO only add qubes gui if gui-agent is not installed in HVM #} - + {% endif %} {% endif %} {% endif %}