1 /*
2 * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2004-2008, Axel Dörfler, axeld@pinc-software.de.
4 * Distributed under the terms of the MIT License.
5 */
6
7
8 #include <OS.h>
9
10 #include <string.h>
11
12 #include <algorithm>
13
14 #include <kernel.h>
15 #include <boot/stage2.h>
16 #include <boot/platform.h>
17
18
19 static const size_t kChunkSize = 16 * B_PAGE_SIZE;
20
21 kernel_args gKernelArgs;
22 KMessage gBootVolume;
23
24 static void* sFirstFree;
25 static void* sLast;
26 static size_t sFree = kChunkSize;
27
28
29 static status_t
add_kernel_args_range(void * start,size_t size)30 add_kernel_args_range(void* start, size_t size)
31 {
32 return insert_address_range(gKernelArgs.kernel_args_range,
33 &gKernelArgs.num_kernel_args_ranges, MAX_KERNEL_ARGS_RANGE,
34 (addr_t)start, size);
35 }
36
37
38 // #pragma mark - addr_range utility functions
39
40
41 static void
remove_range_index(addr_range * ranges,uint32 & numRanges,uint32 index)42 remove_range_index(addr_range* ranges, uint32& numRanges, uint32 index)
43 {
44 if (index + 1 == numRanges) {
45 // remove last range
46 numRanges--;
47 return;
48 }
49
50 memmove(&ranges[index], &ranges[index + 1],
51 sizeof(addr_range) * (numRanges - 1 - index));
52 numRanges--;
53 }
54
55
56 /*! Inserts the specified (start, size) pair (aka range) in the
57 addr_range array.
58 It will extend existing ranges in order to have as little
59 ranges in the array as possible.
60 Returns B_OK on success, or B_ENTRY_NOT_FOUND if there was
61 no free array entry available anymore.
62 */
63 extern "C" status_t
insert_address_range(addr_range * ranges,uint32 * _numRanges,uint32 maxRanges,uint64 start,uint64 size)64 insert_address_range(addr_range* ranges, uint32* _numRanges, uint32 maxRanges,
65 uint64 start, uint64 size)
66 {
67 uint32 numRanges = *_numRanges;
68
69 start = ROUNDDOWN(start, B_PAGE_SIZE);
70 size = ROUNDUP(size, B_PAGE_SIZE);
71 uint64 end = start + size;
72
73 for (uint32 i = 0; i < numRanges; i++) {
74 uint64 rangeStart = ranges[i].start;
75 uint64 rangeEnd = rangeStart + ranges[i].size;
76
77 if (end < rangeStart || start > rangeEnd) {
78 // ranges don't intersect or touch each other
79 continue;
80 }
81 if (start >= rangeStart && end <= rangeEnd) {
82 // range is already completely covered
83 return B_OK;
84 }
85
86 if (start < rangeStart) {
87 // prepend to the existing range
88 ranges[i].start = start;
89 ranges[i].size += rangeStart - start;
90 }
91 if (end > ranges[i].start + ranges[i].size) {
92 // append to the existing range
93 ranges[i].size = end - ranges[i].start;
94 }
95
96 // join ranges if possible
97
98 for (uint32 j = 0; j < numRanges; j++) {
99 if (i == j)
100 continue;
101
102 rangeStart = ranges[i].start;
103 rangeEnd = rangeStart + ranges[i].size;
104 uint64 joinStart = ranges[j].start;
105 uint64 joinEnd = joinStart + ranges[j].size;
106
107 if (rangeStart <= joinEnd && joinEnd <= rangeEnd) {
108 // join range that used to be before the current one, or
109 // the one that's now entirely included by the current one
110 if (joinStart < rangeStart) {
111 ranges[i].size += rangeStart - joinStart;
112 ranges[i].start = joinStart;
113 }
114
115 remove_range_index(ranges, numRanges, j--);
116 } else if (joinStart <= rangeEnd && joinEnd > rangeEnd) {
117 // join range that used to be after the current one
118 ranges[i].size += joinEnd - rangeEnd;
119
120 remove_range_index(ranges, numRanges, j--);
121 }
122 }
123
124 *_numRanges = numRanges;
125 return B_OK;
126 }
127
128 // no range matched, we need to create a new one
129
130 if (numRanges >= maxRanges)
131 return B_ENTRY_NOT_FOUND;
132
133 ranges[numRanges].start = (uint64)start;
134 ranges[numRanges].size = size;
135 (*_numRanges)++;
136
137 return B_OK;
138 }
139
140
141 extern "C" status_t
remove_address_range(addr_range * ranges,uint32 * _numRanges,uint32 maxRanges,uint64 start,uint64 size)142 remove_address_range(addr_range* ranges, uint32* _numRanges, uint32 maxRanges,
143 uint64 start, uint64 size)
144 {
145 uint32 numRanges = *_numRanges;
146
147 uint64 end = ROUNDUP(start + size, B_PAGE_SIZE);
148 start = ROUNDDOWN(start, B_PAGE_SIZE);
149
150 for (uint32 i = 0; i < numRanges; i++) {
151 uint64 rangeStart = ranges[i].start;
152 uint64 rangeEnd = rangeStart + ranges[i].size;
153
154 if (start <= rangeStart) {
155 if (end <= rangeStart) {
156 // no intersection
157 } else if (end >= rangeEnd) {
158 // remove the complete range
159 remove_range_index(ranges, numRanges, i);
160 i--;
161 } else {
162 // remove the head of the range
163 ranges[i].start = end;
164 ranges[i].size = rangeEnd - end;
165 }
166 } else if (end >= rangeEnd) {
167 if (start < rangeEnd) {
168 // remove the tail
169 ranges[i].size = start - rangeStart;
170 } // else: no intersection
171 } else {
172 // rangeStart < start < end < rangeEnd
173 // The ugly case: We have to remove something from the middle of
174 // the range. We keep the head of the range and insert its tail
175 // as a new range.
176 ranges[i].size = start - rangeStart;
177 return insert_address_range(ranges, _numRanges, maxRanges, end,
178 rangeEnd - end);
179 }
180 }
181
182 *_numRanges = numRanges;
183 return B_OK;
184 }
185
186
187 bool
get_free_address_range(addr_range * ranges,uint32 numRanges,uint64 base,uint64 size,uint64 * _rangeBase)188 get_free_address_range(addr_range* ranges, uint32 numRanges, uint64 base,
189 uint64 size, uint64* _rangeBase)
190 {
191 uint64 end = base + size - 1;
192 if (end < base)
193 return false;
194
195 // Note: We don't assume that the ranges are sorted, so we can't do this
196 // in a simple loop. Instead we restart the loop whenever our range
197 // intersects with an existing one.
198
199 for (uint32 i = 0; i < numRanges;) {
200 uint64 rangeStart = ranges[i].start;
201 uint64 rangeEnd = ranges[i].start + ranges[i].size - 1;
202
203 if (base <= rangeEnd && rangeStart <= end) {
204 base = rangeEnd + 1;
205 end = rangeEnd + size;
206 if (base == 0 || end < base)
207 return false;
208
209 i = 0;
210 continue;
211 }
212
213 i++;
214 }
215
216 *_rangeBase = base;
217 return true;
218 }
219
220
221 bool
is_address_range_covered(addr_range * ranges,uint32 numRanges,uint64 base,uint64 size)222 is_address_range_covered(addr_range* ranges, uint32 numRanges, uint64 base,
223 uint64 size)
224 {
225 // Note: We don't assume that the ranges are sorted, so we can't do this
226 // in a simple loop. Instead we restart the loop whenever the start of the
227 // given range intersects with an existing one.
228
229 for (uint32 i = 0; i < numRanges;) {
230 uint64 rangeStart = ranges[i].start;
231 uint64 rangeSize = ranges[i].size;
232
233 if (rangeStart <= base && rangeSize > base - rangeStart) {
234 uint64 intersect = std::min(rangeStart + rangeSize - base, size);
235 base += intersect;
236 size -= intersect;
237 if (size == 0)
238 return true;
239
240 i = 0;
241 continue;
242 }
243
244 i++;
245 }
246
247 return false;
248 }
249
250
251 extern "C" uint64
total_address_ranges_size(addr_range * ranges,uint32 numRanges)252 total_address_ranges_size(addr_range* ranges, uint32 numRanges)
253 {
254 uint64 total = 0;
255 for (uint32 i = 0; i < numRanges; i++)
256 total += ranges[i].size;
257 return total;
258 }
259
260
261 void
sort_address_ranges(addr_range * ranges,uint32 numRanges)262 sort_address_ranges(addr_range* ranges, uint32 numRanges)
263 {
264 // TODO: This is a pretty sucky bubble sort implementation!
265 bool done;
266
267 do {
268 done = true;
269 for (uint32 i = 1; i < numRanges; i++) {
270 if (ranges[i].start < ranges[i - 1].start) {
271 done = false;
272 addr_range tempRange;
273 memcpy(&tempRange, &ranges[i], sizeof(addr_range));
274 memcpy(&ranges[i], &ranges[i - 1], sizeof(addr_range));
275 memcpy(&ranges[i - 1], &tempRange, sizeof(addr_range));
276 }
277 }
278 } while (!done);
279 }
280
281
282 // #pragma mark - kernel args range functions
283
284
285 status_t
insert_physical_memory_range(uint64 start,uint64 size)286 insert_physical_memory_range(uint64 start, uint64 size)
287 {
288 return insert_address_range(gKernelArgs.physical_memory_range,
289 &gKernelArgs.num_physical_memory_ranges, MAX_PHYSICAL_MEMORY_RANGE,
290 start, size);
291 }
292
293
294 status_t
remove_physical_memory_range(uint64 start,uint64 size)295 remove_physical_memory_range(uint64 start, uint64 size)
296 {
297 return remove_address_range(gKernelArgs.physical_memory_range,
298 &gKernelArgs.num_physical_memory_ranges, MAX_PHYSICAL_MEMORY_RANGE,
299 start, size);
300 }
301
302
303 uint64
total_physical_memory()304 total_physical_memory()
305 {
306 return total_address_ranges_size(gKernelArgs.physical_memory_range,
307 gKernelArgs.num_physical_memory_ranges);
308 }
309
310
311 status_t
insert_physical_allocated_range(uint64 start,uint64 size)312 insert_physical_allocated_range(uint64 start, uint64 size)
313 {
314 return insert_address_range(gKernelArgs.physical_allocated_range,
315 &gKernelArgs.num_physical_allocated_ranges,
316 MAX_PHYSICAL_ALLOCATED_RANGE, start, size);
317 }
318
319
320 status_t
remove_physical_allocated_range(uint64 start,uint64 size)321 remove_physical_allocated_range(uint64 start, uint64 size)
322 {
323 return remove_address_range(gKernelArgs.physical_allocated_range,
324 &gKernelArgs.num_physical_allocated_ranges, MAX_PHYSICAL_ALLOCATED_RANGE,
325 start, size);
326 }
327
328
329 status_t
insert_virtual_allocated_range(uint64 start,uint64 size)330 insert_virtual_allocated_range(uint64 start, uint64 size)
331 {
332 return insert_address_range(gKernelArgs.virtual_allocated_range,
333 &gKernelArgs.num_virtual_allocated_ranges, MAX_VIRTUAL_ALLOCATED_RANGE,
334 start, size);
335 }
336
337
338 status_t
remove_virtual_allocated_range(uint64 start,uint64 size)339 remove_virtual_allocated_range(uint64 start, uint64 size)
340 {
341 return remove_address_range(gKernelArgs.virtual_allocated_range,
342 &gKernelArgs.num_virtual_allocated_ranges, MAX_VIRTUAL_ALLOCATED_RANGE,
343 start, size);
344 }
345
346
347 #if B_HAIKU_PHYSICAL_BITS > 32
348
349 void
ignore_physical_memory_ranges_beyond_4gb()350 ignore_physical_memory_ranges_beyond_4gb()
351 {
352 // sort
353 sort_address_ranges(gKernelArgs.physical_memory_range,
354 gKernelArgs.num_physical_memory_ranges);
355
356 static const uint64 kLimit = (uint64)1 << 32;
357
358 // remove everything past 4 GB
359 for (uint32 i = gKernelArgs.num_physical_memory_ranges; i > 0; i--) {
360 addr_range& range = gKernelArgs.physical_memory_range[i - 1];
361 if (range.start >= kLimit) {
362 // the complete range is beyond the limit
363 dprintf("ignore_physical_memory_ranges_beyond_4gb(): ignoring "
364 "range: %#" B_PRIx64 " - %#" B_PRIx64 "\n", range.start,
365 range.start + range.size);
366 gKernelArgs.ignored_physical_memory += range.size;
367 gKernelArgs.num_physical_memory_ranges = i - 1;
368 continue;
369 }
370
371 if ((range.start + range.size) > kLimit) {
372 // the range is partially beyond the limit
373 dprintf("ignore_physical_memory_ranges_beyond_4gb(): ignoring "
374 "range: %#" B_PRIx64 " - %#" B_PRIx64 "\n", kLimit,
375 range.start + range.size);
376 gKernelArgs.ignored_physical_memory
377 += range.size - (kLimit - range.start);
378 range.size = kLimit - range.start;
379 }
380
381 break;
382 }
383 }
384
385 #endif // B_HAIKU_PHYSICAL_BITS > 32
386
387
388 // #pragma mark - kernel_args allocations
389
390
391 /*! Convenience function that copies strdup() functions for the
392 kernel args heap.
393 */
394 char*
kernel_args_strdup(const char * string)395 kernel_args_strdup(const char* string)
396 {
397 if (string == NULL || string[0] == '\0')
398 return NULL;
399
400 size_t length = strlen(string) + 1;
401
402 char* target = (char*)kernel_args_malloc(length);
403 if (target == NULL)
404 return NULL;
405
406 memcpy(target, string, length);
407 return target;
408 }
409
410
411 /*! This function can be used to allocate (optionally aligned) memory that
412 is going to be passed over to the kernel. For example, the preloaded_image
413 structures are allocated this way.
414 The boot loader heap doesn't make it into the kernel!
415 */
416 void*
kernel_args_malloc(size_t size,uint8 alignment)417 kernel_args_malloc(size_t size, uint8 alignment)
418 {
419 //dprintf("kernel_args_malloc(): %ld bytes (%ld bytes left)\n", size, sFree);
420
421 #define ALIGN(addr, align) (((addr) + align - 1) & ~(align - 1))
422 size_t alignedSize = size + alignment - 1;
423
424 if (sFirstFree != NULL && alignedSize <= sFree) {
425 // there is enough space in the current buffer
426 void* address = (void*)ALIGN((addr_t)sFirstFree, alignment);
427 sFirstFree = (void*)((addr_t)sFirstFree + alignedSize);
428 sLast = address;
429 sFree -= alignedSize;
430
431 return address;
432 }
433
434 if (alignedSize > kChunkSize / 2 && sFree < alignedSize) {
435 // the block is so large, we'll allocate a new block for it
436 void* block = NULL;
437 if (platform_allocate_region(&block, alignedSize,
438 B_READ_AREA | B_WRITE_AREA, false) != B_OK) {
439 return NULL;
440 }
441
442 // Translate the address if needed on the current platform
443 addr_t translated_block;
444 platform_bootloader_address_to_kernel_address(block, &translated_block);
445 if (add_kernel_args_range((void *)translated_block, size) != B_OK)
446 panic("kernel_args max range too low!\n");
447
448 return (void*)ALIGN((addr_t)block, alignment);
449 }
450
451 // just allocate a new block and "close" the old one
452 void* block = NULL;
453 if (platform_allocate_region(&block, kChunkSize, B_READ_AREA | B_WRITE_AREA,
454 false) != B_OK) {
455 return NULL;
456 }
457
458 sFirstFree = (void*)((addr_t)block + alignedSize);
459 sLast = block;
460 sFree = kChunkSize - alignedSize;
461
462 // Translate the address if needed on the current platform
463 addr_t translated_block;
464 platform_bootloader_address_to_kernel_address(block, &translated_block);
465 if (add_kernel_args_range((void *)translated_block, kChunkSize) != B_OK)
466 panic("kernel_args max range too low!\n");
467
468 return (void*)ALIGN((addr_t)block, alignment);
469 }
470
471
472 /*! This function frees a block allocated via kernel_args_malloc().
473 It's very simple; it can only free the last allocation. It's
474 enough for its current usage in the boot loader, though.
475 */
476 void
kernel_args_free(void * block)477 kernel_args_free(void* block)
478 {
479 if (sLast != block) {
480 // sorry, we're dumb
481 return;
482 }
483
484 sFree = (addr_t)sFirstFree - (addr_t)sLast;
485 sFirstFree = block;
486 sLast = NULL;
487 }
488
489