diff options
author | Jeff Garzik <jgarzik@pobox.com> | 2005-12-13 11:36:18 -0500 |
---|---|---|
committer | Jeff Garzik <jgarzik@pobox.com> | 2005-12-13 11:36:18 -0500 |
commit | d22a8ccff761c81f2930bd90fa5712e51a0e9a62 (patch) | |
tree | 52a92cf1b9a65e4440276706c4b4e879d4008a87 /arch | |
parent | 783e3385a134305d49d7b431df6e591265e7ec14 (diff) | |
parent | 98684a9d91bceff829b6dc7adf0f662d59cfa6e3 (diff) |
Merge branch 'upstream-fixes'
Diffstat (limited to 'arch')
-rw-r--r-- | arch/i386/kernel/smpboot.c | 3 | ||||
-rw-r--r-- | arch/i386/mm/ioremap.c | 37 | ||||
-rw-r--r-- | arch/i386/pci/direct.c | 4 | ||||
-rw-r--r-- | arch/i386/pci/mmconfig.c | 65 | ||||
-rw-r--r-- | arch/i386/pci/pci.h | 7 | ||||
-rw-r--r-- | arch/x86_64/ia32/ia32_binfmt.c | 3 | ||||
-rw-r--r-- | arch/x86_64/kernel/smpboot.c | 2 | ||||
-rw-r--r-- | arch/x86_64/kernel/time.c | 6 | ||||
-rw-r--r-- | arch/x86_64/mm/ioremap.c | 37 | ||||
-rw-r--r-- | arch/x86_64/mm/numa.c | 4 | ||||
-rw-r--r-- | arch/x86_64/pci/mmconfig.c | 63 |
11 files changed, 188 insertions, 43 deletions
diff --git a/arch/i386/kernel/smpboot.c b/arch/i386/kernel/smpboot.c index d16520da455..9ed449af8e9 100644 --- a/arch/i386/kernel/smpboot.c +++ b/arch/i386/kernel/smpboot.c @@ -1338,8 +1338,7 @@ int __cpu_disable(void) if (cpu == 0) return -EBUSY; - /* We enable the timer again on the exit path of the death loop */ - disable_APIC_timer(); + clear_local_APIC(); /* Allow any queued timer interrupts to get serviced */ local_irq_enable(); mdelay(1); diff --git a/arch/i386/mm/ioremap.c b/arch/i386/mm/ioremap.c index 5d09de8d1c6..8498b5ac395 100644 --- a/arch/i386/mm/ioremap.c +++ b/arch/i386/mm/ioremap.c @@ -223,9 +223,15 @@ void __iomem *ioremap_nocache (unsigned long phys_addr, unsigned long size) } EXPORT_SYMBOL(ioremap_nocache); +/** + * iounmap - Free a IO remapping + * @addr: virtual address from ioremap_* + * + * Caller must ensure there is only one unmapping for the same pointer. + */ void iounmap(volatile void __iomem *addr) { - struct vm_struct *p; + struct vm_struct *p, *o; if ((void __force *)addr <= high_memory) return; @@ -239,22 +245,37 @@ void iounmap(volatile void __iomem *addr) addr < phys_to_virt(ISA_END_ADDRESS)) return; - write_lock(&vmlist_lock); - p = __remove_vm_area((void *)(PAGE_MASK & (unsigned long __force)addr)); - if (!p) { - printk(KERN_WARNING "iounmap: bad address %p\n", addr); + addr = (volatile void *)(PAGE_MASK & (unsigned long __force)addr); + + /* Use the vm area unlocked, assuming the caller + ensures there isn't another iounmap for the same address + in parallel. Reuse of the virtual address is prevented by + leaving it in the global lists until we're done with it. + cpa takes care of the direct mappings. */ + read_lock(&vmlist_lock); + for (p = vmlist; p; p = p->next) { + if (p->addr == addr) + break; + } + read_unlock(&vmlist_lock); + + if (!p) { + printk("iounmap: bad address %p\n", addr); dump_stack(); - goto out_unlock; + return; } + /* Reset the direct mapping. Can block */ if ((p->flags >> 20) && p->phys_addr < virt_to_phys(high_memory) - 1) { change_page_attr(virt_to_page(__va(p->phys_addr)), p->size >> PAGE_SHIFT, PAGE_KERNEL); global_flush_tlb(); } -out_unlock: - write_unlock(&vmlist_lock); + + /* Finally remove it */ + o = remove_vm_area((void *)addr); + BUG_ON(p != o || o == NULL); kfree(p); } EXPORT_SYMBOL(iounmap); diff --git a/arch/i386/pci/direct.c b/arch/i386/pci/direct.c index 94331d6be7a..e3ac502bf2f 100644 --- a/arch/i386/pci/direct.c +++ b/arch/i386/pci/direct.c @@ -13,7 +13,7 @@ #define PCI_CONF1_ADDRESS(bus, devfn, reg) \ (0x80000000 | (bus << 16) | (devfn << 8) | (reg & ~3)) -static int pci_conf1_read(unsigned int seg, unsigned int bus, +int pci_conf1_read(unsigned int seg, unsigned int bus, unsigned int devfn, int reg, int len, u32 *value) { unsigned long flags; @@ -42,7 +42,7 @@ static int pci_conf1_read(unsigned int seg, unsigned int bus, return 0; } -static int pci_conf1_write(unsigned int seg, unsigned int bus, +int pci_conf1_write(unsigned int seg, unsigned int bus, unsigned int devfn, int reg, int len, u32 value) { unsigned long flags; diff --git a/arch/i386/pci/mmconfig.c b/arch/i386/pci/mmconfig.c index dfbf80cff83..08a08490121 100644 --- a/arch/i386/pci/mmconfig.c +++ b/arch/i386/pci/mmconfig.c @@ -19,21 +19,25 @@ /* The base address of the last MMCONFIG device accessed */ static u32 mmcfg_last_accessed_device; +static DECLARE_BITMAP(fallback_slots, 32); + /* * Functions for accessing PCI configuration space with MMCONFIG accesses */ -static u32 get_base_addr(unsigned int seg, int bus) +static u32 get_base_addr(unsigned int seg, int bus, unsigned devfn) { int cfg_num = -1; struct acpi_table_mcfg_config *cfg; + if (seg == 0 && bus == 0 && + test_bit(PCI_SLOT(devfn), fallback_slots)) + return 0; + while (1) { ++cfg_num; if (cfg_num >= pci_mmcfg_config_num) { - /* something bad is going on, no cfg table is found. */ - /* so we fall back to the old way we used to do this */ - /* and just rely on the first entry to be correct. */ - return pci_mmcfg_config[0].base_address; + /* Not found - fallback to type 1 */ + return 0; } cfg = &pci_mmcfg_config[cfg_num]; if (cfg->pci_segment_group_number != seg) @@ -44,9 +48,9 @@ static u32 get_base_addr(unsigned int seg, int bus) } } -static inline void pci_exp_set_dev_base(unsigned int seg, int bus, int devfn) +static inline void pci_exp_set_dev_base(unsigned int base, int bus, int devfn) { - u32 dev_base = get_base_addr(seg, bus) | (bus << 20) | (devfn << 12); + u32 dev_base = base | (bus << 20) | (devfn << 12); if (dev_base != mmcfg_last_accessed_device) { mmcfg_last_accessed_device = dev_base; set_fixmap_nocache(FIX_PCIE_MCFG, dev_base); @@ -57,13 +61,18 @@ static int pci_mmcfg_read(unsigned int seg, unsigned int bus, unsigned int devfn, int reg, int len, u32 *value) { unsigned long flags; + u32 base; if (!value || (bus > 255) || (devfn > 255) || (reg > 4095)) return -EINVAL; + base = get_base_addr(seg, bus, devfn); + if (!base) + return pci_conf1_read(seg,bus,devfn,reg,len,value); + spin_lock_irqsave(&pci_config_lock, flags); - pci_exp_set_dev_base(seg, bus, devfn); + pci_exp_set_dev_base(base, bus, devfn); switch (len) { case 1: @@ -86,13 +95,18 @@ static int pci_mmcfg_write(unsigned int seg, unsigned int bus, unsigned int devfn, int reg, int len, u32 value) { unsigned long flags; + u32 base; if ((bus > 255) || (devfn > 255) || (reg > 4095)) return -EINVAL; + base = get_base_addr(seg, bus, devfn); + if (!base) + return pci_conf1_write(seg,bus,devfn,reg,len,value); + spin_lock_irqsave(&pci_config_lock, flags); - pci_exp_set_dev_base(seg, bus, devfn); + pci_exp_set_dev_base(base, bus, devfn); switch (len) { case 1: @@ -116,6 +130,37 @@ static struct pci_raw_ops pci_mmcfg = { .write = pci_mmcfg_write, }; +/* K8 systems have some devices (typically in the builtin northbridge) + that are only accessible using type1 + Normally this can be expressed in the MCFG by not listing them + and assigning suitable _SEGs, but this isn't implemented in some BIOS. + Instead try to discover all devices on bus 0 that are unreachable using MM + and fallback for them. + We only do this for bus 0/seg 0 */ +static __init void unreachable_devices(void) +{ + int i; + unsigned long flags; + + for (i = 0; i < 32; i++) { + u32 val1; + u32 addr; + + pci_conf1_read(0, 0, PCI_DEVFN(i, 0), 0, 4, &val1); + if (val1 == 0xffffffff) + continue; + + /* Locking probably not needed, but safer */ + spin_lock_irqsave(&pci_config_lock, flags); + addr = get_base_addr(0, 0, PCI_DEVFN(i, 0)); + if (addr != 0) + pci_exp_set_dev_base(addr, 0, PCI_DEVFN(i, 0)); + if (addr == 0 || readl((u32 *)addr) != val1) + set_bit(i, fallback_slots); + spin_unlock_irqrestore(&pci_config_lock, flags); + } +} + static int __init pci_mmcfg_init(void) { if ((pci_probe & PCI_PROBE_MMCONF) == 0) @@ -131,6 +176,8 @@ static int __init pci_mmcfg_init(void) raw_pci_ops = &pci_mmcfg; pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF; + unreachable_devices(); + out: return 0; } diff --git a/arch/i386/pci/pci.h b/arch/i386/pci/pci.h index 127d53ad16b..f550781ec31 100644 --- a/arch/i386/pci/pci.h +++ b/arch/i386/pci/pci.h @@ -74,3 +74,10 @@ extern spinlock_t pci_config_lock; extern int (*pcibios_enable_irq)(struct pci_dev *dev); extern void (*pcibios_disable_irq)(struct pci_dev *dev); + +extern int pci_conf1_write(unsigned int seg, unsigned int bus, + unsigned int devfn, int reg, int len, u32 value); +extern int pci_conf1_read(unsigned int seg, unsigned int bus, + unsigned int devfn, int reg, int len, u32 *value); + + diff --git a/arch/x86_64/ia32/ia32_binfmt.c b/arch/x86_64/ia32/ia32_binfmt.c index 830feb272ec..2b760d0d9ce 100644 --- a/arch/x86_64/ia32/ia32_binfmt.c +++ b/arch/x86_64/ia32/ia32_binfmt.c @@ -217,8 +217,7 @@ elf_core_copy_task_fpregs(struct task_struct *tsk, struct pt_regs *regs, elf_fpr if (!tsk_used_math(tsk)) return 0; if (!regs) - regs = (struct pt_regs *)tsk->thread.rsp0; - --regs; + regs = ((struct pt_regs *)tsk->thread.rsp0) - 1; if (tsk == current) unlazy_fpu(tsk); set_fs(KERNEL_DS); diff --git a/arch/x86_64/kernel/smpboot.c b/arch/x86_64/kernel/smpboot.c index 683c33f7b96..ecbd7b83acc 100644 --- a/arch/x86_64/kernel/smpboot.c +++ b/arch/x86_64/kernel/smpboot.c @@ -1181,7 +1181,7 @@ int __cpu_disable(void) if (cpu == 0) return -EBUSY; - disable_APIC_timer(); + clear_local_APIC(); /* * HACK: diff --git a/arch/x86_64/kernel/time.c b/arch/x86_64/kernel/time.c index fdaddc4e528..74102796e5c 100644 --- a/arch/x86_64/kernel/time.c +++ b/arch/x86_64/kernel/time.c @@ -59,7 +59,7 @@ static int notsc __initdata = 0; unsigned int cpu_khz; /* TSC clocks / usec, not used here */ static unsigned long hpet_period; /* fsecs / HPET clock */ unsigned long hpet_tick; /* HPET clocks / interrupt */ -static int hpet_use_timer; +static int hpet_use_timer; /* Use counter of hpet for time keeping, otherwise PIT */ unsigned long vxtime_hz = PIT_TICK_RATE; int report_lost_ticks; /* command line option */ unsigned long long monotonic_base; @@ -908,12 +908,14 @@ void __init time_init(void) if (!hpet_init()) vxtime_hz = (1000000000000000L + hpet_period / 2) / hpet_period; + else + vxtime.hpet_address = 0; if (hpet_use_timer) { cpu_khz = hpet_calibrate_tsc(); timename = "HPET"; #ifdef CONFIG_X86_PM_TIMER - } else if (pmtmr_ioport) { + } else if (pmtmr_ioport && !vxtime.hpet_address) { vxtime_hz = PM_TIMER_FREQUENCY; timename = "PM"; pit_init(); diff --git a/arch/x86_64/mm/ioremap.c b/arch/x86_64/mm/ioremap.c index ecf7acb5db9..0d260e4492f 100644 --- a/arch/x86_64/mm/ioremap.c +++ b/arch/x86_64/mm/ioremap.c @@ -247,9 +247,15 @@ void __iomem *ioremap_nocache (unsigned long phys_addr, unsigned long size) return __ioremap(phys_addr, size, _PAGE_PCD); } +/** + * iounmap - Free a IO remapping + * @addr: virtual address from ioremap_* + * + * Caller must ensure there is only one unmapping for the same pointer. + */ void iounmap(volatile void __iomem *addr) { - struct vm_struct *p; + struct vm_struct *p, *o; if (addr <= high_memory) return; @@ -257,12 +263,31 @@ void iounmap(volatile void __iomem *addr) addr < phys_to_virt(ISA_END_ADDRESS)) return; - write_lock(&vmlist_lock); - p = __remove_vm_area((void *)((unsigned long)addr & PAGE_MASK)); - if (!p) + addr = (volatile void *)(PAGE_MASK & (unsigned long __force)addr); + /* Use the vm area unlocked, assuming the caller + ensures there isn't another iounmap for the same address + in parallel. Reuse of the virtual address is prevented by + leaving it in the global lists until we're done with it. + cpa takes care of the direct mappings. */ + read_lock(&vmlist_lock); + for (p = vmlist; p; p = p->next) { + if (p->addr == addr) + break; + } + read_unlock(&vmlist_lock); + + if (!p) { printk("iounmap: bad address %p\n", addr); - else if (p->flags >> 20) + dump_stack(); + return; + } + + /* Reset the direct mapping. Can block */ + if (p->flags >> 20) ioremap_change_attr(p->phys_addr, p->size, 0); - write_unlock(&vmlist_lock); + + /* Finally remove it */ + o = remove_vm_area((void *)addr); + BUG_ON(p != o || o == NULL); kfree(p); } diff --git a/arch/x86_64/mm/numa.c b/arch/x86_64/mm/numa.c index a828a01739c..15b67d2760c 100644 --- a/arch/x86_64/mm/numa.c +++ b/arch/x86_64/mm/numa.c @@ -53,6 +53,8 @@ static int __init populate_memnodemap( int res = -1; unsigned long addr, end; + if (shift >= 64) + return -1; memset(memnodemap, 0xff, sizeof(memnodemap)); for (i = 0; i < numnodes; i++) { addr = nodes[i].start; @@ -65,7 +67,7 @@ static int __init populate_memnodemap( if (memnodemap[addr >> shift] != 0xff) return -1; memnodemap[addr >> shift] = i; - addr += (1 << shift); + addr += (1UL << shift); } while (addr < end); res = 1; } diff --git a/arch/x86_64/pci/mmconfig.c b/arch/x86_64/pci/mmconfig.c index a0838c4a94e..9c4f907e301 100644 --- a/arch/x86_64/pci/mmconfig.c +++ b/arch/x86_64/pci/mmconfig.c @@ -8,10 +8,13 @@ #include <linux/pci.h> #include <linux/init.h> #include <linux/acpi.h> +#include <linux/bitmap.h> #include "pci.h" #define MMCONFIG_APER_SIZE (256*1024*1024) +static DECLARE_BITMAP(fallback_slots, 32); + /* Static virtual mapping of the MMCONFIG aperture */ struct mmcfg_virt { struct acpi_table_mcfg_config *cfg; @@ -19,7 +22,7 @@ struct mmcfg_virt { }; static struct mmcfg_virt *pci_mmcfg_virt; -static char *get_virt(unsigned int seg, int bus) +static char *get_virt(unsigned int seg, unsigned bus) { int cfg_num = -1; struct acpi_table_mcfg_config *cfg; @@ -27,10 +30,9 @@ static char *get_virt(unsigned int seg, int bus) while (1) { ++cfg_num; if (cfg_num >= pci_mmcfg_config_num) { - /* something bad is going on, no cfg table is found. */ - /* so we fall back to the old way we used to do this */ - /* and just rely on the first entry to be correct. */ - return pci_mmcfg_virt[0].virt; + /* Not found - fall back to type 1. This happens + e.g. on the internal devices of a K8 northbridge. */ + return NULL; } cfg = pci_mmcfg_virt[cfg_num].cfg; if (cfg->pci_segment_group_number != seg) @@ -41,20 +43,30 @@ static char *get_virt(unsigned int seg, int bus) } } -static inline char *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn) +static char *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn) { - - return get_virt(seg, bus) + ((bus << 20) | (devfn << 12)); + char *addr; + if (seg == 0 && bus == 0 && test_bit(PCI_SLOT(devfn), &fallback_slots)) + return NULL; + addr = get_virt(seg, bus); + if (!addr) + return NULL; + return addr + ((bus << 20) | (devfn << 12)); } static int pci_mmcfg_read(unsigned int seg, unsigned int bus, unsigned int devfn, int reg, int len, u32 *value) { - char *addr = pci_dev_base(seg, bus, devfn); + char *addr; + /* Why do we have this when nobody checks it. How about a BUG()!? -AK */ if (unlikely(!value || (bus > 255) || (devfn > 255) || (reg > 4095))) return -EINVAL; + addr = pci_dev_base(seg, bus, devfn); + if (!addr) + return pci_conf1_read(seg,bus,devfn,reg,len,value); + switch (len) { case 1: *value = readb(addr + reg); @@ -73,11 +85,16 @@ static int pci_mmcfg_read(unsigned int seg, unsigned int bus, static int pci_mmcfg_write(unsigned int seg, unsigned int bus, unsigned int devfn, int reg, int len, u32 value) { - char *addr = pci_dev_base(seg, bus, devfn); + char *addr; + /* Why do we have this when nobody checks it. How about a BUG()!? -AK */ if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) return -EINVAL; + addr = pci_dev_base(seg, bus, devfn); + if (!addr) + return pci_conf1_write(seg,bus,devfn,reg,len,value); + switch (len) { case 1: writeb(value, addr + reg); @@ -98,6 +115,30 @@ static struct pci_raw_ops pci_mmcfg = { .write = pci_mmcfg_write, }; +/* K8 systems have some devices (typically in the builtin northbridge) + that are only accessible using type1 + Normally this can be expressed in the MCFG by not listing them + and assigning suitable _SEGs, but this isn't implemented in some BIOS. + Instead try to discover all devices on bus 0 that are unreachable using MM + and fallback for them. + We only do this for bus 0/seg 0 */ +static __init void unreachable_devices(void) +{ + int i; + for (i = 0; i < 32; i++) { + u32 val1; + char *addr; + + pci_conf1_read(0, 0, PCI_DEVFN(i,0), 0, 4, &val1); + if (val1 == 0xffffffff) + continue; + addr = pci_dev_base(0, 0, PCI_DEVFN(i, 0)); + if (addr == NULL|| readl(addr) != val1) { + set_bit(i, &fallback_slots); + } + } +} + static int __init pci_mmcfg_init(void) { int i; @@ -128,6 +169,8 @@ static int __init pci_mmcfg_init(void) printk(KERN_INFO "PCI: Using MMCONFIG at %x\n", pci_mmcfg_config[i].base_address); } + unreachable_devices(); + raw_pci_ops = &pci_mmcfg; pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF; |