diff --git a/Documentation/ioctl-number.txt b/Documentation/ioctl-number.txt index 8f750c0..0a4358d 100644 --- a/Documentation/ioctl-number.txt +++ b/Documentation/ioctl-number.txt @@ -81,6 +81,7 @@ Code Seq# Include File Comments '8' all SNP8023 advanced NIC card 'A' 00-1F linux/apm_bios.h +'A' 20-2F asm-i386/cpuctl.h 'B' C0-FF advanced bbus 'C' all linux/soundcard.h @@ -180,7 +181,7 @@ Code Seq# Include File Comments 0xAB 00-1F linux/nbd.h 0xAC 00-1F linux/raw.h 0xAD 00 Netfilter device in development: - + 0xB0 all RATIO devices in development: 0xB1 00-1F PPPoX diff --git a/arch/i386/Kconfig b/arch/i386/Kconfig index 53d6237..a48a8de 100644 --- a/arch/i386/Kconfig +++ b/arch/i386/Kconfig @@ -467,8 +467,13 @@ config MICROCODE_OLD_INTERFACE depends on MICROCODE default y +config X86_SETGPR + tristate + default n + config X86_MSR tristate "/dev/cpu/*/msr - Model-specific register support" + select X86_SETGPR help This device gives privileged processes access to the x86 Model-Specific Registers (MSRs). It is a character device with @@ -478,6 +483,7 @@ config X86_MSR config X86_CPUID tristate "/dev/cpu/*/cpuid - CPU information support" + select X86_SETGPR help This device gives processes access to the x86 CPUID instruction to be executed on a specific processor. It is a character device @@ -742,11 +748,11 @@ config EFI kernel should continue to boot on existing non-EFI platforms. config IRQBALANCE - bool "Enable kernel irq balancing" + bool "Enable kernel irq balancing" depends on SMP && X86_IO_APIC default y help - The default yes will allow the kernel to do irq load balancing. + The default yes will allow the kernel to do irq load balancing. Saying no will keep the kernel from doing irq load balancing. # turning this on wastes a bunch of space. @@ -865,19 +871,19 @@ config PHYSICAL_ALIGN range 0x2000 0x400000 help This value puts the alignment restrictions on physical address - where kernel is loaded and run from. Kernel is compiled for an - address which meets above alignment restriction. - - If bootloader loads the kernel at a non-aligned address and - CONFIG_RELOCATABLE is set, kernel will move itself to nearest - address aligned to above value and run from there. - - If bootloader loads the kernel at a non-aligned address and - CONFIG_RELOCATABLE is not set, kernel will ignore the run time - load address and decompress itself to the address it has been - compiled for and run from there. The address for which kernel is - compiled already meets above alignment restrictions. Hence the - end result is that kernel runs from a physical address meeting + where kernel is loaded and run from. Kernel is compiled for an + address which meets above alignment restriction. + + If bootloader loads the kernel at a non-aligned address and + CONFIG_RELOCATABLE is set, kernel will move itself to nearest + address aligned to above value and run from there. + + If bootloader loads the kernel at a non-aligned address and + CONFIG_RELOCATABLE is not set, kernel will ignore the run time + load address and decompress itself to the address it has been + compiled for and run from there. The address for which kernel is + compiled already meets above alignment restrictions. Hence the + end result is that kernel runs from a physical address meeting above alignment restrictions. Don't change this unless you know what you are doing. @@ -1124,7 +1130,7 @@ config PCI_BIOS config PCI_DIRECT bool - depends on PCI && ((PCI_GODIRECT || PCI_GOANY) || X86_VISWS) + depends on PCI && ((PCI_GODIRECT || PCI_GOANY) || X86_VISWS) default y config PCI_MMCONFIG diff --git a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile index 4ae3dcf..34c270d 100644 --- a/arch/i386/kernel/Makefile +++ b/arch/i386/kernel/Makefile @@ -14,8 +14,9 @@ obj-y += cpu/ obj-y += acpi/ obj-$(CONFIG_X86_BIOS_REBOOT) += reboot.o obj-$(CONFIG_MCA) += mca.o -obj-$(CONFIG_X86_MSR) += msr.o -obj-$(CONFIG_X86_CPUID) += cpuid.o +obj-$(CONFIG_X86_MSR) += msr.o msr_asm.o +obj-$(CONFIG_X86_CPUID) += cpuid.o cpuid_asm.o +obj-$(CONFIG_X86_SETGPR) += setgpr.o obj-$(CONFIG_MICROCODE) += microcode.o obj-$(CONFIG_APM) += apm.o obj-$(CONFIG_X86_SMP) += smp.o smpboot.o tsc_sync.o @@ -31,12 +32,12 @@ obj-$(CONFIG_X86_SUMMIT_NUMA) += summit.o obj-$(CONFIG_KPROBES) += kprobes.o obj-$(CONFIG_MODULES) += module.o obj-y += sysenter.o vsyscall.o -obj-$(CONFIG_ACPI_SRAT) += srat.o -obj-$(CONFIG_EFI) += efi.o efi_stub.o -obj-$(CONFIG_DOUBLEFAULT) += doublefault.o +obj-$(CONFIG_ACPI_SRAT) += srat.o +obj-$(CONFIG_EFI) += efi.o efi_stub.o +obj-$(CONFIG_DOUBLEFAULT) += doublefault.o obj-$(CONFIG_VM86) += vm86.o obj-$(CONFIG_EARLY_PRINTK) += early_printk.o -obj-$(CONFIG_HPET_TIMER) += hpet.o +obj-$(CONFIG_HPET_TIMER) += hpet.o obj-$(CONFIG_K8_NB) += k8.o obj-$(CONFIG_VMI) += vmi.o vmitime.o @@ -85,4 +86,3 @@ $(obj)/vsyscall-syms.o: $(src)/vsyscall.lds \ k8-y += ../../x86_64/kernel/k8.o stacktrace-y += ../../x86_64/kernel/stacktrace.o - diff --git a/arch/i386/kernel/cpuid.c b/arch/i386/kernel/cpuid.c index eeae0d9..33a359d 100644 --- a/arch/i386/kernel/cpuid.c +++ b/arch/i386/kernel/cpuid.c @@ -1,6 +1,6 @@ /* ----------------------------------------------------------------------- * - * - * Copyright 2000 H. Peter Anvin - All Rights Reserved + * + * Copyright 2000-2007 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 @@ -42,34 +42,42 @@ #include #include #include +#include + +#include "setgpr.h" static struct class *cpuid_class; +void setgpr_cpuid(void); + +static inline void cpuid_everything(u64 *datain, u64 *dataout) +{ + setgpr_wrapper(datain, dataout, setgpr_cpuid); +} + #ifdef CONFIG_SMP struct cpuid_command { - u32 reg; - u32 *data; + u64 *datain, *dataout; }; static void cpuid_smp_cpuid(void *cmd_block) { struct cpuid_command *cmd = (struct cpuid_command *)cmd_block; - cpuid(cmd->reg, &cmd->data[0], &cmd->data[1], &cmd->data[2], - &cmd->data[3]); + cpuid_everything(cmd->datain, cmd->dataout); } -static inline void do_cpuid(int cpu, u32 reg, u32 * data) +static inline void do_cpuid(int cpu, u64 *datain, u64 *dataout) { - struct cpuid_command cmd; - preempt_disable(); if (cpu == smp_processor_id()) { - cpuid(reg, &data[0], &data[1], &data[2], &data[3]); + cpuid_everything(datain, dataout); } else { - cmd.reg = reg; - cmd.data = data; + struct cpuid_command cmd; + + cmd.datain = datain; + cmd.dataout = dataout; smp_call_function_single(cpu, cpuid_smp_cpuid, &cmd, 1, 1); } @@ -77,9 +85,9 @@ static inline void do_cpuid(int cpu, u32 reg, u32 * data) } #else /* ! CONFIG_SMP */ -static inline void do_cpuid(int cpu, u32 reg, u32 * data) +static inline void do_cpuid(int cpu, u64 *datain, u64 *dataout) { - cpuid(reg, &data[0], &data[1], &data[2], &data[3]); + cpuid_everything(datain, dataout); } #endif /* ! CONFIG_SMP */ @@ -114,12 +122,18 @@ static ssize_t cpuid_read(struct file *file, char __user *buf, u32 data[4]; u32 reg = *ppos; int cpu = iminor(file->f_path.dentry->d_inode); + u64 inreg[16], outreg[16]; if (count % 16) return -EINVAL; /* Invalid chunk size */ for (; count; count -= 16) { - do_cpuid(cpu, reg, data); + inreg[1] = reg; /* ecx */ + do_cpuid(cpu, inreg, outreg); + data[0] = (u32)outreg[0]; /* eax */ + data[1] = (u32)outreg[3]; /* ebx */ + data[2] = (u32)outreg[1]; /* ecx */ + data[3] = (u32)outreg[2]; /* edx */ if (copy_to_user(tmp, &data, 16)) return -EFAULT; tmp += 16; @@ -129,6 +143,29 @@ static ssize_t cpuid_read(struct file *file, char __user *buf, return tmp - buf; } +static long cpuid_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int cpu = iminor(file->f_dentry->d_inode); + u64 inreg[16], outreg[16]; + + switch (cmd) { + case IOC_CPUID_SPECIAL: + if ( !access_ok(VERIFY_WRITE, (void *)arg, sizeof inreg) || + copy_from_user(inreg, (void *)arg, sizeof inreg) ) + return -EFAULT; + + do_cpuid(cpu, inreg, outreg); + + if ( copy_to_user((void *)arg, outreg, sizeof outreg) ) + return -EFAULT; + + return 0; + + default: + return -ENOTTY; + } +} + static int cpuid_open(struct inode *inode, struct file *file) { unsigned int cpu = iminor(file->f_path.dentry->d_inode); @@ -146,10 +183,12 @@ static int cpuid_open(struct inode *inode, struct file *file) * File operations we support */ static const struct file_operations cpuid_fops = { - .owner = THIS_MODULE, - .llseek = cpuid_seek, - .read = cpuid_read, - .open = cpuid_open, + .owner = THIS_MODULE, + .llseek = cpuid_seek, + .read = cpuid_read, + .unlocked_ioctl = cpuid_ioctl, + .compat_ioctl = cpuid_ioctl, + .open = cpuid_open, }; static int cpuid_device_create(int i) @@ -201,7 +240,7 @@ static int __init cpuid_init(void) } for_each_online_cpu(i) { err = cpuid_device_create(i); - if (err != 0) + if (err != 0) goto out_class; } register_hotcpu_notifier(&cpuid_class_cpu_notifier); @@ -216,7 +255,7 @@ out_class: } class_destroy(cpuid_class); out_chrdev: - unregister_chrdev(CPUID_MAJOR, "cpu/cpuid"); + unregister_chrdev(CPUID_MAJOR, "cpu/cpuid"); out: return err; } diff --git a/arch/i386/kernel/cpuid_asm.S b/arch/i386/kernel/cpuid_asm.S new file mode 100644 index 0000000..6f7cdae --- /dev/null +++ b/arch/i386/kernel/cpuid_asm.S @@ -0,0 +1,27 @@ +# ----------------------------------------------------------------------- +# +# Copyright 2005-2007 H. Peter Anvin - All Rights Reserved +# +# This file is part of the Linux kernel, and is made available under +# the terms of the GNU General Public License version 2 or (at your +# option) any later version; incorporated herein by reference. +# +# ----------------------------------------------------------------------- + +# +# arch/i386/cpuid_asm.S +# + +#include + +# cpuid function to be used with setgpr_wrapper() + + .text + .globl setgpr_cpuid + .type setgrp_cpuid,@function + +setgpr_cpuid: + cpuid + ret + + .size setgpr_cpuid,.-setgpr_cpuid diff --git a/arch/i386/kernel/msr.c b/arch/i386/kernel/msr.c index bcaa6e9..284a3df 100644 --- a/arch/i386/kernel/msr.c +++ b/arch/i386/kernel/msr.c @@ -1,6 +1,6 @@ /* ----------------------------------------------------------------------- * - * - * Copyright 2000 H. Peter Anvin - All Rights Reserved + * + * Copyright 2000-2007 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 @@ -42,63 +42,57 @@ #include #include #include +#include + +#include "setgpr.h" static struct class *msr_class; -static inline int wrmsr_eio(u32 reg, u32 eax, u32 edx) -{ - int err; +void setgpr_wrmsr(void); +void setgpr_rdmsr(void); - err = wrmsr_safe(reg, eax, edx); - if (err) - err = -EIO; - return err; +static inline int wrmsr_everything(u64 *datain, u64 *dataout) +{ + return setgpr_wrapper(datain, dataout, setgpr_wrmsr); } - -static inline int rdmsr_eio(u32 reg, u32 *eax, u32 *edx) +static inline int rdmsr_everything(u64 *datain, u64 *dataout) { - int err; - - err = rdmsr_safe(reg, eax, edx); - if (err) - err = -EIO; - return err; + return setgpr_wrapper(datain, dataout, setgpr_rdmsr); } #ifdef CONFIG_SMP struct msr_command { + u64 *datain, *dataout; int err; - u32 reg; - u32 data[2]; }; static void msr_smp_wrmsr(void *cmd_block) { struct msr_command *cmd = (struct msr_command *)cmd_block; - cmd->err = wrmsr_eio(cmd->reg, cmd->data[0], cmd->data[1]); + cmd->err = wrmsr_everything(cmd->datain, cmd->dataout); } static void msr_smp_rdmsr(void *cmd_block) { struct msr_command *cmd = (struct msr_command *)cmd_block; - cmd->err = rdmsr_eio(cmd->reg, &cmd->data[0], &cmd->data[1]); + cmd->err = rdmsr_everything(cmd->datain, cmd->dataout); } -static inline int do_wrmsr(int cpu, u32 reg, u32 eax, u32 edx) +static inline int do_wrmsr(int cpu, u64 *datain, u64 *dataout) { - struct msr_command cmd; int ret; preempt_disable(); if (cpu == smp_processor_id()) { - ret = wrmsr_eio(reg, eax, edx); + ret = wrmsr_everything(datain, dataout); } else { - cmd.reg = reg; - cmd.data[0] = eax; - cmd.data[1] = edx; + struct msr_command cmd; + + cmd.datain = datain; + cmd.dataout = dataout; smp_call_function_single(cpu, msr_smp_wrmsr, &cmd, 1, 1); ret = cmd.err; @@ -107,22 +101,20 @@ static inline int do_wrmsr(int cpu, u32 reg, u32 eax, u32 edx) return ret; } -static inline int do_rdmsr(int cpu, u32 reg, u32 * eax, u32 * edx) +static inline int do_rdmsr(int cpu, u64 *datain, u64 *dataout) { - struct msr_command cmd; int ret; preempt_disable(); if (cpu == smp_processor_id()) { - ret = rdmsr_eio(reg, eax, edx); + ret = rdmsr_everything(datain, dataout); } else { - cmd.reg = reg; + struct msr_command cmd; - smp_call_function_single(cpu, msr_smp_rdmsr, &cmd, 1, 1); - - *eax = cmd.data[0]; - *edx = cmd.data[1]; + cmd.datain = datain; + cmd.dataout = dataout; + smp_call_function_single(cpu, msr_smp_rdmsr, &cmd, 1, 1); ret = cmd.err; } preempt_enable(); @@ -131,14 +123,14 @@ static inline int do_rdmsr(int cpu, u32 reg, u32 * eax, u32 * edx) #else /* ! CONFIG_SMP */ -static inline int do_wrmsr(int cpu, u32 reg, u32 eax, u32 edx) +static inline int do_wrmsr(int cpu, u64 *inreg, u64 *outreg) { - return wrmsr_eio(reg, eax, edx); + return wrmsr_everything(inreg, outreg); } -static inline int do_rdmsr(int cpu, u32 reg, u32 *eax, u32 *edx) +static inline int do_rdmsr(int cpu, u64 *inreg, u64 *outreg) { - return rdmsr_eio(reg, eax, edx); + return rdmsr_everything(inreg, outreg); } #endif /* ! CONFIG_SMP */ @@ -166,6 +158,7 @@ static ssize_t msr_read(struct file *file, char __user * buf, { u32 __user *tmp = (u32 __user *) buf; u32 data[2]; + u64 inreg[16], outreg[16]; u32 reg = *ppos; int cpu = iminor(file->f_path.dentry->d_inode); int err; @@ -174,9 +167,12 @@ static ssize_t msr_read(struct file *file, char __user * buf, return -EINVAL; /* Invalid chunk size */ for (; count; count -= 8) { - err = do_rdmsr(cpu, reg, &data[0], &data[1]); + inreg[1] = reg; /* ecx */ + err = do_rdmsr(cpu, inreg, outreg); if (err) return err; + data[0] = (u32)outreg[0]; /* eax */ + data[1] = (u32)outreg[2]; /* edx */ if (copy_to_user(tmp, &data, 8)) return -EFAULT; tmp += 2; @@ -190,6 +186,7 @@ static ssize_t msr_write(struct file *file, const char __user *buf, { const u32 __user *tmp = (const u32 __user *)buf; u32 data[2]; + u64 inreg[16], outreg[16]; u32 reg = *ppos; int cpu = iminor(file->f_path.dentry->d_inode); int err; @@ -200,7 +197,10 @@ static ssize_t msr_write(struct file *file, const char __user *buf, for (; count; count -= 8) { if (copy_from_user(&data, tmp, 8)) return -EFAULT; - err = do_wrmsr(cpu, reg, data[0], data[1]); + inreg[1] = reg; /* ecx */ + inreg[0] = data[0]; /* eax */ + inreg[2] = data[1]; /* edx */ + err = do_wrmsr(cpu, inreg, outreg); if (err) return err; tmp += 2; @@ -209,6 +209,46 @@ static ssize_t msr_write(struct file *file, const char __user *buf, return ((char __user *)tmp) - buf; } +static long msr_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int cpu = iminor(file->f_dentry->d_inode); + u64 inreg[16], outreg[16]; + int err; + + switch (cmd) { + case IOC_WRMSR_SPECIAL: + if ( !access_ok(VERIFY_WRITE, (void *)arg, sizeof inreg) || + copy_from_user(inreg, (void *)arg, sizeof inreg) ) + return -EFAULT; + + err = do_wrmsr(cpu, inreg, outreg); + if (err) + return err; + + if ( copy_to_user((void *)arg, outreg, sizeof outreg) ) + return -EFAULT; + + return 0; + + case IOC_RDMSR_SPECIAL: + if ( !access_ok(VERIFY_WRITE, (void *)arg, sizeof inreg) || + copy_from_user(inreg, (void *)arg, sizeof inreg) ) + return -EFAULT; + + err = do_rdmsr(cpu, inreg, outreg); + if (err) + return err; + + if ( copy_to_user((void *)arg, outreg, sizeof outreg) ) + return -EFAULT; + + return 0; + + default: + return -ENOTTY; + } +} + static int msr_open(struct inode *inode, struct file *file) { unsigned int cpu = iminor(file->f_path.dentry->d_inode); @@ -226,11 +266,13 @@ static int msr_open(struct inode *inode, struct file *file) * File operations we support */ static const struct file_operations msr_fops = { - .owner = THIS_MODULE, - .llseek = msr_seek, - .read = msr_read, - .write = msr_write, - .open = msr_open, + .owner = THIS_MODULE, + .llseek = msr_seek, + .read = msr_read, + .write = msr_write, + .unlocked_ioctl = msr_ioctl, + .compat_ioctl = msr_ioctl, + .open = msr_open, }; static int msr_device_create(int i) diff --git a/arch/i386/kernel/msr_asm.S b/arch/i386/kernel/msr_asm.S new file mode 100644 index 0000000..0fda456 --- /dev/null +++ b/arch/i386/kernel/msr_asm.S @@ -0,0 +1,52 @@ +# ----------------------------------------------------------------------- +# +# Copyright 2005 H. Peter Anvin - All Rights Reserved +# +# This file is part of the Linux kernel, and is made available under +# the terms of the GNU General Public License version 2 or (at your +# option) any later version; incorporated herein by reference. +# +# ----------------------------------------------------------------------- + +# +# arch/i386/msr_asm.S +# + +#include + +# +# rdmsr/wrmsr functions to be used with setgpr_wrapper() + + .text + + .section .fixup,"ax" +setgpr_msr_fixup: + movl $-EIO,(%esp) + ret + .previous + + + .globl setgpr_wrmsr + .type setgrp_wrmsr,@function +setgpr_wrmsr: +1: wrmsr + ret + + .section __ex_table,"a" + .align 4 + .long 1b,setgpr_msr_fixup + .previous + + .globl setgpr_rdmsr + .type setgrp_rdmsr,@function + .size setgpr_wrmsr, .-setgpr_wrmsr +setgpr_rdmsr: +1: rdmsr + ret + + .section __ex_table,"a" + .align 4 + .long 1b,setgpr_msr_fixup + .previous + + .size setgpr_rdmsr, .-setgpr_rdmsr diff --git a/arch/i386/kernel/setgpr.S b/arch/i386/kernel/setgpr.S new file mode 100644 index 0000000..13c130b --- /dev/null +++ b/arch/i386/kernel/setgpr.S @@ -0,0 +1,78 @@ +# ----------------------------------------------------------------------- +# +# Copyright 2005-2007 H. Peter Anvin - All Rights Reserved +# +# This file is part of the Linux kernel, and is made available under +# the terms of the GNU General Public License version 2 or (at your +# option) any later version; incorporated herein by reference. +# +# ----------------------------------------------------------------------- + +# +# arch/i386/setgpr.S +# + +# Wrapper around a subroutine that allows all the GPRs to be set +# +# The prototype is: +# long setgpr_wrapper(u64 *in, u64 *out, void (*func)(void)) +# +# ... where each pointer points to the GPRs in numerical order. The +# images have 16 64-bit values to make it compatible with x86-64. +# +# The stub is called "naked", and therefore should be an assembly routine. +# The word on the top of the stack is initialized to zero and is the return +# value. + + .text + .globl setgpr_wrapper + .type setgrp_wrapper,@function + +setgpr_wrapper: + pushl %ebp + pushl %ebx + pushl %esi + pushl %edi + pushl $0 # Return value + + # Zero out the output data structure, since we only write + # the fields appropriate to x86 here... + movl 28(%esp),%edi # out pointer + xorl %eax,%eax + movl $2*16,%ecx + cld; rep; stosl + + movl 24(%esp),%ebx # in pointer + movl (%ebx),%eax + movl 8(%ebx),%ecx + movl 16(%ebx),%edx + # 24 is %ebx + # 32 is %esp + movl 40(%ebx),%ebp + movl 48(%ebx),%esi + movl 56(%ebx),%edi + movl 24(%ebx),%ebp + + call *32(%esp) + + pushl %ebx + movl 32(%esp),%ebx # out pointer + movl %eax,(%ebx) + popl %eax # Saved %ebx + movl %ecx,8(%ebx) + movl %edx,16(%ebx) + movl %eax,24(%ebx) # Saved %ebx + # 32 is %esp + movl %ebp,40(%ebx) + movl %esi,48(%ebx) + movl %edi,56(%ebx) + + popl %eax # Return value + popl %edi + popl %esi + popl %ebx + popl %ebp + + ret + + .size setgpr_wrapper,.-setgpr_wrapper diff --git a/arch/i386/kernel/setgpr.h b/arch/i386/kernel/setgpr.h new file mode 100644 index 0000000..6249209 --- /dev/null +++ b/arch/i386/kernel/setgpr.h @@ -0,0 +1,21 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2005-2007 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., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#ifndef KERNEL_SETGPR_H +#define KERNEL_SETGPR_H + +#include +#include + +asmlinkage long setgpr_wrapper(u64 *datain, u64 *dataout, void (*func)(void)); + +#endif /* KERNEL_SETGPR_H */ diff --git a/arch/x86_64/Kconfig b/arch/x86_64/Kconfig index 56eb14c..0714e89 100644 --- a/arch/x86_64/Kconfig +++ b/arch/x86_64/Kconfig @@ -232,8 +232,13 @@ config MICROCODE_OLD_INTERFACE depends on MICROCODE default y +config X86_SETGPR + tristate + default n + config X86_MSR tristate "/dev/cpu/*/msr - Model-specific register support" + select X86_SETGPR help This device gives privileged processes access to the x86 Model-Specific Registers (MSRs). It is a character device with @@ -243,6 +248,7 @@ config X86_MSR config X86_CPUID tristate "/dev/cpu/*/cpuid - CPU information support" + select X86_SETGPR help This device gives processes access to the x86 CPUID instruction to be executed on a specific processor. It is a character device diff --git a/arch/x86_64/kernel/Makefile b/arch/x86_64/kernel/Makefile index bb47e86..fe4cbb8 100644 --- a/arch/x86_64/kernel/Makefile +++ b/arch/x86_64/kernel/Makefile @@ -16,9 +16,10 @@ obj-$(CONFIG_X86_MCE_INTEL) += mce_intel.o obj-$(CONFIG_X86_MCE_AMD) += mce_amd.o obj-$(CONFIG_MTRR) += ../../i386/kernel/cpu/mtrr/ obj-$(CONFIG_ACPI) += acpi/ -obj-$(CONFIG_X86_MSR) += msr.o +obj-$(CONFIG_X86_MSR) += msr.o msr_asm.o obj-$(CONFIG_MICROCODE) += microcode.o -obj-$(CONFIG_X86_CPUID) += cpuid.o +obj-$(CONFIG_X86_CPUID) += cpuid.o cpuid_asm.o +obj-$(CONFIG_X86_SETGPR) += setgpr.o obj-$(CONFIG_SMP) += smp.o smpboot.o trampoline.o tsc_sync.o obj-y += apic.o nmi.o obj-y += io_apic.o mpparse.o \ diff --git a/arch/x86_64/kernel/cpuid_asm.S b/arch/x86_64/kernel/cpuid_asm.S new file mode 100644 index 0000000..33499df --- /dev/null +++ b/arch/x86_64/kernel/cpuid_asm.S @@ -0,0 +1,27 @@ +# ----------------------------------------------------------------------- +# +# Copyright 2005-2007 H. Peter Anvin - All Rights Reserved +# +# This file is part of the Linux kernel, and is made available under +# the terms of the GNU General Public License version 2 or (at your +# option) any later version; incorporated herein by reference. +# +# ----------------------------------------------------------------------- + +# +# arch/x86_64/cpuid_asm.S +# + +#include + +# cpuid function to be used with setgpr_wrapper() + + .text + .globl setgpr_cpuid + .type setgrp_cpuid,@function + +setgpr_cpuid: + cpuid + retq + + .size setgpr_cpuid,.-setgpr_cpuid diff --git a/arch/x86_64/kernel/msr_asm.S b/arch/x86_64/kernel/msr_asm.S new file mode 100644 index 0000000..0dc1e0b --- /dev/null +++ b/arch/x86_64/kernel/msr_asm.S @@ -0,0 +1,54 @@ +# ----------------------------------------------------------------------- +# +# Copyright 2005-2007 H. Peter Anvin - All Rights Reserved +# +# This file is part of the Linux kernel, and is made available under +# the terms of the GNU General Public License version 2 or (at your +# option) any later version; incorporated herein by reference. +# +# ----------------------------------------------------------------------- + +# +# arch/x86_64/msr_asm.S +# + +#include + +# +# rdmsr/wrmsr functions to be used with setgpr_wrapper() + + .text + + .section .fixup,"ax" +setgpr_msr_fixup: + movl $-EIO,(%rsp) + retq + .previous + + .globl setgpr_wrmsr + .type setgrp_wrmsr,@function + +setgpr_wrmsr: +1: wrmsr + retq + + .section __ex_table,"a" + .align 8 + .quad 1b,setgpr_msr_fixup + .previous + + .size setgpr_wrmsr, .-setgpr_wrmsr + + .globl setgpr_rdmsr + .type setgrp_rdmsr,@function + +setgpr_rdmsr: +1: rdmsr + retq + + .section __ex_table,"a" + .align 8 + .quad 1b,setgpr_msr_fixup + .previous + + .size setgpr_rdmsr, .-setgpr_rdmsr diff --git a/arch/x86_64/kernel/setgpr.S b/arch/x86_64/kernel/setgpr.S new file mode 100644 index 0000000..9ef580b --- /dev/null +++ b/arch/x86_64/kernel/setgpr.S @@ -0,0 +1,94 @@ +# ----------------------------------------------------------------------- +# +# Copyright 2005-2007 H. Peter Anvin - All Rights Reserved +# +# This file is part of the Linux kernel, and is made available under +# the terms of the GNU General Public License version 2 or (at your +# option) any later version; incorporated herein by reference. +# +# ----------------------------------------------------------------------- + +# +# arch/x86_64/setgpr.S +# + +# Wrapper around a subroutine that allows all the GPRs to be set +# +# The prototype is: +# long setgpr_wrapper(u64 *in, u64 *out, void (*func)(void)) +# +# ... where each pointer points to the GPRs in numerical order. The +# images have 16 64-bit values to make it compatible with x86-x64. +# +# The stub is called "naked", and therefore should be an assembly routine. +# The word on the top of the stack is initialized to zero and is the return +# value. + + .text + .globl setgpr_wrapper + .type setgrp_wrapper,@function + +setgpr_wrapper: + pushq %rbx + pushq %rbp + pushq %r12 + pushq %r13 + pushq %r14 + pushq %r15 + pushq %rsi # "out" pointer + pushq %rdx # "func" pointer + pushq $0 # Return value + + movq (%rdi),%rax + movq 8(%rdi),%rcx + movq 16(%rdi),%rdx + movq 24(%rdi),%rbx + # 32 is %rsp + movq 40(%rdi),%rbp + movq 48(%rdi),%rsi + # 56 is %rdi + movq 64(%rdi),%r8 + movq 72(%rdi),%r9 + movq 80(%rdi),%r10 + movq 88(%rdi),%r11 + movq 96(%rdi),%r12 + movq 104(%rdi),%r13 + movq 112(%rdi),%r14 + movq 120(%rdi),%r15 + movq 56(%rdi),%rdi + + callq *8(%rsp) + + pushq %rsi + movq 24(%rsp),%rsi + + movq %rax,(%rsi) + popq %rax # Saved %rsi + movq %rcx,8(%rsi) + xorl %ecx,%ecx + movq %rdx,16(%rsi) + movq %rbx,24(%rsi) + movq %rcx,32(%rsi) # Zero out slot for %rsp + movq %rbp,40(%rsi) + movq %rax,48(%rsi) # Saved %rsi + movq %rdi,56(%rsi) + movq %r8,64(%rsi) + movq %r9,72(%rsi) + movq %r10,80(%rsi) + movq %r11,88(%rsi) + movq %r12,96(%rsi) + movq %r13,104(%rsi) + movq %r14,112(%rsi) + movq %r15,120(%rsi) + + popq %rax # Return value + addq $16,%rsp # Drop saved func, out pointers + popq %r15 + popq %r14 + popq %r13 + popq %r12 + popq %rbp + popq %rbx + retq + + .size setgpr_wrapper,.-setgpr_wrapper diff --git a/include/asm-i386/cpuctl.h b/include/asm-i386/cpuctl.h new file mode 100644 index 0000000..17aaaa3 --- /dev/null +++ b/include/asm-i386/cpuctl.h @@ -0,0 +1,29 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007 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., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * asm-i386/cpuctl.h + * + * Header file for CPU control functions, like the MSR and CPUID drivers. + */ + +#ifndef _ASM_I386_CPUCTL_H +#define _ASM_I386_CPUCTL_H + +#include +#include + +#define IOC_WRMSR_SPECIAL _IOWR('A', 0x20, u64[16]) +#define IOC_RDMSR_SPECIAL _IOWR('A', 0x21, u64[16]) +#define IOC_CPUID_SPECIAL _IOWR('A', 0x22, u64[16]) + +#endif /* _ASM_I386_CPUCTL_H */ diff --git a/include/asm-x86_64/cpuctl.h b/include/asm-x86_64/cpuctl.h new file mode 100644 index 0000000..ad48d71 --- /dev/null +++ b/include/asm-x86_64/cpuctl.h @@ -0,0 +1 @@ +#include