diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-02-24 00:12:35 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-02-24 00:12:35 +0000 |
commit | 482368b1a8e45430672c58c9a42e7d2004367126 (patch) | |
tree | ce2a1a567d4d62dee7c2e71a46a99cf72cf1d606 /arch/i386 | |
parent | e4d0251c6f56ab2e191afb70f80f382793e23f74 (diff) |
Merge with 2.3.47. Guys, this is buggy as shit. You've been warned.
Diffstat (limited to 'arch/i386')
-rw-r--r-- | arch/i386/boot/bootsect.S | 25 | ||||
-rw-r--r-- | arch/i386/boot/setup.S | 11 | ||||
-rw-r--r-- | arch/i386/config.in | 7 | ||||
-rw-r--r-- | arch/i386/defconfig | 23 | ||||
-rw-r--r-- | arch/i386/kernel/Makefile | 10 | ||||
-rw-r--r-- | arch/i386/kernel/acpi.c | 1 | ||||
-rw-r--r-- | arch/i386/kernel/apic.c | 2 | ||||
-rw-r--r-- | arch/i386/kernel/apm.c | 416 | ||||
-rw-r--r-- | arch/i386/kernel/i386_ksyms.c | 7 | ||||
-rw-r--r-- | arch/i386/kernel/i8259.c | 4 | ||||
-rw-r--r-- | arch/i386/kernel/io_apic.c | 51 | ||||
-rw-r--r-- | arch/i386/kernel/irq.c | 1 | ||||
-rw-r--r-- | arch/i386/kernel/microcode.c | 266 | ||||
-rw-r--r-- | arch/i386/kernel/mpparse.c | 5 | ||||
-rw-r--r-- | arch/i386/kernel/mtrr.c | 316 | ||||
-rw-r--r-- | arch/i386/kernel/pci-dma.c | 18 | ||||
-rw-r--r-- | arch/i386/kernel/pci-i386.c | 2 | ||||
-rw-r--r-- | arch/i386/kernel/pci-pc.c | 81 | ||||
-rw-r--r-- | arch/i386/kernel/process.c | 22 | ||||
-rw-r--r-- | arch/i386/kernel/setup.c | 2 | ||||
-rw-r--r-- | arch/i386/kernel/smp.c | 11 | ||||
-rw-r--r-- | arch/i386/mm/init.c | 25 |
22 files changed, 902 insertions, 404 deletions
diff --git a/arch/i386/boot/bootsect.S b/arch/i386/boot/bootsect.S index 4a9d59908..f345ea100 100644 --- a/arch/i386/boot/bootsect.S +++ b/arch/i386/boot/bootsect.S @@ -106,9 +106,8 @@ go: movw $0x4000-12, %di # 0x4000 is an arbitrary value >= pushw %ds ldsw %fs:(%bx), %si # ds:si is source movb $6, %cl # copy 12 bytes - cld pushw %di # di = 0x4000-12. - rep + rep # don't need cld -> done on line 66 movsw popw %di popw %ds @@ -140,20 +139,7 @@ load_setup: jmp load_setup ok_load_setup: -# Get disk drive parameters, specifically nr of sectors/track. - -#if 0 - -# bde - the Phoenix BIOS manual says function 0x08 only works for fixed -# disks. It doesn't work for one of my BIOS's (1987 Award). It was -# fatal not to check the error code. - - xorb %dl, %dl - movb $0x08, %ah # AH=8 is get drive parameters - int $0x13 - xorb %ch, %ch - -#else +# Get disk drive parameters, specifically number of sectors/track. # It seems that there is no BIOS call to get the number of sectors. # Guess 36 sectors if sector 36 can be read, 18 sectors if sector 18 @@ -176,7 +162,6 @@ probe_loop: movw $0x0201, %ax # service 2, 1 sector int $0x13 jc probe_loop # try next value -#endif got_sectors: movw $INITSEG, %ax @@ -200,11 +185,11 @@ got_sectors: # Otherwise, one of /dev/fd0H2880 (2,32) or /dev/PS0 (2,28) or /dev/at0 (2,8) # depending on the number of sectors we pretend to know we have. - movw %cs:root_dev, %ax + movw root_dev, %ax orw %ax, %ax jne root_defined - movw %cs:sectors, %bx + movw sectors, %bx movw $0x0208, %ax # /dev/ps0 - 1.2Mb cmpw $15, %bx je root_defined @@ -219,7 +204,7 @@ got_sectors: movb $0, %al # /dev/fd0 - autodetect root_defined: - movw %ax, %cs:root_dev + movw %ax, root_dev # After that (everything loaded), we jump to the setup-routine # loaded directly after the bootblock: diff --git a/arch/i386/boot/setup.S b/arch/i386/boot/setup.S index 1a08bc3ab..7c50d54a8 100644 --- a/arch/i386/boot/setup.S +++ b/arch/i386/boot/setup.S @@ -436,7 +436,7 @@ no_mca: movw $0xAA, (0x1ff) # device present no_psmouse: -#ifdef CONFIG_APM +#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE) # Then check for an APM BIOS... # %ds points to the bootsector movw $0, 0x40 # version = 0 means no APM BIOS @@ -455,7 +455,11 @@ no_psmouse: xorw %bx, %bx int $0x15 # ignore return code movw $0x05303, %ax # 32 bit connect - xorw %bx, %bx + xorl %ebx, %ebx + xorw %cx, %cx # paranoia :-) + xorw %dx, %dx # ... + xorl %esi, %esi # ... + xorw %di, %di # ... int $0x15 jc no_32_apm_bios # Ack, error. @@ -463,12 +467,13 @@ no_psmouse: movl %ebx, (68) # BIOS entry point offset movw %cx, (72) # BIOS 16 bit code segment movw %dx, (74) # BIOS data segment - movl %esi, (78) # BIOS code segment length + movl %esi, (78) # BIOS code segment lengths movw %di, (82) # BIOS data segment length # Redo the installation check as the 32 bit connect # modifies the flags returned on some BIOSs movw $0x05300, %ax # APM BIOS installation check xorw %bx, %bx + xorw %cx, %cx # paranoia int $0x15 jc apm_disconnect # error -> shouldn't happen diff --git a/arch/i386/config.in b/arch/i386/config.in index 2377c24b7..e074ea81d 100644 --- a/arch/i386/config.in +++ b/arch/i386/config.in @@ -49,6 +49,10 @@ if [ "$CONFIG_MK7" = "y" ]; then define_bool CONFIG_X86_USE_3DNOW y fi +if [ "$CONFIG_PROC_FS" = "y" ]; then + tristate '/proc/driver/microcode - Intel P6 CPU microcode support' CONFIG_MICROCODE +fi + choice 'High Memory Support' \ "off CONFIG_NOHIGHMEM \ 4GB CONFIG_HIGHMEM4G \ @@ -144,7 +148,7 @@ if [ "$CONFIG_ACPI" != "n" ]; then fi fi -bool 'Advanced Power Management BIOS support' CONFIG_APM +tristate 'Advanced Power Management BIOS support' CONFIG_APM if [ "$CONFIG_APM" != "n" ]; then bool ' Ignore USER SUSPEND' CONFIG_APM_IGNORE_USER_SUSPEND bool ' Enable PM at boot time' CONFIG_APM_DO_ENABLE @@ -154,7 +158,6 @@ if [ "$CONFIG_APM" != "n" ]; then bool ' Ignore multiple suspend/resume cycles' CONFIG_APM_IGNORE_SUSPEND_BOUNCE bool ' RTC stores time in GMT' CONFIG_APM_RTC_IS_GMT bool ' Allow interrupts during APM BIOS calls' CONFIG_APM_ALLOW_INTS - bool ' Entry point offset fix (some Acer laptops)' CONFIG_APM_BAD_ENTRY_OFFSET bool ' Use real mode APM BIOS call to power off' CONFIG_APM_REAL_MODE_POWER_OFF fi diff --git a/arch/i386/defconfig b/arch/i386/defconfig index fcc962a11..4ca545255 100644 --- a/arch/i386/defconfig +++ b/arch/i386/defconfig @@ -27,6 +27,7 @@ CONFIG_X86_POPAD_OK=y CONFIG_X86_TSC=y CONFIG_X86_GOOD_APIC=y CONFIG_X86_PGE=y +# CONFIG_MICROCODE is not set CONFIG_NOHIGHMEM=y # CONFIG_HIGHMEM4G is not set # CONFIG_HIGHMEM64G is not set @@ -175,7 +176,6 @@ CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y CONFIG_SD_EXTRA_DEVS=40 # CONFIG_CHR_DEV_ST is not set -CONFIG_ST_EXTRA_DEVS=2 # CONFIG_BLK_DEV_SR is not set # CONFIG_CHR_DEV_SG is not set @@ -238,6 +238,11 @@ CONFIG_SCSI_NCR53C8XX_SYNC=20 # CONFIG_SCSI_ULTRASTOR is not set # +# PCMCIA SCSI adapter support +# +# CONFIG_SCSI_PCMCIA is not set + +# # I2O device support # # CONFIG_I2O is not set @@ -270,15 +275,15 @@ CONFIG_NET_ETHERNET=y # CONFIG_NET_VENDOR_RACAL is not set # CONFIG_DEPCA is not set # CONFIG_NET_ISA is not set -CONFIG_NET_EISA=y +CONFIG_NET_PCI=y # CONFIG_PCNET32 is not set # CONFIG_APRICOT is not set -# CONFIG_CS89x0 is not set # CONFIG_DE4X5 is not set # CONFIG_TULIP is not set # CONFIG_DGRS is not set -CONFIG_EEXPRESS_PRO100=y +CONFIG_EEPRO100=y # CONFIG_NE2K_PCI is not set +# CONFIG_8139TOO is not set # CONFIG_SIS900 is not set # CONFIG_TLAN is not set # CONFIG_VIA_RHINE is not set @@ -324,7 +329,6 @@ CONFIG_PCMCIA_PCNET=y # CONFIG_ARCNET_COM20020_CS is not set # CONFIG_PCMCIA_3C575 is not set # CONFIG_PCMCIA_TULIP is not set -# CONFIG_PCMCIA_EPIC100 is not set CONFIG_NET_PCMCIA_RADIO=y CONFIG_PCMCIA_RAYCS=y # CONFIG_PCMCIA_NETWAVE is not set @@ -423,7 +427,7 @@ CONFIG_PCMCIA_SERIAL=y # # -# Filesystems +# File systems # # CONFIG_QUOTA is not set # CONFIG_AUTOFS_FS is not set @@ -442,20 +446,17 @@ CONFIG_ISO9660_FS=y # CONFIG_JOLIET is not set # CONFIG_MINIX_FS is not set # CONFIG_NTFS_FS is not set -# CONFIG_NTFS_RW is not set # CONFIG_HPFS_FS is not set CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_DEBUG is not set CONFIG_DEVPTS_FS=y # CONFIG_QNX4FS_FS is not set -# CONFIG_QNX4FS_RW is not set # CONFIG_ROMFS_FS is not set CONFIG_EXT2_FS=y # CONFIG_SYSV_FS is not set -# CONFIG_SYSV_FS_WRITE is not set # CONFIG_UDF_FS is not set -# CONFIG_UDF_RW is not set # CONFIG_UFS_FS is not set -# CONFIG_UFS_FS_WRITE is not set # # Network File Systems diff --git a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile index 96be4dff6..3b7bb1258 100644 --- a/arch/i386/kernel/Makefile +++ b/arch/i386/kernel/Makefile @@ -40,6 +40,16 @@ else endif endif +ifeq ($(CONFIG_PROC_FS),y) +ifeq ($(CONFIG_MICROCODE),y) +OX_OBJS += microcode.o +else + ifeq ($(CONFIG_MICROCODE),m) + MX_OBJS += microcode.o + endif +endif +endif + ifeq ($(CONFIG_ACPI),y) O_OBJS += acpi.o else diff --git a/arch/i386/kernel/acpi.c b/arch/i386/kernel/acpi.c index 8ae8bc299..9bdd111d1 100644 --- a/arch/i386/kernel/acpi.c +++ b/arch/i386/kernel/acpi.c @@ -643,6 +643,7 @@ const static struct {NULL,}, {acpi_init_piix4}, {acpi_init_via}, + {acpi_init_via}, }; const static struct pci_device_id acpi_pci_tbl[] = diff --git a/arch/i386/kernel/apic.c b/arch/i386/kernel/apic.c index b8bca05c6..a5f72548f 100644 --- a/arch/i386/kernel/apic.c +++ b/arch/i386/kernel/apic.c @@ -3,6 +3,8 @@ * * (c) 1999, 2000 Ingo Molnar <mingo@redhat.com> * + * Fixes + * Maciej W. Rozycki : Bits for genuine 82489DX timers */ #include <linux/config.h> diff --git a/arch/i386/kernel/apm.c b/arch/i386/kernel/apm.c index 81a813b05..4ec5e7993 100644 --- a/arch/i386/kernel/apm.c +++ b/arch/i386/kernel/apm.c @@ -1,6 +1,6 @@ /* -*- linux-c -*- * APM BIOS driver for Linux - * Copyright 1994-1999 Stephen Rothwell (sfr@linuxcare.com) + * Copyright 1994-2000 Stephen Rothwell (sfr@linuxcare.com) * * Initial development of this driver was funded by NEC Australia P/L * and NEC Corporation @@ -35,6 +35,8 @@ * Jan 1999, Version 1.9 * Oct 1999, Version 1.10 * Nov 1999, Version 1.11 + * Jan 2000, Version 1.12 + * Feb 2000, Version 1.13 * * History: * 0.6b: first version in official kernel, Linux 1.3.46 @@ -54,7 +56,7 @@ * The new replacment for it is, but Linux doesn't yet support this. * Alan Cox Linux 2.1.55 * 1.3: Set up a valid data descriptor 0x40 for buggy BIOS's - * 1.4: Upgraded to support APM 1.2. Integrated ThinkPad suspend patch by + * 1.4: Upgraded to support APM 1.2. Integrated ThinkPad suspend patch by * Dean Gaudet <dgaudet@arctic.org>. * C. Scott Ananian <cananian@alumni.princeton.edu> Linux 2.1.87 * 1.5: Fix segment register reloading (in case of bad segments saved @@ -110,6 +112,26 @@ * (reported by Panos Katsaloulis <teras@writeme.com>). * Real mode power off patch (Walter Hofmann * <Walter.Hofmann@physik.stud.uni-erlangen.de>). + * 1.12: Remove CONFIG_SMP as the compiler will optimize + * the code away anyway (smp_num_cpus == 1 in UP) + * noted by Artur Skawina <skawina@geocities.com>. + * Make power off under SMP work again. + * Fix thinko with initial engaging of BIOS. + * Make sure power off only happens on CPU 0 + * (Paul "Rusty" Russell <rusty@linuxcare.com>). + * Do error notification to user mode if BIOS calls fail. + * Move entrypoint offset fix to ...boot/setup.S + * where it belongs (Cosmos <gis88564@cis.nctu.edu.tw>). + * Remove smp-power-off. SMP users must now specify + * "apm=power-off" on the kernel command line. Suggested + * by Jim Avera <jima@hal.com>, modified by Alan Cox + * <alan@lxorguk.ukuu.org.uk>. + * Register the /proc/apm entry even on SMP so that + * scripts that check for it before doing power off + * work (Jim Avera <jima@hal.com>). + * 1.13: Changes for new pm_ interfaces (Andy Henroid + * <andy_henroid@yahoo.com>). + * Modularize the code. * * APM 1.1 Reference: * @@ -145,13 +167,12 @@ #include <linux/apm_bios.h> #include <linux/init.h> #include <linux/sched.h> +#include <linux/pm.h> #include <asm/system.h> #include <asm/uaccess.h> #include <asm/desc.h> -#include <linux/pm.h> - extern unsigned long get_cmos_time(void); extern void machine_real_restart(unsigned char *, int); @@ -172,10 +193,10 @@ extern int (*console_blank_hook)(int); * See Documentation/Config.help for the configuration options. * * Various options can be changed at boot time as follows: + * (We allow underscores for compatibility with the modules code) * apm=on/off enable/disable APM * [no-]debug log some debugging messages - * [no-]power-off power off on shutdown - * [no-]smp-power-off allow power off even for SMP + * [no-]power[-_]off power off on shutdown */ /* KNOWN PROBLEM MACHINES: @@ -219,8 +240,8 @@ extern int (*console_blank_hook)(int); /* * Define to re-initialize the interrupt 0 timer to 100 Hz after a suspend. - * This patched by Chad Miller <cmiller@surfsouth.com>, orig code by David - * Chen <chen@ctpa04.mit.edu> + * This patched by Chad Miller <cmiller@surfsouth.com>, original code by + * David Chen <chen@ctpa04.mit.edu> */ #undef INIT_TIMER_AFTER_SUSPEND @@ -237,7 +258,7 @@ extern int (*console_blank_hook)(int); /* * If CONFIG_APM_IGNORE_SUSPEND_BOUNCE is defined then - * ignore suspend events for this amount of time + * ignore suspend events for this amount of time after a resume */ #define BOUNCE_INTERVAL (3 * HZ) @@ -248,14 +269,40 @@ extern int (*console_blank_hook)(int); __asm__ __volatile__("movl %%" #seg ",%0" : "=m" (where)) /* + * Maximum number of events stored + */ +#define APM_MAX_EVENTS 20 + +/* + * The per-file APM data + */ +struct apm_user { + int magic; + struct apm_user * next; + int suser: 1; + int suspend_wait: 1; + int suspend_result; + int suspends_pending; + int standbys_pending; + int suspends_read; + int standbys_read; + int event_head; + int event_tail; + apm_event_t events[APM_MAX_EVENTS]; +}; + +/* + * The magic number in apm_user + */ +#define APM_BIOS_MAGIC 0x4101 + +/* * Local variables */ static struct { unsigned long offset; unsigned short segment; } apm_bios_entry; -static int apm_enabled = 0; -static int smp_hack = 0; #ifdef CONFIG_APM_CPU_IDLE static int clock_slowed = 0; #endif @@ -274,12 +321,19 @@ static int got_clock_diff = 0; #endif static int debug = 0; static int apm_disabled = 0; -static int power_off_enabled = 1; +#ifdef CONFIG_SMP +static int power_off = 0; +#else +static int power_off = 1; +#endif +static int exit_kapmd = 0; +static int kapmd_running = 0; static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue); -static struct apm_bios_struct * user_list = NULL; +static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue); +static struct apm_user * user_list = NULL; -static char driver_version[] = "1.11"; /* no spaces */ +static char driver_version[] = "1.12"; /* no spaces */ static char * apm_event_name[] = { "system standby", @@ -380,6 +434,10 @@ static u8 apm_bios_call(u32 func, u32 ebx_in, u32 ecx_in, __save_flags(flags); APM_DO_CLI; APM_DO_SAVE_SEGS; + /* + * N.B. We do NOT need a cld after the BIOS call + * because we always save and restore the flags. + */ __asm__ __volatile__(APM_DO_ZERO_SEGS "pushl %%edi\n\t" "pushl %%ebp\n\t" @@ -413,10 +471,14 @@ static u8 apm_bios_call_simple(u32 func, u32 ebx_in, u32 ecx_in, u32 *eax) { int cx, dx, si; + /* + * N.B. We do NOT need a cld after the BIOS call + * because we always save and restore the flags. + */ __asm__ __volatile__(APM_DO_ZERO_SEGS "pushl %%edi\n\t" "pushl %%ebp\n\t" - "lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry)"\n\t" + "lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t" "setc %%bl\n\t" "popl %%ebp\n\t" "popl %%edi\n\t" @@ -499,6 +561,7 @@ static void apm_do_busy(void) } } +#if 0 extern int hlt_counter; /* @@ -543,48 +606,63 @@ static void apm_cpu_idle(void) } } #endif +#endif + +#ifdef CONFIG_SMP +static int apm_magic(void * unused) +{ + while (1) + schedule(); +} +#endif static void apm_power_off(void) { +#ifdef CONFIG_APM_REAL_MODE_POWER_OFF + unsigned char po_bios_call[] = { + 0xb8, 0x00, 0x10, /* movw $0x1000,ax */ + 0x8e, 0xd0, /* movw ax,ss */ + 0xbc, 0x00, 0xf0, /* movw $0xf000,sp */ + 0xb8, 0x07, 0x53, /* movw $0x5307,ax */ + 0xbb, 0x01, 0x00, /* movw $0x0001,bx */ + 0xb9, 0x03, 0x00, /* movw $0x0003,cx */ + 0xcd, 0x15 /* int $0x15 */ + }; +#endif + /* - * smp_hack == 2 means that we would have enabled APM support - * except there is more than one processor and so most of - * the APM stuff is unsafe. We will still try power down - * because is is useful to some people and they know what - * they are doing because they booted with the smp-power-off - * kernel option. + * This may be called on an SMP machine. */ - if (apm_enabled || (smp_hack == 2)) { +#ifdef CONFIG_SMP + /* Some bioses don't like being called from CPU != 0 */ + while (cpu_number_map[smp_processor_id()] != 0) { + kernel_thread(apm_magic, NULL, + CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD); + schedule(); + } +#endif #ifdef CONFIG_APM_REAL_MODE_POWER_OFF - unsigned char po_bios_call[] = { - 0xb8, 0x00, 0x10, /* movw $0x1000,ax */ - 0x8e, 0xd0, /* movw ax,ss */ - 0xbc, 0x00, 0xf0, /* movw $0xf000,sp */ - 0xb8, 0x07, 0x53, /* movw $0x5307,ax */ - 0xbb, 0x01, 0x00, /* movw $0x0001,bx */ - 0xb9, 0x03, 0x00, /* movw $0x0003,cx */ - 0xcd, 0x15 /* int $0x15 */ - }; - - machine_real_restart(po_bios_call, sizeof(po_bios_call)); + machine_real_restart(po_bios_call, sizeof(po_bios_call)); #else - (void) apm_set_power_state(APM_STATE_OFF); + (void) apm_set_power_state(APM_STATE_OFF); #endif - } } -#ifdef CONFIG_APM_DO_ENABLE -static int __init apm_enable_power_management(void) +static int apm_enable_power_management(int enable) { u32 eax; + if ((enable == 0) && (apm_bios_info.flags & APM_BIOS_DISENGAGED)) + return APM_NOT_ENGAGED; if (apm_bios_call_simple(APM_FUNC_ENABLE_PM, APM_DEVICE_BALL, - 1, &eax)) + enable, &eax)) return (eax >> 8) & 0xff; - apm_bios_info.flags &= ~APM_BIOS_DISABLED; + if (enable) + apm_bios_info.flags &= ~APM_BIOS_DISABLED; + else + apm_bios_info.flags |= APM_BIOS_DISABLED; return APM_SUCCESS; } -#endif static int apm_get_power_status(u_short *status, u_short *bat, u_short *life) { @@ -632,12 +710,21 @@ static int apm_get_battery_status(u_short which, u_short *status, } #endif -static int __init apm_engage_power_management(u_short device) +static int apm_engage_power_management(u_short device, int enable) { u32 eax; - if (apm_bios_call_simple(APM_FUNC_ENGAGE_PM, device, 1, &eax)) + if ((enable == 0) && (device == APM_DEVICE_ALL) + && (apm_bios_info.flags & APM_BIOS_DISABLED)) + return APM_DISABLED; + if (apm_bios_call_simple(APM_FUNC_ENGAGE_PM, device, enable, &eax)) return (eax >> 8) & 0xff; + if (device == APM_DEVICE_ALL) { + if (enable) + apm_bios_info.flags &= ~APM_BIOS_DISENGAGED; + else + apm_bios_info.flags |= APM_BIOS_DISENGAGED; + } return APM_SUCCESS; } @@ -655,7 +742,6 @@ static void apm_error(char *str, int err) } #if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT) -/* Called from console driver -- must make sure apm_enabled. */ static int apm_console_blank(int blank) { int error; @@ -664,9 +750,13 @@ static int apm_console_blank(int blank) state = blank ? APM_STATE_STANDBY : APM_STATE_READY; /* Blank the first display device */ error = set_power_state(0x100, state); - if (error != APM_SUCCESS) + if ((error != APM_SUCCESS) && (error != APM_NO_ERROR)) { /* try to blank them all instead */ error = set_power_state(0x1ff, state); + if ((error != APM_SUCCESS) && (error != APM_NO_ERROR)) + /* try to blank device one instead */ + error = set_power_state(0x101, state); + } if ((error == APM_SUCCESS) || (error == APM_NO_ERROR)) return 1; apm_error("set display", error); @@ -674,20 +764,20 @@ static int apm_console_blank(int blank) } #endif -static int queue_empty(struct apm_bios_struct * as) +static int queue_empty(struct apm_user *as) { return as->event_head == as->event_tail; } -static apm_event_t get_queued_event(struct apm_bios_struct * as) +static apm_event_t get_queued_event(struct apm_user *as) { as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS; return as->events[as->event_tail]; } -static void queue_event(apm_event_t event, struct apm_bios_struct *sender) +static void queue_event(apm_event_t event, struct apm_user *sender) { - struct apm_bios_struct * as; + struct apm_user * as; if (user_list == NULL) return; @@ -751,18 +841,11 @@ static void get_time_diff(void) #endif } -static void suspend(void) +static void reinit_timer(void) { - int err; #ifdef INIT_TIMER_AFTER_SUSPEND unsigned long flags; -#endif - get_time_diff(); - err = apm_set_power_state(APM_STATE_SUSPEND); - if ((err != APM_SUCCESS) && (err != APM_NO_ERROR)) - apm_error("suspend", err); -#ifdef INIT_TIMER_AFTER_SUSPEND save_flags(flags); cli(); /* set the clock to 100 Hz */ @@ -774,7 +857,27 @@ static void suspend(void) udelay(10); restore_flags(flags); #endif +} + +static int suspend(void) +{ + int err; + int ret; + struct apm_user *as; + + get_time_diff(); + err = apm_set_power_state(APM_STATE_SUSPEND); + reinit_timer(); set_time(); + ret = (err == APM_SUCCESS) || (err == APM_NO_ERROR); + if (!ret) + apm_error("suspend", err); + for (as = user_list; as != NULL; as = as->next) { + as->suspend_wait = 0; + as->suspend_result = (ret ? 0 : -EIO); + } + wake_up_interruptible(&apm_suspend_waitqueue); + return ret; } static void standby(void) @@ -806,15 +909,14 @@ static apm_event_t get_event(void) return 0; } -static int send_event(apm_event_t event, apm_event_t undo, - struct apm_bios_struct *sender) +static int send_event(apm_event_t event, struct apm_user *sender) { switch (event) { case APM_SYS_SUSPEND: case APM_CRITICAL_SUSPEND: case APM_USER_SUSPEND: /* map all suspends to ACPI D3 */ - if (pm_send_request(PM_SUSPEND, (void*) 3)) { + if (pm_send_request(PM_SUSPEND, (void *)3)) { if (apm_bios_info.version > 0x100) apm_set_power_state(APM_STATE_REJECT); return 0; @@ -823,11 +925,7 @@ static int send_event(apm_event_t event, apm_event_t undo, case APM_NORMAL_RESUME: case APM_CRITICAL_RESUME: /* map all resumes to ACPI D0 */ - if (pm_send_request(PM_RESUME, 0)) { - if (apm_bios_info.version > 0x100) - apm_set_power_state(APM_STATE_REJECT); - return 0; - } + (void) pm_send_request(PM_RESUME, (void *)0); break; } @@ -864,7 +962,7 @@ static void check_events(void) if (waiting_for_resume) break; #endif - if (send_event(event, APM_STANDBY_RESUME, NULL)) { + if (send_event(event, NULL)) { #ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND waiting_for_resume = 1; #endif @@ -888,12 +986,12 @@ static void check_events(void) if (waiting_for_resume) break; #endif - if (send_event(event, APM_NORMAL_RESUME, NULL)) { + if (send_event(event, NULL)) { #ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND waiting_for_resume = 1; #endif if (suspends_pending <= 0) - suspend(); + (void) suspend(); } break; @@ -908,13 +1006,13 @@ static void check_events(void) ignore_bounce = 1; #endif set_time(); - send_event(event, 0, NULL); + send_event(event, NULL); break; case APM_CAPABILITY_CHANGE: case APM_LOW_BATTERY: case APM_POWER_STATUS_CHANGE: - send_event(event, 0, NULL); + send_event(event, NULL); break; case APM_UPDATE_TIME: @@ -922,7 +1020,7 @@ static void check_events(void) break; case APM_CRITICAL_SUSPEND: - suspend(); + (void) suspend(); break; } } @@ -930,18 +1028,18 @@ static void check_events(void) static void apm_event_handler(void) { - static int pending_count = 0; + static int pending_count = 4; int err; if ((standbys_pending > 0) || (suspends_pending > 0)) { - if ((apm_bios_info.version > 0x100) && (pending_count-- <= 0)) { + if ((apm_bios_info.version > 0x100) && (pending_count-- < 0)) { pending_count = 4; err = apm_set_power_state(APM_STATE_BUSY); if (err) apm_error("busy", err); } } else - pending_count = 0; + pending_count = 4; check_events(); } @@ -957,13 +1055,17 @@ static void apm_event_handler(void) static void apm_mainloop(void) { DECLARE_WAITQUEUE(wait, current); - apm_enabled = 1; + + if (smp_num_cpus > 1) + return; add_wait_queue(&apm_waitqueue, &wait); current->state = TASK_INTERRUPTIBLE; for (;;) { /* Nothing to do, just sleep for the timeout */ schedule_timeout(APM_CHECK_TIMEOUT); + if (exit_kapmd) + break; /* * Ok, check all events, check for idle (and mark us sleeping @@ -976,11 +1078,11 @@ static void apm_mainloop(void) continue; if (apm_do_idle()) { unsigned long start = jiffies; - do { + while (system_idle()) { apm_do_idle(); if (jiffies - start > APM_CHECK_TIMEOUT) break; - } while (system_idle()); + } apm_do_busy(); apm_event_handler(); } @@ -988,7 +1090,7 @@ static void apm_mainloop(void) } } -static int check_apm_bios_struct(struct apm_bios_struct *as, const char *func) +static int check_apm_user(struct apm_user *as, const char *func) { if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) { printk(KERN_ERR "apm: %s passed bad filp", func); @@ -999,13 +1101,13 @@ static int check_apm_bios_struct(struct apm_bios_struct *as, const char *func) static ssize_t do_read(struct file *fp, char *buf, size_t count, loff_t *ppos) { - struct apm_bios_struct * as; + struct apm_user * as; int i; apm_event_t event; DECLARE_WAITQUEUE(wait, current); as = fp->private_data; - if (check_apm_bios_struct(as, "read")) + if (check_apm_user(as, "read")) return -EIO; if (count < sizeof(apm_event_t)) return -EINVAL; @@ -1053,10 +1155,10 @@ repeat: static unsigned int do_poll(struct file *fp, poll_table * wait) { - struct apm_bios_struct * as; + struct apm_user * as; as = fp->private_data; - if (check_apm_bios_struct(as, "select")) + if (check_apm_user(as, "poll")) return 0; poll_wait(fp, &apm_waitqueue, wait); if (!queue_empty(as)) @@ -1067,11 +1169,11 @@ static unsigned int do_poll(struct file *fp, poll_table * wait) static int do_ioctl(struct inode * inode, struct file *filp, u_int cmd, u_long arg) { - struct apm_bios_struct * as; - int send_ok = 1; + struct apm_user * as; + DECLARE_WAITQUEUE(wait, current); as = filp->private_data; - if (check_apm_bios_struct(as, "ioctl")) + if (check_apm_user(as, "ioctl")) return -EIO; if (!as->suser) return -EPERM; @@ -1081,11 +1183,9 @@ static int do_ioctl(struct inode * inode, struct file *filp, as->standbys_read--; as->standbys_pending--; standbys_pending--; - } - else - send_ok = send_event(APM_USER_STANDBY, - APM_STANDBY_RESUME, as); - if (send_ok && (standbys_pending <= 0)) + } else if (!send_event(APM_USER_STANDBY, as)) + return -EAGAIN; + if (standbys_pending <= 0) standby(); break; case APM_IOC_SUSPEND: @@ -1093,12 +1193,25 @@ static int do_ioctl(struct inode * inode, struct file *filp, as->suspends_read--; as->suspends_pending--; suspends_pending--; + } else if (!send_event(APM_USER_SUSPEND, as)) + return -EAGAIN; + if (suspends_pending <= 0) { + if (!suspend()) + return -EIO; + } else { + as->suspend_wait = 1; + add_wait_queue(&apm_suspend_waitqueue, &wait); + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + if ((as->suspend_wait == 0) + || signal_pending(current)) + break; + schedule(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&apm_suspend_waitqueue, &wait); + return as->suspend_result; } - else - send_ok = send_event(APM_USER_SUSPEND, - APM_NORMAL_RESUME, as); - if (send_ok && (suspends_pending <= 0)) - suspend(); break; default: return -EINVAL; @@ -1108,10 +1221,10 @@ static int do_ioctl(struct inode * inode, struct file *filp, static int do_release(struct inode * inode, struct file * filp) { - struct apm_bios_struct * as; + struct apm_user * as; as = filp->private_data; - if (check_apm_bios_struct(as, "release")) + if (check_apm_user(as, "release")) return 0; filp->private_data = NULL; if (as->standbys_pending > 0) { @@ -1122,12 +1235,12 @@ static int do_release(struct inode * inode, struct file * filp) if (as->suspends_pending > 0) { suspends_pending -= as->suspends_pending; if (suspends_pending <= 0) - suspend(); + (void) suspend(); } if (user_list == as) user_list = as->next; else { - struct apm_bios_struct * as1; + struct apm_user * as1; for (as1 = user_list; (as1 != NULL) && (as1->next != as); @@ -1139,17 +1252,21 @@ static int do_release(struct inode * inode, struct file * filp) as1->next = as->next; } kfree_s(as, sizeof(*as)); + MOD_DEC_USE_COUNT; return 0; } static int do_open(struct inode * inode, struct file * filp) { - struct apm_bios_struct * as; + struct apm_user * as; - as = (struct apm_bios_struct *)kmalloc(sizeof(*as), GFP_KERNEL); + MOD_INC_USE_COUNT; + + as = (struct apm_user *)kmalloc(sizeof(*as), GFP_KERNEL); if (as == NULL) { printk(KERN_ERR "apm: cannot allocate struct of size %d bytes", sizeof(*as)); + MOD_DEC_USE_COUNT; return -ENOMEM; } as->magic = APM_BIOS_MAGIC; @@ -1184,11 +1301,10 @@ static int apm_get_info(char *buf, char **start, off_t fpos, int length) int time_units = -1; char *units = "?"; - if (!apm_enabled) - return 0; p = buf; - if (!(error = apm_get_power_status(&bx, &cx, &dx))) { + if ((smp_num_cpus == 1) && + !(error = apm_get_power_status(&bx, &cx, &dx))) { ac_line_status = (bx >> 8) & 0xff; battery_status = bx & 0xff; if ((cx & 0xff) != 0xff) @@ -1264,6 +1380,11 @@ static int apm(void *unused) char * power_stat; char * bat_stat; + kapmd_running = 1; + + exit_files(current); /* daemonize doesn't do exit_files */ + daemonize(); + strcpy(current->comm, "kapmd"); sigfillset(¤t->blocked); @@ -1278,7 +1399,7 @@ static int apm(void *unused) apm_bios_info.version = 0x100; } } - if (debug) { + if (debug && (smp_num_cpus == 1)) { printk(KERN_INFO "apm: Connection version %d.%d\n", (apm_bios_info.version >> 8) & 0xff, apm_bios_info.version & 0xff); @@ -1328,32 +1449,51 @@ static int apm(void *unused) * is booted with PM disabled but not in the docking station. * Unfortunate ... */ - error = apm_enable_power_management(); + error = apm_enable_power_management(1); if (error) { apm_error("enable power management", error); return -1; } } #endif - if (((apm_bios_info.flags & APM_BIOS_DISENGAGED) == 0) + if ((apm_bios_info.flags & APM_BIOS_DISENGAGED) && (apm_bios_info.version > 0x0100)) { - if (apm_engage_power_management(0x0001) == APM_SUCCESS) - apm_bios_info.flags &= ~APM_BIOS_DISENGAGED; + error = apm_engage_power_management(APM_DEVICE_ALL, 1); + if (error) { + apm_error("engage power management", error); + return -1; + } } /* Install our power off handler.. */ - if (power_off_enabled) + if (power_off) pm_power_off = apm_power_off; #ifdef CONFIG_MAGIC_SYSRQ sysrq_power_off = apm_power_off; #endif #if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT) - console_blank_hook = apm_console_blank; + if (smp_num_cpus == 1) + console_blank_hook = apm_console_blank; #endif pm_active = 1; apm_mainloop(); + + pm_active = 0; + +#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT) + if (smp_num_cpus == 1) + console_blank_hook = NULL; +#endif +#ifdef CONFIG_MAGIC_SYSRQ + sysrq_power_off = NULL; +#endif + if (power_off) + pm_power_off = NULL; + + kapmd_running = 0; + return 0; } @@ -1371,10 +1511,9 @@ static int __init apm_setup(char *str) str += 3; if (strncmp(str, "debug", 5) == 0) debug = !invert; - if (strncmp(str, "power-off", 9) == 0) - power_off_enabled = !invert; - if (strncmp(str, "smp-power-off", 13) == 0) - smp_hack = !invert; + if ((strncmp(str, "power-off", 9) == 0) || + (strncmp(str, "power_off", 9) == 0)) + power_off = !invert; str = strchr(str, ','); if (str != NULL) str += strspn(str, ", \t"); @@ -1394,7 +1533,7 @@ static struct file_operations apm_bios_fops = { static struct miscdevice apm_device = { APM_MINOR_DEV, - "apm", + "apm_bios", &apm_bios_fops }; @@ -1455,7 +1594,10 @@ static int __init apm_init(void) printk(KERN_NOTICE "apm: disabled on user request.\n"); APM_INIT_ERROR_RETURN; } - + if ((smp_num_cpus > 1) && !power_off) { + printk(KERN_NOTICE "apm: disabled - APM is not SMP safe.\n"); + APM_INIT_ERROR_RETURN; + } if (PM_IS_ACTIVE()) { printk(KERN_NOTICE "apm: overridden by ACPI.\n"); APM_INIT_ERROR_RETURN; @@ -1472,9 +1614,6 @@ static int __init apm_init(void) _set_limit((char *)&gdt[APM_40 >> 3], 4095 - (0x40 << 4)); apm_bios_entry.offset = apm_bios_info.offset; -#ifdef CONFIG_APM_BAD_ENTRY_OFFSET - apm_bios_entry.offset &= 0xffff; -#endif apm_bios_entry.segment = APM_CS; set_base(gdt[APM_CS >> 3], __va((unsigned long)apm_bios_info.cseg << 4)); @@ -1483,18 +1622,16 @@ static int __init apm_init(void) set_base(gdt[APM_DS >> 3], __va((unsigned long)apm_bios_info.dseg << 4)); #ifndef APM_RELAX_SEGMENTS - if (apm_bios_info.version == 0x100) + if (apm_bios_info.version == 0x100) { #endif - { /* For ASUS motherboard, Award BIOS rev 110 (and others?) */ _set_limit((char *)&gdt[APM_CS >> 3], 64 * 1024 - 1); /* For some unknown machine. */ _set_limit((char *)&gdt[APM_CS_16 >> 3], 64 * 1024 - 1); /* For the DEC Hinote Ultra CT475 (and others?) */ _set_limit((char *)&gdt[APM_DS >> 3], 64 * 1024 - 1); - } #ifndef APM_RELAX_SEGMENTS - else { + } else { _set_limit((char *)&gdt[APM_CS >> 3], (apm_bios_info.cseg_len - 1) & 0xffff); _set_limit((char *)&gdt[APM_CS_16 >> 3], @@ -1504,21 +1641,36 @@ static int __init apm_init(void) } #endif -#ifdef CONFIG_SMP + create_proc_info_entry("apm", 0, NULL, apm_get_info); + + kernel_thread(apm, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD); + if (smp_num_cpus > 1) { - printk(KERN_NOTICE "apm: disabled - APM is not SMP safe.\n"); - if (smp_hack) - smp_hack = 2; + printk(KERN_NOTICE + "apm: disabled - APM is not SMP safe (power off active).\n"); APM_INIT_ERROR_RETURN; } -#endif - - create_proc_info_entry("apm", 0, 0, apm_get_info); misc_register(&apm_device); - kernel_thread(apm, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD); return 0; } -__initcall(apm_init); +static void __exit apm_exit(void) +{ + misc_deregister(&apm_device); + remove_proc_entry("apm", NULL); + exit_kapmd = 1; + while (kapmd_running) + schedule(); +} + +module_init(apm_init); +module_exit(apm_exit); + +MODULE_AUTHOR("Stephen Rothwell"); +MODULE_DESCRIPTION("Advanced Power Management"); +MODULE_PARM(debug, "i"); +MODULE_PARM_DESC(debug, "Enable debug mode"); + +EXPORT_NO_SYMBOLS; diff --git a/arch/i386/kernel/i386_ksyms.c b/arch/i386/kernel/i386_ksyms.c index f58e7485f..cad6ceb17 100644 --- a/arch/i386/kernel/i386_ksyms.c +++ b/arch/i386/kernel/i386_ksyms.c @@ -10,6 +10,7 @@ #include <linux/smp_lock.h> #include <linux/pm.h> #include <linux/pci.h> +#include <linux/apm_bios.h> #include <asm/semaphore.h> #include <asm/processor.h> @@ -20,6 +21,7 @@ #include <asm/delay.h> #include <asm/irq.h> #include <asm/mmx.h> +#include <asm/desc.h> extern void dump_thread(struct pt_regs *, struct user *); extern int dump_fpu(elf_fpregset_t *); @@ -34,6 +36,8 @@ extern struct drive_info_struct drive_info; EXPORT_SYMBOL(drive_info); #endif +extern unsigned long get_cmos_time(void); + /* platform dependent support */ EXPORT_SYMBOL(boot_cpu_data); EXPORT_SYMBOL(EISA_bus); @@ -51,6 +55,9 @@ EXPORT_SYMBOL(probe_irq_mask); EXPORT_SYMBOL(kernel_thread); EXPORT_SYMBOL(pm_idle); EXPORT_SYMBOL(pm_power_off); +EXPORT_SYMBOL(get_cmos_time); +EXPORT_SYMBOL(apm_bios_info); +EXPORT_SYMBOL(gdt); EXPORT_SYMBOL_NOVERS(__down_failed); EXPORT_SYMBOL_NOVERS(__down_failed_interruptible); diff --git a/arch/i386/kernel/i8259.c b/arch/i386/kernel/i8259.c index c62e5c2d2..d54f9b503 100644 --- a/arch/i386/kernel/i8259.c +++ b/arch/i386/kernel/i8259.c @@ -311,7 +311,7 @@ spurious_8259A_irq: } } -void init_8259A(int auto_eoi) +void __init init_8259A(int auto_eoi) { unsigned long flags; @@ -384,7 +384,7 @@ static struct irqaction irq2 = { no_action, 0, 0, "cascade", NULL, NULL}; #endif -void init_ISA_irqs (void) +void __init init_ISA_irqs (void) { int i; diff --git a/arch/i386/kernel/io_apic.c b/arch/i386/kernel/io_apic.c index 0037bebdd..75b2bfb9f 100644 --- a/arch/i386/kernel/io_apic.c +++ b/arch/i386/kernel/io_apic.c @@ -11,6 +11,9 @@ * Hidemi Kishimoto <kisimoto@css1.kbnes.nec.co.jp>, * further tested and cleaned up by Zach Brown <zab@redhat.com> * and Ingo Molnar <mingo@redhat.com> + * + * Fixes + * Maciej W. Rozycki : Bits for genuine 82489DX APICs */ #include <linux/mm.h> @@ -249,7 +252,7 @@ int IO_APIC_get_PCI_irq_vector(int bus, int slot, int pci_pin) best_guess = irq; } } - return -1; + return best_guess; } /* @@ -906,40 +909,6 @@ void disable_IO_APIC(void) } } -static void __init setup_ioapic_default_id(void) -{ - struct IO_APIC_reg_00 reg_00; - - /* - * 'default' mptable configurations mean a hardwired setup, - * 2 CPUs, 16 APIC registers. IO-APIC ID is usually set to 0, - * setting it to ID 2 should be fine. - */ - - /* - * Sanity check, is ID 2 really free? Every APIC in the - * system must have a unique ID or we get lots of nice - * 'stuck on smp_invalidate_needed IPI wait' messages. - */ - if (phys_cpu_present_map & (1<<0x2)) - panic("APIC ID 2 already used"); - - /* - * Set the ID - */ - *(int *)®_00 = io_apic_read(0, 0); - printk("...changing IO-APIC physical APIC ID to 2...\n"); - reg_00.ID = 0x2; - io_apic_write(0, 0, *(int *)®_00); - - /* - * Sanity check - */ - *(int *)®_00 = io_apic_read(0, 0); - if (reg_00.ID != 0x2) - panic("could not set ID"); -} - /* * function to set the IO-APIC physical IDs based on the * values stored in the MPC table. @@ -967,6 +936,15 @@ static void __init setup_ioapic_ids_from_mpc (void) printk("...changing IO-APIC physical APIC ID to %d ...", mp_ioapics[apic].mpc_apicid); + /* + * Sanity check, is the ID really free? Every APIC in the + * system must have a unique ID or we get lots of nice + * 'stuck on smp_invalidate_needed IPI wait' messages. + */ + if (phys_cpu_present_map & (1<<mp_ioapics[apic].mpc_apicid)) + panic("APIC ID %d already used", + mp_ioapics[apic].mpc_apicid); + reg_00.ID = mp_ioapics[apic].mpc_apicid; io_apic_write(apic, 0, *(int *)®_00); @@ -1021,7 +999,6 @@ static void __init construct_default_ISA_mptable(void) mp_irqs[0].mpc_dstirq = 2; } - setup_ioapic_default_id(); } /* @@ -1264,6 +1241,7 @@ static inline void check_timer(void) /* * get/set the timer IRQ vector: */ + disable_8259A_irq(0); vector = assign_irq_vector(0); set_intr_gate(vector, interrupt[0]); @@ -1390,7 +1368,6 @@ void IO_APIC_init_uniprocessor (void) { if (!smp_found_config) return; - phys_cpu_present_map = 0xff; setup_local_APIC(); setup_IO_APIC(); setup_APIC_clocks(); diff --git a/arch/i386/kernel/irq.c b/arch/i386/kernel/irq.c index 6112ac036..9d4a81041 100644 --- a/arch/i386/kernel/irq.c +++ b/arch/i386/kernel/irq.c @@ -713,6 +713,7 @@ unsigned long probe_irq_on(void) if (!(status & IRQ_WAITING)) { irq_desc[i].status = status & ~IRQ_AUTODETECT; irq_desc[i].handler->shutdown(i); + continue; } if (i < 32) diff --git a/arch/i386/kernel/microcode.c b/arch/i386/kernel/microcode.c new file mode 100644 index 000000000..26b6525d8 --- /dev/null +++ b/arch/i386/kernel/microcode.c @@ -0,0 +1,266 @@ +/* + * CPU Microcode Update interface for Linux + * + * Copyright (C) 2000 Tigran Aivazian + * + * This driver allows to upgrade microcode on Intel processors + * belonging to P6 family - PentiumPro, Pentium II, Pentium III etc. + * + * Reference: Section 8.10 of Volume III, Intel Pentium III Manual, + * Order Number 243192 or download from: + * + * http://developer.intel.com/design/pentiumii/manuals/243192.htm + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * 1.0 16 February 2000, Tigran Aivazian <tigran@sco.com> + * Initial release. + * 1.01 18 February 2000, Tigran Aivazian <tigran@sco.com> + * Added read() support + cleanups. + */ + +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/module.h> +#include <linux/vmalloc.h> +#include <linux/smp_lock.h> +#include <linux/proc_fs.h> + +#include <asm/msr.h> +#include <asm/uaccess.h> +#include <asm/processor.h> + +#define MICROCODE_VERSION "1.01" + +MODULE_DESCRIPTION("CPU (P6) microcode update driver"); +MODULE_AUTHOR("Tigran Aivazian <tigran@ocston.org>"); +EXPORT_NO_SYMBOLS; + +/* VFS interface */ +static int microcode_open(struct inode *, struct file *); +static int microcode_release(struct inode *, struct file *); +static ssize_t microcode_read(struct file *, char *, size_t, loff_t *); +static ssize_t microcode_write(struct file *, const char *, size_t, loff_t *); + + +/* internal helpers to do the work */ +static int do_microcode_update(void); +static void do_update_one(void *); + +/* + * Bits in microcode_status. (31 bits of room for future expansion) + */ +#define MICROCODE_IS_OPEN 0 /* set if /dev/microcode is in use */ +static unsigned long microcode_status = 0; + +/* the actual array of microcode blocks, each 2048 bytes */ +static struct microcode * microcode = NULL; +static unsigned int microcode_num = 0; +static char *mc_applied = NULL; /* holds an array of applied microcode blocks */ + +static struct file_operations microcode_fops = { + read: microcode_read, + write: microcode_write, + open: microcode_open, + release: microcode_release, +}; + +static struct inode_operations microcode_inops = { + default_file_ops: µcode_fops, +}; + +static struct proc_dir_entry *proc_microcode; + +static int __init microcode_init(void) +{ + int size; + + proc_microcode = create_proc_entry("microcode", S_IWUSR|S_IRUSR, proc_root_driver); + if (!proc_microcode) { + printk(KERN_ERR "microcode: can't create /proc/driver/microcode\n"); + return -ENOMEM; + } + proc_microcode->ops = µcode_inops; + size = smp_num_cpus * sizeof(struct microcode); + mc_applied = kmalloc(size, GFP_KERNEL); + if (!mc_applied) { + remove_proc_entry("microcode", proc_root_driver); + printk(KERN_ERR "microcode: can't allocate memory for saved microcode\n"); + return -ENOMEM; + } + memset(mc_applied, 0, size); /* so that reading from offsets corresponding to failed + update makes this obvious */ + printk(KERN_INFO "P6 Microcode Update Driver v%s registered\n", MICROCODE_VERSION); + return 0; +} + +static void __exit microcode_exit(void) +{ + remove_proc_entry("microcode", proc_root_driver); + kfree(mc_applied); + printk(KERN_INFO "P6 Microcode Update Driver v%s unregistered\n", MICROCODE_VERSION); +} + +module_init(microcode_init); +module_exit(microcode_exit); + +/* + * We enforce only one user at a time here with open/close. + */ +static int microcode_open(struct inode *inode, struct file *file) +{ + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + + /* one at a time, please */ + if (test_and_set_bit(MICROCODE_IS_OPEN, µcode_status)) + return -EBUSY; + + MOD_INC_USE_COUNT; + + return 0; +} + +static int microcode_release(struct inode *inode, struct file *file) +{ + MOD_DEC_USE_COUNT; + + clear_bit(MICROCODE_IS_OPEN, µcode_status); + return 0; +} + +/* a pointer to 'struct update_req' is passed to the IPI hanlder = do_update_one() + * update_req[cpu].err is set to 1 if update failed on 'cpu', 0 otherwise + * if err==0, microcode[update_req[cpu].slot] points to applied block of microcode + */ +struct update_req { + int err; + int slot; +} update_req[NR_CPUS]; + +static int do_microcode_update(void) +{ + int i, error = 0, err; + struct microcode *m; + + if (smp_call_function(do_update_one, (void *)update_req, 1, 1) != 0) + panic("do_microcode_update(): timed out waiting for other CPUs\n"); + do_update_one((void *)update_req); + + for (i=0; i<smp_num_cpus; i++) { + err = update_req[i].err; + error += err; + if (!err) { + m = (struct microcode *)mc_applied + i; + memcpy(m, µcode[update_req[i].slot], sizeof(struct microcode)); + } + } + return error ? -EIO : 0; +} + +static void do_update_one(void *arg) +{ + struct update_req *req; + struct cpuinfo_x86 * c; + unsigned int pf = 0, val[2], rev, sig; + int i, cpu_num; + + cpu_num = smp_processor_id(); + c = cpu_data + cpu_num; + req = (struct update_req *)arg + cpu_num; + req->err = 1; /* be pessimistic */ + + if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 < 6) + return; + + sig = c->x86_mask + (c->x86_model<<4) + (c->x86<<8); + + if (c->x86_model >= 5) { + /* get processor flags from BBL_CR_OVRD MSR (0x17) */ + rdmsr(0x17, val[0], val[1]); + pf = 1 << ((val[1] >> 18) & 7); + } + + for (i=0; i<microcode_num; i++) + if (microcode[i].sig == sig && microcode[i].pf == pf && + microcode[i].ldrver == 1 && microcode[i].hdrver == 1) { + + rdmsr(0x8B, val[0], rev); + if (microcode[i].rev <= rev) { + printk(KERN_ERR + "microcode: CPU%d not 'upgrading' to earlier revision" + " %d (current=%d)\n", cpu_num, microcode[i].rev, rev); + } else { + int sum = 0; + struct microcode *m = µcode[i]; + unsigned int *sump = (unsigned int *)(m+1); + + while (--sump >= (unsigned int *)m) + sum += *sump; + if (sum != 0) { + printk(KERN_ERR "microcode: CPU%d aborting, " + "bad checksum\n", cpu_num); + break; + } + + wrmsr(0x79, (unsigned int)(m->bits), 0); + __asm__ __volatile__ ("cpuid"); + rdmsr(0x8B, val[0], val[1]); + + req->err = 0; + req->slot = i; + printk(KERN_ERR "microcode: CPU%d microcode updated " + "from revision %d to %d, date=%08x\n", + cpu_num, rev, val[1], m->date); + } + break; + } +} + +static ssize_t microcode_read(struct file *file, char *buf, size_t len, loff_t *ppos) +{ + size_t fsize = smp_num_cpus * sizeof(struct microcode); + + if (!proc_microcode->size || *ppos >= fsize) + return 0; /* EOF */ + if (*ppos + len > fsize) + len = fsize - *ppos; + if (copy_to_user(buf, mc_applied + *ppos, len)) + return -EFAULT; + *ppos += len; + return len; +} + +static ssize_t microcode_write(struct file *file, const char *buf, size_t len, loff_t *ppos) +{ + ssize_t ret; + + if (len % sizeof(struct microcode) != 0) { + printk(KERN_ERR "microcode: can only write in N*%d bytes units\n", + sizeof(struct microcode)); + return -EINVAL; + } + lock_kernel(); + microcode_num = len/sizeof(struct microcode); + microcode = vmalloc(len); + if (!microcode) { + unlock_kernel(); + return -ENOMEM; + } + if (copy_from_user(microcode, buf, len)) { + vfree(microcode); + unlock_kernel(); + return -EFAULT; + } + ret = do_microcode_update(); + if (!ret) { + proc_microcode->size = smp_num_cpus * sizeof(struct microcode); + ret = (ssize_t)len; + } + vfree(microcode); + unlock_kernel(); + return ret; +} diff --git a/arch/i386/kernel/mpparse.c b/arch/i386/kernel/mpparse.c index 977dd18ba..81685d2f5 100644 --- a/arch/i386/kernel/mpparse.c +++ b/arch/i386/kernel/mpparse.c @@ -9,7 +9,7 @@ * Erich Boleyn : MP v1.4 and additional changes. * Alan Cox : Added EBDA scanning * Ingo Molnar : various cleanups and rewrites - * Maciej W. Rozycki : Bits for genuine 82489DX timers + * Maciej W. Rozycki : Bits for genuine 82489DX APICs */ #include <linux/mm.h> @@ -339,6 +339,8 @@ static int __init smp_get_mpf(struct intel_mp_floating *mpf) * Now see if we need to read further. */ if (mpf->mpf_feature1 != 0) { + printk("Default MP configuration #%d\n", mpf->mpf_feature1); + /* * local APIC has default address */ @@ -352,6 +354,7 @@ static int __init smp_get_mpf(struct intel_mp_floating *mpf) nr_ioapics = 1; mp_ioapics[0].mpc_apicaddr = 0xFEC00000; + mp_ioapics[0].mpc_apicid = 2; /* * Save the default type number, we * need it later to set the IO-APIC diff --git a/arch/i386/kernel/mtrr.c b/arch/i386/kernel/mtrr.c index 5caa4e477..a0a4ab851 100644 --- a/arch/i386/kernel/mtrr.c +++ b/arch/i386/kernel/mtrr.c @@ -1,6 +1,6 @@ /* Generic MTRR (Memory Type Range Register) driver. - Copyright (C) 1997-1999 Richard Gooch + Copyright (C) 1997-2000 Richard Gooch This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -133,7 +133,7 @@ Fixed version numbering and history for v1.23 -> v1.24. v1.26 19990118 Richard Gooch <rgooch@atnf.csiro.au> - PLACEHOLDER. + Added devfs support. v1.27 19990123 Richard Gooch <rgooch@atnf.csiro.au> Changed locking to spin with reschedule. @@ -178,7 +178,6 @@ Moved to linux/arch/i386/kernel/setup.c and linux/include/asm-i386/bugs.h 19990228 Richard Gooch <rgooch@atnf.csiro.au> - Added #ifdef CONFIG_DEVFS_FS Added MTRRIOC_KILL_ENTRY ioctl(2) Trap for counter underflow in <mtrr_file_del>. Trap for 4 MiB aligned regions for PPro, stepping <= 7. @@ -225,6 +224,10 @@ success. 19991008 Manfred Spraul <manfreds@colorfullife.com> replaced spin_lock_reschedule() with a normal semaphore. + v1.36 + 20000221 Richard Gooch <rgooch@atnf.csiro.au> + Compile fix if procfs and devfs not enabled. + Formatting changes. */ #include <linux/types.h> #include <linux/errno.h> @@ -241,6 +244,7 @@ #include <linux/fs.h> #include <linux/ctype.h> #include <linux/proc_fs.h> +#include <linux/devfs_fs_kernel.h> #include <linux/mm.h> #include <linux/module.h> #define MTRR_NEED_STRINGS @@ -261,7 +265,7 @@ #include <asm/hardirq.h> #include <linux/irq.h> -#define MTRR_VERSION "1.35 (19990512)" +#define MTRR_VERSION "1.36 (20000221)" #define TRUE 1 #define FALSE 0 @@ -305,11 +309,15 @@ typedef u8 mtrr_type; TRUE) #endif -#ifndef CONFIG_PROC_FS +#if defined(CONFIG_PROC_FS) || defined(CONFIG_DEVFS_FS) +# define USERSPACE_INTERFACE +#endif + +#ifndef USERSPACE_INTERFACE # define compute_ascii() while (0) #endif -#ifdef CONFIG_PROC_FS +#ifdef USERSPACE_INTERFACE static char *ascii_buffer = NULL; static unsigned int ascii_buf_bytes = 0; #endif @@ -317,7 +325,7 @@ static unsigned int *usage_table = NULL; static DECLARE_MUTEX(main_lock); /* Private functions */ -#ifdef CONFIG_PROC_FS +#ifdef USERSPACE_INTERFACE static void compute_ascii (void); #endif @@ -480,8 +488,9 @@ static void intel_get_mtrr (unsigned int reg, unsigned long *base, unsigned long dummy, mask_lo, base_lo; rdmsr (MTRRphysMask_MSR(reg), mask_lo, dummy); - if ((mask_lo & 0x800) == 0) { - /* Invalid (i.e. free) range. */ + if ( (mask_lo & 0x800) == 0 ) + { + /* Invalid (i.e. free) range */ *base = 0; *size = 0; *type = 0; @@ -537,22 +546,26 @@ static void cyrix_get_arr (unsigned int reg, unsigned long *base, *size = 0; /* Bit 0 is Cache Enable on ARR7, Cache Disable on ARR0-ARR6 */ - if (reg < 7) { - switch (rcr) { - case 1: *type = MTRR_TYPE_UNCACHABLE; break; - case 8: *type = MTRR_TYPE_WRBACK; break; - case 9: *type = MTRR_TYPE_WRCOMB; break; - case 24: - default: *type = MTRR_TYPE_WRTHROUGH; break; - } - } else { - switch (rcr) { - case 0: *type = MTRR_TYPE_UNCACHABLE; break; - case 8: *type = MTRR_TYPE_WRCOMB; break; - case 9: *type = MTRR_TYPE_WRBACK; break; - case 25: - default: *type = MTRR_TYPE_WRTHROUGH; break; - } + if (reg < 7) + { + switch (rcr) + { + case 1: *type = MTRR_TYPE_UNCACHABLE; break; + case 8: *type = MTRR_TYPE_WRBACK; break; + case 9: *type = MTRR_TYPE_WRCOMB; break; + case 24: + default: *type = MTRR_TYPE_WRTHROUGH; break; + } + } else + { + switch (rcr) + { + case 0: *type = MTRR_TYPE_UNCACHABLE; break; + case 8: *type = MTRR_TYPE_WRCOMB; break; + case 9: *type = MTRR_TYPE_WRBACK; break; + case 25: + default: *type = MTRR_TYPE_WRTHROUGH; break; + } } } /* End Function cyrix_get_arr */ @@ -653,20 +666,24 @@ static void cyrix_set_arr_up (unsigned int reg, unsigned long base, size &= 0x7fff; /* make sure arr_size <= 14 */ for(arr_size = 0; size; arr_size++, size >>= 1); - if (reg<7) { - switch (type) { - case MTRR_TYPE_UNCACHABLE: arr_type = 1; break; - case MTRR_TYPE_WRCOMB: arr_type = 9; break; - case MTRR_TYPE_WRTHROUGH: arr_type = 24; break; - default: arr_type = 8; break; - } - } else { - switch (type) { - case MTRR_TYPE_UNCACHABLE: arr_type = 0; break; - case MTRR_TYPE_WRCOMB: arr_type = 8; break; - case MTRR_TYPE_WRTHROUGH: arr_type = 25; break; - default: arr_type = 9; break; - } + if (reg<7) + { + switch (type) { + case MTRR_TYPE_UNCACHABLE: arr_type = 1; break; + case MTRR_TYPE_WRCOMB: arr_type = 9; break; + case MTRR_TYPE_WRTHROUGH: arr_type = 24; break; + default: arr_type = 8; break; + } + } + else + { + switch (type) + { + case MTRR_TYPE_UNCACHABLE: arr_type = 0; break; + case MTRR_TYPE_WRCOMB: arr_type = 8; break; + case MTRR_TYPE_WRTHROUGH: arr_type = 25; break; + default: arr_type = 9; break; + } } if (do_safe) set_mtrr_prepare (&ctxt); @@ -779,16 +796,18 @@ static int __init set_mtrr_var_range_testing (unsigned int index, int changed = FALSE; rdmsr(MTRRphysBase_MSR(index), lo, hi); - if ((vr->base_lo & 0xfffff0ffUL) != (lo & 0xfffff0ffUL) - || (vr->base_hi & 0xfUL) != (hi & 0xfUL)) { - wrmsr(MTRRphysBase_MSR(index), vr->base_lo, vr->base_hi); + if ( (vr->base_lo & 0xfffff0ffUL) != (lo & 0xfffff0ffUL) + || (vr->base_hi & 0xfUL) != (hi & 0xfUL) ) + { + wrmsr (MTRRphysBase_MSR(index), vr->base_lo, vr->base_hi); changed = TRUE; } - rdmsr(MTRRphysMask_MSR(index), lo, hi); + rdmsr (MTRRphysMask_MSR(index), lo, hi); - if ((vr->mask_lo & 0xfffff800UL) != (lo & 0xfffff800UL) - || (vr->mask_hi & 0xfUL) != (hi & 0xfUL)) { + if ( (vr->mask_lo & 0xfffff800UL) != (lo & 0xfffff800UL) + || (vr->mask_hi & 0xfUL) != (hi & 0xfUL) ) + { wrmsr(MTRRphysMask_MSR(index), vr->mask_lo, vr->mask_hi); changed = TRUE; } @@ -816,22 +835,27 @@ static int __init set_fixed_ranges_testing(mtrr_type *frs) unsigned long lo, hi; rdmsr(MTRRfix64K_00000_MSR, lo, hi); - if (p[0] != lo || p[1] != hi) { - wrmsr(MTRRfix64K_00000_MSR, p[0], p[1]); + if (p[0] != lo || p[1] != hi) + { + wrmsr (MTRRfix64K_00000_MSR, p[0], p[1]); changed = TRUE; } - for (i = 0; i < 2; i++) { - rdmsr(MTRRfix16K_80000_MSR + i, lo, hi); - if (p[2 + i*2] != lo || p[3 + i*2] != hi) { - wrmsr(MTRRfix16K_80000_MSR + i, p[2 + i*2], p[3 + i*2]); + for (i = 0; i < 2; i++) + { + rdmsr (MTRRfix16K_80000_MSR + i, lo, hi); + if (p[2 + i*2] != lo || p[3 + i*2] != hi) + { + wrmsr (MTRRfix16K_80000_MSR + i, p[2 + i*2], p[3 + i*2]); changed = TRUE; } } - for (i = 0; i < 8; i++) { - rdmsr(MTRRfix4K_C0000_MSR + i, lo, hi); - if (p[6 + i*2] != lo || p[7 + i*2] != hi) { + for (i = 0; i < 8; i++) + { + rdmsr (MTRRfix4K_C0000_MSR + i, lo, hi); + if (p[6 + i*2] != lo || p[7 + i*2] != hi) + { wrmsr(MTRRfix4K_C0000_MSR + i, p[6 + i*2], p[7 + i*2]); changed = TRUE; } @@ -899,8 +923,8 @@ static unsigned long __init set_mtrr_state (struct mtrr_state *state, change_mask |= MTRR_CHANGE_MASK_FIXED; /* Set_mtrr_restore restores the old value of MTRRdefType, so to set it we fiddle with the saved value */ - if ((ctxt->deftype_lo & 0xff) != state->def_type - || ((ctxt->deftype_lo & 0xc00) >> 10) != state->enabled) + if ( (ctxt->deftype_lo & 0xff) != state->def_type + || ( (ctxt->deftype_lo & 0xc00) >> 10 ) != state->enabled) { ctxt->deftype_lo |= (state->def_type | state->enabled << 10); change_mask |= MTRR_CHANGE_MASK_DEFTYPE; @@ -1010,7 +1034,7 @@ static void init_table (void) return; } for (i = 0; i < max; i++) usage_table[i] = 1; -#ifdef CONFIG_PROC_FS +#ifdef USERSPACE_INTERFACE if ( ( ascii_buffer = kmalloc (max * LINE_SIZE, GFP_KERNEL) ) == NULL ) { printk ("mtrr: could not allocate\n"); @@ -1053,11 +1077,14 @@ static int cyrix_get_free_region (unsigned long base, unsigned long size) unsigned long lbase, lsize; /* If we are to set up a region >32M then look at ARR7 immediately */ - if (size > 0x2000000UL) { + if (size > 0x2000000UL) + { cyrix_get_arr (7, &lbase, &lsize, <ype); if (lsize < 1) return 7; - /* else try ARR0-ARR6 first */ - } else { + /* Else try ARR0-ARR6 first */ + } + else + { for (i = 0; i < 7; i++) { cyrix_get_arr (i, &lbase, &lsize, <ype); @@ -1095,29 +1122,32 @@ int mtrr_add (unsigned long base, unsigned long size, unsigned int type, switch (boot_cpu_data.x86_vendor) { case X86_VENDOR_AMD: - if (boot_cpu_data.x86 < 6) { /* pre-Athlon CPUs */ - /* Apply the K6 block alignment and size rules + if (boot_cpu_data.x86 < 6) + { /* pre-Athlon CPUs */ + /* Apply the K6 block alignment and size rules In order o Uncached or gathering only o 128K or bigger block o Power of 2 block o base suitably aligned to the power */ - if (type > MTRR_TYPE_WRCOMB || size < (1 << 17) || - (size & ~(size-1))-size || (base & (size-1))) - return -EINVAL; + if ( type > MTRR_TYPE_WRCOMB || size < (1 << 17) || + (size & ~(size-1))-size || ( base & (size-1) ) ) + return -EINVAL; break; - } /* else fall through */ + } + /* Else fall through */ case X86_VENDOR_INTEL: - /* Double check for Intel, we may run on Athlon. */ - if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) { - /* For Intel PPro stepping <= 7, must be 4 MiB aligned */ - if ( (boot_cpu_data.x86 == 6) && (boot_cpu_data.x86_model == 1) && - (boot_cpu_data.x86_mask <= 7) && ( base & ( (1 << 22) - 1 ) ) ) - { - printk ("mtrr: base(0x%lx) is not 4 MiB aligned\n", base); - return -EINVAL; - } + /* Double check for Intel, we may run on Athlon */ + if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) + { + /* For Intel PPro stepping <= 7, must be 4 MiB aligned */ + if ( (boot_cpu_data.x86 == 6) && (boot_cpu_data.x86_model == 1) && + (boot_cpu_data.x86_mask <= 7) && ( base & ( (1 << 22) -1 ) ) ) + { + printk ("mtrr: base(0x%lx) is not 4 MiB aligned\n", base); + return -EINVAL; + } } /* Fall through */ case X86_VENDOR_CYRIX: @@ -1232,7 +1262,7 @@ int mtrr_del (int reg, unsigned long base, unsigned long size) if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return -ENODEV; max = get_num_var_ranges (); - down(&main_lock); + down (&main_lock); if (reg < 0) { /* Search for existing MTRR */ @@ -1254,15 +1284,15 @@ int mtrr_del (int reg, unsigned long base, unsigned long size) } if (reg >= max) { - up(&main_lock); + up (&main_lock); printk ("mtrr: register: %d too big\n", reg); return -EINVAL; } if (boot_cpu_data.x86_vendor == X86_VENDOR_CYRIX) { - if ((reg == 3) && arr3_protected) + if ( (reg == 3) && arr3_protected ) { - up(&main_lock); + up (&main_lock); printk ("mtrr: ARR3 cannot be changed\n"); return -EINVAL; } @@ -1270,23 +1300,23 @@ int mtrr_del (int reg, unsigned long base, unsigned long size) (*get_mtrr) (reg, &lbase, &lsize, <ype); if (lsize < 1) { - up(&main_lock); + up (&main_lock); printk ("mtrr: MTRR %d not used\n", reg); return -EINVAL; } if (usage_table[reg] < 1) { - up(&main_lock); + up (&main_lock); printk ("mtrr: reg: %d has count=0\n", reg); return -EINVAL; } if (--usage_table[reg] < 1) set_mtrr (reg, 0, 0, 0); compute_ascii (); - up(&main_lock); + up (&main_lock); return reg; } /* End Function mtrr_del */ -#ifdef CONFIG_PROC_FS +#ifdef USERSPACE_INTERFACE static int mtrr_file_add (unsigned long base, unsigned long size, unsigned int type, char increment, struct file *file) @@ -1469,18 +1499,25 @@ static int mtrr_close (struct inode *ino, struct file *file) static struct file_operations mtrr_fops = { - read: mtrr_read, - write: mtrr_write, - ioctl: mtrr_ioctl, - release: mtrr_close, + read: mtrr_read, + write: mtrr_write, + ioctl: mtrr_ioctl, + release: mtrr_close, }; -static struct inode_operations proc_mtrr_inode_operations = { - &mtrr_fops, /* default property file-ops */ +# ifdef CONFIG_PROC_FS + +static struct inode_operations proc_mtrr_inode_operations = +{ + &mtrr_fops, /* default property file-ops */ }; static struct proc_dir_entry *proc_root_mtrr; +# endif /* CONFIG_PROC_FS */ + +static devfs_handle_t devfs_handle = NULL; + static void compute_ascii (void) { char factor; @@ -1515,25 +1552,30 @@ static void compute_ascii (void) ascii_buf_bytes += strlen (ascii_buffer + ascii_buf_bytes); } } + devfs_set_file_size (devfs_handle, ascii_buf_bytes); +# ifdef CONFIG_PROC_FS proc_root_mtrr->size = ascii_buf_bytes; +# endif /* CONFIG_PROC_FS */ } /* End Function compute_ascii */ -#endif /* CONFIG_PROC_FS */ +#endif /* USERSPACE_INTERFACE */ EXPORT_SYMBOL(mtrr_add); EXPORT_SYMBOL(mtrr_del); #ifdef __SMP__ -typedef struct { - unsigned long base; - unsigned long size; - mtrr_type type; +typedef struct +{ + unsigned long base; + unsigned long size; + mtrr_type type; } arr_state_t; -arr_state_t arr_state[8] __initdata = { - {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL}, - {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL} +arr_state_t arr_state[8] __initdata = +{ + {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL}, + {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL} }; unsigned char ccr_state[7] __initdata = { 0, 0, 0, 0, 0, 0, 0 }; @@ -1591,31 +1633,35 @@ static void __init cyrix_arr_init(void) ccr[5] = getCx86 (CX86_CCR5); ccr[6] = getCx86 (CX86_CCR6); - if (ccr[3] & 1) { - ccrc[3] = 1; - arr3_protected = 1; - } else { - /* Disable SMM mode (bit 1), access to SMM memory (bit 2) and - * access to SMM memory through ARR3 (bit 7). - */ - if (ccr[1] & 0x80) { ccr[1] &= 0x7f; ccrc[1] |= 0x80; } - if (ccr[1] & 0x04) { ccr[1] &= 0xfb; ccrc[1] |= 0x04; } - if (ccr[1] & 0x02) { ccr[1] &= 0xfd; ccrc[1] |= 0x02; } - arr3_protected = 0; - if (ccr[6] & 0x02) { - ccr[6] &= 0xfd; ccrc[6] = 1; /* Disable write protection of ARR3. */ - setCx86 (CX86_CCR6, ccr[6]); - } - /* Disable ARR3. This is safe now that we disabled SMM. */ - /* cyrix_set_arr_up (3, 0, 0, 0, FALSE); */ + if (ccr[3] & 1) + { + ccrc[3] = 1; + arr3_protected = 1; + } + else + { + /* Disable SMM mode (bit 1), access to SMM memory (bit 2) and + * access to SMM memory through ARR3 (bit 7). + */ + if (ccr[1] & 0x80) { ccr[1] &= 0x7f; ccrc[1] |= 0x80; } + if (ccr[1] & 0x04) { ccr[1] &= 0xfb; ccrc[1] |= 0x04; } + if (ccr[1] & 0x02) { ccr[1] &= 0xfd; ccrc[1] |= 0x02; } + arr3_protected = 0; + if (ccr[6] & 0x02) { + ccr[6] &= 0xfd; ccrc[6] = 1; /* Disable write protection of ARR3 */ + setCx86 (CX86_CCR6, ccr[6]); + } + /* Disable ARR3. This is safe now that we disabled SMM. */ + /* cyrix_set_arr_up (3, 0, 0, 0, FALSE); */ } /* If we changed CCR1 in memory, change it in the processor, too. */ if (ccrc[1]) setCx86 (CX86_CCR1, ccr[1]); /* Enable ARR usage by the processor */ - if (!(ccr[5] & 0x20)) { - ccr[5] |= 0x20; ccrc[5] = 1; - setCx86 (CX86_CCR5, ccr[5]); + if (!(ccr[5] & 0x20)) + { + ccr[5] |= 0x20; ccrc[5] = 1; + setCx86 (CX86_CCR5, ccr[5]); } #ifdef __SMP__ @@ -1667,11 +1713,14 @@ static void __init mtrr_setup(void) switch (boot_cpu_data.x86_vendor) { case X86_VENDOR_AMD: - if (boot_cpu_data.x86 < 6) { /* pre-Athlon CPUs */ - get_mtrr = amd_get_mtrr; - set_mtrr_up = amd_set_mtrr_up; - break; - } /* else fall through */ + if (boot_cpu_data.x86 < 6) + { + /* pre-Athlon CPUs */ + get_mtrr = amd_get_mtrr; + set_mtrr_up = amd_set_mtrr_up; + break; + } + /* Else fall through */ case X86_VENDOR_INTEL: get_mtrr = intel_get_mtrr; set_mtrr_up = intel_set_mtrr_up; @@ -1700,7 +1749,7 @@ void __init mtrr_init_boot_cpu(void) switch (boot_cpu_data.x86_vendor) { case X86_VENDOR_AMD: - if (boot_cpu_data.x86 < 6) break; /* pre-Athlon CPUs */ + if (boot_cpu_data.x86 < 6) break; /* Pre-Athlon CPUs */ case X86_VENDOR_INTEL: get_mtrr_state (&smp_mtrr_state); break; @@ -1738,7 +1787,7 @@ void __init mtrr_init_secondary_cpu(void) switch (boot_cpu_data.x86_vendor) { case X86_VENDOR_AMD: - /* Just for robustness: pre-Athlon CPUs cannot do SMP. */ + /* Just for robustness: pre-Athlon CPUs cannot do SMP */ if (boot_cpu_data.x86 < 6) break; case X86_VENDOR_INTEL: intel_mtrr_init_secondary_cpu (); @@ -1762,17 +1811,17 @@ void __init mtrr_init_secondary_cpu(void) int __init mtrr_init(void) { if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return 0; -# ifdef __SMP__ +#ifdef __SMP__ switch (boot_cpu_data.x86_vendor) { case X86_VENDOR_AMD: - if (boot_cpu_data.x86 < 6) break; /* pre-Athlon CPUs */ + if (boot_cpu_data.x86 < 6) break; /* Pre-Athlon CPUs */ case X86_VENDOR_INTEL: finalize_mtrr_state (&smp_mtrr_state); mtrr_state_warn (smp_changes_mask); break; } -# else /* __SMP__ */ +#else /* __SMP__ */ mtrr_setup (); switch (boot_cpu_data.x86_vendor) { @@ -1783,12 +1832,17 @@ int __init mtrr_init(void) centaur_mcr_init (); break; } -# endif /* !__SMP__ */ +#endif /* !__SMP__ */ -# ifdef CONFIG_PROC_FS - proc_root_mtrr = create_proc_entry("mtrr", S_IWUSR|S_IRUGO, &proc_root); +#ifdef CONFIG_PROC_FS + proc_root_mtrr = create_proc_entry ("mtrr", S_IWUSR | S_IRUGO, &proc_root); proc_root_mtrr->ops = &proc_mtrr_inode_operations; -#endif +#endif +#ifdev CONFIG_DEVFS_FS + devfs_handle = devfs_register (NULL, "cpu/mtrr", 0, DEVFS_FL_DEFAULT, 0, 0, + S_IFREG | S_IRUGO | S_IWUSR, 0, 0, + &mtrr_fops, NULL); +#endif init_table (); return 0; } /* End Function mtrr_init */ diff --git a/arch/i386/kernel/pci-dma.c b/arch/i386/kernel/pci-dma.c index 18b203228..4bd1486fe 100644 --- a/arch/i386/kernel/pci-dma.c +++ b/arch/i386/kernel/pci-dma.c @@ -13,20 +13,6 @@ #include <linux/pci.h> #include <asm/io.h> -/* Pure 2^n version of get_order */ -extern __inline__ int __get_order(unsigned long size) -{ - int order; - - size = (size-1) >> (PAGE_SHIFT-1); - order = -1; - do { - size >>= 1; - order++; - } while (size); - return order; -} - void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, dma_addr_t *dma_handle) { @@ -35,7 +21,7 @@ void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, if (hwdev == NULL || hwdev->dma_mask != 0xffffffff) gfp |= GFP_DMA; - ret = (void *)__get_free_pages(gfp, __get_order(size)); + ret = (void *)__get_free_pages(gfp, get_order(size)); if (ret != NULL) { memset(ret, 0, size); @@ -47,5 +33,5 @@ void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, void pci_free_consistent(struct pci_dev *hwdev, size_t size, void *vaddr, dma_addr_t dma_handle) { - free_pages((unsigned long)vaddr, __get_order(size)); + free_pages((unsigned long)vaddr, get_order(size)); } diff --git a/arch/i386/kernel/pci-i386.c b/arch/i386/kernel/pci-i386.c index ee2edca1e..6dd0a4306 100644 --- a/arch/i386/kernel/pci-i386.c +++ b/arch/i386/kernel/pci-i386.c @@ -132,7 +132,7 @@ pcibios_align_resource(void *data, struct resource *res, unsigned long size) /* We need to avoid collisions with `mirrored' VGA ports and other strange ISA hardware, so we always want the addresses kilobyte aligned. */ - if (size >= 0x100) { + if (size > 0x100) { printk(KERN_ERR "PCI: I/O Region %s/%d too large" " (%ld bytes)\n", dev->slot_name, dev->resource - res, size); diff --git a/arch/i386/kernel/pci-pc.c b/arch/i386/kernel/pci-pc.c index 590e01fd5..d5b2b0062 100644 --- a/arch/i386/kernel/pci-pc.c +++ b/arch/i386/kernel/pci-pc.c @@ -883,6 +883,18 @@ static void __init pci_fixup_i450nx(struct pci_dev *d) } } +static void __init pci_fixup_i450gx(struct pci_dev *d) +{ + /* + * i450GX and i450KX -- Find and scan all secondary buses. + * (called separately for each PCI bridge found) + */ + u8 busno; + pci_read_config_byte(d, 0x4a, &busno); + printk("PCI: i440KX/GX host bridge %s: secondary bus %02x\n", d->slot_name, busno); + pci_scan_bus(busno, pci_root_ops, NULL); +} + static void __init pci_fixup_rcc(struct pci_dev *d) { /* @@ -954,6 +966,7 @@ static void __init pci_fixup_ide_trash(struct pci_dev *d) struct pci_fixup pcibios_fixups[] = { { PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82451NX, pci_fixup_i450nx }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82454GX, pci_fixup_i450gx }, { PCI_FIXUP_HEADER, PCI_VENDOR_ID_RCC, PCI_DEVICE_ID_RCC_HE, pci_fixup_rcc }, { PCI_FIXUP_HEADER, PCI_VENDOR_ID_RCC, PCI_DEVICE_ID_RCC_LE, pci_fixup_rcc }, { PCI_FIXUP_HEADER, PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_6010, pci_fixup_compaq }, @@ -1036,6 +1049,18 @@ static void __init pcibios_irq_peer_trick(struct irq_routing_table *rt) * table, but unfortunately we have to know the interrupt router chip. */ +/* + * Never use: 0, 1, 2 (timer, keyboard, and cascade) + * Avoid using: 13, 14 and 15 (FP error and IDE). + * Penalize: 3, 4, 7, 12 (known ISA uses: serial, parallel and mouse) + */ +static unsigned int pcibios_irq_mask = 0xfff8; + +static unsigned pcibios_irq_penalty[16] = { + 10000, 10000, 10000, 100, 100, 0, 0, 100, + 0, 0, 0, 0, 100, 1000, 1000, 1000 +}; + static char *pcibios_lookup_irq(struct pci_dev *dev, struct irq_routing_table *rt, int pin, int assign) { struct irq_info *q; @@ -1043,6 +1068,7 @@ static char *pcibios_lookup_irq(struct pci_dev *dev, struct irq_routing_table *r int i, pirq, newirq; u32 rtrid, mask; u8 x; + char *msg = NULL; pin--; DBG("IRQ for %s(%d)", dev->slot_name, pin); @@ -1066,10 +1092,15 @@ static char *pcibios_lookup_irq(struct pci_dev *dev, struct irq_routing_table *r return NULL; } DBG(" -> PIRQ %02x, mask %04x", pirq, mask); - if (!assign || (dev->class >> 8) == PCI_CLASS_DISPLAY_VGA) - newirq = 0; - else for(newirq = 13; newirq && !(mask & (1 << newirq)); newirq--) - ; + newirq = 0; + if (assign && (dev->class >> 8) != PCI_CLASS_DISPLAY_VGA) { + for (i = 0; i < 16; i++) { + if (!(mask & pcibios_irq_mask & (1 << i))) + continue; + if (pcibios_irq_penalty[i] < pcibios_irq_penalty[newirq]) + newirq = i; + } + } if (!(router = pci_find_slot(rt->rtr_bus, rt->rtr_devfn))) { DBG(" -> router not found\n"); return NULL; @@ -1094,27 +1125,30 @@ static char *pcibios_lookup_irq(struct pci_dev *dev, struct irq_routing_table *r pci_read_config_byte(router, pirq, &x); if (x < 16) { DBG(" -> [PIIX] %02x\n", x); - dev->irq = x; - return "PIIX"; + newirq = x; + msg = "PIIX"; } else if (newirq) { DBG(" -> [PIIX] set to %02x\n", newirq); pci_write_config_byte(router, pirq, newirq); - dev->irq = newirq; - return "PIIX-NEW"; - } - DBG(" -> [PIIX] sink\n"); - return NULL; + msg = "PIIX-NEW"; + } else + DBG(" -> [PIIX] sink\n"); + break; case ID(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533): default: DBG(" -> unknown router %04x/%04x\n", rt->rtr_vendor, rt->rtr_device); if (newirq && mask == (1 << newirq)) { /* Only one IRQ available -> use it */ - dev->irq = newirq; - return "guess"; + msg = "guess"; } - return NULL; } #undef ID + + if (msg) { + dev->irq = newirq; + pcibios_irq_penalty[newirq]++; + } + return msg; } static void __init pcibios_fixup_irqs(void) @@ -1134,6 +1168,18 @@ static void __init pcibios_fixup_irqs(void) pcibios_irq_peer_trick(rtable); pci_for_each_dev(dev) { + /* + * If the BIOS has set an out of range IRQ number, just ignore it. + * Also keep track of which IRQ's are already in use. + */ + if (dev->irq >= 16) { + DBG("%s: ignoring bogus IRQ %d\n", dev->slot_name, dev->irq); + dev->irq = 0; + } + pcibios_irq_penalty[dev->irq]++; + } + + pci_for_each_dev(dev) { pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); #if defined(CONFIG_X86_IO_APIC) /* @@ -1171,10 +1217,8 @@ static void __init pcibios_fixup_irqs(void) } #endif /* - * Fix out-of-range IRQ numbers and missing IRQs. + * Still no IRQ? Try to assign one... */ - if (dev->irq >= NR_IRQS) - dev->irq = 0; if (pin && !dev->irq && pirq_table) { char *msg = pcibios_lookup_irq(dev, pirq_table, pin, 0); if (msg) @@ -1280,6 +1324,9 @@ char * __init pcibios_setup(char *str) } else if (!strcmp(str, "rom")) { pci_probe |= PCI_ASSIGN_ROMS; return NULL; + } else if (!strncmp(str, "irqmask=", 8)) { + pcibios_irq_mask = simple_strtol(str+8, NULL, 0); + return NULL; } return str; } diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c index c38e383e7..0f61ca543 100644 --- a/arch/i386/kernel/process.c +++ b/arch/i386/kernel/process.c @@ -48,16 +48,6 @@ asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); int hlt_counter=0; -void disable_hlt(void) -{ - hlt_counter++; -} - -void enable_hlt(void) -{ - hlt_counter--; -} - /* * Powermanagement idle function, if any.. */ @@ -68,6 +58,16 @@ void (*pm_idle)(void) = NULL; */ void (*pm_power_off)(void) = NULL; +void disable_hlt(void) +{ + hlt_counter++; +} + +void enable_hlt(void) +{ + hlt_counter--; +} + /* * We use this if we don't have any better * idle routine.. @@ -293,7 +293,7 @@ void machine_real_restart(unsigned char *code, int length) void machine_restart(char * __unused) { -#if __SMP__ +#if CONFIG_SMP /* * Stop all CPUs and turn off local APICs and the IO-APIC, so * other OSs see a clean IRQ state. diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index e72f95160..cd2a3d8af 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -1515,7 +1515,7 @@ unsigned long cpu_initialized = 0; * and IDT. We reload them nevertheless, this function acts as a * 'CPU state barrier', nothing should get across. */ -void cpu_init (void) +void __init cpu_init (void) { int nr = smp_processor_id(); struct tss_struct * t = &init_tss[nr]; diff --git a/arch/i386/kernel/smp.c b/arch/i386/kernel/smp.c index 0b585513f..12f193c71 100644 --- a/arch/i386/kernel/smp.c +++ b/arch/i386/kernel/smp.c @@ -103,7 +103,7 @@ /* The 'big kernel lock' */ spinlock_t kernel_flag = SPIN_LOCK_UNLOCKED; -struct tlb_state cpu_tlbstate[NR_CPUS]; +struct tlb_state cpu_tlbstate[NR_CPUS] = {[0 ... NR_CPUS-1] = { &init_mm, 0 }}; /* * the following functions deal with sending IPIs between CPUs. @@ -347,12 +347,7 @@ asmlinkage void smp_invalidate_interrupt (void) __flush_tlb_one(flush_va); } else leave_mm(cpu); - } else { - extern void show_stack (void *); - printk("hm #1: %p, %p.\n", flush_mm, cpu_tlbstate[cpu].active_mm); - show_stack(NULL); } - __flush_tlb(); ack_APIC_irq(); clear_bit(cpu, &flush_cpumask); } @@ -382,7 +377,7 @@ static void flush_tlb_others (unsigned long cpumask, struct mm_struct *mm, * Temporarily this turns IRQs off, so that lockups are * detected by the NMI watchdog. */ - spin_lock_irq(&tlbstate_lock); + spin_lock(&tlbstate_lock); flush_mm = mm; flush_va = va; @@ -398,7 +393,7 @@ static void flush_tlb_others (unsigned long cpumask, struct mm_struct *mm, flush_mm = NULL; flush_va = 0; - spin_unlock_irq(&tlbstate_lock); + spin_unlock(&tlbstate_lock); } void flush_tlb_current_task(void) diff --git a/arch/i386/mm/init.c b/arch/i386/mm/init.c index 4ef25674f..c3991b056 100644 --- a/arch/i386/mm/init.c +++ b/arch/i386/mm/init.c @@ -307,27 +307,30 @@ static void __init pagetable_init(void) pmd_t *pmd; pte_t *pte; int i, j, k; - unsigned long vaddr; - unsigned long end = (unsigned long)__va(max_low_pfn*PAGE_SIZE); + unsigned long vaddr, end; - pgd_base = swapper_pg_dir; + end = (unsigned long)__va(max_low_pfn*PAGE_SIZE) - 1; - vaddr = PAGE_OFFSET; - i = __pgd_offset(vaddr); + i = __pgd_offset(PAGE_OFFSET); + pgd_base = swapper_pg_dir; pgd = pgd_base + i; - for (; (i < PTRS_PER_PGD) && (vaddr <= end); pgd++, i++) { + for (; i < PTRS_PER_PGD; pgd++, i++) { vaddr = i*PGDIR_SIZE; + if (vaddr >= end) + break; #if CONFIG_X86_PAE - pmd = (pmd_t *) alloc_bootmem_low_pages(PAGE_SIZE); + pmd = (pmd_t *) alloc_bootmem_pages(PAGE_SIZE); set_pgd(pgd, __pgd(__pa(pmd) + 0x1)); #else pmd = (pmd_t *)pgd; #endif if (pmd != pmd_offset(pgd, 0)) BUG(); - for (j = 0; (j < PTRS_PER_PMD) && (vaddr <= end); pmd++, j++) { + for (j = 0; j < PTRS_PER_PMD; pmd++, j++) { vaddr = i*PGDIR_SIZE + j*PMD_SIZE; + if (vaddr >= end) + break; if (cpu_has_pse) { unsigned long __pe; @@ -349,10 +352,10 @@ static void __init pagetable_init(void) if (pte != pte_offset(pmd, 0)) BUG(); - for (k = 0; - (k < PTRS_PER_PTE) && (vaddr <= end); - pte++, k++) { + for (k = 0; k < PTRS_PER_PTE; pte++, k++) { vaddr = i*PGDIR_SIZE + j*PMD_SIZE + k*PAGE_SIZE; + if (vaddr >= end) + break; *pte = mk_pte_phys(__pa(vaddr), PAGE_KERNEL); } } |