| /* |
| * Userspace and service handler hooks |
| * |
| * Copyright (c) 2017 Linaro Limited |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| * |
| */ |
| |
| #include <zephyr/toolchain.h> |
| #include <zephyr/linker/sections.h> |
| #include <offsets_short.h> |
| #include <zephyr/syscall.h> |
| |
| #include <zephyr/arch/arm/exception.h> |
| |
| #if defined(CONFIG_CPU_AARCH32_CORTEX_R) |
| #include <zephyr/arch/cpu.h> |
| #endif |
| |
| _ASM_FILE_PROLOGUE |
| |
| GTEXT(z_arm_userspace_enter) |
| GTEXT(z_arm_do_syscall) |
| GTEXT(arch_user_string_nlen) |
| GTEXT(z_arm_user_string_nlen_fault_start) |
| GTEXT(z_arm_user_string_nlen_fault_end) |
| GTEXT(z_arm_user_string_nlen_fixup) |
| GDATA(_kernel) |
| |
| /* Imports */ |
| GDATA(_k_syscall_table) |
| |
| /** |
| * |
| * User space entry function |
| * |
| * This function is the entry point to user mode from privileged execution. |
| * The conversion is one way, and threads which transition to user mode do |
| * not transition back later, unless they are doing system calls. |
| * |
| * The function is invoked as: |
| * z_arm_userspace_enter(user_entry, p1, p2, p3, |
| * stack_info.start, stack_info.size); |
| */ |
| SECTION_FUNC(TEXT,z_arm_userspace_enter) |
| /* move user_entry to lr */ |
| mov lr, r0 |
| |
| /* prepare to set stack to privileged stack */ |
| ldr r0, =_kernel |
| ldr r0, [r0, #_kernel_offset_to_current] |
| #if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) |
| /* move p1 to ip */ |
| mov ip, r1 |
| ldr r1, =_thread_offset_to_priv_stack_start |
| ldr r0, [r0, r1] /* priv stack ptr */ |
| ldr r1, =CONFIG_PRIVILEGED_STACK_SIZE |
| add r0, r0, r1 |
| /* Restore p1 from ip */ |
| mov r1, ip |
| #elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) |
| ldr r0, [r0, #_thread_offset_to_priv_stack_start] /* priv stack ptr */ |
| ldr ip, =CONFIG_PRIVILEGED_STACK_SIZE |
| add r0, r0, ip |
| #elif defined(CONFIG_CPU_AARCH32_CORTEX_R) |
| ldr r0, [r0, #_thread_offset_to_priv_stack_start] /* priv stack ptr */ |
| ldr ip, =CONFIG_PRIVILEGED_STACK_SIZE |
| add r0, r0, ip |
| |
| ldr ip, =_kernel |
| ldr ip, [ip, #_kernel_offset_to_current] |
| str r0, [ip, #_thread_offset_to_priv_stack_end] /* priv stack end */ |
| #endif |
| |
| /* store current stack pointer to ip |
| * the current stack pointer is needed to retrieve |
| * stack_info.start and stack_info.size |
| */ |
| mov ip, sp |
| |
| #if defined(CONFIG_CPU_AARCH32_CORTEX_R) |
| mov sp, r0 |
| #else |
| /* set stack to privileged stack |
| * |
| * Note [applies only when CONFIG_BUILTIN_STACK_GUARD is enabled]: |
| * modifying PSP via MSR instruction is not subject to stack limit |
| * checking, so we do not need to clear PSPLIM before setting PSP. |
| * The operation is safe since, by design, the privileged stack is |
| * located in memory higher than the default (user) thread stack. |
| */ |
| msr PSP, r0 |
| #endif |
| |
| #if defined(CONFIG_BUILTIN_STACK_GUARD) |
| /* At this point the privileged stack is not yet protected by PSPLIM. |
| * Since we have just switched to the top of the privileged stack, we |
| * are safe, as long as the stack can accommodate the maximum exception |
| * stack frame. |
| */ |
| |
| /* set stack pointer limit to the start of the priv stack */ |
| ldr r0, =_kernel |
| ldr r0, [r0, #_kernel_offset_to_current] |
| ldr r0, [r0, #_thread_offset_to_priv_stack_start] /* priv stack ptr */ |
| msr PSPLIM, r0 |
| #endif |
| |
| /* push args to stack */ |
| push {r1,r2,r3,lr} |
| #if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) |
| mov r1, ip |
| push {r0,r1} |
| #elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) \ |
| || defined(CONFIG_CPU_AARCH32_CORTEX_R) |
| push {r0,ip} |
| #endif |
| |
| /* Re-program dynamic memory map. |
| * |
| * Important note: |
| * z_arm_configure_dynamic_mpu_regions() may re-program the MPU Stack Guard |
| * to guard the privilege stack for overflows (if building with option |
| * CONFIG_MPU_STACK_GUARD). There is a risk of actually overflowing the |
| * stack while doing the re-programming. We minimize the risk by placing |
| * this function immediately after we have switched to the privileged stack |
| * so that the whole stack area is available for this critical operation. |
| * |
| * Note that the risk for overflow is higher if using the normal thread |
| * stack, since we do not control how much stack is actually left, when |
| * user invokes z_arm_userspace_enter(). |
| */ |
| ldr r0, =_kernel |
| ldr r0, [r0, #_kernel_offset_to_current] |
| bl z_arm_configure_dynamic_mpu_regions |
| |
| #if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) |
| pop {r0,r3} |
| |
| /* load up stack info from user stack */ |
| ldr r0, [r3] |
| ldr r3, [r3, #4] |
| mov ip, r3 |
| |
| push {r0,r3} |
| #elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) \ |
| || defined(CONFIG_CPU_AARCH32_CORTEX_R) |
| pop {r0,ip} |
| |
| /* load up stack info from user stack */ |
| ldr r0, [ip] |
| ldr ip, [ip, #4] |
| |
| push {r0,ip} |
| #endif |
| |
| /* clear the user stack area to clean out privileged data */ |
| /* from right past the guard right up to the end */ |
| mov r2, ip |
| #ifdef CONFIG_INIT_STACKS |
| ldr r1,=0xaaaaaaaa |
| #else |
| eors r1, r1 |
| #endif |
| bl memset |
| |
| #if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) |
| pop {r0, r1} |
| mov ip, r1 |
| #elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) \ |
| || defined(CONFIG_CPU_AARCH32_CORTEX_R) |
| pop {r0,ip} |
| #endif |
| |
| /* r0 contains user stack start, ip contains user stack size */ |
| add r0, r0, ip /* calculate top of stack */ |
| |
| /* pop remaining arguments from stack before switching stacks */ |
| #if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) |
| /* Use r4 to pop lr, then restore r4 */ |
| mov ip, r4 |
| pop {r1,r2,r3,r4} |
| mov lr, r4 |
| mov r4, ip |
| #elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) \ |
| || defined(CONFIG_CPU_AARCH32_CORTEX_R) |
| pop {r1,r2,r3,lr} |
| #endif |
| |
| #if defined(CONFIG_CPU_AARCH32_CORTEX_R) |
| /* |
| * set stack to user stack. We are in SYSTEM state, so r13 and r14 are |
| * shared with USER state |
| */ |
| mov sp, r0 |
| #else |
| #if defined(CONFIG_BUILTIN_STACK_GUARD) |
| /* |
| * Guard the default (user) stack until thread drops privileges. |
| * |
| * Notes: |
| * PSPLIM is configured *before* PSP switches to the default (user) stack. |
| * This is safe, since the user stack is located, by design, in a lower |
| * memory area compared to the privileged stack. |
| * |
| * However, we need to prevent a context-switch to occur, because that |
| * would re-configure PSPLIM to guard the privileged stack; we enforce |
| * a PendSV locking for this purporse. |
| * |
| * Between PSPLIM update and PSP switch, the privileged stack will be |
| * left un-guarded; this is safe, as long as the privileged stack is |
| * large enough to accommodate a maximum exception stack frame. |
| */ |
| |
| /* Temporarily store current IRQ locking status in ip */ |
| mrs ip, BASEPRI |
| push {r0, ip} |
| |
| /* Lock PendSV while reprogramming PSP and PSPLIM */ |
| mov r0, #_EXC_PENDSV_PRIO_MASK |
| msr BASEPRI_MAX, r0 |
| isb |
| |
| /* Set PSPLIM to guard the thread's user stack. */ |
| ldr r0, =_kernel |
| ldr r0, [r0, #_kernel_offset_to_current] |
| ldr r0, [r0, #_thread_offset_to_stack_info_start] |
| msr PSPLIM, r0 |
| |
| pop {r0, ip} |
| #endif |
| |
| /* set stack to user stack */ |
| msr PSP, r0 |
| #endif |
| |
| #if defined(CONFIG_BUILTIN_STACK_GUARD) |
| /* Restore interrupt lock status */ |
| msr BASEPRI, ip |
| isb |
| #endif |
| |
| /* restore r0 */ |
| mov r0, lr |
| |
| #if defined(CONFIG_CPU_AARCH32_CORTEX_R) |
| /* change processor mode to unprivileged, with all interrupts enabled. */ |
| msr CPSR_c, #MODE_USR |
| #else |
| /* change processor mode to unprivileged */ |
| #if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) |
| push {r0, r1, r2, r3} |
| ldr r0, =_kernel |
| ldr r0, [r0, #_kernel_offset_to_current] |
| ldr r1, =_thread_offset_to_mode |
| ldr r1, [r0, r1] |
| movs r2, #1 |
| orrs r1, r1, r2 |
| mrs r3, CONTROL |
| orrs r3, r3, r2 |
| mov ip, r3 |
| /* Store (unprivileged) mode in thread's mode state variable */ |
| ldr r2, =_thread_offset_to_mode |
| str r1, [r0, r2] |
| #elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) |
| push {r0, r1} |
| ldr r0, =_kernel |
| ldr r0, [r0, #_kernel_offset_to_current] |
| ldr r1, [r0, #_thread_offset_to_mode] |
| orrs r1, r1, #1 |
| mrs ip, CONTROL |
| orrs ip, ip, #1 |
| /* Store (unprivileged) mode in thread's mode state variable */ |
| str r1, [r0, #_thread_offset_to_mode] |
| #endif |
| dsb |
| msr CONTROL, ip |
| #endif |
| |
| /* ISB is not strictly necessary here (stack pointer is not being |
| * touched), but it's recommended to avoid executing pre-fetched |
| * instructions with the previous privilege. |
| */ |
| isb |
| #if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) |
| pop {r0, r1, r2, r3} |
| #elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) |
| pop {r0, r1} |
| #endif |
| |
| /* jump to z_thread_entry entry */ |
| #if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) |
| push {r0, r1} |
| ldr r0, =z_thread_entry |
| mov ip, r0 |
| pop {r0, r1} |
| #elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) \ |
| || defined(CONFIG_CPU_AARCH32_CORTEX_R) |
| ldr ip, =z_thread_entry |
| #endif |
| bx ip |
| |
| /** |
| * |
| * Userspace system call function |
| * |
| * This function is used to do system calls from unprivileged code. This |
| * function is responsible for the following: |
| * 1) Fixing up bad syscalls |
| * 2) Configuring privileged stack and loading up stack arguments |
| * 3) Dispatching the system call |
| * 4) Restoring stack and calling back to the caller of the SVC |
| * |
| */ |
| SECTION_FUNC(TEXT, z_arm_do_syscall) |
| |
| /* Note [when using MPU-based stack guarding]: |
| * The function is executing in privileged mode. This implies that we |
| * shall not be allowed to use the thread's default unprivileged stack, |
| * (i.e push to or pop from it), to avoid a possible stack corruption. |
| * |
| * Rationale: since we execute in PRIV mode and no MPU guard |
| * is guarding the end of the default stack, we won't be able |
| * to detect any stack overflows. |
| * |
| * Note [when using built-in stack limit checking on ARMv8-M]: |
| * At this point PSPLIM is already configured to guard the default (user) |
| * stack, so pushing to the default thread's stack is safe. |
| */ |
| #if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) |
| /* save current stack pointer (user stack) */ |
| mov ip, sp |
| /* temporarily push to user stack */ |
| push {r0,r1} |
| /* setup privileged stack */ |
| ldr r0, =_kernel |
| ldr r0, [r0, #_kernel_offset_to_current] |
| adds r0, r0, #_thread_offset_to_priv_stack_start |
| ldr r0, [r0] /* priv stack ptr */ |
| ldr r1, =CONFIG_PRIVILEGED_STACK_SIZE |
| add r0, r1 |
| |
| /* Store current SP and LR at the beginning of the priv stack */ |
| subs r0, #8 |
| mov r1, ip |
| str r1, [r0, #0] |
| mov r1, lr |
| str r1, [r0, #4] |
| mov ip, r0 |
| /* Restore user stack and original r0, r1 */ |
| pop {r0, r1} |
| |
| #elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) |
| /* setup privileged stack */ |
| ldr ip, =_kernel |
| ldr ip, [ip, #_kernel_offset_to_current] |
| ldr ip, [ip, #_thread_offset_to_priv_stack_start] /* priv stack ptr */ |
| add ip, #CONFIG_PRIVILEGED_STACK_SIZE |
| |
| /* Store current SP and LR at the beginning of the priv stack */ |
| subs ip, #8 |
| str sp, [ip, #0] |
| str lr, [ip, #4] |
| #elif defined(CONFIG_CPU_AARCH32_CORTEX_R) |
| /* |
| * The SVC handler has already switched to the privileged stack. |
| * Store the user SP and LR at the beginning of the priv stack. |
| */ |
| ldr ip, =_kernel |
| ldr ip, [ip, #_kernel_offset_to_current] |
| ldr ip, [ip, #_thread_offset_to_sp_usr] |
| push {ip, lr} |
| #endif |
| |
| #if !defined(CONFIG_CPU_AARCH32_CORTEX_R) |
| /* switch to privileged stack */ |
| msr PSP, ip |
| #endif |
| |
| /* Note (applies when using stack limit checking): |
| * We do not need to lock IRQs after switching PSP to the privileged stack; |
| * PSPLIM is guarding the default (user) stack, which, by design, is |
| * located at *lower* memory area. Since we switch to the top of the |
| * privileged stack we are safe, as long as the stack can accommodate |
| * the maximum exception stack frame. |
| */ |
| |
| #if defined(CONFIG_BUILTIN_STACK_GUARD) |
| /* Set stack pointer limit (needed in privileged mode) */ |
| ldr ip, =_kernel |
| ldr ip, [ip, #_kernel_offset_to_current] |
| ldr ip, [ip, #_thread_offset_to_priv_stack_start] /* priv stack ptr */ |
| msr PSPLIM, ip |
| #endif |
| |
| /* |
| * r0-r5 contain arguments |
| * r6 contains call_id |
| * r8 contains original LR |
| */ |
| #if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) |
| /* save r0, r1 to ip, lr */ |
| mov ip, r0 |
| mov lr, r1 |
| ldr r0, =K_SYSCALL_BAD |
| cmp r6, r0 |
| bne valid_syscall |
| |
| /* BAD SYSCALL path */ |
| /* fixup stack frame on the privileged stack, adding ssf */ |
| mov r1, sp |
| /* ssf is present in r1 (sp) */ |
| push {r1,lr} |
| push {r4,r5} |
| /* restore r0, r1 */ |
| mov r0, ip |
| mov r1, lr |
| b dispatch_syscall |
| valid_syscall: |
| /* push ssf to privileged stack */ |
| mov r1, sp |
| push {r1} |
| /* push args to complete stack frame */ |
| push {r4,r5} |
| |
| dispatch_syscall: |
| /* original r0 is saved in ip */ |
| ldr r0, =_k_syscall_table |
| lsls r6, #2 |
| add r0, r6 |
| ldr r0, [r0] /* load table address */ |
| /* swap ip and r0, restore r1 from lr */ |
| mov r1, ip |
| mov ip, r0 |
| mov r0, r1 |
| mov r1, lr |
| /* execute function from dispatch table */ |
| blx ip |
| |
| /* restore LR |
| * r0 holds the return value and needs to be preserved |
| */ |
| mov ip, r0 |
| mov r0, sp |
| ldr r0, [r0,#16] |
| mov lr, r0 |
| /* Restore r0 */ |
| mov r0, ip |
| |
| #elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) \ |
| || defined(CONFIG_CPU_AARCH32_CORTEX_R) |
| ldr ip, =K_SYSCALL_BAD |
| cmp r6, ip |
| bne valid_syscall |
| |
| /* BAD SYSCALL path */ |
| /* fixup stack frame on the privileged stack, adding ssf */ |
| mov ip, sp |
| push {r4,r5,ip,lr} |
| b dispatch_syscall |
| |
| valid_syscall: |
| /* push args to complete stack frame */ |
| mov ip, sp |
| push {r4,r5,ip} |
| |
| dispatch_syscall: |
| ldr ip, =_k_syscall_table |
| lsl r6, #2 |
| add ip, r6 |
| ldr ip, [ip] /* load table address */ |
| |
| /* execute function from dispatch table */ |
| blx ip |
| |
| /* restore LR */ |
| ldr lr, [sp,#16] |
| #endif |
| |
| |
| #if defined(CONFIG_BUILTIN_STACK_GUARD) |
| /* |
| * Guard the default (user) stack until thread drops privileges. |
| * |
| * Notes: |
| * PSPLIM is configured *before* PSP switches to the default (user) stack. |
| * This is safe, since the user stack is located, by design, in a lower |
| * memory area compared to the privileged stack. |
| * |
| * However, we need to prevent a context-switch to occur, because that |
| * would re-configure PSPLIM to guard the privileged stack; we enforce |
| * a PendSV locking for this purporse. |
| * |
| * Between PSPLIM update and PSP switch, the privileged stack will be |
| * left un-guarded; this is safe, as long as the privileged stack is |
| * large enough to accommodate a maximum exception stack frame. |
| */ |
| |
| /* Temporarily store current IRQ locking status in r2 */ |
| mrs r2, BASEPRI |
| |
| /* Lock PendSV while reprogramming PSP and PSPLIM */ |
| mov r3, #_EXC_PENDSV_PRIO_MASK |
| msr BASEPRI_MAX, r3 |
| isb |
| |
| /* Set PSPLIM to guard the thread's user stack. */ |
| ldr r3, =_kernel |
| ldr r3, [r3, #_kernel_offset_to_current] |
| ldr r3, [r3, #_thread_offset_to_stack_info_start] /* stack_info.start */ |
| msr PSPLIM, r3 |
| #endif |
| |
| #if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) |
| /* set stack back to unprivileged stack */ |
| mov ip, r0 |
| mov r0, sp |
| ldr r0, [r0,#12] |
| msr PSP, r0 |
| /* Restore r0 */ |
| mov r0, ip |
| |
| #elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) |
| /* set stack back to unprivileged stack */ |
| ldr ip, [sp,#12] |
| msr PSP, ip |
| #endif |
| |
| #if defined(CONFIG_BUILTIN_STACK_GUARD) |
| /* Restore interrupt lock status */ |
| msr BASEPRI, r2 |
| isb |
| #endif |
| |
| push {r0, r1} |
| #if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) |
| push {r2, r3} |
| ldr r0, =_kernel |
| ldr r0, [r0, #_kernel_offset_to_current] |
| ldr r2, =_thread_offset_to_mode |
| ldr r1, [r0, r2] |
| movs r3, #1 |
| orrs r1, r1, r3 |
| /* Store (unprivileged) mode in thread's mode state variable */ |
| str r1, [r0, r2] |
| dsb |
| /* drop privileges by setting bit 0 in CONTROL */ |
| mrs r2, CONTROL |
| orrs r2, r2, r3 |
| msr CONTROL, r2 |
| pop {r2, r3} |
| #elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) \ |
| || defined(CONFIG_CPU_AARCH32_CORTEX_R) |
| ldr r0, =_kernel |
| ldr r0, [r0, #_kernel_offset_to_current] |
| ldr r1, [r0, #_thread_offset_to_mode] |
| orrs r1, r1, #1 |
| /* Store (unprivileged) mode in thread's mode state variable */ |
| str r1, [r0, #_thread_offset_to_mode] |
| dsb |
| #if defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) |
| /* drop privileges by setting bit 0 in CONTROL */ |
| mrs ip, CONTROL |
| orrs ip, ip, #1 |
| msr CONTROL, ip |
| #endif |
| #endif |
| |
| /* ISB is not strictly necessary here (stack pointer is not being |
| * touched), but it's recommended to avoid executing pre-fetched |
| * instructions with the previous privilege. |
| */ |
| isb |
| pop {r0, r1} |
| |
| #if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) |
| /* Zero out volatile (caller-saved) registers so as to not leak state from |
| * kernel mode. The C calling convention for the syscall handler will |
| * restore the others to original values. |
| */ |
| movs r2, #0 |
| movs r3, #0 |
| |
| /* |
| * return back to original function that called SVC, add 1 to force thumb |
| * mode |
| */ |
| |
| /* Save return value temporarily to ip */ |
| mov ip, r0 |
| |
| mov r0, r8 |
| movs r1, #1 |
| orrs r0, r0, r1 |
| |
| /* swap ip, r0 */ |
| mov r1, ip |
| mov ip, r0 |
| mov r0, r1 |
| movs r1, #0 |
| #elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) |
| |
| /* Zero out volatile (caller-saved) registers so as to not leak state from |
| * kernel mode. The C calling convention for the syscall handler will |
| * restore the others to original values. |
| */ |
| mov r1, #0 |
| mov r2, #0 |
| mov r3, #0 |
| |
| /* |
| * return back to original function that called SVC, add 1 to force thumb |
| * mode |
| */ |
| mov ip, r8 |
| orrs ip, ip, #1 |
| #elif defined(CONFIG_CPU_AARCH32_CORTEX_R) |
| /* Restore user stack pointer */ |
| ldr ip, [sp,#12] |
| mov sp, ip |
| |
| /* Zero out volatile (caller-saved) registers so as to not leak state from |
| * kernel mode. The C calling convention for the syscall handler will |
| * restore the others to original values. |
| */ |
| mov r1, #0 |
| mov r2, #0 |
| mov r3, #0 |
| |
| /* |
| * return back to original function that called SVC |
| */ |
| mov ip, r8 |
| cps #MODE_USR |
| #endif |
| |
| bx ip |
| |
| |
| /* |
| * size_t arch_user_string_nlen(const char *s, size_t maxsize, int *err_arg) |
| */ |
| SECTION_FUNC(TEXT, arch_user_string_nlen) |
| push {r0, r1, r2, r4, r5, lr} |
| |
| /* sp+4 is error value, init to -1 */ |
| #if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) \ |
| || defined(CONFIG_CPU_AARCH32_CORTEX_R) |
| ldr r3, =-1 |
| #elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) |
| mov.w r3, #-1 |
| #endif |
| str r3, [sp, #4] |
| |
| /* Perform string length calculation */ |
| movs r3, #0 /* r3 is the counter */ |
| |
| strlen_loop: |
| z_arm_user_string_nlen_fault_start: |
| /* r0 contains the string. r5 = *(r0 + r3]). This could fault. */ |
| ldrb r5, [r0, r3] |
| |
| z_arm_user_string_nlen_fault_end: |
| #if defined(CONFIG_CPU_AARCH32_CORTEX_R) |
| cmp r5, #0 |
| beq strlen_done |
| |
| cmp r3, r1 |
| beq strlen_done |
| |
| adds r3, #1 |
| b strlen_loop |
| #else |
| #if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) |
| cmp r5, #0 |
| beq strlen_done |
| #elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) |
| cbz r5, strlen_done |
| #endif |
| cmp r3, r1 |
| beq.n strlen_done |
| |
| adds r3, #1 |
| b.n strlen_loop |
| #endif |
| |
| strlen_done: |
| /* Move length calculation from r3 to r0 (return value register) */ |
| mov r0, r3 |
| |
| /* Clear error value since we succeeded */ |
| movs r1, #0 |
| str r1, [sp, #4] |
| |
| z_arm_user_string_nlen_fixup: |
| /* Write error value to err pointer parameter */ |
| ldr r1, [sp, #4] |
| str r1, [r2, #0] |
| |
| add sp, #12 |
| pop {r4, r5, pc} |