Allow the EFI loader to work with large kernels and/or modules

(for example, a large mfsroot).  Note that for EFI the kernel and
modules (as well as other metadata files such as splash screens or
memory disk images) are loaded into a statically-sized staging area.
When the EFI loader exits it copies this staging area down to the
location the kernel expects to run at.
- Add bounds checking to the copy routines to fail attempts to access
  memory outside of the staging area.  Previously loading a combined
  kernel + modules larger than the staging size (32MB) would overflow
  the staging area trashing whatever memory was afterwards.  Under
  Intel's OVMF firmware for qemu this resulted in fatal faults in the
  firmware itself.  Now the attempt will fail with ENOMEM.
- Allow the staging area size to be configured at compile time via
  an EFI_STAGING_SIZE variable in src.conf or on the command line.
  It accepts the size of the staging area in MB.  The default size
  remains 32MB.

MFC after:	2 weeks
Sponsored by:	Cisco Systems, Inc.
This commit is contained in:
John Baldwin 2015-03-12 17:07:24 +00:00
parent 8704b9e9db
commit 7939325215
2 changed files with 25 additions and 2 deletions

View File

@ -44,6 +44,10 @@ LIBFICL= ${.OBJDIR}/../../ficl/libficl.a
# Include bcache code.
HAVE_BCACHE= yes
.if defined(EFI_STAGING_SIZE)
CFLAGS+= -DEFI_STAGING_SIZE=${EFI_STAGING_SIZE}
.endif
# Always add MI sources
.PATH: ${.CURDIR}/../../common
.include "${.CURDIR}/../../common/Makefile.inc"

View File

@ -37,9 +37,13 @@ __FBSDID("$FreeBSD$");
#include <efi.h>
#include <efilib.h>
#define STAGE_PAGES 8192 /* 32MB */
#ifndef EFI_STAGING_SIZE
#define EFI_STAGING_SIZE 32
#endif
EFI_PHYSICAL_ADDRESS staging;
#define STAGE_PAGES ((EFI_STAGING_SIZE) * 1024 * 1024 / 4096)
EFI_PHYSICAL_ADDRESS staging, staging_end;
int stage_offset_set = 0;
ssize_t stage_offset;
@ -55,6 +59,7 @@ x86_efi_copy_init(void)
(unsigned long)(status & EFI_ERROR_MASK));
return (status);
}
staging_end = staging + STAGE_PAGES * 4096;
return (0);
}
@ -68,6 +73,11 @@ x86_efi_copyin(const void *src, vm_offset_t dest, const size_t len)
stage_offset_set = 1;
}
/* XXX: Callers do not check for failure. */
if (dest + stage_offset + len > staging_end) {
errno = ENOMEM;
return (-1);
}
bcopy(src, (void *)(dest + stage_offset), len);
return (len);
}
@ -76,6 +86,11 @@ ssize_t
x86_efi_copyout(const vm_offset_t src, void *dest, const size_t len)
{
/* XXX: Callers do not check for failure. */
if (src + stage_offset + len > staging_end) {
errno = ENOMEM;
return (-1);
}
bcopy((void *)(src + stage_offset), dest, len);
return (len);
}
@ -85,6 +100,10 @@ ssize_t
x86_efi_readin(const int fd, vm_offset_t dest, const size_t len)
{
if (dest + stage_offset + len > staging_end) {
errno = ENOMEM;
return (-1);
}
return (read(fd, (void *)(dest + stage_offset), len));
}