From de5c1722f517367d749792ed46c8c464e151b069 Mon Sep 17 00:00:00 2001 From: dexuan Date: Thu, 9 Mar 2017 12:09:07 +0000 Subject: [PATCH] loader.efi: only reduce the size of the staging area on Hyper-V Doing this on physical hosts turns out to be problematic, e.g. see comment 24 and 28 in https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=211746. To fix the real underlying issue correctly & thoroughly, IMO we need a relocatable kernel, but that would require a lot of complicated long term work: https://reviews.freebsd.org/D9686?id=25414#inline-56969 For now, let's only apply efi_verify_staging_size() to VMs running on Hyper-V, and restore the old behavior on physical machines since that has been working for people for a long period of time, though that's potentially unsafe... MFC after: 2 weeks Sponsored by: Microsoft --- sys/boot/efi/loader/copy.c | 52 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/sys/boot/efi/loader/copy.c b/sys/boot/efi/loader/copy.c index 20b806dc53a6..e26c0e56eaaf 100644 --- a/sys/boot/efi/loader/copy.c +++ b/sys/boot/efi/loader/copy.c @@ -30,6 +30,8 @@ __FBSDID("$FreeBSD$"); #include +#include +#include #include #include @@ -41,6 +43,47 @@ __FBSDID("$FreeBSD$"); #if defined(__i386__) || defined(__amd64__) +/* + * The code is excerpted from sys/x86/x86/identcpu.c: identify_cpu(), + * identify_hypervisor(), and dev/hyperv/vmbus/hyperv.c: hyperv_identify(). + */ +#define CPUID_LEAF_HV_MAXLEAF 0x40000000 +#define CPUID_LEAF_HV_INTERFACE 0x40000001 +#define CPUID_LEAF_HV_FEATURES 0x40000003 +#define CPUID_LEAF_HV_LIMITS 0x40000005 +#define CPUID_HV_IFACE_HYPERV 0x31237648 /* HV#1 */ +#define CPUID_HV_MSR_HYPERCALL 0x0020 +static int running_on_hyperv(void) +{ + char hv_vendor[16]; + uint32_t regs[4]; + + do_cpuid(1, regs); + if ((regs[2] & CPUID2_HV) == 0) + return (0); + + do_cpuid(CPUID_LEAF_HV_MAXLEAF, regs); + if (regs[0] < CPUID_LEAF_HV_LIMITS) + return (0); + + ((uint32_t *)&hv_vendor)[0] = regs[1]; + ((uint32_t *)&hv_vendor)[1] = regs[2]; + ((uint32_t *)&hv_vendor)[2] = regs[3]; + hv_vendor[12] = '\0'; + if (strcmp(hv_vendor, "Microsoft Hv") != 0) + return (0); + + do_cpuid(CPUID_LEAF_HV_INTERFACE, regs); + if (regs[0] != CPUID_HV_IFACE_HYPERV) + return (0); + + do_cpuid(CPUID_LEAF_HV_FEATURES, regs); + if ((regs[0] & CPUID_HV_MSR_HYPERCALL) == 0) + return (0); + + return (1); +} + #define KERNEL_PHYSICAL_BASE (2*1024*1024) static void @@ -134,8 +177,13 @@ efi_copy_init(void) nr_pages = EFI_SIZE_TO_PAGES((EFI_STAGING_SIZE) * 1024 * 1024); #if defined(__i386__) || defined(__amd64__) - /* We'll decrease nr_pages, if it's too big. */ - efi_verify_staging_size(&nr_pages); + /* + * We'll decrease nr_pages, if it's too big. Currently we only + * apply this to FreeBSD VM running on Hyper-V. Why? Please see + * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=211746#c28 + */ + if (running_on_hyperv()) + efi_verify_staging_size(&nr_pages); /* * The staging area must reside in the the first 1GB physical