I am following a tutorial from here.
I have the following code:
#include <linux/init.h> // Macros used to mark up functions e.g. __init __exit
#include <linux/module.h> // Core header for loading LKMs into the kernel
#include <linux/device.h> // Header to support the kernel Driver Model
#include <linux/kernel.h> // Contains types, macros, functions for the kernel
#include <linux/fs.h> // Header for the Linux file system support
#include <linux/uaccess.h> // Required for the copy to user function
#define DEVICE_NAME "ebbchar" ///< The device will appear at /dev/ebbchar using this value
#define CLASS_NAME "ebb" ///< The device class -- this is a character device driver
MODULE_LICENSE("GPL"); ///< The license type -- this affects available functionality
MODULE_AUTHOR("Derek Molloy"); ///< The author -- visible when you use modinfo
MODULE_DESCRIPTION("A simple Linux char driver for the BBB"); ///< The description -- see modinfo
MODULE_VERSION("0.1"); ///< A version number to inform users
static int majorNumber; ///< Stores the device number -- determined automatically
static char message[256] = {0}; ///< Memory for the string that is passed from userspace
static short size_of_message; ///< Used to remember the size of the string stored
static int numberOpens = 0; ///< Counts the number of times the device is opened
static struct class* ebbcharClass = NULL; ///< The device-driver class struct pointer
static struct device* ebbcharDevice = NULL; ///< The device-driver device struct pointer
static int dev_open(struct inode *, struct file *);
static int dev_release(struct inode *, struct file *);
static ssize_t dev_read(struct file *, char *, size_t, loff_t *);
static ssize_t dev_write(struct file *, const char *, size_t, loff_t *);
static struct file_operations fops =
{
.open = dev_open,
.read = dev_read,
.write = dev_write,
.release = dev_release,
};
static int __init ebbchar_init(void){
printk(KERN_INFO "EBBChar: Initializing the EBBChar LKM\n");
// Try to dynamically allocate a major number for the device -- more difficult but worth it
majorNumber = register_chrdev(0, DEVICE_NAME, &fops);
if (majorNumber<0){
printk(KERN_ALERT "EBBChar failed to register a major number\n");
return majorNumber;
}
printk(KERN_INFO "EBBChar: registered correctly with major number %d\n", majorNumber);
// Register the device class
ebbcharClass = class_create(THIS_MODULE, CLASS_NAME);
if (IS_ERR(ebbcharClass)){ // Check for error and clean up if there is
unregister_chrdev(majorNumber, DEVICE_NAME);
printk(KERN_ALERT "Failed to register device class\n");
return PTR_ERR(ebbcharClass); // Correct way to return an error on a pointer
}
printk(KERN_INFO "EBBChar: device class registered correctly\n");
// Register the device driver
ebbcharDevice = device_create(ebbcharClass, NULL, MKDEV(majorNumber, 0), NULL, DEVICE_NAME);
if (IS_ERR(ebbcharDevice)){ // Clean up if there is an error
class_destroy(ebbcharClass); // Repeated code but the alternative is goto statements
unregister_chrdev(majorNumber, DEVICE_NAME);
printk(KERN_ALERT "Failed to create the device\n");
return PTR_ERR(ebbcharDevice);
}
printk(KERN_INFO "EBBChar: device class created correctly\n"); // Made it! device was initialized
return 0;
}
static void __exit ebbchar_exit(void){
device_destroy(ebbcharClass, MKDEV(majorNumber, 0)); // remove the device
class_unregister(ebbcharClass); // unregister the device class
class_destroy(ebbcharClass); // remove the device class
unregister_chrdev(majorNumber, DEVICE_NAME); // unregister the major number
printk(KERN_INFO "EBBChar: Goodbye from the LKM!\n");
}
static int dev_open(struct inode *inodep, struct file *filep){
numberOpens++;
printk(KERN_INFO "EBBChar: Device has been opened %d time(s)\n", numberOpens);
return 0;
}
static ssize_t dev_read(struct file *filep, char *buffer, size_t len, loff_t *offset){
int error_count = 0;
// copy_to_user has the format ( * to, *from, size) and returns 0 on success
error_count = copy_to_user(buffer, message, size_of_message);
if (error_count==0){ // if true then have success
printk(KERN_INFO "EBBChar: Sent %d characters to the user\n", size_of_message);
return (size_of_message=0); // clear the position to the start and return 0
}
else {
printk(KERN_INFO "EBBChar: Failed to send %d characters to the user\n", error_count);
return -EFAULT; // Failed -- return a bad address message (i.e. -14)
}
}
static ssize_t dev_write(struct file *filep, const char *buffer, size_t len, loff_t *offset){
sprintf(message, "%s(%zu letters)", buffer, len); // appending received string with its length
size_of_message = strlen(message); // store the length of the stored message
printk(KERN_INFO "EBBChar: Received %zu characters from the user\n", len);
return len;
}
static int dev_release(struct inode *inodep, struct file *filep){
printk(KERN_INFO "EBBChar: Device successfully closed\n");
return 0;
}
module_init(ebbchar_init);
module_exit(ebbchar_exit);
I have a small testing file as well from the tutorial. The problem is that when the testing code runs, the process ends up being killed. The logs files say it is due to Supervisor Mode access and that a page fault exception was thrown.
After some research and looking in log files It came down to compatibility problems with Supervisor Mode Access Prevention, where kernel code can't access user code due to the new SMAP feature of some CPUs.
After disabling SMAP at boot time with the nosmap option the testing code works just fine.
I am looking for a way to disable/circumvent SMAP properly in module code. Since this application could run on multiple CPUs, I don't think that changing the CR4 register is the proper way.
I think the copy_to_user() function is a good lead. The problem arises when write is called. Could anyone point to me what is the proper way to code the write() function for this module?
If you are having a problem, disabling SMAP won't solve it, it will only hide it. The fact that SMAP kills your process is good and it should stay that way, it's a security measure of the Linux kernel and it should not be disabled only to make a buggy module work.
Your error is here:
sprintf(message, "%s(%zu letters)", buffer, len);
you are reading user space memory from kernel space, which is wrong, and SMAP prevents this generating a fault.
You should use copy_from_user(), since you are dealing with a user space buffer:
static ssize_t dev_write(struct file *filep, const char __user *buffer, size_t len, loff_t *offset){
unsigned long remaining;
// NEVER copy more than your message buffer size.
if (len > 256)
len = 256;
// Ensure that the additional string fits (XXX because len is at most 3 chars).
if (len + strlen(" (XXX letters)") >= 256) {
pr_info("User buffer is too big (%zu).\n", len);
return -EINVAL;
}
remaining = copy_from_user(message, buffer, len);
if (remaining > 0) {
pr_info("Failed to copy %lu characters from the user.\n", remaining);
return -EFAULT;
}
sprintf(message + len, " (%zu letters)", len);
size_of_message = len + strlen(message + len);
pr_info("Received %zu characters from the user.\n", len);
return len;
}
A few other tips:
error_count should be an unsigned long instead of an int since copy_to_user() returns that type.
Your dev_read() and dev_write() functions take pointers from user space. Any time a kernel function takes a pointer that comes from user space, that pointer should be declared using the __user annotation, like I did in the function above.
You can use the macro pr_info() instead of printk(KERN_INFO ...), also like I did above.
You can avoid writing the module name (EBBChar:) every single time at the beginning of each line simply by re-defining the pr_fmt macro like this:
// This will make any pr_* function (except pr_cont) prepend the module name to each message.
// KBUILD_MODNAME is automatically generated when building and is your module name.
// Put this at the top of your module.
#ifdef pr_fmt
#undef pr_fmt
#endif
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
As an assignment I need to complete the following C code in order to produce a kernel module able to act as a memory, but from how it's written I can't understand how it works and why many variables are not used but just declared. I have already tried looking on the teaching material they gave me, and it's even more confusing, plus I can't find on the web a good site where to find documentation about these functions.
The code is the following:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#define DEVICE_NAME "my_device"
#define MAJOR_DEVICE_NUMBER 60
#define MINOR_DEVICE_NUMBER 0
#define BUF_LEN 1024
static char msg[BUF_LEN];
static char *msg_ptr; // I'm pretty sure this should become msg_reading_offset
static int major;
MODULE_AUTHOR("<YOUR NAME>");
MODULE_LICENSE("GPL");
static ssize_t my_read (
struct file *filp, char __user *buf,
size_t length, loff_t *offset);
static ssize_t my_write (
struct file *filp, const char __user *buf,
size_t length, loff_t *offset);
static int my_open (struct inode *inode,
struct file *filp);
static int my_close (struct inode *inode,
struct file *filp);
static int __init my_init (void);
static void __exit my_cleanup (void);
static struct file_operations fops = {
.read = my_read,
.write = my_write,
.open = my_open,
.release = my_close,
};
// I need to implement this function
static int my_open (struct inode *inode,
struct file *filp)
{
return 0;
}
// and this function
static int my_close (struct inode *inode,
struct file *filp)
{
return 0;
}
static ssize_t my_read (
struct file *filp, char __user *buf,
size_t length, loff_t *offset)
{
int nc = 0;
// if no more "valid" bytes can be read, stop
if (*msg_reading_offset == 0) return 0;
// no-negative values allowed
if (length < 0)
return -EINVAL;
// read the whole msg, nothing more
if (length > strlen(msg)) {
length = strlen(msg);
}
nc = copy_to_user(buf, msg_reading_offset, length);
/*
updates the current reading offset pointer so that a
recursive call due to not original
full length will get a 0 (nothing to read)
*/
msg_reading_offset += sizeof(char) * (length-nc);
// returns the number of REAL bytes read.
return length - nc;
}
static ssize_t my_write (
struct file *filp, const char __user *buf,
size_t length, loff_t *offset)
{
int nc = 0;
if (length > BUF_LEN)
return BUF_LEN-length;
nc = copy_from_user(msg,buf,length);
msg_ptr = msg;
return length - nc;
}
static int __init my_init (void)
{
register_chrdev (MAJOR_DEVICE_NUMBER,
DEVICE_NAME,
&fops);
}
module_init(my_init);
static void __exit my_cleanup (void)
{
unregister_chrdev (major, DEVICE_NAME);
}
module_exit(my_cleanup);
At the moment these are my biggest problems:
Where are all the *inode, *filp variables going? Am I supposed to use them?
How is this program even working? I know I need to compile it with a makefile I've been give, but then how am I supposed to access these functions?
Is this supposed to be a real program executed by the kernel or is it just a collections of functions I should use in another C program?
I'm sorry if the questions may seem stupid, but I am at a loss to know how the hell I'm supposed to approach this.
Your Q is a bit broad, but I'll try to give you some hints.
Where are all the *inode, *filp variables going? Am I supposed to use them?
First read an example of how a typical character device is implemented, eg, here.
How is this program even working? I know I need to compile it with a makefile I've been give, but then how am I supposed to access these functions?
Is this supposed to be a real program executed by the kernel or is it just a collections of functions I should use in another C program?
This is NOT a normal executable program. As you are writing a kernel module, you are extending the kernel functionalities. You need to tell the kernel about that, normally via an insmod call. Eg.,
insmod chardev.ko
Then create the corresponding character device:
mknod /dev/chardev c 60 0 # 60 being your MAJOR_DEVICE_NUMBER
Then you can create your own program to read and write to your character device. Alternatively, you can use existing user-space tools:
echo "12345678" > /dev/chardev # write to the device
and,
cat /dev/chardev # read from the device
My goal is to write a kernel-module. I am following the memory tutorial of the freesoftware magazine.
The tutorial works fine. I am able to compile the code. When loaded with insmod, the kernel prints <1>Inserting memory module as expected. When I remove the module using rmmod the kernel prints <1>Removing memory module.
For debugging purposes, I am trying to add printk() to the other methods. But they are never printed.
The priority of all the messages is <1>.
I write into the device by: echo -n test1234 > /dev/memory
And use cat /dev/memory to get back the data.
cat /var/log/messages and dmesg donĀ“t print anymore information
[ 5550.651221] <1>Inserting memory module
[ 5550.655396] <1>Inserting memory module !!!!!!!!!!!!!!!
[12230.130847] <1>Removing memory module
cat /proc/sys/kernel/printk
7 4 1 7
uname- a
Linux generic-armv7a-hf 3.14.0-163850-g775a3df-dirty #2 SMP Mon Jan 12 13:53:50 CET 2015 armv7l GNU/Linux
Why does printk() only work in the init and exit method?
Is there any (better) way to print variable values than printk()?
Here the code:
/* Necessary includes for device drivers */
#include <linux/init.h>
//#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/proc_fs.h>
#include <linux/fcntl.h> /* O_ACCMODE */
#include <asm/system.h> /* cli(), *_flags */
#include <asm/uaccess.h> /* copy_from/to_user */
MODULE_LICENSE("Dual BSD/GPL");
/* Declaration of memory.c functions */
int memory_open(struct inode *inode, struct file *filp);
int memory_release(struct inode *inode, struct file *filp);
ssize_t memory_read(struct file *filp, char *buf, size_t count, loff_t *f_pos);
ssize_t memory_write(struct file *filp, char *buf, size_t count, loff_t *f_pos);
void memory_exit(void);
int memory_init(void);
/* Structure that declares the usual file */
/* access functions */
struct file_operations memory_fops = {
read: memory_read,
write: memory_write,
open: memory_open,
release: memory_release
};
/* Declaration of the init and exit functions */
module_init(memory_init);
module_exit(memory_exit);
/* Global variables of the driver */
/* Major number */
int memory_major = 60;
/* Buffer to store data */
char *memory_buffer;
int memory_init(void) {
int result;
/* Registering device */
result = register_chrdev(memory_major, "memory", &memory_fops);
if (result < 0) {
printk(
"<1>memory: cannot obtain major number %d\n", memory_major);
return result;
}
/* Allocating memory for the buffer */
memory_buffer = kmalloc(1, GFP_KERNEL);
if (!memory_buffer) {
result = -ENOMEM;
goto fail;
}
memset(memory_buffer, 0, 1);
printk("<1>Inserting memory module\n"); ///this works fine
printk("<1>Inserting memory module !!!!!!!!!!!!!!!\n"); ///this works fine too
return 0;
fail:
memory_exit();
return result;
}
void memory_exit(void) {
/* Freeing the major number */
unregister_chrdev(memory_major, "memory");
/* Freeing buffer memory */
if (memory_buffer) {
kfree(memory_buffer);
}
printk("<1>Removing memory module\n"); //never printed
}
int memory_open(struct inode *inode, struct file *filp) {
printk("<1>memory open\n"); //never printed
/* Success */
return 0;
}
int memory_release(struct inode *inode, struct file *filp) {
printk("<1>memory_release\n"); //never printed
/* Success */
return 0;
}
ssize_t memory_read(struct file *filp, char *buf,
size_t count, loff_t *f_pos) {
printk("<1>mem read\n"); //never printed
/* Transfering data to user space */
copy_to_user(buf,memory_buffer,1);
/* Changing reading position as best suits */
if (*f_pos == 0) {
*f_pos+=1;
return 1;
} else {
return 0;
}
}
ssize_t memory_write( struct file *filp, char *buf,
size_t count, loff_t *f_pos) {
printk("<1>mem write\n"); //never printed
char *tmp;
tmp=buf+count-1;
copy_from_user(memory_buffer,tmp,1);
return 1;
}
Your driver seems fine, but you aren't actually talking to it with your test commands, so the functions with printks aren't being called. Once the module is loaded, it registers a major and minor number, 60 and 0 in your case. (Down the road you should update the module to request an available major number instead of using a hard-coded one.)
You need to create a file system node with mknod in order to actually use the driver. This will create the /dev/memory node and connect it to the module you have loaded. Then when it is opened, closed, read from, or written to, the file_operations in your module will be called, and the printks will work.
For your module, you should be able to use
mknod /dev/memory c 60 0
You can also chmod 666 /dev/memory to allow any user to use the device, rather than running as root all the time.
Here's a script based on the one I use with modules I develop:
#!/bin/sh
device="memory"
mode="666"
major=$(awk "\$2==\"$device\" {print \$1}" /proc/devices}
mknod /dev/${device} c $major 0
chmod $mode /dev/${device}
It will look up the major number associated with your module and create a file system node for it automatically.
Once you have loaded the module and run mknod or the script, you should be able to use the driver. You will know that it is working becase cat will only return the last character written to the device - your driver only has a one character buffer, and it is automatically overwritten each time a new character comes in. Then dmesg should show the printk's associated with the functions in your module.
The reason your driver seemed to work is because you were creating a regular file with your echo command, which cat happily printed right back to you. It's the same thing that would have happened if you ran those commands on a file in your home directory, you just happened to be in /dev instead.
When I try to read a char device using:
cat /dev/fifodev
I receive the next message from the terminal
cat: /dev/fifodev: Invalid argument.
I have created the file and gaven it the permissions like this
sudo mknod /dev/fifodev c 251 0
sudo chmod 666 /dev/fifodev
The code of my driver is:
/*
* Called when a process, which already opened the dev file, attempts to
* read from it.
*/
static ssize_t device_read(struct file *filp, /* see include/linux/fs.h */
char *buffer, /* buffer to fill with data */
size_t length, /* length of the buffer */
loff_t * offset)
{
char aux[BUF_LEN];
printk(KERN_ALERT "Entering into device_read");
if (size_cbuffer_t(buf)<length){
return -EINVAL;
}
remove_items_cbuffer_t (buf,aux, length);
copy_to_user(buffer, aux, length);
printk(KERN_ALERT "Getting out from device_read");
return length;
}
What problem do I have here? Why can't i use cat with the /dev/fifodev file?
Per your comment, the issue you're running into appears to be that your application is requesting to read data into a buffer that is larger than you have data to fill.
You will need to calculate the appropriate amount of data to copy (e.g, the smaller value of length and size_cbuffer_t(buf)), and use that value in the place of length.
in the function prototype buffer should be mentioned as __user to specify it as userspace pointer.
Return of the read method is length of the string read. This functions keeps getting recalled until it returns zero.
I think following code will work.
static ssize_t device_read(struct file *filp, /* see include/linux/fs.h */
char __user *buffer, /* buffer to fill with data */
size_t length, /* length of the buffer */
loff_t * offset)
{
char aux[BUF_LEN];
int byte_to_read,maxbyte;
printk(KERN_ALERT "Entering into device_read");
/*
if (size_cbuffer_t(buf)<length){
return -EINVAL;
}
*/
maxbyte=strlen(buf) - *offset; //considering buf is the pointer where you have data to copy to buffer(userspace)
byte_to_read=maxbyte>length?length:maxbyte;
if(byte_to_read==0)
{
printk(KERN_ALERT "Allready Read\n");
return 0;
}
aux=buf;//as in your code AUX doesn't have anything. i'm supposing you want to copy data to this from buf and then use copy_to_user
remove_items_cbuffer_t (buf,aux, length); //i have no idea why you have used this but i'm sure this wont create any problem
copy_to_user(buffer, aux, length); //this will copy your data to userspace
printk(KERN_ALERT "Getting out from device_read");
return length;
}
I've been following a tutorial for opening files from userspace from a Linux kernel module at http://www.howtoforge.com/reading-files-from-the-linux-kernel-space-module-driver-fedora-14
The code is the following:
#include <linux/module.h> // Needed by all modules
#include <linux/kernel.h> // Needed for KERN_INFO
#include <linux/fs.h> // Needed by filp
#include <asm/uaccess.h> // Needed by segment descriptors
int init_module(void)
{
// Create variables
struct file *f;
char buf[128];
mm_segment_t fs;
int i;
// Init the buffer with 0
for(i=0;i<128;i++)
buf[i] = 0;
// To see in /var/log/messages that the module is operating
printk(KERN_INFO "My module is loaded\n");
// I am using Fedora and for the test I have chosen following file
// Obviously it is much smaller than the 128 bytes, but hell with it =)
f = filp_open("/etc/fedora-release", O_RDONLY, 0);
if(f == NULL)
printk(KERN_ALERT "filp_open error!!.\n");
else{
// Get current segment descriptor
fs = get_fs();
// Set segment descriptor associated to kernel space
set_fs(get_ds());
// Read the file
f->f_op->read(f, buf, 128, &f->f_pos);
// Restore segment descriptor
set_fs(fs);
// See what we read from file
printk(KERN_INFO "buf:%s\n",buf);
}
filp_close(f,NULL);
return 0;
}
void cleanup_module(void)
{
printk(KERN_INFO "My module is unloaded\n");
}
The code is copy-pasted from the link above. On my machine, running Fedora 19 with 3.11.10-200 kernel, it seems that filp_open isn't run, providing the buf variable with null values.
What could be wrong? I am still learning the ropes of Linux kernel module development.
First thing you should do is to check if any errors are returned from filp_open (in fact, checking for NULL is probably an outright mistake when modern kernels are concerned). The proper sequence should be:
f = filp_open("/etc/fedora-release", O_RDONLY, 0);
if (IS_ERR(f)) {
// inspect the value of PTR_ERR(f), get the necessary clues
// negative values represent various errors
// as defined in asm-generic/errno-base.h
}
Only then you can move on to diagnosing the read.
977 struct file *filp_open(const char *filename, int flags, umode_t mode)
978 {
979 struct filename *name = getname_kernel(filename);
980 struct file *file = ERR_CAST(name);
981
982 if (!IS_ERR(name)) {
983 file = file_open_name(name, flags, mode);
984 putname(name);
985 }
986 return file;
987 }
Probably the error is in how you put the parameters, the flags parameter is in mode parameter position and vice versa, mode in falgs position.
source: http://lxr.free-electrons.com/source/fs/open.c#L977
When my TimerExpire function is finally called when the timer ticks out, it prints out gibberish. Anyone know why? But my printk function in IOCTL_MAKE_TIMER prints out correctly, so I think it's because I'm passing in the data wrong.
setup_timer() works by setting up the timer in the first argument, telling it to call the function specified by the second argument, and passes the data (which is the third argument), to that function.
In my case, it is calling the TimerExpire(char* data) function, passing to it final_arg, which is a char* to kern_arg. I even tried passing kern_arg directly to the function... also gave me gibberish.
Previously (yesterday), I had char* kern_arg, instead of char kern_arg[], and that worked out perfectly, but I think it was unsafe.
If anyone could provide some insight, that would be amazing! Thanks!
//Necessary Includes For Device Drivers.
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#include <linux/timer.h>
#include <linux/ioctl.h>
#define DEVICE_NAME "mytimer"
#define DEVICE_FILE_NAME "mytimer"
#define MAJOR_NUM 61
#define MINOR_NUM 0
#define SUCCESS 0
#define IOCTL_MAKE_TIMER _IOWR(MAJOR_NUM, 0, int)
#define IOCTL_SET_TIMER _IOWR(MAJOR_NUM, 1, int)
#define IOCTL_GET_TIMER _IOWR(MAJOR_NUM, 2, int)
//Module License
MODULE_LICENSE("Dual BSD/GPL");
//Initialize timer structure.
static struct timer_list my_timer;
//Forward Declarations for File Operation Functions and Other Functions.
static int mytimer_open(struct inode *inode, struct file *file);
static int mytimer_release(struct inode *inode, struct file *file);
int mytimer_ioctl(struct inode *inode, struct file *file, unsigned int ioctl_num, unsigned long args);
void TimerExpire(char* data);
//Syscall Operations for the module.
struct file_operations FileOps =
{
.owner = THIS_MODULE,
.open = mytimer_open,
.release = mytimer_release,
.ioctl = mytimer_ioctl
};
//Syscall function for opening the module.
static int mytimer_open(struct inode *inode, struct file *file)
{
try_module_get(THIS_MODULE);
return SUCCESS;
}
//Syscall function for releasing the module.
static int mytimer_release(struct inode *inode, struct file *file)
{
module_put(THIS_MODULE);
return SUCCESS;
}
//Syscall function for controlling the module through IOCTLs.
int mytimer_ioctl(struct inode *inode, struct file *file, unsigned int fcn, unsigned long args)
{
//Copies the function parameters from userspace to kernel space in order to use them in the kernel module.
char* user_arg = args;
char kern_arg[strlen_user(user_arg)];
copy_from_user(kern_arg, user_arg, strlen_user(user_arg));
char* final_arg = kern_arg;
//If there is a timer, and the command is to make a new one, the old timer will be removed so a new one can be setup.
if (timer_pending(&my_timer) && fcn == IOCTL_MAKE_TIMER)
{
del_timer_sync(&my_timer);
printk("Timer already exists. Deleting old timer and setting new timer.\n");
}
//Switch function that serves the function that is called.
//Note that the make and set timer functions are separate. This is because only 1 arg is passed via ioctl at a time, so I had to make two different ioctl calls.
switch (fcn)
{
//Make a new timer.
case IOCTL_MAKE_TIMER:
setup_timer(&my_timer, TimerExpire, final_arg);
printk("Made timer with message: %s\n", final_arg);
break;
//Set the timer made above.
case IOCTL_SET_TIMER:
mod_timer(&my_timer, jiffies + msecs_to_jiffies(args * 1000));
printk("Armed timer for %d seconds.\n", args);
break;
//Print the current timer, if any.
case IOCTL_GET_TIMER:
if (!timer_pending(&my_timer))
{
printk("No timer currently set.\n");
}
else
{
printk("Time left in timer: %u seconds\n", jiffies_to_msecs(my_timer.expires - jiffies)/1000);
printk("Message in timer is: %s\n", my_timer.data);
}
break;
}
return SUCCESS;
}
//Function to perform when timer expires.
void TimerExpire(char* data)
{
printk("%s\n", data);
}
//Module Init and Exit Functions.
int init_module(void)
{
printk("Loading MyTimer Kernel Module...\n");
//Register the device with the system to obtain the major number and register the file operations for syscall functionality.
int initResult = register_chrdev(MAJOR_NUM, "mytimer", &FileOps);
//If we couldn't register the device, print the error.
if (initResult < 0)
{
printk("Cannot obtain major number %d\n", MAJOR_NUM);
return initResult;
}
printk("Please create device file using:\n\tmknod /dev/mytimer c 61 0\n");
return SUCCESS;
}
void cleanup_module(void)
{
//Unregister the device with the system to free the major number.
printk("Unloading MyTimer Kernel Module...\n");
unregister_chrdev(MAJOR_NUM, "mytimer");
printk("MyTimer Kernel Module Unloaded.\n");
}
In this code, a call to ioctl(fd,IOCTL_MAKE_TIMER,...) passes setup_timer() a pointer to an array located on the kernel stack, then returns. By the point that the timer expires, the memory that used to hold that array has probably been reused.
You need to keep the memory around until after the timer expires. You could do this by allocating a buffer on the kernel heap (e.g. kmalloc()) or using static/global data.