diff options
Diffstat (limited to 'arch/i386')
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 *)®_00 = io_apic_read(0); + *(int *)®_01 = io_apic_read(1); + *(int *)®_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 *)®_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 *)®_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 *)®_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 *)®_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, ®s); + 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, ®s); + 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, ®s); + /* * 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(¤t->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(¤t->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(); } |