/*
 * This work is licensed under the terms of the GNU LGPL, version 2.
 *
 * This is a simple allocator that provides contiguous physical addresses
 * with byte granularity.
 */

#ifndef _ALLOC_PAGE_H_
#define _ALLOC_PAGE_H_

#include <stdbool.h>
#include <asm/memory_areas.h>

#define AREA_ANY_NUMBER	0xff

/* The realmode trampoline gets relocated here during bootup. */
#define RM_TRAMPOLINE_ADDR 0

#define AREA_ANY	0x00000
#define AREA_MASK	0x0ffff

#define FLAG_DONTZERO	0x10000
#define FLAG_FRESH	0x20000

/* Returns true if the page allocator has been initialized */
bool page_alloc_initialized(void);

/*
 * Initializes a memory area.
 * n is the number of the area to initialize
 * base_pfn is the physical frame number of the start of the area to initialize
 * top_pfn is the physical frame number of the first page immediately after
 * the end of the area to initialize
 */
void page_alloc_init_area(u8 n, phys_addr_t base_pfn, phys_addr_t top_pfn);

/* Enables the page allocator. At least one area must have been initialized */
void page_alloc_ops_enable(void);

/*
 * Allocate aligned memory with the specified flags.
 * flags is a bitmap of allowed areas and flags.
 * alignment must be a power of 2
 */
void *memalign_pages_flags(size_t alignment, size_t size, unsigned int flags);

/*
 * Allocate aligned memory from any area and with default flags.
 * Equivalent to memalign_pages_flags(alignment, size, AREA_ANY).
 */
static inline void *memalign_pages(size_t alignment, size_t size)
{
	return memalign_pages_flags(alignment, size, AREA_ANY);
}

/*
 * Allocate 1ull << order naturally aligned pages with the specified flags.
 * Equivalent to memalign_pages_flags(1ull << order, 1ull << order, flags).
 */
void *alloc_pages_flags(unsigned int order, unsigned int flags);

/*
 * Allocate 1ull << order naturally aligned pages from any area and with
 * default flags.
 * Equivalent to alloc_pages_flags(order, AREA_ANY);
 */
static inline void *alloc_pages(unsigned int order)
{
	return alloc_pages_flags(order, AREA_ANY);
}

/*
 * Allocate one page from any area and with default flags.
 * Equivalent to alloc_pages(0);
 */
static inline void *alloc_page(void)
{
	return alloc_pages(0);
}

/*
 * Frees a memory block allocated with any of the memalign_pages* or
 * alloc_pages* functions.
 * The pointer must point to the start of the block.
 */
void free_pages(void *mem);

/*
 * Free one page.
 * Equivalent to free_pages(mem).
 */
static inline void free_page(void *mem)
{
	free_pages(mem);
}

/*
 * Free pages by order.
 * Equivalent to free_pages(mem).
 */
static inline void free_pages_by_order(void *mem, unsigned int order)
{
	free_pages(mem);
}

/*
 * Reserves the specified physical memory range if possible.
 * If the specified range cannot be reserved in its entirety, no action is
 * performed and -1 is returned.
 *
 * Returns 0 in case of success, -1 otherwise.
 */
int reserve_pages(phys_addr_t addr, size_t npages);

/*
 * Frees a reserved memory range that had been reserved with
 * reserve_pages.
 * The memory range does not need to match a previous allocation
 * exactly, it can also be a subset, in which case only the specified
 * pages will be freed and unreserved.
 */
void unreserve_pages(phys_addr_t addr, size_t npages);

#endif