cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

cpuid.c (4431B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/* ----------------------------------------------------------------------- *
      3 *
      4 *   Copyright 2000-2008 H. Peter Anvin - All Rights Reserved
      5 *
      6 * ----------------------------------------------------------------------- */
      7
      8/*
      9 * x86 CPUID access device
     10 *
     11 * This device is accessed by lseek() to the appropriate CPUID level
     12 * and then read in chunks of 16 bytes.  A larger size means multiple
     13 * reads of consecutive levels.
     14 *
     15 * The lower 32 bits of the file position is used as the incoming %eax,
     16 * and the upper 32 bits of the file position as the incoming %ecx,
     17 * the latter intended for "counting" eax levels like eax=4.
     18 *
     19 * This driver uses /dev/cpu/%d/cpuid where %d is the minor number, and on
     20 * an SMP box will direct the access to CPU %d.
     21 */
     22
     23#include <linux/module.h>
     24
     25#include <linux/types.h>
     26#include <linux/errno.h>
     27#include <linux/fcntl.h>
     28#include <linux/init.h>
     29#include <linux/poll.h>
     30#include <linux/smp.h>
     31#include <linux/major.h>
     32#include <linux/fs.h>
     33#include <linux/device.h>
     34#include <linux/cpu.h>
     35#include <linux/notifier.h>
     36#include <linux/uaccess.h>
     37#include <linux/gfp.h>
     38#include <linux/completion.h>
     39
     40#include <asm/processor.h>
     41#include <asm/msr.h>
     42
     43static struct class *cpuid_class;
     44static enum cpuhp_state cpuhp_cpuid_state;
     45
     46struct cpuid_regs_done {
     47	struct cpuid_regs regs;
     48	struct completion done;
     49};
     50
     51static void cpuid_smp_cpuid(void *cmd_block)
     52{
     53	struct cpuid_regs_done *cmd = cmd_block;
     54
     55	cpuid_count(cmd->regs.eax, cmd->regs.ecx,
     56		    &cmd->regs.eax, &cmd->regs.ebx,
     57		    &cmd->regs.ecx, &cmd->regs.edx);
     58
     59	complete(&cmd->done);
     60}
     61
     62static ssize_t cpuid_read(struct file *file, char __user *buf,
     63			  size_t count, loff_t *ppos)
     64{
     65	char __user *tmp = buf;
     66	struct cpuid_regs_done cmd;
     67	int cpu = iminor(file_inode(file));
     68	u64 pos = *ppos;
     69	ssize_t bytes = 0;
     70	int err = 0;
     71
     72	if (count % 16)
     73		return -EINVAL;	/* Invalid chunk size */
     74
     75	init_completion(&cmd.done);
     76	for (; count; count -= 16) {
     77		call_single_data_t csd;
     78
     79		INIT_CSD(&csd, cpuid_smp_cpuid, &cmd);
     80
     81		cmd.regs.eax = pos;
     82		cmd.regs.ecx = pos >> 32;
     83
     84		err = smp_call_function_single_async(cpu, &csd);
     85		if (err)
     86			break;
     87		wait_for_completion(&cmd.done);
     88		if (copy_to_user(tmp, &cmd.regs, 16)) {
     89			err = -EFAULT;
     90			break;
     91		}
     92		tmp += 16;
     93		bytes += 16;
     94		*ppos = ++pos;
     95		reinit_completion(&cmd.done);
     96	}
     97
     98	return bytes ? bytes : err;
     99}
    100
    101static int cpuid_open(struct inode *inode, struct file *file)
    102{
    103	unsigned int cpu;
    104	struct cpuinfo_x86 *c;
    105
    106	cpu = iminor(file_inode(file));
    107	if (cpu >= nr_cpu_ids || !cpu_online(cpu))
    108		return -ENXIO;	/* No such CPU */
    109
    110	c = &cpu_data(cpu);
    111	if (c->cpuid_level < 0)
    112		return -EIO;	/* CPUID not supported */
    113
    114	return 0;
    115}
    116
    117/*
    118 * File operations we support
    119 */
    120static const struct file_operations cpuid_fops = {
    121	.owner = THIS_MODULE,
    122	.llseek = no_seek_end_llseek,
    123	.read = cpuid_read,
    124	.open = cpuid_open,
    125};
    126
    127static int cpuid_device_create(unsigned int cpu)
    128{
    129	struct device *dev;
    130
    131	dev = device_create(cpuid_class, NULL, MKDEV(CPUID_MAJOR, cpu), NULL,
    132			    "cpu%d", cpu);
    133	return PTR_ERR_OR_ZERO(dev);
    134}
    135
    136static int cpuid_device_destroy(unsigned int cpu)
    137{
    138	device_destroy(cpuid_class, MKDEV(CPUID_MAJOR, cpu));
    139	return 0;
    140}
    141
    142static char *cpuid_devnode(struct device *dev, umode_t *mode)
    143{
    144	return kasprintf(GFP_KERNEL, "cpu/%u/cpuid", MINOR(dev->devt));
    145}
    146
    147static int __init cpuid_init(void)
    148{
    149	int err;
    150
    151	if (__register_chrdev(CPUID_MAJOR, 0, NR_CPUS,
    152			      "cpu/cpuid", &cpuid_fops)) {
    153		printk(KERN_ERR "cpuid: unable to get major %d for cpuid\n",
    154		       CPUID_MAJOR);
    155		return -EBUSY;
    156	}
    157	cpuid_class = class_create(THIS_MODULE, "cpuid");
    158	if (IS_ERR(cpuid_class)) {
    159		err = PTR_ERR(cpuid_class);
    160		goto out_chrdev;
    161	}
    162	cpuid_class->devnode = cpuid_devnode;
    163
    164	err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "x86/cpuid:online",
    165				cpuid_device_create, cpuid_device_destroy);
    166	if (err < 0)
    167		goto out_class;
    168
    169	cpuhp_cpuid_state = err;
    170	return 0;
    171
    172out_class:
    173	class_destroy(cpuid_class);
    174out_chrdev:
    175	__unregister_chrdev(CPUID_MAJOR, 0, NR_CPUS, "cpu/cpuid");
    176	return err;
    177}
    178module_init(cpuid_init);
    179
    180static void __exit cpuid_exit(void)
    181{
    182	cpuhp_remove_state(cpuhp_cpuid_state);
    183	class_destroy(cpuid_class);
    184	__unregister_chrdev(CPUID_MAJOR, 0, NR_CPUS, "cpu/cpuid");
    185}
    186module_exit(cpuid_exit);
    187
    188MODULE_AUTHOR("H. Peter Anvin <hpa@zytor.com>");
    189MODULE_DESCRIPTION("x86 generic CPUID driver");
    190MODULE_LICENSE("GPL");