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, 157 false) != B_OK) { 158 return NULL; 159 } 160 161 if (add_kernel_args_range(block, size) != B_OK) 162 panic("kernel_args max range to low!\n"); 163 return block; 164 } 165 166 // just allocate a new block and "close" the old one 167 void *block = NULL; 168 if (platform_allocate_region(&block, kChunkSize, 169 B_READ_AREA | B_WRITE_AREA, false) != B_OK) { 170 return NULL; 171 } 172 173 sFirstFree = (void *)((addr_t)block + size); 174 sLast = block; 175 sFree = kChunkSize - size; 176 if (add_kernel_args_range(block, kChunkSize) != B_OK) 177 panic("kernel_args max range to low!\n"); 178 179 return block; 180 } 181 182 183 /** Convenience function that copies strdup() functions for the 184 * kernel args heap. 185 */ 186 187 extern "C" char * 188 kernel_args_strdup(const char *string) 189 { 190 if (string == NULL || string[0] == '\0') 191 return NULL; 192 193 size_t length = strlen(string) + 1; 194 195 char *target = (char *)kernel_args_malloc(length); 196 if (target == NULL) 197 return NULL; 198 199 memcpy(target, string, length); 200 return target; 201 } 202 203 204 /** This function frees a block allocated via kernel_args_malloc(). 205 * It's very simple; it can only free the last allocation. It's 206 * enough for its current usage in the boot loader, though. 207 */ 208 209 extern "C" void 210 kernel_args_free(void *block) 211 { 212 if (sLast != block) { 213 // sorry, we're dumb 214 return; 215 } 216 217 sFree = (addr_t)sFirstFree - (addr_t)sLast; 218 sFirstFree = block; 219 sLast = NULL; 220 } 221 222