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 /* size of the useful part of the bitmap in bytes padded to 4-byte
124 * boundaries to allow 32bit at a time operations.
126 bmsize = (max_pg / 32 + 1) * 4;
128 /* mark all pages occupied by the bitmap as used */
129 used_end = (uint32_t)bitmap + bmsize - 1;
131 max_pg = ADDR_TO_PAGE(used_end);
132 printf("marking pages up to %x (page: %d) as used\n", used_end, max_pg);
133 for(i=0; i<=max_pg; i++) {
138 int alloc_ppage(void)
140 return alloc_ppages(1);
143 /* free_ppage marks the physical page, free in the allocation bitmap.
145 * CAUTION: no checks are done that this page should actually be freed or not.
146 * If you call free_ppage with the address of some part of memory that was
147 * originally reserved due to it being in a memory hole or part of the kernel
148 * image or whatever, it will be subsequently allocatable by alloc_ppage.
150 void free_ppage(int pg)
152 int bmidx = BM_IDX(pg);
154 int intr_state = get_intr_flag();
158 panic("free_ppage(%d): I thought that was already free!\n", pg);
162 if(bmidx < last_alloc_idx) {
163 last_alloc_idx = bmidx;
166 set_intr_flag(intr_state);
170 int alloc_ppages(int count)
172 int i, pg, idx, max, intr_state, found_free = 0;
174 intr_state = get_intr_flag();
177 idx = last_alloc_idx;
181 /* if at least one bit is 0 then we have at least
182 * one free page. find it and try to allocate a range starting from there
184 if(bitmap[idx] != 0xffffffff) {
185 for(i=0; i<32; i++) {
190 last_alloc_idx = idx;
194 if(alloc_ppage_range(pg, count) != -1) {
195 set_intr_flag(intr_state);
204 set_intr_flag(intr_state);
208 void free_ppages(int pg0, int count)
212 for(i=0; i<count; i++) {
217 int alloc_ppage_range(int start, int size)
222 intr_state = get_intr_flag();
225 /* first validate that no page in the requested range is allocated */
226 for(i=0; i<size; i++) {
233 /* all is well, mark them as used */
235 for(i=0; i<size; i++) {
236 mark_page(pg++, USED);
239 set_intr_flag(intr_state);
243 int free_ppage_range(int start, int size)
247 for(i=0; i<size; i++) {
253 /* adds a range of physical memory to the available pool. used during init_mem
254 * when traversing the memory map.
256 static void add_memory(uint32_t start, size_t sz)
260 szpg = ADDR_TO_PAGE(sz + 4095);
261 pg = ADDR_TO_PAGE(start);
263 for(i=0; i<szpg; i++) {
264 mark_page(pg++, FREE);
268 /* maps a page as used or free in the allocation bitmap */
269 static void mark_page(int pg, int used)
271 int idx = BM_IDX(pg);
272 int bit = BM_BIT(pg);
275 bitmap[idx] |= 1 << bit;
277 bitmap[idx] &= ~(1 << bit);