svn commit: r255603 - in projects/pseries: conf powerpc/pseries
Nathan Whitehorn
nwhitehorn at FreeBSD.org
Sun Sep 15 21:18:52 UTC 2013
Author: nwhitehorn
Date: Sun Sep 15 21:18:50 2013
New Revision: 255603
URL: http://svnweb.freebsd.org/changeset/base/255603
Log:
Add support for the PAPR IOMMU. This makes DMA work at least on the
QEMU pSeries emulator. Thanks to kib for working through some details
on IRC.
Added:
projects/pseries/powerpc/pseries/plpar_iommu.c (contents, props changed)
projects/pseries/powerpc/pseries/plpar_iommu.h (contents, props changed)
Modified:
projects/pseries/conf/files.powerpc
projects/pseries/powerpc/pseries/rtas_pci.c
Modified: projects/pseries/conf/files.powerpc
==============================================================================
--- projects/pseries/conf/files.powerpc Sun Sep 15 16:27:25 2013 (r255602)
+++ projects/pseries/conf/files.powerpc Sun Sep 15 21:18:50 2013 (r255603)
@@ -229,6 +229,7 @@ powerpc/pseries/phyp-hvcall.S optional p
powerpc/pseries/mmu_phyp.c optional pseries powerpc64
powerpc/pseries/phyp_console.c optional pseries powerpc64
powerpc/pseries/platform_chrp.c optional pseries
+powerpc/pseries/plpar_iommu.c optional pseries powerpc64
powerpc/pseries/rtas_dev.c optional pseries
powerpc/pseries/rtas_pci.c optional pseries pci
powerpc/pseries/vdevice.c optional pseries powerpc64
Added: projects/pseries/powerpc/pseries/plpar_iommu.c
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ projects/pseries/powerpc/pseries/plpar_iommu.c Sun Sep 15 21:18:50 2013 (r255603)
@@ -0,0 +1,231 @@
+/*-
+ * Copyright (c) 2013, Nathan Whitehorn <nwhitehorn at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice unmodified, this list of conditions, and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/libkern.h>
+#include <sys/module.h>
+#include <sys/vmem.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/openfirm.h>
+
+#include <machine/bus.h>
+
+#include <powerpc/pseries/phyp-hvcall.h>
+#include <powerpc/pseries/plpar_iommu.h>
+
+MALLOC_DEFINE(M_PHYPIOMMU, "iommu", "IOMMU data for PAPR LPARs");
+
+struct papr_iommu_map {
+ uint32_t iobn;
+ vmem_t *vmem;
+ struct papr_iommu_map *next;
+};
+
+static SLIST_HEAD(iommu_maps, iommu_map) iommu_map_head =
+ SLIST_HEAD_INITIALIZER(iommu_map_head);
+static int papr_supports_stuff_tce = -1;
+
+struct iommu_map {
+ uint32_t iobn;
+ vmem_t *vmem;
+
+ SLIST_ENTRY(iommu_map) entries;
+};
+
+struct dma_window {
+ struct iommu_map *map;
+ bus_addr_t start;
+ bus_addr_t end;
+};
+
+int
+phyp_iommu_set_dma_tag(device_t dev, device_t child, bus_dma_tag_t tag)
+{
+ device_t p;
+ phandle_t node;
+ cell_t dma_acells, dma_scells, dmawindow[5];
+ struct iommu_map *i;
+
+ for (p = child; p != NULL; p = device_get_parent(p)) {
+ if (ofw_bus_has_prop(p, "ibm,my-dma-window"))
+ break;
+ if (ofw_bus_has_prop(p, "ibm,dma-window"))
+ break;
+ }
+
+ if (p == NULL)
+ return (ENXIO);
+
+ node = ofw_bus_get_node(p);
+ if (OF_getprop(node, "ibm,#dma-size-cells", &dma_scells,
+ sizeof(cell_t)) <= 0)
+ OF_searchprop(node, "#size-cells", &dma_scells, sizeof(cell_t));
+ if (OF_getprop(node, "ibm,#dma-address-cells", &dma_acells,
+ sizeof(cell_t)) <= 0)
+ OF_searchprop(node, "#address-cells", &dma_acells,
+ sizeof(cell_t));
+
+ if (ofw_bus_has_prop(p, "ibm,my-dma-window"))
+ OF_getprop(node, "ibm,my-dma-window", dmawindow,
+ sizeof(cell_t)*(dma_scells + dma_acells + 1));
+ else
+ OF_getprop(node, "ibm,dma-window", dmawindow,
+ sizeof(cell_t)*(dma_scells + dma_acells + 1));
+
+ struct dma_window *window = malloc(sizeof(struct dma_window),
+ M_PHYPIOMMU, M_WAITOK);
+ if (dma_acells == 1)
+ window->start = dmawindow[1];
+ else
+ window->start = ((uint64_t)(dmawindow[1]) << 32) | dmawindow[2];
+ if (dma_scells == 1)
+ window->end = window->start + dmawindow[dma_acells + 1];
+ else
+ window->end = window->start +
+ (((uint64_t)(dmawindow[dma_acells + 1]) << 32) |
+ dmawindow[dma_acells + 2]);
+
+ window->map = NULL;
+ SLIST_FOREACH(i, &iommu_map_head, entries) {
+ if (i->iobn == dmawindow[0]) {
+ window->map = i;
+ break;
+ }
+ }
+
+ if (window->map == NULL) {
+ window->map = malloc(sizeof(struct iommu_map), M_PHYPIOMMU,
+ M_WAITOK);
+ window->map->iobn = dmawindow[0];
+ window->map->vmem = vmem_create("IOMMU mappings", 0,
+ trunc_page(VMEM_ADDR_MAX), PAGE_SIZE, 0,
+ M_BESTFIT | M_NOWAIT);
+ }
+
+ /*
+ * Check experimentally whether we can use H_STUFF_TCE. It is required
+ * by the spec but some firmware (e.g. QEMU) does not actually support
+ * it
+ */
+ if (papr_supports_stuff_tce == -1)
+ papr_supports_stuff_tce = !(phyp_hcall(H_STUFF_TCE,
+ window->map->iobn, 0, 0, 0) == H_FUNCTION);
+
+ bus_dma_tag_set_iommu(tag, dev, window);
+
+ return (0);
+}
+
+int
+phyp_iommu_map(device_t dev, bus_dma_segment_t *segs, int *nsegs,
+ bus_addr_t min, bus_addr_t max, bus_size_t alignment, bus_addr_t boundary,
+ void *cookie)
+{
+ struct dma_window *window = cookie;
+ bus_addr_t minaddr, maxaddr;
+ bus_addr_t alloced;
+ bus_size_t allocsize;
+ int error, i, j;
+ uint64_t tce;
+ minaddr = window->start;
+ maxaddr = window->end;
+
+ /* XXX: handle exclusion range in a more useful way */
+ if (min < maxaddr)
+ maxaddr = min;
+
+ /* XXX: is this correct? */
+ if (alignment % PAGE_SIZE != 0)
+ alignment = PAGE_SIZE;
+
+ /* XXX: consolidate segs? */
+ for (i = 0; i < *nsegs; i++) {
+ allocsize = round_page(segs[i].ds_len +
+ (segs[i].ds_addr & PAGE_MASK));
+ error = vmem_xalloc(window->map->vmem, allocsize, alignment, 0,
+ boundary, minaddr, maxaddr, M_BESTFIT | M_NOWAIT, &alloced);
+ if (error != 0) {
+ panic("VMEM failure: %d\n", error);
+ return (error);
+ }
+
+ tce = trunc_page(segs[i].ds_addr);
+ tce |= 0x3; /* read/write */
+ if (papr_supports_stuff_tce) {
+ error = phyp_hcall(H_STUFF_TCE, window->map->iobn,
+ alloced, tce, allocsize/PAGE_SIZE);
+ } else {
+ for (j = 0; j < allocsize; j += PAGE_SIZE)
+ error = phyp_hcall(H_PUT_TCE, window->map->iobn,
+ alloced + j, tce + j);
+ }
+
+ segs[i].ds_addr = alloced + (segs[i].ds_addr & PAGE_MASK);
+ if (error < 0) {
+ panic("IOMMU mapping error: %d\n", error);
+ return (ENOMEM);
+ }
+ }
+
+ return (0);
+}
+
+int
+phyp_iommu_unmap(device_t dev, bus_dma_segment_t *segs, int nsegs, void *cookie)
+{
+ struct dma_window *window = cookie;
+ bus_addr_t pageround;
+ bus_size_t roundedsize;
+ int i;
+ bus_addr_t j;
+
+ for (i = 0; i < nsegs; i++) {
+ pageround = trunc_page(segs[i].ds_addr);
+ roundedsize = round_page(segs[i].ds_len +
+ (segs[i].ds_addr & PAGE_MASK));
+
+ if (papr_supports_stuff_tce) {
+ phyp_hcall(H_STUFF_TCE, window->map->iobn, pageround, 0,
+ roundedsize/PAGE_SIZE);
+ } else {
+ for (j = 0; j < roundedsize; j += PAGE_SIZE)
+ phyp_hcall(H_PUT_TCE, window->map->iobn,
+ pageround + j, 0);
+ }
+
+ vmem_xfree(window->map->vmem, pageround, roundedsize);
+ }
+
+ return (0);
+}
+
Added: projects/pseries/powerpc/pseries/plpar_iommu.h
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ projects/pseries/powerpc/pseries/plpar_iommu.h Sun Sep 15 21:18:50 2013 (r255603)
@@ -0,0 +1,41 @@
+/*-
+ * Copyright (c) 2013, Nathan Whitehorn <nwhitehorn at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice unmodified, this list of conditions, and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PSERIES_PHYP_IOMMU_H_
+#define _PSERIES_PHYP_IOMMU_H_
+
+#include <sys/types.h>
+#include <sys/bus_dma.h>
+
+int phyp_iommu_set_dma_tag(device_t dev, device_t child, bus_dma_tag_t tag);
+int phyp_iommu_map(device_t dev, bus_dma_segment_t *segs, int *nsegs,
+ bus_addr_t min, bus_addr_t max, bus_size_t alignment, bus_addr_t boundary,
+ void *cookie);
+int phyp_iommu_unmap(device_t dev, bus_dma_segment_t *segs, int nsegs,
+ void *cookie);
+
+#endif
+
Modified: projects/pseries/powerpc/pseries/rtas_pci.c
==============================================================================
--- projects/pseries/powerpc/pseries/rtas_pci.c Sun Sep 15 16:27:25 2013 (r255602)
+++ projects/pseries/powerpc/pseries/rtas_pci.c Sun Sep 15 21:18:50 2013 (r255603)
@@ -54,8 +54,10 @@ __FBSDID("$FreeBSD: projects/pseries/pow
#include <vm/pmap.h>
#include <powerpc/ofw/ofw_pci.h>
+#include <powerpc/pseries/plpar_iommu.h>
#include "pcib_if.h"
+#include "iommu_if.h"
/*
* Device interface.
@@ -72,6 +74,11 @@ static void rtaspci_write_config(device
u_int, u_int32_t, int);
/*
+ * IOMMU LPAR interface
+ */
+static bus_dma_tag_t rtaspci_get_dma_tag(device_t dev, device_t child);
+
+/*
* Driver methods.
*/
static device_method_t rtaspci_methods[] = {
@@ -83,11 +90,19 @@ static device_method_t rtaspci_methods[]
DEVMETHOD(pcib_read_config, rtaspci_read_config),
DEVMETHOD(pcib_write_config, rtaspci_write_config),
+ /* IOMMU functions */
+ DEVMETHOD(bus_get_dma_tag, rtaspci_get_dma_tag),
+#ifdef __powerpc64__
+ DEVMETHOD(iommu_map, phyp_iommu_map),
+ DEVMETHOD(iommu_unmap, phyp_iommu_unmap),
+#endif
+
DEVMETHOD_END
};
struct rtaspci_softc {
struct ofw_pci_softc pci_sc;
+ bus_dma_tag_t dma_tag;
cell_t read_pci_config, write_pci_config;
cell_t ex_read_pci_config, ex_write_pci_config;
@@ -134,6 +149,15 @@ rtaspci_attach(device_t dev)
OF_getprop(ofw_bus_get_node(dev), "ibm,pci-config-space-type",
&sc->sc_extended_config, sizeof(sc->sc_extended_config));
+ bus_dma_tag_create(bus_get_dma_tag(dev),
+ 1, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR,
+ NULL, NULL, BUS_SPACE_MAXSIZE, 0, BUS_SPACE_MAXSIZE,
+ 0, NULL, NULL, &sc->dma_tag);
+#ifdef __powerpc64__
+ if (!(mfmsr() & PSL_HV))
+ phyp_iommu_set_dma_tag(dev, dev, sc->dma_tag);
+#endif
+
return (ofw_pci_attach(dev));
}
@@ -201,3 +225,12 @@ rtaspci_write_config(device_t dev, u_int
width, val, &pcierror);
}
+static bus_dma_tag_t
+rtaspci_get_dma_tag(device_t dev, device_t child)
+{
+ struct rtaspci_softc *sc;
+
+ sc = device_get_softc(dev);
+ return (sc->dma_tag);
+}
+
More information about the svn-src-projects
mailing list