FreeBSD/sys/i386/i386/pmap.c
/*
 *	Set the physical protection on the
 *	specified range of this map as requested.
 */
Set the physical protection on the
specified range of this map as requested.
Set the physical protection on the specified range of this map as requested .
void
pmap_protect(pmap_t pmap, vm_offset_t sva, vm_offset_t eva, vm_prot_t prot)
{
	vm_offset_t pdnxt;
	pd_entry_t ptpaddr;
	pt_entry_t *pte;
	boolean_t anychanged, pv_lists_locked;
	KASSERT((prot & ~VM_PROT_ALL) == 0, ("invalid prot %x", prot));
	if (prot == VM_PROT_NONE) {
		pmap_remove(pmap, sva, eva);
		return;
	}
#if defined(PAE) || defined(PAE_TABLES)
	if ((prot & (VM_PROT_WRITE|VM_PROT_EXECUTE)) ==
	    (VM_PROT_WRITE|VM_PROT_EXECUTE))
		return;
#else
	if (prot & VM_PROT_WRITE)
		return;
#endif
	if (pmap_is_current(pmap))
		pv_lists_locked = FALSE;
	else {
		pv_lists_locked = TRUE;
resume:
		rw_wlock(&pvh_global_lock);
		sched_pin();
	}
	anychanged = FALSE;
	PMAP_LOCK(pmap);
	for (; sva < eva; sva = pdnxt) {
		pt_entry_t obits, pbits;
		u_int pdirindex;
		pdnxt = (sva + NBPDR) & ~PDRMASK;
		if (pdnxt < sva)
			pdnxt = eva;
		pdirindex = sva >> PDRSHIFT;
		ptpaddr = pmap->pm_pdir[pdirindex];
		if (ptpaddr == 0)
			continue;
		if ((ptpaddr & PG_PS) != 0) {
			if (sva + NBPDR == pdnxt && eva >= pdnxt) {
				if (pmap_protect_pde(pmap,
				    &pmap->pm_pdir[pdirindex], sva, prot))
					anychanged = TRUE;
				continue;
			} else {
				if (!pv_lists_locked) {
					pv_lists_locked = TRUE;
					if (!rw_try_wlock(&pvh_global_lock)) {
						if (anychanged)
							pmap_invalidate_all(
							    pmap);
						PMAP_UNLOCK(pmap);
						goto resume;
					}
					sched_pin();
				}
				if (!pmap_demote_pde(pmap,
				    &pmap->pm_pdir[pdirindex], sva)) {
					continue;
				}
			}
		}
		if (pdnxt > eva)
			pdnxt = eva;
		for (pte = pmap_pte_quick(pmap, sva); sva != pdnxt; pte++,
		    sva += PAGE_SIZE) {
			vm_page_t m;
retry:
			obits = pbits = *pte;
			if ((pbits & PG_V) == 0)
				continue;
			if ((prot & VM_PROT_WRITE) == 0) {
				if ((pbits & (PG_MANAGED | PG_M | PG_RW)) ==
				    (PG_MANAGED | PG_M | PG_RW)) {
					m = PHYS_TO_VM_PAGE(pbits & PG_FRAME);
					vm_page_dirty(m);
				}
				pbits &= ~(PG_RW | PG_M);
			}
#if defined(PAE) || defined(PAE_TABLES)
			if ((prot & VM_PROT_EXECUTE) == 0)
				pbits |= pg_nx;
#endif
			if (pbits != obits) {
#if defined(PAE) || defined(PAE_TABLES)
				if (!atomic_cmpset_64(pte, obits, pbits))
					goto retry;
#else
				if (!atomic_cmpset_int((u_int *)pte, obits,
				    pbits))
					goto retry;
#endif
				if (obits & PG_G)
					pmap_invalidate_page(pmap, sva);
				else
					anychanged = TRUE;
			}
		}
	}
	if (anychanged)
		pmap_invalidate_all(pmap);
	if (pv_lists_locked) {
		sched_unpin();
		rw_wunlock(&pvh_global_lock);
	}
	PMAP_UNLOCK(pmap);
}
void pmap_protect ( pmap_t pmap , vm_offset_t sva , vm_offset_t eva , vm_prot_t prot ) { vm_offset_t pdnxt ; pd_entry_t ptpaddr ; pt_entry_t * pte ; boolean_t anychanged , pv_lists_locked ; KASSERT ( ( prot & ~ VM_PROT_ALL ) == 0 , ( " " , prot ) ) ; if ( prot == VM_PROT_NONE ) { pmap_remove ( pmap , sva , eva ) ; return ; } #if defined ( PAE ) || defined ( PAE_TABLES ) if ( ( prot & ( VM_PROT_WRITE | VM_PROT_EXECUTE ) ) == ( VM_PROT_WRITE | VM_PROT_EXECUTE ) ) return ; #else if ( prot & VM_PROT_WRITE ) return ; #endif if ( pmap_is_current ( pmap ) ) pv_lists_locked = FALSE ; else { pv_lists_locked = TRUE ; resume : rw_wlock ( & pvh_global_lock ) ; sched_pin ( ) ; } anychanged = FALSE ; PMAP_LOCK ( pmap ) ; for ( ; sva < eva ; sva = pdnxt ) { pt_entry_t obits , pbits ; u_int pdirindex ; pdnxt = ( sva + NBPDR ) & ~ PDRMASK ; if ( pdnxt < sva ) pdnxt = eva ; pdirindex = sva >> PDRSHIFT ; ptpaddr = pmap -> pm_pdir [ pdirindex ] ; if ( ptpaddr == 0 ) continue ; if ( ( ptpaddr & PG_PS ) != 0 ) { if ( sva + NBPDR == pdnxt && eva >= pdnxt ) { if ( pmap_protect_pde ( pmap , & pmap -> pm_pdir [ pdirindex ] , sva , prot ) ) anychanged = TRUE ; continue ; } else { if ( ! pv_lists_locked ) { pv_lists_locked = TRUE ; if ( ! rw_try_wlock ( & pvh_global_lock ) ) { if ( anychanged ) pmap_invalidate_all ( pmap ) ; PMAP_UNLOCK ( pmap ) ; goto resume ; } sched_pin ( ) ; } if ( ! pmap_demote_pde ( pmap , & pmap -> pm_pdir [ pdirindex ] , sva ) ) { continue ; } } } if ( pdnxt > eva ) pdnxt = eva ; for ( pte = pmap_pte_quick ( pmap , sva ) ; sva != pdnxt ; pte ++ , sva += PAGE_SIZE ) { vm_page_t m ; retry : obits = pbits = * pte ; if ( ( pbits & PG_V ) == 0 ) continue ; if ( ( prot & VM_PROT_WRITE ) == 0 ) { if ( ( pbits & ( PG_MANAGED | PG_M | PG_RW ) ) == ( PG_MANAGED | PG_M | PG_RW ) ) { m = PHYS_TO_VM_PAGE ( pbits & PG_FRAME ) ; vm_page_dirty ( m ) ; } pbits &= ~ ( PG_RW | PG_M ) ; } #if defined ( PAE ) || defined ( PAE_TABLES ) if ( ( prot & VM_PROT_EXECUTE ) == 0 ) pbits |= pg_nx ; #endif if ( pbits != obits ) { #if defined ( PAE ) || defined ( PAE_TABLES ) if ( ! atomic_cmpset_64 ( pte , obits , pbits ) ) goto retry ; #else if ( ! atomic_cmpset_int ( ( u_int * ) pte , obits , pbits ) ) goto retry ; #endif if ( obits & PG_G ) pmap_invalidate_page ( pmap , sva ) ; else anychanged = TRUE ; } } } if ( anychanged ) pmap_invalidate_all ( pmap ) ; if ( pv_lists_locked ) { sched_unpin ( ) ; rw_wunlock ( & pvh_global_lock ) ; } PMAP_UNLOCK ( pmap ) ; }
Set the physical protection on the
      specified range of this map as requested.
Set the physical protection on the specified range of this map as requested .
/*
		 * Weed out invalid mappings. Note: we assume that the page
		 * directory table is always allocated, and in kernel virtual.
		 */ /*
		 * Check for large page.
		 */ /*
			 * Are we protecting the entire large page?  If not,
			 * demote the mapping and fall through.
			 */ /*
				 * The TLB entry for a PG_G mapping is
				 * invalidated by pmap_protect_pde().
				 */ /*
					 * The large page mapping was
					 * destroyed.
					 */ /*
			 * Regardless of whether a pte is 32 or 64 bits in
			 * size, PG_RW, PG_A, and PG_M are among the least
			 * significant 32 bits.
			 */