@@ -2049,18 +2049,48 @@ int of_find_last_cache_level(unsigned int cpu)
20492049 return cache_level ;
20502050}
20512051
2052+ /*
2053+ * Some DTs have an iommu-map targeting a 2-cell IOMMU node while
2054+ * specifying only 1 cell. Fortunately they all consist of value '1'
2055+ * as the 2nd cell entry with the same target, so check for that pattern.
2056+ *
2057+ * Example:
2058+ * IOMMU node:
2059+ * #iommu-cells = <2>;
2060+ *
2061+ * Device node:
2062+ * iommu-map = <0x0000 &smmu 0x0000 0x1>,
2063+ * <0x0100 &smmu 0x0100 0x1>;
2064+ */
2065+ static bool of_check_bad_map (const __be32 * map , int len )
2066+ {
2067+ __be32 phandle = map [1 ];
2068+
2069+ if (len % 4 )
2070+ return false;
2071+ for (int i = 0 ; i < len ; i += 4 ) {
2072+ if (map [i + 1 ] != phandle || map [i + 3 ] != cpu_to_be32 (1 ))
2073+ return false;
2074+ }
2075+ return true;
2076+ }
2077+
20522078/**
20532079 * of_map_id - Translate an ID through a downstream mapping.
20542080 * @np: root complex device node.
20552081 * @id: device ID to map.
20562082 * @map_name: property name of the map to use.
2083+ * @cells_name: property name of target specifier cells.
20572084 * @map_mask_name: optional property name of the mask to use.
20582085 * @filter_np: optional device node to filter matches by, or NULL to match any.
20592086 * If non-NULL, only map entries targeting this node will be matched.
20602087 * @arg: pointer to a &struct of_phandle_args for the result. On success,
2061- * @arg->args[0] will contain the translated ID. If a map entry was
2062- * matched, @arg->np will be set to the target node with a reference
2063- * held that the caller must release with of_node_put().
2088+ * @arg->args_count will be set to the number of output specifier cells
2089+ * as defined by @cells_name in the target node, and
2090+ * @arg->args[0..args_count-1] will contain the translated output
2091+ * specifier values. If a map entry was matched, @arg->np will be set
2092+ * to the target node with a reference held that the caller must release
2093+ * with of_node_put().
20642094 *
20652095 * Given a device ID, look up the appropriate implementation-defined
20662096 * platform ID and/or the target device which receives transactions on that
@@ -2069,17 +2099,19 @@ int of_find_last_cache_level(unsigned int cpu)
20692099 * Return: 0 on success or a standard error code on failure.
20702100 */
20712101int of_map_id (const struct device_node * np , u32 id ,
2072- const char * map_name , const char * map_mask_name ,
2102+ const char * map_name , const char * cells_name ,
2103+ const char * map_mask_name ,
20732104 const struct device_node * filter_np , struct of_phandle_args * arg )
20742105{
20752106 u32 map_mask , masked_id ;
2076- int map_len ;
2107+ int map_bytes , map_len , offset = 0 ;
2108+ bool bad_map = false;
20772109 const __be32 * map = NULL ;
20782110
20792111 if (!np || !map_name || !arg )
20802112 return - EINVAL ;
20812113
2082- map = of_get_property (np , map_name , & map_len );
2114+ map = of_get_property (np , map_name , & map_bytes );
20832115 if (!map ) {
20842116 if (filter_np )
20852117 return - ENODEV ;
@@ -2089,11 +2121,9 @@ int of_map_id(const struct device_node *np, u32 id,
20892121 return 0 ;
20902122 }
20912123
2092- if (!map_len || map_len % (4 * sizeof (* map ))) {
2093- pr_err ("%pOF: Error: Bad %s length: %d\n" , np ,
2094- map_name , map_len );
2095- return - EINVAL ;
2096- }
2124+ if (map_bytes % sizeof (* map ))
2125+ goto err_map_len ;
2126+ map_len = map_bytes / sizeof (* map );
20972127
20982128 /* The default is to select all bits. */
20992129 map_mask = 0xffffffff ;
@@ -2106,39 +2136,84 @@ int of_map_id(const struct device_node *np, u32 id,
21062136 of_property_read_u32 (np , map_mask_name , & map_mask );
21072137
21082138 masked_id = map_mask & id ;
2109- for ( ; map_len > 0 ; map_len -= 4 * sizeof (* map ), map += 4 ) {
2139+
2140+ while (offset < map_len ) {
21102141 struct device_node * phandle_node ;
2111- u32 id_base = be32_to_cpup (map + 0 );
2112- u32 phandle = be32_to_cpup (map + 1 );
2113- u32 out_base = be32_to_cpup (map + 2 );
2114- u32 id_len = be32_to_cpup (map + 3 );
2142+ u32 id_base , phandle , id_len , id_off , cells = 0 ;
2143+ const __be32 * out_base ;
2144+
2145+ if (map_len - offset < 2 )
2146+ goto err_map_len ;
2147+
2148+ id_base = be32_to_cpup (map + offset );
21152149
21162150 if (id_base & ~map_mask ) {
2117- pr_err ("%pOF: Invalid %s translation - %s-mask (0x%x) ignores id-base (0x%x)\n" ,
2118- np , map_name , map_name ,
2119- map_mask , id_base );
2151+ pr_err ("%pOF: Invalid %s translation - %s (0x%x) ignores id-base (0x%x)\n" ,
2152+ np , map_name , map_mask_name , map_mask , id_base );
21202153 return - EFAULT ;
21212154 }
21222155
2123- if (masked_id < id_base || masked_id >= id_base + id_len )
2124- continue ;
2125-
2156+ phandle = be32_to_cpup (map + offset + 1 );
21262157 phandle_node = of_find_node_by_phandle (phandle );
21272158 if (!phandle_node )
21282159 return - ENODEV ;
21292160
2161+ if (bad_map ) {
2162+ cells = 1 ;
2163+ } else if (of_property_read_u32 (phandle_node , cells_name , & cells )) {
2164+ pr_err ("%pOF: missing %s property\n" , phandle_node , cells_name );
2165+ of_node_put (phandle_node );
2166+ return - EINVAL ;
2167+ }
2168+
2169+ if (map_len - offset < 3 + cells ) {
2170+ of_node_put (phandle_node );
2171+ goto err_map_len ;
2172+ }
2173+
2174+ if (offset == 0 && cells == 2 ) {
2175+ bad_map = of_check_bad_map (map , map_len );
2176+ if (bad_map ) {
2177+ pr_warn_once ("%pOF: %s mismatches target %s, assuming extra cell of 0\n" ,
2178+ np , map_name , cells_name );
2179+ cells = 1 ;
2180+ }
2181+ }
2182+
2183+ out_base = map + offset + 2 ;
2184+ offset += 3 + cells ;
2185+
2186+ id_len = be32_to_cpup (map + offset - 1 );
2187+ if (id_len > 1 && cells > 1 ) {
2188+ /*
2189+ * With 1 output cell we reasonably assume its value
2190+ * has a linear relationship to the input; with more,
2191+ * we'd need help from the provider to know what to do.
2192+ */
2193+ pr_err ("%pOF: Unsupported %s - cannot handle %d-ID range with %d-cell output specifier\n" ,
2194+ np , map_name , id_len , cells );
2195+ of_node_put (phandle_node );
2196+ return - EINVAL ;
2197+ }
2198+ id_off = masked_id - id_base ;
2199+ if (masked_id < id_base || id_off >= id_len ) {
2200+ of_node_put (phandle_node );
2201+ continue ;
2202+ }
2203+
21302204 if (filter_np && filter_np != phandle_node ) {
21312205 of_node_put (phandle_node );
21322206 continue ;
21332207 }
21342208
21352209 arg -> np = phandle_node ;
2136- arg -> args [0 ] = masked_id - id_base + out_base ;
2137- arg -> args_count = 1 ;
2210+ for (int i = 0 ; i < cells ; i ++ )
2211+ arg -> args [i ] = id_off + be32_to_cpu (out_base [i ]);
2212+ arg -> args_count = cells ;
21382213
21392214 pr_debug ("%pOF: %s, using mask %08x, id-base: %08x, out-base: %08x, length: %08x, id: %08x -> %08x\n" ,
2140- np , map_name , map_mask , id_base , out_base ,
2141- id_len , id , masked_id - id_base + out_base );
2215+ np , map_name , map_mask , id_base , be32_to_cpup ( out_base ) ,
2216+ id_len , id , id_off + be32_to_cpup ( out_base ) );
21422217 return 0 ;
21432218 }
21442219
@@ -2149,6 +2224,10 @@ int of_map_id(const struct device_node *np, u32 id,
21492224 arg -> args [0 ] = id ;
21502225 arg -> args_count = 1 ;
21512226 return 0 ;
2227+
2228+ err_map_len :
2229+ pr_err ("%pOF: Error: Bad %s length: %d\n" , np , map_name , map_bytes );
2230+ return - EINVAL ;
21522231}
21532232EXPORT_SYMBOL_GPL (of_map_id );
21542233
@@ -2158,18 +2237,21 @@ EXPORT_SYMBOL_GPL(of_map_id);
21582237 * @id: Requester ID of the device (e.g. PCI RID/BDF or a platform
21592238 * stream/device ID) used as the lookup key in the iommu-map table.
21602239 * @arg: pointer to a &struct of_phandle_args for the result. On success,
2161- * @arg->args[0] contains the translated ID. If a map entry was matched,
2162- * @arg->np holds a reference to the target node that the caller must
2163- * release with of_node_put().
2240+ * @arg->args_count will be set to the number of output specifier cells
2241+ * and @arg->args[0..args_count-1] will contain the translated output
2242+ * specifier values. If a map entry was matched, @arg->np holds a
2243+ * reference to the target node that the caller must release with
2244+ * of_node_put().
21642245 *
2165- * Convenience wrapper around of_map_id() using "iommu-map" and "iommu-map-mask".
2246+ * Convenience wrapper around of_map_id() using "iommu-map", "#iommu-cells",
2247+ * and "iommu-map-mask".
21662248 *
21672249 * Return: 0 on success or a standard error code on failure.
21682250 */
21692251int of_map_iommu_id (const struct device_node * np , u32 id ,
21702252 struct of_phandle_args * arg )
21712253{
2172- return of_map_id (np , id , "iommu-map" , "iommu-map-mask" , NULL , arg );
2254+ return of_map_id (np , id , "iommu-map" , "#iommu-cells" , " iommu-map-mask" , NULL , arg );
21732255}
21742256EXPORT_SYMBOL_GPL (of_map_iommu_id );
21752257
@@ -2182,17 +2264,20 @@ EXPORT_SYMBOL_GPL(of_map_iommu_id);
21822264 * to match any. If non-NULL, only map entries targeting this node will
21832265 * be matched.
21842266 * @arg: pointer to a &struct of_phandle_args for the result. On success,
2185- * @arg->args[0] contains the translated ID. If a map entry was matched,
2186- * @arg->np holds a reference to the target node that the caller must
2187- * release with of_node_put().
2267+ * @arg->args_count will be set to the number of output specifier cells
2268+ * and @arg->args[0..args_count-1] will contain the translated output
2269+ * specifier values. If a map entry was matched, @arg->np holds a
2270+ * reference to the target node that the caller must release with
2271+ * of_node_put().
21882272 *
2189- * Convenience wrapper around of_map_id() using "msi-map" and "msi-map-mask".
2273+ * Convenience wrapper around of_map_id() using "msi-map", "#msi-cells",
2274+ * and "msi-map-mask".
21902275 *
21912276 * Return: 0 on success or a standard error code on failure.
21922277 */
21932278int of_map_msi_id (const struct device_node * np , u32 id ,
21942279 const struct device_node * filter_np , struct of_phandle_args * arg )
21952280{
2196- return of_map_id (np , id , "msi-map" , "msi-map-mask" , filter_np , arg );
2281+ return of_map_id (np , id , "msi-map" , "#msi-cells" , " msi-map-mask" , filter_np , arg );
21972282}
21982283EXPORT_SYMBOL_GPL (of_map_msi_id );
0 commit comments