1 /* 2 ** Copyright 2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 3 ** Distributed under the terms of the Haiku 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 = 16384; 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 /** Inserts the specified (start, size) pair (aka range) in the 40 * addr_range array. 41 * It will extend existing ranges in order to have as little 42 * ranges in the array as possible. 43 * Returns B_OK on success, or B_ENTRY_NOT_FOUND if there was 44 * no free array entry available anymore. 45 */ 46 47 extern "C" status_t 48 insert_address_range(addr_range *ranges, uint32 *_numRanges, uint32 maxRanges, 49 addr_t start, uint32 size) 50 { 51 uint32 numRanges = *_numRanges; 52 53 start = ROUNDOWN(start, B_PAGE_SIZE); 54 size = ROUNDUP(size, B_PAGE_SIZE); 55 addr_t end = start + size; 56 57 for (uint32 i = 0; i < numRanges; i++) { 58 addr_t rangeStart = ranges[i].start; 59 addr_t rangeEnd = rangeStart + ranges[i].size; 60 61 if (end < rangeStart || start > rangeEnd) { 62 // ranges don't intersect or touch each other 63 continue; 64 } 65 if (start >= rangeStart && end <= rangeEnd) { 66 // range is already completely covered 67 return B_OK; 68 } 69 70 if (start < rangeStart) { 71 // prepend to the existing range 72 ranges[i].start = start; 73 ranges[i].size += rangeStart - start; 74 } 75 if (end > ranges[i].start + ranges[i].size) { 76 // append to the existing range 77 ranges[i].size = end - ranges[i].start; 78 } 79 80 // join ranges if possible 81 82 for (uint32 j = 0; j < numRanges; j++) { 83 if (i == j) 84 continue; 85 86 rangeStart = ranges[i].start; 87 rangeEnd = rangeStart + ranges[i].size; 88 addr_t joinStart = ranges[j].start; 89 addr_t joinEnd = joinStart + ranges[j].size; 90 91 if (rangeStart <= joinEnd && joinEnd <= rangeEnd) { 92 // join range that used to be before the current one, or 93 // the one that's now entirely included by the current one 94 if (joinStart < rangeStart) { 95 ranges[i].size += rangeStart - joinStart; 96 ranges[i].start = joinStart; 97 } 98 99 remove_range_index(ranges, numRanges, j--); 100 } else if (joinStart <= rangeEnd && joinEnd > rangeEnd) { 101 // join range that used to be after the current one 102 ranges[i].size += joinEnd - rangeEnd; 103 104 remove_range_index(ranges, numRanges, j--); 105 } 106 } 107 108 *_numRanges = numRanges; 109 return B_OK; 110 } 111 112 // no range matched, we need to create a new one 113 114 if (numRanges >= maxRanges) 115 return B_ENTRY_NOT_FOUND; 116 117 ranges[numRanges].start = (addr_t)start; 118 ranges[numRanges].size = size; 119 (*_numRanges)++; 120 121 return B_OK; 122 } 123 124 125 static status_t 126 add_kernel_args_range(void *start, uint32 size) 127 { 128 return insert_address_range(gKernelArgs.kernel_args_range, 129 &gKernelArgs.num_kernel_args_ranges, MAX_KERNEL_ARGS_RANGE, 130 (addr_t)start, size); 131 } 132 133 134 /** This function can be used to allocate memory that is going 135 * to be passed over to the kernel. For example, the preloaded_image 136 * structures are allocated this way. 137 * The boot loader heap doesn't make it into the kernel! 138 */ 139 140 extern "C" void * 141 kernel_args_malloc(size_t size) 142 { 143 if (sFirstFree != NULL && size <= sFree) { 144 // there is enough space in the current buffer 145 void *address = sFirstFree; 146 sFirstFree = (void *)((addr_t)sFirstFree + size); 147 sLast = address; 148 sFree -= size; 149 150 return address; 151 } 152 153 if (size > kChunkSize / 2 && sFree < size) { 154 // the block is so large, we'll allocate a new block for it 155 void *block = NULL; 156 if (platform_allocate_region(&block, size, B_READ_AREA | B_WRITE_AREA) != B_OK) 157 return NULL; 158 159 if (add_kernel_args_range(block, size) != B_OK) 160 panic("kernel_args max range to low!\n"); 161 return block; 162 } 163 164 // just allocate a new block and "close" the old one 165 void *block = NULL; 166 if (platform_allocate_region(&block, kChunkSize, B_READ_AREA | B_WRITE_AREA) != B_OK) 167 return NULL; 168 169 sFirstFree = (void *)((addr_t)block + size); 170 sLast = block; 171 sFree = kChunkSize - size; 172 if (add_kernel_args_range(block, kChunkSize) != B_OK) 173 panic("kernel_args max range to low!\n"); 174 175 return block; 176 } 177 178 179 /** Convenience function that copies strdup() functions for the 180 * kernel args heap. 181 */ 182 183 extern "C" char * 184 kernel_args_strdup(const char *string) 185 { 186 if (string == NULL || string[0] == '\0') 187 return NULL; 188 189 size_t length = strlen(string) + 1; 190 191 char *target = (char *)kernel_args_malloc(length); 192 if (target == NULL) 193 return NULL; 194 195 memcpy(target, string, length); 196 return target; 197 } 198 199 200 /** This function frees a block allocated via kernel_args_malloc(). 201 * It's very simple; it can only free the last allocation. It's 202 * enough for its current usage in the boot loader, though. 203 */ 204 205 extern "C" void 206 kernel_args_free(void *block) 207 { 208 if (sLast != block) { 209 // sorry, we're dumb 210 return; 211 } 212 213 sFree = (addr_t)sFirstFree - (addr_t)sLast; 214 sFirstFree = block; 215 sLast = NULL; 216 } 217 218