Exercise common setup and teardown flows for a sample platform TSM driver that implements the TSM 'connect' and 'disconnect' flows. This is both a template for platform specific implementations and a simple integration test for the PCI core infrastructure + ABI. Cc: Bjorn Helgaas <bhelgaas@xxxxxxxxxx> Cc: Lukas Wunner <lukas@xxxxxxxxx> Cc: Samuel Ortiz <sameo@xxxxxxxxxxxx> Cc: Alexey Kardashevskiy <aik@xxxxxxx> Cc: Xu Yilun <yilun.xu@xxxxxxxxxxxxxxx> Signed-off-by: Dan Williams <dan.j.williams@xxxxxxxxx> --- samples/devsec/bus.c | 6 ++++ samples/devsec/tsm.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/samples/devsec/bus.c b/samples/devsec/bus.c index 69117db10897..b78c04b21eb9 100644 --- a/samples/devsec/bus.c +++ b/samples/devsec/bus.c @@ -15,6 +15,7 @@ #define NR_DEVSEC_BUSES 1 #define NR_DEVSEC_ROOT_PORTS 1 +#define NR_PLATFORM_STREAMS 4 #define NR_PORT_STREAMS 1 #define NR_ADDR_ASSOC 1 #define NR_DEVSEC_DEVS 1 @@ -589,6 +590,7 @@ static int __init devsec_bus_probe(struct platform_device *pdev) struct devsec *devsec; struct pci_sysdata *sd; u64 mmio_size = SZ_64G; + struct pci_host_bridge *hb; struct device *dev = &pdev->dev; u64 mmio_start = iomem_resource.end + 1 - SZ_64G; @@ -649,6 +651,9 @@ static int __init devsec_bus_probe(struct platform_device *pdev) if (rc) return rc; + hb = pci_find_host_bridge(devsec->bus); + pci_ide_init_nr_streams(hb, NR_PLATFORM_STREAMS); + pci_scan_child_bus(devsec->bus); return 0; @@ -688,5 +693,6 @@ static void __exit devsec_bus_exit(void) } module_exit(devsec_bus_exit); +MODULE_IMPORT_NS("PCI_IDE"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Device Security Sample Infrastructure: TDISP Device Emulation"); diff --git a/samples/devsec/tsm.c b/samples/devsec/tsm.c index 7698df2b4e29..9c033662c1b4 100644 --- a/samples/devsec/tsm.c +++ b/samples/devsec/tsm.c @@ -4,6 +4,7 @@ #define dev_fmt(fmt) "devsec: " fmt #include <linux/platform_device.h> #include <linux/pci-tsm.h> +#include <linux/pci-ide.h> #include <linux/module.h> #include <linux/pci.h> #include <linux/tsm.h> @@ -50,13 +51,83 @@ static void devsec_tsm_pci_remove(struct pci_tsm *tsm) kfree(devsec_tsm); } +/* protected by tsm_ops lock */ +static DECLARE_BITMAP(devsec_stream_ids, NR_TSM_STREAMS); +static struct pci_ide *devsec_streams[NR_TSM_STREAMS]; + static int devsec_tsm_connect(struct pci_dev *pdev) { - return -ENXIO; + struct pci_dev *rp = pcie_find_root_port(pdev); + struct pci_ide *ide; + int rc, stream_id; + + stream_id = + find_first_zero_bit(devsec_stream_ids, NR_TSM_STREAMS); + if (stream_id == NR_TSM_STREAMS) + return -EBUSY; + set_bit(stream_id, devsec_stream_ids); + + ide = pci_ide_stream_alloc(pdev); + if (!ide) { + rc = -ENOMEM; + goto err_stream_alloc; + } + + ide->stream_id = stream_id; + rc = pci_ide_stream_register(ide); + if (rc) + goto err_stream; + + pci_ide_stream_setup(pdev, ide); + pci_ide_stream_setup(rp, ide); + + rc = tsm_ide_stream_register(pdev, ide); + if (rc) + goto err_tsm; + + /* + * Model a TSM that handled enabling the stream at + * tsm_ide_stream_register() time + */ + pci_ide_stream_enable(pdev, ide); + devsec_streams[stream_id] = ide; + + return 0; + +err_tsm: + pci_ide_stream_teardown(rp, ide); + pci_ide_stream_teardown(pdev, ide); + pci_ide_stream_unregister(ide); +err_stream: + pci_ide_stream_free(ide); +err_stream_alloc: + clear_bit(stream_id, devsec_stream_ids); + + return rc; } static void devsec_tsm_disconnect(struct pci_dev *pdev) { + struct pci_dev *rp = pcie_find_root_port(pdev); + struct pci_ide *ide; + int i; + + for_each_set_bit(i, devsec_stream_ids, NR_TSM_STREAMS) + if (devsec_streams[i]->pdev == pdev) + break; + + if (i >= NR_TSM_STREAMS) + return; + + ide = devsec_streams[i]; + devsec_streams[i] = NULL; + pci_ide_stream_disable(pdev, ide); + tsm_ide_stream_unregister(ide); + pci_ide_stream_teardown(rp, ide); + pci_ide_stream_teardown(pdev, ide); + pci_ide_stream_unregister(ide); + pci_ide_stream_free(ide); + clear_bit(i, devsec_stream_ids); } static const struct pci_tsm_ops devsec_pci_ops = {