rem: TRACE(("vm_soft_fault: just allocated page 0x%lx\n", page->ppn));
add: FTRACE(("vm_soft_fault: just allocated page 0x%lx\n", page->ppn));
context: vm_soft_fault(addr_t originalAddress, bool isWrite, bool isUser){	vm_address_space *aspace;	vm_virtual_map *map;	vm_region *region;	vm_cache_ref *cache_ref;	vm_cache_ref *last_cache_ref;	vm_cache_ref *top_cache_ref;	off_t cache_offset;	vm_page dummy_page;	vm_page *page = NULL;	addr_t address;	int change_count;	int err;	TRACE(("vm_soft_fault: thid 0x%lx address 0x%lx, isWrite %d, isUser %d\n",		thread_get_current_thread_id(), originalAddress, isWrite, isUser));	address = ROUNDOWN(originalAddress, B_PAGE_SIZE);	if (IS_KERNEL_ADDRESS(address)) {		aspace = vm_get_kernel_aspace();	} else if (IS_USER_ADDRESS(address)) {		aspace = vm_get_current_user_aspace();		if (aspace == NULL) {			if (isUser == false) {				dprintf("vm_soft_fault: kernel thread accessing invalid user memory!\n");				return ERR_VM_PF_FATAL;			} else {				// XXX weird state.				panic("vm_soft_fault: non kernel thread accessing user memory that doesn't exist!\n");			}		}	} else {		// the hit was probably in the 64k DMZ between kernel and user space		// this keeps a user space thread from passing a buffer that crosses into kernel space		return ERR_VM_PF_FATAL;	}	map = &aspace->virtual_map;	atomic_add(&aspace->fault_count, 1);	// Get the area the fault was in	acquire_sem_etc(map->sem, READ_COUNT, 0, 0);	region = vm_virtual_map_lookup(map, address);	if (region == NULL) {		release_sem_etc(map->sem, READ_COUNT, 0);		vm_put_aspace(aspace);		dprintf("vm_soft_fault: va 0x%lx not covered by region in address space\n", originalAddress);		return ERR_VM_PF_BAD_ADDRESS; // BAD_ADDRESS	}	// check permissions	if (isUser && (region->lock & B_USER_PROTECTION) == 0) {		release_sem_etc(map->sem, READ_COUNT, 0);		vm_put_aspace(aspace);		dprintf("user access on kernel region 0x%lx at %p\n", region->id, (void *)originalAddress);		return ERR_VM_PF_BAD_PERM; // BAD_PERMISSION	}	if (isWrite && (region->lock & (B_WRITE_AREA | (isUser ? 0 : B_KERNEL_WRITE_AREA))) == 0) {		release_sem_etc(map->sem, READ_COUNT, 0);		vm_put_aspace(aspace);		dprintf("write access attempted on read-only region 0x%lx at %p\n", region->id, (void *)originalAddress);		return ERR_VM_PF_BAD_PERM; // BAD_PERMISSION	}	// We have the area, it was a valid access, so let's try to resolve the page fault now.	// At first, the top most cache from the area is investigated	top_cache_ref = region->cache_ref;	cache_offset = address - region->base + region->cache_offset;	vm_cache_acquire_ref(top_cache_ref, true);	change_count = map->change_count;	release_sem_etc(map->sem, READ_COUNT, 0);	// See if this cache has a fault handler - this will do all the work for us	if (top_cache_ref->cache->store->ops->fault) {		// Note, since the page fault is resolved with interrupts enabled, the		// fault handler could be called more than one for the same reason -		// the store must take this into account		int err = (*top_cache_ref->cache->store->ops->fault)(top_cache_ref->cache->store, aspace, cache_offset);		vm_cache_release_ref(top_cache_ref);		vm_put_aspace(aspace);		return err;	}	// The top most cache has no fault handler, so let's see if the cache or its sources	// already have the page we're searching for (we're going from top to bottom)	dummy_page.state = PAGE_STATE_INACTIVE;	dummy_page.type = PAGE_TYPE_DUMMY;	last_cache_ref = top_cache_ref;	for (cache_ref = top_cache_ref; cache_ref; cache_ref = (cache_ref->cache->source) ? cache_ref->cache->source->ref : NULL) {		mutex_lock(&cache_ref->lock);		for (;;) {			page = vm_cache_lookup_page(cache_ref, cache_offset);			if (page != NULL && page->state != PAGE_STATE_BUSY) {				vm_page_set_state(page, PAGE_STATE_BUSY);				mutex_unlock(&cache_ref->lock);				break;			}			if (page == NULL)				break;			// page must be busy			// ToDo: don't wait forever!			mutex_unlock(&cache_ref->lock);			snooze(20000);			mutex_lock(&cache_ref->lock);		}		if (page != NULL)			break;		// The current cache does not contain the page we're looking for		// If we're at the top most cache, insert the dummy page here to keep other threads		// from faulting on the same address and chasing us up the cache chain		if (cache_ref == top_cache_ref) {			dummy_page.state = PAGE_STATE_BUSY;			vm_cache_insert_page(cache_ref, &dummy_page, cache_offset);		}		// see if the vm_store has it		if (cache_ref->cache->store->ops->has_page != NULL			&& cache_ref->cache->store->ops->has_page(cache_ref->cache->store, cache_offset)) {			size_t bytesRead;			iovec vec;			vec.iov_len = bytesRead = B_PAGE_SIZE;			mutex_unlock(&cache_ref->lock);			page = vm_page_allocate_page(PAGE_STATE_FREE);			aspace->translation_map.ops->get_physical_page(page->ppn * B_PAGE_SIZE, (addr_t *)&vec.iov_base, PHYSICAL_PAGE_CAN_WAIT);			// ToDo: handle errors here			err = cache_ref->cache->store->ops->read(cache_ref->cache->store, cache_offset, &vec, 1, &bytesRead);			aspace->translation_map.ops->put_physical_page((addr_t)vec.iov_base);			mutex_lock(&cache_ref->lock);			if (cache_ref == top_cache_ref) {				vm_cache_remove_page(cache_ref, &dummy_page);				dummy_page.state = PAGE_STATE_INACTIVE;			}			vm_cache_insert_page(cache_ref, page, cache_offset);			mutex_unlock(&cache_ref->lock);			break;		}		mutex_unlock(&cache_ref->lock);		last_cache_ref = cache_ref;	}	if (!cache_ref) {		// We rolled off the end of the cache chain, so we need to decide which		// cache will get the new page we're about to create.				cache_ref = isWrite ? top_cache_ref : last_cache_ref;			// Read-only pages come in the deepest cache - only the 			// top most cache may have direct write access.	}	if (page == NULL) {		// we still haven't found a page, so we allocate a clean one		page = vm_page_allocate_page(PAGE_STATE_CLEAR);		TRACE(("vm_soft_fault: just allocated page 0x%lx\n", page->ppn));		// Insert the new page into our cache, and replace it with the dummy page if necessary		mutex_lock(&cache_ref->lock);		// if we inserted a dummy page into this cache, we have to remove it now		if (dummy_page.state == PAGE_STATE_BUSY && dummy_page.cache == cache_ref->cache) {			vm_cache_remove_page(cache_ref, &dummy_page);			dummy_page.state = PAGE_STATE_INACTIVE;		}		vm_cache_insert_page(cache_ref, page, cache_offset);		mutex_unlock(&cache_ref->lock);		if (dummy_page.state == PAGE_STATE_BUSY) {			// we had inserted the dummy cache in another cache, so let's remove it from there			vm_cache_ref *temp_cache = dummy_page.cache->ref;			mutex_lock(&temp_cache->lock);			vm_cache_remove_page(temp_cache, &dummy_page);			mutex_unlock(&temp_cache->lock);			dummy_page.state = PAGE_STATE_INACTIVE;		}	}	// We now have the page and a cache it belongs to - we now need to make	// sure that the area's cache can access it, too, and sees the correct data	if (page->cache != top_cache_ref->cache && isWrite) {		// now we have a page that has the data we want, but in the wrong cache object		// so we need to copy it and stick it into the top cache		vm_page *src_page = page;		void *src, *dest;		TRACE(("get new page, copy it, and put it into the topmost cache\n"));		page = vm_page_allocate_page(PAGE_STATE_FREE);		// try to get a mapping for the src and dest page so we can copy it		for (;;) {			(*aspace->translation_map.ops->get_physical_page)(src_page->ppn * B_PAGE_SIZE, (addr_t *)&src, PHYSICAL_PAGE_CAN_WAIT);			err = (*aspace->translation_map.ops->get_physical_page)(page->ppn * B_PAGE_SIZE, (addr_t *)&dest, PHYSICAL_PAGE_NO_WAIT);			if (err == B_NO_ERROR)				break;			// it couldn't map the second one, so sleep and retry			// keeps an extremely rare deadlock from occuring			(*aspace->translation_map.ops->put_physical_page)((addr_t)src);			snooze(5000);		}		memcpy(dest, src, B_PAGE_SIZE);		(*aspace->translation_map.ops->put_physical_page)((addr_t)src);		(*aspace->translation_map.ops->put_physical_page)((addr_t)dest);		vm_page_set_state(src_page, PAGE_STATE_ACTIVE);		mutex_lock(&top_cache_ref->lock);		// Insert the new page into our cache, and replace it with the dummy page if necessary		// if we inserted a dummy page into this cache, we have to remove it now		if (dummy_page.state == PAGE_STATE_BUSY && dummy_page.cache == top_cache_ref->cache) {			vm_cache_remove_page(top_cache_ref, &dummy_page);			dummy_page.state = PAGE_STATE_INACTIVE;		}		vm_cache_insert_page(top_cache_ref, page, cache_offset);		mutex_unlock(&top_cache_ref->lock);		if (dummy_page.state == PAGE_STATE_BUSY) {			// we had inserted the dummy cache in another cache, so let's remove it from there			vm_cache_ref *temp_cache = dummy_page.cache->ref;			mutex_lock(&temp_cache->lock);			vm_cache_remove_page(temp_cache, &dummy_page);			mutex_unlock(&temp_cache->lock);			dummy_page.state = PAGE_STATE_INACTIVE;		}	}	err = 0;	acquire_sem_etc(map->sem, READ_COUNT, 0, 0);	if (change_count != map->change_count) {		// something may have changed, see if the address is still valid		region = vm_virtual_map_lookup(map, address);		if (region == NULL			|| region->cache_ref != top_cache_ref			|| (address - region->base + region->cache_offset) != cache_offset) {			dprintf("vm_soft_fault: address space layout changed effecting ongoing soft fault\n");			err = ERR_VM_PF_BAD_ADDRESS; // BAD_ADDRESS		}	}	if (err == 0) {		// All went fine, all there is left to do is to map the page into the address space		// If the page doesn't reside in the area's cache, we need to make sure it's		// mapped in read-only, so that we cannot overwrite someone else's data (copy-on-write)		int new_lock = region->lock;		if (page->cache != top_cache_ref->cache && !isWrite)			new_lock &= ~(isUser ? B_WRITE_AREA : B_KERNEL_WRITE_AREA);		atomic_add(&page->ref_count, 1);		(*aspace->translation_map.ops->lock)(&aspace->translation_map);		(*aspace->translation_map.ops->map)(&aspace->translation_map, address,			page->ppn * B_PAGE_SIZE, new_lock);		(*aspace->translation_map.ops->unlock)(&aspace->translation_map);	}	release_sem_etc(map->sem, READ_COUNT, 0);	if (dummy_page.state == PAGE_STATE_BUSY) {		// We still have the dummy page in the cache - that happens if we didn't need		// to allocate a new page before, but could use one in another cache		vm_cache_ref *temp_cache = dummy_page.cache->ref;		mutex_lock(&temp_cache->lock);		vm_cache_remove_page(temp_cache, &dummy_page);		mutex_unlock(&temp_cache->lock);		dummy_page.state = PAGE_STATE_INACTIVE;	}	vm_page_set_state(page, PAGE_STATE_ACTIVE);	vm_cache_release_ref(top_cache_ref);	vm_put_aspace(aspace);	return err;}