Commit 9b1c51ab authored by Dave Jones's avatar Dave Jones Committed by Linus Torvalds

[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
parent 3679a65c
...@@ -582,12 +582,17 @@ static inline void apic_pm_init2(void) { } ...@@ -582,12 +582,17 @@ static inline void apic_pm_init2(void) { }
* Detect and enable local APICs on non-SMP boards. * Detect and enable local APICs on non-SMP boards.
* Original code written by Keir Fraser. * Original code written by Keir Fraser.
*/ */
int dont_enable_local_apic __initdata = 0;
static int __init detect_init_APIC (void) static int __init detect_init_APIC (void)
{ {
u32 h, l, features; u32 h, l, features;
extern void get_cpu_vendor(struct cpuinfo_x86*); 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(). */ /* Workaround for us being called before identify_cpu(). */
get_cpu_vendor(&boot_cpu_data); get_cpu_vendor(&boot_cpu_data);
...@@ -903,8 +908,14 @@ int __init calibrate_APIC_clock(void) ...@@ -903,8 +908,14 @@ int __init calibrate_APIC_clock(void)
static unsigned int calibration_result; static unsigned int calibration_result;
int dont_use_local_apic_timer __initdata = 0;
void __init setup_APIC_clocks (void) 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"); printk("Using local APIC timer interrupts.\n");
using_apic_timer = 1; using_apic_timer = 1;
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <linux/pm.h> #include <linux/pm.h>
#include <asm/keyboard.h> #include <asm/keyboard.h>
#include <asm/system.h> #include <asm/system.h>
#include <linux/bootmem.h>
unsigned long dmi_broken; unsigned long dmi_broken;
int is_sony_vaio_laptop; int is_sony_vaio_laptop;
...@@ -51,7 +52,7 @@ static int __init dmi_table(u32 base, int len, int num, void (*decode)(struct dm ...@@ -51,7 +52,7 @@ static int __init dmi_table(u32 base, int len, int num, void (*decode)(struct dm
u8 *data; u8 *data;
int i=1; int i=1;
buf = ioremap(base, len); buf = bt_ioremap(base, len);
if(buf==NULL) if(buf==NULL)
return -1; return -1;
...@@ -83,7 +84,7 @@ static int __init dmi_table(u32 base, int len, int num, void (*decode)(struct dm ...@@ -83,7 +84,7 @@ static int __init dmi_table(u32 base, int len, int num, void (*decode)(struct dm
data+=2; data+=2;
i++; i++;
} }
iounmap(buf); bt_iounmap(buf, len);
return 0; return 0;
} }
...@@ -155,7 +156,7 @@ static void __init dmi_save_ident(struct dmi_header *dm, int slot, int string) ...@@ -155,7 +156,7 @@ static void __init dmi_save_ident(struct dmi_header *dm, int slot, int string)
return; return;
if (dmi_ident[slot]) if (dmi_ident[slot])
return; return;
dmi_ident[slot] = kmalloc(strlen(p)+1, GFP_KERNEL); dmi_ident[slot] = alloc_bootmem(strlen(p)+1);
if(dmi_ident[slot]) if(dmi_ident[slot])
strcpy(dmi_ident[slot], p); strcpy(dmi_ident[slot], p);
else else
...@@ -416,6 +417,43 @@ static __init int broken_ps2_resume(struct dmi_blacklist *d) ...@@ -416,6 +417,43 @@ static __init int broken_ps2_resume(struct dmi_blacklist *d)
return 0; 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 * Simple "print if true" callback
...@@ -560,6 +598,25 @@ static __initdata struct dmi_blacklist dmi_blacklist[]={ ...@@ -560,6 +598,25 @@ static __initdata struct dmi_blacklist dmi_blacklist[]={
MATCH(DMI_BIOS_DATE, "09/12/00"), NO_MATCH 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 */ /* Problem Intel 440GX bioses */
{ broken_pirq, "SABR1 Bios", { /* Bad $PIR */ { broken_pirq, "SABR1 Bios", { /* Bad $PIR */
...@@ -588,7 +645,7 @@ static __initdata struct dmi_blacklist dmi_blacklist[]={ ...@@ -588,7 +645,7 @@ static __initdata struct dmi_blacklist dmi_blacklist[]={
NO_MATCH, NO_MATCH 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... */ too well either... */
{ broken_pirq, "Dell PowerEdge 8450", { /* Bad $PIR */ { broken_pirq, "Dell PowerEdge 8450", { /* Bad $PIR */
MATCH(DMI_PRODUCT_NAME, "Dell PowerEdge 8450"), MATCH(DMI_PRODUCT_NAME, "Dell PowerEdge 8450"),
...@@ -728,12 +785,9 @@ static void __init dmi_decode(struct dmi_header *dm) ...@@ -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); int err = dmi_iterate(dmi_decode);
if(err == 0) if(err == 0)
dmi_check_blacklist(); dmi_check_blacklist();
return err;
} }
module_init(dmi_scan_machine);
...@@ -99,8 +99,8 @@ ...@@ -99,8 +99,8 @@
#include <linux/highmem.h> #include <linux/highmem.h>
#include <linux/bootmem.h> #include <linux/bootmem.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <asm/processor.h>
#include <linux/console.h> #include <linux/console.h>
#include <asm/processor.h>
#include <asm/mtrr.h> #include <asm/mtrr.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/system.h> #include <asm/system.h>
...@@ -113,6 +113,7 @@ ...@@ -113,6 +113,7 @@
#include <asm/dma.h> #include <asm/dma.h>
#include <asm/mpspec.h> #include <asm/mpspec.h>
#include <asm/mmu_context.h> #include <asm/mmu_context.h>
/* /*
* Machine setup.. * Machine setup..
*/ */
...@@ -158,6 +159,7 @@ struct e820map e820; ...@@ -158,6 +159,7 @@ struct e820map e820;
unsigned char aux_device_present; unsigned char aux_device_present;
extern void mcheck_init(struct cpuinfo_x86 *c); extern void mcheck_init(struct cpuinfo_x86 *c);
extern void dmi_scan_machine(void);
extern int root_mountflags; extern int root_mountflags;
extern char _text, _etext, _edata, _end; extern char _text, _etext, _edata, _end;
extern int blk_nohighio; extern int blk_nohighio;
...@@ -907,7 +909,6 @@ void __init setup_arch(char **cmdline_p) ...@@ -907,7 +909,6 @@ void __init setup_arch(char **cmdline_p)
*/ */
if (smp_found_config) if (smp_found_config)
get_smp_config(); get_smp_config();
init_apic_mappings();
#endif #endif
...@@ -959,6 +960,7 @@ void __init setup_arch(char **cmdline_p) ...@@ -959,6 +960,7 @@ void __init setup_arch(char **cmdline_p)
conswitchp = &dummy_con; conswitchp = &dummy_con;
#endif #endif
#endif #endif
dmi_scan_machine();
} }
static int cachesize_override __initdata = -1; static int cachesize_override __initdata = -1;
......
...@@ -974,6 +974,10 @@ void __init trap_init(void) ...@@ -974,6 +974,10 @@ void __init trap_init(void)
EISA_bus = 1; EISA_bus = 1;
#endif #endif
#ifdef CONFIG_X86_LOCAL_APIC
init_apic_mappings();
#endif
set_trap_gate(0,&divide_error); set_trap_gate(0,&divide_error);
set_trap_gate(1,&debug); set_trap_gate(1,&debug);
set_intr_gate(2,&nmi); set_intr_gate(2,&nmi);
......
...@@ -105,7 +105,6 @@ extern char __init_begin, __init_end; ...@@ -105,7 +105,6 @@ extern char __init_begin, __init_end;
static inline void set_pte_phys (unsigned long vaddr, static inline void set_pte_phys (unsigned long vaddr,
unsigned long phys, pgprot_t flags) unsigned long phys, pgprot_t flags)
{ {
pgprot_t prot;
pgd_t *pgd; pgd_t *pgd;
pmd_t *pmd; pmd_t *pmd;
pte_t *pte; pte_t *pte;
...@@ -121,10 +120,8 @@ static inline void set_pte_phys (unsigned long vaddr, ...@@ -121,10 +120,8 @@ static inline void set_pte_phys (unsigned long vaddr,
return; return;
} }
pte = pte_offset_kernel(pmd, vaddr); pte = pte_offset_kernel(pmd, vaddr);
if (pte_val(*pte)) /* <phys,flags> stored as-is, to permit clearing entries */
pte_ERROR(*pte); set_pte(pte, mk_pte_phys(phys, flags));
pgprot_val(prot) = pgprot_val(PAGE_KERNEL) | pgprot_val(flags);
set_pte(pte, mk_pte_phys(phys, prot));
/* /*
* It's enough to flush this one mapping. * It's enough to flush this one mapping.
......
...@@ -161,3 +161,69 @@ void iounmap(void *addr) ...@@ -161,3 +161,69 @@ void iounmap(void *addr)
if (addr > high_memory) if (addr > high_memory)
return vfree((void *) (PAGE_MASK & (unsigned long) addr)); 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;
}
}
...@@ -65,6 +65,11 @@ enum fixed_addresses { ...@@ -65,6 +65,11 @@ enum fixed_addresses {
FIX_KMAP_BEGIN, /* reserved pte's for temporary kernel mappings */ FIX_KMAP_BEGIN, /* reserved pte's for temporary kernel mappings */
FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1, FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1,
#endif #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 __end_of_fixed_addresses
}; };
...@@ -86,8 +91,8 @@ extern void __set_fixmap (enum fixed_addresses idx, ...@@ -86,8 +91,8 @@ extern void __set_fixmap (enum fixed_addresses idx,
* at the top of mem.. * at the top of mem..
*/ */
#define FIXADDR_TOP (0xffffe000UL) #define FIXADDR_TOP (0xffffe000UL)
#define FIXADDR_SIZE (__end_of_fixed_addresses << PAGE_SHIFT) #define __FIXADDR_SIZE (__end_of_permanent_fixed_addresses << PAGE_SHIFT)
#define FIXADDR_START (FIXADDR_TOP - FIXADDR_SIZE) #define FIXADDR_START (FIXADDR_TOP - __FIXADDR_SIZE)
#define __fix_to_virt(x) (FIXADDR_TOP - ((x) << PAGE_SHIFT)) #define __fix_to_virt(x) (FIXADDR_TOP - ((x) << PAGE_SHIFT))
......
...@@ -94,6 +94,14 @@ static inline void * ioremap_nocache (unsigned long offset, unsigned long size) ...@@ -94,6 +94,14 @@ static inline void * ioremap_nocache (unsigned long offset, unsigned long size)
extern void iounmap(void *addr); 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. * ISA I/O bus memory addresses are 1:1 with the physical address.
*/ */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment