📄 hash_utils_64.c
字号:
}#ifdef CONFIG_MEMORY_HOTPLUGvoid create_section_mapping(unsigned long start, unsigned long end){ BUG_ON(htab_bolt_mapping(start, end, start, _PAGE_ACCESSED | _PAGE_DIRTY | _PAGE_COHERENT | PP_RWXX, mmu_linear_psize));}#endif /* CONFIG_MEMORY_HOTPLUG */void __init htab_initialize(void){ unsigned long table, htab_size_bytes; unsigned long pteg_count; unsigned long mode_rw; unsigned long base = 0, size = 0; int i; extern unsigned long tce_alloc_start, tce_alloc_end; DBG(" -> htab_initialize()\n"); /* Initialize page sizes */ htab_init_page_sizes(); /* * Calculate the required size of the htab. We want the number of * PTEGs to equal one half the number of real pages. */ htab_size_bytes = htab_get_table_size(); pteg_count = htab_size_bytes >> 7; htab_hash_mask = pteg_count - 1; if (platform_is_lpar()) { /* Using a hypervisor which owns the htab */ htab_address = NULL; _SDR1 = 0; } else { /* Find storage for the HPT. Must be contiguous in * the absolute address space. */ table = lmb_alloc(htab_size_bytes, htab_size_bytes); BUG_ON(table == 0); DBG("Hash table allocated at %lx, size: %lx\n", table, htab_size_bytes); htab_address = abs_to_virt(table); /* htab absolute addr + encoded htabsize */ _SDR1 = table + __ilog2(pteg_count) - 11; /* Initialize the HPT with no entries */ memset((void *)table, 0, htab_size_bytes); /* Set SDR1 */ mtspr(SPRN_SDR1, _SDR1); } mode_rw = _PAGE_ACCESSED | _PAGE_DIRTY | _PAGE_COHERENT | PP_RWXX; /* On U3 based machines, we need to reserve the DART area and * _NOT_ map it to avoid cache paradoxes as it's remapped non * cacheable later on */ /* create bolted the linear mapping in the hash table */ for (i=0; i < lmb.memory.cnt; i++) { base = lmb.memory.region[i].base + KERNELBASE; size = lmb.memory.region[i].size; DBG("creating mapping for region: %lx : %lx\n", base, size);#ifdef CONFIG_U3_DART /* Do not map the DART space. Fortunately, it will be aligned * in such a way that it will not cross two lmb regions and * will fit within a single 16Mb page. * The DART space is assumed to be a full 16Mb region even if * we only use 2Mb of that space. We will use more of it later * for AGP GART. We have to use a full 16Mb large page. */ DBG("DART base: %lx\n", dart_tablebase); if (dart_tablebase != 0 && dart_tablebase >= base && dart_tablebase < (base + size)) { if (base != dart_tablebase) BUG_ON(htab_bolt_mapping(base, dart_tablebase, base, mode_rw, mmu_linear_psize)); if ((base + size) > (dart_tablebase + 16*MB)) BUG_ON(htab_bolt_mapping(dart_tablebase+16*MB, base + size, dart_tablebase+16*MB, mode_rw, mmu_linear_psize)); continue; }#endif /* CONFIG_U3_DART */ BUG_ON(htab_bolt_mapping(base, base + size, base, mode_rw, mmu_linear_psize)); } /* * If we have a memory_limit and we've allocated TCEs then we need to * explicitly map the TCE area at the top of RAM. We also cope with the * case that the TCEs start below memory_limit. * tce_alloc_start/end are 16MB aligned so the mapping should work * for either 4K or 16MB pages. */ if (tce_alloc_start) { tce_alloc_start += KERNELBASE; tce_alloc_end += KERNELBASE; if (base + size >= tce_alloc_start) tce_alloc_start = base + size + 1; BUG_ON(htab_bolt_mapping(tce_alloc_start, tce_alloc_end, tce_alloc_start, mode_rw, mmu_linear_psize)); } DBG(" <- htab_initialize()\n");}#undef KB#undef MBvoid htab_initialize_secondary(void){ if (!platform_is_lpar()) mtspr(SPRN_SDR1, _SDR1);}/* * Called by asm hashtable.S for doing lazy icache flush */unsigned int hash_page_do_lazy_icache(unsigned int pp, pte_t pte, int trap){ struct page *page; if (!pfn_valid(pte_pfn(pte))) return pp; page = pte_page(pte); /* page is dirty */ if (!test_bit(PG_arch_1, &page->flags) && !PageReserved(page)) { if (trap == 0x400) { __flush_dcache_icache(page_address(page)); set_bit(PG_arch_1, &page->flags); } else pp |= HPTE_R_N; } return pp;}/* Result code is: * 0 - handled * 1 - normal page fault * -1 - critical hash insertion error */int hash_page(unsigned long ea, unsigned long access, unsigned long trap){ void *pgdir; unsigned long vsid; struct mm_struct *mm; pte_t *ptep; cpumask_t tmp; int rc, user_region = 0, local = 0; DBG_LOW("hash_page(ea=%016lx, access=%lx, trap=%lx\n", ea, access, trap); if ((ea & ~REGION_MASK) >= PGTABLE_RANGE) { DBG_LOW(" out of pgtable range !\n"); return 1; } /* Get region & vsid */ switch (REGION_ID(ea)) { case USER_REGION_ID: user_region = 1; mm = current->mm; if (! mm) { DBG_LOW(" user region with no mm !\n"); return 1; } vsid = get_vsid(mm->context.id, ea); break; case VMALLOC_REGION_ID: mm = &init_mm; vsid = get_kernel_vsid(ea); break; default: /* Not a valid range * Send the problem up to do_page_fault */ return 1; } DBG_LOW(" mm=%p, mm->pgdir=%p, vsid=%016lx\n", mm, mm->pgd, vsid); /* Get pgdir */ pgdir = mm->pgd; if (pgdir == NULL) return 1; /* Check CPU locality */ tmp = cpumask_of_cpu(smp_processor_id()); if (user_region && cpus_equal(mm->cpu_vm_mask, tmp)) local = 1; /* Handle hugepage regions */ if (unlikely(in_hugepage_area(mm->context, ea))) { DBG_LOW(" -> huge page !\n"); return hash_huge_page(mm, access, ea, vsid, local, trap); } /* Get PTE and page size from page tables */ ptep = find_linux_pte(pgdir, ea); if (ptep == NULL || !pte_present(*ptep)) { DBG_LOW(" no PTE !\n"); return 1; }#ifndef CONFIG_PPC_64K_PAGES DBG_LOW(" i-pte: %016lx\n", pte_val(*ptep));#else DBG_LOW(" i-pte: %016lx %016lx\n", pte_val(*ptep), pte_val(*(ptep + PTRS_PER_PTE)));#endif /* Pre-check access permissions (will be re-checked atomically * in __hash_page_XX but this pre-check is a fast path */ if (access & ~pte_val(*ptep)) { DBG_LOW(" no access !\n"); return 1; } /* Do actual hashing */#ifndef CONFIG_PPC_64K_PAGES rc = __hash_page_4K(ea, access, vsid, ptep, trap, local);#else if (mmu_virtual_psize == MMU_PAGE_64K) rc = __hash_page_64K(ea, access, vsid, ptep, trap, local); else rc = __hash_page_4K(ea, access, vsid, ptep, trap, local);#endif /* CONFIG_PPC_64K_PAGES */#ifndef CONFIG_PPC_64K_PAGES DBG_LOW(" o-pte: %016lx\n", pte_val(*ptep));#else DBG_LOW(" o-pte: %016lx %016lx\n", pte_val(*ptep), pte_val(*(ptep + PTRS_PER_PTE)));#endif DBG_LOW(" -> rc=%d\n", rc); return rc;}void hash_preload(struct mm_struct *mm, unsigned long ea, unsigned long access, unsigned long trap){ unsigned long vsid; void *pgdir; pte_t *ptep; cpumask_t mask; unsigned long flags; int local = 0; /* We don't want huge pages prefaulted for now */ if (unlikely(in_hugepage_area(mm->context, ea))) return; DBG_LOW("hash_preload(mm=%p, mm->pgdir=%p, ea=%016lx, access=%lx," " trap=%lx\n", mm, mm->pgd, ea, access, trap); /* Get PTE, VSID, access mask */ pgdir = mm->pgd; if (pgdir == NULL) return; ptep = find_linux_pte(pgdir, ea); if (!ptep) return; vsid = get_vsid(mm->context.id, ea); /* Hash it in */ local_irq_save(flags); mask = cpumask_of_cpu(smp_processor_id()); if (cpus_equal(mm->cpu_vm_mask, mask)) local = 1;#ifndef CONFIG_PPC_64K_PAGES __hash_page_4K(ea, access, vsid, ptep, trap, local);#else if (mmu_virtual_psize == MMU_PAGE_64K) __hash_page_64K(ea, access, vsid, ptep, trap, local); else __hash_page_4K(ea, access, vsid, ptep, trap, local);#endif /* CONFIG_PPC_64K_PAGES */ local_irq_restore(flags);}void flush_hash_page(unsigned long va, real_pte_t pte, int psize, int local){ unsigned long hash, index, shift, hidx, slot; DBG_LOW("flush_hash_page(va=%016x)\n", va); pte_iterate_hashed_subpages(pte, psize, va, index, shift) { hash = hpt_hash(va, shift); hidx = __rpte_to_hidx(pte, index); if (hidx & _PTEIDX_SECONDARY) hash = ~hash; slot = (hash & htab_hash_mask) * HPTES_PER_GROUP; slot += hidx & _PTEIDX_GROUP_IX; DBG_LOW(" sub %d: hash=%x, hidx=%x\n", index, slot, hidx); ppc_md.hpte_invalidate(slot, va, psize, local); } pte_iterate_hashed_end();}void flush_hash_range(unsigned long number, int local){ if (ppc_md.flush_hash_range) ppc_md.flush_hash_range(number, local); else { int i; struct ppc64_tlb_batch *batch = &__get_cpu_var(ppc64_tlb_batch); for (i = 0; i < number; i++) flush_hash_page(batch->vaddr[i], batch->pte[i], batch->psize, local); }}static inline void make_bl(unsigned int *insn_addr, void *func){ unsigned long funcp = *((unsigned long *)func); int offset = funcp - (unsigned long)insn_addr; *insn_addr = (unsigned int)(0x48000001 | (offset & 0x03fffffc)); flush_icache_range((unsigned long)insn_addr, 4+ (unsigned long)insn_addr);}/* * low_hash_fault is called when we the low level hash code failed * to instert a PTE due to an hypervisor error */void low_hash_fault(struct pt_regs *regs, unsigned long address){ if (user_mode(regs)) { siginfo_t info; info.si_signo = SIGBUS; info.si_errno = 0; info.si_code = BUS_ADRERR; info.si_addr = (void __user *)address; force_sig_info(SIGBUS, &info, current); return; } bad_page_fault(regs, address, SIGBUS);}void __init htab_finish_init(void){ extern unsigned int *htab_call_hpte_insert1; extern unsigned int *htab_call_hpte_insert2; extern unsigned int *htab_call_hpte_remove; extern unsigned int *htab_call_hpte_updatepp;#ifdef CONFIG_PPC_64K_PAGES extern unsigned int *ht64_call_hpte_insert1; extern unsigned int *ht64_call_hpte_insert2; extern unsigned int *ht64_call_hpte_remove; extern unsigned int *ht64_call_hpte_updatepp; make_bl(ht64_call_hpte_insert1, ppc_md.hpte_insert); make_bl(ht64_call_hpte_insert2, ppc_md.hpte_insert); make_bl(ht64_call_hpte_remove, ppc_md.hpte_remove); make_bl(ht64_call_hpte_updatepp, ppc_md.hpte_updatepp);#endif /* CONFIG_PPC_64K_PAGES */ make_bl(htab_call_hpte_insert1, ppc_md.hpte_insert); make_bl(htab_call_hpte_insert2, ppc_md.hpte_insert); make_bl(htab_call_hpte_remove, ppc_md.hpte_remove); make_bl(htab_call_hpte_updatepp, ppc_md.hpte_updatepp);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -