Module Name: src Committed By: matt Date: Sun Feb 28 03:21:07 UTC 2010
Modified Files: src/sys/arch/mips/include [matt-nb5-mips64]: locore.h src/sys/arch/mips/mips [matt-nb5-mips64]: mips_fixup.c Log Message: Add code which can change a direct jump to stub with an indirect call to a direct jump to the actual routine. To generate a diff of this commit: cvs rdiff -u -r1.78.36.1.2.16 -r1.78.36.1.2.17 \ src/sys/arch/mips/include/locore.h cvs rdiff -u -r1.1.2.2 -r1.1.2.3 src/sys/arch/mips/mips/mips_fixup.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/mips/include/locore.h diff -u src/sys/arch/mips/include/locore.h:1.78.36.1.2.16 src/sys/arch/mips/include/locore.h:1.78.36.1.2.17 --- src/sys/arch/mips/include/locore.h:1.78.36.1.2.16 Sat Feb 27 07:58:52 2010 +++ src/sys/arch/mips/include/locore.h Sun Feb 28 03:21:06 2010 @@ -1,4 +1,4 @@ -/* $NetBSD: locore.h,v 1.78.36.1.2.16 2010/02/27 07:58:52 matt Exp $ */ +/* $NetBSD: locore.h,v 1.78.36.1.2.17 2010/02/28 03:21:06 matt Exp $ */ /* * This file should not be included by MI code!!! @@ -47,8 +47,14 @@ typedef bool (*mips_fixup_callback_t)(int32_t, uint32_t [2]); +void fixup_splcalls(void); /* splstubs.c */ bool mips_fixup_exceptions(mips_fixup_callback_t); bool mips_fixup_zero_relative(int32_t, uint32_t [2]); +void mips_fixup_stubs(uint32_t *, uint32_t *, const uint32_t *, + const uint32_t *, size_t); +void fixup_mips_cpu_switch_resume(void); + +void mips_cpu_switch_resume(struct lwp *); #ifdef MIPS1 void mips1_tlb_set_asid(uint32_t); Index: src/sys/arch/mips/mips/mips_fixup.c diff -u src/sys/arch/mips/mips/mips_fixup.c:1.1.2.2 src/sys/arch/mips/mips/mips_fixup.c:1.1.2.3 --- src/sys/arch/mips/mips/mips_fixup.c:1.1.2.2 Sat Feb 27 18:25:25 2010 +++ src/sys/arch/mips/mips/mips_fixup.c Sun Feb 28 03:21:07 2010 @@ -29,7 +29,7 @@ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: mips_fixup.c,v 1.1.2.2 2010/02/27 18:25:25 snj Exp $"); +__KERNEL_RCSID(0, "$NetBSD: mips_fixup.c,v 1.1.2.3 2010/02/28 03:21:07 matt Exp $"); #include <sys/param.h> @@ -71,20 +71,20 @@ if (INSN_LUI_P(insn)) { const int32_t offset = insn << 16; lui_reg = (insn >> 16) & 31; -#ifdef DEBUG - printf("%s: %#x: insn %08x: lui r%zu, %%hi(%#x)", +#ifdef DEBUG_VERBOSE + printf("%s: %#x: insn %08x: lui r%zu, %%hi(%#x)", __func__, (int32_t)(intptr_t)insnp, insn, lui_reg, offset); #endif if (upper_addr == offset) { lui_insnp = insnp; -#ifdef DEBUG +#ifdef DEBUG_VERBOSE printf(" (maybe)"); #endif } else { lui_insnp = NULL; } -#ifdef DEBUG +#ifdef DEBUG_VERBOSE printf("\n"); #endif } else if (lui_insnp != NULL && INSN_LOAD_P(insn)) { @@ -95,8 +95,8 @@ && load_addr < addr + size && base == lui_reg && rt == lui_reg) { -#ifdef DEBUG - printf("%s: %#x: insn %08x: %s r%zu, %%lo(%08x)(r%zu)\n", +#ifdef DEBUG_VERBOSE + printf("%s: %#x: insn %08x: %s r%zu, %%lo(%08x)(r%zu)\n", __func__, (int32_t)(intptr_t)insnp, insn, INSN_LW_P(insn) ? "lw" : "ld", rt, load_addr, base); @@ -143,8 +143,8 @@ new_insns[0] = (new_insns[1] & (0xfc1f0000|PAGE_MASK)) | (0xffff & ~PAGE_MASK); new_insns[1] = 0; -#ifdef DEBUG - printf("%s: %08x: insn#1 %08x: %s r%u, %d(r%u)\n", +#ifdef DEBUG_VERBOSE + printf("%s: %08x: insn#1 %08x: %s r%u, %d(r%u)\n", __func__, (int32_t)load_addr, new_insns[0], INSN_LW_P(new_insns[0]) ? "lw" : "ld", (new_insns[0] >> 16) & 31, @@ -171,3 +171,121 @@ return true; } + +#define OPCODE_J 002 +#define OPCODE_JAL 003 + +static void +fixup_mips_jump(uint32_t *insnp, uint32_t stub, uint32_t real) +{ + uint32_t insn = *insnp; + + KASSERT((insn >> (26+1)) == (OPCODE_J >> 1)); + KASSERT((insn << 6) == (stub << 4)); + + insn ^= (stub ^ real) << 4 >> 6; + + KASSERT((insn << 6) == (real << 4)); + + *insnp = insn; +} + +void +mips_fixup_stubs(uint32_t *start, uint32_t *end, + const uint32_t *stub_offsets, const uint32_t *real_offsets, + size_t noffsets) +{ + uint32_t min_offset = 0x03ffffff; + uint32_t max_offset = 0x00000000; +#ifdef DEBUG + size_t fixups = 0; + uint32_t cycles = (CPUISMIPS3 ? mips3_cp0_count_read() : 0); +#endif + + /* + * Find the lowest and highest jumps we will be replacing. We don't + * need to do but it does make weeding out the non-matching jumps + * faster. + */ + for (size_t i = 0; i < noffsets; i++) { + if (stub_offsets[i] < min_offset) + min_offset = stub_offsets[i]; + if (max_offset < stub_offsets[i]) + max_offset = stub_offsets[i]; + } + + for (uint32_t *insnp = start; insnp < end; insnp++) { + uint32_t insn = *insnp; + uint32_t offset = insn & 0x03ffffff; + uint32_t opcode = insn >> 26; + + /* + * First we check to see if this is a jump and whether its + * within the range we are interested in. + */ + if ((opcode != OPCODE_J && opcode != OPCODE_JAL) + || offset < min_offset || max_offset < offset) + continue; + + /* + * We know it's a jump, but does it match one we want to + * fixup? + */ + for (size_t i = 0; i < noffsets; i++) { + if (stub_offsets[i] != offset) + continue; + /* + * Yes, we need to fix it up. Replace the old + * displacement with the real displacement. If we've + * moved to a new cache line, sync the last cacheline + * we fixed. + */ + *insnp ^= offset ^ real_offsets[i]; +#ifdef DEBUG +#if 0 + int32_t va = ((intptr_t) insnp >> 26) << 26; + printf("%s: %08x: [%08x] %s %08x -> [%08x] %s %08x\n", + __func__, (int32_t)(intptr_t)insnp, + insn, opcode == OPCODE_J ? "j" : "jal", + va | (offset << 2), + *insnp, opcode == OPCODE_J ? "j" : "jal", + va | (real_offsets[i] << 2)); +#endif + fixups++; +#endif + break; + } + } + + if (sizeof(uint32_t [end - start]) > mips_cache_info.mci_picache_size) + mips_icache_sync_all(); + else + mips_icache_sync_range((vaddr_t)start, + sizeof(uint32_t [end - start])); + +#ifdef DEBUG + if (CPUISMIPS3) + cycles = mips3_cp0_count_read() - cycles; + printf("%s: %zu fixup%s done in %u cycles\n", __func__, + fixups, fixups == 1 ? "" : "s", + cycles); +#endif +} + +void mips_cpu_switch_resume(struct lwp *l) __section(".stub"); + +void +mips_cpu_switch_resume(struct lwp *l) +{ + (*mips_locoresw.lsw_cpu_switch_resume)(l); +} + +void +fixup_mips_cpu_switch_resume(void) +{ + extern uint32_t __cpu_switchto_fixup[]; + + fixup_mips_jump(__cpu_switchto_fixup, + (uintptr_t)mips_cpu_switch_resume, + (uintptr_t)mips_locoresw.lsw_cpu_switch_resume); +}