/** * * 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 #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. * Shown value during bootup, known cause: * (0x00) cold boot * (0x10) reboot * (0x33) secure watchdog reboot */ static uint32_t reset_status_register; static uint32_t sbl_wonce_register; static uint32_t reset_debug_register; static uint32_t watchdog_status; // The following definitions and locations are taken from the sbl implementation. /* AVM - Fault Register consists of four other registers (from SBL) * Data Fault Status Register (DSFR) * Instruction Fault Status Register (ISFR) * Data Fault Address Register (DFAR) * Instruction Fault Address Register (IFAR) */ struct fault_register_type { uint32_t ISFR; uint32_t IFAR; uint32_t DSFR; uint32_t DFAR; }; static struct fault_register_type fault_register; #define BOOT_SHARED_IMEM_MAGIC_NUM 0xC1F8DB40 /* AVM: Dakota - Architektur */ struct boot_shared_imem_cookie_type_dakota { /* 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*/ /* AVM: Write Once Register * This is a register set to zero only on cold start and short power cut. * We write value 0x504f4f46 in to have an additional indication about the reboot. */ uint32_t wonce_register; /* AVM: Fault Register */ struct fault_register_type fault_register; }; /* AVM: Hawkeye - Architektur */ struct boot_shared_imem_cookie_type_hawkeye { /* 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; /* Boot Log Location Pointer to be accessed in UEFI */ uint64_t boot_log_addr; /* Boot Log Size */ uint32_t boot_log_size; /* Boot failure count */ uint32_t boot_fail_count; /* Error code delivery through EDL */ uint32_t sbl1_error_type; /* Please add new cookie here, do NOT modify or rearrange the existing cookies*/ /* AVM: Write Once Register * This is a register set to zero only on cold start and short power cut. * We write value 0x504f4f46 in to have an additional indication about the reboot. */ uint32_t wonce_register; /* AVM: Fault Register */ struct fault_register_type fault_register; /* AVM: Reset Debug Register */ uint32_t reset_debug_register; /* AVM: Watchdog status */ uint32_t watchdog_status; }; union boot_shared_imem_cookie_type { struct boot_shared_imem_cookie_type_dakota dakota; struct boot_shared_imem_cookie_type_hawkeye hawkeye; }; #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", reset_status_register); } void tz_proc_wonce_register(struct seq_file *m, void *data) { seq_printf(m, "SBL write once register content: 0x%x\n", sbl_wonce_register); } /* AVM: Function write the data of reset debug register in a file */ void tz_proc_debug_register(struct seq_file *m, void *data) { seq_printf(m, "SBL reboot debug: 0x%x\n", reset_debug_register); } /* AVM: Function write the data of watchdog status register in a file */ void tz_proc_wdog_register(struct seq_file *m, void *data) { seq_printf(m, "SBL Watchdog status: 0x%x\n", watchdog_status); } /* AVM: Function write the data of Fault Register in a file */ void tz_proc_fault_register(struct seq_file *m, void *data) { seq_printf(m, "SBL fault register content(secure): DSFR = 0x%08x, ISFR = 0x%08x, DFAR = 0x%08x, IFAR = 0x%08x\n", fault_register.DSFR, fault_register.ISFR, fault_register.DFAR, fault_register.IFAR); } 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) { /* AVM: to define a pointer and set it on the node from device tree */ struct device_node *np = of_find_node_by_path("/avm_boot_shared_imem"); uint32_t boot_shared_imem_base; uint32_t boot_shared_imem_min_sbl_ver; int err_code = 0; /* AVM: to read SBL version from physical storage and set the pointer on him */ struct sbl_boot_status *sbl_status = get_sbl_boot_status(); union boot_shared_imem_cookie_type *boot_shared_imem_cookie_ptr; /* AVM: to read properties from node if a node with the avm_boot_shared_imem * name existed. The node "avm_boot_shared_imem" was created in: * Dakota: Fritz_qcom-ipq4019.dtsi * Hawkeye: qcom-ipq807x-avm-common.dtsi */ add_simple_proc_file("avm/tz_version", NULL, tz_proc_stat, NULL); if (!np) { pr_err("%s: 'avm_boot_shared_imem' node was not found!\n", __func__); return -EINVAL; } err_code = of_property_read_u32(np, "base_reg", &boot_shared_imem_base); if (err_code) { pr_err("%s: 'base_reg' property of 'avm_boot_shared_imem' node was not found!\n", __func__); return err_code; } err_code = of_property_read_u32(np, "sbl_version", &boot_shared_imem_min_sbl_ver); if (err_code) { pr_err("%s: 'sbl_version' property of 'avm_boot_shared_imem' node was not found!\n", __func__); return err_code; } /* AVM: Remap boot shared memory into kernel address space. */ boot_shared_imem_cookie_ptr = (union boot_shared_imem_cookie_type *) ioremap(boot_shared_imem_base, sizeof(union boot_shared_imem_cookie_type)); if (!boot_shared_imem_cookie_ptr) { pr_err("%s: ioremap address was not found!\n", __func__); return -EINVAL; } if (of_find_compatible_node(NULL, NULL, "qcom,ipq4019")) { /* Dakota specific */ struct boot_shared_imem_cookie_type_dakota *imem = &(boot_shared_imem_cookie_ptr->dakota); if (imem->shared_imem_magic != BOOT_SHARED_IMEM_MAGIC_NUM) { pr_err("%s: not valid storage space of boot_shared_imem!\n", __func__); err_code = -EINVAL; goto error; } reset_status_register = imem->reset_status_register; /* AVM: to create new file in the /proc filesystem */ add_simple_proc_file("avm/sbl_reboot", NULL, tz_proc_reboot_status, NULL); /* AVM: SBL sends data of wonce and fault register to kernel only from a version 1222*/ if (sbl_status && sbl_status->sbl_version >= boot_shared_imem_min_sbl_ver) { sbl_wonce_register = imem->wonce_register; fault_register = imem->fault_register; /* AVM : to create new files in the /proc filesystem */ add_simple_proc_file("avm/sbl_wonce", NULL, tz_proc_wonce_register, NULL); add_simple_proc_file("avm/sbl_fault_register", NULL, tz_proc_fault_register, NULL); } } else { /* Hawkeye specific */ struct boot_shared_imem_cookie_type_hawkeye *imem = &(boot_shared_imem_cookie_ptr->hawkeye); if (imem->shared_imem_magic != BOOT_SHARED_IMEM_MAGIC_NUM) { pr_err("%s: not valid storage space of boot_shared_imem!\n", __func__); err_code = -EINVAL; goto error; } reset_status_register = imem->reset_status_register; /* AVM: to create new file in the /proc filesystem */ add_simple_proc_file("avm/sbl_reboot", NULL, tz_proc_reboot_status, NULL); /* AVM: SBL sends data of wonce and fault register to kernel only from a version 1213*/ if (sbl_status && sbl_status->sbl_version >= boot_shared_imem_min_sbl_ver) { sbl_wonce_register = imem->wonce_register; fault_register = imem->fault_register; reset_debug_register = imem->reset_debug_register; watchdog_status = imem->watchdog_status; /* AVM: to create new files in the /proc filesystem */ add_simple_proc_file("avm/sbl_wonce", NULL, tz_proc_wonce_register, NULL); add_simple_proc_file("avm/sbl_fault_register", NULL, tz_proc_fault_register, NULL); add_simple_proc_file("avm/sbl_reset_debug", NULL, tz_proc_debug_register, NULL); add_simple_proc_file("avm/sbl_wdog_status", NULL, tz_proc_wdog_register, NULL); } } error: iounmap(boot_shared_imem_cookie_ptr); return err_code; } late_initcall(avm_tz_proc_setup); #endif