Messages in this thread Patch in this message | | | Date | Sun, 15 Nov 1998 08:59:47 +0000 | From | Jamie Lokier <> | Subject | Re: virt_to_bus and >1G of memory (was MAX_DMA_ADDRESS ...) |
| |
On Fri, Nov 13, 1998 at 11:26:41AM -0500, Jeff Millar wrote: > What's the best way to deal with a BIOS that assigns PCI addresses that > differ from the kernel's assumptions? The i386 io.h and page.h files appear > to make a hard coded assumptions about the location of non-prefetchable > memory.
I don't see anything in those files to do with non-prefetchable memory. PAGE_OFFSET is a kernel virtual address thing only.
> The Ziatech board puts non-prefetchable memory including ethernet, SCSI, and > our special frame buffer device at 0x40000000 and up. But all the other > Linux systems we looked at put non-prefetchable devices at 0xFxxxxxx. > We need to load the PCI bus address of our custom frame buffer device into > the video DMA engine of a BT-848 video decoder. But we ran into trouble > with page.h...
> The problem comes when we go to program the frame buffer address via BTTV. > We had to modify the code to not muck around with the high bits. Right now > the code explicitly tests for certain address ranges and programs the BT848 > accordingly. But that doesn't make it work if another weird BIOS shows up. > What's the right way to solve this problem?
To access MMIO memory from kernel C code, you need to use ioremap() when you initialise the driver to get an address you can use, and iounmap() when you unload the driver. It is the PCI *bus* address that you pass as argument to ioremap().
To pass the MMIO address of one device to another, simply pass the *bus* address directly from one device to another. Your problem is how to get the bus address from a user-space mapping.
On a related note, you can pass kmalloc() returned addresses to a device using virt_to_bus(), but in a bitter ironic twist, you can't pass ioremap() returned addresses in this way.
> I have to admit that we don't really understand the kernel memory system all > that well. Alan Cox replied to this by suggesting that we use physical bus > addresses...presumably by reading the address of the frame buffer as stored > in the PCI base address registers and copying it to the VIDMEM parameter of > BTTV.
Yes, that's exactly what I'd suggest.
> We can do that but we think we prefer to map the frame buffer address > into user space via mmap for use by applications and then convert a user > space address back to physical to programming the bttv. [There are some other problems in bttv.c too, notably a security problem with mappings not getting cleaned up on error]
Your method certainly sounds like a friendly interface, ideal for video to video applications.
But this is where you're getting into trouble. mmaping the frame buffer into user space is fine, but converting a generic user space address back to a bus address is dubious. There's code in bttv.c, but... aha. I see the problem now.
The problem is pte_page returning a kernel direct-mapped virtual address (clear? :-). But the pte actually points to an MMIO physical address, which isn't a valid direct-mapped virtual address. Yucky.
A little further, it seems there is no architecture-independent way to translate the physical address in a pte to a bus address. Nor is there any way to get the physical address without it becoming a "virt" address.
Of course for a specific platform like x86 it can be hacked, but... this whole area is messy.
> Hopefully this comment helps you with your driver. If you have any suggests > about how we can deal with this please feel free to comment.
You need to fix uvirt_to_bus in bttv.c, it's currently broken by design for MMIO mapped regions. You can hack it for x86 by extracting the physical address directly and using it as a bus address. Why don't you try this completely untested patch I just wrote out by hand?
------begin----------- --- linux/drivers/char/bttv.c +++ linux/drivers/char/bttv.c @@ -116,19 +116,27 @@ static inline unsigned long uvirt_to_phys(unsigned long adr) { pgd_t *pgd; pmd_t *pmd; pte_t *ptep, pte; pgd = pgd_offset(current->mm, adr); if (pgd_none(*pgd)) return 0; pmd = pmd_offset(pgd, adr); if (pmd_none(*pmd)) return 0; ptep = pte_offset(pmd, adr/*&(~PGDIR_MASK)*/); pte = *ptep; if(pte_present(pte)) return +#ifdef __i386__ + __pa((void *)(pte_page(pte)|(adr&(PAGE_SIZE-1)))); +#else virt_to_phys((void *)(pte_page(pte)|(adr&(PAGE_SIZE-1)))); +#endif return 0; } static inline unsigned long uvirt_to_bus(unsigned long adr) { +#ifdef __i386__ + return uvirt_to_phys(adr); +#else return virt_to_bus(phys_to_virt(uvirt_to_phys(adr))); +#endif } ---------end----------- Or you can wait for my userdma.c driver which will try to tackle it. Though this particular problem will only be solved by a hack, i386 only.
Thanks very much Jeff, I hadn't noticed this problem until now.
-- Jamie
- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/
| |