aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/mm
diff options
context:
space:
mode:
authorDavid Daney <david.daney@cavium.com>2014-10-20 18:34:23 -0400
committerRalf Baechle <ralf@linux-mips.org>2014-10-22 13:29:08 -0400
commit9e0f162a36914937a937358fcb45e0609ef2bfc4 (patch)
treeb3d2fcaa7b1c301d1319d2442b5ef6d7ab78cde3 /arch/mips/mm
parentaa08ed55442ac6f9810c055e1474be34e785e556 (diff)
MIPS: tlbex: Properly fix HUGE TLB Refill exception handler
In commit 8393c524a25609 (MIPS: tlbex: Fix a missing statement for HUGETLB), the TLB Refill handler was fixed so that non-OCTEON targets would work properly with huge pages. The change was incorrect in that it broke the OCTEON case. The problem is shown here: xxx0: df7a0000 ld k0,0(k1) . . . xxxc0: df610000 ld at,0(k1) xxxc4: 335a0ff0 andi k0,k0,0xff0 xxxc8: e825ffcd bbit1 at,0x5,0x0 xxxcc: 003ad82d daddu k1,at,k0 . . . In the non-octeon case there is a destructive test for the huge PTE bit, and then at 0, $k0 is reloaded (that is what the 8393c524a25609 patch added). In the octeon case, we modify k1 in the branch delay slot, but we never need k0 again, so the new load is not needed, but since k1 is modified, if we do the load, we load from a garbage location and then get a nested TLB Refill, which is seen in userspace as either SIGBUS or SIGSEGV (depending on the garbage). The real fix is to only do this reloading if it is needed, and never where it is harmful. Signed-off-by: David Daney <david.daney@cavium.com> Cc: Huacai Chen <chenhc@lemote.com> Cc: Fuxin Zhang <zhangfx@lemote.com> Cc: Zhangjin Wu <wuzhangjin@gmail.com> Cc: stable@vger.kernel.org Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/8151/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips/mm')
-rw-r--r--arch/mips/mm/tlbex.c6
1 files changed, 5 insertions, 1 deletions
diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c
index a08dd53a1cc5..b5f228e7eae6 100644
--- a/arch/mips/mm/tlbex.c
+++ b/arch/mips/mm/tlbex.c
@@ -1062,6 +1062,7 @@ static void build_update_entries(u32 **p, unsigned int tmp, unsigned int ptep)
1062struct mips_huge_tlb_info { 1062struct mips_huge_tlb_info {
1063 int huge_pte; 1063 int huge_pte;
1064 int restore_scratch; 1064 int restore_scratch;
1065 bool need_reload_pte;
1065}; 1066};
1066 1067
1067static struct mips_huge_tlb_info 1068static struct mips_huge_tlb_info
@@ -1076,6 +1077,7 @@ build_fast_tlb_refill_handler (u32 **p, struct uasm_label **l,
1076 1077
1077 rv.huge_pte = scratch; 1078 rv.huge_pte = scratch;
1078 rv.restore_scratch = 0; 1079 rv.restore_scratch = 0;
1080 rv.need_reload_pte = false;
1079 1081
1080 if (check_for_high_segbits) { 1082 if (check_for_high_segbits) {
1081 UASM_i_MFC0(p, tmp, C0_BADVADDR); 1083 UASM_i_MFC0(p, tmp, C0_BADVADDR);
@@ -1264,6 +1266,7 @@ static void build_r4000_tlb_refill_handler(void)
1264 } else { 1266 } else {
1265 htlb_info.huge_pte = K0; 1267 htlb_info.huge_pte = K0;
1266 htlb_info.restore_scratch = 0; 1268 htlb_info.restore_scratch = 0;
1269 htlb_info.need_reload_pte = true;
1267 vmalloc_mode = refill_noscratch; 1270 vmalloc_mode = refill_noscratch;
1268 /* 1271 /*
1269 * create the plain linear handler 1272 * create the plain linear handler
@@ -1300,7 +1303,8 @@ static void build_r4000_tlb_refill_handler(void)
1300 } 1303 }
1301#ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT 1304#ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT
1302 uasm_l_tlb_huge_update(&l, p); 1305 uasm_l_tlb_huge_update(&l, p);
1303 UASM_i_LW(&p, K0, 0, K1); 1306 if (htlb_info.need_reload_pte)
1307 UASM_i_LW(&p, htlb_info.huge_pte, 0, K1);
1304 build_huge_update_entries(&p, htlb_info.huge_pte, K1); 1308 build_huge_update_entries(&p, htlb_info.huge_pte, K1);
1305 build_huge_tlb_write_entry(&p, &l, &r, K0, tlb_random, 1309 build_huge_tlb_write_entry(&p, &l, &r, K0, tlb_random,
1306 htlb_info.restore_scratch); 1310 htlb_info.restore_scratch);