/** * * Copyright (C) 2016 AVM GmbH * * 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 #include #include #include #include #include #include #include #include #ifdef CONFIG_AVM_FASTIRQ_TZ void setup_avm_fiq_tz(void *dummy __maybe_unused) { register u32 r0 asm("r0") = AVM_SMC_FIQ_MODE; register u32 r1 asm("r1") = 0; asm volatile( ".arch_extension sec\n" "smc #0 @ switch to secure world\n" : : "r" (r0), "r" (r1) : "r2", "r3", "cc"); pr_err("Changed SCR FIQ Bit\n"); } static void _avm_tz_set_fiq_handling(u32 mode) { register u32 r0 asm("r0") = AVM_SMC_FIQ_MODE; register u32 r1 asm("r1") = mode; asm volatile( ".arch_extension sec\n" "smc #0 @ switch to secure world\n" : : "r" (r0), "r" (r1) : "r2", "r3", "cc"); //pr_err("Changed SCR FIQ Bit\n"); } void avm_tz_enable_fiq_handling_in_linux(void *unused) { _avm_tz_set_fiq_handling(0); } void avm_tz_disable_fiq_handling_in_linux(void *unused) { _avm_tz_set_fiq_handling(1); } void avm_tz_set_fiq_handler(void *address, void *reg_save) { register u32 r0 asm("r0") = AVM_SMC_SET_FIQ_HANDLER; register u32 r1 asm("r1") = (u32)address; register u32 r2 asm("r2") = (u32)reg_save; asm volatile( ".arch_extension sec\n" "smc #0 @ switch to secure world\n" : : "r" (r0), "r" (r1), "r" (r2) : "r3", "cc"); pr_err("Set FIQ handler address\n"); } #endif #define AVM_TZ_MAGIC 0x41564d00 uint32_t avm_get_tz_version(uint32_t *version, uint32_t *modified) { register u32 r0 asm("r0") = AVM_SMC_GET_VERSION; register u32 r1 asm("r1") = 0; register u32 r2 asm("r2") = 0; uint32_t magic; asm volatile( __asmeq("%0", "r0") __asmeq("%1", "r1") __asmeq("%2", "r2") __asmeq("%3", "r0") ".arch_extension sec\n" "smc #0 @ switch to secure world\n" : "=r" (r0), "=r" (r1), "=r" (r2) : "r" (r0) : "r3", "cc"); magic = r0; if (magic == 0x41564d00) { *version = r1; *modified = r2; return 0; } return 1; } uint32_t avm_is_avm_tz(void) { static uint32_t version; static uint32_t modified; static uint32_t already_queried; if (already_queried == 0) { already_queried = 1; if (avm_get_tz_version(&version, &modified) != 0) { version = 0; } } if (version > 0) return 1; return 0; } void avm_secure_wdt_pet(void) { qcom_scm_avm_wdt_pet(); } void avm_secure_wdt_config(u32 enable, u32 bark, u32 bite) { qcom_scm_avm_wdt_config(enable, bark, bite); } /* The reset status from the dakota is hold in a register, but unfortunately * the second stage bootloader (sbl) saves the value into a memory location * and clears the register. * As we do not want to update the sbl in the field and not clearing the * reset status register might change the behavior of the trustzone, the * following code has to be used to retrieve the value. * Note: The sbl masks the 5th bit of the value, thus the retrieved * value differs from the one shown during bootup. * Retrieved value, shown value during bootup, known cause: * 0x00 (0x00) cold boot * 0x00 (0x10) reboot * 0x23 (0x33) secure watchdog reboot */ static uint32_t dakota_reset_status; // The following definitions and locations are taken from the sbl implementation. #define BOOT_SHARED_IMEM_MAGIC_NUM 0xC1F8DB40 struct boot_shared_imem_cookie_type { /* Magic number which indicates boot shared imem has been initialized * and the content is valid. */ uint32_t shared_imem_magic; /* Number to indicate what version of this structure is being used */ uint32_t shared_imem_version; /* Pointer that points to etb ram dump buffer, should only be set by HLOS */ uint64_t etb_buf_addr; /* Region where HLOS will write the l2 cache dump buffer start address */ uint64_t *l2_cache_dump_buff_ptr; /* When SBL which is A32 allocates the 64bit pointer above it will only * consume 4 bytes. When HLOS running in A64 mode access this it will over * flow into the member below it. Adding this padding will ensure 8 bytes * are consumed so A32 and A64 have the same view of the remaining members. */ uint32_t a64_pointer_padding; /* Magic number for UEFI ram dump, if this cookie is set along with dload magic numbers, * we don't enter dload mode but continue to boot. This cookie should only be set by UEFI */ uint32_t uefi_ram_dump_magic; uint32_t ddr_training_cookie; /* Abnormal reset cookie used by UEFI */ uint32_t abnormal_reset_occurred; /* Reset Status Register */ uint32_t reset_status_register; /* Please add new cookie here, do NOT modify or rearrange the existing cookies*/ }; #define SCL_IMEM_BASE 0x08600000 #define SCL_SYSTEM_DEBUG_BASE SCL_IMEM_BASE #define SCL_SYSTEM_DEBUG_SIZE 0x0004000 #define SCL_IMEM_SECURE_BUF_BASE (SCL_SYSTEM_DEBUG_BASE + SCL_SYSTEM_DEBUG_SIZE) #define SCL_IMEM_SECURE_BUF_SIZE 0x0001000 #define SCL_IMEM_QDSS_USB_TRACE_BUF_BASE (SCL_IMEM_SECURE_BUF_BASE + SCL_IMEM_SECURE_BUF_SIZE) #define SCL_IMEM_QDSS_USB_TRACE_BUF_SIZE 0x0002000 #define SHARED_IMEM_BASE (SCL_IMEM_QDSS_USB_TRACE_BUF_BASE + SCL_IMEM_QDSS_USB_TRACE_BUF_SIZE) #define SHARED_IMEM_BOOTROM_BASE SHARED_IMEM_BASE #define SHARED_IMEM_BOOTROM_SIZE 1200 #define SHARED_IMEM_BOOT_BASE (SHARED_IMEM_BOOTROM_BASE + SHARED_IMEM_BOOTROM_SIZE) #if defined(CONFIG_PROC_FS) #include void tz_proc_reboot_status(struct seq_file *m, void *data) { seq_printf(m, "SBL reboot status: 0x%x\n", dakota_reset_status); } void tz_proc_stat(struct seq_file *m, void *data __maybe_unused) { uint32_t version, modified; if (avm_get_tz_version(&version, &modified)) { seq_puts(m, "0\n"); return; } seq_printf(m, "%d%s", version, (modified ? "M\n" : "\n")); } int avm_tz_proc_setup(void) { struct device_node *np; np = of_find_compatible_node(NULL, NULL, "qcom,ipq4019"); if (np) { void __iomem *imem = ioremap(SHARED_IMEM_BOOT_BASE, sizeof(struct boot_shared_imem_cookie_type)); if (imem) { struct boot_shared_imem_cookie_type *boot_shared_imem_cookie_ptr = (struct boot_shared_imem_cookie_type *)imem; if (boot_shared_imem_cookie_ptr->shared_imem_magic == BOOT_SHARED_IMEM_MAGIC_NUM) { dakota_reset_status = boot_shared_imem_cookie_ptr->reset_status_register; } iounmap(imem); } } add_simple_proc_file("avm/sbl_reboot", NULL, tz_proc_reboot_status, NULL); add_simple_proc_file("avm/tz_version", NULL, tz_proc_stat, NULL); return 0; } late_initcall(avm_tz_proc_setup); #endif /* print tz log */ #define TZ_BUF_LEN 0x1000 static char *avm_tz_ker_buf; struct tzbsp_log_pos_t { uint16_t wrap; /* Ring buffer wrap-around ctr */ uint16_t offset; /* Ring buffer current position */ }; struct tzbsp_diag_log_t { struct tzbsp_log_pos_t log_pos; /* Ring buffer position mgmt */ uint8_t log_buf[1]; /* Open ended array to the end * of the 4K IMEM buffer */ }; struct tzbsp_diag_t { uint32_t unused[7]; /* Unused variable is to support the * corresponding structure in trustzone */ uint32_t ring_off; uint32_t unused1[514]; struct tzbsp_diag_log_t log; }; int avm_tz_log_init(void) { struct page *page_buf; page_buf = alloc_pages(GFP_KERNEL, get_order(TZ_BUF_LEN)); if (page_buf == NULL) { pr_err("unable to get data tz buffer memory\n"); avm_tz_ker_buf = 0; return -EIO; } avm_tz_ker_buf = page_address(page_buf); pr_err("TZLOG: Buffer allocated\n"); return 0; } int avm_tz_log_print(void) { uint32_t buf_len = TZ_BUF_LEN; uint16_t wrap; struct tzbsp_diag_t *tz_diag; struct tzbsp_diag_log_t *log; uint16_t offset; uint16_t ring; int ret; if (!avm_tz_ker_buf) { pr_err("Failed to get tz log\n"); return -EIO; } /* SCM call to TZ to get the tz log */ ret = qcom_scm_tz_log(SCM_SVC_INFO, TZ_INFO_GET_DIAG_ID, avm_tz_ker_buf, buf_len); if (ret != 0) { pr_err("Error in getting tz log\n"); return -EIO; } tz_diag = (struct tzbsp_diag_t *) avm_tz_ker_buf; ring = tz_diag->ring_off; log = &tz_diag->log; offset = log->log_pos.offset; wrap = log->log_pos.wrap; pr_err("Print TZ log\n"); if (wrap != 0) { print_hex_dump(KERN_ERR, "TZ_LOG:", DUMP_PREFIX_NONE, 16, 4, (avm_tz_ker_buf + offset + ring), (buf_len - offset - ring), 1); } print_hex_dump(KERN_ERR, "TZ_LOG:", DUMP_PREFIX_NONE, 16, 4, (avm_tz_ker_buf + ring), offset, 1); pr_err("TZ log end\n"); return 0; }