qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH 1/1] tmp105: Fix I2C protocol bug
@ 2012-12-05 19:48 Alex Horn
  2012-12-06 21:21 ` Blue Swirl
  2012-12-12  4:34 ` Andreas Färber
  0 siblings, 2 replies; 4+ messages in thread
From: Alex Horn @ 2012-12-05 19:48 UTC (permalink / raw)
  To: qemu-devel; +Cc: peter.maydell, afaerber, anthony, Alex Horn

The private buffer length field must only be incremented after the I2C
frame has been transmitted.

To expose this bug, assume the temperature in the TMP105 hardware model
is +0.125 C (e.g. snow slush). Note that eleven bit precision is required
to read this value; otherwise the reading is equal to zero centigrade (ice).

Continue by considering the following I2C protocol steps:

1) Start transfer with I2C_START_SEND
2) Send byte 0x01 (i.e. configuration register)
3) Send byte 0x40 (i.e. eleven bit precision)
4) End transfer with I2C_FINISH

5) Start transfer with I2C_START_SEND
6) Send byte 0x00 (i.e. temperature register)
7) End transfer I2C_FINISH

8) Start transfer with I2C_START_RECV
9) Receive high-order byte of temperature
   ...

In step (1), the function tmp105_tx() is called. By the conditional
check !s->len and the side effect with ++, s->len is equal to 1 when
step (2) begins. Thus, 0x40 is written to s->buf[1] in step (3).
By definition of tmp105_write(), s->config is set to zero in step (3).
Thus, when we read the higher-order byte in step (9), it is zero!

In other words, the TMP105 hardware model allows us to measure 0 C (ice)
even with eleven bit precision when, in fact, it should be 0.125 C (slush)!

Signed-off-by: Alex Horn <alex.horn@cs.ox.ac.uk>
---
 hw/tmp105.c |    3 ++-
 1 files changed, 2 insertions(+), 1 deletions(-)

diff --git a/hw/tmp105.c b/hw/tmp105.c
index 8e8dbd9..5f41a3f 100644
--- a/hw/tmp105.c
+++ b/hw/tmp105.c
@@ -152,7 +152,7 @@ static int tmp105_tx(I2CSlave *i2c, uint8_t data)
 {
     TMP105State *s = (TMP105State *) i2c;
 
-    if (!s->len ++)
+    if (s->len == 0)
         s->pointer = data;
     else {
         if (s->len <= 2)
@@ -160,6 +160,7 @@ static int tmp105_tx(I2CSlave *i2c, uint8_t data)
         tmp105_write(s);
     }
 
+    s->len ++;
     return 0;
 }
 
-- 
1.7.6.5

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [Qemu-devel] [PATCH 1/1] tmp105: Fix I2C protocol bug
  2012-12-05 19:48 [Qemu-devel] [PATCH 1/1] tmp105: Fix I2C protocol bug Alex Horn
@ 2012-12-06 21:21 ` Blue Swirl
  2012-12-07 13:12   ` Alex Horn
  2012-12-12  4:34 ` Andreas Färber
  1 sibling, 1 reply; 4+ messages in thread
From: Blue Swirl @ 2012-12-06 21:21 UTC (permalink / raw)
  To: Alex Horn; +Cc: peter.maydell, qemu-devel, anthony, afaerber

On Wed, Dec 5, 2012 at 7:48 PM, Alex Horn <alex.horn@cs.ox.ac.uk> wrote:
> The private buffer length field must only be incremented after the I2C
> frame has been transmitted.
>
> To expose this bug, assume the temperature in the TMP105 hardware model
> is +0.125 C (e.g. snow slush). Note that eleven bit precision is required
> to read this value; otherwise the reading is equal to zero centigrade (ice).
>
> Continue by considering the following I2C protocol steps:
>
> 1) Start transfer with I2C_START_SEND
> 2) Send byte 0x01 (i.e. configuration register)
> 3) Send byte 0x40 (i.e. eleven bit precision)
> 4) End transfer with I2C_FINISH
>
> 5) Start transfer with I2C_START_SEND
> 6) Send byte 0x00 (i.e. temperature register)
> 7) End transfer I2C_FINISH
>
> 8) Start transfer with I2C_START_RECV
> 9) Receive high-order byte of temperature
>    ...
>
> In step (1), the function tmp105_tx() is called. By the conditional
> check !s->len and the side effect with ++, s->len is equal to 1 when
> step (2) begins. Thus, 0x40 is written to s->buf[1] in step (3).
> By definition of tmp105_write(), s->config is set to zero in step (3).
> Thus, when we read the higher-order byte in step (9), it is zero!
>
> In other words, the TMP105 hardware model allows us to measure 0 C (ice)
> even with eleven bit precision when, in fact, it should be 0.125 C (slush)!
>
> Signed-off-by: Alex Horn <alex.horn@cs.ox.ac.uk>
> ---
>  hw/tmp105.c |    3 ++-
>  1 files changed, 2 insertions(+), 1 deletions(-)
>
> diff --git a/hw/tmp105.c b/hw/tmp105.c
> index 8e8dbd9..5f41a3f 100644
> --- a/hw/tmp105.c
> +++ b/hw/tmp105.c
> @@ -152,7 +152,7 @@ static int tmp105_tx(I2CSlave *i2c, uint8_t data)
>  {
>      TMP105State *s = (TMP105State *) i2c;
>
> -    if (!s->len ++)
> +    if (s->len == 0)

Please fix coding style (add braces) while touching this line.

QEMU code is not yet consistent with our CODING_STYLE, but changes
should make progress towards that.

>          s->pointer = data;
>      else {
>          if (s->len <= 2)
> @@ -160,6 +160,7 @@ static int tmp105_tx(I2CSlave *i2c, uint8_t data)
>          tmp105_write(s);
>      }
>
> +    s->len ++;

Please remove the space between s->len and ++. However, I don't think
the change is entirely correct since the 'else' clause currently seems
to take the increment into account:
        if (s->len <= 2)
            s->buf[s->len - 1] = data;
        tmp105_write(s);

Shouldn't the '- 1'  in the middle line be removed?

>      return 0;
>  }
>
> --
> 1.7.6.5
>
>

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [Qemu-devel] [PATCH 1/1] tmp105: Fix I2C protocol bug
  2012-12-06 21:21 ` Blue Swirl
@ 2012-12-07 13:12   ` Alex Horn
  0 siblings, 0 replies; 4+ messages in thread
From: Alex Horn @ 2012-12-07 13:12 UTC (permalink / raw)
  To: qemu-devel; +Cc: Blue Swirl, peter.maydell, afaerber, anthony

> [T]he change is entirely correct since the 'else' clause currently seems
> to take the increment into account:
>         if (s->len <= 2)
>             s->buf[s->len - 1] = data;
>         tmp105_write(s);
>
> Shouldn't the '- 1'  in the middle line be removed?

Several clarifications follow.

The bug seems to occur because the else clause does not correctly
account for the increment (i.e. !s->len ++). If this increment
operation in the conditional statement should remain there, the
assignment "s->buf[s->len - 1] = data" would appear to have to be
changed to "s->buf[s->len - 2] = data". However, this offset
adjustment would only shadow the root cause of the bug.

To further clarify this bug, I have also created a standalone unit test:

   https://github.com/ahorn/benchmarks/blob/master/qemu-hw/tmp105/tmp105-test.c#L130

This test case would fail in the unmodified tmp105.c hardware model.
That is, you can produce the runtime assertion failure by reverting
the fix I have implemented in tmp105_tx() in the file

  https://github.com/ahorn/benchmarks/blob/master/qemu-hw/tmp105/tmp105.c#L140

There is also one correction about the bug description itself (see below).

 >> 8) Start transfer with I2C_START_RECV
>> 9) Receive high-order byte of temperature
>>    ...
>> Thus, when we read the higher-order byte in step (9), it is zero!

This description should read instead:

  [as before] ...

  8) Start transfer with I2C_START_RECV
  9) Receive high-order byte of temperature
10) Receive low-order byte of temperature

Thus, when we read the low-order byte in step (10), it is zero.

The rest of the argument remains the same.

I hope this correction and the unit test help in clarifying the source
of the error. I would greatly appreciate your thoughts on this.

With best regards,
Alex

On 6 December 2012 21:21, Blue Swirl <blauwirbel@gmail.com> wrote:
> On Wed, Dec 5, 2012 at 7:48 PM, Alex Horn <alex.horn@cs.ox.ac.uk> wrote:
>> The private buffer length field must only be incremented after the I2C
>> frame has been transmitted.
>>
>> To expose this bug, assume the temperature in the TMP105 hardware model
>> is +0.125 C (e.g. snow slush). Note that eleven bit precision is required
>> to read this value; otherwise the reading is equal to zero centigrade (ice).
>>
>> Continue by considering the following I2C protocol steps:
>>
>> 1) Start transfer with I2C_START_SEND
>> 2) Send byte 0x01 (i.e. configuration register)
>> 3) Send byte 0x40 (i.e. eleven bit precision)
>> 4) End transfer with I2C_FINISH
>>
>> 5) Start transfer with I2C_START_SEND
>> 6) Send byte 0x00 (i.e. temperature register)
>> 7) End transfer I2C_FINISH
>>
>> 8) Start transfer with I2C_START_RECV
>> 9) Receive high-order byte of temperature
>>    ...
>>
>> In step (1), the function tmp105_tx() is called. By the conditional
>> check !s->len and the side effect with ++, s->len is equal to 1 when
>> step (2) begins. Thus, 0x40 is written to s->buf[1] in step (3).
>> By definition of tmp105_write(), s->config is set to zero in step (3).
>> Thus, when we read the higher-order byte in step (9), it is zero!
>>
>> In other words, the TMP105 hardware model allows us to measure 0 C (ice)
>> even with eleven bit precision when, in fact, it should be 0.125 C (slush)!
>>
>> Signed-off-by: Alex Horn <alex.horn@cs.ox.ac.uk>
>> ---
>>  hw/tmp105.c |    3 ++-
>>  1 files changed, 2 insertions(+), 1 deletions(-)
>>
>> diff --git a/hw/tmp105.c b/hw/tmp105.c
>> index 8e8dbd9..5f41a3f 100644
>> --- a/hw/tmp105.c
>> +++ b/hw/tmp105.c
>> @@ -152,7 +152,7 @@ static int tmp105_tx(I2CSlave *i2c, uint8_t data)
>>  {
>>      TMP105State *s = (TMP105State *) i2c;
>>
>> -    if (!s->len ++)
>> +    if (s->len == 0)
>
> Please fix coding style (add braces) while touching this line.
>
> QEMU code is not yet consistent with our CODING_STYLE, but changes
> should make progress towards that.
>
>>          s->pointer = data;
>>      else {
>>          if (s->len <= 2)
>> @@ -160,6 +160,7 @@ static int tmp105_tx(I2CSlave *i2c, uint8_t data)
>>          tmp105_write(s);
>>      }
>>
>> +    s->len ++;
>
> Please remove the space between s->len and ++. However, I don't think
> the change is entirely correct since the 'else' clause currently seems
> to take the increment into account:
>         if (s->len <= 2)
>             s->buf[s->len - 1] = data;
>         tmp105_write(s);
>
> Shouldn't the '- 1'  in the middle line be removed?
>
>>      return 0;
>>  }
>>
>> --
>> 1.7.6.5
>>
>>

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [Qemu-devel] [PATCH 1/1] tmp105: Fix I2C protocol bug
  2012-12-05 19:48 [Qemu-devel] [PATCH 1/1] tmp105: Fix I2C protocol bug Alex Horn
  2012-12-06 21:21 ` Blue Swirl
@ 2012-12-12  4:34 ` Andreas Färber
  1 sibling, 0 replies; 4+ messages in thread
From: Andreas Färber @ 2012-12-12  4:34 UTC (permalink / raw)
  To: Alex Horn; +Cc: Blue Swirl, peter.maydell, qemu-devel, anthony

Am 05.12.2012 20:48, schrieb Alex Horn:
> The private buffer length field must only be incremented after the I2C
> frame has been transmitted.
> 
> To expose this bug, assume the temperature in the TMP105 hardware model
> is +0.125 C (e.g. snow slush). Note that eleven bit precision is required
> to read this value; otherwise the reading is equal to zero centigrade (ice).
> 
> Continue by considering the following I2C protocol steps:
> 
> 1) Start transfer with I2C_START_SEND
> 2) Send byte 0x01 (i.e. configuration register)
> 3) Send byte 0x40 (i.e. eleven bit precision)
> 4) End transfer with I2C_FINISH
> 
> 5) Start transfer with I2C_START_SEND
> 6) Send byte 0x00 (i.e. temperature register)
> 7) End transfer I2C_FINISH
> 
> 8) Start transfer with I2C_START_RECV
> 9) Receive high-order byte of temperature
>    ...
> 
> In step (1), the function tmp105_tx() is called. By the conditional
> check !s->len and the side effect with ++, s->len is equal to 1 when
> step (2) begins. Thus, 0x40 is written to s->buf[1] in step (3).
> By definition of tmp105_write(), s->config is set to zero in step (3).
> Thus, when we read the higher-order byte in step (9), it is zero!
> 
> In other words, the TMP105 hardware model allows us to measure 0 C (ice)
> even with eleven bit precision when, in fact, it should be 0.125 C (slush)!
> 
> Signed-off-by: Alex Horn <alex.horn@cs.ox.ac.uk>
> ---
>  hw/tmp105.c |    3 ++-
>  1 files changed, 2 insertions(+), 1 deletions(-)
> 
> diff --git a/hw/tmp105.c b/hw/tmp105.c
> index 8e8dbd9..5f41a3f 100644
> --- a/hw/tmp105.c
> +++ b/hw/tmp105.c
> @@ -152,7 +152,7 @@ static int tmp105_tx(I2CSlave *i2c, uint8_t data)
>  {
>      TMP105State *s = (TMP105State *) i2c;
>  
> -    if (!s->len ++)
> +    if (s->len == 0)
>          s->pointer = data;
>      else {
>          if (s->len <= 2)
> @@ -160,6 +160,7 @@ static int tmp105_tx(I2CSlave *i2c, uint8_t data)
>          tmp105_write(s);
>      }
>  
> +    s->len ++;
>      return 0;
>  }
>  

This fix leaves setting the upper and lower limits (commands 2 and 3)
broken. The following works for me:

diff --git a/hw/tmp105.c b/hw/tmp105.c
index 8e8dbd9..512fe0c 100644
--- a/hw/tmp105.c
+++ b/hw/tmp105.c
@@ -152,11 +152,14 @@ static int tmp105_tx(I2CSlave *i2c, uint8_t data)
 {
     TMP105State *s = (TMP105State *) i2c;

-    if (!s->len ++)
+    if (s->len == 0) {
         s->pointer = data;
-    else {
-        if (s->len <= 2)
+        s->len++;
+    } else {
+        if (s->len <= 2) {
             s->buf[s->len - 1] = data;
+        }
+        s->len++;
         tmp105_write(s);
     }


The trick is incrementing before the tmp105_write() call but after the
length check and buffer fill.

I've hacked together a real qtest using the n800's omap_i2c that I'll
send out. It sets and reads back the limits, failing in master.

Andreas

^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2012-12-12  4:35 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-12-05 19:48 [Qemu-devel] [PATCH 1/1] tmp105: Fix I2C protocol bug Alex Horn
2012-12-06 21:21 ` Blue Swirl
2012-12-07 13:12   ` Alex Horn
2012-12-12  4:34 ` Andreas Färber

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).