summaryrefslogtreecommitdiffstats
path: root/arch/i386
diff options
context:
space:
mode:
Diffstat (limited to 'arch/i386')
-rw-r--r--arch/i386/Makefile4
-rw-r--r--arch/i386/boot/.cvsignore1
-rw-r--r--arch/i386/boot/bootsect.S1
-rw-r--r--arch/i386/boot/compressed/.cvsignore1
-rw-r--r--arch/i386/boot/compressed/Makefile2
-rw-r--r--arch/i386/boot/compressed/head.S1
-rw-r--r--arch/i386/boot/compressed/misc.c4
-rw-r--r--arch/i386/boot/setup.S6
-rw-r--r--arch/i386/boot/tools/.cvsignore1
-rw-r--r--arch/i386/boot/tools/build.c1
-rw-r--r--arch/i386/boot/video.S2
-rw-r--r--arch/i386/config.in31
-rw-r--r--arch/i386/defconfig39
-rw-r--r--arch/i386/kernel/.cvsignore1
-rw-r--r--arch/i386/kernel/Makefile9
-rw-r--r--arch/i386/kernel/bios32.c45
-rw-r--r--arch/i386/kernel/entry.S16
-rw-r--r--arch/i386/kernel/head.S225
-rw-r--r--arch/i386/kernel/i386_ksyms.c23
-rw-r--r--arch/i386/kernel/io_apic.c644
-rw-r--r--arch/i386/kernel/irq.c994
-rw-r--r--arch/i386/kernel/irq.h47
-rw-r--r--arch/i386/kernel/ldt.c28
-rw-r--r--arch/i386/kernel/mca.c25
-rw-r--r--arch/i386/kernel/process.c33
-rw-r--r--arch/i386/kernel/ptrace.c5
-rw-r--r--arch/i386/kernel/setup.c427
-rw-r--r--arch/i386/kernel/signal.c47
-rw-r--r--arch/i386/kernel/smp.c179
-rw-r--r--arch/i386/kernel/sys_i386.c7
-rw-r--r--arch/i386/kernel/time.c33
-rw-r--r--arch/i386/kernel/traps.c5
-rw-r--r--arch/i386/kernel/vm86.c2
-rw-r--r--arch/i386/lib/.cvsignore1
-rw-r--r--arch/i386/lib/Makefile2
-rw-r--r--arch/i386/lib/checksum.c2
-rw-r--r--arch/i386/lib/delay.c8
-rw-r--r--arch/i386/lib/getuser.S73
-rw-r--r--arch/i386/lib/putuser.S71
-rw-r--r--arch/i386/lib/usercopy.c136
-rw-r--r--arch/i386/math-emu/fpu_entry.c6
-rw-r--r--arch/i386/math-emu/get_address.c28
-rw-r--r--arch/i386/math-emu/reg_ld_str.c6
-rw-r--r--arch/i386/mm/.cvsignore1
-rw-r--r--arch/i386/mm/fault.c26
-rw-r--r--arch/i386/mm/init.c60
46 files changed, 2472 insertions, 837 deletions
diff --git a/arch/i386/Makefile b/arch/i386/Makefile
index bc4e03029..98bde850f 100644
--- a/arch/i386/Makefile
+++ b/arch/i386/Makefile
@@ -23,7 +23,9 @@ OBJCOPY=$(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S
LDFLAGS=-e stext
LINKFLAGS =-T $(TOPDIR)/arch/i386/vmlinux.lds $(LDFLAGS)
-CFLAGS := $(CFLAGS) -pipe -fno-strength-reduce
+CFLAGS_PIPE := -pipe
+CFLAGS_NSR := -fno-strength-reduce
+CFLAGS := $(CFLAGS) $(CFLAGS_PIPE) $(CFLAGS_NSR)
ifdef CONFIG_M386
CFLAGS := $(CFLAGS) -m386 -DCPU=386
diff --git a/arch/i386/boot/.cvsignore b/arch/i386/boot/.cvsignore
index 0c95c90f8..a2f0006fd 100644
--- a/arch/i386/boot/.cvsignore
+++ b/arch/i386/boot/.cvsignore
@@ -1,4 +1,5 @@
*.s
+.*.flags
bootsect
setup
zImage
diff --git a/arch/i386/boot/bootsect.S b/arch/i386/boot/bootsect.S
index 1f06031af..f7f252b41 100644
--- a/arch/i386/boot/bootsect.S
+++ b/arch/i386/boot/bootsect.S
@@ -22,6 +22,7 @@
! read errors will result in a unbreakable loop. Reboot by hand. It
! loads pretty fast by getting whole tracks at a time whenever possible.
+#include <linux/config.h> /* for CONFIG_ROOT_RDONLY */
#include <asm/boot.h>
.text
diff --git a/arch/i386/boot/compressed/.cvsignore b/arch/i386/boot/compressed/.cvsignore
index 97e6777cb..dc5854255 100644
--- a/arch/i386/boot/compressed/.cvsignore
+++ b/arch/i386/boot/compressed/.cvsignore
@@ -1,3 +1,4 @@
*tmppiggy*
+.*.flags
vmlinux
vmlinux.out
diff --git a/arch/i386/boot/compressed/Makefile b/arch/i386/boot/compressed/Makefile
index 653c12eba..770c563ec 100644
--- a/arch/i386/boot/compressed/Makefile
+++ b/arch/i386/boot/compressed/Makefile
@@ -57,4 +57,4 @@ piggy.o: $(SYSTEM)
rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk
clean:
- rm -f vmlinux bvmlinux
+ rm -f vmlinux bvmlinux _tmp_*
diff --git a/arch/i386/boot/compressed/head.S b/arch/i386/boot/compressed/head.S
index 30384a3a3..fb1bd5559 100644
--- a/arch/i386/boot/compressed/head.S
+++ b/arch/i386/boot/compressed/head.S
@@ -28,7 +28,6 @@
.text
#define __ASSEMBLY__
-#include <linux/config.h>
#include <linux/linkage.h>
#include <asm/segment.h>
diff --git a/arch/i386/boot/compressed/misc.c b/arch/i386/boot/compressed/misc.c
index e4a6ede9a..4d2ece791 100644
--- a/arch/i386/boot/compressed/misc.c
+++ b/arch/i386/boot/compressed/misc.c
@@ -349,9 +349,9 @@ int decompress_kernel(struct moveparams *mv)
else setup_output_buffer_if_we_run_high(mv);
makecrc();
- puts("Uncompressing Linux...");
+ puts("Uncompressing Linux... ");
gunzip();
- puts("done.\nNow booting the kernel\n");
+ puts("Ok, booting the kernel.\n");
if (high_loaded) close_output_buffer_if_we_run_high(mv);
return high_loaded;
}
diff --git a/arch/i386/boot/setup.S b/arch/i386/boot/setup.S
index 11310bd49..60261b836 100644
--- a/arch/i386/boot/setup.S
+++ b/arch/i386/boot/setup.S
@@ -274,6 +274,8 @@ loader_ok:
oldstylemem:
pop ebx
+#else
+ mov dword ptr [0x1e0], #0
#endif
mov ah,#0x88
int 0x15
@@ -408,8 +410,8 @@ no_psmouse:
mov [68],ebx ! BIOS entry point offset
mov [72],cx ! BIOS 16 bit code segment
mov [74],dx ! BIOS data segment
- mov [78],si ! BIOS code segment length
- mov [80],di ! BIOS data segment length
+ mov [78],esi ! BIOS code segment length
+ mov [82],di ! BIOS data segment length
jmp done_apm_bios
no_32_apm_bios:
diff --git a/arch/i386/boot/tools/.cvsignore b/arch/i386/boot/tools/.cvsignore
index 378eac25d..8c85bbf5a 100644
--- a/arch/i386/boot/tools/.cvsignore
+++ b/arch/i386/boot/tools/.cvsignore
@@ -1 +1,2 @@
build
+.*.flags
diff --git a/arch/i386/boot/tools/build.c b/arch/i386/boot/tools/build.c
index 3998daf84..5224a6e7b 100644
--- a/arch/i386/boot/tools/build.c
+++ b/arch/i386/boot/tools/build.c
@@ -33,7 +33,6 @@
#include <sys/sysmacros.h>
#include <unistd.h>
#include <fcntl.h>
-#include <linux/config.h>
#include <asm/boot.h>
typedef unsigned char byte;
diff --git a/arch/i386/boot/video.S b/arch/i386/boot/video.S
index d09f11283..25d02a443 100644
--- a/arch/i386/boot/video.S
+++ b/arch/i386/boot/video.S
@@ -5,6 +5,8 @@
! Based on the original setup.S code (C) Linus Torvalds and Mats Anderson
!
+#include <linux/config.h> /* for CONFIG_VIDEO_* */
+
! Enable autodetection of SVGA adapters and modes. If you really need this
! feature, drop me a mail as I think of removing it some day...
#undef CONFIG_VIDEO_SVGA
diff --git a/arch/i386/config.in b/arch/i386/config.in
index 7595ab5fe..3e52c2218 100644
--- a/arch/i386/config.in
+++ b/arch/i386/config.in
@@ -10,6 +10,16 @@ bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL
endmenu
mainmenu_option next_comment
+comment 'Processor type and features'
+choice 'Processor family' \
+ "386 CONFIG_M386 \
+ 486/Cx486 CONFIG_M486 \
+ Pentium/K5/5x86/6x86 CONFIG_M586 \
+ PPro/K6/6x86MX CONFIG_M686" Pentium
+bool 'Math emulation' CONFIG_MATH_EMULATION
+endmenu
+
+mainmenu_option next_comment
comment 'Loadable module support'
bool 'Enable loadable module support' CONFIG_MODULES
if [ "$CONFIG_MODULES" = "y" ]; then
@@ -21,7 +31,6 @@ endmenu
mainmenu_option next_comment
comment 'General setup'
-bool 'Kernel math emulation' CONFIG_MATH_EMULATION
bool 'Networking support' CONFIG_NET
bool 'PCI support' CONFIG_PCI
if [ "$CONFIG_PCI" = "y" ]; then
@@ -30,9 +39,11 @@ if [ "$CONFIG_PCI" = "y" ]; then
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
bool ' PCI bridge optimization (experimental)' CONFIG_PCI_OPTIMIZE
fi
+ bool ' Backward-compatible /proc/pci' CONFIG_PCI_OLD_PROC
fi
bool 'MCA support' CONFIG_MCA
bool 'System V IPC' CONFIG_SYSVIPC
+bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT
bool 'Sysctl support' CONFIG_SYSCTL
tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT
tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
@@ -41,16 +52,14 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
tristate 'Kernel support for JAVA binaries (obsolete)' CONFIG_BINFMT_JAVA
fi
-choice 'Processor type' \
- "386 CONFIG_M386 \
- 486 CONFIG_M486 \
- Pentium CONFIG_M586 \
- PPro CONFIG_M686" Pentium
bool 'Video mode selection support' CONFIG_VIDEO_SELECT
tristate 'Parallel port support' CONFIG_PARPORT
if [ "$CONFIG_PARPORT" != "n" ]; then
- dep_tristate ' PC-style hardware' CONFIG_PARPORT_PC $CONFIG_PARPORT
+ dep_tristate ' PC-style hardware' CONFIG_PARPORT_PC $CONFIG_PARPORT
+ if [ "$CONFIG_PARPORT_PC" != "n" ]; then
+ bool ' Support foreign hardware' CONFIG_PARPORT_OTHER
+ fi
fi
endmenu
@@ -84,7 +93,7 @@ if [ "$CONFIG_NET" = "y" ]; then
endmenu
fi
-source drivers/net/hamradio/Config.in
+source net/ax25/Config.in
mainmenu_option next_comment
comment 'ISDN subsystem'
@@ -105,10 +114,10 @@ fi
endmenu
# Conditionally compile in the Uniform CD-ROM driver
-if [ "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_BLK_DEV_SR" = "y" -o "$CONFIG_SBPCD" = "y" -o "$CONFIG_MCD" = "y" -o "$CONFIG_CM206" = "y" -o "$CONFIG_CDU31A" = "y" ]; then
+if [ "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_BLK_DEV_SR" = "y" -o "$CONFIG_SBPCD" = "y" -o "$CONFIG_MCD" = "y" -o "$CONFIG_MCDX" = "y" -o "$CONFIG_CM206" = "y" -o "$CONFIG_CDU31A" = "y" ]; then
define_bool CONFIG_CDROM y
else
- if [ "$CONFIG_BLK_DEV_IDECD" = "m" -o "$CONFIG_BLK_DEV_SR" = "m" -o "$CONFIG_SBPCD" = "m" -o "$CONFIG_MCD" = "m" -o "$CONFIG_CM206" = "m" -o "$CONFIG_CDU31A" = "m" ]; then
+ if [ "$CONFIG_BLK_DEV_IDECD" = "m" -o "$CONFIG_BLK_DEV_SR" = "m" -o "$CONFIG_SBPCD" = "m" -o "$CONFIG_MCD" = "m" -o "$CONFIG_MCDX" = "m" -o "$CONFIG_CM206" = "m" -o "$CONFIG_CDU31A" = "m" ]; then
define_bool CONFIG_CDROM m
else
define_bool CONFIG_CDROM n
@@ -140,3 +149,5 @@ if [ "$CONFIG_PROFILE" = "y" ]; then
fi
bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ
endmenu
+
+define_bool CONFIG_VGA_CONSOLE y
diff --git a/arch/i386/defconfig b/arch/i386/defconfig
index f8793e75f..2ca6da6bc 100644
--- a/arch/i386/defconfig
+++ b/arch/i386/defconfig
@@ -8,6 +8,15 @@
# CONFIG_EXPERIMENTAL is not set
#
+# Processor type and features
+#
+# CONFIG_M386 is not set
+# CONFIG_M486 is not set
+CONFIG_M586=y
+# CONFIG_M686 is not set
+# CONFIG_MATH_EMULATION is not set
+
+#
# Loadable module support
#
CONFIG_MODULES=y
@@ -17,21 +26,19 @@ CONFIG_MODULES=y
#
# General setup
#
-# CONFIG_MATH_EMULATION is not set
CONFIG_NET=y
CONFIG_PCI=y
CONFIG_PCI_BIOS=y
-# CONFIG_PCI_DIRECT is not set
+CONFIG_PCI_DIRECT=y
+# CONFIG_PCI_OPTIMIZE is not set
+CONFIG_PCI_OLD_PROC=y
# CONFIG_MCA is not set
CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
CONFIG_SYSCTL=y
CONFIG_BINFMT_AOUT=y
CONFIG_BINFMT_ELF=y
CONFIG_BINFMT_MISC=y
-# CONFIG_M386 is not set
-# CONFIG_M486 is not set
-# CONFIG_M586 is not set
-CONFIG_M686=y
# CONFIG_VIDEO_SELECT is not set
# CONFIG_PARPORT is not set
@@ -69,7 +76,8 @@ CONFIG_BLK_DEV_IDEDMA=y
# CONFIG_BLK_DEV_MD is not set
# CONFIG_BLK_DEV_RAM is not set
# CONFIG_BLK_DEV_XD is not set
-# CONFIG_BLK_DEV_EZ is not set
+CONFIG_PARIDE_PARPORT=y
+# CONFIG_PARIDE is not set
# CONFIG_BLK_DEV_HD is not set
#
@@ -79,6 +87,7 @@ CONFIG_PACKET=y
# CONFIG_NETLINK is not set
# CONFIG_FIREWALL is not set
# CONFIG_NET_ALIAS is not set
+# CONFIG_FILTER is not set
CONFIG_UNIX=y
CONFIG_INET=y
# CONFIG_IP_MULTICAST is not set
@@ -123,6 +132,7 @@ CONFIG_BLK_DEV_SD=y
#
CONFIG_SCSI_MULTI_LUN=y
CONFIG_SCSI_CONSTANTS=y
+# CONFIG_SCSI_LOGGING is not set
#
# SCSI low-level drivers
@@ -185,8 +195,7 @@ CONFIG_EEXPRESS_PRO100=y
# CONFIG_DLCI is not set
# CONFIG_PPP is not set
# CONFIG_SLIP is not set
-# CONFIG_STRIP is not set
-# CONFIG_WAVELAN is not set
+# CONFIG_NET_RADIO is not set
# CONFIG_TR is not set
# CONFIG_WAN_DRIVERS is not set
# CONFIG_LAPBETHER is not set
@@ -228,16 +237,14 @@ CONFIG_LOCKD=y
# CONFIG_CODA_FS is not set
# CONFIG_SMB_FS is not set
# CONFIG_HPFS_FS is not set
+# CONFIG_NTFS_FS is not set
# CONFIG_SYSV_FS is not set
# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
# CONFIG_ROMFS_FS is not set
CONFIG_AUTOFS_FS=y
# CONFIG_UFS_FS is not set
# CONFIG_MAC_PARTITION is not set
-
-#
-# Native Language Support
-#
# CONFIG_NLS is not set
#
@@ -262,8 +269,6 @@ CONFIG_82C710_MOUSE=y
# CONFIG_WATCHDOG is not set
# CONFIG_RTC is not set
# CONFIG_VIDEO_DEV is not set
-# CONFIG_VIDEO_BT848 is not set
-# CONFIG_VIDEO_PMS is not set
# CONFIG_NVRAM is not set
# CONFIG_JOYSTICK is not set
# CONFIG_MISC_RADIO is not set
@@ -281,5 +286,7 @@ CONFIG_82C710_MOUSE=y
#
# Kernel hacking
#
-# CONFIG_PROFILE is not set
+CONFIG_PROFILE=y
+CONFIG_PROFILE_SHIFT=2
# CONFIG_MAGIC_SYSRQ is not set
+CONFIG_VGA_CONSOLE=y
diff --git a/arch/i386/kernel/.cvsignore b/arch/i386/kernel/.cvsignore
index 4671378ae..857dd22e9 100644
--- a/arch/i386/kernel/.cvsignore
+++ b/arch/i386/kernel/.cvsignore
@@ -1 +1,2 @@
.depend
+.*.flags
diff --git a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile
index 9491ef562..ce1e6652d 100644
--- a/arch/i386/kernel/Makefile
+++ b/arch/i386/kernel/Makefile
@@ -18,17 +18,22 @@ endif
all: kernel.o head.o init_task.o
O_TARGET := kernel.o
-O_OBJS := process.o signal.o entry.o traps.o irq.o vm86.o bios32.o \
+O_OBJS := process.o signal.o entry.o traps.o irq.o vm86.o \
ptrace.o ioport.o ldt.o setup.o time.o sys_i386.o
OX_OBJS := i386_ksyms.o
+ifdef CONFIG_PCI
+O_OBJS += bios32.o
+endif
+
ifdef CONFIG_MCA
O_OBJS += mca.o
endif
+
ifdef SMP
-O_OBJS += smp.o trampoline.o
+O_OBJS += io_apic.o smp.o trampoline.o
head.o: head.S $(TOPDIR)/include/linux/tasks.h
$(CC) -D__ASSEMBLY__ -D__SMP__ -traditional -c $*.S -o $*.o
diff --git a/arch/i386/kernel/bios32.c b/arch/i386/kernel/bios32.c
index 8e41188c3..7e865c417 100644
--- a/arch/i386/kernel/bios32.c
+++ b/arch/i386/kernel/bios32.c
@@ -78,6 +78,13 @@
#include <asm/system.h>
#include <asm/io.h>
+#include <linux/smp_lock.h>
+#include <asm/irq.h>
+#include <asm/bitops.h>
+#include <asm/smp.h>
+
+#include "irq.h"
+
/*
* Generic PCI access -- indirect calls according to detected HW.
*/
@@ -133,7 +140,39 @@ int pcibios_find_device (unsigned short vendor, unsigned short device_id,
int pcibios_read_config_byte (unsigned char bus,
unsigned char device_fn, unsigned char where, unsigned char *value)
{
- return access_pci->read_config_byte(bus, device_fn, where, value);
+ int res;
+
+ res = access_pci->read_config_byte(bus, device_fn, where, value);
+
+#ifdef __SMP__
+/*
+ * IOAPICs can take PCI IRQs directly, lets first check the mptable:
+ */
+ if (where == PCI_INTERRUPT_LINE) {
+ int irq;
+ char pin;
+
+ /*
+ * get the PCI IRQ INT _physical pin_ for this device
+ */
+ access_pci->read_config_byte(bus, device_fn,
+ PCI_INTERRUPT_PIN, &pin);
+ /*
+ * subtle, PCI pins are numbered starting from 1 ...
+ */
+ pin--;
+
+ irq = IO_APIC_get_PCI_irq_vector (bus,PCI_SLOT(device_fn),pin);
+ if (irq != -1)
+ *value = (unsigned char) irq;
+
+ printk("PCI->APIC IRQ transform: (B%d,I%d,P%d) -> %d\n",
+ bus,PCI_SLOT(device_fn), pin, irq);
+
+ }
+#endif
+
+ return res;
}
int pcibios_read_config_word (unsigned char bus,
@@ -456,7 +495,7 @@ __initfunc(static struct pci_access *pci_check_direct(void))
outb (0x00, 0xCFB);
outb (0x00, 0xCF8);
outb (0x00, 0xCFA);
- if (inb (0xCF8) == 0x00 && inb (0xCFB) == 0x00) {
+ if (inb (0xCF8) == 0x00 && inb (0xCFA) == 0x00) {
restore_flags(flags);
printk("PCI: Using configuration type 2\n");
return &pci_direct_conf2;
@@ -897,6 +936,8 @@ __initfunc(unsigned long pcibios_init(unsigned long memory_start, unsigned long
#else
#ifdef CONFIG_PCI_DIRECT
a = pci_check_direct();
+#else
+#error "You need to set CONFIG_PCI_BIOS or CONFIG_PCI_DIRECT if you want PCI support."
#endif
#endif
if (a)
diff --git a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S
index cf99d6e2e..14b82b45b 100644
--- a/arch/i386/kernel/entry.S
+++ b/arch/i386/kernel/entry.S
@@ -71,13 +71,10 @@ VM_MASK = 0x00020000
* these are offsets into the task-struct.
*/
state = 0
-counter = 4
-priority = 8
-flags = 12
-sigpending = 16
-dbgreg6 = 44
-dbgreg7 = 48
-exec_domain = 52
+flags = 4
+sigpending = 8
+addr_limit = 12
+exec_domain = 16
ENOSYS = 38
@@ -369,7 +366,7 @@ ENTRY(sys_call_table)
.long SYMBOL_NAME(sys_time)
.long SYMBOL_NAME(sys_mknod)
.long SYMBOL_NAME(sys_chmod) /* 15 */
- .long SYMBOL_NAME(sys_chown)
+ .long SYMBOL_NAME(sys_lchown)
.long SYMBOL_NAME(sys_ni_syscall) /* old break syscall holder */
.long SYMBOL_NAME(sys_stat)
.long SYMBOL_NAME(sys_lseek)
@@ -535,7 +532,8 @@ ENTRY(sys_call_table)
.long SYMBOL_NAME(sys_rt_sigsuspend)
.long SYMBOL_NAME(sys_pread) /* 180 */
.long SYMBOL_NAME(sys_pwrite)
+ .long SYMBOL_NAME(sys_chown)
- .rept NR_syscalls-181
+ .rept NR_syscalls-182
.long SYMBOL_NAME(sys_ni_syscall)
.endr
diff --git a/arch/i386/kernel/head.S b/arch/i386/kernel/head.S
index a21ac6791..6722c4f7f 100644
--- a/arch/i386/kernel/head.S
+++ b/arch/i386/kernel/head.S
@@ -1,18 +1,16 @@
/*
- * linux/arch/i386/head.S
+ * linux/arch/i386/head.S -- the 32-bit startup code.
*
* Copyright (C) 1991, 1992 Linus Torvalds
- */
-
-/*
- * head.S contains the 32-bit startup code.
+ *
+ * Enhanced CPU detection and feature setting code by Mike Jagdis
+ * and Martin Mares, November 1997.
*/
.text
#include <linux/tasks.h>
#include <linux/linkage.h>
#include <asm/segment.h>
-#include <linux/config.h>
#define CL_MAGIC_ADDR 0x90020
#define CL_MAGIC 0xA33F
@@ -20,6 +18,20 @@
#define CL_OFFSET 0x90022
/*
+ * References to members of the boot_cpu_data structure.
+ */
+
+#define CPU_PARAMS SYMBOL_NAME(boot_cpu_data)
+#define X86 CPU_PARAMS+0
+#define X86_VENDOR CPU_PARAMS+1
+#define X86_MODEL CPU_PARAMS+2
+#define X86_MASK CPU_PARAMS+3
+#define X86_HARD_MATH CPU_PARAMS+6
+#define X86_CPUID CPU_PARAMS+8
+#define X86_CAPABILITY CPU_PARAMS+12
+#define X86_VENDOR_ID CPU_PARAMS+16
+
+/*
* swapper_pg_dir is the main page directory, address 0x00101000
*/
ENTRY(stext)
@@ -45,15 +57,9 @@ startup_32:
* not yet offset 0xC0000000..
*/
#define cr4_bits mmu_cr4_features-0xC0000000
-#ifdef GAS_KNOWS_CR4
movl %cr4,%eax # Turn on 4Mb pages
orl cr4_bits,%eax
movl %eax,%cr4
-#else
- .byte 0x0f,0x20,0xe0
- orl cr4_bits,%eax
- .byte 0x0f,0x22,0xe0
-#endif
#endif
/*
* Setup paging (the tables are already set up, just switch them on)
@@ -135,13 +141,69 @@ startup_32:
checkCPUtype:
#endif
+ movl $-1,X86_CPUID # -1 for no CPUID initially
+
/* check if it is 486 or 386. */
/*
* XXX - this does a lot of unnecessary setup. Alignment checks don't
* apply at our cpl of 0 and the stack ought to be aligned already, and
* we don't need to preserve eflags.
*/
- movl $3, SYMBOL_NAME(x86)
+ /*
+ * A Cyrix preserves flags in cases where other CPUs change
+ * them in undefined ways. We need to know this since we may
+ * need to enable the CPUID instruction at least. (Cyrix chips
+ * prior to M2 have CPUID disabled by default, the Cx486s
+ * didn't have it at all.)
+ */
+ xor %ax,%ax
+ sahf
+ movb $5,%al
+ movb $2,%bl
+ div %bl
+ lahf
+ cmpb $2,%ah
+ jne ncyrix
+
+ /*
+ * It behaves like a Cyrix so put "Cyrix" in the vendor id
+ * field. It may be overwritten later with the real thing
+ * if CPUID works.
+ */
+ movl $0x69727943,X86_VENDOR_ID # low 4 chars
+ movl $0x00000078,X86_VENDOR_ID+4 # next 4 chars
+
+ /*
+ * N.B. The pattern of accesses to 0x22 and 0x23 is *important*
+ * so do not try and "optimise" it! For the same reason we
+ * do all this with interrupts off just to be sure.
+ */
+#define setCx86(reg, val) \
+ movb reg,%al; \
+ outb %al,$0x22; \
+ movb val,%al; \
+ outb %al,$0x23
+
+#define getCx86(reg) \
+ movb reg,%al; \
+ outb %al,$0x22; \
+ inb $0x23,%al
+
+ getCx86($0xc3) # get CCR3
+ movb %al,%cl # Save old value
+ movb %al,%bl
+ andb $0x0f,%bl # Enable all config registers (for CCR4 access)
+ orb $0x10,%bl
+ setCx86($0xc3,%bl)
+
+ getCx86($0xe8) # CCR4 |= CPUID
+ orb $0x80,%al
+ movb %al,%bl
+ setCx86($0xe8,%bl)
+
+ setCx86($0xc3,%cl) # Restore old CCR3
+
+ncyrix: movl $3,X86 # at least 386
pushfl # push EFLAGS
popl %eax # get EFLAGS
movl %eax,%ecx # save original EFLAGS
@@ -153,7 +215,8 @@ checkCPUtype:
xorl %ecx,%eax # change in flags
andl $0x40000,%eax # check if AC bit changed
je is386
- movl $4,SYMBOL_NAME(x86)
+
+ movl $4,X86 # at least 486
movl %ecx,%eax
xorl $0x200000,%eax # check ID flag
pushl %eax
@@ -161,42 +224,74 @@ checkCPUtype:
pushfl # 487SX we can't change it
popl %eax
xorl %ecx,%eax
- andl $0x200000,%eax
- je is486
-isnew: pushl %ecx # restore original EFLAGS
+ pushl %ecx # restore original EFLAGS
popfl
- incl SYMBOL_NAME(have_cpuid) # we have CPUID
- /* get processor type */
- # LINUS WE HAVE A BUG HERE - MUST CHECK WITH
- # CPUID#0 THAT CPUID#1 IS SUPPORTED...
- movl $1, %eax # Use the CPUID instruction to
- .byte 0x0f, 0xa2 # check the processor type
- movb %al, %cl # save reg for future use
- andb $0x0f,%ah # mask processor family
- movb %ah,SYMBOL_NAME(x86)
- andb $0xf0, %eax # mask model
- shrb $4, %al
- movb %al,SYMBOL_NAME(x86_model)
- andb $0x0f, %cl # mask mask revision
- movb %cl,SYMBOL_NAME(x86_mask)
- movl %edx,SYMBOL_NAME(x86_capability)
+ andl $0x200000,%eax
+ je nocpuid
+
/* get vendor info */
- xorl %eax, %eax # call CPUID with 0 -> return vendor ID
- .byte 0x0f, 0xa2 # CPUID
- movl %ebx,SYMBOL_NAME(x86_vendor_id) # lo 4 chars
- movl %edx,SYMBOL_NAME(x86_vendor_id)+4 # next 4 chars
- movl %ecx,SYMBOL_NAME(x86_vendor_id)+8 # last 4 chars
+ xorl %eax,%eax # call CPUID with 0 -> return vendor ID
+ cpuid
+ movl %eax,X86_CPUID # save CPUID level
+ movl %ebx,X86_VENDOR_ID # lo 4 chars
+ movl %edx,X86_VENDOR_ID+4 # next 4 chars
+ movl %ecx,X86_VENDOR_ID+8 # last 4 chars
- movl %cr0,%eax # 486+
- andl $0x80000011,%eax # Save PG,PE,ET
- orl $0x50022,%eax # set AM, WP, NE and MP
- jmp 2f
-is486: pushl %ecx # restore original EFLAGS
- popfl
- movl %cr0,%eax # 486
+ orl %eax,%eax # do we have processor info as well?
+ je nocpuid
+
+ movl $1,%eax # Use the CPUID instruction to get CPU type
+ cpuid
+ movb %al,%cl # save reg for future use
+ andb $0x0f,%ah # mask processor family
+ movb %ah,X86
+ andb $0xf0,%al # mask model
+ shrb $4,%al
+ movb %al,X86_MODEL
+ andb $0x0f,%cl # mask mask revision
+ movb %cl,X86_MASK
+ movl %edx,X86_CAPABILITY
+
+nocpuid:
+ /*
+ * Even if we had CPUID Cyrix tries to look compatible with
+ * Intel so we have to go elsewhere for the nitty gritty.
+ */
+ cmpl $0x69727943,X86_VENDOR_ID # "Cyri[x.*]"?
+ jne is486 # maybe ...
+
+ movb $0xfe,X86_MODEL # Generic Cx486?
+ movb $0,X86_MASK
+
+ getCx86($0xc3) # Test for DEVID by writing CCR3
+ movb %al,%cl
+ movb %al,%bl
+ orb $0x80,%bl
+ setCx86($0xc3,%bl)
+ getCx86($0xc0) # dummy to change bus
+ getCx86($0xc3)
+ cmpb %al,%cl
+ je is486 # not writable == no DEVID
+
+ setCx86($0xc3,%cl) # restore CCR3
+
+ getCx86($0xff) # get DEVID in preference to any CPUID
+ movb %al,X86_MASK
+ getCx86($0xfe)
+ movb %al,X86_MODEL
+ andb $0xf0,%al # Check for 6x86(L)
+ cmp $0x30,%al
+ jnz is486
+ getCx86($0xe9) # CCR5: reset SLOP bit, so that the udelay loop
+ andb $0xfd,%al # works well on 6x86(L) CPU's.
+ movb %al,%bl
+ setCx86($0xe9,%bl)
+
+is486: movl %cr0,%eax # 486 or better
andl $0x80000011,%eax # Save PG,PE,ET
orl $0x50022,%eax # set AM, WP, NE and MP
jmp 2f
+
is386: pushl %ecx # restore original EFLAGS
popfl
movl %cr0,%eax # 386
@@ -208,17 +303,11 @@ is386: pushl %ecx # restore original EFLAGS
movb ready,%al # First CPU if 0
orb %al,%al
jz 4f # First CPU skip this stuff
-#ifdef GAS_KNOWS_CR4
movl %cr4,%eax # Turn on 4Mb pages
orl $16,%eax
movl %eax,%cr4
-#else
- .byte 0x0f,0x20,0xe0
- orl $16,%eax
- .byte 0x0f,0x22,0xe0
-#endif
- movl %cr3, %eax # Intel specification clarification says
- movl %eax, %cr3 # to do this. Maybe it makes a difference.
+ movl %cr3,%eax # Intel specification clarification says
+ movl %eax,%cr3 # to do this. Maybe it makes a difference.
# Who knows ?
#endif
4:
@@ -228,7 +317,7 @@ is386: pushl %ecx # restore original EFLAGS
lgdt gdt_descr
lidt idt_descr
ljmp $(__KERNEL_CS),$1f
-1: movl $(__KERNEL_DS),%eax# reload all the segment registers
+1: movl $(__KERNEL_DS),%eax # reload all the segment registers
mov %ax,%ds # after changing gdt.
mov %ax,%es
mov %ax,%fs
@@ -258,7 +347,7 @@ ready: .byte 0
* We depend on ET to be correct. This checks for 287/387.
*/
check_x87:
- movb $0,SYMBOL_NAME(hard_math)
+ movb $0,X86_HARD_MATH
clts
fninit
fstsw %ax
@@ -269,7 +358,7 @@ check_x87:
movl %eax,%cr0
ret
ALIGN
-1: movb $1,SYMBOL_NAME(hard_math)
+1: movb $1,X86_HARD_MATH
.byte 0xDB,0xE4 /* fsetpm for 287, ignored by 387 */
ret
@@ -333,11 +422,7 @@ ignore_int:
* of tasks we can have..
*/
#define IDT_ENTRIES 256
-#ifdef CONFIG_APM
-#define GDT_ENTRIES (11+2*NR_TASKS)
-#else
-#define GDT_ENTRIES (8+2*NR_TASKS)
-#endif
+#define GDT_ENTRIES (12+2*NR_TASKS)
.globl SYMBOL_NAME(idt)
@@ -366,7 +451,7 @@ ENTRY(swapper_pg_dir)
.long 0x00102007
.fill 767,4,0
.long 0x00102007
- .fill 127,4,0
+ .fill 255,4,0
/*
* The page tables are initialized to only 4MB here - the final page
@@ -547,9 +632,17 @@ ENTRY(gdt_table)
.quad 0x00cff2000000ffff /* 0x2b user 4GB data at 0x00000000 */
.quad 0x0000000000000000 /* not used */
.quad 0x0000000000000000 /* not used */
+ .quad 0x00c0920000000000 /* 0x40 APM set up for bad BIOS's */
+ .quad 0x00c09a0000000000 /* 0x48 APM CS code */
+ .quad 0x00809a0000000000 /* 0x50 APM CS 16 code (16 bit) */
+ .quad 0x00c0920000000000 /* 0x58 APM DS data */
.fill 2*NR_TASKS,8,0 /* space for LDT's and TSS's etc */
-#ifdef CONFIG_APM
- .quad 0x00c09a0000000000 /* APM CS code */
- .quad 0x00809a0000000000 /* APM CS 16 code (16 bit) */
- .quad 0x00c0920000000000 /* APM DS data */
-#endif
+
+/*
+ * This is to aid debugging, the various locking macros will be putting
+ * code fragments here. When an oops occurs we'd rather know that it's
+ * inside the .text.lock section rather than as some offset from whatever
+ * function happens to be last in the .text segment.
+ */
+.section .text.lock
+ENTRY(stext_lock)
diff --git a/arch/i386/kernel/i386_ksyms.c b/arch/i386/kernel/i386_ksyms.c
index e71177c3c..e5812400e 100644
--- a/arch/i386/kernel/i386_ksyms.c
+++ b/arch/i386/kernel/i386_ksyms.c
@@ -27,20 +27,19 @@ EXPORT_SYMBOL(drive_info);
#endif
/* platform dependent support */
-EXPORT_SYMBOL(x86);
+EXPORT_SYMBOL(boot_cpu_data);
EXPORT_SYMBOL(EISA_bus);
EXPORT_SYMBOL(MCA_bus);
-EXPORT_SYMBOL(wp_works_ok);
EXPORT_SYMBOL(__verify_write);
EXPORT_SYMBOL(dump_thread);
EXPORT_SYMBOL(dump_fpu);
EXPORT_SYMBOL(__ioremap);
EXPORT_SYMBOL(iounmap);
+EXPORT_SYMBOL(local_bh_count);
EXPORT_SYMBOL(local_irq_count);
EXPORT_SYMBOL_NOVERS(__down_failed);
EXPORT_SYMBOL_NOVERS(__down_failed_interruptible);
EXPORT_SYMBOL_NOVERS(__up_wakeup);
-EXPORT_SYMBOL(__intel_bh_counter);
/* Networking helper routines. */
EXPORT_SYMBOL(csum_partial_copy);
/* Delay loops */
@@ -48,6 +47,20 @@ EXPORT_SYMBOL(__udelay);
EXPORT_SYMBOL(__delay);
EXPORT_SYMBOL(__const_udelay);
+EXPORT_SYMBOL_NOVERS(__get_user_1);
+EXPORT_SYMBOL_NOVERS(__get_user_2);
+EXPORT_SYMBOL_NOVERS(__get_user_4);
+EXPORT_SYMBOL_NOVERS(__put_user_1);
+EXPORT_SYMBOL_NOVERS(__put_user_2);
+EXPORT_SYMBOL_NOVERS(__put_user_4);
+
+EXPORT_SYMBOL(strncpy_from_user);
+EXPORT_SYMBOL(__strncpy_from_user);
+EXPORT_SYMBOL(clear_user);
+EXPORT_SYMBOL(__clear_user);
+EXPORT_SYMBOL(__generic_copy_from_user);
+EXPORT_SYMBOL(__generic_copy_to_user);
+EXPORT_SYMBOL(strlen_user);
#ifdef __SMP__
EXPORT_SYMBOL(apic_reg); /* Needed internally for the I386 inlines */
@@ -60,6 +73,9 @@ EXPORT_SYMBOL(lk_lockmsg);
/* Global SMP irq stuff */
EXPORT_SYMBOL(synchronize_irq);
+EXPORT_SYMBOL(synchronize_bh);
+EXPORT_SYMBOL(global_bh_count);
+EXPORT_SYMBOL(global_bh_lock);
EXPORT_SYMBOL(global_irq_holder);
EXPORT_SYMBOL(__global_cli);
EXPORT_SYMBOL(__global_sti);
@@ -80,3 +96,4 @@ EXPORT_SYMBOL(mca_set_adapter_procfn);
EXPORT_SYMBOL(mca_isenabled);
EXPORT_SYMBOL(mca_isadapter);
#endif
+
diff --git a/arch/i386/kernel/io_apic.c b/arch/i386/kernel/io_apic.c
new file mode 100644
index 000000000..be074cac6
--- /dev/null
+++ b/arch/i386/kernel/io_apic.c
@@ -0,0 +1,644 @@
+/*
+ * Intel IO-APIC support for multi-pentium hosts.
+ *
+ * (c) 1997 Ingo Molnar, Hajnalka Szabo
+ *
+ * Many thanks to Stig Venaas for trying out countless experimental
+ * patches and reporting/debugging problems patiently!
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/kernel_stat.h>
+#include <linux/delay.h>
+#include <linux/mc146818rtc.h>
+#include <asm/i82489.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <asm/pgtable.h>
+#include <asm/bitops.h>
+#include <asm/pgtable.h>
+#include <asm/smp.h>
+#include <asm/io.h>
+
+#include "irq.h"
+
+#define IO_APIC_BASE 0xfec00000
+
+/*
+ * volatile is justified in this case, it might change
+ * spontaneously, GCC should not cache it
+ */
+volatile unsigned int * io_apic_reg = NULL;
+
+/*
+ * The structure of the IO-APIC:
+ */
+struct IO_APIC_reg_00 {
+ __u32 __reserved_2 : 24,
+ ID : 4,
+ __reserved_1 : 4;
+} __attribute__ ((packed));
+
+struct IO_APIC_reg_01 {
+ __u32 version : 8,
+ __reserved_2 : 8,
+ entries : 8,
+ __reserved_1 : 8;
+} __attribute__ ((packed));
+
+struct IO_APIC_reg_02 {
+ __u32 __reserved_2 : 24,
+ arbitration : 4,
+ __reserved_1 : 4;
+} __attribute__ ((packed));
+
+struct IO_APIC_route_entry {
+ __u32 vector : 8,
+ delivery_mode : 3, /* 000: FIXED
+ * 001: lowest prio
+ * 111: ExtInt
+ */
+ dest_mode : 1, /* 0: physical, 1: logical */
+ delivery_status : 1,
+ polarity : 1,
+ irr : 1,
+ trigger : 1, /* 0: edge, 1: level */
+ mask : 1, /* 0: enabled, 1: disabled */
+ __reserved_2 : 15;
+
+ union { struct { __u32
+ __reserved_1 : 24,
+ physical_dest : 4,
+ __reserved_2 : 4;
+ } physical;
+
+ struct { __u32
+ __reserved_1 : 24,
+ logical_dest : 8;
+ } logical;
+ } dest;
+
+} __attribute__ ((packed));
+
+#define UNEXPECTED_IO_APIC() \
+ { \
+ printk(" WARNING: unexpected IO-APIC, please mail\n"); \
+ printk(" to linux-smp@vger.rutgers.edu\n"); \
+ }
+
+int nr_ioapic_registers = 0; /* # of IRQ routing registers */
+int mp_irq_entries = 0; /* # of MP IRQ source entries */
+struct mpc_config_intsrc mp_irqs[MAX_IRQ_SOURCES];
+ /* MP IRQ source entries */
+
+unsigned int io_apic_read (unsigned int reg)
+{
+ *io_apic_reg = reg;
+ return *(io_apic_reg+4);
+}
+
+void io_apic_write (unsigned int reg, unsigned int value)
+{
+ *io_apic_reg = reg;
+ *(io_apic_reg+4) = value;
+}
+
+void enable_IO_APIC_irq (unsigned int irq)
+{
+ struct IO_APIC_route_entry entry;
+
+ /*
+ * Enable it in the IO-APIC irq-routing table:
+ */
+ *(((int *)&entry)+0) = io_apic_read(0x10+irq*2);
+ entry.mask = 0;
+ io_apic_write(0x10+2*irq, *(((int *)&entry)+0));
+}
+
+/*
+ * this function is just here to make things complete, otherwise it's
+ * unused
+ */
+void disable_IO_APIC_irq (unsigned int irq)
+{
+ struct IO_APIC_route_entry entry;
+
+ /*
+ * Disable it in the IO-APIC irq-routing table:
+ */
+ *(((int *)&entry)+0) = io_apic_read(0x10+irq*2);
+ entry.mask = 1;
+ io_apic_write(0x10+2*irq, *(((int *)&entry)+0));
+}
+
+void clear_IO_APIC_irq (unsigned int irq)
+{
+ struct IO_APIC_route_entry entry;
+
+ /*
+ * Disable it in the IO-APIC irq-routing table:
+ */
+ memset(&entry, 0, sizeof(entry));
+ entry.mask = 1;
+ io_apic_write(0x10+2*irq, *(((int *)&entry)+0));
+ io_apic_write(0x11+2*irq, *(((int *)&entry)+1));
+}
+
+/*
+ * support for broken MP BIOSes, enables hand-redirection of PIRQ0-3 to
+ * specific CPU-side IRQs.
+ */
+
+#define MAX_PIRQS 8
+int pirq_entries [MAX_PIRQS];
+int pirqs_enabled;
+
+void ioapic_pirq_setup(char *str, int *ints)
+{
+ int i, max;
+
+ for (i=0; i<MAX_PIRQS; i++)
+ pirq_entries[i]=-1;
+
+ if (!ints) {
+ pirqs_enabled=0;
+ printk("PIRQ redirection SETUP, trusting MP-BIOS.\n");
+
+ } else {
+ pirqs_enabled=1;
+ printk("PIRQ redirection SETUP, working around broken MP-BIOS.\n");
+ max = MAX_PIRQS;
+ if (ints[0] < MAX_PIRQS)
+ max = ints[0];
+
+ for (i=0; i < max; i++) {
+ printk("... PIRQ%d -> IRQ %d\n", i, ints[i+1]);
+ /*
+ * PIRQs are mapped upside down, usually.
+ */
+ pirq_entries[MAX_PIRQS-i-1]=ints[i+1];
+ }
+ }
+}
+
+int find_irq_entry(int pin)
+{
+ int i;
+
+ for (i=0; i<mp_irq_entries; i++)
+ if ( (mp_irqs[i].mpc_irqtype == 0x00) &&
+ (mp_irqs[i].mpc_dstirq == pin))
+
+ return i;
+
+ return -1;
+}
+
+void setup_IO_APIC_irqs (void)
+{
+ struct IO_APIC_route_entry entry;
+ int i, idx, bus, irq, first_notcon=1;
+
+ printk("init IO_APIC IRQs\n");
+
+ for (i=0; i<nr_ioapic_registers; i++) {
+
+ /*
+ * add it to the IO-APIC irq-routing table:
+ */
+ memset(&entry,0,sizeof(entry));
+
+ entry.delivery_mode = 1; /* lowest prio */
+ entry.dest_mode = 1; /* logical delivery */
+ entry.mask = 0; /* enable IRQ */
+ entry.dest.logical.logical_dest = 0xff; /* all CPUs */
+
+ idx = find_irq_entry(i);
+ if (idx == -1) {
+ if (first_notcon) {
+ printk(" IO-APIC pin %d", i);
+ first_notcon=0;
+ } else
+ printk(", %d", i);
+ continue;
+ }
+ bus = mp_irqs[idx].mpc_srcbus;
+
+ switch (mp_bus_id_to_type[bus])
+ {
+ case MP_BUS_ISA: /* ISA pin */
+ {
+ irq = mp_irqs[idx].mpc_srcbusirq;
+ break;
+ }
+ case MP_BUS_PCI: /* PCI pin */
+ {
+ /*
+ * PCI IRQs are 'directly mapped'
+ */
+ irq = i;
+ break;
+ }
+ default:
+ {
+ printk("unknown bus type %d.\n",bus);
+ irq = 0;
+ break;
+ }
+ }
+
+ /*
+ * PCI IRQ redirection. Yes, limits are hardcoded.
+ */
+ if ((i>=16) && (i<=19)) {
+ if (pirq_entries[i-16] != -1) {
+ if (!pirq_entries[i-16]) {
+ printk("disabling PIRQ%d\n", i-16);
+ } else {
+ irq = pirq_entries[i-16];
+ printk("using PIRQ%d -> IRQ %d\n",
+ i-16, irq);
+ }
+ }
+ }
+
+ if (!IO_APIC_IRQ(irq))
+ continue;
+
+ entry.vector = IO_APIC_GATE_OFFSET + (irq<<3);
+
+ /*
+ * Determine IRQ line polarity (high active or low active):
+ */
+ switch (mp_irqs[idx].mpc_irqflag & 3)
+ {
+ case 0: /* conforms, ie. bus-type dependent polarity */
+ {
+ switch (mp_bus_id_to_type[bus])
+ {
+ case MP_BUS_ISA: /* ISA pin */
+ {
+ entry.polarity = 0;
+ break;
+ }
+ case MP_BUS_PCI: /* PCI pin */
+ {
+ entry.polarity = 1;
+ break;
+ }
+ default:
+ {
+ printk("broken BIOS!!\n");
+ break;
+ }
+ }
+ break;
+ }
+ case 1: /* high active */
+ {
+ entry.polarity = 0;
+ break;
+ }
+ case 2: /* reserved */
+ {
+ printk("broken BIOS!!\n");
+ break;
+ }
+ case 3: /* low active */
+ {
+ entry.polarity = 1;
+ break;
+ }
+ }
+
+ /*
+ * Determine IRQ trigger mode (edge or level sensitive):
+ */
+ switch ((mp_irqs[idx].mpc_irqflag>>2) & 3)
+ {
+ case 0: /* conforms, ie. bus-type dependent */
+ {
+ switch (mp_bus_id_to_type[bus])
+ {
+ case MP_BUS_ISA: /* ISA pin, edge */
+ {
+ entry.trigger = 0;
+ break;
+ }
+ case MP_BUS_PCI: /* PCI pin, level */
+ {
+ entry.trigger = 1;
+ break;
+ }
+ default:
+ {
+ printk("broken BIOS!!\n");
+ break;
+ }
+ }
+ break;
+ }
+ case 1: /* edge */
+ {
+ entry.trigger = 0;
+ break;
+ }
+ case 2: /* reserved */
+ {
+ printk("broken BIOS!!\n");
+ break;
+ }
+ case 3: /* level */
+ {
+ entry.trigger = 1;
+ break;
+ }
+ }
+
+ io_apic_write(0x10+2*i, *(((int *)&entry)+0));
+ io_apic_write(0x11+2*i, *(((int *)&entry)+1));
+ }
+
+ if (!first_notcon)
+ printk(" not connected.\n");
+}
+
+void setup_IO_APIC_irq_ISA_default (unsigned int irq)
+{
+ struct IO_APIC_route_entry entry;
+
+ /*
+ * add it to the IO-APIC irq-routing table:
+ */
+ memset(&entry,0,sizeof(entry));
+
+ entry.delivery_mode = 1; /* lowest prio */
+ entry.dest_mode = 1; /* logical delivery */
+ entry.mask = 1; /* unmask IRQ now */
+ entry.dest.logical.logical_dest = 0xff; /* all CPUs */
+
+ entry.vector = IO_APIC_GATE_OFFSET + (irq<<3);
+
+ entry.polarity=0;
+ entry.trigger=0;
+
+ io_apic_write(0x10+2*irq, *(((int *)&entry)+0));
+ io_apic_write(0x11+2*irq, *(((int *)&entry)+1));
+}
+
+int IO_APIC_get_PCI_irq_vector (int bus, int slot, int pci_pin)
+{
+ int i;
+
+ for (i=0; i<mp_irq_entries; i++) {
+ int lbus = mp_irqs[i].mpc_srcbus;
+
+ if (IO_APIC_IRQ(mp_irqs[i].mpc_dstirq) &&
+ (mp_bus_id_to_type[lbus] == MP_BUS_PCI) &&
+ !mp_irqs[i].mpc_irqtype &&
+ (bus == mp_bus_id_to_pci_bus[mp_irqs[i].mpc_srcbus]) &&
+ (slot == ((mp_irqs[i].mpc_srcbusirq >> 2) & 0x1f)) &&
+ (pci_pin == (mp_irqs[i].mpc_srcbusirq & 3)))
+
+ return mp_irqs[i].mpc_dstirq;
+ }
+ return -1;
+}
+
+/*
+ * There is a nasty bug in some older SMP boards, their mptable lies
+ * about the timer IRQ. We do the following to work around the situation:
+ *
+ * - timer IRQ defaults to IO-APIC IRQ
+ * - if this function detects that timer IRQs are defunct, then we fall
+ * back to ISA timer IRQs
+ */
+static int timer_irq_works (void)
+{
+ unsigned int t1=jiffies;
+ unsigned long flags;
+
+ save_flags(flags);
+ sti();
+
+ udelay(100*1000);
+
+ if (jiffies-t1>1)
+ return 1;
+
+ return 0;
+}
+
+void print_IO_APIC (void)
+{
+ int i;
+ struct IO_APIC_reg_00 reg_00;
+ struct IO_APIC_reg_01 reg_01;
+ struct IO_APIC_reg_02 reg_02;
+
+ *(int *)&reg_00 = io_apic_read(0);
+ *(int *)&reg_01 = io_apic_read(1);
+ *(int *)&reg_02 = io_apic_read(2);
+
+ /*
+ * We are a bit conservative about what we expect, we have to
+ * know about every HW change ASAP ...
+ */
+ printk("testing the IO APIC.......................\n");
+
+ printk(".... register #00: %08X\n", *(int *)&reg_00);
+ printk("....... : physical APIC id: %02X\n", reg_00.ID);
+ if (reg_00.__reserved_1 || reg_00.__reserved_2)
+ UNEXPECTED_IO_APIC();
+
+ printk(".... register #01: %08X\n", *(int *)&reg_01);
+ printk("....... : max redirection entries: %04X\n", reg_01.entries);
+ if ( (reg_01.entries != 0x0f) && /* ISA-only Neptune boards */
+ (reg_01.entries != 0x17) /* ISA+PCI boards */
+ )
+ UNEXPECTED_IO_APIC();
+ if (reg_01.entries == 0x0f)
+ printk("....... [IO-APIC cannot route PCI PIRQ 0-3]\n");
+
+ printk("....... : IO APIC version: %04X\n", reg_01.version);
+ if ( (reg_01.version != 0x10) && /* oldest IO-APICs */
+ (reg_01.version != 0x11) /* my IO-APIC */
+ )
+ UNEXPECTED_IO_APIC();
+ if (reg_01.__reserved_1 || reg_01.__reserved_2)
+ UNEXPECTED_IO_APIC();
+
+ printk(".... register #02: %08X\n", *(int *)&reg_02);
+ printk("....... : arbitration: %02X\n", reg_02.arbitration);
+ if (reg_02.__reserved_1 || reg_02.__reserved_2)
+ UNEXPECTED_IO_APIC();
+
+ printk(".... IRQ redirection table:\n");
+
+ printk(" NR Log Phy ");
+ printk("Mask Trig IRR Pol Stat Dest Deli Vect: \n");
+
+ for (i=0; i<=reg_01.entries; i++) {
+ struct IO_APIC_route_entry entry;
+
+ *(((int *)&entry)+0) = io_apic_read(0x10+i*2);
+ *(((int *)&entry)+1) = io_apic_read(0x11+i*2);
+
+ printk(" %02x %03X %02X ",
+ i,
+ entry.dest.logical.logical_dest,
+ entry.dest.physical.physical_dest
+ );
+
+ printk("%1d %1d %1d %1d %1d %1d %1d %02X\n",
+ entry.mask,
+ entry.trigger,
+ entry.irr,
+ entry.polarity,
+ entry.delivery_status,
+ entry.dest_mode,
+ entry.delivery_mode,
+ entry.vector
+ );
+ }
+
+ printk(".................................... done.\n");
+
+ return;
+}
+
+void init_sym_mode (void)
+{
+ printk("enabling Symmetric IO mode ... ");
+ outb (0x70, 0x22);
+ outb (0x01, 0x23);
+ printk("...done.\n");
+}
+
+char ioapic_OEM_ID [16];
+char ioapic_Product_ID [16];
+
+struct ioapic_list_entry {
+ char * oem_id;
+ char * product_id;
+};
+
+struct ioapic_list_entry ioapic_whitelist [] = {
+
+ { "INTEL " , "PR440FX " },
+ { "INTEL " , "82440FX " },
+ { "AIR " , "KDI " },
+ { 0 , 0 }
+};
+
+struct ioapic_list_entry ioapic_blacklist [] = {
+
+ { "OEM00000" , "PROD00000000" },
+ { 0 , 0 }
+};
+
+
+static int in_ioapic_list (struct ioapic_list_entry * table)
+{
+ for (;table->oem_id; table++)
+ if ((!strcmp(table->oem_id,ioapic_OEM_ID)) &&
+ (!strcmp(table->product_id,ioapic_Product_ID)))
+ return 1;
+ return 0;
+}
+
+static int ioapic_whitelisted (void)
+{
+/*
+ * Right now, whitelist everything to see whether the new parsing
+ * routines really do work for everybody..
+ */
+#if 1
+ return 1;
+#else
+ return in_ioapic_list(ioapic_whitelist);
+#endif
+}
+
+static int ioapic_blacklisted (void)
+{
+ return in_ioapic_list(ioapic_blacklist);
+}
+
+
+void setup_IO_APIC (void)
+{
+ int i;
+ /*
+ * Map the IO APIC into kernel space
+ */
+
+ printk("mapping IO APIC from standard address.\n");
+ io_apic_reg = ioremap_nocache(IO_APIC_BASE,4096);
+ printk("new virtual address: %p.\n",io_apic_reg);
+
+ init_sym_mode();
+ {
+ struct IO_APIC_reg_01 reg_01;
+
+ *(int *)&reg_01 = io_apic_read(1);
+ nr_ioapic_registers = reg_01.entries+1;
+ }
+
+ /*
+ * do not trust the IO-APIC being empty at bootup
+ */
+ for (i=0; i<nr_ioapic_registers; i++)
+ clear_IO_APIC_irq (i);
+
+#if DEBUG_1
+ for (i=0; i<16; i++)
+ if (IO_APIC_IRQ(i))
+ setup_IO_APIC_irq_ISA_default (i);
+#endif
+
+ /*
+ * the following IO-APIC's can be enabled:
+ *
+ * - whitelisted ones
+ * - those which have no PCI pins connected
+ * - those for which the user has specified a pirq= parameter
+ */
+ if ( ioapic_whitelisted() ||
+ (nr_ioapic_registers == 16) ||
+ pirqs_enabled)
+ {
+ printk("ENABLING IO-APIC IRQs\n");
+ io_apic_irqs = ~((1<<2)|(1<<13));
+ } else {
+ if (ioapic_blacklisted())
+ printk(" blacklisted board, DISABLING IO-APIC IRQs\n");
+ else
+ printk(" unlisted board, DISABLING IO-APIC IRQs\n");
+
+ printk(" see Documentation/IO-APIC.txt to enable them\n");
+ io_apic_irqs = 0;
+ }
+
+ init_IO_APIC_traps();
+ setup_IO_APIC_irqs ();
+
+ if (!timer_irq_works ()) {
+ make_8259A_irq(0);
+ if (!timer_irq_works ())
+ panic("IO-APIC + timer doesnt work!");
+ printk("..MP-BIOS bug: i8254 timer not connected to IO-APIC\n");
+ printk("..falling back to 8259A-based timer interrupt\n");
+ }
+
+ printk("nr of MP irq sources: %d.\n", mp_irq_entries);
+ printk("nr of IOAPIC registers: %d.\n", nr_ioapic_registers);
+ print_IO_APIC();
+}
+
diff --git a/arch/i386/kernel/irq.c b/arch/i386/kernel/irq.c
index 62d074508..24c33be65 100644
--- a/arch/i386/kernel/irq.c
+++ b/arch/i386/kernel/irq.c
@@ -1,7 +1,7 @@
/*
* linux/arch/i386/kernel/irq.c
*
- * Copyright (C) 1992 Linus Torvalds
+ * Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar
*
* This file contains the code used by various IRQ handling routines:
* asking for different IRQ's should be done through these routines
@@ -26,6 +26,7 @@
#include <linux/malloc.h>
#include <linux/random.h>
#include <linux/smp.h>
+#include <linux/tasks.h>
#include <linux/smp_lock.h>
#include <linux/init.h>
@@ -35,150 +36,231 @@
#include <asm/bitops.h>
#include <asm/smp.h>
#include <asm/pgtable.h>
+#include <asm/delay.h>
#include "irq.h"
-#ifdef __SMP_PROF__
-extern volatile unsigned long smp_local_timer_ticks[1+NR_CPUS];
-#endif
-
+unsigned int local_bh_count[NR_CPUS];
unsigned int local_irq_count[NR_CPUS];
-#ifdef __SMP__
-atomic_t __intel_bh_counter;
-#else
-int __intel_bh_counter;
-#endif
-
-#ifdef __SMP_PROF__
-static unsigned int int_count[NR_CPUS][NR_IRQS] = {{0},};
-#endif
atomic_t nmi_counter;
/*
- * This contains the irq mask for both irq controllers
+ * About the IO-APIC, the architecture is 'merged' into our
+ * current irq architecture, seemlessly. (i hope). It is only
+ * visible through 8 more hardware interrupt lines, but otherwise
+ * drivers are unaffected. The main code is believed to be
+ * NR_IRQS-safe (nothing anymore thinks we have 16
+ * irq lines only), but there might be some places left ...
+ */
+
+/*
+ * This contains the irq mask for both 8259A irq controllers,
+ * and on SMP the extended IO-APIC IRQs 16-23. The IO-APIC
+ * uses this mask too, in probe_irq*().
+ *
+ * (0x0000ffff for NR_IRQS==16, 0x00ffffff for NR_IRQS=24)
*/
-static unsigned int cached_irq_mask = 0xffff;
+static unsigned int cached_irq_mask = (1<<NR_IRQS)-1;
-#define cached_21 (((char *)(&cached_irq_mask))[0])
-#define cached_A1 (((char *)(&cached_irq_mask))[1])
+#define cached_21 ((cached_irq_mask | io_apic_irqs) & 0xff)
+#define cached_A1 (((cached_irq_mask | io_apic_irqs) >> 8) & 0xff)
spinlock_t irq_controller_lock;
+static unsigned int irq_events [NR_IRQS] = { -1, };
+static int disabled_irq [NR_IRQS] = { 0, };
+
/*
- * This is always called from an interrupt context
- * with local interrupts disabled. Don't worry about
- * irq-safe locks.
+ * Not all IRQs can be routed through the IO-APIC, eg. on certain (older)
+ * boards the timer interrupt and sometimes the keyboard interrupt is
+ * not connected to any IO-APIC pin, it's fed to the CPU ExtInt IRQ line
+ * directly.
*
- * Note that we always ack the primary irq controller,
- * even if the interrupt came from the secondary, as
- * the primary will still have routed it. Oh, the joys
- * of PC hardware.
+ * Any '1' bit in this mask means the IRQ is routed through the IO-APIC.
+ * this 'mixed mode' IRQ handling costs us one more branch in do_IRQ,
+ * but we have _much_ higher compatibility and robustness this way.
*/
-static inline void mask_and_ack_irq(int irq_nr)
+
+/*
+ * Default to all normal IRQ's _not_ using the IO APIC.
+ *
+ * To get IO-APIC interrupts you should either:
+ * - turn some of them into IO-APIC interrupts at runtime
+ * with some magic system call interface.
+ * - explicitly use irq 16-19 depending on which PCI irq
+ * line your PCI controller uses.
+ */
+unsigned int io_apic_irqs = 0;
+
+struct hw_interrupt_type {
+ void (*handle)(unsigned int irq, int cpu, struct pt_regs * regs);
+ void (*enable)(unsigned int irq);
+ void (*disable)(unsigned int irq);
+};
+
+
+static void do_8259A_IRQ (unsigned int irq, int cpu, struct pt_regs * regs);
+static void enable_8259A_irq (unsigned int irq);
+static void disable_8259A_irq (unsigned int irq);
+
+static struct hw_interrupt_type i8259A_irq_type = {
+ do_8259A_IRQ,
+ enable_8259A_irq,
+ disable_8259A_irq
+};
+
+
+#ifdef __SMP__
+static void do_ioapic_IRQ (unsigned int irq, int cpu, struct pt_regs * regs);
+static void enable_ioapic_irq (unsigned int irq);
+static void disable_ioapic_irq (unsigned int irq);
+
+static struct hw_interrupt_type ioapic_irq_type = {
+ do_ioapic_IRQ,
+ enable_ioapic_irq,
+ disable_ioapic_irq
+};
+#endif
+
+struct hw_interrupt_type *irq_handles[NR_IRQS] =
{
- spin_lock(&irq_controller_lock);
- cached_irq_mask |= 1 << irq_nr;
- if (irq_nr & 8) {
- inb(0xA1); /* DUMMY */
+ [0 ... 15] = &i8259A_irq_type /* standard ISA IRQs */
+#ifdef __SMP__
+ , [16 ... NR_IRQS-1] = &ioapic_irq_type /* 'high' PCI IRQs */
+#endif
+};
+
+
+/*
+ * These have to be protected by the irq controller spinlock
+ * before being called.
+ */
+
+static inline void mask_8259A(unsigned int irq)
+{
+ cached_irq_mask |= 1 << irq;
+ if (irq & 8) {
outb(cached_A1,0xA1);
- outb(0x62,0x20); /* Specific EOI to cascade */
- outb(0x20,0xA0);
} else {
- inb(0x21); /* DUMMY */
outb(cached_21,0x21);
- outb(0x20,0x20);
}
- spin_unlock(&irq_controller_lock);
}
-static inline void set_irq_mask(int irq_nr)
+static inline void unmask_8259A(unsigned int irq)
{
- if (irq_nr & 8) {
+ cached_irq_mask &= ~(1 << irq);
+ if (irq & 8) {
outb(cached_A1,0xA1);
} else {
outb(cached_21,0x21);
}
}
-/*
- * These have to be protected by the spinlock
- * before being called.
- */
-static inline void mask_irq(unsigned int irq_nr)
+void set_8259A_irq_mask(unsigned int irq)
{
- cached_irq_mask |= 1 << irq_nr;
- set_irq_mask(irq_nr);
+ /*
+ * (it might happen that we see IRQ>15 on a UP box, with SMP
+ * emulation)
+ */
+ if (irq < 16) {
+ if (irq & 8) {
+ outb(cached_A1,0xA1);
+ } else {
+ outb(cached_21,0x21);
+ }
+ }
}
-static inline void unmask_irq(unsigned int irq_nr)
+void unmask_generic_irq(unsigned int irq)
{
- cached_irq_mask &= ~(1 << irq_nr);
- set_irq_mask(irq_nr);
+ if (IO_APIC_IRQ(irq))
+ enable_IO_APIC_irq(irq);
+ else {
+ cached_irq_mask &= ~(1 << irq);
+ set_8259A_irq_mask(irq);
+ }
}
-void disable_irq(unsigned int irq_nr)
-{
- unsigned long flags;
+/*
+ * This builds up the IRQ handler stubs using some ugly macros in irq.h
+ *
+ * These macros create the low-level assembly IRQ routines that save
+ * register context and call do_IRQ(). do_IRQ() then does all the
+ * operations that are needed to keep the AT (or SMP IOAPIC)
+ * interrupt-controller happy.
+ */
- spin_lock_irqsave(&irq_controller_lock, flags);
- mask_irq(irq_nr);
- spin_unlock_irqrestore(&irq_controller_lock, flags);
- synchronize_irq();
-}
-void enable_irq(unsigned int irq_nr)
-{
- unsigned long flags;
+BUILD_COMMON_IRQ()
+/*
+ * ISA PIC or IO-APIC triggered (INTA-cycle or APIC) interrupts:
+ */
+BUILD_IRQ(0) BUILD_IRQ(1) BUILD_IRQ(2) BUILD_IRQ(3)
+BUILD_IRQ(4) BUILD_IRQ(5) BUILD_IRQ(6) BUILD_IRQ(7)
+BUILD_IRQ(8) BUILD_IRQ(9) BUILD_IRQ(10) BUILD_IRQ(11)
+BUILD_IRQ(12) BUILD_IRQ(13) BUILD_IRQ(14) BUILD_IRQ(15)
- spin_lock_irqsave(&irq_controller_lock, flags);
- unmask_irq(irq_nr);
- spin_unlock_irqrestore(&irq_controller_lock, flags);
-}
+#ifdef __SMP__
/*
- * This builds up the IRQ handler stubs using some ugly macros in irq.h
+ * The IO-APIC (persent only in SMP boards) has 8 more hardware
+ * interrupt pins, for all of them we define an IRQ vector:
*
- * These macros create the low-level assembly IRQ routines that do all
- * the operations that are needed to keep the AT interrupt-controller
- * happy. They are also written to be fast - and to disable interrupts
- * as little as humanly possible.
+ * raw PCI interrupts 0-3, basically these are the ones used
+ * heavily:
*/
+BUILD_IRQ(16) BUILD_IRQ(17) BUILD_IRQ(18) BUILD_IRQ(19)
-#if NR_IRQS != 16
-#error make irq stub building NR_IRQS dependent and remove me.
-#endif
+/*
+ * [FIXME: anyone with 2 separate PCI buses and 2 IO-APICs, please
+ * speak up if problems and request experimental patches.
+ * --mingo ]
+ */
-BUILD_COMMON_IRQ()
-BUILD_IRQ(FIRST,0,0x01)
-BUILD_IRQ(FIRST,1,0x02)
-BUILD_IRQ(FIRST,2,0x04)
-BUILD_IRQ(FIRST,3,0x08)
-BUILD_IRQ(FIRST,4,0x10)
-BUILD_IRQ(FIRST,5,0x20)
-BUILD_IRQ(FIRST,6,0x40)
-BUILD_IRQ(FIRST,7,0x80)
-BUILD_IRQ(SECOND,8,0x01)
-BUILD_IRQ(SECOND,9,0x02)
-BUILD_IRQ(SECOND,10,0x04)
-BUILD_IRQ(SECOND,11,0x08)
-BUILD_IRQ(SECOND,12,0x10)
-BUILD_IRQ(SECOND,13,0x20)
-BUILD_IRQ(SECOND,14,0x40)
-BUILD_IRQ(SECOND,15,0x80)
+/*
+ * MIRQ (motherboard IRQ) interrupts 0-1:
+ */
+BUILD_IRQ(20) BUILD_IRQ(21)
-#ifdef __SMP__
+/*
+ * 'nondefined general purpose interrupt'.
+ */
+BUILD_IRQ(22)
+/*
+ * optionally rerouted SMI interrupt:
+ */
+BUILD_IRQ(23)
+
+/*
+ * The following vectors are part of the Linux architecture, there
+ * is no hardware IRQ pin equivalent for them, they are triggered
+ * through the ICC by us (IPIs), via smp_message_pass():
+ */
BUILD_SMP_INTERRUPT(reschedule_interrupt)
BUILD_SMP_INTERRUPT(invalidate_interrupt)
BUILD_SMP_INTERRUPT(stop_cpu_interrupt)
+
+/*
+ * every pentium local APIC has two 'local interrupts', with a
+ * soft-definable vector attached to both interrupts, one of
+ * which is a timer interrupt, the other one is error counter
+ * overflow. Linux uses the local APIC timer interrupt to get
+ * a much simpler SMP time architecture:
+ */
BUILD_SMP_TIMER_INTERRUPT(apic_timer_interrupt)
+
#endif
-static void (*interrupt[17])(void) = {
+static void (*interrupt[NR_IRQS])(void) = {
IRQ0_interrupt, IRQ1_interrupt, IRQ2_interrupt, IRQ3_interrupt,
IRQ4_interrupt, IRQ5_interrupt, IRQ6_interrupt, IRQ7_interrupt,
IRQ8_interrupt, IRQ9_interrupt, IRQ10_interrupt, IRQ11_interrupt,
- IRQ12_interrupt, IRQ13_interrupt, IRQ14_interrupt, IRQ15_interrupt
+ IRQ12_interrupt, IRQ13_interrupt, IRQ14_interrupt, IRQ15_interrupt
+#ifdef __SMP__
+ ,IRQ16_interrupt, IRQ17_interrupt, IRQ18_interrupt, IRQ19_interrupt,
+ IRQ20_interrupt, IRQ21_interrupt, IRQ22_interrupt, IRQ23_interrupt
+#endif
};
/*
@@ -202,7 +284,7 @@ static void no_action(int cpl, void *dev_id, struct pt_regs *regs) { }
static void math_error_irq(int cpl, void *dev_id, struct pt_regs *regs)
{
outb(0,0xF0);
- if (ignore_irq13 || !hard_math)
+ if (ignore_irq13 || !boot_cpu_data.hard_math)
return;
math_error();
}
@@ -214,135 +296,59 @@ static struct irqaction irq13 = { math_error_irq, 0, 0, "fpu", NULL, NULL };
*/
static struct irqaction irq2 = { no_action, 0, 0, "cascade", NULL, NULL};
-static struct irqaction *irq_action[16] = {
+static struct irqaction *irq_action[NR_IRQS] = {
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL
+#ifdef __SMP__
+ ,NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL
+#endif
};
int get_irq_list(char *buf)
{
- int i;
+ int i, j;
struct irqaction * action;
char *p = buf;
+ p += sprintf(p, " ");
+ for (j=0; j<smp_num_cpus; j++)
+ p += sprintf(p, "CPU%d ",j);
+ *p++ = '\n';
+
for (i = 0 ; i < NR_IRQS ; i++) {
action = irq_action[i];
if (!action)
continue;
- p += sprintf(p, "%3d: %10u %s",
- i, kstat.interrupts[i], action->name);
+ p += sprintf(p, "%3d: ",i);
+#ifndef __SMP__
+ p += sprintf(p, "%10u ", kstat_irqs(i));
+#else
+ for (j=0; j<smp_num_cpus; j++)
+ p += sprintf(p, "%10u ",
+ kstat.irqs[cpu_logical_map(j)][i]);
+#endif
+
+ if (IO_APIC_IRQ(i))
+ p += sprintf(p, " IO-APIC ");
+ else
+ p += sprintf(p, " XT PIC ");
+ p += sprintf(p, " %s", action->name);
+
for (action=action->next; action; action = action->next) {
p += sprintf(p, ", %s", action->name);
}
*p++ = '\n';
}
p += sprintf(p, "NMI: %10u\n", atomic_read(&nmi_counter));
-#ifdef __SMP_PROF__
+#ifdef __SMP__
p += sprintf(p, "IPI: %10lu\n", ipi_count);
#endif
return p - buf;
}
-#ifdef __SMP_PROF__
-
-extern unsigned int prof_multiplier[NR_CPUS];
-extern unsigned int prof_counter[NR_CPUS];
-
-int get_smp_prof_list(char *buf) {
- int i,j, len = 0;
- struct irqaction * action;
- unsigned long sum_spins = 0;
- unsigned long sum_spins_syscall = 0;
- unsigned long sum_spins_sys_idle = 0;
- unsigned long sum_smp_idle_count = 0;
- unsigned long sum_local_timer_ticks = 0;
-
- for (i=0;i<smp_num_cpus;i++) {
- int cpunum = cpu_logical_map[i];
- sum_spins+=smp_spins[cpunum];
- sum_spins_syscall+=smp_spins_syscall[cpunum];
- sum_spins_sys_idle+=smp_spins_sys_idle[cpunum];
- sum_smp_idle_count+=smp_idle_count[cpunum];
- sum_local_timer_ticks+=smp_local_timer_ticks[cpunum];
- }
-
- len += sprintf(buf+len,"CPUS: %10i \n", smp_num_cpus);
- len += sprintf(buf+len," SUM ");
- for (i=0;i<smp_num_cpus;i++)
- len += sprintf(buf+len," P%1d ",cpu_logical_map[i]);
- len += sprintf(buf+len,"\n");
- for (i = 0 ; i < NR_IRQS ; i++) {
- action = *(i + irq_action);
- if (!action || !action->handler)
- continue;
- len += sprintf(buf+len, "%3d: %10d ",
- i, kstat.interrupts[i]);
- for (j=0;j<smp_num_cpus;j++)
- len+=sprintf(buf+len, "%10d ",
- int_count[cpu_logical_map[j]][i]);
- len += sprintf(buf+len, " %s", action->name);
- for (action=action->next; action; action = action->next) {
- len += sprintf(buf+len, ", %s", action->name);
- }
- len += sprintf(buf+len, "\n");
- }
- len+=sprintf(buf+len, "LCK: %10lu",
- sum_spins);
-
- for (i=0;i<smp_num_cpus;i++)
- len+=sprintf(buf+len," %10lu",smp_spins[cpu_logical_map[i]]);
-
- len +=sprintf(buf+len," spins from int\n");
-
- len+=sprintf(buf+len, "LCK: %10lu",
- sum_spins_syscall);
-
- for (i=0;i<smp_num_cpus;i++)
- len+=sprintf(buf+len," %10lu",smp_spins_syscall[cpu_logical_map[i]]);
-
- len +=sprintf(buf+len," spins from syscall\n");
-
- len+=sprintf(buf+len, "LCK: %10lu",
- sum_spins_sys_idle);
-
- for (i=0;i<smp_num_cpus;i++)
- len+=sprintf(buf+len," %10lu",smp_spins_sys_idle[cpu_logical_map[i]]);
-
- len +=sprintf(buf+len," spins from sysidle\n");
- len+=sprintf(buf+len,"IDLE %10lu",sum_smp_idle_count);
-
- for (i=0;i<smp_num_cpus;i++)
- len+=sprintf(buf+len," %10lu",smp_idle_count[cpu_logical_map[i]]);
-
- len +=sprintf(buf+len," idle ticks\n");
-
- len+=sprintf(buf+len,"TICK %10lu",sum_local_timer_ticks);
- for (i=0;i<smp_num_cpus;i++)
- len+=sprintf(buf+len," %10lu",smp_local_timer_ticks[cpu_logical_map[i]]);
-
- len +=sprintf(buf+len," local APIC timer ticks\n");
-
- len+=sprintf(buf+len,"MULT: ");
- for (i=0;i<smp_num_cpus;i++)
- len+=sprintf(buf+len," %10u",prof_multiplier[cpu_logical_map[i]]);
- len +=sprintf(buf+len," profiling multiplier\n");
-
- len+=sprintf(buf+len,"COUNT: ");
- for (i=0;i<smp_num_cpus;i++)
- len+=sprintf(buf+len," %10u",prof_counter[cpu_logical_map[i]]);
-
- len +=sprintf(buf+len," profiling counter\n");
-
- len+=sprintf(buf+len, "IPI: %10lu received\n",
- ipi_count);
-
- return len;
-}
-#endif
-
-
/*
* Global interrupt locks for SMP. Allow interrupts to come in on any
* CPU, yet make cli/sti act globally to protect critical regions..
@@ -352,8 +358,8 @@ unsigned char global_irq_holder = NO_PROC_ID;
unsigned volatile int global_irq_lock;
atomic_t global_irq_count;
-#define irq_active(cpu) \
- (global_irq_count != local_irq_count[cpu])
+atomic_t global_bh_count;
+atomic_t global_bh_lock;
/*
* "global_cli()" is a special case, in that it can hold the
@@ -371,37 +377,123 @@ static inline void check_smp_invalidate(int cpu)
}
}
-static unsigned long previous_irqholder;
+static void show(char * str)
+{
+ int i;
+ unsigned long *stack;
+ int cpu = smp_processor_id();
+
+ printk("\n%s, CPU %d:\n", str, cpu);
+ printk("irq: %d [%d %d]\n",
+ atomic_read(&global_irq_count), local_irq_count[0], local_irq_count[1]);
+ printk("bh: %d [%d %d]\n",
+ atomic_read(&global_bh_count), local_bh_count[0], local_bh_count[1]);
+ stack = (unsigned long *) &str;
+ for (i = 40; i ; i--) {
+ unsigned long x = *++stack;
+ if (x > (unsigned long) &init_task_union && x < (unsigned long) &vsprintf) {
+ printk("<[%08lx]> ", x);
+ }
+ }
+}
+
+
+#define MAXCOUNT 100000000
-static inline void wait_on_irq(int cpu, unsigned long where)
+static inline void wait_on_bh(void)
{
- int local_count = local_irq_count[cpu];
+ int count = MAXCOUNT;
+ do {
+ if (!--count) {
+ show("wait_on_bh");
+ count = ~0;
+ }
+ /* nothing .. wait for the other bh's to go away */
+ } while (atomic_read(&global_bh_count) != 0);
+}
- /* Are we the only one in an interrupt context? */
- while (local_count != atomic_read(&global_irq_count)) {
- /*
- * No such luck. Now we need to release the lock,
- * _and_ release our interrupt context, because
- * otherwise we'd have dead-locks and live-locks
- * and other fun things.
- */
- atomic_sub(local_count, &global_irq_count);
- global_irq_lock = 0;
+/*
+ * I had a lockup scenario where a tight loop doing
+ * spin_unlock()/spin_lock() on CPU#1 was racing with
+ * spin_lock() on CPU#0. CPU#0 should have noticed spin_unlock(), but
+ * apparently the spin_unlock() information did not make it
+ * through to CPU#0 ... nasty, is this by design, do we have to limit
+ * 'memory update oscillation frequency' artificially like here?
+ *
+ * Such 'high frequency update' races can be avoided by careful design, but
+ * some of our major constructs like spinlocks use similar techniques,
+ * it would be nice to clarify this issue. Set this define to 0 if you
+ * want to check wether your system freezes. I suspect the delay done
+ * by SYNC_OTHER_CORES() is in correlation with 'snooping latency', but
+ * i thought that such things are guaranteed by design, since we use
+ * the 'LOCK' prefix.
+ */
+#define SUSPECTED_CPU_OR_CHIPSET_BUG_WORKAROUND 1
+
+#if SUSPECTED_CPU_OR_CHIPSET_BUG_WORKAROUND
+# define SYNC_OTHER_CORES(x) udelay(x+1)
+#else
+/*
+ * We have to allow irqs to arrive between __sti and __cli
+ */
+# define SYNC_OTHER_CORES(x) __asm__ __volatile__ ("nop")
+#endif
+
+static inline void wait_on_irq(int cpu)
+{
+ int count = MAXCOUNT;
+
+ for (;;) {
/*
- * Wait for everybody else to go away and release
- * their things before trying to get the lock again.
+ * Wait until all interrupts are gone. Wait
+ * for bottom half handlers unless we're
+ * already executing in one..
*/
+ if (!atomic_read(&global_irq_count)) {
+ if (local_bh_count[cpu] || !atomic_read(&global_bh_count))
+ break;
+ }
+
+ /* Duh, we have to loop. Release the lock to avoid deadlocks */
+ clear_bit(0,&global_irq_lock);
+
for (;;) {
+ if (!--count) {
+ show("wait_on_irq");
+ count = ~0;
+ }
+ __sti();
+ SYNC_OTHER_CORES(cpu);
+ __cli();
check_smp_invalidate(cpu);
if (atomic_read(&global_irq_count))
continue;
if (global_irq_lock)
continue;
+ if (!local_bh_count[cpu] && atomic_read(&global_bh_count))
+ continue;
if (!test_and_set_bit(0,&global_irq_lock))
break;
}
- atomic_add(local_count, &global_irq_count);
+ }
+}
+
+/*
+ * This is called when we want to synchronize with
+ * bottom half handlers. We need to wait until
+ * no other CPU is executing any bottom half handler.
+ *
+ * Don't wait if we're already running in an interrupt
+ * context or are inside a bh handler.
+ */
+void synchronize_bh(void)
+{
+ if (atomic_read(&global_bh_count)) {
+ int cpu = smp_processor_id();
+ if (!local_irq_count[cpu] && !local_bh_count[cpu]) {
+ wait_on_bh();
+ }
}
}
@@ -411,23 +503,17 @@ static inline void wait_on_irq(int cpu, unsigned long where)
* stop sending interrupts: but to make sure there
* are no interrupts that are executing on another
* CPU we need to call this function.
- *
- * On UP this is a no-op.
*/
void synchronize_irq(void)
{
- int cpu = smp_processor_id();
- int local_count = local_irq_count[cpu];
-
- /* Do we need to wait? */
- if (local_count != atomic_read(&global_irq_count)) {
- /* The stupid way to do this */
+ if (atomic_read(&global_irq_count)) {
+ /* Stupid approach */
cli();
sti();
}
}
-static inline void get_irqlock(int cpu, unsigned long where)
+static inline void get_irqlock(int cpu)
{
if (test_and_set_bit(0,&global_irq_lock)) {
/* do we already hold the lock? */
@@ -440,105 +526,313 @@ static inline void get_irqlock(int cpu, unsigned long where)
} while (test_bit(0,&global_irq_lock));
} while (test_and_set_bit(0,&global_irq_lock));
}
- /*
- * Ok, we got the lock bit.
- * But that's actually just the easy part.. Now
- * we need to make sure that nobody else is running
+ /*
+ * We also to make sure that nobody else is running
* in an interrupt context.
*/
- wait_on_irq(cpu, where);
+ wait_on_irq(cpu);
/*
- * Finally.
+ * Ok, finally..
*/
global_irq_holder = cpu;
- previous_irqholder = where;
}
+/*
+ * A global "cli()" while in an interrupt context
+ * turns into just a local cli(). Interrupts
+ * should use spinlocks for the (very unlikely)
+ * case that they ever want to protect against
+ * each other.
+ */
void __global_cli(void)
{
int cpu = smp_processor_id();
- unsigned long where;
- __asm__("movl 16(%%esp),%0":"=r" (where));
__cli();
- get_irqlock(cpu, where);
+ if (!local_irq_count[cpu])
+ get_irqlock(cpu);
}
void __global_sti(void)
{
- release_irqlock(smp_processor_id());
+ int cpu = smp_processor_id();
+
+ if (!local_irq_count[cpu])
+ release_irqlock(cpu);
__sti();
}
unsigned long __global_save_flags(void)
{
- return global_irq_holder == (unsigned char) smp_processor_id();
+ if (!local_irq_count[smp_processor_id()])
+ return global_irq_holder == (unsigned char) smp_processor_id();
+ else {
+ unsigned long x;
+ __save_flags(x);
+ return x;
+ }
}
void __global_restore_flags(unsigned long flags)
{
- switch (flags) {
- case 0:
- release_irqlock(smp_processor_id());
- __sti();
- break;
- case 1:
- __global_cli();
- break;
- default:
- printk("global_restore_flags: %08lx (%08lx)\n",
- flags, (&flags)[-1]);
- }
+ if (!local_irq_count[smp_processor_id()]) {
+ switch (flags) {
+ case 0:
+ __global_sti();
+ break;
+ case 1:
+ __global_cli();
+ break;
+ default:
+ printk("global_restore_flags: %08lx (%08lx)\n",
+ flags, (&flags)[-1]);
+ }
+ } else
+ __restore_flags(flags);
}
#endif
-/*
- * do_IRQ handles all normal device IRQ's (the special
- * SMP cross-CPU interrupts have their own specific
- * handlers).
- */
-asmlinkage void do_IRQ(struct pt_regs regs)
+static int handle_IRQ_event(unsigned int irq, struct pt_regs * regs)
{
- int irq = regs.orig_eax & 0xff;
struct irqaction * action;
- int status, cpu;
-
- /*
- * mask and ack quickly, we don't want the irq controller
- * thinking we're snobs just because some other CPU has
- * disabled global interrupts (we have already done the
- * INT_ACK cycles, it's too late to try to pretend to the
- * controller that we aren't taking the interrupt).
- */
- mask_and_ack_irq(irq);
+ int status;
- cpu = smp_processor_id();
- irq_enter(cpu, irq);
- kstat.interrupts[irq]++;
-
- /* Return with this interrupt masked if no action */
status = 0;
action = *(irq + irq_action);
+
if (action) {
+ status |= 1;
+
if (!(action->flags & SA_INTERRUPT))
__sti();
do {
status |= action->flags;
- action->handler(irq, action->dev_id, &regs);
+ action->handler(irq, action->dev_id, regs);
action = action->next;
} while (action);
if (status & SA_SAMPLE_RANDOM)
add_interrupt_randomness(irq);
__cli();
+ }
+
+ return status;
+}
+
+
+void disable_irq(unsigned int irq)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&irq_controller_lock, flags);
+ irq_handles[irq]->disable(irq);
+ spin_unlock_irqrestore(&irq_controller_lock, flags);
+
+ synchronize_irq();
+}
+
+/*
+ * disable/enable_irq() wait for all irq contexts to finish
+ * executing. Also it's recursive.
+ */
+static void disable_8259A_irq(unsigned int irq)
+{
+ disabled_irq[irq]++;
+ cached_irq_mask |= 1 << irq;
+ set_8259A_irq_mask(irq);
+}
+
+#ifdef __SMP__
+static void disable_ioapic_irq(unsigned int irq)
+{
+ disabled_irq[irq]++;
+ /*
+ * We do not disable IO-APIC irqs in hardware ...
+ */
+}
+#endif
+
+void enable_8259A_irq (unsigned int irq)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&irq_controller_lock, flags);
+ if (disabled_irq[irq])
+ disabled_irq[irq]--;
+ else {
+ spin_unlock_irqrestore(&irq_controller_lock, flags);
+ return;
+ }
+ cached_irq_mask &= ~(1 << irq);
+ set_8259A_irq_mask(irq);
+ spin_unlock_irqrestore(&irq_controller_lock, flags);
+}
+
+#ifdef __SMP__
+void enable_ioapic_irq (unsigned int irq)
+{
+ unsigned long flags;
+ int cpu = smp_processor_id(), should_handle_irq;
+
+ spin_lock_irqsave(&irq_controller_lock, flags);
+ if (disabled_irq[irq])
+ disabled_irq[irq]--;
+ else {
+ spin_unlock_irqrestore(&irq_controller_lock, flags);
+ return;
+ }
+ /*
+ * In the SMP+IOAPIC case it might happen that there are an unspecified
+ * number of pending IRQ events unhandled. We protect against multiple
+ * enable_irq()'s executing them via disable_irq[irq]++
+ */
+ if (!disabled_irq[irq] && irq_events[irq]) {
+ struct pt_regs regs; /* FIXME: these are fake currently */
+
+ disabled_irq[irq]++;
+ spin_unlock(&irq_controller_lock);
+ release_irqlock(cpu);
+ irq_enter(cpu, irq);
+again:
+ handle_IRQ_event(irq, &regs);
+
spin_lock(&irq_controller_lock);
- unmask_irq(irq);
+ disabled_irq[irq]--;
+ should_handle_irq=0;
+ if (--irq_events[irq] && !disabled_irq[irq]) {
+ should_handle_irq=1;
+ disabled_irq[irq]++;
+ }
+ spin_unlock(&irq_controller_lock);
+
+ if (should_handle_irq)
+ goto again;
+
+ irq_exit(cpu, irq);
+ __restore_flags(flags);
+ } else
+ spin_unlock_irqrestore(&irq_controller_lock, flags);
+}
+#endif
+
+void enable_irq(unsigned int irq)
+{
+ irq_handles[irq]->enable(irq);
+}
+
+void make_8259A_irq (unsigned int irq)
+{
+ io_apic_irqs &= ~(1<<irq);
+ irq_handles[irq] = &i8259A_irq_type;
+ disable_irq(irq);
+ enable_irq(irq);
+}
+
+/*
+ * Careful! The 8259A is a fragile beast, it pretty
+ * much _has_ to be done exactly like this (mask it
+ * first, _then_ send the EOI, and the order of EOI
+ * to the two 8259s is important!
+ */
+static inline void mask_and_ack_8259A(unsigned int irq)
+{
+ spin_lock(&irq_controller_lock);
+ cached_irq_mask |= 1 << irq;
+ if (irq & 8) {
+ inb(0xA1); /* DUMMY */
+ outb(cached_A1,0xA1);
+ outb(0x62,0x20); /* Specific EOI to cascade */
+ outb(0x20,0xA0);
+ } else {
+ inb(0x21); /* DUMMY */
+ outb(cached_21,0x21);
+ outb(0x20,0x20);
+ }
+ spin_unlock(&irq_controller_lock);
+}
+
+static void do_8259A_IRQ(unsigned int irq, int cpu, struct pt_regs * regs)
+{
+ mask_and_ack_8259A(irq);
+
+ irq_enter(cpu, irq);
+
+ if (handle_IRQ_event(irq, regs)) {
+ spin_lock(&irq_controller_lock);
+ unmask_8259A(irq);
spin_unlock(&irq_controller_lock);
}
irq_exit(cpu, irq);
+}
+
+#ifdef __SMP__
+static void do_ioapic_IRQ(unsigned int irq, int cpu, struct pt_regs * regs)
+{
+ int should_handle_irq = 0;
+
+ ack_APIC_irq();
+
+ spin_lock(&irq_controller_lock);
+
+ if (!irq_events[irq]++ && !disabled_irq[irq])
+ should_handle_irq = 1;
+
+ spin_unlock(&irq_controller_lock);
+
+ irq_enter(cpu, irq);
+
+ if (should_handle_irq) {
+again:
+ handle_IRQ_event(irq, regs);
+
+ spin_lock(&irq_controller_lock);
+ should_handle_irq=0;
+ if (--irq_events[irq] && !disabled_irq[irq])
+ should_handle_irq=1;
+ spin_unlock(&irq_controller_lock);
+
+ if (should_handle_irq)
+ goto again;
+ }
+
+ irq_exit(cpu, irq);
+}
+#endif
+
+/*
+ * do_IRQ handles all normal device IRQ's (the special
+ * SMP cross-CPU interrupts have their own specific
+ * handlers).
+ *
+ * the biggest change on SMP is the fact that we no more mask
+ * interrupts in hardware, please believe me, this is unavoidable,
+ * the hardware is largely message-oriented, i tried to force our
+ * state-driven irq handling scheme onto the IO-APIC, but no avail.
+ *
+ * so we soft-disable interrupts via 'event counters', the first 'incl'
+ * will do the IRQ handling. This also has the nice side effect of increased
+ * overlapping ... i saw no driver problem so far.
+ */
+asmlinkage void do_IRQ(struct pt_regs regs)
+{
+ /*
+ * We ack quickly, we don't want the irq controller
+ * thinking we're snobs just because some other CPU has
+ * disabled global interrupts (we have already done the
+ * INT_ACK cycles, it's too late to try to pretend to the
+ * controller that we aren't taking the interrupt).
+ *
+ * 0 return value means that this irq is already being
+ * handled by some other CPU. (or is disabled)
+ */
+ unsigned int irq = regs.orig_eax & 0xff;
+ int cpu = smp_processor_id();
+
+ kstat.irqs[cpu][irq]++;
+ irq_handles[irq]->handle(irq, cpu, &regs);
+
/*
* This should be conditional: we should really get
* a return code from the irq handler to tell us
@@ -551,7 +845,7 @@ asmlinkage void do_IRQ(struct pt_regs regs)
}
}
-int setup_x86_irq(int irq, struct irqaction * new)
+int setup_x86_irq(unsigned int irq, struct irqaction * new)
{
int shared = 0;
struct irqaction *old, **p;
@@ -580,7 +874,18 @@ int setup_x86_irq(int irq, struct irqaction * new)
if (!shared) {
spin_lock(&irq_controller_lock);
- unmask_irq(irq);
+#ifdef __SMP__
+ if (IO_APIC_IRQ(irq)) {
+ irq_handles[irq] = &ioapic_irq_type;
+ /*
+ * First disable it in the 8259A:
+ */
+ cached_irq_mask |= 1 << irq;
+ if (irq < 16)
+ set_8259A_irq_mask(irq);
+ }
+#endif
+ unmask_generic_irq(irq);
spin_unlock(&irq_controller_lock);
}
restore_flags(flags);
@@ -596,12 +901,13 @@ int request_irq(unsigned int irq,
int retval;
struct irqaction * action;
- if (irq > 15)
+ if (irq >= NR_IRQS)
return -EINVAL;
if (!handler)
return -EINVAL;
- action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL);
+ action = (struct irqaction *)
+ kmalloc(sizeof(struct irqaction), GFP_KERNEL);
if (!action)
return -ENOMEM;
@@ -624,7 +930,7 @@ void free_irq(unsigned int irq, void *dev_id)
struct irqaction * action, **p;
unsigned long flags;
- if (irq > 15) {
+ if (irq >= NR_IRQS) {
printk("Trying to free IRQ%d\n",irq);
return;
}
@@ -643,42 +949,104 @@ void free_irq(unsigned int irq, void *dev_id)
printk("Trying to free free IRQ%d\n",irq);
}
+/*
+ * probing is always single threaded [FIXME: is this true?]
+ */
+static unsigned int probe_irqs[NR_CPUS][NR_IRQS];
+
unsigned long probe_irq_on (void)
{
- unsigned int i, irqs = 0;
+ unsigned int i, j, irqs = 0;
unsigned long delay;
- /* first, enable any unassigned irqs */
- for (i = 15; i > 0; i--) {
+ /*
+ * save current irq counts
+ */
+ memcpy(probe_irqs,kstat.irqs,NR_CPUS*NR_IRQS*sizeof(int));
+
+ /*
+ * first, enable any unassigned irqs
+ */
+ for (i = NR_IRQS-1; i > 0; i--) {
if (!irq_action[i]) {
- enable_irq(i);
+ unsigned long flags;
+ spin_lock_irqsave(&irq_controller_lock, flags);
+ unmask_generic_irq(i);
irqs |= (1 << i);
+ spin_unlock_irqrestore(&irq_controller_lock, flags);
}
}
- /* wait for spurious interrupts to mask themselves out again */
+ /*
+ * wait for spurious interrupts to increase counters
+ */
for (delay = jiffies + HZ/10; delay > jiffies; )
- /* about 100ms delay */;
+ /* about 100ms delay */ synchronize_irq();
- /* now filter out any obviously spurious interrupts */
- return irqs & ~cached_irq_mask;
+ /*
+ * now filter out any obviously spurious interrupts
+ */
+ for (i=0; i<NR_IRQS; i++)
+ for (j=0; j<NR_CPUS; j++)
+ if (kstat.irqs[j][i] != probe_irqs[j][i])
+ irqs &= ~(i<<1);
+
+ return irqs;
}
int probe_irq_off (unsigned long irqs)
{
- unsigned int i;
+ int i,j, irq_found = -1;
-#ifdef DEBUG
- printk("probe_irq_off: irqs=0x%04lx irqmask=0x%04x\n", irqs, cached_irq_mask);
-#endif
- irqs &= cached_irq_mask;
- if (!irqs)
- return 0;
- i = ffz(~irqs);
- if (irqs != (irqs & (1 << i)))
- i = -i;
- return i;
+ for (i=0; i<NR_IRQS; i++) {
+ int sum = 0;
+ for (j=0; j<NR_CPUS; j++) {
+ sum += kstat.irqs[j][i];
+ sum -= probe_irqs[j][i];
+ }
+ if (sum && (irqs & (i<<1))) {
+ if (irq_found != -1) {
+ irq_found = -irq_found;
+ goto out;
+ } else
+ irq_found = i;
+ }
+ }
+ if (irq_found == -1)
+ irq_found = 0;
+out:
+ return irq_found;
+}
+
+#ifdef __SMP__
+void init_IO_APIC_traps(void)
+{
+ int i;
+ /*
+ * NOTE! The local APIC isn't very good at handling
+ * multiple interrupts at the same interrupt level.
+ * As the interrupt level is determined by taking the
+ * vector number and shifting that right by 4, we
+ * want to spread these out a bit so that they don't
+ * all fall in the same interrupt level
+ *
+ * also, we've got to be careful not to trash gate
+ * 0x80, because int 0x80 is hm, kindof importantish ;)
+ */
+ for (i = 0; i < NR_IRQS ; i++)
+ if (IO_APIC_GATE_OFFSET+(i<<3) <= 0xfe) /* HACK */ {
+ if (IO_APIC_IRQ(i)) {
+ irq_handles[i] = &ioapic_irq_type;
+ /*
+ * First disable it in the 8259A:
+ */
+ cached_irq_mask |= 1 << i;
+ if (i < 16)
+ set_8259A_irq_mask(i);
+ }
+ }
}
+#endif
__initfunc(void init_IRQ(void))
{
@@ -689,18 +1057,22 @@ __initfunc(void init_IRQ(void))
outb_p(LATCH & 0xff , 0x40); /* LSB */
outb(LATCH >> 8 , 0x40); /* MSB */
- for (i = 0; i < NR_IRQS ; i++)
+ printk("INIT IRQ\n");
+ for (i=0; i<NR_IRQS; i++) {
+ irq_events[i] = 0;
+ disabled_irq[i] = 0;
+ }
+ /*
+ * 16 old-style INTA-cycle interrupt gates:
+ */
+ for (i = 0; i < 16; i++)
set_intr_gate(0x20+i,interrupt[i]);
#ifdef __SMP__
- /*
- * NOTE! The local APIC isn't very good at handling
- * multiple interrupts at the same interrupt level.
- * As the interrupt level is determined by taking the
- * vector number and shifting that right by 4, we
- * want to spread these out a bit so that they don't
- * all fall in the same interrupt level
- */
+
+ for (i = 0; i < NR_IRQS ; i++)
+ if (IO_APIC_GATE_OFFSET+(i<<3) <= 0xfe) /* hack -- mingo */
+ set_intr_gate(IO_APIC_GATE_OFFSET+(i<<3),interrupt[i]);
/*
* The reschedule interrupt slowly changes it's functionality,
@@ -711,21 +1083,23 @@ __initfunc(void init_IRQ(void))
* [ It has to be here .. it doesn't work if you put
* it down the bottom - assembler explodes 8) ]
*/
- /* IRQ '16' (trap 0x30) - IPI for rescheduling */
- set_intr_gate(0x20+i, reschedule_interrupt);
+ /* IPI for rescheduling */
+ set_intr_gate(0x30, reschedule_interrupt);
- /* IRQ '17' (trap 0x31) - IPI for invalidation */
- set_intr_gate(0x21+i, invalidate_interrupt);
+ /* IPI for invalidation */
+ set_intr_gate(0x31, invalidate_interrupt);
- /* IRQ '18' (trap 0x40) - IPI for CPU halt */
- set_intr_gate(0x30+i, stop_cpu_interrupt);
+ /* IPI for CPU halt */
+ set_intr_gate(0x40, stop_cpu_interrupt);
+
+ /* self generated IPI for local APIC timer */
+ set_intr_gate(0x41, apic_timer_interrupt);
- /* IRQ '19' (trap 0x41) - self generated IPI for local APIC timer */
- set_intr_gate(0x31+i, apic_timer_interrupt);
#endif
request_region(0x20,0x20,"pic1");
request_region(0xa0,0x20,"pic2");
setup_x86_irq(2, &irq2);
setup_x86_irq(13, &irq13);
}
+
diff --git a/arch/i386/kernel/irq.h b/arch/i386/kernel/irq.h
index db70872f1..6404bc9e3 100644
--- a/arch/i386/kernel/irq.h
+++ b/arch/i386/kernel/irq.h
@@ -7,9 +7,42 @@
* Interrupt entry/exit code at both C and assembly level
*/
+#define IO_APIC_GATE_OFFSET 0x51
+
+void mask_irq(unsigned int irq);
+void unmask_irq(unsigned int irq);
+void enable_IO_APIC_irq (unsigned int irq);
+void disable_IO_APIC_irq (unsigned int irq);
+void set_8259A_irq_mask(unsigned int irq);
+void ack_APIC_irq (void);
+void setup_IO_APIC (void);
+void init_IO_APIC_traps(void);
+int IO_APIC_get_PCI_irq_vector (int bus, int slot, int fn);
+void make_8259A_irq (unsigned int irq);
+
+extern unsigned int io_apic_irqs;
+
+#define MAX_IRQ_SOURCES 128
+#define MAX_MP_BUSSES 32
+enum mp_bustype {
+ MP_BUS_ISA,
+ MP_BUS_PCI
+};
+extern int mp_bus_id_to_type [MAX_MP_BUSSES];
+extern int mp_bus_id_to_pci_bus [MAX_MP_BUSSES];
+extern char ioapic_OEM_ID [16];
+extern char ioapic_Product_ID [16];
+
+extern spinlock_t irq_controller_lock; /*
+ * Protects both the 8259 and the
+ * IO-APIC
+ */
+
#ifdef __SMP__
-static inline void irq_enter(int cpu, int irq)
+#include <asm/atomic.h>
+
+static inline void irq_enter(int cpu, unsigned int irq)
{
hardirq_enter(cpu);
while (test_bit(0,&global_irq_lock)) {
@@ -17,17 +50,25 @@ static inline void irq_enter(int cpu, int irq)
}
}
-static inline void irq_exit(int cpu, int irq)
+static inline void irq_exit(int cpu, unsigned int irq)
{
hardirq_exit(cpu);
release_irqlock(cpu);
}
+#define IO_APIC_IRQ(x) ((1<<x) & io_apic_irqs)
+
#else
#define irq_enter(cpu, irq) (++local_irq_count[cpu])
#define irq_exit(cpu, irq) (--local_irq_count[cpu])
+/* Make these no-ops when not using SMP */
+#define enable_IO_APIC_irq(x) do { } while (0)
+#define disable_IO_APIC_irq(x) do { } while (0)
+
+#define IO_APIC_IRQ(x) (0)
+
#endif
#define __STR(x) #x
@@ -94,7 +135,7 @@ __asm__( \
"pushl $ret_from_intr\n\t" \
"jmp "SYMBOL_NAME_STR(do_IRQ));
-#define BUILD_IRQ(chip,nr,mask) \
+#define BUILD_IRQ(nr) \
asmlinkage void IRQ_NAME(nr); \
__asm__( \
"\n"__ALIGN_STR"\n" \
diff --git a/arch/i386/kernel/ldt.c b/arch/i386/kernel/ldt.c
index 09a8c4a67..65c743195 100644
--- a/arch/i386/kernel/ldt.c
+++ b/arch/i386/kernel/ldt.c
@@ -33,31 +33,6 @@ static int read_ldt(void * ptr, unsigned long bytecount)
return copy_to_user(ptr, address, size) ? -EFAULT : size;
}
-static inline int limits_ok(struct modify_ldt_ldt_s *ldt_info)
-{
- unsigned long base, limit;
- /* linear address of first and last accessible byte */
- unsigned long first, last;
-
- base = ldt_info->base_addr;
- limit = ldt_info->limit;
- if (ldt_info->limit_in_pages)
- limit = limit * PAGE_SIZE + PAGE_SIZE - 1;
-
- first = base;
- last = limit + base;
-
- /* segment grows down? */
- if (ldt_info->contents == 1) {
- /* data segment grows down */
- first = base+limit+1;
- last = base+65535;
- if (ldt_info->seg_32bit)
- last = base-1;
- }
- return (last >= first && last < TASK_SIZE);
-}
-
static int write_ldt(void * ptr, unsigned long bytecount, int oldmode)
{
struct modify_ldt_ldt_s ldt_info;
@@ -73,9 +48,6 @@ static int write_ldt(void * ptr, unsigned long bytecount, int oldmode)
if ((ldt_info.contents == 3 && (oldmode || ldt_info.seg_not_present == 0)) || ldt_info.entry_number >= LDT_ENTRIES)
return -EINVAL;
- if (!limits_ok(&ldt_info) && (oldmode || ldt_info.seg_not_present == 0))
- return -EINVAL;
-
if (!current->ldt) {
for (i=1 ; i<NR_TASKS ; i++) {
if (task[i] == current) {
diff --git a/arch/i386/kernel/mca.c b/arch/i386/kernel/mca.c
index 1f8d0799d..de6de8f14 100644
--- a/arch/i386/kernel/mca.c
+++ b/arch/i386/kernel/mca.c
@@ -65,7 +65,7 @@ static struct MCA_info* mca_info = 0;
static long mca_do_proc_init( long memory_start, long memory_end );
static int mca_default_procfn( char* buf, int slot );
-static long proc_mca_read( struct inode*, struct file*, char* buf, unsigned long count );
+static ssize_t proc_mca_read( struct file*, char*, size_t, loff_t *);
static struct file_operations proc_mca_operations = {
NULL, proc_mca_read,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
@@ -527,7 +527,7 @@ static int mca_not_implemented( char* buf )
*/
static int mca_fill( char* page, int pid, int type, char** start,
- off_t offset, int length)
+ loff_t *offset, int length)
{
int len = 0;
int slot = 0;
@@ -571,8 +571,8 @@ static int mca_fill( char* page, int pid, int type, char** start,
#define PROC_BLOCK_SIZE (3*1024)
-long proc_mca_read( struct inode* inode, struct file* file,
- char* buf, unsigned long count)
+static ssize_t proc_mca_read( struct file* file,
+ char* buf, size_t count, loff_t *ppos)
{
unsigned long page;
char *start;
@@ -580,6 +580,7 @@ long proc_mca_read( struct inode* inode, struct file* file,
int end;
unsigned int type, pid;
struct proc_dir_entry *dp;
+ struct inode *inode = file->f_dentry->d_inode;
if (count < 0)
return -EINVAL;
@@ -593,7 +594,7 @@ long proc_mca_read( struct inode* inode, struct file* file,
start = 0;
dp = (struct proc_dir_entry *) inode->u.generic_ip;
length = mca_fill((char *) page, pid, type,
- &start, file->f_pos, count);
+ &start, ppos, count);
if (length < 0) {
free_page(page);
return length;
@@ -601,19 +602,19 @@ long proc_mca_read( struct inode* inode, struct file* file,
if (start != 0) {
/* We have had block-adjusting processing! */
copy_to_user(buf, start, length);
- file->f_pos += length;
+ *ppos += length;
count = length;
} else {
/* Static 4kB (or whatever) block capacity */
- if (file->f_pos >= length) {
+ if (*ppos >= length) {
free_page(page);
return 0;
}
- if (count + file->f_pos > length)
- count = length - file->f_pos;
- end = count + file->f_pos;
- copy_to_user(buf, (char *) page + file->f_pos, count);
- file->f_pos = end;
+ if (count + *ppos > length)
+ count = length - *ppos;
+ end = count + *ppos;
+ copy_to_user(buf, (char *) page + *ppos, count);
+ *ppos = end;
}
free_page(page);
return count;
diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c
index 352c5552a..629e7ef12 100644
--- a/arch/i386/kernel/process.c
+++ b/arch/i386/kernel/process.c
@@ -75,7 +75,7 @@ void enable_hlt(void)
static void hard_idle(void)
{
while (!need_resched) {
- if (hlt_works_ok && !hlt_counter) {
+ if (boot_cpu_data.hlt_works_ok && !hlt_counter) {
#ifdef CONFIG_APM
/* If the APM BIOS is not enabled, or there
is an error calling the idle routine, we
@@ -114,8 +114,7 @@ asmlinkage int sys_idle(void)
/* endless idle loop with no priority at all */
current->priority = -100;
current->counter = -100;
- for (;;)
- {
+ for (;;) {
/*
* We are locked at this point. So we can safely call
* the APM bios knowing only one CPU at a time will do
@@ -124,12 +123,9 @@ asmlinkage int sys_idle(void)
if (!start_idle)
start_idle = jiffies;
if (jiffies - start_idle > HARD_IDLE_TIMEOUT)
- {
hard_idle();
- }
- else
- {
- if (hlt_works_ok && !hlt_counter && !need_resched)
+ else {
+ if (boot_cpu_data.hlt_works_ok && !hlt_counter && !need_resched)
__asm__("hlt");
}
run_task_queue(&tq_scheduler);
@@ -154,7 +150,7 @@ int cpu_idle(void *unused)
current->priority = -100;
while(1)
{
- if(cpu_data[smp_processor_id()].hlt_works_ok &&
+ if(current_cpu_data.hlt_works_ok &&
!hlt_counter && !need_resched)
__asm("hlt");
/*
@@ -185,6 +181,7 @@ asmlinkage int sys_idle(void)
* controller to pulse the reset-line low. We try that for a while,
* and if it doesn't work, we do some other stupid things.
*/
+
static long no_idt[2] = {0, 0};
static int reboot_mode = 0;
static int reboot_thru_bios = 0;
@@ -226,7 +223,7 @@ real_mode_gdt_entries [3] =
{
0x0000000000000000ULL, /* Null descriptor */
0x00009a000000ffffULL, /* 16-bit real-mode 64k code at 0x00000000 */
- 0x000092000100ffffULL /* 16-bit real-mode 64k data at 0x00000100 */
+ 0x000092000100ffffULL /* 16-bit real-mode 64k data at 0x00000100 */
};
static struct
@@ -260,16 +257,16 @@ static unsigned char real_mode_switch [] =
{
0x66, 0x0f, 0x20, 0xc0, /* movl %cr0,%eax */
0x66, 0x83, 0xe0, 0x11, /* andl $0x00000011,%eax */
- 0x66, 0x0d, 0x00, 0x00, 0x00, 0x60, /* orl $0x60000000,%eax */
+ 0x66, 0x0d, 0x00, 0x00, 0x00, 0x60, /* orl $0x60000000,%eax */
0x66, 0x0f, 0x22, 0xc0, /* movl %eax,%cr0 */
0x66, 0x0f, 0x22, 0xd8, /* movl %eax,%cr3 */
0x66, 0x0f, 0x20, 0xc3, /* movl %cr0,%ebx */
0x66, 0x81, 0xe3, 0x00, 0x00, 0x00, 0x60, /* andl $0x60000000,%ebx */
- 0x74, 0x02, /* jz f */
- 0x0f, 0x08, /* invd */
- 0x24, 0x10, /* f: andb $0x10,al */
+ 0x74, 0x02, /* jz f */
+ 0x0f, 0x08, /* invd */
+ 0x24, 0x10, /* f: andb $0x10,al */
0x66, 0x0f, 0x22, 0xc0, /* movl %eax,%cr0 */
- 0xea, 0x00, 0x00, 0xff, 0xff /* ljmp $0xffff,$0x0000 */
+ 0xea, 0x00, 0x00, 0xff, 0xff /* ljmp $0xffff,$0x0000 */
};
static inline void kb_wait(void)
@@ -301,7 +298,7 @@ void machine_restart(char * __unused)
}
}
- cli ();
+ cli();
/* Write zero to CMOS register number 0x0f, which the BIOS POST
routine will recognize as telling it to do a proper reboot. (Well
@@ -325,7 +322,7 @@ void machine_restart(char * __unused)
/* Make sure the first page is mapped to the start of physical memory.
It is normally not mapped, to trap kernel NULL pointer dereferences. */
- pg0 [0] = 7;
+ pg0[0] = 7;
/*
* Use `swapper_pg_dir' as our page directory. We bother with
@@ -530,7 +527,7 @@ int dump_fpu (struct pt_regs * regs, struct user_i387_struct* fpu)
int fpvalid;
if ((fpvalid = current->used_math) != 0) {
- if (hard_math) {
+ if (boot_cpu_data.hard_math) {
if (last_task_used_math == current) {
__asm__("clts ; fsave %0; fwait": :"m" (*fpu));
}
diff --git a/arch/i386/kernel/ptrace.c b/arch/i386/kernel/ptrace.c
index e08d75100..ca2147ee7 100644
--- a/arch/i386/kernel/ptrace.c
+++ b/arch/i386/kernel/ptrace.c
@@ -2,6 +2,7 @@
/* By Ross Biro 1/23/92 */
/* edited by Linus Torvalds */
+#include <linux/config.h> /* for CONFIG_MATH_EMULATION */
#include <linux/head.h>
#include <linux/kernel.h>
#include <linux/sched.h>
@@ -611,7 +612,7 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
child->tss.i387.hard.twd = 0xffffffff;
}
#ifdef CONFIG_MATH_EMULATION
- if ( hard_math ) {
+ if ( boot_cpu_data.hard_math ) {
#endif
if (last_task_used_math == child) {
clts();
@@ -639,7 +640,7 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
}
child->used_math = 1;
#ifdef CONFIG_MATH_EMULATION
- if ( hard_math ) {
+ if ( boot_cpu_data.hard_math ) {
#endif
if (last_task_used_math == child) {
/* Discard the state of the FPU */
diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c
index 9868811a6..3dee41650 100644
--- a/arch/i386/kernel/setup.c
+++ b/arch/i386/kernel/setup.c
@@ -2,6 +2,9 @@
* linux/arch/i386/kernel/setup.c
*
* Copyright (C) 1995 Linus Torvalds
+ *
+ * Enhanced CPU type detection by Mike Jagdis, Patrick St. Jean
+ * and Martin Mares, November 1997.
*/
/*
@@ -34,22 +37,11 @@
#include <asm/smp.h>
/*
- * Tell us the machine setup..
+ * Machine setup..
*/
-char hard_math = 0; /* set by kernel/head.S */
-char x86 = 0; /* set by kernel/head.S to 3..6 */
-char x86_model = 0; /* set by kernel/head.S */
-char x86_mask = 0; /* set by kernel/head.S */
-int x86_capability = 0; /* set by kernel/head.S */
-int fdiv_bug = 0; /* set if Pentium(TM) with FP bug */
-int pentium_f00f_bug = 0; /* set if Pentium(TM) with F00F bug */
-int have_cpuid = 0; /* set if CPUID instruction works */
-
-char x86_vendor_id[13] = "unknown";
char ignore_irq13 = 0; /* set if exception 16 works */
-char wp_works_ok = -1; /* set if paging hardware honours WP */
-char hlt_works_ok = 1; /* set if the "hlt" instruction works */
+struct cpuinfo_x86 boot_cpu_data = { 0, 0, 0, 0, -1, 1, 0, 0, -1 };
/*
* Bus types ..
@@ -93,9 +85,7 @@ extern char empty_zero_page[PAGE_SIZE];
*/
#define PARAM empty_zero_page
#define EXT_MEM_K (*(unsigned short *) (PARAM+2))
-#ifndef STANDARD_MEMORY_BIOS_CALL
#define ALT_MEM_K (*(unsigned long *) (PARAM+0x1e0))
-#endif
#ifdef CONFIG_APM
#define APM_BIOS_INFO (*(struct apm_bios_info *) (PARAM+64))
#endif
@@ -124,15 +114,12 @@ __initfunc(void setup_arch(char **cmdline_p,
unsigned long * memory_start_p, unsigned long * memory_end_p))
{
unsigned long memory_start, memory_end;
- unsigned long memory_alt_end;
char c = ' ', *to = command_line, *from = COMMAND_LINE;
int len = 0;
static unsigned char smptrap=0;
- if(smptrap==1)
- {
+ if (smptrap)
return;
- }
smptrap=1;
ROOT_DEV = to_kdev_t(ORIG_ROOT_DEV);
@@ -150,13 +137,12 @@ __initfunc(void setup_arch(char **cmdline_p,
aux_device_present = AUX_DEVICE_INFO;
memory_end = (1<<20) + (EXT_MEM_K<<10);
#ifndef STANDARD_MEMORY_BIOS_CALL
- memory_alt_end = (1<<20) + (ALT_MEM_K<<10);
- if (memory_alt_end > memory_end) {
- printk("Memory: sized by int13 0e801h\n");
- memory_end = memory_alt_end;
+ {
+ unsigned long memory_alt_end = (1<<20) + (ALT_MEM_K<<10);
+ /* printk(KERN_DEBUG "Memory sizing: %08x %08x\n", memory_end, memory_alt_end); */
+ if (memory_alt_end > memory_end)
+ memory_end = memory_alt_end;
}
- else
- printk("Memory: sized by int13 088h\n");
#endif
memory_end &= PAGE_MASK;
#ifdef CONFIG_BLK_DEV_RAM
@@ -186,7 +172,7 @@ __initfunc(void setup_arch(char **cmdline_p,
if (to != command_line) to--;
if (!memcmp(from+4, "nopentium", 9)) {
from += 9+4;
- x86_capability &= ~8;
+ boot_cpu_data.x86_capability &= ~8;
} else {
memory_end = simple_strtoul(from+4, &from, 0);
if ( *from == 'K' || *from == 'k' ) {
@@ -232,163 +218,290 @@ __initfunc(void setup_arch(char **cmdline_p,
request_region(0xf0,0x10,"fpu");
}
-static const char * i486model(unsigned int nr)
+/*
+ * Detection of CPU model.
+ */
+
+extern inline void cpuid(int op, int *eax, int *ebx, int *ecx, int *edx)
{
- static const char *model[] = {
- "0","DX","SX","DX/2","4","SX/2","6","DX/2-WB","DX/4","DX/4-WB",
- "10","11","12","13","Am5x86-WT","Am5x86-WB"
- };
- if (nr < sizeof(model)/sizeof(char *))
- return model[nr];
- return NULL;
+ __asm__("cpuid"
+ : "=a" (*eax),
+ "=b" (*ebx),
+ "=c" (*ecx),
+ "=d" (*edx)
+ : "a" (op)
+ : "cc");
}
-static const char * i586model(unsigned int nr)
+__initfunc(static int cyrix_model(struct cpuinfo_x86 *c))
{
- static const char *model[] = {
- "0", "Pentium 60/66","Pentium 75+","OverDrive PODP5V83",
- "Pentium MMX"
- };
- if (nr < sizeof(model)/sizeof(char *))
- return model[nr];
- return NULL;
+ int nr = c->x86_model;
+ char *buf = c->x86_model_id;
+
+ /* Note that some of the possibilities this decoding allows
+ * have never actually been manufactured - but those that
+ * do actually exist are correctly decoded.
+ */
+ if (nr < 0x20) {
+ strcpy(buf, "Cx486");
+ if (!(nr & 0x10)) {
+ sprintf(buf+5, "%c%s%c",
+ (nr & 0x01) ? 'D' : 'S',
+ (nr & 0x04) ? "Rx" : "LC",
+ (nr & 0x02) ? '2' : '\000');
+ } else if (!(nr & 0x08)) {
+ sprintf(buf+5, "S%s%c",
+ (nr & 0x01) ? "2" : "",
+ (nr & 0x02) ? 'e' : '\000');
+ } else {
+ sprintf(buf+5, "DX%c",
+ nr == 0x1b ? '2'
+ : (nr == 0x1f ? '4' : '\000'));
+ }
+ } else if (nr >= 0x20 && nr <= 0x4f) { /* 5x86, 6x86 or Gx86 */
+ char *s = "";
+ if (nr >= 0x30 && nr < 0x40) { /* 6x86 */
+ if (c->x86 == 5 && (c->x86_capability & (1 << 8)))
+ s = "L"; /* 6x86L */
+ else if (c->x86 == 6)
+ s = "MX"; /* 6x86MX */
+ }
+ sprintf(buf, "%cx86%s %cx Core/Bus Clock",
+ "??56G"[nr>>4],
+ s,
+ "12??43"[nr & 0x05]);
+ } else if (nr >= 0x50 && nr <= 0x5f) { /* Cyrix 6x86MX */
+ sprintf(buf, "6x86MX %c%sx Core/Bus Clock",
+ "12233445"[nr & 0x07],
+ (nr && !(nr&1)) ? ".5" : "");
+ } else if (nr >= 0xfd && c->cpuid_level < 0) {
+ /* Probably 0xfd (Cx486[SD]LC with no ID register)
+ * or 0xfe (Cx486 A step with no ID register).
+ */
+ strcpy(buf, "Cx486");
+ } else
+ return 0; /* Use CPUID if applicable */
+ return 1;
}
-static const char * k5model(unsigned int nr)
+__initfunc(static int amd_model(struct cpuinfo_x86 *c))
{
- static const char *model[] = {
- "SSA5 (PR-75, PR-90, PR-100)", "5k86 (PR-120, PR-133)",
- "5k86 (PR-166)", "5k86 (PR-200)", "", "",
- "K6(PR-133..PR-166)","K6(PR-133..PR-200)"
- };
- if (nr < sizeof(model)/sizeof(char *))
- return model[nr];
- return NULL;
+ unsigned int n, dummy, *v;
+
+ /* Actually we must have cpuid or we could never have
+ * figured out that this was AMD from the vendor info :-).
+ */
+
+ cpuid(0x80000000, &n, &dummy, &dummy, &dummy);
+ if (n < 4)
+ return 0;
+ v = (unsigned int *) c->x86_model_id;
+ cpuid(0x80000002, &v[0], &v[1], &v[2], &v[3]);
+ cpuid(0x80000003, &v[4], &v[5], &v[6], &v[7]);
+ cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]);
+ c->x86_model_id[48] = 0;
+ return 1;
}
-static const char * i686model(unsigned int nr)
+__initfunc(void get_cpu_vendor(struct cpuinfo_x86 *c))
{
- static const char *model[] = {
- "PPro A-step", "Pentium Pro", "2", "Pentium II"
- };
- if (nr < sizeof(model)/sizeof(char *))
- return model[nr];
- return NULL;
+ char *v = c->x86_vendor_id;
+
+ if (!strcmp(v, "GenuineIntel"))
+ c->x86_vendor = X86_VENDOR_INTEL;
+ else if (!strcmp(v, "AuthenticAMD"))
+ c->x86_vendor = X86_VENDOR_AMD;
+ else if (!strncmp(v, "Cyrix", 5))
+ c->x86_vendor = X86_VENDOR_CYRIX;
+ else if (!strcmp(v, "UMC UMC UMC "))
+ c->x86_vendor = X86_VENDOR_UMC;
+ else if (!strcmp(v, "CentaurHauls"))
+ c->x86_vendor = X86_VENDOR_CENTAUR;
+ else if (!strcmp(v, "NexGenDriven"))
+ c->x86_vendor = X86_VENDOR_NEXGEN;
+ else
+ c->x86_vendor = X86_VENDOR_UNKNOWN;
}
-static const char * getmodel(int x86, int model)
+struct cpu_model_info {
+ int vendor;
+ int x86;
+ char *model_names[16];
+};
+
+static struct cpu_model_info cpu_models[] __initdata = {
+ { X86_VENDOR_INTEL, 4,
+ { "486 DX-25/33", "486 DX-50", "486 SX", "486 DX/2", "486 SL",
+ "486 SX/2", NULL, "486 DX/2-WB", "486 DX/4", "486 DX/4-WB", NULL,
+ NULL, NULL, NULL, NULL, NULL }},
+ { X86_VENDOR_INTEL, 5,
+ { "Pentium 60/66 A-step", "Pentium 60/66", "Pentium 75+",
+ "OverDrive PODP5V83", "Pentium MMX", NULL, NULL,
+ "Mobile Pentium 75+", "Mobile Pentium MMX", NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL }},
+ { X86_VENDOR_INTEL, 6,
+ { "Pentium Pro A-step", "Pentium Pro", NULL, "Pentium II (Klamath)",
+ NULL, "Pentium II (Deschutes)", NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL }},
+ { X86_VENDOR_CYRIX, 4,
+ { NULL, NULL, NULL, NULL, "MediaGX", NULL, NULL, NULL, NULL, "5x86",
+ NULL, NULL, NULL, NULL, NULL, NULL }},
+ { X86_VENDOR_CYRIX, 5,
+ { NULL, NULL, "6x86", NULL, "GXm", NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL }},
+ { X86_VENDOR_CYRIX, 6,
+ { "6x86MX", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL }},
+ { X86_VENDOR_AMD, 4,
+ { NULL, NULL, NULL, "DX/2", NULL, NULL, NULL, "DX/2-WB", "DX/4",
+ "DX/4-WB", NULL, NULL, NULL, NULL, "Am5x86-WT", "Am5x86-WB" }},
+ { X86_VENDOR_AMD, 5,
+ { "K5/SSA5 (PR-75, PR-90, PR-100)", "K5 (PR-120, PR-133)",
+ "K5 (PR-166)", "K5 (PR-200)", NULL, NULL,
+ "K6 (166 - 266)", "K6 (166 - 300)", "K6-3D (200 - 450)",
+ "K6-3D-Plus (200 - 450)", NULL, NULL, NULL, NULL, NULL, NULL }},
+ { X86_VENDOR_UMC, 4,
+ { NULL, "U5D", "U5S", NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL }},
+ { X86_VENDOR_CENTAUR, 5,
+ { NULL, NULL, NULL, NULL, "C6", NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL }},
+ { X86_VENDOR_NEXGEN, 5,
+ { "Nx586", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL }},
+};
+
+__initfunc(void identify_cpu(struct cpuinfo_x86 *c))
{
- const char *p = NULL;
- static char nbuf[12];
- switch (x86) {
- case 4:
- p = i486model(model);
- break;
- case 5:
- if(strcmp(x86_vendor_id, "AuthenticAMD") == 0){
- p = k5model(model);
- } else {
- p = i586model(model);
+ int i;
+ char *p = NULL;
+
+ c->loops_per_sec = loops_per_sec;
+
+ get_cpu_vendor(c);
+
+ if (c->x86_vendor == X86_VENDOR_UNKNOWN &&
+ c->cpuid_level < 0)
+ return;
+
+ if (c->x86_vendor == X86_VENDOR_CYRIX && cyrix_model(c))
+ return;
+
+ if (c->x86_model < 16)
+ for (i=0; i<sizeof(cpu_models)/sizeof(struct cpu_model_info); i++)
+ if (cpu_models[i].vendor == c->x86_vendor &&
+ cpu_models[i].x86 == c->x86) {
+ p = cpu_models[i].model_names[c->x86_model];
+ break;
}
- break;
- case 6:
- p = i686model(model);
- break;
+ if (p) {
+ strcpy(c->x86_model_id, p);
+ return;
}
- if (p)
- return p;
- sprintf(nbuf, "%d", model);
- return nbuf;
+ if (c->x86_vendor == X86_VENDOR_AMD && amd_model(c))
+ return;
+
+ sprintf(c->x86_model_id, "%02x/%02x", c->x86_vendor, c->x86_model);
+}
+
+static char *cpu_vendor_names[] __initdata = {
+ "Intel", "Cyrix", "AMD", "UMC", "NexGen", "Centaur" };
+
+__initfunc(void print_cpu_info(struct cpuinfo_x86 *c))
+{
+ char *vendor = NULL;
+
+ if (c->x86_vendor < sizeof(cpu_vendor_names)/sizeof(char *))
+ vendor = cpu_vendor_names[c->x86_vendor];
+ else if (c->cpuid_level >= 0)
+ vendor = c->x86_vendor_id;
+
+ if (vendor)
+ printk("%s ", vendor);
+
+ if (!c->x86_model_id[0])
+ printk("%d86", c->x86);
+ else
+ printk("%s", c->x86_model_id);
+
+ if (c->x86_mask)
+ printk(" stepping %02x", c->x86_mask);
+
+ printk("\n");
}
+/*
+ * Get CPU information for use by the procfs.
+ */
+
int get_cpuinfo(char * buffer)
{
- int i, len = 0;
+ char *p = buffer;
int sep_bug;
static const char *x86_cap_flags[] = {
"fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce",
"cx8", "apic", "10", "sep", "mtrr", "pge", "mca", "cmov",
- "16", "17", "18", "19", "20", "21", "22", "mmx",
- "24", "25", "26", "27", "28", "29", "30", "31"
+ "fcmov", "17", "18", "19", "20", "21", "22", "mmx",
+ "osfxsr", "25", "26", "27", "28", "29", "30", "amd3d"
};
-
-#ifdef __SMP__
- int n;
-
-#define CD(X) (cpu_data[n].X)
-/* SMP has the wrong name for loops_per_sec */
-#define loops_per_sec udelay_val
-#define CPUN n
-
- for ( n = 0 ; n < 32 ; n++ ) {
- if ( cpu_present_map & (1<<n) ) {
- if (len) buffer[len++] = '\n';
+ struct cpuinfo_x86 *c = cpu_data;
+ int i, n;
-#else
-#define CD(X) (X)
-#define CPUN 0
-#endif
-
- len += sprintf(buffer+len,"processor\t: %d\n"
- "cpu\t\t: %c86\n"
- "model\t\t: %s\n"
- "vendor_id\t: %s\n",
- CPUN,
- CD(x86)+'0',
- CD(have_cpuid) ?
- getmodel(CD(x86), CD(x86_model)) :
- "unknown",
- CD(x86_vendor_id));
-
- if (CD(x86_mask))
- len += sprintf(buffer+len,
- "stepping\t: %d\n",
- CD(x86_mask));
- else
- len += sprintf(buffer+len,
- "stepping\t: unknown\n");
-
- sep_bug = CD(have_cpuid) &&
- (CD(x86_capability) & 0x800) &&
- !memcmp(x86_vendor_id, "GenuineIntel", 12) &&
- CD(x86) == 6 &&
- CD(x86_model) < 3 &&
- CD(x86_mask) < 3;
-
- len += sprintf(buffer+len,
- "fdiv_bug\t: %s\n"
- "hlt_bug\t\t: %s\n"
- "sep_bug\t\t: %s\n"
- "f00f_bug\t: %s\n"
- "fpu\t\t: %s\n"
- "fpu_exception\t: %s\n"
- "cpuid\t\t: %s\n"
- "wp\t\t: %s\n"
- "flags\t\t:",
- CD(fdiv_bug) ? "yes" : "no",
- CD(hlt_works_ok) ? "no" : "yes",
- sep_bug ? "yes" : "no",
- pentium_f00f_bug ? "yes" : "no",
- CD(hard_math) ? "yes" : "no",
- (CD(hard_math) && ignore_irq13)
- ? "yes" : "no",
- CD(have_cpuid) ? "yes" : "no",
- CD(wp_works_ok) ? "yes" : "no");
-
- for ( i = 0 ; i < 32 ; i++ ) {
- if ( CD(x86_capability) & (1 << i) ) {
- len += sprintf(buffer+len, " %s",
- x86_cap_flags[i]);
- }
- }
- len += sprintf(buffer+len,
- "\nbogomips\t: %lu.%02lu\n",
- CD(loops_per_sec+2500)/500000,
- (CD(loops_per_sec+2500)/5000) % 100);
+ for(n=0; n<NR_CPUS; n++, c++) {
#ifdef __SMP__
- }
- }
+ if (!(cpu_present_map & (1<<n)))
+ continue;
#endif
- return len;
+ p += sprintf(p, "processor\t: %d\n"
+ "cpu family\t: %c\n"
+ "model\t\t: %s\n"
+ "vendor_id\t: %s\n",
+ n,
+ c->x86 + '0',
+ c->x86_model_id[0] ? c->x86_model_id : "unknown",
+ c->x86_vendor_id[0] ? c->x86_vendor_id : "unknown");
+ if (c->x86_mask) {
+ if (c->x86_vendor == X86_VENDOR_CYRIX)
+ p += sprintf(p, "stepping\t: %d rev %d\n",
+ c->x86_mask >> 4,
+ c->x86_mask & 0x0f);
+ else
+ p += sprintf(p, "stepping\t: %d\n", c->x86_mask);
+ } else
+ p += sprintf(p, "stepping\t: unknown\n");
+
+ sep_bug = c->x86_vendor == X86_VENDOR_INTEL &&
+ c->x86 == 0x06 &&
+ c->cpuid_level >= 0 &&
+ (c->x86_capability & 0x800) &&
+ c->x86_model < 3 &&
+ c->x86_mask < 3;
+
+ p += sprintf(p, "fdiv_bug\t: %s\n"
+ "hlt_bug\t\t: %s\n"
+ "sep_bug\t\t: %s\n"
+ "f00f_bug\t: %s\n"
+ "fpu\t\t: %s\n"
+ "fpu_exception\t: %s\n"
+ "cpuid level\t: %d\n"
+ "wp\t\t: %s\n"
+ "flags\t\t:",
+ c->fdiv_bug ? "yes" : "no",
+ c->hlt_works_ok ? "no" : "yes",
+ sep_bug ? "yes" : "no",
+ c->f00f_bug ? "yes" : "no",
+ c->hard_math ? "yes" : "no",
+ (c->hard_math && ignore_irq13) ? "yes" : "no",
+ c->cpuid_level,
+ c->wp_works_ok ? "yes" : "no");
+
+ for ( i = 0 ; i < 32 ; i++ )
+ if ( c->x86_capability & (1 << i) )
+ p += sprintf(p, " %s", x86_cap_flags[i]);
+ p += sprintf(p, "\nbogomips\t: %lu.%02lu\n\n",
+ (c->loops_per_sec+2500)/500000,
+ ((c->loops_per_sec+2500)/5000) % 100);
+ }
+ return p - buffer;
}
diff --git a/arch/i386/kernel/signal.c b/arch/i386/kernel/signal.c
index 853b82100..12a777b5c 100644
--- a/arch/i386/kernel/signal.c
+++ b/arch/i386/kernel/signal.c
@@ -165,7 +165,7 @@ static inline void restore_i387(struct _fpstate *buf)
#ifndef CONFIG_MATH_EMULATION
restore_i387_hard(buf);
#else
- if (hard_math)
+ if (boot_cpu_data.hard_math)
restore_i387_hard(buf);
else
restore_i387_soft(&current->tss.i387.soft, buf);
@@ -325,7 +325,7 @@ static struct _fpstate * save_i387(struct _fpstate *buf)
#ifndef CONFIG_MATH_EMULATION
return save_i387_hard(buf);
#else
- return hard_math ? save_i387_hard(buf)
+ return boot_cpu_data.hard_math ? save_i387_hard(buf)
: save_i387_soft(&current->tss.i387.soft, buf);
#endif
}
@@ -365,21 +365,33 @@ setup_sigcontext(struct sigcontext *sc, struct _fpstate *fpstate,
/* non-iBCS2 extensions.. */
__put_user(mask, &sc->oldmask);
__put_user(current->tss.cr2, &sc->cr2);
-}
+}
+
+/*
+ * Determine which stack to use..
+ */
+static inline unsigned long sigstack_esp(struct k_sigaction *ka, struct pt_regs * regs)
+{
+ unsigned long esp;
+
+ /* Default to using normal stack */
+ esp = regs->esp;
+
+ /* This is the legacy signal stack switching. */
+ if ((regs->xss & 0xffff) != __USER_DS &&
+ !(ka->sa.sa_flags & SA_RESTORER) &&
+ ka->sa.sa_restorer)
+ esp = (unsigned long) ka->sa.sa_restorer;
+
+ return esp;
+}
static void setup_frame(int sig, struct k_sigaction *ka,
sigset_t *set, struct pt_regs * regs)
{
struct sigframe *frame;
- frame = (struct sigframe *)((regs->esp - sizeof(*frame)) & -8);
-
- /* XXX: Check here if we need to switch stacks.. */
-
- /* This is legacy signal stack switching. */
- if ((regs->xss & 0xffff) != __USER_DS
- && !(ka->sa.sa_flags & SA_RESTORER) && ka->sa.sa_restorer)
- frame = (struct sigframe *) ka->sa.sa_restorer;
+ frame = (struct sigframe *)((sigstack_esp(ka, regs) - sizeof(*frame)) & -8);
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
goto segv_and_exit;
@@ -416,7 +428,7 @@ static void setup_frame(int sig, struct k_sigaction *ka,
{
unsigned long seg = __USER_DS;
__asm__("mov %w0,%%fs ; mov %w0,%%gs": "=r"(seg) : "0"(seg));
- set_fs(MAKE_MM_SEG(seg));
+ set_fs(USER_DS);
regs->xds = seg;
regs->xes = seg;
regs->xss = seg;
@@ -441,14 +453,7 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
{
struct rt_sigframe *frame;
- frame = (struct rt_sigframe *)((regs->esp - sizeof(*frame)) & -8);
-
- /* XXX: Check here if we need to switch stacks.. */
-
- /* This is legacy signal stack switching. */
- if ((regs->xss & 0xffff) != __USER_DS
- && !(ka->sa.sa_flags & SA_RESTORER) && ka->sa.sa_restorer)
- frame = (struct rt_sigframe *) ka->sa.sa_restorer;
+ frame = (struct rt_sigframe *)((sigstack_esp(ka, regs) - sizeof(*frame)) & -8);
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
goto segv_and_exit;
@@ -488,7 +493,7 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
{
unsigned long seg = __USER_DS;
__asm__("mov %w0,%%fs ; mov %w0,%%gs": "=r"(seg) : "0"(seg));
- set_fs(MAKE_MM_SEG(seg));
+ set_fs(USER_DS);
regs->xds = seg;
regs->xes = seg;
regs->xss = seg;
diff --git a/arch/i386/kernel/smp.c b/arch/i386/kernel/smp.c
index eef030375..6f4bc60ec 100644
--- a/arch/i386/kernel/smp.c
+++ b/arch/i386/kernel/smp.c
@@ -25,6 +25,7 @@
* Alan Cox : Dumb bug: 'B' step PPro's are fine
* Ingo Molnar : Added APIC timers, based on code
* from Jose Renau
+ * Alan Cox : Added EBDA scanning
*/
#include <linux/kernel.h>
@@ -51,10 +52,12 @@
#include "irq.h"
+spinlock_t semaphore_wake_lock = SPIN_LOCK_UNLOCKED;
+
extern unsigned long start_kernel, _etext;
extern void update_one_process( struct task_struct *p,
unsigned long ticks, unsigned long user,
- unsigned long system);
+ unsigned long system, int cpu);
/*
* Some notes on processor bugs:
*
@@ -114,7 +117,7 @@ unsigned long cpu_present_map = 0; /* Bitmask of existing CPU's */
int smp_num_cpus = 1; /* Total count of live CPU's */
int smp_threads_ready=0; /* Set when the idlers are all forked */
volatile int cpu_number_map[NR_CPUS]; /* which CPU maps to which logical number */
-volatile int cpu_logical_map[NR_CPUS]; /* which logical number maps to which CPU */
+volatile int __cpu_logical_map[NR_CPUS]; /* which logical number maps to which CPU */
volatile unsigned long cpu_callin_map[NR_CPUS] = {0,}; /* We always use 0 the rest is ready for parallel delivery */
volatile unsigned long smp_invalidate_needed; /* Used for the invalidate map that's also checked in the spinlock */
volatile unsigned long kstack_ptr; /* Stack vector for booting CPU's */
@@ -138,28 +141,19 @@ volatile unsigned long kernel_counter=0; /* Number of times the processor holds
volatile unsigned long syscall_count=0; /* Number of times the processor holds the syscall lock */
volatile unsigned long ipi_count; /* Number of IPI's delivered */
-#ifdef __SMP_PROF__
-volatile unsigned long smp_spins[NR_CPUS]={0}; /* Count interrupt spins */
-volatile unsigned long smp_spins_syscall[NR_CPUS]={0}; /* Count syscall spins */
-volatile unsigned long smp_spins_syscall_cur[NR_CPUS]={0};/* Count spins for the actual syscall */
-volatile unsigned long smp_spins_sys_idle[NR_CPUS]={0}; /* Count spins for sys_idle */
-volatile unsigned long smp_idle_count[1+NR_CPUS]={0,}; /* Count idle ticks */
-
-/* Count local APIC timer ticks */
-volatile unsigned long smp_local_timer_ticks[1+NR_CPUS]={0,};
-
-#endif
-#if defined (__SMP_PROF__)
-volatile unsigned long smp_idle_map=0; /* Map for idle processors */
-#endif
volatile unsigned long smp_proc_in_lock[NR_CPUS] = {0,};/* for computing process time */
volatile int smp_process_available=0;
const char lk_lockmsg[] = "lock from interrupt context at %p\n";
+int mp_bus_id_to_type [MAX_MP_BUSSES] = { -1, };
+extern int mp_irq_entries;
+extern struct mpc_config_intsrc mp_irqs [MAX_IRQ_SOURCES];
+int mp_bus_id_to_pci_bus [MAX_MP_BUSSES] = { -1, };
+int mp_current_pci_id = 0;
-/*#define SMP_DEBUG*/
+/* #define SMP_DEBUG */
#ifdef SMP_DEBUG
#define SMP_PRINTK(x) printk x
@@ -186,7 +180,7 @@ __initfunc(void smp_setup(char *str, int *ints))
max_cpus = 0;
}
-static inline void ack_APIC_irq (void)
+void ack_APIC_irq (void)
{
/* Clear the IPI */
@@ -268,10 +262,14 @@ __initfunc(static int smp_read_mpc(struct mp_config_table *mpc))
}
memcpy(str,mpc->mpc_oem,8);
str[8]=0;
+ memcpy(ioapic_OEM_ID,str,9);
printk("OEM ID: %s ",str);
+
memcpy(str,mpc->mpc_productid,12);
str[12]=0;
+ memcpy(ioapic_Product_ID,str,13);
printk("Product ID: %s ",str);
+
printk("APIC at: 0x%lX\n",mpc->mpc_lapic);
/* set the local APIC address */
@@ -337,6 +335,18 @@ __initfunc(static int smp_read_mpc(struct mp_config_table *mpc))
SMP_PRINTK(("Bus #%d is %s\n",
m->mpc_busid,
str));
+ if ((strncmp(m->mpc_bustype,"ISA",3) == 0) ||
+ (strncmp(m->mpc_bustype,"EISA",4) == 0))
+ mp_bus_id_to_type[m->mpc_busid] =
+ MP_BUS_ISA;
+ else
+ if (strncmp(m->mpc_bustype,"PCI",3) == 0) {
+ mp_bus_id_to_type[m->mpc_busid] =
+ MP_BUS_PCI;
+ mp_bus_id_to_pci_bus[m->mpc_busid] =
+ mp_current_pci_id;
+ mp_current_pci_id++;
+ }
mpt+=sizeof(*m);
count+=sizeof(*m);
break;
@@ -362,6 +372,13 @@ __initfunc(static int smp_read_mpc(struct mp_config_table *mpc))
struct mpc_config_intsrc *m=
(struct mpc_config_intsrc *)mpt;
+ mp_irqs [mp_irq_entries] = *m;
+ if (++mp_irq_entries == MAX_IRQ_SOURCES) {
+ printk("Max irq sources exceeded!!\n");
+ printk("Skipping remaining sources.\n");
+ --mp_irq_entries;
+ }
+
mpt+=sizeof(*m);
count+=sizeof(*m);
break;
@@ -460,7 +477,7 @@ __initfunc(int smp_scan_config(unsigned long base, unsigned long length))
cpu_present_map=3;
num_processors=2;
printk("I/O APIC at 0xFEC00000.\n");
- printk("Bus#0 is ");
+ printk("Bus #0 is ");
}
switch(mpf->mpf_feature1)
{
@@ -513,7 +530,7 @@ __initfunc(int smp_scan_config(unsigned long base, unsigned long length))
* set some other information about it.
*/
nlong = boot_cpu_id<<24; /* Dummy 'self' for bootup */
- cpu_logical_map[0] = boot_cpu_id;
+ __cpu_logical_map[0] = boot_cpu_id;
global_irq_holder = boot_cpu_id;
current->processor = boot_cpu_id;
@@ -558,7 +575,7 @@ __initfunc(static unsigned long setup_trampoline(void))
__initfunc(unsigned long smp_alloc_memory(unsigned long mem_base))
{
if (virt_to_phys((void *)mem_base) >= 0x9F000)
- panic("smp_alloc_memory: Insufficient low memory for kernel trampoline 0x%lx.\n", mem_base);
+ panic("smp_alloc_memory: Insufficient low memory for kernel trampoline 0x%lx.", mem_base);
trampoline_base = (void *)mem_base;
return mem_base + PAGE_SIZE;
}
@@ -571,22 +588,17 @@ __initfunc(unsigned long smp_alloc_memory(unsigned long mem_base))
__initfunc(void smp_store_cpu_info(int id))
{
struct cpuinfo_x86 *c=&cpu_data[id];
- c->hard_math=hard_math; /* Always assumed same currently */
- c->x86=x86;
- c->x86_model=x86_model;
- c->x86_mask=x86_mask;
+
+ *c = boot_cpu_data;
+ identify_cpu(c);
/*
* Mask B, Pentium, but not Pentium MMX
*/
- if(x86_mask>=1 && x86_mask<=4 && x86==5 && (x86_model>=0&&x86_model<=3))
+ if (c->x86_vendor == X86_VENDOR_INTEL &&
+ c->x86 == 5 &&
+ c->x86_mask >= 1 && c->x86_mask <= 4 &&
+ c->x86_model <= 3)
smp_b_stepping=1; /* Remember we have B step Pentia with bugs */
- c->x86_capability=x86_capability;
- c->fdiv_bug=fdiv_bug;
- c->wp_works_ok=wp_works_ok; /* Always assumed the same currently */
- c->hlt_works_ok=hlt_works_ok;
- c->have_cpuid=have_cpuid;
- c->udelay_val=loops_per_sec;
- strcpy(c->x86_vendor_id, x86_vendor_id);
}
/*
@@ -626,7 +638,7 @@ __initfunc(void smp_callin(void))
/*
* Set up our APIC timer.
*/
- setup_APIC_clock ();
+ setup_APIC_clock();
sti();
/*
@@ -706,10 +718,10 @@ __initfunc(static void do_boot_cpu(int i))
idle = task[cpucount];
if (!idle)
- panic("No idle process for CPU %d\n", i);
+ panic("No idle process for CPU %d", i);
idle->processor = i;
- cpu_logical_map[cpucount] = i;
+ __cpu_logical_map[cpucount] = i;
cpu_number_map[i] = cpucount;
/* start_eip had better be page-aligned! */
@@ -853,8 +865,11 @@ __initfunc(static void do_boot_cpu(int i))
/* number CPUs logically, starting from 1 (BSP is 0) */
#if 0
cpu_number_map[i] = cpucount;
- cpu_logical_map[cpucount] = i;
+ __cpu_logical_map[cpucount] = i;
#endif
+ printk("OK.\n");
+ printk("CPU%d: ", i);
+ print_cpu_info(&cpu_data[i]);
}
else
{
@@ -863,8 +878,14 @@ __initfunc(static void do_boot_cpu(int i))
else
printk("Not responding.\n");
}
- }
SMP_PRINTK(("CPU has booted.\n"));
+ }
+ else
+ {
+ __cpu_logical_map[cpucount] = -1;
+ cpu_number_map[i] = -1;
+ cpucount--;
+ }
swapper_pg_dir[0]=maincfg;
local_flush_tlb();
@@ -901,28 +922,34 @@ __initfunc(void smp_boot_cpus(void))
*/
smp_store_cpu_info(boot_cpu_id); /* Final full version of the data */
+ printk("CPU%d: ", boot_cpu_id);
+ print_cpu_info(&cpu_data[boot_cpu_id]);
cpu_present_map |= (1 << hard_smp_processor_id());
cpu_number_map[boot_cpu_id] = 0;
active_kernel_processor=boot_cpu_id;
/*
- * If SMP should be disabled, then really disable it!
+ * If we don't conform to the Intel MPS standard, get out
+ * of here now!
*/
- if (!max_cpus && smp_found_config)
+ if (!smp_found_config)
{
- smp_found_config = 0;
- printk("SMP mode deactivated, forcing use of dummy APIC emulation.\n");
+ printk(KERN_NOTICE "SMP motherboard not detected. Using dummy APIC emulation.\n");
+ io_apic_irqs = 0;
+ return;
}
/*
- * If we don't conform to the Intel MPS standard, get out
- * of here now!
+ * If SMP should be disabled, then really disable it!
*/
- if (!smp_found_config)
- return;
+ if (!max_cpus)
+ {
+ smp_found_config = 0;
+ printk(KERN_INFO "SMP mode deactivated, forcing use of dummy APIC emulation.\n");
+ }
/*
* Map the local APIC into kernel space
@@ -931,7 +958,7 @@ __initfunc(void smp_boot_cpus(void))
apic_reg = ioremap(apic_addr,4096);
if(apic_reg == NULL)
- panic("Unable to map local apic.\n");
+ panic("Unable to map local apic.");
#ifdef SMP_DEBUG
{
@@ -1010,9 +1037,11 @@ __initfunc(void smp_boot_cpus(void))
* Make sure we unmap all failed CPUs
*/
- if (cpu_number_map[i] == -1)
+ if (cpu_number_map[i] == -1 && (cpu_present_map & (1 << i))) {
+ printk("CPU #%d not responding. Removing from cpu_present_map.\n",i);
cpu_present_map &= ~(1 << i);
- }
+ }
+ }
/*
* Cleanup possible dangling ends...
@@ -1049,7 +1078,7 @@ __initfunc(void smp_boot_cpus(void))
SMP_PRINTK(("Before bogomips.\n"));
if(cpucount==0)
{
- printk("Error: only one processor found.\n");
+ printk(KERN_ERR "Error: only one processor found.\n");
cpu_present_map=(1<<hard_smp_processor_id());
}
else
@@ -1058,9 +1087,9 @@ __initfunc(void smp_boot_cpus(void))
for(i=0;i<32;i++)
{
if(cpu_present_map&(1<<i))
- bogosum+=cpu_data[i].udelay_val;
+ bogosum+=cpu_data[i].loops_per_sec;
}
- printk("Total of %d processors activated (%lu.%02lu BogoMIPS).\n",
+ printk(KERN_INFO "Total of %d processors activated (%lu.%02lu BogoMIPS).\n",
cpucount+1,
(bogosum+2500)/500000,
((bogosum+2500)/5000)%100);
@@ -1069,8 +1098,14 @@ __initfunc(void smp_boot_cpus(void))
smp_num_cpus=cpucount+1;
}
if(smp_b_stepping)
- printk("WARNING: SMP operation may be unreliable with B stepping processors.\n");
+ printk(KERN_WARNING "WARNING: SMP operation may be unreliable with B stepping processors.\n");
SMP_PRINTK(("Boot done.\n"));
+
+ /*
+ * Here we can be sure that there is an IO-APIC in the system, lets
+ * go and set it up:
+ */
+ setup_IO_APIC();
}
/*
@@ -1340,37 +1375,31 @@ void smp_local_timer_interrupt(struct pt_regs * regs)
else
system=1;
- irq_enter(cpu, 0);
+ irq_enter(cpu, 0);
if (p->pid) {
-
- update_one_process(p, 1, user, system);
+ update_one_process(p, 1, user, system, cpu);
p->counter -= 1;
if (p->counter < 0) {
p->counter = 0;
need_resched = 1;
}
- if (p->priority < DEF_PRIORITY)
+ if (p->priority < DEF_PRIORITY) {
kstat.cpu_nice += user;
- else
+ kstat.per_cpu_nice[cpu] += user;
+ } else {
kstat.cpu_user += user;
+ kstat.per_cpu_user[cpu] += user;
+ }
kstat.cpu_system += system;
+ kstat.per_cpu_system[cpu] += system;
- } else {
-#ifdef __SMP_PROF__
- if (test_bit(cpu,&smp_idle_map))
- smp_idle_count[cpu]++;
-#endif
}
prof_counter[cpu]=prof_multiplier[cpu];
-
irq_exit(cpu, 0);
}
-#ifdef __SMP_PROF__
- smp_local_timer_ticks[cpu]++;
-#endif
/*
* We take the 'long' return path, and there every subsystem
* grabs the apropriate locks (kernel lock/ irq lock).
@@ -1378,7 +1407,7 @@ void smp_local_timer_interrupt(struct pt_regs * regs)
* we might want to decouple profiling from the 'long path',
* and do the profiling totally in assembly.
*
- * Currently this isnt too much of an issue (performancewise),
+ * Currently this isnt too much of an issue (performance wise),
* we can take more than 100K local irqs per second on a 100 MHz P5.
*/
}
@@ -1399,7 +1428,9 @@ void smp_apic_timer_interrupt(struct pt_regs * regs)
* want to be able to accept NMI tlb invalidates
* during this time.
*/
+ spin_lock(&irq_controller_lock);
ack_APIC_irq ();
+ spin_unlock(&irq_controller_lock);
smp_local_timer_interrupt(regs);
}
@@ -1416,8 +1447,9 @@ asmlinkage void smp_reschedule_interrupt(void)
* This looks silly, but we actually do need to wait
* for the global interrupt lock.
*/
+ printk("huh, this is used, where???\n");
irq_enter(cpu, 0);
- need_resched=1;
+ need_resched = 1;
irq_exit(cpu, 0);
}
@@ -1451,7 +1483,7 @@ asmlinkage void smp_stop_cpu_interrupt(void)
* closely follows bus clocks.
*/
-#define RTDSC(x) __asm__ __volatile__ ( ".byte 0x0f,0x31" \
+#define RDTSC(x) __asm__ __volatile__ ( "rdtsc" \
:"=a" (((unsigned long*)&x)[0]), \
"=d" (((unsigned long*)&x)[1]))
@@ -1576,7 +1608,7 @@ __initfunc(int calibrate_APIC_clock (void))
/*
* We wrapped around just now, lets start:
*/
- RTDSC(t1);
+ RDTSC(t1);
tt1=apic_read(APIC_TMCCT);
#define LOOPS (HZ/10)
@@ -1587,7 +1619,7 @@ __initfunc(int calibrate_APIC_clock (void))
wait_8254_wraparound ();
tt2=apic_read(APIC_TMCCT);
- RTDSC(t2);
+ RDTSC(t2);
/*
* The APIC bus clock counter is 32 bits only, it
@@ -1696,6 +1728,3 @@ int setup_profiling_timer (unsigned int multiplier)
}
#undef APIC_DIVISOR
-#undef RTDSC
-
-
diff --git a/arch/i386/kernel/sys_i386.c b/arch/i386/kernel/sys_i386.c
index 80ef4be47..b89b5f89b 100644
--- a/arch/i386/kernel/sys_i386.c
+++ b/arch/i386/kernel/sys_i386.c
@@ -65,14 +65,17 @@ asmlinkage int old_mmap(struct mmap_arg_struct *arg)
lock_kernel();
if (copy_from_user(&a, arg, sizeof(a)))
- goto out;
+ goto out;
if (!(a.flags & MAP_ANONYMOUS)) {
error = -EBADF;
- if (a.fd >= NR_OPEN || !(file = current->files->fd[a.fd]))
+ file = fget(a.fd);
+ if (!file)
goto out;
}
a.flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
error = do_mmap(file, a.addr, a.len, a.prot, a.flags, a.offset);
+ if (file)
+ fput(file);
out:
unlock_kernel();
return error;
diff --git a/arch/i386/kernel/time.c b/arch/i386/kernel/time.c
index a08c9c49c..a05292d6d 100644
--- a/arch/i386/kernel/time.c
+++ b/arch/i386/kernel/time.c
@@ -109,8 +109,7 @@ static unsigned long do_fast_gettimeoffset(void)
}
/* Read the time counter */
- __asm__(".byte 0x0f,0x31"
- :"=a" (eax), "=d" (edx));
+ __asm__("rdtsc" : "=a" (eax), "=d" (edx));
/* .. relative to previous jiffy (32 bits is enough) */
edx = 0;
@@ -249,6 +248,7 @@ static unsigned long do_slow_gettimeoffset(void)
return count;
}
+#ifndef CONFIG_APM
/*
* this is only used if we have fast gettimeoffset:
*/
@@ -256,6 +256,7 @@ static void do_x86_get_fast_time(struct timeval * tv)
{
do_gettimeofday(tv);
}
+#endif
static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset;
@@ -437,7 +438,7 @@ static inline void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
static void pentium_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
/* read Pentium cycle counter */
- __asm__(".byte 0x0f,0x31"
+ __asm__("rdtsc"
:"=a" (last_timer_cc.low),
"=d" (last_timer_cc.high));
timer_interrupt(irq, NULL, regs);
@@ -528,26 +529,24 @@ __initfunc(void time_init(void))
/* Don't use them if a suspend/resume could
corrupt the timer value. This problem
needs more debugging. */
- if (x86_capability & 16) {
+ if (boot_cpu_data.x86_capability & 16) {
do_gettimeoffset = do_fast_gettimeoffset;
do_get_fast_time = do_x86_get_fast_time;
- if( strcmp( x86_vendor_id, "AuthenticAMD" ) == 0 ) {
- if( x86 == 5 ) {
- if( x86_model == 0 ) {
- /* turn on cycle counters during power down */
- __asm__ __volatile__ (" movl $0x83, %%ecx \n \
- .byte 0x0f,0x32 \n \
- orl $1,%%eax \n \
- .byte 0x0f,0x30 \n "
- : : : "ax", "cx", "dx" );
- udelay(500);
- }
- }
+ if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD &&
+ boot_cpu_data.x86 == 5 &&
+ boot_cpu_data.x86_model == 0) {
+ /* turn on cycle counters during power down */
+ __asm__ __volatile__ (" movl $0x83, %%ecx \n \
+ rdmsr \n \
+ orl $1,%%eax \n \
+ wrmsr \n "
+ : : : "ax", "cx", "dx" );
+ udelay(500);
}
/* read Pentium cycle counter */
- __asm__(".byte 0x0f,0x31"
+ __asm__("rdtsc"
:"=a" (init_timer_cc.low),
"=d" (init_timer_cc.high));
irq0.handler = pentium_timer_interrupt;
diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c
index bd125a041..fdcf951f3 100644
--- a/arch/i386/kernel/traps.c
+++ b/arch/i386/kernel/traps.c
@@ -6,9 +6,7 @@
/*
* 'Traps.c' handles hardware traps and faults after we have saved some
- * state in 'asm.s'. Currently mostly a debugging-aid, will be extended
- * to mainly kill the offending process (probably by giving it a signal,
- * but possibly by killing it outright if necessary).
+ * state in 'asm.s'.
*/
#include <linux/config.h>
#include <linux/head.h>
@@ -17,7 +15,6 @@
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
-#include <linux/config.h>
#include <linux/timer.h>
#include <linux/mm.h>
#include <linux/smp.h>
diff --git a/arch/i386/kernel/vm86.c b/arch/i386/kernel/vm86.c
index 0628e2d68..5ae87b06a 100644
--- a/arch/i386/kernel/vm86.c
+++ b/arch/i386/kernel/vm86.c
@@ -122,7 +122,7 @@ static void mark_screen_rdonly(struct task_struct * tsk)
-static do_vm86_irq_handling(int subfunction, int irqnumber);
+static int do_vm86_irq_handling(int subfunction, int irqnumber);
static void do_sys_vm86(struct kernel_vm86_struct *info, struct task_struct *tsk);
asmlinkage int sys_vm86old(struct vm86_struct * v86)
diff --git a/arch/i386/lib/.cvsignore b/arch/i386/lib/.cvsignore
index 4671378ae..857dd22e9 100644
--- a/arch/i386/lib/.cvsignore
+++ b/arch/i386/lib/.cvsignore
@@ -1 +1,2 @@
.depend
+.*.flags
diff --git a/arch/i386/lib/Makefile b/arch/i386/lib/Makefile
index 6b76e32d6..a4e4b1486 100644
--- a/arch/i386/lib/Makefile
+++ b/arch/i386/lib/Makefile
@@ -11,6 +11,6 @@ else
endif
L_TARGET = lib.a
-L_OBJS = checksum.o semaphore.o locks.o delay.o
+L_OBJS = checksum.o semaphore.o locks.o delay.o usercopy.o getuser.o putuser.o
include $(TOPDIR)/Rules.make
diff --git a/arch/i386/lib/checksum.c b/arch/i386/lib/checksum.c
index 2293a6c5e..88f250d62 100644
--- a/arch/i386/lib/checksum.c
+++ b/arch/i386/lib/checksum.c
@@ -208,7 +208,7 @@ unsigned int csum_partial_copy_generic (const char *src, char *dst,
# Exception handler:
################################################
#
-.section .fixup, \"a\" #
+.section .fixup, \"ax\" #
#
6000: #
#
diff --git a/arch/i386/lib/delay.c b/arch/i386/lib/delay.c
index 4afc206e5..12b6b4683 100644
--- a/arch/i386/lib/delay.c
+++ b/arch/i386/lib/delay.c
@@ -15,12 +15,6 @@
#include <asm/smp.h>
#endif
-#ifdef __SMP__
-#define __udelay_val cpu_data[smp_processor_id()].udelay_val
-#else
-#define __udelay_val loops_per_sec
-#endif
-
void __delay(unsigned long loops)
{
__asm__ __volatile__(
@@ -34,7 +28,7 @@ inline void __const_udelay(unsigned long xloops)
{
__asm__("mull %0"
:"=d" (xloops)
- :"a" (xloops),"0" (__udelay_val)
+ :"a" (xloops),"0" (current_cpu_data.loops_per_sec)
:"ax");
__delay(xloops);
}
diff --git a/arch/i386/lib/getuser.S b/arch/i386/lib/getuser.S
new file mode 100644
index 000000000..c244721e7
--- /dev/null
+++ b/arch/i386/lib/getuser.S
@@ -0,0 +1,73 @@
+/*
+ * __get_user functions.
+ *
+ * (C) Copyright 1998 Linus Torvalds
+ *
+ * These functions have a non-standard call interface
+ * to make them more efficient, especially as they
+ * return an error value in addition to the "real"
+ * return value.
+ */
+
+/*
+ * __get_user_X
+ *
+ * Inputs: %eax contains the address
+ *
+ * Outputs: %eax is error code (0 or -EFAULT)
+ * %edx contains zero-extended value
+ *
+ * These functions should not modify any other registers,
+ * as they get called from within inline assembly.
+ */
+
+addr_limit = 12
+
+.text
+.align 4
+.globl __get_user_1
+__get_user_1:
+ movl %esp,%edx
+ andl $0xffffe000,%edx
+ cmpl addr_limit(%edx),%eax
+ jae bad_get_user
+1: movzbl (%eax),%edx
+ xorl %eax,%eax
+ ret
+
+.align 4
+.globl __get_user_2
+__get_user_2:
+ addl $1,%eax
+ movl %esp,%edx
+ jc bad_get_user
+ andl $0xffffe000,%edx
+ cmpl addr_limit(%edx),%eax
+ jae bad_get_user
+2: movzwl -1(%eax),%edx
+ xorl %eax,%eax
+ ret
+
+.align 4
+.globl __get_user_4
+__get_user_4:
+ addl $3,%eax
+ movl %esp,%edx
+ jc bad_get_user
+ andl $0xffffe000,%edx
+ cmpl addr_limit(%edx),%eax
+ jae bad_get_user
+3: movl -3(%eax),%edx
+ xorl %eax,%eax
+ ret
+
+bad_get_user:
+ xorl %edx,%edx
+ movl $-14,%eax
+ ret
+
+.section __ex_table,"a"
+ .long 1b,bad_get_user
+ .long 2b,bad_get_user
+ .long 3b,bad_get_user
+.previous
diff --git a/arch/i386/lib/putuser.S b/arch/i386/lib/putuser.S
new file mode 100644
index 000000000..ee56d83f7
--- /dev/null
+++ b/arch/i386/lib/putuser.S
@@ -0,0 +1,71 @@
+/*
+ * __put_user functions.
+ *
+ * (C) Copyright 1998 Linus Torvalds
+ *
+ * These functions have a non-standard call interface
+ * to make them more efficient.
+ */
+
+/*
+ * __put_user_X
+ *
+ * Inputs: %eax contains the address
+ * %edx contains the value
+ *
+ * Outputs: %eax is error code (0 or -EFAULT)
+ * %ecx is corrupted (will contain "current_task").
+ *
+ * These functions should not modify any other registers,
+ * as they get called from within inline assembly.
+ */
+
+addr_limit = 12
+
+.text
+.align 4
+.globl __put_user_1
+__put_user_1:
+ movl %esp,%ecx
+ andl $0xffffe000,%ecx
+ cmpl addr_limit(%ecx),%eax
+ jae bad_put_user
+1: movb %dl,(%eax)
+ xorl %eax,%eax
+ ret
+
+.align 4
+.globl __put_user_2
+__put_user_2:
+ addl $1,%eax
+ movl %esp,%ecx
+ jc bad_put_user
+ andl $0xffffe000,%ecx
+ cmpl addr_limit(%ecx),%eax
+ jae bad_put_user
+2: movw %dx,-1(%eax)
+ xorl %eax,%eax
+ ret
+
+.align 4
+.globl __put_user_4
+__put_user_4:
+ addl $3,%eax
+ movl %esp,%ecx
+ jc bad_put_user
+ andl $0xffffe000,%ecx
+ cmpl addr_limit(%ecx),%eax
+ jae bad_put_user
+3: movl %edx,-3(%eax)
+ xorl %eax,%eax
+ ret
+
+bad_put_user:
+ movl $-14,%eax
+ ret
+
+.section __ex_table,"a"
+ .long 1b,bad_put_user
+ .long 2b,bad_put_user
+ .long 3b,bad_put_user
+.previous
diff --git a/arch/i386/lib/usercopy.c b/arch/i386/lib/usercopy.c
new file mode 100644
index 000000000..6b313d99c
--- /dev/null
+++ b/arch/i386/lib/usercopy.c
@@ -0,0 +1,136 @@
+/*
+ * User address space access functions.
+ * The non inlined parts of asm-i386/uaccess.h are here.
+ *
+ * Copyright 1997 Andi Kleen <ak@muc.de>
+ * Copyright 1997 Linus Torvalds
+ */
+#include <asm/uaccess.h>
+
+inline unsigned long
+__generic_copy_to_user(void *to, const void *from, unsigned long n)
+{
+ if (access_ok(VERIFY_WRITE, to, n))
+ __copy_user(to,from,n);
+ return n;
+}
+
+inline unsigned long
+__generic_copy_from_user(void *to, const void *from, unsigned long n)
+{
+ if (access_ok(VERIFY_READ, from, n))
+ __copy_user(to,from,n);
+ return n;
+}
+
+
+/*
+ * Copy a null terminated string from userspace.
+ */
+
+#define __do_strncpy_from_user(dst,src,count,res) \
+ __asm__ __volatile__( \
+ " testl %1,%1\n" \
+ " jz 2f\n" \
+ "0: lodsb\n" \
+ " stosb\n" \
+ " testb %%al,%%al\n" \
+ " jz 1f\n" \
+ " decl %1\n" \
+ " jnz 0b\n" \
+ "1: subl %1,%0\n" \
+ "2:\n" \
+ ".section .fixup,\"ax\"\n" \
+ "3: movl %2,%0\n" \
+ " jmp 2b\n" \
+ ".previous\n" \
+ ".section __ex_table,\"a\"\n" \
+ " .align 4\n" \
+ " .long 0b,3b\n" \
+ ".previous" \
+ : "=d"(res), "=c"(count) \
+ : "i"(-EFAULT), "0"(count), "1"(count), "S"(src), "D"(dst) \
+ : "si", "di", "ax", "memory")
+
+long
+__strncpy_from_user(char *dst, const char *src, long count)
+{
+ long res;
+ __do_strncpy_from_user(dst, src, count, res);
+ return res;
+}
+
+long
+strncpy_from_user(char *dst, const char *src, long count)
+{
+ long res = -EFAULT;
+ if (access_ok(VERIFY_READ, src, 1))
+ __do_strncpy_from_user(dst, src, count, res);
+ return res;
+}
+
+
+/*
+ * Zero Userspace
+ */
+
+#define __do_clear_user(addr,size) \
+ __asm__ __volatile__( \
+ "0: rep; stosl\n" \
+ " movl %1,%0\n" \
+ "1: rep; stosb\n" \
+ "2:\n" \
+ ".section .fixup,\"ax\"\n" \
+ "3: lea 0(%1,%0,4),%0\n" \
+ " jmp 2b\n" \
+ ".previous\n" \
+ ".section __ex_table,\"a\"\n" \
+ " .align 4\n" \
+ " .long 0b,3b\n" \
+ " .long 1b,2b\n" \
+ ".previous" \
+ : "=&c"(size) \
+ : "r"(size & 3), "0"(size / 4), "D"(addr), "a"(0) \
+ : "di")
+
+unsigned long
+clear_user(void *to, unsigned long n)
+{
+ if (access_ok(VERIFY_WRITE, to, n))
+ __do_clear_user(to, n);
+ return n;
+}
+
+unsigned long
+__clear_user(void *to, unsigned long n)
+{
+ __do_clear_user(to, n);
+ return n;
+}
+
+/*
+ * Return the size of a string (including the ending 0)
+ *
+ * Return 0 for error
+ */
+
+long strlen_user(const char *s)
+{
+ unsigned long res;
+
+ __asm__ __volatile__(
+ "0: repne; scasb\n"
+ " notl %0\n"
+ "1:\n"
+ ".section .fixup,\"ax\"\n"
+ "2: xorl %0,%0\n"
+ " jmp 1b\n"
+ ".previous\n"
+ ".section __ex_table,\"a\"\n"
+ " .align 4\n"
+ " .long 0b,2b\n"
+ ".previous"
+ :"=c" (res), "=D" (s)
+ :"1" (s), "a" (0), "0" (-__addr_ok(s)));
+ return res & -__addr_ok(s);
+}
diff --git a/arch/i386/math-emu/fpu_entry.c b/arch/i386/math-emu/fpu_entry.c
index 36ca90a35..f120bbc49 100644
--- a/arch/i386/math-emu/fpu_entry.c
+++ b/arch/i386/math-emu/fpu_entry.c
@@ -728,11 +728,11 @@ struct _fpstate * save_i387_soft(void *s387, struct _fpstate * buf)
FPU_verify_area(VERIFY_WRITE, d, 7*4 + 8*10);
#ifdef PECULIAR_486
S387->cwd &= ~0xe080;
- /* An 80486 sets all the reserved bits to 1. */
- S387->cwd |= 0xffff0000;
+ /* An 80486 sets nearly all of the reserved bits to 1. */
+ S387->cwd |= 0xffff0040;
S387->swd = sstatus_word() | 0xffff0000;
S387->twd |= 0xffff0000;
- S387->fcs |= 0xf8000000;
+ S387->fcs &= ~0xf8000000;
S387->fos |= 0xffff0000;
#endif PECULIAR_486
__copy_to_user(d, &S387->cwd, 7*4);
diff --git a/arch/i386/math-emu/get_address.c b/arch/i386/math-emu/get_address.c
index 799bc1c41..a4b15ee7f 100644
--- a/arch/i386/math-emu/get_address.c
+++ b/arch/i386/math-emu/get_address.c
@@ -134,8 +134,8 @@ static int sib(int mod, unsigned long *fpu_eip)
static unsigned long vm86_segment(u_char segment,
- unsigned short *selector)
-{
+ struct address *addr)
+{
segment--;
#ifdef PARANOID
if ( segment > PREFIX_SS_ )
@@ -144,14 +144,14 @@ static unsigned long vm86_segment(u_char segment,
math_abort(FPU_info,SIGSEGV);
}
#endif PARANOID
- *selector = VM86_REG_(segment);
+ addr->selector = VM86_REG_(segment);
return (unsigned long)VM86_REG_(segment) << 4;
}
/* This should work for 16 and 32 bit protected mode. */
static long pm_address(u_char FPU_modrm, u_char segment,
- unsigned short *selector, long offset)
+ struct address *addr, long offset)
{
struct desc_struct descriptor;
unsigned long base_address, limit, address, seg_top;
@@ -172,13 +172,17 @@ static long pm_address(u_char FPU_modrm, u_char segment,
/* fs and gs aren't used by the kernel, so they still have their
user-space values. */
case PREFIX_FS_-1:
- __asm__("mov %%fs,%0":"=r" (*selector));
+ /* The cast is needed here to get gcc 2.8.0 to use a 16 bit register
+ in the assembler statement. */
+ __asm__("mov %%fs,%0":"=r" ((unsigned short)addr->selector));
break;
case PREFIX_GS_-1:
- __asm__("mov %%gs,%0":"=r" (*selector));
+ /* The cast is needed here to get gcc 2.8.0 to use a 16 bit register
+ in the assembler statement. */
+ __asm__("mov %%gs,%0":"=r" ((unsigned short)addr->selector));
break;
default:
- *selector = PM_REG_(segment);
+ addr->selector = PM_REG_(segment);
}
descriptor = LDT_DESCRIPTOR(PM_REG_(segment));
@@ -312,13 +316,12 @@ void *FPU_get_address(u_char FPU_modrm, unsigned long *fpu_eip,
case 0:
break;
case VM86:
- address += vm86_segment(addr_modes.override.segment,
- (unsigned short *)&(addr->selector));
+ address += vm86_segment(addr_modes.override.segment, addr);
break;
case PM16:
case SEG32:
address = pm_address(FPU_modrm, addr_modes.override.segment,
- (unsigned short *)&(addr->selector), address);
+ addr, address);
break;
default:
EXCEPTION(EX_INTERNAL|0x133);
@@ -427,13 +430,12 @@ void *FPU_get_address_16(u_char FPU_modrm, unsigned long *fpu_eip,
case 0:
break;
case VM86:
- address += vm86_segment(addr_modes.override.segment,
- (unsigned short *)&(addr->selector));
+ address += vm86_segment(addr_modes.override.segment, addr);
break;
case PM16:
case SEG32:
address = pm_address(FPU_modrm, addr_modes.override.segment,
- (unsigned short *)&(addr->selector), address);
+ addr, address);
break;
default:
EXCEPTION(EX_INTERNAL|0x131);
diff --git a/arch/i386/math-emu/reg_ld_str.c b/arch/i386/math-emu/reg_ld_str.c
index 468e51cc8..b3ed9acbb 100644
--- a/arch/i386/math-emu/reg_ld_str.c
+++ b/arch/i386/math-emu/reg_ld_str.c
@@ -1329,11 +1329,11 @@ u_char *fstenv(fpu_addr_modes addr_modes, u_char *d)
FPU_verify_area(VERIFY_WRITE, d, 7*4);
#ifdef PECULIAR_486
control_word &= ~0xe080;
- /* An 80486 sets all the reserved bits to 1. */
- control_word |= 0xffff0000;
+ /* An 80486 sets nearly all of the reserved bits to 1. */
+ control_word |= 0xffff0040;
partial_status = status_word() | 0xffff0000;
fpu_tag_word |= 0xffff0000;
- I387.soft.fcs |= 0xf8000000;
+ I387.soft.fcs &= ~0xf8000000;
I387.soft.fos |= 0xffff0000;
#endif PECULIAR_486
__copy_to_user(d, &control_word, 7*4);
diff --git a/arch/i386/mm/.cvsignore b/arch/i386/mm/.cvsignore
index 4671378ae..857dd22e9 100644
--- a/arch/i386/mm/.cvsignore
+++ b/arch/i386/mm/.cvsignore
@@ -1 +1,2 @@
.depend
+.*.flags
diff --git a/arch/i386/mm/fault.c b/arch/i386/mm/fault.c
index beb9f91a4..8fac7dc2b 100644
--- a/arch/i386/mm/fault.c
+++ b/arch/i386/mm/fault.c
@@ -20,6 +20,7 @@
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
+#include <asm/hardirq.h>
extern void die_if_kernel(const char *,struct pt_regs *,long);
@@ -76,8 +77,6 @@ bad_area:
asmlinkage void do_invalid_op (struct pt_regs *, unsigned long);
-extern int pentium_f00f_bug;
-
/*
* This routine handles page faults. It determines the address,
* and the problem, and then passes it off to one of the appropriate
@@ -101,6 +100,8 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code)
/* get the address */
__asm__("movl %%cr2,%0":"=r" (address));
+ if (local_irq_count[smp_processor_id()])
+ die_if_kernel("page fault from irq handler",regs,error_code);
lock_kernel();
tsk = current;
mm = tsk->mm;
@@ -180,7 +181,7 @@ bad_area:
/*
* Pentium F0 0F C7 C8 bug workaround.
*/
- if (pentium_f00f_bug) {
+ if (boot_cpu_data.f00f_bug) {
unsigned long nr;
nr = (address - (unsigned long) idt) >> 3;
@@ -194,11 +195,6 @@ bad_area:
/* Are we prepared to handle this kernel fault? */
if ((fixup = search_exception_table(regs->eip)) != 0) {
- printk(KERN_DEBUG "%s: Exception at [<%lx>] cr2=%lx (fixup: %lx)\n",
- tsk->comm,
- regs->eip,
- address,
- fixup);
regs->eip = fixup;
goto out;
}
@@ -209,10 +205,16 @@ bad_area:
*
* First we check if it was the bootup rw-test, though..
*/
- if (wp_works_ok < 0 && address == TASK_SIZE && (error_code & 1)) {
- wp_works_ok = 1;
- pg0[0] = pte_val(mk_pte(TASK_SIZE, PAGE_SHARED));
- flush_tlb();
+ if (boot_cpu_data.wp_works_ok < 0 &&
+ address == PAGE_OFFSET && (error_code & 1)) {
+ boot_cpu_data.wp_works_ok = 1;
+ pg0[0] = pte_val(mk_pte(PAGE_OFFSET, PAGE_KERNEL));
+ local_flush_tlb();
+ /*
+ * Beware: Black magic here. The printk is needed here to flush
+ * CPU state on certain buggy processors.
+ */
+ printk("Ok");
goto out;
}
diff --git a/arch/i386/mm/init.c b/arch/i386/mm/init.c
index 6ed47e2ef..f9172bdae 100644
--- a/arch/i386/mm/init.c
+++ b/arch/i386/mm/init.c
@@ -72,7 +72,7 @@ pte_t __bad_page(void)
void show_mem(void)
{
int i,free = 0,total = 0,reserved = 0;
- int shared = 0;
+ int shared = 0, cached = 0;
printk("Mem-info:\n");
show_free_areas();
@@ -82,6 +82,8 @@ void show_mem(void)
total++;
if (PageReserved(mem_map+i))
reserved++;
+ if (PageSwapCache(mem_map+i))
+ cached++;
else if (!atomic_read(&mem_map[i].count))
free++;
else
@@ -91,6 +93,7 @@ void show_mem(void)
printk("%d free pages\n",free);
printk("%d reserved pages\n",reserved);
printk("%d pages shared\n",shared);
+ printk("%d pages swap cached\n",cached);
show_buffers();
#ifdef CONFIG_NET
show_net_buffers();
@@ -131,14 +134,6 @@ extern char __init_begin, __init_end;
#define X86_FEATURE_MCA 0x4000 /* Machine Check Architecture */
#define X86_FEATURE_CMOV 0x8000 /* Cmov/fcomi */
-#ifdef GAS_KNOWS_CR4
-#define read_cr4 "movl %%cr4,%%eax"
-#define write_cr4 "movl %%eax,%%cr4"
-#else
-#define read_cr4 ".byte 0x0f,0x20,0xe0"
-#define write_cr4 ".byte 0x0f,0x22,0xe0"
-#endif
-
/*
* Save the cr4 feature set we're using (ie
* Pentium 4MB enable and PPro Global page
@@ -150,9 +145,9 @@ unsigned long mmu_cr4_features __initdata = 0;
static inline void set_in_cr4(unsigned long mask)
{
mmu_cr4_features |= mask;
- __asm__(read_cr4 "\n\t"
+ __asm__("movl %%cr4,%%eax\n\t"
"orl %0,%%eax\n\t"
- write_cr4
+ "movl %%eax,%%cr4\n"
: : "irg" (mask)
:"ax");
}
@@ -178,9 +173,6 @@ __initfunc(unsigned long paging_init(unsigned long start_mem, unsigned long end_
* kernel.
* It may also hold the MP configuration table when we are booting SMP.
*/
-#if 0
- memset((void *) 0, 0, PAGE_SIZE);
-#endif
#ifdef __SMP__
if (!smp_scan_config(0x0,0x400)) /* Scan the bottom 1K for a signature */
{
@@ -189,19 +181,23 @@ __initfunc(unsigned long paging_init(unsigned long start_mem, unsigned long end_
* the error...
*/
if (!smp_scan_config(639*0x400,0x400)) /* Scan the top 1K of base RAM */
- smp_scan_config(0xF0000,0x10000); /* Scan the 64K of bios */
+ {
+ if(!smp_scan_config(0xF0000,0x10000)) /* Scan the 64K of bios */
+ {
+ /*
+ * If it is an SMP machine we should know now, unless the configuration
+ * is in an EISA/MCA bus machine with an extended bios data area.
+ */
+
+ address = *(unsigned short *)phys_to_virt(0x40E); /* EBDA */
+ address<<=4; /* Real mode segments to physical */
+ smp_scan_config(address, 0x1000); /* Scan the EBDA */
+ }
+ }
}
- /*
- * If it is an SMP machine we should know now, unless the configuration
- * is in an EISA/MCA bus machine with an extended bios data area. I don't
- * have such a machine so someone else can fill in the check of the EBDA
- * here.
- */
+
/* smp_alloc_memory(8192); */
#endif
-#ifdef TEST_VERIFY_AREA
- wp_works_ok = 0;
-#endif
start_mem = PAGE_ALIGN(start_mem);
address = PAGE_OFFSET;
pg_dir = swapper_pg_dir;
@@ -219,14 +215,14 @@ __initfunc(unsigned long paging_init(unsigned long start_mem, unsigned long end_
* virtual memory boundary, but that's OK as we won't
* use that memory anyway.
*/
- if (x86_capability & X86_FEATURE_PSE) {
+ if (boot_cpu_data.x86_capability & X86_FEATURE_PSE) {
unsigned long __pe;
set_in_cr4(X86_CR4_PSE);
- wp_works_ok = 1;
+ boot_cpu_data.wp_works_ok = 1;
__pe = _KERNPG_TABLE + _PAGE_4M + __pa(address);
/* Make it "global" too if supported */
- if (x86_capability & X86_FEATURE_PGE) {
+ if (boot_cpu_data.x86_capability & X86_FEATURE_PGE) {
set_in_cr4(X86_CR4_PGE);
__pe += _PAGE_GLOBAL;
}
@@ -235,6 +231,7 @@ __initfunc(unsigned long paging_init(unsigned long start_mem, unsigned long end_
address += 4*1024*1024;
continue;
}
+
/*
* We're on a [34]86, use normal page tables.
* pg_table is physical at this point
@@ -247,6 +244,7 @@ __initfunc(unsigned long paging_init(unsigned long start_mem, unsigned long end_
pgd_val(*pg_dir) = _PAGE_TABLE | (unsigned long) pg_table;
pg_dir++;
+
/* now change pg_table to kernel virtual addresses */
pg_table = (pte_t *) __va(pg_table);
for (tmp = 0 ; tmp < PTRS_PER_PTE ; tmp++,pg_table++) {
@@ -288,14 +286,14 @@ __initfunc(void test_wp_bit(void))
pg0[0] = old;
local_flush_tlb();
current->mm->mmap->vm_start -= PAGE_SIZE;
- if (wp_works_ok < 0) {
- wp_works_ok = 0;
+ if (boot_cpu_data.wp_works_ok < 0) {
+ boot_cpu_data.wp_works_ok = 0;
printk("No.\n");
#ifndef CONFIG_M386
panic("This kernel doesn't support CPU's with broken WP. Recompile it for a 386!");
#endif
} else
- printk("Ok.\n");
+ printk(".\n");
}
__initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem))
@@ -377,7 +375,7 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem))
datapages << (PAGE_SHIFT-10),
initpages << (PAGE_SHIFT-10));
- if (wp_works_ok < 0)
+ if (boot_cpu_data.wp_works_ok < 0)
test_wp_bit();
}