Commit 4fb416f4 authored by Andi Kleen's avatar Andi Kleen Committed by David S. Miller

[PATCH] x86-64 merge

Lots of small fixes and I merged the PCI subsystem with i386 again
because it was mostly identical. This makes the patch quite big,
but it only removes files.

 - Merge PCI subsystem with i386.

   This changes the initialization order of PCI and IOMMU slightly.  It
   didn't see any problems yet, but it could cause some in theory.  It
   re-adds some code that used to be removed, but it's only small stuff
   and it should hopefully cause less mainteance overhead longer term.

 - Fix warnings
 - Merge cpuid.c with i386.c
 - Sync msr.c with i386
 - Consolidate externs in asm/proto.h
 - sysfs/sysdev fixes for apic/nmi (Bryan O'Sullivan)
 - Fix /proc/kcore access
 - Add real kern_addr_valid (used for above).
 - Support consistent dma_mask in IOMMU
 - Fix double print of AMD for CPU model.
 - Remove unused wakeup.S file.
 - Remove obsolete CONFIG_SIMNOW ifdef.
 - Support ptrace access for 32bit vsyscalls.
 - Fix warnings in 32bit boot code compilation.
parent 571d4a87
......@@ -42,7 +42,6 @@ CFLAGS += -mno-red-zone
CFLAGS += -mcmodel=kernel
CFLAGS += -pipe
# this makes reading assembly source easier, but produces worse code
# disable for production kernel
CFLAGS += -fno-reorder-blocks
# should lower this a lot and see how much .text is saves
CFLAGS += -finline-limit=2000
......
......@@ -35,7 +35,7 @@ __kernel_sigreturn:
.section .text.rtsigreturn,"ax"
.balign 32
.globl __kernel_rt_sigreturn,"ax"
.globl __kernel_rt_sigreturn
.type __kernel_rt_sigreturn,@function
__kernel_rt_sigreturn:
.LSTART_rt_sigreturn:
......
......@@ -24,6 +24,8 @@ obj-$(CONFIG_MODULES) += module.o
$(obj)/bootflag.c:
@ln -sf ../../i386/kernel/bootflag.c $(obj)/bootflag.c
$(obj)/cpuid.c:
@ln -sf ../../i386/kernel/cpuid.c $(obj)/cpuid.c
clean-files += bootflag.c
clean-files += bootflag.c cpuid.c
......@@ -25,8 +25,6 @@
int fallback_aper_order __initdata = 1; /* 64MB */
int fallback_aper_force __initdata = 0;
extern int no_iommu, force_mmu;
/* This code runs before the PCI subsystem is initialized, so just
access the northbridge directly. */
......
......@@ -25,6 +25,7 @@
#include <linux/interrupt.h>
#include <linux/mc146818rtc.h>
#include <linux/kernel_stat.h>
#include <linux/sysdev.h>
#include <asm/atomic.h>
#include <asm/smp.h>
......@@ -464,13 +465,11 @@ static struct {
unsigned int apic_thmr;
} apic_pm_state;
static int lapic_suspend(struct device *dev, u32 state, u32 level)
static int lapic_suspend(struct sys_device *dev, u32 state)
{
unsigned int l, h;
unsigned long flags;
if (level != SUSPEND_POWER_DOWN)
return 0;
if (!apic_pm_state.active)
return 0;
......@@ -497,13 +496,11 @@ static int lapic_suspend(struct device *dev, u32 state, u32 level)
return 0;
}
static int lapic_resume(struct device *dev, u32 level)
static int lapic_resume(struct sys_device *dev)
{
unsigned int l, h;
unsigned long flags;
if (level != RESUME_POWER_ON)
return 0;
if (!apic_pm_state.active)
return 0;
......@@ -537,38 +534,35 @@ static int lapic_resume(struct device *dev, u32 level)
return 0;
}
static struct device_driver lapic_driver = {
.name = "lapic",
.bus = &system_bus_type,
static struct sysdev_class lapic_sysclass = {
set_kset_name("lapic"),
.resume = lapic_resume,
.suspend = lapic_suspend,
};
/* not static, needed by child devices */
struct sys_device device_lapic = {
.name = "lapic",
static struct sys_device device_lapic = {
.id = 0,
.dev = {
.name = "lapic",
.driver = &lapic_driver,
},
.cls = &lapic_sysclass,
};
EXPORT_SYMBOL(device_lapic);
static void __init apic_pm_activate(void)
{
apic_pm_state.active = 1;
}
static int __init init_lapic_devicefs(void)
static int __init init_lapic_sysfs(void)
{
int error;
if (!cpu_has_apic)
return 0;
/* XXX: remove suspend/resume procs if !apic_pm_state.active? */
driver_register(&lapic_driver);
return sys_device_register(&device_lapic);
error = sysdev_class_register(&lapic_sysclass);
if (!error)
error = sys_device_register(&device_lapic);
return error;
}
device_initcall(init_lapic_devicefs);
device_initcall(init_lapic_sysfs);
#else /* CONFIG_PM */
......
......@@ -303,7 +303,7 @@ static void k8_machine_check(struct pt_regs * regs, long error_code)
wrmsrl(MSR_IA32_MCG_STATUS, 0);
if (regs && (status & (1<<1)))
printk(KERN_EMERG "MCE at EIP %lx ESP %lx\n", regs->rip, regs->rsp);
printk(KERN_EMERG "MCE at RIP %lx RSP %lx\n", regs->rip, regs->rsp);
others:
generic_machine_check(regs, error_code);
......@@ -352,7 +352,6 @@ static void __init k8_mcheck_init(struct cpuinfo_x86 *c)
{
u64 cap;
int i;
struct pci_dev *nb;
if (!test_bit(X86_FEATURE_MCE, &c->x86_capability) ||
!test_bit(X86_FEATURE_MCA, &c->x86_capability))
......
#ident "$Id: cpuid.c,v 1.4 2001/10/24 23:58:53 ak Exp $"
/* ----------------------------------------------------------------------- *
*
* Copyright 2000 H. Peter Anvin - All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
* USA; either version 2 of the License, or (at your option) any later
* version; incorporated herein by reference.
*
* ----------------------------------------------------------------------- */
/*
* cpuid.c
*
* x86 CPUID access device
*
* This device is accessed by lseek() to the appropriate CPUID level
* and then read in chunks of 16 bytes. A larger size means multiple
* reads of consecutive levels.
*
* This driver uses /dev/cpu/%d/cpuid where %d is the minor number, and on
* an SMP box will direct the access to CPU %d.
*/
#include <linux/module.h>
#include <linux/config.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/smp.h>
#include <linux/major.h>
#include <linux/fs.h>
#include <linux/smp_lock.h>
#include <linux/fs.h>
#include <asm/processor.h>
#include <asm/msr.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#ifdef CONFIG_SMP
struct cpuid_command {
int cpu;
u32 reg;
u32 *data;
};
static void cpuid_smp_cpuid(void *cmd_block)
{
struct cpuid_command *cmd = (struct cpuid_command *) cmd_block;
if ( cmd->cpu == smp_processor_id() )
cpuid(cmd->reg, &cmd->data[0], &cmd->data[1], &cmd->data[2], &cmd->data[3]);
}
static inline void do_cpuid(int cpu, u32 reg, u32 *data)
{
struct cpuid_command cmd;
preempt_disable();
if ( cpu == smp_processor_id() ) {
cpuid(reg, &data[0], &data[1], &data[2], &data[3]);
} else {
cmd.cpu = cpu;
cmd.reg = reg;
cmd.data = data;
smp_call_function(cpuid_smp_cpuid, &cmd, 1, 1);
}
preempt_enable();
}
#else /* ! CONFIG_SMP */
static inline void do_cpuid(int cpu, u32 reg, u32 *data)
{
cpuid(reg, &data[0], &data[1], &data[2], &data[3]);
}
#endif /* ! CONFIG_SMP */
static loff_t cpuid_seek(struct file *file, loff_t offset, int orig)
{
loff_t ret;
lock_kernel();
switch (orig) {
case 0:
file->f_pos = offset;
ret = file->f_pos;
break;
case 1:
file->f_pos += offset;
ret = file->f_pos;
break;
default:
ret = -EINVAL;
}
unlock_kernel();
return ret;
}
static ssize_t cpuid_read(struct file * file, char * buf,
size_t count, loff_t *ppos)
{
u32 *tmp = (u32 *)buf;
u32 data[4];
size_t rv;
u32 reg = *ppos;
int cpu = minor(file->f_dentry->d_inode->i_rdev);
if ( count % 16 )
return -EINVAL; /* Invalid chunk size */
for ( rv = 0 ; count ; count -= 16 ) {
do_cpuid(cpu, reg, data);
if ( copy_to_user(tmp,&data,16) )
return -EFAULT;
tmp += 4;
*ppos = reg++;
}
return ((char *)tmp) - buf;
}
static int cpuid_open(struct inode *inode, struct file *file)
{
int cpu = minor(file->f_dentry->d_inode->i_rdev);
struct cpuinfo_x86 *c = &(cpu_data)[cpu];
if ( !(cpu_online_map & (1UL << cpu)) )
return -ENXIO; /* No such CPU */
if ( c->cpuid_level < 0 )
return -EIO; /* CPUID not supported */
return 0;
}
/*
* File operations we support
*/
static struct file_operations cpuid_fops = {
.owner = THIS_MODULE,
.llseek = cpuid_seek,
.read = cpuid_read,
.open = cpuid_open,
};
int __init cpuid_init(void)
{
if (register_chrdev(CPUID_MAJOR, "cpu/cpuid", &cpuid_fops)) {
printk(KERN_ERR "cpuid: unable to get major %d for cpuid\n",
CPUID_MAJOR);
return -EBUSY;
}
return 0;
}
void __exit cpuid_exit(void)
{
unregister_chrdev(CPUID_MAJOR, "cpu/cpuid");
}
module_init(cpuid_init);
module_exit(cpuid_exit)
MODULE_AUTHOR("H. Peter Anvin <hpa@zytor.com>");
MODULE_DESCRIPTION("x86 generic CPUID driver");
MODULE_LICENSE("GPL");
......@@ -21,8 +21,7 @@
#include <asm/system.h>
#include <asm/ldt.h>
#include <asm/desc.h>
extern void load_gs_index(unsigned gs);
#include <asm/proto.h>
#ifdef CONFIG_SMP /* avoids "defined but not used" warnig */
static void flush_ldt(void *null)
......
#ident "$Id: msr.c,v 1.6 2001/10/24 23:58:53 ak Exp $"
#ident "$Id$"
/* ----------------------------------------------------------------------- *
*
* Copyright 2000 H. Peter Anvin - All Rights Reserved
......@@ -22,9 +22,6 @@
*
* This driver uses /dev/cpu/%d/msr where %d is the minor number, and on
* an SMP box will direct the access to CPU %d.
RED-PEN: need to get power management for S3 restore
*/
#include <linux/module.h>
......@@ -44,7 +41,6 @@ RED-PEN: need to get power management for S3 restore
#include <asm/msr.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/cpufeature.h>
/* Note: "err" is handled in a funny way below. Otherwise one version
of gcc or another breaks. */
......@@ -119,12 +115,11 @@ static void msr_smp_rdmsr(void *cmd_block)
static inline int do_wrmsr(int cpu, u32 reg, u32 eax, u32 edx)
{
struct msr_command cmd;
int ret;
preempt_disable();
if ( cpu == smp_processor_id() ) {
int ret = wrmsr_eio(reg, eax, edx);
preempt_enable();
return ret;
ret = wrmsr_eio(reg, eax, edx);
} else {
cmd.cpu = cpu;
cmd.reg = reg;
......@@ -132,17 +127,20 @@ static inline int do_wrmsr(int cpu, u32 reg, u32 eax, u32 edx)
cmd.data[1] = edx;
smp_call_function(msr_smp_wrmsr, &cmd, 1, 1);
preempt_enable();
return cmd.err;
ret = cmd.err;
}
preempt_enable();
return ret;
}
static inline int do_rdmsr(int cpu, u32 reg, u32 *eax, u32 *edx)
{
struct msr_command cmd;
int ret;
preempt_disable();
if ( cpu == smp_processor_id() ) {
return rdmsr_eio(reg, eax, edx);
ret = rdmsr_eio(reg, eax, edx);
} else {
cmd.cpu = cpu;
cmd.reg = reg;
......@@ -152,8 +150,10 @@ static inline int do_rdmsr(int cpu, u32 reg, u32 *eax, u32 *edx)
*eax = cmd.data[0];
*edx = cmd.data[1];
return cmd.err;
ret = cmd.err;
}
preempt_enable();
return ret;
}
#else /* ! CONFIG_SMP */
......
......@@ -22,6 +22,7 @@
#include <linux/mc146818rtc.h>
#include <linux/kernel_stat.h>
#include <linux/module.h>
#include <linux/sysdev.h>
#include <asm/smp.h>
#include <asm/mtrr.h>
......@@ -152,50 +153,45 @@ void enable_lapic_nmi_watchdog(void)
#include <linux/device.h>
static int lapic_nmi_suspend(struct device *dev, u32 state, u32 level)
{
if (level != SUSPEND_POWER_DOWN)
return 0;
static int lapic_nmi_suspend(struct sys_device *dev, u32 state)
{
disable_lapic_nmi_watchdog();
return 0;
}
}
static int lapic_nmi_resume(struct device *dev, u32 level)
{
if (level != RESUME_POWER_ON)
return 0;
static int lapic_nmi_resume(struct sys_device *dev)
{
#if 0
enable_lapic_nmi_watchdog();
#endif
return 0;
}
}
static struct device_driver lapic_nmi_driver = {
.name = "lapic_nmi",
.bus = &system_bus_type,
static struct sysdev_class nmi_sysclass = {
set_kset_name("lapic_nmi"),
.resume = lapic_nmi_resume,
.suspend = lapic_nmi_suspend,
};
static struct sys_device device_lapic_nmi = {
.name = "lapic_nmi",
.id = 0,
.dev = {
.name = "lapic_nmi",
.driver = &lapic_nmi_driver,
.parent = &device_lapic.dev,
},
.cls = &nmi_sysclass,
};
static int __init init_lapic_nmi_devicefs(void)
static int __init init_lapic_nmi_sysfs(void)
{
int error;
if (nmi_active == 0)
return 0;
driver_register(&lapic_nmi_driver);
return sys_device_register(&device_lapic_nmi);
error = sysdev_class_register(&nmi_sysclass);
if (!error)
error = sys_device_register(&device_lapic_nmi);
return error;
}
/* must come after the local APIC's device_initcall() */
late_initcall(init_lapic_nmi_devicefs);
late_initcall(init_lapic_nmi_sysfs);
#endif /* CONFIG_PM */
......@@ -332,13 +328,11 @@ asmlinkage void do_nmi(struct pt_regs * regs, long error_code)
{
int cpu = safe_smp_processor_id();
init_tss[cpu].ist[NMI_STACK] -= 2048; /* this shouldn't be needed. */
nmi_enter();
add_pda(__nmi_count,1);
if (!nmi_callback(regs, cpu))
default_do_nmi(regs);
nmi_exit();
init_tss[cpu].ist[NMI_STACK] += 2048;
}
void set_nmi_callback(nmi_callback_t callback)
......
......@@ -38,6 +38,8 @@ possible future tuning:
#include <asm/pgtable.h>
#include <asm/proto.h>
#include <asm/cacheflush.h>
#include <asm/kdebug.h>
#include <asm/proto.h>
unsigned long iommu_bus_base; /* GART remapping area (physical) */
static unsigned long iommu_size; /* size of remapping area bytes */
......@@ -53,9 +55,6 @@ int force_mmu = 1;
int force_mmu = 0;
#endif
extern int fallback_aper_order;
extern int fallback_aper_force;
/* Allocation bitmap for the remapping area */
static spinlock_t iommu_bitmap_lock = SPIN_LOCK_UNLOCKED;
static unsigned long *iommu_gart_bitmap; /* guarded by iommu_bitmap_lock */
......@@ -135,10 +134,19 @@ void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size,
void *memory;
int gfp = GFP_ATOMIC;
int i;
unsigned long iommu_page;
int flush = 0;
unsigned long iommu_page;
unsigned long dma_mask;
if (hwdev == NULL || hwdev->dma_mask < 0xffffffff || no_iommu)
if (hwdev == NULL) {
gfp |= GFP_DMA;
dma_mask = 0xffffffff;
} else {
dma_mask = hwdev->consistent_dma_mask;
}
if (dma_mask == 0)
dma_mask = 0xffffffff;
if (dma_mask < 0xffffffff || no_iommu)
gfp |= GFP_DMA;
/*
......@@ -151,7 +159,7 @@ void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size,
return NULL;
} else {
int high = 0, mmu;
if (((unsigned long)virt_to_bus(memory) + size) > 0xffffffffUL)
if (((unsigned long)virt_to_bus(memory) + size) > dma_mask)
high = 1;
mmu = 1;
if (force_mmu && !(gfp & GFP_DMA))
......@@ -222,7 +230,6 @@ void pci_free_consistent(struct pci_dev *hwdev, size_t size,
static void **iommu_leak_tab;
static int leak_trace;
int iommu_leak_pages = 20;
extern unsigned long printk_address(unsigned long);
void dump_leak(void)
{
int i;
......@@ -459,7 +466,7 @@ static __init int init_k8_gatt(struct agp_kern_info *info)
extern int agp_amdk8_init(void);
void __init pci_iommu_init(void)
int __init pci_iommu_init(void)
{
struct agp_kern_info info;
unsigned long aper_size;
......@@ -468,6 +475,7 @@ void __init pci_iommu_init(void)
#ifndef CONFIG_AGP_AMD_8151
no_agp = 1;
#else
/* Makefile puts PCI initialization via subsys_initcall first. */
/* Add other K8 AGP bridge drivers here */
no_agp = no_agp ||
(agp_amdk8_init() < 0) ||
......@@ -477,7 +485,7 @@ void __init pci_iommu_init(void)
if (no_iommu || (!force_mmu && end_pfn < 0xffffffff>>PAGE_SHIFT)) {
printk(KERN_INFO "PCI-DMA: Disabling IOMMU.\n");
no_iommu = 1;
return;
return -1;
}
if (no_agp) {
......@@ -489,7 +497,7 @@ void __init pci_iommu_init(void)
if (err < 0) {
printk(KERN_INFO "PCI-DMA: Disabling IOMMU.\n");
no_iommu = 1;
return;
return -1;
}
}
......@@ -540,8 +548,13 @@ void __init pci_iommu_init(void)
clear_kernel_mapping((unsigned long)__va(iommu_bus_base), iommu_size);
flush_gart();
return 0;
}
/* Must execute after PCI subsystem */
fs_initcall(pci_iommu_init);
/* iommu=[size][,noagp][,off][,force][,noforce][,leak][,memaper[=order]]
size set size of iommu (in bytes)
noagp don't initialize the AGP driver and use full aperture.
......@@ -589,4 +602,3 @@ __init int iommu_setup(char *opt)
}
return 1;
}
......@@ -2,13 +2,12 @@
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/string.h>
#include <asm/proto.h>
/*
* Dummy IO MMU functions
*/
extern unsigned long end_pfn;
void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size,
dma_addr_t *dma_handle)
{
......
......@@ -200,8 +200,6 @@ void show_regs(struct pt_regs *regs)
show_trace(&regs->rsp);
}
extern void load_gs_index(unsigned);
/*
* Free current thread data structures etc..
*/
......
......@@ -585,7 +585,7 @@ void __init identify_cpu(struct cpuinfo_x86 *c)
void __init print_cpu_info(struct cpuinfo_x86 *c)
{
if (c->x86_model_id[0])
printk("AMD %s", c->x86_model_id);
printk("%s", c->x86_model_id);
if (c->x86_mask || c->cpuid_level >= 0)
printk(" stepping %02x\n", c->x86_mask);
......
......@@ -475,8 +475,6 @@ void do_notify_resume(struct pt_regs *regs, sigset_t *oldset, __u32 thread_info_
do_signal(regs,oldset);
}
extern int exception_trace;
void signal_fault(struct pt_regs *regs, void *frame, char *where)
{
struct task_struct *me = current;
......
......@@ -51,8 +51,7 @@
#include <asm/desc.h>
#include <asm/kdebug.h>
#include <asm/tlbflush.h>
extern int disable_apic;
#include <asm/proto.h>
/* Bitmask of currently online CPUs */
unsigned long cpu_online_map = 1;
......@@ -67,8 +66,6 @@ struct cpuinfo_x86 cpu_data[NR_CPUS] __cacheline_aligned;
/* Set when the idlers are all forked */
int smp_threads_ready;
extern void time_init_smp(void);
/*
* Trampoline 80x86 program as an array.
*/
......@@ -128,7 +125,6 @@ static void __init synchronize_tsc_bp (void)
long long delta;
long one_usec;
int buggy = 0;
extern unsigned cpu_khz;
printk(KERN_INFO "checking TSC synchronization across %u CPUs: ",num_booting_cpus());
......@@ -242,8 +238,6 @@ static void __init synchronize_tsc_ap (void)
}
#undef NR_LOOPS
extern void calibrate_delay(void);
static atomic_t init_deasserted;
void __init smp_callin(void)
......@@ -337,8 +331,6 @@ void __init smp_callin(void)
int cpucount;
extern int cpu_idle(void);
/*
* Activate a secondary processor.
*/
......@@ -560,8 +552,6 @@ static int __init wakeup_secondary_via_INIT(int phys_apicid, unsigned int start_
return (send_status | accept_status);
}
extern unsigned long cpu_initialized;
static void __init do_boot_cpu (int apicid)
{
struct task_struct *idle;
......
......@@ -27,6 +27,7 @@
#include <asm/pgtable.h>
#include <asm/vsyscall.h>
#include <asm/timex.h>
#include <asm/proto.h>
#ifdef CONFIG_X86_LOCAL_APIC
#include <asm/apic.h>
#endif
......@@ -38,9 +39,6 @@ extern int using_apic_timer;
spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;
spinlock_t i8253_lock = SPIN_LOCK_UNLOCKED;
extern int using_apic_timer;
extern void smp_local_timer_interrupt(struct pt_regs * regs);
#undef HPET_HACK_ENABLE_DANGEROUS
......
......@@ -41,13 +41,10 @@
#include <asm/smp.h>
#include <asm/pgalloc.h>
#include <asm/pda.h>
#include <asm/proto.h>
#include <linux/irq.h>
asmlinkage int system_call(void);
asmlinkage int kernel_syscall(void);
extern void ia32_syscall(void);
extern struct gate_struct idt_table[256];
asmlinkage void divide_error(void);
......@@ -73,8 +70,6 @@ asmlinkage void machine_check(void);
asmlinkage void spurious_interrupt_bug(void);
asmlinkage void call_debug(void);
extern int exception_trace;
struct notifier_block *die_chain;
static inline void conditional_sti(struct pt_regs *regs)
......@@ -457,8 +452,6 @@ DO_ERROR(12, SIGBUS, "stack segment", stack_segment)
DO_ERROR_INFO(17, SIGBUS, "alignment check", alignment_check, BUS_ADRALN, get_cr2())
DO_ERROR(18, SIGSEGV, "reserved", reserved)
extern void dump_pagetable(unsigned long);
asmlinkage void do_general_protection(struct pt_regs * regs, long error_code)
{
conditional_sti(regs);
......
/*
* ACPI S3 entry/exit handling.
*
* Notes:
* Relies on kernel being loaded below 4GB.
* Needs restore_low_mappings called before.
*
* Copyright 2003 by Andi Kleen, SuSE Labs.
*
* Long mode entry loosely based on example code in chapter 14 of the x86-64 system
* programmer's manual.
*
* Notebook:
FIXME need to interface with suspend.c properly. do_magic. check i386. rename to suspend64.S
Need to fix vgacon,mtrr,bluesmoke to do resume
Interrupts should be off until the io-apic code has reinited the APIC.
Need support for that in the pm frame work or a special hack?
SMP support is non existent. Need to somehow restart the other CPUs again.
If CPU hotplug was working it could be used. Save/Restore needs to run on the same CPU.
Should check magic like i386 code
suspend code copies something. check what it is.
*/
#include <linux/linkage.h>
#include <asm/msr.h>
#include <asm/segment.h>
#include <asm/page.h>
#define O(x) (x-acpi_wakeup)
.text
.code16
ENTRY(acpi_wakeup)
/* 16bit real mode entered from ACPI BIOS */
/* The machine is just through BIOS setup after power down and everything set up
by Linux needs to be restored. */
/* The code here needs to be position independent or manually relocated,
because it is copied to a <1MB page for real mode execution */
/* A20 enabled (according to ACPI spec) */
/* cs = acpi_wakeup >> 4 ; eip = acpi_wakeup & 0xF */
movw %cs,%ax
movw %ax,%ds /* make %ds point to acpi_wakeup */
movw %ax,%ss
movw $O(wakeup_stack),%sp /* setup stack */
pushl $0
popfl /* clear EFLAGS */
lgdt %ds:O(pGDT) /* load kernel GDT */
movl $0x1,%eax /* enable protected mode */
movl %eax,%cr0
movl %ds:O(wakeup_page_table),%edi
ljmpl $__KERNEL16_CS,$0 /* -> s3_prot16 (filled in earlier by caller) */
/* patched by s3_restore_state below */
pGDT:
.short 0
.quad 0
.align 4
.globl wakeup_page_table
wakeup_page_table:
.long 0
.align 8
wakeup_stack:
.fill 128,1,0
.globl acpi_wakeup_end
acpi_wakeup_end:
/* end of real mode trampoline */
/* pointed to by __KERNEL16_CS:0 */
.code16
ENTRY(s3_prot16)
/* Now in 16bit protected mode, still no paging, stack/data segments invalid */
/* Prepare everything for 64bit paging, but still keep it turned off */
movl %cr4,%eax
bts $5,%eax /* set PAE bit */
movl %eax,%cr4
movl %edi,%cr3 /* load kernel page table */
movl $0x80000001,%eax
cpuid /* no execute supported ? */
movl %edx,%esi
movl $MSR_EFER,%ecx
rdmsr
bts $8,%eax /* long mode */
bt $20,%esi /* NX supported ? */
jnc 1f
bt $_EFER_NX,%eax
1:
wrmsr /* set temporary efer - real one is restored a bit later */
movl %cr0,%eax
bts $31,%eax /* paging */
movl %eax,%cr0
/* running in identity mapping now */
/* go to 64bit code segment */
ljmpl $__KERNEL_CS,$s3_restore_state-__START_KERNEL_map
.code64
.macro SAVEMSR msr,target
movl $\msr,%ecx
rdmsr
shlq $32,%rdx
orq %rax,%rdx
movq %rdx,\target(%rip)
.endm
.macro RESTMSR msr,src
movl $\msr,%ecx
movq \src(%rip),%rax
movq %rax,%rdx
shrq $32,%rdx
wrmsr
.endm
.macro SAVECTL reg
movq %\reg,%rax
movq %rax,saved_\reg(%rip)
.endm
.macro RESTCTL reg
movq saved_\reg(%rip),%rax
movq %rax,%\reg
.endm
/* Running in identity mapping, long mode */
s3_restore_state_low:
movq $s3_restore_state,%rax
jmpq *%rax
/* Running in real kernel mapping now */
s3_restore_state:
xorl %eax,%eax
movl %eax,%ds
movq saved_rsp(%rip),%rsp
movw saved_ss(%rip),%ss
movw saved_fs(%rip),%fs
movw saved_gs(%rip),%gs
movw saved_es(%rip),%es
movw saved_ds(%rip),%ds
lidt saved_idt
ltr saved_tr
lldt saved_ldt
/* gdt is already loaded */
RESTCTL cr0
RESTCTL cr4
/* cr3 is already loaded */
RESTMSR MSR_EFER,saved_efer
RESTMSR MSR_LSTAR,saved_lstar
RESTMSR MSR_CSTAR,saved_cstar
RESTMSR MSR_FS_BASE,saved_fs_base
RESTMSR MSR_GS_BASE,saved_gs_base
RESTMSR MSR_KERNEL_GS_BASE,saved_kernel_gs_base
RESTMSR MSR_SYSCALL_MASK,saved_syscall_mask
fxrstor fpustate(%rip)
RESTCTL dr0
RESTCTL dr1
RESTCTL dr2
RESTCTL dr3
RESTCTL dr6
RESTCTL dr7
movq saved_rflags(%rip),%rax
pushq %rax
popfq
movq saved_rbp(%rip),%rbp
movq saved_rbx(%rip),%rbx
movq saved_r12(%rip),%r12
movq saved_r13(%rip),%r13
movq saved_r14(%rip),%r14
movq saved_r15(%rip),%r15
ret
ENTRY(acpi_prepare_wakeup)
sgdt saved_gdt
/* copy gdt descr and page table to low level wakeup code so that it can
reload them early. */
movq acpi_wakeup_address(%rip),%rax
movw saved_gdt+8(%rip),%cx
movw %cx,O(pGDT)+8(%rax)
movq saved_gdt(%rip),%rcx
movq %rcx,O(pGDT)(%rax)
movq %cr3,%rdi
movl %edi,O(wakeup_page_table)(%rax)
ret
/* Save CPU state. */
/* Everything saved here needs to be restored above. */
ENTRY(do_suspend_lowlevel)
testl %edi,%edi
jnz s3_restore_state
SAVECTL cr0
SAVECTL cr4
SAVECTL cr3
str saved_tr
sidt saved_idt
sgdt saved_gdt
sldt saved_ldt
SAVEMSR MSR_EFER,saved_efer
SAVEMSR MSR_LSTAR,saved_lstar
SAVEMSR MSR_CSTAR,saved_cstar
SAVEMSR MSR_FS_BASE,saved_fs_base
SAVEMSR MSR_GS_BASE,saved_gs_base
SAVEMSR MSR_KERNEL_GS_BASE,saved_kernel_gs_base
SAVEMSR MSR_SYSCALL_MASK,saved_syscall_mask
movw %ds,saved_ds(%rip)
movw %es,saved_es(%rip)
movw %fs,saved_fs(%rip)
movw %gs,saved_gs(%rip)
movw %ss,saved_ss(%rip)
movq %rsp,saved_rsp(%rip)
pushfq
popq %rax
movq %rax,saved_rflags(%rip)
SAVECTL dr0
SAVECTL dr1
SAVECTL dr2
SAVECTL dr3
SAVECTL dr6
SAVECTL dr7
fxsave fpustate(%rip)
/* finally save callee saved registers */
movq %rbp,saved_rbp(%rip)
movq %rbx,saved_rbx(%rip)
movq %r12,saved_r12(%rip)
movq %r13,saved_r13(%rip)
movq %r14,saved_r14(%rip)
movq %r15,saved_r15(%rip)
movq $3,%rdi
call acpi_enter_sleep_state
ret /* should not happen */
.data
.align 8
saved_efer: .quad 0
saved_lstar: .quad 0
saved_cstar: .quad 0
saved_cr4: .quad 0
saved_cr3: .quad 0
saved_cr0: .quad 0
saved_rbp: .quad 0
saved_rbx: .quad 0
saved_rsp: .quad 0
saved_r12: .quad 0
saved_r13: .quad 0
saved_r14: .quad 0
saved_r15: .quad 0
saved_rflags: .quad 0
saved_gs_base: .quad 0
saved_fs_base: .quad 0
saved_kernel_gs_base: .quad 0
saved_syscall_mask: .quad 0
saved_dr0: .quad 0
saved_dr1: .quad 0
saved_dr2: .quad 0
saved_dr3: .quad 0
saved_dr6: .quad 0
saved_dr7: .quad 0
saved_ds: .short 0
saved_fs: .short 0
saved_gs: .short 0
saved_es: .short 0
saved_ss: .short 0
saved_idt: .short 0
.quad 0
saved_ldt: .short 0
saved_gdt: .short 0
.quad 0
saved_tr: .short 0
.align 16
fpustate: .fill 512,1,0
......@@ -21,7 +21,6 @@ int x86_udelay_tsc = 0; /* Delay via TSC */
void __delay(unsigned long loops)
{
#ifndef CONFIG_SIMNOW
unsigned long bclock, now;
rdtscl(bclock);
......@@ -31,7 +30,6 @@ void __delay(unsigned long loops)
rdtscl(now);
}
while((now-bclock) < loops);
#endif
}
inline void __const_udelay(unsigned long xloops)
......
......@@ -24,6 +24,7 @@
#endif
#include <linux/pagemap.h>
#include <linux/bootmem.h>
#include <linux/proc_fs.h>
#include <asm/processor.h>
#include <asm/system.h>
......@@ -41,6 +42,8 @@
#define Dprintk(x...)
extern char _stext;
DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
/*
......@@ -393,6 +396,9 @@ static inline int page_is_ram (unsigned long pagenr)
return 0;
}
static struct kcore_list kcore_mem, kcore_vmalloc, kcore_kernel, kcore_modules,
kcore_vsyscall;
void __init mem_init(void)
{
int codesize, reservedpages, datasize, initsize;
......@@ -434,6 +440,15 @@ void __init mem_init(void)
datasize = (unsigned long) &_edata - (unsigned long) &_etext;
initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin;
/* Register memory areas for /proc/kcore */
kclist_add(&kcore_mem, __va(0), max_low_pfn << PAGE_SHIFT);
kclist_add(&kcore_vmalloc, (void *)VMALLOC_START,
VMALLOC_END-VMALLOC_START);
kclist_add(&kcore_kernel, &_stext, &_end - &_stext);
kclist_add(&kcore_modules, (void *)MODULES_VADDR, MODULES_LEN);
kclist_add(&kcore_vsyscall, (void *)VSYSCALL_START,
VSYSCALL_END - VSYSCALL_START);
printk("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, %dk init)\n",
(unsigned long) nr_free_pages() << (PAGE_SHIFT-10),
end_pfn << (PAGE_SHIFT-10),
......@@ -462,7 +477,7 @@ void free_initmem(void)
ClearPageReserved(virt_to_page(addr));
set_page_count(virt_to_page(addr), 1);
#ifdef CONFIG_INIT_DEBUG
memset(addr & ~(PAGE_SIZE-1), 0xcc, PAGE_SIZE);
memset((void *)(addr & ~(PAGE_SIZE-1)), 0xcc, PAGE_SIZE);
#endif
free_page(addr);
totalram_pages++;
......@@ -497,3 +512,29 @@ void __init reserve_bootmem_generic(unsigned long phys, unsigned len)
reserve_bootmem(phys, len);
#endif
}
int kern_addr_valid(unsigned long addr)
{
unsigned long above = ((long)addr) >> __VIRTUAL_MASK_SHIFT;
if (above != 0 && above != -1UL)
return 0;
pml4_t *pml4 = pml4_offset_k(addr);
if (pml4_none(*pml4))
return 0;
pgd_t *pgd = pgd_offset_k(addr);
if (pgd_none(*pgd))
return 0;
pmd_t *pmd = pmd_offset(pgd, addr);
if (pmd_none(*pmd))
return 0;
if (pmd_large(*pmd))
return pfn_valid(pmd_pfn(*pmd));
pte_t *pte = pte_offset_kernel(pmd, addr);
if (pte_none(*pte))
return 0;
return pfn_valid(pte_pfn(*pte));
}
#
# Makefile for X86_64 specific PCI routines
#
obj-y := x86-64.o
# Reuse the i386 PCI subsystem using symlinks
#
obj-y := i386.o
obj-$(CONFIG_PCI_DIRECT)+= direct.o
obj-y += fixup.o
obj-$(CONFIG_ACPI_PCI) += acpi.o
obj-y += legacy.o irq.o common.o
$(obj)/direct.c: $(obj)/pci.h
@ln -sf ../../i386/pci/direct.c $(obj)/direct.c
$(obj)/legacy.c: $(obj)/pci.h
@ln -sf ../../i386/pci/legacy.c $(obj)/legacy.c
$(obj)/common.c: $(obj)/pci.h
@ln -sf ../../i386/pci/common.c $(obj)/common.c
$(obj)/acpi.c: $(obj)/pci.h
@ln -sf ../../i386/pci/acpi.c $(obj)/acpi.c
$(obj)/pci.h:
@ln -sf ../../i386/pci/pci.h $(obj)/pci.h
$(obj)/irq.c: $(obj)/pci.h
@ln -sf ../../i386/pci/irq.c $(obj)/irq.c
$(obj)/fixup.c: $(obj)/pci.h
@ln -sf ../../i386/pci/fixup.c $(obj)/fixup.c
$(obj)/i386.c: $(obj)/pci.h
@ln -sf ../../i386/pci/i386.c $(obj)/i386.c
clean-files += i386.c legacy.c fixup.c acpi.c irq.c pci.h common.c direct.c
#include <linux/pci.h>
#include <linux/acpi.h>
#include <linux/init.h>
#include "pci.h"
static int __init pci_acpi_init(void)
{
if (pcibios_scanned)
return 0;
if (!(pci_probe & PCI_NO_ACPI_ROUTING)) {
if (!acpi_pci_irq_init()) {
printk(KERN_INFO "PCI: Using ACPI for IRQ routing\n");
printk(KERN_INFO "PCI: if you experience problems, try using option 'pci=noacpi'\n");
pcibios_scanned++;
pcibios_enable_irq = acpi_pci_irq_enable;
} else
printk(KERN_WARNING "PCI: Invalid ACPI-PCI IRQ routing table\n");
/* still scan manually in case ACPI forgot some bus */
pcibios_fixup_peer_bridges();
}
return 0;
}
subsys_initcall(pci_acpi_init);
See arch/i386/pci/changelog for early changelog.
/*
* Low-Level PCI Support for PC
*
* (c) 1999--2000 Martin Mares <mj@ucw.cz>
Note: on x86-64 there is no PCI BIOS so there is no way to sort in the
same order as 32bit Linux. This could cause grief for dualbooting because
devices may wander. May want to use ACPI for sorting eventually.
*/
#include <linux/sched.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <asm/segment.h>
#include <asm/io.h>
#include <asm/smp.h>
#include <asm/cache.h>
#include "pci.h"
unsigned int pci_probe = PCI_PROBE_CONF1 | PCI_PROBE_CONF2;
int pcibios_last_bus = 0xff; /* XXX */
struct pci_bus *pci_root_bus = NULL;
struct pci_ops *pci_root_ops = NULL;
int (*pci_config_read)(int seg, int bus, int dev, int fn, int reg, int len, u32 *value) = NULL;
int (*pci_config_write)(int seg, int bus, int dev, int fn, int reg, int len, u32 value) = NULL;
/*
* legacy, numa, and acpi all want to call pcibios_scan_root
* from their initcalls. This flag prevents that.
*/
int pcibios_scanned;
/*
* This interrupt-safe spinlock protects all accesses to PCI
* configuration space.
*/
spinlock_t pci_config_lock = SPIN_LOCK_UNLOCKED;
/*
* Several buggy motherboards address only 16 devices and mirror
* them to next 16 IDs. We try to detect this `feature' on all
* primary buses (those containing host bridges as they are
* expected to be unique) and remove the ghost devices.
*/
static void __devinit pcibios_fixup_ghosts(struct pci_bus *b)
{
struct list_head *ln, *mn;
struct pci_dev *d, *e;
int mirror = PCI_DEVFN(16,0);
int seen_host_bridge = 0;
int i;
DBG("PCI: Scanning for ghost devices on bus %d\n", b->number);
for (ln=b->devices.next; ln != &b->devices; ln=ln->next) {
d = pci_dev_b(ln);
if ((d->class >> 8) == PCI_CLASS_BRIDGE_HOST)
seen_host_bridge++;
for (mn=ln->next; mn != &b->devices; mn=mn->next) {
e = pci_dev_b(mn);
if (e->devfn != d->devfn + mirror ||
e->vendor != d->vendor ||
e->device != d->device ||
e->class != d->class)
continue;
for(i=0; i<PCI_NUM_RESOURCES; i++)
if (e->resource[i].start != d->resource[i].start ||
e->resource[i].end != d->resource[i].end ||
e->resource[i].flags != d->resource[i].flags)
continue;
break;
}
if (mn == &b->devices)
return;
}
if (!seen_host_bridge)
return;
printk(KERN_WARNING "PCI: Ignoring ghost devices on bus %02x\n", b->number);
ln = &b->devices;
while (ln->next != &b->devices) {
d = pci_dev_b(ln->next);
if (d->devfn >= mirror) {
list_del(&d->global_list);
list_del(&d->bus_list);
kfree(d);
} else
ln = ln->next;
}
}
struct pbus_set_ranges_data;
void __devinit
pcibios_fixup_pbus_ranges (struct pci_bus *bus, struct pbus_set_ranges_data *ranges)
{
}
/*
* Called after each bus is probed, but before its children
* are examined.
*/
void __devinit pcibios_fixup_bus(struct pci_bus *b)
{
pcibios_fixup_ghosts(b);
pci_read_bridge_bases(b);
}
struct pci_bus * __devinit pcibios_scan_root(int busnum)
{
struct list_head *list;
struct pci_bus *bus;
list_for_each(list, &pci_root_buses) {
bus = pci_bus_b(list);
if (bus->number == busnum) {
/* Already scanned */
return bus;
}
}
printk("PCI: Probing PCI hardware (bus %02x)\n", busnum);
return pci_scan_bus(busnum, pci_root_ops, NULL);
}
extern u8 pci_cache_line_size;
static int __init pcibios_init(void)
{
if (!pci_root_ops) {
printk("PCI: System does not support PCI\n");
return 0;
}
pci_cache_line_size = boot_cpu_data.x86_clflush_size >> 2;
pcibios_resource_survey();
#ifdef CONFIG_GART_IOMMU
pci_iommu_init();
#endif
/* may eventually need to do ACPI sort here. */
return 0;
}
subsys_initcall(pcibios_init);
char * __devinit pcibios_setup(char *str)
{
if (!strcmp(str, "off")) {
pci_probe = 0;
return NULL;
}
#ifdef CONFIG_PCI_DIRECT
else if (!strcmp(str, "conf1")) {
pci_probe = PCI_PROBE_CONF1 | PCI_NO_CHECKS;
return NULL;
}
else if (!strcmp(str, "conf2")) {
pci_probe = PCI_PROBE_CONF2 | PCI_NO_CHECKS;
return NULL;
}
#endif
#ifdef CONFIG_ACPI_PCI
else if (!strcmp(str, "noacpi")) {
pci_probe |= PCI_NO_ACPI_ROUTING;
return NULL;
}
#endif
else if (!strcmp(str, "rom")) {
pci_probe |= PCI_ASSIGN_ROMS;
return NULL;
} else if (!strcmp(str, "assign-busses")) {
pci_probe |= PCI_ASSIGN_ALL_BUSSES;
return NULL;
} else if (!strcmp(str, "usepirqmask")) {
pci_probe |= PCI_USE_PIRQ_MASK;
return NULL;
} else if (!strncmp(str, "irqmask=", 8)) {
pcibios_irq_mask = simple_strtol(str+8, NULL, 0);
return NULL;
} else if (!strncmp(str, "lastbus=", 8)) {
pcibios_last_bus = simple_strtol(str+8, NULL, 0);
return NULL;
}
return str;
}
unsigned int pcibios_assign_all_busses(void)
{
return (pci_probe & PCI_ASSIGN_ALL_BUSSES) ? 1 : 0;
}
int pcibios_enable_device(struct pci_dev *dev, int mask)
{
int err;
if ((err = pcibios_enable_resources(dev, mask)) < 0)
return err;
return pcibios_enable_irq(dev);
}
/*
* direct.c - Low-level direct PCI config space access
*/
#include <linux/pci.h>
#include <linux/init.h>
#include "pci.h"
/*
* Functions for accessing PCI configuration space with type 1 accesses
*/
#define PCI_CONF1_ADDRESS(bus, dev, fn, reg) \
(0x80000000 | (bus << 16) | (dev << 11) | (fn << 8) | (reg & ~3))
static int __pci_conf1_read (int seg, int bus, int dev, int fn, int reg, int len, u32 *value)
{
unsigned long flags;
if (!value || (bus > 255) || (dev > 31) || (fn > 7) || (reg > 255))
return -EINVAL;
spin_lock_irqsave(&pci_config_lock, flags);
outl(PCI_CONF1_ADDRESS(bus, dev, fn, reg), 0xCF8);
switch (len) {
case 1:
*value = inb(0xCFC + (reg & 3));
break;
case 2:
*value = inw(0xCFC + (reg & 2));
break;
case 4:
*value = inl(0xCFC);
break;
}
spin_unlock_irqrestore(&pci_config_lock, flags);
return 0;
}
static int __pci_conf1_write (int seg, int bus, int dev, int fn, int reg, int len, u32 value)
{
unsigned long flags;
if ((bus > 255) || (dev > 31) || (fn > 7) || (reg > 255))
return -EINVAL;
spin_lock_irqsave(&pci_config_lock, flags);
outl(PCI_CONF1_ADDRESS(bus, dev, fn, reg), 0xCF8);
switch (len) {
case 1:
outb((u8)value, 0xCFC + (reg & 3));
break;
case 2:
outw((u16)value, 0xCFC + (reg & 2));
break;
case 4:
outl((u32)value, 0xCFC);
break;
}
spin_unlock_irqrestore(&pci_config_lock, flags);
return 0;
}
#undef PCI_CONF1_ADDRESS
static int pci_conf1_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *value)
{
return __pci_conf1_read(0, bus->number, PCI_SLOT(devfn),
PCI_FUNC(devfn), where, size, value);
}
static int pci_conf1_write(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 value)
{
return __pci_conf1_write(0, bus->number, PCI_SLOT(devfn),
PCI_FUNC(devfn), where, size, value);
}
static struct pci_ops pci_direct_conf1 = {
.read = pci_conf1_read,
.write = pci_conf1_write,
};
/*
* Functions for accessing PCI configuration space with type 2 accesses
*/
#define PCI_CONF2_ADDRESS(dev, reg) (u16)(0xC000 | (dev << 8) | reg)
static int __pci_conf2_read (int seg, int bus, int dev, int fn, int reg, int len, u32 *value)
{
unsigned long flags;
if (!value || (bus > 255) || (dev > 31) || (fn > 7) || (reg > 255))
return -EINVAL;
if (dev & 0x10)
return PCIBIOS_DEVICE_NOT_FOUND;
spin_lock_irqsave(&pci_config_lock, flags);
outb((u8)(0xF0 | (fn << 1)), 0xCF8);
outb((u8)bus, 0xCFA);
switch (len) {
case 1:
*value = inb(PCI_CONF2_ADDRESS(dev, reg));
break;
case 2:
*value = inw(PCI_CONF2_ADDRESS(dev, reg));
break;
case 4:
*value = inl(PCI_CONF2_ADDRESS(dev, reg));
break;
}
outb (0, 0xCF8);
spin_unlock_irqrestore(&pci_config_lock, flags);
return 0;
}
static int __pci_conf2_write (int seg, int bus, int dev, int fn, int reg, int len, u32 value)
{
unsigned long flags;
if ((bus > 255) || (dev > 31) || (fn > 7) || (reg > 255))
return -EINVAL;
if (dev & 0x10)
return PCIBIOS_DEVICE_NOT_FOUND;
spin_lock_irqsave(&pci_config_lock, flags);
outb((u8)(0xF0 | (fn << 1)), 0xCF8);
outb((u8)bus, 0xCFA);
switch (len) {
case 1:
outb ((u8)value, PCI_CONF2_ADDRESS(dev, reg));
break;
case 2:
outw ((u16)value, PCI_CONF2_ADDRESS(dev, reg));
break;
case 4:
outl ((u32)value, PCI_CONF2_ADDRESS(dev, reg));
break;
}
outb (0, 0xCF8);
spin_unlock_irqrestore(&pci_config_lock, flags);
return 0;
}
#undef PCI_CONF2_ADDRESS
static int pci_conf2_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *value)
{
return __pci_conf2_read(0, bus->number, PCI_SLOT(devfn),
PCI_FUNC(devfn), where, size, value);
}
static int pci_conf2_write(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 value)
{
return __pci_conf2_write(0, bus->number, PCI_SLOT(devfn),
PCI_FUNC(devfn), where, size, value);
}
static struct pci_ops pci_direct_conf2 = {
.read = pci_conf2_read,
.write = pci_conf2_write,
};
/*
* Before we decide to use direct hardware access mechanisms, we try to do some
* trivial checks to ensure it at least _seems_ to be working -- we just test
* whether bus 00 contains a host bridge (this is similar to checking
* techniques used in XFree86, but ours should be more reliable since we
* attempt to make use of direct access hints provided by the PCI BIOS).
*
* This should be close to trivial, but it isn't, because there are buggy
* chipsets (yes, you guessed it, by Intel and Compaq) that have no class ID.
*/
static int __devinit pci_sanity_check(struct pci_ops *o)
{
u32 x = 0;
int retval = 0;
struct pci_bus *bus; /* Fake bus and device */
struct pci_dev *dev;
if (pci_probe & PCI_NO_CHECKS)
return 1;
bus = kmalloc(sizeof(*bus), GFP_ATOMIC);
dev = kmalloc(sizeof(*dev), GFP_ATOMIC);
if (!bus || !dev) {
printk(KERN_ERR "Out of memory in %s\n", __FUNCTION__);
goto exit;
}
bus->number = 0;
dev->bus = bus;
for(dev->devfn=0; dev->devfn < 0x100; dev->devfn++)
if ((!o->read(bus, dev->devfn, PCI_CLASS_DEVICE, 2, &x) &&
(x == PCI_CLASS_BRIDGE_HOST || x == PCI_CLASS_DISPLAY_VGA)) ||
(!o->read(bus, dev->devfn, PCI_VENDOR_ID, 2, &x) &&
(x == PCI_VENDOR_ID_INTEL || x == PCI_VENDOR_ID_COMPAQ))) {
retval = 1;
goto exit;
}
DBG("PCI: Sanity check failed\n");
exit:
kfree(dev);
kfree(bus);
return retval;
}
static int __init pci_direct_init(void)
{
unsigned int tmp;
unsigned long flags;
local_irq_save(flags);
/*
* Check if configuration type 1 works.
*/
if (pci_probe & PCI_PROBE_CONF1) {
outb (0x01, 0xCFB);
tmp = inl (0xCF8);
outl (0x80000000, 0xCF8);
if (inl (0xCF8) == 0x80000000 &&
pci_sanity_check(&pci_direct_conf1)) {
outl (tmp, 0xCF8);
local_irq_restore(flags);
printk(KERN_INFO "PCI: Using configuration type 1\n");
if (!request_region(0xCF8, 8, "PCI conf1"))
pci_root_ops = NULL;
else
pci_root_ops = &pci_direct_conf1;
return 0;
}
outl (tmp, 0xCF8);
}
/*
* Check if configuration type 2 works.
*/
if (pci_probe & PCI_PROBE_CONF2) {
outb (0x00, 0xCFB);
outb (0x00, 0xCF8);
outb (0x00, 0xCFA);
if (inb (0xCF8) == 0x00 && inb (0xCFA) == 0x00 &&
pci_sanity_check(&pci_direct_conf2)) {
local_irq_restore(flags);
printk(KERN_INFO "PCI: Using configuration type 2\n");
if (!request_region(0xCF8, 4, "PCI conf2"))
pci_root_ops = NULL;
else
pci_root_ops = &pci_direct_conf2;
return 0;
}
}
local_irq_restore(flags);
return 0;
}
arch_initcall(pci_direct_init);
/*
* Exceptions for specific devices. Usually work-arounds for fatal design flaws.
*
Short list on x86-64........so far.
*/
#include <linux/pci.h>
#include <linux/init.h>
#include "pci.h"
static void __devinit pci_fixup_ncr53c810(struct pci_dev *d)
{
/*
* NCR 53C810 returns class code 0 (at least on some systems).
* Fix class to be PCI_CLASS_STORAGE_SCSI
*/
if (!d->class) {
printk(KERN_WARNING "PCI: fixing NCR 53C810 class code for %s\n", d->slot_name);
d->class = PCI_CLASS_STORAGE_SCSI << 8;
}
}
static void __devinit pci_fixup_ide_bases(struct pci_dev *d)
{
int i;
/*
* PCI IDE controllers use non-standard I/O port decoding, respect it.
*/
if ((d->class >> 8) != PCI_CLASS_STORAGE_IDE)
return;
DBG("PCI: IDE base address fixup for %s\n", d->slot_name);
for(i=0; i<4; i++) {
struct resource *r = &d->resource[i];
if ((r->start & ~0x80) == 0x374) {
r->start |= 2;
r->end = r->start;
}
}
}
struct pci_fixup pcibios_fixups[] = {
{ PCI_FIXUP_HEADER, PCI_ANY_ID, PCI_ANY_ID, pci_fixup_ide_bases },
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_NCR, PCI_DEVICE_ID_NCR_53C810, pci_fixup_ncr53c810 },
{ 0 }
};
/*
* Low-Level PCI Support for PC -- Routing of Interrupts
*
* (c) 1999--2000 Martin Mares <mj@ucw.cz>
*/
#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <asm/io.h>
#include <asm/smp.h>
#include <asm/io_apic.h>
#include "pci.h"
#define PIRQ_SIGNATURE (('$' << 0) + ('P' << 8) + ('I' << 16) + ('R' << 24))
#define PIRQ_VERSION 0x0100
int broken_hp_bios_irq9;
static struct irq_routing_table *pirq_table;
/*
* Never use: 0, 1, 2 (timer, keyboard, and cascade)
* Avoid using: 13, 14 and 15 (FP error and IDE).
* Penalize: 3, 4, 6, 7, 12 (known ISA uses: serial, floppy, parallel and mouse)
*/
unsigned int pcibios_irq_mask = 0xfff8;
static int pirq_penalty[16] = {
1000000, 1000000, 1000000, 1000, 1000, 0, 1000, 1000,
0, 0, 0, 0, 1000, 100000, 100000, 100000
};
struct irq_router {
char *name;
u16 vendor, device;
int (*get)(struct pci_dev *router, struct pci_dev *dev, int pirq);
int (*set)(struct pci_dev *router, struct pci_dev *dev, int pirq, int new);
};
int (*pcibios_enable_irq)(struct pci_dev *dev) = NULL;
/*
* Search 0xf0000 -- 0xfffff for the PCI IRQ Routing Table.
*/
static struct irq_routing_table * __init pirq_find_routing_table(void)
{
u8 *addr;
struct irq_routing_table *rt;
int i;
u8 sum;
for(addr = (u8 *) __va(0xf0000); addr < (u8 *) __va(0x100000); addr += 16) {
rt = (struct irq_routing_table *) addr;
if (rt->signature != PIRQ_SIGNATURE ||
rt->version != PIRQ_VERSION ||
rt->size % 16 ||
rt->size < sizeof(struct irq_routing_table))
continue;
sum = 0;
for(i=0; i<rt->size; i++)
sum += addr[i];
if (!sum) {
DBG("PCI: Interrupt Routing Table found at 0x%p\n", rt);
return rt;
}
}
return NULL;
}
/*
* If we have a IRQ routing table, use it to search for peer host
* bridges. It's a gross hack, but since there are no other known
* ways how to get a list of buses, we have to go this way.
*/
static void __init pirq_peer_trick(void)
{
struct irq_routing_table *rt = pirq_table;
u8 busmap[256];
int i;
struct irq_info *e;
memset(busmap, 0, sizeof(busmap));
for(i=0; i < (rt->size - sizeof(struct irq_routing_table)) / sizeof(struct irq_info); i++) {
e = &rt->slots[i];
#ifdef DEBUG
{
int j;
DBG("%02x:%02x slot=%02x", e->bus, e->devfn/8, e->slot);
for(j=0; j<4; j++)
DBG(" %d:%02x/%04x", j, e->irq[j].link, e->irq[j].bitmap);
DBG("\n");
}
#endif
busmap[e->bus] = 1;
}
for(i=1; i<256; i++)
/*
* It might be a secondary bus, but in this case its parent is already
* known (ascending bus order) and therefore pci_scan_bus returns immediately.
*/
if (busmap[i] && pci_scan_bus(i, pci_root_bus->ops, NULL))
printk(KERN_INFO "PCI: Discovered primary peer bus %02x [IRQ]\n", i);
//pcibios_last_bus = -1;
}
/*
* Code for querying and setting of IRQ routes on various interrupt routers.
*/
void eisa_set_level_irq(unsigned int irq)
{
unsigned char mask = 1 << (irq & 7);
unsigned int port = 0x4d0 + (irq >> 3);
unsigned char val = inb(port);
if (!(val & mask)) {
DBG(" -> edge");
outb(val | mask, port);
}
}
/*
* Common IRQ routing practice: nybbles in config space,
* offset by some magic constant.
*/
static unsigned int read_config_nybble(struct pci_dev *router, unsigned offset, unsigned nr)
{
u8 x;
unsigned reg = offset + (nr >> 1);
pci_read_config_byte(router, reg, &x);
return (nr & 1) ? (x >> 4) : (x & 0xf);
}
static void write_config_nybble(struct pci_dev *router, unsigned offset, unsigned nr, unsigned int val)
{
u8 x;
unsigned reg = offset + (nr >> 1);
pci_read_config_byte(router, reg, &x);
x = (nr & 1) ? ((x & 0x0f) | (val << 4)) : ((x & 0xf0) | val);
pci_write_config_byte(router, reg, x);
}
#if 0 /* enable when pci ids ae known */
/*
* The VIA pirq rules are nibble-based, like ALI,
* but without the ugly irq number munging.
*/
static int pirq_via_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
{
return read_config_nybble(router, 0x55, pirq);
}
static int pirq_via_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
{
write_config_nybble(router, 0x55, pirq, irq);
return 1;
}
/*
* PIRQ routing for SiS 85C503 router used in several SiS chipsets
* According to the SiS 5595 datasheet (preliminary V1.0, 12/24/1997)
* the related registers work as follows:
*
* general: one byte per re-routable IRQ,
* bit 7 IRQ mapping enabled (0) or disabled (1)
* bits [6:4] reserved
* bits [3:0] IRQ to map to
* allowed: 3-7, 9-12, 14-15
* reserved: 0, 1, 2, 8, 13
*
* individual registers in device config space:
*
* 0x41/0x42/0x43/0x44: PCI INT A/B/C/D - bits as in general case
*
* 0x61: IDEIRQ: bits as in general case - but:
* bits [6:5] must be written 01
* bit 4 channel-select primary (0), secondary (1)
*
* 0x62: USBIRQ: bits as in general case - but:
* bit 4 OHCI function disabled (0), enabled (1)
*
* 0x6a: ACPI/SCI IRQ - bits as in general case
*
* 0x7e: Data Acq. Module IRQ - bits as in general case
*
* Apparently there are systems implementing PCI routing table using both
* link values 0x01-0x04 and 0x41-0x44 for PCI INTA..D, but register offsets
* like 0x62 as link values for USBIRQ e.g. So there is no simple
* "register = offset + pirq" relation.
* Currently we support PCI INTA..D and USBIRQ and try our best to handle
* both link mappings.
* IDE/ACPI/DAQ mapping is currently unsupported (left untouched as set by BIOS).
*/
static int pirq_sis_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
{
u8 x;
int reg = pirq;
switch(pirq) {
case 0x01:
case 0x02:
case 0x03:
case 0x04:
reg += 0x40;
case 0x41:
case 0x42:
case 0x43:
case 0x44:
case 0x62:
pci_read_config_byte(router, reg, &x);
if (reg != 0x62)
break;
if (!(x & 0x40))
return 0;
break;
case 0x61:
case 0x6a:
case 0x7e:
printk(KERN_INFO "SiS pirq: advanced IDE/ACPI/DAQ mapping not yet implemented\n");
return 0;
default:
printk(KERN_INFO "SiS router pirq escape (%d)\n", pirq);
return 0;
}
return (x & 0x80) ? 0 : (x & 0x0f);
}
static int pirq_sis_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
{
u8 x;
int reg = pirq;
switch(pirq) {
case 0x01:
case 0x02:
case 0x03:
case 0x04:
reg += 0x40;
case 0x41:
case 0x42:
case 0x43:
case 0x44:
case 0x62:
x = (irq&0x0f) ? (irq&0x0f) : 0x80;
if (reg != 0x62)
break;
/* always mark OHCI enabled, as nothing else knows about this */
x |= 0x40;
break;
case 0x61:
case 0x6a:
case 0x7e:
printk(KERN_INFO "advanced SiS pirq mapping not yet implemented\n");
return 0;
default:
printk(KERN_INFO "SiS router pirq escape (%d)\n", pirq);
return 0;
}
pci_write_config_byte(router, reg, x);
return 1;
}
#endif
/* Support for AMD756 PCI IRQ Routing
* Jhon H. Caicedo <jhcaiced@osso.org.co>
* Jun/21/2001 0.2.0 Release, fixed to use "nybble" functions... (jhcaiced)
* Jun/19/2001 Alpha Release 0.1.0 (jhcaiced)
* The AMD756 pirq rules are nibble-based
* offset 0x56 0-3 PIRQA 4-7 PIRQB
* offset 0x57 0-3 PIRQC 4-7 PIRQD
*/
static int pirq_amd756_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
{
u8 irq;
irq = 0;
if (pirq <= 4)
{
irq = read_config_nybble(router, 0x56, pirq - 1);
}
printk(KERN_INFO "AMD756: dev %04x:%04x, router pirq : %d get irq : %2d\n",
dev->vendor, dev->device, pirq, irq);
return irq;
}
static int pirq_amd756_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
{
printk(KERN_INFO "AMD756: dev %04x:%04x, router pirq : %d SET irq : %2d\n",
dev->vendor, dev->device, pirq, irq);
if (pirq <= 4)
{
write_config_nybble(router, 0x56, pirq - 1, irq);
}
return 1;
}
static struct irq_router pirq_routers[] = {
#if 0 /* all these do not exist on Hammer currently, but keep one example
for each. All these vendors have announced K8 chipsets, so we'll
eventually need a router for them. Luckily they tend to use the
same ones, so with luck just enabling the existing ones will work
when you know the final PCI ids. */
{ "ALI", PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, pirq_ali_get, pirq_ali_set },
{ "VIA", PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_0, pirq_via_get, pirq_via_set },
{ "SIS", PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503, pirq_sis_get, pirq_sis_set },
#endif
{ "AMD756 VIPER", PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_740B,
pirq_amd756_get, pirq_amd756_set },
{ "default", 0, 0, NULL, NULL }
};
static struct irq_router *pirq_router;
static struct pci_dev *pirq_router_dev;
static void __init pirq_find_router(void)
{
struct irq_routing_table *rt = pirq_table;
struct irq_router *r;
DBG("PCI: Attempting to find IRQ router for %04x:%04x\n",
rt->rtr_vendor, rt->rtr_device);
/* fall back to default router if nothing else found */
pirq_router = &pirq_routers[ARRAY_SIZE(pirq_routers) - 1];
pirq_router_dev = pci_find_slot(rt->rtr_bus, rt->rtr_devfn);
if (!pirq_router_dev) {
DBG("PCI: Interrupt router not found at %02x:%02x\n", rt->rtr_bus, rt->rtr_devfn);
return;
}
for(r=pirq_routers; r->vendor; r++) {
/* Exact match against router table entry? Use it! */
if (r->vendor == rt->rtr_vendor && r->device == rt->rtr_device) {
pirq_router = r;
break;
}
/* Match against router device entry? Use it as a fallback */
if (r->vendor == pirq_router_dev->vendor && r->device == pirq_router_dev->device) {
pirq_router = r;
}
}
printk(KERN_INFO "PCI: Using IRQ router %s [%04x/%04x] at %s\n",
pirq_router->name,
pirq_router_dev->vendor,
pirq_router_dev->device,
pirq_router_dev->slot_name);
}
static struct irq_info *pirq_get_info(struct pci_dev *dev)
{
struct irq_routing_table *rt = pirq_table;
int entries = (rt->size - sizeof(struct irq_routing_table)) / sizeof(struct irq_info);
struct irq_info *info;
for (info = rt->slots; entries--; info++)
if (info->bus == dev->bus->number && PCI_SLOT(info->devfn) == PCI_SLOT(dev->devfn))
return info;
return NULL;
}
static irqreturn_t pcibios_test_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
{
return IRQ_NONE;
}
static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
{
u8 pin;
struct irq_info *info;
int i, pirq, newirq;
int irq = 0;
u32 mask;
struct irq_router *r = pirq_router;
struct pci_dev *dev2 = NULL;
char *msg = NULL;
/* Find IRQ pin */
pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
if (!pin) {
DBG(" -> no interrupt pin\n");
return 0;
}
pin = pin - 1;
/* Find IRQ routing entry */
if (!pirq_table)
return 0;
DBG("IRQ for %s:%d", dev->slot_name, pin);
info = pirq_get_info(dev);
if (!info) {
DBG(" -> not found in routing table\n");
return 0;
}
pirq = info->irq[pin].link;
mask = info->irq[pin].bitmap;
if (!pirq) {
DBG(" -> not routed\n");
return 0;
}
DBG(" -> PIRQ %02x, mask %04x, excl %04x", pirq, mask, pirq_table->exclusive_irqs);
mask &= pcibios_irq_mask;
/* Work around broken HP Pavilion Notebooks which assign USB to
IRQ 9 even though it is actually wired to IRQ 11 */
if (broken_hp_bios_irq9 && pirq == 0x59 && dev->irq == 9) {
dev->irq = 11;
pci_write_config_byte(dev, PCI_INTERRUPT_LINE, 11);
r->set(pirq_router_dev, dev, pirq, 11);
}
/*
* Find the best IRQ to assign: use the one
* reported by the device if possible.
*/
newirq = dev->irq;
if (!((1 << newirq) & mask)) {
if ( pci_probe & PCI_USE_PIRQ_MASK) newirq = 0;
else printk(KERN_WARNING "PCI: IRQ %i for device %s doesn't match PIRQ mask - try pci=usepirqmask\n", newirq, dev->slot_name);
}
if (!newirq && assign) {
for (i = 0; i < 16; i++) {
if (!(mask & (1 << i)))
continue;
if (pirq_penalty[i] < pirq_penalty[newirq] &&
!request_irq(i, pcibios_test_irq_handler, SA_SHIRQ, "pci-test", dev)) {
free_irq(i, dev);
newirq = i;
}
}
}
DBG(" -> newirq=%d", newirq);
/* Check if it is hardcoded */
if ((pirq & 0xf0) == 0xf0) {
irq = pirq & 0xf;
DBG(" -> hardcoded IRQ %d\n", irq);
msg = "Hardcoded";
} else if ( r->get && (irq = r->get(pirq_router_dev, dev, pirq)) && \
((!(pci_probe & PCI_USE_PIRQ_MASK)) || ((1 << irq) & mask)) ) {
DBG(" -> got IRQ %d\n", irq);
msg = "Found";
} else if (newirq && r->set && (dev->class >> 8) != PCI_CLASS_DISPLAY_VGA) {
DBG(" -> assigning IRQ %d", newirq);
if (r->set(pirq_router_dev, dev, pirq, newirq)) {
eisa_set_level_irq(newirq);
DBG(" ... OK\n");
msg = "Assigned";
irq = newirq;
}
}
if (!irq) {
DBG(" ... failed\n");
if (newirq && mask == (1 << newirq)) {
msg = "Guessed";
irq = newirq;
} else
return 0;
}
printk(KERN_INFO "PCI: %s IRQ %d for device %s\n", msg, irq, dev->slot_name);
/* Update IRQ for all devices with the same pirq value */
while ((dev2 = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev2)) != NULL) {
pci_read_config_byte(dev2, PCI_INTERRUPT_PIN, &pin);
if (!pin)
continue;
pin--;
info = pirq_get_info(dev2);
if (!info)
continue;
if (info->irq[pin].link == pirq) {
/* We refuse to override the dev->irq information. Give a warning! */
if ( dev2->irq && dev2->irq != irq && \
(!(pci_probe & PCI_USE_PIRQ_MASK) || \
((1 << dev2->irq) & mask)) ) {
printk(KERN_INFO "IRQ routing conflict for %s, have irq %d, want irq %d\n",
dev2->slot_name, dev2->irq, irq);
continue;
}
dev2->irq = irq;
pirq_penalty[irq]++;
if (dev != dev2)
printk(KERN_INFO "PCI: Sharing IRQ %d with %s\n", irq, dev2->slot_name);
}
}
return 1;
}
void __init pcibios_fixup_irqs(void)
{
struct pci_dev *dev = NULL;
u8 pin;
DBG("PCI: IRQ fixup\n");
while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
/*
* If the BIOS has set an out of range IRQ number, just ignore it.
* Also keep track of which IRQ's are already in use.
*/
if (dev->irq >= 16) {
DBG("%s: ignoring bogus IRQ %d\n", dev->slot_name, dev->irq);
dev->irq = 0;
}
/* If the IRQ is already assigned to a PCI device, ignore its ISA use penalty */
if (pirq_penalty[dev->irq] >= 100 && pirq_penalty[dev->irq] < 100000)
pirq_penalty[dev->irq] = 0;
pirq_penalty[dev->irq]++;
}
dev = NULL;
while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
#ifdef CONFIG_X86_IO_APIC
/*
* Recalculate IRQ numbers if we use the I/O APIC.
*/
if (io_apic_assign_pci_irqs)
{
int irq;
if (pin) {
pin--; /* interrupt pins are numbered starting from 1 */
irq = IO_APIC_get_PCI_irq_vector(dev->bus->number, PCI_SLOT(dev->devfn), pin);
/*
* Busses behind bridges are typically not listed in the MP-table.
* In this case we have to look up the IRQ based on the parent bus,
* parent slot, and pin number. The SMP code detects such bridged
* busses itself so we should get into this branch reliably.
*/
if (irq < 0 && dev->bus->parent) { /* go back to the bridge */
struct pci_dev * bridge = dev->bus->self;
pin = (pin + PCI_SLOT(dev->devfn)) % 4;
irq = IO_APIC_get_PCI_irq_vector(bridge->bus->number,
PCI_SLOT(bridge->devfn), pin);
if (irq >= 0)
printk(KERN_WARNING "PCI: using PPB(B%d,I%d,P%d) to get irq %d\n",
bridge->bus->number, PCI_SLOT(bridge->devfn), pin, irq);
}
if (irq >= 0) {
printk(KERN_INFO "PCI->APIC IRQ transform: (B%d,I%d,P%d) -> %d\n",
dev->bus->number, PCI_SLOT(dev->devfn), pin, irq);
dev->irq = irq;
}
}
}
#endif
/*
* Still no IRQ? Try to lookup one...
*/
if (pin && !dev->irq)
pcibios_lookup_irq(dev, 0);
}
}
static int __init pcibios_irq_init(void)
{
DBG("PCI: IRQ init\n");
if (pcibios_enable_irq)
return 0;
pirq_table = pirq_find_routing_table();
if (pirq_table) {
pirq_peer_trick();
pirq_find_router();
if (pirq_table->exclusive_irqs) {
int i;
for (i=0; i<16; i++)
if (!(pirq_table->exclusive_irqs & (1 << i)))
pirq_penalty[i] += 100;
}
/* If we're using the I/O APIC, avoid using the PCI IRQ routing table */
if (io_apic_assign_pci_irqs)
pirq_table = NULL;
}
pcibios_enable_irq = pirq_enable_irq;
pcibios_fixup_irqs();
return 0;
}
subsys_initcall(pcibios_irq_init);
void pcibios_penalize_isa_irq(int irq)
{
/*
* If any ISAPnP device reports an IRQ in its list of possible
* IRQ's, we try to avoid assigning it to PCI devices.
*/
pirq_penalty[irq] += 100;
}
int pirq_enable_irq(struct pci_dev *dev)
{
u8 pin;
extern int interrupt_line_quirk;
pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
if (pin && !pcibios_lookup_irq(dev, 1) && !dev->irq) {
/* With IDE legacy devices the IRQ lookup failure is not a problem.. */
if (dev->class >> 8 == PCI_CLASS_STORAGE_IDE && !(dev->class & 0x5))
return 0;
printk(KERN_WARNING "PCI: No IRQ known for interrupt pin %c of device %s.\n",
'A' + pin - 1, dev->slot_name);
}
/* VIA bridges use interrupt line for apic/pci steering across
the V-Link */
else if (interrupt_line_quirk)
pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
return 0;
}
/*
* legacy.c - traditional, old school PCI bus probing
*/
#include <linux/init.h>
#include <linux/pci.h>
#include "pci.h"
/*
* Discover remaining PCI buses in case there are peer host bridges.
* We use the number of last PCI bus provided by the PCI BIOS.
*/
void __devinit pcibios_fixup_peer_bridges(void)
{
int n;
struct pci_bus *bus;
struct pci_dev *dev;
u16 l;
if (pcibios_last_bus <= 0 || pcibios_last_bus >= 0xff)
return;
DBG("PCI: Peer bridge fixup\n");
bus = kmalloc(sizeof(*bus), GFP_ATOMIC);
dev = kmalloc(sizeof(*dev), GFP_ATOMIC);
if (!bus || !dev) {
printk(KERN_ERR "Out of memory in %s\n", __FUNCTION__);
goto exit;
}
for (n=0; n <= pcibios_last_bus; n++) {
if (pci_bus_exists(&pci_root_buses, n))
continue;
bus->number = n;
bus->ops = pci_root_ops;
dev->bus = bus;
for (dev->devfn=0; dev->devfn<256; dev->devfn += 8)
if (!pci_read_config_word(dev, PCI_VENDOR_ID, &l) &&
l != 0x0000 && l != 0xffff) {
DBG("Found device at %02x:%02x [%04x]\n", n, dev->devfn, l);
printk(KERN_INFO "PCI: Discovered peer bus %02x\n", n);
pci_scan_bus(n, pci_root_ops, NULL);
break;
}
}
exit:
kfree(dev);
kfree(bus);
}
static int __init pci_legacy_init(void)
{
if (!pci_root_ops) {
printk("PCI: System does not support PCI\n");
return 0;
}
if (pcibios_scanned++)
return 0;
printk("PCI: Probing PCI hardware\n");
pci_root_bus = pcibios_scan_root(0);
pcibios_fixup_peer_bridges();
return 0;
}
subsys_initcall(pci_legacy_init);
/*
* Low-Level PCI Access for x86-64 machines.
*
* (c) 1999 Martin Mares <mj@ucw.cz>
*/
#undef DEBUG
#ifdef DEBUG
#define DBG(x...) printk(x)
#else
#define DBG(x...)
#endif
#define PCI_PROBE_BIOS 0x0001
#define PCI_PROBE_CONF1 0x0002
#define PCI_PROBE_CONF2 0x0004
#define PCI_NO_SORT 0x0100
#define PCI_BIOS_SORT 0x0200
#define PCI_NO_CHECKS 0x0400
#define PCI_USE_PIRQ_MASK 0x0800
#define PCI_ASSIGN_ROMS 0x1000
#define PCI_BIOS_IRQ_SCAN 0x2000
#define PCI_ASSIGN_ALL_BUSSES 0x4000
#define PCI_NO_ACPI_ROUTING 0x8000
extern unsigned int pci_probe;
extern unsigned int pcibios_max_latency;
void pcibios_resource_survey(void);
int pcibios_enable_resources(struct pci_dev *, int);
/* pci-pc.c */
extern int pcibios_last_bus;
extern struct pci_bus *pci_root_bus;
extern struct pci_ops *pci_root_ops;
/* pci-irq.c */
struct irq_info {
u8 bus, devfn; /* Bus, device and function */
struct {
u8 link; /* IRQ line ID, chipset dependent, 0=not routed */
u16 bitmap; /* Available IRQs */
} __attribute__((packed)) irq[4];
u8 slot; /* Slot number, 0=onboard */
u8 rfu;
} __attribute__((packed));
struct irq_routing_table {
u32 signature; /* PIRQ_SIGNATURE should be here */
u16 version; /* PIRQ_VERSION */
u16 size; /* Table size in bytes */
u8 rtr_bus, rtr_devfn; /* Where the interrupt router lies */
u16 exclusive_irqs; /* IRQs devoted exclusively to PCI usage */
u16 rtr_vendor, rtr_device; /* Vendor and device ID of interrupt router */
u32 miniport_data; /* Crap */
u8 rfu[11];
u8 checksum; /* Modulo 256 checksum must give zero */
struct irq_info slots[0];
} __attribute__((packed));
extern unsigned int pcibios_irq_mask;
extern int pcibios_scanned;
extern spinlock_t pci_config_lock;
int pirq_enable_irq(struct pci_dev *dev);
extern int (*pcibios_enable_irq)(struct pci_dev *dev);
/* legacy.c */
extern void pcibios_fixup_peer_bridges(void);
/*
* Low-Level PCI Access for x86-64 machines
*
* Copyright 1993, 1994 Drew Eckhardt
* Visionary Computing
* (Unix and Linux consulting and custom programming)
* Drew@Colorado.EDU
* +1 (303) 786-7975
*
* Drew's work was sponsored by:
* iX Multiuser Multitasking Magazine
* Hannover, Germany
* hm@ix.de
*
* Copyright 1997--2000 Martin Mares <mj@ucw.cz>
*
* For more information, please consult the following manuals (look at
* http://www.pcisig.com/ for how to get them):
*
* PCI BIOS Specification
* PCI Local Bus Specification
* PCI to PCI Bridge Specification
* PCI System Design Guide
*
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/errno.h>
#include "pci.h"
/*
* We need to avoid collisions with `mirrored' VGA ports
* and other strange ISA hardware, so we always want the
* addresses to be allocated in the 0x000-0x0ff region
* modulo 0x400.
*
* Why? Because some silly external IO cards only decode
* the low 10 bits of the IO address. The 0x00-0xff region
* is reserved for motherboard devices that decode all 16
* bits, so it's ok to allocate at, say, 0x2800-0x28ff,
* but we want to try to avoid allocating at 0x2900-0x2bff
* which might have be mirrored at 0x0100-0x03ff..
*/
void
pcibios_align_resource(void *data, struct resource *res,
unsigned long size, unsigned long align)
{
if (res->flags & IORESOURCE_IO) {
unsigned long start = res->start;
if (start & 0x300) {
start = (start + 0x3ff) & ~0x3ff;
res->start = start;
}
}
}
/*
* Handle resources of PCI devices. If the world were perfect, we could
* just allocate all the resource regions and do nothing more. It isn't.
* On the other hand, we cannot just re-allocate all devices, as it would
* require us to know lots of host bridge internals. So we attempt to
* keep as much of the original configuration as possible, but tweak it
* when it's found to be wrong.
*
* Known BIOS problems we have to work around:
* - I/O or memory regions not configured
* - regions configured, but not enabled in the command register
* - bogus I/O addresses above 64K used
* - expansion ROMs left enabled (this may sound harmless, but given
* the fact the PCI specs explicitly allow address decoders to be
* shared between expansion ROMs and other resource regions, it's
* at least dangerous)
*
* Our solution:
* (1) Allocate resources for all buses behind PCI-to-PCI bridges.
* This gives us fixed barriers on where we can allocate.
* (2) Allocate resources for all enabled devices. If there is
* a collision, just mark the resource as unallocated. Also
* disable expansion ROMs during this step.
* (3) Try to allocate resources for disabled devices. If the
* resources were assigned correctly, everything goes well,
* if they weren't, they won't disturb allocation of other
* resources.
* (4) Assign new addresses to resources which were either
* not configured at all or misconfigured. If explicitly
* requested by the user, configure expansion ROM address
* as well.
*/
static void __init pcibios_allocate_bus_resources(struct list_head *bus_list)
{
struct list_head *ln;
struct pci_bus *bus;
struct pci_dev *dev;
int idx;
struct resource *r, *pr;
/* Depth-First Search on bus tree */
for (ln=bus_list->next; ln != bus_list; ln=ln->next) {
bus = pci_bus_b(ln);
if ((dev = bus->self)) {
for (idx = PCI_BRIDGE_RESOURCES; idx < PCI_NUM_RESOURCES; idx++) {
r = &dev->resource[idx];
if (!r->start)
continue;
pr = pci_find_parent_resource(dev, r);
if (!pr || request_resource(pr, r) < 0)
printk(KERN_ERR "PCI: Cannot allocate resource region %d of bridge %s\n", idx, dev->slot_name);
}
}
pcibios_allocate_bus_resources(&bus->children);
}
}
static void __init pcibios_allocate_resources(int pass)
{
struct pci_dev *dev = NULL;
int idx, disabled;
u16 command;
struct resource *r, *pr;
while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
pci_read_config_word(dev, PCI_COMMAND, &command);
for(idx = 0; idx < 6; idx++) {
r = &dev->resource[idx];
if (r->parent) /* Already allocated */
continue;
if (!r->start) /* Address not assigned at all */
continue;
if (r->flags & IORESOURCE_IO)
disabled = !(command & PCI_COMMAND_IO);
else
disabled = !(command & PCI_COMMAND_MEMORY);
if (pass == disabled) {
DBG("PCI: Resource %08lx-%08lx (f=%lx, d=%d, p=%d)\n",
r->start, r->end, r->flags, disabled, pass);
pr = pci_find_parent_resource(dev, r);
if (!pr || request_resource(pr, r) < 0) {
printk(KERN_ERR "PCI: Cannot allocate resource region %d of device %s\n", idx, dev->slot_name);
/* We'll assign a new address later */
r->end -= r->start;
r->start = 0;
}
}
}
if (!pass) {
r = &dev->resource[PCI_ROM_RESOURCE];
if (r->flags & PCI_ROM_ADDRESS_ENABLE) {
/* Turn the ROM off, leave the resource region, but keep it unregistered. */
u32 reg;
DBG("PCI: Switching off ROM of %s\n", dev->slot_name);
r->flags &= ~PCI_ROM_ADDRESS_ENABLE;
pci_read_config_dword(dev, dev->rom_base_reg, &reg);
pci_write_config_dword(dev, dev->rom_base_reg, reg & ~PCI_ROM_ADDRESS_ENABLE);
}
}
}
}
static void __init pcibios_assign_resources(void)
{
struct pci_dev *dev = NULL;
int idx;
struct resource *r;
while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
int class = dev->class >> 8;
/* Don't touch classless devices and host bridges */
if (!class || class == PCI_CLASS_BRIDGE_HOST)
continue;
for(idx=0; idx<6; idx++) {
r = &dev->resource[idx];
/*
* Don't touch IDE controllers and I/O ports of video cards!
*/
if ((class == PCI_CLASS_STORAGE_IDE && idx < 4) ||
(class == PCI_CLASS_DISPLAY_VGA && (r->flags & IORESOURCE_IO)))
continue;
/*
* We shall assign a new address to this resource, either because
* the BIOS forgot to do so or because we have decided the old
* address was unusable for some reason.
*/
if (!r->start && r->end)
pci_assign_resource(dev, idx);
}
if (pci_probe & PCI_ASSIGN_ROMS) {
r = &dev->resource[PCI_ROM_RESOURCE];
r->end -= r->start;
r->start = 0;
if (r->end)
pci_assign_resource(dev, PCI_ROM_RESOURCE);
}
}
}
void __init pcibios_resource_survey(void)
{
DBG("PCI: Allocating resources\n");
pcibios_allocate_bus_resources(&pci_root_buses);
pcibios_allocate_resources(0);
pcibios_allocate_resources(1);
pcibios_assign_resources();
}
int pcibios_enable_resources(struct pci_dev *dev, int mask)
{
u16 cmd, old_cmd;
int idx;
struct resource *r;
pci_read_config_word(dev, PCI_COMMAND, &cmd);
old_cmd = cmd;
for(idx=0; idx<6; idx++) {
if (!(mask & (1<<idx)))
continue;
r = &dev->resource[idx];
if (!r->start && r->end) {
printk(KERN_ERR "PCI: Device %s not available because of resource collisions\n", dev->slot_name);
return -EINVAL;
}
if (r->flags & IORESOURCE_IO)
cmd |= PCI_COMMAND_IO;
if (r->flags & IORESOURCE_MEM)
cmd |= PCI_COMMAND_MEMORY;
}
if (dev->resource[PCI_ROM_RESOURCE].start)
cmd |= PCI_COMMAND_MEMORY;
if (cmd != old_cmd) {
printk("PCI: Enabling device %s (%04x -> %04x)\n", dev->slot_name, old_cmd, cmd);
pci_write_config_word(dev, PCI_COMMAND, cmd);
}
return 0;
}
/*
* If we set up a device for bus mastering, we need to check the latency
* timer as certain crappy BIOSes forget to set it properly.
*/
unsigned int pcibios_max_latency = 255;
void pcibios_set_master(struct pci_dev *dev)
{
u8 lat;
pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
if (lat < 16)
lat = (64 <= pcibios_max_latency) ? 64 : pcibios_max_latency;
else if (lat > pcibios_max_latency)
lat = pcibios_max_latency;
else
return;
printk("PCI: Setting latency timer of device %s to %d\n", dev->slot_name, lat);
pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat);
}
int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
enum pci_mmap_state mmap_state, int write_combine)
{
unsigned long prot;
/* I/O space cannot be accessed via normal processor loads and
* stores on this platform.
*/
if (mmap_state == pci_mmap_io)
return -EINVAL;
/* Leave vm_pgoff as-is, the PCI space address is the physical
* address on this platform.
*/
vma->vm_flags |= (VM_SHM | VM_LOCKED | VM_IO);
prot = pgprot_val(vma->vm_page_prot);
if (boot_cpu_data.x86 > 3)
prot |= _PAGE_PCD | _PAGE_PWT;
vma->vm_page_prot = __pgprot(prot);
/* Write-combine setting is ignored, it is changed via the mtrr
* interfaces on this platform.
*/
if (remap_page_range(vma, vma->vm_start, vma->vm_pgoff << PAGE_SHIFT,
vma->vm_end - vma->vm_start,
vma->vm_page_prot))
return -EAGAIN;
return 0;
}
......@@ -16,6 +16,7 @@
#include <asm/apicdef.h>
#include <asm/page.h>
#include <asm/vsyscall.h>
#include <asm/vsyscall32.h>
/*
* Here we define all the compile-time 'special' virtual
......@@ -62,6 +63,10 @@ extern void __set_fixmap (enum fixed_addresses idx,
#define FIXADDR_SIZE (__end_of_fixed_addresses << PAGE_SHIFT)
#define FIXADDR_START (FIXADDR_TOP - FIXADDR_SIZE)
/* Only covers 32bit vsyscalls currently. Need another set for 64bit. */
#define FIXADDR_USER_START ((unsigned long)VSYSCALL32_VSYSCALL)
#define FIXADDR_USER_END (FIXADDR_USER_START + PAGE_SIZE)
#define __fix_to_virt(x) (FIXADDR_TOP - ((x) << PAGE_SHIFT))
extern void __this_fixmap_does_not_exist(void);
......
......@@ -105,7 +105,7 @@ __OUTS(l)
#define IO_SPACE_LIMIT 0xffff
#ifdef __KERNEL__
#if defined(__KERNEL__) && __x86_64__
#include <linux/vmalloc.h>
......
......@@ -23,7 +23,6 @@ extern int maxnode;
extern struct pglist_data *node_data[];
/* kern_addr_valid below hardcodes the same algorithm*/
static inline __attribute__((pure)) int phys_to_nid(unsigned long addr)
{
int nid;
......@@ -46,19 +45,6 @@ static inline __attribute__((pure)) int phys_to_nid(unsigned long addr)
#define local_mapnr(kvaddr) \
( (__pa(kvaddr) >> PAGE_SHIFT) - node_start_pfn(kvaddr_to_nid(kvaddr)) )
#define kern_addr_valid(kvaddr) ({ \
int ok = 0; \
unsigned long index = __pa(kvaddr) >> memnode_shift; \
if (index <= NODEMAPSIZE) { \
unsigned nodeid = memnodemap[index]; \
unsigned long pfn = __pa(kvaddr) >> PAGE_SHIFT; \
unsigned long start_pfn = node_start_pfn(nodeid); \
ok = (nodeid != 0xff) && \
(pfn >= start_pfn) && \
(pfn < start_pfn + node_size(nodeid)); \
} \
ok; \
})
/* AK: this currently doesn't deal with invalid addresses. We'll see
if the 2.5 kernel doesn't pass them
......
......@@ -76,6 +76,8 @@ extern unsigned long vm_force_exec32;
#define __PAGE_OFFSET 0x0000010000000000 /* 1 << 40 */
#define __PHYSICAL_MASK_SHIFT 40
#define __PHYSICAL_MASK ((1UL << __PHYSICAL_MASK_SHIFT) - 1)
#define __VIRTUAL_MASK_SHIFT 48
#define __VIRTUAL_MASK ((1UL << __VIRTUAL_MASK_SHIFT) - 1)
#define KERNEL_TEXT_SIZE (40UL*1024*1024)
#define KERNEL_TEXT_START 0xffffffff80000000UL
......
......@@ -45,7 +45,6 @@ int pcibios_set_irq_routing(struct pci_dev *dev, int pin, int irq);
struct pci_dev;
extern int iommu_setup(char *opt);
extern void pci_iommu_init(void);
/* Allocate and map kernel buffer using consistent mode DMA for a device.
* hwdev should be valid struct pci_dev pointer for PCI devices,
......
......@@ -339,7 +339,7 @@ static inline pgd_t *current_pgd_offset_k(unsigned long address)
#define pmd_clear(xp) do { set_pmd(xp, __pmd(0)); } while (0)
#define pmd_bad(x) ((pmd_val(x) & (~PTE_MASK & ~_PAGE_USER)) != _KERNPG_TABLE )
#define pfn_pmd(nr,prot) (__pmd(((nr) << PAGE_SHIFT) | pgprot_val(prot)))
#define pmd_pfn(x) ((pmd_val(x) >> PAGE_SHIFT) & __PHYSICAL_MASK)
#define pte_to_pgoff(pte) ((pte_val(pte) & PHYSICAL_PAGE_MASK) >> PAGE_SHIFT)
#define pgoff_to_pte(off) ((pte_t) { ((off) << PAGE_SHIFT) | _PAGE_FILE })
......@@ -392,9 +392,7 @@ typedef pte_t *pte_addr_t;
#endif /* !__ASSEMBLY__ */
#ifndef CONFIG_DISCONTIGMEM
#define kern_addr_valid(addr) (1)
#endif
extern int kern_addr_valid(unsigned long addr);
#define io_remap_page_range remap_page_range
......@@ -403,4 +401,9 @@ typedef pte_t *pte_addr_t;
#define pgtable_cache_init() do { } while (0)
#define check_pgt_cache() do { } while (0)
/* fs/proc/kcore.c */
#define kc_vaddr_to_offset(v) ((v) & __VIRTUAL_MASK)
#define kc_offset_to_vaddr(o) \
(((o) & (1UL << (__VIRTUAL_MASK_SHIFT-1))) ? ((o) | (~__VIRTUAL_MASK)) : (o))
#endif /* _X86_64_PGTABLE_H */
......@@ -16,7 +16,12 @@ extern void mcheck_init(struct cpuinfo_x86 *c);
extern void init_memory_mapping(void);
extern void system_call(void);
extern int kernel_syscall(void);
extern void syscall_init(void);
extern void ia32_syscall(void);
extern void ia32_cstar_target(void);
extern void calibrate_delay(void);
extern void cpu_idle(void);
extern void sys_ni_syscall(void);
......@@ -24,6 +29,8 @@ extern void config_acpi_tables(void);
extern void ia32_syscall(void);
extern void iommu_hole_init(void);
extern void time_init_smp(void);
extern void do_softirq_thunk(void);
extern int numa_setup(char *opt);
......@@ -39,8 +46,12 @@ extern unsigned long numa_free_all_bootmem(void);
extern void reserve_bootmem_generic(unsigned long phys, unsigned len);
extern void free_bootmem_generic(unsigned long phys, unsigned len);
extern void load_gs_index(unsigned gs);
extern unsigned long end_pfn_map;
extern unsigned long cpu_initialized;
extern void show_stack(unsigned long * rsp);
extern void show_trace(unsigned long * rsp);
extern void show_registers(struct pt_regs *regs);
......@@ -66,9 +77,16 @@ extern unsigned long max_mapnr;
extern unsigned long end_pfn;
extern unsigned long table_start, table_end;
extern void syscall_init(void);
extern int exception_trace;
extern int no_iommu, force_mmu;
extern int using_apic_timer;
extern int disable_apic;
extern unsigned cpu_khz;
struct pt_regs;
extern int fallback_aper_order;
extern int fallback_aper_force;
extern void smp_local_timer_interrupt(struct pt_regs * regs);
long do_arch_prctl(struct task_struct *task, int code, unsigned long addr);
......
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