Porting LKM to LSM - Unexpected behavior [closed] - c

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 2 years ago.
Improve this question
I have a Linux Kernel Module that checks for the presence of a specific USB device and performs a printk upon a match. This code works fine and performs as i expect.
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
#include <linux/list.h>
#include <linux/slab.h>
MODULE_LICENSE("GPL");
static int HARD_VEND;
static int HARD_PROD;
static char *HARD_SERI;
struct USBCred
{
int vendor;
int product;
char serial[128];
};
static struct USBCred create_creds_device(struct usb_device *dev)
{
struct USBCred creds;
creds.vendor = dev->descriptor.idVendor;
creds.product = dev->descriptor.idProduct;
if((dev->serial) == NULL)
{
strcpy(creds.serial, "(null)");
} else {
strcpy(creds.serial, dev->serial);
}
return creds;
}
static struct USBCred create_creds_hub(struct usb_bus *bus)
{
struct USBCred creds;
creds.vendor = bus->root_hub->descriptor.idVendor;
creds.product = bus->root_hub->descriptor.idProduct;
if((bus->root_hub->serial) == NULL)
{
strcpy(creds.serial, "(null)");
} else {
strcpy(creds.serial, bus->root_hub->serial);
}
return creds;
}
static int check_usb_creds(struct USBCred usb_data)
{
if(usb_data.vendor != HARD_VEND && usb_data.product != HARD_PROD && strcmp(usb_data.serial, HARD_SERI))
{
return 1;
} else
{
printk(KERN_INFO "*********** RootPug Module - Match ***********");
printk(KERN_INFO "Vendor ID = %x, HC Vendor ID = %x", usb_data.vendor, HARD_VEND);
printk(KERN_INFO "Product ID = %x, HC Product ID = %x", usb_data.product, HARD_PROD);
printk(KERN_INFO "Serial = %s, HC Serial= %s", usb_data.serial, HARD_SERI);
return 0;
}
}
static int __init usb_fun_init (void)
{
int id;
int chix;
struct USBCred cred;
struct usb_bus *bus;
struct usb_device *dev, *childdev = NULL;
HARD_VEND = 0x26bd;
HARD_PROD = 0x9917;
HARD_SERI = "070172966462EB10";
mutex_lock(&usb_bus_idr_lock);
idr_for_each_entry(&usb_bus_idr, bus, id)
{
cred = create_creds_hub(bus);
//print_USBCred(cred);
check_usb_creds(cred);
dev = bus->root_hub;
usb_hub_for_each_child(dev, chix, childdev)
{
if(childdev)
{
usb_lock_device(childdev);
cred = create_creds_device(childdev);
//print_USBCred(cred);
check_usb_creds(cred);
usb_unlock_device(childdev);
}
}
}
mutex_unlock(&usb_bus_idr_lock);
return 0;
}
static void __exit usb_fun_exit (void)
{
printk(KERN_INFO "***************** RootPug Module - Exit *****************\n");
}
module_init(usb_fun_init);
module_exit(usb_fun_exit);
When moving over to an LSM, the LSM registers without an issue, and the hook registers correctly, yet the code is not behaving as I would like, and I can not understand why. I have added debugging statements to understand at which points the code is getting to, but it seems the idr_for_each_entry() is not being executed, as the debug statment ithin that loop is not being printed. I have used a flag based system to provide a mechanism for having -EPERM as the default return value, being overwritten if the usb is found.
I cannot understand why only the mutex is being performed, with no check to the hubs or usb devices, yet the same code works as an LKM. The actual code that is in question is within the appcl_inode_create hook. I have left out the other hooks for brevity, but they currently do nothing except return the expected value of 0.
#include <linux/init.h>
#include <linux/kd.h>
#include <linux/kernel.h>
#include <linux/tracehook.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/lsm_hooks.h>
#include <linux/xattr.h>
#include <linux/security.h>
#include <linux/capability.h>
#include <linux/unistd.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/slab.h>
#include <linux/pagemap.h>
#include <linux/proc_fs.h>
#include <linux/magic.h>
#include <linux/ctype.h>
#include <linux/swap.h>
#include <linux/spinlock.h>
#include <linux/syscalls.h>
#include <linux/dcache.h>
#include <linux/file.h>
#include <linux/fdtable.h>
#include <linux/namei.h>
#include <linux/mount.h>
#include <linux/tty.h>
#include <linux/types.h>
#include <linux/atomic.h>
#include <linux/bitops.h>
#include <linux/interrupt.h>
#include <linux/parser.h>
#include <linux/nfs_mount.h>
#include <linux/string.h>
#include <linux/mutex.h>
#include <linux/posix-timers.h>
#include <linux/syslog.h>
#include <linux/user_namespace.h>
#include <linux/export.h>
#include <linux/msg.h>
#include <linux/shm.h>
#include <linux/gfp.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/list.h>
#include <linux/cred.h>
#include <linux/fs.h>
#include <linux/fs_struct.h>
#include <linux/fsnotify.h>
#include <linux/path.h>
#include <linux/fdtable.h>
#include <linux/binfmts.h>
#include <linux/time.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
#include "include/appcl_lsm.h"
//#include "include/audit.h"
MODULE_LICENSE("GPLv3");
MODULE_AUTHOR("Jack Cuthbertson");
static int HARD_VEND;
static int HARD_PROD;
static char *HARD_SERI;
struct USBCred
{
int vendor;
int product;
char serial[128];
};
static struct USBCred create_creds_device(struct usb_device *dev)
{
struct USBCred creds;
creds.vendor = dev->descriptor.idVendor;
creds.product = dev->descriptor.idProduct;
if((dev->serial) == NULL)
{
strcpy(creds.serial, "(null)");
} else {
strcpy(creds.serial, dev->serial);
}
return creds;
}
static struct USBCred create_creds_hub(struct usb_bus *bus)
{
struct USBCred creds;
creds.vendor = bus->root_hub->descriptor.idVendor;
creds.product = bus->root_hub->descriptor.idProduct;
if((bus->root_hub->serial) == NULL)
{
strcpy(creds.serial, "(null)");
} else {
strcpy(creds.serial, bus->root_hub->serial);
}
return creds;
}
static int check_usb_creds(struct USBCred usb_data)
{
if(usb_data.vendor != HARD_VEND && usb_data.product != HARD_PROD && strcmp(usb_data.serial, HARD_SERI))
{
printk(KERN_INFO "*********** RootPlug Module - Non Match ***********");
printk(KERN_INFO "Vendor ID = %x, HC Vendor ID = %x", usb_data.vendor, HARD_VEND);
printk(KERN_INFO "Product ID = %x, HC Product ID = %x", usb_data.product, HARD_PROD);
printk(KERN_INFO "Serial = %s, HC Serial= %s", usb_data.serial, HARD_SERI);
return 1;
} else
{
printk(KERN_INFO "*********** RootPlug Module - Match ***********");
printk(KERN_INFO "Vendor ID = %x, HC Vendor ID = %x", usb_data.vendor, HARD_VEND);
printk(KERN_INFO "Product ID = %x, HC Product ID = %x", usb_data.product, HARD_PROD);
printk(KERN_INFO "Serial = %s, HC Serial= %s", usb_data.serial, HARD_SERI);
return 0;
}
}
static int appcl_lsm_inode_create(struct inode *dir, struct dentry *dentry, umode_t mode)
{
int id;
int chix;
int flag = -EPERM;
struct USBCred cred;
struct usb_bus *bus;
struct usb_device *dev, *childdev = NULL;
HARD_VEND = 0x26bd;
HARD_PROD = 0x9917;
HARD_SERI = "070172966462EB10";
printk(KERN_ALERT "Rootplug: Beginning check\n");
mutex_lock(&usb_bus_idr_lock);
printk(KERN_ALERT "Rootplug: Mutex locked the bus list\n");
//loop though all usb buses
idr_for_each_entry(&usb_bus_idr, bus, id)
{
printk(KERN_ALERT "Rootplug: Checking hubs.\n");
cred = create_creds_hub(bus);
//Check creds of usb buses
if(check_usb_creds(cred) == 0)
{
mutex_unlock(&usb_bus_idr_lock);
printk(KERN_ALERT "Rootplug: Unlock hub success!\n");
flag = 0;
} else {
printk(KERN_ALERT "Rootplug: Unlock hub fail!\n");
}
dev = bus->root_hub;
usb_hub_for_each_child(dev, chix, childdev)
{
if(childdev)
{
printk(KERN_ALERT "Rootplug: Checking USB devices\n");
usb_lock_device(childdev);
cred = create_creds_device(childdev);
if(check_usb_creds(cred) == 0)
{
usb_unlock_device(childdev);
mutex_unlock(&usb_bus_idr_lock);
printk(KERN_ALERT "Rootplug: Unlock USB success!\n");
} else {
usb_unlock_device(childdev);
printk(KERN_ALERT "Rootplug: Unlock USB Failure!\n");
}
} else {
printk(KERN_ALERT "Rootplug: No child dev\n");
}
}
}
mutex_unlock(&usb_bus_idr_lock);
printk(KERN_ALERT "Rootplug: Unlock failed - Flag: %i\n", flag);
return flag;
}
static struct security_hook_list appcl_hooks[] = {
/*
* XATTR HOOKS
*/
LSM_HOOK_INIT(inode_setxattr, appcl_lsm_inode_setxattr),
LSM_HOOK_INIT(inode_post_setxattr, appcl_lsm_inode_post_setxattr),
LSM_HOOK_INIT(inode_getxattr, appcl_lsm_inode_getxattr),
LSM_HOOK_INIT(inode_removexattr, appcl_lsm_inode_removexattr),
LSM_HOOK_INIT(d_instantiate, appcl_lsm_d_instantiate),
LSM_HOOK_INIT(inode_setsecurity, appcl_lsm_inode_setsecurity),
LSM_HOOK_INIT(inode_init_security, appcl_lsm_inode_init_security),
/*
* INODE HOOKS
*/
LSM_HOOK_INIT(inode_alloc_security, appcl_lsm_inode_alloc_security),
LSM_HOOK_INIT(inode_free_security, appcl_lsm_inode_free_security),
/*
* General permission mask
*/
LSM_HOOK_INIT(inode_permission, appcl_lsm_inode_permission),
/*
* Specific permission hooks
*/
LSM_HOOK_INIT(inode_create, appcl_lsm_inode_create),
LSM_HOOK_INIT(inode_rename, appcl_lsm_inode_rename),
LSM_HOOK_INIT(inode_link, appcl_lsm_inode_link),
LSM_HOOK_INIT(inode_unlink, appcl_lsm_inode_unlink),
LSM_HOOK_INIT(inode_symlink, appcl_lsm_inode_symlink),
LSM_HOOK_INIT(inode_mkdir, appcl_lsm_inode_mkdir),
LSM_HOOK_INIT(inode_rmdir, appcl_lsm_inode_rmdir),
LSM_HOOK_INIT(inode_mknod, appcl_lsm_inode_mknod),
LSM_HOOK_INIT(inode_readlink, appcl_lsm_inode_readlink),
LSM_HOOK_INIT(inode_follow_link, appcl_lsm_inode_follow_link),
//LSM_HOOK_INIT(inode_setattr, appcl_lsm_inode_setattr),
//LSM_HOOK_INIT(inode_getattr, appcl_lsm_inode_getattr),
/*
* FILE HOOKS
*/
LSM_HOOK_INIT(file_alloc_security, appcl_lsm_file_alloc_security),
LSM_HOOK_INIT(file_free_security, appcl_lsm_file_free_security),
LSM_HOOK_INIT(file_permission, appcl_lsm_file_permission),
//LSM_HOOK_INIT(file_fcntl, appcl_lsm_file_fcntl),
LSM_HOOK_INIT(file_open, appcl_lsm_file_open),
LSM_HOOK_INIT(file_receive, appcl_lsm_file_receive),
/*
* CRED HOOKS
*/
LSM_HOOK_INIT(cred_alloc_blank, appcl_lsm_cred_alloc_blank),
LSM_HOOK_INIT(cred_free, appcl_lsm_cred_free),
LSM_HOOK_INIT(cred_prepare, appcl_lsm_cred_prepare),
LSM_HOOK_INIT(cred_transfer, appcl_lsm_cred_transfer),
LSM_HOOK_INIT(bprm_set_creds, appcl_lsm_bprm_set_creds),
/*
* SUPERBLOCK HOOKS
*/
LSM_HOOK_INIT(sb_alloc_security, appcl_lsm_sb_alloc_security),
LSM_HOOK_INIT(sb_free_security, appcl_lsm_sb_free_security),
};
static __init int appcl_init(void)
{
printk(KERN_ALERT "Rootplug: Module loading... \n");
/*
* Set security attributes for initial task
*/
security_add_hooks(appcl_hooks, ARRAY_SIZE(appcl_hooks), "appcl");
printk(KERN_ALERT "Rootplug: module initialised\n");
return 0;
}
DEFINE_LSM(appcl) = {
.name = "appcl",
.init = appcl_init,
};

The function that handled the matching used a different alert level for the printk function. As such, the messages were not displayed to the console at that stage in the boot process.
What was:
printk(KERN_INFO "message");
Should be:
printk(KERN_ALERT "message");
The matching function was being called, after recompiling the kernel with the hooks forced to return 0, i could see from /var/log/* that the function was being executed and outputting the correct information.

Related

Using printk in tracepoint causes the system to freeze

I am learning tracepoint related knowledge of Linux, and I wrote a module, as shown below.
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
#include <linux/unistd.h>
#include <linux/sched.h>
#include <linux/tracepoint.h>
#define MAXNUM 600
MODULE_LICENSE("GPL");
static int monitored_pid = 0;
module_param(monitored_pid, int, 0);
MODULE_PARM_DESC(monitored_pid, "The pid of the monitored process.");
void lookup_tps(struct tracepoint *tp, void *priv);
void sys_enter_probe(struct pt_regs *regs, long id);
static struct tracepoint * sys_enter_tp = NULL;
static unsigned monitored_syscalls[MAXNUM];
static int count = 0;
int __init start(void){
int i = 0;
// find sys_enter tracepoint
for_each_kernel_tracepoint(lookup_tps, NULL);
if(sys_enter_tp == NULL){
printk(KERN_INFO "cannot find sys_enter tracepoint.\n");
// let module loading fail
return -1;
}
if(tracepoint_probe_register(sys_enter_tp, sys_enter_probe, NULL) != 0){
printk(KERN_INFO "regist fail.\n");
return -1;
}
for(i=0; i<MAXNUM; i++){
monitored_syscalls[i] = 0;
}
monitored_syscalls[__NR_open] = 1; // I want to monitor open operation
monitored_syscalls[__NR_openat] = 1;
printk(KERN_INFO "Start to monitor process of pid : %d\n", monitored_pid);
return 0;
}
void __exit end(void){
tracepoint_probe_unregister(sys_enter_tp, sys_enter_probe, NULL);
printk(KERN_INFO "End to monitor process of pid : %d\n", monitored_pid);
printk(KERN_INFO "open count : %d\n", count);
}
void lookup_tps(struct tracepoint *tp, void *priv){
if(strcmp(tp->name, "sys_enter") == 0){
sys_enter_tp = tp;
}
}
void sys_enter_probe(struct pt_regs *regs, long id){
if(id>=MAXNUM){
return;
}
if(monitored_syscalls[id] == 0){
return;
}
if(current->pid != monitored_pid){
return;
}
printk(KERN_INFO "1234\n");
count++;
}
module_init(start);
module_exit(end);
When I entered the insmod command on the command line and pressed Enter, the system did not respond. After many experiments, I found that this problem occurs when writing count or using printk in the probe function(sys_enter_probe). I don't know much about the various mechanisms in the kernel, and I hope someone can tell me what rules my code violates and where I can learn these rules.

implicit declaration of blk_init_queue

I was trying to write a basic block driver following this link:
https://linux-kernel-labs.github.io/master/labs/block_device_drivers.html
I wrote the following code:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
MODULE_LICENSE("GPL");
#define BLK_DEV_NAME "my_block"
#define MY_BLOCK_MINORS 1
int major;
struct gendisk *gd;
struct request_queue *queue; /* The device request queue */
spinlock_t lock; /* For mutual exclusion */
struct block_device_operations my_block_ops = {
.owner = THIS_MODULE
};
static void my_block_request(struct request_queue *q)
{
pr_info("%s\n", __func__);
}
static int test_hello_init(void)
{
major = register_blkdev(0, BLK_DEV_NAME);
pr_info("major = %d\n", major);
spin_lock_init(&lock);
queue = blk_init_queue(my_block_request, &lock);
if (!queue) {
pr_err("blk_init_queue failed\n");
return -ENOMEM;
}
blk_queue_logical_block_size(queue, 512);
gd = alloc_disk(MY_BLOCK_MINORS);
if (!gd) {
pr_err("alloc_disk failure\n");
return -ENOMEM;
}
gd->major = major;
gd->first_minor = 0;
gd->fops = &my_block_ops;
gd->queue = queue;
snprintf (gd->disk_name, 32, "myblock");
add_disk(gd);
pr_info("adding disk successful\n");
return 0;
}
static void test_hello_exit(void)
{
unregister_blkdev(major, BLK_DEV_NAME);
if (gd) {
del_gendisk(gd);
}
}
module_init(test_hello_init);
module_exit(test_hello_exit);
I get the following error when i compile the code:
error: implicit declaration of function ‘blk_init_queue’; did you mean ‘blk_put_queue’? [-Werror=implicit-function-declaration]
queue = blk_init_queue(my_block_request, &lock);
^~~~~~~~~~~~~~
blk_put_queue
Looks like this API has been removed from latest kernel version. Can anyone provide an alternative API or some reference for basic block driver.

Linux kernel version mismatch

I'm trying to create a simple kernel module which makes the keyboard LEDs flashing. It works fine on Linux 3.16 (Ubuntu 14.04) but it doesn't change the LED status on 4.5 (Arch).
I can't figure out what kind of difference causes it, I didn't find anything useful in demsg. Any idea how could I find about this problem?
Code:
#include <linux/module.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kd.h>
#include <linux/vt.h>
#include <linux/console_struct.h>
#include <linux/vt_kern.h>
#include "device.h"
#include "timer.h"
#define LEDS_ON 0x07
#define LEDS_OFF 0xFF
static int major_number;
static struct class *class;
static struct tty_driver *driver;
static unsigned long led_status = LEDS_OFF;
static void timer_callbak(void)
{
if (led_status == LEDS_OFF) {
led_status = LEDS_ON;
} else {
led_status = LEDS_OFF;
}
driver->ops->ioctl(vc_cons[fg_console].d->port.tty, KDSETLED, led_status);
}
static long device_ioctl(struct file *file, unsigned int command, unsigned long argument)
{
if (command == ON && led_status == LEDS_OFF) {
timer_init(&timer_callbak);
} else if (command == OFF && led_status == LEDS_OFF){
timer_exit();
} else if (command != OFF && command != ON) {
printk(KERN_WARNING "Invalid ioctl() command");
return -1;
}
return 0;
}
static struct file_operations operations = {
.unlocked_ioctl = device_ioctl
};
dev_t create_dev_type(void)
{
return MKDEV(major_number, 0);
}
void create_device(void)
{
major_number = register_chrdev(0, NAME, &operations);
printk("Keyboard leds registered with major_number \"%d\"", major_number);
class = class_create(THIS_MODULE, NAME);
device_create(class, NULL, create_dev_type(), NULL, NAME);
}
void device_init(void)
{
create_device();
driver = vc_cons[fg_console].d->port.tty->driver;
printk("Keyboard leds device Initialized");
}
void device_exit(void)
{
device_destroy(class, create_dev_type());
class_unregister(class);
class_destroy(class);
unregister_chrdev(major_number, NAME);
printk("Keyboard leds device exited");
}
MODULE_LICENSE("GPL");
That's how I'm trying to invoke it:
#include <sys/ioctl.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include "src/device.h"
int main(void)
{
int fd;
if ((fd = open("/dev/keyboard_leds", O_RDWR|O_NONBLOCK)) < 0) {
perror("Open failed!");
return -1;
}
ioctl(fd, ON);
close(fd);
return 0;
}

Linux kernel module - IOCTL usage returns ENOTTY

Im working on little kernel module. Im trying to use IOCTL (in ioctl_add), but I get ENOTTY when I call it, which is checked in switch, on the bottom of main. The code is below. Has anyone got any idea what am I doing wrong?
user.c:
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <linux/ioctl.h>
#include <sys/stat.h>
#include <sys/poll.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#define IOCTL_TYPE (100)
#define IOCTL_ADD (_IO(IOCTL_TYPE, 1))
void cleanup()
{
if(f>=0) {
close(f);
}
}
int ioctl_add(int f)
{
int ret;
ret = ioctl(f, IOCTL_ADD);
printf("Add \n");
return ret;
}
int main(int argc, char * argv[])
{
int fd;
int *ptr;
fd = open(argv[1], O_RDWR);
if (fd < 0) {
perror("error");
}
posix_memalign((void **)&ptr, 4096, 4096);
* ptr = atoi(argv[2]);
write(fd, ptr, 4096);
ioctl_add(fd);
printf("data is %d\n", *ptr);
close(fd);
switch(errno){
case EBADF:
printf("errno: EBADF \n");
break;
case EFAULT:
printf("errno: EFAULT \n");
break;
case EINVAL:
printf("errno: EINVAL \n");
break;
case ENOTTY:
printf("errno: ENOTTY \n");
break;
default:
printf("errno: none \n");
return 0;
}
return 0;
}
module.c:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/sched.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/cdev.h>
//#include <linux/mm.h>
//#include <linux/config.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/poll.h>
#include <asm/io.h>
#include <asm/bitops.h>
#include <linux/ioctl.h>
#define IOCTL_TYPE (100)
#define IOCTL_ADD (_IO(IOCTL_TYPE, 1))
#include <linux/mm.h>
#include <linux/pagemap.h>
#define DEVICE_NAME "acc_priv"
MODULE_LICENSE("GPL v2");
int ress, tmp;
struct page *page;
int *myaddr;
ssize_t acc_read(struct file *filp,
char __user *buf, size_t count,loff_t * off)
{
printk (KERN_ALERT "Opened\n\r");
return 0;
}
ssize_t acc_write(struct file *filp,
const char __user *buf, size_t count,loff_t * off)
{
printk (KERN_ALERT "Write\n\r");
printk(KERN_INFO "%s\n", __FUNCTION__);
down_read(&current->mm->mmap_sem);
ress = get_user_pages(current, current->mm,(unsigned long)buf,1,1,1,&page,NULL);
if (ress) {
printk(KERN_INFO "Got mmaped.\n");
myaddr = kmap(page);
printk(KERN_INFO "%d\n", *myaddr);
tmp = *myaddr;
tmp = tmp * 2;
printk(KERN_INFO "the result of multiplying: %d\n", tmp);
* myaddr = tmp;
page_cache_release(page);
}
up_read(&current->mm->mmap_sem);
return (0);
}
static int acc_open(struct inode *inode,
struct file *file)
{
printk(KERN_INFO "Opened inode:%p, file:%p\n", inode, file);
return 0;
}
long acc_ioctl(struct file *filp,
unsigned int cmd,unsigned long arg)
{
if(cmd == IOCTL_ADD)
printk(KERN_INFO "Do specified job \n");
return 0;
{
int acc_release(struct inode *inode,
struct file *file)
{
printk (KERN_INFO "device_release(%p,%p)\n", inode, file);
return 0;
}
struct file_operations Fops = {
.owner=THIS_MODULE,
.read=acc_read,
.write=acc_write,
.open=acc_open,
.unlocked_ioctl=acc_ioctl,
.release=acc_release,
};
dev_t my_dev=0;
struct cdev * my_cdev = NULL;
static struct class *class_acc_priv = NULL;
void clean_up(void)
{
if(my_dev && class_acc_priv) {
device_destroy(class_acc_priv,my_dev);
}
if(my_cdev) {
cdev_del(my_cdev);
my_cdev=NULL;
}
if(my_dev) {
unregister_chrdev_region(my_dev, 1);
}
if(class_acc_priv) {
class_destroy(class_acc_priv);
class_acc_priv=NULL;
}
}
int init_acc_priv(void)
{
int res=0;
res=alloc_chrdev_region(&my_dev, 0, 1, DEVICE_NAME);
if(res) {
printk (KERN_ALERT "Alocation of the device number for %s failed\n",
DEVICE_NAME);
return res;
};
class_acc_priv = class_create(THIS_MODULE, "acc_class");
if (IS_ERR(class_acc_priv)) {
printk(KERN_ERR "Error creating rs_class.\n");
res=PTR_ERR(class_acc_priv);
goto err1;
}
my_cdev = cdev_alloc( );
my_cdev->ops = &Fops;
my_cdev->owner = THIS_MODULE;
res=cdev_add(my_cdev, my_dev, 1);
if(res) {
printk (KERN_ALERT "Registration of the device number for %s failed\n",
DEVICE_NAME);
res=-EFAULT;
goto err1;
};
device_create(class_acc_priv,NULL,my_dev,NULL,"acc_priv%d",MINOR(my_dev));
printk (KERN_ALERT "%s The major device number is %d.\n",
"Registeration is a success.",
MAJOR(my_dev));
return res;
err1:
clean_up();
return res;
}
module_init(init_acc_priv);
void cleanup_acc_priv( void )
{
clean_up();
}
module_exit(cleanup_acc_priv);
When 32bit application is run on 64bit OS, it uses compat_ioctl syscall instead of unlocked_ioctl one for perform ioctl command. The reason of special syscall is that size of ioctl argument may differ for 64bit and 32bit applications.
So you need to implement .compat_ioctl file operation.

Write timer in Linux device driver

I'm newbies with the module linux.
I try to create a counter module where the counter is increment on timer callback.
The result of the counter must be send to an other module (a memory module).
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/fcntl.h>
#include <asm/system.h>
#include <asm/uaccess.h>
MODULE_AUTHOR("Helene");
MODULE_DESCRIPTION("Module memory");
MODULE_SUPPORTED_DEVICE("none");
MODULE_LICENSE("Dual BSD/GPL");
/* Global variables of the driver */
/* Buffer to store data */
char *memory_buffer;
int result;
struct file_operations memory_fops;
int memory_open(struct inode *inode, struct file *filp) {
// printk(KERN_DEBUG "Opening memory module\n");
return 0;
}
int memory_release(struct inode *inode, struct file *filp) {
// printk(KERN_DEBUG "Releasing of memory module\n");
return 0;
}
ssize_t memory_read(struct file *filp, char *buf, size_t count, loff_t *f_pos){
// printk(KERN_DEBUG "Reading memory module : %s\n", buf);
if (*f_pos > 0)
return 0;
if (count > strlen(memory_buffer))
count = strlen(memory_buffer);
copy_to_user(buf,memory_buffer,count);
*f_pos = *f_pos + count;
return count;
}
ssize_t memory_write( struct file *filp, const char *buf, size_t count, loff_t *f_pos) {
// printk(KERN_DEBUG "Writing memory module : %s\n", buf);
copy_from_user(memory_buffer, buf, count);
return count;
}
static int __init memory_init(void) {
/* Registering device */
result = register_chrdev(0, "memory", &memory_fops);
if (result < 0) {
printk(KERN_DEBUG "memory: cannot obtain major number \n");
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(KERN_ALERT "Inserting memory module : %d\n", result);
return 0;
fail:
//memory_exit();
return result;
}
static void __exit memory_exit(void) {
/* Freeing the major number */
unregister_chrdev(result, "memory");
/* Freeing buffer memory */
if (memory_buffer) {
kfree(memory_buffer);
}
printk(KERN_DEBUG "Removing memory module\n");
}
struct file_operations memory_fops = {
owner: THIS_MODULE,
read: memory_read,
write: memory_write,
open: memory_open,
release: memory_release
};
module_init(memory_init);
module_exit(memory_exit);
The memory module works. My problem is when I call the function : filp_open/fp->f_op->write/filp_close on the function timer callback.
I have test these functions out of the timer callback and it's work.
Why the filp_open function (& co) don't work on timer callback function ?
#include <linux/init.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("GPL");
static struct timer_list my_timer;
int cptNbClic ;
int result;
struct file_operations timer_fops;
int write_file_system(struct file *fp, char * buf){
int nb;
mm_segment_t old_fs=get_fs();
set_fs(get_ds());
nb = fp->f_op->write(fp,buf ,10, &fp->f_pos);
set_fs(old_fs);
return nb;
}
void writeInMemory(void){
// printk(KERN_DEBUG "busy %d\n", busy);
int nbwrite;
char buf[3];
int fmemory;
fmemory=filp_open ("/dev/memory", O_WRONLY | O_APPEND | O_CREAT,0); //don't work on this function
if (fmemory==NULL){//verification de l'ouverture
printk(KERN_ALERT "filp_open error input memory!!.\n");
return -1;
}
sprintf(buf, "%d", cptNbClic);
printk(KERN_DEBUG "%d\n", cptNbClic);
nbwrite = write_file_system(fmemory, buf);
filp_close(fmemory, 0);
}
void my_timer_callback( unsigned long data )
{
cptNbClic++;
printk(KERN_DEBUG "cptNbClic %d\n", cptNbClic);
writeInMemory();
setup_timer(&my_timer, my_timer_callback, 0);
mod_timer(&my_timer, jiffies + msecs_to_jiffies(1000));
}
static int timer_open(struct inode *inode, struct file *filp) {
/* setup your timer to call my_timer_callback */
cptNbClic = 0;
setup_timer(&my_timer, my_timer_callback, 0);
/* setup timer interval to 200 msecs */
mod_timer(&my_timer, jiffies + msecs_to_jiffies(1000));
return 0;
}
static int timer_release(struct inode *inode, struct file *filp) {
/* Success */
printk(KERN_DEBUG "Releasing of cpt module\n");
del_timer(&my_timer);
return 0;
}
static int __init timer_init(void) {
/* Registering device */
result = register_chrdev(0, "timer", &timer_fops);
if (result < 0) {
printk(KERN_DEBUG "timer: cannot obtain major number \n");
return result;
}
printk(KERN_ALERT "Inserting timer module : %d\n", result);
return 0;
}
static void __exit timer_exit(void) {
unregister_chrdev(result, "timer");
printk(KERN_DEBUG "Removing timer module\n");
}
struct file_operations timer_fops = {
owner: THIS_MODULE,
open: timer_open,
release: timer_release
};
/* Declaration of the init and exit functions */
module_init(timer_init);
module_exit(timer_exit);
Sorry for my bad english
No need to call setup_timer function in your my_timer_callback().Already timer is setup. If you want a recurring timer then just again call mod_timer() in your handler which will updates your timer expire value and your timer happily runs again and again till del_timer() call.

Resources