On Mon, 14 Aug 2017, Chris Brandt wrote:

> On Monday, August 14, 2017, Nicolas Pitre wrote:
> > > However, now with your mkcramfs tool, I can no longer mount my cramfs
> > > image as the rootfs on boot. I was able to do that before (ie, 30
> > minutes
> > > ago) when using the community mkcramfs (ie, 30 minutes ago).
> > >
> > > I get this:
> > >
> > > [    1.712425] cramfs: checking physical address 0x1b000000 for linear
> > cramfs image
> > > [    1.720531] cramfs: linear cramfs image appears to be 15744 KB in
> > size
> > > [    1.728656] VFS: Mounted root (cramfs_physmem filesystem) readonly on
> > device 0:12.
> > > [    1.737062] devtmpfs: mounted
> > > [    1.741139] Freeing unused kernel memory: 48K
> > > [    1.745545] This architecture does not have kernel memory protection.
> > > [    1.760381] Starting init: /sbin/init exists but couldn't execute it
> > (error -22)
> > > [    1.769685] Starting init: /bin/sh exists but couldn't execute it
> > (error -14)
> > 
> > Is /sbin/init a link to busybox?
> 
> Yes.
> 
> 
> > I suppose it just boots if you do mkcramfs without -X?
> 
> Correct. I just created another image and removed the "-X -X" when 
> creating it. Now I can boot that image as my rootfs.
>   (I'm using -X -X because I'm using a Cortex-A9 with MMU).
> 
> 
> > If so could you share your non-working cramfs image with me?
> 
> I will send it (in a separate email)

I was able to reproduce. The following patch on top should partially fix 
it.  I'm trying to figure out how to split a vma and link it properly in 
the case the vma cannot be mapped entirely. In the mean time shared libs 
won't be XIP.


diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c
index 5aedbd224e..4c7f01fcd2 100644
--- a/fs/cramfs/inode.c
+++ b/fs/cramfs/inode.c
@@ -285,10 +285,10 @@ static void *cramfs_read(struct super_block *sb, unsigned 
int offset,
 
 /*
  * For a mapping to be possible, we need a range of uncompressed and
- * contiguous blocks. Return the offset for the first block if that
- * verifies, or zero otherwise.
+ * contiguous blocks. Return the offset for the first block and number of
+ * valid blocks for which that is true, or zero otherwise.
  */
-static u32 cramfs_get_block_range(struct inode *inode, u32 pgoff, u32 pages)
+static u32 cramfs_get_block_range(struct inode *inode, u32 pgoff, u32 *pages)
 {
        struct super_block *sb = inode->i_sb;
        struct cramfs_sb_info *sbi = CRAMFS_SB(sb);
@@ -306,11 +306,16 @@ static u32 cramfs_get_block_range(struct inode *inode, 
u32 pgoff, u32 pages)
        do {
                u32 expect = blockaddr + i * (PAGE_SIZE >> 2);
                expect |= 
CRAMFS_BLK_FLAG_DIRECT_PTR|CRAMFS_BLK_FLAG_UNCOMPRESSED;
-               pr_debug("range: block %d/%d got %#x expects %#x\n",
-                        pgoff+i, pgoff+pages-1, blockptrs[i], expect);
-               if (blockptrs[i] != expect)
-                       return 0;
-       } while (++i < pages);
+               if (blockptrs[i] != expect) {
+                       pr_debug("range: block %d/%d got %#x expects %#x\n",
+                                pgoff+i, pgoff+*pages-1, blockptrs[i], expect);
+                       if (i == 0)
+                               return 0;
+                       break;
+               }
+       } while (++i < *pages);
+
+       *pages = i;
 
        /* stored "direct" block ptrs are shifted down by 2 bits */
        return blockaddr << 2;
@@ -321,8 +326,8 @@ static int cramfs_physmem_mmap(struct file *file, struct 
vm_area_struct *vma)
        struct inode *inode = file_inode(file);
        struct super_block *sb = inode->i_sb;
        struct cramfs_sb_info *sbi = CRAMFS_SB(sb);
-       unsigned int pages, max_pages, offset;
-       unsigned long length, address;
+       unsigned int pages, vma_pages, max_pages, offset;
+       unsigned long address;
        char *fail_reason;
        int ret;
 
@@ -332,17 +337,20 @@ static int cramfs_physmem_mmap(struct file *file, struct 
vm_area_struct *vma)
        if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE))
                return -EINVAL;
 
-       vma->vm_ops = &generic_file_vm_ops;
+       fail_reason = "vma is writable";
        if (vma->vm_flags & VM_WRITE)
-               return 0;
+               goto fail;
 
-       length = vma->vm_end - vma->vm_start;
-       pages = (length + PAGE_SIZE - 1) >> PAGE_SHIFT;
+       vma_pages = (vma->vm_end - vma->vm_start + PAGE_SIZE - 1) >> PAGE_SHIFT;
        max_pages = (inode->i_size + PAGE_SIZE - 1) >> PAGE_SHIFT;
-       if (vma->vm_pgoff >= max_pages || pages > max_pages - vma->vm_pgoff)
-               return -EINVAL;
+       fail_reason = "beyond file limit";
+       if (vma->vm_pgoff >= max_pages)
+               goto fail;
+       pages = vma_pages;
+       if (pages > max_pages - vma->vm_pgoff)
+               pages = max_pages - vma->vm_pgoff;
 
-       offset = cramfs_get_block_range(inode, vma->vm_pgoff, pages);
+       offset = cramfs_get_block_range(inode, vma->vm_pgoff, &pages);
        fail_reason = "unsuitable block layout";
        if (!offset)
                goto fail;
@@ -351,37 +359,60 @@ static int cramfs_physmem_mmap(struct file *file, struct 
vm_area_struct *vma)
        if (!PAGE_ALIGNED(address))
                goto fail;
 
-       /* Don't map a partial page if it contains some other data */
+       /* Don't map the last page if it contains some other data */
        if (unlikely(vma->vm_pgoff + pages == max_pages)) {
                unsigned int partial = offset_in_page(inode->i_size);
                if (partial) {
                        char *data = sbi->linear_virt_addr + offset;
                        data += (pages - 1) * PAGE_SIZE + partial;
-                       fail_reason = "last partial page is shared";
                        while ((unsigned long)data & 7)
                                if (*data++ != 0)
-                                       goto fail;
+                                       goto nonzero;
                        while (offset_in_page(data)) {
-                               if (*(u64 *)data != 0)
-                                       goto fail;
+                               if (*(u64 *)data != 0) {
+                                       nonzero:
+                                       pr_debug("mmap: %s: last page is 
shared\n",
+                                                
file_dentry(file)->d_name.name);
+                                       pages--;
+                                       break;
+                               }
                                data += 8;
                        }
                }
        }
-       
-       ret = remap_pfn_range(vma, vma->vm_start, address >> PAGE_SHIFT,
-                             length, vma->vm_page_prot);
-       if (ret)
-               return ret;
-       pr_debug("mapped %s at 0x%08lx, length %lu to vma 0x%08lx, "
+
+       if (pages) {
+               /*
+                * Split the vma if we can't map it all so normal paging
+                * will take care of the rest through cramfs_readpage().
+                */
+               if (pages != vma_pages) {
+                       if (1) {
+                               fail_reason = "fix me";
+                               goto fail;
+                       }
+                       ret = split_vma(vma->vm_mm, vma,
+                                       vma->vm_start + pages * PAGE_SIZE, 0);
+                       if (ret)
+                               return ret;
+               }
+
+               ret = remap_pfn_range(vma, vma->vm_start, address >> PAGE_SHIFT,
+                                     pages * PAGE_SIZE, vma->vm_page_prot);
+               if (ret)
+                       return ret;
+       }
+
+       pr_debug("mapped %s at 0x%08lx, %u/%u pages to vma 0x%08lx, "
                 "page_prot 0x%llx\n", file_dentry(file)->d_name.name,
-                address, length, vma->vm_start,
+                address, pages, vma_pages, vma->vm_start,
                 (unsigned long long)pgprot_val(vma->vm_page_prot));
        return 0;
 
 fail:
        pr_debug("%s: direct mmap failed: %s\n",
                 file_dentry(file)->d_name.name, fail_reason);
+       vma->vm_ops = &generic_file_vm_ops;
        return 0;
 }
 
@@ -394,14 +425,15 @@ static unsigned long 
cramfs_physmem_get_unmapped_area(struct file *file,
        struct inode *inode = file_inode(file);
        struct super_block *sb = inode->i_sb;
        struct cramfs_sb_info *sbi = CRAMFS_SB(sb);
-       unsigned int pages, max_pages, offset;
+       unsigned int pages, block_pages, max_pages, offset;
 
        pages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT;
        max_pages = (inode->i_size + PAGE_SIZE - 1) >> PAGE_SHIFT;
        if (pgoff >= max_pages || pages > max_pages - pgoff)
                return -EINVAL;
-       offset = cramfs_get_block_range(inode, pgoff, pages);
-       if (!offset)
+       block_pages = pages;
+       offset = cramfs_get_block_range(inode, pgoff, &block_pages);
+       if (!offset || block_pages != pages)
                return -ENOSYS;
        addr = sbi->linear_phys_addr + offset;
        pr_debug("get_unmapped for %s ofs %#lx siz %lu at 0x%08lx\n",

Reply via email to