Merge branch 'master' of git://git.denx.de/u-boot-arm

This commit is contained in:
Tom Rini 2013-10-04 13:17:48 -04:00
commit f835c77fb7
9 changed files with 476 additions and 2 deletions

View File

@ -20,6 +20,11 @@ ifneq ($(CONFIG_AM43XX)$(CONFIG_AM33XX)$(CONFIG_OMAP44XX)$(CONFIG_OMAP54XX)$(CON
SOBJS += lowlevel_init.o
endif
ifneq ($(CONFIG_ARMV7_NONSEC)$(CONFIG_ARMV7_VIRT),)
SOBJS += nonsec_virt.o
COBJS += virt-v7.o
endif
SRCS := $(START:.o=.S) $(COBJS:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS) $(SOBJS))
START := $(addprefix $(obj),$(START))

View File

@ -0,0 +1,208 @@
/*
* code for switching cores into non-secure state and into HYP mode
*
* Copyright (c) 2013 Andre Przywara <andre.przywara@linaro.org>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <config.h>
#include <linux/linkage.h>
#include <asm/gic.h>
#include <asm/armv7.h>
.arch_extension sec
.arch_extension virt
/* the vector table for secure state and HYP mode */
_monitor_vectors:
.word 0 /* reset */
.word 0 /* undef */
adr pc, _secure_monitor
.word 0
.word 0
adr pc, _hyp_trap
.word 0
.word 0
/*
* secure monitor handler
* U-boot calls this "software interrupt" in start.S
* This is executed on a "smc" instruction, we use a "smc #0" to switch
* to non-secure state.
* We use only r0 and r1 here, due to constraints in the caller.
*/
.align 5
_secure_monitor:
mrc p15, 0, r1, c1, c1, 0 @ read SCR
bic r1, r1, #0x4e @ clear IRQ, FIQ, EA, nET bits
orr r1, r1, #0x31 @ enable NS, AW, FW bits
#ifdef CONFIG_ARMV7_VIRT
mrc p15, 0, r0, c0, c1, 1 @ read ID_PFR1
and r0, r0, #CPUID_ARM_VIRT_MASK @ mask virtualization bits
cmp r0, #(1 << CPUID_ARM_VIRT_SHIFT)
orreq r1, r1, #0x100 @ allow HVC instruction
#endif
mcr p15, 0, r1, c1, c1, 0 @ write SCR (with NS bit set)
#ifdef CONFIG_ARMV7_VIRT
mrceq p15, 0, r0, c12, c0, 1 @ get MVBAR value
mcreq p15, 4, r0, c12, c0, 0 @ write HVBAR
#endif
movs pc, lr @ return to non-secure SVC
_hyp_trap:
mrs lr, elr_hyp @ for older asm: .byte 0x00, 0xe3, 0x0e, 0xe1
mov pc, lr @ do no switch modes, but
@ return to caller
/*
* Secondary CPUs start here and call the code for the core specific parts
* of the non-secure and HYP mode transition. The GIC distributor specific
* code has already been executed by a C function before.
* Then they go back to wfi and wait to be woken up by the kernel again.
*/
ENTRY(_smp_pen)
mrs r0, cpsr
orr r0, r0, #0xc0
msr cpsr, r0 @ disable interrupts
ldr r1, =_start
mcr p15, 0, r1, c12, c0, 0 @ set VBAR
bl _nonsec_init
mov r12, r0 @ save GICC address
#ifdef CONFIG_ARMV7_VIRT
bl _switch_to_hyp
#endif
ldr r1, [r12, #GICC_IAR] @ acknowledge IPI
str r1, [r12, #GICC_EOIR] @ signal end of interrupt
adr r0, _smp_pen @ do not use this address again
b smp_waitloop @ wait for IPIs, board specific
ENDPROC(_smp_pen)
/*
* Switch a core to non-secure state.
*
* 1. initialize the GIC per-core interface
* 2. allow coprocessor access in non-secure modes
* 3. switch the cpu mode (by calling "smc #0")
*
* Called from smp_pen by secondary cores and directly by the BSP.
* Do not assume that the stack is available and only use registers
* r0-r3 and r12.
*
* PERIPHBASE is used to get the GIC address. This could be 40 bits long,
* though, but we check this in C before calling this function.
*/
ENTRY(_nonsec_init)
#ifdef CONFIG_ARM_GIC_BASE_ADDRESS
ldr r2, =CONFIG_ARM_GIC_BASE_ADDRESS
#else
mrc p15, 4, r2, c15, c0, 0 @ read CBAR
bfc r2, #0, #15 @ clear reserved bits
#endif
add r3, r2, #GIC_DIST_OFFSET @ GIC dist i/f offset
mvn r1, #0 @ all bits to 1
str r1, [r3, #GICD_IGROUPRn] @ allow private interrupts
mrc p15, 0, r0, c0, c0, 0 @ read MIDR
ldr r1, =MIDR_PRIMARY_PART_MASK
and r0, r0, r1 @ mask out variant and revision
ldr r1, =MIDR_CORTEX_A7_R0P0 & MIDR_PRIMARY_PART_MASK
cmp r0, r1 @ check for Cortex-A7
ldr r1, =MIDR_CORTEX_A15_R0P0 & MIDR_PRIMARY_PART_MASK
cmpne r0, r1 @ check for Cortex-A15
movne r1, #GIC_CPU_OFFSET_A9 @ GIC CPU offset for A9
moveq r1, #GIC_CPU_OFFSET_A15 @ GIC CPU offset for A15/A7
add r3, r2, r1 @ r3 = GIC CPU i/f addr
mov r1, #1 @ set GICC_CTLR[enable]
str r1, [r3, #GICC_CTLR] @ and clear all other bits
mov r1, #0xff
str r1, [r3, #GICC_PMR] @ set priority mask register
movw r1, #0x3fff
movt r1, #0x0006
mcr p15, 0, r1, c1, c1, 2 @ NSACR = all copros to non-sec
/* The CNTFRQ register of the generic timer needs to be
* programmed in secure state. Some primary bootloaders / firmware
* omit this, so if the frequency is provided in the configuration,
* we do this here instead.
* But first check if we have the generic timer.
*/
#ifdef CONFIG_SYS_CLK_FREQ
mrc p15, 0, r0, c0, c1, 1 @ read ID_PFR1
and r0, r0, #CPUID_ARM_GENTIMER_MASK @ mask arch timer bits
cmp r0, #(1 << CPUID_ARM_GENTIMER_SHIFT)
ldreq r1, =CONFIG_SYS_CLK_FREQ
mcreq p15, 0, r1, c14, c0, 0 @ write CNTFRQ
#endif
adr r1, _monitor_vectors
mcr p15, 0, r1, c12, c0, 1 @ set MVBAR to secure vectors
mrc p15, 0, ip, c12, c0, 0 @ save secure copy of VBAR
isb
smc #0 @ call into MONITOR mode
mcr p15, 0, ip, c12, c0, 0 @ write non-secure copy of VBAR
mov r1, #1
str r1, [r3, #GICC_CTLR] @ enable non-secure CPU i/f
add r2, r2, #GIC_DIST_OFFSET
str r1, [r2, #GICD_CTLR] @ allow private interrupts
mov r0, r3 @ return GICC address
bx lr
ENDPROC(_nonsec_init)
#ifdef CONFIG_SMP_PEN_ADDR
/* void __weak smp_waitloop(unsigned previous_address); */
ENTRY(smp_waitloop)
wfi
ldr r1, =CONFIG_SMP_PEN_ADDR @ load start address
ldr r1, [r1]
cmp r0, r1 @ make sure we dont execute this code
beq smp_waitloop @ again (due to a spurious wakeup)
mov pc, r1
ENDPROC(smp_waitloop)
.weak smp_waitloop
#endif
ENTRY(_switch_to_hyp)
mov r0, lr
mov r1, sp @ save SVC copy of LR and SP
isb
hvc #0 @ for older asm: .byte 0x70, 0x00, 0x40, 0xe1
mov sp, r1
mov lr, r0 @ restore SVC copy of LR and SP
bx lr
ENDPROC(_switch_to_hyp)

View File

@ -0,0 +1,173 @@
/*
* (C) Copyright 2013
* Andre Przywara, Linaro
*
* Routines to transition ARMv7 processors from secure into non-secure state
* and from non-secure SVC into HYP mode
* needed to enable ARMv7 virtualization for current hypervisors
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <asm/armv7.h>
#include <asm/gic.h>
#include <asm/io.h>
unsigned long gic_dist_addr;
static unsigned int read_cpsr(void)
{
unsigned int reg;
asm volatile ("mrs %0, cpsr\n" : "=r" (reg));
return reg;
}
static unsigned int read_id_pfr1(void)
{
unsigned int reg;
asm("mrc p15, 0, %0, c0, c1, 1\n" : "=r"(reg));
return reg;
}
static unsigned long get_gicd_base_address(void)
{
#ifdef CONFIG_ARM_GIC_BASE_ADDRESS
return CONFIG_ARM_GIC_BASE_ADDRESS + GIC_DIST_OFFSET;
#else
unsigned midr;
unsigned periphbase;
/* check whether we are an Cortex-A15 or A7.
* The actual HYP switch should work with all CPUs supporting
* the virtualization extension, but we need the GIC address,
* which we know only for sure for those two CPUs.
*/
asm("mrc p15, 0, %0, c0, c0, 0\n" : "=r"(midr));
switch (midr & MIDR_PRIMARY_PART_MASK) {
case MIDR_CORTEX_A9_R0P1:
case MIDR_CORTEX_A15_R0P0:
case MIDR_CORTEX_A7_R0P0:
break;
default:
printf("nonsec: could not determine GIC address.\n");
return -1;
}
/* get the GIC base address from the CBAR register */
asm("mrc p15, 4, %0, c15, c0, 0\n" : "=r" (periphbase));
/* the PERIPHBASE can be mapped above 4 GB (lower 8 bits used to
* encode this). Bail out here since we cannot access this without
* enabling paging.
*/
if ((periphbase & 0xff) != 0) {
printf("nonsec: PERIPHBASE is above 4 GB, no access.\n");
return -1;
}
return (periphbase & CBAR_MASK) + GIC_DIST_OFFSET;
#endif
}
static void kick_secondary_cpus_gic(unsigned long gicdaddr)
{
/* kick all CPUs (except this one) by writing to GICD_SGIR */
writel(1U << 24, gicdaddr + GICD_SGIR);
}
void __weak smp_kick_all_cpus(void)
{
kick_secondary_cpus_gic(gic_dist_addr);
}
int armv7_switch_hyp(void)
{
unsigned int reg;
/* check whether we are in HYP mode already */
if ((read_cpsr() & 0x1f) == 0x1a) {
debug("CPU already in HYP mode\n");
return 0;
}
/* check whether the CPU supports the virtualization extensions */
reg = read_id_pfr1();
if ((reg & CPUID_ARM_VIRT_MASK) != 1 << CPUID_ARM_VIRT_SHIFT) {
printf("HYP mode: Virtualization extensions not implemented.\n");
return -1;
}
/* call the HYP switching code on this CPU also */
_switch_to_hyp();
if ((read_cpsr() & 0x1F) != 0x1a) {
printf("HYP mode: switch not successful.\n");
return -1;
}
return 0;
}
int armv7_switch_nonsec(void)
{
unsigned int reg;
unsigned itlinesnr, i;
/* check whether the CPU supports the security extensions */
reg = read_id_pfr1();
if ((reg & 0xF0) == 0) {
printf("nonsec: Security extensions not implemented.\n");
return -1;
}
/* the SCR register will be set directly in the monitor mode handler,
* according to the spec one should not tinker with it in secure state
* in SVC mode. Do not try to read it once in non-secure state,
* any access to it will trap.
*/
gic_dist_addr = get_gicd_base_address();
if (gic_dist_addr == -1)
return -1;
/* enable the GIC distributor */
writel(readl(gic_dist_addr + GICD_CTLR) | 0x03,
gic_dist_addr + GICD_CTLR);
/* TYPER[4:0] contains an encoded number of available interrupts */
itlinesnr = readl(gic_dist_addr + GICD_TYPER) & 0x1f;
/* set all bits in the GIC group registers to one to allow access
* from non-secure state. The first 32 interrupts are private per
* CPU and will be set later when enabling the GIC for each core
*/
for (i = 1; i <= itlinesnr; i++)
writel((unsigned)-1, gic_dist_addr + GICD_IGROUPRn + 4 * i);
smp_set_core_boot_addr((unsigned long)_smp_pen, -1);
smp_kick_all_cpus();
/* call the non-sec switching code on this CPU also */
_nonsec_init();
return 0;
}

View File

@ -7,7 +7,6 @@
*/
#ifndef ARMV7_H
#define ARMV7_H
#include <linux/types.h>
/* Cortex-A9 revisions */
#define MIDR_CORTEX_A9_R0P1 0x410FC091
@ -19,6 +18,22 @@
#define MIDR_CORTEX_A15_R0P0 0x410FC0F0
#define MIDR_CORTEX_A15_R2P2 0x412FC0F2
/* Cortex-A7 revisions */
#define MIDR_CORTEX_A7_R0P0 0x410FC070
#define MIDR_PRIMARY_PART_MASK 0xFF0FFFF0
/* ID_PFR1 feature fields */
#define CPUID_ARM_SEC_SHIFT 4
#define CPUID_ARM_SEC_MASK (0xF << CPUID_ARM_SEC_SHIFT)
#define CPUID_ARM_VIRT_SHIFT 12
#define CPUID_ARM_VIRT_MASK (0xF << CPUID_ARM_VIRT_SHIFT)
#define CPUID_ARM_GENTIMER_SHIFT 16
#define CPUID_ARM_GENTIMER_MASK (0xF << CPUID_ARM_GENTIMER_SHIFT)
/* valid bits in CBAR register / PERIPHBASE value */
#define CBAR_MASK 0xFFFF8000
/* CCSIDR */
#define CCSIDR_LINE_SIZE_OFFSET 0
#define CCSIDR_LINE_SIZE_MASK 0x7
@ -41,6 +56,9 @@
#define ARMV7_CLIDR_CTYPE_INSTRUCTION_DATA 3
#define ARMV7_CLIDR_CTYPE_UNIFIED 4
#ifndef __ASSEMBLY__
#include <linux/types.h>
/*
* CP15 Barrier instructions
* Please note that we have separate barrier instructions in ARMv7
@ -58,4 +76,17 @@ void v7_outer_cache_inval_all(void);
void v7_outer_cache_flush_range(u32 start, u32 end);
void v7_outer_cache_inval_range(u32 start, u32 end);
#if defined(CONFIG_ARMV7_NONSEC) || defined(CONFIG_ARMV7_VIRT)
int armv7_switch_nonsec(void);
int armv7_switch_hyp(void);
/* defined in assembly file */
unsigned int _nonsec_init(void);
void _smp_pen(void);
void _switch_to_hyp(void);
#endif /* CONFIG_ARMV7_NONSEC || CONFIG_ARMV7_VIRT */
#endif /* ! __ASSEMBLY__ */
#endif

View File

@ -0,0 +1,19 @@
#ifndef __GIC_V2_H__
#define __GIC_V2_H__
/* register offsets for the ARM generic interrupt controller (GIC) */
#define GIC_DIST_OFFSET 0x1000
#define GICD_CTLR 0x0000
#define GICD_TYPER 0x0004
#define GICD_IGROUPRn 0x0080
#define GICD_SGIR 0x0F00
#define GIC_CPU_OFFSET_A9 0x0100
#define GIC_CPU_OFFSET_A15 0x2000
#define GICC_CTLR 0x0000
#define GICC_PMR 0x0004
#define GICC_IAR 0x000C
#define GICC_EOIR 0x0010
#endif

View File

@ -22,6 +22,10 @@
#include <asm/bootm.h>
#include <linux/compiler.h>
#if defined(CONFIG_ARMV7_NONSEC) || defined(CONFIG_ARMV7_VIRT)
#include <asm/armv7.h>
#endif
DECLARE_GLOBAL_DATA_PTR;
static struct tag *params;
@ -181,6 +185,19 @@ static void setup_end_tag(bd_t *bd)
__weak void setup_board_tags(struct tag **in_params) {}
static void do_nonsec_virt_switch(void)
{
#if defined(CONFIG_ARMV7_NONSEC) || defined(CONFIG_ARMV7_VIRT)
if (armv7_switch_nonsec() == 0)
#ifdef CONFIG_ARMV7_VIRT
if (armv7_switch_hyp() == 0)
debug("entered HYP mode\n");
#else
debug("entered non-secure state\n");
#endif
#endif
}
/* Subcommand: PREP */
static void boot_prep_linux(bootm_headers_t *images)
{
@ -217,6 +234,7 @@ static void boot_prep_linux(bootm_headers_t *images)
printf("FDT and ATAGS support not compiled in - hanging\n");
hang();
}
do_nonsec_virt_switch();
}
/* Subcommand: GO */

View File

@ -256,3 +256,18 @@ ulong get_tbclk(void)
{
return (ulong)CONFIG_SYS_HZ;
}
#if defined(CONFIG_ARMV7_NONSEC) || defined(CONFIG_ARMV7_VIRT)
/* Setting the address at which secondary cores start from.
* Versatile Express uses one address for all cores, so ignore corenr
*/
void smp_set_core_boot_addr(unsigned long addr, int corenr)
{
/* The SYSFLAGS register on VExpress needs to be cleared first
* by writing to the next address, since any writes to the address
* at offset 0 will only be ORed in
*/
writel(~0, CONFIG_SYSFLAGS_ADDR + 4);
writel(addr, CONFIG_SYSFLAGS_ADDR);
}
#endif

View File

@ -627,6 +627,8 @@ void ft_pci_setup(void *blob, bd_t *bd);
#endif
#endif
void smp_set_core_boot_addr(unsigned long addr, int corenr);
void smp_kick_all_cpus(void);
/* $(CPU)/serial.c */
int serial_init (void);

View File

@ -15,6 +15,9 @@
#include "vexpress_common.h"
#define CONFIG_BOOTP_VCI_STRING "U-boot.armv7.vexpress_ca15x2_tc2"
#define CONFIG_SYS_CLK_FREQ 24000000
#define CONFIG_SYSFLAGS_ADDR 0x1c010030
#define CONFIG_SMP_PEN_ADDR CONFIG_SYSFLAGS_ADDR
#define CONFIG_ARMV7_VIRT
#endif