/** * * 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 */ #define pr_fmt(fmt) "[avm_scm] " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static struct { bool initialized; char version[AVM_TZ_VERSION_SIZE]; uint64_t features; } avm_tz_info; bool avm_get_tz_version(char *buffer, size_t size) { BUG_ON(!avm_tz_info.initialized); return !*avm_tz_info.version ? false : strlcpy(buffer, avm_tz_info.version, size); } uint64_t avm_tz_supported_features(void) { BUG_ON(!avm_tz_info.initialized); return avm_tz_info.features; } #if defined(CONFIG_AVM_TZ_TYPE_ATF_RTE) static struct platform_device *avm_scm_pdev; static __init int __avm_tz_supported_features(uint64_t *avm_tz_features) { int err; uint32_t feature_flags_upper, feature_flags_lower; asm volatile(".arch_extension sec\n" "mov r0, %3\n" "smc #0 @ switch to secure world\n" "mov %0, r0\n" "mov %1, r1\n" "mov %2, r2\n" : "=r"(err), "=r"(feature_flags_upper), "=r"(feature_flags_lower) : "r"(AVM_SVC_GET_FEATURES) : "r0", "r1", "r2", "cc", "memory"); if (err) { dev_err(&avm_scm_pdev->dev, "AVM_SVC_GET_FEATURES (%08x) did not return SMC_OK: %d.\n", AVM_SVC_GET_FEATURES, err); *avm_tz_features = 0; return -EIO; } *avm_tz_features = (uint64_t)feature_flags_upper << 32 | feature_flags_lower; return 0; } static __init bool __avm_get_tz_version(char *avm_tz_version) { int ret, err, version_len; char *dma_version_str; dma_addr_t dma_handle; struct device *dev = &avm_scm_pdev->dev; dma_version_str = kzalloc(AVM_TZ_VERSION_SIZE, GFP_KERNEL); if (!dma_version_str) { ret = -ENOMEM; goto error; } dma_handle = dma_map_single(dev, dma_version_str, AVM_TZ_VERSION_SIZE, DMA_FROM_DEVICE); err = dma_mapping_error(dev, dma_handle); if (err) { dev_err(dev, "Error obtaining TZ version: Could not map memory for TZ access.\n"); ret = -EFAULT; goto error_free; } asm volatile(".arch_extension sec\n" "mov r0, %3\n" "mov r1, %4\n" "mov r2, %5\n" "smc #0 @ switch to secure world\n" "mov %0, r0\n" "mov %1, r1\n" : "=r"(err), "=r"(version_len), "=m"(*(char(*)[AVM_TZ_VERSION_SIZE])dma_version_str) : "r"(AVM_SMC_GET_TZ_VERSION), "r"(dma_handle), "r"(AVM_TZ_VERSION_SIZE) : "r0", "r1", "r2", "cc", "memory"); dma_unmap_single(dev, dma_handle, AVM_TZ_VERSION_SIZE, DMA_FROM_DEVICE); if (err) { dev_err(dev, "AVM_SMC_GET_TZ_VERSION (%08x) did not return SMC_OK: %d.\n", AVM_SMC_GET_TZ_VERSION, err); ret = -EIO; goto error_free; } if (version_len >= AVM_TZ_VERSION_SIZE) { dev_err(dev, "Error obtaining TZ version: String too long (%d bytes) for buffer.\n", version_len); ret = -E2BIG; goto error_free; } strlcpy(avm_tz_version, dma_version_str, version_len + 1); ret = 0; error_free: kfree(dma_version_str); error: if (ret) *avm_tz_version = 0; return ret; } static __init int avm_scm_probe(struct platform_device *pdev) { int err; pr_info("%s ...\n", __func__); if (avm_scm_pdev) return -EEXIST; avm_scm_pdev = pdev; err = __avm_tz_supported_features(&avm_tz_info.features); if (err < 0) dev_err(&avm_scm_pdev->dev, "Could not get AVM TZ supported features.\n"); err = __avm_get_tz_version(avm_tz_info.version); if (err < 0) dev_err(&avm_scm_pdev->dev, "Could not get AVM TZ version.\n"); /* * We use a memory barrier here to prevent a race where due to * reordering, .initialized is set to true too early, s.t. * avm_get_tz_version() or avm_tz_supported_features() could erroneously * read it as true at a point where it should still be false (and * thus trigger a BUG). */ smp_store_release(&avm_tz_info.initialized, true); return 0; } static const struct of_device_id avm_scm_dt_match[] = { { .compatible = "avm,scm", }, {}, }; static struct platform_driver avm_scm_driver = { .probe = avm_scm_probe, .driver = { .name = "avm_scm", .of_match_table = avm_scm_dt_match, }, }; static int __init avm_scm_init(void) { return platform_driver_register(&avm_scm_driver); } core_initcall(avm_scm_init); int avm_get_atf_last_log(void *ker_buf, u32 buf_len) { int ret; dma_addr_t dma_handle; struct device *dev = &avm_scm_pdev->dev; dma_handle = dma_map_single(dev, ker_buf, buf_len, DMA_FROM_DEVICE); ret = dma_mapping_error(dev, dma_handle); if (ret != 0) { pr_err("DMA Mapping Error : %d\n", ret); return -EINVAL; } asm volatile( ".arch_extension sec\n" "mov r0, %2\n" "mov r1, %3\n" "mov r2, %4\n" "smc #0 @ switch to secure world\n" "mov %0, r0\n" : "=r" (ret), "=m" (*(char (*)[buf_len]) ker_buf) : "r" (AVM_SMC_GET_LAST_LOG), "r" (dma_handle), "r" (buf_len) : "r0", "r1", "r2", "cc", "memory"); dma_unmap_single(dev, dma_handle, buf_len, DMA_FROM_DEVICE); return ret; } #elif defined(CONFIG_AVM_TZ_TYPE_QCA_RTE) static __init int avm_tz_init(void) { uint32_t version; bool is_avm_tz; bool modified = false; register u32 r0 asm("r0") = AVM_SMC_GET_TZ_VERSION; register u32 r1 asm("r1") = 0; register u32 r2 asm("r2") = 0; uint32_t magic; int ret, version_len; 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; is_avm_tz = true; modified = r2; } else { version = 0; is_avm_tz = false; } /* Get feature flags. */ if (is_avm_tz && version > 0) avm_tz_info.features |= AVM_TZ_IS_AVM_TZ | AVM_TZ_FEAT_FIQ; if (version >= 10000) avm_tz_info.features |= AVM_TZ_FEAT_FIQ_PREEMPT; /* Get TZ version. */ version_len = snprintf(avm_tz_info.version, AVM_TZ_VERSION_SIZE, "%d%s", version, (modified ? "-M" : "")); if (version_len >= AVM_TZ_VERSION_SIZE) { pr_err("Error obtaining TZ version: String too long for buffer (%d).\n", version_len); *avm_tz_info.version = 0; ret = -E2BIG; } else { ret = 0; } /* * We use a memory barrier here to prevent a race where due to * reordering, .initialized is set to true too early, s.t. * avm_get_tz_version() or avm_tz_supported_features() could erroneously * read it as true at a point where it should still be false (and * thus trigger a BUG). */ smp_store_release(&avm_tz_info.initialized, true); return ret; } core_initcall(avm_tz_init); #endif #ifdef CONFIG_AVM_FASTIRQ_TZ static void _avm_tz_set_fiq_handling(u32 mode) { #ifndef CONFIG_AVM_TZ_TYPE_ATF_RTE 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"); #endif } 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"); } uint32_t avm_tz_secure_wdt_pet(void) { register u32 r0 asm("r0") = AVM_SMC_SEC_WDT_PET; asm volatile(__asmeq("%0", "r0") ".arch_extension sec\n" "smc #0 @ switch to secure world\n" : "=r"(r0) : "r"(r0) : "r1", "r2", "r3", "cc"); return r0; } uint32_t avm_tz_secure_wdt_config(bool enable, uint32_t bark_timeout, uint32_t bite_timeout) { register u32 r0 asm("r0") = AVM_SMC_SEC_WDT_CFG; register u32 r1 asm("r1") = enable; register u32 r2 asm("r2") = bark_timeout; register u32 r3 asm("r3") = bite_timeout; asm volatile(__asmeq("%0", "r0") __asmeq("%1", "r1") __asmeq("%2", "r2") __asmeq("%3", "r3") ".arch_extension sec\n" "smc #0 @ switch to secure world\n" : "=r"(r0), "=r"(r1), "=r"(r2), "=r"(r3) : "r"(r0), "r"(r1), "r"(r2), "r"(r3) : "cc"); return r0; } void avm_secure_wdt_pet(void) { if (avm_tz_supported_features() & AVM_TZ_FEAT_SECURE_WDT) avm_tz_secure_wdt_pet(); else qcom_scm_avm_wdt_pet(); } void avm_secure_wdt_config(u32 enable, u32 bark_ms, u32 bite_ms) { if (avm_tz_supported_features() & AVM_TZ_FEAT_SECURE_WDT) avm_tz_secure_wdt_config(enable, bark_ms, bite_ms); else qcom_scm_avm_wdt_config(enable, bark_ms, bite_ms); } #endif /* 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; }; /* AVM: Maple - Architektur */ struct boot_shared_imem_cookie_type_maple { /* 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; /* Storage Space * - Available => (SHARED_IMEM_BOOT_SIZE = 200) => 200 Byte * - Used => 88 Byte * - Free => 200 Byte - 88 Byte = 112 Byte */ }; union boot_shared_imem_cookie_type { struct boot_shared_imem_cookie_type_dakota dakota; struct boot_shared_imem_cookie_type_hawkeye hawkeye; struct boot_shared_imem_cookie_type_maple maple; }; #if defined(CONFIG_PROC_FS) #include static void tz_proc_reboot_status(struct seq_file *m, void *data) { seq_printf(m, "SBL reboot status: 0x%x\n", reset_status_register); } static 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 */ static 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 */ static 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 */ static 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); } static void tz_proc_show_version(struct seq_file *m, void *data __maybe_unused) { seq_puts(m, avm_tz_info.version); } static void tz_proc_is_sec_wdt_enabled(struct seq_file *m, void *data __maybe_unused) { seq_printf(m, "%d\n", avm_tz_info.features & AVM_TZ_FEAT_NO_SEC_WDT_ENABLED ? 0 : 1); } static int is_maple_sbl_supported(union sbl_boot_status *sbl_status) { int sbl_status_version; struct avm_tag_version booted_sbl_version; if (!sbl_status) { return 0; } sbl_status_version = get_sbl_boot_status_version(sbl_status); if (sbl_status_version != 2) { return 0; } booted_sbl_version = get_avm_tag_version_from_string(sbl_status->v2.sbl_version); return avm_tag_version_valid(booted_sbl_version); } static int 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 it */ union 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 * Maple: arm64/boot/dts/qcom/qcom-ipq5018-avm-common.dtsi */ add_simple_proc_file("avm/tz_version", NULL, tz_proc_show_version, NULL); add_simple_proc_file("avm/tz_is_sec_wdt_enabled", NULL, tz_proc_is_sec_wdt_enabled, 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; } if (!of_find_compatible_node(NULL, NULL, "qcom,ipq5018")) { 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->v1.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 if (of_find_compatible_node(NULL, NULL, "qcom,ipq5018")) { /* Maple specific */ struct boot_shared_imem_cookie_type_maple *imem = &(boot_shared_imem_cookie_ptr->maple); 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; 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 since a certain minimal version */ if (is_maple_sbl_supported(sbl_status)) { sbl_wonce_register = imem->wonce_register; fault_register = imem->fault_register; reset_debug_register = imem->reset_debug_register; watchdog_status = imem->watchdog_status; 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); } } 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->v1.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(tz_proc_setup); #endif