/* Memoire haute */
/* Copyright (C) 2008 Emmanuel Varoquaux
This file is part of XOS.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
/* Partie de la memoire physique non accessible directement par les tables des
* pages du noyau (http://en.wikipedia.org/wiki/High_memory). */
#include
#include
#include
union highmem_page_frame_data_union {
unsigned short count;
union highmem_page_frame_data_union *next;
};
/* memoire haute */
static unsigned long highmem_size_m; /* en Mo */
static unsigned long highmem_start; /* defini seulement si highmem_size_m != 0 */
/* carte de la memoire haute */
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;
}