kern

annotate src/mem.c @ 71:c7bd6ec7b946

changed test_proc to modify memory after the fork in order to try out copy-on-write, by pushing the result of getpid on the stack.
author John Tsiombikas <nuclear@member.fsf.org>
date Thu, 13 Oct 2011 05:22:35 +0300
parents 9939a6d7a45a
children
rev   line source
nuclear@17 1 #include <stdio.h>
nuclear@19 2 #include <string.h>
nuclear@17 3 #include "mem.h"
nuclear@17 4 #include "panic.h"
nuclear@17 5 #include "vm.h"
nuclear@25 6 #include "intr.h"
nuclear@17 7
nuclear@19 8 #define FREE 0
nuclear@19 9 #define USED 1
nuclear@19 10
nuclear@19 11 #define BM_IDX(pg) ((pg) / 32)
nuclear@19 12 #define BM_BIT(pg) ((pg) & 0x1f)
nuclear@19 13
nuclear@19 14 #define IS_FREE(pg) ((bitmap[BM_IDX(pg)] & (1 << BM_BIT(pg))) == 0)
nuclear@19 15
nuclear@19 16 static void mark_page(int pg, int free);
nuclear@19 17 static void add_memory(uint32_t start, size_t size);
nuclear@19 18
nuclear@17 19 /* end of kernel image */
nuclear@17 20 extern int _end;
nuclear@17 21
nuclear@20 22 /* A bitmap is used to track which physical memory pages are used or available
nuclear@20 23 * for allocation by alloc_phys_page.
nuclear@20 24 *
nuclear@20 25 * last_alloc_idx keeps track of the last 32bit element in the bitmap array
nuclear@20 26 * where a free page was found. It's guaranteed that all the elements before
nuclear@20 27 * this have no free pages, but it doesn't imply that there will be another
nuclear@20 28 * free page there. So it's used as a starting point for the search.
nuclear@20 29 */
nuclear@19 30 static uint32_t *bitmap;
nuclear@19 31 static int bmsize, last_alloc_idx;
nuclear@17 32
nuclear@20 33
nuclear@17 34 void init_mem(struct mboot_info *mb)
nuclear@17 35 {
nuclear@19 36 int i, num_pages, max_pg = 0;
nuclear@19 37 uint32_t used_end;
nuclear@19 38
nuclear@19 39 num_pages = 0;
nuclear@19 40 last_alloc_idx = 0;
nuclear@19 41
nuclear@20 42 /* the allocation bitmap starts right at the end of the ELF image */
nuclear@19 43 bitmap = (uint32_t*)&_end;
nuclear@19 44
nuclear@20 45 /* start by marking all posible pages (2**20) as used. We do not "reserve"
nuclear@20 46 * all this space. Pages beyond the end of the useful bitmap area
nuclear@20 47 * ((char*)bitmap + bmsize), which will be determined after we traverse the
nuclear@20 48 * memory map, are going to be marked as available for allocation.
nuclear@20 49 */
nuclear@19 50 memset(bitmap, 0xff, 1024 * 1024 / 8);
nuclear@19 51
nuclear@20 52 /* if the bootloader gave us an available memory map, traverse it and mark
nuclear@20 53 * all the corresponding pages as free.
nuclear@20 54 */
nuclear@19 55 if(mb->flags & MB_MMAP) {
nuclear@19 56 struct mboot_mmap *mem, *mmap_end;
nuclear@19 57
nuclear@19 58 mem = mb->mmap;
nuclear@19 59 mmap_end = (struct mboot_mmap*)((char*)mb->mmap + mb->mmap_len);
nuclear@19 60
nuclear@19 61 printf("memory map:\n");
nuclear@19 62 while(mem < mmap_end) {
nuclear@21 63 /* ignore memory ranges that start beyond the 4gb mark */
nuclear@21 64 if(mem->base_high == 0 && mem->base_low != 0xffffffff) {
nuclear@21 65 char *type;
nuclear@21 66 unsigned int end, rest = 0xffffffff - mem->base_low;
nuclear@19 67
nuclear@21 68 /* make sure the length does not extend beyond 4gb */
nuclear@21 69 if(mem->length_high || mem->length_low > rest) {
nuclear@21 70 mem->length_low = rest;
nuclear@21 71 }
nuclear@21 72 end = mem->base_low + mem->length_low;
nuclear@19 73
nuclear@21 74 if(mem->type == MB_MEM_VALID) {
nuclear@21 75 type = "free:";
nuclear@21 76 add_memory(mem->base_low, mem->length_low);
nuclear@21 77
nuclear@21 78 num_pages = ADDR_TO_PAGE(mem->base_low + mem->length_low);
nuclear@21 79 if(max_pg < num_pages) {
nuclear@21 80 max_pg = num_pages;
nuclear@21 81 }
nuclear@21 82 } else {
nuclear@21 83 type = "hole:";
nuclear@19 84 }
nuclear@21 85
nuclear@21 86 printf(" %s %x - %x (%u bytes)\n", type, mem->base_low, end, mem->length_low);
nuclear@19 87 }
nuclear@19 88 mem = (struct mboot_mmap*)((char*)mem + mem->skip + sizeof mem->skip);
nuclear@19 89 }
nuclear@19 90 } else if(mb->flags & MB_MEM) {
nuclear@20 91 /* if we don't have a detailed memory map, just use the lower and upper
nuclear@20 92 * memory block sizes to determine which pages should be available.
nuclear@20 93 */
nuclear@19 94 add_memory(0, mb->mem_lower);
nuclear@19 95 add_memory(0x100000, mb->mem_upper * 1024);
nuclear@19 96 max_pg = mb->mem_upper / 4;
nuclear@19 97
nuclear@19 98 printf("lower memory: %ukb, upper mem: %ukb\n", mb->mem_lower, mb->mem_upper);
nuclear@19 99 } else {
nuclear@20 100 /* I don't think this should ever happen with a multiboot-compliant boot loader */
nuclear@19 101 panic("didn't get any memory info from the boot loader, I give up\n");
nuclear@19 102 }
nuclear@19 103
nuclear@20 104 bmsize = max_pg / 8; /* size of the useful bitmap in bytes */
nuclear@19 105
nuclear@19 106 /* mark all the used pages as ... well ... used */
nuclear@19 107 used_end = ((uint32_t)bitmap + bmsize - 1);
nuclear@19 108
nuclear@19 109 printf("marking pages up to %x ", used_end);
nuclear@19 110 used_end = ADDR_TO_PAGE(used_end);
nuclear@19 111 printf("(page: %d) inclusive as used\n", used_end);
nuclear@19 112
nuclear@19 113 for(i=0; i<=used_end; i++) {
nuclear@19 114 mark_page(i, USED);
nuclear@19 115 }
nuclear@17 116 }
nuclear@17 117
nuclear@20 118 /* alloc_phys_page finds the first available page of physical memory,
nuclear@20 119 * marks it as used in the bitmap, and returns its address. If there's
nuclear@20 120 * no unused physical page, 0 is returned.
nuclear@20 121 */
nuclear@17 122 uint32_t alloc_phys_page(void)
nuclear@17 123 {
nuclear@25 124 int i, idx, max, intr_state;
nuclear@25 125
nuclear@25 126 intr_state = get_intr_state();
nuclear@25 127 disable_intr();
nuclear@17 128
nuclear@19 129 idx = last_alloc_idx;
nuclear@19 130 max = bmsize / 4;
nuclear@19 131
nuclear@19 132 while(idx <= max) {
nuclear@19 133 /* if at least one bit is 0 then we have at least
nuclear@19 134 * one free page. find it and allocate it.
nuclear@19 135 */
nuclear@19 136 if(bitmap[idx] != 0xffffffff) {
nuclear@19 137 for(i=0; i<32; i++) {
nuclear@19 138 int pg = idx * 32 + i;
nuclear@19 139
nuclear@19 140 if(IS_FREE(pg)) {
nuclear@19 141 mark_page(pg, USED);
nuclear@19 142
nuclear@19 143 last_alloc_idx = idx;
nuclear@19 144
nuclear@46 145 /*printf("alloc_phys_page() -> %x (page: %d)\n", PAGE_TO_ADDR(pg), pg);*/
nuclear@25 146
nuclear@25 147 set_intr_state(intr_state);
nuclear@19 148 return PAGE_TO_ADDR(pg);
nuclear@19 149 }
nuclear@19 150 }
nuclear@19 151 panic("can't happen: alloc_phys_page (mem.c)\n");
nuclear@19 152 }
nuclear@19 153 idx++;
nuclear@17 154 }
nuclear@17 155
nuclear@25 156 set_intr_state(intr_state);
nuclear@19 157 return 0;
nuclear@19 158 }
nuclear@19 159
nuclear@20 160 /* free_phys_page marks the physical page which corresponds to the specified
nuclear@20 161 * address as free in the allocation bitmap.
nuclear@20 162 *
nuclear@20 163 * CAUTION: no checks are done that this page should actually be freed or not.
nuclear@20 164 * If you call free_phys_page with the address of some part of memory that was
nuclear@20 165 * originally reserved due to it being in a memory hole or part of the kernel
nuclear@20 166 * image or whatever, it will be subsequently allocatable by alloc_phys_page.
nuclear@20 167 */
nuclear@19 168 void free_phys_page(uint32_t addr)
nuclear@19 169 {
nuclear@19 170 int pg = ADDR_TO_PAGE(addr);
nuclear@19 171 int bmidx = BM_IDX(pg);
nuclear@19 172
nuclear@25 173 int intr_state = get_intr_state();
nuclear@25 174 disable_intr();
nuclear@25 175
nuclear@46 176 if(IS_FREE(pg)) {
nuclear@19 177 panic("free_phys_page(%d): I thought that was already free!\n", pg);
nuclear@17 178 }
nuclear@17 179
nuclear@19 180 mark_page(pg, FREE);
nuclear@19 181 if(bmidx < last_alloc_idx) {
nuclear@19 182 last_alloc_idx = bmidx;
nuclear@19 183 }
nuclear@25 184
nuclear@25 185 set_intr_state(intr_state);
nuclear@19 186 }
nuclear@17 187
nuclear@20 188 /* this is only ever used by the VM init code to find out what the extends of
nuclear@20 189 * the kernel image are, in order to map them 1-1 before enabling paging.
nuclear@20 190 */
nuclear@19 191 void get_kernel_mem_range(uint32_t *start, uint32_t *end)
nuclear@19 192 {
nuclear@19 193 if(start) {
nuclear@19 194 *start = 0x100000;
nuclear@19 195 }
nuclear@19 196 if(end) {
nuclear@19 197 uint32_t e = (uint32_t)bitmap + bmsize;
nuclear@19 198
nuclear@19 199 if(e & PGOFFS_MASK) {
nuclear@23 200 *end = (e + 4096) & ~PGOFFS_MASK;
nuclear@19 201 } else {
nuclear@19 202 *end = e;
nuclear@19 203 }
nuclear@19 204 }
nuclear@17 205 }
nuclear@19 206
nuclear@20 207 /* adds a range of physical memory to the available pool. used during init_mem
nuclear@20 208 * when traversing the memory map.
nuclear@20 209 */
nuclear@19 210 static void add_memory(uint32_t start, size_t sz)
nuclear@19 211 {
nuclear@19 212 int i, szpg, pg;
nuclear@19 213
nuclear@19 214 szpg = ADDR_TO_PAGE(sz);
nuclear@19 215 pg = ADDR_TO_PAGE(start);
nuclear@19 216
nuclear@19 217 for(i=0; i<szpg; i++) {
nuclear@19 218 mark_page(pg++, FREE);
nuclear@19 219 }
nuclear@19 220 }
nuclear@19 221
nuclear@20 222 /* maps a page as used or free in the allocation bitmap */
nuclear@19 223 static void mark_page(int pg, int used)
nuclear@19 224 {
nuclear@19 225 int idx = BM_IDX(pg);
nuclear@19 226 int bit = BM_BIT(pg);
nuclear@19 227
nuclear@19 228 if(used) {
nuclear@19 229 bitmap[idx] |= 1 << bit;
nuclear@19 230 } else {
nuclear@19 231 bitmap[idx] &= ~(1 << bit);
nuclear@19 232 }
nuclear@19 233 }
nuclear@19 234