diff mbox series

[XEN,v4,04/11] xen/drivers: ns16550: Use paddr_t for io_base/io_size

Message ID 20230321140357.24094-5-ayan.kumar.halder@amd.com (mailing list archive)
State New, archived
Headers show
Series Add support for 32 bit physical address | expand

Commit Message

Ayan Kumar Halder March 21, 2023, 2:03 p.m. UTC
io_base and io_size represent physical addresses. So they should use
paddr_t (instead of u64).

However in future, paddr_t may be defined as u32. So when typecasting
values from u64 to paddr_t, one should always check for any possible
truncation. If any truncation is discovered, Xen needs to return an
appropriate an error message for this.

Also moved the definition of PARSE_ERR_RET before its first usage.

Signed-off-by: Ayan Kumar Halder <ayan.kumar.halder@amd.com>
---
Changes from -
v1 - NA

v2 - 1. Extracted the patch from "[XEN v2 05/11] xen/arm: Use paddr_t instead of u64 for address/size"
into a separate patch of its own.

v3 - 1. Reduced the scope of pci_uart_io_base and uart_io_base definitions.
2. Instead of crashing, invoke PARSE_ERR_RET().
3. Moved PARSE_ERR_RET() so that it is defined before its first use.

 xen/drivers/char/ns16550.c | 41 ++++++++++++++++++++++++++------------
 1 file changed, 28 insertions(+), 13 deletions(-)

Comments

Jan Beulich March 21, 2023, 2:16 p.m. UTC | #1
On 21.03.2023 15:03, Ayan Kumar Halder wrote:
> @@ -1163,10 +1163,16 @@ static const struct ns16550_config __initconst uart_config[] =
>      },
>  };
>  
> +#define PARSE_ERR_RET(_f, _a...)             \
> +    do {                                     \
> +        printk( "ERROR: " _f "\n" , ## _a ); \
> +        return false;                        \
> +    } while ( 0 )

You can't really re-use this construct unchanged (and perhaps it's also
not worth changing for this single use that you need): Note the "return
false", which ...

>  static int __init
>  pci_uart_config(struct ns16550 *uart, bool_t skip_amt, unsigned int idx)

... for a function returning "int" is equivalent to "return 0", which
is kind of a success indicator here. Whatever adjustment you make
needs to be in line with (at least) the two callers checking the
return value (the other two not doing so is suspicious, but then the
way the return values are used is somewhat odd, too).

> @@ -1235,6 +1241,8 @@ pci_uart_config(struct ns16550 *uart, bool_t skip_amt, unsigned int idx)
>                  /* MMIO based */
>                  if ( param->mmio && !(bar & PCI_BASE_ADDRESS_SPACE_IO) )
>                  {
> +                    uint64_t pci_uart_io_base;
> +
>                      pci_conf_write32(PCI_SBDF(0, b, d, f),
>                                       PCI_BASE_ADDRESS_0 + bar_idx*4, ~0u);
>                      len = pci_conf_read32(PCI_SBDF(0, b, d, f),
> @@ -1259,8 +1267,14 @@ pci_uart_config(struct ns16550 *uart, bool_t skip_amt, unsigned int idx)
>                      else
>                          size = len & PCI_BASE_ADDRESS_MEM_MASK;
>  
> -                    uart->io_base = ((u64)bar_64 << 32) |
> -                                    (bar & PCI_BASE_ADDRESS_MEM_MASK);
> +                    pci_uart_io_base = ((u64)bar_64 << 32) |

As you touch this code, please be so kind and also switch to using
uint64_t here.

Also why do you change parse_positional() but not (also)
parse_namevalue_pairs()?

Jan
Ayan Kumar Halder March 29, 2023, 2:35 p.m. UTC | #2
Hi Jan,

On 21/03/2023 14:16, Jan Beulich wrote:
> On 21.03.2023 15:03, Ayan Kumar Halder wrote:
>> @@ -1163,10 +1163,16 @@ static const struct ns16550_config __initconst uart_config[] =
>>       },
>>   };
>>   
>> +#define PARSE_ERR_RET(_f, _a...)             \
>> +    do {                                     \
>> +        printk( "ERROR: " _f "\n" , ## _a ); \
>> +        return false;                        \
>> +    } while ( 0 )
> You can't really re-use this construct unchanged (and perhaps it's also
> not worth changing for this single use that you need): Note the "return
> false", which ...
>
>>   static int __init
>>   pci_uart_config(struct ns16550 *uart, bool_t skip_amt, unsigned int idx)
> ... for a function returning "int" is equivalent to "return 0", which
> is kind of a success indicator here. Whatever adjustment you make
> needs to be in line with (at least) the two callers checking the
> return value (the other two not doing so is suspicious, but then the
> way the return values are used is somewhat odd, too).
>
>> @@ -1235,6 +1241,8 @@ pci_uart_config(struct ns16550 *uart, bool_t skip_amt, unsigned int idx)
>>                   /* MMIO based */
>>                   if ( param->mmio && !(bar & PCI_BASE_ADDRESS_SPACE_IO) )
>>                   {
>> +                    uint64_t pci_uart_io_base;
>> +
>>                       pci_conf_write32(PCI_SBDF(0, b, d, f),
>>                                        PCI_BASE_ADDRESS_0 + bar_idx*4, ~0u);
>>                       len = pci_conf_read32(PCI_SBDF(0, b, d, f),
>> @@ -1259,8 +1267,14 @@ pci_uart_config(struct ns16550 *uart, bool_t skip_amt, unsigned int idx)
>>                       else
>>                           size = len & PCI_BASE_ADDRESS_MEM_MASK;
>>   
>> -                    uart->io_base = ((u64)bar_64 << 32) |
>> -                                    (bar & PCI_BASE_ADDRESS_MEM_MASK);
>> +                    pci_uart_io_base = ((u64)bar_64 << 32) |
> As you touch this code, please be so kind and also switch to using
> uint64_t here.
>
> Also why do you change parse_positional() but not (also)
> parse_namevalue_pairs()?
>
> Jan

Please let me know if the below patch looks fine.

     xen/drivers: ns16550: Use paddr_t for io_base/io_size

     io_base and io_size represent physical addresses. So they should use
     paddr_t (instead of u64).

     However in future, paddr_t may be defined as u32. So when typecasting
     values from u64 to paddr_t, one should always check for any possible
     truncation. If any truncation is discovered, Xen needs to return an
     appropriate an error message for this.

     Signed-off-by: Ayan Kumar Halder <ayan.kumar.halder@amd.com>

diff --git a/xen/drivers/char/ns16550.c b/xen/drivers/char/ns16550.c
index 092f6b9c4b..5c52e7e642 100644
--- a/xen/drivers/char/ns16550.c
+++ b/xen/drivers/char/ns16550.c
@@ -42,8 +42,8 @@

  static struct ns16550 {
      int baud, clock_hz, data_bits, parity, stop_bits, fifo_size, irq;
-    u64 io_base;   /* I/O port or memory-mapped I/O address. */
-    u64 io_size;
+    paddr_t io_base;   /* I/O port or memory-mapped I/O address. */
+    paddr_t io_size;
      int reg_shift; /* Bits to shift register offset by */
      int reg_width; /* Size of access to use, the registers
                      * themselves are still bytes */
@@ -1166,7 +1166,7 @@ static const struct ns16550_config __initconst 
uart_config[] =
  static int __init
  pci_uart_config(struct ns16550 *uart, bool_t skip_amt, unsigned int idx)
  {
-    u64 orig_base = uart->io_base;
+    paddr_t orig_base = uart->io_base;
      unsigned int b, d, f, nextf, i;

      /* NB. Start at bus 1 to avoid AMT: a plug-in card cannot be on 
bus 0. */
@@ -1235,6 +1235,8 @@ pci_uart_config(struct ns16550 *uart, bool_t 
skip_amt, unsigned int idx)
                  /* MMIO based */
                  if ( param->mmio && !(bar & PCI_BASE_ADDRESS_SPACE_IO) )
                  {
+                    uint64_t pci_uart_io_base;
+
                      pci_conf_write32(PCI_SBDF(0, b, d, f),
                                       PCI_BASE_ADDRESS_0 + bar_idx*4, ~0u);
                      len = pci_conf_read32(PCI_SBDF(0, b, d, f),
@@ -1259,8 +1261,17 @@ pci_uart_config(struct ns16550 *uart, bool_t 
skip_amt, unsigned int idx)
                      else
                          size = len & PCI_BASE_ADDRESS_MEM_MASK;

-                    uart->io_base = ((u64)bar_64 << 32) |
-                                    (bar & PCI_BASE_ADDRESS_MEM_MASK);
+                    pci_uart_io_base = ((uint64_t)bar_64 << 32) |
+                                        (bar & PCI_BASE_ADDRESS_MEM_MASK);
+
+                    /* Truncation detected while converting to paddr_t */
+                    if ( pci_uart_io_base != (paddr_t)pci_uart_io_base )
+                    {
+                        printk("ERROR: Truncation detected for io_base 
address");
+                        return -EINVAL;
+                    }
+
+                    uart->io_base = pci_uart_io_base;
                  }
                  /* IO based */
                  else if ( !param->mmio && (bar & 
PCI_BASE_ADDRESS_SPACE_IO) )
@@ -1519,20 +1530,40 @@ static bool __init parse_positional(struct 
ns16550 *uart, char **str)
  #ifdef CONFIG_HAS_PCI
          if ( strncmp(conf, "pci", 3) == 0 )
          {
-            if ( pci_uart_config(uart, 1/* skip AMT */, uart - 
ns16550_com) )
+            int ret;
+
+            ret = pci_uart_config(uart, 1/* skip AMT */, uart - 
ns16550_com);
+
+            if ( ret == -EINVAL )
+                return false;
+            else if ( ret )
                  return true;
+
              conf += 3;
          }
          else if ( strncmp(conf, "amt", 3) == 0 )
          {
-            if ( pci_uart_config(uart, 0, uart - ns16550_com) )
+            int ret = pci_uart_config(uart, 0, uart - ns16550_com);
+
+            if ( ret == -EINVAL )
+                return false;
+            else if ( ret )
                  return true;
+
              conf += 3;
          }
          else
  #endif
          {
-            uart->io_base = simple_strtoull(conf, &conf, 0);
+            uint64_t uart_io_base;
+
+            uart_io_base = simple_strtoull(conf, &conf, 0);
+
+            /* Truncation detected while converting to paddr_t */
+            if ( uart_io_base != (paddr_t)uart_io_base )
+                PARSE_ERR_RET("Truncation detected for uart_io_base 
address");
+
+            uart->io_base = uart_io_base;
          }
      }

@@ -1577,6 +1608,7 @@ static bool __init parse_namevalue_pairs(char 
*str, struct ns16550 *uart)
      char *token, *start = str;
      char *param_value = NULL;
      bool dev_set = false;
+    uint64_t uart_io_base;

      if ( (str == NULL) || (*str == '\0') )
          return true;
@@ -1603,7 +1635,14 @@ static bool __init parse_namevalue_pairs(char 
*str, struct ns16550 *uart)
                         "Can't use io_base with dev=pci or dev=amt 
options\n");
                  break;
              }
-            uart->io_base = simple_strtoull(param_value, NULL, 0);
+

+            uart_io_base = simple_strtoull(param_value, NULL, 0);

+            uart->io_base = uart_io_base;
+            if ( uart_io_base != uart->io_base )
+                PARSE_ERR_RET("Truncation detected for io_base address");
+
              break;

          case irq:


- Ayan
Jan Beulich March 30, 2023, 6:55 a.m. UTC | #3
On 29.03.2023 16:35, Ayan Kumar Halder wrote:
> Please let me know if the below patch looks fine.

Apart from the comments below there may be formatting issues, which
I can't sensibly comment on when the patch was mangled by your mailer
anyway. (Which in turn is why it is generally better to properly send
a new version, rather than replying with kind-of-a-new-version on an
earlier thread.)

Additionally, up front: I'm sorry for the extra requests, but I'm
afraid to sensibly make the changes you want to make some things need
sorting first, to avoid extending pre-existing clumsiness. This is
irrespective of the present state of things clearly not being your
fault.

> @@ -1235,6 +1235,8 @@ pci_uart_config(struct ns16550 *uart, bool_t 
> skip_amt, unsigned int idx)
>                   /* MMIO based */
>                   if ( param->mmio && !(bar & PCI_BASE_ADDRESS_SPACE_IO) )
>                   {
> +                    uint64_t pci_uart_io_base;
> +
>                       pci_conf_write32(PCI_SBDF(0, b, d, f),
>                                        PCI_BASE_ADDRESS_0 + bar_idx*4, ~0u);
>                       len = pci_conf_read32(PCI_SBDF(0, b, d, f),
> @@ -1259,8 +1261,17 @@ pci_uart_config(struct ns16550 *uart, bool_t 
> skip_amt, unsigned int idx)
>                       else
>                           size = len & PCI_BASE_ADDRESS_MEM_MASK;
> 
> -                    uart->io_base = ((u64)bar_64 << 32) |
> -                                    (bar & PCI_BASE_ADDRESS_MEM_MASK);
> +                    pci_uart_io_base = ((uint64_t)bar_64 << 32) |
> +                                        (bar & PCI_BASE_ADDRESS_MEM_MASK);
> +
> +                    /* Truncation detected while converting to paddr_t */
> +                    if ( pci_uart_io_base != (paddr_t)pci_uart_io_base )
> +                    {
> +                        printk("ERROR: Truncation detected for io_base 
> address");
> +                        return -EINVAL;
> +                    }

Further down the function returns -1, so here you assume EINVAL != 1.
Such assumptions (and mixing of value spaces) is generally not a good
idea. Since there are other issues (see below), maybe you really want
to add a prereq patch addressing those? That would include changing the
"return -1" to either "return 1" or making it use some sensible and
properly distinguishable errno value.

> @@ -1519,20 +1530,40 @@ static bool __init parse_positional(struct 
> ns16550 *uart, char **str)
>   #ifdef CONFIG_HAS_PCI
>           if ( strncmp(conf, "pci", 3) == 0 )
>           {
> -            if ( pci_uart_config(uart, 1/* skip AMT */, uart - 
> ns16550_com) )
> +            int ret;
> +
> +            ret = pci_uart_config(uart, 1/* skip AMT */, uart - 
> ns16550_com);
> +
> +            if ( ret == -EINVAL )
> +                return false;
> +            else if ( ret )
>                   return true;

With skip_amt != 0 the function presently can only return 0. You're
therefore converting pre-existing dead code to another form of dead
code. Otoh (and as, I think, previously indicated) ...

> +
>               conf += 3;
>           }
>           else if ( strncmp(conf, "amt", 3) == 0 )
>           {
> -            if ( pci_uart_config(uart, 0, uart - ns16550_com) )
> +            int ret = pci_uart_config(uart, 0, uart - ns16550_com);
> +
> +            if ( ret == -EINVAL )
> +                return false;
> +            else if ( ret )
>                   return true;

... the equivalent of this in parse_namevalue_pairs() not checking
the return value is bogus. But it is further bogus that the case
where skip_amt has passed 1 for it sets dev_set to true
unconditionally, i.e. even when no device was found. IOW I also
question the correctness of the final "return 0" in pci_uart_config().
I looks to me as if this wants to be a skip_amt-independent
"return -ENODEV". skip_amt would only control whether uart->io_base is
restored before returning (leaving aside the question of why that is).

Jan
Ayan Kumar Halder July 7, 2023, 11:37 a.m. UTC | #4
Hi Jan,

On 30/03/2023 07:55, Jan Beulich wrote:
> On 29.03.2023 16:35, Ayan Kumar Halder wrote:
>> Please let me know if the below patch looks fine.
> Apart from the comments below there may be formatting issues, which
> I can't sensibly comment on when the patch was mangled by your mailer
> anyway. (Which in turn is why it is generally better to properly send
> a new version, rather than replying with kind-of-a-new-version on an
> earlier thread.)
>
> Additionally, up front: I'm sorry for the extra requests, but I'm
> afraid to sensibly make the changes you want to make some things need
> sorting first, to avoid extending pre-existing clumsiness. This is
> irrespective of the present state of things clearly not being your
> fault.
>
>> @@ -1235,6 +1235,8 @@ pci_uart_config(struct ns16550 *uart, bool_t
>> skip_amt, unsigned int idx)
>>                    /* MMIO based */
>>                    if ( param->mmio && !(bar & PCI_BASE_ADDRESS_SPACE_IO) )
>>                    {
>> +                    uint64_t pci_uart_io_base;
>> +
>>                        pci_conf_write32(PCI_SBDF(0, b, d, f),
>>                                         PCI_BASE_ADDRESS_0 + bar_idx*4, ~0u);
>>                        len = pci_conf_read32(PCI_SBDF(0, b, d, f),
>> @@ -1259,8 +1261,17 @@ pci_uart_config(struct ns16550 *uart, bool_t
>> skip_amt, unsigned int idx)
>>                        else
>>                            size = len & PCI_BASE_ADDRESS_MEM_MASK;
>>
>> -                    uart->io_base = ((u64)bar_64 << 32) |
>> -                                    (bar & PCI_BASE_ADDRESS_MEM_MASK);
>> +                    pci_uart_io_base = ((uint64_t)bar_64 << 32) |
>> +                                        (bar & PCI_BASE_ADDRESS_MEM_MASK);
>> +
>> +                    /* Truncation detected while converting to paddr_t */
>> +                    if ( pci_uart_io_base != (paddr_t)pci_uart_io_base )
>> +                    {
>> +                        printk("ERROR: Truncation detected for io_base
>> address");
>> +                        return -EINVAL;
>> +                    }
> Further down the function returns -1, so here you assume EINVAL != 1.
> Such assumptions (and mixing of value spaces) is generally not a good
> idea. Since there are other issues (see below), maybe you really want
> to add a prereq patch addressing those? That would include changing the
> "return -1" to either "return 1" or making it use some sensible and
> properly distinguishable errno value.
>
>> @@ -1519,20 +1530,40 @@ static bool __init parse_positional(struct
>> ns16550 *uart, char **str)
>>    #ifdef CONFIG_HAS_PCI
>>            if ( strncmp(conf, "pci", 3) == 0 )
>>            {
>> -            if ( pci_uart_config(uart, 1/* skip AMT */, uart -
>> ns16550_com) )
>> +            int ret;
>> +
>> +            ret = pci_uart_config(uart, 1/* skip AMT */, uart -
>> ns16550_com);
>> +
>> +            if ( ret == -EINVAL )
>> +                return false;
>> +            else if ( ret )
>>                    return true;
> With skip_amt != 0 the function presently can only return 0. You're
> therefore converting pre-existing dead code to another form of dead
> code. Otoh (and as, I think, previously indicated) ...
>
>> +
>>                conf += 3;
>>            }
>>            else if ( strncmp(conf, "amt", 3) == 0 )
>>            {
>> -            if ( pci_uart_config(uart, 0, uart - ns16550_com) )
>> +            int ret = pci_uart_config(uart, 0, uart - ns16550_com);
>> +
>> +            if ( ret == -EINVAL )
>> +                return false;
>> +            else if ( ret )
>>                    return true;
> ... the equivalent of this in parse_namevalue_pairs() not checking
> the return value is bogus. But it is further bogus that the case
> where skip_amt has passed 1 for it sets dev_set to true
> unconditionally, i.e. even when no device was found. IOW I also
> question the correctness of the final "return 0" in pci_uart_config().
> I looks to me as if this wants to be a skip_amt-independent
> "return -ENODEV". skip_amt would only control whether uart->io_base is
> restored before returning (leaving aside the question of why that is).

I have sent out a patch to fix the return logic pci_uart_config()

[PATCH v1] xen/drivers: ns16550: Fix the return logic for pci_uart_config()

Let me know if I understood you correctly.

- Ayan

>
> Jan
diff mbox series

Patch

diff --git a/xen/drivers/char/ns16550.c b/xen/drivers/char/ns16550.c
index 092f6b9c4b..2e8a9cfb24 100644
--- a/xen/drivers/char/ns16550.c
+++ b/xen/drivers/char/ns16550.c
@@ -42,8 +42,8 @@ 
 
 static struct ns16550 {
     int baud, clock_hz, data_bits, parity, stop_bits, fifo_size, irq;
-    u64 io_base;   /* I/O port or memory-mapped I/O address. */
-    u64 io_size;
+    paddr_t io_base;   /* I/O port or memory-mapped I/O address. */
+    paddr_t io_size;
     int reg_shift; /* Bits to shift register offset by */
     int reg_width; /* Size of access to use, the registers
                     * themselves are still bytes */
@@ -1163,10 +1163,16 @@  static const struct ns16550_config __initconst uart_config[] =
     },
 };
 
+#define PARSE_ERR_RET(_f, _a...)             \
+    do {                                     \
+        printk( "ERROR: " _f "\n" , ## _a ); \
+        return false;                        \
+    } while ( 0 )
+
 static int __init
 pci_uart_config(struct ns16550 *uart, bool_t skip_amt, unsigned int idx)
 {
-    u64 orig_base = uart->io_base;
+    paddr_t orig_base = uart->io_base;
     unsigned int b, d, f, nextf, i;
 
     /* NB. Start at bus 1 to avoid AMT: a plug-in card cannot be on bus 0. */
@@ -1235,6 +1241,8 @@  pci_uart_config(struct ns16550 *uart, bool_t skip_amt, unsigned int idx)
                 /* MMIO based */
                 if ( param->mmio && !(bar & PCI_BASE_ADDRESS_SPACE_IO) )
                 {
+                    uint64_t pci_uart_io_base;
+
                     pci_conf_write32(PCI_SBDF(0, b, d, f),
                                      PCI_BASE_ADDRESS_0 + bar_idx*4, ~0u);
                     len = pci_conf_read32(PCI_SBDF(0, b, d, f),
@@ -1259,8 +1267,14 @@  pci_uart_config(struct ns16550 *uart, bool_t skip_amt, unsigned int idx)
                     else
                         size = len & PCI_BASE_ADDRESS_MEM_MASK;
 
-                    uart->io_base = ((u64)bar_64 << 32) |
-                                    (bar & PCI_BASE_ADDRESS_MEM_MASK);
+                    pci_uart_io_base = ((u64)bar_64 << 32) |
+                                        (bar & PCI_BASE_ADDRESS_MEM_MASK);
+
+                    /* Truncation detected while converting to paddr_t */
+                    if ( (pci_uart_io_base >> (PADDR_BITS - 1)) > 1 )
+                        PARSE_ERR_RET("Truncation detected for io_base address");
+
+                    uart->io_base = pci_uart_io_base;
                 }
                 /* IO based */
                 else if ( !param->mmio && (bar & PCI_BASE_ADDRESS_SPACE_IO) )
@@ -1456,13 +1470,6 @@  static enum __init serial_param_type get_token(char *token, char **value)
         return;                              \
     } while ( 0 )
 
-#define PARSE_ERR_RET(_f, _a...)             \
-    do {                                     \
-        printk( "ERROR: " _f "\n" , ## _a ); \
-        return false;                        \
-    } while ( 0 )
-
-
 static bool __init parse_positional(struct ns16550 *uart, char **str)
 {
     int baud;
@@ -1532,7 +1539,15 @@  static bool __init parse_positional(struct ns16550 *uart, char **str)
         else
 #endif
         {
-            uart->io_base = simple_strtoull(conf, &conf, 0);
+            uint64_t uart_io_base;
+
+            uart_io_base = simple_strtoull(conf, &conf, 0);
+
+            /* Truncation detected while converting to paddr_t */
+            if ( (uart_io_base >> (PADDR_BITS - 1)) > 1 )
+                PARSE_ERR_RET("Truncation detected for uart_io_base address");
+
+            uart->io_base = uart_io_base;
         }
     }