void arch_set_itlb(void* vaddr, void* paddr) { unsigned int flags = ITLB_PR_NOLIMIT; unsigned int mr = ((unsigned int)vaddr & SPR_ITLBMR_VPN) | SPR_ITLBMR_V; unsigned int tr = ((unsigned int)paddr & SPR_ITLBTR_PPN) | (flags & ITLB_PR_MASK) | SPR_ITLBTR_A | SPR_ITLBTR_D; unsigned int set = ((((unsigned int)vaddr) >> PAGE_BITS) & 63); or1k_mtspr (SPR_ITLBMR_BASE(0)+set, mr); or1k_mtspr (SPR_ITLBTR_BASE(0)+set, tr); }
void flush_tlb_all(void) { int i; unsigned long num_tlb_sets; /* Determine number of sets for IMMU. */ /* FIXME: Assumption is I & D nsets equal. */ num_tlb_sets = NUM_ITLB_SETS; for (i = 0; i < num_tlb_sets; i++) { mtspr_off(SPR_DTLBMR_BASE(0), i, 0); mtspr_off(SPR_ITLBMR_BASE(0), i, 0); } }
void tlb_invalidate_all () { bool intr_flag; local_intr_save (intr_flag); int i; for (i = 0; i < NR_SETS; i++) { mtspr (SPR_DTLBMR_BASE(0) + i, 0); mtspr (SPR_ITLBMR_BASE(0) + i, 0); asm ("l.nop;l.nop;"); } local_intr_restore (intr_flag); }
/* ITLB miss exception handler */ void itlb_miss_handler (void) { unsigned long ea; int set, way = 0; int i; /* Get EA that cause the exception */ ea = mfspr (SPR_EEAR_BASE); /* Find TLB set and LRU way */ set = (ea / PAGE_SIZE) % ITLB_SETS; for (i = 0; i < ITLB_WAYS; i++) { if ((mfspr (SPR_ITLBMR_BASE(i) + set) & SPR_ITLBMR_LRU) == 0) { way = i; break; } } mtspr (SPR_ITLBMR_BASE(way) + set, (ea & SPR_ITLBMR_VPN) | SPR_ITLBMR_V); mtspr (SPR_ITLBTR_BASE(way) + set, (ea & SPR_ITLBTR_PPN) | itlb_val); except_mask |= 1 << V_ITLB_MISS; except_count++; }
/* Intstruction page fault exception handler */ void ipage_fault_handler (void) { unsigned long ea; int set, way = 0; int i; /* Get EA that cause the exception */ ea = mfspr (SPR_EEAR_BASE); /* Find TLB set and way */ set = (ea / PAGE_SIZE) % ITLB_SETS; for (i = 0; i < ITLB_WAYS; i++) { if ((mfspr (SPR_ITLBMR_BASE(i) + set) & SPR_ITLBMR_VPN) == (ea & SPR_ITLBMR_VPN)) { way = i; break; } } /* Give permission */ mtspr (SPR_ITLBTR_BASE(way) + set, (mfspr (SPR_ITLBTR_BASE(way) + set) & ~ITLB_PR_NOLIMIT) | itlb_val); except_mask |= 1 << V_IPF; except_count++; }
//check_boot_pgdir (); #endif print_pgdir (kprintf); slab_init (); } // invalidate a TLB entry, but only if the page tables being // edited are the ones currently in use by the processor. void tlb_update (pde_t *pgdir, uintptr_t la) { uint32_t set = (la >> PGSHIFT) & (NR_SETS - 1); mtspr (SPR_DTLBMR_BASE(0) + set, 0); mtspr (SPR_ITLBMR_BASE(0) + set, 0); asm ("l.nop;l.nop;"); } void tlb_invalidate(pde_t *pgdir, uintptr_t la) { uint32_t set = (la >> PGSHIFT) & (NR_SETS - 1); mtspr (SPR_DTLBMR_BASE(0) + set, 0); mtspr (SPR_ITLBMR_BASE(0) + set, 0); asm ("l.nop;l.nop;"); } void check_boot_pgdir(void) { pte_t *ptep; int i;
/* Exception priority test */ void except_priority_test (void) { int i, j; unsigned long ea, ta, ret; /* Invalidate all entries in ITLB */ for (i = 0; i < ITLB_WAYS; i++) { for (j = 0; j < ITLB_SETS; j++) { mtspr (SPR_ITLBMR_BASE(i) + j, 0); mtspr (SPR_ITLBTR_BASE(i) + j, 0); } } /* Set one to one translation for the use of this program */ for (i = 0; i < TLB_TEXT_SET_NB; i++) { ea = RAM_START + (i*PAGE_SIZE); ta = RAM_START + (i*PAGE_SIZE); mtspr (SPR_ITLBMR_BASE(0) + i, ea | SPR_ITLBMR_V); mtspr (SPR_ITLBTR_BASE(0) + i, ta | ITLB_PR_NOLIMIT); } /* Set dtlb no permisions */ itlb_val = SPR_ITLBTR_CI; /* Invalidate all entries in DTLB */ for (i = 0; i < DTLB_WAYS; i++) { for (j = 0; j < DTLB_SETS; j++) { mtspr (SPR_DTLBMR_BASE(i) + j, 0); mtspr (SPR_DTLBTR_BASE(i) + j, 0); } } /* Set one to one translation for the use of this program */ for (i = 0; i < TLB_DATA_SET_NB; i++) { ea = RAM_START + (i*PAGE_SIZE); ta = RAM_START + (i*PAGE_SIZE); mtspr (SPR_DTLBMR_BASE(0) + i, ea | SPR_ITLBMR_V); mtspr (SPR_DTLBTR_BASE(0) + i, ta | DTLB_PR_NOLIMIT); } /* Init tick timer */ tick_init (1, 1); /* Set dtlb no permisions */ dtlb_val = SPR_DTLBTR_CI; /* Reset except counter */ except_count = 0; except_mask = 0; except_pc = 0; except_ea = 0; /* Enable IMMU */ immu_enable (); /* The following is currently disabled due to differing behavior between or1ksim and the OR1200 RTL. Or1ksim appears to receive only 1 exception during the call_with_int() call. The OR1200 correctly, in my opionion, reports 2 exceptions - ITLB miss and tick timer. -- Julius TODO: Investigate why or1ksim isn't reporting ITLB miss. */ #if 0 /* Check if there was INT exception */ call_with_int (RAM_START + (RAM_SIZE) + (TLB_TEXT_SET_NB*PAGE_SIZE), 0); printf("ec:%d 0x%lx\n",except_count,except_mask); ASSERT(except_count == 2); ASSERT(except_mask == ((1 << V_TICK) | (1 << V_ITLB_MISS))); printf("epc %8lx\n",except_pc); ASSERT(except_pc == (RAM_START + (RAM_SIZE) + (TLB_TEXT_SET_NB*PAGE_SIZE))); /* Reset except counter */ except_count = 0; except_mask = 0; except_pc = 0; except_ea = 0; #endif /* Check if there was ITLB exception */ call (RAM_START + (RAM_SIZE) + (TLB_TEXT_SET_NB*PAGE_SIZE), 0); ASSERT(except_count == 1); ASSERT(except_mask == (1 << V_ITLB_MISS)); ASSERT(except_pc == (RAM_START + (RAM_SIZE) + (TLB_TEXT_SET_NB*PAGE_SIZE))); ASSERT(except_ea == (RAM_START + (RAM_SIZE) + (TLB_TEXT_SET_NB*PAGE_SIZE))); /* Set dtlb permisions */ itlb_val |= SPR_ITLBTR_SXE; /* Reset except counter */ except_count = 0; except_mask = 0; except_pc = 0; except_ea = 0; /* Check if there was IPF exception */ call (RAM_START + (RAM_SIZE) + (TLB_TEXT_SET_NB*PAGE_SIZE), 0); ASSERT(except_count == 1); ASSERT(except_mask == (1 << V_IPF)); ASSERT(except_pc == (RAM_START + (RAM_SIZE) + (TLB_TEXT_SET_NB*PAGE_SIZE))); ASSERT(except_ea == (RAM_START + (RAM_SIZE) + (TLB_TEXT_SET_NB*PAGE_SIZE))); /* Reset except counter */ except_count = 0; except_mask = 0; except_pc = 0; except_ea = 0; /* Disable MMU */ immu_disable (); /* Set illegal instruction. JPB. Use a really illegal instruction, not l.cust8 0x3ffffff. */ REG32(RAM_START + (RAM_SIZE/2) + (TLB_TEXT_SET_NB*PAGE_SIZE) + 0) = 0x00000000; REG32(RAM_START + (RAM_SIZE/2) + (TLB_TEXT_SET_NB*PAGE_SIZE) + 4) = 0xe8000000; REG32(RAM_START + (RAM_SIZE/2) + (TLB_TEXT_SET_NB*PAGE_SIZE) + 8) = 0x00000000; /* Check if there was illegal insn exception */ call (RAM_START + (RAM_SIZE/2) + (TLB_TEXT_SET_NB*PAGE_SIZE) + 4, 0); ASSERT(except_count == 1); ASSERT(except_mask == (1 << V_ILLINSN)); ASSERT(except_pc == (RAM_START + (RAM_SIZE/2) + (TLB_TEXT_SET_NB*PAGE_SIZE) + 4)); ASSERT(except_ea == (RAM_START + (RAM_SIZE/2) + (TLB_TEXT_SET_NB*PAGE_SIZE) + 4 )); /* Reset except counter */ except_count = 0; except_mask = 0; except_pc = 0; except_ea = 0; /* Enable DMMU */ dmmu_enable (); /* Check if there was alignment exception on read insn */ ret = call ((unsigned long)&load_acc_32, RAM_START + (RAM_SIZE) + (TLB_DATA_SET_NB*PAGE_SIZE) + 1); ASSERT(except_count == 1); ASSERT(except_mask == (1 << V_ALIGN)); ASSERT(ret == 0x12345678); ASSERT(except_pc == ((unsigned long)(load_acc_32) + 8)); ASSERT(except_ea == (RAM_START + (RAM_SIZE) + (TLB_DATA_SET_NB*PAGE_SIZE + 1))); /* Reset except counter */ except_count = 0; except_mask = 0; except_pc = 0; except_ea = 0; /* Check if there was DTLB exception */ ret = call ((unsigned long)&load_acc_32, RAM_START + (RAM_SIZE) + (TLB_DATA_SET_NB*PAGE_SIZE)); ASSERT(except_count == 1); ASSERT(except_mask == (1 << V_DTLB_MISS)); ASSERT(ret == 0x12345678); ASSERT(except_pc == ((unsigned long)(load_acc_32) + 8)); ASSERT(except_ea == (RAM_START + (RAM_SIZE) + (TLB_DATA_SET_NB*PAGE_SIZE))); /* Reset except counter */ except_count = 0; except_mask = 0; except_pc = 0; except_ea = 0; /* Set dtlb permisions */ dtlb_val |= SPR_DTLBTR_SRE; /* Check if there was DPF exception */ ret = call ((unsigned long)&load_acc_32, RAM_START + (RAM_SIZE) + (TLB_DATA_SET_NB*PAGE_SIZE)); ASSERT(except_count == 1); ASSERT(except_mask == (1 << V_DPF)); ASSERT(ret == 0x12345678); ASSERT(except_pc == ((unsigned long)(load_acc_32) + 8)); ASSERT(except_ea == (RAM_START + (RAM_SIZE) + (TLB_DATA_SET_NB*PAGE_SIZE))); /* Reset except counter */ except_count = 0; except_mask = 0; except_pc = 0; except_ea = 0; /* Check if there was trap exception */ call ((unsigned long)&trap, 0); ASSERT(except_count == 1); ASSERT(except_mask == (1 << V_TRAP)); ASSERT(except_pc == (unsigned long)(trap)); }
/* Bus error test */ int buserr_test (void) { int i, j, ret; unsigned long ea, ta; /* Invalidate all entries in ITLB */ for (i = 0; i < ITLB_WAYS; i++) { for (j = 0; j < ITLB_SETS; j++) { mtspr (SPR_ITLBMR_BASE(i) + j, 0); mtspr (SPR_ITLBTR_BASE(i) + j, 0); } } /* Set one to one translation for the use of this program */ for (i = 0; i < TLB_TEXT_SET_NB; i++) { ea = RAM_START + (i*PAGE_SIZE); ta = RAM_START + (i*PAGE_SIZE); mtspr (SPR_ITLBMR_BASE(0) + i, ea | SPR_ITLBMR_V); mtspr (SPR_ITLBTR_BASE(0) + i, ta | ITLB_PR_NOLIMIT); } /* Invalidate all entries in DTLB */ for (i = 0; i < DTLB_WAYS; i++) { for (j = 0; j < DTLB_SETS; j++) { mtspr (SPR_DTLBMR_BASE(i) + j, 0); mtspr (SPR_DTLBTR_BASE(i) + j, 0); } } /* Set one to one translation for the use of this program */ for (i = 0; i < TLB_DATA_SET_NB; i++) { ea = RAM_START + (i*PAGE_SIZE); ta = RAM_START + (i*PAGE_SIZE); mtspr (SPR_DTLBMR_BASE(0) + i, ea | SPR_ITLBMR_V); mtspr (SPR_DTLBTR_BASE(0) + i, ta | DTLB_PR_NOLIMIT); } /* Reset except counter */ except_count = 0; except_mask = 0; except_pc = 0; except_ea = 0; /* Set IMMU translation */ ea = RAM_START + (RAM_SIZE) + ((TLB_TEXT_SET_NB)*PAGE_SIZE); itlb_val = SPR_ITLBTR_CI | SPR_ITLBTR_SXE; mtspr (SPR_ITLBMR_BASE(0) + TLB_TEXT_SET_NB, (ea & SPR_ITLBMR_VPN) | SPR_ITLBMR_V); // Set translate to invalid address: 0xee000000 mtspr (SPR_ITLBTR_BASE(0) + TLB_TEXT_SET_NB, (0xee000000 & SPR_ITLBTR_PPN) | itlb_val); /* Enable IMMU */ immu_enable (); /* Check if there was bus error exception */ ret = call (ea, 0); ASSERT(except_count == 1); ASSERT(except_mask == (1 << V_BERR)); ASSERT(except_pc == ea); ASSERT(except_ea == ea); /* Disable IMMU */ immu_disable (); /* Reset except counter */ except_count = 0; except_mask = 0; except_pc = 0; except_ea = 0; /* Set EA as an invalid memory location */ ea = 0xee000000; /* Check if there was bus error exception */ ret = call (ea, 0); ASSERT(except_count == 1); ASSERT(except_mask == (1 << V_BERR)); ASSERT(except_pc == ea); ASSERT(except_ea == ea); /* Reset except counter */ except_count = 0; except_mask = 0; except_pc = 0; except_ea = 0; /* Set DMMU translation */ ea = RAM_START + (RAM_SIZE) + ((TLB_DATA_SET_NB)*PAGE_SIZE); dtlb_val = SPR_DTLBTR_CI | SPR_DTLBTR_SRE; mtspr (SPR_DTLBMR_BASE(0) + TLB_DATA_SET_NB, (ea & SPR_DTLBMR_VPN) | SPR_DTLBMR_V); // Set translate to invalid address: 0xee000000 mtspr (SPR_DTLBTR_BASE(0) + TLB_DATA_SET_NB, (0xee000000 & SPR_DTLBTR_PPN) | dtlb_val); /* Enable DMMU */ dmmu_enable (); /* Check if there was bus error exception */ ret = call ((unsigned long)&load_acc_32, ea ); ASSERT(except_count == 1); ASSERT(except_mask == (1 << V_BERR)); ASSERT(except_pc == (unsigned long)load_acc_32 + 8); ASSERT(except_ea == ea); ASSERT(ret == 0x12345678); /* Disable DMMU */ dmmu_disable (); /* Reset except counter */ except_count = 0; except_mask = 0; except_pc = 0; except_ea = 0; // Set ea to invalid address ea = 0xee000000; /* Check if there was bus error exception */ ret = call ((unsigned long)&load_acc_32, ea ); ASSERT(except_count == 1); ASSERT(except_mask == (1 << V_BERR)); ASSERT(except_pc == (unsigned long)load_acc_32 + 8); ASSERT(except_ea == ea); ASSERT(ret == 0x12345678); return 0; }
/* ITLB miss test */ int itlb_test (void) { int i, j, ret; unsigned long ea, ta; /* Invalidate all entries in ITLB */ for (i = 0; i < ITLB_WAYS; i++) { for (j = 0; j < ITLB_SETS; j++) { mtspr (SPR_ITLBMR_BASE(i) + j, 0); mtspr (SPR_ITLBTR_BASE(i) + j, 0); } } /* Set one to one translation for the use of this program */ for (i = 0; i < TLB_TEXT_SET_NB; i++) { ea = RAM_START + (i*PAGE_SIZE); ta = RAM_START + (i*PAGE_SIZE); mtspr (SPR_ITLBMR_BASE(0) + i, ea | SPR_ITLBMR_V); mtspr (SPR_ITLBTR_BASE(0) + i, ta | ITLB_PR_NOLIMIT); } /* Set dtlb no permisions */ itlb_val = SPR_ITLBTR_CI; /* Reset except counter */ except_count = 0; except_mask = 0; except_pc = 0; except_ea = 0; /* Enable IMMU */ immu_enable (); /* Copy jump instruction to last location of a page */ ea = RAM_START + (RAM_SIZE/2) + ((TLB_TEXT_SET_NB + 1)*PAGE_SIZE) - 8; memcpy((void *)ea, (void *)&jump_back, 12); /* Check if there was ITLB miss exception */ ret = call (ea, 0); ASSERT(except_count == 1); ASSERT(except_mask == (1 << V_ITLB_MISS)); ASSERT(except_pc == ea); ASSERT(ret == 0); /* Set dtlb no permisions */ itlb_val = SPR_ITLBTR_CI | SPR_ITLBTR_SXE; /* Reset except counter */ except_count = 0; except_mask = 0; except_pc = 0; except_ea = 0; /* Check if there was IPF miss exception */ ret = call (ea, 0); ASSERT(except_count == 1); ASSERT(except_mask == (1 << V_IPF)); ASSERT(except_pc == ea); ASSERT(ret == 0); /* Set dtlb no permisions */ itlb_val = SPR_ITLBTR_CI; /* Reset except counter */ except_count = 0; except_mask = 0; except_pc = 0; except_ea = 0; /* Check if there was ITLB miss exception */ ret = call (ea, 0); ASSERT(except_count == 1); ASSERT(except_mask == (1 << V_ITLB_MISS)); #ifndef __OR1K_NODELAY__ ASSERT(except_pc == ea + 4); ASSERT(ret == 0); #else ASSERT(except_pc == ea + 8); ASSERT(ret == 1); #endif /* Set dtlb no permisions */ itlb_val = SPR_ITLBTR_CI | SPR_ITLBTR_SXE; /* Reset except counter */ except_count = 0; except_mask = 0; except_pc = 0; except_ea = 0; /* Check if there was IPF exception */ ret = call (ea, 0); ASSERT(except_count == 1); ASSERT(except_mask == (1 << V_IPF)); #ifndef __OR1K_NODELAY__ ASSERT(except_pc == ea + 4); ASSERT(ret == 0); #else ASSERT(except_pc == ea + 8); ASSERT(ret == 1); #endif /* Reset except counter */ except_count = 0; except_mask = 0; except_pc = 0; except_ea = 0; ret = call (ea, 0); ASSERT(except_count == 0); ASSERT(ret == 1); /* Disable IMMU */ immu_disable (); return 0; }