Merge branch 'core-iommu-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
* 'core-iommu-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip: (63 commits) x86, Calgary IOMMU quirk: Find nearest matching Calgary while walking up the PCI tree x86/amd-iommu: Remove amd_iommu_pd_table x86/amd-iommu: Move reset_iommu_command_buffer out of locked code x86/amd-iommu: Cleanup DTE flushing code x86/amd-iommu: Introduce iommu_flush_device() function x86/amd-iommu: Cleanup attach/detach_device code x86/amd-iommu: Keep devices per domain in a list x86/amd-iommu: Add device bind reference counting x86/amd-iommu: Use dev->arch->iommu to store iommu related information x86/amd-iommu: Remove support for domain sharing x86/amd-iommu: Rearrange dma_ops related functions x86/amd-iommu: Move some pte allocation functions in the right section x86/amd-iommu: Remove iommu parameter from dma_ops_domain_alloc x86/amd-iommu: Use get_device_id and check_device where appropriate x86/amd-iommu: Move find_protection_domain to helper functions x86/amd-iommu: Simplify get_device_resources() x86/amd-iommu: Let domain_for_device handle aliases x86/amd-iommu: Remove iommu specific handling from dma_ops path x86/amd-iommu: Remove iommu parameter from __(un)map_single x86/amd-iommu: Make alloc_new_range aware of multiple IOMMUs ...
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2008 Advanced Micro Devices, Inc.
|
||||
* Copyright (C) 2007-2009 Advanced Micro Devices, Inc.
|
||||
* Author: Joerg Roedel <joerg.roedel@amd.com>
|
||||
* Leo Duran <leo.duran@amd.com>
|
||||
*
|
||||
@@ -25,10 +25,12 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/msi.h>
|
||||
#include <asm/pci-direct.h>
|
||||
#include <asm/amd_iommu_proto.h>
|
||||
#include <asm/amd_iommu_types.h>
|
||||
#include <asm/amd_iommu.h>
|
||||
#include <asm/iommu.h>
|
||||
#include <asm/gart.h>
|
||||
#include <asm/x86_init.h>
|
||||
|
||||
/*
|
||||
* definitions for the ACPI scanning code
|
||||
@@ -123,18 +125,24 @@ u16 amd_iommu_last_bdf; /* largest PCI device id we have
|
||||
to handle */
|
||||
LIST_HEAD(amd_iommu_unity_map); /* a list of required unity mappings
|
||||
we find in ACPI */
|
||||
#ifdef CONFIG_IOMMU_STRESS
|
||||
bool amd_iommu_isolate = false;
|
||||
#else
|
||||
bool amd_iommu_isolate = true; /* if true, device isolation is
|
||||
enabled */
|
||||
#endif
|
||||
|
||||
bool amd_iommu_unmap_flush; /* if true, flush on every unmap */
|
||||
|
||||
LIST_HEAD(amd_iommu_list); /* list of all AMD IOMMUs in the
|
||||
system */
|
||||
|
||||
/* Array to assign indices to IOMMUs*/
|
||||
struct amd_iommu *amd_iommus[MAX_IOMMUS];
|
||||
int amd_iommus_present;
|
||||
|
||||
/* IOMMUs have a non-present cache? */
|
||||
bool amd_iommu_np_cache __read_mostly;
|
||||
|
||||
/*
|
||||
* List of protection domains - used during resume
|
||||
*/
|
||||
LIST_HEAD(amd_iommu_pd_list);
|
||||
spinlock_t amd_iommu_pd_lock;
|
||||
|
||||
/*
|
||||
* Pointer to the device table which is shared by all AMD IOMMUs
|
||||
* it is indexed by the PCI device id or the HT unit id and contains
|
||||
@@ -156,12 +164,6 @@ u16 *amd_iommu_alias_table;
|
||||
*/
|
||||
struct amd_iommu **amd_iommu_rlookup_table;
|
||||
|
||||
/*
|
||||
* The pd table (protection domain table) is used to find the protection domain
|
||||
* data structure a device belongs to. Indexed with the PCI device id too.
|
||||
*/
|
||||
struct protection_domain **amd_iommu_pd_table;
|
||||
|
||||
/*
|
||||
* AMD IOMMU allows up to 2^16 differend protection domains. This is a bitmap
|
||||
* to know which ones are already in use.
|
||||
@@ -838,7 +840,18 @@ static void __init free_iommu_all(void)
|
||||
static int __init init_iommu_one(struct amd_iommu *iommu, struct ivhd_header *h)
|
||||
{
|
||||
spin_lock_init(&iommu->lock);
|
||||
|
||||
/* Add IOMMU to internal data structures */
|
||||
list_add_tail(&iommu->list, &amd_iommu_list);
|
||||
iommu->index = amd_iommus_present++;
|
||||
|
||||
if (unlikely(iommu->index >= MAX_IOMMUS)) {
|
||||
WARN(1, "AMD-Vi: System has more IOMMUs than supported by this driver\n");
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
/* Index is fine - add IOMMU to the array */
|
||||
amd_iommus[iommu->index] = iommu;
|
||||
|
||||
/*
|
||||
* Copy data from ACPI table entry to the iommu struct
|
||||
@@ -868,6 +881,9 @@ static int __init init_iommu_one(struct amd_iommu *iommu, struct ivhd_header *h)
|
||||
init_iommu_from_acpi(iommu, h);
|
||||
init_iommu_devices(iommu);
|
||||
|
||||
if (iommu->cap & (1UL << IOMMU_CAP_NPCACHE))
|
||||
amd_iommu_np_cache = true;
|
||||
|
||||
return pci_enable_device(iommu->dev);
|
||||
}
|
||||
|
||||
@@ -925,7 +941,7 @@ static int __init init_iommu_all(struct acpi_table_header *table)
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int __init iommu_setup_msi(struct amd_iommu *iommu)
|
||||
static int iommu_setup_msi(struct amd_iommu *iommu)
|
||||
{
|
||||
int r;
|
||||
|
||||
@@ -1176,19 +1192,10 @@ static struct sys_device device_amd_iommu = {
|
||||
* functions. Finally it prints some information about AMD IOMMUs and
|
||||
* the driver state and enables the hardware.
|
||||
*/
|
||||
int __init amd_iommu_init(void)
|
||||
static int __init amd_iommu_init(void)
|
||||
{
|
||||
int i, ret = 0;
|
||||
|
||||
|
||||
if (no_iommu) {
|
||||
printk(KERN_INFO "AMD-Vi disabled by kernel command line\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!amd_iommu_detected)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* First parse ACPI tables to find the largest Bus/Dev/Func
|
||||
* we need to handle. Upon this information the shared data
|
||||
@@ -1225,15 +1232,6 @@ int __init amd_iommu_init(void)
|
||||
if (amd_iommu_rlookup_table == NULL)
|
||||
goto free;
|
||||
|
||||
/*
|
||||
* Protection Domain table - maps devices to protection domains
|
||||
* This table has the same size as the rlookup_table
|
||||
*/
|
||||
amd_iommu_pd_table = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
|
||||
get_order(rlookup_table_size));
|
||||
if (amd_iommu_pd_table == NULL)
|
||||
goto free;
|
||||
|
||||
amd_iommu_pd_alloc_bitmap = (void *)__get_free_pages(
|
||||
GFP_KERNEL | __GFP_ZERO,
|
||||
get_order(MAX_DOMAIN_ID/8));
|
||||
@@ -1255,6 +1253,8 @@ int __init amd_iommu_init(void)
|
||||
*/
|
||||
amd_iommu_pd_alloc_bitmap[0] = 1;
|
||||
|
||||
spin_lock_init(&amd_iommu_pd_lock);
|
||||
|
||||
/*
|
||||
* now the data structures are allocated and basically initialized
|
||||
* start the real acpi table scan
|
||||
@@ -1286,17 +1286,12 @@ int __init amd_iommu_init(void)
|
||||
if (iommu_pass_through)
|
||||
goto out;
|
||||
|
||||
printk(KERN_INFO "AMD-Vi: device isolation ");
|
||||
if (amd_iommu_isolate)
|
||||
printk("enabled\n");
|
||||
else
|
||||
printk("disabled\n");
|
||||
|
||||
if (amd_iommu_unmap_flush)
|
||||
printk(KERN_INFO "AMD-Vi: IO/TLB flush on unmap enabled\n");
|
||||
else
|
||||
printk(KERN_INFO "AMD-Vi: Lazy IO/TLB flushing enabled\n");
|
||||
|
||||
x86_platform.iommu_shutdown = disable_iommus;
|
||||
out:
|
||||
return ret;
|
||||
|
||||
@@ -1304,9 +1299,6 @@ free:
|
||||
free_pages((unsigned long)amd_iommu_pd_alloc_bitmap,
|
||||
get_order(MAX_DOMAIN_ID/8));
|
||||
|
||||
free_pages((unsigned long)amd_iommu_pd_table,
|
||||
get_order(rlookup_table_size));
|
||||
|
||||
free_pages((unsigned long)amd_iommu_rlookup_table,
|
||||
get_order(rlookup_table_size));
|
||||
|
||||
@@ -1323,11 +1315,6 @@ free:
|
||||
goto out;
|
||||
}
|
||||
|
||||
void amd_iommu_shutdown(void)
|
||||
{
|
||||
disable_iommus();
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Early detect code. This code runs at IOMMU detection time in the DMA
|
||||
@@ -1342,16 +1329,13 @@ static int __init early_amd_iommu_detect(struct acpi_table_header *table)
|
||||
|
||||
void __init amd_iommu_detect(void)
|
||||
{
|
||||
if (swiotlb || no_iommu || (iommu_detected && !gart_iommu_aperture))
|
||||
if (no_iommu || (iommu_detected && !gart_iommu_aperture))
|
||||
return;
|
||||
|
||||
if (acpi_table_parse("IVRS", early_amd_iommu_detect) == 0) {
|
||||
iommu_detected = 1;
|
||||
amd_iommu_detected = 1;
|
||||
#ifdef CONFIG_GART_IOMMU
|
||||
gart_iommu_aperture_disabled = 1;
|
||||
gart_iommu_aperture = 0;
|
||||
#endif
|
||||
x86_init.iommu.iommu_init = amd_iommu_init;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1372,10 +1356,6 @@ static int __init parse_amd_iommu_dump(char *str)
|
||||
static int __init parse_amd_iommu_options(char *str)
|
||||
{
|
||||
for (; *str; ++str) {
|
||||
if (strncmp(str, "isolate", 7) == 0)
|
||||
amd_iommu_isolate = true;
|
||||
if (strncmp(str, "share", 5) == 0)
|
||||
amd_iommu_isolate = false;
|
||||
if (strncmp(str, "fullflush", 9) == 0)
|
||||
amd_iommu_unmap_flush = true;
|
||||
}
|
||||
|
@@ -28,6 +28,7 @@
|
||||
#include <asm/pci-direct.h>
|
||||
#include <asm/dma.h>
|
||||
#include <asm/k8.h>
|
||||
#include <asm/x86_init.h>
|
||||
|
||||
int gart_iommu_aperture;
|
||||
int gart_iommu_aperture_disabled __initdata;
|
||||
@@ -400,6 +401,7 @@ void __init gart_iommu_hole_init(void)
|
||||
|
||||
iommu_detected = 1;
|
||||
gart_iommu_aperture = 1;
|
||||
x86_init.iommu.iommu_init = gart_iommu_init;
|
||||
|
||||
aper_order = (read_pci_config(bus, slot, 3, AMD64_GARTAPERTURECTL) >> 1) & 7;
|
||||
aper_size = (32 * 1024 * 1024) << aper_order;
|
||||
@@ -456,7 +458,7 @@ out:
|
||||
|
||||
if (aper_alloc) {
|
||||
/* Got the aperture from the AGP bridge */
|
||||
} else if (swiotlb && !valid_agp) {
|
||||
} else if (!valid_agp) {
|
||||
/* Do nothing */
|
||||
} else if ((!no_iommu && max_pfn > MAX_DMA32_PFN) ||
|
||||
force_iommu ||
|
||||
|
@@ -27,8 +27,7 @@
|
||||
#include <asm/cpu.h>
|
||||
#include <asm/reboot.h>
|
||||
#include <asm/virtext.h>
|
||||
#include <asm/iommu.h>
|
||||
|
||||
#include <asm/x86_init.h>
|
||||
|
||||
#if defined(CONFIG_SMP) && defined(CONFIG_X86_LOCAL_APIC)
|
||||
|
||||
@@ -106,7 +105,7 @@ void native_machine_crash_shutdown(struct pt_regs *regs)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_X86_64
|
||||
pci_iommu_shutdown();
|
||||
x86_platform.iommu_shutdown();
|
||||
#endif
|
||||
|
||||
crash_save_cpu(regs, safe_smp_processor_id());
|
||||
|
@@ -46,6 +46,7 @@
|
||||
#include <asm/dma.h>
|
||||
#include <asm/rio.h>
|
||||
#include <asm/bios_ebda.h>
|
||||
#include <asm/x86_init.h>
|
||||
|
||||
#ifdef CONFIG_CALGARY_IOMMU_ENABLED_BY_DEFAULT
|
||||
int use_calgary __read_mostly = 1;
|
||||
@@ -244,7 +245,7 @@ static unsigned long iommu_range_alloc(struct device *dev,
|
||||
if (panic_on_overflow)
|
||||
panic("Calgary: fix the allocator.\n");
|
||||
else
|
||||
return bad_dma_address;
|
||||
return DMA_ERROR_CODE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,12 +261,15 @@ static dma_addr_t iommu_alloc(struct device *dev, struct iommu_table *tbl,
|
||||
void *vaddr, unsigned int npages, int direction)
|
||||
{
|
||||
unsigned long entry;
|
||||
dma_addr_t ret = bad_dma_address;
|
||||
dma_addr_t ret;
|
||||
|
||||
entry = iommu_range_alloc(dev, tbl, npages);
|
||||
|
||||
if (unlikely(entry == bad_dma_address))
|
||||
goto error;
|
||||
if (unlikely(entry == DMA_ERROR_CODE)) {
|
||||
printk(KERN_WARNING "Calgary: failed to allocate %u pages in "
|
||||
"iommu %p\n", npages, tbl);
|
||||
return DMA_ERROR_CODE;
|
||||
}
|
||||
|
||||
/* set the return dma address */
|
||||
ret = (entry << PAGE_SHIFT) | ((unsigned long)vaddr & ~PAGE_MASK);
|
||||
@@ -273,13 +277,7 @@ static dma_addr_t iommu_alloc(struct device *dev, struct iommu_table *tbl,
|
||||
/* put the TCEs in the HW table */
|
||||
tce_build(tbl, entry, npages, (unsigned long)vaddr & PAGE_MASK,
|
||||
direction);
|
||||
|
||||
return ret;
|
||||
|
||||
error:
|
||||
printk(KERN_WARNING "Calgary: failed to allocate %u pages in "
|
||||
"iommu %p\n", npages, tbl);
|
||||
return bad_dma_address;
|
||||
}
|
||||
|
||||
static void iommu_free(struct iommu_table *tbl, dma_addr_t dma_addr,
|
||||
@@ -290,8 +288,8 @@ static void iommu_free(struct iommu_table *tbl, dma_addr_t dma_addr,
|
||||
unsigned long flags;
|
||||
|
||||
/* were we called with bad_dma_address? */
|
||||
badend = bad_dma_address + (EMERGENCY_PAGES * PAGE_SIZE);
|
||||
if (unlikely((dma_addr >= bad_dma_address) && (dma_addr < badend))) {
|
||||
badend = DMA_ERROR_CODE + (EMERGENCY_PAGES * PAGE_SIZE);
|
||||
if (unlikely((dma_addr >= DMA_ERROR_CODE) && (dma_addr < badend))) {
|
||||
WARN(1, KERN_ERR "Calgary: driver tried unmapping bad DMA "
|
||||
"address 0x%Lx\n", dma_addr);
|
||||
return;
|
||||
@@ -318,13 +316,15 @@ static inline struct iommu_table *find_iommu_table(struct device *dev)
|
||||
|
||||
pdev = to_pci_dev(dev);
|
||||
|
||||
/* search up the device tree for an iommu */
|
||||
pbus = pdev->bus;
|
||||
|
||||
/* is the device behind a bridge? Look for the root bus */
|
||||
while (pbus->parent)
|
||||
do {
|
||||
tbl = pci_iommu(pbus);
|
||||
if (tbl && tbl->it_busno == pbus->number)
|
||||
break;
|
||||
tbl = NULL;
|
||||
pbus = pbus->parent;
|
||||
|
||||
tbl = pci_iommu(pbus);
|
||||
} while (pbus);
|
||||
|
||||
BUG_ON(tbl && (tbl->it_busno != pbus->number));
|
||||
|
||||
@@ -373,7 +373,7 @@ static int calgary_map_sg(struct device *dev, struct scatterlist *sg,
|
||||
npages = iommu_num_pages(vaddr, s->length, PAGE_SIZE);
|
||||
|
||||
entry = iommu_range_alloc(dev, tbl, npages);
|
||||
if (entry == bad_dma_address) {
|
||||
if (entry == DMA_ERROR_CODE) {
|
||||
/* makes sure unmap knows to stop */
|
||||
s->dma_length = 0;
|
||||
goto error;
|
||||
@@ -391,7 +391,7 @@ static int calgary_map_sg(struct device *dev, struct scatterlist *sg,
|
||||
error:
|
||||
calgary_unmap_sg(dev, sg, nelems, dir, NULL);
|
||||
for_each_sg(sg, s, nelems, i) {
|
||||
sg->dma_address = bad_dma_address;
|
||||
sg->dma_address = DMA_ERROR_CODE;
|
||||
sg->dma_length = 0;
|
||||
}
|
||||
return 0;
|
||||
@@ -446,7 +446,7 @@ static void* calgary_alloc_coherent(struct device *dev, size_t size,
|
||||
|
||||
/* set up tces to cover the allocated range */
|
||||
mapping = iommu_alloc(dev, tbl, ret, npages, DMA_BIDIRECTIONAL);
|
||||
if (mapping == bad_dma_address)
|
||||
if (mapping == DMA_ERROR_CODE)
|
||||
goto free;
|
||||
*dma_handle = mapping;
|
||||
return ret;
|
||||
@@ -727,7 +727,7 @@ static void __init calgary_reserve_regions(struct pci_dev *dev)
|
||||
struct iommu_table *tbl = pci_iommu(dev->bus);
|
||||
|
||||
/* reserve EMERGENCY_PAGES from bad_dma_address and up */
|
||||
iommu_range_reserve(tbl, bad_dma_address, EMERGENCY_PAGES);
|
||||
iommu_range_reserve(tbl, DMA_ERROR_CODE, EMERGENCY_PAGES);
|
||||
|
||||
/* avoid the BIOS/VGA first 640KB-1MB region */
|
||||
/* for CalIOC2 - avoid the entire first MB */
|
||||
@@ -1344,6 +1344,23 @@ static void __init get_tce_space_from_tar(void)
|
||||
return;
|
||||
}
|
||||
|
||||
static int __init calgary_iommu_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* ok, we're trying to use Calgary - let's roll */
|
||||
printk(KERN_INFO "PCI-DMA: Using Calgary IOMMU\n");
|
||||
|
||||
ret = calgary_init();
|
||||
if (ret) {
|
||||
printk(KERN_ERR "PCI-DMA: Calgary init failed %d, "
|
||||
"falling back to no_iommu\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __init detect_calgary(void)
|
||||
{
|
||||
int bus;
|
||||
@@ -1357,7 +1374,7 @@ void __init detect_calgary(void)
|
||||
* if the user specified iommu=off or iommu=soft or we found
|
||||
* another HW IOMMU already, bail out.
|
||||
*/
|
||||
if (swiotlb || no_iommu || iommu_detected)
|
||||
if (no_iommu || iommu_detected)
|
||||
return;
|
||||
|
||||
if (!use_calgary)
|
||||
@@ -1442,9 +1459,7 @@ void __init detect_calgary(void)
|
||||
printk(KERN_INFO "PCI-DMA: Calgary TCE table spec is %d\n",
|
||||
specified_table_size);
|
||||
|
||||
/* swiotlb for devices that aren't behind the Calgary. */
|
||||
if (max_pfn > MAX_DMA32_PFN)
|
||||
swiotlb = 1;
|
||||
x86_init.iommu.iommu_init = calgary_iommu_init;
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -1457,35 +1472,6 @@ cleanup:
|
||||
}
|
||||
}
|
||||
|
||||
int __init calgary_iommu_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (no_iommu || (swiotlb && !calgary_detected))
|
||||
return -ENODEV;
|
||||
|
||||
if (!calgary_detected)
|
||||
return -ENODEV;
|
||||
|
||||
/* ok, we're trying to use Calgary - let's roll */
|
||||
printk(KERN_INFO "PCI-DMA: Using Calgary IOMMU\n");
|
||||
|
||||
ret = calgary_init();
|
||||
if (ret) {
|
||||
printk(KERN_ERR "PCI-DMA: Calgary init failed %d, "
|
||||
"falling back to no_iommu\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
force_iommu = 1;
|
||||
bad_dma_address = 0x0;
|
||||
/* dma_ops is set to swiotlb or nommu */
|
||||
if (!dma_ops)
|
||||
dma_ops = &nommu_dma_ops;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init calgary_parse_options(char *p)
|
||||
{
|
||||
unsigned int bridge;
|
||||
|
@@ -11,10 +11,11 @@
|
||||
#include <asm/gart.h>
|
||||
#include <asm/calgary.h>
|
||||
#include <asm/amd_iommu.h>
|
||||
#include <asm/x86_init.h>
|
||||
|
||||
static int forbid_dac __read_mostly;
|
||||
|
||||
struct dma_map_ops *dma_ops;
|
||||
struct dma_map_ops *dma_ops = &nommu_dma_ops;
|
||||
EXPORT_SYMBOL(dma_ops);
|
||||
|
||||
static int iommu_sac_force __read_mostly;
|
||||
@@ -42,9 +43,6 @@ int iommu_detected __read_mostly = 0;
|
||||
*/
|
||||
int iommu_pass_through __read_mostly;
|
||||
|
||||
dma_addr_t bad_dma_address __read_mostly = 0;
|
||||
EXPORT_SYMBOL(bad_dma_address);
|
||||
|
||||
/* Dummy device used for NULL arguments (normally ISA). */
|
||||
struct device x86_dma_fallback_dev = {
|
||||
.init_name = "fallback device",
|
||||
@@ -126,20 +124,17 @@ void __init pci_iommu_alloc(void)
|
||||
/* free the range so iommu could get some range less than 4G */
|
||||
dma32_free_bootmem();
|
||||
#endif
|
||||
if (pci_swiotlb_init())
|
||||
return;
|
||||
|
||||
/*
|
||||
* The order of these functions is important for
|
||||
* fall-back/fail-over reasons
|
||||
*/
|
||||
gart_iommu_hole_init();
|
||||
|
||||
detect_calgary();
|
||||
|
||||
detect_intel_iommu();
|
||||
|
||||
/* needs to be called after gart_iommu_hole_init */
|
||||
amd_iommu_detect();
|
||||
|
||||
pci_swiotlb_init();
|
||||
}
|
||||
|
||||
void *dma_generic_alloc_coherent(struct device *dev, size_t size,
|
||||
@@ -214,7 +209,7 @@ static __init int iommu_setup(char *p)
|
||||
if (!strncmp(p, "allowdac", 8))
|
||||
forbid_dac = 0;
|
||||
if (!strncmp(p, "nodac", 5))
|
||||
forbid_dac = -1;
|
||||
forbid_dac = 1;
|
||||
if (!strncmp(p, "usedac", 6)) {
|
||||
forbid_dac = -1;
|
||||
return 1;
|
||||
@@ -289,25 +284,17 @@ static int __init pci_iommu_init(void)
|
||||
#ifdef CONFIG_PCI
|
||||
dma_debug_add_bus(&pci_bus_type);
|
||||
#endif
|
||||
x86_init.iommu.iommu_init();
|
||||
|
||||
calgary_iommu_init();
|
||||
if (swiotlb) {
|
||||
printk(KERN_INFO "PCI-DMA: "
|
||||
"Using software bounce buffering for IO (SWIOTLB)\n");
|
||||
swiotlb_print_info();
|
||||
} else
|
||||
swiotlb_free();
|
||||
|
||||
intel_iommu_init();
|
||||
|
||||
amd_iommu_init();
|
||||
|
||||
gart_iommu_init();
|
||||
|
||||
no_iommu_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pci_iommu_shutdown(void)
|
||||
{
|
||||
gart_iommu_shutdown();
|
||||
|
||||
amd_iommu_shutdown();
|
||||
}
|
||||
/* Must execute after PCI subsystem */
|
||||
rootfs_initcall(pci_iommu_init);
|
||||
|
||||
|
@@ -39,6 +39,7 @@
|
||||
#include <asm/swiotlb.h>
|
||||
#include <asm/dma.h>
|
||||
#include <asm/k8.h>
|
||||
#include <asm/x86_init.h>
|
||||
|
||||
static unsigned long iommu_bus_base; /* GART remapping area (physical) */
|
||||
static unsigned long iommu_size; /* size of remapping area bytes */
|
||||
@@ -46,6 +47,8 @@ static unsigned long iommu_pages; /* .. and in pages */
|
||||
|
||||
static u32 *iommu_gatt_base; /* Remapping table */
|
||||
|
||||
static dma_addr_t bad_dma_addr;
|
||||
|
||||
/*
|
||||
* If this is disabled the IOMMU will use an optimized flushing strategy
|
||||
* of only flushing when an mapping is reused. With it true the GART is
|
||||
@@ -92,7 +95,7 @@ static unsigned long alloc_iommu(struct device *dev, int size,
|
||||
|
||||
base_index = ALIGN(iommu_bus_base & dma_get_seg_boundary(dev),
|
||||
PAGE_SIZE) >> PAGE_SHIFT;
|
||||
boundary_size = ALIGN((unsigned long long)dma_get_seg_boundary(dev) + 1,
|
||||
boundary_size = ALIGN((u64)dma_get_seg_boundary(dev) + 1,
|
||||
PAGE_SIZE) >> PAGE_SHIFT;
|
||||
|
||||
spin_lock_irqsave(&iommu_bitmap_lock, flags);
|
||||
@@ -216,7 +219,7 @@ static dma_addr_t dma_map_area(struct device *dev, dma_addr_t phys_mem,
|
||||
if (panic_on_overflow)
|
||||
panic("dma_map_area overflow %lu bytes\n", size);
|
||||
iommu_full(dev, size, dir);
|
||||
return bad_dma_address;
|
||||
return bad_dma_addr;
|
||||
}
|
||||
|
||||
for (i = 0; i < npages; i++) {
|
||||
@@ -294,7 +297,7 @@ static int dma_map_sg_nonforce(struct device *dev, struct scatterlist *sg,
|
||||
int i;
|
||||
|
||||
#ifdef CONFIG_IOMMU_DEBUG
|
||||
printk(KERN_DEBUG "dma_map_sg overflow\n");
|
||||
pr_debug("dma_map_sg overflow\n");
|
||||
#endif
|
||||
|
||||
for_each_sg(sg, s, nents, i) {
|
||||
@@ -302,7 +305,7 @@ static int dma_map_sg_nonforce(struct device *dev, struct scatterlist *sg,
|
||||
|
||||
if (nonforced_iommu(dev, addr, s->length)) {
|
||||
addr = dma_map_area(dev, addr, s->length, dir, 0);
|
||||
if (addr == bad_dma_address) {
|
||||
if (addr == bad_dma_addr) {
|
||||
if (i > 0)
|
||||
gart_unmap_sg(dev, sg, i, dir, NULL);
|
||||
nents = 0;
|
||||
@@ -389,12 +392,14 @@ static int gart_map_sg(struct device *dev, struct scatterlist *sg, int nents,
|
||||
if (!dev)
|
||||
dev = &x86_dma_fallback_dev;
|
||||
|
||||
out = 0;
|
||||
start = 0;
|
||||
start_sg = sgmap = sg;
|
||||
seg_size = 0;
|
||||
max_seg_size = dma_get_max_seg_size(dev);
|
||||
ps = NULL; /* shut up gcc */
|
||||
out = 0;
|
||||
start = 0;
|
||||
start_sg = sg;
|
||||
sgmap = sg;
|
||||
seg_size = 0;
|
||||
max_seg_size = dma_get_max_seg_size(dev);
|
||||
ps = NULL; /* shut up gcc */
|
||||
|
||||
for_each_sg(sg, s, nents, i) {
|
||||
dma_addr_t addr = sg_phys(s);
|
||||
|
||||
@@ -417,11 +422,12 @@ static int gart_map_sg(struct device *dev, struct scatterlist *sg, int nents,
|
||||
sgmap, pages, need) < 0)
|
||||
goto error;
|
||||
out++;
|
||||
seg_size = 0;
|
||||
sgmap = sg_next(sgmap);
|
||||
pages = 0;
|
||||
start = i;
|
||||
start_sg = s;
|
||||
|
||||
seg_size = 0;
|
||||
sgmap = sg_next(sgmap);
|
||||
pages = 0;
|
||||
start = i;
|
||||
start_sg = s;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -455,7 +461,7 @@ error:
|
||||
|
||||
iommu_full(dev, pages << PAGE_SHIFT, dir);
|
||||
for_each_sg(sg, s, nents, i)
|
||||
s->dma_address = bad_dma_address;
|
||||
s->dma_address = bad_dma_addr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -479,7 +485,7 @@ gart_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_addr,
|
||||
DMA_BIDIRECTIONAL, align_mask);
|
||||
|
||||
flush_gart();
|
||||
if (paddr != bad_dma_address) {
|
||||
if (paddr != bad_dma_addr) {
|
||||
*dma_addr = paddr;
|
||||
return page_address(page);
|
||||
}
|
||||
@@ -499,6 +505,11 @@ gart_free_coherent(struct device *dev, size_t size, void *vaddr,
|
||||
free_pages((unsigned long)vaddr, get_order(size));
|
||||
}
|
||||
|
||||
static int gart_mapping_error(struct device *dev, dma_addr_t dma_addr)
|
||||
{
|
||||
return (dma_addr == bad_dma_addr);
|
||||
}
|
||||
|
||||
static int no_agp;
|
||||
|
||||
static __init unsigned long check_iommu_size(unsigned long aper, u64 aper_size)
|
||||
@@ -515,7 +526,7 @@ static __init unsigned long check_iommu_size(unsigned long aper, u64 aper_size)
|
||||
iommu_size -= round_up(a, PMD_PAGE_SIZE) - a;
|
||||
|
||||
if (iommu_size < 64*1024*1024) {
|
||||
printk(KERN_WARNING
|
||||
pr_warning(
|
||||
"PCI-DMA: Warning: Small IOMMU %luMB."
|
||||
" Consider increasing the AGP aperture in BIOS\n",
|
||||
iommu_size >> 20);
|
||||
@@ -570,28 +581,32 @@ void set_up_gart_resume(u32 aper_order, u32 aper_alloc)
|
||||
aperture_alloc = aper_alloc;
|
||||
}
|
||||
|
||||
static void gart_fixup_northbridges(struct sys_device *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!fix_up_north_bridges)
|
||||
return;
|
||||
|
||||
pr_info("PCI-DMA: Restoring GART aperture settings\n");
|
||||
|
||||
for (i = 0; i < num_k8_northbridges; i++) {
|
||||
struct pci_dev *dev = k8_northbridges[i];
|
||||
|
||||
/*
|
||||
* Don't enable translations just yet. That is the next
|
||||
* step. Restore the pre-suspend aperture settings.
|
||||
*/
|
||||
pci_write_config_dword(dev, AMD64_GARTAPERTURECTL, aperture_order << 1);
|
||||
pci_write_config_dword(dev, AMD64_GARTAPERTUREBASE, aperture_alloc >> 25);
|
||||
}
|
||||
}
|
||||
|
||||
static int gart_resume(struct sys_device *dev)
|
||||
{
|
||||
printk(KERN_INFO "PCI-DMA: Resuming GART IOMMU\n");
|
||||
pr_info("PCI-DMA: Resuming GART IOMMU\n");
|
||||
|
||||
if (fix_up_north_bridges) {
|
||||
int i;
|
||||
|
||||
printk(KERN_INFO "PCI-DMA: Restoring GART aperture settings\n");
|
||||
|
||||
for (i = 0; i < num_k8_northbridges; i++) {
|
||||
struct pci_dev *dev = k8_northbridges[i];
|
||||
|
||||
/*
|
||||
* Don't enable translations just yet. That is the next
|
||||
* step. Restore the pre-suspend aperture settings.
|
||||
*/
|
||||
pci_write_config_dword(dev, AMD64_GARTAPERTURECTL,
|
||||
aperture_order << 1);
|
||||
pci_write_config_dword(dev, AMD64_GARTAPERTUREBASE,
|
||||
aperture_alloc >> 25);
|
||||
}
|
||||
}
|
||||
gart_fixup_northbridges(dev);
|
||||
|
||||
enable_gart_translations();
|
||||
|
||||
@@ -604,15 +619,14 @@ static int gart_suspend(struct sys_device *dev, pm_message_t state)
|
||||
}
|
||||
|
||||
static struct sysdev_class gart_sysdev_class = {
|
||||
.name = "gart",
|
||||
.suspend = gart_suspend,
|
||||
.resume = gart_resume,
|
||||
.name = "gart",
|
||||
.suspend = gart_suspend,
|
||||
.resume = gart_resume,
|
||||
|
||||
};
|
||||
|
||||
static struct sys_device device_gart = {
|
||||
.id = 0,
|
||||
.cls = &gart_sysdev_class,
|
||||
.cls = &gart_sysdev_class,
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -627,7 +641,8 @@ static __init int init_k8_gatt(struct agp_kern_info *info)
|
||||
void *gatt;
|
||||
int i, error;
|
||||
|
||||
printk(KERN_INFO "PCI-DMA: Disabling AGP.\n");
|
||||
pr_info("PCI-DMA: Disabling AGP.\n");
|
||||
|
||||
aper_size = aper_base = info->aper_size = 0;
|
||||
dev = NULL;
|
||||
for (i = 0; i < num_k8_northbridges; i++) {
|
||||
@@ -645,6 +660,7 @@ static __init int init_k8_gatt(struct agp_kern_info *info)
|
||||
}
|
||||
if (!aper_base)
|
||||
goto nommu;
|
||||
|
||||
info->aper_base = aper_base;
|
||||
info->aper_size = aper_size >> 20;
|
||||
|
||||
@@ -667,14 +683,14 @@ static __init int init_k8_gatt(struct agp_kern_info *info)
|
||||
|
||||
flush_gart();
|
||||
|
||||
printk(KERN_INFO "PCI-DMA: aperture base @ %x size %u KB\n",
|
||||
pr_info("PCI-DMA: aperture base @ %x size %u KB\n",
|
||||
aper_base, aper_size>>10);
|
||||
|
||||
return 0;
|
||||
|
||||
nommu:
|
||||
/* Should not happen anymore */
|
||||
printk(KERN_WARNING "PCI-DMA: More than 4GB of RAM and no IOMMU\n"
|
||||
pr_warning("PCI-DMA: More than 4GB of RAM and no IOMMU\n"
|
||||
"falling back to iommu=soft.\n");
|
||||
return -1;
|
||||
}
|
||||
@@ -686,14 +702,15 @@ static struct dma_map_ops gart_dma_ops = {
|
||||
.unmap_page = gart_unmap_page,
|
||||
.alloc_coherent = gart_alloc_coherent,
|
||||
.free_coherent = gart_free_coherent,
|
||||
.mapping_error = gart_mapping_error,
|
||||
};
|
||||
|
||||
void gart_iommu_shutdown(void)
|
||||
static void gart_iommu_shutdown(void)
|
||||
{
|
||||
struct pci_dev *dev;
|
||||
int i;
|
||||
|
||||
if (no_agp && (dma_ops != &gart_dma_ops))
|
||||
if (no_agp)
|
||||
return;
|
||||
|
||||
for (i = 0; i < num_k8_northbridges; i++) {
|
||||
@@ -708,7 +725,7 @@ void gart_iommu_shutdown(void)
|
||||
}
|
||||
}
|
||||
|
||||
void __init gart_iommu_init(void)
|
||||
int __init gart_iommu_init(void)
|
||||
{
|
||||
struct agp_kern_info info;
|
||||
unsigned long iommu_start;
|
||||
@@ -718,7 +735,7 @@ void __init gart_iommu_init(void)
|
||||
long i;
|
||||
|
||||
if (cache_k8_northbridges() < 0 || num_k8_northbridges == 0)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
#ifndef CONFIG_AGP_AMD64
|
||||
no_agp = 1;
|
||||
@@ -730,35 +747,28 @@ void __init gart_iommu_init(void)
|
||||
(agp_copy_info(agp_bridge, &info) < 0);
|
||||
#endif
|
||||
|
||||
if (swiotlb)
|
||||
return;
|
||||
|
||||
/* Did we detect a different HW IOMMU? */
|
||||
if (iommu_detected && !gart_iommu_aperture)
|
||||
return;
|
||||
|
||||
if (no_iommu ||
|
||||
(!force_iommu && max_pfn <= MAX_DMA32_PFN) ||
|
||||
!gart_iommu_aperture ||
|
||||
(no_agp && init_k8_gatt(&info) < 0)) {
|
||||
if (max_pfn > MAX_DMA32_PFN) {
|
||||
printk(KERN_WARNING "More than 4GB of memory "
|
||||
"but GART IOMMU not available.\n");
|
||||
printk(KERN_WARNING "falling back to iommu=soft.\n");
|
||||
pr_warning("More than 4GB of memory but GART IOMMU not available.\n");
|
||||
pr_warning("falling back to iommu=soft.\n");
|
||||
}
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* need to map that range */
|
||||
aper_size = info.aper_size << 20;
|
||||
aper_base = info.aper_base;
|
||||
end_pfn = (aper_base>>PAGE_SHIFT) + (aper_size>>PAGE_SHIFT);
|
||||
aper_size = info.aper_size << 20;
|
||||
aper_base = info.aper_base;
|
||||
end_pfn = (aper_base>>PAGE_SHIFT) + (aper_size>>PAGE_SHIFT);
|
||||
|
||||
if (end_pfn > max_low_pfn_mapped) {
|
||||
start_pfn = (aper_base>>PAGE_SHIFT);
|
||||
init_memory_mapping(start_pfn<<PAGE_SHIFT, end_pfn<<PAGE_SHIFT);
|
||||
}
|
||||
|
||||
printk(KERN_INFO "PCI-DMA: using GART IOMMU.\n");
|
||||
pr_info("PCI-DMA: using GART IOMMU.\n");
|
||||
iommu_size = check_iommu_size(info.aper_base, aper_size);
|
||||
iommu_pages = iommu_size >> PAGE_SHIFT;
|
||||
|
||||
@@ -773,8 +783,7 @@ void __init gart_iommu_init(void)
|
||||
|
||||
ret = dma_debug_resize_entries(iommu_pages);
|
||||
if (ret)
|
||||
printk(KERN_DEBUG
|
||||
"PCI-DMA: Cannot trace all the entries\n");
|
||||
pr_debug("PCI-DMA: Cannot trace all the entries\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -784,15 +793,14 @@ void __init gart_iommu_init(void)
|
||||
*/
|
||||
iommu_area_reserve(iommu_gart_bitmap, 0, EMERGENCY_PAGES);
|
||||
|
||||
agp_memory_reserved = iommu_size;
|
||||
printk(KERN_INFO
|
||||
"PCI-DMA: Reserving %luMB of IOMMU area in the AGP aperture\n",
|
||||
pr_info("PCI-DMA: Reserving %luMB of IOMMU area in the AGP aperture\n",
|
||||
iommu_size >> 20);
|
||||
|
||||
iommu_start = aper_size - iommu_size;
|
||||
iommu_bus_base = info.aper_base + iommu_start;
|
||||
bad_dma_address = iommu_bus_base;
|
||||
iommu_gatt_base = agp_gatt_table + (iommu_start>>PAGE_SHIFT);
|
||||
agp_memory_reserved = iommu_size;
|
||||
iommu_start = aper_size - iommu_size;
|
||||
iommu_bus_base = info.aper_base + iommu_start;
|
||||
bad_dma_addr = iommu_bus_base;
|
||||
iommu_gatt_base = agp_gatt_table + (iommu_start>>PAGE_SHIFT);
|
||||
|
||||
/*
|
||||
* Unmap the IOMMU part of the GART. The alias of the page is
|
||||
@@ -814,7 +822,7 @@ void __init gart_iommu_init(void)
|
||||
* the pages as Not-Present:
|
||||
*/
|
||||
wbinvd();
|
||||
|
||||
|
||||
/*
|
||||
* Now all caches are flushed and we can safely enable
|
||||
* GART hardware. Doing it early leaves the possibility
|
||||
@@ -838,6 +846,10 @@ void __init gart_iommu_init(void)
|
||||
|
||||
flush_gart();
|
||||
dma_ops = &gart_dma_ops;
|
||||
x86_platform.iommu_shutdown = gart_iommu_shutdown;
|
||||
swiotlb = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __init gart_parse_options(char *p)
|
||||
@@ -856,7 +868,7 @@ void __init gart_parse_options(char *p)
|
||||
#endif
|
||||
if (isdigit(*p) && get_option(&p, &arg))
|
||||
iommu_size = arg;
|
||||
if (!strncmp(p, "fullflush", 8))
|
||||
if (!strncmp(p, "fullflush", 9))
|
||||
iommu_fullflush = 1;
|
||||
if (!strncmp(p, "nofullflush", 11))
|
||||
iommu_fullflush = 0;
|
||||
|
@@ -33,7 +33,7 @@ static dma_addr_t nommu_map_page(struct device *dev, struct page *page,
|
||||
dma_addr_t bus = page_to_phys(page) + offset;
|
||||
WARN_ON(size == 0);
|
||||
if (!check_addr("map_single", dev, bus, size))
|
||||
return bad_dma_address;
|
||||
return DMA_ERROR_CODE;
|
||||
flush_write_buffers();
|
||||
return bus;
|
||||
}
|
||||
@@ -103,12 +103,3 @@ struct dma_map_ops nommu_dma_ops = {
|
||||
.sync_sg_for_device = nommu_sync_sg_for_device,
|
||||
.is_phys = 1,
|
||||
};
|
||||
|
||||
void __init no_iommu_init(void)
|
||||
{
|
||||
if (dma_ops)
|
||||
return;
|
||||
|
||||
force_iommu = 0; /* no HW IOMMU */
|
||||
dma_ops = &nommu_dma_ops;
|
||||
}
|
||||
|
@@ -42,18 +42,28 @@ static struct dma_map_ops swiotlb_dma_ops = {
|
||||
.dma_supported = NULL,
|
||||
};
|
||||
|
||||
void __init pci_swiotlb_init(void)
|
||||
/*
|
||||
* pci_swiotlb_init - initialize swiotlb if necessary
|
||||
*
|
||||
* This returns non-zero if we are forced to use swiotlb (by the boot
|
||||
* option).
|
||||
*/
|
||||
int __init pci_swiotlb_init(void)
|
||||
{
|
||||
int use_swiotlb = swiotlb | swiotlb_force;
|
||||
|
||||
/* don't initialize swiotlb if iommu=off (no_iommu=1) */
|
||||
#ifdef CONFIG_X86_64
|
||||
if ((!iommu_detected && !no_iommu && max_pfn > MAX_DMA32_PFN))
|
||||
if (!no_iommu && max_pfn > MAX_DMA32_PFN)
|
||||
swiotlb = 1;
|
||||
#endif
|
||||
if (swiotlb_force)
|
||||
swiotlb = 1;
|
||||
|
||||
if (swiotlb) {
|
||||
printk(KERN_INFO "PCI-DMA: Using software bounce buffering for IO (SWIOTLB)\n");
|
||||
swiotlb_init();
|
||||
swiotlb_init(0);
|
||||
dma_ops = &swiotlb_dma_ops;
|
||||
}
|
||||
|
||||
return use_swiotlb;
|
||||
}
|
||||
|
@@ -23,7 +23,7 @@
|
||||
# include <linux/ctype.h>
|
||||
# include <linux/mc146818rtc.h>
|
||||
#else
|
||||
# include <asm/iommu.h>
|
||||
# include <asm/x86_init.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ -622,7 +622,7 @@ void native_machine_shutdown(void)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_X86_64
|
||||
pci_iommu_shutdown();
|
||||
x86_platform.iommu_shutdown();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@@ -14,10 +14,13 @@
|
||||
#include <asm/time.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/tsc.h>
|
||||
#include <asm/iommu.h>
|
||||
|
||||
void __cpuinit x86_init_noop(void) { }
|
||||
void __init x86_init_uint_noop(unsigned int unused) { }
|
||||
void __init x86_init_pgd_noop(pgd_t *unused) { }
|
||||
int __init iommu_init_noop(void) { return 0; }
|
||||
void iommu_shutdown_noop(void) { }
|
||||
|
||||
/*
|
||||
* The platform setup functions are preset with the default functions
|
||||
@@ -62,6 +65,10 @@ struct x86_init_ops x86_init __initdata = {
|
||||
.tsc_pre_init = x86_init_noop,
|
||||
.timer_init = hpet_time_init,
|
||||
},
|
||||
|
||||
.iommu = {
|
||||
.iommu_init = iommu_init_noop,
|
||||
},
|
||||
};
|
||||
|
||||
struct x86_cpuinit_ops x86_cpuinit __cpuinitdata = {
|
||||
@@ -72,4 +79,5 @@ struct x86_platform_ops x86_platform = {
|
||||
.calibrate_tsc = native_calibrate_tsc,
|
||||
.get_wallclock = mach_get_cmos_time,
|
||||
.set_wallclock = mach_set_rtc_mmss,
|
||||
.iommu_shutdown = iommu_shutdown_noop,
|
||||
};
|
||||
|
Reference in New Issue
Block a user