Core: Trusted Firmware M core implementation

Current version includes-
 --Initialization of SAU/MPC to configure secure
   and non-secure boundaries
 --Initialization of PPC to configure non-secure peripherals
 --Secure Context Management
 --Interface for secure service integration
 --Secure/non-secure interface management

Change-Id: I468c41e619828d20a709811039b96830191c0d08
Signed-off-by: Abhishek Pandit <abhishek.pandit@arm.com>
Co-Authored-By: Abhishek Pandit <abhishek.pandit@arm.com>
diff --git a/secure_fw/spm/spm_api.c b/secure_fw/spm/spm_api.c
new file mode 100644
index 0000000..79ba4cd
--- /dev/null
+++ b/secure_fw/spm/spm_api.c
@@ -0,0 +1,341 @@
+/*
+ * Copyright (c) 2017, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+/* This file contains the apis exported by the SPM to tfm core */
+
+#include <stdio.h>
+#include "spm_db.h"
+#include "spm_api.h"
+#include "mpu_armv8m_drv.h"
+#include "region_defs.h"
+#include "secure_fw/core/tfm_core.h"
+
+struct spm_service_db_t g_spm_service_db = {0,};
+
+#define MPU_REGION_VENEERS   0
+#define MPU_REGION_TFM_UNPRIV_CODE   1
+#define MPU_REGION_TFM_UNPRIV_DATA   2
+#define MPU_REGION_NS_DATA      3
+#define SERVICE_REGION_RO       4
+#define SERVICE_REGION_RW_STACK 5
+#define SERVICE_REGION_PERIPH   6
+#define SERVICE_REGION_SHARE    7
+
+/* This should move to platform retarget */
+struct mpu_armv8m_dev_t dev_mpu_s = { MPU_BASE };
+
+typedef enum {
+    TFM_INIT_FAILURE,
+} ss_error_type_t;
+
+/*
+ * This function is called when a secure service causes an error.
+ */
+static void tfm_spm_service_err_handler(
+    uint32_t ss_id, ss_error_type_t err_type, int32_t err_code)
+{
+    /*
+     * FixMe: error handling to be added. E.g. service info to be updated with
+     * init failed so that calls to the service are rejected
+     */
+#ifdef TFM_CORE_DEBUG
+    if (err_type == TFM_INIT_FAILURE) {
+        printf("Service init failed for service id 0x%08X\r\n", ss_id);
+    } else {
+        printf("Unknown service error %d for service id 0x%08X\r\n",
+            err_type,
+            ss_id);
+    }
+#endif
+}
+
+enum spm_err_t tfm_spm_db_init(void)
+{
+    /* This function initialises service db */
+    g_spm_service_db.is_init = 1;
+
+    g_spm_service_db.services_count =
+        create_user_service_db(&g_spm_service_db, SPM_MAX_SERVICES);
+
+    return SPM_ERR_OK;
+}
+
+#if TFM_LVL != 1
+REGION_DECLARE(Image$$, TFM_UNPRIV_CODE, $$RO$$Base);
+REGION_DECLARE(Image$$, TFM_UNPRIV_CODE, $$RO$$Limit);
+REGION_DECLARE(Image$$, TFM_UNPRIV_RO_DATA, $$RW$$Base);
+REGION_DECLARE(Image$$, TFM_UNPRIV_RO_DATA, $$ZI$$Limit);
+REGION_DECLARE(Image$$, TFM_UNPRIV_SCRATCH, $$ZI$$Base);
+REGION_DECLARE(Image$$, TFM_UNPRIV_SCRATCH, $$ZI$$Limit);
+
+enum spm_err_t tfm_spm_mpu_init(void)
+{
+    mpu_armv8m_clean(&dev_mpu_s);
+
+    struct mpu_armv8m_region_cfg_t region_cfg;
+
+    /* Veneer region */
+    region_cfg.region_nr = MPU_REGION_VENEERS;
+    region_cfg.region_base = CMSE_VENEER_REGION_START;
+    region_cfg.region_limit = CMSE_VENEER_REGION_LIMIT;
+    region_cfg.attr_access = MPU_ARMV8M_AP_RO_PRIV_UNPRIV;
+    region_cfg.attr_sh = MPU_ARMV8M_SH_NONE;
+    region_cfg.attr_exec = MPU_ARMV8M_XN_EXEC_OK;
+    mpu_armv8m_region_enable(&dev_mpu_s, &region_cfg);
+
+    /* TFM Core unprivileged code region */
+    region_cfg.region_nr = MPU_REGION_TFM_UNPRIV_CODE;
+    region_cfg.region_base =
+        (uint32_t)&REGION_NAME(Image$$, TFM_UNPRIV_CODE, $$RO$$Base);
+    region_cfg.region_limit =
+        (uint32_t)&REGION_NAME(Image$$, TFM_UNPRIV_CODE, $$RO$$Limit);
+    region_cfg.attr_access = MPU_ARMV8M_AP_RO_PRIV_UNPRIV;
+    region_cfg.attr_sh = MPU_ARMV8M_SH_NONE;
+    region_cfg.attr_exec = MPU_ARMV8M_XN_EXEC_OK;
+    mpu_armv8m_region_enable(&dev_mpu_s, &region_cfg);
+
+    /* TFM Core unprivileged data region */
+    region_cfg.region_nr = MPU_REGION_TFM_UNPRIV_DATA;
+    region_cfg.region_base =
+        (uint32_t)&REGION_NAME(Image$$, TFM_UNPRIV_RO_DATA, $$RW$$Base);
+    region_cfg.region_limit =
+        (uint32_t)&REGION_NAME(Image$$, TFM_UNPRIV_RO_DATA, $$ZI$$Limit);
+    region_cfg.attr_access = MPU_ARMV8M_AP_RO_PRIV_UNPRIV;
+    region_cfg.attr_sh = MPU_ARMV8M_SH_NONE;
+    region_cfg.attr_exec = MPU_ARMV8M_XN_EXEC_NEVER;
+    mpu_armv8m_region_enable(&dev_mpu_s, &region_cfg);
+
+    /* TFM Core unprivileged non-secure data region */
+    region_cfg.region_nr = MPU_REGION_NS_DATA;
+    region_cfg.region_base = NS_DATA_START;
+    region_cfg.region_limit = NS_DATA_LIMIT;
+    region_cfg.attr_access = MPU_ARMV8M_AP_RW_PRIV_UNPRIV;
+    region_cfg.attr_sh = MPU_ARMV8M_SH_NONE;
+    region_cfg.attr_exec = MPU_ARMV8M_XN_EXEC_NEVER;
+    mpu_armv8m_region_enable(&dev_mpu_s, &region_cfg);
+
+    mpu_armv8m_enable(&dev_mpu_s, 1, 1);
+
+    return SPM_ERR_OK;
+}
+
+enum spm_err_t tfm_spm_set_share_region(enum tfm_buffer_share_region_e share)
+{
+    enum spm_err_t res = SPM_ERR_INVALID_CONFIG;
+    uint32_t scratch_base =
+        (uint32_t)&REGION_NAME(Image$$, TFM_UNPRIV_SCRATCH, $$ZI$$Base);
+    uint32_t scratch_limit =
+        (uint32_t)&REGION_NAME(Image$$, TFM_UNPRIV_SCRATCH, $$ZI$$Limit);
+
+    mpu_armv8m_disable(&dev_mpu_s);
+
+    if (share == TFM_BUFFER_SHARE_DISABLE) {
+        mpu_armv8m_region_disable(&dev_mpu_s, SERVICE_REGION_SHARE);
+    } else {
+        struct mpu_armv8m_region_cfg_t region_cfg;
+
+        region_cfg.region_nr = SERVICE_REGION_SHARE;
+        region_cfg.attr_access = MPU_ARMV8M_AP_RW_PRIV_UNPRIV;
+        region_cfg.attr_sh = MPU_ARMV8M_SH_NONE;
+        region_cfg.attr_exec = MPU_ARMV8M_XN_EXEC_NEVER;
+        switch (share) {
+        case TFM_BUFFER_SHARE_SCRATCH:
+            /* Use scratch area for service-to-service data sharing */
+            region_cfg.region_base = scratch_base;
+            region_cfg.region_limit = scratch_limit;
+            res = SPM_ERR_OK;
+            break;
+        case TFM_BUFFER_SHARE_NS_CODE:
+            region_cfg.region_base = NS_CODE_START;
+            region_cfg.region_limit = NS_CODE_LIMIT;
+            /* Only allow read access to NS code region and keep
+             * exec.never attribute
+             */
+            region_cfg.attr_access = MPU_ARMV8M_AP_RO_PRIV_UNPRIV;
+            res = SPM_ERR_OK;
+            break;
+        default:
+            res = SPM_ERR_INVALID_CONFIG;
+            break;
+        }
+        if (res == SPM_ERR_OK) {
+            mpu_armv8m_region_enable(&dev_mpu_s, &region_cfg);
+        }
+    }
+    mpu_armv8m_enable(&dev_mpu_s, 1, 1);
+
+    return res;
+}
+#endif
+
+enum spm_err_t tfm_spm_service_init(void)
+{
+    struct spm_service_region_t *serv;
+    int32_t fail_cnt = 0;
+    uint32_t i;
+
+    /* Call the init function for each service */
+    /* FixMe: This implementation only fits level 1 isolation.
+     * On higher levels MPU (and PPC) configuration need to be in place to have
+     * proper isolation during init.
+     */
+    for (i = 0; i < g_spm_service_db.services_count; ++i) {
+        serv = &g_spm_service_db.services[i];
+        if (serv->periph_start) {
+            ppc_configure_to_secure(serv->periph_ppc_bank,
+                                    serv->periph_ppc_loc);
+        }
+        if (serv->service_init != 0) {
+            int32_t ret = serv->service_init();
+
+            if (ret != 0) {
+                tfm_spm_service_err_handler(serv->service_id,
+                    TFM_INIT_FAILURE, ret);
+                fail_cnt++;
+            }
+        }
+    }
+
+    if (fail_cnt == 0) {
+        return SPM_ERR_OK;
+    } else {
+        return SPM_ERR_SERV_NOT_AVAILABLE;
+    }
+}
+
+#if TFM_LVL != 1
+enum spm_err_t tfm_spm_service_sandbox_config(uint32_t service_id)
+{
+    /* This function takes a service id and enables the
+     * SPM partition for that service
+     */
+
+    struct spm_service_region_t *serv;
+    struct mpu_armv8m_region_cfg_t region_cfg;
+
+    if (!g_spm_service_db.is_init) {
+        return SPM_ERR_SERV_DB_NOT_INIT;
+    }
+
+    /*brute force id*/
+    serv = &g_spm_service_db.services[SERVICE_ID_GET(service_id)];
+
+    mpu_armv8m_disable(&dev_mpu_s);
+
+    /* Configure Regions */
+
+    /* RO region*/
+    region_cfg.region_nr = SERVICE_REGION_RO;
+    region_cfg.region_base = serv->ro_start;
+    region_cfg.region_limit = serv->ro_limit;
+    region_cfg.attr_access = MPU_ARMV8M_AP_RO_PRIV_UNPRIV;
+    region_cfg.attr_sh = MPU_ARMV8M_SH_NONE;
+    region_cfg.attr_exec = MPU_ARMV8M_XN_EXEC_OK;
+
+    mpu_armv8m_region_enable(&dev_mpu_s, &region_cfg);
+
+    /* RW, ZI and stack as one region*/
+    region_cfg.region_nr = SERVICE_REGION_RW_STACK;
+    region_cfg.region_base = serv->rw_start;
+    region_cfg.region_limit = serv->stack_top;
+    region_cfg.attr_access = MPU_ARMV8M_AP_RW_PRIV_UNPRIV;
+    region_cfg.attr_sh = MPU_ARMV8M_SH_NONE;
+    region_cfg.attr_exec = MPU_ARMV8M_XN_EXEC_NEVER;
+
+    mpu_armv8m_region_enable(&dev_mpu_s, &region_cfg);
+
+    if (serv->periph_start) {
+        /* Peripheral */
+        region_cfg.region_nr = SERVICE_REGION_PERIPH;
+        region_cfg.region_base = serv->periph_start;
+        region_cfg.region_limit = serv->periph_limit;
+        region_cfg.attr_access = MPU_ARMV8M_AP_RW_PRIV_UNPRIV;
+        region_cfg.attr_sh = MPU_ARMV8M_SH_NONE;
+        region_cfg.attr_exec = MPU_ARMV8M_XN_EXEC_NEVER;
+        mpu_armv8m_region_enable(&dev_mpu_s, &region_cfg);
+
+        ppc_en_secure_unpriv(serv->periph_ppc_bank, serv->periph_ppc_loc);
+    }
+
+    mpu_armv8m_enable(&dev_mpu_s, 1, 1);
+
+#ifndef UNPRIV_JUMP_TO_NS
+    /* FixMe: if jump_to_ns_code() from unprivileged is solved,
+     * this code can be removed
+     */
+    /* Initialization is done, set thread mode to unprivileged.
+     */
+    CONTROL_Type ctrl;
+
+    ctrl.w = __get_CONTROL();
+    ctrl.b.nPRIV = 1;
+    __set_CONTROL(ctrl.w);
+    __DSB();
+    __ISB();
+#endif
+
+    return SPM_ERR_OK;
+}
+
+enum spm_err_t tfm_spm_service_sandbox_deconfig(uint32_t service_id)
+{
+    /* This function takes a service id and disables the
+     * SPM partition for that service
+     */
+
+#ifndef UNPRIV_JUMP_TO_NS
+    /* FixMe: if jump_to_ns_code() from unprivileged is solved,
+     * this code can be removed
+     */
+    CONTROL_Type ctrl;
+
+    ctrl.w = __get_CONTROL();
+    ctrl.b.nPRIV = 0;
+    __set_CONTROL(ctrl.w);
+    __DSB();
+    __ISB();
+#endif
+
+    struct spm_service_region_t *serv;
+
+    serv = &g_spm_service_db.services[SERVICE_ID_GET(service_id)];
+
+    if (serv->periph_start) {
+        /* Peripheral */
+        ppc_clr_secure_unpriv(serv->periph_ppc_bank, serv->periph_ppc_loc);
+    }
+
+    mpu_armv8m_disable(&dev_mpu_s);
+    mpu_armv8m_region_disable(&dev_mpu_s, SERVICE_REGION_RO);
+    mpu_armv8m_region_disable(&dev_mpu_s, SERVICE_REGION_RW_STACK);
+    mpu_armv8m_region_disable(&dev_mpu_s, SERVICE_REGION_PERIPH);
+    mpu_armv8m_region_disable(&dev_mpu_s, SERVICE_REGION_SHARE);
+    mpu_armv8m_enable(&dev_mpu_s, 1, 1);
+
+    return SPM_ERR_OK;
+}
+
+uint32_t tfm_spm_service_get_stack(uint32_t service_id)
+{
+    return g_spm_service_db.services[SERVICE_ID_GET(service_id)].stack_ptr;
+}
+
+uint32_t tfm_spm_service_get_stack_bottom(uint32_t service_id)
+{
+    return g_spm_service_db.services[SERVICE_ID_GET(service_id)].stack_bottom;
+}
+
+uint32_t tfm_spm_service_get_stack_top(uint32_t service_id)
+{
+    return g_spm_service_db.services[SERVICE_ID_GET(service_id)].stack_top;
+}
+
+void tfm_spm_service_set_stack(uint32_t service_id, uint32_t stack_ptr)
+{
+    g_spm_service_db.services[SERVICE_ID_GET(service_id)].stack_ptr = stack_ptr;
+}
+#endif