/* * Copyright (c) 2006-2023, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes */ #include "dfs_file.h" #include "dfs_dentry.h" #include "dfs_mnt.h" #define DBG_TAG "dfs.mmap" #define DBG_LVL DBG_WARNING #include #if defined(RT_USING_SMART) && defined(ARCH_MM_MMU) && defined(RT_USING_PAGECACHE) #include "dfs_pcache.h" #include #include #include #include #include #include #include #include #include #include static rt_mem_obj_t dfs_get_mem_obj(struct dfs_file *file); static void *dfs_mem_obj_get_file(rt_mem_obj_t mem_obj); static void *_do_mmap(struct rt_lwp *lwp, void *map_vaddr, size_t map_size, size_t attr, mm_flag_t flags, off_t pgoffset, void *data, rt_err_t *code) { int ret = 0; void *vaddr = map_vaddr; rt_mem_obj_t mem_obj = dfs_get_mem_obj(data); ret = rt_aspace_map(lwp->aspace, &vaddr, map_size, attr, flags, mem_obj, pgoffset); if (ret != RT_EOK) { vaddr = RT_NULL; LOG_E("failed to map %lx with size %lx with errno %d", map_vaddr, map_size, ret); } if (code) { *code = ret; } return vaddr; } static void *_map_data_to_uspace(struct dfs_mmap2_args *mmap2, void *data, rt_err_t *code) { size_t offset = 0; void *map_vaddr = mmap2->addr; size_t map_size = mmap2->length; struct rt_lwp *lwp = mmap2->lwp; rt_size_t k_attr; rt_size_t k_flags; if (map_size) { offset = (size_t)map_vaddr & ARCH_PAGE_MASK; map_size += (offset + ARCH_PAGE_SIZE - 1); map_size &= ~ARCH_PAGE_MASK; map_vaddr = (void *)((size_t)map_vaddr & ~ARCH_PAGE_MASK); k_flags = lwp_user_mm_flag_to_kernel(mmap2->flags); k_attr = lwp_user_mm_attr_to_kernel(mmap2->prot); map_vaddr = _do_mmap(lwp, map_vaddr, map_size, k_attr, k_flags, mmap2->pgoffset, data, code); } return map_vaddr; } static void hint_free(rt_mm_va_hint_t hint) { } static void on_page_fault(struct rt_varea *varea, struct rt_aspace_fault_msg *msg) { void *page; struct dfs_file *file = dfs_mem_obj_get_file(varea->mem_obj); if (file) { LOG_I("%s varea: %p", __func__, varea); LOG_I("varea start: %p size: 0x%x offset: 0x%x attr: 0x%x flag: 0x%x", varea->start, varea->size, varea->offset, varea->attr, varea->flag); LOG_I("fault vaddr: %p", msg->fault_vaddr); if (file->dentry) { LOG_I("file: %s%s", file->dentry->mnt->fullpath, file->dentry->pathname); } page = dfs_aspace_mmap(file, varea, msg->fault_vaddr); if (page) { msg->response.status = MM_FAULT_STATUS_OK_MAPPED; msg->response.size = ARCH_PAGE_SIZE; msg->response.vaddr = page; } else { LOG_E("%s varea %p mmap failed at vaddr %p", __func__, varea, msg->fault_vaddr); } } else { LOG_E("%s varea %p not a file, vaddr %p", __func__, varea, varea->start); } } /* do pre open bushiness like inc a ref */ static void on_varea_open(struct rt_varea *varea) { struct dfs_file *file = dfs_mem_obj_get_file(varea->mem_obj); varea->data = RT_NULL; rt_atomic_add(&(file->ref_count), 1); } /* do post close bushiness like def a ref */ static void on_varea_close(struct rt_varea *varea) { struct dfs_file *file = dfs_mem_obj_get_file(varea->mem_obj); if (file) { LOG_I("%s varea: %p", __func__, varea); LOG_I("varea start: %p size: 0x%x offset: 0x%x attr: 0x%x flag: 0x%x", varea->start, varea->size, varea->offset, varea->attr, varea->flag); if (file->dentry) { LOG_I("file: %s%s", file->dentry->mnt->fullpath, file->dentry->pathname); } dfs_aspace_unmap(file, varea); dfs_file_lock(); if (rt_atomic_load(&(file->ref_count)) == 1) { dfs_file_close(file); } else { rt_atomic_sub(&(file->ref_count), 1); } dfs_file_unlock(); } else { LOG_E("%s varea %p not a file, vaddr %p", __func__, varea, varea->start); } } static const char *get_name(rt_varea_t varea) { struct dfs_file *file = dfs_mem_obj_get_file(varea->mem_obj); return (file && file->dentry) ? file->dentry->pathname : "file-mapper"; } void page_read(struct rt_varea *varea, struct rt_aspace_io_msg *msg) { rt_ubase_t ret; struct dfs_file *file = dfs_mem_obj_get_file(varea->mem_obj); if (file) { LOG_I("%s varea: %p", __func__, varea); LOG_I("varea start: %p size: 0x%x offset: 0x%x attr: 0x%x flag: 0x%x", varea->start, varea->size, varea->offset, varea->attr, varea->flag); ret = dfs_aspace_mmap_read(file, varea, msg); if (ret > 0) { msg->response.status = MM_FAULT_STATUS_OK; if (ret < ARCH_PAGE_SIZE) { memset((char *)msg->buffer_vaddr + ret, 0, ARCH_PAGE_SIZE - ret); } } } else { LOG_E("%s varea %p not a file, vaddr %p", __func__, varea, varea->start); } } void page_write(struct rt_varea *varea, struct rt_aspace_io_msg *msg) { rt_ubase_t ret; struct dfs_file *file = dfs_mem_obj_get_file(varea->mem_obj); if (file) { LOG_I("%s varea: %p", __func__, varea); LOG_I("varea start: %p size: 0x%x offset: 0x%x attr: 0x%x flag: 0x%x", varea->start, varea->size, varea->offset, varea->attr, varea->flag); ret = dfs_aspace_mmap_write(file, varea, msg); if (ret > 0) { msg->response.status = MM_FAULT_STATUS_OK; if (ret < ARCH_PAGE_SIZE) { memset((char *)msg->buffer_vaddr + ret, 0, ARCH_PAGE_SIZE - ret); } } } else { LOG_E("%s varea %p not a file, vaddr %p", __func__, varea, varea->start); } } static rt_err_t unmap_pages(rt_varea_t varea, void *rm_start, void *rm_end) { struct dfs_file *file = dfs_mem_obj_get_file(varea->mem_obj); if (file) { LOG_I("%s varea: %p start: %p end: %p", __func__, varea, rm_start, rm_end); RT_ASSERT(!((rt_ubase_t)rm_start & ARCH_PAGE_MASK)); RT_ASSERT(!((rt_ubase_t)rm_end & ARCH_PAGE_MASK)); while (rm_start != rm_end) { dfs_aspace_page_unmap(file, varea, rm_start); rm_start += ARCH_PAGE_SIZE; } return RT_EOK; } else { LOG_E("%s varea %p not a file, vaddr %p", __func__, varea, varea->start); } return -RT_ERROR; } rt_err_t on_varea_shrink(struct rt_varea *varea, void *new_vaddr, rt_size_t size) { char *varea_start = varea->start; void *rm_start; void *rm_end; LOG_I("%s varea: %p", __func__, varea); LOG_I("varea start: %p size: 0x%x offset: 0x%x attr: 0x%x flag: 0x%x", varea->start, varea->size, varea->offset, varea->attr, varea->flag); LOG_I("new_vaddr: %p size: %p", new_vaddr, size); if (varea_start == (char *)new_vaddr) { rm_start = varea_start + size; rm_end = varea_start + varea->size; } else { rm_start = varea_start; rm_end = new_vaddr; } return unmap_pages(varea, rm_start, rm_end); } rt_err_t on_varea_expand(struct rt_varea *varea, void *new_vaddr, rt_size_t size) { LOG_I("%s varea: %p", __func__, varea); LOG_I("varea start: %p size: 0x%x offset: 0x%x attr: 0x%x flag: 0x%x", varea->start, varea->size, varea->offset, varea->attr, varea->flag); LOG_I("new_vaddr: %p size: %p", new_vaddr, size); return RT_EOK; } rt_err_t on_varea_split(struct rt_varea *existed, void *unmap_start, rt_size_t unmap_len, struct rt_varea *subset) { rt_err_t rc; struct dfs_file *file = dfs_mem_obj_get_file(existed->mem_obj); if (file) { LOG_I("%s varea: %p", __func__, existed); LOG_I("varea start: %p size: 0x%x offset: 0x%x attr: 0x%x flag: 0x%x", existed->start, existed->size, existed->offset, existed->attr, existed->flag); LOG_I("unmap_start: %p unmap_len: %p", unmap_start, unmap_len); if (file->dentry) { LOG_I("file: %s%s", file->dentry->mnt->fullpath, file->dentry->pathname); } rc = unmap_pages(existed, unmap_start, (char *)unmap_start + unmap_len); if (!rc) { rc = unmap_pages(existed, subset->start, (char *)subset->start + subset->size); if (!rc) on_varea_open(subset); } return rc; } else { LOG_E("%s varea %p not a file, vaddr %p", __func__, existed, existed->start); } return -RT_ERROR; } rt_err_t on_varea_merge(struct rt_varea *merge_to, struct rt_varea *merge_from) { struct dfs_file *file = dfs_mem_obj_get_file(merge_from->mem_obj); if (file) { LOG_I("%s varea: %p", __func__, merge_from); LOG_I("varea start: %p size: 0x%x offset: 0x%x attr: 0x%x flag: 0x%x", merge_from->start, merge_from->size, merge_from->offset, merge_from->attr, merge_from->flag); if (file->dentry) { LOG_I("file: %s%s", file->dentry->mnt->fullpath, file->dentry->pathname); } dfs_aspace_unmap(file, merge_from); on_varea_close(merge_from); return RT_EOK; } else { LOG_E("%s varea %p not a file, vaddr %p", __func__, merge_from, merge_from->start); } return -RT_ERROR; } void *on_varea_mremap(struct rt_varea *varea, rt_size_t new_size, int flags, void *new_address) { void *vaddr = RT_NULL; struct dfs_file *file = dfs_mem_obj_get_file(varea->mem_obj); #ifndef MREMAP_MAYMOVE #define MREMAP_MAYMOVE 1 #endif if (file && flags == MREMAP_MAYMOVE) { int ret; rt_mem_obj_t mem_obj = dfs_get_mem_obj(file); vaddr = new_address ? new_address : varea->start; new_size = (new_size + ARCH_PAGE_SIZE - 1); new_size &= ~ARCH_PAGE_MASK; ret = rt_aspace_map(varea->aspace, &vaddr, new_size, varea->attr, varea->flag, mem_obj, varea->offset); if (ret != RT_EOK) { LOG_E("failed to map %lx with size %lx with errno %d", vaddr, new_size, ret); vaddr = RT_NULL; } else { LOG_I("old: %p size: %p new: %p size: %p", varea->start, varea->size, vaddr, new_size); } } return vaddr; } static struct rt_mem_obj _mem_obj = { .hint_free = hint_free, .on_page_fault = on_page_fault, .on_varea_open = on_varea_open, .on_varea_close = on_varea_close, .get_name = get_name, .page_read = page_read, .page_write = page_write, .on_varea_shrink = on_varea_shrink, .on_varea_expand = on_varea_expand, .on_varea_split = on_varea_split, .on_varea_merge = on_varea_merge, .on_varea_mremap = on_varea_mremap, }; struct dfs_mem_obj { struct rt_mem_obj mem_obj; void *file; }; static rt_mem_obj_t dfs_get_mem_obj(struct dfs_file *file) { rt_mem_obj_t mobj = file->mmap_context; if (!mobj) { struct dfs_mem_obj *dfs_mobj; dfs_file_lock(); dfs_mobj = rt_malloc(sizeof(*dfs_mobj)); if (dfs_mobj) { dfs_mobj->file = file; mobj = &dfs_mobj->mem_obj; memcpy(mobj, &_mem_obj, sizeof(*mobj)); file->mmap_context = mobj; } dfs_file_unlock(); } return mobj; } static void *dfs_mem_obj_get_file(rt_mem_obj_t mem_obj) { struct dfs_mem_obj *dfs_mobj; dfs_mobj = rt_container_of(mem_obj, struct dfs_mem_obj, mem_obj); return dfs_mobj->file; } int dfs_file_mmap(struct dfs_file *file, struct dfs_mmap2_args *mmap2) { rt_err_t ret = -EINVAL; void *map_vaddr; LOG_I("mmap2 args addr: %p length: 0x%x prot: %d flags: 0x%x pgoffset: 0x%x", mmap2->addr, mmap2->length, mmap2->prot, mmap2->flags, mmap2->pgoffset); if (file && file->vnode) { if (file->vnode->aspace) { /* create a va area in user space (lwp) */ map_vaddr = _map_data_to_uspace(mmap2, file, &ret); if (map_vaddr) { mmap2->ret = map_vaddr; LOG_I("file: %s%s", file->dentry->mnt->fullpath, file->dentry->pathname); } } else { LOG_E("File mapping is not supported, file: %s%s", file->dentry->mnt->fullpath, file->dentry->pathname); } } return ret; } #else int dfs_file_mmap(struct dfs_file *file, struct dfs_mmap2_args *mmap2) { LOG_E("File mapping support is not enabled, file: %s%s", file->dentry->mnt->fullpath, file->dentry->pathname); LOG_E("mmap2 args addr: %p length: 0x%x prot: %d flags: 0x%x pgoffset: 0x%x", mmap2->addr, mmap2->length, mmap2->prot, mmap2->flags, mmap2->pgoffset); return -EPERM; } #endif