Date: Wed, 28 Dec 2016 02:55:26 +0000 (UTC) From: Alexander Kabaev <kan@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r310650 - in head/sys/mips: include mips Message-ID: <201612280255.uBS2tQeR045512@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: kan Date: Wed Dec 28 02:55:26 2016 New Revision: 310650 URL: https://svnweb.freebsd.org/changeset/base/310650 Log: Implement pmap_change_attr and related APIs on MIPS On platforms that have uncached-accelerate cache attribute, map it to VM_MEMATTR_WRITE_COMBINING. Otherwise, leave write comining undefined. Reviewed by: adrian, jhb (glance) Differential Revision: https://reviews.freebsd.org/D8894 Modified: head/sys/mips/include/cpuregs.h head/sys/mips/include/pmap.h head/sys/mips/include/pte.h head/sys/mips/include/vm.h head/sys/mips/mips/pmap.c Modified: head/sys/mips/include/cpuregs.h ============================================================================== --- head/sys/mips/include/cpuregs.h Tue Dec 27 23:56:46 2016 (r310649) +++ head/sys/mips/include/cpuregs.h Wed Dec 28 02:55:26 2016 (r310650) @@ -171,6 +171,10 @@ #define MIPS_CCA_CACHED MIPS_CCA_CCS #endif +#if defined(CPU_XBURST) +#define MIPS_CCA_UA 0x01 +#endif + #ifndef MIPS_CCA_UNCACHED #define MIPS_CCA_UNCACHED MIPS_CCA_UC #endif @@ -188,6 +192,16 @@ #endif #endif +/* + * Use uncached-accelerated mode for write-combining maps, if one is defined, + * otherwise fall back to uncached + */ +#ifndef MIPS_CCA_WC +#ifdef MIPS_CCA_UA +#define MIPS_CCA_WC MIPS_CCA_UA +#endif +#endif + #define MIPS_PHYS_TO_XKPHYS(cca,x) \ ((0x2ULL << 62) | ((unsigned long long)(cca) << 59) | (x)) #define MIPS_PHYS_TO_XKPHYS_CACHED(x) \ Modified: head/sys/mips/include/pmap.h ============================================================================== --- head/sys/mips/include/pmap.h Tue Dec 27 23:56:46 2016 (r310649) +++ head/sys/mips/include/pmap.h Wed Dec 28 02:55:26 2016 (r310650) @@ -74,7 +74,8 @@ struct md_page { }; #define PV_TABLE_REF 0x02 /* referenced */ -#define PV_MEMATTR_UNCACHEABLE 0x04 +#define PV_MEMATTR_MASK 0xf0 /* store vm_memattr_t here */ +#define PV_MEMATTR_SHIFT 0x04 #define ASID_BITS 8 #define ASIDGEN_BITS (32 - ASID_BITS) @@ -163,22 +164,24 @@ extern vm_offset_t virtual_end; extern vm_paddr_t dump_avail[PHYS_AVAIL_ENTRIES + 2]; -#define pmap_page_get_memattr(m) VM_MEMATTR_DEFAULT +#define pmap_page_get_memattr(m) (((m)->md.pv_flags & PV_MEMATTR_MASK) >> PV_MEMATTR_SHIFT) #define pmap_page_is_mapped(m) (!TAILQ_EMPTY(&(m)->md.pv_list)) #define pmap_page_is_write_mapped(m) (((m)->aflags & PGA_WRITEABLE) != 0) void pmap_bootstrap(void); void *pmap_mapdev(vm_paddr_t, vm_size_t); +void *pmap_mapdev_attr(vm_paddr_t, vm_size_t, vm_memattr_t); void pmap_unmapdev(vm_offset_t, vm_size_t); vm_offset_t pmap_steal_memory(vm_size_t size); void pmap_kenter(vm_offset_t va, vm_paddr_t pa); -void pmap_kenter_attr(vm_offset_t va, vm_paddr_t pa, int attr); +void pmap_kenter_attr(vm_offset_t va, vm_paddr_t pa, vm_memattr_t attr); void pmap_kremove(vm_offset_t va); void *pmap_kenter_temporary(vm_paddr_t pa, int i); void pmap_kenter_temporary_free(vm_paddr_t pa); void pmap_flush_pvcache(vm_page_t m); int pmap_emulate_modified(pmap_t pmap, vm_offset_t va); void pmap_page_set_memattr(vm_page_t, vm_memattr_t); +int pmap_change_attr(vm_offset_t, vm_size_t, vm_memattr_t); #endif /* _KERNEL */ Modified: head/sys/mips/include/pte.h ============================================================================== --- head/sys/mips/include/pte.h Tue Dec 27 23:56:46 2016 (r310649) +++ head/sys/mips/include/pte.h Wed Dec 28 02:55:26 2016 (r310650) @@ -132,8 +132,10 @@ typedef pt_entry_t *pd_entry_t; * it is matched. */ #define PTE_C(attr) ((attr & 0x07) << 3) +#define PTE_C_MASK (PTE_C(0x07)) #define PTE_C_UNCACHED (PTE_C(MIPS_CCA_UNCACHED)) #define PTE_C_CACHE (PTE_C(MIPS_CCA_CACHED)) +#define PTE_C_WC (PTE_C(MIPS_CCA_WC)) #define PTE_D 0x04 #define PTE_V 0x02 #define PTE_G 0x01 @@ -158,6 +160,7 @@ typedef pt_entry_t *pd_entry_t; #define pte_clear(pte, bit) (*(pte) &= ~(bit)) #define pte_set(pte, bit) (*(pte) |= (bit)) #define pte_test(pte, bit) ((*(pte) & (bit)) == (bit)) +#define pte_cache_bits(pte) ((*(pte) >> 3) & 0x07) /* Assembly support for PTE access*/ #ifdef LOCORE Modified: head/sys/mips/include/vm.h ============================================================================== --- head/sys/mips/include/vm.h Tue Dec 27 23:56:46 2016 (r310649) +++ head/sys/mips/include/vm.h Wed Dec 28 02:55:26 2016 (r310650) @@ -32,7 +32,11 @@ #include <machine/pte.h> /* Memory attributes. */ -#define VM_MEMATTR_UNCACHEABLE ((vm_memattr_t)PTE_C_UNCACHED) -#define VM_MEMATTR_DEFAULT ((vm_memattr_t)PTE_C_CACHE) +#define VM_MEMATTR_UNCACHEABLE ((vm_memattr_t)MIPS_CCA_UNCACHED) +#define VM_MEMATTR_WRITE_BACK ((vm_memattr_t)MIPS_CCA_CACHED) +#define VM_MEMATTR_DEFAULT VM_MEMATTR_WRITE_BACK +#ifdef MIPS_CCA_WC +#define VM_MEMATTR_WRITE_COMBINING ((vm_memattr_t)MIPS_CCA_WC) +#endif #endif /* !_MACHINE_VM_H_ */ Modified: head/sys/mips/mips/pmap.c ============================================================================== --- head/sys/mips/mips/pmap.c Tue Dec 27 23:56:46 2016 (r310649) +++ head/sys/mips/mips/pmap.c Wed Dec 28 02:55:26 2016 (r310650) @@ -189,10 +189,10 @@ static void pmap_update_page_action(void * The highmem area does not have a KSEG0 mapping, and we need a mechanism to * do temporary per-CPU mappings for pmap_zero_page, pmap_copy_page etc. * - * At bootup, we reserve 2 virtual pages per CPU for mapping highmem pages. To + * At bootup, we reserve 2 virtual pages per CPU for mapping highmem pages. To * access a highmem physical address on a CPU, we map the physical address to - * the reserved virtual address for the CPU in the kernel pagetable. This is - * done with interrupts disabled(although a spinlock and sched_pin would be + * the reserved virtual address for the CPU in the kernel pagetable. This is + * done with interrupts disabled(although a spinlock and sched_pin would be * sufficient). */ struct local_sysmaps { @@ -303,7 +303,7 @@ pmap_lmem_map2(vm_paddr_t phys1, vm_padd return (0); } -static __inline vm_offset_t +static __inline vm_offset_t pmap_lmem_unmap(void) { @@ -312,12 +312,18 @@ pmap_lmem_unmap(void) #endif /* !__mips_n64 */ static __inline int -is_cacheable_page(vm_paddr_t pa, vm_page_t m) +pmap_pte_cache_bits(vm_paddr_t pa, vm_page_t m) { + vm_memattr_t ma; - return ((m->md.pv_flags & PV_MEMATTR_UNCACHEABLE) == 0 && - is_cacheable_mem(pa)); - + ma = pmap_page_get_memattr(m); + if (ma == VM_MEMATTR_WRITE_BACK && !is_cacheable_mem(pa)) + ma = VM_MEMATTR_UNCACHEABLE; + return PTE_C(ma); +} +#define PMAP_PTE_SET_CACHE_BITS(pte, ps, m) { \ + pte &= ~PTE_C_MASK; \ + pte |= pmap_pte_cache_bits(pa, m); \ } /* @@ -359,7 +365,7 @@ pmap_pdpe_to_pde(pd_entry_t *pdpe, vm_of return (pdpe); } -static __inline +static __inline pd_entry_t *pmap_pde(pmap_t pmap, vm_offset_t va) { @@ -423,7 +429,7 @@ pmap_steal_memory(vm_size_t size) * Bootstrap the system enough to run with virtual memory. This * assumes that the phys_avail array has been initialized. */ -static void +static void pmap_create_kernel_pagetable(void) { int i, j; @@ -486,7 +492,7 @@ void pmap_bootstrap(void) { int i; - int need_local_mappings = 0; + int need_local_mappings = 0; /* Sort. */ again: @@ -600,7 +606,7 @@ pmap_page_init(vm_page_t m) { TAILQ_INIT(&m->md.pv_list); - m->md.pv_flags = 0; + m->md.pv_flags = VM_MEMATTR_DEFAULT << PV_MEMATTR_SHIFT; } /* @@ -635,8 +641,8 @@ pmap_call_on_active_cpus(pmap_t pmap, vo pmap->pm_asid[cpu].gen = 0; } cpuid = PCPU_GET(cpuid); - /* - * XXX: barrier/locking for active? + /* + * XXX: barrier/locking for active? * * Take a snapshot of active here, any further changes are ignored. * tlb update/invalidate should be harmless on inactive CPUs @@ -819,7 +825,7 @@ retry: * add a wired page to the kva */ void -pmap_kenter_attr(vm_offset_t va, vm_paddr_t pa, int attr) +pmap_kenter_attr(vm_offset_t va, vm_paddr_t pa, vm_memattr_t ma) { pt_entry_t *pte; pt_entry_t opte, npte; @@ -830,7 +836,7 @@ pmap_kenter_attr(vm_offset_t va, vm_padd pte = pmap_pte(kernel_pmap, va); opte = *pte; - npte = TLBLO_PA_TO_PFN(pa) | attr | PTE_D | PTE_V | PTE_G; + npte = TLBLO_PA_TO_PFN(pa) | PTE_C(ma) | PTE_D | PTE_V | PTE_G; *pte = npte; if (pte_test(&opte, PTE_V) && opte != npte) pmap_update_page(kernel_pmap, va, npte); @@ -843,7 +849,7 @@ pmap_kenter(vm_offset_t va, vm_paddr_t p KASSERT(is_cacheable_mem(pa), ("pmap_kenter: memory at 0x%lx is not cacheable", (u_long)pa)); - pmap_kenter_attr(va, pa, PTE_C_CACHE); + pmap_kenter_attr(va, pa, VM_MEMATTR_DEFAULT); } /* @@ -1144,11 +1150,11 @@ _pmap_allocpte(pmap_t pmap, unsigned pte int segindex = ptepindex >> (SEGSHIFT - PDRSHIFT); int pdeindex = ptepindex & (NPDEPG - 1); vm_page_t pg; - + pdep = &pmap->pm_segtab[segindex]; - if (*pdep == NULL) { + if (*pdep == NULL) { /* recurse for allocating page dir */ - if (_pmap_allocpte(pmap, NUPDE + segindex, + if (_pmap_allocpte(pmap, NUPDE + segindex, flags) == NULL) { /* alloc failed, release current */ --m->wire_count; @@ -1680,7 +1686,7 @@ pmap_try_insert_pv_entry(pmap_t pmap, vm * pmap_remove_pte: do the things to unmap a page in a process */ static int -pmap_remove_pte(struct pmap *pmap, pt_entry_t *ptq, vm_offset_t va, +pmap_remove_pte(struct pmap *pmap, pt_entry_t *ptq, vm_offset_t va, pd_entry_t pde) { pt_entry_t oldpte; @@ -1864,7 +1870,7 @@ pmap_remove_all(vm_page_t m) PMAP_LOCK(pmap); /* - * If it's last mapping writeback all caches from + * If it's last mapping writeback all caches from * the page being destroyed */ if (TAILQ_NEXT(pv, pv_list) == NULL) @@ -2030,10 +2036,7 @@ pmap_enter(pmap_t pmap, vm_offset_t va, newpte |= PTE_W; if (is_kernel_pmap(pmap)) newpte |= PTE_G; - if (is_cacheable_page(pa, m)) - newpte |= PTE_C_CACHE; - else - newpte |= PTE_C_UNCACHED; + PMAP_PTE_SET_CACHE_BITS(newpte, pa, m); mpte = NULL; @@ -2218,7 +2221,7 @@ static vm_page_t pmap_enter_quick_locked(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot, vm_page_t mpte) { - pt_entry_t *pte; + pt_entry_t *pte, npte; vm_paddr_t pa; KASSERT(va < kmi.clean_sva || va >= kmi.clean_eva || @@ -2297,18 +2300,16 @@ pmap_enter_quick_locked(pmap_t pmap, vm_ /* * Now validate mapping with RO protection */ - *pte = PTE_RO | TLBLO_PA_TO_PFN(pa) | PTE_V; + npte = PTE_RO | TLBLO_PA_TO_PFN(pa) | PTE_V; if ((m->oflags & VPO_UNMANAGED) == 0) - *pte |= PTE_MANAGED; + npte |= PTE_MANAGED; - if (is_cacheable_page(pa, m)) - *pte |= PTE_C_CACHE; - else - *pte |= PTE_C_UNCACHED; + PMAP_PTE_SET_CACHE_BITS(npte, pa, m); if (is_kernel_pmap(pmap)) - *pte |= PTE_G; + *pte = npte | PTE_G; else { + *pte = npte; /* * Sync I & D caches. Do this only if the target pmap * belongs to the current process. Otherwise, an @@ -2649,12 +2650,12 @@ pmap_quick_enter_page(vm_page_t m) #else vm_paddr_t pa; struct local_sysmaps *sysm; - pt_entry_t *pte; + pt_entry_t *pte, npte; pa = VM_PAGE_TO_PHYS(m); if (MIPS_DIRECT_MAPPABLE(pa)) { - if (m->md.pv_flags & PV_MEMATTR_UNCACHEABLE) + if (pmap_page_get_memattr(m) != VM_MEMATTR_WRITE_BACK) return (MIPS_PHYS_TO_DIRECT_UNCACHED(pa)); else return (MIPS_PHYS_TO_DIRECT(pa)); @@ -2665,8 +2666,9 @@ pmap_quick_enter_page(vm_page_t m) KASSERT(sysm->valid1 == 0, ("pmap_quick_enter_page: PTE busy")); pte = pmap_pte(kernel_pmap, sysm->base); - *pte = TLBLO_PA_TO_PFN(pa) | PTE_D | PTE_V | PTE_G | - (is_cacheable_page(pa, m) ? PTE_C_CACHE : PTE_C_UNCACHED); + npte = TLBLO_PA_TO_PFN(pa) | PTE_D | PTE_V | PTE_G; + PMAP_PTE_SET_CACHE_BITS(npte, pa, m); + *pte = npte; sysm->valid1 = 1; return (sysm->base); @@ -3146,26 +3148,26 @@ pmap_is_referenced(vm_page_t m) * Use XKPHYS uncached for 64 bit, and KSEG1 where possible for 32 bit. */ void * -pmap_mapdev(vm_paddr_t pa, vm_size_t size) +pmap_mapdev_attr(vm_paddr_t pa, vm_size_t size, vm_memattr_t ma) { vm_offset_t va, tmpva, offset; - /* - * KSEG1 maps only first 512M of phys address space. For + /* + * KSEG1 maps only first 512M of phys address space. For * pa > 0x20000000 we should make proper mapping * using pmap_kenter. */ - if (MIPS_DIRECT_MAPPABLE(pa + size - 1)) + if (MIPS_DIRECT_MAPPABLE(pa + size - 1) && ma == VM_MEMATTR_UNCACHEABLE) return ((void *)MIPS_PHYS_TO_DIRECT_UNCACHED(pa)); else { offset = pa & PAGE_MASK; size = roundup(size + offset, PAGE_SIZE); - + va = kva_alloc(size); if (!va) panic("pmap_mapdev: Couldn't alloc kernel virtual memory"); pa = trunc_page(pa); for (tmpva = va; size > 0;) { - pmap_kenter_attr(tmpva, pa, PTE_C_UNCACHED); + pmap_kenter_attr(tmpva, pa, ma); size -= PAGE_SIZE; tmpva += PAGE_SIZE; pa += PAGE_SIZE; @@ -3175,6 +3177,12 @@ pmap_mapdev(vm_paddr_t pa, vm_size_t siz return ((void *)(va + offset)); } +void * +pmap_mapdev(vm_paddr_t pa, vm_size_t size) +{ + return pmap_mapdev_attr(pa, size, VM_MEMATTR_UNCACHEABLE); +} + void pmap_unmapdev(vm_offset_t va, vm_size_t size) { @@ -3220,7 +3228,7 @@ retry: * This may falsely report the given address as * MINCORE_REFERENCED. Unfortunately, due to the lack of * per-PTE reference information, it is impossible to - * determine if the address is MINCORE_REFERENCED. + * determine if the address is MINCORE_REFERENCED. */ m = PHYS_TO_VM_PAGE(pa); if ((m->aflags & PGA_REFERENCED) != 0) @@ -3500,7 +3508,7 @@ pmap_kextract(vm_offset_t va) mapped = (va >= MIPS_KSEG2_START || va < MIPS_KSEG2_END); #if defined(__mips_n64) mapped = mapped || (va >= MIPS_XKSEG_START || va < MIPS_XKSEG_END); -#endif +#endif /* * Kernel virtual. */ @@ -3524,7 +3532,7 @@ pmap_kextract(vm_offset_t va) } -void +void pmap_flush_pvcache(vm_page_t m) { pv_entry_t pv; @@ -3551,12 +3559,85 @@ pmap_page_set_memattr(vm_page_t m, vm_me if (TAILQ_FIRST(&m->md.pv_list) != NULL) panic("Can't change memattr on page with existing mappings"); - /* - * The only memattr we support is UNCACHEABLE, translate the (semi-)MI - * representation of that into our internal flag in the page MD struct. - */ - if (ma == VM_MEMATTR_UNCACHEABLE) - m->md.pv_flags |= PV_MEMATTR_UNCACHEABLE; - else - m->md.pv_flags &= ~PV_MEMATTR_UNCACHEABLE; + /* Clean memattr portion of pv_flags */ + m->md.pv_flags &= ~PV_MEMATTR_MASK; + m->md.pv_flags |= (ma << PV_MEMATTR_SHIFT) & PV_MEMATTR_MASK; +} + +static __inline void +pmap_pte_attr(pt_entry_t *pte, vm_memattr_t ma) +{ + u_int npte; + + npte = *(u_int *)pte; + npte &= ~PTE_C_MASK; + npte |= PTE_C(ma); + *pte = npte; +} + +int +pmap_change_attr(vm_offset_t sva, vm_size_t size, vm_memattr_t ma) +{ + pd_entry_t *pde, *pdpe; + pt_entry_t *pte; + vm_offset_t ova, eva, va, va_next; + pmap_t pmap; + + ova = sva; + eva = sva + size; + if (eva < sva) + return (EINVAL); + + pmap = kernel_pmap; + PMAP_LOCK(pmap); + + for (; sva < eva; sva = va_next) { + pdpe = pmap_segmap(pmap, sva); +#ifdef __mips_n64 + if (*pdpe == 0) { + va_next = (sva + NBSEG) & ~SEGMASK; + if (va_next < sva) + va_next = eva; + continue; + } +#endif + va_next = (sva + NBPDR) & ~PDRMASK; + if (va_next < sva) + va_next = eva; + + pde = pmap_pdpe_to_pde(pdpe, sva); + if (*pde == NULL) + continue; + + /* + * Limit our scan to either the end of the va represented + * by the current page table page, or to the end of the + * range being removed. + */ + if (va_next > eva) + va_next = eva; + + va = va_next; + for (pte = pmap_pde_to_pte(pde, sva); sva != va_next; pte++, + sva += PAGE_SIZE) { + if (!pte_test(pte, PTE_V) || pte_cache_bits(pte) == ma) { + if (va != va_next) { + pmap_invalidate_range(pmap, va, sva); + va = va_next; + } + continue; + } + if (va == va_next) + va = sva; + + pmap_pte_attr(pte, ma); + } + if (va != va_next) + pmap_invalidate_range(pmap, va, sva); + } + PMAP_UNLOCK(pmap); + + /* Flush caches to be in the safe side */ + mips_dcache_wbinv_range(ova, size); + return 0; }
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201612280255.uBS2tQeR045512>