// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019 MediaTek Inc. */ #define pr_fmt(fmt) "[TMEM] ssmr: " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ssmr_internal.h" #define SSMR_FEATURES_DT_UNAME "memory-ssmr-features" #define FFA_ENABLED_DT_UNAME "memory-ffa-enabled" #define SVP_FEATURES_DT_UNAME "SecureVideoPath" #define SVP_ON_MTEE_DT_UNAME "MTEE" #define SVP_STATIC_RESERVED_DT_UNAME "mediatek,reserve-memory-svp" #define GRANULARITY_SIZE 0x200000 static struct device *sec_ssmr_dev; static struct device *apmd_ssmr_dev; static struct device *apscp_ssmr_dev; static struct SSMR_HEAP_INFO _ssmr_heap_info[__MAX_NR_SSMR_FEATURES]; #if IS_ENABLED(CONFIG_MTK_SEC_VIDEO_PATH_SUPPORT) || \ IS_ENABLED(CONFIG_TRUSTONIC_TEE_SUPPORT) || \ IS_ENABLED(CONFIG_MICROTRUST_TEE_SUPPORT) static void set_svp_reserve_memory(void) { struct device_node *rmem_node; struct reserved_mem *rmem; struct SSMR_Feature *feature; u64 base, size; feature = &_ssmr_feats[SSMR_FEAT_SVP_REGION]; /* Get reserved memory */ rmem_node = of_find_compatible_node(NULL, NULL, SVP_STATIC_RESERVED_DT_UNAME); if (!rmem_node) return; rmem = of_reserved_mem_lookup(rmem_node); if (!rmem) { pr_info("svp cannot lookup reserved memory\n"); return; } base = rmem->base; size = rmem->size; pr_info("%s, svp reserved pa base=0x%lx, size=0x%lx\n", __func__, base, size); feature->use_cache_memory = true; feature->count = size / PAGE_SIZE; feature->cache_page = phys_to_page(base); } #endif /* query ssmr heap name and id for ION */ int ssmr_query_total_sec_heap_count(void) { int i; int total_heap_count = 0; for (i = 0; i < __MAX_NR_SSMR_FEATURES; i++) { if (!strncmp(_ssmr_feats[i].enable, "on", 2)) { _ssmr_heap_info[total_heap_count].heap_id = i; snprintf(_ssmr_heap_info[total_heap_count].heap_name, NAME_SIZE, "ion_%s_heap", _ssmr_feats[i].feat_name); total_heap_count++; } } return total_heap_count; } int ssmr_query_heap_info(int heap_index, char *heap_name) { int heap_id = 0; int i; for (i = 0; i < __MAX_NR_SSMR_FEATURES; i++) { if (i == heap_index) { heap_id = _ssmr_heap_info[i].heap_id; snprintf(heap_name, NAME_SIZE, "%s", _ssmr_heap_info[i].heap_name); return heap_id; } } return -1; } static void setup_feature_size(void) { int i = 0; struct device_node *dt_node; dt_node = of_find_node_by_name(NULL, SSMR_FEATURES_DT_UNAME); if (!dt_node) pr_info("%s, failed to find the ssmr device tree\n", __func__); if (!strncmp(dt_node->name, SSMR_FEATURES_DT_UNAME, strlen(SSMR_FEATURES_DT_UNAME))) { for (; i < __MAX_NR_SSMR_FEATURES; i++) { if (!strncmp(_ssmr_feats[i].enable, "on", 2)) { of_property_read_u64( dt_node, _ssmr_feats[i].dt_prop_name, &_ssmr_feats[i].req_size); } } } for (i = 0; i < __MAX_NR_SSMR_FEATURES; i++) { pr_info("%s, %s: %pa\n", __func__, _ssmr_feats[i].dt_prop_name, &_ssmr_feats[i].req_size); } } static void finalize_scenario_size(void) { int i = 0; for (; i < __MAX_NR_SCHEME; i++) { u64 total_size = 0; int j = 0; for (; j < __MAX_NR_SSMR_FEATURES; j++) { if ((!strncmp(_ssmr_feats[j].enable, "on", 2)) && (_ssmr_feats[j].scheme_flag & _ssmrscheme[i].flags)) { total_size += _ssmr_feats[j].req_size; } } _ssmrscheme[i].usable_size = total_size; pr_info("%s, %s: %pa\n", __func__, _ssmrscheme[i].name, &_ssmrscheme[i].usable_size); } } static int memory_ssmr_init_feature(char *name, u64 size, struct SSMR_Feature *feature, const struct file_operations *entry_fops) { if (size <= 0) { feature->state = SSMR_STATE_DISABLED; return -1; } feature->state = SSMR_STATE_ON; pr_debug("%s: %s is enable with size: %pa\n", __func__, name, &size); return 0; } static bool is_valid_feature(unsigned int feat) { if (SSMR_INVALID_FEATURE(feat)) { pr_info("%s: invalid feature_type: %d\n", __func__, feat); return false; } return true; } static void show_scheme_status(u64 size) { int i = 0; for (; i < __MAX_NR_SCHEME; i++) { int j = 0; pr_info("**** %s (size: %pa)****\n", _ssmrscheme[i].name, &_ssmrscheme[i].usable_size); for (; j < __MAX_NR_SSMR_FEATURES; j++) { if (_ssmr_feats[j].scheme_flag & _ssmrscheme[i].flags) { pr_info("%s: size= %pa, state=%s\n", _ssmr_feats[j].feat_name, &_ssmr_feats[j].req_size, ssmr_state_text[_ssmr_feats[j].state]); } } } } static int get_reserved_memory(struct device *dev) { struct device_node *np; struct reserved_mem *rmem; np = of_parse_phandle(dev->of_node, "memory-region", 0); if (!np) { pr_info("%s, no ssmr region\n", __func__); return -EINVAL; } rmem = of_reserved_mem_lookup(np); of_node_put(np); if (!rmem) { pr_info("%s, no ssmr device info\n", __func__); return -EINVAL; } pr_info("rmem base=%pa, size=%pa\n", &rmem->base, &rmem->size); /* * setup init device with rmem */ of_reserved_mem_device_init_by_idx(dev, dev->of_node, 0); return 0; } static int ssmr_reserved_mem_alloc(struct SSMR_Feature *feature, phys_addr_t *pa, unsigned long *size, u64 upper_limit, struct device *ssmr_dev) { *pa = feature->phy_addr; *size = feature->req_size; pr_info("%s, %s reserved pa base=0x%lx, size=0x%lx\n", __func__, feature->feat_name, *pa, *size); return 0; } static int ssmr_dma_alloc(struct SSMR_Feature *feature, phys_addr_t *pa, unsigned long *size, u64 upper_limit, struct device *ssmr_dev) { size_t alloc_size; int offline_retry = 0; /* Determine alloc size by feature */ alloc_size = feature->req_size + GRANULARITY_SIZE; feature->alloc_size = alloc_size; feature->phy_addr = 0; pr_debug("%s %d: upper_limit: %llx, feature{ alloc_size : 0x%lx", __func__, __LINE__, upper_limit, alloc_size); /* * setup init device with rmem */ of_reserved_mem_device_init_by_idx(ssmr_dev, ssmr_dev->of_node, 0); while (!feature->phy_addr && offline_retry < 20) { feature->virt_addr = dma_alloc_attrs(ssmr_dev, alloc_size, &feature->phy_addr, GFP_KERNEL, DMA_ATTR_NO_KERNEL_MAPPING); #if IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT) if (!feature->phy_addr) { offline_retry++; msleep(100); } #else if (feature->phy_addr == U32_MAX) { feature->phy_addr = 0; offline_retry++; msleep(200); } #endif } if (feature->phy_addr) { pr_info("%s: feature: %s, pa=%pad is allocated, retry = %d\n", __func__, feature->feat_name, &feature->phy_addr, offline_retry); } else { pr_info("%s: feature: %s, ssmr offline failed, retry = %d\n", __func__, feature->feat_name, offline_retry); return -1; } if (pa) { *pa = dma_to_phys(ssmr_dev, feature->phy_addr); /* s2-map and s2-unamp must be 2MB alignment */ if (feature->phy_addr % GRANULARITY_SIZE) { /* pa add 1MB, then pa is 2MB alignment */ *pa = *pa + GRANULARITY_SIZE/2; pr_info("%s: feature: %s, adjust 2MB alignment: pa=0x%lx, retry = %d\n", __func__, feature->feat_name, *pa, offline_retry); } } if (size) *size = feature->req_size; /* check pa must be 2MB alignment */ if (*pa % GRANULARITY_SIZE) { pr_info("%s: feature: %s, pa=%pad is not 2MB alignment\n", __func__, feature->feat_name, *pa); return -1; } /* * kmemleak scan secure memory(stage2 is unmapping), so that ignore * the cma space in order not to scan stage2 unmapping space */ kmemleak_ignore_phys(*pa); return 0; } static int ssmr_cma_alloc(struct SSMR_Feature *feature, phys_addr_t *pa, unsigned long *size, u64 upper_limit, struct device *ssmr_dev) { size_t alloc_size; int offline_retry = 0; /* Determine alloc size by feature */ alloc_size = feature->req_size; feature->alloc_size = alloc_size; pr_debug("%s %d: upper_limit: %llx, feature{ alloc_size : 0x%lx", __func__, __LINE__, upper_limit, alloc_size); /* * setup init device with rmem */ of_reserved_mem_device_init_by_idx(ssmr_dev, ssmr_dev->of_node, 0); do { /* s2-map and s2-unamp must be 2MB alignment */ feature->cma_page = cma_alloc(ssmr_dev->cma_area, alloc_size >> PAGE_SHIFT, get_order(SZ_2M), GFP_KERNEL); if (!feature->cma_page) { offline_retry++; msleep(100); } } while ((feature->cma_page == NULL) && (offline_retry < 20)); if (feature->cma_page) { feature->phy_addr = page_to_phys(feature->cma_page); pr_info("%s: feature: %s, pa=%pad is allocated, retry = %d\n", __func__, feature->feat_name, &feature->phy_addr, offline_retry); } else { pr_info("%s: feature: %s, ssmr offline failed, retry = %d\n", __func__, feature->feat_name, offline_retry); return -1; } if (pa) *pa = feature->phy_addr; if (size) *size = feature->req_size; /* * kmemleak scan secure memory(stage2 is unmapping), so that ignore * the cma space in order not to scan stage2 unmapping space */ kmemleak_ignore_phys(*pa); return 0; } static int memory_region_offline(struct SSMR_Feature *feature, phys_addr_t *pa, unsigned long *size, u64 upper_limit) { struct device_node *np; size_t alloc_size; struct page *page; struct device *ssmr_dev; int ret; if (!strncmp(feature->feat_name, _ssmr_feats[SSMR_FEAT_AP_MD_SHM].feat_name, TRUSTED_MEM_AP_MD_SHM)) { ssmr_dev = apmd_ssmr_dev; } else if (!strncmp(feature->feat_name, _ssmr_feats[SSMR_FEAT_AP_SCP_SHM].feat_name, TRUSTED_MEM_AP_SCP_SHM)) { ssmr_dev = apscp_ssmr_dev; } else { ssmr_dev = sec_ssmr_dev; } if (!ssmr_dev) { pr_info("%s: feature: %s, No ssmr device\n", __func__, feature->feat_name); return -EINVAL; } if (feature->cache_page) { alloc_size = (feature->count) << PAGE_SHIFT; page = feature->cache_page; if (pa) *pa = page_to_phys(page); if (size) *size = alloc_size; feature->phy_addr = page_to_phys(page); feature->alloc_size = alloc_size; pr_info("%s: feature: %s, [cache memory] pa %pa(%zx) is allocated\n", __func__, feature->feat_name, &feature->phy_addr, alloc_size); return 0; } np = of_parse_phandle(ssmr_dev->of_node, "memory-region", 0); if (!np) { pr_info(" %s: feature: %s, No ssmr region\n", __func__, feature->feat_name); return -EINVAL; } if (feature->is_reserved_mem) { ret = ssmr_reserved_mem_alloc(feature, pa, size, upper_limit, ssmr_dev); } else { if (feature->is_dma_alloc) ret = ssmr_dma_alloc(feature, pa, size, upper_limit, ssmr_dev); else ret = ssmr_cma_alloc(feature, pa, size, upper_limit, ssmr_dev); } return ret; } static int _ssmr_offline_internal(phys_addr_t *pa, unsigned long *size, u64 upper_limit, unsigned int feat) { int retval = 0; struct SSMR_Feature *feature = NULL; feature = &_ssmr_feats[feat]; pr_info("%s: START: feature: %s, state: %s\n", __func__, feat < __MAX_NR_SSMR_FEATURES ? feature->feat_name : "NULL", ssmr_state_text[feature->state]); if (feature->state != SSMR_STATE_ON) { retval = -EBUSY; goto out; } feature->state = SSMR_STATE_OFFING; retval = memory_region_offline(feature, pa, size, upper_limit); if (retval < 0) { feature->state = SSMR_STATE_ON; retval = -EAGAIN; goto out; } feature->state = SSMR_STATE_OFF; pr_info("%s: feature: %s, pa: 0x%lx, size: 0x%lx\n", __func__, feature->feat_name, *pa, *size); out: pr_info("%s: END: feature: %s, state: %s, retval: %d\n", __func__, feat < __MAX_NR_SSMR_FEATURES ? _ssmr_feats[feat].feat_name : "NULL", ssmr_state_text[feature->state], retval); if (retval < 0) show_scheme_status(feature->req_size); return retval; } int ssmr_offline(phys_addr_t *pa, unsigned long *size, bool is_64bit, unsigned int feat) { if (!is_valid_feature(feat)) return -EINVAL; return _ssmr_offline_internal( pa, size, is_64bit ? UPPER_LIMIT64 : UPPER_LIMIT32, feat); } EXPORT_SYMBOL(ssmr_offline); static int memory_region_online(struct SSMR_Feature *feature) { size_t alloc_size; struct device *ssmr_dev; if (!strncmp(feature->feat_name, _ssmr_feats[SSMR_FEAT_AP_MD_SHM].feat_name, TRUSTED_MEM_AP_MD_SHM)) { ssmr_dev = apmd_ssmr_dev; } else if (!strncmp(feature->feat_name, _ssmr_feats[SSMR_FEAT_AP_SCP_SHM].feat_name, TRUSTED_MEM_AP_SCP_SHM)) { ssmr_dev = apscp_ssmr_dev; } else { ssmr_dev = sec_ssmr_dev; } if (!ssmr_dev) pr_info("%s: No ssmr device\n", __func__); if (feature->use_cache_memory) { feature->alloc_pages = 0; feature->alloc_size = 0; feature->phy_addr = 0; return 0; } alloc_size = feature->alloc_size; if (feature->phy_addr && !feature->is_reserved_mem) { if (feature->is_dma_alloc) dma_free_attrs(ssmr_dev, alloc_size, feature->virt_addr, feature->phy_addr, DMA_ATTR_NO_KERNEL_MAPPING); else cma_release(ssmr_dev->cma_area, feature->cma_page, alloc_size >> PAGE_SHIFT); feature->alloc_size = 0; feature->phy_addr = 0; } return 0; } static int _ssmr_online_internal(unsigned int feat) { int retval; struct SSMR_Feature *feature = NULL; feature = &_ssmr_feats[feat]; pr_info("%s: START: feature: %s, state: %s\n", __func__, feat < __MAX_NR_SSMR_FEATURES ? _ssmr_feats[feat].feat_name : "NULL", ssmr_state_text[feature->state]); if (feature->state != SSMR_STATE_OFF) { retval = -EBUSY; goto out; } feature->state = SSMR_STATE_ONING_WAIT; retval = memory_region_online(feature); feature->state = SSMR_STATE_ON; out: pr_info("%s: END: feature: %s, state: %s, retval: %d", __func__, feat < __MAX_NR_SSMR_FEATURES ? _ssmr_feats[feat].feat_name : "NULL", ssmr_state_text[feature->state], retval); return retval; } int ssmr_online(unsigned int feat) { if (!is_valid_feature(feat)) return -EINVAL; return _ssmr_online_internal(feat); } EXPORT_SYMBOL(ssmr_online); bool is_page_based_memory(enum TRUSTED_MEM_TYPE mem_type) { return _ssmr_feats[mem_type].is_page_based && (_ssmr_feats[mem_type].req_size > 0); } bool is_svp_on_mtee(void) { struct device_node *dt_node; dt_node = of_find_node_by_name(NULL, SVP_ON_MTEE_DT_UNAME); if (!dt_node) return false; return true; } bool is_svp_enabled(void) { struct device_node *dt_node; dt_node = of_find_node_by_name(NULL, SVP_FEATURES_DT_UNAME); if (!dt_node) return false; return true; } bool is_ffa_enabled(void) { #if IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT) struct device_node *dt_node; dt_node = of_find_node_by_name(NULL, FFA_ENABLED_DT_UNAME); if (!dt_node) return false; return true; #else return false; #endif } int sec_ssmr_init(struct platform_device *pdev) { int i; sec_ssmr_dev = &pdev->dev; pdev->dev.coherent_dma_mask = DMA_BIT_MASK(64); /* setup secure feature size */ setup_feature_size(); /* ssmr region init */ finalize_scenario_size(); #if IS_ENABLED(CONFIG_MTK_SEC_VIDEO_PATH_SUPPORT) ||\ IS_ENABLED(CONFIG_TRUSTONIC_TEE_SUPPORT) ||\ IS_ENABLED(CONFIG_MICROTRUST_TEE_SUPPORT) /* check svp statis reserved status */ set_svp_reserve_memory(); #endif for (i = 0; i < __MAX_NR_SSMR_FEATURES; i++) { memory_ssmr_init_feature( _ssmr_feats[i].feat_name, _ssmr_feats[i].req_size, &_ssmr_feats[i], _ssmr_feats[i].proc_entry_fops); } get_reserved_memory(&pdev->dev); pr_info("sec_ssmr init done\n"); return 0; } int apmd_ssmr_init(struct platform_device *pdev) { apmd_ssmr_dev = &pdev->dev; pdev->dev.coherent_dma_mask = DMA_BIT_MASK(64); memory_ssmr_init_feature(_ssmr_feats[SSMR_FEAT_AP_MD_SHM].feat_name, _ssmr_feats[SSMR_FEAT_AP_MD_SHM].req_size, &_ssmr_feats[SSMR_FEAT_AP_MD_SHM], _ssmr_feats[SSMR_FEAT_AP_MD_SHM].proc_entry_fops); get_reserved_memory(&pdev->dev); pr_info("apmd_ssmr init done\n"); return 0; } int apscp_ssmr_init(struct platform_device *pdev) { struct device_node *np; struct reserved_mem *rmem; apscp_ssmr_dev = &pdev->dev; pdev->dev.coherent_dma_mask = DMA_BIT_MASK(64); memory_ssmr_init_feature(_ssmr_feats[SSMR_FEAT_AP_SCP_SHM].feat_name, _ssmr_feats[SSMR_FEAT_AP_SCP_SHM].req_size, &_ssmr_feats[SSMR_FEAT_AP_SCP_SHM], _ssmr_feats[SSMR_FEAT_AP_SCP_SHM].proc_entry_fops); np = of_parse_phandle(apscp_ssmr_dev->of_node, "memory-region", 0); if (!np) { pr_info("%s, no ssmr region\n", __func__); return -EINVAL; } rmem = of_reserved_mem_lookup(np); if (!rmem) { pr_info("%s, no ssmr device info\n", __func__); return -EINVAL; } _ssmr_feats[SSMR_FEAT_AP_SCP_SHM].phy_addr = rmem->base; _ssmr_feats[SSMR_FEAT_AP_SCP_SHM].req_size = rmem->size; get_reserved_memory(&pdev->dev); pr_info("apscp_ssmr init done\n"); return 0; }