[FOSS_TLK]platform: tegra: common: add ss get config syscall
Scott Long [Tue, 18 Nov 2014 21:11:28 +0000 (13:11 -0800)]
This change adds a new syscall (OTE_IOCTL_SS_GET_CONFIG) that will
be used by the secure storage TA to retrieve a mask of configuration
options (e.g. rollback protection-related values).

These configuration options are determined by reading various fuse
values using new fuse I/O support.

Change-Id: I4716d2a4472f05b53fb799c5d1c7b6680d752a72
Signed-off-by: Scott Long <scottl@nvidia.com>
Reviewed-on: http://git-master/r/707294
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Varun Wadekar <vwadekar@nvidia.com>
Tested-by: Varun Wadekar <vwadekar@nvidia.com>

kernel/syscall.c
platform/tegra/common/fuse.c [new file with mode: 0644]
platform/tegra/common/platform.c
platform/tegra/include/platform/memmap.h
platform/tegra/include/platform/platform_fuse.h [new file with mode: 0644]
platform/tegra/include/platform/platform_p.h
platform/tegra/rules.mk

index 5239f1f..c75b08f 100644 (file)
@@ -237,6 +237,18 @@ static int platform_ioctl_handler(u_int cmd, void *cmdbuf)
                {
                        return platform_ss_request_handler();
                }
+               case OTE_IOCTL_SS_GET_CONFIG:
+               {
+                       uint32_t *ss_config = cmdbuf;
+
+                       /* validate command buffer */
+                       if (!task_valid_address((vaddr_t)ss_config,
+                                                       sizeof(uint32_t)))
+                               return -EFAULT;
+
+                       return platform_ss_get_config(ss_config);
+                       break;
+               }
                case OTE_IOCTL_GET_DEVICE_ID:
                {
                        te_device_id_args_t *args = cmdbuf;
diff --git a/platform/tegra/common/fuse.c b/platform/tegra/common/fuse.c
new file mode 100644 (file)
index 0000000..a0ab508
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2012-2014, NVIDIA CORPORATION. All rights reserved
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <assert.h>
+#include <debug.h>
+#include <string.h>
+#include <platform.h>
+#include <reg.h>
+
+#include <platform/platform_fuse.h>
+#include <platform/memmap.h>
+
+/*
+ * Fuse location information.
+ */
+#if defined(TARGET_T124) || defined(TARGET_T132)
+
+#define ODM_PROD_ADDR                  (0x0)
+#define ODM_PROD_START_BIT             (11)
+
+#define ODM_RESERVED0_ADDR             (0x2E)
+#define ODM_RESERVED0_START_BIT                (5)
+
+#elif defined(TARGET_T210) || defined(TARGET_T186)
+
+#define ODM_PROD_ADDR                  (0x0)
+#define ODM_PROD_START_BIT             (11)
+
+#define ODM_RESERVED0_ADDR             (0x2E)
+#define ODM_RESERVED0_START_BIT                (17)
+
+#else
+
+#error unknown architecture
+
+#endif
+
+/*
+ * The rollback protection fuses are in the ODM reserved fuse range.
+ * ODM reserved fuses are 256bits wide.  To calculate the location of
+ * a given fuse we need to determine its offset from ODM_RESERVED0's
+ * address/start bit.
+ *
+ * Note that ODM reserved addresses increment by 2 (so if ODM_RESERVED0
+ * is 0x2e then ODM_RESERVED1 is 0x30, etc).
+ */
+
+/* calculate ODM_RESERVED1[13:12] bit offset from start of reserved block */
+#define RB_ENABLE_BIT                  (ODM_RESERVED0_START_BIT + 32 + 12)
+#define RB_KEY_PROGRAMMED_BIT          (ODM_RESERVED0_START_BIT + 32 + 13)
+
+/* given bit offset from start of reserved block get address */
+#define GET_ADDR(b)                    (ODM_RESERVED0_ADDR + (((b) / 32) * 2))
+
+/* given bit offset from start of reserved block get addr-relative start bit */
+#define GET_START_BIT(b)               ((b) % 32)
+
+/* get fuse-specific address/start bit */
+#define RB_ENABLE_ADDR                 GET_ADDR(RB_ENABLE_BIT)
+#define RB_ENABLE_START_BIT            GET_START_BIT(RB_ENABLE_BIT)
+
+#define RB_KEY_PROGRAMMED_ADDR         GET_ADDR(RB_KEY_PROGRAMMED_BIT)
+#define RB_KEY_PROGRAMMED_START_BIT    GET_START_BIT(RB_KEY_PROGRAMMED_BIT)
+
+/* fuse values table */
+static uint32_t fuse_values[FUSE_ID_MAX];
+
+/* fuse register information */
+#define FUSECTRL_0                     (0x0)
+#define FUSEADDR_0                     (0x4)
+#define FUSERDATA_0                    (0x8)
+#define FUSEWDATA_0                    (0xC)
+
+#define FUSECTRL_STATE_MASK            (0x1F << 16)
+#define FUSECTRL_STATE_IDLE            (0x4 << 16)
+
+#define FUSECTRL_CMD_MASK              (0x3 << 0)
+#define FUSECTRL_CMD_READ              (0x1)
+#define FUSECTRL_CMD_WRITE             (0x2)
+#define FUSECTRL_CMD_SENSE             (0x3)
+
+#define FUSE_WRITE32(o,v)              writel((v), TEGRA_FUSE_BASE + (o))
+#define FUSE_READ32(o)                         readl(TEGRA_FUSE_BASE + (o))
+
+/* car register information */
+#define CLK_MASK_ARM_0                 (0x48)
+#define CLK_MASK_ARM_CFG_ALL_VISIBLE   (0x10000000)
+
+#define RST_DEVICES_H_0                        (0x8)
+#define RST_DEVICES_H_SWR_FUSE_RST     (0x80)
+
+#define CLK_OUT_ENB_H_0                        (0x14)
+#define CLK_OUT_ENB_H_CLK_ENB_FUSE     (0x80)
+
+#define CAR_WRITE32(o,v)               writel((v), TEGRA_CLK_RESET_BASE + (o))
+#define CAR_READ32(o)                  readl(TEGRA_CLK_RESET_BASE + (o))
+
+static void fuse_wait_for_idle(void)
+{
+       uint32_t data;
+
+       do {
+               spin(1);
+               data = FUSE_READ32(FUSECTRL_0);
+       } while ((data & FUSECTRL_STATE_MASK) != FUSECTRL_STATE_IDLE);
+}
+
+static void fuse_cmd_sense(void)
+{
+       uint32_t data;
+
+       fuse_wait_for_idle();
+
+       data = FUSE_READ32(FUSECTRL_0);
+       data &= ~FUSECTRL_CMD_MASK;
+       data |= FUSECTRL_CMD_SENSE;
+       FUSE_WRITE32(FUSECTRL_0, data);
+
+       fuse_wait_for_idle();
+}
+
+static uint32_t fuse_read(uint32_t addr)
+{
+       uint32_t data;
+
+       fuse_cmd_sense();
+
+       fuse_wait_for_idle();
+
+       FUSE_WRITE32(FUSEADDR_0, addr);
+       data = FUSE_READ32(FUSECTRL_0);
+       data &= ~FUSECTRL_CMD_MASK;
+       data |= FUSECTRL_CMD_READ;
+       FUSE_WRITE32(FUSECTRL_0, data);
+
+       fuse_wait_for_idle();
+
+       data = FUSE_READ32(FUSERDATA_0);
+
+       return data;
+}
+
+void platform_fuse_init(void)
+{
+       uint32_t fuse_data, clk_mask_arm_data = 0;
+       bool made_visible = false;
+
+       /* for now assume fuse block is out of reset with clock enabled */
+       ASSERT(!(CAR_READ32(RST_DEVICES_H_0) & RST_DEVICES_H_SWR_FUSE_RST));
+       ASSERT(CAR_READ32(CLK_OUT_ENB_H_0) & CLK_OUT_ENB_H_CLK_ENB_FUSE);
+
+       /* clear fuse values */
+       memset(fuse_values, 0, sizeof(fuse_values));
+
+       /* make all fuse registers visible */
+       clk_mask_arm_data = CAR_READ32(CLK_MASK_ARM_0);
+       if ((clk_mask_arm_data & CLK_MASK_ARM_CFG_ALL_VISIBLE) == 0) {
+               CAR_WRITE32(CLK_MASK_ARM_0,
+                       clk_mask_arm_data | CLK_MASK_ARM_CFG_ALL_VISIBLE);
+               made_visible = true;
+       }
+
+       /* save odm production fuse value */
+       fuse_data = fuse_read(ODM_PROD_ADDR);
+       if (fuse_data & (1 << ODM_PROD_START_BIT))
+               fuse_values[FUSE_ID_ODM_PROD] = true;
+
+       /* save rollback protection enable fuse value */
+       fuse_data = fuse_read(RB_ENABLE_ADDR);
+       if (fuse_data & (1 << RB_ENABLE_START_BIT))
+               fuse_values[FUSE_ID_RB_ENABLE] = true;
+
+       /* save rollback protection key programmed fuse value */
+       fuse_data = fuse_read(RB_KEY_PROGRAMMED_ADDR);
+       if (fuse_data & (1 << RB_KEY_PROGRAMMED_START_BIT))
+               fuse_values[FUSE_ID_RB_KEY_PROGRAMMED] = true;
+
+       dprintf(SPEW, "%s: odm_prod %d rpb_en %d rpb_key_prgrm %d\n",
+               __func__,
+               fuse_values[FUSE_ID_ODM_PROD],
+               fuse_values[FUSE_ID_RB_ENABLE],
+               fuse_values[FUSE_ID_RB_KEY_PROGRAMMED]);
+
+       /* move fuses back to invisible if necessary */
+       if (made_visible) {
+               CAR_WRITE32(CLK_MASK_ARM_0, clk_mask_arm_data);
+       }
+
+       return;
+}
+
+uint32_t platform_fuse_show(enum fuse_ids fuse_id)
+{
+       if (fuse_id >= FUSE_ID_MAX)
+               return false;
+
+       return fuse_values[fuse_id];
+}
index 0bce7b1..1e18645 100644 (file)
@@ -35,6 +35,7 @@
 #include <platform.h>
 #include <platform/memmap.h>
 #include <platform/irqs.h>
+#include <platform/platform_fuse.h>
 #include <kernel/task.h>
 #include <target/debugconfig.h>
 #include <lib/monitor/monitor_vector.h>
@@ -127,6 +128,8 @@ void platform_init(void)
 
        platform_setup_keys();
 
+       platform_fuse_init();
+
        /*
         * Set SE_TZRAM_SECURITY sticky bit to respect secure TZRAM accesses.
         * Note: No need to reprogram it after LP0 exit as it's part of SE's
@@ -224,6 +227,27 @@ int platform_ss_request_handler(void)
        return 0;
 }
 
+int platform_ss_get_config(uint32_t *ss_config)
+{
+       *ss_config = 0;
+
+       /*
+        * If we're on an ODM production unit, then return bits
+        * indicating if rollback protection should be enabled and
+        * if so, should key provisioning be performed.
+        */
+       if (platform_fuse_show(FUSE_ID_ODM_PROD)) {
+               if (platform_fuse_show(FUSE_ID_RB_ENABLE)) {
+                       *ss_config |= OTE_SS_CONFIG_RB_ENABLE;
+                       if (!platform_fuse_show(FUSE_ID_RB_KEY_PROGRAMMED)) {
+                               *ss_config |= OTE_SS_CONFIG_RB_PROGRAM_KEY;
+                       }
+               }
+       }
+
+       return 0;
+}
+
 void platform_get_device_id(te_device_id_args_t *out)
 {
        if (out)
index 0e666a8..27a3b6a 100644 (file)
 #define TEGRA_PMC_BASE                 0x7000E400
 #define TEGRA_PMC_SIZE                 SZ_256
 
+#define TEGRA_FUSE_BASE                        0x7000F800
+#define TEGRA_FUSE_SIZE                        SZ_1K
+
 #define TEGRA_SE_BASE                  0x70012000
 #define TEGRA_SE_SIZE                  SZ_8K
 
diff --git a/platform/tegra/include/platform/platform_fuse.h b/platform/tegra/include/platform/platform_fuse.h
new file mode 100644 (file)
index 0000000..84c60c5
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef __PLATFORM_FUSE_H
+#define __PLATFORM_FUSE_H
+
+/*
+ * ODM_PROD          -> ODM production fuse set
+ * RB_ENABLE         -> enable rollback protection feature
+ * RB_KEY_PROGRAMMED -> rollback protection key has been programmed
+ */
+enum fuse_ids {
+       FUSE_ID_ODM_PROD,
+       FUSE_ID_RB_ENABLE,
+       FUSE_ID_RB_KEY_PROGRAMMED,
+       FUSE_ID_MAX
+};
+
+void platform_fuse_init(void);
+uint32_t platform_fuse_show(enum fuse_ids fuse);
+
+#endif
index f0e4fc5..2ba5741 100644 (file)
@@ -46,6 +46,7 @@ void platform_enable_debug_intf(void);
 void platform_set_intr_ready_state(bool, struct tz_monitor_frame *frame);
 status_t platform_ss_register_handler(struct tz_monitor_frame *frame);
 int platform_ss_request_handler(void);
+int platform_ss_get_config(uint32_t *ss_config);
 void platform_get_device_id(te_device_id_args_t *args);
 uint32_t platform_get_time_us(void);
 void platform_clean_invalidate_cache_range(vaddr_t range, uint32_t size);
index a26c982..e362704 100644 (file)
@@ -19,6 +19,7 @@ PLATFORM_SOC_DIR := $(LOCAL_DIR)/$(PLATFORM_SOC)
 
 MODULE_SRCS += \
        $(COMMON_DIR)/cpu_early_init.S  \
+       $(COMMON_DIR)/fuse.c            \
        $(COMMON_DIR)/tz.c              \
        $(COMMON_DIR)/cpu.c             \
        $(COMMON_DIR)/platform.c        \