1/* $Id: entry.S,v 1.37 2004/06/11 13:02:46 doyu Exp $ 2 * 3 * linux/arch/sh/entry.S 4 * 5 * Copyright (C) 1999, 2000, 2002 Niibe Yutaka 6 * Copyright (C) 2003 Paul Mundt 7 * 8 * This file is subject to the terms and conditions of the GNU General Public 9 * License. See the file "COPYING" in the main directory of this archive 10 * for more details. 11 * 12 */ 13 14! NOTE: 15! GNU as (as of 2.9.1) changes bf/s into bt/s and bra, when the address 16! to be jumped is too far, but it causes illegal slot exception. 17 18/* 19 * entry.S contains the system-call and fault low-level handling routines. 20 * This also contains the timer-interrupt handler, as well as all interrupts 21 * and faults that can result in a task-switch. 22 * 23 * NOTE: This code handles signal-recognition, which happens every time 24 * after a timer-interrupt and after each system call. 25 * 26 * NOTE: This code uses a convention that instructions in the delay slot 27 * of a transfer-control instruction are indented by an extra space, thus: 28 * 29 * jmp @k0 ! control-transfer instruction 30 * ldc k1, ssr ! delay slot 31 * 32 * Stack layout in 'ret_from_syscall': 33 * ptrace needs to have all regs on the stack. 34 * if the order here is changed, it needs to be 35 * updated in ptrace.c and ptrace.h 36 * 37 * r0 38 * ... 39 * r15 = stack pointer 40 * spc 41 * pr 42 * ssr 43 * gbr 44 * mach 45 * macl 46 * syscall # 47 * 48 */ 49 50#if defined(CONFIG_PREEMPT) 51# define preempt_stop() cli 52#else 53# define preempt_stop() 54# define resume_kernel __restore_all 55#endif 56 57#if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB) 58! Handle kernel debug if either kgdb (SW) or gdb-stub (FW) is present. 59! If both are configured, handle the debug traps (breakpoints) in SW, 60! but still allow BIOS traps to FW. 61 62 .align 2 63debug_kernel: 64#if defined(CONFIG_SH_STANDARD_BIOS) && defined(CONFIG_SH_KGDB) 65 /* Force BIOS call to FW (debug_trap put TRA in r8) */ 66 mov r8,r0 67 shlr2 r0 68 cmp/eq #0x3f,r0 69 bt debug_kernel_fw 70#endif /* CONFIG_SH_STANDARD_BIOS && CONFIG_SH_KGDB */ 71 72debug_enter: 73#if defined(CONFIG_SH_KGDB) 74 /* Jump to kgdb, pass stacked regs as arg */ 75debug_kernel_sw: 76 mov.l 3f, r0 77 jmp @r0 78 mov r15, r4 79 .align 2 803: .long kgdb_handle_exception 81#endif /* CONFIG_SH_KGDB */ 82#ifdef CONFIG_SH_STANDARD_BIOS 83 bra debug_kernel_fw 84 nop 85#endif 86#endif /* CONFIG_SH_STANDARD_BIOS || CONFIG_SH_KGDB */ 87 88 .align 2 89debug_trap: 90#if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB) 91 mov r8, r0 92 shlr2 r0 93 cmp/eq #0x3f, r0 ! sh_bios() trap 94 bf 1f 95#ifdef CONFIG_SH_KGDB 96 cmp/eq #0xff, r0 ! XXX: KGDB trap, fix for SH-2. 97 bf 1f 98#endif 99 mov #OFF_SR, r0 100 mov.l @(r0,r15), r0 ! get status register 101 shll r0 102 shll r0 ! kernel space? 103 bt/s debug_kernel 1041: 105#endif 106 mov.l @r15, r0 ! Restore R0 value 107 mov.l 1f, r8 108 jmp @r8 109 nop 110 111 .align 2 112ENTRY(exception_error) 113 ! 114#ifdef CONFIG_TRACE_IRQFLAGS 115 mov.l 3f, r0 116 jsr @r0 117 nop 118#endif 119 sti 120 mov.l 2f, r0 121 jmp @r0 122 nop 123 124! 125 .align 2 1261: .long break_point_trap_software 1272: .long do_exception_error 128#ifdef CONFIG_TRACE_IRQFLAGS 1293: .long trace_hardirqs_on 130#endif 131 132 .align 2 133ret_from_exception: 134 preempt_stop() 135#ifdef CONFIG_TRACE_IRQFLAGS 136 mov.l 4f, r0 137 jsr @r0 138 nop 139#endif 140ENTRY(ret_from_irq) 141 ! 142 mov #OFF_SR, r0 143 mov.l @(r0,r15), r0 ! get status register 144 shll r0 145 shll r0 ! kernel space? 146 get_current_thread_info r8, r0 147 bt resume_kernel ! Yes, it's from kernel, go back soon 148 149#ifdef CONFIG_PREEMPT 150 bra resume_userspace 151 nop 152ENTRY(resume_kernel) 153 mov.l @(TI_PRE_COUNT,r8), r0 ! current_thread_info->preempt_count 154 tst r0, r0 155 bf noresched 156need_resched: 157 mov.l @(TI_FLAGS,r8), r0 ! current_thread_info->flags 158 tst #_TIF_NEED_RESCHED, r0 ! need_resched set? 159 bt noresched 160 161 mov #OFF_SR, r0 162 mov.l @(r0,r15), r0 ! get status register 163 and #0xf0, r0 ! interrupts off (exception path)? 164 cmp/eq #0xf0, r0 165 bt noresched 166 167 mov.l 1f, r0 168 mov.l r0, @(TI_PRE_COUNT,r8) 169 170#ifdef CONFIG_TRACE_IRQFLAGS 171 mov.l 3f, r0 172 jsr @r0 173 nop 174#endif 175 sti 176 mov.l 2f, r0 177 jsr @r0 178 nop 179 mov #0, r0 180 mov.l r0, @(TI_PRE_COUNT,r8) 181 cli 182#ifdef CONFIG_TRACE_IRQFLAGS 183 mov.l 4f, r0 184 jsr @r0 185 nop 186#endif 187 188 bra need_resched 189 nop 190 191noresched: 192 bra __restore_all 193 nop 194 195 .align 2 1961: .long PREEMPT_ACTIVE 1972: .long schedule 198#ifdef CONFIG_TRACE_IRQFLAGS 1993: .long trace_hardirqs_on 2004: .long trace_hardirqs_off 201#endif 202#endif 203 204ENTRY(resume_userspace) 205 ! r8: current_thread_info 206 cli 207#ifdef CONFIG_TRACE_IRQFLAGS 208 mov.l 5f, r0 209 jsr @r0 210 nop 211#endif 212 mov.l @(TI_FLAGS,r8), r0 ! current_thread_info->flags 213 tst #_TIF_WORK_MASK, r0 214 bt/s __restore_all 215 tst #_TIF_NEED_RESCHED, r0 216 217 .align 2 218work_pending: 219 ! r0: current_thread_info->flags 220 ! r8: current_thread_info 221 ! t: result of "tst #_TIF_NEED_RESCHED, r0" 222 bf/s work_resched 223 tst #(_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK), r0 224work_notifysig: 225 bt/s __restore_all 226 mov r15, r4 227 mov r12, r5 ! set arg1(save_r0) 228 mov r0, r6 229 mov.l 2f, r1 230 mov.l 3f, r0 231 jmp @r1 232 lds r0, pr 233work_resched: 234#ifndef CONFIG_PREEMPT 235 ! gUSA handling 236 mov.l @(OFF_SP,r15), r0 ! get user space stack pointer 237 mov r0, r1 238 shll r0 239 bf/s 1f 240 shll r0 241 bf/s 1f 242 mov #OFF_PC, r0 243 ! SP >= 0xc0000000 : gUSA mark 244 mov.l @(r0,r15), r2 ! get user space PC (program counter) 245 mov.l @(OFF_R0,r15), r3 ! end point 246 cmp/hs r3, r2 ! r2 >= r3? 247 bt 1f 248 add r3, r1 ! rewind point #2 249 mov.l r1, @(r0,r15) ! reset PC to rewind point #2 250 ! 2511: 252#endif 253 mov.l 1f, r1 254 jsr @r1 ! schedule 255 nop 256 cli 257#ifdef CONFIG_TRACE_IRQFLAGS 258 mov.l 5f, r0 259 jsr @r0 260 nop 261#endif 262 ! 263 mov.l @(TI_FLAGS,r8), r0 ! current_thread_info->flags 264 tst #_TIF_WORK_MASK, r0 265 bt __restore_all 266 bra work_pending 267 tst #_TIF_NEED_RESCHED, r0 268 269 .align 2 2701: .long schedule 2712: .long do_notify_resume 2723: .long restore_all 273#ifdef CONFIG_TRACE_IRQFLAGS 2744: .long trace_hardirqs_on 2755: .long trace_hardirqs_off 276#endif 277 278 .align 2 279syscall_exit_work: 280 ! r0: current_thread_info->flags 281 ! r8: current_thread_info 282 tst #_TIF_SYSCALL_TRACE, r0 283 bt/s work_pending 284 tst #_TIF_NEED_RESCHED, r0 285#ifdef CONFIG_TRACE_IRQFLAGS 286 mov.l 5f, r0 287 jsr @r0 288 nop 289#endif 290 sti 291 ! XXX setup arguments... 292 mov.l 4f, r0 ! do_syscall_trace 293 jsr @r0 294 nop 295 bra resume_userspace 296 nop 297 298 .align 2 299syscall_trace_entry: 300 ! Yes it is traced. 301 ! XXX setup arguments... 302 mov.l 4f, r11 ! Call do_syscall_trace which notifies 303 jsr @r11 ! superior (will chomp R[0-7]) 304 nop 305 ! Reload R0-R4 from kernel stack, where the 306 ! parent may have modified them using 307 ! ptrace(POKEUSR). (Note that R0-R2 are 308 ! used by the system call handler directly 309 ! from the kernel stack anyway, so don't need 310 ! to be reloaded here.) This allows the parent 311 ! to rewrite system calls and args on the fly. 312 mov.l @(OFF_R4,r15), r4 ! arg0 313 mov.l @(OFF_R5,r15), r5 314 mov.l @(OFF_R6,r15), r6 315 mov.l @(OFF_R7,r15), r7 ! arg3 316 mov.l @(OFF_R3,r15), r3 ! syscall_nr 317 ! 318 mov.l 2f, r10 ! Number of syscalls 319 cmp/hs r10, r3 320 bf syscall_call 321 mov #-ENOSYS, r0 322 bra syscall_exit 323 mov.l r0, @(OFF_R0,r15) ! Return value 324 325__restore_all: 326 mov.l 1f, r0 327 jmp @r0 328 nop 329 330 .align 2 3311: .long restore_all 332 333 .align 2 334not_syscall_tra: 335 bra debug_trap 336 nop 337 338 .align 2 339syscall_badsys: ! Bad syscall number 340 mov #-ENOSYS, r0 341 bra resume_userspace 342 mov.l r0, @(OFF_R0,r15) ! Return value 343 344 345/* 346 * Syscall interface: 347 * 348 * Syscall #: R3 349 * Arguments #0 to #3: R4--R7 350 * Arguments #4 to #6: R0, R1, R2 351 * TRA: (number of arguments + 0x10) x 4 352 * 353 * This code also handles delegating other traps to the BIOS/gdb stub 354 * according to: 355 * 356 * Trap number 357 * (TRA>>2) Purpose 358 * -------- ------- 359 * 0x0-0xf old syscall ABI 360 * 0x10-0x1f new syscall ABI 361 * 0x20-0xff delegated through debug_trap to BIOS/gdb stub. 362 * 363 * Note: When we're first called, the TRA value must be shifted 364 * right 2 bits in order to get the value that was used as the "trapa" 365 * argument. 366 */ 367 368 .align 2 369 .globl ret_from_fork 370ret_from_fork: 371 mov.l 1f, r8 372 jsr @r8 373 mov r0, r4 374 bra syscall_exit 375 nop 376 .align 2 3771: .long schedule_tail 378 ! 379ENTRY(system_call) 380#if !defined(CONFIG_CPU_SH2) 381 mov.l 1f, r9 382 mov.l @r9, r8 ! Read from TRA (Trap Address) Register 383#endif 384 ! 385 ! Is the trap argument >= 0x20? (TRA will be >= 0x80) 386 mov #0x7f, r9 387 cmp/hi r9, r8 388 bt/s not_syscall_tra 389 mov #OFF_TRA, r9 390 add r15, r9 391 mov.l r8, @r9 ! set TRA value to tra 392#ifdef CONFIG_TRACE_IRQFLAGS 393 mov.l 5f, r10 394 jsr @r10 395 nop 396#endif 397 sti 398 399 ! 400 get_current_thread_info r8, r10 401 mov.l @(TI_FLAGS,r8), r8 402 mov #_TIF_SYSCALL_TRACE, r10 403 tst r10, r8 404 bf syscall_trace_entry 405 ! 406 mov.l 2f, r8 ! Number of syscalls 407 cmp/hs r8, r3 408 bt syscall_badsys 409 ! 410syscall_call: 411 shll2 r3 ! x4 412 mov.l 3f, r8 ! Load the address of sys_call_table 413 add r8, r3 414 mov.l @r3, r8 415 jsr @r8 ! jump to specific syscall handler 416 nop 417 mov.l @(OFF_R0,r15), r12 ! save r0 418 mov.l r0, @(OFF_R0,r15) ! save the return value 419 ! 420syscall_exit: 421 cli 422#ifdef CONFIG_TRACE_IRQFLAGS 423 mov.l 6f, r0 424 jsr @r0 425 nop 426#endif 427 ! 428 get_current_thread_info r8, r0 429 mov.l @(TI_FLAGS,r8), r0 ! current_thread_info->flags 430 tst #_TIF_ALLWORK_MASK, r0 431 bf syscall_exit_work 432 bra __restore_all 433 nop 434 .align 2 435#if !defined(CONFIG_CPU_SH2) 4361: .long TRA 437#endif 4382: .long NR_syscalls 4393: .long sys_call_table 4404: .long do_syscall_trace 441#ifdef CONFIG_TRACE_IRQFLAGS 4425: .long trace_hardirqs_on 4436: .long trace_hardirqs_off 444#endif 445