PCI BIOS reboot patch

Joe Pranevich (joepran@telerama.lm.com)
Mon, 01 Dec 1997 17:30:39 -0500


Hello, again.

It's been a while and I didn't get any response about the feasibility of
my PCI patch that would stop reboots on certain computers when using the
PCI BIOS to probe for devices.

Here is my message again, in case it was missed the last time through.
If you merely have not gotten a chance to look it over ir it made it
into a kernel and I didn't see it for some reason, you have my sincerest
apologies.

-- 
Hello,

Well, I guess that maybe I'm impatient but I'm sending these to you, anyway. Please feel free to ignore the 2.1 patch, but I was worried as to whether or not you'd be releasing 2.0.33 before I got around on mailing this directly to you. (As opposed to the mailing list.) If you'd prefer more testing or any changes, please feel free to just tell me so and I'll go away for a while. :) I also hope that I'm not sending you something that you don't want. :)

And, since I was sending you the one for 2.0, I figured that I'd add the one for 2.1 while I was at it. If for nothing else than to have an advance knowledge of why you'd reject it later.

> 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 some > more specifics on other buggy versions, I'm leaving it as-is for now.

I hope that explains things a bit. Patches follow, first for 2.0, then 2.1. If these don't meet your style requirements or anything, just let me know. (This is my first patch, after all.)

Thanks for yor time,

Joe Pranevich

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. */

--

Then, 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.