162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* NOMMU mmap support for RomFS on MTD devices 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright © 2007 Red Hat, Inc. All Rights Reserved. 562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/mm.h> 962306a36Sopenharmony_ci#include <linux/mtd/super.h> 1062306a36Sopenharmony_ci#include "internal.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci/* 1362306a36Sopenharmony_ci * try to determine where a shared mapping can be made 1462306a36Sopenharmony_ci * - only supported for NOMMU at the moment (MMU can't doesn't copy private 1562306a36Sopenharmony_ci * mappings) 1662306a36Sopenharmony_ci * - attempts to map through to the underlying MTD device 1762306a36Sopenharmony_ci */ 1862306a36Sopenharmony_cistatic unsigned long romfs_get_unmapped_area(struct file *file, 1962306a36Sopenharmony_ci unsigned long addr, 2062306a36Sopenharmony_ci unsigned long len, 2162306a36Sopenharmony_ci unsigned long pgoff, 2262306a36Sopenharmony_ci unsigned long flags) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci struct inode *inode = file->f_mapping->host; 2562306a36Sopenharmony_ci struct mtd_info *mtd = inode->i_sb->s_mtd; 2662306a36Sopenharmony_ci unsigned long isize, offset, maxpages, lpages; 2762306a36Sopenharmony_ci int ret; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci if (!mtd) 3062306a36Sopenharmony_ci return (unsigned long) -ENOSYS; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci /* the mapping mustn't extend beyond the EOF */ 3362306a36Sopenharmony_ci lpages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; 3462306a36Sopenharmony_ci isize = i_size_read(inode); 3562306a36Sopenharmony_ci offset = pgoff << PAGE_SHIFT; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci maxpages = (isize + PAGE_SIZE - 1) >> PAGE_SHIFT; 3862306a36Sopenharmony_ci if ((pgoff >= maxpages) || (maxpages - pgoff < lpages)) 3962306a36Sopenharmony_ci return (unsigned long) -EINVAL; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci if (addr != 0) 4262306a36Sopenharmony_ci return (unsigned long) -EINVAL; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci if (len > mtd->size || pgoff >= (mtd->size >> PAGE_SHIFT)) 4562306a36Sopenharmony_ci return (unsigned long) -EINVAL; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci offset += ROMFS_I(inode)->i_dataoffset; 4862306a36Sopenharmony_ci if (offset >= mtd->size) 4962306a36Sopenharmony_ci return (unsigned long) -EINVAL; 5062306a36Sopenharmony_ci /* the mapping mustn't extend beyond the EOF */ 5162306a36Sopenharmony_ci if ((offset + len) > mtd->size) 5262306a36Sopenharmony_ci len = mtd->size - offset; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci ret = mtd_get_unmapped_area(mtd, len, offset, flags); 5562306a36Sopenharmony_ci if (ret == -EOPNOTSUPP) 5662306a36Sopenharmony_ci ret = -ENOSYS; 5762306a36Sopenharmony_ci return (unsigned long) ret; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/* 6162306a36Sopenharmony_ci * permit a R/O mapping to be made directly through onto an MTD device if 6262306a36Sopenharmony_ci * possible 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_cistatic int romfs_mmap(struct file *file, struct vm_area_struct *vma) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci return is_nommu_shared_mapping(vma->vm_flags) ? 0 : -ENOSYS; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic unsigned romfs_mmap_capabilities(struct file *file) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci struct mtd_info *mtd = file_inode(file)->i_sb->s_mtd; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (!mtd) 7462306a36Sopenharmony_ci return NOMMU_MAP_COPY; 7562306a36Sopenharmony_ci return mtd_mmap_capabilities(mtd); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ciconst struct file_operations romfs_ro_fops = { 7962306a36Sopenharmony_ci .llseek = generic_file_llseek, 8062306a36Sopenharmony_ci .read_iter = generic_file_read_iter, 8162306a36Sopenharmony_ci .splice_read = filemap_splice_read, 8262306a36Sopenharmony_ci .mmap = romfs_mmap, 8362306a36Sopenharmony_ci .get_unmapped_area = romfs_get_unmapped_area, 8462306a36Sopenharmony_ci .mmap_capabilities = romfs_mmap_capabilities, 8562306a36Sopenharmony_ci}; 86