1 /* SPDX-License-Identifier: LGPL-2.1 OR MIT */ 2 /* 3 * parisc/hppa (32-bit) specific definitions for NOLIBC 4 * Copyright (C) 2026 Thomas Weißschuh <linux@weissschuh.net> 5 */ 6 7 #ifndef _NOLIBC_ARCH_PARISC_H 8 #define _NOLIBC_ARCH_PARISC_H 9 10 #include <linux/unistd.h> 11 12 #include "compiler.h" 13 #include "crt.h" 14 15 /* Syscalls for parisc : 16 * - syscall number is passed in r20 17 * - arguments are in r26 to r21 18 * - the system call is performed by calling "ble 0x100(%sr2, %r0)", 19 * the instruction after that is in the delay slot and executed before 20 * the jump to 0x100 actually happens, use it to load the syscall number 21 * - syscall return comes in r28 22 * - the arguments are cast to long and assigned into the target 23 * registers which are then simply passed as registers to the asm code, 24 * so that we don't have to experience issues with register constraints. 25 */ 26 27 #define _NOLIBC_SYSCALL_CLOBBERLIST \ 28 "memory", "%r1", "%r2", "%r4", "%r20", "%r29", "%r31" 29 30 #define __nolibc_syscall0(num) \ 31 ({ \ 32 register long _ret __asm__ ("r28"); \ 33 \ 34 __asm__ volatile ( \ 35 "ble 0x100(%%sr2, %%r0)\n\t" \ 36 "copy %1, %%r20\n\t" \ 37 : "=r"(_ret) \ 38 : "r"(num) \ 39 : _NOLIBC_SYSCALL_CLOBBERLIST, \ 40 "%r21", "%r22", "%r23", "%r24", "%r25", "%r26" \ 41 ); \ 42 _ret; \ 43 }) 44 45 #define __nolibc_syscall1(num, arg1) \ 46 ({ \ 47 register long _ret __asm__ ("r28"); \ 48 register long _arg1 __asm__ ("r26") = (long)(arg1); \ 49 \ 50 __asm__ volatile ( \ 51 "ble 0x100(%%sr2, %%r0)\n\t" \ 52 "copy %2, %%r20\n\t" \ 53 : "=r"(_ret), \ 54 "+r"(_arg1) \ 55 : "r"(num) \ 56 : _NOLIBC_SYSCALL_CLOBBERLIST, \ 57 "%r21", "%r22", "%r23", "%r24", "%r25" \ 58 ); \ 59 _ret; \ 60 }) 61 62 #define __nolibc_syscall2(num, arg1, arg2) \ 63 ({ \ 64 register long _ret __asm__ ("r28"); \ 65 register long _arg1 __asm__ ("r26") = (long)(arg1); \ 66 register long _arg2 __asm__ ("r25") = (long)(arg2); \ 67 \ 68 __asm__ volatile ( \ 69 "ble 0x100(%%sr2, %%r0)\n\t" \ 70 "copy %3, %%r20\n\t" \ 71 : "=r"(_ret), \ 72 "+r"(_arg1), "+r"(_arg2) \ 73 : "r"(num) \ 74 : _NOLIBC_SYSCALL_CLOBBERLIST, \ 75 "%r21", "%r22", "%r23", "%r24" \ 76 ); \ 77 _ret; \ 78 }) 79 80 #define __nolibc_syscall3(num, arg1, arg2, arg3) \ 81 ({ \ 82 register long _ret __asm__ ("r28"); \ 83 register long _arg1 __asm__ ("r26") = (long)(arg1); \ 84 register long _arg2 __asm__ ("r25") = (long)(arg2); \ 85 register long _arg3 __asm__ ("r24") = (long)(arg3); \ 86 \ 87 __asm__ volatile ( \ 88 "ble 0x100(%%sr2, %%r0)\n\t" \ 89 "copy %4, %%r20\n\t" \ 90 : "=r"(_ret), \ 91 "+r"(_arg1), "+r"(_arg2), "+r"(_arg3) \ 92 : "r"(num) \ 93 : _NOLIBC_SYSCALL_CLOBBERLIST, \ 94 "%r21", "%r22", "%r23" \ 95 ); \ 96 _ret; \ 97 }) 98 99 #define __nolibc_syscall4(num, arg1, arg2, arg3, arg4) \ 100 ({ \ 101 register long _ret __asm__ ("r28"); \ 102 register long _arg1 __asm__ ("r26") = (long)(arg1); \ 103 register long _arg2 __asm__ ("r25") = (long)(arg2); \ 104 register long _arg3 __asm__ ("r24") = (long)(arg3); \ 105 register long _arg4 __asm__ ("r23") = (long)(arg4); \ 106 \ 107 __asm__ volatile ( \ 108 "ble 0x100(%%sr2, %%r0)\n\t" \ 109 "copy %5, %%r20\n\t" \ 110 : "=r"(_ret), \ 111 "+r"(_arg1), "+r"(_arg2), "+r"(_arg3), "+r"(_arg4) \ 112 : "r"(num) \ 113 : _NOLIBC_SYSCALL_CLOBBERLIST, \ 114 "%r21", "%r22" \ 115 ); \ 116 _ret; \ 117 }) 118 119 #define __nolibc_syscall5(num, arg1, arg2, arg3, arg4, arg5) \ 120 ({ \ 121 register long _ret __asm__ ("r28"); \ 122 register long _arg1 __asm__ ("r26") = (long)(arg1); \ 123 register long _arg2 __asm__ ("r25") = (long)(arg2); \ 124 register long _arg3 __asm__ ("r24") = (long)(arg3); \ 125 register long _arg4 __asm__ ("r23") = (long)(arg4); \ 126 register long _arg5 __asm__ ("r22") = (long)(arg5); \ 127 \ 128 __asm__ volatile ( \ 129 "ble 0x100(%%sr2, %%r0)\n\t" \ 130 "copy %6, %%r20\n\t" \ 131 : "=r"(_ret), \ 132 "+r"(_arg1), "+r"(_arg2), "+r"(_arg3), "+r"(_arg4), \ 133 "+r"(_arg5) \ 134 : "r"(num) \ 135 : _NOLIBC_SYSCALL_CLOBBERLIST, \ 136 "%r21" \ 137 ); \ 138 _ret; \ 139 }) 140 141 #define __nolibc_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6) \ 142 ({ \ 143 register long _ret __asm__ ("r28"); \ 144 register long _arg1 __asm__ ("r26") = (long)(arg1); \ 145 register long _arg2 __asm__ ("r25") = (long)(arg2); \ 146 register long _arg3 __asm__ ("r24") = (long)(arg3); \ 147 register long _arg4 __asm__ ("r23") = (long)(arg4); \ 148 register long _arg5 __asm__ ("r22") = (long)(arg5); \ 149 register long _arg6 __asm__ ("r21") = (long)(arg6); \ 150 \ 151 __asm__ volatile ( \ 152 "ble 0x100(%%sr2, %%r0)\n\t" \ 153 "copy %7, %%r20\n\t" \ 154 : "=r"(_ret), \ 155 "+r"(_arg1), "+r"(_arg2), "+r"(_arg3), "+r"(_arg4), \ 156 "+r"(_arg5), "+r"(_arg6) \ 157 : "r"(num) \ 158 : _NOLIBC_SYSCALL_CLOBBERLIST \ 159 ); \ 160 _ret; \ 161 }) 162 163 #ifndef NOLIBC_NO_RUNTIME 164 /* startup code */ 165 void __attribute__((weak, noreturn)) __nolibc_entrypoint __nolibc_no_stack_protector _start(void) 166 { 167 __asm__ volatile ( 168 #ifdef __LP64__ 169 ".import __gp\n" /* Set up the dp register */ 170 "ldil L%__gp, %dp\n" 171 "ldo R%__gp(%dp), %dp\n" 172 #else 173 ".import $global$\n" /* Set up the dp register */ 174 "ldil L%$global$, %dp\n" 175 "ldo R%$global$(%dp), %dp\n" 176 #endif 177 178 "b _start_c\n" /* Call _start_c, the load below is executed first */ 179 180 "ldo -4(%r24), %r26\n" /* The sp register is special on parisc. 181 * r24 points to argv. Subtract 4 to get &argc. 182 * Pass that as first argument to _start_c. 183 */ 184 ); 185 __nolibc_entrypoint_epilogue(); 186 } 187 #endif /* NOLIBC_NO_RUNTIME */ 188 189 #ifdef __LP64__ 190 /* 191 * This override is normally only meant for 32-bit architectures. 192 * But parisc 64-bit has __NR_ftruncate64, so this path is taken. 193 * Add an unused second argument to satisfy the calling convention. 194 */ 195 static __attribute__((unused)) 196 int _sys_ftruncate64(int fd, uint64_t length, uint64_t unused) 197 { 198 (void)unused; 199 200 return __nolibc_syscall2(__NR_ftruncate64, fd, length); 201 } 202 #define _sys_ftruncate64 _sys_ftruncate64 203 #endif 204 205 #endif /* _NOLIBC_ARCH_PARISC_H */ 206