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/>.
27 #define BM_IDX(pg) ((pg) / 32)
28 #define BM_BIT(pg) ((pg) & 0x1f)
30 #define IS_FREE(pg) ((bitmap[BM_IDX(pg)] & (1 << BM_BIT(pg))) == 0)
32 #define MEM_START ((uint32_t)&_mem_start)
40 static void mark_page(int pg, int used);
41 static void add_memory(uint32_t start, size_t size);
43 #define MAX_MAP_SIZE 16
44 extern struct mem_range boot_mem_map[MAX_MAP_SIZE];
45 extern int boot_mem_map_size;
47 /* linker supplied symbol, points to the end of the kernel image */
48 extern uint32_t _mem_start;
51 /* A bitmap is used to track which physical memory pages are used, and which
52 * are available for allocation by alloc_ppage.
54 * last_alloc_idx keeps track of the last 32bit element in the bitmap array
55 * where a free page was found. It's guaranteed that all the elements before
56 * this have no free pages, but it doesn't imply that there will be another
57 * free page there. So it's used as a starting point for the search.
59 static uint32_t *bitmap;
60 static int bmsize, last_alloc_idx;
66 int i, pg, max_pg = 0;
67 uint32_t used_end, start, end, sz, total = 0, rem;
68 const char *suffix[] = {"bytes", "KB", "MB", "GB"};
72 /* the allocation bitmap starts right at the end of the kernel image */
73 bitmap = (uint32_t*)&_mem_start;
75 /* start by marking all posible pages (2**20) as used. We do not "reserve"
76 * all this space. Pages beyond the end of the useful bitmap area
77 * ((char*)bitmap + bmsize), which will be determined after we traverse the
78 * memory map, are going to be marked as available for allocation.
80 memset(bitmap, 0xff, 1024 * 1024 / 8);
83 if(boot_mem_map_size <= 0 || boot_mem_map_size > MAX_MAP_SIZE) {
84 panic("invalid memory map size reported by the boot loader: %d\n", boot_mem_map_size);
87 printf("Memory map:\n");
88 for(i=0; i<boot_mem_map_size; i++) {
89 printf(" start: %08x - size: %08x\n", (unsigned int)boot_mem_map[i].start,
90 (unsigned int)boot_mem_map[i].size);
92 start = boot_mem_map[i].start;
93 sz = boot_mem_map[i].size & 0xfffffffc;
100 /* ignore any memory ranges which end before the end of the kernel image */
101 if(end < MEM_START) continue;
102 if(start < MEM_START) {
106 add_memory(start, sz);
109 pg = ADDR_TO_PAGE(end);
116 while(total > 1024) {
121 printf("Total usable RAM: %u.%u %s\n", total, 100 * rem / 1024, suffix[i]);
123 bmsize = max_pg / 8; /* size of the useful bitmap in bytes */
125 /* mark all pages occupied by the bitmap as usef */
126 used_end = (uint32_t)bitmap + bmsize - 1;
128 printf("marking pages up to %x (page: %d) as used\n", used_end, ADDR_TO_PAGE(used_end));
130 for(i=0; i<=used_end; i++) {
135 int alloc_ppage(void)
137 return alloc_ppages(1);
140 /* free_ppage marks the physical page, free in the allocation bitmap.
142 * CAUTION: no checks are done that this page should actually be freed or not.
143 * If you call free_ppage with the address of some part of memory that was
144 * originally reserved due to it being in a memory hole or part of the kernel
145 * image or whatever, it will be subsequently allocatable by alloc_ppage.
147 void free_ppage(int pg)
149 int bmidx = BM_IDX(pg);
151 int intr_state = get_intr_flag();
155 panic("free_ppage(%d): I thought that was already free!\n", pg);
159 if(bmidx < last_alloc_idx) {
160 last_alloc_idx = bmidx;
163 set_intr_flag(intr_state);
167 int alloc_ppages(int count)
169 int i, pg, idx, max, intr_state, found_free = 0;
171 intr_state = get_intr_flag();
174 idx = last_alloc_idx;
178 /* if at least one bit is 0 then we have at least
179 * one free page. find it and try to allocate a range starting from there
181 if(bitmap[idx] != 0xffffffff) {
182 for(i=0; i<32; i++) {
187 last_alloc_idx = idx;
191 if(alloc_ppage_range(pg, count) != -1) {
192 set_intr_flag(intr_state);
201 set_intr_flag(intr_state);
205 void free_ppages(int pg0, int count)
209 for(i=0; i<count; i++) {
214 int alloc_ppage_range(int start, int size)
219 intr_state = get_intr_flag();
222 /* first validate that no page in the requested range is allocated */
223 for(i=0; i<size; i++) {
230 /* all is well, mark them as used */
232 for(i=0; i<size; i++) {
233 mark_page(pg++, USED);
236 set_intr_flag(intr_state);
240 int free_ppage_range(int start, int size)
244 for(i=0; i<size; i++) {
250 /* adds a range of physical memory to the available pool. used during init_mem
251 * when traversing the memory map.
253 static void add_memory(uint32_t start, size_t sz)
257 szpg = ADDR_TO_PAGE(sz + 4095);
258 pg = ADDR_TO_PAGE(start);
260 for(i=0; i<szpg; i++) {
261 mark_page(pg++, FREE);
265 /* maps a page as used or free in the allocation bitmap */
266 static void mark_page(int pg, int used)
268 int idx = BM_IDX(pg);
269 int bit = BM_BIT(pg);
272 bitmap[idx] |= 1 << bit;
274 bitmap[idx] &= ~(1 << bit);