···11+/*22+ * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>33+ * Copyright (C) 2004 Microtronix Datacom Ltd.44+ *55+ * This file is subject to the terms and conditions of the GNU General Public66+ * License. See the file "COPYING" in the main directory of this archive77+ * for more details.88+ */99+1010+#ifndef _ASM_NIOS2_MMU_H1111+#define _ASM_NIOS2_MMU_H1212+1313+/* Default "unsigned long" context */1414+typedef unsigned long mm_context_t;1515+1616+#endif /* _ASM_NIOS2_MMU_H */
+111
arch/nios2/include/asm/page.h
···11+/*22+ * Copyright (C) 2011 Tobias Klauser <tklauser@distanz.ch>33+ * Copyright (C) 2004 Microtronix Datacom Ltd.44+ *55+ * MMU support based on asm/page.h from mips which is:66+ *77+ * Copyright (C) 1994 - 1999, 2000, 03 Ralf Baechle88+ * Copyright (C) 1999, 2000 Silicon Graphics, Inc.99+ *1010+ * This file is subject to the terms and conditions of the GNU General Public1111+ * License. See the file "COPYING" in the main directory of this archive1212+ * for more details.1313+ */1414+1515+#ifndef _ASM_NIOS2_PAGE_H1616+#define _ASM_NIOS2_PAGE_H1717+1818+#include <linux/pfn.h>1919+#include <linux/const.h>2020+2121+/*2222+ * PAGE_SHIFT determines the page size2323+ */2424+#define PAGE_SHIFT 122525+#define PAGE_SIZE (_AC(1, UL) << PAGE_SHIFT)2626+#define PAGE_MASK (~(PAGE_SIZE - 1))2727+2828+/*2929+ * PAGE_OFFSET -- the first address of the first page of memory.3030+ */3131+#define PAGE_OFFSET \3232+ (CONFIG_NIOS2_MEM_BASE + CONFIG_NIOS2_KERNEL_REGION_BASE)3333+3434+#ifndef __ASSEMBLY__3535+3636+/*3737+ * This gives the physical RAM offset.3838+ */3939+#define PHYS_OFFSET CONFIG_NIOS2_MEM_BASE4040+4141+/*4242+ * It's normally defined only for FLATMEM config but it's4343+ * used in our early mem init code for all memory models.4444+ * So always define it.4545+ */4646+#define ARCH_PFN_OFFSET PFN_UP(PHYS_OFFSET)4747+4848+#define clear_page(page) memset((page), 0, PAGE_SIZE)4949+#define copy_page(to, from) memcpy((to), (from), PAGE_SIZE)5050+5151+struct page;5252+5353+extern void clear_user_page(void *addr, unsigned long vaddr, struct page *page);5454+extern void copy_user_page(void *vto, void *vfrom, unsigned long vaddr,5555+ struct page *to);5656+5757+extern unsigned long shm_align_mask;5858+5959+/*6060+ * These are used to make use of C type-checking.6161+ */6262+typedef struct page *pgtable_t;6363+typedef struct { unsigned long pte; } pte_t;6464+typedef struct { unsigned long pgd; } pgd_t;6565+typedef struct { unsigned long pgprot; } pgprot_t;6666+6767+#define pte_val(x) ((x).pte)6868+#define pgd_val(x) ((x).pgd)6969+#define pgprot_val(x) ((x).pgprot)7070+7171+#define __pte(x) ((pte_t) { (x) })7272+#define __pgd(x) ((pgd_t) { (x) })7373+#define __pgprot(x) ((pgprot_t) { (x) })7474+7575+extern unsigned long memory_start;7676+extern unsigned long memory_end;7777+extern unsigned long memory_size;7878+7979+extern struct page *mem_map;8080+8181+#endif /* !__ASSEMBLY__ */8282+8383+# define __pa(x) \8484+ ((unsigned long)(x) - PAGE_OFFSET + PHYS_OFFSET)8585+# define __va(x) \8686+ ((void *)((unsigned long)(x) + PAGE_OFFSET - PHYS_OFFSET))8787+8888+#define page_to_virt(page) \8989+ ((((page) - mem_map) << PAGE_SHIFT) + PAGE_OFFSET)9090+9191+# define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT)9292+# define pfn_valid(pfn) ((pfn) >= ARCH_PFN_OFFSET && \9393+ (pfn) < max_mapnr)9494+9595+# define virt_to_page(vaddr) pfn_to_page(PFN_DOWN(virt_to_phys(vaddr)))9696+# define virt_addr_valid(vaddr) pfn_valid(PFN_DOWN(virt_to_phys(vaddr)))9797+9898+# define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | \9999+ VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)100100+101101+# define UNCAC_ADDR(addr) \102102+ ((void *)((unsigned)(addr) | CONFIG_NIOS2_IO_REGION_BASE))103103+# define CAC_ADDR(addr) \104104+ ((void *)(((unsigned)(addr) & ~CONFIG_NIOS2_IO_REGION_BASE) | \105105+ CONFIG_NIOS2_KERNEL_REGION_BASE))106106+107107+#include <asm-generic/memory_model.h>108108+109109+#include <asm-generic/getorder.h>110110+111111+#endif /* _ASM_NIOS2_PAGE_H */
+231
arch/nios2/include/asm/uaccess.h
···11+/*22+ * User space memory access functions for Nios II33+ *44+ * Copyright (C) 2010-2011, Tobias Klauser <tklauser@distanz.ch>55+ * Copyright (C) 2009, Wind River Systems Inc66+ * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com77+ *88+ * This file is subject to the terms and conditions of the GNU General Public99+ * License. See the file "COPYING" in the main directory of this archive1010+ * for more details.1111+ */1212+1313+#ifndef _ASM_NIOS2_UACCESS_H1414+#define _ASM_NIOS2_UACCESS_H1515+1616+#include <linux/errno.h>1717+#include <linux/thread_info.h>1818+#include <linux/string.h>1919+2020+#include <asm/page.h>2121+2222+#define VERIFY_READ 02323+#define VERIFY_WRITE 12424+2525+/*2626+ * The exception table consists of pairs of addresses: the first is the2727+ * address of an instruction that is allowed to fault, and the second is2828+ * the address at which the program should continue. No registers are2929+ * modified, so it is entirely up to the continuation code to figure out3030+ * what to do.3131+ *3232+ * All the routines below use bits of fixup code that are out of line3333+ * with the main instruction path. This means when everything is well,3434+ * we don't even have to jump over them. Further, they do not intrude3535+ * on our cache or tlb entries.3636+ */3737+struct exception_table_entry {3838+ unsigned long insn;3939+ unsigned long fixup;4040+};4141+4242+extern int fixup_exception(struct pt_regs *regs);4343+4444+/*4545+ * Segment stuff4646+ */4747+#define MAKE_MM_SEG(s) ((mm_segment_t) { (s) })4848+#define USER_DS MAKE_MM_SEG(0x80000000UL)4949+#define KERNEL_DS MAKE_MM_SEG(0)5050+5151+#define get_ds() (KERNEL_DS)5252+5353+#define get_fs() (current_thread_info()->addr_limit)5454+#define set_fs(seg) (current_thread_info()->addr_limit = (seg))5555+5656+#define segment_eq(a, b) ((a).seg == (b).seg)5757+5858+#define __access_ok(addr, len) \5959+ (((signed long)(((long)get_fs().seg) & \6060+ ((long)(addr) | (((long)(addr)) + (len)) | (len)))) == 0)6161+6262+#define access_ok(type, addr, len) \6363+ likely(__access_ok((unsigned long)(addr), (unsigned long)(len)))6464+6565+# define __EX_TABLE_SECTION ".section __ex_table,\"a\"\n"6666+6767+/*6868+ * Zero Userspace6969+ */7070+7171+static inline unsigned long __must_check __clear_user(void __user *to,7272+ unsigned long n)7373+{7474+ __asm__ __volatile__ (7575+ "1: stb zero, 0(%1)\n"7676+ " addi %0, %0, -1\n"7777+ " addi %1, %1, 1\n"7878+ " bne %0, zero, 1b\n"7979+ "2:\n"8080+ __EX_TABLE_SECTION8181+ ".word 1b, 2b\n"8282+ ".previous\n"8383+ : "=r" (n), "=r" (to)8484+ : "0" (n), "1" (to)8585+ );8686+8787+ return n;8888+}8989+9090+static inline unsigned long __must_check clear_user(void __user *to,9191+ unsigned long n)9292+{9393+ if (!access_ok(VERIFY_WRITE, to, n))9494+ return n;9595+ return __clear_user(to, n);9696+}9797+9898+extern long __copy_from_user(void *to, const void __user *from,9999+ unsigned long n);100100+extern long __copy_to_user(void __user *to, const void *from, unsigned long n);101101+102102+static inline long copy_from_user(void *to, const void __user *from,103103+ unsigned long n)104104+{105105+ if (!access_ok(VERIFY_READ, from, n))106106+ return n;107107+ return __copy_from_user(to, from, n);108108+}109109+110110+static inline long copy_to_user(void __user *to, const void *from,111111+ unsigned long n)112112+{113113+ if (!access_ok(VERIFY_WRITE, to, n))114114+ return n;115115+ return __copy_to_user(to, from, n);116116+}117117+118118+extern long strncpy_from_user(char *__to, const char __user *__from,119119+ long __len);120120+extern long strnlen_user(const char __user *s, long n);121121+122122+#define __copy_from_user_inatomic __copy_from_user123123+#define __copy_to_user_inatomic __copy_to_user124124+125125+/* Optimized macros */126126+#define __get_user_asm(val, insn, addr, err) \127127+{ \128128+ __asm__ __volatile__( \129129+ " movi %0, %3\n" \130130+ "1: " insn " %1, 0(%2)\n" \131131+ " movi %0, 0\n" \132132+ "2:\n" \133133+ " .section __ex_table,\"a\"\n" \134134+ " .word 1b, 2b\n" \135135+ " .previous" \136136+ : "=&r" (err), "=r" (val) \137137+ : "r" (addr), "i" (-EFAULT)); \138138+}139139+140140+#define __get_user_unknown(val, size, ptr, err) do { \141141+ err = 0; \142142+ if (copy_from_user(&(val), ptr, size)) { \143143+ err = -EFAULT; \144144+ } \145145+ } while (0)146146+147147+#define __get_user_common(val, size, ptr, err) \148148+do { \149149+ switch (size) { \150150+ case 1: \151151+ __get_user_asm(val, "ldbu", ptr, err); \152152+ break; \153153+ case 2: \154154+ __get_user_asm(val, "ldhu", ptr, err); \155155+ break; \156156+ case 4: \157157+ __get_user_asm(val, "ldw", ptr, err); \158158+ break; \159159+ default: \160160+ __get_user_unknown(val, size, ptr, err); \161161+ break; \162162+ } \163163+} while (0)164164+165165+#define __get_user(x, ptr) \166166+ ({ \167167+ long __gu_err = -EFAULT; \168168+ const __typeof__(*(ptr)) __user *__gu_ptr = (ptr); \169169+ unsigned long __gu_val; \170170+ __get_user_common(__gu_val, sizeof(*(ptr)), __gu_ptr, __gu_err);\171171+ (x) = (__typeof__(x))__gu_val; \172172+ __gu_err; \173173+ })174174+175175+#define get_user(x, ptr) \176176+({ \177177+ long __gu_err = -EFAULT; \178178+ const __typeof__(*(ptr)) __user *__gu_ptr = (ptr); \179179+ unsigned long __gu_val = 0; \180180+ if (access_ok(VERIFY_READ, __gu_ptr, sizeof(*__gu_ptr))) \181181+ __get_user_common(__gu_val, sizeof(*__gu_ptr), \182182+ __gu_ptr, __gu_err); \183183+ (x) = (__typeof__(x))__gu_val; \184184+ __gu_err; \185185+})186186+187187+#define __put_user_asm(val, insn, ptr, err) \188188+{ \189189+ __asm__ __volatile__( \190190+ " movi %0, %3\n" \191191+ "1: " insn " %1, 0(%2)\n" \192192+ " movi %0, 0\n" \193193+ "2:\n" \194194+ " .section __ex_table,\"a\"\n" \195195+ " .word 1b, 2b\n" \196196+ " .previous\n" \197197+ : "=&r" (err) \198198+ : "r" (val), "r" (ptr), "i" (-EFAULT)); \199199+}200200+201201+#define put_user(x, ptr) \202202+({ \203203+ long __pu_err = -EFAULT; \204204+ __typeof__(*(ptr)) __user *__pu_ptr = (ptr); \205205+ __typeof__(*(ptr)) __pu_val = (__typeof(*ptr))(x); \206206+ if (access_ok(VERIFY_WRITE, __pu_ptr, sizeof(*__pu_ptr))) { \207207+ switch (sizeof(*__pu_ptr)) { \208208+ case 1: \209209+ __put_user_asm(__pu_val, "stb", __pu_ptr, __pu_err); \210210+ break; \211211+ case 2: \212212+ __put_user_asm(__pu_val, "sth", __pu_ptr, __pu_err); \213213+ break; \214214+ case 4: \215215+ __put_user_asm(__pu_val, "stw", __pu_ptr, __pu_err); \216216+ break; \217217+ default: \218218+ /* XXX: This looks wrong... */ \219219+ __pu_err = 0; \220220+ if (copy_to_user(__pu_ptr, &(__pu_val), \221221+ sizeof(*__pu_ptr))) \222222+ __pu_err = -EFAULT; \223223+ break; \224224+ } \225225+ } \226226+ __pu_err; \227227+})228228+229229+#define __put_user(x, ptr) put_user(x, ptr)230230+231231+#endif /* _ASM_NIOS2_UACCESS_H */
+142
arch/nios2/mm/init.c
···11+/*22+ * Copyright (C) 2013 Altera Corporation33+ * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>44+ * Copyright (C) 2009 Wind River Systems Inc55+ * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com66+ * Copyright (C) 2004 Microtronix Datacom Ltd77+ *88+ * based on arch/m68k/mm/init.c99+ *1010+ * This file is subject to the terms and conditions of the GNU General Public1111+ * License. See the file "COPYING" in the main directory of this archive1212+ * for more details.1313+ */1414+1515+#include <linux/signal.h>1616+#include <linux/sched.h>1717+#include <linux/kernel.h>1818+#include <linux/errno.h>1919+#include <linux/string.h>2020+#include <linux/types.h>2121+#include <linux/ptrace.h>2222+#include <linux/mman.h>2323+#include <linux/mm.h>2424+#include <linux/init.h>2525+#include <linux/pagemap.h>2626+#include <linux/bootmem.h>2727+#include <linux/slab.h>2828+#include <linux/binfmts.h>2929+3030+#include <asm/setup.h>3131+#include <asm/page.h>3232+#include <asm/pgtable.h>3333+#include <asm/sections.h>3434+#include <asm/tlb.h>3535+#include <asm/mmu_context.h>3636+#include <asm/cpuinfo.h>3737+#include <asm/processor.h>3838+3939+pgd_t *pgd_current;4040+4141+/*4242+ * paging_init() continues the virtual memory environment setup which4343+ * was begun by the code in arch/head.S.4444+ * The parameters are pointers to where to stick the starting and ending4545+ * addresses of available kernel virtual memory.4646+ */4747+void __init paging_init(void)4848+{4949+ unsigned long zones_size[MAX_NR_ZONES];5050+5151+ memset(zones_size, 0, sizeof(zones_size));5252+5353+ pagetable_init();5454+ pgd_current = swapper_pg_dir;5555+5656+ zones_size[ZONE_NORMAL] = max_mapnr;5757+5858+ /* pass the memory from the bootmem allocator to the main allocator */5959+ free_area_init(zones_size);6060+6161+ flush_dcache_range((unsigned long)empty_zero_page,6262+ (unsigned long)empty_zero_page + PAGE_SIZE);6363+}6464+6565+void __init mem_init(void)6666+{6767+ unsigned long end_mem = memory_end; /* this must not include6868+ kernel stack at top */6969+7070+ pr_debug("mem_init: start=%lx, end=%lx\n", memory_start, memory_end);7171+7272+ end_mem &= PAGE_MASK;7373+ high_memory = __va(end_mem);7474+7575+ /* this will put all memory onto the freelists */7676+ free_all_bootmem();7777+ mem_init_print_info(NULL);7878+}7979+8080+void __init mmu_init(void)8181+{8282+ flush_tlb_all();8383+}8484+8585+#ifdef CONFIG_BLK_DEV_INITRD8686+void __init free_initrd_mem(unsigned long start, unsigned long end)8787+{8888+ free_reserved_area((void *)start, (void *)end, -1, "initrd");8989+}9090+#endif9191+9292+void __init_refok free_initmem(void)9393+{9494+ free_initmem_default(-1);9595+}9696+9797+#define __page_aligned(order) __aligned(PAGE_SIZE << (order))9898+pgd_t swapper_pg_dir[PTRS_PER_PGD] __page_aligned(PGD_ORDER);9999+pte_t invalid_pte_table[PTRS_PER_PTE] __page_aligned(PTE_ORDER);100100+static struct page *kuser_page[1];101101+102102+static int alloc_kuser_page(void)103103+{104104+ extern char __kuser_helper_start[], __kuser_helper_end[];105105+ int kuser_sz = __kuser_helper_end - __kuser_helper_start;106106+ unsigned long vpage;107107+108108+ vpage = get_zeroed_page(GFP_ATOMIC);109109+ if (!vpage)110110+ return -ENOMEM;111111+112112+ /* Copy kuser helpers */113113+ memcpy((void *)vpage, __kuser_helper_start, kuser_sz);114114+115115+ flush_icache_range(vpage, vpage + KUSER_SIZE);116116+ kuser_page[0] = virt_to_page(vpage);117117+118118+ return 0;119119+}120120+arch_initcall(alloc_kuser_page);121121+122122+int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)123123+{124124+ struct mm_struct *mm = current->mm;125125+ int ret;126126+127127+ down_write(&mm->mmap_sem);128128+129129+ /* Map kuser helpers to user space address */130130+ ret = install_special_mapping(mm, KUSER_BASE, KUSER_SIZE,131131+ VM_READ | VM_EXEC | VM_MAYREAD |132132+ VM_MAYEXEC, kuser_page);133133+134134+ up_write(&mm->mmap_sem);135135+136136+ return ret;137137+}138138+139139+const char *arch_vma_name(struct vm_area_struct *vma)140140+{141141+ return (vma->vm_start == KUSER_BASE) ? "[kuser]" : NULL;142142+}
+163
arch/nios2/mm/uaccess.c
···11+/*22+ * This file is subject to the terms and conditions of the GNU General Public33+ * License. See the file "COPYING" in the main directory of this archive44+ * for more details.55+ *66+ * Copyright (C) 2009, Wind River Systems Inc77+ * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com88+ */99+1010+#include <linux/export.h>1111+#include <linux/uaccess.h>1212+1313+asm(".global __copy_from_user\n"1414+ " .type __copy_from_user, @function\n"1515+ "__copy_from_user:\n"1616+ " movi r2,7\n"1717+ " mov r3,r4\n"1818+ " bge r2,r6,1f\n"1919+ " xor r2,r4,r5\n"2020+ " andi r2,r2,3\n"2121+ " movi r7,3\n"2222+ " beq r2,zero,4f\n"2323+ "1: addi r6,r6,-1\n"2424+ " movi r2,-1\n"2525+ " beq r6,r2,3f\n"2626+ " mov r7,r2\n"2727+ "2: ldbu r2,0(r5)\n"2828+ " addi r6,r6,-1\n"2929+ " addi r5,r5,1\n"3030+ " stb r2,0(r3)\n"3131+ " addi r3,r3,1\n"3232+ " bne r6,r7,2b\n"3333+ "3:\n"3434+ " addi r2,r6,1\n"3535+ " ret\n"3636+ "13:mov r2,r6\n"3737+ " ret\n"3838+ "4: andi r2,r4,1\n"3939+ " cmpeq r2,r2,zero\n"4040+ " beq r2,zero,7f\n"4141+ "5: andi r2,r3,2\n"4242+ " beq r2,zero,6f\n"4343+ "9: ldhu r2,0(r5)\n"4444+ " addi r6,r6,-2\n"4545+ " addi r5,r5,2\n"4646+ " sth r2,0(r3)\n"4747+ " addi r3,r3,2\n"4848+ "6: bge r7,r6,1b\n"4949+ "10:ldw r2,0(r5)\n"5050+ " addi r6,r6,-4\n"5151+ " addi r5,r5,4\n"5252+ " stw r2,0(r3)\n"5353+ " addi r3,r3,4\n"5454+ " br 6b\n"5555+ "7: ldbu r2,0(r5)\n"5656+ " addi r6,r6,-1\n"5757+ " addi r5,r5,1\n"5858+ " addi r3,r4,1\n"5959+ " stb r2,0(r4)\n"6060+ " br 5b\n"6161+ ".section __ex_table,\"a\"\n"6262+ ".word 2b,3b\n"6363+ ".word 9b,13b\n"6464+ ".word 10b,13b\n"6565+ ".word 7b,13b\n"6666+ ".previous\n"6767+ );6868+EXPORT_SYMBOL(__copy_from_user);6969+7070+asm(7171+ " .global __copy_to_user\n"7272+ " .type __copy_to_user, @function\n"7373+ "__copy_to_user:\n"7474+ " movi r2,7\n"7575+ " mov r3,r4\n"7676+ " bge r2,r6,1f\n"7777+ " xor r2,r4,r5\n"7878+ " andi r2,r2,3\n"7979+ " movi r7,3\n"8080+ " beq r2,zero,4f\n"8181+ /* Bail if we try to copy zero bytes */8282+ "1: addi r6,r6,-1\n"8383+ " movi r2,-1\n"8484+ " beq r6,r2,3f\n"8585+ /* Copy byte by byte for small copies and if src^dst != 0 */8686+ " mov r7,r2\n"8787+ "2: ldbu r2,0(r5)\n"8888+ " addi r5,r5,1\n"8989+ "9: stb r2,0(r3)\n"9090+ " addi r6,r6,-1\n"9191+ " addi r3,r3,1\n"9292+ " bne r6,r7,2b\n"9393+ "3: addi r2,r6,1\n"9494+ " ret\n"9595+ "13:mov r2,r6\n"9696+ " ret\n"9797+ /* If 'to' is an odd address byte copy */9898+ "4: andi r2,r4,1\n"9999+ " cmpeq r2,r2,zero\n"100100+ " beq r2,zero,7f\n"101101+ /* If 'to' is not divideable by four copy halfwords */102102+ "5: andi r2,r3,2\n"103103+ " beq r2,zero,6f\n"104104+ " ldhu r2,0(r5)\n"105105+ " addi r5,r5,2\n"106106+ "10:sth r2,0(r3)\n"107107+ " addi r6,r6,-2\n"108108+ " addi r3,r3,2\n"109109+ /* Copy words */110110+ "6: bge r7,r6,1b\n"111111+ " ldw r2,0(r5)\n"112112+ " addi r5,r5,4\n"113113+ "11:stw r2,0(r3)\n"114114+ " addi r6,r6,-4\n"115115+ " addi r3,r3,4\n"116116+ " br 6b\n"117117+ /* Copy remaining bytes */118118+ "7: ldbu r2,0(r5)\n"119119+ " addi r5,r5,1\n"120120+ " addi r3,r4,1\n"121121+ "12: stb r2,0(r4)\n"122122+ " addi r6,r6,-1\n"123123+ " br 5b\n"124124+ ".section __ex_table,\"a\"\n"125125+ ".word 9b,3b\n"126126+ ".word 10b,13b\n"127127+ ".word 11b,13b\n"128128+ ".word 12b,13b\n"129129+ ".previous\n");130130+EXPORT_SYMBOL(__copy_to_user);131131+132132+long strncpy_from_user(char *__to, const char __user *__from, long __len)133133+{134134+ int l = strnlen_user(__from, __len);135135+ int is_zt = 1;136136+137137+ if (l > __len) {138138+ is_zt = 0;139139+ l = __len;140140+ }141141+142142+ if (l == 0 || copy_from_user(__to, __from, l))143143+ return -EFAULT;144144+145145+ if (is_zt)146146+ l--;147147+ return l;148148+}149149+150150+long strnlen_user(const char __user *s, long n)151151+{152152+ long i;153153+154154+ for (i = 0; i < n; i++) {155155+ char c;156156+157157+ if (get_user(c, s + i) == -EFAULT)158158+ return 0;159159+ if (c == 0)160160+ return i + 1;161161+ }162162+ return n + 1;163163+}