View of xos/mm/highmem.c


XOS | Parent Directory | View | Download

/* 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 <http://www.gnu.org/licenses/>. */
 
/* Partie de la memoire physique non accessible directement par les tables des
 * pages du noyau (http://en.wikipedia.org/wiki/High_memory). */
 
#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;
};
 
/* 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;
}