162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * copy_page, __copy_user_page, __copy_user implementation of SuperH 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2001 Niibe Yutaka & Kaz Kojima 662306a36Sopenharmony_ci * Copyright (C) 2002 Toshinobu Sugioka 762306a36Sopenharmony_ci * Copyright (C) 2006 Paul Mundt 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/linkage.h> 1062306a36Sopenharmony_ci#include <asm/page.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci/* 1362306a36Sopenharmony_ci * copy_page 1462306a36Sopenharmony_ci * @to: P1 address 1562306a36Sopenharmony_ci * @from: P1 address 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * void copy_page(void *to, void *from) 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* 2162306a36Sopenharmony_ci * r0, r1, r2, r3, r4, r5, r6, r7 --- scratch 2262306a36Sopenharmony_ci * r8 --- from + PAGE_SIZE 2362306a36Sopenharmony_ci * r9 --- not used 2462306a36Sopenharmony_ci * r10 --- to 2562306a36Sopenharmony_ci * r11 --- from 2662306a36Sopenharmony_ci */ 2762306a36Sopenharmony_ciENTRY(copy_page) 2862306a36Sopenharmony_ci mov.l r8,@-r15 2962306a36Sopenharmony_ci mov.l r10,@-r15 3062306a36Sopenharmony_ci mov.l r11,@-r15 3162306a36Sopenharmony_ci mov r4,r10 3262306a36Sopenharmony_ci mov r5,r11 3362306a36Sopenharmony_ci mov r5,r8 3462306a36Sopenharmony_ci mov #(PAGE_SIZE >> 10), r0 3562306a36Sopenharmony_ci shll8 r0 3662306a36Sopenharmony_ci shll2 r0 3762306a36Sopenharmony_ci add r0,r8 3862306a36Sopenharmony_ci ! 3962306a36Sopenharmony_ci1: mov.l @r11+,r0 4062306a36Sopenharmony_ci mov.l @r11+,r1 4162306a36Sopenharmony_ci mov.l @r11+,r2 4262306a36Sopenharmony_ci mov.l @r11+,r3 4362306a36Sopenharmony_ci mov.l @r11+,r4 4462306a36Sopenharmony_ci mov.l @r11+,r5 4562306a36Sopenharmony_ci mov.l @r11+,r6 4662306a36Sopenharmony_ci mov.l @r11+,r7 4762306a36Sopenharmony_ci#if defined(CONFIG_CPU_SH4) 4862306a36Sopenharmony_ci movca.l r0,@r10 4962306a36Sopenharmony_ci#else 5062306a36Sopenharmony_ci mov.l r0,@r10 5162306a36Sopenharmony_ci#endif 5262306a36Sopenharmony_ci add #32,r10 5362306a36Sopenharmony_ci mov.l r7,@-r10 5462306a36Sopenharmony_ci mov.l r6,@-r10 5562306a36Sopenharmony_ci mov.l r5,@-r10 5662306a36Sopenharmony_ci mov.l r4,@-r10 5762306a36Sopenharmony_ci mov.l r3,@-r10 5862306a36Sopenharmony_ci mov.l r2,@-r10 5962306a36Sopenharmony_ci mov.l r1,@-r10 6062306a36Sopenharmony_ci cmp/eq r11,r8 6162306a36Sopenharmony_ci bf/s 1b 6262306a36Sopenharmony_ci add #28,r10 6362306a36Sopenharmony_ci ! 6462306a36Sopenharmony_ci mov.l @r15+,r11 6562306a36Sopenharmony_ci mov.l @r15+,r10 6662306a36Sopenharmony_ci mov.l @r15+,r8 6762306a36Sopenharmony_ci rts 6862306a36Sopenharmony_ci nop 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* 7162306a36Sopenharmony_ci * __kernel_size_t __copy_user(void *to, const void *from, __kernel_size_t n); 7262306a36Sopenharmony_ci * Return the number of bytes NOT copied 7362306a36Sopenharmony_ci */ 7462306a36Sopenharmony_ci#define EX(...) \ 7562306a36Sopenharmony_ci 9999: __VA_ARGS__ ; \ 7662306a36Sopenharmony_ci .section __ex_table, "a"; \ 7762306a36Sopenharmony_ci .long 9999b, 6000f ; \ 7862306a36Sopenharmony_ci .previous 7962306a36Sopenharmony_ci#define EX_NO_POP(...) \ 8062306a36Sopenharmony_ci 9999: __VA_ARGS__ ; \ 8162306a36Sopenharmony_ci .section __ex_table, "a"; \ 8262306a36Sopenharmony_ci .long 9999b, 6005f ; \ 8362306a36Sopenharmony_ci .previous 8462306a36Sopenharmony_ciENTRY(__copy_user) 8562306a36Sopenharmony_ci ! Check if small number of bytes 8662306a36Sopenharmony_ci mov #11,r0 8762306a36Sopenharmony_ci mov r4,r3 8862306a36Sopenharmony_ci cmp/gt r0,r6 ! r6 (len) > r0 (11) 8962306a36Sopenharmony_ci bf/s .L_cleanup_loop_no_pop 9062306a36Sopenharmony_ci add r6,r3 ! last destination address 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci ! Calculate bytes needed to align to src 9362306a36Sopenharmony_ci mov.l r11,@-r15 9462306a36Sopenharmony_ci neg r5,r0 9562306a36Sopenharmony_ci mov.l r10,@-r15 9662306a36Sopenharmony_ci add #4,r0 9762306a36Sopenharmony_ci mov.l r9,@-r15 9862306a36Sopenharmony_ci and #3,r0 9962306a36Sopenharmony_ci mov.l r8,@-r15 10062306a36Sopenharmony_ci tst r0,r0 10162306a36Sopenharmony_ci bt 2f 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci1: 10462306a36Sopenharmony_ci ! Copy bytes to long word align src 10562306a36Sopenharmony_ciEX( mov.b @r5+,r1 ) 10662306a36Sopenharmony_ci dt r0 10762306a36Sopenharmony_ci add #-1,r6 10862306a36Sopenharmony_ciEX( mov.b r1,@r4 ) 10962306a36Sopenharmony_ci bf/s 1b 11062306a36Sopenharmony_ci add #1,r4 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci ! Jump to appropriate routine depending on dest 11362306a36Sopenharmony_ci2: mov #3,r1 11462306a36Sopenharmony_ci mov r6, r2 11562306a36Sopenharmony_ci and r4,r1 11662306a36Sopenharmony_ci shlr2 r2 11762306a36Sopenharmony_ci shll2 r1 11862306a36Sopenharmony_ci mova .L_jump_tbl,r0 11962306a36Sopenharmony_ci mov.l @(r0,r1),r1 12062306a36Sopenharmony_ci jmp @r1 12162306a36Sopenharmony_ci nop 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci .align 2 12462306a36Sopenharmony_ci.L_jump_tbl: 12562306a36Sopenharmony_ci .long .L_dest00 12662306a36Sopenharmony_ci .long .L_dest01 12762306a36Sopenharmony_ci .long .L_dest10 12862306a36Sopenharmony_ci .long .L_dest11 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci/* 13162306a36Sopenharmony_ci * Come here if there are less than 12 bytes to copy 13262306a36Sopenharmony_ci * 13362306a36Sopenharmony_ci * Keep the branch target close, so the bf/s callee doesn't overflow 13462306a36Sopenharmony_ci * and result in a more expensive branch being inserted. This is the 13562306a36Sopenharmony_ci * fast-path for small copies, the jump via the jump table will hit the 13662306a36Sopenharmony_ci * default slow-path cleanup. -PFM. 13762306a36Sopenharmony_ci */ 13862306a36Sopenharmony_ci.L_cleanup_loop_no_pop: 13962306a36Sopenharmony_ci tst r6,r6 ! Check explicitly for zero 14062306a36Sopenharmony_ci bt 1f 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci2: 14362306a36Sopenharmony_ciEX_NO_POP( mov.b @r5+,r0 ) 14462306a36Sopenharmony_ci dt r6 14562306a36Sopenharmony_ciEX_NO_POP( mov.b r0,@r4 ) 14662306a36Sopenharmony_ci bf/s 2b 14762306a36Sopenharmony_ci add #1,r4 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci1: mov #0,r0 ! normal return 15062306a36Sopenharmony_ci5000: 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci# Exception handler: 15362306a36Sopenharmony_ci.section .fixup, "ax" 15462306a36Sopenharmony_ci6005: 15562306a36Sopenharmony_ci mov.l 8000f,r1 15662306a36Sopenharmony_ci mov r3,r0 15762306a36Sopenharmony_ci jmp @r1 15862306a36Sopenharmony_ci sub r4,r0 15962306a36Sopenharmony_ci .align 2 16062306a36Sopenharmony_ci8000: .long 5000b 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci.previous 16362306a36Sopenharmony_ci rts 16462306a36Sopenharmony_ci nop 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci! Destination = 00 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci.L_dest00: 16962306a36Sopenharmony_ci ! Skip the large copy for small transfers 17062306a36Sopenharmony_ci mov #(32+32-4), r0 17162306a36Sopenharmony_ci cmp/gt r6, r0 ! r0 (60) > r6 (len) 17262306a36Sopenharmony_ci bt 1f 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci ! Align dest to a 32 byte boundary 17562306a36Sopenharmony_ci neg r4,r0 17662306a36Sopenharmony_ci add #0x20, r0 17762306a36Sopenharmony_ci and #0x1f, r0 17862306a36Sopenharmony_ci tst r0, r0 17962306a36Sopenharmony_ci bt 2f 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci sub r0, r6 18262306a36Sopenharmony_ci shlr2 r0 18362306a36Sopenharmony_ci3: 18462306a36Sopenharmony_ciEX( mov.l @r5+,r1 ) 18562306a36Sopenharmony_ci dt r0 18662306a36Sopenharmony_ciEX( mov.l r1,@r4 ) 18762306a36Sopenharmony_ci bf/s 3b 18862306a36Sopenharmony_ci add #4,r4 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci2: 19162306a36Sopenharmony_ciEX( mov.l @r5+,r0 ) 19262306a36Sopenharmony_ciEX( mov.l @r5+,r1 ) 19362306a36Sopenharmony_ciEX( mov.l @r5+,r2 ) 19462306a36Sopenharmony_ciEX( mov.l @r5+,r7 ) 19562306a36Sopenharmony_ciEX( mov.l @r5+,r8 ) 19662306a36Sopenharmony_ciEX( mov.l @r5+,r9 ) 19762306a36Sopenharmony_ciEX( mov.l @r5+,r10 ) 19862306a36Sopenharmony_ciEX( mov.l @r5+,r11 ) 19962306a36Sopenharmony_ci#ifdef CONFIG_CPU_SH4 20062306a36Sopenharmony_ciEX( movca.l r0,@r4 ) 20162306a36Sopenharmony_ci#else 20262306a36Sopenharmony_ciEX( mov.l r0,@r4 ) 20362306a36Sopenharmony_ci#endif 20462306a36Sopenharmony_ci add #-32, r6 20562306a36Sopenharmony_ciEX( mov.l r1,@(4,r4) ) 20662306a36Sopenharmony_ci mov #32, r0 20762306a36Sopenharmony_ciEX( mov.l r2,@(8,r4) ) 20862306a36Sopenharmony_ci cmp/gt r6, r0 ! r0 (32) > r6 (len) 20962306a36Sopenharmony_ciEX( mov.l r7,@(12,r4) ) 21062306a36Sopenharmony_ciEX( mov.l r8,@(16,r4) ) 21162306a36Sopenharmony_ciEX( mov.l r9,@(20,r4) ) 21262306a36Sopenharmony_ciEX( mov.l r10,@(24,r4) ) 21362306a36Sopenharmony_ciEX( mov.l r11,@(28,r4) ) 21462306a36Sopenharmony_ci bf/s 2b 21562306a36Sopenharmony_ci add #32,r4 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci1: mov r6, r0 21862306a36Sopenharmony_ci shlr2 r0 21962306a36Sopenharmony_ci tst r0, r0 22062306a36Sopenharmony_ci bt .L_cleanup 22162306a36Sopenharmony_ci1: 22262306a36Sopenharmony_ciEX( mov.l @r5+,r1 ) 22362306a36Sopenharmony_ci dt r0 22462306a36Sopenharmony_ciEX( mov.l r1,@r4 ) 22562306a36Sopenharmony_ci bf/s 1b 22662306a36Sopenharmony_ci add #4,r4 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci bra .L_cleanup 22962306a36Sopenharmony_ci nop 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci! Destination = 10 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci.L_dest10: 23462306a36Sopenharmony_ci mov r2,r7 23562306a36Sopenharmony_ci shlr2 r7 23662306a36Sopenharmony_ci shlr r7 23762306a36Sopenharmony_ci tst r7,r7 23862306a36Sopenharmony_ci mov #7,r0 23962306a36Sopenharmony_ci bt/s 1f 24062306a36Sopenharmony_ci and r0,r2 24162306a36Sopenharmony_ci2: 24262306a36Sopenharmony_ci dt r7 24362306a36Sopenharmony_ci#ifdef CONFIG_CPU_LITTLE_ENDIAN 24462306a36Sopenharmony_ciEX( mov.l @r5+,r0 ) 24562306a36Sopenharmony_ciEX( mov.l @r5+,r1 ) 24662306a36Sopenharmony_ciEX( mov.l @r5+,r8 ) 24762306a36Sopenharmony_ciEX( mov.l @r5+,r9 ) 24862306a36Sopenharmony_ciEX( mov.l @r5+,r10 ) 24962306a36Sopenharmony_ciEX( mov.w r0,@r4 ) 25062306a36Sopenharmony_ci add #2,r4 25162306a36Sopenharmony_ci xtrct r1,r0 25262306a36Sopenharmony_ci xtrct r8,r1 25362306a36Sopenharmony_ci xtrct r9,r8 25462306a36Sopenharmony_ci xtrct r10,r9 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ciEX( mov.l r0,@r4 ) 25762306a36Sopenharmony_ciEX( mov.l r1,@(4,r4) ) 25862306a36Sopenharmony_ciEX( mov.l r8,@(8,r4) ) 25962306a36Sopenharmony_ciEX( mov.l r9,@(12,r4) ) 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ciEX( mov.l @r5+,r1 ) 26262306a36Sopenharmony_ciEX( mov.l @r5+,r8 ) 26362306a36Sopenharmony_ciEX( mov.l @r5+,r0 ) 26462306a36Sopenharmony_ci xtrct r1,r10 26562306a36Sopenharmony_ci xtrct r8,r1 26662306a36Sopenharmony_ci xtrct r0,r8 26762306a36Sopenharmony_ci shlr16 r0 26862306a36Sopenharmony_ciEX( mov.l r10,@(16,r4) ) 26962306a36Sopenharmony_ciEX( mov.l r1,@(20,r4) ) 27062306a36Sopenharmony_ciEX( mov.l r8,@(24,r4) ) 27162306a36Sopenharmony_ciEX( mov.w r0,@(28,r4) ) 27262306a36Sopenharmony_ci bf/s 2b 27362306a36Sopenharmony_ci add #30,r4 27462306a36Sopenharmony_ci#else 27562306a36Sopenharmony_ciEX( mov.l @(28,r5),r0 ) 27662306a36Sopenharmony_ciEX( mov.l @(24,r5),r8 ) 27762306a36Sopenharmony_ciEX( mov.l @(20,r5),r9 ) 27862306a36Sopenharmony_ciEX( mov.l @(16,r5),r10 ) 27962306a36Sopenharmony_ciEX( mov.w r0,@(30,r4) ) 28062306a36Sopenharmony_ci add #-2,r4 28162306a36Sopenharmony_ci xtrct r8,r0 28262306a36Sopenharmony_ci xtrct r9,r8 28362306a36Sopenharmony_ci xtrct r10,r9 28462306a36Sopenharmony_ciEX( mov.l r0,@(28,r4) ) 28562306a36Sopenharmony_ciEX( mov.l r8,@(24,r4) ) 28662306a36Sopenharmony_ciEX( mov.l r9,@(20,r4) ) 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ciEX( mov.l @(12,r5),r0 ) 28962306a36Sopenharmony_ciEX( mov.l @(8,r5),r8 ) 29062306a36Sopenharmony_ci xtrct r0,r10 29162306a36Sopenharmony_ciEX( mov.l @(4,r5),r9 ) 29262306a36Sopenharmony_ci mov.l r10,@(16,r4) 29362306a36Sopenharmony_ciEX( mov.l @r5,r10 ) 29462306a36Sopenharmony_ci xtrct r8,r0 29562306a36Sopenharmony_ci xtrct r9,r8 29662306a36Sopenharmony_ci xtrct r10,r9 29762306a36Sopenharmony_ciEX( mov.l r0,@(12,r4) ) 29862306a36Sopenharmony_ciEX( mov.l r8,@(8,r4) ) 29962306a36Sopenharmony_ci swap.w r10,r0 30062306a36Sopenharmony_ciEX( mov.l r9,@(4,r4) ) 30162306a36Sopenharmony_ciEX( mov.w r0,@(2,r4) ) 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci add #32,r5 30462306a36Sopenharmony_ci bf/s 2b 30562306a36Sopenharmony_ci add #34,r4 30662306a36Sopenharmony_ci#endif 30762306a36Sopenharmony_ci tst r2,r2 30862306a36Sopenharmony_ci bt .L_cleanup 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci1: ! Read longword, write two words per iteration 31162306a36Sopenharmony_ciEX( mov.l @r5+,r0 ) 31262306a36Sopenharmony_ci dt r2 31362306a36Sopenharmony_ci#ifdef CONFIG_CPU_LITTLE_ENDIAN 31462306a36Sopenharmony_ciEX( mov.w r0,@r4 ) 31562306a36Sopenharmony_ci shlr16 r0 31662306a36Sopenharmony_ciEX( mov.w r0,@(2,r4) ) 31762306a36Sopenharmony_ci#else 31862306a36Sopenharmony_ciEX( mov.w r0,@(2,r4) ) 31962306a36Sopenharmony_ci shlr16 r0 32062306a36Sopenharmony_ciEX( mov.w r0,@r4 ) 32162306a36Sopenharmony_ci#endif 32262306a36Sopenharmony_ci bf/s 1b 32362306a36Sopenharmony_ci add #4,r4 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci bra .L_cleanup 32662306a36Sopenharmony_ci nop 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci! Destination = 01 or 11 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci.L_dest01: 33162306a36Sopenharmony_ci.L_dest11: 33262306a36Sopenharmony_ci ! Read longword, write byte, word, byte per iteration 33362306a36Sopenharmony_ciEX( mov.l @r5+,r0 ) 33462306a36Sopenharmony_ci dt r2 33562306a36Sopenharmony_ci#ifdef CONFIG_CPU_LITTLE_ENDIAN 33662306a36Sopenharmony_ciEX( mov.b r0,@r4 ) 33762306a36Sopenharmony_ci shlr8 r0 33862306a36Sopenharmony_ci add #1,r4 33962306a36Sopenharmony_ciEX( mov.w r0,@r4 ) 34062306a36Sopenharmony_ci shlr16 r0 34162306a36Sopenharmony_ciEX( mov.b r0,@(2,r4) ) 34262306a36Sopenharmony_ci bf/s .L_dest01 34362306a36Sopenharmony_ci add #3,r4 34462306a36Sopenharmony_ci#else 34562306a36Sopenharmony_ciEX( mov.b r0,@(3,r4) ) 34662306a36Sopenharmony_ci shlr8 r0 34762306a36Sopenharmony_ci swap.w r0,r7 34862306a36Sopenharmony_ciEX( mov.b r7,@r4 ) 34962306a36Sopenharmony_ci add #1,r4 35062306a36Sopenharmony_ciEX( mov.w r0,@r4 ) 35162306a36Sopenharmony_ci bf/s .L_dest01 35262306a36Sopenharmony_ci add #3,r4 35362306a36Sopenharmony_ci#endif 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci! Cleanup last few bytes 35662306a36Sopenharmony_ci.L_cleanup: 35762306a36Sopenharmony_ci mov r6,r0 35862306a36Sopenharmony_ci and #3,r0 35962306a36Sopenharmony_ci tst r0,r0 36062306a36Sopenharmony_ci bt .L_exit 36162306a36Sopenharmony_ci mov r0,r6 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci.L_cleanup_loop: 36462306a36Sopenharmony_ciEX( mov.b @r5+,r0 ) 36562306a36Sopenharmony_ci dt r6 36662306a36Sopenharmony_ciEX( mov.b r0,@r4 ) 36762306a36Sopenharmony_ci bf/s .L_cleanup_loop 36862306a36Sopenharmony_ci add #1,r4 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci.L_exit: 37162306a36Sopenharmony_ci mov #0,r0 ! normal return 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci5000: 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci# Exception handler: 37662306a36Sopenharmony_ci.section .fixup, "ax" 37762306a36Sopenharmony_ci6000: 37862306a36Sopenharmony_ci mov.l 8000f,r1 37962306a36Sopenharmony_ci mov r3,r0 38062306a36Sopenharmony_ci jmp @r1 38162306a36Sopenharmony_ci sub r4,r0 38262306a36Sopenharmony_ci .align 2 38362306a36Sopenharmony_ci8000: .long 5000b 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci.previous 38662306a36Sopenharmony_ci mov.l @r15+,r8 38762306a36Sopenharmony_ci mov.l @r15+,r9 38862306a36Sopenharmony_ci mov.l @r15+,r10 38962306a36Sopenharmony_ci rts 39062306a36Sopenharmony_ci mov.l @r15+,r11 391