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:
		 Dirk Mueller
					Dirk Mueller
				
			
				
					committed by
					
						 Kashyap Chamarthy
						Kashyap Chamarthy
					
				
			
			
				
	
			
			
			 Kashyap Chamarthy
						Kashyap Chamarthy
					
				
			
						parent
						
							8ad437d142
						
					
				
				
					commit
					363710b655
				
			| @@ -6423,8 +6423,8 @@ class LibvirtConnTestCase(test.NoDBTestCase, | ||||
|                                      _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 | ||||
| @@ -6468,8 +6468,8 @@ class LibvirtConnTestCase(test.NoDBTestCase, | ||||
|         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 | ||||
|   | ||||
| @@ -139,8 +139,11 @@ DEFAULT_FIREWALL_DRIVER = "%s.%s" % ( | ||||
|     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 | ||||
| @@ -4959,12 +4962,33 @@ class LibvirtDriver(driver.ComputeDriver): | ||||
|         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): | ||||
|  | ||||
| @@ -5023,8 +5047,9 @@ class LibvirtDriver(driver.ComputeDriver): | ||||
|                                     "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() | ||||
|   | ||||
		Reference in New Issue
	
	Block a user