Index: head/sys/powerpc/aim/mmu_oea64.c =================================================================== --- head/sys/powerpc/aim/mmu_oea64.c (revision 290988) +++ head/sys/powerpc/aim/mmu_oea64.c (revision 290989) @@ -1,2725 +1,2725 @@ /*- * Copyright (c) 2008-2015 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * Manages physical address maps. * * Since the information managed by this module is also stored by the * logical address mapping module, this module may throw away valid virtual * to physical mappings at almost any time. However, invalidations of * mappings must be done as requested. * * In order to cope with hardware architectures which make virtual to * physical map invalidates expensive, this module may delay invalidate * reduced protection operations until such time as they are actually * necessary. This module is given full information as to which processors * are currently using which maps, and to when physical maps must be made * correct. */ #include "opt_compat.h" #include "opt_kstack_pages.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mmu_oea64.h" #include "mmu_if.h" #include "moea64_if.h" void moea64_release_vsid(uint64_t vsid); uintptr_t moea64_get_unique_vsid(void); #define DISABLE_TRANS(msr) msr = mfmsr(); mtmsr(msr & ~PSL_DR) #define ENABLE_TRANS(msr) mtmsr(msr) #define VSID_MAKE(sr, hash) ((sr) | (((hash) & 0xfffff) << 4)) #define VSID_TO_HASH(vsid) (((vsid) >> 4) & 0xfffff) #define VSID_HASH_MASK 0x0000007fffffffffULL /* * Locking semantics: * * There are two locks of interest: the page locks and the pmap locks, which * protect their individual PVO lists and are locked in that order. The contents * of all PVO entries are protected by the locks of their respective pmaps. * The pmap of any PVO is guaranteed not to change so long as the PVO is linked * into any list. * */ #define PV_LOCK_COUNT PA_LOCK_COUNT*3 static struct mtx_padalign pv_lock[PV_LOCK_COUNT]; #define PV_LOCKPTR(pa) ((struct mtx *)(&pv_lock[pa_index(pa) % PV_LOCK_COUNT])) #define PV_LOCK(pa) mtx_lock(PV_LOCKPTR(pa)) #define PV_UNLOCK(pa) mtx_unlock(PV_LOCKPTR(pa)) #define PV_LOCKASSERT(pa) mtx_assert(PV_LOCKPTR(pa), MA_OWNED) #define PV_PAGE_LOCK(m) PV_LOCK(VM_PAGE_TO_PHYS(m)) #define PV_PAGE_UNLOCK(m) PV_UNLOCK(VM_PAGE_TO_PHYS(m)) #define PV_PAGE_LOCKASSERT(m) PV_LOCKASSERT(VM_PAGE_TO_PHYS(m)) struct ofw_map { cell_t om_va; cell_t om_len; uint64_t om_pa; cell_t om_mode; }; extern unsigned char _etext[]; extern unsigned char _end[]; /* * Map of physical memory regions. */ static struct mem_region *regions; static struct mem_region *pregions; static u_int phys_avail_count; static int regions_sz, pregions_sz; extern void bs_remap_earlyboot(void); /* * Lock for the SLB tables. */ struct mtx moea64_slb_mutex; /* * PTEG data. */ u_int moea64_pteg_count; u_int moea64_pteg_mask; /* * PVO data. */ uma_zone_t moea64_pvo_zone; /* zone for pvo entries */ static struct pvo_entry *moea64_bpvo_pool; static int moea64_bpvo_pool_index = 0; static int moea64_bpvo_pool_size = 327680; TUNABLE_INT("machdep.moea64_bpvo_pool_size", &moea64_bpvo_pool_size); SYSCTL_INT(_machdep, OID_AUTO, moea64_allocated_bpvo_entries, CTLFLAG_RD, &moea64_bpvo_pool_index, 0, ""); #define VSID_NBPW (sizeof(u_int32_t) * 8) #ifdef __powerpc64__ #define NVSIDS (NPMAPS * 16) #define VSID_HASHMASK 0xffffffffUL #else #define NVSIDS NPMAPS #define VSID_HASHMASK 0xfffffUL #endif static u_int moea64_vsid_bitmap[NVSIDS / VSID_NBPW]; static boolean_t moea64_initialized = FALSE; /* * Statistics. */ u_int moea64_pte_valid = 0; u_int moea64_pte_overflow = 0; u_int moea64_pvo_entries = 0; u_int moea64_pvo_enter_calls = 0; u_int moea64_pvo_remove_calls = 0; SYSCTL_INT(_machdep, OID_AUTO, moea64_pte_valid, CTLFLAG_RD, &moea64_pte_valid, 0, ""); SYSCTL_INT(_machdep, OID_AUTO, moea64_pte_overflow, CTLFLAG_RD, &moea64_pte_overflow, 0, ""); SYSCTL_INT(_machdep, OID_AUTO, moea64_pvo_entries, CTLFLAG_RD, &moea64_pvo_entries, 0, ""); SYSCTL_INT(_machdep, OID_AUTO, moea64_pvo_enter_calls, CTLFLAG_RD, &moea64_pvo_enter_calls, 0, ""); SYSCTL_INT(_machdep, OID_AUTO, moea64_pvo_remove_calls, CTLFLAG_RD, &moea64_pvo_remove_calls, 0, ""); vm_offset_t moea64_scratchpage_va[2]; struct pvo_entry *moea64_scratchpage_pvo[2]; struct mtx moea64_scratchpage_mtx; uint64_t moea64_large_page_mask = 0; uint64_t moea64_large_page_size = 0; int moea64_large_page_shift = 0; /* * PVO calls. */ static int moea64_pvo_enter(mmu_t mmu, struct pvo_entry *pvo, struct pvo_head *pvo_head); static void moea64_pvo_remove_from_pmap(mmu_t mmu, struct pvo_entry *pvo); static void moea64_pvo_remove_from_page(mmu_t mmu, struct pvo_entry *pvo); static struct pvo_entry *moea64_pvo_find_va(pmap_t, vm_offset_t); /* * Utility routines. */ static boolean_t moea64_query_bit(mmu_t, vm_page_t, uint64_t); static u_int moea64_clear_bit(mmu_t, vm_page_t, uint64_t); static void moea64_kremove(mmu_t, vm_offset_t); static void moea64_syncicache(mmu_t, pmap_t pmap, vm_offset_t va, vm_paddr_t pa, vm_size_t sz); static void moea64_pmap_init_qpages(void); /* * Kernel MMU interface */ void moea64_clear_modify(mmu_t, vm_page_t); void moea64_copy_page(mmu_t, vm_page_t, vm_page_t); void moea64_copy_pages(mmu_t mmu, vm_page_t *ma, vm_offset_t a_offset, vm_page_t *mb, vm_offset_t b_offset, int xfersize); int moea64_enter(mmu_t, pmap_t, vm_offset_t, vm_page_t, vm_prot_t, u_int flags, int8_t psind); void moea64_enter_object(mmu_t, pmap_t, vm_offset_t, vm_offset_t, vm_page_t, vm_prot_t); void moea64_enter_quick(mmu_t, pmap_t, vm_offset_t, vm_page_t, vm_prot_t); vm_paddr_t moea64_extract(mmu_t, pmap_t, vm_offset_t); vm_page_t moea64_extract_and_hold(mmu_t, pmap_t, vm_offset_t, vm_prot_t); void moea64_init(mmu_t); boolean_t moea64_is_modified(mmu_t, vm_page_t); boolean_t moea64_is_prefaultable(mmu_t, pmap_t, vm_offset_t); boolean_t moea64_is_referenced(mmu_t, vm_page_t); int moea64_ts_referenced(mmu_t, vm_page_t); vm_offset_t moea64_map(mmu_t, vm_offset_t *, vm_paddr_t, vm_paddr_t, int); boolean_t moea64_page_exists_quick(mmu_t, pmap_t, vm_page_t); int moea64_page_wired_mappings(mmu_t, vm_page_t); void moea64_pinit(mmu_t, pmap_t); void moea64_pinit0(mmu_t, pmap_t); void moea64_protect(mmu_t, pmap_t, vm_offset_t, vm_offset_t, vm_prot_t); void moea64_qenter(mmu_t, vm_offset_t, vm_page_t *, int); void moea64_qremove(mmu_t, vm_offset_t, int); void moea64_release(mmu_t, pmap_t); void moea64_remove(mmu_t, pmap_t, vm_offset_t, vm_offset_t); void moea64_remove_pages(mmu_t, pmap_t); void moea64_remove_all(mmu_t, vm_page_t); void moea64_remove_write(mmu_t, vm_page_t); void moea64_unwire(mmu_t, pmap_t, vm_offset_t, vm_offset_t); void moea64_zero_page(mmu_t, vm_page_t); void moea64_zero_page_area(mmu_t, vm_page_t, int, int); void moea64_zero_page_idle(mmu_t, vm_page_t); void moea64_activate(mmu_t, struct thread *); void moea64_deactivate(mmu_t, struct thread *); void *moea64_mapdev(mmu_t, vm_paddr_t, vm_size_t); void *moea64_mapdev_attr(mmu_t, vm_paddr_t, vm_size_t, vm_memattr_t); void moea64_unmapdev(mmu_t, vm_offset_t, vm_size_t); vm_paddr_t moea64_kextract(mmu_t, vm_offset_t); void moea64_page_set_memattr(mmu_t, vm_page_t m, vm_memattr_t ma); void moea64_kenter_attr(mmu_t, vm_offset_t, vm_paddr_t, vm_memattr_t ma); void moea64_kenter(mmu_t, vm_offset_t, vm_paddr_t); boolean_t moea64_dev_direct_mapped(mmu_t, vm_paddr_t, vm_size_t); static void moea64_sync_icache(mmu_t, pmap_t, vm_offset_t, vm_size_t); void moea64_dumpsys_map(mmu_t mmu, vm_paddr_t pa, size_t sz, void **va); void moea64_scan_init(mmu_t mmu); vm_offset_t moea64_quick_enter_page(mmu_t mmu, vm_page_t m); void moea64_quick_remove_page(mmu_t mmu, vm_offset_t addr); static mmu_method_t moea64_methods[] = { MMUMETHOD(mmu_clear_modify, moea64_clear_modify), MMUMETHOD(mmu_copy_page, moea64_copy_page), MMUMETHOD(mmu_copy_pages, moea64_copy_pages), MMUMETHOD(mmu_enter, moea64_enter), MMUMETHOD(mmu_enter_object, moea64_enter_object), MMUMETHOD(mmu_enter_quick, moea64_enter_quick), MMUMETHOD(mmu_extract, moea64_extract), MMUMETHOD(mmu_extract_and_hold, moea64_extract_and_hold), MMUMETHOD(mmu_init, moea64_init), MMUMETHOD(mmu_is_modified, moea64_is_modified), MMUMETHOD(mmu_is_prefaultable, moea64_is_prefaultable), MMUMETHOD(mmu_is_referenced, moea64_is_referenced), MMUMETHOD(mmu_ts_referenced, moea64_ts_referenced), MMUMETHOD(mmu_map, moea64_map), MMUMETHOD(mmu_page_exists_quick,moea64_page_exists_quick), MMUMETHOD(mmu_page_wired_mappings,moea64_page_wired_mappings), MMUMETHOD(mmu_pinit, moea64_pinit), MMUMETHOD(mmu_pinit0, moea64_pinit0), MMUMETHOD(mmu_protect, moea64_protect), MMUMETHOD(mmu_qenter, moea64_qenter), MMUMETHOD(mmu_qremove, moea64_qremove), MMUMETHOD(mmu_release, moea64_release), MMUMETHOD(mmu_remove, moea64_remove), MMUMETHOD(mmu_remove_pages, moea64_remove_pages), MMUMETHOD(mmu_remove_all, moea64_remove_all), MMUMETHOD(mmu_remove_write, moea64_remove_write), MMUMETHOD(mmu_sync_icache, moea64_sync_icache), MMUMETHOD(mmu_unwire, moea64_unwire), MMUMETHOD(mmu_zero_page, moea64_zero_page), MMUMETHOD(mmu_zero_page_area, moea64_zero_page_area), MMUMETHOD(mmu_zero_page_idle, moea64_zero_page_idle), MMUMETHOD(mmu_activate, moea64_activate), MMUMETHOD(mmu_deactivate, moea64_deactivate), MMUMETHOD(mmu_page_set_memattr, moea64_page_set_memattr), MMUMETHOD(mmu_quick_enter_page, moea64_quick_enter_page), MMUMETHOD(mmu_quick_remove_page, moea64_quick_remove_page), /* Internal interfaces */ MMUMETHOD(mmu_mapdev, moea64_mapdev), MMUMETHOD(mmu_mapdev_attr, moea64_mapdev_attr), MMUMETHOD(mmu_unmapdev, moea64_unmapdev), MMUMETHOD(mmu_kextract, moea64_kextract), MMUMETHOD(mmu_kenter, moea64_kenter), MMUMETHOD(mmu_kenter_attr, moea64_kenter_attr), MMUMETHOD(mmu_dev_direct_mapped,moea64_dev_direct_mapped), MMUMETHOD(mmu_scan_init, moea64_scan_init), MMUMETHOD(mmu_dumpsys_map, moea64_dumpsys_map), { 0, 0 } }; MMU_DEF(oea64_mmu, "mmu_oea64_base", moea64_methods, 0); static struct pvo_head * vm_page_to_pvoh(vm_page_t m) { mtx_assert(PV_LOCKPTR(VM_PAGE_TO_PHYS(m)), MA_OWNED); return (&m->md.mdpg_pvoh); } static struct pvo_entry * alloc_pvo_entry(int bootstrap) { struct pvo_entry *pvo; if (!moea64_initialized || bootstrap) { if (moea64_bpvo_pool_index >= moea64_bpvo_pool_size) { panic("moea64_enter: bpvo pool exhausted, %d, %d, %zd", moea64_bpvo_pool_index, moea64_bpvo_pool_size, moea64_bpvo_pool_size * sizeof(struct pvo_entry)); } pvo = &moea64_bpvo_pool[ atomic_fetchadd_int(&moea64_bpvo_pool_index, 1)]; bzero(pvo, sizeof(*pvo)); pvo->pvo_vaddr = PVO_BOOTSTRAP; } else { pvo = uma_zalloc(moea64_pvo_zone, M_NOWAIT); bzero(pvo, sizeof(*pvo)); } return (pvo); } static void init_pvo_entry(struct pvo_entry *pvo, pmap_t pmap, vm_offset_t va) { uint64_t vsid; uint64_t hash; int shift; PMAP_LOCK_ASSERT(pmap, MA_OWNED); pvo->pvo_pmap = pmap; va &= ~ADDR_POFF; pvo->pvo_vaddr |= va; vsid = va_to_vsid(pmap, va); pvo->pvo_vpn = (uint64_t)((va & ADDR_PIDX) >> ADDR_PIDX_SHFT) | (vsid << 16); shift = (pvo->pvo_vaddr & PVO_LARGE) ? moea64_large_page_shift : ADDR_PIDX_SHFT; hash = (vsid & VSID_HASH_MASK) ^ (((uint64_t)va & ADDR_PIDX) >> shift); pvo->pvo_pte.slot = (hash & moea64_pteg_mask) << 3; } static void free_pvo_entry(struct pvo_entry *pvo) { if (!(pvo->pvo_vaddr & PVO_BOOTSTRAP)) uma_zfree(moea64_pvo_zone, pvo); } void moea64_pte_from_pvo(const struct pvo_entry *pvo, struct lpte *lpte) { lpte->pte_hi = (pvo->pvo_vpn >> (ADDR_API_SHFT64 - ADDR_PIDX_SHFT)) & LPTE_AVPN_MASK; lpte->pte_hi |= LPTE_VALID; if (pvo->pvo_vaddr & PVO_LARGE) lpte->pte_hi |= LPTE_BIG; if (pvo->pvo_vaddr & PVO_WIRED) lpte->pte_hi |= LPTE_WIRED; if (pvo->pvo_vaddr & PVO_HID) lpte->pte_hi |= LPTE_HID; lpte->pte_lo = pvo->pvo_pte.pa; /* Includes WIMG bits */ if (pvo->pvo_pte.prot & VM_PROT_WRITE) lpte->pte_lo |= LPTE_BW; else lpte->pte_lo |= LPTE_BR; if (!(pvo->pvo_pte.prot & VM_PROT_EXECUTE)) lpte->pte_lo |= LPTE_NOEXEC; } static __inline uint64_t moea64_calc_wimg(vm_paddr_t pa, vm_memattr_t ma) { uint64_t pte_lo; int i; if (ma != VM_MEMATTR_DEFAULT) { switch (ma) { case VM_MEMATTR_UNCACHEABLE: return (LPTE_I | LPTE_G); case VM_MEMATTR_WRITE_COMBINING: case VM_MEMATTR_WRITE_BACK: case VM_MEMATTR_PREFETCHABLE: return (LPTE_I); case VM_MEMATTR_WRITE_THROUGH: return (LPTE_W | LPTE_M); } } /* * Assume the page is cache inhibited and access is guarded unless * it's in our available memory array. */ pte_lo = LPTE_I | LPTE_G; for (i = 0; i < pregions_sz; i++) { if ((pa >= pregions[i].mr_start) && (pa < (pregions[i].mr_start + pregions[i].mr_size))) { pte_lo &= ~(LPTE_I | LPTE_G); pte_lo |= LPTE_M; break; } } return pte_lo; } /* * Quick sort callout for comparing memory regions. */ static int om_cmp(const void *a, const void *b); static int om_cmp(const void *a, const void *b) { const struct ofw_map *mapa; const struct ofw_map *mapb; mapa = a; mapb = b; if (mapa->om_pa < mapb->om_pa) return (-1); else if (mapa->om_pa > mapb->om_pa) return (1); else return (0); } static void moea64_add_ofw_mappings(mmu_t mmup, phandle_t mmu, size_t sz) { struct ofw_map translations[sz/(4*sizeof(cell_t))]; /*>= 4 cells per */ pcell_t acells, trans_cells[sz/sizeof(cell_t)]; struct pvo_entry *pvo; register_t msr; vm_offset_t off; vm_paddr_t pa_base; int i, j; bzero(translations, sz); - OF_getprop(OF_finddevice("/"), "#address-cells", &acells, + OF_getencprop(OF_finddevice("/"), "#address-cells", &acells, sizeof(acells)); - if (OF_getprop(mmu, "translations", trans_cells, sz) == -1) + if (OF_getencprop(mmu, "translations", trans_cells, sz) == -1) panic("moea64_bootstrap: can't get ofw translations"); CTR0(KTR_PMAP, "moea64_add_ofw_mappings: translations"); sz /= sizeof(cell_t); for (i = 0, j = 0; i < sz; j++) { translations[j].om_va = trans_cells[i++]; translations[j].om_len = trans_cells[i++]; translations[j].om_pa = trans_cells[i++]; if (acells == 2) { translations[j].om_pa <<= 32; translations[j].om_pa |= trans_cells[i++]; } translations[j].om_mode = trans_cells[i++]; } KASSERT(i == sz, ("Translations map has incorrect cell count (%d/%zd)", i, sz)); sz = j; qsort(translations, sz, sizeof (*translations), om_cmp); for (i = 0; i < sz; i++) { pa_base = translations[i].om_pa; #ifndef __powerpc64__ if ((translations[i].om_pa >> 32) != 0) panic("OFW translations above 32-bit boundary!"); #endif if (pa_base % PAGE_SIZE) panic("OFW translation not page-aligned (phys)!"); if (translations[i].om_va % PAGE_SIZE) panic("OFW translation not page-aligned (virt)!"); CTR3(KTR_PMAP, "translation: pa=%#zx va=%#x len=%#x", pa_base, translations[i].om_va, translations[i].om_len); /* Now enter the pages for this mapping */ DISABLE_TRANS(msr); for (off = 0; off < translations[i].om_len; off += PAGE_SIZE) { /* If this address is direct-mapped, skip remapping */ if (hw_direct_map && translations[i].om_va == pa_base && moea64_calc_wimg(pa_base + off, VM_MEMATTR_DEFAULT) == LPTE_M) continue; PMAP_LOCK(kernel_pmap); pvo = moea64_pvo_find_va(kernel_pmap, translations[i].om_va + off); PMAP_UNLOCK(kernel_pmap); if (pvo != NULL) continue; moea64_kenter(mmup, translations[i].om_va + off, pa_base + off); } ENABLE_TRANS(msr); } } #ifdef __powerpc64__ static void moea64_probe_large_page(void) { uint16_t pvr = mfpvr() >> 16; switch (pvr) { case IBM970: case IBM970FX: case IBM970MP: powerpc_sync(); isync(); mtspr(SPR_HID4, mfspr(SPR_HID4) & ~HID4_970_DISABLE_LG_PG); powerpc_sync(); isync(); /* FALLTHROUGH */ default: moea64_large_page_size = 0x1000000; /* 16 MB */ moea64_large_page_shift = 24; } moea64_large_page_mask = moea64_large_page_size - 1; } static void moea64_bootstrap_slb_prefault(vm_offset_t va, int large) { struct slb *cache; struct slb entry; uint64_t esid, slbe; uint64_t i; cache = PCPU_GET(slb); esid = va >> ADDR_SR_SHFT; slbe = (esid << SLBE_ESID_SHIFT) | SLBE_VALID; for (i = 0; i < 64; i++) { if (cache[i].slbe == (slbe | i)) return; } entry.slbe = slbe; entry.slbv = KERNEL_VSID(esid) << SLBV_VSID_SHIFT; if (large) entry.slbv |= SLBV_L; slb_insert_kernel(entry.slbe, entry.slbv); } #endif static void moea64_setup_direct_map(mmu_t mmup, vm_offset_t kernelstart, vm_offset_t kernelend) { struct pvo_entry *pvo; register_t msr; vm_paddr_t pa; vm_offset_t size, off; uint64_t pte_lo; int i; if (moea64_large_page_size == 0) hw_direct_map = 0; DISABLE_TRANS(msr); if (hw_direct_map) { PMAP_LOCK(kernel_pmap); for (i = 0; i < pregions_sz; i++) { for (pa = pregions[i].mr_start; pa < pregions[i].mr_start + pregions[i].mr_size; pa += moea64_large_page_size) { pte_lo = LPTE_M; pvo = alloc_pvo_entry(1 /* bootstrap */); pvo->pvo_vaddr |= PVO_WIRED | PVO_LARGE; init_pvo_entry(pvo, kernel_pmap, pa); /* * Set memory access as guarded if prefetch within * the page could exit the available physmem area. */ if (pa & moea64_large_page_mask) { pa &= moea64_large_page_mask; pte_lo |= LPTE_G; } if (pa + moea64_large_page_size > pregions[i].mr_start + pregions[i].mr_size) pte_lo |= LPTE_G; pvo->pvo_pte.prot = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; pvo->pvo_pte.pa = pa | pte_lo; moea64_pvo_enter(mmup, pvo, NULL); } } PMAP_UNLOCK(kernel_pmap); } else { size = moea64_bpvo_pool_size*sizeof(struct pvo_entry); off = (vm_offset_t)(moea64_bpvo_pool); for (pa = off; pa < off + size; pa += PAGE_SIZE) moea64_kenter(mmup, pa, pa); /* * Map certain important things, like ourselves. * * NOTE: We do not map the exception vector space. That code is * used only in real mode, and leaving it unmapped allows us to * catch NULL pointer deferences, instead of making NULL a valid * address. */ for (pa = kernelstart & ~PAGE_MASK; pa < kernelend; pa += PAGE_SIZE) moea64_kenter(mmup, pa, pa); } ENABLE_TRANS(msr); /* * Allow user to override unmapped_buf_allowed for testing. * XXXKIB Only direct map implementation was tested. */ if (!TUNABLE_INT_FETCH("vfs.unmapped_buf_allowed", &unmapped_buf_allowed)) unmapped_buf_allowed = hw_direct_map; } void moea64_early_bootstrap(mmu_t mmup, vm_offset_t kernelstart, vm_offset_t kernelend) { int i, j; vm_size_t physsz, hwphyssz; #ifndef __powerpc64__ /* We don't have a direct map since there is no BAT */ hw_direct_map = 0; /* Make sure battable is zero, since we have no BAT */ for (i = 0; i < 16; i++) { battable[i].batu = 0; battable[i].batl = 0; } #else moea64_probe_large_page(); /* Use a direct map if we have large page support */ if (moea64_large_page_size > 0) hw_direct_map = 1; else hw_direct_map = 0; #endif /* Get physical memory regions from firmware */ mem_regions(&pregions, &pregions_sz, ®ions, ®ions_sz); CTR0(KTR_PMAP, "moea64_bootstrap: physical memory"); if (sizeof(phys_avail)/sizeof(phys_avail[0]) < regions_sz) panic("moea64_bootstrap: phys_avail too small"); phys_avail_count = 0; physsz = 0; hwphyssz = 0; TUNABLE_ULONG_FETCH("hw.physmem", (u_long *) &hwphyssz); for (i = 0, j = 0; i < regions_sz; i++, j += 2) { CTR3(KTR_PMAP, "region: %#zx - %#zx (%#zx)", regions[i].mr_start, regions[i].mr_start + regions[i].mr_size, regions[i].mr_size); if (hwphyssz != 0 && (physsz + regions[i].mr_size) >= hwphyssz) { if (physsz < hwphyssz) { phys_avail[j] = regions[i].mr_start; phys_avail[j + 1] = regions[i].mr_start + hwphyssz - physsz; physsz = hwphyssz; phys_avail_count++; } break; } phys_avail[j] = regions[i].mr_start; phys_avail[j + 1] = regions[i].mr_start + regions[i].mr_size; phys_avail_count++; physsz += regions[i].mr_size; } /* Check for overlap with the kernel and exception vectors */ for (j = 0; j < 2*phys_avail_count; j+=2) { if (phys_avail[j] < EXC_LAST) phys_avail[j] += EXC_LAST; if (kernelstart >= phys_avail[j] && kernelstart < phys_avail[j+1]) { if (kernelend < phys_avail[j+1]) { phys_avail[2*phys_avail_count] = (kernelend & ~PAGE_MASK) + PAGE_SIZE; phys_avail[2*phys_avail_count + 1] = phys_avail[j+1]; phys_avail_count++; } phys_avail[j+1] = kernelstart & ~PAGE_MASK; } if (kernelend >= phys_avail[j] && kernelend < phys_avail[j+1]) { if (kernelstart > phys_avail[j]) { phys_avail[2*phys_avail_count] = phys_avail[j]; phys_avail[2*phys_avail_count + 1] = kernelstart & ~PAGE_MASK; phys_avail_count++; } phys_avail[j] = (kernelend & ~PAGE_MASK) + PAGE_SIZE; } } physmem = btoc(physsz); #ifdef PTEGCOUNT moea64_pteg_count = PTEGCOUNT; #else moea64_pteg_count = 0x1000; while (moea64_pteg_count < physmem) moea64_pteg_count <<= 1; moea64_pteg_count >>= 1; #endif /* PTEGCOUNT */ } void moea64_mid_bootstrap(mmu_t mmup, vm_offset_t kernelstart, vm_offset_t kernelend) { int i; /* * Set PTEG mask */ moea64_pteg_mask = moea64_pteg_count - 1; /* * Initialize SLB table lock and page locks */ mtx_init(&moea64_slb_mutex, "SLB table", NULL, MTX_DEF); for (i = 0; i < PV_LOCK_COUNT; i++) mtx_init(&pv_lock[i], "page pv", NULL, MTX_DEF); /* * Initialise the bootstrap pvo pool. */ moea64_bpvo_pool = (struct pvo_entry *)moea64_bootstrap_alloc( moea64_bpvo_pool_size*sizeof(struct pvo_entry), 0); moea64_bpvo_pool_index = 0; /* * Make sure kernel vsid is allocated as well as VSID 0. */ #ifndef __powerpc64__ moea64_vsid_bitmap[(KERNEL_VSIDBITS & (NVSIDS - 1)) / VSID_NBPW] |= 1 << (KERNEL_VSIDBITS % VSID_NBPW); moea64_vsid_bitmap[0] |= 1; #endif /* * Initialize the kernel pmap (which is statically allocated). */ #ifdef __powerpc64__ for (i = 0; i < 64; i++) { pcpup->pc_slb[i].slbv = 0; pcpup->pc_slb[i].slbe = 0; } #else for (i = 0; i < 16; i++) kernel_pmap->pm_sr[i] = EMPTY_SEGMENT + i; #endif kernel_pmap->pmap_phys = kernel_pmap; CPU_FILL(&kernel_pmap->pm_active); RB_INIT(&kernel_pmap->pmap_pvo); PMAP_LOCK_INIT(kernel_pmap); /* * Now map in all the other buffers we allocated earlier */ moea64_setup_direct_map(mmup, kernelstart, kernelend); } void moea64_late_bootstrap(mmu_t mmup, vm_offset_t kernelstart, vm_offset_t kernelend) { ihandle_t mmui; phandle_t chosen; phandle_t mmu; ssize_t sz; int i; vm_offset_t pa, va; void *dpcpu; /* * Set up the Open Firmware pmap and add its mappings if not in real * mode. */ chosen = OF_finddevice("/chosen"); - if (chosen != -1 && OF_getprop(chosen, "mmu", &mmui, 4) != -1) { + if (chosen != -1 && OF_getencprop(chosen, "mmu", &mmui, 4) != -1) { mmu = OF_instance_to_package(mmui); if (mmu == -1 || (sz = OF_getproplen(mmu, "translations")) == -1) sz = 0; if (sz > 6144 /* tmpstksz - 2 KB headroom */) panic("moea64_bootstrap: too many ofw translations"); if (sz > 0) moea64_add_ofw_mappings(mmup, mmu, sz); } /* * Calculate the last available physical address. */ for (i = 0; phys_avail[i + 2] != 0; i += 2) ; Maxmem = powerpc_btop(phys_avail[i + 1]); /* * Initialize MMU and remap early physical mappings */ MMU_CPU_BOOTSTRAP(mmup,0); mtmsr(mfmsr() | PSL_DR | PSL_IR); pmap_bootstrapped++; bs_remap_earlyboot(); /* * Set the start and end of kva. */ virtual_avail = VM_MIN_KERNEL_ADDRESS; virtual_end = VM_MAX_SAFE_KERNEL_ADDRESS; /* * Map the entire KVA range into the SLB. We must not fault there. */ #ifdef __powerpc64__ for (va = virtual_avail; va < virtual_end; va += SEGMENT_LENGTH) moea64_bootstrap_slb_prefault(va, 0); #endif /* * Figure out how far we can extend virtual_end into segment 16 * without running into existing mappings. Segment 16 is guaranteed * to contain neither RAM nor devices (at least on Apple hardware), * but will generally contain some OFW mappings we should not * step on. */ #ifndef __powerpc64__ /* KVA is in high memory on PPC64 */ PMAP_LOCK(kernel_pmap); while (virtual_end < VM_MAX_KERNEL_ADDRESS && moea64_pvo_find_va(kernel_pmap, virtual_end+1) == NULL) virtual_end += PAGE_SIZE; PMAP_UNLOCK(kernel_pmap); #endif /* * Allocate a kernel stack with a guard page for thread0 and map it * into the kernel page map. */ pa = moea64_bootstrap_alloc(kstack_pages * PAGE_SIZE, PAGE_SIZE); va = virtual_avail + KSTACK_GUARD_PAGES * PAGE_SIZE; virtual_avail = va + kstack_pages * PAGE_SIZE; CTR2(KTR_PMAP, "moea64_bootstrap: kstack0 at %#x (%#x)", pa, va); thread0.td_kstack = va; thread0.td_kstack_pages = kstack_pages; for (i = 0; i < kstack_pages; i++) { moea64_kenter(mmup, va, pa); pa += PAGE_SIZE; va += PAGE_SIZE; } /* * Allocate virtual address space for the message buffer. */ pa = msgbuf_phys = moea64_bootstrap_alloc(msgbufsize, PAGE_SIZE); msgbufp = (struct msgbuf *)virtual_avail; va = virtual_avail; virtual_avail += round_page(msgbufsize); while (va < virtual_avail) { moea64_kenter(mmup, va, pa); pa += PAGE_SIZE; va += PAGE_SIZE; } /* * Allocate virtual address space for the dynamic percpu area. */ pa = moea64_bootstrap_alloc(DPCPU_SIZE, PAGE_SIZE); dpcpu = (void *)virtual_avail; va = virtual_avail; virtual_avail += DPCPU_SIZE; while (va < virtual_avail) { moea64_kenter(mmup, va, pa); pa += PAGE_SIZE; va += PAGE_SIZE; } dpcpu_init(dpcpu, 0); /* * Allocate some things for page zeroing. We put this directly * in the page table and use MOEA64_PTE_REPLACE to avoid any * of the PVO book-keeping or other parts of the VM system * from even knowing that this hack exists. */ if (!hw_direct_map) { mtx_init(&moea64_scratchpage_mtx, "pvo zero page", NULL, MTX_DEF); for (i = 0; i < 2; i++) { moea64_scratchpage_va[i] = (virtual_end+1) - PAGE_SIZE; virtual_end -= PAGE_SIZE; moea64_kenter(mmup, moea64_scratchpage_va[i], 0); PMAP_LOCK(kernel_pmap); moea64_scratchpage_pvo[i] = moea64_pvo_find_va( kernel_pmap, (vm_offset_t)moea64_scratchpage_va[i]); PMAP_UNLOCK(kernel_pmap); } } } static void moea64_pmap_init_qpages(void) { struct pcpu *pc; int i; if (hw_direct_map) return; CPU_FOREACH(i) { pc = pcpu_find(i); pc->pc_qmap_addr = kva_alloc(PAGE_SIZE); if (pc->pc_qmap_addr == 0) panic("pmap_init_qpages: unable to allocate KVA"); PMAP_LOCK(kernel_pmap); pc->pc_qmap_pvo = moea64_pvo_find_va(kernel_pmap, pc->pc_qmap_addr); PMAP_UNLOCK(kernel_pmap); mtx_init(&pc->pc_qmap_lock, "qmap lock", NULL, MTX_DEF); } } SYSINIT(qpages_init, SI_SUB_CPU, SI_ORDER_ANY, moea64_pmap_init_qpages, NULL); /* * Activate a user pmap. This mostly involves setting some non-CPU * state. */ void moea64_activate(mmu_t mmu, struct thread *td) { pmap_t pm; pm = &td->td_proc->p_vmspace->vm_pmap; CPU_SET(PCPU_GET(cpuid), &pm->pm_active); #ifdef __powerpc64__ PCPU_SET(userslb, pm->pm_slb); __asm __volatile("slbmte %0, %1; isync" :: "r"(td->td_pcb->pcb_cpu.aim.usr_vsid), "r"(USER_SLB_SLBE)); #else PCPU_SET(curpmap, pm->pmap_phys); mtsrin(USER_SR << ADDR_SR_SHFT, td->td_pcb->pcb_cpu.aim.usr_vsid); #endif } void moea64_deactivate(mmu_t mmu, struct thread *td) { pmap_t pm; __asm __volatile("isync; slbie %0" :: "r"(USER_ADDR)); pm = &td->td_proc->p_vmspace->vm_pmap; CPU_CLR(PCPU_GET(cpuid), &pm->pm_active); #ifdef __powerpc64__ PCPU_SET(userslb, NULL); #else PCPU_SET(curpmap, NULL); #endif } void moea64_unwire(mmu_t mmu, pmap_t pm, vm_offset_t sva, vm_offset_t eva) { struct pvo_entry key, *pvo; vm_page_t m; int64_t refchg; key.pvo_vaddr = sva; PMAP_LOCK(pm); for (pvo = RB_NFIND(pvo_tree, &pm->pmap_pvo, &key); pvo != NULL && PVO_VADDR(pvo) < eva; pvo = RB_NEXT(pvo_tree, &pm->pmap_pvo, pvo)) { if ((pvo->pvo_vaddr & PVO_WIRED) == 0) panic("moea64_unwire: pvo %p is missing PVO_WIRED", pvo); pvo->pvo_vaddr &= ~PVO_WIRED; refchg = MOEA64_PTE_REPLACE(mmu, pvo, 0 /* No invalidation */); if ((pvo->pvo_vaddr & PVO_MANAGED) && (pvo->pvo_pte.prot & VM_PROT_WRITE)) { if (refchg < 0) refchg = LPTE_CHG; m = PHYS_TO_VM_PAGE(pvo->pvo_pte.pa & LPTE_RPGN); refchg |= atomic_readandclear_32(&m->md.mdpg_attrs); if (refchg & LPTE_CHG) vm_page_dirty(m); if (refchg & LPTE_REF) vm_page_aflag_set(m, PGA_REFERENCED); } pm->pm_stats.wired_count--; } PMAP_UNLOCK(pm); } /* * This goes through and sets the physical address of our * special scratch PTE to the PA we want to zero or copy. Because * of locking issues (this can get called in pvo_enter() by * the UMA allocator), we can't use most other utility functions here */ static __inline void moea64_set_scratchpage_pa(mmu_t mmup, int which, vm_paddr_t pa) { KASSERT(!hw_direct_map, ("Using OEA64 scratchpage with a direct map!")); mtx_assert(&moea64_scratchpage_mtx, MA_OWNED); moea64_scratchpage_pvo[which]->pvo_pte.pa = moea64_calc_wimg(pa, VM_MEMATTR_DEFAULT) | (uint64_t)pa; MOEA64_PTE_REPLACE(mmup, moea64_scratchpage_pvo[which], MOEA64_PTE_INVALIDATE); isync(); } void moea64_copy_page(mmu_t mmu, vm_page_t msrc, vm_page_t mdst) { vm_offset_t dst; vm_offset_t src; dst = VM_PAGE_TO_PHYS(mdst); src = VM_PAGE_TO_PHYS(msrc); if (hw_direct_map) { bcopy((void *)src, (void *)dst, PAGE_SIZE); } else { mtx_lock(&moea64_scratchpage_mtx); moea64_set_scratchpage_pa(mmu, 0, src); moea64_set_scratchpage_pa(mmu, 1, dst); bcopy((void *)moea64_scratchpage_va[0], (void *)moea64_scratchpage_va[1], PAGE_SIZE); mtx_unlock(&moea64_scratchpage_mtx); } } static inline void moea64_copy_pages_dmap(mmu_t mmu, vm_page_t *ma, vm_offset_t a_offset, vm_page_t *mb, vm_offset_t b_offset, int xfersize) { void *a_cp, *b_cp; vm_offset_t a_pg_offset, b_pg_offset; int cnt; while (xfersize > 0) { a_pg_offset = a_offset & PAGE_MASK; cnt = min(xfersize, PAGE_SIZE - a_pg_offset); a_cp = (char *)VM_PAGE_TO_PHYS(ma[a_offset >> PAGE_SHIFT]) + a_pg_offset; b_pg_offset = b_offset & PAGE_MASK; cnt = min(cnt, PAGE_SIZE - b_pg_offset); b_cp = (char *)VM_PAGE_TO_PHYS(mb[b_offset >> PAGE_SHIFT]) + b_pg_offset; bcopy(a_cp, b_cp, cnt); a_offset += cnt; b_offset += cnt; xfersize -= cnt; } } static inline void moea64_copy_pages_nodmap(mmu_t mmu, vm_page_t *ma, vm_offset_t a_offset, vm_page_t *mb, vm_offset_t b_offset, int xfersize) { void *a_cp, *b_cp; vm_offset_t a_pg_offset, b_pg_offset; int cnt; mtx_lock(&moea64_scratchpage_mtx); while (xfersize > 0) { a_pg_offset = a_offset & PAGE_MASK; cnt = min(xfersize, PAGE_SIZE - a_pg_offset); moea64_set_scratchpage_pa(mmu, 0, VM_PAGE_TO_PHYS(ma[a_offset >> PAGE_SHIFT])); a_cp = (char *)moea64_scratchpage_va[0] + a_pg_offset; b_pg_offset = b_offset & PAGE_MASK; cnt = min(cnt, PAGE_SIZE - b_pg_offset); moea64_set_scratchpage_pa(mmu, 1, VM_PAGE_TO_PHYS(mb[b_offset >> PAGE_SHIFT])); b_cp = (char *)moea64_scratchpage_va[1] + b_pg_offset; bcopy(a_cp, b_cp, cnt); a_offset += cnt; b_offset += cnt; xfersize -= cnt; } mtx_unlock(&moea64_scratchpage_mtx); } void moea64_copy_pages(mmu_t mmu, vm_page_t *ma, vm_offset_t a_offset, vm_page_t *mb, vm_offset_t b_offset, int xfersize) { if (hw_direct_map) { moea64_copy_pages_dmap(mmu, ma, a_offset, mb, b_offset, xfersize); } else { moea64_copy_pages_nodmap(mmu, ma, a_offset, mb, b_offset, xfersize); } } void moea64_zero_page_area(mmu_t mmu, vm_page_t m, int off, int size) { vm_paddr_t pa = VM_PAGE_TO_PHYS(m); if (size + off > PAGE_SIZE) panic("moea64_zero_page: size + off > PAGE_SIZE"); if (hw_direct_map) { bzero((caddr_t)pa + off, size); } else { mtx_lock(&moea64_scratchpage_mtx); moea64_set_scratchpage_pa(mmu, 0, pa); bzero((caddr_t)moea64_scratchpage_va[0] + off, size); mtx_unlock(&moea64_scratchpage_mtx); } } /* * Zero a page of physical memory by temporarily mapping it */ void moea64_zero_page(mmu_t mmu, vm_page_t m) { vm_paddr_t pa = VM_PAGE_TO_PHYS(m); vm_offset_t va, off; if (!hw_direct_map) { mtx_lock(&moea64_scratchpage_mtx); moea64_set_scratchpage_pa(mmu, 0, pa); va = moea64_scratchpage_va[0]; } else { va = pa; } for (off = 0; off < PAGE_SIZE; off += cacheline_size) __asm __volatile("dcbz 0,%0" :: "r"(va + off)); if (!hw_direct_map) mtx_unlock(&moea64_scratchpage_mtx); } void moea64_zero_page_idle(mmu_t mmu, vm_page_t m) { moea64_zero_page(mmu, m); } vm_offset_t moea64_quick_enter_page(mmu_t mmu, vm_page_t m) { struct pvo_entry *pvo; vm_paddr_t pa = VM_PAGE_TO_PHYS(m); if (hw_direct_map) return (pa); /* * MOEA64_PTE_REPLACE does some locking, so we can't just grab * a critical section and access the PCPU data like on i386. * Instead, pin the thread and grab the PCPU lock to prevent * a preempting thread from using the same PCPU data. */ sched_pin(); mtx_assert(PCPU_PTR(qmap_lock), MA_NOTOWNED); pvo = PCPU_GET(qmap_pvo); mtx_lock(PCPU_PTR(qmap_lock)); pvo->pvo_pte.pa = moea64_calc_wimg(pa, pmap_page_get_memattr(m)) | (uint64_t)pa; MOEA64_PTE_REPLACE(mmu, pvo, MOEA64_PTE_INVALIDATE); isync(); return (PCPU_GET(qmap_addr)); } void moea64_quick_remove_page(mmu_t mmu, vm_offset_t addr) { if (hw_direct_map) return; mtx_assert(PCPU_PTR(qmap_lock), MA_OWNED); KASSERT(PCPU_GET(qmap_addr) == addr, ("moea64_quick_remove_page: invalid address")); mtx_unlock(PCPU_PTR(qmap_lock)); sched_unpin(); } /* * Map the given physical page at the specified virtual address in the * target pmap with the protection requested. If specified the page * will be wired down. */ int moea64_enter(mmu_t mmu, pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot, u_int flags, int8_t psind) { struct pvo_entry *pvo, *oldpvo; struct pvo_head *pvo_head; uint64_t pte_lo; int error; if ((m->oflags & VPO_UNMANAGED) == 0 && !vm_page_xbusied(m)) VM_OBJECT_ASSERT_LOCKED(m->object); pvo = alloc_pvo_entry(0); pvo->pvo_pmap = NULL; /* to be filled in later */ pvo->pvo_pte.prot = prot; pte_lo = moea64_calc_wimg(VM_PAGE_TO_PHYS(m), pmap_page_get_memattr(m)); pvo->pvo_pte.pa = VM_PAGE_TO_PHYS(m) | pte_lo; if ((flags & PMAP_ENTER_WIRED) != 0) pvo->pvo_vaddr |= PVO_WIRED; if ((m->oflags & VPO_UNMANAGED) != 0 || !moea64_initialized) { pvo_head = NULL; } else { pvo_head = &m->md.mdpg_pvoh; pvo->pvo_vaddr |= PVO_MANAGED; } for (;;) { PV_PAGE_LOCK(m); PMAP_LOCK(pmap); if (pvo->pvo_pmap == NULL) init_pvo_entry(pvo, pmap, va); if (prot & VM_PROT_WRITE) if (pmap_bootstrapped && (m->oflags & VPO_UNMANAGED) == 0) vm_page_aflag_set(m, PGA_WRITEABLE); oldpvo = moea64_pvo_find_va(pmap, va); if (oldpvo != NULL) { if (oldpvo->pvo_vaddr == pvo->pvo_vaddr && oldpvo->pvo_pte.pa == pvo->pvo_pte.pa && oldpvo->pvo_pte.prot == prot) { /* Identical mapping already exists */ error = 0; /* If not in page table, reinsert it */ if (MOEA64_PTE_SYNCH(mmu, oldpvo) < 0) { moea64_pte_overflow--; MOEA64_PTE_INSERT(mmu, oldpvo); } /* Then just clean up and go home */ PV_PAGE_UNLOCK(m); PMAP_UNLOCK(pmap); free_pvo_entry(pvo); break; } /* Otherwise, need to kill it first */ KASSERT(oldpvo->pvo_pmap == pmap, ("pmap of old " "mapping does not match new mapping")); moea64_pvo_remove_from_pmap(mmu, oldpvo); } error = moea64_pvo_enter(mmu, pvo, pvo_head); PV_PAGE_UNLOCK(m); PMAP_UNLOCK(pmap); /* Free any dead pages */ if (oldpvo != NULL) { PV_LOCK(oldpvo->pvo_pte.pa & LPTE_RPGN); moea64_pvo_remove_from_page(mmu, oldpvo); PV_UNLOCK(oldpvo->pvo_pte.pa & LPTE_RPGN); free_pvo_entry(oldpvo); } if (error != ENOMEM) break; if ((flags & PMAP_ENTER_NOSLEEP) != 0) return (KERN_RESOURCE_SHORTAGE); VM_OBJECT_ASSERT_UNLOCKED(m->object); VM_WAIT; } /* * Flush the page from the instruction cache if this page is * mapped executable and cacheable. */ if (pmap != kernel_pmap && !(m->aflags & PGA_EXECUTABLE) && (pte_lo & (LPTE_I | LPTE_G | LPTE_NOEXEC)) == 0) { vm_page_aflag_set(m, PGA_EXECUTABLE); moea64_syncicache(mmu, pmap, va, VM_PAGE_TO_PHYS(m), PAGE_SIZE); } return (KERN_SUCCESS); } static void moea64_syncicache(mmu_t mmu, pmap_t pmap, vm_offset_t va, vm_paddr_t pa, vm_size_t sz) { /* * This is much trickier than on older systems because * we can't sync the icache on physical addresses directly * without a direct map. Instead we check a couple of cases * where the memory is already mapped in and, failing that, * use the same trick we use for page zeroing to create * a temporary mapping for this physical address. */ if (!pmap_bootstrapped) { /* * If PMAP is not bootstrapped, we are likely to be * in real mode. */ __syncicache((void *)pa, sz); } else if (pmap == kernel_pmap) { __syncicache((void *)va, sz); } else if (hw_direct_map) { __syncicache((void *)pa, sz); } else { /* Use the scratch page to set up a temp mapping */ mtx_lock(&moea64_scratchpage_mtx); moea64_set_scratchpage_pa(mmu, 1, pa & ~ADDR_POFF); __syncicache((void *)(moea64_scratchpage_va[1] + (va & ADDR_POFF)), sz); mtx_unlock(&moea64_scratchpage_mtx); } } /* * Maps a sequence of resident pages belonging to the same object. * The sequence begins with the given page m_start. This page is * mapped at the given virtual address start. Each subsequent page is * mapped at a virtual address that is offset from start by the same * amount as the page is offset from m_start within the object. The * last page in the sequence is the page with the largest offset from * m_start that can be mapped at a virtual address less than the given * virtual address end. Not every virtual page between start and end * is mapped; only those for which a resident page exists with the * corresponding offset from m_start are mapped. */ void moea64_enter_object(mmu_t mmu, pmap_t pm, vm_offset_t start, vm_offset_t end, vm_page_t m_start, vm_prot_t prot) { vm_page_t m; vm_pindex_t diff, psize; VM_OBJECT_ASSERT_LOCKED(m_start->object); psize = atop(end - start); m = m_start; while (m != NULL && (diff = m->pindex - m_start->pindex) < psize) { moea64_enter(mmu, pm, start + ptoa(diff), m, prot & (VM_PROT_READ | VM_PROT_EXECUTE), PMAP_ENTER_NOSLEEP, 0); m = TAILQ_NEXT(m, listq); } } void moea64_enter_quick(mmu_t mmu, pmap_t pm, vm_offset_t va, vm_page_t m, vm_prot_t prot) { moea64_enter(mmu, pm, va, m, prot & (VM_PROT_READ | VM_PROT_EXECUTE), PMAP_ENTER_NOSLEEP, 0); } vm_paddr_t moea64_extract(mmu_t mmu, pmap_t pm, vm_offset_t va) { struct pvo_entry *pvo; vm_paddr_t pa; PMAP_LOCK(pm); pvo = moea64_pvo_find_va(pm, va); if (pvo == NULL) pa = 0; else pa = (pvo->pvo_pte.pa & LPTE_RPGN) | (va - PVO_VADDR(pvo)); PMAP_UNLOCK(pm); return (pa); } /* * Atomically extract and hold the physical page with the given * pmap and virtual address pair if that mapping permits the given * protection. */ vm_page_t moea64_extract_and_hold(mmu_t mmu, pmap_t pmap, vm_offset_t va, vm_prot_t prot) { struct pvo_entry *pvo; vm_page_t m; vm_paddr_t pa; m = NULL; pa = 0; PMAP_LOCK(pmap); retry: pvo = moea64_pvo_find_va(pmap, va & ~ADDR_POFF); if (pvo != NULL && (pvo->pvo_pte.prot & prot) == prot) { if (vm_page_pa_tryrelock(pmap, pvo->pvo_pte.pa & LPTE_RPGN, &pa)) goto retry; m = PHYS_TO_VM_PAGE(pvo->pvo_pte.pa & LPTE_RPGN); vm_page_hold(m); } PA_UNLOCK_COND(pa); PMAP_UNLOCK(pmap); return (m); } static mmu_t installed_mmu; static void * moea64_uma_page_alloc(uma_zone_t zone, vm_size_t bytes, uint8_t *flags, int wait) { struct pvo_entry *pvo; vm_offset_t va; vm_page_t m; int pflags, needed_lock; /* * This entire routine is a horrible hack to avoid bothering kmem * for new KVA addresses. Because this can get called from inside * kmem allocation routines, calling kmem for a new address here * can lead to multiply locking non-recursive mutexes. */ *flags = UMA_SLAB_PRIV; needed_lock = !PMAP_LOCKED(kernel_pmap); pflags = malloc2vm_flags(wait) | VM_ALLOC_WIRED; for (;;) { m = vm_page_alloc(NULL, 0, pflags | VM_ALLOC_NOOBJ); if (m == NULL) { if (wait & M_NOWAIT) return (NULL); VM_WAIT; } else break; } va = VM_PAGE_TO_PHYS(m); pvo = alloc_pvo_entry(1 /* bootstrap */); pvo->pvo_pte.prot = VM_PROT_READ | VM_PROT_WRITE; pvo->pvo_pte.pa = VM_PAGE_TO_PHYS(m) | LPTE_M; if (needed_lock) PMAP_LOCK(kernel_pmap); init_pvo_entry(pvo, kernel_pmap, va); pvo->pvo_vaddr |= PVO_WIRED; moea64_pvo_enter(installed_mmu, pvo, NULL); if (needed_lock) PMAP_UNLOCK(kernel_pmap); if ((wait & M_ZERO) && (m->flags & PG_ZERO) == 0) bzero((void *)va, PAGE_SIZE); return (void *)va; } extern int elf32_nxstack; void moea64_init(mmu_t mmu) { CTR0(KTR_PMAP, "moea64_init"); moea64_pvo_zone = uma_zcreate("UPVO entry", sizeof (struct pvo_entry), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_VM | UMA_ZONE_NOFREE); if (!hw_direct_map) { installed_mmu = mmu; uma_zone_set_allocf(moea64_pvo_zone,moea64_uma_page_alloc); } #ifdef COMPAT_FREEBSD32 elf32_nxstack = 1; #endif moea64_initialized = TRUE; } boolean_t moea64_is_referenced(mmu_t mmu, vm_page_t m) { KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("moea64_is_referenced: page %p is not managed", m)); return (moea64_query_bit(mmu, m, LPTE_REF)); } boolean_t moea64_is_modified(mmu_t mmu, vm_page_t m) { KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("moea64_is_modified: page %p is not managed", m)); /* * If the page is not exclusive busied, then PGA_WRITEABLE cannot be * concurrently set while the object is locked. Thus, if PGA_WRITEABLE * is clear, no PTEs can have LPTE_CHG set. */ VM_OBJECT_ASSERT_LOCKED(m->object); if (!vm_page_xbusied(m) && (m->aflags & PGA_WRITEABLE) == 0) return (FALSE); return (moea64_query_bit(mmu, m, LPTE_CHG)); } boolean_t moea64_is_prefaultable(mmu_t mmu, pmap_t pmap, vm_offset_t va) { struct pvo_entry *pvo; boolean_t rv = TRUE; PMAP_LOCK(pmap); pvo = moea64_pvo_find_va(pmap, va & ~ADDR_POFF); if (pvo != NULL) rv = FALSE; PMAP_UNLOCK(pmap); return (rv); } void moea64_clear_modify(mmu_t mmu, vm_page_t m) { KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("moea64_clear_modify: page %p is not managed", m)); VM_OBJECT_ASSERT_WLOCKED(m->object); KASSERT(!vm_page_xbusied(m), ("moea64_clear_modify: page %p is exclusive busied", m)); /* * If the page is not PGA_WRITEABLE, then no PTEs can have LPTE_CHG * set. If the object containing the page is locked and the page is * not exclusive busied, then PGA_WRITEABLE cannot be concurrently set. */ if ((m->aflags & PGA_WRITEABLE) == 0) return; moea64_clear_bit(mmu, m, LPTE_CHG); } /* * Clear the write and modified bits in each of the given page's mappings. */ void moea64_remove_write(mmu_t mmu, vm_page_t m) { struct pvo_entry *pvo; int64_t refchg, ret; pmap_t pmap; KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("moea64_remove_write: page %p is not managed", m)); /* * If the page is not exclusive busied, then PGA_WRITEABLE cannot be * set by another thread while the object is locked. Thus, * if PGA_WRITEABLE is clear, no page table entries need updating. */ VM_OBJECT_ASSERT_WLOCKED(m->object); if (!vm_page_xbusied(m) && (m->aflags & PGA_WRITEABLE) == 0) return; powerpc_sync(); PV_PAGE_LOCK(m); refchg = 0; LIST_FOREACH(pvo, vm_page_to_pvoh(m), pvo_vlink) { pmap = pvo->pvo_pmap; PMAP_LOCK(pmap); if (!(pvo->pvo_vaddr & PVO_DEAD) && (pvo->pvo_pte.prot & VM_PROT_WRITE)) { pvo->pvo_pte.prot &= ~VM_PROT_WRITE; ret = MOEA64_PTE_REPLACE(mmu, pvo, MOEA64_PTE_PROT_UPDATE); if (ret < 0) ret = LPTE_CHG; refchg |= ret; if (pvo->pvo_pmap == kernel_pmap) isync(); } PMAP_UNLOCK(pmap); } if ((refchg | atomic_readandclear_32(&m->md.mdpg_attrs)) & LPTE_CHG) vm_page_dirty(m); vm_page_aflag_clear(m, PGA_WRITEABLE); PV_PAGE_UNLOCK(m); } /* * moea64_ts_referenced: * * Return a count of reference bits for a page, clearing those bits. * It is not necessary for every reference bit to be cleared, but it * is necessary that 0 only be returned when there are truly no * reference bits set. * * XXX: The exact number of bits to check and clear is a matter that * should be tested and standardized at some point in the future for * optimal aging of shared pages. */ int moea64_ts_referenced(mmu_t mmu, vm_page_t m) { KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("moea64_ts_referenced: page %p is not managed", m)); return (moea64_clear_bit(mmu, m, LPTE_REF)); } /* * Modify the WIMG settings of all mappings for a page. */ void moea64_page_set_memattr(mmu_t mmu, vm_page_t m, vm_memattr_t ma) { struct pvo_entry *pvo; int64_t refchg; pmap_t pmap; uint64_t lo; if ((m->oflags & VPO_UNMANAGED) != 0) { m->md.mdpg_cache_attrs = ma; return; } lo = moea64_calc_wimg(VM_PAGE_TO_PHYS(m), ma); PV_PAGE_LOCK(m); LIST_FOREACH(pvo, vm_page_to_pvoh(m), pvo_vlink) { pmap = pvo->pvo_pmap; PMAP_LOCK(pmap); if (!(pvo->pvo_vaddr & PVO_DEAD)) { pvo->pvo_pte.pa &= ~LPTE_WIMG; pvo->pvo_pte.pa |= lo; refchg = MOEA64_PTE_REPLACE(mmu, pvo, MOEA64_PTE_INVALIDATE); if (refchg < 0) refchg = (pvo->pvo_pte.prot & VM_PROT_WRITE) ? LPTE_CHG : 0; if ((pvo->pvo_vaddr & PVO_MANAGED) && (pvo->pvo_pte.prot & VM_PROT_WRITE)) { refchg |= atomic_readandclear_32(&m->md.mdpg_attrs); if (refchg & LPTE_CHG) vm_page_dirty(m); if (refchg & LPTE_REF) vm_page_aflag_set(m, PGA_REFERENCED); } if (pvo->pvo_pmap == kernel_pmap) isync(); } PMAP_UNLOCK(pmap); } m->md.mdpg_cache_attrs = ma; PV_PAGE_UNLOCK(m); } /* * Map a wired page into kernel virtual address space. */ void moea64_kenter_attr(mmu_t mmu, vm_offset_t va, vm_paddr_t pa, vm_memattr_t ma) { int error; struct pvo_entry *pvo, *oldpvo; pvo = alloc_pvo_entry(0); pvo->pvo_pte.prot = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; pvo->pvo_pte.pa = (pa & ~ADDR_POFF) | moea64_calc_wimg(pa, ma); pvo->pvo_vaddr |= PVO_WIRED; PMAP_LOCK(kernel_pmap); oldpvo = moea64_pvo_find_va(kernel_pmap, va); if (oldpvo != NULL) moea64_pvo_remove_from_pmap(mmu, oldpvo); init_pvo_entry(pvo, kernel_pmap, va); error = moea64_pvo_enter(mmu, pvo, NULL); PMAP_UNLOCK(kernel_pmap); /* Free any dead pages */ if (oldpvo != NULL) { PV_LOCK(oldpvo->pvo_pte.pa & LPTE_RPGN); moea64_pvo_remove_from_page(mmu, oldpvo); PV_UNLOCK(oldpvo->pvo_pte.pa & LPTE_RPGN); free_pvo_entry(oldpvo); } if (error != 0 && error != ENOENT) panic("moea64_kenter: failed to enter va %#zx pa %#zx: %d", va, pa, error); } void moea64_kenter(mmu_t mmu, vm_offset_t va, vm_paddr_t pa) { moea64_kenter_attr(mmu, va, pa, VM_MEMATTR_DEFAULT); } /* * Extract the physical page address associated with the given kernel virtual * address. */ vm_paddr_t moea64_kextract(mmu_t mmu, vm_offset_t va) { struct pvo_entry *pvo; vm_paddr_t pa; /* * Shortcut the direct-mapped case when applicable. We never put * anything but 1:1 mappings below VM_MIN_KERNEL_ADDRESS. */ if (va < VM_MIN_KERNEL_ADDRESS) return (va); PMAP_LOCK(kernel_pmap); pvo = moea64_pvo_find_va(kernel_pmap, va); KASSERT(pvo != NULL, ("moea64_kextract: no addr found for %#" PRIxPTR, va)); pa = (pvo->pvo_pte.pa & LPTE_RPGN) | (va - PVO_VADDR(pvo)); PMAP_UNLOCK(kernel_pmap); return (pa); } /* * Remove a wired page from kernel virtual address space. */ void moea64_kremove(mmu_t mmu, vm_offset_t va) { moea64_remove(mmu, kernel_pmap, va, va + PAGE_SIZE); } /* * Map a range of physical addresses into kernel virtual address space. * * The value passed in *virt is a suggested virtual address for the mapping. * Architectures which can support a direct-mapped physical to virtual region * can return the appropriate address within that region, leaving '*virt' * unchanged. Other architectures should map the pages starting at '*virt' and * update '*virt' with the first usable address after the mapped region. */ vm_offset_t moea64_map(mmu_t mmu, vm_offset_t *virt, vm_paddr_t pa_start, vm_paddr_t pa_end, int prot) { vm_offset_t sva, va; if (hw_direct_map) { /* * Check if every page in the region is covered by the direct * map. The direct map covers all of physical memory. Use * moea64_calc_wimg() as a shortcut to see if the page is in * physical memory as a way to see if the direct map covers it. */ for (va = pa_start; va < pa_end; va += PAGE_SIZE) if (moea64_calc_wimg(va, VM_MEMATTR_DEFAULT) != LPTE_M) break; if (va == pa_end) return (pa_start); } sva = *virt; va = sva; /* XXX respect prot argument */ for (; pa_start < pa_end; pa_start += PAGE_SIZE, va += PAGE_SIZE) moea64_kenter(mmu, va, pa_start); *virt = va; return (sva); } /* * Returns true if the pmap's pv is one of the first * 16 pvs linked to from this page. This count may * be changed upwards or downwards in the future; it * is only necessary that true be returned for a small * subset of pmaps for proper page aging. */ boolean_t moea64_page_exists_quick(mmu_t mmu, pmap_t pmap, vm_page_t m) { int loops; struct pvo_entry *pvo; boolean_t rv; KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("moea64_page_exists_quick: page %p is not managed", m)); loops = 0; rv = FALSE; PV_PAGE_LOCK(m); LIST_FOREACH(pvo, vm_page_to_pvoh(m), pvo_vlink) { if (!(pvo->pvo_vaddr & PVO_DEAD) && pvo->pvo_pmap == pmap) { rv = TRUE; break; } if (++loops >= 16) break; } PV_PAGE_UNLOCK(m); return (rv); } /* * Return the number of managed mappings to the given physical page * that are wired. */ int moea64_page_wired_mappings(mmu_t mmu, vm_page_t m) { struct pvo_entry *pvo; int count; count = 0; if ((m->oflags & VPO_UNMANAGED) != 0) return (count); PV_PAGE_LOCK(m); LIST_FOREACH(pvo, vm_page_to_pvoh(m), pvo_vlink) if ((pvo->pvo_vaddr & (PVO_DEAD | PVO_WIRED)) == PVO_WIRED) count++; PV_PAGE_UNLOCK(m); return (count); } static uintptr_t moea64_vsidcontext; uintptr_t moea64_get_unique_vsid(void) { u_int entropy; register_t hash; uint32_t mask; int i; entropy = 0; __asm __volatile("mftb %0" : "=r"(entropy)); mtx_lock(&moea64_slb_mutex); for (i = 0; i < NVSIDS; i += VSID_NBPW) { u_int n; /* * Create a new value by mutiplying by a prime and adding in * entropy from the timebase register. This is to make the * VSID more random so that the PT hash function collides * less often. (Note that the prime casues gcc to do shifts * instead of a multiply.) */ moea64_vsidcontext = (moea64_vsidcontext * 0x1105) + entropy; hash = moea64_vsidcontext & (NVSIDS - 1); if (hash == 0) /* 0 is special, avoid it */ continue; n = hash >> 5; mask = 1 << (hash & (VSID_NBPW - 1)); hash = (moea64_vsidcontext & VSID_HASHMASK); if (moea64_vsid_bitmap[n] & mask) { /* collision? */ /* anything free in this bucket? */ if (moea64_vsid_bitmap[n] == 0xffffffff) { entropy = (moea64_vsidcontext >> 20); continue; } i = ffs(~moea64_vsid_bitmap[n]) - 1; mask = 1 << i; hash &= VSID_HASHMASK & ~(VSID_NBPW - 1); hash |= i; } if (hash == VSID_VRMA) /* also special, avoid this too */ continue; KASSERT(!(moea64_vsid_bitmap[n] & mask), ("Allocating in-use VSID %#zx\n", hash)); moea64_vsid_bitmap[n] |= mask; mtx_unlock(&moea64_slb_mutex); return (hash); } mtx_unlock(&moea64_slb_mutex); panic("%s: out of segments",__func__); } #ifdef __powerpc64__ void moea64_pinit(mmu_t mmu, pmap_t pmap) { RB_INIT(&pmap->pmap_pvo); pmap->pm_slb_tree_root = slb_alloc_tree(); pmap->pm_slb = slb_alloc_user_cache(); pmap->pm_slb_len = 0; } #else void moea64_pinit(mmu_t mmu, pmap_t pmap) { int i; uint32_t hash; RB_INIT(&pmap->pmap_pvo); if (pmap_bootstrapped) pmap->pmap_phys = (pmap_t)moea64_kextract(mmu, (vm_offset_t)pmap); else pmap->pmap_phys = pmap; /* * Allocate some segment registers for this pmap. */ hash = moea64_get_unique_vsid(); for (i = 0; i < 16; i++) pmap->pm_sr[i] = VSID_MAKE(i, hash); KASSERT(pmap->pm_sr[0] != 0, ("moea64_pinit: pm_sr[0] = 0")); } #endif /* * Initialize the pmap associated with process 0. */ void moea64_pinit0(mmu_t mmu, pmap_t pm) { PMAP_LOCK_INIT(pm); moea64_pinit(mmu, pm); bzero(&pm->pm_stats, sizeof(pm->pm_stats)); } /* * Set the physical protection on the specified range of this map as requested. */ static void moea64_pvo_protect(mmu_t mmu, pmap_t pm, struct pvo_entry *pvo, vm_prot_t prot) { struct vm_page *pg; vm_prot_t oldprot; int32_t refchg; PMAP_LOCK_ASSERT(pm, MA_OWNED); /* * Change the protection of the page. */ oldprot = pvo->pvo_pte.prot; pvo->pvo_pte.prot = prot; pg = PHYS_TO_VM_PAGE(pvo->pvo_pte.pa & LPTE_RPGN); /* * If the PVO is in the page table, update mapping */ refchg = MOEA64_PTE_REPLACE(mmu, pvo, MOEA64_PTE_PROT_UPDATE); if (refchg < 0) refchg = (oldprot & VM_PROT_WRITE) ? LPTE_CHG : 0; if (pm != kernel_pmap && pg != NULL && !(pg->aflags & PGA_EXECUTABLE) && (pvo->pvo_pte.pa & (LPTE_I | LPTE_G | LPTE_NOEXEC)) == 0) { if ((pg->oflags & VPO_UNMANAGED) == 0) vm_page_aflag_set(pg, PGA_EXECUTABLE); moea64_syncicache(mmu, pm, PVO_VADDR(pvo), pvo->pvo_pte.pa & LPTE_RPGN, PAGE_SIZE); } /* * Update vm about the REF/CHG bits if the page is managed and we have * removed write access. */ if (pg != NULL && (pvo->pvo_vaddr & PVO_MANAGED) && (oldprot & VM_PROT_WRITE)) { refchg |= atomic_readandclear_32(&pg->md.mdpg_attrs); if (refchg & LPTE_CHG) vm_page_dirty(pg); if (refchg & LPTE_REF) vm_page_aflag_set(pg, PGA_REFERENCED); } } void moea64_protect(mmu_t mmu, pmap_t pm, vm_offset_t sva, vm_offset_t eva, vm_prot_t prot) { struct pvo_entry *pvo, *tpvo, key; CTR4(KTR_PMAP, "moea64_protect: pm=%p sva=%#x eva=%#x prot=%#x", pm, sva, eva, prot); KASSERT(pm == &curproc->p_vmspace->vm_pmap || pm == kernel_pmap, ("moea64_protect: non current pmap")); if ((prot & VM_PROT_READ) == VM_PROT_NONE) { moea64_remove(mmu, pm, sva, eva); return; } PMAP_LOCK(pm); key.pvo_vaddr = sva; for (pvo = RB_NFIND(pvo_tree, &pm->pmap_pvo, &key); pvo != NULL && PVO_VADDR(pvo) < eva; pvo = tpvo) { tpvo = RB_NEXT(pvo_tree, &pm->pmap_pvo, pvo); moea64_pvo_protect(mmu, pm, pvo, prot); } PMAP_UNLOCK(pm); } /* * Map a list of wired pages into kernel virtual address space. This is * intended for temporary mappings which do not need page modification or * references recorded. Existing mappings in the region are overwritten. */ void moea64_qenter(mmu_t mmu, vm_offset_t va, vm_page_t *m, int count) { while (count-- > 0) { moea64_kenter(mmu, va, VM_PAGE_TO_PHYS(*m)); va += PAGE_SIZE; m++; } } /* * Remove page mappings from kernel virtual address space. Intended for * temporary mappings entered by moea64_qenter. */ void moea64_qremove(mmu_t mmu, vm_offset_t va, int count) { while (count-- > 0) { moea64_kremove(mmu, va); va += PAGE_SIZE; } } void moea64_release_vsid(uint64_t vsid) { int idx, mask; mtx_lock(&moea64_slb_mutex); idx = vsid & (NVSIDS-1); mask = 1 << (idx % VSID_NBPW); idx /= VSID_NBPW; KASSERT(moea64_vsid_bitmap[idx] & mask, ("Freeing unallocated VSID %#jx", vsid)); moea64_vsid_bitmap[idx] &= ~mask; mtx_unlock(&moea64_slb_mutex); } void moea64_release(mmu_t mmu, pmap_t pmap) { /* * Free segment registers' VSIDs */ #ifdef __powerpc64__ slb_free_tree(pmap); slb_free_user_cache(pmap->pm_slb); #else KASSERT(pmap->pm_sr[0] != 0, ("moea64_release: pm_sr[0] = 0")); moea64_release_vsid(VSID_TO_HASH(pmap->pm_sr[0])); #endif } /* * Remove all pages mapped by the specified pmap */ void moea64_remove_pages(mmu_t mmu, pmap_t pm) { struct pvo_entry *pvo, *tpvo; struct pvo_tree tofree; RB_INIT(&tofree); PMAP_LOCK(pm); RB_FOREACH_SAFE(pvo, pvo_tree, &pm->pmap_pvo, tpvo) { if (pvo->pvo_vaddr & PVO_WIRED) continue; /* * For locking reasons, remove this from the page table and * pmap, but save delinking from the vm_page for a second * pass */ moea64_pvo_remove_from_pmap(mmu, pvo); RB_INSERT(pvo_tree, &tofree, pvo); } PMAP_UNLOCK(pm); RB_FOREACH_SAFE(pvo, pvo_tree, &tofree, tpvo) { PV_LOCK(pvo->pvo_pte.pa & LPTE_RPGN); moea64_pvo_remove_from_page(mmu, pvo); PV_UNLOCK(pvo->pvo_pte.pa & LPTE_RPGN); RB_REMOVE(pvo_tree, &tofree, pvo); free_pvo_entry(pvo); } } /* * Remove the given range of addresses from the specified map. */ void moea64_remove(mmu_t mmu, pmap_t pm, vm_offset_t sva, vm_offset_t eva) { struct pvo_entry *pvo, *tpvo, key; struct pvo_tree tofree; /* * Perform an unsynchronized read. This is, however, safe. */ if (pm->pm_stats.resident_count == 0) return; key.pvo_vaddr = sva; RB_INIT(&tofree); PMAP_LOCK(pm); for (pvo = RB_NFIND(pvo_tree, &pm->pmap_pvo, &key); pvo != NULL && PVO_VADDR(pvo) < eva; pvo = tpvo) { tpvo = RB_NEXT(pvo_tree, &pm->pmap_pvo, pvo); /* * For locking reasons, remove this from the page table and * pmap, but save delinking from the vm_page for a second * pass */ moea64_pvo_remove_from_pmap(mmu, pvo); RB_INSERT(pvo_tree, &tofree, pvo); } PMAP_UNLOCK(pm); RB_FOREACH_SAFE(pvo, pvo_tree, &tofree, tpvo) { PV_LOCK(pvo->pvo_pte.pa & LPTE_RPGN); moea64_pvo_remove_from_page(mmu, pvo); PV_UNLOCK(pvo->pvo_pte.pa & LPTE_RPGN); RB_REMOVE(pvo_tree, &tofree, pvo); free_pvo_entry(pvo); } } /* * Remove physical page from all pmaps in which it resides. moea64_pvo_remove() * will reflect changes in pte's back to the vm_page. */ void moea64_remove_all(mmu_t mmu, vm_page_t m) { struct pvo_entry *pvo, *next_pvo; struct pvo_head freequeue; int wasdead; pmap_t pmap; LIST_INIT(&freequeue); PV_PAGE_LOCK(m); LIST_FOREACH_SAFE(pvo, vm_page_to_pvoh(m), pvo_vlink, next_pvo) { pmap = pvo->pvo_pmap; PMAP_LOCK(pmap); wasdead = (pvo->pvo_vaddr & PVO_DEAD); if (!wasdead) moea64_pvo_remove_from_pmap(mmu, pvo); moea64_pvo_remove_from_page(mmu, pvo); if (!wasdead) LIST_INSERT_HEAD(&freequeue, pvo, pvo_vlink); PMAP_UNLOCK(pmap); } KASSERT(!pmap_page_is_mapped(m), ("Page still has mappings")); KASSERT(!(m->aflags & PGA_WRITEABLE), ("Page still writable")); PV_PAGE_UNLOCK(m); /* Clean up UMA allocations */ LIST_FOREACH_SAFE(pvo, &freequeue, pvo_vlink, next_pvo) free_pvo_entry(pvo); } /* * Allocate a physical page of memory directly from the phys_avail map. * Can only be called from moea64_bootstrap before avail start and end are * calculated. */ vm_offset_t moea64_bootstrap_alloc(vm_size_t size, u_int align) { vm_offset_t s, e; int i, j; size = round_page(size); for (i = 0; phys_avail[i + 1] != 0; i += 2) { if (align != 0) s = (phys_avail[i] + align - 1) & ~(align - 1); else s = phys_avail[i]; e = s + size; if (s < phys_avail[i] || e > phys_avail[i + 1]) continue; if (s + size > platform_real_maxaddr()) continue; if (s == phys_avail[i]) { phys_avail[i] += size; } else if (e == phys_avail[i + 1]) { phys_avail[i + 1] -= size; } else { for (j = phys_avail_count * 2; j > i; j -= 2) { phys_avail[j] = phys_avail[j - 2]; phys_avail[j + 1] = phys_avail[j - 1]; } phys_avail[i + 3] = phys_avail[i + 1]; phys_avail[i + 1] = s; phys_avail[i + 2] = e; phys_avail_count++; } return (s); } panic("moea64_bootstrap_alloc: could not allocate memory"); } static int moea64_pvo_enter(mmu_t mmu, struct pvo_entry *pvo, struct pvo_head *pvo_head) { int first, err; PMAP_LOCK_ASSERT(pvo->pvo_pmap, MA_OWNED); KASSERT(moea64_pvo_find_va(pvo->pvo_pmap, PVO_VADDR(pvo)) == NULL, ("Existing mapping for VA %#jx", (uintmax_t)PVO_VADDR(pvo))); moea64_pvo_enter_calls++; /* * Add to pmap list */ RB_INSERT(pvo_tree, &pvo->pvo_pmap->pmap_pvo, pvo); /* * Remember if the list was empty and therefore will be the first * item. */ if (pvo_head != NULL) { if (LIST_FIRST(pvo_head) == NULL) first = 1; LIST_INSERT_HEAD(pvo_head, pvo, pvo_vlink); } if (pvo->pvo_vaddr & PVO_WIRED) pvo->pvo_pmap->pm_stats.wired_count++; pvo->pvo_pmap->pm_stats.resident_count++; /* * Insert it into the hardware page table */ err = MOEA64_PTE_INSERT(mmu, pvo); if (err != 0) { panic("moea64_pvo_enter: overflow"); } moea64_pvo_entries++; if (pvo->pvo_pmap == kernel_pmap) isync(); #ifdef __powerpc64__ /* * Make sure all our bootstrap mappings are in the SLB as soon * as virtual memory is switched on. */ if (!pmap_bootstrapped) moea64_bootstrap_slb_prefault(PVO_VADDR(pvo), pvo->pvo_vaddr & PVO_LARGE); #endif return (first ? ENOENT : 0); } static void moea64_pvo_remove_from_pmap(mmu_t mmu, struct pvo_entry *pvo) { struct vm_page *pg; int32_t refchg; KASSERT(pvo->pvo_pmap != NULL, ("Trying to remove PVO with no pmap")); PMAP_LOCK_ASSERT(pvo->pvo_pmap, MA_OWNED); KASSERT(!(pvo->pvo_vaddr & PVO_DEAD), ("Trying to remove dead PVO")); /* * If there is an active pte entry, we need to deactivate it */ refchg = MOEA64_PTE_UNSET(mmu, pvo); if (refchg < 0) { /* * If it was evicted from the page table, be pessimistic and * dirty the page. */ if (pvo->pvo_pte.prot & VM_PROT_WRITE) refchg = LPTE_CHG; else refchg = 0; } /* * Update our statistics. */ pvo->pvo_pmap->pm_stats.resident_count--; if (pvo->pvo_vaddr & PVO_WIRED) pvo->pvo_pmap->pm_stats.wired_count--; /* * Remove this PVO from the pmap list. */ RB_REMOVE(pvo_tree, &pvo->pvo_pmap->pmap_pvo, pvo); /* * Mark this for the next sweep */ pvo->pvo_vaddr |= PVO_DEAD; /* Send RC bits to VM */ if ((pvo->pvo_vaddr & PVO_MANAGED) && (pvo->pvo_pte.prot & VM_PROT_WRITE)) { pg = PHYS_TO_VM_PAGE(pvo->pvo_pte.pa & LPTE_RPGN); if (pg != NULL) { refchg |= atomic_readandclear_32(&pg->md.mdpg_attrs); if (refchg & LPTE_CHG) vm_page_dirty(pg); if (refchg & LPTE_REF) vm_page_aflag_set(pg, PGA_REFERENCED); } } } static void moea64_pvo_remove_from_page(mmu_t mmu, struct pvo_entry *pvo) { struct vm_page *pg; KASSERT(pvo->pvo_vaddr & PVO_DEAD, ("Trying to delink live page")); /* Use NULL pmaps as a sentinel for races in page deletion */ if (pvo->pvo_pmap == NULL) return; pvo->pvo_pmap = NULL; /* * Update vm about page writeability/executability if managed */ PV_LOCKASSERT(pvo->pvo_pte.pa & LPTE_RPGN); pg = PHYS_TO_VM_PAGE(pvo->pvo_pte.pa & LPTE_RPGN); if ((pvo->pvo_vaddr & PVO_MANAGED) && pg != NULL) { LIST_REMOVE(pvo, pvo_vlink); if (LIST_EMPTY(vm_page_to_pvoh(pg))) vm_page_aflag_clear(pg, PGA_WRITEABLE | PGA_EXECUTABLE); } moea64_pvo_entries--; moea64_pvo_remove_calls++; } static struct pvo_entry * moea64_pvo_find_va(pmap_t pm, vm_offset_t va) { struct pvo_entry key; PMAP_LOCK_ASSERT(pm, MA_OWNED); key.pvo_vaddr = va & ~ADDR_POFF; return (RB_FIND(pvo_tree, &pm->pmap_pvo, &key)); } static boolean_t moea64_query_bit(mmu_t mmu, vm_page_t m, uint64_t ptebit) { struct pvo_entry *pvo; int64_t ret; boolean_t rv; /* * See if this bit is stored in the page already. */ if (m->md.mdpg_attrs & ptebit) return (TRUE); /* * Examine each PTE. Sync so that any pending REF/CHG bits are * flushed to the PTEs. */ rv = FALSE; powerpc_sync(); PV_PAGE_LOCK(m); LIST_FOREACH(pvo, vm_page_to_pvoh(m), pvo_vlink) { ret = 0; /* * See if this pvo has a valid PTE. if so, fetch the * REF/CHG bits from the valid PTE. If the appropriate * ptebit is set, return success. */ PMAP_LOCK(pvo->pvo_pmap); if (!(pvo->pvo_vaddr & PVO_DEAD)) ret = MOEA64_PTE_SYNCH(mmu, pvo); PMAP_UNLOCK(pvo->pvo_pmap); if (ret > 0) { atomic_set_32(&m->md.mdpg_attrs, ret & (LPTE_CHG | LPTE_REF)); if (ret & ptebit) { rv = TRUE; break; } } } PV_PAGE_UNLOCK(m); return (rv); } static u_int moea64_clear_bit(mmu_t mmu, vm_page_t m, u_int64_t ptebit) { u_int count; struct pvo_entry *pvo; int64_t ret; /* * Sync so that any pending REF/CHG bits are flushed to the PTEs (so * we can reset the right ones). */ powerpc_sync(); /* * For each pvo entry, clear the pte's ptebit. */ count = 0; PV_PAGE_LOCK(m); LIST_FOREACH(pvo, vm_page_to_pvoh(m), pvo_vlink) { ret = 0; PMAP_LOCK(pvo->pvo_pmap); if (!(pvo->pvo_vaddr & PVO_DEAD)) ret = MOEA64_PTE_CLEAR(mmu, pvo, ptebit); PMAP_UNLOCK(pvo->pvo_pmap); if (ret > 0 && (ret & ptebit)) count++; } atomic_clear_32(&m->md.mdpg_attrs, ptebit); PV_PAGE_UNLOCK(m); return (count); } boolean_t moea64_dev_direct_mapped(mmu_t mmu, vm_paddr_t pa, vm_size_t size) { struct pvo_entry *pvo, key; vm_offset_t ppa; int error = 0; PMAP_LOCK(kernel_pmap); key.pvo_vaddr = ppa = pa & ~ADDR_POFF; for (pvo = RB_FIND(pvo_tree, &kernel_pmap->pmap_pvo, &key); ppa < pa + size; ppa += PAGE_SIZE, pvo = RB_NEXT(pvo_tree, &kernel_pmap->pmap_pvo, pvo)) { if (pvo == NULL || (pvo->pvo_pte.pa & LPTE_RPGN) != ppa) { error = EFAULT; break; } } PMAP_UNLOCK(kernel_pmap); return (error); } /* * Map a set of physical memory pages into the kernel virtual * address space. Return a pointer to where it is mapped. This * routine is intended to be used for mapping device memory, * NOT real memory. */ void * moea64_mapdev_attr(mmu_t mmu, vm_paddr_t pa, vm_size_t size, vm_memattr_t ma) { vm_offset_t va, tmpva, ppa, offset; ppa = trunc_page(pa); offset = pa & PAGE_MASK; size = roundup2(offset + size, PAGE_SIZE); va = kva_alloc(size); if (!va) panic("moea64_mapdev: Couldn't alloc kernel virtual memory"); for (tmpva = va; size > 0;) { moea64_kenter_attr(mmu, tmpva, ppa, ma); size -= PAGE_SIZE; tmpva += PAGE_SIZE; ppa += PAGE_SIZE; } return ((void *)(va + offset)); } void * moea64_mapdev(mmu_t mmu, vm_paddr_t pa, vm_size_t size) { return moea64_mapdev_attr(mmu, pa, size, VM_MEMATTR_DEFAULT); } void moea64_unmapdev(mmu_t mmu, vm_offset_t va, vm_size_t size) { vm_offset_t base, offset; base = trunc_page(va); offset = va & PAGE_MASK; size = roundup2(offset + size, PAGE_SIZE); kva_free(base, size); } void moea64_sync_icache(mmu_t mmu, pmap_t pm, vm_offset_t va, vm_size_t sz) { struct pvo_entry *pvo; vm_offset_t lim; vm_paddr_t pa; vm_size_t len; PMAP_LOCK(pm); while (sz > 0) { lim = round_page(va); len = MIN(lim - va, sz); pvo = moea64_pvo_find_va(pm, va & ~ADDR_POFF); if (pvo != NULL && !(pvo->pvo_pte.pa & LPTE_I)) { pa = (pvo->pvo_pte.pa & LPTE_RPGN) | (va & ADDR_POFF); moea64_syncicache(mmu, pm, va, pa, len); } va += len; sz -= len; } PMAP_UNLOCK(pm); } void moea64_dumpsys_map(mmu_t mmu, vm_paddr_t pa, size_t sz, void **va) { *va = (void *)pa; } extern struct dump_pa dump_map[PHYS_AVAIL_SZ + 1]; void moea64_scan_init(mmu_t mmu) { struct pvo_entry *pvo; vm_offset_t va; int i; if (!do_minidump) { /* Initialize phys. segments for dumpsys(). */ memset(&dump_map, 0, sizeof(dump_map)); mem_regions(&pregions, &pregions_sz, ®ions, ®ions_sz); for (i = 0; i < pregions_sz; i++) { dump_map[i].pa_start = pregions[i].mr_start; dump_map[i].pa_size = pregions[i].mr_size; } return; } /* Virtual segments for minidumps: */ memset(&dump_map, 0, sizeof(dump_map)); /* 1st: kernel .data and .bss. */ dump_map[0].pa_start = trunc_page((uintptr_t)_etext); dump_map[0].pa_size = round_page((uintptr_t)_end) - dump_map[0].pa_start; /* 2nd: msgbuf and tables (see pmap_bootstrap()). */ dump_map[1].pa_start = (vm_paddr_t)msgbufp->msg_ptr; dump_map[1].pa_size = round_page(msgbufp->msg_size); /* 3rd: kernel VM. */ va = dump_map[1].pa_start + dump_map[1].pa_size; /* Find start of next chunk (from va). */ while (va < virtual_end) { /* Don't dump the buffer cache. */ if (va >= kmi.buffer_sva && va < kmi.buffer_eva) { va = kmi.buffer_eva; continue; } pvo = moea64_pvo_find_va(kernel_pmap, va & ~ADDR_POFF); if (pvo != NULL && !(pvo->pvo_vaddr & PVO_DEAD)) break; va += PAGE_SIZE; } if (va < virtual_end) { dump_map[2].pa_start = va; va += PAGE_SIZE; /* Find last page in chunk. */ while (va < virtual_end) { /* Don't run into the buffer cache. */ if (va == kmi.buffer_sva) break; pvo = moea64_pvo_find_va(kernel_pmap, va & ~ADDR_POFF); if (pvo != NULL && !(pvo->pvo_vaddr & PVO_DEAD)) break; va += PAGE_SIZE; } dump_map[2].pa_size = va - dump_map[2].pa_start; } } Index: head/sys/powerpc/ofw/ofw_machdep.c =================================================================== --- head/sys/powerpc/ofw/ofw_machdep.c (revision 290988) +++ head/sys/powerpc/ofw/ofw_machdep.c (revision 290989) @@ -1,699 +1,699 @@ /*- * Copyright (C) 1996 Wolfgang Solfrank. * Copyright (C) 1996 TooLs GmbH. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $NetBSD: ofw_machdep.c,v 1.5 2000/05/23 13:25:43 tsubai Exp $ */ #include __FBSDID("$FreeBSD$"); #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void *fdt; int ofw_real_mode; #ifdef AIM extern register_t ofmsr[5]; extern void *openfirmware_entry; char save_trap_init[0x2f00]; /* EXC_LAST */ char save_trap_of[0x2f00]; /* EXC_LAST */ int ofwcall(void *); static int openfirmware(void *args); __inline void ofw_save_trap_vec(char *save_trap_vec) { if (!ofw_real_mode) return; bcopy((void *)EXC_RST, save_trap_vec, EXC_LAST - EXC_RST); } static __inline void ofw_restore_trap_vec(char *restore_trap_vec) { if (!ofw_real_mode) return; bcopy(restore_trap_vec, (void *)EXC_RST, EXC_LAST - EXC_RST); __syncicache(EXC_RSVD, EXC_LAST - EXC_RSVD); } /* * Saved SPRG0-3 from OpenFirmware. Will be restored prior to the callback. */ register_t ofw_sprg0_save; static __inline void ofw_sprg_prepare(void) { if (ofw_real_mode) return; /* * Assume that interrupt are disabled at this point, or * SPRG1-3 could be trashed */ __asm __volatile("mfsprg0 %0\n\t" "mtsprg0 %1\n\t" "mtsprg1 %2\n\t" "mtsprg2 %3\n\t" "mtsprg3 %4\n\t" : "=&r"(ofw_sprg0_save) : "r"(ofmsr[1]), "r"(ofmsr[2]), "r"(ofmsr[3]), "r"(ofmsr[4])); } static __inline void ofw_sprg_restore(void) { if (ofw_real_mode) return; /* * Note that SPRG1-3 contents are irrelevant. They are scratch * registers used in the early portion of trap handling when * interrupts are disabled. * * PCPU data cannot be used until this routine is called ! */ __asm __volatile("mtsprg0 %0" :: "r"(ofw_sprg0_save)); } #endif static int parse_ofw_memory(phandle_t node, const char *prop, struct mem_region *output) { cell_t address_cells, size_cells; cell_t OFmem[4 * PHYS_AVAIL_SZ]; int sz, i, j; phandle_t phandle; sz = 0; /* * Get #address-cells from root node, defaulting to 1 if it cannot * be found. */ phandle = OF_finddevice("/"); - if (OF_getprop(phandle, "#address-cells", &address_cells, + if (OF_getencprop(phandle, "#address-cells", &address_cells, sizeof(address_cells)) < (ssize_t)sizeof(address_cells)) address_cells = 1; - if (OF_getprop(phandle, "#size-cells", &size_cells, + if (OF_getencprop(phandle, "#size-cells", &size_cells, sizeof(size_cells)) < (ssize_t)sizeof(size_cells)) size_cells = 1; /* * Get memory. */ - if (node == -1 || (sz = OF_getprop(node, prop, + if (node == -1 || (sz = OF_getencprop(node, prop, OFmem, sizeof(OFmem))) <= 0) panic("Physical memory map not found"); i = 0; j = 0; while (i < sz/sizeof(cell_t)) { #ifndef __powerpc64__ /* On 32-bit PPC, ignore regions starting above 4 GB */ if (address_cells > 1 && OFmem[i] > 0) { i += address_cells + size_cells; continue; } #endif output[j].mr_start = OFmem[i++]; if (address_cells == 2) { #ifdef __powerpc64__ output[j].mr_start <<= 32; #endif output[j].mr_start += OFmem[i++]; } output[j].mr_size = OFmem[i++]; if (size_cells == 2) { #ifdef __powerpc64__ output[j].mr_size <<= 32; #endif output[j].mr_size += OFmem[i++]; } #ifndef __powerpc64__ /* * Check for memory regions extending above 32-bit * memory space, and restrict them to stay there. */ if (((uint64_t)output[j].mr_start + (uint64_t)output[j].mr_size) > BUS_SPACE_MAXADDR_32BIT) { output[j].mr_size = BUS_SPACE_MAXADDR_32BIT - output[j].mr_start; } #endif j++; } sz = j*sizeof(output[0]); return (sz); } static int excise_fdt_reserved(struct mem_region *avail, int asz) { struct { uint64_t address; uint64_t size; } fdtmap[16]; ssize_t fdtmapsize; phandle_t chosen; int i, j, k; chosen = OF_finddevice("/chosen"); fdtmapsize = OF_getprop(chosen, "fdtmemreserv", fdtmap, sizeof(fdtmap)); for (j = 0; j < fdtmapsize/sizeof(fdtmap[0]); j++) { fdtmap[j].address = be64toh(fdtmap[j].address); fdtmap[j].size = be64toh(fdtmap[j].size); } for (i = 0; i < asz; i++) { for (j = 0; j < fdtmapsize/sizeof(fdtmap[0]); j++) { /* * Case 1: Exclusion region encloses complete * available entry. Drop it and move on. */ if (fdtmap[j].address <= avail[i].mr_start && fdtmap[j].address + fdtmap[j].size >= avail[i].mr_start + avail[i].mr_size) { for (k = i+1; k < asz; k++) avail[k-1] = avail[k]; asz--; i--; /* Repeat some entries */ continue; } /* * Case 2: Exclusion region starts in available entry. * Trim it to where the entry begins and append * a new available entry with the region after * the excluded region, if any. */ if (fdtmap[j].address >= avail[i].mr_start && fdtmap[j].address < avail[i].mr_start + avail[i].mr_size) { if (fdtmap[j].address + fdtmap[j].size < avail[i].mr_start + avail[i].mr_size) { avail[asz].mr_start = fdtmap[j].address + fdtmap[j].size; avail[asz].mr_size = avail[i].mr_start + avail[i].mr_size - avail[asz].mr_start; asz++; } avail[i].mr_size = fdtmap[j].address - avail[i].mr_start; } /* * Case 3: Exclusion region ends in available entry. * Move start point to where the exclusion zone ends. * The case of a contained exclusion zone has already * been caught in case 2. */ if (fdtmap[j].address + fdtmap[j].size >= avail[i].mr_start && fdtmap[j].address + fdtmap[j].size < avail[i].mr_start + avail[i].mr_size) { avail[i].mr_size += avail[i].mr_start; avail[i].mr_start = fdtmap[j].address + fdtmap[j].size; avail[i].mr_size -= avail[i].mr_start; } } } return (asz); } /* * This is called during powerpc_init, before the system is really initialized. * It shall provide the total and the available regions of RAM. * The available regions need not take the kernel into account. */ void ofw_mem_regions(struct mem_region *memp, int *memsz, struct mem_region *availp, int *availsz) { phandle_t phandle; int asz, msz; int res; char name[31]; asz = msz = 0; /* * Get memory from all the /memory nodes. */ for (phandle = OF_child(OF_peer(0)); phandle != 0; phandle = OF_peer(phandle)) { if (OF_getprop(phandle, "name", name, sizeof(name)) <= 0) continue; if (strncmp(name, "memory", sizeof(name)) != 0 && strncmp(name, "memory@", strlen("memory@")) != 0) continue; res = parse_ofw_memory(phandle, "reg", &memp[msz]); msz += res/sizeof(struct mem_region); if (OF_getproplen(phandle, "available") >= 0) res = parse_ofw_memory(phandle, "available", &availp[asz]); else res = parse_ofw_memory(phandle, "reg", &availp[asz]); asz += res/sizeof(struct mem_region); } phandle = OF_finddevice("/chosen"); if (OF_hasprop(phandle, "fdtmemreserv")) asz = excise_fdt_reserved(availp, asz); *memsz = msz; *availsz = asz; } void OF_initial_setup(void *fdt_ptr, void *junk, int (*openfirm)(void *)) { #ifdef AIM ofmsr[0] = mfmsr(); #ifdef __powerpc64__ ofmsr[0] &= ~PSL_SF; #endif __asm __volatile("mfsprg0 %0" : "=&r"(ofmsr[1])); __asm __volatile("mfsprg1 %0" : "=&r"(ofmsr[2])); __asm __volatile("mfsprg2 %0" : "=&r"(ofmsr[3])); __asm __volatile("mfsprg3 %0" : "=&r"(ofmsr[4])); openfirmware_entry = openfirm; if (ofmsr[0] & PSL_DR) ofw_real_mode = 0; else ofw_real_mode = 1; ofw_save_trap_vec(save_trap_init); #else ofw_real_mode = 1; #endif fdt = fdt_ptr; #ifdef FDT_DTB_STATIC /* Check for a statically included blob */ if (fdt == NULL) fdt = &fdt_static_dtb; #endif } boolean_t OF_bootstrap() { boolean_t status = FALSE; #ifdef AIM if (openfirmware_entry != NULL) { if (ofw_real_mode) { status = OF_install(OFW_STD_REAL, 0); } else { #ifdef __powerpc64__ status = OF_install(OFW_STD_32BIT, 0); #else status = OF_install(OFW_STD_DIRECT, 0); #endif } if (status != TRUE) return status; OF_init(openfirmware); } else #endif if (fdt != NULL) { status = OF_install(OFW_FDT, 0); if (status != TRUE) return status; OF_init(fdt); OF_interpret("perform-fixup", 0); } return (status); } #ifdef AIM void ofw_quiesce(void) { struct { cell_t name; cell_t nargs; cell_t nreturns; } args; KASSERT(!pmap_bootstrapped, ("Cannot call ofw_quiesce after VM is up")); args.name = (cell_t)(uintptr_t)"quiesce"; args.nargs = 0; args.nreturns = 0; openfirmware(&args); } static int openfirmware_core(void *args) { int result; register_t oldmsr; if (openfirmware_entry == NULL) return (-1); /* * Turn off exceptions - we really don't want to end up * anywhere unexpected with PCPU set to something strange * or the stack pointer wrong. */ oldmsr = intr_disable(); ofw_sprg_prepare(); /* Save trap vectors */ ofw_save_trap_vec(save_trap_of); /* Restore initially saved trap vectors */ ofw_restore_trap_vec(save_trap_init); #if defined(AIM) && !defined(__powerpc64__) /* * Clear battable[] translations */ if (!(cpu_features & PPC_FEATURE_64)) __asm __volatile("mtdbatu 2, %0\n" "mtdbatu 3, %0" : : "r" (0)); isync(); #endif result = ofwcall(args); /* Restore trap vecotrs */ ofw_restore_trap_vec(save_trap_of); ofw_sprg_restore(); intr_restore(oldmsr); return (result); } #ifdef SMP struct ofw_rv_args { void *args; int retval; volatile int in_progress; }; static void ofw_rendezvous_dispatch(void *xargs) { struct ofw_rv_args *rv_args = xargs; /* NOTE: Interrupts are disabled here */ if (PCPU_GET(cpuid) == 0) { /* * Execute all OF calls on CPU 0 */ rv_args->retval = openfirmware_core(rv_args->args); rv_args->in_progress = 0; } else { /* * Spin with interrupts off on other CPUs while OF has * control of the machine. */ while (rv_args->in_progress) cpu_spinwait(); } } #endif static int openfirmware(void *args) { int result; #ifdef SMP struct ofw_rv_args rv_args; #endif if (openfirmware_entry == NULL) return (-1); #ifdef SMP rv_args.args = args; rv_args.in_progress = 1; smp_rendezvous(smp_no_rendevous_barrier, ofw_rendezvous_dispatch, smp_no_rendevous_barrier, &rv_args); result = rv_args.retval; #else result = openfirmware_core(args); #endif return (result); } void OF_reboot() { struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t arg; } args; args.name = (cell_t)(uintptr_t)"interpret"; args.nargs = 1; args.nreturns = 0; args.arg = (cell_t)(uintptr_t)"reset-all"; openfirmware_core(&args); /* Don't do rendezvous! */ for (;;); /* just in case */ } #endif /* AIM */ void OF_getetheraddr(device_t dev, u_char *addr) { phandle_t node; node = ofw_bus_get_node(dev); OF_getprop(node, "local-mac-address", addr, ETHER_ADDR_LEN); } /* * Return a bus handle and bus tag that corresponds to the register * numbered regno for the device referenced by the package handle * dev. This function is intended to be used by console drivers in * early boot only. It works by mapping the address of the device's * register in the address space of its parent and recursively walk * the device tree upward this way. */ static void OF_get_addr_props(phandle_t node, uint32_t *addrp, uint32_t *sizep, int *pcip) { char type[64]; uint32_t addr, size; int pci, res; - res = OF_getprop(node, "#address-cells", &addr, sizeof(addr)); + res = OF_getencprop(node, "#address-cells", &addr, sizeof(addr)); if (res == -1) addr = 2; - res = OF_getprop(node, "#size-cells", &size, sizeof(size)); + res = OF_getencprop(node, "#size-cells", &size, sizeof(size)); if (res == -1) size = 1; pci = 0; if (addr == 3 && size == 2) { res = OF_getprop(node, "device_type", type, sizeof(type)); if (res != -1) { type[sizeof(type) - 1] = '\0'; pci = (strcmp(type, "pci") == 0) ? 1 : 0; } } if (addrp != NULL) *addrp = addr; if (sizep != NULL) *sizep = size; if (pcip != NULL) *pcip = pci; } int OF_decode_addr(phandle_t dev, int regno, bus_space_tag_t *tag, bus_space_handle_t *handle) { uint32_t cell[32]; bus_addr_t addr, raddr, baddr; bus_size_t size, rsize; uint32_t c, nbridge, naddr, nsize; phandle_t bridge, parent; u_int spc, rspc, prefetch; int pci, pcib, res; /* Sanity checking. */ if (dev == 0) return (EINVAL); bridge = OF_parent(dev); if (bridge == 0) return (EINVAL); if (regno < 0) return (EINVAL); if (tag == NULL || handle == NULL) return (EINVAL); /* Assume big-endian unless we find a PCI device */ *tag = &bs_be_tag; /* Get the requested register. */ OF_get_addr_props(bridge, &naddr, &nsize, &pci); if (pci) *tag = &bs_le_tag; - res = OF_getprop(dev, (pci) ? "assigned-addresses" : "reg", + res = OF_getencprop(dev, (pci) ? "assigned-addresses" : "reg", cell, sizeof(cell)); if (res == -1) return (ENXIO); if (res % sizeof(cell[0])) return (ENXIO); res /= sizeof(cell[0]); regno *= naddr + nsize; if (regno + naddr + nsize > res) return (EINVAL); spc = (pci) ? cell[regno] & OFW_PCI_PHYS_HI_SPACEMASK : ~0; prefetch = (pci) ? cell[regno] & OFW_PCI_PHYS_HI_PREFETCHABLE : 0; addr = 0; for (c = 0; c < naddr; c++) addr = ((uint64_t)addr << 32) | cell[regno++]; size = 0; for (c = 0; c < nsize; c++) size = ((uint64_t)size << 32) | cell[regno++]; /* * Map the address range in the bridge's decoding window as given * by the "ranges" property. If a node doesn't have such property * then no mapping is done. */ parent = OF_parent(bridge); while (parent != 0) { OF_get_addr_props(parent, &nbridge, NULL, &pcib); if (pcib) *tag = &bs_le_tag; - res = OF_getprop(bridge, "ranges", cell, sizeof(cell)); + res = OF_getencprop(bridge, "ranges", cell, sizeof(cell)); if (res == -1) goto next; if (res % sizeof(cell[0])) return (ENXIO); res /= sizeof(cell[0]); regno = 0; while (regno < res) { rspc = (pci) ? cell[regno] & OFW_PCI_PHYS_HI_SPACEMASK : ~0; if (rspc != spc) { regno += naddr + nbridge + nsize; continue; } raddr = 0; for (c = 0; c < naddr; c++) raddr = ((uint64_t)raddr << 32) | cell[regno++]; rspc = (pcib) ? cell[regno] & OFW_PCI_PHYS_HI_SPACEMASK : ~0; baddr = 0; for (c = 0; c < nbridge; c++) baddr = ((uint64_t)baddr << 32) | cell[regno++]; rsize = 0; for (c = 0; c < nsize; c++) rsize = ((uint64_t)rsize << 32) | cell[regno++]; if (addr < raddr || addr >= raddr + rsize) continue; addr = addr - raddr + baddr; if (rspc != ~0) spc = rspc; } next: bridge = parent; parent = OF_parent(bridge); OF_get_addr_props(bridge, &naddr, &nsize, &pci); } return (bus_space_map(*tag, addr, size, prefetch ? BUS_SPACE_MAP_PREFETCHABLE : 0, handle)); } Index: head/sys/powerpc/ofw/ofw_pci.c =================================================================== --- head/sys/powerpc/ofw/ofw_pci.c (revision 290988) +++ head/sys/powerpc/ofw/ofw_pci.c (revision 290989) @@ -1,561 +1,562 @@ /*- * Copyright (c) 2011 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pcib_if.h" /* * Bus interface. */ static int ofw_pci_read_ivar(device_t, device_t, int, uintptr_t *); static struct resource * ofw_pci_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags); static int ofw_pci_release_resource(device_t bus, device_t child, int type, int rid, struct resource *res); static int ofw_pci_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *res); static int ofw_pci_deactivate_resource(device_t bus, device_t child, int type, int rid, struct resource *res); static int ofw_pci_adjust_resource(device_t bus, device_t child, int type, struct resource *res, u_long start, u_long end); /* * pcib interface. */ static int ofw_pci_maxslots(device_t); static int ofw_pci_route_interrupt(device_t, device_t, int); /* * ofw_bus interface */ static phandle_t ofw_pci_get_node(device_t bus, device_t dev); /* * local methods */ static int ofw_pci_nranges(phandle_t node); static int ofw_pci_fill_ranges(phandle_t node, struct ofw_pci_range *ranges); /* * Driver methods. */ static device_method_t ofw_pci_methods[] = { /* Device interface */ DEVMETHOD(device_attach, ofw_pci_attach), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_read_ivar, ofw_pci_read_ivar), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD(bus_alloc_resource, ofw_pci_alloc_resource), DEVMETHOD(bus_release_resource, ofw_pci_release_resource), DEVMETHOD(bus_activate_resource, ofw_pci_activate_resource), DEVMETHOD(bus_deactivate_resource, ofw_pci_deactivate_resource), DEVMETHOD(bus_adjust_resource, ofw_pci_adjust_resource), /* pcib interface */ DEVMETHOD(pcib_maxslots, ofw_pci_maxslots), DEVMETHOD(pcib_route_interrupt, ofw_pci_route_interrupt), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, ofw_pci_get_node), DEVMETHOD_END }; DEFINE_CLASS_0(ofw_pci, ofw_pci_driver, ofw_pci_methods, 0); int ofw_pci_init(device_t dev) { struct ofw_pci_softc *sc; phandle_t node; u_int32_t busrange[2]; struct ofw_pci_range *rp; int error; node = ofw_bus_get_node(dev); sc = device_get_softc(dev); sc->sc_initialized = 1; - if (OF_getprop(node, "reg", &sc->sc_pcir, sizeof(sc->sc_pcir)) == -1) + if (OF_getencprop(node, "reg", (pcell_t *)&sc->sc_pcir, + sizeof(sc->sc_pcir)) == -1) return (ENXIO); - if (OF_getprop(node, "bus-range", busrange, sizeof(busrange)) != 8) + if (OF_getencprop(node, "bus-range", busrange, sizeof(busrange)) != 8) busrange[0] = 0; sc->sc_dev = dev; sc->sc_node = node; sc->sc_bus = busrange[0]; if (sc->sc_quirks & OFW_PCI_QUIRK_RANGES_ON_CHILDREN) { phandle_t c; int n, i; sc->sc_nrange = 0; for (c = OF_child(node); c != 0; c = OF_peer(c)) { n = ofw_pci_nranges(c); if (n > 0) sc->sc_nrange += n; } if (sc->sc_nrange == 0) return (ENXIO); sc->sc_range = malloc(sc->sc_nrange * sizeof(sc->sc_range[0]), M_DEVBUF, M_WAITOK); i = 0; for (c = OF_child(node); c != 0; c = OF_peer(c)) { n = ofw_pci_fill_ranges(c, &sc->sc_range[i]); if (n > 0) i += n; } KASSERT(i == sc->sc_nrange, ("range count mismatch")); } else { sc->sc_nrange = ofw_pci_nranges(node); if (sc->sc_nrange <= 0) { device_printf(dev, "could not get ranges\n"); return (ENXIO); } sc->sc_range = malloc(sc->sc_nrange * sizeof(sc->sc_range[0]), M_DEVBUF, M_WAITOK); ofw_pci_fill_ranges(node, sc->sc_range); } sc->sc_io_rman.rm_type = RMAN_ARRAY; sc->sc_io_rman.rm_descr = "PCI I/O Ports"; error = rman_init(&sc->sc_io_rman); if (error) { device_printf(dev, "rman_init() failed. error = %d\n", error); return (error); } sc->sc_mem_rman.rm_type = RMAN_ARRAY; sc->sc_mem_rman.rm_descr = "PCI Memory"; error = rman_init(&sc->sc_mem_rman); if (error) { device_printf(dev, "rman_init() failed. error = %d\n", error); return (error); } for (rp = sc->sc_range; rp < sc->sc_range + sc->sc_nrange && rp->pci_hi != 0; rp++) { error = 0; switch (rp->pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) { case OFW_PCI_PHYS_HI_SPACE_CONFIG: break; case OFW_PCI_PHYS_HI_SPACE_IO: error = rman_manage_region(&sc->sc_io_rman, rp->pci, rp->pci + rp->size - 1); break; case OFW_PCI_PHYS_HI_SPACE_MEM32: case OFW_PCI_PHYS_HI_SPACE_MEM64: error = rman_manage_region(&sc->sc_mem_rman, rp->pci, rp->pci + rp->size - 1); break; } if (error) { device_printf(dev, "rman_manage_region(%x, %#jx, %#jx) failed. " "error = %d\n", rp->pci_hi & OFW_PCI_PHYS_HI_SPACEMASK, rp->pci, rp->pci + rp->size - 1, error); return (error); } } ofw_bus_setup_iinfo(node, &sc->sc_pci_iinfo, sizeof(cell_t)); return (error); } int ofw_pci_attach(device_t dev) { struct ofw_pci_softc *sc; int error; sc = device_get_softc(dev); if (!sc->sc_initialized) { error = ofw_pci_init(dev); if (error) return (error); } device_add_child(dev, "pci", -1); return (bus_generic_attach(dev)); } static int ofw_pci_maxslots(device_t dev) { return (PCI_SLOTMAX); } static int ofw_pci_route_interrupt(device_t bus, device_t dev, int pin) { struct ofw_pci_softc *sc; struct ofw_pci_register reg; uint32_t pintr, mintr[2]; int intrcells; phandle_t iparent; sc = device_get_softc(bus); pintr = pin; /* Fabricate imap information in case this isn't an OFW device */ bzero(®, sizeof(reg)); reg.phys_hi = (pci_get_bus(dev) << OFW_PCI_PHYS_HI_BUSSHIFT) | (pci_get_slot(dev) << OFW_PCI_PHYS_HI_DEVICESHIFT) | (pci_get_function(dev) << OFW_PCI_PHYS_HI_FUNCTIONSHIFT); intrcells = ofw_bus_lookup_imap(ofw_bus_get_node(dev), &sc->sc_pci_iinfo, ®, sizeof(reg), &pintr, sizeof(pintr), mintr, sizeof(mintr), &iparent); if (intrcells) { pintr = ofw_bus_map_intr(dev, iparent, intrcells, mintr); return (pintr); } /* Maybe it's a real interrupt, not an intpin */ if (pin > 4) return (pin); device_printf(bus, "could not route pin %d for device %d.%d\n", pin, pci_get_slot(dev), pci_get_function(dev)); return (PCI_INVALID_IRQ); } static int ofw_pci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) { struct ofw_pci_softc *sc; sc = device_get_softc(dev); switch (which) { case PCIB_IVAR_DOMAIN: *result = device_get_unit(dev); return (0); case PCIB_IVAR_BUS: *result = sc->sc_bus; return (0); } return (ENOENT); } static struct resource * ofw_pci_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { struct ofw_pci_softc *sc; struct resource *rv; struct rman *rm; int needactivate; needactivate = flags & RF_ACTIVE; flags &= ~RF_ACTIVE; sc = device_get_softc(bus); switch (type) { case SYS_RES_MEMORY: rm = &sc->sc_mem_rman; break; case SYS_RES_IOPORT: rm = &sc->sc_io_rman; break; case SYS_RES_IRQ: return (bus_alloc_resource(bus, type, rid, start, end, count, flags)); default: device_printf(bus, "unknown resource request from %s\n", device_get_nameunit(child)); return (NULL); } rv = rman_reserve_resource(rm, start, end, count, flags, child); if (rv == NULL) { device_printf(bus, "failed to reserve resource for %s\n", device_get_nameunit(child)); return (NULL); } rman_set_rid(rv, *rid); if (needactivate) { if (bus_activate_resource(child, type, *rid, rv) != 0) { device_printf(bus, "failed to activate resource for %s\n", device_get_nameunit(child)); rman_release_resource(rv); return (NULL); } } return (rv); } static int ofw_pci_release_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { if (rman_get_flags(res) & RF_ACTIVE) { int error = bus_deactivate_resource(child, type, rid, res); if (error) return error; } return (rman_release_resource(res)); } static int ofw_pci_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { struct ofw_pci_softc *sc; void *p; sc = device_get_softc(bus); if (type == SYS_RES_IRQ) { return (bus_activate_resource(bus, type, rid, res)); } if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) { struct ofw_pci_range *rp; vm_offset_t start; int space; start = (vm_offset_t)rman_get_start(res); /* * Map this through the ranges list */ for (rp = sc->sc_range; rp < sc->sc_range + sc->sc_nrange && rp->pci_hi != 0; rp++) { if (start < rp->pci || start >= rp->pci + rp->size) continue; switch (rp->pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) { case OFW_PCI_PHYS_HI_SPACE_IO: space = SYS_RES_IOPORT; break; case OFW_PCI_PHYS_HI_SPACE_MEM32: case OFW_PCI_PHYS_HI_SPACE_MEM64: space = SYS_RES_MEMORY; break; default: space = -1; } if (type == space) { start += (rp->host - rp->pci); break; } } if (bootverbose) printf("ofw_pci mapdev: start %zx, len %ld\n", start, rman_get_size(res)); p = pmap_mapdev(start, (vm_size_t)rman_get_size(res)); if (p == NULL) return (ENOMEM); rman_set_virtual(res, p); rman_set_bustag(res, &bs_le_tag); rman_set_bushandle(res, (u_long)p); } return (rman_activate_resource(res)); } static int ofw_pci_deactivate_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { /* * If this is a memory resource, unmap it. */ if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) { u_int32_t psize; psize = rman_get_size(res); pmap_unmapdev((vm_offset_t)rman_get_virtual(res), psize); } return (rman_deactivate_resource(res)); } static int ofw_pci_adjust_resource(device_t bus, device_t child, int type, struct resource *res, u_long start, u_long end) { struct rman *rm = NULL; struct ofw_pci_softc *sc = device_get_softc(bus); KASSERT(!(rman_get_flags(res) & RF_ACTIVE), ("active resources cannot be adjusted")); if (rman_get_flags(res) & RF_ACTIVE) return (EINVAL); switch (type) { case SYS_RES_MEMORY: rm = &sc->sc_mem_rman; break; case SYS_RES_IOPORT: rm = &sc->sc_io_rman; break; default: return (ENXIO); } if (!rman_is_region_manager(res, rm)) return (EINVAL); return (rman_adjust_resource(res, start, end)); } static phandle_t ofw_pci_get_node(device_t bus, device_t dev) { struct ofw_pci_softc *sc; sc = device_get_softc(bus); /* We only have one child, the PCI bus, which needs our own node. */ return (sc->sc_node); } static int ofw_pci_nranges(phandle_t node) { int host_address_cells = 1, pci_address_cells = 3, size_cells = 2; ssize_t nbase_ranges; - OF_getprop(OF_parent(node), "#address-cells", &host_address_cells, + OF_getencprop(OF_parent(node), "#address-cells", &host_address_cells, sizeof(host_address_cells)); - OF_getprop(node, "#address-cells", &pci_address_cells, + OF_getencprop(node, "#address-cells", &pci_address_cells, sizeof(pci_address_cells)); - OF_getprop(node, "#size-cells", &size_cells, sizeof(size_cells)); + OF_getencprop(node, "#size-cells", &size_cells, sizeof(size_cells)); nbase_ranges = OF_getproplen(node, "ranges"); if (nbase_ranges <= 0) return (-1); return (nbase_ranges / sizeof(cell_t) / (pci_address_cells + host_address_cells + size_cells)); } static int ofw_pci_fill_ranges(phandle_t node, struct ofw_pci_range *ranges) { int host_address_cells = 1, pci_address_cells = 3, size_cells = 2; cell_t *base_ranges; ssize_t nbase_ranges; int nranges; int i, j, k; - OF_getprop(OF_parent(node), "#address-cells", &host_address_cells, + OF_getencprop(OF_parent(node), "#address-cells", &host_address_cells, sizeof(host_address_cells)); - OF_getprop(node, "#address-cells", &pci_address_cells, + OF_getencprop(node, "#address-cells", &pci_address_cells, sizeof(pci_address_cells)); - OF_getprop(node, "#size-cells", &size_cells, sizeof(size_cells)); + OF_getencprop(node, "#size-cells", &size_cells, sizeof(size_cells)); nbase_ranges = OF_getproplen(node, "ranges"); if (nbase_ranges <= 0) return (-1); nranges = nbase_ranges / sizeof(cell_t) / (pci_address_cells + host_address_cells + size_cells); base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK); - OF_getprop(node, "ranges", base_ranges, nbase_ranges); + OF_getencprop(node, "ranges", base_ranges, nbase_ranges); for (i = 0, j = 0; i < nranges; i++) { ranges[i].pci_hi = base_ranges[j++]; ranges[i].pci = 0; for (k = 0; k < pci_address_cells - 1; k++) { ranges[i].pci <<= 32; ranges[i].pci |= base_ranges[j++]; } ranges[i].host = 0; for (k = 0; k < host_address_cells; k++) { ranges[i].host <<= 32; ranges[i].host |= base_ranges[j++]; } ranges[i].size = 0; for (k = 0; k < size_cells; k++) { ranges[i].size <<= 32; ranges[i].size |= base_ranges[j++]; } } free(base_ranges, M_DEVBUF); return (nranges); } Index: head/sys/powerpc/ofw/ofw_pcibus.c =================================================================== --- head/sys/powerpc/ofw/ofw_pcibus.c (revision 290988) +++ head/sys/powerpc/ofw/ofw_pcibus.c (revision 290989) @@ -1,355 +1,357 @@ /*- * Copyright (c) 1997, Stefan Esser * Copyright (c) 2000, Michael Smith * Copyright (c) 2000, BSDi * Copyright (c) 2003, Thomas Moestl * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ofw_pcibus.h" #include "pcib_if.h" #include "pci_if.h" typedef uint32_t ofw_pci_intr_t; /* Methods */ static device_probe_t ofw_pcibus_probe; static device_attach_t ofw_pcibus_attach; static pci_assign_interrupt_t ofw_pcibus_assign_interrupt; static ofw_bus_get_devinfo_t ofw_pcibus_get_devinfo; static int ofw_pcibus_child_pnpinfo_str_method(device_t cbdev, device_t child, char *buf, size_t buflen); static void ofw_pcibus_enum_devtree(device_t dev, u_int domain, u_int busno); static void ofw_pcibus_enum_bus(device_t dev, u_int domain, u_int busno); static device_method_t ofw_pcibus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ofw_pcibus_probe), DEVMETHOD(device_attach, ofw_pcibus_attach), /* Bus interface */ DEVMETHOD(bus_child_pnpinfo_str, ofw_pcibus_child_pnpinfo_str_method), /* PCI interface */ DEVMETHOD(pci_assign_interrupt, ofw_pcibus_assign_interrupt), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_devinfo, ofw_pcibus_get_devinfo), DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), DEVMETHOD_END }; static devclass_t pci_devclass; DEFINE_CLASS_1(pci, ofw_pcibus_driver, ofw_pcibus_methods, sizeof(struct pci_softc), pci_driver); DRIVER_MODULE(ofw_pcibus, pcib, ofw_pcibus_driver, pci_devclass, 0, 0); MODULE_VERSION(ofw_pcibus, 1); MODULE_DEPEND(ofw_pcibus, pci, 1, 1, 1); static int ofw_devices_only = 0; TUNABLE_INT("hw.pci.ofw_devices_only", &ofw_devices_only); static int ofw_pcibus_probe(device_t dev) { if (ofw_bus_get_node(dev) == -1) return (ENXIO); device_set_desc(dev, "OFW PCI bus"); return (BUS_PROBE_DEFAULT); } static int ofw_pcibus_attach(device_t dev) { u_int busno, domain; int error; error = pci_attach_common(dev); if (error) return (error); domain = pcib_get_domain(dev); busno = pcib_get_bus(dev); /* * Attach those children represented in the device tree. */ ofw_pcibus_enum_devtree(dev, domain, busno); /* * We now attach any laggard devices. FDT, for instance, allows * the device tree to enumerate only some PCI devices. Apple's * OF device tree on some Grackle-based hardware can also miss * functions on multi-function cards. */ if (!ofw_devices_only) ofw_pcibus_enum_bus(dev, domain, busno); return (bus_generic_attach(dev)); } static void ofw_pcibus_enum_devtree(device_t dev, u_int domain, u_int busno) { device_t pcib; struct ofw_pci_register pcir; struct ofw_pcibus_devinfo *dinfo; phandle_t node, child; u_int func, slot; int intline; pcib = device_get_parent(dev); node = ofw_bus_get_node(dev); for (child = OF_child(node); child != 0; child = OF_peer(child)) { - if (OF_getprop(child, "reg", &pcir, sizeof(pcir)) == -1) + if (OF_getencprop(child, "reg", (pcell_t *)&pcir, + sizeof(pcir)) == -1) continue; slot = OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi); func = OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi); /* Some OFW device trees contain dupes. */ if (pci_find_dbsf(domain, busno, slot, func) != NULL) continue; /* * The preset in the intline register is usually bogus. Reset * it such that the PCI code will reroute the interrupt if * needed. */ intline = PCI_INVALID_IRQ; if (OF_getproplen(child, "interrupts") > 0) intline = 0; PCIB_WRITE_CONFIG(pcib, busno, slot, func, PCIR_INTLINE, intline, 1); /* * Now set up the PCI and OFW bus layer devinfo and add it * to the PCI bus. */ dinfo = (struct ofw_pcibus_devinfo *)pci_read_device(pcib, domain, busno, slot, func, sizeof(*dinfo)); if (dinfo == NULL) continue; if (ofw_bus_gen_setup_devinfo(&dinfo->opd_obdinfo, child) != 0) { pci_freecfg((struct pci_devinfo *)dinfo); continue; } dinfo->opd_dma_tag = NULL; pci_add_child(dev, (struct pci_devinfo *)dinfo); /* * Some devices don't have an intpin set, but do have * interrupts. These are fully specified, and set in the * interrupts property, so add that value to the device's * resource list. */ if (dinfo->opd_dinfo.cfg.intpin == 0) ofw_bus_intr_to_rl(dev, child, &dinfo->opd_dinfo.resources, NULL); } } /* * The following is an almost exact clone of pci_add_children(), with the * addition that it (a) will not add children that have already been added, * and (b) will set up the OFW devinfo to point to invalid values. This is * to handle non-enumerated PCI children as exist in FDT and on the second * function of the Rage 128 in my Blue & White G3. */ static void ofw_pcibus_enum_bus(device_t dev, u_int domain, u_int busno) { device_t pcib; struct ofw_pcibus_devinfo *dinfo; int maxslots; int s, f, pcifunchigh; uint8_t hdrtype; pcib = device_get_parent(dev); maxslots = PCIB_MAXSLOTS(pcib); for (s = 0; s <= maxslots; s++) { pcifunchigh = 0; f = 0; DELAY(1); hdrtype = PCIB_READ_CONFIG(pcib, busno, s, f, PCIR_HDRTYPE, 1); if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE) continue; if (hdrtype & PCIM_MFDEV) pcifunchigh = PCI_FUNCMAX; for (f = 0; f <= pcifunchigh; f++) { /* Filter devices we have already added */ if (pci_find_dbsf(domain, busno, s, f) != NULL) continue; dinfo = (struct ofw_pcibus_devinfo *)pci_read_device( pcib, domain, busno, s, f, sizeof(*dinfo)); if (dinfo == NULL) continue; dinfo->opd_dma_tag = NULL; dinfo->opd_obdinfo.obd_node = -1; dinfo->opd_obdinfo.obd_name = NULL; dinfo->opd_obdinfo.obd_compat = NULL; dinfo->opd_obdinfo.obd_type = NULL; dinfo->opd_obdinfo.obd_model = NULL; /* * For non OFW-devices, don't believe 0 * for an interrupt. */ if (dinfo->opd_dinfo.cfg.intline == 0) { dinfo->opd_dinfo.cfg.intline = PCI_INVALID_IRQ; PCIB_WRITE_CONFIG(pcib, busno, s, f, PCIR_INTLINE, PCI_INVALID_IRQ, 1); } pci_add_child(dev, (struct pci_devinfo *)dinfo); } } } static int ofw_pcibus_child_pnpinfo_str_method(device_t cbdev, device_t child, char *buf, size_t buflen) { pci_child_pnpinfo_str_method(cbdev, child, buf, buflen); if (ofw_bus_get_node(child) != -1) { strlcat(buf, " ", buflen); /* Separate info */ ofw_bus_gen_child_pnpinfo_str(cbdev, child, buf, buflen); } return (0); } static int ofw_pcibus_assign_interrupt(device_t dev, device_t child) { ofw_pci_intr_t intr[2]; phandle_t node, iparent; int isz, icells; node = ofw_bus_get_node(child); if (node == -1) { /* Non-firmware enumerated child, use standard routing */ intr[0] = pci_get_intpin(child); return (PCIB_ROUTE_INTERRUPT(device_get_parent(dev), child, intr[0])); } /* * Try to determine the node's interrupt parent so we know which * PIC to use. */ iparent = -1; - if (OF_getprop(node, "interrupt-parent", &iparent, sizeof(iparent)) < 0) + if (OF_getencprop(node, "interrupt-parent", &iparent, + sizeof(iparent)) < 0) iparent = -1; icells = 1; if (iparent != -1) - OF_getprop(OF_node_from_xref(iparent), "#interrupt-cells", + OF_getencprop(OF_node_from_xref(iparent), "#interrupt-cells", &icells, sizeof(icells)); /* * Any AAPL,interrupts property gets priority and is * fully specified (i.e. does not need routing) */ - isz = OF_getprop(node, "AAPL,interrupts", intr, sizeof(intr)); + isz = OF_getencprop(node, "AAPL,interrupts", intr, sizeof(intr)); if (isz == sizeof(intr[0])*icells) return ((iparent == -1) ? intr[0] : ofw_bus_map_intr(dev, iparent, icells, intr)); - isz = OF_getprop(node, "interrupts", intr, sizeof(intr)); + isz = OF_getencprop(node, "interrupts", intr, sizeof(intr)); if (isz == sizeof(intr[0])*icells) { if (iparent != -1) intr[0] = ofw_bus_map_intr(dev, iparent, icells, intr); } else { /* No property: our best guess is the intpin. */ intr[0] = pci_get_intpin(child); } /* * If we got intr from a property, it may or may not be an intpin. * For on-board devices, it frequently is not, and is completely out * of the valid intpin range. For PCI slots, it hopefully is, * otherwise we will have trouble interfacing with non-OFW buses * such as cardbus. * Since we cannot tell which it is without violating layering, we * will always use the route_interrupt method, and treat exceptions * on the level they become apparent. */ return (PCIB_ROUTE_INTERRUPT(device_get_parent(dev), child, intr[0])); } static const struct ofw_bus_devinfo * ofw_pcibus_get_devinfo(device_t bus, device_t dev) { struct ofw_pcibus_devinfo *dinfo; dinfo = device_get_ivars(dev); return (&dinfo->opd_obdinfo); } Index: head/sys/powerpc/ofw/openpic_ofw.c =================================================================== --- head/sys/powerpc/ofw/openpic_ofw.c (revision 290988) +++ head/sys/powerpc/ofw/openpic_ofw.c (revision 290989) @@ -1,168 +1,168 @@ /*- * Copyright 2003 by Peter Grehan. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pic_if.h" /* * OFW interface */ static int openpic_ofw_probe(device_t); static int openpic_ofw_attach(device_t); static void openpic_ofw_translate_code(device_t, u_int irq, int code, enum intr_trigger *trig, enum intr_polarity *pol); static device_method_t openpic_ofw_methods[] = { /* Device interface */ DEVMETHOD(device_probe, openpic_ofw_probe), DEVMETHOD(device_attach, openpic_ofw_attach), DEVMETHOD(device_suspend, openpic_suspend), DEVMETHOD(device_resume, openpic_resume), /* PIC interface */ DEVMETHOD(pic_bind, openpic_bind), DEVMETHOD(pic_config, openpic_config), DEVMETHOD(pic_dispatch, openpic_dispatch), DEVMETHOD(pic_enable, openpic_enable), DEVMETHOD(pic_eoi, openpic_eoi), DEVMETHOD(pic_ipi, openpic_ipi), DEVMETHOD(pic_mask, openpic_mask), DEVMETHOD(pic_unmask, openpic_unmask), DEVMETHOD(pic_translate_code, openpic_ofw_translate_code), DEVMETHOD_END }; static driver_t openpic_ofw_driver = { "openpic", openpic_ofw_methods, sizeof(struct openpic_softc), }; DRIVER_MODULE(openpic, ofwbus, openpic_ofw_driver, openpic_devclass, 0, 0); DRIVER_MODULE(openpic, simplebus, openpic_ofw_driver, openpic_devclass, 0, 0); DRIVER_MODULE(openpic, macio, openpic_ofw_driver, openpic_devclass, 0, 0); static int openpic_ofw_probe(device_t dev) { const char *type = ofw_bus_get_type(dev); if (type == NULL) return (ENXIO); if (!ofw_bus_is_compatible(dev, "chrp,open-pic") && strcmp(type, "open-pic") != 0) return (ENXIO); /* * On some U4 systems, there is a phantom MPIC in the mac-io cell. * The uninorth driver will pick up the real PIC, so ignore it here. */ if (OF_finddevice("/u4") != (phandle_t)-1) return (ENXIO); device_set_desc(dev, OPENPIC_DEVSTR); return (0); } static int openpic_ofw_attach(device_t dev) { phandle_t xref, node; node = ofw_bus_get_node(dev); - if (OF_getprop(node, "phandle", &xref, sizeof(xref)) == -1 && - OF_getprop(node, "ibm,phandle", &xref, sizeof(xref)) == -1 && - OF_getprop(node, "linux,phandle", &xref, sizeof(xref)) == -1) + if (OF_getencprop(node, "phandle", &xref, sizeof(xref)) == -1 && + OF_getencprop(node, "ibm,phandle", &xref, sizeof(xref)) == -1 && + OF_getencprop(node, "linux,phandle", &xref, sizeof(xref)) == -1) xref = node; return (openpic_common_attach(dev, xref)); } static void openpic_ofw_translate_code(device_t dev, u_int irq, int code, enum intr_trigger *trig, enum intr_polarity *pol) { switch (code) { case 0: /* L to H edge */ *trig = INTR_TRIGGER_EDGE; *pol = INTR_POLARITY_HIGH; break; case 1: /* Active L level */ *trig = INTR_TRIGGER_LEVEL; *pol = INTR_POLARITY_LOW; break; case 2: /* Active H level */ *trig = INTR_TRIGGER_LEVEL; *pol = INTR_POLARITY_HIGH; break; case 3: /* H to L edge */ *trig = INTR_TRIGGER_EDGE; *pol = INTR_POLARITY_LOW; break; default: *trig = INTR_TRIGGER_CONFORM; *pol = INTR_POLARITY_CONFORM; } } Index: head/sys/powerpc/powermac/cpcht.c =================================================================== --- head/sys/powerpc/powermac/cpcht.c (revision 290988) +++ head/sys/powerpc/powermac/cpcht.c (revision 290989) @@ -1,735 +1,735 @@ /*- * Copyright (C) 2008-2010 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pcib_if.h" #include "pic_if.h" /* * IBM CPC9X5 Hypertransport Device interface. */ static int cpcht_probe(device_t); static int cpcht_attach(device_t); static void cpcht_configure_htbridge(device_t, phandle_t); /* * pcib interface. */ static u_int32_t cpcht_read_config(device_t, u_int, u_int, u_int, u_int, int); static void cpcht_write_config(device_t, u_int, u_int, u_int, u_int, u_int32_t, int); static int cpcht_route_interrupt(device_t, device_t, int); static int cpcht_alloc_msi(device_t dev, device_t child, int count, int maxcount, int *irqs); static int cpcht_release_msi(device_t dev, device_t child, int count, int *irqs); static int cpcht_alloc_msix(device_t dev, device_t child, int *irq); static int cpcht_release_msix(device_t dev, device_t child, int irq); static int cpcht_map_msi(device_t dev, device_t child, int irq, uint64_t *addr, uint32_t *data); /* * Driver methods. */ static device_method_t cpcht_methods[] = { /* Device interface */ DEVMETHOD(device_probe, cpcht_probe), DEVMETHOD(device_attach, cpcht_attach), /* pcib interface */ DEVMETHOD(pcib_read_config, cpcht_read_config), DEVMETHOD(pcib_write_config, cpcht_write_config), DEVMETHOD(pcib_route_interrupt, cpcht_route_interrupt), DEVMETHOD(pcib_alloc_msi, cpcht_alloc_msi), DEVMETHOD(pcib_release_msi, cpcht_release_msi), DEVMETHOD(pcib_alloc_msix, cpcht_alloc_msix), DEVMETHOD(pcib_release_msix, cpcht_release_msix), DEVMETHOD(pcib_map_msi, cpcht_map_msi), DEVMETHOD_END }; struct cpcht_irq { enum { IRQ_NONE, IRQ_HT, IRQ_MSI, IRQ_INTERNAL } irq_type; int ht_source; vm_offset_t ht_base; vm_offset_t apple_eoi; uint32_t eoi_data; int edge; }; static struct cpcht_irq *cpcht_irqmap = NULL; uint32_t cpcht_msipic = 0; struct cpcht_softc { struct ofw_pci_softc pci_sc; vm_offset_t sc_data; uint64_t sc_populated_slots; struct cpcht_irq htirq_map[128]; struct mtx htirq_mtx; }; static devclass_t cpcht_devclass; DEFINE_CLASS_1(pcib, cpcht_driver, cpcht_methods, sizeof(struct cpcht_softc), ofw_pci_driver); DRIVER_MODULE(cpcht, ofwbus, cpcht_driver, cpcht_devclass, 0, 0); #define CPCHT_IOPORT_BASE 0xf4000000UL /* Hardwired */ #define CPCHT_IOPORT_SIZE 0x00400000UL #define HTAPIC_REQUEST_EOI 0x20 #define HTAPIC_TRIGGER_LEVEL 0x02 #define HTAPIC_MASK 0x01 static int cpcht_probe(device_t dev) { const char *type, *compatible; type = ofw_bus_get_type(dev); compatible = ofw_bus_get_compat(dev); if (type == NULL || compatible == NULL) return (ENXIO); if (strcmp(type, "ht") != 0) return (ENXIO); if (strcmp(compatible, "u3-ht") != 0) return (ENXIO); device_set_desc(dev, "IBM CPC9X5 HyperTransport Tunnel"); return (0); } static int cpcht_attach(device_t dev) { struct cpcht_softc *sc; phandle_t node, child; u_int32_t reg[3]; int i; node = ofw_bus_get_node(dev); sc = device_get_softc(dev); - if (OF_getprop(node, "reg", reg, sizeof(reg)) < 12) + if (OF_getencprop(node, "reg", reg, sizeof(reg)) < 12) return (ENXIO); if (OF_getproplen(node, "ranges") <= 0) sc->pci_sc.sc_quirks = OFW_PCI_QUIRK_RANGES_ON_CHILDREN; sc->sc_populated_slots = 0; sc->sc_data = (vm_offset_t)pmap_mapdev(reg[1], reg[2]); /* * Set up the resource manager and the HT->MPIC mapping. For cpcht, * the ranges are properties of the child bridges, and this is also * where we get the HT interrupts properties. */ #if 0 /* I/O port mappings are usually not in the device tree */ rman_manage_region(&sc->pci_sc.sc_io_rman, 0, CPCHT_IOPORT_SIZE - 1); #endif bzero(sc->htirq_map, sizeof(sc->htirq_map)); mtx_init(&sc->htirq_mtx, "cpcht irq", NULL, MTX_DEF); for (i = 0; i < 8; i++) sc->htirq_map[i].irq_type = IRQ_INTERNAL; for (child = OF_child(node); child != 0; child = OF_peer(child)) cpcht_configure_htbridge(dev, child); /* Now make the mapping table available to the MPIC */ cpcht_irqmap = sc->htirq_map; return (ofw_pci_attach(dev)); } static void cpcht_configure_htbridge(device_t dev, phandle_t child) { struct cpcht_softc *sc; struct ofw_pci_register pcir; int ptr, nextptr; uint32_t vend, val; int i, nirq, irq; u_int b, f, s; sc = device_get_softc(dev); - if (OF_getprop(child, "reg", &pcir, sizeof(pcir)) == -1) + if (OF_getencprop(child, "reg", (pcell_t *)&pcir, sizeof(pcir)) == -1) return; b = OFW_PCI_PHYS_HI_BUS(pcir.phys_hi); s = OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi); f = OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi); /* * Mark this slot is populated. The remote south bridge does * not like us talking to unpopulated slots on the root bus. */ sc->sc_populated_slots |= (1 << s); /* * Next build up any HT->MPIC mappings for this sub-bus. One would * naively hope that enabling, disabling, and EOIing interrupts would * cause the appropriate HT bus transactions to that effect. This is * not the case. * * Instead, we have to muck about on the HT peer's root PCI bridges, * figure out what interrupts they send, enable them, and cache * the location of their WaitForEOI registers so that we can * send EOIs later. */ /* All the devices we are interested in have caps */ if (!(PCIB_READ_CONFIG(dev, b, s, f, PCIR_STATUS, 2) & PCIM_STATUS_CAPPRESENT)) return; nextptr = PCIB_READ_CONFIG(dev, b, s, f, PCIR_CAP_PTR, 1); while (nextptr != 0) { ptr = nextptr; nextptr = PCIB_READ_CONFIG(dev, b, s, f, ptr + PCICAP_NEXTPTR, 1); /* Find the HT IRQ capabilities */ if (PCIB_READ_CONFIG(dev, b, s, f, ptr + PCICAP_ID, 1) != PCIY_HT) continue; val = PCIB_READ_CONFIG(dev, b, s, f, ptr + PCIR_HT_COMMAND, 2); if ((val & PCIM_HTCMD_CAP_MASK) != PCIM_HTCAP_INTERRUPT) continue; /* Ask for the IRQ count */ PCIB_WRITE_CONFIG(dev, b, s, f, ptr + PCIR_HT_COMMAND, 0x1, 1); nirq = PCIB_READ_CONFIG(dev, b, s, f, ptr + 4, 4); nirq = ((nirq >> 16) & 0xff) + 1; device_printf(dev, "%d HT IRQs on device %d.%d\n", nirq, s, f); for (i = 0; i < nirq; i++) { PCIB_WRITE_CONFIG(dev, b, s, f, ptr + PCIR_HT_COMMAND, 0x10 + (i << 1), 1); irq = PCIB_READ_CONFIG(dev, b, s, f, ptr + 4, 4); /* * Mask this interrupt for now. */ PCIB_WRITE_CONFIG(dev, b, s, f, ptr + 4, irq | HTAPIC_MASK, 4); irq = (irq >> 16) & 0xff; sc->htirq_map[irq].irq_type = IRQ_HT; sc->htirq_map[irq].ht_source = i; sc->htirq_map[irq].ht_base = sc->sc_data + (((((s & 0x1f) << 3) | (f & 0x07)) << 8) | (ptr)); PCIB_WRITE_CONFIG(dev, b, s, f, ptr + PCIR_HT_COMMAND, 0x11 + (i << 1), 1); sc->htirq_map[irq].eoi_data = PCIB_READ_CONFIG(dev, b, s, f, ptr + 4, 4) | 0x80000000; /* * Apple uses a non-compliant IO/APIC that differs * in how we signal EOIs. Check if this device was * made by Apple, and act accordingly. */ vend = PCIB_READ_CONFIG(dev, b, s, f, PCIR_DEVVENDOR, 4); if ((vend & 0xffff) == 0x106b) sc->htirq_map[irq].apple_eoi = (sc->htirq_map[irq].ht_base - ptr) + 0x60; } } } static u_int32_t cpcht_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, int width) { struct cpcht_softc *sc; vm_offset_t caoff; sc = device_get_softc(dev); caoff = sc->sc_data + (((((slot & 0x1f) << 3) | (func & 0x07)) << 8) | reg); if (bus == 0 && (!(sc->sc_populated_slots & (1 << slot)) || func > 0)) return (0xffffffff); if (bus > 0) caoff += 0x01000000UL + (bus << 16); switch (width) { case 1: return (in8rb(caoff)); break; case 2: return (in16rb(caoff)); break; case 4: return (in32rb(caoff)); break; } return (0xffffffff); } static void cpcht_write_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, u_int32_t val, int width) { struct cpcht_softc *sc; vm_offset_t caoff; sc = device_get_softc(dev); caoff = sc->sc_data + (((((slot & 0x1f) << 3) | (func & 0x07)) << 8) | reg); if (bus == 0 && (!(sc->sc_populated_slots & (1 << slot)) || func > 0)) return; if (bus > 0) caoff += 0x01000000UL + (bus << 16); switch (width) { case 1: out8rb(caoff, val); break; case 2: out16rb(caoff, val); break; case 4: out32rb(caoff, val); break; } } static int cpcht_route_interrupt(device_t bus, device_t dev, int pin) { return (pin); } static int cpcht_alloc_msi(device_t dev, device_t child, int count, int maxcount, int *irqs) { struct cpcht_softc *sc; int i, j; sc = device_get_softc(dev); j = 0; /* Bail if no MSI PIC yet */ if (cpcht_msipic == 0) return (ENXIO); mtx_lock(&sc->htirq_mtx); for (i = 8; i < 124 - count; i++) { for (j = 0; j < count; j++) { if (sc->htirq_map[i+j].irq_type != IRQ_NONE) break; } if (j == count) break; i += j; /* We know there isn't a large enough run */ } if (j != count) { mtx_unlock(&sc->htirq_mtx); return (ENXIO); } for (j = 0; j < count; j++) { irqs[j] = MAP_IRQ(cpcht_msipic, i+j); sc->htirq_map[i+j].irq_type = IRQ_MSI; } mtx_unlock(&sc->htirq_mtx); return (0); } static int cpcht_release_msi(device_t dev, device_t child, int count, int *irqs) { struct cpcht_softc *sc; int i; sc = device_get_softc(dev); mtx_lock(&sc->htirq_mtx); for (i = 0; i < count; i++) sc->htirq_map[irqs[i] & 0xff].irq_type = IRQ_NONE; mtx_unlock(&sc->htirq_mtx); return (0); } static int cpcht_alloc_msix(device_t dev, device_t child, int *irq) { struct cpcht_softc *sc; int i; sc = device_get_softc(dev); /* Bail if no MSI PIC yet */ if (cpcht_msipic == 0) return (ENXIO); mtx_lock(&sc->htirq_mtx); for (i = 8; i < 124; i++) { if (sc->htirq_map[i].irq_type == IRQ_NONE) { sc->htirq_map[i].irq_type = IRQ_MSI; *irq = MAP_IRQ(cpcht_msipic, i); mtx_unlock(&sc->htirq_mtx); return (0); } } mtx_unlock(&sc->htirq_mtx); return (ENXIO); } static int cpcht_release_msix(device_t dev, device_t child, int irq) { struct cpcht_softc *sc; sc = device_get_softc(dev); mtx_lock(&sc->htirq_mtx); sc->htirq_map[irq & 0xff].irq_type = IRQ_NONE; mtx_unlock(&sc->htirq_mtx); return (0); } static int cpcht_map_msi(device_t dev, device_t child, int irq, uint64_t *addr, uint32_t *data) { device_t pcib; struct pci_devinfo *dinfo; struct pcicfg_ht *ht = NULL; for (pcib = child; pcib != dev; pcib = device_get_parent(device_get_parent(pcib))) { dinfo = device_get_ivars(pcib); ht = &dinfo->cfg.ht; if (ht == NULL) continue; } if (ht == NULL) return (ENXIO); *addr = ht->ht_msiaddr; *data = irq & 0xff; return (0); } /* * Driver for the integrated MPIC on U3/U4 (CPC925/CPC945) */ static int openpic_cpcht_probe(device_t); static int openpic_cpcht_attach(device_t); static void openpic_cpcht_config(device_t, u_int irq, enum intr_trigger trig, enum intr_polarity pol); static void openpic_cpcht_enable(device_t, u_int irq, u_int vector); static void openpic_cpcht_unmask(device_t, u_int irq); static void openpic_cpcht_eoi(device_t, u_int irq); static device_method_t openpic_cpcht_methods[] = { /* Device interface */ DEVMETHOD(device_probe, openpic_cpcht_probe), DEVMETHOD(device_attach, openpic_cpcht_attach), /* PIC interface */ DEVMETHOD(pic_bind, openpic_bind), DEVMETHOD(pic_config, openpic_cpcht_config), DEVMETHOD(pic_dispatch, openpic_dispatch), DEVMETHOD(pic_enable, openpic_cpcht_enable), DEVMETHOD(pic_eoi, openpic_cpcht_eoi), DEVMETHOD(pic_ipi, openpic_ipi), DEVMETHOD(pic_mask, openpic_mask), DEVMETHOD(pic_unmask, openpic_cpcht_unmask), { 0, 0 }, }; struct openpic_cpcht_softc { struct openpic_softc sc_openpic; struct mtx sc_ht_mtx; }; static driver_t openpic_cpcht_driver = { "htpic", openpic_cpcht_methods, sizeof(struct openpic_cpcht_softc), }; DRIVER_MODULE(openpic, unin, openpic_cpcht_driver, openpic_devclass, 0, 0); static int openpic_cpcht_probe(device_t dev) { const char *type = ofw_bus_get_type(dev); if (strcmp(type, "open-pic") != 0) return (ENXIO); device_set_desc(dev, OPENPIC_DEVSTR); return (0); } static int openpic_cpcht_attach(device_t dev) { struct openpic_cpcht_softc *sc; phandle_t node; int err, irq; node = ofw_bus_get_node(dev); err = openpic_common_attach(dev, node); if (err != 0) return (err); /* * The HT APIC stuff is not thread-safe, so we need a mutex to * protect it. */ sc = device_get_softc(dev); mtx_init(&sc->sc_ht_mtx, "htpic", NULL, MTX_SPIN); /* * Interrupts 0-3 are internally sourced and are level triggered * active low. Interrupts 4-123 are connected to a pulse generator * and should be programmed as edge triggered low-to-high. * * IBM CPC945 Manual, Section 9.3. */ for (irq = 0; irq < 4; irq++) openpic_config(dev, irq, INTR_TRIGGER_LEVEL, INTR_POLARITY_LOW); for (irq = 4; irq < 124; irq++) openpic_config(dev, irq, INTR_TRIGGER_EDGE, INTR_POLARITY_LOW); /* * Use this PIC for MSI only if it is the root PIC. This may not * be necessary, but Linux does it, and I cannot find any U3 machines * with MSI devices to test. */ if (dev == root_pic) cpcht_msipic = node; return (0); } static void openpic_cpcht_config(device_t dev, u_int irq, enum intr_trigger trig, enum intr_polarity pol) { struct openpic_cpcht_softc *sc; uint32_t ht_irq; /* * The interrupt settings for the MPIC are completely determined * by the internal wiring in the northbridge. Real changes to these * settings need to be negotiated with the remote IO-APIC on the HT * link. */ sc = device_get_softc(dev); if (cpcht_irqmap != NULL && irq < 128 && cpcht_irqmap[irq].ht_base > 0 && !cpcht_irqmap[irq].edge) { mtx_lock_spin(&sc->sc_ht_mtx); /* Program the data port */ out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND, 0x10 + (cpcht_irqmap[irq].ht_source << 1)); /* Grab the IRQ config register */ ht_irq = in32rb(cpcht_irqmap[irq].ht_base + 4); /* Mask the IRQ while we fiddle settings */ out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq | HTAPIC_MASK); /* Program the interrupt sense */ ht_irq &= ~(HTAPIC_TRIGGER_LEVEL | HTAPIC_REQUEST_EOI); if (trig == INTR_TRIGGER_EDGE) { cpcht_irqmap[irq].edge = 1; } else { cpcht_irqmap[irq].edge = 0; ht_irq |= HTAPIC_TRIGGER_LEVEL | HTAPIC_REQUEST_EOI; } out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq); mtx_unlock_spin(&sc->sc_ht_mtx); } } static void openpic_cpcht_enable(device_t dev, u_int irq, u_int vec) { struct openpic_cpcht_softc *sc; uint32_t ht_irq; openpic_enable(dev, irq, vec); sc = device_get_softc(dev); if (cpcht_irqmap != NULL && irq < 128 && cpcht_irqmap[irq].ht_base > 0) { mtx_lock_spin(&sc->sc_ht_mtx); /* Program the data port */ out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND, 0x10 + (cpcht_irqmap[irq].ht_source << 1)); /* Unmask the interrupt */ ht_irq = in32rb(cpcht_irqmap[irq].ht_base + 4); ht_irq &= ~HTAPIC_MASK; out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq); mtx_unlock_spin(&sc->sc_ht_mtx); } openpic_cpcht_eoi(dev, irq); } static void openpic_cpcht_unmask(device_t dev, u_int irq) { struct openpic_cpcht_softc *sc; uint32_t ht_irq; openpic_unmask(dev, irq); sc = device_get_softc(dev); if (cpcht_irqmap != NULL && irq < 128 && cpcht_irqmap[irq].ht_base > 0) { mtx_lock_spin(&sc->sc_ht_mtx); /* Program the data port */ out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND, 0x10 + (cpcht_irqmap[irq].ht_source << 1)); /* Unmask the interrupt */ ht_irq = in32rb(cpcht_irqmap[irq].ht_base + 4); ht_irq &= ~HTAPIC_MASK; out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq); mtx_unlock_spin(&sc->sc_ht_mtx); } openpic_cpcht_eoi(dev, irq); } static void openpic_cpcht_eoi(device_t dev, u_int irq) { struct openpic_cpcht_softc *sc; uint32_t off, mask; if (irq == 255) return; sc = device_get_softc(dev); if (cpcht_irqmap != NULL && irq < 128 && cpcht_irqmap[irq].ht_base > 0 && !cpcht_irqmap[irq].edge) { /* If this is an HT IRQ, acknowledge it at the remote APIC */ if (cpcht_irqmap[irq].apple_eoi) { off = (cpcht_irqmap[irq].ht_source >> 3) & ~3; mask = 1 << (cpcht_irqmap[irq].ht_source & 0x1f); out32rb(cpcht_irqmap[irq].apple_eoi + off, mask); } else { mtx_lock_spin(&sc->sc_ht_mtx); out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND, 0x11 + (cpcht_irqmap[irq].ht_source << 1)); out32rb(cpcht_irqmap[irq].ht_base + 4, cpcht_irqmap[irq].eoi_data); mtx_unlock_spin(&sc->sc_ht_mtx); } } openpic_eoi(dev, irq); } Index: head/sys/powerpc/powermac/kiic.c =================================================================== --- head/sys/powerpc/powermac/kiic.c (revision 290988) +++ head/sys/powerpc/powermac/kiic.c (revision 290989) @@ -1,444 +1,444 @@ /*- * Copyright (c) 2001 Tsubai Masanari. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ * NetBSD: ki2c.c,v 1.11 2007/12/06 17:00:33 ad Exp * Id: ki2c.c,v 1.7 2002/10/05 09:56:05 tsubai Exp */ /* * Support routines for the Keywest I2C controller. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iicbus_if.h" /* Keywest I2C Register offsets */ #define MODE 0 #define CONTROL 1 #define STATUS 2 #define ISR 3 #define IER 4 #define ADDR 5 #define SUBADDR 6 #define DATA 7 #define REV 8 /* MODE */ #define I2C_SPEED 0x03 /* Speed mask */ #define I2C_100kHz 0x00 #define I2C_50kHz 0x01 #define I2C_25kHz 0x02 #define I2C_MODE 0x0c /* Mode mask */ #define I2C_DUMBMODE 0x00 /* Dumb mode */ #define I2C_STDMODE 0x04 /* Standard mode */ #define I2C_STDSUBMODE 0x08 /* Standard mode + sub address */ #define I2C_COMBMODE 0x0c /* Combined mode */ #define I2C_PORT 0xf0 /* Port mask */ /* CONTROL */ #define I2C_CT_AAK 0x01 /* Send AAK */ #define I2C_CT_ADDR 0x02 /* Send address(es) */ #define I2C_CT_STOP 0x04 /* Send STOP */ #define I2C_CT_START 0x08 /* Send START */ /* STATUS */ #define I2C_ST_BUSY 0x01 /* Busy */ #define I2C_ST_LASTAAK 0x02 /* Last AAK */ #define I2C_ST_LASTRW 0x04 /* Last R/W */ #define I2C_ST_SDA 0x08 /* SDA */ #define I2C_ST_SCL 0x10 /* SCL */ /* ISR/IER */ #define I2C_INT_DATA 0x01 /* Data byte sent/received */ #define I2C_INT_ADDR 0x02 /* Address sent */ #define I2C_INT_STOP 0x04 /* STOP condition sent */ #define I2C_INT_START 0x08 /* START condition sent */ /* I2C flags */ #define I2C_BUSY 0x01 #define I2C_READING 0x02 #define I2C_ERROR 0x04 #define I2C_SELECTED 0x08 struct kiic_softc { device_t sc_dev; phandle_t sc_node; struct mtx sc_mutex; struct resource *sc_reg; int sc_irqrid; struct resource *sc_irq; void *sc_ih; u_int sc_regstep; u_int sc_flags; u_char *sc_data; int sc_resid; uint16_t sc_i2c_base; device_t sc_iicbus; }; static int kiic_probe(device_t dev); static int kiic_attach(device_t dev); static void kiic_writereg(struct kiic_softc *sc, u_int, u_int); static u_int kiic_readreg(struct kiic_softc *, u_int); static void kiic_setport(struct kiic_softc *, u_int); static void kiic_setmode(struct kiic_softc *, u_int); static void kiic_setspeed(struct kiic_softc *, u_int); static void kiic_intr(void *xsc); static int kiic_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs); static phandle_t kiic_get_node(device_t bus, device_t dev); static device_method_t kiic_methods[] = { /* device interface */ DEVMETHOD(device_probe, kiic_probe), DEVMETHOD(device_attach, kiic_attach), /* iicbus interface */ DEVMETHOD(iicbus_callback, iicbus_null_callback), DEVMETHOD(iicbus_transfer, kiic_transfer), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, kiic_get_node), { 0, 0 } }; static driver_t kiic_driver = { "iichb", kiic_methods, sizeof(struct kiic_softc) }; static devclass_t kiic_devclass; DRIVER_MODULE(kiic, macio, kiic_driver, kiic_devclass, 0, 0); DRIVER_MODULE(kiic, unin, kiic_driver, kiic_devclass, 0, 0); static int kiic_probe(device_t self) { const char *name; name = ofw_bus_get_name(self); if (name && strcmp(name, "i2c") == 0) { device_set_desc(self, "Keywest I2C controller"); return (0); } return (ENXIO); } static int kiic_attach(device_t self) { struct kiic_softc *sc = device_get_softc(self); int rid, rate; phandle_t node; char name[64]; bzero(sc, sizeof(*sc)); sc->sc_dev = self; node = ofw_bus_get_node(self); if (node == 0 || node == -1) { return (EINVAL); } rid = 0; sc->sc_reg = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->sc_reg == NULL) { return (ENOMEM); } - if (OF_getprop(node, "AAPL,i2c-rate", &rate, 4) != 4) { + if (OF_getencprop(node, "AAPL,i2c-rate", &rate, 4) != 4) { device_printf(self, "cannot get i2c-rate\n"); return (ENXIO); } - if (OF_getprop(node, "AAPL,address-step", &sc->sc_regstep, 4) != 4) { + if (OF_getencprop(node, "AAPL,address-step", &sc->sc_regstep, 4) != 4) { device_printf(self, "unable to find i2c address step\n"); return (ENXIO); } /* * Some Keywest I2C devices have their children attached directly * underneath them. Some have a single 'iicbus' child with the * devices underneath that. Sort this out, and make sure that the * OFW I2C layer has the correct node. * * Note: the I2C children of the Uninorth bridges have two ports. * In general, the port is designated in the 9th bit of the I2C * address. However, for kiic devices with children attached below * an i2c-bus node, the port is indicated in the 'reg' property * of the i2c-bus node. */ sc->sc_node = node; node = OF_child(node); if (OF_getprop(node, "name", name, sizeof(name)) > 0) { if (strcmp(name,"i2c-bus") == 0) { phandle_t reg; if (OF_getprop(node, "reg", ®, sizeof(reg)) > 0) sc->sc_i2c_base = reg << 8; sc->sc_node = node; } } mtx_init(&sc->sc_mutex, "kiic", NULL, MTX_DEF); sc->sc_irq = bus_alloc_resource_any(self, SYS_RES_IRQ, &sc->sc_irqrid, RF_ACTIVE); bus_setup_intr(self, sc->sc_irq, INTR_TYPE_MISC | INTR_MPSAFE, NULL, kiic_intr, sc, &sc->sc_ih); kiic_writereg(sc, ISR, kiic_readreg(sc, ISR)); kiic_writereg(sc, STATUS, 0); kiic_writereg(sc, IER, 0); kiic_setmode(sc, I2C_STDMODE); kiic_setspeed(sc, I2C_100kHz); /* XXX rate */ kiic_writereg(sc, IER, I2C_INT_DATA | I2C_INT_ADDR | I2C_INT_STOP); if (bootverbose) device_printf(self, "Revision: %02X\n", kiic_readreg(sc, REV)); /* Add the IIC bus layer */ sc->sc_iicbus = device_add_child(self, "iicbus", -1); return (bus_generic_attach(self)); } static void kiic_writereg(struct kiic_softc *sc, u_int reg, u_int val) { bus_write_4(sc->sc_reg, sc->sc_regstep * reg, val); DELAY(100); /* register access delay */ } static u_int kiic_readreg(struct kiic_softc *sc, u_int reg) { return bus_read_4(sc->sc_reg, sc->sc_regstep * reg) & 0xff; } static void kiic_setmode(struct kiic_softc *sc, u_int mode) { u_int x; KASSERT((mode & ~I2C_MODE) == 0, ("bad mode")); x = kiic_readreg(sc, MODE); x &= ~I2C_MODE; x |= mode; kiic_writereg(sc, MODE, x); } static void kiic_setport(struct kiic_softc *sc, u_int port) { u_int x; KASSERT(port == 1 || port == 0, ("bad port")); x = kiic_readreg(sc, MODE); x &= ~I2C_PORT; x |= (port << 4); kiic_writereg(sc, MODE, x); } static void kiic_setspeed(struct kiic_softc *sc, u_int speed) { u_int x; KASSERT((speed & ~I2C_SPEED) == 0, ("bad speed")); x = kiic_readreg(sc, MODE); x &= ~I2C_SPEED; x |= speed; kiic_writereg(sc, MODE, x); } static void kiic_intr(void *xsc) { struct kiic_softc *sc = xsc; u_int isr; uint32_t x; mtx_lock(&sc->sc_mutex); isr = kiic_readreg(sc, ISR); if (isr & I2C_INT_ADDR) { sc->sc_flags |= I2C_SELECTED; if (sc->sc_flags & I2C_READING) { if (sc->sc_resid > 1) { x = kiic_readreg(sc, CONTROL); x |= I2C_CT_AAK; kiic_writereg(sc, CONTROL, x); } } else { kiic_writereg(sc, DATA, *sc->sc_data++); sc->sc_resid--; } } if (isr & I2C_INT_DATA) { if (sc->sc_flags & I2C_READING) { if (sc->sc_resid > 0) { *sc->sc_data++ = kiic_readreg(sc, DATA); sc->sc_resid--; } if (sc->sc_resid == 0) /* done */ kiic_writereg(sc, CONTROL, 0); } else { if (sc->sc_resid == 0) { x = kiic_readreg(sc, CONTROL); x |= I2C_CT_STOP; kiic_writereg(sc, CONTROL, x); } else { kiic_writereg(sc, DATA, *sc->sc_data++); sc->sc_resid--; } } } if (isr & I2C_INT_STOP) { kiic_writereg(sc, CONTROL, 0); sc->sc_flags &= ~I2C_SELECTED; wakeup(sc->sc_dev); } kiic_writereg(sc, ISR, isr); mtx_unlock(&sc->sc_mutex); } static int kiic_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) { struct kiic_softc *sc; int i, x, timo, err; uint16_t addr; uint8_t subaddr; sc = device_get_softc(dev); timo = 100; subaddr = 0; mtx_lock(&sc->sc_mutex); if (sc->sc_flags & I2C_BUSY) mtx_sleep(dev, &sc->sc_mutex, 0, "kiic", timo); if (sc->sc_flags & I2C_BUSY) { mtx_unlock(&sc->sc_mutex); return (ETIMEDOUT); } sc->sc_flags = I2C_BUSY; /* Clear pending interrupts, and reset controller */ kiic_writereg(sc, ISR, kiic_readreg(sc, ISR)); kiic_writereg(sc, STATUS, 0); for (i = 0; i < nmsgs; i++) { if (msgs[i].flags & IIC_M_NOSTOP) { if (msgs[i+1].flags & IIC_M_RD) kiic_setmode(sc, I2C_COMBMODE); else kiic_setmode(sc, I2C_STDSUBMODE); KASSERT(msgs[i].len == 1, ("oversize I2C message")); subaddr = msgs[i].buf[0]; i++; } else { kiic_setmode(sc, I2C_STDMODE); } sc->sc_data = msgs[i].buf; sc->sc_resid = msgs[i].len; sc->sc_flags = I2C_BUSY; addr = msgs[i].slave; timo = 1000 + sc->sc_resid * 200; timo += 100000; if (msgs[i].flags & IIC_M_RD) { sc->sc_flags |= I2C_READING; addr |= 1; } addr |= sc->sc_i2c_base; kiic_setport(sc, (addr & 0x100) >> 8); kiic_writereg(sc, ADDR, addr & 0xff); kiic_writereg(sc, SUBADDR, subaddr); x = kiic_readreg(sc, CONTROL) | I2C_CT_ADDR; kiic_writereg(sc, CONTROL, x); err = mtx_sleep(dev, &sc->sc_mutex, 0, "kiic", timo); msgs[i].len -= sc->sc_resid; if ((sc->sc_flags & I2C_ERROR) || err == EWOULDBLOCK) { device_printf(sc->sc_dev, "I2C error\n"); sc->sc_flags = 0; mtx_unlock(&sc->sc_mutex); return (EIO); } } sc->sc_flags = 0; mtx_unlock(&sc->sc_mutex); return (0); } static phandle_t kiic_get_node(device_t bus, device_t dev) { struct kiic_softc *sc; sc = device_get_softc(bus); /* We only have one child, the I2C bus, which needs our own node. */ return sc->sc_node; } Index: head/sys/powerpc/powermac/macgpio.c =================================================================== --- head/sys/powerpc/powermac/macgpio.c (revision 290988) +++ head/sys/powerpc/powermac/macgpio.c (revision 290989) @@ -1,403 +1,403 @@ /*- * Copyright 2008 by Nathan Whitehorn. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Driver for MacIO GPIO controller */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Macgpio softc */ struct macgpio_softc { phandle_t sc_node; struct resource *sc_gpios; int sc_gpios_rid; uint32_t sc_saved_gpio_levels[2]; uint32_t sc_saved_gpios[GPIO_COUNT]; uint32_t sc_saved_extint_gpios[GPIO_EXTINT_COUNT]; }; static MALLOC_DEFINE(M_MACGPIO, "macgpio", "macgpio device information"); static int macgpio_probe(device_t); static int macgpio_attach(device_t); static int macgpio_print_child(device_t dev, device_t child); static void macgpio_probe_nomatch(device_t, device_t); static struct resource *macgpio_alloc_resource(device_t, device_t, int, int *, u_long, u_long, u_long, u_int); static int macgpio_activate_resource(device_t, device_t, int, int, struct resource *); static int macgpio_deactivate_resource(device_t, device_t, int, int, struct resource *); static ofw_bus_get_devinfo_t macgpio_get_devinfo; static int macgpio_suspend(device_t dev); static int macgpio_resume(device_t dev); /* * Bus interface definition */ static device_method_t macgpio_methods[] = { /* Device interface */ DEVMETHOD(device_probe, macgpio_probe), DEVMETHOD(device_attach, macgpio_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, macgpio_suspend), DEVMETHOD(device_resume, macgpio_resume), /* Bus interface */ DEVMETHOD(bus_print_child, macgpio_print_child), DEVMETHOD(bus_probe_nomatch, macgpio_probe_nomatch), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD(bus_alloc_resource, macgpio_alloc_resource), DEVMETHOD(bus_activate_resource, macgpio_activate_resource), DEVMETHOD(bus_deactivate_resource, macgpio_deactivate_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_child_pnpinfo_str, ofw_bus_gen_child_pnpinfo_str), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_devinfo, macgpio_get_devinfo), DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), { 0, 0 } }; static driver_t macgpio_pci_driver = { "macgpio", macgpio_methods, sizeof(struct macgpio_softc) }; devclass_t macgpio_devclass; DRIVER_MODULE(macgpio, macio, macgpio_pci_driver, macgpio_devclass, 0, 0); struct macgpio_devinfo { struct ofw_bus_devinfo mdi_obdinfo; struct resource_list mdi_resources; int gpio_num; }; static int macgpio_probe(device_t dev) { const char *name; name = ofw_bus_get_name(dev); if (name && strcmp(name, "gpio") == 0) { device_set_desc(dev, "MacIO GPIO Controller"); return (0); } return (ENXIO); } /* * Scan Open Firmware child nodes, and attach these as children * of the macgpio bus */ static int macgpio_attach(device_t dev) { struct macgpio_softc *sc; struct macgpio_devinfo *dinfo; phandle_t root, child, iparent; device_t cdev; uint32_t irq; sc = device_get_softc(dev); root = sc->sc_node = ofw_bus_get_node(dev); sc->sc_gpios = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_gpios_rid, RF_ACTIVE); /* * Iterate through the sub-devices */ for (child = OF_child(root); child != 0; child = OF_peer(child)) { dinfo = malloc(sizeof(*dinfo), M_MACGPIO, M_WAITOK | M_ZERO); if (ofw_bus_gen_setup_devinfo(&dinfo->mdi_obdinfo, child) != 0) { free(dinfo, M_MACGPIO); continue; } - if (OF_getprop(child,"reg",&dinfo->gpio_num, + if (OF_getencprop(child, "reg", &dinfo->gpio_num, sizeof(dinfo->gpio_num)) != sizeof(dinfo->gpio_num)) { /* * Some early GPIO controllers don't provide GPIO * numbers for GPIOs designed only to provide * interrupt resources. We should still allow these * to attach, but with caution. */ dinfo->gpio_num = -1; } resource_list_init(&dinfo->mdi_resources); - if (OF_getprop(child, "interrupts", &irq, sizeof(irq)) == + if (OF_getencprop(child, "interrupts", &irq, sizeof(irq)) == sizeof(irq)) { - OF_searchprop(child, "interrupt-parent", &iparent, + OF_searchencprop(child, "interrupt-parent", &iparent, sizeof(iparent)); resource_list_add(&dinfo->mdi_resources, SYS_RES_IRQ, 0, MAP_IRQ(iparent, irq), MAP_IRQ(iparent, irq), 1); } /* Fix messed-up offsets */ if (dinfo->gpio_num > 0x50) dinfo->gpio_num -= 0x50; cdev = device_add_child(dev, NULL, -1); if (cdev == NULL) { device_printf(dev, "<%s>: device_add_child failed\n", dinfo->mdi_obdinfo.obd_name); ofw_bus_gen_destroy_devinfo(&dinfo->mdi_obdinfo); free(dinfo, M_MACGPIO); continue; } device_set_ivars(cdev, dinfo); } return (bus_generic_attach(dev)); } static int macgpio_print_child(device_t dev, device_t child) { struct macgpio_devinfo *dinfo; int retval = 0; dinfo = device_get_ivars(child); retval += bus_print_child_header(dev, child); if (dinfo->gpio_num >= GPIO_BASE) printf(" gpio %d", dinfo->gpio_num - GPIO_BASE); else if (dinfo->gpio_num >= GPIO_EXTINT_BASE) printf(" extint-gpio %d", dinfo->gpio_num - GPIO_EXTINT_BASE); else if (dinfo->gpio_num >= 0) printf(" addr 0x%02x", dinfo->gpio_num); /* should not happen */ resource_list_print_type(&dinfo->mdi_resources, "irq", SYS_RES_IRQ, "%ld"); retval += bus_print_child_footer(dev, child); return (retval); } static void macgpio_probe_nomatch(device_t dev, device_t child) { struct macgpio_devinfo *dinfo; const char *type; if (bootverbose) { dinfo = device_get_ivars(child); if ((type = ofw_bus_get_type(child)) == NULL) type = "(unknown)"; device_printf(dev, "<%s, %s>", type, ofw_bus_get_name(child)); if (dinfo->gpio_num >= 0) printf(" gpio %d",dinfo->gpio_num); resource_list_print_type(&dinfo->mdi_resources, "irq", SYS_RES_IRQ, "%ld"); printf(" (no driver attached)\n"); } } static struct resource * macgpio_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { struct macgpio_devinfo *dinfo; dinfo = device_get_ivars(child); if (type != SYS_RES_IRQ) return (NULL); return (resource_list_alloc(&dinfo->mdi_resources, bus, child, type, rid, start, end, count, flags)); } static int macgpio_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { struct macgpio_softc *sc; struct macgpio_devinfo *dinfo; u_char val; sc = device_get_softc(bus); dinfo = device_get_ivars(child); if (type != SYS_RES_IRQ) return ENXIO; if (dinfo->gpio_num >= 0) { val = bus_read_1(sc->sc_gpios,dinfo->gpio_num); val |= 0x80; bus_write_1(sc->sc_gpios,dinfo->gpio_num,val); } return (bus_activate_resource(bus, type, rid, res)); } static int macgpio_deactivate_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { struct macgpio_softc *sc; struct macgpio_devinfo *dinfo; u_char val; sc = device_get_softc(bus); dinfo = device_get_ivars(child); if (type != SYS_RES_IRQ) return ENXIO; if (dinfo->gpio_num >= 0) { val = bus_read_1(sc->sc_gpios,dinfo->gpio_num); val &= ~0x80; bus_write_1(sc->sc_gpios,dinfo->gpio_num,val); } return (bus_deactivate_resource(bus, type, rid, res)); } uint8_t macgpio_read(device_t dev) { struct macgpio_softc *sc; struct macgpio_devinfo *dinfo; sc = device_get_softc(device_get_parent(dev)); dinfo = device_get_ivars(dev); if (dinfo->gpio_num < 0) return (0); return (bus_read_1(sc->sc_gpios,dinfo->gpio_num)); } void macgpio_write(device_t dev, uint8_t val) { struct macgpio_softc *sc; struct macgpio_devinfo *dinfo; sc = device_get_softc(device_get_parent(dev)); dinfo = device_get_ivars(dev); if (dinfo->gpio_num < 0) return; bus_write_1(sc->sc_gpios,dinfo->gpio_num,val); } static const struct ofw_bus_devinfo * macgpio_get_devinfo(device_t dev, device_t child) { struct macgpio_devinfo *dinfo; dinfo = device_get_ivars(child); return (&dinfo->mdi_obdinfo); } static int macgpio_suspend(device_t dev) { struct macgpio_softc *sc; int i; sc = device_get_softc(dev); sc->sc_saved_gpio_levels[0] = bus_read_4(sc->sc_gpios, GPIO_LEVELS_0); sc->sc_saved_gpio_levels[1] = bus_read_4(sc->sc_gpios, GPIO_LEVELS_1); for (i = 0; i < GPIO_COUNT; i++) sc->sc_saved_gpios[i] = bus_read_1(sc->sc_gpios, GPIO_BASE + i); for (i = 0; i < GPIO_EXTINT_COUNT; i++) sc->sc_saved_extint_gpios[i] = bus_read_1(sc->sc_gpios, GPIO_EXTINT_BASE + i); return (0); } static int macgpio_resume(device_t dev) { struct macgpio_softc *sc; int i; sc = device_get_softc(dev); bus_write_4(sc->sc_gpios, GPIO_LEVELS_0, sc->sc_saved_gpio_levels[0]); bus_write_4(sc->sc_gpios, GPIO_LEVELS_1, sc->sc_saved_gpio_levels[1]); for (i = 0; i < GPIO_COUNT; i++) bus_write_1(sc->sc_gpios, GPIO_BASE + i, sc->sc_saved_gpios[i]); for (i = 0; i < GPIO_EXTINT_COUNT; i++) bus_write_1(sc->sc_gpios, GPIO_EXTINT_BASE + i, sc->sc_saved_extint_gpios[i]); return (0); } Index: head/sys/powerpc/pseries/mmu_phyp.c =================================================================== --- head/sys/powerpc/pseries/mmu_phyp.c (revision 290988) +++ head/sys/powerpc/pseries/mmu_phyp.c (revision 290989) @@ -1,474 +1,474 @@ /* * Copyright (C) 2010 Andreas Tobler * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mmu_if.h" #include "moea64_if.h" #include "phyp-hvcall.h" extern int n_slbs; static struct rmlock mphyp_eviction_lock; /* * Kernel MMU interface */ static void mphyp_bootstrap(mmu_t mmup, vm_offset_t kernelstart, vm_offset_t kernelend); static void mphyp_cpu_bootstrap(mmu_t mmup, int ap); static int64_t mphyp_pte_synch(mmu_t, struct pvo_entry *pvo); static int64_t mphyp_pte_clear(mmu_t, struct pvo_entry *pvo, uint64_t ptebit); static int64_t mphyp_pte_unset(mmu_t, struct pvo_entry *pvo); static int mphyp_pte_insert(mmu_t, struct pvo_entry *pvo); static mmu_method_t mphyp_methods[] = { MMUMETHOD(mmu_bootstrap, mphyp_bootstrap), MMUMETHOD(mmu_cpu_bootstrap, mphyp_cpu_bootstrap), MMUMETHOD(moea64_pte_synch, mphyp_pte_synch), MMUMETHOD(moea64_pte_clear, mphyp_pte_clear), MMUMETHOD(moea64_pte_unset, mphyp_pte_unset), MMUMETHOD(moea64_pte_insert, mphyp_pte_insert), /* XXX: pmap_copy_page, pmap_init_page with H_PAGE_INIT */ { 0, 0 } }; MMU_DEF_INHERIT(pseries_mmu, "mmu_phyp", mphyp_methods, 0, oea64_mmu); static int brokenkvm = 0; static void print_kvm_bug_warning(void *data) { if (brokenkvm) printf("WARNING: Running on a broken hypervisor that does " "not support mandatory H_CLEAR_MOD and H_CLEAR_REF " "hypercalls. Performance will be suboptimal.\n"); } SYSINIT(kvmbugwarn1, SI_SUB_COPYRIGHT, SI_ORDER_THIRD + 1, print_kvm_bug_warning, NULL); SYSINIT(kvmbugwarn2, SI_SUB_LAST, SI_ORDER_THIRD + 1, print_kvm_bug_warning, NULL); static void mphyp_bootstrap(mmu_t mmup, vm_offset_t kernelstart, vm_offset_t kernelend) { uint64_t final_pteg_count = 0; char buf[8]; uint32_t prop[2]; uint32_t nptlp, shift = 0, slb_encoding = 0; uint32_t lp_size, lp_encoding; struct lpte old; uint64_t vsid; phandle_t dev, node, root; int idx, len, res; rm_init(&mphyp_eviction_lock, "pte eviction"); moea64_early_bootstrap(mmup, kernelstart, kernelend); root = OF_peer(0); dev = OF_child(root); while (dev != 0) { res = OF_getprop(dev, "name", buf, sizeof(buf)); if (res > 0 && strcmp(buf, "cpus") == 0) break; dev = OF_peer(dev); } node = OF_child(dev); while (node != 0) { res = OF_getprop(node, "device_type", buf, sizeof(buf)); if (res > 0 && strcmp(buf, "cpu") == 0) break; node = OF_peer(node); } - res = OF_getprop(node, "ibm,pft-size", prop, sizeof(prop)); + res = OF_getencprop(node, "ibm,pft-size", prop, sizeof(prop)); if (res <= 0) panic("mmu_phyp: unknown PFT size"); final_pteg_count = 1 << prop[1]; - res = OF_getprop(node, "ibm,slb-size", prop, sizeof(prop[0])); + res = OF_getencprop(node, "ibm,slb-size", prop, sizeof(prop[0])); if (res > 0) n_slbs = prop[0]; moea64_pteg_count = final_pteg_count / sizeof(struct lpteg); /* Clear any old page table entries */ for (idx = 0; idx < moea64_pteg_count*8; idx++) { phyp_pft_hcall(H_READ, 0, idx, 0, 0, &old.pte_hi, &old.pte_lo, &old.pte_lo); vsid = (old.pte_hi << (ADDR_API_SHFT64 - ADDR_PIDX_SHFT)) >> 28; if (vsid == VSID_VRMA || vsid == 0 /* Older VRMA */) continue; if (old.pte_hi & LPTE_VALID) phyp_hcall(H_REMOVE, 0, idx, 0); } /* * Scan the large page size property for PAPR compatible machines. * See PAPR D.5 Changes to Section 5.1.4, 'CPU Node Properties' * for the encoding of the property. */ len = OF_getproplen(node, "ibm,segment-page-sizes"); if (len > 0) { /* * We have to use a variable length array on the stack * since we have very limited stack space. */ pcell_t arr[len/sizeof(cell_t)]; res = OF_getencprop(node, "ibm,segment-page-sizes", arr, sizeof(arr)); len /= 4; idx = 0; while (len > 0) { shift = arr[idx]; slb_encoding = arr[idx + 1]; nptlp = arr[idx + 2]; idx += 3; len -= 3; while (len > 0 && nptlp) { lp_size = arr[idx]; lp_encoding = arr[idx+1]; if (slb_encoding == SLBV_L && lp_encoding == 0) break; idx += 2; len -= 2; nptlp--; } if (nptlp && slb_encoding == SLBV_L && lp_encoding == 0) break; } if (len == 0) panic("Standard large pages (SLB[L] = 1, PTE[LP] = 0) " "not supported by this system. Please enable huge " "page backing if running under PowerKVM."); moea64_large_page_shift = shift; moea64_large_page_size = 1ULL << lp_size; } moea64_mid_bootstrap(mmup, kernelstart, kernelend); moea64_late_bootstrap(mmup, kernelstart, kernelend); /* Test for broken versions of KVM that don't conform to the spec */ if (phyp_hcall(H_CLEAR_MOD, 0, 0) == H_FUNCTION) brokenkvm = 1; } static void mphyp_cpu_bootstrap(mmu_t mmup, int ap) { struct slb *slb = PCPU_GET(slb); register_t seg0; int i; /* * Install kernel SLB entries */ __asm __volatile ("slbia"); __asm __volatile ("slbmfee %0,%1; slbie %0;" : "=r"(seg0) : "r"(0)); for (i = 0; i < 64; i++) { if (!(slb[i].slbe & SLBE_VALID)) continue; __asm __volatile ("slbmte %0, %1" :: "r"(slb[i].slbv), "r"(slb[i].slbe)); } } static int64_t mphyp_pte_synch(mmu_t mmu, struct pvo_entry *pvo) { struct lpte pte; uint64_t junk; __asm __volatile("ptesync"); phyp_pft_hcall(H_READ, 0, pvo->pvo_pte.slot, 0, 0, &pte.pte_hi, &pte.pte_lo, &junk); if ((pte.pte_hi & LPTE_AVPN_MASK) != ((pvo->pvo_vpn >> (ADDR_API_SHFT64 - ADDR_PIDX_SHFT)) & LPTE_AVPN_MASK)) return (-1); if (!(pte.pte_hi & LPTE_VALID)) return (-1); return (pte.pte_lo & (LPTE_CHG | LPTE_REF)); } static int64_t mphyp_pte_clear(mmu_t mmu, struct pvo_entry *pvo, uint64_t ptebit) { struct rm_priotracker track; int64_t refchg; uint64_t ptelo, junk; int err; /* * This involves two steps (synch and clear) so we need the entry * not to change in the middle. We are protected against deliberate * unset by virtue of holding the pmap lock. Protection against * incidental unset (page table eviction) comes from holding the * shared eviction lock. */ PMAP_LOCK_ASSERT(pvo->pvo_pmap, MA_OWNED); rm_rlock(&mphyp_eviction_lock, &track); refchg = mphyp_pte_synch(mmu, pvo); if (refchg < 0) { rm_runlock(&mphyp_eviction_lock, &track); return (refchg); } if (brokenkvm) { /* * No way to clear either bit, which is total madness. * Pessimistically claim that, once modified, it stays so * forever and that it is never referenced. */ rm_runlock(&mphyp_eviction_lock, &track); return (refchg & ~LPTE_REF); } if (ptebit & LPTE_CHG) { err = phyp_pft_hcall(H_CLEAR_MOD, 0, pvo->pvo_pte.slot, 0, 0, &ptelo, &junk, &junk); KASSERT(err == H_SUCCESS, ("Error clearing page change bit: %d", err)); refchg |= (ptelo & LPTE_CHG); } if (ptebit & LPTE_REF) { err = phyp_pft_hcall(H_CLEAR_REF, 0, pvo->pvo_pte.slot, 0, 0, &ptelo, &junk, &junk); KASSERT(err == H_SUCCESS, ("Error clearing page reference bit: %d", err)); refchg |= (ptelo & LPTE_REF); } rm_runlock(&mphyp_eviction_lock, &track); return (refchg); } static int64_t mphyp_pte_unset(mmu_t mmu, struct pvo_entry *pvo) { struct lpte pte; uint64_t junk; int err; PMAP_LOCK_ASSERT(pvo->pvo_pmap, MA_OWNED); moea64_pte_from_pvo(pvo, &pte); err = phyp_pft_hcall(H_REMOVE, H_AVPN, pvo->pvo_pte.slot, pte.pte_hi & LPTE_AVPN_MASK, 0, &pte.pte_hi, &pte.pte_lo, &junk); KASSERT(err == H_SUCCESS || err == H_NOT_FOUND, ("Error removing page: %d", err)); if (err == H_NOT_FOUND) { moea64_pte_overflow--; return (-1); } return (pte.pte_lo & (LPTE_REF | LPTE_CHG)); } static uintptr_t mphyp_pte_spillable_ident(uintptr_t ptegbase, struct lpte *to_evict) { uint64_t slot, junk, k; struct lpte pt; int i, j; /* Start at a random slot */ i = mftb() % 8; k = -1; for (j = 0; j < 8; j++) { slot = ptegbase + (i + j) % 8; phyp_pft_hcall(H_READ, 0, slot, 0, 0, &pt.pte_hi, &pt.pte_lo, &junk); if (pt.pte_hi & LPTE_WIRED) continue; /* This is a candidate, so remember it */ k = slot; /* Try to get a page that has not been used lately */ if (!(pt.pte_hi & LPTE_VALID) || !(pt.pte_lo & LPTE_REF)) { memcpy(to_evict, &pt, sizeof(struct lpte)); return (k); } } if (k == -1) return (k); phyp_pft_hcall(H_READ, 0, k, 0, 0, &to_evict->pte_hi, &to_evict->pte_lo, &junk); return (k); } static int mphyp_pte_insert(mmu_t mmu, struct pvo_entry *pvo) { struct rm_priotracker track; int64_t result; struct lpte evicted, pte; uint64_t index, junk, lastptelo; PMAP_LOCK_ASSERT(pvo->pvo_pmap, MA_OWNED); /* Initialize PTE */ moea64_pte_from_pvo(pvo, &pte); evicted.pte_hi = 0; /* Make sure further insertion is locked out during evictions */ rm_rlock(&mphyp_eviction_lock, &track); /* * First try primary hash. */ pvo->pvo_pte.slot &= ~7UL; /* Base slot address */ result = phyp_pft_hcall(H_ENTER, 0, pvo->pvo_pte.slot, pte.pte_hi, pte.pte_lo, &index, &evicted.pte_lo, &junk); if (result == H_SUCCESS) { rm_runlock(&mphyp_eviction_lock, &track); pvo->pvo_pte.slot = index; return (0); } KASSERT(result == H_PTEG_FULL, ("Page insertion error: %ld " "(ptegidx: %#zx/%#x, PTE %#lx/%#lx", result, pvo->pvo_pte.slot, moea64_pteg_count, pte.pte_hi, pte.pte_lo)); /* * Next try secondary hash. */ pvo->pvo_vaddr ^= PVO_HID; pte.pte_hi ^= LPTE_HID; pvo->pvo_pte.slot ^= (moea64_pteg_mask << 3); result = phyp_pft_hcall(H_ENTER, 0, pvo->pvo_pte.slot, pte.pte_hi, pte.pte_lo, &index, &evicted.pte_lo, &junk); if (result == H_SUCCESS) { rm_runlock(&mphyp_eviction_lock, &track); pvo->pvo_pte.slot = index; return (0); } KASSERT(result == H_PTEG_FULL, ("Secondary page insertion error: %ld", result)); /* * Out of luck. Find a PTE to sacrifice. */ /* Lock out all insertions for a bit */ rm_runlock(&mphyp_eviction_lock, &track); rm_wlock(&mphyp_eviction_lock); index = mphyp_pte_spillable_ident(pvo->pvo_pte.slot, &evicted); if (index == -1L) { /* Try other hash table? */ pvo->pvo_vaddr ^= PVO_HID; pte.pte_hi ^= LPTE_HID; pvo->pvo_pte.slot ^= (moea64_pteg_mask << 3); index = mphyp_pte_spillable_ident(pvo->pvo_pte.slot, &evicted); } if (index == -1L) { /* No freeable slots in either PTEG? We're hosed. */ rm_wunlock(&mphyp_eviction_lock); panic("mphyp_pte_insert: overflow"); return (-1); } /* Victim acquired: update page before waving goodbye */ if (evicted.pte_hi & LPTE_VALID) { result = phyp_pft_hcall(H_REMOVE, H_AVPN, index, evicted.pte_hi & LPTE_AVPN_MASK, 0, &junk, &lastptelo, &junk); moea64_pte_overflow++; KASSERT(result == H_SUCCESS, ("Error evicting page: %d", (int)result)); } /* * Set the new PTE. */ result = phyp_pft_hcall(H_ENTER, H_EXACT, index, pte.pte_hi, pte.pte_lo, &index, &evicted.pte_lo, &junk); rm_wunlock(&mphyp_eviction_lock); /* All clear */ pvo->pvo_pte.slot = index; if (result == H_SUCCESS) return (0); panic("Page replacement error: %ld", result); return (result); } Index: head/sys/powerpc/pseries/phyp_console.c =================================================================== --- head/sys/powerpc/pseries/phyp_console.c (revision 290988) +++ head/sys/powerpc/pseries/phyp_console.c (revision 290989) @@ -1,433 +1,433 @@ /*- * Copyright (C) 2011 by Nathan Whitehorn. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "phyp-hvcall.h" #include "uart_if.h" struct uart_phyp_softc { device_t dev; phandle_t node; int vtermid; struct tty *tp; struct resource *irqres; int irqrid; struct callout callout; void *sc_icookie; int polltime; struct mtx sc_mtx; int protocol; union { uint64_t u64[2]; char str[16]; } phyp_inbuf; uint64_t inbuflen; uint8_t outseqno; }; static struct uart_phyp_softc *console_sc = NULL; #if defined(KDB) static int alt_break_state; #endif enum { HVTERM1, HVTERMPROT }; #define VS_DATA_PACKET_HEADER 0xff #define VS_CONTROL_PACKET_HEADER 0xfe #define VSV_SET_MODEM_CTL 0x01 #define VSV_MODEM_CTL_UPDATE 0x02 #define VSV_RENEGOTIATE_CONNECTION 0x03 #define VS_QUERY_PACKET_HEADER 0xfd #define VSV_SEND_VERSION_NUMBER 0x01 #define VSV_SEND_MODEM_CTL_STATUS 0x02 #define VS_QUERY_RESPONSE_PACKET_HEADER 0xfc static int uart_phyp_probe(device_t dev); static int uart_phyp_attach(device_t dev); static void uart_phyp_intr(void *v); static device_method_t uart_phyp_methods[] = { /* Device interface */ DEVMETHOD(device_probe, uart_phyp_probe), DEVMETHOD(device_attach, uart_phyp_attach), DEVMETHOD_END }; static driver_t uart_phyp_driver = { "uart", uart_phyp_methods, sizeof(struct uart_phyp_softc), }; DRIVER_MODULE(uart_phyp, vdevice, uart_phyp_driver, uart_devclass, 0, 0); static cn_probe_t uart_phyp_cnprobe; static cn_init_t uart_phyp_cninit; static cn_term_t uart_phyp_cnterm; static cn_getc_t uart_phyp_cngetc; static cn_putc_t uart_phyp_cnputc; static cn_grab_t uart_phyp_cngrab; static cn_ungrab_t uart_phyp_cnungrab; CONSOLE_DRIVER(uart_phyp); static void uart_phyp_ttyoutwakeup(struct tty *tp); static struct ttydevsw uart_phyp_tty_class = { .tsw_flags = TF_INITLOCK|TF_CALLOUT, .tsw_outwakeup = uart_phyp_ttyoutwakeup, }; static int uart_phyp_probe_node(struct uart_phyp_softc *sc) { phandle_t node = sc->node; uint32_t reg; char buf[64]; sc->inbuflen = 0; sc->outseqno = 0; if (OF_getprop(node, "name", buf, sizeof(buf)) <= 0) return (ENXIO); if (strcmp(buf, "vty") != 0) return (ENXIO); if (OF_getprop(node, "device_type", buf, sizeof(buf)) <= 0) return (ENXIO); if (strcmp(buf, "serial") != 0) return (ENXIO); reg = -1; - OF_getprop(node, "reg", ®, sizeof(reg)); + OF_getencprop(node, "reg", ®, sizeof(reg)); if (reg == -1) return (ENXIO); sc->vtermid = reg; sc->node = node; if (OF_getprop(node, "compatible", buf, sizeof(buf)) <= 0) return (ENXIO); if (strcmp(buf, "hvterm1") == 0) { sc->protocol = HVTERM1; return (0); } else if (strcmp(buf, "hvterm-protocol") == 0) { sc->protocol = HVTERMPROT; return (0); } return (ENXIO); } static int uart_phyp_probe(device_t dev) { const char *name; struct uart_phyp_softc sc; int err; name = ofw_bus_get_name(dev); if (name == NULL || strcmp(name, "vty") != 0) return (ENXIO); sc.node = ofw_bus_get_node(dev); err = uart_phyp_probe_node(&sc); if (err != 0) return (err); device_set_desc(dev, "POWER Hypervisor Virtual Serial Port"); return (err); } static void uart_phyp_cnprobe(struct consdev *cp) { char buf[64]; ihandle_t stdout; phandle_t input, chosen; static struct uart_phyp_softc sc; if ((chosen = OF_finddevice("/chosen")) == -1) goto fail; /* Check if OF has an active stdin/stdout */ input = -1; - if (OF_getprop(chosen, "stdout", &stdout, + if (OF_getencprop(chosen, "stdout", &stdout, sizeof(stdout)) == sizeof(stdout) && stdout != 0) input = OF_instance_to_package(stdout); if (input == -1) goto fail; if (OF_getprop(input, "device_type", buf, sizeof(buf)) == -1) goto fail; if (strcmp(buf, "serial") != 0) goto fail; sc.node = input; if (uart_phyp_probe_node(&sc) != 0) goto fail; mtx_init(&sc.sc_mtx, "uart_phyp", NULL, MTX_SPIN | MTX_QUIET | MTX_NOWITNESS); cp->cn_pri = CN_NORMAL; console_sc = ≻ return; fail: cp->cn_pri = CN_DEAD; return; } static int uart_phyp_attach(device_t dev) { struct uart_phyp_softc *sc; int unit; sc = device_get_softc(dev); sc->dev = dev; sc->node = ofw_bus_get_node(dev); uart_phyp_probe_node(sc); unit = device_get_unit(dev); sc->tp = tty_alloc(&uart_phyp_tty_class, sc); mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_SPIN | MTX_QUIET | MTX_NOWITNESS); if (console_sc != NULL && console_sc->vtermid == sc->vtermid) { sc->outseqno = console_sc->outseqno; console_sc = sc; sprintf(uart_phyp_consdev.cn_name, "ttyu%r", unit); tty_init_console(sc->tp, 0); } sc->irqrid = 0; sc->irqres = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqrid, RF_ACTIVE | RF_SHAREABLE); if (sc->irqres != NULL) { bus_setup_intr(dev, sc->irqres, INTR_TYPE_TTY | INTR_MPSAFE, NULL, uart_phyp_intr, sc, &sc->sc_icookie); } else { callout_init(&sc->callout, 1); sc->polltime = hz / 20; if (sc->polltime < 1) sc->polltime = 1; callout_reset(&sc->callout, sc->polltime, uart_phyp_intr, sc); } tty_makedev(sc->tp, NULL, "u%r", unit); return (0); } static void uart_phyp_cninit(struct consdev *cp) { strcpy(cp->cn_name, "phypcons"); } static void uart_phyp_cnterm(struct consdev *cp) { } static int uart_phyp_get(struct uart_phyp_softc *sc, void *buffer, size_t bufsize) { int err; int hdr = 0; uart_lock(&sc->sc_mtx); if (sc->inbuflen == 0) { err = phyp_pft_hcall(H_GET_TERM_CHAR, sc->vtermid, 0, 0, 0, &sc->inbuflen, &sc->phyp_inbuf.u64[0], &sc->phyp_inbuf.u64[1]); if (err != H_SUCCESS) { uart_unlock(&sc->sc_mtx); return (-1); } hdr = 1; } if (sc->inbuflen == 0) { uart_unlock(&sc->sc_mtx); return (0); } if (bufsize > sc->inbuflen) bufsize = sc->inbuflen; if ((sc->protocol == HVTERMPROT) && (hdr == 1)) { sc->inbuflen = sc->inbuflen - 4; /* The VTERM protocol has a 4 byte header, skip it here. */ memmove(&sc->phyp_inbuf.str[0], &sc->phyp_inbuf.str[4], sc->inbuflen); } memcpy(buffer, sc->phyp_inbuf.str, bufsize); sc->inbuflen -= bufsize; if (sc->inbuflen > 0) memmove(&sc->phyp_inbuf.str[0], &sc->phyp_inbuf.str[bufsize], sc->inbuflen); uart_unlock(&sc->sc_mtx); return (bufsize); } static int uart_phyp_put(struct uart_phyp_softc *sc, void *buffer, size_t bufsize) { uint16_t seqno; uint64_t len = 0; int err; union { uint64_t u64[2]; char bytes[16]; } cbuf; uart_lock(&sc->sc_mtx); switch (sc->protocol) { case HVTERM1: if (bufsize > 16) bufsize = 16; memcpy(&cbuf, buffer, bufsize); len = bufsize; break; case HVTERMPROT: if (bufsize > 12) bufsize = 12; seqno = sc->outseqno++; cbuf.bytes[0] = VS_DATA_PACKET_HEADER; cbuf.bytes[1] = 4 + bufsize; /* total length, max 16 bytes */ cbuf.bytes[2] = (seqno >> 8) & 0xff; cbuf.bytes[3] = seqno & 0xff; memcpy(&cbuf.bytes[4], buffer, bufsize); len = 4 + bufsize; break; } do { err = phyp_hcall(H_PUT_TERM_CHAR, sc->vtermid, len, cbuf.u64[0], cbuf.u64[1]); DELAY(100); } while (err == H_BUSY); uart_unlock(&sc->sc_mtx); return (bufsize); } static int uart_phyp_cngetc(struct consdev *cp) { unsigned char c; int retval; retval = uart_phyp_get(console_sc, &c, 1); if (retval != 1) return (-1); #if defined(KDB) kdb_alt_break(c, &alt_break_state); #endif return (c); } static void uart_phyp_cnputc(struct consdev *cp, int c) { unsigned char ch = c; uart_phyp_put(console_sc, &ch, 1); } static void uart_phyp_cngrab(struct consdev *cp) { } static void uart_phyp_cnungrab(struct consdev *cp) { } static void uart_phyp_ttyoutwakeup(struct tty *tp) { struct uart_phyp_softc *sc; char buffer[8]; int len; sc = tty_softc(tp); while ((len = ttydisc_getc(tp, buffer, sizeof(buffer))) != 0) uart_phyp_put(sc, buffer, len); } static void uart_phyp_intr(void *v) { struct uart_phyp_softc *sc = v; struct tty *tp = sc->tp; unsigned char c; int len; tty_lock(tp); while ((len = uart_phyp_get(sc, &c, 1)) > 0) ttydisc_rint(tp, c, 0); ttydisc_rint_done(tp); tty_unlock(tp); if (sc->irqres == NULL) callout_reset(&sc->callout, sc->polltime, uart_phyp_intr, sc); } Index: head/sys/powerpc/pseries/phyp_llan.c =================================================================== --- head/sys/powerpc/pseries/phyp_llan.c (revision 290988) +++ head/sys/powerpc/pseries/phyp_llan.c (revision 290989) @@ -1,512 +1,512 @@ /*- * Copyright 2013 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define LLAN_MAX_RX_PACKETS 100 #define LLAN_MAX_TX_PACKETS 100 #define LLAN_RX_BUF_LEN 8*PAGE_SIZE #define LLAN_BUFDESC_VALID (1ULL << 63) #define LLAN_ADD_MULTICAST 0x1 #define LLAN_DEL_MULTICAST 0x2 #define LLAN_CLEAR_MULTICAST 0x3 struct llan_xfer { struct mbuf *rx_mbuf; bus_dmamap_t rx_dmamap; uint64_t rx_bufdesc; }; struct llan_receive_queue_entry { /* PAPR page 539 */ uint8_t control; uint8_t reserved; uint16_t offset; uint32_t length; uint64_t handle; } __packed; struct llan_softc { device_t dev; struct mtx io_lock; cell_t unit; uint8_t mac_address[8]; int irqid; struct resource *irq; void *irq_cookie; bus_dma_tag_t rx_dma_tag; bus_dma_tag_t rxbuf_dma_tag; bus_dma_tag_t tx_dma_tag; bus_dmamap_t tx_dma_map; struct llan_receive_queue_entry *rx_buf; int rx_dma_slot; int rx_valid_val; bus_dmamap_t rx_buf_map; bus_addr_t rx_buf_phys; bus_size_t rx_buf_len; bus_addr_t input_buf_phys; bus_addr_t filter_buf_phys; struct llan_xfer rx_xfer[LLAN_MAX_RX_PACKETS]; struct ifnet *ifp; }; static int llan_probe(device_t); static int llan_attach(device_t); static void llan_intr(void *xsc); static void llan_init(void *xsc); static void llan_start(struct ifnet *ifp); static int llan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data); static void llan_rx_load_cb(void *xsc, bus_dma_segment_t *segs, int nsegs, int err); static int llan_add_rxbuf(struct llan_softc *sc, struct llan_xfer *rx); static int llan_set_multicast(struct llan_softc *sc); static devclass_t llan_devclass; static device_method_t llan_methods[] = { DEVMETHOD(device_probe, llan_probe), DEVMETHOD(device_attach, llan_attach), DEVMETHOD_END }; static driver_t llan_driver = { "llan", llan_methods, sizeof(struct llan_softc) }; DRIVER_MODULE(llan, vdevice, llan_driver, llan_devclass, 0, 0); static int llan_probe(device_t dev) { if (!ofw_bus_is_compatible(dev,"IBM,l-lan")) return (ENXIO); device_set_desc(dev, "POWER Hypervisor Virtual Ethernet"); return (0); } static int llan_attach(device_t dev) { struct llan_softc *sc; phandle_t node; int error, i; sc = device_get_softc(dev); sc->dev = dev; /* Get firmware properties */ node = ofw_bus_get_node(dev); OF_getprop(node, "local-mac-address", sc->mac_address, sizeof(sc->mac_address)); - OF_getprop(node, "reg", &sc->unit, sizeof(sc->unit)); + OF_getencprop(node, "reg", &sc->unit, sizeof(sc->unit)); mtx_init(&sc->io_lock, "llan", NULL, MTX_DEF); /* Setup interrupt */ sc->irqid = 0; sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqid, RF_ACTIVE); if (!sc->irq) { device_printf(dev, "Could not allocate IRQ\n"); mtx_destroy(&sc->io_lock); return (ENXIO); } bus_setup_intr(dev, sc->irq, INTR_TYPE_MISC | INTR_MPSAFE | INTR_ENTROPY, NULL, llan_intr, sc, &sc->irq_cookie); /* Setup DMA */ error = bus_dma_tag_create(bus_get_dma_tag(dev), 16, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, LLAN_RX_BUF_LEN, 1, BUS_SPACE_MAXSIZE_32BIT, 0, NULL, NULL, &sc->rx_dma_tag); error = bus_dma_tag_create(bus_get_dma_tag(dev), 4, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, BUS_SPACE_MAXSIZE, 1, BUS_SPACE_MAXSIZE_32BIT, 0, NULL, NULL, &sc->rxbuf_dma_tag); error = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, BUS_SPACE_MAXSIZE, 6, BUS_SPACE_MAXSIZE_32BIT, 0, busdma_lock_mutex, &sc->io_lock, &sc->tx_dma_tag); error = bus_dmamem_alloc(sc->rx_dma_tag, (void **)&sc->rx_buf, BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->rx_buf_map); error = bus_dmamap_load(sc->rx_dma_tag, sc->rx_buf_map, sc->rx_buf, LLAN_RX_BUF_LEN, llan_rx_load_cb, sc, 0); /* TX DMA maps */ bus_dmamap_create(sc->tx_dma_tag, 0, &sc->tx_dma_map); /* RX DMA */ for (i = 0; i < LLAN_MAX_RX_PACKETS; i++) { error = bus_dmamap_create(sc->rxbuf_dma_tag, 0, &sc->rx_xfer[i].rx_dmamap); sc->rx_xfer[i].rx_mbuf = NULL; } /* Attach to network stack */ sc->ifp = if_alloc(IFT_ETHER); sc->ifp->if_softc = sc; if_initname(sc->ifp, device_get_name(dev), device_get_unit(dev)); sc->ifp->if_mtu = ETHERMTU; /* XXX max-frame-size from OF? */ sc->ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; sc->ifp->if_hwassist = 0; /* XXX: ibm,illan-options */ sc->ifp->if_capabilities = 0; sc->ifp->if_capenable = 0; sc->ifp->if_start = llan_start; sc->ifp->if_ioctl = llan_ioctl; sc->ifp->if_init = llan_init; IFQ_SET_MAXLEN(&sc->ifp->if_snd, LLAN_MAX_TX_PACKETS); sc->ifp->if_snd.ifq_drv_maxlen = LLAN_MAX_TX_PACKETS; IFQ_SET_READY(&sc->ifp->if_snd); ether_ifattach(sc->ifp, &sc->mac_address[2]); return (0); } static void llan_rx_load_cb(void *xsc, bus_dma_segment_t *segs, int nsegs, int err) { struct llan_softc *sc = xsc; sc->rx_buf_phys = segs[0].ds_addr; sc->rx_buf_len = segs[0].ds_len - 2*PAGE_SIZE; sc->input_buf_phys = segs[0].ds_addr + segs[0].ds_len - PAGE_SIZE; sc->filter_buf_phys = segs[0].ds_addr + segs[0].ds_len - 2*PAGE_SIZE; } static void llan_init(void *xsc) { struct llan_softc *sc = xsc; uint64_t rx_buf_desc; uint64_t macaddr; int err, i; mtx_lock(&sc->io_lock); phyp_hcall(H_FREE_LOGICAL_LAN, sc->unit); /* Create buffers (page 539) */ sc->rx_dma_slot = 0; sc->rx_valid_val = 1; rx_buf_desc = LLAN_BUFDESC_VALID; rx_buf_desc |= (sc->rx_buf_len << 32); rx_buf_desc |= sc->rx_buf_phys; memcpy(&macaddr, sc->mac_address, 8); err = phyp_hcall(H_REGISTER_LOGICAL_LAN, sc->unit, sc->input_buf_phys, rx_buf_desc, sc->filter_buf_phys, macaddr); for (i = 0; i < LLAN_MAX_RX_PACKETS; i++) llan_add_rxbuf(sc, &sc->rx_xfer[i]); phyp_hcall(H_VIO_SIGNAL, sc->unit, 1); /* Enable interrupts */ /* Tell stack we're up */ sc->ifp->if_drv_flags |= IFF_DRV_RUNNING; sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; mtx_unlock(&sc->io_lock); /* Check for pending receives scheduled before interrupt enable */ llan_intr(sc); } static int llan_add_rxbuf(struct llan_softc *sc, struct llan_xfer *rx) { struct mbuf *m; bus_dma_segment_t segs[1]; int error, nsegs; mtx_assert(&sc->io_lock, MA_OWNED); m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (m == NULL) return (ENOBUFS); m->m_len = m->m_pkthdr.len = m->m_ext.ext_size; if (rx->rx_mbuf != NULL) { bus_dmamap_sync(sc->rxbuf_dma_tag, rx->rx_dmamap, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->rxbuf_dma_tag, rx->rx_dmamap); } /* Save pointer to buffer structure */ m_copyback(m, 0, 8, (void *)&rx); error = bus_dmamap_load_mbuf_sg(sc->rxbuf_dma_tag, rx->rx_dmamap, m, segs, &nsegs, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->dev, "cannot load RX DMA map %p, error = %d\n", rx, error); m_freem(m); return (error); } /* If nsegs is wrong then the stack is corrupt. */ KASSERT(nsegs == 1, ("%s: too many DMA segments (%d)", __func__, nsegs)); rx->rx_mbuf = m; bus_dmamap_sync(sc->rxbuf_dma_tag, rx->rx_dmamap, BUS_DMASYNC_PREREAD); rx->rx_bufdesc = LLAN_BUFDESC_VALID; rx->rx_bufdesc |= (((uint64_t)segs[0].ds_len) << 32); rx->rx_bufdesc |= segs[0].ds_addr; error = phyp_hcall(H_ADD_LOGICAL_LAN_BUFFER, sc->unit, rx->rx_bufdesc); if (error != 0) { m_freem(m); rx->rx_mbuf = NULL; return (ENOBUFS); } return (0); } static void llan_intr(void *xsc) { struct llan_softc *sc = xsc; struct llan_xfer *rx; struct mbuf *m; mtx_lock(&sc->io_lock); restart: phyp_hcall(H_VIO_SIGNAL, sc->unit, 0); while ((sc->rx_buf[sc->rx_dma_slot].control >> 7) == sc->rx_valid_val) { rx = (struct llan_xfer *)sc->rx_buf[sc->rx_dma_slot].handle; m = rx->rx_mbuf; m_adj(m, sc->rx_buf[sc->rx_dma_slot].offset - 8); m->m_len = sc->rx_buf[sc->rx_dma_slot].length; /* llan_add_rxbuf does DMA sync and unload as well as requeue */ if (llan_add_rxbuf(sc, rx) != 0) { if_inc_counter(sc->ifp, IFCOUNTER_IERRORS, 1); phyp_hcall(H_ADD_LOGICAL_LAN_BUFFER, sc->unit, rx->rx_bufdesc); continue; } if_inc_counter(sc->ifp, IFCOUNTER_IPACKETS, 1); m_adj(m, sc->rx_buf[sc->rx_dma_slot].offset); m->m_len = sc->rx_buf[sc->rx_dma_slot].length; m->m_pkthdr.rcvif = sc->ifp; m->m_pkthdr.len = m->m_len; sc->rx_dma_slot++; if (sc->rx_dma_slot >= sc->rx_buf_len/sizeof(sc->rx_buf[0])) { sc->rx_dma_slot = 0; sc->rx_valid_val = !sc->rx_valid_val; } mtx_unlock(&sc->io_lock); (*sc->ifp->if_input)(sc->ifp, m); mtx_lock(&sc->io_lock); } phyp_hcall(H_VIO_SIGNAL, sc->unit, 1); /* * H_VIO_SIGNAL enables interrupts for future packets only. * Make sure none were queued between the end of the loop and the * enable interrupts call. */ if ((sc->rx_buf[sc->rx_dma_slot].control >> 7) == sc->rx_valid_val) goto restart; mtx_unlock(&sc->io_lock); } static void llan_send_packet(void *xsc, bus_dma_segment_t *segs, int nsegs, bus_size_t mapsize, int error) { struct llan_softc *sc = xsc; uint64_t bufdescs[6]; int i; bzero(bufdescs, sizeof(bufdescs)); for (i = 0; i < nsegs; i++) { bufdescs[i] = LLAN_BUFDESC_VALID; bufdescs[i] |= (((uint64_t)segs[i].ds_len) << 32); bufdescs[i] |= segs[i].ds_addr; } phyp_hcall(H_SEND_LOGICAL_LAN, sc->unit, bufdescs[0], bufdescs[1], bufdescs[2], bufdescs[3], bufdescs[4], bufdescs[5], 0); /* * The hypercall returning implies completion -- or that the call will * not complete. In principle, we should try a few times if we get back * H_BUSY based on the continuation token in R4. For now, just drop * the packet in such cases. */ } static void llan_start_locked(struct ifnet *ifp) { struct llan_softc *sc = ifp->if_softc; bus_addr_t first; int nsegs; struct mbuf *mb_head, *m; mtx_assert(&sc->io_lock, MA_OWNED); first = 0; if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING) return; while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) { IFQ_DRV_DEQUEUE(&ifp->if_snd, mb_head); if (mb_head == NULL) break; BPF_MTAP(ifp, mb_head); for (m = mb_head, nsegs = 0; m != NULL; m = m->m_next) nsegs++; if (nsegs > 6) { m = m_collapse(mb_head, M_NOWAIT, 6); if (m == NULL) { m_freem(mb_head); continue; } } bus_dmamap_load_mbuf(sc->tx_dma_tag, sc->tx_dma_map, mb_head, llan_send_packet, sc, 0); bus_dmamap_unload(sc->tx_dma_tag, sc->tx_dma_map); m_freem(mb_head); } } static void llan_start(struct ifnet *ifp) { struct llan_softc *sc = ifp->if_softc; mtx_lock(&sc->io_lock); llan_start_locked(ifp); mtx_unlock(&sc->io_lock); } static int llan_set_multicast(struct llan_softc *sc) { struct ifnet *ifp = sc->ifp; struct ifmultiaddr *inm; uint64_t macaddr; mtx_assert(&sc->io_lock, MA_OWNED); phyp_hcall(H_MULTICAST_CTRL, sc->unit, LLAN_CLEAR_MULTICAST, 0); if_maddr_rlock(ifp); TAILQ_FOREACH(inm, &ifp->if_multiaddrs, ifma_link) { if (inm->ifma_addr->sa_family != AF_LINK) continue; memcpy((uint8_t *)&macaddr + 2, LLADDR((struct sockaddr_dl *)inm->ifma_addr), 6); phyp_hcall(H_MULTICAST_CTRL, sc->unit, LLAN_ADD_MULTICAST, macaddr); } if_maddr_runlock(ifp); return (0); } static int llan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { int err = 0; struct llan_softc *sc = ifp->if_softc; switch (cmd) { case SIOCADDMULTI: case SIOCDELMULTI: mtx_lock(&sc->io_lock); if ((sc->ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) llan_set_multicast(sc); mtx_unlock(&sc->io_lock); break; case SIOCSIFFLAGS: default: err = ether_ioctl(ifp, cmd, data); break; } return (err); } Index: head/sys/powerpc/pseries/phyp_vscsi.c =================================================================== --- head/sys/powerpc/pseries/phyp_vscsi.c (revision 290988) +++ head/sys/powerpc/pseries/phyp_vscsi.c (revision 290989) @@ -1,992 +1,993 @@ /*- * Copyright 2013 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct vscsi_softc; /* VSCSI CRQ format from table 260 of PAPR spec 2.4 (page 760) */ struct vscsi_crq { uint8_t valid; uint8_t format; uint8_t reserved; uint8_t status; uint16_t timeout; uint16_t iu_length; uint64_t iu_data; }; struct vscsi_xfer { TAILQ_ENTRY(vscsi_xfer) queue; struct vscsi_softc *sc; union ccb *ccb; bus_dmamap_t dmamap; uint64_t tag; vmem_addr_t srp_iu_offset; vmem_size_t srp_iu_size; }; TAILQ_HEAD(vscsi_xferq, vscsi_xfer); struct vscsi_softc { device_t dev; struct cam_devq *devq; struct cam_sim *sim; struct cam_path *path; struct mtx io_lock; cell_t unit; int bus_initialized; int bus_logged_in; int max_transactions; int irqid; struct resource *irq; void *irq_cookie; bus_dma_tag_t crq_tag; struct vscsi_crq *crq_queue; int n_crqs, cur_crq; bus_dmamap_t crq_map; bus_addr_t crq_phys; vmem_t *srp_iu_arena; void *srp_iu_queue; bus_addr_t srp_iu_phys; bus_dma_tag_t data_tag; struct vscsi_xfer loginxp; struct vscsi_xfer *xfer; struct vscsi_xferq active_xferq; struct vscsi_xferq free_xferq; }; struct srp_login { uint8_t type; uint8_t reserved[7]; uint64_t tag; uint64_t max_cmd_length; uint32_t reserved2; uint16_t buffer_formats; uint8_t flags; uint8_t reserved3[5]; uint8_t initiator_port_id[16]; uint8_t target_port_id[16]; } __packed; struct srp_login_rsp { uint8_t type; uint8_t reserved[3]; uint32_t request_limit_delta; uint8_t tag; uint32_t max_i_to_t_len; uint32_t max_t_to_i_len; uint16_t buffer_formats; uint8_t flags; /* Some reserved bits follow */ } __packed; struct srp_cmd { uint8_t type; uint8_t flags1; uint8_t reserved[3]; uint8_t formats; uint8_t out_buffer_count; uint8_t in_buffer_count; uint64_t tag; uint32_t reserved2; uint64_t lun; uint8_t reserved3[3]; uint8_t additional_cdb; uint8_t cdb[16]; uint8_t data_payload[0]; } __packed; struct srp_rsp { uint8_t type; uint8_t reserved[3]; uint32_t request_limit_delta; uint64_t tag; uint16_t reserved2; uint8_t flags; uint8_t status; uint32_t data_out_resid; uint32_t data_in_resid; uint32_t sense_data_len; uint32_t response_data_len; uint8_t data_payload[0]; } __packed; struct srp_tsk_mgmt { uint8_t type; uint8_t reserved[7]; uint64_t tag; uint32_t reserved2; uint64_t lun; uint8_t reserved3[2]; uint8_t function; uint8_t reserved4; uint64_t manage_tag; uint64_t reserved5; } __packed; /* Message code type */ #define SRP_LOGIN_REQ 0x00 #define SRP_TSK_MGMT 0x01 #define SRP_CMD 0x02 #define SRP_I_LOGOUT 0x03 #define SRP_LOGIN_RSP 0xC0 #define SRP_RSP 0xC1 #define SRP_LOGIN_REJ 0xC2 #define SRP_T_LOGOUT 0x80 #define SRP_CRED_REQ 0x81 #define SRP_AER_REQ 0x82 #define SRP_CRED_RSP 0x41 #define SRP_AER_RSP 0x41 /* Flags for srp_rsp flags field */ #define SRP_RSPVALID 0x01 #define SRP_SNSVALID 0x02 #define SRP_DOOVER 0x04 #define SRP_DOUNDER 0x08 #define SRP_DIOVER 0x10 #define SRP_DIUNDER 0x20 #define MAD_SUCESS 0x00 #define MAD_NOT_SUPPORTED 0xf1 #define MAD_FAILED 0xf7 #define MAD_EMPTY_IU 0x01 #define MAD_ERROR_LOGGING_REQUEST 0x02 #define MAD_ADAPTER_INFO_REQUEST 0x03 #define MAD_CAPABILITIES_EXCHANGE 0x05 #define MAD_PHYS_ADAP_INFO_REQUEST 0x06 #define MAD_TAPE_PASSTHROUGH_REQUEST 0x07 #define MAD_ENABLE_FAST_FAIL 0x08 static int vscsi_probe(device_t); static int vscsi_attach(device_t); static int vscsi_detach(device_t); static void vscsi_cam_action(struct cam_sim *, union ccb *); static void vscsi_cam_poll(struct cam_sim *); static void vscsi_intr(void *arg); static void vscsi_check_response_queue(struct vscsi_softc *sc); static void vscsi_setup_bus(struct vscsi_softc *sc); static void vscsi_srp_login(struct vscsi_softc *sc); static void vscsi_crq_load_cb(void *, bus_dma_segment_t *, int, int); static void vscsi_scsi_command(void *xxp, bus_dma_segment_t *segs, int nsegs, int err); static void vscsi_task_management(struct vscsi_softc *sc, union ccb *ccb); static void vscsi_srp_response(struct vscsi_xfer *, struct vscsi_crq *); static devclass_t vscsi_devclass; static device_method_t vscsi_methods[] = { DEVMETHOD(device_probe, vscsi_probe), DEVMETHOD(device_attach, vscsi_attach), DEVMETHOD(device_detach, vscsi_detach), DEVMETHOD_END }; static driver_t vscsi_driver = { "vscsi", vscsi_methods, sizeof(struct vscsi_softc) }; DRIVER_MODULE(vscsi, vdevice, vscsi_driver, vscsi_devclass, 0, 0); MALLOC_DEFINE(M_VSCSI, "vscsi", "CAM device queue for VSCSI"); static int vscsi_probe(device_t dev) { if (!ofw_bus_is_compatible(dev, "IBM,v-scsi")) return (ENXIO); device_set_desc(dev, "POWER Hypervisor Virtual SCSI Bus"); return (0); } static int vscsi_attach(device_t dev) { struct vscsi_softc *sc; struct vscsi_xfer *xp; int error, i; sc = device_get_softc(dev); if (sc == NULL) return (EINVAL); sc->dev = dev; mtx_init(&sc->io_lock, "vscsi", NULL, MTX_DEF); /* Get properties */ - OF_getprop(ofw_bus_get_node(dev), "reg", &sc->unit, sizeof(sc->unit)); + OF_getencprop(ofw_bus_get_node(dev), "reg", &sc->unit, + sizeof(sc->unit)); /* Setup interrupt */ sc->irqid = 0; sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqid, RF_ACTIVE); if (!sc->irq) { device_printf(dev, "Could not allocate IRQ\n"); mtx_destroy(&sc->io_lock); return (ENXIO); } bus_setup_intr(dev, sc->irq, INTR_TYPE_CAM | INTR_MPSAFE | INTR_ENTROPY, NULL, vscsi_intr, sc, &sc->irq_cookie); /* Data DMA */ error = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, BUS_SPACE_MAXSIZE, 256, BUS_SPACE_MAXSIZE_32BIT, 0, busdma_lock_mutex, &sc->io_lock, &sc->data_tag); TAILQ_INIT(&sc->active_xferq); TAILQ_INIT(&sc->free_xferq); /* First XFER for login data */ sc->loginxp.sc = sc; bus_dmamap_create(sc->data_tag, 0, &sc->loginxp.dmamap); TAILQ_INSERT_TAIL(&sc->free_xferq, &sc->loginxp, queue); /* CRQ area */ error = bus_dma_tag_create(bus_get_dma_tag(dev), PAGE_SIZE, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, 8*PAGE_SIZE, 1, BUS_SPACE_MAXSIZE, 0, NULL, NULL, &sc->crq_tag); error = bus_dmamem_alloc(sc->crq_tag, (void **)&sc->crq_queue, BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->crq_map); sc->crq_phys = 0; sc->n_crqs = 0; error = bus_dmamap_load(sc->crq_tag, sc->crq_map, sc->crq_queue, 8*PAGE_SIZE, vscsi_crq_load_cb, sc, 0); mtx_lock(&sc->io_lock); vscsi_setup_bus(sc); sc->xfer = malloc(sizeof(sc->xfer[0])*sc->max_transactions, M_VSCSI, M_NOWAIT); for (i = 0; i < sc->max_transactions; i++) { xp = &sc->xfer[i]; xp->sc = sc; error = bus_dmamap_create(sc->data_tag, 0, &xp->dmamap); if (error) { device_printf(dev, "Could not create DMA map (%d)\n", error); break; } TAILQ_INSERT_TAIL(&sc->free_xferq, xp, queue); } mtx_unlock(&sc->io_lock); /* Allocate CAM bits */ if ((sc->devq = cam_simq_alloc(sc->max_transactions)) == NULL) return (ENOMEM); sc->sim = cam_sim_alloc(vscsi_cam_action, vscsi_cam_poll, "vscsi", sc, device_get_unit(dev), &sc->io_lock, sc->max_transactions, sc->max_transactions, sc->devq); if (sc->sim == NULL) { cam_simq_free(sc->devq); sc->devq = NULL; device_printf(dev, "CAM SIM attach failed\n"); return (EINVAL); } mtx_lock(&sc->io_lock); if (xpt_bus_register(sc->sim, dev, 0) != 0) { device_printf(dev, "XPT bus registration failed\n"); cam_sim_free(sc->sim, FALSE); sc->sim = NULL; cam_simq_free(sc->devq); sc->devq = NULL; mtx_unlock(&sc->io_lock); return (EINVAL); } mtx_unlock(&sc->io_lock); return (0); } static int vscsi_detach(device_t dev) { struct vscsi_softc *sc; sc = device_get_softc(dev); if (sc == NULL) return (EINVAL); if (sc->sim != NULL) { mtx_lock(&sc->io_lock); xpt_bus_deregister(cam_sim_path(sc->sim)); cam_sim_free(sc->sim, FALSE); sc->sim = NULL; mtx_unlock(&sc->io_lock); } if (sc->devq != NULL) { cam_simq_free(sc->devq); sc->devq = NULL; } mtx_destroy(&sc->io_lock); return (0); } static void vscsi_cam_action(struct cam_sim *sim, union ccb *ccb) { struct vscsi_softc *sc = cam_sim_softc(sim); mtx_assert(&sc->io_lock, MA_OWNED); switch (ccb->ccb_h.func_code) { case XPT_PATH_INQ: { struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = 1; cpi->hba_inquiry = PI_TAG_ABLE; cpi->hba_misc = PIM_EXTLUNS; cpi->target_sprt = 0; cpi->hba_eng_cnt = 0; cpi->max_target = 0; cpi->max_lun = 0; cpi->initiator_id = ~0; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "IBM", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); cpi->bus_id = cam_sim_bus(sim); cpi->base_transfer_speed = 150000; cpi->transport = XPORT_SRP; cpi->transport_version = 0; cpi->protocol = PROTO_SCSI; cpi->protocol_version = SCSI_REV_SPC4; cpi->ccb_h.status = CAM_REQ_CMP; break; } case XPT_RESET_BUS: ccb->ccb_h.status = CAM_REQ_CMP; break; case XPT_RESET_DEV: ccb->ccb_h.status = CAM_REQ_INPROG; vscsi_task_management(sc, ccb); return; case XPT_GET_TRAN_SETTINGS: ccb->cts.protocol = PROTO_SCSI; ccb->cts.protocol_version = SCSI_REV_SPC4; ccb->cts.transport = XPORT_SRP; ccb->cts.transport_version = 0; ccb->cts.proto_specific.valid = 0; ccb->cts.xport_specific.valid = 0; ccb->ccb_h.status = CAM_REQ_CMP; break; case XPT_SET_TRAN_SETTINGS: ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; break; case XPT_SCSI_IO: { struct vscsi_xfer *xp; ccb->ccb_h.status = CAM_REQ_INPROG; xp = TAILQ_FIRST(&sc->free_xferq); if (xp == NULL) panic("SCSI queue flooded"); xp->ccb = ccb; TAILQ_REMOVE(&sc->free_xferq, xp, queue); TAILQ_INSERT_TAIL(&sc->active_xferq, xp, queue); bus_dmamap_load_ccb(sc->data_tag, xp->dmamap, ccb, vscsi_scsi_command, xp, 0); return; } default: ccb->ccb_h.status = CAM_REQ_INVALID; break; } xpt_done(ccb); return; } static void vscsi_srp_login(struct vscsi_softc *sc) { struct vscsi_xfer *xp; struct srp_login *login; struct vscsi_crq crq; int err; mtx_assert(&sc->io_lock, MA_OWNED); xp = TAILQ_FIRST(&sc->free_xferq); if (xp == NULL) panic("SCSI queue flooded"); xp->ccb = NULL; TAILQ_REMOVE(&sc->free_xferq, xp, queue); TAILQ_INSERT_TAIL(&sc->active_xferq, xp, queue); /* Set up command */ xp->srp_iu_size = crq.iu_length = 64; err = vmem_alloc(xp->sc->srp_iu_arena, xp->srp_iu_size, M_BESTFIT | M_NOWAIT, &xp->srp_iu_offset); if (err) panic("Error during VMEM allocation (%d)", err); login = (struct srp_login *)((uint8_t *)xp->sc->srp_iu_queue + (uintptr_t)xp->srp_iu_offset); bzero(login, xp->srp_iu_size); login->type = SRP_LOGIN_REQ; login->tag = (uint64_t)(xp); login->max_cmd_length = htobe64(256); login->buffer_formats = htobe16(0x1 | 0x2); /* Direct and indirect */ login->flags = 0; /* Create CRQ entry */ crq.valid = 0x80; crq.format = 0x01; crq.iu_data = xp->sc->srp_iu_phys + xp->srp_iu_offset; bus_dmamap_sync(sc->crq_tag, sc->crq_map, BUS_DMASYNC_PREWRITE); err = phyp_hcall(H_SEND_CRQ, xp->sc->unit, ((uint64_t *)(&crq))[0], ((uint64_t *)(&crq))[1]); if (err != 0) panic("CRQ send failure (%d)", err); } static void vscsi_task_management(struct vscsi_softc *sc, union ccb *ccb) { struct srp_tsk_mgmt *cmd; struct vscsi_xfer *xp; struct vscsi_crq crq; int err; mtx_assert(&sc->io_lock, MA_OWNED); xp = TAILQ_FIRST(&sc->free_xferq); if (xp == NULL) panic("SCSI queue flooded"); xp->ccb = ccb; TAILQ_REMOVE(&sc->free_xferq, xp, queue); TAILQ_INSERT_TAIL(&sc->active_xferq, xp, queue); xp->srp_iu_size = crq.iu_length = sizeof(*cmd); err = vmem_alloc(xp->sc->srp_iu_arena, xp->srp_iu_size, M_BESTFIT | M_NOWAIT, &xp->srp_iu_offset); if (err) panic("Error during VMEM allocation (%d)", err); cmd = (struct srp_tsk_mgmt *)((uint8_t *)xp->sc->srp_iu_queue + (uintptr_t)xp->srp_iu_offset); bzero(cmd, xp->srp_iu_size); cmd->type = SRP_TSK_MGMT; cmd->tag = (uint64_t)xp; cmd->lun = htobe64(CAM_EXTLUN_BYTE_SWIZZLE(ccb->ccb_h.target_lun)); switch (ccb->ccb_h.func_code) { case XPT_RESET_DEV: cmd->function = 0x08; break; default: panic("Unimplemented code %d", ccb->ccb_h.func_code); break; } bus_dmamap_sync(xp->sc->crq_tag, xp->sc->crq_map, BUS_DMASYNC_PREWRITE); /* Create CRQ entry */ crq.valid = 0x80; crq.format = 0x01; crq.iu_data = xp->sc->srp_iu_phys + xp->srp_iu_offset; err = phyp_hcall(H_SEND_CRQ, xp->sc->unit, ((uint64_t *)(&crq))[0], ((uint64_t *)(&crq))[1]); if (err != 0) panic("CRQ send failure (%d)", err); } static void vscsi_scsi_command(void *xxp, bus_dma_segment_t *segs, int nsegs, int err) { struct vscsi_xfer *xp = xxp; uint8_t *cdb; union ccb *ccb = xp->ccb; struct srp_cmd *cmd; uint64_t chunk_addr; uint32_t chunk_size; int desc_start, i; struct vscsi_crq crq; KASSERT(err == 0, ("DMA error %d\n", err)); mtx_assert(&xp->sc->io_lock, MA_OWNED); cdb = (ccb->ccb_h.flags & CAM_CDB_POINTER) ? ccb->csio.cdb_io.cdb_ptr : ccb->csio.cdb_io.cdb_bytes; /* Command format from Table 20, page 37 of SRP spec */ crq.iu_length = 48 + ((nsegs > 1) ? 20 : 16) + ((ccb->csio.cdb_len > 16) ? (ccb->csio.cdb_len - 16) : 0); xp->srp_iu_size = crq.iu_length; if (nsegs > 1) xp->srp_iu_size += nsegs*16; xp->srp_iu_size = roundup(xp->srp_iu_size, 16); err = vmem_alloc(xp->sc->srp_iu_arena, xp->srp_iu_size, M_BESTFIT | M_NOWAIT, &xp->srp_iu_offset); if (err) panic("Error during VMEM allocation (%d)", err); cmd = (struct srp_cmd *)((uint8_t *)xp->sc->srp_iu_queue + (uintptr_t)xp->srp_iu_offset); bzero(cmd, xp->srp_iu_size); cmd->type = SRP_CMD; if (ccb->csio.cdb_len > 16) cmd->additional_cdb = (ccb->csio.cdb_len - 16) << 2; memcpy(cmd->cdb, cdb, ccb->csio.cdb_len); cmd->tag = (uint64_t)(xp); /* Let the responder find this again */ cmd->lun = htobe64(CAM_EXTLUN_BYTE_SWIZZLE(ccb->ccb_h.target_lun)); if (nsegs > 1) { /* Use indirect descriptors */ switch (ccb->ccb_h.flags & CAM_DIR_MASK) { case CAM_DIR_OUT: cmd->formats = (2 << 4); break; case CAM_DIR_IN: cmd->formats = 2; break; default: panic("Does not support bidirectional commands (%d)", ccb->ccb_h.flags & CAM_DIR_MASK); break; } desc_start = ((ccb->csio.cdb_len > 16) ? ccb->csio.cdb_len - 16 : 0); chunk_addr = xp->sc->srp_iu_phys + xp->srp_iu_offset + 20 + desc_start + sizeof(*cmd); chunk_size = 16*nsegs; memcpy(&cmd->data_payload[desc_start], &chunk_addr, 8); memcpy(&cmd->data_payload[desc_start+12], &chunk_size, 4); chunk_size = 0; for (i = 0; i < nsegs; i++) chunk_size += segs[i].ds_len; memcpy(&cmd->data_payload[desc_start+16], &chunk_size, 4); desc_start += 20; for (i = 0; i < nsegs; i++) { chunk_addr = segs[i].ds_addr; chunk_size = segs[i].ds_len; memcpy(&cmd->data_payload[desc_start + 16*i], &chunk_addr, 8); /* Set handle tag to 0 */ memcpy(&cmd->data_payload[desc_start + 16*i + 12], &chunk_size, 4); } } else if (nsegs == 1) { switch (ccb->ccb_h.flags & CAM_DIR_MASK) { case CAM_DIR_OUT: cmd->formats = (1 << 4); break; case CAM_DIR_IN: cmd->formats = 1; break; default: panic("Does not support bidirectional commands (%d)", ccb->ccb_h.flags & CAM_DIR_MASK); break; } /* * Memory descriptor: * 8 byte address * 4 byte handle * 4 byte length */ chunk_addr = segs[0].ds_addr; chunk_size = segs[0].ds_len; desc_start = ((ccb->csio.cdb_len > 16) ? ccb->csio.cdb_len - 16 : 0); memcpy(&cmd->data_payload[desc_start], &chunk_addr, 8); /* Set handle tag to 0 */ memcpy(&cmd->data_payload[desc_start+12], &chunk_size, 4); KASSERT(xp->srp_iu_size >= 48 + ((ccb->csio.cdb_len > 16) ? ccb->csio.cdb_len : 16), ("SRP IU command length")); } else { cmd->formats = 0; } bus_dmamap_sync(xp->sc->crq_tag, xp->sc->crq_map, BUS_DMASYNC_PREWRITE); /* Create CRQ entry */ crq.valid = 0x80; crq.format = 0x01; crq.iu_data = xp->sc->srp_iu_phys + xp->srp_iu_offset; err = phyp_hcall(H_SEND_CRQ, xp->sc->unit, ((uint64_t *)(&crq))[0], ((uint64_t *)(&crq))[1]); if (err != 0) panic("CRQ send failure (%d)", err); } static void vscsi_crq_load_cb(void *xsc, bus_dma_segment_t *segs, int nsegs, int err) { struct vscsi_softc *sc = xsc; sc->crq_phys = segs[0].ds_addr; sc->n_crqs = PAGE_SIZE/sizeof(struct vscsi_crq); sc->srp_iu_queue = (uint8_t *)(sc->crq_queue); sc->srp_iu_phys = segs[0].ds_addr; sc->srp_iu_arena = vmem_create("VSCSI SRP IU", PAGE_SIZE, segs[0].ds_len - PAGE_SIZE, 16, 0, M_BESTFIT | M_NOWAIT); } static void vscsi_setup_bus(struct vscsi_softc *sc) { struct vscsi_crq crq; struct vscsi_xfer *xp; int error; struct { uint32_t type; uint16_t status; uint16_t length; uint64_t tag; uint64_t buffer; struct { char srp_version[8]; char partition_name[96]; uint32_t partition_number; uint32_t mad_version; uint32_t os_type; uint32_t port_max_txu[8]; } payload; } mad_adapter_info; bzero(&crq, sizeof(crq)); /* Init message */ crq.valid = 0xc0; crq.format = 0x01; do { error = phyp_hcall(H_FREE_CRQ, sc->unit); } while (error == H_BUSY); /* See initialization sequence page 757 */ bzero(sc->crq_queue, sc->n_crqs*sizeof(sc->crq_queue[0])); sc->cur_crq = 0; sc->bus_initialized = 0; sc->bus_logged_in = 0; bus_dmamap_sync(sc->crq_tag, sc->crq_map, BUS_DMASYNC_PREWRITE); error = phyp_hcall(H_REG_CRQ, sc->unit, sc->crq_phys, sc->n_crqs*sizeof(sc->crq_queue[0])); KASSERT(error == 0, ("CRQ registration success")); error = phyp_hcall(H_SEND_CRQ, sc->unit, ((uint64_t *)(&crq))[0], ((uint64_t *)(&crq))[1]); if (error != 0) panic("CRQ setup failure (%d)", error); while (sc->bus_initialized == 0) vscsi_check_response_queue(sc); /* Send MAD adapter info */ mad_adapter_info.type = MAD_ADAPTER_INFO_REQUEST; mad_adapter_info.status = 0; mad_adapter_info.length = sizeof(mad_adapter_info.payload); strcpy(mad_adapter_info.payload.srp_version, "16.a"); strcpy(mad_adapter_info.payload.partition_name, "UNKNOWN"); mad_adapter_info.payload.partition_number = -1; mad_adapter_info.payload.mad_version = 1; mad_adapter_info.payload.os_type = 2; /* Claim we are Linux */ mad_adapter_info.payload.port_max_txu[0] = 0; /* If this fails, we get the defaults above */ OF_getprop(OF_finddevice("/"), "ibm,partition-name", mad_adapter_info.payload.partition_name, sizeof(mad_adapter_info.payload.partition_name)); OF_getprop(OF_finddevice("/"), "ibm,partition-no", &mad_adapter_info.payload.partition_number, sizeof(mad_adapter_info.payload.partition_number)); xp = TAILQ_FIRST(&sc->free_xferq); xp->ccb = NULL; TAILQ_REMOVE(&sc->free_xferq, xp, queue); TAILQ_INSERT_TAIL(&sc->active_xferq, xp, queue); xp->srp_iu_size = crq.iu_length = sizeof(mad_adapter_info); vmem_alloc(xp->sc->srp_iu_arena, xp->srp_iu_size, M_BESTFIT | M_NOWAIT, &xp->srp_iu_offset); mad_adapter_info.buffer = xp->sc->srp_iu_phys + xp->srp_iu_offset + 24; mad_adapter_info.tag = (uint64_t)xp; memcpy((uint8_t *)xp->sc->srp_iu_queue + (uintptr_t)xp->srp_iu_offset, &mad_adapter_info, sizeof(mad_adapter_info)); crq.valid = 0x80; crq.format = 0x02; crq.iu_data = xp->sc->srp_iu_phys + xp->srp_iu_offset; bus_dmamap_sync(sc->crq_tag, sc->crq_map, BUS_DMASYNC_PREWRITE); phyp_hcall(H_SEND_CRQ, xp->sc->unit, ((uint64_t *)(&crq))[0], ((uint64_t *)(&crq))[1]); while (TAILQ_EMPTY(&sc->free_xferq)) vscsi_check_response_queue(sc); /* Send SRP login */ vscsi_srp_login(sc); while (sc->bus_logged_in == 0) vscsi_check_response_queue(sc); error = phyp_hcall(H_VIO_SIGNAL, sc->unit, 1); /* Enable interrupts */ } static void vscsi_intr(void *xsc) { struct vscsi_softc *sc = xsc; mtx_lock(&sc->io_lock); vscsi_check_response_queue(sc); mtx_unlock(&sc->io_lock); } static void vscsi_srp_response(struct vscsi_xfer *xp, struct vscsi_crq *crq) { union ccb *ccb = xp->ccb; struct vscsi_softc *sc = xp->sc; struct srp_rsp *rsp; uint32_t sense_len; /* SRP response packet in original request */ rsp = (struct srp_rsp *)((uint8_t *)sc->srp_iu_queue + (uintptr_t)xp->srp_iu_offset); ccb->csio.scsi_status = rsp->status; if (ccb->csio.scsi_status == SCSI_STATUS_OK) ccb->ccb_h.status = CAM_REQ_CMP; else ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; #ifdef NOTYET /* Collect fast fail codes */ if (crq->status != 0) ccb->ccb_h.status = CAM_REQ_CMP_ERR; #endif if (ccb->ccb_h.status != CAM_REQ_CMP) { ccb->ccb_h.status |= CAM_DEV_QFRZN; xpt_freeze_devq(ccb->ccb_h.path, /*count*/ 1); } if (!(rsp->flags & SRP_RSPVALID)) rsp->response_data_len = 0; if (!(rsp->flags & SRP_SNSVALID)) rsp->sense_data_len = 0; if (!(rsp->flags & (SRP_DOOVER | SRP_DOUNDER))) rsp->data_out_resid = 0; if (!(rsp->flags & (SRP_DIOVER | SRP_DIUNDER))) rsp->data_in_resid = 0; if (rsp->flags & SRP_SNSVALID) { bzero(&ccb->csio.sense_data, sizeof(struct scsi_sense_data)); ccb->ccb_h.status |= CAM_AUTOSNS_VALID; sense_len = min(be32toh(rsp->sense_data_len), ccb->csio.sense_len); memcpy(&ccb->csio.sense_data, &rsp->data_payload[be32toh(rsp->response_data_len)], sense_len); ccb->csio.sense_resid = ccb->csio.sense_len - be32toh(rsp->sense_data_len); } switch (ccb->ccb_h.flags & CAM_DIR_MASK) { case CAM_DIR_OUT: ccb->csio.resid = rsp->data_out_resid; break; case CAM_DIR_IN: ccb->csio.resid = rsp->data_in_resid; break; } bus_dmamap_sync(sc->data_tag, xp->dmamap, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->data_tag, xp->dmamap); xpt_done(ccb); xp->ccb = NULL; } static void vscsi_login_response(struct vscsi_xfer *xp, struct vscsi_crq *crq) { struct vscsi_softc *sc = xp->sc; struct srp_login_rsp *rsp; /* SRP response packet in original request */ rsp = (struct srp_login_rsp *)((uint8_t *)sc->srp_iu_queue + (uintptr_t)xp->srp_iu_offset); KASSERT(be16toh(rsp->buffer_formats) & 0x3, ("Both direct and indirect " "buffers supported")); sc->max_transactions = be32toh(rsp->request_limit_delta); device_printf(sc->dev, "Queue depth %d commands\n", sc->max_transactions); sc->bus_logged_in = 1; } static void vscsi_cam_poll(struct cam_sim *sim) { struct vscsi_softc *sc = cam_sim_softc(sim); vscsi_check_response_queue(sc); } static void vscsi_check_response_queue(struct vscsi_softc *sc) { struct vscsi_crq *crq; struct vscsi_xfer *xp; int code; mtx_assert(&sc->io_lock, MA_OWNED); while (sc->crq_queue[sc->cur_crq].valid != 0) { /* The hypercalls at both ends of this are not optimal */ phyp_hcall(H_VIO_SIGNAL, sc->unit, 0); bus_dmamap_sync(sc->crq_tag, sc->crq_map, BUS_DMASYNC_POSTREAD); crq = &sc->crq_queue[sc->cur_crq]; switch (crq->valid) { case 0xc0: if (crq->format == 0x02) sc->bus_initialized = 1; break; case 0x80: /* IU data is set to tag pointer (the XP) */ xp = (struct vscsi_xfer *)crq->iu_data; switch (crq->format) { case 0x01: code = *((uint8_t *)sc->srp_iu_queue + (uintptr_t)xp->srp_iu_offset); switch (code) { case SRP_RSP: vscsi_srp_response(xp, crq); break; case SRP_LOGIN_RSP: vscsi_login_response(xp, crq); break; default: device_printf(sc->dev, "Unknown SRP " "response code %d\n", code); break; } break; case 0x02: /* Ignore management datagrams */ break; default: panic("Unknown CRQ format %d\n", crq->format); break; } vmem_free(sc->srp_iu_arena, xp->srp_iu_offset, xp->srp_iu_size); TAILQ_REMOVE(&sc->active_xferq, xp, queue); TAILQ_INSERT_TAIL(&sc->free_xferq, xp, queue); break; default: device_printf(sc->dev, "Unknown CRQ message type %d\n", crq->valid); break; } crq->valid = 0; sc->cur_crq = (sc->cur_crq + 1) % sc->n_crqs; bus_dmamap_sync(sc->crq_tag, sc->crq_map, BUS_DMASYNC_PREWRITE); phyp_hcall(H_VIO_SIGNAL, sc->unit, 1); } } Index: head/sys/powerpc/pseries/platform_chrp.c =================================================================== --- head/sys/powerpc/pseries/platform_chrp.c (revision 290988) +++ head/sys/powerpc/pseries/platform_chrp.c (revision 290989) @@ -1,516 +1,517 @@ /*- * Copyright (c) 2008 Marcel Moolenaar * Copyright (c) 2009 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "platform_if.h" #ifdef SMP extern void *ap_pcpu; #endif #ifdef __powerpc64__ static uint8_t splpar_vpa[MAXCPU][640] __aligned(128); /* XXX: dpcpu */ #endif static vm_offset_t realmaxaddr = VM_MAX_ADDRESS; static int chrp_probe(platform_t); static int chrp_attach(platform_t); void chrp_mem_regions(platform_t, struct mem_region *phys, int *physsz, struct mem_region *avail, int *availsz); static vm_offset_t chrp_real_maxaddr(platform_t); static u_long chrp_timebase_freq(platform_t, struct cpuref *cpuref); static int chrp_smp_first_cpu(platform_t, struct cpuref *cpuref); static int chrp_smp_next_cpu(platform_t, struct cpuref *cpuref); static int chrp_smp_get_bsp(platform_t, struct cpuref *cpuref); static void chrp_smp_ap_init(platform_t); #ifdef SMP static int chrp_smp_start_cpu(platform_t, struct pcpu *cpu); static struct cpu_group *chrp_smp_topo(platform_t plat); #endif static void chrp_reset(platform_t); #ifdef __powerpc64__ #include "phyp-hvcall.h" static void phyp_cpu_idle(sbintime_t sbt); #endif static platform_method_t chrp_methods[] = { PLATFORMMETHOD(platform_probe, chrp_probe), PLATFORMMETHOD(platform_attach, chrp_attach), PLATFORMMETHOD(platform_mem_regions, chrp_mem_regions), PLATFORMMETHOD(platform_real_maxaddr, chrp_real_maxaddr), PLATFORMMETHOD(platform_timebase_freq, chrp_timebase_freq), PLATFORMMETHOD(platform_smp_ap_init, chrp_smp_ap_init), PLATFORMMETHOD(platform_smp_first_cpu, chrp_smp_first_cpu), PLATFORMMETHOD(platform_smp_next_cpu, chrp_smp_next_cpu), PLATFORMMETHOD(platform_smp_get_bsp, chrp_smp_get_bsp), #ifdef SMP PLATFORMMETHOD(platform_smp_start_cpu, chrp_smp_start_cpu), PLATFORMMETHOD(platform_smp_topo, chrp_smp_topo), #endif PLATFORMMETHOD(platform_reset, chrp_reset), { 0, 0 } }; static platform_def_t chrp_platform = { "chrp", chrp_methods, 0 }; PLATFORM_DEF(chrp_platform); static int chrp_probe(platform_t plat) { if (OF_finddevice("/memory") != -1 || OF_finddevice("/memory@0") != -1) return (BUS_PROBE_GENERIC); return (ENXIO); } static int chrp_attach(platform_t plat) { #ifdef __powerpc64__ int i; /* XXX: check for /rtas/ibm,hypertas-functions? */ if (!(mfmsr() & PSL_HV)) { struct mem_region *phys, *avail; int nphys, navail; mem_regions(&phys, &nphys, &avail, &navail); realmaxaddr = phys[0].mr_size; pmap_mmu_install("mmu_phyp", BUS_PROBE_SPECIFIC); cpu_idle_hook = phyp_cpu_idle; /* Set up important VPA fields */ for (i = 0; i < MAXCPU; i++) { bzero(splpar_vpa[i], sizeof(splpar_vpa)); /* First two: VPA size */ splpar_vpa[i][4] = (uint8_t)((sizeof(splpar_vpa[i]) >> 8) & 0xff); splpar_vpa[i][5] = (uint8_t)(sizeof(splpar_vpa[i]) & 0xff); splpar_vpa[i][0xba] = 1; /* Maintain FPRs */ splpar_vpa[i][0xbb] = 1; /* Maintain PMCs */ splpar_vpa[i][0xfc] = 0xff; /* Maintain full SLB */ splpar_vpa[i][0xfd] = 0xff; splpar_vpa[i][0xff] = 1; /* Maintain Altivec */ } mb(); /* Set up hypervisor CPU stuff */ chrp_smp_ap_init(plat); } #endif /* Some systems (e.g. QEMU) need Open Firmware to stand down */ ofw_quiesce(); return (0); } static int parse_drconf_memory(struct mem_region *ofmem, int *msz, struct mem_region *ofavail, int *asz) { phandle_t phandle; vm_offset_t base; int i, idx, len, lasz, lmsz, res; uint32_t flags, lmb_size[2]; - uint64_t *dmem; + uint32_t *dmem; lmsz = *msz; lasz = *asz; phandle = OF_finddevice("/ibm,dynamic-reconfiguration-memory"); if (phandle == -1) /* No drconf node, return. */ return (0); - res = OF_getprop(phandle, "ibm,lmb-size", lmb_size, sizeof(lmb_size)); + res = OF_getencprop(phandle, "ibm,lmb-size", lmb_size, + sizeof(lmb_size)); if (res == -1) return (0); printf("Logical Memory Block size: %d MB\n", lmb_size[1] >> 20); /* Parse the /ibm,dynamic-memory. The first position gives the # of entries. The next two words reflect the address of the memory block. The next four words are the DRC index, reserved, list index and flags. (see PAPR C.6.6.2 ibm,dynamic-reconfiguration-memory) #el Addr DRC-idx res list-idx flags ------------------------------------------------- | 4 | 8 | 4 | 4 | 4 | 4 |.... ------------------------------------------------- */ len = OF_getproplen(phandle, "ibm,dynamic-memory"); if (len > 0) { /* We have to use a variable length array on the stack since we have very limited stack space. */ cell_t arr[len/sizeof(cell_t)]; - res = OF_getprop(phandle, "ibm,dynamic-memory", &arr, - sizeof(arr)); + res = OF_getencprop(phandle, "ibm,dynamic-memory", arr, + sizeof(arr)); if (res == -1) return (0); /* Number of elements */ idx = arr[0]; /* First address, in arr[1], arr[2]*/ - dmem = (uint64_t*)&arr[1]; + dmem = &arr[1]; for (i = 0; i < idx; i++) { - base = *dmem; - dmem += 2; - flags = *dmem; + base = ((uint64_t)dmem[0] << 32) + dmem[1]; + dmem += 4; + flags = dmem[1]; /* Use region only if available and not reserved. */ if ((flags & 0x8) && !(flags & 0x80)) { ofmem[lmsz].mr_start = base; ofmem[lmsz].mr_size = (vm_size_t)lmb_size[1]; ofavail[lasz].mr_start = base; ofavail[lasz].mr_size = (vm_size_t)lmb_size[1]; lmsz++; lasz++; } - dmem++; + dmem += 2; } } *msz = lmsz; *asz = lasz; return (1); } void chrp_mem_regions(platform_t plat, struct mem_region *phys, int *physsz, struct mem_region *avail, int *availsz) { vm_offset_t maxphysaddr; int i; ofw_mem_regions(phys, physsz, avail, availsz); parse_drconf_memory(phys, physsz, avail, availsz); /* * On some firmwares (SLOF), some memory may be marked available that * doesn't actually exist. This manifests as an extension of the last * available segment past the end of physical memory, so truncate that * one. */ maxphysaddr = 0; for (i = 0; i < *physsz; i++) if (phys[i].mr_start + phys[i].mr_size > maxphysaddr) maxphysaddr = phys[i].mr_start + phys[i].mr_size; for (i = 0; i < *availsz; i++) if (avail[i].mr_start + avail[i].mr_size > maxphysaddr) avail[i].mr_size = maxphysaddr - avail[i].mr_start; } static vm_offset_t chrp_real_maxaddr(platform_t plat) { return (realmaxaddr); } static u_long chrp_timebase_freq(platform_t plat, struct cpuref *cpuref) { phandle_t phandle; int32_t ticks = -1; phandle = cpuref->cr_hwref; - OF_getprop(phandle, "timebase-frequency", &ticks, sizeof(ticks)); + OF_getencprop(phandle, "timebase-frequency", &ticks, sizeof(ticks)); if (ticks <= 0) panic("Unable to determine timebase frequency!"); return (ticks); } static int chrp_smp_first_cpu(platform_t plat, struct cpuref *cpuref) { char buf[8]; phandle_t cpu, dev, root; int res, cpuid; root = OF_peer(0); dev = OF_child(root); while (dev != 0) { res = OF_getprop(dev, "name", buf, sizeof(buf)); if (res > 0 && strcmp(buf, "cpus") == 0) break; dev = OF_peer(dev); } if (dev == 0) { /* * psim doesn't have a name property on the /cpus node, * but it can be found directly */ dev = OF_finddevice("/cpus"); if (dev == 0) return (ENOENT); } cpu = OF_child(dev); while (cpu != 0) { res = OF_getprop(cpu, "device_type", buf, sizeof(buf)); if (res > 0 && strcmp(buf, "cpu") == 0) break; cpu = OF_peer(cpu); } if (cpu == 0) return (ENOENT); cpuref->cr_hwref = cpu; - res = OF_getprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid, + res = OF_getencprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid, sizeof(cpuid)); if (res <= 0) - res = OF_getprop(cpu, "reg", &cpuid, sizeof(cpuid)); + res = OF_getencprop(cpu, "reg", &cpuid, sizeof(cpuid)); if (res <= 0) cpuid = 0; cpuref->cr_cpuid = cpuid; return (0); } static int chrp_smp_next_cpu(platform_t plat, struct cpuref *cpuref) { char buf[8]; phandle_t cpu; int i, res, cpuid; /* Check for whether it should be the next thread */ res = OF_getproplen(cpuref->cr_hwref, "ibm,ppc-interrupt-server#s"); if (res > 0) { cell_t interrupt_servers[res/sizeof(cell_t)]; - OF_getprop(cpuref->cr_hwref, "ibm,ppc-interrupt-server#s", + OF_getencprop(cpuref->cr_hwref, "ibm,ppc-interrupt-server#s", interrupt_servers, res); for (i = 0; i < res/sizeof(cell_t) - 1; i++) { if (interrupt_servers[i] == cpuref->cr_cpuid) { cpuref->cr_cpuid = interrupt_servers[i+1]; return (0); } } } /* Next CPU core/package */ cpu = OF_peer(cpuref->cr_hwref); while (cpu != 0) { res = OF_getprop(cpu, "device_type", buf, sizeof(buf)); if (res > 0 && strcmp(buf, "cpu") == 0) break; cpu = OF_peer(cpu); } if (cpu == 0) return (ENOENT); cpuref->cr_hwref = cpu; - res = OF_getprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid, + res = OF_getencprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid, sizeof(cpuid)); if (res <= 0) - res = OF_getprop(cpu, "reg", &cpuid, sizeof(cpuid)); + res = OF_getencprop(cpu, "reg", &cpuid, sizeof(cpuid)); if (res <= 0) cpuid = 0; cpuref->cr_cpuid = cpuid; return (0); } static int chrp_smp_get_bsp(platform_t plat, struct cpuref *cpuref) { ihandle_t inst; phandle_t bsp, chosen; int res, cpuid; chosen = OF_finddevice("/chosen"); if (chosen == 0) return (ENXIO); - res = OF_getprop(chosen, "cpu", &inst, sizeof(inst)); + res = OF_getencprop(chosen, "cpu", &inst, sizeof(inst)); if (res < 0) return (ENXIO); bsp = OF_instance_to_package(inst); /* Pick the primary thread. Can it be any other? */ cpuref->cr_hwref = bsp; - res = OF_getprop(bsp, "ibm,ppc-interrupt-server#s", &cpuid, + res = OF_getencprop(bsp, "ibm,ppc-interrupt-server#s", &cpuid, sizeof(cpuid)); if (res <= 0) - res = OF_getprop(bsp, "reg", &cpuid, sizeof(cpuid)); + res = OF_getencprop(bsp, "reg", &cpuid, sizeof(cpuid)); if (res <= 0) cpuid = 0; cpuref->cr_cpuid = cpuid; return (0); } #ifdef SMP static int chrp_smp_start_cpu(platform_t plat, struct pcpu *pc) { cell_t start_cpu; int result, err, timeout; if (!rtas_exists()) { printf("RTAS uninitialized: unable to start AP %d\n", pc->pc_cpuid); return (ENXIO); } start_cpu = rtas_token_lookup("start-cpu"); if (start_cpu == -1) { printf("RTAS unknown method: unable to start AP %d\n", pc->pc_cpuid); return (ENXIO); } ap_pcpu = pc; powerpc_sync(); result = rtas_call_method(start_cpu, 3, 1, pc->pc_cpuid, EXC_RST, pc, &err); if (result < 0 || err != 0) { printf("RTAS error (%d/%d): unable to start AP %d\n", result, err, pc->pc_cpuid); return (ENXIO); } timeout = 10000; while (!pc->pc_awake && timeout--) DELAY(100); return ((pc->pc_awake) ? 0 : EBUSY); } static struct cpu_group * chrp_smp_topo(platform_t plat) { struct pcpu *pc, *last_pc; int i, ncores, ncpus; ncores = ncpus = 0; last_pc = NULL; for (i = 0; i <= mp_maxid; i++) { pc = pcpu_find(i); if (pc == NULL) continue; if (last_pc == NULL || pc->pc_hwref != last_pc->pc_hwref) ncores++; last_pc = pc; ncpus++; } if (ncpus % ncores != 0) { printf("WARNING: Irregular SMP topology. Performance may be " "suboptimal (%d CPUS, %d cores)\n", ncpus, ncores); return (smp_topo_none()); } /* Don't do anything fancier for non-threaded SMP */ if (ncpus == ncores) return (smp_topo_none()); return (smp_topo_1level(CG_SHARE_L1, ncpus / ncores, CG_FLAG_SMT)); } #endif static void chrp_reset(platform_t platform) { OF_reboot(); } #ifdef __powerpc64__ static void phyp_cpu_idle(sbintime_t sbt) { phyp_hcall(H_CEDE); } static void chrp_smp_ap_init(platform_t platform) { if (!(mfmsr() & PSL_HV)) { /* Register VPA */ phyp_hcall(H_REGISTER_VPA, 1UL, PCPU_GET(cpuid), splpar_vpa[PCPU_GET(cpuid)]); /* Set interrupt priority */ phyp_hcall(H_CPPR, 0xff); } } #else static void chrp_smp_ap_init(platform_t platform) { } #endif Index: head/sys/powerpc/pseries/plpar_iommu.c =================================================================== --- head/sys/powerpc/pseries/plpar_iommu.c (revision 290988) +++ head/sys/powerpc/pseries/plpar_iommu.c (revision 290989) @@ -1,244 +1,245 @@ /*- * Copyright (c) 2013, Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include MALLOC_DEFINE(M_PHYPIOMMU, "iommu", "IOMMU data for PAPR LPARs"); struct papr_iommu_map { uint32_t iobn; vmem_t *vmem; struct papr_iommu_map *next; }; static SLIST_HEAD(iommu_maps, iommu_map) iommu_map_head = SLIST_HEAD_INITIALIZER(iommu_map_head); static int papr_supports_stuff_tce = -1; struct iommu_map { uint32_t iobn; vmem_t *vmem; SLIST_ENTRY(iommu_map) entries; }; struct dma_window { struct iommu_map *map; bus_addr_t start; bus_addr_t end; }; int phyp_iommu_set_dma_tag(device_t bus, device_t dev, bus_dma_tag_t tag) { device_t p; phandle_t node; cell_t dma_acells, dma_scells, dmawindow[6]; struct iommu_map *i; int cell; for (p = dev; device_get_parent(p) != NULL; p = device_get_parent(p)) { if (ofw_bus_has_prop(p, "ibm,my-dma-window")) break; if (ofw_bus_has_prop(p, "ibm,dma-window")) break; } if (p == NULL) return (ENXIO); node = ofw_bus_get_node(p); - if (OF_getprop(node, "ibm,#dma-size-cells", &dma_scells, + if (OF_getencprop(node, "ibm,#dma-size-cells", &dma_scells, sizeof(cell_t)) <= 0) - OF_searchprop(node, "#size-cells", &dma_scells, sizeof(cell_t)); - if (OF_getprop(node, "ibm,#dma-address-cells", &dma_acells, + OF_searchencprop(node, "#size-cells", &dma_scells, + sizeof(cell_t)); + if (OF_getencprop(node, "ibm,#dma-address-cells", &dma_acells, sizeof(cell_t)) <= 0) - OF_searchprop(node, "#address-cells", &dma_acells, + OF_searchencprop(node, "#address-cells", &dma_acells, sizeof(cell_t)); if (ofw_bus_has_prop(p, "ibm,my-dma-window")) - OF_getprop(node, "ibm,my-dma-window", dmawindow, + OF_getencprop(node, "ibm,my-dma-window", dmawindow, sizeof(cell_t)*(dma_scells + dma_acells + 1)); else - OF_getprop(node, "ibm,dma-window", dmawindow, + OF_getencprop(node, "ibm,dma-window", dmawindow, sizeof(cell_t)*(dma_scells + dma_acells + 1)); struct dma_window *window = malloc(sizeof(struct dma_window), M_PHYPIOMMU, M_WAITOK); window->start = 0; for (cell = 1; cell < 1 + dma_acells; cell++) { window->start <<= 32; window->start |= dmawindow[cell]; } window->end = 0; for (; cell < 1 + dma_acells + dma_scells; cell++) { window->end <<= 32; window->end |= dmawindow[cell]; } window->end += window->start; if (bootverbose) device_printf(dev, "Mapping IOMMU domain %#x\n", dmawindow[0]); window->map = NULL; SLIST_FOREACH(i, &iommu_map_head, entries) { if (i->iobn == dmawindow[0]) { window->map = i; break; } } if (window->map == NULL) { window->map = malloc(sizeof(struct iommu_map), M_PHYPIOMMU, M_WAITOK); window->map->iobn = dmawindow[0]; /* * Allocate IOMMU range beginning at PAGE_SIZE. Some drivers * (em(4), for example) do not like getting mappings at 0. */ window->map->vmem = vmem_create("IOMMU mappings", PAGE_SIZE, trunc_page(VMEM_ADDR_MAX) - PAGE_SIZE, PAGE_SIZE, 0, M_BESTFIT | M_NOWAIT); SLIST_INSERT_HEAD(&iommu_map_head, window->map, entries); } /* * Check experimentally whether we can use H_STUFF_TCE. It is required * by the spec but some firmware (e.g. QEMU) does not actually support * it */ if (papr_supports_stuff_tce == -1) papr_supports_stuff_tce = !(phyp_hcall(H_STUFF_TCE, window->map->iobn, 0, 0, 0) == H_FUNCTION); bus_dma_tag_set_iommu(tag, bus, window); return (0); } int phyp_iommu_map(device_t dev, bus_dma_segment_t *segs, int *nsegs, bus_addr_t min, bus_addr_t max, bus_size_t alignment, bus_addr_t boundary, void *cookie) { struct dma_window *window = cookie; bus_addr_t minaddr, maxaddr; bus_addr_t alloced; bus_size_t allocsize; int error, i, j; uint64_t tce; minaddr = window->start; maxaddr = window->end; /* XXX: handle exclusion range in a more useful way */ if (min < maxaddr) maxaddr = min; /* XXX: consolidate segs? */ for (i = 0; i < *nsegs; i++) { allocsize = round_page(segs[i].ds_len + (segs[i].ds_addr & PAGE_MASK)); error = vmem_xalloc(window->map->vmem, allocsize, (alignment < PAGE_SIZE) ? PAGE_SIZE : alignment, 0, boundary, minaddr, maxaddr, M_BESTFIT | M_NOWAIT, &alloced); if (error != 0) { panic("VMEM failure: %d\n", error); return (error); } KASSERT(alloced % PAGE_SIZE == 0, ("Alloc not page aligned")); KASSERT((alloced + (segs[i].ds_addr & PAGE_MASK)) % alignment == 0, ("Allocated segment does not match alignment constraint")); tce = trunc_page(segs[i].ds_addr); tce |= 0x3; /* read/write */ for (j = 0; j < allocsize; j += PAGE_SIZE) { error = phyp_hcall(H_PUT_TCE, window->map->iobn, alloced + j, tce + j); if (error < 0) { panic("IOMMU mapping error: %d\n", error); return (ENOMEM); } } segs[i].ds_addr = alloced + (segs[i].ds_addr & PAGE_MASK); KASSERT(segs[i].ds_addr > 0, ("Address needs to be positive")); KASSERT(segs[i].ds_addr + segs[i].ds_len < maxaddr, ("Address not in range")); if (error < 0) { panic("IOMMU mapping error: %d\n", error); return (ENOMEM); } } return (0); } int phyp_iommu_unmap(device_t dev, bus_dma_segment_t *segs, int nsegs, void *cookie) { struct dma_window *window = cookie; bus_addr_t pageround; bus_size_t roundedsize; int i; bus_addr_t j; for (i = 0; i < nsegs; i++) { pageround = trunc_page(segs[i].ds_addr); roundedsize = round_page(segs[i].ds_len + (segs[i].ds_addr & PAGE_MASK)); if (papr_supports_stuff_tce) { phyp_hcall(H_STUFF_TCE, window->map->iobn, pageround, 0, roundedsize/PAGE_SIZE); } else { for (j = 0; j < roundedsize; j += PAGE_SIZE) phyp_hcall(H_PUT_TCE, window->map->iobn, pageround + j, 0); } vmem_xfree(window->map->vmem, pageround, roundedsize); } return (0); } Index: head/sys/powerpc/pseries/rtas_pci.c =================================================================== --- head/sys/powerpc/pseries/rtas_pci.c (revision 290988) +++ head/sys/powerpc/pseries/rtas_pci.c (revision 290989) @@ -1,205 +1,205 @@ /*- * Copyright (c) 2011 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pcib_if.h" #include "iommu_if.h" /* * Device interface. */ static int rtaspci_probe(device_t); static int rtaspci_attach(device_t); /* * pcib interface. */ static u_int32_t rtaspci_read_config(device_t, u_int, u_int, u_int, u_int, int); static void rtaspci_write_config(device_t, u_int, u_int, u_int, u_int, u_int32_t, int); /* * Driver methods. */ static device_method_t rtaspci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rtaspci_probe), DEVMETHOD(device_attach, rtaspci_attach), /* pcib interface */ DEVMETHOD(pcib_read_config, rtaspci_read_config), DEVMETHOD(pcib_write_config, rtaspci_write_config), DEVMETHOD_END }; struct rtaspci_softc { struct ofw_pci_softc pci_sc; cell_t read_pci_config, write_pci_config; cell_t ex_read_pci_config, ex_write_pci_config; int sc_extended_config; }; static devclass_t rtaspci_devclass; DEFINE_CLASS_1(pcib, rtaspci_driver, rtaspci_methods, sizeof(struct rtaspci_softc), ofw_pci_driver); DRIVER_MODULE(rtaspci, ofwbus, rtaspci_driver, rtaspci_devclass, 0, 0); static int rtaspci_probe(device_t dev) { const char *type; if (!rtas_exists()) return (ENXIO); type = ofw_bus_get_type(dev); if (OF_getproplen(ofw_bus_get_node(dev), "used-by-rtas") < 0) return (ENXIO); if (type == NULL || strcmp(type, "pci") != 0) return (ENXIO); device_set_desc(dev, "RTAS Host-PCI bridge"); return (BUS_PROBE_GENERIC); } static int rtaspci_attach(device_t dev) { struct rtaspci_softc *sc; sc = device_get_softc(dev); sc->read_pci_config = rtas_token_lookup("read-pci-config"); sc->write_pci_config = rtas_token_lookup("write-pci-config"); sc->ex_read_pci_config = rtas_token_lookup("ibm,read-pci-config"); sc->ex_write_pci_config = rtas_token_lookup("ibm,write-pci-config"); sc->sc_extended_config = 0; - OF_getprop(ofw_bus_get_node(dev), "ibm,pci-config-space-type", + OF_getencprop(ofw_bus_get_node(dev), "ibm,pci-config-space-type", &sc->sc_extended_config, sizeof(sc->sc_extended_config)); return (ofw_pci_attach(dev)); } static uint32_t rtaspci_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, int width) { struct rtaspci_softc *sc; uint32_t retval = 0xffffffff; uint32_t config_addr; int error, pcierror; sc = device_get_softc(dev); config_addr = ((bus & 0xff) << 16) | ((slot & 0x1f) << 11) | ((func & 0x7) << 8) | (reg & 0xff); if (sc->sc_extended_config) config_addr |= (reg & 0xf00) << 16; if (sc->ex_read_pci_config != -1) error = rtas_call_method(sc->ex_read_pci_config, 4, 2, config_addr, sc->pci_sc.sc_pcir.phys_hi, sc->pci_sc.sc_pcir.phys_mid, width, &pcierror, &retval); else error = rtas_call_method(sc->read_pci_config, 2, 2, config_addr, width, &pcierror, &retval); /* Sign-extend output */ switch (width) { case 1: retval = (int32_t)(int8_t)(retval); break; case 2: retval = (int32_t)(int16_t)(retval); break; } if (error < 0 || pcierror != 0) retval = 0xffffffff; return (retval); } static void rtaspci_write_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, uint32_t val, int width) { struct rtaspci_softc *sc; uint32_t config_addr; int pcierror; sc = device_get_softc(dev); config_addr = ((bus & 0xff) << 16) | ((slot & 0x1f) << 11) | ((func & 0x7) << 8) | (reg & 0xff); if (sc->sc_extended_config) config_addr |= (reg & 0xf00) << 16; if (sc->ex_write_pci_config != -1) rtas_call_method(sc->ex_write_pci_config, 5, 1, config_addr, sc->pci_sc.sc_pcir.phys_hi, sc->pci_sc.sc_pcir.phys_mid, width, val, &pcierror); else rtas_call_method(sc->write_pci_config, 3, 1, config_addr, width, val, &pcierror); }