libvirt: Handle alternative UEFI firmware binary paths
The OVMF binary paths differ based on the Linux distribution:
- Debian and Ubuntu:
- /usr/share/OVMF/OVMF_CODE.fd
- Fedora:
- /usr/share/edk2/ovmf/OVMF_CODE.fd
(`symlink`s to /usr/share/OVMF/OVMF_CODE.fd)
- /usr/share/edk2/ovmf/OVMF_CODE.secboot.fd (`symlink`s to
/usr/share/OVMF/OVMF_CODE.secboot.fd)
- CentOS and RHEL:
- /usr/share/OVMF/OVMF_CODE.secboot.fd
- SUSE:
- /usr/share/qemu/ovmf-x86_64-opensuse-code.bin
Currently, Nova only checks for one location OVMF_CODE.fd. Let's also
check for the other two common distributions, SUSE and CentOS OVMF
binary paths. This is a short-term solution to fix two bugs.
In the long run:
- We will get rid of the "DEFAULT_UEFI_LOADER_PATH", which is used to
probe for firmware file paths. Instead, we'll use the more robust
approach of the recently introduced[1] get_domain_capabilities()[1]
to query for the firmware binary paths (as reported in the 'loader'
attribute).
- Use libvirt's (>=5.3) firmware auto-selection feature. Which is a
more robust way to decide UEFI boot (secure or otherwise). More
details of it in the spec here[2].
[1] https://opendev.org/openstack/nova/commit/297f3ba687 -- Add
infrastructure for invoking libvirt's getDomainCapabilities API
[2] http://specs.openstack.org/openstack/nova-specs/specs/train/approved/allow-secure-boot-for-qemu-kvm-guests.html
Co-Authored-By: Kashyap Chamarthy <kchamart@redhat.com>
Closes-Bug: 1607400
Closes-Bug: 1825386
blueprint: allow-secure-boot-for-qemu-kvm-guests
Signed-off-by: Kashyap Chamarthy <kchamart@redhat.com>
Change-Id: I28afdb09d300be39981606d5234fd837ea738e1d
This commit is contained in:
committed by
Kashyap Chamarthy
parent
8ad437d142
commit
363710b655
@@ -6423,8 +6423,8 @@ class LibvirtConnTestCase(test.NoDBTestCase,
|
|||||||
_fake_network_info(self, 1),
|
_fake_network_info(self, 1),
|
||||||
image_meta, disk_info)
|
image_meta, disk_info)
|
||||||
self.assertTrue(mock_path_exists.called)
|
self.assertTrue(mock_path_exists.called)
|
||||||
mock_path_exists.assert_called_with(
|
mock_path_exists.assert_any_call(
|
||||||
libvirt_driver.DEFAULT_UEFI_LOADER_PATH['aarch64'])
|
libvirt_driver.DEFAULT_UEFI_LOADER_PATH['aarch64'][0])
|
||||||
self.assertEqual(cfg.os_mach_type, "virt")
|
self.assertEqual(cfg.os_mach_type, "virt")
|
||||||
|
|
||||||
num_ports = 0
|
num_ports = 0
|
||||||
@@ -6468,8 +6468,8 @@ class LibvirtConnTestCase(test.NoDBTestCase,
|
|||||||
cfg = self._get_guest_config_with_graphics()
|
cfg = self._get_guest_config_with_graphics()
|
||||||
|
|
||||||
self.assertTrue(mock_path_exists.called)
|
self.assertTrue(mock_path_exists.called)
|
||||||
mock_path_exists.assert_called_with(
|
mock_path_exists.assert_any_call(
|
||||||
libvirt_driver.DEFAULT_UEFI_LOADER_PATH['aarch64'])
|
libvirt_driver.DEFAULT_UEFI_LOADER_PATH['aarch64'][0])
|
||||||
self.assertEqual(cfg.os_mach_type, "virt")
|
self.assertEqual(cfg.os_mach_type, "virt")
|
||||||
|
|
||||||
usbhost_exists = False
|
usbhost_exists = False
|
||||||
|
|||||||
@@ -139,8 +139,11 @@ DEFAULT_FIREWALL_DRIVER = "%s.%s" % (
|
|||||||
libvirt_firewall.IptablesFirewallDriver.__name__)
|
libvirt_firewall.IptablesFirewallDriver.__name__)
|
||||||
|
|
||||||
DEFAULT_UEFI_LOADER_PATH = {
|
DEFAULT_UEFI_LOADER_PATH = {
|
||||||
"x86_64": "/usr/share/OVMF/OVMF_CODE.fd",
|
"x86_64": ['/usr/share/OVMF/OVMF_CODE.fd',
|
||||||
"aarch64": "/usr/share/AAVMF/AAVMF_CODE.fd"
|
'/usr/share/OVMF/OVMF_CODE.secboot.fd',
|
||||||
|
'/usr/share/qemu/ovmf-x86_64-code.bin'],
|
||||||
|
"aarch64": ['/usr/share/AAVMF/AAVMF_CODE.fd',
|
||||||
|
'/usr/share/qemu/aavmf-aarch64-code.bin']
|
||||||
}
|
}
|
||||||
|
|
||||||
MAX_CONSOLE_BYTES = 100 * units.Ki
|
MAX_CONSOLE_BYTES = 100 * units.Ki
|
||||||
@@ -4959,12 +4962,33 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||||||
return instance.flavor
|
return instance.flavor
|
||||||
|
|
||||||
def _has_uefi_support(self):
|
def _has_uefi_support(self):
|
||||||
# This means that the host can support uefi booting for guests
|
# This means that the host can support UEFI booting for guests
|
||||||
supported_archs = [fields.Architecture.X86_64,
|
supported_archs = [fields.Architecture.X86_64,
|
||||||
fields.Architecture.AARCH64]
|
fields.Architecture.AARCH64]
|
||||||
caps = self._host.get_capabilities()
|
caps = self._host.get_capabilities()
|
||||||
|
# TODO(dmllr, kchamart): Get rid of probing the OVMF binary file
|
||||||
|
# paths, it is not robust, because nothing but the binary's
|
||||||
|
# filename is reported, which means you have to detect its
|
||||||
|
# architecture and features by other means. To solve this,
|
||||||
|
# query the libvirt's getDomainCapabilities() to get the
|
||||||
|
# firmware paths (as reported in the 'loader' value). Nova now
|
||||||
|
# has a wrapper method for this, get_domain_capabilities().
|
||||||
|
# This is a more reliable way to detect UEFI boot support.
|
||||||
|
#
|
||||||
|
# Further, with libvirt 5.3 onwards, support for UEFI boot is
|
||||||
|
# much more simplified by the "firmware auto-selection" feature.
|
||||||
|
# When using this, Nova doesn't need to query OVMF file paths at
|
||||||
|
# all; libvirt will take care of it. This is done by taking
|
||||||
|
# advantage of the so-called firmware "descriptor files" --
|
||||||
|
# small JSON files (which will be shipped by Linux
|
||||||
|
# distributions) that describe a UEFI firmware binary's
|
||||||
|
# "characteristics", such as the binary's file path, its
|
||||||
|
# features, architecture, supported machine type, NVRAM template
|
||||||
|
# and so forth.
|
||||||
|
|
||||||
return ((caps.host.cpu.arch in supported_archs) and
|
return ((caps.host.cpu.arch in supported_archs) and
|
||||||
os.path.exists(DEFAULT_UEFI_LOADER_PATH[caps.host.cpu.arch]))
|
any((os.path.exists(p)
|
||||||
|
for p in DEFAULT_UEFI_LOADER_PATH[caps.host.cpu.arch])))
|
||||||
|
|
||||||
def _get_supported_perf_events(self):
|
def _get_supported_perf_events(self):
|
||||||
|
|
||||||
@@ -5023,8 +5047,9 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||||||
"functional testing and therefore "
|
"functional testing and therefore "
|
||||||
"considered experimental.")
|
"considered experimental.")
|
||||||
uefi_logged = True
|
uefi_logged = True
|
||||||
guest.os_loader = DEFAULT_UEFI_LOADER_PATH[
|
for lpath in DEFAULT_UEFI_LOADER_PATH[caps.host.cpu.arch]:
|
||||||
caps.host.cpu.arch]
|
if os.path.exists(lpath):
|
||||||
|
guest.os_loader = lpath
|
||||||
guest.os_loader_type = "pflash"
|
guest.os_loader_type = "pflash"
|
||||||
else:
|
else:
|
||||||
raise exception.UEFINotSupported()
|
raise exception.UEFINotSupported()
|
||||||
|
|||||||
Reference in New Issue
Block a user