On 01/18/16 22:05, John Snow wrote:
> 
> 
> On 01/18/2016 02:33 PM, Laszlo Ersek wrote:
>> On 01/18/16 19:44, John Snow wrote:
>>>
>>>
>>> On 01/18/2016 05:57 AM, Laszlo Ersek wrote:
>>>> Hello Feng, John,
>>>>
>>>> On 11/03/15 02:48, Tian Feng wrote:
>>>>> When executing ATAPI cmd at IDE mode, EFI_SUCCESS may be returned wrongly
>>>>> with old logic but in fact DRQ is not ready and the transaction doesn't
>>>>> get executed correctly at this time.
>>>>>
>>>>> Contributed-under: TianoCore Contribution Agreement 1.0
>>>>> Signed-off-by: Feng Tian <feng.t...@intel.com>
>>>>> Cc: Star Zeng <star.z...@intel.com>
>>>>> ---
>>>>>  MdeModulePkg/Bus/Ata/AtaAtapiPassThru/IdeMode.c | 2 +-
>>>>>  1 file changed, 1 insertion(+), 1 deletion(-)
>>>>>
>>>>> diff --git a/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/IdeMode.c 
>>>>> b/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/IdeMode.c
>>>>> index 4928ed5..f74e5ca 100644
>>>>> --- a/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/IdeMode.c
>>>>> +++ b/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/IdeMode.c
>>>>> @@ -1949,7 +1949,7 @@ AtaPacketReadWrite (
>>>>>      //
>>>>>      Status = DRQReady2 (PciIo, IdeRegisters, Timeout);
>>>>>      if (EFI_ERROR (Status)) {
>>>>> -      return CheckStatusRegister (PciIo, IdeRegisters);
>>>>> +      return Status;
>>>>>      }
>>>>>  
>>>>>      //
>>>>>
>>>>
>>>> Unfortunately, this patch breaks OVMF on QEMU's ide-cd device. The
>>>> symptoms were reported in <https://github.com/tianocore/edk2/issues/43>;
>>>
>>> To confirm, does this affect both the AHCI ATAPI device and the BMDMA
>>> ATAPI device? (Q35 and PC default HBA, respectively) Or is it just AHCI?
>>
>> I only used i440fx for testing. This is my command line:
>>
>> qemu-system-x86_64 \
>>   -debugcon file:ovmf.log \
>>   -global isa-debugcon.iobase=0x402 \
>>   -net none \
>>   -enable-kvm \
>>   -pflash OVMF.fd \
>>   -drive id=cd0,if=none,readonly,format=raw,file=f22.iso \
>>   -device ide-cd,drive=cd0,bootindex=0
>>
>> If I add "-M q35" (--> AHCI), then things seem to work fine; I can reach
>> grub on the F22 workstation installer CD.
>>
>>>> I reproduced the issue and bisected it to this patch (git commit
>>>> 7cac2401 / SVN 19611). I also confirmed it by reverting the patch on top
>>>> of current master, and the CD-ROM started to work.
>>>>
>>>> I'm adding John Snow, who maintains QEMU's IDE emulation.
>>>>
>>>> Can you guys please investigate this patch, and discuss why it breaks on
>>>> QEMU's ide-cd device?
>>>>
>>>> John, if you'd like to browse the code, the following link highlights
>>>> the line that the above patch changes:
>>>>
>>>> https://github.com/tianocore/edk2/blob/7cac2401635317360226a235d1f95f8347081cbb/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/IdeMode.c#L1952
>>>>
>>>> As far as I can see, this problem could be timing-sensitive. The
>>>> DRQReady2() function polls for the DRQ bit to become set, in a certain
>>>> amount of time. If that doesn't happen (or a definitive error emerges),
>>>> the function fails.
>>>>
>>>> Pre-patch, such failure would not be decisive; the status register would
>>>> be read and evaluated. Post-patch, the failure is decisive, even if it
>>>> is just a timeout, and the CheckStatusRegister() call would otherwise
>>>> return success.
>>>>
>>>> ... Hm, I added a debug log right after DRQReady2(), on the error branch
>>>> (i.e., when it fails). The status code is *not* EFI_TIMEOUT, it is
>>>> EFI_NOT_READY.
>>>>
>>>> Looking at DRQReady2(), this means:
>>>> - BSY is clear
>>>> - ERR is clear
>>>> - DRQ is clear too
>>>>
>>>> Apparently, the DRQReady2() function expects that as soon as BSY is
>>>> clear (and there is no error), DRQ must be immediately set (more or
>>>> less: not busy, no error, so ready to transfer data).
>>>>
>>>
>>> Thank you for your research!
>>>
>>>> Is that a valid assumption to make?
>>>>
>>>
>>> Whenever BSY, DRQ and ERR are all clear that should be confirmation of a
>>> completed command.
>>
>> That's interesting, because the edk2 code seems to treat this condition
>> as "failure to transition to data transfer", or some such.
>>
> 
> If it's in the middle of a transfer loop, it's a weird condition to
> encounter. We expect to see BSY and/or DRQ for the duration of the
> command. At the end of the command, though, READY|SEEK is the normal
> idle status.
> 
>>>                    Under AHCI, this should *not* be true before the AHCI
>>> device has signalled completion (Either via the status shadow register,
>>> an interrupt, or an MSI interrupt, depending on device configuration.)
>>>
>>> (In fact, hw/ide/core.c ide_exec_cmd uses the presence of DRQ|BSY to
>>> know whether or not it is in a state fit to accept a new command. If the
>>> guest is not blocked when BSY|DRQ are unset, this means a new command
>>> can be issued.)
>>>
>>> == Thought #1: ==
>>>
>>> However, when a command completes, it updates the shadow registers in
>>> the AHCI HBA right before signalling completion. It is, I think,
>>> possible that if you are polling the AHCI shadow registers instead of
>>> polling the AHCI status registers that you could find DRQ/BSY/ERR unset
>>> before receiving a command completed notice (by a time window of two
>>> passing electrons.)
>>>
>>> Take a look at ahci_cmd_done, which calls ahci_write_fis_d2h. We
>>> populate a guest memory FIS buffer with the values of the registers,
>>> then update the status shadow register, then signal completion (via IRQ,
>>> MSI, or neither.)
>>>
>>> You are probably not hitting that reliably, so can I ask what command is
>>> erroring out, here? 0xA0 PACKET for IDE, and 0x28 CMD_READ for SCSI with
>>> the DMA bit off? There might be a problem with the device not managing
>>> its state well.
>>
>> Again, I can only see it with i440fx. This is the section of the IDE
>> debug log from QEMU that fails:
>>
> 
> That helps narrow it down, at least. AHCI is hard to track sometimes due
> to all the register shadowing going on.
> 
> Are you ready for more information than you wanted? Let's go!
> 
>> ide: CMD=a0
> 
> ATAPI packet command received by ide_exec_cmd.
> 
> (NOTE: I really should add a debug printf here in cmd_packet that tells
> us if it's PIO or DMA, that'd be helpful!)
> 
>> ide: read addr=0x177 val=58
> 
> primary status read by OVMF; 0x58 is READY | SEEK | DRQ. Interrupts are
> reset.
> OVMF sends the ATAPI/SCSI packet.
> QEMU receives the last expected byte of this DRQ phase.
> DRQ is unset.
> ide_data_writew invokes ide_atapi_cmd as s->end_transfer_func().
> 
>> ATAPI limit=0xfffe packet: 12 00 00 00 60 00 00 00 00 00 00 00
> 
> DRQ read phase of the SCSI packet is over; command is 0x12 INQUIRY.
> Max length OVMF is prepared to receive is 60 bytes.
> QEMU prepares the reply in s->io_buffer.
> Callchain is ide_atapi_cmd -> cmd_inquiry -> ide_atapi_cmd_reply.
> Status (if pio ...) is set to 0x50, (READY | SEEK).
> 
>> reply: tx_size=36 elem_tx_size=0 index=0
> 
> ide_atapi_cmd_reply_end has been called and we are preparing to send
> data back to OVMF.
> 
>> byte_count_limit=65534
> 
> OVMF has requested no more than 0xfffe bytes at a time. We only have 36
> bytes to send. s->hcyl and s->lcyl are updated to reflect the new limit
> of just 36 bytes.
> 
> QEMU starts the PIO transfer and sets +DRQ again.
> 
>> status=0x58
> 
> print from ide_atapi_cmd_reply_end prints the status, +DRQ +READY +SEEK.
> 
>> ide: read status addr=0x376 val=58
> 
> Alternate status read by OVMF, any pending interrupts are not reset. DRQ
> is set, so OVMF is free to read our prepared reply.
> 
>> ide: read addr=0x175 val=00
> 
> OVMF reads s->hcyl, gets 0x00.
> 
>> ide: read addr=0x174 val=24
> 
> OVMF reads s->lcyl, gets 0x24. OVMF now knows to expect 0x24 = 36 bytes
> of data.

Aha!

> There are no prints for clues in the middle here, but OVMF should have
> then read 36 bytes, which causes ide_data_readw to:
> - Clear DRQ
> - Invoke s->end_transfer_func, which is ide_atapi_cmd_reply_end.
> 
>> reply: tx_size=0 elem_tx_size=0 index=36
>> end of transfer, status=0x50
> 
> ide_atapi_cmd_reply_end is invoked as a callback, nothing left to
> transfer; tx_size is 0 and the index is equal to the total transfer
> size. DRQ remains unset and we begin cleaning up by calling
> ide_atapi_cmd_ok:
> 
> - status is set to 0x50 (READY | SEEK)
> - IRQ is raised.
> 
>> ide: read addr=0x177 val=50
> 
> Primary status register is read, IRQ is cleared, and status is READY|SEEK.
> 
>> ide: read status addr=0x376 val=50  <---- triggers the failure in edk2
> 
> Alternate status register is read. Status is still 0x50.
> 
> At this point, the transfer is done and EDK2 should not expect further
> bytes. READY|SEEK should not be interpreted as an error, IMO.
> 
> Is there some reason it's still expecting data (i.e., waiting for DRQ?)

This is extremely helpful, thanks!

The edk2 code is [MdeModulePkg/Bus/Ata/AtaAtapiPassThru/IdeMode.c], sorry for 
the big paste:

  1896  /**
  1897    This function is called by AtaPacketCommandExecute().
  1898    It is used to transfer data between host and device. The data 
direction is specified
  1899    by the fourth parameter.
  1900  
  1901    @param PciIo         A pointer to ATA_ATAPI_PASS_THRU_INSTANCE data 
structure.
  1902    @param IdeRegisters  A pointer to EFI_IDE_REGISTERS data structure.
  1903    @param Buffer        Buffer contained data transferred between host 
and device.
  1904    @param ByteCount     Data size in byte unit of the buffer.
  1905    @param Read          Flag used to determine the data transfer 
direction.
  1906                         Read equals 1, means data transferred from 
device to host;
  1907                         Read equals 0, means data transferred from host 
to device.
  1908    @param Timeout       Timeout value for wait DRQ ready before each 
data stream's transfer
  1909                         , uses 100ns as a unit.
  1910  
  1911    @retval EFI_SUCCESS      data is transferred successfully.
  1912    @retval EFI_DEVICE_ERROR the device failed to transfer data.
  1913  **/
  1914  EFI_STATUS
  1915  EFIAPI
  1916  AtaPacketReadWrite (
  1917    IN     EFI_PCI_IO_PROTOCOL       *PciIo,
  1918    IN     EFI_IDE_REGISTERS         *IdeRegisters,
  1919    IN OUT VOID                      *Buffer,
  1920    IN     UINT64                    ByteCount,
  1921    IN     BOOLEAN                   Read,
  1922    IN     UINT64                    Timeout
  1923    )
  1924  {
  1925    UINT32      RequiredWordCount;
  1926    UINT32      ActualWordCount;
  1927    UINT32      WordCount;
  1928    EFI_STATUS  Status;
  1929    UINT16      *PtrBuffer;
  1930  
  1931    //
  1932    // No data transfer is premitted.
  1933    //
  1934    if (ByteCount == 0) {
  1935      return EFI_SUCCESS;
  1936    }
  1937  
  1938    PtrBuffer         = Buffer;
  1939    RequiredWordCount = (UINT32)RShiftU64(ByteCount, 1);
  1940    //
  1941    // ActuralWordCount means the word count of data really transferred.
  1942    //
  1943    ActualWordCount = 0;
  1944  
  1945    while (ActualWordCount < RequiredWordCount) {
  1946      //
  1947      // before each data transfer stream, the host should poll DRQ bit 
ready,
  1948      // to see whether indicates device is ready to transfer data.
  1949      //
  1950      Status = DRQReady2 (PciIo, IdeRegisters, Timeout);
  1951      if (EFI_ERROR (Status)) {
  1952        return Status;
  1953      }
  1954  
  1955      //
  1956      // get current data transfer size from Cylinder Registers.
  1957      //
  1958      WordCount  = IdeReadPortB (PciIo, IdeRegisters->CylinderMsb) << 8;
  1959      WordCount  = WordCount | IdeReadPortB (PciIo, 
IdeRegisters->CylinderLsb);
  1960      WordCount  = WordCount & 0xffff;
  1961      WordCount /= 2;
  1962  
  1963      WordCount = MIN (WordCount, (RequiredWordCount - ActualWordCount));
  1964  
  1965      if (Read) {
  1966        IdeReadPortWMultiple (
  1967          PciIo,
  1968          IdeRegisters->Data,
  1969          WordCount,
  1970          PtrBuffer
  1971          );
  1972      } else {
  1973        IdeWritePortWMultiple (
  1974          PciIo,
  1975          IdeRegisters->Data,
  1976          WordCount,
  1977          PtrBuffer
  1978          );
  1979      }
  1980  
  1981      //
  1982      // read status register to check whether error happens.
  1983      //
  1984      Status = CheckStatusRegister (PciIo, IdeRegisters);
  1985      if (EFI_ERROR (Status)) {
  1986        return EFI_DEVICE_ERROR;
  1987      }
  1988  
  1989      PtrBuffer       += WordCount;
  1990      ActualWordCount += WordCount;
  1991    }
  1992  
  1993    if (Read) {
  1994      //
  1995      // In the case where the drive wants to send more data than we need 
to read,
  1996      // the DRQ bit will be set and cause delays from DRQClear2().
  1997      // We need to read data from the drive until it clears DRQ so we 
can move on.
  1998      //
  1999      AtaPacketReadPendingData (PciIo, IdeRegisters);
  2000    }
  2001  
  2002    //
  2003    // read status register to check whether error happens.
  2004    //
  2005    Status = CheckStatusRegister (PciIo, IdeRegisters);
  2006    if (EFI_ERROR (Status)) {
  2007      return EFI_DEVICE_ERROR;
  2008    }
  2009  
  2010    //
  2011    // After data transfer is completed, normally, DRQ bit should clear.
  2012    //
  2013    Status = DRQClear (PciIo, IdeRegisters, Timeout);
  2014    if (EFI_ERROR (Status)) {
  2015      return EFI_DEVICE_ERROR;
  2016    }
  2017  
  2018    return Status;
  2019  }

I think I understand what's going on!

In its current form, the loop above continues to recognize a short read -- same 
as the pre-patch version. We bail out of the loop when we notice that the 
device is willing to give us less than what we originally wanted to have.

However, the exit status of the function changed! Instead of the old action (= 
"just make sure everything is fine with the status register"), we now directly 
propagate *any and all* errors from DRQReady2(). The short read (that is, BSY=0 
&& DRQ=0 && ERR=0) situation is reported by DRQReady2() as EFI_NOT_READY, and 
we blindly propagate that EFI error code outwards, instead of reporting it as 
EFI_SUCCESS.

So, I think the fix here is to restore the original code, *conditional* on:
- we are reading, not writing, *and*
- DRQReady2() actually noticed a short read.

Let me try to come up with an edk2 patch for this.

Thank you!
Laszlo

> 
>> IDE: write addr=0x176 val=0xe0
>> ide: write control addr=0x376 val=0a
>> ide: read addr=0x177 val=50
>> IDE: write addr=0x176 val=0xe0
>> ide: read status addr=0x376 val=50
>> IDE: write addr=0x171 val=0x00
>> IDE: write addr=0x171 val=0x00
>> IDE: write addr=0x172 val=0x00
>> IDE: write addr=0x172 val=0x00
>> IDE: write addr=0x173 val=0x00
>> IDE: write addr=0x173 val=0x00
>> IDE: write addr=0x174 val=0x00
>> IDE: write addr=0x174 val=0xfe
>> IDE: write addr=0x175 val=0x00
>> IDE: write addr=0x175 val=0xff
>> IDE: write addr=0x177 val=0xa0
>>
>>
>>>
>>>
>>> == Thought #2: ==
>>>
>>> We recently updated the ATAPI pio buffering loop to be asynchronous in
>>> cases where it can be, which included some changes to when we fiddle
>>> with DRQ and BSY. These changes went live for 2.5.0. Does version 2.4.1
>>> work? That would point to the new buffering loop.
>>
>> Great tip, let me check. (The above is experienced on v2.5.0-618-g19b6d84.)
>>
>> With v2.4.1, i440fx fails the same, and q35 works the same.
>>
>>> Useful information here:
>>>
>>>     - In the 0xA0 PACKET command sent, what is the value of the
>>> byte_count_limit? (this is a 16bit count comprised of the hcyl and lcyl
>>> fields in the packet command structure.) Depending on if it matches the
>>> block size of 2048 or not, we take a different buffering path. Multiples
>>> of 2048 are buffered asynchronously, others are buffered synchronously.
>>
>> Please see above: 0xfffe; seems to imply synchronous.
>>
>>>
>>>     - In the 0xA0 PACKET command sent, what is the value of bit 0 in the
>>> "feature" field? (the ATAPI DMA bit.) Affects the buffer loop depending
>>> on which transport it's preparing to use.
>>>
>>>     - What SCSI read command is being used? (Presumed 0x28 CMD_READ)
>>
>> Apparently 0x12 INQUIRY?
>>
> 
> Good news and bad news.
> 
> Bad news is I don't have any clue right now.
> Good news is that the PIO buffering loop is annoying to debug anyway,
> and inquiry is pretty easy. It's always synchronous.
> 
> (Written before the above tracing.)
> 
>> Thanks
>> Laszlo
>>
>>>
>>>     - How many sectors are being requested?
>>>
>>>
>>>> Thanks!
>>>> Laszlo
>>>>
>>>
>>
> 
> 
> Thanks,
> --js
> 

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel

Reply via email to