#define NO_INTS (0xf<<6)
#define NEW_EL NO_INTS | 0x4
adr x0, el1_entry
msr ELR_EL2, x0 // where to branch to when exception completes
ldr x1, =NEW_EL
msr SPSR_EL2, x1 // set the program state for EL1
I select the value 0x4 to indicate "0100 - EL1 with SP_EL0".
I also set the mask to disable all interrupts.
I verify the values in these registers (for one build of the code) as:
x0 : 0000000002000058 x1 : 00000000000003c4
* * Switch from EL2 to EL1 for ARMv8 * @ep: kernel entry point * @flag: The execution state flag for lower exception * level, ES_TO_AARCH64 or ES_TO_AARCH32 * @tmp: temporary register * * For loading 32-bit OS, x1 is machine nr and x2 is ftaddr. * For loading 64-bit OS, x0 is physical address to the FDT blob. * They will be passed to the guest. */ .macro armv8_switch_to_el1_m, ep, flag, tmp, tmp2This is used in two places:
arch/arm/cpu/armv8/transition.S arch/arm/cpu/armv8/fsl-layerscape/spintable.SNote that restricting the search to ".S" files and paths containing "armv8" would also be sensible. But we got there without such measures.
The second call is from __secondary_boot_func(), which I believe is used when secondary
cores get launched.
The first call is from this function:
ENTRY(armv8_switch_to_el1)
switch_el x6, 0f, 1f, 0f
0:
/* x4 is kernel entry point. When running in EL1
* now, jump to the address saved in x4.
*/
br x4
1: armv8_switch_to_el1_m x4, x5, x6, x7
ENDPROC(armv8_switch_to_el1)
The "switch_el" macro used above is a 3 way switch on EL
Only the EL2 case with do the switch to EL1.
One set of calls is in arch/arm/lib/bootm.c
static void switch_to_el1(void)
{
if ((IH_ARCH_DEFAULT == IH_ARCH_ARM64) &&
(images.os.arch == IH_ARCH_ARM))
armv8_switch_to_el1(0, (u64)gd->bd->bi_arch_number,
(u64)images.ft_addr, 0,
(u64)images.ep,
ES_TO_AARCH32);
else
armv8_switch_to_el1((u64)images.ft_addr, 0, 0, 0,
images.ep,
ES_TO_AARCH64);
}
Tom's electronics pages / tom@mmto.org