net: ipa: fix register write command validation
authorAlex Elder <elder@linaro.org>
Fri, 12 Feb 2021 14:34:00 +0000 (08:34 -0600)
committerDavid S. Miller <davem@davemloft.net>
Sat, 13 Feb 2021 00:54:17 +0000 (16:54 -0800)
In ipa_cmd_register_write_valid() we verify that values we will
supply to a REGISTER_WRITE IPA immediate command will fit in
the fields that need to hold them.  This patch fixes some issues
in that function and ipa_cmd_register_write_offset_valid().

The dev_err() call in ipa_cmd_register_write_offset_valid() has
some printf format errors:
  - The name of the register (corresponding to the string format
    specifier) was not supplied.
  - The IPA base offset and offset need to be supplied separately to
    match the other format specifiers.
Also make the ~0 constant used there to compute the maximum
supported offset value explicitly unsigned.

There are two other issues in ipa_cmd_register_write_valid():
  - There's no need to check the hash flush register for platforms
    (like IPA v4.2) that do not support hashed tables
  - The highest possible endpoint number, whose status register
    offset is computed, is COUNT - 1, not COUNT.

Fix these problems, and add some additional commentary.

Signed-off-by: Alex Elder <elder@linaro.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ipa/ipa_cmd.c

index 97b50fe..fd8bf64 100644 (file)
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 
 /* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
- * Copyright (C) 2019-2020 Linaro Ltd.
+ * Copyright (C) 2019-2021 Linaro Ltd.
  */
 
 #include <linux/types.h>
@@ -244,11 +244,15 @@ static bool ipa_cmd_register_write_offset_valid(struct ipa *ipa,
        if (ipa->version != IPA_VERSION_3_5_1)
                bit_count += hweight32(REGISTER_WRITE_FLAGS_OFFSET_HIGH_FMASK);
        BUILD_BUG_ON(bit_count > 32);
-       offset_max = ~0 >> (32 - bit_count);
+       offset_max = ~0U >> (32 - bit_count);
 
+       /* Make sure the offset can be represented by the field(s)
+        * that holds it.  Also make sure the offset is not outside
+        * the overall IPA memory range.
+        */
        if (offset > offset_max || ipa->mem_offset > offset_max - offset) {
                dev_err(dev, "%s offset too large 0x%04x + 0x%04x > 0x%04x)\n",
-                               ipa->mem_offset + offset, offset_max);
+                       name, ipa->mem_offset, offset, offset_max);
                return false;
        }
 
@@ -261,12 +265,24 @@ static bool ipa_cmd_register_write_valid(struct ipa *ipa)
        const char *name;
        u32 offset;
 
-       offset = ipa_reg_filt_rout_hash_flush_offset(ipa->version);
-       name = "filter/route hash flush";
-       if (!ipa_cmd_register_write_offset_valid(ipa, name, offset))
-               return false;
+       /* If hashed tables are supported, ensure the hash flush register
+        * offset will fit in a register write IPA immediate command.
+        */
+       if (ipa->version != IPA_VERSION_4_2) {
+               offset = ipa_reg_filt_rout_hash_flush_offset(ipa->version);
+               name = "filter/route hash flush";
+               if (!ipa_cmd_register_write_offset_valid(ipa, name, offset))
+                       return false;
+       }
 
-       offset = IPA_REG_ENDP_STATUS_N_OFFSET(IPA_ENDPOINT_COUNT);
+       /* Each endpoint can have a status endpoint associated with it,
+        * and this is recorded in an endpoint register.  If the modem
+        * crashes, we reset the status endpoint for all modem endpoints
+        * using a register write IPA immediate command.  Make sure the
+        * worst case (highest endpoint number) offset of that endpoint
+        * fits in the register write command field(s) that must hold it.
+        */
+       offset = IPA_REG_ENDP_STATUS_N_OFFSET(IPA_ENDPOINT_COUNT - 1);
        name = "maximal endpoint status";
        if (!ipa_cmd_register_write_offset_valid(ipa, name, offset))
                return false;