Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 | /* SPDX-License-Identifier: GPL-2.0-only */ /* * arch/arm/include/asm/assembler.h * * Copyright (C) 1996-2000 Russell King * * This file contains arm architecture specific defines * for the different processors. * * Do not include any C declarations in this file - it is included by * assembler source. */ #ifndef __ASM_ASSEMBLER_H__ #define __ASM_ASSEMBLER_H__ #ifndef __ASSEMBLY__ #error "Only include this from assembly code" #endif #include <asm/ptrace.h> #include <asm/opcodes-virt.h> #include <asm/asm-offsets.h> #include <asm/page.h> #include <asm/thread_info.h> #include <asm/uaccess-asm.h> #define IOMEM(x) (x) /* * Endian independent macros for shifting bytes within registers. */ #ifndef __ARMEB__ #define lspull lsr #define lspush lsl #define get_byte_0 lsl #0 #define get_byte_1 lsr #8 #define get_byte_2 lsr #16 #define get_byte_3 lsr #24 #define put_byte_0 lsl #0 #define put_byte_1 lsl #8 #define put_byte_2 lsl #16 #define put_byte_3 lsl #24 #else #define lspull lsl #define lspush lsr #define get_byte_0 lsr #24 #define get_byte_1 lsr #16 #define get_byte_2 lsr #8 #define get_byte_3 lsl #0 #define put_byte_0 lsl #24 #define put_byte_1 lsl #16 #define put_byte_2 lsl #8 #define put_byte_3 lsl #0 #endif /* Select code for any configuration running in BE8 mode */ #ifdef CONFIG_CPU_ENDIAN_BE8 #define ARM_BE8(code...) code #else #define ARM_BE8(code...) #endif /* * Data preload for architectures that support it */ #if __LINUX_ARM_ARCH__ >= 5 #define PLD(code...) code #else #define PLD(code...) #endif /* * This can be used to enable code to cacheline align the destination * pointer when bulk writing to memory. Experiments on StrongARM and * XScale didn't show this a worthwhile thing to do when the cache is not * set to write-allocate (this would need further testing on XScale when WA * is used). * * On Feroceon there is much to gain however, regardless of cache mode. */ #ifdef CONFIG_CPU_FEROCEON #define CALGN(code...) code #else #define CALGN(code...) #endif #define IMM12_MASK 0xfff /* the frame pointer used for stack unwinding */ ARM( fpreg .req r11 ) THUMB( fpreg .req r7 ) /* * Enable and disable interrupts */ #if __LINUX_ARM_ARCH__ >= 6 .macro disable_irq_notrace cpsid i .endm .macro enable_irq_notrace cpsie i .endm #else .macro disable_irq_notrace msr cpsr_c, #PSR_I_BIT | SVC_MODE .endm .macro enable_irq_notrace msr cpsr_c, #SVC_MODE .endm #endif #if __LINUX_ARM_ARCH__ < 7 .macro dsb, args mcr p15, 0, r0, c7, c10, 4 .endm .macro isb, args mcr p15, 0, r0, c7, c5, 4 .endm #endif .macro asm_trace_hardirqs_off, save=1 #if defined(CONFIG_TRACE_IRQFLAGS) .if \save stmdb sp!, {r0-r3, ip, lr} .endif bl trace_hardirqs_off .if \save ldmia sp!, {r0-r3, ip, lr} .endif #endif .endm .macro asm_trace_hardirqs_on, cond=al, save=1 #if defined(CONFIG_TRACE_IRQFLAGS) /* * actually the registers should be pushed and pop'd conditionally, but * after bl the flags are certainly clobbered */ .if \save stmdb sp!, {r0-r3, ip, lr} .endif bl\cond trace_hardirqs_on .if \save ldmia sp!, {r0-r3, ip, lr} .endif #endif .endm .macro disable_irq, save=1 disable_irq_notrace asm_trace_hardirqs_off \save .endm .macro enable_irq asm_trace_hardirqs_on enable_irq_notrace .endm /* * Save the current IRQ state and disable IRQs. Note that this macro * assumes FIQs are enabled, and that the processor is in SVC mode. */ .macro save_and_disable_irqs, oldcpsr #ifdef CONFIG_CPU_V7M mrs \oldcpsr, primask #else mrs \oldcpsr, cpsr #endif disable_irq .endm .macro save_and_disable_irqs_notrace, oldcpsr #ifdef CONFIG_CPU_V7M mrs \oldcpsr, primask #else mrs \oldcpsr, cpsr #endif disable_irq_notrace .endm /* * Restore interrupt state previously stored in a register. We don't * guarantee that this will preserve the flags. */ .macro restore_irqs_notrace, oldcpsr #ifdef CONFIG_CPU_V7M msr primask, \oldcpsr #else msr cpsr_c, \oldcpsr #endif .endm .macro restore_irqs, oldcpsr tst \oldcpsr, #PSR_I_BIT asm_trace_hardirqs_on cond=eq restore_irqs_notrace \oldcpsr .endm /* * Assembly version of "adr rd, BSYM(sym)". This should only be used to * reference local symbols in the same assembly file which are to be * resolved by the assembler. Other usage is undefined. */ .irp c,,eq,ne,cs,cc,mi,pl,vs,vc,hi,ls,ge,lt,gt,le,hs,lo .macro badr\c, rd, sym #ifdef CONFIG_THUMB2_KERNEL adr\c \rd, \sym + 1 #else adr\c \rd, \sym #endif .endm .endr /* * Get current thread_info. */ .macro get_thread_info, rd /* thread_info is the first member of struct task_struct */ get_current \rd .endm /* * Increment/decrement the preempt count. */ #ifdef CONFIG_PREEMPT_COUNT .macro inc_preempt_count, ti, tmp ldr \tmp, [\ti, #TI_PREEMPT] @ get preempt count add \tmp, \tmp, #1 @ increment it str \tmp, [\ti, #TI_PREEMPT] .endm .macro dec_preempt_count, ti, tmp ldr \tmp, [\ti, #TI_PREEMPT] @ get preempt count sub \tmp, \tmp, #1 @ decrement it str \tmp, [\ti, #TI_PREEMPT] .endm .macro dec_preempt_count_ti, ti, tmp get_thread_info \ti dec_preempt_count \ti, \tmp .endm #else .macro inc_preempt_count, ti, tmp .endm .macro dec_preempt_count, ti, tmp .endm .macro dec_preempt_count_ti, ti, tmp .endm #endif #define USERL(l, x...) \ 9999: x; \ .pushsection __ex_table,"a"; \ .align 3; \ .long 9999b,l; \ .popsection #define USER(x...) USERL(9001f, x) #ifdef CONFIG_SMP #define ALT_SMP(instr...) \ 9998: instr /* * Note: if you get assembler errors from ALT_UP() when building with * CONFIG_THUMB2_KERNEL, you almost certainly need to use * ALT_SMP( W(instr) ... ) */ #define ALT_UP(instr...) \ .pushsection ".alt.smp.init", "a" ;\ .align 2 ;\ .long 9998b - . ;\ 9997: instr ;\ .if . - 9997b == 2 ;\ nop ;\ .endif ;\ .if . - 9997b != 4 ;\ .error "ALT_UP() content must assemble to exactly 4 bytes";\ .endif ;\ .popsection #define ALT_UP_B(label) \ .pushsection ".alt.smp.init", "a" ;\ .align 2 ;\ .long 9998b - . ;\ W(b) . + (label - 9998b) ;\ .popsection #else #define ALT_SMP(instr...) #define ALT_UP(instr...) instr #define ALT_UP_B(label) b label #endif /* * this_cpu_offset - load the per-CPU offset of this CPU into * register 'rd' */ .macro this_cpu_offset, rd:req #ifdef CONFIG_SMP ALT_SMP(mrc p15, 0, \rd, c13, c0, 4) #ifdef CONFIG_CPU_V6 ALT_UP_B(.L1_\@) .L0_\@: .subsection 1 .L1_\@: ldr_va \rd, __per_cpu_offset b .L0_\@ .previous #endif #else mov \rd, #0 #endif .endm /* * set_current - store the task pointer of this CPU's current task */ .macro set_current, rn:req, tmp:req #if defined(CONFIG_CURRENT_POINTER_IN_TPIDRURO) || defined(CONFIG_SMP) 9998: mcr p15, 0, \rn, c13, c0, 3 @ set TPIDRURO register #ifdef CONFIG_CPU_V6 ALT_UP_B(.L0_\@) .subsection 1 .L0_\@: str_va \rn, __current, \tmp b .L1_\@ .previous .L1_\@: #endif #else str_va \rn, __current, \tmp #endif .endm /* * get_current - load the task pointer of this CPU's current task */ .macro get_current, rd:req #if defined(CONFIG_CURRENT_POINTER_IN_TPIDRURO) || defined(CONFIG_SMP) 9998: mrc p15, 0, \rd, c13, c0, 3 @ get TPIDRURO register #ifdef CONFIG_CPU_V6 ALT_UP_B(.L0_\@) .subsection 1 .L0_\@: ldr_va \rd, __current b .L1_\@ .previous .L1_\@: #endif #else ldr_va \rd, __current #endif .endm /* * reload_current - reload the task pointer of this CPU's current task * into the TLS register */ .macro reload_current, t1:req, t2:req #if defined(CONFIG_CURRENT_POINTER_IN_TPIDRURO) || defined(CONFIG_SMP) #ifdef CONFIG_CPU_V6 ALT_SMP(nop) ALT_UP_B(.L0_\@) #endif ldr_this_cpu \t1, __entry_task, \t1, \t2 mcr p15, 0, \t1, c13, c0, 3 @ store in TPIDRURO .L0_\@: #endif .endm /* * Instruction barrier */ .macro instr_sync #if __LINUX_ARM_ARCH__ >= 7 isb #elif __LINUX_ARM_ARCH__ == 6 mcr p15, 0, r0, c7, c5, 4 #endif .endm /* * SMP data memory barrier */ .macro smp_dmb mode #ifdef CONFIG_SMP #if __LINUX_ARM_ARCH__ >= 7 .ifeqs "\mode","arm" ALT_SMP(dmb ish) .else ALT_SMP(W(dmb) ish) .endif #elif __LINUX_ARM_ARCH__ == 6 ALT_SMP(mcr p15, 0, r0, c7, c10, 5) @ dmb #else #error Incompatible SMP platform #endif .ifeqs "\mode","arm" ALT_UP(nop) .else ALT_UP(W(nop)) .endif #endif .endm #if defined(CONFIG_CPU_V7M) /* * setmode is used to assert to be in svc mode during boot. For v7-M * this is done in __v7m_setup, so setmode can be empty here. */ .macro setmode, mode, reg .endm #elif defined(CONFIG_THUMB2_KERNEL) .macro setmode, mode, reg mov \reg, #\mode msr cpsr_c, \reg .endm #else .macro setmode, mode, reg msr cpsr_c, #\mode .endm #endif /* * Helper macro to enter SVC mode cleanly and mask interrupts. reg is * a scratch register for the macro to overwrite. * * This macro is intended for forcing the CPU into SVC mode at boot time. * you cannot return to the original mode. */ .macro safe_svcmode_maskall reg:req #if __LINUX_ARM_ARCH__ >= 6 && !defined(CONFIG_CPU_V7M) mrs \reg , cpsr eor \reg, \reg, #HYP_MODE tst \reg, #MODE_MASK bic \reg , \reg , #MODE_MASK orr \reg , \reg , #PSR_I_BIT | PSR_F_BIT | SVC_MODE THUMB( orr \reg , \reg , #PSR_T_BIT ) bne 1f orr \reg, \reg, #PSR_A_BIT badr lr, 2f msr spsr_cxsf, \reg __MSR_ELR_HYP(14) __ERET 1: msr cpsr_c, \reg 2: #else /* * workaround for possibly broken pre-v6 hardware * (akita, Sharp Zaurus C-1000, PXA270-based) */ setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, \reg #endif .endm /* * STRT/LDRT access macros with ARM and Thumb-2 variants */ #ifdef CONFIG_THUMB2_KERNEL .macro usraccoff, instr, reg, ptr, inc, off, cond, abort, t=TUSER() 9999: .if \inc == 1 \instr\()b\t\cond\().w \reg, [\ptr, #\off] .elseif \inc == 4 \instr\t\cond\().w \reg, [\ptr, #\off] .else .error "Unsupported inc macro argument" .endif .pushsection __ex_table,"a" .align 3 .long 9999b, \abort .popsection .endm .macro usracc, instr, reg, ptr, inc, cond, rept, abort @ explicit IT instruction needed because of the label @ introduced by the USER macro .ifnc \cond,al .if \rept == 1 itt \cond .elseif \rept == 2 ittt \cond .else .error "Unsupported rept macro argument" .endif .endif @ Slightly optimised to avoid incrementing the pointer twice usraccoff \instr, \reg, \ptr, \inc, 0, \cond, \abort .if \rept == 2 usraccoff \instr, \reg, \ptr, \inc, \inc, \cond, \abort .endif add\cond \ptr, #\rept * \inc .endm #else /* !CONFIG_THUMB2_KERNEL */ .macro usracc, instr, reg, ptr, inc, cond, rept, abort, t=TUSER() .rept \rept 9999: .if \inc == 1 \instr\()b\t\cond \reg, [\ptr], #\inc .elseif \inc == 4 \instr\t\cond \reg, [\ptr], #\inc .else .error "Unsupported inc macro argument" .endif .pushsection __ex_table,"a" .align 3 .long 9999b, \abort .popsection .endr .endm #endif /* CONFIG_THUMB2_KERNEL */ .macro strusr, reg, ptr, inc, cond=al, rept=1, abort=9001f usracc str, \reg, \ptr, \inc, \cond, \rept, \abort .endm .macro ldrusr, reg, ptr, inc, cond=al, rept=1, abort=9001f usracc ldr, \reg, \ptr, \inc, \cond, \rept, \abort .endm /* Utility macro for declaring string literals */ .macro string name:req, string .type \name , #object \name: .asciz "\string" .size \name , . - \name .endm .irp c,,eq,ne,cs,cc,mi,pl,vs,vc,hi,ls,ge,lt,gt,le,hs,lo .macro ret\c, reg #if __LINUX_ARM_ARCH__ < 6 mov\c pc, \reg #else .ifeqs "\reg", "lr" bx\c \reg .else mov\c pc, \reg .endif #endif .endm .endr .macro ret.w, reg ret \reg #ifdef CONFIG_THUMB2_KERNEL nop #endif .endm .macro bug, msg, line #ifdef CONFIG_THUMB2_KERNEL 1: .inst 0xde02 #else 1: .inst 0xe7f001f2 #endif #ifdef CONFIG_DEBUG_BUGVERBOSE .pushsection .rodata.str, "aMS", %progbits, 1 2: .asciz "\msg" .popsection .pushsection __bug_table, "aw" .align 2 .word 1b, 2b .hword \line .popsection #endif .endm #ifdef CONFIG_KPROBES #define _ASM_NOKPROBE(entry) \ .pushsection "_kprobe_blacklist", "aw" ; \ .balign 4 ; \ .long entry; \ .popsection #else #define _ASM_NOKPROBE(entry) #endif .macro __adldst_l, op, reg, sym, tmp, c .if __LINUX_ARM_ARCH__ < 7 ldr\c \tmp, .La\@ .subsection 1 .align 2 .La\@: .long \sym - .Lpc\@ .previous .else .ifnb \c THUMB( ittt \c ) .endif movw\c \tmp, #:lower16:\sym - .Lpc\@ movt\c \tmp, #:upper16:\sym - .Lpc\@ .endif #ifndef CONFIG_THUMB2_KERNEL .set .Lpc\@, . + 8 // PC bias .ifc \op, add add\c \reg, \tmp, pc .else \op\c \reg, [pc, \tmp] .endif #else .Lb\@: add\c \tmp, \tmp, pc /* * In Thumb-2 builds, the PC bias depends on whether we are currently * emitting into a .arm or a .thumb section. The size of the add opcode * above will be 2 bytes when emitting in Thumb mode and 4 bytes when * emitting in ARM mode, so let's use this to account for the bias. */ .set .Lpc\@, . + (. - .Lb\@) .ifnc \op, add \op\c \reg, [\tmp] .endif #endif .endm /* * mov_l - move a constant value or [relocated] address into a register */ .macro mov_l, dst:req, imm:req, cond .if __LINUX_ARM_ARCH__ < 7 ldr\cond \dst, =\imm .else movw\cond \dst, #:lower16:\imm movt\cond \dst, #:upper16:\imm .endif .endm /* * adr_l - adr pseudo-op with unlimited range * * @dst: destination register * @sym: name of the symbol * @cond: conditional opcode suffix */ .macro adr_l, dst:req, sym:req, cond __adldst_l add, \dst, \sym, \dst, \cond .endm /* * ldr_l - ldr <literal> pseudo-op with unlimited range * * @dst: destination register * @sym: name of the symbol * @cond: conditional opcode suffix */ .macro ldr_l, dst:req, sym:req, cond __adldst_l ldr, \dst, \sym, \dst, \cond .endm /* * str_l - str <literal> pseudo-op with unlimited range * * @src: source register * @sym: name of the symbol * @tmp: mandatory scratch register * @cond: conditional opcode suffix */ .macro str_l, src:req, sym:req, tmp:req, cond __adldst_l str, \src, \sym, \tmp, \cond .endm .macro __ldst_va, op, reg, tmp, sym, cond, offset #if __LINUX_ARM_ARCH__ >= 7 || \ !defined(CONFIG_ARM_HAS_GROUP_RELOCS) || \ (defined(MODULE) && defined(CONFIG_ARM_MODULE_PLTS)) mov_l \tmp, \sym, \cond #else /* * Avoid a literal load, by emitting a sequence of ADD/LDR instructions * with the appropriate relocations. The combined sequence has a range * of -/+ 256 MiB, which should be sufficient for the core kernel and * for modules loaded into the module region. */ .globl \sym .reloc .L0_\@, R_ARM_ALU_PC_G0_NC, \sym .reloc .L1_\@, R_ARM_ALU_PC_G1_NC, \sym .reloc .L2_\@, R_ARM_LDR_PC_G2, \sym .L0_\@: sub\cond \tmp, pc, #8 - \offset .L1_\@: sub\cond \tmp, \tmp, #4 - \offset .L2_\@: #endif \op\cond \reg, [\tmp, #\offset] .endm /* * ldr_va - load a 32-bit word from the virtual address of \sym */ .macro ldr_va, rd:req, sym:req, cond, tmp, offset=0 .ifnb \tmp __ldst_va ldr, \rd, \tmp, \sym, \cond, \offset .else __ldst_va ldr, \rd, \rd, \sym, \cond, \offset .endif .endm /* * str_va - store a 32-bit word to the virtual address of \sym */ .macro str_va, rn:req, sym:req, tmp:req, cond __ldst_va str, \rn, \tmp, \sym, \cond, 0 .endm /* * ldr_this_cpu_armv6 - Load a 32-bit word from the per-CPU variable 'sym', * without using a temp register. Supported in ARM mode * only. */ .macro ldr_this_cpu_armv6, rd:req, sym:req this_cpu_offset \rd .globl \sym .reloc .L0_\@, R_ARM_ALU_PC_G0_NC, \sym .reloc .L1_\@, R_ARM_ALU_PC_G1_NC, \sym .reloc .L2_\@, R_ARM_LDR_PC_G2, \sym add \rd, \rd, pc .L0_\@: sub \rd, \rd, #4 .L1_\@: sub \rd, \rd, #0 .L2_\@: ldr \rd, [\rd, #4] .endm /* * ldr_this_cpu - Load a 32-bit word from the per-CPU variable 'sym' * into register 'rd', which may be the stack pointer, * using 't1' and 't2' as general temp registers. These * are permitted to overlap with 'rd' if != sp */ .macro ldr_this_cpu, rd:req, sym:req, t1:req, t2:req #ifndef CONFIG_SMP ldr_va \rd, \sym, tmp=\t1 #elif __LINUX_ARM_ARCH__ >= 7 || \ !defined(CONFIG_ARM_HAS_GROUP_RELOCS) || \ (defined(MODULE) && defined(CONFIG_ARM_MODULE_PLTS)) this_cpu_offset \t1 mov_l \t2, \sym ldr \rd, [\t1, \t2] #else ldr_this_cpu_armv6 \rd, \sym #endif .endm /* * rev_l - byte-swap a 32-bit value * * @val: source/destination register * @tmp: scratch register */ .macro rev_l, val:req, tmp:req .if __LINUX_ARM_ARCH__ < 6 eor \tmp, \val, \val, ror #16 bic \tmp, \tmp, #0x00ff0000 mov \val, \val, ror #8 eor \val, \val, \tmp, lsr #8 .else rev \val, \val .endif .endm /* * bl_r - branch and link to register * * @dst: target to branch to * @c: conditional opcode suffix */ .macro bl_r, dst:req, c .if __LINUX_ARM_ARCH__ < 6 mov\c lr, pc mov\c pc, \dst .else blx\c \dst .endif .endm #endif /* __ASM_ASSEMBLER_H__ */ |