|  | /* | 
|  | * OpenRISC string.S | 
|  | * | 
|  | * Linux architectural port borrowing liberally from similar works of | 
|  | * others.  All original copyrights apply as per the original source | 
|  | * declaration. | 
|  | * | 
|  | * Modifications for the OpenRISC architecture: | 
|  | * Copyright (C) 2003 Matjaz Breskvar <phoenix@bsemi.com> | 
|  | * Copyright (C) 2010-2011 Jonas Bonn <jonas@southpole.se> | 
|  | * | 
|  | *      This program is free software; you can redistribute it and/or | 
|  | *      modify it under the terms of the GNU General Public License | 
|  | *      as published by the Free Software Foundation; either version | 
|  | *      2 of the License, or (at your option) any later version. | 
|  | */ | 
|  |  | 
|  | #include <linux/linkage.h> | 
|  | #include <asm/errno.h> | 
|  |  | 
|  | /* | 
|  | * this can be optimized by doing gcc inline assemlby with | 
|  | * proper constraints (no need to save args registers...) | 
|  | * | 
|  | */ | 
|  |  | 
|  |  | 
|  | /* | 
|  | * | 
|  | * int __copy_tofrom_user(void *to, const void *from, unsigned long size); | 
|  | * | 
|  | * NOTE: it returns number of bytes NOT copied !!! | 
|  | * | 
|  | */ | 
|  | .global	__copy_tofrom_user | 
|  | __copy_tofrom_user: | 
|  | l.addi  r1,r1,-12 | 
|  | l.sw    0(r1),r6 | 
|  | l.sw    4(r1),r4 | 
|  | l.sw    8(r1),r3 | 
|  |  | 
|  | l.addi  r11,r5,0 | 
|  | 2:  	l.sfeq  r11,r0 | 
|  | l.bf    1f | 
|  | l.addi  r11,r11,-1 | 
|  | 8:    	l.lbz   r6,0(r4) | 
|  | 9:    	l.sb    0(r3),r6 | 
|  | l.addi  r3,r3,1 | 
|  | l.j     2b | 
|  | l.addi  r4,r4,1 | 
|  | 1: | 
|  | l.addi  r11,r11,1               // r11 holds the return value | 
|  |  | 
|  | l.lwz   r6,0(r1) | 
|  | l.lwz   r4,4(r1) | 
|  | l.lwz   r3,8(r1) | 
|  | l.jr    r9 | 
|  | l.addi  r1,r1,12 | 
|  |  | 
|  | .section .fixup, "ax" | 
|  | 99: | 
|  | l.j     1b | 
|  | l.nop | 
|  | .previous | 
|  |  | 
|  | .section __ex_table, "a" | 
|  | .long 8b, 99b		// read fault | 
|  | .long 9b, 99b		// write fault | 
|  | .previous | 
|  |  | 
|  | /* | 
|  | * unsigned long clear_user(void *addr, unsigned long size) ; | 
|  | * | 
|  | * NOTE: it returns number of bytes NOT cleared !!! | 
|  | */ | 
|  | .global	__clear_user | 
|  | __clear_user: | 
|  | l.addi  r1,r1,-8 | 
|  | l.sw    0(r1),r4 | 
|  | l.sw    4(r1),r3 | 
|  |  | 
|  | 2:	l.sfeq	r4,r0 | 
|  | l.bf	1f | 
|  | l.addi	r4,r4,-1 | 
|  | 9:	l.sb	0(r3),r0 | 
|  | l.j	2b | 
|  | l.addi  r3,r3,1 | 
|  |  | 
|  | 1: | 
|  | l.addi  r11,r4,1 | 
|  |  | 
|  | l.lwz	r4,0(r1) | 
|  | l.lwz	r3,4(r1) | 
|  | l.jr	r9 | 
|  | l.addi	r1,r1,8 | 
|  |  | 
|  | .section .fixup, "ax" | 
|  | 99: | 
|  | l.j     1b | 
|  | l.nop | 
|  | .previous | 
|  |  | 
|  | .section __ex_table, "a" | 
|  | .long 9b, 99b		// write fault | 
|  | .previous | 
|  |  | 
|  | /* | 
|  | * long strncpy_from_user(char *dst, const char *src, long count) | 
|  | * | 
|  | * | 
|  | */ | 
|  | .global	__strncpy_from_user | 
|  | __strncpy_from_user: | 
|  | l.addi  r1,r1,-16 | 
|  | l.sw    0(r1),r6 | 
|  | l.sw    4(r1),r5 | 
|  | l.sw    8(r1),r4 | 
|  | l.sw    12(r1),r3 | 
|  |  | 
|  | l.addi  r11,r5,0 | 
|  | 2:  	l.sfeq  r5,r0 | 
|  | l.bf    1f | 
|  | l.addi  r5,r5,-1 | 
|  | 8:    	l.lbz   r6,0(r4) | 
|  | l.sfeq  r6,r0 | 
|  | l.bf    1f | 
|  | 9:    	l.sb    0(r3),r6 | 
|  | l.addi  r3,r3,1 | 
|  | l.j     2b | 
|  | l.addi  r4,r4,1 | 
|  | 1: | 
|  | l.lwz   r6,0(r1) | 
|  | l.addi  r5,r5,1 | 
|  | l.sub   r11,r11,r5              // r11 holds the return value | 
|  |  | 
|  | l.lwz   r6,0(r1) | 
|  | l.lwz   r5,4(r1) | 
|  | l.lwz   r4,8(r1) | 
|  | l.lwz   r3,12(r1) | 
|  | l.jr    r9 | 
|  | l.addi  r1,r1,16 | 
|  |  | 
|  | .section .fixup, "ax" | 
|  | 99: | 
|  | l.movhi r11,hi(-EFAULT) | 
|  | l.ori   r11,r11,lo(-EFAULT) | 
|  |  | 
|  | l.lwz   r6,0(r1) | 
|  | l.lwz   r5,4(r1) | 
|  | l.lwz   r4,8(r1) | 
|  | l.lwz   r3,12(r1) | 
|  | l.jr	r9 | 
|  | l.addi  r1,r1,16 | 
|  | .previous | 
|  |  | 
|  | .section __ex_table, "a" | 
|  | .long 8b, 99b		// read fault | 
|  | .previous | 
|  |  | 
|  | /* | 
|  | * extern int __strnlen_user(const char *str, long len, unsigned long top); | 
|  | * | 
|  | * | 
|  | * RTRN: - length of a string including NUL termination character | 
|  | *       - on page fault 0 | 
|  | */ | 
|  |  | 
|  | .global __strnlen_user | 
|  | __strnlen_user: | 
|  | l.addi  r1,r1,-8 | 
|  | l.sw    0(r1),r6 | 
|  | l.sw    4(r1),r3 | 
|  |  | 
|  | l.addi  r11,r0,0 | 
|  | 2:  	l.sfeq  r11,r4 | 
|  | l.bf    1f | 
|  | l.addi  r11,r11,1 | 
|  | 8:    	l.lbz   r6,0(r3) | 
|  | l.sfeq  r6,r0 | 
|  | l.bf    1f | 
|  | l.sfgeu r3,r5                  // are we over the top ? | 
|  | l.bf    99f | 
|  | l.j     2b | 
|  | l.addi  r3,r3,1 | 
|  |  | 
|  | 1: | 
|  | l.lwz   r6,0(r1) | 
|  | l.lwz	r3,4(r1) | 
|  | l.jr    r9 | 
|  | l.addi  r1,r1,8 | 
|  |  | 
|  | .section .fixup, "ax" | 
|  | 99: | 
|  | l.addi  r11,r0,0 | 
|  |  | 
|  | l.lwz   r6,0(r1) | 
|  | l.lwz	r3,4(r1) | 
|  | l.jr	r9 | 
|  | l.addi  r1,r1,8 | 
|  | .previous | 
|  |  | 
|  | .section __ex_table, "a" | 
|  | .long 8b, 99b		// read fault | 
|  | .previous |