752ceb1640
Signed-off-by: Peter Jones <pjones@redhat.com>
85 lines
3.3 KiB
Diff
85 lines
3.3 KiB
Diff
From 566a03a623ca5ec07c815683625aecc410dc3095 Mon Sep 17 00:00:00 2001
|
||
From: Stefan Fritsch <fritsch@genua.de>
|
||
Date: Fri, 19 Jan 2018 14:13:29 +0100
|
||
Subject: [PATCH] ahci: Improve error handling
|
||
MIME-Version: 1.0
|
||
Content-Type: text/plain; charset=UTF-8
|
||
Content-Transfer-Encoding: 8bit
|
||
|
||
Check the error bits in the interrupt status register. According to the
|
||
AHCI 1.2 spec, "Interrupt sources that are disabled (‘0’) are still
|
||
reflected in the status registers.", so this should work even though
|
||
grub uses polling
|
||
|
||
This fixes the following problem on a Fujitsu E744 laptop:
|
||
|
||
Sometimes there is a very long delay (up to several minutes) when
|
||
booting from hard disk. It seems accessing the DVD drive (which has no
|
||
disk inserted) sometimes fails with some errors, which leads to each
|
||
access being stalled until the 20s timeout triggers. This seems to
|
||
happen when grub is trying to read filesystem/partition data.
|
||
|
||
The problem is that the command_issue bit that is checked in the loop is
|
||
only reset if the "HBA receives a FIS which clears the BSY, DRQ, and ERR
|
||
bits for the command", but the ERR bit is never cleared. Therefore
|
||
command_issue is never reset and grub waits for the timeout.
|
||
|
||
The relevant bit in our case is the Task File Error Status (TFES), which
|
||
is equivalent to the ERR bit 0 in tfd. But this patch also checks
|
||
the other error bits except for the "Interface non-fatal error status"
|
||
bit.
|
||
|
||
Signed-off-by: Stefan Fritsch <fritsch@genua.de>
|
||
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
|
||
---
|
||
grub-core/disk/ahci.c | 22 ++++++++++++++++++++--
|
||
1 file changed, 20 insertions(+), 2 deletions(-)
|
||
|
||
diff --git a/grub-core/disk/ahci.c b/grub-core/disk/ahci.c
|
||
index 494a1b7734e..f2f606423ac 100644
|
||
--- a/grub-core/disk/ahci.c
|
||
+++ b/grub-core/disk/ahci.c
|
||
@@ -82,6 +82,20 @@ enum grub_ahci_hba_port_command
|
||
GRUB_AHCI_HBA_PORT_CMD_FR = 0x4000,
|
||
};
|
||
|
||
+enum grub_ahci_hba_port_int_status
|
||
+ {
|
||
+ GRUB_AHCI_HBA_PORT_IS_IFS = (1UL << 27),
|
||
+ GRUB_AHCI_HBA_PORT_IS_HBDS = (1UL << 28),
|
||
+ GRUB_AHCI_HBA_PORT_IS_HBFS = (1UL << 29),
|
||
+ GRUB_AHCI_HBA_PORT_IS_TFES = (1UL << 30),
|
||
+ };
|
||
+
|
||
+#define GRUB_AHCI_HBA_PORT_IS_FATAL_MASK ( \
|
||
+ GRUB_AHCI_HBA_PORT_IS_IFS | \
|
||
+ GRUB_AHCI_HBA_PORT_IS_HBDS | \
|
||
+ GRUB_AHCI_HBA_PORT_IS_HBFS | \
|
||
+ GRUB_AHCI_HBA_PORT_IS_TFES)
|
||
+
|
||
struct grub_ahci_hba
|
||
{
|
||
grub_uint32_t cap;
|
||
@@ -1026,7 +1040,8 @@ grub_ahci_readwrite_real (struct grub_ahci_device *dev,
|
||
|
||
endtime = grub_get_time_ms () + (spinup ? 20000 : 20000);
|
||
while ((dev->hba->ports[dev->port].command_issue & 1))
|
||
- if (grub_get_time_ms () > endtime)
|
||
+ if (grub_get_time_ms () > endtime ||
|
||
+ (dev->hba->ports[dev->port].intstatus & GRUB_AHCI_HBA_PORT_IS_FATAL_MASK))
|
||
{
|
||
grub_dprintf ("ahci", "AHCI status <%x %x %x %x>\n",
|
||
dev->hba->ports[dev->port].command_issue,
|
||
@@ -1034,7 +1049,10 @@ grub_ahci_readwrite_real (struct grub_ahci_device *dev,
|
||
dev->hba->ports[dev->port].intstatus,
|
||
dev->hba->ports[dev->port].task_file_data);
|
||
dev->hba->ports[dev->port].command_issue = 0;
|
||
- err = grub_error (GRUB_ERR_IO, "AHCI transfer timed out");
|
||
+ if (dev->hba->ports[dev->port].intstatus & GRUB_AHCI_HBA_PORT_IS_FATAL_MASK)
|
||
+ err = grub_error (GRUB_ERR_IO, "AHCI transfer error");
|
||
+ else
|
||
+ err = grub_error (GRUB_ERR_IO, "AHCI transfer timed out");
|
||
if (!reset)
|
||
grub_ahci_reset_port (dev, 1);
|
||
break;
|