-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathlkm_device.c
210 lines (168 loc) · 5.33 KB
/
lkm_device.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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
/*
* LKM Sandbox::Device
* <https://github.com/tpiekarski/lkm-sandbox>
* ---
* Copyright 2020 Thomas Piekarski <[email protected]>
*
* This file is part of LKM Sandbox.
*
* LKM Sandbox 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, either version 2 of the License, or
* (at your option) any later version.
*
* LKM Sandbox is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LKM Sandbox. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Thomas Piekarski");
MODULE_DESCRIPTION("A Sandbox device module for the Linux Kernel");
MODULE_VERSION("0.1");
static int device_init_sub(void);
static int proc_init_sub(void);
static int device_open(struct inode *inode, struct file *file);
static int device_release(struct inode *inode, struct file *file);
static ssize_t device_read(struct file *flip, char *buffer, size_t len,
loff_t *offset);
static ssize_t device_write(struct file *flip, const char *buffer, size_t len,
loff_t *offset);
static int proc_show(struct seq_file *seq, void *v);
#define DEVICE_NAME "lkm_device"
#define MESSAGE "Hello, Linux!\n"
#define MESSAGE_BUFFER_LENGTH 15
#define PARAM_MAJOR_NUM_PERMISSION 0664
#define PROC_FILE_NAME "lkm_device_major"
#define PROC_PARENT NULL // root of /proc fs
#define PROC_PERMISSION 0444
static int major_num;
static int param_major_num = 0;
static int device_open_count = 0;
static char message_buffer[MESSAGE_BUFFER_LENGTH];
static char *message_ptr;
static struct file_operations device_fops = { .owner = THIS_MODULE,
.open = device_open,
.read = device_read,
.release = device_release,
.write = device_write };
module_param(param_major_num, int, PARAM_MAJOR_NUM_PERMISSION);
static ssize_t device_read(struct file *flip, char *buffer, size_t len,
loff_t *offset)
{
int bytes_read = 0;
printk(KERN_INFO "lkm_device: Starting to read from sandbox device.\n");
if (*message_ptr == 0)
message_ptr = message_buffer;
while (len && *message_ptr) {
printk(KERN_INFO "lkm_device: Reading from device.\n");
if (put_user(*(message_ptr++), buffer++) == -EFAULT) {
printk(KERN_ALERT
"lkm_device: Failed copying message from kernel to user space.\n");
break;
}
len--;
bytes_read++;
}
return bytes_read;
}
static ssize_t device_write(struct file *flip, const char *buffer, size_t len,
loff_t *offset)
{
printk(KERN_ALERT
"lkm_device: Writing to sandbox device is not supported.\n");
return -EINVAL;
}
static int device_open(struct inode *inode, struct file *file)
{
if (device_open_count > 0) {
printk(KERN_INFO "lkm_device: Sandbox device already open.\n");
return -EBUSY;
}
printk(KERN_INFO "lkm_device: Opening sandbox device.\n");
device_open_count++;
try_module_get(THIS_MODULE);
return 0;
}
static int device_release(struct inode *inode, struct file *file)
{
printk(KERN_INFO "lkm_device: Closing sandbox device.\n");
device_open_count--;
module_put(THIS_MODULE);
return 0;
}
static int proc_show(struct seq_file *seq, void *v)
{
seq_printf(seq, "%d", major_num);
return 0;
}
static int __init lkm_device_init(void)
{
printk(KERN_INFO "lkm_device: Initialize Sandbox Device Module.\n");
if (device_init_sub() < 0) {
return -1;
}
if (proc_init_sub() < 0) {
return -2;
}
return 0;
}
static void __exit lkm_device_exit(void)
{
printk(KERN_INFO "lkm_device: Exiting Sandbox Device Module.\n");
unregister_chrdev(major_num, DEVICE_NAME);
remove_proc_entry(PROC_FILE_NAME, PROC_PARENT);
}
static int device_init_sub(void)
{
printk(KERN_INFO
"lkm_device: Registering character device to print test message.\n");
strncpy(message_buffer, MESSAGE, MESSAGE_BUFFER_LENGTH);
message_ptr = message_buffer;
if (param_major_num != 0) {
printk(KERN_INFO
"lkm_device: Failed allocating %d as major for sandbox device.\n",
param_major_num);
}
major_num = register_chrdev(param_major_num, DEVICE_NAME, &device_fops);
if (major_num < 0) {
printk(KERN_ALERT
"lkm_device: Failed to register sandbox device with major %d.\n",
major_num);
return -1;
}
return 0;
}
static int proc_init_sub(void)
{
struct proc_dir_entry *proc_major_entry = NULL;
printk(KERN_INFO
"lkm_device: Registered sandbox device with major number %d.\n",
major_num);
printk(KERN_INFO
"lkm_device: Creating /proc file %s for storing major number %d.\n",
PROC_FILE_NAME, major_num);
proc_major_entry = proc_create_single(PROC_FILE_NAME, PROC_PERMISSION,
PROC_PARENT, proc_show);
if (proc_major_entry == NULL) {
printk(KERN_ALERT
"lkm_device: Failed to create /proc entry '%s' for device major.\n",
PROC_FILE_NAME);
return -1;
}
return 0;
}
module_init(lkm_device_init);
module_exit(lkm_device_exit);