blob: 5022a31fc960ef15a02f4478fd2800c24726b6b1 [file] [log] [blame]
/*
* Copyright (C) 2012, 2017, Red Hat Inc.
*
* This allocator provides contiguous physical addresses with page
* granularity.
*/
#include "libcflat.h"
#include "asm/spinlock.h"
#include "asm/page.h"
#include "asm/io.h"
#include "alloc.h"
#include "alloc_phys.h"
#include "alloc_page.h"
#include "vmalloc.h"
static struct spinlock lock;
static void *vfree_top = 0;
static void *page_root;
void *alloc_vpages(ulong nr)
{
spin_lock(&lock);
vfree_top -= PAGE_SIZE * nr;
spin_unlock(&lock);
return vfree_top;
}
void *alloc_vpage(void)
{
return alloc_vpages(1);
}
void init_alloc_vpage(void *top)
{
vfree_top = top;
}
void *vmap(phys_addr_t phys, size_t size)
{
void *mem, *p;
unsigned pages;
size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
pages = size / PAGE_SIZE;
mem = p = alloc_vpages(pages);
phys &= ~(unsigned long long)(PAGE_SIZE - 1);
while (pages--) {
install_page(page_root, phys, p);
phys += PAGE_SIZE;
p += PAGE_SIZE;
}
return mem;
}
static void *vm_memalign(size_t alignment, size_t size)
{
void *mem, *p;
unsigned pages;
assert(alignment <= PAGE_SIZE);
size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
pages = size / PAGE_SIZE;
mem = p = alloc_vpages(pages);
while (pages--) {
phys_addr_t pa = virt_to_phys(alloc_page());
install_page(page_root, pa, p);
p += PAGE_SIZE;
}
return mem;
}
static void vm_free(void *mem, size_t size)
{
while (size) {
free_page(phys_to_virt(virt_to_pte_phys(page_root, mem)));
mem += PAGE_SIZE;
size -= PAGE_SIZE;
}
}
static struct alloc_ops vmalloc_ops = {
.memalign = vm_memalign,
.free = vm_free,
.align_min = PAGE_SIZE,
};
void __attribute__((__weak__)) find_highmem(void)
{
}
void setup_vm()
{
phys_addr_t base, top;
if (alloc_ops == &vmalloc_ops)
return;
phys_alloc_get_unused(&base, &top);
assert(base != top || page_alloc_initialized());
/*
* Give low memory immediately to the page allocator,
* so that it can be used to allocate page tables.
*/
if (!page_alloc_initialized()) {
base = (base + PAGE_SIZE - 1) & -PAGE_SIZE;
top = top & -PAGE_SIZE;
free_pages(phys_to_virt(base), top - base);
}
find_highmem();
phys_alloc_get_unused(&base, &top);
page_root = setup_mmu(top);
if (base != top) {
base = (base + PAGE_SIZE - 1) & -PAGE_SIZE;
top = top & -PAGE_SIZE;
free_pages(phys_to_virt(base), top - base);
}
alloc_ops = &vmalloc_ops;
}