linux-cvs
[Top] [All Lists]

CVS Update@-mips.org: linux

To: linux-cvs@linux-mips.org
Subject: CVS Update@-mips.org: linux
From: ralf@linux-mips.org
Date: Wed, 25 Feb 2004 22:09:29 +0000
Reply-to: linux-mips@linux-mips.org
Sender: linux-cvs-bounce@linux-mips.org
CVSROOT:        /home/cvs
Module name:    linux
Changes by:     ralf@ftp.linux-mips.org 04/02/25 22:09:29

Modified files:
        arch/mips/mm   : ioremap.c 

Log message:
        Fix for off by one bug from OSDL's buzilla:
        
        http://bugme.osdl.org/show_bug.cgi?id=2188
        
        Summary: __ioremap does not map entirely the size specified (if
        size = 1 + multiple of PAGE_SIZE)
        Kernel Version: 2.6.x , 2.4.x
        Status: NEW
        Severity: normal
        Owner: io_other@kernel-bugs.osdl.org
        Submitter: phil@equator.com
        
        Distribution: redHat , snapgear
        Hardware Environment:
        Software Environment: linux2.6.x linux2.4.x linux2.2.x
        Problem Description:__ioremap does remap properly if the size specified 
is a
        (mulptiple of PAGE_SIZE) + 1 and if the physical address passed happens 
to be
        already page aligned. In this particular case, the last byte is not 
covered and
        an access to it will create a Segmentation Fault.
        
        Steps to reproduce: ioremap  a piece of PCI memory of size PAGE_SIZE + 
1 and
        write to the last byte : Segmentation Fault occurs.
        
        I checked in several architectures (ARM, x86 ) and different kernel 
versions
        2.4.x, 2.6.x, the __ioremap implementations are similar and contain the 
bug
        
        The bug is quite simple to fix :
        For example, in snpagear distribution for ARM architecture
        
        void * __ioremap(unsigned long phys_addr, size_t size, unsigned long 
flags)
        {
        void * addr;
        struct vm_struct * area;
        unsigned long offset, last_addr;
        
        /* Don't allow wraparound or zero size */
        last_addr = phys_addr + size - 1;   <----- last address is inside
        
        if (!size || last_addr < phys_addr)
        return NULL;
        
        /*
        * Mappings have to be page-aligned
        */
        offset = phys_addr & ~PAGE_MASK;
        phys_addr &= PAGE_MASK;
        //size = PAGE_ALIGN(last_addr ) - phys_addr;
        size = PAGE_ALIGN(last_addr + 1) - phys_addr;  <--- THE FIX
        
        /*
        * Ok, go for it..
        */
        area = get_vm_area(size, VM_IOREMAP);
        if (!area)
        return NULL;
        addr = area->addr;
        if (remap_area_pages(VMALLOC_VMADDR(addr), phys_addr >> PAGE_SHIFT, 
size, flags)) {
        vfree(addr);
        return NULL;
        }
        return (void *) (offset + (char *)addr);
        }


<Prev in Thread] Current Thread [Next in Thread>