Tivy, Robert wrote:
Engineers at TI are looking to get the TI CMEM module into the Linux kernel mainline in the near future. In order to progress to this goal we need to address some items. One of the items that needs addressing is the CMEM driver get_phys() function. Currently it just decodes page mappings at a low level, using pgd/pmd/pte low-level functions/macros. Kevin Hilman provided an improved version, one that is much more portable and correct. However, there's a problem with this new version in that it doesn't translate some kernel virtual addresses correctly, so I'm asking the list for help. CMEM is responsible for allocating contiguous memory blocks. It is granted a block of physical memory during module insertion time. Typically this block is one not managed by the kernel, i.e., the kernel has been "told" to not touch this memory, by virtue of the "mem=" boot command line arg. In order to manage this memory block, CMEM performs an ioremap() (either ioremap_nocache() or ioremap_cached()), getting a kernel virtual pointer as a result. At some later time (as a result of a user request) CMEM calls get_phys() on a kernel virtual address within this block (get_phys() also is called for user mappings to the same block). The "new" get_phys() doesn't handle this kernel translation correctly - the returned phys addr is always some constant offset from the correct one. The ioremap functions obtain a virtual address block large enough for the physical block through the kernel get_vm_area() function. This function traverses the list 'vmlist' for an area that is large enough to satisfy the request. The chosen virtual address is then mapped to the requested physical address. The problem with get_phys() is that it uses the 'virt_to_phys()' API which assumes a fixed, constant relationship between a kernel virt addr and the corresponding phys addr, but in this case the virt addr is some arbitrary address found to be free somewhere between VMALLOC_START->VMALLOC_END. >From what I can see, the only way to do this translation is to traverse the 'vmlist' and find the vm_struct for the virt addr, and return the phys_addr element of the vm_struct. However, vmlist is not exported, and I would expect that there is a helper function somewhere for traversing vmlist, but I can't find anything. The following code will return the wrong physp: block_virtp = (unsigned long) ioremap_nocache(start, length);
    ...
    physp = get_phys(block_virtp);
And here is the code in get_phys() that is returning the wrong value:

    /* For kernel direct-mapped memory, take the easy way */
    if (virtp >= PAGE_OFFSET) {
        physp = virt_to_phys((void *)virtp);
    }
So, for example, if the physical block starts at 0x87800000 and we do
    block_virtp = (unsigned long) ioremap_nocache(0x87800000, 0x800000);
we get block_virtp = 0xc80800000, and the call
    physp = get_phys(0xc8080000);
returns a physp of 0x88080000, but it should be 0x87800000 (and all phys addrs it returns are the same amount too high - 0x00880000). Moving to a different chip w/ a different Linux, we get
    block_virtp = 0xce000000
and
    get_phys(0xce000000)
returns 0x8e000000. Clearly both these systems have a virt_to_phys() macro/function that simply subtracts 0x40000000, but for virt addrs that were allocated by ioremap() there is no such fixed relationship and the vmlist must be consulted, but the CMEM module can't get to the unexported symbol 'vmlist'. Please advise on how to successfully translate these types of virtual addresses. Thanks in advance for any "pointers" you can provide :) Regards, - Rob Tivy
Texas Instruments, Santa Barbara
cmem_1_02 implements get_phys as

/* Traverses the page tables and translates a virtual adress to a physical. */
static unsigned long get_phys(unsigned long virtp)
{
    pgd_t *pgd;
    pmd_t *pmd;
    pte_t *pte;
    struct mm_struct *mm = current->mm;

    pgd = pgd_offset(mm, virtp);
    if (!(pgd_none(*pgd) || pgd_bad(*pgd))) {
        pmd = pmd_offset(pgd, virtp);

        if (!(pmd_none(*pmd) || pmd_bad(*pmd))) {
            pte = pte_offset_kernel(pmd, virtp);

            if (pte_present(*pte)) {
                return __pa(page_address(pte_page(*pte)) +
                            (virtp & ~PAGE_MASK));
            }
        }
    }

    return 0;
}
Does this use vmlist under the hood?

I fixed cmem_1_01 by remembering the physical address remmaped.
i.e.

static unsigned long get_phys1(unsigned long virtp)
{
    unsigned long physp = 0;
    struct mm_struct *mm = current->mm;
    struct vm_area_struct *vma;

    if (virtp >= bs.block_virtp) {
        if (virtp < (bs.block_virtp + bs.block_totalSize)) {
                return (virtp - bs.block_virtp) + bs.block_physp;
        }
    }
        ......

}

Troy

_______________________________________________
Davinci-linux-open-source mailing list
[email protected]
http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source

Reply via email to