Commit e8fed55d authored by Oliver Horst's avatar Oliver Horst
Browse files

[merge] Integrated memguard and perfmon architecture specific parts

parents 2743cb4e 7942c4e8
/******************************************************************************
* Created by Dorel Coman on 23.10.17.
*
* This file contains all the events which can be monitored by Performance
* Monitor Unit (PMU) for each core. The perfmon.c API provided can be used to
* monitor these events and trigger and interrupt when a certain
* event is occurring for a certain amount of times.
*
* With the function pmu_type_select() inside perfmon.c one can choose which
* of the following events to monitor.
*
* For a better description of the events check the paragraph "12.9 Events"
* in the technical reference manual of the
* ARM Cortex-A53
*
* Following list is taken from:
* https://elixir.bootlin.com/linux/v4.0/source/arch/arm64/kernel/perf_event.c
******************************************************************************/
#ifndef FREERTOS_EXTRA_PERFMON_ARCH_ARM_CA53_EVENTS_H
#define FREERTOS_EXTRA_PERFMON_ARCH_ARM_CA53_EVENTS_H
#define PERFMON_PERFCTR_PMNC_SW_INCR 0x00
#define PERFMON_PERFCTR_L1_DCACHE_REFILL 0x03
#define PERFMON_PERFCTR_L1_DCACHE_ACCESS 0x04
#define PERFMON_PERFCTR_PC_BRANCH_MIS_PRED 0x10
#define PERFMON_PERFCTR_CLOCK_CYCLES 0x11
#define PERFMON_PERFCTR_PC_BRANCH_PRED 0x12
#define PERFMON_PERFCTR_INSTR_EXECUTED 0x08
#define PERFMON_PERFCTR_OP_SPEC 0x1B
#define PERFMON_PERFCTR_MEM_READ 0x06
#define PERFMON_PERFCTR_MEM_WRITE 0x07
#define PERFMON_PERFCTR_EXC_TAKEN 0x09
#define PERFMON_PERFCTR_EXC_EXECUTED 0x0A
#define PERFMON_PERFCTR_CID_WRITE 0x0B
#define PERFMON_PERFCTR_PC_WRITE 0x0C
#define PERFMON_PERFCTR_PC_IMM_BRANCH 0x0D
#define PERFMON_PERFCTR_PC_PROC_RETURN 0x0E
#define PERFMON_PERFCTR_MEM_UNALIGNED_ACCESS 0x0F
#define PERFMON_PERFCTR_TTBR_WRITE 0x1C
#define PERFMON_PERFCTR_CHAIN 0x1E
#define PERFMON_PERFCTR_BR_RETIRED 0x21
#define PERFMON_PERFCTR_L1_ICACHE_REFILL 0x01
#define PERFMON_PERFCTR_ITLB_REFILL 0x02
#define PERFMON_PERFCTR_DTLB_REFILL 0x05
#define PERFMON_PERFCTR_MEM_ACCESS 0x13
#define PERFMON_PERFCTR_L1_ICACHE_ACCESS 0x14
#define PERFMON_PERFCTR_L1_DCACHE_WB 0x15
#define PERFMON_PERFCTR_L2_CACHE_ACCESS 0x16
#define PERFMON_PERFCTR_L2_CACHE_REFILL 0x17
#define PERFMON_PERFCTR_L2_CACHE_WB 0x18
#define PERFMON_PERFCTR_BUS_ACCESS 0x19
#define PERFMON_PERFCTR_MEM_ERROR 0x1A
#define PERFMON_PERFCTR_BUS_CYCLES 0x1D
#define PERFMON_PERFCTR_L1D_CACHE_ALLOCATE 0x1F
#define PERFMON_PERFCTR_L2D_CACHE_ALLOCATE 0x20
#define PERFMON_PERFCTR_BR_MIS_PRED_RETIRED 0x22
#define PERFMON_PERFCTR_STALL_FRONTEND 0x23
#define PERFMON_PERFCTR_STALL_BACKEND 0x24
#define PERFMON_PERFCTR_L1D_TLB 0x25
#define PERFMON_PERFCTR_L1I_TLB 0x26
#define PERFMON_PERFCTR_L2I_CACHE 0x27
#define PERFMON_PERFCTR_L2I_CACHE_REFILL 0x28
#define PERFMON_PERFCTR_L3D_CACHE_ALLOCATE 0x29
#define PERFMON_PERFCTR_L3D_CACHE_REFILL 0x2A
#define PERFMON_PERFCTR_L3D_CACHE 0x2B
#define PERFMON_PERFCTR_L3D_CACHE_WB 0x2C
#define PERFMON_PERFCTR_L2D_TLB_REFILL 0x2D
#define PERFMON_PERFCTR_L21_TLB_REFILL 0x2E
#define PERFMON_PERFCTR_L2D_TLB 0x2F
#define PERFMON_PERFCTR_L21_TLB 0x30
#define PERFMON_PERFCTR_PREFETCH_LINEFILL 0xC2
#endif /* FREERTOS_EXTRA_PERFMON_ARCH_ARM_CA53_EVENTS_H */
#ifndef FREERTOS_EXTRA_PERFMON_ARCH_ARM_CA53_PLATFORM_H
#define FREERTOS_EXTRA_PERFMON_ARCH_ARM_CA53_PLATFORM_H
/* This ID has to be used, when managing the Cycle Counter (PMCCNTR_EL0) is
and it is wanted to access some functions related like the followings:
vPerfmonEnableInterruptForCounter(), vPerfmonDisableInterruptForCounter(),
vPerfmonClearInterruptForCounter().
The define represents the bit on the following registers which has to be used
in order to manipulate the PMCCNTR_EL0: PMINTENCLR_EL1, PMINTENSET_EL1,
PMOVSCLR_EL0, PMOVSSET_EL0.
For more details please read the description of the registers in the ARMv8
Technical Reference Manual between the chapters D10.4.10 and D10.4.13 (Pag.
2866 - 2873) which can be found at the link:
https://static.docs.arm.com/ddi0487/ca/DDI0487C_a_armv8_arm.pdf */
#define PERFMON_CYLCE_CNTR_ID 31U
/* The number of PMU counters available on each core of the cluster Cortex-A53 */
#define PERFMON_NUMBER_OF_COUNTERS 6U
#endif /* FREERTOS_EXTRA_PERFMON_ARCH_ARM_CA53_PLATFORM_H */
cmake_minimum_required(VERSION 3.7 FATAL_ERROR)
target_sources(
freertos
#
PRIVATE
"${CMAKE_CURRENT_LIST_DIR}/memguard_arch.c"
)
BSD with Attribution License
Copyright (c) 2015-2020 fortiss GmbH - Research Institute of the
Free State of Bavaria, Guerickestr. 25, 80805 Munich, Germany
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. 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.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
4. Redistributions of any form whatsoever must retain the following
acknowledgment: 'This product includes software developed by the
fortiss GmbH -- Research Institute of the Free State of Bavaria,
Munich, Germany (https://www.fortiss.org/).'
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.
SPDX-License-Identifier: BSD-3-Clause-Attribution
#include "freertos/FreeRTOS.h"
#include "xil/xil_printf.h"
#include "xil/xil_cache.h"
#include "xil/xil_mmu.h"
#include "xil/drivers/xttcps.h"
#include "xil/drivers/xscugic.h"
#include "freertos/hal/cpu.h"
#include "freertos/extra/perfmon.h"
#include "freertos/extra/memguard.h"
/* Base address of the on-chip memory where we store the variables shared
among the cores. Defined by the linker script. */
extern void *__ocm_ram_start;
/* Instance of the GIC Controller, it is set inside the portZynqUltrascale.c */
extern XScuGic xInterruptController;
/* Memguard Window Timer, it is used only by the master core */
static XTtcPs xPortMemguardTimerInstance;
/* Frequency of the TTC timer of memguard */
#define MEMGUARD_TIMER_FREQ ( (uint32_t)( 1 / configMEMGUARD_WINDOW_TIME ) )
/* */
void vPortMemguardEnableDCache()
{
u64 attrib;
/* TODO: the cache is enabled again, it should be checked whether
there is a need of enabling/disabling the cache during the boot. */
attrib = (NORM_NONCACHE | INNER_SHAREABLE | OUTER_SHAREABLE);
Xil_SetTlbAttributes( (UINTPTR)__ocm_ram_start, attrib );
Xil_DCacheEnable();
Xil_SetTlbAttributes( (UINTPTR)__ocm_ram_start, attrib );
}
/* */
void vPortMemguardDisableDCache()
{
/* TODO: the cache is disabled until all the cores are boot. It should
be explored further whether we really need to disable the caches
or not. The cache enabling is also marked as open topic.
*/
Xil_DCacheDisable();
}
/* */
BaseType_t xPortMemguardIsMaster()
{
BaseType_t xReturn;
if( uxHalGetCpuId() == configMEMGUARD_MASTER_CORE_ID )
{
xReturn = pdTRUE;
}
else
{
xReturn = pdFALSE;
}
return xReturn;
}
/* */
BaseType_t xPortMemguardSignalMaster()
{
s32 lStatus;
BaseType_t xReturn;
lStatus = XScuGic_SoftwareIntr(
&xInterruptController,
configMEMGUARD_ICI_MASTER_INT_ID,
configMEMGUARD_ICI_MASTER_INT_MASK
);
if ( XST_SUCCESS == lStatus )
{
xReturn = pdTRUE;
}
else
{
xReturn = pdFALSE;
}
return xReturn;
}
/* */
BaseType_t xPortMemguardSignalSlaves()
{
s32 lStatus;
BaseType_t xReturn;
lStatus = XScuGic_SoftwareIntr(
&xInterruptController,
configMEMGUARD_ICI_SLAVE_INT_ID,
configMEMGUARD_ICI_SLAVE_INT_MASK
);
if ( XST_SUCCESS == lStatus )
{
xReturn = pdTRUE;
}
else
{
xReturn = pdFALSE;
}
return xReturn;
}
/**
* This function sets up the window timer. It is supposed to be executed
* inside a Task context in order to execute properly. We are using an
* independent timer which allows more fine grained observation of the memory
* bandwidth (e.g., 1ms time window) and allows a window to span between 2
* different ticks. For more information refer to the original paper at the
* Subsection 6.2.
*/
void vPortMemguardSetupTimer()
{
s32 lStatus;
XTtcPs_Config *xTimerConfig;
XInterval INTERVAL = 0; // Interval value
u8 PRESCALER = 0; // PreScaler value
u16 TIMER_OPTIONS = 0; // Option settings
/* Set up appropriate options for window timer: interval mode without
waveform output */
TIMER_OPTIONS |= ( XTTCPS_OPTION_INTERVAL_MODE | XTTCPS_OPTION_WAVE_DISABLE );
/* Look up the configuration based on the device identifier */
xTimerConfig = XTtcPs_LookupConfig( configMEMGUARD_TIMER_DEVICE_ID );
configASSERT( xTimerConfig != NULL );
/* Initialize the TTC device */
lStatus = XTtcPs_CfgInitialize( &xPortMemguardTimerInstance, xTimerConfig, xTimerConfig->BaseAddress );
configASSERT( XST_SUCCESS == lStatus );
/* Set the options */
XTtcPs_SetOptions( &xPortMemguardTimerInstance, TIMER_OPTIONS );
/* The following call will map the frequency to the interval and
prescaler values. */
XTtcPs_CalcIntervalFromFreq( &xPortMemguardTimerInstance, MEMGUARD_TIMER_FREQ,
&INTERVAL, &PRESCALER);
/* Set the interval and pre-scale */
XTtcPs_SetInterval( &xPortMemguardTimerInstance, INTERVAL );
XTtcPs_SetPrescaler( &xPortMemguardTimerInstance, PRESCALER );
/* Connect to the interrupt controller */
lStatus = XScuGic_Connect( &xInterruptController, configMEMGUARD_TIMER_INTR_ID,
(Xil_ExceptionHandler)vMemguardMasterPeriodTickHandler,
(void *)(&xPortMemguardTimerInstance) );
configASSERT( XST_SUCCESS == lStatus );
/* Enable the GIC for the window timer */
XScuGic_Enable( &xInterruptController, configMEMGUARD_TIMER_INTR_ID );
/* Enable the TTC for window timer */
XTtcPs_EnableInterrupts( &xPortMemguardTimerInstance, XTTCPS_IXR_INTERVAL_MASK );
}
/* */
void vPortMemguardStopTimer()
{
/* Stop the timer because we need to start a new window */
XTtcPs_Stop( &xPortMemguardTimerInstance );
}
/* */
void vPortMemguardResetTimer()
{
if( pdTRUE == xPortMemguardIsMaster() )
{
/* Resetting the MemGuard timer in order to have a new time window */
XTtcPs_ResetCounterValue( &xPortMemguardTimerInstance );
XTtcPs_Start( &xPortMemguardTimerInstance );
}
}
/**
* This function sets up the inter-core communications in the framework.
* These are exectued trough software generated interrupts (SGI). The ICI
* setup here are bidirectional, meaning from Master to Slave and vice versa.
*/
BaseType_t xPortMemguardSetupIPCInterrupts()
{
s32 lReturn;
/* Connecting the periodic_timer_handler_master() to an SGI, in order to
make the function callable by the slave cores when the minimum
guaranteed bandwidth finishes so the master can restart a new window. */
if( pdTRUE == xPortMemguardIsMaster() )
{
lReturn = XScuGic_Connect( &xInterruptController, configMEMGUARD_ICI_MASTER_INT_ID,
(Xil_ExceptionHandler)vMemguardMasterPeriodTickHandler,
(void *)&xInterruptController );
if ( XST_SUCCESS != lReturn )
{
xil_printf( "Failed to XScuGic_connect" );
return pdFALSE;
}
XScuGic_SetPriorityTriggerType( &xInterruptController,
configMEMGUARD_ICI_MASTER_INT_ID,
configMEMGUARD_ICI_INT_PRIORITY,
0b01 );
XScuGic_Enable( &xInterruptController, configMEMGUARD_ICI_MASTER_INT_ID );
}
/* Connecting the periodic_timer_handler_slave() on each core as a handler
in order to make the handler on each core callable by the master core, when
a new window has to be started. We are executing this interrupt also on the
master core in order to keep the cores synchronized when calling the
memguard window reset and to be sure that the timing of execution is
almost the same. */
lReturn = XScuGic_Connect( &xInterruptController, configMEMGUARD_ICI_SLAVE_INT_ID,
(Xil_ExceptionHandler)vMemguardSlavePeriodTickHandler,
(void *)&xInterruptController );
if ( XST_SUCCESS != lReturn )
{
xil_printf( "Failed to XScuGic_connect" );
return pdFALSE;
}
/* Enable the interrupt for inter-core communication */
XScuGic_SetPriorityTriggerType( &xInterruptController,
configMEMGUARD_ICI_SLAVE_INT_ID,
configMEMGUARD_ICI_INT_PRIORITY, 0b01);
XScuGic_Enable( &xInterruptController, configMEMGUARD_ICI_SLAVE_INT_ID );
return pdTRUE;
}
/**
* It sets the GIC to handle the Performance Monitor Unit (PMU) counters
* interrupts and to make them to be handled by the
* overflow_interrupt_handler() function
*
* @return status of the setup
*/
BaseType_t xPortMemguardSetupPMUInterrupt()
{
s32 lReturn;
uint32_t PMU_INT_ID;
/* Selecting the right PMU interrupt on each core */
PMU_INT_ID = configPMU_INT_BASE_ID + uxHalGetCpuId();
/* Connect the handler to the interrupt controller */
lReturn = XScuGic_Connect(
&xInterruptController,
PMU_INT_ID,
(Xil_ExceptionHandler)vMemguardOverflowHandler,
(void *)&xInterruptController
);
if ( XST_SUCCESS != lReturn )
{
xil_printf( "Failed to XScuGic_connect" );
return pdFALSE;
}
/* Setting the priority of the PMU interrupts to low value, in order to
make the PMU interrupts preemtable by the MemGuard timer or MemGuard ICI
interrupts. This precaution is taken in order to avoid PMU overflow interrupts
to interrupt an ongoing window reset initiated by a MemGuard timer or ICI
interrupt. This situation would create problems in the MemGuard workflow.
Trigger type: 0b01 => Active HIGH level sensitive */
XScuGic_SetPriorityTriggerType(
&xInterruptController,
PMU_INT_ID,
(u8)configMEMGUARD_PMU_OVERFLOW_INT_PRIORITY,
0b01
);
XScuGic_InterruptMaptoCpu(
&xInterruptController,
(u8)( uxHalGetCpuId() ),
PMU_INT_ID
);
XScuGic_Enable( &xInterruptController, PMU_INT_ID );
return pdTRUE;
}
/* */
void vPortMemguardClearMasterInterrupt( void *callback_ref )
{
u32 ulEventStatus;
/* Read the interrupt status, then write it back to clear the interrupt. */
ulEventStatus = XTtcPs_GetInterruptStatus( (XTtcPs *)callback_ref );
/* We clear the interrupt status only if it was triggered by a timer */
if ( 0 != ( XTTCPS_IXR_INTERVAL_MASK & ulEventStatus ) )
{
XTtcPs_ClearInterruptStatus( (XTtcPs *)callback_ref, ulEventStatus );
}
}
cmake_minimum_required(VERSION 3.7 FATAL_ERROR)
target_sources(
freertos
#
PRIVATE
"${CMAKE_CURRENT_LIST_DIR}/perfmon_arch.c"
)
BSD with Attribution License
Copyright (c) 2015-2020 fortiss GmbH - Research Institute of the
Free State of Bavaria, Guerickestr. 25, 80805 Munich, Germany
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. 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.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
4. Redistributions of any form whatsoever must retain the following
acknowledgment: 'This product includes software developed by the
fortiss GmbH -- Research Institute of the Free State of Bavaria,
Munich, Germany (https://www.fortiss.org/).'
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.
SPDX-License-Identifier: BSD-3-Clause-Attribution
/*******************************************************************************
* Created by Dorel Coman on 21.10.17.
*
* This API provides functionalities for accessing the Performance Monitor
* Counters (PMU) registers of the ARM64 architectures
******************************************************************************/
#include "freertos/FreeRTOS.h"
#include "freertos/extra/perfmon.h"
#include "xil/xpseudo_asm.h"
/* Macros used for accessing and setting up the PMU registers */
#define PMCR_MASK 0x3Fu
#define PMCR_E (1u << 0u) /* Enable all counters */
#define PMCR_P (1u << 1u) /* Reset all counters */
#define PMCR_C (1u << 2u) /* Cycle counter reset */
#define PMCR_D (1u << 3u) /* CCNT counts every 64th CPU cycle */
#define PMCR_N_SHIFT 11u /* Mask used for getting Number of counters supported */
#define PMCR_N_MASK 0x1Fu
#define PMU_USERENR_MASK 0xFu /* Mask for writable bits */
#define PMUSERENR_EN_EL0 (1u << 0u) /* EL0 access enable */
#define PMUSERENR_CR (1u << 2u) /* Cycle counter read enable */
#define PMUSERENR_ER (1u << 3u) /* Event counter read enable */
#define PMUSERENR_ENABLE_ALL 0xDu
/*********************************************************
* Registers of the Performance Monitor Unit (PMU)
*********************************************************/
/* Performance Monitors Event Counter Selection Register */
#define PMSELR_EL0 "PMSELR_EL0"
/* Performance Monitors User Enable Register */
#define PMUSERENR_EL0 "PMUSERENR_EL0"
/* Performance Monitors Count Enable Set register */
#define PMCNTENSET_EL0 "PMCNTENSET_EL0"
/* Performance Monitors Control Register */
#define PMCR_EL0 "PMCR_EL0"
/* Performance Monitors Cycle Count Register - 64-bit register */
#define PMCCNTR_EL0 "PMCCNTR_EL0"
/* Performance Monitors Selected Event Count Register */
#define PMXEVCNTR_EL0 "PMXEVCNTR_EL0"
/* Performance Monitors Selected Event Type Register */
#define PMXEVTYPER_EL0 "PMXEVTYPER_EL0"
/* Performance Monitors Interrupt Enable Clear register */
#define PMINTENCLR_EL1 "PMINTENCLR_EL1"
/* Performance Monitors Interrupt Enable Set register */
#define PMINTENSET_EL1 "PMINTENSET_EL1"
/* Performance Monitors Overflow Flag Status Clear Register */
#define PMOVSCLR_EL0 "PMOVSCLR_EL0"
/* Performance Monitors Overflow Flag Status Set register */
#define PMOVSSET_EL0 "PMOVSSET_EL0"
/*******************************************************************************
* Function definitions
******************************************************************************/
#if( INCLUDE_vPerfmonEnableUserAccess == 1 )
/**
* It enables user-mode access (EL0: applications) to counters. It is
* important in order to be able to use the counters in FreeRTOS.
*/
static inline void vPerfmonEnableUserAccess()
{
uint32_t reg_val;
reg_val = mfcp( PMUSERENR_EL0 );
reg_val = reg_val | PMUSERENR_ENABLE_ALL;
mtcp( PMUSERENR_EL0, reg_val );
}
#endif
/**
* This function enables the selected counter in order to make it count. If
* the counter is not enabled, it will not count the occurencies of its
* selected event type
* @idx: The counter n index