/* 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; }