Skip to content

Commit

Permalink
libvirt: Handle alternative UEFI firmware binary paths
Browse files Browse the repository at this point in the history
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
  • Loading branch information
dirkmueller and kashyapc committed Jul 31, 2019
1 parent 8ad437d commit 363710b
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 10 deletions.
8 changes: 4 additions & 4 deletions nova/tests/unit/virt/libvirt/test_driver.py
Expand Up @@ -6423,8 +6423,8 @@ def get_host_capabilities_stub(self):
_fake_network_info(self, 1),
image_meta, disk_info)
self.assertTrue(mock_path_exists.called)
mock_path_exists.assert_called_with(
libvirt_driver.DEFAULT_UEFI_LOADER_PATH['aarch64'])
mock_path_exists.assert_any_call(
libvirt_driver.DEFAULT_UEFI_LOADER_PATH['aarch64'][0])
self.assertEqual(cfg.os_mach_type, "virt")

num_ports = 0
Expand Down Expand Up @@ -6468,8 +6468,8 @@ def get_host_capabilities_stub(self):
cfg = self._get_guest_config_with_graphics()

self.assertTrue(mock_path_exists.called)
mock_path_exists.assert_called_with(
libvirt_driver.DEFAULT_UEFI_LOADER_PATH['aarch64'])
mock_path_exists.assert_any_call(
libvirt_driver.DEFAULT_UEFI_LOADER_PATH['aarch64'][0])
self.assertEqual(cfg.os_mach_type, "virt")

usbhost_exists = False
Expand Down
37 changes: 31 additions & 6 deletions nova/virt/libvirt/driver.py
Expand Up @@ -139,8 +139,11 @@
libvirt_firewall.IptablesFirewallDriver.__name__)

DEFAULT_UEFI_LOADER_PATH = {
"x86_64": "/usr/share/OVMF/OVMF_CODE.fd",
"aarch64": "/usr/share/AAVMF/AAVMF_CODE.fd"
"x86_64": ['/usr/share/OVMF/OVMF_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
Expand Down Expand Up @@ -4959,12 +4962,33 @@ def _get_flavor(self, ctxt, instance, flavor):
return instance.flavor

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,
fields.Architecture.AARCH64]
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
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):

Expand Down Expand Up @@ -5023,8 +5047,9 @@ def _configure_guest_by_virt_type(self, guest, virt_type, caps, instance,
"functional testing and therefore "
"considered experimental.")
uefi_logged = True
guest.os_loader = DEFAULT_UEFI_LOADER_PATH[
caps.host.cpu.arch]
for lpath in DEFAULT_UEFI_LOADER_PATH[caps.host.cpu.arch]:
if os.path.exists(lpath):
guest.os_loader = lpath
guest.os_loader_type = "pflash"
else:
raise exception.UEFINotSupported()
Expand Down

0 comments on commit 363710b

Please sign in to comment.