forked from Minki/linux
80c59dafb1
This patch does two things: * Ensure that asynchronous aborts are masked at kernel entry. The bootloader should be masking these anyway, but this reduces the damage window just in case it doesn't. * Enter svc mode via exception return to ensure that CPU state is properly serialised. This does not matter when switching from an ordinary privileged mode ("PL1" modes in ARMv7-AR rev C parlance), but it potentially does matter when switching from a another privileged mode such as hyp mode. This should allow the kernel to boot safely either from svc mode or hyp mode, even if no support for use of the ARM Virtualization Extensions is built into the kernel. Signed-off-by: Dave Martin <dave.martin@linaro.org> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
352 lines
7.4 KiB
C
352 lines
7.4 KiB
C
/*
|
|
* arch/arm/include/asm/assembler.h
|
|
*
|
|
* Copyright (C) 1996-2000 Russell King
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* 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/domain.h>
|
|
#include <asm/opcodes-virt.h>
|
|
|
|
#define IOMEM(x) (x)
|
|
|
|
/*
|
|
* Endian independent macros for shifting bytes within registers.
|
|
*/
|
|
#ifndef __ARMEB__
|
|
#define pull lsr
|
|
#define push 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 pull lsl
|
|
#define push 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
|
|
|
|
/*
|
|
* 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
|
|
|
|
/*
|
|
* 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
|
|
|
|
.macro asm_trace_hardirqs_off
|
|
#if defined(CONFIG_TRACE_IRQFLAGS)
|
|
stmdb sp!, {r0-r3, ip, lr}
|
|
bl trace_hardirqs_off
|
|
ldmia sp!, {r0-r3, ip, lr}
|
|
#endif
|
|
.endm
|
|
|
|
.macro asm_trace_hardirqs_on_cond, cond
|
|
#if defined(CONFIG_TRACE_IRQFLAGS)
|
|
/*
|
|
* actually the registers should be pushed and pop'd conditionally, but
|
|
* after bl the flags are certainly clobbered
|
|
*/
|
|
stmdb sp!, {r0-r3, ip, lr}
|
|
bl\cond trace_hardirqs_on
|
|
ldmia sp!, {r0-r3, ip, lr}
|
|
#endif
|
|
.endm
|
|
|
|
.macro asm_trace_hardirqs_on
|
|
asm_trace_hardirqs_on_cond al
|
|
.endm
|
|
|
|
.macro disable_irq
|
|
disable_irq_notrace
|
|
asm_trace_hardirqs_off
|
|
.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
|
|
mrs \oldcpsr, cpsr
|
|
disable_irq
|
|
.endm
|
|
|
|
.macro save_and_disable_irqs_notrace, oldcpsr
|
|
mrs \oldcpsr, cpsr
|
|
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
|
|
msr cpsr_c, \oldcpsr
|
|
.endm
|
|
|
|
.macro restore_irqs, oldcpsr
|
|
tst \oldcpsr, #PSR_I_BIT
|
|
asm_trace_hardirqs_on_cond eq
|
|
restore_irqs_notrace \oldcpsr
|
|
.endm
|
|
|
|
#define USER(x...) \
|
|
9999: x; \
|
|
.pushsection __ex_table,"a"; \
|
|
.align 3; \
|
|
.long 9999b,9001f; \
|
|
.popsection
|
|
|
|
#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" ;\
|
|
.long 9998b ;\
|
|
9997: instr ;\
|
|
.if . - 9997b != 4 ;\
|
|
.error "ALT_UP() content must assemble to exactly 4 bytes";\
|
|
.endif ;\
|
|
.popsection
|
|
#define ALT_UP_B(label) \
|
|
.equ up_b_offset, label - 9998b ;\
|
|
.pushsection ".alt.smp.init", "a" ;\
|
|
.long 9998b ;\
|
|
W(b) . + up_b_offset ;\
|
|
.popsection
|
|
#else
|
|
#define ALT_SMP(instr...)
|
|
#define ALT_UP(instr...) instr
|
|
#define ALT_UP_B(label) b label
|
|
#endif
|
|
|
|
/*
|
|
* 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)
|
|
.else
|
|
ALT_SMP(W(dmb))
|
|
.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
|
|
|
|
#ifdef 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.
|
|
*
|
|
* Beware, it also clobers LR.
|
|
*/
|
|
.macro safe_svcmode_maskall reg:req
|
|
mrs \reg , cpsr
|
|
mov lr , \reg
|
|
and lr , lr , #MODE_MASK
|
|
cmp lr , #HYP_MODE
|
|
orr \reg , \reg , #PSR_A_BIT | PSR_I_BIT | PSR_F_BIT
|
|
bic \reg , \reg , #MODE_MASK
|
|
orr \reg , \reg , #SVC_MODE
|
|
THUMB( orr \reg , \reg , #PSR_T_BIT )
|
|
msr spsr_cxsf, \reg
|
|
adr lr, BSYM(2f)
|
|
bne 1f
|
|
__MSR_ELR_HYP(14)
|
|
__ERET
|
|
1: movs pc, lr
|
|
2:
|
|
.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\cond\()b\()\t\().w \reg, [\ptr, #\off]
|
|
.elseif \inc == 4
|
|
\instr\cond\()\t\().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\cond\()b\()\t \reg, [\ptr], #\inc
|
|
.elseif \inc == 4
|
|
\instr\cond\()\t \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
|
|
|
|
#endif /* __ASM_ASSEMBLER_H__ */
|