From: Peter Feiner <pfeiner@xxxxxxxxxx> Need this for testing huge pages. Change-Id: Iecf4f916a3d3d24844957dcd4e81c8aa10b1cdda Signed-off-by: David Matlack <dmatlack@xxxxxxxxxx> --- lib/x86/vm.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/x86/vm.h | 1 + 2 files changed, 58 insertions(+) diff --git a/lib/x86/vm.c b/lib/x86/vm.c index 8bd0c88e141e..438ab9c70ffa 100644 --- a/lib/x86/vm.c +++ b/lib/x86/vm.c @@ -45,6 +45,63 @@ void *alloc_page() return p; } +/* + * Allocates (1 << order) physically contiguous and naturally aligned pages. + * Returns NULL if there's no memory left. + */ +void *alloc_pages(unsigned long order) +{ + /* Generic list traversal. */ + void *prev; + void *curr = NULL; + void *next = free; + + /* Looking for a run of length (1 << order). */ + unsigned long run = 0; + const unsigned long n = 1ul << order; + const unsigned long align_mask = (n << PAGE_SHIFT) - 1; + void *run_start = NULL; + void *run_prev = NULL; + unsigned long run_next_pa = 0; + unsigned long pa; + + assert(order < sizeof(unsigned long) * 8); + + for (;;) { + prev = curr; + curr = next; + next = curr ? *((void **) curr) : NULL; + + if (!curr) + return 0; + + pa = virt_to_phys(curr); + + if (run == 0) { + if (!(pa & align_mask)) { + run_start = curr; + run_prev = prev; + run_next_pa = pa + PAGE_SIZE; + run = 1; + } + } else if (pa == run_next_pa) { + run_next_pa += PAGE_SIZE; + run += 1; + } else { + run = 0; + } + + if (run == n) { + if (run_prev) + *((void **) run_prev) = next; + else + free = next; + return run_start; + } + } +} + + void free_page(void *page) { *(void **)page = free; diff --git a/lib/x86/vm.h b/lib/x86/vm.h index 6a4384f5a48d..64e4b566333f 100644 --- a/lib/x86/vm.h +++ b/lib/x86/vm.h @@ -22,6 +22,7 @@ unsigned long *install_pte(unsigned long *cr3, unsigned long *pt_page); void *alloc_page(); +void *alloc_pages(unsigned long order); void free_page(void *page); unsigned long *install_large_page(unsigned long *cr3,unsigned long phys, -- 2.12.2.816.g2cccc81164-goog