Changeset View
Changeset View
Standalone View
Standalone View
head/stand/efi/loader/copy.c
Show First 20 Lines • Show All 179 Lines • ▼ Show 20 Lines | |||||
#define EFI_STAGING_SIZE 100 | #define EFI_STAGING_SIZE 100 | ||||
#elif defined(__arm__) | #elif defined(__arm__) | ||||
#define EFI_STAGING_SIZE 32 | #define EFI_STAGING_SIZE 32 | ||||
#else | #else | ||||
#define EFI_STAGING_SIZE 64 | #define EFI_STAGING_SIZE 64 | ||||
#endif | #endif | ||||
#endif | #endif | ||||
EFI_PHYSICAL_ADDRESS staging, staging_end; | EFI_PHYSICAL_ADDRESS staging, staging_end, staging_base; | ||||
int stage_offset_set = 0; | int stage_offset_set = 0; | ||||
ssize_t stage_offset; | ssize_t stage_offset; | ||||
int | int | ||||
efi_copy_init(void) | efi_copy_init(void) | ||||
{ | { | ||||
EFI_STATUS status; | EFI_STATUS status; | ||||
Show All 22 Lines | #else | ||||
status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, | status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, | ||||
nr_pages, &staging); | nr_pages, &staging); | ||||
#endif | #endif | ||||
if (EFI_ERROR(status)) { | if (EFI_ERROR(status)) { | ||||
printf("failed to allocate staging area: %lu\n", | printf("failed to allocate staging area: %lu\n", | ||||
EFI_ERROR_CODE(status)); | EFI_ERROR_CODE(status)); | ||||
return (status); | return (status); | ||||
} | } | ||||
staging_base = staging; | |||||
staging_end = staging + nr_pages * EFI_PAGE_SIZE; | staging_end = staging + nr_pages * EFI_PAGE_SIZE; | ||||
#if defined(__aarch64__) || defined(__arm__) | #if defined(__aarch64__) || defined(__arm__) | ||||
/* | /* | ||||
* Round the kernel load address to a 2MiB value. This is needed | * Round the kernel load address to a 2MiB value. This is needed | ||||
* because the kernel builds a page table based on where it has | * because the kernel builds a page table based on where it has | ||||
* been loaded in physical address space. As the kernel will use | * been loaded in physical address space. As the kernel will use | ||||
* either a 1MiB or 2MiB page for this we need to make sure it | * either a 1MiB or 2MiB page for this we need to make sure it | ||||
* is correctly aligned for both cases. | * is correctly aligned for both cases. | ||||
*/ | */ | ||||
staging = roundup2(staging, 2 * 1024 * 1024); | staging = roundup2(staging, 2 * 1024 * 1024); | ||||
#endif | #endif | ||||
return (0); | return (0); | ||||
} | } | ||||
static bool | |||||
efi_check_space(vm_offset_t end) | |||||
{ | |||||
EFI_PHYSICAL_ADDRESS addr; | |||||
EFI_STATUS status; | |||||
unsigned long nr_pages; | |||||
/* There is already enough space */ | |||||
if (end <= staging_end) | |||||
return (true); | |||||
end = roundup2(end, EFI_PAGE_SIZE); | |||||
nr_pages = EFI_SIZE_TO_PAGES(end - staging_end); | |||||
#if defined(__i386__) || defined(__amd64__) | |||||
/* X86 needs all memory to be allocated under the 1G boundary */ | |||||
if (end > 1024*1024*1024) | |||||
goto before_staging; | |||||
#endif | |||||
/* Try to allocate more space after the previous allocation */ | |||||
addr = staging_end; | |||||
status = BS->AllocatePages(AllocateAddress, EfiLoaderData, nr_pages, | |||||
&addr); | |||||
if (!EFI_ERROR(status)) { | |||||
staging_end = staging_end + nr_pages * EFI_PAGE_SIZE; | |||||
return (true); | |||||
} | |||||
before_staging: | |||||
/* Try allocating space before the previous allocation */ | |||||
if (staging < nr_pages * EFI_PAGE_SIZE) { | |||||
printf("Not enough space before allocation\n"); | |||||
return (false); | |||||
} | |||||
addr = staging - nr_pages * EFI_PAGE_SIZE; | |||||
#if defined(__aarch64__) || defined(__arm__) | |||||
/* See efi_copy_init for why this is needed */ | |||||
addr = rounddown2(addr, 2 * 1024 * 1024); | |||||
#endif | |||||
nr_pages = EFI_SIZE_TO_PAGES(staging_base - addr); | |||||
status = BS->AllocatePages(AllocateAddress, EfiLoaderData, nr_pages, | |||||
&addr); | |||||
if (!EFI_ERROR(status)) { | |||||
/* | |||||
* Move the old allocation and update the state so | |||||
* translation still works. | |||||
*/ | |||||
staging_base = addr; | |||||
memmove((void *)staging_base, (void *)staging, | |||||
staging_end - staging); | |||||
stage_offset -= (staging - staging_base); | |||||
staging = staging_base; | |||||
return (true); | |||||
} | |||||
printf("efi_check_space: Unable to expand staging area\n"); | |||||
return (false); | |||||
} | |||||
void * | void * | ||||
efi_translate(vm_offset_t ptr) | efi_translate(vm_offset_t ptr) | ||||
{ | { | ||||
return ((void *)(ptr + stage_offset)); | return ((void *)(ptr + stage_offset)); | ||||
} | } | ||||
ssize_t | ssize_t | ||||
efi_copyin(const void *src, vm_offset_t dest, const size_t len) | efi_copyin(const void *src, vm_offset_t dest, const size_t len) | ||||
{ | { | ||||
if (!stage_offset_set) { | if (!stage_offset_set) { | ||||
stage_offset = (vm_offset_t)staging - dest; | stage_offset = (vm_offset_t)staging - dest; | ||||
stage_offset_set = 1; | stage_offset_set = 1; | ||||
} | } | ||||
/* XXX: Callers do not check for failure. */ | /* XXX: Callers do not check for failure. */ | ||||
if (dest + stage_offset + len > staging_end) { | if (!efi_check_space(dest + stage_offset + len)) { | ||||
errno = ENOMEM; | errno = ENOMEM; | ||||
return (-1); | return (-1); | ||||
} | } | ||||
bcopy(src, (void *)(dest + stage_offset), len); | bcopy(src, (void *)(dest + stage_offset), len); | ||||
return (len); | return (len); | ||||
} | } | ||||
ssize_t | ssize_t | ||||
Show All 9 Lines | efi_copyout(const vm_offset_t src, void *dest, const size_t len) | ||||
return (len); | return (len); | ||||
} | } | ||||
ssize_t | ssize_t | ||||
efi_readin(const int fd, vm_offset_t dest, const size_t len) | efi_readin(const int fd, vm_offset_t dest, const size_t len) | ||||
{ | { | ||||
if (dest + stage_offset + len > staging_end) { | if (!efi_check_space(dest + stage_offset + len)) { | ||||
errno = ENOMEM; | errno = ENOMEM; | ||||
return (-1); | return (-1); | ||||
} | } | ||||
return (read(fd, (void *)(dest + stage_offset), len)); | return (read(fd, (void *)(dest + stage_offset), len)); | ||||
} | } | ||||
void | void | ||||
efi_copy_finish(void) | efi_copy_finish(void) | ||||
Show All 10 Lines |