[haiku-commits] r37050 - haiku/trunk/src/system/kernel/arch/x86

  • From: ingo_weinhold@xxxxxx
  • To: haiku-commits@xxxxxxxxxxxxx
  • Date: Mon, 7 Jun 2010 22:36:34 +0200 (CEST)

Author: bonefish
Date: 2010-06-07 22:36:33 +0200 (Mon, 07 Jun 2010)
New Revision: 37050
Changeset: http://dev.haiku-os.org/changeset/37050/haiku

Added:
   haiku/trunk/src/system/kernel/arch/x86/X86PagingMethod.cpp
   haiku/trunk/src/system/kernel/arch/x86/X86PagingMethod.h
   haiku/trunk/src/system/kernel/arch/x86/X86PagingMethod32Bit.cpp
   haiku/trunk/src/system/kernel/arch/x86/X86PagingMethod32Bit.h
Modified:
   haiku/trunk/src/system/kernel/arch/x86/Jamfile
   haiku/trunk/src/system/kernel/arch/x86/arch_vm_translation_map.cpp
   haiku/trunk/src/system/kernel/arch/x86/x86_paging.h
Log:
* Added a level of indirection for the arch_vm_translation_map functions.
  Introduced the interface X86PagingMethod which is used by those. ATM
  there's one implementing class, X86PagingMethod32Bit.
* Made X86PagingStructures a base class, with one derived class,
  X86PagingStructures32Bit.


Modified: haiku/trunk/src/system/kernel/arch/x86/Jamfile
===================================================================
--- haiku/trunk/src/system/kernel/arch/x86/Jamfile      2010-06-07 17:37:33 UTC 
(rev 37049)
+++ haiku/trunk/src/system/kernel/arch/x86/Jamfile      2010-06-07 20:36:33 UTC 
(rev 37050)
@@ -40,6 +40,8 @@
        x86_physical_page_mapper.cpp
        x86_physical_page_mapper_large_memory.cpp
        x86_syscalls.cpp
+       X86PagingMethod.cpp
+       X86PagingMethod32Bit.cpp
 
        x86_apic.cpp
        x86_hpet.cpp

Added: haiku/trunk/src/system/kernel/arch/x86/X86PagingMethod.cpp
===================================================================
--- haiku/trunk/src/system/kernel/arch/x86/X86PagingMethod.cpp                  
        (rev 0)
+++ haiku/trunk/src/system/kernel/arch/x86/X86PagingMethod.cpp  2010-06-07 
20:36:33 UTC (rev 37050)
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2010, Ingo Weinhold, ingo_weinhold@xxxxxxx
+ * Distributed under the terms of the MIT License.
+ */
+
+
+#include "X86PagingMethod.h"
+
+
+X86PagingMethod::~X86PagingMethod()
+{
+}

Added: haiku/trunk/src/system/kernel/arch/x86/X86PagingMethod.h
===================================================================
--- haiku/trunk/src/system/kernel/arch/x86/X86PagingMethod.h                    
        (rev 0)
+++ haiku/trunk/src/system/kernel/arch/x86/X86PagingMethod.h    2010-06-07 
20:36:33 UTC (rev 37050)
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2010, Ingo Weinhold, ingo_weinhold@xxxxxxx
+ * Distributed under the terms of the MIT License.
+ */
+#ifndef KERNEL_ARCH_X86_X86_PAGING_METHOD_H
+#define KERNEL_ARCH_X86_X86_PAGING_METHOD_H
+
+
+#include <SupportDefs.h>
+
+
+struct kernel_args;
+struct VMPhysicalPageMapper;
+struct VMTranslationMap;
+
+
+class X86PagingMethod {
+public:
+       virtual                                         ~X86PagingMethod();
+
+       virtual status_t                        Init(kernel_args* args,
+                                                                       
VMPhysicalPageMapper** _physicalPageMapper)
+                                                                               
= 0;
+       virtual status_t                        InitPostArea(kernel_args* args) 
= 0;
+
+       virtual status_t                        CreateTranslationMap(bool 
kernel,
+                                                                       
VMTranslationMap** _map) = 0;
+
+       virtual status_t                        MapEarly(kernel_args* args,
+                                                                       addr_t 
virtualAddress,
+                                                                       
phys_addr_t physicalAddress,
+                                                                       uint8 
attributes,
+                                                                       
phys_addr_t (*get_free_page)(kernel_args*))
+                                                                               
= 0;
+
+       virtual bool                            IsKernelPageAccessible(addr_t 
virtualAddress,
+                                                                       uint32 
protection) = 0;
+};
+
+
+#endif // KERNEL_ARCH_X86_X86_PAGING_METHOD_H

Copied: haiku/trunk/src/system/kernel/arch/x86/X86PagingMethod32Bit.cpp (from 
rev 37025, haiku/trunk/src/system/kernel/arch/x86/arch_vm_translation_map.cpp)
===================================================================
--- haiku/trunk/src/system/kernel/arch/x86/X86PagingMethod32Bit.cpp             
                (rev 0)
+++ haiku/trunk/src/system/kernel/arch/x86/X86PagingMethod32Bit.cpp     
2010-06-07 20:36:33 UTC (rev 37050)
@@ -0,0 +1,1503 @@
+/*
+ * Copyright 2008-2010, Ingo Weinhold, ingo_weinhold@xxxxxxx
+ * Copyright 2002-2007, Axel Dörfler, axeld@xxxxxxxxxxxxxxxxx All rights 
reserved.
+ * Distributed under the terms of the MIT License.
+ *
+ * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
+ * Distributed under the terms of the NewOS License.
+ */
+
+
+#include "X86PagingMethod32Bit.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <AutoDeleter.h>
+
+#include <arch_system_info.h>
+#include <heap.h>
+#include <int.h>
+#include <thread.h>
+#include <slab/Slab.h>
+#include <smp.h>
+#include <util/AutoLock.h>
+#include <util/queue.h>
+#include <vm/vm_page.h>
+#include <vm/vm_priv.h>
+#include <vm/VMAddressSpace.h>
+#include <vm/VMCache.h>
+
+#include "x86_physical_page_mapper.h"
+#include "X86VMTranslationMap.h"
+
+
+//#define TRACE_X86_PAGING_METHOD_32_BIT
+#ifdef TRACE_X86_PAGING_METHOD_32_BIT
+#      define TRACE(x...) dprintf(x)
+#else
+#      define TRACE(x...) ;
+#endif
+
+
+static X86PagingMethod32Bit sMethod;
+
+static page_table_entry *sPageHole = NULL;
+static page_directory_entry *sPageHolePageDir = NULL;
+static uint32 sKernelPhysicalPageDirectory = 0;
+static page_directory_entry *sKernelVirtualPageDirectory = NULL;
+
+static X86PhysicalPageMapper* sPhysicalPageMapper;
+static TranslationMapPhysicalPageMapper* sKernelPhysicalPageMapper;
+
+
+// Accessor class to reuse the SinglyLinkedListLink of DeferredDeletable for
+// X86PagingStructures32Bit.
+struct PagingStructuresGetLink {
+private:
+       typedef SinglyLinkedListLink<X86PagingStructures32Bit> Link;
+
+public:
+       inline Link* operator()(X86PagingStructures32Bit* element) const
+       {
+               return (Link*)element->GetSinglyLinkedListLink();
+       }
+
+       inline const Link* operator()(
+               const X86PagingStructures32Bit* element) const
+       {
+               return (const Link*)element->GetSinglyLinkedListLink();
+       }
+};
+
+
+typedef SinglyLinkedList<X86PagingStructures32Bit, PagingStructuresGetLink>
+       PagingStructuresList;
+
+
+static PagingStructuresList sPagingStructuresList;
+static spinlock sPagingStructuresListLock;
+
+
+#define FIRST_USER_PGDIR_ENT    (VADDR_TO_PDENT(USER_BASE))
+#define NUM_USER_PGDIR_ENTS     (VADDR_TO_PDENT(ROUNDUP(USER_SIZE, \
+                                                                       
B_PAGE_SIZE * 1024)))
+#define FIRST_KERNEL_PGDIR_ENT  (VADDR_TO_PDENT(KERNEL_BASE))
+#define NUM_KERNEL_PGDIR_ENTS   (VADDR_TO_PDENT(KERNEL_SIZE))
+#define IS_KERNEL_MAP(map)             (fPagingStructures->pgdir_phys \
+                                                                       == 
sKernelPhysicalPageDirectory)
+
+
+X86PagingStructures32Bit::X86PagingStructures32Bit()
+{
+}
+
+
+X86PagingStructures32Bit::~X86PagingStructures32Bit()
+{
+}
+
+
+void
+X86PagingStructures32Bit::Delete()
+{
+       // remove from global list
+       InterruptsSpinLocker locker(sPagingStructuresListLock);
+       sPagingStructuresList.Remove(this);
+       locker.Unlock();
+
+#if 0
+       // this sanity check can be enabled when corruption due to
+       // overwriting an active page directory is suspected
+       uint32 activePageDirectory;
+       read_cr3(activePageDirectory);
+       if (activePageDirectory == pgdir_phys)
+               panic("deleting a still active page directory\n");
+#endif
+
+       if (are_interrupts_enabled())
+               delete this;
+       else
+               deferred_delete(this);
+}
+
+
+//     #pragma mark -
+
+
+//! TODO: currently assumes this translation map is active
+static status_t
+early_query(addr_t va, phys_addr_t *_physicalAddress)
+{
+       if ((sPageHolePageDir[VADDR_TO_PDENT(va)] & X86_PDE_PRESENT) == 0) {
+               // no pagetable here
+               return B_ERROR;
+       }
+
+       page_table_entry* pentry = sPageHole + va / B_PAGE_SIZE;
+       if ((*pentry & X86_PTE_PRESENT) == 0) {
+               // page mapping not valid
+               return B_ERROR;
+       }
+
+       *_physicalAddress = *pentry & X86_PTE_ADDRESS_MASK;
+       return B_OK;
+}
+
+
+static inline uint32
+memory_type_to_pte_flags(uint32 memoryType)
+{
+       // ATM we only handle the uncacheable and write-through type 
explicitly. For
+       // all other types we rely on the MTRRs to be set up correctly. Since 
we set
+       // the default memory type to write-back and since the uncacheable type 
in
+       // the PTE overrides any MTRR attribute (though, as per the specs, that 
is
+       // not recommended for performance reasons), this reduces the work we
+       // actually *have* to do with the MTRRs to setting the remaining types
+       // (usually only write-combining for the frame buffer).
+       switch (memoryType) {
+               case B_MTR_UC:
+                       return X86_PTE_CACHING_DISABLED | X86_PTE_WRITE_THROUGH;
+
+               case B_MTR_WC:
+                       // X86_PTE_WRITE_THROUGH would be closer, but the 
combination with
+                       // MTRR WC is "implementation defined" for Pentium 
Pro/II.
+                       return 0;
+
+               case B_MTR_WT:
+                       return X86_PTE_WRITE_THROUGH;
+
+               case B_MTR_WP:
+               case B_MTR_WB:
+               default:
+                       return 0;
+       }
+}
+
+
+static void
+put_page_table_entry_in_pgtable(page_table_entry* entry,
+       phys_addr_t physicalAddress, uint32 attributes, uint32 memoryType,
+       bool globalPage)
+{
+       page_table_entry page = (physicalAddress & X86_PTE_ADDRESS_MASK)
+               | X86_PTE_PRESENT | (globalPage ? X86_PTE_GLOBAL : 0)
+               | memory_type_to_pte_flags(memoryType);
+
+       // if the page is user accessible, it's automatically
+       // accessible in kernel space, too (but with the same
+       // protection)
+       if ((attributes & B_USER_PROTECTION) != 0) {
+               page |= X86_PTE_USER;
+               if ((attributes & B_WRITE_AREA) != 0)
+                       page |= X86_PTE_WRITABLE;
+       } else if ((attributes & B_KERNEL_WRITE_AREA) != 0)
+               page |= X86_PTE_WRITABLE;
+
+       // put it in the page table
+       *(volatile page_table_entry*)entry = page;
+}
+
+
+//     #pragma mark -
+
+
+uint32
+i386_translation_map_get_pgdir(VMTranslationMap* map)
+{
+       return static_cast<X86VMTranslationMap*>(map)->PhysicalPageDir();
+}
+
+
+void
+x86_update_all_pgdirs(int index, page_directory_entry e)
+{
+       unsigned int state = disable_interrupts();
+
+       acquire_spinlock(&sPagingStructuresListLock);
+
+       PagingStructuresList::Iterator it = sPagingStructuresList.GetIterator();
+       while (X86PagingStructures32Bit* info = it.Next())
+               info->pgdir_virt[index] = e;
+
+       release_spinlock(&sPagingStructuresListLock);
+       restore_interrupts(state);
+}
+
+
+void
+x86_put_pgtable_in_pgdir(page_directory_entry *entry,
+       phys_addr_t pgtablePhysical, uint32 attributes)
+{
+       *entry = (pgtablePhysical & X86_PDE_ADDRESS_MASK)
+               | X86_PDE_PRESENT
+               | X86_PDE_WRITABLE
+               | X86_PDE_USER;
+               // TODO: we ignore the attributes of the page table - for 
compatibility
+               // with BeOS we allow having user accessible areas in the 
kernel address
+               // space. This is currently being used by some drivers, mainly 
for the
+               // frame buffer. Our current real time data implementation 
makes use of
+               // this fact, too.
+               // We might want to get rid of this possibility one day, 
especially if
+               // we intend to port it to a platform that does not support 
this.
+}
+
+
+void
+x86_early_prepare_page_tables(page_table_entry* pageTables, addr_t address,
+       size_t size)
+{
+       memset(pageTables, 0, B_PAGE_SIZE * (size / (B_PAGE_SIZE * 1024)));
+
+       // put the array of pgtables directly into the kernel pagedir
+       // these will be wired and kept mapped into virtual space to be easy to 
get
+       // to
+       {
+               addr_t virtualTable = (addr_t)pageTables;
+
+               for (size_t i = 0; i < (size / (B_PAGE_SIZE * 1024));
+                               i++, virtualTable += B_PAGE_SIZE) {
+                       phys_addr_t physicalTable = 0;
+                       early_query(virtualTable, &physicalTable);
+                       page_directory_entry* entry = &sPageHolePageDir[
+                               (address / (B_PAGE_SIZE * 1024)) + i];
+                       x86_put_pgtable_in_pgdir(entry, physicalTable,
+                               B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
+               }
+       }
+}
+
+
+// #pragma mark - VM ops
+
+
+X86VMTranslationMap::X86VMTranslationMap()
+       :
+       fPagingStructures(NULL),
+       fPageMapper(NULL),
+       fInvalidPagesCount(0)
+{
+}
+
+
+X86VMTranslationMap::~X86VMTranslationMap()
+{
+       if (fPagingStructures == NULL)
+               return;
+
+       if (fPageMapper != NULL)
+               fPageMapper->Delete();
+
+       if (fPagingStructures->pgdir_virt != NULL) {
+               // cycle through and free all of the user space pgtables
+               for (uint32 i = VADDR_TO_PDENT(USER_BASE);
+                               i <= VADDR_TO_PDENT(USER_BASE + (USER_SIZE - 
1)); i++) {
+                       if ((fPagingStructures->pgdir_virt[i] & 
X86_PDE_PRESENT) != 0) {
+                               addr_t address = 
fPagingStructures->pgdir_virt[i]
+                                       & X86_PDE_ADDRESS_MASK;
+                               vm_page* page = vm_lookup_page(address / 
B_PAGE_SIZE);
+                               if (!page)
+                                       panic("destroy_tmap: didn't find 
pgtable page\n");
+                               DEBUG_PAGE_ACCESS_START(page);
+                               vm_page_set_state(page, PAGE_STATE_FREE);
+                       }
+               }
+       }
+
+       fPagingStructures->RemoveReference();
+}
+
+
+status_t
+X86VMTranslationMap::Init(bool kernel)
+{
+       TRACE("X86VMTranslationMap::Init()\n");
+
+       fPagingStructures = new(std::nothrow) X86PagingStructures32Bit;
+       if (fPagingStructures == NULL)
+               return B_NO_MEMORY;
+
+       fPagingStructures->active_on_cpus = 0;
+
+       if (!kernel) {
+               // user
+               // allocate a physical page mapper
+               status_t error = sPhysicalPageMapper
+                       ->CreateTranslationMapPhysicalPageMapper(&fPageMapper);
+               if (error != B_OK)
+                       return error;
+
+               // allocate a pgdir
+               fPagingStructures->pgdir_virt = (page_directory_entry 
*)memalign(
+                       B_PAGE_SIZE, B_PAGE_SIZE);
+               if (fPagingStructures->pgdir_virt == NULL)
+                       return B_NO_MEMORY;
+
+               phys_addr_t physicalPageDir;
+               vm_get_page_mapping(VMAddressSpace::KernelID(),
+                       (addr_t)fPagingStructures->pgdir_virt,
+                       &physicalPageDir);
+               fPagingStructures->pgdir_phys = physicalPageDir;
+       } else {
+               // kernel
+               // get the physical page mapper
+               fPageMapper = sKernelPhysicalPageMapper;
+
+               // we already know the kernel pgdir mapping
+               fPagingStructures->pgdir_virt = sKernelVirtualPageDirectory;
+               fPagingStructures->pgdir_phys = sKernelPhysicalPageDirectory;
+       }
+
+       // zero out the bottom portion of the new pgdir
+       memset(fPagingStructures->pgdir_virt + FIRST_USER_PGDIR_ENT, 0,
+               NUM_USER_PGDIR_ENTS * sizeof(page_directory_entry));
+
+       // insert this new map into the map list
+       {
+               int state = disable_interrupts();
+               acquire_spinlock(&sPagingStructuresListLock);
+
+               // copy the top portion of the pgdir from the current one
+               memcpy(fPagingStructures->pgdir_virt + FIRST_KERNEL_PGDIR_ENT,
+                       sKernelVirtualPageDirectory + FIRST_KERNEL_PGDIR_ENT,
+                       NUM_KERNEL_PGDIR_ENTS * sizeof(page_directory_entry));
+
+               sPagingStructuresList.Add(
+                       
static_cast<X86PagingStructures32Bit*>(fPagingStructures));
+
+               release_spinlock(&sPagingStructuresListLock);
+               restore_interrupts(state);
+       }
+
+       return B_OK;
+}
+
+
+/*!    Acquires the map's recursive lock, and resets the invalidate pages 
counter
+       in case it's the first locking recursion.
+*/
+bool
+X86VMTranslationMap::Lock()
+{
+       TRACE("%p->X86VMTranslationMap::Lock()\n", this);
+
+       recursive_lock_lock(&fLock);
+       if (recursive_lock_get_recursion(&fLock) == 1) {
+               // we were the first one to grab the lock
+               TRACE("clearing invalidated page count\n");
+               fInvalidPagesCount = 0;
+       }
+
+       return true;
+}
+
+
+/*!    Unlocks the map, and, if we are actually losing the recursive lock,
+       flush all pending changes of this map (ie. flush TLB caches as
+       needed).
+*/
+void
+X86VMTranslationMap::Unlock()
+{
+       TRACE("%p->X86VMTranslationMap::Unlock()\n", this);
+
+       if (recursive_lock_get_recursion(&fLock) == 1) {
+               // we're about to release it for the last time
+               X86VMTranslationMap::Flush();
+       }
+
+       recursive_lock_unlock(&fLock);
+}
+
+
+size_t
+X86VMTranslationMap::MaxPagesNeededToMap(addr_t start, addr_t end) const
+{
+       // If start == 0, the actual base address is not yet known to the 
caller and
+       // we shall assume the worst case.
+       if (start == 0) {
+               // offset the range so it has the worst possible alignment
+               start = 1023 * B_PAGE_SIZE;
+               end += 1023 * B_PAGE_SIZE;
+       }
+
+       return VADDR_TO_PDENT(end) + 1 - VADDR_TO_PDENT(start);
+}
+
+
+status_t
+X86VMTranslationMap::Map(addr_t va, phys_addr_t pa, uint32 attributes,
+       uint32 memoryType, vm_page_reservation* reservation)
+{
+       TRACE("map_tmap: entry pa 0x%lx va 0x%lx\n", pa, va);
+
+/*
+       dprintf("pgdir at 0x%x\n", pgdir);
+       dprintf("index is %d\n", va / B_PAGE_SIZE / 1024);
+       dprintf("final at 0x%x\n", &pgdir[va / B_PAGE_SIZE / 1024]);
+       dprintf("value is 0x%x\n", *(int *)&pgdir[va / B_PAGE_SIZE / 1024]);
+       dprintf("present bit is %d\n", pgdir[va / B_PAGE_SIZE / 1024].present);
+       dprintf("addr is %d\n", pgdir[va / B_PAGE_SIZE / 1024].addr);
+*/
+       page_directory_entry* pd = fPagingStructures->pgdir_virt;
+
+       // check to see if a page table exists for this range
+       uint32 index = VADDR_TO_PDENT(va);
+       if ((pd[index] & X86_PDE_PRESENT) == 0) {
+               phys_addr_t pgtable;
+               vm_page *page;
+
+               // we need to allocate a pgtable
+               page = vm_page_allocate_page(reservation,
+                       PAGE_STATE_WIRED | VM_PAGE_ALLOC_CLEAR);
+
+               DEBUG_PAGE_ACCESS_END(page);
+
+               pgtable = (phys_addr_t)page->physical_page_number * B_PAGE_SIZE;
+
+               TRACE("map_tmap: asked for free page for pgtable. 0x%lx\n", 
pgtable);
+
+               // put it in the pgdir
+               x86_put_pgtable_in_pgdir(&pd[index], pgtable, attributes
+                       | ((attributes & B_USER_PROTECTION) != 0
+                                       ? B_WRITE_AREA : B_KERNEL_WRITE_AREA));
+
+               // update any other page directories, if it maps kernel space
+               if (index >= FIRST_KERNEL_PGDIR_ENT
+                       && index < (FIRST_KERNEL_PGDIR_ENT + 
NUM_KERNEL_PGDIR_ENTS))
+                       x86_update_all_pgdirs(index, pd[index]);
+
+               fMapCount++;
+       }
+
+       // now, fill in the pentry
+       struct thread* thread = thread_get_current_thread();
+       ThreadCPUPinner pinner(thread);
+
+       page_table_entry* pt = fPageMapper->GetPageTableAt(
+               pd[index] & X86_PDE_ADDRESS_MASK);
+       index = VADDR_TO_PTENT(va);
+
+       ASSERT_PRINT((pt[index] & X86_PTE_PRESENT) == 0,
+               "virtual address: %#" B_PRIxADDR ", existing pte: %#" B_PRIx32, 
va,
+               pt[index]);
+
+       put_page_table_entry_in_pgtable(&pt[index], pa, attributes, memoryType,
+               IS_KERNEL_MAP(map));
+
+       pinner.Unlock();
+
+       // Note: We don't need to invalidate the TLB for this address, as 
previously
+       // the entry was not present and the TLB doesn't cache those entries.
+
+       fMapCount++;
+
+       return 0;
+}
+
+
+status_t
+X86VMTranslationMap::Unmap(addr_t start, addr_t end)
+{
+       page_directory_entry *pd = fPagingStructures->pgdir_virt;
+
+       start = ROUNDDOWN(start, B_PAGE_SIZE);
+       end = ROUNDUP(end, B_PAGE_SIZE);
+
+       TRACE("unmap_tmap: asked to free pages 0x%lx to 0x%lx\n", start, end);
+
+restart:
+       if (start >= end)
+               return B_OK;
+
+       int index = VADDR_TO_PDENT(start);
+       if ((pd[index] & X86_PDE_PRESENT) == 0) {
+               // no pagetable here, move the start up to access the next page 
table
+               start = ROUNDUP(start + 1, B_PAGE_SIZE * 1024);
+               if (start == 0)
+                       return B_OK;
+               goto restart;
+       }
+
+       struct thread* thread = thread_get_current_thread();
+       ThreadCPUPinner pinner(thread);
+
+       page_table_entry* pt = fPageMapper->GetPageTableAt(
+               pd[index] & X86_PDE_ADDRESS_MASK);
+
+       for (index = VADDR_TO_PTENT(start); (index < 1024) && (start < end);
+                       index++, start += B_PAGE_SIZE) {
+               if ((pt[index] & X86_PTE_PRESENT) == 0) {
+                       // page mapping not valid
+                       continue;
+               }
+
+               TRACE("unmap_tmap: removing page 0x%lx\n", start);
+
+               page_table_entry oldEntry = 
clear_page_table_entry_flags(&pt[index],
+                       X86_PTE_PRESENT);
+               fMapCount--;
+
+               if ((oldEntry & X86_PTE_ACCESSED) != 0) {
+                       // Note, that we only need to invalidate the address, 
if the
+                       // accessed flags was set, since only then the entry 
could have been
+                       // in any TLB.
+                       if (fInvalidPagesCount < PAGE_INVALIDATE_CACHE_SIZE)
+                               fInvalidPages[fInvalidPagesCount] = start;
+
+                       fInvalidPagesCount++;
+               }
+       }
+
+       pinner.Unlock();
+
+       goto restart;
+}
+
+
+/*!    Caller must have locked the cache of the page to be unmapped.
+       This object shouldn't be locked.
+*/
+status_t
+X86VMTranslationMap::UnmapPage(VMArea* area, addr_t address,
+       bool updatePageQueue)
+{
+       ASSERT(address % B_PAGE_SIZE == 0);
+
+       page_directory_entry* pd = fPagingStructures->pgdir_virt;
+
+       TRACE("X86VMTranslationMap::UnmapPage(%#" B_PRIxADDR ")\n", address);
+
+       RecursiveLocker locker(fLock);
+
+       int index = VADDR_TO_PDENT(address);
+       if ((pd[index] & X86_PDE_PRESENT) == 0)
+               return B_ENTRY_NOT_FOUND;
+
+       ThreadCPUPinner pinner(thread_get_current_thread());
+
+       page_table_entry* pt = fPageMapper->GetPageTableAt(
+               pd[index] & X86_PDE_ADDRESS_MASK);
+
+       index = VADDR_TO_PTENT(address);
+       page_table_entry oldEntry = clear_page_table_entry(&pt[index]);
+
+       pinner.Unlock();
+
+       if ((oldEntry & X86_PTE_PRESENT) == 0) {
+               // page mapping not valid
+               return B_ENTRY_NOT_FOUND;
+       }
+
+       fMapCount--;
+
+       if ((oldEntry & X86_PTE_ACCESSED) != 0) {
+               // Note, that we only need to invalidate the address, if the
+               // accessed flags was set, since only then the entry could have 
been
+               // in any TLB.
+               if (fInvalidPagesCount < PAGE_INVALIDATE_CACHE_SIZE)
+                       fInvalidPages[fInvalidPagesCount] = address;
+
+               fInvalidPagesCount++;
+
+               Flush();
+
+               // NOTE: Between clearing the page table entry and Flush() other
+               // processors (actually even this processor with another thread 
of the
+               // same team) could still access the page in question via their 
cached
+               // entry. We can obviously lose a modified flag in this case, 
with the
+               // effect that the page looks unmodified (and might thus be 
recycled),
+               // but is actually modified.
+               // In most cases this is harmless, but for 
vm_remove_all_page_mappings()
+               // this is actually a problem.
+               // Interestingly FreeBSD seems to ignore this problem as well
+               // (cf. pmap_remove_all()), unless I've missed something.
+       }
+
+       if (area->cache_type == CACHE_TYPE_DEVICE)
+               return B_OK;
+
+       // get the page
+       vm_page* page = vm_lookup_page(
+               (oldEntry & X86_PTE_ADDRESS_MASK) / B_PAGE_SIZE);
+       ASSERT(page != NULL);
+
+       // transfer the accessed/dirty flags to the page
+       if ((oldEntry & X86_PTE_ACCESSED) != 0)
+               page->accessed = true;
+       if ((oldEntry & X86_PTE_DIRTY) != 0)
+               page->modified = true;
+
+       // remove the mapping object/decrement the wired_count of the page
+       vm_page_mapping* mapping = NULL;
+       if (area->wiring == B_NO_LOCK) {
+               vm_page_mappings::Iterator iterator = 
page->mappings.GetIterator();
+               while ((mapping = iterator.Next()) != NULL) {
+                       if (mapping->area == area) {
+                               area->mappings.Remove(mapping);
+                               page->mappings.Remove(mapping);
+                               break;
+                       }
+               }
+
+               ASSERT(mapping != NULL);
+       } else
+               page->wired_count--;
+
+       locker.Unlock();
+
+       if (page->wired_count == 0 && page->mappings.IsEmpty()) {
+               atomic_add(&gMappedPagesCount, -1);
+
+               if (updatePageQueue) {
+                       if (page->Cache()->temporary)
+                               vm_page_set_state(page, PAGE_STATE_INACTIVE);
+                       else if (page->modified)
+                               vm_page_set_state(page, PAGE_STATE_MODIFIED);
+                       else
+                               vm_page_set_state(page, PAGE_STATE_CACHED);
+               }
+       }
+
+       if (mapping != NULL) {
+               bool isKernelSpace = area->address_space == 
VMAddressSpace::Kernel();
+               object_cache_free(gPageMappingsObjectCache, mapping,
+                       CACHE_DONT_WAIT_FOR_MEMORY
+                               | (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE 
: 0));
+       }
+
+       return B_OK;
+}
+
+
+void
+X86VMTranslationMap::UnmapPages(VMArea* area, addr_t base, size_t size,
+       bool updatePageQueue)
+{
+       page_directory_entry* pd = fPagingStructures->pgdir_virt;
+
+       addr_t start = base;
+       addr_t end = base + size;
+
+       TRACE("X86VMTranslationMap::UnmapPages(%p, %#" B_PRIxADDR ", %#"
+               B_PRIxADDR ")\n", area, start, end);
+
+       VMAreaMappings queue;
+
+       RecursiveLocker locker(fLock);
+
+       while (start < end) {
+               int index = VADDR_TO_PDENT(start);
+               if ((pd[index] & X86_PDE_PRESENT) == 0) {
+                       // no page table here, move the start up to access the 
next page
+                       // table
+                       start = ROUNDUP(start + 1, B_PAGE_SIZE * 1024);
+                       if (start == 0)
+                               break;
+                       continue;
+               }
+
+               struct thread* thread = thread_get_current_thread();
+               ThreadCPUPinner pinner(thread);
+
+               page_table_entry* pt = fPageMapper->GetPageTableAt(
+                       pd[index] & X86_PDE_ADDRESS_MASK);
+
+               for (index = VADDR_TO_PTENT(start); (index < 1024) && (start < 
end);
+                               index++, start += B_PAGE_SIZE) {
+                       page_table_entry oldEntry = 
clear_page_table_entry(&pt[index]);
+                       if ((oldEntry & X86_PTE_PRESENT) == 0)
+                               continue;
+
+                       fMapCount--;
+
+                       if ((oldEntry & X86_PTE_ACCESSED) != 0) {
+                               // Note, that we only need to invalidate the 
address, if the
+                               // accessed flags was set, since only then the 
entry could have
+                               // been in any TLB.
+                               if (fInvalidPagesCount < 
PAGE_INVALIDATE_CACHE_SIZE)
+                                       fInvalidPages[fInvalidPagesCount] = 
start;
+
+                               fInvalidPagesCount++;
+                       }
+
+                       if (area->cache_type != CACHE_TYPE_DEVICE) {
+                               // get the page
+                               vm_page* page = vm_lookup_page(
+                                       (oldEntry & X86_PTE_ADDRESS_MASK) / 
B_PAGE_SIZE);
+                               ASSERT(page != NULL);
+
+                               DEBUG_PAGE_ACCESS_START(page);
+
+                               // transfer the accessed/dirty flags to the page
+                               if ((oldEntry & X86_PTE_ACCESSED) != 0)
+                                       page->accessed = true;
+                               if ((oldEntry & X86_PTE_DIRTY) != 0)
+                                       page->modified = true;
+
+                               // remove the mapping object/decrement the 
wired_count of the
+                               // page
+                               if (area->wiring == B_NO_LOCK) {
+                                       vm_page_mapping* mapping = NULL;
+                                       vm_page_mappings::Iterator iterator
+                                               = page->mappings.GetIterator();
+                                       while ((mapping = iterator.Next()) != 
NULL) {
+                                               if (mapping->area == area)
+                                                       break;
+                                       }
+
+                                       ASSERT(mapping != NULL);
+
+                                       area->mappings.Remove(mapping);
+                                       page->mappings.Remove(mapping);
+                                       queue.Add(mapping);
+                               } else
+                                       page->wired_count--;
+
+                               if (page->wired_count == 0 && 
page->mappings.IsEmpty()) {
+                                       atomic_add(&gMappedPagesCount, -1);
+
+                                       if (updatePageQueue) {
+                                               if (page->Cache()->temporary)
+                                                       vm_page_set_state(page, 
PAGE_STATE_INACTIVE);
+                                               else if (page->modified)
+                                                       vm_page_set_state(page, 
PAGE_STATE_MODIFIED);
+                                               else
+                                                       vm_page_set_state(page, 
PAGE_STATE_CACHED);
+                                       }
+                               }
+
+                               DEBUG_PAGE_ACCESS_END(page);
+                       }
+               }
+
+               Flush();
+                       // flush explicitly, since we directly use the lock
+
+               pinner.Unlock();
+       }
+
+       // TODO: As in UnmapPage() we can lose page dirty flags here. ATM it's 
not
+       // really critical here, as in all cases this method is used, the 
unmapped
+       // area range is unmapped for good (resized/cut) and the pages will 
likely
+       // be freed.
+
+       locker.Unlock();
+
+       // free removed mappings
+       bool isKernelSpace = area->address_space == VMAddressSpace::Kernel();
+       uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY
+               | (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
+       while (vm_page_mapping* mapping = queue.RemoveHead())
+               object_cache_free(gPageMappingsObjectCache, mapping, freeFlags);
+}
+
+
+void
+X86VMTranslationMap::UnmapArea(VMArea* area, bool deletingAddressSpace,
+       bool ignoreTopCachePageFlags)
+{
+       if (area->cache_type == CACHE_TYPE_DEVICE || area->wiring != B_NO_LOCK) 
{
+               X86VMTranslationMap::UnmapPages(area, area->Base(), 
area->Size(), true);
+               return;
+       }
+
+       bool unmapPages = !deletingAddressSpace || !ignoreTopCachePageFlags;
+
+       page_directory_entry* pd = fPagingStructures->pgdir_virt;
+
+       RecursiveLocker locker(fLock);
+
+       VMAreaMappings mappings;
+       mappings.MoveFrom(&area->mappings);
+
+       for (VMAreaMappings::Iterator it = mappings.GetIterator();
+                       vm_page_mapping* mapping = it.Next();) {
+               vm_page* page = mapping->page;
+               page->mappings.Remove(mapping);
+
+               VMCache* cache = page->Cache();
+
+               bool pageFullyUnmapped = false;
+               if (page->wired_count == 0 && page->mappings.IsEmpty()) {
+                       atomic_add(&gMappedPagesCount, -1);
+                       pageFullyUnmapped = true;
+               }
+
+               if (unmapPages || cache != area->cache) {
+                       addr_t address = area->Base()
+                               + ((page->cache_offset * B_PAGE_SIZE) - 
area->cache_offset);
+
+                       int index = VADDR_TO_PDENT(address);
+                       if ((pd[index] & X86_PDE_PRESENT) == 0) {
+                               panic("page %p has mapping for area %p (%#" 
B_PRIxADDR "), but "
+                                       "has no page dir entry", page, area, 
address);
+                               continue;
+                       }
+
+                       ThreadCPUPinner pinner(thread_get_current_thread());
+
+                       page_table_entry* pt = fPageMapper->GetPageTableAt(
+                               pd[index] & X86_PDE_ADDRESS_MASK);
+                       page_table_entry oldEntry = clear_page_table_entry(
+                               &pt[VADDR_TO_PTENT(address)]);
+
+                       pinner.Unlock();
+
+                       if ((oldEntry & X86_PTE_PRESENT) == 0) {
+                               panic("page %p has mapping for area %p (%#" 
B_PRIxADDR "), but "
+                                       "has no page table entry", page, area, 
address);
+                               continue;
+                       }
+
+                       // transfer the accessed/dirty flags to the page and 
invalidate
+                       // the mapping, if necessary
+                       if ((oldEntry & X86_PTE_ACCESSED) != 0) {
+                               page->accessed = true;
+
+                               if (!deletingAddressSpace) {
+                                       if (fInvalidPagesCount < 
PAGE_INVALIDATE_CACHE_SIZE)
+                                               
fInvalidPages[fInvalidPagesCount] = address;
+
+                                       fInvalidPagesCount++;
+                               }
+                       }
+
+                       if ((oldEntry & X86_PTE_DIRTY) != 0)
+                               page->modified = true;
+
+                       if (pageFullyUnmapped) {
+                               DEBUG_PAGE_ACCESS_START(page);
+
+                               if (cache->temporary)
+                                       vm_page_set_state(page, 
PAGE_STATE_INACTIVE);
+                               else if (page->modified)
+                                       vm_page_set_state(page, 
PAGE_STATE_MODIFIED);
+                               else
+                                       vm_page_set_state(page, 
PAGE_STATE_CACHED);
+
+                               DEBUG_PAGE_ACCESS_END(page);
+                       }
+               }
+
+               fMapCount--;
+       }
+
+       Flush();
+               // flush explicitely, since we directly use the lock
+
+       locker.Unlock();
+
+       bool isKernelSpace = area->address_space == VMAddressSpace::Kernel();
+       uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY
+               | (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
+       while (vm_page_mapping* mapping = mappings.RemoveHead())
+               object_cache_free(gPageMappingsObjectCache, mapping, freeFlags);
+}
+
+
+status_t
+X86VMTranslationMap::Query(addr_t va, phys_addr_t *_physical, uint32 *_flags)
+{
+       // default the flags to not present
+       *_flags = 0;
+       *_physical = 0;
+
+       int index = VADDR_TO_PDENT(va);
+       page_directory_entry *pd = fPagingStructures->pgdir_virt;
+       if ((pd[index] & X86_PDE_PRESENT) == 0) {
+               // no pagetable here
+               return B_OK;
+       }
+
+       struct thread* thread = thread_get_current_thread();
+       ThreadCPUPinner pinner(thread);
+
+       page_table_entry* pt = fPageMapper->GetPageTableAt(

[... truncated: 2151 lines follow ...]

Other related posts:

  • » [haiku-commits] r37050 - haiku/trunk/src/system/kernel/arch/x86 - ingo_weinhold