Boot loader (x86 mmu.cpp):
* Made the page table allocation more flexible. Got rid of sMaxVirtualAddress and added new virtual_end address to the architecture specific kernel args. * Increased the virtual space we reserve for the kernel to 16 MB. That should suffice for quite a while. The previous 2 MB were too tight when building the kernel with debug info. * mmu_init(): The way we were translating the BIOS' extended memory map to our physical ranges arrays was broken. Small gaps between usable memory ranges would be ignored and instead marked allocated. This worked fine for the boot loader and during the early kernel initialization, but after the VM has been fully set up it frees all physical ranges that have not been claimed otherwise. So those ranges could be entered into the free pages list and would be used later. This could possibly cause all kinds of weird problems, probably including ACPI issues. Now we add only the actually usable ranges to our list. Kernel: * vm_page_init(): The pages of the ranges between the usable physical memory ranges are now marked PAGE_STATE_UNUSED, the allocated ranges PAGE_STATE_WIRED. * unmap_and_free_physical_pages(): Don't free pages marked as unused. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@35726 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
a93fa3c9ea
commit
d40a935560
@ -25,6 +25,7 @@ typedef struct {
|
||||
uint32 vir_pgdir;
|
||||
uint32 num_pgtables;
|
||||
uint32 pgtables[MAX_BOOT_PTABLES];
|
||||
uint32 virtual_end;
|
||||
uint32 phys_idt;
|
||||
uint32 vir_idt;
|
||||
uint32 phys_gdt;
|
||||
|
@ -16,9 +16,9 @@
|
||||
|
||||
// must match SMP_MAX_CPUS in arch_smp.h
|
||||
#define MAX_BOOT_CPUS 8
|
||||
#define MAX_PHYSICAL_MEMORY_RANGE 6
|
||||
#define MAX_PHYSICAL_ALLOCATED_RANGE 6
|
||||
#define MAX_VIRTUAL_ALLOCATED_RANGE 6
|
||||
#define MAX_PHYSICAL_MEMORY_RANGE 8
|
||||
#define MAX_PHYSICAL_ALLOCATED_RANGE 8
|
||||
#define MAX_VIRTUAL_ALLOCATED_RANGE 8
|
||||
|
||||
#define MAX_SERIAL_PORTS 4
|
||||
|
||||
|
@ -72,7 +72,7 @@ struct extended_memory {
|
||||
|
||||
|
||||
static const uint32 kDefaultPageTableFlags = 0x07; // present, user, R/W
|
||||
static const size_t kMaxKernelSize = 0x200000; // 2 MB for the kernel
|
||||
static const size_t kMaxKernelSize = 0x1000000; // 16 MB for the kernel
|
||||
|
||||
// working page directory and page table
|
||||
static uint32 *sPageDirectory = 0;
|
||||
@ -81,7 +81,6 @@ static uint32 *sPageDirectory = 0;
|
||||
|
||||
static addr_t sNextPhysicalAddress = 0x112000;
|
||||
static addr_t sNextVirtualAddress = KERNEL_BASE + kMaxKernelSize;
|
||||
static addr_t sMaxVirtualAddress = KERNEL_BASE + 0x400000;
|
||||
|
||||
static addr_t sNextPageTableAddress = 0x7d000;
|
||||
static const uint32 kPageTableRegionEnd = 0x8b000;
|
||||
@ -91,7 +90,6 @@ static const uint32 kPageTableRegionEnd = 0x8b000;
|
||||
|
||||
static addr_t sNextPhysicalAddress = 0x100000;
|
||||
static addr_t sNextVirtualAddress = KERNEL_BASE + kMaxKernelSize;
|
||||
static addr_t sMaxVirtualAddress = KERNEL_BASE + 0x400000;
|
||||
|
||||
static addr_t sNextPageTableAddress = 0x90000;
|
||||
static const uint32 kPageTableRegionEnd = 0x9e000;
|
||||
@ -151,14 +149,17 @@ get_next_page_table()
|
||||
|
||||
|
||||
/*! Adds a new page table for the specified base address */
|
||||
static void
|
||||
static uint32*
|
||||
add_page_table(addr_t base)
|
||||
{
|
||||
base = ROUNDDOWN(base, B_PAGE_SIZE * 1024);
|
||||
|
||||
// Get new page table and clear it out
|
||||
uint32 *pageTable = get_next_page_table();
|
||||
if (pageTable > (uint32 *)(8 * 1024 * 1024)) {
|
||||
panic("tried to add page table beyond the identity mapped 8 MB "
|
||||
"region\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TRACE("add_page_table(base = %p), got page: %p\n", (void*)base, pageTable);
|
||||
@ -172,6 +173,13 @@ add_page_table(addr_t base)
|
||||
// put the new page table into the page directory
|
||||
sPageDirectory[base / (4 * 1024 * 1024)]
|
||||
= (uint32)pageTable | kDefaultPageTableFlags;
|
||||
|
||||
// update the virtual end address in the kernel args
|
||||
base += B_PAGE_SIZE * 1024;
|
||||
if (base > gKernelArgs.arch_args.virtual_end)
|
||||
gKernelArgs.arch_args.virtual_end = base;
|
||||
|
||||
return pageTable;
|
||||
}
|
||||
|
||||
|
||||
@ -210,22 +218,23 @@ map_page(addr_t virtualAddress, addr_t physicalAddress, uint32 flags)
|
||||
(void *)virtualAddress);
|
||||
}
|
||||
|
||||
if (virtualAddress >= sMaxVirtualAddress) {
|
||||
// we need to add a new page table
|
||||
add_page_table(sMaxVirtualAddress);
|
||||
sMaxVirtualAddress += B_PAGE_SIZE * 1024;
|
||||
uint32 *pageTable = (uint32 *)(sPageDirectory[virtualAddress
|
||||
/ (B_PAGE_SIZE * 1024)] & 0xfffff000);
|
||||
|
||||
if (virtualAddress >= sMaxVirtualAddress) {
|
||||
panic("map_page: asked to map a page to %p\n",
|
||||
(void *)virtualAddress);
|
||||
if (pageTable == NULL) {
|
||||
// we need to add a new page table
|
||||
pageTable = add_page_table(virtualAddress);
|
||||
|
||||
if (pageTable == NULL) {
|
||||
panic("map_page: failed to allocate a page table for virtual "
|
||||
"address %p\n", (void*)virtualAddress);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
physicalAddress &= ~(B_PAGE_SIZE - 1);
|
||||
|
||||
// map the page to the correct page table
|
||||
uint32 *pageTable = (uint32 *)(sPageDirectory[virtualAddress
|
||||
/ (B_PAGE_SIZE * 1024)] & 0xfffff000);
|
||||
uint32 tableEntry = (virtualAddress % (B_PAGE_SIZE * 1024)) / B_PAGE_SIZE;
|
||||
|
||||
TRACE("map_page: inserting pageTable %p, tableEntry %" B_PRIu32
|
||||
@ -350,7 +359,6 @@ init_page_directory(void)
|
||||
sPageDirectory[1] = (uint32)pageTable | kDefaultPageFlags;
|
||||
|
||||
gKernelArgs.arch_args.num_pgtables = 0;
|
||||
add_page_table(KERNEL_BASE);
|
||||
|
||||
// switch to the new pgdir and enable paging
|
||||
asm("movl %0, %%eax;"
|
||||
@ -598,6 +606,8 @@ mmu_init(void)
|
||||
{
|
||||
TRACE("mmu_init\n");
|
||||
|
||||
gKernelArgs.arch_args.virtual_end = KERNEL_BASE;
|
||||
|
||||
gKernelArgs.physical_allocated_range[0].start = sNextPhysicalAddress;
|
||||
gKernelArgs.physical_allocated_range[0].size = 0;
|
||||
gKernelArgs.num_physical_allocated_ranges = 1;
|
||||
@ -635,69 +645,42 @@ mmu_init(void)
|
||||
for (uint32 i = 0; i < extMemoryCount; i++) {
|
||||
// Type 1 is available memory
|
||||
if (extMemoryBlock[i].type == 1) {
|
||||
uint64 base = extMemoryBlock[i].base_addr;
|
||||
uint64 end = base + extMemoryBlock[i].length;
|
||||
|
||||
// round everything up to page boundaries, exclusive of pages
|
||||
// it partially occupies
|
||||
if ((extMemoryBlock[i].base_addr % B_PAGE_SIZE) != 0) {
|
||||
extMemoryBlock[i].length -= B_PAGE_SIZE
|
||||
- extMemoryBlock[i].base_addr % B_PAGE_SIZE;
|
||||
}
|
||||
extMemoryBlock[i].base_addr
|
||||
= ROUNDUP(extMemoryBlock[i].base_addr, B_PAGE_SIZE);
|
||||
extMemoryBlock[i].length
|
||||
= ROUNDDOWN(extMemoryBlock[i].length, B_PAGE_SIZE);
|
||||
base = ROUNDUP(base, B_PAGE_SIZE);
|
||||
end = ROUNDDOWN(end, B_PAGE_SIZE);
|
||||
|
||||
// we ignore all memory beyond 4 GB
|
||||
if (extMemoryBlock[i].base_addr > 0xffffffffULL)
|
||||
if (end > 0x100000000ULL)
|
||||
end = 0x100000000ULL;
|
||||
if (end <= base)
|
||||
continue;
|
||||
|
||||
if (extMemoryBlock[i].base_addr + extMemoryBlock[i].length
|
||||
> 0xffffffffULL) {
|
||||
extMemoryBlock[i].length
|
||||
= 0x100000000ULL - extMemoryBlock[i].base_addr;
|
||||
if (insert_physical_memory_range(base, end - base) != B_OK) {
|
||||
panic("mmu_init(): Failed to add physical memory range "
|
||||
"%#" B_PRIx64 " - %#" B_PRIx64 "\n", base, end);
|
||||
}
|
||||
|
||||
if (gKernelArgs.num_physical_memory_ranges > 0) {
|
||||
// we might want to extend a previous hole
|
||||
addr_t previousEnd = gKernelArgs.physical_memory_range[
|
||||
gKernelArgs.num_physical_memory_ranges - 1].start
|
||||
+ gKernelArgs.physical_memory_range[
|
||||
gKernelArgs.num_physical_memory_ranges - 1].size;
|
||||
addr_t holeSize = extMemoryBlock[i].base_addr - previousEnd;
|
||||
|
||||
// If the hole is smaller than 1 MB, we try to mark the
|
||||
// memory as allocated and extend the previous memory range
|
||||
if (previousEnd <= extMemoryBlock[i].base_addr
|
||||
&& holeSize < 0x100000
|
||||
&& insert_physical_allocated_range(previousEnd,
|
||||
extMemoryBlock[i].base_addr - previousEnd)
|
||||
== B_OK) {
|
||||
gKernelArgs.physical_memory_range[
|
||||
gKernelArgs.num_physical_memory_ranges - 1].size
|
||||
+= holeSize;
|
||||
}
|
||||
}
|
||||
|
||||
insert_physical_memory_range(extMemoryBlock[i].base_addr,
|
||||
extMemoryBlock[i].length);
|
||||
}
|
||||
}
|
||||
|
||||
// sort the range
|
||||
sort_addr_range(gKernelArgs.physical_memory_range,
|
||||
gKernelArgs.num_physical_memory_ranges);
|
||||
} else {
|
||||
// TODO: for now!
|
||||
dprintf("No extended memory block - using 32 MB (fix me!)\n");
|
||||
uint32 memSize = 32 * 1024 * 1024;
|
||||
dprintf("No extended memory block - using 64 MB (fix me!)\n");
|
||||
uint32 memSize = 64 * 1024 * 1024;
|
||||
|
||||
// We dont have an extended map, assume memory is contiguously mapped
|
||||
// at 0x0
|
||||
// at 0x0, but leave out the BIOS range ((640k - 1 page) to 1 MB).
|
||||
gKernelArgs.physical_memory_range[0].start = 0;
|
||||
gKernelArgs.physical_memory_range[0].size = memSize;
|
||||
gKernelArgs.num_physical_memory_ranges = 1;
|
||||
|
||||
// mark the bios area allocated
|
||||
uint32 biosRange = gKernelArgs.num_physical_allocated_ranges++;
|
||||
|
||||
gKernelArgs.physical_allocated_range[biosRange].start = 0x9f000;
|
||||
// 640k - 1 page
|
||||
gKernelArgs.physical_allocated_range[biosRange].size = 0x61000;
|
||||
gKernelArgs.physical_memory_range[0].size = 0x9f000;
|
||||
gKernelArgs.physical_memory_range[1].start = 0x100000;
|
||||
gKernelArgs.physical_memory_range[1].size = memSize;
|
||||
gKernelArgs.num_physical_memory_ranges = 2;
|
||||
}
|
||||
|
||||
gKernelArgs.arch_args.page_hole = 0xffc00000;
|
||||
|
@ -615,7 +615,7 @@ arch_vm_init_end(kernel_args *args)
|
||||
|
||||
// throw away anything in the kernel_args.pgtable[] that's not yet mapped
|
||||
vm_free_unused_boot_loader_range(KERNEL_BASE,
|
||||
0x400000 * args->arch_args.num_pgtables);
|
||||
args->arch_args.virtual_end - KERNEL_BASE);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
@ -3004,7 +3004,8 @@ unmap_and_free_physical_pages(VMTranslationMap* map, addr_t start, addr_t end)
|
||||
&& (flags & PAGE_PRESENT) != 0) {
|
||||
vm_page* page = vm_lookup_page(physicalAddress / B_PAGE_SIZE);
|
||||
if (page != NULL && page->State() != PAGE_STATE_FREE
|
||||
&& page->State() != PAGE_STATE_CLEAR) {
|
||||
&& page->State() != PAGE_STATE_CLEAR
|
||||
&& page->State() != PAGE_STATE_UNUSED) {
|
||||
DEBUG_PAGE_ACCESS_START(page);
|
||||
vm_page_set_state(page, PAGE_STATE_FREE);
|
||||
}
|
||||
|
@ -1253,6 +1253,63 @@ clear_page(struct vm_page *page)
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
mark_page_range_in_use(addr_t startPage, addr_t length, bool wired)
|
||||
{
|
||||
TRACE(("mark_page_range_in_use: start 0x%lx, len 0x%lx\n",
|
||||
startPage, length));
|
||||
|
||||
if (sPhysicalPageOffset > startPage) {
|
||||
TRACE(("mark_page_range_in_use: start page %ld is before free list\n",
|
||||
startPage));
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
startPage -= sPhysicalPageOffset;
|
||||
if (startPage + length > sNumPages) {
|
||||
TRACE(("mark_page_range_in_use: range would extend past free list\n"));
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
WriteLocker locker(sFreePageQueuesLock);
|
||||
|
||||
for (addr_t i = 0; i < length; i++) {
|
||||
vm_page *page = &sPages[startPage + i];
|
||||
switch (page->State()) {
|
||||
case PAGE_STATE_FREE:
|
||||
case PAGE_STATE_CLEAR:
|
||||
{
|
||||
// TODO: This violates the page reservation policy, since we remove pages from
|
||||
// the free/clear queues without having reserved them before. This should happen
|
||||
// in the early boot process only, though.
|
||||
DEBUG_PAGE_ACCESS_START(page);
|
||||
VMPageQueue& queue = page->State() == PAGE_STATE_FREE
|
||||
? sFreePageQueue : sClearPageQueue;
|
||||
queue.Remove(page);
|
||||
page->SetState(wired ? PAGE_STATE_UNUSED : PAGE_STATE_UNUSED);
|
||||
page->busy = false;
|
||||
atomic_add(&sUnreservedFreePages, -1);
|
||||
DEBUG_PAGE_ACCESS_END(page);
|
||||
break;
|
||||
}
|
||||
case PAGE_STATE_WIRED:
|
||||
case PAGE_STATE_UNUSED:
|
||||
break;
|
||||
case PAGE_STATE_ACTIVE:
|
||||
case PAGE_STATE_INACTIVE:
|
||||
case PAGE_STATE_MODIFIED:
|
||||
case PAGE_STATE_CACHED:
|
||||
default:
|
||||
// uh
|
||||
dprintf("mark_page_range_in_use: page 0x%lx in non-free state %d!\n",
|
||||
startPage + i, page->State());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
This is a background thread that wakes up every now and then (every 100ms)
|
||||
and moves some pages from the free queue over to the clear queue.
|
||||
@ -2761,10 +2818,23 @@ vm_page_init(kernel_args *args)
|
||||
|
||||
TRACE(("initialized table\n"));
|
||||
|
||||
// mark some of the page ranges in use
|
||||
// mark the ranges between usable physical memory unused
|
||||
addr_t previousEnd = 0;
|
||||
for (uint32 i = 0; i < args->num_physical_memory_ranges; i++) {
|
||||
addr_t base = args->physical_memory_range[i].start;
|
||||
addr_t size = args->physical_memory_range[i].size;
|
||||
if (base > previousEnd) {
|
||||
mark_page_range_in_use(previousEnd / B_PAGE_SIZE,
|
||||
(base - previousEnd) / B_PAGE_SIZE, false);
|
||||
}
|
||||
previousEnd = base + size;
|
||||
}
|
||||
|
||||
// mark the allocated physical page ranges wired
|
||||
for (uint32 i = 0; i < args->num_physical_allocated_ranges; i++) {
|
||||
vm_mark_page_range_inuse(args->physical_allocated_range[i].start / B_PAGE_SIZE,
|
||||
args->physical_allocated_range[i].size / B_PAGE_SIZE);
|
||||
mark_page_range_in_use(
|
||||
args->physical_allocated_range[i].start / B_PAGE_SIZE,
|
||||
args->physical_allocated_range[i].size / B_PAGE_SIZE, true);
|
||||
}
|
||||
|
||||
// The target of actually free pages. This must be at least the system
|
||||
@ -2873,57 +2943,7 @@ vm_mark_page_inuse(addr_t page)
|
||||
status_t
|
||||
vm_mark_page_range_inuse(addr_t startPage, addr_t length)
|
||||
{
|
||||
TRACE(("vm_mark_page_range_inuse: start 0x%lx, len 0x%lx\n",
|
||||
startPage, length));
|
||||
|
||||
if (sPhysicalPageOffset > startPage) {
|
||||
TRACE(("vm_mark_page_range_inuse: start page %ld is before free list\n",
|
||||
startPage));
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
startPage -= sPhysicalPageOffset;
|
||||
if (startPage + length > sNumPages) {
|
||||
TRACE(("vm_mark_page_range_inuse: range would extend past free list\n"));
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
WriteLocker locker(sFreePageQueuesLock);
|
||||
|
||||
for (addr_t i = 0; i < length; i++) {
|
||||
vm_page *page = &sPages[startPage + i];
|
||||
switch (page->State()) {
|
||||
case PAGE_STATE_FREE:
|
||||
case PAGE_STATE_CLEAR:
|
||||
{
|
||||
// TODO: This violates the page reservation policy, since we remove pages from
|
||||
// the free/clear queues without having reserved them before. This should happen
|
||||
// in the early boot process only, though.
|
||||
DEBUG_PAGE_ACCESS_START(page);
|
||||
VMPageQueue& queue = page->State() == PAGE_STATE_FREE
|
||||
? sFreePageQueue : sClearPageQueue;
|
||||
queue.Remove(page);
|
||||
page->SetState(PAGE_STATE_UNUSED);
|
||||
page->busy = false;
|
||||
atomic_add(&sUnreservedFreePages, -1);
|
||||
DEBUG_PAGE_ACCESS_END(page);
|
||||
break;
|
||||
}
|
||||
case PAGE_STATE_WIRED:
|
||||
break;
|
||||
case PAGE_STATE_ACTIVE:
|
||||
case PAGE_STATE_INACTIVE:
|
||||
case PAGE_STATE_MODIFIED:
|
||||
case PAGE_STATE_CACHED:
|
||||
case PAGE_STATE_UNUSED:
|
||||
default:
|
||||
// uh
|
||||
dprintf("vm_mark_page_range_inuse: page 0x%lx in non-free state %d!\n",
|
||||
startPage + i, page->State());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
return mark_page_range_in_use(startPage, length, false);
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user