#include <sys/cdefs.h>
__FBSDID("$FreeBSD: head/sys/boot/efi/loader/copy.c 293724 2016-01-12 02:17:39Z smh $");
#include <sys/param.h>
#include <stand.h>
#include <bootstrap.h>
#include <efi.h>
#include <efilib.h>
#include "loader_efi.h"
#ifndef EFI_STAGING_SIZE
#define EFI_STAGING_SIZE 96
#endif
#define STAGE_PAGES EFI_SIZE_TO_PAGES((EFI_STAGING_SIZE) * 1024 * 1024)
EFI_PHYSICAL_ADDRESS staging, staging_end;
int stage_offset_set = 0;
ssize_t stage_offset;
int
efi_copy_init(void)
{
EFI_STATUS status;
size_t pages = STAGE_PAGES;
status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
pages, &staging);
if (EFI_ERROR(status)) {
printf("failed to allocate %uMB staging area: %llu\n",
EFI_STAGING_SIZE, status);
printf("retrying with smaller %uMB allocation\n",
EFI_STAGING_SIZE/2);
pages /= 2;
status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
pages, &staging);
}
if (EFI_ERROR(status)) {
printf("failed to allocate staging area: %llu\n",
status);
return (status);
}
staging_end = staging + pages * EFI_PAGE_SIZE;
#if defined(__aarch64__)
staging = roundup2(staging, 2 * 1024 * 1024);
#endif
return (0);
}
void *
efi_translate(vm_offset_t ptr)
{
return ((void *)(ptr + stage_offset));
}
ssize_t
efi_copyin(const void *src, vm_offset_t dest, const size_t len)
{
if (!stage_offset_set) {
stage_offset = (vm_offset_t)staging - dest;
stage_offset_set = 1;
}
if (dest + stage_offset + len > staging_end) {
errno = ENOMEM;
return (-1);
}
bcopy(src, (void *)(dest + stage_offset), len);
return (len);
}
ssize_t
efi_copyout(const vm_offset_t src, void *dest, const size_t len)
{
if (src + stage_offset + len > staging_end) {
errno = ENOMEM;
return (-1);
}
bcopy((void *)(src + stage_offset), dest, len);
return (len);
}
ssize_t
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));
}
void
efi_copy_finish(void)
{
uint64_t *src, *dst, *last;
src = (uint64_t *)staging;
dst = (uint64_t *)(staging - stage_offset);
last = (uint64_t *)staging_end;
while (src < last)
*dst++ = *src++;
}