2 pcboot - bootable PC demo/game kernel
3 Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY, without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>.
28 #define BM_IDX(pg) ((pg) / 32)
29 #define BM_BIT(pg) ((pg) & 0x1f)
31 #define IS_FREE(pg) ((bitmap[BM_IDX(pg)] & (1 << BM_BIT(pg))) == 0)
33 #define MEM_START ((uint32_t)&_mem_start)
41 void move_stack(uint32_t newaddr); /* defined in startup.s */
43 static void mark_page(int pg, int used);
44 static void add_memory(uint32_t start, size_t size);
46 #define MAX_MAP_SIZE 16
47 extern struct mem_range boot_mem_map[MAX_MAP_SIZE];
48 extern int boot_mem_map_size;
50 /* linker supplied symbol, points to the end of the kernel image */
51 extern uint32_t _mem_start;
54 /* A bitmap is used to track which physical memory pages are used, and which
55 * are available for allocation by alloc_ppage.
57 * last_alloc_idx keeps track of the last 32bit element in the bitmap array
58 * where a free page was found. It's guaranteed that all the elements before
59 * this have no free pages, but it doesn't imply that there will be another
60 * free page there. So it's used as a starting point for the search.
62 static uint32_t *bitmap;
63 static int bmsize, last_alloc_idx;
69 int i, pg, max_used_pg, end_pg = 0;
70 uint32_t used_end, start, end, sz, total = 0, rem;
71 const char *suffix[] = {"bytes", "KB", "MB", "GB"};
75 /* the allocation bitmap starts right at the end of the kernel image */
76 bitmap = (uint32_t*)&_mem_start;
78 /* start by marking all posible pages (2**20) as used. We do not "reserve"
79 * all this space. Pages beyond the end of the useful bitmap area
80 * ((char*)bitmap + bmsize), which will be determined after we traverse the
81 * memory map, are going to be marked as available for allocation.
83 memset(bitmap, 0xff, 1024 * 1024 / 8);
86 if(boot_mem_map_size <= 0 || boot_mem_map_size > MAX_MAP_SIZE) {
87 panic("invalid memory map size reported by the boot loader: %d\n", boot_mem_map_size);
90 printf("Memory map:\n");
91 for(i=0; i<boot_mem_map_size; i++) {
92 printf(" start: %08x - size: %08x\n", (unsigned int)boot_mem_map[i].start,
93 (unsigned int)boot_mem_map[i].size);
95 start = boot_mem_map[i].start;
96 sz = boot_mem_map[i].size & 0xfffffffc;
103 /* ignore any memory ranges which end before the end of the kernel image */
104 if(end < MEM_START) continue;
105 if(start < MEM_START) {
109 add_memory(start, sz);
112 pg = ADDR_TO_PAGE(end);
119 while(total > 1024) {
124 printf("Total usable RAM: %u.%u %s\n", total, 100 * rem / 1024, suffix[i]);
126 /* size of the useful part of the bitmap in bytes padded to 4-byte
127 * boundaries to allow 32bit at a time operations.
129 bmsize = (end_pg / 32) * 4;
131 /* mark all pages occupied by the bitmap as used */
132 used_end = (uint32_t)bitmap + bmsize - 1;
134 max_used_pg = ADDR_TO_PAGE(used_end);
135 printf("marking pages up to %x (page: %d) as used\n", used_end, max_used_pg);
136 for(i=0; i<=max_used_pg; i++) {
140 #ifdef MOVE_STACK_RAMTOP
141 /* allocate space for the stack at the top of RAM and move it there */
142 if((pg = alloc_ppages(STACK_PAGES, MEM_STACK)) != -1) {
143 printf("moving stack-top to: %x (%d pages)\n", PAGE_TO_ADDR(end_pg) - 4, STACK_PAGES);
144 move_stack(PAGE_TO_ADDR(pg + STACK_PAGES) - 4);
149 int alloc_ppage(int area)
151 return alloc_ppages(1, area);
154 /* free_ppage marks the physical page, free in the allocation bitmap.
156 * CAUTION: no checks are done that this page should actually be freed or not.
157 * If you call free_ppage with the address of some part of memory that was
158 * originally reserved due to it being in a memory hole or part of the kernel
159 * image or whatever, it will be subsequently allocatable by alloc_ppage.
161 void free_ppage(int pg)
163 int bmidx = BM_IDX(pg);
165 int intr_state = get_intr_flag();
169 panic("free_ppage(%d): I thought that was already free!\n", pg);
173 if(bmidx < last_alloc_idx) {
174 last_alloc_idx = bmidx;
177 set_intr_flag(intr_state);
181 int alloc_ppages(int count, int area)
183 int i, dir, pg, idx, max, intr_state, found_free = 0;
185 intr_state = get_intr_flag();
188 if(area == MEM_STACK) {
189 idx = (bmsize - 1) / 4;
193 idx = last_alloc_idx;
199 /* if at least one bit is 0 then we have at least
200 * one free page. find it and try to allocate a range starting from there
202 if(bitmap[idx] != 0xffffffff) {
204 if(dir < 0) pg += 31;
206 for(i=0; i<32; i++) {
208 if(!found_free && dir > 0) {
209 last_alloc_idx = idx;
213 if(alloc_ppage_range(pg, count) != -1) {
214 set_intr_flag(intr_state);
224 set_intr_flag(intr_state);
228 void free_ppages(int pg0, int count)
232 for(i=0; i<count; i++) {
237 int alloc_ppage_range(int start, int size)
242 intr_state = get_intr_flag();
245 /* first validate that no page in the requested range is allocated */
246 for(i=0; i<size; i++) {
253 /* all is well, mark them as used */
255 for(i=0; i<size; i++) {
256 mark_page(pg++, USED);
259 set_intr_flag(intr_state);
263 int free_ppage_range(int start, int size)
267 for(i=0; i<size; i++) {
273 /* adds a range of physical memory to the available pool. used during init_mem
274 * when traversing the memory map.
276 static void add_memory(uint32_t start, size_t sz)
280 szpg = ADDR_TO_PAGE(sz + 4095);
281 pg = ADDR_TO_PAGE(start);
283 for(i=0; i<szpg; i++) {
284 mark_page(pg++, FREE);
288 /* maps a page as used or free in the allocation bitmap */
289 static void mark_page(int pg, int used)
291 int idx = BM_IDX(pg);
292 int bit = BM_BIT(pg);
295 bitmap[idx] |= 1 << bit;
297 bitmap[idx] &= ~(1 << bit);
301 void print_page_bitmap(void)
305 for(i=0; i<bmsize/4; i++) {
307 uint32_t pg = i * 32;
308 uint32_t addr = PAGE_TO_ADDR(pg);
309 printf("\n%5d [%08x]:", (int)pg, (unsigned long)addr);
311 printf(" %08x", bitmap[i]);