Home Home > GIT Browse
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Jones <davej@suse.de>2002-02-25 19:36:01 -0800
committerLinus Torvalds <torvalds@penguin.transmeta.com>2002-02-25 19:36:01 -0800
commit9b1c51abad696c18d1c6192068266342366132e4 (patch)
treebb4c4121d3d08e3d2ea7e626141cf7a137cef15b
parent3679a65c20b6c2f18aae5da03b1619dccc95b59c (diff)
[PATCH] later DMI scan.
This moves the dmi scan to an earlier stage so that we can trap issues such as the various laptops that don't like enabling APIC. It's likely to be useful for trapping other similar early-boot problems. Originally by Mikael Pettersson
-rw-r--r--arch/i386/kernel/apic.c11
-rw-r--r--arch/i386/kernel/dmi_scan.c70
-rw-r--r--arch/i386/kernel/setup.c6
-rw-r--r--arch/i386/kernel/traps.c4
-rw-r--r--arch/i386/mm/init.c7
-rw-r--r--arch/i386/mm/ioremap.c66
-rw-r--r--include/asm-i386/fixmap.h9
-rw-r--r--include/asm-i386/io.h8
8 files changed, 164 insertions, 17 deletions
diff --git a/arch/i386/kernel/apic.c b/arch/i386/kernel/apic.c
index 254f0e572fad..5072e08395f7 100644
--- a/arch/i386/kernel/apic.c
+++ b/arch/i386/kernel/apic.c
@@ -582,12 +582,17 @@ static inline void apic_pm_init2(void) { }
* Detect and enable local APICs on non-SMP boards.
* Original code written by Keir Fraser.
*/
+int dont_enable_local_apic __initdata = 0;
static int __init detect_init_APIC (void)
{
u32 h, l, features;
extern void get_cpu_vendor(struct cpuinfo_x86*);
+ /* Disabled by DMI scan or kernel option? */
+ if (dont_enable_local_apic)
+ return -1;
+
/* Workaround for us being called before identify_cpu(). */
get_cpu_vendor(&boot_cpu_data);
@@ -903,8 +908,14 @@ int __init calibrate_APIC_clock(void)
static unsigned int calibration_result;
+int dont_use_local_apic_timer __initdata = 0;
+
void __init setup_APIC_clocks (void)
{
+ /* Disabled by DMI scan or kernel option? */
+ if (dont_use_local_apic_timer)
+ return;
+
printk("Using local APIC timer interrupts.\n");
using_apic_timer = 1;
diff --git a/arch/i386/kernel/dmi_scan.c b/arch/i386/kernel/dmi_scan.c
index c35058c2f1f5..5323df47722c 100644
--- a/arch/i386/kernel/dmi_scan.c
+++ b/arch/i386/kernel/dmi_scan.c
@@ -9,6 +9,7 @@
#include <linux/pm.h>
#include <asm/keyboard.h>
#include <asm/system.h>
+#include <linux/bootmem.h>
unsigned long dmi_broken;
int is_sony_vaio_laptop;
@@ -51,7 +52,7 @@ static int __init dmi_table(u32 base, int len, int num, void (*decode)(struct dm
u8 *data;
int i=1;
- buf = ioremap(base, len);
+ buf = bt_ioremap(base, len);
if(buf==NULL)
return -1;
@@ -83,7 +84,7 @@ static int __init dmi_table(u32 base, int len, int num, void (*decode)(struct dm
data+=2;
i++;
}
- iounmap(buf);
+ bt_iounmap(buf, len);
return 0;
}
@@ -155,7 +156,7 @@ static void __init dmi_save_ident(struct dmi_header *dm, int slot, int string)
return;
if (dmi_ident[slot])
return;
- dmi_ident[slot] = kmalloc(strlen(p)+1, GFP_KERNEL);
+ dmi_ident[slot] = alloc_bootmem(strlen(p)+1);
if(dmi_ident[slot])
strcpy(dmi_ident[slot], p);
else
@@ -416,6 +417,43 @@ static __init int broken_ps2_resume(struct dmi_blacklist *d)
return 0;
}
+/*
+ * Some machines, usually laptops, can't handle an enabled local APIC.
+ * The symptoms include hangs or reboots when suspending or resuming,
+ * attaching or detaching the power cord, or entering BIOS setup screens
+ * through magic key sequences.
+ */
+static int __init local_apic_kills_bios(struct dmi_blacklist *d)
+{
+#ifdef CONFIG_X86_LOCAL_APIC
+ extern int dont_enable_local_apic;
+ if (!dont_enable_local_apic) {
+ dont_enable_local_apic = 1;
+ printk(KERN_WARNING "%s with broken BIOS detected. "
+ "Refusing to enable the local APIC.\n",
+ d->ident);
+ }
+#endif
+ return 0;
+}
+
+/*
+ * The Intel AL440LX mainboard will hang randomly if the local APIC
+ * timer is running and the APM BIOS hasn't been disabled.
+ */
+static int __init apm_kills_local_apic_timer(struct dmi_blacklist *d)
+{
+#ifdef CONFIG_X86_LOCAL_APIC
+ extern int dont_use_local_apic_timer;
+ if (apm_info.bios.version && !dont_use_local_apic_timer) {
+ dont_use_local_apic_timer = 1;
+ printk(KERN_WARNING "%s with broken BIOS detected. "
+ "The local APIC timer will not be used.\n",
+ d->ident);
+ }
+#endif
+ return 0;
+}
/*
* Simple "print if true" callback
@@ -560,6 +598,25 @@ static __initdata struct dmi_blacklist dmi_blacklist[]={
MATCH(DMI_BIOS_DATE, "09/12/00"), NO_MATCH
} },
+ /* Machines which have problems handling enabled local APICs */
+
+ { local_apic_kills_bios, "Dell Inspiron", {
+ MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
+ MATCH(DMI_PRODUCT_NAME, "Inspiron"),
+ NO_MATCH, NO_MATCH
+ } },
+
+ { local_apic_kills_bios, "Dell Latitude", {
+ MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
+ MATCH(DMI_PRODUCT_NAME, "Latitude"),
+ NO_MATCH, NO_MATCH
+ } },
+
+ { apm_kills_local_apic_timer, "Intel AL440LX", {
+ MATCH(DMI_BOARD_VENDOR, "Intel Corporation"),
+ MATCH(DMI_BOARD_NAME, "AL440LX"),
+ NO_MATCH, NO_MATCH } },
+
/* Problem Intel 440GX bioses */
{ broken_pirq, "SABR1 Bios", { /* Bad $PIR */
@@ -588,7 +645,7 @@ static __initdata struct dmi_blacklist dmi_blacklist[]={
NO_MATCH, NO_MATCH
} },
- /* Intel in disgiuse - In this case they can't hide and they don't run
+ /* Intel in disguise - In this case they can't hide and they don't run
too well either... */
{ broken_pirq, "Dell PowerEdge 8450", { /* Bad $PIR */
MATCH(DMI_PRODUCT_NAME, "Dell PowerEdge 8450"),
@@ -728,12 +785,9 @@ static void __init dmi_decode(struct dmi_header *dm)
}
}
-static int __init dmi_scan_machine(void)
+void __init dmi_scan_machine(void)
{
int err = dmi_iterate(dmi_decode);
if(err == 0)
dmi_check_blacklist();
- return err;
}
-
-module_init(dmi_scan_machine);
diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c
index 844d923b183a..2a52ea64b73d 100644
--- a/arch/i386/kernel/setup.c
+++ b/arch/i386/kernel/setup.c
@@ -99,8 +99,8 @@
#include <linux/highmem.h>
#include <linux/bootmem.h>
#include <linux/seq_file.h>
-#include <asm/processor.h>
#include <linux/console.h>
+#include <asm/processor.h>
#include <asm/mtrr.h>
#include <asm/uaccess.h>
#include <asm/system.h>
@@ -113,6 +113,7 @@
#include <asm/dma.h>
#include <asm/mpspec.h>
#include <asm/mmu_context.h>
+
/*
* Machine setup..
*/
@@ -158,6 +159,7 @@ struct e820map e820;
unsigned char aux_device_present;
extern void mcheck_init(struct cpuinfo_x86 *c);
+extern void dmi_scan_machine(void);
extern int root_mountflags;
extern char _text, _etext, _edata, _end;
extern int blk_nohighio;
@@ -907,7 +909,6 @@ void __init setup_arch(char **cmdline_p)
*/
if (smp_found_config)
get_smp_config();
- init_apic_mappings();
#endif
@@ -959,6 +960,7 @@ void __init setup_arch(char **cmdline_p)
conswitchp = &dummy_con;
#endif
#endif
+ dmi_scan_machine();
}
static int cachesize_override __initdata = -1;
diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c
index b90e252ed049..4a40438bdf2c 100644
--- a/arch/i386/kernel/traps.c
+++ b/arch/i386/kernel/traps.c
@@ -974,6 +974,10 @@ void __init trap_init(void)
EISA_bus = 1;
#endif
+#ifdef CONFIG_X86_LOCAL_APIC
+ init_apic_mappings();
+#endif
+
set_trap_gate(0,&divide_error);
set_trap_gate(1,&debug);
set_intr_gate(2,&nmi);
diff --git a/arch/i386/mm/init.c b/arch/i386/mm/init.c
index 229771dc89d3..702b4a45e56b 100644
--- a/arch/i386/mm/init.c
+++ b/arch/i386/mm/init.c
@@ -105,7 +105,6 @@ extern char __init_begin, __init_end;
static inline void set_pte_phys (unsigned long vaddr,
unsigned long phys, pgprot_t flags)
{
- pgprot_t prot;
pgd_t *pgd;
pmd_t *pmd;
pte_t *pte;
@@ -121,10 +120,8 @@ static inline void set_pte_phys (unsigned long vaddr,
return;
}
pte = pte_offset_kernel(pmd, vaddr);
- if (pte_val(*pte))
- pte_ERROR(*pte);
- pgprot_val(prot) = pgprot_val(PAGE_KERNEL) | pgprot_val(flags);
- set_pte(pte, mk_pte_phys(phys, prot));
+ /* <phys,flags> stored as-is, to permit clearing entries */
+ set_pte(pte, mk_pte_phys(phys, flags));
/*
* It's enough to flush this one mapping.
diff --git a/arch/i386/mm/ioremap.c b/arch/i386/mm/ioremap.c
index 27e393d4f500..043accaad82c 100644
--- a/arch/i386/mm/ioremap.c
+++ b/arch/i386/mm/ioremap.c
@@ -161,3 +161,69 @@ void iounmap(void *addr)
if (addr > high_memory)
return vfree((void *) (PAGE_MASK & (unsigned long) addr));
}
+
+#include <asm/fixmap.h>
+void __init *bt_ioremap(unsigned long phys_addr, unsigned long size)
+{
+ unsigned long offset, last_addr;
+ unsigned int nrpages;
+ enum fixed_addresses idx;
+
+ /* Don't allow wraparound or zero size */
+ last_addr = phys_addr + size - 1;
+ if (!size || last_addr < phys_addr)
+ return NULL;
+
+ /*
+ * Don't remap the low PCI/ISA area, it's always mapped..
+ */
+ if (phys_addr >= 0xA0000 && last_addr < 0x100000)
+ return phys_to_virt(phys_addr);
+
+ /*
+ * Mappings have to be page-aligned
+ */
+ offset = phys_addr & ~PAGE_MASK;
+ phys_addr &= PAGE_MASK;
+ size = PAGE_ALIGN(last_addr) - phys_addr;
+
+ /*
+ * Mappings have to fit in the FIX_BTMAP area.
+ */
+ nrpages = size >> PAGE_SHIFT;
+ if (nrpages > NR_FIX_BTMAPS)
+ return NULL;
+
+ /*
+ * Ok, go for it..
+ */
+ idx = FIX_BTMAP_BEGIN;
+ while (nrpages > 0) {
+ set_fixmap(idx, phys_addr);
+ phys_addr += PAGE_SIZE;
+ --idx;
+ --nrpages;
+ }
+ return (void*) (offset + fix_to_virt(FIX_BTMAP_BEGIN));
+}
+
+void __init bt_iounmap(void *addr, unsigned long size)
+{
+ unsigned long virt_addr;
+ unsigned long offset;
+ unsigned int nrpages;
+ enum fixed_addresses idx;
+
+ virt_addr = (unsigned long)addr;
+ if (virt_addr < fix_to_virt(FIX_BTMAP_BEGIN))
+ return;
+ offset = virt_addr & ~PAGE_MASK;
+ nrpages = PAGE_ALIGN(offset + size - 1) >> PAGE_SHIFT;
+
+ idx = FIX_BTMAP_BEGIN;
+ while (nrpages > 0) {
+ __set_fixmap(idx, 0, __pgprot(0));
+ --idx;
+ --nrpages;
+ }
+}
diff --git a/include/asm-i386/fixmap.h b/include/asm-i386/fixmap.h
index 381e9e9f3b4f..033278aa50e7 100644
--- a/include/asm-i386/fixmap.h
+++ b/include/asm-i386/fixmap.h
@@ -65,6 +65,11 @@ enum fixed_addresses {
FIX_KMAP_BEGIN, /* reserved pte's for temporary kernel mappings */
FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1,
#endif
+ __end_of_permanent_fixed_addresses,
+ /* temporary boot-time mappings, used before ioremap() is functional */
+#define NR_FIX_BTMAPS 16
+ FIX_BTMAP_END = __end_of_permanent_fixed_addresses,
+ FIX_BTMAP_BEGIN = FIX_BTMAP_END + NR_FIX_BTMAPS - 1,
__end_of_fixed_addresses
};
@@ -86,8 +91,8 @@ extern void __set_fixmap (enum fixed_addresses idx,
* at the top of mem..
*/
#define FIXADDR_TOP (0xffffe000UL)
-#define FIXADDR_SIZE (__end_of_fixed_addresses << PAGE_SHIFT)
-#define FIXADDR_START (FIXADDR_TOP - FIXADDR_SIZE)
+#define __FIXADDR_SIZE (__end_of_permanent_fixed_addresses << PAGE_SHIFT)
+#define FIXADDR_START (FIXADDR_TOP - __FIXADDR_SIZE)
#define __fix_to_virt(x) (FIXADDR_TOP - ((x) << PAGE_SHIFT))
diff --git a/include/asm-i386/io.h b/include/asm-i386/io.h
index d443efbd91e6..1d12e37180ac 100644
--- a/include/asm-i386/io.h
+++ b/include/asm-i386/io.h
@@ -95,6 +95,14 @@ static inline void * ioremap_nocache (unsigned long offset, unsigned long size)
extern void iounmap(void *addr);
/*
+ * bt_ioremap() and bt_iounmap() are for temporary early boot-time
+ * mappings, before the real ioremap() is functional.
+ * A boot-time mapping is currently limited to at most 16 pages.
+ */
+extern void *bt_ioremap(unsigned long offset, unsigned long size);
+extern void bt_iounmap(void *addr, unsigned long size);
+
+/*
* ISA I/O bus memory addresses are 1:1 with the physical address.
*/
#define isa_virt_to_bus virt_to_phys