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
This commit is contained in:
dexuan 2017-03-09 12:09:07 +00:00
parent 15b103e791
commit de5c1722f5

View File

@ -30,6 +30,8 @@
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <machine/cpufunc.h>
#include <machine/specialreg.h>
#include <stand.h>
#include <bootstrap.h>
@ -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