PCI Reboot Patches (2.0 and 2.1)

Joe Pranevich (joepran@telerama.lm.com)
Wed, 19 Nov 1997 16:45:51 -0500


Hello, again.

Okay, here's my official plea for help. I have attatched to this message
patches for 2.0.32 and 2.1.63 (or, whatever the latest of both are, I
forget the numbers) to fix the rebooting problem that I was
experiencing. PLEASE, if anyone is experiencing simular problems, please
mail me and try these patches. (If your BIOS is different than mine,
we'll need to add yours to the patch.) Also, if there's anyone on here
that is using Award Modular BIOS (latest version, v4.51, I think. I
forget the exact number here, too.) with flashed version 'ATA0820B' and
is *not* experiencing a problem, mail me, also. From the mails I have
received, several versions of the Award Modular BIOS and Award WinBIOS
are affected by this problem and I'll need to know their version numbers
and some other info. (Note: In the *latest* flashable version, this
problem still exists.)

PROBLEM:

During kernel startup with PCI BIOS support compiled in, some machines
experience a reboot when in the pci_find_device() function. I have
narrowed down this problem to a problem with the PCI BIOSes of these
machines. (This assumes that the Linux code is correct, of course, but
with the number of machines working correctly with it, I assume that it
is.) Also, this problem is partiucularly hard to diagnose because PCI
initialization occurs rather early in the boot proccess and there will
be no error message before the reboot. The only way to be sure that this
is the problem is to try booting without PCI support or PCI Direct
Access-only support. (Which is next to impossible for people doing first
installations on their machines via distributions.)

SOLUTION:

We can either disable the function or switch to PCI direct access if a
faulty BIOS is detected. The BIOS name is detected by examining a memory
location for its name string. (Each BIOS may have its own location for a
name string, that makes the proccess somewhat more diffcult.) Following
that, we can examine another location for the version string. (Again,
this may differ from version to version.) In order to make it very
likely that your BIOS is exactly what we think it is, both the name and
the version must match. (Hence, an ongoing need to keep names and
versions that are buggy updated) If a faulty BIOS is detected in version
2.0, we will make an attempt first to switch to direct access before
giving up and disabling the individual BIOS function. In 2.1, the same
occurs but is more controlable via the CONFIG_PCI_DIRECT flag. (Without
it, no direct probing will be attempted.) In addition, I have attempted
to make the BIOS detection scheme easily updatable, but without soem
more specifics on other buggy versions, I'm leaving it as-is for now.

As a side note, these patches are not being sent to Linus directly at
this point because I'd like to get *some* feedback, first. They work on
my machine, maybe not on yours. (But I doubt very much that that is the
case.) If I think 2.0.33 is impeding, I'll mail this to Linus and try to
get it in in time for that. The 2.1 patch might wait a week or so, or
might not.

Alan, I decided against implementing your suggestion as I wanted to be
able to keep it nice and neat in detecting other problems, should they
arise. Also, I wasn't exactly sure how to do it. :)

First, for 2.1:

diff -ruN linux/arch/i386/kernel/bios32.c
linux-play/arch/i386/kernel/bios32.c
--- linux/arch/i386/kernel/bios32.c Sat Sep 6 13:43:49 1997
+++ linux-play/arch/i386/kernel/bios32.c Wed Nov 19 14:22:46 1997
@@ -64,6 +64,9 @@
*
* Aug 2, 1997 : Split to PCI BIOS handling and direct PCI access
parts
* and cleaned it up... Martin Mares
<mj@atrey.karlin.mff.cuni.cz>
+ *
+ * Nov 19, 1997 : Added support for detection and handling of buggy
+ * PCI BIOSes. Joe Pranevich (joepran@telerama.lm.com)
*/

#include <linux/config.h>
@@ -818,6 +821,48 @@
pci_bios_write_config_dword
};

+static struct pci_access *pci_bug_detect(
+ struct pci_access *pci_struct)
+{
+ /* What this piece of code attempts to do is to detect
*
+ * any bioses that are known to have bugs in their pci *
+ * code. Whenever possible, we should substitute *
+ * working code (maybe defaulting to direct access if *
+ * that is known to work) If not, we can use the stub *
+ * functions. If we cannot do *anything* with this BIOS *
+ * then just turn the whole thing off. */
+
+ /* I do not know of any other way to determine BIOS *
+ * version and name than to scan for its unique strings */
+
+ /* This will be generalized as more BIOSes are discovered.
*/
+
+ struct pci_access *pci_temp_struct;
+
+ if ((!strncmp("ATA0820B", __va(AWARD_VERSION_BASE), 8)) &&
+ (!strncmp("Award Modular BIOS", __va(AWARD_NAME_BASE), 18)))
+ {
+ printk("PCI: Possibly Buggy BIOS Version Detected (Award Modular
Bios ATA0820B)\n");
+#ifdef CONFIG_PCI_DIRECT
+ printk("PCI: Attempting to bypass the BIOS...\n");
+ pci_temp_struct = pci_check_direct();
+ if (pci_temp_struct)
+ {
+ printk("PCI: Direct access supported, enabled.\n");
+ return pci_temp_struct;
+ } else {
+ printk("PCI: Direct access not supported, disabling some
options.\n");
+ pci_struct->find_device = (void *) pci_stub;
+ return pci_struct;
+ }
+#else
+ printk("PCI: Disabling Some Options\n");
+ pci_struct->find_device = (void *) pci_stub;
+ return pci_struct;
+#endif
+ }
+}
+
/*
* Try to find PCI BIOS.
*/
@@ -865,7 +910,7 @@
printk ("PCI: BIOS32 Service Directory entry at
0x%lx\n", bios32_entry);
bios32_indirect.address = bios32_entry +
PAGE_OFFSET;
if (check_pcibios())
- return &pci_bios_access;
+ return pci_bug_detect(&pci_bios_access);
}
break; /* Hopefully more than one BIOS32 cannot
happen... */
}
diff -ruN linux/include/linux/bios32.h linux-play/include/linux/bios32.h
--- linux/include/linux/bios32.h Tue Jan 14 19:46:07 1997
+++ linux-play/include/linux/bios32.h Wed Nov 19 14:19:14 1997
@@ -22,6 +22,16 @@
#ifndef BIOS32_H
#define BIOS32_H

+/*
+ * Base addresses to use for detection of BIOS names and
+ * versions. These may change from version to version of
+ * the BIOSes so we may need to change the way this is handled
+ */
+
+/* Award Modular BIOS */
+#define AWARD_NAME_BASE 0x000fe061
+#define AWARD_VERSION_BASE 0x000fe0c1
+
/*
* Error values that may be returned by the PCI bios. Use
* pcibios_strerror() to convert to a printable string.

--

Then, for 2.0

diff -ruN linux-2.0-base/arch/i386/kernel/bios32.c linux-2.0/arch/i386/kernel/bios32.c --- linux-2.0-base/arch/i386/kernel/bios32.c Wed Nov 19 14:44:16 1997 +++ linux-2.0/arch/i386/kernel/bios32.c Wed Nov 19 15:31:52 1997 @@ -61,6 +61,9 @@ * * Jun 20, 1997 : Corrected problems in "conf1" type accesses. * (paubert@iram.es) + * + * Nov 19, 1997 : Added support for detection and handling of buggy + * PCI BIOSes. Joe Pranevich (joepran@telerama.lm.com) */ #include <linux/config.h> @@ -68,6 +71,7 @@ #include <linux/kernel.h> #include <linux/bios32.h> #include <linux/pci.h> +#include <linux/string.h> #include <asm/segment.h> #include <asm/system.h> @@ -863,6 +867,46 @@ #endif +#ifdef CONFIG_PCI + +static struct pci_access *pci_bug_detect( + struct pci_access *pci_struct) +{ + /* What this piece of code attempts to do is to detect * + * any bioses that are known to have bugs in their pci * + * code. Whenever possible, we should substitute * + * working code (maybe defaulting to direct access if * + * that is known to work) If we cannot do *anything * + * with this BIOS then just turn the whole thing off. */ + + /* I do not know of any other way to determine BIOS * + * version and name than to scan for its unique strings */ + + /* This will be generalized as more BIOSes are discovered. */ + + struct pci_access *pci_temp_struct; + + if ((!strncmp("ATA0820B", AWARD_VERSION_BASE, 8)) && + (!strncmp("Award Modular BIOS", AWARD_NAME_BASE, 18))) + { + printk("PCI: Possibly Buggy BIOS Version Detected (Award Modular Bios ATA0820B\n"); + printk("PCI: Attempting to bypass the BIOS...\n"); + pci_temp_struct = check_direct_pci(); + if (pci_temp_struct) + { + printk("PCI: Direct access supported, enabled.\n"); + return pci_temp_struct; + } else { + printk("PCI: Direct access not supported, disabling some options.\n"); + pci_struct->find_device = (void *) 0; + return pci_struct; + } + } + /* Should have returned but the compiler doesn't play nice. */ + return pci_struct; +} +#endif + unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end) { #ifdef CONFIG_PCI @@ -908,7 +952,7 @@ } } if (bios32_entry && check_pcibios()) - access_pci = &pci_bios_access; + access_pci = pci_bug_detect(&pci_bios_access); #endif return memory_start; } diff -ruN linux-2.0-base/include/linux/bios32.h linux-2.0/include/linux/bios32.h --- linux-2.0-base/include/linux/bios32.h Wed Nov 19 14:43:55 1997 +++ linux-2.0/include/linux/bios32.h Wed Nov 19 15:01:44 1997 @@ -23,6 +23,16 @@ #define BIOS32_H /* + * Base addresses to use for detection of BIOS names and + * versions. These may change from version to version of + * the BIOSes so we may need to change the way this is handled + */ + +/* Award Modular BIOS */ +#define AWARD_NAME_BASE 0x000fe061 +#define AWARD_VERSION_BASE 0x000fe0c1 + +/* * Error values that may be returned by the PCI bios. Use * pcibios_strerror() to convert to a printable string. */