// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2013-2020 TRUSTONIC LIMITED * All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. */ #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_DMA_SHARED_BUFFER #include #ifdef CONFIG_MTK_ION /* for mtk_ion, struct ion_buffer is decleared here */ #include #endif #endif #ifdef CONFIG_XEN /* To get the MFN */ #include #include #endif #include "public/mc_user.h" #include "mci/mcimcp.h" #include "main.h" #include "mcp.h" /* mcp_buffer_map */ #include "protocol.h" /* protocol_fe_uses_pages_and_vas */ #include "mmu.h" #define PHYS_48BIT_MASK (BIT(48) - 1) /* Common */ #define MMU_BUFFERABLE BIT(2) /* AttrIndx[0] */ #define MMU_CACHEABLE BIT(3) /* AttrIndx[1] */ #define MMU_EXT_NG BIT(11) /* ARMv6 and higher */ /* LPAE */ #define MMU_TYPE_PAGE (3 << 0) #define MMU_NS BIT(5) #define MMU_AP_RW_ALL BIT(6) /* AP[2:1], RW, at any privilege level */ #define MMU_AP2_RO BIT(7) #define MMU_EXT_SHARED_64 (3 << 8) /* SH[1:0], inner shareable */ #define MMU_EXT_AF BIT(10) /* Access Flag */ #define MMU_EXT_XN (((u64)1) << 54) /* XN */ /* ION */ /* Trustonic Specific flag to detect ION mem */ #define MMU_ION_BUF BIT(24) static inline long gup_local(struct mm_struct *mm, uintptr_t start, unsigned long nr_pages, int write, struct page **pages) { unsigned int gup_flags = 0; if (write) gup_flags |= FOLL_WRITE; return get_user_pages(start, nr_pages, gup_flags, pages, NULL); } static inline long gup_local_repeat(struct mm_struct *mm, uintptr_t start, unsigned long nr_pages, int write, struct page **pages) { int retries = 10; long ret = 0; while (retries--) { ret = gup_local(mm, start, nr_pages, write, pages); if (-EBUSY != ret) break; } return ret; } /* * A table that could be either a pmd or pte */ union mmu_table { u64 *entries; /* Array of PTEs */ /* Array of pages */ struct page **pages; /* Array of VAs */ uintptr_t *vas; /* Address of table */ void *addr; /* Page for table */ unsigned long page; }; /* * MMU table allocated to the Daemon or a TLC describing a world shared * buffer. * When users map a malloc()ed area into SWd, a MMU table is allocated. * In addition, the area of maximum 1MB virtual address space is mapped into * the MMU table and a handle for this table is returned to the user. */ struct tee_mmu { struct kref kref; /* Array of pages that hold buffer ptes*/ union mmu_table pte_tables[PMD_ENTRIES_MAX]; /* Actual number of ptes tables */ size_t nr_pmd_entries; /* Contains phys @ of ptes tables */ union mmu_table pmd_table; struct tee_deleter *deleter; /* Xen map to free */ unsigned long nr_pages; int pages_created; /* Leak check */ int pages_locked; /* Leak check */ u32 offset; u32 length; u32 flags; /* Pages are from user space */ bool user; bool use_pages_and_vas; /* ION case only */ struct dma_buf *dma_buf; struct dma_buf_attachment *attach; struct sg_table *sgt; }; static void tee_mmu_delete(struct tee_mmu *mmu) { unsigned long chunk, nr_pages_left = mmu->nr_pages; #ifdef CONFIG_DMA_SHARED_BUFFER if (mmu->dma_buf) { dma_buf_unmap_attachment(mmu->attach, mmu->sgt, DMA_BIDIRECTIONAL); dma_buf_detach(mmu->dma_buf, mmu->attach); dma_buf_put(mmu->dma_buf); } #endif /* Release all locked user space pages */ for (chunk = 0; chunk < mmu->nr_pmd_entries; chunk++) { union mmu_table *pte_table = &mmu->pte_tables[chunk]; unsigned long nr_pages = nr_pages_left; if (nr_pages > PTE_ENTRIES_MAX) nr_pages = PTE_ENTRIES_MAX; nr_pages_left -= nr_pages; if (!pte_table->page) break; if (mmu->user && mmu->use_pages_and_vas) { struct page **page = pte_table->pages; int i; for (i = 0; i < nr_pages; i++, page++) put_page(*page); mmu->pages_locked -= nr_pages; } else if (mmu->user) { u64 *pte64 = pte_table->entries; pte_t pte; int i; for (i = 0; i < nr_pages; i++) { #if defined(CONFIG_ARM) { pte = *pte64++; /* Unused entries are 0 */ if (!pte) break; } #else { pte.pte = *pte64++; /* Unused entries are 0 */ if (!pte.pte) break; } #endif /* pte_page() cannot return NULL */ put_page(pte_page(pte)); } mmu->pages_locked -= nr_pages; } free_page(pte_table->page); mmu->pages_created--; } if (mmu->pmd_table.page) { free_page(mmu->pmd_table.page); mmu->pages_created--; } if (mmu->pages_created || mmu->pages_locked) mc_dev_err(-EUCLEAN, "leak detected: still in use %d, still locked %d", mmu->pages_created, mmu->pages_locked); if (mmu->deleter) mmu->deleter->delete(mmu->deleter->object); kfree(mmu); /* Decrement debug counter */ atomic_dec(&g_ctx.c_mmus); } static struct tee_mmu *tee_mmu_create_common(const struct mcp_buffer_map *b_map) { struct tee_mmu *mmu; int ret = -ENOMEM; if (b_map->nr_pages > (PMD_ENTRIES_MAX * PTE_ENTRIES_MAX)) { ret = -EINVAL; mc_dev_err(ret, "data mapping exceeds %d pages: %lu", PMD_ENTRIES_MAX * PTE_ENTRIES_MAX, b_map->nr_pages); return ERR_PTR(ret); } /* Allocate the struct */ mmu = kzalloc(sizeof(*mmu), GFP_KERNEL); if (!mmu) return ERR_PTR(-ENOMEM); /* Increment debug counter */ atomic_inc(&g_ctx.c_mmus); kref_init(&mmu->kref); /* The Xen front-end does not use PTEs */ if (protocol_fe_uses_pages_and_vas()) mmu->use_pages_and_vas = true; /* Buffer info */ mmu->offset = b_map->offset; mmu->length = b_map->length; mmu->flags = b_map->flags; /* Pages info */ mmu->nr_pages = b_map->nr_pages; mmu->nr_pmd_entries = (mmu->nr_pages + PTE_ENTRIES_MAX - 1) / PTE_ENTRIES_MAX; mc_dev_devel("mmu->nr_pages %lu num_ptes_pages %zu", mmu->nr_pages, mmu->nr_pmd_entries); /* Allocate a page for the L1 table, always used for DomU */ mmu->pmd_table.page = get_zeroed_page(GFP_KERNEL); if (!mmu->pmd_table.page) goto end; mmu->pages_created++; return mmu; end: tee_mmu_delete(mmu); return ERR_PTR(ret); } static bool mmu_get_dma_buffer(struct tee_mmu *mmu, int va) { #ifdef CONFIG_DMA_SHARED_BUFFER struct dma_buf *buf; buf = dma_buf_get(va); if (IS_ERR(buf)) return false; mmu->dma_buf = buf; mmu->attach = dma_buf_attach(mmu->dma_buf, g_ctx.mcd); if (IS_ERR(mmu->attach)) goto err_attach; mmu->sgt = dma_buf_map_attachment(mmu->attach, DMA_BIDIRECTIONAL); if (IS_ERR(mmu->sgt)) goto err_map; return true; err_map: dma_buf_detach(mmu->dma_buf, mmu->attach); err_attach: dma_buf_put(mmu->dma_buf); #endif return false; } /* * Allocate MMU table and map buffer into it. * That is, create respective table entries. */ struct tee_mmu *tee_mmu_create(struct mm_struct *mm, const struct mc_ioctl_buffer *buf) { struct tee_mmu *mmu; const void *data = (const void *)(uintptr_t)buf->va; const void *reader = (const void *)((uintptr_t)data & PAGE_MASK); struct page **pages; /* Same as below, conveniently typed */ unsigned long pages_page = 0; /* Page to contain the page pointers */ unsigned long chunk; struct mcp_buffer_map b_map = { .offset = (u32)(buf->va & ~PAGE_MASK), .length = buf->len, .flags = buf->flags, }; bool writeable = buf->flags & MC_IO_MAP_OUTPUT; int ret = 0; #ifndef CONFIG_DMA_SHARED_BUFFER if (buf->flags & MMU_ION_BUF) { mc_dev_err(-EINVAL, "ION buffers not supported by kernel"); return ERR_PTR(-EINVAL); } #endif /* Check input arguments */ if (!(buf->flags & MMU_ION_BUF) && !buf->va) return ERR_PTR(-EINVAL); if (buf->flags & MMU_ION_BUF) /* buf->va is not a valid address. ION buffers are aligned */ b_map.offset = 0; /* Allocate the struct */ b_map.nr_pages = PAGE_ALIGN(b_map.offset + b_map.length) / PAGE_SIZE; /* Allow Registered Shared mem with valid pointer and zero size. */ if (!b_map.nr_pages) b_map.nr_pages = 1; mmu = tee_mmu_create_common(&b_map); if (IS_ERR(mmu)) return mmu; if (buf->flags & MMU_ION_BUF) { mc_dev_devel("Buffer is ION"); /* Buffer is ION - * va is the client's dma_buf fd, which should be converted * to a struct sg_table * directly. */ if (!mmu_get_dma_buffer(mmu, buf->va)) { mc_dev_err(ret, "mmu_get_dma_buffer failed"); ret = -EINVAL; goto end; } } /* Get a page to store page pointers */ pages_page = get_zeroed_page(GFP_KERNEL); if (!pages_page) { ret = -ENOMEM; goto end; } mmu->pages_created++; pages = (struct page **)pages_page; for (chunk = 0; chunk < mmu->nr_pmd_entries; chunk++) { unsigned long nr_pages; int i; /* Size to map for this chunk */ if (chunk == (mmu->nr_pmd_entries - 1)) nr_pages = ((mmu->nr_pages - 1) % PTE_ENTRIES_MAX) + 1; else nr_pages = PTE_ENTRIES_MAX; /* Allocate a page to hold ptes that describe buffer pages */ mmu->pte_tables[chunk].page = get_zeroed_page(GFP_KERNEL); if (!mmu->pte_tables[chunk].page) { ret = -ENOMEM; goto end; } mmu->pages_created++; /* Add page address to pmd table if needed */ if (mmu->use_pages_and_vas) mmu->pmd_table.vas[chunk] = mmu->pte_tables[chunk].page; else mmu->pmd_table.entries[chunk] = virt_to_phys(mmu->pte_tables[chunk].addr); /* Get pages */ if (mmu->dma_buf) { /* Buffer is ION */ struct sg_mapping_iter miter; struct page **page_ptr; unsigned int cnt = 0; unsigned int global_cnt = 0; page_ptr = pages; sg_miter_start(&miter, mmu->sgt->sgl, mmu->sgt->nents, SG_MITER_FROM_SG); while (sg_miter_next(&miter)) { if (((global_cnt) >= (PTE_ENTRIES_MAX * chunk)) && cnt < nr_pages) { page_ptr[cnt] = miter.page; cnt++; } global_cnt++; } sg_miter_stop(&miter); } else if (mm) { long gup_ret; /* Buffer was allocated in user space */ down_read(&mm->mmap_lock); /* * Always try to map read/write from a Linux PoV, so * Linux creates (page faults) the underlying pages if * missing. */ gup_ret = gup_local_repeat(mm, (uintptr_t)reader, nr_pages, 1, pages); if ((gup_ret == -EFAULT) && !writeable) { /* * If mapping read/write fails, and the buffer * is to be shared as input only, try to map * again read-only. */ gup_ret = gup_local_repeat(mm, (uintptr_t)reader, nr_pages, 0, pages); } up_read(&mm->mmap_lock); if (gup_ret < 0) { ret = gup_ret; mc_dev_err(ret, "failed to get user pages @%p", reader); goto end; } /* check if we could lock all pages. */ if (gup_ret != nr_pages) { mc_dev_err((int)gup_ret, "failed to get user pages"); release_pages(pages, gup_ret); ret = -EINVAL; goto end; } reader += nr_pages * PAGE_SIZE; mmu->user = true; mmu->pages_locked += nr_pages; } else if (is_vmalloc_addr(data)) { /* Buffer vmalloc'ed in kernel space */ for (i = 0; i < nr_pages; i++) { struct page *page = vmalloc_to_page(reader); if (!page) { ret = -EINVAL; mc_dev_err(ret, "failed to map address"); goto end; } pages[i] = page; reader += PAGE_SIZE; } } else { /* Buffer kmalloc'ed in kernel space */ struct page *page = virt_to_page(reader); reader += nr_pages * PAGE_SIZE; for (i = 0; i < nr_pages; i++) pages[i] = page++; } /* Create Table of physical addresses*/ if (mmu->use_pages_and_vas) { memcpy(mmu->pte_tables[chunk].pages, pages, nr_pages * sizeof(*pages)); } else { for (i = 0; i < nr_pages; i++) { mmu->pte_tables[chunk].entries[i] = page_to_phys(pages[i]); } } } end: if (pages_page) { free_page(pages_page); mmu->pages_created--; } if (ret) { tee_mmu_delete(mmu); return ERR_PTR(ret); } mc_dev_devel( "created mmu %p: %s va %llx len %u off %u flg %x pmd table %lx", mmu, mmu->user ? "user" : "kernel", buf->va, mmu->length, mmu->offset, mmu->flags, mmu->pmd_table.page); return mmu; } struct tee_mmu *tee_mmu_wrap(struct tee_deleter *deleter, struct page **pages, const struct mcp_buffer_map *b_map) { int ret = -EINVAL; #ifdef CONFIG_XEN struct tee_mmu *mmu; unsigned long chunk, nr_pages_left; /* Allocate the struct */ mmu = tee_mmu_create_common(b_map); if (IS_ERR(mmu)) return mmu; nr_pages_left = mmu->nr_pages; for (chunk = 0; chunk < mmu->nr_pmd_entries; chunk++) { unsigned long nr_pages = nr_pages_left; u64 *pte; int i; if (nr_pages > PTE_ENTRIES_MAX) nr_pages = PTE_ENTRIES_MAX; nr_pages_left -= nr_pages; /* Allocate a page to hold ptes that describe buffer pages */ mmu->pte_tables[chunk].page = get_zeroed_page(GFP_KERNEL); if (!mmu->pte_tables[chunk].page) { ret = -ENOMEM; goto err; } mmu->pages_created++; /* Add page address to pmd table if needed */ mmu->pmd_table.entries[chunk] = virt_to_phys(mmu->pte_tables[chunk].addr); /* Convert to PTEs */ pte = &mmu->pte_tables[chunk].entries[0]; for (i = 0; i < nr_pages; i++, pages++, pte++) { unsigned long phys; unsigned long pfn; phys = page_to_phys(*pages); #if defined CONFIG_ARM64 phys &= PHYS_48BIT_MASK; #endif pfn = PFN_DOWN(phys); *pte = __pfn_to_mfn(pfn) << PAGE_SHIFT; } } mmu->deleter = deleter; mc_dev_devel("wrapped mmu %p: len %u off %u flg %x pmd table %lx", mmu, mmu->length, mmu->offset, mmu->flags, mmu->pmd_table.page); return mmu; err: tee_mmu_delete(mmu); #endif return ERR_PTR(ret); } void tee_mmu_set_deleter(struct tee_mmu *mmu, struct tee_deleter *deleter) { mmu->deleter = deleter; } static void tee_mmu_release(struct kref *kref) { struct tee_mmu *mmu = container_of(kref, struct tee_mmu, kref); mc_dev_devel("free mmu %p: %s len %u off %u pmd table %lx", mmu, mmu->user ? "user" : "kernel", mmu->length, mmu->offset, mmu->pmd_table.page); tee_mmu_delete(mmu); } void tee_mmu_get(struct tee_mmu *mmu) { kref_get(&mmu->kref); } void tee_mmu_put(struct tee_mmu *mmu) { kref_put(&mmu->kref, tee_mmu_release); } void tee_mmu_buffer(struct tee_mmu *mmu, struct mcp_buffer_map *map) { if (mmu->use_pages_and_vas) map->addr = mmu->pmd_table.page; else map->addr = virt_to_phys(mmu->pmd_table.addr); map->secure_va = 0; map->offset = mmu->offset; map->length = mmu->length; map->nr_pages = mmu->nr_pages; map->flags = mmu->flags; map->type = WSM_L1; #ifdef CONFIG_DMA_SHARED_BUFFER if (mmu->dma_buf) { /* ION */ if (!(((struct ion_buffer *)mmu->dma_buf->priv)->flags & ION_FLAG_CACHED)) { map->type |= WSM_UNCACHED; mc_dev_devel("ION buffer Non cacheable"); } else { mc_dev_devel("ION buffer cacheable"); } } #endif map->mmu = mmu; } int tee_mmu_debug_structs(struct kasnprintf_buf *buf, const struct tee_mmu *mmu) { return kasnprintf(buf, "\t\t\tmmu %pK: %s len %u off %u table %pK\n", mmu, mmu->user ? "user" : "kernel", mmu->length, mmu->offset, (void *)mmu->pmd_table.page); }