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

isst_if_common.c (18444B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Intel Speed Select Interface: Common functions
      4 * Copyright (c) 2019, Intel Corporation.
      5 * All rights reserved.
      6 *
      7 * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
      8 */
      9
     10#include <linux/cpufeature.h>
     11#include <linux/cpuhotplug.h>
     12#include <linux/fs.h>
     13#include <linux/hashtable.h>
     14#include <linux/miscdevice.h>
     15#include <linux/module.h>
     16#include <linux/pci.h>
     17#include <linux/sched/signal.h>
     18#include <linux/slab.h>
     19#include <linux/uaccess.h>
     20#include <uapi/linux/isst_if.h>
     21
     22#include "isst_if_common.h"
     23
     24#define MSR_THREAD_ID_INFO	0x53
     25#define MSR_CPU_BUS_NUMBER	0x128
     26
     27static struct isst_if_cmd_cb punit_callbacks[ISST_IF_DEV_MAX];
     28
     29static int punit_msr_white_list[] = {
     30	MSR_TURBO_RATIO_LIMIT,
     31	MSR_CONFIG_TDP_CONTROL,
     32	MSR_TURBO_RATIO_LIMIT1,
     33	MSR_TURBO_RATIO_LIMIT2,
     34};
     35
     36struct isst_valid_cmd_ranges {
     37	u16 cmd;
     38	u16 sub_cmd_beg;
     39	u16 sub_cmd_end;
     40};
     41
     42struct isst_cmd_set_req_type {
     43	u16 cmd;
     44	u16 sub_cmd;
     45	u16 param;
     46};
     47
     48static const struct isst_valid_cmd_ranges isst_valid_cmds[] = {
     49	{0xD0, 0x00, 0x03},
     50	{0x7F, 0x00, 0x0B},
     51	{0x7F, 0x10, 0x12},
     52	{0x7F, 0x20, 0x23},
     53	{0x94, 0x03, 0x03},
     54	{0x95, 0x03, 0x03},
     55};
     56
     57static const struct isst_cmd_set_req_type isst_cmd_set_reqs[] = {
     58	{0xD0, 0x00, 0x08},
     59	{0xD0, 0x01, 0x08},
     60	{0xD0, 0x02, 0x08},
     61	{0xD0, 0x03, 0x08},
     62	{0x7F, 0x02, 0x00},
     63	{0x7F, 0x08, 0x00},
     64	{0x95, 0x03, 0x03},
     65};
     66
     67struct isst_cmd {
     68	struct hlist_node hnode;
     69	u64 data;
     70	u32 cmd;
     71	int cpu;
     72	int mbox_cmd_type;
     73	u32 param;
     74};
     75
     76static DECLARE_HASHTABLE(isst_hash, 8);
     77static DEFINE_MUTEX(isst_hash_lock);
     78
     79static int isst_store_new_cmd(int cmd, u32 cpu, int mbox_cmd_type, u32 param,
     80			      u32 data)
     81{
     82	struct isst_cmd *sst_cmd;
     83
     84	sst_cmd = kmalloc(sizeof(*sst_cmd), GFP_KERNEL);
     85	if (!sst_cmd)
     86		return -ENOMEM;
     87
     88	sst_cmd->cpu = cpu;
     89	sst_cmd->cmd = cmd;
     90	sst_cmd->mbox_cmd_type = mbox_cmd_type;
     91	sst_cmd->param = param;
     92	sst_cmd->data = data;
     93
     94	hash_add(isst_hash, &sst_cmd->hnode, sst_cmd->cmd);
     95
     96	return 0;
     97}
     98
     99static void isst_delete_hash(void)
    100{
    101	struct isst_cmd *sst_cmd;
    102	struct hlist_node *tmp;
    103	int i;
    104
    105	hash_for_each_safe(isst_hash, i, tmp, sst_cmd, hnode) {
    106		hash_del(&sst_cmd->hnode);
    107		kfree(sst_cmd);
    108	}
    109}
    110
    111/**
    112 * isst_store_cmd() - Store command to a hash table
    113 * @cmd: Mailbox command.
    114 * @sub_cmd: Mailbox sub-command or MSR id.
    115 * @mbox_cmd_type: Mailbox or MSR command.
    116 * @param: Mailbox parameter.
    117 * @data: Mailbox request data or MSR data.
    118 *
    119 * Stores the command to a hash table if there is no such command already
    120 * stored. If already stored update the latest parameter and data for the
    121 * command.
    122 *
    123 * Return: Return result of store to hash table, 0 for success, others for
    124 * failure.
    125 */
    126int isst_store_cmd(int cmd, int sub_cmd, u32 cpu, int mbox_cmd_type,
    127		   u32 param, u64 data)
    128{
    129	struct isst_cmd *sst_cmd;
    130	int full_cmd, ret;
    131
    132	full_cmd = (cmd & GENMASK_ULL(15, 0)) << 16;
    133	full_cmd |= (sub_cmd & GENMASK_ULL(15, 0));
    134	mutex_lock(&isst_hash_lock);
    135	hash_for_each_possible(isst_hash, sst_cmd, hnode, full_cmd) {
    136		if (sst_cmd->cmd == full_cmd && sst_cmd->cpu == cpu &&
    137		    sst_cmd->mbox_cmd_type == mbox_cmd_type) {
    138			sst_cmd->param = param;
    139			sst_cmd->data = data;
    140			mutex_unlock(&isst_hash_lock);
    141			return 0;
    142		}
    143	}
    144
    145	ret = isst_store_new_cmd(full_cmd, cpu, mbox_cmd_type, param, data);
    146	mutex_unlock(&isst_hash_lock);
    147
    148	return ret;
    149}
    150EXPORT_SYMBOL_GPL(isst_store_cmd);
    151
    152static void isst_mbox_resume_command(struct isst_if_cmd_cb *cb,
    153				     struct isst_cmd *sst_cmd)
    154{
    155	struct isst_if_mbox_cmd mbox_cmd;
    156	int wr_only;
    157
    158	mbox_cmd.command = (sst_cmd->cmd & GENMASK_ULL(31, 16)) >> 16;
    159	mbox_cmd.sub_command = sst_cmd->cmd & GENMASK_ULL(15, 0);
    160	mbox_cmd.parameter = sst_cmd->param;
    161	mbox_cmd.req_data = sst_cmd->data;
    162	mbox_cmd.logical_cpu = sst_cmd->cpu;
    163	(cb->cmd_callback)((u8 *)&mbox_cmd, &wr_only, 1);
    164}
    165
    166/**
    167 * isst_resume_common() - Process Resume request
    168 *
    169 * On resume replay all mailbox commands and MSRs.
    170 *
    171 * Return: None.
    172 */
    173void isst_resume_common(void)
    174{
    175	struct isst_cmd *sst_cmd;
    176	int i;
    177
    178	hash_for_each(isst_hash, i, sst_cmd, hnode) {
    179		struct isst_if_cmd_cb *cb;
    180
    181		if (sst_cmd->mbox_cmd_type) {
    182			cb = &punit_callbacks[ISST_IF_DEV_MBOX];
    183			if (cb->registered)
    184				isst_mbox_resume_command(cb, sst_cmd);
    185		} else {
    186			wrmsrl_safe_on_cpu(sst_cmd->cpu, sst_cmd->cmd,
    187					   sst_cmd->data);
    188		}
    189	}
    190}
    191EXPORT_SYMBOL_GPL(isst_resume_common);
    192
    193static void isst_restore_msr_local(int cpu)
    194{
    195	struct isst_cmd *sst_cmd;
    196	int i;
    197
    198	mutex_lock(&isst_hash_lock);
    199	for (i = 0; i < ARRAY_SIZE(punit_msr_white_list); ++i) {
    200		if (!punit_msr_white_list[i])
    201			break;
    202
    203		hash_for_each_possible(isst_hash, sst_cmd, hnode,
    204				       punit_msr_white_list[i]) {
    205			if (!sst_cmd->mbox_cmd_type && sst_cmd->cpu == cpu)
    206				wrmsrl_safe(sst_cmd->cmd, sst_cmd->data);
    207		}
    208	}
    209	mutex_unlock(&isst_hash_lock);
    210}
    211
    212/**
    213 * isst_if_mbox_cmd_invalid() - Check invalid mailbox commands
    214 * @cmd: Pointer to the command structure to verify.
    215 *
    216 * Invalid command to PUNIT to may result in instability of the platform.
    217 * This function has a whitelist of commands, which are allowed.
    218 *
    219 * Return: Return true if the command is invalid, else false.
    220 */
    221bool isst_if_mbox_cmd_invalid(struct isst_if_mbox_cmd *cmd)
    222{
    223	int i;
    224
    225	if (cmd->logical_cpu >= nr_cpu_ids)
    226		return true;
    227
    228	for (i = 0; i < ARRAY_SIZE(isst_valid_cmds); ++i) {
    229		if (cmd->command == isst_valid_cmds[i].cmd &&
    230		    (cmd->sub_command >= isst_valid_cmds[i].sub_cmd_beg &&
    231		     cmd->sub_command <= isst_valid_cmds[i].sub_cmd_end)) {
    232			return false;
    233		}
    234	}
    235
    236	return true;
    237}
    238EXPORT_SYMBOL_GPL(isst_if_mbox_cmd_invalid);
    239
    240/**
    241 * isst_if_mbox_cmd_set_req() - Check mailbox command is a set request
    242 * @cmd: Pointer to the command structure to verify.
    243 *
    244 * Check if the given mail box level is set request and not a get request.
    245 *
    246 * Return: Return true if the command is set_req, else false.
    247 */
    248bool isst_if_mbox_cmd_set_req(struct isst_if_mbox_cmd *cmd)
    249{
    250	int i;
    251
    252	for (i = 0; i < ARRAY_SIZE(isst_cmd_set_reqs); ++i) {
    253		if (cmd->command == isst_cmd_set_reqs[i].cmd &&
    254		    cmd->sub_command == isst_cmd_set_reqs[i].sub_cmd &&
    255		    cmd->parameter == isst_cmd_set_reqs[i].param) {
    256			return true;
    257		}
    258	}
    259
    260	return false;
    261}
    262EXPORT_SYMBOL_GPL(isst_if_mbox_cmd_set_req);
    263
    264static int isst_if_get_platform_info(void __user *argp)
    265{
    266	struct isst_if_platform_info info;
    267
    268	info.api_version = ISST_IF_API_VERSION;
    269	info.driver_version = ISST_IF_DRIVER_VERSION;
    270	info.max_cmds_per_ioctl = ISST_IF_CMD_LIMIT;
    271	info.mbox_supported = punit_callbacks[ISST_IF_DEV_MBOX].registered;
    272	info.mmio_supported = punit_callbacks[ISST_IF_DEV_MMIO].registered;
    273
    274	if (copy_to_user(argp, &info, sizeof(info)))
    275		return -EFAULT;
    276
    277	return 0;
    278}
    279
    280
    281struct isst_if_cpu_info {
    282	/* For BUS 0 and BUS 1 only, which we need for PUNIT interface */
    283	int bus_info[2];
    284	struct pci_dev *pci_dev[2];
    285	int punit_cpu_id;
    286	int numa_node;
    287};
    288
    289static struct isst_if_cpu_info *isst_cpu_info;
    290#define ISST_MAX_PCI_DOMAINS	8
    291
    292static struct pci_dev *_isst_if_get_pci_dev(int cpu, int bus_no, int dev, int fn)
    293{
    294	struct pci_dev *matched_pci_dev = NULL;
    295	struct pci_dev *pci_dev = NULL;
    296	int no_matches = 0;
    297	int i, bus_number;
    298
    299	if (bus_no < 0 || bus_no > 1 || cpu < 0 || cpu >= nr_cpu_ids ||
    300	    cpu >= num_possible_cpus())
    301		return NULL;
    302
    303	bus_number = isst_cpu_info[cpu].bus_info[bus_no];
    304	if (bus_number < 0)
    305		return NULL;
    306
    307	for (i = 0; i < ISST_MAX_PCI_DOMAINS; ++i) {
    308		struct pci_dev *_pci_dev;
    309		int node;
    310
    311		_pci_dev = pci_get_domain_bus_and_slot(i, bus_number, PCI_DEVFN(dev, fn));
    312		if (!_pci_dev)
    313			continue;
    314
    315		++no_matches;
    316		if (!matched_pci_dev)
    317			matched_pci_dev = _pci_dev;
    318
    319		node = dev_to_node(&_pci_dev->dev);
    320		if (node == NUMA_NO_NODE) {
    321			pr_info("Fail to get numa node for CPU:%d bus:%d dev:%d fn:%d\n",
    322				cpu, bus_no, dev, fn);
    323			continue;
    324		}
    325
    326		if (node == isst_cpu_info[cpu].numa_node) {
    327			pci_dev = _pci_dev;
    328			break;
    329		}
    330	}
    331
    332	/*
    333	 * If there is no numa matched pci_dev, then there can be following cases:
    334	 * 1. CONFIG_NUMA is not defined: In this case if there is only single device
    335	 *    match, then we don't need numa information. Simply return last match.
    336	 *    Othewise return NULL.
    337	 * 2. NUMA information is not exposed via _SEG method. In this case it is similar
    338	 *    to case 1.
    339	 * 3. Numa information doesn't match with CPU numa node and more than one match
    340	 *    return NULL.
    341	 */
    342	if (!pci_dev && no_matches == 1)
    343		pci_dev = matched_pci_dev;
    344
    345	return pci_dev;
    346}
    347
    348/**
    349 * isst_if_get_pci_dev() - Get the PCI device instance for a CPU
    350 * @cpu: Logical CPU number.
    351 * @bus_number: The bus number assigned by the hardware.
    352 * @dev: The device number assigned by the hardware.
    353 * @fn: The function number assigned by the hardware.
    354 *
    355 * Using cached bus information, find out the PCI device for a bus number,
    356 * device and function.
    357 *
    358 * Return: Return pci_dev pointer or NULL.
    359 */
    360struct pci_dev *isst_if_get_pci_dev(int cpu, int bus_no, int dev, int fn)
    361{
    362	struct pci_dev *pci_dev;
    363
    364	if (bus_no < 0 || bus_no > 1 || cpu < 0 || cpu >= nr_cpu_ids ||
    365	    cpu >= num_possible_cpus())
    366		return NULL;
    367
    368	pci_dev = isst_cpu_info[cpu].pci_dev[bus_no];
    369
    370	if (pci_dev && pci_dev->devfn == PCI_DEVFN(dev, fn))
    371		return pci_dev;
    372
    373	return _isst_if_get_pci_dev(cpu, bus_no, dev, fn);
    374}
    375EXPORT_SYMBOL_GPL(isst_if_get_pci_dev);
    376
    377static int isst_if_cpu_online(unsigned int cpu)
    378{
    379	u64 data;
    380	int ret;
    381
    382	isst_cpu_info[cpu].numa_node = cpu_to_node(cpu);
    383
    384	ret = rdmsrl_safe(MSR_CPU_BUS_NUMBER, &data);
    385	if (ret) {
    386		/* This is not a fatal error on MSR mailbox only I/F */
    387		isst_cpu_info[cpu].bus_info[0] = -1;
    388		isst_cpu_info[cpu].bus_info[1] = -1;
    389	} else {
    390		isst_cpu_info[cpu].bus_info[0] = data & 0xff;
    391		isst_cpu_info[cpu].bus_info[1] = (data >> 8) & 0xff;
    392		isst_cpu_info[cpu].pci_dev[0] = _isst_if_get_pci_dev(cpu, 0, 0, 1);
    393		isst_cpu_info[cpu].pci_dev[1] = _isst_if_get_pci_dev(cpu, 1, 30, 1);
    394	}
    395
    396	ret = rdmsrl_safe(MSR_THREAD_ID_INFO, &data);
    397	if (ret) {
    398		isst_cpu_info[cpu].punit_cpu_id = -1;
    399		return ret;
    400	}
    401	isst_cpu_info[cpu].punit_cpu_id = data;
    402
    403	isst_restore_msr_local(cpu);
    404
    405	return 0;
    406}
    407
    408static int isst_if_online_id;
    409
    410static int isst_if_cpu_info_init(void)
    411{
    412	int ret;
    413
    414	isst_cpu_info = kcalloc(num_possible_cpus(),
    415				sizeof(*isst_cpu_info),
    416				GFP_KERNEL);
    417	if (!isst_cpu_info)
    418		return -ENOMEM;
    419
    420	ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
    421				"platform/x86/isst-if:online",
    422				isst_if_cpu_online, NULL);
    423	if (ret < 0) {
    424		kfree(isst_cpu_info);
    425		return ret;
    426	}
    427
    428	isst_if_online_id = ret;
    429
    430	return 0;
    431}
    432
    433static void isst_if_cpu_info_exit(void)
    434{
    435	cpuhp_remove_state(isst_if_online_id);
    436	kfree(isst_cpu_info);
    437};
    438
    439static long isst_if_proc_phyid_req(u8 *cmd_ptr, int *write_only, int resume)
    440{
    441	struct isst_if_cpu_map *cpu_map;
    442
    443	cpu_map = (struct isst_if_cpu_map *)cmd_ptr;
    444	if (cpu_map->logical_cpu >= nr_cpu_ids ||
    445	    cpu_map->logical_cpu >= num_possible_cpus())
    446		return -EINVAL;
    447
    448	*write_only = 0;
    449	cpu_map->physical_cpu = isst_cpu_info[cpu_map->logical_cpu].punit_cpu_id;
    450
    451	return 0;
    452}
    453
    454static bool match_punit_msr_white_list(int msr)
    455{
    456	int i;
    457
    458	for (i = 0; i < ARRAY_SIZE(punit_msr_white_list); ++i) {
    459		if (punit_msr_white_list[i] == msr)
    460			return true;
    461	}
    462
    463	return false;
    464}
    465
    466static long isst_if_msr_cmd_req(u8 *cmd_ptr, int *write_only, int resume)
    467{
    468	struct isst_if_msr_cmd *msr_cmd;
    469	int ret;
    470
    471	msr_cmd = (struct isst_if_msr_cmd *)cmd_ptr;
    472
    473	if (!match_punit_msr_white_list(msr_cmd->msr))
    474		return -EINVAL;
    475
    476	if (msr_cmd->logical_cpu >= nr_cpu_ids)
    477		return -EINVAL;
    478
    479	if (msr_cmd->read_write) {
    480		if (!capable(CAP_SYS_ADMIN))
    481			return -EPERM;
    482
    483		ret = wrmsrl_safe_on_cpu(msr_cmd->logical_cpu,
    484					 msr_cmd->msr,
    485					 msr_cmd->data);
    486		*write_only = 1;
    487		if (!ret && !resume)
    488			ret = isst_store_cmd(0, msr_cmd->msr,
    489					     msr_cmd->logical_cpu,
    490					     0, 0, msr_cmd->data);
    491	} else {
    492		u64 data;
    493
    494		ret = rdmsrl_safe_on_cpu(msr_cmd->logical_cpu,
    495					 msr_cmd->msr, &data);
    496		if (!ret) {
    497			msr_cmd->data = data;
    498			*write_only = 0;
    499		}
    500	}
    501
    502
    503	return ret;
    504}
    505
    506static long isst_if_exec_multi_cmd(void __user *argp, struct isst_if_cmd_cb *cb)
    507{
    508	unsigned char __user *ptr;
    509	u32 cmd_count;
    510	u8 *cmd_ptr;
    511	long ret;
    512	int i;
    513
    514	/* Each multi command has u32 command count as the first field */
    515	if (copy_from_user(&cmd_count, argp, sizeof(cmd_count)))
    516		return -EFAULT;
    517
    518	if (!cmd_count || cmd_count > ISST_IF_CMD_LIMIT)
    519		return -EINVAL;
    520
    521	cmd_ptr = kmalloc(cb->cmd_size, GFP_KERNEL);
    522	if (!cmd_ptr)
    523		return -ENOMEM;
    524
    525	/* cb->offset points to start of the command after the command count */
    526	ptr = argp + cb->offset;
    527
    528	for (i = 0; i < cmd_count; ++i) {
    529		int wr_only;
    530
    531		if (signal_pending(current)) {
    532			ret = -EINTR;
    533			break;
    534		}
    535
    536		if (copy_from_user(cmd_ptr, ptr, cb->cmd_size)) {
    537			ret = -EFAULT;
    538			break;
    539		}
    540
    541		ret = cb->cmd_callback(cmd_ptr, &wr_only, 0);
    542		if (ret)
    543			break;
    544
    545		if (!wr_only && copy_to_user(ptr, cmd_ptr, cb->cmd_size)) {
    546			ret = -EFAULT;
    547			break;
    548		}
    549
    550		ptr += cb->cmd_size;
    551	}
    552
    553	kfree(cmd_ptr);
    554
    555	return i ? i : ret;
    556}
    557
    558static long isst_if_def_ioctl(struct file *file, unsigned int cmd,
    559			      unsigned long arg)
    560{
    561	void __user *argp = (void __user *)arg;
    562	struct isst_if_cmd_cb cmd_cb;
    563	struct isst_if_cmd_cb *cb;
    564	long ret = -ENOTTY;
    565
    566	switch (cmd) {
    567	case ISST_IF_GET_PLATFORM_INFO:
    568		ret = isst_if_get_platform_info(argp);
    569		break;
    570	case ISST_IF_GET_PHY_ID:
    571		cmd_cb.cmd_size = sizeof(struct isst_if_cpu_map);
    572		cmd_cb.offset = offsetof(struct isst_if_cpu_maps, cpu_map);
    573		cmd_cb.cmd_callback = isst_if_proc_phyid_req;
    574		ret = isst_if_exec_multi_cmd(argp, &cmd_cb);
    575		break;
    576	case ISST_IF_IO_CMD:
    577		cb = &punit_callbacks[ISST_IF_DEV_MMIO];
    578		if (cb->registered)
    579			ret = isst_if_exec_multi_cmd(argp, cb);
    580		break;
    581	case ISST_IF_MBOX_COMMAND:
    582		cb = &punit_callbacks[ISST_IF_DEV_MBOX];
    583		if (cb->registered)
    584			ret = isst_if_exec_multi_cmd(argp, cb);
    585		break;
    586	case ISST_IF_MSR_COMMAND:
    587		cmd_cb.cmd_size = sizeof(struct isst_if_msr_cmd);
    588		cmd_cb.offset = offsetof(struct isst_if_msr_cmds, msr_cmd);
    589		cmd_cb.cmd_callback = isst_if_msr_cmd_req;
    590		ret = isst_if_exec_multi_cmd(argp, &cmd_cb);
    591		break;
    592	default:
    593		break;
    594	}
    595
    596	return ret;
    597}
    598
    599/* Lock to prevent module registration when already opened by user space */
    600static DEFINE_MUTEX(punit_misc_dev_open_lock);
    601/* Lock to allow one share misc device for all ISST interace */
    602static DEFINE_MUTEX(punit_misc_dev_reg_lock);
    603static int misc_usage_count;
    604static int misc_device_ret;
    605static int misc_device_open;
    606
    607static int isst_if_open(struct inode *inode, struct file *file)
    608{
    609	int i, ret = 0;
    610
    611	/* Fail open, if a module is going away */
    612	mutex_lock(&punit_misc_dev_open_lock);
    613	for (i = 0; i < ISST_IF_DEV_MAX; ++i) {
    614		struct isst_if_cmd_cb *cb = &punit_callbacks[i];
    615
    616		if (cb->registered && !try_module_get(cb->owner)) {
    617			ret = -ENODEV;
    618			break;
    619		}
    620	}
    621	if (ret) {
    622		int j;
    623
    624		for (j = 0; j < i; ++j) {
    625			struct isst_if_cmd_cb *cb;
    626
    627			cb = &punit_callbacks[j];
    628			if (cb->registered)
    629				module_put(cb->owner);
    630		}
    631	} else {
    632		misc_device_open++;
    633	}
    634	mutex_unlock(&punit_misc_dev_open_lock);
    635
    636	return ret;
    637}
    638
    639static int isst_if_relase(struct inode *inode, struct file *f)
    640{
    641	int i;
    642
    643	mutex_lock(&punit_misc_dev_open_lock);
    644	misc_device_open--;
    645	for (i = 0; i < ISST_IF_DEV_MAX; ++i) {
    646		struct isst_if_cmd_cb *cb = &punit_callbacks[i];
    647
    648		if (cb->registered)
    649			module_put(cb->owner);
    650	}
    651	mutex_unlock(&punit_misc_dev_open_lock);
    652
    653	return 0;
    654}
    655
    656static const struct file_operations isst_if_char_driver_ops = {
    657	.open = isst_if_open,
    658	.unlocked_ioctl = isst_if_def_ioctl,
    659	.release = isst_if_relase,
    660};
    661
    662static struct miscdevice isst_if_char_driver = {
    663	.minor		= MISC_DYNAMIC_MINOR,
    664	.name		= "isst_interface",
    665	.fops		= &isst_if_char_driver_ops,
    666};
    667
    668static int isst_misc_reg(void)
    669{
    670	mutex_lock(&punit_misc_dev_reg_lock);
    671	if (misc_device_ret)
    672		goto unlock_exit;
    673
    674	if (!misc_usage_count) {
    675		misc_device_ret = isst_if_cpu_info_init();
    676		if (misc_device_ret)
    677			goto unlock_exit;
    678
    679		misc_device_ret = misc_register(&isst_if_char_driver);
    680		if (misc_device_ret) {
    681			isst_if_cpu_info_exit();
    682			goto unlock_exit;
    683		}
    684	}
    685	misc_usage_count++;
    686
    687unlock_exit:
    688	mutex_unlock(&punit_misc_dev_reg_lock);
    689
    690	return misc_device_ret;
    691}
    692
    693static void isst_misc_unreg(void)
    694{
    695	mutex_lock(&punit_misc_dev_reg_lock);
    696	if (misc_usage_count)
    697		misc_usage_count--;
    698	if (!misc_usage_count && !misc_device_ret) {
    699		misc_deregister(&isst_if_char_driver);
    700		isst_if_cpu_info_exit();
    701	}
    702	mutex_unlock(&punit_misc_dev_reg_lock);
    703}
    704
    705/**
    706 * isst_if_cdev_register() - Register callback for IOCTL
    707 * @device_type: The device type this callback handling.
    708 * @cb:	Callback structure.
    709 *
    710 * This function registers a callback to device type. On very first call
    711 * it will register a misc device, which is used for user kernel interface.
    712 * Other calls simply increment ref count. Registry will fail, if the user
    713 * already opened misc device for operation. Also if the misc device
    714 * creation failed, then it will not try again and all callers will get
    715 * failure code.
    716 *
    717 * Return: Return the return value from the misc creation device or -EINVAL
    718 * for unsupported device type.
    719 */
    720int isst_if_cdev_register(int device_type, struct isst_if_cmd_cb *cb)
    721{
    722	int ret;
    723
    724	if (device_type >= ISST_IF_DEV_MAX)
    725		return -EINVAL;
    726
    727	mutex_lock(&punit_misc_dev_open_lock);
    728	/* Device is already open, we don't want to add new callbacks */
    729	if (misc_device_open) {
    730		mutex_unlock(&punit_misc_dev_open_lock);
    731		return -EAGAIN;
    732	}
    733	memcpy(&punit_callbacks[device_type], cb, sizeof(*cb));
    734	punit_callbacks[device_type].registered = 1;
    735	mutex_unlock(&punit_misc_dev_open_lock);
    736
    737	ret = isst_misc_reg();
    738	if (ret) {
    739		/*
    740		 * No need of mutex as the misc device register failed
    741		 * as no one can open device yet. Hence no contention.
    742		 */
    743		punit_callbacks[device_type].registered = 0;
    744		return ret;
    745	}
    746	return 0;
    747}
    748EXPORT_SYMBOL_GPL(isst_if_cdev_register);
    749
    750/**
    751 * isst_if_cdev_unregister() - Unregister callback for IOCTL
    752 * @device_type: The device type to unregister.
    753 *
    754 * This function unregisters the previously registered callback. If this
    755 * is the last callback unregistering, then misc device is removed.
    756 *
    757 * Return: None.
    758 */
    759void isst_if_cdev_unregister(int device_type)
    760{
    761	isst_misc_unreg();
    762	mutex_lock(&punit_misc_dev_open_lock);
    763	punit_callbacks[device_type].registered = 0;
    764	if (device_type == ISST_IF_DEV_MBOX)
    765		isst_delete_hash();
    766	mutex_unlock(&punit_misc_dev_open_lock);
    767}
    768EXPORT_SYMBOL_GPL(isst_if_cdev_unregister);
    769
    770MODULE_LICENSE("GPL v2");