18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci
38c2ecf20Sopenharmony_ci#include <linux/efi.h>
48c2ecf20Sopenharmony_ci#include <asm/efi.h>
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include "efistub.h"
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci/**
98c2ecf20Sopenharmony_ci * efi_allocate_pages_aligned() - Allocate memory pages
108c2ecf20Sopenharmony_ci * @size:	minimum number of bytes to allocate
118c2ecf20Sopenharmony_ci * @addr:	On return the address of the first allocated page. The first
128c2ecf20Sopenharmony_ci *		allocated page has alignment EFI_ALLOC_ALIGN which is an
138c2ecf20Sopenharmony_ci *		architecture dependent multiple of the page size.
148c2ecf20Sopenharmony_ci * @max:	the address that the last allocated memory page shall not
158c2ecf20Sopenharmony_ci *		exceed
168c2ecf20Sopenharmony_ci * @align:	minimum alignment of the base of the allocation
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci * Allocate pages as EFI_LOADER_DATA. The allocated pages are aligned according
198c2ecf20Sopenharmony_ci * to @align, which should be >= EFI_ALLOC_ALIGN. The last allocated page will
208c2ecf20Sopenharmony_ci * not exceed the address given by @max.
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci * Return:	status code
238c2ecf20Sopenharmony_ci */
248c2ecf20Sopenharmony_ciefi_status_t efi_allocate_pages_aligned(unsigned long size, unsigned long *addr,
258c2ecf20Sopenharmony_ci					unsigned long max, unsigned long align)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	efi_physical_addr_t alloc_addr;
288c2ecf20Sopenharmony_ci	efi_status_t status;
298c2ecf20Sopenharmony_ci	int slack;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	if (align < EFI_ALLOC_ALIGN)
328c2ecf20Sopenharmony_ci		align = EFI_ALLOC_ALIGN;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	alloc_addr = ALIGN_DOWN(max + 1, align) - 1;
358c2ecf20Sopenharmony_ci	size = round_up(size, EFI_ALLOC_ALIGN);
368c2ecf20Sopenharmony_ci	slack = align / EFI_PAGE_SIZE - 1;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	status = efi_bs_call(allocate_pages, EFI_ALLOCATE_MAX_ADDRESS,
398c2ecf20Sopenharmony_ci			     EFI_LOADER_DATA, size / EFI_PAGE_SIZE + slack,
408c2ecf20Sopenharmony_ci			     &alloc_addr);
418c2ecf20Sopenharmony_ci	if (status != EFI_SUCCESS)
428c2ecf20Sopenharmony_ci		return status;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	*addr = ALIGN((unsigned long)alloc_addr, align);
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	if (slack > 0) {
478c2ecf20Sopenharmony_ci		int l = (alloc_addr & (align - 1)) / EFI_PAGE_SIZE;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci		if (l) {
508c2ecf20Sopenharmony_ci			efi_bs_call(free_pages, alloc_addr, slack - l + 1);
518c2ecf20Sopenharmony_ci			slack = l - 1;
528c2ecf20Sopenharmony_ci		}
538c2ecf20Sopenharmony_ci		if (slack)
548c2ecf20Sopenharmony_ci			efi_bs_call(free_pages, *addr + size, slack);
558c2ecf20Sopenharmony_ci	}
568c2ecf20Sopenharmony_ci	return EFI_SUCCESS;
578c2ecf20Sopenharmony_ci}
58