diff mbox

[PATCH_v2] ahci.c: mask unused flags when reading size PRDT DBC

Message ID 1404213207-89115-1-git-send-email-reza.jelveh@tuhh.de
State New
Headers show

Commit Message

Reza Jelveh July 1, 2014, 11:13 a.m. UTC
From: Reza Jelveh <reza.jelveh@tuhh.de>

The data byte count(DBC) read from the description information is defined for
bits 21:00. Bits 30:22 are reserved and bit 31 is the Interrupt on Completion
(I) flag.

Completion interrupts are triggered after every transaction instead of on
I-flag in QEMU. tbl_entry_size is a signed integer and improperly reading the
DBC leads to a negative offset that causes sglist allocation to fail.

Signed-off-by: Reza Jelveh <reza.jelveh@tuhh.de>
---
This requires a custom ovmf image with sata controller for testing:

http://reza.jelveh.me/assets/OVMF.fd.bz2

Signed-off-by: Reza Jelveh <reza.jelveh@tuhh.de>
---
 hw/ide/ahci.c | 11 ++++++++---
 hw/ide/ahci.h |  2 ++
 2 files changed, 10 insertions(+), 3 deletions(-)

Comments

Alexander Graf July 1, 2014, 11:19 a.m. UTC | #1
On 01.07.14 13:13, reza.jelveh@tuhh.de wrote:
> From: Reza Jelveh <reza.jelveh@tuhh.de>
>
> The data byte count(DBC) read from the description information is defined for
> bits 21:00. Bits 30:22 are reserved and bit 31 is the Interrupt on Completion
> (I) flag.
>
> Completion interrupts are triggered after every transaction instead of on
> I-flag in QEMU. tbl_entry_size is a signed integer and improperly reading the
> DBC leads to a negative offset that causes sglist allocation to fail.
>
> Signed-off-by: Reza Jelveh <reza.jelveh@tuhh.de>
> ---
> This requires a custom ovmf image with sata controller for testing:
>
> http://reza.jelveh.me/assets/OVMF.fd.bz2
>
> Signed-off-by: Reza Jelveh <reza.jelveh@tuhh.de>

Reviewed-by: Alexander Graf <agraf@suse.de>

I'm still puzzled that this ever worked at all ;).


Alex
Kevin Wolf July 1, 2014, 11:36 a.m. UTC | #2
Am 01.07.2014 um 13:13 hat reza.jelveh@tuhh.de geschrieben:
> From: Reza Jelveh <reza.jelveh@tuhh.de>
> 
> The data byte count(DBC) read from the description information is defined for
> bits 21:00. Bits 30:22 are reserved and bit 31 is the Interrupt on Completion
> (I) flag.
> 
> Completion interrupts are triggered after every transaction instead of on
> I-flag in QEMU. tbl_entry_size is a signed integer and improperly reading the
> DBC leads to a negative offset that causes sglist allocation to fail.
> 
> Signed-off-by: Reza Jelveh <reza.jelveh@tuhh.de>
> ---
> This requires a custom ovmf image with sata controller for testing:
> 
> http://reza.jelveh.me/assets/OVMF.fd.bz2
> 
> Signed-off-by: Reza Jelveh <reza.jelveh@tuhh.de>

Reviewed-by: Kevin Wolf <kwolf@redhat.com>

The spec also seems to require an even byte count, which we don't seem
to check. Do we want to add this? (In a separate patch, of course.)

We'll also want a qtest case to verify the fix and for regression
testing. John?

And finally, please don't forget to CC the block maintainers (Stefan and
me) for any AHCI patches.

Kevin
Alexander Graf July 1, 2014, 11:49 a.m. UTC | #3
On 01.07.14 13:36, Kevin Wolf wrote:
> Am 01.07.2014 um 13:13 hat reza.jelveh@tuhh.de geschrieben:
>> From: Reza Jelveh <reza.jelveh@tuhh.de>
>>
>> The data byte count(DBC) read from the description information is defined for
>> bits 21:00. Bits 30:22 are reserved and bit 31 is the Interrupt on Completion
>> (I) flag.
>>
>> Completion interrupts are triggered after every transaction instead of on
>> I-flag in QEMU. tbl_entry_size is a signed integer and improperly reading the
>> DBC leads to a negative offset that causes sglist allocation to fail.
>>
>> Signed-off-by: Reza Jelveh <reza.jelveh@tuhh.de>
>> ---
>> This requires a custom ovmf image with sata controller for testing:
>>
>> http://reza.jelveh.me/assets/OVMF.fd.bz2
>>
>> Signed-off-by: Reza Jelveh <reza.jelveh@tuhh.de>
> Reviewed-by: Kevin Wolf <kwolf@redhat.com>
>
> The spec also seems to require an even byte count, which we don't seem
> to check. Do we want to add this? (In a separate patch, of course.)

We could just remove the lowest bit in the mask, no? ;)


Alex
John Snow July 1, 2014, 2:47 p.m. UTC | #4
On 07/01/2014 07:49 AM, Alexander Graf wrote:
>
> On 01.07.14 13:36, Kevin Wolf wrote:
>> Am 01.07.2014 um 13:13 hat reza.jelveh@tuhh.de geschrieben:
>>> From: Reza Jelveh <reza.jelveh@tuhh.de>
>>>
>>> The data byte count(DBC) read from the description information is 
>>> defined for
>>> bits 21:00. Bits 30:22 are reserved and bit 31 is the Interrupt on 
>>> Completion
>>> (I) flag.
>>>
>>> Completion interrupts are triggered after every transaction instead 
>>> of on
>>> I-flag in QEMU. tbl_entry_size is a signed integer and improperly 
>>> reading the
>>> DBC leads to a negative offset that causes sglist allocation to fail.
>>>
>>> Signed-off-by: Reza Jelveh <reza.jelveh@tuhh.de>
>>> ---
>>> This requires a custom ovmf image with sata controller for testing:
>>>
>>> http://reza.jelveh.me/assets/OVMF.fd.bz2
>>>
>>> Signed-off-by: Reza Jelveh <reza.jelveh@tuhh.de>
>> Reviewed-by: Kevin Wolf <kwolf@redhat.com>
>>
>> The spec also seems to require an even byte count, which we don't seem
>> to check. Do we want to add this? (In a separate patch, of course.)
>
> We could just remove the lowest bit in the mask, no? ;)
>
>
> Alex
>
Reviewed-by: John Snow <jsnow@redhat.com>

Taking a look at the spec, AHCI 1.3 sec 4.2.3.3 p. 40; a value of 0x00 
implies one byte, and 0x01 implies two bytes. Masking off the one bit 
would probably lead to an off-by-one somewhere. The spec does state that 
it requires the 0th bit to be set, so in a separate patch we should 
check to make sure, but the mask as-is is appropriate.

--John
Stefan Hajnoczi July 2, 2014, 8:17 a.m. UTC | #5
On Tue, Jul 01, 2014 at 01:13:27PM +0200, reza.jelveh@tuhh.de wrote:
> From: Reza Jelveh <reza.jelveh@tuhh.de>
> 
> The data byte count(DBC) read from the description information is defined for
> bits 21:00. Bits 30:22 are reserved and bit 31 is the Interrupt on Completion
> (I) flag.
> 
> Completion interrupts are triggered after every transaction instead of on
> I-flag in QEMU. tbl_entry_size is a signed integer and improperly reading the
> DBC leads to a negative offset that causes sglist allocation to fail.
> 
> Signed-off-by: Reza Jelveh <reza.jelveh@tuhh.de>
> ---
> This requires a custom ovmf image with sata controller for testing:
> 
> http://reza.jelveh.me/assets/OVMF.fd.bz2
> 
> Signed-off-by: Reza Jelveh <reza.jelveh@tuhh.de>
> ---
>  hw/ide/ahci.c | 11 ++++++++---
>  hw/ide/ahci.h |  2 ++
>  2 files changed, 10 insertions(+), 3 deletions(-)

Thanks, applied to my block tree:
https://github.com/stefanha/qemu/commits/block

Stefan
John Snow July 12, 2014, 12:24 a.m. UTC | #6
On 07/01/2014 07:36 AM, Kevin Wolf wrote:
> Am 01.07.2014 um 13:13 hat reza.jelveh@tuhh.de geschrieben:
>> From: Reza Jelveh <reza.jelveh@tuhh.de>
>>
>> The data byte count(DBC) read from the description information is defined for
>> bits 21:00. Bits 30:22 are reserved and bit 31 is the Interrupt on Completion
>> (I) flag.
>>
>> Completion interrupts are triggered after every transaction instead of on
>> I-flag in QEMU. tbl_entry_size is a signed integer and improperly reading the
>> DBC leads to a negative offset that causes sglist allocation to fail.
>>
>> Signed-off-by: Reza Jelveh <reza.jelveh@tuhh.de>
>> ---
>> This requires a custom ovmf image with sata controller for testing:
>>
>> http://reza.jelveh.me/assets/OVMF.fd.bz2
>>
>> Signed-off-by: Reza Jelveh <reza.jelveh@tuhh.de>
> Reviewed-by: Kevin Wolf <kwolf@redhat.com>
>
> The spec also seems to require an even byte count, which we don't seem
> to check. Do we want to add this? (In a separate patch, of course.)
>
> We'll also want a qtest case to verify the fix and for regression
> testing. John?
>
> And finally, please don't forget to CC the block maintainers (Stefan and
> me) for any AHCI patches.
>
> Kevin
>
The test_identify case I submitted a patch for ( 
http://lists.gnu.org/archive/html/qemu-devel/2014-07/msg01241.html ) 
does attempt to use the interrupt bit, and as such, will fail without 
Reza's patch.

The current version of the test only warns when it doesn't see the 
interrupt posted, though, but that's not relevant to testing this 
particular fix.

--John
diff mbox

Patch

diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
index 9bae22e..cd140d1 100644
--- a/hw/ide/ahci.c
+++ b/hw/ide/ahci.c
@@ -639,6 +639,11 @@  static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis)
     }
 }
 
+static int prdt_tbl_entry_size(const AHCI_SG *tbl)
+{
+    return (le32_to_cpu(tbl->flags_size) & AHCI_PRDT_SIZE_MASK) + 1;
+}
+
 static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist, int offset)
 {
     AHCICmdHdr *cmd = ad->cur_cmd;
@@ -681,7 +686,7 @@  static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist, int offset)
         sum = 0;
         for (i = 0; i < sglist_alloc_hint; i++) {
             /* flags_size is zero-based */
-            tbl_entry_size = (le32_to_cpu(tbl[i].flags_size) + 1);
+            tbl_entry_size = prdt_tbl_entry_size(&tbl[i]);
             if (offset <= (sum + tbl_entry_size)) {
                 off_idx = i;
                 off_pos = offset - sum;
@@ -700,12 +705,12 @@  static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist, int offset)
         qemu_sglist_init(sglist, qbus->parent, (sglist_alloc_hint - off_idx),
                          ad->hba->as);
         qemu_sglist_add(sglist, le64_to_cpu(tbl[off_idx].addr + off_pos),
-                        le32_to_cpu(tbl[off_idx].flags_size) + 1 - off_pos);
+                        prdt_tbl_entry_size(&tbl[off_idx]) - off_pos);
 
         for (i = off_idx + 1; i < sglist_alloc_hint; i++) {
             /* flags_size is zero-based */
             qemu_sglist_add(sglist, le64_to_cpu(tbl[i].addr),
-                            le32_to_cpu(tbl[i].flags_size) + 1);
+                            prdt_tbl_entry_size(&tbl[i]));
         }
     }
 
diff --git a/hw/ide/ahci.h b/hw/ide/ahci.h
index 9a4064f..f418b30 100644
--- a/hw/ide/ahci.h
+++ b/hw/ide/ahci.h
@@ -201,6 +201,8 @@ 
 
 #define AHCI_COMMAND_TABLE_ACMD            0x40
 
+#define AHCI_PRDT_SIZE_MASK                0x3fffff
+
 #define IDE_FEATURE_DMA                    1
 
 #define READ_FPDMA_QUEUED                  0x60