-
Notifications
You must be signed in to change notification settings - Fork 26
/
msr_version.c
153 lines (134 loc) · 3.44 KB
/
msr_version.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Copyright 2011-2021 Lawrence Livermore National Security, LLC and other
// msr-safe Project Developers. See the top-level COPYRIGHT file for
// details.
//
// SPDX-License-Identifier: GPL-2.0-only
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/version.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include "msr_version.h"
static struct class *cdev_class;
static char cdev_created;
static char cdev_registered;
static char cdev_class_created;
static int open_version(struct inode *inode, struct file *file)
{
return 0;
}
static ssize_t read_version(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
size_t len = strlen( THIS_MODULE->version ) + 1 < count ?
strlen( THIS_MODULE->version ) + 1 :
count;
if (*ppos > 0)
{
return 0;
}
if (len > count)
{
return -EFAULT;
}
if (copy_to_user(buf, THIS_MODULE->version, len))
{
return -EFAULT;
}
*ppos = 1;
return len;
}
static const struct file_operations fops =
{
.owner = THIS_MODULE,
.read = read_version,
.open = open_version
};
#define msr_version_nodename_selector _Generic(\
(((struct class *)0)->devnode),\
char * (*) ( struct device *, mode_t *) : msr_version_nodename1,\
char * (*) ( struct device *, umode_t *) : msr_version_nodename2,\
char * (*) (const struct device *, umode_t *) : msr_version_nodename3 \
)
static char *msr_version_nodename1(struct device *dev, mode_t *mode)
{
if (mode)
{
*mode = 0400; // read-only
}
return kasprintf(GFP_KERNEL, "cpu/msr_safe_version");
}
static char *msr_version_nodename2(struct device *dev, umode_t *mode)
{
if (mode)
{
*mode = 0400; // read-only
}
return kasprintf(GFP_KERNEL, "cpu/msr_safe_version");
}
static char *msr_version_nodename3(const struct device *dev, umode_t *mode)
{
if (mode)
{
*mode = 0400; // read-only
}
return kasprintf(GFP_KERNEL, "cpu/msr_safe_version");
}
void msr_version_cleanup(int majordev)
{
if (cdev_created)
{
cdev_created = 0;
device_destroy(cdev_class, MKDEV(majordev, 0));
}
if (cdev_class_created)
{
cdev_class_created = 0;
class_destroy(cdev_class);
}
if (cdev_registered)
{
cdev_registered = 0;
unregister_chrdev(majordev, "cpu/msr_safe_version");
}
}
int msr_version_init(int *majordev)
{
int err = 0;
struct device *dev;
err = register_chrdev(*majordev, "cpu/msr_safe_version", &fops);
if (err < 0)
{
pr_debug("%s: unable to register chrdev\n", __FUNCTION__);
msr_version_cleanup(*majordev);
err = -EBUSY;
return err;
}
if (err > 0)
{
*majordev = err;
}
cdev_registered = 1;
cdev_class = class_create(
#if LINUX_VERSION_CODE < KERNEL_VERSION(6,4,0)
THIS_MODULE,
#endif
"msr_safe_version");
if (IS_ERR(cdev_class))
{
err = PTR_ERR(cdev_class);
msr_version_cleanup(*majordev);
return err;
}
cdev_class_created = 1;
cdev_class->devnode = msr_version_nodename_selector;
dev = device_create(cdev_class, NULL, MKDEV(*majordev, 0), NULL, "msr_safe_version");
if (IS_ERR(dev))
{
err = PTR_ERR(dev);
msr_version_cleanup(*majordev);
return err;
}
cdev_created = 1;
return 0;
}