]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - arch/mips/mm/tlbex.c
MIPS: Implement Read Inhibit/eXecute Inhibit
[mv-sheeva.git] / arch / mips / mm / tlbex.c
index 4a2907c595694b9bdbd2a791bf5c1e7492551413..0de0e4127d6600743d7a8d453b4fd1278939e428 100644 (file)
@@ -76,6 +76,8 @@ enum label_id {
        label_vmalloc_done,
        label_tlbw_hazard,
        label_split,
+       label_tlbl_goaround1,
+       label_tlbl_goaround2,
        label_nopage_tlbl,
        label_nopage_tlbs,
        label_nopage_tlbm,
@@ -92,6 +94,8 @@ UASM_L_LA(_vmalloc)
 UASM_L_LA(_vmalloc_done)
 UASM_L_LA(_tlbw_hazard)
 UASM_L_LA(_split)
+UASM_L_LA(_tlbl_goaround1)
+UASM_L_LA(_tlbl_goaround2)
 UASM_L_LA(_nopage_tlbl)
 UASM_L_LA(_nopage_tlbs)
 UASM_L_LA(_nopage_tlbm)
@@ -396,36 +400,60 @@ static void __cpuinit build_tlb_write_entry(u32 **p, struct uasm_label **l,
        }
 }
 
-#ifdef CONFIG_HUGETLB_PAGE
-static __cpuinit void build_huge_tlb_write_entry(u32 **p,
-                                                struct uasm_label **l,
-                                                struct uasm_reloc **r,
-                                                unsigned int tmp,
-                                                enum tlb_write_entry wmode)
+static __cpuinit __maybe_unused void build_convert_pte_to_entrylo(u32 **p,
+                                                                 unsigned int reg)
 {
-       /* Set huge page tlb entry size */
-       uasm_i_lui(p, tmp, PM_HUGE_MASK >> 16);
-       uasm_i_ori(p, tmp, tmp, PM_HUGE_MASK & 0xffff);
-       uasm_i_mtc0(p, tmp, C0_PAGEMASK);
+       if (kernel_uses_smartmips_rixi) {
+               UASM_i_SRL(p, reg, reg, ilog2(_PAGE_NO_EXEC));
+               UASM_i_ROTR(p, reg, reg, ilog2(_PAGE_GLOBAL) - ilog2(_PAGE_NO_EXEC));
+       } else {
+#ifdef CONFIG_64BIT_PHYS_ADDR
+               uasm_i_dsrl(p, reg, reg, ilog2(_PAGE_GLOBAL));
+#else
+               UASM_i_SRL(p, reg, reg, ilog2(_PAGE_GLOBAL));
+#endif
+       }
+}
 
-       build_tlb_write_entry(p, l, r, wmode);
+#ifdef CONFIG_HUGETLB_PAGE
 
+static __cpuinit void build_restore_pagemask(u32 **p,
+                                            struct uasm_reloc **r,
+                                            unsigned int tmp,
+                                            enum label_id lid)
+{
        /* Reset default page size */
        if (PM_DEFAULT_MASK >> 16) {
                uasm_i_lui(p, tmp, PM_DEFAULT_MASK >> 16);
                uasm_i_ori(p, tmp, tmp, PM_DEFAULT_MASK & 0xffff);
-               uasm_il_b(p, r, label_leave);
+               uasm_il_b(p, r, lid);
                uasm_i_mtc0(p, tmp, C0_PAGEMASK);
        } else if (PM_DEFAULT_MASK) {
                uasm_i_ori(p, tmp, 0, PM_DEFAULT_MASK);
-               uasm_il_b(p, r, label_leave);
+               uasm_il_b(p, r, lid);
                uasm_i_mtc0(p, tmp, C0_PAGEMASK);
        } else {
-               uasm_il_b(p, r, label_leave);
+               uasm_il_b(p, r, lid);
                uasm_i_mtc0(p, 0, C0_PAGEMASK);
        }
 }
 
+static __cpuinit void build_huge_tlb_write_entry(u32 **p,
+                                                struct uasm_label **l,
+                                                struct uasm_reloc **r,
+                                                unsigned int tmp,
+                                                enum tlb_write_entry wmode)
+{
+       /* Set huge page tlb entry size */
+       uasm_i_lui(p, tmp, PM_HUGE_MASK >> 16);
+       uasm_i_ori(p, tmp, tmp, PM_HUGE_MASK & 0xffff);
+       uasm_i_mtc0(p, tmp, C0_PAGEMASK);
+
+       build_tlb_write_entry(p, l, r, wmode);
+
+       build_restore_pagemask(p, r, tmp, label_leave);
+}
+
 /*
  * Check if Huge PTE is present, if so then jump to LABEL.
  */
@@ -459,7 +487,7 @@ static __cpuinit void build_huge_update_entries(u32 **p,
        if (!small_sequence)
                uasm_i_lui(p, tmp, HPAGE_SIZE >> (7 + 16));
 
-       UASM_i_SRL(p, pte, pte, 6); /* convert to entrylo */
+       build_convert_pte_to_entrylo(p, pte);
        UASM_i_MTC0(p, pte, C0_ENTRYLO0); /* load it */
        /* convert to entrylo1 */
        if (small_sequence)
@@ -685,9 +713,17 @@ static void __cpuinit build_update_entries(u32 **p, unsigned int tmp,
        if (cpu_has_64bits) {
                uasm_i_ld(p, tmp, 0, ptep); /* get even pte */
                uasm_i_ld(p, ptep, sizeof(pte_t), ptep); /* get odd pte */
-               uasm_i_dsrl(p, tmp, tmp, 6); /* convert to entrylo0 */
-               UASM_i_MTC0(p, tmp, C0_ENTRYLO0); /* load it */
-               uasm_i_dsrl(p, ptep, ptep, 6); /* convert to entrylo1 */
+               if (kernel_uses_smartmips_rixi) {
+                       UASM_i_SRL(p, tmp, tmp, ilog2(_PAGE_NO_EXEC));
+                       UASM_i_SRL(p, ptep, ptep, ilog2(_PAGE_NO_EXEC));
+                       UASM_i_ROTR(p, tmp, tmp, ilog2(_PAGE_GLOBAL) - ilog2(_PAGE_NO_EXEC));
+                       UASM_i_MTC0(p, tmp, C0_ENTRYLO0); /* load it */
+                       UASM_i_ROTR(p, ptep, ptep, ilog2(_PAGE_GLOBAL) - ilog2(_PAGE_NO_EXEC));
+               } else {
+                       uasm_i_dsrl(p, tmp, tmp, ilog2(_PAGE_GLOBAL)); /* convert to entrylo0 */
+                       UASM_i_MTC0(p, tmp, C0_ENTRYLO0); /* load it */
+                       uasm_i_dsrl(p, ptep, ptep, ilog2(_PAGE_GLOBAL)); /* convert to entrylo1 */
+               }
                UASM_i_MTC0(p, ptep, C0_ENTRYLO1); /* load it */
        } else {
                int pte_off_even = sizeof(pte_t) / 2;
@@ -704,13 +740,23 @@ static void __cpuinit build_update_entries(u32 **p, unsigned int tmp,
        UASM_i_LW(p, ptep, sizeof(pte_t), ptep); /* get odd pte */
        if (r45k_bvahwbug())
                build_tlb_probe_entry(p);
-       UASM_i_SRL(p, tmp, tmp, 6); /* convert to entrylo0 */
-       if (r4k_250MHZhwbug())
-               UASM_i_MTC0(p, 0, C0_ENTRYLO0);
-       UASM_i_MTC0(p, tmp, C0_ENTRYLO0); /* load it */
-       UASM_i_SRL(p, ptep, ptep, 6); /* convert to entrylo1 */
-       if (r45k_bvahwbug())
-               uasm_i_mfc0(p, tmp, C0_INDEX);
+       if (kernel_uses_smartmips_rixi) {
+               UASM_i_SRL(p, tmp, tmp, ilog2(_PAGE_NO_EXEC));
+               UASM_i_SRL(p, ptep, ptep, ilog2(_PAGE_NO_EXEC));
+               UASM_i_ROTR(p, tmp, tmp, ilog2(_PAGE_GLOBAL) - ilog2(_PAGE_NO_EXEC));
+               if (r4k_250MHZhwbug())
+                       UASM_i_MTC0(p, 0, C0_ENTRYLO0);
+               UASM_i_MTC0(p, tmp, C0_ENTRYLO0); /* load it */
+               UASM_i_ROTR(p, ptep, ptep, ilog2(_PAGE_GLOBAL) - ilog2(_PAGE_NO_EXEC));
+       } else {
+               UASM_i_SRL(p, tmp, tmp, ilog2(_PAGE_GLOBAL)); /* convert to entrylo0 */
+               if (r4k_250MHZhwbug())
+                       UASM_i_MTC0(p, 0, C0_ENTRYLO0);
+               UASM_i_MTC0(p, tmp, C0_ENTRYLO0); /* load it */
+               UASM_i_SRL(p, ptep, ptep, ilog2(_PAGE_GLOBAL)); /* convert to entrylo1 */
+               if (r45k_bvahwbug())
+                       uasm_i_mfc0(p, tmp, C0_INDEX);
+       }
        if (r4k_250MHZhwbug())
                UASM_i_MTC0(p, 0, C0_ENTRYLO1);
        UASM_i_MTC0(p, ptep, C0_ENTRYLO1); /* load it */
@@ -986,9 +1032,14 @@ static void __cpuinit
 build_pte_present(u32 **p, struct uasm_reloc **r,
                  unsigned int pte, unsigned int ptr, enum label_id lid)
 {
-       uasm_i_andi(p, pte, pte, _PAGE_PRESENT | _PAGE_READ);
-       uasm_i_xori(p, pte, pte, _PAGE_PRESENT | _PAGE_READ);
-       uasm_il_bnez(p, r, pte, lid);
+       if (kernel_uses_smartmips_rixi) {
+               uasm_i_andi(p, pte, pte, _PAGE_PRESENT);
+               uasm_il_beqz(p, r, pte, lid);
+       } else {
+               uasm_i_andi(p, pte, pte, _PAGE_PRESENT | _PAGE_READ);
+               uasm_i_xori(p, pte, pte, _PAGE_PRESENT | _PAGE_READ);
+               uasm_il_bnez(p, r, pte, lid);
+       }
        iPTE_LW(p, pte, ptr);
 }
 
@@ -1273,6 +1324,34 @@ static void __cpuinit build_r4000_tlb_load_handler(void)
        build_pte_present(&p, &r, K0, K1, label_nopage_tlbl);
        if (m4kc_tlbp_war())
                build_tlb_probe_entry(&p);
+
+       if (kernel_uses_smartmips_rixi) {
+               /*
+                * If the page is not _PAGE_VALID, RI or XI could not
+                * have triggered it.  Skip the expensive test..
+                */
+               uasm_i_andi(&p, K0, K0, _PAGE_VALID);
+               uasm_il_beqz(&p, &r, K0, label_tlbl_goaround1);
+               uasm_i_nop(&p);
+
+               uasm_i_tlbr(&p);
+               /* Examine  entrylo 0 or 1 based on ptr. */
+               uasm_i_andi(&p, K0, K1, sizeof(pte_t));
+               uasm_i_beqz(&p, K0, 8);
+
+               UASM_i_MFC0(&p, K0, C0_ENTRYLO0); /* load it in the delay slot*/
+               UASM_i_MFC0(&p, K0, C0_ENTRYLO1); /* load it if ptr is odd */
+               /*
+                * If the entryLo (now in K0) is valid (bit 1), RI or
+                * XI must have triggered it.
+                */
+               uasm_i_andi(&p, K0, K0, 2);
+               uasm_il_bnez(&p, &r, K0, label_nopage_tlbl);
+
+               uasm_l_tlbl_goaround1(&l, p);
+               /* Reload the PTE value */
+               iPTE_LW(&p, K0, K1);
+       }
        build_make_valid(&p, &r, K0, K1);
        build_r4000_tlbchange_handler_tail(&p, &l, &r, K0, K1);
 
@@ -1285,6 +1364,40 @@ static void __cpuinit build_r4000_tlb_load_handler(void)
        iPTE_LW(&p, K0, K1);
        build_pte_present(&p, &r, K0, K1, label_nopage_tlbl);
        build_tlb_probe_entry(&p);
+
+       if (kernel_uses_smartmips_rixi) {
+               /*
+                * If the page is not _PAGE_VALID, RI or XI could not
+                * have triggered it.  Skip the expensive test..
+                */
+               uasm_i_andi(&p, K0, K0, _PAGE_VALID);
+               uasm_il_beqz(&p, &r, K0, label_tlbl_goaround2);
+               uasm_i_nop(&p);
+
+               uasm_i_tlbr(&p);
+               /* Examine  entrylo 0 or 1 based on ptr. */
+               uasm_i_andi(&p, K0, K1, sizeof(pte_t));
+               uasm_i_beqz(&p, K0, 8);
+
+               UASM_i_MFC0(&p, K0, C0_ENTRYLO0); /* load it in the delay slot*/
+               UASM_i_MFC0(&p, K0, C0_ENTRYLO1); /* load it if ptr is odd */
+               /*
+                * If the entryLo (now in K0) is valid (bit 1), RI or
+                * XI must have triggered it.
+                */
+               uasm_i_andi(&p, K0, K0, 2);
+               uasm_il_beqz(&p, &r, K0, label_tlbl_goaround2);
+               /* Reload the PTE value */
+               iPTE_LW(&p, K0, K1);
+
+               /*
+                * We clobbered C0_PAGEMASK, restore it.  On the other branch
+                * it is restored in build_huge_tlb_write_entry.
+                */
+               build_restore_pagemask(&p, &r, K0, label_nopage_tlbl);
+
+               uasm_l_tlbl_goaround2(&l, p);
+       }
        uasm_i_ori(&p, K0, K0, (_PAGE_ACCESSED | _PAGE_VALID));
        build_huge_handler_tail(&p, &r, &l, K0, K1);
 #endif