FreeBSD/sys/arm/arm/pmap-v6.c
/*
 *  Handle access and R/W emulation faults.
 */
Handle access and R/W emulation faults.
Handle access and R / W emulation faults .
int
pmap_fault(pmap_t pmap, vm_offset_t far, uint32_t fsr, int idx, bool usermode)
{
	pt1_entry_t *pte1p, pte1;
	pt2_entry_t *pte2p, pte2;
	if (pmap == NULL)
		pmap = kernel_pmap;
	if (__predict_false(IN_RANGE2(far, pmap->pm_pt1, NB_IN_PT1))) {
		if (!usermode || (idx != FAULT_ALIGN && idx != FAULT_PERM_L2)) {
			CTR4(KTR_PMAP, "%s: pmap %#x pm_pt1 %#x far %#x",
			    __func__, pmap, pmap->pm_pt1, far);
			panic("%s: pm_pt1 abort", __func__);
		}
		return (KERN_INVALID_ADDRESS);
	}
	if (__predict_false(IN_RANGE2(far, PT2MAP, PT2MAP_SIZE))) {
		if (!usermode || (idx != FAULT_ALIGN &&
		    idx != FAULT_TRAN_L2 && idx != FAULT_PERM_L2)) {
			CTR4(KTR_PMAP, "%s: pmap %#x PT2MAP %#x far %#x",
			    __func__, pmap, PT2MAP, far);
			panic("%s: PT2MAP abort", __func__);
		}
		return (KERN_INVALID_ADDRESS);
	}
	PMAP_LOCK(pmap);
#ifdef SMP
	if (idx == FAULT_TRAN_L1 && usermode && cp15_ats1cur_check(far) == 0) {
		PMAP_UNLOCK(pmap);
		return (KERN_SUCCESS);
	}
#endif
	if (idx == FAULT_ACCESS_L2) {
		pte2p = pt2map_entry(far);
		pte2 = pte2_load(pte2p);
		if (pte2_is_valid(pte2)) {
			pte2_store(pte2p, pte2 | PTE2_A);
			PMAP_UNLOCK(pmap);
			return (KERN_SUCCESS);
		}
	}
	if (idx == FAULT_ACCESS_L1) {
		pte1p = pmap_pte1(pmap, far);
		pte1 = pte1_load(pte1p);
		if (pte1_is_section(pte1)) {
			pte1_store(pte1p, pte1 | PTE1_A);
			PMAP_UNLOCK(pmap);
			return (KERN_SUCCESS);
		}
	}
	if ((fsr & FSR_WNR) && (idx == FAULT_PERM_L2)) {
		pte2p = pt2map_entry(far);
		pte2 = pte2_load(pte2p);
		if (pte2_is_valid(pte2) && !(pte2 & PTE2_RO) &&
		    (pte2 & PTE2_NM)) {
			pte2_store(pte2p, pte2 & ~PTE2_NM);
			tlb_flush(trunc_page(far));
			PMAP_UNLOCK(pmap);
			return (KERN_SUCCESS);
		}
	}
	if ((fsr & FSR_WNR) && (idx == FAULT_PERM_L1)) {
		pte1p = pmap_pte1(pmap, far);
		pte1 = pte1_load(pte1p);
		if (pte1_is_section(pte1) && !(pte1 & PTE1_RO) &&
		    (pte1 & PTE1_NM)) {
			pte1_store(pte1p, pte1 & ~PTE1_NM);
			tlb_flush(pte1_trunc(far));
			PMAP_UNLOCK(pmap);
			return (KERN_SUCCESS);
		}
	}
#ifdef INVARIANTS
	pte2 = pt2tab_load(pmap_pt2tab_entry(pmap, far));
	if (pte2_is_valid(pte2)) {
		pte2 = pte2_load(pt2map_entry(far));
		if (pte2_is_valid(pte2)) {
			pte1 = pte1_load(pmap_pte1(pmap, far));
			if (!pte1_is_valid(pte1)) {
				panic("%s: missing L1 page entry (%p, %#x)",
				    __func__, pmap, far);
			}
		}
	}
#endif
	PMAP_UNLOCK(pmap);
	return (KERN_FAILURE);
}
int pmap_fault ( pmap_t pmap , vm_offset_t far , uint32_t fsr , int idx , bool usermode ) { pt1_entry_t * pte1p , pte1 ; pt2_entry_t * pte2p , pte2 ; if ( pmap == NULL ) pmap = kernel_pmap ; if ( __predict_false ( IN_RANGE2 ( far , pmap -> pm_pt1 , NB_IN_PT1 ) ) ) { if ( ! usermode || ( idx != FAULT_ALIGN && idx != FAULT_PERM_L2 ) ) { CTR4 ( KTR_PMAP , " " , __func__ , pmap , pmap -> pm_pt1 , far ) ; panic ( " " , __func__ ) ; } return ( KERN_INVALID_ADDRESS ) ; } if ( __predict_false ( IN_RANGE2 ( far , PT2MAP , PT2MAP_SIZE ) ) ) { if ( ! usermode || ( idx != FAULT_ALIGN && idx != FAULT_TRAN_L2 && idx != FAULT_PERM_L2 ) ) { CTR4 ( KTR_PMAP , " " , __func__ , pmap , PT2MAP , far ) ; panic ( " " , __func__ ) ; } return ( KERN_INVALID_ADDRESS ) ; } PMAP_LOCK ( pmap ) ; #ifdef SMP if ( idx == FAULT_TRAN_L1 && usermode && cp15_ats1cur_check ( far ) == 0 ) { PMAP_UNLOCK ( pmap ) ; return ( KERN_SUCCESS ) ; } #endif if ( idx == FAULT_ACCESS_L2 ) { pte2p = pt2map_entry ( far ) ; pte2 = pte2_load ( pte2p ) ; if ( pte2_is_valid ( pte2 ) ) { pte2_store ( pte2p , pte2 | PTE2_A ) ; PMAP_UNLOCK ( pmap ) ; return ( KERN_SUCCESS ) ; } } if ( idx == FAULT_ACCESS_L1 ) { pte1p = pmap_pte1 ( pmap , far ) ; pte1 = pte1_load ( pte1p ) ; if ( pte1_is_section ( pte1 ) ) { pte1_store ( pte1p , pte1 | PTE1_A ) ; PMAP_UNLOCK ( pmap ) ; return ( KERN_SUCCESS ) ; } } if ( ( fsr & FSR_WNR ) && ( idx == FAULT_PERM_L2 ) ) { pte2p = pt2map_entry ( far ) ; pte2 = pte2_load ( pte2p ) ; if ( pte2_is_valid ( pte2 ) && ! ( pte2 & PTE2_RO ) && ( pte2 & PTE2_NM ) ) { pte2_store ( pte2p , pte2 & ~ PTE2_NM ) ; tlb_flush ( trunc_page ( far ) ) ; PMAP_UNLOCK ( pmap ) ; return ( KERN_SUCCESS ) ; } } if ( ( fsr & FSR_WNR ) && ( idx == FAULT_PERM_L1 ) ) { pte1p = pmap_pte1 ( pmap , far ) ; pte1 = pte1_load ( pte1p ) ; if ( pte1_is_section ( pte1 ) && ! ( pte1 & PTE1_RO ) && ( pte1 & PTE1_NM ) ) { pte1_store ( pte1p , pte1 & ~ PTE1_NM ) ; tlb_flush ( pte1_trunc ( far ) ) ; PMAP_UNLOCK ( pmap ) ; return ( KERN_SUCCESS ) ; } } #ifdef INVARIANTS pte2 = pt2tab_load ( pmap_pt2tab_entry ( pmap , far ) ) ; if ( pte2_is_valid ( pte2 ) ) { pte2 = pte2_load ( pt2map_entry ( far ) ) ; if ( pte2_is_valid ( pte2 ) ) { pte1 = pte1_load ( pmap_pte1 ( pmap , far ) ) ; if ( ! pte1_is_valid ( pte1 ) ) { panic ( " " , __func__ , pmap , far ) ; } } } #endif PMAP_UNLOCK ( pmap ) ; return ( KERN_FAILURE ) ; }
Handle access and R/W emulation faults.
Handle access and R / W emulation faults .
/*
	 * In kernel, we should never get abort with FAR which is in range of
	 * pmap->pm_pt1 or PT2MAP address spaces. If it happens, stop here
	 * and print out a useful abort message and even get to the debugger
	 * otherwise it likely ends with never ending loop of aborts.
	 */ /*
		 * All L1 tables should always be mapped and present.
		 * However, we check only current one herein. For user mode,
		 * only permission abort from malicious user is not fatal.
		 * And alignment abort as it may have higher priority.
		 */ /*
		 * PT2MAP should be always mapped and present in current
		 * L1 table. However, only existing L2 tables are mapped
		 * in PT2MAP. For user mode, only L2 translation abort and
		 * permission abort from malicious user is not fatal.
		 * And alignment abort as it may have higher priority.
		 */ /*
	 * A pmap lock is used below for handling of access and R/W emulation
	 * aborts. They were handled by atomic operations before so some
	 * analysis of new situation is needed to answer the following question:
	 * Is it safe to use the lock even for these aborts?
	 *
	 * There may happen two cases in general:
	 *
	 * (1) Aborts while the pmap lock is locked already - this should not
	 * happen as pmap lock is not recursive. However, under pmap lock only
	 * internal kernel data should be accessed and such data should be
	 * mapped with A bit set and NM bit cleared. If double abort happens,
	 * then a mapping of data which has caused it must be fixed. Further,
	 * all new mappings are always made with A bit set and the bit can be
	 * cleared only on managed mappings.
	 *
	 * (2) Aborts while another lock(s) is/are locked - this already can
	 * happen. However, there is no difference here if it's either access or
	 * R/W emulation abort, or if it's some other abort.
	 */ /*
	 * Special treatment is due to break-before-make approach done when
	 * pte1 is updated for userland mapping during section promotion or
	 * demotion. If not caught here, pmap_enter() can find a section
	 * mapping on faulting address. That is not allowed.
	 */ /*
	 * Accesss bits for page and section. Note that the entry
	 * is not in TLB yet, so TLB flush is not necessary.
	 *
	 * QQQ: This is hardware emulation, we do not call userret()
	 *      for aborts from user mode.
	 */ /*
	 * Handle modify bits for page and section. Note that the modify
	 * bit is emulated by software. So PTEx_RO is software read only
	 * bit and PTEx_NM flag is real hardware read only bit.
	 *
	 * QQQ: This is hardware emulation, we do not call userret()
	 *      for aborts from user mode.
	 */ /*
	 * QQQ: The previous code, mainly fast handling of access and
	 *      modify bits aborts, could be moved to ASM. Now we are
	 *      starting to deal with not fast aborts.
	 */ /*
	 * Read an entry in PT2TAB associated with both pmap and far.
	 * It's safe because PT2TAB is always mapped.
	 */ /*
		 * Now, when we know that L2 page table is allocated,
		 * we can use PT2MAP to get L2 page table entry.
		 */ /*
			 * If L2 page table entry is valid, make sure that
			 * L1 page table entry is valid too.  Note that we
			 * leave L2 page entries untouched when promoted.
			 */