1 | This series introduces SMMU handling for PCIe passthrough on ARM. These patches | 1 | This series introduces SMMU handling for PCIe passthrough on ARM. These patches |
---|---|---|---|
2 | are independent from (and don't depend on) the vPCI reference counting/locking | 2 | should be able to be upstreamed independently from the vPCI series [1]. See [2] |
3 | work in progress, and should be able to be upstreamed independently. | 3 | for notes about test cases. |
4 | |||
5 | [1] https://lists.xenproject.org/archives/html/xen-devel/2023-10/msg00660.html | ||
6 | [2] https://lists.xenproject.org/archives/html/xen-devel/2023-06/msg01135.html | ||
7 | |||
8 | v6->v7: | ||
9 | * drop ("xen/arm: don't pass iommu properties to hwdom for iommu-map") | ||
10 | |||
11 | v5->v6: | ||
12 | * don't revert ("xen/arm: Add cmdline boot option "pci-passthrough = <boolean>"") | ||
13 | * add ("xen/arm: enable dom0 to use PCI devices with pci-passthrough=no") | ||
14 | |||
15 | v4->v5: | ||
16 | * drop ("xen/arm: Improve readability of check for registered devices") | ||
17 | * drop ("xen/arm: Move is_protected flag to struct device") | ||
18 | * add ("xen/arm: don't pass iommu properties to hwdom for iommu-map") | ||
19 | * add ("xen/arm: Fix mapping for PCI bridge mmio region") | ||
20 | * revert ("xen/arm: Add cmdline boot option "pci-passthrough = <boolean>"") | ||
21 | * add ("xen/arm: Map ITS doorbell register to IOMMU page tables.") | ||
22 | * fix test case #1 with PCI device in dom0 | ||
4 | 23 | ||
5 | v3->v4: | 24 | v3->v4: |
6 | * split a change from ("xen/arm: Move is_protected flag to struct device") into | 25 | * split a change from ("xen/arm: Move is_protected flag to struct device") into |
7 | a new separate patch | 26 | a new separate patch |
8 | * see individual patches for further details | 27 | * see individual patches for further details |
... | ... | ||
11 | * drop "pci/arm: Use iommu_add_dt_pci_device()" | 30 | * drop "pci/arm: Use iommu_add_dt_pci_device()" |
12 | * drop "RFC: pci/arm: don't do iommu call for phantom functions" | 31 | * drop "RFC: pci/arm: don't do iommu call for phantom functions" |
13 | * move invocation of sideband ID mapping function to add_device() | 32 | * move invocation of sideband ID mapping function to add_device() |
14 | platform_ops/iommu_ops hook | 33 | platform_ops/iommu_ops hook |
15 | 34 | ||
16 | v1->v2: | ||
17 | * phantom device handling | ||
18 | * shuffle around iommu_add_dt_pci_device() | ||
19 | 35 | ||
20 | Oleksandr Andrushchenko (1): | 36 | Oleksandr Andrushchenko (1): |
21 | xen/arm: smmuv2: Add PCI devices support for SMMUv2 | 37 | xen/arm: smmuv2: Add PCI devices support for SMMUv2 |
22 | 38 | ||
23 | Oleksandr Tyshchenko (4): | 39 | Oleksandr Tyshchenko (2): |
24 | xen/arm: Improve readability of check for registered devices | ||
25 | xen/arm: Move is_protected flag to struct device | ||
26 | iommu/arm: Add iommu_dt_xlate() | 40 | iommu/arm: Add iommu_dt_xlate() |
27 | iommu/arm: Introduce iommu_add_dt_pci_sideband_ids API | 41 | iommu/arm: Introduce iommu_add_dt_pci_sideband_ids API |
28 | 42 | ||
29 | Rahul Singh (1): | 43 | Rahul Singh (3): |
30 | xen/arm: smmuv3: Add PCI devices support for SMMUv3 | 44 | xen/arm: smmuv3: Add PCI devices support for SMMUv3 |
45 | xen/arm: Fix mapping for PCI bridge mmio region | ||
46 | xen/arm: Map ITS doorbell register to IOMMU page tables | ||
31 | 47 | ||
32 | Stewart Hildebrand (1): | 48 | Stewart Hildebrand (2): |
33 | iommu/arm: iommu_add_dt_pci_sideband_ids phantom handling | 49 | iommu/arm: iommu_add_dt_pci_sideband_ids phantom handling |
50 | xen/arm: enable dom0 to use PCI devices with pci-passthrough=no | ||
34 | 51 | ||
35 | xen/arch/arm/domain_build.c | 4 +- | 52 | xen/arch/arm/device.c | 2 +- |
36 | xen/arch/arm/include/asm/device.h | 14 ++ | 53 | xen/arch/arm/pci/pci.c | 5 +- |
37 | xen/common/device_tree.c | 2 +- | 54 | xen/arch/arm/vgic-v3-its.c | 20 +++ |
38 | xen/drivers/passthrough/arm/ipmmu-vmsa.c | 4 +- | 55 | xen/arch/x86/include/asm/pci.h | 6 - |
39 | xen/drivers/passthrough/arm/smmu-v3.c | 81 ++++++++- | 56 | xen/common/device-tree/device-tree.c | 91 ++++++++++++ |
40 | xen/drivers/passthrough/arm/smmu.c | 122 +++++++++++--- | 57 | xen/drivers/passthrough/arm/smmu-v3.c | 117 ++++++++++++++-- |
41 | xen/drivers/passthrough/device_tree.c | 205 ++++++++++++++++++++--- | 58 | xen/drivers/passthrough/arm/smmu.c | 190 ++++++++++++++++++++------ |
42 | xen/include/xen/device_tree.h | 38 +++-- | 59 | xen/drivers/passthrough/device_tree.c | 97 ++++++++++--- |
43 | xen/include/xen/iommu.h | 22 ++- | 60 | xen/drivers/pci/physdev.c | 4 +- |
44 | 9 files changed, 423 insertions(+), 69 deletions(-) | 61 | xen/include/xen/device_tree.h | 23 ++++ |
62 | xen/include/xen/iommu.h | 31 ++++- | ||
63 | 11 files changed, 502 insertions(+), 84 deletions(-) | ||
45 | 64 | ||
46 | -- | 65 | -- |
47 | 2.40.1 | 66 | 2.34.1 | diff view generated by jsdifflib |
... | ... | ||
---|---|---|---|
7 | 7 | ||
8 | While at it introduce NO_IOMMU to avoid magic "1". | 8 | While at it introduce NO_IOMMU to avoid magic "1". |
9 | 9 | ||
10 | Signed-off-by: Oleksandr Tyshchenko <oleksandr_tyshchenko@epam.com> | 10 | Signed-off-by: Oleksandr Tyshchenko <oleksandr_tyshchenko@epam.com> |
11 | Signed-off-by: Stewart Hildebrand <stewart.hildebrand@amd.com> | 11 | Signed-off-by: Stewart Hildebrand <stewart.hildebrand@amd.com> |
12 | Signed-off-by: Mykyta Poturai <mykyta_poturai@epam.com> | ||
13 | Reviewed-by: Julien Grall <jgrall@amazon.com> | ||
12 | --- | 14 | --- |
15 | v6->v7: | ||
16 | * explained NO_IOMMU in comments | ||
17 | |||
18 | v5->v6: | ||
19 | * pass ops parameter to iommu_dt_xlate() | ||
20 | * add Julien's R-b | ||
21 | |||
22 | v4->v5: | ||
23 | * rebase on top of "dynamic node programming using overlay dtbo" series | ||
24 | * move #define NO_IOMMU 1 to header | ||
25 | * s/these/this/ inside comment | ||
26 | |||
13 | v3->v4: | 27 | v3->v4: |
14 | * make dt_phandle_args *iommu_spec const | 28 | * make dt_phandle_args *iommu_spec const |
15 | * move !ops->add_device check to helper | 29 | * move !ops->add_device check to helper |
16 | 30 | ||
17 | v2->v3: | 31 | v2->v3: |
... | ... | ||
26 | 40 | ||
27 | (cherry picked from commit c26bab0415ca303df86aba1d06ef8edc713734d3 from | 41 | (cherry picked from commit c26bab0415ca303df86aba1d06ef8edc713734d3 from |
28 | the downstream branch poc/pci-passthrough from | 42 | the downstream branch poc/pci-passthrough from |
29 | https://gitlab.com/xen-project/people/bmarquis/xen-arm-poc.git) | 43 | https://gitlab.com/xen-project/people/bmarquis/xen-arm-poc.git) |
30 | --- | 44 | --- |
31 | xen/drivers/passthrough/device_tree.c | 47 ++++++++++++++++++--------- | 45 | xen/drivers/passthrough/device_tree.c | 48 +++++++++++++++++---------- |
32 | 1 file changed, 31 insertions(+), 16 deletions(-) | 46 | xen/include/xen/iommu.h | 3 ++ |
47 | 2 files changed, 33 insertions(+), 18 deletions(-) | ||
33 | 48 | ||
34 | diff --git a/xen/drivers/passthrough/device_tree.c b/xen/drivers/passthrough/device_tree.c | 49 | diff --git a/xen/drivers/passthrough/device_tree.c b/xen/drivers/passthrough/device_tree.c |
35 | index XXXXXXX..XXXXXXX 100644 | 50 | index XXXXXXX..XXXXXXX 100644 |
36 | --- a/xen/drivers/passthrough/device_tree.c | 51 | --- a/xen/drivers/passthrough/device_tree.c |
37 | +++ b/xen/drivers/passthrough/device_tree.c | 52 | +++ b/xen/drivers/passthrough/device_tree.c |
38 | @@ -XXX,XX +XXX,XX @@ int iommu_release_dt_devices(struct domain *d) | 53 | @@ -XXX,XX +XXX,XX @@ int iommu_release_dt_devices(struct domain *d) |
39 | return 0; | 54 | return 0; |
40 | } | 55 | } |
41 | 56 | ||
42 | +/* This correlation must not be altered */ | ||
43 | +#define NO_IOMMU 1 | ||
44 | + | ||
45 | +static int iommu_dt_xlate(struct device *dev, | 57 | +static int iommu_dt_xlate(struct device *dev, |
46 | + const struct dt_phandle_args *iommu_spec) | 58 | + const struct dt_phandle_args *iommu_spec, |
59 | + const struct iommu_ops *ops) | ||
47 | +{ | 60 | +{ |
48 | + const struct iommu_ops *ops = iommu_get_ops(); | ||
49 | + int rc; | 61 | + int rc; |
50 | + | 62 | + |
51 | + if ( !ops->dt_xlate ) | 63 | + if ( !ops->dt_xlate ) |
52 | + return -EINVAL; | 64 | + return -EINVAL; |
53 | + | 65 | + |
... | ... | ||
64 | + * The driver is responsible to decide how to interpret them. | 76 | + * The driver is responsible to decide how to interpret them. |
65 | + */ | 77 | + */ |
66 | + return ops->dt_xlate(dev, iommu_spec); | 78 | + return ops->dt_xlate(dev, iommu_spec); |
67 | +} | 79 | +} |
68 | + | 80 | + |
69 | int iommu_add_dt_device(struct dt_device_node *np) | 81 | int iommu_remove_dt_device(struct dt_device_node *np) |
70 | { | 82 | { |
83 | const struct iommu_ops *ops = iommu_get_ops(); | ||
84 | @@ -XXX,XX +XXX,XX @@ int iommu_remove_dt_device(struct dt_device_node *np) | ||
85 | ASSERT(rw_is_locked(&dt_host_lock)); | ||
86 | |||
87 | if ( !iommu_enabled ) | ||
88 | - return 1; | ||
89 | + return NO_IOMMU; | ||
90 | |||
91 | if ( !ops ) | ||
92 | return -EOPNOTSUPP; | ||
93 | @@ -XXX,XX +XXX,XX @@ int iommu_add_dt_device(struct dt_device_node *np) | ||
71 | const struct iommu_ops *ops = iommu_get_ops(); | 94 | const struct iommu_ops *ops = iommu_get_ops(); |
72 | struct dt_phandle_args iommu_spec; | 95 | struct dt_phandle_args iommu_spec; |
73 | struct device *dev = dt_to_dev(np); | 96 | struct device *dev = dt_to_dev(np); |
74 | - int rc = 1, index = 0; | 97 | - int rc = 1, index = 0; |
75 | + int rc = NO_IOMMU, index = 0; | 98 | + int rc = NO_IOMMU, index = 0; |
76 | 99 | ||
100 | ASSERT(system_state < SYS_STATE_active || rw_is_locked(&dt_host_lock)); | ||
101 | |||
77 | if ( !iommu_enabled ) | 102 | if ( !iommu_enabled ) |
78 | - return 1; | 103 | - return 1; |
79 | + return NO_IOMMU; | 104 | + return NO_IOMMU; |
80 | 105 | ||
81 | if ( !ops ) | 106 | if ( !ops ) |
82 | return -EINVAL; | 107 | return -EINVAL; |
83 | @@ -XXX,XX +XXX,XX @@ int iommu_add_dt_device(struct dt_device_node *np) | 108 | @@ -XXX,XX +XXX,XX @@ int iommu_add_dt_device(struct dt_device_node *np) |
109 | { | ||
110 | /* | ||
84 | * The driver which supports generic IOMMU DT bindings must have | 111 | * The driver which supports generic IOMMU DT bindings must have |
85 | * these callback implemented. | 112 | - * these callback implemented. |
113 | + * this callback implemented. | ||
86 | */ | 114 | */ |
87 | - if ( !ops->add_device || !ops->dt_xlate ) | 115 | - if ( !ops->add_device || !ops->dt_xlate ) |
88 | + if ( !ops->add_device ) | 116 | + if ( !ops->add_device ) |
89 | return -EINVAL; | 117 | { |
118 | rc = -EINVAL; | ||
119 | goto fail; | ||
120 | } | ||
90 | 121 | ||
91 | - if ( !dt_device_is_available(iommu_spec.np) ) | 122 | - if ( !dt_device_is_available(iommu_spec.np) ) |
92 | - break; | 123 | - break; |
93 | - | 124 | - |
94 | - rc = iommu_fwspec_init(dev, &iommu_spec.np->dev); | 125 | - rc = iommu_fwspec_init(dev, &iommu_spec.np->dev); |
... | ... | ||
99 | - * Provide DT IOMMU specifier which describes the IOMMU master | 130 | - * Provide DT IOMMU specifier which describes the IOMMU master |
100 | - * interfaces of that device (device IDs, etc) to the driver. | 131 | - * interfaces of that device (device IDs, etc) to the driver. |
101 | - * The driver is responsible to decide how to interpret them. | 132 | - * The driver is responsible to decide how to interpret them. |
102 | - */ | 133 | - */ |
103 | - rc = ops->dt_xlate(dev, &iommu_spec); | 134 | - rc = ops->dt_xlate(dev, &iommu_spec); |
104 | + rc = iommu_dt_xlate(dev, &iommu_spec); | 135 | + rc = iommu_dt_xlate(dev, &iommu_spec, ops); |
105 | if ( rc ) | 136 | if ( rc ) |
106 | break; | 137 | break; |
107 | 138 | ||
139 | diff --git a/xen/include/xen/iommu.h b/xen/include/xen/iommu.h | ||
140 | index XXXXXXX..XXXXXXX 100644 | ||
141 | --- a/xen/include/xen/iommu.h | ||
142 | +++ b/xen/include/xen/iommu.h | ||
143 | @@ -XXX,XX +XXX,XX @@ int iommu_do_dt_domctl(struct xen_domctl *domctl, struct domain *d, | ||
144 | */ | ||
145 | int iommu_remove_dt_device(struct dt_device_node *np); | ||
146 | |||
147 | +/* Error code for reporting no IOMMU is present */ | ||
148 | +#define NO_IOMMU 1 | ||
149 | + | ||
150 | #endif /* HAS_DEVICE_TREE */ | ||
151 | |||
152 | struct page_info; | ||
108 | -- | 153 | -- |
109 | 2.40.1 | 154 | 2.34.1 | diff view generated by jsdifflib |
... | ... | ||
---|---|---|---|
6 | 6 | ||
7 | This behaves similarly to the existing iommu_add_dt_device API, except it | 7 | This behaves similarly to the existing iommu_add_dt_device API, except it |
8 | handles PCI devices, and it is to be invoked from the add_device hook in the | 8 | handles PCI devices, and it is to be invoked from the add_device hook in the |
9 | SMMU driver. | 9 | SMMU driver. |
10 | 10 | ||
11 | The function of_map_id to translate an ID through a downstream mapping | 11 | The function dt_map_id to translate an ID through a downstream mapping |
12 | (which is also suitable for mapping Requester ID) was borrowed from Linux | 12 | (which is also suitable for mapping Requester ID) was borrowed from Linux |
13 | (v5.10-rc6) and updated according to the Xen code base. | 13 | (v5.10-rc6) and updated according to the Xen code base. |
14 | 14 | ||
15 | [1] https://www.kernel.org/doc/Documentation/devicetree/bindings/pci/pci-iommu.txt | 15 | [1] https://www.kernel.org/doc/Documentation/devicetree/bindings/pci/pci-iommu.txt |
16 | 16 | ||
17 | Signed-off-by: Oleksandr Tyshchenko <oleksandr_tyshchenko@epam.com> | 17 | Signed-off-by: Oleksandr Tyshchenko <oleksandr_tyshchenko@epam.com> |
18 | Signed-off-by: Stewart Hildebrand <stewart.hildebrand@amd.com> | 18 | Signed-off-by: Stewart Hildebrand <stewart.hildebrand@amd.com> |
19 | Signed-off-by: Mykyta Poturai <mykyta_poturai@epam.com> | ||
19 | --- | 20 | --- |
21 | Regarding pci_for_each_dma_alias question: getting host bridge node | ||
22 | directly seems like a simpler solution with the same result. AFAIU | ||
23 | with pci_for_each_dma_alias in linux we would arrive to the host brige | ||
24 | node anyway, but also try to call dt_map_id for each device along the | ||
25 | way. I am not sure why exactly it is done this way in linux, as | ||
26 | according to the pci-iommu.txt, iommu-map node can only be present in | ||
27 | the PCI root. | ||
28 | |||
29 | v6->v7: | ||
30 | * put iommu_add_pci_sideband_ids under ifdef | ||
31 | * remove ifdef CONFIG_APCI | ||
32 | * style: add newline for symmetry | ||
33 | |||
34 | v5->v6: | ||
35 | * pass ops to iommu_dt_xlate() | ||
36 | |||
37 | v4->v5: | ||
38 | * style: add newlines after variable declarations and before return in iommu.h | ||
39 | * drop device_is_protected() check in iommu_add_dt_pci_sideband_ids() | ||
40 | * rebase on top of ("dynamic node programming using overlay dtbo") series | ||
41 | * fix typo in commit message | ||
42 | * remove #ifdef around dt_map_id() prototype | ||
43 | * move dt_map_id() to xen/common/device_tree.c | ||
44 | * add function name in error prints | ||
45 | * use dprintk for debug prints | ||
46 | * use GENMASK and #include <xen/bitops.h> | ||
47 | * fix typo in comment | ||
48 | * remove unnecessary (int) cast in loop condition | ||
49 | * assign *id_out and return success in case of no translation in dt_map_id() | ||
50 | * don't initialize local variable unnecessarily | ||
51 | * return error in case of ACPI/no DT in iommu_add_{dt_}pci_sideband_ids() | ||
52 | |||
20 | v3->v4: | 53 | v3->v4: |
21 | * wrap #include <asm/acpi.h> and if ( acpi_disabled ) in #ifdef CONFIG_ACPI | 54 | * wrap #include <asm/acpi.h> and if ( acpi_disabled ) in #ifdef CONFIG_ACPI |
22 | * fix Michal's remarks about style, parenthesis, and print formats | 55 | * fix Michal's remarks about style, parenthesis, and print formats |
23 | * remove !ops->dt_xlate check since it is already in iommu_dt_xlate helper | 56 | * remove !ops->dt_xlate check since it is already in iommu_dt_xlate helper |
24 | * rename s/iommu_dt_pci_map_id/dt_map_id/ because it is generic, not specific | 57 | * rename s/iommu_dt_pci_map_id/dt_map_id/ because it is generic, not specific |
... | ... | ||
56 | 89 | ||
57 | (cherry picked from commit 734e3bf6ee77e7947667ab8fa96c25b349c2e1da from | 90 | (cherry picked from commit 734e3bf6ee77e7947667ab8fa96c25b349c2e1da from |
58 | the downstream branch poc/pci-passthrough from | 91 | the downstream branch poc/pci-passthrough from |
59 | https://gitlab.com/xen-project/people/bmarquis/xen-arm-poc.git) | 92 | https://gitlab.com/xen-project/people/bmarquis/xen-arm-poc.git) |
60 | --- | 93 | --- |
61 | xen/drivers/passthrough/device_tree.c | 134 ++++++++++++++++++++++++++ | 94 | xen/common/device-tree/device-tree.c | 91 +++++++++++++++++++++++++++ |
62 | xen/include/xen/device_tree.h | 25 +++++ | 95 | xen/drivers/passthrough/device_tree.c | 42 +++++++++++++ |
63 | xen/include/xen/iommu.h | 22 ++++- | 96 | xen/include/xen/device_tree.h | 23 +++++++ |
64 | 3 files changed, 180 insertions(+), 1 deletion(-) | 97 | xen/include/xen/iommu.h | 28 ++++++++- |
65 | 98 | 4 files changed, 183 insertions(+), 1 deletion(-) | |
99 | |||
100 | diff --git a/xen/common/device-tree/device-tree.c b/xen/common/device-tree/device-tree.c | ||
101 | index XXXXXXX..XXXXXXX 100644 | ||
102 | --- a/xen/common/device-tree/device-tree.c | ||
103 | +++ b/xen/common/device-tree/device-tree.c | ||
104 | @@ -XXX,XX +XXX,XX @@ | ||
105 | * published by the Free Software Foundation. | ||
106 | */ | ||
107 | |||
108 | +#include <xen/bitops.h> | ||
109 | #include <xen/types.h> | ||
110 | #include <xen/init.h> | ||
111 | #include <xen/guest_access.h> | ||
112 | @@ -XXX,XX +XXX,XX @@ int dt_get_pci_domain_nr(struct dt_device_node *node) | ||
113 | return (u16)domain; | ||
114 | } | ||
115 | |||
116 | +int dt_map_id(const struct dt_device_node *np, uint32_t id, | ||
117 | + const char *map_name, const char *map_mask_name, | ||
118 | + struct dt_device_node **target, uint32_t *id_out) | ||
119 | +{ | ||
120 | + uint32_t map_mask, masked_id, map_len; | ||
121 | + const __be32 *map = NULL; | ||
122 | + | ||
123 | + if ( !np || !map_name || (!target && !id_out) ) | ||
124 | + return -EINVAL; | ||
125 | + | ||
126 | + map = dt_get_property(np, map_name, &map_len); | ||
127 | + if ( !map ) | ||
128 | + { | ||
129 | + if ( target ) | ||
130 | + return -ENODEV; | ||
131 | + | ||
132 | + /* Otherwise, no map implies no translation */ | ||
133 | + *id_out = id; | ||
134 | + return 0; | ||
135 | + } | ||
136 | + | ||
137 | + if ( !map_len || (map_len % (4 * sizeof(*map))) ) | ||
138 | + { | ||
139 | + printk(XENLOG_ERR "%s(): %s: Error: Bad %s length: %u\n", __func__, | ||
140 | + np->full_name, map_name, map_len); | ||
141 | + return -EINVAL; | ||
142 | + } | ||
143 | + | ||
144 | + /* The default is to select all bits. */ | ||
145 | + map_mask = GENMASK(31, 0); | ||
146 | + | ||
147 | + /* | ||
148 | + * Can be overridden by "{iommu,msi}-map-mask" property. | ||
149 | + * If dt_property_read_u32() fails, the default is used. | ||
150 | + */ | ||
151 | + if ( map_mask_name ) | ||
152 | + dt_property_read_u32(np, map_mask_name, &map_mask); | ||
153 | + | ||
154 | + masked_id = map_mask & id; | ||
155 | + for ( ; map_len > 0; map_len -= 4 * sizeof(*map), map += 4 ) | ||
156 | + { | ||
157 | + struct dt_device_node *phandle_node; | ||
158 | + uint32_t id_base = be32_to_cpup(map + 0); | ||
159 | + uint32_t phandle = be32_to_cpup(map + 1); | ||
160 | + uint32_t out_base = be32_to_cpup(map + 2); | ||
161 | + uint32_t id_len = be32_to_cpup(map + 3); | ||
162 | + | ||
163 | + if ( id_base & ~map_mask ) | ||
164 | + { | ||
165 | + printk(XENLOG_ERR "%s(): %s: Invalid %s translation - %s-mask (0x%"PRIx32") ignores id-base (0x%"PRIx32")\n", | ||
166 | + __func__, np->full_name, map_name, map_name, map_mask, | ||
167 | + id_base); | ||
168 | + return -EFAULT; | ||
169 | + } | ||
170 | + | ||
171 | + if ( (masked_id < id_base) || (masked_id >= (id_base + id_len)) ) | ||
172 | + continue; | ||
173 | + | ||
174 | + phandle_node = dt_find_node_by_phandle(phandle); | ||
175 | + if ( !phandle_node ) | ||
176 | + return -ENODEV; | ||
177 | + | ||
178 | + if ( target ) | ||
179 | + { | ||
180 | + if ( !*target ) | ||
181 | + *target = phandle_node; | ||
182 | + | ||
183 | + if ( *target != phandle_node ) | ||
184 | + continue; | ||
185 | + } | ||
186 | + | ||
187 | + if ( id_out ) | ||
188 | + *id_out = masked_id - id_base + out_base; | ||
189 | + | ||
190 | + dprintk(XENLOG_DEBUG, "%s: %s, using mask %08"PRIx32", id-base: %08"PRIx32", out-base: %08"PRIx32", length: %08"PRIx32", id: %08"PRIx32" -> %08"PRIx32"\n", | ||
191 | + np->full_name, map_name, map_mask, id_base, out_base, id_len, id, | ||
192 | + masked_id - id_base + out_base); | ||
193 | + return 0; | ||
194 | + } | ||
195 | + | ||
196 | + dprintk(XENLOG_DEBUG, "%s: no %s translation for id 0x%"PRIx32" on %s\n", | ||
197 | + np->full_name, map_name, id, | ||
198 | + (target && *target) ? (*target)->full_name : NULL); | ||
199 | + | ||
200 | + if ( id_out ) | ||
201 | + *id_out = id; | ||
202 | + | ||
203 | + return 0; | ||
204 | +} | ||
205 | + | ||
206 | /* | ||
207 | * Local variables: | ||
208 | * mode: C | ||
66 | diff --git a/xen/drivers/passthrough/device_tree.c b/xen/drivers/passthrough/device_tree.c | 209 | diff --git a/xen/drivers/passthrough/device_tree.c b/xen/drivers/passthrough/device_tree.c |
67 | index XXXXXXX..XXXXXXX 100644 | 210 | index XXXXXXX..XXXXXXX 100644 |
68 | --- a/xen/drivers/passthrough/device_tree.c | 211 | --- a/xen/drivers/passthrough/device_tree.c |
69 | +++ b/xen/drivers/passthrough/device_tree.c | 212 | +++ b/xen/drivers/passthrough/device_tree.c |
70 | @@ -XXX,XX +XXX,XX @@ static int iommu_dt_xlate(struct device *dev, | 213 | @@ -XXX,XX +XXX,XX @@ static int iommu_dt_xlate(struct device *dev, |
71 | return ops->dt_xlate(dev, iommu_spec); | 214 | return ops->dt_xlate(dev, iommu_spec); |
72 | } | 215 | } |
73 | 216 | ||
74 | +#ifdef CONFIG_HAS_PCI | 217 | +#ifdef CONFIG_HAS_PCI |
75 | +int dt_map_id(const struct dt_device_node *np, uint32_t id, | ||
76 | + const char *map_name, const char *map_mask_name, | ||
77 | + struct dt_device_node **target, uint32_t *id_out) | ||
78 | +{ | ||
79 | + uint32_t map_mask, masked_id, map_len; | ||
80 | + const __be32 *map = NULL; | ||
81 | + | ||
82 | + if ( !np || !map_name || (!target && !id_out) ) | ||
83 | + return -EINVAL; | ||
84 | + | ||
85 | + map = dt_get_property(np, map_name, &map_len); | ||
86 | + if ( !map ) | ||
87 | + { | ||
88 | + if ( target ) | ||
89 | + return -ENODEV; | ||
90 | + | ||
91 | + /* Otherwise, no map implies no translation */ | ||
92 | + *id_out = id; | ||
93 | + return 0; | ||
94 | + } | ||
95 | + | ||
96 | + if ( !map_len || (map_len % (4 * sizeof(*map))) ) | ||
97 | + { | ||
98 | + printk(XENLOG_ERR "%s: Error: Bad %s length: %u\n", np->full_name, | ||
99 | + map_name, map_len); | ||
100 | + return -EINVAL; | ||
101 | + } | ||
102 | + | ||
103 | + /* The default is to select all bits. */ | ||
104 | + map_mask = 0xffffffff; | ||
105 | + | ||
106 | + /* | ||
107 | + * Can be overridden by "{iommu,msi}-map-mask" property. | ||
108 | + * If df_property_read_u32() fails, the default is used. | ||
109 | + */ | ||
110 | + if ( map_mask_name ) | ||
111 | + dt_property_read_u32(np, map_mask_name, &map_mask); | ||
112 | + | ||
113 | + masked_id = map_mask & id; | ||
114 | + for ( ; (int)map_len > 0; map_len -= 4 * sizeof(*map), map += 4 ) | ||
115 | + { | ||
116 | + struct dt_device_node *phandle_node; | ||
117 | + uint32_t id_base = be32_to_cpup(map + 0); | ||
118 | + uint32_t phandle = be32_to_cpup(map + 1); | ||
119 | + uint32_t out_base = be32_to_cpup(map + 2); | ||
120 | + uint32_t id_len = be32_to_cpup(map + 3); | ||
121 | + | ||
122 | + if ( id_base & ~map_mask ) | ||
123 | + { | ||
124 | + printk(XENLOG_ERR "%s: Invalid %s translation - %s-mask (0x%"PRIx32") ignores id-base (0x%"PRIx32")\n", | ||
125 | + np->full_name, map_name, map_name, map_mask, id_base); | ||
126 | + return -EFAULT; | ||
127 | + } | ||
128 | + | ||
129 | + if ( (masked_id < id_base) || (masked_id >= (id_base + id_len)) ) | ||
130 | + continue; | ||
131 | + | ||
132 | + phandle_node = dt_find_node_by_phandle(phandle); | ||
133 | + if ( !phandle_node ) | ||
134 | + return -ENODEV; | ||
135 | + | ||
136 | + if ( target ) | ||
137 | + { | ||
138 | + if ( !*target ) | ||
139 | + *target = phandle_node; | ||
140 | + | ||
141 | + if ( *target != phandle_node ) | ||
142 | + continue; | ||
143 | + } | ||
144 | + | ||
145 | + if ( id_out ) | ||
146 | + *id_out = masked_id - id_base + out_base; | ||
147 | + | ||
148 | + printk(XENLOG_DEBUG "%s: %s, using mask %08"PRIx32", id-base: %08"PRIx32", out-base: %08"PRIx32", length: %08"PRIx32", id: %08"PRIx32" -> %08"PRIx32"\n", | ||
149 | + np->full_name, map_name, map_mask, id_base, out_base, id_len, id, | ||
150 | + masked_id - id_base + out_base); | ||
151 | + return 0; | ||
152 | + } | ||
153 | + | ||
154 | + printk(XENLOG_ERR "%s: no %s translation for id 0x%"PRIx32" on %s\n", | ||
155 | + np->full_name, map_name, id, (target && *target) ? (*target)->full_name : NULL); | ||
156 | + | ||
157 | + /* | ||
158 | + * NOTE: Linux bypasses translation without returning an error here, | ||
159 | + * but should we behave in the same way on Xen? Restrict for now. | ||
160 | + */ | ||
161 | + return -EFAULT; | ||
162 | +} | ||
163 | + | ||
164 | +int iommu_add_dt_pci_sideband_ids(struct pci_dev *pdev) | 218 | +int iommu_add_dt_pci_sideband_ids(struct pci_dev *pdev) |
165 | +{ | 219 | +{ |
166 | + const struct iommu_ops *ops = iommu_get_ops(); | 220 | + const struct iommu_ops *ops = iommu_get_ops(); |
167 | + struct dt_phandle_args iommu_spec = { .args_count = 1 }; | 221 | + struct dt_phandle_args iommu_spec = { .args_count = 1 }; |
168 | + struct device *dev = pci_to_dev(pdev); | 222 | + struct device *dev = pci_to_dev(pdev); |
169 | + const struct dt_device_node *np; | 223 | + const struct dt_device_node *np; |
170 | + int rc = NO_IOMMU; | 224 | + int rc; |
171 | + | 225 | + |
172 | + if ( !iommu_enabled ) | 226 | + if ( !iommu_enabled ) |
173 | + return NO_IOMMU; | 227 | + return NO_IOMMU; |
174 | + | 228 | + |
175 | + if ( !ops ) | 229 | + if ( !ops ) |
176 | + return -EINVAL; | 230 | + return -EINVAL; |
177 | + | ||
178 | + if ( device_is_protected(dev) ) | ||
179 | + return 0; | ||
180 | + | 231 | + |
181 | + if ( dev_iommu_fwspec_get(dev) ) | 232 | + if ( dev_iommu_fwspec_get(dev) ) |
182 | + return -EEXIST; | 233 | + return -EEXIST; |
183 | + | 234 | + |
184 | + np = pci_find_host_bridge_node(pdev); | 235 | + np = pci_find_host_bridge_node(pdev); |
... | ... | ||
192 | + rc = dt_map_id(np, PCI_BDF(pdev->bus, pdev->devfn), "iommu-map", | 243 | + rc = dt_map_id(np, PCI_BDF(pdev->bus, pdev->devfn), "iommu-map", |
193 | + "iommu-map-mask", &iommu_spec.np, iommu_spec.args); | 244 | + "iommu-map-mask", &iommu_spec.np, iommu_spec.args); |
194 | + if ( rc ) | 245 | + if ( rc ) |
195 | + return (rc == -ENODEV) ? NO_IOMMU : rc; | 246 | + return (rc == -ENODEV) ? NO_IOMMU : rc; |
196 | + | 247 | + |
197 | + rc = iommu_dt_xlate(dev, &iommu_spec); | 248 | + rc = iommu_dt_xlate(dev, &iommu_spec, ops); |
198 | + if ( rc < 0 ) | 249 | + if ( rc < 0 ) |
199 | + { | 250 | + { |
200 | + iommu_fwspec_free(dev); | 251 | + iommu_fwspec_free(dev); |
201 | + return -EINVAL; | 252 | + return -EINVAL; |
202 | + } | 253 | + } |
203 | + | 254 | + |
204 | + return rc; | 255 | + return rc; |
205 | +} | 256 | +} |
206 | +#endif /* CONFIG_HAS_PCI */ | 257 | +#endif /* CONFIG_HAS_PCI */ |
207 | + | 258 | + |
208 | int iommu_add_dt_device(struct dt_device_node *np) | 259 | int iommu_remove_dt_device(struct dt_device_node *np) |
209 | { | 260 | { |
210 | const struct iommu_ops *ops = iommu_get_ops(); | 261 | const struct iommu_ops *ops = iommu_get_ops(); |
211 | diff --git a/xen/include/xen/device_tree.h b/xen/include/xen/device_tree.h | 262 | diff --git a/xen/include/xen/device_tree.h b/xen/include/xen/device_tree.h |
212 | index XXXXXXX..XXXXXXX 100644 | 263 | index XXXXXXX..XXXXXXX 100644 |
213 | --- a/xen/include/xen/device_tree.h | 264 | --- a/xen/include/xen/device_tree.h |
214 | +++ b/xen/include/xen/device_tree.h | 265 | +++ b/xen/include/xen/device_tree.h |
215 | @@ -XXX,XX +XXX,XX @@ int dt_count_phandle_with_args(const struct dt_device_node *np, | 266 | @@ -XXX,XX +XXX,XX @@ int dt_count_phandle_with_args(const struct dt_device_node *np, |
216 | */ | 267 | */ |
217 | int dt_get_pci_domain_nr(struct dt_device_node *node); | 268 | int dt_get_pci_domain_nr(struct dt_device_node *node); |
218 | 269 | ||
219 | +#ifdef CONFIG_HAS_PCI | ||
220 | +/** | 270 | +/** |
221 | + * dt_map_id - Translate an ID through a downstream mapping. | 271 | + * dt_map_id - Translate an ID through a downstream mapping. |
222 | + * @np: root complex device node. | 272 | + * @np: root complex device node. |
223 | + * @id: device ID to map. | 273 | + * @id: device ID to map. |
224 | + * @map_name: property name of the map to use. | 274 | + * @map_name: property name of the map to use. |
... | ... | ||
237 | + * Return: 0 on success or a standard error code on failure. | 287 | + * Return: 0 on success or a standard error code on failure. |
238 | + */ | 288 | + */ |
239 | +int dt_map_id(const struct dt_device_node *np, uint32_t id, | 289 | +int dt_map_id(const struct dt_device_node *np, uint32_t id, |
240 | + const char *map_name, const char *map_mask_name, | 290 | + const char *map_name, const char *map_mask_name, |
241 | + struct dt_device_node **target, uint32_t *id_out); | 291 | + struct dt_device_node **target, uint32_t *id_out); |
242 | +#endif /* CONFIG_HAS_PCI */ | ||
243 | + | 292 | + |
244 | struct dt_device_node *dt_find_node_by_phandle(dt_phandle handle); | 293 | struct dt_device_node *dt_find_node_by_phandle(dt_phandle handle); |
245 | 294 | ||
246 | #ifdef CONFIG_DEVICE_TREE_DEBUG | 295 | #ifdef CONFIG_DEVICE_TREE_DEBUG |
247 | diff --git a/xen/include/xen/iommu.h b/xen/include/xen/iommu.h | 296 | diff --git a/xen/include/xen/iommu.h b/xen/include/xen/iommu.h |
248 | index XXXXXXX..XXXXXXX 100644 | 297 | index XXXXXXX..XXXXXXX 100644 |
249 | --- a/xen/include/xen/iommu.h | 298 | --- a/xen/include/xen/iommu.h |
250 | +++ b/xen/include/xen/iommu.h | 299 | +++ b/xen/include/xen/iommu.h |
251 | @@ -XXX,XX +XXX,XX @@ | 300 | @@ -XXX,XX +XXX,XX @@ |
252 | #include <xen/spinlock.h> | 301 | #include <xen/errno.h> |
253 | #include <public/domctl.h> | 302 | #include <public/domctl.h> |
254 | #include <public/hvm/ioreq.h> | 303 | #include <public/hvm/ioreq.h> |
255 | +#ifdef CONFIG_ACPI | 304 | +#include <xen/acpi.h> |
256 | +#include <asm/acpi.h> | ||
257 | +#endif | ||
258 | #include <asm/device.h> | 305 | #include <asm/device.h> |
259 | 306 | ||
260 | TYPE_SAFE(uint64_t, dfn); | 307 | TYPE_SAFE(uint64_t, dfn); |
261 | @@ -XXX,XX +XXX,XX @@ int iommu_dt_domain_init(struct domain *d); | 308 | @@ -XXX,XX +XXX,XX @@ int iommu_dt_domain_init(struct domain *d); |
262 | int iommu_release_dt_devices(struct domain *d); | 309 | int iommu_release_dt_devices(struct domain *d); |
... | ... | ||
272 | * (IOMMU is not enabled/present or device is not connected to it). | 319 | * (IOMMU is not enabled/present or device is not connected to it). |
273 | */ | 320 | */ |
274 | int iommu_add_dt_device(struct dt_device_node *np); | 321 | int iommu_add_dt_device(struct dt_device_node *np); |
275 | +int iommu_add_dt_pci_sideband_ids(struct pci_dev *pdev); | 322 | +int iommu_add_dt_pci_sideband_ids(struct pci_dev *pdev); |
276 | 323 | ||
277 | int iommu_do_dt_domctl(struct xen_domctl *, struct domain *, | 324 | int iommu_do_dt_domctl(struct xen_domctl *domctl, struct domain *d, |
278 | XEN_GUEST_HANDLE_PARAM(xen_domctl_t)); | 325 | XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl); |
326 | @@ -XXX,XX +XXX,XX @@ int iommu_remove_dt_device(struct dt_device_node *np); | ||
327 | /* Error code for reporting no IOMMU is present */ | ||
328 | #define NO_IOMMU 1 | ||
279 | 329 | ||
280 | +#else /* !HAS_DEVICE_TREE */ | 330 | +#else /* !HAS_DEVICE_TREE */ |
281 | +static inline int iommu_add_dt_pci_sideband_ids(struct pci_dev *pdev) | 331 | +static inline int iommu_add_dt_pci_sideband_ids(struct pci_dev *pdev) |
282 | +{ | 332 | +{ |
283 | + return 0; | 333 | + return -ENOSYS; |
284 | +} | 334 | +} |
335 | + | ||
285 | #endif /* HAS_DEVICE_TREE */ | 336 | #endif /* HAS_DEVICE_TREE */ |
286 | 337 | ||
338 | +#ifdef CONFIG_HAS_PCI | ||
287 | +static inline int iommu_add_pci_sideband_ids(struct pci_dev *pdev) | 339 | +static inline int iommu_add_pci_sideband_ids(struct pci_dev *pdev) |
288 | +{ | 340 | +{ |
289 | + int ret = 0; | 341 | + int ret = -ENOSYS; |
290 | +#ifdef CONFIG_ACPI | 342 | + |
291 | + if ( acpi_disabled ) | 343 | + if ( acpi_disabled ) |
344 | + ret = iommu_add_dt_pci_sideband_ids(pdev); | ||
345 | + | ||
346 | + return ret; | ||
347 | +} | ||
348 | +#else /* !HAS_PCI */ | ||
349 | +static inline int iommu_add_pci_sideband_ids(struct pci_dev *pdev) | ||
350 | +{ | ||
351 | + return -ENOSYS; | ||
352 | +} | ||
292 | +#endif | 353 | +#endif |
293 | + ret = iommu_add_dt_pci_sideband_ids(pdev); | ||
294 | + return ret; | ||
295 | +} | ||
296 | + | 354 | + |
297 | struct page_info; | 355 | struct page_info; |
298 | 356 | ||
299 | /* | 357 | /* |
300 | -- | 358 | -- |
301 | 2.40.1 | 359 | 2.34.1 | diff view generated by jsdifflib |
1 | From: Stewart Hildebrand <stewart.hildebrand@amd.com> | ||
---|---|---|---|
2 | |||
1 | Handle phantom functions in iommu_add_dt_pci_sideband_ids(). Each phantom | 3 | Handle phantom functions in iommu_add_dt_pci_sideband_ids(). Each phantom |
2 | function will have a unique requestor ID (RID)/BDF. On ARM, we need to | 4 | function will have a unique requestor ID (RID)/BDF. On ARM, we need to |
3 | map/translate the RID/BDF to an AXI stream ID for each phantom function | 5 | map/translate the RID/BDF to an AXI stream ID for each phantom function |
4 | according to the pci-iommu device tree mapping [1]. The RID/BDF -> AXI stream ID | 6 | according to the pci-iommu device tree mapping [1]. The RID/BDF -> AXI stream ID |
5 | mapping in DT could allow phantom devices (i.e. devices with phantom functions) | 7 | mapping in DT could allow phantom devices (i.e. devices with phantom functions) |
6 | to use different AXI stream IDs based on the (phantom) function. | 8 | to use different AXI stream IDs based on the (phantom) function. |
7 | 9 | ||
8 | [1] https://www.kernel.org/doc/Documentation/devicetree/bindings/pci/pci-iommu.txt | 10 | [1] https://www.kernel.org/doc/Documentation/devicetree/bindings/pci/pci-iommu.txt |
9 | 11 | ||
10 | Signed-off-by: Stewart Hildebrand <stewart.hildebrand@amd.com> | 12 | Signed-off-by: Stewart Hildebrand <stewart.hildebrand@amd.com> |
11 | --- | 13 | --- |
14 | v6->v7: | ||
15 | * no change | ||
16 | |||
17 | v5->v6: | ||
18 | * no change | ||
19 | |||
20 | v4->v5: | ||
21 | * no change | ||
22 | |||
12 | v3->v4: | 23 | v3->v4: |
13 | * s/iommu_dt_pci_map_id/dt_map_id/ | 24 | * s/iommu_dt_pci_map_id/dt_map_id/ |
14 | 25 | ||
15 | v2->v3: | 26 | v2->v3: |
16 | * new patch title (was: iommu/arm: iommu_add_dt_pci_device phantom handling) | 27 | * new patch title (was: iommu/arm: iommu_add_dt_pci_device phantom handling) |
17 | * rework loop to reduce duplication | 28 | * rework loop to reduce duplication |
18 | * s/iommu_fwspec_free(pci_to_dev(pdev))/iommu_fwspec_free(dev)/ | 29 | * s/iommu_fwspec_free(pci_to_dev(pdev))/iommu_fwspec_free(dev)/ |
19 | 30 | ||
20 | v1->v2: | 31 | v1->v2: |
21 | * new patch | 32 | * new patch |
33 | |||
34 | --- | ||
35 | TODO: investigate Jan's comment [2] | ||
36 | [2] https://lore.kernel.org/xen-devel/806a2978-19fb-4d31-ab6a-35ea7317c8de@suse.com/ | ||
22 | --- | 37 | --- |
23 | xen/drivers/passthrough/device_tree.c | 33 ++++++++++++++++----------- | 38 | xen/drivers/passthrough/device_tree.c | 33 ++++++++++++++++----------- |
24 | 1 file changed, 20 insertions(+), 13 deletions(-) | 39 | 1 file changed, 20 insertions(+), 13 deletions(-) |
25 | 40 | ||
26 | diff --git a/xen/drivers/passthrough/device_tree.c b/xen/drivers/passthrough/device_tree.c | 41 | diff --git a/xen/drivers/passthrough/device_tree.c b/xen/drivers/passthrough/device_tree.c |
27 | index XXXXXXX..XXXXXXX 100644 | 42 | index XXXXXXX..XXXXXXX 100644 |
28 | --- a/xen/drivers/passthrough/device_tree.c | 43 | --- a/xen/drivers/passthrough/device_tree.c |
29 | +++ b/xen/drivers/passthrough/device_tree.c | 44 | +++ b/xen/drivers/passthrough/device_tree.c |
30 | @@ -XXX,XX +XXX,XX @@ int iommu_add_dt_pci_sideband_ids(struct pci_dev *pdev) | 45 | @@ -XXX,XX +XXX,XX @@ int iommu_add_dt_pci_sideband_ids(struct pci_dev *pdev) |
31 | struct device *dev = pci_to_dev(pdev); | 46 | struct device *dev = pci_to_dev(pdev); |
32 | const struct dt_device_node *np; | 47 | const struct dt_device_node *np; |
33 | int rc = NO_IOMMU; | 48 | int rc; |
34 | + unsigned int devfn = pdev->devfn; | 49 | + unsigned int devfn = pdev->devfn; |
35 | 50 | ||
36 | if ( !iommu_enabled ) | 51 | if ( !iommu_enabled ) |
37 | return NO_IOMMU; | 52 | return NO_IOMMU; |
38 | @@ -XXX,XX +XXX,XX @@ int iommu_add_dt_pci_sideband_ids(struct pci_dev *pdev) | 53 | @@ -XXX,XX +XXX,XX @@ int iommu_add_dt_pci_sideband_ids(struct pci_dev *pdev) |
... | ... | ||
55 | + rc = dt_map_id(np, PCI_BDF(pdev->bus, devfn), "iommu-map", | 70 | + rc = dt_map_id(np, PCI_BDF(pdev->bus, devfn), "iommu-map", |
56 | + "iommu-map-mask", &iommu_spec.np, iommu_spec.args); | 71 | + "iommu-map-mask", &iommu_spec.np, iommu_spec.args); |
57 | + if ( rc ) | 72 | + if ( rc ) |
58 | + return (rc == -ENODEV) ? NO_IOMMU : rc; | 73 | + return (rc == -ENODEV) ? NO_IOMMU : rc; |
59 | 74 | ||
60 | - rc = iommu_dt_xlate(dev, &iommu_spec); | 75 | - rc = iommu_dt_xlate(dev, &iommu_spec, ops); |
61 | - if ( rc < 0 ) | 76 | - if ( rc < 0 ) |
62 | - { | 77 | - { |
63 | - iommu_fwspec_free(dev); | 78 | - iommu_fwspec_free(dev); |
64 | - return -EINVAL; | 79 | - return -EINVAL; |
65 | + rc = iommu_dt_xlate(dev, &iommu_spec); | 80 | + rc = iommu_dt_xlate(dev, &iommu_spec, ops); |
66 | + if ( rc < 0 ) | 81 | + if ( rc < 0 ) |
67 | + { | 82 | + { |
68 | + iommu_fwspec_free(dev); | 83 | + iommu_fwspec_free(dev); |
69 | + return -EINVAL; | 84 | + return -EINVAL; |
70 | + } | 85 | + } |
... | ... | ||
75 | + (PCI_SLOT(devfn) == PCI_SLOT(pdev->devfn)) ); | 90 | + (PCI_SLOT(devfn) == PCI_SLOT(pdev->devfn)) ); |
76 | 91 | ||
77 | return rc; | 92 | return rc; |
78 | } | 93 | } |
79 | -- | 94 | -- |
80 | 2.40.1 | 95 | 2.34.1 | diff view generated by jsdifflib |
1 | From: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> | 1 | From: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> |
---|---|---|---|
2 | |||
3 | Implement support for PCI devices in the SMMU driver. Make arm_smmu_master | ||
4 | structure to hold a pointer to the device to allow it to hold PCI devices. | ||
5 | Trigger iommu-map parsing when new PCI device is added. Add checks to | ||
6 | assign/deassign functions to ensure PCI devices are handled correctly. | ||
7 | Implement basic quarantining. | ||
8 | |||
9 | All pci devices are automatically assigned to hardware domain if it exists | ||
10 | to ensure it can probe them. | ||
11 | |||
12 | TODO: | ||
13 | Implement scratch page quarantining support. | ||
2 | 14 | ||
3 | Signed-off-by: Oleksandr Tyshchenko <oleksandr_tyshchenko@epam.com> | 15 | Signed-off-by: Oleksandr Tyshchenko <oleksandr_tyshchenko@epam.com> |
4 | Signed-off-by: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> | 16 | Signed-off-by: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> |
5 | Signed-off-by: Stewart Hildebrand <stewart.hildebrand@amd.com> | 17 | Signed-off-by: Stewart Hildebrand <stewart.hildebrand@amd.com> |
18 | Signed-off-by: Mykyta Poturai <mykyta_poturai@epam.com> | ||
6 | --- | 19 | --- |
20 | v6->v7: | ||
21 | * use d->pci_lock in arm_smmu_assign_dev() | ||
22 | * remove !is_hardware_domain and pdev->domain == d checks in assign to | ||
23 | support future dom0less use case when dom0 is using vPCI | ||
24 | * remove stale todo in dev_get_dev_node | ||
25 | * don't print "" | ||
26 | * remove redundant dt_device_is_protected check | ||
27 | * remove assign/deassing prints | ||
28 | * change assign logic to remove reassign reimplementation | ||
29 | * check if pdev->domain exists before assigning to it | ||
30 | * explain pdev->devfn check | ||
31 | * make reassign check stricter and update comment | ||
32 | |||
33 | v5->v6: | ||
34 | * check for hardware_domain == NULL (dom0less test case) | ||
35 | * locking: assign pdev->domain before list_add() | ||
36 | |||
37 | v4->v5: | ||
38 | * assign device to pdev->domain (usually dom0) by default in add_device() hook | ||
39 | * deassign from hwdom | ||
40 | * rebase on top of ("dynamic node programming using overlay dtbo") series | ||
41 | * remove TODO in comment about device prints | ||
42 | * add TODO regarding locking | ||
43 | * fixup after dropping ("xen/arm: Move is_protected flag to struct device") | ||
44 | |||
7 | v3->v4: | 45 | v3->v4: |
8 | * add new device_is_protected check in add_device hook to match SMMUv3 and | 46 | * add new device_is_protected check in add_device hook to match SMMUv3 and |
9 | IPMMU-VMSA drivers | 47 | IPMMU-VMSA drivers |
10 | 48 | ||
11 | v2->v3: | 49 | v2->v3: |
... | ... | ||
27 | 65 | ||
28 | (cherry picked from commit 0c11a7f65f044c26d87d1e27ac6283ef1f9cfb7a from | 66 | (cherry picked from commit 0c11a7f65f044c26d87d1e27ac6283ef1f9cfb7a from |
29 | the downstream branch spider-master from | 67 | the downstream branch spider-master from |
30 | https://github.com/xen-troops/xen.git) | 68 | https://github.com/xen-troops/xen.git) |
31 | --- | 69 | --- |
32 | 70 | xen/drivers/passthrough/arm/smmu.c | 190 ++++++++++++++++++++++------- | |
33 | This is a file imported from Linux with modifications for Xen. What should be | 71 | 1 file changed, 147 insertions(+), 43 deletions(-) |
34 | the coding style for Xen modifications? | ||
35 | --- | ||
36 | xen/drivers/passthrough/arm/smmu.c | 120 ++++++++++++++++++++++++----- | ||
37 | 1 file changed, 99 insertions(+), 21 deletions(-) | ||
38 | 72 | ||
39 | diff --git a/xen/drivers/passthrough/arm/smmu.c b/xen/drivers/passthrough/arm/smmu.c | 73 | diff --git a/xen/drivers/passthrough/arm/smmu.c b/xen/drivers/passthrough/arm/smmu.c |
40 | index XXXXXXX..XXXXXXX 100644 | 74 | index XXXXXXX..XXXXXXX 100644 |
41 | --- a/xen/drivers/passthrough/arm/smmu.c | 75 | --- a/xen/drivers/passthrough/arm/smmu.c |
42 | +++ b/xen/drivers/passthrough/arm/smmu.c | 76 | +++ b/xen/drivers/passthrough/arm/smmu.c |
43 | @@ -XXX,XX +XXX,XX @@ typedef enum irqreturn irqreturn_t; | 77 | @@ -XXX,XX +XXX,XX @@ enum irqreturn { |
44 | /* Device logger functions | 78 | |
45 | * TODO: Handle PCI | 79 | typedef enum irqreturn irqreturn_t; |
46 | */ | 80 | |
81 | -/* Device logger functions | ||
82 | - * TODO: Handle PCI | ||
83 | - */ | ||
47 | -#define dev_print(dev, lvl, fmt, ...) \ | 84 | -#define dev_print(dev, lvl, fmt, ...) \ |
48 | - printk(lvl "smmu: %s: " fmt, dt_node_full_name(dev_to_dt(dev)), ## __VA_ARGS__) | 85 | - printk(lvl "smmu: %s: " fmt, dt_node_full_name(dev_to_dt(dev)), ## __VA_ARGS__) |
86 | +/* Device logger functions */ | ||
49 | +#ifndef CONFIG_HAS_PCI | 87 | +#ifndef CONFIG_HAS_PCI |
50 | +#define dev_print(dev, lvl, fmt, ...) \ | 88 | +#define dev_print(dev, lvl, fmt, ...) \ |
51 | + printk(lvl "smmu: %s: " fmt, dev_name(dev), ## __VA_ARGS__) | 89 | + printk(lvl "smmu: %s: " fmt, dev_name(dev), ## __VA_ARGS__) |
52 | +#else | 90 | +#else |
53 | +#define dev_print(dev, lvl, fmt, ...) ({ \ | 91 | +#define dev_print(dev, lvl, fmt, ...) ({ \ |
... | ... | ||
78 | +#endif | 116 | +#endif |
79 | 117 | ||
80 | /* Xen: misc */ | 118 | /* Xen: misc */ |
81 | #define PHYS_MASK_SHIFT PADDR_BITS | 119 | #define PHYS_MASK_SHIFT PADDR_BITS |
82 | @@ -XXX,XX +XXX,XX @@ struct arm_smmu_master_cfg { | 120 | @@ -XXX,XX +XXX,XX @@ struct arm_smmu_master_cfg { |
83 | for (i = 0; idx = cfg->smendx[i], i < num; ++i) | 121 | for (i = 0; idx = (cfg)->smendx[i], (i) < (num); ++(i)) |
84 | 122 | ||
85 | struct arm_smmu_master { | 123 | struct arm_smmu_master { |
86 | - struct device_node *of_node; | 124 | - struct device_node *of_node; |
87 | + struct device *dev; | 125 | + struct device *dev; |
88 | struct rb_node node; | 126 | struct rb_node node; |
... | ... | ||
95 | - return dev_iommu_fwspec_get(&master->of_node->dev); | 133 | - return dev_iommu_fwspec_get(&master->of_node->dev); |
96 | + return dev_iommu_fwspec_get(master->dev); | 134 | + return dev_iommu_fwspec_get(master->dev); |
97 | } | 135 | } |
98 | 136 | ||
99 | static void parse_driver_options(struct arm_smmu_device *smmu) | 137 | static void parse_driver_options(struct arm_smmu_device *smmu) |
100 | @@ -XXX,XX +XXX,XX @@ static struct device_node *dev_get_dev_node(struct device *dev) | 138 | @@ -XXX,XX +XXX,XX @@ static void parse_driver_options(struct arm_smmu_device *smmu) |
101 | } | 139 | |
140 | static struct device_node *dev_get_dev_node(struct device *dev) | ||
141 | { | ||
142 | -#if 0 /* Xen: TODO: Add support for PCI */ | ||
143 | - if (dev_is_pci(dev)) { | ||
144 | - struct pci_bus *bus = to_pci_dev(dev)->bus; | ||
145 | - | ||
146 | - while (!pci_is_root_bus(bus)) | ||
147 | - bus = bus->parent; | ||
148 | - return bus->bridge->parent->of_node; | ||
149 | - } | ||
150 | -#endif | ||
151 | - | ||
152 | return dev->of_node; | ||
153 | } | ||
102 | 154 | ||
103 | static struct arm_smmu_master *find_smmu_master(struct arm_smmu_device *smmu, | 155 | static struct arm_smmu_master *find_smmu_master(struct arm_smmu_device *smmu, |
104 | - struct device_node *dev_node) | 156 | - struct device_node *dev_node) |
105 | + struct device *dev) | 157 | + struct device *dev) |
106 | { | 158 | { |
... | ... | ||
136 | 188 | ||
137 | - master = find_smmu_master(smmu, dev_node); | 189 | - master = find_smmu_master(smmu, dev_node); |
138 | + master = find_smmu_master(smmu, dev); | 190 | + master = find_smmu_master(smmu, dev); |
139 | if (master) { | 191 | if (master) { |
140 | dev_err(dev, | 192 | dev_err(dev, |
141 | "rejecting multiple registrations for master device %s\n", | 193 | - "rejecting multiple registrations for master device %s\n", |
142 | - dev_node->name); | 194 | - dev_node->name); |
143 | + dev_node ? dev_node->name : ""); | 195 | + "rejecting multiple registrations for master device\n"); |
144 | return -EBUSY; | 196 | return -EBUSY; |
145 | } | 197 | } |
146 | 198 | ||
147 | master = devm_kzalloc(dev, sizeof(*master), GFP_KERNEL); | 199 | master = devm_kzalloc(dev, sizeof(*master), GFP_KERNEL); |
148 | if (!master) | 200 | if (!master) |
149 | return -ENOMEM; | 201 | return -ENOMEM; |
150 | - master->of_node = dev_node; | 202 | - master->of_node = dev_node; |
151 | + master->dev = dev; | 203 | + master->dev = dev; |
152 | + | 204 | |
153 | + if ( device_is_protected(dev) ) | 205 | - /* Xen: Let Xen know that the device is protected by an SMMU */ |
154 | + { | 206 | - dt_device_set_protected(dev_node); |
155 | + dev_err(dev, "Already added to SMMU\n"); | 207 | + if ( !dev_is_pci(dev) ) |
156 | + return -EEXIST; | 208 | + { |
209 | + /* Xen: Let Xen know that the device is protected by an SMMU */ | ||
210 | + dt_device_set_protected(dev_node); | ||
157 | + } | 211 | + } |
158 | 212 | ||
159 | /* Xen: Let Xen know that the device is protected by an SMMU */ | 213 | for (i = 0; i < fwspec->num_ids; ++i) { |
160 | device_set_protected(dev); | 214 | if (!(smmu->features & ARM_SMMU_FEAT_STREAM_MATCH) && |
161 | @@ -XXX,XX +XXX,XX @@ static int arm_smmu_dt_add_device_legacy(struct arm_smmu_device *smmu, | ||
162 | (fwspec->ids[i] >= smmu->num_mapping_groups)) { | 215 | (fwspec->ids[i] >= smmu->num_mapping_groups)) { |
163 | dev_err(dev, | 216 | dev_err(dev, |
164 | "stream ID for master device %s greater than maximum allowed (%d)\n", | 217 | - "stream ID for master device %s greater than maximum allowed (%d)\n", |
165 | - dev_node->name, smmu->num_mapping_groups); | 218 | - dev_node->name, smmu->num_mapping_groups); |
166 | + dev_node ? dev_node->name : "", smmu->num_mapping_groups); | 219 | + "SMMU stream ID %d is greater than maximum allowed (%d)\n", |
220 | + fwspec->ids[i], smmu->num_mapping_groups); | ||
167 | return -ERANGE; | 221 | return -ERANGE; |
168 | } | 222 | } |
169 | master->cfg.smendx[i] = INVALID_SMENDX; | 223 | master->cfg.smendx[i] = INVALID_SMENDX; |
224 | @@ -XXX,XX +XXX,XX @@ static int arm_smmu_dt_remove_device_legacy(struct arm_smmu_device *smmu, | ||
225 | struct device_node *dev_node = dev_get_dev_node(dev); | ||
226 | int ret; | ||
227 | |||
228 | - master = find_smmu_master(smmu, dev_node); | ||
229 | + master = find_smmu_master(smmu, dev); | ||
230 | if (master == NULL) { | ||
231 | dev_err(dev, | ||
232 | "No registrations found for master device %s\n", | ||
233 | @@ -XXX,XX +XXX,XX @@ static int arm_smmu_dt_remove_device_legacy(struct arm_smmu_device *smmu, | ||
234 | if (ret) | ||
235 | return ret; | ||
236 | |||
237 | - /* Protected by dt_host_lock and dtdevs_lock as caller holds these locks. */ | ||
238 | - dev_node->is_protected = false; | ||
239 | + if ( !dev_is_pci(dev) ) | ||
240 | + /* Protected by dt_host_lock and dtdevs_lock as caller holds these locks. */ | ||
241 | + dev_node->is_protected = false; | ||
242 | |||
243 | kfree(master); | ||
244 | return 0; | ||
245 | @@ -XXX,XX +XXX,XX @@ static int register_smmu_master(struct arm_smmu_device *smmu, | ||
246 | fwspec); | ||
247 | } | ||
248 | |||
249 | +/* Forward declaration */ | ||
250 | +static int arm_smmu_assign_dev(struct domain *d, u8 devfn, | ||
251 | + struct device *dev, u32 flag); | ||
252 | +static int arm_smmu_deassign_dev(struct domain *d, uint8_t devfn, | ||
253 | + struct device *dev); | ||
254 | + | ||
255 | /* | ||
256 | * The driver which supports generic IOMMU DT bindings must have this | ||
257 | * callback implemented. | ||
170 | @@ -XXX,XX +XXX,XX @@ static int arm_smmu_dt_add_device_generic(u8 devfn, struct device *dev) | 258 | @@ -XXX,XX +XXX,XX @@ static int arm_smmu_dt_add_device_generic(u8 devfn, struct device *dev) |
259 | { | ||
171 | struct arm_smmu_device *smmu; | 260 | struct arm_smmu_device *smmu; |
172 | struct iommu_fwspec *fwspec; | 261 | struct iommu_fwspec *fwspec; |
173 | 262 | + int ret; | |
263 | + | ||
174 | +#ifdef CONFIG_HAS_PCI | 264 | +#ifdef CONFIG_HAS_PCI |
175 | + if ( dev_is_pci(dev) ) | 265 | + if ( dev_is_pci(dev) ) |
176 | + { | 266 | + { |
177 | + struct pci_dev *pdev = dev_to_pci(dev); | 267 | + struct pci_dev *pdev = dev_to_pci(dev); |
178 | + int ret; | 268 | + int ret; |
179 | + | 269 | + |
270 | + /* Ignore calls for phantom functions */ | ||
180 | + if ( devfn != pdev->devfn ) | 271 | + if ( devfn != pdev->devfn ) |
181 | + return 0; | 272 | + return 0; |
182 | + | 273 | + |
183 | + ret = iommu_add_pci_sideband_ids(pdev); | 274 | + ret = iommu_add_pci_sideband_ids(pdev); |
184 | + if ( ret < 0 ) | 275 | + if ( ret < 0 ) |
185 | + iommu_fwspec_free(dev); | 276 | + iommu_fwspec_free(dev); |
186 | + } | 277 | + } |
187 | +#endif | 278 | +#endif |
188 | + | 279 | |
189 | fwspec = dev_iommu_fwspec_get(dev); | 280 | fwspec = dev_iommu_fwspec_get(dev); |
190 | if (fwspec == NULL) | 281 | if (fwspec == NULL) |
282 | @@ -XXX,XX +XXX,XX @@ static int arm_smmu_dt_add_device_generic(u8 devfn, struct device *dev) | ||
283 | if (smmu == NULL) | ||
191 | return -ENXIO; | 284 | return -ENXIO; |
285 | |||
286 | - return arm_smmu_dt_add_device_legacy(smmu, dev, fwspec); | ||
287 | + ret = arm_smmu_dt_add_device_legacy(smmu, dev, fwspec); | ||
288 | + if ( ret ) | ||
289 | + return ret; | ||
290 | + | ||
291 | +#ifdef CONFIG_HAS_PCI | ||
292 | + if ( dev_is_pci(dev) ) | ||
293 | + { | ||
294 | + struct pci_dev *pdev = dev_to_pci(dev); | ||
295 | + | ||
296 | + /* | ||
297 | + * During PHYSDEVOP_pci_device_add, Xen does not assign the | ||
298 | + * device, so we must do it here. | ||
299 | + */ | ||
300 | + if ( pdev->domain ) | ||
301 | + ret = arm_smmu_assign_dev(pdev->domain, devfn, dev, 0); | ||
302 | + } | ||
303 | +#endif | ||
304 | + | ||
305 | + return ret; | ||
306 | } | ||
307 | |||
308 | static int arm_smmu_dt_xlate_generic(struct device *dev, | ||
192 | @@ -XXX,XX +XXX,XX @@ static struct arm_smmu_device *find_smmu_for_device(struct device *dev) | 309 | @@ -XXX,XX +XXX,XX @@ static struct arm_smmu_device *find_smmu_for_device(struct device *dev) |
193 | { | 310 | { |
194 | struct arm_smmu_device *smmu; | 311 | struct arm_smmu_device *smmu; |
195 | struct arm_smmu_master *master = NULL; | 312 | struct arm_smmu_master *master = NULL; |
196 | - struct device_node *dev_node = dev_get_dev_node(dev); | 313 | - struct device_node *dev_node = dev_get_dev_node(dev); |
... | ... | ||
219 | static int arm_smmu_add_device(struct device *dev) | 336 | static int arm_smmu_add_device(struct device *dev) |
220 | { | 337 | { |
221 | @@ -XXX,XX +XXX,XX @@ static int arm_smmu_add_device(struct device *dev) | 338 | @@ -XXX,XX +XXX,XX @@ static int arm_smmu_add_device(struct device *dev) |
222 | struct arm_smmu_master_cfg *cfg; | 339 | struct arm_smmu_master_cfg *cfg; |
223 | struct iommu_group *group; | 340 | struct iommu_group *group; |
224 | void (*releasefn)(void *) = NULL; | 341 | void (*releasefn)(void *data) = NULL; |
225 | - int ret; | 342 | - int ret; |
226 | 343 | ||
227 | smmu = find_smmu_for_device(dev); | 344 | smmu = find_smmu_for_device(dev); |
228 | if (!smmu) | 345 | if (!smmu) |
229 | return -ENODEV; | 346 | return -ENODEV; |
... | ... | ||
251 | @@ -XXX,XX +XXX,XX @@ static int arm_smmu_assign_dev(struct domain *d, u8 devfn, | 368 | @@ -XXX,XX +XXX,XX @@ static int arm_smmu_assign_dev(struct domain *d, u8 devfn, |
252 | return -ENOMEM; | 369 | return -ENOMEM; |
253 | } | 370 | } |
254 | 371 | ||
255 | +#ifdef CONFIG_HAS_PCI | 372 | +#ifdef CONFIG_HAS_PCI |
256 | + if ( dev_is_pci(dev) && !is_hardware_domain(d) ) | 373 | + if ( dev_is_pci(dev) ) |
257 | + { | 374 | + { |
258 | + struct pci_dev *pdev = dev_to_pci(dev); | 375 | + struct pci_dev *pdev = dev_to_pci(dev); |
259 | + | 376 | + |
260 | + printk(XENLOG_INFO "Assigning device %04x:%02x:%02x.%u to dom%d\n", | 377 | + /* Ignore calls for phantom functions */ |
261 | + pdev->seg, pdev->bus, PCI_SLOT(devfn), PCI_FUNC(devfn), | 378 | + if ( devfn != pdev->devfn ) |
262 | + d->domain_id); | ||
263 | + | ||
264 | + if ( devfn != pdev->devfn || pdev->domain == d ) | ||
265 | + return 0; | 379 | + return 0; |
266 | + | 380 | + |
267 | + list_move(&pdev->domain_list, &d->pdev_list); | 381 | + ASSERT(pcidevs_locked()); |
382 | + | ||
383 | + write_lock(&pdev->domain->pci_lock); | ||
384 | + list_del(&pdev->domain_list); | ||
385 | + write_unlock(&pdev->domain->pci_lock); | ||
386 | + | ||
268 | + pdev->domain = d; | 387 | + pdev->domain = d; |
388 | + | ||
389 | + write_lock(&d->pci_lock); | ||
390 | + list_add(&pdev->domain_list, &d->pdev_list); | ||
391 | + write_unlock(&d->pci_lock); | ||
269 | + | 392 | + |
270 | + /* dom_io is used as a sentinel for quarantined devices */ | 393 | + /* dom_io is used as a sentinel for quarantined devices */ |
271 | + if ( d == dom_io ) | 394 | + if ( d == dom_io ) |
395 | + { | ||
396 | + struct iommu_domain *domain = dev_iommu_domain(dev); | ||
397 | + if ( !iommu_quarantine ) | ||
398 | + return 0; | ||
399 | + | ||
400 | + if ( domain && domain->priv ) | ||
401 | + arm_smmu_deassign_dev(domain->priv->cfg.domain, devfn, dev); | ||
402 | + | ||
272 | + return 0; | 403 | + return 0; |
404 | + } | ||
273 | + } | 405 | + } |
274 | +#endif | 406 | +#endif |
275 | + | 407 | + |
276 | if (!dev_iommu_group(dev)) { | 408 | if (!dev_iommu_group(dev)) { |
277 | ret = arm_smmu_add_device(dev); | 409 | ret = arm_smmu_add_device(dev); |
278 | if (ret) | 410 | if (ret) |
279 | @@ -XXX,XX +XXX,XX @@ out: | 411 | @@ -XXX,XX +XXX,XX @@ out: |
280 | return ret; | 412 | return ret; |
281 | } | 413 | } |
282 | 414 | ||
283 | -static int arm_smmu_deassign_dev(struct domain *d, struct device *dev) | 415 | -static int arm_smmu_deassign_dev(struct domain *d, struct device *dev) |
284 | +static int arm_smmu_deassign_dev(struct domain *d, u8 devfn, struct device *dev) | 416 | +static int arm_smmu_deassign_dev(struct domain *d, uint8_t devfn, |
417 | + struct device *dev) | ||
285 | { | 418 | { |
286 | struct iommu_domain *domain = dev_iommu_domain(dev); | 419 | struct iommu_domain *domain = dev_iommu_domain(dev); |
287 | struct arm_smmu_xen_domain *xen_domain; | 420 | struct arm_smmu_xen_domain *xen_domain; |
288 | 421 | ||
289 | +#ifdef CONFIG_HAS_PCI | 422 | +#ifdef CONFIG_HAS_PCI |
290 | + if ( dev_is_pci(dev) ) | 423 | + if ( dev_is_pci(dev) ) |
291 | + { | 424 | + { |
292 | + struct pci_dev *pdev = dev_to_pci(dev); | 425 | + struct pci_dev *pdev = dev_to_pci(dev); |
293 | + | 426 | + |
294 | + printk(XENLOG_INFO "Deassigning device %04x:%02x:%02x.%u from dom%d\n", | 427 | + /* Ignore calls for phantom functions */ |
295 | + pdev->seg, pdev->bus, PCI_SLOT(devfn), PCI_FUNC(devfn), | ||
296 | + d->domain_id); | ||
297 | + | ||
298 | + if ( devfn != pdev->devfn ) | 428 | + if ( devfn != pdev->devfn ) |
299 | + return 0; | 429 | + return 0; |
300 | + | 430 | + |
301 | + /* dom_io is used as a sentinel for quarantined devices */ | 431 | + /* dom_io is used as a sentinel for quarantined devices */ |
302 | + if ( d == dom_io ) | 432 | + if ( d == dom_io ) |
... | ... | ||
306 | + | 436 | + |
307 | xen_domain = dom_iommu(d)->arch.priv; | 437 | xen_domain = dom_iommu(d)->arch.priv; |
308 | 438 | ||
309 | if (!domain || domain->priv->cfg.domain != d) { | 439 | if (!domain || domain->priv->cfg.domain != d) { |
310 | @@ -XXX,XX +XXX,XX @@ static int arm_smmu_reassign_dev(struct domain *s, struct domain *t, | 440 | @@ -XXX,XX +XXX,XX @@ static int arm_smmu_reassign_dev(struct domain *s, struct domain *t, |
441 | { | ||
311 | int ret = 0; | 442 | int ret = 0; |
312 | 443 | ||
313 | /* Don't allow remapping on other domain than hwdom */ | 444 | - /* Don't allow remapping on other domain than hwdom */ |
314 | - if ( t && !is_hardware_domain(t) ) | 445 | - if ( t && !is_hardware_domain(t) ) |
315 | + if ( t && !is_hardware_domain(t) && t != dom_io ) | 446 | + /* Don't allow remapping on other domain than hwdom |
447 | + * or dom_io for PCI devices | ||
448 | + */ | ||
449 | + if ( t && !is_hardware_domain(t) && (t != dom_io || !dev_is_pci(dev)) ) | ||
316 | return -EPERM; | 450 | return -EPERM; |
317 | 451 | ||
318 | if (t == s) | 452 | if (t == s) |
319 | return 0; | 453 | return 0; |
320 | 454 | ||
321 | - ret = arm_smmu_deassign_dev(s, dev); | 455 | - ret = arm_smmu_deassign_dev(s, dev); |
322 | + ret = arm_smmu_deassign_dev(s, devfn, dev); | 456 | + ret = arm_smmu_deassign_dev(s, devfn, dev); |
323 | if (ret) | 457 | if (ret) |
324 | return ret; | 458 | return ret; |
325 | 459 | ||
326 | -- | 460 | -- |
327 | 2.40.1 | 461 | 2.34.1 | diff view generated by jsdifflib |
1 | From: Rahul Singh <rahul.singh@arm.com> | 1 | From: Rahul Singh <rahul.singh@arm.com> |
---|---|---|---|
2 | |||
3 | Implement support for PCI devices in the SMMU driver. Trigger iommu-map | ||
4 | parsing when new PCI device is added. Add checks to assign/deassign | ||
5 | functions to ensure PCI devices are handled correctly. Implement basic | ||
6 | quarantining. | ||
7 | |||
8 | All pci devices are automatically assigned to hardware domain if it exists | ||
9 | to ensure it can probe them. | ||
10 | |||
11 | TODO: | ||
12 | Implement scratch page quarantining support. | ||
2 | 13 | ||
3 | Signed-off-by: Rahul Singh <rahul.singh@arm.com> | 14 | Signed-off-by: Rahul Singh <rahul.singh@arm.com> |
4 | Signed-off-by: Stewart Hildebrand <stewart.hildebrand@amd.com> | 15 | Signed-off-by: Stewart Hildebrand <stewart.hildebrand@amd.com> |
16 | Signed-off-by: Mykyta Poturai <mykyta_poturai@epam.com> | ||
5 | --- | 17 | --- |
18 | v6->v7: | ||
19 | * address TODO: use d->pci_lock in arm_smmu_assign_dev() | ||
20 | * remove !is_hardware_domain and pdev->domain == d checks in assign to | ||
21 | support future dom0less use case when dom0 is using vPCI | ||
22 | * check if pdev->domain exists before assigning to it | ||
23 | * don't print "" | ||
24 | * change assign logic to remove reassign reimplementation | ||
25 | * explain pdev->devfn check | ||
26 | * make reassign check stricter and update comment | ||
27 | |||
28 | v5->v6: | ||
29 | * check for hardware_domain == NULL (dom0less test case) | ||
30 | * locking: assign pdev->domain before list_add() | ||
31 | |||
32 | v4->v5: | ||
33 | * deassign from hwdom | ||
34 | * add TODO regarding locking | ||
35 | * fixup after dropping ("xen/arm: Move is_protected flag to struct device") | ||
36 | |||
6 | v3->v4: | 37 | v3->v4: |
7 | * no change | 38 | * no change |
8 | 39 | ||
9 | v2->v3: | 40 | v2->v3: |
10 | * rebase | 41 | * rebase |
... | ... | ||
27 | 58 | ||
28 | (cherry picked from commit 7ed6c3ab250d899fe6e893a514278e406a2893e8 from | 59 | (cherry picked from commit 7ed6c3ab250d899fe6e893a514278e406a2893e8 from |
29 | the downstream branch poc/pci-passthrough from | 60 | the downstream branch poc/pci-passthrough from |
30 | https://gitlab.com/xen-project/people/bmarquis/xen-arm-poc.git) | 61 | https://gitlab.com/xen-project/people/bmarquis/xen-arm-poc.git) |
31 | --- | 62 | --- |
32 | 63 | xen/drivers/passthrough/arm/smmu-v3.c | 117 +++++++++++++++++++++++--- | |
33 | This is a file imported from Linux with modifications for Xen. What should be | 64 | 1 file changed, 106 insertions(+), 11 deletions(-) |
34 | the coding style used for Xen modifications? | ||
35 | --- | ||
36 | xen/drivers/passthrough/arm/smmu-v3.c | 76 +++++++++++++++++++++++++-- | ||
37 | 1 file changed, 72 insertions(+), 4 deletions(-) | ||
38 | 65 | ||
39 | diff --git a/xen/drivers/passthrough/arm/smmu-v3.c b/xen/drivers/passthrough/arm/smmu-v3.c | 66 | diff --git a/xen/drivers/passthrough/arm/smmu-v3.c b/xen/drivers/passthrough/arm/smmu-v3.c |
40 | index XXXXXXX..XXXXXXX 100644 | 67 | index XXXXXXX..XXXXXXX 100644 |
41 | --- a/xen/drivers/passthrough/arm/smmu-v3.c | 68 | --- a/xen/drivers/passthrough/arm/smmu-v3.c |
42 | +++ b/xen/drivers/passthrough/arm/smmu-v3.c | 69 | +++ b/xen/drivers/passthrough/arm/smmu-v3.c |
43 | @@ -XXX,XX +XXX,XX @@ static bool arm_smmu_sid_in_range(struct arm_smmu_device *smmu, u32 sid) | 70 | @@ -XXX,XX +XXX,XX @@ static bool arm_smmu_sid_in_range(struct arm_smmu_device *smmu, u32 sid) |
44 | } | 71 | } |
45 | /* Forward declaration */ | 72 | /* Forward declaration */ |
46 | static struct arm_smmu_device *arm_smmu_get_by_dev(const struct device *dev); | 73 | static struct arm_smmu_device *arm_smmu_get_by_dev(const struct device *dev); |
47 | +static int arm_smmu_assign_dev(struct domain *d, u8 devfn, | 74 | +static int arm_smmu_assign_dev(struct domain *d, u8 devfn, struct device *dev, |
48 | + struct device *dev, u32 flag); | 75 | + u32 flag); |
76 | +static int arm_smmu_deassign_dev(struct domain *d, uint8_t devfn, | ||
77 | + struct device *dev); | ||
49 | 78 | ||
50 | static int arm_smmu_add_device(u8 devfn, struct device *dev) | 79 | static int arm_smmu_add_device(u8 devfn, struct device *dev) |
51 | { | 80 | { |
52 | int i, ret; | 81 | int i, ret; |
53 | struct arm_smmu_device *smmu; | 82 | struct arm_smmu_device *smmu; |
... | ... | ||
58 | +#ifdef CONFIG_HAS_PCI | 87 | +#ifdef CONFIG_HAS_PCI |
59 | + if ( dev_is_pci(dev) ) | 88 | + if ( dev_is_pci(dev) ) |
60 | + { | 89 | + { |
61 | + struct pci_dev *pdev = dev_to_pci(dev); | 90 | + struct pci_dev *pdev = dev_to_pci(dev); |
62 | + int ret; | 91 | + int ret; |
63 | + | 92 | + |
93 | + /* Ignore calls for phantom functions */ | ||
64 | + if ( devfn != pdev->devfn ) | 94 | + if ( devfn != pdev->devfn ) |
65 | + return 0; | 95 | + return 0; |
66 | + | 96 | + |
67 | + ret = iommu_add_pci_sideband_ids(pdev); | 97 | + ret = iommu_add_pci_sideband_ids(pdev); |
68 | + if ( ret < 0 ) | 98 | + if ( ret < 0 ) |
... | ... | ||
73 | + fwspec = dev_iommu_fwspec_get(dev); | 103 | + fwspec = dev_iommu_fwspec_get(dev); |
74 | if (!fwspec) | 104 | if (!fwspec) |
75 | return -ENODEV; | 105 | return -ENODEV; |
76 | 106 | ||
77 | @@ -XXX,XX +XXX,XX @@ static int arm_smmu_add_device(u8 devfn, struct device *dev) | 107 | @@ -XXX,XX +XXX,XX @@ static int arm_smmu_add_device(u8 devfn, struct device *dev) |
108 | */ | ||
109 | arm_smmu_enable_pasid(master); | ||
110 | |||
111 | - if (dt_device_is_protected(dev_to_dt(dev))) { | ||
112 | - dev_err(dev, "Already added to SMMUv3\n"); | ||
113 | - return -EEXIST; | ||
114 | - } | ||
115 | + if ( !dev_is_pci(dev) ) | ||
116 | + { | ||
117 | + if (dt_device_is_protected(dev_to_dt(dev))) { | ||
118 | + dev_err(dev, "Already added to SMMUv3\n"); | ||
119 | + return -EEXIST; | ||
120 | + } | ||
121 | |||
122 | - /* Let Xen know that the master device is protected by an IOMMU. */ | ||
123 | - dt_device_set_protected(dev_to_dt(dev)); | ||
124 | + /* Let Xen know that the master device is protected by an IOMMU. */ | ||
125 | + dt_device_set_protected(dev_to_dt(dev)); | ||
126 | + } | ||
127 | |||
78 | dev_info(dev, "Added master device (SMMUv3 %s StreamIds %u)\n", | 128 | dev_info(dev, "Added master device (SMMUv3 %s StreamIds %u)\n", |
79 | dev_name(fwspec->iommu_dev), fwspec->num_ids); | 129 | dev_name(fwspec->iommu_dev), fwspec->num_ids); |
80 | 130 | ||
81 | +#ifdef CONFIG_HAS_PCI | 131 | +#ifdef CONFIG_HAS_PCI |
82 | + if ( dev_is_pci(dev) ) | 132 | + if ( dev_is_pci(dev) ) |
83 | + { | 133 | + { |
84 | + struct pci_dev *pdev = dev_to_pci(dev); | 134 | + struct pci_dev *pdev = dev_to_pci(dev); |
85 | + | 135 | + |
86 | + ret = arm_smmu_assign_dev(pdev->domain, devfn, dev, 0); | 136 | + /* |
87 | + if (ret) | 137 | + * During PHYSDEVOP_pci_device_add, Xen does not assign the |
88 | + goto err_free_master; | 138 | + * device, so we must do it here. |
139 | + */ | ||
140 | + if ( pdev->domain ) | ||
141 | + { | ||
142 | + ret = arm_smmu_assign_dev(pdev->domain, devfn, dev, 0); | ||
143 | + if (ret) | ||
144 | + goto err_free_master; | ||
145 | + } | ||
89 | + } | 146 | + } |
90 | +#endif | 147 | +#endif |
91 | + | 148 | + |
92 | return 0; | 149 | return 0; |
93 | 150 | ||
94 | err_free_master: | 151 | err_free_master: |
95 | @@ -XXX,XX +XXX,XX @@ static int arm_smmu_assign_dev(struct domain *d, u8 devfn, | 152 | @@ -XXX,XX +XXX,XX @@ static int arm_smmu_assign_dev(struct domain *d, u8 devfn, |
96 | struct arm_smmu_domain *smmu_domain; | 153 | struct arm_smmu_domain *smmu_domain; |
97 | struct arm_smmu_xen_domain *xen_domain = dom_iommu(d)->arch.priv; | 154 | struct arm_smmu_xen_domain *xen_domain = dom_iommu(d)->arch.priv; |
98 | 155 | ||
99 | +#ifdef CONFIG_HAS_PCI | 156 | +#ifdef CONFIG_HAS_PCI |
100 | + if ( dev_is_pci(dev) && !is_hardware_domain(d) ) | 157 | + if ( dev_is_pci(dev) ) |
101 | + { | 158 | + { |
102 | + struct pci_dev *pdev = dev_to_pci(dev); | 159 | + struct pci_dev *pdev = dev_to_pci(dev); |
103 | + | 160 | + |
104 | + printk(XENLOG_INFO "Assigning device %04x:%02x:%02x.%u to dom%d\n", | 161 | + /* Ignore calls for phantom functions */ |
105 | + pdev->seg, pdev->bus, PCI_SLOT(devfn), | 162 | + if ( devfn != pdev->devfn ) |
106 | + PCI_FUNC(devfn), d->domain_id); | 163 | + return 0; |
107 | + | 164 | + |
108 | + if ( devfn != pdev->devfn || pdev->domain == d ) | 165 | + ASSERT(pcidevs_locked()); |
109 | + return 0; | 166 | + |
110 | + | 167 | + write_lock(&pdev->domain->pci_lock); |
111 | + list_move(&pdev->domain_list, &d->pdev_list); | 168 | + list_del(&pdev->domain_list); |
169 | + write_unlock(&pdev->domain->pci_lock); | ||
170 | + | ||
112 | + pdev->domain = d; | 171 | + pdev->domain = d; |
172 | + | ||
173 | + write_lock(&d->pci_lock); | ||
174 | + list_add(&pdev->domain_list, &d->pdev_list); | ||
175 | + write_unlock(&d->pci_lock); | ||
113 | + | 176 | + |
114 | + /* dom_io is used as a sentinel for quarantined devices */ | 177 | + /* dom_io is used as a sentinel for quarantined devices */ |
115 | + if ( d == dom_io ) | 178 | + if ( d == dom_io ) |
116 | + return 0; | 179 | + { |
180 | + struct arm_smmu_master *master = dev_iommu_priv_get(dev); | ||
181 | + if ( !iommu_quarantine ) | ||
182 | + return 0; | ||
183 | + | ||
184 | + if ( master && master->domain ) | ||
185 | + arm_smmu_deassign_dev(master->domain->d, devfn, dev); | ||
186 | + | ||
187 | + return 0; | ||
188 | + } | ||
117 | + } | 189 | + } |
118 | +#endif | 190 | +#endif |
119 | + | 191 | + |
120 | spin_lock(&xen_domain->lock); | 192 | spin_lock(&xen_domain->lock); |
121 | 193 | ||
... | ... | ||
136 | +#ifdef CONFIG_HAS_PCI | 208 | +#ifdef CONFIG_HAS_PCI |
137 | + if ( dev_is_pci(dev) ) | 209 | + if ( dev_is_pci(dev) ) |
138 | + { | 210 | + { |
139 | + struct pci_dev *pdev = dev_to_pci(dev); | 211 | + struct pci_dev *pdev = dev_to_pci(dev); |
140 | + | 212 | + |
141 | + printk(XENLOG_INFO "Deassigning device %04x:%02x:%02x.%u from dom%d\n", | 213 | + /* Ignore calls for phantom functions */ |
142 | + pdev->seg, pdev->bus, PCI_SLOT(devfn), | ||
143 | + PCI_FUNC(devfn), d->domain_id); | ||
144 | + | ||
145 | + if ( devfn != pdev->devfn ) | 214 | + if ( devfn != pdev->devfn ) |
146 | + return 0; | 215 | + return 0; |
147 | + | 216 | + |
148 | + /* dom_io is used as a sentinel for quarantined devices */ | 217 | + /* dom_io is used as a sentinel for quarantined devices */ |
149 | + if ( d == dom_io ) | 218 | + if ( d == dom_io ) |
... | ... | ||
153 | + | 222 | + |
154 | spin_lock(&xen_domain->lock); | 223 | spin_lock(&xen_domain->lock); |
155 | 224 | ||
156 | arm_smmu_detach_dev(master); | 225 | arm_smmu_detach_dev(master); |
157 | @@ -XXX,XX +XXX,XX @@ static int arm_smmu_reassign_dev(struct domain *s, struct domain *t, | 226 | @@ -XXX,XX +XXX,XX @@ static int arm_smmu_reassign_dev(struct domain *s, struct domain *t, |
227 | { | ||
158 | int ret = 0; | 228 | int ret = 0; |
159 | 229 | ||
160 | /* Don't allow remapping on other domain than hwdom */ | 230 | - /* Don't allow remapping on other domain than hwdom */ |
161 | - if ( t && !is_hardware_domain(t) ) | 231 | - if ( t && !is_hardware_domain(t) ) |
162 | + if ( t && !is_hardware_domain(t) && (t != dom_io) ) | 232 | + /* Don't allow remapping on other domain than hwdom |
233 | + * or dom_io for PCI devices | ||
234 | + */ | ||
235 | + if ( t && !is_hardware_domain(t) && (t != dom_io || !dev_is_pci(dev)) ) | ||
163 | return -EPERM; | 236 | return -EPERM; |
164 | 237 | ||
165 | if (t == s) | 238 | if (t == s) |
166 | return 0; | 239 | return 0; |
167 | 240 | ||
168 | - ret = arm_smmu_deassign_dev(s, dev); | 241 | - ret = arm_smmu_deassign_dev(s, dev); |
169 | + ret = arm_smmu_deassign_dev(s, devfn, dev); | 242 | + ret = arm_smmu_deassign_dev(s, devfn, dev); |
170 | if (ret) | 243 | if (ret) |
171 | return ret; | 244 | return ret; |
172 | 245 | ||
173 | -- | 246 | -- |
174 | 2.40.1 | 247 | 2.34.1 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Rahul Singh <rahul.singh@arm.com> | ||
1 | 2 | ||
3 | Current code skip the mapping for PCI bridge MMIO region to dom0 when | ||
4 | pci_passthrough_enabled flag is set. Mapping should be skip when | ||
5 | has_vpci(d) is enabled for the domain, as we need to skip the mapping | ||
6 | only when VPCI handler are registered for ECAM. | ||
7 | |||
8 | Signed-off-by: Rahul Singh <rahul.singh@arm.com> | ||
9 | Signed-off-by: Stewart Hildebrand <stewart.hildebrand@amd.com> | ||
10 | Acked-by: Julien Grall <jgrall@amazon.com> | ||
11 | --- | ||
12 | This patch was originally picked up from [1] | ||
13 | v6->v7: | ||
14 | * add Julien's A-b | ||
15 | |||
16 | v5->v6: | ||
17 | * drop unrelated change in xen/arch/arm/domain_build.c:handle_linux_pci_domain() | ||
18 | |||
19 | v4->v5: | ||
20 | * new patch | ||
21 | |||
22 | changes since picking up from [1]: | ||
23 | * rebase on top of "dynamic node programming using overlay dtbo" series | ||
24 | * replace !is_pci_passthrough_enabled() check with !IS_ENABLED(CONFIG_HAS_PCI) | ||
25 | instead of removing | ||
26 | |||
27 | [1] https://lists.xenproject.org/archives/html/xen-devel/2023-07/msg00483.html | ||
28 | --- | ||
29 | xen/arch/arm/device.c | 2 +- | ||
30 | 1 file changed, 1 insertion(+), 1 deletion(-) | ||
31 | |||
32 | diff --git a/xen/arch/arm/device.c b/xen/arch/arm/device.c | ||
33 | index XXXXXXX..XXXXXXX 100644 | ||
34 | --- a/xen/arch/arm/device.c | ||
35 | +++ b/xen/arch/arm/device.c | ||
36 | @@ -XXX,XX +XXX,XX @@ int handle_device(struct domain *d, struct dt_device_node *dev, p2m_type_t p2mt, | ||
37 | .d = d, | ||
38 | .p2mt = p2mt, | ||
39 | .skip_mapping = !own_device || | ||
40 | - (is_pci_passthrough_enabled() && | ||
41 | + (has_vpci(d) && | ||
42 | (device_get_class(dev) == DEVICE_PCI_HOSTBRIDGE)), | ||
43 | .iomem_ranges = iomem_ranges, | ||
44 | .irq_ranges = irq_ranges | ||
45 | -- | ||
46 | 2.34.1 | diff view generated by jsdifflib |
1 | From: Oleksandr Tyshchenko <oleksandr_tyshchenko@epam.com> | 1 | From: Stewart Hildebrand <stewart.hildebrand@amd.com> |
---|---|---|---|
2 | 2 | ||
3 | This flag will be re-used for PCI devices by the subsequent | 3 | Enable the use of IOMMU + PCI in dom0 without having to specify |
4 | patches. | 4 | "pci-passthrough=yes". We rely on dom0 to initialize the PCI controller |
5 | and perform a PHYSDEVOP_pci_device_add call to add each device to SMMU. | ||
5 | 6 | ||
6 | Signed-off-by: Oleksandr Tyshchenko <oleksandr_tyshchenko@epam.com> | 7 | Enable pci_init() for initializing Xen's internal PCI subsystem, and |
8 | allow PHYSDEVOP_pci_device_add when pci-passthrough is disabled. | ||
9 | |||
10 | is_pci_passthrough_enabled() is not an Arm-only construct, so remove the | ||
11 | x86 definition of the function. | ||
12 | |||
7 | Signed-off-by: Stewart Hildebrand <stewart.hildebrand@amd.com> | 13 | Signed-off-by: Stewart Hildebrand <stewart.hildebrand@amd.com> |
14 | Signed-off-by: Mykyta Poturai <mykyta_poturai@epam.com> | ||
8 | --- | 15 | --- |
9 | v3->v4: | 16 | hmm. Since |
10 | * move is_protected flag within struct device to reduce padding | 17 | dec9e02f3190 ("xen: avoid generation of stub <asm/pci.h> header") |
11 | * re-add device_is_protected checks in add_device hooks in smmu-v3.c/ipmmu-vmsa.c | 18 | Should we also move is_pci_passthrough_enabled() back to xen/arch/arm/include/asm/pci.h ? |
12 | * split mmu-masters check into separate patch | 19 | Not sure if PPC/RISC-V will plan on using this check. |
13 | 20 | ||
14 | v2->v3: | 21 | v6->v7: |
15 | * no change | 22 | * remove x86 definition of is_pci_passthrough_enabled() |
23 | * update comments | ||
24 | * make pci_physdev_op checks stricter | ||
16 | 25 | ||
17 | v1->v2: | 26 | v5->v6: |
18 | * no change | 27 | * new patch - this effectively replaces |
28 | ("Revert "xen/arm: Add cmdline boot option "pci-passthrough = <boolean>""") | ||
29 | --- | ||
30 | xen/arch/arm/pci/pci.c | 5 +++-- | ||
31 | xen/arch/x86/include/asm/pci.h | 6 ------ | ||
32 | xen/drivers/pci/physdev.c | 4 ++-- | ||
33 | 3 files changed, 5 insertions(+), 10 deletions(-) | ||
19 | 34 | ||
20 | downstream->v1: | 35 | diff --git a/xen/arch/arm/pci/pci.c b/xen/arch/arm/pci/pci.c |
21 | * rebase | ||
22 | * s/dev_node->is_protected/dev_node->dev.is_protected/ in smmu.c | ||
23 | * s/dt_device_set_protected(dev_to_dt(dev))/device_set_protected(dev)/ in smmu-v3.c | ||
24 | * remove redundant device_is_protected checks in smmu-v3.c/ipmmu-vmsa.c | ||
25 | |||
26 | (cherry picked from commit 59753aac77528a584d3950936b853ebf264b68e7 from | ||
27 | the downstream branch poc/pci-passthrough from | ||
28 | https://gitlab.com/xen-project/people/bmarquis/xen-arm-poc.git) | ||
29 | --- | ||
30 | xen/arch/arm/domain_build.c | 4 ++-- | ||
31 | xen/arch/arm/include/asm/device.h | 14 ++++++++++++++ | ||
32 | xen/common/device_tree.c | 2 +- | ||
33 | xen/drivers/passthrough/arm/ipmmu-vmsa.c | 4 ++-- | ||
34 | xen/drivers/passthrough/arm/smmu-v3.c | 5 +++-- | ||
35 | xen/drivers/passthrough/arm/smmu.c | 2 +- | ||
36 | xen/drivers/passthrough/device_tree.c | 8 ++++---- | ||
37 | xen/include/xen/device_tree.h | 13 ------------- | ||
38 | 8 files changed, 27 insertions(+), 25 deletions(-) | ||
39 | |||
40 | diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c | ||
41 | index XXXXXXX..XXXXXXX 100644 | 36 | index XXXXXXX..XXXXXXX 100644 |
42 | --- a/xen/arch/arm/domain_build.c | 37 | --- a/xen/arch/arm/pci/pci.c |
43 | +++ b/xen/arch/arm/domain_build.c | 38 | +++ b/xen/arch/arm/pci/pci.c |
44 | @@ -XXX,XX +XXX,XX @@ static int __init handle_device(struct domain *d, struct dt_device_node *dev, | 39 | @@ -XXX,XX +XXX,XX @@ |
45 | return res; | 40 | #include <xen/device_tree.h> |
46 | } | 41 | #include <xen/errno.h> |
47 | 42 | #include <xen/init.h> | |
48 | - if ( dt_device_is_protected(dev) ) | 43 | +#include <xen/iommu.h> |
49 | + if ( device_is_protected(dt_to_dev(dev)) ) | 44 | #include <xen/param.h> |
50 | { | 45 | #include <xen/pci.h> |
51 | dt_dprintk("%s setup iommu\n", dt_node_full_name(dev)); | 46 | |
52 | res = iommu_assign_dt_device(d, dev); | 47 | @@ -XXX,XX +XXX,XX @@ static int __init pci_init(void) |
53 | @@ -XXX,XX +XXX,XX @@ static int __init handle_passthrough_prop(struct kernel_info *kinfo, | 48 | { |
54 | return res; | 49 | /* |
55 | 50 | * Enable PCI passthrough when has been enabled explicitly | |
56 | /* If xen_force, we allow assignment of devices without IOMMU protection. */ | 51 | - * (pci-passthrough=on). |
57 | - if ( xen_force && !dt_device_is_protected(node) ) | 52 | + * (pci-passthrough=on) or IOMMU is present and enabled. |
58 | + if ( xen_force && !device_is_protected(dt_to_dev(node)) ) | 53 | */ |
54 | - if ( !pci_passthrough_enabled ) | ||
55 | + if ( !is_pci_passthrough_enabled() && !iommu_enabled ) | ||
59 | return 0; | 56 | return 0; |
60 | 57 | ||
61 | return iommu_assign_dt_device(kinfo->d, node); | 58 | pci_segments_init(); |
62 | diff --git a/xen/arch/arm/include/asm/device.h b/xen/arch/arm/include/asm/device.h | 59 | diff --git a/xen/arch/x86/include/asm/pci.h b/xen/arch/x86/include/asm/pci.h |
63 | index XXXXXXX..XXXXXXX 100644 | 60 | index XXXXXXX..XXXXXXX 100644 |
64 | --- a/xen/arch/arm/include/asm/device.h | 61 | --- a/xen/arch/x86/include/asm/pci.h |
65 | +++ b/xen/arch/arm/include/asm/device.h | 62 | +++ b/xen/arch/x86/include/asm/pci.h |
66 | @@ -XXX,XX +XXX,XX @@ | 63 | @@ -XXX,XX +XXX,XX @@ bool pci_ro_mmcfg_decode(unsigned long mfn, unsigned int *seg, |
67 | #ifndef __ASM_ARM_DEVICE_H | 64 | extern int pci_mmcfg_config_num; |
68 | #define __ASM_ARM_DEVICE_H | 65 | extern struct acpi_mcfg_allocation *pci_mmcfg_config; |
69 | 66 | ||
70 | +#include <xen/types.h> | 67 | -/* Unlike ARM, PCI passthrough is always enabled for x86. */ |
71 | + | 68 | -static always_inline bool is_pci_passthrough_enabled(void) |
72 | enum device_type | ||
73 | { | ||
74 | DEV_DT, | ||
75 | @@ -XXX,XX +XXX,XX @@ struct dev_archdata { | ||
76 | struct device | ||
77 | { | ||
78 | enum device_type type; | ||
79 | + bool is_protected; /* Shows that device is protected by IOMMU */ | ||
80 | + uint8_t _pad[3]; | ||
81 | #ifdef CONFIG_HAS_DEVICE_TREE | ||
82 | struct dt_device_node *of_node; /* Used by drivers imported from Linux */ | ||
83 | #endif | ||
84 | @@ -XXX,XX +XXX,XX @@ int device_init(struct dt_device_node *dev, enum device_class class, | ||
85 | */ | ||
86 | enum device_class device_get_class(const struct dt_device_node *dev); | ||
87 | |||
88 | +static inline void device_set_protected(struct device *device) | ||
89 | +{ | ||
90 | + device->is_protected = true; | ||
91 | +} | ||
92 | + | ||
93 | +static inline bool device_is_protected(const struct device *device) | ||
94 | +{ | ||
95 | + return device->is_protected; | ||
96 | +} | ||
97 | + | ||
98 | #define DT_DEVICE_START(_name, _namestr, _class) \ | ||
99 | static const struct device_desc __dev_desc_##_name __used \ | ||
100 | __section(".dev.info") = { \ | ||
101 | diff --git a/xen/common/device_tree.c b/xen/common/device_tree.c | ||
102 | index XXXXXXX..XXXXXXX 100644 | ||
103 | --- a/xen/common/device_tree.c | ||
104 | +++ b/xen/common/device_tree.c | ||
105 | @@ -XXX,XX +XXX,XX @@ static unsigned long __init unflatten_dt_node(const void *fdt, | ||
106 | /* By default dom0 owns the device */ | ||
107 | np->used_by = 0; | ||
108 | /* By default the device is not protected */ | ||
109 | - np->is_protected = false; | ||
110 | + np->dev.is_protected = false; | ||
111 | INIT_LIST_HEAD(&np->domain_list); | ||
112 | |||
113 | if ( new_format ) | ||
114 | diff --git a/xen/drivers/passthrough/arm/ipmmu-vmsa.c b/xen/drivers/passthrough/arm/ipmmu-vmsa.c | ||
115 | index XXXXXXX..XXXXXXX 100644 | ||
116 | --- a/xen/drivers/passthrough/arm/ipmmu-vmsa.c | ||
117 | +++ b/xen/drivers/passthrough/arm/ipmmu-vmsa.c | ||
118 | @@ -XXX,XX +XXX,XX @@ static int ipmmu_add_device(u8 devfn, struct device *dev) | ||
119 | if ( !to_ipmmu(dev) ) | ||
120 | return -ENODEV; | ||
121 | |||
122 | - if ( dt_device_is_protected(dev_to_dt(dev)) ) | ||
123 | + if ( device_is_protected(dev) ) | ||
124 | { | ||
125 | dev_err(dev, "Already added to IPMMU\n"); | ||
126 | return -EEXIST; | ||
127 | } | ||
128 | |||
129 | /* Let Xen know that the master device is protected by an IOMMU. */ | ||
130 | - dt_device_set_protected(dev_to_dt(dev)); | ||
131 | + device_set_protected(dev); | ||
132 | |||
133 | dev_info(dev, "Added master device (IPMMU %s micro-TLBs %u)\n", | ||
134 | dev_name(fwspec->iommu_dev), fwspec->num_ids); | ||
135 | diff --git a/xen/drivers/passthrough/arm/smmu-v3.c b/xen/drivers/passthrough/arm/smmu-v3.c | ||
136 | index XXXXXXX..XXXXXXX 100644 | ||
137 | --- a/xen/drivers/passthrough/arm/smmu-v3.c | ||
138 | +++ b/xen/drivers/passthrough/arm/smmu-v3.c | ||
139 | @@ -XXX,XX +XXX,XX @@ static int arm_smmu_add_device(u8 devfn, struct device *dev) | ||
140 | */ | ||
141 | arm_smmu_enable_pasid(master); | ||
142 | |||
143 | - if (dt_device_is_protected(dev_to_dt(dev))) { | ||
144 | + if ( device_is_protected(dev) ) | ||
145 | + { | ||
146 | dev_err(dev, "Already added to SMMUv3\n"); | ||
147 | return -EEXIST; | ||
148 | } | ||
149 | |||
150 | /* Let Xen know that the master device is protected by an IOMMU. */ | ||
151 | - dt_device_set_protected(dev_to_dt(dev)); | ||
152 | + device_set_protected(dev); | ||
153 | |||
154 | dev_info(dev, "Added master device (SMMUv3 %s StreamIds %u)\n", | ||
155 | dev_name(fwspec->iommu_dev), fwspec->num_ids); | ||
156 | diff --git a/xen/drivers/passthrough/arm/smmu.c b/xen/drivers/passthrough/arm/smmu.c | ||
157 | index XXXXXXX..XXXXXXX 100644 | ||
158 | --- a/xen/drivers/passthrough/arm/smmu.c | ||
159 | +++ b/xen/drivers/passthrough/arm/smmu.c | ||
160 | @@ -XXX,XX +XXX,XX @@ static int arm_smmu_dt_add_device_legacy(struct arm_smmu_device *smmu, | ||
161 | master->of_node = dev_node; | ||
162 | |||
163 | /* Xen: Let Xen know that the device is protected by an SMMU */ | ||
164 | - dt_device_set_protected(dev_node); | ||
165 | + device_set_protected(dev); | ||
166 | |||
167 | for (i = 0; i < fwspec->num_ids; ++i) { | ||
168 | if (!(smmu->features & ARM_SMMU_FEAT_STREAM_MATCH) && | ||
169 | diff --git a/xen/drivers/passthrough/device_tree.c b/xen/drivers/passthrough/device_tree.c | ||
170 | index XXXXXXX..XXXXXXX 100644 | ||
171 | --- a/xen/drivers/passthrough/device_tree.c | ||
172 | +++ b/xen/drivers/passthrough/device_tree.c | ||
173 | @@ -XXX,XX +XXX,XX @@ int iommu_assign_dt_device(struct domain *d, struct dt_device_node *dev) | ||
174 | if ( !is_iommu_enabled(d) ) | ||
175 | return -EINVAL; | ||
176 | |||
177 | - if ( !dt_device_is_protected(dev) ) | ||
178 | + if ( !device_is_protected(dt_to_dev(dev)) ) | ||
179 | return -EINVAL; | ||
180 | |||
181 | spin_lock(&dtdevs_lock); | ||
182 | @@ -XXX,XX +XXX,XX @@ int iommu_deassign_dt_device(struct domain *d, struct dt_device_node *dev) | ||
183 | if ( !is_iommu_enabled(d) ) | ||
184 | return -EINVAL; | ||
185 | |||
186 | - if ( !dt_device_is_protected(dev) ) | ||
187 | + if ( !device_is_protected(dt_to_dev(dev)) ) | ||
188 | return -EINVAL; | ||
189 | |||
190 | spin_lock(&dtdevs_lock); | ||
191 | @@ -XXX,XX +XXX,XX @@ static bool_t iommu_dt_device_is_assigned(const struct dt_device_node *dev) | ||
192 | { | ||
193 | bool_t assigned = 0; | ||
194 | |||
195 | - if ( !dt_device_is_protected(dev) ) | ||
196 | + if ( !device_is_protected(dt_to_dev(dev)) ) | ||
197 | return 0; | ||
198 | |||
199 | spin_lock(&dtdevs_lock); | ||
200 | @@ -XXX,XX +XXX,XX @@ int iommu_add_dt_device(struct dt_device_node *np) | ||
201 | * and iommus property, there is no need to register it again. In this case | ||
202 | * simply return success early. | ||
203 | */ | ||
204 | - if ( dt_device_is_protected(np) ) | ||
205 | + if ( device_is_protected(dev) ) | ||
206 | return 0; | ||
207 | |||
208 | if ( dev_iommu_fwspec_get(dev) ) | ||
209 | diff --git a/xen/include/xen/device_tree.h b/xen/include/xen/device_tree.h | ||
210 | index XXXXXXX..XXXXXXX 100644 | ||
211 | --- a/xen/include/xen/device_tree.h | ||
212 | +++ b/xen/include/xen/device_tree.h | ||
213 | @@ -XXX,XX +XXX,XX @@ struct dt_device_node { | ||
214 | struct dt_device_node *next; /* TODO: Remove it. Only use to know the last children */ | ||
215 | struct dt_device_node *allnext; | ||
216 | |||
217 | - /* IOMMU specific fields */ | ||
218 | - bool is_protected; | ||
219 | - | ||
220 | /* HACK: Remove this if there is a need of space */ | ||
221 | bool_t static_evtchn_created; | ||
222 | |||
223 | @@ -XXX,XX +XXX,XX @@ static inline domid_t dt_device_used_by(const struct dt_device_node *device) | ||
224 | return device->used_by; | ||
225 | } | ||
226 | |||
227 | -static inline void dt_device_set_protected(struct dt_device_node *device) | ||
228 | -{ | 69 | -{ |
229 | - device->is_protected = true; | 70 | - return true; |
230 | -} | 71 | -} |
231 | - | 72 | - |
232 | -static inline bool dt_device_is_protected(const struct dt_device_node *device) | 73 | void arch_pci_init_pdev(struct pci_dev *pdev); |
233 | -{ | 74 | |
234 | - return device->is_protected; | 75 | static inline bool pci_check_bar(const struct pci_dev *pdev, |
235 | -} | 76 | diff --git a/xen/drivers/pci/physdev.c b/xen/drivers/pci/physdev.c |
236 | - | 77 | index XXXXXXX..XXXXXXX 100644 |
237 | static inline bool_t dt_property_name_is_equal(const struct dt_property *pp, | 78 | --- a/xen/drivers/pci/physdev.c |
238 | const char *name) | 79 | +++ b/xen/drivers/pci/physdev.c |
239 | { | 80 | @@ -XXX,XX +XXX,XX @@ ret_t pci_physdev_op(int cmd, XEN_GUEST_HANDLE_PARAM(void) arg) |
81 | struct pci_dev_info pdev_info; | ||
82 | nodeid_t node = NUMA_NO_NODE; | ||
83 | |||
84 | - if ( !is_pci_passthrough_enabled() ) | ||
85 | + if ( !is_pci_passthrough_enabled() && !iommu_enabled ) | ||
86 | return -EOPNOTSUPP; | ||
87 | |||
88 | ret = -EFAULT; | ||
89 | @@ -XXX,XX +XXX,XX @@ ret_t pci_physdev_op(int cmd, XEN_GUEST_HANDLE_PARAM(void) arg) | ||
90 | case PHYSDEVOP_pci_device_remove: { | ||
91 | struct physdev_pci_device dev; | ||
92 | |||
93 | - if ( !is_pci_passthrough_enabled() ) | ||
94 | + if ( !is_pci_passthrough_enabled() && !iommu_enabled ) | ||
95 | return -EOPNOTSUPP; | ||
96 | |||
97 | ret = -EFAULT; | ||
240 | -- | 98 | -- |
241 | 2.40.1 | 99 | 2.34.1 | diff view generated by jsdifflib |
1 | From: Oleksandr Tyshchenko <oleksandr_tyshchenko@epam.com> | 1 | From: Rahul Singh <rahul.singh@arm.com> |
---|---|---|---|
2 | 2 | ||
3 | Improve readability of check for devices already registered with the SMMU with | 3 | When ITS is enabled and PCI devices that are behind an SMMU generate an |
4 | legacy mmu-masters DT bindings by using is_protected. | 4 | MSI interrupt, SMMU fault will be observed as there is currently no |
5 | mapping in p2m table for the ITS translation register (GITS_TRANSLATER). | ||
5 | 6 | ||
6 | There are 2 device tree bindings for registering a device with the SMMU: | 7 | A mapping is required in the iommu page tables so that the device can |
7 | * mmu-masters (legacy, SMMUv1/2 only) | 8 | generate the MSI interrupt writing to the GITS_TRANSLATER register. |
8 | * iommus | ||
9 | 9 | ||
10 | A device tree may include both mmu-masters and iommus properties (although it is | 10 | The GITS_TRANSLATER register is a 32-bit register, and there is nothing |
11 | unnecessary to do so). When a device appears in the mmu-masters list, | 11 | else in a page containing it, so map that page. |
12 | np->is_protected and dev->iommu_fwspec both get set by the SMMUv1/2 driver. The | ||
13 | function iommu_add_dt_device() is subsequently invoked for devices that have an | ||
14 | iommus specification. | ||
15 | 12 | ||
16 | The check as it was before this patch: | 13 | Signed-off-by: Rahul Singh <rahul.singh@arm.com> |
14 | Signed-off-by: Stewart Hildebrand <stewart.hildebrand@amd.com> | ||
15 | Signed-off-by: Mykyta Poturai <mykyta_poturai@epam.com> | ||
16 | --- | ||
17 | This patch was originally picked up from [1], and commit description | ||
18 | loosely borrowed from [2]. | ||
17 | 19 | ||
18 | if ( dev_iommu_fwspec_get(dev) ) | 20 | Example SMMUv3 fault (qemu-system-aarch64 virt model), ITS base 0x8080000: |
19 | return 0; | ||
20 | 21 | ||
21 | and the new check: | 22 | (XEN) SMMUv3: /smmuv3@9050000: event 0x10 received: |
23 | (XEN) SMMUv3: /smmuv3@9050000: 0x0000000800000010 | ||
24 | (XEN) SMMUv3: /smmuv3@9050000: 0x0000008000000000 | ||
25 | (XEN) SMMUv3: /smmuv3@9050000: 0x0000000008090040 | ||
26 | (XEN) SMMUv3: /smmuv3@9050000: 0x0000000000000000 | ||
22 | 27 | ||
23 | if ( dt_device_is_protected(np) ) | 28 | Example SMMUv2 fault (AMD/Xilinx Versal), ITS base 0xf9020000: |
24 | return 0; | ||
25 | 29 | ||
26 | are guarding against the same corner case: when a device has both mmu-masters | 30 | (XEN) smmu: /axi/smmu@fd800000: Unhandled context fault: fsr=0x402, iova=0xf9030040, fsynr=0x12, cb=0 |
27 | and iommus specifications in the device tree. The is_protected naming is more | ||
28 | descriptive. | ||
29 | 31 | ||
30 | If np->is_protected is not set (i.e. false), but dev->iommu_fwspec is set, it is | 32 | v6->v7: |
31 | an error condition, so return an error in this case. | 33 | * add tlb flush after mapping |
34 | * style: update formatting | ||
35 | * revert back to printk with XENLOG_G_ERR | ||
32 | 36 | ||
33 | Expand the comment to further clarify the corner case. | 37 | v5->v6: |
38 | * switch to iommu_map() interface | ||
39 | * fix page_count argument | ||
40 | * style fixup | ||
41 | * use gprintk instead of printk | ||
42 | * add my Signed-off-by | ||
43 | * move to vgic_v3_its_init_virtual() | ||
34 | 44 | ||
35 | Signed-off-by: Oleksandr Tyshchenko <oleksandr_tyshchenko@epam.com> | 45 | v4->v5: |
36 | Signed-off-by: Stewart Hildebrand <stewart.hildebrand@amd.com> | 46 | * new patch |
47 | |||
48 | [1] https://lists.xenproject.org/archives/html/xen-devel/2023-07/msg00483.html | ||
49 | [2] https://gitlab.com/xen-project/people/bmarquis/xen-arm-poc/-/commit/6232a0d53377009bb7fbc3c3ab81d0153734be6b | ||
37 | --- | 50 | --- |
38 | v3->v4: | 51 | xen/arch/arm/vgic-v3-its.c | 20 ++++++++++++++++++++ |
39 | * new patch: this change was split from ("xen/arm: Move is_protected flag to struct device") | 52 | 1 file changed, 20 insertions(+) |
40 | --- | ||
41 | xen/drivers/passthrough/device_tree.c | 11 ++++++++--- | ||
42 | 1 file changed, 8 insertions(+), 3 deletions(-) | ||
43 | 53 | ||
44 | diff --git a/xen/drivers/passthrough/device_tree.c b/xen/drivers/passthrough/device_tree.c | 54 | diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c |
45 | index XXXXXXX..XXXXXXX 100644 | 55 | index XXXXXXX..XXXXXXX 100644 |
46 | --- a/xen/drivers/passthrough/device_tree.c | 56 | --- a/xen/arch/arm/vgic-v3-its.c |
47 | +++ b/xen/drivers/passthrough/device_tree.c | 57 | +++ b/xen/arch/arm/vgic-v3-its.c |
48 | @@ -XXX,XX +XXX,XX @@ int iommu_add_dt_device(struct dt_device_node *np) | 58 | @@ -XXX,XX +XXX,XX @@ static int vgic_v3_its_init_virtual(struct domain *d, paddr_t guest_addr, |
49 | return -EINVAL; | 59 | |
50 | 60 | register_mmio_handler(d, &vgic_its_mmio_handler, guest_addr, SZ_64K, its); | |
51 | /* | 61 | |
52 | - * The device may already have been registered. As there is no harm in | 62 | + if ( is_iommu_enabled(its->d) ) |
53 | - * it just return success early. | 63 | + { |
54 | + * Devices that appear in the legacy mmu-masters list may have already been | 64 | + mfn_t mfn = maddr_to_mfn(its->doorbell_address); |
55 | + * registered with the SMMU. In case a device has both a mmu-masters entry | 65 | + unsigned int flush_flags = 0; |
56 | + * and iommus property, there is no need to register it again. In this case | 66 | + int ret = iommu_map(its->d, _dfn(mfn_x(mfn)), mfn, 1, IOMMUF_writable, |
57 | + * simply return success early. | 67 | + &flush_flags); |
58 | */ | ||
59 | - if ( dev_iommu_fwspec_get(dev) ) | ||
60 | + if ( dt_device_is_protected(np) ) | ||
61 | return 0; | ||
62 | |||
63 | + if ( dev_iommu_fwspec_get(dev) ) | ||
64 | + return -EEXIST; | ||
65 | + | 68 | + |
66 | /* | 69 | + if ( ret < 0 ) |
67 | * According to the Documentation/devicetree/bindings/iommu/iommu.txt | 70 | + { |
68 | * from Linux. | 71 | + printk(XENLOG_G_ERR |
72 | + "GICv3: Map ITS translation register for %pd failed.\n", | ||
73 | + its->d); | ||
74 | + return ret; | ||
75 | + } | ||
76 | + | ||
77 | + ret = iommu_iotlb_flush(its->d, _dfn(mfn_x(mfn)), 1, flush_flags); | ||
78 | + if ( ret < 0 ) | ||
79 | + return ret; | ||
80 | + } | ||
81 | + | ||
82 | /* Register the virtual ITS to be able to clean it up later. */ | ||
83 | list_add_tail(&its->vits_list, &d->arch.vgic.vits_list); | ||
84 | |||
69 | -- | 85 | -- |
70 | 2.40.1 | 86 | 2.34.1 | diff view generated by jsdifflib |