📄 pci-gart.c
字号:
/* * Dynamic DMA mapping support for AMD Hammer. * * Use the integrated AGP GART in the Hammer northbridge as an IOMMU for PCI. * This allows to use PCI devices that only support 32bit addresses on systems * with more than 4GB. * * See Documentation/DMA-mapping.txt for the interface specification. * * Copyright 2002 Andi Kleen, SuSE Labs. * $Id: pci-gart.c,v 1.18 2002/11/30 03:46:14 ak Exp $ *//* * Notebook:agpgart_be check if the simple reservation scheme is enough.possible future tuning: fast path for sg streaming mappings more intelligent flush strategy - flush only a single NB? flush only when gart area fills up and alloc_iommu wraps. don't flush on allocation - need to unmap the gart area first to avoid prefetches by the CPU move boundary between IOMMU and AGP in GART dynamically */ #include <linux/config.h>#include <linux/types.h>#include <linux/ctype.h>#include <linux/agp_backend.h>#include <linux/init.h>#include <linux/mm.h>#include <linux/string.h>#include <linux/spinlock.h>#include <linux/pci.h>#include <linux/module.h>#include <asm/io.h>#include <asm/mtrr.h>#include <asm/bitops.h>#include <asm/pgtable.h>#include <asm/proto.h>#include "pci-x86_64.h"unsigned long iommu_bus_base; /* GART remapping area (physical) */static unsigned long iommu_size; /* size of remapping area bytes */static unsigned long iommu_pages; /* .. and in pages */u32 *iommu_gatt_base; /* Remapping table */int no_iommu; static int no_agp; #ifdef CONFIG_IOMMU_DEBUGint force_mmu = 1;#elseint force_mmu = 0;#endifextern int fallback_aper_order;extern int fallback_aper_force;/* Allocation bitmap for the remapping area */ static spinlock_t iommu_bitmap_lock = SPIN_LOCK_UNLOCKED;static unsigned long *iommu_gart_bitmap; /* guarded by iommu_bitmap_lock */#define GPTE_VALID 1#define GPTE_COHERENT 2#define GPTE_ENCODE(x) (((x) & 0xfffff000) | (((x) >> 32) << 4) | GPTE_VALID | GPTE_COHERENT)#define GPTE_DECODE(x) (((x) & 0xfffff000) | (((u64)(x) & 0xff0) << 28))#define for_all_nb(dev) \ pci_for_each_dev(dev) \ if (dev->bus->number == 0 && PCI_FUNC(dev->devfn) == 3 && \ (PCI_SLOT(dev->devfn) >= 24) && (PCI_SLOT(dev->devfn) <= 31))#define EMERGENCY_PAGES 32 /* = 128KB */ #ifdef CONFIG_AGPextern int agp_init(void);#define AGPEXTERN extern#else#define AGPEXTERN#endif/* backdoor interface to AGP driver */AGPEXTERN int agp_memory_reserved;AGPEXTERN __u32 *agp_gatt_table;static unsigned long next_bit; /* protected by iommu_bitmap_lock */static unsigned long alloc_iommu(int size) { unsigned long offset, flags; spin_lock_irqsave(&iommu_bitmap_lock, flags); offset = find_next_zero_string(iommu_gart_bitmap,next_bit,iommu_pages,size); if (offset == -1) offset = find_next_zero_string(iommu_gart_bitmap,0,next_bit,size); if (offset != -1) { set_bit_string(iommu_gart_bitmap, offset, size); next_bit = offset+size; if (next_bit >= iommu_pages) next_bit = 0; } spin_unlock_irqrestore(&iommu_bitmap_lock, flags); return offset;} static void free_iommu(unsigned long offset, int size){ unsigned long flags; spin_lock_irqsave(&iommu_bitmap_lock, flags); clear_bit_string(iommu_gart_bitmap, offset, size); next_bit = offset; spin_unlock_irqrestore(&iommu_bitmap_lock, flags);} static inline void flush_gart(void) { struct pci_dev *nb; for_all_nb(nb) { u32 flag; pci_read_config_dword(nb, 0x9c, &flag); /* could cache this */ /* could complain for PTE walk errors here (bit 1 of flag) */ flag |= 1; pci_write_config_dword(nb, 0x9c, flag); } } void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, dma_addr_t *dma_handle){ void *memory; int gfp = GFP_ATOMIC; int i; unsigned long iommu_page; if (hwdev == NULL || hwdev->dma_mask < 0xffffffff || no_iommu) gfp |= GFP_DMA; /* * First try to allocate continuous and use directly if already * in lowmem. */ size = round_up(size, PAGE_SIZE); memory = (void *)__get_free_pages(gfp, get_order(size)); if (memory == NULL) { return NULL; } else { int high = (unsigned long)virt_to_bus(memory) + size >= 0xffffffff; int mmu = high; if (force_mmu) mmu = 1; if (no_iommu) { if (high) goto error; mmu = 0; } memset(memory, 0, size); if (!mmu) { *dma_handle = virt_to_bus(memory); return memory; } } size >>= PAGE_SHIFT; iommu_page = alloc_iommu(size); if (iommu_page == -1) goto error; /* Fill in the GATT, allocating pages as needed. */ for (i = 0; i < size; i++) { unsigned long phys_mem; void *mem = memory + i*PAGE_SIZE; if (i > 0) atomic_inc(&virt_to_page(mem)->count); phys_mem = virt_to_phys(mem); BUG_ON(phys_mem & ~PHYSICAL_PAGE_MASK); iommu_gatt_base[iommu_page + i] = GPTE_ENCODE(phys_mem); } flush_gart(); *dma_handle = iommu_bus_base + (iommu_page << PAGE_SHIFT); return memory; error: free_pages((unsigned long)memory, get_order(size)); return NULL; }/* * Unmap consistent memory. * The caller must ensure that the device has finished accessing the mapping. */void pci_free_consistent(struct pci_dev *hwdev, size_t size, void *vaddr, dma_addr_t bus){ u64 pte; unsigned long iommu_page; int i; size = round_up(size, PAGE_SIZE); if (bus < iommu_bus_base || bus > iommu_bus_base + iommu_size) { free_pages((unsigned long)vaddr, get_order(size)); return; } size >>= PAGE_SHIFT; iommu_page = (bus - iommu_bus_base) / PAGE_SIZE; for (i = 0; i < size; i++) { pte = iommu_gatt_base[iommu_page + i]; BUG_ON((pte & GPTE_VALID) == 0); iommu_gatt_base[iommu_page + i] = 0; free_page((unsigned long) __va(GPTE_DECODE(pte))); } flush_gart(); free_iommu(iommu_page, size);}#ifdef CONFIG_IOMMU_DEBUG/* Debugging aid for drivers that don't free their IOMMU tables */static void **iommu_leak_tab; static int leak_trace;int iommu_leak_pages = 20; extern unsigned long printk_address(unsigned long);void dump_leak(void){ int i; static int dump; if (dump || !iommu_leak_tab) return; dump = 1; show_stack(NULL); /* Very crude. dump some from the end of the table too */ printk("Dumping %d pages from end of IOMMU:\n", iommu_leak_pages); for (i = 0; i < iommu_leak_pages; i+=2) { printk("%lu: ", iommu_pages-i); printk_address((unsigned long) iommu_leak_tab[iommu_pages-i]); printk("%c", (i+1)%2 == 0 ? '\n' : ' '); } printk("\n");}#endifstatic void iommu_full(struct pci_dev *dev, void *addr, size_t size, int dir){ /* * Ran out of IOMMU space for this operation. This is very bad. * Unfortunately the drivers cannot handle this operation properly. * Return some non mapped prereserved space in the aperture and * let the Northbridge deal with it. This will result in garbage * in the IO operation. When the size exceeds the prereserved space * memory corruption will occur or random memory will be DMAed * out. Hopefully no network devices use single mappings that big. */ printk(KERN_ERR "PCI-DMA: Error: ran out out IOMMU space for %p size %lu at device %s[%s]\n", addr,size, dev ? dev->name : "?", dev ? dev->slot_name : "?"); if (size > PAGE_SIZE*EMERGENCY_PAGES) { if (dir == PCI_DMA_FROMDEVICE || dir == PCI_DMA_BIDIRECTIONAL) panic("PCI-DMA: Memory will be corrupted\n"); if (dir == PCI_DMA_TODEVICE || dir == PCI_DMA_BIDIRECTIONAL) panic("PCI-DMA: Random memory will be DMAed\n"); } #ifdef CONFIG_IOMMU_DEBUG dump_leak(); #endif} static inline int need_iommu(struct pci_dev *dev, unsigned long addr, size_t size){ u64 mask = dev ? dev->dma_mask : 0xffffffff; int high = (~mask & (unsigned long)(addr + size)) != 0; int mmu = high; if (force_mmu) mmu = 1; if (no_iommu) { if (high) panic("pci_map_single: high address but no IOMMU.\n"); mmu = 0; } return mmu; }dma_addr_t __pci_map_single(struct pci_dev *dev, void *addr, size_t size, int dir, int flush){ unsigned long iommu_page;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -