Commit 4f6ad66a authored by Achin Gupta's avatar Achin Gupta Committed by James Morrissey
Browse files

ARMv8 Trusted Firmware release v0.2

parents
/*
* Copyright (c) 2013, ARM Limited. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of ARM nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <arch.h>
#include <runtime_svc.h>
.globl runtime_exceptions
#include <asm_macros.S>
.section aarch64_code, "ax"; .align 11
.align 7
runtime_exceptions:
/* -----------------------------------------------------
* Current EL with _sp_el0 : 0x0 - 0x180
* -----------------------------------------------------
*/
sync_exception_sp_el0:
exception_entry save_regs
mov x0, #SYNC_EXCEPTION_SP_EL0
mov x1, sp
bl sync_exception_handler
exception_exit restore_regs
eret
.align 7
irq_sp_el0:
exception_entry save_regs
mov x0, #IRQ_SP_EL0
mov x1, sp
bl async_exception_handler
exception_exit restore_regs
eret
.align 7
fiq_sp_el0:
exception_entry save_regs
mov x0, #FIQ_SP_EL0
mov x1, sp
bl async_exception_handler
exception_exit restore_regs
eret
.align 7
serror_sp_el0:
exception_entry save_regs
mov x0, #SERROR_SP_EL0
mov x1, sp
bl async_exception_handler
exception_exit restore_regs
eret
/* -----------------------------------------------------
* Current EL with SPx: 0x200 - 0x380
* -----------------------------------------------------
*/
.align 7
sync_exception_sp_elx:
exception_entry save_regs
mov x0, #SYNC_EXCEPTION_SP_ELX
mov x1, sp
bl sync_exception_handler
exception_exit restore_regs
eret
.align 7
irq_sp_elx:
exception_entry save_regs
mov x0, #IRQ_SP_ELX
mov x1, sp
bl async_exception_handler
exception_exit restore_regs
eret
.align 7
fiq_sp_elx:
exception_entry save_regs
mov x0, #FIQ_SP_ELX
mov x1, sp
bl async_exception_handler
exception_exit restore_regs
eret
.align 7
serror_sp_elx:
exception_entry save_regs
mov x0, #SERROR_SP_ELX
mov x1, sp
bl async_exception_handler
exception_exit restore_regs
eret
/* -----------------------------------------------------
* Lower EL using AArch64 : 0x400 - 0x580
* -----------------------------------------------------
*/
.align 7
sync_exception_aarch64:
exception_entry save_regs
mov x0, #SYNC_EXCEPTION_AARCH64
mov x1, sp
bl sync_exception_handler
exception_exit restore_regs
eret
.align 7
irq_aarch64:
exception_entry save_regs
mov x0, #IRQ_AARCH64
mov x1, sp
bl async_exception_handler
exception_exit restore_regs
eret
.align 7
fiq_aarch64:
exception_entry save_regs
mov x0, #FIQ_AARCH64
mov x1, sp
bl async_exception_handler
exception_exit restore_regs
eret
.align 7
serror_aarch64:
exception_entry save_regs
mov x0, #IRQ_AARCH32
mov x1, sp
bl async_exception_handler
exception_exit restore_regs
eret
/* -----------------------------------------------------
* Lower EL using AArch32 : 0x600 - 0x780
* -----------------------------------------------------
*/
.align 7
sync_exception_aarch32:
exception_entry save_regs
mov x0, #SYNC_EXCEPTION_AARCH32
mov x1, sp
bl sync_exception_handler
exception_exit restore_regs
eret
.align 7
irq_aarch32:
exception_entry save_regs
mov x0, #IRQ_AARCH32
mov x1, sp
bl async_exception_handler
exception_exit restore_regs
eret
.align 7
fiq_aarch32:
exception_entry save_regs
mov x0, #FIQ_AARCH32
mov x1, sp
bl async_exception_handler
exception_exit restore_regs
eret
.align 7
serror_aarch32:
exception_entry save_regs
mov x0, #SERROR_AARCH32
mov x1, sp
bl async_exception_handler
exception_exit restore_regs
eret
.align 7
save_regs:; .type save_regs, %function
sub sp, sp, #0x100
stp x0, x1, [sp, #0x0]
stp x2, x3, [sp, #0x10]
stp x4, x5, [sp, #0x20]
stp x6, x7, [sp, #0x30]
stp x8, x9, [sp, #0x40]
stp x10, x11, [sp, #0x50]
stp x12, x13, [sp, #0x60]
stp x14, x15, [sp, #0x70]
stp x16, x17, [sp, #0x80]
stp x18, x19, [sp, #0x90]
stp x20, x21, [sp, #0xa0]
stp x22, x23, [sp, #0xb0]
stp x24, x25, [sp, #0xc0]
stp x26, x27, [sp, #0xd0]
mrs x0, sp_el0
stp x28, x0, [sp, #0xe0]
mrs x0, spsr_el3
str x0, [sp, #0xf0]
ret
restore_regs:; .type restore_regs, %function
ldr x9, [sp, #0xf0]
msr spsr_el3, x9
ldp x28, x9, [sp, #0xe0]
msr sp_el0, x9
ldp x26, x27, [sp, #0xd0]
ldp x24, x25, [sp, #0xc0]
ldp x22, x23, [sp, #0xb0]
ldp x20, x21, [sp, #0xa0]
ldp x18, x19, [sp, #0x90]
ldp x16, x17, [sp, #0x80]
ldp x14, x15, [sp, #0x70]
ldp x12, x13, [sp, #0x60]
ldp x10, x11, [sp, #0x50]
ldp x8, x9, [sp, #0x40]
ldp x6, x7, [sp, #0x30]
ldp x4, x5, [sp, #0x20]
ldp x2, x3, [sp, #0x10]
ldp x0, x1, [sp, #0x0]
add sp, sp, #0x100
ret
/*
* Copyright (c) 2013, ARM Limited. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of ARM nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <platform.h>
OUTPUT_FORMAT(PLATFORM_LINKER_FORMAT)
OUTPUT_ARCH(PLATFORM_LINKER_ARCH)
MEMORY {
/* RAM is read/write and Initialised */
RAM (rwx): ORIGIN = TZRAM_BASE, LENGTH = TZRAM_SIZE
}
SECTIONS
{
. = BL31_BASE;
BL31_RO ALIGN (4096): {
*(entry_code)
*(.text)
*(.rodata)
} >RAM
BL31_STACKS ALIGN (4096): {
. += 0x1000;
*(tzfw_normal_stacks)
} >RAM
BL31_COHERENT_RAM ALIGN (4096): {
*(tzfw_coherent_mem)
/* . += 0x1000;*/
/* Do we need to ensure at least 4k here? */
. = ALIGN(4096);
} >RAM
__BL31_DATA_START__ = .;
.bss ALIGN (4096): {
*(.bss)
*(COMMON)
} >RAM
.data : {
*(.data)
} >RAM
__BL31_DATA_STOP__ = .;
__BL31_RO_BASE__ = LOADADDR(BL31_RO);
__BL31_RO_SIZE__ = SIZEOF(BL31_RO);
__BL31_STACKS_BASE__ = LOADADDR(BL31_STACKS);
__BL31_STACKS_SIZE__ = SIZEOF(BL31_STACKS);
__BL31_COHERENT_RAM_BASE__ = LOADADDR(BL31_COHERENT_RAM);
__BL31_COHERENT_RAM_SIZE__ = SIZEOF(BL31_COHERENT_RAM);
__BL31_RW_BASE__ = __BL31_DATA_START__;
__BL31_RW_SIZE__ = __BL31_DATA_STOP__ - __BL31_DATA_START__;
}
#
# Copyright (c) 2013, ARM Limited. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# Neither the name of ARM nor the names of its contributors may be used
# to endorse or promote products derived from this software without specific
# prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
vpath %.c drivers/arm/interconnect/cci-400/ common/ lib/ \
drivers/arm/peripherals/pl011 plat/fvp common/psci \
lib/semihosting arch/aarch64/ lib/non-semihosting \
lib/sync/locks/bakery/ drivers/power/ arch/system/gic/ \
plat/fvp/aarch64/
vpath %.S lib/arch/aarch64 common/psci \
lib/semihosting/aarch64 include/ plat/fvp/${ARCH} \
lib/sync/locks/exclusive plat/common/aarch64/ \
arch/system/gic/${ARCH}
BL31_ASM_OBJS := bl31_entrypoint.o runtime_exceptions.o psci_entry.o \
spinlock.o gic_v3_sysregs.o fvp_helpers.o
BL31_C_OBJS := bl31_main.o bl31_plat_setup.o bl31_arch_setup.o \
exception_handlers.o bakery_lock.o cci400.o \
fvp_common.o fvp_pm.o fvp_pwrc.o fvp_topology.o \
runtime_svc.o gic_v3.o gic_v2.o psci_setup.o \
psci_common.o psci_afflvl_on.o psci_main.o \
psci_afflvl_off.o psci_afflvl_suspend.o
BL31_ENTRY_POINT := bl31_entrypoint
BL31_MAPFILE := bl31.map
BL31_LINKERFILE := bl31.ld
BL31_OBJS := $(BL31_C_OBJS) $(BL31_ASM_OBJS)
/*
* Copyright (c) 2013, ARM Limited. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of ARM nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <arch_helpers.h>
#include <console.h>
#include <platform.h>
#include <semihosting.h>
#include <bl_common.h>
#include <bl31.h>
#include <runtime_svc.h>
void bl31_arch_next_el_setup(void);
/*******************************************************************************
* BL31 is responsible for setting up the runtime services for the primary cpu
* before passing control to the bootloader (UEFI) or Linux.
******************************************************************************/
void bl31_main(void)
{
el_change_info *image_info;
unsigned long mpidr = read_mpidr();
/* Perform remaining generic architectural setup from EL3 */
bl31_arch_setup();
/* Perform platform setup in BL1 */
bl31_platform_setup();
#if defined (__GNUC__)
printf("BL31 Built : %s, %s\n\r", __TIME__, __DATE__);
#endif
/* Initialize the runtime services e.g. psci */
runtime_svc_init(mpidr);
/* Clean caches before re-entering normal world */
dcsw_op_all(DCCSW);
image_info = bl31_get_next_image_info(mpidr);
bl31_arch_next_el_setup();
change_el(image_info);
/* There is no valid reason for change_el() to return */
assert(0);
}
/*
* Copyright (c) 2013, ARM Limited. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of ARM nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <arch_helpers.h>
#include <console.h>
#include <platform.h>
#include <semihosting.h>
#include <bl_common.h>
#include <bl1.h>
/***********************************************************
* Memory for sharing data while changing exception levels.
* Only used by the primary core.
**********************************************************/
unsigned char bl2_el_change_mem_ptr[EL_CHANGE_MEM_SIZE];
unsigned long *get_el_change_mem_ptr(void)
{
return (unsigned long *) bl2_el_change_mem_ptr;
}
unsigned long page_align(unsigned long value, unsigned dir)
{
unsigned long page_size = 1 << FOUR_KB_SHIFT;
/* Round up the limit to the next page boundary */
if (value & (page_size - 1)) {
value &= ~(page_size - 1);
if (dir == UP)
value += page_size;
}
return value;
}
static inline unsigned int is_page_aligned (unsigned long addr) {
const unsigned long page_size = 1 << FOUR_KB_SHIFT;
return (addr & (page_size - 1)) == 0;
}
void change_security_state(unsigned int target_security_state)
{
unsigned long scr = read_scr();
if (target_security_state == SECURE)
scr &= ~SCR_NS_BIT;
else if (target_security_state == NON_SECURE)
scr |= SCR_NS_BIT;
else
assert(0);
write_scr(scr);
}
int drop_el(aapcs64_params *args,
unsigned long spsr,
unsigned long entrypoint)
{
write_spsr(spsr);
write_elr(entrypoint);
eret(args->arg0,
args->arg1,
args->arg2,
args->arg3,
args->arg4,
args->arg5,
args->arg6,
args->arg7);
return -EINVAL;
}
long raise_el(aapcs64_params *args)
{
return smc(args->arg0,
args->arg1,
args->arg2,
args->arg3,
args->arg4,
args->arg5,
args->arg6,
args->arg7);
}
/*
* TODO: If we are not EL3 then currently we only issue an SMC.
* Add support for dropping into EL0 etc. Consider adding support
* for switching from S-EL1 to S-EL0/1 etc.
*/
long change_el(el_change_info *info)
{
unsigned long current_el = read_current_el();
if (GET_EL(current_el) == MODE_EL3) {
/*
* We can go anywhere from EL3. So find where.
* TODO: Lots to do if we are going non-secure.
* Flip the NS bit. Restore NS registers etc.
* Just doing the bare minimal for now.
*/
if (info->security_state == NON_SECURE)
change_security_state(info->security_state);
return drop_el(&info->args, info->spsr, info->entrypoint);
} else
return raise_el(&info->args);
}
/* TODO: add a parameter for DAIF. not needed right now */
unsigned long make_spsr(unsigned long target_el,
unsigned long target_sp,
unsigned long target_rw)
{
unsigned long spsr;
/* Disable all exceptions & setup the EL */
spsr = (DAIF_FIQ_BIT | DAIF_IRQ_BIT | DAIF_ABT_BIT | DAIF_DBG_BIT)
<< PSR_DAIF_SHIFT;
spsr |= PSR_MODE(target_rw, target_el, target_sp);
return spsr;
}
/*******************************************************************************
* The next two functions are the weak definitions. Platform specific
* code can override them if it wishes to.
******************************************************************************/
/*******************************************************************************
* Function that takes a memory layout into which BL31 has been either top or
* bottom loaded. Using this information, it populates bl31_mem_layout to tell
* BL31 how much memory it has access to and how much is available for use. It
* does not need the address where BL31 has been loaded as BL31 will reclaim
* all the memory used by BL2.
* TODO: Revisit if this and init_bl2_mem_layout can be replaced by a single
* routine.
******************************************************************************/
void init_bl31_mem_layout(const meminfo *bl2_mem_layout,
meminfo *bl31_mem_layout,
unsigned int load_type)
{
if (load_type == BOT_LOAD) {
/*
* ------------ ^
* | BL2 | |
* |----------| ^ | BL2
* | | | BL2 free | total
* | | | size | size
* |----------| BL2 free base v |
* | BL31 | |
* ------------ BL2 total base v
*/
unsigned long bl31_size;
bl31_mem_layout->free_base = bl2_mem_layout->free_base;
bl31_size = bl2_mem_layout->free_base - bl2_mem_layout->total_base;
bl31_mem_layout->free_size = bl2_mem_layout->total_size - bl31_size;
} else {
/*
* ------------ ^
* | BL31 | |
* |----------| ^ | BL2
* | | | BL2 free | total
* | | | size | size
* |----------| BL2 free base v |
* | BL2 | |
* ------------ BL2 total base v
*/
unsigned long bl2_size;
bl31_mem_layout->free_base = bl2_mem_layout->total_base;
bl2_size = bl2_mem_layout->free_base - bl2_mem_layout->total_base;
bl31_mem_layout->free_size = bl2_mem_layout->free_size + bl2_size;
}
bl31_mem_layout->total_base = bl2_mem_layout->total_base;
bl31_mem_layout->total_size = bl2_mem_layout->total_size;
bl31_mem_layout->attr = load_type;
flush_dcache_range((unsigned long) bl31_mem_layout, sizeof(meminfo));
return;
}
/*******************************************************************************
* Function that takes a memory layout into which BL2 has been either top or
* bottom loaded along with the address where BL2 has been loaded in it. Using
* this information, it populates bl2_mem_layout to tell BL2 how much memory
* it has access to and how much is available for use.
******************************************************************************/
void init_bl2_mem_layout(meminfo *bl1_mem_layout,
meminfo *bl2_mem_layout,
unsigned int load_type,
unsigned long bl2_base)
{
unsigned tmp;
if (load_type == BOT_LOAD) {
bl2_mem_layout->total_base = bl2_base;
tmp = bl1_mem_layout->free_base - bl2_base;
bl2_mem_layout->total_size = bl1_mem_layout->free_size + tmp;
} else {
bl2_mem_layout->total_base = bl1_mem_layout->free_base;
tmp = bl1_mem_layout->total_base + bl1_mem_layout->total_size;
bl2_mem_layout->total_size = tmp - bl1_mem_layout->free_base;
}
bl2_mem_layout->free_base = bl1_mem_layout->free_base;
bl2_mem_layout->free_size = bl1_mem_layout->free_size;
bl2_mem_layout->attr = load_type;
flush_dcache_range((unsigned long) bl2_mem_layout, sizeof(meminfo));
return;
}
static void dump_load_info(unsigned long image_load_addr,
unsigned long image_size,
const meminfo *mem_layout)
{
#if DEBUG
printf("Trying to load image at address 0x%lx, size = 0x%lx\r\n",
image_load_addr, image_size);
printf("Current memory layout:\r\n");
printf(" total region = [0x%lx, 0x%lx]\r\n", mem_layout->total_base,
mem_layout->total_base + mem_layout->total_size);
printf(" free region = [0x%lx, 0x%lx]\r\n", mem_layout->free_base,
mem_layout->free_base + mem_layout->free_size);
#endif
}
/*******************************************************************************
* Generic function to load an image into the trusted RAM using semihosting
* given a name, extents of free memory & whether the image should be loaded at
* the bottom or top of the free memory. It updates the memory layout if the
* load is successful.
******************************************************************************/
unsigned long load_image(meminfo *mem_layout,
const char *image_name,
unsigned int load_type,
unsigned long fixed_addr)
{
unsigned long temp_image_base, image_base;
long offset;
int image_flen;
/* Find the size of the image */
image_flen = semihosting_get_flen(image_name);
if (image_flen < 0) {
printf("ERROR: Cannot access '%s' file (%i).\r\n",
image_name, image_flen);
return 0;
}
/* See if we have enough space */
if (image_flen > mem_layout->free_size) {
printf("ERROR: Cannot load '%s' file: Not enough space.\r\n",
image_name);
dump_load_info(0, image_flen, mem_layout);
return 0;
}
switch (load_type) {
case TOP_LOAD:
/* Load the image in the top of free memory */
temp_image_base = mem_layout->free_base + mem_layout->free_size;
temp_image_base -= image_flen;
/* Page align base address and check whether the image still fits */
image_base = page_align(temp_image_base, DOWN);
assert(image_base <= temp_image_base);
if (image_base < mem_layout->free_base) {
printf("ERROR: Cannot load '%s' file: Not enough space.\r\n",
image_name);
dump_load_info(image_base, image_flen, mem_layout);
return 0;
}
/* Calculate the amount of extra memory used due to alignment */
offset = temp_image_base - image_base;
break;
case BOT_LOAD:
/* Load the BL2 image in the bottom of free memory */
temp_image_base = mem_layout->free_base;
image_base = page_align(temp_image_base, UP);
assert(image_base >= temp_image_base);
/* Page align base address and check whether the image still fits */
if (image_base + image_flen >
mem_layout->free_base + mem_layout->free_size) {
printf("ERROR: Cannot load '%s' file: Not enough space.\r\n",
image_name);
dump_load_info(image_base, image_flen, mem_layout);
return 0;
}
/* Calculate the amount of extra memory used due to alignment */
offset = image_base - temp_image_base;
break;
default:
assert(0);
}
/*
* Some images must be loaded at a fixed address, not a dynamic one.
*
* This has been implemented as a hack on top of the existing dynamic
* loading mechanism, for the time being. If the 'fixed_addr' function
* argument is different from zero, then it will force the load address.
* So we still have this principle of top/bottom loading but the code
* determining the load address is bypassed and the load address is
* forced to the fixed one.
*
* This can result in quite a lot of wasted space because we still use
* 1 sole meminfo structure to represent the extents of free memory,
* where we should use some sort of linked list.
*
* E.g. we want to load BL2 at address 0x04020000, the resulting memory
* layout should look as follows:
* ------------ 0x04040000
* | | <- Free space (1)
* |----------|
* | BL2 |
* |----------| 0x04020000
* | | <- Free space (2)
* |----------|
* | BL1 |
* ------------ 0x04000000
*
* But in the current hacky implementation, we'll need to specify
* whether BL2 is loaded at the top or bottom of the free memory.
* E.g. if BL2 is considered as top-loaded, the meminfo structure
* will give the following view of the memory, hiding the chunk of
* free memory above BL2:
* ------------ 0x04040000
* | |
* | |
* | BL2 |
* |----------| 0x04020000
* | | <- Free space (2)
* |----------|
* | BL1 |
* ------------ 0x04000000
*/
if (fixed_addr != 0) {
/* Load the image at the given address. */
image_base = fixed_addr;
/* Check whether the image fits. */
if ((image_base < mem_layout->free_base) ||
(image_base + image_flen >
mem_layout->free_base + mem_layout->free_size)) {
printf("ERROR: Cannot load '%s' file: Not enough space.\r\n",
image_name);
dump_load_info(image_base, image_flen, mem_layout);
return 0;
}
/* Check whether the fixed load address is page-aligned. */
if (!is_page_aligned(image_base)) {
printf("ERROR: Cannot load '%s' file at unaligned address 0x%lx.\r\n",
image_name, fixed_addr);
return 0;
}
/*
* Calculate the amount of extra memory used due to fixed
* loading.
*/
if (load_type == TOP_LOAD) {
unsigned long max_addr, space_used;
/*
* ------------ max_addr
* | /wasted/ | | offset
* |..........|..............................
* | image | | image_flen
* |----------| fixed_addr
* | |
* | |
* ------------ total_base
*/
max_addr = mem_layout->total_base + mem_layout->total_size;
/*
* Compute the amount of memory used by the image.
* Corresponds to all space above the image load
* address.
*/
space_used = max_addr - fixed_addr;
/*
* Calculate the amount of wasted memory within the
* amount of memory used by the image.
*/
offset = space_used - image_flen;
} else /* BOT_LOAD */
/*
* ------------
* | |
* | |
* |----------|
* | image |
* |..........| fixed_addr
* | /wasted/ | | offset
* ------------ total_base
*/
offset = fixed_addr - mem_layout->total_base;
}
/* We have enough space so load the image now */
image_flen = semihosting_download_file(image_name,
image_flen,
(void *) image_base);
if (image_flen <= 0) {
printf("ERROR: Failed to load '%s' file from semihosting (%i).\r\n",
image_name, image_flen);
return 0;
}
/*
* File has been successfully loaded. Update the free memory
* data structure & flush the contents of the TZRAM so that
* the next EL can see it.
*/
/* Update the memory contents */
flush_dcache_range(image_base, image_flen);
mem_layout->free_size -= image_flen + offset;
/* Update the base of free memory since its moved up */
if (load_type == BOT_LOAD)
mem_layout->free_base += offset + image_flen;
return image_base;
}
/*******************************************************************************
* Run a loaded image from the given entry point. This could result in either
* dropping into a lower exception level or jumping to a higher exception level.
* The only way of doing the latter is through an SMC. In either case, setup the
* parameters for the EL change request correctly.
******************************************************************************/
int run_image(unsigned long entrypoint,
unsigned long spsr,
unsigned long target_security_state,
meminfo *mem_layout,
void *data)
{
el_change_info run_image_info;
unsigned long current_el = read_current_el();
/* Tell next EL what we want done */
run_image_info.args.arg0 = RUN_IMAGE;
run_image_info.entrypoint = entrypoint;
run_image_info.spsr = spsr;
run_image_info.security_state = target_security_state;
run_image_info.next = 0;
/*
* If we are EL3 then only an eret can take us to the desired
* exception level. Else for the time being assume that we have
* to jump to a higher EL and issue an SMC. Contents of argY
* will go into the general purpose register xY e.g. arg0->x0
*/
if (GET_EL(current_el) == MODE_EL3) {
run_image_info.args.arg1 = (unsigned long) mem_layout;
run_image_info.args.arg2 = (unsigned long) data;
} else {
run_image_info.args.arg1 = entrypoint;
run_image_info.args.arg2 = spsr;
run_image_info.args.arg3 = (unsigned long) mem_layout;
run_image_info.args.arg4 = (unsigned long) data;
}
return change_el(&run_image_info);
}
/*
* Copyright (c) 2013, ARM Limited. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of ARM nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <arch_helpers.h>
#include <console.h>
#include <platform.h>
#include <psci.h>
#include <psci_private.h>
typedef int (*afflvl_off_handler)(unsigned long, aff_map_node *);
/*******************************************************************************
* The next three functions implement a handler for each supported affinity
* level which is called when that affinity level is turned off.
******************************************************************************/
static int psci_afflvl0_off(unsigned long mpidr, aff_map_node *cpu_node)
{
unsigned int index, plat_state;
int rc = PSCI_E_SUCCESS;
unsigned long sctlr = read_sctlr();
assert(cpu_node->level == MPIDR_AFFLVL0);
/*
* Generic management: Get the index for clearing any
* lingering re-entry information
*/
index = cpu_node->data;
memset(&psci_ns_entry_info[index], 0, sizeof(psci_ns_entry_info[index]));
/*
* Arch. management. Perform the necessary steps to flush all
* cpu caches.
*
* TODO: This power down sequence varies across cpus so it needs to be
* abstracted out on the basis of the MIDR like in cpu_reset_handler().
* Do the bare minimal for the time being. Fix this before porting to
* Cortex models.
*/
sctlr &= ~SCTLR_C_BIT;
write_sctlr(sctlr);
/*
* CAUTION: This flush to the level of unification makes an assumption
* about the cache hierarchy at affinity level 0 (cpu) in the platform.
* Ideally the platform should tell psci which levels to flush to exit
* coherency.
*/
dcsw_op_louis(DCCISW);
/*
* Plat. management: Perform platform specific actions to turn this
* cpu off e.g. exit cpu coherency, program the power controller etc.
*/
if (psci_plat_pm_ops->affinst_off) {
/* Get the current physical state of this cpu */
plat_state = psci_get_aff_phys_state(cpu_node);
rc = psci_plat_pm_ops->affinst_off(mpidr,
cpu_node->level,
plat_state);
}
/*
* The only error cpu_off can return is E_DENIED. So check if that's
* indeed the case. The caller will simply 'eret' in case of an error.
*/
if (rc != PSCI_E_SUCCESS)
assert(rc == PSCI_E_DENIED);
return rc;
}
static int psci_afflvl1_off(unsigned long mpidr, aff_map_node *cluster_node)
{
int rc = PSCI_E_SUCCESS;
unsigned int plat_state;
/* Sanity check the cluster level */
assert(cluster_node->level == MPIDR_AFFLVL1);
/*
* Keep the physical state of this cluster handy to decide
* what action needs to be taken
*/
plat_state = psci_get_aff_phys_state(cluster_node);
/*
* Arch. Management. Flush all levels of caches to PoC if
* the cluster is to be shutdown
*/
if (plat_state == PSCI_STATE_OFF)
dcsw_op_all(DCCISW);
/*
* Plat. Management. Allow the platform to do it's cluster
* specific bookeeping e.g. turn off interconnect coherency,
* program the power controller etc.
*/
if (psci_plat_pm_ops->affinst_off)
rc = psci_plat_pm_ops->affinst_off(mpidr,
cluster_node->level,
plat_state);
return rc;
}
static int psci_afflvl2_off(unsigned long mpidr, aff_map_node *system_node)
{
int rc = PSCI_E_SUCCESS;
unsigned int plat_state;
/* Cannot go beyond this level */
assert(system_node->level == MPIDR_AFFLVL2);
/*
* Keep the physical state of the system handy to decide what
* action needs to be taken
*/
plat_state = psci_get_aff_phys_state(system_node);
/* No arch. and generic bookeeping to do here currently */
/*
* Plat. Management : Allow the platform to do it's bookeeping
* at this affinity level
*/
if (psci_plat_pm_ops->affinst_off)
rc = psci_plat_pm_ops->affinst_off(mpidr,
system_node->level,
plat_state);
return rc;
}
static const afflvl_off_handler psci_afflvl_off_handlers[] = {
psci_afflvl0_off,
psci_afflvl1_off,
psci_afflvl2_off,
};
/*******************************************************************************
* This function implements the core of the processing required to turn a cpu
* off. It's assumed that along with turning the cpu off, higher affinity levels
* will be turned off as far as possible. We first need to determine the new
* state off all the affinity instances in the mpidr corresponding to the target
* cpu. Action will be taken on the basis of this new state. To do the state
* change we first need to acquire the locks for all the implemented affinity
* level to be able to snapshot the system state. Then we need to start turning
* affinity levels off from the lowest to the highest (e.g. a cpu needs to be
* off before a cluster can be turned off). To achieve this flow, we start
* acquiring the locks from the highest to the lowest affinity level. Once we
* reach affinity level 0, we do the state change followed by the actions
* corresponding to the new state for affinity level 0. Actions as per the
* updated state for higher affinity levels are performed as we unwind back to
* highest affinity level.
******************************************************************************/
int psci_afflvl_off(unsigned long mpidr,
int cur_afflvl,
int tgt_afflvl)
{
int rc = PSCI_E_SUCCESS, level;
unsigned int next_state, prev_state;
aff_map_node *aff_node;
mpidr &= MPIDR_AFFINITY_MASK;;
/*
* Some affinity instances at levels between the current and
* target levels could be absent in the mpidr. Skip them and
* start from the first present instance.
*/
level = psci_get_first_present_afflvl(mpidr,
cur_afflvl,
tgt_afflvl,
&aff_node);
/*
* Return if there are no more affinity instances beyond this
* level to process. Else ensure that the returned affinity
* node makes sense.
*/
if (aff_node == NULL)
return rc;
assert(level == aff_node->level);
/*
* This function acquires the lock corresponding to each
* affinity level so that state management can be done safely.
*/
bakery_lock_get(mpidr, &aff_node->lock);
/* Keep the old state and the next one handy */
prev_state = psci_get_state(aff_node->state);
next_state = PSCI_STATE_OFF;
/*
* We start from the highest affinity level and work our way
* downwards to the lowest i.e. MPIDR_AFFLVL0.
*/
if (aff_node->level == tgt_afflvl) {
psci_change_state(mpidr,
tgt_afflvl,
get_max_afflvl(),
next_state);
} else {
rc = psci_afflvl_off(mpidr, level - 1, tgt_afflvl);
if (rc != PSCI_E_SUCCESS) {
psci_set_state(aff_node->state, prev_state);
goto exit;
}
}
/*
* Perform generic, architecture and platform specific
* handling
*/
rc = psci_afflvl_off_handlers[level](mpidr, aff_node);
if (rc != PSCI_E_SUCCESS) {
psci_set_state(aff_node->state, prev_state);
goto exit;
}
/*
* If all has gone as per plan then this cpu should be
* marked as OFF
*/
if (level == MPIDR_AFFLVL0) {
next_state = psci_get_state(aff_node->state);
assert(next_state == PSCI_STATE_OFF);
}
exit:
bakery_lock_release(mpidr, &aff_node->lock);
return rc;
}
/*
* Copyright (c) 2013, ARM Limited. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of ARM nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <arch_helpers.h>
#include <console.h>
#include <platform.h>
#include <psci.h>
#include <psci_private.h>
typedef int (*afflvl_on_handler)(unsigned long,
aff_map_node *,
unsigned long,
unsigned long);
/*******************************************************************************
* This function checks whether a cpu which has been requested to be turned on
* is OFF to begin with.
******************************************************************************/
static int cpu_on_validate_state(unsigned int state)
{
unsigned int psci_state;
/* Get the raw psci state */
psci_state = psci_get_state(state);
if (psci_state == PSCI_STATE_ON || psci_state == PSCI_STATE_SUSPEND)
return PSCI_E_ALREADY_ON;
if (psci_state == PSCI_STATE_ON_PENDING)
return PSCI_E_ON_PENDING;
assert(psci_state == PSCI_STATE_OFF);
return PSCI_E_SUCCESS;
}
/*******************************************************************************
* Handler routine to turn a cpu on. It takes care of any generic, architectural
* or platform specific setup required.
* TODO: Split this code across separate handlers for each type of setup?
******************************************************************************/
static int psci_afflvl0_on(unsigned long target_cpu,
aff_map_node *cpu_node,
unsigned long ns_entrypoint,
unsigned long context_id)
{
unsigned int index, plat_state;
unsigned long psci_entrypoint;
int rc;
/* Sanity check to safeguard against data corruption */
assert(cpu_node->level == MPIDR_AFFLVL0);
/*
* Generic management: Ensure that the cpu is off to be
* turned on
*/
rc = cpu_on_validate_state(cpu_node->state);
if (rc != PSCI_E_SUCCESS)
return rc;
/*
* Arch. management: Derive the re-entry information for
* the non-secure world from the non-secure state from
* where this call originated.
*/
index = cpu_node->data;
rc = psci_set_ns_entry_info(index, ns_entrypoint, context_id);
if (rc != PSCI_E_SUCCESS)
return rc;
/* Set the secure world (EL3) re-entry point after BL1 */
psci_entrypoint = (unsigned long) psci_aff_on_finish_entry;
/*
* Plat. management: Give the platform the current state
* of the target cpu to allow it to perform the necessary
* steps to power on.
*/
if (psci_plat_pm_ops->affinst_on) {
/* Get the current physical state of this cpu */
plat_state = psci_get_aff_phys_state(cpu_node);
rc = psci_plat_pm_ops->affinst_on(target_cpu,
psci_entrypoint,
ns_entrypoint,
cpu_node->level,
plat_state);
}
return rc;
}
/*******************************************************************************
* Handler routine to turn a cluster on. It takes care or any generic, arch.
* or platform specific setup required.
* TODO: Split this code across separate handlers for each type of setup?
******************************************************************************/
static int psci_afflvl1_on(unsigned long target_cpu,
aff_map_node *cluster_node,
unsigned long ns_entrypoint,
unsigned long context_id)
{
int rc = PSCI_E_SUCCESS;
unsigned int plat_state;
unsigned long psci_entrypoint;
assert(cluster_node->level == MPIDR_AFFLVL1);
/*
* There is no generic and arch. specific cluster
* management required
*/
/*
* Plat. management: Give the platform the current state
* of the target cpu to allow it to perform the necessary
* steps to power on.
*/
if (psci_plat_pm_ops->affinst_on) {
plat_state = psci_get_aff_phys_state(cluster_node);
psci_entrypoint = (unsigned long) psci_aff_on_finish_entry;
rc = psci_plat_pm_ops->affinst_on(target_cpu,
psci_entrypoint,
ns_entrypoint,
cluster_node->level,
plat_state);
}
return rc;
}
/*******************************************************************************
* Handler routine to turn a cluster of clusters on. It takes care or any
* generic, arch. or platform specific setup required.
* TODO: Split this code across separate handlers for each type of setup?
******************************************************************************/
static int psci_afflvl2_on(unsigned long target_cpu,
aff_map_node *system_node,
unsigned long ns_entrypoint,
unsigned long context_id)
{
int rc = PSCI_E_SUCCESS;
unsigned int plat_state;
unsigned long psci_entrypoint;
/* Cannot go beyond affinity level 2 in this psci imp. */
assert(system_node->level == MPIDR_AFFLVL2);
/*
* There is no generic and arch. specific system management
* required
*/
/*
* Plat. management: Give the platform the current state
* of the target cpu to allow it to perform the necessary
* steps to power on.
*/
if (psci_plat_pm_ops->affinst_on) {
plat_state = psci_get_aff_phys_state(system_node);
psci_entrypoint = (unsigned long) psci_aff_on_finish_entry;
rc = psci_plat_pm_ops->affinst_on(target_cpu,
psci_entrypoint,
ns_entrypoint,
system_node->level,
plat_state);
}
return rc;
}
/* Private data structure to make this handlers accessible through indexing */
static const afflvl_on_handler psci_afflvl_on_handlers[] = {
psci_afflvl0_on,
psci_afflvl1_on,
psci_afflvl2_on,
};
/*******************************************************************************
* This function implements the core of the processing required to turn a cpu
* on. It avoids recursion to traverse from the lowest to the highest affinity
* level unlike the off/suspend/pon_finisher functions. It does ensure that the
* locks are picked in the same order as the order routines to avoid deadlocks.
* The flow is: Take all the locks until the highest affinity level, Call the
* handlers for turning an affinity level on & finally change the state of the
* affinity level.
******************************************************************************/
int psci_afflvl_on(unsigned long target_cpu,
unsigned long entrypoint,
unsigned long context_id,
int current_afflvl,
int target_afflvl)
{
unsigned int prev_state, next_state;
int rc = PSCI_E_SUCCESS, level;
aff_map_node *aff_node;
unsigned long mpidr = read_mpidr() & MPIDR_AFFINITY_MASK;
/*
* This loop acquires the lock corresponding to each
* affinity level so that by the time we hit the lowest
* affinity level, the system topology is snapshot and
* state management can be done safely.
*/
for (level = current_afflvl; level >= target_afflvl; level--) {
aff_node = psci_get_aff_map_node(target_cpu, level);
if (aff_node)
bakery_lock_get(mpidr, &aff_node->lock);
}
/*
* Perform generic, architecture and platform specific
* handling
*/
for (level = current_afflvl; level >= target_afflvl; level--) {
/* Grab the node for each affinity level once again */
aff_node = psci_get_aff_map_node(target_cpu, level);
if (aff_node) {
/* Keep the old state and the next one handy */
prev_state = psci_get_state(aff_node->state);
rc = psci_afflvl_on_handlers[level](target_cpu,
aff_node,
entrypoint,
context_id);
if (rc != PSCI_E_SUCCESS) {
psci_set_state(aff_node->state, prev_state);
goto exit;
}
}
}
/*
* State management: Update the states since this is the
* target affinity level requested.
*/
psci_change_state(target_cpu,
target_afflvl,
get_max_afflvl(),
PSCI_STATE_ON_PENDING);
exit:
/*
* This loop releases the lock corresponding to each affinity level
* in the reverse order. It also checks the final state of the cpu.
*/
for (level = target_afflvl; level <= current_afflvl; level++) {
aff_node = psci_get_aff_map_node(target_cpu, level);
if (aff_node) {
if (level == MPIDR_AFFLVL0) {
next_state = psci_get_state(aff_node->state);
assert(next_state == PSCI_STATE_ON_PENDING);
}
bakery_lock_release(mpidr, &aff_node->lock);
}
}
return rc;
}
/*******************************************************************************
* The following functions finish an earlier affinity power on request. They
* are called by the common finisher routine in psci_common.c.
******************************************************************************/
static unsigned int psci_afflvl0_on_finish(unsigned long mpidr,
aff_map_node *cpu_node,
unsigned int prev_state)
{
unsigned int index, plat_state, rc = PSCI_E_SUCCESS;
assert(cpu_node->level == MPIDR_AFFLVL0);
/*
* Plat. management: Perform the platform specific actions
* for this cpu e.g. enabling the gic or zeroing the mailbox
* register. The actual state of this cpu has already been
* changed.
*/
if (psci_plat_pm_ops->affinst_on_finish) {
/* Get the previous physical state of this cpu */
plat_state = psci_get_phys_state(prev_state);
rc = psci_plat_pm_ops->affinst_on_finish(mpidr,
cpu_node->level,
plat_state);
assert(rc == PSCI_E_SUCCESS);
}
/*
* Arch. management: Turn on mmu & restore architectural state
*/
write_vbar((unsigned long) runtime_exceptions);
enable_mmu();
/*
* All the platform specific actions for turning this cpu
* on have completed. Perform enough arch.initialization
* to run in the non-secure address space.
*/
bl31_arch_setup();
/*
* Generic management: Now we just need to retrieve the
* information that we had stashed away during the cpu_on
* call to set this cpu on it's way. First get the index
* for restoring the re-entry info
*/
index = cpu_node->data;
rc = psci_get_ns_entry_info(index);
/* Clean caches before re-entering normal world */
dcsw_op_louis(DCCSW);
return rc;
}
static unsigned int psci_afflvl1_on_finish(unsigned long mpidr,
aff_map_node *cluster_node,
unsigned int prev_state)
{
unsigned int rc = PSCI_E_SUCCESS;
unsigned int plat_state;
assert(cluster_node->level == MPIDR_AFFLVL1);
/*
* Plat. management: Perform the platform specific actions
* as per the old state of the cluster e.g. enabling
* coherency at the interconnect depends upon the state with
* which this cluster was powered up. If anything goes wrong
* then assert as there is no way to recover from this
* situation.
*/
if (psci_plat_pm_ops->affinst_on_finish) {
plat_state = psci_get_phys_state(prev_state);
rc = psci_plat_pm_ops->affinst_on_finish(mpidr,
cluster_node->level,
plat_state);
assert(rc == PSCI_E_SUCCESS);
}
return rc;
}
static unsigned int psci_afflvl2_on_finish(unsigned long mpidr,
aff_map_node *system_node,
unsigned int prev_state)
{
int rc = PSCI_E_SUCCESS;
unsigned int plat_state;
/* Cannot go beyond this affinity level */
assert(system_node->level == MPIDR_AFFLVL2);
/*
* Currently, there are no architectural actions to perform
* at the system level.
*/
/*
* Plat. management: Perform the platform specific actions
* as per the old state of the cluster e.g. enabling
* coherency at the interconnect depends upon the state with
* which this cluster was powered up. If anything goes wrong
* then assert as there is no way to recover from this
* situation.
*/
if (psci_plat_pm_ops->affinst_on_finish) {
plat_state = psci_get_phys_state(system_node->state);
rc = psci_plat_pm_ops->affinst_on_finish(mpidr,
system_node->level,
plat_state);
assert(rc == PSCI_E_SUCCESS);
}
return rc;
}
const afflvl_power_on_finisher psci_afflvl_on_finishers[] = {
psci_afflvl0_on_finish,
psci_afflvl1_on_finish,
psci_afflvl2_on_finish,
};
/*
* Copyright (c) 2013, ARM Limited. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of ARM nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <arch_helpers.h>
#include <console.h>
#include <platform.h>
#include <psci.h>
#include <psci_private.h>
typedef int (*afflvl_suspend_handler)(unsigned long,
aff_map_node *,
unsigned long,
unsigned long,
unsigned int);
/*******************************************************************************
* The next three functions implement a handler for each supported affinity
* level which is called when that affinity level is about to be suspended.
******************************************************************************/
static int psci_afflvl0_suspend(unsigned long mpidr,
aff_map_node *cpu_node,
unsigned long ns_entrypoint,
unsigned long context_id,
unsigned int power_state)
{
unsigned int index, plat_state;
unsigned long psci_entrypoint, sctlr = read_sctlr();
int rc = PSCI_E_SUCCESS;
/* Sanity check to safeguard against data corruption */
assert(cpu_node->level == MPIDR_AFFLVL0);
/*
* Generic management: Store the re-entry information for the
* non-secure world
*/
index = cpu_node->data;
rc = psci_set_ns_entry_info(index, ns_entrypoint, context_id);
if (rc != PSCI_E_SUCCESS)
return rc;
/*
* Arch. management: Save the secure context, flush the
* L1 caches and exit intra-cluster coherency et al
*/
psci_secure_context[index].sctlr = read_sctlr();
psci_secure_context[index].scr = read_scr();
psci_secure_context[index].cptr = read_cptr();
psci_secure_context[index].cpacr = read_cpacr();
psci_secure_context[index].cntfrq = read_cntfrq_el0();
psci_secure_context[index].mair = read_mair();
psci_secure_context[index].tcr = read_tcr();
psci_secure_context[index].ttbr = read_ttbr0();
psci_secure_context[index].vbar = read_vbar();
/* Set the secure world (EL3) re-entry point after BL1 */
psci_entrypoint = (unsigned long) psci_aff_suspend_finish_entry;
/*
* Arch. management. Perform the necessary steps to flush all
* cpu caches.
*
* TODO: This power down sequence varies across cpus so it needs to be
* abstracted out on the basis of the MIDR like in cpu_reset_handler().
* Do the bare minimal for the time being. Fix this before porting to
* Cortex models.
*/
sctlr &= ~SCTLR_C_BIT;
write_sctlr(sctlr);
/*
* CAUTION: This flush to the level of unification makes an assumption
* about the cache hierarchy at affinity level 0 (cpu) in the platform.
* Ideally the platform should tell psci which levels to flush to exit
* coherency.
*/
dcsw_op_louis(DCCISW);
/*
* Plat. management: Allow the platform to perform the
* necessary actions to turn off this cpu e.g. set the
* platform defined mailbox with the psci entrypoint,
* program the power controller etc.
*/
if (psci_plat_pm_ops->affinst_suspend) {
plat_state = psci_get_aff_phys_state(cpu_node);
rc = psci_plat_pm_ops->affinst_suspend(mpidr,
psci_entrypoint,
ns_entrypoint,
cpu_node->level,
plat_state);
}
return rc;
}
static int psci_afflvl1_suspend(unsigned long mpidr,
aff_map_node *cluster_node,
unsigned long ns_entrypoint,
unsigned long context_id,
unsigned int power_state)
{
int rc = PSCI_E_SUCCESS;
unsigned int plat_state;
unsigned long psci_entrypoint;
/* Sanity check the cluster level */
assert(cluster_node->level == MPIDR_AFFLVL1);
/*
* Keep the physical state of this cluster handy to decide
* what action needs to be taken
*/
plat_state = psci_get_aff_phys_state(cluster_node);
/*
* Arch. management: Flush all levels of caches to PoC if the
* cluster is to be shutdown
*/
if (plat_state == PSCI_STATE_OFF)
dcsw_op_all(DCCISW);
/*
* Plat. Management. Allow the platform to do it's cluster
* specific bookeeping e.g. turn off interconnect coherency,
* program the power controller etc.
*/
if (psci_plat_pm_ops->affinst_suspend) {
/*
* Sending the psci entrypoint is currently redundant
* beyond affinity level 0 but one never knows what a
* platform might do. Also it allows us to keep the
* platform handler prototype the same.
*/
psci_entrypoint = (unsigned long) psci_aff_suspend_finish_entry;
rc = psci_plat_pm_ops->affinst_suspend(mpidr,
psci_entrypoint,
ns_entrypoint,
cluster_node->level,
plat_state);
}
return rc;
}
static int psci_afflvl2_suspend(unsigned long mpidr,
aff_map_node *system_node,
unsigned long ns_entrypoint,
unsigned long context_id,
unsigned int power_state)
{
int rc = PSCI_E_SUCCESS;
unsigned int plat_state;
unsigned long psci_entrypoint;
/* Cannot go beyond this */
assert(system_node->level == MPIDR_AFFLVL2);
/*
* Keep the physical state of the system handy to decide what
* action needs to be taken
*/
plat_state = psci_get_aff_phys_state(system_node);
/*
* Plat. Management : Allow the platform to do it's bookeeping
* at this affinity level
*/
if (psci_plat_pm_ops->affinst_suspend) {
/*
* Sending the psci entrypoint is currently redundant
* beyond affinity level 0 but one never knows what a
* platform might do. Also it allows us to keep the
* platform handler prototype the same.
*/
psci_entrypoint = (unsigned long) psci_aff_suspend_finish_entry;
rc = psci_plat_pm_ops->affinst_suspend(mpidr,
psci_entrypoint,
ns_entrypoint,
system_node->level,
plat_state);
}
return rc;
}
static const afflvl_suspend_handler psci_afflvl_suspend_handlers[] = {
psci_afflvl0_suspend,
psci_afflvl1_suspend,
psci_afflvl2_suspend,
};
/*******************************************************************************
* This function implements the core of the processing required to suspend a cpu
* It'S assumed that along with suspending the cpu, higher affinity levels will
* be suspended as far as possible. Suspending a cpu is equivalent to physically
* powering it down, but the cpu is still available to the OS for scheduling.
* We first need to determine the new state off all the affinity instances in
* the mpidr corresponding to the target cpu. Action will be taken on the basis
* of this new state. To do the state change we first need to acquire the locks
* for all the implemented affinity level to be able to snapshot the system
* state. Then we need to start suspending affinity levels from the lowest to
* the highest (e.g. a cpu needs to be suspended before a cluster can be). To
* achieve this flow, we start acquiring the locks from the highest to the
* lowest affinity level. Once we reach affinity level 0, we do the state change
* followed by the actions corresponding to the new state for affinity level 0.
* Actions as per the updated state for higher affinity levels are performed as
* we unwind back to highest affinity level.
******************************************************************************/
int psci_afflvl_suspend(unsigned long mpidr,
unsigned long entrypoint,
unsigned long context_id,
unsigned int power_state,
int cur_afflvl,
int tgt_afflvl)
{
int rc = PSCI_E_SUCCESS, level;
unsigned int prev_state, next_state;
aff_map_node *aff_node;
mpidr &= MPIDR_AFFINITY_MASK;
/*
* Some affinity instances at levels between the current and
* target levels could be absent in the mpidr. Skip them and
* start from the first present instance.
*/
level = psci_get_first_present_afflvl(mpidr,
cur_afflvl,
tgt_afflvl,
&aff_node);
/*
* Return if there are no more affinity instances beyond this
* level to process. Else ensure that the returned affinity
* node makes sense.
*/
if (aff_node == NULL)
return rc;
assert(level == aff_node->level);
/*
* This function acquires the lock corresponding to each
* affinity level so that state management can be done safely.
*/
bakery_lock_get(mpidr, &aff_node->lock);
/* Keep the old state and the next one handy */
prev_state = psci_get_state(aff_node->state);
next_state = PSCI_STATE_SUSPEND;
/*
* We start from the highest affinity level and work our way
* downwards to the lowest i.e. MPIDR_AFFLVL0.
*/
if (aff_node->level == tgt_afflvl) {
psci_change_state(mpidr,
tgt_afflvl,
get_max_afflvl(),
next_state);
} else {
rc = psci_afflvl_suspend(mpidr,
entrypoint,
context_id,
power_state,
level - 1,
tgt_afflvl);
if (rc != PSCI_E_SUCCESS) {
psci_set_state(aff_node->state, prev_state);
goto exit;
}
}
/*
* Perform generic, architecture and platform specific
* handling
*/
rc = psci_afflvl_suspend_handlers[level](mpidr,
aff_node,
entrypoint,
context_id,
power_state);
if (rc != PSCI_E_SUCCESS) {
psci_set_state(aff_node->state, prev_state);
goto exit;
}
/*
* If all has gone as per plan then this cpu should be
* marked as OFF
*/
if (level == MPIDR_AFFLVL0) {
next_state = psci_get_state(aff_node->state);
assert(next_state == PSCI_STATE_SUSPEND);
}
exit:
bakery_lock_release(mpidr, &aff_node->lock);
return rc;
}
/*******************************************************************************
* The following functions finish an earlier affinity suspend request. They
* are called by the common finisher routine in psci_common.c.
******************************************************************************/
static unsigned int psci_afflvl0_suspend_finish(unsigned long mpidr,
aff_map_node *cpu_node,
unsigned int prev_state)
{
unsigned int index, plat_state, rc = 0;
assert(cpu_node->level == MPIDR_AFFLVL0);
/*
* Plat. management: Perform the platform specific actions
* before we change the state of the cpu e.g. enabling the
* gic or zeroing the mailbox register. If anything goes
* wrong then assert as there is no way to recover from this
* situation.
*/
if (psci_plat_pm_ops->affinst_suspend_finish) {
plat_state = psci_get_phys_state(prev_state);
rc = psci_plat_pm_ops->affinst_suspend_finish(mpidr,
cpu_node->level,
plat_state);
assert(rc == PSCI_E_SUCCESS);
}
/* Get the index for restoring the re-entry information */
index = cpu_node->data;
/*
* Arch. management: Restore the stashed secure architectural
* context in the right order.
*/
write_vbar(psci_secure_context[index].vbar);
write_mair(psci_secure_context[index].mair);
write_tcr(psci_secure_context[index].tcr);
write_ttbr0(psci_secure_context[index].ttbr);
write_sctlr(psci_secure_context[index].sctlr);
/* MMU and coherency should be enabled by now */
write_scr(psci_secure_context[index].scr);
write_cptr(psci_secure_context[index].cptr);
write_cpacr(psci_secure_context[index].cpacr);
write_cntfrq_el0(psci_secure_context[index].cntfrq);
/*
* Generic management: Now we just need to retrieve the
* information that we had stashed away during the suspend
* call to set this cpu on it's way.
*/
rc = psci_get_ns_entry_info(index);
/* Clean caches before re-entering normal world */
dcsw_op_louis(DCCSW);
return rc;
}
static unsigned int psci_afflvl1_suspend_finish(unsigned long mpidr,
aff_map_node *cluster_node,
unsigned int prev_state)
{
unsigned int rc = 0;
unsigned int plat_state;
assert(cluster_node->level == MPIDR_AFFLVL1);
/*
* Plat. management: Perform the platform specific actions
* as per the old state of the cluster e.g. enabling
* coherency at the interconnect depends upon the state with
* which this cluster was powered up. If anything goes wrong
* then assert as there is no way to recover from this
* situation.
*/
if (psci_plat_pm_ops->affinst_suspend_finish) {
plat_state = psci_get_phys_state(prev_state);
rc = psci_plat_pm_ops->affinst_suspend_finish(mpidr,
cluster_node->level,
plat_state);
assert(rc == PSCI_E_SUCCESS);
}
return rc;
}
static unsigned int psci_afflvl2_suspend_finish(unsigned long mpidr,
aff_map_node *system_node,
unsigned int target_afflvl)
{
int rc = PSCI_E_SUCCESS;
unsigned int plat_state;
/* Cannot go beyond this affinity level */
assert(system_node->level == MPIDR_AFFLVL2);
/*
* Currently, there are no architectural actions to perform
* at the system level.
*/
/*
* Plat. management: Perform the platform specific actions
* as per the old state of the cluster e.g. enabling
* coherency at the interconnect depends upon the state with
* which this cluster was powered up. If anything goes wrong
* then assert as there is no way to recover from this
* situation.
*/
if (psci_plat_pm_ops->affinst_suspend_finish) {
plat_state = psci_get_phys_state(system_node->state);
rc = psci_plat_pm_ops->affinst_suspend_finish(mpidr,
system_node->level,
plat_state);
assert(rc == PSCI_E_SUCCESS);
}
return rc;
}
const afflvl_power_on_finisher psci_afflvl_suspend_finishers[] = {
psci_afflvl0_suspend_finish,
psci_afflvl1_suspend_finish,
psci_afflvl2_suspend_finish,
};
/*
* Copyright (c) 2013, ARM Limited. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of ARM nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <arch_helpers.h>
#include <console.h>
#include <platform.h>
#include <psci.h>
#include <psci_private.h>
/*******************************************************************************
* Arrays that contains information needs to resume a cpu's execution when woken
* out of suspend or off states. 'psci_ns_einfo_idx' keeps track of the next
* free index in the 'psci_ns_entry_info' & 'psci_secure_context' arrays. Each
* cpu is allocated a single entry in each array during startup.
******************************************************************************/
secure_context psci_secure_context[PSCI_NUM_AFFS];
ns_entry_info psci_ns_entry_info[PSCI_NUM_AFFS];
unsigned int psci_ns_einfo_idx;
/*******************************************************************************
* Grand array that holds the platform's topology information for state
* management of affinity instances. Each node (aff_map_node) in the array
* corresponds to an affinity instance e.g. cluster, cpu within an mpidr
******************************************************************************/
aff_map_node psci_aff_map[PSCI_NUM_AFFS]
__attribute__ ((section("tzfw_coherent_mem")));
/*******************************************************************************
* In a system, a certain number of affinity instances are present at an
* affinity level. The cumulative number of instances across all levels are
* stored in 'psci_aff_map'. The topology tree has been flattenned into this
* array. To retrieve nodes, information about the extents of each affinity
* level i.e. start index and end index needs to be present. 'psci_aff_limits'
* stores this information.
******************************************************************************/
aff_limits_node psci_aff_limits[MPIDR_MAX_AFFLVL + 1];
/*******************************************************************************
* Pointer to functions exported by the platform to complete power mgmt. ops
******************************************************************************/
plat_pm_ops *psci_plat_pm_ops;
/*******************************************************************************
* Simple routine to retrieve the maximum affinity level supported by the
* platform and check that it makes sense.
******************************************************************************/
int get_max_afflvl()
{
int aff_lvl;
aff_lvl = plat_get_max_afflvl();
assert(aff_lvl <= MPIDR_MAX_AFFLVL && aff_lvl >= MPIDR_AFFLVL0);
return aff_lvl;
}
/*******************************************************************************
* Simple routine to set the id of an affinity instance at a given level in the
* mpidr.
******************************************************************************/
unsigned long mpidr_set_aff_inst(unsigned long mpidr,
unsigned char aff_inst,
int aff_lvl)
{
unsigned long aff_shift;
assert(aff_lvl <= MPIDR_AFFLVL3);
/*
* Decide the number of bits to shift by depending upon
* the affinity level
*/
aff_shift = get_afflvl_shift(aff_lvl);
/* Clear the existing affinity instance & set the new one*/
mpidr &= ~(MPIDR_AFFLVL_MASK << aff_shift);
mpidr |= aff_inst << aff_shift;
return mpidr;
}
/*******************************************************************************
* Simple routine to determine whether an affinity instance at a given level
* in an mpidr exists or not.
******************************************************************************/
int psci_validate_mpidr(unsigned long mpidr, int level)
{
aff_map_node *node;
node = psci_get_aff_map_node(mpidr, level);
if (node && (node->state & PSCI_AFF_PRESENT))
return PSCI_E_SUCCESS;
else
return PSCI_E_INVALID_PARAMS;
}
/*******************************************************************************
* Simple routine to determine the first affinity level instance that is present
* between the start and end affinity levels. This helps to skip handling of
* absent affinity levels while performing psci operations.
* The start level can be > or <= to the end level depending upon whether this
* routine is expected to search top down or bottom up.
******************************************************************************/
int psci_get_first_present_afflvl(unsigned long mpidr,
int start_afflvl,
int end_afflvl,
aff_map_node **node)
{
int level;
/* Check whether we have to search up or down */
if (start_afflvl <= end_afflvl) {
for (level = start_afflvl; level <= end_afflvl; level++) {
*node = psci_get_aff_map_node(mpidr, level);
if (*node && ((*node)->state & PSCI_AFF_PRESENT))
break;
}
} else {
for (level = start_afflvl; level >= end_afflvl; level--) {
*node = psci_get_aff_map_node(mpidr, level);
if (*node && ((*node)->state & PSCI_AFF_PRESENT))
break;
}
}
return level;
}
/*******************************************************************************
* Recursively change the affinity state between the current and target affinity
* levels. The target state matters only if we are starting from affinity level
* 0 i.e. a cpu otherwise the state depends upon the state of the lower affinity
* levels.
******************************************************************************/
int psci_change_state(unsigned long mpidr,
int cur_afflvl,
int tgt_afflvl,
unsigned int tgt_state)
{
int rc = PSCI_E_SUCCESS;
unsigned int state;
aff_map_node *aff_node;
/* Sanity check the affinity levels */
assert(tgt_afflvl >= cur_afflvl);
aff_node = psci_get_aff_map_node(mpidr, cur_afflvl);
assert(aff_node);
/* TODO: Check whether the affinity level is present or absent*/
if (cur_afflvl == MPIDR_AFFLVL0) {
psci_set_state(aff_node->state, tgt_state);
} else {
state = psci_calculate_affinity_state(aff_node);
psci_set_state(aff_node->state, state);
}
if (cur_afflvl != tgt_afflvl)
psci_change_state(mpidr, cur_afflvl + 1, tgt_afflvl, tgt_state);
return rc;
}
/*******************************************************************************
* This routine does the heavy lifting for psci_change_state(). It examines the
* state of each affinity instance at the next lower affinity level and decides
* it's final state accordingly. If a lower affinity instance is ON then the
* higher affinity instance is ON. If all the lower affinity instances are OFF
* then the higher affinity instance is OFF. If atleast one lower affinity
* instance is SUSPENDED then the higher affinity instance is SUSPENDED. If only
* a single lower affinity instance is ON_PENDING then the higher affinity
* instance in ON_PENDING as well.
******************************************************************************/
unsigned int psci_calculate_affinity_state(aff_map_node *aff_node)
{
int ctr;
unsigned int aff_count, hi_aff_state;
unsigned long tempidr;
aff_map_node *lo_aff_node;
/* Cannot calculate lowest affinity state. It's simply assigned */
assert(aff_node->level > MPIDR_AFFLVL0);
/*
* Find the number of affinity instances at level X-1 e.g. number of
* cpus in a cluster. The level X state depends upon the state of each
* instance at level X-1
*/
hi_aff_state = PSCI_STATE_OFF;
aff_count = plat_get_aff_count(aff_node->level - 1, aff_node->mpidr);
for (ctr = 0; ctr < aff_count; ctr++) {
/*
* Create a mpidr for each lower affinity level (X-1). Use their
* states to influence the higher affinity state (X).
*/
tempidr = mpidr_set_aff_inst(aff_node->mpidr,
ctr,
aff_node->level - 1);
lo_aff_node = psci_get_aff_map_node(tempidr,
aff_node->level - 1);
assert(lo_aff_node);
/* Continue only if the cpu exists within the cluster */
if (!(lo_aff_node->state & PSCI_AFF_PRESENT))
continue;
switch (psci_get_state(lo_aff_node->state)) {
/*
* If any lower affinity is on within the cluster, then
* the higher affinity is on.
*/
case PSCI_STATE_ON:
return PSCI_STATE_ON;
/*
* At least one X-1 needs to be suspended for X to be suspended
* but it's effectively on for the affinity_info call.
* SUSPEND > ON_PENDING > OFF.
*/
case PSCI_STATE_SUSPEND:
hi_aff_state = PSCI_STATE_SUSPEND;
continue;
/*
* Atleast one X-1 needs to be on_pending & the rest off for X
* to be on_pending. ON_PENDING > OFF.
*/
case PSCI_STATE_ON_PENDING:
if (hi_aff_state != PSCI_STATE_SUSPEND)
hi_aff_state = PSCI_STATE_ON_PENDING;
continue;
/* Higher affinity is off if all lower affinities are off. */
case PSCI_STATE_OFF:
continue;
default:
assert(0);
}
}
return hi_aff_state;
}
/*******************************************************************************
* This function retrieves all the stashed information needed to correctly
* resume a cpu's execution in the non-secure state after it has been physically
* powered on i.e. turned ON or resumed from SUSPEND
******************************************************************************/
unsigned int psci_get_ns_entry_info(unsigned int index)
{
unsigned long sctlr = 0, scr, el_status, id_aa64pfr0;
scr = read_scr();
/* Switch to the non-secure view of the registers */
write_scr(scr | SCR_NS_BIT);
/* Find out which EL we are going to */
id_aa64pfr0 = read_id_aa64pfr0_el1();
el_status = (id_aa64pfr0 >> ID_AA64PFR0_EL2_SHIFT) &
ID_AA64PFR0_ELX_MASK;
/* Restore endianess */
if (psci_ns_entry_info[index].sctlr & SCTLR_EE_BIT)
sctlr |= SCTLR_EE_BIT;
else
sctlr &= ~SCTLR_EE_BIT;
/* Turn off MMU and Caching */
sctlr &= ~(SCTLR_M_BIT | SCTLR_C_BIT | SCTLR_M_BIT);
/* Set the register width */
if (psci_ns_entry_info[index].scr & SCR_RW_BIT)
scr |= SCR_RW_BIT;
else
scr &= ~SCR_RW_BIT;
scr |= SCR_NS_BIT;
if (el_status)
write_sctlr_el2(sctlr);
else
write_sctlr_el1(sctlr);
/* Fulfill the cpu_on entry reqs. as per the psci spec */
write_scr(scr);
write_spsr(psci_ns_entry_info[index].eret_info.spsr);
write_elr(psci_ns_entry_info[index].eret_info.entrypoint);
return psci_ns_entry_info[index].context_id;
}
/*******************************************************************************
* This function retrieves and stashes all the information needed to correctly
* resume a cpu's execution in the non-secure state after it has been physically
* powered on i.e. turned ON or resumed from SUSPEND. This is done prior to
* turning it on or before suspending it.
******************************************************************************/
int psci_set_ns_entry_info(unsigned int index,
unsigned long entrypoint,
unsigned long context_id)
{
int rc = PSCI_E_SUCCESS;
unsigned int rw, mode, ee, spsr = 0;
unsigned long id_aa64pfr0 = read_id_aa64pfr0_el1(), scr = read_scr();
unsigned long el_status;
/* Figure out what mode do we enter the non-secure world in */
el_status = (id_aa64pfr0 >> ID_AA64PFR0_EL2_SHIFT) &
ID_AA64PFR0_ELX_MASK;
/*
* Figure out whether the cpu enters the non-secure address space
* in aarch32 or aarch64
*/
rw = scr & SCR_RW_BIT;
if (rw) {
/*
* Check whether a Thumb entry point has been provided for an
* aarch64 EL
*/
if (entrypoint & 0x1)
return PSCI_E_INVALID_PARAMS;
if (el_status && (scr & SCR_HCE_BIT)) {
mode = MODE_EL2;
ee = read_sctlr_el2() & SCTLR_EE_BIT;
} else {
mode = MODE_EL1;
ee = read_sctlr_el1() & SCTLR_EE_BIT;
}
spsr = DAIF_DBG_BIT | DAIF_ABT_BIT;
spsr |= DAIF_IRQ_BIT | DAIF_FIQ_BIT;
spsr <<= PSR_DAIF_SHIFT;
spsr |= make_spsr(mode, MODE_SP_ELX, !rw);
psci_ns_entry_info[index].sctlr |= ee;
psci_ns_entry_info[index].scr |= SCR_RW_BIT;
} else {
/* Check whether aarch32 has to be entered in Thumb mode */
if (entrypoint & 0x1)
spsr = SPSR32_T_BIT;
if (el_status && (scr & SCR_HCE_BIT)) {
mode = AARCH32_MODE_HYP;
ee = read_sctlr_el2() & SCTLR_EE_BIT;
} else {
mode = AARCH32_MODE_SVC;
ee = read_sctlr_el1() & SCTLR_EE_BIT;
}
/*
* TODO: Choose async. exception bits if HYP mode is not
* implemented according to the values of SCR.{AW, FW} bits
*/
spsr |= DAIF_ABT_BIT | DAIF_IRQ_BIT | DAIF_FIQ_BIT;
spsr <<= PSR_DAIF_SHIFT;
if(ee)
spsr |= SPSR32_EE_BIT;
spsr |= mode;
/* Ensure that the CSPR.E and SCTLR.EE bits match */
psci_ns_entry_info[index].sctlr |= ee;
psci_ns_entry_info[index].scr &= ~SCR_RW_BIT;
}
psci_ns_entry_info[index].eret_info.entrypoint = entrypoint;
psci_ns_entry_info[index].eret_info.spsr = spsr;
psci_ns_entry_info[index].context_id = context_id;
return rc;
}
/*******************************************************************************
* An affinity level could be on, on_pending, suspended or off. These are the
* logical states it can be in. Physically either it's off or on. When it's in
* the state on_pending then it's about to be turned on. It's not possible to
* tell whether that's actually happenned or not. So we err on the side of
* caution & treat the affinity level as being turned off.
******************************************************************************/
inline unsigned int psci_get_phys_state(unsigned int aff_state)
{
return (aff_state != PSCI_STATE_ON ? PSCI_STATE_OFF : PSCI_STATE_ON);
}
unsigned int psci_get_aff_phys_state(aff_map_node *aff_node)
{
unsigned int aff_state;
aff_state = psci_get_state(aff_node->state);
return psci_get_phys_state(aff_state);
}
/*******************************************************************************
* Generic handler which is called when a cpu is physically powered on. It
* recurses through all the affinity levels performing generic, architectural,
* platform setup and state management e.g. for a cluster that's been powered
* on, it will call the platform specific code which will enable coherency at
* the interconnect level. For a cpu it could mean turning on the MMU etc.
*
* This function traverses from the lowest to the highest affinity level
* implemented by the platform. Since it's recursive, for each call the
* 'cur_afflvl' & 'tgt_afflvl' parameters keep track of which level we are at
* and which level we need to get to respectively. Locks are picked up along the
* way so that when the lowest affinity level is hit, state management can be
* safely done. Prior to this, each affinity level does it's bookeeping as per
* the state out of reset.
*
* CAUTION: This function is called with coherent stacks so that coherency and
* the mmu can be turned on safely.
******************************************************************************/
unsigned int psci_afflvl_power_on_finish(unsigned long mpidr,
int cur_afflvl,
int tgt_afflvl,
afflvl_power_on_finisher *pon_handlers)
{
unsigned int prev_state, next_state, rc = PSCI_E_SUCCESS;
aff_map_node *aff_node;
int level;
mpidr &= MPIDR_AFFINITY_MASK;;
/*
* Some affinity instances at levels between the current and
* target levels could be absent in the mpidr. Skip them and
* start from the first present instance.
*/
level = psci_get_first_present_afflvl(mpidr,
cur_afflvl,
tgt_afflvl,
&aff_node);
/*
* Return if there are no more affinity instances beyond this
* level to process. Else ensure that the returned affinity
* node makes sense.
*/
if (aff_node == NULL)
return rc;
assert(level == aff_node->level);
/*
* This function acquires the lock corresponding to each
* affinity level so that by the time we hit the highest
* affinity level, the system topology is snapshot and state
* management can be done safely.
*/
bakery_lock_get(mpidr, &aff_node->lock);
/* Keep the old and new state handy */
prev_state = psci_get_state(aff_node->state);
next_state = PSCI_STATE_ON;
/* Perform generic, architecture and platform specific handling */
rc = pon_handlers[level](mpidr, aff_node, prev_state);
if (rc != PSCI_E_SUCCESS) {
psci_set_state(aff_node->state, prev_state);
goto exit;
}
/*
* State management: Update the states if this is the highest
* affinity level requested else pass the job to the next level.
*/
if (aff_node->level != tgt_afflvl) {
rc = psci_afflvl_power_on_finish(mpidr,
level + 1,
tgt_afflvl,
pon_handlers);
} else {
psci_change_state(mpidr, MPIDR_AFFLVL0, tgt_afflvl, next_state);
}
/* If all has gone as per plan then this cpu should be marked as ON */
if (level == MPIDR_AFFLVL0) {
next_state = psci_get_state(aff_node->state);
assert(next_state == PSCI_STATE_ON);
}
exit:
bakery_lock_release(mpidr, &aff_node->lock);
return rc;
}
/*
* Copyright (c) 2013, ARM Limited. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of ARM nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <arch.h>
#include <platform.h>
#include <psci.h>
#include <psci_private.h>
#include <asm_macros.S>
.globl psci_aff_on_finish_entry
.globl psci_aff_suspend_finish_entry
.globl __psci_cpu_off
.globl __psci_cpu_suspend
.section platform_code, "ax"; .align 3
/* -----------------------------------------------------
* This cpu has been physically powered up. Depending
* upon whether it was resumed from suspend or simply
* turned on, call the common power on finisher with
* the handlers (chosen depending upon original state).
* For ease, the finisher is called with coherent
* stacks. This allows the cluster/cpu finishers to
* enter coherency and enable the mmu without running
* into issues. We switch back to normal stacks once
* all this is done.
* -----------------------------------------------------
*/
psci_aff_on_finish_entry:
adr x23, psci_afflvl_on_finishers
b psci_aff_common_finish_entry
psci_aff_suspend_finish_entry:
adr x23, psci_afflvl_suspend_finishers
psci_aff_common_finish_entry:
adr x22, psci_afflvl_power_on_finish
bl read_mpidr
mov x19, x0
bl platform_set_coherent_stack
/* ---------------------------------------------
* Call the finishers starting from affinity
* level 0.
* ---------------------------------------------
*/
bl get_max_afflvl
mov x3, x23
mov x2, x0
mov x0, x19
mov x1, #MPIDR_AFFLVL0
blr x22
mov x21, x0
/* --------------------------------------------
* Give ourselves a stack allocated in Normal
* -IS-WBWA memory
* --------------------------------------------
*/
mov x0, x19
bl platform_set_stack
/* --------------------------------------------
* Restore the context id. value
* --------------------------------------------
*/
mov x0, x21
/* --------------------------------------------
* Jump back to the non-secure world assuming
* that the elr and spsr setup has been done
* by the finishers
* --------------------------------------------
*/
eret
_panic:
b _panic
/* -----------------------------------------------------
* The following two stubs give the calling cpu a
* coherent stack to allow flushing of caches without
* suffering from stack coherency issues
* -----------------------------------------------------
*/
__psci_cpu_off:
func_prologue
sub sp, sp, #0x10
stp x19, x20, [sp, #0]
mov x19, sp
bl read_mpidr
bl platform_set_coherent_stack
bl psci_cpu_off
mov x1, #PSCI_E_SUCCESS
cmp x0, x1
b.eq final_wfi
mov sp, x19
ldp x19, x20, [sp,#0]
add sp, sp, #0x10
func_epilogue
ret
__psci_cpu_suspend:
func_prologue
sub sp, sp, #0x20
stp x19, x20, [sp, #0]
stp x21, x22, [sp, #0x10]
mov x19, sp
mov x20, x0
mov x21, x1
mov x22, x2
bl read_mpidr
bl platform_set_coherent_stack
mov x0, x20
mov x1, x21
mov x2, x22
bl psci_cpu_suspend
mov x1, #PSCI_E_SUCCESS
cmp x0, x1
b.eq final_wfi
mov sp, x19
ldp x21, x22, [sp,#0x10]
ldp x19, x20, [sp,#0]
add sp, sp, #0x20
func_epilogue
ret
final_wfi:
dsb sy
wfi
wfi_spill:
b wfi_spill
/*
* Copyright (c) 2013, ARM Limited. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of ARM nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <arch_helpers.h>
#include <console.h>
#include <platform.h>
#include <psci_private.h>
/*******************************************************************************
* PSCI frontend api for servicing SMCs. Described in the PSCI spec.
******************************************************************************/
int psci_cpu_on(unsigned long target_cpu,
unsigned long entrypoint,
unsigned long context_id)
{
int rc;
unsigned int start_afflvl, target_afflvl;
/* Determine if the cpu exists of not */
rc = psci_validate_mpidr(target_cpu, MPIDR_AFFLVL0);
if (rc != PSCI_E_SUCCESS) {
goto exit;
}
start_afflvl = get_max_afflvl();
target_afflvl = MPIDR_AFFLVL0;
rc = psci_afflvl_on(target_cpu,
entrypoint,
context_id,
start_afflvl,
target_afflvl);
exit:
return rc;
}
unsigned int psci_version(void)
{
return PSCI_MAJOR_VER | PSCI_MINOR_VER;
}
int psci_cpu_suspend(unsigned int power_state,
unsigned long entrypoint,
unsigned long context_id)
{
int rc;
unsigned long mpidr;
unsigned int tgt_afflvl, pstate_type;
/* TODO: Standby states are not supported at the moment */
pstate_type = psci_get_pstate_type(power_state);
if (pstate_type == 0) {
rc = PSCI_E_INVALID_PARAMS;
goto exit;
}
/* Sanity check the requested state */
tgt_afflvl = psci_get_pstate_afflvl(power_state);
if (tgt_afflvl > MPIDR_MAX_AFFLVL) {
rc = PSCI_E_INVALID_PARAMS;
goto exit;
}
mpidr = read_mpidr();
rc = psci_afflvl_suspend(mpidr,
entrypoint,
context_id,
power_state,
tgt_afflvl,
MPIDR_AFFLVL0);
exit:
if (rc != PSCI_E_SUCCESS)
assert(rc == PSCI_E_INVALID_PARAMS);
return rc;
}
int psci_cpu_off(void)
{
int rc;
unsigned long mpidr;
int target_afflvl = get_max_afflvl();
mpidr = read_mpidr();
/*
* Traverse from the highest to the lowest affinity level. When the
* lowest affinity level is hit, all the locks are acquired. State
* management is done immediately followed by cpu, cluster ...
* ..target_afflvl specific actions as this function unwinds back.
*/
rc = psci_afflvl_off(mpidr, target_afflvl, MPIDR_AFFLVL0);
if (rc != PSCI_E_SUCCESS) {
assert(rc == PSCI_E_DENIED);
}
return rc;
}
int psci_affinity_info(unsigned long target_affinity,
unsigned int lowest_affinity_level)
{
int rc = PSCI_E_INVALID_PARAMS;
unsigned int aff_state;
aff_map_node *node;
if (lowest_affinity_level > get_max_afflvl()) {
goto exit;
}
node = psci_get_aff_map_node(target_affinity, lowest_affinity_level);
if (node && (node->state & PSCI_AFF_PRESENT)) {
aff_state = psci_get_state(node->state);
/* A suspended cpu is available & on for the OS */
if (aff_state == PSCI_STATE_SUSPEND) {
aff_state = PSCI_STATE_ON;
}
rc = aff_state;
}
exit:
return rc;
}
/* Unimplemented */
int psci_migrate(unsigned int target_cpu)
{
return PSCI_E_NOT_SUPPORTED;
}
/* Unimplemented */
unsigned int psci_migrate_info_type(void)
{
return PSCI_TOS_NOT_PRESENT;
}
unsigned long psci_migrate_info_up_cpu(void)
{
/*
* Return value of this currently unsupported call depends upon
* what psci_migrate_info_type() returns.
*/
return PSCI_E_SUCCESS;
}
/* Unimplemented */
void psci_system_off(void)
{
assert(0);
}
/* Unimplemented */
void psci_system_reset(void)
{
assert(0);
}
/*
* Copyright (c) 2013, ARM Limited. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of ARM nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __PSCI_PRIVATE_H__
#define __PSCI_PRIVATE_H__
#include <bakery_lock.h>
#ifndef __ASSEMBLY__
/*******************************************************************************
* The following two data structures hold the generic information to bringup
* a suspended/hotplugged out cpu
******************************************************************************/
typedef struct {
unsigned long entrypoint;
unsigned long spsr;
} eret_params;
typedef struct {
eret_params eret_info;
unsigned long context_id;
unsigned int scr;
unsigned int sctlr;
} ns_entry_info;
/*******************************************************************************
*
*
******************************************************************************/
typedef struct {
unsigned long sctlr;
unsigned long scr;
unsigned long cptr;
unsigned long cpacr;
unsigned long cntfrq;
unsigned long mair;
unsigned long tcr;
unsigned long ttbr;
unsigned long vbar;
} secure_context;
/*******************************************************************************
* The following two data structures hold the topology tree which in turn tracks
* the state of the all the affinity instances supported by the platform.
******************************************************************************/
typedef struct {
unsigned long mpidr;
unsigned char state;
char level;
unsigned int data;
bakery_lock lock;
} aff_map_node;
typedef struct {
int min;
int max;
} aff_limits_node;
typedef unsigned int (*afflvl_power_on_finisher)(unsigned long,
aff_map_node *,
unsigned int);
/*******************************************************************************
* Data prototypes
******************************************************************************/
extern secure_context psci_secure_context[PSCI_NUM_AFFS];
extern ns_entry_info psci_ns_entry_info[PSCI_NUM_AFFS];
extern unsigned int psci_ns_einfo_idx;
extern aff_limits_node psci_aff_limits[MPIDR_MAX_AFFLVL + 1];
extern plat_pm_ops *psci_plat_pm_ops;
extern aff_map_node psci_aff_map[PSCI_NUM_AFFS];
extern afflvl_power_on_finisher psci_afflvl_off_finish_handlers[];
extern afflvl_power_on_finisher psci_afflvl_sus_finish_handlers[];
/*******************************************************************************
* Function prototypes
******************************************************************************/
/* Private exported functions from psci_common.c */
extern int get_max_afflvl(void);
extern unsigned int psci_get_phys_state(unsigned int);
extern unsigned int psci_get_aff_phys_state(aff_map_node *);
extern unsigned int psci_calculate_affinity_state(aff_map_node *);
extern unsigned int psci_get_ns_entry_info(unsigned int index);
extern unsigned long mpidr_set_aff_inst(unsigned long,unsigned char, int);
extern int psci_change_state(unsigned long, int, int, unsigned int);
extern int psci_validate_mpidr(unsigned long, int);
extern unsigned int psci_afflvl_power_on_finish(unsigned long,
int,
int,
afflvl_power_on_finisher *);
extern int psci_set_ns_entry_info(unsigned int index,
unsigned long entrypoint,
unsigned long context_id);
extern int psci_get_first_present_afflvl(unsigned long,
int, int,
aff_map_node **);
/* Private exported functions from psci_setup.c */
extern aff_map_node *psci_get_aff_map_node(unsigned long, int);
/* Private exported functions from psci_affinity_on.c */
extern int psci_afflvl_on(unsigned long,
unsigned long,
unsigned long,
int,
int);
/* Private exported functions from psci_affinity_off.c */
extern int psci_afflvl_off(unsigned long, int, int);
/* Private exported functions from psci_affinity_suspend.c */
extern int psci_afflvl_suspend(unsigned long,
unsigned long,
unsigned long,
unsigned int,
int,
int);
extern unsigned int psci_afflvl_suspend_finish(unsigned long, int, int);
#endif /*__ASSEMBLY__*/
#endif /* __PSCI_PRIVATE_H__ */
/*
* Copyright (c) 2013, ARM Limited. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of ARM nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <arch_helpers.h>
#include <console.h>
#include <platform.h>
#include <psci_private.h>
/*******************************************************************************
* Routines for retrieving the node corresponding to an affinity level instance
* in the mpidr. The first one uses binary search to find the node corresponding
* to the mpidr (key) at a particular affinity level. The second routine decides
* extents of the binary search at each affinity level.
******************************************************************************/
static int psci_aff_map_get_idx(unsigned long key,
int min_idx,
int max_idx)
{
int mid;
/*
* Terminating condition: If the max and min indices have crossed paths
* during the binary search then the key has not been found.
*/
if (max_idx < min_idx)
return PSCI_E_INVALID_PARAMS;
/*
* Bisect the array around 'mid' and then recurse into the array chunk
* where the key is likely to be found. The mpidrs in each node in the
* 'psci_aff_map' for a given affinity level are stored in an ascending
* order which makes the binary search possible.
*/
mid = min_idx + ((max_idx - min_idx) >> 1); /* Divide by 2 */
if (psci_aff_map[mid].mpidr > key)
return psci_aff_map_get_idx(key, min_idx, mid - 1);
else if (psci_aff_map[mid].mpidr < key)
return psci_aff_map_get_idx(key, mid + 1, max_idx);
else
return mid;
}
aff_map_node *psci_get_aff_map_node(unsigned long mpidr, int aff_lvl)
{
int rc;
/* Right shift the mpidr to the required affinity level */
mpidr = mpidr_mask_lower_afflvls(mpidr, aff_lvl);
rc = psci_aff_map_get_idx(mpidr,
psci_aff_limits[aff_lvl].min,
psci_aff_limits[aff_lvl].max);
if (rc >= 0)
return &psci_aff_map[rc];
else
return NULL;
}
/*******************************************************************************
* Function which initializes the 'aff_map_node' corresponding to an affinity
* level instance. Each node has a unique mpidr, level and bakery lock. The data
* field is opaque and holds affinity level specific data e.g. for affinity
* level 0 it contains the index into arrays that hold the secure/non-secure
* state for a cpu that's been turned on/off
******************************************************************************/
static void psci_init_aff_map_node(unsigned long mpidr,
int level,
unsigned int idx)
{
unsigned char state;
psci_aff_map[idx].mpidr = mpidr;
psci_aff_map[idx].level = level;
bakery_lock_init(&psci_aff_map[idx].lock);
/*
* If an affinity instance is present then mark it as OFF to begin with.
*/
state = plat_get_aff_state(level, mpidr);
psci_aff_map[idx].state = state;
if (state & PSCI_AFF_PRESENT) {
psci_set_state(psci_aff_map[idx].state, PSCI_STATE_OFF);
}
if (level == MPIDR_AFFLVL0) {
/* Ensure that we have not overflowed the psci_ns_einfo array */
assert(psci_ns_einfo_idx < PSCI_NUM_AFFS);
psci_aff_map[idx].data = psci_ns_einfo_idx;
psci_ns_einfo_idx++;
}
return;
}
/*******************************************************************************
* Core routine used by the Breadth-First-Search algorithm to populate the
* affinity tree. Each level in the tree corresponds to an affinity level. This
* routine's aim is to traverse to the target affinity level and populate nodes
* in the 'psci_aff_map' for all the siblings at that level. It uses the current
* affinity level to keep track of how many levels from the root of the tree
* have been traversed. If the current affinity level != target affinity level,
* then the platform is asked to return the number of children that each
* affinity instance has at the current affinity level. Traversal is then done
* for each child at the next lower level i.e. current affinity level - 1.
*
* CAUTION: This routine assumes that affinity instance ids are allocated in a
* monotonically increasing manner at each affinity level in a mpidr starting
* from 0. If the platform breaks this assumption then this code will have to
* be reworked accordingly.
******************************************************************************/
static unsigned int psci_init_aff_map(unsigned long mpidr,
unsigned int affmap_idx,
int cur_afflvl,
int tgt_afflvl)
{
unsigned int ctr, aff_count;
assert(cur_afflvl >= tgt_afflvl);
/*
* Find the number of siblings at the current affinity level &
* assert if there are none 'cause then we have been invoked with
* an invalid mpidr.
*/
aff_count = plat_get_aff_count(cur_afflvl, mpidr);
assert(aff_count);
if (tgt_afflvl < cur_afflvl) {
for (ctr = 0; ctr < aff_count; ctr++) {
mpidr = mpidr_set_aff_inst(mpidr, ctr, cur_afflvl);
affmap_idx = psci_init_aff_map(mpidr,
affmap_idx,
cur_afflvl - 1,
tgt_afflvl);
}
} else {
for (ctr = 0; ctr < aff_count; ctr++, affmap_idx++) {
mpidr = mpidr_set_aff_inst(mpidr, ctr, cur_afflvl);
psci_init_aff_map_node(mpidr, cur_afflvl, affmap_idx);
}
/* affmap_idx is 1 greater than the max index of cur_afflvl */
psci_aff_limits[cur_afflvl].max = affmap_idx - 1;
}
return affmap_idx;
}
/*******************************************************************************
* This function initializes the topology tree by querying the platform. To do
* so, it's helper routines implement a Breadth-First-Search. At each affinity
* level the platform conveys the number of affinity instances that exist i.e.
* the affinity count. The algorithm populates the psci_aff_map recursively
* using this information. On a platform that implements two clusters of 4 cpus
* each, the populated aff_map_array would look like this:
*
* <- cpus cluster0 -><- cpus cluster1 ->
* ---------------------------------------------------
* | 0 | 1 | 0 | 1 | 2 | 3 | 0 | 1 | 2 | 3 |
* ---------------------------------------------------
* ^ ^
* cluster __| cpu __|
* limit limit
*
* The first 2 entries are of the cluster nodes. The next 4 entries are of cpus
* within cluster 0. The last 4 entries are of cpus within cluster 1.
* The 'psci_aff_limits' array contains the max & min index of each affinity
* level within the 'psci_aff_map' array. This allows restricting search of a
* node at an affinity level between the indices in the limits array.
******************************************************************************/
void psci_setup(unsigned long mpidr)
{
int afflvl, affmap_idx, rc, max_afflvl;
aff_map_node *node;
/* Initialize psci's internal state */
memset(psci_aff_map, 0, sizeof(psci_aff_map));
memset(psci_aff_limits, 0, sizeof(psci_aff_limits));
memset(psci_ns_entry_info, 0, sizeof(psci_ns_entry_info));
psci_ns_einfo_idx = 0;
psci_plat_pm_ops = NULL;
/* Find out the maximum affinity level that the platform implements */
max_afflvl = get_max_afflvl();
assert(max_afflvl <= MPIDR_MAX_AFFLVL);
/*
* This call traverses the topology tree with help from the platform and
* populates the affinity map using a breadth-first-search recursively.
* We assume that the platform allocates affinity instance ids from 0
* onwards at each affinity level in the mpidr. FIRST_MPIDR = 0.0.0.0
*/
affmap_idx = 0;
for (afflvl = max_afflvl; afflvl >= MPIDR_AFFLVL0; afflvl--) {
affmap_idx = psci_init_aff_map(FIRST_MPIDR,
affmap_idx,
max_afflvl,
afflvl);
}
/*
* Set the bounds for the affinity counts of each level in the map. Also
* flush out the entire array so that it's visible to subsequent power
* management operations. The 'psci_aff_map' array is allocated in
* coherent memory so does not need flushing. The 'psci_aff_limits'
* array is allocated in normal memory. It will be accessed when the mmu
* is off e.g. after reset. Hence it needs to be flushed.
*/
for (afflvl = MPIDR_AFFLVL0; afflvl < max_afflvl; afflvl++) {
psci_aff_limits[afflvl].min =
psci_aff_limits[afflvl + 1].max + 1;
}
flush_dcache_range((unsigned long) psci_aff_limits,
sizeof(psci_aff_limits));
/*
* Mark the affinity instances in our mpidr as ON. No need to lock as
* this is the primary cpu.
*/
mpidr &= MPIDR_AFFINITY_MASK;
for (afflvl = max_afflvl; afflvl >= MPIDR_AFFLVL0; afflvl--) {
node = psci_get_aff_map_node(mpidr, afflvl);
assert(node);
/* Mark each present node as ON. */
if (node->state & PSCI_AFF_PRESENT) {
psci_set_state(node->state, PSCI_STATE_ON);
}
}
rc = platform_setup_pm(&psci_plat_pm_ops);
assert(rc == 0);
assert(psci_plat_pm_ops);
return;
}
/*
* Copyright (c) 2013, ARM Limited. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of ARM nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <arch_helpers.h>
#include <console.h>
#include <platform.h>
#include <semihosting.h>
#include <bl_common.h>
#include <psci.h>
/*******************************************************************************
* Perform initialization of runtime services possibly across exception levels
* in the secure address space e.g. psci & interrupt handling.
******************************************************************************/
void runtime_svc_init(unsigned long mpidr)
{
psci_setup(mpidr);
return;
}
ARM Trusted Firmware - version 0.2
==================================
New features
------------
* First source release.
* Code for the PSCI suspend feature is supplied, although this is not enabled
by default since there are known issues (see below).
Issues resolved since last release
----------------------------------
* The "psci" nodes in the FDTs provided in this release now fully comply
with the recommendations made in the PSCI specification.
Known issues
------------
The following is a list of issues which are expected to be fixed in the future
releases of the ARM Trusted Firmware.
* The TrustZone Address Space Controller (TZC-400) is not being programmed
yet. Use of model parameter `-C bp.secure_memory=1` is not supported.
* No support yet for secure world interrupt handling or for switching context
between secure and normal worlds in EL3.
* GICv3 support is experimental. The Linux kernel patches to support this are
not widely available. There are known issues with GICv3 initialization in
the ARM Trusted Firmware.
* Dynamic image loading is not available yet. The current image loader
implementation (used to load BL2 and all subsequent images) has some
limitations. Changing BL2 or BL3-1 load addresses in certain ways can lead
to loading errors, even if the images should theoretically fit in memory.
* Although support for PSCI `CPU_SUSPEND` is present, it is not yet stable
and ready for use.
* PSCI api calls `AFFINITY_INFO` & `PSCI_VERSION` are implemented but have not
been tested.
* The ARM Trusted Firmware make files result in all build artifacts being
placed in the root of the project. These should be placed in appropriate
sub-directories.
* The compilation of ARM Trusted Firmware is not free from compilation
warnings. Some of these warnings have not been investigated yet so they
could mask real bugs.
* The ARM Trusted Firmware currently uses toolchain/system include files like
stdio.h. It should provide versions of these within the project to maintain
compatibility between toolchains/systems.
* The PSCI code takes some locks in an incorrect sequence. This may cause
problems with suspend and hotplug in certain conditions.
* The Linux kernel used in this release is based on version 3.12-rc4. Using
this kernel with the ARM Trusted Firmware fails to start the file-system as
a RAM-disk. It fails to execute user-space `init` from the RAM-disk. As an
alternative, the VirtioBlock mechanism can be used to provide a file-system
to the kernel.
Detailed changes since last release
-----------------------------------
First source release – not applicable.
- - - - - - - - - - - - - - - - - - - - - - - - - -
_Copyright (c) 2013 ARM Ltd. All rights reserved._
ARM Trusted Firmware Porting Guide
==================================
Contents
--------
1. Introduction
2. Common Modifications
* Common mandatory modifications
* Common optional modifications
3. Boot Loader stage specific modifications
* Boot Loader stage 1 (BL1)
* Boot Loader stage 2 (BL2)
* Boot Loader stage 3-1 (BL3-1)
* PSCI implementation (in BL3-1)
- - - - - - - - - - - - - - - - - -
1. Introduction
----------------
Porting the ARM Trusted Firmware to a new platform involves making some
mandatory and optional modifications for both the cold and warm boot paths.
Modifications consist of:
* Implementing a platform-specific function or variable,
* Setting up the execution context in a certain way, or
* Defining certain constants (for example #defines).
The firmware provides a default implementation of variables and functions to
fulfill the optional requirements. These implementations are all weakly defined;
they are provided to ease the porting effort. Each platform port can override
them with its own implementation if the default implementation is inadequate.
Some modifications are common to all Boot Loader (BL) stages. Section 2
discusses these in detail. The subsequent sections discuss the remaining
modifications for each BL stage in detail.
This document should be read in conjunction with the ARM Trusted Firmware
[User Guide].
2. Common modifications
------------------------
This section covers the modifications that should be made by the platform for
each BL stage to correctly port the firmware stack. They are categorized as
either mandatory or optional.
2.1 Common mandatory modifications
----------------------------------
A platform port must enable the Memory Management Unit (MMU) with identity
mapped page tables, and enable both the instruction and data caches for each BL
stage. In the ARM FVP port, each BL stage configures the MMU in its platform-
specific architecture setup function, for example `blX_plat_arch_setup()`.
Each platform must allocate a block of identity mapped secure memory with
Device-nGnRE attributes aligned to page boundary (4K) for each BL stage. This
memory is identified by the section name `tzfw_coherent_mem` so that its
possible for the firmware to place variables in it using the following C code
directive:
__attribute__ ((section("tzfw_coherent_mem")))
Or alternatively the following assembler code directive:
.section tzfw_coherent_mem
The `tzfw_coherent_mem` section is used to allocate any data structures that are
accessed both when a CPU is executing with its MMU and caches enabled, and when
it's running with its MMU and caches disabled. Examples are given below.
The following variables, functions and constants must be defined by the platform
for the firmware to work correctly.
### File : platform.h [mandatory]
Each platform must export a header file of this name with the following
constants defined. In the ARM FVP port, this file is found in
[../plat/fvp/platform.h].
* ** #define : PLATFORM_LINKER_FORMAT **
Defines the linker format used by the platform, for example
`elf64-littleaarch64` used by the FVP.
* ** #define : PLATFORM_LINKER_ARCH **
Defines the processor architecture for the linker by the platform, for
example `aarch64` used by the FVP.
* ** #define : PLATFORM_STACK_SIZE **
Defines the normal stack memory available to each CPU. This constant is used
by `platform_set_stack()`.
* ** #define : FIRMWARE_WELCOME_STR **
Defines the character string printed by BL1 upon entry into the `bl1_main()`
function.
* ** #define : BL2_IMAGE_NAME **
Name of the BL2 binary image on the host file-system. This name is used by
BL1 to load BL2 into secure memory using semi-hosting.
* ** #define : PLATFORM_CACHE_LINE_SIZE **
Defines the size (in bytes) of the largest cache line across all the cache
levels in the platform.
* ** #define : PLATFORM_CLUSTER_COUNT **
Defines the total number of clusters implemented by the platform in the
system.
* ** #define : PLATFORM_CORE_COUNT **
Defines the total number of CPUs implemented by the platform across all
clusters in the system.
* ** #define : PLATFORM_MAX_CPUS_PER_CLUSTER **
Defines the maximum number of CPUs that can be implemented within a cluster
on the platform.
* ** #define : PRIMARY_CPU **
Defines the `MPIDR` of the primary CPU on the platform. This value is used
after a cold boot to distinguish between primary and secondary CPUs.
* ** #define : TZROM_BASE **
Defines the base address of secure ROM on the platform, where the BL1 binary
is loaded. This constant is used by the linker scripts to ensure that the
BL1 image fits into the available memory.
* ** #define : TZROM_SIZE **
Defines the size of secure ROM on the platform. This constant is used by the
linker scripts to ensure that the BL1 image fits into the available memory.
* ** #define : TZRAM_BASE **
Defines the base address of the secure RAM on platform, where the data
section of the BL1 binary is loaded. The BL2 and BL3-1 images are also
loaded in this secure RAM region. This constant is used by the linker
scripts to ensure that the BL1 data section and BL2/BL3-1 binary images fit
into the available memory.
* ** #define : TZRAM_SIZE **
Defines the size of the secure RAM on the platform. This constant is used by
the linker scripts to ensure that the BL1 data section and BL2/BL3-1 binary
images fit into the available memory.
* ** #define : SYS_CNTCTL_BASE **
Defines the base address of the `CNTCTLBase` frame of the memory mapped
counter and timer in the system level implementation of the generic timer.
* ** #define : BL2_BASE **
Defines the base address in secure RAM where BL1 loads the BL2 binary image.
* ** #define : BL31_BASE **
Defines the base address in secure RAM where BL2 loads the BL3-1 binary
image.
### Other mandatory modifications
The following following mandatory modifications may be implemented in any file
the implementer chooses. In the ARM FVP port, they are implemented in
[../plat/fvp/aarch64/fvp_common.c].
* ** Variable : unsigned char platform_normal_stacks[X][Y] **
where X = PLATFORM_STACK_SIZE
and Y = PLATFORM_CORE_COUNT
Each platform must allocate a block of memory with Normal Cacheable, Write
back, Write allocate and Inner Shareable attributes aligned to the size (in
bytes) of the largest cache line amongst all caches implemented in the
system. A pointer to this memory should be exported with the name
`platform_normal_stacks`. This pointer is used by the common platform helper
function `platform_set_stack()` to allocate a stack to each CPU in the
platform (see [../plat/common/aarch64/platform_helpers.S]).
2.2 Common optional modifications
---------------------------------
The following are helper functions implemented by the firmware that perform
common platform-specific tasks. A platform may choose to override these
definitions.
### Function : platform_get_core_pos()
Argument : unsigned long
Return : int
A platform may need to convert the `MPIDR` of a CPU to an absolute number, which
can be used as a CPU-specific linear index into blocks of memory (for example
while allocating per-CPU stacks). This routine contains a simple mechanism
to perform this conversion, using the assumption that each cluster contains a
maximum of 4 CPUs:
linear index = cpu_id + (cluster_id * 4)
cpu_id = 8-bit value in MPIDR at affinity level 0
cluster_id = 8-bit value in MPIDR at affinity level 1
### Function : platform_set_coherent_stack()
Argument : unsigned long
Return : void
A platform may need stack memory that is coherent with main memory to perform
certain operations like:
* Turning the MMU on, or
* Flushing caches prior to powering down a CPU or cluster.
Each BL stage allocates this coherent stack memory for each CPU in the
`tzfw_coherent_mem` section. A pointer to this memory (`pcpu_dv_mem_stack`) is
used by this function to allocate a coherent stack for each CPU. A CPU is
identified by its `MPIDR`, which is passed as an argument to this function.
The size of the stack allocated to each CPU is specified by the constant
`PCPU_DV_MEM_STACK_SIZE`.
### Function : platform_is_primary_cpu()
Argument : unsigned long
Return : unsigned int
This function identifies a CPU by its `MPIDR`, which is passed as the argument,
to determine whether this CPU is the primary CPU or a secondary CPU. A return
value of zero indicates that the CPU is not the primary CPU, while a non-zero
return value indicates that the CPU is the primary CPU.
### Function : platform_set_stack()
Argument : unsigned long
Return : void
This function uses the `platform_normal_stacks` pointer variable to allocate
stacks to each CPU. Further details are given in the description of the
`platform_normal_stacks` variable below. A CPU is identified by its `MPIDR`,
which is passed as the argument.
The size of the stack allocated to each CPU is specified by the platform defined
constant `PLATFORM_STACK_SIZE`.
### Function : plat_report_exception()
Argument : unsigned int
Return : void
A platform may need to report various information about its status when an
exception is taken, for example the current exception level, the CPU security
state (secure/non-secure), the exception type, and so on. This function is
called in the following circumstances:
* In BL1, whenever an exception is taken.
* In BL2, whenever an exception is taken.
* In BL3-1, whenever an asynchronous exception or a synchronous exception
other than an SMC32/SMC64 exception is taken.
The default implementation doesn't do anything, to avoid making assumptions
about the way the platform displays its status information.
This function receives the exception type as its argument. Possible values for
exceptions types are listed in the [../include/runtime_svc.h] header file. Note
that these constants are not related to any architectural exception code; they
are just an ARM Trusted Firmware convention.
3. Modifications specific to a Boot Loader stage
-------------------------------------------------
3.1 Boot Loader Stage 1 (BL1)
-----------------------------
BL1 implements the reset vector where execution starts from after a cold or
warm boot. For each CPU, BL1 is responsible for the following tasks:
1. Distinguishing between a cold boot and a warm boot.
2. In the case of a cold boot and the CPU being the primary CPU, ensuring that
only this CPU executes the remaining BL1 code, including loading and passing
control to the BL2 stage.
3. In the case of a cold boot and the CPU being a secondary CPU, ensuring that
the CPU is placed in a platform-specific state until the primary CPU
performs the necessary steps to remove it from this state.
4. In the case of a warm boot, ensuring that the CPU jumps to a platform-
specific address in the BL3-1 image in the same processor mode as it was
when released from reset.
5. Loading the BL2 image in secure memory using semi-hosting at the
address specified by the platform defined constant `BL2_BASE`.
6. Populating a `meminfo` structure with the following information in memory,
accessible by BL2 immediately upon entry.
meminfo.total_base = Base address of secure RAM visible to BL2
meminfo.total_size = Size of secure RAM visible to BL2
meminfo.free_base = Base address of secure RAM available for
allocation to BL2
meminfo.free_size = Size of secure RAM available for allocation to BL2
BL1 places this `meminfo` structure at the beginning of the free memory
available for its use. Since BL1 cannot allocate memory dynamically at the
moment, its free memory will be available for BL2's use as-is. However, this
means that BL2 must read the `meminfo` structure before it starts using its
free memory (this is discussed in Section 3.2).
In future releases of the ARM Trusted Firmware it will be possible for
the platform to decide where it wants to place the `meminfo` structure for
BL2.
BL1 implements the `init_bl2_mem_layout()` function to populate the
BL2 `meminfo` structure. The platform may override this implementation, for
example if the platform wants to restrict the amount of memory visible to
BL2. Details of how to do this are given below.
The following functions need to be implemented by the platform port to enable
BL1 to perform the above tasks.
### Function : platform_get_entrypoint() [mandatory]
Argument : unsigned long
Return : unsigned int
This function is called with the `SCTLR.M` and `SCTLR.C` bits disabled. The CPU
is identified by its `MPIDR`, which is passed as the argument. The function is
responsible for distinguishing between a warm and cold reset using platform-
specific means. If it's a warm reset then it returns the entrypoint into the
BL3-1 image that the CPU must jump to. If it's a cold reset then this function
must return zero.
This function is also responsible for implementing a platform-specific mechanism
to handle the condition where the CPU has been warm reset but there is no
entrypoint to jump to.
This function does not follow the Procedure Call Standard used by the
Application Binary Interface for the ARM 64-bit architecture. The caller should
not assume that callee saved registers are preserved across a call to this
function.
This function fulfills requirement 1 listed above.
### Function : plat_secondary_cold_boot_setup() [mandatory]
Argument : void
Return : void
This function is called with the MMU and data caches disabled. It is responsible
for placing the executing secondary CPU in a platform-specific state until the
primary CPU performs the necessary actions to bring it out of that state and
allow entry into the OS.
In the ARM FVP port, each secondary CPU powers itself off. The primary CPU is
responsible for powering up the secondary CPU when normal world software
requires them.
This function fulfills requirement 3 above.
### Function : platform_cold_boot_init() [mandatory]
Argument : unsigned long
Return : unsigned int
This function executes with the MMU and data caches disabled. It is only called
by the primary CPU. The argument to this function is the address of the
`bl1_main()` routine where the generic BL1-specific actions are performed.
This function performs any platform-specific and architectural setup that the
platform requires to make execution of `bl1_main()` possible.
The platform must enable the MMU with identity mapped page tables and enable
caches by setting the `SCTLR.I` and `SCTLR.C` bits.
Platform-specific setup might include configuration of memory controllers,
configuration of the interconnect to allow the cluster to service cache snoop
requests from another cluster, zeroing of the ZI section, and so on.
In the ARM FVP port, this function enables CCI snoops into the cluster that the
primary CPU is part of. It also enables the MMU and initializes the ZI section
in the BL1 image through the use of linker defined symbols.
This function helps fulfill requirement 2 above.
### Function : bl1_platform_setup() [mandatory]
Argument : void
Return : void
This function executes with the MMU and data caches enabled. It is responsible
for performing any remaining platform-specific setup that can occur after the
MMU and data cache have been enabled.
In the ARM FVP port, it zeros out the ZI section, enables the system level
implementation of the generic timer counter and initializes the console.
This function helps fulfill requirement 5 above.
### Function : bl1_get_sec_mem_layout() [mandatory]
Argument : void
Return : meminfo
This function executes with the MMU and data caches enabled. The `meminfo`
structure returned by this function must contain the extents and availability of
secure RAM for the BL1 stage.
meminfo.total_base = Base address of secure RAM visible to BL1
meminfo.total_size = Size of secure RAM visible to BL1
meminfo.free_base = Base address of secure RAM available for allocation
to BL1
meminfo.free_size = Size of secure RAM available for allocation to BL1
This information is used by BL1 to load the BL2 image in secure RAM. BL1 also
populates a similar structure to tell BL2 the extents of memory available for
its own use.
This function helps fulfill requirement 5 above.
### Function : init_bl2_mem_layout() [optional]
Argument : meminfo *, meminfo *, unsigned int, unsigned long
Return : void
Each BL stage needs to tell the next stage the amount of secure RAM available
for it to use. For example, as part of handing control to BL2, BL1 informs BL2
of the extents of secure RAM available for BL2 to use. BL2 must do the same when
passing control to BL3-1. This information is populated in a `meminfo`
structure.
Depending upon where BL2 has been loaded in secure RAM (determined by
`BL2_BASE`), BL1 calculates the amount of free memory available for BL2 to use.
BL1 also ensures that its data sections resident in secure RAM are not visible
to BL2. An illustration of how this is done in the ARM FVP port is given in the
[User Guide], in the Section "Memory layout on Base FVP".
3.2 Boot Loader Stage 2 (BL2)
-----------------------------
The BL2 stage is executed only by the primary CPU, which is determined in BL1
using the `platform_is_primary_cpu()` function. BL1 passed control to BL2 at
`BL2_BASE`. BL2 executes in Secure EL1 and is responsible for:
1. Loading the BL3-1 binary image in secure RAM using semi-hosting. To load the
BL3-1 image, BL2 makes use of the `meminfo` structure passed to it by BL1.
This structure allows BL2 to calculate how much secure RAM is available for
its use. The platform also defines the address in secure RAM where BL3-1 is
loaded through the constant `BL31_BASE`. BL2 uses this information to
determine if there is enough memory to load the BL3-1 image.
2. Arranging to pass control to a normal world BL image that has been
pre-loaded at a platform-specific address. This address is determined using
the `plat_get_ns_image_entrypoint()` function described below.
BL2 populates an `el_change_info` structure in memory provided by the
platform with information about how BL3-1 should pass control to the normal
world BL image.
3. Populating a `meminfo` structure with the following information in
memory that is accessible by BL3-1 immediately upon entry.
meminfo.total_base = Base address of secure RAM visible to BL3-1
meminfo.total_size = Size of secure RAM visible to BL3-1
meminfo.free_base = Base address of secure RAM available for allocation
to BL3-1
meminfo.free_size = Size of secure RAM available for allocation to
BL3-1
BL2 places this `meminfo` structure in memory provided by the
platform (`bl2_el_change_mem_ptr`). BL2 implements the
`init_bl31_mem_layout()` function to populate the BL3-1 meminfo structure
described above. The platform may override this implementation, for example
if the platform wants to restrict the amount of memory visible to BL3-1.
Details of this function are given below.
The following functions must be implemented by the platform port to enable BL2
to perform the above tasks.
### Function : bl2_early_platform_setup() [mandatory]
Argument : meminfo *, void *
Return : void
This function executes with the MMU and data caches disabled. It is only called
by the primary CPU. The arguments to this function are:
* The address of the `meminfo` structure populated by BL1
* An opaque pointer that the platform may use as needed.
The platform must copy the contents of the `meminfo` structure into a private
variable as the original memory may be subsequently overwritten by BL2. The
copied structure is made available to all BL2 code through the
`bl2_get_sec_mem_layout()` function.
### Function : bl2_plat_arch_setup() [mandatory]
Argument : void
Return : void
This function executes with the MMU and data caches disabled. It is only called
by the primary CPU.
The purpose of this function is to perform any architectural initialization
that varies across platforms, for example enabling the MMU (since the memory
map differs across platforms).
### Function : bl2_platform_setup() [mandatory]
Argument : void
Return : void
This function may execute with the MMU and data caches enabled if the platform
port does the necessary initialization in `bl2_plat_arch_setup()`. It is only
called by the primary CPU.
The purpose of this function is to perform any platform initialization specific
to BL2. This function must initialize a pointer to memory
(`bl2_el_change_mem_ptr`), which can then be used to populate an
`el_change_info` structure. The underlying requirement is that the platform must
initialize this pointer before the `get_el_change_mem_ptr()` function
accesses it in `bl2_main()`.
The ARM FVP port initializes this pointer to the base address of Secure DRAM
(`0x06000000`).
### Variable : unsigned char bl2_el_change_mem_ptr[EL_CHANGE_MEM_SIZE] [mandatory]
As mentioned in the description of `bl2_platform_setup()`, this pointer is
initialized by the platform to point to memory where an `el_change_info`
structure can be populated.
### Function : bl2_get_sec_mem_layout() [mandatory]
Argument : void
Return : meminfo
This function may execute with the MMU and data caches enabled if the platform
port does the necessary initialization in `bl2_plat_arch_setup()`. It is only
called by the primary CPU.
The purpose of this function is to return a `meminfo` structure populated with
the extents of secure RAM available for BL2 to use. See
`bl2_early_platform_setup()` above.
### Function : init_bl31_mem_layout() [optional]
Argument : meminfo *, meminfo *, unsigned int
Return : void
Each BL stage needs to tell the next stage the amount of secure RAM that is
available for it to use. For example, as part of handing control to BL2, BL1
must inform BL2 about the extents of secure RAM that is available for BL2 to
use. BL2 must do the same when passing control to BL3-1. This information is
populated in a `meminfo` structure.
Depending upon where BL3-1 has been loaded in secure RAM (determined by
`BL31_BASE`), BL2 calculates the amount of free memory available for BL3-1 to
use. BL2 also ensures that BL3-1 is able reclaim memory occupied by BL2. This
is done because BL2 never executes again after passing control to BL3-1.
An illustration of how this is done in the ARM FVP port is given in the
[User Guide], in the section "Memory layout on Base FVP".
### Function : plat_get_ns_image_entrypoint() [mandatory]
Argument : void
Return : unsigned long
As previously described, BL2 is responsible for arranging for control to be
passed to a normal world BL image through BL3-1. This function returns the
entrypoint of that image, which BL3-1 uses to jump to it.
The ARM FVP port assumes that flash memory has been pre-loaded with the UEFI
image, and so returns the base address of flash memory.
3.2 Boot Loader Stage 3-1 (BL3-1)
---------------------------------
During cold boot, the BL3-1 stage is executed only by the primary CPU. This is
determined in BL1 using the `platform_is_primary_cpu()` function. BL1 passes
control to BL3-1 at `BL31_BASE`. During warm boot, BL3-1 is executed by all
CPUs. BL3-1 executes at EL3 and is responsible for:
1. Re-initializing all architectural and platform state. Although BL1 performs
some of this initialization, BL3-1 remains resident in EL3 and must ensure
that EL3 architectural and platform state is completely initialized. It
should make no assumptions about the system state when it receives control.
2. Passing control to a normal world BL image, pre-loaded at a platform-
specific address by BL2. BL3-1 uses the `el_change_info` structure that BL2
populated in memory to do this.
3. Providing runtime firmware services. Currently, BL3-1 only implements a
subset of the Power State Coordination Interface (PSCI) API as a runtime
service. See Section 3.3 below for details of porting the PSCI
implementation.
The following functions must be implemented by the platform port to enable BL3-1
to perform the above tasks.
### Function : bl31_early_platform_setup() [mandatory]
Argument : meminfo *, void *, unsigned long
Return : void
This function executes with the MMU and data caches disabled. It is only called
by the primary CPU. The arguments to this function are:
* The address of the `meminfo` structure populated by BL2.
* An opaque pointer that the platform may use as needed.
* The `MPIDR` of the primary CPU.
The platform must copy the contents of the `meminfo` structure into a private
variable as the original memory may be subsequently overwritten by BL3-1. The
copied structure is made available to all BL3-1 code through the
`bl31_get_sec_mem_layout()` function.
### Function : bl31_plat_arch_setup() [mandatory]
Argument : void
Return : void
This function executes with the MMU and data caches disabled. It is only called
by the primary CPU.
The purpose of this function is to perform any architectural initialization
that varies across platforms, for example enabling the MMU (since the memory
map differs across platforms).
### Function : bl31_platform_setup() [mandatory]
Argument : void
Return : void
This function may execute with the MMU and data caches enabled if the platform
port does the necessary initialization in `bl31_plat_arch_setup()`. It is only
called by the primary CPU.
The purpose of this function is to complete platform initialization so that both
BL3-1 runtime services and normal world software can function correctly.
The ARM FVP port does the following:
* Initializes the generic interrupt controller.
* Configures the CLCD controller.
* Grants access to the system counter timer module
* Initializes the FVP power controller device
* Detects the system topology.
### Function : bl31_get_next_image_info() [mandatory]
Argument : unsigned long
Return : el_change_info *
This function may execute with the MMU and data caches enabled if the platform
port does the necessary initializations in `bl31_plat_arch_setup()`.
This function is called by `bl31_main()` to retrieve information provided by
BL2, so that BL3-1 can pass control to the normal world software image. This
function must return a pointer to the `el_change_info` structure (that was
copied during `bl31_early_platform_setup()`).
### Function : bl31_get_sec_mem_layout() [mandatory]
Argument : void
Return : meminfo
This function may execute with the MMU and data caches enabled if the platform
port does the necessary initializations in `bl31_plat_arch_setup()`. It is only
called by the primary CPU.
The purpose of this function is to return a `meminfo` structure populated with
the extents of secure RAM available for BL3-1 to use. See
`bl31_early_platform_setup()` above.
3.3 Power State Coordination Interface (in BL3-1)
------------------------------------------------
The ARM Trusted Firmware's implementation of the PSCI API is based around the
concept of an _affinity instance_. Each _affinity instance_ can be uniquely
identified in a system by a CPU ID (the processor `MPIDR` is used in the PSCI
interface) and an _affinity level_. A processing element (for example, a
CPU) is at level 0. If the CPUs in the system are described in a tree where the
node above a CPU is a logical grouping of CPUs that share some state, then
affinity level 1 is that group of CPUs (for example, a cluster), and affinity
level 2 is a group of clusters (for example, the system). The implementation
assumes that the affinity level 1 ID can be computed from the affinity level 0
ID (for example, a unique cluster ID can be computed from the CPU ID). The
current implementation computes this on the basis of the recommended use of
`MPIDR` affinity fields in the ARM Architecture Reference Manual.
BL3-1's platform initialization code exports a pointer to the platform-specific
power management operations required for the PSCI implementation to function
correctly. This information is populated in the `plat_pm_ops` structure. The
PSCI implementation calls members of the `plat_pm_ops` structure for performing
power management operations for each affinity instance. For example, the target
CPU is specified by its `MPIDR` in a PSCI `CPU_ON` call. The `affinst_on()`
handler (if present) is called for each affinity instance as the PSCI
implementation powers up each affinity level implemented in the `MPIDR` (for
example, CPU, cluster and system).
The following functions must be implemented to initialize PSCI functionality in
the ARM Trusted Firmware.
### Function : plat_get_aff_count() [mandatory]
Argument : unsigned int, unsigned long
Return : unsigned int
This function may execute with the MMU and data caches enabled if the platform
port does the necessary initializations in `bl31_plat_arch_setup()`. It is only
called by the primary CPU.
This function is called by the PSCI initialization code to detect the system
topology. Its purpose is to return the number of affinity instances implemented
at a given `affinity level` (specified by the first argument) and a given
`MPIDR` (specified by the second argument). For example, on a dual-cluster
system where first cluster implements 2 CPUs and the second cluster implements 4
CPUs, a call to this function with an `MPIDR` corresponding to the first cluster
(`0x0`) and affinity level 0, would return 2. A call to this function with an
`MPIDR` corresponding to the second cluster (`0x100`) and affinity level 0,
would return 4.
### Function : plat_get_aff_state() [mandatory]
Argument : unsigned int, unsigned long
Return : unsigned int
This function may execute with the MMU and data caches enabled if the platform
port does the necessary initializations in `bl31_plat_arch_setup()`. It is only
called by the primary CPU.
This function is called by the PSCI initialization code. Its purpose is to
return the state of an affinity instance. The affinity instance is determined by
the affinity ID at a given `affinity level` (specified by the first argument)
and an `MPIDR` (specified by the second argument). The state can be one of
`PSCI_AFF_PRESENT` or `PSCI_AFF_ABSENT`. The latter state is used to cater for
system topologies where certain affinity instances are unimplemented. For
example, consider a platform that implements a single cluster with 4 CPUs and
another CPU implemented directly on the interconnect with the cluster. The
`MPIDR`s of the cluster would range from `0x0-0x3`. The `MPIDR` of the single
CPU would be 0x100 to indicate that it does not belong to cluster 0. Cluster 1
is missing but needs to be accounted for to reach this single CPU in the
topology tree. Hence it is marked as `PSCI_AFF_ABSENT`.
### Function : plat_get_max_afflvl() [mandatory]
Argument : void
Return : int
This function may execute with the MMU and data caches enabled if the platform
port does the necessary initializations in `bl31_plat_arch_setup()`. It is only
called by the primary CPU.
This function is called by the PSCI implementation both during cold and warm
boot, to determine the maximum affinity level that the power management
operations should apply to. ARMv8 has support for 4 affinity levels. It is
likely that hardware will implement fewer affinity levels. This function allows
the PSCI implementation to consider only those affinity levels in the system
that the platform implements. For example, the Base AEM FVP implements two
clusters with a configurable number of CPUs. It reports the maximum affinity
level as 1, resulting in PSCI power control up to the cluster level.
### Function : platform_setup_pm() [mandatory]
Argument : plat_pm_ops **
Return : int
This function may execute with the MMU and data caches enabled if the platform
port does the necessary initializations in `bl31_plat_arch_setup()`. It is only
called by the primary CPU.
This function is called by PSCI initialization code. Its purpose is to export
handler routines for platform-specific power management actions by populating
the passed pointer with a pointer to BL3-1's private `plat_pm_ops` structure.
A description of each member of this structure is given below. Please refer to
the ARM FVP specific implementation of these handlers in [../plat/fvp/fvp_pm.c]
as an example. A platform port may choose not implement some of the power
management operations. For example, the ARM FVP port does not implement the
`affinst_standby()` function.
#### plat_pm_ops.affinst_standby()
Perform the platform-specific setup to enter the standby state indicated by the
passed argument.
#### plat_pm_ops.affinst_on()
Perform the platform specific setup to power on an affinity instance, specified
by the `MPIDR` (first argument) and `affinity level` (fourth argument). The
`state` (fifth argument) contains the current state of that affinity instance
(ON or OFF). This is useful to determine whether any action must be taken. For
example, while powering on a CPU, the cluster that contains this CPU might
already be in the ON state. The platform decides what actions must be taken to
transition from the current state to the target state (indicated by the power
management operation).
#### plat_pm_ops.affinst_off()
Perform the platform specific setup to power off an affinity instance in the
`MPIDR` of the calling CPU. It is called by the PSCI `CPU_OFF` API
implementation.
The `MPIDR` (first argument), `affinity level` (second argument) and `state`
(third argument) have a similar meaning as described in the `affinst_on()`
operation. They are used to identify the affinity instance on which the call
is made and its current state. This gives the platform port an indication of the
state transition it must make to perform the requested action. For example, if
the calling CPU is the last powered on CPU in the cluster, after powering down
affinity level 0 (CPU), the platform port should power down affinity level 1
(the cluster) as well.
This function is called with coherent stacks. This allows the PSCI
implementation to flush caches at a given affinity level without running into
stale stack state after turning off the caches. On ARMv8 cache hits do not occur
after the cache has been turned off.
#### plat_pm_ops.affinst_suspend()
Perform the platform specific setup to power off an affinity instance in the
`MPIDR` of the calling CPU. It is called by the PSCI `CPU_SUSPEND` API
implementation.
The `MPIDR` (first argument), `affinity level` (third argument) and `state`
(fifth argument) have a similar meaning as described in the `affinst_on()`
operation. They are used to identify the affinity instance on which the call
is made and its current state. This gives the platform port an indication of the
state transition it must make to perform the requested action. For example, if
the calling CPU is the last powered on CPU in the cluster, after powering down
affinity level 0 (CPU), the platform port should power down affinity level 1
(the cluster) as well.
The difference between turning an affinity instance off versus suspending it
is that in the former case, the affinity instance is expected to re-initialize
its state when its next powered on (see `affinst_on_finish()`). In the latter
case, the affinity instance is expected to save enough state so that it can
resume execution by restoring this state when its powered on (see
`affinst_suspend_finish()`).
This function is called with coherent stacks. This allows the PSCI
implementation to flush caches at a given affinity level without running into
stale stack state after turning off the caches. On ARMv8 cache hits do not occur
after the cache has been turned off.
#### plat_pm_ops.affinst_on_finish()
This function is called by the PSCI implementation after the calling CPU is
powered on and released from reset in response to an earlier PSCI `CPU_ON` call.
It performs the platform-specific setup required to initialize enough state for
this CPU to enter the normal world and also provide secure runtime firmware
services.
The `MPIDR` (first argument), `affinity level` (second argument) and `state`
(third argument) have a similar meaning as described in the previous operations.
This function is called with coherent stacks. This allows the PSCI
implementation to flush caches at a given affinity level without running into
stale stack state after turning off the caches. On ARMv8 cache hits do not occur
after the cache has been turned off.
#### plat_pm_ops.affinst_on_suspend()
This function is called by the PSCI implementation after the calling CPU is
powered on and released from reset in response to an asynchronous wakeup
event, for example a timer interrupt that was programmed by the CPU during the
`CPU_SUSPEND` call. It performs the platform-specific setup required to
restore the saved state for this CPU to resume execution in the normal world
and also provide secure runtime firmware services.
The `MPIDR` (first argument), `affinity level` (second argument) and `state`
(third argument) have a similar meaning as described in the previous operations.
This function is called with coherent stacks. This allows the PSCI
implementation to flush caches at a given affinity level without running into
stale stack state after turning off the caches. On ARMv8 cache hits do not occur
after the cache has been turned off.
BL3-1 platform initialization code must also detect the system topology and
the state of each affinity instance in the topology. This information is
critical for the PSCI runtime service to function correctly. More details are
provided in the description of the `plat_get_aff_count()` and
`plat_get_aff_state()` functions above.
- - - - - - - - - - - - - - - - - - - - - - - - - -
_Copyright (c) 2013 ARM Ltd. All rights reserved._
[User Guide]: user-guide.md
[../plat/common/aarch64/platform_helpers.S]: ../plat/common/aarch64/platform_helpers.S
[../plat/fvp/platform.h]: ../plat/fvp/platform.h
[../plat/fvp/aarch64/fvp_common.c]: ../plat/fvp/aarch64/fvp_common.c
[../plat/fvp/fvp_pm.c]: ../plat/fvp/fvp_pm.c
[../include/runtime_svc.h]: ../include/runtime_svc.h
ARM Trusted Firmware User Guide
===============================
Contents :
1. Introduction
2. Using the Software
3. Firmware Design
4. References
1. Introduction
----------------
The ARM Trusted Firmware implements a subset of the Trusted Board Boot
Requirements (TBBR) Platform Design Document (PDD) [1] for ARM reference
platforms. The TBB sequence starts when the platform is powered on and runs up
to the stage where it hands-off control to firmware running in the normal
world in DRAM. This is the cold boot path.
The ARM Trusted Firmware also implements the Power State Coordination Interface
([PSCI]) PDD [2] as a runtime service. PSCI is the interface from normal world
software to firmware implementing power management use-cases (for example,
secondary CPU boot, hotplug and idle). Normal world software can access ARM
Trusted Firmware runtime services via the ARM SMC (Secure Monitor Call)
instruction. The SMC instruction must be used as mandated by the [SMC Calling
Convention PDD][SMCCC] [3].
2. Using the Software
----------------------
### Host machine requirements
The minimum recommended machine specification is an Intel Core2Duo clocking at
2.6GHz or above, and 12GB RAM. For best performance, use a machine with Intel
Core i7 (SandyBridge) and 16GB of RAM.
### Tools
The following tools are required to use the ARM Trusted Firmware:
* Ubuntu desktop OS. The software has been tested on Ubuntu 12.04.02 (64-bit).
The following packages are also needed:
* `ia32-libs` package.
* `make` and `uuid-dev` packages for building UEFI.
* `bc` and `ncurses-dev` packages for building Linux.
* Baremetal GNU GCC tools. Verified packages can be downloaded from [Linaro]
[Linaro Toolchain]. The rest of this document assumes that the
`gcc-linaro-aarch64-none-elf-4.8-2013.09-01_linux.tar.xz` tools are used.
wget http://releases.linaro.org/13.09/components/toolchain/binaries/gcc-linaro-aarch64-none-elf-4.8-2013.09-01_linux.tar.xz
tar -xf gcc-linaro-aarch64-none-elf-4.8-2013.09-01_linux.tar.xz
* The Device Tree Compiler (DTC) included with Linux kernel 3.12-rc4 is used
to build the Flattened Device Tree (FDT) source files (`.dts` files)
provided with this release.
* (Optional) For debugging, ARM [Development Studio 5 (DS-5)][DS-5] v5.16.
### Building the Trusted Firmware
To build the software for the Base FVPs, follow these steps:
1. Clone the ARM Trusted Firmware repository from Github:
git clone https://github.com/ARM-software/arm-trusted-firmware.git
2. Change to the trusted firmware directory:
cd arm-trusted-firmware
3. Set the compiler path and build:
CROSS_COMPILE=<path/to>/aarch64-none-elf- make
By default this produces a release version of the build. To produce a debug
version instead, refer to the "Debugging options" section below.
The build creates ELF and raw binary files in the current directory. It
generates the following boot loader binary files from the ELF files:
* `bl1.bin`
* `bl2.bin`
* `bl31.bin`
4. Copy the above 3 boot loader binary files to the directory where the FVPs
are launched from. Symbolic links of the same names may be created instead.
5. (Optional) To clean the build directory use
make distclean
#### Debugging options
To compile a debug version and make the build more verbose use
CROSS_COMPILE=<path/to>/aarch64-none-elf- make DEBUG=1 V=1
AArch64 GCC uses DWARF version 4 debugging symbols by default. Some tools (for
example DS-5) might not support this and may need an older version of DWARF
symbols to be emitted by GCC. This can be achieved by using the
`-gdwarf-<version>` flag, with the version being set to 2 or 3. Setting the
version to 2 is recommended for DS-5 versions older than 5.16.
When debugging logic problems it might also be useful to disable all compiler
optimizations by using `-O0`.
NOTE: Using `-O0` could cause output images to be larger and base addresses
might need to be recalculated (see the later memory layout section).
Extra debug options can be passed to the build system by setting `CFLAGS`:
CFLAGS='-O0 -gdwarf-2' CROSS_COMPILE=<path/to>/aarch64-none-elf- make DEBUG=1 V=1
### Obtaining the normal world software
#### Obtaining UEFI
Download an archive of the [EDK2 (EFI Development Kit 2) source code][EDK2]
supporting the Base FVPs. EDK2 is an open source implementation of the UEFI
specification:
wget http://sourceforge.net/projects/edk2/files/ARM/aarch64-uefi-rev14582.tgz/download -O aarch64-uefi-rev14582.tgz
tar -xf aarch64-uefi-rev14582.tgz
To build the software for the Base FVPs, follow these steps:
1. Change into the unpacked EDK2 source directory
cd uefi
2. Copy build config templates to local workspace
export EDK_TOOLS_PATH=$(pwd)/BaseTools
. edksetup.sh $(pwd)/BaseTools/
3. Rebuild EDK2 host tools
make -C "$EDK_TOOLS_PATH" clean
make -C "$EDK_TOOLS_PATH"
4. Build the software
AARCH64GCC_TOOLS_PATH=<full-path-to-aarch64-gcc>/bin/ \
build -v -d3 -a AARCH64 -t ARMGCC \
-p ArmPlatformPkg/ArmVExpressPkg/ArmVExpress-FVP-AArch64.dsc
The EDK2 binary for use with the ARM Trusted Firmware can then be found
here:
Build/ArmVExpress-FVP-AArch64/DEBUG_ARMGCC/FV/FVP_AARCH64_EFI.fd
This will build EDK2 for the default settings as used by the FVPs.
To boot Linux using a VirtioBlock file-system, the command line passed from EDK2
to the Linux kernel must be modified as described in the "Obtaining a
File-system" section below.
If legacy GICv2 locations are used, the EDK2 platform description must be
updated. This is required as EDK2 does not support probing for the GIC location.
To do this, open the `ArmPlatformPkg/ArmVExpressPkg/ArmVExpress-FVP-AArch64.dsc`
file for editing and make the modifications as below. Rebuild EDK2 after doing a
`clean`.
gArmTokenSpaceGuid.PcdGicDistributorBase|0x2C001000
gArmTokenSpaceGuid.PcdGicInterruptInterfaceBase|0x2C002000
The EDK2 binary `FVP_AARCH64_EFI.fd` should be loaded into FVP FLASH0 via model
parameters as described in the "Running the Software" section below.
#### Obtaining a Linux kernel
The software has been verified using Linux kernel version 3.12-rc4. Patches
have been applied to the kernel in order to enable CPU hotplug.
Preparing a Linux kernel for use on the FVPs with hotplug support can
be done as follows (GICv2 support only):
1. Clone Linux:
git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
The CPU hotplug features are not yet included in the mainline kernel. To use
these, add the patches from Mark Rutland's kernel, based on Linux 3.12-rc4:
cd linux
git remote add -f --tags markr git://linux-arm.org/linux-mr.git
git checkout -b hotplug arm64-cpu-hotplug-20131023
2. Build with the Linaro GCC tools.
# in linux/
make mrproper
make ARCH=arm64 defconfig
# Enable Hotplug
make ARCH=arm64 menuconfig
# Kernel Features ---> [*] Support for hot-pluggable CPUs
CROSS_COMPILE=/path/to/aarch64-none-elf- make -j6 ARCH=arm64
3. Copy the Linux image `arch/arm64/boot/Image` to the working directory from
where the FVP is launched. A symbolic link may also be created instead.
#### Obtaining the Flattened Device Trees
Depending on the FVP configuration and Linux configuration used, different
FDT files are required. FDTs for the Base FVP can be found in the Trusted
Firmware source directory under `fdts`.
* `fvp-base-gicv2-psci.dtb`
(Default) For use with both AEMv8 and Cortex-A57-A53 Base FVPs with
default memory map configuration.
* `fvp-base-gicv2legacy-psci.dtb`
For use with both AEMv8 and Cortex-A57-A53 Base FVPs with legacy GICv2
memory map configuration.
* `fvp-base-gicv3-psci.dtb`
For use with AEMv8 Base FVP with default memory map configuration and
Linux GICv3 support.
Copy the chosen FDT blob as `fdt.dtb` to the directory from which the FVP
is launched. A symbolic link may also be created instead.
#### Obtaining a File-system
To prepare a Linaro LAMP based Open Embedded file-system, the following
instructions can be used as a guide. The file-system can be provided to Linux
via VirtioBlock or as a RAM-disk. Both methods are described below.
##### Prepare VirtioBlock
To prepare a VirtioBlock file-system, do the following:
1. Download and unpack the disk image.
NOTE: The unpacked disk image grows to 2 GiB in size.
wget http://releases.linaro.org/13.09/openembedded/aarch64/vexpress64-openembedded_lamp-armv8_20130927-7.img.gz
gunzip vexpress64-openembedded_lamp-armv8_20130927-7.img.gz
2. Make sure the Linux kernel has Virtio support enabled using
`make ARCH=arm64 menuconfig`.
Device Drivers ---> Virtio drivers ---> <*> Platform bus driver for memory mapped virtio devices
Device Drivers ---> [*] Block devices ---> <*> Virtio block driver
File systems ---> <*> The Extended 4 (ext4) filesystem
If some of these configurations are missing, enable them, save the kernel
configuration, then rebuild the kernel image using the instructions provided
in the section "Obtaining a Linux kernel".
3. Change the Kernel command line to include `root=/dev/vda2`. This can either
be done in the EDK2 boot menu or in the platform file. Editing the platform
file and rebuilding EDK2 will make the change persist. To do this:
1. In EDK, edit the following file:
ArmPlatformPkg/ArmVExpressPkg/ArmVExpress-FVP-AArch64.dsc
2. Add `root=/dev/vda2` to:
gArmPlatformTokenSpaceGuid.PcdDefaultBootArgument|"<Other default options>"
3. Remove the entry:
gArmPlatformTokenSpaceGuid.PcdDefaultBootInitrdPath|""
4. Rebuild EDK2 (see "Obtaining UEFI" section above).
4. The file-system image file should be provided to the model environment by
passing it the correct command line option. In the Base FVP the following
option should be provided in addition to the ones described in the
"Running the software" section below.
NOTE: A symbolic link to this file cannot be used with the FVP; the path
to the real file must be provided.
-C bp.virtioblockdevice.image_path="<path/to/>vexpress64-openembedded_lamp-armv8_20130927-7.img"
5. Ensure that the FVP doesn't output any error messages. If the following
error message is displayed:
ERROR: BlockDevice: Failed to open "vexpress64-openembedded_lamp-armv8_20130927-7.img"!
then make sure the path to the file-system image in the model parameter is
correct and that read permission is correctly set on the file-system image
file.
##### Prepare RAM-disk
NOTE: The RAM-disk option does not currently work with the Linux kernel version
described above; use the VirtioBlock method instead. For further information
please see the "Known issues" section in the [Change Log].
To Prepare a RAM-disk file-system, do the following:
1. Download the file-system image:
wget http://releases.linaro.org/13.09/openembedded/aarch64/linaro-image-lamp-genericarmv8-20130912-487.rootfs.tar.gz
2. Modify the Linaro image:
# Prepare for use as RAM-disk. Normally use MMC, NFS or VirtioBlock.
# Be careful, otherwise you could damage your host file-system.
mkdir tmp; cd tmp
sudo sh -c "zcat ../linaro-image-lamp-genericarmv8-20130912-487.rootfs.tar.gz | cpio -id"
sudo ln -s sbin/init .
sudo ln -s S35mountall.sh etc/rcS.d/S03mountall.sh
sudo sh -c "echo 'devtmpfs /dev devtmpfs mode=0755,nosuid 0 0' >> etc/fstab"
sudo sh -c "find . | cpio --quiet -H newc -o | gzip -3 -n > ../filesystem.cpio.gz"
cd ..
3. Copy the resultant `filesystem.cpio.gz` to the directory where the FVP is
launched from. A symbolic link may also be created instead.
### Running the software
This release of the ARM Trusted Firmware has been tested on the following ARM
FVPs (64-bit versions only).
* `FVP_Base_AEMv8A-AEMv8A` (Version 5.1 build 8)
* `FVP_Base_Cortex-A57x4-A53x4` (Version 5.1 build 8)
Please refer to the FVP documentation for a detailed description of the model
parameter options. A brief description of the important ones that affect the
ARM Trusted Firmware and normal world software behavior is provided below.
#### Running on the AEMv8 Base FVP
The following `FVP_Base_AEMv8A-AEMv8A` parameters should be used to boot Linux
with 8 CPUs using the ARM Trusted Firmware.
NOTE: Using `cache_state_modelled=1` makes booting very slow. The software will
still work (and run much faster) without this option but this will hide any
cache maintenance defects in the software.
NOTE: Using the `-C bp.virtioblockdevice.image_path` parameter is not necessary
if a Linux RAM-disk file-system is used (see the "Obtaining a File-system"
section above).
FVP_Base_AEMv8A-AEMv8A \
-C pctl.startup=0.0.0.0 \
-C bp.secure_memory=0 \
-C cluster0.NUM_CORES=4 \
-C cluster1.NUM_CORES=4 \
-C cache_state_modelled=1 \
-C bp.pl011_uart0.untimed_fifos=1 \
-C bp.secureflashloader.fname=<path to bl1.bin> \
-C bp.flashloader0.fname=<path to UEFI binary> \
-C bp.virtioblockdevice.image_path="<path/to/>vexpress64-openembedded_lamp-armv8_20130927-7.img"
#### Running on the Cortex-A57-A53 Base FVP
The following `FVP_Base_Cortex-A57x4-A53x4` model parameters should be used to
boot Linux with 8 CPUs using the ARM Trusted Firmware.
NOTE: Using `cache_state_modelled=1` makes booting very slow. The software will
still work (and run much faster) without this option but this will hide any
cache maintenance defects in the software.
NOTE: Using the `-C bp.virtioblockdevice.image_path` parameter is not necessary
if a Linux RAM-disk file-system is used (see the "Obtaining a File-system"
section above).
FVP_Base_Cortex-A57x4-A53x4 \
-C pctl.startup=0.0.0.0 \
-C bp.secure_memory=0 \
-C cache_state_modelled=1 \
-C bp.pl011_uart0.untimed_fifos=1 \
-C bp.secureflashloader.fname=<path to bl1.bin> \
-C bp.flashloader0.fname=<path to UEFI binary> \
-C bp.virtioblockdevice.image_path="<path/to/>vexpress64-openembedded_lamp-armv8_20130927-7.img"
### Configuring the GICv2 memory map
The Base FVP models support GICv2 with the default model parameters at the
following addresses.
GICv2 Distributor Interface 0x2f000000
GICv2 CPU Interface 0x2c000000
GICv2 Virtual CPU Interface 0x2c010000
GICv2 Hypervisor Interface 0x2c02f000
The models can be configured to support GICv2 at addresses corresponding to the
legacy (Versatile Express) memory map as follows.
GICv2 Distributor Interface 0x2c001000
GICv2 CPU Interface 0x2c002000
GICv2 Virtual CPU Interface 0x2c004000
GICv2 Hypervisor Interface 0x2c006000
The choice of memory map is reflected in the build field (bits[15:12]) in the
`SYS_ID` register (Offset `0x0`) in the Versatile Express System registers
memory map (`0x1c010000`).
* `SYS_ID.Build[15:12]`
`0x1` corresponds to the presence of the default GICv2 memory map. This is
the default value.
* `SYS_ID.Build[15:12]`
`0x0` corresponds to the presence of the Legacy VE GICv2 memory map. This
value can be configured as described in the next section.
NOTE: If the legacy VE GICv2 memory map is used, then the corresponding FDT and
UEFI images should be used.
#### Configuring AEMv8 Base FVP for legacy VE memory map
The following parameters configure the GICv2 memory map in legacy VE mode:
NOTE: Using the `-C bp.virtioblockdevice.image_path` parameter is not necessary
if a Linux RAM-disk file-system is used (see the "Obtaining a File-system"
section above).
FVP_Base_AEMv8A-AEMv8A \
-C cluster0.gic.GICD-offset=0x1000 \
-C cluster0.gic.GICC-offset=0x2000 \
-C cluster0.gic.GICH-offset=0x4000 \
-C cluster0.gic.GICH-other-CPU-offset=0x5000 \
-C cluster0.gic.GICV-offset=0x6000 \
-C cluster0.gic.PERIPH-size=0x8000 \
-C cluster1.gic.GICD-offset=0x1000 \
-C cluster1.gic.GICC-offset=0x2000 \
-C cluster1.gic.GICH-offset=0x4000 \
-C cluster1.gic.GICH-other-CPU-offset=0x5000 \
-C cluster1.gic.GICV-offset=0x6000 \
-C cluster1.gic.PERIPH-size=0x8000 \
-C gic_distributor.GICD-alias=0x2c001000 \
-C bp.variant=0x0 \
-C bp.virtioblockdevice.image_path="<path/to/>vexpress64-openembedded_lamp-armv8_20130927-7.img"
The last parameter sets the build variant field of the `SYS_ID` register to
`0x0`. This allows the ARM Trusted Firmware to detect the legacy VE memory map
while configuring the GIC.
#### Configuring Cortex-A57-A53 Base FVP for legacy VE memory map
Configuration of the GICv2 as per the legacy VE memory map is controlled by
the following parameter. In this case, separate configuration of the `SYS_ID`
register is not required.
NOTE: Using the `-C bp.virtioblockdevice.image_path` parameter is not necessary
if a Linux RAM-disk file-system is used (see the "Obtaining a File-system"
section above).
FVP_Base_Cortex-A57x4-A53x4 \
-C legacy_gicv2_map=1 \
-C bp.virtioblockdevice.image_path="<path/to/>vexpress64-openembedded_lamp-armv8_20130927-7.img"
3. Firmware Design
-------------------
The cold boot path starts when the platform is physically turned on. One of
the CPUs released from reset is chosen as the primary CPU, and the remaining
CPUs are considered secondary CPUs. The primary CPU is chosen through
platform-specific means. The cold boot path is mainly executed by the primary
CPU, other than essential CPU initialization executed by all CPUs. The
secondary CPUs are kept in a safe platform-specific state until the primary
CPU has performed enough initialization to boot them.
The cold boot path in this implementation of the ARM Trusted Firmware is divided
into three stages (in order of execution):
* Boot Loader stage 1 (BL1)
* Boot Loader stage 2 (BL2)
* Boot Loader stage 3 (BL3-1). The '1' distinguishes this from other 3rd level
boot loader stages.
The ARM Fixed Virtual Platforms (FVPs) provide trusted ROM, trusted SRAM and
trusted DRAM regions. Each boot loader stage uses one or more of these
memories for its code and data.
### BL1
This stage begins execution from the platform's reset vector in trusted ROM at
EL3. BL1 code starts at `0x00000000` (trusted ROM) in the FVP memory map. The
BL1 data section is placed at the start of trusted SRAM, `0x04000000`. The
functionality implemented by this stage is as follows.
#### Determination of boot path
Whenever a CPU is released from reset, BL1 needs to distinguish between a warm
boot and a cold boot. This is done using a platform-specific mechanism. The
ARM FVPs implement a simple power controller at `0x1c100000`. The `PSYS`
register (`0x10`) is used to distinguish between a cold and warm boot. This
information is contained in the `PSYS.WK[25:24]` field. Additionally, a
per-CPU mailbox is maintained in trusted DRAM (`0x00600000`), to which BL1
writes an entrypoint. Each CPU jumps to this entrypoint upon warm boot. During
cold boot, BL1 places the secondary CPUs in a safe platform-specific state while
the primary CPU executes the remaining cold boot path as described in the
following sections.
#### Architectural initialization
BL1 performs minimal architectural initialization as follows.
* Exception vectors
BL1 sets up simple exception vectors for both synchronous and asynchronous
exceptions. The default behavior upon receiving an exception is to set a
status code. In the case of the FVP this code is written to the Versatile
Express System LED register in the following format:
SYS_LED[0] - Security state (Secure=0/Non-Secure=1)
SYS_LED[2:1] - Exception Level (EL3=0x3, EL2=0x2, EL1=0x1, EL0=0x0)
SYS_LED[7:3] - Exception Class (Sync/Async & origin). The values for
each exception class are:
0x0 : Synchronous exception from Current EL with SP_EL0
0x1 : IRQ exception from Current EL with SP_EL0
0x2 : FIQ exception from Current EL with SP_EL0
0x3 : System Error exception from Current EL with SP_EL0
0x4 : Synchronous exception from Current EL with SP_ELx
0x5 : IRQ exception from Current EL with SP_ELx
0x6 : FIQ exception from Current EL with SP_ELx
0x7 : System Error exception from Current EL with SP_ELx
0x8 : Synchronous exception from Lower EL using aarch64
0x9 : IRQ exception from Lower EL using aarch64
0xa : FIQ exception from Lower EL using aarch64
0xb : System Error exception from Lower EL using aarch64
0xc : Synchronous exception from Lower EL using aarch32
0xd : IRQ exception from Lower EL using aarch32
0xe : FIQ exception from Lower EL using aarch32
0xf : System Error exception from Lower EL using aarch32
A write to the LED register reflects in the System LEDs (S6LED0..7) in the
CLCD window of the FVP. This behavior is because this boot loader stage
does not expect to receive any exceptions other than the SMC exception.
For the latter, BL1 installs a simple stub. The stub expects to receive
only a single type of SMC (determined by its function ID in the general
purpose register `X0`). This SMC is raised by BL2 to make BL1 pass control
to BL3-1 (loaded by BL2) at EL3. Any other SMC leads to an assertion
failure.
* MMU setup
BL1 sets up EL3 memory translation by creating page tables to cover the
first 4GB of physical address space. This covers all the memories and
peripherals needed by BL1.
* Control register setup
- `SCTLR_EL3`. Instruction cache is enabled by setting the `SCTLR_EL3.I`
bit. Alignment and stack alignment checking is enabled by setting the
`SCTLR_EL3.A` and `SCTLR_EL3.SA` bits. Exception endianness is set to
little-endian by clearing the `SCTLR_EL3.EE` bit.
- `CPUECTLR`. When the FVP includes a model of a specific ARM processor
implementation (for example A57 or A53), then intra-cluster coherency is
enabled by setting the `CPUECTLR.SMPEN` bit. The AEMv8 Base FVP is
inherently coherent so does not implement `CPUECTLR`.
- `SCR`. Use of the HVC instruction from EL1 is enabled by setting the
`SCR.HCE` bit. FIQ exceptions are configured to be taken in EL3 by
setting the `SCR.FIQ` bit. The register width of the next lower
exception level is set to AArch64 by setting the `SCR.RW` bit.
- `CPTR_EL3`. Accesses to the `CPACR` from EL1 or EL2, or the `CPTR_EL2`
from EL2 are configured to not trap to EL3 by clearing the
`CPTR_EL3.TCPAC` bit. Instructions that access the registers associated
with Floating Point and Advanced SIMD execution are configured to not
trap to EL3 by clearing the `CPTR_EL3.TFP` bit.
- `CNTFRQ_EL0`. The `CNTFRQ_EL0` register is programmed with the base
frequency of the system counter, which is retrieved from the first entry
in the frequency modes table.
- Generic Timer. The system level implementation of the generic timer is
enabled through the memory mapped interface.
#### Platform initialization
BL1 enables issuing of snoop and DVM (Distributed Virtual Memory) requests from
the CCI-400 slave interface corresponding to the cluster that includes the
primary CPU. BL1 also initializes UART0 (PL011 console), which enables access to
the `printf` family of functions.
#### BL2 image load and execution
BL1 execution continues as follows:
1. BL1 determines the amount of free trusted SRAM memory available by
calculating the extent of its own data section, which also resides in
trusted SRAM. BL1 loads a BL2 raw binary image through semi-hosting, at a
platform-specific base address. The filename of the BL2 raw binary image on
the host file system must be `bl2.bin`. If the BL2 image file is not present
or if there is not enough free trusted SRAM the following error message
is printed:
"Failed to load boot loader stage 2 (BL2) firmware."
If the load is successful, BL1 updates the limits of the remaining free
trusted SRAM. It also populates information about the amount of trusted
SRAM used by the BL2 image. The exact load location of the image is
provided as a base address in the platform header. Further description of
the memory layout can be found later in this document.
2. BL1 prints the following string from the primary CPU to indicate successful
execution of the BL1 stage:
"Booting trusted firmware boot loader stage 1"
3. BL1 passes control to the BL2 image at Secure EL1, starting from its load
address.
4. BL1 also passes information about the amount of trusted SRAM used and
available for use. This information is populated at a platform-specific
memory address.
### BL2
BL1 loads and passes control to BL2 at Secure EL1. BL2 is linked against and
loaded at a platform-specific base address (more information can found later
in this document). The functionality implemented by BL2 is as follows.
#### Architectural initialization
BL2 performs minimal architectural initialization required for subsequent
stages of the ARM Trusted Firmware and normal world software. It sets up
Secure EL1 memory translation by creating page tables to address the first 4GB
of the physical address space in a similar way to BL1. EL1 and EL0 are given
access to Floating Point & Advanced SIMD registers by clearing the `CPACR.FPEN`
bits.
#### Platform initialization
BL2 does not perform any platform initialization that affects subsequent
stages of the ARM Trusted Firmware or normal world software. It copies the
information regarding the trusted SRAM populated by BL1 using a
platform-specific mechanism. It also calculates the limits of DRAM (main memory)
to determine whether there is enough space to load the normal world software
images. A platform defined base address is used to specify the load address for
the BL3-1 image.
#### Normal world image load
BL2 loads a rich boot firmware image (UEFI). The image executes in the normal
world. BL2 relies on BL3-1 to pass control to the normal world software image it
loads. Hence, BL2 populates a platform-specific area of memory with the
entrypoint and Current Program Status Register (`CPSR`) of the normal world
software image. The entrypoint is the load address of the normal world software
image. The `CPSR` is determined as specified in Section 5.13 of the [PSCI PDD]
[PSCI]. This information is passed to BL3-1.
##### UEFI firmware load
By default, BL2 assumes the UEFI image is present at the base of NOR flash0
(`0x08000000`), and arranges for BL3-1 to pass control to that location. As
mentioned earlier, BL2 populates platform-specific memory with the entrypoint
and `CPSR` of the UEFI image.
#### BL3-1 image load and execution
BL2 execution continues as follows:
1. BL2 loads the BL3-1 image into a platform-specific address in trusted SRAM.
This is done using semi-hosting. The image is identified by the file
`bl31.bin` on the host file-system. If there is not enough memory to load
the image or the image is missing it leads to an assertion failure. If the
BL3-1 image loads successfully, BL1 updates the amount of trusted SRAM used
and available for use by BL3-1. This information is populated at a
platform-specific memory address.
2. BL2 passes control back to BL1 by raising an SMC, providing BL1 with the
BL3-1 entrypoint. The exception is handled by the SMC exception handler
installed by BL1.
3. BL1 turns off the MMU and flushes the caches. It clears the
`SCTLR_EL3.M/I/C` bits, flushes the data cache to the point of coherency
and invalidates the TLBs.
4. BL1 passes control to BL3-1 at the specified entrypoint at EL3.
### BL3-1
The image for this stage is loaded by BL2 and BL1 passes control to BL3-1 at
EL3. BL3-1 executes solely in trusted SRAM. BL3-1 is linked against and
loaded at a platform-specific base address (more information can found later
in this document). The functionality implemented by BL3-1 is as follows.
#### Architectural initialization
Currently, BL3-1 performs a similar architectural initialization to BL1 as
far as system register settings are concerned. Since BL1 code resides in ROM,
architectural initialization in BL3-1 allows override of any previous
initialization done by BL1. BL3-1 creates page tables to address the first
4GB of physical address space and initializes the MMU accordingly. It replaces
the exception vectors populated by BL1 with its own. BL3-1 exception vectors
signal error conditions in the same way as BL1 does if an unexpected
exception is raised. They implement more elaborate support for handling SMCs
since this is the only mechanism to access the runtime services implemented by
BL3-1 (PSCI for example). BL3-1 checks each SMC for validity as specified by
the [SMC calling convention PDD][SMCCC] before passing control to the required
SMC handler routine.
#### Platform initialization
BL3-1 performs detailed platform initialization, which enables normal world
software to function correctly. It also retrieves entrypoint information for
the normal world software image loaded by BL2 from the platform defined
memory address populated by BL2.
* GICv2 initialization:
- Enable group0 interrupts in the GIC CPU interface.
- Configure group0 interrupts to be asserted as FIQs.
- Disable the legacy interrupt bypass mechanism.
- Configure the priority mask register to allow interrupts of all
priorities to be signaled to the CPU interface.
- Mark SGIs 8-15, the secure physical timer interrupt (#29) and the
trusted watchdog interrupt (#56) as group0 (secure).
- Target the trusted watchdog interrupt to CPU0.
- Enable these group0 interrupts in the GIC distributor.
- Configure all other interrupts as group1 (non-secure).
- Enable signaling of group0 interrupts in the GIC distributor.
* GICv3 initialization:
If a GICv3 implementation is available in the platform, BL3-1 initializes
the GICv3 in GICv2 emulation mode with settings as described for GICv2
above.
* Power management initialization:
BL3-1 implements a state machine to track CPU and cluster state. The state
can be one of `OFF`, `ON_PENDING`, `SUSPEND` or `ON`. All secondary CPUs are
initially in the `OFF` state. The cluster that the primary CPU belongs to is
`ON`; any other cluster is `OFF`. BL3-1 initializes the data structures that
implement the state machine, including the locks that protect them. BL3-1
accesses the state of a CPU or cluster immediately after reset and before
the MMU is enabled in the warm boot path. It is not currently possible to
use 'exclusive' based spinlocks, therefore BL3-1 uses locks based on
Lamport's Bakery algorithm instead. BL3-1 allocates these locks in device
memory. They are accessible irrespective of MMU state.
* Runtime services initialization:
The only runtime service implemented by BL3-1 is PSCI. The complete PSCI API
is not yet implemented. The following functions are currently implemented:
- `PSCI_VERSION`
- `CPU_OFF`
- `CPU_ON`
- `AFFINITY_INFO`
The `CPU_ON` and `CPU_OFF` functions implement the warm boot path in ARM
Trusted Firmware. These are the only functions which have been tested.
`AFFINITY_INFO` & `PSCI_VERSION` are present but completely untested in
this release.
Unsupported PSCI functions that can return, return the `NOT_SUPPORTED`
(`-1`) error code. Other unsupported PSCI functions that don't return,
signal an assertion failure.
BL3-1 returns the error code `-1` if an SMC is raised for any other runtime
service. This behavior is mandated by the [SMC calling convention PDD]
[SMCCC].
### Normal world software execution
BL3-1 uses the entrypoint information provided by BL2 to jump to the normal
world software image at the highest available Exception Level (EL2 if
available, otherwise EL1).
### Memory layout on Base FVP ###
The current implementation of the image loader has some limitations. It is
designed to load images dynamically, at a load address chosen to minimize memory
fragmentation. The chosen image location can be either at the top or the bottom
of free memory. However, until this feature is fully functional, the code also
contains support for loading images at a link-time fixed address. The code that
dynamically calculates the load address is bypassed and the load address is
specified statically by the platform.
BL1 is always loaded at address `0x0`. BL2 and BL3-1 are loaded at specified
locations in Trusted SRAM. The lack of dynamic image loader support means these
load addresses must currently be adjusted as the code grows. The individual
images must be linked against their ultimate runtime locations.
BL2 is loaded near the top of the Trusted SRAM. BL3-1 is loaded between BL1
and BL2. As a general rule, the following constraints must always be enforced:
1. `BL2_MAX_ADDR <= (<Top of Trusted SRAM>)`
2. `BL31_BASE >= BL1_MAX_ADDR`
3. `BL2_BASE >= BL31_MAX_ADDR`
Constraint 1 is enforced by BL2's linker script. If it is violated then the
linker will report an error while building BL2 to indicate that it doesn't
fit. For example:
aarch64-none-elf-ld: address 0x40400c8 of bl2.elf section `.bss' is not
within region `RAM'
This error means that the BL2 base address needs to be moved down. Be sure that
the new BL2 load address still obeys constraint 3.
Constraints 2 & 3 must currently be checked by hand. To ensure they are
enforced, first determine the maximum addresses used by BL1 and BL3-1. This can
be deduced from the link map files of the different images.
The BL1 link map file (`bl1.map`) gives these 2 values:
* `FIRMWARE_RAM_COHERENT_START`
* `FIRMWARE_RAM_COHERENT_SIZE`
The maximum address used by BL1 can then be easily determined:
BL1_MAX_ADDR = FIRMWARE_RAM_COHERENT_START + FIRMWARE_RAM_COHERENT_SIZE
The BL3-1 link map file (`bl31.map`) gives the following value:
* `BL31_DATA_STOP`. This is the the maximum address used by BL3-1.
The current implementation can result in wasted space because a simplified
`meminfo` structure represents the extents of free memory. For example, to load
BL2 at address `0x04020000`, the resulting memory layout should be as follows:
------------ 0x04040000
| | <- Free space (1)
|----------|
| BL2 |
|----------| BL2_BASE (0x0402D000)
| | <- Free space (2)
|----------|
| BL1 |
------------ 0x04000000
In the current implementation, we need to specify whether BL2 is loaded at the
top or bottom of the free memory. BL2 is top-loaded so in the example above,
the free space (1) above BL2 is hidden, resulting in the following view of
memory:
------------ 0x04040000
| |
| |
| BL2 |
|----------| BL2_BASE (0x0402D000)
| | <- Free space (2)
|----------|
| BL1 |
------------ 0x04000000
BL3-1 is bottom-loaded above BL1. For example, if BL3-1 is bottom-loaded at
`0x0400E000`, the memory layout should look like this:
------------ 0x04040000
| |
| |
| BL2 |
|----------| BL2_BASE (0x0402D000)
| | <- Free space (2)
| |
|----------|
| |
| BL31 |
|----------| BL31_BASE (0x0400E000)
| | <- Free space (3)
|----------|
| BL1 |
------------ 0x04000000
But the free space (3) between BL1 and BL3-1 is wasted, resulting in the
following view:
------------ 0x04040000
| |
| |
| BL2 |
|----------| BL2_BASE (0x0402D000)
| | <- Free space (2)
| |
|----------|
| |
| |
| BL31 | BL31_BASE (0x0400E000)
| |
|----------|
| BL1 |
------------ 0x04000000
### Code Structure ###
Trusted Firmware code is logically divided between the three boot loader
stages mentioned in the previous sections. The code is also divided into the
following categories (present as directories in the source code):
* **Architecture specific.** This could be AArch32 or AArch64.
* **Platform specific.** Choice of architecture specific code depends upon
the platform.
* **Common code.** This is platform and architecture agnostic code.
* **Library code.** This code comprises of functionality commonly used by all
other code.
* **Stage specific.** Code specific to a boot stage.
* **Drivers.**
Each boot loader stage uses code from one or more of the above mentioned
categories. Based upon the above, the code layout looks like this:
Directory Used by BL1? Used by BL2? Used by BL3?
bl1 Yes No No
bl2 No Yes No
bl31 No No Yes
arch Yes Yes Yes
plat Yes Yes Yes
drivers Yes No Yes
common Yes Yes Yes
lib Yes Yes Yes
All assembler files have the `.S` extension. The linker files for each boot
stage has the `.ld.S` extension. These are processed by GCC to create the
resultant `.ld` files used for linking.
FDTs provide a description of the hardware platform and is used by the Linux
kernel at boot time. These can be found in the `fdts` directory.
4. References
--------------
1. Trusted Board Boot Requirements CLIENT PDD (ARM DEN 0006B-5). Available
under NDA through your ARM account representative.
2. [Power State Coordination Interface PDD (ARM DEN 0022B.b)][PSCI].
3. [SMC Calling Convention PDD (ARM DEN 0028A)][SMCCC].
- - - - - - - - - - - - - - - - - - - - - - - - - -
_Copyright (c) 2013 ARM Ltd. All rights reserved._
[Change Log]: change-log.md
[Linaro Toolchain]: http://releases.linaro.org/13.09/components/toolchain/binaries/
[EDK2]: http://sourceforge.net/projects/edk2/files/ARM/aarch64-uefi-rev14582.tgz/download
[DS-5]: http://www.arm.com/products/tools/software-tools/ds-5/index.php
[PSCI]: http://infocenter.arm.com/help/topic/com.arm.doc.den0022b/index.html "Power State Coordination Interface PDD (ARM DEN 0022B.b)"
[SMCCC]: http://infocenter.arm.com/help/topic/com.arm.doc.den0028a/index.html "SMC Calling Convention PDD (ARM DEN 0028A)"
/*
* Copyright (c) 2013, ARM Limited. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of ARM nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <assert.h>
#include <platform.h>
#include <cci400.h>
static inline unsigned long get_slave_iface_base(unsigned long mpidr)
{
return CCI400_BASE + SLAVE_IFACE_OFFSET(CCI400_SL_IFACE_INDEX(mpidr));
}
void cci_enable_coherency(unsigned long mpidr)
{
/* Enable Snoops and DVM messages */
mmio_write_32(get_slave_iface_base(mpidr) + SNOOP_CTRL_REG,
DVM_EN_BIT | SNOOP_EN_BIT);
/* Wait for the dust to settle down */
while (mmio_read_32(CCI400_BASE + STATUS_REG) & CHANGE_PENDING_BIT);
}
void cci_disable_coherency(unsigned long mpidr)
{
/* Disable Snoops and DVM messages */
mmio_write_32(get_slave_iface_base(mpidr) + SNOOP_CTRL_REG,
~(DVM_EN_BIT | SNOOP_EN_BIT));
/* Wait for the dust to settle down */
while (mmio_read_32(CCI400_BASE + STATUS_REG) & CHANGE_PENDING_BIT);
}
/*
* Copyright (c) 2013, ARM Limited. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of ARM nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __CCI_400_H__
#define __CCI_400_H__
/* Slave interface offsets from PERIPHBASE */
#define SLAVE_IFACE4_OFFSET 0x5000
#define SLAVE_IFACE3_OFFSET 0x4000
#define SLAVE_IFACE2_OFFSET 0x3000
#define SLAVE_IFACE1_OFFSET 0x2000
#define SLAVE_IFACE0_OFFSET 0x1000
#define SLAVE_IFACE_OFFSET(index) SLAVE_IFACE0_OFFSET + (0x1000 * index)
/* Control and ID register offsets */
#define CTRL_OVERRIDE_REG 0x0
#define SPEC_CTRL_REG 0x4
#define SECURE_ACCESS_REG 0x8
#define STATUS_REG 0xc
#define IMPRECISE_ERR_REG 0x10
#define PERFMON_CTRL_REG 0x100
/* Slave interface register offsets */
#define SNOOP_CTRL_REG 0x0
#define SH_OVERRIDE_REG 0x4
#define READ_CHNL_QOS_VAL_OVERRIDE_REG 0x100
#define WRITE_CHNL_QOS_VAL_OVERRIDE_REG 0x104
#define QOS_CTRL_REG 0x10c
#define MAX_OT_REG 0x110
#define TARGET_LATENCY_REG 0x130
#define LATENCY_REGULATION_REG 0x134
#define QOS_RANGE_REG 0x138
/* Snoop Control register bit definitions */
#define DVM_EN_BIT (1 << 1)
#define SNOOP_EN_BIT (1 << 0)
/* Status register bit definitions */
#define CHANGE_PENDING_BIT (1 << 0)
/* Function declarations */
extern void cci_enable_coherency(unsigned long mpidr);
extern void cci_disable_coherency(unsigned long mpidr);
#endif /* __CCI_400_H__ */
/*
* Copyright (c) 2013, ARM Limited. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of ARM nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __CONSOLE_H__
#define __CONSOLE_H__
void console_init(void);
int console_putc(int c);
int console_getc(void);
#endif /* __CONSOLE_H__ */
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment