110e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S/*
210e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S * Copyright (c) 2006, Intel Corporation.
310e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S *
410e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S * This program is free software; you can redistribute it and/or modify it
510e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S * under the terms and conditions of the GNU General Public License,
610e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S * version 2, as published by the Free Software Foundation.
710e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S *
810e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S * This program is distributed in the hope it will be useful, but WITHOUT
910e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1010e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
1110e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S * more details.
1210e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S *
1310e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S * You should have received a copy of the GNU General Public License along with
1410e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
1510e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S * Place - Suite 330, Boston, MA 02111-1307 USA.
1610e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S *
1798bcef56cadb4da138e2c1a2a0790f372382b236mark gross * Copyright (C) 2006-2008 Intel Corporation
1898bcef56cadb4da138e2c1a2a0790f372382b236mark gross * Author: Ashok Raj <ashok.raj@intel.com>
1998bcef56cadb4da138e2c1a2a0790f372382b236mark gross * Author: Shaohua Li <shaohua.li@intel.com>
2098bcef56cadb4da138e2c1a2a0790f372382b236mark gross * Author: Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
2110e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S *
22e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha * This file implements early detection/parsing of Remapping Devices
2310e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S * reported to OS through BIOS via DMA remapping reporting (DMAR) ACPI
2410e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S * tables.
25e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha *
26e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha * These routines are used by both DMA-remapping and Interrupt-remapping
2710e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S */
2810e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S
2910e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S#include <linux/pci.h>
3010e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S#include <linux/dmar.h>
31387179464257921eb9aa3d15cc3ff194f6945a7cKay, Allen M#include <linux/iova.h>
32387179464257921eb9aa3d15cc3ff194f6945a7cKay, Allen M#include <linux/intel-iommu.h>
33fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha#include <linux/timer.h>
340ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha#include <linux/irq.h>
350ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha#include <linux/interrupt.h>
3669575d388603365f2afbf4166df93152df59b165Shane Wang#include <linux/tboot.h>
37eb27cae8adaa658a0bf31631baa1ce29d8183759Len Brown#include <linux/dmi.h>
385a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
394db77ff3237a88ea74f691dd776e92b2f86a8f3fKonrad Rzeszutek Wilk#include <asm/iommu_table.h>
4010e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S
41a192a9580bcc41692be1f36b77c3b681827f566aLen Brown#define PREFIX "DMAR: "
4210e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S
4310e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S/* No locks are needed as DMA remapping hardware unit
4410e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S * list is constructed at boot time and hotplug of
4510e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S * these units are not supported by the architecture.
4610e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S */
4710e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil SLIST_HEAD(dmar_drhd_units);
4810e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S
4941750d31fc9599fd81763e685a6b7b42d298c4f8Suresh Siddhastruct acpi_table_header * __initdata dmar_tbl;
508e1568f3500287d0b36c9776132cb53a42d5651dYinghai Lustatic acpi_size dmar_tbl_size;
5110e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S
5210e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil Sstatic void __init dmar_register_drhd_unit(struct dmar_drhd_unit *drhd)
5310e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S{
5410e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	/*
5510e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	 * add INCLUDE_ALL at the tail, so scan the list will find it at
5610e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	 * the very end.
5710e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	 */
5810e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	if (drhd->include_all)
5910e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		list_add_tail(&drhd->list, &dmar_drhd_units);
6010e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	else
6110e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		list_add(&drhd->list, &dmar_drhd_units);
6210e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S}
6310e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S
6410e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil Sstatic int __init dmar_parse_one_dev_scope(struct acpi_dmar_device_scope *scope,
6510e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S					   struct pci_dev **dev, u16 segment)
6610e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S{
6710e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	struct pci_bus *bus;
6810e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	struct pci_dev *pdev = NULL;
6910e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	struct acpi_dmar_pci_path *path;
7010e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	int count;
7110e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S
7210e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	bus = pci_find_bus(segment, scope->bus);
7310e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	path = (struct acpi_dmar_pci_path *)(scope + 1);
7410e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	count = (scope->length - sizeof(struct acpi_dmar_device_scope))
7510e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		/ sizeof(struct acpi_dmar_pci_path);
7610e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S
7710e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	while (count) {
7810e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		if (pdev)
7910e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S			pci_dev_put(pdev);
8010e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		/*
8110e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		 * Some BIOSes list non-exist devices in DMAR table, just
8210e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		 * ignore it
8310e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		 */
8410e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		if (!bus) {
8510e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S			printk(KERN_WARNING
8610e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S			PREFIX "Device scope bus [%d] not found\n",
8710e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S			scope->bus);
8810e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S			break;
8910e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		}
9010e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		pdev = pci_get_slot(bus, PCI_DEVFN(path->dev, path->fn));
9110e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		if (!pdev) {
9210e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S			printk(KERN_WARNING PREFIX
9310e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S			"Device scope device [%04x:%02x:%02x.%02x] not found\n",
9410e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S				segment, bus->number, path->dev, path->fn);
9510e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S			break;
9610e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		}
9710e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		path ++;
9810e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		count --;
9910e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		bus = pdev->subordinate;
10010e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	}
10110e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	if (!pdev) {
10210e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		printk(KERN_WARNING PREFIX
10310e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		"Device scope device [%04x:%02x:%02x.%02x] not found\n",
10410e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		segment, scope->bus, path->dev, path->fn);
10510e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		*dev = NULL;
10610e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		return 0;
10710e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	}
10810e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	if ((scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT && \
10910e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S			pdev->subordinate) || (scope->entry_type == \
11010e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S			ACPI_DMAR_SCOPE_TYPE_BRIDGE && !pdev->subordinate)) {
11110e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		pci_dev_put(pdev);
11210e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		printk(KERN_WARNING PREFIX
11310e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S			"Device scope type does not match for %s\n",
11410e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S			 pci_name(pdev));
11510e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		return -EINVAL;
11610e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	}
11710e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	*dev = pdev;
11810e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	return 0;
11910e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S}
12010e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S
121318fe7df9d8456f778451b01913b5d0dc0a25854Suresh Siddhaint __init dmar_parse_dev_scope(void *start, void *end, int *cnt,
122318fe7df9d8456f778451b01913b5d0dc0a25854Suresh Siddha				struct pci_dev ***devices, u16 segment)
12310e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S{
12410e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	struct acpi_dmar_device_scope *scope;
12510e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	void * tmp = start;
12610e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	int index;
12710e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	int ret;
12810e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S
12910e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	*cnt = 0;
13010e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	while (start < end) {
13110e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		scope = start;
13210e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT ||
13310e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		    scope->entry_type == ACPI_DMAR_SCOPE_TYPE_BRIDGE)
13410e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S			(*cnt)++;
1355715f0f9d3814e83e5f2f754d3f7abdfa096a0b9Yinghai Lu		else if (scope->entry_type != ACPI_DMAR_SCOPE_TYPE_IOAPIC) {
13610e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S			printk(KERN_WARNING PREFIX
1375715f0f9d3814e83e5f2f754d3f7abdfa096a0b9Yinghai Lu			       "Unsupported device scope\n");
1385715f0f9d3814e83e5f2f754d3f7abdfa096a0b9Yinghai Lu		}
13910e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		start += scope->length;
14010e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	}
14110e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	if (*cnt == 0)
14210e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		return 0;
14310e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S
14410e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	*devices = kcalloc(*cnt, sizeof(struct pci_dev *), GFP_KERNEL);
14510e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	if (!*devices)
14610e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		return -ENOMEM;
14710e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S
14810e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	start = tmp;
14910e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	index = 0;
15010e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	while (start < end) {
15110e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		scope = start;
15210e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT ||
15310e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		    scope->entry_type == ACPI_DMAR_SCOPE_TYPE_BRIDGE) {
15410e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S			ret = dmar_parse_one_dev_scope(scope,
15510e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S				&(*devices)[index], segment);
15610e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S			if (ret) {
15710e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S				kfree(*devices);
15810e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S				return ret;
15910e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S			}
16010e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S			index ++;
16110e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		}
16210e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		start += scope->length;
16310e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	}
16410e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S
16510e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	return 0;
16610e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S}
16710e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S
16810e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S/**
16910e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S * dmar_parse_one_drhd - parses exactly one DMA remapping hardware definition
17010e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S * structure which uniquely represent one DMA remapping hardware unit
17110e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S * present in the platform
17210e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S */
17310e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil Sstatic int __init
17410e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil Sdmar_parse_one_drhd(struct acpi_dmar_header *header)
17510e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S{
17610e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	struct acpi_dmar_hardware_unit *drhd;
17710e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	struct dmar_drhd_unit *dmaru;
17810e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	int ret = 0;
17910e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S
180e523b38e2f568af58baa13120a994cbf24e6dee0David Woodhouse	drhd = (struct acpi_dmar_hardware_unit *)header;
18110e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	dmaru = kzalloc(sizeof(*dmaru), GFP_KERNEL);
18210e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	if (!dmaru)
18310e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		return -ENOMEM;
18410e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S
1851886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha	dmaru->hdr = header;
18610e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	dmaru->reg_base_addr = drhd->address;
187276dbf997043cbf38f0087624e0f9c51742c8885David Woodhouse	dmaru->segment = drhd->segment;
18810e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	dmaru->include_all = drhd->flags & 0x1; /* BIT0: INCLUDE_ALL */
18910e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S
1901886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha	ret = alloc_iommu(dmaru);
1911886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha	if (ret) {
1921886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha		kfree(dmaru);
1931886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha		return ret;
1941886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha	}
1951886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha	dmar_register_drhd_unit(dmaru);
1961886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha	return 0;
1971886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha}
1981886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha
199f82851a8a480a26611175f064f54e17f5f7b01aeDavid Woodhousestatic int __init dmar_parse_dev(struct dmar_drhd_unit *dmaru)
2001886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha{
2011886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha	struct acpi_dmar_hardware_unit *drhd;
202f82851a8a480a26611175f064f54e17f5f7b01aeDavid Woodhouse	int ret = 0;
2031886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha
2041886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha	drhd = (struct acpi_dmar_hardware_unit *) dmaru->hdr;
2051886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha
2062e824f79240476d57a8589f46232cabf151efe90Yu Zhao	if (dmaru->include_all)
2072e824f79240476d57a8589f46232cabf151efe90Yu Zhao		return 0;
2082e824f79240476d57a8589f46232cabf151efe90Yu Zhao
2092e824f79240476d57a8589f46232cabf151efe90Yu Zhao	ret = dmar_parse_dev_scope((void *)(drhd + 1),
2101886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha				((void *)drhd) + drhd->header.length,
21110e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S				&dmaru->devices_cnt, &dmaru->devices,
21210e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S				drhd->segment);
2131c7d1bcad218808a4f67a4492a5e1d920e85c239Suresh Siddha	if (ret) {
2141886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha		list_del(&dmaru->list);
21510e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		kfree(dmaru);
2161886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha	}
21710e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	return ret;
21810e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S}
21910e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S
220aa697079ee66315c4b9747a5eb3e48487fb1b8beDavid Woodhouse#ifdef CONFIG_ACPI_NUMA
221ee34b32d8c2950f66038c8975747ef9aec855289Suresh Siddhastatic int __init
222ee34b32d8c2950f66038c8975747ef9aec855289Suresh Siddhadmar_parse_one_rhsa(struct acpi_dmar_header *header)
223ee34b32d8c2950f66038c8975747ef9aec855289Suresh Siddha{
224ee34b32d8c2950f66038c8975747ef9aec855289Suresh Siddha	struct acpi_dmar_rhsa *rhsa;
225ee34b32d8c2950f66038c8975747ef9aec855289Suresh Siddha	struct dmar_drhd_unit *drhd;
226ee34b32d8c2950f66038c8975747ef9aec855289Suresh Siddha
227ee34b32d8c2950f66038c8975747ef9aec855289Suresh Siddha	rhsa = (struct acpi_dmar_rhsa *)header;
228aa697079ee66315c4b9747a5eb3e48487fb1b8beDavid Woodhouse	for_each_drhd_unit(drhd) {
229ee34b32d8c2950f66038c8975747ef9aec855289Suresh Siddha		if (drhd->reg_base_addr == rhsa->base_address) {
230ee34b32d8c2950f66038c8975747ef9aec855289Suresh Siddha			int node = acpi_map_pxm_to_node(rhsa->proximity_domain);
231ee34b32d8c2950f66038c8975747ef9aec855289Suresh Siddha
232ee34b32d8c2950f66038c8975747ef9aec855289Suresh Siddha			if (!node_online(node))
233ee34b32d8c2950f66038c8975747ef9aec855289Suresh Siddha				node = -1;
234ee34b32d8c2950f66038c8975747ef9aec855289Suresh Siddha			drhd->iommu->node = node;
235aa697079ee66315c4b9747a5eb3e48487fb1b8beDavid Woodhouse			return 0;
236aa697079ee66315c4b9747a5eb3e48487fb1b8beDavid Woodhouse		}
237ee34b32d8c2950f66038c8975747ef9aec855289Suresh Siddha	}
238fd0c8894893cba722bdea12de25b49f980795d06Ben Hutchings	WARN_TAINT(
239fd0c8894893cba722bdea12de25b49f980795d06Ben Hutchings		1, TAINT_FIRMWARE_WORKAROUND,
240fd0c8894893cba722bdea12de25b49f980795d06Ben Hutchings		"Your BIOS is broken; RHSA refers to non-existent DMAR unit at %llx\n"
241fd0c8894893cba722bdea12de25b49f980795d06Ben Hutchings		"BIOS vendor: %s; Ver: %s; Product Version: %s\n",
242fd0c8894893cba722bdea12de25b49f980795d06Ben Hutchings		drhd->reg_base_addr,
243fd0c8894893cba722bdea12de25b49f980795d06Ben Hutchings		dmi_get_system_info(DMI_BIOS_VENDOR),
244fd0c8894893cba722bdea12de25b49f980795d06Ben Hutchings		dmi_get_system_info(DMI_BIOS_VERSION),
245fd0c8894893cba722bdea12de25b49f980795d06Ben Hutchings		dmi_get_system_info(DMI_PRODUCT_VERSION));
246ee34b32d8c2950f66038c8975747ef9aec855289Suresh Siddha
247aa697079ee66315c4b9747a5eb3e48487fb1b8beDavid Woodhouse	return 0;
248ee34b32d8c2950f66038c8975747ef9aec855289Suresh Siddha}
249aa697079ee66315c4b9747a5eb3e48487fb1b8beDavid Woodhouse#endif
250ee34b32d8c2950f66038c8975747ef9aec855289Suresh Siddha
25110e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil Sstatic void __init
25210e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil Sdmar_table_print_dmar_entry(struct acpi_dmar_header *header)
25310e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S{
25410e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	struct acpi_dmar_hardware_unit *drhd;
25510e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	struct acpi_dmar_reserved_memory *rmrr;
256aa5d2b515b6fca5f8a56eac84f7fa0a68c1ce9b7Yu Zhao	struct acpi_dmar_atsr *atsr;
25717b6097753e926ca546189463070a7e94e7ea9faRoland Dreier	struct acpi_dmar_rhsa *rhsa;
25810e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S
25910e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	switch (header->type) {
26010e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	case ACPI_DMAR_TYPE_HARDWARE_UNIT:
261aa5d2b515b6fca5f8a56eac84f7fa0a68c1ce9b7Yu Zhao		drhd = container_of(header, struct acpi_dmar_hardware_unit,
262aa5d2b515b6fca5f8a56eac84f7fa0a68c1ce9b7Yu Zhao				    header);
26310e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		printk (KERN_INFO PREFIX
264aa5d2b515b6fca5f8a56eac84f7fa0a68c1ce9b7Yu Zhao			"DRHD base: %#016Lx flags: %#x\n",
265aa5d2b515b6fca5f8a56eac84f7fa0a68c1ce9b7Yu Zhao			(unsigned long long)drhd->address, drhd->flags);
26610e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		break;
26710e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	case ACPI_DMAR_TYPE_RESERVED_MEMORY:
268aa5d2b515b6fca5f8a56eac84f7fa0a68c1ce9b7Yu Zhao		rmrr = container_of(header, struct acpi_dmar_reserved_memory,
269aa5d2b515b6fca5f8a56eac84f7fa0a68c1ce9b7Yu Zhao				    header);
27010e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		printk (KERN_INFO PREFIX
271aa5d2b515b6fca5f8a56eac84f7fa0a68c1ce9b7Yu Zhao			"RMRR base: %#016Lx end: %#016Lx\n",
2725b6985ce8ec7127b4d60ad450b64ca8b82748a3bFenghua Yu			(unsigned long long)rmrr->base_address,
2735b6985ce8ec7127b4d60ad450b64ca8b82748a3bFenghua Yu			(unsigned long long)rmrr->end_address);
27410e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		break;
275aa5d2b515b6fca5f8a56eac84f7fa0a68c1ce9b7Yu Zhao	case ACPI_DMAR_TYPE_ATSR:
276aa5d2b515b6fca5f8a56eac84f7fa0a68c1ce9b7Yu Zhao		atsr = container_of(header, struct acpi_dmar_atsr, header);
277aa5d2b515b6fca5f8a56eac84f7fa0a68c1ce9b7Yu Zhao		printk(KERN_INFO PREFIX "ATSR flags: %#x\n", atsr->flags);
278aa5d2b515b6fca5f8a56eac84f7fa0a68c1ce9b7Yu Zhao		break;
27917b6097753e926ca546189463070a7e94e7ea9faRoland Dreier	case ACPI_DMAR_HARDWARE_AFFINITY:
28017b6097753e926ca546189463070a7e94e7ea9faRoland Dreier		rhsa = container_of(header, struct acpi_dmar_rhsa, header);
28117b6097753e926ca546189463070a7e94e7ea9faRoland Dreier		printk(KERN_INFO PREFIX "RHSA base: %#016Lx proximity domain: %#x\n",
28217b6097753e926ca546189463070a7e94e7ea9faRoland Dreier		       (unsigned long long)rhsa->base_address,
28317b6097753e926ca546189463070a7e94e7ea9faRoland Dreier		       rhsa->proximity_domain);
28417b6097753e926ca546189463070a7e94e7ea9faRoland Dreier		break;
28510e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	}
28610e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S}
28710e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S
288f6dd5c3106fb283e37d915eeb33019ef40510f85Yinghai Lu/**
289f6dd5c3106fb283e37d915eeb33019ef40510f85Yinghai Lu * dmar_table_detect - checks to see if the platform supports DMAR devices
290f6dd5c3106fb283e37d915eeb33019ef40510f85Yinghai Lu */
291f6dd5c3106fb283e37d915eeb33019ef40510f85Yinghai Lustatic int __init dmar_table_detect(void)
292f6dd5c3106fb283e37d915eeb33019ef40510f85Yinghai Lu{
293f6dd5c3106fb283e37d915eeb33019ef40510f85Yinghai Lu	acpi_status status = AE_OK;
294f6dd5c3106fb283e37d915eeb33019ef40510f85Yinghai Lu
295f6dd5c3106fb283e37d915eeb33019ef40510f85Yinghai Lu	/* if we could find DMAR table, then there are DMAR devices */
2968e1568f3500287d0b36c9776132cb53a42d5651dYinghai Lu	status = acpi_get_table_with_size(ACPI_SIG_DMAR, 0,
2978e1568f3500287d0b36c9776132cb53a42d5651dYinghai Lu				(struct acpi_table_header **)&dmar_tbl,
2988e1568f3500287d0b36c9776132cb53a42d5651dYinghai Lu				&dmar_tbl_size);
299f6dd5c3106fb283e37d915eeb33019ef40510f85Yinghai Lu
300f6dd5c3106fb283e37d915eeb33019ef40510f85Yinghai Lu	if (ACPI_SUCCESS(status) && !dmar_tbl) {
301f6dd5c3106fb283e37d915eeb33019ef40510f85Yinghai Lu		printk (KERN_WARNING PREFIX "Unable to map DMAR\n");
302f6dd5c3106fb283e37d915eeb33019ef40510f85Yinghai Lu		status = AE_NOT_FOUND;
303f6dd5c3106fb283e37d915eeb33019ef40510f85Yinghai Lu	}
304f6dd5c3106fb283e37d915eeb33019ef40510f85Yinghai Lu
305f6dd5c3106fb283e37d915eeb33019ef40510f85Yinghai Lu	return (ACPI_SUCCESS(status) ? 1 : 0);
306f6dd5c3106fb283e37d915eeb33019ef40510f85Yinghai Lu}
307aaa9d1dd63bf89b62f4ea9f46de376ab1a3fbc6cSuresh Siddha
30810e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S/**
30910e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S * parse_dmar_table - parses the DMA reporting table
31010e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S */
31110e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil Sstatic int __init
31210e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil Sparse_dmar_table(void)
31310e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S{
31410e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	struct acpi_table_dmar *dmar;
31510e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	struct acpi_dmar_header *entry_header;
31610e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	int ret = 0;
31710e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S
318f6dd5c3106fb283e37d915eeb33019ef40510f85Yinghai Lu	/*
319f6dd5c3106fb283e37d915eeb33019ef40510f85Yinghai Lu	 * Do it again, earlier dmar_tbl mapping could be mapped with
320f6dd5c3106fb283e37d915eeb33019ef40510f85Yinghai Lu	 * fixed map.
321f6dd5c3106fb283e37d915eeb33019ef40510f85Yinghai Lu	 */
322f6dd5c3106fb283e37d915eeb33019ef40510f85Yinghai Lu	dmar_table_detect();
323f6dd5c3106fb283e37d915eeb33019ef40510f85Yinghai Lu
324a59b50e995465911ba580df0bd10cf64aa81fc43Joseph Cihula	/*
325a59b50e995465911ba580df0bd10cf64aa81fc43Joseph Cihula	 * ACPI tables may not be DMA protected by tboot, so use DMAR copy
326a59b50e995465911ba580df0bd10cf64aa81fc43Joseph Cihula	 * SINIT saved in SinitMleData in TXT heap (which is DMA protected)
327a59b50e995465911ba580df0bd10cf64aa81fc43Joseph Cihula	 */
328a59b50e995465911ba580df0bd10cf64aa81fc43Joseph Cihula	dmar_tbl = tboot_get_dmar_table(dmar_tbl);
329a59b50e995465911ba580df0bd10cf64aa81fc43Joseph Cihula
33010e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	dmar = (struct acpi_table_dmar *)dmar_tbl;
33110e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	if (!dmar)
33210e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		return -ENODEV;
33310e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S
3345b6985ce8ec7127b4d60ad450b64ca8b82748a3bFenghua Yu	if (dmar->width < PAGE_SHIFT - 1) {
335093f87d279669c74e84530e925e4735c9aae8898Fenghua Yu		printk(KERN_WARNING PREFIX "Invalid DMAR haw\n");
33610e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		return -EINVAL;
33710e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	}
33810e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S
33910e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	printk (KERN_INFO PREFIX "Host address width %d\n",
34010e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		dmar->width + 1);
34110e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S
34210e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	entry_header = (struct acpi_dmar_header *)(dmar + 1);
34310e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	while (((unsigned long)entry_header) <
34410e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S			(((unsigned long)dmar) + dmar_tbl->length)) {
345084eb960e81505680a9963665722d1bfd94af6a7Tony Battersby		/* Avoid looping forever on bad ACPI tables */
346084eb960e81505680a9963665722d1bfd94af6a7Tony Battersby		if (entry_header->length == 0) {
347084eb960e81505680a9963665722d1bfd94af6a7Tony Battersby			printk(KERN_WARNING PREFIX
348084eb960e81505680a9963665722d1bfd94af6a7Tony Battersby				"Invalid 0-length structure\n");
349084eb960e81505680a9963665722d1bfd94af6a7Tony Battersby			ret = -EINVAL;
350084eb960e81505680a9963665722d1bfd94af6a7Tony Battersby			break;
351084eb960e81505680a9963665722d1bfd94af6a7Tony Battersby		}
352084eb960e81505680a9963665722d1bfd94af6a7Tony Battersby
35310e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		dmar_table_print_dmar_entry(entry_header);
35410e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S
35510e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		switch (entry_header->type) {
35610e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		case ACPI_DMAR_TYPE_HARDWARE_UNIT:
35710e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S			ret = dmar_parse_one_drhd(entry_header);
35810e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S			break;
35910e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		case ACPI_DMAR_TYPE_RESERVED_MEMORY:
36010e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S			ret = dmar_parse_one_rmrr(entry_header);
36110e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S			break;
362aa5d2b515b6fca5f8a56eac84f7fa0a68c1ce9b7Yu Zhao		case ACPI_DMAR_TYPE_ATSR:
363aa5d2b515b6fca5f8a56eac84f7fa0a68c1ce9b7Yu Zhao			ret = dmar_parse_one_atsr(entry_header);
364aa5d2b515b6fca5f8a56eac84f7fa0a68c1ce9b7Yu Zhao			break;
36517b6097753e926ca546189463070a7e94e7ea9faRoland Dreier		case ACPI_DMAR_HARDWARE_AFFINITY:
366aa697079ee66315c4b9747a5eb3e48487fb1b8beDavid Woodhouse#ifdef CONFIG_ACPI_NUMA
367ee34b32d8c2950f66038c8975747ef9aec855289Suresh Siddha			ret = dmar_parse_one_rhsa(entry_header);
368aa697079ee66315c4b9747a5eb3e48487fb1b8beDavid Woodhouse#endif
36917b6097753e926ca546189463070a7e94e7ea9faRoland Dreier			break;
37010e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		default:
37110e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S			printk(KERN_WARNING PREFIX
3724de75cf9391b538bbfe7dc0a9782f1ebe8e242adRoland Dreier				"Unknown DMAR structure type %d\n",
3734de75cf9391b538bbfe7dc0a9782f1ebe8e242adRoland Dreier				entry_header->type);
37410e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S			ret = 0; /* for forward compatibility */
37510e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S			break;
37610e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		}
37710e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		if (ret)
37810e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S			break;
37910e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S
38010e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		entry_header = ((void *)entry_header + entry_header->length);
38110e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	}
38210e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	return ret;
38310e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S}
38410e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S
385dda565492776b7dff5f8507298d868745e734aabYinghaistatic int dmar_pci_device_match(struct pci_dev *devices[], int cnt,
386e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha			  struct pci_dev *dev)
387e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha{
388e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha	int index;
389e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha
390e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha	while (dev) {
391e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha		for (index = 0; index < cnt; index++)
392e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha			if (dev == devices[index])
393e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha				return 1;
394e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha
395e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha		/* Check our parent */
396e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha		dev = dev->bus->self;
397e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha	}
398e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha
399e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha	return 0;
400e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha}
401e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha
402e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddhastruct dmar_drhd_unit *
403e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddhadmar_find_matched_drhd_unit(struct pci_dev *dev)
404e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha{
4052e824f79240476d57a8589f46232cabf151efe90Yu Zhao	struct dmar_drhd_unit *dmaru = NULL;
4062e824f79240476d57a8589f46232cabf151efe90Yu Zhao	struct acpi_dmar_hardware_unit *drhd;
4072e824f79240476d57a8589f46232cabf151efe90Yu Zhao
408dda565492776b7dff5f8507298d868745e734aabYinghai	dev = pci_physfn(dev);
409dda565492776b7dff5f8507298d868745e734aabYinghai
4102e824f79240476d57a8589f46232cabf151efe90Yu Zhao	list_for_each_entry(dmaru, &dmar_drhd_units, list) {
4112e824f79240476d57a8589f46232cabf151efe90Yu Zhao		drhd = container_of(dmaru->hdr,
4122e824f79240476d57a8589f46232cabf151efe90Yu Zhao				    struct acpi_dmar_hardware_unit,
4132e824f79240476d57a8589f46232cabf151efe90Yu Zhao				    header);
4142e824f79240476d57a8589f46232cabf151efe90Yu Zhao
4152e824f79240476d57a8589f46232cabf151efe90Yu Zhao		if (dmaru->include_all &&
4162e824f79240476d57a8589f46232cabf151efe90Yu Zhao		    drhd->segment == pci_domain_nr(dev->bus))
4172e824f79240476d57a8589f46232cabf151efe90Yu Zhao			return dmaru;
418e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha
4192e824f79240476d57a8589f46232cabf151efe90Yu Zhao		if (dmar_pci_device_match(dmaru->devices,
4202e824f79240476d57a8589f46232cabf151efe90Yu Zhao					  dmaru->devices_cnt, dev))
4212e824f79240476d57a8589f46232cabf151efe90Yu Zhao			return dmaru;
422e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha	}
423e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha
424e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha	return NULL;
425e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha}
426e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha
4271886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddhaint __init dmar_dev_scope_init(void)
4281886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha{
429c2c7286ac6d996a8ffc8d391d782ba35570b1236Suresh Siddha	static int dmar_dev_scope_initialized;
43004e2ea67069e285404192a35c24dfe7c53b9c61fSuresh Siddha	struct dmar_drhd_unit *drhd, *drhd_n;
4311886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha	int ret = -ENODEV;
4321886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha
433c2c7286ac6d996a8ffc8d391d782ba35570b1236Suresh Siddha	if (dmar_dev_scope_initialized)
434c2c7286ac6d996a8ffc8d391d782ba35570b1236Suresh Siddha		return dmar_dev_scope_initialized;
435c2c7286ac6d996a8ffc8d391d782ba35570b1236Suresh Siddha
436318fe7df9d8456f778451b01913b5d0dc0a25854Suresh Siddha	if (list_empty(&dmar_drhd_units))
437318fe7df9d8456f778451b01913b5d0dc0a25854Suresh Siddha		goto fail;
438318fe7df9d8456f778451b01913b5d0dc0a25854Suresh Siddha
43904e2ea67069e285404192a35c24dfe7c53b9c61fSuresh Siddha	list_for_each_entry_safe(drhd, drhd_n, &dmar_drhd_units, list) {
4401886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha		ret = dmar_parse_dev(drhd);
4411886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha		if (ret)
442c2c7286ac6d996a8ffc8d391d782ba35570b1236Suresh Siddha			goto fail;
4431886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha	}
4441886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha
445318fe7df9d8456f778451b01913b5d0dc0a25854Suresh Siddha	ret = dmar_parse_rmrr_atsr_dev();
446318fe7df9d8456f778451b01913b5d0dc0a25854Suresh Siddha	if (ret)
447318fe7df9d8456f778451b01913b5d0dc0a25854Suresh Siddha		goto fail;
4481886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha
449c2c7286ac6d996a8ffc8d391d782ba35570b1236Suresh Siddha	dmar_dev_scope_initialized = 1;
450c2c7286ac6d996a8ffc8d391d782ba35570b1236Suresh Siddha	return 0;
451c2c7286ac6d996a8ffc8d391d782ba35570b1236Suresh Siddha
452c2c7286ac6d996a8ffc8d391d782ba35570b1236Suresh Siddhafail:
453c2c7286ac6d996a8ffc8d391d782ba35570b1236Suresh Siddha	dmar_dev_scope_initialized = ret;
4541886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha	return ret;
4551886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha}
4561886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha
45710e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S
45810e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil Sint __init dmar_table_init(void)
45910e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S{
4601886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha	static int dmar_table_initialized;
461093f87d279669c74e84530e925e4735c9aae8898Fenghua Yu	int ret;
462093f87d279669c74e84530e925e4735c9aae8898Fenghua Yu
4631886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha	if (dmar_table_initialized)
4641886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha		return 0;
4651886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha
4661886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha	dmar_table_initialized = 1;
4671886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha
468093f87d279669c74e84530e925e4735c9aae8898Fenghua Yu	ret = parse_dmar_table();
469093f87d279669c74e84530e925e4735c9aae8898Fenghua Yu	if (ret) {
4701886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha		if (ret != -ENODEV)
4711886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha			printk(KERN_INFO PREFIX "parse DMAR table failure.\n");
472093f87d279669c74e84530e925e4735c9aae8898Fenghua Yu		return ret;
473093f87d279669c74e84530e925e4735c9aae8898Fenghua Yu	}
474093f87d279669c74e84530e925e4735c9aae8898Fenghua Yu
47510e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	if (list_empty(&dmar_drhd_units)) {
47610e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		printk(KERN_INFO PREFIX "No DMAR devices found\n");
47710e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S		return -ENODEV;
47810e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	}
479093f87d279669c74e84530e925e4735c9aae8898Fenghua Yu
48010e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S	return 0;
48110e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S}
48210e5247f40f3bf7508a0ed2848c9cae37bddf4bcKeshavamurthy, Anil S
4833a8663ee6171e1e61f5c139ed65aa0a769380f00Ben Hutchingsstatic void warn_invalid_dmar(u64 addr, const char *message)
4843a8663ee6171e1e61f5c139ed65aa0a769380f00Ben Hutchings{
485fd0c8894893cba722bdea12de25b49f980795d06Ben Hutchings	WARN_TAINT_ONCE(
486fd0c8894893cba722bdea12de25b49f980795d06Ben Hutchings		1, TAINT_FIRMWARE_WORKAROUND,
487fd0c8894893cba722bdea12de25b49f980795d06Ben Hutchings		"Your BIOS is broken; DMAR reported at address %llx%s!\n"
488fd0c8894893cba722bdea12de25b49f980795d06Ben Hutchings		"BIOS vendor: %s; Ver: %s; Product Version: %s\n",
489fd0c8894893cba722bdea12de25b49f980795d06Ben Hutchings		addr, message,
490fd0c8894893cba722bdea12de25b49f980795d06Ben Hutchings		dmi_get_system_info(DMI_BIOS_VENDOR),
491fd0c8894893cba722bdea12de25b49f980795d06Ben Hutchings		dmi_get_system_info(DMI_BIOS_VERSION),
492fd0c8894893cba722bdea12de25b49f980795d06Ben Hutchings		dmi_get_system_info(DMI_PRODUCT_VERSION));
4933a8663ee6171e1e61f5c139ed65aa0a769380f00Ben Hutchings}
4946ecbf01c7ce4c0f4c3bdfa0e64ac6258328fda6cDavid Woodhouse
49586cf898e1d0fca245173980e3897580db38569a8David Woodhouseint __init check_zero_address(void)
49686cf898e1d0fca245173980e3897580db38569a8David Woodhouse{
49786cf898e1d0fca245173980e3897580db38569a8David Woodhouse	struct acpi_table_dmar *dmar;
49886cf898e1d0fca245173980e3897580db38569a8David Woodhouse	struct acpi_dmar_header *entry_header;
49986cf898e1d0fca245173980e3897580db38569a8David Woodhouse	struct acpi_dmar_hardware_unit *drhd;
50086cf898e1d0fca245173980e3897580db38569a8David Woodhouse
50186cf898e1d0fca245173980e3897580db38569a8David Woodhouse	dmar = (struct acpi_table_dmar *)dmar_tbl;
50286cf898e1d0fca245173980e3897580db38569a8David Woodhouse	entry_header = (struct acpi_dmar_header *)(dmar + 1);
50386cf898e1d0fca245173980e3897580db38569a8David Woodhouse
50486cf898e1d0fca245173980e3897580db38569a8David Woodhouse	while (((unsigned long)entry_header) <
50586cf898e1d0fca245173980e3897580db38569a8David Woodhouse			(((unsigned long)dmar) + dmar_tbl->length)) {
50686cf898e1d0fca245173980e3897580db38569a8David Woodhouse		/* Avoid looping forever on bad ACPI tables */
50786cf898e1d0fca245173980e3897580db38569a8David Woodhouse		if (entry_header->length == 0) {
50886cf898e1d0fca245173980e3897580db38569a8David Woodhouse			printk(KERN_WARNING PREFIX
50986cf898e1d0fca245173980e3897580db38569a8David Woodhouse				"Invalid 0-length structure\n");
51086cf898e1d0fca245173980e3897580db38569a8David Woodhouse			return 0;
51186cf898e1d0fca245173980e3897580db38569a8David Woodhouse		}
51286cf898e1d0fca245173980e3897580db38569a8David Woodhouse
51386cf898e1d0fca245173980e3897580db38569a8David Woodhouse		if (entry_header->type == ACPI_DMAR_TYPE_HARDWARE_UNIT) {
5142c99220810c1c79322034628b993573b088ff2daChris Wright			void __iomem *addr;
5152c99220810c1c79322034628b993573b088ff2daChris Wright			u64 cap, ecap;
5162c99220810c1c79322034628b993573b088ff2daChris Wright
51786cf898e1d0fca245173980e3897580db38569a8David Woodhouse			drhd = (void *)entry_header;
51886cf898e1d0fca245173980e3897580db38569a8David Woodhouse			if (!drhd->address) {
5193a8663ee6171e1e61f5c139ed65aa0a769380f00Ben Hutchings				warn_invalid_dmar(0, "");
5202c99220810c1c79322034628b993573b088ff2daChris Wright				goto failed;
5212c99220810c1c79322034628b993573b088ff2daChris Wright			}
5222c99220810c1c79322034628b993573b088ff2daChris Wright
5232c99220810c1c79322034628b993573b088ff2daChris Wright			addr = early_ioremap(drhd->address, VTD_PAGE_SIZE);
5242c99220810c1c79322034628b993573b088ff2daChris Wright			if (!addr ) {
5252c99220810c1c79322034628b993573b088ff2daChris Wright				printk("IOMMU: can't validate: %llx\n", drhd->address);
5262c99220810c1c79322034628b993573b088ff2daChris Wright				goto failed;
5272c99220810c1c79322034628b993573b088ff2daChris Wright			}
5282c99220810c1c79322034628b993573b088ff2daChris Wright			cap = dmar_readq(addr + DMAR_CAP_REG);
5292c99220810c1c79322034628b993573b088ff2daChris Wright			ecap = dmar_readq(addr + DMAR_ECAP_REG);
5302c99220810c1c79322034628b993573b088ff2daChris Wright			early_iounmap(addr, VTD_PAGE_SIZE);
5312c99220810c1c79322034628b993573b088ff2daChris Wright			if (cap == (uint64_t)-1 && ecap == (uint64_t)-1) {
5323a8663ee6171e1e61f5c139ed65aa0a769380f00Ben Hutchings				warn_invalid_dmar(drhd->address,
5333a8663ee6171e1e61f5c139ed65aa0a769380f00Ben Hutchings						  " returns all ones");
5342c99220810c1c79322034628b993573b088ff2daChris Wright				goto failed;
53586cf898e1d0fca245173980e3897580db38569a8David Woodhouse			}
53686cf898e1d0fca245173980e3897580db38569a8David Woodhouse		}
53786cf898e1d0fca245173980e3897580db38569a8David Woodhouse
53886cf898e1d0fca245173980e3897580db38569a8David Woodhouse		entry_header = ((void *)entry_header + entry_header->length);
53986cf898e1d0fca245173980e3897580db38569a8David Woodhouse	}
54086cf898e1d0fca245173980e3897580db38569a8David Woodhouse	return 1;
5412c99220810c1c79322034628b993573b088ff2daChris Wright
5422c99220810c1c79322034628b993573b088ff2daChris Wrightfailed:
5432c99220810c1c79322034628b993573b088ff2daChris Wright	return 0;
54486cf898e1d0fca245173980e3897580db38569a8David Woodhouse}
54586cf898e1d0fca245173980e3897580db38569a8David Woodhouse
546480125ba49ba62be93beea37770f266846e077abKonrad Rzeszutek Wilkint __init detect_intel_iommu(void)
5472ae21010694e56461a63bfc80e960090ce0a5ed9Suresh Siddha{
5482ae21010694e56461a63bfc80e960090ce0a5ed9Suresh Siddha	int ret;
5492ae21010694e56461a63bfc80e960090ce0a5ed9Suresh Siddha
550f6dd5c3106fb283e37d915eeb33019ef40510f85Yinghai Lu	ret = dmar_table_detect();
55186cf898e1d0fca245173980e3897580db38569a8David Woodhouse	if (ret)
55286cf898e1d0fca245173980e3897580db38569a8David Woodhouse		ret = check_zero_address();
5532ae21010694e56461a63bfc80e960090ce0a5ed9Suresh Siddha	{
5541cb11583a6c4ceda7426eb36f7bf0419da8dfbc2Suresh Siddha		struct acpi_table_dmar *dmar;
555b3a530e4e734cab7043a3ab7d135f539042443a7Jan Kiszka
5561cb11583a6c4ceda7426eb36f7bf0419da8dfbc2Suresh Siddha		dmar = (struct acpi_table_dmar *) dmar_tbl;
557f5d1b97bcdd8ac195f48c645bffcb88bcea533e4Suresh Siddha
558f5d1b97bcdd8ac195f48c645bffcb88bcea533e4Suresh Siddha		if (ret && intr_remapping_enabled && cpu_has_x2apic &&
559f5d1b97bcdd8ac195f48c645bffcb88bcea533e4Suresh Siddha		    dmar->flags & 0x1)
5601cb11583a6c4ceda7426eb36f7bf0419da8dfbc2Suresh Siddha			printk(KERN_INFO
561f5d1b97bcdd8ac195f48c645bffcb88bcea533e4Suresh Siddha			       "Queued invalidation will be enabled to support x2apic and Intr-remapping.\n");
562f5d1b97bcdd8ac195f48c645bffcb88bcea533e4Suresh Siddha
56311bd04f6f35621193311c32e0721142b073a7794Linus Torvalds		if (ret && !no_iommu && !iommu_detected && !dmar_disabled) {
5642ae21010694e56461a63bfc80e960090ce0a5ed9Suresh Siddha			iommu_detected = 1;
5655d990b627537e59a3a2f039ff588a4750e9c1a6aChris Wright			/* Make sure ACS will be enabled */
5665d990b627537e59a3a2f039ff588a4750e9c1a6aChris Wright			pci_request_acs();
5675d990b627537e59a3a2f039ff588a4750e9c1a6aChris Wright		}
568f5d1b97bcdd8ac195f48c645bffcb88bcea533e4Suresh Siddha
5699d5ce73a64be2be8112147a3e0b551ad9cd1247bFUJITA Tomonori#ifdef CONFIG_X86
5709d5ce73a64be2be8112147a3e0b551ad9cd1247bFUJITA Tomonori		if (ret)
5719d5ce73a64be2be8112147a3e0b551ad9cd1247bFUJITA Tomonori			x86_init.iommu.iommu_init = intel_iommu_init;
5729d5ce73a64be2be8112147a3e0b551ad9cd1247bFUJITA Tomonori#endif
573cacd4213d8ffed83676f38d5d8e93c673e0f1af7Youquan Song	}
5748e1568f3500287d0b36c9776132cb53a42d5651dYinghai Lu	early_acpi_os_unmap_memory(dmar_tbl, dmar_tbl_size);
575f6dd5c3106fb283e37d915eeb33019ef40510f85Yinghai Lu	dmar_tbl = NULL;
576480125ba49ba62be93beea37770f266846e077abKonrad Rzeszutek Wilk
5774db77ff3237a88ea74f691dd776e92b2f86a8f3fKonrad Rzeszutek Wilk	return ret ? 1 : -ENODEV;
5782ae21010694e56461a63bfc80e960090ce0a5ed9Suresh Siddha}
5792ae21010694e56461a63bfc80e960090ce0a5ed9Suresh Siddha
5802ae21010694e56461a63bfc80e960090ce0a5ed9Suresh Siddha
5811886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddhaint alloc_iommu(struct dmar_drhd_unit *drhd)
582e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha{
583c42d9f32443397aed2d37d37df161392e6a5862fSuresh Siddha	struct intel_iommu *iommu;
584e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha	int map_size;
585e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha	u32 ver;
586c42d9f32443397aed2d37d37df161392e6a5862fSuresh Siddha	static int iommu_allocated = 0;
58743f7392ba9e2585bf34f21399b1ed78692b5d437Joerg Roedel	int agaw = 0;
5884ed0d3e6c64cfd9ba4ceb2099b10d1cf8ece4320Fenghua Yu	int msagaw = 0;
589c42d9f32443397aed2d37d37df161392e6a5862fSuresh Siddha
5906ecbf01c7ce4c0f4c3bdfa0e64ac6258328fda6cDavid Woodhouse	if (!drhd->reg_base_addr) {
5913a8663ee6171e1e61f5c139ed65aa0a769380f00Ben Hutchings		warn_invalid_dmar(0, "");
5926ecbf01c7ce4c0f4c3bdfa0e64ac6258328fda6cDavid Woodhouse		return -EINVAL;
5936ecbf01c7ce4c0f4c3bdfa0e64ac6258328fda6cDavid Woodhouse	}
5946ecbf01c7ce4c0f4c3bdfa0e64ac6258328fda6cDavid Woodhouse
595c42d9f32443397aed2d37d37df161392e6a5862fSuresh Siddha	iommu = kzalloc(sizeof(*iommu), GFP_KERNEL);
596c42d9f32443397aed2d37d37df161392e6a5862fSuresh Siddha	if (!iommu)
5971886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha		return -ENOMEM;
598c42d9f32443397aed2d37d37df161392e6a5862fSuresh Siddha
599c42d9f32443397aed2d37d37df161392e6a5862fSuresh Siddha	iommu->seq_id = iommu_allocated++;
6009d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha	sprintf (iommu->name, "dmar%d", iommu->seq_id);
601e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha
6025b6985ce8ec7127b4d60ad450b64ca8b82748a3bFenghua Yu	iommu->reg = ioremap(drhd->reg_base_addr, VTD_PAGE_SIZE);
603e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha	if (!iommu->reg) {
604e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha		printk(KERN_ERR "IOMMU: can't map the region\n");
605e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha		goto error;
606e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha	}
607e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha	iommu->cap = dmar_readq(iommu->reg + DMAR_CAP_REG);
608e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha	iommu->ecap = dmar_readq(iommu->reg + DMAR_ECAP_REG);
609e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha
6100815565adfe3f4c369110c57d8ffe83caefeed68David Woodhouse	if (iommu->cap == (uint64_t)-1 && iommu->ecap == (uint64_t)-1) {
6113a8663ee6171e1e61f5c139ed65aa0a769380f00Ben Hutchings		warn_invalid_dmar(drhd->reg_base_addr, " returns all ones");
6120815565adfe3f4c369110c57d8ffe83caefeed68David Woodhouse		goto err_unmap;
6130815565adfe3f4c369110c57d8ffe83caefeed68David Woodhouse	}
6140815565adfe3f4c369110c57d8ffe83caefeed68David Woodhouse
6151b5736839ae13dadc5947940144f95dd0f4a4a8cWeidong Han	agaw = iommu_calculate_agaw(iommu);
6161b5736839ae13dadc5947940144f95dd0f4a4a8cWeidong Han	if (agaw < 0) {
6171b5736839ae13dadc5947940144f95dd0f4a4a8cWeidong Han		printk(KERN_ERR
6184ed0d3e6c64cfd9ba4ceb2099b10d1cf8ece4320Fenghua Yu		       "Cannot get a valid agaw for iommu (seq_id = %d)\n",
6194ed0d3e6c64cfd9ba4ceb2099b10d1cf8ece4320Fenghua Yu		       iommu->seq_id);
6200815565adfe3f4c369110c57d8ffe83caefeed68David Woodhouse		goto err_unmap;
6214ed0d3e6c64cfd9ba4ceb2099b10d1cf8ece4320Fenghua Yu	}
6224ed0d3e6c64cfd9ba4ceb2099b10d1cf8ece4320Fenghua Yu	msagaw = iommu_calculate_max_sagaw(iommu);
6234ed0d3e6c64cfd9ba4ceb2099b10d1cf8ece4320Fenghua Yu	if (msagaw < 0) {
6244ed0d3e6c64cfd9ba4ceb2099b10d1cf8ece4320Fenghua Yu		printk(KERN_ERR
6254ed0d3e6c64cfd9ba4ceb2099b10d1cf8ece4320Fenghua Yu			"Cannot get a valid max agaw for iommu (seq_id = %d)\n",
6261b5736839ae13dadc5947940144f95dd0f4a4a8cWeidong Han			iommu->seq_id);
6270815565adfe3f4c369110c57d8ffe83caefeed68David Woodhouse		goto err_unmap;
6281b5736839ae13dadc5947940144f95dd0f4a4a8cWeidong Han	}
6291b5736839ae13dadc5947940144f95dd0f4a4a8cWeidong Han	iommu->agaw = agaw;
6304ed0d3e6c64cfd9ba4ceb2099b10d1cf8ece4320Fenghua Yu	iommu->msagaw = msagaw;
6311b5736839ae13dadc5947940144f95dd0f4a4a8cWeidong Han
632ee34b32d8c2950f66038c8975747ef9aec855289Suresh Siddha	iommu->node = -1;
633ee34b32d8c2950f66038c8975747ef9aec855289Suresh Siddha
634e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha	/* the registers might be more than one page */
635e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha	map_size = max_t(int, ecap_max_iotlb_offset(iommu->ecap),
636e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha		cap_max_fault_reg_offset(iommu->cap));
6375b6985ce8ec7127b4d60ad450b64ca8b82748a3bFenghua Yu	map_size = VTD_PAGE_ALIGN(map_size);
6385b6985ce8ec7127b4d60ad450b64ca8b82748a3bFenghua Yu	if (map_size > VTD_PAGE_SIZE) {
639e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha		iounmap(iommu->reg);
640e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha		iommu->reg = ioremap(drhd->reg_base_addr, map_size);
641e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha		if (!iommu->reg) {
642e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha			printk(KERN_ERR "IOMMU: can't map the region\n");
643e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha			goto error;
644e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha		}
645e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha	}
646e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha
647e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha	ver = readl(iommu->reg + DMAR_VER_REG);
648680a7524622356f5476e8fad2fe32b2b68b432c0Yinghai Lu	pr_info("IOMMU %d: reg_base_addr %llx ver %d:%d cap %llx ecap %llx\n",
649680a7524622356f5476e8fad2fe32b2b68b432c0Yinghai Lu		iommu->seq_id,
6505b6985ce8ec7127b4d60ad450b64ca8b82748a3bFenghua Yu		(unsigned long long)drhd->reg_base_addr,
6515b6985ce8ec7127b4d60ad450b64ca8b82748a3bFenghua Yu		DMAR_VER_MAJOR(ver), DMAR_VER_MINOR(ver),
6525b6985ce8ec7127b4d60ad450b64ca8b82748a3bFenghua Yu		(unsigned long long)iommu->cap,
6535b6985ce8ec7127b4d60ad450b64ca8b82748a3bFenghua Yu		(unsigned long long)iommu->ecap);
654e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha
6551f5b3c3fd2d73d6b30e9ef6dcbf131a791d5cbbdThomas Gleixner	raw_spin_lock_init(&iommu->register_lock);
656e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha
657e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha	drhd->iommu = iommu;
6581886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha	return 0;
6590815565adfe3f4c369110c57d8ffe83caefeed68David Woodhouse
6600815565adfe3f4c369110c57d8ffe83caefeed68David Woodhouse err_unmap:
6610815565adfe3f4c369110c57d8ffe83caefeed68David Woodhouse	iounmap(iommu->reg);
6620815565adfe3f4c369110c57d8ffe83caefeed68David Woodhouse error:
663e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha	kfree(iommu);
6641886e8a90a580f3ad343f2065c84c1b9e1dac9efSuresh Siddha	return -1;
665e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha}
666e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha
667e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddhavoid free_iommu(struct intel_iommu *iommu)
668e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha{
669e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha	if (!iommu)
670e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha		return;
671e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha
672e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha	free_dmar_iommu(iommu);
673e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha
674e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha	if (iommu->reg)
675e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha		iounmap(iommu->reg);
676e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha	kfree(iommu);
677e61d98d8dad0048619bb138b0ff996422ffae53bSuresh Siddha}
678fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha
679fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha/*
680fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha * Reclaim all the submitted descriptors which have completed its work.
681fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha */
682fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddhastatic inline void reclaim_free_desc(struct q_inval *qi)
683fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha{
6846ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao	while (qi->desc_status[qi->free_tail] == QI_DONE ||
6856ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao	       qi->desc_status[qi->free_tail] == QI_ABORT) {
686fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha		qi->desc_status[qi->free_tail] = QI_FREE;
687fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha		qi->free_tail = (qi->free_tail + 1) % QI_LENGTH;
688fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha		qi->free_cnt++;
689fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	}
690fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha}
691fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha
692704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhaostatic int qi_check_fault(struct intel_iommu *iommu, int index)
693704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhao{
694704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhao	u32 fault;
6956ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao	int head, tail;
696704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhao	struct q_inval *qi = iommu->qi;
697704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhao	int wait_index = (index + 1) % QI_LENGTH;
698704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhao
6996ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao	if (qi->desc_status[wait_index] == QI_ABORT)
7006ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao		return -EAGAIN;
7016ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao
702704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhao	fault = readl(iommu->reg + DMAR_FSTS_REG);
703704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhao
704704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhao	/*
705704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhao	 * If IQE happens, the head points to the descriptor associated
706704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhao	 * with the error. No new descriptors are fetched until the IQE
707704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhao	 * is cleared.
708704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhao	 */
709704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhao	if (fault & DMA_FSTS_IQE) {
710704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhao		head = readl(iommu->reg + DMAR_IQH_REG);
7116ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao		if ((head >> DMAR_IQ_SHIFT) == index) {
7126ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao			printk(KERN_ERR "VT-d detected invalid descriptor: "
7136ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao				"low=%llx, high=%llx\n",
7146ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao				(unsigned long long)qi->desc[index].low,
7156ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao				(unsigned long long)qi->desc[index].high);
716704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhao			memcpy(&qi->desc[index], &qi->desc[wait_index],
717704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhao					sizeof(struct qi_desc));
718704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhao			__iommu_flush_cache(iommu, &qi->desc[index],
719704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhao					sizeof(struct qi_desc));
720704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhao			writel(DMA_FSTS_IQE, iommu->reg + DMAR_FSTS_REG);
721704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhao			return -EINVAL;
722704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhao		}
723704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhao	}
724704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhao
7256ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao	/*
7266ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao	 * If ITE happens, all pending wait_desc commands are aborted.
7276ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao	 * No new descriptors are fetched until the ITE is cleared.
7286ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao	 */
7296ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao	if (fault & DMA_FSTS_ITE) {
7306ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao		head = readl(iommu->reg + DMAR_IQH_REG);
7316ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao		head = ((head >> DMAR_IQ_SHIFT) - 1 + QI_LENGTH) % QI_LENGTH;
7326ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao		head |= 1;
7336ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao		tail = readl(iommu->reg + DMAR_IQT_REG);
7346ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao		tail = ((tail >> DMAR_IQ_SHIFT) - 1 + QI_LENGTH) % QI_LENGTH;
7356ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao
7366ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao		writel(DMA_FSTS_ITE, iommu->reg + DMAR_FSTS_REG);
7376ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao
7386ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao		do {
7396ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao			if (qi->desc_status[head] == QI_IN_USE)
7406ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao				qi->desc_status[head] = QI_ABORT;
7416ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao			head = (head - 2 + QI_LENGTH) % QI_LENGTH;
7426ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao		} while (head != tail);
7436ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao
7446ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao		if (qi->desc_status[wait_index] == QI_ABORT)
7456ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao			return -EAGAIN;
7466ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao	}
7476ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao
7486ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao	if (fault & DMA_FSTS_ICE)
7496ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao		writel(DMA_FSTS_ICE, iommu->reg + DMAR_FSTS_REG);
7506ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao
751704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhao	return 0;
752704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhao}
753704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhao
754fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha/*
755fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha * Submit the queued invalidation descriptor to the remapping
756fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha * hardware unit and wait for its completion.
757fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha */
758704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhaoint qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu)
759fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha{
7606ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao	int rc;
761fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	struct q_inval *qi = iommu->qi;
762fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	struct qi_desc *hw, wait_desc;
763fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	int wait_index, index;
764fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	unsigned long flags;
765fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha
766fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	if (!qi)
767704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhao		return 0;
768fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha
769fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	hw = qi->desc;
770fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha
7716ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhaorestart:
7726ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao	rc = 0;
7736ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao
7743b8f40481513a7b6123def5a02db4cff96ae2198Thomas Gleixner	raw_spin_lock_irqsave(&qi->q_lock, flags);
775fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	while (qi->free_cnt < 3) {
7763b8f40481513a7b6123def5a02db4cff96ae2198Thomas Gleixner		raw_spin_unlock_irqrestore(&qi->q_lock, flags);
777fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha		cpu_relax();
7783b8f40481513a7b6123def5a02db4cff96ae2198Thomas Gleixner		raw_spin_lock_irqsave(&qi->q_lock, flags);
779fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	}
780fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha
781fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	index = qi->free_head;
782fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	wait_index = (index + 1) % QI_LENGTH;
783fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha
784fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	qi->desc_status[index] = qi->desc_status[wait_index] = QI_IN_USE;
785fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha
786fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	hw[index] = *desc;
787fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha
788704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhao	wait_desc.low = QI_IWD_STATUS_DATA(QI_DONE) |
789704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhao			QI_IWD_STATUS_WRITE | QI_IWD_TYPE;
790fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	wait_desc.high = virt_to_phys(&qi->desc_status[wait_index]);
791fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha
792fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	hw[wait_index] = wait_desc;
793fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha
794fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	__iommu_flush_cache(iommu, &hw[index], sizeof(struct qi_desc));
795fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	__iommu_flush_cache(iommu, &hw[wait_index], sizeof(struct qi_desc));
796fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha
797fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	qi->free_head = (qi->free_head + 2) % QI_LENGTH;
798fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	qi->free_cnt -= 2;
799fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha
800fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	/*
801fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	 * update the HW tail register indicating the presence of
802fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	 * new descriptors.
803fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	 */
8046ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao	writel(qi->free_head << DMAR_IQ_SHIFT, iommu->reg + DMAR_IQT_REG);
805fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha
806fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	while (qi->desc_status[wait_index] != QI_DONE) {
807f05810c9962bba3e809f07619bda1bfdebbfbfb9Suresh Siddha		/*
808f05810c9962bba3e809f07619bda1bfdebbfbfb9Suresh Siddha		 * We will leave the interrupts disabled, to prevent interrupt
809f05810c9962bba3e809f07619bda1bfdebbfbfb9Suresh Siddha		 * context to queue another cmd while a cmd is already submitted
810f05810c9962bba3e809f07619bda1bfdebbfbfb9Suresh Siddha		 * and waiting for completion on this cpu. This is to avoid
811f05810c9962bba3e809f07619bda1bfdebbfbfb9Suresh Siddha		 * a deadlock where the interrupt context can wait indefinitely
812f05810c9962bba3e809f07619bda1bfdebbfbfb9Suresh Siddha		 * for free slots in the queue.
813f05810c9962bba3e809f07619bda1bfdebbfbfb9Suresh Siddha		 */
814704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhao		rc = qi_check_fault(iommu, index);
815704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhao		if (rc)
8166ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao			break;
817704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhao
8183b8f40481513a7b6123def5a02db4cff96ae2198Thomas Gleixner		raw_spin_unlock(&qi->q_lock);
819fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha		cpu_relax();
8203b8f40481513a7b6123def5a02db4cff96ae2198Thomas Gleixner		raw_spin_lock(&qi->q_lock);
821fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	}
8226ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao
8236ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao	qi->desc_status[index] = QI_DONE;
824fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha
825fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	reclaim_free_desc(qi);
8263b8f40481513a7b6123def5a02db4cff96ae2198Thomas Gleixner	raw_spin_unlock_irqrestore(&qi->q_lock, flags);
827704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhao
8286ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao	if (rc == -EAGAIN)
8296ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao		goto restart;
8306ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao
831704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhao	return rc;
832fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha}
833fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha
834fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha/*
835fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha * Flush the global interrupt entry cache.
836fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha */
837fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddhavoid qi_global_iec(struct intel_iommu *iommu)
838fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha{
839fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	struct qi_desc desc;
840fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha
841fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	desc.low = QI_IEC_TYPE;
842fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	desc.high = 0;
843fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha
844704126ad81b8cb7d3d70adb9ecb143f4d3fb38afYu Zhao	/* should never fail */
845fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	qi_submit_sync(&desc, iommu);
846fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha}
847fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha
8484c25a2c1b90bf785fc2e2f0f0c74a80b3e070d39David Woodhousevoid qi_flush_context(struct intel_iommu *iommu, u16 did, u16 sid, u8 fm,
8494c25a2c1b90bf785fc2e2f0f0c74a80b3e070d39David Woodhouse		      u64 type)
8503481f21097cb560392c411377893b5109fbde557Youquan Song{
8513481f21097cb560392c411377893b5109fbde557Youquan Song	struct qi_desc desc;
8523481f21097cb560392c411377893b5109fbde557Youquan Song
8533481f21097cb560392c411377893b5109fbde557Youquan Song	desc.low = QI_CC_FM(fm) | QI_CC_SID(sid) | QI_CC_DID(did)
8543481f21097cb560392c411377893b5109fbde557Youquan Song			| QI_CC_GRAN(type) | QI_CC_TYPE;
8553481f21097cb560392c411377893b5109fbde557Youquan Song	desc.high = 0;
8563481f21097cb560392c411377893b5109fbde557Youquan Song
8574c25a2c1b90bf785fc2e2f0f0c74a80b3e070d39David Woodhouse	qi_submit_sync(&desc, iommu);
8583481f21097cb560392c411377893b5109fbde557Youquan Song}
8593481f21097cb560392c411377893b5109fbde557Youquan Song
8601f0ef2aa18802a8ce7eb5a5164aaaf4d59073801David Woodhousevoid qi_flush_iotlb(struct intel_iommu *iommu, u16 did, u64 addr,
8611f0ef2aa18802a8ce7eb5a5164aaaf4d59073801David Woodhouse		    unsigned int size_order, u64 type)
8623481f21097cb560392c411377893b5109fbde557Youquan Song{
8633481f21097cb560392c411377893b5109fbde557Youquan Song	u8 dw = 0, dr = 0;
8643481f21097cb560392c411377893b5109fbde557Youquan Song
8653481f21097cb560392c411377893b5109fbde557Youquan Song	struct qi_desc desc;
8663481f21097cb560392c411377893b5109fbde557Youquan Song	int ih = 0;
8673481f21097cb560392c411377893b5109fbde557Youquan Song
8683481f21097cb560392c411377893b5109fbde557Youquan Song	if (cap_write_drain(iommu->cap))
8693481f21097cb560392c411377893b5109fbde557Youquan Song		dw = 1;
8703481f21097cb560392c411377893b5109fbde557Youquan Song
8713481f21097cb560392c411377893b5109fbde557Youquan Song	if (cap_read_drain(iommu->cap))
8723481f21097cb560392c411377893b5109fbde557Youquan Song		dr = 1;
8733481f21097cb560392c411377893b5109fbde557Youquan Song
8743481f21097cb560392c411377893b5109fbde557Youquan Song	desc.low = QI_IOTLB_DID(did) | QI_IOTLB_DR(dr) | QI_IOTLB_DW(dw)
8753481f21097cb560392c411377893b5109fbde557Youquan Song		| QI_IOTLB_GRAN(type) | QI_IOTLB_TYPE;
8763481f21097cb560392c411377893b5109fbde557Youquan Song	desc.high = QI_IOTLB_ADDR(addr) | QI_IOTLB_IH(ih)
8773481f21097cb560392c411377893b5109fbde557Youquan Song		| QI_IOTLB_AM(size_order);
8783481f21097cb560392c411377893b5109fbde557Youquan Song
8791f0ef2aa18802a8ce7eb5a5164aaaf4d59073801David Woodhouse	qi_submit_sync(&desc, iommu);
8803481f21097cb560392c411377893b5109fbde557Youquan Song}
8813481f21097cb560392c411377893b5109fbde557Youquan Song
8826ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhaovoid qi_flush_dev_iotlb(struct intel_iommu *iommu, u16 sid, u16 qdep,
8836ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao			u64 addr, unsigned mask)
8846ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao{
8856ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao	struct qi_desc desc;
8866ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao
8876ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao	if (mask) {
8886ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao		BUG_ON(addr & ((1 << (VTD_PAGE_SHIFT + mask)) - 1));
8896ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao		addr |= (1 << (VTD_PAGE_SHIFT + mask - 1)) - 1;
8906ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao		desc.high = QI_DEV_IOTLB_ADDR(addr) | QI_DEV_IOTLB_SIZE;
8916ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao	} else
8926ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao		desc.high = QI_DEV_IOTLB_ADDR(addr);
8936ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao
8946ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao	if (qdep >= QI_DEV_IOTLB_MAX_INVS)
8956ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao		qdep = 0;
8966ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao
8976ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao	desc.low = QI_DEV_IOTLB_SID(sid) | QI_DEV_IOTLB_QDEP(qdep) |
8986ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao		   QI_DIOTLB_TYPE;
8996ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao
9006ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao	qi_submit_sync(&desc, iommu);
9016ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao}
9026ba6c3a4cacfd68bf970e3e04e2ff0d66fa0f695Yu Zhao
903fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha/*
904eba67e5da6e971993b2899d2cdf459ce77d3dbc5Suresh Siddha * Disable Queued Invalidation interface.
905eba67e5da6e971993b2899d2cdf459ce77d3dbc5Suresh Siddha */
906eba67e5da6e971993b2899d2cdf459ce77d3dbc5Suresh Siddhavoid dmar_disable_qi(struct intel_iommu *iommu)
907eba67e5da6e971993b2899d2cdf459ce77d3dbc5Suresh Siddha{
908eba67e5da6e971993b2899d2cdf459ce77d3dbc5Suresh Siddha	unsigned long flags;
909eba67e5da6e971993b2899d2cdf459ce77d3dbc5Suresh Siddha	u32 sts;
910eba67e5da6e971993b2899d2cdf459ce77d3dbc5Suresh Siddha	cycles_t start_time = get_cycles();
911eba67e5da6e971993b2899d2cdf459ce77d3dbc5Suresh Siddha
912eba67e5da6e971993b2899d2cdf459ce77d3dbc5Suresh Siddha	if (!ecap_qis(iommu->ecap))
913eba67e5da6e971993b2899d2cdf459ce77d3dbc5Suresh Siddha		return;
914eba67e5da6e971993b2899d2cdf459ce77d3dbc5Suresh Siddha
9151f5b3c3fd2d73d6b30e9ef6dcbf131a791d5cbbdThomas Gleixner	raw_spin_lock_irqsave(&iommu->register_lock, flags);
916eba67e5da6e971993b2899d2cdf459ce77d3dbc5Suresh Siddha
917eba67e5da6e971993b2899d2cdf459ce77d3dbc5Suresh Siddha	sts =  dmar_readq(iommu->reg + DMAR_GSTS_REG);
918eba67e5da6e971993b2899d2cdf459ce77d3dbc5Suresh Siddha	if (!(sts & DMA_GSTS_QIES))
919eba67e5da6e971993b2899d2cdf459ce77d3dbc5Suresh Siddha		goto end;
920eba67e5da6e971993b2899d2cdf459ce77d3dbc5Suresh Siddha
921eba67e5da6e971993b2899d2cdf459ce77d3dbc5Suresh Siddha	/*
922eba67e5da6e971993b2899d2cdf459ce77d3dbc5Suresh Siddha	 * Give a chance to HW to complete the pending invalidation requests.
923eba67e5da6e971993b2899d2cdf459ce77d3dbc5Suresh Siddha	 */
924eba67e5da6e971993b2899d2cdf459ce77d3dbc5Suresh Siddha	while ((readl(iommu->reg + DMAR_IQT_REG) !=
925eba67e5da6e971993b2899d2cdf459ce77d3dbc5Suresh Siddha		readl(iommu->reg + DMAR_IQH_REG)) &&
926eba67e5da6e971993b2899d2cdf459ce77d3dbc5Suresh Siddha		(DMAR_OPERATION_TIMEOUT > (get_cycles() - start_time)))
927eba67e5da6e971993b2899d2cdf459ce77d3dbc5Suresh Siddha		cpu_relax();
928eba67e5da6e971993b2899d2cdf459ce77d3dbc5Suresh Siddha
929eba67e5da6e971993b2899d2cdf459ce77d3dbc5Suresh Siddha	iommu->gcmd &= ~DMA_GCMD_QIE;
930eba67e5da6e971993b2899d2cdf459ce77d3dbc5Suresh Siddha	writel(iommu->gcmd, iommu->reg + DMAR_GCMD_REG);
931eba67e5da6e971993b2899d2cdf459ce77d3dbc5Suresh Siddha
932eba67e5da6e971993b2899d2cdf459ce77d3dbc5Suresh Siddha	IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, readl,
933eba67e5da6e971993b2899d2cdf459ce77d3dbc5Suresh Siddha		      !(sts & DMA_GSTS_QIES), sts);
934eba67e5da6e971993b2899d2cdf459ce77d3dbc5Suresh Siddhaend:
9351f5b3c3fd2d73d6b30e9ef6dcbf131a791d5cbbdThomas Gleixner	raw_spin_unlock_irqrestore(&iommu->register_lock, flags);
936eba67e5da6e971993b2899d2cdf459ce77d3dbc5Suresh Siddha}
937eba67e5da6e971993b2899d2cdf459ce77d3dbc5Suresh Siddha
938eba67e5da6e971993b2899d2cdf459ce77d3dbc5Suresh Siddha/*
939eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu * Enable queued invalidation.
940eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu */
941eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yustatic void __dmar_enable_qi(struct intel_iommu *iommu)
942eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu{
943c416daa98a584596df21ee2c26fac6579ee58f57David Woodhouse	u32 sts;
944eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu	unsigned long flags;
945eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu	struct q_inval *qi = iommu->qi;
946eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu
947eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu	qi->free_head = qi->free_tail = 0;
948eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu	qi->free_cnt = QI_LENGTH;
949eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu
9501f5b3c3fd2d73d6b30e9ef6dcbf131a791d5cbbdThomas Gleixner	raw_spin_lock_irqsave(&iommu->register_lock, flags);
951eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu
952eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu	/* write zero to the tail reg */
953eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu	writel(0, iommu->reg + DMAR_IQT_REG);
954eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu
955eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu	dmar_writeq(iommu->reg + DMAR_IQA_REG, virt_to_phys(qi->desc));
956eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu
957eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu	iommu->gcmd |= DMA_GCMD_QIE;
958c416daa98a584596df21ee2c26fac6579ee58f57David Woodhouse	writel(iommu->gcmd, iommu->reg + DMAR_GCMD_REG);
959eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu
960eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu	/* Make sure hardware complete it */
961eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu	IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, readl, (sts & DMA_GSTS_QIES), sts);
962eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu
9631f5b3c3fd2d73d6b30e9ef6dcbf131a791d5cbbdThomas Gleixner	raw_spin_unlock_irqrestore(&iommu->register_lock, flags);
964eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu}
965eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu
966eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu/*
967fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha * Enable Queued Invalidation interface. This is a must to support
968fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha * interrupt-remapping. Also used by DMA-remapping, which replaces
969fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha * register based IOTLB invalidation.
970fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha */
971fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddhaint dmar_enable_qi(struct intel_iommu *iommu)
972fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha{
973fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	struct q_inval *qi;
974751cafe3aeceb9ff887c97237f6daaf596c9e547Suresh Siddha	struct page *desc_page;
975fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha
976fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	if (!ecap_qis(iommu->ecap))
977fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha		return -ENOENT;
978fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha
979fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	/*
980fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	 * queued invalidation is already setup and enabled.
981fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	 */
982fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	if (iommu->qi)
983fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha		return 0;
984fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha
985fa4b57cc045d6134b9862b2873f9c8ba9ed53ffeSuresh Siddha	iommu->qi = kmalloc(sizeof(*qi), GFP_ATOMIC);
986fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	if (!iommu->qi)
987fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha		return -ENOMEM;
988fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha
989fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	qi = iommu->qi;
990fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha
991751cafe3aeceb9ff887c97237f6daaf596c9e547Suresh Siddha
992751cafe3aeceb9ff887c97237f6daaf596c9e547Suresh Siddha	desc_page = alloc_pages_node(iommu->node, GFP_ATOMIC | __GFP_ZERO, 0);
993751cafe3aeceb9ff887c97237f6daaf596c9e547Suresh Siddha	if (!desc_page) {
994fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha		kfree(qi);
995fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha		iommu->qi = 0;
996fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha		return -ENOMEM;
997fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	}
998fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha
999751cafe3aeceb9ff887c97237f6daaf596c9e547Suresh Siddha	qi->desc = page_address(desc_page);
1000751cafe3aeceb9ff887c97237f6daaf596c9e547Suresh Siddha
1001fa4b57cc045d6134b9862b2873f9c8ba9ed53ffeSuresh Siddha	qi->desc_status = kmalloc(QI_LENGTH * sizeof(int), GFP_ATOMIC);
1002fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	if (!qi->desc_status) {
1003fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha		free_page((unsigned long) qi->desc);
1004fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha		kfree(qi);
1005fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha		iommu->qi = 0;
1006fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha		return -ENOMEM;
1007fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	}
1008fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha
1009fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	qi->free_head = qi->free_tail = 0;
1010fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	qi->free_cnt = QI_LENGTH;
1011fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha
10123b8f40481513a7b6123def5a02db4cff96ae2198Thomas Gleixner	raw_spin_lock_init(&qi->q_lock);
1013fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha
1014eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu	__dmar_enable_qi(iommu);
1015fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha
1016fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha	return 0;
1017fe962e90cb17a8426e144dee970e77ed789d98eeSuresh Siddha}
10180ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha
10190ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha/* iommu interrupt handling. Most stuff are MSI-like. */
10200ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha
10219d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddhaenum faulttype {
10229d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha	DMA_REMAP,
10239d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha	INTR_REMAP,
10249d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha	UNKNOWN,
10259d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha};
10269d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha
10279d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddhastatic const char *dma_remap_fault_reasons[] =
10280ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha{
10290ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	"Software",
10300ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	"Present bit in root entry is clear",
10310ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	"Present bit in context entry is clear",
10320ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	"Invalid context entry",
10330ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	"Access beyond MGAW",
10340ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	"PTE Write access is not set",
10350ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	"PTE Read access is not set",
10360ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	"Next page table ptr is invalid",
10370ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	"Root table address invalid",
10380ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	"Context table ptr is invalid",
10390ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	"non-zero reserved fields in RTP",
10400ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	"non-zero reserved fields in CTP",
10410ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	"non-zero reserved fields in PTE",
10420ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha};
10439d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha
10449d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddhastatic const char *intr_remap_fault_reasons[] =
10459d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha{
10469d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha	"Detected reserved fields in the decoded interrupt-remapped request",
10479d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha	"Interrupt index exceeded the interrupt-remapping table size",
10489d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha	"Present field in the IRTE entry is clear",
10499d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha	"Error accessing interrupt-remapping table pointed by IRTA_REG",
10509d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha	"Detected reserved fields in the IRTE entry",
10519d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha	"Blocked a compatibility format interrupt request",
10529d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha	"Blocked an interrupt request due to source-id verification failure",
10539d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha};
10549d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha
10550ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha#define MAX_FAULT_REASON_IDX 	(ARRAY_SIZE(fault_reason_strings) - 1)
10560ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha
10579d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddhaconst char *dmar_get_fault_reason(u8 fault_reason, int *fault_type)
10580ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha{
1059dcff6a45745b9df7f2c9255fc4998be8d52ea67cDan Carpenter	if (fault_reason >= 0x20 && (fault_reason - 0x20 <
1060dcff6a45745b9df7f2c9255fc4998be8d52ea67cDan Carpenter					ARRAY_SIZE(intr_remap_fault_reasons))) {
10619d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha		*fault_type = INTR_REMAP;
10629d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha		return intr_remap_fault_reasons[fault_reason - 0x20];
10639d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha	} else if (fault_reason < ARRAY_SIZE(dma_remap_fault_reasons)) {
10649d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha		*fault_type = DMA_REMAP;
10659d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha		return dma_remap_fault_reasons[fault_reason];
10669d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha	} else {
10679d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha		*fault_type = UNKNOWN;
10680ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha		return "Unknown";
10699d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha	}
10700ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha}
10710ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha
10725c2837fbaa609e615ef9a1c58a4cd26ce90be35bThomas Gleixnervoid dmar_msi_unmask(struct irq_data *data)
10730ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha{
1074dced35aeb0367dda2636ee9ee914bda14510dcc9Thomas Gleixner	struct intel_iommu *iommu = irq_data_get_irq_handler_data(data);
10750ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	unsigned long flag;
10760ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha
10770ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	/* unmask it */
10781f5b3c3fd2d73d6b30e9ef6dcbf131a791d5cbbdThomas Gleixner	raw_spin_lock_irqsave(&iommu->register_lock, flag);
10790ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	writel(0, iommu->reg + DMAR_FECTL_REG);
10800ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	/* Read a reg to force flush the post write */
10810ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	readl(iommu->reg + DMAR_FECTL_REG);
10821f5b3c3fd2d73d6b30e9ef6dcbf131a791d5cbbdThomas Gleixner	raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
10830ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha}
10840ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha
10855c2837fbaa609e615ef9a1c58a4cd26ce90be35bThomas Gleixnervoid dmar_msi_mask(struct irq_data *data)
10860ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha{
10870ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	unsigned long flag;
1088dced35aeb0367dda2636ee9ee914bda14510dcc9Thomas Gleixner	struct intel_iommu *iommu = irq_data_get_irq_handler_data(data);
10890ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha
10900ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	/* mask it */
10911f5b3c3fd2d73d6b30e9ef6dcbf131a791d5cbbdThomas Gleixner	raw_spin_lock_irqsave(&iommu->register_lock, flag);
10920ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	writel(DMA_FECTL_IM, iommu->reg + DMAR_FECTL_REG);
10930ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	/* Read a reg to force flush the post write */
10940ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	readl(iommu->reg + DMAR_FECTL_REG);
10951f5b3c3fd2d73d6b30e9ef6dcbf131a791d5cbbdThomas Gleixner	raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
10960ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha}
10970ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha
10980ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddhavoid dmar_msi_write(int irq, struct msi_msg *msg)
10990ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha{
1100dced35aeb0367dda2636ee9ee914bda14510dcc9Thomas Gleixner	struct intel_iommu *iommu = irq_get_handler_data(irq);
11010ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	unsigned long flag;
11020ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha
11031f5b3c3fd2d73d6b30e9ef6dcbf131a791d5cbbdThomas Gleixner	raw_spin_lock_irqsave(&iommu->register_lock, flag);
11040ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	writel(msg->data, iommu->reg + DMAR_FEDATA_REG);
11050ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	writel(msg->address_lo, iommu->reg + DMAR_FEADDR_REG);
11060ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	writel(msg->address_hi, iommu->reg + DMAR_FEUADDR_REG);
11071f5b3c3fd2d73d6b30e9ef6dcbf131a791d5cbbdThomas Gleixner	raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
11080ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha}
11090ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha
11100ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddhavoid dmar_msi_read(int irq, struct msi_msg *msg)
11110ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha{
1112dced35aeb0367dda2636ee9ee914bda14510dcc9Thomas Gleixner	struct intel_iommu *iommu = irq_get_handler_data(irq);
11130ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	unsigned long flag;
11140ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha
11151f5b3c3fd2d73d6b30e9ef6dcbf131a791d5cbbdThomas Gleixner	raw_spin_lock_irqsave(&iommu->register_lock, flag);
11160ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	msg->data = readl(iommu->reg + DMAR_FEDATA_REG);
11170ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	msg->address_lo = readl(iommu->reg + DMAR_FEADDR_REG);
11180ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	msg->address_hi = readl(iommu->reg + DMAR_FEUADDR_REG);
11191f5b3c3fd2d73d6b30e9ef6dcbf131a791d5cbbdThomas Gleixner	raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
11200ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha}
11210ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha
11220ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddhastatic int dmar_fault_do_one(struct intel_iommu *iommu, int type,
11230ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha		u8 fault_reason, u16 source_id, unsigned long long addr)
11240ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha{
11250ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	const char *reason;
11269d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha	int fault_type;
11270ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha
11289d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha	reason = dmar_get_fault_reason(fault_reason, &fault_type);
11290ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha
11309d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha	if (fault_type == INTR_REMAP)
11319d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha		printk(KERN_ERR "INTR-REMAP: Request device [[%02x:%02x.%d] "
11329d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha		       "fault index %llx\n"
11339d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha			"INTR-REMAP:[fault reason %02d] %s\n",
11349d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha			(source_id >> 8), PCI_SLOT(source_id & 0xFF),
11359d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha			PCI_FUNC(source_id & 0xFF), addr >> 48,
11369d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha			fault_reason, reason);
11379d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha	else
11389d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha		printk(KERN_ERR
11399d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha		       "DMAR:[%s] Request device [%02x:%02x.%d] "
11409d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha		       "fault addr %llx \n"
11419d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha		       "DMAR:[fault reason %02d] %s\n",
11429d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha		       (type ? "DMA Read" : "DMA Write"),
11439d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha		       (source_id >> 8), PCI_SLOT(source_id & 0xFF),
11449d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha		       PCI_FUNC(source_id & 0xFF), addr, fault_reason, reason);
11450ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	return 0;
11460ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha}
11470ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha
11480ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha#define PRIMARY_FAULT_REG_LEN (16)
11491531a6a6b81a4e6f9eec9a5608758a6ea14b96e0Suresh Siddhairqreturn_t dmar_fault(int irq, void *dev_id)
11500ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha{
11510ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	struct intel_iommu *iommu = dev_id;
11520ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	int reg, fault_index;
11530ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	u32 fault_status;
11540ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	unsigned long flag;
11550ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha
11561f5b3c3fd2d73d6b30e9ef6dcbf131a791d5cbbdThomas Gleixner	raw_spin_lock_irqsave(&iommu->register_lock, flag);
11570ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	fault_status = readl(iommu->reg + DMAR_FSTS_REG);
11589d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha	if (fault_status)
11599d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha		printk(KERN_ERR "DRHD: handling fault status reg %x\n",
11609d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha		       fault_status);
11610ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha
11620ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	/* TBD: ignore advanced fault log currently */
11630ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	if (!(fault_status & DMA_FSTS_PPF))
11649d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha		goto clear_rest;
11650ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha
11660ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	fault_index = dma_fsts_fault_record_index(fault_status);
11670ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	reg = cap_fault_reg_offset(iommu->cap);
11680ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	while (1) {
11690ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha		u8 fault_reason;
11700ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha		u16 source_id;
11710ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha		u64 guest_addr;
11720ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha		int type;
11730ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha		u32 data;
11740ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha
11750ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha		/* highest 32 bits */
11760ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha		data = readl(iommu->reg + reg +
11770ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha				fault_index * PRIMARY_FAULT_REG_LEN + 12);
11780ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha		if (!(data & DMA_FRCD_F))
11790ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha			break;
11800ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha
11810ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha		fault_reason = dma_frcd_fault_reason(data);
11820ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha		type = dma_frcd_type(data);
11830ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha
11840ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha		data = readl(iommu->reg + reg +
11850ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha				fault_index * PRIMARY_FAULT_REG_LEN + 8);
11860ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha		source_id = dma_frcd_source_id(data);
11870ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha
11880ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha		guest_addr = dmar_readq(iommu->reg + reg +
11890ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha				fault_index * PRIMARY_FAULT_REG_LEN);
11900ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha		guest_addr = dma_frcd_page_addr(guest_addr);
11910ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha		/* clear the fault */
11920ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha		writel(DMA_FRCD_F, iommu->reg + reg +
11930ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha			fault_index * PRIMARY_FAULT_REG_LEN + 12);
11940ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha
11951f5b3c3fd2d73d6b30e9ef6dcbf131a791d5cbbdThomas Gleixner		raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
11960ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha
11970ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha		dmar_fault_do_one(iommu, type, fault_reason,
11980ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha				source_id, guest_addr);
11990ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha
12000ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha		fault_index++;
12018211a7b5857914058c52ae977c96463e419b37abTroy Heber		if (fault_index >= cap_num_fault_regs(iommu->cap))
12020ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha			fault_index = 0;
12031f5b3c3fd2d73d6b30e9ef6dcbf131a791d5cbbdThomas Gleixner		raw_spin_lock_irqsave(&iommu->register_lock, flag);
12040ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	}
12059d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddhaclear_rest:
12069d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha	/* clear all the other faults */
12070ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	fault_status = readl(iommu->reg + DMAR_FSTS_REG);
12089d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha	writel(fault_status, iommu->reg + DMAR_FSTS_REG);
12090ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha
12101f5b3c3fd2d73d6b30e9ef6dcbf131a791d5cbbdThomas Gleixner	raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
12110ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	return IRQ_HANDLED;
12120ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha}
12130ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha
12140ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddhaint dmar_set_interrupt(struct intel_iommu *iommu)
12150ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha{
12160ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	int irq, ret;
12170ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha
12189d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha	/*
12199d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha	 * Check if the fault interrupt is already initialized.
12209d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha	 */
12219d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha	if (iommu->irq)
12229d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha		return 0;
12239d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha
12240ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	irq = create_irq();
12250ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	if (!irq) {
12260ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha		printk(KERN_ERR "IOMMU: no free vectors\n");
12270ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha		return -EINVAL;
12280ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	}
12290ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha
1230dced35aeb0367dda2636ee9ee914bda14510dcc9Thomas Gleixner	irq_set_handler_data(irq, iommu);
12310ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	iommu->irq = irq;
12320ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha
12330ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	ret = arch_setup_dmar_msi(irq);
12340ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	if (ret) {
1235dced35aeb0367dda2636ee9ee914bda14510dcc9Thomas Gleixner		irq_set_handler_data(irq, NULL);
12360ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha		iommu->irq = 0;
12370ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha		destroy_irq(irq);
1238dd7264355a203c3456dbba04db471947d3b55e7eChris Wright		return ret;
12390ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	}
12400ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha
1241477694e71113fd0694b6bb0bcc2d006b8ac62691Thomas Gleixner	ret = request_irq(irq, dmar_fault, IRQF_NO_THREAD, iommu->name, iommu);
12420ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	if (ret)
12430ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha		printk(KERN_ERR "IOMMU: can't request irq\n");
12440ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha	return ret;
12450ac2491f57af5644f88383d28809760902d6f4d7Suresh Siddha}
12469d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha
12479d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddhaint __init enable_drhd_fault_handling(void)
12489d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha{
12499d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha	struct dmar_drhd_unit *drhd;
12509d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha
12519d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha	/*
12529d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha	 * Enable fault control interrupt.
12539d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha	 */
12549d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha	for_each_drhd_unit(drhd) {
12559d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha		int ret;
12569d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha		struct intel_iommu *iommu = drhd->iommu;
12579d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha		ret = dmar_set_interrupt(iommu);
12589d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha
12599d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha		if (ret) {
12609d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha			printk(KERN_ERR "DRHD %Lx: failed to enable fault, "
12619d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha			       " interrupt, ret %d\n",
12629d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha			       (unsigned long long)drhd->reg_base_addr, ret);
12639d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha			return -1;
12649d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha		}
12657f99d946e71e71d484b7543b49e990508e70d0c0Suresh Siddha
12667f99d946e71e71d484b7543b49e990508e70d0c0Suresh Siddha		/*
12677f99d946e71e71d484b7543b49e990508e70d0c0Suresh Siddha		 * Clear any previous faults.
12687f99d946e71e71d484b7543b49e990508e70d0c0Suresh Siddha		 */
12697f99d946e71e71d484b7543b49e990508e70d0c0Suresh Siddha		dmar_fault(iommu->irq, iommu);
12709d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha	}
12719d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha
12729d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha	return 0;
12739d783ba042771284fb4ee5013c3d94220755ae7fSuresh Siddha}
1274eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu
1275eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu/*
1276eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu * Re-enable Queued Invalidation interface.
1277eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu */
1278eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yuint dmar_reenable_qi(struct intel_iommu *iommu)
1279eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu{
1280eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu	if (!ecap_qis(iommu->ecap))
1281eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu		return -ENOENT;
1282eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu
1283eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu	if (!iommu->qi)
1284eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu		return -ENOENT;
1285eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu
1286eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu	/*
1287eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu	 * First disable queued invalidation.
1288eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu	 */
1289eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu	dmar_disable_qi(iommu);
1290eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu	/*
1291eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu	 * Then enable queued invalidation again. Since there is no pending
1292eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu	 * invalidation requests now, it's safe to re-enable queued
1293eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu	 * invalidation.
1294eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu	 */
1295eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu	__dmar_enable_qi(iommu);
1296eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu
1297eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu	return 0;
1298eb4a52bc660ea835482c582eaaf4893742cbd160Fenghua Yu}
1299074835f0143b83845af5044af2739c52c9f53808Youquan Song
1300074835f0143b83845af5044af2739c52c9f53808Youquan Song/*
1301074835f0143b83845af5044af2739c52c9f53808Youquan Song * Check interrupt remapping support in DMAR table description.
1302074835f0143b83845af5044af2739c52c9f53808Youquan Song */
13030b8973a81876d90f916507ac40d1381068dc986aTony Luckint __init dmar_ir_support(void)
1304074835f0143b83845af5044af2739c52c9f53808Youquan Song{
1305074835f0143b83845af5044af2739c52c9f53808Youquan Song	struct acpi_table_dmar *dmar;
1306074835f0143b83845af5044af2739c52c9f53808Youquan Song	dmar = (struct acpi_table_dmar *)dmar_tbl;
13074f506e07e0a3dff34427cece255a8f390a78d5a0Arnaud Patard	if (!dmar)
13084f506e07e0a3dff34427cece255a8f390a78d5a0Arnaud Patard		return 0;
1309074835f0143b83845af5044af2739c52c9f53808Youquan Song	return dmar->flags & 0x1;
1310074835f0143b83845af5044af2739c52c9f53808Youquan Song}
13114db77ff3237a88ea74f691dd776e92b2f86a8f3fKonrad Rzeszutek WilkIOMMU_INIT_POST(detect_intel_iommu);
1312