2004-10-16 03:46:52

by glenn

[permalink] [raw]
Subject: remap_page_range64() for PPC

I'm writing an application to run on a PowerPC with a 2.4 embedded
Linux kernel, and I want to make device registers for our custom
hardware accessable from user space with mmap(). The physical address
of the device is above the 4gb boundary (we attach to the 440's
external peripheral bus), so a standard 'remap_page_range()' call
won't work.

I've come up with a solution that appears to work, but never having
worked with the page tables before, I'm a bit nervous. So if anyone
would give the following code a short "code review", I'd greatly
appreciate it. I'm assuming that the page table entries created here
will be removed when a process exits as part of the normal cleanup.

The physical address passed to 'mk_pte_phys' has a value like
0x150000000ULL. Our device has only 0x100 bytes of addresses, so a
single page table entry is enough (pages are 0x1000 long), and we'll
only make one mmap() call per device.

TNA!!



static struct EBC_DATA {
void *baseaddr;
phys_addr_t phys_addr;
} ebc_data[2]; /* only 2 banks used */

static inline pgprot_t pgprot_noncached(pgprot_t _prot)
{
unsigned long prot = pgprot_val(_prot);

#if defined(__powerpc__)
prot |= _PAGE_NO_CACHE | _PAGE_GUARDED;
#endif
return __pgprot(prot);
}

#endif /* !pgprot_noncached */

static int mmap_ebc(struct file * file, struct vm_area_struct * vma)
{
unsigned long address;

if (vma->vm_pgoff) {
printk(KERN_INFO "EBC invalid vma offset: %lx\n", vma->vm_pgoff);
return -EINVAL;
}

/*
* Use non-cached access.
*/
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);

/* Don't try to swap out physical pages..
* Don't dump addresses that are not real memory to a core file.
*/
vma->vm_flags |= VM_RESERVED|VM_IO;

/* remap_page_range64()
Create a page table entry for the Peripheral Bus physical
addresses, in user space. Need only a single page, since
Phoenix boards have only 0x100 bytes of addresses.
*/
struct mm_struct *mm = current->mm;
pgd_t * dir;

dir = pgd_offset(mm, vma->vm_start);
if (pgd_none(*dir) || pgd_bad(*dir)) return -EINVAL;

spin_lock(&mm->page_table_lock);

pmd_t *pmd = pmd_alloc(mm, dir, vma->vm_start);
if (!pmd) return -ENOMEM;

address = vma->vm_start & ~PGDIR_MASK;

pte_t * pte = pte_alloc(mm, pmd, address);
if (!pte) return -ENOMEM;

/* Should probably add in "vma->vm_pgoff << PAGE_SHIFT" to
physical address here, but count on only one mmap call
per process per EBC device.
*/
set_pte(pte,
mk_pte_phys(((struct EBC_DATA *)file->private_data)->phys_addr,
vma->vm_page_prot));

spin_unlock(&mm->page_table_lock);

return 0;
}


2004-10-16 05:10:48

by Benjamin Herrenschmidt

[permalink] [raw]
Subject: Re: remap_page_range64() for PPC

On Sat, 2004-10-16 at 13:46, Glenn Burkhardt wrote:
> I'm writing an application to run on a PowerPC with a 2.4 embedded
> Linux kernel, and I want to make device registers for our custom
> hardware accessable from user space with mmap(). The physical address
> of the device is above the 4gb boundary (we attach to the 440's
> external peripheral bus), so a standard 'remap_page_range()' call
> won't work.
>
>.../...

I suggest you post this to the linuxppc-embedded or linuxppc-dev
lists (see https://ozlabs.org/mailman/listinfo/linuxppc-embedded)

Ben.


2004-10-16 06:32:36

by Matt Porter

[permalink] [raw]
Subject: Re: remap_page_range64() for PPC

On Fri, Oct 15, 2004 at 11:46:42PM -0400, Glenn Burkhardt wrote:
> I'm writing an application to run on a PowerPC with a 2.4 embedded
> Linux kernel, and I want to make device registers for our custom
> hardware accessable from user space with mmap(). The physical address
> of the device is above the 4gb boundary (we attach to the 440's
> external peripheral bus), so a standard 'remap_page_range()' call
> won't work.

<snip>

This has come up several times on the ppc lists (but since we still
don't have archives back, nobody can search anyway). I dropped
2.4 and 2.5 patches in source.mvista.com:/pub/linuxppc/ a long
time ago. You just need to update your board-specific fixup routine
in that version or just make a copy or remap_page_range() into your
driver and use u64 for the phys address.

The real fix, of course, is in the -mm tree as remap_pfn_range(),
I plan to merge 440 io_remap_page_range() support on top of that
call when it goes into mainline. But that doesn't help you with
2.4. :)

-Matt

2004-10-17 13:17:12

by Glenn Burkhardt

[permalink] [raw]
Subject: Re: remap_page_range64() for PPC

On Saturday 16 October 2004 02:32 am, Matt Porter wrote:
> On Fri, Oct 15, 2004 at 11:46:42PM -0400, Glenn Burkhardt wrote:
> > I'm writing an application to run on a PowerPC with a 2.4 embedded
> > Linux kernel, and I want to make device registers for our custom
> > hardware accessable from user space with mmap(). The physical address
> > of the device is above the 4gb boundary (we attach to the 440's
> > external peripheral bus), so a standard 'remap_page_range()' call
> > won't work.
>
> <snip>
>
> This has come up several times on the ppc lists (but since we still
> don't have archives back, nobody can search anyway). I dropped
^^^^^^
> 2.4 and 2.5 patches in source.mvista.com:/pub/linuxppc/ a long
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> time ago. You just need to update your board-specific fixup routine
> in that version or just make a copy or remap_page_range() into your
> driver and use u64 for the phys address.

Do you mean that you applied a patch, or had one and decided not to
use it? I just looked at mm/memory.c didn't notice anything
substantially different in remap_page_range(). To which module was
the patch applied?

Thanks for verifying that the approach I took was correct, which was
to use u64 for the phys address for remap_page_range().

2004-10-22 14:53:45

by William Lee Irwin III

[permalink] [raw]
Subject: Re: remap_page_range64() for PPC

On Fri, Oct 15, 2004 at 11:46:42PM -0400, Glenn Burkhardt wrote:
>> I'm writing an application to run on a PowerPC with a 2.4 embedded
>> Linux kernel, and I want to make device registers for our custom
>> hardware accessable from user space with mmap(). The physical address
>> of the device is above the 4gb boundary (we attach to the 440's
>> external peripheral bus), so a standard 'remap_page_range()' call
>> won't work.

On Fri, Oct 15, 2004 at 11:32:26PM -0700, Matt Porter wrote:
> This has come up several times on the ppc lists (but since we still
> don't have archives back, nobody can search anyway). I dropped
> 2.4 and 2.5 patches in source.mvista.com:/pub/linuxppc/ a long
> time ago. You just need to update your board-specific fixup routine
> in that version or just make a copy or remap_page_range() into your
> driver and use u64 for the phys address.
> The real fix, of course, is in the -mm tree as remap_pfn_range(),
> I plan to merge 440 io_remap_page_range() support on top of that
> call when it goes into mainline. But that doesn't help you with
> 2.4. :)

It has gone mainline. Glenn Burkhardt's troubles should now be fixed
for all time, unless io_remap_page_range() itself is in question. I'd
like to carry out a similar conversion on io_remap_page_range() so that
it can have similar safety against physical address overflow. There is
one 32-bit architecture where all IO memory is beyond the 4GB boundary,
and a number where at least some crucial portions of it are. This would
then decide how many arguments io_remap_page_range() takes. There are
some difficulties in this (particularly for the architecture[s] it
would benefit most) so I've been punting on this issue thus far.


-- wli