Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 1 addition & 8 deletions qubes/ext/pci.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
148 changes: 148 additions & 0 deletions qubes/tests/vm/qubesvm.py
Original file line number Diff line number Diff line change
Expand Up @@ -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": """
<device>
<name>pci_0000_00_02_0</name>
<path>/sys/devices/pci0000:00/0000:00:02.0</path>
<parent>computer</parent>
<capability type='pci'>
<class>0x030000</class>
<domain>0</domain>
<bus>0</bus>
<slot>2</slot>
<function>0</function>
<product id='0x0000'>Unknown</product>
<vendor id='0x8086'>Intel Corporation</vendor>
</capability>
</device>""",
}
)
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)
Expand Down Expand Up @@ -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 = """<domain type="xen">
<name>test-inst-test</name>
<uuid>7db78950-c467-4863-94d1-af59806384ea</uuid>
<memory unit="MiB">400</memory>
<currentMemory unit="MiB">400</currentMemory>
<vcpu placement="static">2</vcpu>
<cpu mode='host-passthrough'>
<!-- disable nested HVM -->
<feature name='vmx' policy='disable'/>
<feature name='svm' policy='disable'/>
<!-- let the guest know the TSC is safe to use (no migration) -->
<feature name='invtsc' policy='require'/>
</cpu>
<os>
<type arch="x86_64" machine="xenfv">hvm</type>
<!--
For the libxl backend libvirt switches between OVMF (UEFI)
and SeaBIOS based on the loader type. This has nothing to
do with the hvmloader binary.
-->
<loader type="rom">hvmloader</loader>
<boot dev="cdrom" />
<boot dev="hd" />
</os>
<features>
<pae/>
<acpi/>
<apic/>
<viridian/>
</features>
<clock offset="variable" adjustment="0" basis="utc" />
<on_poweroff>destroy</on_poweroff>
<on_reboot>destroy</on_reboot>
<on_crash>destroy</on_crash>
<devices>
<!-- server_ip is the address of stubdomain. It hosts it's own DNS server. -->
<emulator type="stubdom-linux" cmdline="-qubes-audio:audiovm_xid=-1"/>
<input type="tablet" bus="usb"/>
<video>
<model type="vga"/>
</video>
<graphics type="qubes"
domain="test-inst-guivm"
log_level="2"
/>
<console type="pty">
<target type="xen" port="0"/>
</console>
</devices>
</domain>
"""
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"""<domain type="xen">
Expand Down
22 changes: 19 additions & 3 deletions qubes/vm/qubesvm.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down
10 changes: 9 additions & 1 deletion templates/libvirt/xen.xml
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,15 @@
</video>
{% if vm.features.check_with_template('linux-stubdom', True) %}
{# TODO only add qubes gui if gui-agent is not installed in HVM #}
<graphics type="qubes"/>
<graphics type="qubes"
{% if vm.guivm and vm.guivm.is_running()
and vm.guivm.name != "dom0" -%}
domain="{{vm.guivm.name}}"
{% endif -%}
{% if vm.debug -%}
log_level="2"
{% endif -%}
/>
{% endif %}
{% endif %}
{% endif %}
Expand Down