#include <printk.h>
#include <i386.h>
#include <stddef.h>
union highmem_page_frame_data_union {
unsigned short count;
union highmem_page_frame_data_union *next;
};
static unsigned long highmem_size_m;
static unsigned long highmem_start;
static union highmem_page_frame_data_union *highmem_map;
static union highmem_page_frame_data_union *first_free_page_frame_data;
static void print_human_readable_size(unsigned long size_m)
{
if (size_m < 1024)
printk("%luMB", size_m);
else if (size_m & 511)
printk("%lu.%luGB", size_m >> 10, (size_m & 1023) * 100 / 1024);
else if (size_m & 1023)
printk("%lu.%luGB", size_m >> 10, (size_m & 1023) * 10 / 1024);
else
printk("%luGB", size_m >> 10);
}
static void init_highmem_map(void *start, unsigned long size, void **end)
{
static union highmem_page_frame_data_union *page_frame_data;
page_frame_data = highmem_map = start;
if (size) {
while (--size) {
page_frame_data->next = page_frame_data + 1;
page_frame_data++;
}
page_frame_data->next = NULL;
page_frame_data++;
first_free_page_frame_data = &highmem_map[0];
}
else
first_free_page_frame_data = NULL;
*end = page_frame_data;
}
static unsigned long get_page_frame(union highmem_page_frame_data_union *page_frame_data)
{
return highmem_start + ((page_frame_data - &highmem_map[0]) << LOG_PAGE_SIZE);
}
static union highmem_page_frame_data_union *get_page_frame_data(unsigned long page_frame)
{
return &highmem_map[(page_frame - highmem_start) >> LOG_PAGE_SIZE];
}
static void push_free_page_frame_data(union highmem_page_frame_data_union *page_frame_data)
{
page_frame_data->next = first_free_page_frame_data;
first_free_page_frame_data = page_frame_data;
}
static union highmem_page_frame_data_union *pop_free_page_frame_data()
{
union highmem_page_frame_data_union *page_frame_data;
if ((page_frame_data = first_free_page_frame_data))
first_free_page_frame_data = first_free_page_frame_data->next;
return page_frame_data;
}
void highmem_init(unsigned short start_m, unsigned short size_m, void *start_data, void **end_data)
{
highmem_size_m = size_m;
if (highmem_size_m)
highmem_start = start_m << 20;
init_highmem_map(start_data, size_m << (20 - LOG_PAGE_SIZE), end_data);
}
void highmem_print_info()
{
printk("High memory size: ");
print_human_readable_size(highmem_size_m);
printk("\n");
}
int is_highmem(unsigned long page_frame)
{
return highmem_size_m ? page_frame >= highmem_start : 0;
}
unsigned long alloc_page_frame_highmem()
{
union highmem_page_frame_data_union *page_frame_data;
if (!(page_frame_data = pop_free_page_frame_data()))
return 0;
page_frame_data->count = 1;
return get_page_frame(page_frame_data);
}
void hold_page_frame_highmem(unsigned long page_frame)
{
get_page_frame_data(page_frame)->count++;
}
void release_page_frame_highmem(unsigned long page_frame)
{
union highmem_page_frame_data_union *page_frame_data;
page_frame_data = get_page_frame_data(page_frame);
if (!--page_frame_data->count)
push_free_page_frame_data(page_frame_data);
}
int is_page_frame_shared_highmem(unsigned long page_frame)
{
return get_page_frame_data(page_frame)->count > 1;
}