diff options
Diffstat (limited to 'arch')
39 files changed, 2020 insertions, 161 deletions
diff --git a/arch/alpha/kernel/signal.c b/arch/alpha/kernel/signal.c index ff12b9e8d..1f70c98e6 100644 --- a/arch/alpha/kernel/signal.c +++ b/arch/alpha/kernel/signal.c @@ -36,6 +36,36 @@ asmlinkage int do_signal(sigset_t *, struct pt_regs *, struct switch_stack *, unsigned long, unsigned long); +int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from) +{ + if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t))) + return -EFAULT; + if (from->si_code < 0) + return __copy_to_user(to, from, sizeof(siginfo_t)); + else { + int err; + + /* If you change siginfo_t structure, please be sure + this code is fixed accordingly. + It should never copy any pad contained in the structure + to avoid security leaks, but must copy the generic + 3 ints plus the relevant union member. */ + err = __put_user(*(long *)&from->si_signo, (long *)&to->si_signo); + err |= __put_user((short)from->si_code, &to->si_code); + switch (from->si_code >> 16) { + case __SI_CHLD >> 16: + err |= __put_user(from->si_utime, &to->si_utime); + err |= __put_user(from->si_stime, &to->si_stime); + err |= __put_user(from->si_status, &to->si_status); + default: + err |= __put_user(from->si_addr, &to->si_addr); + break; + /* case __SI_RT: This is not generated by the kernel as of now. */ + } + return err; + } +} + /* * The OSF/1 sigprocmask calling sequence is different from the * C sigprocmask() sequence.. @@ -489,7 +519,7 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, if (verify_area(VERIFY_WRITE, frame, sizeof(*frame))) goto give_sigsegv; - err |= __copy_to_user(&frame->info, info, sizeof(siginfo_t)); + err |= copy_siginfo_to_user(&frame->info, info); /* Create the ucontext. */ err |= __put_user(0, &frame->uc.uc_flags); diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index 54d962790..15f8fb4cb 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c @@ -34,6 +34,41 @@ asmlinkage int do_signal(sigset_t *oldset, struct pt_regs * regs, int syscall); extern int ptrace_cancel_bpt (struct task_struct *); extern int ptrace_set_bpt (struct task_struct *); +int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from) +{ + if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t))) + return -EFAULT; + if (from->si_code < 0) + return __copy_to_user(to, from, sizeof(siginfo_t)); + else { + int err; + + /* If you change siginfo_t structure, please be sure + this code is fixed accordingly. + It should never copy any pad contained in the structure + to avoid security leaks, but must copy the generic + 3 ints plus the relevant union member. */ + err = __put_user(from->si_signo, &to->si_signo); + err |= __put_user(from->si_errno, &to->si_errno); + err |= __put_user((short)from->si_code, &to->si_code); + /* First 32bits of unions are always present. */ + err |= __put_user(from->si_pid, &to->si_pid); + switch (from->si_code >> 16) { + case __SI_FAULT >> 16: + break; + case __SI_CHLD >> 16: + err |= __put_user(from->si_utime, &to->si_utime); + err |= __put_user(from->si_stime, &to->si_stime); + err |= __put_user(from->si_status, &to->si_status); + default: + err |= __put_user(from->si_uid, &to->si_uid); + break; + /* case __SI_RT: This is not generated by the kernel as of now. */ + } + return err; + } +} + /* * atomically swap in the new signal mask, and wait for a signal. */ @@ -370,7 +405,7 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, err |= __put_user(&frame->info, &frame->pinfo); err |= __put_user(&frame->uc, &frame->puc); - err |= __copy_to_user(&frame->info, info, sizeof(*info)); + err |= copy_siginfo_to_user(&frame->info, info); /* Clear all the bits of the ucontext we don't use. */ err |= __clear_user(&frame->uc, offsetof(struct ucontext, uc_mcontext)); diff --git a/arch/i386/Makefile b/arch/i386/Makefile index 113a00fa6..755cc877a 100644 --- a/arch/i386/Makefile +++ b/arch/i386/Makefile @@ -49,6 +49,10 @@ ifdef CONFIG_M686 CFLAGS += $(shell if $(CC) -march=i686 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=i686"; fi) endif +ifdef CONFIG_M686FX +CFLAGS += $(shell if $(CC) -march=i686 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=i686"; fi) +endif + ifdef CONFIG_MK6 CFLAGS += $(shell if $(CC) -march=k6 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=k6"; fi) endif diff --git a/arch/i386/config.in b/arch/i386/config.in index 1208a6b82..a928efb33 100644 --- a/arch/i386/config.in +++ b/arch/i386/config.in @@ -18,13 +18,15 @@ endmenu mainmenu_option next_comment comment 'Processor type and features' choice 'Processor family' \ - "386 CONFIG_M386 \ - 486/Cx486 CONFIG_M486 \ - 586/K5/5x86/6x86/6x86MX CONFIG_M586 \ - Pentium/TSC CONFIG_M586TSC \ - PPro/P-II/P-III CONFIG_M686 \ - K6/II/III CONFIG_MK6 \ - Athlon CONFIG_MK7" PPro + "386 CONFIG_M386 \ + 486/Cx486 CONFIG_M486 \ + 586/K5/5x86/6x86/6x86MX CONFIG_M586 \ + Pentium/TSC CONFIG_M586TSC \ + PPro/Pentium-II CONFIG_M686 \ + Pentium-III CONFIG_M686FX \ + K6/K6-II/K6-III CONFIG_MK6 \ + Athlon CONFIG_MK7 \ + Crusoe CONFIG_MCRUSOE" PPro # # Define implied options from the CPU selection here # @@ -60,6 +62,14 @@ if [ "$CONFIG_M686" = "y" ]; then define_bool CONFIG_X86_PGE y define_bool CONFIG_X86_USE_PPRO_CHECKSUM y fi +if [ "$CONFIG_M686FX" = "y" ]; then + define_int CONFIG_X86_L1_CACHE_BYTES 32 + define_bool CONFIG_X86_TSC y + define_bool CONFIG_X86_GOOD_APIC y + define_bool CONFIG_X86_PGE y + define_bool CONFIG_X86_USE_PPRO_CHECKSUM y + define_bool CONFIG_X86_FX y +fi if [ "$CONFIG_MK6" = "y" ]; then define_int CONFIG_X86_L1_CACHE_BYTES 32 define_bool CONFIG_X86_ALIGNMENT_16 y @@ -74,8 +84,14 @@ if [ "$CONFIG_MK7" = "y" ]; then define_bool CONFIG_X86_PGE y define_bool CONFIG_X86_USE_PPRO_CHECKSUM y fi +if [ "$CONFIG_MCRUSOE" = "y" ]; then + define_int CONFIG_X86_L1_CACHE_BYTES 32 + define_bool CONFIG_X86_TSC y +fi tristate '/dev/cpu/microcode - Intel P6 CPU microcode support' CONFIG_MICROCODE +tristate '/dev/cpu/*/msr - Model-specific register support' CONFIG_X86_MSR +tristate '/dev/cpu/*/cpuid - CPU information support' CONFIG_X86_CPUID choice 'High Memory Support' \ "off CONFIG_NOHIGHMEM \ @@ -89,7 +105,9 @@ if [ "$CONFIG_HIGHMEM64G" = "y" ]; then define_bool CONFIG_X86_PAE y fi -bool 'Math emulation' CONFIG_MATH_EMULATION +if [ "$CONFIG_X86_FX" != "y" ]; then + bool 'Math emulation' CONFIG_MATH_EMULATION +fi bool 'MTRR (Memory Type Range Register) support' CONFIG_MTRR bool 'Symmetric multi-processing support' CONFIG_SMP if [ "$CONFIG_SMP" != "y" ]; then diff --git a/arch/i386/defconfig b/arch/i386/defconfig index e00ac753a..281a82cea 100644 --- a/arch/i386/defconfig +++ b/arch/i386/defconfig @@ -19,8 +19,10 @@ CONFIG_UID16=y # CONFIG_M586 is not set # CONFIG_M586TSC is not set CONFIG_M686=y +# CONFIG_M686FX is not set # CONFIG_MK6 is not set # CONFIG_MK7 is not set +# CONFIG_MCRUSOE is not set CONFIG_X86_WP_WORKS_OK=y CONFIG_X86_INVLPG=y CONFIG_X86_CMPXCHG=y @@ -32,6 +34,8 @@ CONFIG_X86_GOOD_APIC=y CONFIG_X86_PGE=y CONFIG_X86_USE_PPRO_CHECKSUM=y # CONFIG_MICROCODE is not set +# CONFIG_X86_MSR is not set +# CONFIG_X86_CPUID is not set CONFIG_NOHIGHMEM=y # CONFIG_HIGHMEM4G is not set # CONFIG_HIGHMEM64G is not set @@ -194,7 +198,6 @@ CONFIG_IDEPCI_SHARE_IRQ=y # CONFIG_BLK_DEV_OFFBOARD is not set # CONFIG_IDEDMA_PCI_AUTO is not set # CONFIG_BLK_DEV_IDEDMA is not set -# CONFIG_IDEDMA_PCI_EXPERIMENTAL is not set # CONFIG_IDEDMA_PCI_WIP is not set # CONFIG_IDEDMA_NEW_DRIVE_LISTINGS is not set # CONFIG_BLK_DEV_AEC62XX is not set diff --git a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile index 56db72ef8..9e9b76848 100644 --- a/arch/i386/kernel/Makefile +++ b/arch/i386/kernel/Makefile @@ -40,6 +40,22 @@ else endif endif +ifeq ($(CONFIG_X86_MSR),y) +OX_OBJS += msr.o +else + ifeq ($(CONFIG_X86_MSR),m) + MX_OBJS += msr.o + endif +endif + +ifeq ($(CONFIG_X86_CPUID),y) +OX_OBJS += cpuid.o +else + ifeq ($(CONFIG_X86_CPUID),m) + MX_OBJS += cpuid.o + endif +endif + ifeq ($(CONFIG_MICROCODE),y) OX_OBJS += microcode.o else diff --git a/arch/i386/kernel/apic.c b/arch/i386/kernel/apic.c index eab365e26..0a19bb758 100644 --- a/arch/i386/kernel/apic.c +++ b/arch/i386/kernel/apic.c @@ -128,6 +128,11 @@ void disable_local_APIC(void) void __init sync_Arb_IDs(void) { + /* + * Wait for idle. + */ + apic_wait_icr_idle(); + Dprintk("Synchronizing Arb IDs.\n"); apic_write_around(APIC_ICR, APIC_DEST_ALLINC | APIC_INT_LEVELTRIG | APIC_DM_INIT); diff --git a/arch/i386/kernel/apm.c b/arch/i386/kernel/apm.c index b1e0e8b7c..0b4517137 100644 --- a/arch/i386/kernel/apm.c +++ b/arch/i386/kernel/apm.c @@ -922,6 +922,10 @@ static int send_event(apm_event_t event, struct apm_user *sender) case APM_USER_SUSPEND: /* map all suspends to ACPI D3 */ if (pm_send_all(PM_SUSPEND, (void *)3)) { + if (event == APM_CRITICAL_SUSPEND) { + printk(KERN_CRIT "apm: Critical suspend was vetoed, expect armagedon\n" ); + return 0; + } if (apm_bios_info.version > 0x100) apm_set_power_state(APM_STATE_REJECT); return 0; @@ -934,7 +938,6 @@ static int send_event(apm_event_t event, struct apm_user *sender) break; } - queue_event(event, sender); return 1; } @@ -964,6 +967,7 @@ static void check_events(void) case APM_SYS_STANDBY: case APM_USER_STANDBY: if (send_event(event, NULL)) { + queue_event(event, NULL); if (standbys_pending <= 0) standby(); } @@ -980,17 +984,18 @@ static void check_events(void) if (ignore_bounce) break; #endif - /* - * If we are already processing a SUSPEND, - * then further SUSPEND events from the BIOS - * will be ignored. We also return here to - * cope with the fact that the Thinkpads keep - * sending a SUSPEND event until something else - * happens! - */ + /* + * If we are already processing a SUSPEND, + * then further SUSPEND events from the BIOS + * will be ignored. We also return here to + * cope with the fact that the Thinkpads keep + * sending a SUSPEND event until something else + * happens! + */ if (waiting_for_resume) - return; + return; if (send_event(event, NULL)) { + queue_event(event, NULL); waiting_for_resume = 1; if (suspends_pending <= 0) (void) suspend(); @@ -1007,6 +1012,7 @@ static void check_events(void) #endif set_time(); send_event(event, NULL); + queue_event(event, NULL); break; case APM_CAPABILITY_CHANGE: @@ -1020,6 +1026,7 @@ static void check_events(void) break; case APM_CRITICAL_SUSPEND: + send_event(event, NULL); /* We can only hope it worked; critical suspend may not fail */ (void) suspend(); break; } @@ -1056,6 +1063,7 @@ static void apm_event_handler(void) static void apm_mainloop(void) { + int timeout = HZ; DECLARE_WAITQUEUE(wait, current); if (smp_num_cpus > 1) @@ -1065,7 +1073,10 @@ static void apm_mainloop(void) current->state = TASK_INTERRUPTIBLE; for (;;) { /* Nothing to do, just sleep for the timeout */ - schedule_timeout(APM_CHECK_TIMEOUT); + timeout = 2*timeout; + if (timeout > APM_CHECK_TIMEOUT) + timeout = APM_CHECK_TIMEOUT; + schedule_timeout(timeout); if (exit_kapmd) break; @@ -1080,13 +1091,16 @@ static void apm_mainloop(void) continue; if (apm_do_idle()) { unsigned long start = jiffies; - while (system_idle()) { + while ((!exit_kapmd) && system_idle()) { apm_do_idle(); - if (jiffies - start > APM_CHECK_TIMEOUT) - break; + if (jiffies - start > (5*APM_CHECK_TIMEOUT)) { + apm_event_handler(); + start = jiffies; + } } apm_do_busy(); apm_event_handler(); + timeout = 1; } #endif } diff --git a/arch/i386/kernel/cpuid.c b/arch/i386/kernel/cpuid.c new file mode 100644 index 000000000..34c34d818 --- /dev/null +++ b/arch/i386/kernel/cpuid.c @@ -0,0 +1,168 @@ +#ident "$Id$" +/* ----------------------------------------------------------------------- * + * + * Copyright 2000 H. Peter Anvin - All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139, + * USA; either version 2 of the License, or (at your option) any later + * version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + + +/* + * cpuid.c + * + * x86 CPUID access device + * + * This device is accessed by lseek() to the appropriate CPUID level + * and then read in chunks of 16 bytes. A larger size means multiple + * reads of consecutive levels. + * + * This driver uses /dev/cpu/%d/cpuid where %d is the minor number, and on + * an SMP box will direct the access to CPU %d. + */ + +#include <linux/module.h> +#include <linux/config.h> + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/fcntl.h> +#include <linux/init.h> +#include <linux/poll.h> +#include <linux/smp.h> +#include <linux/major.h> + +#include <asm/processor.h> +#include <asm/msr.h> +#include <asm/uaccess.h> +#include <asm/system.h> + +#ifdef CONFIG_SMP + +struct cpuid_command { + int cpu; + u32 reg; + u32 *data; +}; + +static void cpuid_smp_cpuid(void *cmd_block) +{ + struct cpuid_command *cmd = (struct cpuid_command *) cmd_block; + + if ( cmd->cpu == smp_processor_id() ) + cpuid(cmd->reg, &cmd->data[0], &cmd->data[1], &cmd->data[2], &cmd->data[3]); +} + +extern inline void do_cpuid(int cpu, u32 reg, u32 *data) +{ + struct cpuid_command cmd; + + if ( cpu == smp_processor_id() ) { + cpuid(reg, &data[0], &data[1], &data[2], &data[3]); + } else { + cmd->cpu = cpu; + cmd->reg = reg; + cmd->data = data; + + smp_call_function(cpuid_smp_cpuid, (void *)cmd, 1, 1); + } +} +#else /* ! CONFIG_SMP */ + +extern inline void do_cpuid(int cpu, u32 reg, u32 *data) +{ + cpuid(reg, &data[0], &data[1], &data[2], &data[3]); +} + +#endif /* ! CONFIG_SMP */ + +static loff_t cpuid_seek(struct file *file, loff_t offset, int orig) +{ + switch (orig) { + case 0: + file->f_pos = offset; + return file->f_pos; + case 1: + file->f_pos += offset; + return file->f_pos; + default: + return -EINVAL; /* SEEK_END not supported */ + } +} + +static ssize_t cpuid_read(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + u32 *tmp = (u32 *)buf; + u32 data[4]; + size_t rv; + u32 reg = *ppos; + int cpu = MINOR(file->f_dentry->d_inode->i_rdev); + + if ( count % 16 ) + return -EINVAL; /* Invalid chunk size */ + + for ( rv = 0 ; count ; count -= 16 ) { + do_cpuid(cpu, reg, data); + if ( copy_to_user(tmp,&data,16) ) + return -EFAULT; + tmp += 4; + *ppos = reg++; + } + + return ((char *)tmp) - buf; +} + +static int cpuid_open(struct inode *inode, struct file *file) +{ + int cpu = MINOR(file->f_dentry->d_inode->i_rdev); + + if ( !(cpu_online_map & (1UL << cpu)) ) + return -ENXIO; /* No such CPU */ + + MOD_INC_USE_COUNT; + return 0; +} + +static int cpuid_release(struct inode *inode, struct file *file) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * File operations we support + */ +static struct file_operations cpuid_fops = { + llseek: cpuid_seek, + read: cpuid_read, + open: cpuid_open, + release: cpuid_release, +}; + +int __init cpuid_init(void) +{ + if (register_chrdev(CPUID_MAJOR, "cpu/cpuid", &cpuid_fops)) { + printk(KERN_ERR "cpuid: unable to get major %d for cpuid\n", + CPUID_MAJOR); + return -EBUSY; + } + + return 0; +} + +void __exit cpuid_exit(void) +{ +} + +module_init(cpuid_init); +module_exit(cpuid_exit) + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("H. Peter Anvin <hpa@zytor.com>"); +MODULE_DESCRIPTION("x86 generic CPUID driver"); diff --git a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S index 50887c15c..26f3d505d 100644 --- a/arch/i386/kernel/entry.S +++ b/arch/i386/kernel/entry.S @@ -413,6 +413,11 @@ ENTRY(spurious_interrupt_bug) pushl $ SYMBOL_NAME(do_spurious_interrupt_bug) jmp error_code +ENTRY(xmm_fault) + pushl $0 + pushl $ SYMBOL_NAME(do_xmm_fault) + jmp error_code + .data ENTRY(sys_call_table) .long SYMBOL_NAME(sys_ni_syscall) /* 0 - old "setup()" system call*/ diff --git a/arch/i386/kernel/i8259.c b/arch/i386/kernel/i8259.c index e88fa9022..fff171c24 100644 --- a/arch/i386/kernel/i8259.c +++ b/arch/i386/kernel/i8259.c @@ -390,10 +390,11 @@ void __init init_8259A(int auto_eoi) static void math_error_irq(int cpl, void *dev_id, struct pt_regs *regs) { + extern void math_error(void *); outb(0,0xF0); if (ignore_irq13 || !boot_cpu_data.hard_math) return; - math_error(); + math_error((void *)regs->eip); } static struct irqaction irq13 = { math_error_irq, 0, 0, "fpu", NULL, NULL }; diff --git a/arch/i386/kernel/msr.c b/arch/i386/kernel/msr.c new file mode 100644 index 000000000..2ab1ecf33 --- /dev/null +++ b/arch/i386/kernel/msr.c @@ -0,0 +1,274 @@ +#ident "$Id$" +/* ----------------------------------------------------------------------- * + * + * Copyright 2000 H. Peter Anvin - All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139, + * USA; either version 2 of the License, or (at your option) any later + * version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * msr.c + * + * x86 MSR access device + * + * This device is accessed by lseek() to the appropriate register number + * and then read/write in chunks of 8 bytes. A larger size means multiple + * reads or writes of the same register. + * + * This driver uses /dev/cpu/%d/msr where %d is the minor number, and on + * an SMP box will direct the access to CPU %d. + */ + +#include <linux/module.h> +#include <linux/config.h> + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/fcntl.h> +#include <linux/init.h> +#include <linux/poll.h> +#include <linux/smp.h> +#include <linux/major.h> + +#include <asm/processor.h> +#include <asm/msr.h> +#include <asm/uaccess.h> +#include <asm/system.h> + +extern inline int wrmsr_eio(u32 reg, u32 eax, u32 edx) +{ + int err = 0; + + asm volatile( + "1: wrmsr\n" + "2:\n" + ".section .fixup,\"ax\"\n" + "3: movl %4,%0\n" + " jmp 1b\n" + ".previous\n" + ".section __ex_table,\"a\"\n" + " .align 4\n" + " .long 1b,3b\n" + ".previous" + : "+r" (err) + : "a" (eax), "d" (edx), "c" (reg), "i" (-EIO)); + + return err; +} + +extern inline int rdmsr_eio(u32 reg, u32 *eax, u32 *edx) +{ + int err = 0; + + asm volatile( + "1: rdmsr\n" + "2:\n" + ".section .fixup,\"ax\"\n" + "3: movl %4,%0\n" + " jmp 1b\n" + ".previous\n" + ".section __ex_table,\"a\"\n" + " .align 4\n" + " .long 1b,3b\n" + ".previous" + : "+r" (err), "=a" (*eax), "=d" (*eax) + : "c" (reg), "i" (-EIO)); + + return err; +} + +#ifdef CONFIG_SMP + +struct msr_command { + int cpu; + int err; + u32 reg; + u32 data[2]; +}; + +static void msr_smp_wrmsr(void *cmd_block) +{ + struct msr_command *cmd = (struct msr_command *) cmd_block; + + if ( cmd->cpu == smp_processor_id() ) + cmd->err = wrmsr_eio(cmd->reg, &cmd->data[0], &cmd->data[1]); +} + +static void msr_smp_rdmsr(void *cmd_block) +{ + struct msr_command *cmd = (struct msr_command *) cmd_block; + + if ( cmd->cpu == smp_processor_id() ) + cmd->err = rdmsr_eio(cmd->reg, &cmd->data[0], &cmd->data[1]); +} + +extern inline int do_wrmsr(int cpu, u32 reg, u32 eax, u32 edx) +{ + struct msr_command cmd; + + if ( cpu == smp_processor_id() ) { + return wrmsr_eio(reg, eax, edx); + } else { + cmd.cpu = cpu; + cmd.reg = reg; + cmd.data[0] = eax; + cmd.data[1] = edx; + + smp_call_function(msr_smp_wrmsr, (void *)cmd, 1, 1); + return cmd.err; + } +} + +extern inline int do_rdmsr(int cpu, u32 reg, u32 *eax, u32 *edx) +{ + struct msr_command cmd; + + if ( cpu == smp_processor_id() ) { + return rdmsr_eio(reg, eax, edx); + } else { + cmd.cpu = cpu; + cmd.reg = reg; + + smp_call_function(msr_smp_rdmsr, (void *)cmd, 1, 1); + + *eax = cmd.data[0]; + *edx = cmd.data[1]; + + return cmd.err; + } +} + +#else /* ! CONFIG_SMP */ + +extern inline int do_wrmsr(int cpu, u32 reg, u32 eax, u32 edx) +{ + return wrmsr_eio(reg, eax, edx); +} + +extern inline int do_rdmsr(int cpu, u32 reg, u32 *eax, u32 *edx) +{ + return rdmsr_eio(reg, eax, edx); +} + +#endif /* ! CONFIG_SMP */ + +static loff_t msr_seek(struct file *file, loff_t offset, int orig) +{ + switch (orig) { + case 0: + file->f_pos = offset; + return file->f_pos; + case 1: + file->f_pos += offset; + return file->f_pos; + default: + return -EINVAL; /* SEEK_END not supported */ + } +} + +static ssize_t msr_read(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + u32 *tmp = (u32 *)buf; + u32 data[2]; + size_t rv; + u32 reg = *ppos; + int cpu = MINOR(file->f_dentry->d_inode->i_rdev); + int err; + + if ( count % 8 ) + return -EINVAL; /* Invalid chunk size */ + + for ( rv = 0 ; count ; count -= 8 ) { + err = do_rdmsr(cpu, reg, &data[0], &data[1]); + if ( err ) + return err; + if ( copy_to_user(tmp,&data,8) ) + return -EFAULT; + tmp += 2; + } + + return ((char *)tmp) - buf; +} + +static ssize_t msr_write(struct file * file, const char * buf, + size_t count, loff_t *ppos) +{ + const u32 *tmp = (const u32 *)buf; + u32 data[2]; + size_t rv; + u32 reg = *ppos; + int cpu = MINOR(file->f_dentry->d_inode->i_rdev); + int err; + + if ( count % 8 ) + return -EINVAL; /* Invalid chunk size */ + + for ( rv = 0 ; count ; count -= 8 ) { + if ( copy_from_user(&data,tmp,8) ) + return -EFAULT; + err = do_wrmsr(cpu, reg, data[0], data[1]); + if ( err ) + return err; + tmp += 2; + } + + return ((char *)tmp) - buf; +} + +static int msr_open(struct inode *inode, struct file *file) +{ + int cpu = MINOR(file->f_dentry->d_inode->i_rdev); + + if ( !(cpu_online_map & (1UL << cpu)) || + !((cpu_data)[cpu].x86_capability & X86_FEATURE_MSR) ) + return -ENXIO; /* No such CPU */ + + MOD_INC_USE_COUNT; + return 0; +} + +static int msr_release(struct inode *inode, struct file *file) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * File operations we support + */ +static struct file_operations msr_fops = { + llseek: msr_seek, + read: msr_read, + write: msr_write, + open: msr_open, + release: msr_release, +}; + +int __init msr_init(void) +{ + if (register_chrdev(MSR_MAJOR, "cpu/msr", &msr_fops)) { + printk(KERN_ERR "msr: unable to get major %d for msr\n", + MSR_MAJOR); + return -EBUSY; + } + + return 0; +} + +void __exit msr_exit(void) +{ +} + +module_init(msr_init); +module_exit(msr_exit) + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("H. Peter Anvin <hpa@zytor.com>"); +MODULE_DESCRIPTION("x86 generic MSR driver"); diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c index 96eedb519..dddd807c8 100644 --- a/arch/i386/kernel/process.c +++ b/arch/i386/kernel/process.c @@ -2,6 +2,8 @@ * linux/arch/i386/kernel/process.c * * Copyright (C) 1995 Linus Torvalds + * Pentium III code by Ingo Molnar with changes and support for + * OS exception support by Goutham Rao */ /* @@ -469,6 +471,94 @@ void copy_segments(struct task_struct *p, struct mm_struct *new_mm) return; } +#ifdef CONFIG_X86_FX + +int i387_hard_to_user ( struct _fpstate * user, + struct i387_hard_struct * hard) +{ + int i, err = 0; + short *tmp, *tmp2; + long *ltmp1, *ltmp2; + + err |= put_user(hard->cwd, &user->cw); + err |= put_user(hard->swd, &user->sw); + err |= put_user(fputag_KNIto387(hard->twd), &user->tag); + err |= put_user(hard->fip, &user->ipoff); + err |= put_user(hard->fcs, &user->cssel); + err |= put_user(hard->fdp, &user->dataoff); + err |= put_user(hard->fds, &user->datasel); + err |= put_user(hard->mxcsr, &user->mxcsr); + + tmp = (short *)&user->_st; + tmp2 = (short *)&hard->st_space; + + /* + * Transform the two layouts: + * (we do not mix 32-bit access with 16-bit access because + * thats suboptimal on PPros) + */ + for (i = 0; i < 8; i++) + { + err |= put_user(*tmp2, tmp); tmp++; tmp2++; + err |= put_user(*tmp2, tmp); tmp++; tmp2++; + err |= put_user(*tmp2, tmp); tmp++; tmp2++; + err |= put_user(*tmp2, tmp); tmp++; tmp2++; + err |= put_user(*tmp2, tmp); tmp++; tmp2 += 3; + } + + ltmp1 = (unsigned long *)&(user->_xmm[0]); + ltmp2 = (unsigned long *)&(hard->xmm_space[0]); + for(i = 0; i < 88; i++) + { + err |= put_user(*ltmp2, ltmp1); + ltmp1++; ltmp2++; + } + + return err; +} + +int i387_user_to_hard (struct i387_hard_struct * hard, + struct _fpstate * user) +{ + int i, err = 0; + short *tmp, *tmp2; + long *ltmp1, *ltmp2; + + err |= get_user(hard->cwd, &user->cw); + err |= get_user(hard->swd, &user->sw); + err |= get_user(hard->twd, &user->tag); + hard->twd = fputag_387toKNI(hard->twd); + err |= get_user(hard->fip, &user->ipoff); + err |= get_user(hard->fcs, &user->cssel); + err |= get_user(hard->fdp, &user->dataoff); + err |= get_user(hard->fds, &user->datasel); + err |= get_user(hard->mxcsr, &user->mxcsr); + + tmp2 = (short *)&hard->st_space; + tmp = (short *)&user->_st; + + for (i = 0; i < 8; i++) + { + err |= get_user(*tmp2, tmp); tmp++; tmp2++; + err |= get_user(*tmp2, tmp); tmp++; tmp2++; + err |= get_user(*tmp2, tmp); tmp++; tmp2++; + err |= get_user(*tmp2, tmp); tmp++; tmp2++; + err |= get_user(*tmp2, tmp); tmp++; tmp2 += 3; + } + + ltmp1 = (unsigned long *)(&user->_xmm[0]); + ltmp2 = (unsigned long *)(&hard->xmm_space[0]); + for(i = 0; i < (88); i++) + { + err |= get_user(*ltmp2, ltmp1); + ltmp2++; ltmp1++; + } + + return err; +} + +#endif + /* * Save a segment. */ diff --git a/arch/i386/kernel/ptrace.c b/arch/i386/kernel/ptrace.c index de9656150..01edbc37a 100644 --- a/arch/i386/kernel/ptrace.c +++ b/arch/i386/kernel/ptrace.c @@ -1,5 +1,6 @@ /* ptrace.c */ /* By Ross Biro 1/23/92 */ +/* FXSAVE/FXRSTOR support by Ingo Molnar and modifications by Goutham Rao */ /* edited by Linus Torvalds */ #include <linux/config.h> /* for CONFIG_MATH_EMULATION */ @@ -398,14 +399,14 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) ret = 0; if ( !child->used_math ) { /* Simulate an empty FPU. */ - child->thread.i387.hard.cwd = 0xffff037f; - child->thread.i387.hard.swd = 0xffff0000; - child->thread.i387.hard.twd = 0xffffffff; - } + i387_set_cwd(child->thread.i387.hard, 0x037f); + i387_set_swd(child->thread.i387.hard, 0x0000); + i387_set_twd(child->thread.i387.hard, 0xffff); + } #ifdef CONFIG_MATH_EMULATION if ( boot_cpu_data.hard_math ) { #endif - __copy_to_user((void *)data, &child->thread.i387.hard, sizeof(struct user_i387_struct)); + i387_hard_to_user((struct _fpstate *)data, &child->thread.i387.hard); #ifdef CONFIG_MATH_EMULATION } else { save_i387_soft(&child->thread.i387.soft, (struct _fpstate *)data); @@ -423,7 +424,7 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) #ifdef CONFIG_MATH_EMULATION if ( boot_cpu_data.hard_math ) { #endif - __copy_from_user(&child->thread.i387.hard, (void *)data, sizeof(struct user_i387_struct)); + i387_user_to_hard(&child->thread.i387.hard,(struct _fpstate *)data); #ifdef CONFIG_MATH_EMULATION } else { restore_i387_soft(&child->thread.i387.soft, (struct _fpstate *)data); diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index 358d9f917..3ea101d69 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -39,7 +39,8 @@ * Detection for Celeron coppermine, identify_cpu() overhauled, * and a few other clean ups. * Dave Jones <dave@powertweak.com>, April 2000 - * + * Pentium-III code by Ingo Molnar and modifications by Goutham Rao + * */ /* @@ -800,14 +801,30 @@ void __init setup_arch(char **cmdline_p) conswitchp = &dummy_con; #endif #endif +#ifdef CONFIG_X86_FX + if (boot_cpu_data.x86_capability & X86_FEATURE_FXSR) + { + printk("Enabling extended fast FPU save and restore ... "); + set_in_cr4(X86_CR4_OSFXSR); + printk("done.\n"); + } + if (boot_cpu_data.x86_capability & X86_FEATURE_XMM) + { + printk("Enabling KNI unmasked exception support ... "); + set_in_cr4(X86_CR4_OSXMMEXCPT); + printk("done.\n"); + } +#endif } static int __init get_model_name(struct cpuinfo_x86 *c) { unsigned int n, dummy, *v; - /* Actually we must have cpuid or we could never have - * figured out that this was AMD/Cyrix from the vendor info :-). + /* + * Actually we must have cpuid or we could never have + * figured out that this was AMD/Cyrix/Transmeta + * from the vendor info :-). */ cpuid(0x80000000, &n, &dummy, &dummy, &dummy); @@ -1196,6 +1213,86 @@ static void __init centaur_model(struct cpuinfo_x86 *c) sprintf( c->x86_model_id, "WinChip %s", name ); } +static void __init transmeta_model(struct cpuinfo_x86 *c) +{ + unsigned int cap_mask, uk, max, dummy, n, ecx, edx; + unsigned int cms_rev1, cms_rev2; + unsigned int cpu_rev, cpu_freq, cpu_flags; + char cpu_info[65]; + + get_model_name(c); /* Same as AMD/Cyrix */ + + /* Print CMS and CPU revision */ + cpuid(0x80860000, &max, &dummy, &dummy, &dummy); + if ( max >= 0x80860001 ) { + cpuid(0x80860001, &dummy, &cpu_rev, &cpu_freq, &cpu_flags); + printk("CPU: Processor revision %u.%u.%u.%u, %u MHz%s%s\n", + (cpu_rev >> 24) & 0xff, + (cpu_rev >> 16) & 0xff, + (cpu_rev >> 8) & 0xff, + cpu_rev & 0xff, + cpu_freq, + (cpu_flags & 1) ? " [recovery]" : "", + (cpu_flags & 2) ? " [longrun]" : ""); + } + if ( max >= 0x80860002 ) { + cpuid(0x80860002, &dummy, &cms_rev1, &cms_rev2, &dummy); + printk("CPU: Code Morphing Software revision %u.%u.%u-%u-%u\n", + (cms_rev1 >> 24) & 0xff, + (cms_rev1 >> 16) & 0xff, + (cms_rev1 >> 8) & 0xff, + cms_rev1 & 0xff, + cms_rev2); + } + if ( max >= 0x80860006 ) { + cpuid(0x80860003, + (void *)&cpu_info[0], + (void *)&cpu_info[4], + (void *)&cpu_info[8], + (void *)&cpu_info[12]); + cpuid(0x80860004, + (void *)&cpu_info[16], + (void *)&cpu_info[20], + (void *)&cpu_info[24], + (void *)&cpu_info[28]); + cpuid(0x80860005, + (void *)&cpu_info[32], + (void *)&cpu_info[36], + (void *)&cpu_info[40], + (void *)&cpu_info[44]); + cpuid(0x80860006, + (void *)&cpu_info[48], + (void *)&cpu_info[52], + (void *)&cpu_info[56], + (void *)&cpu_info[60]); + cpu_info[64] = '\0'; + printk("CPU: %s\n", cpu_info); + } + + /* Unhide possibly hidden flags */ + rdmsr(0x80860004, cap_mask, uk); + wrmsr(0x80860004, ~0, uk); + cpuid(0x00000001, &dummy, &dummy, &dummy, &c->x86_capability); + wrmsr(0x80860004, cap_mask, uk); + + + /* L1/L2 cache */ + cpuid(0x80000000, &n, &dummy, &dummy, &dummy); + + if (n >= 0x80000005) { + cpuid(0x80000005, &dummy, &dummy, &ecx, &edx); + printk("CPU: L1 I Cache: %dK L1 D Cache: %dK\n", + ecx>>24, edx>>24); + c->x86_cache_size=(ecx>>24)+(edx>>24); + } + if (n >= 0x80000006) { + cpuid(0x80000006, &dummy, &dummy, &ecx, &edx); + printk("CPU: L2 Cache: %dK\n", ecx>>16); + c->x86_cache_size=(ecx>>16); + } +} + + void __init get_cpu_vendor(struct cpuinfo_x86 *c) { char *v = c->x86_vendor_id; @@ -1214,6 +1311,8 @@ void __init get_cpu_vendor(struct cpuinfo_x86 *c) c->x86_vendor = X86_VENDOR_NEXGEN; else if (!strcmp(v, "RiseRiseRise")) c->x86_vendor = X86_VENDOR_RISE; + else if (!strcmp(v, "GenuineTMx86")) + c->x86_vendor = X86_VENDOR_TRANSMETA; else c->x86_vendor = X86_VENDOR_UNKNOWN; } @@ -1263,6 +1362,9 @@ static struct cpu_model_info cpu_models[] __initdata = { { X86_VENDOR_RISE, 5, { "mP6", "mP6", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }}, + { X86_VENDOR_TRANSMETA, 5, + { NULL, NULL, NULL, "Crusoe", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }}, }; void __init identify_cpu(struct cpuinfo_x86 *c) @@ -1275,6 +1377,16 @@ void __init identify_cpu(struct cpuinfo_x86 *c) get_cpu_vendor(c); + /* It should be possible for the user to override this. */ + if(c->x86_capability&(1<<18)) { + /* Disable processor serial number */ + unsigned long lo,hi; + rdmsr(0x119,lo,hi); + lo |= 0x200000; + wrmsr(0x119,lo,hi); + printk(KERN_INFO "CPU serial number disabled.\n"); + } + switch (c->x86_vendor) { case X86_VENDOR_UNKNOWN: @@ -1296,16 +1408,6 @@ void __init identify_cpu(struct cpuinfo_x86 *c) return; case X86_VENDOR_INTEL: - if(c->x86_capability&(1<<18)) { - /* Disable processor serial number on Intel Pentium III - from code by Phil Karn */ - unsigned long lo,hi; - rdmsr(0x119,lo,hi); - lo |= 0x200000; - wrmsr(0x119,lo,hi); - printk(KERN_INFO "Pentium-III serial number disabled.\n"); - } - if (c->cpuid_level > 1) { /* supports eax=2 call */ int edx, dummy; @@ -1374,6 +1476,10 @@ void __init identify_cpu(struct cpuinfo_x86 *c) goto name_decoded; break; + + case X86_VENDOR_TRANSMETA: + transmeta_model(c); + return; } @@ -1412,7 +1518,7 @@ void __init dodgy_tsc(void) static char *cpu_vendor_names[] __initdata = { - "Intel", "Cyrix", "AMD", "UMC", "NexGen", "Centaur", "Rise" }; + "Intel", "Cyrix", "AMD", "UMC", "NexGen", "Centaur", "Rise", "Transmeta" }; void __init print_cpu_info(struct cpuinfo_x86 *c) @@ -1424,7 +1530,7 @@ void __init print_cpu_info(struct cpuinfo_x86 *c) else if (c->cpuid_level >= 0) vendor = c->x86_vendor_id; - if (vendor) + if (vendor && strncmp(c->x86_model_id, vendor, strlen(vendor))) printk("%s ", vendor); if (!c->x86_model_id[0]) @@ -1434,6 +1540,8 @@ void __init print_cpu_info(struct cpuinfo_x86 *c) if (c->x86_mask || c->cpuid_level>=0) printk(" stepping %02x\n", c->x86_mask); + else + printk("\n"); } /* @@ -1513,7 +1621,9 @@ int get_cpuinfo(char * buffer) case X86_VENDOR_INTEL: x86_cap_flags[16] = "pat"; + x86_cap_flags[18] = "pn"; x86_cap_flags[24] = "fxsr"; + x86_cap_flags[25] = "xmm"; break; case X86_VENDOR_CENTAUR: @@ -1522,7 +1632,7 @@ int get_cpuinfo(char * buffer) break; default: - /* Unknown CPU manufacturer. Transmeta ? :-) */ + /* Unknown CPU manufacturer or no special handling needed */ break; } diff --git a/arch/i386/kernel/signal.c b/arch/i386/kernel/signal.c index 4e813fac7..c35f5bae8 100644 --- a/arch/i386/kernel/signal.c +++ b/arch/i386/kernel/signal.c @@ -4,6 +4,8 @@ * Copyright (C) 1991, 1992 Linus Torvalds * * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson + * Pentium III support by Ingo Molnar, modifications and OS Exception support + * by Goutham Rao */ #include <linux/config.h> @@ -30,6 +32,41 @@ asmlinkage int sys_wait4(pid_t pid, unsigned long *stat_addr, int options, unsigned long *ru); asmlinkage int FASTCALL(do_signal(struct pt_regs *regs, sigset_t *oldset)); +int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from) +{ + if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t))) + return -EFAULT; + if (from->si_code < 0) + return __copy_to_user(to, from, sizeof(siginfo_t)); + else { + int err; + + /* If you change siginfo_t structure, please be sure + this code is fixed accordingly. + It should never copy any pad contained in the structure + to avoid security leaks, but must copy the generic + 3 ints plus the relevant union member. */ + err = __put_user(from->si_signo, &to->si_signo); + err |= __put_user(from->si_errno, &to->si_errno); + err |= __put_user((short)from->si_code, &to->si_code); + /* First 32bits of unions are always present. */ + err |= __put_user(from->si_pid, &to->si_pid); + switch (from->si_code >> 16) { + case __SI_FAULT >> 16: + break; + case __SI_CHLD >> 16: + err |= __put_user(from->si_utime, &to->si_utime); + err |= __put_user(from->si_stime, &to->si_stime); + err |= __put_user(from->si_status, &to->si_status); + default: + err |= __put_user(from->si_uid, &to->si_uid); + break; + /* case __SI_RT: This is not generated by the kernel as of now. */ + } + return err; + } +} + /* * Atomically swap in the new signal mask, and wait for a signal. */ @@ -155,7 +192,7 @@ static inline int restore_i387_hard(struct _fpstate *buf) { struct task_struct *tsk = current; clear_fpu(tsk); - return __copy_from_user(&tsk->thread.i387.hard, buf, sizeof(*buf)); + return i387_user_to_hard(&tsk->thread.i387.hard, buf); } static inline int restore_i387(struct _fpstate *buf) @@ -309,7 +346,7 @@ static inline int save_i387_hard(struct _fpstate * buf) unlazy_fpu(tsk); tsk->thread.i387.hard.status = tsk->thread.i387.hard.swd; - if (__copy_to_user(buf, &tsk->thread.i387.hard, sizeof(*buf))) + if (i387_hard_to_user(buf, &tsk->thread.i387.hard)) return -1; return 1; } @@ -491,7 +528,7 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, &frame->sig); err |= __put_user(&frame->info, &frame->pinfo); err |= __put_user(&frame->uc, &frame->puc); - err |= __copy_to_user(&frame->info, info, sizeof(*info)); + err |= copy_siginfo_to_user(&frame->info, info); if (err) goto give_sigsegv; diff --git a/arch/i386/kernel/smp.c b/arch/i386/kernel/smp.c index 94ba5c49f..e08418fe0 100644 --- a/arch/i386/kernel/smp.c +++ b/arch/i386/kernel/smp.c @@ -133,6 +133,11 @@ static inline void __send_IPI_shortcut(unsigned int shortcut, int vector) unsigned int cfg; /* + * Wait for idle. + */ + apic_wait_icr_idle(); + + /* * No need to touch the target chip field */ cfg = __prepare_ICR(shortcut, vector); @@ -173,6 +178,11 @@ static inline void send_IPI_mask(int mask, int vector) __cli(); /* + * Wait for idle. + */ + apic_wait_icr_idle(); + + /* * prepare target chip field */ cfg = __prepare_ICR2(mask); diff --git a/arch/i386/kernel/smpboot.c b/arch/i386/kernel/smpboot.c index ae84ff2b5..e0ae38b28 100644 --- a/arch/i386/kernel/smpboot.c +++ b/arch/i386/kernel/smpboot.c @@ -502,6 +502,11 @@ static inline void inquire_remote_apic(int apicid) for (i = 0; i < sizeof(regs) / sizeof(*regs); i++) { printk("... APIC #%d %s: ", apicid, names[i]); + /* + * Wait for idle. + */ + apic_wait_icr_idle(); + apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(apicid)); apic_write_around(APIC_ICR, APIC_DM_REMRD | regs[i]); diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index 7fb5ebc61..0df54449f 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c @@ -2,6 +2,7 @@ * linux/arch/i386/traps.c * * Copyright (C) 1991, 1992 Linus Torvalds + * FXSAVE/FXRSTOR support by Ingo Molnar, OS exception support by Goutham Rao */ /* @@ -82,6 +83,20 @@ asmlinkage void do_##name(struct pt_regs * regs, long error_code) \ force_sig(signr, tsk); \ } +#define DO_ERROR_INFO(trapnr, signr, str, name, tsk, sicode, siaddr) \ +asmlinkage void do_##name(struct pt_regs * regs, long error_code) \ +{ \ + siginfo_t info; \ + tsk->thread.error_code = error_code; \ + tsk->thread.trap_no = trapnr; \ + die_if_no_fixup(str,regs,error_code); \ + info.si_signo = signr; \ + info.si_errno = 0; \ + info.si_code = sicode; \ + info.si_addr = (void *)siaddr; \ + force_sig_info(signr, &info, tsk); \ +} + #define DO_VM86_ERROR(trapnr, signr, str, name, tsk) \ asmlinkage void do_##name(struct pt_regs * regs, long error_code) \ { \ @@ -99,6 +114,28 @@ out: \ unlock_kernel(); \ } +#define DO_VM86_ERROR_INFO(trapnr, signr, str, name, tsk, sicode, siaddr) \ +asmlinkage void do_##name(struct pt_regs * regs, long error_code) \ +{ \ + siginfo_t info; \ + lock_kernel(); \ + if (regs->eflags & VM_MASK) { \ + if (!handle_vm86_trap((struct kernel_vm86_regs *) regs, error_code, trapnr)) \ + goto out; \ + /* else fall through */ \ + } \ + tsk->thread.error_code = error_code; \ + tsk->thread.trap_no = trapnr; \ + info.si_signo = signr; \ + info.si_errno = 0; \ + info.si_code = sicode; \ + info.si_addr = (void *)siaddr; \ + force_sig_info(signr, &info, tsk); \ + die_if_kernel(str,regs,error_code); \ +out: \ + unlock_kernel(); \ +} + void page_exception(void); asmlinkage void divide_error(void); @@ -120,6 +157,7 @@ asmlinkage void coprocessor_error(void); asmlinkage void reserved(void); asmlinkage void alignment_check(void); asmlinkage void spurious_interrupt_bug(void); +asmlinkage void xmm_fault(void); int kstack_depth_to_print = 24; @@ -260,34 +298,29 @@ static void die_if_no_fixup(const char * str, struct pt_regs * regs, long err) } } -DO_VM86_ERROR( 0, SIGFPE, "divide error", divide_error, current) +static inline unsigned long get_cr2(void) +{ + unsigned long address; + + /* get the address */ + __asm__("movl %%cr2,%0":"=r" (address)); + return address; +} + +DO_VM86_ERROR_INFO( 0, SIGFPE, "divide error", divide_error, current, FPE_INTDIV, regs->eip) DO_VM86_ERROR( 3, SIGTRAP, "int3", int3, current) DO_VM86_ERROR( 4, SIGSEGV, "overflow", overflow, current) DO_VM86_ERROR( 5, SIGSEGV, "bounds", bounds, current) -DO_ERROR( 6, SIGILL, "invalid operand", invalid_op, current) +DO_ERROR_INFO( 6, SIGILL, "invalid operand", invalid_op, current, ILL_ILLOPN, regs->eip) DO_VM86_ERROR( 7, SIGSEGV, "device not available", device_not_available, current) DO_ERROR( 8, SIGSEGV, "double fault", double_fault, current) DO_ERROR( 9, SIGFPE, "coprocessor segment overrun", coprocessor_segment_overrun, current) DO_ERROR(10, SIGSEGV, "invalid TSS", invalid_TSS, current) DO_ERROR(11, SIGBUS, "segment not present", segment_not_present, current) DO_ERROR(12, SIGBUS, "stack segment", stack_segment, current) -DO_ERROR(17, SIGSEGV, "alignment check", alignment_check, current) +DO_ERROR_INFO(17, SIGBUS, "alignment check", alignment_check, current, BUS_ADRALN, get_cr2()) DO_ERROR(18, SIGSEGV, "reserved", reserved, current) -/* I don't have documents for this but it does seem to cover the cache - flush from user space exception some people get. */ -DO_ERROR(19, SIGSEGV, "cache flush denied", cache_flush_denied, current) - -asmlinkage void cache_flush_denied(struct pt_regs * regs, long error_code) -{ - if (regs->eflags & VM_MASK) { - handle_vm86_fault((struct kernel_vm86_regs *) regs, error_code); - return; - } - die_if_kernel("cache flush denied",regs,error_code); - current->thread.error_code = error_code; - current->thread.trap_no = 19; - force_sig(SIGSEGV, current); -} +DO_VM86_ERROR(19, SIGFPE, "XMM fault", xmm_fault, current) asmlinkage void do_general_protection(struct pt_regs * regs, long error_code) { @@ -485,6 +518,7 @@ asmlinkage void do_debug(struct pt_regs * regs, long error_code) { unsigned int condition; struct task_struct *tsk = current; + siginfo_t info; __asm__ __volatile__("movl %%db6,%0" : "=r" (condition)); @@ -519,7 +553,11 @@ asmlinkage void do_debug(struct pt_regs * regs, long error_code) /* Ok, finally something we can handle */ tsk->thread.trap_no = 1; tsk->thread.error_code = error_code; - force_sig(SIGTRAP, tsk); + info.si_signo = SIGTRAP; + info.si_errno = 0; + info.si_code = TRAP_BRKPT; + info.si_addr = (void *)regs->eip; + force_sig_info(SIGTRAP, &info, tsk); return; debug_vm86: @@ -544,9 +582,10 @@ clear_TF: * the correct behaviour even in the presence of the asynchronous * IRQ13 behaviour */ -void math_error(void) +void math_error(void *eip) { struct task_struct * task; + siginfo_t info; /* * Save the info for the exception handler @@ -556,13 +595,52 @@ void math_error(void) save_fpu(task); task->thread.trap_no = 16; task->thread.error_code = 0; - force_sig(SIGFPE, task); + info.si_signo = SIGFPE; + info.si_errno = 0; + info.si_code = __SI_FAULT; + info.si_addr = eip; + /* + * (~cwd & swd) will mask out exceptions that are not set to unmasked + * status. 0x3f is the exception bits in these regs, 0x200 is the + * C1 reg you need in case of a stack fault, 0x040 is the stack + * fault bit. We should only be taking one exception at a time, + * so if this combination doesn't produce any single exception, + * then we have a bad program that isn't syncronizing its FPU usage + * and it will suffer the consequences since we won't be able to + * fully reproduce the context of the exception + */ + switch(((~task->thread.i387.hard.cwd) & + task->thread.i387.hard.swd & 0x3f) | + (task->thread.i387.hard.swd & 0x240)) { + case 0x000: + default: + break; + case 0x001: /* Invalid Op */ + case 0x040: /* Stack Fault */ + case 0x240: /* Stack Fault | Direction */ + info.si_code = FPE_FLTINV; + break; + case 0x002: /* Denormalize */ + case 0x010: /* Underflow */ + info.si_code = FPE_FLTUND; + break; + case 0x004: /* Zero Divide */ + info.si_code = FPE_FLTDIV; + break; + case 0x008: /* Overflow */ + info.si_code = FPE_FLTOVF; + break; + case 0x020: /* Precision */ + info.si_code = FPE_FLTRES; + break; + } + force_sig_info(SIGFPE, &info, task); } asmlinkage void do_coprocessor_error(struct pt_regs * regs, long error_code) { ignore_irq13 = 1; - math_error(); + math_error((void *)regs->eip); } asmlinkage void do_spurious_interrupt_bug(struct pt_regs * regs, @@ -586,7 +664,7 @@ asmlinkage void math_state_restore(struct pt_regs regs) __asm__ __volatile__("clts"); /* Allow maths ops (or we recurse) */ if(current->used_math) - __asm__("frstor %0": :"m" (current->thread.i387)); + i387_restore_hard(current->thread.i387); else { /* @@ -829,6 +907,8 @@ void __init trap_init(void) set_trap_gate(15,&spurious_interrupt_bug); set_trap_gate(16,&coprocessor_error); set_trap_gate(17,&alignment_check); + set_trap_gate(19,&xmm_fault); + set_system_gate(SYSCALL_VECTOR,&system_call); /* diff --git a/arch/i386/mm/fault.c b/arch/i386/mm/fault.c index 697645988..b88c4a422 100644 --- a/arch/i386/mm/fault.c +++ b/arch/i386/mm/fault.c @@ -124,13 +124,14 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code) unsigned long page; unsigned long fixup; int write; - int si_code = SEGV_MAPERR; + siginfo_t info; /* get the address */ __asm__("movl %%cr2,%0":"=r" (address)); tsk = current; mm = tsk->mm; + info.si_code = SEGV_MAPERR; /* * If we're in an interrupt or have no user @@ -165,9 +166,8 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code) * we can handle it.. */ good_area: + info.si_code = SEGV_ACCERR; write = 0; - si_code = SEGV_ACCERR; - switch (error_code & 3) { default: /* 3: write, present */ #ifdef TEST_VERIFY_AREA @@ -225,14 +225,14 @@ bad_area: /* User mode accesses just cause a SIGSEGV */ if (error_code & 4) { - struct siginfo si; tsk->thread.cr2 = address; tsk->thread.error_code = error_code; tsk->thread.trap_no = 14; - si.si_signo = SIGSEGV; - si.si_code = si_code; - si.si_addr = (void*) address; - force_sig_info(SIGSEGV, &si, tsk); + info.si_signo = SIGSEGV; + info.si_errno = 0; + /* info.si_code has been set above */ + info.si_addr = (void *)address; + force_sig_info(SIGSEGV, &info, tsk); return; } @@ -309,7 +309,11 @@ do_sigbus: tsk->thread.cr2 = address; tsk->thread.error_code = error_code; tsk->thread.trap_no = 14; - force_sig(SIGBUS, tsk); + info.si_code = SIGBUS; + info.si_errno = 0; + info.si_code = BUS_ADRERR; + info.si_addr = (void *)address; + force_sig_info(SIGBUS, &info, tsk); /* Kernel mode? Handle exceptions or die */ if (!(error_code & 4)) diff --git a/arch/ia64/ia32/ia32_signal.c b/arch/ia64/ia32/ia32_signal.c index 83e6c9e61..e85af6ced 100644 --- a/arch/ia64/ia32/ia32_signal.c +++ b/arch/ia64/ia32/ia32_signal.c @@ -48,13 +48,58 @@ struct rt_sigframe_ia32 int sig; int pinfo; int puc; - struct siginfo info; + siginfo_t32 info; struct ucontext_ia32 uc; struct _fpstate_ia32 fpstate; char retcode[8]; }; static int +copy_siginfo_to_user32(siginfo_t32 *to, siginfo_t *from) +{ + int err; + + if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t32))) + return -EFAULT; + + /* If you change siginfo_t structure, please be sure + this code is fixed accordingly. + It should never copy any pad contained in the structure + to avoid security leaks, but must copy the generic + 3 ints plus the relevant union member. + This routine must convert siginfo from 64bit to 32bit as well + at the same time. */ + err = __put_user(from->si_signo, &to->si_signo); + err |= __put_user(from->si_errno, &to->si_errno); + err |= __put_user((short)from->si_code, &to->si_code); + if (from->si_code < 0) + err |= __copy_to_user(&to->_sifields._pad, &from->_sifields._pad, SI_PAD_SIZE); + else { + switch (from->si_code >> 16) { + case __SI_CHLD >> 16: + err |= __put_user(from->si_utime, &to->si_utime); + err |= __put_user(from->si_stime, &to->si_stime); + err |= __put_user(from->si_status, &to->si_status); + default: + err |= __put_user(from->si_pid, &to->si_pid); + err |= __put_user(from->si_uid, &to->si_uid); + break; + case __SI_FAULT >> 16: + err |= __put_user((long)from->si_addr, &to->si_addr); + break; + case __SI_POLL >> 16: + err |= __put_user(from->si_band, &to->si_band); + err |= __put_user(from->si_fd, &to->si_fd); + break; + /* case __SI_RT: This is not generated by the kernel as of now. */ + } + } + return err; +} + + + +static int setup_sigcontext_ia32(struct sigcontext_ia32 *sc, struct _fpstate_ia32 *fpstate, struct pt_regs *regs, unsigned long mask) { @@ -283,7 +328,7 @@ setup_rt_frame_ia32(int sig, struct k_sigaction *ka, siginfo_t *info, &frame->sig); err |= __put_user(&frame->info, &frame->pinfo); err |= __put_user(&frame->uc, &frame->puc); - err |= __copy_to_user(&frame->info, info, sizeof(*info)); + err |= copy_siginfo_to_user32(&frame->info, info); /* Create the ucontext. */ err |= __put_user(0, &frame->uc.uc_flags); diff --git a/arch/ia64/kernel/signal.c b/arch/ia64/kernel/signal.c index a0cca9da7..25197c1d4 100644 --- a/arch/ia64/kernel/signal.c +++ b/arch/ia64/kernel/signal.c @@ -138,6 +138,43 @@ restore_sigcontext (struct sigcontext *sc, struct pt_regs *pt) return err; } +int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from) +{ + if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t))) + return -EFAULT; + if (from->si_code < 0) + return __copy_to_user(to, from, sizeof(siginfo_t)); + else { + int err; + + /* If you change siginfo_t structure, please be sure + this code is fixed accordingly. + It should never copy any pad contained in the structure + to avoid security leaks, but must copy the generic + 3 ints plus the relevant union member. */ + err = __put_user(from->si_signo, &to->si_signo); + err |= __put_user(from->si_errno, &to->si_errno); + err |= __put_user((short)from->si_code, &to->si_code); + switch (from->si_code >> 16) { + case __SI_FAULT >> 16: + case __SI_POLL >> 16: + err |= __put_user(from->si_addr, &to->si_addr); + err |= __put_user(from->si_imm, &to->si_imm); + break; + case __SI_CHLD >> 16: + err |= __put_user(from->si_utime, &to->si_utime); + err |= __put_user(from->si_stime, &to->si_stime); + err |= __put_user(from->si_status, &to->si_status); + default: + err |= __put_user(from->si_uid, &to->si_uid); + err |= __put_user(from->si_pid, &to->si_pid); + break; + /* case __SI_RT: This is not generated by the kernel as of now. */ + } + return err; + } +} + /* * When we get here, ((struct switch_stack *) pt - 1) is a * switch_stack frame that has no defined value. Upon return, we @@ -290,7 +327,7 @@ setup_frame (int sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, st if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) goto give_sigsegv; - err = __copy_to_user(&frame->info, info, sizeof(siginfo_t)); + err = copy_siginfo_to_user(&frame->info, info); err |= __put_user(current->sas_ss_sp, &frame->sc.sc_stack.ss_sp); err |= __put_user(current->sas_ss_size, &frame->sc.sc_stack.ss_size); diff --git a/arch/m68k/kernel/signal.c b/arch/m68k/kernel/signal.c index d795c71ad..d67617bea 100644 --- a/arch/m68k/kernel/signal.c +++ b/arch/m68k/kernel/signal.c @@ -189,6 +189,41 @@ struct rt_sigframe }; +int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from) +{ + if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t))) + return -EFAULT; + if (from->si_code < 0) + return __copy_to_user(to, from, sizeof(siginfo_t)); + else { + int err; + + /* If you change siginfo_t structure, please be sure + this code is fixed accordingly. + It should never copy any pad contained in the structure + to avoid security leaks, but must copy the generic + 3 ints plus the relevant union member. */ + err = __put_user(from->si_signo, &to->si_signo); + err |= __put_user(from->si_errno, &to->si_errno); + err |= __put_user((short)from->si_code, &to->si_code); + /* First 32bits of unions are always present. */ + err |= __put_user(from->si_pid, &to->si_pid); + switch (from->si_code >> 16) { + case __SI_FAULT >> 16: + break; + case __SI_CHLD >> 16: + err |= __put_user(from->si_utime, &to->si_utime); + err |= __put_user(from->si_stime, &to->si_stime); + err |= __put_user(from->si_status, &to->si_status); + default: + err |= __put_user(from->si_uid, &to->si_uid); + break; + /* case __SI_RT: This is not generated by the kernel as of now. */ + } + return err; + } +} + static unsigned char fpu_version = 0; /* version number of fpu, set by setup_frame */ static inline int restore_fpu_state(struct sigcontext *sc) @@ -877,7 +912,7 @@ static void setup_rt_frame (int sig, struct k_sigaction *ka, siginfo_t *info, &frame->sig); err |= __put_user(&frame->info, &frame->pinfo); err |= __put_user(&frame->uc, &frame->puc); - err |= __copy_to_user(&frame->info, info, sizeof(*info)); + err |= copy_siginfo_to_user(&frame->info, info); /* Create the ucontext. */ err |= __put_user(0, &frame->uc.uc_flags); diff --git a/arch/mips/kernel/signal.c b/arch/mips/kernel/signal.c index 81ff3173d..c145b3343 100644 --- a/arch/mips/kernel/signal.c +++ b/arch/mips/kernel/signal.c @@ -39,6 +39,41 @@ extern asmlinkage int restore_fp_context(struct sigcontext *sc); extern asmlinkage void syscall_trace(void); +int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from) +{ + if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t))) + return -EFAULT; + if (from->si_code < 0) + return __copy_to_user(to, from, sizeof(siginfo_t)); + else { + int err; + + /* If you change siginfo_t structure, please be sure + this code is fixed accordingly. + It should never copy any pad contained in the structure + to avoid security leaks, but must copy the generic + 3 ints plus the relevant union member. */ + err = __put_user(from->si_signo, &to->si_signo); + err |= __put_user(from->si_errno, &to->si_errno); + err |= __put_user((short)from->si_code, &to->si_code); + /* First 32bits of unions are always present. */ + err |= __put_user(from->si_pid, &to->si_pid); + switch (from->si_code >> 16) { + case __SI_FAULT >> 16: + break; + case __SI_CHLD >> 16: + err |= __put_user(from->si_utime, &to->si_utime); + err |= __put_user(from->si_stime, &to->si_stime); + err |= __put_user(from->si_status, &to->si_status); + default: + err |= __put_user(from->si_uid, &to->si_uid); + break; + /* case __SI_RT: This is not generated by the kernel as of now. */ + } + return err; + } +} + /* * Atomically swap in the new signal mask, and wait for a signal. */ @@ -442,7 +477,7 @@ setup_rt_frame(struct k_sigaction * ka, struct pt_regs *regs, } /* Create siginfo. */ - err |= __copy_to_user(&frame->rs_info, info, sizeof(*info)); + err |= copy_siginfo_to_user(&frame->rs_info, info); /* Create the ucontext. */ err |= __put_user(0, &frame->rs_uc.uc_flags); diff --git a/arch/mips/mm/fault.c b/arch/mips/mm/fault.c index e2d74fd11..6c9b46b82 100644 --- a/arch/mips/mm/fault.c +++ b/arch/mips/mm/fault.c @@ -1,10 +1,9 @@ -/* $Id: fault.c,v 1.15 2000/02/04 07:40:23 ralf Exp $ - * +/* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 1995, 1996, 1997, 1998 by Ralf Baechle + * Copyright (C) 1995 - 2000 by Ralf Baechle */ #include <linux/signal.h> #include <linux/sched.h> @@ -47,9 +46,10 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long write, struct vm_area_struct * vma; struct task_struct *tsk = current; struct mm_struct *mm = tsk->mm; - int si_code = SEGV_MAPERR; unsigned long fixup; + siginfo_t info; + info.si_code = SEGV_MAPERR; /* * If we're in an interrupt or have no user * context, we must not take the fault.. @@ -75,7 +75,7 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long write, * we can handle it.. */ good_area: - si_code = SEGV_ACCERR; + info.si_code = SEGV_ACCERR; if (write) { if (!(vma->vm_flags & VM_WRITE)) @@ -126,10 +126,11 @@ bad_area: (unsigned long) regs->cp0_epc, (unsigned long) regs->regs[31]); #endif - si.si_signo = SIGSEGV; - si.si_code = si_code; - si.si_addr = (void *) address; - force_sig_info(SIGSEGV, &si, tsk); + info.si_signo = SIGSEGV; + info.si_errno = 0; + /* info.si_code has been set above */ + info.si_addr = (void *) address; + force_sig_info(SIGSEGV, &info, tsk); return; } @@ -177,7 +178,11 @@ do_sigbus: * or user mode. */ tsk->thread.cp0_badvaddr = address; - force_sig(SIGBUS, tsk); + info.si_code = SIGBUS; + info.si_errno = 0; + info.si_code = BUS_ADRERR; + info.si_addr = (void *) address; + force_sig_info(SIGBUS, &info, tsk); /* Kernel mode? Handle exceptions or die */ if (!user_mode(regs)) diff --git a/arch/mips64/kernel/signal.c b/arch/mips64/kernel/signal.c index 9fad47ffe..d45f44b5a 100644 --- a/arch/mips64/kernel/signal.c +++ b/arch/mips64/kernel/signal.c @@ -40,6 +40,41 @@ extern asmlinkage int restore_fp_context(struct sigcontext *sc); extern asmlinkage void syscall_trace(void); +int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from) +{ + if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t))) + return -EFAULT; + if (from->si_code < 0) + return __copy_to_user(to, from, sizeof(siginfo_t)); + else { + int err; + + /* If you change siginfo_t structure, please be sure + this code is fixed accordingly. + It should never copy any pad contained in the structure + to avoid security leaks, but must copy the generic + 3 ints plus the relevant union member. */ + err = __put_user(from->si_signo, &to->si_signo); + err |= __put_user(from->si_errno, &to->si_errno); + err |= __put_user((short)from->si_code, &to->si_code); + switch (from->si_code >> 16) { + case __SI_FAULT >> 16: + err |= __put_user((long)from->si_addr, &to->si_addr); + break; + case __SI_CHLD >> 16: + err |= __put_user(from->si_utime, &to->si_utime); + err |= __put_user(from->si_stime, &to->si_stime); + err |= __put_user(from->si_status, &to->si_status); + default: + err |= __put_user(from->si_pid, &to->si_pid); + err |= __put_user(from->si_uid, &to->si_uid); + break; + /* case __SI_RT: This is not generated by the kernel as of now. */ + } + return err; + } +} + static inline int store_fp_context(struct sigcontext *sc) { unsigned int fcr0; @@ -468,7 +503,7 @@ setup_rt_frame(struct k_sigaction * ka, struct pt_regs *regs, } /* Create siginfo. */ - err |= __copy_to_user(&frame->rs_info, info, sizeof(*info)); + err |= copy_siginfo_to_user(&frame->rs_info, info); /* Create the ucontext. */ err |= __put_user(0, &frame->rs_uc.uc_flags); diff --git a/arch/mips64/mm/fault.c b/arch/mips64/mm/fault.c index e31c82568..01ed01f79 100644 --- a/arch/mips64/mm/fault.c +++ b/arch/mips64/mm/fault.c @@ -1,11 +1,10 @@ -/* $Id: fault.c,v 1.7 2000/03/13 22:43:25 kanoj Exp $ - * +/* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 1995, 1996, 1997, 1998, 1999 by Ralf Baechle - * Copyright (C) 1999 by Silicon Graphics + * Copyright (C) 1995 - 2000 by Ralf Baechle + * Copyright (C) 1999, 2000 by Silicon Graphics, Inc. */ #include <linux/signal.h> #include <linux/sched.h> @@ -68,9 +67,10 @@ do_page_fault(struct pt_regs *regs, unsigned long write, unsigned long address) struct vm_area_struct * vma; struct task_struct *tsk = current; struct mm_struct *mm = tsk->mm; - int si_code = SEGV_MAPERR; unsigned long fixup; + siginfo_t info; + info.si_code = SEGV_MAPERR; /* * If we're in an interrupt or have no user * context, we must not take the fault.. @@ -96,7 +96,7 @@ do_page_fault(struct pt_regs *regs, unsigned long write, unsigned long address) * we can handle it.. */ good_area: - si_code = SEGV_ACCERR; + info.si_code = SEGV_ACCERR; if (write) { if (!(vma->vm_flags & VM_WRITE)) @@ -147,10 +147,11 @@ bad_area: (unsigned long) regs->cp0_epc, (unsigned long) regs->regs[31]); #endif - si.si_signo = SIGSEGV; - si.si_code = si_code; - si.si_addr = (void *) address; - force_sig_info(SIGSEGV, &si, tsk); + info.si_signo = SIGSEGV; + info.si_errno = 0; + /* info.si_code has been set above */ + info.si_addr = (void *) address; + force_sig_info(SIGSEGV, &info, tsk); return; } @@ -198,7 +199,11 @@ do_sigbus: * or user mode. */ tsk->thread.cp0_badvaddr = address; - force_sig(SIGBUS, tsk); + info.si_code = SIGBUS; + info.si_errno = 0; + info.si_code = BUS_ADRERR; + info.si_addr = (void *) address; + force_sig_info(SIGBUS, &info, tsk); /* Kernel mode? Handle exceptions or die */ if (!user_mode(regs)) diff --git a/arch/ppc/kernel/signal.c b/arch/ppc/kernel/signal.c index 1ca3a65f8..e5452fedb 100644 --- a/arch/ppc/kernel/signal.c +++ b/arch/ppc/kernel/signal.c @@ -58,6 +58,41 @@ int do_signal(sigset_t *oldset, struct pt_regs *regs); extern int sys_wait4(pid_t pid, unsigned long *stat_addr, int options, unsigned long *ru); +int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from) +{ + if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t))) + return -EFAULT; + if (from->si_code < 0) + return __copy_to_user(to, from, sizeof(siginfo_t)); + else { + int err; + + /* If you change siginfo_t structure, please be sure + this code is fixed accordingly. + It should never copy any pad contained in the structure + to avoid security leaks, but must copy the generic + 3 ints plus the relevant union member. */ + err = __put_user(from->si_signo, &to->si_signo); + err |= __put_user(from->si_errno, &to->si_errno); + err |= __put_user((short)from->si_code, &to->si_code); + /* First 32bits of unions are always present. */ + err |= __put_user(from->si_pid, &to->si_pid); + switch (from->si_code >> 16) { + case __SI_FAULT >> 16: + break; + case __SI_CHLD >> 16: + err |= __put_user(from->si_utime, &to->si_utime); + err |= __put_user(from->si_stime, &to->si_stime); + err |= __put_user(from->si_status, &to->si_status); + default: + err |= __put_user(from->si_uid, &to->si_uid); + break; + /* case __SI_RT: This is not generated by the kernel as of now. */ + } + return err; + } +} + /* * Atomically swap in the new signal mask, and wait for a signal. */ diff --git a/arch/sh/kernel/signal.c b/arch/sh/kernel/signal.c index 774359a1b..14ce2eae9 100644 --- a/arch/sh/kernel/signal.c +++ b/arch/sh/kernel/signal.c @@ -33,6 +33,41 @@ asmlinkage int sys_wait4(pid_t pid, unsigned long *stat_addr, int options, unsigned long *ru); asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset); +int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from) +{ + if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t))) + return -EFAULT; + if (from->si_code < 0) + return __copy_to_user(to, from, sizeof(siginfo_t)); + else { + int err; + + /* If you change siginfo_t structure, please be sure + this code is fixed accordingly. + It should never copy any pad contained in the structure + to avoid security leaks, but must copy the generic + 3 ints plus the relevant union member. */ + err = __put_user(from->si_signo, &to->si_signo); + err |= __put_user(from->si_errno, &to->si_errno); + err |= __put_user((short)from->si_code, &to->si_code); + /* First 32bits of unions are always present. */ + err |= __put_user(from->si_pid, &to->si_pid); + switch (from->si_code >> 16) { + case __SI_FAULT >> 16: + break; + case __SI_CHLD >> 16: + err |= __put_user(from->si_utime, &to->si_utime); + err |= __put_user(from->si_stime, &to->si_stime); + err |= __put_user(from->si_status, &to->si_status); + default: + err |= __put_user(from->si_uid, &to->si_uid); + break; + /* case __SI_RT: This is not generated by the kernel as of now. */ + } + return err; + } +} + /* * Atomically swap in the new signal mask, and wait for a signal. */ @@ -424,7 +459,7 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, err |= __put_user(&frame->info, &frame->pinfo); err |= __put_user(&frame->uc, &frame->puc); - err |= __copy_to_user(&frame->info, info, sizeof(*info)); + err |= copy_siginfo_to_user(&frame->info, info); /* Create the ucontext. */ err |= __put_user(0, &frame->uc.uc_flags); diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile index f63f542ba..e8524e430 100644 --- a/arch/sparc/kernel/Makefile +++ b/arch/sparc/kernel/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.54 2000/05/12 23:51:24 davem Exp $ +# $Id: Makefile,v 1.55 2000/05/23 23:09:08 davem Exp $ # Makefile for the linux kernel. # # Note! Dependencies are done automagically by 'make dep', which also diff --git a/arch/sparc/kernel/signal.c b/arch/sparc/kernel/signal.c index e75d5d000..e807683ea 100644 --- a/arch/sparc/kernel/signal.c +++ b/arch/sparc/kernel/signal.c @@ -95,6 +95,38 @@ struct rt_signal_frame { __siginfo_fpu_t fpu_state; }; +int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from) +{ + if (!access_ok(VERIFY_WRITE, to, sizeof(siginfo_t))) + return -EFAULT; + if (from->si_code < 0) + return __copy_to_user(to, from, sizeof(siginfo_t)); + else { + int err; + + /* If you change siginfo_t structure, please be sure + this code is fixed accordingly. + It should never copy any pad contained in the structure + to avoid security leaks, but must copy the generic + 3 ints plus the relevant union member. */ + err = __put_user(from->si_signo, &to->si_signo); + err |= __put_user(from->si_errno, &to->si_errno); + err |= __put_user((short)from->si_code, &to->si_code); + switch (from->si_code >> 16) { + case __SI_CHLD >> 16: + err |= __put_user(from->si_utime, &to->si_utime); + err |= __put_user(from->si_stime, &to->si_stime); + /* case __SI_RT: This is not generated by the kernel as of now. */ + err |= __put_user(from->si_status, &to->si_status); + default: + err |= __put_user(from->si_uid, &to->si_uid); + err |= __put_user(from->si_pid, &to->si_pid); + break; + } + return err; + } +} + /* Align macros */ #define SF_ALIGNEDSZ (((sizeof(struct signal_sframe) + 7) & (~7))) #define NF_ALIGNEDSZ (((sizeof(struct new_signal_frame) + 7) & (~7))) @@ -710,7 +742,7 @@ new_setup_rt_frame(struct k_sigaction *ka, struct pt_regs *regs, err |= __copy_to_user(sf, (char *) regs->u_regs [UREG_FP], sizeof (struct reg_window)); - err |= __copy_to_user(&sf->info, info, sizeof(siginfo_t)); + err |= copy_siginfo_to_user(&sf->info, info); if (err) goto sigsegv; diff --git a/arch/sparc64/defconfig b/arch/sparc64/defconfig index 135ee279e..2800d1bd5 100644 --- a/arch/sparc64/defconfig +++ b/arch/sparc64/defconfig @@ -201,6 +201,13 @@ CONFIG_BLK_DEV_IDE=y # CONFIG_BLK_DEV_HD is not set CONFIG_BLK_DEV_IDEDISK=y # CONFIG_IDEDISK_MULTI_MODE is not set +# CONFIG_BLK_DEV_IDEDISK_VENDOR is not set +# CONFIG_BLK_DEV_IDEDISK_FUJITSU is not set +# CONFIG_BLK_DEV_IDEDISK_IBM is not set +# CONFIG_BLK_DEV_IDEDISK_MAXTOR is not set +# CONFIG_BLK_DEV_IDEDISK_QUANTUM is not set +# CONFIG_BLK_DEV_IDEDISK_SEAGATE is not set +# CONFIG_BLK_DEV_IDEDISK_WD is not set # CONFIG_BLK_DEV_IDECS is not set CONFIG_BLK_DEV_IDECD=y CONFIG_BLK_DEV_IDETAPE=m diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile index edb6b0571..df823619c 100644 --- a/arch/sparc64/kernel/Makefile +++ b/arch/sparc64/kernel/Makefile @@ -81,13 +81,11 @@ check_asm: dummy @echo -e "# error Please issue 'make check_asm' in linux top-level directory first\n# endif\n#endif\n" >> asm_offsets.h @echo -e "#ifndef CONFIG_SMP\n" >> asm_offsets.h @echo "#include <linux/config.h>" > tmp.c - @echo "#undef __SMP__" >> tmp.c @echo "#undef CONFIG_SMP" >> tmp.c @echo "#include <linux/sched.h>" >> tmp.c $(CPP) $(CPPFLAGS) tmp.c -o tmp.i @echo "/* Automatically generated. Do not edit. */" > check_asm.c @echo "#include <linux/config.h>" >> check_asm.c - @echo "#undef __SMP__" >> check_asm.c @echo "#undef CONFIG_SMP" >> check_asm.c @echo "#include <linux/sched.h>" >> check_asm.c @echo 'struct task_struct _task;' >> check_asm.c @@ -128,7 +126,7 @@ check_asm: dummy $(SH) ./check_asm.sh thread tmp.i check_asm.c @echo 'return 0; }' >> check_asm.c @rm -f tmp.[ci] - #$(CC) -D__SMP__ -o check_asm check_asm.c + #$(CC) -o check_asm check_asm.c # <hack> Until we can do this natively, a hack has to take place $(CC) $(CPPFLAGS) $(CMODEL_CFLAG) -ffixed-g4 -S -o check_asm.s check_asm.c $(HOSTCC) -Wa,-Av9a -o check_asm check_asm.s @@ -153,7 +151,7 @@ check_asm: dummy $(SH) ./check_asm.sh thread tmp.i check_asm.c @echo 'return 0; }' >> check_asm.c @rm -f tmp.[ci] - #$(CC) -D__SMP__ -DSPIN_LOCK_DEBUG -o check_asm check_asm.c + #$(CC) -DSPIN_LOCK_DEBUG -o check_asm check_asm.c # <hack> Until we can do this natively, a hack has to take place $(CC) $(CPPFLAGS) -DSPIN_LOCK_DEBUG $(CMODEL_CFLAG) -ffixed-g4 -S -o check_asm.s check_asm.c $(HOSTCC) -Wa,-Av9a -o check_asm check_asm.s diff --git a/arch/sparc64/kernel/ioctl32.c b/arch/sparc64/kernel/ioctl32.c index 6c00ba2f6..9f5626b5b 100644 --- a/arch/sparc64/kernel/ioctl32.c +++ b/arch/sparc64/kernel/ioctl32.c @@ -1,4 +1,4 @@ -/* $Id: ioctl32.c,v 1.90 2000/05/22 07:29:39 davem Exp $ +/* $Id: ioctl32.c,v 1.91 2000/05/23 05:25:44 davem Exp $ * ioctl32.c: Conversion between 32bit and 64bit native ioctls. * * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com) @@ -2353,6 +2353,542 @@ static int do_lvm_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) } #endif +#if defined(CONFIG_DRM) || defined(CONFIG_DRM_MODULE) +/* This really belongs in include/linux/drm.h -DaveM */ +#include "../../../drivers/char/drm/drm.h" + +typedef struct drm32_version { + int version_major; /* Major version */ + int version_minor; /* Minor version */ + int version_patchlevel;/* Patch level */ + int name_len; /* Length of name buffer */ + u32 name; /* Name of driver */ + int date_len; /* Length of date buffer */ + u32 date; /* User-space buffer to hold date */ + int desc_len; /* Length of desc buffer */ + u32 desc; /* User-space buffer to hold desc */ +} drm32_version_t; +#define DRM32_IOCTL_VERSION DRM_IOWR(0x00, drm32_version_t) + +static int drm32_version(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_version_t *uversion = (drm32_version_t *)arg; + char *name_ptr, *date_ptr, *desc_ptr; + u32 tmp1, tmp2, tmp3; + drm_version_t kversion; + mm_segment_t old_fs; + int ret; + + memset(&kversion, 0, sizeof(kversion)); + if (get_user(kversion.name_len, &uversion->name_len) || + get_user(kversion.date_len, &uversion->date_len) || + get_user(kversion.desc_len, &uversion->desc_len) || + get_user(tmp1, &uversion->name) || + get_user(tmp2, &uversion->date) || + get_user(tmp3, &uversion->desc)) + return -EFAULT; + + name_ptr = (char *) A(tmp1); + date_ptr = (char *) A(tmp2); + desc_ptr = (char *) A(tmp3); + + ret = -ENOMEM; + if (kversion.name_len && name_ptr) { + kversion.name = kmalloc(kversion.name_len, GFP_KERNEL); + if (!kversion.name) + goto out; + } + if (kversion.date_len && date_ptr) { + kversion.date = kmalloc(kversion.date_len, GFP_KERNEL); + if (!kversion.date) + goto out; + } + if (kversion.desc_len && desc_ptr) { + kversion.desc = kmalloc(kversion.desc_len, GFP_KERNEL); + if (!kversion.desc) + goto out; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl (fd, DRM_IOCTL_VERSION, (unsigned long)&kversion); + set_fs(old_fs); + + if (!ret) { + if ((kversion.name && + copy_to_user(name_ptr, kversion.name, kversion.name_len)) || + (kversion.date && + copy_to_user(date_ptr, kversion.date, kversion.date_len)) || + (kversion.desc && + copy_to_user(desc_ptr, kversion.desc, kversion.desc_len))) + ret = -EFAULT; + if (put_user(kversion.version_major, &uversion->version_major) || + put_user(kversion.version_minor, &uversion->version_minor) || + put_user(kversion.version_patchlevel, &uversion->version_patchlevel) || + put_user(kversion.name_len, &uversion->name_len) || + put_user(kversion.date_len, &uversion->date_len) || + put_user(kversion.desc_len, &uversion->desc_len)) + ret = -EFAULT; + } + +out: + if (kversion.name) + kfree(kversion.name); + if (kversion.date) + kfree(kversion.date); + if (kversion.desc) + kfree(kversion.desc); + return ret; +} + +typedef struct drm32_unique { + size_t unique_len; /* Length of unique */ + u32 unique; /* Unique name for driver instantiation */ +} drm32_unique_t; +#define DRM32_IOCTL_GET_UNIQUE DRM_IOWR(0x01, drm32_unique_t) +#define DRM32_IOCTL_SET_UNIQUE DRM_IOW( 0x10, drm32_unique_t) + +static int drm32_getsetunique(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_unique_t *uarg = (drm32_unique_t *)arg; + drm_unique_t karg; + mm_segment_t old_fs; + char *uptr; + u32 tmp; + int ret; + + if (get_user(karg.unique_len, &uarg->unique_len)) + return -EFAULT; + karg.unique = NULL; + + if (get_user(tmp, &uarg->unique)) + return -EFAULT; + + uptr = (char *) A(tmp); + + if (uptr) { + karg.unique = kmalloc(karg.unique_len, GFP_KERNEL); + if (!karg.unique) + return -ENOMEM; + if (cmd == DRM32_IOCTL_SET_UNIQUE && + copy_from_user(karg.unique, uptr, karg.unique_len)) { + kfree(karg.unique); + return -EFAULT; + } + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + if (cmd == DRM32_IOCTL_GET_UNIQUE) + ret = sys_ioctl (fd, DRM_IOCTL_GET_UNIQUE, (unsigned long)&karg); + else + ret = sys_ioctl (fd, DRM_IOCTL_SET_UNIQUE, (unsigned long)&karg); + set_fs(old_fs); + + if (!ret) { + if (cmd == DRM32_IOCTL_GET_UNIQUE && + copy_to_user(uptr, karg.unique, karg.unique_len)) + ret = -EFAULT; + if (put_user(karg.unique_len, &uarg->unique_len)) + ret = -EFAULT; + } + + kfree(karg.unique); + + return ret; +} + +typedef struct drm32_map { + u32 offset; /* Requested physical address (0 for SAREA)*/ + u32 size; /* Requested physical size (bytes) */ + drm_map_type_t type; /* Type of memory to map */ + drm_map_flags_t flags; /* Flags */ + u32 handle; /* User-space: "Handle" to pass to mmap */ + /* Kernel-space: kernel-virtual address */ + int mtrr; /* MTRR slot used */ + /* Private data */ +} drm32_map_t; +#define DRM32_IOCTL_ADD_MAP DRM_IOWR(0x15, drm32_map_t) + +static int drm32_addmap(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_map_t *uarg = (drm32_map_t *) arg; + drm_map_t karg; + mm_segment_t old_fs; + u32 tmp; + int ret; + + ret = get_user(karg.offset, &uarg->offset); + ret |= get_user(karg.size, &uarg->size); + ret |= get_user(karg.type, &uarg->type); + ret |= get_user(karg.flags, &uarg->flags); + ret |= get_user(tmp, &uarg->handle); + ret |= get_user(karg.mtrr, &uarg->mtrr); + if (ret) + return -EFAULT; + + karg.handle = (void *) A(tmp); + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_ADD_MAP, (unsigned long) &karg); + set_fs(old_fs); + + if (!ret) { + ret = put_user(karg.offset, &uarg->offset); + ret |= put_user(karg.size, &uarg->size); + ret |= put_user(karg.type, &uarg->type); + ret |= put_user(karg.flags, &uarg->flags); + tmp = (u32) (long)karg.handle; + ret |= put_user(tmp, &uarg->handle); + ret |= put_user(karg.mtrr, &uarg->mtrr); + if (ret) + ret = -EFAULT; + } + + return ret; +} + +typedef struct drm32_buf_info { + int count; /* Entries in list */ + u32 list; /* (drm_buf_desc_t *) */ +} drm32_buf_info_t; +#define DRM32_IOCTL_INFO_BUFS DRM_IOWR(0x18, drm32_buf_info_t) + +static int drm32_info_bufs(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_buf_info_t *uarg = (drm32_buf_info_t *)arg; + drm_buf_desc_t *ulist; + drm_buf_info_t karg; + mm_segment_t old_fs; + int orig_count, ret; + u32 tmp; + + if (get_user(karg.count, &uarg->count) || + get_user(tmp, &uarg->list)) + return -EFAULT; + + ulist = (drm_buf_desc_t *) A(tmp); + + orig_count = karg.count; + + karg.list = kmalloc(karg.count * sizeof(drm_buf_desc_t), GFP_KERNEL); + if (!karg.list) + return -EFAULT; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_INFO_BUFS, (unsigned long) &karg); + set_fs(old_fs); + + if (!ret) { + if (karg.count <= orig_count && + (copy_to_user(ulist, karg.list, + karg.count * sizeof(drm_buf_desc_t)))) + ret = -EFAULT; + if (put_user(karg.count, &uarg->count)) + ret = -EFAULT; + } + + kfree(karg.list); + + return ret; +} + +typedef struct drm32_buf_free { + int count; + u32 list; /* (int *) */ +} drm32_buf_free_t; +#define DRM32_IOCTL_FREE_BUFS DRM_IOW( 0x1a, drm32_buf_free_t) + +static int drm32_free_bufs(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_buf_free_t *uarg = (drm32_buf_free_t *)arg; + drm_buf_free_t karg; + mm_segment_t old_fs; + int *ulist; + int ret; + u32 tmp; + + if (get_user(karg.count, &uarg->count) || + get_user(tmp, &uarg->list)) + return -EFAULT; + + ulist = (int *) A(tmp); + + karg.list = kmalloc(karg.count * sizeof(int), GFP_KERNEL); + if (!karg.list) + return -ENOMEM; + + ret = -EFAULT; + if (copy_from_user(karg.list, ulist, (karg.count * sizeof(int)))) + goto out; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_FREE_BUFS, (unsigned long) &karg); + set_fs(old_fs); + +out: + kfree(karg.list); + + return ret; +} + +typedef struct drm32_buf_pub { + int idx; /* Index into master buflist */ + int total; /* Buffer size */ + int used; /* Amount of buffer in use (for DMA) */ + u32 address; /* Address of buffer (void *) */ +} drm32_buf_pub_t; + +typedef struct drm32_buf_map { + int count; /* Length of buflist */ + u32 virtual; /* Mmaped area in user-virtual (void *) */ + u32 list; /* Buffer information (drm_buf_pub_t *) */ +} drm32_buf_map_t; +#define DRM32_IOCTL_MAP_BUFS DRM_IOWR(0x19, drm32_buf_map_t) + +static int drm32_map_bufs(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_buf_map_t *uarg = (drm32_buf_map_t *)arg; + drm32_buf_pub_t *ulist; + drm_buf_map_t karg; + mm_segment_t old_fs; + int orig_count, ret, i; + u32 tmp1, tmp2; + + if (get_user(karg.count, &uarg->count) || + get_user(tmp1, &uarg->virtual) || + get_user(tmp2, &uarg->list)) + return -EFAULT; + + karg.virtual = (void *) A(tmp1); + ulist = (drm32_buf_pub_t *) A(tmp2); + + orig_count = karg.count; + + karg.list = kmalloc(karg.count * sizeof(drm_buf_pub_t), GFP_KERNEL); + if (!karg.list) + return -ENOMEM; + + ret = -EFAULT; + for (i = 0; i < karg.count; i++) { + if (get_user(karg.list[i].idx, &ulist[i].idx) || + get_user(karg.list[i].total, &ulist[i].total) || + get_user(karg.list[i].used, &ulist[i].used) || + get_user(tmp1, &ulist[i].address)) + goto out; + + karg.list[i].address = (void *) A(tmp1); + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_MAP_BUFS, (unsigned long) &karg); + set_fs(old_fs); + + if (!ret) { + for (i = 0; i < orig_count; i++) { + tmp1 = (u32) (long) karg.list[i].address; + if (put_user(karg.list[i].idx, &ulist[i].idx) || + put_user(karg.list[i].total, &ulist[i].total) || + put_user(karg.list[i].used, &ulist[i].used) || + put_user(tmp1, &ulist[i].address)) { + ret = -EFAULT; + goto out; + } + } + if (put_user(karg.count, &uarg->count)) + ret = -EFAULT; + } + +out: + kfree(karg.list); + return ret; +} + +typedef struct drm32_dma { + /* Indices here refer to the offset into + buflist in drm_buf_get_t. */ + int context; /* Context handle */ + int send_count; /* Number of buffers to send */ + u32 send_indices; /* List of handles to buffers (int *) */ + u32 send_sizes; /* Lengths of data to send (int *) */ + drm_dma_flags_t flags; /* Flags */ + int request_count; /* Number of buffers requested */ + int request_size; /* Desired size for buffers */ + u32 request_indices; /* Buffer information (int *) */ + u32 request_sizes; /* (int *) */ + int granted_count; /* Number of buffers granted */ +} drm32_dma_t; +#define DRM32_IOCTL_DMA DRM_IOWR(0x29, drm32_dma_t) + +/* RED PEN The DRM layer blindly dereferences the send/request + * indice/size arrays even though they are userland + * pointers. -DaveM + */ +static int drm32_dma(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_dma_t *uarg = (drm32_dma_t *) arg; + int *u_si, *u_ss, *u_ri, *u_rs; + drm_dma_t karg; + mm_segment_t old_fs; + int ret; + u32 tmp1, tmp2, tmp3, tmp4; + + karg.send_indices = karg.send_sizes = NULL; + karg.request_indices = karg.request_sizes = NULL; + + if (get_user(karg.context, &uarg->context) || + get_user(karg.send_count, &uarg->send_count) || + get_user(tmp1, &uarg->send_indices) || + get_user(tmp2, &uarg->send_sizes) || + get_user(karg.flags, &uarg->flags) || + get_user(karg.request_count, &uarg->request_count) || + get_user(karg.request_size, &uarg->request_size) || + get_user(tmp3, &uarg->request_indices) || + get_user(tmp4, &uarg->request_sizes) || + get_user(karg.granted_count, &uarg->granted_count)) + return -EFAULT; + + u_si = (int *) A(tmp1); + u_ss = (int *) A(tmp2); + u_ri = (int *) A(tmp3); + u_rs = (int *) A(tmp4); + + if (karg.send_count) { + karg.send_indices = kmalloc(karg.send_count * sizeof(int), GFP_KERNEL); + karg.send_sizes = kmalloc(karg.send_count * sizeof(int), GFP_KERNEL); + + ret = -ENOMEM; + if (!karg.send_indices || !karg.send_sizes) + goto out; + + ret = -EFAULT; + if (copy_from_user(karg.send_indices, u_si, + (karg.send_count * sizeof(int))) || + copy_from_user(karg.send_sizes, u_ss, + (karg.send_count * sizeof(int)))) + goto out; + } + + if (karg.request_count) { + karg.request_indices = kmalloc(karg.request_count * sizeof(int), GFP_KERNEL); + karg.request_sizes = kmalloc(karg.request_count * sizeof(int), GFP_KERNEL); + + ret = -ENOMEM; + if (!karg.request_indices || !karg.request_sizes) + goto out; + + ret = -EFAULT; + if (copy_from_user(karg.request_indices, u_ri, + (karg.request_count * sizeof(int))) || + copy_from_user(karg.request_sizes, u_rs, + (karg.request_count * sizeof(int)))) + goto out; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_DMA, (unsigned long) &karg); + set_fs(old_fs); + + if (!ret) { + if (put_user(karg.context, &uarg->context) || + put_user(karg.send_count, &uarg->send_count) || + put_user(karg.flags, &uarg->flags) || + put_user(karg.request_count, &uarg->request_count) || + put_user(karg.request_size, &uarg->request_size) || + put_user(karg.granted_count, &uarg->granted_count)) + ret = -EFAULT; + + if (karg.send_count) { + if (copy_to_user(u_si, karg.send_indices, + (karg.send_count * sizeof(int))) || + copy_to_user(u_ss, karg.send_sizes, + (karg.send_count * sizeof(int)))) + ret = -EFAULT; + } + if (karg.request_count) { + if (copy_to_user(u_ri, karg.request_indices, + (karg.request_count * sizeof(int))) || + copy_to_user(u_rs, karg.request_sizes, + (karg.request_count * sizeof(int)))) + ret = -EFAULT; + } + } + +out: + if (karg.send_indices) + kfree(karg.send_indices); + if (karg.send_sizes) + kfree(karg.send_sizes); + if (karg.request_indices) + kfree(karg.request_indices); + if (karg.request_sizes) + kfree(karg.request_sizes); + + return ret; +} + +typedef struct drm32_ctx_res { + int count; + u32 contexts; /* (drm_ctx_t *) */ +} drm32_ctx_res_t; +#define DRM32_IOCTL_RES_CTX DRM_IOWR(0x26, drm_ctx_res_t) + +static int drm32_res_ctx(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_ctx_res_t *uarg = (drm32_ctx_res_t *) arg; + drm_ctx_t *ulist; + drm_ctx_res_t karg; + mm_segment_t old_fs; + int orig_count, ret; + u32 tmp; + + karg.contexts = NULL; + if (get_user(karg.count, &uarg->count) || + get_user(tmp, &uarg->contexts)) + return -EFAULT; + + ulist = (drm_ctx_t *) A(tmp); + + orig_count = karg.count; + if (karg.count && ulist) { + karg.contexts = kmalloc((karg.count * sizeof(drm_ctx_t)), GFP_KERNEL); + if (!karg.contexts) + return -ENOMEM; + if (copy_from_user(karg.contexts, ulist, + (karg.count * sizeof(drm_ctx_t)))) { + kfree(karg.contexts); + return -EFAULT; + } + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_RES_CTX, (unsigned long) &karg); + set_fs(old_fs); + + if (!ret) { + if (orig_count) { + if (copy_to_user(ulist, karg.contexts, + (orig_count * sizeof(drm_ctx_t)))) + ret = -EFAULT; + } + if (put_user(karg.count, &uarg->count)) + ret = -EFAULT; + } + + if (karg.contexts) + kfree(karg.contexts); + + return ret; +} + +#endif + static int ret_einval(unsigned int fd, unsigned int cmd, unsigned long arg) { return -EINVAL; @@ -2961,6 +3497,27 @@ COMPATIBLE_IOCTL(LV_SET_ACCESS) COMPATIBLE_IOCTL(LV_SET_STATUS) COMPATIBLE_IOCTL(LV_SET_ALLOCATION) #endif /* LVM */ +#if defined(CONFIG_DRM) || defined(CONFIG_DRM_MODULE) +COMPATIBLE_IOCTL(DRM_IOCTL_GET_MAGIC) +COMPATIBLE_IOCTL(DRM_IOCTL_IRQ_BUSID) +COMPATIBLE_IOCTL(DRM_IOCTL_AUTH_MAGIC) +COMPATIBLE_IOCTL(DRM_IOCTL_BLOCK) +COMPATIBLE_IOCTL(DRM_IOCTL_UNBLOCK) +COMPATIBLE_IOCTL(DRM_IOCTL_CONTROL) +COMPATIBLE_IOCTL(DRM_IOCTL_ADD_BUFS) +COMPATIBLE_IOCTL(DRM_IOCTL_MARK_BUFS) +COMPATIBLE_IOCTL(DRM_IOCTL_ADD_CTX) +COMPATIBLE_IOCTL(DRM_IOCTL_RM_CTX) +COMPATIBLE_IOCTL(DRM_IOCTL_MOD_CTX) +COMPATIBLE_IOCTL(DRM_IOCTL_GET_CTX) +COMPATIBLE_IOCTL(DRM_IOCTL_SWITCH_CTX) +COMPATIBLE_IOCTL(DRM_IOCTL_NEW_CTX) +COMPATIBLE_IOCTL(DRM_IOCTL_ADD_DRAW) +COMPATIBLE_IOCTL(DRM_IOCTL_RM_DRAW) +COMPATIBLE_IOCTL(DRM_IOCTL_LOCK) +COMPATIBLE_IOCTL(DRM_IOCTL_UNLOCK) +COMPATIBLE_IOCTL(DRM_IOCTL_FINISH) +#endif /* DRM */ /* elevator */ COMPATIBLE_IOCTL(BLKELVGET) COMPATIBLE_IOCTL(BLKELVSET) @@ -3108,6 +3665,17 @@ HANDLE_IOCTL(LE_REMAP, do_lvm_ioctl) HANDLE_IOCTL(PV_CHANGE, do_lvm_ioctl) HANDLE_IOCTL(PV_STATUS, do_lvm_ioctl) #endif /* LVM */ +#if defined(CONFIG_DRM) || defined(CONFIG_DRM_MODULE) +HANDLE_IOCTL(DRM32_IOCTL_VERSION, drm32_version); +HANDLE_IOCTL(DRM32_IOCTL_GET_UNIQUE, drm32_getsetunique); +HANDLE_IOCTL(DRM32_IOCTL_SET_UNIQUE, drm32_getsetunique); +HANDLE_IOCTL(DRM32_IOCTL_ADD_MAP, drm32_addmap); +HANDLE_IOCTL(DRM32_IOCTL_INFO_BUFS, drm32_info_bufs); +HANDLE_IOCTL(DRM32_IOCTL_FREE_BUFS, drm32_free_bufs); +HANDLE_IOCTL(DRM32_IOCTL_MAP_BUFS, drm32_map_bufs); +HANDLE_IOCTL(DRM32_IOCTL_DMA, drm32_dma); +HANDLE_IOCTL(DRM32_IOCTL_RES_CTX, drm32_res_ctx); +#endif /* DRM */ IOCTL_TABLE_END unsigned int ioctl32_hash_table[1024]; diff --git a/arch/sparc64/kernel/signal.c b/arch/sparc64/kernel/signal.c index efd6a64d1..f893e7f3a 100644 --- a/arch/sparc64/kernel/signal.c +++ b/arch/sparc64/kernel/signal.c @@ -42,6 +42,38 @@ asmlinkage int do_signal(sigset_t *oldset, struct pt_regs * regs, /* #define DEBUG_SIGNALS_TRACE 1 */ /* #define DEBUG_SIGNALS_MAPS 1 */ +int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from) +{ + if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t))) + return -EFAULT; + if (from->si_code < 0) + return __copy_to_user(to, from, sizeof(siginfo_t)); + else { + int err; + + /* If you change siginfo_t structure, please be sure + this code is fixed accordingly. + It should never copy any pad contained in the structure + to avoid security leaks, but must copy the generic + 3 ints plus the relevant union member. */ + err = __put_user(*(long *)&from->si_signo, (long *)&to->si_signo); + err |= __put_user((short)from->si_code, &to->si_code); + switch (from->si_code >> 16) { + case __SI_CHLD >> 16: + err |= __put_user(from->si_utime, &to->si_utime); + err |= __put_user(from->si_stime, &to->si_stime); + case __SI_FAULT >> 16: + case __SI_POLL >> 16: + err |= __put_user(from->si_trapno, &to->si_trapno); + default: + err |= __put_user(from->si_addr, &to->si_addr); + break; + /* case __SI_RT: This is not generated by the kernel as of now. */ + } + return err; + } +} + /* {set, get}context() needed for 64-bit SparcLinux userland. */ asmlinkage void sparc64_set_context(struct pt_regs *regs) { @@ -512,7 +544,7 @@ setup_rt_frame(struct k_sigaction *ka, struct pt_regs *regs, sizeof(struct reg_window)); if (info) - err |= copy_to_user(&sf->info, info, sizeof(siginfo_t)); + err |= copy_siginfo_to_user(&sf->info, info); else { err |= __put_user(signo, &sf->info.si_signo); err |= __put_user(SI_NOINFO, &sf->info.si_code); diff --git a/arch/sparc64/kernel/signal32.c b/arch/sparc64/kernel/signal32.c index 1f5c03716..f908b0636 100644 --- a/arch/sparc64/kernel/signal32.c +++ b/arch/sparc64/kernel/signal32.c @@ -108,17 +108,21 @@ int copy_siginfo_to_user32(siginfo_t32 *to, siginfo_t *from) if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t32))) return -EFAULT; + /* If you change siginfo_t structure, please be sure + this code is fixed accordingly. + It should never copy any pad contained in the structure + to avoid security leaks, but must copy the generic + 3 ints plus the relevant union member. + This routine must convert siginfo from 64bit to 32bit as well + at the same time. */ err = __put_user(from->si_signo, &to->si_signo); err |= __put_user(from->si_errno, &to->si_errno); - err |= __put_user(from->si_code, &to->si_code); + err |= __put_user((short)from->si_code, &to->si_code); if (from->si_code < 0) err |= __copy_to_user(&to->_sifields._pad, &from->_sifields._pad, SI_PAD_SIZE); else { - int signo = from->si_signo; - if (from->si_code == SI_USER || from->si_code == SI_KERNEL) - signo = SIGRTMIN; - switch (signo) { - case SIGCHLD: + switch (from->si_code >> 16) { + case __SI_CHLD >> 16: err |= __put_user(from->si_utime, &to->si_utime); err |= __put_user(from->si_stime, &to->si_stime); err |= __put_user(from->si_status, &to->si_status); @@ -126,16 +130,12 @@ int copy_siginfo_to_user32(siginfo_t32 *to, siginfo_t *from) err |= __put_user(from->si_pid, &to->si_pid); err |= __put_user(from->si_uid, &to->si_uid); break; - case SIGURG: - case SIGIO: - case SIGSEGV: - case SIGILL: - case SIGFPE: - case SIGBUS: - case SIGEMT: + case __SI_FAULT >> 16: + case __SI_POLL >> 16: err |= __put_user(from->si_trapno, &to->si_trapno); err |= __put_user((long)from->si_addr, &to->si_addr); break; + /* case __SI_RT: This is not generated by the kernel as of now. */ } } return err; diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c index 12a872447..942f37640 100644 --- a/arch/sparc64/kernel/sparc64_ksyms.c +++ b/arch/sparc64/kernel/sparc64_ksyms.c @@ -1,4 +1,4 @@ -/* $Id: sparc64_ksyms.c,v 1.84 2000/05/09 17:40:14 davem Exp $ +/* $Id: sparc64_ksyms.c,v 1.85 2000/05/23 02:14:25 davem Exp $ * arch/sparc64/kernel/sparc64_ksyms.c: Sparc64 specific ksyms support. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -83,6 +83,7 @@ extern int (*handle_mathemu)(struct pt_regs *, struct fpustate *); extern long sparc32_open(const char * filename, int flags, int mode); extern int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, unsigned int, unsigned long, struct file *)); extern int unregister_ioctl32_conversion(unsigned int cmd); +extern int io_remap_page_range(unsigned long from, unsigned long offset, unsigned long size, pgprot_t prot, int space); extern void bcopy (const char *, char *, int); extern int __ashrdi3(int, int); @@ -223,6 +224,9 @@ EXPORT_SYMBOL(pci_dma_supported); EXPORT_SYMBOL(register_ioctl32_conversion); EXPORT_SYMBOL(unregister_ioctl32_conversion); +/* I/O device mmaping on Sparc64. */ +EXPORT_SYMBOL(io_remap_page_range); + /* Solaris/SunOS binary compatibility */ EXPORT_SYMBOL(_sigpause_common); diff --git a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c index 12592b747..db6d2b4d0 100644 --- a/arch/sparc64/kernel/sys_sparc32.c +++ b/arch/sparc64/kernel/sys_sparc32.c @@ -2052,43 +2052,84 @@ asmlinkage int sys32_rt_sigpending(sigset_t32 *set, __kernel_size_t32 sigsetsize return ret; } -extern asmlinkage int -sys_rt_sigtimedwait(const sigset_t *uthese, siginfo_t *uinfo, - const struct timespec *uts, size_t sigsetsize); - asmlinkage int sys32_rt_sigtimedwait(sigset_t32 *uthese, siginfo_t32 *uinfo, struct timespec32 *uts, __kernel_size_t32 sigsetsize) { - sigset_t s; - sigset_t32 s32; - struct timespec t; - int ret; - mm_segment_t old_fs = get_fs(); + int ret, sig; + sigset_t these; + sigset_t32 these32; + struct timespec ts; siginfo_t info; - - if (copy_from_user (&s32, uthese, sizeof(sigset_t32))) + long timeout = 0; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (copy_from_user (&these32, uthese, sizeof(sigset_t32))) return -EFAULT; + switch (_NSIG_WORDS) { - case 4: s.sig[3] = s32.sig[6] | (((long)s32.sig[7]) << 32); - case 3: s.sig[2] = s32.sig[4] | (((long)s32.sig[5]) << 32); - case 2: s.sig[1] = s32.sig[2] | (((long)s32.sig[3]) << 32); - case 1: s.sig[0] = s32.sig[0] | (((long)s32.sig[1]) << 32); + case 4: these.sig[3] = these32.sig[6] | (((long)these32.sig[7]) << 32); + case 3: these.sig[2] = these32.sig[4] | (((long)these32.sig[5]) << 32); + case 2: these.sig[1] = these32.sig[2] | (((long)these32.sig[3]) << 32); + case 1: these.sig[0] = these32.sig[0] | (((long)these32.sig[1]) << 32); } + + /* + * Invert the set of allowed signals to get those we + * want to block. + */ + sigdelsetmask(&these, sigmask(SIGKILL)|sigmask(SIGSTOP)); + signotset(&these); + if (uts) { - ret = get_user (t.tv_sec, &uts->tv_sec); - ret |= __get_user (t.tv_nsec, &uts->tv_nsec); - if (ret) - return -EFAULT; + if (get_user (ts.tv_sec, &uts->tv_sec) || + get_user (ts.tv_nsec, &uts->tv_nsec)) + return -EINVAL; + if (ts.tv_nsec >= 1000000000L || ts.tv_nsec < 0 + || ts.tv_sec < 0) + return -EINVAL; } - set_fs (KERNEL_DS); - ret = sys_rt_sigtimedwait(&s, &info, &t, sigsetsize); - set_fs (old_fs); - if (ret >= 0 && uinfo) { - extern int copy_siginfo_to_user32(siginfo_t32 *, siginfo_t *); - if (copy_siginfo_to_user32(uinfo, &info)) - ret = -EFAULT; + + spin_lock_irq(¤t->sigmask_lock); + sig = dequeue_signal(&these, &info); + if (!sig) { + /* None ready -- temporarily unblock those we're interested + in so that we'll be awakened when they arrive. */ + sigset_t oldblocked = current->blocked; + sigandsets(¤t->blocked, ¤t->blocked, &these); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + timeout = MAX_SCHEDULE_TIMEOUT; + if (uts) + timeout = (timespec_to_jiffies(&ts) + + (ts.tv_sec || ts.tv_nsec)); + + current->state = TASK_INTERRUPTIBLE; + timeout = schedule_timeout(timeout); + + spin_lock_irq(¤t->sigmask_lock); + sig = dequeue_signal(&these, &info); + current->blocked = oldblocked; + recalc_sigpending(current); } + spin_unlock_irq(¤t->sigmask_lock); + + if (sig) { + ret = sig; + if (uinfo) { + if (copy_siginfo_to_user32(uinfo, &info)) + ret = -EFAULT; + } + } else { + ret = -EAGAIN; + if (timeout) + ret = -EINTR; + } + return ret; } diff --git a/arch/sparc64/mm/modutil.c b/arch/sparc64/mm/modutil.c index 6d68d7468..8ae715c38 100644 --- a/arch/sparc64/mm/modutil.c +++ b/arch/sparc64/mm/modutil.c @@ -1,4 +1,4 @@ -/* $Id: modutil.c,v 1.4 1998/07/26 06:29:08 davem Exp $ +/* $Id: modutil.c,v 1.5 2000/05/23 23:09:08 davem Exp $ * arch/sparc64/mm/modutil.c * * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -58,7 +58,7 @@ void * module_map (unsigned long size) area->next = *p; *p = area; - if (vmalloc_area_pages(VMALLOC_VMADDR(addr), size)) { + if (vmalloc_area_pages(VMALLOC_VMADDR(addr), size, GFP_KERNEL)) { vfree(addr); return NULL; } |