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_phys_page.
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 /* alloc_phys_page finds the first available page of physical memory,
136 * marks it as used in the bitmap, and returns its number. If there's
137 * no unused physical page, -1 is returned.
139 int alloc_ppage(void)
141 int i, idx, max, intr_state;
143 intr_state = get_intr_flag();
146 idx = last_alloc_idx;
150 /* if at least one bit is 0 then we have at least
151 * one free page. find it and allocate it.
153 if(bitmap[idx] != 0xffffffff) {
154 for(i=0; i<32; i++) {
155 int pg = idx * 32 + i;
160 last_alloc_idx = idx;
162 /*printf("alloc_phys_page() -> %x (page: %d)\n", PAGE_TO_ADDR(pg), pg);*/
164 set_intr_flag(intr_state);
168 panic("can't happen: alloc_ppage (mem.c)\n");
173 set_intr_flag(intr_state);
177 /* free_ppage marks the physical page, free in the allocation bitmap.
179 * CAUTION: no checks are done that this page should actually be freed or not.
180 * If you call free_phys_page with the address of some part of memory that was
181 * originally reserved due to it being in a memory hole or part of the kernel
182 * image or whatever, it will be subsequently allocatable by alloc_phys_page.
184 void free_ppage(int pg)
186 int bmidx = BM_IDX(pg);
188 int intr_state = get_intr_flag();
192 panic("free_ppage(%d): I thought that was already free!\n", pg);
196 if(bmidx < last_alloc_idx) {
197 last_alloc_idx = bmidx;
200 set_intr_flag(intr_state);
204 int alloc_ppage_range(int start, int size)
208 /* first validate that no page in the requested range is allocated */
209 for(i=0; i<size; i++) {
216 /* all is well, mark them as used */
218 for(i=0; i<size; i++) {
219 mark_page(pg++, USED);
224 int free_ppage_range(int start, int size)
228 for(i=0; i<size; i++) {
234 /* adds a range of physical memory to the available pool. used during init_mem
235 * when traversing the memory map.
237 static void add_memory(uint32_t start, size_t sz)
241 szpg = ADDR_TO_PAGE(sz + 4095);
242 pg = ADDR_TO_PAGE(start);
244 for(i=0; i<szpg; i++) {
245 mark_page(pg++, FREE);
249 /* maps a page as used or free in the allocation bitmap */
250 static void mark_page(int pg, int used)
252 int idx = BM_IDX(pg);
253 int bit = BM_BIT(pg);
256 bitmap[idx] |= 1 << bit;
258 bitmap[idx] &= ~(1 << bit);