1 /* 2 * Copyright 2004-2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include <OS.h> 8 9 #include <kernel.h> 10 #include <boot/stage2.h> 11 #include <boot/platform.h> 12 13 #include <string.h> 14 15 16 static const size_t kChunkSize = 4 * B_PAGE_SIZE; 17 18 kernel_args gKernelArgs; 19 20 static void *sFirstFree; 21 static void *sLast; 22 static size_t sFree = kChunkSize; 23 24 25 static void 26 remove_range_index(addr_range *ranges, uint32 &numRanges, uint32 index) 27 { 28 if (index + 1 == numRanges) { 29 // remove last range 30 numRanges--; 31 return; 32 } 33 34 memmove(&ranges[index], &ranges[index + 1], sizeof(addr_range) * (numRanges - 1 - index)); 35 numRanges--; 36 } 37 38 39 static status_t 40 add_kernel_args_range(void *start, uint32 size) 41 { 42 return insert_address_range(gKernelArgs.kernel_args_range, 43 &gKernelArgs.num_kernel_args_ranges, MAX_KERNEL_ARGS_RANGE, 44 (addr_t)start, size); 45 } 46 47 48 // #pragma mark - addr_range utility 49 50 51 /** Inserts the specified (start, size) pair (aka range) in the 52 * addr_range array. 53 * It will extend existing ranges in order to have as little 54 * ranges in the array as possible. 55 * Returns B_OK on success, or B_ENTRY_NOT_FOUND if there was 56 * no free array entry available anymore. 57 */ 58 59 extern "C" status_t 60 insert_address_range(addr_range *ranges, uint32 *_numRanges, uint32 maxRanges, 61 addr_t start, uint32 size) 62 { 63 uint32 numRanges = *_numRanges; 64 65 start = ROUNDOWN(start, B_PAGE_SIZE); 66 size = ROUNDUP(size, B_PAGE_SIZE); 67 addr_t end = start + size; 68 69 for (uint32 i = 0; i < numRanges; i++) { 70 addr_t rangeStart = ranges[i].start; 71 addr_t rangeEnd = rangeStart + ranges[i].size; 72 73 if (end < rangeStart || start > rangeEnd) { 74 // ranges don't intersect or touch each other 75 continue; 76 } 77 if (start >= rangeStart && end <= rangeEnd) { 78 // range is already completely covered 79 return B_OK; 80 } 81 82 if (start < rangeStart) { 83 // prepend to the existing range 84 ranges[i].start = start; 85 ranges[i].size += rangeStart - start; 86 } 87 if (end > ranges[i].start + ranges[i].size) { 88 // append to the existing range 89 ranges[i].size = end - ranges[i].start; 90 } 91 92 // join ranges if possible 93 94 for (uint32 j = 0; j < numRanges; j++) { 95 if (i == j) 96 continue; 97 98 rangeStart = ranges[i].start; 99 rangeEnd = rangeStart + ranges[i].size; 100 addr_t joinStart = ranges[j].start; 101 addr_t joinEnd = joinStart + ranges[j].size; 102 103 if (rangeStart <= joinEnd && joinEnd <= rangeEnd) { 104 // join range that used to be before the current one, or 105 // the one that's now entirely included by the current one 106 if (joinStart < rangeStart) { 107 ranges[i].size += rangeStart - joinStart; 108 ranges[i].start = joinStart; 109 } 110 111 remove_range_index(ranges, numRanges, j--); 112 } else if (joinStart <= rangeEnd && joinEnd > rangeEnd) { 113 // join range that used to be after the current one 114 ranges[i].size += joinEnd - rangeEnd; 115 116 remove_range_index(ranges, numRanges, j--); 117 } 118 } 119 120 *_numRanges = numRanges; 121 return B_OK; 122 } 123 124 // no range matched, we need to create a new one 125 126 if (numRanges >= maxRanges) 127 return B_ENTRY_NOT_FOUND; 128 129 ranges[numRanges].start = (addr_t)start; 130 ranges[numRanges].size = size; 131 (*_numRanges)++; 132 133 return B_OK; 134 } 135 136 137 extern "C" status_t 138 remove_address_range(addr_range *ranges, uint32 *_numRanges, uint32 maxRanges, 139 addr_t start, uint32 size) 140 { 141 uint32 numRanges = *_numRanges; 142 143 addr_t end = ROUNDUP(start + size, B_PAGE_SIZE); 144 start = ROUNDOWN(start, B_PAGE_SIZE); 145 146 for (uint32 i = 0; i < numRanges; i++) { 147 addr_t rangeStart = ranges[i].start; 148 addr_t rangeEnd = rangeStart + ranges[i].size; 149 150 if (start <= rangeStart) { 151 if (end <= rangeStart) { 152 // no intersection 153 } else if (end >= rangeEnd) { 154 // remove the complete range 155 remove_range_index(ranges, numRanges, i); 156 i--; 157 } else { 158 // remove the head of the range 159 ranges[i].start = end; 160 ranges[i].size = rangeEnd - end; 161 } 162 } else if (end >= rangeEnd) { 163 if (start < rangeEnd) { 164 // remove the tail 165 ranges[i].size = start - rangeStart; 166 } // else: no intersection 167 } else { 168 // rangeStart < start < end < rangeEnd 169 // The ugly case: We have to remove something from the middle of 170 // the range. We keep the head of the range and insert its tail 171 // as a new range. 172 ranges[i].size = start - rangeStart; 173 return insert_address_range(ranges, _numRanges, maxRanges, 174 end, rangeEnd - end); 175 } 176 } 177 178 *_numRanges = numRanges; 179 return B_OK; 180 } 181 182 183 status_t 184 insert_physical_memory_range(addr_t start, uint32 size) 185 { 186 return insert_address_range(gKernelArgs.physical_memory_range, 187 &gKernelArgs.num_physical_memory_ranges, MAX_PHYSICAL_MEMORY_RANGE, 188 start, size); 189 } 190 191 192 status_t 193 insert_physical_allocated_range(addr_t start, uint32 size) 194 { 195 return insert_address_range(gKernelArgs.physical_allocated_range, 196 &gKernelArgs.num_physical_allocated_ranges, MAX_PHYSICAL_ALLOCATED_RANGE, 197 start, size); 198 } 199 200 201 status_t 202 insert_virtual_allocated_range(addr_t start, uint32 size) 203 { 204 return insert_address_range(gKernelArgs.virtual_allocated_range, 205 &gKernelArgs.num_virtual_allocated_ranges, MAX_VIRTUAL_ALLOCATED_RANGE, 206 start, size); 207 } 208 209 210 // #pragma mark - kernel_args allocations 211 212 213 /** This function can be used to allocate memory that is going 214 * to be passed over to the kernel. For example, the preloaded_image 215 * structures are allocated this way. 216 * The boot loader heap doesn't make it into the kernel! 217 */ 218 219 extern "C" void * 220 kernel_args_malloc(size_t size) 221 { 222 //dprintf("kernel_args_malloc(): %ld bytes (%ld bytes left)\n", size, sFree); 223 224 if (sFirstFree != NULL && size <= sFree) { 225 // there is enough space in the current buffer 226 void *address = sFirstFree; 227 sFirstFree = (void *)((addr_t)sFirstFree + size); 228 sLast = address; 229 sFree -= size; 230 231 return address; 232 } 233 234 if (size > kChunkSize / 2 && sFree < size) { 235 // the block is so large, we'll allocate a new block for it 236 void *block = NULL; 237 if (platform_allocate_region(&block, size, B_READ_AREA | B_WRITE_AREA, 238 false) != B_OK) { 239 return NULL; 240 } 241 242 if (add_kernel_args_range(block, size) != B_OK) 243 panic("kernel_args max range to low!\n"); 244 return block; 245 } 246 247 // just allocate a new block and "close" the old one 248 void *block = NULL; 249 if (platform_allocate_region(&block, kChunkSize, 250 B_READ_AREA | B_WRITE_AREA, false) != B_OK) { 251 return NULL; 252 } 253 254 sFirstFree = (void *)((addr_t)block + size); 255 sLast = block; 256 sFree = kChunkSize - size; 257 if (add_kernel_args_range(block, kChunkSize) != B_OK) 258 panic("kernel_args max range to low!\n"); 259 260 return block; 261 } 262 263 264 /** Convenience function that copies strdup() functions for the 265 * kernel args heap. 266 */ 267 268 extern "C" char * 269 kernel_args_strdup(const char *string) 270 { 271 if (string == NULL || string[0] == '\0') 272 return NULL; 273 274 size_t length = strlen(string) + 1; 275 276 char *target = (char *)kernel_args_malloc(length); 277 if (target == NULL) 278 return NULL; 279 280 memcpy(target, string, length); 281 return target; 282 } 283 284 285 /** This function frees a block allocated via kernel_args_malloc(). 286 * It's very simple; it can only free the last allocation. It's 287 * enough for its current usage in the boot loader, though. 288 */ 289 290 extern "C" void 291 kernel_args_free(void *block) 292 { 293 if (sLast != block) { 294 // sorry, we're dumb 295 return; 296 } 297 298 sFree = (addr_t)sFirstFree - (addr_t)sLast; 299 sFirstFree = block; 300 sLast = NULL; 301 } 302 303