kern

annotate src/mem.c @ 80:4db99a52863e

fixed the "endianess" of the text messages in the ATA identify info block. this is the first time I've seen wrong byteorder in ascii text, the ATA committee should be commended.
author John Tsiombikas <nuclear@member.fsf.org>
date Tue, 06 Dec 2011 13:35:39 +0200
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