From patchwork Mon Jun 20 11:02:14 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomasz Nowicki X-Patchwork-Id: 9187221 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 891CB6075E for ; Mon, 20 Jun 2016 11:05:59 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 726D4265B9 for ; Mon, 20 Jun 2016 11:05:59 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 6722726861; Mon, 20 Jun 2016 11:05:59 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.1 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_MED, T_DKIM_INVALID autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id BC6F6265B9 for ; Mon, 20 Jun 2016 11:05:58 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1bEx07-00020t-Ss; Mon, 20 Jun 2016 11:04:19 +0000 Received: from mail-lf0-x22c.google.com ([2a00:1450:4010:c07::22c]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1bEwzu-0001pf-DQ for linux-arm-kernel@lists.infradead.org; Mon, 20 Jun 2016 11:04:08 +0000 Received: by mail-lf0-x22c.google.com with SMTP id l188so35200138lfe.2 for ; Mon, 20 Jun 2016 04:03:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=semihalf-com.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=nIl+dKbYZfIEl5x1+O1NbCwXrZ4YSIzyKOSsdFhMxYA=; b=UmMDHRu+kvcuAzMmHV7o8lH8OxlAKgI6nxwnEWa2IMD+imeoUIXmguW33sgr/qX7yf 7zyaAht1+yNyR61QtSuM4s2fKVxzjL7xVLc40lNKpINDoYGg0wnPi6T2piBCg/BdUaTm n91IPqaC866yRaU5S511b2WqQFmRuXvnqUfXCDjqzZ1QJWNKEquHneUJblKicypDMtQ+ OYjGa+kn9VW5gA08iqUfMI/BcWnLqyu+Trs9RCszBId1xSvkLbRJcSbeADuEyVHbP855 0BJo4NRDxJQ35QwZd7YL1TcUu7rkNB47gaWSdpMyo4S2m7xCo+EsoREhEWaj/RQle7ME JmtA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=nIl+dKbYZfIEl5x1+O1NbCwXrZ4YSIzyKOSsdFhMxYA=; b=W8gv6pXPq5SkH/PKeKSOLFIUobNhdQsvFmiGcPX1xIP6GRUaJJBt/p+pzjn8qEuvtU v11R20mOdnvMDfaRaYXWQqHPAgMDyrH1xx7+Ow5R5/wO/mk3d1Ds2FIraZ5fEncOY+by 7T7tJ5qjm8qjK83GRe4mzZDqlkk4NIjzHm8Dv7tvlP0oRaOSLCWVsfch2AwVcLP3y1CM 33A6jjsrJrKfcqeqW2C9dXRuQaLQrk4IXJRwYrsNjXRW4M3c0zYq0hDl6uWNMIx6qSyl pQzmN8QmoOeWNPBdQ49OCdKXkLy5K72eB26uxCFRz9spbNYhwJOKJJ7/V0Au/z0BdqCb N5LA== X-Gm-Message-State: ALyK8tK/6D6MSLhFISEa4IzGoGCKCqO/RLiVVGsWBNEHvMhhjpMdU1rFOyiGDXdpnntFyQ== X-Received: by 10.46.5.138 with SMTP id 132mr3867926ljf.9.1466420624178; Mon, 20 Jun 2016 04:03:44 -0700 (PDT) Received: from tn-HP-4.semihalf.local (31-172-191-173.noc.fibertech.net.pl. [31.172.191.173]) by smtp.gmail.com with ESMTPSA id e10sm44291lbr.36.2016.06.20.04.03.41 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 20 Jun 2016 04:03:43 -0700 (PDT) From: Tomasz Nowicki To: marc.zyngier@arm.com, tglx@linutronix.de, jason@lakedaemon.net, rjw@rjwysocki.net, bhelgaas@google.com, lorenzo.pieralisi@arm.com, robert.richter@caviumnetworks.com, shijie.huang@arm.com, Suravee.Suthikulpanit@amd.com, hanjun.guo@linaro.org Subject: [PATCH V7 1/8] ACPI: I/O Remapping Table (IORT) initial support Date: Mon, 20 Jun 2016 13:02:14 +0200 Message-Id: <1466420541-20101-2-git-send-email-tn@semihalf.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1466420541-20101-1-git-send-email-tn@semihalf.com> References: <1466420541-20101-1-git-send-email-tn@semihalf.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20160620_040406_956984_08BE0C02 X-CRM114-Status: GOOD ( 26.91 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: al.stone@linaro.org, graeme.gregory@linaro.org, Catalin.Marinas@arm.com, will.deacon@arm.com, linux-kernel@vger.kernel.org, okaya@codeaurora.org, linux-acpi@vger.kernel.org, ddaney.cavm@gmail.com, linux-pci@vger.kernel.org, Tomasz Nowicki , mw@semihalf.com, andrea.gallo@linaro.org, linux-arm-kernel@lists.infradead.org MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP IORT shows representation of IO topology for ARM based systems. It describes how various components are connected together on parent-child basis e.g. PCI RC -> SMMU -> ITS. Also see IORT spec. Initial support allows to detect IORT table presence and save its root pointer obtained through acpi_get_table(). The pointer validity depends on acpi_gbl_permanent_mmap because if acpi_gbl_permanent_mmap is not set while using IORT nodes we would dereference unmapped pointers. For the aforementioned reason call iort_table_detect() from acpi_init() which guarantees acpi_gbl_permanent_mmap to be set at that point. Add generic helpers which are helpful for scanning and retrieving information from IORT table content. List of the most important helpers: - iort_find_dev_node() finds IORT node for a given device - iort_node_map_rid() maps device RID and returns IORT node which provides final translation Signed-off-by: Tomasz Nowicki Reviewed-by: Hanjun Guo --- drivers/acpi/Kconfig | 3 + drivers/acpi/Makefile | 1 + drivers/acpi/bus.c | 2 + drivers/acpi/iort.c | 220 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/iort.h | 30 +++++++ 5 files changed, 256 insertions(+) create mode 100644 drivers/acpi/iort.c create mode 100644 include/linux/iort.h diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index b7e2e77..848471f 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -57,6 +57,9 @@ config ACPI_SYSTEM_POWER_STATES_SUPPORT config ACPI_CCA_REQUIRED bool +config IORT_TABLE + bool + config ACPI_DEBUGGER bool "AML debugger interface" select ACPI_DEBUG diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 251ce85..c7c9b29 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -82,6 +82,7 @@ obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o obj-$(CONFIG_ACPI_BGRT) += bgrt.o obj-$(CONFIG_ACPI_CPPC_LIB) += cppc_acpi.o obj-$(CONFIG_ACPI_DEBUGGER_USER) += acpi_dbg.o +obj-$(CONFIG_IORT_TABLE) += iort.o # processor has its own "processor." module_param namespace processor-y := processor_driver.o diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 31e8da6..176c17d 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -33,6 +33,7 @@ #ifdef CONFIG_X86 #include #endif +#include #include #include #include @@ -1118,6 +1119,7 @@ static int __init acpi_init(void) } pci_mmcfg_late_init(); + iort_table_detect(); acpi_scan_init(); acpi_ec_init(); acpi_debugfs_init(); diff --git a/drivers/acpi/iort.c b/drivers/acpi/iort.c new file mode 100644 index 0000000..e342bac --- /dev/null +++ b/drivers/acpi/iort.c @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2016, Semihalf + * Author: Tomasz Nowicki + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * This file implements early detection/parsing of I/O mapping + * reported to OS through firmware via I/O Remapping Table (IORT) + * IORT document number: ARM DEN 0049A + */ + +#define pr_fmt(fmt) "ACPI: IORT: " fmt + +#include +#include +#include + +typedef acpi_status (*iort_find_node_callback) + (struct acpi_iort_node *node, void *context); + +/* Root pointer to the mapped IORT table */ +static struct acpi_table_header *iort_table; + +static struct acpi_iort_node * +iort_scan_node(enum acpi_iort_node_type type, + iort_find_node_callback callback, void *context) +{ + struct acpi_iort_node *iort_node, *iort_end; + struct acpi_table_iort *iort; + int i; + + /* Get the first IORT node */ + iort = (struct acpi_table_iort *)iort_table; + iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort, + iort->node_offset); + iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, + iort_table->length); + + for (i = 0; i < iort->node_count; i++) { + if (WARN_TAINT(iort_node >= iort_end, TAINT_FIRMWARE_WORKAROUND, + "IORT node pointer overflows, bad table!\n")) + return NULL; + + if (iort_node->type == type) { + if (ACPI_SUCCESS(callback(iort_node, context))) + return iort_node; + } + + iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node, + iort_node->length); + } + + return NULL; +} + +static acpi_status +iort_match_node_callback(struct acpi_iort_node *node, void *context) +{ + struct device *dev = context; + + switch (node->type) { + case ACPI_IORT_NODE_NAMED_COMPONENT: { + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_device *adev = to_acpi_device_node(dev->fwnode); + struct acpi_iort_named_component *ncomp; + + if (!adev) + break; + + ncomp = (struct acpi_iort_named_component *)node->node_data; + + if (ACPI_FAILURE(acpi_get_name(adev->handle, + ACPI_FULL_PATHNAME, &buffer))) { + dev_warn(dev, "Can't get device full path name\n"); + break; + } + + if (!strcmp(ncomp->device_name, (char *)buffer.pointer)) + return AE_OK; + + break; + } + case ACPI_IORT_NODE_PCI_ROOT_COMPLEX: { + struct acpi_iort_root_complex *pci_rc; + struct pci_bus *bus; + + bus = to_pci_bus(dev); + pci_rc = (struct acpi_iort_root_complex *)node->node_data; + + /* + * It is assumed that PCI segment numbers maps one-to-one + * with root complexes. Each segment number can represent only + * one root complex. + */ + if (pci_rc->pci_segment_number == pci_domain_nr(bus)) + return AE_OK; + + break; + } + } + + return AE_NOT_FOUND; +} + +static int +iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in, u32 *rid_out) +{ + if (!rid_out) + return -EINVAL; + + /* Single mapping does not care for input id */ + if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) { + if (type == ACPI_IORT_NODE_NAMED_COMPONENT || + type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { + *rid_out = map->output_base; + return 0; + } + + pr_warn(FW_BUG "[map %p] SINGLE MAPPING flag not allowed for node type %d, skipping ID map\n", + map, type); + return -ENXIO; + } + + if (rid_in < map->input_base || + (rid_in > map->input_base + map->id_count)) + return -ENXIO; + + *rid_out = map->output_base + (rid_in - map->input_base); + return 0; +} + +static struct acpi_iort_node * +iort_node_map_rid(struct acpi_iort_node *node, u32 rid_in, + u32 *rid_out, u8 type) +{ + u32 rid = rid_in; + int found = 0; + + /* Climb up ID mapping tree to find specified node type */ + while (node && node->type != type) { + struct acpi_iort_id_mapping *map; + int i; + + if (!node->mapping_offset || !node->mapping_count) { + node = NULL; + break; + } + + map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node, + node->mapping_offset); + + /* Firmware bug! */ + if (!map->output_reference) { + pr_err(FW_BUG "[node %p type %d] ID map has NULL parent reference\n", + node, node->type); + node = NULL; + break; + } + + /* Do the RID translation */ + for (i = 0; i < node->mapping_count; i++, map++) { + if(!iort_id_map(map, node->type, rid, &rid)) { + found = 1; + break; + } + } + + if (!found) { + node = NULL; + break; + } + + node = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, + map->output_reference); + } + + /* Take input RID as output RID in case of any error */ + if (rid_out) + *rid_out = node ? rid : rid_in; + return node; +} + +static struct acpi_iort_node * +iort_find_dev_node(struct device *dev) +{ + struct pci_bus *pbus; + + if (!dev_is_pci(dev)) + return iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, + iort_match_node_callback, dev); + + /* Find a PCI root bus */ + pbus = to_pci_dev(dev)->bus; + while (!pci_is_root_bus(pbus)) + pbus = pbus->parent; + + return iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX, + iort_match_node_callback, &pbus->dev); +} + +void __init iort_table_detect(void) +{ + acpi_status status; + + if (acpi_disabled) + return; + + status = acpi_get_table(ACPI_SIG_IORT, 0, &iort_table); + if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { + const char *msg = acpi_format_exception(status); + pr_err("Failed to get table, %s\n", msg); + } +} diff --git a/include/linux/iort.h b/include/linux/iort.h new file mode 100644 index 0000000..cde6809 --- /dev/null +++ b/include/linux/iort.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2016, Semihalf + * Author: Tomasz Nowicki + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + */ + +#ifndef __IORT_H__ +#define __IORT_H__ + +#include + +#ifdef CONFIG_IORT_TABLE +void iort_table_detect(void); +#else +static inline void iort_table_detect(void) { } +#endif + +#endif /* __IORT_H__ */