status_t AddressSpace::HandleFault(unsigned int va, bool write, bool user) { va &= ~(PAGE_SIZE - 1); // Round down to a page boundry. fAreaLock.LockRead(); int lastChangeCount = fChangeCount; Area *area = static_cast<Area*>(fAreas.Find(va)); if (area == 0) { fAreaLock.UnlockRead(); return E_BAD_ADDRESS; } PageProtection protection = area->GetProtection(); if ((user && write && !(protection & USER_WRITE)) || (user && !write && !(protection & USER_READ)) || (!user && write && !(protection & SYSTEM_WRITE)) || (!user && !write && !(protection & SYSTEM_READ))) { fAreaLock.UnlockRead(); return E_NOT_ALLOWED; } PageCache *cache = area->GetPageCache(); if (cache == 0) { fAreaLock.UnlockRead(); return E_NOT_ALLOWED; } bool copy = cache->IsCopy(); cache->AcquireRef(); fAreaLock.UnlockRead(); off_t offset = va - area->GetBaseAddress() + area->GetCacheOffset(); Page *page = cache->GetPage(offset, write && cache->IsCopy()); cache->ReleaseRef(); if (page == 0) return E_IO; fAreaLock.LockRead(); if (lastChangeCount != fChangeCount) { // Changes have occured to this address. Make sure that // the area hasn't changed underneath the fault handler. Area *newArea = static_cast<Area*>(fAreas.Find(va)); if (newArea != area || newArea->GetPageCache() != cache || newArea->GetCacheOffset() != offset) fAreaLock.UnlockRead(); return E_BAD_ADDRESS; } // If this is a read from copy-on-write page, it is shared with the // original cache. Mark it read only. if (copy && !write) protection &= ~(USER_WRITE | SYSTEM_WRITE); fPhysicalMap->Map(va, page->GetPhysicalAddress(), protection); fAreaLock.UnlockRead(); AtomicAdd(&fFaultCount, 1); return E_NO_ERROR; }
Area* AddressSpace::CreateArea(const char name[], unsigned int size, AreaWiring wiring, PageProtection protection, PageCache *cache, off_t offset, unsigned int va, int flags) { if (size % PAGE_SIZE != 0 || size == 0) return 0; if (cache != 0 && cache->Commit(offset + size) != offset + size) return 0; fAreaLock.LockWrite(); if (va == INVALID_PAGE) va = FindFreeRange(size, flags); else if (va % PAGE_SIZE != 0 || !fAreas.IsRangeFree(va, va + size - 1)) va = INVALID_PAGE; Area *area = 0; if (va != INVALID_PAGE) { area = new Area(name, protection, cache, offset, wiring); fAreas.Add(area, va, va + size - 1); if (wiring & AREA_WIRED) { for (unsigned int aroffs = 0; aroffs < size; aroffs += PAGE_SIZE) { Page *page = area->GetPageCache()->GetPage(area->GetCacheOffset() + aroffs, false); page->Wire(); fPhysicalMap->Map(va + aroffs, page->GetPhysicalAddress(), protection); } } } fChangeCount++; fAreaLock.UnlockWrite(); return area; }