diff --git a/headers/private/kernel/boot/platform.h b/headers/private/kernel/boot/platform.h index 3725cf2348..6f207ada0e 100644 --- a/headers/private/kernel/boot/platform.h +++ b/headers/private/kernel/boot/platform.h @@ -25,7 +25,8 @@ extern void platform_release_heap(struct stage2_args *args, void *base); extern status_t platform_init_heap(struct stage2_args *args, void **_base, void **_top); /* MMU/memory functions */ -extern status_t platform_allocate_region(void **_virtualAddress, size_t size, uint8 protection); +extern status_t platform_allocate_region(void **_virtualAddress, size_t size, + uint8 protection, bool exactAddress); extern status_t platform_free_region(void *address, size_t size); /* boot options */ diff --git a/headers/private/kernel/boot/platform/openfirmware/platform_arch.h b/headers/private/kernel/boot/platform/openfirmware/platform_arch.h index 66465bc5be..5c87528485 100644 --- a/headers/private/kernel/boot/platform/openfirmware/platform_arch.h +++ b/headers/private/kernel/boot/platform/openfirmware/platform_arch.h @@ -14,7 +14,8 @@ extern "C" { #endif extern status_t arch_set_callback(void); -extern void *arch_mmu_allocate(void *address, size_t size, uint8 protection); +extern void *arch_mmu_allocate(void *address, size_t size, uint8 protection, + bool exactAddress); extern status_t arch_mmu_free(void *address, size_t size); extern status_t arch_mmu_init(void); diff --git a/src/system/boot/loader/elf.cpp b/src/system/boot/loader/elf.cpp index ca23b91759..8c70585974 100644 --- a/src/system/boot/loader/elf.cpp +++ b/src/system/boot/loader/elf.cpp @@ -213,6 +213,13 @@ elf_load_image(int fd, preloaded_image *image) region->start, region->size, region->delta)); } + // found both, text and data? + if (image->data_region.size == 0 || image->text_region.size == 0) { + dprintf("Couldn't find both text and data segment!\n"); + status = B_BAD_DATA; + goto error1; + } + // get the segment order elf_region *firstRegion; elf_region *secondRegion; @@ -233,10 +240,11 @@ elf_load_image(int fd, preloaded_image *image) goto error1; } - // if image->text_region.start == NULL (image is relocatable), - // platform_allocate_region() automatically allocates an address + // The kernel and the modules are relocatable, thus + // platform_allocate_region() can automatically allocate an address, + // but shall prefer the specified base address. if (platform_allocate_region((void **)&firstRegion->start, totalSize, - B_READ_AREA | B_WRITE_AREA) < B_OK) { + B_READ_AREA | B_WRITE_AREA, false) < B_OK) { status = B_NO_MEMORY; goto error1; } diff --git a/src/system/boot/loader/file_systems/tarfs/tarfs.cpp b/src/system/boot/loader/file_systems/tarfs/tarfs.cpp index 9170bde2fb..82e21d8559 100644 --- a/src/system/boot/loader/file_systems/tarfs/tarfs.cpp +++ b/src/system/boot/loader/file_systems/tarfs/tarfs.cpp @@ -558,7 +558,7 @@ TarFS::Volume::Init(boot::Partition *partition) return B_BAD_DATA; if (platform_allocate_region((void **)&out, kTarRegionSize, - B_READ_AREA | B_WRITE_AREA) != B_OK) { + B_READ_AREA | B_WRITE_AREA, false) != B_OK) { TRACE(("tarfs: allocating region failed!\n")); return B_NO_MEMORY; } diff --git a/src/system/boot/loader/kernel_args.cpp b/src/system/boot/loader/kernel_args.cpp index a532fefba1..8212959b2f 100644 --- a/src/system/boot/loader/kernel_args.cpp +++ b/src/system/boot/loader/kernel_args.cpp @@ -153,8 +153,10 @@ kernel_args_malloc(size_t size) if (size > kChunkSize / 2 && sFree < size) { // the block is so large, we'll allocate a new block for it void *block = NULL; - if (platform_allocate_region(&block, size, B_READ_AREA | B_WRITE_AREA) != B_OK) + if (platform_allocate_region(&block, size, B_READ_AREA | B_WRITE_AREA, + false) != B_OK) { return NULL; + } if (add_kernel_args_range(block, size) != B_OK) panic("kernel_args max range to low!\n"); @@ -163,8 +165,10 @@ kernel_args_malloc(size_t size) // just allocate a new block and "close" the old one void *block = NULL; - if (platform_allocate_region(&block, kChunkSize, B_READ_AREA | B_WRITE_AREA) != B_OK) + if (platform_allocate_region(&block, kChunkSize, + B_READ_AREA | B_WRITE_AREA, false) != B_OK) { return NULL; + } sFirstFree = (void *)((addr_t)block + size); sLast = block; diff --git a/src/system/boot/platform/bios_ia32/mmu.cpp b/src/system/boot/platform/bios_ia32/mmu.cpp index 8c2367b6f4..a70b4cb6c6 100644 --- a/src/system/boot/platform/bios_ia32/mmu.cpp +++ b/src/system/boot/platform/bios_ia32/mmu.cpp @@ -593,7 +593,8 @@ mmu_init(void) extern "C" status_t -platform_allocate_region(void **_address, size_t size, uint8 protection) +platform_allocate_region(void **_address, size_t size, uint8 protection, + bool /*exactAddress*/) { void *address = mmu_allocate(*_address, size); if (address == NULL) diff --git a/src/system/boot/platform/openfirmware/arch/ppc/mmu.cpp b/src/system/boot/platform/openfirmware/arch/ppc/mmu.cpp index da6e8790ab..bdde388ca8 100644 --- a/src/system/boot/platform/openfirmware/arch/ppc/mmu.cpp +++ b/src/system/boot/platform/openfirmware/arch/ppc/mmu.cpp @@ -175,6 +175,10 @@ find_physical_memory_ranges(size_t &total) static bool is_in_range(addr_range *ranges, uint32 numRanges, void *address, size_t size) { + // TODO: This function returns whether any single allocated range + // completely contains the given range. If the given range crosses + // allocated range boundaries, but is nevertheless covered completely, the + // function returns false! addr_t start = (addr_t)address; addr_t end = start + size; @@ -190,10 +194,30 @@ is_in_range(addr_range *ranges, uint32 numRanges, void *address, size_t size) } +static bool +intersects_ranges(addr_range *ranges, uint32 numRanges, void *address, + size_t size) +{ + addr_t start = (addr_t)address; + addr_t end = start + size; + + for (uint32 i = 0; i < numRanges; i++) { + addr_t rangeStart = ranges[i].start; + addr_t rangeEnd = rangeStart + ranges[i].size; + + if ((start >= rangeStart && start < rangeEnd) + || (rangeStart >= start && rangeStart < end)) { + return true; + } + } + return false; +} + + static bool is_virtual_allocated(void *address, size_t size) { - return is_in_range(gKernelArgs.virtual_allocated_range, + return intersects_ranges(gKernelArgs.virtual_allocated_range, gKernelArgs.num_virtual_allocated_ranges, address, size); } @@ -202,7 +226,7 @@ is_virtual_allocated(void *address, size_t size) static bool is_physical_allocated(void *address, size_t size) { - return is_in_range(gKernelArgs.physical_allocated_range, + return intersects_ranges(gKernelArgs.physical_allocated_range, gKernelArgs.num_physical_allocated_ranges, address, size); } @@ -328,6 +352,7 @@ find_allocated_ranges(void *pageTable, page_table_entry_group **_physicalPageTab for (int i = 0; i < length; i++) { struct translation_map *map = &translations[i]; //printf("%i: map: %p, length %d -> physical: %p, mode %d\n", i, map->virtual_address, map->length, map->physical_address, map->mode); +printf("%i: map: %p, length %d -> physical: %p, mode %d\n", i, map->virtual_address, map->length, map->physical_address, map->mode); // insert range in physical allocated, if it points to physical memory @@ -425,19 +450,34 @@ find_free_physical_range(size_t size) static void * -find_free_virtual_range(size_t size) +find_free_virtual_range(void *base, size_t size) { + if (base && !is_virtual_allocated(base, size)) + return base; + + void *firstFound = NULL; + void *firstBaseFound = NULL; for (uint32 i = 0; i < gKernelArgs.num_virtual_allocated_ranges; i++) { void *address = (void *)(gKernelArgs.virtual_allocated_range[i].start + gKernelArgs.virtual_allocated_range[i].size); - if (!is_virtual_allocated(address, size)) - return address; + if (!is_virtual_allocated(address, size)) { + if (!base) + return address; + + if (firstFound == NULL) + firstFound = address; + if (address >= base + && (firstBaseFound == NULL || address < firstBaseFound)) { + firstBaseFound = address; + } + } } - return NULL; + return (firstBaseFound ? firstBaseFound : firstFound); } extern "C" void * -arch_mmu_allocate(void *virtualAddress, size_t size, uint8 protection) +arch_mmu_allocate(void *_virtualAddress, size_t size, uint8 protection, + bool exactAddress) { // we only know page sizes size = ROUNDUP(size, B_PAGE_SIZE); @@ -452,14 +492,17 @@ arch_mmu_allocate(void *virtualAddress, size_t size, uint8 protection) else protection = 0x21; - if (virtualAddress == NULL) { - // find free address large enough to hold "size" - virtualAddress = find_free_virtual_range(size); - if (virtualAddress == NULL) - return NULL; - } else { - if (is_virtual_allocated(virtualAddress, size)) - return NULL; + // find free address large enough to hold "size" + void *virtualAddress = find_free_virtual_range(_virtualAddress, size); + if (virtualAddress == NULL) + return NULL; + + // fail if the exact address was requested, but is not free + if (exactAddress && _virtualAddress && virtualAddress != _virtualAddress) { + dprintf("arch_mmu_allocate(): exact address requested, but virtual " + "range (base: %p, size: %lu) is not free.\n", + _virtualAddress, size); + return NULL; } // we have a free virtual range for the allocation, now @@ -468,8 +511,11 @@ arch_mmu_allocate(void *virtualAddress, size_t size, uint8 protection) // so that we don't have to optimize for these cases :) void *physicalAddress = find_free_physical_range(size); - if (physicalAddress == NULL) + if (physicalAddress == NULL) { + dprintf("arch_mmu_allocate(base: %p, size: %lu) no free physical " + "address\n", virtualAddress, size); return NULL; + } // everything went fine, so lets mark the space as used. diff --git a/src/system/boot/platform/openfirmware/heap.cpp b/src/system/boot/platform/openfirmware/heap.cpp index 0a46cd76eb..768882ac4c 100644 --- a/src/system/boot/platform/openfirmware/heap.cpp +++ b/src/system/boot/platform/openfirmware/heap.cpp @@ -26,7 +26,7 @@ platform_init_heap(stage2_args *args, void **_base, void **_top) *_base = NULL; status_t error = platform_allocate_region(_base, args->heap_size, - B_READ_AREA | B_WRITE_AREA); + B_READ_AREA | B_WRITE_AREA, false); if (error != B_OK) return error; diff --git a/src/system/boot/platform/openfirmware/mmu.cpp b/src/system/boot/platform/openfirmware/mmu.cpp index 85ecae23aa..7f81c74eea 100644 --- a/src/system/boot/platform/openfirmware/mmu.cpp +++ b/src/system/boot/platform/openfirmware/mmu.cpp @@ -13,12 +13,14 @@ status_t -platform_allocate_region(void **_address, size_t size, uint8 protection) +platform_allocate_region(void **_address, size_t size, uint8 protection, + bool exactAddress) { if (size == 0) return B_BAD_VALUE; - void *address = arch_mmu_allocate(*_address, size, protection); + void *address = arch_mmu_allocate(*_address, size, protection, + exactAddress); if (address == NULL) return B_NO_MEMORY;