rem: dprintf("write access attempted on read-only region 0x%lx at %p\n", region->id, (void *)address);
add: dprintf("write access attempted on read-only region 0x%lx at %p\n", region->id, (void *)originalAddress);
context: vm_soft_fault(addr_t address, bool is_write, bool is_user){	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;	int change_count;	int err;//	dprintf("vm_soft_fault: thid 0x%x address 0x%x, is_write %d, is_user %d\n",//		thread_get_current_thread_id(), address, is_write, is_user);	address = ROUNDOWN(address, 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 (is_user == 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);	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", address);		return ERR_VM_PF_BAD_ADDRESS; // BAD_ADDRESS	}	// check permissions	if (is_user && (region->lock & B_USER_PROTECTION) == 0) {		release_sem_etc(map->sem, READ_COUNT, 0);		vm_put_aspace(aspace);		dprintf("user access on kernel region\n");		return ERR_VM_PF_BAD_PERM; // BAD_PERMISSION	}	if (is_write && (region->lock & (B_WRITE_AREA | (is_user ? 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 *)address);		return ERR_VM_PF_BAD_PERM; // BAD_PERMISSION	}	TRACEPFAULT;	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	if (top_cache_ref->cache->store->ops->fault) {		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;	}	TRACEPFAULT;	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);		TRACEPFAULT;		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;			TRACEPFAULT;			// page must be busy			mutex_unlock(&cache_ref->lock);			snooze(20000);			mutex_lock(&cache_ref->lock);		}		TRACEPFAULT;		if (page != NULL)			break;		TRACEPFAULT;		// insert this 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) {			if (cache_ref->cache->store->ops->has_page(cache_ref->cache->store, cache_offset)) {				IOVECS(vecs, 1);				TRACEPFAULT;				mutex_unlock(&cache_ref->lock);				vecs->num = 1;				vecs->total_len = PAGE_SIZE;				vecs->vec[0].iov_len = PAGE_SIZE;				page = vm_page_allocate_page(PAGE_STATE_FREE);				(*aspace->translation_map.ops->get_physical_page)(page->ppn * PAGE_SIZE, (addr_t *)&vecs->vec[0].iov_base, PHYSICAL_PAGE_CAN_WAIT);				// handle errors here				err = cache_ref->cache->store->ops->read(cache_ref->cache->store, cache_offset, vecs);				(*aspace->translation_map.ops->put_physical_page)((addr_t)vecs->vec[0].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;		TRACEPFAULT;	}	TRACEPFAULT;	// 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	if (!cache_ref) {		if (!is_write)			cache_ref = last_cache_ref; // put it in the deepest cache		else			cache_ref = top_cache_ref; // put it in the topmost cache	}	TRACEPFAULT;	if (page == NULL) {		// still haven't found a page, so zero out a new one		page = vm_page_allocate_page(PAGE_STATE_CLEAR);//		dprintf("vm_soft_fault: just allocated page 0x%x\n", page->ppn);		mutex_lock(&cache_ref->lock);		if (dummy_page.state == PAGE_STATE_BUSY && dummy_page.cache_ref == 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);		if (dummy_page.state == PAGE_STATE_BUSY) {			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;		}	}	TRACEPFAULT;	if (page->cache_ref != top_cache_ref && is_write) {		// 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;		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 * PAGE_SIZE, (addr_t *)&src, PHYSICAL_PAGE_CAN_WAIT);			err = (*aspace->translation_map.ops->get_physical_page)(page->ppn * 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, 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);		if (dummy_page.state == PAGE_STATE_BUSY && dummy_page.cache_ref == top_cache_ref) {			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) {			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;		}	}	TRACEPFAULT;	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		}	}	TRACEPFAULT;	if (err == 0) {		// ToDo: find out what this is about!		int new_lock = region->lock;		if (page->cache_ref != top_cache_ref && !is_write)			new_lock &= ~(is_user ? 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 * PAGE_SIZE, new_lock);		(*aspace->translation_map.ops->unlock)(&aspace->translation_map);	}	TRACEPFAULT;	release_sem_etc(map->sem, READ_COUNT, 0);	TRACEPFAULT;	if (dummy_page.state == PAGE_STATE_BUSY) {		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;	}	TRACEPFAULT;	vm_page_set_state(page, PAGE_STATE_ACTIVE);	vm_cache_release_ref(top_cache_ref);	vm_put_aspace(aspace);	TRACEPFAULT;	return err;}