diff options
Diffstat (limited to 'arch/xtensa/kernel/entry.S')
-rw-r--r-- | arch/xtensa/kernel/entry.S | 228 |
1 files changed, 150 insertions, 78 deletions
diff --git a/arch/xtensa/kernel/entry.S b/arch/xtensa/kernel/entry.S index c0b56b17927f..9e271ba009bf 100644 --- a/arch/xtensa/kernel/entry.S +++ b/arch/xtensa/kernel/entry.S | |||
@@ -1004,13 +1004,10 @@ ENTRY(fast_syscall_kernel) | |||
1004 | 1004 | ||
1005 | rsr a0, DEPC # get syscall-nr | 1005 | rsr a0, DEPC # get syscall-nr |
1006 | _beqz a0, fast_syscall_spill_registers | 1006 | _beqz a0, fast_syscall_spill_registers |
1007 | 1007 | _beqi a0, __NR_xtensa, fast_syscall_xtensa | |
1008 | addi a0, a0, -__NR_sysxtensa | ||
1009 | _beqz a0, fast_syscall_sysxtensa | ||
1010 | 1008 | ||
1011 | j kernel_exception | 1009 | j kernel_exception |
1012 | 1010 | ||
1013 | |||
1014 | ENTRY(fast_syscall_user) | 1011 | ENTRY(fast_syscall_user) |
1015 | 1012 | ||
1016 | /* Skip syscall. */ | 1013 | /* Skip syscall. */ |
@@ -1024,9 +1021,7 @@ ENTRY(fast_syscall_user) | |||
1024 | 1021 | ||
1025 | rsr a0, DEPC # get syscall-nr | 1022 | rsr a0, DEPC # get syscall-nr |
1026 | _beqz a0, fast_syscall_spill_registers | 1023 | _beqz a0, fast_syscall_spill_registers |
1027 | 1024 | _beqi a0, __NR_xtensa, fast_syscall_xtensa | |
1028 | addi a0, a0, -__NR_sysxtensa | ||
1029 | _beqz a0, fast_syscall_sysxtensa | ||
1030 | 1025 | ||
1031 | j user_exception | 1026 | j user_exception |
1032 | 1027 | ||
@@ -1047,18 +1042,19 @@ ENTRY(fast_syscall_unrecoverable) | |||
1047 | /* | 1042 | /* |
1048 | * sysxtensa syscall handler | 1043 | * sysxtensa syscall handler |
1049 | * | 1044 | * |
1050 | * int sysxtensa (XTENSA_ATOMIC_SET, ptr, val, unused); | 1045 | * int sysxtensa (SYS_XTENSA_ATOMIC_SET, ptr, val, unused); |
1051 | * int sysxtensa (XTENSA_ATOMIC_ADD, ptr, val, unused); | 1046 | * int sysxtensa (SYS_XTENSA_ATOMIC_ADD, ptr, val, unused); |
1052 | * int sysxtensa (XTENSA_ATOMIC_EXG_ADD, ptr, val, unused); | 1047 | * int sysxtensa (SYS_XTENSA_ATOMIC_EXG_ADD, ptr, val, unused); |
1053 | * int sysxtensa (XTENSA_ATOMIC_CMP_SWP, ptr, oldval, newval); | 1048 | * int sysxtensa (SYS_XTENSA_ATOMIC_CMP_SWP, ptr, oldval, newval); |
1054 | * a2 a6 a3 a4 a5 | 1049 | * a2 a6 a3 a4 a5 |
1055 | * | 1050 | * |
1056 | * Entry condition: | 1051 | * Entry condition: |
1057 | * | 1052 | * |
1058 | * a0: trashed, original value saved on stack (PT_AREG0) | 1053 | * a0: a2 (syscall-nr), original value saved on stack (PT_AREG0) |
1059 | * a1: a1 | 1054 | * a1: a1 |
1060 | * a2: new stack pointer, original in DEPC | 1055 | * a2: new stack pointer, original in a0 and DEPC |
1061 | * a3: dispatch table | 1056 | * a3: dispatch table, original in excsave_1 |
1057 | * a4..a15: unchanged | ||
1062 | * depc: a2, original value saved on stack (PT_DEPC) | 1058 | * depc: a2, original value saved on stack (PT_DEPC) |
1063 | * excsave_1: a3 | 1059 | * excsave_1: a3 |
1064 | * | 1060 | * |
@@ -1091,59 +1087,62 @@ ENTRY(fast_syscall_unrecoverable) | |||
1091 | #define CATCH \ | 1087 | #define CATCH \ |
1092 | 67: | 1088 | 67: |
1093 | 1089 | ||
1094 | ENTRY(fast_syscall_sysxtensa) | 1090 | ENTRY(fast_syscall_xtensa) |
1095 | |||
1096 | _beqz a6, 1f | ||
1097 | _blti a6, SYSXTENSA_COUNT, 2f | ||
1098 | 1091 | ||
1099 | 1: j user_exception | 1092 | xsr a3, EXCSAVE_1 # restore a3, excsave1 |
1100 | |||
1101 | 2: xsr a3, EXCSAVE_1 # restore a3, excsave1 | ||
1102 | s32i a7, a2, PT_AREG7 | ||
1103 | 1093 | ||
1094 | s32i a7, a2, PT_AREG7 # we need an additional register | ||
1104 | movi a7, 4 # sizeof(unsigned int) | 1095 | movi a7, 4 # sizeof(unsigned int) |
1105 | access_ok a0, a3, a7, a2, .Leac | 1096 | access_ok a3, a7, a0, a2, .Leac # a0: scratch reg, a2: sp |
1106 | 1097 | ||
1107 | _beqi a6, SYSXTENSA_ATOMIC_SET, .Lset | 1098 | addi a6, a6, -1 # assuming SYS_XTENSA_ATOMIC_SET = 1 |
1108 | _beqi a6, SYSXTENSA_ATOMIC_EXG_ADD, .Lexg | 1099 | _bgeui a6, SYS_XTENSA_COUNT - 1, .Lill |
1109 | _beqi a6, SYSXTENSA_ATOMIC_ADD, .Ladd | 1100 | _bnei a6, SYS_XTENSA_ATOMIC_CMP_SWP - 1, .Lnswp |
1110 | 1101 | ||
1111 | /* Fall through for SYSXTENSA_ATOMIC_CMP_SWP */ | 1102 | /* Fall through for ATOMIC_CMP_SWP. */ |
1112 | 1103 | ||
1113 | .Lswp: /* Atomic compare and swap */ | 1104 | .Lswp: /* Atomic compare and swap */ |
1114 | 1105 | ||
1115 | TRY l32i a7, a3, 0 # read old value | 1106 | TRY l32i a0, a3, 0 # read old value |
1116 | bne a7, a4, 1f # same as old value? jump | 1107 | bne a0, a4, 1f # same as old value? jump |
1117 | s32i a5, a3, 0 # different, modify value | 1108 | TRY s32i a5, a3, 0 # different, modify value |
1118 | movi a7, 1 # and return 1 | 1109 | l32i a7, a2, PT_AREG7 # restore a7 |
1119 | j .Lret | 1110 | l32i a0, a2, PT_AREG0 # restore a0 |
1120 | 1111 | movi a2, 1 # and return 1 | |
1121 | 1: movi a7, 0 # same values: return 0 | 1112 | addi a6, a6, 1 # restore a6 (really necessary?) |
1122 | j .Lret | 1113 | rfe |
1123 | |||
1124 | .Ladd: /* Atomic add */ | ||
1125 | .Lexg: /* Atomic (exchange) add */ | ||
1126 | 1114 | ||
1127 | TRY l32i a7, a3, 0 | 1115 | 1: l32i a7, a2, PT_AREG7 # restore a7 |
1128 | add a4, a4, a7 | 1116 | l32i a0, a2, PT_AREG0 # restore a0 |
1129 | s32i a4, a3, 0 | 1117 | movi a2, 0 # return 0 (note that we cannot set |
1130 | j .Lret | 1118 | addi a6, a6, 1 # restore a6 (really necessary?) |
1119 | rfe | ||
1131 | 1120 | ||
1132 | .Lset: /* Atomic set */ | 1121 | .Lnswp: /* Atomic set, add, and exg_add. */ |
1133 | 1122 | ||
1134 | TRY l32i a7, a3, 0 # read old value as return value | 1123 | TRY l32i a7, a3, 0 # orig |
1135 | s32i a4, a3, 0 # write new value | 1124 | add a0, a4, a7 # + arg |
1125 | moveqz a0, a4, a6 # set | ||
1126 | TRY s32i a0, a3, 0 # write new value | ||
1136 | 1127 | ||
1137 | .Lret: mov a0, a2 | 1128 | mov a0, a2 |
1138 | mov a2, a7 | 1129 | mov a2, a7 |
1139 | l32i a7, a0, PT_AREG7 | 1130 | l32i a7, a0, PT_AREG7 # restore a7 |
1140 | l32i a3, a0, PT_AREG3 | 1131 | l32i a0, a0, PT_AREG0 # restore a0 |
1141 | l32i a0, a0, PT_AREG0 | 1132 | addi a6, a6, 1 # restore a6 (really necessary?) |
1142 | rfe | 1133 | rfe |
1143 | 1134 | ||
1144 | CATCH | 1135 | CATCH |
1145 | .Leac: movi a7, -EFAULT | 1136 | .Leac: l32i a7, a2, PT_AREG7 # restore a7 |
1146 | j .Lret | 1137 | l32i a0, a2, PT_AREG0 # restore a0 |
1138 | movi a2, -EFAULT | ||
1139 | rfe | ||
1140 | |||
1141 | .Lill: l32i a7, a2, PT_AREG0 # restore a7 | ||
1142 | l32i a0, a2, PT_AREG0 # restore a0 | ||
1143 | movi a2, -EINVAL | ||
1144 | rfe | ||
1145 | |||
1147 | 1146 | ||
1148 | 1147 | ||
1149 | 1148 | ||
@@ -1907,6 +1906,103 @@ ENTRY(fast_coprocessor) | |||
1907 | #endif /* XCHAL_EXTRA_SA_SIZE */ | 1906 | #endif /* XCHAL_EXTRA_SA_SIZE */ |
1908 | 1907 | ||
1909 | /* | 1908 | /* |
1909 | * System Calls. | ||
1910 | * | ||
1911 | * void system_call (struct pt_regs* regs, int exccause) | ||
1912 | * a2 a3 | ||
1913 | */ | ||
1914 | |||
1915 | ENTRY(system_call) | ||
1916 | entry a1, 32 | ||
1917 | |||
1918 | /* regs->syscall = regs->areg[2] */ | ||
1919 | |||
1920 | l32i a3, a2, PT_AREG2 | ||
1921 | mov a6, a2 | ||
1922 | movi a4, do_syscall_trace_enter | ||
1923 | s32i a3, a2, PT_SYSCALL | ||
1924 | callx4 a4 | ||
1925 | |||
1926 | /* syscall = sys_call_table[syscall_nr] */ | ||
1927 | |||
1928 | movi a4, sys_call_table; | ||
1929 | movi a5, __NR_syscall_count | ||
1930 | movi a6, -ENOSYS | ||
1931 | bgeu a3, a5, 1f | ||
1932 | |||
1933 | addx4 a4, a3, a4 | ||
1934 | l32i a4, a4, 0 | ||
1935 | movi a5, sys_ni_syscall; | ||
1936 | beq a4, a5, 1f | ||
1937 | |||
1938 | /* Load args: arg0 - arg5 are passed via regs. */ | ||
1939 | |||
1940 | l32i a6, a2, PT_AREG6 | ||
1941 | l32i a7, a2, PT_AREG3 | ||
1942 | l32i a8, a2, PT_AREG4 | ||
1943 | l32i a9, a2, PT_AREG5 | ||
1944 | l32i a10, a2, PT_AREG8 | ||
1945 | l32i a11, a2, PT_AREG9 | ||
1946 | |||
1947 | /* Pass one additional argument to the syscall: pt_regs (on stack) */ | ||
1948 | s32i a2, a1, 0 | ||
1949 | |||
1950 | callx4 a4 | ||
1951 | |||
1952 | 1: /* regs->areg[2] = return_value */ | ||
1953 | |||
1954 | s32i a6, a2, PT_AREG2 | ||
1955 | movi a4, do_syscall_trace_leave | ||
1956 | mov a6, a2 | ||
1957 | callx4 a4 | ||
1958 | retw | ||
1959 | |||
1960 | |||
1961 | /* | ||
1962 | * Create a kernel thread | ||
1963 | * | ||
1964 | * int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) | ||
1965 | * a2 a2 a3 a4 | ||
1966 | */ | ||
1967 | |||
1968 | ENTRY(kernel_thread) | ||
1969 | entry a1, 16 | ||
1970 | |||
1971 | mov a5, a2 # preserve fn over syscall | ||
1972 | mov a7, a3 # preserve args over syscall | ||
1973 | |||
1974 | movi a3, _CLONE_VM | _CLONE_UNTRACED | ||
1975 | movi a2, __NR_clone | ||
1976 | or a6, a4, a3 # arg0: flags | ||
1977 | mov a3, a1 # arg1: sp | ||
1978 | syscall | ||
1979 | |||
1980 | beq a3, a1, 1f # branch if parent | ||
1981 | mov a6, a7 # args | ||
1982 | callx4 a5 # fn(args) | ||
1983 | |||
1984 | movi a2, __NR_exit | ||
1985 | syscall # return value of fn(args) still in a6 | ||
1986 | |||
1987 | 1: retw | ||
1988 | |||
1989 | /* | ||
1990 | * Do a system call from kernel instead of calling sys_execve, so we end up | ||
1991 | * with proper pt_regs. | ||
1992 | * | ||
1993 | * int kernel_execve(const char *fname, char *const argv[], charg *const envp[]) | ||
1994 | * a2 a2 a3 a4 | ||
1995 | */ | ||
1996 | |||
1997 | ENTRY(kernel_execve) | ||
1998 | entry a1, 16 | ||
1999 | mov a6, a2 # arg0 is in a6 | ||
2000 | movi a2, __NR_execve | ||
2001 | syscall | ||
2002 | |||
2003 | retw | ||
2004 | |||
2005 | /* | ||
1910 | * Task switch. | 2006 | * Task switch. |
1911 | * | 2007 | * |
1912 | * struct task* _switch_to (struct task* prev, struct task* next) | 2008 | * struct task* _switch_to (struct task* prev, struct task* next) |
@@ -1964,33 +2060,9 @@ ENTRY(ret_from_fork) | |||
1964 | movi a4, schedule_tail | 2060 | movi a4, schedule_tail |
1965 | callx4 a4 | 2061 | callx4 a4 |
1966 | 2062 | ||
1967 | movi a4, do_syscall_trace | 2063 | movi a4, do_syscall_trace_leave |
2064 | mov a6, a1 | ||
1968 | callx4 a4 | 2065 | callx4 a4 |
1969 | 2066 | ||
1970 | j common_exception_return | 2067 | j common_exception_return |
1971 | 2068 | ||
1972 | |||
1973 | |||
1974 | /* | ||
1975 | * Table of syscalls | ||
1976 | */ | ||
1977 | |||
1978 | .data | ||
1979 | .align 4 | ||
1980 | .global sys_call_table | ||
1981 | sys_call_table: | ||
1982 | |||
1983 | #define SYSCALL(call, narg) .word call | ||
1984 | #include "syscalls.h" | ||
1985 | |||
1986 | /* | ||
1987 | * Number of arguments of each syscall | ||
1988 | */ | ||
1989 | |||
1990 | .global sys_narg_table | ||
1991 | sys_narg_table: | ||
1992 | |||
1993 | #undef SYSCALL | ||
1994 | #define SYSCALL(call, narg) .byte narg | ||
1995 | #include "syscalls.h" | ||
1996 | |||