FreeBSD/sys/i386/i386/pmap.c
/*
 * Changes the specified virtual address range's memory type to that given by
 * the parameter "mode".  The specified virtual address range must be
 * completely contained within either the kernel map.
 *
 * Returns zero if the change completed successfully, and either EINVAL or
 * ENOMEM if the change failed.  Specifically, EINVAL is returned if some part
 * of the virtual address range was not mapped, and ENOMEM is returned if
 * there was insufficient memory available to complete the change.
 */
Changes the specified virtual address range's memory type to that given by
the parameter "mode".  The specified virtual address range must be
completely contained within either the kernel map.

Returns zero if the change completed successfully, and either EINVAL or
ENOMEM if the change failed.  Specifically, EINVAL is returned if some part
of the virtual address range was not mapped, and ENOMEM is returned if
there was insufficient memory available to complete the change.
Changes the specified virtual address range ' s memory type to that given by the parameter " mode " . The specified virtual address range must be completely contained within either the kernel map . Returns zero if the change completed successfully and either EINVAL or ENOMEM if the change failed . Specifically EINVAL is returned if some part of the virtual address range was not mapped and ENOMEM is returned if there was insufficient memory available to complete the change .
int
pmap_change_attr(vm_offset_t va, vm_size_t size, int mode)
{
	vm_offset_t base, offset, tmpva;
	pd_entry_t *pde;
	pt_entry_t *pte;
	int cache_bits_pte, cache_bits_pde;
	boolean_t changed;
	base = trunc_page(va);
	offset = va & PAGE_MASK;
	size = round_page(offset + size);
	if (base < VM_MIN_KERNEL_ADDRESS)
		return (EINVAL);
	cache_bits_pde = pmap_cache_bits(mode, 1);
	cache_bits_pte = pmap_cache_bits(mode, 0);
	changed = FALSE;
	PMAP_LOCK(kernel_pmap);
	for (tmpva = base; tmpva < base + size; ) {
		pde = pmap_pde(kernel_pmap, tmpva);
		if (*pde == 0) {
			PMAP_UNLOCK(kernel_pmap);
			return (EINVAL);
		}
		if (*pde & PG_PS) {
			if ((*pde & PG_PDE_CACHE) == cache_bits_pde) {
				tmpva = trunc_4mpage(tmpva) + NBPDR;
				continue;
			}
			if ((tmpva & PDRMASK) == 0 &&
			    tmpva + PDRMASK < base + size) {
				tmpva += NBPDR;
				continue;
			}
			if (!pmap_demote_pde(kernel_pmap, pde, tmpva)) {
				PMAP_UNLOCK(kernel_pmap);
				return (ENOMEM);
			}
		}
		pte = vtopte(tmpva);
		if (*pte == 0) {
			PMAP_UNLOCK(kernel_pmap);
			return (EINVAL);
		}
		tmpva += PAGE_SIZE;
	}
	PMAP_UNLOCK(kernel_pmap);
	for (tmpva = base; tmpva < base + size; ) {
		pde = pmap_pde(kernel_pmap, tmpva);
		if (*pde & PG_PS) {
			if ((*pde & PG_PDE_CACHE) != cache_bits_pde) {
				pmap_pde_attr(pde, cache_bits_pde);
				changed = TRUE;
			}
			tmpva = trunc_4mpage(tmpva) + NBPDR;
		} else {
			pte = vtopte(tmpva);
			if ((*pte & PG_PTE_CACHE) != cache_bits_pte) {
				pmap_pte_attr(pte, cache_bits_pte);
				changed = TRUE;
			}
			tmpva += PAGE_SIZE;
		}
	}
	if (changed) {
		pmap_invalidate_range(kernel_pmap, base, tmpva);
		pmap_invalidate_cache_range(base, tmpva, FALSE);
	}
	return (0);
}
int pmap_change_attr ( vm_offset_t va , vm_size_t size , int mode ) { vm_offset_t base , offset , tmpva ; pd_entry_t * pde ; pt_entry_t * pte ; int cache_bits_pte , cache_bits_pde ; boolean_t changed ; base = trunc_page ( va ) ; offset = va & PAGE_MASK ; size = round_page ( offset + size ) ; if ( base < VM_MIN_KERNEL_ADDRESS ) return ( EINVAL ) ; cache_bits_pde = pmap_cache_bits ( mode , 1 ) ; cache_bits_pte = pmap_cache_bits ( mode , 0 ) ; changed = FALSE ; PMAP_LOCK ( kernel_pmap ) ; for ( tmpva = base ; tmpva < base + size ; ) { pde = pmap_pde ( kernel_pmap , tmpva ) ; if ( * pde == 0 ) { PMAP_UNLOCK ( kernel_pmap ) ; return ( EINVAL ) ; } if ( * pde & PG_PS ) { if ( ( * pde & PG_PDE_CACHE ) == cache_bits_pde ) { tmpva = trunc_4mpage ( tmpva ) + NBPDR ; continue ; } if ( ( tmpva & PDRMASK ) == 0 && tmpva + PDRMASK < base + size ) { tmpva += NBPDR ; continue ; } if ( ! pmap_demote_pde ( kernel_pmap , pde , tmpva ) ) { PMAP_UNLOCK ( kernel_pmap ) ; return ( ENOMEM ) ; } } pte = vtopte ( tmpva ) ; if ( * pte == 0 ) { PMAP_UNLOCK ( kernel_pmap ) ; return ( EINVAL ) ; } tmpva += PAGE_SIZE ; } PMAP_UNLOCK ( kernel_pmap ) ; for ( tmpva = base ; tmpva < base + size ; ) { pde = pmap_pde ( kernel_pmap , tmpva ) ; if ( * pde & PG_PS ) { if ( ( * pde & PG_PDE_CACHE ) != cache_bits_pde ) { pmap_pde_attr ( pde , cache_bits_pde ) ; changed = TRUE ; } tmpva = trunc_4mpage ( tmpva ) + NBPDR ; } else { pte = vtopte ( tmpva ) ; if ( ( * pte & PG_PTE_CACHE ) != cache_bits_pte ) { pmap_pte_attr ( pte , cache_bits_pte ) ; changed = TRUE ; } tmpva += PAGE_SIZE ; } } if ( changed ) { pmap_invalidate_range ( kernel_pmap , base , tmpva ) ; pmap_invalidate_cache_range ( base , tmpva , FALSE ) ; } return ( 0 ) ; }
Changes the specified virtual address range's memory type to that given by
 the parameter "mode".
Changes the specified virtual address range ' s memory type to that given by the parameter " mode " .
/*
	 * Only supported on kernel virtual addresses above the recursive map.
	 */ /*
	 * Pages that aren't mapped aren't supported.  Also break down
	 * 2/4MB pages into 4KB pages if required.
	 */ /*
			 * If the current 2/4MB page already has
			 * the required memory type, then we need not
			 * demote this page.  Just increment tmpva to
			 * the next 2/4MB page frame.
			 */ /*
			 * If the current offset aligns with a 2/4MB
			 * page frame and there is at least 2/4MB left
			 * within the range, then we need not break
			 * down this page into 4KB pages.
			 */ /*
	 * Ok, all the pages exist, so run through them updating their
	 * cache mode if required.
	 */ /*
	 * Flush CPU caches to make sure any data isn't cached that
	 * shouldn't be, etc.
	 */