]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[hyperv] Do not steal ownership from the Gen 2 UEFI firmware
authorMichael Brown <mcb30@ipxe.org>
Fri, 28 Jul 2017 20:19:45 +0000 (21:19 +0100)
committerMichael Brown <mcb30@ipxe.org>
Fri, 28 Jul 2017 20:30:43 +0000 (21:30 +0100)
We must not steal ownership from the Gen 2 UEFI firmware, since doing
so will cause an immediate system crash (most likely in the form of a
reboot).

This problem was masked before commit a0f6e75 ("[hyperv] Do not fail
if guest OS ID MSR is already set"), since prior to that commit we
would always fail if we found any non-zero guest OS identity.  We now
accept a non-zero previous guest OS identity in order to allow for
situations such as chainloading from iPXE to another iPXE, and as a
prerequisite for commit b91cc98 ("[hyperv] Cope with Windows Server
2016 enlightenments").

A proper fix would be to reverse engineer the UEFI protocols exposed
within the Hyper-V Gen 2 firmware and use these to bind to the VMBus
device representing the network connection, (with the native Hyper-V
driver moved to become a BIOS-only feature).

As an interim solution, fail to initialise the native Hyper-V driver
if we detect the guest OS identity known to be used by the Gen 2 UEFI
firmware.  This will cause the standard all-drivers build (ipxe.efi)
to fall back to using the SNP driver.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/arch/x86/drivers/hyperv/hyperv.c
src/include/ipxe/hyperv.h

index 4e687687886a4e9fae85a622b5fb40feeb2fb2e7..1903d1db23d90a726fbc980df3ea2b8103871428 100644 (file)
@@ -220,6 +220,29 @@ static int hv_check_features ( struct hv_hypervisor *hv ) {
        return 0;
 }
 
+/**
+ * Check that Gen 2 UEFI firmware is not running
+ *
+ * @v hv               Hyper-V hypervisor
+ * @ret rc             Return status code
+ *
+ * We must not steal ownership from the Gen 2 UEFI firmware, since
+ * doing so will cause an immediate crash.  Avoid this by checking for
+ * the guest OS identity known to be used by the Gen 2 UEFI firmware.
+ */
+static int hv_check_uefi ( struct hv_hypervisor *hv ) {
+       uint64_t guest_os_id;
+
+       /* Check for UEFI firmware's guest OS identity */
+       guest_os_id = rdmsr ( HV_X64_MSR_GUEST_OS_ID );
+       if ( guest_os_id == HV_GUEST_OS_ID_UEFI ) {
+               DBGC ( hv, "HV %p is owned by UEFI firmware\n", hv );
+               return -ENOTSUP;
+       }
+
+       return 0;
+}
+
 /**
  * Map hypercall page
  *
@@ -556,6 +579,10 @@ static int hv_probe ( struct root_device *rootdev ) {
        if ( ( rc = hv_check_features ( hv ) ) != 0 )
                goto err_check_features;
 
+       /* Check that Gen 2 UEFI firmware is not running */
+       if ( ( rc = hv_check_uefi ( hv ) ) != 0 )
+               goto err_check_uefi;
+
        /* Allocate pages */
        if ( ( rc = hv_alloc_pages ( hv, &hv->hypercall, &hv->synic.message,
                                     &hv->synic.event, NULL ) ) != 0 )
@@ -587,6 +614,7 @@ static int hv_probe ( struct root_device *rootdev ) {
        hv_free_pages ( hv, hv->hypercall, hv->synic.message, hv->synic.event,
                        NULL );
  err_alloc_pages:
+ err_check_uefi:
  err_check_features:
        free ( hv );
  err_alloc:
index 9194a9766a03a906da1d4cbc708656b4f8d47e3a..9b7e54a51203c796f42f7327e9cea4757242e76e 100644 (file)
@@ -37,6 +37,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
  */
 #define HV_GUEST_OS_ID_IPXE ( ( 1ULL << 63 ) | ( 0x18aeULL << 48 ) )
 
+/** Guest OS identity for Gen 2 UEFI firmware
+ *
+ * This does not conform to the documented structure for guest OS
+ * identities.
+ */
+#define HV_GUEST_OS_ID_UEFI ( 1ULL << 40 )
+
 /** Enable hypercall page */
 #define HV_HYPERCALL_ENABLE 0x00000001UL