| /* | |
| * User address space access functions. | |
| * The non-inlined parts of asm-metag/uaccess.h are here. | |
| * | |
| * Copyright (C) 2006, Imagination Technologies. | |
| * Copyright (C) 2000, Axis Communications AB. | |
| * | |
| * Written by Hans-Peter Nilsson. | |
| * Pieces used from memcpy, originally by Kenny Ranerup long time ago. | |
| * Modified for Meta by Will Newton. | |
| */ | |
| #include <linux/export.h> | |
| #include <linux/uaccess.h> | |
| #include <asm/cache.h> /* def of L1_CACHE_BYTES */ | |
| #define USE_RAPF | |
| #define RAPF_MIN_BUF_SIZE (3*L1_CACHE_BYTES) | |
| /* The "double write" in this code is because the Meta will not fault | |
| * immediately unless the memory pipe is forced to by e.g. a data stall or | |
| * another memory op. The second write should be discarded by the write | |
| * combiner so should have virtually no cost. | |
| */ | |
| #define __asm_copy_user_cont(to, from, ret, COPY, FIXUP, TENTRY) \ | |
| asm volatile ( \ | |
| COPY \ | |
| "1:\n" \ | |
| " .section .fixup,\"ax\"\n" \ | |
| " MOV D1Ar1,#0\n" \ | |
| FIXUP \ | |
| " MOVT D1Ar1,#HI(1b)\n" \ | |
| " JUMP D1Ar1,#LO(1b)\n" \ | |
| " .previous\n" \ | |
| " .section __ex_table,\"a\"\n" \ | |
| TENTRY \ | |
| " .previous\n" \ | |
| : "=r" (to), "=r" (from), "=r" (ret) \ | |
| : "0" (to), "1" (from), "2" (ret) \ | |
| : "D1Ar1", "memory") | |
| #define __asm_copy_to_user_1(to, from, ret) \ | |
| __asm_copy_user_cont(to, from, ret, \ | |
| " GETB D1Ar1,[%1++]\n" \ | |
| " SETB [%0],D1Ar1\n" \ | |
| "2: SETB [%0++],D1Ar1\n", \ | |
| "3: ADD %2,%2,#1\n", \ | |
| " .long 2b,3b\n") | |
| #define __asm_copy_to_user_2x_cont(to, from, ret, COPY, FIXUP, TENTRY) \ | |
| __asm_copy_user_cont(to, from, ret, \ | |
| " GETW D1Ar1,[%1++]\n" \ | |
| " SETW [%0],D1Ar1\n" \ | |
| "2: SETW [%0++],D1Ar1\n" COPY, \ | |
| "3: ADD %2,%2,#2\n" FIXUP, \ | |
| " .long 2b,3b\n" TENTRY) | |
| #define __asm_copy_to_user_2(to, from, ret) \ | |
| __asm_copy_to_user_2x_cont(to, from, ret, "", "", "") | |
| #define __asm_copy_to_user_3(to, from, ret) \ | |
| __asm_copy_to_user_2x_cont(to, from, ret, \ | |
| " GETB D1Ar1,[%1++]\n" \ | |
| " SETB [%0],D1Ar1\n" \ | |
| "4: SETB [%0++],D1Ar1\n", \ | |
| "5: ADD %2,%2,#1\n", \ | |
| " .long 4b,5b\n") | |
| #define __asm_copy_to_user_4x_cont(to, from, ret, COPY, FIXUP, TENTRY) \ | |
| __asm_copy_user_cont(to, from, ret, \ | |
| " GETD D1Ar1,[%1++]\n" \ | |
| " SETD [%0],D1Ar1\n" \ | |
| "2: SETD [%0++],D1Ar1\n" COPY, \ | |
| "3: ADD %2,%2,#4\n" FIXUP, \ | |
| " .long 2b,3b\n" TENTRY) | |
| #define __asm_copy_to_user_4(to, from, ret) \ | |
| __asm_copy_to_user_4x_cont(to, from, ret, "", "", "") | |
| #define __asm_copy_to_user_5(to, from, ret) \ | |
| __asm_copy_to_user_4x_cont(to, from, ret, \ | |
| " GETB D1Ar1,[%1++]\n" \ | |
| " SETB [%0],D1Ar1\n" \ | |
| "4: SETB [%0++],D1Ar1\n", \ | |
| "5: ADD %2,%2,#1\n", \ | |
| " .long 4b,5b\n") | |
| #define __asm_copy_to_user_6x_cont(to, from, ret, COPY, FIXUP, TENTRY) \ | |
| __asm_copy_to_user_4x_cont(to, from, ret, \ | |
| " GETW D1Ar1,[%1++]\n" \ | |
| " SETW [%0],D1Ar1\n" \ | |
| "4: SETW [%0++],D1Ar1\n" COPY, \ | |
| "5: ADD %2,%2,#2\n" FIXUP, \ | |
| " .long 4b,5b\n" TENTRY) | |
| #define __asm_copy_to_user_6(to, from, ret) \ | |
| __asm_copy_to_user_6x_cont(to, from, ret, "", "", "") | |
| #define __asm_copy_to_user_7(to, from, ret) \ | |
| __asm_copy_to_user_6x_cont(to, from, ret, \ | |
| " GETB D1Ar1,[%1++]\n" \ | |
| " SETB [%0],D1Ar1\n" \ | |
| "6: SETB [%0++],D1Ar1\n", \ | |
| "7: ADD %2,%2,#1\n", \ | |
| " .long 6b,7b\n") | |
| #define __asm_copy_to_user_8x_cont(to, from, ret, COPY, FIXUP, TENTRY) \ | |
| __asm_copy_to_user_4x_cont(to, from, ret, \ | |
| " GETD D1Ar1,[%1++]\n" \ | |
| " SETD [%0],D1Ar1\n" \ | |
| "4: SETD [%0++],D1Ar1\n" COPY, \ | |
| "5: ADD %2,%2,#4\n" FIXUP, \ | |
| " .long 4b,5b\n" TENTRY) | |
| #define __asm_copy_to_user_8(to, from, ret) \ | |
| __asm_copy_to_user_8x_cont(to, from, ret, "", "", "") | |
| #define __asm_copy_to_user_9(to, from, ret) \ | |
| __asm_copy_to_user_8x_cont(to, from, ret, \ | |
| " GETB D1Ar1,[%1++]\n" \ | |
| " SETB [%0],D1Ar1\n" \ | |
| "6: SETB [%0++],D1Ar1\n", \ | |
| "7: ADD %2,%2,#1\n", \ | |
| " .long 6b,7b\n") | |
| #define __asm_copy_to_user_10x_cont(to, from, ret, COPY, FIXUP, TENTRY) \ | |
| __asm_copy_to_user_8x_cont(to, from, ret, \ | |
| " GETW D1Ar1,[%1++]\n" \ | |
| " SETW [%0],D1Ar1\n" \ | |
| "6: SETW [%0++],D1Ar1\n" COPY, \ | |
| "7: ADD %2,%2,#2\n" FIXUP, \ | |
| " .long 6b,7b\n" TENTRY) | |
| #define __asm_copy_to_user_10(to, from, ret) \ | |
| __asm_copy_to_user_10x_cont(to, from, ret, "", "", "") | |
| #define __asm_copy_to_user_11(to, from, ret) \ | |
| __asm_copy_to_user_10x_cont(to, from, ret, \ | |
| " GETB D1Ar1,[%1++]\n" \ | |
| " SETB [%0],D1Ar1\n" \ | |
| "8: SETB [%0++],D1Ar1\n", \ | |
| "9: ADD %2,%2,#1\n", \ | |
| " .long 8b,9b\n") | |
| #define __asm_copy_to_user_12x_cont(to, from, ret, COPY, FIXUP, TENTRY) \ | |
| __asm_copy_to_user_8x_cont(to, from, ret, \ | |
| " GETD D1Ar1,[%1++]\n" \ | |
| " SETD [%0],D1Ar1\n" \ | |
| "6: SETD [%0++],D1Ar1\n" COPY, \ | |
| "7: ADD %2,%2,#4\n" FIXUP, \ | |
| " .long 6b,7b\n" TENTRY) | |
| #define __asm_copy_to_user_12(to, from, ret) \ | |
| __asm_copy_to_user_12x_cont(to, from, ret, "", "", "") | |
| #define __asm_copy_to_user_13(to, from, ret) \ | |
| __asm_copy_to_user_12x_cont(to, from, ret, \ | |
| " GETB D1Ar1,[%1++]\n" \ | |
| " SETB [%0],D1Ar1\n" \ | |
| "8: SETB [%0++],D1Ar1\n", \ | |
| "9: ADD %2,%2,#1\n", \ | |
| " .long 8b,9b\n") | |
| #define __asm_copy_to_user_14x_cont(to, from, ret, COPY, FIXUP, TENTRY) \ | |
| __asm_copy_to_user_12x_cont(to, from, ret, \ | |
| " GETW D1Ar1,[%1++]\n" \ | |
| " SETW [%0],D1Ar1\n" \ | |
| "8: SETW [%0++],D1Ar1\n" COPY, \ | |
| "9: ADD %2,%2,#2\n" FIXUP, \ | |
| " .long 8b,9b\n" TENTRY) | |
| #define __asm_copy_to_user_14(to, from, ret) \ | |
| __asm_copy_to_user_14x_cont(to, from, ret, "", "", "") | |
| #define __asm_copy_to_user_15(to, from, ret) \ | |
| __asm_copy_to_user_14x_cont(to, from, ret, \ | |
| " GETB D1Ar1,[%1++]\n" \ | |
| " SETB [%0],D1Ar1\n" \ | |
| "10: SETB [%0++],D1Ar1\n", \ | |
| "11: ADD %2,%2,#1\n", \ | |
| " .long 10b,11b\n") | |
| #define __asm_copy_to_user_16x_cont(to, from, ret, COPY, FIXUP, TENTRY) \ | |
| __asm_copy_to_user_12x_cont(to, from, ret, \ | |
| " GETD D1Ar1,[%1++]\n" \ | |
| " SETD [%0],D1Ar1\n" \ | |
| "8: SETD [%0++],D1Ar1\n" COPY, \ | |
| "9: ADD %2,%2,#4\n" FIXUP, \ | |
| " .long 8b,9b\n" TENTRY) | |
| #define __asm_copy_to_user_16(to, from, ret) \ | |
| __asm_copy_to_user_16x_cont(to, from, ret, "", "", "") | |
| #define __asm_copy_to_user_8x64(to, from, ret) \ | |
| asm volatile ( \ | |
| " GETL D0Ar2,D1Ar1,[%1++]\n" \ | |
| " SETL [%0],D0Ar2,D1Ar1\n" \ | |
| "2: SETL [%0++],D0Ar2,D1Ar1\n" \ | |
| "1:\n" \ | |
| " .section .fixup,\"ax\"\n" \ | |
| "3: ADD %2,%2,#8\n" \ | |
| " MOVT D0Ar2,#HI(1b)\n" \ | |
| " JUMP D0Ar2,#LO(1b)\n" \ | |
| " .previous\n" \ | |
| " .section __ex_table,\"a\"\n" \ | |
| " .long 2b,3b\n" \ | |
| " .previous\n" \ | |
| : "=r" (to), "=r" (from), "=r" (ret) \ | |
| : "0" (to), "1" (from), "2" (ret) \ | |
| : "D1Ar1", "D0Ar2", "memory") | |
| /* | |
| * optimized copying loop using RAPF when 64 bit aligned | |
| * | |
| * n will be automatically decremented inside the loop | |
| * ret will be left intact. if error occurs we will rewind | |
| * so that the original non optimized code will fill up | |
| * this value correctly. | |
| * | |
| * on fault: | |
| * > n will hold total number of uncopied bytes | |
| * | |
| * > {'to','from'} will be rewind back so that | |
| * the non-optimized code will do the proper fix up | |
| * | |
| * DCACHE drops the cacheline which helps in reducing cache | |
| * pollution. | |
| * | |
| * We introduce an extra SETL at the end of the loop to | |
| * ensure we don't fall off the loop before we catch all | |
| * erros. | |
| * | |
| * NOTICE: | |
| * LSM_STEP in TXSTATUS must be cleared in fix up code. | |
| * since we're using M{S,G}ETL, a fault might happen at | |
| * any address in the middle of M{S,G}ETL causing | |
| * the value of LSM_STEP to be incorrect which can | |
| * cause subsequent use of M{S,G}ET{L,D} to go wrong. | |
| * ie: if LSM_STEP was 1 when a fault occurs, the | |
| * next call to M{S,G}ET{L,D} will skip the first | |
| * copy/getting as it think that the first 1 has already | |
| * been done. | |
| * | |
| */ | |
| #define __asm_copy_user_64bit_rapf_loop( \ | |
| to, from, ret, n, id, FIXUP) \ | |
| asm volatile ( \ | |
| ".balign 8\n" \ | |
| "MOV RAPF, %1\n" \ | |
| "MSETL [A0StP++], D0Ar6, D0FrT, D0.5, D0.6, D0.7\n" \ | |
| "MOV D0Ar6, #0\n" \ | |
| "LSR D1Ar5, %3, #6\n" \ | |
| "SUB TXRPT, D1Ar5, #2\n" \ | |
| "MOV RAPF, %1\n" \ | |
| "$Lloop"id":\n" \ | |
| "ADD RAPF, %1, #64\n" \ | |
| "21:\n" \ | |
| "MGETL D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \ | |
| "22:\n" \ | |
| "MSETL [%0++], D0FrT, D0.5, D0.6, D0.7\n" \ | |
| "SUB %3, %3, #32\n" \ | |
| "23:\n" \ | |
| "MGETL D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \ | |
| "24:\n" \ | |
| "MSETL [%0++], D0FrT, D0.5, D0.6, D0.7\n" \ | |
| "SUB %3, %3, #32\n" \ | |
| "DCACHE [%1+#-64], D0Ar6\n" \ | |
| "BR $Lloop"id"\n" \ | |
| \ | |
| "MOV RAPF, %1\n" \ | |
| "25:\n" \ | |
| "MGETL D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \ | |
| "26:\n" \ | |
| "MSETL [%0++], D0FrT, D0.5, D0.6, D0.7\n" \ | |
| "SUB %3, %3, #32\n" \ | |
| "27:\n" \ | |
| "MGETL D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \ | |
| "28:\n" \ | |
| "MSETL [%0++], D0FrT, D0.5, D0.6, D0.7\n" \ | |
| "SUB %0, %0, #8\n" \ | |
| "29:\n" \ | |
| "SETL [%0++], D0.7, D1.7\n" \ | |
| "SUB %3, %3, #32\n" \ | |
| "1:" \ | |
| "DCACHE [%1+#-64], D0Ar6\n" \ | |
| "GETL D0Ar6, D1Ar5, [A0StP+#-40]\n" \ | |
| "GETL D0FrT, D1RtP, [A0StP+#-32]\n" \ | |
| "GETL D0.5, D1.5, [A0StP+#-24]\n" \ | |
| "GETL D0.6, D1.6, [A0StP+#-16]\n" \ | |
| "GETL D0.7, D1.7, [A0StP+#-8]\n" \ | |
| "SUB A0StP, A0StP, #40\n" \ | |
| " .section .fixup,\"ax\"\n" \ | |
| "4:\n" \ | |
| " ADD %0, %0, #8\n" \ | |
| "3:\n" \ | |
| " MOV D0Ar2, TXSTATUS\n" \ | |
| " MOV D1Ar1, TXSTATUS\n" \ | |
| " AND D1Ar1, D1Ar1, #0xFFFFF8FF\n" \ | |
| " MOV TXSTATUS, D1Ar1\n" \ | |
| FIXUP \ | |
| " MOVT D0Ar2,#HI(1b)\n" \ | |
| " JUMP D0Ar2,#LO(1b)\n" \ | |
| " .previous\n" \ | |
| " .section __ex_table,\"a\"\n" \ | |
| " .long 21b,3b\n" \ | |
| " .long 22b,3b\n" \ | |
| " .long 23b,3b\n" \ | |
| " .long 24b,3b\n" \ | |
| " .long 25b,3b\n" \ | |
| " .long 26b,3b\n" \ | |
| " .long 27b,3b\n" \ | |
| " .long 28b,3b\n" \ | |
| " .long 29b,4b\n" \ | |
| " .previous\n" \ | |
| : "=r" (to), "=r" (from), "=r" (ret), "=d" (n) \ | |
| : "0" (to), "1" (from), "2" (ret), "3" (n) \ | |
| : "D1Ar1", "D0Ar2", "memory") | |
| /* rewind 'to' and 'from' pointers when a fault occurs | |
| * | |
| * Rationale: | |
| * A fault always occurs on writing to user buffer. A fault | |
| * is at a single address, so we need to rewind by only 4 | |
| * bytes. | |
| * Since we do a complete read from kernel buffer before | |
| * writing, we need to rewind it also. The amount to be | |
| * rewind equals the number of faulty writes in MSETD | |
| * which is: [4 - (LSM_STEP-1)]*8 | |
| * LSM_STEP is bits 10:8 in TXSTATUS which is already read | |
| * and stored in D0Ar2 | |
| * | |
| * NOTE: If a fault occurs at the last operation in M{G,S}ETL | |
| * LSM_STEP will be 0. ie: we do 4 writes in our case, if | |
| * a fault happens at the 4th write, LSM_STEP will be 0 | |
| * instead of 4. The code copes with that. | |
| * | |
| * n is updated by the number of successful writes, which is: | |
| * n = n - (LSM_STEP-1)*8 | |
| */ | |
| #define __asm_copy_to_user_64bit_rapf_loop(to, from, ret, n, id)\ | |
| __asm_copy_user_64bit_rapf_loop(to, from, ret, n, id, \ | |
| "LSR D0Ar2, D0Ar2, #8\n" \ | |
| "AND D0Ar2, D0Ar2, #0x7\n" \ | |
| "ADDZ D0Ar2, D0Ar2, #4\n" \ | |
| "SUB D0Ar2, D0Ar2, #1\n" \ | |
| "MOV D1Ar1, #4\n" \ | |
| "SUB D0Ar2, D1Ar1, D0Ar2\n" \ | |
| "LSL D0Ar2, D0Ar2, #3\n" \ | |
| "LSL D1Ar1, D1Ar1, #3\n" \ | |
| "SUB D1Ar1, D1Ar1, D0Ar2\n" \ | |
| "SUB %0, %0, #8\n" \ | |
| "SUB %1, %1,D0Ar2\n" \ | |
| "SUB %3, %3, D1Ar1\n") | |
| /* | |
| * optimized copying loop using RAPF when 32 bit aligned | |
| * | |
| * n will be automatically decremented inside the loop | |
| * ret will be left intact. if error occurs we will rewind | |
| * so that the original non optimized code will fill up | |
| * this value correctly. | |
| * | |
| * on fault: | |
| * > n will hold total number of uncopied bytes | |
| * | |
| * > {'to','from'} will be rewind back so that | |
| * the non-optimized code will do the proper fix up | |
| * | |
| * DCACHE drops the cacheline which helps in reducing cache | |
| * pollution. | |
| * | |
| * We introduce an extra SETD at the end of the loop to | |
| * ensure we don't fall off the loop before we catch all | |
| * erros. | |
| * | |
| * NOTICE: | |
| * LSM_STEP in TXSTATUS must be cleared in fix up code. | |
| * since we're using M{S,G}ETL, a fault might happen at | |
| * any address in the middle of M{S,G}ETL causing | |
| * the value of LSM_STEP to be incorrect which can | |
| * cause subsequent use of M{S,G}ET{L,D} to go wrong. | |
| * ie: if LSM_STEP was 1 when a fault occurs, the | |
| * next call to M{S,G}ET{L,D} will skip the first | |
| * copy/getting as it think that the first 1 has already | |
| * been done. | |
| * | |
| */ | |
| #define __asm_copy_user_32bit_rapf_loop( \ | |
| to, from, ret, n, id, FIXUP) \ | |
| asm volatile ( \ | |
| ".balign 8\n" \ | |
| "MOV RAPF, %1\n" \ | |
| "MSETL [A0StP++], D0Ar6, D0FrT, D0.5, D0.6, D0.7\n" \ | |
| "MOV D0Ar6, #0\n" \ | |
| "LSR D1Ar5, %3, #6\n" \ | |
| "SUB TXRPT, D1Ar5, #2\n" \ | |
| "MOV RAPF, %1\n" \ | |
| "$Lloop"id":\n" \ | |
| "ADD RAPF, %1, #64\n" \ | |
| "21:\n" \ | |
| "MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \ | |
| "22:\n" \ | |
| "MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \ | |
| "SUB %3, %3, #16\n" \ | |
| "23:\n" \ | |
| "MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \ | |
| "24:\n" \ | |
| "MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \ | |
| "SUB %3, %3, #16\n" \ | |
| "25:\n" \ | |
| "MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \ | |
| "26:\n" \ | |
| "MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \ | |
| "SUB %3, %3, #16\n" \ | |
| "27:\n" \ | |
| "MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \ | |
| "28:\n" \ | |
| "MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \ | |
| "SUB %3, %3, #16\n" \ | |
| "DCACHE [%1+#-64], D0Ar6\n" \ | |
| "BR $Lloop"id"\n" \ | |
| \ | |
| "MOV RAPF, %1\n" \ | |
| "29:\n" \ | |
| "MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \ | |
| "30:\n" \ | |
| "MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \ | |
| "SUB %3, %3, #16\n" \ | |
| "31:\n" \ | |
| "MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \ | |
| "32:\n" \ | |
| "MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \ | |
| "SUB %3, %3, #16\n" \ | |
| "33:\n" \ | |
| "MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \ | |
| "34:\n" \ | |
| "MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \ | |
| "SUB %3, %3, #16\n" \ | |
| "35:\n" \ | |
| "MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \ | |
| "36:\n" \ | |
| "MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \ | |
| "SUB %0, %0, #4\n" \ | |
| "37:\n" \ | |
| "SETD [%0++], D0.7\n" \ | |
| "SUB %3, %3, #16\n" \ | |
| "1:" \ | |
| "DCACHE [%1+#-64], D0Ar6\n" \ | |
| "GETL D0Ar6, D1Ar5, [A0StP+#-40]\n" \ | |
| "GETL D0FrT, D1RtP, [A0StP+#-32]\n" \ | |
| "GETL D0.5, D1.5, [A0StP+#-24]\n" \ | |
| "GETL D0.6, D1.6, [A0StP+#-16]\n" \ | |
| "GETL D0.7, D1.7, [A0StP+#-8]\n" \ | |
| "SUB A0StP, A0StP, #40\n" \ | |
| " .section .fixup,\"ax\"\n" \ | |
| "4:\n" \ | |
| " ADD %0, %0, #4\n" \ | |
| "3:\n" \ | |
| " MOV D0Ar2, TXSTATUS\n" \ | |
| " MOV D1Ar1, TXSTATUS\n" \ | |
| " AND D1Ar1, D1Ar1, #0xFFFFF8FF\n" \ | |
| " MOV TXSTATUS, D1Ar1\n" \ | |
| FIXUP \ | |
| " MOVT D0Ar2,#HI(1b)\n" \ | |
| " JUMP D0Ar2,#LO(1b)\n" \ | |
| " .previous\n" \ | |
| " .section __ex_table,\"a\"\n" \ | |
| " .long 21b,3b\n" \ | |
| " .long 22b,3b\n" \ | |
| " .long 23b,3b\n" \ | |
| " .long 24b,3b\n" \ | |
| " .long 25b,3b\n" \ | |
| " .long 26b,3b\n" \ | |
| " .long 27b,3b\n" \ | |
| " .long 28b,3b\n" \ | |
| " .long 29b,3b\n" \ | |
| " .long 30b,3b\n" \ | |
| " .long 31b,3b\n" \ | |
| " .long 32b,3b\n" \ | |
| " .long 33b,3b\n" \ | |
| " .long 34b,3b\n" \ | |
| " .long 35b,3b\n" \ | |
| " .long 36b,3b\n" \ | |
| " .long 37b,4b\n" \ | |
| " .previous\n" \ | |
| : "=r" (to), "=r" (from), "=r" (ret), "=d" (n) \ | |
| : "0" (to), "1" (from), "2" (ret), "3" (n) \ | |
| : "D1Ar1", "D0Ar2", "memory") | |
| /* rewind 'to' and 'from' pointers when a fault occurs | |
| * | |
| * Rationale: | |
| * A fault always occurs on writing to user buffer. A fault | |
| * is at a single address, so we need to rewind by only 4 | |
| * bytes. | |
| * Since we do a complete read from kernel buffer before | |
| * writing, we need to rewind it also. The amount to be | |
| * rewind equals the number of faulty writes in MSETD | |
| * which is: [4 - (LSM_STEP-1)]*4 | |
| * LSM_STEP is bits 10:8 in TXSTATUS which is already read | |
| * and stored in D0Ar2 | |
| * | |
| * NOTE: If a fault occurs at the last operation in M{G,S}ETL | |
| * LSM_STEP will be 0. ie: we do 4 writes in our case, if | |
| * a fault happens at the 4th write, LSM_STEP will be 0 | |
| * instead of 4. The code copes with that. | |
| * | |
| * n is updated by the number of successful writes, which is: | |
| * n = n - (LSM_STEP-1)*4 | |
| */ | |
| #define __asm_copy_to_user_32bit_rapf_loop(to, from, ret, n, id)\ | |
| __asm_copy_user_32bit_rapf_loop(to, from, ret, n, id, \ | |
| "LSR D0Ar2, D0Ar2, #8\n" \ | |
| "AND D0Ar2, D0Ar2, #0x7\n" \ | |
| "ADDZ D0Ar2, D0Ar2, #4\n" \ | |
| "SUB D0Ar2, D0Ar2, #1\n" \ | |
| "MOV D1Ar1, #4\n" \ | |
| "SUB D0Ar2, D1Ar1, D0Ar2\n" \ | |
| "LSL D0Ar2, D0Ar2, #2\n" \ | |
| "LSL D1Ar1, D1Ar1, #2\n" \ | |
| "SUB D1Ar1, D1Ar1, D0Ar2\n" \ | |
| "SUB %0, %0, #4\n" \ | |
| "SUB %1, %1, D0Ar2\n" \ | |
| "SUB %3, %3, D1Ar1\n") | |
| unsigned long __copy_user(void __user *pdst, const void *psrc, | |
| unsigned long n) | |
| { | |
| register char __user *dst asm ("A0.2") = pdst; | |
| register const char *src asm ("A1.2") = psrc; | |
| unsigned long retn = 0; | |
| if (n == 0) | |
| return 0; | |
| if ((unsigned long) src & 1) { | |
| __asm_copy_to_user_1(dst, src, retn); | |
| n--; | |
| } | |
| if ((unsigned long) dst & 1) { | |
| /* Worst case - byte copy */ | |
| while (n > 0) { | |
| __asm_copy_to_user_1(dst, src, retn); | |
| n--; | |
| } | |
| } | |
| if (((unsigned long) src & 2) && n >= 2) { | |
| __asm_copy_to_user_2(dst, src, retn); | |
| n -= 2; | |
| } | |
| if ((unsigned long) dst & 2) { | |
| /* Second worst case - word copy */ | |
| while (n >= 2) { | |
| __asm_copy_to_user_2(dst, src, retn); | |
| n -= 2; | |
| } | |
| } | |
| #ifdef USE_RAPF | |
| /* 64 bit copy loop */ | |
| if (!(((unsigned long) src | (__force unsigned long) dst) & 7)) { | |
| if (n >= RAPF_MIN_BUF_SIZE) { | |
| /* copy user using 64 bit rapf copy */ | |
| __asm_copy_to_user_64bit_rapf_loop(dst, src, retn, | |
| n, "64cu"); | |
| } | |
| while (n >= 8) { | |
| __asm_copy_to_user_8x64(dst, src, retn); | |
| n -= 8; | |
| } | |
| } | |
| if (n >= RAPF_MIN_BUF_SIZE) { | |
| /* copy user using 32 bit rapf copy */ | |
| __asm_copy_to_user_32bit_rapf_loop(dst, src, retn, n, "32cu"); | |
| } | |
| #else | |
| /* 64 bit copy loop */ | |
| if (!(((unsigned long) src | (__force unsigned long) dst) & 7)) { | |
| while (n >= 8) { | |
| __asm_copy_to_user_8x64(dst, src, retn); | |
| n -= 8; | |
| } | |
| } | |
| #endif | |
| while (n >= 16) { | |
| __asm_copy_to_user_16(dst, src, retn); | |
| n -= 16; | |
| } | |
| while (n >= 4) { | |
| __asm_copy_to_user_4(dst, src, retn); | |
| n -= 4; | |
| } | |
| switch (n) { | |
| case 0: | |
| break; | |
| case 1: | |
| __asm_copy_to_user_1(dst, src, retn); | |
| break; | |
| case 2: | |
| __asm_copy_to_user_2(dst, src, retn); | |
| break; | |
| case 3: | |
| __asm_copy_to_user_3(dst, src, retn); | |
| break; | |
| } | |
| return retn; | |
| } | |
| EXPORT_SYMBOL(__copy_user); | |
| #define __asm_copy_from_user_1(to, from, ret) \ | |
| __asm_copy_user_cont(to, from, ret, \ | |
| " GETB D1Ar1,[%1++]\n" \ | |
| "2: SETB [%0++],D1Ar1\n", \ | |
| "3: ADD %2,%2,#1\n" \ | |
| " SETB [%0++],D1Ar1\n", \ | |
| " .long 2b,3b\n") | |
| #define __asm_copy_from_user_2x_cont(to, from, ret, COPY, FIXUP, TENTRY) \ | |
| __asm_copy_user_cont(to, from, ret, \ | |
| " GETW D1Ar1,[%1++]\n" \ | |
| "2: SETW [%0++],D1Ar1\n" COPY, \ | |
| "3: ADD %2,%2,#2\n" \ | |
| " SETW [%0++],D1Ar1\n" FIXUP, \ | |
| " .long 2b,3b\n" TENTRY) | |
| #define __asm_copy_from_user_2(to, from, ret) \ | |
| __asm_copy_from_user_2x_cont(to, from, ret, "", "", "") | |
| #define __asm_copy_from_user_3(to, from, ret) \ | |
| __asm_copy_from_user_2x_cont(to, from, ret, \ | |
| " GETB D1Ar1,[%1++]\n" \ | |
| "4: SETB [%0++],D1Ar1\n", \ | |
| "5: ADD %2,%2,#1\n" \ | |
| " SETB [%0++],D1Ar1\n", \ | |
| " .long 4b,5b\n") | |
| #define __asm_copy_from_user_4x_cont(to, from, ret, COPY, FIXUP, TENTRY) \ | |
| __asm_copy_user_cont(to, from, ret, \ | |
| " GETD D1Ar1,[%1++]\n" \ | |
| "2: SETD [%0++],D1Ar1\n" COPY, \ | |
| "3: ADD %2,%2,#4\n" \ | |
| " SETD [%0++],D1Ar1\n" FIXUP, \ | |
| " .long 2b,3b\n" TENTRY) | |
| #define __asm_copy_from_user_4(to, from, ret) \ | |
| __asm_copy_from_user_4x_cont(to, from, ret, "", "", "") | |
| #define __asm_copy_from_user_5(to, from, ret) \ | |
| __asm_copy_from_user_4x_cont(to, from, ret, \ | |
| " GETB D1Ar1,[%1++]\n" \ | |
| "4: SETB [%0++],D1Ar1\n", \ | |
| "5: ADD %2,%2,#1\n" \ | |
| " SETB [%0++],D1Ar1\n", \ | |
| " .long 4b,5b\n") | |
| #define __asm_copy_from_user_6x_cont(to, from, ret, COPY, FIXUP, TENTRY) \ | |
| __asm_copy_from_user_4x_cont(to, from, ret, \ | |
| " GETW D1Ar1,[%1++]\n" \ | |
| "4: SETW [%0++],D1Ar1\n" COPY, \ | |
| "5: ADD %2,%2,#2\n" \ | |
| " SETW [%0++],D1Ar1\n" FIXUP, \ | |
| " .long 4b,5b\n" TENTRY) | |
| #define __asm_copy_from_user_6(to, from, ret) \ | |
| __asm_copy_from_user_6x_cont(to, from, ret, "", "", "") | |
| #define __asm_copy_from_user_7(to, from, ret) \ | |
| __asm_copy_from_user_6x_cont(to, from, ret, \ | |
| " GETB D1Ar1,[%1++]\n" \ | |
| "6: SETB [%0++],D1Ar1\n", \ | |
| "7: ADD %2,%2,#1\n" \ | |
| " SETB [%0++],D1Ar1\n", \ | |
| " .long 6b,7b\n") | |
| #define __asm_copy_from_user_8x_cont(to, from, ret, COPY, FIXUP, TENTRY) \ | |
| __asm_copy_from_user_4x_cont(to, from, ret, \ | |
| " GETD D1Ar1,[%1++]\n" \ | |
| "4: SETD [%0++],D1Ar1\n" COPY, \ | |
| "5: ADD %2,%2,#4\n" \ | |
| " SETD [%0++],D1Ar1\n" FIXUP, \ | |
| " .long 4b,5b\n" TENTRY) | |
| #define __asm_copy_from_user_8(to, from, ret) \ | |
| __asm_copy_from_user_8x_cont(to, from, ret, "", "", "") | |
| #define __asm_copy_from_user_9(to, from, ret) \ | |
| __asm_copy_from_user_8x_cont(to, from, ret, \ | |
| " GETB D1Ar1,[%1++]\n" \ | |
| "6: SETB [%0++],D1Ar1\n", \ | |
| "7: ADD %2,%2,#1\n" \ | |
| " SETB [%0++],D1Ar1\n", \ | |
| " .long 6b,7b\n") | |
| #define __asm_copy_from_user_10x_cont(to, from, ret, COPY, FIXUP, TENTRY) \ | |
| __asm_copy_from_user_8x_cont(to, from, ret, \ | |
| " GETW D1Ar1,[%1++]\n" \ | |
| "6: SETW [%0++],D1Ar1\n" COPY, \ | |
| "7: ADD %2,%2,#2\n" \ | |
| " SETW [%0++],D1Ar1\n" FIXUP, \ | |
| " .long 6b,7b\n" TENTRY) | |
| #define __asm_copy_from_user_10(to, from, ret) \ | |
| __asm_copy_from_user_10x_cont(to, from, ret, "", "", "") | |
| #define __asm_copy_from_user_11(to, from, ret) \ | |
| __asm_copy_from_user_10x_cont(to, from, ret, \ | |
| " GETB D1Ar1,[%1++]\n" \ | |
| "8: SETB [%0++],D1Ar1\n", \ | |
| "9: ADD %2,%2,#1\n" \ | |
| " SETB [%0++],D1Ar1\n", \ | |
| " .long 8b,9b\n") | |
| #define __asm_copy_from_user_12x_cont(to, from, ret, COPY, FIXUP, TENTRY) \ | |
| __asm_copy_from_user_8x_cont(to, from, ret, \ | |
| " GETD D1Ar1,[%1++]\n" \ | |
| "6: SETD [%0++],D1Ar1\n" COPY, \ | |
| "7: ADD %2,%2,#4\n" \ | |
| " SETD [%0++],D1Ar1\n" FIXUP, \ | |
| " .long 6b,7b\n" TENTRY) | |
| #define __asm_copy_from_user_12(to, from, ret) \ | |
| __asm_copy_from_user_12x_cont(to, from, ret, "", "", "") | |
| #define __asm_copy_from_user_13(to, from, ret) \ | |
| __asm_copy_from_user_12x_cont(to, from, ret, \ | |
| " GETB D1Ar1,[%1++]\n" \ | |
| "8: SETB [%0++],D1Ar1\n", \ | |
| "9: ADD %2,%2,#1\n" \ | |
| " SETB [%0++],D1Ar1\n", \ | |
| " .long 8b,9b\n") | |
| #define __asm_copy_from_user_14x_cont(to, from, ret, COPY, FIXUP, TENTRY) \ | |
| __asm_copy_from_user_12x_cont(to, from, ret, \ | |
| " GETW D1Ar1,[%1++]\n" \ | |
| "8: SETW [%0++],D1Ar1\n" COPY, \ | |
| "9: ADD %2,%2,#2\n" \ | |
| " SETW [%0++],D1Ar1\n" FIXUP, \ | |
| " .long 8b,9b\n" TENTRY) | |
| #define __asm_copy_from_user_14(to, from, ret) \ | |
| __asm_copy_from_user_14x_cont(to, from, ret, "", "", "") | |
| #define __asm_copy_from_user_15(to, from, ret) \ | |
| __asm_copy_from_user_14x_cont(to, from, ret, \ | |
| " GETB D1Ar1,[%1++]\n" \ | |
| "10: SETB [%0++],D1Ar1\n", \ | |
| "11: ADD %2,%2,#1\n" \ | |
| " SETB [%0++],D1Ar1\n", \ | |
| " .long 10b,11b\n") | |
| #define __asm_copy_from_user_16x_cont(to, from, ret, COPY, FIXUP, TENTRY) \ | |
| __asm_copy_from_user_12x_cont(to, from, ret, \ | |
| " GETD D1Ar1,[%1++]\n" \ | |
| "8: SETD [%0++],D1Ar1\n" COPY, \ | |
| "9: ADD %2,%2,#4\n" \ | |
| " SETD [%0++],D1Ar1\n" FIXUP, \ | |
| " .long 8b,9b\n" TENTRY) | |
| #define __asm_copy_from_user_16(to, from, ret) \ | |
| __asm_copy_from_user_16x_cont(to, from, ret, "", "", "") | |
| #define __asm_copy_from_user_8x64(to, from, ret) \ | |
| asm volatile ( \ | |
| " GETL D0Ar2,D1Ar1,[%1++]\n" \ | |
| "2: SETL [%0++],D0Ar2,D1Ar1\n" \ | |
| "1:\n" \ | |
| " .section .fixup,\"ax\"\n" \ | |
| " MOV D1Ar1,#0\n" \ | |
| " MOV D0Ar2,#0\n" \ | |
| "3: ADD %2,%2,#8\n" \ | |
| " SETL [%0++],D0Ar2,D1Ar1\n" \ | |
| " MOVT D0Ar2,#HI(1b)\n" \ | |
| " JUMP D0Ar2,#LO(1b)\n" \ | |
| " .previous\n" \ | |
| " .section __ex_table,\"a\"\n" \ | |
| " .long 2b,3b\n" \ | |
| " .previous\n" \ | |
| : "=a" (to), "=r" (from), "=r" (ret) \ | |
| : "0" (to), "1" (from), "2" (ret) \ | |
| : "D1Ar1", "D0Ar2", "memory") | |
| /* rewind 'from' pointer when a fault occurs | |
| * | |
| * Rationale: | |
| * A fault occurs while reading from user buffer, which is the | |
| * source. Since the fault is at a single address, we only | |
| * need to rewind by 8 bytes. | |
| * Since we don't write to kernel buffer until we read first, | |
| * the kernel buffer is at the right state and needn't be | |
| * corrected. | |
| */ | |
| #define __asm_copy_from_user_64bit_rapf_loop(to, from, ret, n, id) \ | |
| __asm_copy_user_64bit_rapf_loop(to, from, ret, n, id, \ | |
| "SUB %1, %1, #8\n") | |
| /* rewind 'from' pointer when a fault occurs | |
| * | |
| * Rationale: | |
| * A fault occurs while reading from user buffer, which is the | |
| * source. Since the fault is at a single address, we only | |
| * need to rewind by 4 bytes. | |
| * Since we don't write to kernel buffer until we read first, | |
| * the kernel buffer is at the right state and needn't be | |
| * corrected. | |
| */ | |
| #define __asm_copy_from_user_32bit_rapf_loop(to, from, ret, n, id) \ | |
| __asm_copy_user_32bit_rapf_loop(to, from, ret, n, id, \ | |
| "SUB %1, %1, #4\n") | |
| /* Copy from user to kernel, zeroing the bytes that were inaccessible in | |
| userland. The return-value is the number of bytes that were | |
| inaccessible. */ | |
| unsigned long __copy_user_zeroing(void *pdst, const void __user *psrc, | |
| unsigned long n) | |
| { | |
| register char *dst asm ("A0.2") = pdst; | |
| register const char __user *src asm ("A1.2") = psrc; | |
| unsigned long retn = 0; | |
| if (n == 0) | |
| return 0; | |
| if ((unsigned long) src & 1) { | |
| __asm_copy_from_user_1(dst, src, retn); | |
| n--; | |
| } | |
| if ((unsigned long) dst & 1) { | |
| /* Worst case - byte copy */ | |
| while (n > 0) { | |
| __asm_copy_from_user_1(dst, src, retn); | |
| n--; | |
| if (retn) | |
| goto copy_exception_bytes; | |
| } | |
| } | |
| if (((unsigned long) src & 2) && n >= 2) { | |
| __asm_copy_from_user_2(dst, src, retn); | |
| n -= 2; | |
| } | |
| if ((unsigned long) dst & 2) { | |
| /* Second worst case - word copy */ | |
| while (n >= 2) { | |
| __asm_copy_from_user_2(dst, src, retn); | |
| n -= 2; | |
| if (retn) | |
| goto copy_exception_bytes; | |
| } | |
| } | |
| /* We only need one check after the unalignment-adjustments, | |
| because if both adjustments were done, either both or | |
| neither reference had an exception. */ | |
| if (retn != 0) | |
| goto copy_exception_bytes; | |
| #ifdef USE_RAPF | |
| /* 64 bit copy loop */ | |
| if (!(((unsigned long) src | (unsigned long) dst) & 7)) { | |
| if (n >= RAPF_MIN_BUF_SIZE) { | |
| /* Copy using fast 64bit rapf */ | |
| __asm_copy_from_user_64bit_rapf_loop(dst, src, retn, | |
| n, "64cuz"); | |
| } | |
| while (n >= 8) { | |
| __asm_copy_from_user_8x64(dst, src, retn); | |
| n -= 8; | |
| if (retn) | |
| goto copy_exception_bytes; | |
| } | |
| } | |
| if (n >= RAPF_MIN_BUF_SIZE) { | |
| /* Copy using fast 32bit rapf */ | |
| __asm_copy_from_user_32bit_rapf_loop(dst, src, retn, | |
| n, "32cuz"); | |
| } | |
| #else | |
| /* 64 bit copy loop */ | |
| if (!(((unsigned long) src | (unsigned long) dst) & 7)) { | |
| while (n >= 8) { | |
| __asm_copy_from_user_8x64(dst, src, retn); | |
| n -= 8; | |
| if (retn) | |
| goto copy_exception_bytes; | |
| } | |
| } | |
| #endif | |
| while (n >= 4) { | |
| __asm_copy_from_user_4(dst, src, retn); | |
| n -= 4; | |
| if (retn) | |
| goto copy_exception_bytes; | |
| } | |
| /* If we get here, there were no memory read faults. */ | |
| switch (n) { | |
| /* These copies are at least "naturally aligned" (so we don't | |
| have to check each byte), due to the src alignment code. | |
| The *_3 case *will* get the correct count for retn. */ | |
| case 0: | |
| /* This case deliberately left in (if you have doubts check the | |
| generated assembly code). */ | |
| break; | |
| case 1: | |
| __asm_copy_from_user_1(dst, src, retn); | |
| break; | |
| case 2: | |
| __asm_copy_from_user_2(dst, src, retn); | |
| break; | |
| case 3: | |
| __asm_copy_from_user_3(dst, src, retn); | |
| break; | |
| } | |
| /* If we get here, retn correctly reflects the number of failing | |
| bytes. */ | |
| return retn; | |
| copy_exception_bytes: | |
| /* We already have "retn" bytes cleared, and need to clear the | |
| remaining "n" bytes. A non-optimized simple byte-for-byte in-line | |
| memset is preferred here, since this isn't speed-critical code and | |
| we'd rather have this a leaf-function than calling memset. */ | |
| { | |
| char *endp; | |
| for (endp = dst + n; dst < endp; dst++) | |
| *dst = 0; | |
| } | |
| return retn + n; | |
| } | |
| EXPORT_SYMBOL(__copy_user_zeroing); | |
| #define __asm_clear_8x64(to, ret) \ | |
| asm volatile ( \ | |
| " MOV D0Ar2,#0\n" \ | |
| " MOV D1Ar1,#0\n" \ | |
| " SETL [%0],D0Ar2,D1Ar1\n" \ | |
| "2: SETL [%0++],D0Ar2,D1Ar1\n" \ | |
| "1:\n" \ | |
| " .section .fixup,\"ax\"\n" \ | |
| "3: ADD %1,%1,#8\n" \ | |
| " MOVT D0Ar2,#HI(1b)\n" \ | |
| " JUMP D0Ar2,#LO(1b)\n" \ | |
| " .previous\n" \ | |
| " .section __ex_table,\"a\"\n" \ | |
| " .long 2b,3b\n" \ | |
| " .previous\n" \ | |
| : "=r" (to), "=r" (ret) \ | |
| : "0" (to), "1" (ret) \ | |
| : "D1Ar1", "D0Ar2", "memory") | |
| /* Zero userspace. */ | |
| #define __asm_clear(to, ret, CLEAR, FIXUP, TENTRY) \ | |
| asm volatile ( \ | |
| " MOV D1Ar1,#0\n" \ | |
| CLEAR \ | |
| "1:\n" \ | |
| " .section .fixup,\"ax\"\n" \ | |
| FIXUP \ | |
| " MOVT D1Ar1,#HI(1b)\n" \ | |
| " JUMP D1Ar1,#LO(1b)\n" \ | |
| " .previous\n" \ | |
| " .section __ex_table,\"a\"\n" \ | |
| TENTRY \ | |
| " .previous" \ | |
| : "=r" (to), "=r" (ret) \ | |
| : "0" (to), "1" (ret) \ | |
| : "D1Ar1", "memory") | |
| #define __asm_clear_1(to, ret) \ | |
| __asm_clear(to, ret, \ | |
| " SETB [%0],D1Ar1\n" \ | |
| "2: SETB [%0++],D1Ar1\n", \ | |
| "3: ADD %1,%1,#1\n", \ | |
| " .long 2b,3b\n") | |
| #define __asm_clear_2(to, ret) \ | |
| __asm_clear(to, ret, \ | |
| " SETW [%0],D1Ar1\n" \ | |
| "2: SETW [%0++],D1Ar1\n", \ | |
| "3: ADD %1,%1,#2\n", \ | |
| " .long 2b,3b\n") | |
| #define __asm_clear_3(to, ret) \ | |
| __asm_clear(to, ret, \ | |
| "2: SETW [%0++],D1Ar1\n" \ | |
| " SETB [%0],D1Ar1\n" \ | |
| "3: SETB [%0++],D1Ar1\n", \ | |
| "4: ADD %1,%1,#2\n" \ | |
| "5: ADD %1,%1,#1\n", \ | |
| " .long 2b,4b\n" \ | |
| " .long 3b,5b\n") | |
| #define __asm_clear_4x_cont(to, ret, CLEAR, FIXUP, TENTRY) \ | |
| __asm_clear(to, ret, \ | |
| " SETD [%0],D1Ar1\n" \ | |
| "2: SETD [%0++],D1Ar1\n" CLEAR, \ | |
| "3: ADD %1,%1,#4\n" FIXUP, \ | |
| " .long 2b,3b\n" TENTRY) | |
| #define __asm_clear_4(to, ret) \ | |
| __asm_clear_4x_cont(to, ret, "", "", "") | |
| #define __asm_clear_8x_cont(to, ret, CLEAR, FIXUP, TENTRY) \ | |
| __asm_clear_4x_cont(to, ret, \ | |
| " SETD [%0],D1Ar1\n" \ | |
| "4: SETD [%0++],D1Ar1\n" CLEAR, \ | |
| "5: ADD %1,%1,#4\n" FIXUP, \ | |
| " .long 4b,5b\n" TENTRY) | |
| #define __asm_clear_8(to, ret) \ | |
| __asm_clear_8x_cont(to, ret, "", "", "") | |
| #define __asm_clear_12x_cont(to, ret, CLEAR, FIXUP, TENTRY) \ | |
| __asm_clear_8x_cont(to, ret, \ | |
| " SETD [%0],D1Ar1\n" \ | |
| "6: SETD [%0++],D1Ar1\n" CLEAR, \ | |
| "7: ADD %1,%1,#4\n" FIXUP, \ | |
| " .long 6b,7b\n" TENTRY) | |
| #define __asm_clear_12(to, ret) \ | |
| __asm_clear_12x_cont(to, ret, "", "", "") | |
| #define __asm_clear_16x_cont(to, ret, CLEAR, FIXUP, TENTRY) \ | |
| __asm_clear_12x_cont(to, ret, \ | |
| " SETD [%0],D1Ar1\n" \ | |
| "8: SETD [%0++],D1Ar1\n" CLEAR, \ | |
| "9: ADD %1,%1,#4\n" FIXUP, \ | |
| " .long 8b,9b\n" TENTRY) | |
| #define __asm_clear_16(to, ret) \ | |
| __asm_clear_16x_cont(to, ret, "", "", "") | |
| unsigned long __do_clear_user(void __user *pto, unsigned long pn) | |
| { | |
| register char __user *dst asm ("D0Re0") = pto; | |
| register unsigned long n asm ("D1Re0") = pn; | |
| register unsigned long retn asm ("D0Ar6") = 0; | |
| if ((unsigned long) dst & 1) { | |
| __asm_clear_1(dst, retn); | |
| n--; | |
| } | |
| if ((unsigned long) dst & 2) { | |
| __asm_clear_2(dst, retn); | |
| n -= 2; | |
| } | |
| /* 64 bit copy loop */ | |
| if (!((__force unsigned long) dst & 7)) { | |
| while (n >= 8) { | |
| __asm_clear_8x64(dst, retn); | |
| n -= 8; | |
| } | |
| } | |
| while (n >= 16) { | |
| __asm_clear_16(dst, retn); | |
| n -= 16; | |
| } | |
| while (n >= 4) { | |
| __asm_clear_4(dst, retn); | |
| n -= 4; | |
| } | |
| switch (n) { | |
| case 0: | |
| break; | |
| case 1: | |
| __asm_clear_1(dst, retn); | |
| break; | |
| case 2: | |
| __asm_clear_2(dst, retn); | |
| break; | |
| case 3: | |
| __asm_clear_3(dst, retn); | |
| break; | |
| } | |
| return retn; | |
| } | |
| EXPORT_SYMBOL(__do_clear_user); | |
| unsigned char __get_user_asm_b(const void __user *addr, long *err) | |
| { | |
| register unsigned char x asm ("D0Re0") = 0; | |
| asm volatile ( | |
| " GETB %0,[%2]\n" | |
| "1:\n" | |
| " GETB %0,[%2]\n" | |
| "2:\n" | |
| " .section .fixup,\"ax\"\n" | |
| "3: MOV D0FrT,%3\n" | |
| " SETD [%1],D0FrT\n" | |
| " MOVT D0FrT,#HI(2b)\n" | |
| " JUMP D0FrT,#LO(2b)\n" | |
| " .previous\n" | |
| " .section __ex_table,\"a\"\n" | |
| " .long 1b,3b\n" | |
| " .previous\n" | |
| : "=r" (x) | |
| : "r" (err), "r" (addr), "P" (-EFAULT) | |
| : "D0FrT"); | |
| return x; | |
| } | |
| EXPORT_SYMBOL(__get_user_asm_b); | |
| unsigned short __get_user_asm_w(const void __user *addr, long *err) | |
| { | |
| register unsigned short x asm ("D0Re0") = 0; | |
| asm volatile ( | |
| " GETW %0,[%2]\n" | |
| "1:\n" | |
| " GETW %0,[%2]\n" | |
| "2:\n" | |
| " .section .fixup,\"ax\"\n" | |
| "3: MOV D0FrT,%3\n" | |
| " SETD [%1],D0FrT\n" | |
| " MOVT D0FrT,#HI(2b)\n" | |
| " JUMP D0FrT,#LO(2b)\n" | |
| " .previous\n" | |
| " .section __ex_table,\"a\"\n" | |
| " .long 1b,3b\n" | |
| " .previous\n" | |
| : "=r" (x) | |
| : "r" (err), "r" (addr), "P" (-EFAULT) | |
| : "D0FrT"); | |
| return x; | |
| } | |
| EXPORT_SYMBOL(__get_user_asm_w); | |
| unsigned int __get_user_asm_d(const void __user *addr, long *err) | |
| { | |
| register unsigned int x asm ("D0Re0") = 0; | |
| asm volatile ( | |
| " GETD %0,[%2]\n" | |
| "1:\n" | |
| " GETD %0,[%2]\n" | |
| "2:\n" | |
| " .section .fixup,\"ax\"\n" | |
| "3: MOV D0FrT,%3\n" | |
| " SETD [%1],D0FrT\n" | |
| " MOVT D0FrT,#HI(2b)\n" | |
| " JUMP D0FrT,#LO(2b)\n" | |
| " .previous\n" | |
| " .section __ex_table,\"a\"\n" | |
| " .long 1b,3b\n" | |
| " .previous\n" | |
| : "=r" (x) | |
| : "r" (err), "r" (addr), "P" (-EFAULT) | |
| : "D0FrT"); | |
| return x; | |
| } | |
| EXPORT_SYMBOL(__get_user_asm_d); | |
| long __put_user_asm_b(unsigned int x, void __user *addr) | |
| { | |
| register unsigned int err asm ("D0Re0") = 0; | |
| asm volatile ( | |
| " MOV %0,#0\n" | |
| " SETB [%2],%1\n" | |
| "1:\n" | |
| " SETB [%2],%1\n" | |
| "2:\n" | |
| ".section .fixup,\"ax\"\n" | |
| "3: MOV %0,%3\n" | |
| " MOVT D0FrT,#HI(2b)\n" | |
| " JUMP D0FrT,#LO(2b)\n" | |
| ".previous\n" | |
| ".section __ex_table,\"a\"\n" | |
| " .long 1b,3b\n" | |
| ".previous" | |
| : "=r"(err) | |
| : "d" (x), "a" (addr), "P"(-EFAULT) | |
| : "D0FrT"); | |
| return err; | |
| } | |
| EXPORT_SYMBOL(__put_user_asm_b); | |
| long __put_user_asm_w(unsigned int x, void __user *addr) | |
| { | |
| register unsigned int err asm ("D0Re0") = 0; | |
| asm volatile ( | |
| " MOV %0,#0\n" | |
| " SETW [%2],%1\n" | |
| "1:\n" | |
| " SETW [%2],%1\n" | |
| "2:\n" | |
| ".section .fixup,\"ax\"\n" | |
| "3: MOV %0,%3\n" | |
| " MOVT D0FrT,#HI(2b)\n" | |
| " JUMP D0FrT,#LO(2b)\n" | |
| ".previous\n" | |
| ".section __ex_table,\"a\"\n" | |
| " .long 1b,3b\n" | |
| ".previous" | |
| : "=r"(err) | |
| : "d" (x), "a" (addr), "P"(-EFAULT) | |
| : "D0FrT"); | |
| return err; | |
| } | |
| EXPORT_SYMBOL(__put_user_asm_w); | |
| long __put_user_asm_d(unsigned int x, void __user *addr) | |
| { | |
| register unsigned int err asm ("D0Re0") = 0; | |
| asm volatile ( | |
| " MOV %0,#0\n" | |
| " SETD [%2],%1\n" | |
| "1:\n" | |
| " SETD [%2],%1\n" | |
| "2:\n" | |
| ".section .fixup,\"ax\"\n" | |
| "3: MOV %0,%3\n" | |
| " MOVT D0FrT,#HI(2b)\n" | |
| " JUMP D0FrT,#LO(2b)\n" | |
| ".previous\n" | |
| ".section __ex_table,\"a\"\n" | |
| " .long 1b,3b\n" | |
| ".previous" | |
| : "=r"(err) | |
| : "d" (x), "a" (addr), "P"(-EFAULT) | |
| : "D0FrT"); | |
| return err; | |
| } | |
| EXPORT_SYMBOL(__put_user_asm_d); | |
| long __put_user_asm_l(unsigned long long x, void __user *addr) | |
| { | |
| register unsigned int err asm ("D0Re0") = 0; | |
| asm volatile ( | |
| " MOV %0,#0\n" | |
| " SETL [%2],%1,%t1\n" | |
| "1:\n" | |
| " SETL [%2],%1,%t1\n" | |
| "2:\n" | |
| ".section .fixup,\"ax\"\n" | |
| "3: MOV %0,%3\n" | |
| " MOVT D0FrT,#HI(2b)\n" | |
| " JUMP D0FrT,#LO(2b)\n" | |
| ".previous\n" | |
| ".section __ex_table,\"a\"\n" | |
| " .long 1b,3b\n" | |
| ".previous" | |
| : "=r"(err) | |
| : "d" (x), "a" (addr), "P"(-EFAULT) | |
| : "D0FrT"); | |
| return err; | |
| } | |
| EXPORT_SYMBOL(__put_user_asm_l); | |
| long strnlen_user(const char __user *src, long count) | |
| { | |
| long res; | |
| if (!access_ok(VERIFY_READ, src, 0)) | |
| return 0; | |
| asm volatile (" MOV D0Ar4, %1\n" | |
| " MOV D0Ar6, %2\n" | |
| "0:\n" | |
| " SUBS D0FrT, D0Ar6, #0\n" | |
| " SUB D0Ar6, D0Ar6, #1\n" | |
| " BLE 2f\n" | |
| " GETB D0FrT, [D0Ar4+#1++]\n" | |
| "1:\n" | |
| " TST D0FrT, #255\n" | |
| " BNE 0b\n" | |
| "2:\n" | |
| " SUB %0, %2, D0Ar6\n" | |
| "3:\n" | |
| " .section .fixup,\"ax\"\n" | |
| "4:\n" | |
| " MOV %0, #0\n" | |
| " MOVT D0FrT,#HI(3b)\n" | |
| " JUMP D0FrT,#LO(3b)\n" | |
| " .previous\n" | |
| " .section __ex_table,\"a\"\n" | |
| " .long 1b,4b\n" | |
| " .previous\n" | |
| : "=r" (res) | |
| : "r" (src), "r" (count) | |
| : "D0FrT", "D0Ar4", "D0Ar6", "cc"); | |
| return res; | |
| } | |
| EXPORT_SYMBOL(strnlen_user); | |
| long __strncpy_from_user(char *dst, const char __user *src, long count) | |
| { | |
| long res; | |
| if (count == 0) | |
| return 0; | |
| /* | |
| * Currently, in 2.4.0-test9, most ports use a simple byte-copy loop. | |
| * So do we. | |
| * | |
| * This code is deduced from: | |
| * | |
| * char tmp2; | |
| * long tmp1, tmp3; | |
| * tmp1 = count; | |
| * while ((*dst++ = (tmp2 = *src++)) != 0 | |
| * && --tmp1) | |
| * ; | |
| * | |
| * res = count - tmp1; | |
| * | |
| * with tweaks. | |
| */ | |
| asm volatile (" MOV %0,%3\n" | |
| "1:\n" | |
| " GETB D0FrT,[%2++]\n" | |
| "2:\n" | |
| " CMP D0FrT,#0\n" | |
| " SETB [%1++],D0FrT\n" | |
| " BEQ 3f\n" | |
| " SUBS %0,%0,#1\n" | |
| " BNZ 1b\n" | |
| "3:\n" | |
| " SUB %0,%3,%0\n" | |
| "4:\n" | |
| " .section .fixup,\"ax\"\n" | |
| "5:\n" | |
| " MOV %0,%7\n" | |
| " MOVT D0FrT,#HI(4b)\n" | |
| " JUMP D0FrT,#LO(4b)\n" | |
| " .previous\n" | |
| " .section __ex_table,\"a\"\n" | |
| " .long 2b,5b\n" | |
| " .previous" | |
| : "=r" (res), "=r" (dst), "=r" (src), "=r" (count) | |
| : "3" (count), "1" (dst), "2" (src), "P" (-EFAULT) | |
| : "D0FrT", "memory", "cc"); | |
| return res; | |
| } | |
| EXPORT_SYMBOL(__strncpy_from_user); |