/* Copyright (C) 2012, Aaron Lindsay This file is part of Aedrix. 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include struct dlist_node free_frames_list; void frames_init() { init_list(&free_frames_list); } /* * Adds a page frame struct to the list of free page frames. Presupposes * free_frames_list is a properly initialized list. */ static void insert_page_frame(struct frame *p) { if (list_empty(&free_frames_list) || p->address < container(free_frames_list.next, struct frame, list)->address) { insert_after(&free_frames_list, &p->list); } else if (p->address > container(free_frames_list.prev, struct frame, list)->address) { insert_before(&free_frames_list, &p->list); } else { struct frame *it; for_each_list(it, &free_frames_list, struct frame, list) { if (p->address < it->address) { insert_before(&it->list, &p->list); return; } } print("Error: failed to insert page frame\n"); } } /* * Called to add a segment of memory to the frame allocation pool. */ static void add_physical_memory(void *start, void *end) { unsigned int num_pages, usable_pages; struct frame *p; void *page; //If region starts at 0x0, make it start at next page to not screw up null pointer detection, etc. if (start == 0) start = (char *)start + CONFIG_PAGE_SIZE; //make sure both start and end address are aligned to the size of a page if ((arch_uint_ptr)start % CONFIG_PAGE_SIZE != 0) start = (char*)start + (CONFIG_PAGE_SIZE - ((arch_uint_ptr)start % CONFIG_PAGE_SIZE)); if (((arch_uint_ptr)end + 1) % CONFIG_PAGE_SIZE != 0) end = (char*)end - ((arch_uint_ptr)end + 1) % CONFIG_PAGE_SIZE; if ((char *)end + 1 - (char *)start < CONFIG_PAGE_SIZE<<1) { print("Error: Supplied memory area(%x,%x) is smaller than the page size (%d)\n", (arch_uint_ptr)start, (arch_uint_ptr)end, CONFIG_PAGE_SIZE); return; } //reserve enough pages at the front of this chunk of memory to hold the //page structs, then build the structs and add them to the list //TODO we're potentially losing memory here because we're calculating //the number of page structs we need even for those pages that will contain only page structs num_pages = ((char *)end + 1 - (char *)start) / CONFIG_PAGE_SIZE; usable_pages = num_pages - num_pages * sizeof(struct frame) / CONFIG_PAGE_SIZE; if (num_pages * sizeof(struct frame) % CONFIG_PAGE_SIZE) usable_pages--; p = (struct frame *)start; for (page = ((char *)start) + (num_pages - usable_pages)*CONFIG_PAGE_SIZE; page < end; page = (void *)(((char *)page) + CONFIG_PAGE_SIZE)) { p->address = page; insert_page_frame(p); p++; } } /* * Attempt to get 2^power contiguous page frames. Returns the frame struct of * the first frame on success or a null pointer on failure. */ struct frame* get_free_frames(unsigned int power) { unsigned int num_pages = 1<list.next, struct frame, list); it2->list.next != &free_frames_list && curr_pages < num_pages; it2 = container(it2->list.next, struct frame, list)) { if ((char*)it2->address != (char*)container(it2->list.prev, struct frame, list)->address + CONFIG_PAGE_SIZE) { it = it2; //fast-forward 'it' to start of next contiguous section of pages break; } else { curr_pages++; } } if (curr_pages == num_pages) { remove_splice(&it->list, it2->list.prev); return it; } } return (struct frame*)0; } /* * Return pages allocated to the pool of unused pages. */ int put_free_frames(struct frame *f) { struct frame *it; for_each_list(it, &free_frames_list, struct frame, list) { if (f->address < it->address) { insert_splice_before(&it->list, &f->list, f->list.prev); return 0; } } return 1; } static inline int mmu_region_contains(void *lower_a, void *upper_a, void *lower_b, void *upper_b) { return lower_b >= lower_a && upper_b <= upper_a; } static inline int mmu_region_contains_single(void *lower_a, void *upper_a, void *ptr) { return lower_a <= ptr && ptr <= upper_a; } #define page_round_down(ptr) (((uint32)ptr) & ~(CONFIG_INIT_PAGE_SIZE-1)) #define page_round_up(ptr) (((((uint32)ptr) & ~1) + (CONFIG_INIT_PAGE_SIZE-1) ) & ~(CONFIG_INIT_PAGE_SIZE-1)) /* Called once per physical memory region by bootup code. This function is * responsible for only adding (via add_physical_memory()) those parts of the * memory region which are still available (i.e. aren't in the kernel and * haven't been remapped anywhere else. */ void declare_memory_region(void *lower, void *upper) { void *k_section_start_phys = (void *)page_round_down(kernel_start_physical()); void *k_section_end_phys = (void *)(page_round_up(kernel_end_physical()) - 1); void *k_section_start_virt = (void *)page_round_down(kernel_start_virtual()); void *k_section_end_virt = (void *)(page_round_up(kernel_end_virtual()) - 1); if (upper - lower < 1) { print("Warning: declare_memory_region() called with lower=%x, upper=%x. Ignoring.\n", lower, upper); return; } if (mmu_region_contains(lower, upper, k_section_start_phys, k_section_end_phys)) { //Don't map any of the physical kernel's memory declare_memory_region(lower, (void *) ((char *)k_section_start_phys - 1)); declare_memory_region((void *) ((char *)k_section_end_phys + 1), upper); add_physical_memory(kernel_end_virtual(), k_section_end_virt); } else if (mmu_region_contains(lower, upper, k_section_start_virt, k_section_end_virt)) { declare_memory_region(lower, (void *) ((char *)k_section_start_virt - 1)); declare_memory_region((void *) ((char *)k_section_end_virt + 1), upper); } else if (mmu_region_contains_single(lower, upper, k_section_start_phys)) { if ((void*)((char*)lower + 1) < k_section_start_phys) declare_memory_region(lower, (void *) ((char *)k_section_start_phys - 1)); } else if (mmu_region_contains_single(lower, upper, k_section_end_phys)) { if (k_section_end_phys < (void*)((char*)upper - 1)) declare_memory_region((void *) ((char *)k_section_end_phys + 1), upper); } else if (mmu_region_contains_single(lower, upper, k_section_start_virt)) { if ((void*)((char*)lower + 1) < k_section_start_virt) declare_memory_region(lower, (void *) ((char *)k_section_start_virt - 1)); } else if (mmu_region_contains_single(lower, upper, k_section_end_virt)) { if (k_section_end_virt < (void*)((char*)upper - 1)) declare_memory_region((void *) ((char *)k_section_end_virt + 1), upper); } else { add_physical_memory(lower, upper); } }