📄 fault.c
字号:
die("Oops", regs, error_code); bust_spinlocks(0); do_exit(SIGKILL);/* * We ran out of memory, or some other thing happened to us that made * us unable to handle the page fault gracefully. */out_of_memory: up_read(&mm->mmap_sem); if (tsk->pid == 1) { yield(); down_read(&mm->mmap_sem); goto survive; } printk("VM: killing process %s\n", tsk->comm); if (error_code & ACE_USERMODE) do_exit(SIGKILL); goto no_context;do_sigbus: up_read(&mm->mmap_sem); /* Kernel mode? Handle exception or die */ if (!(error_code & ACE_USERMODE)) goto no_context; tsk->thread.address = address; tsk->thread.error_code = error_code; tsk->thread.trap_no = 14; info.si_signo = SIGBUS; info.si_errno = 0; info.si_code = BUS_ADRERR; info.si_addr = (void __user *)address; force_sig_info(SIGBUS, &info, tsk); return;vmalloc_fault: { /* * Synchronize this task's top level page-table * with the 'reference' page table. * * Do _not_ use "tsk" here. We might be inside * an interrupt in the middle of a task switch.. */ int offset = pgd_index(address); pgd_t *pgd, *pgd_k; pmd_t *pmd, *pmd_k; pte_t *pte_k; pgd = (pgd_t *)*(unsigned long *)MPTB; pgd = offset + (pgd_t *)pgd; pgd_k = init_mm.pgd + offset; if (!pgd_present(*pgd_k)) goto no_context; /* * set_pgd(pgd, *pgd_k); here would be useless on PAE * and redundant with the set_pmd() on non-PAE. */ pmd = pmd_offset(pgd, address); pmd_k = pmd_offset(pgd_k, address); if (!pmd_present(*pmd_k)) goto no_context; set_pmd(pmd, *pmd_k); pte_k = pte_offset_kernel(pmd_k, address); if (!pte_present(*pte_k)) goto no_context; addr = (address & PAGE_MASK) | (error_code & ACE_INSTRUCTION); update_mmu_cache(NULL, addr, *pte_k); return; }}/*======================================================================* * update_mmu_cache() *======================================================================*/#define TLB_MASK (NR_TLB_ENTRIES - 1)#define ITLB_END (unsigned long *)(ITLB_BASE + (NR_TLB_ENTRIES * 8))#define DTLB_END (unsigned long *)(DTLB_BASE + (NR_TLB_ENTRIES * 8))void update_mmu_cache(struct vm_area_struct *vma, unsigned long vaddr, pte_t pte){ unsigned long *entry1, *entry2; unsigned long pte_data, flags; unsigned int *entry_dat; int inst = get_thread_fault_code() & ACE_INSTRUCTION; int i; /* Ptrace may call this routine. */ if (vma && current->active_mm != vma->vm_mm) return; local_irq_save(flags); vaddr = (vaddr & PAGE_MASK) | get_asid();#ifdef CONFIG_CHIP_OPSP entry1 = (unsigned long *)ITLB_BASE; for(i = 0 ; i < NR_TLB_ENTRIES; i++) { if(*entry1++ == vaddr) { pte_data = pte_val(pte); set_tlb_data(entry1, pte_data); break; } entry1++; } entry2 = (unsigned long *)DTLB_BASE; for(i = 0 ; i < NR_TLB_ENTRIES ; i++) { if(*entry2++ == vaddr) { pte_data = pte_val(pte); set_tlb_data(entry2, pte_data); break; } entry2++; } local_irq_restore(flags); return;#else pte_data = pte_val(pte); /* * Update TLB entries * entry1: ITLB entry address * entry2: DTLB entry address */ __asm__ __volatile__ ( "seth %0, #high(%4) \n\t" "st %2, @(%5, %0) \n\t" "ldi %1, #1 \n\t" "st %1, @(%6, %0) \n\t" "add3 r4, %0, %7 \n\t" ".fillinsn \n" "1: \n\t" "ld %1, @(%6, %0) \n\t" "bnez %1, 1b \n\t" "ld %0, @r4+ \n\t" "ld %1, @r4 \n\t" "st %3, @+%0 \n\t" "st %3, @+%1 \n\t" : "=&r" (entry1), "=&r" (entry2) : "r" (vaddr), "r" (pte_data), "i" (MMU_REG_BASE), "i" (MSVA_offset), "i" (MTOP_offset), "i" (MIDXI_offset) : "r4", "memory" ); if ((!inst && entry2 >= DTLB_END) || (inst && entry1 >= ITLB_END)) goto notfound;found: local_irq_restore(flags); return; /* Valid entry not found */notfound: /* * Update ITLB or DTLB entry * entry1: TLB entry address * entry2: TLB base address */ if (!inst) { entry2 = (unsigned long *)DTLB_BASE; entry_dat = &tlb_entry_d; } else { entry2 = (unsigned long *)ITLB_BASE; entry_dat = &tlb_entry_i; } entry1 = entry2 + (((*entry_dat - 1) & TLB_MASK) << 1); for (i = 0 ; i < NR_TLB_ENTRIES ; i++) { if (!(entry1[1] & 2)) /* Valid bit check */ break; if (entry1 != entry2) entry1 -= 2; else entry1 += TLB_MASK << 1; } if (i >= NR_TLB_ENTRIES) { /* Empty entry not found */ entry1 = entry2 + (*entry_dat << 1); *entry_dat = (*entry_dat + 1) & TLB_MASK; } *entry1++ = vaddr; /* Set TLB tag */ set_tlb_data(entry1, pte_data); goto found;#endif}/*======================================================================* * flush_tlb_page() : flushes one page *======================================================================*/void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page){ if (vma->vm_mm && mm_context(vma->vm_mm) != NO_CONTEXT) { unsigned long flags; local_irq_save(flags); page &= PAGE_MASK; page |= (mm_context(vma->vm_mm) & MMU_CONTEXT_ASID_MASK); __flush_tlb_page(page); local_irq_restore(flags); }}/*======================================================================* * flush_tlb_range() : flushes a range of pages *======================================================================*/void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end){ struct mm_struct *mm; mm = vma->vm_mm; if (mm_context(mm) != NO_CONTEXT) { unsigned long flags; int size; local_irq_save(flags); size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; if (size > (NR_TLB_ENTRIES / 4)) { /* Too many TLB to flush */ mm_context(mm) = NO_CONTEXT; if (mm == current->mm) activate_context(mm); } else { unsigned long asid; asid = mm_context(mm) & MMU_CONTEXT_ASID_MASK; start &= PAGE_MASK; end += (PAGE_SIZE - 1); end &= PAGE_MASK; start |= asid; end |= asid; while (start < end) { __flush_tlb_page(start); start += PAGE_SIZE; } } local_irq_restore(flags); }}/*======================================================================* * flush_tlb_mm() : flushes the specified mm context TLB's *======================================================================*/void local_flush_tlb_mm(struct mm_struct *mm){ /* Invalidate all TLB of this process. */ /* Instead of invalidating each TLB, we get new MMU context. */ if (mm_context(mm) != NO_CONTEXT) { unsigned long flags; local_irq_save(flags); mm_context(mm) = NO_CONTEXT; if (mm == current->mm) activate_context(mm); local_irq_restore(flags); }}/*======================================================================* * flush_tlb_all() : flushes all processes TLBs *======================================================================*/void local_flush_tlb_all(void){ unsigned long flags; local_irq_save(flags); __flush_tlb_all(); local_irq_restore(flags);}/*======================================================================* * init_mmu() *======================================================================*/void __init init_mmu(void){ tlb_entry_i = 0; tlb_entry_d = 0; mmu_context_cache = MMU_CONTEXT_FIRST_VERSION; set_asid(mmu_context_cache & MMU_CONTEXT_ASID_MASK); *(volatile unsigned long *)MPTB = (unsigned long)swapper_pg_dir;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -