|
@@ -6,7 +6,7 @@
|
|
|
* Synthesize TLB refill handlers at runtime.
|
|
|
*
|
|
|
* Copyright (C) 2004, 2005, 2006, 2008 Thiemo Seufer
|
|
|
- * Copyright (C) 2005, 2007 Maciej W. Rozycki
|
|
|
+ * Copyright (C) 2005, 2007, 2008, 2009 Maciej W. Rozycki
|
|
|
* Copyright (C) 2006 Ralf Baechle (ralf@linux-mips.org)
|
|
|
*
|
|
|
* ... and the days got worse and worse and now you see
|
|
@@ -19,6 +19,7 @@
|
|
|
* (Condolences to Napoleon XIV)
|
|
|
*/
|
|
|
|
|
|
+#include <linux/bug.h>
|
|
|
#include <linux/kernel.h>
|
|
|
#include <linux/types.h>
|
|
|
#include <linux/string.h>
|
|
@@ -732,36 +733,60 @@ static void __cpuinit build_r4000_tlb_refill_handler(void)
|
|
|
uasm_copy_handler(relocs, labels, tlb_handler, p, f);
|
|
|
final_len = p - tlb_handler;
|
|
|
} else {
|
|
|
- /*
|
|
|
- * Split two instructions before the end. One for the
|
|
|
- * branch and one for the instruction in the delay
|
|
|
- * slot.
|
|
|
- */
|
|
|
- u32 *split = tlb_handler + MIPS64_REFILL_INSNS - 2;
|
|
|
+#ifdef MODULE_START
|
|
|
+ const enum label_id ls = label_module_alloc;
|
|
|
+#else
|
|
|
+ const enum label_id ls = label_vmalloc;
|
|
|
+#endif
|
|
|
+ u32 *split;
|
|
|
+ int ov = 0;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; i < ARRAY_SIZE(labels) && labels[i].lab != ls; i++)
|
|
|
+ ;
|
|
|
+ BUG_ON(i == ARRAY_SIZE(labels));
|
|
|
+ split = labels[i].addr;
|
|
|
|
|
|
/*
|
|
|
- * Find the split point. If the branch would fall in
|
|
|
- * a delay slot, we must back up an additional
|
|
|
- * instruction so that it is no longer in a delay
|
|
|
- * slot.
|
|
|
+ * See if we have overflown one way or the other.
|
|
|
*/
|
|
|
- if (uasm_insn_has_bdelay(relocs, split - 1))
|
|
|
- split--;
|
|
|
-
|
|
|
+ if (split > tlb_handler + MIPS64_REFILL_INSNS ||
|
|
|
+ split < p - MIPS64_REFILL_INSNS)
|
|
|
+ ov = 1;
|
|
|
+
|
|
|
+ if (ov) {
|
|
|
+ /*
|
|
|
+ * Split two instructions before the end. One
|
|
|
+ * for the branch and one for the instruction
|
|
|
+ * in the delay slot.
|
|
|
+ */
|
|
|
+ split = tlb_handler + MIPS64_REFILL_INSNS - 2;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If the branch would fall in a delay slot,
|
|
|
+ * we must back up an additional instruction
|
|
|
+ * so that it is no longer in a delay slot.
|
|
|
+ */
|
|
|
+ if (uasm_insn_has_bdelay(relocs, split - 1))
|
|
|
+ split--;
|
|
|
+ }
|
|
|
/* Copy first part of the handler. */
|
|
|
uasm_copy_handler(relocs, labels, tlb_handler, split, f);
|
|
|
f += split - tlb_handler;
|
|
|
|
|
|
- /* Insert branch. */
|
|
|
- uasm_l_split(&l, final_handler);
|
|
|
- uasm_il_b(&f, &r, label_split);
|
|
|
- if (uasm_insn_has_bdelay(relocs, split))
|
|
|
- uasm_i_nop(&f);
|
|
|
- else {
|
|
|
- uasm_copy_handler(relocs, labels, split, split + 1, f);
|
|
|
- uasm_move_labels(labels, f, f + 1, -1);
|
|
|
- f++;
|
|
|
- split++;
|
|
|
+ if (ov) {
|
|
|
+ /* Insert branch. */
|
|
|
+ uasm_l_split(&l, final_handler);
|
|
|
+ uasm_il_b(&f, &r, label_split);
|
|
|
+ if (uasm_insn_has_bdelay(relocs, split))
|
|
|
+ uasm_i_nop(&f);
|
|
|
+ else {
|
|
|
+ uasm_copy_handler(relocs, labels,
|
|
|
+ split, split + 1, f);
|
|
|
+ uasm_move_labels(labels, f, f + 1, -1);
|
|
|
+ f++;
|
|
|
+ split++;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/* Copy the rest of the handler. */
|