Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[ARM] 3105/4: ARM EABI: new syscall entry convention

Patch from Nicolas Pitre

For a while we wanted to change the way syscalls were called on ARM.
Instead of encoding the syscall number in the swi instruction which
requires reading back the instruction from memory to extract that number
and polluting the data cache, it was decided that simply storing the
syscall number into r7 would be more efficient. Since this represents
an ABI change then making that change at the same time as EABI support
is the right thing to do.

It is now expected that EABI user space binaries put the syscall number
into r7 and use "swi 0" to call the kernel. Syscall register argument
are also expected to have "EABI arrangement" i.e. 64-bit arguments
should be put in a pair of registers from an even register number.

Example with long ftruncate64(unsigned int fd, loff_t length):

legacy ABI:
- put fd into r0
- put length into r1-r2
- use "swi #(0x900000 + 194)" to call the kernel

new ARM EABI:
- put fd into r0
- put length into r2-r3 (skipping over r1)
- put 194 into r7
- use "swi 0" to call the kernel

Note that it is important to use 0 for the swi argument as backward
compatibility with legacy ABI user space relies on this.
The syscall macros in asm-arm/unistd.h were also updated to support
both ABIs and implement the right call method automatically.

Signed-off-by: Nicolas Pitre <nico@cam.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

authored by

Nicolas Pitre and committed by
Russell King
3f2829a3 ba95e4e4

+48 -37
+21 -14
arch/arm/kernel/entry-common.S
··· 98 98 run on an ARM7 and we can save a couple of instructions. 99 99 --pb */ 100 100 #ifdef CONFIG_CPU_ARM710 101 - .macro arm710_bug_check, instr, temp 102 - and \temp, \instr, #0x0f000000 @ check for SWI 103 - teq \temp, #0x0f000000 104 - bne .Larm700bug 105 - .endm 106 - 107 - .Larm700bug: 101 + #define A710(code...) code 102 + .Larm710bug: 108 103 ldmia sp, {r0 - lr}^ @ Get calling r0 - lr 109 104 mov r0, r0 110 105 add sp, sp, #S_FRAME_SIZE 111 106 subs pc, lr, #4 112 107 #else 113 - .macro arm710_bug_check, instr, temp 114 - .endm 108 + #define A710(code...) 115 109 #endif 116 110 117 111 .align 5 ··· 123 129 /* 124 130 * Get the system call number. 125 131 */ 126 - #ifdef CONFIG_ARM_THUMB 132 + #if defined(CONFIG_AEABI) 133 + 134 + @ syscall number is in scno (r7) already. 135 + 136 + A710( ldr ip, [lr, #-4] @ get SWI instruction ) 137 + A710( and ip, ip, #0x0f000000 @ check for SWI ) 138 + A710( teq ip, #0x0f000000 ) 139 + A710( bne .Larm710bug ) 140 + #elif defined(CONFIG_ARM_THUMB) 127 141 tst r8, #PSR_T_BIT @ this is SPSR from save_user_regs 128 142 addne scno, r7, #__NR_SYSCALL_BASE @ put OS number in 129 143 ldreq scno, [lr, #-4] 130 144 #else 131 145 ldr scno, [lr, #-4] @ get SWI instruction 146 + A710( and ip, scno, #0x0f000000 @ check for SWI ) 147 + A710( teq ip, #0x0f000000 ) 148 + A710( bne .Larm710bug ) 132 149 #endif 133 - arm710_bug_check scno, ip 134 150 135 151 #ifdef CONFIG_ALIGNMENT_TRAP 136 152 ldr ip, __cr_alignment ··· 149 145 #endif 150 146 enable_irq 151 147 152 - stmdb sp!, {r4, r5} @ push fifth and sixth args 153 - 154 148 get_thread_info tsk 155 149 ldr ip, [tsk, #TI_FLAGS] @ check for syscall tracing 150 + #ifndef CONFIG_AEABI 156 151 bic scno, scno, #0xff000000 @ mask off SWI op-code 157 152 eor scno, scno, #__NR_SYSCALL_BASE @ check OS number 153 + #endif 158 154 adr tbl, sys_call_table @ load syscall table pointer 155 + stmdb sp!, {r4, r5} @ push fifth and sixth args 159 156 tst ip, #_TIF_SYSCALL_TRACE @ are we tracing syscalls? 160 157 bne __sys_trace 161 158 162 - adr lr, ret_fast_syscall @ return address 163 159 cmp scno, #NR_syscalls @ check upper syscall limit 160 + adr lr, ret_fast_syscall @ return address 164 161 ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine 165 162 166 163 add r1, sp, #S_OFF ··· 212 207 @ r8 = syscall table 213 208 .type sys_syscall, #function 214 209 sys_syscall: 210 + #ifndef CONFIG_AEABI 215 211 eor scno, r0, #__NR_SYSCALL_BASE 216 212 cmp scno, #__NR_syscall - __NR_SYSCALL_BASE 217 213 cmpne scno, #NR_syscalls @ check range ··· 222 216 movlo r2, r3 223 217 movlo r3, r4 224 218 ldrlo pc, [tbl, scno, lsl #2] 219 + #endif 225 220 b sys_ni_syscall 226 221 227 222 sys_fork_wrapper:
+1 -1
arch/arm/kernel/traps.c
··· 404 404 struct thread_info *thread = current_thread_info(); 405 405 siginfo_t info; 406 406 407 - if ((no >> 16) != 0x9f) 407 + if ((no >> 16) != (__ARM_NR_BASE>> 16)) 408 408 return bad_syscall(no, regs); 409 409 410 410 switch (no & 0xffff) {
+26 -22
include/asm-arm/unistd.h
··· 15 15 16 16 #include <linux/linkage.h> 17 17 18 - #if defined(__thumb__) 18 + #define __NR_OABI_SYSCALL_BASE 0x900000 19 + 20 + #if defined(__thumb__) || defined(__ARM_EABI__) 19 21 #define __NR_SYSCALL_BASE 0 20 22 #else 21 - #define __NR_SYSCALL_BASE 0x900000 23 + #define __NR_SYSCALL_BASE __NR_OABI_SYSCALL_BASE 22 24 #endif 23 25 24 26 /* ··· 375 373 #define __sys1(x) __sys2(x) 376 374 377 375 #ifndef __syscall 378 - #if defined(__thumb__) 379 - #define __syscall(name) \ 380 - "push {r7}\n\t" \ 381 - "mov r7, #" __sys1(__NR_##name) "\n\t" \ 382 - "swi 0\n\t" \ 383 - "pop {r7}" 376 + #if defined(__thumb__) || defined(__ARM_EABI__) 377 + #define __SYS_REG(name) register long __sysreg __asm__("r7") = __NR_##name; 378 + #define __SYS_REG_LIST(regs...) "r" (__sysreg) , ##regs 379 + #define __syscall(name) "swi\t0" 384 380 #else 381 + #define __SYS_REG(name) 382 + #define __SYS_REG_LIST(regs...) regs 385 383 #define __syscall(name) "swi\t" __sys1(__NR_##name) "" 386 384 #endif 387 385 #endif ··· 397 395 398 396 #define _syscall0(type,name) \ 399 397 type name(void) { \ 398 + __SYS_REG(name) \ 400 399 register long __res_r0 __asm__("r0"); \ 401 400 long __res; \ 402 401 __asm__ __volatile__ ( \ 403 402 __syscall(name) \ 404 403 : "=r" (__res_r0) \ 405 - : \ 406 - : "lr"); \ 404 + : __SYS_REG_LIST() ); \ 407 405 __res = __res_r0; \ 408 406 __syscall_return(type,__res); \ 409 407 } 410 408 411 409 #define _syscall1(type,name,type1,arg1) \ 412 410 type name(type1 arg1) { \ 411 + __SYS_REG(name) \ 413 412 register long __r0 __asm__("r0") = (long)arg1; \ 414 413 register long __res_r0 __asm__("r0"); \ 415 414 long __res; \ 416 415 __asm__ __volatile__ ( \ 417 416 __syscall(name) \ 418 417 : "=r" (__res_r0) \ 419 - : "r" (__r0) \ 420 - : "lr"); \ 418 + : __SYS_REG_LIST( "0" (__r0) ) ); \ 421 419 __res = __res_r0; \ 422 420 __syscall_return(type,__res); \ 423 421 } 424 422 425 423 #define _syscall2(type,name,type1,arg1,type2,arg2) \ 426 424 type name(type1 arg1,type2 arg2) { \ 425 + __SYS_REG(name) \ 427 426 register long __r0 __asm__("r0") = (long)arg1; \ 428 427 register long __r1 __asm__("r1") = (long)arg2; \ 429 428 register long __res_r0 __asm__("r0"); \ ··· 432 429 __asm__ __volatile__ ( \ 433 430 __syscall(name) \ 434 431 : "=r" (__res_r0) \ 435 - : "r" (__r0),"r" (__r1) \ 436 - : "lr"); \ 432 + : __SYS_REG_LIST( "0" (__r0), "r" (__r1) ) ); \ 437 433 __res = __res_r0; \ 438 434 __syscall_return(type,__res); \ 439 435 } ··· 440 438 441 439 #define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \ 442 440 type name(type1 arg1,type2 arg2,type3 arg3) { \ 441 + __SYS_REG(name) \ 443 442 register long __r0 __asm__("r0") = (long)arg1; \ 444 443 register long __r1 __asm__("r1") = (long)arg2; \ 445 444 register long __r2 __asm__("r2") = (long)arg3; \ ··· 449 446 __asm__ __volatile__ ( \ 450 447 __syscall(name) \ 451 448 : "=r" (__res_r0) \ 452 - : "r" (__r0),"r" (__r1),"r" (__r2) \ 453 - : "lr"); \ 449 + : __SYS_REG_LIST( "0" (__r0), "r" (__r1), "r" (__r2) ) ); \ 454 450 __res = __res_r0; \ 455 451 __syscall_return(type,__res); \ 456 452 } ··· 457 455 458 456 #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4)\ 459 457 type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ 458 + __SYS_REG(name) \ 460 459 register long __r0 __asm__("r0") = (long)arg1; \ 461 460 register long __r1 __asm__("r1") = (long)arg2; \ 462 461 register long __r2 __asm__("r2") = (long)arg3; \ ··· 467 464 __asm__ __volatile__ ( \ 468 465 __syscall(name) \ 469 466 : "=r" (__res_r0) \ 470 - : "r" (__r0),"r" (__r1),"r" (__r2),"r" (__r3) \ 471 - : "lr"); \ 467 + : __SYS_REG_LIST( "0" (__r0), "r" (__r1), "r" (__r2), "r" (__r3) ) ); \ 472 468 __res = __res_r0; \ 473 469 __syscall_return(type,__res); \ 474 470 } ··· 475 473 476 474 #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,type5,arg5) \ 477 475 type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) { \ 476 + __SYS_REG(name) \ 478 477 register long __r0 __asm__("r0") = (long)arg1; \ 479 478 register long __r1 __asm__("r1") = (long)arg2; \ 480 479 register long __r2 __asm__("r2") = (long)arg3; \ ··· 486 483 __asm__ __volatile__ ( \ 487 484 __syscall(name) \ 488 485 : "=r" (__res_r0) \ 489 - : "r" (__r0),"r" (__r1),"r" (__r2),"r" (__r3),"r" (__r4) \ 490 - : "lr"); \ 486 + : __SYS_REG_LIST( "0" (__r0), "r" (__r1), "r" (__r2), \ 487 + "r" (__r3), "r" (__r4) ) ); \ 491 488 __res = __res_r0; \ 492 489 __syscall_return(type,__res); \ 493 490 } 494 491 495 492 #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,type5,arg5,type6,arg6) \ 496 493 type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6) { \ 494 + __SYS_REG(name) \ 497 495 register long __r0 __asm__("r0") = (long)arg1; \ 498 496 register long __r1 __asm__("r1") = (long)arg2; \ 499 497 register long __r2 __asm__("r2") = (long)arg3; \ ··· 506 502 __asm__ __volatile__ ( \ 507 503 __syscall(name) \ 508 504 : "=r" (__res_r0) \ 509 - : "r" (__r0),"r" (__r1),"r" (__r2),"r" (__r3), "r" (__r4),"r" (__r5) \ 510 - : "lr"); \ 505 + : __SYS_REG_LIST( "0" (__r0), "r" (__r1), "r" (__r2), \ 506 + "r" (__r3), "r" (__r4), "r" (__r5) ) ); \ 511 507 __res = __res_r0; \ 512 508 __syscall_return(type,__res); \ 513 509 }