net: ipa: validate IPA table memory earlier
authorAlex Elder <elder@linaro.org>
Fri, 21 Oct 2022 19:13:36 +0000 (14:13 -0500)
committerPaolo Abeni <pabeni@redhat.com>
Tue, 25 Oct 2022 09:15:18 +0000 (11:15 +0200)
Add checks in ipa_table_init() to ensure the memory regions defined
for IPA filter and routing tables are valid.

For routing tables, the checks ensure:
  - The non-hashed IPv4 and IPv6 routing tables are defined
  - The non-hashed IPv4 and IPv6 routing tables are the same size
  - The number entries in the non-hashed IPv4 routing table is enough
    to hold the number entries available to the modem, plus at least
    one usable by the AP.

For filter tables, the checks ensure:
  - The non-hashed IPv4 and IPv6 filter tables are defined
  - The non-hashed IPv4 and IPv6 filter tables are the same size
  - The number entries in the non-hashed IPv4 filter table is enough
    to hold the endpoint bitmap, plus an entry for each defined
    endpoint that supports filtering.

In addition, for both routing and filter tables:
  - If hashing isn't supported (IPA v4.2), hashed tables are zero size
  - If hashing *is* supported, all hashed tables are the same size as
    their non-hashed counterparts.

When validating the size of routing tables, require the AP to have
at least one entry (in addition to those used by the modem).

Signed-off-by: Alex Elder <elder@linaro.org>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
drivers/net/ipa/ipa_table.c

index 74d7082..222362a 100644 (file)
@@ -134,9 +134,25 @@ static void ipa_table_validate_build(void)
        BUILD_BUG_ON(IPA_ROUTE_COUNT_MAX > 32);
        /* The modem must be allotted at least one route table entry */
        BUILD_BUG_ON(!IPA_ROUTE_MODEM_COUNT);
-       /* But it can't have more than what is available */
-       BUILD_BUG_ON(IPA_ROUTE_MODEM_COUNT > IPA_ROUTE_COUNT_MAX);
+       /* AP must too, but we can't use more than what is available */
+       BUILD_BUG_ON(IPA_ROUTE_MODEM_COUNT >= IPA_ROUTE_COUNT_MAX);
+}
 
+static const struct ipa_mem *
+ipa_table_mem(struct ipa *ipa, bool filter, bool hashed, bool ipv6)
+{
+       enum ipa_mem_id mem_id;
+
+       mem_id = filter ? hashed ? ipv6 ? IPA_MEM_V6_FILTER_HASHED
+                                       : IPA_MEM_V4_FILTER_HASHED
+                                : ipv6 ? IPA_MEM_V6_FILTER
+                                       : IPA_MEM_V4_FILTER
+                       : hashed ? ipv6 ? IPA_MEM_V6_ROUTE_HASHED
+                                       : IPA_MEM_V4_ROUTE_HASHED
+                                : ipv6 ? IPA_MEM_V6_ROUTE
+                                       : IPA_MEM_V4_ROUTE;
+
+       return ipa_mem_find(ipa, mem_id);
 }
 
 static bool
@@ -604,8 +620,77 @@ void ipa_table_config(struct ipa *ipa)
        ipa_route_config(ipa, true);
 }
 
-/*
- * Initialize a coherent DMA allocation containing initialized filter and
+/* Zero modem_route_count means filter table memory check */
+static bool ipa_table_mem_valid(struct ipa *ipa, bool modem_route_count)
+{
+       bool hash_support = ipa_table_hash_support(ipa);
+       bool filter = !modem_route_count;
+       const struct ipa_mem *mem_hashed;
+       const struct ipa_mem *mem_ipv4;
+       const struct ipa_mem *mem_ipv6;
+       u32 count;
+
+       /* IPv4 and IPv6 non-hashed tables are expected to be defined and
+        * have the same size.  Both must have at least two entries (and
+        * would normally have more than that).
+        */
+       mem_ipv4 = ipa_table_mem(ipa, filter, false, false);
+       if (!mem_ipv4)
+               return false;
+
+       mem_ipv6 = ipa_table_mem(ipa, filter, false, true);
+       if (!mem_ipv6)
+               return false;
+
+       if (mem_ipv4->size != mem_ipv6->size)
+               return false;
+
+       /* Make sure the regions are big enough */
+       count = mem_ipv4->size / sizeof(__le64);
+       if (count < 2)
+               return false;
+       if (filter) {
+               /* Filter tables must able to hold the endpoint bitmap plus
+                * an entry for each endpoint that supports filtering
+                */
+               if (count < 1 + hweight32(ipa->filter_map))
+                       return false;
+       } else {
+               /* Routing tables must be able to hold all modem entries,
+                * plus at least one entry for the AP.
+                */
+               if (count < modem_route_count + 1)
+                       return false;
+       }
+
+       /* If hashing is supported, hashed tables are expected to be defined,
+        * and have the same size as non-hashed tables.  If hashing is not
+        * supported, hashed tables are expected to have zero size (or not
+        * be defined).
+        */
+       mem_hashed = ipa_table_mem(ipa, filter, true, false);
+       if (hash_support) {
+               if (!mem_hashed || mem_hashed->size != mem_ipv4->size)
+                       return false;
+       } else {
+               if (mem_hashed && mem_hashed->size)
+                       return false;
+       }
+
+       /* Same check for IPv6 tables */
+       mem_hashed = ipa_table_mem(ipa, filter, true, true);
+       if (hash_support) {
+               if (!mem_hashed || mem_hashed->size != mem_ipv6->size)
+                       return false;
+       } else {
+               if (mem_hashed && mem_hashed->size)
+                       return false;
+       }
+
+       return true;
+}
+
+/* Initialize a coherent DMA allocation containing initialized filter and
  * route table data.  This is used when initializing or resetting the IPA
  * filter or route table.
  *
@@ -653,6 +738,11 @@ int ipa_table_init(struct ipa *ipa)
 
        ipa_table_validate_build();
 
+       if (!ipa_table_mem_valid(ipa, 0))
+               return -EINVAL;
+       if (!ipa_table_mem_valid(ipa, IPA_ROUTE_MODEM_COUNT))
+               return -EINVAL;
+
        /* The IPA hardware requires route and filter table rules to be
         * aligned on a 128-byte boundary.  We put the "zero rule" at the
         * base of the table area allocated here.  The DMA address returned