1/* This is a simple version of setjmp and longjmp. 2 3 Copyright (c) 1997 Nick Clifton, Cygnus Solutions 4 */ 5 6#include <picolibc.h> 7 8#include "machine/acle-compat.h" 9 10/* ANSI concatenation macros. */ 11#define CONCAT(a, b) CONCAT2(a, b) 12#define CONCAT2(a, b) a##b 13 14#ifndef __USER_LABEL_PREFIX__ 15#error __USER_LABEL_PREFIX__ not defined 16#endif 17 18#define SYM(x) CONCAT (__USER_LABEL_PREFIX__, x) 19 20#ifdef __ELF__ 21#define TYPE(x) .type SYM(x),function 22#define SIZE(x) .size SYM(x), . - SYM(x) 23#else 24#define TYPE(x) 25#define SIZE(x) 26#endif 27 28/* Jump buffer allocation sizes. */ 29#define JUMPBUF_CORE_REGS_SIZE (10 * 4) 30#define JUMPBUF_FP_REGS_SIZE (8 * 8) 31#define JUMPBUF_PAC (JUMPBUF_CORE_REGS_SIZE + JUMPBUF_FP_REGS_SIZE + 0) 32 33/* Arm/Thumb interworking support: 34 35 The interworking scheme expects functions to use a BX instruction 36 to return control to their parent. Since we need this code to work 37 in both interworked and non-interworked environments as well as with 38 older processors which do not have the BX instruction we do the 39 following: 40 Test the return address. 41 If the bottom bit is clear perform an "old style" function exit. 42 (We know that we are in ARM mode and returning to an ARM mode caller). 43 Otherwise use the BX instruction to perform the function exit. 44 45 We know that we will never attempt to perform the BX instruction on 46 an older processor, because that kind of processor will never be 47 interworked, and a return address with the bottom bit set will never 48 be generated. 49 50 In addition, we do not actually assemble the BX instruction as this would 51 require us to tell the assembler that the processor is an ARM7TDMI and 52 it would store this information in the binary. We want this binary to be 53 able to be linked with binaries compiled for older processors however, so 54 we do not want such information stored there. 55 56 If we are running using the APCS-26 convention however, then we never 57 test the bottom bit, because this is part of the processor status. 58 Instead we just do a normal return, since we know that we cannot be 59 returning to a Thumb caller - the Thumb does not support APCS-26. 60 61 Function entry is much simpler. If we are compiling for the Thumb we 62 just switch into ARM mode and then drop through into the rest of the 63 function. The function exit code will take care of the restore to 64 Thumb mode. 65 66 For Thumb-2 do everything in Thumb mode. */ 67 68 .syntax unified 69 70/* GCC 12.1 and later and clang will tell the assembler exactly which 71 floating point (or MVE) unit is required and we don't want to 72 override that. Conversely, older versions of the compiler don't 73 pass this information so we need to enable the VFP version that is 74 most appropriate. The choice here should support all suitable VFP 75 versions that the older toolchains can handle. */ 76#if __GNUC__ && __GNUC__ < 12 && !defined(__clang__) 77/* Ensure that FPU instructions are correctly compiled and, likewise, 78 the appropriate build attributes are added to the resulting object 79 file. Check whether the MVE extension is present and whether 80 we have support for hardware floating point-operations. VFPxd 81 covers all the cases we need in this file for hardware 82 floating-point and should be compatible with all required FPUs 83 that we need to support. */ 84# if __ARM_FP 85 .fpu vfpxd 86# endif 87# if __ARM_FEATURE_MVE 88 .arch_extension mve 89# endif 90#endif 91 92#if __ARM_ARCH_ISA_THUMB == 1 && !__ARM_ARCH_ISA_ARM 93/* ARMv6-M-like has to be implemented in Thumb mode. */ 94 95.thumb 96.thumb_func 97 .globl SYM (setjmp) 98 TYPE (setjmp) 99SYM (setjmp): 100 /* Save registers in jump buffer. */ 101 stmia r0!, {r4, r5, r6, r7} 102 mov r1, r8 103 mov r2, r9 104 mov r3, r10 105 mov r4, fp 106 mov r5, sp 107 mov r6, lr 108 stmia r0!, {r1, r2, r3, r4, r5, r6} 109 subs r0, r0, #40 110 /* Restore callee-saved low regs. */ 111 ldmia r0!, {r4, r5, r6, r7} 112 /* Return zero. */ 113 movs r0, #0 114 bx lr 115 116.thumb_func 117 .globl SYM (longjmp) 118 TYPE (longjmp) 119SYM (longjmp): 120 /* Restore High regs. */ 121 adds r0, r0, #16 122 ldmia r0!, {r2, r3, r4, r5, r6} 123 mov r8, r2 124 mov r9, r3 125 mov r10, r4 126 mov fp, r5 127 mov sp, r6 128 ldmia r0!, {r3} /* lr */ 129 /* Restore low regs. */ 130 subs r0, r0, #40 131 ldmia r0!, {r4, r5, r6, r7} 132 /* Return the result argument, or 1 if it is zero. */ 133 movs r0, r1 134 bne 1f 135 movs r0, #1 1361: 137 bx r3 138 139#else 140 141#ifdef __APCS_26__ 142#define RET movs pc, lr 143#elif defined(__thumb2__) 144#define RET bx lr 145#else 146#define RET tst lr, #1; \ 147 moveq pc, lr ; \ 148.inst 0xe12fff1e /* bx lr */ 149#endif 150 151#ifdef __thumb2__ 152.macro COND where when 153 i\where \when 154.endm 155#else 156.macro COND where when 157.endm 158#endif 159 160#if defined(__thumb2__) 161.macro MODE 162 .thumb 163 .thumb_func 164.endm 165.macro PROLOGUE name 166.endm 167 168#elif defined(__thumb__) 169#define MODE .thumb_func 170.macro PROLOGUE name 171 .code 16 172 bx pc 173 nop 174 .code 32 175SYM (.arm_start_of.\name): 176.endm 177#else /* Arm */ 178#define MODE .code 32 179.macro PROLOGUE name 180.endm 181#endif 182 183.macro FUNC_START name 184 .text 185 .align 2 186 MODE 187 .globl SYM (\name) 188 .cfi_sections .debug_frame 189 .cfi_startproc 190 TYPE (\name) 191SYM (\name): 192 PROLOGUE \name 193.endm 194 195.macro FUNC_END name 196 RET 197 .cfi_endproc 198 SIZE (\name) 199.endm 200 201/* -------------------------------------------------------------------- 202 int setjmp (jmp_buf); 203 -------------------------------------------------------------------- */ 204 205 FUNC_START setjmp 206 207#if __ARM_FEATURE_PAC_DEFAULT 208# if __ARM_FEATURE_BTI_DEFAULT 209 pacbti ip, lr, sp 210# else 211 pac ip, lr, sp 212# endif /* __ARM_FEATURE_BTI_DEFAULT */ 213 mov r3, ip 214 str r3, [r0, #JUMPBUF_PAC] 215 .cfi_register 143, 12 216#else 217# if __ARM_FEATURE_BTI_DEFAULT 218 bti 219# endif /* __ARM_FEATURE_BTI_DEFAULT */ 220#endif /* __ARM_FEATURE_PAC_DEFAULT */ 221 222 /* Save all the callee-preserved registers into the jump buffer. */ 223#ifdef __thumb2__ 224 mov ip, sp 225 stmia r0!, { r4-r10, fp, ip, lr } 226#else 227 stmia r0!, { r4-r10, fp, sp, lr } 228#endif 229#if defined __ARM_FP || defined __ARM_FEATURE_MVE 230 vstm r0, { d8-d15 } 231#endif 232 233 /* When setting up the jump buffer return 0. */ 234 mov r0, #0 235#if __ARM_FEATURE_PAC_DEFAULT 236 mov ip, r3 237 aut ip, lr, sp 238#endif /* __ARM_FEATURE_PAC_DEFAULT */ 239 240 FUNC_END setjmp 241 242/* -------------------------------------------------------------------- 243 volatile void longjmp (jmp_buf, int); 244 -------------------------------------------------------------------- */ 245 246 FUNC_START longjmp 247 248#if __ARM_FEATURE_BTI_DEFAULT 249 bti 250#endif /* __ARM_FEATURE_BTI_DEFAULT */ 251 252#if __ARM_FEATURE_PAC_DEFAULT 253 /* Keep original jmpbuf address for retrieving pac-code 254 for authentication. */ 255 mov r2, r0 256#endif /* __ARM_FEATURE_PAC_DEFAULT */ 257 258 /* If we have stack extension code it ought to be handled here. */ 259 260 /* Restore the registers, retrieving the state when setjmp() was called. */ 261#ifdef __thumb2__ 262 ldmia r0!, { r4-r10, fp, ip, lr } 263 mov sp, ip 264#else 265 ldmia r0!, { r4-r10, fp, sp, lr } 266#endif 267#if defined __ARM_FP || defined __ARM_FEATURE_MVE 268 vldm r0, { d8-d15 } 269#endif 270 271 /* Put the return value into the integer result register. 272 But if it is zero then return 1 instead. */ 273 movs r0, r1 274 it eq 275 moveq r0, #1 276 277#if __ARM_FEATURE_PAC_DEFAULT 278 ldr ip, [r2, #JUMPBUF_PAC] 279 aut ip, lr, sp 280#endif /* __ARM_FEATURE_PAC_DEFAULT */ 281 282 FUNC_END longjmp 283#endif 284